Skip to content

Commit 8ed779f

Browse files
fix: reject malformed Arrow field IDs (#845)
1 parent 7dcc4ce commit 8ed779f

2 files changed

Lines changed: 28 additions & 8 deletions

File tree

‎src/iceberg/schema_internal.cc‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919

2020
#include "iceberg/schema_internal.h"
2121

22-
#include <cerrno>
23-
#include <charconv>
2422
#include <cstring>
2523
#include <optional>
2624
#include <string>
@@ -30,6 +28,7 @@
3028
#include "iceberg/schema.h"
3129
#include "iceberg/type.h"
3230
#include "iceberg/util/macros.h"
31+
#include "iceberg/util/string_util.h"
3332

3433
namespace iceberg {
3534

@@ -228,7 +227,7 @@ Status ToArrowSchema(const Schema& schema, ArrowSchema* out) {
228227

229228
namespace {
230229

231-
int32_t GetFieldId(const ArrowSchema& schema) {
230+
Result<int32_t> GetFieldId(const ArrowSchema& schema) {
232231
if (schema.metadata == nullptr) {
233232
return kUnknownFieldId;
234233
}
@@ -241,19 +240,21 @@ int32_t GetFieldId(const ArrowSchema& schema) {
241240
return kUnknownFieldId;
242241
}
243242

244-
int32_t field_id = kUnknownFieldId;
245-
std::from_chars(field_id_value.data, field_id_value.data + field_id_value.size_bytes,
246-
field_id);
243+
std::string_view field_id(field_id_value.data, field_id_value.size_bytes);
244+
auto field_id_result = StringUtils::ParseNumber<int32_t>(field_id);
245+
if (!field_id_result.has_value()) {
246+
return InvalidSchema("Invalid Arrow field ID: '{}'", field_id);
247+
}
247248

248-
return field_id;
249+
return field_id_result.value();
249250
}
250251

251252
Result<std::shared_ptr<Type>> FromArrowSchema(const ArrowSchema& schema) {
252253
auto to_schema_field =
253254
[](const ArrowSchema& schema) -> Result<std::unique_ptr<SchemaField>> {
254255
ICEBERG_ASSIGN_OR_RAISE(auto field_type, FromArrowSchema(schema));
255256

256-
auto field_id = GetFieldId(schema);
257+
ICEBERG_ASSIGN_OR_RAISE(auto field_id, GetFieldId(schema));
257258
bool is_optional = (schema.flags & ARROW_FLAG_NULLABLE) != 0;
258259
if (field_type->type_id() == TypeId::kUnknown && !is_optional) {
259260
return InvalidSchema("Arrow null field '{}' must be nullable", schema.name);

‎src/iceberg/test/arrow_test.cc‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,25 @@ TEST_P(FromArrowSchemaTest, PrimitiveType) {
387387
ASSERT_EQ(*field.type(), *param.iceberg_type);
388388
}
389389

390+
TEST(FromArrowSchemaTest, RejectMalformedFieldIdMetadata) {
391+
for (const auto& field_id : {"1x", "2147483648", ""}) {
392+
auto metadata =
393+
::arrow::key_value_metadata(std::unordered_map<std::string, std::string>{
394+
{std::string(kParquetFieldIdKey), field_id}});
395+
auto arrow_schema = ::arrow::schema({::arrow::field(
396+
"foo", ::arrow::int32(), /*nullable=*/true, std::move(metadata))});
397+
ArrowSchema exported_schema;
398+
ASSERT_TRUE(::arrow::ExportSchema(*arrow_schema, &exported_schema).ok());
399+
400+
auto result = FromArrowSchema(exported_schema, /*schema_id=*/1);
401+
ArrowSchemaRelease(&exported_schema);
402+
403+
EXPECT_THAT(result, IsError(ErrorKind::kInvalidSchema));
404+
EXPECT_THAT(result,
405+
HasErrorMessage(std::format("Invalid Arrow field ID: '{}'", field_id)));
406+
}
407+
}
408+
390409
INSTANTIATE_TEST_SUITE_P(
391410
SchemaConversion, FromArrowSchemaTest,
392411
::testing::Values(

0 commit comments

Comments
 (0)