Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/iceberg/schema_field.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "iceberg/result.h"
#include "iceberg/type.h"
#include "iceberg/util/checked_cast.h"
#include "iceberg/util/decimal.h"
#include "iceberg/util/formatter.h" // IWYU pragma: keep
#include "iceberg/util/macros.h"

Expand Down Expand Up @@ -185,6 +186,20 @@ Status ValidateDefault(const SchemaField& field, const Literal& value,
return InvalidSchema("Invalid {} value for {}: value must be finite", kind,
field.name());
}
// A decimal literal carries a precision in its type but stores only an unscaled value,
// so a value that does not fit the field precision would pass the type check above yet
// be rejected when the metadata is parsed back. Reject it here to keep the default
// consistent with what the reader accepts.
if (field.type()->type_id() == TypeId::kDecimal) {
if (const auto* dec = std::get_if<Decimal>(&value.value())) {
const auto& decimal_type =
internal::checked_cast<const DecimalType&>(*field.type());
if (!dec->FitsInPrecision(decimal_type.precision())) {
return InvalidSchema("{} of field {} does not fit precision {}", kind,
field.name(), decimal_type.precision());
}
}
}
return {};
}

Expand Down
19 changes: 19 additions & 0 deletions src/iceberg/test/schema_field_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,23 @@ TEST(SchemaFieldTest, ValidateAcceptsFiniteFloatingDefault) {
EXPECT_THAT(field.Validate(), IsOk());
}

TEST(SchemaFieldTest, ValidateRejectsDecimalDefaultExceedingPrecision) {
// A decimal default whose unscaled value needs more digits than the field precision has
// the matching literal type (decimal(2, 1)) but does not fit; Validate must reject it
// so it is never serialized to metadata the reader would later refuse to parse.
SchemaField field(/*field_id=*/1, /*name=*/"d", decimal(2, 1),
/*optional=*/true, /*doc=*/"",
std::make_shared<const Literal>(Literal::Decimal(999, 2, 1)));
auto status = field.Validate();
EXPECT_THAT(status, IsError(ErrorKind::kInvalidSchema));
EXPECT_THAT(status, HasErrorMessage("does not fit precision"));
}

TEST(SchemaFieldTest, ValidateAcceptsDecimalDefaultWithinPrecision) {
SchemaField field(/*field_id=*/1, /*name=*/"d", decimal(4, 1),
/*optional=*/true, /*doc=*/"",
std::make_shared<const Literal>(Literal::Decimal(999, 4, 1)));
EXPECT_THAT(field.Validate(), IsOk());
}

} // namespace iceberg
Loading