Skip to content

Commit 6bcd33b

Browse files
committed
feat: Implement Type Casting and toString for Literals
1 parent ad3baff commit 6bcd33b

File tree

4 files changed

+65
-66
lines changed

4 files changed

+65
-66
lines changed

src/iceberg/expression/literal.cc

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@
1919

2020
#include "iceberg/expression/literal.h"
2121

22+
#include <cmath>
2223
#include <concepts>
2324
#include <cstdint>
25+
#include <string>
2426

25-
#include "iceberg/exception.h"
27+
#include "iceberg/type_fwd.h"
28+
#include "iceberg/util/macros.h"
2629

2730
namespace iceberg {
2831

@@ -100,7 +103,6 @@ Result<Literal> LiteralCaster::CastFromInt(
100103
return Literal::Double(static_cast<double>(int_val));
101104
case TypeId::kDate:
102105
return Literal::Date(int_val);
103-
// TODO(Li Feiyang): Implement cast from Int to decimal
104106
default:
105107
return NotSupported("Cast from Int to {} is not implemented",
106108
target_type->ToString());
@@ -141,7 +143,6 @@ Result<Literal> LiteralCaster::CastFromLong(
141143
return Literal::Timestamp(long_val);
142144
case TypeId::kTimestampTz:
143145
return Literal::TimestampTz(long_val);
144-
// TODO(Li Feiyang): Implement cast from Long to decimal, TimestampNs and
145146
default:
146147
return NotSupported("Cast from Long to {} is not supported",
147148
target_type->ToString());
@@ -155,7 +156,6 @@ Result<Literal> LiteralCaster::CastFromFloat(
155156
switch (target_type->type_id()) {
156157
case TypeId::kDouble:
157158
return Literal::Double(static_cast<double>(float_val));
158-
// TODO(Li Feiyang): Implement cast from Float to decimal
159159
default:
160160
return NotSupported("Cast from Float to {} is not supported",
161161
target_type->ToString());
@@ -191,10 +191,9 @@ Result<Literal> LiteralCaster::CastFromString(
191191
case TypeId::kTime:
192192
case TypeId::kTimestamp:
193193
case TypeId::kTimestampTz:
194+
case TypeId::kUuid:
194195
return NotImplemented("Cast from String to {} is not implemented yet",
195196
target_type->ToString());
196-
// TODO(Li Feiyang): Implement cast from String to uuid and decimal
197-
198197
default:
199198
return NotSupported("Cast from String to {} is not supported",
200199
target_type->ToString());
@@ -253,8 +252,7 @@ Result<Literal> LiteralCaster::CastFromFixed(
253252
const Literal& literal, const std::shared_ptr<PrimitiveType>& target_type) {
254253
switch (target_type->type_id()) {
255254
case TypeId::kBinary:
256-
return Literal::Binary(
257-
std::get<std::vector<uint8_t>>(literal.value_)); // 直接拷贝+move
255+
return Literal::Binary(std::get<std::vector<uint8_t>>(literal.value_));
258256
default:
259257
return NotSupported("Cast from Fixed to {} is not supported",
260258
target_type->ToString());
@@ -404,6 +402,13 @@ std::partial_ordering Literal::operator<=>(const Literal& other) const {
404402
}
405403

406404
std::string Literal::ToString() const {
405+
auto unsupported_error = [this]() {
406+
return std::format("ToString not supported for type: {}", type_->ToString());
407+
};
408+
auto invalid_argument = [this]() {
409+
return std::format("Invalid argument for type: {}", type_->ToString());
410+
};
411+
407412
if (std::holds_alternative<BelowMin>(value_)) {
408413
return "belowMin";
409414
}
@@ -431,14 +436,13 @@ std::string Literal::ToString() const {
431436
return std::to_string(std::get<double>(value_));
432437
}
433438
case TypeId::kString: {
434-
return std::get<std::string>(value_);
439+
return "\"" + std::get<std::string>(value_) + "\"";
435440
}
436441
case TypeId::kBinary:
437442
case TypeId::kFixed: {
438443
const auto& binary_data = std::get<std::vector<uint8_t>>(value_);
439444
std::string result = "X'";
440-
result.reserve(2 + binary_data.size() * 2 +
441-
1); // 2 chars per byte and 2 + 1 for prefix and suffix
445+
result.reserve(/*prefix*/ 2 + /*suffix*/ 1 + /*data*/ binary_data.size() * 2);
442446
for (const auto& byte : binary_data) {
443447
std::format_to(std::back_inserter(result), "{:02X}", byte);
444448
}
@@ -453,12 +457,8 @@ std::string Literal::ToString() const {
453457
case TypeId::kDate: {
454458
return std::to_string(std::get<int32_t>(value_));
455459
}
456-
case TypeId::kDecimal:
457-
case TypeId::kUuid: {
458-
throw NotImplemented("kDecimal and kUuid are not implemented yet");
459-
}
460460
default: {
461-
throw IcebergError("Unknown type: " + type_->ToString());
461+
return unsupported_error();
462462
}
463463
}
464464
}
@@ -490,6 +490,9 @@ Result<Literal> LiteralCaster::CastTo(const Literal& literal,
490490

491491
// Delegate to specific cast functions based on source type
492492
switch (source_type_id) {
493+
case TypeId::kBoolean:
494+
// No casts defined for Boolean, other than to itself.
495+
break;
493496
case TypeId::kInt:
494497
return CastFromInt(literal, target_type);
495498
case TypeId::kLong:
@@ -508,12 +511,11 @@ Result<Literal> LiteralCaster::CastTo(const Literal& literal,
508511
return CastFromTimestamp(literal, target_type);
509512
case TypeId::kTimestampTz:
510513
return CastFromTimestampTz(literal, target_type);
511-
case TypeId::kBoolean:
512514
default:
513515
break;
514516
}
515517

516-
return NotSupported("Cast from {} to {} is not implemented", literal.type_->ToString(),
518+
return NotSupported("Cast from {} to {} is not supported", literal.type_->ToString(),
517519
target_type->ToString());
518520
}
519521

src/iceberg/expression/literal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class ICEBERG_EXPORT Literal : public util::Formattable {
5757
double, // for double
5858
std::string, // for string
5959
std::vector<uint8_t>, // for binary, fixed
60-
std::array<uint8_t, 16>, // for uuid and decimal
60+
std::array<uint8_t, 16>, // for uuid
6161
BelowMin, AboveMax>;
6262

6363
/// \brief Factory methods for primitive types

src/iceberg/test/literal_test.cc

Lines changed: 43 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ void AssertCastSucceeds(const Result<Literal>& result, TypeId expected_type_id,
4040
ASSERT_THAT(result, IsOk());
4141
EXPECT_EQ(result->type()->type_id(), expected_type_id);
4242
ASSERT_NO_THROW(EXPECT_EQ(std::get<T>(result->value()), expected_value))
43-
<< "Value type mismatch in std::get. Expected type for TypeId "
43+
<< "Type mismatch in std::get. Expected type for TypeId "
4444
<< static_cast<int>(expected_type_id);
4545
}
4646

@@ -146,15 +146,15 @@ TEST(LiteralTest, LongCastTo) {
146146

147147
// Cast to Time
148148
AssertCastSucceeds(long_literal.CastTo(iceberg::time()), TypeId::kTime,
149-
static_cast<int64_t>(42L));
149+
static_cast<int64_t>(42));
150150

151151
// Cast to Timestamp
152152
AssertCastSucceeds(long_literal.CastTo(iceberg::timestamp()), TypeId::kTimestamp,
153-
static_cast<int64_t>(42L));
153+
static_cast<int64_t>(42));
154154

155155
// Cast to TimestampTz
156156
AssertCastSucceeds(long_literal.CastTo(iceberg::timestamp_tz()), TypeId::kTimestampTz,
157-
static_cast<int64_t>(42L));
157+
static_cast<int64_t>(42));
158158
}
159159

160160
TEST(LiteralTest, LongCastToOverflow) {
@@ -204,11 +204,11 @@ TEST(LiteralTest, FloatComparison) {
204204
}
205205

206206
TEST(LiteralTest, FloatCastTo) {
207-
auto float_literal = Literal::Float(3.14f);
207+
auto float_literal = Literal::Float(2.0f);
208208

209209
// Cast to Double
210210
AssertCastSucceeds(float_literal.CastTo(iceberg::float64()), TypeId::kDouble,
211-
static_cast<double>(3.14f));
211+
static_cast<double>(2.0f));
212212
}
213213

214214
// Double type tests
@@ -234,10 +234,10 @@ TEST(LiteralTest, DoubleComparison) {
234234
}
235235

236236
TEST(LiteralTest, DoubleCastTo) {
237-
auto double_literal = Literal::Double(3.14);
237+
auto double_literal = Literal::Double(2.0);
238238

239239
// Cast to Float
240-
AssertCastSucceeds(double_literal.CastTo(iceberg::float32()), TypeId::kFloat, 3.14f);
240+
AssertCastSucceeds(double_literal.CastTo(iceberg::float32()), TypeId::kFloat, 2.0f);
241241
}
242242

243243
TEST(LiteralTest, DoubleCastToOverflow) {
@@ -264,8 +264,8 @@ TEST(LiteralTest, StringBasics) {
264264
EXPECT_EQ(string_literal.type()->type_id(), TypeId::kString);
265265
EXPECT_EQ(empty_string.type()->type_id(), TypeId::kString);
266266

267-
EXPECT_EQ(string_literal.ToString(), "hello world");
268-
EXPECT_EQ(empty_string.ToString(), "");
267+
EXPECT_EQ(string_literal.ToString(), "\"hello world\"");
268+
EXPECT_EQ(empty_string.ToString(), "\"\"");
269269
}
270270

271271
TEST(LiteralTest, StringComparison) {
@@ -305,6 +305,18 @@ TEST(LiteralTest, BinaryComparison) {
305305
EXPECT_EQ(binary2 <=> binary1, std::partial_ordering::greater);
306306
}
307307

308+
TEST(LiteralTest, BinaryCastTo) {
309+
std::vector<uint8_t> data4 = {0x01, 0x02, 0x03, 0x04};
310+
auto binary_literal = Literal::Binary(data4);
311+
312+
// Cast to Fixed with matching length
313+
AssertCastSucceeds(binary_literal.CastTo(iceberg::fixed(4)), TypeId::kFixed, data4);
314+
315+
// Cast to Fixed with different length should fail
316+
EXPECT_THAT(binary_literal.CastTo(iceberg::fixed(5)),
317+
IsError(ErrorKind::kInvalidArgument));
318+
}
319+
308320
// Fixed type tests
309321
TEST(LiteralTest, FixedBasics) {
310322
std::vector<uint8_t> data = {0x01, 0x02, 0x03, 0xFF};
@@ -332,6 +344,20 @@ TEST(LiteralTest, FixedComparison) {
332344
EXPECT_EQ(fixed2 <=> fixed1, std::partial_ordering::greater);
333345
}
334346

347+
TEST(LiteralTest, FixedCastTo) {
348+
std::vector<uint8_t> data4 = {0x01, 0x02, 0x03, 0x04};
349+
auto fixed_literal = Literal::Fixed(data4);
350+
351+
// Cast to Binary
352+
AssertCastSucceeds(fixed_literal.CastTo(iceberg::binary()), TypeId::kBinary, data4);
353+
354+
// Cast to Fixed with same length
355+
AssertCastSucceeds(fixed_literal.CastTo(iceberg::fixed(4)), TypeId::kFixed, data4);
356+
357+
// Cast to Fixed with different length should fail
358+
EXPECT_THAT(fixed_literal.CastTo(iceberg::fixed(5)), IsError(ErrorKind::kNotSupported));
359+
}
360+
335361
// Date type tests
336362
TEST(LiteralTest, DateBasics) {
337363
auto date_literal = Literal::Date(19489); // May 15, 2023
@@ -422,32 +448,6 @@ TEST(LiteralTest, TimestampTzComparison) {
422448
EXPECT_EQ(timestamptz2 <=> timestamptz1, std::partial_ordering::greater);
423449
}
424450

425-
TEST(LiteralTest, BinaryCastTo) {
426-
std::vector<uint8_t> data4 = {0x01, 0x02, 0x03, 0x04};
427-
auto binary_literal = Literal::Binary(data4);
428-
429-
// Cast to Fixed with matching length
430-
AssertCastSucceeds(binary_literal.CastTo(iceberg::fixed(4)), TypeId::kFixed, data4);
431-
432-
// Cast to Fixed with different length should fail
433-
EXPECT_THAT(binary_literal.CastTo(iceberg::fixed(5)),
434-
IsError(ErrorKind::kInvalidArgument));
435-
}
436-
437-
TEST(LiteralTest, FixedCastTo) {
438-
std::vector<uint8_t> data4 = {0x01, 0x02, 0x03, 0x04};
439-
auto fixed_literal = Literal::Fixed(data4);
440-
441-
// Cast to Binary
442-
AssertCastSucceeds(fixed_literal.CastTo(iceberg::binary()), TypeId::kBinary, data4);
443-
444-
// Cast to Fixed with same length
445-
AssertCastSucceeds(fixed_literal.CastTo(iceberg::fixed(4)), TypeId::kFixed, data4);
446-
447-
// Cast to Fixed with different length should fail
448-
EXPECT_THAT(fixed_literal.CastTo(iceberg::fixed(5)), IsError(ErrorKind::kNotSupported));
449-
}
450-
451451
// Cross-type comparison tests
452452
TEST(LiteralTest, CrossTypeComparison) {
453453
auto int_literal = Literal::Int(42);
@@ -457,22 +457,18 @@ TEST(LiteralTest, CrossTypeComparison) {
457457
EXPECT_EQ(int_literal <=> string_literal, std::partial_ordering::unordered);
458458
}
459459

460-
// Special value tests
461-
TEST(LiteralTest, SpecialValues) {
460+
// Same type cast tests
461+
TEST(LiteralTest, SameTypeCast) {
462462
auto int_literal = Literal::Int(42);
463463

464-
EXPECT_FALSE(int_literal.IsAboveMax());
465-
EXPECT_FALSE(int_literal.IsBelowMin());
464+
AssertCastSucceeds(int_literal.CastTo(iceberg::int32()), TypeId::kInt, 42);
466465
}
467466

468-
// Same type cast test
469-
TEST(LiteralTest, SameTypeCast) {
467+
// Special value tests
468+
TEST(LiteralTest, SpecialValues) {
470469
auto int_literal = Literal::Int(42);
471-
472-
auto same_type_result = int_literal.CastTo(iceberg::int32());
473-
ASSERT_THAT(same_type_result, IsOk());
474-
EXPECT_EQ(same_type_result->type()->type_id(), TypeId::kInt);
475-
EXPECT_EQ(same_type_result->ToString(), "42");
470+
EXPECT_FALSE(int_literal.IsAboveMax());
471+
EXPECT_FALSE(int_literal.IsBelowMin());
476472
}
477473

478474
// Float special values tests

src/iceberg/transform_function.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
#include "iceberg/expression/literal.h"
2929
#include "iceberg/type.h"
30+
#include "iceberg/util/int128.h"
3031
#include "iceberg/util/murmurhash3_internal.h"
3132
#include "iceberg/util/truncate_util.h"
3233

0 commit comments

Comments
 (0)