This is an automated email from the ASF dual-hosted git repository.

gangwu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-cpp.git


The following commit(s) were added to refs/heads/main by this push:
     new 53e15a95 feat: make schema id non-optional with default value (#449)
53e15a95 is described below

commit 53e15a950227d9559de1d7550e5d1e6f836ce5ae
Author: Guotao Yu <[email protected]>
AuthorDate: Tue Dec 30 14:37:14 2025 +0800

    feat: make schema id non-optional with default value (#449)
---
 src/iceberg/expression/residual_evaluator.cc    |  3 +-
 src/iceberg/json_internal.cc                    | 13 ++--
 src/iceberg/schema.cc                           | 16 +++--
 src/iceberg/schema.h                            | 14 +++--
 src/iceberg/schema_internal.cc                  |  3 +-
 src/iceberg/table_metadata.cc                   | 27 +++-----
 src/iceberg/table_metadata.h                    |  5 +-
 src/iceberg/table_requirement.cc                |  9 +--
 src/iceberg/table_requirements.cc               |  4 +-
 src/iceberg/table_scan.cc                       |  5 +-
 src/iceberg/test/metadata_serde_test.cc         |  5 +-
 src/iceberg/test/residual_evaluator_test.cc     | 24 +++-----
 src/iceberg/test/rest_json_internal_test.cc     | 38 +++++-------
 src/iceberg/test/schema_test.cc                 |  2 +-
 src/iceberg/test/table_metadata_builder_test.cc | 82 +++++++++++++------------
 src/iceberg/test/table_requirement_test.cc      |  7 ---
 src/iceberg/util/snapshot_util.cc               |  2 +-
 17 files changed, 119 insertions(+), 140 deletions(-)

diff --git a/src/iceberg/expression/residual_evaluator.cc 
b/src/iceberg/expression/residual_evaluator.cc
index cf8f9812..3cdf2914 100644
--- a/src/iceberg/expression/residual_evaluator.cc
+++ b/src/iceberg/expression/residual_evaluator.cc
@@ -308,8 +308,7 @@ class UnpartitionedResidualEvaluator : public 
ResidualEvaluator {
 
  private:
   // Store an empty schema to avoid dangling reference when passing to base 
class
-  inline static const std::shared_ptr<Schema> kEmptySchema_ =
-      std::make_shared<Schema>(std::vector<SchemaField>{}, std::nullopt);
+  inline static const std::shared_ptr<Schema> kEmptySchema_ = 
Schema::EmptySchema();
 };
 
 }  // namespace
diff --git a/src/iceberg/json_internal.cc b/src/iceberg/json_internal.cc
index 9d955d46..d175bb6d 100644
--- a/src/iceberg/json_internal.cc
+++ b/src/iceberg/json_internal.cc
@@ -308,7 +308,7 @@ nlohmann::json ToJson(const Type& type) {
 
 nlohmann::json ToJson(const Schema& schema) {
   nlohmann::json json = ToJson(static_cast<const Type&>(schema));
-  SetOptionalField(json, kSchemaId, schema.schema_id());
+  json[kSchemaId] = schema.schema_id();
   // TODO(gangwu): add identifier-field-ids.
   return json;
 }
@@ -466,7 +466,8 @@ Result<std::unique_ptr<SchemaField>> FieldFromJson(const 
nlohmann::json& json) {
 }
 
 Result<std::unique_ptr<Schema>> SchemaFromJson(const nlohmann::json& json) {
-  ICEBERG_ASSIGN_OR_RAISE(auto schema_id, GetJsonValueOptional<int32_t>(json, 
kSchemaId));
+  ICEBERG_ASSIGN_OR_RAISE(auto schema_id_opt,
+                          GetJsonValueOptional<int32_t>(json, kSchemaId));
   ICEBERG_ASSIGN_OR_RAISE(auto type, TypeFromJson(json));
 
   if (type->type_id() != TypeId::kStruct) [[unlikely]] {
@@ -474,6 +475,7 @@ Result<std::unique_ptr<Schema>> SchemaFromJson(const 
nlohmann::json& json) {
   }
 
   auto& struct_type = static_cast<StructType&>(*type);
+  auto schema_id = schema_id_opt.value_or(Schema::kInitialSchemaId);
   return FromStructType(std::move(struct_type), schema_id);
 }
 
@@ -762,7 +764,7 @@ nlohmann::json ToJson(const TableMetadata& table_metadata) {
   }
 
   // write the current schema ID and schema list
-  SetOptionalField(json, kCurrentSchemaId, table_metadata.current_schema_id);
+  json[kCurrentSchemaId] = table_metadata.current_schema_id;
   json[kSchemas] = ToJsonList(table_metadata.schemas);
 
   // for older readers, continue writing the default spec as "partition-spec"
@@ -824,8 +826,7 @@ namespace {
 ///
 /// \return The current schema or parse error.
 Result<std::shared_ptr<Schema>> ParseSchemas(
-    const nlohmann::json& json, int8_t format_version,
-    std::optional<int32_t>& current_schema_id,
+    const nlohmann::json& json, int8_t format_version, int32_t& 
current_schema_id,
     std::vector<std::shared_ptr<Schema>>& schemas) {
   std::shared_ptr<Schema> current_schema;
   if (json.contains(kSchemas)) {
@@ -848,7 +849,7 @@ Result<std::shared_ptr<Schema>> ParseSchemas(
     }
     if (!current_schema) {
       return JsonParseError("Cannot find schema with {}={} from {}", 
kCurrentSchemaId,
-                            current_schema_id.value(), 
SafeDumpJson(schema_array));
+                            current_schema_id, SafeDumpJson(schema_array));
     }
   } else {
     if (format_version != 1) {
diff --git a/src/iceberg/schema.cc b/src/iceberg/schema.cc
index afe6be1e..bc325251 100644
--- a/src/iceberg/schema.cc
+++ b/src/iceberg/schema.cc
@@ -34,14 +34,14 @@
 
 namespace iceberg {
 
-Schema::Schema(std::vector<SchemaField> fields, std::optional<int32_t> 
schema_id,
+Schema::Schema(std::vector<SchemaField> fields, int32_t schema_id,
                std::vector<int32_t> identifier_field_ids)
     : StructType(std::move(fields)),
       schema_id_(schema_id),
       identifier_field_ids_(std::move(identifier_field_ids)) {}
 
 Result<std::unique_ptr<Schema>> Schema::Make(
-    std::vector<SchemaField> fields, std::optional<int32_t> schema_id,
+    std::vector<SchemaField> fields, int32_t schema_id,
     const std::vector<std::string>& identifier_field_names) {
   auto schema = std::make_unique<Schema>(std::move(fields), schema_id);
 
@@ -57,7 +57,13 @@ Result<std::unique_ptr<Schema>> Schema::Make(
   return schema;
 }
 
-std::optional<int32_t> Schema::schema_id() const { return schema_id_; }
+const std::shared_ptr<Schema>& Schema::EmptySchema() {
+  static const auto empty_schema =
+      std::make_shared<Schema>(std::vector<SchemaField>{}, kInitialSchemaId);
+  return empty_schema;
+}
+
+int32_t Schema::schema_id() const { return schema_id_; }
 
 std::string Schema::ToString() const {
   std::string repr = "schema<";
@@ -196,7 +202,7 @@ Result<std::unique_ptr<Schema>> 
Schema::Select(std::span<const std::string> name
       auto pruned_type, 
visitor.Visit(std::shared_ptr<StructType>(ToStructType(*this))));
 
   if (!pruned_type) {
-    return std::make_unique<Schema>(std::vector<SchemaField>{}, std::nullopt);
+    return std::make_unique<Schema>(std::vector<SchemaField>{}, 
kInitialSchemaId);
   }
 
   if (pruned_type->type_id() != TypeId::kStruct) {
@@ -214,7 +220,7 @@ Result<std::unique_ptr<Schema>> Schema::Project(
       auto project_type, 
visitor.Visit(std::shared_ptr<StructType>(ToStructType(*this))));
 
   if (!project_type) {
-    return std::make_unique<Schema>(std::vector<SchemaField>{}, std::nullopt);
+    return std::make_unique<Schema>(std::vector<SchemaField>{}, 
kInitialSchemaId);
   }
 
   if (project_type->type_id() != TypeId::kStruct) {
diff --git a/src/iceberg/schema.h b/src/iceberg/schema.h
index a2686c9f..fc082569 100644
--- a/src/iceberg/schema.h
+++ b/src/iceberg/schema.h
@@ -52,8 +52,7 @@ class ICEBERG_EXPORT Schema : public StructType {
   /// \brief Special value to select all columns from manifest files.
   static constexpr std::string_view kAllColumns = "*";
 
-  explicit Schema(std::vector<SchemaField> fields,
-                  std::optional<int32_t> schema_id = std::nullopt,
+  explicit Schema(std::vector<SchemaField> fields, int32_t schema_id = 
kInitialSchemaId,
                   std::vector<int32_t> identifier_field_ids = {});
 
   /// \brief Create a schema.
@@ -63,14 +62,19 @@ class ICEBERG_EXPORT Schema : public StructType {
   /// \param identifier_field_names Canonical names of fields that uniquely 
identify rows
   /// in the table (default: empty). \return A new Schema instance or Status 
if failed.
   static Result<std::unique_ptr<Schema>> Make(
-      std::vector<SchemaField> fields, std::optional<int32_t> schema_id = 
std::nullopt,
+      std::vector<SchemaField> fields, int32_t schema_id = kInitialSchemaId,
       const std::vector<std::string>& identifier_field_names = {});
 
+  /// \brief Get an empty schema.
+  ///
+  /// An empty schema has no fields and a schema ID of 0.
+  static const std::shared_ptr<Schema>& EmptySchema();
+
   /// \brief Get the schema ID.
   ///
   /// A schema is identified by a unique ID for the purposes of schema
   /// evolution.
-  std::optional<int32_t> schema_id() const;
+  int32_t schema_id() const;
 
   std::string ToString() const override;
 
@@ -178,7 +182,7 @@ class ICEBERG_EXPORT Schema : public StructType {
       const Schema&);
   static Result<int32_t> InitHighestFieldId(const Schema&);
 
-  const std::optional<int32_t> schema_id_;
+  const int32_t schema_id_;
   /// Field IDs that uniquely identify rows in the table.
   std::vector<int32_t> identifier_field_ids_;
   /// Mapping from field id to field.
diff --git a/src/iceberg/schema_internal.cc b/src/iceberg/schema_internal.cc
index e020a9b7..dedb603e 100644
--- a/src/iceberg/schema_internal.cc
+++ b/src/iceberg/schema_internal.cc
@@ -304,12 +304,13 @@ Result<std::shared_ptr<Type>> FromArrowSchema(const 
ArrowSchema& schema) {
 }  // namespace
 
 std::unique_ptr<Schema> FromStructType(StructType&& struct_type,
-                                       std::optional<int32_t> schema_id) {
+                                       std::optional<int32_t> schema_id_opt) {
   std::vector<SchemaField> fields;
   fields.reserve(struct_type.fields().size());
   for (auto& field : struct_type.fields()) {
     fields.emplace_back(std::move(field));
   }
+  auto schema_id = schema_id_opt.value_or(Schema::kInitialSchemaId);
   return std::make_unique<Schema>(std::move(fields), schema_id);
 }
 
diff --git a/src/iceberg/table_metadata.cc b/src/iceberg/table_metadata.cc
index 095f91d1..114f3d0b 100644
--- a/src/iceberg/table_metadata.cc
+++ b/src/iceberg/table_metadata.cc
@@ -233,13 +233,12 @@ Result<std::shared_ptr<Schema>> TableMetadata::Schema() 
const {
   return SchemaById(current_schema_id);
 }
 
-Result<std::shared_ptr<Schema>> TableMetadata::SchemaById(
-    std::optional<int32_t> schema_id) const {
+Result<std::shared_ptr<Schema>> TableMetadata::SchemaById(int32_t schema_id) 
const {
   auto iter = std::ranges::find_if(schemas, [schema_id](const auto& schema) {
     return schema != nullptr && schema->schema_id() == schema_id;
   });
   if (iter == schemas.end()) {
-    return NotFound("Schema with ID {} is not found", schema_id.value_or(-1));
+    return NotFound("Schema with ID {} is not found", schema_id);
   }
   return *iter;
 }
@@ -359,11 +358,8 @@ Result<TableMetadataCache::SnapshotsMapRef> 
TableMetadataCache::GetSnapshotsById
 
 Result<TableMetadataCache::SchemasMap> TableMetadataCache::InitSchemasMap(
     const TableMetadata* metadata) {
-  return metadata->schemas | std::views::filter([](const auto& schema) {
-           return schema->schema_id().has_value();
-         }) |
-         std::views::transform([](const auto& schema) {
-           return std::make_pair(schema->schema_id().value(), schema);
+  return metadata->schemas | std::views::transform([](const auto& schema) {
+           return std::make_pair(schema->schema_id(), schema);
          }) |
          std::ranges::to<SchemasMap>();
 }
@@ -548,9 +544,7 @@ class TableMetadataBuilder::Impl {
       : base_(base_metadata), metadata_(*base_metadata) {
     // Initialize index maps from base metadata
     for (const auto& schema : metadata_.schemas) {
-      if (schema->schema_id().has_value()) {
-        schemas_by_id_.emplace(schema->schema_id().value(), schema);
-      }
+      schemas_by_id_.emplace(schema->schema_id(), schema);
     }
 
     for (const auto& spec : metadata_.partition_specs) {
@@ -920,14 +914,13 @@ Status 
TableMetadataBuilder::Impl::SetCurrentSchema(int32_t schema_id) {
 
 Status TableMetadataBuilder::Impl::RemoveSchemas(
     const std::unordered_set<int32_t>& schema_ids) {
-  auto current_schema_id = 
metadata_.current_schema_id.value_or(Schema::kInitialSchemaId);
+  auto current_schema_id = metadata_.current_schema_id;
   ICEBERG_PRECHECK(!schema_ids.contains(current_schema_id),
                    "Cannot remove current schema: {}", current_schema_id);
 
   if (!schema_ids.empty()) {
     metadata_.schemas = metadata_.schemas | std::views::filter([&](const auto& 
schema) {
-                          return schema->schema_id().has_value() &&
-                                 
!schema_ids.contains(schema->schema_id().value());
+                          return !schema_ids.contains(schema->schema_id());
                         }) |
                         
std::ranges::to<std::vector<std::shared_ptr<Schema>>>();
     changes_.push_back(std::make_unique<table::RemoveSchemas>(schema_ids));
@@ -999,7 +992,7 @@ Result<std::unique_ptr<TableMetadata>> 
TableMetadataBuilder::Impl::Build() {
             std::chrono::system_clock::now().time_since_epoch())};
   }
 
-  auto current_schema_id = 
metadata_.current_schema_id.value_or(Schema::kInitialSchemaId);
+  auto current_schema_id = metadata_.current_schema_id;
   auto schema_it = schemas_by_id_.find(current_schema_id);
   ICEBERG_PRECHECK(schema_it != schemas_by_id_.end(),
                    "Current schema ID {} not found in schemas", 
current_schema_id);
@@ -1072,9 +1065,9 @@ int32_t 
TableMetadataBuilder::Impl::ReuseOrCreateNewPartitionSpecId(
 int32_t TableMetadataBuilder::Impl::ReuseOrCreateNewSchemaId(
     const Schema& new_schema) const {
   // if the schema already exists, use its id; otherwise use the highest id + 1
-  auto new_schema_id = 
metadata_.current_schema_id.value_or(Schema::kInitialSchemaId);
+  auto new_schema_id = metadata_.current_schema_id;
   for (auto& schema : metadata_.schemas) {
-    auto schema_id = schema->schema_id().value_or(Schema::kInitialSchemaId);
+    auto schema_id = schema->schema_id();
     if (schema->SameSchema(new_schema)) {
       return schema_id;
     } else if (new_schema_id <= schema_id) {
diff --git a/src/iceberg/table_metadata.h b/src/iceberg/table_metadata.h
index 068ab319..3e3eb9c7 100644
--- a/src/iceberg/table_metadata.h
+++ b/src/iceberg/table_metadata.h
@@ -95,7 +95,7 @@ struct ICEBERG_EXPORT TableMetadata {
   /// A list of schemas
   std::vector<std::shared_ptr<iceberg::Schema>> schemas;
   /// ID of the table's current schema
-  std::optional<int32_t> current_schema_id;
+  int32_t current_schema_id;
   /// A list of partition specs
   std::vector<std::shared_ptr<iceberg::PartitionSpec>> partition_specs;
   /// ID of the current partition spec that writers should use by default
@@ -136,8 +136,7 @@ struct ICEBERG_EXPORT TableMetadata {
   /// \brief Get the current schema, return NotFoundError if not found
   Result<std::shared_ptr<iceberg::Schema>> Schema() const;
   /// \brief Get the current schema by ID, return NotFoundError if not found
-  Result<std::shared_ptr<iceberg::Schema>> SchemaById(
-      std::optional<int32_t> schema_id) const;
+  Result<std::shared_ptr<iceberg::Schema>> SchemaById(int32_t schema_id) const;
   /// \brief Get the current partition spec, return NotFoundError if not found
   Result<std::shared_ptr<iceberg::PartitionSpec>> PartitionSpec() const;
   /// \brief Get the current sort order, return NotFoundError if not found
diff --git a/src/iceberg/table_requirement.cc b/src/iceberg/table_requirement.cc
index 3c0a01e4..49628928 100644
--- a/src/iceberg/table_requirement.cc
+++ b/src/iceberg/table_requirement.cc
@@ -90,15 +90,10 @@ Status AssertCurrentSchemaID::Validate(const TableMetadata* 
base) const {
     return CommitFailed("Requirement failed: current table metadata is 
missing");
   }
 
-  if (!base->current_schema_id.has_value()) {
-    return CommitFailed(
-        "Requirement failed: current schema ID is not set in table metadata");
-  }
-
-  if (base->current_schema_id.value() != schema_id_) {
+  if (base->current_schema_id != schema_id_) {
     return CommitFailed(
         "Requirement failed: current schema ID does not match, expected {} != 
{}",
-        schema_id_, base->current_schema_id.value());
+        schema_id_, base->current_schema_id);
   }
 
   return {};
diff --git a/src/iceberg/table_requirements.cc 
b/src/iceberg/table_requirements.cc
index 8222ded3..3268980e 100644
--- a/src/iceberg/table_requirements.cc
+++ b/src/iceberg/table_requirements.cc
@@ -51,8 +51,8 @@ void 
TableUpdateContext::RequireLastAssignedFieldIdUnchanged() {
 void TableUpdateContext::RequireCurrentSchemaIdUnchanged() {
   if (!added_current_schema_id_) {
     if (base_ != nullptr && !is_replace_) {
-      AddRequirement(std::make_unique<table::AssertCurrentSchemaID>(
-          base_->current_schema_id.value()));
+      AddRequirement(
+          
std::make_unique<table::AssertCurrentSchemaID>(base_->current_schema_id));
     }
     added_current_schema_id_ = true;
   }
diff --git a/src/iceberg/table_scan.cc b/src/iceberg/table_scan.cc
index 6c182845..6918de82 100644
--- a/src/iceberg/table_scan.cc
+++ b/src/iceberg/table_scan.cc
@@ -216,8 +216,7 @@ Result<std::unique_ptr<TableScan>> 
TableScanBuilder::Build() {
 
   if (!context_.projected_schema) {
     const auto& snapshot = context_.snapshot;
-    auto schema_id =
-        snapshot->schema_id ? snapshot->schema_id : 
table_metadata->current_schema_id;
+    auto schema_id = table_metadata->current_schema_id;
     ICEBERG_ASSIGN_OR_RAISE(auto schema, 
table_metadata->SchemaById(schema_id));
 
     if (column_names_.empty()) {
@@ -231,7 +230,7 @@ Result<std::unique_ptr<TableScan>> 
TableScanBuilder::Build() {
         auto field_opt = schema->GetFieldByName(column_name);
         if (!field_opt) {
           return InvalidArgument("Column {} not found in schema '{}'", 
column_name,
-                                 *schema_id);
+                                 schema_id);
         }
         projected_fields.emplace_back(field_opt.value()->get());
       }
diff --git a/src/iceberg/test/metadata_serde_test.cc 
b/src/iceberg/test/metadata_serde_test.cc
index 51126b27..62682b80 100644
--- a/src/iceberg/test/metadata_serde_test.cc
+++ b/src/iceberg/test/metadata_serde_test.cc
@@ -96,8 +96,7 @@ TEST(MetadataSerdeTest, DeserializeV1Valid) {
   auto expected_schema = std::make_shared<Schema>(
       std::vector<SchemaField>{SchemaField::MakeRequired(1, "x", int64()),
                                SchemaField::MakeRequired(2, "y", int64()),
-                               SchemaField::MakeRequired(3, "z", int64())},
-      /*schema_id=*/std::nullopt);
+                               SchemaField::MakeRequired(3, "z", int64())});
 
   auto expected_spec_result = PartitionSpec::Make(
       /*spec_id=*/0,
@@ -115,7 +114,7 @@ TEST(MetadataSerdeTest, DeserializeV1Valid) {
       .last_updated_ms = TimePointMsFromUnixMs(1602638573874).value(),
       .last_column_id = 3,
       .schemas = {expected_schema},
-      .current_schema_id = std::nullopt,
+      .current_schema_id = Schema::kInitialSchemaId,
       .partition_specs = {expected_spec},
       .default_spec_id = 0,
       .last_partition_id = 1000,
diff --git a/src/iceberg/test/residual_evaluator_test.cc 
b/src/iceberg/test/residual_evaluator_test.cc
index 04c82e25..d2aa2aeb 100644
--- a/src/iceberg/test/residual_evaluator_test.cc
+++ b/src/iceberg/test/residual_evaluator_test.cc
@@ -90,8 +90,7 @@ class ResidualEvaluatorTest : public ::testing::Test {
 TEST_F(ResidualEvaluatorTest, IdentityTransformResiduals) {
   auto schema = std::make_shared<Schema>(
       std::vector<SchemaField>{SchemaField::MakeOptional(50, "dateint", 
int32()),
-                               SchemaField::MakeOptional(51, "hour", int32())},
-      std::nullopt);
+                               SchemaField::MakeOptional(51, "hour", 
int32())});
 
   auto identity_transform = Transform::Identity();
   PartitionField pt_field(50, 1000, "dateint", identity_transform);
@@ -158,8 +157,7 @@ TEST_F(ResidualEvaluatorTest, IdentityTransformResiduals) {
 TEST_F(ResidualEvaluatorTest, CaseInsensitiveIdentityTransformResiduals) {
   auto schema = std::make_shared<Schema>(
       std::vector<SchemaField>{SchemaField::MakeOptional(50, "dateint", 
int32()),
-                               SchemaField::MakeOptional(51, "hour", int32())},
-      std::nullopt);
+                               SchemaField::MakeOptional(51, "hour", 
int32())});
 
   auto identity_transform = Transform::Identity();
   PartitionField pt_field(50, 1000, "dateint", identity_transform);
@@ -249,8 +247,7 @@ TEST_F(ResidualEvaluatorTest, UnpartitionedResiduals) {
 TEST_F(ResidualEvaluatorTest, In) {
   auto schema = std::make_shared<Schema>(
       std::vector<SchemaField>{SchemaField::MakeOptional(50, "dateint", 
int32()),
-                               SchemaField::MakeOptional(51, "hour", int32())},
-      std::nullopt);
+                               SchemaField::MakeOptional(51, "hour", 
int32())});
 
   auto identity_transform = Transform::Identity();
   PartitionField pt_field(50, 1000, "dateint", identity_transform);
@@ -276,8 +273,7 @@ TEST_F(ResidualEvaluatorTest, In) {
 TEST_F(ResidualEvaluatorTest, NotIn) {
   auto schema = std::make_shared<Schema>(
       std::vector<SchemaField>{SchemaField::MakeOptional(50, "dateint", 
int32()),
-                               SchemaField::MakeOptional(51, "hour", int32())},
-      std::nullopt);
+                               SchemaField::MakeOptional(51, "hour", 
int32())});
 
   auto identity_transform = Transform::Identity();
   PartitionField pt_field(50, 1000, "dateint", identity_transform);
@@ -304,8 +300,7 @@ TEST_F(ResidualEvaluatorTest, NotIn) {
 TEST_F(ResidualEvaluatorTest, IsNaN) {
   auto schema = std::make_shared<Schema>(
       std::vector<SchemaField>{SchemaField::MakeOptional(50, "double", 
float64()),
-                               SchemaField::MakeOptional(51, "float", 
float32())},
-      std::nullopt);
+                               SchemaField::MakeOptional(51, "float", 
float32())});
 
   // Test double field
   auto identity_transform = Transform::Identity();
@@ -353,8 +348,7 @@ TEST_F(ResidualEvaluatorTest, IsNaN) {
 TEST_F(ResidualEvaluatorTest, NotNaN) {
   auto schema = std::make_shared<Schema>(
       std::vector<SchemaField>{SchemaField::MakeOptional(50, "double", 
float64()),
-                               SchemaField::MakeOptional(51, "float", 
float32())},
-      std::nullopt);
+                               SchemaField::MakeOptional(51, "float", 
float32())});
 
   // Test double field
   auto identity_transform = Transform::Identity();
@@ -401,8 +395,7 @@ TEST_F(ResidualEvaluatorTest, NotNaN) {
 
 TEST_F(ResidualEvaluatorTest, IntegerTruncateTransformResiduals) {
   auto schema = std::make_shared<Schema>(
-      std::vector<SchemaField>{SchemaField::MakeOptional(50, "value", 
int32())},
-      std::nullopt);
+      std::vector<SchemaField>{SchemaField::MakeOptional(50, "value", 
int32())});
 
   // Valid partitions would be 0, 10, 20...90, 100 etc.
   auto truncate_transform = Transform::Truncate(10);
@@ -518,8 +511,7 @@ TEST_F(ResidualEvaluatorTest, 
IntegerTruncateTransformResiduals) {
 
 TEST_F(ResidualEvaluatorTest, StringTruncateTransformResiduals) {
   auto schema = std::make_shared<Schema>(
-      std::vector<SchemaField>{SchemaField::MakeOptional(50, "value", 
string())},
-      std::nullopt);
+      std::vector<SchemaField>{SchemaField::MakeOptional(50, "value", 
string())});
 
   // Valid partitions would be two letter strings for eg: ab, bc etc
   auto truncate_transform = Transform::Truncate(2);
diff --git a/src/iceberg/test/rest_json_internal_test.cc 
b/src/iceberg/test/rest_json_internal_test.cc
index c4b7fb55..60facf43 100644
--- a/src/iceberg/test/rest_json_internal_test.cc
+++ b/src/iceberg/test/rest_json_internal_test.cc
@@ -37,9 +37,8 @@ namespace iceberg::rest {
 // Helper function to create a simple schema for testing
 static std::shared_ptr<Schema> MakeSimpleSchema() {
   return std::make_shared<Schema>(
-      std::vector<SchemaField>{SchemaField(1, "id", int32(), false),     // 
required
-                               SchemaField(2, "data", string(), true)},  // 
optional
-      std::nullopt);
+      std::vector<SchemaField>{SchemaField(1, "id", int32(), false),  // 
required
+                               SchemaField(2, "data", string(), true)});
 }
 
 // Helper function to create a simple TableMetadata for testing
@@ -956,13 +955,13 @@ INSTANTIATE_TEST_SUITE_P(
         CreateTableRequestParam{
             .test_name = "MinimalRequest",
             .expected_json_str =
-                
R"({"name":"my_table","schema":{"type":"struct","fields":[{"id":1,"name":"id","type":"int","required":true},{"id":2,"name":"data","type":"string","required":false}]}})",
+                
R"({"name":"my_table","schema":{"type":"struct","schema-id":0,"fields":[{"id":1,"name":"id","type":"int","required":true},{"id":2,"name":"data","type":"string","required":false}]}})",
             .model = {.name = "my_table", .schema = MakeSimpleSchema()}},
         // Request with location
         CreateTableRequestParam{
             .test_name = "WithLocation",
             .expected_json_str =
-                
R"({"name":"my_table","schema":{"type":"struct","fields":[{"id":1,"name":"id","type":"int","required":true},{"id":2,"name":"data","type":"string","required":false}]},"location":"s3://bucket/warehouse/my_table"})",
+                
R"({"name":"my_table","schema":{"type":"struct","schema-id":0,"fields":[{"id":1,"name":"id","type":"int","required":true},{"id":2,"name":"data","type":"string","required":false}]},"location":"s3://bucket/warehouse/my_table"})",
             .model = {.name = "my_table",
                       .location = "s3://bucket/warehouse/my_table",
                       .schema = MakeSimpleSchema()}},
@@ -970,7 +969,7 @@ INSTANTIATE_TEST_SUITE_P(
         CreateTableRequestParam{
             .test_name = "WithProperties",
             .expected_json_str =
-                
R"({"name":"my_table","schema":{"type":"struct","fields":[{"id":1,"name":"id","type":"int","required":true},{"id":2,"name":"data","type":"string","required":false}]},"properties":{"owner":"alice","version":"1.0"}})",
+                
R"({"name":"my_table","schema":{"type":"struct","schema-id":0,"fields":[{"id":1,"name":"id","type":"int","required":true},{"id":2,"name":"data","type":"string","required":false}]},"properties":{"owner":"alice","version":"1.0"}})",
             .model = {.name = "my_table",
                       .schema = MakeSimpleSchema(),
                       .properties = {{"owner", "alice"}, {"version", "1.0"}}}},
@@ -978,7 +977,7 @@ INSTANTIATE_TEST_SUITE_P(
         CreateTableRequestParam{
             .test_name = "WithStageCreate",
             .expected_json_str =
-                
R"({"name":"my_table","schema":{"type":"struct","fields":[{"id":1,"name":"id","type":"int","required":true},{"id":2,"name":"data","type":"string","required":false}]},"stage-create":true})",
+                
R"({"name":"my_table","schema":{"type":"struct","schema-id":0,"fields":[{"id":1,"name":"id","type":"int","required":true},{"id":2,"name":"data","type":"string","required":false}]},"stage-create":true})",
             .model = {.name = "my_table",
                       .schema = MakeSimpleSchema(),
                       .stage_create = true}},
@@ -986,7 +985,7 @@ INSTANTIATE_TEST_SUITE_P(
         CreateTableRequestParam{
             .test_name = "WithUnpartitionedSpec",
             .expected_json_str =
-                
R"({"name":"my_table","schema":{"type":"struct","fields":[{"id":1,"name":"id","type":"int","required":true},{"id":2,"name":"data","type":"string","required":false}]},"partition-spec":{"spec-id":0,"fields":[]}})",
+                
R"({"name":"my_table","schema":{"type":"struct","schema-id":0,"fields":[{"id":1,"name":"id","type":"int","required":true},{"id":2,"name":"data","type":"string","required":false}]},"partition-spec":{"spec-id":0,"fields":[]}})",
             .model = {.name = "my_table",
                       .schema = MakeSimpleSchema(),
                       .partition_spec = PartitionSpec::Unpartitioned()}},
@@ -994,7 +993,7 @@ INSTANTIATE_TEST_SUITE_P(
         CreateTableRequestParam{
             .test_name = "WithUnsortedOrder",
             .expected_json_str =
-                
R"({"name":"my_table","schema":{"type":"struct","fields":[{"id":1,"name":"id","type":"int","required":true},{"id":2,"name":"data","type":"string","required":false}]},"write-order":{"order-id":0,"fields":[]}})",
+                
R"({"name":"my_table","schema":{"type":"struct","schema-id":0,"fields":[{"id":1,"name":"id","type":"int","required":true},{"id":2,"name":"data","type":"string","required":false}]},"write-order":{"order-id":0,"fields":[]}})",
             .model = {.name = "my_table",
                       .schema = MakeSimpleSchema(),
                       .write_order = SortOrder::Unsorted()}}),
@@ -1013,20 +1012,18 @@ INSTANTIATE_TEST_SUITE_P(
             .json_str =
                 
R"({"name":"my_table","schema":{"type":"struct","fields":[{"id":1,"name":"id","type":"int","required":true}]}})",
             .expected_model = {.name = "my_table",
-                               .schema = std::make_shared<Schema>(
-                                   std::vector<SchemaField>{
-                                       SchemaField(1, "id", int32(), false)},  
// required
-                                   std::nullopt)}},
+                               .schema =
+                                   
std::make_shared<Schema>(std::vector<SchemaField>{
+                                       SchemaField(1, "id", int32(), 
false)})}},
         // stage-create field is missing (should default to false)
         CreateTableRequestDeserializeParam{
             .test_name = "MissingStageCreate",
             .json_str =
                 
R"({"name":"my_table","schema":{"type":"struct","fields":[{"id":1,"name":"id","type":"int","required":true}]}})",
             .expected_model = {.name = "my_table",
-                               .schema = std::make_shared<Schema>(
-                                   std::vector<SchemaField>{
-                                       SchemaField(1, "id", int32(), false)},  
// required
-                                   std::nullopt),
+                               .schema =
+                                   
std::make_shared<Schema>(std::vector<SchemaField>{
+                                       SchemaField(1, "id", int32(), false)}),
                                .stage_create = false}},
         // Properties field is missing (should deserialize to empty map)
         CreateTableRequestDeserializeParam{
@@ -1034,10 +1031,9 @@ INSTANTIATE_TEST_SUITE_P(
             .json_str =
                 
R"({"name":"my_table","schema":{"type":"struct","fields":[{"id":1,"name":"id","type":"int","required":true}]}})",
             .expected_model = {.name = "my_table",
-                               .schema = std::make_shared<Schema>(
-                                   std::vector<SchemaField>{
-                                       SchemaField(1, "id", int32(), false)},  
// required
-                                   std::nullopt)}}),
+                               .schema =
+                                   
std::make_shared<Schema>(std::vector<SchemaField>{
+                                       SchemaField(1, "id", int32(), 
false)})}}),
     [](const ::testing::TestParamInfo<CreateTableRequestDeserializeParam>& 
info) {
       return info.param.test_name;
     });
diff --git a/src/iceberg/test/schema_test.cc b/src/iceberg/test/schema_test.cc
index ff6bf060..38fef36c 100644
--- a/src/iceberg/test/schema_test.cc
+++ b/src/iceberg/test/schema_test.cc
@@ -39,7 +39,7 @@ std::shared_ptr<iceberg::StructType> MakeStructType(Args&&... 
args) {
 template <typename... Args>
 std::unique_ptr<iceberg::Schema> MakeSchema(Args&&... args) {
   return std::make_unique<iceberg::Schema>(
-      std::vector<iceberg::SchemaField>{std::move(args)...}, std::nullopt);
+      std::vector<iceberg::SchemaField>{std::move(args)...});
 }
 
 TEST(SchemaTest, Basics) {
diff --git a/src/iceberg/test/table_metadata_builder_test.cc 
b/src/iceberg/test/table_metadata_builder_test.cc
index 77380e89..84a272c8 100644
--- a/src/iceberg/test/table_metadata_builder_test.cc
+++ b/src/iceberg/test/table_metadata_builder_test.cc
@@ -55,7 +55,8 @@ std::shared_ptr<Schema> CreateInvalidSchema() {
   auto field2 = SchemaField::MakeRequired(5, "part_col", string());
   auto field3 = SchemaField::MakeRequired(8, "sort_col", timestamp());
   return std::make_shared<Schema>(std::vector<SchemaField>{field1, field2, 
field3},
-                                  std::make_optional(1), 
std::vector<int32_t>{1});
+                                  /*schema_id=*/1,
+                                  
/*identifier_field_ids=*/std::vector<int32_t>{10});
 }
 
 // Helper function to create a simple schema with disordered field_ids
@@ -64,7 +65,8 @@ std::shared_ptr<Schema> CreateDisorderedSchema() {
   auto field2 = SchemaField::MakeRequired(5, "part_col", string());
   auto field3 = SchemaField::MakeRequired(8, "sort_col", timestamp());
   return std::make_shared<Schema>(std::vector<SchemaField>{field1, field2, 
field3},
-                                  std::make_optional(1), 
std::vector<int32_t>{2});
+                                  /*schema_id=*/1,
+                                  
/*identifier_field_ids=*/std::vector<int32_t>{2});
 }
 
 // Helper function to create base metadata for tests
@@ -559,7 +561,7 @@ TEST(TableMetadataBuilderTest, AddSchemaBasic) {
   builder->AddSchema(new_schema);
   ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
   ASSERT_EQ(metadata->schemas.size(), 2);
-  EXPECT_EQ(metadata->schemas[1]->schema_id().value(), 1);
+  EXPECT_EQ(metadata->schemas[1]->schema_id(), 1);
   EXPECT_EQ(metadata->last_column_id, 5);
 
   // 2. Add duplicate schema - should be idempotent
@@ -580,8 +582,8 @@ TEST(TableMetadataBuilderTest, AddSchemaBasic) {
   builder->AddSchema(schema4);
   ICEBERG_UNWRAP_OR_FAIL(metadata, builder->Build());
   ASSERT_EQ(metadata->schemas.size(), 3);
-  EXPECT_EQ(metadata->schemas[1]->schema_id().value(), 1);
-  EXPECT_EQ(metadata->schemas[2]->schema_id().value(), 2);
+  EXPECT_EQ(metadata->schemas[1]->schema_id(), 1);
+  EXPECT_EQ(metadata->schemas[2]->schema_id(), 2);
   EXPECT_EQ(metadata->last_column_id, 6);
 }
 
@@ -643,7 +645,7 @@ TEST(TableMetadataBuilderTest, AddSchemaIdempotent) {
 
   ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
   ASSERT_EQ(metadata->schemas.size(), 2);  // Only one new schema should be 
added
-  EXPECT_EQ(metadata->schemas[1]->schema_id().value(), 1);
+  EXPECT_EQ(metadata->schemas[1]->schema_id(), 1);
 }
 
 TEST(TableMetadataBuilderTest, AddSchemaMultipleDifferent) {
@@ -665,9 +667,9 @@ TEST(TableMetadataBuilderTest, AddSchemaMultipleDifferent) {
 
   ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
   ASSERT_EQ(metadata->schemas.size(), 4);  // Original + 3 new
-  EXPECT_EQ(metadata->schemas[1]->schema_id().value(), 1);
-  EXPECT_EQ(metadata->schemas[2]->schema_id().value(), 2);
-  EXPECT_EQ(metadata->schemas[3]->schema_id().value(), 3);
+  EXPECT_EQ(metadata->schemas[1]->schema_id(), 1);
+  EXPECT_EQ(metadata->schemas[2]->schema_id(), 2);
+  EXPECT_EQ(metadata->schemas[3]->schema_id(), 3);
   EXPECT_EQ(metadata->last_column_id, 6);
 }
 
@@ -682,8 +684,8 @@ TEST(TableMetadataBuilderTest, SetCurrentSchemaBasic) {
   builder->SetCurrentSchema(new_schema, 4);
   ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
   ASSERT_EQ(metadata->schemas.size(), 2);
-  EXPECT_EQ(metadata->current_schema_id.value(), 1);
-  EXPECT_EQ(metadata->schemas[1]->schema_id().value(), 1);
+  EXPECT_EQ(metadata->current_schema_id, 1);
+  EXPECT_EQ(metadata->schemas[1]->schema_id(), 1);
   EXPECT_EQ(metadata->last_column_id, 4);
 
   // 2. Set current schema by schema ID
@@ -692,7 +694,7 @@ TEST(TableMetadataBuilderTest, SetCurrentSchemaBasic) {
   builder->AddSchema(schema1);
   builder->SetCurrentSchema(1);
   ICEBERG_UNWRAP_OR_FAIL(metadata, builder->Build());
-  EXPECT_EQ(metadata->current_schema_id.value(), 1);
+  EXPECT_EQ(metadata->current_schema_id, 1);
 
   // 3. Set current schema using -1 (last added)
   builder = TableMetadataBuilder::BuildFrom(base.get());
@@ -701,13 +703,13 @@ TEST(TableMetadataBuilderTest, SetCurrentSchemaBasic) {
   builder->AddSchema(schema2);
   builder->SetCurrentSchema(-1);  // Use last added
   ICEBERG_UNWRAP_OR_FAIL(metadata, builder->Build());
-  EXPECT_EQ(metadata->current_schema_id.value(), 1);
+  EXPECT_EQ(metadata->current_schema_id, 1);
 
   // 4. Setting same schema is no-op
   builder = TableMetadataBuilder::BuildFrom(base.get());
   builder->SetCurrentSchema(0);
   ICEBERG_UNWRAP_OR_FAIL(metadata, builder->Build());
-  EXPECT_EQ(metadata->current_schema_id.value(), 0);
+  EXPECT_EQ(metadata->current_schema_id, 0);
 }
 
 TEST(TableMetadataBuilderTest, SetCurrentSchemaWithInvalidLastColumnId) {
@@ -732,7 +734,7 @@ TEST(TableMetadataBuilderTest, 
SetCurrentSchemaUpdatesLastColumnId) {
   auto new_schema = std::make_shared<Schema>(std::vector<SchemaField>{field1, 
field2}, 1);
   builder->SetCurrentSchema(new_schema, 10);  // Higher than field IDs
   ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
-  EXPECT_EQ(metadata->current_schema_id.value(), 1);
+  EXPECT_EQ(metadata->current_schema_id, 1);
   EXPECT_EQ(metadata->last_column_id, 10);
 }
 
@@ -753,7 +755,7 @@ TEST(TableMetadataBuilderTest, AddSchemaWithNestedTypes) {
   ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
 
   ASSERT_EQ(metadata->schemas.size(), 2);
-  EXPECT_EQ(metadata->schemas[1]->schema_id().value(), 1);
+  EXPECT_EQ(metadata->schemas[1]->schema_id(), 1);
   EXPECT_EQ(metadata->last_column_id, 11);  // Should be highest nested field 
ID
 }
 
@@ -777,7 +779,7 @@ TEST(TableMetadataBuilderTest, 
AddSchemaWithListAndMapTypes) {
   ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
 
   ASSERT_EQ(metadata->schemas.size(), 2);
-  EXPECT_EQ(metadata->schemas[1]->schema_id().value(), 1);
+  EXPECT_EQ(metadata->schemas[1]->schema_id(), 1);
   EXPECT_EQ(metadata->last_column_id, 12);  // Should be highest field ID 
including nested
 }
 
@@ -803,10 +805,10 @@ TEST(TableMetadataBuilderTest, AddSchemaSequentialIds) {
   ASSERT_EQ(metadata->schemas.size(), 4);  // Original + 3 new
 
   // Verify sequential schema IDs
-  EXPECT_EQ(metadata->schemas[0]->schema_id().value(), 0);  // Original
-  EXPECT_EQ(metadata->schemas[1]->schema_id().value(), 1);
-  EXPECT_EQ(metadata->schemas[2]->schema_id().value(), 2);
-  EXPECT_EQ(metadata->schemas[3]->schema_id().value(), 3);
+  EXPECT_EQ(metadata->schemas[0]->schema_id(), 0);  // Original
+  EXPECT_EQ(metadata->schemas[1]->schema_id(), 1);
+  EXPECT_EQ(metadata->schemas[2]->schema_id(), 2);
+  EXPECT_EQ(metadata->schemas[3]->schema_id(), 3);
 }
 
 TEST(TableMetadataBuilderTest, AddSchemaWithOptionalFields) {
@@ -823,7 +825,7 @@ TEST(TableMetadataBuilderTest, AddSchemaWithOptionalFields) 
{
   ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
 
   ASSERT_EQ(metadata->schemas.size(), 2);
-  EXPECT_EQ(metadata->schemas[1]->schema_id().value(), 1);
+  EXPECT_EQ(metadata->schemas[1]->schema_id(), 1);
 
   // Verify field properties
   const auto& fields = metadata->schemas[1]->fields();
@@ -872,7 +874,7 @@ TEST(TableMetadataBuilderTest, 
SetCurrentSchemaWithPartitionSpecRebuild) {
   ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
 
   // Verify schema was set
-  EXPECT_EQ(metadata->current_schema_id.value(), 1);
+  EXPECT_EQ(metadata->current_schema_id, 1);
   EXPECT_EQ(metadata->last_column_id, 5);
 
   // Verify partition specs were rebuilt (they should still exist)
@@ -896,19 +898,19 @@ TEST(TableMetadataBuilderTest, 
SetCurrentSchemaMultipleOperations) {
   // Set current to first added schema
   builder->SetCurrentSchema(1);
   ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
-  EXPECT_EQ(metadata->current_schema_id.value(), 1);
+  EXPECT_EQ(metadata->current_schema_id, 1);
 
   // Change current to second schema
   builder = TableMetadataBuilder::BuildFrom(metadata.get());
   builder->SetCurrentSchema(2);
   ICEBERG_UNWRAP_OR_FAIL(metadata, builder->Build());
-  EXPECT_EQ(metadata->current_schema_id.value(), 2);
+  EXPECT_EQ(metadata->current_schema_id, 2);
 
   // Change back to original schema
   builder = TableMetadataBuilder::BuildFrom(metadata.get());
   builder->SetCurrentSchema(0);
   ICEBERG_UNWRAP_OR_FAIL(metadata, builder->Build());
-  EXPECT_EQ(metadata->current_schema_id.value(), 0);
+  EXPECT_EQ(metadata->current_schema_id, 0);
 }
 
 TEST(TableMetadataBuilderTest, SetCurrentSchemaLastAddedTracking) {
@@ -928,7 +930,7 @@ TEST(TableMetadataBuilderTest, 
SetCurrentSchemaLastAddedTracking) {
   // Use -1 to set current to last added schema
   builder->SetCurrentSchema(-1);
   ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
-  EXPECT_EQ(metadata->current_schema_id.value(), 2);  // Should be schema2
+  EXPECT_EQ(metadata->current_schema_id, 2);  // Should be schema2
 }
 
 TEST(TableMetadataBuilderTest, AddSchemaAndSetCurrentCombined) {
@@ -944,8 +946,8 @@ TEST(TableMetadataBuilderTest, 
AddSchemaAndSetCurrentCombined) {
 
   ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
   ASSERT_EQ(metadata->schemas.size(), 2);
-  EXPECT_EQ(metadata->current_schema_id.value(), 1);
-  EXPECT_EQ(metadata->schemas[1]->schema_id().value(), 1);
+  EXPECT_EQ(metadata->current_schema_id, 1);
+  EXPECT_EQ(metadata->schemas[1]->schema_id(), 1);
   EXPECT_EQ(metadata->last_column_id, 4);
 }
 
@@ -998,7 +1000,7 @@ TEST(TableMetadataBuilderTest, 
SetCurrentSchemaRebuildsSpecsAndOrders) {
   ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
 
   // Verify schema was set
-  EXPECT_EQ(metadata->current_schema_id.value(), 1);
+  EXPECT_EQ(metadata->current_schema_id, 1);
 
   // Verify partition specs were rebuilt (they should still exist)
   ASSERT_EQ(metadata->partition_specs.size(), 2);
@@ -1032,16 +1034,16 @@ TEST(TableMetadataBuilderTest, RemoveSchemasBasic) {
   builder->RemoveSchemas({1});
   ICEBERG_UNWRAP_OR_FAIL(metadata, builder->Build());
   ASSERT_EQ(metadata->schemas.size(), 3);
-  EXPECT_EQ(metadata->schemas[0]->schema_id().value(), 0);
-  EXPECT_EQ(metadata->schemas[1]->schema_id().value(), 2);
-  EXPECT_EQ(metadata->schemas[2]->schema_id().value(), 3);
+  EXPECT_EQ(metadata->schemas[0]->schema_id(), 0);
+  EXPECT_EQ(metadata->schemas[1]->schema_id(), 2);
+  EXPECT_EQ(metadata->schemas[2]->schema_id(), 3);
 
   // Remove multiple schemas
   builder = TableMetadataBuilder::BuildFrom(metadata.get());
   builder->RemoveSchemas({2, 3});
   ICEBERG_UNWRAP_OR_FAIL(metadata, builder->Build());
   ASSERT_EQ(metadata->schemas.size(), 1);
-  EXPECT_EQ(metadata->schemas[0]->schema_id().value(), 
Schema::kInitialSchemaId);
+  EXPECT_EQ(metadata->schemas[0]->schema_id(), Schema::kInitialSchemaId);
 }
 
 TEST(TableMetadataBuilderTest, RemoveSchemasCannotRemoveCurrent) {
@@ -1055,7 +1057,7 @@ TEST(TableMetadataBuilderTest, 
RemoveSchemasCannotRemoveCurrent) {
 
   ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
   ASSERT_EQ(metadata->schemas.size(), 2);
-  EXPECT_EQ(metadata->current_schema_id.value(), 0);
+  EXPECT_EQ(metadata->current_schema_id, 0);
 
   // Try to remove current schema (ID 0)
   builder = TableMetadataBuilder::BuildFrom(metadata.get());
@@ -1093,7 +1095,7 @@ TEST(TableMetadataBuilderTest, RemoveSchemasNonExistent) {
   builder->RemoveSchemas({1, 999, 888});
   ICEBERG_UNWRAP_OR_FAIL(metadata, builder->Build());
   ASSERT_EQ(metadata->schemas.size(), 1);
-  EXPECT_EQ(metadata->schemas[0]->schema_id().value(), 
Schema::kInitialSchemaId);
+  EXPECT_EQ(metadata->schemas[0]->schema_id(), Schema::kInitialSchemaId);
 }
 
 TEST(TableMetadataBuilderTest, RemoveSchemasEmptySet) {
@@ -1131,16 +1133,16 @@ TEST(TableMetadataBuilderTest, 
RemoveSchemasAfterSchemaChange) {
 
   ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
   ASSERT_EQ(metadata->schemas.size(), 3);
-  EXPECT_EQ(metadata->current_schema_id.value(), 1);
+  EXPECT_EQ(metadata->current_schema_id, 1);
 
   // Now remove the old current schema (ID 0)
   builder = TableMetadataBuilder::BuildFrom(metadata.get());
   builder->RemoveSchemas({0});
   ICEBERG_UNWRAP_OR_FAIL(metadata, builder->Build());
   ASSERT_EQ(metadata->schemas.size(), 2);
-  EXPECT_EQ(metadata->current_schema_id.value(), 1);
-  EXPECT_EQ(metadata->schemas[0]->schema_id().value(), 1);
-  EXPECT_EQ(metadata->schemas[1]->schema_id().value(), 2);
+  EXPECT_EQ(metadata->current_schema_id, 1);
+  EXPECT_EQ(metadata->schemas[0]->schema_id(), 1);
+  EXPECT_EQ(metadata->schemas[1]->schema_id(), 2);
 
   // Cannot remove the new current schema
   builder = TableMetadataBuilder::BuildFrom(metadata.get());
diff --git a/src/iceberg/test/table_requirement_test.cc 
b/src/iceberg/test/table_requirement_test.cc
index 07055f18..d50d9f41 100644
--- a/src/iceberg/test/table_requirement_test.cc
+++ b/src/iceberg/test/table_requirement_test.cc
@@ -77,13 +77,6 @@ TEST(TableRequirementTest, AssertCurrentSchemaID) {
   status = req_for_null.Validate(nullptr);
   EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
   EXPECT_THAT(status, HasErrorMessage("metadata is missing"));
-
-  // Schema ID not set
-  base->current_schema_id = std::nullopt;
-  table::AssertCurrentSchemaID req_for_nullopt(5);
-  status = req_for_nullopt.Validate(base.get());
-  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
-  EXPECT_THAT(status, HasErrorMessage("schema ID is not set"));
 }
 
 TEST(TableRequirementTest, AssertDoesNotExist) {
diff --git a/src/iceberg/util/snapshot_util.cc 
b/src/iceberg/util/snapshot_util.cc
index d2d083ea..e76426f3 100644
--- a/src/iceberg/util/snapshot_util.cc
+++ b/src/iceberg/util/snapshot_util.cc
@@ -298,7 +298,7 @@ Result<std::shared_ptr<Schema>> 
SnapshotUtil::SchemaFor(const TableMetadata& met
     return metadata.Schema();
   }
 
-  return metadata.SchemaById(snapshot->schema_id);
+  return metadata.SchemaById(snapshot->schema_id.value());
 }
 
 Result<std::shared_ptr<Snapshot>> SnapshotUtil::LatestSnapshot(


Reply via email to