Skip to content

Antalya 26.1 : Fix Alter table operations(add, modify, drop, rename) support for Iceberg#1594

Open
subkanthi wants to merge 15 commits intoantalya-26.1from
rename_columns_iceberg
Open

Antalya 26.1 : Fix Alter table operations(add, modify, drop, rename) support for Iceberg#1594
subkanthi wants to merge 15 commits intoantalya-26.1from
rename_columns_iceberg

Conversation

@subkanthi
Copy link
Copy Markdown
Collaborator

@subkanthi subkanthi commented Mar 29, 2026

Changelog category (leave one):

  • Bugfix

Changelog entry (a user-readable short description of the changes that goes to CHANGELOG.md):

Fixes Alter table ADD/DROP/RENAME/MODIFY column for Iceberg tables.

Documentation entry for user-facing changes

Solved ClickHouse#86024.
Fixed logic of updating metadata JSON as per Iceberg REST API Spec.
Added unit/integration tests.

CI/CD Options

Exclude tests:

  • Fast test
  • Integration Tests
  • Stateless tests
  • Stateful tests
  • Performance tests
  • All with ASAN
  • All with TSAN
  • All with MSAN
  • All with UBSAN
  • All with Coverage
  • All with Aarch64
  • All Regression
  • Disable CI Cache

Regression jobs to run:

  • Fast suites (mostly <1h)
  • Aggregate Functions (2h)
  • Alter (1.5h)
  • Benchmark (30m)
  • ClickHouse Keeper (1h)
  • Iceberg (2h)
  • LDAP (1h)
  • Parquet (1.5h)
  • RBAC (1.5h)
  • SSL Server (1h)
  • S3 (2h)
  • S3 Export (2h)
  • Swarms (30m)
  • Tiered Storage (2h)

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 29, 2026

Workflow [PR], commit [e2ae734]

@subkanthi subkanthi changed the title Rename column support for IcebergLocal Antalya 26.1 backport: Rename column support for Iceberg Mar 31, 2026
@subkanthi
Copy link
Copy Markdown
Collaborator Author

ALTER TABLE ice.`nyc.taxis_p_by_day`
    (RENAME COLUMN extra TO trip_extra)

Query id: 249360cb-6640-4246-ba40-3c5c0d2f3759

2026.03.31 16:27:45.208651 [ 3987905 ] {249360cb-6640-4246-ba40-3c5c0d2f3759} <Fatal> : Logical error: 'Metadata is not initialized'.
2026.03.31 16:27:45.208758 [ 3987905 ] {249360cb-6640-4246-ba40-3c5c0d2f3759} <Fatal> : Format string: 'Metadata is not initialized'.
2026.03.31 16:27:46.794815 [ 3987905 ] {249360cb-6640-4246-ba40-3c5c0d2f3759} <Fatal> : Stack trace (when copying this message, always include the lines below):

0. /root/Documents/ClickHouse/contrib/llvm-project/libcxx/include/__exception/exception.h:113: Poco::Exception::Exception(String const&, int) @ 0x00000000232350b2
1. /root/Documents/ClickHouse/src/Common/Exception.cpp:136: DB::Exception::Exception(DB::Exception::MessageMasked&&, int, bool) @ 0x0000000013cf1e5e
2. /root/Documents/ClickHouse/src/Common/Exception.h:172: DB::Exception::Exception(String&&, int, String, bool) @ 0x000000000ba5e3ce
3. /root/Documents/ClickHouse/src/Common/Exception.h:58: DB::Exception::Exception(PreformattedMessage&&, int) @ 0x000000000ba5dec5
4. /root/Documents/ClickHouse/src/Common/Exception.h:190: DB::Exception::Exception<>(int, FormatStringHelperImpl<>) @ 0x0000000013cfa42b
5. /root/Documents/ClickHouse/src/Storages/ObjectStorage/DataLakes/DataLakeConfiguration.h:440: DB::DataLakeConfiguration<DB::StorageS3Configuration, DB::IcebergMetadata, true>::checkAlterIsPossible(DB::AlterCommands const&) @ 0x00000000161021c8
6. /root/Documents/ClickHouse/src/Storages/ObjectStorage/StorageObjectStorage.cpp:804: DB::StorageObjectStorage::checkAlterIsPossible(DB::AlterCommands const&, std::shared_ptr<DB::Context const>) const @ 0x0000000017f4ab9e
7. /root/Documents/ClickHouse/src/Storages/ObjectStorage/StorageObjectStorageCluster.cpp:812: DB::StorageObjectStorageCluster::checkAlterIsPossible(DB::AlterCommands const&, std::shared_ptr<DB::Context const>) const @ 0x0000000017f5d2b8
8. /root/Documents/ClickHouse/src/Interpreters/InterpreterAlterQuery.cpp:274: DB::InterpreterAlterQuery::executeToTable(DB::ASTAlterQuery const&) @ 0x0000000019991d61
9. /root/Documents/ClickHouse/src/Interpreters/InterpreterAlterQuery.cpp:85: DB::InterpreterAlterQuery::execute() @ 0x000000001998e292
10. /root/Documents/ClickHouse/src/Interpreters/executeQuery.cpp:1720: DB::executeQueryImpl(char const*, char const*, std::shared_ptr<DB::Context>, DB::QueryFlags, DB::QueryProcessingStage::Enum, std::unique_ptr<DB::ReadBuffer, std::default_delete<DB::ReadBuffer>>&, boost::intrusive_ptr<DB::IAST>&, std::shared_ptr<DB::ImplicitTransactionControlExecutor>, std::function<void ()>, DB::QueryResultDetails&) @ 0x0000000019d7b016
11. /root/Documents/ClickHouse/src/Interpreters/executeQuery.cpp:1948: DB::executeQuery(String const&, std::shared_ptr<DB::Context>, DB::QueryFlags, DB::QueryProcessingStage::Enum) @ 0x0000000019d73661
12. /root/Documents/ClickHouse/src/Client/LocalConnection.cpp:286: DB::LocalConnection::sendQuery(DB::ConnectionTimeouts const&, String const&, std::unordered_map<String, String, std::hash<String>, std::equal_to<String>, std::allocator<std::pair<String const, String>>> const&, String const&, unsigned long, DB::Settings const*, DB::ClientInfo const*, bool, std::vector<String, std::allocator<String>> const&, std::function<void (DB::Progress const&)>) @ 0x000000001cf7111d
13. /root/Documents/ClickHouse/src/Client/ClientBase.cpp:1305: DB::ClientBase::processOrdinaryQuery(String, boost::intrusive_ptr<DB::IAST>) @ 0x000000001ceee2ec
14. /root/Documents/ClickHouse/src/Client/ClientBase.cpp:2382: DB::ClientBase::processParsedSingleQuery(std::basic_string_view<char, std::char_traits<char>>, boost::intrusive_ptr<DB::IAST>, bool&, unsigned long) @ 0x000000001ceec5ee
15. /root/Documents/ClickHouse/src/Client/ClientBase.cpp:2759: DB::ClientBase::executeMultiQuery(String const&) @ 0x000000001cef80a3
16. /root/Documents/ClickHouse/src/Client/ClientBase.cpp:3000: DB::ClientBase::processQueryText(String const&) @ 0x000000001cef91f0
17. /root/Documents/ClickHouse/src/Client/ClientBase.cpp:3716: DB::ClientBase::runInteractive() @ 0x000000001cf02c8b
18. /root/Documents/ClickHouse/programs/local/LocalServer.cpp:0: DB::LocalServer::main(std::vector<String, std::allocator<String>> const&) @ 0x0000000013fcc9ca
19. /root/Documents/ClickHouse/base/poco/Util/src/Application.cpp:315: Poco::Util::Application::run() @ 0x0000000023310326
20. /root/Documents/ClickHouse/programs/local/LocalServer.cpp:1267: mainEntryClickHouseLocal(int, char**) @ 0x0000000013fdc761
21. /root/Documents/ClickHouse/programs/main.cpp:380: main @ 0x000000000ba50026
22. ? @ 0x000000000002a1ca
23. __libc_start_main @ 0x000000000002a28b
24. _start @ 0x000000000ba4da6e

2026.03.31 16:27:46.802683 [ 3987908 ] {} <Fatal> ClientBase: ########## Short fault info ############
2026.03.31 16:27:46.802722 [ 3987908 ] {} <Fatal> ClientBase: (version 26.1.6.20001.altinityantalya, build id: C0A44390CD84E87ED79B82DD53952B95716112BC, git hash: 3addd5f5a7e558b892f961683c19c5a393dcc55e, architecture: x86_64) (from thread 3987905) Received signal 6
2026.03.31 16:27:46.802728 [ 3987908 ] {} <Fatal> ClientBase: Signal description: Aborted
2026.03.31 16:27:46.802732 [ 3987908 ] {} <Fatal> ClientBase: 
2026.03.31 16:27:46.802760 [ 3987908 ] {} <Fatal> ClientBase: Stack trace: 0x0000706cdd49eb2d 0x0000706cdd44527e 0x0000706cdd4288ff 0x00005793745bef67 0x00005793745bfd24 0x00005793745bffe5 0x000057936c32c3ce 0x000057936c32bec5 0x00005793745c842b 0x00005793769d01c8 0x0000579378818b9e 0x000057937882b2b8 0x000057937a25fd61 0x000057937a25c292 0x000057937a649016 0x000057937a641661 0x000057937d83f11d 0x000057937d7bc2ec 0x000057937d7ba5ee 0x000057937d7c60a3 0x000057937d7c71f0 0x000057937d7d0c8b 0x000057937489a9ca 0x0000579383bde326 0x00005793748aa761 0x000057936c31e026 0x0000706cdd42a1ca 0x0000706cdd42a28b 0x000057936c31ba6e
2026.03.31 16:27:46.802769 [ 3987908 ] {} <Fatal> ClientBase: ########################################
2026.03.31 16:27:46.802867 [ 3987908 ] {} <Fatal> ClientBase: (version 26.1.6.20001.altinityantalya, build id: C0A44390CD84E87ED79B82DD53952B95716112BC, git hash: 3addd5f5a7e558b892f961683c19c5a393dcc55e) (from thread 3987905) (query_id: 249360cb-6640-4246-ba40-3c5c0d2f3759) (query: alter table ice.`nyc.taxis_p_by_day` rename column extra to trip_extra;) Received signal Aborted (6)
2026.03.31 16:27:46.802918 [ 3987908 ] {} <Fatal> ClientBase: 
2026.03.31 16:27:46.802962 [ 3987908 ] {} <Fatal> ClientBase: Stack trace: 0x0000706cdd49eb2d 0x0000706cdd44527e 0x0000706cdd4288ff 0x00005793745bef67 0x00005793745bfd24 0x00005793745bffe5 0x000057936c32c3ce 0x000057936c32bec5 0x00005793745c842b 0x00005793769d01c8 0x0000579378818b9e 0x000057937882b2b8 0x000057937a25fd61 0x000057937a25c292 0x000057937a649016 0x000057937a641661 0x000057937d83f11d 0x000057937d7bc2ec 0x000057937d7ba5ee 0x000057937d7c60a3 0x000057937d7c71f0 0x000057937d7d0c8b 0x000057937489a9ca 0x0000579383bde326 0x00005793748aa761 0x000057936c31e026 0x0000706cdd42a1ca 0x0000706cdd42a28b 0x000057936c31ba6e
2026.03.31 16:27:46.803043 [ 3987908 ] {} <Fatal> ClientBase: 3. pthread_kill @ 0x000000000009eb2d
2026.03.31 16:27:46.803087 [ 3987908 ] {} <Fatal> ClientBase: 4. raise @ 0x000000000004527e
2026.03.31 16:27:46.803142 [ 3987908 ] {} <Fatal> ClientBase: 5. abort @ 0x00000000000288ff
2026.03.31 16:27:49.523119 [ 3987908 ] {} <Fatal> ClientBase: 6. /root/Documents/ClickHouse/src/Common/Exception.cpp:57: DB::abortOnFailedAssertion(String const&, std::basic_string_view<char, std::char_traits<char>>, void* const*, unsigned long, unsigned long) @ 0x0000000013cf0f67
2026.03.31 16:27:49.698761 [ 3987908 ] {} <Fatal> ClientBase: 7. /root/Documents/ClickHouse/src/Common/Exception.cpp:90: DB::handle_error_code(String const&, std::basic_string_view<char, std::char_traits<char>>, int, bool, std::vector<void*, std::allocator<void*>> const&) @ 0x0000000013cf1d24
2026.03.31 16:27:49.723073 [ 3987908 ] {} <Fatal> ClientBase: 8. /root/Documents/ClickHouse/src/Common/Exception.cpp:143: DB::Exception::Exception(DB::Exception::MessageMasked&&, int, bool) @ 0x0000000013cf1fe5
2026.03.31 16:27:49.784329 [ 3987908 ] {} <Fatal> ClientBase: 9. /root/Documents/ClickHouse/src/Common/Exception.h:172: DB::Exception::Exception(String&&, int, String, bool) @ 0x000000000ba5e3ce
2026.03.31 16:27:49.807185 [ 3987908 ] {} <Fatal> ClientBase: 10. /root/Documents/ClickHouse/src/Common/Exception.h:58: DB::Exception::Exception(PreformattedMessage&&, int) @ 0x000000000ba5dec5
2026.03.31 16:27:49.826915 [ 3987908 ] {} <Fatal> ClientBase: 11. /root/Documents/ClickHouse/src/Common/Exception.h:190: DB::Exception::Exception<>(int, FormatStringHelperImpl<>) @ 0x0000000013cfa42b
2026.03.31 16:27:53.567602 [ 3987908 ] {} <Fatal> ClientBase: 12.0. inlined from /root/Documents/ClickHouse/src/Storages/ObjectStorage/DataLakes/DataLakeConfiguration.h:440: DB::DataLakeConfiguration<DB::StorageS3Configuration, DB::IcebergMetadata, true>::assertInitializedDL() const
2026.03.31 16:27:53.567648 [ 3987908 ] {} <Fatal> ClientBase: 12. /root/Documents/ClickHouse/src/Storages/ObjectStorage/DataLakes/DataLakeConfiguration.h:167: DB::DataLakeConfiguration<DB::StorageS3Configuration, DB::IcebergMetadata, true>::checkAlterIsPossible(DB::AlterCommands const&) @ 0x00000000161021c8
2026.03.31 16:27:54.472552 [ 3987908 ] {} <Fatal> ClientBase: 13. /root/Documents/ClickHouse/src/Storages/ObjectStorage/StorageObjectStorage.cpp:804: DB::StorageObjectStorage::checkAlterIsPossible(DB::AlterCommands const&, std::shared_ptr<DB::Context const>) const @ 0x0000000017f4ab9e
2026.03.31 16:27:54.680056 [ 3987908 ] {} <Fatal> ClientBase: 14. /root/Documents/ClickHouse/src/Storages/ObjectStorage/StorageObjectStorageCluster.cpp:812: DB::StorageObjectStorageCluster::checkAlterIsPossible(DB::AlterCommands const&, std::shared_ptr<DB::Context const>) const @ 0x0000000017f5d2b8
2026.03.31 16:27:56.432026 [ 3987908 ] {} <Fatal> ClientBase: 15. /root/Documents/ClickHouse/src/Interpreters/InterpreterAlterQuery.cpp:274: DB::InterpreterAlterQuery::executeToTable(DB::ASTAlterQuery const&) @ 0x0000000019991d61
2026.03.31 16:27:56.462880 [ 3987908 ] {} <Fatal> ClientBase: 16. /root/Documents/ClickHouse/src/Interpreters/InterpreterAlterQuery.cpp:85: DB::InterpreterAlterQuery::execute() @ 0x000000001998e292
2026.03.31 16:27:58.985322 [ 3987908 ] {} <Fatal> ClientBase: 17. /root/Documents/ClickHouse/src/Interpreters/executeQuery.cpp:1720: DB::executeQueryImpl(char const*, char const*, std::shared_ptr<DB::Context>, DB::QueryFlags, DB::QueryProcessingStage::Enum, std::unique_ptr<DB::ReadBuffer, std::default_delete<DB::ReadBuffer>>&, boost::intrusive_ptr<DB::IAST>&, std::shared_ptr<DB::ImplicitTransactionControlExecutor>, std::function<void ()>, DB::QueryResultDetails&) @ 0x0000000019d7b016
2026.03.31 16:27:59.066951 [ 3987908 ] {} <Fatal> ClientBase: 18. /root/Documents/ClickHouse/src/Interpreters/executeQuery.cpp:1948: DB::executeQuery(String const&, std::shared_ptr<DB::Context>, DB::QueryFlags, DB::QueryProcessingStage::Enum) @ 0x0000000019d73661
2026.03.31 16:28:00.241923 [ 3987908 ] {} <Fatal> ClientBase: 19. /root/Documents/ClickHouse/src/Client/LocalConnection.cpp:286: DB::LocalConnection::sendQuery(DB::ConnectionTimeouts const&, String const&, std::unordered_map<String, String, std::hash<String>, std::equal_to<String>, std::allocator<std::pair<String const, String>>> const&, String const&, unsigned long, DB::Settings const*, DB::ClientInfo const*, bool, std::vector<String, std::allocator<String>> const&, std::function<void (DB::Progress const&)>) @ 0x000000001cf7111d
2026.03.31 16:28:01.507287 [ 3987908 ] {} <Fatal> ClientBase: 20. /root/Documents/ClickHouse/src/Client/ClientBase.cpp:1305: DB::ClientBase::processOrdinaryQuery(String, boost::intrusive_ptr<DB::IAST>) @ 0x000000001ceee2ec
2026.03.31 16:28:01.817085 [ 3987908 ] {} <Fatal> ClientBase: 21. /root/Documents/ClickHouse/src/Client/ClientBase.cpp:2382: DB::ClientBase::processParsedSingleQuery(std::basic_string_view<char, std::char_traits<char>>, boost::intrusive_ptr<DB::IAST>, bool&, unsigned long) @ 0x000000001ceec5ee
2026.03.31 16:28:02.078285 [ 3987908 ] {} <Fatal> ClientBase: 22. /root/Documents/ClickHouse/src/Client/ClientBase.cpp:2759: DB::ClientBase::executeMultiQuery(String const&) @ 0x000000001cef80a3
2026.03.31 16:28:02.213265 [ 3987908 ] {} <Fatal> ClientBase: 23. /root/Documents/ClickHouse/src/Client/ClientBase.cpp:3000: DB::ClientBase::processQueryText(String const&) @ 0x000000001cef91f0
2026.03.31 16:28:02.281558 [ 3987908 ] {} <Fatal> ClientBase: 24. /root/Documents/ClickHouse/src/Client/ClientBase.cpp:3716: DB::ClientBase::runInteractive() @ 0x000000001cf02c8b
2026.03.31 16:28:02.351519 [ 3987908 ] {} <Fatal> ClientBase: 25. /root/Documents/ClickHouse/programs/local/LocalServer.cpp:0: DB::LocalServer::main(std::vector<String, std::allocator<String>> const&) @ 0x0000000013fcc9ca
2026.03.31 16:28:03.006053 [ 3987908 ] {} <Fatal> ClientBase: 26. /root/Documents/ClickHouse/base/poco/Util/src/Application.cpp:315: Poco::Util::Application::run() @ 0x0000000023310326
2026.03.31 16:28:03.066159 [ 3987908 ] {} <Fatal> ClientBase: 27. /root/Documents/ClickHouse/programs/local/LocalServer.cpp:1267: mainEntryClickHouseLocal(int, char**) @ 0x0000000013fdc761
2026.03.31 16:28:03.072398 [ 3987908 ] {} <Fatal> ClientBase: 28. /root/Documents/ClickHouse/programs/main.cpp:380: main @ 0x000000000ba50026
2026.03.31 16:28:03.072452 [ 3987908 ] {} <Fatal> ClientBase: 29. ? @ 0x000000000002a1ca
2026.03.31 16:28:03.072498 [ 3987908 ] {} <Fatal> ClientBase: 30. __libc_start_main @ 0x000000000002a28b
2026.03.31 16:28:54.584438 [ 3987908 ] {} <Fatal> ClientBase: 31. _start @ 0x000000000ba4da6e
2026.03.31 16:28:54.584652 [ 3987908 ] {} <Fatal> ClientBase: Changed settings: allow_introspection_functions = true, serialize_query_plan = false, storage_file_read_method = 'mmap', allow_experimental_database_iceberg = true, implicit_select = true
Aborted (core dumped)

@subkanthi
Copy link
Copy Markdown
Collaborator Author

ClickHouse#86024

@subkanthi subkanthi changed the title Antalya 26.1 backport: Rename column support for Iceberg Antalya 26.1 : Fix Rename column support for Iceberg Apr 2, 2026
@subkanthi subkanthi added bugfix and removed backport Backport labels Apr 2, 2026
@subkanthi subkanthi changed the title Antalya 26.1 : Fix Rename column support for Iceberg Antalya 26.1 : Fix Alter table operations(add, modify, drop, rename) support for Iceberg Apr 2, 2026

void assertInitializedDL() const
{
BaseStorageConfiguration::assertInitialized();
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was removed in upstream

@subkanthi
Copy link
Copy Markdown
Collaborator Author

subkanthi commented Apr 2, 2026

Testing:

alter table ice.`nyc.taxis_p_by_day` rename column trip_extra2 to trip_extra3;

ALTER TABLE ice.`nyc.taxis_p_by_day`
    (RENAME COLUMN trip_extra2 TO trip_extra3)

Query id: d5889a6e-3bcf-4a45-9945-84674b988228

Ok.

0 rows in set. Elapsed: 0.889 sec. 

Ubuntu-2404-noble-amd64-base :) show create table ice.`nyc.taxis_p_by_day`;

SHOW CREATE TABLE ice.`nyc.taxis_p_by_day`

Query id: e854cddf-2a1f-41f4-82be-1940ea229b7a

   ┌─statement─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
1. │ CREATE TABLE ice.`nyc.taxis_p_by_day`                                                                                    ↴│
   │↳(                                                                                                                        ↴│
   │↳    `VendorID` Nullable(Int32),                                                                                          ↴│
   │↳    `tpep_pickup_datetime` Nullable(DateTime64(6)),                                                                      ↴│
   │↳    `tpep_dropoff_datetime` Nullable(DateTime64(6)),                                                                     ↴│
   │↳    `passenger_count` Nullable(Int64),                                                                                   ↴│
   │↳    `trip_distance` Nullable(Float64),                                                                                   ↴│
   │↳    `RatecodeID` Nullable(Int64),                                                                                        ↴│
   │↳    `store_and_fwd_flag` Nullable(String),                                                                               ↴│
   │↳    `PULocationID` Nullable(Int32),                                                                                      ↴│
   │↳    `DOLocationID` Nullable(Int32),                                                                                      ↴│
   │↳    `payment_type` Nullable(Int64),                                                                                      ↴│
   │↳    `fare_amount` Nullable(Float64),                                                                                     ↴│
   │↳    `trip_extra3` Nullable(Float64),                                                                                     ↴│
   │↳    `mta_tax` Nullable(Float64),                                                                                         ↴│
   │↳    `tip_amount` Nullable(Float64),                                                                                      ↴│
   │↳    `tolls_amount` Nullable(Float64),                                                                                    ↴│
   │↳    `improvement_surcharge` Nullable(Float64),                                                                           ↴│
   │↳    `total_amount` Nullable(Float64),                                                                                    ↴│
   │↳    `congestion_surcharge` Nullable(Float64),                                                                            ↴│
   │↳    `Airport_fee` Nullable(Float64),                                                                                     ↴│
   │↳    `cbd_congestion_fee` Nullable(Float64),                                                                              ↴│
   │↳    `VendorName` Nullable(String)                                                                                        ↴│
   │↳)                                                                                                                        ↴│
   │↳ENGINE = Iceberg('http://localhost:9000/bucket1/nyc/taxis_p_by_day/metadata/v2.metadata.json/metadata/v3.metadata.json/') │
   └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

1 row in set. Elapsed: 0.051 sec.
 alter table ice.`nyc.taxis_p_by_day` add column VendorName2 Nullable(String);

ALTER TABLE ice.`nyc.taxis_p_by_day`
    (ADD COLUMN `VendorName2` Nullable(String))

Query id: c2de007c-46a5-4a17-af3f-278c67409590

Ok.

0 rows in set. Elapsed: 3.540 sec. 

Ubuntu-2404-noble-amd64-base :) show create table ice.`nyc.taxis_p_by_day`

SHOW CREATE TABLE ice.`nyc.taxis_p_by_day`

Query id: d1544cf6-6faf-4429-8111-d914875d3a26

   ┌─statement─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
1. │ CREATE TABLE ice.`nyc.taxis_p_by_day`                                                                                    ↴│
   │↳(                                                                                                                        ↴│
   │↳    `VendorID` Nullable(Int32),                                                                                          ↴│
   │↳    `tpep_pickup_datetime` Nullable(DateTime64(6)),                                                                      ↴│
   │↳    `tpep_dropoff_datetime` Nullable(DateTime64(6)),                                                                     ↴│
   │↳    `passenger_count` Nullable(Int64),                                                                                   ↴│
   │↳    `trip_distance` Nullable(Float64),                                                                                   ↴│
   │↳    `RatecodeID` Nullable(Int64),                                                                                        ↴│
   │↳    `store_and_fwd_flag` Nullable(String),                                                                               ↴│
   │↳    `PULocationID` Nullable(Int32),                                                                                      ↴│
   │↳    `DOLocationID` Nullable(Int32),                                                                                      ↴│
   │↳    `payment_type` Nullable(Int64),                                                                                      ↴│
   │↳    `fare_amount` Nullable(Float64),                                                                                     ↴│
   │↳    `trip_extra3` Nullable(Float64),                                                                                     ↴│
   │↳    `mta_tax` Nullable(Float64),                                                                                         ↴│
   │↳    `tip_amount` Nullable(Float64),                                                                                      ↴│
   │↳    `tolls_amount` Nullable(Float64),                                                                                    ↴│
   │↳    `improvement_surcharge` Nullable(Float64),                                                                           ↴│
   │↳    `total_amount` Nullable(Float64),                                                                                    ↴│
   │↳    `congestion_surcharge` Nullable(Float64),                                                                            ↴│
   │↳    `Airport_fee` Nullable(Float64),                                                                                     ↴│
   │↳    `cbd_congestion_fee` Nullable(Float64),                                                                              ↴│
   │↳    `VendorName` Nullable(String),                                                                                       ↴│
   │↳    `VendorName2` Nullable(String)                                                                                       ↴│
   │↳)                                                                                                                        ↴│
   │↳ENGINE = Iceberg('http://localhost:9000/bucket1/nyc/taxis_p_by_day/metadata/v2.metadata.json/metadata/v3.metadata.json/') │
   └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

1 row in set. Elapsed: 0.078 sec. 
Ubuntu-2404-noble-amd64-base :) alter table ice.`nyc.taxis_p_by_day` drop column VendorName;

ALTER TABLE ice.`nyc.taxis_p_by_day`
    (DROP COLUMN VendorName)

Query id: 91b802f5-1dac-4a3d-8232-fc5900a49087

Ok.

0 rows in set. Elapsed: 0.610 sec. 

Ubuntu-2404-noble-amd64-base :) show create table ice.`nyc.taxis_p_by_day`

SHOW CREATE TABLE ice.`nyc.taxis_p_by_day`

Query id: ba5a67b1-4da3-464a-bb8d-47c3d53a1f96

   ┌─statement─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
1. │ CREATE TABLE ice.`nyc.taxis_p_by_day`                                                                                    ↴│
   │↳(                                                                                                                        ↴│
   │↳    `VendorID` Nullable(Int32),                                                                                          ↴│
   │↳    `tpep_pickup_datetime` Nullable(DateTime64(6)),                                                                      ↴│
   │↳    `tpep_dropoff_datetime` Nullable(DateTime64(6)),                                                                     ↴│
   │↳    `passenger_count` Nullable(Int64),                                                                                   ↴│
   │↳    `trip_distance` Nullable(Float64),                                                                                   ↴│
   │↳    `RatecodeID` Nullable(Int64),                                                                                        ↴│
   │↳    `store_and_fwd_flag` Nullable(String),                                                                               ↴│
   │↳    `PULocationID` Nullable(Int32),                                                                                      ↴│
   │↳    `DOLocationID` Nullable(Int32),                                                                                      ↴│
   │↳    `payment_type` Nullable(Int64),                                                                                      ↴│
   │↳    `fare_amount` Nullable(Float64),                                                                                     ↴│
   │↳    `trip_extra3` Nullable(Float64),                                                                                     ↴│
   │↳    `mta_tax` Nullable(Float64),                                                                                         ↴│
   │↳    `tip_amount` Nullable(Float64),                                                                                      ↴│
   │↳    `tolls_amount` Nullable(Float64),                                                                                    ↴│
   │↳    `improvement_surcharge` Nullable(Float64),                                                                           ↴│
   │↳    `total_amount` Nullable(Float64),                                                                                    ↴│
   │↳    `congestion_surcharge` Nullable(Float64),                                                                            ↴│
   │↳    `Airport_fee` Nullable(Float64),                                                                                     ↴│
   │↳    `cbd_congestion_fee` Nullable(Float64),                                                                              ↴│
   │↳    `VendorName2` Nullable(String)                                                                                       ↴│
   │↳)                                                                                                                        ↴│
   │↳ENGINE = Iceberg('http://localhost:9000/bucket1/nyc/taxis_p_by_day/metadata/v2.metadata.json/metadata/v3.metadata.json/') │
   └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

1 row in set. Elapsed: 0.045 sec. 
Ubuntu-2404-noble-amd64-base :) alter table ice.`nyc.taxis_p_by_day` modify column PULocationID Nullable(Int64);

ALTER TABLE ice.`nyc.taxis_p_by_day`
    (MODIFY COLUMN `PULocationID` Nullable(Int64))

Query id: f64beedc-6c62-4e9b-8d82-d642028ffcd1

Ok.

0 rows in set. Elapsed: 0.455 sec. 

Ubuntu-2404-noble-amd64-base :) show create table ice.`nyc.taxis_p_by_day`

SHOW CREATE TABLE ice.`nyc.taxis_p_by_day`

Query id: 2ca8f3e4-286a-4b83-9e2b-cb52fa7b086c

   ┌─statement─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
1. │ CREATE TABLE ice.`nyc.taxis_p_by_day`                                                                                    ↴│
   │↳(                                                                                                                        ↴│
   │↳    `VendorID` Nullable(Int32),                                                                                          ↴│
   │↳    `tpep_pickup_datetime` Nullable(DateTime64(6)),                                                                      ↴│
   │↳    `tpep_dropoff_datetime` Nullable(DateTime64(6)),                                                                     ↴│
   │↳    `passenger_count` Nullable(Int64),                                                                                   ↴│
   │↳    `trip_distance` Nullable(Float64),                                                                                   ↴│
   │↳    `RatecodeID` Nullable(Int64),                                                                                        ↴│
   │↳    `store_and_fwd_flag` Nullable(String),                                                                               ↴│
   │↳    `PULocationID` Nullable(Int64),                                                                                      ↴│
   │↳    `DOLocationID` Nullable(Int32),                                                                                      ↴│
   │↳    `payment_type` Nullable(Int64),                                                                                      ↴│
   │↳    `fare_amount` Nullable(Float64),                                                                                     ↴│
   │↳    `trip_extra3` Nullable(Float64),                                                                                     ↴│
   │↳    `mta_tax` Nullable(Float64),                                                                                         ↴│
   │↳    `tip_amount` Nullable(Float64),                                                                                      ↴│
   │↳    `tolls_amount` Nullable(Float64),                                                                                    ↴│
   │↳    `improvement_surcharge` Nullable(Float64),                                                                           ↴│
   │↳    `total_amount` Nullable(Float64),                                                                                    ↴│
   │↳    `congestion_surcharge` Nullable(Float64),                                                                            ↴│
   │↳    `Airport_fee` Nullable(Float64),                                                                                     ↴│
   │↳    `cbd_congestion_fee` Nullable(Float64),                                                                              ↴│
   │↳    `VendorName2` Nullable(String)                                                                                       ↴│
   │↳)                                                                                                                        ↴│
   │↳ENGINE = Iceberg('http://localhost:9000/bucket1/nyc/taxis_p_by_day/metadata/v2.metadata.json/metadata/v3.metadata.json/') │
   └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

1 row in set. Elapsed: 0.024 sec. 

@subkanthi subkanthi marked this pull request as ready for review April 2, 2026 21:50
@subkanthi
Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@arthurpassos
Copy link
Copy Markdown
Collaborator

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f344d72b29

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Databases/DataLake/RestCatalog.cpp Outdated
Comment on lines +215 to +217
Poco::JSON::Array::Ptr identifier_fields = new Poco::JSON::Array;
schema_for_rest->set("identifier-field-ids", identifier_fields);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve identifier-field-ids when sending schema updates

Overwriting identifier-field-ids with an empty array discards any existing row identity configuration for tables created outside ClickHouse. In buildUpdateMetadataRequestBody, schema_for_rest is cloned from the new schema but then unconditionally reset, so an ALTER against a REST-catalog Iceberg table with non-empty identifier fields will silently clear them in the committed schema update. This can break downstream equality-delete/merge workflows that depend on those IDs.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed, added logic to update identifier-field-ids only if its already set.

Comment on lines +756 to +760
if (!catalog->updateMetadata(namespace_name, table_name, catalog_filename, metadata))
{
LOG_WARNING(log, "Iceberg alter: catalog update failed for '{}'", catalog_filename);
continue;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not retry by reapplying ALTER after failed catalog commit

After successfully writing the new metadata file, a catalog commit failure currently does continue, which re-enters the loop and applies the same alter operation again on top of already-updated metadata. For ADD COLUMN, this path can produce duplicate field entries (the add generator does not guard against existing names), and for other operations it can cause spurious failures because the first write already changed the schema version. This failure mode is triggered whenever updateMetadata returns false after a successful metadata write (e.g., transient REST error).

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed. If the catalog updateMetadata fails throw an exception.

…, fixed retry logic in Mutations.cpp for catalog updateMetadata
@subkanthi subkanthi requested a review from arthurpassos April 14, 2026 14:27

namespace
{
Poco::JSON::Object::Ptr cloneJsonObject(const Poco::JSON::Object::Ptr & obj)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow, is this really the best way to clone it? I havent checked th rest of the code yet, but I am curious to understand why this is needed

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I understand why you need it. I also see poco does not offer a deep copy APi, so this is ok

return result;
}

Poco::JSON::Object::Ptr request_body = new Poco::JSON::Object;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder how they (poco lib) manage the life cycle of such objects

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From poco docs Poco::JSON::Object::Ptr is a type alias for a Poco::SharedPtr managing a Poco::JSON::Object .
Its a reference counting smart pointer

if (!schema_for_rest->has("identifier-field-ids"))
schema_for_rest->set("identifier-field-ids", new Poco::JSON::Array);

if (old_schema_id >= 0)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this means: is this not the first schema?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I honestly dont know he specs, so I cant really give an opinion on the below fields

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes correct, the logic is const Int32 old_schema_id = new_schema_id - 1; if a current schema exists, then add it to the assert-current-schema-id in the request

{
  "identifier": {
    "namespace": ["sales", "events"],
    "name": "orders"
  },
  "requirements": [
    { "type": "assert-table-uuid", "uuid": "<table-uuid-from-loadTable>" },
    { "type": "assert-current-schema-id", "current-schema-id": 3 }
  ],
  "updates": [
    {
      "action": "rename-column",
      "name": "old_name",
      "new-name": "new_name"
    }
  ]
}

https://github.com/Altinity/ClickHouse/pull/1594/changes/BASE..4f37d22fc562d75bdad4f41d414125a966520964#diff-a70dee27f63a76254ba9d7e7f9dc632d5cdee3d19be91663d04686247582e6faR167

// "operation" : "replace",
// "summary" : "replace snapshot 1 with snapshot 2"
// }
if (new_snapshot->has("parent-snapshot-id"))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I dont know the specs, but to my understand it is impossible that old_schema_id < 0 while parent_snapshot_id != -1. Please educate me

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image The else part https://github.com/Altinity/ClickHouse/pull/1594/changes/BASE..4f37d22fc562d75bdad4f41d414125a966520964#diff-a70dee27f63a76254ba9d7e7f9dc632d5cdee3d19be91663d04686247582e6faL1308 is from pre-existing code and is not related to updating schemas, is used for insert/append snapshot. I just moved the code to a separate function as its both related to updating the REST API request.

Comment thread src/Databases/DataLake/RestCatalog.cpp Outdated
request_body->set("updates", updates);
}
const auto built = buildUpdateMetadataRequestBody(namespace_name, table_name, new_snapshot);
if (built.status == UpdateMetadataRequestBodyResult::Status::Skip)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to follow this "err as return value" pattern? Usually we just throw exceptions with clear messages and those are propagated back to the user or logged somewhere

metadata_object->getArray(Iceberg::f_schemas)->add(current_schema);
}

void MetadataGenerator::generateRenameColumnMetadata(const String & column_name, const String & new_column_name)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesnt look like it generates or outputs anything, I would personally name it differently like set or update

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bugfix of the original upstream PR, kept the same name generateRenameColumnMetadata

void generateAddColumnMetadata(const String & column_name, DataTypePtr type);
    void generateDropColumnMetadata(const String & column_name);
    void generateModifyColumnMetadata(const String & column_name, DataTypePtr type);
    void generateRenameColumnMetadata(const String & column_name, const String & new_column_name);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the original upstream PR https://github.com/ClickHouse/ClickHouse/pull/97455/changes#diff-58d3a6dbeceacc359645fc8bcac193781a4c82ecccfb67a9362db280df353154R339, which implemented these functions, its probably better to keep the function names the same.

throw Exception(ErrorCodes::BAD_ARGUMENTS, "Iceberg alter supports exactly one command at a time, got {}", params.size());

size_t i = 0;
while (i++ < MAX_TRANSACTION_RETRIES)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would honestly avoid such unrelated changes, unless we ship it to upstream and then backport

Copy link
Copy Markdown
Collaborator Author

@subkanthi subkanthi Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a bug, because i is post-incremented and here it will start with 1.

if (i == MAX_TRANSACTION_RETRIES)
        throw Exception(ErrorCodes::LIMIT_EXCEEDED, "Too many unsuccessed retries to alter iceberg table");

return;
}

bool file_exists = object_storage->exists(StoredObject(storage_metadata_name));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont understand why we need two branches, one in case we wrote the hint and one in case we did not

@arthurpassos
Copy link
Copy Markdown
Collaborator

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4f37d22fc5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +779 to +782
if (catalog->updateMetadata(namespace_name, table_name, catalog_filename, metadata))
{
LOG_INFO(log, "Iceberg alter: adopted existing metadata file '{}' and updated catalog", storage_metadata_name);
return;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not treat foreign metadata file as successful ALTER

When writeMetadataFileAndVersionHint fails because the target file already exists, this branch returns success as soon as catalog->updateMetadata(...) succeeds. For Glue catalogs, GlueCatalog::updateMetadata (see src/Databases/DataLake/GlueCatalog.cpp) only repoints metadata_location and does not validate that the existing file contains this ALTER’s schema changes, so a concurrent writer’s file can be "adopted" and the user gets a successful ALTER that was never applied.

Useful? React with 👍 / 👎.

Comment on lines +757 to +761
if (!catalog->updateMetadata(namespace_name, table_name, catalog_filename, metadata))
{
throw Exception(
ErrorCodes::DATALAKE_DATABASE_ERROR,
"Iceberg alter: catalog commit failed for '{}' after metadata file was written successfully",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Retry catalog commit failures inside ALTER retry loop

A failed catalog->updateMetadata(...) immediately throws and exits the transaction loop, so MAX_TRANSACTION_RETRIES is effectively skipped for catalog conflicts/transient failures after metadata write. In practice, concurrent schema updates (e.g. REST commit conflict responses) will fail on first conflict instead of retrying from fresh metadata, unlike the mutation path’s retry behavior.

Useful? React with 👍 / 👎.

subkanthi added a commit that referenced this pull request Apr 22, 2026
…ter post-write branches

Addresses review feedback on PR #1594:

R1480: replace err-as-return-value pattern in
buildUpdateMetadataRequestBody with standard exception/null semantics.
- Return nullptr for the "no snapshot, nothing to commit" skip case.
- Throw DB::Exception(DATALAKE_DATABASE_ERROR) with a specific message
  for malformed metadata (missing current-schema-id, no schema object
  matching current-schema-id).
- RestCatalog::updateMetadata keeps its bool return for ICatalog
  compatibility but now logs the full HTTPException displayText on
  commit failure instead of silently swallowing it.
- Drop UpdateMetadataRequestBodyResult struct; callers consume the
  Poco::JSON::Object::Ptr directly.
- Update gtest_rest_catalog_update_metadata: use EXPECT_FALSE(body) for
  skip, EXPECT_THROW(..., DB::Exception) for malformed inputs,
  ASSERT_TRUE(body) for success, and assert on the requirements/updates
  payload shape.

R768: collapse the two-branch post-write logic in Mutations.cpp alter
retry loop into a single flow driven by wrote_ok and file_exists:
- wrote_ok=true -> commit to catalog, fatal if catalog rejects.
- wrote_ok=false && file_exists -> orphan-adopt: attempt catalog
  commit; on failure continue to next retry (do not throw).
- wrote_ok=false && !file_exists -> retry with a new version.
Adds a comment explaining why orphan-adopt is preferred over writing a
new version.

Made-with: Cursor
@subkanthi subkanthi force-pushed the rename_columns_iceberg branch from a773167 to 64783dc Compare April 22, 2026 22:07
… code.

Signed-off-by: Kanthi Subramanian <subkanthi@gmail.com>
@subkanthi subkanthi force-pushed the rename_columns_iceberg branch from 64783dc to c29588a Compare April 22, 2026 22:10
@subkanthi subkanthi requested a review from arthurpassos April 23, 2026 03:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants