|
73 | 73 | #include <Storages/ObjectStorage/DataLakes/Iceberg/IcebergMetadata.h> |
74 | 74 | #include <Storages/ObjectStorage/DataLakes/Iceberg/IcebergTableStateSnapshot.h> |
75 | 75 | #include <Storages/ObjectStorage/DataLakes/Iceberg/IcebergWrites.h> |
| 76 | +#include <Storages/ObjectStorage/DataLakes/Iceberg/MetadataGenerator.h> |
76 | 77 | #include <Storages/ObjectStorage/DataLakes/Iceberg/ManifestFile.h> |
77 | 78 | #include <Storages/ObjectStorage/DataLakes/Iceberg/ManifestFilesPruning.h> |
78 | 79 | #include <Storages/ObjectStorage/DataLakes/Iceberg/Mutations.h> |
@@ -115,6 +116,7 @@ extern const int ICEBERG_SPECIFICATION_VIOLATION; |
115 | 116 | extern const int S3_ERROR; |
116 | 117 | extern const int TABLE_ALREADY_EXISTS; |
117 | 118 | extern const int SUPPORT_IS_DISABLED; |
| 119 | +extern const int INCORRECT_DATA; |
118 | 120 | } |
119 | 121 |
|
120 | 122 | namespace Setting |
@@ -277,15 +279,17 @@ void IcebergMetadata::backgroundMetadataPrefetcherThread() |
277 | 279 | /// first, we fetch the latest metadata version and cache it; |
278 | 280 | /// as a part of the same method, we download metadata.json of the latest metadata version |
279 | 281 | /// and after parsing it, we fetch manifest lists, parse and cache them |
280 | | - auto ctx = Context::getGlobalContextInstance()->getBackgroundContext(); |
| 282 | + auto ctx = Context::createCopy(Context::getGlobalContextInstance()); |
281 | 283 | auto [actual_data_snapshot, actual_table_state_snapshot] = getRelevantState(ctx, true); |
282 | 284 | if (actual_data_snapshot) |
283 | 285 | { |
284 | 286 | for (const auto & entry : actual_data_snapshot->manifest_list_entries) |
285 | 287 | { |
286 | 288 | /// second, we fetch, parse and cache each manifest file |
287 | | - auto manifest_file_ptr = getManifestFileEntriesHandle( |
288 | | - object_storage, persistent_components, ctx, log, entry, actual_table_state_snapshot.schema_id); |
| 289 | + auto manifest_file_ptr = Iceberg::getManifestFile( |
| 290 | + object_storage, persistent_components, ctx, log, |
| 291 | + entry.manifest_file_path, |
| 292 | + entry.manifest_file_byte_size); |
289 | 293 | } |
290 | 294 | } |
291 | 295 |
|
@@ -611,6 +615,84 @@ void IcebergMetadata::mutate( |
611 | 615 | catalog); |
612 | 616 | } |
613 | 617 |
|
| 618 | +void IcebergMetadata::truncate(ContextPtr context, std::shared_ptr<DataLake::ICatalog> catalog, const StorageID & storage_id) |
| 619 | +{ |
| 620 | + if (!context->getSettingsRef()[Setting::allow_insert_into_iceberg].value) |
| 621 | + throw Exception( |
| 622 | + ErrorCodes::SUPPORT_IS_DISABLED, |
| 623 | + "Iceberg truncate requires the allow_insert_into_iceberg setting to be enabled."); |
| 624 | + |
| 625 | + auto [actual_data_snapshot, actual_table_state_snapshot] = getRelevantState(context); |
| 626 | + auto metadata_object = getMetadataJSONObject( |
| 627 | + actual_table_state_snapshot.metadata_file_path, |
| 628 | + object_storage, |
| 629 | + persistent_components.metadata_cache, |
| 630 | + context, |
| 631 | + log, |
| 632 | + persistent_components.metadata_compression_method, |
| 633 | + persistent_components.table_uuid); |
| 634 | + |
| 635 | + // Use -1 as the Iceberg spec sentinel for "no parent snapshot" |
| 636 | + // (distinct from snapshot ID 0 which is a valid snapshot). |
| 637 | + Int64 parent_snapshot_id = actual_table_state_snapshot.snapshot_id.value_or(-1); |
| 638 | + |
| 639 | + // On antalya-26.6 all metadata paths flow through the table's IcebergPathResolver: |
| 640 | + // FileNamesGenerator produces IcebergPathFromMetadata values (relative to the table |
| 641 | + // location), and resolver.resolve() / resolveForCatalog() turn those into the storage |
| 642 | + // path for I/O and the fully-qualified path the catalog expects. This mirrors the |
| 643 | + // write path in IcebergStorageSink (see IcebergWrites.cpp) so transactional (REST) and |
| 644 | + // non-transactional catalogs are handled uniformly. |
| 645 | + const auto & resolver = persistent_components.path_resolver; |
| 646 | + |
| 647 | + bool is_transactional = (catalog != nullptr && catalog->isTransactional()); |
| 648 | + |
| 649 | + FileNamesGenerator filename_generator( |
| 650 | + resolver.getTableLocation(), |
| 651 | + is_transactional, |
| 652 | + persistent_components.metadata_compression_method, |
| 653 | + write_format); |
| 654 | + |
| 655 | + Int32 new_metadata_version = actual_table_state_snapshot.metadata_version + 1; |
| 656 | + filename_generator.setVersion(new_metadata_version); |
| 657 | + |
| 658 | + auto metadata_info = filename_generator.generateMetadataPathWithInfo(); |
| 659 | + |
| 660 | + auto [new_snapshot, manifest_list_path] = MetadataGenerator(metadata_object).generateNextMetadata( |
| 661 | + filename_generator, metadata_info.path, parent_snapshot_id, |
| 662 | + /* added_files */ 0, /* added_records */ 0, /* added_files_size */ 0, |
| 663 | + /* num_partitions */ 0, /* added_delete_files */ 0, /* num_deleted_rows */ 0, |
| 664 | + std::nullopt, std::nullopt, /*is_truncate=*/true); |
| 665 | + |
| 666 | + auto storage_manifest_list_name = resolver.resolve(manifest_list_path); |
| 667 | + |
| 668 | + auto write_settings = context->getWriteSettings(); |
| 669 | + auto buf = object_storage->writeObject( |
| 670 | + StoredObject(storage_manifest_list_name), |
| 671 | + WriteMode::Rewrite, std::nullopt, |
| 672 | + DBMS_DEFAULT_BUFFER_SIZE, write_settings); |
| 673 | + |
| 674 | + // Truncate writes a metadata-only overwrite snapshot: an empty manifest list |
| 675 | + // (no manifest entries, no sizes) that supersedes all previous snapshots. |
| 676 | + generateManifestList(resolver, metadata_object, object_storage, |
| 677 | + context, {}, new_snapshot, {}, *buf, Iceberg::FileContentType::DATA, /*use_previous_snapshots=*/false); |
| 678 | + buf->finalize(); |
| 679 | + |
| 680 | + String metadata_content = dumpMetadataObjectToString(metadata_object); |
| 681 | + writeMessageToFile(metadata_content, resolver.resolve(metadata_info.path), object_storage, |
| 682 | + context, "*", "", persistent_components.metadata_compression_method); |
| 683 | + |
| 684 | + if (catalog) |
| 685 | + { |
| 686 | + String catalog_filename = resolver.resolveForCatalog(metadata_info.path); |
| 687 | + |
| 688 | + const auto & [namespace_name, table_name] = DataLake::parseTableName(storage_id.getTableName()); |
| 689 | + if (!catalog->updateMetadata(namespace_name, table_name, catalog_filename, new_snapshot)) |
| 690 | + throw Exception(ErrorCodes::INCORRECT_DATA, |
| 691 | + "Failed to commit Iceberg truncate update to catalog."); |
| 692 | + } |
| 693 | +} |
| 694 | + |
| 695 | + |
614 | 696 | void IcebergMetadata::checkMutationIsPossible(const MutationCommands & commands) |
615 | 697 | { |
616 | 698 | for (const auto & command : commands) |
|
0 commit comments