-
Notifications
You must be signed in to change notification settings - Fork 57
feat: Implement Type Casting and toString for Literals #206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
b599a7a
to
9dfc38a
Compare
3f912eb
to
59e57fc
Compare
cf43748
to
406a3f2
Compare
406a3f2
to
9525400
Compare
a62ac22
to
1e106ed
Compare
2beaf96
to
632ec79
Compare
src/iceberg/expression/literal.cc
Outdated
result.reserve(2 + binary_data.size() * 2 + | ||
1); // 2 chars per byte and 2 + 1 for prefix and suffix |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
result.reserve(2 + binary_data.size() * 2 + | |
1); // 2 chars per byte and 2 + 1 for prefix and suffix | |
result.reserve(/*prefix*/2 + binary_data.size() * 2 + /*suffix*/1); |
src/iceberg/expression/literal.h
Outdated
|
||
/// \brief Factory methods for decimal type | ||
static Literal Decimal(const Decimal& value, int32_t precision, int32_t scale); | ||
static Literal Decimal(int128_t value, int32_t precision, int32_t scale); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// \brief Factory methods for decimal type | |
static Literal Decimal(const Decimal& value, int32_t precision, int32_t scale); | |
static Literal Decimal(int128_t value, int32_t precision, int32_t scale); | |
static Literal Decimal(int128_t value, int32_t precision, int32_t scale); |
- Decimal type is also a primitive type so let's put them together without blank line.
- I think
int128_t
is enough and it duplicates withDecimal
src/iceberg/expression/literal.cc
Outdated
case TypeId::kDate: | ||
return Literal::Date(int_val); | ||
case TypeId::kDecimal: { | ||
auto decimal_type = std::static_pointer_cast<DecimalType>(target_type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
auto decimal_type = std::static_pointer_cast<DecimalType>(target_type); | |
auto decimal_type = internal::checked_pointer_cast<DecimalType>(target_type); |
src/iceberg/expression/literal.cc
Outdated
case TypeId::kDouble: | ||
return Literal::Double(static_cast<double>(float_val)); | ||
case TypeId::kDecimal: { | ||
auto decimal_type = std::static_pointer_cast<DecimalType>(target_type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
src/iceberg/expression/literal.cc
Outdated
case TypeId::kTimestampTz: | ||
return Literal::TimestampTz(long_val); | ||
case TypeId::kDecimal: { | ||
auto decimal_type = std::static_pointer_cast<DecimalType>(target_type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
auto decimal_type = std::static_pointer_cast<DecimalType>(target_type); | |
auto decimal_type = internal::checked_pointer_cast<DecimalType>(target_type); |
src/iceberg/expression/literal.cc
Outdated
iceberg::Decimal parsed_val; | ||
int32_t parsed_scale = 0; | ||
ICEBERG_ASSIGN_OR_RAISE( | ||
parsed_val, iceberg::Decimal::FromString(double_str, nullptr, &parsed_scale)); | ||
iceberg::Decimal rescaled_val; | ||
ICEBERG_ASSIGN_OR_RAISE(rescaled_val, | ||
parsed_val.Rescale(parsed_scale, decimal_type->scale())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
iceberg::Decimal parsed_val; | |
int32_t parsed_scale = 0; | |
ICEBERG_ASSIGN_OR_RAISE( | |
parsed_val, iceberg::Decimal::FromString(double_str, nullptr, &parsed_scale)); | |
iceberg::Decimal rescaled_val; | |
ICEBERG_ASSIGN_OR_RAISE(rescaled_val, | |
parsed_val.Rescale(parsed_scale, decimal_type->scale())); | |
int32_t parsed_scale = 0; | |
ICEBERG_ASSIGN_OR_RAISE( | |
auto parsed_val, iceberg::Decimal::FromString(double_str, /*precision=*/nullptr, &parsed_scale)); | |
iceberg::Decimal rescaled_val; | |
ICEBERG_ASSIGN_OR_RAISE(rescaled_val, | |
parsed_val.Rescale(parsed_scale, decimal_type->scale())); |
src/iceberg/expression/literal.cc
Outdated
iceberg::Decimal dec_val; | ||
ICEBERG_ASSIGN_OR_RAISE(dec_val, iceberg::Decimal::FromString(str_val)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
iceberg::Decimal dec_val; | |
ICEBERG_ASSIGN_OR_RAISE(dec_val, iceberg::Decimal::FromString(str_val)); | |
ICEBERG_ASSIGN_OR_RAISE(auto dec_val, iceberg::Decimal::FromString(str_val)); |
auto binary_val = std::get<std::vector<uint8_t>>(literal.value_); | ||
switch (target_type->type_id()) { | ||
case TypeId::kFixed: { | ||
auto target_fixed_type = std::static_pointer_cast<FixedType>(target_type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
auto target_fixed_type = std::static_pointer_cast<FixedType>(target_type); | |
auto target_fixed_type = internal::checked_pointer_cast<FixedType>(target_type); |
src/iceberg/expression/literal.cc
Outdated
auto unsupported_error = [this]() { | ||
return std::format("ToString not supported for type: {}", type_->ToString()); | ||
}; | ||
auto invalid_argument = [this]() { | ||
return std::format("Invalid argument for type: {}", type_->ToString()); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
auto unsupported_error = [this]() { | |
return std::format("ToString not supported for type: {}", type_->ToString()); | |
}; | |
auto invalid_argument = [this]() { | |
return std::format("Invalid argument for type: {}", type_->ToString()); | |
}; | |
auto invalid_literal = [this]() { | |
return std::format("Invalid literal of type: {}", type_->ToString()); | |
}; |
This function will be called in printing an expression so it is better to print a consistent short message.
src/iceberg/expression/literal.cc
Outdated
if (str_res.has_value()) { | ||
return str_res.value(); | ||
} | ||
return invalid_argument(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can use std::expected.value_or to simplify this.
// Special value tests | ||
TEST(LiteralTest, SpecialValues) { | ||
// Same type cast tests | ||
TEST(LiteralTest, SameTypeCast) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed offline, please refactor this file to use parameterized test to use common code for testing different data types.
case TypeId::kTimestamp: | ||
case TypeId::kTimestampTz: { | ||
throw IcebergError("Not implemented: ToString for " + type_->ToString()); | ||
return std::to_string(std::get<int64_t>(value_)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these switch cases can be easily rewritten by return std::to_string(std::get<typename LiteralTraits<type_id>::ValueType>(value_));
once #185 is merged.
auto unsupported_error = [this]() { | ||
return std::format("ToString not supported for type: {}", type_->ToString()); | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
auto unsupported_error = [this]() { | |
return std::format("ToString not supported for type: {}", type_->ToString()); | |
}; |
} | ||
default: { | ||
throw IcebergError("Unknown type: " + type_->ToString()); | ||
return unsupported_error(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return unsupported_error(); | |
return std::format("invalid literal of type {}", type_->ToString()); |
#include "iceberg/util/decimal.h" | ||
#include "iceberg/util/int128.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Revert these related lines?
std::string, // for string | ||
std::vector<uint8_t>, // for binary, fixed | ||
std::array<uint8_t, 16>, // for uuid and decimal | ||
std::array<uint8_t, 16>, // for uuid |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
ASSERT_THAT(min_result, IsOk()); | ||
EXPECT_TRUE(min_result->IsBelowMin()); | ||
|
||
max_result = max_long.CastTo(iceberg::date()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
max_result = max_long.CastTo(iceberg::date()); | |
max_result = max_long.CastTo(date()); |
ASSERT_THAT(double_result, IsOk()); | ||
EXPECT_EQ(double_result->type()->type_id(), TypeId::kDouble); | ||
AssertCastSucceeds(float_literal.CastTo(iceberg::float64()), TypeId::kDouble, | ||
static_cast<double>(2.0f)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static_cast<double>(2.0f)); | |
2.0); |
auto min_double = | ||
Literal::Double(-static_cast<double>(std::numeric_limits<float>::max()) * 2); | ||
|
||
auto max_result = max_double.CastTo(iceberg::float32()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
auto max_result = max_double.CastTo(iceberg::float32()); | |
auto max_result = max_double.CastTo(float32()); |
auto binary_literal = Literal::Binary(data4); | ||
|
||
// Cast to Fixed with matching length | ||
AssertCastSucceeds(binary_literal.CastTo(iceberg::fixed(4)), TypeId::kFixed, data4); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AssertCastSucceeds(binary_literal.CastTo(iceberg::fixed(4)), TypeId::kFixed, data4); | |
AssertCastSucceeds(binary_literal.CastTo(fixed(4)), TypeId::kFixed, data4); |
AssertCastSucceeds(binary_literal.CastTo(iceberg::fixed(4)), TypeId::kFixed, data4); | ||
|
||
// Cast to Fixed with different length should fail | ||
EXPECT_THAT(binary_literal.CastTo(iceberg::fixed(5)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EXPECT_THAT(binary_literal.CastTo(iceberg::fixed(5)), | |
EXPECT_THAT(binary_literal.CastTo(fixed(5)), |
iceberg::Literal
in theLiteralCaster
class to align with the Java reference implementation. This is critical for expression evaluation and predicate pushdown.ToString()
to match Java's output format for better consistency (e.g.,X'...'
for binary).ToString()
formatting.