1919#include " arrow/api.h"
2020#include " arrow/c/bridge.h"
2121#include " gtest/gtest.h"
22+ #include " paimon/catalog/identifier.h"
2223#include " paimon/common/data/blob_defs.h"
2324#include " paimon/common/data/blob_descriptor.h"
25+ #include " paimon/common/data/blob_view_struct.h"
2426#include " paimon/common/types/data_field.h"
2527#include " paimon/data/blob.h"
2628#include " paimon/memory/memory_pool.h"
2931namespace paimon ::test {
3032
3133class BlobUtilsTest : public ::testing::Test {
32- private :
34+ public :
3335 std::shared_ptr<arrow::KeyValueMetadata> CreateBlobMetadata () {
3436 std::unordered_map<std::string, std::string> blob_metadata_map = {
3537 {BlobDefs::kExtensionTypeKey , BlobDefs::kExtensionTypeValue }};
3638 return std::make_shared<arrow::KeyValueMetadata>(blob_metadata_map);
3739 }
40+
41+ private:
42+ std::shared_ptr<MemoryPool> pool_ = GetDefaultPool();
3843};
3944
4045TEST_F (BlobUtilsTest, IsBlobMetadata) {
4146 auto correct_metadata = CreateBlobMetadata ();
42- EXPECT_TRUE (BlobUtils::IsBlobMetadata (correct_metadata));
43- EXPECT_FALSE (BlobUtils::IsBlobMetadata (nullptr ));
47+ ASSERT_TRUE (BlobUtils::IsBlobMetadata (correct_metadata));
48+ ASSERT_FALSE (BlobUtils::IsBlobMetadata (nullptr ));
4449 std::unordered_map<std::string, std::string> wrong_metadata_map = {
4550 {BlobDefs::kExtensionTypeKey , " paimon.type.varchar" }};
4651 auto wrong_metadata = std::make_shared<arrow::KeyValueMetadata>(wrong_metadata_map);
47- EXPECT_FALSE (BlobUtils::IsBlobMetadata (wrong_metadata));
52+ ASSERT_FALSE (BlobUtils::IsBlobMetadata (wrong_metadata));
4853 std::unordered_map<std::string, std::string> no_extension_metadata_map = {
4954 {" other_key" , BlobDefs::kExtensionTypeValue }};
5055 auto no_extension_metadata =
5156 std::make_shared<arrow::KeyValueMetadata>(no_extension_metadata_map);
52- EXPECT_FALSE (BlobUtils::IsBlobMetadata (no_extension_metadata));
57+ ASSERT_FALSE (BlobUtils::IsBlobMetadata (no_extension_metadata));
5358}
5459
5560TEST_F (BlobUtilsTest, IsBlobField) {
5661 std::shared_ptr<arrow::Field> blob_field = BlobUtils::ToArrowField (" f1" , true );
57- EXPECT_TRUE (BlobUtils::IsBlobField (blob_field));
62+ ASSERT_TRUE (BlobUtils::IsBlobField (blob_field));
5863
5964 auto int_field = arrow::field (" i_int" , arrow::int32 ());
60- EXPECT_FALSE (BlobUtils::IsBlobField (int_field));
65+ ASSERT_FALSE (BlobUtils::IsBlobField (int_field));
6166
6267 auto binary_field_no_meta = arrow::field (" b_no_meta" , arrow::large_binary ());
63- EXPECT_FALSE (BlobUtils::IsBlobField (binary_field_no_meta));
68+ ASSERT_FALSE (BlobUtils::IsBlobField (binary_field_no_meta));
6469
6570 auto wrong_meta = std::make_shared<arrow::KeyValueMetadata>(
6671 std::unordered_map<std::string, std::string>{{" other_key" , " value" }});
6772 auto binary_field_wrong_meta =
6873 arrow::field (" b_wrong_meta" , arrow::large_binary (), false , wrong_meta);
69- EXPECT_FALSE (BlobUtils::IsBlobField (binary_field_wrong_meta));
74+ ASSERT_FALSE (BlobUtils::IsBlobField (binary_field_wrong_meta));
7075}
7176
7277TEST_F (BlobUtilsTest, SeparateBlobSchema) {
@@ -233,7 +238,7 @@ TEST_F(BlobUtilsTest, ValidateInlineBlobDescriptorsEmptyFields) {
233238 auto struct_array =
234239 arrow::StructArray::Make ({array}, {BlobUtils::ToArrowField (" b0" )}).ValueOrDie ();
235240 auto sa = std::dynamic_pointer_cast<arrow::StructArray>(struct_array);
236- ASSERT_OK (BlobUtils::ValidateInlineBlobDescriptors (sa, {}));
241+ ASSERT_OK (BlobUtils::ValidateBlobInlineFields (sa, {}, " blob-descriptor-field " ));
237242}
238243
239244TEST_F (BlobUtilsTest, ValidateInlineBlobDescriptorsFieldNotPresent) {
@@ -245,22 +250,21 @@ TEST_F(BlobUtilsTest, ValidateInlineBlobDescriptorsFieldNotPresent) {
245250 arrow::StructArray::Make ({int_array}, {arrow::field (" f0" , arrow::int32 ())}).ValueOrDie ();
246251 auto sa = std::dynamic_pointer_cast<arrow::StructArray>(struct_array);
247252 // "b0" does not exist in the struct -> should pass
248- ASSERT_OK (BlobUtils::ValidateInlineBlobDescriptors (sa, {" b0" }));
253+ ASSERT_OK (BlobUtils::ValidateBlobInlineFields (sa, {" b0" }, " blob-descriptor-field " ));
249254}
250255
251256TEST_F (BlobUtilsTest, ValidateInlineBlobDescriptorsWithValidDescriptor) {
252257 // Valid BlobDescriptor bytes -> OK
253- auto pool = GetDefaultPool ();
254258 ASSERT_OK_AND_ASSIGN (auto descriptor, BlobDescriptor::Create (" file:///tmp/test.bin" , 0 , 100 ));
255- auto serialized = descriptor->Serialize (pool );
259+ auto serialized = descriptor->Serialize (pool_ );
256260
257261 arrow::LargeBinaryBuilder builder;
258262 ASSERT_TRUE (builder.Append (serialized->data (), serialized->size ()).ok ());
259263 auto blob_array = builder.Finish ().ValueOrDie ();
260264 auto struct_array =
261265 arrow::StructArray::Make ({blob_array}, {BlobUtils::ToArrowField (" b0" )}).ValueOrDie ();
262266 auto sa = std::dynamic_pointer_cast<arrow::StructArray>(struct_array);
263- ASSERT_OK (BlobUtils::ValidateInlineBlobDescriptors (sa, {" b0" }));
267+ ASSERT_OK (BlobUtils::ValidateBlobInlineFields (sa, {" b0" }, " blob-descriptor-field " ));
264268}
265269
266270TEST_F (BlobUtilsTest, ValidateInlineBlobDescriptorsWithNullValue) {
@@ -271,7 +275,7 @@ TEST_F(BlobUtilsTest, ValidateInlineBlobDescriptorsWithNullValue) {
271275 auto struct_array =
272276 arrow::StructArray::Make ({blob_array}, {BlobUtils::ToArrowField (" b0" )}).ValueOrDie ();
273277 auto sa = std::dynamic_pointer_cast<arrow::StructArray>(struct_array);
274- ASSERT_OK (BlobUtils::ValidateInlineBlobDescriptors (sa, {" b0" }));
278+ ASSERT_OK (BlobUtils::ValidateBlobInlineFields (sa, {" b0" }, " blob-descriptor-field " ));
275279}
276280
277281TEST_F (BlobUtilsTest, ValidateInlineBlobDescriptorsWithRawBytes) {
@@ -282,36 +286,29 @@ TEST_F(BlobUtilsTest, ValidateInlineBlobDescriptorsWithRawBytes) {
282286 auto struct_array =
283287 arrow::StructArray::Make ({blob_array}, {BlobUtils::ToArrowField (" b0" )}).ValueOrDie ();
284288 auto sa = std::dynamic_pointer_cast<arrow::StructArray>(struct_array);
285- ASSERT_NOK_WITH_MSG (
286- BlobUtils::ValidateInlineBlobDescriptors (sa, {" b0" }),
287- " BLOB inline field b0 configured by blob-descriptor-field or blob-view-field "
288- " require values to be a BlobDescriptor or BlobViewStruct." );
289+ ASSERT_NOK_WITH_MSG (BlobUtils::ValidateBlobInlineFields (sa, {" b0" }, " blob-descriptor-field" ),
290+ " BLOB inline field b0 require values to be set as corresponding type." );
289291}
290292
291293TEST_F (BlobUtilsTest, ValidateInlineBlobDescriptorsMixedValidAndInvalid) {
292294 // First row is valid descriptor, second row is raw bytes -> error on row 1
293- auto pool = GetDefaultPool ();
294295 ASSERT_OK_AND_ASSIGN (auto descriptor, BlobDescriptor::Create (" file:///tmp/test.bin" , 0 , 100 ));
295- auto serialized = descriptor->Serialize (pool);
296-
296+ auto serialized = descriptor->Serialize (pool_);
297297 arrow::LargeBinaryBuilder builder;
298298 ASSERT_TRUE (builder.Append (serialized->data (), serialized->size ()).ok ());
299299 ASSERT_TRUE (builder.Append (" raw_bytes_not_descriptor" ).ok ());
300300 auto blob_array = builder.Finish ().ValueOrDie ();
301301 auto struct_array =
302302 arrow::StructArray::Make ({blob_array}, {BlobUtils::ToArrowField (" b0" )}).ValueOrDie ();
303303 auto sa = std::dynamic_pointer_cast<arrow::StructArray>(struct_array);
304- ASSERT_NOK_WITH_MSG (
305- BlobUtils::ValidateInlineBlobDescriptors (sa, {" b0" }),
306- " BLOB inline field b0 configured by blob-descriptor-field or blob-view-field "
307- " require values to be a BlobDescriptor or BlobViewStruct." );
304+ ASSERT_NOK_WITH_MSG (BlobUtils::ValidateBlobInlineFields (sa, {" b0" }, " blob-descriptor-field" ),
305+ " BLOB inline field b0 require values to be set as corresponding type." );
308306}
309307
310308TEST_F (BlobUtilsTest, ValidateInlineBlobDescriptorsMultipleFields) {
311309 // Two inline fields: b0 is valid, b1 has raw bytes -> error on b1
312- auto pool = GetDefaultPool ();
313310 ASSERT_OK_AND_ASSIGN (auto descriptor, BlobDescriptor::Create (" file:///tmp/test.bin" , 0 , 100 ));
314- auto serialized = descriptor->Serialize (pool );
311+ auto serialized = descriptor->Serialize (pool_ );
315312
316313 arrow::LargeBinaryBuilder b0_builder;
317314 ASSERT_TRUE (b0_builder.Append (serialized->data (), serialized->size ()).ok ());
@@ -327,9 +324,83 @@ TEST_F(BlobUtilsTest, ValidateInlineBlobDescriptorsMultipleFields) {
327324 .ValueOrDie ();
328325 auto sa = std::dynamic_pointer_cast<arrow::StructArray>(struct_array);
329326 ASSERT_NOK_WITH_MSG (
330- BlobUtils::ValidateInlineBlobDescriptors (sa, {" b0" , " b1" }),
331- " BLOB inline field b1 configured by blob-descriptor-field or blob-view-field "
332- " require values to be a BlobDescriptor or BlobViewStruct." );
327+ BlobUtils::ValidateBlobInlineFields (sa, {" b0" , " b1" }, " blob-descriptor-field" ),
328+ " BLOB inline field b1 require values to be set as corresponding type." );
329+ }
330+
331+ TEST_F (BlobUtilsTest, ValidateBlobViewFieldsEmptyFields) {
332+ // Empty view_fields -> always OK
333+ arrow::LargeBinaryBuilder builder;
334+ ASSERT_TRUE (builder.Append (" random_data" ).ok ());
335+ auto array = builder.Finish ().ValueOrDie ();
336+ auto struct_array =
337+ arrow::StructArray::Make ({array}, {BlobUtils::ToArrowField (" view" )}).ValueOrDie ();
338+ auto sa = std::dynamic_pointer_cast<arrow::StructArray>(struct_array);
339+ ASSERT_OK (BlobUtils::ValidateBlobInlineFields (sa, {}, " blob-view-field" ));
340+ }
341+
342+ TEST_F (BlobUtilsTest, ValidateBlobViewFieldsFieldNotPresent) {
343+ // Field not in struct_array -> skip, OK
344+ arrow::Int32Builder int_builder;
345+ ASSERT_TRUE (int_builder.Append (42 ).ok ());
346+ auto int_array = int_builder.Finish ().ValueOrDie ();
347+ auto struct_array =
348+ arrow::StructArray::Make ({int_array}, {arrow::field (" f0" , arrow::int32 ())}).ValueOrDie ();
349+ auto sa = std::dynamic_pointer_cast<arrow::StructArray>(struct_array);
350+ ASSERT_OK (BlobUtils::ValidateBlobInlineFields (sa, {" view" }, " blob-view-field" ));
351+ }
352+
353+ TEST_F (BlobUtilsTest, ValidateBlobViewFieldsWithValidViewStruct) {
354+ // A BlobViewStruct value is accepted for a view field.
355+ BlobViewStruct view_struct (Identifier (" db" , " tbl" ), /* field_id=*/ 2 , /* row_id=*/ 5 );
356+ auto serialized = view_struct.Serialize (pool_);
357+
358+ arrow::LargeBinaryBuilder builder;
359+ ASSERT_TRUE (builder.Append (serialized->data (), serialized->size ()).ok ());
360+ auto blob_array = builder.Finish ().ValueOrDie ();
361+ auto struct_array =
362+ arrow::StructArray::Make ({blob_array}, {BlobUtils::ToArrowField (" view" )}).ValueOrDie ();
363+ auto sa = std::dynamic_pointer_cast<arrow::StructArray>(struct_array);
364+ ASSERT_OK (BlobUtils::ValidateBlobInlineFields (sa, {" view" }, " blob-view-field" ));
365+ }
366+
367+ TEST_F (BlobUtilsTest, ValidateBlobViewFieldsWithNullValue) {
368+ // Null values in view column -> skip, OK
369+ arrow::LargeBinaryBuilder builder;
370+ ASSERT_TRUE (builder.AppendNull ().ok ());
371+ auto blob_array = builder.Finish ().ValueOrDie ();
372+ auto struct_array =
373+ arrow::StructArray::Make ({blob_array}, {BlobUtils::ToArrowField (" view" )}).ValueOrDie ();
374+ auto sa = std::dynamic_pointer_cast<arrow::StructArray>(struct_array);
375+ ASSERT_OK (BlobUtils::ValidateBlobInlineFields (sa, {" view" }, " blob-view-field" ));
376+ }
377+
378+ TEST_F (BlobUtilsTest, ValidateBlobViewFieldsWithRawBytes) {
379+ // Raw bytes -> error
380+ arrow::LargeBinaryBuilder builder;
381+ ASSERT_TRUE (builder.Append (" raw_bytes_not_view" ).ok ());
382+ auto blob_array = builder.Finish ().ValueOrDie ();
383+ auto struct_array =
384+ arrow::StructArray::Make ({blob_array}, {BlobUtils::ToArrowField (" view" )}).ValueOrDie ();
385+ auto sa = std::dynamic_pointer_cast<arrow::StructArray>(struct_array);
386+ ASSERT_NOK_WITH_MSG (BlobUtils::ValidateBlobInlineFields (sa, {" view" }, " blob-view-field" ),
387+ " BLOB inline field view require values to be set as corresponding type." );
388+ }
389+
390+ TEST_F (BlobUtilsTest, ValidateBlobViewFieldsRejectsBlobDescriptor) {
391+ // A BlobDescriptor value is NOT accepted for a view field.
392+ auto pool = GetDefaultPool ();
393+ ASSERT_OK_AND_ASSIGN (auto descriptor, BlobDescriptor::Create (" file:///tmp/test.bin" , 0 , 100 ));
394+ auto serialized = descriptor->Serialize (pool);
395+
396+ arrow::LargeBinaryBuilder builder;
397+ ASSERT_TRUE (builder.Append (serialized->data (), serialized->size ()).ok ());
398+ auto blob_array = builder.Finish ().ValueOrDie ();
399+ auto struct_array =
400+ arrow::StructArray::Make ({blob_array}, {BlobUtils::ToArrowField (" view" )}).ValueOrDie ();
401+ auto sa = std::dynamic_pointer_cast<arrow::StructArray>(struct_array);
402+ ASSERT_NOK_WITH_MSG (BlobUtils::ValidateBlobInlineFields (sa, {" view" }, " blob-view-field" ),
403+ " BLOB inline field view require values to be set as corresponding type." );
333404}
334405
335406TEST_F (BlobUtilsTest, TestConvertBlobInlineDataFields) {
0 commit comments