Skip to content

Commit 9d4f425

Browse files
tsafinclaude
andcommitted
Fix DICTIONARY array handling in CSV, ORC, Paimon, Iceberg writers
Arrow dict8<int8, utf8> arrays (used for low-cardinality TPC-H columns since Phase 3.3) were not handled in non-Lance writers: - csv_writer: fell back to array->ToString() producing corrupt column representation per cell; fix does index→value lookup via GetString() - orc_writer: arrow_type_to_orc_type_name() threw "Unsupported type" on DICTIONARY; copy_array_to_orc_column() also threw; fix maps DICTIONARY to ORC "string" and expands indices via GetView() into dict buffer - paimon_writer: arrow_type_to_paimon_type() threw on DICTIONARY; fix maps to "string" (data path via parquet::arrow::WriteTable is fine) - iceberg_writer: arrow_type_to_iceberg_type() threw on DICTIONARY; fix maps to "string" (data path via parquet::arrow::WriteTable is fine) Parquet and Lance handle DICTIONARY natively through Arrow — no changes needed. ORC fix tested via code review only (requires -DTPCH_ENABLE_ORC). CSV fix verified: l_returnflag/linestatus/shipinstruct/shipmode output correct string values ("N", "O", "DELIVER IN PERSON", "TRUCK"). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f881154 commit 9d4f425

4 files changed

Lines changed: 33 additions & 0 deletions

File tree

src/writers/csv_writer.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,13 @@ void CSVWriter::write_batch(
269269
auto float_array =
270270
std::dynamic_pointer_cast<arrow::FloatArray>(array);
271271
row_buffer << float_array->Value(row);
272+
} else if (field_type->id() == arrow::Type::DICTIONARY) {
273+
auto dict_array =
274+
std::dynamic_pointer_cast<arrow::DictionaryArray>(array);
275+
auto dict_values =
276+
std::dynamic_pointer_cast<arrow::StringArray>(dict_array->dictionary());
277+
auto idx = dict_array->GetValueIndex(row);
278+
row_buffer << escape_csv_value(dict_values->GetString(idx));
272279
} else {
273280
// Fallback: convert to string using Arrow's ToString
274281
row_buffer << array->ToString();

src/writers/iceberg_writer.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ std::string IcebergWriter::arrow_type_to_iceberg_type(
5858
return "decimal(" + std::to_string(decimal_type->precision()) + ","
5959
+ std::to_string(decimal_type->scale()) + ")";
6060
}
61+
case arrow::Type::DICTIONARY:
62+
return "string"; // dict8<int8, utf8> written as Parquet dict page, exposed as string
6163
default:
6264
throw std::runtime_error(
6365
std::string("Unsupported Arrow type for Iceberg: ") + arrow_type->ToString()

src/writers/orc_writer.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ std::string arrow_type_to_orc_type_name(const std::shared_ptr<arrow::DataType>&
3232
return "double";
3333
} else if (type->id() == arrow::Type::STRING) {
3434
return "string";
35+
} else if (type->id() == arrow::Type::DICTIONARY) {
36+
return "string"; // dict8<int8, utf8> expanded to string on write
3537
} else {
3638
throw std::runtime_error("Unsupported Arrow type for ORC conversion");
3739
}
@@ -139,6 +141,26 @@ void copy_array_to_orc_column(
139141
string_col->length[i] = static_cast<int64_t>(str.length());
140142
}
141143
}
144+
} else if (array->type()->id() == arrow::Type::DICTIONARY) {
145+
// Expand dict8<int8, utf8> to ORC string column via index lookup.
146+
// string_view points into the dictionary buffer which outlives this function.
147+
auto dict_array = std::static_pointer_cast<arrow::DictionaryArray>(array);
148+
auto dict_values = std::static_pointer_cast<arrow::StringArray>(dict_array->dictionary());
149+
auto* string_col = dynamic_cast<orc::StringVectorBatch*>(col_batch);
150+
if (!string_col) {
151+
throw std::runtime_error("Failed to cast ORC column to StringVectorBatch");
152+
}
153+
for (size_t i = 0; i < size; ++i) {
154+
if (dict_array->IsNull(static_cast<int64_t>(i))) {
155+
string_col->notNull[i] = 0;
156+
} else {
157+
string_col->notNull[i] = 1;
158+
auto idx = dict_array->GetValueIndex(static_cast<int64_t>(i));
159+
auto sv = dict_values->GetView(idx);
160+
string_col->data[i] = const_cast<char*>(sv.data());
161+
string_col->length[i] = static_cast<int64_t>(sv.length());
162+
}
163+
}
142164
} else {
143165
throw std::runtime_error("Unsupported Arrow type for ORC column copy");
144166
}

src/writers/paimon_writer.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ std::string PaimonWriter::arrow_type_to_paimon_type(
7474
return "decimal";
7575
case arrow::Type::BOOL:
7676
return "boolean";
77+
case arrow::Type::DICTIONARY:
78+
return "string"; // dict8<int8, utf8> written as Parquet dict page, exposed as string
7779
default:
7880
throw std::runtime_error(
7981
std::string("Unsupported Arrow type for Paimon: ") + arrow_type->ToString()

0 commit comments

Comments
 (0)