|
21 | 21 |
|
22 | 22 | #include <unistd.h> |
23 | 23 |
|
24 | | -#include <algorithm> |
25 | 24 | #include <chrono> |
| 25 | +#include <filesystem> |
| 26 | +#include <fstream> |
26 | 27 | #include <memory> |
27 | 28 | #include <print> |
28 | 29 | #include <string> |
@@ -687,4 +688,142 @@ TEST_F(RestCatalogIntegrationTest, StageCreateTable) { |
687 | 688 | EXPECT_EQ(props.at("key1"), "value1"); |
688 | 689 | } |
689 | 690 |
|
| 691 | +TEST_F(RestCatalogIntegrationTest, LoadTableWithSnapshotLoadingMode) { |
| 692 | + auto catalog_result = CreateCatalog(); |
| 693 | + ASSERT_THAT(catalog_result, IsOk()); |
| 694 | + auto& catalog = catalog_result.value(); |
| 695 | + |
| 696 | + Namespace ns{.levels = {"test_snapshot_mode"}}; |
| 697 | + auto status = catalog->CreateNamespace(ns, {}); |
| 698 | + ASSERT_THAT(status, IsOk()); |
| 699 | + |
| 700 | + auto schema = CreateDefaultSchema(); |
| 701 | + auto partition_spec = PartitionSpec::Unpartitioned(); |
| 702 | + auto sort_order = SortOrder::Unsorted(); |
| 703 | + |
| 704 | + TableIdentifier table_id{.ns = ns, .name = "snapshot_mode_table"}; |
| 705 | + std::unordered_map<std::string, std::string> table_properties; |
| 706 | + auto table_result = catalog->CreateTable(table_id, schema, partition_spec, sort_order, |
| 707 | + "", table_properties); |
| 708 | + ASSERT_THAT(table_result, IsOk()); |
| 709 | + auto& table = table_result.value(); |
| 710 | + |
| 711 | + std::string original_metadata_location(table->metadata_file_location()); |
| 712 | + status = catalog->DropTable(table_id, /*purge=*/false); |
| 713 | + ASSERT_THAT(status, IsOk()); |
| 714 | + |
| 715 | + // Create a fake metadata JSON with 2 snapshots: |
| 716 | + // - Snapshot 1: not referenced by any branch/tag (will be filtered in REFS mode) |
| 717 | + // - Snapshot 2: referenced by main branch (will be loaded in both modes) |
| 718 | + auto& test_data_dir = docker_compose_->test_data_dir(); |
| 719 | + auto metadata_filename = std::format("00000-{}.metadata.json", getpid()); |
| 720 | + auto metadata_path = test_data_dir / metadata_filename; |
| 721 | + auto container_metadata_path = std::format("/tmp/{}", metadata_filename); |
| 722 | + std::string fake_metadata_json = std::format(R"({{ |
| 723 | + "format-version": 2, |
| 724 | + "table-uuid": "12345678-1234-5678-1234-123456789abc", |
| 725 | + "location": "file:/tmp/iceberg_warehouse/{}", |
| 726 | + "last-sequence-number": 2, |
| 727 | + "last-updated-ms": 1602638573590, |
| 728 | + "last-column-id": 2, |
| 729 | + "current-schema-id": 0, |
| 730 | + "schemas": [{{"type": "struct", "schema-id": 0, "fields": [ |
| 731 | + {{"id": 1, "name": "id", "required": true, "type": "int"}}, |
| 732 | + {{"id": 2, "name": "data", "required": false, "type": "string"}} |
| 733 | + ]}}], |
| 734 | + "default-spec-id": 0, |
| 735 | + "partition-specs": [{{"spec-id": 0, "fields": []}}], |
| 736 | + "last-partition-id": 1000, |
| 737 | + "default-sort-order-id": 0, |
| 738 | + "sort-orders": [{{"order-id": 0, "fields": []}}], |
| 739 | + "properties": {{}}, |
| 740 | + "current-snapshot-id": 2, |
| 741 | + "snapshots": [ |
| 742 | + {{ |
| 743 | + "snapshot-id": 1, |
| 744 | + "timestamp-ms": 1515100955770, |
| 745 | + "sequence-number": 1, |
| 746 | + "summary": {{"operation": "append"}}, |
| 747 | + "manifest-list": "file:/tmp/iceberg_warehouse/{}/metadata/snap-1.avro" |
| 748 | + }}, |
| 749 | + {{ |
| 750 | + "snapshot-id": 2, |
| 751 | + "parent-snapshot-id": 1, |
| 752 | + "timestamp-ms": 1525100955770, |
| 753 | + "sequence-number": 2, |
| 754 | + "summary": {{"operation": "append"}}, |
| 755 | + "manifest-list": "file:/tmp/iceberg_warehouse/{}/metadata/snap-2.avro" |
| 756 | + }} |
| 757 | + ], |
| 758 | + "snapshot-log": [ |
| 759 | + {{"snapshot-id": 1, "timestamp-ms": 1515100955770}}, |
| 760 | + {{"snapshot-id": 2, "timestamp-ms": 1525100955770}} |
| 761 | + ], |
| 762 | + "metadata-log": [], |
| 763 | + "refs": {{ |
| 764 | + "main": {{ |
| 765 | + "snapshot-id": 2, |
| 766 | + "type": "branch" |
| 767 | + }} |
| 768 | + }} |
| 769 | +}})", |
| 770 | + ns.levels[0], ns.levels[0], ns.levels[0]); |
| 771 | + |
| 772 | + // Write metadata file and register the table |
| 773 | + std::ofstream metadata_file(metadata_path.string()); |
| 774 | + metadata_file << fake_metadata_json; |
| 775 | + metadata_file.close(); |
| 776 | + auto register_metadata_location = std::format("file:{}", container_metadata_path); |
| 777 | + auto register_result = catalog->RegisterTable(table_id, register_metadata_location); |
| 778 | + ASSERT_THAT(register_result, IsOk()); |
| 779 | + |
| 780 | + // Test with ALL mode (default) |
| 781 | + auto config_all = RestCatalogProperties::default_properties(); |
| 782 | + config_all |
| 783 | + ->Set(RestCatalogProperties::kUri, |
| 784 | + std::format("{}:{}", kLocalhostUri, kRestCatalogPort)) |
| 785 | + .Set(RestCatalogProperties::kName, std::string(kCatalogName)) |
| 786 | + .Set(RestCatalogProperties::kWarehouse, std::string(kWarehouseName)) |
| 787 | + .Set(RestCatalogProperties::kSnapshotLoadingMode, std::string("ALL")); |
| 788 | + auto catalog_all_result = |
| 789 | + RestCatalog::Make(*config_all, std::make_shared<test::StdFileIO>()); |
| 790 | + ASSERT_THAT(catalog_all_result, IsOk()); |
| 791 | + auto& catalog_all = catalog_all_result.value(); |
| 792 | + |
| 793 | + // Load table with ALL mode and verify both snapshots are loaded |
| 794 | + auto table_all_result = catalog_all->LoadTable(table_id); |
| 795 | + ASSERT_THAT(table_all_result, IsOk()); |
| 796 | + auto& table_all = table_all_result.value(); |
| 797 | + EXPECT_EQ(table_all->metadata()->snapshots.size(), 2); |
| 798 | + |
| 799 | + // Test with REFS mode |
| 800 | + auto config_refs = RestCatalogProperties::default_properties(); |
| 801 | + config_refs |
| 802 | + ->Set(RestCatalogProperties::kUri, |
| 803 | + std::format("{}:{}", kLocalhostUri, kRestCatalogPort)) |
| 804 | + .Set(RestCatalogProperties::kName, std::string(kCatalogName)) |
| 805 | + .Set(RestCatalogProperties::kWarehouse, std::string(kWarehouseName)) |
| 806 | + .Set(RestCatalogProperties::kSnapshotLoadingMode, std::string("REFS")); |
| 807 | + auto catalog_refs_result = |
| 808 | + RestCatalog::Make(*config_refs, std::make_shared<test::StdFileIO>()); |
| 809 | + ASSERT_THAT(catalog_refs_result, IsOk()); |
| 810 | + auto& catalog_refs = catalog_refs_result.value(); |
| 811 | + |
| 812 | + // Load table with REFS mode and verify only referenced snapshot is loaded |
| 813 | + auto table_refs_result = catalog_refs->LoadTable(table_id); |
| 814 | + ASSERT_THAT(table_refs_result, IsOk()); |
| 815 | + auto& table_refs = table_refs_result.value(); |
| 816 | + EXPECT_EQ(table_refs->metadata()->snapshots.size(), 1); |
| 817 | + EXPECT_EQ(table_refs->metadata()->snapshots[0]->snapshot_id, 2); |
| 818 | + |
| 819 | + // Verify refs are preserved in both modes |
| 820 | + EXPECT_EQ(table_all->metadata()->refs.size(), 1); |
| 821 | + EXPECT_EQ(table_refs->metadata()->refs.size(), 1); |
| 822 | + EXPECT_TRUE(table_all->metadata()->refs.contains("main")); |
| 823 | + EXPECT_TRUE(table_refs->metadata()->refs.contains("main")); |
| 824 | + |
| 825 | + // Clean up metadata file |
| 826 | + std::filesystem::remove(metadata_path); |
| 827 | +} |
| 828 | + |
690 | 829 | } // namespace iceberg::rest |
0 commit comments