From ca5f135273c613b703db4ee303b299bcb79edbf0 Mon Sep 17 00:00:00 2001 From: gentle_hu Date: Sat, 22 Aug 2020 14:01:20 +0800 Subject: [PATCH] UPSERT test case --- .../regress/expected/insert_update_001.out | 3 + .../regress/expected/insert_update_002.out | 3 + .../regress/expected/insert_update_003.out | 3 + .../regress/expected/insert_update_008.out | 75 +-- .../regress/expected/insert_update_009.out | 3 + .../regress/expected/insert_update_010.out | 3 + src/test/regress/expected/rangefuncs.out | 6 +- src/test/regress/expected/upsert_001.out | 515 ++++++++++++++++ src/test/regress/expected/upsert_002.out | 545 +++++++++++++++++ src/test/regress/expected/upsert_003.out | 313 ++++++++++ src/test/regress/expected/upsert_008.out | 466 +++++++++++++++ src/test/regress/expected/upsert_009.out | 548 ++++++++++++++++++ src/test/regress/expected/upsert_010.out | 313 ++++++++++ src/test/regress/expected/upsert_clean.out | 19 + .../regress/expected/upsert_clean_etc.out | 1 + .../regress/expected/upsert_composite.out | 404 +++++++++++++ src/test/regress/expected/upsert_explain.out | 297 ++++++++++ .../expected/upsert_grammer_test_01.out | 188 ++++++ .../expected/upsert_grammer_test_02.out | 188 ++++++ src/test/regress/expected/upsert_prepare.out | 92 +++ .../regress/expected/upsert_prepare_etc.out | 4 + .../regress/expected/upsert_restriction.out | 229 ++++++++ src/test/regress/expected/upsert_tmp_test.out | 173 ++++++ .../regress/expected/upsert_trigger_test.out | 148 +++++ .../regress/expected/upsert_unlog_test.out | 152 +++++ .../regress/output/recovery_2pc_tools.source | 1 + src/test/regress/parallel_schedule | 8 +- src/test/regress/parallel_schedule3 | 8 + src/test/regress/sql/insert_update_001.sql | 4 + src/test/regress/sql/insert_update_002.sql | 4 + src/test/regress/sql/insert_update_003.sql | 4 + src/test/regress/sql/insert_update_008.sql | 76 +-- src/test/regress/sql/insert_update_009.sql | 5 + src/test/regress/sql/insert_update_010.sql | 5 + src/test/regress/sql/rangefuncs.sql | 3 + src/test/regress/sql/upsert_001.sql | 284 +++++++++ src/test/regress/sql/upsert_002.sql | 279 +++++++++ src/test/regress/sql/upsert_003.sql | 144 +++++ src/test/regress/sql/upsert_008.sql | 359 ++++++++++++ src/test/regress/sql/upsert_009.sql | 281 +++++++++ src/test/regress/sql/upsert_010.sql | 144 +++++ src/test/regress/sql/upsert_clean.sql | 3 + src/test/regress/sql/upsert_composite.sql | 146 +++++ src/test/regress/sql/upsert_explain.sql | 63 ++ .../regress/sql/upsert_grammer_test_01.sql | 113 ++++ .../regress/sql/upsert_grammer_test_02.sql | 113 ++++ src/test/regress/sql/upsert_prepare.sql | 76 +++ src/test/regress/sql/upsert_restriction.sql | 170 ++++++ src/test/regress/sql/upsert_tmp_test.sql | 99 ++++ src/test/regress/sql/upsert_trigger_test.sql | 63 ++ src/test/regress/sql/upsert_unlog_test.sql | 89 +++ 51 files changed, 7161 insertions(+), 74 deletions(-) create mode 100644 src/test/regress/expected/upsert_001.out create mode 100644 src/test/regress/expected/upsert_002.out create mode 100644 src/test/regress/expected/upsert_003.out create mode 100644 src/test/regress/expected/upsert_008.out create mode 100644 src/test/regress/expected/upsert_009.out create mode 100644 src/test/regress/expected/upsert_010.out create mode 100644 src/test/regress/expected/upsert_clean.out create mode 100644 src/test/regress/expected/upsert_clean_etc.out create mode 100644 src/test/regress/expected/upsert_composite.out create mode 100644 src/test/regress/expected/upsert_explain.out create mode 100644 src/test/regress/expected/upsert_grammer_test_01.out create mode 100644 src/test/regress/expected/upsert_grammer_test_02.out create mode 100644 src/test/regress/expected/upsert_prepare.out create mode 100644 src/test/regress/expected/upsert_prepare_etc.out create mode 100644 src/test/regress/expected/upsert_restriction.out create mode 100644 src/test/regress/expected/upsert_tmp_test.out create mode 100644 src/test/regress/expected/upsert_trigger_test.out create mode 100644 src/test/regress/expected/upsert_unlog_test.out create mode 100644 src/test/regress/sql/upsert_001.sql create mode 100644 src/test/regress/sql/upsert_002.sql create mode 100644 src/test/regress/sql/upsert_003.sql create mode 100644 src/test/regress/sql/upsert_008.sql create mode 100644 src/test/regress/sql/upsert_009.sql create mode 100644 src/test/regress/sql/upsert_010.sql create mode 100644 src/test/regress/sql/upsert_clean.sql create mode 100644 src/test/regress/sql/upsert_composite.sql create mode 100644 src/test/regress/sql/upsert_explain.sql create mode 100644 src/test/regress/sql/upsert_grammer_test_01.sql create mode 100644 src/test/regress/sql/upsert_grammer_test_02.sql create mode 100644 src/test/regress/sql/upsert_prepare.sql create mode 100644 src/test/regress/sql/upsert_restriction.sql create mode 100644 src/test/regress/sql/upsert_tmp_test.sql create mode 100644 src/test/regress/sql/upsert_trigger_test.sql create mode 100644 src/test/regress/sql/upsert_unlog_test.sql diff --git a/src/test/regress/expected/insert_update_001.out b/src/test/regress/expected/insert_update_001.out index 8c91d3185..21a4e091a 100644 --- a/src/test/regress/expected/insert_update_001.out +++ b/src/test/regress/expected/insert_update_001.out @@ -2,6 +2,9 @@ DROP SCHEMA test_insert_update_001 CASCADE; ERROR: schema "test_insert_update_001" does not exist CREATE SCHEMA test_insert_update_001; SET CURRENT_SCHEMA TO test_insert_update_001; +-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge, +-- real upsert will be tested in specialized case. +SET enable_upsert_to_merge TO ON; -- test description \h INSERT Command: INSERT diff --git a/src/test/regress/expected/insert_update_002.out b/src/test/regress/expected/insert_update_002.out index 004e431f2..dd75b4bdd 100644 --- a/src/test/regress/expected/insert_update_002.out +++ b/src/test/regress/expected/insert_update_002.out @@ -2,6 +2,9 @@ DROP SCHEMA test_insert_update_002 CASCADE; ERROR: schema "test_insert_update_002" does not exist CREATE SCHEMA test_insert_update_002; SET CURRENT_SCHEMA TO test_insert_update_002; +-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge, +-- real upsert will be tested in specialized case. +SET enable_upsert_to_merge TO ON; -- test t1 with no index CREATE TABLE t1 ( col1 INT, diff --git a/src/test/regress/expected/insert_update_003.out b/src/test/regress/expected/insert_update_003.out index 7ecefa6b3..a792add37 100644 --- a/src/test/regress/expected/insert_update_003.out +++ b/src/test/regress/expected/insert_update_003.out @@ -2,6 +2,9 @@ DROP SCHEMA test_insert_update_003 CASCADE; ERROR: schema "test_insert_update_003" does not exist CREATE SCHEMA test_insert_update_003; SET CURRENT_SCHEMA TO test_insert_update_003; +-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge, +-- real upsert will be tested in specialized case. +SET enable_upsert_to_merge TO ON; -- test t4 with one primary key with three columns CREATE TABLE t4 ( col1 INT, diff --git a/src/test/regress/expected/insert_update_008.out b/src/test/regress/expected/insert_update_008.out index bcb736168..3d20af82f 100644 --- a/src/test/regress/expected/insert_update_008.out +++ b/src/test/regress/expected/insert_update_008.out @@ -4,6 +4,9 @@ -- initial CREATE SCHEMA test_insert_update_008; SET current_schema = test_insert_update_008; +-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge, +-- real upsert will be tested in specialized case. +SET enable_upsert_to_merge TO ON; CREATE TABLE products_base ( product_id INTEGER DEFAULT 0, @@ -58,9 +61,9 @@ EXPLAIN (VERBOSE on, COSTS off) INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Merge on test_insert_update_008.products_row @@ -84,9 +87,9 @@ INSERT INTO products_row FROM newproducts_row, products_row WHERE products_row.total + newproducts_row.total < 1000 ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Merge on test_insert_update_008.products_row @@ -113,9 +116,9 @@ INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row WHERE product_id IS NOT NULL AND product_name IS NOT NULL ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Merge on test_insert_update_008.products_row @@ -137,9 +140,9 @@ EXPLAIN (ANALYZE on, COSTS off, TIMING off) INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; QUERY PLAN --------------------------------------------------------------------------- Merge on products_row (actual rows=4 loops=1) @@ -162,9 +165,9 @@ EXPLAIN PERFORMANCE INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; ROLLBACK; \o -- explain analyze @@ -173,9 +176,9 @@ EXPLAIN (ANALYZE on, COSTS off, TIMING off) INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; QUERY PLAN --------------------------------------------------------------------------- Merge on products_row (actual rows=4 loops=1) @@ -198,9 +201,9 @@ EXPLAIN (VERBOSE on, COSTS off) INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Merge on test_insert_update_008.products_row @@ -221,9 +224,9 @@ EXPLAIN (ANALYZE on, COSTS off, TIMING off) INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; QUERY PLAN --------------------------------------------------------------------------- Merge on products_row (actual rows=4 loops=1) @@ -245,9 +248,9 @@ EXPLAIN (ANALYZE on, COSTS off, TIMING off) INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; QUERY PLAN --------------------------------------------------------------------------- Merge on products_row (actual rows=4 loops=1) @@ -270,9 +273,9 @@ EXPLAIN PERFORMANCE INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; ROLLBACK; SET explain_perf_mode = run; BEGIN; @@ -280,9 +283,9 @@ EXPLAIN PERFORMANCE INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; ROLLBACK; SET explain_perf_mode = summary; BEGIN; @@ -290,9 +293,9 @@ EXPLAIN PERFORMANCE INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; ROLLBACK; \o CREATE TABLE item diff --git a/src/test/regress/expected/insert_update_009.out b/src/test/regress/expected/insert_update_009.out index 02bf76df1..d36df3747 100644 --- a/src/test/regress/expected/insert_update_009.out +++ b/src/test/regress/expected/insert_update_009.out @@ -3,6 +3,9 @@ ERROR: schema "test_insert_update_009" does not exist CREATE SCHEMA test_insert_update_009; SET CURRENT_SCHEMA TO test_insert_update_009; SET enable_light_proxy=off; +-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge, +-- real upsert will be tested in specialized case. +SET enable_upsert_to_merge TO ON; -- test t1 with no index CREATE TABLE t1 ( col1 INT, diff --git a/src/test/regress/expected/insert_update_010.out b/src/test/regress/expected/insert_update_010.out index 3a749bcb4..a33893b36 100644 --- a/src/test/regress/expected/insert_update_010.out +++ b/src/test/regress/expected/insert_update_010.out @@ -2,6 +2,9 @@ DROP SCHEMA test_insert_update_010 CASCADE; ERROR: schema "test_insert_update_010" does not exist CREATE SCHEMA test_insert_update_010; SET CURRENT_SCHEMA TO test_insert_update_010; +-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge, +-- real upsert will be tested in specialized case. +SET enable_upsert_to_merge TO ON; -- test t4 with one primary key with three columns CREATE TABLE t4 ( col1 INT, diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 6e251bdd7..e43285c01 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -1,3 +1,6 @@ +-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge, +-- real upsert will be tested in specialized case. +SET enable_upsert_to_merge TO ON; SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%' ORDER BY name; name | setting -----------------------------------+--------- @@ -74,12 +77,13 @@ SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%' ORDER BY name; enable_thread_pool | off enable_tidscan | on enable_upgrade_merge_lock_mode | off + enable_upsert_to_merge | on enable_user_metric_persistent | on enable_valuepartition_pruning | on enable_vector_engine | on enable_wdr_snapshot | off enable_xlog_prune | on -(78 rows) +(79 rows) CREATE TABLE foo2(fooid int, f2 int); INSERT INTO foo2 VALUES(1, 11); diff --git a/src/test/regress/expected/upsert_001.out b/src/test/regress/expected/upsert_001.out new file mode 100644 index 000000000..3b3625c2f --- /dev/null +++ b/src/test/regress/expected/upsert_001.out @@ -0,0 +1,515 @@ +DROP SCHEMA test_upsert_001 CASCADE; +ERROR: schema "test_upsert_001" does not exist +CREATE SCHEMA test_upsert_001; +SET CURRENT_SCHEMA TO test_upsert_001; +-- enable_upsert_to_merge must is off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +-- test description +\h INSERT +Command: INSERT +Description: create new rows in a table +Syntax: +[ WITH [ RECURSIVE ] with_query [, ...] ] +INSERT INTO table_name [ ( column_name [, ...] ) ] + { DEFAULT VALUES | VALUES {( { expression | DEFAULT } [, ...] ) }[, ...] | query } + [ ON DUPLICATE KEY UPDATE { column_name = { expression | DEFAULT } } [, ...] ] + [ RETURNING {* | {output_expression [ [ AS ] output_name ] }[, ...]} ]; + +-- test permission +--- test with no sequence column +CREATE TABLE t00 (col1 INT DEFAULT 1 PRIMARY KEY, col2 INT); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t00_pkey" for table "t00" +CREATE USER upsert_tester PASSWORD '123456@cc'; +GRANT ALL PRIVILEGES ON SCHEMA test_upsert_001 TO upsert_tester; +SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc'; +INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 5; +ERROR: permission denied for relation t00 +RESET SESSION AUTHORIZATION; +---- error: only have INSERT permission +GRANT INSERT ON test_upsert_001.t00 TO upsert_tester; +SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc'; +INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 5; +ERROR: permission denied for relation t00 +RESET SESSION AUTHORIZATION; +---- success: have INSERT UPDATE permission +GRANT INSERT, UPDATE ON test_upsert_001.t00 TO upsert_tester; +SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc'; +INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 5; +RESET SESSION AUTHORIZATION; +--- have SELECT INSERT UPDATE permission +GRANT SELECT, INSERT, UPDATE ON test_upsert_001.t00 TO upsert_tester; +SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc'; +INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 5; +INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col3 = 5; +ERROR: column "col3" of relation "t00" does not exist +LINE 1: ...t_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col3 = 5; + ^ +RESET SESSION AUTHORIZATION; +--- test with sequnce column +CREATE TABLE t01 (col1 INT , col2 BIGSERIAL PRIMARY KEY, col3 INT) ; +NOTICE: CREATE TABLE will create implicit sequence "t01_col2_seq" for serial column "t01.col2" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t01_pkey" for table "t01" +---- error: don't have UPDATE permission on sequence table. +GRANT SELECT, INSERT, UPDATE ON test_upsert_001.t01 TO upsert_tester; +SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc'; +INSERT INTO test_upsert_001.t01 VALUES(1) ON DUPLICATE KEY UPDATE col3 = 5; +ERROR: permission denied for sequence t01_col2_seq +CONTEXT: referenced column: col2 +RESET SESSION AUTHORIZATION; +---- have SELECT INSERT UPDATE permission on target relation, and UPDATE permission on sequence table. +GRANT UPDATE ON test_upsert_001.t01_col2_seq TO upsert_tester; +SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc'; +INSERT INTO test_upsert_001.t01 VALUES(1) ON DUPLICATE KEY UPDATE col3 = 5; +INSERT INTO test_upsert_001.t01 VALUES(1) ON DUPLICATE KEY UPDATE col3 = 5; +RESET SESSION AUTHORIZATION; +-- test ommit INSERT target column +CREATE TABLE t02 (col1 INT DEFAULT 1 PRIMARY KEY, col2 INT, col3 INT); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t02_pkey" for table "t02" +INSERT INTO t02 VALUES(1, 2, 3) ON DUPLICATE KEY UPDATE col2 = 20; +SELECT * FROM t02 ORDER BY 1, 2; + col1 | col2 | col3 +------+------+------ + 1 | 2 | 3 +(1 row) + +INSERT INTO t02 VALUES(1, 2, 3) ON DUPLICATE KEY UPDATE col2 = 20; +SELECT * FROM t02 ORDER BY 1, 2; + col1 | col2 | col3 +------+------+------ + 1 | 20 | 3 +(1 row) + +ALTER TABLE t02 DROP COLUMN col2; +ALTER TABLE t02 ADD COLUMN col4 INT; +INSERT INTO t02 VALUES(1, 2, 3) ON DUPLICATE KEY UPDATE col4 = 40; +SELECT * FROM t02 ORDER BY 1, 2; + col1 | col3 | col4 +------+------+------ + 1 | 3 | 40 +(1 row) + +INSERT INTO t02 VALUES(2, 3, 4) ON DUPLICATE KEY UPDATE col4 = 40; +SELECT * FROM t02 ORDER BY 1, 2; + col1 | col3 | col4 +------+------+------ + 1 | 3 | 40 + 2 | 3 | 4 +(2 rows) + +INSERT INTO t02 VALUES(2, 3, 4) ON DUPLICATE KEY UPDATE col4 = 40; +SELECT * FROM t02 ORDER BY 1, 2; + col1 | col3 | col4 +------+------+------ + 1 | 3 | 40 + 2 | 3 | 40 +(2 rows) + +-- test restriction +--- test replication table +CREATE TABLE t03 (col1 int PRIMARY KEY, col2 INT, col3 smallserial) ; +NOTICE: CREATE TABLE will create implicit sequence "t03_col3_seq" for serial column "t03.col3" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t03_pkey" for table "t03" +--- error: not allowed volatile function as default value +INSERT INTO t03(col2) VALUES(1) ON DUPLICATE KEY UPDATE col2 = 100; +ERROR: null value in column "col1" violates not-null constraint +DETAIL: Failing row contains (null, 1, 1). +ALTER TABLE t03 DROP COLUMN col3; +--- error: primary key are not allowed to update +INSERT INTO t03 VALUES(1) ON DUPLICATE KEY UPDATE col1 = 1; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +--- error: clause other than VALUSES are not allowed to use +INSERT INTO t03 SELECT * FROM t03 ON DUPLICATE KEY UPDATE col2 = 1; +--- success: expression index are supported +CREATE UNIQUE INDEX u_expr_index ON t03 USING btree (abs(col1)); +INSERT INTO t03 VALUES(-10, 10) ON DUPLICATE KEY UPDATE col2 = 20; +DROP INDEX u_expr_index; +-- test with stream operator on +INSERT INTO t03 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 100; +SELECT * FROM t03; + col1 | col2 +------+------ + -10 | 10 + 1 | +(2 rows) + +INSERT INTO t03 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 100; +SELECT * FROM t03; + col1 | col2 +------+------ + -10 | 10 + 1 | 100 +(2 rows) + +SELECT * FROM t03; + col1 | col2 +------+------ + -10 | 10 + 1 | 100 +(2 rows) + +--- test PBE +PREPARE p1 AS INSERT INTO t03 VALUES($1, $2) ON DUPLICATE KEY UPDATE col2 = $1*100; +EXECUTE p1(5, 50); +SELECT * FROM t03 WHERE col1 = 5; + col1 | col2 +------+------ + 5 | 50 +(1 row) + +EXECUTE p1(5, 50); +SELECT * FROM t03 WHERE col1 = 5; + col1 | col2 +------+------ + 5 | 500 +(1 row) + +DELETE t03 WHERE col1 = 5; +---- test with primary key +INSERT INTO t03 VALUES(2) ON DUPLICATE KEY UPDATE col2 = 200; +SELECT * FROM t03; + col1 | col2 +------+------ + -10 | 10 + 1 | 100 + 2 | +(3 rows) + +INSERT INTO t03 VALUES(2) ON DUPLICATE KEY UPDATE col2 = 200; +SELECT * FROM t03; + col1 | col2 +------+------ + -10 | 10 + 1 | 100 + 2 | 200 +(3 rows) + +SELECT * FROM t03; + col1 | col2 +------+------ + -10 | 10 + 1 | 100 + 2 | 200 +(3 rows) + +---- test with unique key without NOT NULL constraint +ALTER TABLE t03 DROP CONSTRAINT t03_pkey; +ALTER TABLE t03 ADD COLUMN col3 INT; +CREATE UNIQUE INDEX ON t03 (col1, col3); +---- unique constraints might contain NULL, depends on the plan +----- for cn light and fqs, it can be done +INSERT INTO t03 VALUES(3) ON DUPLICATE KEY UPDATE col2 = 300; +----- for stream or pgxc it can not be done +INSERT INTO t03 VALUES(3) ON DUPLICATE KEY UPDATE col2 = 300; +---- test with unique key with NOT NULL constraint, should success +CREATE UNIQUE INDEX ON t03 (col1); +ERROR: could not create unique index "t03_col1_idx" +DETAIL: Key (col1)=(3) is duplicated. +INSERT INTO t03 VALUES(3) ON DUPLICATE KEY UPDATE col2 = 300; +SELECT * FROM t03; + col1 | col2 | col3 +------+------+------ + -10 | 10 | + 1 | 100 | + 2 | 200 | + 3 | | + 3 | | + 3 | | +(6 rows) + +INSERT INTO t03 VALUES(3) ON DUPLICATE KEY UPDATE col2 = 300; +SELECT * FROM t03; + col1 | col2 | col3 +------+------+------ + -10 | 10 | + 1 | 100 | + 2 | 200 | + 3 | | + 3 | | + 3 | | + 3 | | +(7 rows) + +SELECT * FROM t03; + col1 | col2 | col3 +------+------+------ + -10 | 10 | + 1 | 100 | + 2 | 200 | + 3 | | + 3 | | + 3 | | + 3 | | +(7 rows) + +---- test PBE +PREPARE p2 AS INSERT INTO t03 VALUES($1, $2) ON DUPLICATE KEY UPDATE col2 = $1*100; +EXECUTE p2(5, 50); +SELECT * FROM t03 WHERE col1 = 5; + col1 | col2 | col3 +------+------+------ + 5 | 50 | +(1 row) + +EXECUTE p2(5, 50); +SELECT * FROM t03 WHERE col1 = 5; + col1 | col2 | col3 +------+------+------ + 5 | 50 | + 5 | 50 | +(2 rows) + +--- error: test with clause +WITH tmp(col1, col2) AS (SELECT * FROM t01) +INSERT INTO t01 SELECT * FROM tmp ON DUPLICATE KEY UPDATE col1 = 1; +ERROR: WITH clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement. +WITH RECURSIVE rq AS + ( + SELECT col1, col2 FROM t00 WHERE col1 = 1 + UNION ALL + SELECT origin.col1, rq.col2 + FROM rq JOIN t00 AS origin ON origin.col1 = rq.col1 + ) + INSERT INTO t03 SELECT * FROM rq ON DUPLICATE KEY UPDATE col1 = rq.col1; +ERROR: WITH clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement. +--- error: test returning clause +INSERT INTO t01 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 1 RETURNING NOT(1::bool); +ERROR: RETURNING clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement. +--- error: distribute key are not allowed to UPDATE +CREATE TABLE t04 (col1 INT, col2 INT) ; +INSERT INTO t04 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 5; +--- error: unique index referenced column are not allowed to UPDATE +CREATE UNIQUE INDEX t04_u_index ON t04(col1, col2); +INSERT INTO t04 VALUES (1) ON DUPLICATE KEY UPDATE col2 = 5; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +DROP INDEX t04_u_index; +--- error: primary key referenced column are not allowed to UPDATE +ALTER TABLE t04 ADD PRIMARY KEY (col1, col2); +ERROR: column "col2" contains null values +INSERT INTO t04 VALUES (1) ON DUPLICATE KEY UPDATE col2 = 5; +--- error: invalid column +INSERT INTO t04 (col2, col3) VALUES (2, 3) ON DUPLICATE KEY UPDATE col2 = 5; +ERROR: column "col3" of relation "t04" does not exist +LINE 1: INSERT INTO t04 (col2, col3) VALUES (2, 3) ON DUPLICATE KEY ... + ^ +--- error: duplicate column +INSERT INTO t04 (col2, col2) VALUES (2, 3) ON DUPLICATE KEY UPDATE col2 = 5; +ERROR: column "col2" specified more than once +LINE 1: INSERT INTO t04 (col2, col2) VALUES (2, 3) ON DUPLICATE KEY ... + ^ +-- error: target column more than insert target +INSERT INTO t04 (col1, col2) VALUES (2, 3, 4) ON DUPLICATE KEY UPDATE col2 = 5; +ERROR: INSERT has more expressions than target columns +LINE 1: INSERT INTO t04 (col1, col2) VALUES (2, 3, 4) ON DUPLICATE K... + ^ +INSERT INTO t04 (col1, col2) VALUES (1) ON DUPLICATE KEY UPDATE col2 = 5; +ERROR: INSERT has more target columns than expressions +LINE 1: INSERT INTO t04 (col1, col2) VALUES (1) ON DUPLICATE KEY UPD... + ^ +INSERT INTO t04 (col1, col2) SELECT col1 FROM t04 ON DUPLICATE KEY UPDATE col2 = 5; +ERROR: INSERT has more target columns than expressions +LINE 1: INSERT INTO t04 (col1, col2) SELECT col1 FROM t04 ON DUPLICA... + ^ +INSERT INTO t04 (col1, col2) SELECT *, col1 FROM t04 ON DUPLICATE KEY UPDATE col2 = 5; +ERROR: INSERT has more expressions than target columns +LINE 1: INSERT INTO t04 (col1, col2) SELECT *, col1 FROM t04 ON DUPL... + ^ +INSERT INTO t04 VALUES (2, 3, 4) ON DUPLICATE KEY UPDATE col2 = 5; +ERROR: INSERT has more expressions than target columns +LINE 1: INSERT INTO t04 VALUES (2, 3, 4) ON DUPLICATE KEY UPDATE col... + ^ +INSERT INTO t04 VALUES (1) ON DUPLICATE KEY UPDATE col2 = 5; +INSERT INTO t04 SELECT col1 FROM t04 ON DUPLICATE KEY UPDATE col2 = 5; +INSERT INTO t04 SELECT *, col1 FROM t04 ON DUPLICATE KEY UPDATE col2 = 5; +ERROR: INSERT has more expressions than target columns +LINE 1: INSERT INTO t04 SELECT *, col1 FROM t04 ON DUPLICATE KEY UPD... + ^ +-- test DEFAULT VALUES +TRUNCATE t00; +TRUNCATE t01; +--- without sequence +----should insert +INSERT INTO t00 DEFAULT VALUES ON DUPLICATE KEY UPDATE col2 = col1; +SELECT * FROM t00 ORDER BY 1, 2; + col1 | col2 +------+------ + 1 | +(1 row) + +---- should update +INSERT INTO t00 DEFAULT VALUES ON DUPLICATE KEY UPDATE col2 = col1; +SELECT * FROM t00 ORDER BY 1, 2; + col1 | col2 +------+------ + 1 | 1 +(1 row) + +--- test drop column +TRUNCATE t00; +ALTER TABLE t00 DROP COLUMN col2; +ALTER TABLE t00 ADD COLUMN col3 INT DEFAULT 100; +----should insert +INSERT INTO t00 DEFAULT VALUES ON DUPLICATE KEY UPDATE col3 = col1; +SELECT * FROM t00 ORDER BY 1, 2; + col1 | col3 +------+------ + 1 | 100 +(1 row) + +---- should update +INSERT INTO t00 DEFAULT VALUES ON DUPLICATE KEY UPDATE col3 = col1; +SELECT * FROM t00 ORDER BY 1, 2; + col1 | col3 +------+------ + 1 | 1 +(1 row) + +--- with sequence +----should insert +INSERT INTO t01 DEFAULT VALUES ON DUPLICATE KEY UPDATE col1 = col2; +INSERT INTO t01 (col2) SELECT col2 + 1 FROM t01 LIMIT 1; +SELECT * FROM t01 ORDER BY 1, 2, 3; + col1 | col2 | col3 +------+------+------ + | 3 | + | 4 | +(2 rows) + +---- should update +INSERT INTO t01 DEFAULT VALUES ON DUPLICATE KEY UPDATE col1 = col2; +SELECT * FROM t01 ORDER BY 1, 2, 3; + col1 | col2 | col3 +------+------+------ + 4 | 4 | + | 3 | +(2 rows) + +-- test VALUES(DEFAULT) +CREATE TABLE t05 (col1 INT , col2 INT DEFAULT 1 PRIMARY KEY, col3 INT DEFAULT 100) ; +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t05_pkey" for table "t05" +--- should insert +INSERT INTO t05 VALUES(DEFAULT) ON DUPLICATE KEY UPDATE col3 = 1000; +SELECT * FROM t05 ORDER BY 1, 2, 3; + col1 | col2 | col3 +------+------+------ + | 1 | 100 +(1 row) + +--- should update +INSERT INTO t05 VALUES(DEFAULT) ON DUPLICATE KEY UPDATE col3 = 1000; +SELECT * FROM t05 ORDER BY 1, 2, 3; + col1 | col2 | col3 +------+------+------ + | 1 | 1000 +(1 row) + +-- test UPDATE DEFAULT +INSERT INTO t05 (col1, col2, col3) VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col3 = DEFAULT; +SELECT * FROM t05 ORDER BY 1, 2, 3; + col1 | col2 | col3 +------+------+------ + 2 | 2 | 2 + | 1 | 1000 +(2 rows) + +INSERT INTO t05 (col1, col2, col3) VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col3 = DEFAULT; +SELECT * FROM t05 ORDER BY 1, 2, 3; + col1 | col2 | col3 +------+------+------ + 2 | 2 | 100 + | 1 | 1000 +(2 rows) + +-- test VALUES (DEFAULT, ...) +TRUNCATE t05; +--- should insert +INSERT INTO t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT) +ON DUPLICATE KEY UPDATE col3 = DEFAULT, col1 = col3; +SELECT * FROM t05 ORDER BY 1, 2, 3; + col1 | col2 | col3 +------+------+------ + | 1 | 200 + | 200 | 100 +(2 rows) + +--- should update +INSERT INTO t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT) +ON DUPLICATE KEY UPDATE col3 = DEFAULT, col1 = col3; +SELECT * FROM t05 ORDER BY 1, 2, 3; + col1 | col2 | col3 +------+------+------ + 100 | 200 | 100 + 200 | 1 | 100 +(2 rows) + +--- test drop coulmn +BEGIN; +ALTER TABLE t05 ADD COLUMN col4 INT; +ALTER TABLE t05 ADD COLUMN col5 INT DEFAULT 500; +ALTER TABLE t05 DROP COLUMN col3; +TRUNCATE t05; +INSERT INTO t05 VALUES(DEFAULT, DEFAULT, DEFAULT, 600) ON DUPLICATE KEY UPDATE col5 = DEFAULT; +SELECT * FROM t05 ORDER BY 1, 2, 3; + col1 | col2 | col4 | col5 +------+------+------+------ + | 1 | | 600 +(1 row) + +INSERT INTO t05 VALUES(DEFAULT, DEFAULT, DEFAULT, 600) ON DUPLICATE KEY UPDATE col5 = DEFAULT; +SELECT * FROM t05 ORDER BY 1, 2, 3; + col1 | col2 | col4 | col5 +------+------+------+------ + | 1 | | 500 +(1 row) + +ROLLBACK; +-- test schema +SET current_schema = public; +TRUNCATE test_upsert_001.t05; +--- should insert +INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT) +ON DUPLICATE KEY UPDATE col3 = DEFAULT, col1 = col3; +SELECT * FROM test_upsert_001.t05 ORDER BY 1, 2, 3; + col1 | col2 | col3 +------+------+------ + | 1 | 200 + | 200 | 100 +(2 rows) + +--- should update +INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT) +ON DUPLICATE KEY UPDATE col3 = DEFAULT, col1 = col3; +SELECT * FROM test_upsert_001.t05 ORDER BY 1, 2, 3; + col1 | col2 | col3 +------+------+------ + 100 | 200 | 100 + 200 | 1 | 100 +(2 rows) + +--- test using schema on update +INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT) +ON DUPLICATE KEY UPDATE t05.col3 = DEFAULT, t05.col1 = t05.col3 + 1; +SELECT * FROM test_upsert_001.t05 ORDER BY 1, 2, 3; + col1 | col2 | col3 +------+------+------ + 101 | 1 | 100 + 101 | 200 | 100 +(2 rows) + +--- error: should not append schema +INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT) +ON DUPLICATE KEY UPDATE test_upsert_001.t05.col3 = DEFAULT, t05.col1 = t05.col3 + 1; +ERROR: column "test_upsert_001.t05" of relation "t05" does not exist +LINE 2: ON DUPLICATE KEY UPDATE test_upsert_001.t05.col3 = DEFAULT, ... + ^ +INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT) +ON DUPLICATE KEY UPDATE t05.col3 = DEFAULT, t05.col1 = test_upsert_001.t05.col3 + 1; +SET CURRENT_SCHEMA TO test_upsert_001; +DROP USER upsert_tester CASCADE; +DROP SCHEMA test_upsert_001 CASCADE; +NOTICE: drop cascades to 6 other objects +DETAIL: drop cascades to table t00 +drop cascades to table t01 +drop cascades to table t02 +drop cascades to table t03 +drop cascades to table t04 +drop cascades to table t05 diff --git a/src/test/regress/expected/upsert_002.out b/src/test/regress/expected/upsert_002.out new file mode 100644 index 000000000..7701d4384 --- /dev/null +++ b/src/test/regress/expected/upsert_002.out @@ -0,0 +1,545 @@ +DROP SCHEMA test_upsert_002 CASCADE; +ERROR: schema "test_upsert_002" does not exist +CREATE SCHEMA test_upsert_002; +SET CURRENT_SCHEMA TO test_upsert_002; +-- enable_upsert_to_merge must is off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +-- test t1 with no index +CREATE TABLE t1 ( + col1 INT, + col2 INT, + col3 INT DEFAULT 1, +-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 BIGSERIAL +) ; +NOTICE: CREATE TABLE will create implicit sequence "t1_col5_seq" for serial column "t1.col5" +INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE col1 = 3; +--- should always insert +INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE col2 = 3; +INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE t1.col2 = 4; +--- appoint column list in insert clause, should always insert +INSERT INTO t1(col1, col3) VALUES (1, 3) ON DUPLICATE KEY UPDATE col2 = 5; +INSERT INTO t1(col1, col3) VALUES (1, 3) ON DUPLICATE KEY UPDATE t1.col2 = 6; +--- multiple rows, should always insert +INSERT INTO t1 VALUES (2, 1), (2, 1) ON DUPLICATE KEY UPDATE col2 = 7, col3 = 7; +SELECT * FROM t1 ORDER BY col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | 2 | 1 | 1 + 1 | 2 | 1 | 2 + 1 | 2 | 1 | 3 + 1 | | 3 | 4 + 1 | | 3 | 5 + 2 | 1 | 1 | 6 + 2 | 1 | 1 | 7 +(7 rows) + +--- test union, should insert +INSERT INTO t1 (col1, col2) + SELECT * FROM + (SELECT col1, col2 FROM t1 + UNION + SELECT col1, col3 FROM t1) AS union_table + ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3) * 10; +SELECT col1, col2, col3 FROM t1 WHERE col5 > 6 ORDER BY col1, col2; + col1 | col2 | col3 +------+------+------ + 1 | 1 | 1 + 1 | 2 | 1 + 1 | 3 | 1 + 1 | | 1 + 2 | 1 | 1 + 2 | 1 | 1 +(6 rows) + +--- test subquery, should insert +INSERT INTO t1 + (SELECT col1 || col2 || '00' FROM t1 ORDER BY col5) + ON DUPLICATE KEY UPDATE col3 = col1 * 100; +SELECT col1, col2, col3 FROM t1 WHERE col1 >= 100 ORDER BY col1; + col1 | col2 | col3 +------+------+------ + 100 | | 1 + 100 | | 1 + 100 | | 1 + 1100 | | 1 + 1200 | | 1 + 1200 | | 1 + 1200 | | 1 + 1200 | | 1 + 1300 | | 1 + 2100 | | 1 + 2100 | | 1 + 2100 | | 1 +(12 rows) + +-- test t2 with one primary key +CREATE TABLE t2 ( + col1 INT, + col2 INT PRIMARY KEY, + col3 INT DEFAULT 1, + col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 BIGSERIAL +) ; +NOTICE: CREATE TABLE will create implicit sequence "t2_col5_seq" for serial column "t2.col5" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t2_pkey" for table "t2" +--- primary key or unique key are not allowed to update +INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE col2 = 3; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE t2.col2 = 3; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +INSERT INTO t2 (col2, col3, col4, col5) + VALUES (10, 10, CURRENT_TIMESTAMP(0), 10), + (20, 20, CURRENT_TIMESTAMP(1), 20), + (30, 30, CURRENT_TIMESTAMP(2), 30) + ON DUPLICATE KEY UPDATE + col1 = 100, + col2 = 100, + col3 = 100, + col4 = '2019-08-09'::TIMESTAMP, + col5 = 100; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +--- should insert +INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 30; +INSERT INTO t2 VALUES (2, 2) ON DUPLICATE KEY UPDATE t2.col1 = 40; +INSERT INTO t2 VALUES (3, 3) ON DUPLICATE KEY UPDATE col1 = col1 * 2; +INSERT INTO t2 VALUES (4, 4) ON DUPLICATE KEY UPDATE t2.col1 = t2.col1 * 2 ; +INSERT INTO t2 VALUES (5, 5) ON DUPLICATE KEY UPDATE col1 = col2 + 1; +INSERT INTO t2 VALUES (6, 6) ON DUPLICATE KEY UPDATE t2.col1 = t2.col2 + 1; +INSERT INTO t2 VALUES (7, 7) ON DUPLICATE KEY UPDATE col1 = extract(dow from col4) + 10; +INSERT INTO t2 VALUES (8, 8) ON DUPLICATE KEY UPDATE t2.col1 = extract(century from col4) * 100 + extract(isodow from col4); +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | 1 | 1 | 1 + 2 | 2 | 1 | 2 + 3 | 3 | 1 | 3 + 4 | 4 | 1 | 4 + 5 | 5 | 1 | 5 + 6 | 6 | 1 | 6 + 7 | 7 | 1 | 7 + 8 | 8 | 1 | 8 +(8 rows) + +--- should update +INSERT INTO t2 VALUES (3, 1) ON DUPLICATE KEY UPDATE col1 = 30, col3 = col5 + 1; +INSERT INTO t2 VALUES (4, 2) ON DUPLICATE KEY UPDATE t2.col1 = 40, t2.col3 = t2.col5; +INSERT INTO t2 VALUES (3, 3), (4, 4) ON DUPLICATE KEY UPDATE col3 = t2.col5 + 1; +INSERT INTO t2 VALUES (5, 5) ON DUPLICATE KEY UPDATE col1 = extract(dow from col4) + 10; +INSERT INTO t2 VALUES (6, 6) ON DUPLICATE KEY UPDATE t2.col1 = extract(century from col4) * 100 + extract(isodow from col4); +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 30 | 1 | 2 | 1 + 40 | 2 | 2 | 2 + 3 | 3 | 4 | 3 + 4 | 4 | 5 | 4 +--?.*| 5 | 1 | 5 +--?.*| 6 | 1 | 6 + 7 | 7 | 1 | 7 + 8 | 8 | 1 | 8 +(8 rows) + +-- primary key are not allowed to be null +INSERT INTO t2 (col1) VALUES (10) ON DUPLICATE KEY UPDATE col1 = 10; +ERROR: null value in column "col2" violates not-null constraint +--? +--- appoint column list in insert clause +---- should insert +INSERT INTO t2 (col2, col3) VALUES (9, 9) ON DUPLICATE KEY UPDATE col1 = 90; +INSERT INTO t2 (col2, col3, col4, col5) + VALUES (10, 10, CURRENT_TIMESTAMP(0), 10), + (20, 20, CURRENT_TIMESTAMP(1), 20), + (30, 30, CURRENT_TIMESTAMP(2), 30) + ON DUPLICATE KEY UPDATE + col1 = 100, + col3 = 100, + col4 = '2019-08-20'::TIMESTAMP, + col5 = 100; +SELECT * FROM t2 ORDER BY col5; + col1 | col2 | col3 | col4 | col5 +------+------+------+---------------------------------+------ +--? 30 | 1 | 2 | .* | 1 +--? 40 | 2 | 2 | .* | 2 +--? 3 | 3 | 4 | .* | 3 +--? 4 | 4 | 5 | .* | 4 +--? .* | 5 | 1 | .* | 5 +--? .* | 6 | 1 | .* | 6 +--? 7 | 7 | 1 | .* | 7 +--? 8 | 8 | 1 | .* | 8 +--? | 10 | 10 | .* | 10 +--? | 9 | 9 | .* | 16 +--? | 20 | 20 | .* | 20 +--? | 30 | 30 | .* | 30 +(12 rows) + +---- should update +INSERT INTO t2 (col2, col3) VALUES (9, 9) ON DUPLICATE KEY UPDATE col1 = 90; +INSERT INTO t2 (col2, col3, col4, col5) + VALUES (10, 10, CURRENT_TIMESTAMP(0), 10), + (20, 20, CURRENT_TIMESTAMP(1), 20), + (30, 30, CURRENT_TIMESTAMP(2), 30) + ON DUPLICATE KEY UPDATE + col1 = 100, + col3 = 100, + col4 = '2019-08-20'::TIMESTAMP, + col5 = 100; +SELECT * FROM t2 ORDER BY col5, col2; + col1 | col2 | col3 | col4 | col5 +------+------+------+---------------------------------+------ +--? 30 | 1 | 2 | .* | 1 +--? 40 | 2 | 2 | .* | 2 +--? 3 | 3 | 4 | .* | 3 +--? 4 | 4 | 5 | .* | 4 +--? .* | 5 | 1 | .* | 5 +--? .* | 6 | 1 | .* | 6 +--? 7 | 7 | 1 | .* | 7 +--? 8 | 8 | 1 | .* | 8 +--? 90 | 9 | 9 | .* | 16 + 100 | 10 | 100 | Tue Aug 20 00:00:00 2019 | 100 + 100 | 20 | 100 | Tue Aug 20 00:00:00 2019 | 100 + 100 | 30 | 100 | Tue Aug 20 00:00:00 2019 | 100 +(12 rows) + +--- test subquery +---- should insert +INSERT INTO t2 + (SELECT col1 * 1000, col2 * 1000 + 1 FROM t2 ORDER BY col5 LIMIT 2) + ON DUPLICATE KEY UPDATE col3 = col2 + 1; +INSERT INTO t2 (col2, col3) + (SELECT col1 * 1000 + 2, col2 * 1000 FROM t2 ORDER BY col5 LIMIT 2 OFFSET 1) + ON DUPLICATE KEY UPDATE col3 = col2 + 1; +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2; + col1 | col2 | col3 | col5 +-------+-------+------+------ + 30 | 1 | 2 | 1 + 40 | 2 | 2 | 2 + 3 | 3 | 4 | 3 + 4 | 4 | 5 | 4 +--? .* | 5 | 1 | 5 +--? .* | 6 | 1 | 6 + 7 | 7 | 1 | 7 + 8 | 8 | 1 | 8 + 90 | 9 | 9 | 16 +--? 30000 | 1001 | 1 | 11 +--? 40000 | 2001 | 1 | 12 +--? | 40002 | 2000 | 13 +--? | 3002 | 3000 | 14 + 100 | 10 | 100 | 100 + 100 | 20 | 100 | 100 + 100 | 30 | 100 | 100 +(16 rows) + +---- should update +INSERT INTO t2 + (SELECT col1 * 1000, col2 * 1000 + 1 FROM t2 ORDER BY col5 LIMIT 2) + ON DUPLICATE KEY UPDATE col3 = col2 + 1; +INSERT INTO t2 (col2, col3) + (SELECT col1 * 1000 + 2, col2 * 1000 FROM t2 ORDER BY col5 LIMIT 2 OFFSET 1) + ON DUPLICATE KEY UPDATE col3 = col2 + 1; +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2; + col1 | col2 | col3 | col5 +-------+-------+-------+------ + 30 | 1 | 2 | 1 + 40 | 2 | 2 | 2 + 3 | 3 | 4 | 3 + 4 | 4 | 5 | 4 +--? .* | 5 | 1 | 5 +--? .* | 6 | 1 | 6 + 7 | 7 | 1 | 7 + 8 | 8 | 1 | 8 + 90 | 9 | 9 | 16 +--? 30000 | 1001 | 1002 | 11 +--? 40000 | 2001 | 2002 | 12 +--? | 40002 | 40003 | 13 +--? | 3002 | 3003 | 14 + 100 | 10 | 100 | 100 + 100 | 20 | 100 | 100 + 100 | 30 | 100 | 100 +(16 rows) + +--- test union, some insert some update +SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT col1, col3 FROM t1 ORDER BY 1, 2; + col1 | col2 +------+------ + 1 | 1 + 1 | 2 + 1 | 3 + 2 | 1 + 100 | 1 + 1100 | 1 + 1200 | 1 + 1300 | 1 + 2100 | 1 +(9 rows) + +INSERT INTO t2 (col1, col2) + SELECT * FROM + (SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT col1, col3 FROM t1) AS union_table + ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3); +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2; + col1 | col2 | col3 | col5 +-------+-------+-------+------ + 30 | 1 | 219 | 1 + 40 | 2 | 44 | 2 + 3 | 3 | 10 | 3 + 4 | 4 | 5 | 4 +--? .* | 5 | 1 | 5 +--? .* | 6 | 1 | 6 + 7 | 7 | 1 | 7 + 8 | 8 | 1 | 8 + 90 | 9 | 9 | 16 +--? 30000 | 1001 | 1002 | 11 +--? 40000 | 2001 | 2002 | 12 +--? | 40002 | 40003 | 13 +--? | 3002 | 3003 | 14 + 100 | 10 | 100 | 100 + 100 | 20 | 100 | 100 + 100 | 30 | 100 | 100 +(16 rows) + +INSERT INTO t2 (col1, col2) + SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT col1, col3 FROM t1 + ON DUPLICATE KEY UPDATE col3 = col3 + 1; +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2; + col1 | col2 | col3 | col5 +-------+-------+-------+------ + 30 | 1 | 226 | 1 + 40 | 2 | 45 | 2 + 3 | 3 | 11 | 3 + 4 | 4 | 5 | 4 +--? 15 | 5 | 1 | 5 +--? 2105 | 6 | 1 | 6 + 7 | 7 | 1 | 7 + 8 | 8 | 1 | 8 + 90 | 9 | 9 | 16 +--? 30000 | 1001 | 1002 | 11 +--? 40000 | 2001 | 2002 | 12 +--? | 40002 | 40003 | 13 +--? | 3002 | 3003 | 14 + 100 | 10 | 100 | 100 + 100 | 20 | 100 | 100 + 100 | 30 | 100 | 100 +(16 rows) + +reset behavior_compat_options; +-- test INTERSECT, should update +(SELECT col1, col1 + col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT 1, 2) + INTERSECT + SELECT col1, col3 FROM t1; + col1 | ?column? +------+---------- + 1 | 3 +(1 row) + +INSERT INTO t2 (col1, col2) + (SELECT col1, col1 + col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT 1, 2) + INTERSECT + SELECT col1, col3 FROM t1 + ON DUPLICATE KEY UPDATE col3 = col3 + 1; +SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 3 ORDER BY col5, col2; + col1 | col2 | col3 | col5 +------+------+------+------ + 3 | 3 | 12 | 3 +(1 row) + +-- test EXCEPT, should update +SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + EXCEPT + (SELECT col1, col3 FROM t1 + UNION + SELECT NULL, NULL); + col1 | col2 +------+------ + 1 | 2 +(1 row) + +INSERT INTO t2 (col1, col2) + SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + EXCEPT + (SELECT col1, col3 FROM t1 + UNION + SELECT NULL, NULL) + ON DUPLICATE KEY UPDATE col3 = col3 + 1; +SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 2 ORDER BY col5, col2; + col1 | col2 | col3 | col5 +------+------+------+------ + 40 | 2 | 46 | 2 +(1 row) + +-- test unique index with not default value +ALTER TABLE t2 DROP CONSTRAINT t2_pkey; +CREATE UNIQUE INDEX t2_u_index ON t2(col2, col5); +SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + EXCEPT + (SELECT col1, col3 FROM t1 + UNION + SELECT NULL, NULL); + col1 | col2 +------+------ + 1 | 2 +(1 row) + +INSERT INTO t2 + SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + EXCEPT + (SELECT col1, col3 FROM t1 + UNION + SELECT NULL, NULL) + ON DUPLICATE KEY UPDATE col3 = col3 + 1; +SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 2 ORDER BY col5, col2; + col1 | col2 | col3 | col5 +------+------+------+------ + 40 | 2 | 46 | 2 + 1 | 2 | 1 | 46 +(2 rows) + +-- test t3 with one primary index with two columns +CREATE TABLE t3 ( + col1 INT, + col2 INT, + col3 INT DEFAULT 1, +-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 BIGSERIAL, + PRIMARY KEY (col2, col3) +) ; +NOTICE: CREATE TABLE will create implicit sequence "t3_col5_seq" for serial column "t3.col5" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t3_pkey" for table "t3" +--- column referenced by primary key are not allowed to update +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col3 = 3; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +--- should insert when not contains primary key and not all primary key referred columns have default value. +--- but will fail cause primary key should not be null +INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2; +ERROR: null value in column "col2" violates not-null constraint +--?.* +--- should insert +--- (SEQUENCE BUG: the serial column will starts from 2 since the above statement has applied for a sequence) +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20; +SELECT * FROM t3 order by col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | 1 | 1 | 2 + 2 | 2 | 2 | 3 +(2 rows) + +--- should update +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20; +SELECT * FROM t3 order by col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 10 | 1 | 1 | 2 + 2 | 2 | 2 | 20 +(2 rows) + +--- test subquery +---- should insert +INSERT INTO t3 (col2, col3) (SELECT max(col2) + 1, max(col3) + 1 FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +SELECT * FROM t3 order by col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 10 | 1 | 1 | 2 + | 3 | 3 | 6 + 2 | 2 | 2 | 20 +(3 rows) + +---- should update +INSERT INTO t3 (col2, col3) (SELECT max(col2), max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +SELECT * FROM t3 order by col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 10 | 1 | 1 | 2 + 6 | 3 | 3 | 6 + 2 | 2 | 2 | 20 +(3 rows) + +-- test t3 with one unique index with two columns +TRUNCATE t3; +ALTER TABLE t3 DROP CONSTRAINT t3_pkey; +ALTER TABLE t3 ALTER COLUMN col2 DROP NOT NULL; +CREATE UNIQUE INDEX t3_ukey ON t3 (col2, col3); +--- column referenced by unique key are not allowed to update +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col3 = 3; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +--- should insert cause not contains unique key and not all unique key referred columns have default value. +INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2; +INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2; +SELECT * FROM t3 order by col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | | 1 | 8 + 1 | | 1 | 9 +(2 rows) + +--- should insert +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE t3.col5 = 20; +SELECT * FROM t3 ORDER BY col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | | 1 | 8 + 1 | | 1 | 9 + 1 | 1 | 1 | 10 + 2 | 2 | 2 | 11 +(4 rows) + +--- should update +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE t3.col1 = 10; +INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20; +SELECT * FROM t3 ORDER BY col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | | 1 | 8 + 1 | | 1 | 9 + 10 | 1 | 1 | 10 + 2 | 2 | 2 | 20 +(4 rows) + +--- test subquery +---- should insert +INSERT INTO t3 (SELECT 100, NULL, max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +INSERT INTO t3 (SELECT 100, NULL, max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +INSERT INTO t3 (col2, col3) (SELECT max(col2) + 1, max(col3) + 1 FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +SELECT * FROM t3 ORDER BY col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | | 1 | 8 + 1 | | 1 | 9 + 10 | 1 | 1 | 10 + 100 | | 2 | 14 + 100 | | 2 | 15 + | 3 | 3 | 16 + 2 | 2 | 2 | 20 +(7 rows) + +---- should update +INSERT INTO t3 (col2, col3) (SELECT max(col2), max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +SELECT * FROM t3 ORDER BY col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | | 1 | 8 + 1 | | 1 | 9 + 10 | 1 | 1 | 10 + 100 | | 2 | 14 + 100 | | 2 | 15 + 6 | 3 | 3 | 16 + 2 | 2 | 2 | 20 +(7 rows) + +DROP SCHEMA test_upsert_002 CASCADE; +NOTICE: drop cascades to 3 other objects +DETAIL: drop cascades to table t1 +drop cascades to table t2 +drop cascades to table t3 diff --git a/src/test/regress/expected/upsert_003.out b/src/test/regress/expected/upsert_003.out new file mode 100644 index 000000000..6b2e90ad6 --- /dev/null +++ b/src/test/regress/expected/upsert_003.out @@ -0,0 +1,313 @@ +DROP SCHEMA test_insert_update_003 CASCADE; +ERROR: schema "test_insert_update_003" does not exist +CREATE SCHEMA test_insert_update_003; +SET CURRENT_SCHEMA TO test_insert_update_003; +-- enable_upsert_to_merge must is off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +-- test t4 with one primary key with three columns +CREATE TABLE t4 ( + col1 INT, + col2 INT DEFAULT 0, + col3 INT DEFAULT 1, + col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 BIGSERIAL, + PRIMARY KEY (col2, col3, col5) +) ; +NOTICE: CREATE TABLE will create implicit sequence "t4_col5_seq" for serial column "t4.col5" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t4_pkey" for table "t4" +--- should insert +INSERT INTO t4 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t4 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t4 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col1 = 200; +INSERT INTO t4 VALUES (100, 100, 100, CURRENT_TIMESTAMP, 100) ON DUPLICATE KEY UPDATE col1 = 1000; +SELECT col1, col2, col3, col5 FROM t4 ORDER BY col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | 0 | 1 | 1 + 1 | 0 | 1 | 2 + 2 | 2 | 2 | 3 + 100 | 100 | 100 | 100 +(4 rows) + +--- should update +INSERT INTO t4 VALUES (2, 2, 2, CURRENT_TIMESTAMP, 3) ON DUPLICATE KEY UPDATE col1 = 200; +INSERT INTO t4 VALUES (100, 100, 100, CURRENT_TIMESTAMP, 100) ON DUPLICATE KEY UPDATE col1 = 1000; +SELECT col1, col2, col3, col5 FROM t4 ORDER BY col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | 0 | 1 | 1 + 1 | 0 | 1 | 2 + 200 | 2 | 2 | 3 + 1000 | 100 | 100 | 100 +(4 rows) + +--- error: duplicate key update on (x, x, 20) +--- this is because current version is not inplace update but merge, +--- so when the subquery contains multiple same values, it will cause duplicate insert failure. +SELECT col3, sum(col3) * 10 FROM t4 GROUP BY col3 ORDER BY 1, 2; + col3 | ?column? +------+---------- + 1 | 20 + 2 | 20 + 100 | 1000 +(3 rows) + +INSERT INTO t4 (col1, col5) + (SELECT col3, sum(col3) * 10 FROM t4 GROUP BY col3) + ON DUPLICATE KEY UPDATE col1 = 3; +-- test t5 with sequence or default column with volatile function in constaint index +CREATE TABLE t5 ( + col1 INT, + col2 INT DEFAULT 1, + col3 BIGSERIAL, +-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 INTEGER(10, 5) DEFAULT RANDOM() + 1 +) ; +NOTICE: CREATE TABLE will create implicit sequence "t5_col3_seq" for serial column "t5.col3" +-- test t5 with sequence column in constaint index +CREATE UNIQUE INDEX u_t5_index1 ON t5(col1, col3); +--- should insert +INSERT INTO t5 VALUES (1), (1), (1) ON DUPLICATE KEY UPDATE col2 = col3; +INSERT INTO t5 DEFAULT VALUES ON DUPLICATE KEY UPDATE col2 = col3; +SELECT * FROM t5 ORDER BY col3; + col1 | col2 | col3 | col5 +------+------+------+--------- +--? 1 | 1 | 1 | 1.58182 +--? 1 | 1 | 2 | 1.00814 +--? 1 | 1 | 3 | 1.50194 +--? | 1 | 4 | 1.12955 +(4 rows) + +--- should update +INSERT INTO t5 (col1, col3) VALUES (1, 1), (1, 2), (1, 3) ON DUPLICATE KEY UPDATE col5 = col2, col2 = col3 * 10; +SELECT * FROM t5 WHERE col1 = 1 ORDER BY col3; + col1 | col2 | col3 | col5 +------+------+------+--------- + 1 | 10 | 1 | 1.00000 + 1 | 20 | 2 | 1.00000 + 1 | 30 | 3 | 1.00000 +(3 rows) + +--- should some insert some update +INSERT INTO t5 (col1, col3) VALUES (2, 5), (2, 6); +SELECT col1, col2, col3 FROM t5 ORDER BY col3; + col1 | col2 | col3 +------+------+------ + 1 | 10 | 1 + 1 | 20 | 2 + 1 | 30 | 3 + | 1 | 4 + 2 | 1 | 5 + 2 | 1 | 6 +(6 rows) + +INSERT INTO t5 (col1) VALUES (2), (2), (2) ON DUPLICATE KEY UPDATE col2 = col3; +SELECT col1, col2, col3 FROM t5 ORDER BY col3; + col1 | col2 | col3 +------+------+------ + 1 | 10 | 1 + 1 | 20 | 2 + 1 | 30 | 3 + | 1 | 4 + 2 | 5 | 5 + 2 | 6 | 6 + 2 | 1 | 7 +(7 rows) + +--- should INSERT and sequence starting from 7 +INSERT INTO t5 VALUES (2), (2); +SELECT col1, col2, col3 FROM t5 ORDER BY col3; + col1 | col2 | col3 +------+------+------ + 1 | 10 | 1 + 1 | 20 | 2 + 1 | 30 | 3 + | 1 | 4 + 2 | 5 | 5 + 2 | 6 | 6 + 2 | 1 | 7 + 2 | 1 | 8 + 2 | 1 | 9 +(9 rows) + +-- test with volatile function as default column in constraint index +TRUNCATE t5; +DROP INDEX u_t5_index1; +CREATE UNIQUE INDEX u_t5_index2 ON t5(col1, col5) WHERE col1 > 2; +--- should insert +INSERT INTO t5 VALUES (3), (3), (3) ON DUPLICATE KEY UPDATE col2 = col3; +INSERT INTO t5 (col1) VALUES (4), (4), (4) ON DUPLICATE KEY UPDATE col2 = col3; +SELECT * FROM t5 ORDER BY col3; + col1 | col2 | col3 | col5 +------+------+------+--------- +--? 3 | 1 | 10 | 1.24521 +--? 3 | 1 | 11 | 1.80598 +--? 3 | 1 | 12 | 1.86845 +--? 4 | 1 | 13 | 1.65797 +--? 4 | 1 | 14 | 1.07881 +--? 4 | 1 | 15 | 1.69493 +(6 rows) + +--- should update +INSERT INTO t5 (col1, col5) SELECT col1, col5 FROM t5 where col1 = 3 ON DUPLICATE KEY UPDATE col2 = col5 * 100; +SELECT * FROM t5 ORDER BY col3; + col1 | col2 | col3 | col5 +------+------+------+--------- +--? 3 | 125 | 10 | 1.24521 +--? 3 | 181 | 11 | 1.80598 +--? 3 | 187 | 12 | 1.86845 +--? 4 | 1 | 13 | 1.65797 +--? 4 | 1 | 14 | 1.07881 +--? 4 | 1 | 15 | 1.69493 +(6 rows) + +--- test subquery +---- should insert +INSERT INTO t5 (col1, col2) SELECT col1, col2 FROM t5 ON DUPLICATE KEY UPDATE col2 = col5 * 100; +SELECT * FROM t5 ORDER BY col3; + col1 | col2 | col3 | col5 +------+------+------+--------- +--? 3 | 167 | 10 | 1.67400 +--? 3 | 132 | 11 | 1.31552 +--? 3 | 129 | 12 | 1.29423 +--? 4 | 1 | 13 | 1.44100 +--? 4 | 1 | 14 | 1.37620 +--? 4 | 1 | 15 | 1.51296 +--? 4 | 1 | 16 | 1.37516 +--? 4 | 1 | 17 | 1.21710 +--? 4 | 1 | 18 | 1.50422 +--? 3 | 132 | 19 | 1.30560 +--? 3 | 167 | 20 | 1.62282 +--? 3 | 129 | 21 | 1.76699 +(12 rows) + +---- should update +INSERT INTO t5 SELECT * FROM t5 ON DUPLICATE KEY UPDATE col2 = col5 * 1000; +SELECT * FROM t5 ORDER BY col3; + col1 | col2 | col3 | col5 +------+------+------+--------- +--? 3 | 1674 | 10 | 1.67400 +--? 3 | 1316 | 11 | 1.31552 +--? 3 | 1294 | 12 | 1.29423 +--? 4 | 1441 | 13 | 1.44100 +--? 4 | 1376 | 14 | 1.37620 +--? 4 | 1513 | 15 | 1.51296 +--? 4 | 1375 | 16 | 1.37516 +--? 4 | 1217 | 17 | 1.21710 +--? 4 | 1504 | 18 | 1.50422 +--? 3 | 1306 | 19 | 1.30560 +--? 3 | 1623 | 20 | 1.62282 +--? 3 | 1767 | 21 | 1.76699 +(12 rows) + +-- test t6 with one more index +CREATE TABLE t6 ( + col1 INT, + col2 INT DEFAULT 1, + col3 BIGSERIAL, +-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 INTEGER(10, 5) DEFAULT RANDOM(), + col6 INT, + col7 TEXT +) ; +NOTICE: CREATE TABLE will create implicit sequence "t6_col3_seq" for serial column "t6.col3" +ALTER TABLE t6 ADD PRIMARY KEY (col1, col3); +NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index "t6_pkey" for table "t6" +CREATE UNIQUE INDEX u_t6_index1 ON t6(col1, col5, col6); +INSERT INTO t6 (col1) VALUES (1), (2), (3), (4), (5); +SELECT col1, col2, col3, col6, col7 FROM t6 ORDER BY col3; + col1 | col2 | col3 | col6 | col7 +------+------+------+------+------ + 1 | 1 | 1 | | + 2 | 1 | 2 | | + 3 | 1 | 3 | | + 4 | 1 | 4 | | + 5 | 1 | 5 | | +(5 rows) + +--- should not insert +INSERT INTO t6 (col1) VALUES (1), (2), (3), (4), (5) ON DUPLICATE KEY UPDATE col6 = power(col1, col2); +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +SELECT col1, col2, col3, col6, col7 FROM t6 ORDER BY col3; + col1 | col2 | col3 | col6 | col7 +------+------+------+------+------ + 1 | 1 | 1 | | + 2 | 1 | 2 | | + 3 | 1 | 3 | | + 4 | 1 | 4 | | + 5 | 1 | 5 | | +(5 rows) + +--- should update because primary key matches +INSERT INTO t6 (col1, col3) VALUES (6, 11), (6, 12); +SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col3; + col1 | col2 | col3 | col6 | col7 +------+------+------+------+------ + 6 | 1 | 11 | | + 6 | 1 | 12 | | +(2 rows) + +INSERT INTO t6 (col1) VALUES (6), (6), (6) ON DUPLICATE KEY UPDATE col2 = col1 + col3, col6 = col2 * 10; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col3; + col1 | col2 | col3 | col6 | col7 +------+------+------+------+------ + 6 | 1 | 11 | | + 6 | 1 | 12 | | +(2 rows) + +--- should update for those col6 is not null bacause they will match the unique index, +--- and insert for those col6 is null because the unique index containing null never matches, +--- also primary key will not match +--- be ware the sequence column of the inserted row will jump n step, where n is the count of the not null rows, +--- because those sequence have to be generated during the unique index join stage. +INSERT INTO t6 (col1, col5, col6) + (SELECT col1, col5, col6 FROM t6 WHERE col1 = 6) + ON DUPLICATE KEY UPDATE + col7 = col2 + 1; +SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col1, col3, col6, col7; + col1 | col2 | col3 | col6 | col7 +------+------+------+------+------ + 6 | 1 | 6 | | + 6 | 1 | 7 | | + 6 | 1 | 11 | | + 6 | 1 | 12 | | +(4 rows) + +--- should update because unique index and primary key both match +INSERT INTO t6 (col1, col3, col5, col6) + (SELECT col1, col3, col5, col6 FROM t6 WHERE col1 = 6 AND col6 IS NOT NULL) + ON DUPLICATE KEY UPDATE + col7 = col7 * 10; +SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col1, col3, col6, col7; + col1 | col2 | col3 | col6 | col7 +------+------+------+------+------ + 6 | 1 | 6 | | + 6 | 1 | 7 | | + 6 | 1 | 11 | | + 6 | 1 | 12 | | +(4 rows) + +--- insert when col3 = 17 because constaints does not match, +--- but update when col3 = 18 because 18 has been inserted and will cause a match +INSERT INTO t6 (col1, col3, col5, col6) VALUES (7, 18, 100, 100); +SELECT * FROM t6 WHERE col3 > 16 ORDER BY col1, col3, col6, col7; + col1 | col2 | col3 | col5 | col6 | col7 +------+------+------+-----------+------+------ + 7 | 1 | 18 | 100.00000 | 100 | +(1 row) + +INSERT INTO t6 (col1, col5, col6) VALUES (7, 10, 10), (7, 100, 100) ON DUPLICATE KEY UPDATE + col7 = col3 * 100; +SELECT * FROM t6 WHERE col1 = 7 ORDER BY col1, col3, col6, col7; + col1 | col2 | col3 | col5 | col6 | col7 +------+------+------+-----------+------+------ + 7 | 1 | 8 | 10.00000 | 10 | + 7 | 1 | 18 | 100.00000 | 100 | 1800 +(2 rows) + +DROP SCHEMA test_insert_update_003 CASCADE; +NOTICE: drop cascades to 3 other objects +DETAIL: drop cascades to table t4 +drop cascades to table t5 +drop cascades to table t6 diff --git a/src/test/regress/expected/upsert_008.out b/src/test/regress/expected/upsert_008.out new file mode 100644 index 000000000..1d241be3b --- /dev/null +++ b/src/test/regress/expected/upsert_008.out @@ -0,0 +1,466 @@ +-- +-- INSERT UPDATE, test explain command, comes from merge_explain and merge_explain_pretty +-- +-- initial +CREATE SCHEMA test_insert_update_008; +SET current_schema = test_insert_update_008; +-- enable_upsert_to_merge must is off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +CREATE TABLE products_base +( +product_id INTEGER DEFAULT 0, +product_name VARCHAR(60) DEFAULT 'null', +category VARCHAR(60) DEFAULT 'unknown', +total INTEGER DEFAULT '0' +); +INSERT INTO products_base VALUES (1501, 'vivitar 35mm', 'electrncs', 100); +INSERT INTO products_base VALUES (1502, 'olympus is50', 'electrncs', 100); +INSERT INTO products_base VALUES (1600, 'play gym', 'toys', 100); +INSERT INTO products_base VALUES (1601, 'lamaze', 'toys', 100); +INSERT INTO products_base VALUES (1666, 'harry potter', 'dvd', 100); +CREATE TABLE newproducts_base +( +product_id INTEGER DEFAULT 0, +product_name VARCHAR(60) DEFAULT 'null', +category VARCHAR(60) DEFAULT 'unknown', +total INTEGER DEFAULT '0' +); +INSERT INTO newproducts_base VALUES (1502, 'olympus camera', 'electrncs', 200); +INSERT INTO newproducts_base VALUES (1601, 'lamaze', 'toys', 200); +INSERT INTO newproducts_base VALUES (1666, 'harry potter', 'toys', 200); +INSERT INTO newproducts_base VALUES (1700, 'wait interface', 'books', 200); +ANALYZE products_base; +ANALYZE newproducts_base; +-- +-- row table +-- +CREATE TABLE products_row +( +product_id INTEGER DEFAULT 0 PRIMARY KEY, +product_name VARCHAR(60) DEFAULT 'null', +category VARCHAR(60) DEFAULT 'unknown', +total INTEGER DEFAULT '0' +); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "products_row_pkey" for table "products_row" +CREATE TABLE newproducts_row +( +product_id INTEGER DEFAULT 0 PRIMARY KEY, +product_name VARCHAR(60) DEFAULT 'null', +category VARCHAR(60) DEFAULT 'unknown', +total INTEGER DEFAULT '0' +); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "newproducts_row_pkey" for table "newproducts_row" +INSERT INTO products_row SELECT * FROM products_base; +INSERT INTO newproducts_row SELECT * FROM newproducts_base; +ANALYZE products_row; +ANALYZE newproducts_row; +SET explain_perf_mode = normal; +-- explain verbose +EXPLAIN (VERBOSE on, COSTS off) +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------- + Insert on test_insert_update_008.products_row + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: products_row_pkey + -> Seq Scan on test_insert_update_008.newproducts_row + Output: newproducts_row.product_id, newproducts_row.product_name, newproducts_row.category, newproducts_row.total +(5 rows) + +EXPLAIN (VERBOSE on, COSTS off) +INSERT INTO products_row + SELECT newproducts_row.product_id, + newproducts_row.product_name, + newproducts_row.category, + newproducts_row.total + FROM newproducts_row, products_row + WHERE products_row.total + newproducts_row.total < 1000 + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Insert on test_insert_update_008.products_row + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: products_row_pkey + -> Nested Loop + Output: newproducts_row.product_id, newproducts_row.product_name, newproducts_row.category, newproducts_row.total + Join Filter: ((test_insert_update_008.products_row.total + newproducts_row.total) < 1000) + -> Seq Scan on test_insert_update_008.products_row + Output: test_insert_update_008.products_row.product_id, test_insert_update_008.products_row.product_name, test_insert_update_008.products_row.category, test_insert_update_008.products_row.total + -> Materialize + Output: newproducts_row.product_id, newproducts_row.product_name, newproducts_row.category, newproducts_row.total + -> Seq Scan on test_insert_update_008.newproducts_row + Output: newproducts_row.product_id, newproducts_row.product_name, newproducts_row.category, newproducts_row.total +(12 rows) + +EXPLAIN (VERBOSE on, COSTS off) +INSERT INTO products_row + SELECT product_id, product_name, category, total + FROM newproducts_row WHERE product_id IS NOT NULL AND product_name IS NOT NULL + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------- + Insert on test_insert_update_008.products_row + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: products_row_pkey + -> Seq Scan on test_insert_update_008.newproducts_row + Output: newproducts_row.product_id, newproducts_row.product_name, newproducts_row.category, newproducts_row.total + Filter: ((newproducts_row.product_id IS NOT NULL) AND (newproducts_row.product_name IS NOT NULL)) +(6 rows) + +-- explain analyze +BEGIN; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; + QUERY PLAN +----------------------------------------------------------- + Insert on products_row (actual rows=4 loops=1) + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: products_row_pkey + -> Seq Scan on newproducts_row (actual rows=4 loops=1) +--? Total runtime: .* ms +(5 rows) + +ROLLBACK; +-- explain performance +\o insert_update_explain.txt +BEGIN; +EXPLAIN PERFORMANCE +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; +ROLLBACK; +\o +-- explain analyze +BEGIN; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; + QUERY PLAN +----------------------------------------------------------- + Insert on products_row (actual rows=4 loops=1) + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: products_row_pkey + -> Seq Scan on newproducts_row (actual rows=4 loops=1) +--? Total runtime: .* ms +(5 rows) + +ROLLBACK; +-- pretty mode performance +SET explain_perf_mode = pretty; +-- explain verbose +EXPLAIN (VERBOSE on, COSTS off) +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------- + Insert on test_insert_update_008.products_row + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: products_row_pkey + -> Seq Scan on test_insert_update_008.newproducts_row + Output: newproducts_row.product_id, newproducts_row.product_name, newproducts_row.category, newproducts_row.total +(5 rows) + +-- explain analyze +BEGIN; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; + QUERY PLAN +----------------------------------------------------------- + Insert on products_row (actual rows=4 loops=1) + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: products_row_pkey + -> Seq Scan on newproducts_row (actual rows=4 loops=1) +--? Total runtime: .* ms +(5 rows) + +ROLLBACK; +-- explain analyze +BEGIN; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; + QUERY PLAN +----------------------------------------------------------- + Insert on products_row (actual rows=4 loops=1) + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: products_row_pkey + -> Seq Scan on newproducts_row (actual rows=4 loops=1) +--? Total runtime: .* ms +(5 rows) + +ROLLBACK; +-- explain performance +\o insert_update_explain_pretty.txt +BEGIN; +EXPLAIN PERFORMANCE +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; +ROLLBACK; +SET explain_perf_mode = run; +BEGIN; +EXPLAIN PERFORMANCE +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; +ROLLBACK; +SET explain_perf_mode = summary; +BEGIN; +EXPLAIN PERFORMANCE +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; +ROLLBACK; +\o +CREATE TABLE item +( + a INT DEFAULT 3, + item_id NUMERIC(18,10), + item_name VARCHAR(100), + item_level NUMERIC(39,0), + item_desc VARCHAR(250), + item_subclass_cd VARCHAR(50), + item_type_cd VARCHAR(50), + inventory_ind CHAR(300), + vendor_party_id SMALLINT, + commodity_cd VARCHAR(50), + brand_cd VARCHAR(50), + item_available CHAR(100), + CONSTRAINT u_item_index UNIQUE (item_subclass_cd, vendor_party_id) +) +PARTITION BY RANGE (vendor_party_id) +( +PARTITION item_1 VALUES LESS THAN (0), +PARTITION item_2 VALUES LESS THAN (1), +PARTITION item_3 VALUES LESS THAN (2), +PARTITION item_4 VALUES LESS THAN (3), +PARTITION item_5 VALUES LESS THAN (6), +PARTITION item_6 VALUES LESS THAN (8), +PARTITION item_7 VALUES LESS THAN (10), +PARTITION item_8 VALUES LESS THAN (15), +PARTITION item_9 VALUES LESS THAN (MAXVALUE) +) ENABLE ROW MOVEMENT; +NOTICE: CREATE TABLE / UNIQUE will create implicit index "u_item_index" for table "item" +CREATE TABLE region +( + a INT DEFAULT 8, + region_cd VARCHAR(50), + region_name VARCHAR(100), + division_cd VARCHAR(50), + region_mgr_associate_id number(18,9) +); +CREATE TABLE associate_benefit_expense +( + a INT DEFAULT 44, + period_end_dt DATE, + associate_expns_type_cd VARCHAR(50), + associate_party_id INTEGER, + benefit_hours_qty decimal(38,11), + benefit_cost_amt number(38,4) +) +PARTITION BY RANGE (associate_expns_type_cd) +( +PARTITION associate_benefit_expense_1 VALUES LESS THAN ('B'), +PARTITION associate_benefit_expense_2 VALUES LESS THAN ('E'), +PARTITION associate_benefit_expense_3 VALUES LESS THAN ('G'), +PARTITION associate_benefit_expense_4 VALUES LESS THAN ('I'), +PARTITION associate_benefit_expense_5 VALUES LESS THAN ('L'), +PARTITION associate_benefit_expense_6 VALUES LESS THAN ('N'), +PARTITION associate_benefit_expense_7 VALUES LESS THAN ('P'), +PARTITION associate_benefit_expense_8 VALUES LESS THAN ('Q'), +PARTITION associate_benefit_expense_9 VALUES LESS THAN ('R'), +PARTITION associate_benefit_expense_10 VALUES LESS THAN ('T'), +PARTITION associate_benefit_expense_11 VALUES LESS THAN ('U'), +PARTITION associate_benefit_expense_12 VALUES LESS THAN ('V'), +PARTITION associate_benefit_expense_13 VALUES LESS THAN (MAXVALUE) +) ENABLE ROW MOVEMENT; +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (0.12, ' ' , 'A' , NULL, 'TGK' , 'A' , 2, 'A' , 'A' , 'Y'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (1.3, 'B' , NULL, 'B' , 'B' , NULL, 1, 'B' , NULL , 'N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (2.23, 'C' , 'C' , NULL, 'C' , 'C' , 2, 'C' , 'C' , 'N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (3.33, 'D' , 'D' , 'PT' , NULL, 'D' , 3, 'D' , 'D' , 'N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (4.98, ' ' , NULL, 'E' , 'E' , 'E' , 4, 'E' , 'E' , 'Y'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (5.01, NULL, 'F' , ' ' , 'F' , 'F' , 5, 'F' , 'F' , 'N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (6, 'G' , 'G' , 'G' , '_D' , 'G' , 6, 'G' , NULL ,'N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (0.7, NULL, NULL, NULL, 'H' , 'H' , 7, NULL, 'G' , 'Y'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (0.08, 'I' , ' ' , ' T ' , NULL, 'I' , 8, 'I' , '' , 'N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (9.12, ' ' , 'J' , ' PP' , 'J' , 'J' , 9, 'J' , NULL , 'Y'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (10.10, NULL, ' ' , 'A' , 'A' , 'A' , 2, NULL, 'A','Y'); +--INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (11.11, 'B' , 'B' , 'B' , 'BCDAA' , NULL, 1, 'B' , 'B','N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (12.02, 'D' , NULL, NULL, 'C' , 'C' , 2, 'C' , 'C','N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (13.99, NULL, ' ' , 'D' , 'D' , 'D' , 3, 'D' , 'D','Y'); +--INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (14, 'G' , 'E' , 'E' , NULL, 'E' , 4, 'E' , 'E','N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (15, 'F' , ' ' , 'C' , 'CLEANING' , 'F' , 5, 'F' , 'F','Y'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (16, '' , 'Z' , NULL, 'G' , 'G' , 6, 'G' , NULL,'N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (17, NULL, '' , ' PAPER' , 'H' , '' , 7, NULL, NULL,'Y'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (19, ' ' , 'B' , '' , '' , 'I' , 8, 'I' , NULL,'N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (20 , 'A' , 'J' , 'J' , 'J' , NULL, 9, 'J' , 'G','Y'); +/*--REGION--*/ +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('A', 'A ', 'A', 0.123433); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('B', 'B', 'B', NULL); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('C', 'C', 'C', 2.232008908); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('D', ' DD', 'D', 3.878789); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('E', 'A', 'E', 4.89060603); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('F', 'F', 'F', 5.82703827); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('G', 'G', 'TTT', NULL); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('H', 'H', 'G', 7.3829083); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('I', 'C', 'M', 8.983989); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('J', 'J', 'G', NULL); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('K', ' ', 'C', 2.232008908); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('L', 'D', 'X', 3.878789); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('M', 'TTTTTT ', 'D' , 4.89060603); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('N', 'G' , 'B' , NULL); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('O' , 'G', 'F', 6.6703972); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1970-01-01', 'A', 5, 0.5 , 0.5); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1973-01-01', 'B', 1, NULL, 1.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1976-01-01', 'C', 2, 2.0 , NULL); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1979-01-01', 'D', 3, 3.0 , 3.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1982-01-01', 'E', 4, 4.0 , 4.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1985-01-01', 'F', 5, 5.0 , 5.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1988-01-01', 'F', 6, NULL, 6.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1991-01-01', 'G', 6, NULL, NULL); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1994-01-01', 'G', 15, 8.0 , 8.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1997-01-01', 'G', 16, 9.0 , 9.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1983-01-03', 'I', 14, 4.0 , 4.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1984-01-01', 'GO', 15, 5.0 , NULL); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1985-05-01', 'I', 16, 6.0 , 6.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1990-01-01', 'TTT', 16, NULL, 7.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1992-02-01', 'A', 15, 8.0 , 8.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1997-02-01', 'G', 17, 9.0 , NULL); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1997-05-01', 'G' , 17, 9.0 , NULL); +ANALYZE item; +ANALYZE region; +ANALYZE associate_benefit_expense; +EXPLAIN (VERBOSE ON, COSTS OFF) +INSERT INTO item (item_level, item_subclass_cd, item_desc, vendor_party_id) + SELECT Table_001.REGION_MGR_ASSOCIATE_ID Column_003, + Table_002.associate_expns_type_cd Column_004, + CAST(Table_001.region_name AS VARCHAR) Column_005, + 10 Column_006 +-- 'o' Column_007, +-- 'F' Column_008, +-- pg_client_encoding() Column_009 + FROM region Table_001, associate_benefit_expense Table_002 +ON DUPLICATE KEY UPDATE item_level = -1000; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Insert on test_insert_update_008.item + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: u_item_index + -> Nested Loop + Output: 3, NULL::numeric, NULL::character varying, table_001.region_mgr_associate_id, (table_001.region_name)::character varying(250), table_002.associate_expns_type_cd, NULL::character varying, NULL::bpchar, 10::smallint, NULL::character varying, NULL::character varying, NULL::bpchar + -> Partition Iterator + Output: table_002.a, table_002.period_end_dt, table_002.associate_expns_type_cd, table_002.associate_party_id, table_002.benefit_hours_qty, table_002.benefit_cost_amt + Iterations: 13 + -> Partitioned Seq Scan on test_insert_update_008.associate_benefit_expense table_002 + Output: table_002.a, table_002.period_end_dt, table_002.associate_expns_type_cd, table_002.associate_party_id, table_002.benefit_hours_qty, table_002.benefit_cost_amt + Selected Partitions: 1..13 + -> Materialize + Output: table_001.region_mgr_associate_id, table_001.region_name + -> Seq Scan on test_insert_update_008.region table_001 + Output: table_001.region_mgr_associate_id, table_001.region_name +(15 rows) + +EXPLAIN (VERBOSE ON, COSTS OFF) +INSERT INTO products_row VALUES(100) + ON DUPLICATE KEY UPDATE total=100; + QUERY PLAN +----------------------------------------------------------------------------------------- + Insert on test_insert_update_008.products_row + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: products_row_pkey + -> Result + Output: 100, 'null'::character varying(60), 'unknown'::character varying(60), 0 +(5 rows) + +SET enable_light_proxy=off; +EXPLAIN (VERBOSE ON, COSTS OFF) +INSERT INTO products_row VALUES(100) + ON DUPLICATE KEY UPDATE total=100; + QUERY PLAN +----------------------------------------------------------------------------------------- + Insert on test_insert_update_008.products_row + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: products_row_pkey + -> Result + Output: 100, 'null'::character varying(60), 'unknown'::character varying(60), 0 +(5 rows) + +EXPLAIN (VERBOSE ON, COSTS OFF) +INSERT INTO products_row VALUES(100) + ON DUPLICATE KEY UPDATE total=100; + QUERY PLAN +----------------------------------------------------------------------------------------- + Insert on test_insert_update_008.products_row + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: products_row_pkey + -> Result + Output: 100, 'null'::character varying(60), 'unknown'::character varying(60), 0 +(5 rows) + +EXPLAIN (VERBOSE ON, COSTS OFF) +INSERT INTO products_row VALUES(100) + ON DUPLICATE KEY UPDATE total=100; + QUERY PLAN +----------------------------------------------------------------------------------------- + Insert on test_insert_update_008.products_row + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: products_row_pkey + -> Result + Output: 100, 'null'::character varying(60), 'unknown'::character varying(60), 0 +(5 rows) + +RESET enable_light_proxy; +DROP SCHEMA test_insert_update_008 CASCADE; +NOTICE: drop cascades to 7 other objects +DETAIL: drop cascades to table products_base +drop cascades to table newproducts_base +drop cascades to table products_row +drop cascades to table newproducts_row +drop cascades to table item +drop cascades to table region +drop cascades to table associate_benefit_expense diff --git a/src/test/regress/expected/upsert_009.out b/src/test/regress/expected/upsert_009.out new file mode 100644 index 000000000..ab5bad7ad --- /dev/null +++ b/src/test/regress/expected/upsert_009.out @@ -0,0 +1,548 @@ +DROP SCHEMA test_insert_update_009 CASCADE; +ERROR: schema "test_insert_update_009" does not exist +CREATE SCHEMA test_insert_update_009; +SET CURRENT_SCHEMA TO test_insert_update_009; +SET enable_light_proxy=off; +-- enable_upsert_to_merge must is off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +-- test t1 with no index +CREATE TABLE t1 ( + col1 INT, + col2 INT, + col3 INT DEFAULT 1, +-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 BIGSERIAL +) ; +NOTICE: CREATE TABLE will create implicit sequence "t1_col5_seq" for serial column "t1.col5" +--- distribute key are not allowed to update +INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE col1 = 3; +--- should always insert +INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE col2 = 3; +INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE t1.col2 = 4; +--- appoint column list in insert clause, should always insert +INSERT INTO t1(col1, col3) VALUES (1, 3) ON DUPLICATE KEY UPDATE col2 = 5; +INSERT INTO t1(col1, col3) VALUES (1, 3) ON DUPLICATE KEY UPDATE t1.col2 = 6; +--- multiple rows, should always insert +INSERT INTO t1 VALUES (2, 1), (2, 1) ON DUPLICATE KEY UPDATE col2 = 7, col3 = 7; +SELECT * FROM t1 ORDER BY col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | 2 | 1 | 1 + 1 | 2 | 1 | 2 + 1 | 2 | 1 | 3 + 1 | | 3 | 4 + 1 | | 3 | 5 + 2 | 1 | 1 | 6 + 2 | 1 | 1 | 7 +(7 rows) + +--- test union, should insert +INSERT INTO t1 (col1, col2) + SELECT * FROM + (SELECT col1, col2 FROM t1 + UNION + SELECT col1, col3 FROM t1) AS union_table + ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3); +SELECT col1, col2, col3 FROM t1 WHERE col5 > 6 ORDER BY col1, col2; + col1 | col2 | col3 +------+------+------ + 1 | 1 | 1 + 1 | 2 | 1 + 1 | 3 | 1 + 1 | | 1 + 2 | 1 | 1 + 2 | 1 | 1 +(6 rows) + +--- test subquery, should insert +INSERT INTO t1 + (SELECT col1 || col2 || '00' FROM t1 ORDER BY col5) + ON DUPLICATE KEY UPDATE col3 = col1 * 100; +SELECT col1, col2, col3 FROM t1 WHERE col1 >= 100 ORDER BY col1; + col1 | col2 | col3 +------+------+------ + 100 | | 1 + 100 | | 1 + 100 | | 1 + 1100 | | 1 + 1200 | | 1 + 1200 | | 1 + 1200 | | 1 + 1200 | | 1 + 1300 | | 1 + 2100 | | 1 + 2100 | | 1 + 2100 | | 1 +(12 rows) + +-- test t2 with one primary key +CREATE TABLE t2 ( + col1 INT, + col2 INT PRIMARY KEY, + col3 INT DEFAULT 1, + col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 BIGSERIAL +) ; +NOTICE: CREATE TABLE will create implicit sequence "t2_col5_seq" for serial column "t2.col5" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t2_pkey" for table "t2" +--- distribute key are not allowed to update +INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE col2 = 3; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE t2.col2 = 3; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +INSERT INTO t2 (col2, col3, col4, col5) + VALUES (10, 10, CURRENT_TIMESTAMP(0), 10), + (20, 20, CURRENT_TIMESTAMP(1), 20), + (30, 30, CURRENT_TIMESTAMP(2), 30) + ON DUPLICATE KEY UPDATE + col1 = 100, + col2 = 100, + col3 = 100, + col4 = '2019-08-09'::TIMESTAMP, + col5 = 100; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +--- should insert +INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 30; +INSERT INTO t2 VALUES (2, 2) ON DUPLICATE KEY UPDATE t2.col1 = 40; +INSERT INTO t2 VALUES (3, 3) ON DUPLICATE KEY UPDATE col1 = col1 * 2; +INSERT INTO t2 VALUES (4, 4) ON DUPLICATE KEY UPDATE t2.col1 = t2.col1 * 2 ; +INSERT INTO t2 VALUES (5, 5) ON DUPLICATE KEY UPDATE col1 = col2 + 1; +INSERT INTO t2 VALUES (6, 6) ON DUPLICATE KEY UPDATE t2.col1 = t2.col2 + 1; +INSERT INTO t2 VALUES (7, 7) ON DUPLICATE KEY UPDATE col1 = extract(dow from col4) + 10; +INSERT INTO t2 VALUES (8, 8) ON DUPLICATE KEY UPDATE t2.col1 = extract(century from col4) * 100 + extract(isodow from col4); +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | 1 | 1 | 1 + 2 | 2 | 1 | 2 + 3 | 3 | 1 | 3 + 4 | 4 | 1 | 4 + 5 | 5 | 1 | 5 + 6 | 6 | 1 | 6 + 7 | 7 | 1 | 7 + 8 | 8 | 1 | 8 +(8 rows) + +--- should update +INSERT INTO t2 VALUES (3, 1) ON DUPLICATE KEY UPDATE col1 = 30, col3 = col5 + 1; +INSERT INTO t2 VALUES (4, 2) ON DUPLICATE KEY UPDATE t2.col1 = 40, t2.col3 = t2.col5; +INSERT INTO t2 VALUES (3, 3), (4, 4) ON DUPLICATE KEY UPDATE col3 = t2.col5 + 1; +INSERT INTO t2 VALUES (5, 5) ON DUPLICATE KEY UPDATE col1 = extract(dow from col4) + 10; +INSERT INTO t2 VALUES (6, 6) ON DUPLICATE KEY UPDATE t2.col1 = extract(century from col4) * 100 + extract(isodow from col4); +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 30 | 1 | 2 | 1 + 40 | 2 | 2 | 2 + 3 | 3 | 4 | 3 + 4 | 4 | 5 | 4 +--?.*| 5 | 1 | 5 +--?.*| 6 | 1 | 6 + 7 | 7 | 1 | 7 + 8 | 8 | 1 | 8 +(8 rows) + +-- primary key are not allowed to be null +INSERT INTO t2 (col1) VALUES (10) ON DUPLICATE KEY UPDATE col1 = 10; +ERROR: null value in column "col2" violates not-null constraint +--? +--- appoint column list in insert clause +---- should insert +INSERT INTO t2 (col2, col3) VALUES (9, 9) ON DUPLICATE KEY UPDATE col1 = 90; +INSERT INTO t2 (col2, col3, col4, col5) + VALUES (10, 10, CURRENT_TIMESTAMP(0), 10), + (20, 20, CURRENT_TIMESTAMP(1), 20), + (30, 30, CURRENT_TIMESTAMP(2), 30) + ON DUPLICATE KEY UPDATE + col1 = 100, + col3 = 100, + col4 = '2019-08-20'::TIMESTAMP, + col5 = 100; +SELECT * FROM t2 ORDER BY col5; + col1 | col2 | col3 | col4 | col5 +------+------+------+---------------------------------+------ +--? 30 | 1 | 2 | .* | 1 +--? 40 | 2 | 2 | .* | 2 +--? 3 | 3 | 4 | .* | 3 +--? 4 | 4 | 5 | .* | 4 +--? .* | 5 | 1 | .* | 5 +--? .* | 6 | 1 | .* | 6 +--? 7 | 7 | 1 | .* | 7 +--? 8 | 8 | 1 | .* | 8 +--? | 9 | 9 | .* | 10 +--? | 10 | 10 | .* | 10 +--? | 20 | 20 | .* | 20 +--? | 30 | 30 | .* | 30 +(12 rows) + +---- should update +INSERT INTO t2 (col2, col3) VALUES (9, 9) ON DUPLICATE KEY UPDATE col1 = 90; +INSERT INTO t2 (col2, col3, col4, col5) + VALUES (10, 10, CURRENT_TIMESTAMP(0), 10), + (20, 20, CURRENT_TIMESTAMP(1), 20), + (30, 30, CURRENT_TIMESTAMP(2), 30) + ON DUPLICATE KEY UPDATE + col1 = 100, + col3 = 100, + col4 = '2019-08-20'::TIMESTAMP, + col5 = 100; +SELECT * FROM t2 ORDER BY col5, col2; + col1 | col2 | col3 | col4 | col5 +------+------+------+---------------------------------+------ +--? 30 | 1 | 2 | .* | 1 +--? 40 | 2 | 2 | .* | 2 +--? 3 | 3 | 4 | .* | 3 +--? 4 | 4 | 5 | .* | 4 +--? .* | 5 | 1 | .* | 5 +--? .* | 6 | 1 | .* | 6 +--? 7 | 7 | 1 | .* | 7 +--? 8 | 8 | 1 | .* | 8 +--? 90 | 9 | 9 | .* | 10 + 100 | 10 | 100 | Tue Aug 20 00:00:00 2019 | 100 + 100 | 20 | 100 | Tue Aug 20 00:00:00 2019 | 100 + 100 | 30 | 100 | Tue Aug 20 00:00:00 2019 | 100 +(12 rows) + +--- test subquery +---- should insert +INSERT INTO t2 + (SELECT col1 * 1000, col2 * 1000 + 1 FROM t2 ORDER BY col5 LIMIT 2) + ON DUPLICATE KEY UPDATE col3 = col2 + 1; +INSERT INTO t2 (col2, col3) + (SELECT col1 * 1000 + 2, col2 * 1000 FROM t2 ORDER BY col5 LIMIT 2 OFFSET 1) + ON DUPLICATE KEY UPDATE col3 = col2 + 1; +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2; + col1 | col2 | col3 | col5 +-------+-------+------+------ + 30 | 1 | 2 | 1 + 40 | 2 | 2 | 2 + 3 | 3 | 4 | 3 + 4 | 4 | 5 | 4 +--? .* | 5 | 1 | 5 +--? .* | 6 | 1 | 6 + 7 | 7 | 1 | 7 + 8 | 8 | 1 | 8 + 90 | 9 | 9 | 16 +--? 30000 | 1001 | 1 | 11 +--? 40000 | 2001 | 1 | 12 +--? | 40002 | 2000 | 13 +--? | 3002 | 3000 | 14 + 100 | 10 | 100 | 100 + 100 | 20 | 100 | 100 + 100 | 30 | 100 | 100 +(16 rows) + +---- should update +INSERT INTO t2 + (SELECT col1 * 1000, col2 * 1000 + 1 FROM t2 ORDER BY col5 LIMIT 2) + ON DUPLICATE KEY UPDATE col3 = col2 + 1; +INSERT INTO t2 (col2, col3) + (SELECT col1 * 1000 + 2, col2 * 1000 FROM t2 ORDER BY col5 LIMIT 2 OFFSET 1) + ON DUPLICATE KEY UPDATE col3 = col2 + 1; +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2; + col1 | col2 | col3 | col5 +-------+-------+-------+------ + 30 | 1 | 2 | 1 + 40 | 2 | 2 | 2 + 3 | 3 | 4 | 3 + 4 | 4 | 5 | 4 +--? 2 | 5 | 1 | 5 +--? 2101 | 6 | 1 | 6 + 7 | 7 | 1 | 7 + 8 | 8 | 1 | 8 + 90 | 9 | 9 | 16 +--? 30000 | 1001 | 1002 | 11 +--? 40000 | 2001 | 2002 | 12 +--? | 40002 | 40003 | 13 +--? | 3002 | 3003 | 14 + 100 | 10 | 100 | 100 + 100 | 20 | 100 | 100 + 100 | 30 | 100 | 100 +(16 rows) + +--- test union, some insert some update +SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT col1, col3 FROM t1 ORDER BY 1, 2; + col1 | col2 +------+------ + 1 | 1 + 1 | 2 + 1 | 3 + 2 | 1 + 100 | 1 + 1100 | 1 + 1200 | 1 + 1300 | 1 + 2100 | 1 +(9 rows) + +INSERT INTO t2 (col1, col2) + SELECT * FROM + (SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT col1, col3 FROM t1) AS union_table + ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3); +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2; + col1 | col2 | col3 | col5 +-------+-------+-------+------ + 30 | 1 | 219 | 1 + 40 | 2 | 44 | 2 + 3 | 3 | 10 | 3 + 4 | 4 | 5 | 4 +--? .* | 5 | 1 | 5 +--? .* | 6 | 1 | 6 + 7 | 7 | 1 | 7 + 8 | 8 | 1 | 8 + 90 | 9 | 9 | 16 +--? 30000 | 1001 | 1002 | 11 +--? 40000 | 2001 | 2002 | 12 +--? | 40002 | 40003 | 13 +--? | 3002 | 3003 | 14 + 100 | 10 | 100 | 100 + 100 | 20 | 100 | 100 + 100 | 30 | 100 | 100 +(16 rows) + +INSERT INTO t2 (col1, col2) + SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT col1, col3 FROM t1 + ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3); +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2; + col1 | col2 | col3 | col5 +-------+-------+-------+------ + 30 | 1 | 436 | 1 + 40 | 2 | 86 | 2 + 3 | 3 | 16 | 3 + 4 | 4 | 5 | 4 +--? 15 | 5 | 1 | 5 +--? 2105 | 6 | 1 | 6 + 7 | 7 | 1 | 7 + 8 | 8 | 1 | 8 + 90 | 9 | 9 | 16 +--? 30000 | 1001 | 1002 | 11 +--? 40000 | 2001 | 2002 | 12 +--? | 40002 | 40003 | 13 +--? | 3002 | 3003 | 14 + 100 | 10 | 100 | 100 + 100 | 20 | 100 | 100 + 100 | 30 | 100 | 100 +(16 rows) + +reset behavior_compat_options; +-- test INTERSECT, should update +(SELECT col1, col1 + col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT 1, 2) + INTERSECT + SELECT col1, col3 FROM t1; + col1 | ?column? +------+---------- + 1 | 3 +(1 row) + +INSERT INTO t2 (col1, col2) + (SELECT col1, col1 + col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT 1, 2) + INTERSECT + SELECT col1, col3 FROM t1 + ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3); +SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 3 ORDER BY col5, col2; + col1 | col2 | col3 | col5 +------+------+------+------ + 3 | 3 | 22 | 3 +(1 row) + +-- test EXCEPT, should update +SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + EXCEPT + (SELECT col1, col3 FROM t1 + UNION + SELECT NULL, NULL); + col1 | col2 +------+------ + 1 | 2 +(1 row) + +INSERT INTO t2 (col1, col2) + SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + EXCEPT + (SELECT col1, col3 FROM t1 + UNION + SELECT NULL, NULL) + ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3); +SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 2 ORDER BY col5, col2; + col1 | col2 | col3 | col5 +------+------+------+------ + 40 | 2 | 128 | 2 +(1 row) + +-- test unique index with not default value +ALTER TABLE t2 DROP CONSTRAINT t2_pkey; +CREATE UNIQUE INDEX t2_u_index ON t2(col2, col5); +SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + EXCEPT + (SELECT col1, col3 FROM t1 + UNION + SELECT NULL, NULL); + col1 | col2 +------+------ + 1 | 2 +(1 row) + +INSERT INTO t2 + SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + EXCEPT + (SELECT col1, col3 FROM t1 + UNION + SELECT NULL, NULL) + ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3); +SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 2 ORDER BY col5, col2; + col1 | col2 | col3 | col5 +------+------+------+------ + 40 | 2 | 128 | 2 + 1 | 2 | 1 | 46 +(2 rows) + +-- test t3 with one primary index with two columns +CREATE TABLE t3 ( + col1 INT, + col2 INT, + col3 INT DEFAULT 1, +-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 BIGSERIAL, + PRIMARY KEY (col2, col3) +) ; +NOTICE: CREATE TABLE will create implicit sequence "t3_col5_seq" for serial column "t3.col5" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t3_pkey" for table "t3" +--- column referenced by primary key are not allowed to update +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col3 = 3; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +--- should insert when not contains primary key and not all primary key referred columns have default value. +--- but will fail cause primary key should not be null +INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2; +ERROR: null value in column "col2" violates not-null constraint +--?.* +--- should insert +--- (SEQUENCE BUG: the serial column will starts from 2 since the above statement has applied for a sequence) +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20; +SELECT * FROM t3 order by col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | 1 | 1 | 2 + 2 | 2 | 2 | 3 +(2 rows) + +--- should update +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20; +SELECT * FROM t3 order by col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 10 | 1 | 1 | 2 + 2 | 2 | 2 | 20 +(2 rows) + +--- test subquery +---- should insert +INSERT INTO t3 (col2, col3) (SELECT max(col2) + 1, max(col3) + 1 FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +SELECT * FROM t3 order by col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 10 | 1 | 1 | 2 + | 3 | 3 | 6 + 2 | 2 | 2 | 20 +(3 rows) + +---- should update +INSERT INTO t3 (col2, col3) (SELECT max(col2), max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +SELECT * FROM t3 order by col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 10 | 1 | 1 | 2 + 6 | 3 | 3 | 6 + 2 | 2 | 2 | 20 +(3 rows) + +-- test t3 with one unique index with two columns +TRUNCATE t3; +ALTER TABLE t3 DROP CONSTRAINT t3_pkey; +ALTER TABLE t3 ALTER COLUMN col2 DROP NOT NULL; +CREATE UNIQUE INDEX t3_ukey ON t3 (col2, col3); +--- column referenced by unique key are not allowed to update +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col3 = 3; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +--- should insert cause not contains unique key and not all unique key referred columns have default value. +INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2; +INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2; +SELECT * FROM t3 order by col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | | 1 | 8 + 1 | | 1 | 9 +(2 rows) + +--- should insert +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE t3.col5 = 20; +SELECT * FROM t3 ORDER BY col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | | 1 | 8 + 1 | | 1 | 9 + 1 | 1 | 1 | 10 + 2 | 2 | 2 | 11 +(4 rows) + +--- should update +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE t3.col1 = 10; +INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20; +SELECT * FROM t3 ORDER BY col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | | 1 | 8 + 1 | | 1 | 9 + 10 | 1 | 1 | 10 + 2 | 2 | 2 | 20 +(4 rows) + +--- test subquery +---- should insert +INSERT INTO t3 (SELECT 100, NULL, max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +INSERT INTO t3 (SELECT 100, NULL, max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +INSERT INTO t3 (col2, col3) (SELECT max(col2) + 1, max(col3) + 1 FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +SELECT * FROM t3 ORDER BY col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | | 1 | 8 + 1 | | 1 | 9 + 10 | 1 | 1 | 10 + 100 | | 2 | 14 + 100 | | 2 | 15 + | 3 | 3 | 16 + 2 | 2 | 2 | 20 +(7 rows) + +---- should update +INSERT INTO t3 (col2, col3) (SELECT max(col2), max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +SELECT * FROM t3 ORDER BY col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | | 1 | 8 + 1 | | 1 | 9 + 10 | 1 | 1 | 10 + 100 | | 2 | 14 + 100 | | 2 | 15 + 6 | 3 | 3 | 16 + 2 | 2 | 2 | 20 +(7 rows) + +RESET enable_light_proxy; +DROP SCHEMA test_insert_update_009 CASCADE; +NOTICE: drop cascades to 3 other objects +DETAIL: drop cascades to table t1 +drop cascades to table t2 +drop cascades to table t3 diff --git a/src/test/regress/expected/upsert_010.out b/src/test/regress/expected/upsert_010.out new file mode 100644 index 000000000..4c506f245 --- /dev/null +++ b/src/test/regress/expected/upsert_010.out @@ -0,0 +1,313 @@ +DROP SCHEMA test_insert_update_010 CASCADE; +ERROR: schema "test_insert_update_010" does not exist +CREATE SCHEMA test_insert_update_010; +SET CURRENT_SCHEMA TO test_insert_update_010; +-- enable_upsert_to_merge must is off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +-- test t4 with one primary key with three columns +CREATE TABLE t4 ( + col1 INT, + col2 INT DEFAULT 0, + col3 INT DEFAULT 1, + col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 BIGSERIAL, + PRIMARY KEY (col2, col3, col5) +) ; +NOTICE: CREATE TABLE will create implicit sequence "t4_col5_seq" for serial column "t4.col5" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t4_pkey" for table "t4" +--- should insert +INSERT INTO t4 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t4 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t4 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col1 = 200; +INSERT INTO t4 VALUES (100, 100, 100, CURRENT_TIMESTAMP, 100) ON DUPLICATE KEY UPDATE col1 = 1000; +SELECT col1, col2, col3, col5 FROM t4 ORDER BY col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | 0 | 1 | 1 + 1 | 0 | 1 | 2 + 2 | 2 | 2 | 3 + 100 | 100 | 100 | 100 +(4 rows) + +--- should update +INSERT INTO t4 VALUES (2, 2, 2, CURRENT_TIMESTAMP, 3) ON DUPLICATE KEY UPDATE col1 = 200; +INSERT INTO t4 VALUES (100, 100, 100, CURRENT_TIMESTAMP, 100) ON DUPLICATE KEY UPDATE col1 = 1000; +SELECT col1, col2, col3, col5 FROM t4 ORDER BY col5; + col1 | col2 | col3 | col5 +------+------+------+------ + 1 | 0 | 1 | 1 + 1 | 0 | 1 | 2 + 200 | 2 | 2 | 3 + 1000 | 100 | 100 | 100 +(4 rows) + +--- error: duplicate key update on (x, x, 20) +--- this is because current version is not inplace update but merge, +--- so when the subquery contains multiple same values, it will cause duplicate insert failure. +SELECT col3, sum(col3) * 10 FROM t4 GROUP BY col3 ORDER BY 1, 2; + col3 | ?column? +------+---------- + 1 | 20 + 2 | 20 + 100 | 1000 +(3 rows) + +INSERT INTO t4 (col1, col5) + (SELECT col3, sum(col3) * 10 FROM t4 GROUP BY col3) + ON DUPLICATE KEY UPDATE col1 = 3; +-- test t5 with sequence or default column with volatile function in constaint index +CREATE TABLE t5 ( + col1 INT, + col2 INT DEFAULT 1, + col3 BIGSERIAL, +-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 INTEGER(10, 5) DEFAULT RANDOM() + 1 +) ; +NOTICE: CREATE TABLE will create implicit sequence "t5_col3_seq" for serial column "t5.col3" +-- test t5 with sequence column in constaint index +CREATE UNIQUE INDEX u_t5_index1 ON t5(col1, col3); +--- should insert +INSERT INTO t5 VALUES (1), (1), (1) ON DUPLICATE KEY UPDATE col2 = col3; +INSERT INTO t5 DEFAULT VALUES ON DUPLICATE KEY UPDATE col2 = col3; +SELECT * FROM t5 ORDER BY col3; + col1 | col2 | col3 | col5 +------+------+------+--------- +--? 1 | 1 | 1 | 1.58182 +--? 1 | 1 | 2 | 1.00814 +--? 1 | 1 | 3 | 1.50194 +--? | 1 | 4 | 1.12955 +(4 rows) + +--- should update +INSERT INTO t5 (col1, col3) VALUES (1, 1), (1, 2), (1, 3) ON DUPLICATE KEY UPDATE col5 = col2, col2 = col3 * 10; +SELECT * FROM t5 WHERE col1 = 1 ORDER BY col3; + col1 | col2 | col3 | col5 +------+------+------+--------- + 1 | 10 | 1 | 1.00000 + 1 | 20 | 2 | 1.00000 + 1 | 30 | 3 | 1.00000 +(3 rows) + +--- should some insert some update +INSERT INTO t5 (col1, col3) VALUES (2, 5), (2, 6); +SELECT col1, col2, col3 FROM t5 ORDER BY col3; + col1 | col2 | col3 +------+------+------ + 1 | 10 | 1 + 1 | 20 | 2 + 1 | 30 | 3 + | 1 | 4 + 2 | 1 | 5 + 2 | 1 | 6 +(6 rows) + +INSERT INTO t5 (col1) VALUES (2), (2), (2) ON DUPLICATE KEY UPDATE col2 = col3; +SELECT col1, col2, col3 FROM t5 ORDER BY col3; + col1 | col2 | col3 +------+------+------ + 1 | 10 | 1 + 1 | 20 | 2 + 1 | 30 | 3 + | 1 | 4 + 2 | 5 | 5 + 2 | 6 | 6 + 2 | 1 | 7 +(7 rows) + +--- should INSERT and sequence starting from 7 +INSERT INTO t5 VALUES (2), (2); +SELECT col1, col2, col3 FROM t5 ORDER BY col3; + col1 | col2 | col3 +------+------+------ + 1 | 10 | 1 + 1 | 20 | 2 + 1 | 30 | 3 + | 1 | 4 + 2 | 5 | 5 + 2 | 6 | 6 + 2 | 1 | 7 + 2 | 1 | 8 + 2 | 1 | 9 +(9 rows) + +-- test with volatile function as default column in constraint index +TRUNCATE t5; +DROP INDEX u_t5_index1; +CREATE UNIQUE INDEX u_t5_index2 ON t5(col1, col5) WHERE col1 > 2; +--- should insert +INSERT INTO t5 VALUES (3), (3), (3) ON DUPLICATE KEY UPDATE col2 = col3; +INSERT INTO t5 (col1) VALUES (4), (4), (4) ON DUPLICATE KEY UPDATE col2 = col3; +SELECT * FROM t5 ORDER BY col3; + col1 | col2 | col3 | col5 +------+------+------+--------- +--? 3 | 1 | 10 | 1.24521 +--? 3 | 1 | 11 | 1.80598 +--? 3 | 1 | 12 | 1.86845 +--? 4 | 1 | 13 | 1.65797 +--? 4 | 1 | 14 | 1.07881 +--? 4 | 1 | 15 | 1.69493 +(6 rows) + +--- should update +INSERT INTO t5 (col1, col5) SELECT col1, col5 FROM t5 where col1 = 3 ON DUPLICATE KEY UPDATE col2 = col5 * 100; +SELECT * FROM t5 ORDER BY col3; + col1 | col2 | col3 | col5 +------+------+------+--------- +--? 3 | 125 | 10 | 1.24521 +--? 3 | 181 | 11 | 1.80598 +--? 3 | 187 | 12 | 1.86845 +--? 4 | 1 | 13 | 1.65797 +--? 4 | 1 | 14 | 1.07881 +--? 4 | 1 | 15 | 1.69493 +(6 rows) + +--- test subquery +---- should insert +INSERT INTO t5 (col1, col2) SELECT col1, col2 FROM t5 ON DUPLICATE KEY UPDATE col2 = col5 * 100; +SELECT * FROM t5 ORDER BY col3; + col1 | col2 | col3 | col5 +------+------+------+--------- +--? 3 | 167 | 10 | 1.67400 +--? 3 | 132 | 11 | 1.31552 +--? 3 | 129 | 12 | 1.29423 +--? 4 | 1 | 13 | 1.44100 +--? 4 | 1 | 14 | 1.37620 +--? 4 | 1 | 15 | 1.51296 +--? 4 | 1 | 16 | 1.37516 +--? 4 | 1 | 17 | 1.21710 +--? 4 | 1 | 18 | 1.50422 +--? 3 | 132 | 19 | 1.30560 +--? 3 | 167 | 20 | 1.62282 +--? 3 | 129 | 21 | 1.76699 +(12 rows) + +---- should update +INSERT INTO t5 SELECT * FROM t5 ON DUPLICATE KEY UPDATE col2 = col5 * 1000; +SELECT * FROM t5 ORDER BY col3; + col1 | col2 | col3 | col5 +------+------+------+--------- +--? 3 | 1674 | 10 | 1.67400 +--? 3 | 1316 | 11 | 1.31552 +--? 3 | 1294 | 12 | 1.29423 +--? 4 | 1441 | 13 | 1.44100 +--? 4 | 1376 | 14 | 1.37620 +--? 4 | 1513 | 15 | 1.51296 +--? 4 | 1375 | 16 | 1.37516 +--? 4 | 1217 | 17 | 1.21710 +--? 4 | 1504 | 18 | 1.50422 +--? 3 | 1306 | 19 | 1.30560 +--? 3 | 1623 | 20 | 1.62282 +--? 3 | 1767 | 21 | 1.76699 +(12 rows) + +-- test t6 with one more index +CREATE TABLE t6 ( + col1 INT, + col2 INT DEFAULT 1, + col3 BIGSERIAL, +-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 INTEGER(10, 5) DEFAULT RANDOM(), + col6 INT, + col7 TEXT +) ; +NOTICE: CREATE TABLE will create implicit sequence "t6_col3_seq" for serial column "t6.col3" +ALTER TABLE t6 ADD PRIMARY KEY (col1, col3); +NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index "t6_pkey" for table "t6" +CREATE UNIQUE INDEX u_t6_index1 ON t6(col1, col5, col6); +INSERT INTO t6 (col1) VALUES (1), (2), (3), (4), (5); +SELECT col1, col2, col3, col6, col7 FROM t6 ORDER BY col3; + col1 | col2 | col3 | col6 | col7 +------+------+------+------+------ + 1 | 1 | 1 | | + 2 | 1 | 2 | | + 3 | 1 | 3 | | + 4 | 1 | 4 | | + 5 | 1 | 5 | | +(5 rows) + +--- should insert +INSERT INTO t6 (col1) VALUES (1), (2), (3), (4), (5) ON DUPLICATE KEY UPDATE col6 = power(col1, col2); +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +SELECT col1, col2, col3, col6, col7 FROM t6 ORDER BY col3; + col1 | col2 | col3 | col6 | col7 +------+------+------+------+------ + 1 | 1 | 1 | | + 2 | 1 | 2 | | + 3 | 1 | 3 | | + 4 | 1 | 4 | | + 5 | 1 | 5 | | +(5 rows) + +--- should update because primary key matches +INSERT INTO t6 (col1, col3) VALUES (6, 11), (6, 12); +SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col3; + col1 | col2 | col3 | col6 | col7 +------+------+------+------+------ + 6 | 1 | 11 | | + 6 | 1 | 12 | | +(2 rows) + +INSERT INTO t6 (col1) VALUES (6), (6), (6) ON DUPLICATE KEY UPDATE col2 = col1 + col3, col6 = col2 * 10; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col3; + col1 | col2 | col3 | col6 | col7 +------+------+------+------+------ + 6 | 1 | 11 | | + 6 | 1 | 12 | | +(2 rows) + +--- should update for those col6 is not null bacause they will match the unique index, +--- and insert for those col6 is null because the unique index containing null never matches, +--- also primary key will not match +--- be ware the sequence column of the inserted row will jump n step, where n is the count of the not null rows, +--- because those sequence have to be generated during the unique index join stage. +INSERT INTO t6 (col1, col5, col6) + (SELECT col1, col5, col6 FROM t6 WHERE col1 = 6) + ON DUPLICATE KEY UPDATE + col7 = col2 + 1; +SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col1, col3, col6, col7; + col1 | col2 | col3 | col6 | col7 +------+------+------+------+------ + 6 | 1 | 6 | | + 6 | 1 | 7 | | + 6 | 1 | 11 | | + 6 | 1 | 12 | | +(4 rows) + +--- should update because unique index and primary key both match +INSERT INTO t6 (col1, col3, col5, col6) + (SELECT col1, col3, col5, col6 FROM t6 WHERE col1 = 6 AND col6 IS NOT NULL) + ON DUPLICATE KEY UPDATE + col7 = col7 * 10; +SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col1, col3, col6, col7; + col1 | col2 | col3 | col6 | col7 +------+------+------+------+------ + 6 | 1 | 6 | | + 6 | 1 | 7 | | + 6 | 1 | 11 | | + 6 | 1 | 12 | | +(4 rows) + +--- insert when col3 = 17 because constaints does not match, +--- but update when col3 = 18 because 18 has been inserted and will cause a match +INSERT INTO t6 (col1, col3, col5, col6) VALUES (7, 18, 100, 100); +SELECT * FROM t6 WHERE col3 > 16 ORDER BY col1, col3, col6, col7; + col1 | col2 | col3 | col5 | col6 | col7 +------+------+------+-----------+------+------ + 7 | 1 | 18 | 100.00000 | 100 | +(1 row) + +INSERT INTO t6 (col1, col5, col6) VALUES (7, 10, 10), (7, 100, 100) ON DUPLICATE KEY UPDATE + col7 = col3 * 100; +SELECT * FROM t6 WHERE col1 = 7 ORDER BY col1, col3, col6, col7; + col1 | col2 | col3 | col5 | col6 | col7 +------+------+------+-----------+------+------ + 7 | 1 | 8 | 10.00000 | 10 | + 7 | 1 | 18 | 100.00000 | 100 | 1800 +(2 rows) + +DROP SCHEMA test_insert_update_010 CASCADE; +NOTICE: drop cascades to 3 other objects +DETAIL: drop cascades to table t4 +drop cascades to table t5 +drop cascades to table t6 diff --git a/src/test/regress/expected/upsert_clean.out b/src/test/regress/expected/upsert_clean.out new file mode 100644 index 000000000..e6157a7f1 --- /dev/null +++ b/src/test/regress/expected/upsert_clean.out @@ -0,0 +1,19 @@ +\c upsert +DROP SCHEMA upsert_test CASCADE; +NOTICE: drop cascades to 8 other objects +DETAIL: drop cascades to type upsert_test.atype +drop cascades to type upsert_test.btype +drop cascades to table upsert_test.t_grammer +drop cascades to table upsert_test.t_default +drop cascades to table upsert_test.t_data +drop cascades to table upsert_test.t_trigger +drop cascades to function upsert_test.upsert_before_func() +drop cascades to function upsert_test.upsert_after_func() +DROP SCHEMA upsert_test_unlog CASCADE; +NOTICE: drop cascades to 6 other objects +DETAIL: drop cascades to type upsert_test_unlog.atype +drop cascades to type upsert_test_unlog.btype +drop cascades to table upsert_test_unlog.t_hash_unlog_0 +drop cascades to table upsert_test_unlog.t_hash_unlog_1 +drop cascades to table upsert_test_unlog.t_rep_unlog_0 +drop cascades to table upsert_test_unlog.t_rep_unlog_1 diff --git a/src/test/regress/expected/upsert_clean_etc.out b/src/test/regress/expected/upsert_clean_etc.out new file mode 100644 index 000000000..0ddfbdc7b --- /dev/null +++ b/src/test/regress/expected/upsert_clean_etc.out @@ -0,0 +1 @@ +drop database upsert_etc; diff --git a/src/test/regress/expected/upsert_composite.out b/src/test/regress/expected/upsert_composite.out new file mode 100644 index 000000000..648d22c9e --- /dev/null +++ b/src/test/regress/expected/upsert_composite.out @@ -0,0 +1,404 @@ +-------------------------------------------------------------------------------------------- +/* + * procedure + */ +-------------------------------------------------------------------------------------------- +\c upsert; +SET CURRENT_SCHEMA TO upsert_test_procedure; +-- enable_upsert_to_merge must be off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +SHOW enable_upsert_to_merge; + enable_upsert_to_merge +------------------------ + off +(1 row) + +drop table IF EXISTS t_proc; +NOTICE: table "t_proc" does not exist, skipping +create table t_proc(c1 int, c2 int, c3 int unique); +NOTICE: CREATE TABLE / UNIQUE will create implicit index "t_proc_c3_key" for table "t_proc" +insert into t_proc select a,a,a from generate_series(1,20) as a; +select *from t_proc order by c3; + c1 | c2 | c3 +----+----+---- + 1 | 1 | 1 + 2 | 2 | 2 + 3 | 3 | 3 + 4 | 4 | 4 + 5 | 5 | 5 + 6 | 6 | 6 + 7 | 7 | 7 + 8 | 8 | 8 + 9 | 9 | 9 + 10 | 10 | 10 + 11 | 11 | 11 + 12 | 12 | 12 + 13 | 13 | 13 + 14 | 14 | 14 + 15 | 15 | 15 + 16 | 16 | 16 + 17 | 17 | 17 + 18 | 18 | 18 + 19 | 19 | 19 + 20 | 20 | 20 +(20 rows) + +create or replace procedure mul_ups( c in INTEGER) +as +declare +val int; +begin + for val in select c3 from t_proc loop + insert into t_proc values(c,c, val) on duplicate key update c1 = c, c2 =c; + end loop; +end; +/ +call mul_ups(100); + mul_ups +--------- + +(1 row) + +select *from t_proc order by c3; + c1 | c2 | c3 +-----+-----+---- + 100 | 100 | 1 + 100 | 100 | 2 + 100 | 100 | 3 + 100 | 100 | 4 + 100 | 100 | 5 + 100 | 100 | 6 + 100 | 100 | 7 + 100 | 100 | 8 + 100 | 100 | 9 + 100 | 100 | 10 + 100 | 100 | 11 + 100 | 100 | 12 + 100 | 100 | 13 + 100 | 100 | 14 + 100 | 100 | 15 + 100 | 100 | 16 + 100 | 100 | 17 + 100 | 100 | 18 + 100 | 100 | 19 + 100 | 100 | 20 +(20 rows) + +create or replace procedure mul_ups( c in INTEGER) +as +declare +val int; +begin + for val in select c3 from t_proc loop + insert into t_proc values(c,c, val) on duplicate key update c1 = $1, c2 =$1; + end loop; +end; +/ +call mul_ups(200); + mul_ups +--------- + +(1 row) + +select *from t_proc order by c3; + c1 | c2 | c3 +-----+-----+---- + 200 | 200 | 1 + 200 | 200 | 2 + 200 | 200 | 3 + 200 | 200 | 4 + 200 | 200 | 5 + 200 | 200 | 6 + 200 | 200 | 7 + 200 | 200 | 8 + 200 | 200 | 9 + 200 | 200 | 10 + 200 | 200 | 11 + 200 | 200 | 12 + 200 | 200 | 13 + 200 | 200 | 14 + 200 | 200 | 15 + 200 | 200 | 16 + 200 | 200 | 17 + 200 | 200 | 18 + 200 | 200 | 19 + 200 | 200 | 20 +(20 rows) + +create or replace procedure mul_ups_00() +as +declare +val int; +begin + for val in select c3 from t_proc loop + insert into t_proc values(val,val,val) on duplicate key update c1 = 300, c2 =300; + end loop; +end; +/ +call mul_ups_00(); + mul_ups_00 +------------ + +(1 row) + +select *from t_proc order by c3; + c1 | c2 | c3 +-----+-----+---- + 300 | 300 | 1 + 300 | 300 | 2 + 300 | 300 | 3 + 300 | 300 | 4 + 300 | 300 | 5 + 300 | 300 | 6 + 300 | 300 | 7 + 300 | 300 | 8 + 300 | 300 | 9 + 300 | 300 | 10 + 300 | 300 | 11 + 300 | 300 | 12 + 300 | 300 | 13 + 300 | 300 | 14 + 300 | 300 | 15 + 300 | 300 | 16 + 300 | 300 | 17 + 300 | 300 | 18 + 300 | 300 | 19 + 300 | 300 | 20 +(20 rows) + +declare +val int; +c int :=400; +begin + for val in select c3 from t_proc loop + insert into t_proc values(c,c, val) on duplicate key update c1 = c, c2 =c; + end loop; +end; +/ +select *from t_proc order by c3; + c1 | c2 | c3 +-----+-----+---- + 400 | 400 | 1 + 400 | 400 | 2 + 400 | 400 | 3 + 400 | 400 | 4 + 400 | 400 | 5 + 400 | 400 | 6 + 400 | 400 | 7 + 400 | 400 | 8 + 400 | 400 | 9 + 400 | 400 | 10 + 400 | 400 | 11 + 400 | 400 | 12 + 400 | 400 | 13 + 400 | 400 | 14 + 400 | 400 | 15 + 400 | 400 | 16 + 400 | 400 | 17 + 400 | 400 | 18 + 400 | 400 | 19 + 400 | 400 | 20 +(20 rows) + +create or replace procedure mul_ups( c in INTEGER) +as +declare +val int; +begin + for val in select c3 from t_proc loop + insert into t_proc values(c,c, val+100) on duplicate key update c1 = $1, c2 =$1; + end loop; +end; +/ +call mul_ups(500); + mul_ups +--------- + +(1 row) + +select *from t_proc order by c3; + c1 | c2 | c3 +-----+-----+----- + 400 | 400 | 1 + 400 | 400 | 2 + 400 | 400 | 3 + 400 | 400 | 4 + 400 | 400 | 5 + 400 | 400 | 6 + 400 | 400 | 7 + 400 | 400 | 8 + 400 | 400 | 9 + 400 | 400 | 10 + 400 | 400 | 11 + 400 | 400 | 12 + 400 | 400 | 13 + 400 | 400 | 14 + 400 | 400 | 15 + 400 | 400 | 16 + 400 | 400 | 17 + 400 | 400 | 18 + 400 | 400 | 19 + 400 | 400 | 20 + 500 | 500 | 101 + 500 | 500 | 102 + 500 | 500 | 103 + 500 | 500 | 104 + 500 | 500 | 105 + 500 | 500 | 106 + 500 | 500 | 107 + 500 | 500 | 108 + 500 | 500 | 109 + 500 | 500 | 110 + 500 | 500 | 111 + 500 | 500 | 112 + 500 | 500 | 113 + 500 | 500 | 114 + 500 | 500 | 115 + 500 | 500 | 116 + 500 | 500 | 117 + 500 | 500 | 118 + 500 | 500 | 119 + 500 | 500 | 120 +(40 rows) + +/* + * insert + */ +drop table if exists t_default; +NOTICE: table "t_default" does not exist, skipping +CREATE TABLE t_default (c1 INT PRIMARY KEY DEFAULT 10, c2 FLOAT DEFAULT 3.0, c3 TIMESTAMP DEFAULT '20200508'); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_default_pkey" for table "t_default" +create view v_default as select *from t_default order by c1; +-- insert src +---- insert values +truncate t_default; +insert into t_default values(DEFAULT, DEFAULT, DEFAULT); +insert into t_default values(1, 2, '20200630'); +select *from t_default order by c1; + c1 | c2 | c3 +----+----+-------------------------- + 1 | 2 | Tue Jun 30 00:00:00 2020 + 10 | 3 | Fri May 08 00:00:00 2020 +(2 rows) + +insert into t_default values(DEFAULT, DEFAULT, DEFAULT) on duplicate key update c2 = 1; +select *from t_default order by c1; + c1 | c2 | c3 +----+----+-------------------------- + 1 | 2 | Tue Jun 30 00:00:00 2020 + 10 | 1 | Fri May 08 00:00:00 2020 +(2 rows) + +insert into t_default values(1, 2, '20200630') on duplicate key update c2 = 2; +select *from t_default order by c1; + c1 | c2 | c3 +----+----+-------------------------- + 1 | 2 | Tue Jun 30 00:00:00 2020 + 10 | 1 | Fri May 08 00:00:00 2020 +(2 rows) + +insert into t_default values(DEFAULT, DEFAULT, DEFAULT),(1, 2, '20200630') on duplicate key update c2 = 3; +select *from t_default order by c1; + c1 | c2 | c3 +----+----+-------------------------- + 1 | 3 | Tue Jun 30 00:00:00 2020 + 10 | 3 | Fri May 08 00:00:00 2020 +(2 rows) + +---- insert subquery +insert into t_default select *from t_default order by c1 on duplicate key update c2 = 4; +select *from t_default order by c1; + c1 | c2 | c3 +----+----+-------------------------- + 1 | 4 | Tue Jun 30 00:00:00 2020 + 10 | 4 | Fri May 08 00:00:00 2020 +(2 rows) + +insert into t_default select *from t_default order by c1 limit 1 on duplicate key update c2 = 5; +select *from t_default order by c1; + c1 | c2 | c3 +----+----+-------------------------- + 1 | 5 | Tue Jun 30 00:00:00 2020 + 10 | 4 | Fri May 08 00:00:00 2020 +(2 rows) + +insert into t_default select *from t_default where c1=10 on duplicate key update c2 = 6; +select *from t_default order by c1; + c1 | c2 | c3 +----+----+-------------------------- + 1 | 5 | Tue Jun 30 00:00:00 2020 + 10 | 6 | Fri May 08 00:00:00 2020 +(2 rows) + +insert into t_default select *from t_default order by c1 on duplicate key update c2 = 7; +select *from t_default order by c1; + c1 | c2 | c3 +----+----+-------------------------- + 1 | 7 | Tue Jun 30 00:00:00 2020 + 10 | 7 | Fri May 08 00:00:00 2020 +(2 rows) + +insert into t_default select *from v_default order by c1 on duplicate key update c2 = 8; +select *from t_default order by c1; + c1 | c2 | c3 +----+----+-------------------------- + 1 | 8 | Tue Jun 30 00:00:00 2020 + 10 | 8 | Fri May 08 00:00:00 2020 +(2 rows) + +insert into t_default select *from t_default union select c1+1,c2+1,c3+1 from t_default order by c1 on duplicate key update c2 = 9; +select *from t_default order by c1; + c1 | c2 | c3 +----+----+-------------------------- + 1 | 9 | Tue Jun 30 00:00:00 2020 + 2 | 9 | Wed Jul 01 00:00:00 2020 + 10 | 9 | Fri May 08 00:00:00 2020 + 11 | 9 | Sat May 09 00:00:00 2020 +(4 rows) + +insert into t_default(c1,c2) select max(c1),c2 from t_default group by c2 on duplicate key update c2 = 10; +-- index +---- mul index +truncate t_default; +create unique index t_default_mul on t_default(c1,c3); +insert into t_default values(DEFAULT, DEFAULT, DEFAULT); +insert into t_default values(1, 2, '20200630'); +select *from t_default order by c1; + c1 | c2 | c3 +----+----+-------------------------- + 1 | 2 | Tue Jun 30 00:00:00 2020 + 10 | 3 | Fri May 08 00:00:00 2020 +(2 rows) + +insert into t_default values(DEFAULT, DEFAULT, DEFAULT) on duplicate key update c2 = 1; +select *from t_default order by c1; + c1 | c2 | c3 +----+----+-------------------------- + 1 | 2 | Tue Jun 30 00:00:00 2020 + 10 | 1 | Fri May 08 00:00:00 2020 +(2 rows) + +insert into t_default values(1, 2, '20200630') on duplicate key update c2 = 2; +select *from t_default order by c1; + c1 | c2 | c3 +----+----+-------------------------- + 1 | 2 | Tue Jun 30 00:00:00 2020 + 10 | 1 | Fri May 08 00:00:00 2020 +(2 rows) + +insert into t_default values(DEFAULT, DEFAULT, DEFAULT),(1, 2, '20200630') on duplicate key update c2 = 3; +select *from t_default order by c1; + c1 | c2 | c3 +----+----+-------------------------- + 1 | 3 | Tue Jun 30 00:00:00 2020 + 10 | 3 | Fri May 08 00:00:00 2020 +(2 rows) + +insert into t_default select *from t_default on duplicate key update c2 = 4; +select *from t_default order by c1; + c1 | c2 | c3 +----+----+-------------------------- + 1 | 4 | Tue Jun 30 00:00:00 2020 + 10 | 4 | Fri May 08 00:00:00 2020 +(2 rows) + diff --git a/src/test/regress/expected/upsert_explain.out b/src/test/regress/expected/upsert_explain.out new file mode 100644 index 000000000..5ff2ec4d1 --- /dev/null +++ b/src/test/regress/expected/upsert_explain.out @@ -0,0 +1,297 @@ +-------------------------------------------------------------------------------------------- +/* + * explain upsert + */ +-------------------------------------------------------------------------------------------- +\c upsert; +SET CURRENT_SCHEMA TO upsert_test_explain; +-- enable_upsert_to_merge must be off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +SHOW enable_upsert_to_merge; + enable_upsert_to_merge +------------------------ + off +(1 row) + +create temp table up_expl_temp(c1 int, c2 int, c3 int unique) ; +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_expl_temp_c3_key" for table "up_expl_temp" +insert into up_expl_hash select a,a,a from generate_series(1,20) as a; +insert into up_expl_repl select a,a,a from generate_series(1,20) as a; +insert into up_expl_part select a,a,a from generate_series(1,20) as a; +insert into up_expl_temp select a,a,a from generate_series(1,20) as a; +insert into up_expl_unlog select a,a,a from generate_series(1,20) as a; +insert into up_expl_node select a,a,a from generate_series(1,20) as a; +insert into up_expl_repl2 select a,a,a,a,a from generate_series(1,20) as a; +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_hash values(1,1,1) on duplicate key update c1 = 2; + QUERY PLAN +------------------------------------------------- + Insert on upsert_test_explain.up_expl_hash + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: up_expl_hash_c3_key + -> Result + Output: 1, 1, 1 +(5 rows) + +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_hash values(1,1,1) on duplicate key update c1 = 2; + QUERY PLAN +------------------------------------------------- + Insert on up_expl_hash (actual rows=1 loops=1) + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: up_expl_hash_c3_key + -> Result (actual rows=1 loops=1) +--? Total runtime: .* ms +(5 rows) + +EXPLAIN PERFORMANCE insert into up_expl_hash values(1,1,1) on duplicate key update c1 = 2; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------ +--? Insert on upsert_test_explain.up_expl_hash (cost=.* rows=1 width=0) (actual time=.* rows=1 loops=1) + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: up_expl_hash_c3_key + (Buffers: shared hit=4) +--? (CPU: ex c/r=.*, ex row=1, ex cyc=.*, inc cyc=.*) +--? -> Result (cost=.* rows=1 width=0) (actual time=.* rows=1 loops=1) + Output: 1, 1, 1 +--? (CPU: ex c/r=.*, ex row=1, ex cyc=.*, inc cyc=.*) +--? Total runtime: .* ms +(9 rows) + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_hash values(1,1,1),(2,2,2)on duplicate key update c1 = 2; + QUERY PLAN +---------------------------------------------------------------------------- + Insert on upsert_test_explain.up_expl_hash + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: up_expl_hash_c3_key + -> Values Scan on "*VALUES*" + Output: "*VALUES*".column1, "*VALUES*".column2, "*VALUES*".column3 +(5 rows) + +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_hash values(1,1,1),(2,2,2)on duplicate key update c1 = 2; + QUERY PLAN +--------------------------------------------------------- + Insert on up_expl_hash (actual rows=2 loops=1) + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: up_expl_hash_c3_key + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) +--? Total runtime: .* ms +(5 rows) + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_repl values(1,1,1) on duplicate key update c1 = 2; + QUERY PLAN +------------------------------------------------- + Insert on upsert_test_explain.up_expl_repl + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: up_expl_repl_c3_key + -> Result + Output: 1, 1, 1 +(5 rows) + +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_repl values(1,1,1) on duplicate key update c1 = 2; + QUERY PLAN +------------------------------------------------- + Insert on up_expl_repl (actual rows=1 loops=1) + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: up_expl_repl_c3_key + -> Result (actual rows=1 loops=1) +--? Total runtime: .* ms +(5 rows) + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_part values(1,1,1) on duplicate key update c1 = 2; + QUERY PLAN +------------------------------------------------- + Insert on upsert_test_explain.up_expl_part + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: up_expl_part_c3_key + -> Result + Output: 1, 1, 1 +(5 rows) + +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_part values(1,1,1) on duplicate key update c1 = 2; + QUERY PLAN +------------------------------------------------- + Insert on up_expl_part (actual rows=1 loops=1) + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: up_expl_part_c3_key + -> Result (actual rows=1 loops=1) +--? Total runtime: .* ms +(5 rows) + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_temp values(1,1,1) on duplicate key update c1 = 2; + QUERY PLAN +--?.* +--? Insert on pg_temp_datanod.*.up_expl_temp + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: up_expl_temp_c3_key + -> Result + Output: 1, 1, 1 +(5 rows) + +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_temp values(1,1,1) on duplicate key update c1 = 2; + QUERY PLAN +------------------------------------------------- + Insert on up_expl_temp (actual rows=1 loops=1) + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: up_expl_temp_c3_key + -> Result (actual rows=1 loops=1) +--? Total runtime: .* ms +(5 rows) + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_unlog values(1,1,1) on duplicate key update c1 = 2; + QUERY PLAN +-------------------------------------------------- + Insert on upsert_test_explain.up_expl_unlog + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: up_expl_unlog_c3_key + -> Result + Output: 1, 1, 1 +(5 rows) + +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_unlog values(1,1,1) on duplicate key update c1 = 2; + QUERY PLAN +-------------------------------------------------- + Insert on up_expl_unlog (actual rows=1 loops=1) + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: up_expl_unlog_c3_key + -> Result (actual rows=1 loops=1) +--? Total runtime: .* ms +(5 rows) + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_node values(1,1,1) on duplicate key update c1 = 2; + QUERY PLAN +------------------------------------------------- + Insert on upsert_test_explain.up_expl_node + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: up_expl_node_c3_key + -> Result + Output: 1, 1, 1 +(5 rows) + +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_node values(1,1,1) on duplicate key update c1 = 2; + QUERY PLAN +------------------------------------------------- + Insert on up_expl_node (actual rows=1 loops=1) + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: up_expl_node_c3_key + -> Result (actual rows=1 loops=1) +--? Total runtime: .* ms +(5 rows) + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_hash values(1,1,1),(2,2,2)on duplicate key update nothing; + QUERY PLAN +---------------------------------------------------------------------------- + Insert on upsert_test_explain.up_expl_hash + Conflict Resolution: NOTHING + Conflict Arbiter Indexes: up_expl_hash_c3_key + -> Values Scan on "*VALUES*" + Output: "*VALUES*".column1, "*VALUES*".column2, "*VALUES*".column3 +(5 rows) + +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_hash values(1,1,1),(2,2,2)on duplicate key update nothing; + QUERY PLAN +--------------------------------------------------------- + Insert on up_expl_hash (actual rows=0 loops=1) + Conflict Resolution: NOTHING + Conflict Arbiter Indexes: up_expl_hash_c3_key + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) +--? Total runtime: .* ms +(5 rows) + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_repl values(1,1,1) on duplicate key update nothing; + QUERY PLAN +------------------------------------------------- + Insert on upsert_test_explain.up_expl_repl + Conflict Resolution: NOTHING + Conflict Arbiter Indexes: up_expl_repl_c3_key + -> Result + Output: 1, 1, 1 +(5 rows) + +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_repl values(1,1,1) on duplicate key update nothing; + QUERY PLAN +------------------------------------------------- + Insert on up_expl_repl (actual rows=0 loops=1) + Conflict Resolution: NOTHING + Conflict Arbiter Indexes: up_expl_repl_c3_key + -> Result (actual rows=1 loops=1) +--? Total runtime: .* ms +(5 rows) + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_part values(1,1,1) on duplicate key update nothing; + QUERY PLAN +------------------------------------------------- + Insert on upsert_test_explain.up_expl_part + Conflict Resolution: NOTHING + Conflict Arbiter Indexes: up_expl_part_c3_key + -> Result + Output: 1, 1, 1 +(5 rows) + +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_part values(1,1,1) on duplicate key update nothing; + QUERY PLAN +------------------------------------------------- + Insert on up_expl_part (actual rows=0 loops=1) + Conflict Resolution: NOTHING + Conflict Arbiter Indexes: up_expl_part_c3_key + -> Result (actual rows=1 loops=1) +--? Total runtime: .* ms +(5 rows) + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_temp values(1,1,1) on duplicate key update nothing; + QUERY PLAN +--?.* +--? Insert on pg_temp_datanod.*.up_expl_temp + Conflict Resolution: NOTHING + Conflict Arbiter Indexes: up_expl_temp_c3_key + -> Result + Output: 1, 1, 1 +(5 rows) + +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_temp values(1,1,1) on duplicate key update nothing; + QUERY PLAN +------------------------------------------------- + Insert on up_expl_temp (actual rows=0 loops=1) + Conflict Resolution: NOTHING + Conflict Arbiter Indexes: up_expl_temp_c3_key + -> Result (actual rows=1 loops=1) +--? Total runtime: .* ms +(5 rows) + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_unlog values(1,1,1) on duplicate key update nothing; + QUERY PLAN +-------------------------------------------------- + Insert on upsert_test_explain.up_expl_unlog + Conflict Resolution: NOTHING + Conflict Arbiter Indexes: up_expl_unlog_c3_key + -> Result + Output: 1, 1, 1 +(5 rows) + +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_unlog values(1,1,1) on duplicate key update nothing; + QUERY PLAN +-------------------------------------------------- + Insert on up_expl_unlog (actual rows=0 loops=1) + Conflict Resolution: NOTHING + Conflict Arbiter Indexes: up_expl_unlog_c3_key + -> Result (actual rows=1 loops=1) +--? Total runtime: .* ms +(5 rows) + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_node values(1,1,1) on duplicate key update nothing; + QUERY PLAN +------------------------------------------------- + Insert on upsert_test_explain.up_expl_node + Conflict Resolution: NOTHING + Conflict Arbiter Indexes: up_expl_node_c3_key + -> Result + Output: 1, 1, 1 +(5 rows) + +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_node values(1,1,1) on duplicate key update nothing; + QUERY PLAN +------------------------------------------------- + Insert on up_expl_node (actual rows=0 loops=1) + Conflict Resolution: NOTHING + Conflict Arbiter Indexes: up_expl_node_c3_key + -> Result (actual rows=1 loops=1) +--? Total runtime: .* ms +(5 rows) diff --git a/src/test/regress/expected/upsert_grammer_test_01.out b/src/test/regress/expected/upsert_grammer_test_01.out new file mode 100644 index 000000000..378d916f5 --- /dev/null +++ b/src/test/regress/expected/upsert_grammer_test_01.out @@ -0,0 +1,188 @@ +\c upsert +SET CURRENT_SCHEMA TO upsert_test; +-- enable_upsert_to_merge must be off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +SHOW enable_upsert_to_merge; + enable_upsert_to_merge +------------------------ + off +(1 row) + +-- support multiple set +INSERT INTO t_grammer VALUES(11, 1) ON DUPLICATE KEY UPDATE c2 = 2, c4 = ROW(10, 20), c3 = '{10, 20, 30}'; +INSERT INTO t_grammer VALUES(12, 2) ON DUPLICATE KEY UPDATE c2 = 2, c3[1] = 3; +-- support const value and expr +INSERT INTO t_grammer VALUES(23) ON DUPLICATE KEY UPDATE c2 = 2; +INSERT INTO t_grammer VALUES(24) ON DUPLICATE KEY UPDATE c2 = length('test') + 100; +-- support without table name +INSERT INTO t_grammer VALUES(31, 1) ON DUPLICATE KEY UPDATE c2 = c1, c4.a = VALUES(c1); +INSERT INTO t_grammer VALUES(32, 2) ON DUPLICATE KEY UPDATE c2 = c1 + 5, c4.a = VALUES(c1); +-- support with table name +INSERT INTO t_grammer VALUES(41, 1) ON DUPLICATE KEY UPDATE c2 = t_grammer.c1, c4.a = VALUES(c1); +INSERT INTO t_grammer VALUES(42, 2) ON DUPLICATE KEY UPDATE c2 = t_grammer.c1 + 5, c4.a = VALUES(c1); +-- support EXCLUDED alone and with expr +INSERT INTO t_grammer VALUES(51, 1) ON DUPLICATE KEY UPDATE c2 = c1 + 5, c4.a = EXCLUDED.c1; +INSERT INTO t_grammer VALUES(52, 2) ON DUPLICATE KEY UPDATE c2 = t_grammer.c1 + 5, c4.a = EXCLUDED.c1 + 5; +INSERT INTO t_grammer VALUES(53, 3) ON DUPLICATE KEY UPDATE c4.a = sqrt(EXCLUDED.c1 - EXCLUDED.c2 - 1) + 1, c2 = t_grammer.c1 + 5; +INSERT INTO t_grammer VALUES(54, 4) ON DUPLICATE KEY UPDATE c4.b = EXCLUDED.c1 + t_grammer.c1; +INSERT INTO t_grammer VALUES(55, 5) ON DUPLICATE KEY UPDATE c4.a = EXCLUDED.c2, c4.b = EXCLUDED.c1 + sqrt(c1 - 6); +-- support ARRAY +INSERT INTO t_grammer VALUES(61, 1) ON DUPLICATE KEY UPDATE c2 = VALUES(c3[1]); +INSERT INTO t_grammer VALUES(62, 2) ON DUPLICATE KEY UPDATE c5 = VALUES(c3[2,3]); +INSERT INTO t_grammer VALUES(63, 3) ON DUPLICATE KEY UPDATE c5 = VALUES(c3[2:3]); +INSERT INTO t_grammer VALUES(64, 4, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c2 = EXCLUDED.c3[1]; +INSERT INTO t_grammer VALUES(65, 5, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = EXCLUDED.c3[2,3]; +INSERT INTO t_grammer VALUES(66, 6, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = EXCLUDED.c3[1:2]; +INSERT INTO t_grammer VALUES(67, 7, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c2 = c3[1]; +INSERT INTO t_grammer VALUES(68, 8, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = c3[2,3]; +INSERT INTO t_grammer VALUES(69, 9, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = c3[1:2]; +-- support user defined type +INSERT INTO t_grammer VALUES(71, 1) ON DUPLICATE KEY UPDATE c4.a = c3[1] + c1; +INSERT INTO t_grammer VALUES(72, 2, '{71, 72, 73}') ON DUPLICATE KEY UPDATE c4.a = c3[1] + c1; +INSERT INTO t_grammer VALUES(73, 3, '{71, 72, 73}') ON DUPLICATE KEY UPDATE c4.a = c3[1] + c1, c4.b = c3[2]; +INSERT INTO t_grammer VALUES(74, 4, '{71, 72, 73}', ROW(74, 75)) ON DUPLICATE KEY UPDATE c4 = ROW(740, 750); +-- appoint INSERT target column +INSERT INTO t_grammer (c5, c1, c2) VALUES('{81,82}', 81, DEFAULT) ON DUPLICATE KEY UPDATE c2 = VALUES(c1); +INSERT INTO t_grammer (c3[1], c3[2], c1) VALUES(881, 882, 82) ON DUPLICATE KEY UPDATE c5 = VALUES(c3), c2 = VALUES(c3[1]); +INSERT INTO t_grammer (c1, c3, c4) VALUES(83, '{81, 82, 83}', ROW(810, 820)) ON DUPLICATE KEY UPDATE c4 = EXCLUDED.c4; +INSERT INTO t_grammer (c1, c3, c4.a) VALUES(84, '{81, 82, 83}', 850) ON DUPLICATE KEY UPDATE c4.b = c3[3]; +-- support UPDATE NOTHING +INSERT INTO t_grammer VALUES(91, 1, '{91, 92, 93}', ROW(94, 95)) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_grammer (c5, c1, c2, c4) VALUES('{91,92}', 92, DEFAULT, ROW(910, 920)) ON DUPLICATE KEY UPDATE NOTHING; +-- UPDATE target: unsupport with schema but support with tablename +INSERT INTO t_grammer VALUES(0, 0, '{0,0}', ROW(0, 0), '{107, 108}') ON DUPLICATE KEY UPDATE + upsert_test.t_grammer.c2 = c2 * 10, upsert_test.t_grammer.c3[1:2] = c5[1:2], upsert_test.t_grammer.c4.a = c1; +ERROR: column "upsert_test.t_grammer" of relation "t_grammer" does not exist +LINE 2: upsert_test.t_grammer.c2 = c2 * 10, upsert_test.t_grammer.c... + ^ +INSERT INTO t_grammer VALUES(101, 1, '{102, 103, 104}', ROW(105, 106), '{107, 108}') ON DUPLICATE KEY UPDATE + t_grammer.c2 = c2 * 10, t_grammer.c3[1:2] = c5[1:2], t_grammer.c4.a = c1; +-- INSERT target: appoint schema +INSERT INTO upsert_test.t_grammer VALUES(102, 2, '{102, 103, 104}', ROW(105, 106), '{107, 108}') ON DUPLICATE KEY UPDATE + t_grammer.c2 = EXCLUDED.c2 * 10, t_grammer.c3[1:2] = c5[1:2], t_grammer.c4.a = VALUES(c1); +CREATE SCHEMA upsert_test_tmpschema; +SET CURRENT_SCHEMA TO upsert_test_tmpschema; +INSERT INTO upsert_test.t_grammer VALUES(103, 3, '{102, 103, 104}', ROW(105, 106), '{107, 108}') ON DUPLICATE KEY UPDATE + t_grammer.c2 = EXCLUDED.c2 * 10, t_grammer.c3[1:2] = c5[1:2], t_grammer.c4.a = VALUES(c1); +SET CURRENT_SCHEMA TO upsert_test; +DROP SCHEMA upsert_test_tmpschema; +-- support data type +INSERT INTO t_data VALUES(11, 11, 11, 11, 11, '11', '11', '11', '2020-04-23', '2020-04-23 11:00', '1:00') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_data VALUES(12, 12, 12, 12, 12, '12', '12', '12', '2020-04-24', '2020-04-24 11:00', '2:00') + ON DUPLICATE KEY UPDATE + c_tiny = 13, c_smallint = 13, c_bigint = 13, c_numeric = 13, + c_var = '13', c_text = '13', c_bytea = '13', c_date = '2020-05-24', c_timestamp = '2020-05-24 11:00', c_time = '3:00'; +INSERT INTO t_data VALUES(13, 13, 13, 13, 13, '13', '13', '13', '2020-04-25', '2020-04-25 11:00', '4:00') + ON DUPLICATE KEY UPDATE + c_tiny = EXCLUDED.c_tiny + 1, c_smallint = EXCLUDED.c_smallint + 1, c_bigint = EXCLUDED.c_bigint + 1, c_numeric = EXCLUDED.c_numeric + 1, + c_var = EXCLUDED.c_var || '3', c_text = EXCLUDED.c_text || '3', c_bytea = EXCLUDED.c_bytea || '3', + c_date = EXCLUDED.c_date + '1 day'::interval, c_timestamp = EXCLUDED.c_timestamp + '1 day'::interval, c_time = EXCLUDED.c_time + '1 hour'::interval; +-- support UPDATE to default +INSERT INTO t_default DEFAULT VALUES ON DUPLICATE KEY UPDATE c2 = 100, c3 = '2020-05-17'; +INSERT INTO t_default VALUES(91, 0.91, '2020-05-17') ON DUPLICATE KEY UPDATE c2 = DEFAULT, c3 = DEFAULT; +INSERT INTO t_default VALUES(92, DEFAULT, '2020-05-17') ON DUPLICATE KEY UPDATE c2 = 10, c3 = DEFAULT; +-- unsupport alias +INSERT INTO t_grammer AS alias VALUES (91) ON DUPLICATE KEY UPDATE c2 = VALUES(c1); +ERROR: syntax error at or near "AS" +LINE 1: INSERT INTO t_grammer AS alias VALUES (91) ON DUPLICATE KEY ... + ^ +-- unsupport VALUES with expr +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = sqrt(VALUES(c2)); +ERROR: syntax error at or near "(" +LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = sqrt(VALUES(c2)); + ^ +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c3[1]) + 1; +ERROR: syntax error at or near "+" +LINE 1: ...VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c3[1]) + 1; + ^ +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a) + 1; +ERROR: only allow column name within VALUES +LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a) + 1; + ^ +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.c3[1]) + 1; +ERROR: only allow column name within VALUES +LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.... + ^ +-- unsupport VALUES with table name +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.c4.a); +ERROR: only allow column name within VALUES +LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.... + ^ +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.c2); +ERROR: only allow column name within VALUES +LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.... + ^ +-- unsupport DEFAULT with expr +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = DEFAULT + 1; +ERROR: syntax error at or near "+" +LINE 1: ...ammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = DEFAULT + 1; + ^ +-- unsupport user defined typed column's element +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a); +ERROR: only allow column name within VALUES +LINE 1: ...mmer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a); + ^ +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = EXCLUDED.c4.a; +ERROR: schema "excluded" does not exist +CONTEXT: referenced column: c2 +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = c4.a; +ERROR: missing FROM-clause entry for table "c4" +LINE 1: ...TO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = c4.a; + ^ +CONTEXT: referenced column: c2 +SELECT * FROM t_data ORDER BY 1; + c_int | c_tiny | c_smallint | c_bigint | c_numeric | c_var | c_text | c_bytea | c_date | c_timestamp | c_time | c_intarray | c_com +-------+--------+------------+----------+-----------+-------+--------+---------+--------------------------+--------------------------+----------+------------+------- + 11 | 11 | 11 | 11 | 11 | 11 | 11 | \x3131 | Thu Apr 23 00:00:00 2020 | Thu Apr 23 11:00:00 2020 | 01:00:00 | | + 12 | 12 | 12 | 12 | 12 | 12 | 12 | \x3132 | Fri Apr 24 00:00:00 2020 | Fri Apr 24 11:00:00 2020 | 02:00:00 | | + 13 | 13 | 13 | 13 | 13 | 13 | 13 | \x3133 | Sat Apr 25 00:00:00 2020 | Sat Apr 25 11:00:00 2020 | 04:00:00 | | +(3 rows) + +SELECT * FROM t_grammer ORDER BY 1; + c1 | c2 | c3 | c4 | c5 +-----+------+---------------+-----------+----------- + 11 | 1 | | | + 12 | 2 | | | + 23 | -100 | | | + 24 | -100 | | | + 31 | 1 | | | + 32 | 2 | | | + 41 | 1 | | | + 42 | 2 | | | + 51 | 1 | | | + 52 | 2 | | | + 53 | 3 | | | + 54 | 4 | | | + 55 | 5 | | | + 61 | 1 | | | + 62 | 2 | | | + 63 | 3 | | | + 64 | 4 | {10,20,30} | | + 65 | 5 | {10,20,30} | | + 66 | 6 | {10,20,30} | | + 67 | 7 | {10,20,30} | | + 68 | 8 | {10,20,30} | | + 69 | 9 | {10,20,30} | | + 71 | 1 | | | + 72 | 2 | {71,72,73} | | + 73 | 3 | {71,72,73} | | + 74 | 4 | {71,72,73} | (74,75) | + 81 | -100 | | | {81,82} + 82 | -100 | {881,882} | | + 83 | -100 | {81,82,83} | (810,820) | + 84 | -100 | {81,82,83} | (850,) | + 91 | 1 | {91,92,93} | (94,95) | + 92 | -100 | | (910,920) | {91,92} + 101 | 1 | {102,103,104} | (105,106) | {107,108} + 102 | 2 | {102,103,104} | (105,106) | {107,108} + 103 | 3 | {102,103,104} | (105,106) | {107,108} +(35 rows) + +SELECT * FROM t_default ORDER BY 1; +--? c1 | c2 | c3 +--? +--? 10 | .396464773453772 | Wed Apr 29 10:25:23.202729 2020 +--? 91 | .91 | Sun May 17 00:00:00 2020 +--? 92 | .840485369320959 | Sun May 17 00:00:00 2020 +(3 rows) + diff --git a/src/test/regress/expected/upsert_grammer_test_02.out b/src/test/regress/expected/upsert_grammer_test_02.out new file mode 100644 index 000000000..0b12c39a4 --- /dev/null +++ b/src/test/regress/expected/upsert_grammer_test_02.out @@ -0,0 +1,188 @@ +\c upsert +SET CURRENT_SCHEMA TO upsert_test; +-- enable_upsert_to_merge must be off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +SHOW enable_upsert_to_merge; + enable_upsert_to_merge +------------------------ + off +(1 row) + +-- support multiple set +INSERT INTO t_grammer VALUES(11, 1) ON DUPLICATE KEY UPDATE c2 = 2, c4 = ROW(10, 20), c3 = '{10, 20, 30}'; +INSERT INTO t_grammer VALUES(12, 2) ON DUPLICATE KEY UPDATE c2 = 2, c3[1] = 3; +-- support const value and expr +INSERT INTO t_grammer VALUES(23) ON DUPLICATE KEY UPDATE c2 = 2; +INSERT INTO t_grammer VALUES(24) ON DUPLICATE KEY UPDATE c2 = length('test') + 100; +-- support without table name +INSERT INTO t_grammer VALUES(31, 1) ON DUPLICATE KEY UPDATE c2 = c1, c4.a = VALUES(c1); +INSERT INTO t_grammer VALUES(32, 2) ON DUPLICATE KEY UPDATE c2 = c1 + 5, c4.a = VALUES(c1); +-- support with table name +INSERT INTO t_grammer VALUES(41, 1) ON DUPLICATE KEY UPDATE c2 = t_grammer.c1, c4.a = VALUES(c1); +INSERT INTO t_grammer VALUES(42, 2) ON DUPLICATE KEY UPDATE c2 = t_grammer.c1 + 5, c4.a = VALUES(c1); +-- support EXCLUDED alone and with expr +INSERT INTO t_grammer VALUES(51, 1) ON DUPLICATE KEY UPDATE c2 = c1 + 5, c4.a = EXCLUDED.c1; +INSERT INTO t_grammer VALUES(52, 2) ON DUPLICATE KEY UPDATE c2 = t_grammer.c1 + 5, c4.a = EXCLUDED.c1 + 5; +INSERT INTO t_grammer VALUES(53, 3) ON DUPLICATE KEY UPDATE c4.a = sqrt(EXCLUDED.c1 - EXCLUDED.c2 - 1) + 1, c2 = t_grammer.c1 + 5; +INSERT INTO t_grammer VALUES(54, 4) ON DUPLICATE KEY UPDATE c4.b = EXCLUDED.c1 + t_grammer.c1; +INSERT INTO t_grammer VALUES(55, 5) ON DUPLICATE KEY UPDATE c4.a = EXCLUDED.c2, c4.b = EXCLUDED.c1 + sqrt(c1 - 6); +-- support ARRAY +INSERT INTO t_grammer VALUES(61, 1) ON DUPLICATE KEY UPDATE c2 = VALUES(c3[1]); +INSERT INTO t_grammer VALUES(62, 2) ON DUPLICATE KEY UPDATE c5 = VALUES(c3[2,3]); +INSERT INTO t_grammer VALUES(63, 3) ON DUPLICATE KEY UPDATE c5 = VALUES(c3[2:3]); +INSERT INTO t_grammer VALUES(64, 4, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c2 = EXCLUDED.c3[1]; +INSERT INTO t_grammer VALUES(65, 5, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = EXCLUDED.c3[2,3]; +INSERT INTO t_grammer VALUES(66, 6, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = EXCLUDED.c3[1:2]; +INSERT INTO t_grammer VALUES(67, 7, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c2 = c3[1]; +INSERT INTO t_grammer VALUES(68, 8, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = c3[2,3]; +INSERT INTO t_grammer VALUES(69, 9, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = c3[1:2]; +-- support user defined type +INSERT INTO t_grammer VALUES(71, 1) ON DUPLICATE KEY UPDATE c4.a = c3[1] + c1; +INSERT INTO t_grammer VALUES(72, 2, '{71, 72, 73}') ON DUPLICATE KEY UPDATE c4.a = c3[1] + c1; +INSERT INTO t_grammer VALUES(73, 3, '{71, 72, 73}') ON DUPLICATE KEY UPDATE c4.a = c3[1] + c1, c4.b = c3[2]; +INSERT INTO t_grammer VALUES(74, 4, '{71, 72, 73}', ROW(74, 75)) ON DUPLICATE KEY UPDATE c4 = ROW(740, 750); +-- appoint INSERT target column +INSERT INTO t_grammer (c5, c1, c2) VALUES('{81,82}', 81, DEFAULT) ON DUPLICATE KEY UPDATE c2 = VALUES(c1); +INSERT INTO t_grammer (c3[1], c3[2], c1) VALUES(881, 882, 82) ON DUPLICATE KEY UPDATE c5 = VALUES(c3), c2 = VALUES(c3[1]); +INSERT INTO t_grammer (c1, c3, c4) VALUES(83, '{81, 82, 83}', ROW(810, 820)) ON DUPLICATE KEY UPDATE c4 = EXCLUDED.c4; +INSERT INTO t_grammer (c1, c3, c4.a) VALUES(84, '{81, 82, 83}', 850) ON DUPLICATE KEY UPDATE c4.b = c3[3]; +-- support UPDATE NOTHING +INSERT INTO t_grammer VALUES(91, 1, '{91, 92, 93}', ROW(94, 95)) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_grammer (c5, c1, c2, c4) VALUES('{91,92}', 92, DEFAULT, ROW(910, 920)) ON DUPLICATE KEY UPDATE NOTHING; +-- UPDATE target: unsupport with schema but support with tablename +INSERT INTO t_grammer VALUES(0, 0, '{0,0}', ROW(0, 0), '{107, 108}') ON DUPLICATE KEY UPDATE + upsert_test.t_grammer.c2 = c2 * 10, upsert_test.t_grammer.c3[1:2] = c5[1:2], upsert_test.t_grammer.c4.a = c1; +ERROR: column "upsert_test.t_grammer" of relation "t_grammer" does not exist +LINE 2: upsert_test.t_grammer.c2 = c2 * 10, upsert_test.t_grammer.c... + ^ +INSERT INTO t_grammer VALUES(101, 1, '{102, 103, 104}', ROW(105, 106), '{107, 108}') ON DUPLICATE KEY UPDATE + t_grammer.c2 = c2 * 10, t_grammer.c3[1:2] = c5[1:2], t_grammer.c4.a = c1; +-- INSERT target: appoint schema +INSERT INTO upsert_test.t_grammer VALUES(102, 2, '{102, 103, 104}', ROW(105, 106), '{107, 108}') ON DUPLICATE KEY UPDATE + t_grammer.c2 = EXCLUDED.c2 * 10, t_grammer.c3[1:2] = c5[1:2], t_grammer.c4.a = VALUES(c1); +CREATE SCHEMA upsert_test_tmp; +SET CURRENT_SCHEMA TO upsert_test_tmp; +INSERT INTO upsert_test.t_grammer VALUES(103, 3, '{102, 103, 104}', ROW(105, 106), '{107, 108}') ON DUPLICATE KEY UPDATE + t_grammer.c2 = EXCLUDED.c2 * 10, t_grammer.c3[1:2] = c5[1:2], t_grammer.c4.a = VALUES(c1); +SET CURRENT_SCHEMA TO upsert_test; +DROP SCHEMA upsert_test_tmp; +-- support data type +INSERT INTO t_data VALUES(11, 11, 11, 11, 11, '11', '11', '11', '2020-04-23', '2020-04-23 11:00', '1:00') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_data VALUES(12, 12, 12, 12, 12, '12', '12', '12', '2020-04-24', '2020-04-24 11:00', '2:00') + ON DUPLICATE KEY UPDATE + c_tiny = 13, c_smallint = 13, c_bigint = 13, c_numeric = 13, + c_var = '13', c_text = '13', c_bytea = '13', c_date = '2020-05-24', c_timestamp = '2020-05-24 11:00', c_time = '3:00'; +INSERT INTO t_data VALUES(13, 13, 13, 13, 13, '13', '13', '13', '2020-04-25', '2020-04-25 11:00', '4:00') + ON DUPLICATE KEY UPDATE + c_tiny = EXCLUDED.c_tiny + 1, c_smallint = EXCLUDED.c_smallint + 1, c_bigint = EXCLUDED.c_bigint + 1, c_numeric = EXCLUDED.c_numeric + 1, + c_var = EXCLUDED.c_var || '3', c_text = EXCLUDED.c_text || '3', c_bytea = EXCLUDED.c_bytea || '3', + c_date = EXCLUDED.c_date + '1 day'::interval, c_timestamp = EXCLUDED.c_timestamp + '1 day'::interval, c_time = EXCLUDED.c_time + '1 hour'::interval; +-- support UPDATE to default +INSERT INTO t_default DEFAULT VALUES ON DUPLICATE KEY UPDATE c2 = 100, c3 = '2020-05-17'; +INSERT INTO t_default VALUES(91, 0.91, '2020-05-17') ON DUPLICATE KEY UPDATE c2 = DEFAULT, c3 = DEFAULT; +INSERT INTO t_default VALUES(92, DEFAULT, '2020-05-17') ON DUPLICATE KEY UPDATE c2 = 10, c3 = DEFAULT; +-- unsupport alias +INSERT INTO t_grammer AS alias VALUES (91) ON DUPLICATE KEY UPDATE c2 = VALUES(c1); +ERROR: syntax error at or near "AS" +LINE 1: INSERT INTO t_grammer AS alias VALUES (91) ON DUPLICATE KEY ... + ^ +-- unsupport VALUES with expr +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = sqrt(VALUES(c2)); +ERROR: syntax error at or near "(" +LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = sqrt(VALUES(c2)); + ^ +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c3[1]) + 1; +ERROR: syntax error at or near "+" +LINE 1: ...VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c3[1]) + 1; + ^ +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a) + 1; +ERROR: only allow column name within VALUES +LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a) + 1; + ^ +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.c3[1]) + 1; +ERROR: only allow column name within VALUES +LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.... + ^ +-- unsupport VALUES with table name +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.c4.a); +ERROR: only allow column name within VALUES +LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.... + ^ +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.c2); +ERROR: only allow column name within VALUES +LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.... + ^ +-- unsupport DEFAULT with expr +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = DEFAULT + 1; +ERROR: syntax error at or near "+" +LINE 1: ...ammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = DEFAULT + 1; + ^ +-- unsupport user defined typed column's element +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a); +ERROR: only allow column name within VALUES +LINE 1: ...mmer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a); + ^ +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = EXCLUDED.c4.a; +ERROR: schema "excluded" does not exist +CONTEXT: referenced column: c2 +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = c4.a; +ERROR: missing FROM-clause entry for table "c4" +LINE 1: ...TO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = c4.a; + ^ +CONTEXT: referenced column: c2 +SELECT * FROM t_data ORDER BY 1; + c_int | c_tiny | c_smallint | c_bigint | c_numeric | c_var | c_text | c_bytea | c_date | c_timestamp | c_time | c_intarray | c_com +-------+--------+------------+----------+-----------+-------+--------+----------+--------------------------+--------------------------+----------+------------+------- + 11 | 11 | 11 | 11 | 11 | 11 | 11 | \x3131 | Thu Apr 23 00:00:00 2020 | Thu Apr 23 11:00:00 2020 | 01:00:00 | | + 12 | 13 | 13 | 13 | 13 | 13 | 13 | \x3133 | Sun May 24 00:00:00 2020 | Sun May 24 11:00:00 2020 | 03:00:00 | | + 13 | 14 | 14 | 14 | 14 | 133 | 133 | \x313333 | Sun Apr 26 00:00:00 2020 | Sun Apr 26 11:00:00 2020 | 05:00:00 | | +(3 rows) + +SELECT * FROM t_grammer ORDER BY 1; + c1 | c2 | c3 | c4 | c5 +-----+------+---------------+-----------+----------- + 11 | 2 | {10,20,30} | (10,20) | + 12 | 2 | {3} | | + 23 | 2 | | | + 24 | 104 | | | + 31 | 31 | | (31,) | + 32 | 37 | | (32,) | + 41 | 41 | | (41,) | + 42 | 47 | | (42,) | + 51 | 56 | | (51,) | + 52 | 57 | | (57,) | + 53 | 58 | | (8,) | + 54 | 4 | | (,108) | + 55 | 5 | | (5,62) | + 61 | | | | + 62 | 2 | | | + 63 | 3 | | | + 64 | 10 | {10,20,30} | | + 65 | 5 | {10,20,30} | | {20,30} + 66 | 6 | {10,20,30} | | {10,20} + 67 | 10 | {10,20,30} | | + 68 | 8 | {10,20,30} | | {20,30} + 69 | 9 | {10,20,30} | | {10,20} + 71 | 1 | | (,) | + 72 | 2 | {71,72,73} | (143,) | + 73 | 3 | {71,72,73} | (144,72) | + 74 | 4 | {71,72,73} | (740,750) | + 81 | 81 | | | {81,82} + 82 | 881 | {881,882} | | {881,882} + 83 | -100 | {81,82,83} | (810,820) | + 84 | -100 | {81,82,83} | (850,83) | + 91 | 1 | {91,92,93} | (94,95) | + 92 | -100 | | (910,920) | {91,92} + 101 | 10 | {107,108,104} | (101,106) | {107,108} + 102 | 20 | {107,108,104} | (102,106) | {107,108} + 103 | 30 | {107,108,104} | (103,106) | {107,108} +(35 rows) + +SELECT * FROM t_default ORDER BY 1; +--? c1 | c2 | c3 +--? +--? 10 | 100 | Sun May 17 00:00:00 2020 +--? 91 | .840485369320959 | Wed Apr 29 10:25:28.439693 2020 +--? 92 | 10 | Wed Apr 29 10:25:28.442916 2020 +(3 rows) + diff --git a/src/test/regress/expected/upsert_prepare.out b/src/test/regress/expected/upsert_prepare.out new file mode 100644 index 000000000..57868e0e4 --- /dev/null +++ b/src/test/regress/expected/upsert_prepare.out @@ -0,0 +1,92 @@ +-- Note: about upsert test +-- test point: +-- grammer +-- table, unlogged table, temp table, matview +-- constraint(primary key, index, foreign key, null ...) +-- trigger +-- sequence +CREATE DATABASE upsert WITH TEMPLATE template0 ENCODING 'UTF8'; +\c upsert +-- grammer test +CREATE SCHEMA upsert_test; +SET CURRENT_SCHEMA TO upsert_test; +CREATE TYPE atype AS(a int, b int); +CREATE TYPE btype AS(a int, b atype, c varchar[3]); +CREATE TABLE t_grammer (c1 INT PRIMARY KEY, c2 INT DEFAULT -100, c3 int[3], c4 atype, c5 int[2]); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_grammer_pkey" for table "t_grammer" +CREATE TABLE t_default (c1 INT PRIMARY KEY DEFAULT 10, c2 FLOAT DEFAULT random(), c3 TIMESTAMP DEFAULT current_timestamp); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_default_pkey" for table "t_default" +CREATE TABLE t_data (c_int INT PRIMARY KEY, c_tiny TINYINT, c_smallint SMALLINT, c_bigint BIGINT, c_numeric NUMERIC, + c_var VARCHAR, c_text TEXT, c_bytea BYTEA, c_date DATE, c_timestamp TIMESTAMP, c_time TIME, + c_intarray INT[3], c_com atype); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_data_pkey" for table "t_data" +CREATE TABLE t_trigger (key INT PRIMARY KEY, color TEXT); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_trigger_pkey" for table "t_trigger" +-- unlogged table +CREATE SCHEMA upsert_test_unlog; +SET CURRENT_SCHEMA TO upsert_test_unlog; +CREATE TYPE atype AS(a int, b int); +CREATE TYPE btype AS(a int, b atype, c varchar[3]); +CREATE UNLOGGED TABLE t_hash_unlog_0 (c1 INT, c2 INT, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype); +CREATE UNLOGGED TABLE t_hash_unlog_1 (c1 INT, c2 INT PRIMARY KEY, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_hash_unlog_1_pkey" for table "t_hash_unlog_1" +CREATE UNLOGGED TABLE t_rep_unlog_0 (c1 INT, c2 INT, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype) ; +CREATE UNLOGGED TABLE t_rep_unlog_1 (c1 INT, c2 INT PRIMARY KEY, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_rep_unlog_1_pkey" for table "t_rep_unlog_1" +-- temp table +-- temp table can not be created here, we create it in upsert_tmp_test. +-- restriction test +CREATE SCHEMA upsert_test_etc; +SET CURRENT_SCHEMA TO upsert_test_etc; +create table up_neg_01 (c1 int, c2 int, c3 int) with (ORIENTATION = COLUMN); +create table up_neg_02 (c1 int, c2 int, c3 int) with (ORIENTATION = COLUMN); +create table up_neg_03 (c1 int, c2 int UNIQUE DEFERRABLE, c3 int); +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_03_c2_key" for table "up_neg_03" +create table up_neg_04 (c1 int, c2 int UNIQUE, c3 int); +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_04_c2_key" for table "up_neg_04" +create table up_neg_05(c1 int, c2 int, c3 int, c4 int unique, c5 int primary key, unique(c2,c3)); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "up_neg_05_pkey" for table "up_neg_05" +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_05_c4_key" for table "up_neg_05" +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_05_c2_c3_key" for table "up_neg_05" +create table up_neg_0(c1 int, c2 int, c3 int, c4 int unique, c5 int primary key, unique(c2,c3)) ; +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "up_neg_0_pkey" for table "up_neg_0" +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_0_c4_key" for table "up_neg_0" +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_0_c2_c3_key" for table "up_neg_0" +create table up_neg_06(c1 int, c2 int, c3 int unique) ; +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_06_c3_key" for table "up_neg_06" +create table up_neg_07(c1 int, c2 int, c3 int unique) ; +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_07_c3_key" for table "up_neg_07" +create table up_neg_08(c1 int, c2 int, c3 int unique) ; +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_08_c3_key" for table "up_neg_08" +create table up_neg_09(c1 int, c2 int, c3 int, unique(c2,c3)) ; +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_09_c2_c3_key" for table "up_neg_09" +create table up_neg_10(c1 int, c2 int, c3 int, unique(c2,c3)) ; +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_10_c2_c3_key" for table "up_neg_10" +create view up_view as select *from up_neg_01; +create materialized view mat_view as select *from up_neg_01; +create table pkt (a int primary key, b int, c int); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pkt_pkey" for table "pkt" +create table fkt (a int primary key, b int references pkt, c int); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "fkt_pkey" for table "fkt" +-- procedure test +CREATE SCHEMA upsert_test_procedure; +-- explain test +CREATE SCHEMA upsert_test_explain; +SET CURRENT_SCHEMA TO upsert_test_explain; +create table up_expl_hash(c1 int, c2 int, c3 int unique) ; +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_expl_hash_c3_key" for table "up_expl_hash" +create table up_expl_repl(c1 int, c2 int, c3 int unique) ; +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_expl_repl_c3_key" for table "up_expl_repl" +create table up_expl_part(c1 int, c2 int, c3 int unique) ; +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_expl_part_c3_key" for table "up_expl_part" +create unlogged table up_expl_unlog(c1 int, c2 int, c3 int unique) ; +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_expl_unlog_c3_key" for table "up_expl_unlog" +create table up_expl_node(c1 int, c2 int, c3 int unique) ; +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_expl_node_c3_key" for table "up_expl_node" +create table up_expl_repl(c1 int, c2 int, c3 int unique) ; +ERROR: relation "up_expl_repl" already exists +create table up_expl_repl2(c1 int, c2 int, c3 int, c4 int unique, c5 int primary key, unique(c2,c3)); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "up_expl_repl2_pkey" for table "up_expl_repl2" +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_expl_repl2_c4_key" for table "up_expl_repl2" +NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_expl_repl2_c2_c3_key" for table "up_expl_repl2" +-- create temp table up_expl_temp(c1 int, c2 int, c3 int unique); diff --git a/src/test/regress/expected/upsert_prepare_etc.out b/src/test/regress/expected/upsert_prepare_etc.out new file mode 100644 index 000000000..3b4fc888a --- /dev/null +++ b/src/test/regress/expected/upsert_prepare_etc.out @@ -0,0 +1,4 @@ +CREATE DATABASE upsert_etc; +\c upsert_etc; +CREATE SCHEMA upsert_test_etc; +SET CURRENT_SCHEMA TO upsert_test_etc; diff --git a/src/test/regress/expected/upsert_restriction.out b/src/test/regress/expected/upsert_restriction.out new file mode 100644 index 000000000..7180de575 --- /dev/null +++ b/src/test/regress/expected/upsert_restriction.out @@ -0,0 +1,229 @@ +/* + * upsert negative test cases + * + table cases + col table + ORC table + VIEW + MAT view + index + DEFERABLE index + insert stmt + returning + with + with recur + update stmt + VALUES with expr + update distribute key + update primary key + update unique key + update partition key + where clause + with clause + sub query + */ +-------------------------------------------------------------------------------------------- +\c upsert +SET CURRENT_SCHEMA TO upsert_test_etc; +-- enable_upsert_to_merge must be off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +SHOW enable_upsert_to_merge; + enable_upsert_to_merge +------------------------ + off +(1 row) + +insert into up_neg_04 select a,a,a from generate_series(1,20) as a; +insert into up_neg_05 select a,a,a,a,a from generate_series(1,20) as a; +insert into up_neg_06 select a,a,a from generate_series(1,20) as a; +insert into up_neg_07 select a,a,a from generate_series(1,20) as a; +insert into up_neg_08 select a,a,a from generate_series(1,20) as a; +insert into up_neg_09 select a,a,a from generate_series(1,20) as a; +insert into up_neg_10 select a,a,a from generate_series(1,20) as a; +-- table cases +---- col table +insert into up_neg_01 values(1,2,3) on duplicate key update c3 = 1; +ERROR: INSERT ON DUPLICATE KEY UPDATE is not supported on column orientated table. +---- ORC table +insert into up_neg_02 values(1,2,3) on duplicate key update c3 = 1; +ERROR: INSERT ON DUPLICATE KEY UPDATE is not supported on column orientated table. +---- VIEW +insert into up_view values(1) on duplicate key update c3 = 1; +ERROR: INSERT ON DUPLICATE KEY UPDATE is not supported on VIEW. +-- matview +refresh materialized view mat_view; +insert into mat_view values(1) on duplicate key update c3 = 1; +ERROR: cannot change materialized view "mat_view" +-- index +---- DEFERABLE index +insert into up_neg_03 values(1,2,3) on duplicate key update c1 = 1; +ERROR: INSERT ON DUPLICATE KEY UPDATE does not support deferrable unique constraints/exclusion constraints. +insert into up_neg_03 values(1) on duplicate key update c1 = 1; +ERROR: INSERT ON DUPLICATE KEY UPDATE does not support deferrable unique constraints/exclusion constraints. +-- insert stmt +----returning +insert into up_neg_04 values(1,1,1) on duplicate key update c1 = 1 returning c1; +ERROR: RETURNING clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement. +----with +with sub as (select *from up_neg_04) +insert into up_neg_04 select *from sub on duplicate key update c1 =1; +ERROR: WITH clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement. +----with recur +with RECURSIVE sub as (select *from up_neg_04) +insert into up_neg_04 select *from sub on duplicate key update c1 =1; +ERROR: WITH clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement. +with sub as (select *from up_neg_04) +insert into up_neg_04 select *from sub on duplicate key update c1 =1 returning c1; +ERROR: RETURNING clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement. +-- update stmt +---- VALUES with expr +insert into up_neg_05 values(1,1,1,1) on duplicate key update c3 = values(1+100); +ERROR: syntax error at or near "1" +LINE 1: ... values(1,1,1,1) on duplicate key update c3 = values(1+100); + ^ +insert into up_neg_05 values(1,1,1,1) on duplicate key update c3 = values(100); +ERROR: syntax error at or near "100" +LINE 1: ...05 values(1,1,1,1) on duplicate key update c3 = values(100); + ^ +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c1 = 1; +---- update primary key +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c5 = 1; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +---- update unique key +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c4 = 1; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c2 = 1, c3=2; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c2 = 1; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c3 = 2; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c1 =1, c2 = 1, c3=2, c4=1,c5=1; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +---- where clause +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c4 = 1 where c1=1; +ERROR: syntax error at or near "where" +LINE 1: ... values(1,1,1,1,1) on duplicate key update c4 = 1 where c1=1... + ^ +---- from clause +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c4 = 1 from up_neg_04 where c1=1; +ERROR: syntax error at or near "from" +LINE 1: ... values(1,1,1,1,1) on duplicate key update c4 = 1 from up_ne... + ^ +---- sub query +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update (c2) = (select c2 from up_neg_05); +ERROR: Update with subquery is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement. +---- update distribute key +insert into up_neg_06 values(101, 1, 1) on duplicate key update c3=101; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +---- update partition key +insert into up_neg_07 values(101, 1, 300) on duplicate key update c3=101; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +insert into up_neg_08 values(101, 1, 300) on duplicate key update c3=101; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +insert into up_neg_09 values(101, 1, 1) on duplicate key update c3=101; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +insert into up_neg_09 values(101, 1, 1) on duplicate key update c2=101; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +insert into up_neg_09 values(101, 1, 1) on duplicate key update c2 =1,c3=101; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +--update unique key mul type plans +EXPLAIN (VERBOSE on, COSTS off) insert into up_neg_10 values(101, 1, 300) on duplicate key update c1=101; + QUERY PLAN +------------------------------------------------- + Insert on upsert_test_etc.up_neg_10 + Conflict Resolution: UPDATE + Conflict Arbiter Indexes: up_neg_10_c2_c3_key + -> Result + Output: 101, 1, 300 +(5 rows) + +insert into up_neg_10 values(101, 1, 300) on duplicate key update c3=101; +ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +--trigger +create table upsert_base +(c1 int primary key, + c2 varchar(100), --record the type + cold varchar(1024), + cnew varchar(1024) ); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "upsert_base_pkey" for table "upsert_base" +create table upsert_tri +(c1 int, + c2 varchar(100), + c3 int, + unique(c1,c3) +); +NOTICE: CREATE TABLE / UNIQUE will create implicit index "upsert_tri_c1_c3_key" for table "upsert_tri" +--S1 after trigger +CREATE OR REPLACE FUNCTION trig_after() RETURNS TRIGGER AS $emp_audit$ + BEGIN + IF (TG_OP = 'INSERT') THEN + insert into upsert_base(c2,cnew) values('after insert',new.c2); + RETURN NEW; + ELSIF (TG_OP = 'UPDATE') THEN + insert into upsert_base(c2,cold,cnew) values('after update',old.c2,new.c2); + RETURN NEW; + END IF; + RETURN NULL; + END; +$emp_audit$ LANGUAGE plpgsql; +CREATE TRIGGER tri_after +AFTER UPDATE OR INSERT ON upsert_tri + FOR EACH ROW EXECUTE PROCEDURE trig_after(); +--S2 before trigger +CREATE OR REPLACE FUNCTION trig_before() RETURNS TRIGGER AS $emp_audit$ + BEGIN + IF (TG_OP = 'INSERT') THEN + insert into upsert_base(c2,cnew) values('before insert',new.c2); + RETURN NEW; + ELSIF (TG_OP = 'UPDATE') THEN + insert into upsert_base(c2,cold,cnew) values('before update',old.c2,new.c2); + RETURN NEW; + END IF; + RETURN NULL; + END; +$emp_audit$ LANGUAGE plpgsql; +CREATE TRIGGER tri_before +BEFORE UPDATE OR INSERT ON upsert_tri + FOR EACH ROW EXECUTE PROCEDURE trig_before(); +---- upsert-insert +insert into upsert_tri(c1,c2,c3) values(1000,'abc',1) on duplicate key update nothing; +ERROR: null value in column "c1" violates not-null constraint +DETAIL: Failing row contains (null, before insert, null, abc). +CONTEXT: SQL statement "insert into upsert_base(c2,cnew) values('before insert',new.c2)" +PL/pgSQL function trig_before() line 4 at SQL statement +insert into upsert_tri(c1,c2,c3) values(2000,'abc',1) on duplicate key update c2='bcd'; +ERROR: null value in column "c1" violates not-null constraint +DETAIL: Failing row contains (null, before insert, null, abc). +CONTEXT: SQL statement "insert into upsert_base(c2,cnew) values('before insert',new.c2)" +PL/pgSQL function trig_before() line 4 at SQL statement +---- upsert-nothing +insert into upsert_tri(c1,c2,c3) values(1000,'abc',1) on duplicate key update nothing; +ERROR: null value in column "c1" violates not-null constraint +DETAIL: Failing row contains (null, before insert, null, abc). +CONTEXT: SQL statement "insert into upsert_base(c2,cnew) values('before insert',new.c2)" +PL/pgSQL function trig_before() line 4 at SQL statement +---- upsert-update +insert into upsert_tri(c1,c2,c3) values(2000,'abc',1) on duplicate key update c2='bcd'; +ERROR: null value in column "c1" violates not-null constraint +DETAIL: Failing row contains (null, before insert, null, abc). +CONTEXT: SQL statement "insert into upsert_base(c2,cnew) values('before insert',new.c2)" +PL/pgSQL function trig_before() line 4 at SQL statement +---- check results +SELECT * from upsert_tri; + c1 | c2 | c3 +----+----+---- +(0 rows) + +SELECT * from upsert_base; + c1 | c2 | cold | cnew +----+----+------+------ +(0 rows) + +-- foreign key +insert into pkt values(1,1,1),(2,2,2),(3,3,3),(4,4,4),(5,5,5); +insert into fkt values(1,1,1),(2,2,2),(3,3,3); +insert into fkt values(1,1,1),(2,2,2),(3,3,3) on duplicate key update b=excluded.b+1, c=0; +insert into fkt values(1,1,1),(2,2,2),(3,3,3) on duplicate key update b=excluded.b+3, c=-1; +ERROR: insert or update on table "fkt" violates foreign key constraint "fkt_b_fkey" +DETAIL: Key (b)=(6) is not present in table "pkt". diff --git a/src/test/regress/expected/upsert_tmp_test.out b/src/test/regress/expected/upsert_tmp_test.out new file mode 100644 index 000000000..c78457643 --- /dev/null +++ b/src/test/regress/expected/upsert_tmp_test.out @@ -0,0 +1,173 @@ +\c upsert +CREATE SCHEMA upsert_test_tmp; +SET CURRENT_SCHEMA TO upsert_test_tmp; +CREATE TYPE atype AS(a int, b int); +CREATE TYPE btype AS(a int, b atype, c varchar[3]); +CREATE TEMP TABLE t_hash_tmp_0 (c1 INT, c2 INT, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype); +CREATE TEMP TABLE t_hash_tmp_1 (c1 INT, c2 INT PRIMARY KEY, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_hash_tmp_1_pkey" for table "t_hash_tmp_1" +CREATE TEMP TABLE t_rep_tmp_0 (c1 INT, c2 INT, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype) ; +CREATE TEMP TABLE t_rep_tmp_1 (c1 INT, c2 INT PRIMARY KEY, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype) ; +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_rep_tmp_1_pkey" for table "t_rep_tmp_1" +-- enable_upsert_to_merge must be off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +SHOW enable_upsert_to_merge; + enable_upsert_to_merge +------------------------ + off +(1 row) + +-- hash table +-- tmp table without primary key +INSERT INTO t_hash_tmp_0 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_hash_tmp_0 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE c2 = 100; +-- tmp table with primary key +-- multi insert +INSERT INTO t_hash_tmp_1 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_hash_tmp_1 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_hash_tmp_1 ORDER BY c2; + c1 | c2 | c3 | c4 | c5 | c6 | c7 +----+----+----+---------+------------------+---------+----------------------------- + 1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 2 | 3 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 2 | 4 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") +(4 rows) + +INSERT INTO t_hash_tmp_1 VALUES(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 2, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_hash_tmp_1 VALUES(20, 3, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (21, 4, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_hash_tmp_1 ORDER BY c2; + c1 | c2 | c3 | c4 | c5 | c6 | c7 +----+----+-----+------------+-----------------------+-----------+---------------------------------- + 1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}") + 21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") +(4 rows) + +-- insert and update same tuple, and update twice for another same tuple +INSERT INTO t_hash_tmp_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')), + (10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE NOTHING; +SELECT * FROM t_hash_tmp_1 ORDER BY c2; + c1 | c2 | c3 | c4 | c5 | c6 | c7 +----+----+-----+------------+-----------------------+-----------+---------------------------------- + 1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}") + 21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") + 0 | 5 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}") +(5 rows) + +INSERT INTO t_hash_tmp_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')), + (10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_hash_tmp_1 ORDER BY c2; + c1 | c2 | c3 | c4 | c5 | c6 | c7 +----+----+-----+------------+-----------------------+-----------+---------------------------------- + 11 | 1 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") + 1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}") + 21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") + 1 | 5 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") +(5 rows) + +-- replication table +-- tmp table without primary key +INSERT INTO t_rep_tmp_0 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_rep_tmp_0 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE c2 = 100; +-- tmp table with primary key +-- multi insert +INSERT INTO t_rep_tmp_1 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_rep_tmp_1 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_rep_tmp_1 ORDER BY c2; + c1 | c2 | c3 | c4 | c5 | c6 | c7 +----+----+----+---------+------------------+---------+----------------------------- + 1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 2 | 3 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 2 | 4 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") +(4 rows) + +INSERT INTO t_rep_tmp_1 VALUES(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 2, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_rep_tmp_1 VALUES(20, 3, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (21, 4, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_rep_tmp_1 ORDER BY c2; + c1 | c2 | c3 | c4 | c5 | c6 | c7 +----+----+-----+------------+-----------------------+-----------+---------------------------------- + 1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}") + 21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") +(4 rows) + +-- insert and update same tuple, and update twice for another same tuple +INSERT INTO t_rep_tmp_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')), + (10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE NOTHING; +SELECT * FROM t_rep_tmp_1 ORDER BY c2; + c1 | c2 | c3 | c4 | c5 | c6 | c7 +----+----+-----+------------+-----------------------+-----------+---------------------------------- + 1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}") + 21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") + 0 | 5 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}") +(5 rows) + +INSERT INTO t_rep_tmp_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')), + (10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_rep_tmp_1 ORDER BY c2; + c1 | c2 | c3 | c4 | c5 | c6 | c7 +----+----+-----+------------+-----------------------+-----------+---------------------------------- + 11 | 1 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") + 1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}") + 21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") + 1 | 5 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") +(5 rows) + +DROP SCHEMA upsert_test_tmp CASCADE; +NOTICE: drop cascades to 10 other objects +DETAIL: drop cascades to type atype +drop cascades to table t_hash_tmp_0 column c6 +drop cascades to table t_hash_tmp_1 column c6 +drop cascades to table t_rep_tmp_0 column c6 +drop cascades to table t_rep_tmp_1 column c6 +drop cascades to type btype +drop cascades to table t_hash_tmp_0 column c7 +drop cascades to table t_hash_tmp_1 column c7 +drop cascades to table t_rep_tmp_0 column c7 +drop cascades to table t_rep_tmp_1 column c7 diff --git a/src/test/regress/expected/upsert_trigger_test.out b/src/test/regress/expected/upsert_trigger_test.out new file mode 100644 index 000000000..b0c3acaa6 --- /dev/null +++ b/src/test/regress/expected/upsert_trigger_test.out @@ -0,0 +1,148 @@ +\c upsert +SET CURRENT_SCHEMA TO upsert_test; +-- enable_upsert_to_merge must be off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +SHOW enable_upsert_to_merge; + enable_upsert_to_merge +------------------------ + off +(1 row) + +CREATE FUNCTION upsert_before_func() + RETURNS TRIGGER language plpgsql AS +$$ +BEGIN + IF (TG_OP = 'UPDATE') THEN + RAISE warning 'before update (old): %', old.*::TEXT; + RAISE warning 'before update (new): %', new.*::TEXT; + elsIF (TG_OP = 'INSERT') THEN + RAISE warning 'before insert (new): %', new.*::TEXT; + IF NEW.key % 2 = 0 THEN + NEW.color := NEW.color || ' trig modified'; + RAISE warning 'before insert (new, modified): %', new.*::TEXT; + END IF; + END IF; + RETURN new; +END; +$$; +CREATE TRIGGER upsert_before_trig BEFORE INSERT OR UPDATE ON t_trigger + FOR EACH ROW EXECUTE procedure upsert_before_func(); +CREATE FUNCTION upsert_after_func() + RETURNS TRIGGER language plpgsql AS +$$ +BEGIN + IF (TG_OP = 'UPDATE') THEN + RAISE warning 'after update (old): %', old.*::TEXT; + RAISE warning 'after update (new): %', new.*::TEXT; + elsIF (TG_OP = 'INSERT') THEN + RAISE warning 'after insert (new): %', new.*::TEXT; + END IF; + RETURN null; +END; +$$; +CREATE TRIGGER upsert_after_trig AFTER INSERT OR UPDATE ON t_trigger + FOR EACH ROW EXECUTE procedure upsert_after_func(); +INSERT INTO t_trigger values(1, 'black') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +WARNING: before insert (new): (1,black) +WARNING: after insert (new): (1,black) +INSERT INTO t_trigger values(2, 'red') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +WARNING: before insert (new): (2,red) +WARNING: before insert (new, modified): (2,"red trig modified") +WARNING: after insert (new): (2,"red trig modified") +INSERT INTO t_trigger values(3, 'orange') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +WARNING: before insert (new): (3,orange) +WARNING: after insert (new): (3,orange) +INSERT INTO t_trigger values(4, 'green') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +WARNING: before insert (new): (4,green) +WARNING: before insert (new, modified): (4,"green trig modified") +WARNING: after insert (new): (4,"green trig modified") +INSERT INTO t_trigger values(5, 'purple') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +WARNING: before insert (new): (5,purple) +WARNING: after insert (new): (5,purple) +INSERT INTO t_trigger values(6, 'white') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +WARNING: before insert (new): (6,white) +WARNING: before insert (new, modified): (6,"white trig modified") +WARNING: after insert (new): (6,"white trig modified") +INSERT INTO t_trigger values(7, 'pink') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +WARNING: before insert (new): (7,pink) +WARNING: after insert (new): (7,pink) +INSERT INTO t_trigger values(8, 'yellow') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +WARNING: before insert (new): (8,yellow) +WARNING: before insert (new, modified): (8,"yellow trig modified") +WARNING: after insert (new): (8,"yellow trig modified") +SELECT * FROM t_trigger ORDER BY key; + key | color +-----+---------------------- + 1 | black + 2 | red trig modified + 3 | orange + 4 | green trig modified + 5 | purple + 6 | white trig modified + 7 | pink + 8 | yellow trig modified +(8 rows) + +INSERT INTO t_trigger values(2, 'black') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +WARNING: before insert (new): (2,black) +WARNING: before insert (new, modified): (2,"black trig modified") +WARNING: before update (old): (2,"red trig modified") +WARNING: before update (new): (2,"updated red trig modified") +WARNING: after update (old): (2,"red trig modified") +WARNING: after update (new): (2,"updated red trig modified") +INSERT INTO t_trigger values(3, 'red') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +WARNING: before insert (new): (3,red) +WARNING: before update (old): (3,orange) +WARNING: before update (new): (3,"updated orange") +WARNING: after update (old): (3,orange) +WARNING: after update (new): (3,"updated orange") +INSERT INTO t_trigger values(4, 'orange') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +WARNING: before insert (new): (4,orange) +WARNING: before insert (new, modified): (4,"orange trig modified") +WARNING: before update (old): (4,"green trig modified") +WARNING: before update (new): (4,"updated green trig modified") +WARNING: after update (old): (4,"green trig modified") +WARNING: after update (new): (4,"updated green trig modified") +INSERT INTO t_trigger values(5, 'green') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +WARNING: before insert (new): (5,green) +WARNING: before update (old): (5,purple) +WARNING: before update (new): (5,"updated purple") +WARNING: after update (old): (5,purple) +WARNING: after update (new): (5,"updated purple") +INSERT INTO t_trigger values(6, 'purple') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +WARNING: before insert (new): (6,purple) +WARNING: before insert (new, modified): (6,"purple trig modified") +WARNING: before update (old): (6,"white trig modified") +WARNING: before update (new): (6,"updated white trig modified") +WARNING: after update (old): (6,"white trig modified") +WARNING: after update (new): (6,"updated white trig modified") +INSERT INTO t_trigger values(7, 'white') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +WARNING: before insert (new): (7,white) +WARNING: before update (old): (7,pink) +WARNING: before update (new): (7,"updated pink") +WARNING: after update (old): (7,pink) +WARNING: after update (new): (7,"updated pink") +INSERT INTO t_trigger values(8, 'pink') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +WARNING: before insert (new): (8,pink) +WARNING: before insert (new, modified): (8,"pink trig modified") +WARNING: before update (old): (8,"yellow trig modified") +WARNING: before update (new): (8,"updated yellow trig modified") +WARNING: after update (old): (8,"yellow trig modified") +WARNING: after update (new): (8,"updated yellow trig modified") +INSERT INTO t_trigger values(9, 'yellow') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +WARNING: before insert (new): (9,yellow) +WARNING: after insert (new): (9,yellow) +SELECT * FROM t_trigger ORDER BY key; + key | color +-----+------------------------------ + 1 | black + 2 | updated red trig modified + 3 | updated orange + 4 | updated green trig modified + 5 | updated purple + 6 | updated white trig modified + 7 | updated pink + 8 | updated yellow trig modified + 9 | yellow +(9 rows) + diff --git a/src/test/regress/expected/upsert_unlog_test.out b/src/test/regress/expected/upsert_unlog_test.out new file mode 100644 index 000000000..3d6c69ea9 --- /dev/null +++ b/src/test/regress/expected/upsert_unlog_test.out @@ -0,0 +1,152 @@ +\c upsert +SET CURRENT_SCHEMA TO upsert_test_unlog; +-- enable_upsert_to_merge must be off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +SHOW enable_upsert_to_merge; + enable_upsert_to_merge +------------------------ + off +(1 row) + +-- hash table +-- unlogged table without primary key +INSERT INTO t_hash_unlog_0 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_hash_unlog_0 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE c2 = 100; +-- unlogged table with primary key +-- multi insert +INSERT INTO t_hash_unlog_1 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_hash_unlog_1 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_hash_unlog_1 ORDER BY c2; + c1 | c2 | c3 | c4 | c5 | c6 | c7 +----+----+----+---------+------------------+---------+----------------------------- + 1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 2 | 3 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 2 | 4 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") +(4 rows) + +INSERT INTO t_hash_unlog_1 VALUES(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 2, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_hash_unlog_1 VALUES(20, 3, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (21, 4, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_hash_unlog_1 ORDER BY c2; + c1 | c2 | c3 | c4 | c5 | c6 | c7 +----+----+-----+------------+-----------------------+-----------+---------------------------------- + 1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}") + 21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") +(4 rows) + +-- insert and update same tuple, and update twice for another same tuple +INSERT INTO t_hash_unlog_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')), + (10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE NOTHING; +SELECT * FROM t_hash_unlog_1 ORDER BY c2; + c1 | c2 | c3 | c4 | c5 | c6 | c7 +----+----+-----+------------+-----------------------+-----------+---------------------------------- + 1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}") + 21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") + 0 | 5 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}") +(5 rows) + +INSERT INTO t_hash_unlog_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')), + (10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_hash_unlog_1 ORDER BY c2; + c1 | c2 | c3 | c4 | c5 | c6 | c7 +----+----+-----+------------+-----------------------+-----------+---------------------------------- + 11 | 1 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") + 1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}") + 21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") + 1 | 5 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") +(5 rows) + +-- replication table +-- unlogged table without primary key +INSERT INTO t_rep_unlog_0 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_rep_unlog_0 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE c2 = 100; +-- unlogged table with primary key +-- multi insert +INSERT INTO t_rep_unlog_1 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_rep_unlog_1 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_rep_unlog_1 ORDER BY c2; + c1 | c2 | c3 | c4 | c5 | c6 | c7 +----+----+----+---------+------------------+---------+----------------------------- + 1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 2 | 3 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 2 | 4 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") +(4 rows) + +INSERT INTO t_rep_unlog_1 VALUES(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 2, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_rep_unlog_1 VALUES(20, 3, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (21, 4, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_rep_unlog_1 ORDER BY c2; + c1 | c2 | c3 | c4 | c5 | c6 | c7 +----+----+-----+------------+-----------------------+-----------+---------------------------------- + 1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}") + 21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") +(4 rows) + +-- insert and update same tuple, and update twice for another same tuple +INSERT INTO t_rep_unlog_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')), + (10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE NOTHING; +SELECT * FROM t_rep_unlog_1 ORDER BY c2; + c1 | c2 | c3 | c4 | c5 | c6 | c7 +----+----+-----+------------+-----------------------+-----------+---------------------------------- + 1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}") + 21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") + 0 | 5 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}") +(5 rows) + +INSERT INTO t_rep_unlog_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')), + (10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_rep_unlog_1 ORDER BY c2; + c1 | c2 | c3 | c4 | c5 | c6 | c7 +----+----+-----+------------+-----------------------+-----------+---------------------------------- + 11 | 1 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") + 1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}") + 20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}") + 21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") + 1 | 5 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}") +(5 rows) + diff --git a/src/test/regress/output/recovery_2pc_tools.source b/src/test/regress/output/recovery_2pc_tools.source index cbec567e8..078f72220 100644 --- a/src/test/regress/output/recovery_2pc_tools.source +++ b/src/test/regress/output/recovery_2pc_tools.source @@ -227,6 +227,7 @@ select name,vartype,unit,min_val,max_val from pg_settings where name <> 'qunit_c enable_tsdb | bool | | | enable_twophase_commit | bool | | | enable_upgrade_merge_lock_mode | bool | | | + enable_upsert_to_merge | bool | | | enable_user_metric_persistent | bool | | | enable_valuepartition_pruning | bool | | | enable_vector_engine | bool | | | diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 0b39cb601..ba166aad8 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -44,12 +44,18 @@ test: hw_smp # test MERGE INTO -# test INSERT UPDATE +# test INSERT UPDATE UPSERT test: insert_update_001 insert_update_002 insert_update_003 insert_update_008 insert_update_009 insert_update_010 test: delete update namespace case select_having select_implicit test: hw_test_operate_user test: hw_createtbl_llt gsqlerr test: hw_sql_llt sqlLLT +test: upsert_prepare +test: upsert_001 upsert_002 upsert_003 upsert_008 upsert_009 upsert_010 +test: upsert_grammer_test_01 upsert_unlog_test upsert_tmp_test +test: upsert_grammer_test_02 upsert_restriction upsert_composite +test: upsert_trigger_test upsert_explain +test: upsert_clean # all pass # run tablespace by itself, and first, because it forces a checkpoint; diff --git a/src/test/regress/parallel_schedule3 b/src/test/regress/parallel_schedule3 index 1f8d55795..644da549f 100644 --- a/src/test/regress/parallel_schedule3 +++ b/src/test/regress/parallel_schedule3 @@ -17,3 +17,11 @@ test: delete update namespace case select_having select_implicit test: hw_test_operate_user test: hw_createtbl_llt gsqlerr test: hw_sql_llt sqlLLT + +# test UPSERT +test: upsert_prepare +test: upsert_001 upsert_002 upsert_003 upsert_008 upsert_009 upsert_010 +test: upsert_grammer_test_01 upsert_unlog_test upsert_tmp_test +test: upsert_grammer_test_02 upsert_restriction upsert_composite +test: upsert_trigger_test upsert_explain +test: upsert_clean diff --git a/src/test/regress/sql/insert_update_001.sql b/src/test/regress/sql/insert_update_001.sql index e230ed0ae..abe03deb4 100644 --- a/src/test/regress/sql/insert_update_001.sql +++ b/src/test/regress/sql/insert_update_001.sql @@ -2,6 +2,10 @@ DROP SCHEMA test_insert_update_001 CASCADE; CREATE SCHEMA test_insert_update_001; SET CURRENT_SCHEMA TO test_insert_update_001; +-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge, +-- real upsert will be tested in specialized case. +SET enable_upsert_to_merge TO ON; + -- test description \h INSERT diff --git a/src/test/regress/sql/insert_update_002.sql b/src/test/regress/sql/insert_update_002.sql index e1bbe1720..84238b80e 100644 --- a/src/test/regress/sql/insert_update_002.sql +++ b/src/test/regress/sql/insert_update_002.sql @@ -2,6 +2,10 @@ DROP SCHEMA test_insert_update_002 CASCADE; CREATE SCHEMA test_insert_update_002; SET CURRENT_SCHEMA TO test_insert_update_002; +-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge, +-- real upsert will be tested in specialized case. +SET enable_upsert_to_merge TO ON; + -- test t1 with no index CREATE TABLE t1 ( col1 INT, diff --git a/src/test/regress/sql/insert_update_003.sql b/src/test/regress/sql/insert_update_003.sql index fc82b796d..5d687f084 100644 --- a/src/test/regress/sql/insert_update_003.sql +++ b/src/test/regress/sql/insert_update_003.sql @@ -2,6 +2,10 @@ DROP SCHEMA test_insert_update_003 CASCADE; CREATE SCHEMA test_insert_update_003; SET CURRENT_SCHEMA TO test_insert_update_003; +-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge, +-- real upsert will be tested in specialized case. +SET enable_upsert_to_merge TO ON; + -- test t4 with one primary key with three columns CREATE TABLE t4 ( col1 INT, diff --git a/src/test/regress/sql/insert_update_008.sql b/src/test/regress/sql/insert_update_008.sql index 6cb674b68..7caa71495 100644 --- a/src/test/regress/sql/insert_update_008.sql +++ b/src/test/regress/sql/insert_update_008.sql @@ -6,6 +6,10 @@ CREATE SCHEMA test_insert_update_008; SET current_schema = test_insert_update_008; +-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge, +-- real upsert will be tested in specialized case. +SET enable_upsert_to_merge TO ON; + CREATE TABLE products_base ( product_id INTEGER DEFAULT 0, @@ -66,9 +70,9 @@ EXPLAIN (VERBOSE on, COSTS off) INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; EXPLAIN (VERBOSE on, COSTS off) INSERT INTO products_row @@ -79,18 +83,18 @@ INSERT INTO products_row FROM newproducts_row, products_row WHERE products_row.total + newproducts_row.total < 1000 ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; EXPLAIN (VERBOSE on, COSTS off) INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row WHERE product_id IS NOT NULL AND product_name IS NOT NULL ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; -- explain analyze BEGIN; @@ -98,9 +102,9 @@ EXPLAIN (ANALYZE on, COSTS off, TIMING off) INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; ROLLBACK; -- explain performance @@ -110,9 +114,9 @@ EXPLAIN PERFORMANCE INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; ROLLBACK; \o @@ -122,9 +126,9 @@ EXPLAIN (ANALYZE on, COSTS off, TIMING off) INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; ROLLBACK; -- pretty mode performance @@ -135,9 +139,9 @@ EXPLAIN (VERBOSE on, COSTS off) INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; -- explain analyze BEGIN; @@ -145,9 +149,9 @@ EXPLAIN (ANALYZE on, COSTS off, TIMING off) INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; ROLLBACK; -- explain analyze @@ -156,9 +160,9 @@ EXPLAIN (ANALYZE on, COSTS off, TIMING off) INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; ROLLBACK; -- explain performance @@ -168,9 +172,9 @@ EXPLAIN PERFORMANCE INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; ROLLBACK; SET explain_perf_mode = run; @@ -180,9 +184,9 @@ EXPLAIN PERFORMANCE INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; ROLLBACK; SET explain_perf_mode = summary; @@ -192,9 +196,9 @@ EXPLAIN PERFORMANCE INSERT INTO products_row SELECT product_id, product_name, category, total FROM newproducts_row ON DUPLICATE KEY UPDATE - product_name = __unnamed_subquery_source__.product_name, - category = __unnamed_subquery_source__.category, - total = __unnamed_subquery_source__.total; + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; ROLLBACK; \o diff --git a/src/test/regress/sql/insert_update_009.sql b/src/test/regress/sql/insert_update_009.sql index 8ba6b0799..25632efce 100644 --- a/src/test/regress/sql/insert_update_009.sql +++ b/src/test/regress/sql/insert_update_009.sql @@ -2,6 +2,11 @@ DROP SCHEMA test_insert_update_009 CASCADE; CREATE SCHEMA test_insert_update_009; SET CURRENT_SCHEMA TO test_insert_update_009; SET enable_light_proxy=off; + +-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge, +-- real upsert will be tested in specialized case. +SET enable_upsert_to_merge TO ON; + -- test t1 with no index CREATE TABLE t1 ( col1 INT, diff --git a/src/test/regress/sql/insert_update_010.sql b/src/test/regress/sql/insert_update_010.sql index 1a7929570..7fa9995a3 100644 --- a/src/test/regress/sql/insert_update_010.sql +++ b/src/test/regress/sql/insert_update_010.sql @@ -1,6 +1,11 @@ DROP SCHEMA test_insert_update_010 CASCADE; CREATE SCHEMA test_insert_update_010; SET CURRENT_SCHEMA TO test_insert_update_010; + +-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge, +-- real upsert will be tested in specialized case. +SET enable_upsert_to_merge TO ON; + -- test t4 with one primary key with three columns CREATE TABLE t4 ( col1 INT, diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql index 171fc7903..37b6f8c77 100644 --- a/src/test/regress/sql/rangefuncs.sql +++ b/src/test/regress/sql/rangefuncs.sql @@ -1,3 +1,6 @@ +-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge, +-- real upsert will be tested in specialized case. +SET enable_upsert_to_merge TO ON; SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%' ORDER BY name; diff --git a/src/test/regress/sql/upsert_001.sql b/src/test/regress/sql/upsert_001.sql new file mode 100644 index 000000000..dc39b4c39 --- /dev/null +++ b/src/test/regress/sql/upsert_001.sql @@ -0,0 +1,284 @@ +DROP SCHEMA test_upsert_001 CASCADE; +CREATE SCHEMA test_upsert_001; +SET CURRENT_SCHEMA TO test_upsert_001; + +-- enable_upsert_to_merge must is off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; + +-- test description +\h INSERT + +-- test permission +--- test with no sequence column +CREATE TABLE t00 (col1 INT DEFAULT 1 PRIMARY KEY, col2 INT); +CREATE USER upsert_tester PASSWORD '123456@cc'; +GRANT ALL PRIVILEGES ON SCHEMA test_upsert_001 TO upsert_tester; + +SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc'; +INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 5; +RESET SESSION AUTHORIZATION; + +---- error: only have INSERT permission +GRANT INSERT ON test_upsert_001.t00 TO upsert_tester; +SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc'; +INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 5; +RESET SESSION AUTHORIZATION; + +---- success: have INSERT UPDATE permission +GRANT INSERT, UPDATE ON test_upsert_001.t00 TO upsert_tester; +SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc'; +INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 5; +RESET SESSION AUTHORIZATION; + +--- have SELECT INSERT UPDATE permission +GRANT SELECT, INSERT, UPDATE ON test_upsert_001.t00 TO upsert_tester; +SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc'; +INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 5; +INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col3 = 5; +RESET SESSION AUTHORIZATION; + +--- test with sequnce column +CREATE TABLE t01 (col1 INT , col2 BIGSERIAL PRIMARY KEY, col3 INT) ; + +---- error: don't have UPDATE permission on sequence table. +GRANT SELECT, INSERT, UPDATE ON test_upsert_001.t01 TO upsert_tester; +SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc'; +INSERT INTO test_upsert_001.t01 VALUES(1) ON DUPLICATE KEY UPDATE col3 = 5; +RESET SESSION AUTHORIZATION; + +---- have SELECT INSERT UPDATE permission on target relation, and UPDATE permission on sequence table. +GRANT UPDATE ON test_upsert_001.t01_col2_seq TO upsert_tester; +SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc'; +INSERT INTO test_upsert_001.t01 VALUES(1) ON DUPLICATE KEY UPDATE col3 = 5; +INSERT INTO test_upsert_001.t01 VALUES(1) ON DUPLICATE KEY UPDATE col3 = 5; +RESET SESSION AUTHORIZATION; + +-- test ommit INSERT target column +CREATE TABLE t02 (col1 INT DEFAULT 1 PRIMARY KEY, col2 INT, col3 INT); +INSERT INTO t02 VALUES(1, 2, 3) ON DUPLICATE KEY UPDATE col2 = 20; +SELECT * FROM t02 ORDER BY 1, 2; +INSERT INTO t02 VALUES(1, 2, 3) ON DUPLICATE KEY UPDATE col2 = 20; +SELECT * FROM t02 ORDER BY 1, 2; + +ALTER TABLE t02 DROP COLUMN col2; +ALTER TABLE t02 ADD COLUMN col4 INT; +INSERT INTO t02 VALUES(1, 2, 3) ON DUPLICATE KEY UPDATE col4 = 40; +SELECT * FROM t02 ORDER BY 1, 2; +INSERT INTO t02 VALUES(2, 3, 4) ON DUPLICATE KEY UPDATE col4 = 40; +SELECT * FROM t02 ORDER BY 1, 2; +INSERT INTO t02 VALUES(2, 3, 4) ON DUPLICATE KEY UPDATE col4 = 40; +SELECT * FROM t02 ORDER BY 1, 2; + +-- test restriction +--- test replication table +CREATE TABLE t03 (col1 int PRIMARY KEY, col2 INT, col3 smallserial) ; +--- error: not allowed volatile function as default value +INSERT INTO t03(col2) VALUES(1) ON DUPLICATE KEY UPDATE col2 = 100; + +ALTER TABLE t03 DROP COLUMN col3; +--- error: primary key are not allowed to update +INSERT INTO t03 VALUES(1) ON DUPLICATE KEY UPDATE col1 = 1; +--- error: clause other than VALUSES are not allowed to use +INSERT INTO t03 SELECT * FROM t03 ON DUPLICATE KEY UPDATE col2 = 1; +--- success: expression index are supported +CREATE UNIQUE INDEX u_expr_index ON t03 USING btree (abs(col1)); +INSERT INTO t03 VALUES(-10, 10) ON DUPLICATE KEY UPDATE col2 = 20; +DROP INDEX u_expr_index; + +-- test with stream operator on +INSERT INTO t03 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 100; +SELECT * FROM t03; + +INSERT INTO t03 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 100; +SELECT * FROM t03; +SELECT * FROM t03; + +--- test PBE +PREPARE p1 AS INSERT INTO t03 VALUES($1, $2) ON DUPLICATE KEY UPDATE col2 = $1*100; +EXECUTE p1(5, 50); +SELECT * FROM t03 WHERE col1 = 5; +EXECUTE p1(5, 50); +SELECT * FROM t03 WHERE col1 = 5; +DELETE t03 WHERE col1 = 5; + +---- test with primary key +INSERT INTO t03 VALUES(2) ON DUPLICATE KEY UPDATE col2 = 200; +SELECT * FROM t03; + +INSERT INTO t03 VALUES(2) ON DUPLICATE KEY UPDATE col2 = 200; +SELECT * FROM t03; +SELECT * FROM t03; + +---- test with unique key without NOT NULL constraint +ALTER TABLE t03 DROP CONSTRAINT t03_pkey; +ALTER TABLE t03 ADD COLUMN col3 INT; +CREATE UNIQUE INDEX ON t03 (col1, col3); +---- unique constraints might contain NULL, depends on the plan +----- for cn light and fqs, it can be done +INSERT INTO t03 VALUES(3) ON DUPLICATE KEY UPDATE col2 = 300; +----- for stream or pgxc it can not be done +INSERT INTO t03 VALUES(3) ON DUPLICATE KEY UPDATE col2 = 300; + +---- test with unique key with NOT NULL constraint, should success +CREATE UNIQUE INDEX ON t03 (col1); +INSERT INTO t03 VALUES(3) ON DUPLICATE KEY UPDATE col2 = 300; +SELECT * FROM t03; + +INSERT INTO t03 VALUES(3) ON DUPLICATE KEY UPDATE col2 = 300; +SELECT * FROM t03; +SELECT * FROM t03; + +---- test PBE +PREPARE p2 AS INSERT INTO t03 VALUES($1, $2) ON DUPLICATE KEY UPDATE col2 = $1*100; +EXECUTE p2(5, 50); +SELECT * FROM t03 WHERE col1 = 5; +EXECUTE p2(5, 50); +SELECT * FROM t03 WHERE col1 = 5; + +--- error: test with clause +WITH tmp(col1, col2) AS (SELECT * FROM t01) +INSERT INTO t01 SELECT * FROM tmp ON DUPLICATE KEY UPDATE col1 = 1; + +WITH RECURSIVE rq AS + ( + SELECT col1, col2 FROM t00 WHERE col1 = 1 + UNION ALL + SELECT origin.col1, rq.col2 + FROM rq JOIN t00 AS origin ON origin.col1 = rq.col1 + ) + INSERT INTO t03 SELECT * FROM rq ON DUPLICATE KEY UPDATE col1 = rq.col1; + +--- error: test returning clause +INSERT INTO t01 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 1 RETURNING NOT(1::bool); + +--- error: distribute key are not allowed to UPDATE +CREATE TABLE t04 (col1 INT, col2 INT) ; +INSERT INTO t04 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 5; + +--- error: unique index referenced column are not allowed to UPDATE +CREATE UNIQUE INDEX t04_u_index ON t04(col1, col2); +INSERT INTO t04 VALUES (1) ON DUPLICATE KEY UPDATE col2 = 5; +DROP INDEX t04_u_index; + +--- error: primary key referenced column are not allowed to UPDATE +ALTER TABLE t04 ADD PRIMARY KEY (col1, col2); +INSERT INTO t04 VALUES (1) ON DUPLICATE KEY UPDATE col2 = 5; + +--- error: invalid column +INSERT INTO t04 (col2, col3) VALUES (2, 3) ON DUPLICATE KEY UPDATE col2 = 5; + +--- error: duplicate column +INSERT INTO t04 (col2, col2) VALUES (2, 3) ON DUPLICATE KEY UPDATE col2 = 5; + +-- error: target column more than insert target +INSERT INTO t04 (col1, col2) VALUES (2, 3, 4) ON DUPLICATE KEY UPDATE col2 = 5; +INSERT INTO t04 (col1, col2) VALUES (1) ON DUPLICATE KEY UPDATE col2 = 5; +INSERT INTO t04 (col1, col2) SELECT col1 FROM t04 ON DUPLICATE KEY UPDATE col2 = 5; +INSERT INTO t04 (col1, col2) SELECT *, col1 FROM t04 ON DUPLICATE KEY UPDATE col2 = 5; +INSERT INTO t04 VALUES (2, 3, 4) ON DUPLICATE KEY UPDATE col2 = 5; +INSERT INTO t04 VALUES (1) ON DUPLICATE KEY UPDATE col2 = 5; +INSERT INTO t04 SELECT col1 FROM t04 ON DUPLICATE KEY UPDATE col2 = 5; +INSERT INTO t04 SELECT *, col1 FROM t04 ON DUPLICATE KEY UPDATE col2 = 5; + +-- test DEFAULT VALUES +TRUNCATE t00; +TRUNCATE t01; + +--- without sequence +----should insert +INSERT INTO t00 DEFAULT VALUES ON DUPLICATE KEY UPDATE col2 = col1; +SELECT * FROM t00 ORDER BY 1, 2; +---- should update +INSERT INTO t00 DEFAULT VALUES ON DUPLICATE KEY UPDATE col2 = col1; +SELECT * FROM t00 ORDER BY 1, 2; + +--- test drop column +TRUNCATE t00; +ALTER TABLE t00 DROP COLUMN col2; +ALTER TABLE t00 ADD COLUMN col3 INT DEFAULT 100; +----should insert +INSERT INTO t00 DEFAULT VALUES ON DUPLICATE KEY UPDATE col3 = col1; +SELECT * FROM t00 ORDER BY 1, 2; +---- should update +INSERT INTO t00 DEFAULT VALUES ON DUPLICATE KEY UPDATE col3 = col1; +SELECT * FROM t00 ORDER BY 1, 2; + +--- with sequence +----should insert +INSERT INTO t01 DEFAULT VALUES ON DUPLICATE KEY UPDATE col1 = col2; +INSERT INTO t01 (col2) SELECT col2 + 1 FROM t01 LIMIT 1; +SELECT * FROM t01 ORDER BY 1, 2, 3; + +---- should update +INSERT INTO t01 DEFAULT VALUES ON DUPLICATE KEY UPDATE col1 = col2; +SELECT * FROM t01 ORDER BY 1, 2, 3; + +-- test VALUES(DEFAULT) +CREATE TABLE t05 (col1 INT , col2 INT DEFAULT 1 PRIMARY KEY, col3 INT DEFAULT 100) ; +--- should insert +INSERT INTO t05 VALUES(DEFAULT) ON DUPLICATE KEY UPDATE col3 = 1000; +SELECT * FROM t05 ORDER BY 1, 2, 3; + +--- should update +INSERT INTO t05 VALUES(DEFAULT) ON DUPLICATE KEY UPDATE col3 = 1000; +SELECT * FROM t05 ORDER BY 1, 2, 3; + +-- test UPDATE DEFAULT +INSERT INTO t05 (col1, col2, col3) VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col3 = DEFAULT; +SELECT * FROM t05 ORDER BY 1, 2, 3; +INSERT INTO t05 (col1, col2, col3) VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col3 = DEFAULT; +SELECT * FROM t05 ORDER BY 1, 2, 3; + +-- test VALUES (DEFAULT, ...) +TRUNCATE t05; +--- should insert +INSERT INTO t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT) +ON DUPLICATE KEY UPDATE col3 = DEFAULT, col1 = col3; +SELECT * FROM t05 ORDER BY 1, 2, 3; + +--- should update +INSERT INTO t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT) +ON DUPLICATE KEY UPDATE col3 = DEFAULT, col1 = col3; +SELECT * FROM t05 ORDER BY 1, 2, 3; + +--- test drop coulmn +BEGIN; +ALTER TABLE t05 ADD COLUMN col4 INT; +ALTER TABLE t05 ADD COLUMN col5 INT DEFAULT 500; +ALTER TABLE t05 DROP COLUMN col3; +TRUNCATE t05; +INSERT INTO t05 VALUES(DEFAULT, DEFAULT, DEFAULT, 600) ON DUPLICATE KEY UPDATE col5 = DEFAULT; +SELECT * FROM t05 ORDER BY 1, 2, 3; +INSERT INTO t05 VALUES(DEFAULT, DEFAULT, DEFAULT, 600) ON DUPLICATE KEY UPDATE col5 = DEFAULT; +SELECT * FROM t05 ORDER BY 1, 2, 3; +ROLLBACK; + +-- test schema +SET current_schema = public; +TRUNCATE test_upsert_001.t05; +--- should insert +INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT) +ON DUPLICATE KEY UPDATE col3 = DEFAULT, col1 = col3; +SELECT * FROM test_upsert_001.t05 ORDER BY 1, 2, 3; + +--- should update +INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT) +ON DUPLICATE KEY UPDATE col3 = DEFAULT, col1 = col3; +SELECT * FROM test_upsert_001.t05 ORDER BY 1, 2, 3; + +--- test using schema on update +INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT) +ON DUPLICATE KEY UPDATE t05.col3 = DEFAULT, t05.col1 = t05.col3 + 1; +SELECT * FROM test_upsert_001.t05 ORDER BY 1, 2, 3; + +--- error: should not append schema +INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT) +ON DUPLICATE KEY UPDATE test_upsert_001.t05.col3 = DEFAULT, t05.col1 = t05.col3 + 1; + +INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT) +ON DUPLICATE KEY UPDATE t05.col3 = DEFAULT, t05.col1 = test_upsert_001.t05.col3 + 1; + +SET CURRENT_SCHEMA TO test_upsert_001; + +DROP USER upsert_tester CASCADE; +DROP SCHEMA test_upsert_001 CASCADE; diff --git a/src/test/regress/sql/upsert_002.sql b/src/test/regress/sql/upsert_002.sql new file mode 100644 index 000000000..66cd71825 --- /dev/null +++ b/src/test/regress/sql/upsert_002.sql @@ -0,0 +1,279 @@ +DROP SCHEMA test_upsert_002 CASCADE; +CREATE SCHEMA test_upsert_002; +SET CURRENT_SCHEMA TO test_upsert_002; + +-- enable_upsert_to_merge must is off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; + +-- test t1 with no index +CREATE TABLE t1 ( + col1 INT, + col2 INT, + col3 INT DEFAULT 1, +-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 BIGSERIAL +) ; + +INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE col1 = 3; + +--- should always insert +INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE col2 = 3; +INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE t1.col2 = 4; + +--- appoint column list in insert clause, should always insert +INSERT INTO t1(col1, col3) VALUES (1, 3) ON DUPLICATE KEY UPDATE col2 = 5; +INSERT INTO t1(col1, col3) VALUES (1, 3) ON DUPLICATE KEY UPDATE t1.col2 = 6; + +--- multiple rows, should always insert +INSERT INTO t1 VALUES (2, 1), (2, 1) ON DUPLICATE KEY UPDATE col2 = 7, col3 = 7; +SELECT * FROM t1 ORDER BY col5; + +--- test union, should insert +INSERT INTO t1 (col1, col2) + SELECT * FROM + (SELECT col1, col2 FROM t1 + UNION + SELECT col1, col3 FROM t1) AS union_table + ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3) * 10; +SELECT col1, col2, col3 FROM t1 WHERE col5 > 6 ORDER BY col1, col2; + +--- test subquery, should insert +INSERT INTO t1 + (SELECT col1 || col2 || '00' FROM t1 ORDER BY col5) + ON DUPLICATE KEY UPDATE col3 = col1 * 100; +SELECT col1, col2, col3 FROM t1 WHERE col1 >= 100 ORDER BY col1; + +-- test t2 with one primary key +CREATE TABLE t2 ( + col1 INT, + col2 INT PRIMARY KEY, + col3 INT DEFAULT 1, + col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 BIGSERIAL +) ; + +--- primary key or unique key are not allowed to update +INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE col2 = 3; +INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE t2.col2 = 3; +INSERT INTO t2 (col2, col3, col4, col5) + VALUES (10, 10, CURRENT_TIMESTAMP(0), 10), + (20, 20, CURRENT_TIMESTAMP(1), 20), + (30, 30, CURRENT_TIMESTAMP(2), 30) + ON DUPLICATE KEY UPDATE + col1 = 100, + col2 = 100, + col3 = 100, + col4 = '2019-08-09'::TIMESTAMP, + col5 = 100; + +--- should insert +INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 30; +INSERT INTO t2 VALUES (2, 2) ON DUPLICATE KEY UPDATE t2.col1 = 40; +INSERT INTO t2 VALUES (3, 3) ON DUPLICATE KEY UPDATE col1 = col1 * 2; +INSERT INTO t2 VALUES (4, 4) ON DUPLICATE KEY UPDATE t2.col1 = t2.col1 * 2 ; +INSERT INTO t2 VALUES (5, 5) ON DUPLICATE KEY UPDATE col1 = col2 + 1; +INSERT INTO t2 VALUES (6, 6) ON DUPLICATE KEY UPDATE t2.col1 = t2.col2 + 1; +INSERT INTO t2 VALUES (7, 7) ON DUPLICATE KEY UPDATE col1 = extract(dow from col4) + 10; +INSERT INTO t2 VALUES (8, 8) ON DUPLICATE KEY UPDATE t2.col1 = extract(century from col4) * 100 + extract(isodow from col4); +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5; + +--- should update +INSERT INTO t2 VALUES (3, 1) ON DUPLICATE KEY UPDATE col1 = 30, col3 = col5 + 1; +INSERT INTO t2 VALUES (4, 2) ON DUPLICATE KEY UPDATE t2.col1 = 40, t2.col3 = t2.col5; +INSERT INTO t2 VALUES (3, 3), (4, 4) ON DUPLICATE KEY UPDATE col3 = t2.col5 + 1; +INSERT INTO t2 VALUES (5, 5) ON DUPLICATE KEY UPDATE col1 = extract(dow from col4) + 10; +INSERT INTO t2 VALUES (6, 6) ON DUPLICATE KEY UPDATE t2.col1 = extract(century from col4) * 100 + extract(isodow from col4); +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5; + +-- primary key are not allowed to be null +INSERT INTO t2 (col1) VALUES (10) ON DUPLICATE KEY UPDATE col1 = 10; + +--- appoint column list in insert clause +---- should insert +INSERT INTO t2 (col2, col3) VALUES (9, 9) ON DUPLICATE KEY UPDATE col1 = 90; +INSERT INTO t2 (col2, col3, col4, col5) + VALUES (10, 10, CURRENT_TIMESTAMP(0), 10), + (20, 20, CURRENT_TIMESTAMP(1), 20), + (30, 30, CURRENT_TIMESTAMP(2), 30) + ON DUPLICATE KEY UPDATE + col1 = 100, + col3 = 100, + col4 = '2019-08-20'::TIMESTAMP, + col5 = 100; +SELECT * FROM t2 ORDER BY col5; + +---- should update +INSERT INTO t2 (col2, col3) VALUES (9, 9) ON DUPLICATE KEY UPDATE col1 = 90; +INSERT INTO t2 (col2, col3, col4, col5) + VALUES (10, 10, CURRENT_TIMESTAMP(0), 10), + (20, 20, CURRENT_TIMESTAMP(1), 20), + (30, 30, CURRENT_TIMESTAMP(2), 30) + ON DUPLICATE KEY UPDATE + col1 = 100, + col3 = 100, + col4 = '2019-08-20'::TIMESTAMP, + col5 = 100; +SELECT * FROM t2 ORDER BY col5, col2; + +--- test subquery +---- should insert +INSERT INTO t2 + (SELECT col1 * 1000, col2 * 1000 + 1 FROM t2 ORDER BY col5 LIMIT 2) + ON DUPLICATE KEY UPDATE col3 = col2 + 1; +INSERT INTO t2 (col2, col3) + (SELECT col1 * 1000 + 2, col2 * 1000 FROM t2 ORDER BY col5 LIMIT 2 OFFSET 1) + ON DUPLICATE KEY UPDATE col3 = col2 + 1; +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2; + +---- should update +INSERT INTO t2 + (SELECT col1 * 1000, col2 * 1000 + 1 FROM t2 ORDER BY col5 LIMIT 2) + ON DUPLICATE KEY UPDATE col3 = col2 + 1; +INSERT INTO t2 (col2, col3) + (SELECT col1 * 1000 + 2, col2 * 1000 FROM t2 ORDER BY col5 LIMIT 2 OFFSET 1) + ON DUPLICATE KEY UPDATE col3 = col2 + 1; +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2; + +--- test union, some insert some update +SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT col1, col3 FROM t1 ORDER BY 1, 2; +INSERT INTO t2 (col1, col2) + SELECT * FROM + (SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT col1, col3 FROM t1) AS union_table + ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3); + +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2; + +INSERT INTO t2 (col1, col2) + SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT col1, col3 FROM t1 + ON DUPLICATE KEY UPDATE col3 = col3 + 1; +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2; +reset behavior_compat_options; + +-- test INTERSECT, should update +(SELECT col1, col1 + col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT 1, 2) + INTERSECT + SELECT col1, col3 FROM t1; + +INSERT INTO t2 (col1, col2) + (SELECT col1, col1 + col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT 1, 2) + INTERSECT + SELECT col1, col3 FROM t1 + ON DUPLICATE KEY UPDATE col3 = col3 + 1; +SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 3 ORDER BY col5, col2; + +-- test EXCEPT, should update +SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + EXCEPT + (SELECT col1, col3 FROM t1 + UNION + SELECT NULL, NULL); + +INSERT INTO t2 (col1, col2) + SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + EXCEPT + (SELECT col1, col3 FROM t1 + UNION + SELECT NULL, NULL) + ON DUPLICATE KEY UPDATE col3 = col3 + 1; +SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 2 ORDER BY col5, col2; + +-- test unique index with not default value +ALTER TABLE t2 DROP CONSTRAINT t2_pkey; +CREATE UNIQUE INDEX t2_u_index ON t2(col2, col5); +SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + EXCEPT + (SELECT col1, col3 FROM t1 + UNION + SELECT NULL, NULL); +INSERT INTO t2 + SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + EXCEPT + (SELECT col1, col3 FROM t1 + UNION + SELECT NULL, NULL) + ON DUPLICATE KEY UPDATE col3 = col3 + 1; +SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 2 ORDER BY col5, col2; + +-- test t3 with one primary index with two columns +CREATE TABLE t3 ( + col1 INT, + col2 INT, + col3 INT DEFAULT 1, +-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 BIGSERIAL, + PRIMARY KEY (col2, col3) +) ; + +--- column referenced by primary key are not allowed to update +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col3 = 3; + +--- should insert when not contains primary key and not all primary key referred columns have default value. +--- but will fail cause primary key should not be null +INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2; + +--- should insert +--- (SEQUENCE BUG: the serial column will starts from 2 since the above statement has applied for a sequence) +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20; +SELECT * FROM t3 order by col5; + +--- should update +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20; +SELECT * FROM t3 order by col5; + +--- test subquery +---- should insert +INSERT INTO t3 (col2, col3) (SELECT max(col2) + 1, max(col3) + 1 FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +SELECT * FROM t3 order by col5; + +---- should update +INSERT INTO t3 (col2, col3) (SELECT max(col2), max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +SELECT * FROM t3 order by col5; + +-- test t3 with one unique index with two columns +TRUNCATE t3; +ALTER TABLE t3 DROP CONSTRAINT t3_pkey; +ALTER TABLE t3 ALTER COLUMN col2 DROP NOT NULL; +CREATE UNIQUE INDEX t3_ukey ON t3 (col2, col3); + +--- column referenced by unique key are not allowed to update +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col3 = 3; + +--- should insert cause not contains unique key and not all unique key referred columns have default value. +INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2; +INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2; +SELECT * FROM t3 order by col5; + +--- should insert +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE t3.col5 = 20; +SELECT * FROM t3 ORDER BY col5; + +--- should update +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE t3.col1 = 10; +INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20; +SELECT * FROM t3 ORDER BY col5; + +--- test subquery +---- should insert +INSERT INTO t3 (SELECT 100, NULL, max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +INSERT INTO t3 (SELECT 100, NULL, max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +INSERT INTO t3 (col2, col3) (SELECT max(col2) + 1, max(col3) + 1 FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +SELECT * FROM t3 ORDER BY col5; + +---- should update +INSERT INTO t3 (col2, col3) (SELECT max(col2), max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +SELECT * FROM t3 ORDER BY col5; + +DROP SCHEMA test_upsert_002 CASCADE; diff --git a/src/test/regress/sql/upsert_003.sql b/src/test/regress/sql/upsert_003.sql new file mode 100644 index 000000000..9e11df7c5 --- /dev/null +++ b/src/test/regress/sql/upsert_003.sql @@ -0,0 +1,144 @@ +DROP SCHEMA test_insert_update_003 CASCADE; +CREATE SCHEMA test_insert_update_003; +SET CURRENT_SCHEMA TO test_insert_update_003; + +-- enable_upsert_to_merge must is off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; + +-- test t4 with one primary key with three columns +CREATE TABLE t4 ( + col1 INT, + col2 INT DEFAULT 0, + col3 INT DEFAULT 1, + col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 BIGSERIAL, + PRIMARY KEY (col2, col3, col5) +) ; + +--- should insert +INSERT INTO t4 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t4 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t4 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col1 = 200; +INSERT INTO t4 VALUES (100, 100, 100, CURRENT_TIMESTAMP, 100) ON DUPLICATE KEY UPDATE col1 = 1000; +SELECT col1, col2, col3, col5 FROM t4 ORDER BY col5; + +--- should update +INSERT INTO t4 VALUES (2, 2, 2, CURRENT_TIMESTAMP, 3) ON DUPLICATE KEY UPDATE col1 = 200; +INSERT INTO t4 VALUES (100, 100, 100, CURRENT_TIMESTAMP, 100) ON DUPLICATE KEY UPDATE col1 = 1000; +SELECT col1, col2, col3, col5 FROM t4 ORDER BY col5; + +--- error: duplicate key update on (x, x, 20) +--- this is because current version is not inplace update but merge, +--- so when the subquery contains multiple same values, it will cause duplicate insert failure. +SELECT col3, sum(col3) * 10 FROM t4 GROUP BY col3 ORDER BY 1, 2; +INSERT INTO t4 (col1, col5) + (SELECT col3, sum(col3) * 10 FROM t4 GROUP BY col3) + ON DUPLICATE KEY UPDATE col1 = 3; + +-- test t5 with sequence or default column with volatile function in constaint index +CREATE TABLE t5 ( + col1 INT, + col2 INT DEFAULT 1, + col3 BIGSERIAL, +-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 INTEGER(10, 5) DEFAULT RANDOM() + 1 +) ; + +-- test t5 with sequence column in constaint index +CREATE UNIQUE INDEX u_t5_index1 ON t5(col1, col3); +--- should insert +INSERT INTO t5 VALUES (1), (1), (1) ON DUPLICATE KEY UPDATE col2 = col3; +INSERT INTO t5 DEFAULT VALUES ON DUPLICATE KEY UPDATE col2 = col3; +SELECT * FROM t5 ORDER BY col3; + +--- should update +INSERT INTO t5 (col1, col3) VALUES (1, 1), (1, 2), (1, 3) ON DUPLICATE KEY UPDATE col5 = col2, col2 = col3 * 10; +SELECT * FROM t5 WHERE col1 = 1 ORDER BY col3; + +--- should some insert some update +INSERT INTO t5 (col1, col3) VALUES (2, 5), (2, 6); +SELECT col1, col2, col3 FROM t5 ORDER BY col3; +INSERT INTO t5 (col1) VALUES (2), (2), (2) ON DUPLICATE KEY UPDATE col2 = col3; +SELECT col1, col2, col3 FROM t5 ORDER BY col3; + +--- should INSERT and sequence starting from 7 +INSERT INTO t5 VALUES (2), (2); +SELECT col1, col2, col3 FROM t5 ORDER BY col3; + +-- test with volatile function as default column in constraint index +TRUNCATE t5; +DROP INDEX u_t5_index1; +CREATE UNIQUE INDEX u_t5_index2 ON t5(col1, col5) WHERE col1 > 2; + +--- should insert +INSERT INTO t5 VALUES (3), (3), (3) ON DUPLICATE KEY UPDATE col2 = col3; +INSERT INTO t5 (col1) VALUES (4), (4), (4) ON DUPLICATE KEY UPDATE col2 = col3; +SELECT * FROM t5 ORDER BY col3; + +--- should update +INSERT INTO t5 (col1, col5) SELECT col1, col5 FROM t5 where col1 = 3 ON DUPLICATE KEY UPDATE col2 = col5 * 100; +SELECT * FROM t5 ORDER BY col3; + +--- test subquery +---- should insert +INSERT INTO t5 (col1, col2) SELECT col1, col2 FROM t5 ON DUPLICATE KEY UPDATE col2 = col5 * 100; +SELECT * FROM t5 ORDER BY col3; + +---- should update +INSERT INTO t5 SELECT * FROM t5 ON DUPLICATE KEY UPDATE col2 = col5 * 1000; +SELECT * FROM t5 ORDER BY col3; + +-- test t6 with one more index +CREATE TABLE t6 ( + col1 INT, + col2 INT DEFAULT 1, + col3 BIGSERIAL, +-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 INTEGER(10, 5) DEFAULT RANDOM(), + col6 INT, + col7 TEXT +) ; + +ALTER TABLE t6 ADD PRIMARY KEY (col1, col3); +CREATE UNIQUE INDEX u_t6_index1 ON t6(col1, col5, col6); + +INSERT INTO t6 (col1) VALUES (1), (2), (3), (4), (5); +SELECT col1, col2, col3, col6, col7 FROM t6 ORDER BY col3; + +--- should not insert +INSERT INTO t6 (col1) VALUES (1), (2), (3), (4), (5) ON DUPLICATE KEY UPDATE col6 = power(col1, col2); +SELECT col1, col2, col3, col6, col7 FROM t6 ORDER BY col3; + +--- should update because primary key matches +INSERT INTO t6 (col1, col3) VALUES (6, 11), (6, 12); +SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col3; +INSERT INTO t6 (col1) VALUES (6), (6), (6) ON DUPLICATE KEY UPDATE col2 = col1 + col3, col6 = col2 * 10; +SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col3; + +--- should update for those col6 is not null bacause they will match the unique index, +--- and insert for those col6 is null because the unique index containing null never matches, +--- also primary key will not match +--- be ware the sequence column of the inserted row will jump n step, where n is the count of the not null rows, +--- because those sequence have to be generated during the unique index join stage. +INSERT INTO t6 (col1, col5, col6) + (SELECT col1, col5, col6 FROM t6 WHERE col1 = 6) + ON DUPLICATE KEY UPDATE + col7 = col2 + 1; +SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col1, col3, col6, col7; + +--- should update because unique index and primary key both match +INSERT INTO t6 (col1, col3, col5, col6) + (SELECT col1, col3, col5, col6 FROM t6 WHERE col1 = 6 AND col6 IS NOT NULL) + ON DUPLICATE KEY UPDATE + col7 = col7 * 10; +SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col1, col3, col6, col7; + +--- insert when col3 = 17 because constaints does not match, +--- but update when col3 = 18 because 18 has been inserted and will cause a match +INSERT INTO t6 (col1, col3, col5, col6) VALUES (7, 18, 100, 100); +SELECT * FROM t6 WHERE col3 > 16 ORDER BY col1, col3, col6, col7; +INSERT INTO t6 (col1, col5, col6) VALUES (7, 10, 10), (7, 100, 100) ON DUPLICATE KEY UPDATE + col7 = col3 * 100; +SELECT * FROM t6 WHERE col1 = 7 ORDER BY col1, col3, col6, col7; + +DROP SCHEMA test_insert_update_003 CASCADE; diff --git a/src/test/regress/sql/upsert_008.sql b/src/test/regress/sql/upsert_008.sql new file mode 100644 index 000000000..79b3563ef --- /dev/null +++ b/src/test/regress/sql/upsert_008.sql @@ -0,0 +1,359 @@ +-- +-- INSERT UPDATE, test explain command, comes from merge_explain and merge_explain_pretty +-- + +-- initial +CREATE SCHEMA test_insert_update_008; +SET current_schema = test_insert_update_008; + +-- enable_upsert_to_merge must is off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; + +CREATE TABLE products_base +( +product_id INTEGER DEFAULT 0, +product_name VARCHAR(60) DEFAULT 'null', +category VARCHAR(60) DEFAULT 'unknown', +total INTEGER DEFAULT '0' +); + +INSERT INTO products_base VALUES (1501, 'vivitar 35mm', 'electrncs', 100); +INSERT INTO products_base VALUES (1502, 'olympus is50', 'electrncs', 100); +INSERT INTO products_base VALUES (1600, 'play gym', 'toys', 100); +INSERT INTO products_base VALUES (1601, 'lamaze', 'toys', 100); +INSERT INTO products_base VALUES (1666, 'harry potter', 'dvd', 100); + +CREATE TABLE newproducts_base +( +product_id INTEGER DEFAULT 0, +product_name VARCHAR(60) DEFAULT 'null', +category VARCHAR(60) DEFAULT 'unknown', +total INTEGER DEFAULT '0' +); + +INSERT INTO newproducts_base VALUES (1502, 'olympus camera', 'electrncs', 200); +INSERT INTO newproducts_base VALUES (1601, 'lamaze', 'toys', 200); +INSERT INTO newproducts_base VALUES (1666, 'harry potter', 'toys', 200); +INSERT INTO newproducts_base VALUES (1700, 'wait interface', 'books', 200); + +ANALYZE products_base; +ANALYZE newproducts_base; + +-- +-- row table +-- +CREATE TABLE products_row +( +product_id INTEGER DEFAULT 0 PRIMARY KEY, +product_name VARCHAR(60) DEFAULT 'null', +category VARCHAR(60) DEFAULT 'unknown', +total INTEGER DEFAULT '0' +); + +CREATE TABLE newproducts_row +( +product_id INTEGER DEFAULT 0 PRIMARY KEY, +product_name VARCHAR(60) DEFAULT 'null', +category VARCHAR(60) DEFAULT 'unknown', +total INTEGER DEFAULT '0' +); + +INSERT INTO products_row SELECT * FROM products_base; +INSERT INTO newproducts_row SELECT * FROM newproducts_base; +ANALYZE products_row; +ANALYZE newproducts_row; + +SET explain_perf_mode = normal; +-- explain verbose +EXPLAIN (VERBOSE on, COSTS off) +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; + +EXPLAIN (VERBOSE on, COSTS off) +INSERT INTO products_row + SELECT newproducts_row.product_id, + newproducts_row.product_name, + newproducts_row.category, + newproducts_row.total + FROM newproducts_row, products_row + WHERE products_row.total + newproducts_row.total < 1000 + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; + +EXPLAIN (VERBOSE on, COSTS off) +INSERT INTO products_row + SELECT product_id, product_name, category, total + FROM newproducts_row WHERE product_id IS NOT NULL AND product_name IS NOT NULL + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; + +-- explain analyze +BEGIN; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; +ROLLBACK; + +-- explain performance +\o insert_update_explain.txt +BEGIN; +EXPLAIN PERFORMANCE +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; +ROLLBACK; +\o + +-- explain analyze +BEGIN; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; +ROLLBACK; + +-- pretty mode performance +SET explain_perf_mode = pretty; + +-- explain verbose +EXPLAIN (VERBOSE on, COSTS off) +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; + +-- explain analyze +BEGIN; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; +ROLLBACK; + +-- explain analyze +BEGIN; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; +ROLLBACK; + +-- explain performance +\o insert_update_explain_pretty.txt +BEGIN; +EXPLAIN PERFORMANCE +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; +ROLLBACK; + +SET explain_perf_mode = run; + +BEGIN; +EXPLAIN PERFORMANCE +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; +ROLLBACK; + +SET explain_perf_mode = summary; + +BEGIN; +EXPLAIN PERFORMANCE +INSERT INTO products_row + SELECT product_id, product_name, category, total FROM newproducts_row + ON DUPLICATE KEY UPDATE + product_name = excluded.product_name, + category = excluded.category, + total = excluded.total; +ROLLBACK; +\o + +CREATE TABLE item +( + a INT DEFAULT 3, + item_id NUMERIC(18,10), + item_name VARCHAR(100), + item_level NUMERIC(39,0), + item_desc VARCHAR(250), + item_subclass_cd VARCHAR(50), + item_type_cd VARCHAR(50), + inventory_ind CHAR(300), + vendor_party_id SMALLINT, + commodity_cd VARCHAR(50), + brand_cd VARCHAR(50), + item_available CHAR(100), + CONSTRAINT u_item_index UNIQUE (item_subclass_cd, vendor_party_id) +) +PARTITION BY RANGE (vendor_party_id) +( +PARTITION item_1 VALUES LESS THAN (0), +PARTITION item_2 VALUES LESS THAN (1), +PARTITION item_3 VALUES LESS THAN (2), +PARTITION item_4 VALUES LESS THAN (3), +PARTITION item_5 VALUES LESS THAN (6), +PARTITION item_6 VALUES LESS THAN (8), +PARTITION item_7 VALUES LESS THAN (10), +PARTITION item_8 VALUES LESS THAN (15), +PARTITION item_9 VALUES LESS THAN (MAXVALUE) +) ENABLE ROW MOVEMENT; + +CREATE TABLE region +( + a INT DEFAULT 8, + region_cd VARCHAR(50), + region_name VARCHAR(100), + division_cd VARCHAR(50), + region_mgr_associate_id number(18,9) +); + +CREATE TABLE associate_benefit_expense +( + a INT DEFAULT 44, + period_end_dt DATE, + associate_expns_type_cd VARCHAR(50), + associate_party_id INTEGER, + benefit_hours_qty decimal(38,11), + benefit_cost_amt number(38,4) +) +PARTITION BY RANGE (associate_expns_type_cd) +( +PARTITION associate_benefit_expense_1 VALUES LESS THAN ('B'), +PARTITION associate_benefit_expense_2 VALUES LESS THAN ('E'), +PARTITION associate_benefit_expense_3 VALUES LESS THAN ('G'), +PARTITION associate_benefit_expense_4 VALUES LESS THAN ('I'), +PARTITION associate_benefit_expense_5 VALUES LESS THAN ('L'), +PARTITION associate_benefit_expense_6 VALUES LESS THAN ('N'), +PARTITION associate_benefit_expense_7 VALUES LESS THAN ('P'), +PARTITION associate_benefit_expense_8 VALUES LESS THAN ('Q'), +PARTITION associate_benefit_expense_9 VALUES LESS THAN ('R'), +PARTITION associate_benefit_expense_10 VALUES LESS THAN ('T'), +PARTITION associate_benefit_expense_11 VALUES LESS THAN ('U'), +PARTITION associate_benefit_expense_12 VALUES LESS THAN ('V'), +PARTITION associate_benefit_expense_13 VALUES LESS THAN (MAXVALUE) +) ENABLE ROW MOVEMENT; + +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (0.12, ' ' , 'A' , NULL, 'TGK' , 'A' , 2, 'A' , 'A' , 'Y'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (1.3, 'B' , NULL, 'B' , 'B' , NULL, 1, 'B' , NULL , 'N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (2.23, 'C' , 'C' , NULL, 'C' , 'C' , 2, 'C' , 'C' , 'N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (3.33, 'D' , 'D' , 'PT' , NULL, 'D' , 3, 'D' , 'D' , 'N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (4.98, ' ' , NULL, 'E' , 'E' , 'E' , 4, 'E' , 'E' , 'Y'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (5.01, NULL, 'F' , ' ' , 'F' , 'F' , 5, 'F' , 'F' , 'N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (6, 'G' , 'G' , 'G' , '_D' , 'G' , 6, 'G' , NULL ,'N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (0.7, NULL, NULL, NULL, 'H' , 'H' , 7, NULL, 'G' , 'Y'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (0.08, 'I' , ' ' , ' T ' , NULL, 'I' , 8, 'I' , '' , 'N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (9.12, ' ' , 'J' , ' PP' , 'J' , 'J' , 9, 'J' , NULL , 'Y'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (10.10, NULL, ' ' , 'A' , 'A' , 'A' , 2, NULL, 'A','Y'); +--INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (11.11, 'B' , 'B' , 'B' , 'BCDAA' , NULL, 1, 'B' , 'B','N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (12.02, 'D' , NULL, NULL, 'C' , 'C' , 2, 'C' , 'C','N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (13.99, NULL, ' ' , 'D' , 'D' , 'D' , 3, 'D' , 'D','Y'); +--INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (14, 'G' , 'E' , 'E' , NULL, 'E' , 4, 'E' , 'E','N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (15, 'F' , ' ' , 'C' , 'CLEANING' , 'F' , 5, 'F' , 'F','Y'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (16, '' , 'Z' , NULL, 'G' , 'G' , 6, 'G' , NULL,'N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (17, NULL, '' , ' PAPER' , 'H' , '' , 7, NULL, NULL,'Y'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (19, ' ' , 'B' , '' , '' , 'I' , 8, 'I' , NULL,'N'); +INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (20 , 'A' , 'J' , 'J' , 'J' , NULL, 9, 'J' , 'G','Y'); + +/*--REGION--*/ +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('A', 'A ', 'A', 0.123433); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('B', 'B', 'B', NULL); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('C', 'C', 'C', 2.232008908); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('D', ' DD', 'D', 3.878789); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('E', 'A', 'E', 4.89060603); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('F', 'F', 'F', 5.82703827); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('G', 'G', 'TTT', NULL); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('H', 'H', 'G', 7.3829083); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('I', 'C', 'M', 8.983989); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('J', 'J', 'G', NULL); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('K', ' ', 'C', 2.232008908); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('L', 'D', 'X', 3.878789); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('M', 'TTTTTT ', 'D' , 4.89060603); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('N', 'G' , 'B' , NULL); +INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('O' , 'G', 'F', 6.6703972); + +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1970-01-01', 'A', 5, 0.5 , 0.5); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1973-01-01', 'B', 1, NULL, 1.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1976-01-01', 'C', 2, 2.0 , NULL); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1979-01-01', 'D', 3, 3.0 , 3.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1982-01-01', 'E', 4, 4.0 , 4.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1985-01-01', 'F', 5, 5.0 , 5.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1988-01-01', 'F', 6, NULL, 6.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1991-01-01', 'G', 6, NULL, NULL); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1994-01-01', 'G', 15, 8.0 , 8.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1997-01-01', 'G', 16, 9.0 , 9.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1983-01-03', 'I', 14, 4.0 , 4.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1984-01-01', 'GO', 15, 5.0 , NULL); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1985-05-01', 'I', 16, 6.0 , 6.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1990-01-01', 'TTT', 16, NULL, 7.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1992-02-01', 'A', 15, 8.0 , 8.0); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1997-02-01', 'G', 17, 9.0 , NULL); +INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1997-05-01', 'G' , 17, 9.0 , NULL); + +ANALYZE item; +ANALYZE region; +ANALYZE associate_benefit_expense; + +EXPLAIN (VERBOSE ON, COSTS OFF) +INSERT INTO item (item_level, item_subclass_cd, item_desc, vendor_party_id) + SELECT Table_001.REGION_MGR_ASSOCIATE_ID Column_003, + Table_002.associate_expns_type_cd Column_004, + CAST(Table_001.region_name AS VARCHAR) Column_005, + 10 Column_006 +-- 'o' Column_007, +-- 'F' Column_008, +-- pg_client_encoding() Column_009 + FROM region Table_001, associate_benefit_expense Table_002 +ON DUPLICATE KEY UPDATE item_level = -1000; + +EXPLAIN (VERBOSE ON, COSTS OFF) +INSERT INTO products_row VALUES(100) + ON DUPLICATE KEY UPDATE total=100; + +SET enable_light_proxy=off; +EXPLAIN (VERBOSE ON, COSTS OFF) +INSERT INTO products_row VALUES(100) + ON DUPLICATE KEY UPDATE total=100; + +EXPLAIN (VERBOSE ON, COSTS OFF) +INSERT INTO products_row VALUES(100) + ON DUPLICATE KEY UPDATE total=100; + +EXPLAIN (VERBOSE ON, COSTS OFF) +INSERT INTO products_row VALUES(100) + ON DUPLICATE KEY UPDATE total=100; + +RESET enable_light_proxy; + +DROP SCHEMA test_insert_update_008 CASCADE; diff --git a/src/test/regress/sql/upsert_009.sql b/src/test/regress/sql/upsert_009.sql new file mode 100644 index 000000000..f6b68d3c7 --- /dev/null +++ b/src/test/regress/sql/upsert_009.sql @@ -0,0 +1,281 @@ +DROP SCHEMA test_insert_update_009 CASCADE; +CREATE SCHEMA test_insert_update_009; +SET CURRENT_SCHEMA TO test_insert_update_009; +SET enable_light_proxy=off; + +-- enable_upsert_to_merge must is off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; + +-- test t1 with no index +CREATE TABLE t1 ( + col1 INT, + col2 INT, + col3 INT DEFAULT 1, +-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 BIGSERIAL +) ; + +--- distribute key are not allowed to update +INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE col1 = 3; + +--- should always insert +INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE col2 = 3; +INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE t1.col2 = 4; + +--- appoint column list in insert clause, should always insert +INSERT INTO t1(col1, col3) VALUES (1, 3) ON DUPLICATE KEY UPDATE col2 = 5; +INSERT INTO t1(col1, col3) VALUES (1, 3) ON DUPLICATE KEY UPDATE t1.col2 = 6; + +--- multiple rows, should always insert +INSERT INTO t1 VALUES (2, 1), (2, 1) ON DUPLICATE KEY UPDATE col2 = 7, col3 = 7; +SELECT * FROM t1 ORDER BY col5; + +--- test union, should insert +INSERT INTO t1 (col1, col2) + SELECT * FROM + (SELECT col1, col2 FROM t1 + UNION + SELECT col1, col3 FROM t1) AS union_table + ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3); +SELECT col1, col2, col3 FROM t1 WHERE col5 > 6 ORDER BY col1, col2; + +--- test subquery, should insert +INSERT INTO t1 + (SELECT col1 || col2 || '00' FROM t1 ORDER BY col5) + ON DUPLICATE KEY UPDATE col3 = col1 * 100; +SELECT col1, col2, col3 FROM t1 WHERE col1 >= 100 ORDER BY col1; + +-- test t2 with one primary key +CREATE TABLE t2 ( + col1 INT, + col2 INT PRIMARY KEY, + col3 INT DEFAULT 1, + col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 BIGSERIAL +) ; + +--- distribute key are not allowed to update +INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE col2 = 3; +INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE t2.col2 = 3; +INSERT INTO t2 (col2, col3, col4, col5) + VALUES (10, 10, CURRENT_TIMESTAMP(0), 10), + (20, 20, CURRENT_TIMESTAMP(1), 20), + (30, 30, CURRENT_TIMESTAMP(2), 30) + ON DUPLICATE KEY UPDATE + col1 = 100, + col2 = 100, + col3 = 100, + col4 = '2019-08-09'::TIMESTAMP, + col5 = 100; + +--- should insert +INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 30; +INSERT INTO t2 VALUES (2, 2) ON DUPLICATE KEY UPDATE t2.col1 = 40; +INSERT INTO t2 VALUES (3, 3) ON DUPLICATE KEY UPDATE col1 = col1 * 2; +INSERT INTO t2 VALUES (4, 4) ON DUPLICATE KEY UPDATE t2.col1 = t2.col1 * 2 ; +INSERT INTO t2 VALUES (5, 5) ON DUPLICATE KEY UPDATE col1 = col2 + 1; +INSERT INTO t2 VALUES (6, 6) ON DUPLICATE KEY UPDATE t2.col1 = t2.col2 + 1; +INSERT INTO t2 VALUES (7, 7) ON DUPLICATE KEY UPDATE col1 = extract(dow from col4) + 10; +INSERT INTO t2 VALUES (8, 8) ON DUPLICATE KEY UPDATE t2.col1 = extract(century from col4) * 100 + extract(isodow from col4); +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5; + +--- should update +INSERT INTO t2 VALUES (3, 1) ON DUPLICATE KEY UPDATE col1 = 30, col3 = col5 + 1; +INSERT INTO t2 VALUES (4, 2) ON DUPLICATE KEY UPDATE t2.col1 = 40, t2.col3 = t2.col5; +INSERT INTO t2 VALUES (3, 3), (4, 4) ON DUPLICATE KEY UPDATE col3 = t2.col5 + 1; +INSERT INTO t2 VALUES (5, 5) ON DUPLICATE KEY UPDATE col1 = extract(dow from col4) + 10; +INSERT INTO t2 VALUES (6, 6) ON DUPLICATE KEY UPDATE t2.col1 = extract(century from col4) * 100 + extract(isodow from col4); +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5; + +-- primary key are not allowed to be null +INSERT INTO t2 (col1) VALUES (10) ON DUPLICATE KEY UPDATE col1 = 10; + +--- appoint column list in insert clause +---- should insert +INSERT INTO t2 (col2, col3) VALUES (9, 9) ON DUPLICATE KEY UPDATE col1 = 90; +INSERT INTO t2 (col2, col3, col4, col5) + VALUES (10, 10, CURRENT_TIMESTAMP(0), 10), + (20, 20, CURRENT_TIMESTAMP(1), 20), + (30, 30, CURRENT_TIMESTAMP(2), 30) + ON DUPLICATE KEY UPDATE + col1 = 100, + col3 = 100, + col4 = '2019-08-20'::TIMESTAMP, + col5 = 100; +SELECT * FROM t2 ORDER BY col5; + +---- should update +INSERT INTO t2 (col2, col3) VALUES (9, 9) ON DUPLICATE KEY UPDATE col1 = 90; +INSERT INTO t2 (col2, col3, col4, col5) + VALUES (10, 10, CURRENT_TIMESTAMP(0), 10), + (20, 20, CURRENT_TIMESTAMP(1), 20), + (30, 30, CURRENT_TIMESTAMP(2), 30) + ON DUPLICATE KEY UPDATE + col1 = 100, + col3 = 100, + col4 = '2019-08-20'::TIMESTAMP, + col5 = 100; +SELECT * FROM t2 ORDER BY col5, col2; + +--- test subquery +---- should insert +INSERT INTO t2 + (SELECT col1 * 1000, col2 * 1000 + 1 FROM t2 ORDER BY col5 LIMIT 2) + ON DUPLICATE KEY UPDATE col3 = col2 + 1; +INSERT INTO t2 (col2, col3) + (SELECT col1 * 1000 + 2, col2 * 1000 FROM t2 ORDER BY col5 LIMIT 2 OFFSET 1) + ON DUPLICATE KEY UPDATE col3 = col2 + 1; +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2; + +---- should update +INSERT INTO t2 + (SELECT col1 * 1000, col2 * 1000 + 1 FROM t2 ORDER BY col5 LIMIT 2) + ON DUPLICATE KEY UPDATE col3 = col2 + 1; +INSERT INTO t2 (col2, col3) + (SELECT col1 * 1000 + 2, col2 * 1000 FROM t2 ORDER BY col5 LIMIT 2 OFFSET 1) + ON DUPLICATE KEY UPDATE col3 = col2 + 1; +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2; + +--- test union, some insert some update +SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT col1, col3 FROM t1 ORDER BY 1, 2; +INSERT INTO t2 (col1, col2) + SELECT * FROM + (SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT col1, col3 FROM t1) AS union_table + ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3); +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2; + +INSERT INTO t2 (col1, col2) + SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT col1, col3 FROM t1 + ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3); +SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2; +reset behavior_compat_options; + +-- test INTERSECT, should update +(SELECT col1, col1 + col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT 1, 2) + INTERSECT + SELECT col1, col3 FROM t1; + +INSERT INTO t2 (col1, col2) + (SELECT col1, col1 + col2 FROM t1 WHERE col2 IS NOT NULL + UNION + SELECT 1, 2) + INTERSECT + SELECT col1, col3 FROM t1 + ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3); +SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 3 ORDER BY col5, col2; + +-- test EXCEPT, should update +SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + EXCEPT + (SELECT col1, col3 FROM t1 + UNION + SELECT NULL, NULL); + +INSERT INTO t2 (col1, col2) + SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + EXCEPT + (SELECT col1, col3 FROM t1 + UNION + SELECT NULL, NULL) + ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3); +SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 2 ORDER BY col5, col2; + +-- test unique index with not default value +ALTER TABLE t2 DROP CONSTRAINT t2_pkey; +CREATE UNIQUE INDEX t2_u_index ON t2(col2, col5); +SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + EXCEPT + (SELECT col1, col3 FROM t1 + UNION + SELECT NULL, NULL); +INSERT INTO t2 + SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL + EXCEPT + (SELECT col1, col3 FROM t1 + UNION + SELECT NULL, NULL) + ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3); +SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 2 ORDER BY col5, col2; + +-- test t3 with one primary index with two columns +CREATE TABLE t3 ( + col1 INT, + col2 INT, + col3 INT DEFAULT 1, +-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 BIGSERIAL, + PRIMARY KEY (col2, col3) +) ; + +--- column referenced by primary key are not allowed to update +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col3 = 3; + +--- should insert when not contains primary key and not all primary key referred columns have default value. +--- but will fail cause primary key should not be null +INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2; + +--- should insert +--- (SEQUENCE BUG: the serial column will starts from 2 since the above statement has applied for a sequence) +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20; +SELECT * FROM t3 order by col5; + +--- should update +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20; +SELECT * FROM t3 order by col5; + +--- test subquery +---- should insert +INSERT INTO t3 (col2, col3) (SELECT max(col2) + 1, max(col3) + 1 FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +SELECT * FROM t3 order by col5; + +---- should update +INSERT INTO t3 (col2, col3) (SELECT max(col2), max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +SELECT * FROM t3 order by col5; + +-- test t3 with one unique index with two columns +TRUNCATE t3; +ALTER TABLE t3 DROP CONSTRAINT t3_pkey; +ALTER TABLE t3 ALTER COLUMN col2 DROP NOT NULL; +CREATE UNIQUE INDEX t3_ukey ON t3 (col2, col3); + +--- column referenced by unique key are not allowed to update +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col3 = 3; + +--- should insert cause not contains unique key and not all unique key referred columns have default value. +INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2; +INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2; +SELECT * FROM t3 order by col5; + +--- should insert +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE t3.col5 = 20; +SELECT * FROM t3 ORDER BY col5; + +--- should update +INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE t3.col1 = 10; +INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20; +SELECT * FROM t3 ORDER BY col5; + +--- test subquery +---- should insert +INSERT INTO t3 (SELECT 100, NULL, max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +INSERT INTO t3 (SELECT 100, NULL, max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +INSERT INTO t3 (col2, col3) (SELECT max(col2) + 1, max(col3) + 1 FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +SELECT * FROM t3 ORDER BY col5; + +---- should update +INSERT INTO t3 (col2, col3) (SELECT max(col2), max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3; +SELECT * FROM t3 ORDER BY col5; + +RESET enable_light_proxy; +DROP SCHEMA test_insert_update_009 CASCADE; diff --git a/src/test/regress/sql/upsert_010.sql b/src/test/regress/sql/upsert_010.sql new file mode 100644 index 000000000..0838b836c --- /dev/null +++ b/src/test/regress/sql/upsert_010.sql @@ -0,0 +1,144 @@ +DROP SCHEMA test_insert_update_010 CASCADE; +CREATE SCHEMA test_insert_update_010; +SET CURRENT_SCHEMA TO test_insert_update_010; + +-- enable_upsert_to_merge must is off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; + +-- test t4 with one primary key with three columns +CREATE TABLE t4 ( + col1 INT, + col2 INT DEFAULT 0, + col3 INT DEFAULT 1, + col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 BIGSERIAL, + PRIMARY KEY (col2, col3, col5) +) ; + +--- should insert +INSERT INTO t4 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t4 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 10; +INSERT INTO t4 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col1 = 200; +INSERT INTO t4 VALUES (100, 100, 100, CURRENT_TIMESTAMP, 100) ON DUPLICATE KEY UPDATE col1 = 1000; +SELECT col1, col2, col3, col5 FROM t4 ORDER BY col5; + +--- should update +INSERT INTO t4 VALUES (2, 2, 2, CURRENT_TIMESTAMP, 3) ON DUPLICATE KEY UPDATE col1 = 200; +INSERT INTO t4 VALUES (100, 100, 100, CURRENT_TIMESTAMP, 100) ON DUPLICATE KEY UPDATE col1 = 1000; +SELECT col1, col2, col3, col5 FROM t4 ORDER BY col5; + +--- error: duplicate key update on (x, x, 20) +--- this is because current version is not inplace update but merge, +--- so when the subquery contains multiple same values, it will cause duplicate insert failure. +SELECT col3, sum(col3) * 10 FROM t4 GROUP BY col3 ORDER BY 1, 2; +INSERT INTO t4 (col1, col5) + (SELECT col3, sum(col3) * 10 FROM t4 GROUP BY col3) + ON DUPLICATE KEY UPDATE col1 = 3; + +-- test t5 with sequence or default column with volatile function in constaint index +CREATE TABLE t5 ( + col1 INT, + col2 INT DEFAULT 1, + col3 BIGSERIAL, +-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 INTEGER(10, 5) DEFAULT RANDOM() + 1 +) ; + +-- test t5 with sequence column in constaint index +CREATE UNIQUE INDEX u_t5_index1 ON t5(col1, col3); +--- should insert +INSERT INTO t5 VALUES (1), (1), (1) ON DUPLICATE KEY UPDATE col2 = col3; +INSERT INTO t5 DEFAULT VALUES ON DUPLICATE KEY UPDATE col2 = col3; +SELECT * FROM t5 ORDER BY col3; + +--- should update +INSERT INTO t5 (col1, col3) VALUES (1, 1), (1, 2), (1, 3) ON DUPLICATE KEY UPDATE col5 = col2, col2 = col3 * 10; +SELECT * FROM t5 WHERE col1 = 1 ORDER BY col3; + +--- should some insert some update +INSERT INTO t5 (col1, col3) VALUES (2, 5), (2, 6); +SELECT col1, col2, col3 FROM t5 ORDER BY col3; +INSERT INTO t5 (col1) VALUES (2), (2), (2) ON DUPLICATE KEY UPDATE col2 = col3; +SELECT col1, col2, col3 FROM t5 ORDER BY col3; + +--- should INSERT and sequence starting from 7 +INSERT INTO t5 VALUES (2), (2); +SELECT col1, col2, col3 FROM t5 ORDER BY col3; + +-- test with volatile function as default column in constraint index +TRUNCATE t5; +DROP INDEX u_t5_index1; +CREATE UNIQUE INDEX u_t5_index2 ON t5(col1, col5) WHERE col1 > 2; + +--- should insert +INSERT INTO t5 VALUES (3), (3), (3) ON DUPLICATE KEY UPDATE col2 = col3; +INSERT INTO t5 (col1) VALUES (4), (4), (4) ON DUPLICATE KEY UPDATE col2 = col3; +SELECT * FROM t5 ORDER BY col3; + +--- should update +INSERT INTO t5 (col1, col5) SELECT col1, col5 FROM t5 where col1 = 3 ON DUPLICATE KEY UPDATE col2 = col5 * 100; +SELECT * FROM t5 ORDER BY col3; + +--- test subquery +---- should insert +INSERT INTO t5 (col1, col2) SELECT col1, col2 FROM t5 ON DUPLICATE KEY UPDATE col2 = col5 * 100; +SELECT * FROM t5 ORDER BY col3; + +---- should update +INSERT INTO t5 SELECT * FROM t5 ON DUPLICATE KEY UPDATE col2 = col5 * 1000; +SELECT * FROM t5 ORDER BY col3; + +-- test t6 with one more index +CREATE TABLE t6 ( + col1 INT, + col2 INT DEFAULT 1, + col3 BIGSERIAL, +-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 INTEGER(10, 5) DEFAULT RANDOM(), + col6 INT, + col7 TEXT +) ; + +ALTER TABLE t6 ADD PRIMARY KEY (col1, col3); +CREATE UNIQUE INDEX u_t6_index1 ON t6(col1, col5, col6); + +INSERT INTO t6 (col1) VALUES (1), (2), (3), (4), (5); +SELECT col1, col2, col3, col6, col7 FROM t6 ORDER BY col3; + +--- should insert +INSERT INTO t6 (col1) VALUES (1), (2), (3), (4), (5) ON DUPLICATE KEY UPDATE col6 = power(col1, col2); +SELECT col1, col2, col3, col6, col7 FROM t6 ORDER BY col3; + +--- should update because primary key matches +INSERT INTO t6 (col1, col3) VALUES (6, 11), (6, 12); +SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col3; +INSERT INTO t6 (col1) VALUES (6), (6), (6) ON DUPLICATE KEY UPDATE col2 = col1 + col3, col6 = col2 * 10; +SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col3; + +--- should update for those col6 is not null bacause they will match the unique index, +--- and insert for those col6 is null because the unique index containing null never matches, +--- also primary key will not match +--- be ware the sequence column of the inserted row will jump n step, where n is the count of the not null rows, +--- because those sequence have to be generated during the unique index join stage. +INSERT INTO t6 (col1, col5, col6) + (SELECT col1, col5, col6 FROM t6 WHERE col1 = 6) + ON DUPLICATE KEY UPDATE + col7 = col2 + 1; +SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col1, col3, col6, col7; + +--- should update because unique index and primary key both match +INSERT INTO t6 (col1, col3, col5, col6) + (SELECT col1, col3, col5, col6 FROM t6 WHERE col1 = 6 AND col6 IS NOT NULL) + ON DUPLICATE KEY UPDATE + col7 = col7 * 10; +SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col1, col3, col6, col7; + +--- insert when col3 = 17 because constaints does not match, +--- but update when col3 = 18 because 18 has been inserted and will cause a match +INSERT INTO t6 (col1, col3, col5, col6) VALUES (7, 18, 100, 100); +SELECT * FROM t6 WHERE col3 > 16 ORDER BY col1, col3, col6, col7; +INSERT INTO t6 (col1, col5, col6) VALUES (7, 10, 10), (7, 100, 100) ON DUPLICATE KEY UPDATE + col7 = col3 * 100; +SELECT * FROM t6 WHERE col1 = 7 ORDER BY col1, col3, col6, col7; + +DROP SCHEMA test_insert_update_010 CASCADE; diff --git a/src/test/regress/sql/upsert_clean.sql b/src/test/regress/sql/upsert_clean.sql new file mode 100644 index 000000000..442be9ffe --- /dev/null +++ b/src/test/regress/sql/upsert_clean.sql @@ -0,0 +1,3 @@ +\c upsert +DROP SCHEMA upsert_test CASCADE; +DROP SCHEMA upsert_test_unlog CASCADE; diff --git a/src/test/regress/sql/upsert_composite.sql b/src/test/regress/sql/upsert_composite.sql new file mode 100644 index 000000000..57ce939df --- /dev/null +++ b/src/test/regress/sql/upsert_composite.sql @@ -0,0 +1,146 @@ +-------------------------------------------------------------------------------------------- +/* + * procedure + */ +-------------------------------------------------------------------------------------------- +\c upsert; +SET CURRENT_SCHEMA TO upsert_test_procedure; +-- enable_upsert_to_merge must be off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +SHOW enable_upsert_to_merge; + +drop table IF EXISTS t_proc; +create table t_proc(c1 int, c2 int, c3 int unique); +insert into t_proc select a,a,a from generate_series(1,20) as a; +select *from t_proc order by c3; + +create or replace procedure mul_ups( c in INTEGER) +as +declare +val int; +begin + for val in select c3 from t_proc loop + insert into t_proc values(c,c, val) on duplicate key update c1 = c, c2 =c; + end loop; +end; +/ + +call mul_ups(100); +select *from t_proc order by c3; + +create or replace procedure mul_ups( c in INTEGER) +as +declare +val int; +begin + for val in select c3 from t_proc loop + insert into t_proc values(c,c, val) on duplicate key update c1 = $1, c2 =$1; + end loop; +end; +/ + +call mul_ups(200); +select *from t_proc order by c3; + +create or replace procedure mul_ups_00() +as +declare +val int; +begin + for val in select c3 from t_proc loop + insert into t_proc values(val,val,val) on duplicate key update c1 = 300, c2 =300; + end loop; +end; +/ + +call mul_ups_00(); +select *from t_proc order by c3; + +declare +val int; +c int :=400; +begin + for val in select c3 from t_proc loop + insert into t_proc values(c,c, val) on duplicate key update c1 = c, c2 =c; + end loop; +end; +/ +select *from t_proc order by c3; + +create or replace procedure mul_ups( c in INTEGER) +as +declare +val int; +begin + for val in select c3 from t_proc loop + insert into t_proc values(c,c, val+100) on duplicate key update c1 = $1, c2 =$1; + end loop; +end; +/ + +call mul_ups(500); +select *from t_proc order by c3; + +/* + * insert + */ +drop table if exists t_default; +CREATE TABLE t_default (c1 INT PRIMARY KEY DEFAULT 10, c2 FLOAT DEFAULT 3.0, c3 TIMESTAMP DEFAULT '20200508'); +create view v_default as select *from t_default order by c1; + +-- insert src +---- insert values +truncate t_default; +insert into t_default values(DEFAULT, DEFAULT, DEFAULT); +insert into t_default values(1, 2, '20200630'); +select *from t_default order by c1; + +insert into t_default values(DEFAULT, DEFAULT, DEFAULT) on duplicate key update c2 = 1; +select *from t_default order by c1; + +insert into t_default values(1, 2, '20200630') on duplicate key update c2 = 2; +select *from t_default order by c1; + +insert into t_default values(DEFAULT, DEFAULT, DEFAULT),(1, 2, '20200630') on duplicate key update c2 = 3; +select *from t_default order by c1; + +---- insert subquery +insert into t_default select *from t_default order by c1 on duplicate key update c2 = 4; +select *from t_default order by c1; + +insert into t_default select *from t_default order by c1 limit 1 on duplicate key update c2 = 5; +select *from t_default order by c1; + +insert into t_default select *from t_default where c1=10 on duplicate key update c2 = 6; +select *from t_default order by c1; + +insert into t_default select *from t_default order by c1 on duplicate key update c2 = 7; +select *from t_default order by c1; + +insert into t_default select *from v_default order by c1 on duplicate key update c2 = 8; +select *from t_default order by c1; + +insert into t_default select *from t_default union select c1+1,c2+1,c3+1 from t_default order by c1 on duplicate key update c2 = 9; +select *from t_default order by c1; + +insert into t_default(c1,c2) select max(c1),c2 from t_default group by c2 on duplicate key update c2 = 10; + +-- index +---- mul index +truncate t_default; +create unique index t_default_mul on t_default(c1,c3); +insert into t_default values(DEFAULT, DEFAULT, DEFAULT); +insert into t_default values(1, 2, '20200630'); +select *from t_default order by c1; + +insert into t_default values(DEFAULT, DEFAULT, DEFAULT) on duplicate key update c2 = 1; +select *from t_default order by c1; + +insert into t_default values(1, 2, '20200630') on duplicate key update c2 = 2; +select *from t_default order by c1; + +insert into t_default values(DEFAULT, DEFAULT, DEFAULT),(1, 2, '20200630') on duplicate key update c2 = 3; +select *from t_default order by c1; + +insert into t_default select *from t_default on duplicate key update c2 = 4; +select *from t_default order by c1; diff --git a/src/test/regress/sql/upsert_explain.sql b/src/test/regress/sql/upsert_explain.sql new file mode 100644 index 000000000..6408507c9 --- /dev/null +++ b/src/test/regress/sql/upsert_explain.sql @@ -0,0 +1,63 @@ +-------------------------------------------------------------------------------------------- +/* + * explain upsert + */ +-------------------------------------------------------------------------------------------- +\c upsert; +SET CURRENT_SCHEMA TO upsert_test_explain; +-- enable_upsert_to_merge must be off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +SHOW enable_upsert_to_merge; + +create temp table up_expl_temp(c1 int, c2 int, c3 int unique) ; + + +insert into up_expl_hash select a,a,a from generate_series(1,20) as a; +insert into up_expl_repl select a,a,a from generate_series(1,20) as a; +insert into up_expl_part select a,a,a from generate_series(1,20) as a; +insert into up_expl_temp select a,a,a from generate_series(1,20) as a; +insert into up_expl_unlog select a,a,a from generate_series(1,20) as a; +insert into up_expl_node select a,a,a from generate_series(1,20) as a; +insert into up_expl_repl2 select a,a,a,a,a from generate_series(1,20) as a; + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_hash values(1,1,1) on duplicate key update c1 = 2; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_hash values(1,1,1) on duplicate key update c1 = 2; +EXPLAIN PERFORMANCE insert into up_expl_hash values(1,1,1) on duplicate key update c1 = 2; + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_hash values(1,1,1),(2,2,2)on duplicate key update c1 = 2; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_hash values(1,1,1),(2,2,2)on duplicate key update c1 = 2; + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_repl values(1,1,1) on duplicate key update c1 = 2; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_repl values(1,1,1) on duplicate key update c1 = 2; + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_part values(1,1,1) on duplicate key update c1 = 2; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_part values(1,1,1) on duplicate key update c1 = 2; + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_temp values(1,1,1) on duplicate key update c1 = 2; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_temp values(1,1,1) on duplicate key update c1 = 2; + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_unlog values(1,1,1) on duplicate key update c1 = 2; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_unlog values(1,1,1) on duplicate key update c1 = 2; + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_node values(1,1,1) on duplicate key update c1 = 2; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_node values(1,1,1) on duplicate key update c1 = 2; + + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_hash values(1,1,1),(2,2,2)on duplicate key update nothing; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_hash values(1,1,1),(2,2,2)on duplicate key update nothing; + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_repl values(1,1,1) on duplicate key update nothing; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_repl values(1,1,1) on duplicate key update nothing; + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_part values(1,1,1) on duplicate key update nothing; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_part values(1,1,1) on duplicate key update nothing; + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_temp values(1,1,1) on duplicate key update nothing; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_temp values(1,1,1) on duplicate key update nothing; + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_unlog values(1,1,1) on duplicate key update nothing; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_unlog values(1,1,1) on duplicate key update nothing; + +EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_node values(1,1,1) on duplicate key update nothing; +EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_node values(1,1,1) on duplicate key update nothing; + diff --git a/src/test/regress/sql/upsert_grammer_test_01.sql b/src/test/regress/sql/upsert_grammer_test_01.sql new file mode 100644 index 000000000..7124bed4c --- /dev/null +++ b/src/test/regress/sql/upsert_grammer_test_01.sql @@ -0,0 +1,113 @@ +\c upsert +SET CURRENT_SCHEMA TO upsert_test; +-- enable_upsert_to_merge must be off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +SHOW enable_upsert_to_merge; + +-- support multiple set +INSERT INTO t_grammer VALUES(11, 1) ON DUPLICATE KEY UPDATE c2 = 2, c4 = ROW(10, 20), c3 = '{10, 20, 30}'; +INSERT INTO t_grammer VALUES(12, 2) ON DUPLICATE KEY UPDATE c2 = 2, c3[1] = 3; + +-- support const value and expr +INSERT INTO t_grammer VALUES(23) ON DUPLICATE KEY UPDATE c2 = 2; +INSERT INTO t_grammer VALUES(24) ON DUPLICATE KEY UPDATE c2 = length('test') + 100; + +-- support without table name +INSERT INTO t_grammer VALUES(31, 1) ON DUPLICATE KEY UPDATE c2 = c1, c4.a = VALUES(c1); +INSERT INTO t_grammer VALUES(32, 2) ON DUPLICATE KEY UPDATE c2 = c1 + 5, c4.a = VALUES(c1); + +-- support with table name +INSERT INTO t_grammer VALUES(41, 1) ON DUPLICATE KEY UPDATE c2 = t_grammer.c1, c4.a = VALUES(c1); +INSERT INTO t_grammer VALUES(42, 2) ON DUPLICATE KEY UPDATE c2 = t_grammer.c1 + 5, c4.a = VALUES(c1); + +-- support EXCLUDED alone and with expr +INSERT INTO t_grammer VALUES(51, 1) ON DUPLICATE KEY UPDATE c2 = c1 + 5, c4.a = EXCLUDED.c1; +INSERT INTO t_grammer VALUES(52, 2) ON DUPLICATE KEY UPDATE c2 = t_grammer.c1 + 5, c4.a = EXCLUDED.c1 + 5; +INSERT INTO t_grammer VALUES(53, 3) ON DUPLICATE KEY UPDATE c4.a = sqrt(EXCLUDED.c1 - EXCLUDED.c2 - 1) + 1, c2 = t_grammer.c1 + 5; +INSERT INTO t_grammer VALUES(54, 4) ON DUPLICATE KEY UPDATE c4.b = EXCLUDED.c1 + t_grammer.c1; +INSERT INTO t_grammer VALUES(55, 5) ON DUPLICATE KEY UPDATE c4.a = EXCLUDED.c2, c4.b = EXCLUDED.c1 + sqrt(c1 - 6); + +-- support ARRAY +INSERT INTO t_grammer VALUES(61, 1) ON DUPLICATE KEY UPDATE c2 = VALUES(c3[1]); +INSERT INTO t_grammer VALUES(62, 2) ON DUPLICATE KEY UPDATE c5 = VALUES(c3[2,3]); +INSERT INTO t_grammer VALUES(63, 3) ON DUPLICATE KEY UPDATE c5 = VALUES(c3[2:3]); +INSERT INTO t_grammer VALUES(64, 4, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c2 = EXCLUDED.c3[1]; +INSERT INTO t_grammer VALUES(65, 5, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = EXCLUDED.c3[2,3]; +INSERT INTO t_grammer VALUES(66, 6, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = EXCLUDED.c3[1:2]; +INSERT INTO t_grammer VALUES(67, 7, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c2 = c3[1]; +INSERT INTO t_grammer VALUES(68, 8, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = c3[2,3]; +INSERT INTO t_grammer VALUES(69, 9, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = c3[1:2]; + +-- support user defined type +INSERT INTO t_grammer VALUES(71, 1) ON DUPLICATE KEY UPDATE c4.a = c3[1] + c1; +INSERT INTO t_grammer VALUES(72, 2, '{71, 72, 73}') ON DUPLICATE KEY UPDATE c4.a = c3[1] + c1; +INSERT INTO t_grammer VALUES(73, 3, '{71, 72, 73}') ON DUPLICATE KEY UPDATE c4.a = c3[1] + c1, c4.b = c3[2]; +INSERT INTO t_grammer VALUES(74, 4, '{71, 72, 73}', ROW(74, 75)) ON DUPLICATE KEY UPDATE c4 = ROW(740, 750); + +-- appoint INSERT target column +INSERT INTO t_grammer (c5, c1, c2) VALUES('{81,82}', 81, DEFAULT) ON DUPLICATE KEY UPDATE c2 = VALUES(c1); +INSERT INTO t_grammer (c3[1], c3[2], c1) VALUES(881, 882, 82) ON DUPLICATE KEY UPDATE c5 = VALUES(c3), c2 = VALUES(c3[1]); +INSERT INTO t_grammer (c1, c3, c4) VALUES(83, '{81, 82, 83}', ROW(810, 820)) ON DUPLICATE KEY UPDATE c4 = EXCLUDED.c4; +INSERT INTO t_grammer (c1, c3, c4.a) VALUES(84, '{81, 82, 83}', 850) ON DUPLICATE KEY UPDATE c4.b = c3[3]; + +-- support UPDATE NOTHING +INSERT INTO t_grammer VALUES(91, 1, '{91, 92, 93}', ROW(94, 95)) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_grammer (c5, c1, c2, c4) VALUES('{91,92}', 92, DEFAULT, ROW(910, 920)) ON DUPLICATE KEY UPDATE NOTHING; + +-- UPDATE target: unsupport with schema but support with tablename +INSERT INTO t_grammer VALUES(0, 0, '{0,0}', ROW(0, 0), '{107, 108}') ON DUPLICATE KEY UPDATE + upsert_test.t_grammer.c2 = c2 * 10, upsert_test.t_grammer.c3[1:2] = c5[1:2], upsert_test.t_grammer.c4.a = c1; +INSERT INTO t_grammer VALUES(101, 1, '{102, 103, 104}', ROW(105, 106), '{107, 108}') ON DUPLICATE KEY UPDATE + t_grammer.c2 = c2 * 10, t_grammer.c3[1:2] = c5[1:2], t_grammer.c4.a = c1; + +-- INSERT target: appoint schema +INSERT INTO upsert_test.t_grammer VALUES(102, 2, '{102, 103, 104}', ROW(105, 106), '{107, 108}') ON DUPLICATE KEY UPDATE + t_grammer.c2 = EXCLUDED.c2 * 10, t_grammer.c3[1:2] = c5[1:2], t_grammer.c4.a = VALUES(c1); +CREATE SCHEMA upsert_test_tmpschema; +SET CURRENT_SCHEMA TO upsert_test_tmpschema; +INSERT INTO upsert_test.t_grammer VALUES(103, 3, '{102, 103, 104}', ROW(105, 106), '{107, 108}') ON DUPLICATE KEY UPDATE + t_grammer.c2 = EXCLUDED.c2 * 10, t_grammer.c3[1:2] = c5[1:2], t_grammer.c4.a = VALUES(c1); +SET CURRENT_SCHEMA TO upsert_test; +DROP SCHEMA upsert_test_tmpschema; + +-- support data type +INSERT INTO t_data VALUES(11, 11, 11, 11, 11, '11', '11', '11', '2020-04-23', '2020-04-23 11:00', '1:00') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_data VALUES(12, 12, 12, 12, 12, '12', '12', '12', '2020-04-24', '2020-04-24 11:00', '2:00') + ON DUPLICATE KEY UPDATE + c_tiny = 13, c_smallint = 13, c_bigint = 13, c_numeric = 13, + c_var = '13', c_text = '13', c_bytea = '13', c_date = '2020-05-24', c_timestamp = '2020-05-24 11:00', c_time = '3:00'; +INSERT INTO t_data VALUES(13, 13, 13, 13, 13, '13', '13', '13', '2020-04-25', '2020-04-25 11:00', '4:00') + ON DUPLICATE KEY UPDATE + c_tiny = EXCLUDED.c_tiny + 1, c_smallint = EXCLUDED.c_smallint + 1, c_bigint = EXCLUDED.c_bigint + 1, c_numeric = EXCLUDED.c_numeric + 1, + c_var = EXCLUDED.c_var || '3', c_text = EXCLUDED.c_text || '3', c_bytea = EXCLUDED.c_bytea || '3', + c_date = EXCLUDED.c_date + '1 day'::interval, c_timestamp = EXCLUDED.c_timestamp + '1 day'::interval, c_time = EXCLUDED.c_time + '1 hour'::interval; + +-- support UPDATE to default +INSERT INTO t_default DEFAULT VALUES ON DUPLICATE KEY UPDATE c2 = 100, c3 = '2020-05-17'; +INSERT INTO t_default VALUES(91, 0.91, '2020-05-17') ON DUPLICATE KEY UPDATE c2 = DEFAULT, c3 = DEFAULT; +INSERT INTO t_default VALUES(92, DEFAULT, '2020-05-17') ON DUPLICATE KEY UPDATE c2 = 10, c3 = DEFAULT; + +-- unsupport alias +INSERT INTO t_grammer AS alias VALUES (91) ON DUPLICATE KEY UPDATE c2 = VALUES(c1); + +-- unsupport VALUES with expr +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = sqrt(VALUES(c2)); +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c3[1]) + 1; +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a) + 1; +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.c3[1]) + 1; + +-- unsupport VALUES with table name +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.c4.a); +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.c2); + +-- unsupport DEFAULT with expr +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = DEFAULT + 1; + +-- unsupport user defined typed column's element +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a); +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = EXCLUDED.c4.a; +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = c4.a; + +SELECT * FROM t_data ORDER BY 1; +SELECT * FROM t_grammer ORDER BY 1; +SELECT * FROM t_default ORDER BY 1; diff --git a/src/test/regress/sql/upsert_grammer_test_02.sql b/src/test/regress/sql/upsert_grammer_test_02.sql new file mode 100644 index 000000000..ecdaf5d28 --- /dev/null +++ b/src/test/regress/sql/upsert_grammer_test_02.sql @@ -0,0 +1,113 @@ +\c upsert +SET CURRENT_SCHEMA TO upsert_test; +-- enable_upsert_to_merge must be off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +SHOW enable_upsert_to_merge; + +-- support multiple set +INSERT INTO t_grammer VALUES(11, 1) ON DUPLICATE KEY UPDATE c2 = 2, c4 = ROW(10, 20), c3 = '{10, 20, 30}'; +INSERT INTO t_grammer VALUES(12, 2) ON DUPLICATE KEY UPDATE c2 = 2, c3[1] = 3; + +-- support const value and expr +INSERT INTO t_grammer VALUES(23) ON DUPLICATE KEY UPDATE c2 = 2; +INSERT INTO t_grammer VALUES(24) ON DUPLICATE KEY UPDATE c2 = length('test') + 100; + +-- support without table name +INSERT INTO t_grammer VALUES(31, 1) ON DUPLICATE KEY UPDATE c2 = c1, c4.a = VALUES(c1); +INSERT INTO t_grammer VALUES(32, 2) ON DUPLICATE KEY UPDATE c2 = c1 + 5, c4.a = VALUES(c1); + +-- support with table name +INSERT INTO t_grammer VALUES(41, 1) ON DUPLICATE KEY UPDATE c2 = t_grammer.c1, c4.a = VALUES(c1); +INSERT INTO t_grammer VALUES(42, 2) ON DUPLICATE KEY UPDATE c2 = t_grammer.c1 + 5, c4.a = VALUES(c1); + +-- support EXCLUDED alone and with expr +INSERT INTO t_grammer VALUES(51, 1) ON DUPLICATE KEY UPDATE c2 = c1 + 5, c4.a = EXCLUDED.c1; +INSERT INTO t_grammer VALUES(52, 2) ON DUPLICATE KEY UPDATE c2 = t_grammer.c1 + 5, c4.a = EXCLUDED.c1 + 5; +INSERT INTO t_grammer VALUES(53, 3) ON DUPLICATE KEY UPDATE c4.a = sqrt(EXCLUDED.c1 - EXCLUDED.c2 - 1) + 1, c2 = t_grammer.c1 + 5; +INSERT INTO t_grammer VALUES(54, 4) ON DUPLICATE KEY UPDATE c4.b = EXCLUDED.c1 + t_grammer.c1; +INSERT INTO t_grammer VALUES(55, 5) ON DUPLICATE KEY UPDATE c4.a = EXCLUDED.c2, c4.b = EXCLUDED.c1 + sqrt(c1 - 6); + +-- support ARRAY +INSERT INTO t_grammer VALUES(61, 1) ON DUPLICATE KEY UPDATE c2 = VALUES(c3[1]); +INSERT INTO t_grammer VALUES(62, 2) ON DUPLICATE KEY UPDATE c5 = VALUES(c3[2,3]); +INSERT INTO t_grammer VALUES(63, 3) ON DUPLICATE KEY UPDATE c5 = VALUES(c3[2:3]); +INSERT INTO t_grammer VALUES(64, 4, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c2 = EXCLUDED.c3[1]; +INSERT INTO t_grammer VALUES(65, 5, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = EXCLUDED.c3[2,3]; +INSERT INTO t_grammer VALUES(66, 6, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = EXCLUDED.c3[1:2]; +INSERT INTO t_grammer VALUES(67, 7, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c2 = c3[1]; +INSERT INTO t_grammer VALUES(68, 8, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = c3[2,3]; +INSERT INTO t_grammer VALUES(69, 9, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = c3[1:2]; + +-- support user defined type +INSERT INTO t_grammer VALUES(71, 1) ON DUPLICATE KEY UPDATE c4.a = c3[1] + c1; +INSERT INTO t_grammer VALUES(72, 2, '{71, 72, 73}') ON DUPLICATE KEY UPDATE c4.a = c3[1] + c1; +INSERT INTO t_grammer VALUES(73, 3, '{71, 72, 73}') ON DUPLICATE KEY UPDATE c4.a = c3[1] + c1, c4.b = c3[2]; +INSERT INTO t_grammer VALUES(74, 4, '{71, 72, 73}', ROW(74, 75)) ON DUPLICATE KEY UPDATE c4 = ROW(740, 750); + +-- appoint INSERT target column +INSERT INTO t_grammer (c5, c1, c2) VALUES('{81,82}', 81, DEFAULT) ON DUPLICATE KEY UPDATE c2 = VALUES(c1); +INSERT INTO t_grammer (c3[1], c3[2], c1) VALUES(881, 882, 82) ON DUPLICATE KEY UPDATE c5 = VALUES(c3), c2 = VALUES(c3[1]); +INSERT INTO t_grammer (c1, c3, c4) VALUES(83, '{81, 82, 83}', ROW(810, 820)) ON DUPLICATE KEY UPDATE c4 = EXCLUDED.c4; +INSERT INTO t_grammer (c1, c3, c4.a) VALUES(84, '{81, 82, 83}', 850) ON DUPLICATE KEY UPDATE c4.b = c3[3]; + +-- support UPDATE NOTHING +INSERT INTO t_grammer VALUES(91, 1, '{91, 92, 93}', ROW(94, 95)) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_grammer (c5, c1, c2, c4) VALUES('{91,92}', 92, DEFAULT, ROW(910, 920)) ON DUPLICATE KEY UPDATE NOTHING; + +-- UPDATE target: unsupport with schema but support with tablename +INSERT INTO t_grammer VALUES(0, 0, '{0,0}', ROW(0, 0), '{107, 108}') ON DUPLICATE KEY UPDATE + upsert_test.t_grammer.c2 = c2 * 10, upsert_test.t_grammer.c3[1:2] = c5[1:2], upsert_test.t_grammer.c4.a = c1; +INSERT INTO t_grammer VALUES(101, 1, '{102, 103, 104}', ROW(105, 106), '{107, 108}') ON DUPLICATE KEY UPDATE + t_grammer.c2 = c2 * 10, t_grammer.c3[1:2] = c5[1:2], t_grammer.c4.a = c1; + +-- INSERT target: appoint schema +INSERT INTO upsert_test.t_grammer VALUES(102, 2, '{102, 103, 104}', ROW(105, 106), '{107, 108}') ON DUPLICATE KEY UPDATE + t_grammer.c2 = EXCLUDED.c2 * 10, t_grammer.c3[1:2] = c5[1:2], t_grammer.c4.a = VALUES(c1); +CREATE SCHEMA upsert_test_tmp; +SET CURRENT_SCHEMA TO upsert_test_tmp; +INSERT INTO upsert_test.t_grammer VALUES(103, 3, '{102, 103, 104}', ROW(105, 106), '{107, 108}') ON DUPLICATE KEY UPDATE + t_grammer.c2 = EXCLUDED.c2 * 10, t_grammer.c3[1:2] = c5[1:2], t_grammer.c4.a = VALUES(c1); +SET CURRENT_SCHEMA TO upsert_test; +DROP SCHEMA upsert_test_tmp; + +-- support data type +INSERT INTO t_data VALUES(11, 11, 11, 11, 11, '11', '11', '11', '2020-04-23', '2020-04-23 11:00', '1:00') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_data VALUES(12, 12, 12, 12, 12, '12', '12', '12', '2020-04-24', '2020-04-24 11:00', '2:00') + ON DUPLICATE KEY UPDATE + c_tiny = 13, c_smallint = 13, c_bigint = 13, c_numeric = 13, + c_var = '13', c_text = '13', c_bytea = '13', c_date = '2020-05-24', c_timestamp = '2020-05-24 11:00', c_time = '3:00'; +INSERT INTO t_data VALUES(13, 13, 13, 13, 13, '13', '13', '13', '2020-04-25', '2020-04-25 11:00', '4:00') + ON DUPLICATE KEY UPDATE + c_tiny = EXCLUDED.c_tiny + 1, c_smallint = EXCLUDED.c_smallint + 1, c_bigint = EXCLUDED.c_bigint + 1, c_numeric = EXCLUDED.c_numeric + 1, + c_var = EXCLUDED.c_var || '3', c_text = EXCLUDED.c_text || '3', c_bytea = EXCLUDED.c_bytea || '3', + c_date = EXCLUDED.c_date + '1 day'::interval, c_timestamp = EXCLUDED.c_timestamp + '1 day'::interval, c_time = EXCLUDED.c_time + '1 hour'::interval; + +-- support UPDATE to default +INSERT INTO t_default DEFAULT VALUES ON DUPLICATE KEY UPDATE c2 = 100, c3 = '2020-05-17'; +INSERT INTO t_default VALUES(91, 0.91, '2020-05-17') ON DUPLICATE KEY UPDATE c2 = DEFAULT, c3 = DEFAULT; +INSERT INTO t_default VALUES(92, DEFAULT, '2020-05-17') ON DUPLICATE KEY UPDATE c2 = 10, c3 = DEFAULT; + +-- unsupport alias +INSERT INTO t_grammer AS alias VALUES (91) ON DUPLICATE KEY UPDATE c2 = VALUES(c1); + +-- unsupport VALUES with expr +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = sqrt(VALUES(c2)); +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c3[1]) + 1; +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a) + 1; +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.c3[1]) + 1; + +-- unsupport VALUES with table name +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.c4.a); +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.c2); + +-- unsupport DEFAULT with expr +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = DEFAULT + 1; + +-- unsupport user defined typed column's element +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a); +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = EXCLUDED.c4.a; +INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = c4.a; + +SELECT * FROM t_data ORDER BY 1; +SELECT * FROM t_grammer ORDER BY 1; +SELECT * FROM t_default ORDER BY 1; diff --git a/src/test/regress/sql/upsert_prepare.sql b/src/test/regress/sql/upsert_prepare.sql new file mode 100644 index 000000000..4ff4a7807 --- /dev/null +++ b/src/test/regress/sql/upsert_prepare.sql @@ -0,0 +1,76 @@ +-- Note: about upsert test +-- test point: +-- grammer +-- table, unlogged table, temp table, matview +-- constraint(primary key, index, foreign key, null ...) +-- trigger +-- sequence + +CREATE DATABASE upsert WITH TEMPLATE template0 ENCODING 'UTF8'; +\c upsert + +-- grammer test +CREATE SCHEMA upsert_test; +SET CURRENT_SCHEMA TO upsert_test; +CREATE TYPE atype AS(a int, b int); +CREATE TYPE btype AS(a int, b atype, c varchar[3]); +CREATE TABLE t_grammer (c1 INT PRIMARY KEY, c2 INT DEFAULT -100, c3 int[3], c4 atype, c5 int[2]); +CREATE TABLE t_default (c1 INT PRIMARY KEY DEFAULT 10, c2 FLOAT DEFAULT random(), c3 TIMESTAMP DEFAULT current_timestamp); +CREATE TABLE t_data (c_int INT PRIMARY KEY, c_tiny TINYINT, c_smallint SMALLINT, c_bigint BIGINT, c_numeric NUMERIC, + c_var VARCHAR, c_text TEXT, c_bytea BYTEA, c_date DATE, c_timestamp TIMESTAMP, c_time TIME, + c_intarray INT[3], c_com atype); +CREATE TABLE t_trigger (key INT PRIMARY KEY, color TEXT); + +-- unlogged table +CREATE SCHEMA upsert_test_unlog; +SET CURRENT_SCHEMA TO upsert_test_unlog; +CREATE TYPE atype AS(a int, b int); +CREATE TYPE btype AS(a int, b atype, c varchar[3]); +CREATE UNLOGGED TABLE t_hash_unlog_0 (c1 INT, c2 INT, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype); +CREATE UNLOGGED TABLE t_hash_unlog_1 (c1 INT, c2 INT PRIMARY KEY, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype); +CREATE UNLOGGED TABLE t_rep_unlog_0 (c1 INT, c2 INT, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype) ; +CREATE UNLOGGED TABLE t_rep_unlog_1 (c1 INT, c2 INT PRIMARY KEY, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype); + +-- temp table +-- temp table can not be created here, we create it in upsert_tmp_test. + + +-- restriction test +CREATE SCHEMA upsert_test_etc; +SET CURRENT_SCHEMA TO upsert_test_etc; +create table up_neg_01 (c1 int, c2 int, c3 int) with (ORIENTATION = COLUMN); +create table up_neg_02 (c1 int, c2 int, c3 int) with (ORIENTATION = COLUMN); +create table up_neg_03 (c1 int, c2 int UNIQUE DEFERRABLE, c3 int); +create table up_neg_04 (c1 int, c2 int UNIQUE, c3 int); + +create table up_neg_05(c1 int, c2 int, c3 int, c4 int unique, c5 int primary key, unique(c2,c3)); + +create table up_neg_0(c1 int, c2 int, c3 int, c4 int unique, c5 int primary key, unique(c2,c3)) ; + +create table up_neg_06(c1 int, c2 int, c3 int unique) ; +create table up_neg_07(c1 int, c2 int, c3 int unique) ; +create table up_neg_08(c1 int, c2 int, c3 int unique) ; +create table up_neg_09(c1 int, c2 int, c3 int, unique(c2,c3)) ; +create table up_neg_10(c1 int, c2 int, c3 int, unique(c2,c3)) ; +create view up_view as select *from up_neg_01; +create materialized view mat_view as select *from up_neg_01; +create table pkt (a int primary key, b int, c int); +create table fkt (a int primary key, b int references pkt, c int); + + +-- procedure test +CREATE SCHEMA upsert_test_procedure; + + +-- explain test +CREATE SCHEMA upsert_test_explain; +SET CURRENT_SCHEMA TO upsert_test_explain; +create table up_expl_hash(c1 int, c2 int, c3 int unique) ; +create table up_expl_repl(c1 int, c2 int, c3 int unique) ; +create table up_expl_part(c1 int, c2 int, c3 int unique) ; +create unlogged table up_expl_unlog(c1 int, c2 int, c3 int unique) ; +create table up_expl_node(c1 int, c2 int, c3 int unique) ; +create table up_expl_repl(c1 int, c2 int, c3 int unique) ; +create table up_expl_repl2(c1 int, c2 int, c3 int, c4 int unique, c5 int primary key, unique(c2,c3)); +-- create temp table up_expl_temp(c1 int, c2 int, c3 int unique); + diff --git a/src/test/regress/sql/upsert_restriction.sql b/src/test/regress/sql/upsert_restriction.sql new file mode 100644 index 000000000..65e714d41 --- /dev/null +++ b/src/test/regress/sql/upsert_restriction.sql @@ -0,0 +1,170 @@ +/* + * upsert negative test cases + * + table cases + col table + ORC table + VIEW + MAT view + index + DEFERABLE index + insert stmt + returning + with + with recur + update stmt + VALUES with expr + update distribute key + update primary key + update unique key + update partition key + where clause + with clause + sub query + */ +-------------------------------------------------------------------------------------------- +\c upsert +SET CURRENT_SCHEMA TO upsert_test_etc; +-- enable_upsert_to_merge must be off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +SHOW enable_upsert_to_merge; + +insert into up_neg_04 select a,a,a from generate_series(1,20) as a; +insert into up_neg_05 select a,a,a,a,a from generate_series(1,20) as a; +insert into up_neg_06 select a,a,a from generate_series(1,20) as a; +insert into up_neg_07 select a,a,a from generate_series(1,20) as a; +insert into up_neg_08 select a,a,a from generate_series(1,20) as a; +insert into up_neg_09 select a,a,a from generate_series(1,20) as a; +insert into up_neg_10 select a,a,a from generate_series(1,20) as a; + +-- table cases +---- col table +insert into up_neg_01 values(1,2,3) on duplicate key update c3 = 1; + +---- ORC table +insert into up_neg_02 values(1,2,3) on duplicate key update c3 = 1; + +---- VIEW +insert into up_view values(1) on duplicate key update c3 = 1; + +-- matview +refresh materialized view mat_view; +insert into mat_view values(1) on duplicate key update c3 = 1; + +-- index +---- DEFERABLE index +insert into up_neg_03 values(1,2,3) on duplicate key update c1 = 1; +insert into up_neg_03 values(1) on duplicate key update c1 = 1; + +-- insert stmt +----returning +insert into up_neg_04 values(1,1,1) on duplicate key update c1 = 1 returning c1; +----with +with sub as (select *from up_neg_04) +insert into up_neg_04 select *from sub on duplicate key update c1 =1; + +----with recur +with RECURSIVE sub as (select *from up_neg_04) +insert into up_neg_04 select *from sub on duplicate key update c1 =1; + +with sub as (select *from up_neg_04) +insert into up_neg_04 select *from sub on duplicate key update c1 =1 returning c1; + +-- update stmt +---- VALUES with expr +insert into up_neg_05 values(1,1,1,1) on duplicate key update c3 = values(1+100); +insert into up_neg_05 values(1,1,1,1) on duplicate key update c3 = values(100); + +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c1 = 1; +---- update primary key +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c5 = 1; +---- update unique key +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c4 = 1; +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c2 = 1, c3=2; +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c2 = 1; +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c3 = 2; +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c1 =1, c2 = 1, c3=2, c4=1,c5=1; + +---- where clause +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c4 = 1 where c1=1; +---- from clause +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c4 = 1 from up_neg_04 where c1=1; +---- sub query +insert into up_neg_05 values(1,1,1,1,1) on duplicate key update (c2) = (select c2 from up_neg_05); + +---- update distribute key +insert into up_neg_06 values(101, 1, 1) on duplicate key update c3=101; +---- update partition key +insert into up_neg_07 values(101, 1, 300) on duplicate key update c3=101; +insert into up_neg_08 values(101, 1, 300) on duplicate key update c3=101; +insert into up_neg_09 values(101, 1, 1) on duplicate key update c3=101; +insert into up_neg_09 values(101, 1, 1) on duplicate key update c2=101; +insert into up_neg_09 values(101, 1, 1) on duplicate key update c2 =1,c3=101; + +--update unique key mul type plans +EXPLAIN (VERBOSE on, COSTS off) insert into up_neg_10 values(101, 1, 300) on duplicate key update c1=101; +insert into up_neg_10 values(101, 1, 300) on duplicate key update c3=101; + +--trigger +create table upsert_base +(c1 int primary key, + c2 varchar(100), --record the type + cold varchar(1024), + cnew varchar(1024) ); +create table upsert_tri +(c1 int, + c2 varchar(100), + c3 int, + unique(c1,c3) +); + +--S1 after trigger +CREATE OR REPLACE FUNCTION trig_after() RETURNS TRIGGER AS $emp_audit$ + BEGIN + IF (TG_OP = 'INSERT') THEN + insert into upsert_base(c2,cnew) values('after insert',new.c2); + RETURN NEW; + ELSIF (TG_OP = 'UPDATE') THEN + insert into upsert_base(c2,cold,cnew) values('after update',old.c2,new.c2); + RETURN NEW; + END IF; + RETURN NULL; + END; +$emp_audit$ LANGUAGE plpgsql; +CREATE TRIGGER tri_after +AFTER UPDATE OR INSERT ON upsert_tri + FOR EACH ROW EXECUTE PROCEDURE trig_after(); +--S2 before trigger +CREATE OR REPLACE FUNCTION trig_before() RETURNS TRIGGER AS $emp_audit$ + BEGIN + IF (TG_OP = 'INSERT') THEN + insert into upsert_base(c2,cnew) values('before insert',new.c2); + RETURN NEW; + ELSIF (TG_OP = 'UPDATE') THEN + insert into upsert_base(c2,cold,cnew) values('before update',old.c2,new.c2); + RETURN NEW; + END IF; + RETURN NULL; + END; +$emp_audit$ LANGUAGE plpgsql; +CREATE TRIGGER tri_before +BEFORE UPDATE OR INSERT ON upsert_tri + FOR EACH ROW EXECUTE PROCEDURE trig_before(); +---- upsert-insert +insert into upsert_tri(c1,c2,c3) values(1000,'abc',1) on duplicate key update nothing; +insert into upsert_tri(c1,c2,c3) values(2000,'abc',1) on duplicate key update c2='bcd'; +---- upsert-nothing +insert into upsert_tri(c1,c2,c3) values(1000,'abc',1) on duplicate key update nothing; +---- upsert-update +insert into upsert_tri(c1,c2,c3) values(2000,'abc',1) on duplicate key update c2='bcd'; +---- check results +SELECT * from upsert_tri; +SELECT * from upsert_base; + +-- foreign key +insert into pkt values(1,1,1),(2,2,2),(3,3,3),(4,4,4),(5,5,5); +insert into fkt values(1,1,1),(2,2,2),(3,3,3); + +insert into fkt values(1,1,1),(2,2,2),(3,3,3) on duplicate key update b=excluded.b+1, c=0; +insert into fkt values(1,1,1),(2,2,2),(3,3,3) on duplicate key update b=excluded.b+3, c=-1; + diff --git a/src/test/regress/sql/upsert_tmp_test.sql b/src/test/regress/sql/upsert_tmp_test.sql new file mode 100644 index 000000000..62795fc47 --- /dev/null +++ b/src/test/regress/sql/upsert_tmp_test.sql @@ -0,0 +1,99 @@ +\c upsert +CREATE SCHEMA upsert_test_tmp; +SET CURRENT_SCHEMA TO upsert_test_tmp; +CREATE TYPE atype AS(a int, b int); +CREATE TYPE btype AS(a int, b atype, c varchar[3]); +CREATE TEMP TABLE t_hash_tmp_0 (c1 INT, c2 INT, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype); +CREATE TEMP TABLE t_hash_tmp_1 (c1 INT, c2 INT PRIMARY KEY, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype); +CREATE TEMP TABLE t_rep_tmp_0 (c1 INT, c2 INT, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype) ; +CREATE TEMP TABLE t_rep_tmp_1 (c1 INT, c2 INT PRIMARY KEY, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype) ; + +-- enable_upsert_to_merge must be off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +SHOW enable_upsert_to_merge; + +-- hash table +-- tmp table without primary key +INSERT INTO t_hash_tmp_0 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_hash_tmp_0 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE c2 = 100; + +-- tmp table with primary key +-- multi insert +INSERT INTO t_hash_tmp_1 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_hash_tmp_1 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_hash_tmp_1 ORDER BY c2; + +INSERT INTO t_hash_tmp_1 VALUES(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 2, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_hash_tmp_1 VALUES(20, 3, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (21, 4, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_hash_tmp_1 ORDER BY c2; + +-- insert and update same tuple, and update twice for another same tuple +INSERT INTO t_hash_tmp_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')), + (10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE NOTHING; +SELECT * FROM t_hash_tmp_1 ORDER BY c2; + +INSERT INTO t_hash_tmp_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')), + (10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_hash_tmp_1 ORDER BY c2; + +-- replication table +-- tmp table without primary key +INSERT INTO t_rep_tmp_0 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_rep_tmp_0 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE c2 = 100; + +-- tmp table with primary key +-- multi insert +INSERT INTO t_rep_tmp_1 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_rep_tmp_1 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_rep_tmp_1 ORDER BY c2; + +INSERT INTO t_rep_tmp_1 VALUES(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 2, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_rep_tmp_1 VALUES(20, 3, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (21, 4, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_rep_tmp_1 ORDER BY c2; + +-- insert and update same tuple, and update twice for another same tuple +INSERT INTO t_rep_tmp_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')), + (10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE NOTHING; +SELECT * FROM t_rep_tmp_1 ORDER BY c2; + +INSERT INTO t_rep_tmp_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')), + (10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_rep_tmp_1 ORDER BY c2; + +DROP SCHEMA upsert_test_tmp CASCADE; diff --git a/src/test/regress/sql/upsert_trigger_test.sql b/src/test/regress/sql/upsert_trigger_test.sql new file mode 100644 index 000000000..b731ad50e --- /dev/null +++ b/src/test/regress/sql/upsert_trigger_test.sql @@ -0,0 +1,63 @@ +\c upsert +SET CURRENT_SCHEMA TO upsert_test; +-- enable_upsert_to_merge must be off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +SHOW enable_upsert_to_merge; + +CREATE FUNCTION upsert_before_func() + RETURNS TRIGGER language plpgsql AS +$$ +BEGIN + IF (TG_OP = 'UPDATE') THEN + RAISE warning 'before update (old): %', old.*::TEXT; + RAISE warning 'before update (new): %', new.*::TEXT; + elsIF (TG_OP = 'INSERT') THEN + RAISE warning 'before insert (new): %', new.*::TEXT; + IF NEW.key % 2 = 0 THEN + NEW.color := NEW.color || ' trig modified'; + RAISE warning 'before insert (new, modified): %', new.*::TEXT; + END IF; + END IF; + RETURN new; +END; +$$; +CREATE TRIGGER upsert_before_trig BEFORE INSERT OR UPDATE ON t_trigger + FOR EACH ROW EXECUTE procedure upsert_before_func(); + +CREATE FUNCTION upsert_after_func() + RETURNS TRIGGER language plpgsql AS +$$ +BEGIN + IF (TG_OP = 'UPDATE') THEN + RAISE warning 'after update (old): %', old.*::TEXT; + RAISE warning 'after update (new): %', new.*::TEXT; + elsIF (TG_OP = 'INSERT') THEN + RAISE warning 'after insert (new): %', new.*::TEXT; + END IF; + RETURN null; +END; +$$; +CREATE TRIGGER upsert_after_trig AFTER INSERT OR UPDATE ON t_trigger + FOR EACH ROW EXECUTE procedure upsert_after_func(); + +INSERT INTO t_trigger values(1, 'black') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +INSERT INTO t_trigger values(2, 'red') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +INSERT INTO t_trigger values(3, 'orange') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +INSERT INTO t_trigger values(4, 'green') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +INSERT INTO t_trigger values(5, 'purple') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +INSERT INTO t_trigger values(6, 'white') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +INSERT INTO t_trigger values(7, 'pink') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +INSERT INTO t_trigger values(8, 'yellow') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; + +SELECT * FROM t_trigger ORDER BY key; + +INSERT INTO t_trigger values(2, 'black') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +INSERT INTO t_trigger values(3, 'red') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +INSERT INTO t_trigger values(4, 'orange') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +INSERT INTO t_trigger values(5, 'green') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +INSERT INTO t_trigger values(6, 'purple') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +INSERT INTO t_trigger values(7, 'white') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +INSERT INTO t_trigger values(8, 'pink') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; +INSERT INTO t_trigger values(9, 'yellow') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color; + +SELECT * FROM t_trigger ORDER BY key; diff --git a/src/test/regress/sql/upsert_unlog_test.sql b/src/test/regress/sql/upsert_unlog_test.sql new file mode 100644 index 000000000..22036601f --- /dev/null +++ b/src/test/regress/sql/upsert_unlog_test.sql @@ -0,0 +1,89 @@ +\c upsert +SET CURRENT_SCHEMA TO upsert_test_unlog; +-- enable_upsert_to_merge must be off, or upsert will be translated to merge. +SET enable_upsert_to_merge TO OFF; +SHOW enable_upsert_to_merge; + +-- hash table +-- unlogged table without primary key +INSERT INTO t_hash_unlog_0 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_hash_unlog_0 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE c2 = 100; + +-- unlogged table with primary key +-- multi insert +INSERT INTO t_hash_unlog_1 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_hash_unlog_1 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_hash_unlog_1 ORDER BY c2; + +INSERT INTO t_hash_unlog_1 VALUES(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 2, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_hash_unlog_1 VALUES(20, 3, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (21, 4, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_hash_unlog_1 ORDER BY c2; + +-- insert and update same tuple, and update twice for another same tuple +INSERT INTO t_hash_unlog_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')), + (10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE NOTHING; +SELECT * FROM t_hash_unlog_1 ORDER BY c2; + +INSERT INTO t_hash_unlog_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')), + (10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_hash_unlog_1 ORDER BY c2; + +-- replication table +-- unlogged table without primary key +INSERT INTO t_rep_unlog_0 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_rep_unlog_0 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE c2 = 100; + +-- unlogged table with primary key +-- multi insert +INSERT INTO t_rep_unlog_1 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_rep_unlog_1 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')), + (2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_rep_unlog_1 ORDER BY c2; + +INSERT INTO t_rep_unlog_1 VALUES(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 2, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO t_rep_unlog_1 VALUES(20, 3, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (21, 4, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_rep_unlog_1 ORDER BY c2; + +-- insert and update same tuple, and update twice for another same tuple +INSERT INTO t_rep_unlog_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')), + (10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE NOTHING; +SELECT * FROM t_rep_unlog_1 ORDER BY c2; + +INSERT INTO t_rep_unlog_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')), + (10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')), + (11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')) + ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7; +SELECT * FROM t_rep_unlog_1 ORDER BY c2;