blob: 2b1a1eb20572eb6dec7e4d0bcfabf649400768e9 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "paimon/common/predicate/literal_converter.h"
#include <optional>
#include "arrow/array/array_base.h"
#include "arrow/array/array_binary.h"
#include "arrow/array/array_decimal.h"
#include "arrow/array/array_dict.h"
#include "arrow/array/array_primitive.h"
#include "arrow/array/builder_binary.h"
#include "arrow/array/builder_decimal.h"
#include "arrow/array/builder_primitive.h"
#include "arrow/array/builder_time.h"
#include "arrow/memory_pool.h"
#include "arrow/type.h"
#include "arrow/type_traits.h"
#include "arrow/util/decimal.h"
#include "fmt/format.h"
#include "paimon/common/data/binary_string.h"
#include "paimon/common/data/internal_row.h"
#include "paimon/common/utils/arrow/status_utils.h"
#include "paimon/common/utils/checked_cast.h"
#include "paimon/common/utils/date_time_utils.h"
#include "paimon/common/utils/field_type_utils.h"
#include "paimon/common/utils/string_utils.h"
#include "paimon/data/decimal.h"
#include "paimon/data/timestamp.h"
#include "paimon/defs.h"
#include "paimon/memory/bytes.h"
#include "paimon/status.h"
namespace paimon {
Result<Literal> LiteralConverter::ConvertLiteralsFromString(const FieldType& type,
const std::string& value_str) {
switch (type) {
case FieldType::BOOLEAN: {
auto value = StringUtils::StringToValue<bool>(value_str);
if (value == std::nullopt) {
return Status::Invalid(fmt::format("cannot convert {} to BOOLEAN type", value_str));
}
return Literal(value.value());
}
case FieldType::TINYINT: {
auto value = StringUtils::StringToValue<int8_t>(value_str);
if (value == std::nullopt) {
return Status::Invalid(fmt::format("cannot convert {} to TINYINT type", value_str));
}
return Literal(value.value());
}
case FieldType::SMALLINT: {
auto value = StringUtils::StringToValue<int16_t>(value_str);
if (value == std::nullopt) {
return Status::Invalid(
fmt::format("cannot convert {} to SMALLINT type", value_str));
}
return Literal(value.value());
}
case FieldType::INT: {
auto value = StringUtils::StringToValue<int32_t>(value_str);
if (value == std::nullopt) {
return Status::Invalid(fmt::format("cannot convert {} to INT type", value_str));
}
return Literal(value.value());
}
case FieldType::DATE: {
PAIMON_ASSIGN_OR_RAISE(int32_t value, StringUtils::StringToDate(value_str));
return Literal(FieldType::DATE, value);
}
case FieldType::BIGINT: {
auto value = StringUtils::StringToValue<int64_t>(value_str);
if (value == std::nullopt) {
return Status::Invalid(fmt::format("cannot convert {} to BIGINT type", value_str));
}
return Literal(value.value());
}
case FieldType::FLOAT: {
auto value = StringUtils::StringToValue<float>(value_str);
if (value == std::nullopt) {
return Status::Invalid(fmt::format("cannot convert {} to FLOAT type", value_str));
}
return Literal(value.value());
}
case FieldType::DOUBLE: {
auto value = StringUtils::StringToValue<double>(value_str);
if (value == std::nullopt) {
return Status::Invalid(fmt::format("cannot convert {} to DOUBLE type", value_str));
}
return Literal(value.value());
}
case FieldType::STRING:
case FieldType::BINARY:
return Literal(type, value_str.data(), value_str.size());
default:
return Status::Invalid(
fmt::format("Do not support type {} in ConvertLiteralsFromString",
FieldTypeUtils::FieldTypeToString(type)));
}
}
Result<Literal> LiteralConverter::ConvertLiteralsFromRow(
const std::shared_ptr<arrow::Schema>& schema, const InternalRow& row, int32_t field_idx,
const FieldType& type) {
if (row.IsNullAt(field_idx)) {
return Literal(type);
}
switch (type) {
case FieldType::BOOLEAN:
return Literal(row.GetBoolean(field_idx));
case FieldType::TINYINT:
return Literal(static_cast<int8_t>(row.GetByte(field_idx)));
case FieldType::SMALLINT:
return Literal(row.GetShort(field_idx));
case FieldType::INT:
return Literal(row.GetInt(field_idx));
case FieldType::BIGINT:
return Literal(row.GetLong(field_idx));
case FieldType::FLOAT:
return Literal(row.GetFloat(field_idx));
case FieldType::DOUBLE:
return Literal(row.GetDouble(field_idx));
case FieldType::STRING: {
std::string field = row.GetString(field_idx).ToString();
return Literal(type, field.data(), field.size());
}
case FieldType::BINARY: {
auto field = row.GetBinary(field_idx);
return Literal(type, field->data(), field->size());
}
case FieldType::TIMESTAMP: {
const std::shared_ptr<arrow::DataType>& field_type = schema->field(field_idx)->type();
if (!field_type || field_type->id() != arrow::Type::TIMESTAMP) {
return Status::Invalid(
fmt::format("Convert literal from row not valid for schema {}, field_idx {}",
schema->ToString(), field_idx));
}
auto timestamp_type = checked_pointer_cast<arrow::TimestampType>(field_type);
int32_t precision = DateTimeUtils::GetPrecisionFromType(timestamp_type);
Timestamp field = row.GetTimestamp(field_idx, precision);
return Literal(field);
}
case FieldType::DECIMAL: {
const std::shared_ptr<arrow::DataType>& field_type = schema->field(field_idx)->type();
if (!field_type || field_type->id() != arrow::Type::DECIMAL128) {
return Status::Invalid(
fmt::format("Convert literal from row not valid for schema {}, field_idx {}",
schema->ToString(), field_idx));
}
auto* decimal_type = checked_cast<arrow::Decimal128Type*>(field_type.get());
auto precision = decimal_type->precision();
auto scale = decimal_type->scale();
Decimal field = row.GetDecimal(field_idx, precision, scale);
return Literal(field);
}
case FieldType::DATE:
return Literal(FieldType::DATE, row.GetInt(field_idx));
case FieldType::ARRAY:
case FieldType::VECTOR:
case FieldType::MAP:
case FieldType::STRUCT:
default:
return Status::Invalid(fmt::format("Convert literal from row not valid for {}",
FieldTypeUtils::FieldTypeToString(type)));
}
}
Result<std::vector<Literal>> LiteralConverter::ConvertLiteralsFromArray(const arrow::Array& array,
bool own_data) {
const auto kind = array.type_id();
switch (kind) {
case arrow::Type::type::BOOL:
return GetLiteralFromGenericArray<arrow::BooleanType>(array, FieldType::BOOLEAN);
case arrow::Type::type::INT8:
return GetLiteralFromGenericArray<arrow::Int8Type>(array, FieldType::TINYINT);
case arrow::Type::type::INT16:
return GetLiteralFromGenericArray<arrow::Int16Type>(array, FieldType::SMALLINT);
case arrow::Type::type::INT32:
return GetLiteralFromGenericArray<arrow::Int32Type>(array, FieldType::INT);
case arrow::Type::type::INT64:
return GetLiteralFromGenericArray<arrow::Int64Type>(array, FieldType::BIGINT);
case arrow::Type::type::FLOAT:
return GetLiteralFromGenericArray<arrow::FloatType>(array, FieldType::FLOAT);
case arrow::Type::type::DOUBLE:
return GetLiteralFromGenericArray<arrow::DoubleType>(array, FieldType::DOUBLE);
case arrow::Type::type::STRING:
return GetLiteralFromStringArray<arrow::StringType>(array, FieldType::STRING, own_data);
case arrow::Type::type::BINARY:
return GetLiteralFromStringArray<arrow::BinaryType>(array, FieldType::BINARY, own_data);
case arrow::Type::type::TIMESTAMP:
return GetLiteralFromTimestampArray(array);
case arrow::Type::type::DECIMAL128:
return GetLiteralFromDecimalArray(array);
case arrow::Type::type::DATE32:
return GetLiteralFromDateArray(array);
case arrow::Type::type::DICTIONARY: {
const auto& dict_array = checked_cast<const arrow::DictionaryArray&>(array);
auto* dict_type = checked_cast<arrow::DictionaryType*>(dict_array.type().get());
auto value_type_id = dict_type->value_type()->id();
auto index_type_id = dict_type->index_type()->id();
if ((value_type_id == arrow::Type::type::STRING ||
value_type_id == arrow::Type::type::BINARY) &&
index_type_id == arrow::Type::type::INT32) {
FieldType literal_type = value_type_id == arrow::Type::type::STRING
? FieldType::STRING
: FieldType::BINARY;
return GetLiteralFromDictionaryArray<arrow::BinaryArray, arrow::Int32Array>(
dict_array, literal_type, own_data);
} else if ((value_type_id == arrow::Type::type::LARGE_STRING ||
value_type_id == arrow::Type::type::LARGE_BINARY) &&
index_type_id == arrow::Type::type::INT64) {
FieldType literal_type = value_type_id == arrow::Type::type::LARGE_STRING
? FieldType::STRING
: FieldType::BINARY;
return GetLiteralFromDictionaryArray<arrow::LargeBinaryArray, arrow::Int64Array>(
dict_array, literal_type, own_data);
} else {
return Status::Invalid(
"only support [STRING|BINARY, INT32] or "
"[LARGE_STRING|LARGE_BINARY, INT64] for DictionaryArray");
}
}
default:
return Status::Invalid(
fmt::format("Not support literal on arrow {} type", array.type()->ToString()));
}
}
std::vector<Literal> LiteralConverter::GetLiteralFromDecimalArray(const arrow::Array& array) {
using ArrayType = typename arrow::TypeTraits<arrow::Decimal128Type>::ArrayType;
const auto& array_(checked_cast<const ArrayType&>(array));
auto* arrow_type = checked_cast<arrow::Decimal128Type*>(array.type().get());
int32_t precision = arrow_type->precision();
int32_t scale = arrow_type->scale();
std::vector<Literal> literals;
literals.reserve(array_.length());
for (int64_t i = 0; i < array_.length(); i++) {
if (array_.IsNull(i)) {
literals.emplace_back(FieldType::DECIMAL);
} else {
const arrow::Decimal128 decimal(array_.GetValue(i));
auto value = static_cast<Decimal::int128_t>(
static_cast<Decimal::uint128_t>(static_cast<uint64_t>(decimal.high_bits())) << 64 |
decimal.low_bits());
literals.emplace_back(Decimal(precision, scale, value));
}
}
return literals;
}
std::vector<Literal> LiteralConverter::GetLiteralFromDateArray(const arrow::Array& array) {
using ArrayType = typename arrow::TypeTraits<arrow::Date32Type>::ArrayType;
const auto& array_(checked_cast<const ArrayType&>(array));
std::vector<Literal> literals;
literals.reserve(array_.length());
for (int64_t i = 0; i < array_.length(); i++) {
if (array_.IsNull(i)) {
literals.emplace_back(FieldType::DATE);
} else {
literals.emplace_back(FieldType::DATE, array_.Value(i));
}
}
return literals;
}
std::vector<Literal> LiteralConverter::GetLiteralFromTimestampArray(const arrow::Array& array) {
using ArrayType = typename arrow::TypeTraits<arrow::TimestampType>::ArrayType;
const auto& array_(checked_cast<const ArrayType&>(array));
auto timestamp_type = checked_pointer_cast<arrow::TimestampType>(array_.type());
DateTimeUtils::TimeType time_type = DateTimeUtils::GetTimeTypeFromArrowType(timestamp_type);
std::vector<Literal> literals;
literals.reserve(array_.length());
for (int64_t i = 0; i < array_.length(); i++) {
if (array_.IsNull(i)) {
literals.emplace_back(FieldType::TIMESTAMP);
} else {
int64_t data = array_.Value(i);
auto [milli, nano] = DateTimeUtils::TimestampConverter(
data, time_type, DateTimeUtils::TimeType::MILLISECOND,
DateTimeUtils::TimeType::NANOSECOND);
literals.emplace_back(Timestamp(milli, nano));
}
}
return literals;
}
arrow::TimeUnit::type LiteralConverter::MinRequiredTimeUnit(const std::vector<Literal>& literals) {
bool needs_micro = false;
for (const auto& literal : literals) {
if (literal.IsNull()) {
continue;
}
const int32_t nano = literal.GetValue<Timestamp>().GetNanoOfMillisecond();
if (nano % 1000 != 0) {
return arrow::TimeUnit::NANO;
}
needs_micro = needs_micro || nano != 0;
}
return needs_micro ? arrow::TimeUnit::MICRO : arrow::TimeUnit::MILLI;
}
namespace {
// Appends every literal, read by `extract`, to a `BuilderType`, keeping a null literal a null.
template <typename BuilderType, typename Extract>
Result<std::shared_ptr<arrow::Array>> BuildArray(const std::vector<Literal>& literals,
Extract extract, arrow::MemoryPool* pool) {
BuilderType builder(pool);
PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.Reserve(static_cast<int64_t>(literals.size())));
for (const auto& literal : literals) {
if (literal.IsNull()) {
PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.AppendNull());
continue;
}
PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.Append(extract(literal)));
}
std::shared_ptr<arrow::Array> array;
PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.Finish(&array));
return array;
}
// Writes the literals to a decimal array of the precision and the scale they carry themselves. One
// array holds one arrow type, so a non null literal is needed to settle it, and every non null
// literal has to carry the same pair: rescaling a value to another scale loses digits or overflows,
// which is not something a conversion decides on its own.
Result<std::shared_ptr<arrow::Array>> BuildDecimalArray(const std::vector<Literal>& literals,
arrow::MemoryPool* pool) {
std::optional<Decimal> typed_value;
for (const auto& literal : literals) {
if (!literal.IsNull()) {
typed_value = literal.GetValue<Decimal>();
break;
}
}
if (typed_value == std::nullopt) {
return Status::Invalid(
"Not support converting literals of DECIMAL type to an arrow array without a non null "
"literal to take the precision and the scale from");
}
int32_t precision = typed_value->Precision();
int32_t scale = typed_value->Scale();
// `arrow::decimal128` checks the precision fatally, `Make` reports it instead.
PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(std::shared_ptr<arrow::DataType> type,
arrow::Decimal128Type::Make(precision, scale));
arrow::Decimal128Builder builder(type, pool);
PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.Reserve(static_cast<int64_t>(literals.size())));
for (const auto& literal : literals) {
if (literal.IsNull()) {
PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.AppendNull());
continue;
}
auto value = literal.GetValue<Decimal>();
if (value.Precision() != precision || value.Scale() != scale) {
return Status::Invalid(fmt::format(
"Not support converting literals of DECIMAL type to an arrow array, {} and {} do "
"not share one precision and scale",
typed_value->ToString(), value.ToString()));
}
PAIMON_RETURN_NOT_OK_FROM_ARROW(
builder.Append(arrow::Decimal128(value.HighBits(), value.LowBits())));
}
std::shared_ptr<arrow::Array> array;
PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.Finish(&array));
return array;
}
// Writes the literals to a timestamp array of the finest time unit that keeps every value, one
// `is_in` can only compare against a column of that very unit. One array holds one arrow type, so
// a non null literal is needed to settle the unit.
Result<std::shared_ptr<arrow::Array>> BuildTimestampArray(const std::vector<Literal>& literals,
arrow::MemoryPool* pool) {
bool has_value = false;
for (const auto& literal : literals) {
if (!literal.IsNull()) {
has_value = true;
break;
}
}
if (!has_value) {
return Status::Invalid(
"Not support converting literals of TIMESTAMP type to an arrow array without a non "
"null literal to take the time unit from");
}
arrow::TimestampBuilder builder(
arrow::timestamp(LiteralConverter::MinRequiredTimeUnit(literals)), pool);
const DateTimeUtils::TimeType time_type = DateTimeUtils::GetTimeTypeFromArrowType(
checked_pointer_cast<arrow::TimestampType>(builder.type()));
PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.Reserve(static_cast<int64_t>(literals.size())));
for (const auto& literal : literals) {
if (literal.IsNull()) {
PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.AppendNull());
continue;
}
PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.Append(
DateTimeUtils::TimestampToInteger(literal.GetValue<Timestamp>(), time_type)));
}
std::shared_ptr<arrow::Array> array;
PAIMON_RETURN_NOT_OK_FROM_ARROW(builder.Finish(&array));
return array;
}
} // namespace
Result<std::shared_ptr<arrow::Array>> LiteralConverter::ConvertLiteralsToArray(
const FieldType& field_type, const std::vector<Literal>& literals, arrow::MemoryPool* pool) {
switch (field_type) {
case FieldType::BOOLEAN:
return BuildArray<arrow::BooleanBuilder>(
literals, [](const Literal& literal) { return literal.GetValue<bool>(); }, pool);
case FieldType::TINYINT:
return BuildArray<arrow::Int8Builder>(
literals, [](const Literal& literal) { return literal.GetValue<int8_t>(); }, pool);
case FieldType::SMALLINT:
return BuildArray<arrow::Int16Builder>(
literals, [](const Literal& literal) { return literal.GetValue<int16_t>(); }, pool);
case FieldType::INT:
return BuildArray<arrow::Int32Builder>(
literals, [](const Literal& literal) { return literal.GetValue<int32_t>(); }, pool);
case FieldType::BIGINT:
return BuildArray<arrow::Int64Builder>(
literals, [](const Literal& literal) { return literal.GetValue<int64_t>(); }, pool);
case FieldType::FLOAT:
return BuildArray<arrow::FloatBuilder>(
literals, [](const Literal& literal) { return literal.GetValue<float>(); }, pool);
case FieldType::DOUBLE:
return BuildArray<arrow::DoubleBuilder>(
literals, [](const Literal& literal) { return literal.GetValue<double>(); }, pool);
case FieldType::DATE:
return BuildArray<arrow::Date32Builder>(
literals, [](const Literal& literal) { return literal.GetValue<int32_t>(); }, pool);
case FieldType::STRING:
return BuildArray<arrow::StringBuilder>(
literals, [](const Literal& literal) { return literal.GetValue<std::string>(); },
pool);
case FieldType::BINARY:
return BuildArray<arrow::BinaryBuilder>(
literals, [](const Literal& literal) { return literal.GetValue<std::string>(); },
pool);
case FieldType::DECIMAL:
return BuildDecimalArray(literals, pool);
case FieldType::TIMESTAMP:
return BuildTimestampArray(literals, pool);
default:
return Status::Invalid(
fmt::format("Not support converting literals of {} type to an arrow array",
FieldTypeUtils::FieldTypeToString(field_type)));
}
}
} // namespace paimon