Skip to content

Commit e44ac81

Browse files
committed
validate decimal default fits the field precision
1 parent 0825f56 commit e44ac81

2 files changed

Lines changed: 32 additions & 0 deletions

File tree

src/iceberg/schema_field.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "iceberg/expression/literal.h"
2828
#include "iceberg/type.h"
2929
#include "iceberg/util/checked_cast.h"
30+
#include "iceberg/util/decimal.h"
3031
#include "iceberg/util/formatter.h" // IWYU pragma: keep
3132
#include "iceberg/util/macros.h"
3233

@@ -168,6 +169,18 @@ Status ValidateDefault(const SchemaField& field, const Literal& value,
168169
return InvalidSchema("{} of field {} has type {} but expected {}", kind, field.name(),
169170
*value.type(), *field.type());
170171
}
172+
// A decimal literal carries a precision in its type but stores only an unscaled value,
173+
// so a value that does not fit the field precision would pass the type check above yet
174+
// be rejected when the metadata is parsed back. Reject it here to keep the default
175+
// consistent with what the reader accepts.
176+
if (field.type()->type_id() == TypeId::kDecimal &&
177+
std::holds_alternative<Decimal>(value.value())) {
178+
const auto& decimal_type = internal::checked_cast<const DecimalType&>(*field.type());
179+
if (!std::get<Decimal>(value.value()).FitsInPrecision(decimal_type.precision())) {
180+
return InvalidSchema("{} of field {} does not fit precision {}", kind, field.name(),
181+
decimal_type.precision());
182+
}
183+
}
171184
return {};
172185
}
173186

src/iceberg/test/schema_field_test.cc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <gtest/gtest.h>
2626

2727
#include "iceberg/expression/literal.h"
28+
#include "iceberg/test/matchers.h"
2829
#include "iceberg/type.h"
2930
#include "iceberg/util/formatter.h" // IWYU pragma: keep
3031

@@ -170,4 +171,22 @@ TEST(SchemaFieldTest, CastDefaultValue) {
170171
}
171172
}
172173

174+
TEST(SchemaFieldTest, ValidateRejectsDecimalDefaultExceedingPrecision) {
175+
// A decimal default whose unscaled value needs more digits than the field precision has
176+
// the matching literal type (decimal(2, 1)) but does not fit; Validate must reject it
177+
// so it is never serialized to metadata the reader would later refuse to parse.
178+
SchemaField field(/*field_id=*/1, /*name=*/"d", decimal(2, 1),
179+
/*optional=*/true, /*doc=*/"",
180+
std::make_shared<const Literal>(Literal::Decimal(999, 2, 1)));
181+
EXPECT_THAT(field.Validate(), IsError(ErrorKind::kInvalidSchema));
182+
EXPECT_THAT(field.Validate(), HasErrorMessage("does not fit precision"));
183+
}
184+
185+
TEST(SchemaFieldTest, ValidateAcceptsDecimalDefaultWithinPrecision) {
186+
SchemaField field(/*field_id=*/1, /*name=*/"d", decimal(4, 1),
187+
/*optional=*/true, /*doc=*/"",
188+
std::make_shared<const Literal>(Literal::Decimal(999, 4, 1)));
189+
EXPECT_THAT(field.Validate(), IsOk());
190+
}
191+
173192
} // namespace iceberg

0 commit comments

Comments
 (0)