Skip to content

Commit 63063c2

Browse files
committed
modify test
1 parent 7525f27 commit 63063c2

File tree

3 files changed

+172
-156
lines changed

3 files changed

+172
-156
lines changed

src/iceberg/expression/literal.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,69 @@ class ICEBERG_EXPORT Literal {
150150
std::shared_ptr<PrimitiveType> type_;
151151
};
152152

153+
template <TypeId type_id>
154+
struct LiteralTraits {
155+
using ValueType = void;
156+
};
157+
158+
template <>
159+
struct LiteralTraits<TypeId::kBoolean> {
160+
using ValueType = bool;
161+
};
162+
163+
template <>
164+
struct LiteralTraits<TypeId::kInt> {
165+
using ValueType = int32_t;
166+
};
167+
168+
template <>
169+
struct LiteralTraits<TypeId::kDate> {
170+
using ValueType = int32_t;
171+
};
172+
173+
template <>
174+
struct LiteralTraits<TypeId::kLong> {
175+
using ValueType = int64_t;
176+
};
177+
178+
template <>
179+
struct LiteralTraits<TypeId::kTime> {
180+
using ValueType = int64_t;
181+
};
182+
183+
template <>
184+
struct LiteralTraits<TypeId::kTimestamp> {
185+
using ValueType = int64_t;
186+
};
187+
188+
template <>
189+
struct LiteralTraits<TypeId::kTimestampTz> {
190+
using ValueType = int64_t;
191+
};
192+
193+
template <>
194+
struct LiteralTraits<TypeId::kFloat> {
195+
using ValueType = float;
196+
};
197+
198+
template <>
199+
struct LiteralTraits<TypeId::kDouble> {
200+
using ValueType = double;
201+
};
202+
203+
template <>
204+
struct LiteralTraits<TypeId::kString> {
205+
using ValueType = std::string;
206+
};
207+
208+
template <>
209+
struct LiteralTraits<TypeId::kBinary> {
210+
using ValueType = std::vector<uint8_t>;
211+
};
212+
213+
template <>
214+
struct LiteralTraits<TypeId::kFixed> {
215+
using ValueType = std::vector<uint8_t>;
216+
};
217+
153218
} // namespace iceberg

src/iceberg/util/conversions.cc

Lines changed: 26 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -52,83 +52,55 @@ Result<T> ReadLittleEndian(std::span<const uint8_t> data) {
5252
return FromLittleEndian(value);
5353
}
5454

55+
template <TypeId type_id>
56+
Result<std::vector<uint8_t>> ToBytesImpl(const Literal::Value& value) {
57+
using CppType = typename LiteralTraits<type_id>::ValueType;
58+
return WriteLittleEndian(std::get<CppType>(value));
59+
}
60+
61+
#define DISPATCH_LITERAL_TO_BYTES(type_id) \
62+
case type_id: \
63+
return ToBytesImpl<type_id>(value);
64+
5565
Result<std::vector<uint8_t>> Conversions::ToBytes(const PrimitiveType& type,
5666
const Literal::Value& value) {
57-
std::vector<uint8_t> result;
5867
const auto type_id = type.type_id();
5968

6069
switch (type_id) {
70+
DISPATCH_LITERAL_TO_BYTES(TypeId::kInt)
71+
DISPATCH_LITERAL_TO_BYTES(TypeId::kDate)
72+
DISPATCH_LITERAL_TO_BYTES(TypeId::kLong)
73+
DISPATCH_LITERAL_TO_BYTES(TypeId::kTime)
74+
DISPATCH_LITERAL_TO_BYTES(TypeId::kTimestamp)
75+
DISPATCH_LITERAL_TO_BYTES(TypeId::kTimestampTz)
76+
DISPATCH_LITERAL_TO_BYTES(TypeId::kFloat)
77+
DISPATCH_LITERAL_TO_BYTES(TypeId::kDouble)
6178
case TypeId::kBoolean: {
62-
result.push_back(std::get<bool>(value) ? 0x01 : 0x00);
63-
return result;
64-
}
65-
66-
case TypeId::kInt: {
67-
result = WriteLittleEndian(std::get<int32_t>(value));
68-
return result;
69-
}
70-
71-
case TypeId::kDate: {
72-
result = WriteLittleEndian(std::get<int32_t>(value));
73-
return result;
74-
}
75-
76-
case TypeId::kLong: {
77-
result = WriteLittleEndian(std::get<int64_t>(value));
78-
return result;
79-
}
80-
81-
case TypeId::kTime: {
82-
result = WriteLittleEndian(std::get<int64_t>(value));
83-
return result;
84-
}
85-
86-
case TypeId::kTimestamp: {
87-
result = WriteLittleEndian(std::get<int64_t>(value));
88-
return result;
89-
}
90-
91-
case TypeId::kTimestampTz: {
92-
result = WriteLittleEndian(std::get<int64_t>(value));
93-
return result;
94-
}
95-
96-
case TypeId::kFloat: {
97-
result = WriteLittleEndian(std::get<float>(value));
98-
return result;
99-
}
100-
101-
case TypeId::kDouble: {
102-
result = WriteLittleEndian(std::get<double>(value));
103-
return result;
79+
return std::vector<uint8_t>{std::get<bool>(value) ? static_cast<uint8_t>(0x01)
80+
: static_cast<uint8_t>(0x00)};
10481
}
10582

10683
case TypeId::kString: {
10784
const auto& str = std::get<std::string>(value);
108-
result.insert(result.end(), str.begin(), str.end());
109-
return result;
85+
return std::vector<uint8_t>(str.begin(), str.end());
11086
}
11187

11288
case TypeId::kBinary: {
113-
const auto& binary_data = std::get<std::vector<uint8_t>>(value);
114-
result.insert(result.end(), binary_data.begin(), binary_data.end());
115-
return result;
89+
return std::get<std::vector<uint8_t>>(value);
11690
}
11791

11892
case TypeId::kFixed: {
11993
if (std::holds_alternative<std::array<uint8_t, 16>>(value)) {
12094
const auto& fixed_bytes = std::get<std::array<uint8_t, 16>>(value);
121-
result.insert(result.end(), fixed_bytes.begin(), fixed_bytes.end());
95+
return std::vector<uint8_t>(fixed_bytes.begin(), fixed_bytes.end());
12296
} else if (std::holds_alternative<std::vector<uint8_t>>(value)) {
123-
result = std::get<std::vector<uint8_t>>(value);
97+
return std::get<std::vector<uint8_t>>(value);
12498
} else {
12599
std::string actual_type = std::visit(
126100
[](auto&& arg) -> std::string { return typeid(arg).name(); }, value);
127-
128101
return InvalidArgument("Invalid value type for Fixed literal, got type: {}",
129102
actual_type);
130103
}
131-
return result;
132104
}
133105
// TODO(Li Feiyang): Add support for UUID and Decimal
134106

@@ -137,6 +109,8 @@ Result<std::vector<uint8_t>> Conversions::ToBytes(const PrimitiveType& type,
137109
}
138110
}
139111

112+
#undef DISPATCH_LITERAL_TO_BYTES
113+
140114
Result<std::vector<uint8_t>> Conversions::ToBytes(const Literal& literal) {
141115
// Cannot serialize special values
142116
if (literal.IsAboveMax()) {

test/literal_test.cc

Lines changed: 81 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -383,61 +383,6 @@ TEST(LiteralTest, DoubleZeroComparison) {
383383
EXPECT_EQ(neg_zero <=> pos_zero, std::partial_ordering::less);
384384
}
385385

386-
void CheckBinaryRoundTrip(const std::vector<uint8_t>& input_bytes,
387-
const Literal& expected_literal,
388-
std::shared_ptr<PrimitiveType> type) {
389-
// Deserialize from bytes
390-
auto literal_result = Literal::Deserialize(input_bytes, type);
391-
ASSERT_TRUE(literal_result.has_value());
392-
393-
// Check type and value are correct
394-
EXPECT_EQ(literal_result->type()->type_id(), expected_literal.type()->type_id());
395-
EXPECT_EQ(literal_result->ToString(), expected_literal.ToString());
396-
397-
// Serialize back to bytes
398-
auto bytes_result = literal_result->Serialize();
399-
ASSERT_TRUE(bytes_result.has_value());
400-
EXPECT_EQ(*bytes_result, input_bytes);
401-
402-
// Deserialize again to verify
403-
auto final_literal = Literal::Deserialize(*bytes_result, type);
404-
ASSERT_TRUE(final_literal.has_value());
405-
EXPECT_EQ(final_literal->type()->type_id(), expected_literal.type()->type_id());
406-
EXPECT_EQ(final_literal->ToString(), expected_literal.ToString());
407-
}
408-
409-
// binary serialization tests
410-
TEST(LiteralSerializationTest, BinaryBoolean) {
411-
CheckBinaryRoundTrip({1}, Literal::Boolean(true), boolean());
412-
CheckBinaryRoundTrip({0}, Literal::Boolean(false), boolean());
413-
}
414-
415-
TEST(LiteralSerializationTest, BinaryInt) {
416-
CheckBinaryRoundTrip({32, 0, 0, 0}, Literal::Int(32), int32());
417-
}
418-
419-
TEST(LiteralSerializationTest, BinaryLong) {
420-
CheckBinaryRoundTrip({32, 0, 0, 0, 0, 0, 0, 0}, Literal::Long(32), int64());
421-
}
422-
423-
TEST(LiteralSerializationTest, BinaryFloat) {
424-
CheckBinaryRoundTrip({0, 0, 128, 63}, Literal::Float(1.0f), float32());
425-
}
426-
427-
TEST(LiteralSerializationTest, BinaryDouble) {
428-
CheckBinaryRoundTrip({0, 0, 0, 0, 0, 0, 240, 63}, Literal::Double(1.0), float64());
429-
}
430-
431-
TEST(LiteralSerializationTest, BinaryString) {
432-
CheckBinaryRoundTrip({105, 99, 101, 98, 101, 114, 103}, Literal::String("iceberg"),
433-
string());
434-
}
435-
436-
TEST(LiteralSerializationTest, BinaryData) {
437-
std::vector<uint8_t> data = {0x01, 0x02, 0x03, 0xFF};
438-
CheckBinaryRoundTrip(data, Literal::Binary(data), binary());
439-
}
440-
441386
// Type promotion tests
442387
TEST(LiteralSerializationTest, TypePromotion) {
443388
// 4-byte int data can be deserialized as long
@@ -463,61 +408,93 @@ TEST(LiteralSerializationTest, TypePromotion) {
463408
EXPECT_EQ(double_bytes->size(), 8);
464409
}
465410

466-
// Edge case serialization tests
467-
TEST(LiteralSerializationTest, EdgeCases) {
468-
// Negative integers
469-
CheckBinaryRoundTrip({224, 255, 255, 255}, Literal::Int(-32), int32());
470-
CheckBinaryRoundTrip({224, 255, 255, 255, 255, 255, 255, 255}, Literal::Long(-32),
471-
int64());
411+
struct LiteralRoundTripParam {
412+
std::string test_name;
413+
std::vector<uint8_t> input_bytes;
414+
Literal expected_literal;
415+
std::shared_ptr<PrimitiveType> type;
416+
};
417+
418+
class LiteralSerializationParamTest
419+
: public ::testing::TestWithParam<LiteralRoundTripParam> {};
420+
421+
TEST_P(LiteralSerializationParamTest, RoundTrip) {
422+
const auto& param = GetParam();
423+
424+
// Deserialize from bytes
425+
Result<Literal> literal_result = Literal::Deserialize(param.input_bytes, param.type);
426+
ASSERT_TRUE(literal_result.has_value())
427+
<< "Deserialization failed: " << literal_result.error().message;
428+
429+
// Check type and value
430+
EXPECT_EQ(literal_result->type()->type_id(), param.expected_literal.type()->type_id());
431+
EXPECT_EQ(literal_result->ToString(), param.expected_literal.ToString());
472432

473-
// Empty string special handling: empty string -> empty bytes -> null conversion
433+
// Serialize back to bytes
434+
Result<std::vector<uint8_t>> bytes_result = literal_result->Serialize();
435+
ASSERT_TRUE(bytes_result.has_value())
436+
<< "Serialization failed: " << bytes_result.error().message;
437+
EXPECT_EQ(*bytes_result, param.input_bytes);
438+
439+
// Deserialize again to verify idempotency
440+
Result<Literal> final_literal = Literal::Deserialize(*bytes_result, param.type);
441+
ASSERT_TRUE(final_literal.has_value())
442+
<< "Final deserialization failed: " << final_literal.error().message;
443+
EXPECT_EQ(final_literal->type()->type_id(), param.expected_literal.type()->type_id());
444+
EXPECT_EQ(final_literal->ToString(), param.expected_literal.ToString());
445+
}
446+
447+
INSTANTIATE_TEST_SUITE_P(
448+
BinarySerializationTests, LiteralSerializationParamTest,
449+
::testing::Values(
450+
// Basic types
451+
LiteralRoundTripParam{"BooleanTrue", {1}, Literal::Boolean(true), boolean()},
452+
LiteralRoundTripParam{"BooleanFalse", {0}, Literal::Boolean(false), boolean()},
453+
LiteralRoundTripParam{"Int", {32, 0, 0, 0}, Literal::Int(32), int32()},
454+
LiteralRoundTripParam{
455+
"Long", {32, 0, 0, 0, 0, 0, 0, 0}, Literal::Long(32), int64()},
456+
LiteralRoundTripParam{"Float", {0, 0, 128, 63}, Literal::Float(1.0f), float32()},
457+
LiteralRoundTripParam{
458+
"Double", {0, 0, 0, 0, 0, 0, 240, 63}, Literal::Double(1.0), float64()},
459+
LiteralRoundTripParam{"String",
460+
{105, 99, 101, 98, 101, 114, 103},
461+
Literal::String("iceberg"),
462+
string()},
463+
LiteralRoundTripParam{"BinaryData",
464+
{0x01, 0x02, 0x03, 0xFF},
465+
Literal::Binary({0x01, 0x02, 0x03, 0xFF}),
466+
binary()},
467+
// Edge cases that fit the round-trip pattern
468+
LiteralRoundTripParam{
469+
"NegativeInt", {224, 255, 255, 255}, Literal::Int(-32), int32()},
470+
LiteralRoundTripParam{"NegativeLong",
471+
{224, 255, 255, 255, 255, 255, 255, 255},
472+
Literal::Long(-32),
473+
int64()},
474+
// IEEE 754 representation for NaN and Infinity (in little-endian)
475+
LiteralRoundTripParam{"FloatInfinity",
476+
{0, 0, 128, 127},
477+
Literal::Float(std::numeric_limits<float>::infinity()),
478+
float32()},
479+
LiteralRoundTripParam{"FloatNaN",
480+
{0, 0, 192, 127},
481+
Literal::Float(std::numeric_limits<float>::quiet_NaN()),
482+
float32()}
483+
// TODO(Li Feiyang): Add tests for Date, Time, Timestamp, TimestampTz
484+
),
485+
486+
[](const testing::TestParamInfo<LiteralSerializationParamTest::ParamType>& info) {
487+
return info.param.test_name;
488+
});
489+
490+
TEST(LiteralSerializationEdgeCaseTest, EmptyStringSerialization) {
474491
auto empty_string = Literal::String("");
475492
auto empty_bytes = empty_string.Serialize();
476493
ASSERT_TRUE(empty_bytes.has_value());
477494
EXPECT_TRUE(empty_bytes->empty());
478495

479-
// Empty bytes cause an InvalidArgument error
480-
auto null_result = Literal::Deserialize(*empty_bytes, string());
481-
EXPECT_THAT(null_result, IsError(ErrorKind::kInvalidArgument));
482-
483-
// Special floating point value serialization
484-
auto nan_float = Literal::Float(std::numeric_limits<float>::quiet_NaN());
485-
auto nan_bytes = nan_float.Serialize();
486-
ASSERT_TRUE(nan_bytes.has_value());
487-
EXPECT_EQ(nan_bytes->size(), 4);
488-
489-
auto inf_float = Literal::Float(std::numeric_limits<float>::infinity());
490-
auto inf_bytes = inf_float.Serialize();
491-
ASSERT_TRUE(inf_bytes.has_value());
492-
EXPECT_EQ(inf_bytes->size(), 4);
493-
}
494-
495-
// Error case serialization tests
496-
TEST(LiteralSerializationTest, ErrorCases) {
497-
// AboveMax/BelowMin values cannot be serialized
498-
auto long_literal =
499-
Literal::Long(static_cast<int64_t>(std::numeric_limits<int32_t>::max()) + 1);
500-
auto above_max_result = long_literal.CastTo(int32());
501-
ASSERT_TRUE(above_max_result.has_value());
502-
EXPECT_TRUE(above_max_result->IsAboveMax());
503-
504-
auto serialize_result = above_max_result->Serialize();
505-
EXPECT_FALSE(serialize_result.has_value());
506-
507-
// Insufficient data size for deserialization should fail
508-
std::vector<uint8_t> insufficient_int_data = {0x01}; // Need 4 bytes but only have 1
509-
auto insufficient_data = Literal::Deserialize(insufficient_int_data, int32());
510-
EXPECT_FALSE(insufficient_data.has_value());
511-
512-
std::vector<uint8_t> insufficient_long_data = {
513-
0x01, 0x02}; // Need 4 or 8 bytes but only have 2
514-
auto insufficient_long = Literal::Deserialize(insufficient_long_data, int64());
515-
EXPECT_FALSE(insufficient_long.has_value());
516-
517-
// Oversized decimal data should fail
518-
std::vector<uint8_t> oversized_decimal(20, 0xFF); // Exceeds 16-byte limit
519-
auto oversized_result = Literal::Deserialize(oversized_decimal, decimal(10, 2));
520-
EXPECT_FALSE(oversized_result.has_value());
496+
auto deserialize_result = Literal::Deserialize(*empty_bytes, string());
497+
EXPECT_THAT(deserialize_result, IsError(ErrorKind::kInvalidArgument));
521498
}
522499

523500
} // namespace iceberg

0 commit comments

Comments
 (0)