Skip to content

Commit 030c221

Browse files
committed
fix: address PR #780 review comments
- Rename row_builder_internal.cc → arrow_row_builder.cc (no _internal suffix) - Move files from inspect/ to core level; rename to arrow_row_builder* - Merge NanoarrowArrayBuilder into ArrowRowBuilder as a single RAII class - Add Make(const ArrowSchema*) overload for low-level callers - Add ArrowArrayGuard::Release() and guard InitFromSchema in Make() - Move test to arrow_row_builder_test.cc in data_test target
1 parent b42f0da commit 030c221

7 files changed

Lines changed: 67 additions & 58 deletions

File tree

src/iceberg/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818
set(ICEBERG_INCLUDES "$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/src>"
1919
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>")
2020
set(ICEBERG_SOURCES
21-
arrow_c_data_util.cc
2221
arrow_c_data_guard_internal.cc
22+
arrow_c_data_util.cc
23+
arrow_row_builder.cc
2324
catalog/memory/in_memory_catalog.cc
2425
catalog/session_catalog.cc
2526
catalog/session_context.cc
@@ -45,7 +46,6 @@ set(ICEBERG_SOURCES
4546
file_writer.cc
4647
inspect/history_table.cc
4748
inspect/metadata_table.cc
48-
inspect/row_builder_internal.cc
4949
inspect/snapshots_table.cc
5050
inheritable_metadata.cc
5151
json_serde.cc

src/iceberg/arrow_c_data_guard_internal.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ class ICEBERG_EXPORT ArrowArrayGuard {
3131
explicit ArrowArrayGuard(ArrowArray* array) : array_(array) {}
3232
~ArrowArrayGuard();
3333

34+
/// \brief Release the guard without calling ArrowArrayRelease.
35+
///
36+
/// Call this when ownership of the underlying ArrowArray has been
37+
/// transferred elsewhere and the guard should not release it.
38+
void Release() { array_ = nullptr; }
39+
3440
private:
3541
ArrowArray* array_;
3642
};

src/iceberg/inspect/row_builder_internal.cc renamed to src/iceberg/arrow_row_builder.cc

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,13 @@
1717
* under the License.
1818
*/
1919

20-
#include "iceberg/inspect/row_builder_internal.h"
21-
2220
#include <utility>
2321

2422
#include <nanoarrow/nanoarrow.h>
2523

2624
#include "iceberg/arrow/nanoarrow_status_internal.h"
2725
#include "iceberg/arrow_c_data_guard_internal.h"
26+
#include "iceberg/arrow_row_builder_internal.h"
2827
#include "iceberg/schema.h"
2928
#include "iceberg/schema_internal.h"
3029

@@ -34,60 +33,64 @@ Result<ArrowRowBuilder> ArrowRowBuilder::Make(const Schema& schema) {
3433
ArrowSchema arrow_schema;
3534
ICEBERG_RETURN_UNEXPECTED(ToArrowSchema(schema, &arrow_schema));
3635
internal::ArrowSchemaGuard schema_guard(&arrow_schema);
36+
return Make(&arrow_schema);
37+
}
3738

38-
auto array = std::make_unique<ArrowArray>();
39+
Result<ArrowRowBuilder> ArrowRowBuilder::Make(const ArrowSchema* schema) {
40+
ArrowRowBuilder builder;
3941
ArrowError error;
4042
ICEBERG_NANOARROW_RETURN_UNEXPECTED_WITH_ERROR(
41-
ArrowArrayInitFromSchema(array.get(), &arrow_schema, &error), error);
42-
ICEBERG_NANOARROW_RETURN_UNEXPECTED(ArrowArrayStartAppending(array.get()));
43-
44-
return ArrowRowBuilder(std::move(array));
43+
ArrowArrayInitFromSchema(&builder.array_, schema, &error), error);
44+
// Guard the array in case StartAppending fails.
45+
internal::ArrowArrayGuard guard(&builder.array_);
46+
ICEBERG_NANOARROW_RETURN_UNEXPECTED(ArrowArrayStartAppending(&builder.array_));
47+
// Ownership stays with the builder — disarm the guard.
48+
guard.Release();
49+
return builder;
4550
}
4651

47-
ArrowRowBuilder::ArrowRowBuilder(std::unique_ptr<ArrowArray>&& array) noexcept
48-
: array_(std::move(array)) {}
49-
5052
ArrowRowBuilder::ArrowRowBuilder(ArrowRowBuilder&& other) noexcept
51-
: array_(std::move(other.array_)) {}
53+
: array_(other.array_) {
54+
other.array_.release = nullptr;
55+
}
5256

5357
ArrowRowBuilder& ArrowRowBuilder::operator=(ArrowRowBuilder&& other) noexcept {
5458
if (this != &other) {
55-
if (array_ != nullptr && array_->release != nullptr) {
56-
ArrowArrayRelease(array_.get());
59+
if (array_.release != nullptr) {
60+
ArrowArrayRelease(&array_);
5761
}
58-
array_ = std::move(other.array_);
62+
array_ = other.array_;
63+
other.array_.release = nullptr;
5964
}
6065
return *this;
6166
}
6267

6368
ArrowRowBuilder::~ArrowRowBuilder() {
64-
if (array_ != nullptr && array_->release != nullptr) {
65-
ArrowArrayRelease(array_.get());
69+
if (array_.release != nullptr) {
70+
ArrowArrayRelease(&array_);
6671
}
6772
}
6873

69-
int64_t ArrowRowBuilder::num_columns() const {
70-
return array_ == nullptr ? 0 : array_->n_children;
71-
}
74+
int64_t ArrowRowBuilder::num_columns() const { return array_.n_children; }
7275

7376
ArrowArray* ArrowRowBuilder::column(int64_t index) {
74-
if (array_ == nullptr || index < 0 || index >= array_->n_children) {
77+
if (index < 0 || index >= array_.n_children) {
7578
return nullptr;
7679
}
77-
return array_->children[index];
80+
return array_.children[index];
7881
}
7982

8083
Status ArrowRowBuilder::FinishRow() {
81-
ICEBERG_NANOARROW_RETURN_UNEXPECTED(ArrowArrayFinishElement(array_.get()));
84+
ICEBERG_NANOARROW_RETURN_UNEXPECTED(ArrowArrayFinishElement(&array_));
8285
return {};
8386
}
8487

8588
Result<ArrowArray> ArrowRowBuilder::Finish() && {
8689
ArrowError error;
8790
ICEBERG_NANOARROW_RETURN_UNEXPECTED_WITH_ERROR(
88-
ArrowArrayFinishBuildingDefault(array_.get(), &error), error);
89-
ArrowArray result = *array_;
90-
array_->release = nullptr;
91+
ArrowArrayFinishBuildingDefault(&array_, &error), error);
92+
ArrowArray result = array_;
93+
array_.release = nullptr;
9194
return result;
9295
}
9396

src/iceberg/inspect/row_builder_internal.h renamed to src/iceberg/arrow_row_builder_internal.h

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

2020
#pragma once
2121

22-
/// \file iceberg/inspect/row_builder_internal.h
22+
/// \file iceberg/arrow_row_builder_internal.h
2323
/// Internal Arrow row-building utilities shared by metadata tables.
2424
///
2525
/// Metadata tables (snapshots, history, manifests, ...) materialize in-memory
2626
/// structures into Arrow batches that conform to the table's Iceberg schema.
2727
/// `ArrowRowBuilder` wraps a nanoarrow `ArrowArray` initialized from such a
28-
/// schema and exposes per-column builders plus typed append helpers so each
28+
/// schema and exposes per-column access plus typed append helpers so each
2929
/// metadata table can emit rows without re-implementing the nanoarrow
3030
/// boilerplate.
3131

3232
#include <cstdint>
33-
#include <memory>
3433
#include <string_view>
3534
#include <unordered_map>
3635

@@ -41,7 +40,15 @@
4140

4241
namespace iceberg {
4342

44-
/// \brief Builds an Arrow struct array (a batch) for an arbitrary Iceberg schema.
43+
/// \brief Movable RAII builder that materializes rows into an Arrow struct array.
44+
///
45+
/// Handles the nanoarrow lifecycle: InitFromSchema → StartAppending →
46+
/// ... append values ... → FinishBuilding → Release.
47+
///
48+
/// Two constructors:
49+
/// - `Make(schema)` accepts an Iceberg Schema (typical for metadata tables).
50+
/// - `Make(arrow_schema)` accepts a raw ArrowSchema (for lower-level callers
51+
/// like position_delete_writer or manifest_adapter).
4552
///
4653
/// Typical usage:
4754
/// \code
@@ -55,9 +62,15 @@ namespace iceberg {
5562
/// \endcode
5663
class ICEBERG_EXPORT ArrowRowBuilder {
5764
public:
58-
/// \brief Create a row builder for the given Iceberg schema.
65+
/// \brief Create a row builder from an Iceberg schema.
5966
static Result<ArrowRowBuilder> Make(const Schema& schema);
6067

68+
/// \brief Create a row builder from an ArrowSchema.
69+
///
70+
/// The schema must outlive this call (the caller guards it). On failure the
71+
/// partially-initialized array is released automatically.
72+
static Result<ArrowRowBuilder> Make(const ArrowSchema* schema);
73+
6174
ArrowRowBuilder(ArrowRowBuilder&& other) noexcept;
6275
ArrowRowBuilder& operator=(ArrowRowBuilder&& other) noexcept;
6376

@@ -85,9 +98,8 @@ class ICEBERG_EXPORT ArrowRowBuilder {
8598
Result<ArrowArray> Finish() &&;
8699

87100
private:
88-
explicit ArrowRowBuilder(std::unique_ptr<ArrowArray>&& array) noexcept;
89-
90-
std::unique_ptr<ArrowArray> array_;
101+
ArrowRowBuilder() = default;
102+
ArrowArray array_{};
91103
};
92104

93105
/// \brief Append a null to a nanoarrow array builder.

src/iceberg/meson.build

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ iceberg_include_dir = include_directories('..')
4545
iceberg_sources = files(
4646
'arrow_c_data_guard_internal.cc',
4747
'arrow_c_data_util.cc',
48+
'arrow_row_builder.cc',
4849
'catalog/memory/in_memory_catalog.cc',
4950
'catalog/session_catalog.cc',
5051
'catalog/session_context.cc',
@@ -71,7 +72,6 @@ iceberg_sources = files(
7172
'inheritable_metadata.cc',
7273
'inspect/history_table.cc',
7374
'inspect/metadata_table.cc',
74-
'inspect/row_builder_internal.cc',
7575
'inspect/snapshots_table.cc',
7676
'json_serde.cc',
7777
'location_provider.cc',

src/iceberg/test/CMakeLists.txt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,7 @@ if(ICEBERG_BUILD_BUNDLE)
184184

185185
add_iceberg_test(catalog_test USE_BUNDLE SOURCES in_memory_catalog_test.cc)
186186

187-
add_iceberg_test(metadata_table_test
188-
USE_BUNDLE
189-
SOURCES
190-
metadata_table_test.cc
191-
row_builder_test.cc)
187+
add_iceberg_test(metadata_table_test USE_BUNDLE SOURCES metadata_table_test.cc)
192188

193189
add_iceberg_test(eval_expr_test
194190
USE_BUNDLE
@@ -249,6 +245,7 @@ if(ICEBERG_BUILD_BUNDLE)
249245
USE_BUNDLE
250246
SOURCES
251247
arrow_c_data_util_test.cc
248+
arrow_row_builder_test.cc
252249
data_writer_test.cc
253250
delete_filter_test.cc
254251
delete_loader_test.cc

src/iceberg/test/row_builder_test.cc renamed to src/iceberg/test/arrow_row_builder_test.cc

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
* under the License.
1818
*/
1919

20-
/// \file row_builder_test.cc
20+
/// \file arrow_row_builder_test.cc
2121
/// Unit tests for ArrowRowBuilder and its typed append helpers.
2222

2323
#include <memory>
@@ -28,7 +28,7 @@
2828
#include <arrow/record_batch.h>
2929
#include <gtest/gtest.h>
3030

31-
#include "iceberg/inspect/row_builder_internal.h"
31+
#include "iceberg/arrow_row_builder_internal.h"
3232
#include "iceberg/schema.h"
3333
#include "iceberg/schema_field.h"
3434
#include "iceberg/schema_internal.h"
@@ -70,9 +70,7 @@ std::shared_ptr<::arrow::RecordBatch> FinishAndImport(ArrowRowBuilder builder,
7070

7171
TEST(ArrowRowBuilderTest, BuildsRowsWithTypedValues) {
7272
auto schema = MakeTestSchema();
73-
auto builder_result = ArrowRowBuilder::Make(*schema);
74-
ASSERT_THAT(builder_result, IsOk());
75-
auto builder = std::move(*builder_result);
73+
ICEBERG_UNWRAP_OR_FAIL(auto builder, ArrowRowBuilder::Make(*schema));
7674

7775
ASSERT_EQ(builder.num_columns(), 5);
7876

@@ -122,9 +120,7 @@ TEST(ArrowRowBuilderTest, BuildsRowsWithTypedValues) {
122120

123121
TEST(ArrowRowBuilderTest, AppendsNullForOptionalColumns) {
124122
auto schema = MakeTestSchema();
125-
auto builder_result = ArrowRowBuilder::Make(*schema);
126-
ASSERT_THAT(builder_result, IsOk());
127-
auto builder = std::move(*builder_result);
123+
ICEBERG_UNWRAP_OR_FAIL(auto builder, ArrowRowBuilder::Make(*schema));
128124

129125
ASSERT_THAT(AppendInt(builder.column(0), 42), IsOk());
130126
ASSERT_THAT(AppendNull(builder.column(1)), IsOk());
@@ -147,9 +143,7 @@ TEST(ArrowRowBuilderTest, AppendsNullForOptionalColumns) {
147143

148144
TEST(ArrowRowBuilderTest, AppendsMultiEntryStringMap) {
149145
auto schema = MakeTestSchema();
150-
auto builder_result = ArrowRowBuilder::Make(*schema);
151-
ASSERT_THAT(builder_result, IsOk());
152-
auto builder = std::move(*builder_result);
146+
ICEBERG_UNWRAP_OR_FAIL(auto builder, ArrowRowBuilder::Make(*schema));
153147

154148
ASSERT_THAT(AppendInt(builder.column(0), 1), IsOk());
155149
ASSERT_THAT(AppendNull(builder.column(1)), IsOk());
@@ -166,19 +160,16 @@ TEST(ArrowRowBuilderTest, AppendsMultiEntryStringMap) {
166160

167161
TEST(ArrowRowBuilderTest, EmptyBuilderProducesZeroRowBatch) {
168162
auto schema = MakeTestSchema();
169-
auto builder_result = ArrowRowBuilder::Make(*schema);
170-
ASSERT_THAT(builder_result, IsOk());
163+
ICEBERG_UNWRAP_OR_FAIL(auto builder, ArrowRowBuilder::Make(*schema));
171164

172-
auto batch = FinishAndImport(std::move(*builder_result), *schema);
165+
auto batch = FinishAndImport(std::move(builder), *schema);
173166
EXPECT_EQ(batch->num_rows(), 0);
174167
EXPECT_EQ(batch->num_columns(), 5);
175168
}
176169

177170
TEST(ArrowRowBuilderTest, ColumnIndexOutOfRangeReturnsNull) {
178171
auto schema = MakeTestSchema();
179-
auto builder_result = ArrowRowBuilder::Make(*schema);
180-
ASSERT_THAT(builder_result, IsOk());
181-
auto builder = std::move(*builder_result);
172+
ICEBERG_UNWRAP_OR_FAIL(auto builder, ArrowRowBuilder::Make(*schema));
182173

183174
EXPECT_EQ(builder.num_columns(), 5);
184175
EXPECT_NE(builder.column(0), nullptr);

0 commit comments

Comments
 (0)