|
20 | 20 | #include "iceberg/table_metadata.h" |
21 | 21 |
|
22 | 22 | #include <algorithm> |
| 23 | +#include <chrono> |
23 | 24 | #include <format> |
24 | 25 | #include <string> |
25 | 26 |
|
|
36 | 37 | #include "iceberg/table_update.h" |
37 | 38 | #include "iceberg/util/gzip_internal.h" |
38 | 39 | #include "iceberg/util/macros.h" |
| 40 | +#include "iceberg/util/uuid.h" |
39 | 41 |
|
40 | 42 | namespace iceberg { |
41 | 43 |
|
@@ -201,13 +203,46 @@ Status TableMetadataUtil::Write(FileIO& io, const std::string& location, |
201 | 203 |
|
202 | 204 | // TableMetadataBuilder implementation |
203 | 205 |
|
204 | | -struct TableMetadataBuilder::Impl {}; |
| 206 | +struct TableMetadataBuilder::Impl { |
| 207 | + // Base metadata (nullptr for new tables) |
| 208 | + const TableMetadata* base; |
| 209 | + |
| 210 | + // Working metadata copy |
| 211 | + TableMetadata metadata; |
| 212 | + |
| 213 | + // Change tracking |
| 214 | + std::vector<std::unique_ptr<TableUpdate>> changes; |
| 215 | + |
| 216 | + // Error collection (since methods return *this and cannot throw) |
| 217 | + std::vector<Status> errors; |
| 218 | + |
| 219 | + // Metadata location tracking |
| 220 | + std::optional<std::string> metadata_location; |
| 221 | + std::optional<std::string> previous_metadata_location; |
| 222 | + |
| 223 | + // Constructor for new table |
| 224 | + explicit Impl(int8_t format_version) : base(nullptr), metadata{} { |
| 225 | + metadata.format_version = format_version; |
| 226 | + metadata.last_sequence_number = TableMetadata::kInitialSequenceNumber; |
| 227 | + metadata.last_updated_ms = TimePointMs{std::chrono::milliseconds(0)}; |
| 228 | + metadata.last_column_id = 0; |
| 229 | + metadata.default_spec_id = TableMetadata::kInitialSpecId; |
| 230 | + metadata.last_partition_id = 0; |
| 231 | + metadata.current_snapshot_id = TableMetadata::kInvalidSnapshotId; |
| 232 | + metadata.default_sort_order_id = TableMetadata::kInitialSortOrderId; |
| 233 | + metadata.next_row_id = TableMetadata::kInitialRowId; |
| 234 | + } |
| 235 | + |
| 236 | + // Constructor from existing metadata |
| 237 | + explicit Impl(const TableMetadata* base_metadata) |
| 238 | + : base(base_metadata), metadata(*base_metadata) {} |
| 239 | +}; |
205 | 240 |
|
206 | 241 | TableMetadataBuilder::TableMetadataBuilder(int8_t format_version) |
207 | | - : impl_(std::make_unique<Impl>()) {} |
| 242 | + : impl_(std::make_unique<Impl>(format_version)) {} |
208 | 243 |
|
209 | 244 | TableMetadataBuilder::TableMetadataBuilder(const TableMetadata* base) |
210 | | - : impl_(std::make_unique<Impl>()) {} |
| 245 | + : impl_(std::make_unique<Impl>(base)) {} |
211 | 246 |
|
212 | 247 | TableMetadataBuilder::~TableMetadataBuilder() = default; |
213 | 248 |
|
@@ -238,12 +273,35 @@ TableMetadataBuilder& TableMetadataBuilder::SetPreviousMetadataLocation( |
238 | 273 | } |
239 | 274 |
|
240 | 275 | TableMetadataBuilder& TableMetadataBuilder::AssignUUID() { |
241 | | - throw IcebergError(std::format("{} not implemented", __FUNCTION__)); |
| 276 | + if (impl_->metadata.table_uuid.empty()) { |
| 277 | + // Generate a random UUID |
| 278 | + return AssignUUID(Uuid::GenerateV4().ToString()); |
| 279 | + } |
| 280 | + |
| 281 | + return *this; |
242 | 282 | } |
243 | 283 |
|
244 | 284 | TableMetadataBuilder& TableMetadataBuilder::AssignUUID(std::string_view uuid) { |
245 | | - throw IcebergError(std::format("{} not implemented", __FUNCTION__)); |
246 | | - ; |
| 285 | + std::string uuid_str(uuid); |
| 286 | + |
| 287 | + // Validation: UUID cannot be null or empty |
| 288 | + if (uuid_str.empty()) { |
| 289 | + impl_->errors.emplace_back(InvalidArgument("Cannot assign null or empty UUID")); |
| 290 | + return *this; |
| 291 | + } |
| 292 | + |
| 293 | + // Check if UUID is already set to the same value (no-op) |
| 294 | + if (StringUtils::EqualsIgnoreCase(impl_->metadata.table_uuid, uuid_str)) { |
| 295 | + return *this; |
| 296 | + } |
| 297 | + |
| 298 | + // Update the metadata |
| 299 | + impl_->metadata.table_uuid = uuid_str; |
| 300 | + |
| 301 | + // Record the change |
| 302 | + impl_->changes.push_back(std::make_unique<table::AssignUUID>(uuid_str)); |
| 303 | + |
| 304 | + return *this; |
247 | 305 | } |
248 | 306 |
|
249 | 307 | TableMetadataBuilder& TableMetadataBuilder::UpgradeFormatVersion( |
@@ -378,11 +436,50 @@ TableMetadataBuilder& TableMetadataBuilder::RemoveEncryptionKey(std::string_view |
378 | 436 | } |
379 | 437 |
|
380 | 438 | TableMetadataBuilder& TableMetadataBuilder::DiscardChanges() { |
381 | | - throw IcebergError(std::format("{} not implemented", __FUNCTION__)); |
| 439 | + // Clear all changes and errors |
| 440 | + impl_->changes.clear(); |
| 441 | + impl_->errors.clear(); |
| 442 | + |
| 443 | + // Reset metadata to base state |
| 444 | + if (impl_->base != nullptr) { |
| 445 | + impl_->metadata = *impl_->base; |
| 446 | + } else { |
| 447 | + // Reset to initial state for new table |
| 448 | + *impl_ = Impl(impl_->metadata.format_version); |
| 449 | + } |
| 450 | + |
| 451 | + return *this; |
382 | 452 | } |
383 | 453 |
|
384 | 454 | Result<std::unique_ptr<TableMetadata>> TableMetadataBuilder::Build() { |
385 | | - return NotImplemented("TableMetadataBuilder::Build not implemented"); |
| 455 | + // 1. Check for accumulated errors |
| 456 | + if (!impl_->errors.empty()) { |
| 457 | + std::string error_msg = "Failed to build TableMetadata due to validation errors:\n"; |
| 458 | + for (const auto& error : impl_->errors) { |
| 459 | + error_msg += " - " + error.error().message + "\n"; |
| 460 | + } |
| 461 | + return CommitFailed("{}", error_msg); |
| 462 | + } |
| 463 | + |
| 464 | + // 2. Validate metadata consistency |
| 465 | + |
| 466 | + // Validate UUID exists for format version > 1 |
| 467 | + if (impl_->metadata.format_version > 1 && impl_->metadata.table_uuid.empty()) { |
| 468 | + return InvalidArgument("UUID is required for format version {}", |
| 469 | + impl_->metadata.format_version); |
| 470 | + } |
| 471 | + |
| 472 | + // 3. Update last_updated_ms if there are changes |
| 473 | + if (!impl_->changes.empty() && impl_->base != nullptr) { |
| 474 | + impl_->metadata.last_updated_ms = |
| 475 | + TimePointMs{std::chrono::duration_cast<std::chrono::milliseconds>( |
| 476 | + std::chrono::system_clock::now().time_since_epoch())}; |
| 477 | + } |
| 478 | + |
| 479 | + // 4. Create and return the TableMetadata |
| 480 | + auto result = std::make_unique<TableMetadata>(std::move(impl_->metadata)); |
| 481 | + |
| 482 | + return result; |
386 | 483 | } |
387 | 484 |
|
388 | 485 | } // namespace iceberg |
0 commit comments