Skip to content

Commit 727b1d3

Browse files
SteNicholasclaude
andcommitted
feat(variant): support nested-ROW variant shredding and align with Java
- Discover, infer and shred VARIANT columns nested inside ROW types on the write path and reassemble them on the read path (struct-only recursion by field-index path, as in Java) - Share one variant.shredding.maxSchemaWidth budget across all variant columns of a schema (the Java MaxFields semantics) - Parse and validate the variant.* options in CoreOptions::FromMap and create VariantShreddingWritePlanFactory from the validated options instead of deferring configuration errors to write time - Write files unshredded when the configured shredding schema matches no variant column, as in Java - Skip rows whose enclosing struct is null when sampling and shredding nested variants; child slot contents under a null ancestor are unspecified in Arrow and must not be decoded - Accept microsecond TIMESTAMP typed_value columns when building variant schemas and reject other precisions - Render DOUBLE/FLOAT to STRING casts with Java Double/Float.toString semantics and emit at least two significant digits in scientific form (Double.MIN_VALUE is 4.9E-324) - Reassemble within the caller-provided memory pools, include an error message in MalformedVariant, and move output parameters to the end of parameter lists - Use explicit types in PAIMON_ASSIGN_OR_RAISE, checked_cast for known array types, a pure-virtual CreateMetadataFinalizer, and document the ShreddingFileReader position in the reader chains - Extend tests: CoreOptions variant options, shared-budget, nested and null-ancestor shredding, Java Double/Float.toString special values, variant-null cast semantics, deep-nested JSON and IO-exception integration cases Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 8565e76 commit 727b1d3

45 files changed

Lines changed: 1431 additions & 482 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/source/user_guide/data_types.rst

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -241,14 +241,17 @@ and `Arrow DataTypes <https://arrow.apache.org/docs/format/Columnar.html#data-ty
241241
parquet columns per the parquet-format
242242
`Variant Shredding <https://github.com/apache/parquet-format/blob/master/VariantShredding.md>`_
243243
specification by setting ``variant.shreddingSchema`` to a ROW type JSON
244-
whose fields map variant column names to their shredding types.
245-
Alternatively, setting ``variant.inferShreddingSchema`` to ``true``
246-
infers a shredding schema per file from the first written rows
247-
(tuned by ``variant.shredding.maxSchemaWidth``,
244+
whose fields map top-level variant column names to their shredding
245+
types. Alternatively, setting ``variant.inferShreddingSchema`` to
246+
``true`` infers a shredding schema per file from the first written rows
247+
(tuned by ``variant.shredding.maxSchemaWidth``, which bounds the total
248+
number of shredded fields across all variant columns of the schema,
248249
``variant.shredding.maxSchemaDepth``,
249250
``variant.shredding.minFieldCardinalityRatio`` and
250-
``variant.shredding.maxInferBufferRow``). Readers reassemble shredded
251-
columns transparently.
251+
``variant.shredding.maxInferBufferRow``). Inference also covers variant
252+
columns nested inside ROW columns (variants inside arrays or maps stay
253+
unshredded, as in Java Paimon). Readers reassemble shredded columns
254+
transparently.
252255

253256
When reading, instead of the full variant, specific paths can be
254257
extracted by replacing the variant column in the read schema with a

src/paimon/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ if(PAIMON_BUILD_TESTS)
466466
common/data/variant/infer_variant_shredding_schema_test.cpp
467467
common/data/variant/variant_shredding_write_plan_factory_test.cpp
468468
common/data/variant/variant_get_test.cpp
469+
common/data/variant/variant_json_utils_test.cpp
469470
common/data/variant/variant_shredding_test.cpp
470471
common/data/variant/variant_test.cpp
471472
common/data/variant/variant_type_utils_test.cpp

src/paimon/common/data/shredding/shredding_write_plan_factories.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ std::shared_ptr<ShreddingWritePlanFactory> ShreddingWritePlanFactories::SelectAc
3535
return map_factory;
3636
}
3737
}
38-
auto variant_factory =
39-
std::make_shared<VariantShreddingWritePlanFactory>(options, write_schema, pool);
38+
auto variant_factory = VariantShreddingWritePlanFactory::Create(options, write_schema, pool);
4039
if (variant_factory->ShouldCreateWritePlan()) {
4140
return variant_factory;
4241
}

src/paimon/common/data/shredding/shredding_write_plan_factory.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,7 @@ class ShreddingWritePlanFactory {
6161
/// The per-file metadata finalizer persisted into the file footer, or nullptr when the
6262
/// physical schema is self-describing (as it is for VARIANT shredding).
6363
virtual MetadataFinalizer CreateMetadataFinalizer(
64-
const std::shared_ptr<ShreddingBatchConverter>& converter) const {
65-
return nullptr;
66-
}
64+
const std::shared_ptr<ShreddingBatchConverter>& converter) const = 0;
6765
};
6866

6967
} // namespace paimon

src/paimon/common/data/variant/generic_variant.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <utility>
2626

2727
#include "arrow/util/base64.h"
28+
#include "fmt/format.h"
2829
#include "paimon/common/data/variant/variant_builder.h"
2930
#include "paimon/common/data/variant/variant_defs.h"
3031
#include "paimon/common/data/variant/variant_json_utils.h"
@@ -170,7 +171,7 @@ Status ToJsonImpl(std::string_view value, std::string_view metadata, int32_t pos
170171
return Status::OK();
171172
}
172173
}
173-
return VariantBinaryUtil::MalformedVariant();
174+
return VariantBinaryUtil::MalformedVariant("unknown variant value type in JSON rendering");
174175
}
175176

176177
} // namespace
@@ -187,7 +188,7 @@ Result<std::shared_ptr<GenericVariant>> GenericVariant::Create(std::shared_ptr<B
187188
// There is currently only one allowed version.
188189
if (metadata->size() < 1 || (static_cast<uint8_t>((*metadata)[0]) &
189190
VariantDefs::kVersionMask) != VariantDefs::kVersion) {
190-
return VariantBinaryUtil::MalformedVariant();
191+
return VariantBinaryUtil::MalformedVariant("unsupported variant metadata version");
191192
}
192193
// Don't attempt to use a Variant larger than 128 MiB. We'll never produce one, and it risks
193194
// memory instability.
@@ -389,7 +390,8 @@ Result<int32_t> GenericVariant::GetDictionaryIdAtIndex(int32_t index) const {
389390
PAIMON_ASSIGN_OR_RAISE(VariantBinaryUtil::ObjectInfo info,
390391
VariantBinaryUtil::GetObjectInfo(raw, pos_));
391392
if (index < 0 || index >= info.num_elements) {
392-
return VariantBinaryUtil::MalformedVariant();
393+
return VariantBinaryUtil::MalformedVariant(fmt::format(
394+
"object field index {} is out of bounds for {} fields", index, info.num_elements));
393395
}
394396
return VariantBinaryUtil::ReadUnsigned(raw, info.id_start + info.id_size * index, info.id_size);
395397
}

src/paimon/common/data/variant/infer_variant_shredding_schema.cpp

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,6 @@ struct SimpleSchema {
5959
}
6060
};
6161

62-
// Tracks the total number of shredded fields remaining, shared across the whole schema.
63-
struct MaxFields {
64-
int32_t remaining;
65-
};
66-
6762
std::shared_ptr<SimpleSchema> MergeSchema(const std::shared_ptr<SimpleSchema>& s1,
6863
const std::shared_ptr<SimpleSchema>& s2);
6964

@@ -180,9 +175,10 @@ Result<std::shared_ptr<SimpleSchema>> SchemaOf(const GenericVariant& variant, in
180175
result->is_object = true;
181176
result->fields.reserve(size);
182177
for (int32_t i = 0; i < size; ++i) {
183-
PAIMON_ASSIGN_OR_RAISE(auto field, variant.GetFieldAtIndex(i));
178+
PAIMON_ASSIGN_OR_RAISE(std::optional<GenericVariant::ObjectField> field,
179+
variant.GetFieldAtIndex(i));
184180
if (!field.has_value()) {
185-
return VariantBinaryUtil::MalformedVariant();
181+
return VariantBinaryUtil::MalformedVariant("an object field is missing");
186182
}
187183
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<SimpleSchema> field_schema,
188184
SchemaOf(*field->value, max_depth - 1));
@@ -275,9 +271,9 @@ Result<std::shared_ptr<SimpleSchema>> SchemaOf(const GenericVariant& variant, in
275271

276272
// Finalizes the inferred schema: 1) widen integer types to int64, 2) replace empty objects with
277273
// VARIANT, 3) limit the total number of shredded fields in the schema.
278-
std::shared_ptr<arrow::DataType> FinalizeSimpleSchema(const std::shared_ptr<SimpleSchema>& schema,
279-
int64_t min_cardinality,
280-
MaxFields* max_fields) {
274+
std::shared_ptr<arrow::DataType> FinalizeSimpleSchema(
275+
const std::shared_ptr<SimpleSchema>& schema, int64_t min_cardinality,
276+
InferVariantShreddingSchema::MaxFields* max_fields) {
281277
// Every field uses a value column.
282278
--max_fields->remaining;
283279
if (max_fields->remaining <= 0) {
@@ -332,7 +328,7 @@ std::shared_ptr<arrow::DataType> FinalizeSimpleSchema(const std::shared_ptr<Simp
332328
} // namespace
333329

334330
Result<std::shared_ptr<arrow::DataType>> InferVariantShreddingSchema::InferColumnShreddingType(
335-
const std::vector<std::shared_ptr<GenericVariant>>& samples) const {
331+
const std::vector<std::shared_ptr<GenericVariant>>& samples, MaxFields* max_fields) const {
336332
int64_t num_non_null_values = 0;
337333
std::shared_ptr<SimpleSchema> simple_schema;
338334
for (const auto& sample : samples) {
@@ -348,9 +344,8 @@ Result<std::shared_ptr<arrow::DataType>> InferVariantShreddingSchema::InferColum
348344
// the rows.
349345
auto min_cardinality = static_cast<int64_t>(
350346
std::ceil(static_cast<double>(num_non_null_values) * min_field_cardinality_ratio_));
351-
MaxFields max_fields{max_schema_width_};
352347
std::shared_ptr<arrow::DataType> finalized =
353-
FinalizeSimpleSchema(simple_schema, min_cardinality, &max_fields);
348+
FinalizeSimpleSchema(simple_schema, min_cardinality, max_fields);
354349
if (finalized->id() == arrow::Type::NA) {
355350
// The whole column stays unshredded.
356351
return std::shared_ptr<arrow::DataType>(nullptr);

src/paimon/common/data/variant/infer_variant_shredding_schema.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,30 @@ namespace paimon {
3434
/// fields is limited.
3535
class InferVariantShreddingSchema {
3636
public:
37+
/// The mutable budget of shredded fields remaining. One instance is shared across all
38+
/// variant columns of a schema so that the total inferred width stays within
39+
/// `variant.shredding.maxSchemaWidth` (mirroring the Java `MaxFields`).
40+
struct MaxFields {
41+
int32_t remaining;
42+
};
43+
3744
InferVariantShreddingSchema(int32_t max_schema_width, int32_t max_schema_depth,
3845
double min_field_cardinality_ratio)
3946
: max_schema_width_(max_schema_width),
4047
max_schema_depth_(max_schema_depth),
4148
min_field_cardinality_ratio_(min_field_cardinality_ratio) {}
4249

50+
/// Creates the shared shredded-field budget for one schema inference.
51+
MaxFields CreateMaxFieldsBudget() const {
52+
return MaxFields{max_schema_width_};
53+
}
54+
4355
/// Infers the shredding type of one variant column from its sampled non-null values, e.g.
4456
/// `struct{a: int64, b: string}`. `arrow::null()` leaves denote untyped variant sub-values.
45-
/// Returns nullptr when no useful shredding schema was found (the column should stay
46-
/// unshredded).
57+
/// `max_fields` is the budget shared across all columns of the schema. Returns nullptr when
58+
/// no useful shredding schema was found (the column should stay unshredded).
4759
Result<std::shared_ptr<arrow::DataType>> InferColumnShreddingType(
48-
const std::vector<std::shared_ptr<GenericVariant>>& samples) const;
60+
const std::vector<std::shared_ptr<GenericVariant>>& samples, MaxFields* max_fields) const;
4961

5062
private:
5163
int32_t max_schema_width_;

src/paimon/common/data/variant/infer_variant_shredding_schema_test.cpp

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ namespace paimon::test {
3030

3131
class InferVariantShreddingSchemaTest : public ::testing::Test {
3232
public:
33+
// Infers one column with a fresh shared-width budget.
34+
static Result<std::shared_ptr<arrow::DataType>> InferColumn(
35+
const InferVariantShreddingSchema& infer,
36+
const std::vector<std::shared_ptr<GenericVariant>>& samples) {
37+
InferVariantShreddingSchema::MaxFields max_fields = infer.CreateMaxFieldsBudget();
38+
return infer.InferColumnShreddingType(samples, &max_fields);
39+
}
40+
3341
std::vector<std::shared_ptr<GenericVariant>> Samples(const std::vector<const char*>& jsons) {
3442
std::vector<std::shared_ptr<GenericVariant>> samples;
3543
for (const char* json : jsons) {
@@ -57,8 +65,7 @@ TEST_F(InferVariantShreddingSchemaTest, InferObjectSchema) {
5765
nullptr,
5866
R"({"age": 120000000000, "city": "Shanghai"})",
5967
});
60-
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> inferred,
61-
infer_.InferColumnShreddingType(samples));
68+
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> inferred, InferColumn(infer_, samples));
6269
ASSERT_NE(inferred, nullptr);
6370
// Integers widen to int64, strings stay, arrays of small ints infer as list<int64>.
6471
auto expected =
@@ -73,8 +80,7 @@ TEST_F(InferVariantShreddingSchemaTest, MixedTypesFallToVariant) {
7380
R"({"x": 1, "y": 1.5e0})",
7481
R"({"x": "string now", "y": 2.5e0})",
7582
});
76-
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> inferred,
77-
infer_.InferColumnShreddingType(samples));
83+
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> inferred, InferColumn(infer_, samples));
7884
ASSERT_NE(inferred, nullptr);
7985
// x saw both int and string: untyped variant leaf; y stays double (exponent notation
8086
// parses as double, plain decimals parse as DECIMAL).
@@ -91,8 +97,7 @@ TEST_F(InferVariantShreddingSchemaTest, RareFieldsDropped) {
9197
}
9298
jsons.push_back(R"({"common": 2, "rare": true})");
9399
auto samples = Samples(jsons);
94-
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> inferred,
95-
infer_.InferColumnShreddingType(samples));
100+
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> inferred, InferColumn(infer_, samples));
96101
ASSERT_NE(inferred, nullptr);
97102
// "rare" appears in 1/20 rows (< 0.1 ratio): dropped from the typed schema.
98103
auto expected = arrow::struct_({arrow::field("common", arrow::int64())});
@@ -105,8 +110,7 @@ TEST_F(InferVariantShreddingSchemaTest, DecimalMerging) {
105110
"{\"d\": 1.5}",
106111
"{\"d\": 42}",
107112
});
108-
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> inferred,
109-
infer_.InferColumnShreddingType(samples));
113+
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> inferred, InferColumn(infer_, samples));
110114
ASSERT_NE(inferred, nullptr);
111115
// Decimals merge to a widened decimal (scale 2, enough integer digits), capped at 18 digits.
112116
auto expected = arrow::struct_({arrow::field("d", arrow::decimal128(18, 2))});
@@ -116,20 +120,20 @@ TEST_F(InferVariantShreddingSchemaTest, DecimalMerging) {
116120
TEST_F(InferVariantShreddingSchemaTest, NoUsefulSchema) {
117121
auto scalar_samples = Samples({"1", "2"});
118122
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> scalar_inferred,
119-
infer_.InferColumnShreddingType(scalar_samples));
123+
InferColumn(infer_, scalar_samples));
120124
ASSERT_NE(scalar_inferred, nullptr);
121125
ASSERT_TRUE(scalar_inferred->Equals(*arrow::int64())) << scalar_inferred->ToString();
122126

123127
// Conflicting top-level types stay unshredded.
124128
auto mixed_samples = Samples({"1", "\"a string\""});
125129
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> mixed_inferred,
126-
infer_.InferColumnShreddingType(mixed_samples));
130+
InferColumn(infer_, mixed_samples));
127131
ASSERT_EQ(mixed_inferred, nullptr);
128132

129133
// All-null columns stay unshredded.
130134
auto null_samples = Samples({nullptr, nullptr});
131135
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> null_inferred,
132-
infer_.InferColumnShreddingType(null_samples));
136+
InferColumn(infer_, null_samples));
133137
ASSERT_EQ(null_inferred, nullptr);
134138
}
135139

@@ -138,19 +142,33 @@ TEST_F(InferVariantShreddingSchemaTest, MaxSchemaWidthLimit) {
138142
/*min_field_cardinality_ratio=*/0.1};
139143
auto samples = Samples({R"({"a": 1, "b": 2, "c": 3, "d": 4, "e": 5})"});
140144
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> inferred,
141-
narrow_infer.InferColumnShreddingType(samples));
142-
if (inferred != nullptr) {
143-
ASSERT_EQ(inferred->id(), arrow::Type::STRUCT);
144-
ASSERT_LT(inferred->num_fields(), 5);
145-
}
145+
InferColumn(narrow_infer, samples));
146+
ASSERT_NE(inferred, nullptr);
147+
// Budget of 3: the root object costs 1, "a" costs 2 (value + typed_value); the remaining
148+
// fields exceed the budget and are dropped from the typed schema.
149+
auto expected = arrow::struct_({arrow::field("a", arrow::int64())});
150+
ASSERT_TRUE(inferred->Equals(*expected)) << inferred->ToString();
151+
152+
// One budget serves all variant columns of a schema: after the first column consumes it, a
153+
// second column cannot shred anymore.
154+
InferVariantShreddingSchema::MaxFields max_fields = narrow_infer.CreateMaxFieldsBudget();
155+
auto first_samples = Samples({R"({"a": 1, "b": 2})"});
156+
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> first,
157+
narrow_infer.InferColumnShreddingType(first_samples, &max_fields));
158+
ASSERT_NE(first, nullptr);
159+
ASSERT_TRUE(first->Equals(*expected)) << first->ToString();
160+
auto second_samples = Samples({R"({"c": 1})"});
161+
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> second,
162+
narrow_infer.InferColumnShreddingType(second_samples, &max_fields));
163+
ASSERT_EQ(second, nullptr);
146164
}
147165

148166
TEST_F(InferVariantShreddingSchemaTest, MaxSchemaDepthLimit) {
149167
InferVariantShreddingSchema shallow_infer{/*max_schema_width=*/300, /*max_schema_depth=*/1,
150168
/*min_field_cardinality_ratio=*/0.1};
151169
auto samples = Samples({R"({"outer": {"inner": 1}})"});
152170
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> inferred,
153-
shallow_infer.InferColumnShreddingType(samples));
171+
InferColumn(shallow_infer, samples));
154172
ASSERT_NE(inferred, nullptr);
155173
// Depth 1: the nested object stays an untyped variant leaf.
156174
auto expected = arrow::struct_({arrow::field("outer", arrow::null())});
@@ -162,14 +180,13 @@ TEST_F(InferVariantShreddingSchemaTest, TrailingZeroDecimalNormalized) {
162180
// scale or reassembling the shredded file would be rejected. After normalization the value
163181
// is an integral decimal, which finalization widens to int64.
164182
auto samples = Samples({"100.00"});
165-
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> inferred,
166-
infer_.InferColumnShreddingType(samples));
183+
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> inferred, InferColumn(infer_, samples));
167184
ASSERT_NE(inferred, nullptr);
168185
ASSERT_TRUE(inferred->Equals(*arrow::int64())) << inferred->ToString();
169186

170187
auto mixed = Samples({"100.00", "1.5"});
171188
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> mixed_inferred,
172-
infer_.InferColumnShreddingType(mixed));
189+
InferColumn(infer_, mixed));
173190
ASSERT_NE(mixed_inferred, nullptr);
174191
ASSERT_TRUE(mixed_inferred->Equals(*arrow::decimal128(18, 1))) << mixed_inferred->ToString();
175192
}
@@ -181,14 +198,14 @@ TEST_F(InferVariantShreddingSchemaTest, TemporalValuesStayUnshredded) {
181198
ASSERT_OK(date_builder.AppendDate(19000));
182199
ASSERT_OK_AND_ASSIGN(std::shared_ptr<GenericVariant> date_variant, date_builder.Build(pool_));
183200
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> date_inferred,
184-
infer_.InferColumnShreddingType({date_variant}));
201+
InferColumn(infer_, {date_variant}));
185202
ASSERT_EQ(date_inferred, nullptr);
186203

187204
VariantBuilder ts_builder(/*allow_duplicate_keys=*/false);
188205
ASSERT_OK(ts_builder.AppendTimestamp(1700000000000000));
189206
ASSERT_OK_AND_ASSIGN(std::shared_ptr<GenericVariant> ts_variant, ts_builder.Build(pool_));
190207
ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> ts_inferred,
191-
infer_.InferColumnShreddingType({ts_variant}));
208+
InferColumn(infer_, {ts_variant}));
192209
ASSERT_EQ(ts_inferred, nullptr);
193210
}
194211

src/paimon/common/data/variant/variant.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@ class Variant::Impl {
4545
return pool_;
4646
}
4747

48-
arrow::MemoryPool* GetArrowMemoryPool() const {
49-
return arrow_pool_.get();
48+
const std::shared_ptr<arrow::MemoryPool>& GetArrowMemoryPool() const {
49+
return arrow_pool_;
5050
}
5151

5252
private:
5353
std::shared_ptr<GenericVariant> variant_;
5454
std::shared_ptr<MemoryPool> pool_;
55-
std::unique_ptr<arrow::MemoryPool> arrow_pool_;
55+
std::shared_ptr<arrow::MemoryPool> arrow_pool_;
5656
};
5757

5858
Variant::Variant(std::unique_ptr<Impl>&& impl) : impl_(std::move(impl)) {}
@@ -148,7 +148,7 @@ Status VariantAccessBuilder::AddField(struct ArrowSchema* target_type, const std
148148
PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(std::shared_ptr<arrow::Field> target,
149149
arrow::ImportField(target_type));
150150
// Validate the path eagerly so mistakes fail at build time, not at read time.
151-
PAIMON_RETURN_NOT_OK(VariantPathSegment::Parse(path).status());
151+
PAIMON_RETURN_NOT_OK(VariantPathSegment::Parse(path));
152152
// Keep the target field's own metadata (e.g. the variant extension marker of a
153153
// `Variant::ArrowField` target, which drives the deep re-encode cast) and add the access
154154
// description to it.

0 commit comments

Comments
 (0)