|
25 | 25 | #include "arrow/array/array_decimal.h" |
26 | 26 | #include "arrow/array/array_dict.h" |
27 | 27 | #include "arrow/array/array_primitive.h" |
| 28 | +#include "arrow/array/builder_binary.h" |
| 29 | +#include "arrow/array/builder_decimal.h" |
| 30 | +#include "arrow/array/builder_primitive.h" |
| 31 | +#include "arrow/array/builder_time.h" |
| 32 | +#include "arrow/memory_pool.h" |
28 | 33 | #include "arrow/type.h" |
29 | 34 | #include "arrow/type_traits.h" |
30 | 35 | #include "arrow/util/decimal.h" |
31 | 36 | #include "fmt/format.h" |
32 | 37 | #include "paimon/common/data/binary_string.h" |
33 | 38 | #include "paimon/common/data/internal_row.h" |
| 39 | +#include "paimon/common/utils/arrow/status_utils.h" |
34 | 40 | #include "paimon/common/utils/checked_cast.h" |
35 | 41 | #include "paimon/common/utils/date_time_utils.h" |
36 | 42 | #include "paimon/common/utils/field_type_utils.h" |
@@ -284,4 +290,165 @@ std::vector<Literal> LiteralConverter::GetLiteralFromTimestampArray(const arrow: |
284 | 290 | } |
285 | 291 | return literals; |
286 | 292 | } |
| 293 | + |
| 294 | +arrow::TimeUnit::type LiteralConverter::MinRequiredTimeUnit(const std::vector<Literal>& literals) { |
| 295 | + bool needs_micro = false; |
| 296 | + for (const auto& literal : literals) { |
| 297 | + if (literal.IsNull()) { |
| 298 | + continue; |
| 299 | + } |
| 300 | + const int32_t nano = literal.GetValue<Timestamp>().GetNanoOfMillisecond(); |
| 301 | + if (nano % 1000 != 0) { |
| 302 | + return arrow::TimeUnit::NANO; |
| 303 | + } |
| 304 | + needs_micro = needs_micro || nano != 0; |
| 305 | + } |
| 306 | + return needs_micro ? arrow::TimeUnit::MICRO : arrow::TimeUnit::MILLI; |
| 307 | +} |
| 308 | + |
| 309 | +namespace { |
| 310 | +// Appends every literal, read by `extract`, to a `BuilderType`, keeping a null literal a null. |
| 311 | +template <typename BuilderType, typename Extract> |
| 312 | +Result<std::shared_ptr<arrow::Array>> BuildArray(const std::vector<Literal>& literals, |
| 313 | + Extract extract, arrow::MemoryPool* pool) { |
| 314 | + BuilderType builder(pool); |
| 315 | + PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.Reserve(static_cast<int64_t>(literals.size()))); |
| 316 | + for (const auto& literal : literals) { |
| 317 | + if (literal.IsNull()) { |
| 318 | + PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.AppendNull()); |
| 319 | + continue; |
| 320 | + } |
| 321 | + PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.Append(extract(literal))); |
| 322 | + } |
| 323 | + std::shared_ptr<arrow::Array> array; |
| 324 | + PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.Finish(&array)); |
| 325 | + return array; |
| 326 | +} |
| 327 | + |
| 328 | +// Writes the literals to a decimal array of the precision and the scale they carry themselves. One |
| 329 | +// array holds one arrow type, so a non null literal is needed to settle it, and every non null |
| 330 | +// literal has to carry the same pair: rescaling a value to another scale loses digits or overflows, |
| 331 | +// which is not something a conversion decides on its own. |
| 332 | +Result<std::shared_ptr<arrow::Array>> BuildDecimalArray(const std::vector<Literal>& literals, |
| 333 | + arrow::MemoryPool* pool) { |
| 334 | + std::optional<Decimal> typed_value; |
| 335 | + for (const auto& literal : literals) { |
| 336 | + if (!literal.IsNull()) { |
| 337 | + typed_value = literal.GetValue<Decimal>(); |
| 338 | + break; |
| 339 | + } |
| 340 | + } |
| 341 | + if (typed_value == std::nullopt) { |
| 342 | + return Status::Invalid( |
| 343 | + "Not support converting literals of DECIMAL type to an arrow array without a non null " |
| 344 | + "literal to take the precision and the scale from"); |
| 345 | + } |
| 346 | + int32_t precision = typed_value->Precision(); |
| 347 | + int32_t scale = typed_value->Scale(); |
| 348 | + // `arrow::decimal128` checks the precision fatally, `Make` reports it instead. |
| 349 | + PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(std::shared_ptr<arrow::DataType> type, |
| 350 | + arrow::Decimal128Type::Make(precision, scale)); |
| 351 | + arrow::Decimal128Builder builder(type, pool); |
| 352 | + PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.Reserve(static_cast<int64_t>(literals.size()))); |
| 353 | + for (const auto& literal : literals) { |
| 354 | + if (literal.IsNull()) { |
| 355 | + PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.AppendNull()); |
| 356 | + continue; |
| 357 | + } |
| 358 | + auto value = literal.GetValue<Decimal>(); |
| 359 | + if (value.Precision() != precision || value.Scale() != scale) { |
| 360 | + return Status::Invalid(fmt::format( |
| 361 | + "Not support converting literals of DECIMAL type to an arrow array, {} and {} do " |
| 362 | + "not share one precision and scale", |
| 363 | + typed_value->ToString(), value.ToString())); |
| 364 | + } |
| 365 | + PAIMON_RETURN_NOT_OK_FROM_ARROW( |
| 366 | + builder.Append(arrow::Decimal128(value.HighBits(), value.LowBits()))); |
| 367 | + } |
| 368 | + std::shared_ptr<arrow::Array> array; |
| 369 | + PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.Finish(&array)); |
| 370 | + return array; |
| 371 | +} |
| 372 | + |
| 373 | +// Writes the literals to a timestamp array of the finest time unit that keeps every value, one |
| 374 | +// `is_in` can only compare against a column of that very unit. One array holds one arrow type, so |
| 375 | +// a non null literal is needed to settle the unit. |
| 376 | +Result<std::shared_ptr<arrow::Array>> BuildTimestampArray(const std::vector<Literal>& literals, |
| 377 | + arrow::MemoryPool* pool) { |
| 378 | + bool has_value = false; |
| 379 | + for (const auto& literal : literals) { |
| 380 | + if (!literal.IsNull()) { |
| 381 | + has_value = true; |
| 382 | + break; |
| 383 | + } |
| 384 | + } |
| 385 | + if (!has_value) { |
| 386 | + return Status::Invalid( |
| 387 | + "Not support converting literals of TIMESTAMP type to an arrow array without a non " |
| 388 | + "null literal to take the time unit from"); |
| 389 | + } |
| 390 | + arrow::TimestampBuilder builder( |
| 391 | + arrow::timestamp(LiteralConverter::MinRequiredTimeUnit(literals)), pool); |
| 392 | + const DateTimeUtils::TimeType time_type = DateTimeUtils::GetTimeTypeFromArrowType( |
| 393 | + checked_pointer_cast<arrow::TimestampType>(builder.type())); |
| 394 | + PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.Reserve(static_cast<int64_t>(literals.size()))); |
| 395 | + for (const auto& literal : literals) { |
| 396 | + if (literal.IsNull()) { |
| 397 | + PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.AppendNull()); |
| 398 | + continue; |
| 399 | + } |
| 400 | + PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.Append( |
| 401 | + DateTimeUtils::TimestampToInteger(literal.GetValue<Timestamp>(), time_type))); |
| 402 | + } |
| 403 | + std::shared_ptr<arrow::Array> array; |
| 404 | + PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.Finish(&array)); |
| 405 | + return array; |
| 406 | +} |
| 407 | +} // namespace |
| 408 | + |
| 409 | +Result<std::shared_ptr<arrow::Array>> LiteralConverter::ConvertLiteralsToArray( |
| 410 | + const FieldType& field_type, const std::vector<Literal>& literals, arrow::MemoryPool* pool) { |
| 411 | + switch (field_type) { |
| 412 | + case FieldType::BOOLEAN: |
| 413 | + return BuildArray<arrow::BooleanBuilder>( |
| 414 | + literals, [](const Literal& literal) { return literal.GetValue<bool>(); }, pool); |
| 415 | + case FieldType::TINYINT: |
| 416 | + return BuildArray<arrow::Int8Builder>( |
| 417 | + literals, [](const Literal& literal) { return literal.GetValue<int8_t>(); }, pool); |
| 418 | + case FieldType::SMALLINT: |
| 419 | + return BuildArray<arrow::Int16Builder>( |
| 420 | + literals, [](const Literal& literal) { return literal.GetValue<int16_t>(); }, pool); |
| 421 | + case FieldType::INT: |
| 422 | + return BuildArray<arrow::Int32Builder>( |
| 423 | + literals, [](const Literal& literal) { return literal.GetValue<int32_t>(); }, pool); |
| 424 | + case FieldType::BIGINT: |
| 425 | + return BuildArray<arrow::Int64Builder>( |
| 426 | + literals, [](const Literal& literal) { return literal.GetValue<int64_t>(); }, pool); |
| 427 | + case FieldType::FLOAT: |
| 428 | + return BuildArray<arrow::FloatBuilder>( |
| 429 | + literals, [](const Literal& literal) { return literal.GetValue<float>(); }, pool); |
| 430 | + case FieldType::DOUBLE: |
| 431 | + return BuildArray<arrow::DoubleBuilder>( |
| 432 | + literals, [](const Literal& literal) { return literal.GetValue<double>(); }, pool); |
| 433 | + case FieldType::DATE: |
| 434 | + return BuildArray<arrow::Date32Builder>( |
| 435 | + literals, [](const Literal& literal) { return literal.GetValue<int32_t>(); }, pool); |
| 436 | + case FieldType::STRING: |
| 437 | + return BuildArray<arrow::StringBuilder>( |
| 438 | + literals, [](const Literal& literal) { return literal.GetValue<std::string>(); }, |
| 439 | + pool); |
| 440 | + case FieldType::BINARY: |
| 441 | + return BuildArray<arrow::BinaryBuilder>( |
| 442 | + literals, [](const Literal& literal) { return literal.GetValue<std::string>(); }, |
| 443 | + pool); |
| 444 | + case FieldType::DECIMAL: |
| 445 | + return BuildDecimalArray(literals, pool); |
| 446 | + case FieldType::TIMESTAMP: |
| 447 | + return BuildTimestampArray(literals, pool); |
| 448 | + default: |
| 449 | + return Status::Invalid( |
| 450 | + fmt::format("Not support converting literals of {} type to an arrow array", |
| 451 | + FieldTypeUtils::FieldTypeToString(field_type))); |
| 452 | + } |
| 453 | +} |
287 | 454 | } // namespace paimon |
0 commit comments