From 07af26514b889d6fccb2898aff568adc886e5a31 Mon Sep 17 00:00:00 2001 From: Xin Huang Date: Fri, 3 Jul 2026 14:12:38 -0700 Subject: [PATCH 1/2] reject non-finite floating-point default values --- src/iceberg/schema_field.cc | 16 ++++++++++++++ src/iceberg/test/schema_field_test.cc | 32 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/iceberg/schema_field.cc b/src/iceberg/schema_field.cc index 80ea61bce..c9ebad076 100644 --- a/src/iceberg/schema_field.cc +++ b/src/iceberg/schema_field.cc @@ -19,6 +19,7 @@ #include "iceberg/schema_field.h" +#include #include #include #include @@ -168,6 +169,21 @@ Status ValidateDefault(const SchemaField& field, const Literal& value, return InvalidSchema("{} of field {} has type {} but expected {}", kind, field.name(), *value.type(), *field.type()); } + // A non-finite float/double default cannot be represented in JSON: the serializer emits + // it as `null`, which reads back as an absent default. Reject it so the default is not + // silently lost when the metadata round-trips. The literal type already matches the + // field type here, so this only inspects the value for actual floating fields and other + // types pay nothing. + if (field.type()->type_id() == TypeId::kFloat && + !std::isfinite(std::get(value.value()))) { + return InvalidSchema("Invalid {} value for {}: value must be finite", kind, + field.name()); + } + if (field.type()->type_id() == TypeId::kDouble && + !std::isfinite(std::get(value.value()))) { + return InvalidSchema("Invalid {} value for {}: value must be finite", kind, + field.name()); + } return {}; } diff --git a/src/iceberg/test/schema_field_test.cc b/src/iceberg/test/schema_field_test.cc index d757cb0b2..6e5697977 100644 --- a/src/iceberg/test/schema_field_test.cc +++ b/src/iceberg/test/schema_field_test.cc @@ -20,11 +20,13 @@ #include "iceberg/schema_field.h" #include +#include #include #include #include "iceberg/expression/literal.h" +#include "iceberg/test/matchers.h" #include "iceberg/type.h" #include "iceberg/util/formatter.h" // IWYU pragma: keep @@ -170,4 +172,34 @@ TEST(SchemaFieldTest, CastDefaultValue) { } } +TEST(SchemaFieldTest, ValidateRejectsNonFiniteFloatingDefault) { + // NaN / infinity cannot be represented in JSON (the serializer emits `null`, which + // reads back as an absent default), so a non-finite floating default must be rejected. + SchemaField nan_field(/*field_id=*/1, /*name=*/"f", float32(), + /*optional=*/true, /*doc=*/"", + std::make_shared( + Literal::Float(std::numeric_limits::quiet_NaN()))); + EXPECT_THAT(nan_field.Validate(), IsError(ErrorKind::kInvalidSchema)); + EXPECT_THAT(nan_field.Validate(), HasErrorMessage("must be finite")); + + SchemaField inf_field(/*field_id=*/2, /*name=*/"d", float64(), + /*optional=*/true, /*doc=*/"", + std::make_shared( + Literal::Double(std::numeric_limits::infinity()))); + EXPECT_THAT(inf_field.Validate(), IsError(ErrorKind::kInvalidSchema)); + + SchemaField neg_inf_field(/*field_id=*/3, /*name=*/"d2", float64(), + /*optional=*/true, /*doc=*/"", + std::make_shared(Literal::Double( + -std::numeric_limits::infinity()))); + EXPECT_THAT(neg_inf_field.Validate(), IsError(ErrorKind::kInvalidSchema)); +} + +TEST(SchemaFieldTest, ValidateAcceptsFiniteFloatingDefault) { + SchemaField field(/*field_id=*/1, /*name=*/"f", float32(), + /*optional=*/true, /*doc=*/"", + std::make_shared(Literal::Float(1.5f))); + EXPECT_THAT(field.Validate(), IsOk()); +} + } // namespace iceberg From 88224d4f5c2f579b1a53016fc551b687cd2b637b Mon Sep 17 00:00:00 2001 From: Xin Huang Date: Sun, 12 Jul 2026 15:11:13 -0700 Subject: [PATCH 2/2] add direct include for Result to satisfy include-cleaner --- src/iceberg/schema_field.cc | 1 + src/iceberg/test/schema_field_test.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/src/iceberg/schema_field.cc b/src/iceberg/schema_field.cc index c9ebad076..d09852a04 100644 --- a/src/iceberg/schema_field.cc +++ b/src/iceberg/schema_field.cc @@ -26,6 +26,7 @@ #include #include "iceberg/expression/literal.h" +#include "iceberg/result.h" #include "iceberg/type.h" #include "iceberg/util/checked_cast.h" #include "iceberg/util/formatter.h" // IWYU pragma: keep diff --git a/src/iceberg/test/schema_field_test.cc b/src/iceberg/test/schema_field_test.cc index 6e5697977..434742ab6 100644 --- a/src/iceberg/test/schema_field_test.cc +++ b/src/iceberg/test/schema_field_test.cc @@ -26,6 +26,7 @@ #include #include "iceberg/expression/literal.h" +#include "iceberg/result.h" #include "iceberg/test/matchers.h" #include "iceberg/type.h" #include "iceberg/util/formatter.h" // IWYU pragma: keep