Skip to content

Commit e178a11

Browse files
committed
Add tests for merge/on conflict
1 parent ece1dc0 commit e178a11

File tree

2 files changed

+301
-0
lines changed

2 files changed

+301
-0
lines changed

test/sql/storage/attach_merge.test

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# name: test/sql/storage/attach_merge.test
2+
# description: Test MERGE INTO
3+
# group: [storage]
4+
5+
require postgres_scanner
6+
7+
require-env POSTGRES_TEST_DATABASE_AVAILABLE
8+
9+
statement ok
10+
PRAGMA enable_verification
11+
12+
statement ok
13+
ATTACH 'dbname=postgresscanner' AS s (TYPE POSTGRES);
14+
15+
statement ok
16+
USE s
17+
18+
statement ok
19+
CREATE OR REPLACE TABLE Stock(item_id int, balance int);
20+
21+
statement ok
22+
CREATE OR REPLACE TABLE Buy(item_id int, volume int);
23+
24+
statement ok
25+
INSERT INTO Buy values(10, 1000);
26+
27+
statement ok
28+
INSERT INTO Buy values(30, 300);
29+
30+
# insert using merge into with a CTE
31+
query I
32+
WITH initial_stocks(item_id, balance) AS (VALUES (10, 2200), (20, 1900))
33+
MERGE INTO Stock USING initial_stocks ON FALSE
34+
WHEN MATCHED THEN DO NOTHING
35+
WHEN NOT MATCHED THEN INSERT VALUES (initial_stocks.item_id, initial_stocks.balance)
36+
----
37+
2
38+
39+
# DO NOTHING is the default - this is a nop
40+
query I
41+
WITH initial_stocks(item_id, balance) AS (VALUES (10, 2200), (20, 1900))
42+
MERGE INTO Stock USING initial_stocks USING (item_id)
43+
WHEN NOT MATCHED THEN INSERT VALUES (item_id, initial_stocks.balance)
44+
----
45+
0
46+
47+
query II
48+
FROM Stock ORDER BY item_id
49+
----
50+
10 2200
51+
20 1900
52+
53+
# update and insert
54+
query I
55+
MERGE INTO Stock AS s USING Buy AS b ON s.item_id = b.item_id
56+
WHEN MATCHED THEN UPDATE SET balance = balance + b.volume
57+
WHEN NOT MATCHED THEN INSERT VALUES (b.item_id, b.volume)
58+
----
59+
2
60+
61+
query II
62+
FROM Stock ORDER BY item_id
63+
----
64+
10 3200
65+
20 1900
66+
30 300
67+
68+
# sell - deleting all rows that are fully sold
69+
statement ok
70+
CREATE OR REPLACE TABLE Sale(item_id int, volume int);
71+
72+
statement ok
73+
INSERT INTO Sale VALUES (10, 2200);
74+
75+
statement ok
76+
INSERT INTO Sale VALUES (20, 1900);
77+
78+
query I
79+
MERGE INTO Stock USING Sale ON Stock.item_id = Sale.item_id
80+
WHEN MATCHED AND Sale.volume > balance THEN ERROR
81+
WHEN MATCHED AND Sale.volume = balance THEN DELETE
82+
WHEN MATCHED AND TRUE THEN UPDATE SET balance = balance - Sale.volume
83+
WHEN MATCHED THEN ERROR
84+
WHEN NOT MATCHED THEN ERROR
85+
----
86+
2
87+
88+
query II
89+
FROM Stock ORDER BY item_id
90+
----
91+
10 1000
92+
30 300
93+
94+
# abort - row does not exist
95+
statement error
96+
MERGE INTO Stock USING Sale ON Stock.item_id = Sale.item_id
97+
WHEN MATCHED AND Sale.volume >= balance THEN DELETE
98+
WHEN MATCHED THEN UPDATE SET balance = balance - Sale.volume
99+
WHEN NOT MATCHED THEN ERROR CONCAT('Sale item with item id ', Sale.item_id, ' not found');
100+
----
101+
Sale item with item id 20 not found
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# name: test/sql/storage/attach_on_conflict.test
2+
# group: [storage]
3+
4+
require postgres_scanner
5+
6+
require-env POSTGRES_TEST_DATABASE_AVAILABLE
7+
8+
statement ok
9+
PRAGMA enable_verification
10+
11+
statement ok
12+
ATTACH 'dbname=postgresscanner' AS s (TYPE POSTGRES);
13+
14+
statement ok
15+
USE s
16+
17+
statement ok
18+
PRAGMA enable_verification;
19+
20+
statement ok
21+
CREATE OR REPLACE TABLE tbl(
22+
i INT PRIMARY KEY,
23+
j INT UNIQUE,
24+
k INT
25+
);
26+
27+
statement ok
28+
INSERT INTO tbl VALUES (1, 10, 1), (2, 20, 1), (3, 30, 2);
29+
30+
# Update the on-conflict column.
31+
statement ok
32+
INSERT INTO tbl VALUES (3, 5, 1)
33+
ON CONFLICT (i) DO UPDATE SET i = i + 1;
34+
35+
query III
36+
SELECT i, j, k FROM tbl ORDER BY ALL;
37+
----
38+
1 10 1
39+
2 20 1
40+
4 30 2
41+
42+
query III
43+
SELECT i, j, k FROM tbl WHERE i = 4;
44+
----
45+
4 30 2
46+
47+
# Update the on-conflict column again.
48+
statement ok
49+
INSERT INTO tbl VALUES (4, 30, 2)
50+
ON CONFLICT (i) DO UPDATE SET i = i - 1;
51+
52+
query III
53+
SELECT i, j, k FROM tbl ORDER BY ALL;
54+
----
55+
1 10 1
56+
2 20 1
57+
3 30 2
58+
59+
# Cannot update to the same PK value as another column.
60+
statement error
61+
INSERT INTO tbl VALUES (3, 30, 2)
62+
ON CONFLICT (i) DO UPDATE SET i = i - 2;
63+
----
64+
duplicate key value
65+
66+
# 'excluded' refers to the VALUES list, turning this into:
67+
# (k)2 + (k.excluded)1 = 3
68+
statement ok
69+
insert into tbl VALUES
70+
(3,5,1)
71+
ON CONFLICT (i) DO UPDATE SET k = k + excluded.k;
72+
73+
query III rowsort
74+
select * from tbl;
75+
----
76+
1 10 1
77+
2 20 1
78+
3 30 3
79+
80+
# The ON CONFLICT does not refer to a column that's indexed on, so it's never true
81+
statement error
82+
insert into tbl VALUES
83+
(3,5,1)
84+
ON CONFLICT (k) DO UPDATE SET k = excluded.k;
85+
----
86+
Binder Error: The specified columns as conflict target are not referenced by a UNIQUE/PRIMARY KEY CONSTRAINT
87+
88+
# Overwrite the existing value with the new value
89+
statement ok
90+
insert into tbl VALUES
91+
(3,5,1)
92+
ON CONFLICT (i) DO UPDATE SET k = excluded.k;
93+
94+
query III rowsort
95+
select * from tbl;
96+
----
97+
1 10 1
98+
2 20 1
99+
3 30 1
100+
101+
# Don't alter the existing row, but still insert the non-conflicting row
102+
statement ok
103+
insert into tbl VALUES
104+
(4,2,3),
105+
(3,5,10)
106+
ON CONFLICT (i) DO NOTHING;
107+
108+
query III rowsort
109+
select * from tbl;
110+
----
111+
1 10 1
112+
2 20 1
113+
3 30 1
114+
4 2 3
115+
116+
# Two rows cause a conflict, on the same existing row
117+
# only the last one is used
118+
statement ok
119+
insert into tbl VALUES
120+
(3,3,10),
121+
(3,3,10)
122+
ON CONFLICT (i) DO UPDATE SET
123+
k = excluded.k;
124+
125+
query III rowsort
126+
select * from tbl order by all
127+
----
128+
1 10 1
129+
2 20 1
130+
3 30 10
131+
4 2 3
132+
133+
# condition is not matched - no updates have happened
134+
query I
135+
insert into tbl VALUES (3,5,1) ON CONFLICT (i) DO UPDATE SET k = 1 WHERE k < 5;
136+
----
137+
0
138+
139+
query III rowsort
140+
select * from tbl;
141+
----
142+
1 10 1
143+
2 20 1
144+
3 30 10
145+
4 2 3
146+
147+
# When the condition is met, the DO is performed
148+
query I
149+
insert into tbl VALUES (3,5,1) ON CONFLICT (i) DO UPDATE SET k = 1 WHERE k >= 5;
150+
----
151+
1
152+
153+
# 'k' in row_id:3 is updated to 1
154+
query III rowsort
155+
select * from tbl;
156+
----
157+
1 10 1
158+
2 20 1
159+
3 30 1
160+
4 2 3
161+
162+
# When the condition is on the DO UPDATE part,
163+
# it will always succeed, but turn into a DO NOTHING for the conflicts that don't meet the condition
164+
165+
statement ok
166+
insert into tbl VALUES (3,5,3) on conflict (i) do update set k = 10 WHERE k != 1;
167+
168+
# Unchanged, because the where clause is not met
169+
query III rowsort
170+
select * from tbl;
171+
----
172+
1 10 1
173+
2 20 1
174+
3 30 1
175+
4 2 3
176+
177+
statement ok
178+
insert into tbl VALUES (3,5,3) on conflict (i) do update set k = 10 WHERE k == 1;
179+
180+
# Changed, because the where clause is met
181+
query III rowsort
182+
select * from tbl;
183+
----
184+
1 10 1
185+
2 20 1
186+
3 30 10
187+
4 2 3
188+
189+
# When we don't specify a conflict target, all unique/primary key constraints are used as the conflict target
190+
statement ok
191+
insert into tbl VALUES (5,1,0), (3,5,20) ON CONFLICT DO NOTHING;
192+
193+
query III rowsort
194+
select * from tbl;
195+
----
196+
1 10 1
197+
2 20 1
198+
3 30 10
199+
4 2 3
200+
5 1 0

0 commit comments

Comments
 (0)