should be it

This commit is contained in:
2025-10-24 19:21:19 -05:00
parent a4b23fc57c
commit f09560c7b1
14047 changed files with 3161551 additions and 1 deletions

View File

@@ -0,0 +1,40 @@
# name: test/sql/upsert/insert_or_replace.test_slow
# group: [upsert]
statement ok
pragma enable_verification;
statement ok
create table tbl (a integer, b integer unique, c integer default 10);
statement ok
insert into tbl(a,b) values (1,2), (2,1);
statement ok
insert or replace into tbl(a,b,c) values (5,2,20), (10,1,30);
query III
select a,b,c from tbl;
----
5 2 20
10 1 30
# This should not overwrite the existing value of 'c', as it is explicitly not inserted into
statement ok
insert or replace into tbl (a,b) values (5,2);
query I
select c from tbl;
----
20
30
# This should also work when the table is aliased
statement ok
insert or replace into tbl AS not_tbl (a,b) values (5,2);
query I
select c from tbl;
----
20
30

View File

@@ -0,0 +1,25 @@
# name: test/sql/upsert/insert_or_replace/pk_and_non_unique_index.test
# group: [insert_or_replace]
# Single primary key on multiple columns (with non-unique INDEX)
statement ok
create table tbl(
a int,
b int,
c int,
primary key(a,b,c)
);
statement ok
create index non_unique on tbl(b);
statement ok
insert or replace into tbl values (1,2,3);
statement ok
insert or replace into tbl values (1,2,3);
query III
select * from tbl;
----
1 2 3

View File

@@ -0,0 +1,22 @@
# name: test/sql/upsert/insert_or_replace/primary_key.test
# group: [insert_or_replace]
# Single primary key on multiple columns
statement ok
create table tbl(
a int,
b int,
c int,
primary key(a,b,c)
);
statement ok
insert or replace into tbl values (1,2,3);
statement ok
insert or replace into tbl values (1,2,3);
query III
select * from tbl;
----
1 2 3

View File

@@ -0,0 +1,24 @@
# name: test/sql/upsert/insert_or_replace/returning.test
# group: [insert_or_replace]
statement ok
create table foo(
bar text primary key,
baz bigint,
);
query I
insert or replace into foo(bar, baz) values ('baz', 1) returning bar;
----
baz
query I
insert or replace into foo(bar, baz) values ('baz', 2) returning bar;
----
baz
query I
insert or replace into foo(bar, baz) values ('baz', 1), ('bar', 2) returning bar;
----
baz
bar

View File

@@ -0,0 +1,31 @@
# name: test/sql/upsert/insert_or_replace/returning_nothing.test
# group: [insert_or_replace]
statement ok
CREATE SEQUENCE seq START 1;
statement ok
CREATE TABLE bug (
id INTEGER PRIMARY KEY DEFAULT NEXTVAL('seq'),
name VARCHAR
);
statement ok
CREATE UNIQUE INDEX idx ON bug (name);
query I
INSERT OR IGNORE INTO bug VALUES
(DEFAULT, 'toto') RETURNING(id);
----
1
query I
INSERT OR IGNORE INTO bug VALUES
(DEFAULT, 'toto') RETURNING(id);
----
query I
INSERT OR IGNORE INTO bug VALUES
(DEFAULT, 'toto'), (DEFAULT, 'yoyo') RETURNING(id);
----
4

View File

@@ -0,0 +1,27 @@
# name: test/sql/upsert/insert_or_replace/unique_and_non_unique_index.test
# group: [insert_or_replace]
# Single UNIQUE INDEX + non-unique INDEX
statement ok
create table tbl(
a int,
b int,
c int
);
statement ok
create UNIQUE index "unique" on tbl(a, b);
statement ok
create index non_unique on tbl(a, b);
statement ok
insert or replace into tbl values (1,2,3);
statement ok
insert or replace into tbl values (1,2,3);
query III
select * from tbl;
----
1 2 3

View File

@@ -0,0 +1,33 @@
# name: test/sql/upsert/insert_or_replace/unique_constraint.test
# group: [insert_or_replace]
# Single UNIQUE CONSTRAINT
statement ok
create table tbl(
a int unique,
b int
);
statement ok
insert or replace into tbl values(1, 2);
statement ok
insert or replace into tbl values(1, 2);
query II
select * from tbl;
----
1 2
# Multiple UNIQUE CONSTRAINTs
statement ok
create table multiple_unique(
a int unique,
b int unique,
c int
);
statement error
insert or replace into multiple_unique values(1, 2, 3);
----
Binder Error: Conflict target has to be provided for a DO UPDATE operation when the table has multiple UNIQUE/PRIMARY KEY constraints

View File

@@ -0,0 +1,36 @@
# name: test/sql/upsert/insert_or_replace/unique_constraint_and_non_unique_index.test
# group: [insert_or_replace]
# Single UNIQUE CONSTRAINT
statement ok
create table tbl(
a int unique,
b int
);
statement ok
create index non_unique on tbl(a, b);
statement ok
insert or replace into tbl values(1, 2);
statement ok
insert or replace into tbl values(1, 2);
query II
select * from tbl;
----
1 2
# Multiple UNIQUE CONSTRAINTs
statement ok
create table multiple_unique(
a int unique,
b int unique,
c int
);
statement error
insert or replace into multiple_unique values(1, 2, 3);
----
Binder Error: Conflict target has to be provided for a DO UPDATE operation when the table has multiple UNIQUE/PRIMARY KEY constraints

View File

@@ -0,0 +1,35 @@
# name: test/sql/upsert/insert_or_replace/unique_index.test
# description: Test INSERT OR REPLACE with a unique index.
# group: [insert_or_replace]
statement ok
PRAGMA enable_verification
# Single UNIQUE INDEX
statement ok
CREATE TABLE tbl(a INT, b INT, c INT);
statement ok
CREATE UNIQUE INDEX "unique" ON tbl(a, b);
statement ok
INSERT OR REPLACE INTO tbl VALUES (1, 2, 3);
statement ok
INSERT OR REPLACE INTO tbl VALUES (1, 2, 3);
query III
SELECT * FROM tbl;
----
1 2 3
# Multiple UNIQUE indexes.
statement ok
CREATE UNIQUE INDEX idx3 ON tbl(b, c);
statement error
INSERT OR REPLACE INTO tbl VALUES (1, 2, 3);
----
<REGEX>:Binder Error.*Conflict target has to be provided for a DO UPDATE operation when the table has multiple UNIQUE/PRIMARY KEY constraints.*

View File

@@ -0,0 +1,34 @@
# name: test/sql/upsert/minimal_reproducable_example.test
# group: [upsert]
statement ok
pragma enable_verification;
# Create a table with 3 columns, 2 of which are indexed on
statement ok
create or replace table tbl(
i integer PRIMARY KEY,
j integer UNIQUE,
k integer
);
# Add 3 rows
statement ok
insert into tbl VALUES (1, 10, 1), (2, 20, 1), (3, 30, 2);
query III
select * from tbl;
----
1 10 1
2 20 1
3 30 2
statement ok
insert into tbl VALUES (3,5,1) ON CONFLICT (i) DO UPDATE SET k = k + excluded.k;
query III
select * from tbl;
----
1 10 1
2 20 1
3 30 3

View File

@@ -0,0 +1,13 @@
# name: test/sql/upsert/no_complex_default.test
# group: [upsert]
statement ok
pragma enable_verification;
statement ok
create or replace table tbl (a integer default 3);
statement error
insert into tbl VALUES ((3+DEFAULT));
----
Binder Error: DEFAULT is not allowed here!

View File

@@ -0,0 +1,109 @@
# name: test/sql/upsert/postgres/composite_key.test
# group: [postgres]
statement ok
pragma enable_verification;
# insert...on conflict do unique index inference
statement ok
create table insertconflicttest(
key int4,
fruit text,
other int4,
unique (key, fruit)
);
# fails
statement error
insert into insertconflicttest values(0, 'Crowberry', 0) on conflict (key) do nothing;
----
statement error
insert into insertconflicttest values(0, 'Crowberry', 0) on conflict (fruit) do nothing;
----
# succeeds
statement ok
insert into insertconflicttest values(0, 'Crowberry', 0) on conflict (key, fruit) do nothing;
statement ok
insert into insertconflicttest values(0, 'Crowberry', 0) on conflict (fruit, key, fruit, key) do nothing;
## -- We explicitly don't support a subquery in the WHERE clause currently -- ##
statement error
insert into insertconflicttest
values (0, 'Crowberry', 0) on conflict (key, fruit) do update set other = 1 where exists (select 1 from insertconflicttest ii where ii.key = excluded.key);
----
DO UPDATE SET clause cannot contain a subquery
# inference succeeds:
statement ok
insert into insertconflicttest values (7, 'Raspberry', 0) on conflict (key, fruit) do update set other = 1
statement ok
insert into insertconflicttest values (8, 'Lime', 0) on conflict (fruit, key) do update set other = 1
# inference fails:
statement error
insert into insertconflicttest values (9, 'Banana', 0) on conflict (key) do update set other = 1
----
statement error
insert into insertconflicttest values (10, 'Blueberry', 0) on conflict (key, key, key) do update set other = 1
----
## -- This fails on postgres for some reason? -- ##
statement ok
insert into insertconflicttest values (11, 'Cherry', 0) on conflict (key, fruit) do update set other = 1
## -- This fails on postgres for some reason? -- ##
statement ok
insert into insertconflicttest values (12, 'Date', 0) on conflict (fruit, key) do update set other = 1
# Partial index tests, no inference predicate specified
statement error
create unique index part_comp_key_index on insertconflicttest(key, fruit) where key < 5;
----
Creating partial indexes is not supported currently
statement error
create unique index expr_part_comp_key_index on insertconflicttest(key, fruit) where key < 5;
----
Creating partial indexes is not supported currently
# Expression index tests
statement ok
create unique index expr_key_index on insertconflicttest(fruit);
# inference succeeds:
statement ok
insert into insertconflicttest values (20, 'Quince', 0) on conflict (fruit) do update set other = 1
statement ok
insert into insertconflicttest values (21, 'Pomegranate', 0) on conflict (fruit, fruit) do update set other = 1
# Expression index tests (with regular column)
statement ok
create unique index expr_comp_key_index on insertconflicttest(key, fruit);
statement ok
create unique index tricky_expr_comp_key_index on insertconflicttest(key, fruit, fruit);
# inference succeeds:
statement ok
insert into insertconflicttest values (24, 'Plum', 0) on conflict (key, fruit) do update set other = 1
statement ok
insert into insertconflicttest values (25, 'Peach', 0) on conflict (fruit, key) do update set other = 1
# Should not infer "tricky_expr_comp_key_index" index:
statement ok
insert into insertconflicttest values (26, 'Fig', 0) on conflict (fruit, key, fruit, key) do update set other = 1

View File

@@ -0,0 +1,56 @@
# name: test/sql/upsert/postgres/non_spurious_duplicate_violation.test
# group: [postgres]
statement ok
pragma enable_verification;
# insert...on conflict do unique index inference
statement ok
create table insertconflicttest(
key int4,
fruit text,
other int4,
unique (key),
unique (fruit)
);
statement ok
insert into insertconflicttest values (25, 'Fig', 0) on conflict (fruit) do update set other = 1
# Succeeds
statement ok
insert into insertconflicttest values (23, 'Blackberry', 0) on conflict (key) do update set other = 1 where fruit like '%berry'
# Test that wholerow references to ON CONFLICT's EXCLUDED work
# Succeeds, updates existing row:
statement ok
insert into insertconflicttest as i values (23, 'Jackfruit', 0) on conflict (key) do update set other = 1
# No update this time, though:
statement ok
insert into insertconflicttest as i values (23, 'Jackfruit', 0) on conflict (key) do update set other = 1
# Predicate changed to require match rather than non-match, so updates once more:
statement ok
insert into insertconflicttest as i values (23, 'Jackfruit', 0) on conflict (key) do update set other = 1
# Assign:
statement ok
insert into insertconflicttest as i values (23, 'Avocado', 0) on conflict (key) do update set other = 1
# deparse whole row var in WHERE and SET clauses:
statement ok
insert into insertconflicttest as i values (23, 'Avocado', 0) on conflict (key) do update set other = 1
statement ok
insert into insertconflicttest as i values (23, 'Avocado', 0) on conflict (key) do update set other = 1
# Cleanup
statement ok
drop table insertconflicttest;

View File

@@ -0,0 +1,207 @@
# name: test/sql/upsert/postgres/planner_preprocessing.test
# group: [postgres]
statement ok
pragma enable_verification;
# ******************************************************************
# * *
# * Test inheritance (example taken from tutorial) *
# * *
# ******************************************************************
statement ok
create table cities (
name text,
population float8,
altitude int,
unique (name)
);
statement ok
create table capitals (
name text,
population float8,
altitude int,
state char(2),
unique (name)
);
# Create unique indexes. Due to a general limitation of inheritance,
# uniqueness is only enforced per-relation. Unique index inference
# specification will do the right thing, though.
# prepopulate the tables.
statement ok
insert into cities values ('San Francisco', 7.24E+5, 63);
statement ok
insert into cities values ('Las Vegas', 2.583E+5, 2174);
statement ok
insert into cities values ('Mariposa', 1200, 1953);
statement ok
insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA');
statement ok
insert into capitals values ('Madison', 1.913E+5, 845, 'WI');
# Tests proper for inheritance:
query IIII
select * from capitals;
----
Sacramento 369400.0 30 CA
Madison 191300.0 845 WI
# Succeeds:
statement ok
insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict do nothing;
statement ok
insert into capitals values ('Sacramento', 4664.E+5, 30, 'CA') on conflict (name) do update set population = excluded.population;
# Wrong "Sacramento", so do nothing:
statement ok
insert into capitals values ('Sacramento', 50, 2267, 'NE') on conflict (name) do nothing;
query IIII
select * from capitals;
----
Sacramento 466400000.0 30 CA
Madison 191300.0 845 WI
statement ok
insert into cities values ('Las Vegas', 5.83E+5, 2001) on conflict (name) do update set population = excluded.population, altitude = excluded.altitude;
query IIII
select rowid, * from cities;
----
0 San Francisco 724000.0 63
1 Las Vegas 583000.0 2001
2 Mariposa 1200.0 1953
statement ok
insert into capitals values ('Las Vegas', 5.83E+5, 2222, 'NV') on conflict (name) do update set population = excluded.population;
# Capitals will contain new capital, Las Vegas:
query IIII
select * from capitals;
----
Sacramento 466400000.0 30 CA
Madison 191300.0 845 WI
Las Vegas 583000.0 2222 NV
# Cities contains two instances of "Las Vegas", since unique constraints don't
# work across inheritance:
query IIII
select rowid, * from cities;
----
0 San Francisco 724000.0 63
1 Las Vegas 583000.0 2001
2 Mariposa 1200.0 1953
# This only affects "cities" version of "Las Vegas":
statement ok
insert into cities values ('Las Vegas', 5.86E+5, 2223) on conflict (name) do update set population = excluded.population, altitude = excluded.altitude;
select rowid, * from cities;
# clean up
statement ok
drop table capitals;
statement ok
drop table cities;
# Make sure a table named excluded is handled properly
statement ok
create table excluded(key int primary key, data text);
statement ok
insert into excluded values(1, '1');
# error, ambiguous
statement error
insert into excluded values(1, '2') on conflict (key) do update set data = excluded.data RETURNING *;
----
# ok, aliased
statement ok
insert into excluded AS target values(1, '2') on conflict (key) do update set data = excluded.data RETURNING *;
# ok, aliased
statement ok
insert into excluded AS target values(1, '2') on conflict (key) do update set data = target.data RETURNING *;
## -- We don't support excluded in RETURNING, also, this is ambiguous??? -- ##
# make sure excluded isn't a problem in returning clause
statement error
insert into excluded values(1, '2') on conflict (key) do update set data = 3 RETURNING excluded.*;
----
Ambiguous reference to table "excluded"
# clean up
statement ok
drop table excluded;
# check that references to columns after dropped columns are handled correctly
statement ok
create table dropcol(key int primary key, drop1 int, keep1 text, drop2 numeric, keep2 float);
statement ok
insert into dropcol(key, drop1, keep1, drop2, keep2) values(1, 1, '1', '1', 1);
# set using excluded
statement ok
insert into dropcol(key, drop1, keep1, drop2, keep2) values(1, 2, '2', '2', 2) on conflict(key)
do update set drop1 = excluded.drop1, keep1 = excluded.keep1, drop2 = excluded.drop2, keep2 = excluded.keep2
where excluded.drop1 is not null and excluded.keep1 is not null and excluded.drop2 is not null and excluded.keep2 is not null
and dropcol.drop1 is not null and dropcol.keep1 is not null and dropcol.drop2 is not null and dropcol.keep2 is not null
returning *;
;
# set using existing table
statement ok
insert into dropcol(key, drop1, keep1, drop2, keep2) values(1, 3, '3', '3', 3) on conflict(key)
do update set drop1 = dropcol.drop1, keep1 = dropcol.keep1, drop2 = dropcol.drop2, keep2 = dropcol.keep2
returning *;
;
statement ok
alter table dropcol
drop column drop1;
statement ok
alter table dropcol
drop column drop2;
# set using excluded
statement ok
insert into dropcol(key, keep1, keep2) values(1, '4', 4) on conflict(key)
do update set keep1 = excluded.keep1, keep2 = excluded.keep2
where excluded.keep1 is not null and excluded.keep2 is not null
and dropcol.keep1 is not null and dropcol.keep2 is not null
returning *;
;
# set using existing table
statement ok
insert into dropcol(key, keep1, keep2) values(1, '5', 5) on conflict(key)
do update set keep1 = dropcol.keep1, keep2 = dropcol.keep2
returning *;
;
statement ok
DROP TABLE dropcol;

View File

@@ -0,0 +1,111 @@
# name: test/sql/upsert/postgres/single_key.test
# group: [postgres]
statement ok
pragma enable_verification;
# insert...on conflict do unique index inference
statement ok
create table insertconflicttest(
key int4,
fruit text,
unique (key)
);
# Explain tests
statement ok
insert into insertconflicttest values (0, 'Bilberry') on conflict (key) do update set fruit = excluded.fruit;
# Should display qual actually attributable to internal sequential scan:
statement ok
insert into insertconflicttest values (0, 'Bilberry') on conflict (key) do update set fruit = excluded.fruit where insertconflicttest.fruit != 'Cawesh';
# With EXCLUDED.* expression in scan node:
statement ok
insert into insertconflicttest values(0, 'Crowberry') on conflict (key) do update set fruit = excluded.fruit where excluded.fruit != 'Elderberry';
# Does the same, but JSON format shows "Conflict Arbiter Index" as JSON array:
statement ok
insert into insertconflicttest values (0, 'Bilberry') on conflict (key) do update set fruit = excluded.fruit where insertconflicttest.fruit != 'Lime' returning *;
# Fails (no unique index inference specification, required for do update variant):
## -- We accept this because there is only 1 Index on the table -- ##
statement ok
insert into insertconflicttest values (1, 'Apple') on conflict do update set fruit = excluded.fruit;
# inference succeeds:
statement ok
insert into insertconflicttest values (1, 'Apple') on conflict (key) do update set fruit = excluded.fruit;
statement ok
insert into insertconflicttest values (2, 'Orange') on conflict (key, key, key) do update set fruit = excluded.fruit;
# Succeed, since multi-assignment does not involve subquery:
statement ok
INSERT INTO insertconflicttest VALUES (1, 'Apple'), (2, 'Orange')
ON CONFLICT (key) DO UPDATE SET fruit = excluded.fruit, key = excluded.key;
# Give good diagnostic message when EXCLUDED.* spuriously referenced from
# RETURNING:
## -- We don't support 'excluded' qualified columns in the RETURNING clause yet -- ##
statement error
insert into insertconflicttest values (1, 'Apple') on conflict (key) do update set fruit = excluded.fruit RETURNING excluded.fruit;
----
<REGEX>:.*Not implemented Error.*not supported in the RETURNING clause yet.*
# Only suggest <table>.* column when inference element misspelled:
statement error
insert into insertconflicttest values (1, 'Apple') on conflict (keyy) do update set fruit = excluded.fruit;
----
<REGEX>:.*Binder Error.*Table "insertconflicttest" does not have a column.*
# Have useful HINT for EXCLUDED.* RTE within UPDATE:
statement error
insert into insertconflicttest values (1, 'Apple') on conflict (key) do update set fruit = excluded.fruitt;
----
<REGEX>:.*Binder Error.*does not have a column named "fruitt".*
# inference fails:
statement error
insert into insertconflicttest values (3, 'Kiwi') on conflict (key, fruit) do update set fruit = excluded.fruit;
----
<REGEX>:.*Binder Error.*not referenced by a UNIQUE/PRIMARY KEY CONSTRAINT or INDEX.*
statement error
insert into insertconflicttest values (4, 'Mango') on conflict (fruit, key) do update set fruit = excluded.fruit;
----
<REGEX>:.*Binder Error.*not referenced by a UNIQUE/PRIMARY KEY CONSTRAINT or INDEX.*
statement error
insert into insertconflicttest values (5, 'Lemon') on conflict (fruit) do update set fruit = excluded.fruit;
----
<REGEX>:.*Binder Error.*not referenced by a UNIQUE/PRIMARY KEY CONSTRAINT or INDEX.*
statement error
insert into insertconflicttest values (6, 'Passionfruit') on conflict (fruit) do update set fruit = excluded.fruit;
----
<REGEX>:.*Binder Error.*not referenced by a UNIQUE/PRIMARY KEY CONSTRAINT or INDEX.*
# Check the target relation can be aliased
statement ok
insert into insertconflicttest AS ict values (6, 'Passionfruit') on conflict (key) do update set fruit = excluded.fruit;
# ok, no reference to target table
statement ok
insert into insertconflicttest AS ict values (6, 'Passionfruit') on conflict (key) do update set fruit = ict.fruit;
# ok, alias
# error, references aliased away name
statement error
insert into insertconflicttest AS ict values (6, 'Passionfruit') on conflict (key) do update set fruit = insertconflicttest.fruit;
----
<REGEX>:.*Binder Error.*Referenced table.*not found.*

View File

@@ -0,0 +1,103 @@
# name: test/sql/upsert/test_big_insert.test
# description: Test insert into statements
# group: [upsert]
statement ok
PRAGMA enable_verification;
statement ok
SET preserve_insertion_order = false;
statement ok
CREATE TABLE integers(
i INT UNIQUE,
j INT DEFAULT 0,
k INT DEFAULT 0
);
statement ok
INSERT INTO integers(i) SELECT i FROM range(5000) tbl(i);
query I
SELECT COUNT(*) FROM integers
----
5000
# All tuples hit a conflict - DO NOTHING.
statement ok
INSERT INTO integers SELECT * FROM integers ON CONFLICT DO NOTHING;
# All tuples hit a conflict - DO UPDATE.
statement ok
INSERT INTO integers SELECT * FROM integers ON CONFLICT DO UPDATE SET j = 10;
# All 'j' entries are changed to 10.
query I
SELECT COUNT(*) FILTER (WHERE j = 10) FROM integers
----
5000
statement ok
INSERT INTO integers(i, j) SELECT i % 5, i FROM range(4995, 5000) tbl(i) ON CONFLICT DO UPDATE SET j = excluded.j, k = excluded.i;
query I
SELECT j FROM integers LIMIT 5;
----
4995
4996
4997
4998
4999
# This is the worst conflicting rowid pattern we could have.
# Every odd-indexed insert tuple conflicts with a row at the start of the existing tuples.
# And every even-indexed insert tuple conflicts with a row at the end of the existing tuples.
statement ok
INSERT INTO integers(i, j)
SELECT CASE WHEN i % 2 = 0
THEN 4999 - (i // 2)
ELSE i - ((i // 2) + 1)
END, i
FROM range(5000) tbl(i)
ON CONFLICT DO UPDATE SET j = excluded.j;
# This shows that the odd-indexed insert tuples conflicted with the first rows.
query I
SELECT j FROM integers LIMIT 5;
----
1
3
5
7
9
# This shows that the even-indexed insert tuples conflicted with the last rows.
query I
SELECT j FROM integers LIMIT 5 OFFSET 4995;
----
8
6
4
2
0
# Reset j.
statement ok
UPDATE integers SET j = 0;
# Only set j if both the existing tuple and the insert tuple are even.
statement ok
INSERT INTO integers(i, j)
SELECT CASE WHEN i % 2 = 0
THEN 4999 - (i // 2)
ELSE i - ((i // 2) + 1)
END, i
FROM range(5000) tbl(i)
ON CONFLICT DO UPDATE SET j = excluded.j
WHERE i % 2 = 0 AND excluded.j % 2 = 0;
# The DO UPDATE WHERE clause is only true for a quarter of the cases.
query I
SELECT COUNT(j) FILTER (WHERE j != 0) FROM integers;
----
1250

View File

@@ -0,0 +1,28 @@
# name: test/sql/upsert/test_big_insert_no_vector_verification.test
# description: Test ON CONFLICT statement on the same conflicting row.
# group: [upsert]
require vector_size 2048
statement ok
PRAGMA enable_verification;
statement ok
SET preserve_insertion_order = false;
statement ok
CREATE TABLE integers(
i INT UNIQUE,
j INT DEFAULT 0,
k INT DEFAULT 0
);
statement ok
INSERT INTO integers(i) SELECT i FROM range(5000) tbl(i);
statement ok
INSERT INTO integers(i, j)
SELECT i % 5, i
FROM range(5000) tbl(i) ON CONFLICT DO UPDATE SET
j = excluded.j,
k = excluded.i;

View File

@@ -0,0 +1,48 @@
# name: test/sql/upsert/test_generated_column.test
# group: [upsert]
# SET expression targets b (located after the virtual column)
statement ok
CREATE TABLE t1 (
a CHAR NOT NULL,
c CHAR GENERATED ALWAYS AS (a) VIRTUAL,
b INT,
);
statement ok
CREATE UNIQUE INDEX t1_idx ON t1 (a);
statement ok
INSERT INTO t1 VALUES ('a', 1) ON CONFLICT(a) DO UPDATE SET b = excluded.b;
statement ok
INSERT INTO t1 VALUES ('a', 1) ON CONFLICT(a) DO UPDATE SET b = excluded.b;
query III
SELECT * FROM t1;
----
a a 1
# The ON CONFLICT (a) is logically located after the virtual column
statement ok
CREATE TABLE t2 (
b INT,
c CHAR GENERATED ALWAYS AS (a) VIRTUAL,
a CHAR NOT NULL,
);
statement ok
CREATE UNIQUE INDEX t2_idx ON t2 (a);
statement ok
INSERT INTO t2 VALUES (1, 'a') ON CONFLICT(a) DO UPDATE SET b = excluded.b;
statement ok
INSERT INTO t2 VALUES (1, 'a') ON CONFLICT(a) DO UPDATE SET b = excluded.b;
query III
SELECT * FROM t1;
----
a a 1

View File

@@ -0,0 +1,40 @@
# name: test/sql/upsert/test_problematic_conditional_do_update.test
# group: [upsert]
statement ok
CREATE TABLE users (
id BIGINT PRIMARY KEY,
username TEXT UNIQUE,
email TEXT
);
# FIXME: not consistent
mode skip
# The condition skips the last tuple
statement error
INSERT INTO users (id, username, email)
VALUES
(3, 'inner_conflict', 'test'),
(3, 'inner_conflict2', 'other_test'),
(3, 'inner_conflict3', 'filtered_out')
ON CONFLICT (id) DO
UPDATE SET email = EXCLUDED.email
WHERE EXCLUDED.email != 'filtered_out'
----
Not implemented Error: Inner conflicts detected with a conditional DO UPDATE on-conflict action, not fully implemented yet
# The result of the condition can also be influenced based on previous updates
statement error
INSERT INTO users (id, username, email)
VALUES
(3, 'inner_conflict', 'test'),
(3, 'inner_conflict2', 'other_test'),
(3, 'inner_conflict3', 'yet_another_test'),
(3, 'inner_conflict4', 'dont_skip_me')
ON CONFLICT (id) DO
UPDATE SET email = EXCLUDED.email
WHERE email != 'other_test' OR EXCLUDED.email == 'dont_skip_me'
RETURNING *;
----
Not implemented Error: Inner conflicts detected with a conditional DO UPDATE on-conflict action, not fully implemented yet

View File

@@ -0,0 +1,90 @@
# name: test/sql/upsert/upsert_aliased.test
# group: [upsert]
statement ok
pragma enable_verification;
statement ok
create table tbl (
i integer,
j integer unique
);
statement ok
insert into tbl values (5,3), (6,7);
# This works when we don't reference it anywhere
statement ok
insert into tbl AS test values (3,5), (8,3), (2,6) on conflict (j) do update set i = excluded.i * 2 where i <= excluded.i;
query II
select * from tbl order by all
----
2 6
3 5
6 7
16 3
# We can use the alias in the expressions
statement ok
insert into tbl AS test values (2,3) on conflict do update set i = test.i;
# Essentially a no-op
query II
select * from tbl order by all
----
2 6
3 5
6 7
16 3
# We can use the 'test' alias in the conflict target condition
query I
insert into tbl as test values (2,3) on conflict (j) do update set i = excluded.j where test.i < 5;
----
0
statement ok
insert into tbl as test values (2,3) on conflict (j) do update set i = excluded.j where test.i >= 5;
query II rowsort
select * from tbl
----
2 6
3 3
3 5
6 7
# We can also use the 'test' alias in the DO UPDATE condition
statement ok
insert into tbl as test (j, i) values (5,3) on conflict (j) do update set i = 10 where test.j <= 3;
# No-op, do update condition was not met
query II rowsort
select * from tbl;
----
2 6
3 3
3 5
6 7
statement ok
insert into tbl as test (j, i) values (5,3) on conflict (j) do update set i = 10 where test.j > 3;
# Now 'i' is changed to 10 in the conflicting tuple
query II
select * from tbl order by all
----
2 6
3 3
6 7
10 5
# When we alias to 'excluded' we create an ambiguity error
statement error
insert into tbl as excluded values (8,3) on conflict (j) do update set i = 5;
----
Ambiguous reference to table "excluded"

View File

@@ -0,0 +1,218 @@
# name: test/sql/upsert/upsert_basic.test
# group: [upsert]
statement ok
PRAGMA enable_verification;
statement ok
CREATE TABLE tbl(
i INT PRIMARY KEY,
j INT UNIQUE,
k INT
);
statement ok
INSERT INTO tbl VALUES (1, 10, 1), (2, 20, 1), (3, 30, 2);
# Update the on-conflict column.
statement ok
INSERT INTO tbl VALUES (3, 5, 1)
ON CONFLICT (i) DO UPDATE SET i = i + 1;
query III
SELECT i, j, k FROM tbl ORDER BY ALL;
----
1 10 1
2 20 1
4 30 2
query III
SELECT i, j, k FROM tbl WHERE i = 4;
----
4 30 2
# Update the on-conflict column again.
statement ok
INSERT INTO tbl VALUES (4, 30, 2)
ON CONFLICT (i) DO UPDATE SET i = i - 1;
query III
SELECT i, j, k FROM tbl ORDER BY ALL;
----
1 10 1
2 20 1
3 30 2
# Cannot update to the same PK value as another column.
statement error
INSERT INTO tbl VALUES (3, 30, 2)
ON CONFLICT (i) DO UPDATE SET i = i - 2;
----
<REGEX>:Constraint Error.*violates primary key constraint.*
# 'excluded' refers to the VALUES list, turning this into:
# (k)2 + (k.excluded)1 = 3
statement ok
insert into tbl VALUES
(3,5,1)
ON CONFLICT (i) DO UPDATE SET k = k + excluded.k;
query III
select * from tbl;
----
1 10 1
2 20 1
3 30 3
# The ON CONFLICT does not refer to a column that's indexed on, so it's never true
statement error
insert into tbl VALUES
(3,5,1)
ON CONFLICT (k) DO UPDATE SET k = excluded.k;
----
Binder Error: The specified columns as conflict target are not referenced by a UNIQUE/PRIMARY KEY CONSTRAINT
# Overwrite the existing value with the new value
statement ok
insert into tbl VALUES
(3,5,1)
ON CONFLICT (i) DO UPDATE SET k = excluded.k;
query III
select * from tbl;
----
1 10 1
2 20 1
3 30 1
# Don't alter the existing row, but still insert the non-conflicting row
statement ok
insert into tbl VALUES
(4,2,3),
(3,5,10)
ON CONFLICT (i) DO NOTHING;
query III
select * from tbl;
----
1 10 1
2 20 1
3 30 1
4 2 3
# Two rows cause a conflict, on the same existing row
# only the last one is used
statement ok
insert into tbl VALUES
(3,3,10),
(3,3,10)
ON CONFLICT (i) DO UPDATE SET
k = excluded.k;
query III
select * from tbl order by all
----
1 10 1
2 20 1
3 30 10
4 2 3
# condition is not matched - no updates have happened
query I
insert into tbl VALUES (3,5,1) ON CONFLICT (i) DO UPDATE SET k = 1 WHERE k < 5;
----
0
query III
select * from tbl;
----
1 10 1
2 20 1
3 30 10
4 2 3
# When the condition is met, the DO is performed
query I
insert into tbl VALUES (3,5,1) ON CONFLICT (i) DO UPDATE SET k = 1 WHERE k >= 5;
----
1
# 'k' in row_id:3 is updated to 1
query III
select * from tbl;
----
1 10 1
2 20 1
3 30 1
4 2 3
# When the condition is on the DO UPDATE part,
# it will always succeed, but turn into a DO NOTHING for the conflicts that don't meet the condition
statement ok
insert into tbl VALUES (3,5,3) on conflict (i) do update set k = 10 WHERE k != 1;
# Unchanged, because the where clause is not met
query III
select * from tbl;
----
1 10 1
2 20 1
3 30 1
4 2 3
statement ok
insert into tbl VALUES (3,5,3) on conflict (i) do update set k = 10 WHERE k == 1;
# Changed, because the where clause is met
query III
select * from tbl;
----
1 10 1
2 20 1
3 30 10
4 2 3
# When we don't specify a conflict target, all unique/primary key constraints are used as the conflict target
statement ok
insert into tbl VALUES (5,1,0), (3,5,20) ON CONFLICT DO NOTHING;
query III
select * from tbl;
----
1 10 1
2 20 1
3 30 10
4 2 3
5 1 0
# Not supported because:
# > It's because one insert row could violate multiple different unique constraints,
# with it being a different row for each of the constraints that's causing the violation,
# and the upsert is only intended to update a single row.
# https://sqlite.org/forum/info/45cf84d3e89d590d
statement error
insert into tbl VALUES (5,1,0), (3,5,20) ON CONFLICT DO UPDATE set k = excluded.k;
----
Binder Error: Conflict target has to be provided for a DO UPDATE operation when the table has multiple UNIQUE/PRIMARY KEY constraints
statement ok
create or replace table single_constraint (
i integer PRIMARY KEY,
j integer,
k varchar,
);
statement ok
insert into single_constraint values (5,1,'hello'), (1,10,'test');
# This is however accepted if only a single constraint exists on the table
statement ok
insert into single_constraint values (1,5,'bye'), (3,10,'quack') on conflict do update set j = excluded.j, k = concat(k, excluded.k);
query III
select * from single_constraint
----
5 1 hello
1 5 testbye
3 10 quack

View File

@@ -0,0 +1,40 @@
# name: test/sql/upsert/upsert_conflict_target.test
# group: [upsert]
statement ok
PRAGMA enable_verification;
statement ok
CREATE OR REPLACE TABLE tbl (a INT, b INT, c INT, PRIMARY KEY (a, b));
# a and b combined are not unique.
statement error
INSERT INTO tbl VALUES (1, 2, 3), (1, 2, 3);
----
Constraint Error: PRIMARY KEY or UNIQUE constraint violation: duplicate key
statement ok
INSERT INTO tbl VALUES (1, 2, 3), (1, 4, 5);
# Conflict target does not match any index on the table, only valid conflict target would be: (a,b)
statement error
INSERT INTO tbl VALUES (1,4,7), (1,8,4) ON CONFLICT (a) DO UPDATE SET c = 5;
----
Binder Error: The specified columns as conflict target are not referenced by a UNIQUE/PRIMARY KEY CONSTRAINT
# Conflict target does not match any index on the table, only valid conflict target would be: (a,b)
statement error
INSERT INTO tbl VALUES (1,4,7), (1,8,4) ON CONFLICT (b) DO UPDATE SET c = 5;
----
Binder Error: The specified columns as conflict target are not referenced by a UNIQUE/PRIMARY KEY CONSTRAINT
# Conflict target matches the index on the table, conflict is resolved by the ON CONFLICT clause
statement ok
INSERT INTO tbl VALUES (1,4,7), (1,8,4) ON CONFLICT (a,b) DO UPDATE SET c = 5;
query III
SELECT a, b, c FROM tbl ORDER BY ALL;
----
1 2 3
1 4 5
1 8 4

View File

@@ -0,0 +1,33 @@
# name: test/sql/upsert/upsert_conflict_target_index.test
# group: [upsert]
statement ok
pragma enable_verification;
statement ok
create or replace table index_tbl (
i integer,
j integer
);
statement ok
insert into index_tbl values (5, 3);
statement ok
create unique index other_index on index_tbl(i);
statement error
insert into index_tbl values (5, 5);
----
query II
select * from index_tbl;
----
5 3
# TODO: Indexes are only checked as part of committing a transaction, not in the Insert, so we currently can't support the
# ON CONFLICT ON CONSTRAINT <constraint_name> syntax
statement error
insert into index_tbl values (5, 10) on conflict on constraint other_index do update set j = excluded.j;
----
Not implemented Error: ON CONSTRAINT conflict target is not supported yet

View File

@@ -0,0 +1,36 @@
# name: test/sql/upsert/upsert_coverage.test
# group: [upsert]
statement ok
PRAGMA enable_verification;
statement ok
CREATE TABLE tbl (a INT, b INT UNIQUE, c INT UNIQUE, d INT UNIQUE);
statement ok
INSERT INTO tbl (b, c, d) VALUES (1, 2, 3), (2, 3, 1), (3, 1, 2)
# Create conflicts in three existing tuples with one INSERT tuple.
# Each conflict has a different row ID.
# We never have to deal with one-to-many conflicts in anything other than
# a DO NOTHING action. That is because we need a conflict target
# to use any sort of WHERE or SET expression that references the existing table.
statement ok
INSERT INTO tbl(b, c, d) VALUES (3, 3, 3) ON CONFLICT DO NOTHING;
query III
SELECT b, c, d FROM tbl ORDER BY ALL;
----
1 2 3
2 3 1
3 1 2
# We fail without a conflict target.
statement error
INSERT INTO tbl(b, c, d) VALUES (3, 3, 3)
ON CONFLICT DO UPDATE SET b = EXCLUDED.b, c = EXCLUDED.c, d = EXCLUDED.d;
----
<REGEX>:Binder Error.*Conflict target has to be provided for a DO UPDATE operation when the table has multiple UNIQUE/PRIMARY KEY constraints.*

View File

@@ -0,0 +1,47 @@
# name: test/sql/upsert/upsert_default.test
# group: [upsert]
statement ok
pragma enable_verification;
statement ok
create table tbl (
a integer DEFAULT 5,
b integer unique,
c integer DEFAULT 10
);
statement ok
insert into tbl(b) VALUES (3), (5), (6);
statement ok
insert into tbl(b) VALUES (7), (3), (4) ON CONFLICT do update set c = 5, a = 10;
query III rowsort
select * from tbl;
----
10 3 5
5 4 10
5 5 10
5 6 10
5 7 10
statement ok
create table t (i int primary key, j int);
query I
insert into t values (1, 1) on conflict do nothing;
----
1
# 0 updates/insertions were performed
query I
insert into t values (1, 1) on conflict do nothing
----
0
# 0 insertions, but one tuple is updated
query I
insert into t values (1, 1) on conflict (i) do update set j = excluded.i;
----
1

View File

@@ -0,0 +1,42 @@
# name: test/sql/upsert/upsert_default_expressions.test
# group: [upsert]
statement ok
pragma enable_verification;
statement ok
create or replace table tbl (
a integer primary key default 4,
b integer DEFAULT 3
);
statement ok
insert into tbl VALUES (2,3), (4,5)
query II
select * from tbl;
----
2 3
4 5
# DEFAULT in set expression
statement ok
insert into tbl VALUES (DEFAULT, 6) ON CONFLICT (a) DO UPDATE SET b = DEFAULT
query II
select * from tbl;
----
2 3
4 3
# DEFAULT in ON CONFLICT (..) WHERE <expr>
statement error
insert into tbl VALUES (4,8) ON CONFLICT (a) DO UPDATE SET b = excluded.b WHERE a = DEFAULT;
----
WHERE clause cannot contain DEFAULT clause
# DEFAULT in DO UPDATE SET .. WHERE <expr>
statement error
insert into tbl VALUES (4,3) ON CONFLICT (a) DO UPDATE SET b = excluded.b WHERE excluded.b = DEFAULT * 2;
----
WHERE clause cannot contain DEFAULT clause

View File

@@ -0,0 +1,44 @@
# name: test/sql/upsert/upsert_default_value_causes_conflict.test
# group: [upsert]
statement ok
pragma enable_verification;
statement ok
create or replace table tbl (
a integer primary key default 5,
b integer
);
statement ok
insert into tbl(b) VALUES (10);
query II
select * from tbl;
----
5 10
# The default expression is a constant, can't be inserted twice
statement error
insert into tbl(b) VALUES (10);
----
Constraint Error: Duplicate key "a: 5" violates primary key constraint
# We can ignore the error
statement ok
insert into tbl(b) VALUES (10) ON CONFLICT (a) DO NOTHING;
# The tuple is not inserted
query II
select * from tbl;
----
5 10
# We can also change the value of 'b', but not of 'a' because it's indexed on
statement ok
insert into tbl(b) VALUES (10) ON CONFLICT (a) DO UPDATE SET b = excluded.b * 2;
query II
select * from tbl;
----
5 20

View File

@@ -0,0 +1,20 @@
# name: test/sql/upsert/upsert_default_values.test
# group: [upsert]
statement ok
pragma enable_verification;
statement ok
create or replace table tbl (
a integer primary key DEFAULT 5,
b integer
);
statement ok
insert into tbl DEFAULT VALUES;
query II
FROM tbl
----
5 NULL

View File

@@ -0,0 +1,51 @@
# name: test/sql/upsert/upsert_distinct_bug.test
# group: [upsert]
# Create raw data table
statement ok
CREATE TABLE test_table_raw(id VARCHAR, name VARCHAR);
# Insert raw data
statement ok
INSERT INTO test_table_raw VALUES
('abc001','foo'),
('abc002','bar'),
('abc001','foo2'),
('abc002','bar2');
# Create aggregated data table
statement ok
CREATE TABLE test_table(id VARCHAR PRIMARY KEY, name VARCHAR);
# Insert aggregated data
statement error
INSERT INTO test_table
SELECT
DISTINCT(id) as id,
name
FROM test_table_raw;
----
# Insert aggregated data
statement error
INSERT INTO test_table
SELECT
DISTINCT(id) as id,
name
FROM test_table_raw;
----
# Insert aggregated data second time with "INSERT OR IGNORE" => Segmentation fault
# This contains conflicts between the to-be-inserted rows, still won't succeed
# FIXME - this sometimes works - it depends on how the aggregate returns rows
statement maybe
INSERT OR IGNORE INTO test_table
SELECT
DISTINCT(id) as id,
name
FROM test_table_raw;
----
statement ok
SELECT * FROM test_table_raw;

View File

@@ -0,0 +1,17 @@
# name: test/sql/upsert/upsert_duplicates_issue.test
# group: [upsert]
statement ok
pragma enable_verification;
require parquet
statement ok
CREATE TABLE tmp_edges(from_v VARCHAR, to_v VARCHAR, PRIMARY KEY(from_v, to_v));
statement ok
INSERT INTO tmp_edges
from 'data/parquet-testing/upsert_bug.parquet'
ON CONFLICT DO UPDATE SET
from_v = excluded.from_v
;

View File

@@ -0,0 +1,74 @@
# name: test/sql/upsert/upsert_excluded_references.test
# group: [upsert]
statement ok
pragma enable_verification;
# Within the ON CONFLICT clause, we can have special column references that are qualified with 'excluded'
# These refer to the to-be-inserted tuples that caused the conflict, instead of the existing tuples that were found by the conflict.
statement ok
create or replace table tbl (
a integer primary key,
b integer
);
statement ok
insert into tbl VALUES (1,2), (2,2);
query II
select * from tbl;
----
1 2
2 2
# Conflict on '1' in 'a'
statement error
insert into tbl VALUES (1,3), (3,4);
----
Constraint Error: Duplicate key "a: 1" violates primary key constraint
# Usage of 'excluded' in a SET expression
statement ok
insert into tbl VALUES (1,3), (3,4) ON CONFLICT (a) DO UPDATE SET b = excluded.b;
query II
select * from tbl;
----
1 3
2 2
3 4
# Usage of 'excluded' in a ON CONFLICT (..) WHERE <expr>
statement error
insert into tbl VALUES (4,3), (3,8) ON CONFLICT (a) WHERE excluded.b < 8 DO NOTHING;
----
ON CONFLICT WHERE clause is only supported in DO UPDATE SET
statement ok
insert into tbl VALUES (4,3), (3,8) ON CONFLICT (a) DO UPDATE SET b = 10;
query II
select * from tbl;
----
1 3
2 2
3 10
4 3
# Usage of 'excluded' in a DO UPDATE SET ... WHERE <expr>
# Both '2' and '3' are conflicts, but only '2' meets the condition
# When the condition is not met, this turns into a DO NOTHING instead for that tuple
statement ok
insert into tbl VALUES (3,8), (2,2), (7,2) ON CONFLICT (a) DO UPDATE SET b = b*2 WHERE b == excluded.b;
# Only '2' is updated
query II
select * from tbl;
----
1 3
2 4
3 10
4 3
7 2

View File

@@ -0,0 +1,50 @@
# name: test/sql/upsert/upsert_explicit_index.test
# description: Test index UPSERTs.
# group: [upsert]
statement ok
pragma enable_verification;
statement ok
CREATE TABLE tbl (i INT, j INT);
statement ok
INSERT INTO tbl VALUES (5, 3), (3, 2);
statement ok
CREATE UNIQUE INDEX my_index ON tbl(i);
statement ok
INSERT INTO tbl VALUES (5, 2) ON CONFLICT (i) DO UPDATE SET j = 10;
query II
SELECT i, j FROM tbl ORDER BY ALL DESC;
----
5 10
3 2
statement ok
DROP TABLE tbl CASCADE;
statement ok
CREATE TABLE tbl (i INT PRIMARY KEY, j INT);
statement ok
INSERT INTO tbl VALUES (42, 21), (21, 42);
statement ok
CREATE INDEX my_index ON tbl(j);
statement ok
INSERT INTO tbl VALUES (42, 20) ON CONFLICT DO UPDATE SET j = 30;
query III
SELECT i, j, rowid FROM tbl WHERE j = 30;
----
42 30 2
query III
SELECT i, j, rowid FROM tbl ORDER BY ALL;
----
21 42 1
42 30 2

View File

@@ -0,0 +1,57 @@
# name: test/sql/upsert/upsert_global_mixed_conflicts.test
# group: [upsert]
statement ok
create table tbl (
number INTEGER PRIMARY KEY,
str VARCHAR
);
statement ok
insert into tbl VALUES
(1, 'd'),
(3, 'nope'),
(2, 'n')
;
## Postgres says:
## ERROR: ON CONFLICT DO UPDATE command cannot affect row a second time
## HINT: Ensure that no rows proposed for insertion within the same command have duplicate constrained values.
# This insert causes both:
# - conflicts only within the to-be-inserted-data
# - conflicts only with the existing data
# - both conflicts with the existing data as well as within the to-be-inserted-data
statement ok
insert into tbl VALUES
(1, 'c'), -- conflict (existing)
(6, 'yep'),
(3, 'abc'), -- conflict (existing)
(4, 'abc'),
(7, 'tes'),
(2, 'b'), -- conflict (existing)
(1, 'c'), -- conflict (existing + inner)
(5, 'abc'),
(7, 'tes'), -- conflict (inner)
(2, 'b') -- conflict (existing + inner)
ON CONFLICT (number) DO UPDATE SET str = excluded.str || 'e';
query II rowsort
select * from tbl;
----
1 ce
2 be
3 abce
4 abc
5 abc
6 yep
7 tes
## sqlite says this is the correct order ??:
## 1 e
## 2 e
## 3 e
## 4 abc
## 5 abc
## 6 yep
## 7 e

View File

@@ -0,0 +1,20 @@
# name: test/sql/upsert/upsert_inner_conflict_do_nothing.test
# group: [upsert]
statement ok
create table tbl(
i int primary key
);
statement ok
insert into tbl values (1);
# The inserted data has a conflict within the to-be-inserted-data (inner conflict)
# As well as a conflict with the existing data
statement ok
insert into tbl select 1 from range(2) on conflict do nothing;
query I
select * from tbl;
----
1

View File

@@ -0,0 +1,91 @@
# name: test/sql/upsert/upsert_lambda.test
# group: [upsert]
statement ok
PRAGMA enable_verification;
statement ok
CREATE OR REPLACE TABLE foo (
pk_col INT PRIMARY KEY,
str VARCHAR,
str_list VARCHAR[],
payload_col INT
);
statement ok
INSERT INTO foo
SELECT 1, 'hello', ['x', 'y', 'z'], 40
ON CONFLICT DO UPDATE SET
str = list_reduce(EXCLUDED.str_list, lambda x, y: x || '||' || y);
query IIII
FROM foo;
----
1 hello [x, y, z] 40
# Use the new (excluded) list as input.
statement ok
INSERT INTO foo
SELECT 1, 'world', ['a', 'b', 'c'], 41
ON CONFLICT DO UPDATE SET
str = list_reduce(EXCLUDED.str_list, lambda x, y: x || '||' || y);
query IIII
FROM foo;
----
1 a||b||c [x, y, z] 40
# Additionally update the payload.
statement ok
INSERT INTO foo
SELECT 1, '', ['1', '2'], 42
ON CONFLICT DO UPDATE SET
str = list_reduce(EXCLUDED.str_list, lambda x, y: x || '||' || y),
payload_col = EXCLUDED.payload_col;
query IIII
FROM foo;
----
1 1||2 [x, y, z] 42
# Use the existing list as input.
statement ok
INSERT INTO foo
SELECT 1, '', ['l', 'm', 'n'], 43
ON CONFLICT DO UPDATE SET
str = list_reduce(str_list, lambda x, y: x || '||' || y);
query IIII
FROM foo;
----
1 x||y||z [x, y, z] 42
# Reference the existing and the new (excluded) str column without qualification.
statement ok
INSERT INTO foo
SELECT 1, 'world', ['s', 't'], 42
ON CONFLICT DO UPDATE SET
str = list_reduce(EXCLUDED.str_list, lambda x, y: x || str || y || EXCLUDED.str);
query IIII
FROM foo;
----
1 sx||y||ztworld [x, y, z] 42
# lambda function in the WHERE clause.
statement ok
INSERT INTO foo
SELECT 1, 'motorcycle', ['brrr', 'brrrrrr'], 1042
ON CONFLICT DO UPDATE SET
str = 'black-bellied whistling duck'
WHERE list_reduce(EXCLUDED.str_list, lambda x, y: x || str || y || EXCLUDED.str) = 'brrrsx||y||ztworldbrrrrrrmotorcycle';
query IIII
FROM foo;
----
1 black-bellied whistling duck [x, y, z] 42

View File

@@ -0,0 +1,58 @@
# name: test/sql/upsert/upsert_local_no_tuples.test
# group: [upsert]
statement ok
pragma enable_verification;
# Table with a constraint
statement ok
create table tbl (
a short primary key,
b short
);
# Populate the table
statement ok
insert into tbl(a, b) values(1, 2);
# Start a transaction, local storage will be created if an INSERT happens
statement ok
begin transaction;
# Create a local storage, appending 0 tuples because of DO NOTHING
statement ok
insert into tbl(a, b) values(1, 2) on conflict do nothing;
# Scan from the table, hitting the local storage
query II
select * from tbl where a = 1;
----
1 2
# Do it again to test proper cleanup of state
statement ok
insert into tbl(a, b) values(1, 2) on conflict do nothing;
# Scan from the table, hitting the local storage
statement ok
select * from tbl where a = 1;
statement ok
commit;
statement ok
begin transaction;
# Now create a LocalStorage with tuples before doing an append with 0 tuples
statement ok
insert into tbl (a, b) values (5,4);
statement ok
insert into tbl(a, b) values(1, 2) on conflict do nothing;
# Scan from the table, hitting the local storage
query II
select * from tbl order by a;
----
1 2
5 4

View File

@@ -0,0 +1,16 @@
# name: test/sql/upsert/upsert_multiple_constraints.test
# description: Test upsert with a mix of primary / unique constraints
# group: [upsert]
statement ok
CREATE TABLE IF NOT EXISTS tbl (id INTEGER PRIMARY KEY, uniq INTEGER, UNIQUE(uniq));
query I
INSERT OR IGNORE INTO tbl VALUES (1, 1), (2, 1);
----
1
query I
SELECT uniq FROM tbl
----
1

View File

@@ -0,0 +1,62 @@
# name: test/sql/upsert/upsert_order_coverage.test
# group: [upsert]
statement ok
pragma enable_verification;
# Create a table, most columns are indexed on
statement ok
create or replace table tbl(
i integer UNIQUE,
j integer,
k integer PRIMARY KEY
);
statement ok
insert into tbl values (3,4,2), (5,3,1);
query III
select * from tbl;
----
3 4 2
5 3 1
# 'k' is violated in row1 and row2, 'i' is violated in row1
# Since our conflict target is on 'k', and the violations of 'i' are a subset of the violations of 'k'
# We accept this, and don't throw an error
statement ok
insert into tbl(k, i) values (2,3), (4,4), (1,8) on conflict (k) do update set j = excluded.j;
query III
select * from tbl;
----
3 NULL 2
5 NULL 1
4 NULL 4
# If this is the other way around, and the violations caused by indexes that are not our conflict target ('i')
# are not a subset of the violations caused by the index that is our conflict target ('k') then this is an error
# 'i' is violated in row1 and row2, 'k' is only violated in row1
statement error
insert into tbl(i,k) values (3,2), (5,5) on conflict (k) do update set j = 10;
----
query III
select * from tbl;
----
3 NULL 2
5 NULL 1
4 NULL 4
# Only when the non-conflict-target violation shares an insert tuple with a conflict-target violation
# Do we not throw on it, so this does cause an error, because the conflict of 'k' is not on the same insert tuple as the conflict of 'k'
statement error
insert into tbl(i,k) values (3,10), (6,2) on conflict(i) do update set j = 10;
----
query III
select * from tbl;
----
3 NULL 2
5 NULL 1
4 NULL 4

View File

@@ -0,0 +1,22 @@
# name: test/sql/upsert/upsert_partial_update.test
# group: [upsert]
statement ok
pragma enable_verification;
statement ok
create or replace table tbl (a integer primary key, b integer);
statement ok
insert into tbl VALUES (1,3), (2,2), (3,10), (4,3);
statement ok
insert into tbl VALUES (3,8), (2,2) ON CONFLICT (a) DO UPDATE SET b = b*2 WHERE b == excluded.b;
query II
select * from tbl;
----
1 3
2 4
3 10
4 3

View File

@@ -0,0 +1,83 @@
# name: test/sql/upsert/upsert_returning.test
# group: [upsert]
require vector_size 2048
statement ok
CREATE TABLE users (
id BIGINT PRIMARY KEY,
username TEXT UNIQUE,
email TEXT
);
query III
INSERT INTO users (id, username, email) VALUES
(1, 'john_doe', 'john@example.com')
ON CONFLICT (username) DO NOTHING
RETURNING *;
----
1 john_doe john@example.com
query III
INSERT INTO users (id, username, email) VALUES
(1, 'john_doe', 'john@example.com')
ON CONFLICT (username) DO NOTHING
RETURNING *;
----
# We create a conflict, with a where clause that filters out this conflict
# Because the where clause filters it out, the DO UPDATE becomes a DO NOTHING for this row instead
# So it does not get added to the returning clause.
query III
INSERT INTO users (id, username, email)
VALUES (1, 'john_doe', 'john_new@example.com')
ON CONFLICT (id) DO
UPDATE SET email = EXCLUDED.email
WHERE EXCLUDED.email != 'john_new@example.com'
RETURNING *;
----
# Verify that the *other* tuple does get added to the returning clause
query III
INSERT INTO users (id, username, email)
VALUES
(1, 'john_doe', 'john_new@example.com'),
(2, 'not_john_doe', 'not_john_new@example.com')
ON CONFLICT (id) DO
UPDATE SET email = EXCLUDED.email
WHERE EXCLUDED.email != 'john_new@example.com'
RETURNING *;
----
2 not_john_doe not_john_new@example.com
# FIXME: not consistent
mode skip
# Here we have conflicts within the inserted data
# Only the last occurrence of this conflict should be present in the returning clause.
query III
INSERT INTO users (id, username, email)
VALUES
(3, 'inner_conflict', 'test'),
(4, 'a', ''),
(5, 'b', ''),
(6, 'c', ''),
(3, 'inner_conflict2', 'other_test'),
(7, 'd', ''),
(8, 'e', ''),
(9, 'f', ''),
(3, 'inner_conflict3', 'yet_another_test')
ON CONFLICT (id) DO
UPDATE SET email = EXCLUDED.email
WHERE EXCLUDED.email != 'test'
RETURNING *;
----
3 inner_conflict test
4 a (empty)
5 b (empty)
6 c (empty)
7 d (empty)
8 e (empty)
9 f (empty)
3 inner_conflict3 yet_another_test

View File

@@ -0,0 +1,56 @@
# name: test/sql/upsert/upsert_set_expressions.test
# group: [upsert]
statement ok
pragma enable_verification;
# Create a table with only 1 non-indexed column
statement ok
create or replace table tbl(
i integer PRIMARY KEY,
j integer UNIQUE,
k integer
);
# Add 3 rows
statement ok
insert into tbl VALUES
(1, 10, 1),
(2, 20, 1),
(3, 30, 2);
# the 'i' column is indexed on and 3 already exists, causes a conflict
# this updates 'k' to 5
statement ok
insert into tbl VALUES (3,5,1) ON CONFLICT (i) DO UPDATE SET k = 5;
query III
select * from tbl;
----
1 10 1
2 20 1
3 30 5
# the 'i' column is indexed on and 3 already exists, causes a conflict
# this updates 'k' to 1 + excluded.k
statement ok
insert into tbl VALUES (3,5,1) ON CONFLICT (i) DO UPDATE SET k = 1 + excluded.k;
query III
select * from tbl;
----
1 10 1
2 20 1
3 30 2
# the 'i' column is indexed on and 3 already exists, causes a conflict
# this updates 'k' to k + excluded.k
statement ok
insert into tbl VALUES (3,5,1) ON CONFLICT (i) DO UPDATE SET k = k + excluded.k;
query III
select * from tbl;
----
1 10 1
2 20 1
3 30 3

View File

@@ -0,0 +1,43 @@
# name: test/sql/upsert/upsert_shorthand.test
# group: [upsert]
statement ok
pragma enable_verification;
statement ok
create table tbl (a integer, b integer unique);
statement ok
insert into tbl values (1,2), (2,1);
statement ok
insert into tbl values (1,2) on conflict do update set a = excluded.a;
# We can not supply both a shorthand and a verbose version of the ON CONFLICT clause
statement error
insert or replace into tbl values (4,3) on conflict do nothing;
----
<REGEX>:.*Parser Error.*You can not provide both OR.*
# INSERT OR IGNORE is shorthand for DO NOTHING
statement ok
insert or ignore into tbl values (1,2), (2,1);
# INSERT OR REPLACE is shorthand for DO UPDATE SET a = excluded.a, SET b = excluded.b;
statement ok
insert or replace into tbl values (5,2), (10,1);
query II
select * from tbl;
----
5 2
10 1
statement ok
create or replace table tbl (a integer unique, b integer unique);
# When there is more than 1 index on the table, OR REPLACE is not supported, just like DO UPDATE
statement error
insert or replace into tbl values (1,2);
----
<REGEX>:.*Binder Error.*Conflict target has to be provided for a DO UPDATE.*

View File

@@ -0,0 +1,134 @@
# name: test/sql/upsert/upsert_transaction.test
# group: [upsert]
# This tests the desired behavior when UPSERT is used on a conflict that only exists within the transaction local storage.
# NOTE: This does not test behavior of conflicts that arise between transactions
# DO UPDATE
statement ok
BEGIN TRANSACTION;
statement ok
CREATE TABLE tbl (
a SHORT PRIMARY KEY,
b SHORT
);
statement ok
INSERT INTO tbl VALUES
(1, 2)
ON CONFLICT (a) DO UPDATE SET b = excluded.b;
# Create a conflict within the transaction
statement ok
INSERT INTO tbl VALUES
(1, 3)
ON CONFLICT (a) DO UPDATE SET b = excluded.b;
query II
select * from tbl;
----
1 3
statement ok
COMMIT;
query II
select * from tbl;
----
1 3
# DO NOTHING
statement ok
BEGIN TRANSACTION;
statement ok
INSERT INTO tbl VALUES
(2, 1),
(3, 1),
(4, 1);
statement ok
INSERT INTO tbl VALUES
(2, 1),
(3, 1),
(4, 1)
ON CONFLICT (a) DO NOTHING;
statement ok
COMMIT;
# Attempt to update the same row twice within the same UPSERT
statement ok
BEGIN TRANSACTION;
statement ok
INSERT INTO tbl VALUES
(5, 0)
statement ok
insert into tbl VALUES
(5, 0),
(5, 1)
ON CONFLICT (a) DO UPDATE SET
b = excluded.b;
statement ok
COMMIT;
# DO UPDATE 'affected_tuples' return value
statement ok
BEGIN TRANSACTION;
statement ok
INSERT INTO tbl VALUES (6,0);
# Both of these inserts turn into updates
# The first affected tuple (5) is an update in the global storage
# The second affected tuple (6) is an update in the local storage
# The third affected tuple is an insert into the local storage
query I
INSERT INTO tbl VALUES (5,0), (6,0), (7,0) ON CONFLICT (a) DO UPDATE set b = excluded.b;
----
3
# The only affected tuple is the insert into the local storage
# (5,0) causes a constraint error in the global storage
# (3,0) causes a constraint error in the local storage
query I
INSERT INTO tbl VALUES (-1, 0), (5,0), (6,0) ON CONFLICT (a) DO NOTHING;
----
1
statement ok
COMMIT;
# DO UPDATE > STANDARD_VECTOR_SIZE
statement ok
BEGIN TRANSACTION;
statement ok
CREATE OR REPLACE TABLE tbl (a SHORT PRIMARY KEY, b SHORT);
statement ok
INSERT INTO tbl (select i, 0 from range(2500) tbl(i));
query I
select max(b) from tbl;
----
0
statement ok
INSERT INTO tbl (select i, i from range(2500) tbl(i)) ON CONFLICT (a) DO UPDATE SET b = excluded.b;
query I
select max(b) from tbl;
----
2499
statement ok
COMMIT;

View File

@@ -0,0 +1,37 @@
# name: test/sql/upsert/upsert_unique_null.test
# group: [upsert]
statement ok
pragma enable_verification;
statement ok
create or replace table tbl (
a integer unique,
b integer
);
statement ok
insert into tbl VALUES (3,2), (1,3);
statement ok
insert into tbl(b) VALUES (5) ON CONFLICT (a) DO UPDATE SET b = 8;
# UNIQUE can be NULL, PRIMARY KEY can't be
query II
select * from tbl;
----
3 2
1 3
NULL 5
statement ok
insert into tbl(b) VALUES (5) ON CONFLICT (a) DO UPDATE SET b = 8;
# And NULL does not take part in the UNIQUE Index, so it does not activate the ON CONFLICT clause
query II
select * from tbl;
----
3 2
1 3
NULL 5
NULL 5