Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/storage/ducklake_transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1232,11 +1232,13 @@ void DuckLakeTransaction::CheckForConflicts(const TransactionChangeInformation &
}
for (auto &table_id : changes.tables_inserted_into) {
ConflictCheck(table_id, other_changes.dropped_tables, "insert into table", "dropped it");
ConflictCheck(table_id, other_changes.altered_tables, "insert into table", "altered it");
// NOTE: insert-vs-alter is safe because each data file carries its own mapping_id
// that describes the column layout at write time. The multi-file reader uses
// this mapping to correctly read files written under older schemas, even after
// ALTER TABLE ADD/DROP/RENAME COLUMN operations.
}
for (auto &table_id : changes.tables_inserted_inlined) {
ConflictCheck(table_id, other_changes.dropped_tables, "insert into table", "dropped it");
ConflictCheck(table_id, other_changes.altered_tables, "insert into table", "altered it");
}
for (auto &table_id : changes.tables_deleted_from) {
ConflictCheck(table_id, other_changes.dropped_tables, "delete from table", "dropped it");
Expand Down
103 changes: 103 additions & 0 deletions test/sql/transaction/transaction_insert_alter_no_conflict.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# name: test/sql/transaction/transaction_insert_alter_no_conflict.test
# description: Test that concurrent INSERT and ALTER TABLE do not conflict
# group: [transaction]

require ducklake

require parquet

test-env DUCKLAKE_CONNECTION __TEST_DIR__/{UUID}.db

test-env DATA_PATH __TEST_DIR__

statement ok
ATTACH 'ducklake:${DUCKLAKE_CONNECTION}' AS ducklake (DATA_PATH '${DATA_PATH}/ducklake_insert_alter_no_conflict_files')

statement ok
SET immediate_transaction_mode=true

statement ok
CREATE TABLE ducklake.test(i INTEGER, j INTEGER);

statement ok
INSERT INTO ducklake.test VALUES (1, 10);

# one transaction inserts while another alters: should NOT conflict
statement ok con1
BEGIN

statement ok con2
BEGIN

statement ok con1
INSERT INTO ducklake.test VALUES (2, 20);

statement ok con2
ALTER TABLE ducklake.test ADD COLUMN k INTEGER

statement ok con1
COMMIT

statement ok con2
COMMIT

# verify both changes applied: 2 rows, 3 columns, new column is NULL for existing rows
query III
SELECT i, j, k FROM ducklake.test ORDER BY i
----
1 10 NULL
2 20 NULL

# one transaction alters while another inserts (reversed order): should NOT conflict
statement ok con1
BEGIN

statement ok con2
BEGIN

statement ok con1
ALTER TABLE ducklake.test ADD COLUMN m INTEGER

statement ok con2
INSERT INTO ducklake.test VALUES (3, 30, 300);

statement ok con1
COMMIT

statement ok con2
COMMIT

# verify: 3 rows, 4 columns
query IIII
SELECT i, j, k, m FROM ducklake.test ORDER BY i
----
1 10 NULL NULL
2 20 NULL NULL
3 30 300 NULL

# concurrent insert and rename column: should NOT conflict
statement ok con1
BEGIN

statement ok con2
BEGIN

statement ok con1
INSERT INTO ducklake.test VALUES (4, 40, 400, 4000);

statement ok con2
ALTER TABLE ducklake.test RENAME COLUMN m TO n

statement ok con1
COMMIT

statement ok con2
COMMIT

query IIII
SELECT i, j, k, n FROM ducklake.test ORDER BY i
----
1 10 NULL NULL
2 20 NULL NULL
3 30 300 NULL
4 40 400 4000
Loading