summaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/regress/expected/merge.out266
-rw-r--r--src/test/regress/expected/rowsecurity.out32
-rw-r--r--src/test/regress/expected/rules.out16
-rw-r--r--src/test/regress/expected/updatable_views.out30
-rw-r--r--src/test/regress/expected/with.out10
-rw-r--r--src/test/regress/sql/merge.sql169
-rw-r--r--src/test/regress/sql/rowsecurity.sql21
-rw-r--r--src/test/regress/sql/rules.sql6
-rw-r--r--src/test/regress/sql/updatable_views.sql9
-rw-r--r--src/test/regress/sql/with.sql8
10 files changed, 521 insertions, 46 deletions
diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out
index 1a6f6ad43d1..07561f02407 100644
--- a/src/test/regress/expected/merge.out
+++ b/src/test/regress/expected/merge.out
@@ -124,20 +124,20 @@ ON tid = tid
WHEN MATCHED THEN DO NOTHING;
ERROR: name "target" specified more than once
DETAIL: The name is used both as MERGE target table and data source.
--- used in a CTE
+-- used in a CTE without RETURNING
WITH foo AS (
MERGE INTO target USING source ON (true)
WHEN MATCHED THEN DELETE
) SELECT * FROM foo;
-ERROR: MERGE not supported in WITH query
-LINE 1: WITH foo AS (
- ^
--- used in COPY
+ERROR: WITH query "foo" does not have a RETURNING clause
+LINE 4: ) SELECT * FROM foo;
+ ^
+-- used in COPY without RETURNING
COPY (
MERGE INTO target USING source ON (true)
WHEN MATCHED THEN DELETE
) TO stdout;
-ERROR: MERGE not supported in COPY
+ERROR: COPY query must have a RETURNING clause
-- unsupported relation types
-- materialized view
CREATE MATERIALIZED VIEW mv AS SELECT * FROM target;
@@ -1255,7 +1255,7 @@ BEGIN;
MERGE INTO sq_target
USING v
ON tid = sid
-WHEN MATCHED AND tid > 2 THEN
+WHEN MATCHED AND tid >= 2 THEN
UPDATE SET balance = balance + delta
WHEN NOT MATCHED THEN
INSERT (balance, tid) VALUES (balance + delta, sid)
@@ -1270,7 +1270,7 @@ INSERT INTO sq_source (sid, balance, delta) VALUES (-1, -1, -10);
MERGE INTO sq_target t
USING v
ON tid = sid
-WHEN MATCHED AND tid > 2 THEN
+WHEN MATCHED AND tid >= 2 THEN
UPDATE SET balance = t.balance + delta
WHEN NOT MATCHED THEN
INSERT (balance, tid) VALUES (balance + delta, sid)
@@ -1294,7 +1294,7 @@ WITH targq AS (
MERGE INTO sq_target t
USING v
ON tid = sid
-WHEN MATCHED AND tid > 2 THEN
+WHEN MATCHED AND tid >= 2 THEN
UPDATE SET balance = t.balance + delta
WHEN NOT MATCHED THEN
INSERT (balance, tid) VALUES (balance + delta, sid)
@@ -1302,21 +1302,207 @@ WHEN MATCHED AND tid < 2 THEN
DELETE;
ROLLBACK;
-- RETURNING
+SELECT * FROM sq_source ORDER BY sid;
+ delta | sid | balance
+-------+-----+---------
+ 10 | 1 | 0
+ 20 | 2 | 0
+ 40 | 4 | 0
+(3 rows)
+
+SELECT * FROM sq_target ORDER BY tid;
+ tid | balance
+-----+---------
+ 1 | 100
+ 2 | 200
+ 3 | 300
+(3 rows)
+
BEGIN;
-INSERT INTO sq_source (sid, balance, delta) VALUES (-1, -1, -10);
+CREATE TABLE merge_actions(action text, abbrev text);
+INSERT INTO merge_actions VALUES ('INSERT', 'ins'), ('UPDATE', 'upd'), ('DELETE', 'del');
MERGE INTO sq_target t
-USING v
+USING sq_source s
ON tid = sid
-WHEN MATCHED AND tid > 2 THEN
+WHEN MATCHED AND tid >= 2 THEN
UPDATE SET balance = t.balance + delta
WHEN NOT MATCHED THEN
- INSERT (balance, tid) VALUES (balance + delta, sid)
+ INSERT (balance, tid) VALUES (balance + delta, sid)
WHEN MATCHED AND tid < 2 THEN
- DELETE
-RETURNING *;
-ERROR: syntax error at or near "RETURNING"
-LINE 10: RETURNING *;
- ^
+ DELETE
+RETURNING (SELECT abbrev FROM merge_actions
+ WHERE action = merge_action()) AS action,
+ t.*,
+ CASE merge_action()
+ WHEN 'INSERT' THEN 'Inserted '||t
+ WHEN 'UPDATE' THEN 'Added '||delta||' to balance'
+ WHEN 'DELETE' THEN 'Removed '||t
+ END AS description;
+ action | tid | balance | description
+--------+-----+---------+---------------------
+ del | 1 | 100 | Removed (1,100)
+ upd | 2 | 220 | Added 20 to balance
+ ins | 4 | 40 | Inserted (4,40)
+(3 rows)
+
+ROLLBACK;
+-- error when using merge_action() outside MERGE
+SELECT merge_action() FROM sq_target;
+ERROR: MERGE_ACTION() can only be used in the RETURNING list of a MERGE command
+LINE 1: SELECT merge_action() FROM sq_target;
+ ^
+UPDATE sq_target SET balance = balance + 1 RETURNING merge_action();
+ERROR: MERGE_ACTION() can only be used in the RETURNING list of a MERGE command
+LINE 1: ...ATE sq_target SET balance = balance + 1 RETURNING merge_acti...
+ ^
+-- RETURNING in CTEs
+CREATE TABLE sq_target_merge_log (tid integer NOT NULL, last_change text);
+INSERT INTO sq_target_merge_log VALUES (1, 'Original value');
+BEGIN;
+WITH m AS (
+ MERGE INTO sq_target t
+ USING sq_source s
+ ON tid = sid
+ WHEN MATCHED AND tid >= 2 THEN
+ UPDATE SET balance = t.balance + delta
+ WHEN NOT MATCHED THEN
+ INSERT (balance, tid) VALUES (balance + delta, sid)
+ WHEN MATCHED AND tid < 2 THEN
+ DELETE
+ RETURNING merge_action() AS action, t.*,
+ CASE merge_action()
+ WHEN 'INSERT' THEN 'Inserted '||t
+ WHEN 'UPDATE' THEN 'Added '||delta||' to balance'
+ WHEN 'DELETE' THEN 'Removed '||t
+ END AS description
+), m2 AS (
+ MERGE INTO sq_target_merge_log l
+ USING m
+ ON l.tid = m.tid
+ WHEN MATCHED THEN
+ UPDATE SET last_change = description
+ WHEN NOT MATCHED THEN
+ INSERT VALUES (m.tid, description)
+ RETURNING action, merge_action() AS log_action, l.*
+)
+SELECT * FROM m2;
+ action | log_action | tid | last_change
+--------+------------+-----+---------------------
+ DELETE | UPDATE | 1 | Removed (1,100)
+ UPDATE | INSERT | 2 | Added 20 to balance
+ INSERT | INSERT | 4 | Inserted (4,40)
+(3 rows)
+
+SELECT * FROM sq_target_merge_log ORDER BY tid;
+ tid | last_change
+-----+---------------------
+ 1 | Removed (1,100)
+ 2 | Added 20 to balance
+ 4 | Inserted (4,40)
+(3 rows)
+
+ROLLBACK;
+-- COPY (MERGE ... RETURNING) TO ...
+BEGIN;
+COPY (
+ MERGE INTO sq_target t
+ USING sq_source s
+ ON tid = sid
+ WHEN MATCHED AND tid >= 2 THEN
+ UPDATE SET balance = t.balance + delta
+ WHEN NOT MATCHED THEN
+ INSERT (balance, tid) VALUES (balance + delta, sid)
+ WHEN MATCHED AND tid < 2 THEN
+ DELETE
+ RETURNING merge_action(), t.*
+) TO stdout;
+DELETE 1 100
+UPDATE 2 220
+INSERT 4 40
+ROLLBACK;
+-- SQL function with MERGE ... RETURNING
+BEGIN;
+CREATE FUNCTION merge_into_sq_target(sid int, balance int, delta int,
+ OUT action text, OUT tid int, OUT new_balance int)
+LANGUAGE sql AS
+$$
+ MERGE INTO sq_target t
+ USING (VALUES ($1, $2, $3)) AS v(sid, balance, delta)
+ ON tid = v.sid
+ WHEN MATCHED AND tid >= 2 THEN
+ UPDATE SET balance = t.balance + v.delta
+ WHEN NOT MATCHED THEN
+ INSERT (balance, tid) VALUES (v.balance + v.delta, v.sid)
+ WHEN MATCHED AND tid < 2 THEN
+ DELETE
+ RETURNING merge_action(), t.*;
+$$;
+SELECT m.*
+FROM (VALUES (1, 0, 0), (3, 0, 20), (4, 100, 10)) AS v(sid, balance, delta),
+LATERAL (SELECT action, tid, new_balance FROM merge_into_sq_target(sid, balance, delta)) m;
+ action | tid | new_balance
+--------+-----+-------------
+ DELETE | 1 | 100
+ UPDATE | 3 | 320
+ INSERT | 4 | 110
+(3 rows)
+
+ROLLBACK;
+-- SQL SRF with MERGE ... RETURNING
+BEGIN;
+CREATE FUNCTION merge_sq_source_into_sq_target()
+RETURNS TABLE (action text, tid int, balance int)
+LANGUAGE sql AS
+$$
+ MERGE INTO sq_target t
+ USING sq_source s
+ ON tid = sid
+ WHEN MATCHED AND tid >= 2 THEN
+ UPDATE SET balance = t.balance + delta
+ WHEN NOT MATCHED THEN
+ INSERT (balance, tid) VALUES (balance + delta, sid)
+ WHEN MATCHED AND tid < 2 THEN
+ DELETE
+ RETURNING merge_action(), t.*;
+$$;
+SELECT * FROM merge_sq_source_into_sq_target();
+ action | tid | balance
+--------+-----+---------
+ DELETE | 1 | 100
+ UPDATE | 2 | 220
+ INSERT | 4 | 40
+(3 rows)
+
+ROLLBACK;
+-- PL/pgSQL function with MERGE ... RETURNING ... INTO
+BEGIN;
+CREATE FUNCTION merge_into_sq_target(sid int, balance int, delta int,
+ OUT r_action text, OUT r_tid int, OUT r_balance int)
+LANGUAGE plpgsql AS
+$$
+BEGIN
+ MERGE INTO sq_target t
+ USING (VALUES ($1, $2, $3)) AS v(sid, balance, delta)
+ ON tid = v.sid
+ WHEN MATCHED AND tid >= 2 THEN
+ UPDATE SET balance = t.balance + v.delta
+ WHEN NOT MATCHED THEN
+ INSERT (balance, tid) VALUES (v.balance + v.delta, v.sid)
+ WHEN MATCHED AND tid < 2 THEN
+ DELETE
+ RETURNING merge_action(), t.* INTO r_action, r_tid, r_balance;
+END;
+$$;
+SELECT m.*
+FROM (VALUES (1, 0, 0), (3, 0, 20), (4, 100, 10)) AS v(sid, balance, delta),
+LATERAL (SELECT r_action, r_tid, r_balance FROM merge_into_sq_target(sid, balance, delta)) m;
+ r_action | r_tid | r_balance
+----------+-------+-----------
+ DELETE | 1 | 100
+ UPDATE | 3 | 320
+ INSERT | 4 | 110
+(3 rows)
+
ROLLBACK;
-- EXPLAIN
CREATE TABLE ex_mtarget (a int, b int)
@@ -1563,7 +1749,7 @@ SELECT * FROM sq_target WHERE tid = 1;
(1 row)
ROLLBACK;
-DROP TABLE sq_target, sq_source CASCADE;
+DROP TABLE sq_target, sq_target_merge_log, sq_source CASCADE;
NOTICE: drop cascades to view v
CREATE TABLE pa_target (tid integer, balance float, val text)
PARTITION BY LIST (tid);
@@ -1689,6 +1875,32 @@ SELECT * FROM pa_target ORDER BY tid;
(14 rows)
ROLLBACK;
+-- update partition key to partition not initially scanned
+BEGIN;
+MERGE INTO pa_target t
+ USING pa_source s
+ ON t.tid = s.sid AND t.tid = 1
+ WHEN MATCHED THEN
+ UPDATE SET tid = tid + 1, balance = balance + delta, val = val || ' updated by merge'
+ RETURNING merge_action(), t.*;
+ merge_action | tid | balance | val
+--------------+-----+---------+--------------------------
+ UPDATE | 2 | 110 | initial updated by merge
+(1 row)
+
+SELECT * FROM pa_target ORDER BY tid;
+ tid | balance | val
+-----+---------+--------------------------
+ 2 | 110 | initial updated by merge
+ 3 | 300 | initial
+ 5 | 500 | initial
+ 7 | 700 | initial
+ 9 | 900 | initial
+ 11 | 1100 | initial
+ 13 | 1300 | initial
+(7 rows)
+
+ROLLBACK;
DROP TABLE pa_target CASCADE;
-- The target table is partitioned in the same way, but this time by attaching
-- partitions which have columns in different order, dropped columns etc.
@@ -1938,7 +2150,21 @@ MERGE INTO pa_target t
WHEN MATCHED THEN
UPDATE SET balance = balance + delta, val = val || ' updated by merge'
WHEN NOT MATCHED THEN
- INSERT VALUES (slogts::timestamp, sid, delta, 'inserted by merge');
+ INSERT VALUES (slogts::timestamp, sid, delta, 'inserted by merge')
+ RETURNING merge_action(), t.*;
+ merge_action | logts | tid | balance | val
+--------------+--------------------------+-----+---------+--------------------------
+ UPDATE | Tue Jan 31 00:00:00 2017 | 1 | 110 | initial updated by merge
+ UPDATE | Tue Feb 28 00:00:00 2017 | 2 | 220 | initial updated by merge
+ INSERT | Sun Jan 15 00:00:00 2017 | 3 | 30 | inserted by merge
+ UPDATE | Tue Jan 31 00:00:00 2017 | 4 | 440 | initial updated by merge
+ UPDATE | Tue Feb 28 00:00:00 2017 | 5 | 550 | initial updated by merge
+ INSERT | Sun Jan 15 00:00:00 2017 | 6 | 60 | inserted by merge
+ UPDATE | Tue Jan 31 00:00:00 2017 | 7 | 770 | initial updated by merge
+ UPDATE | Tue Feb 28 00:00:00 2017 | 8 | 880 | initial updated by merge
+ INSERT | Sun Jan 15 00:00:00 2017 | 9 | 90 | inserted by merge
+(9 rows)
+
SELECT * FROM pa_target ORDER BY tid;
logts | tid | balance | val
--------------------------+-----+---------+--------------------------
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index 4538f0c37d5..d507a2c8ca5 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -2302,6 +2302,35 @@ WHEN MATCHED THEN
UPDATE SET dnotes = dnotes || ' notes added by merge8 '
WHEN NOT MATCHED THEN
INSERT VALUES (13, 44, 1, 'regress_rls_bob', 'new manga');
+SELECT * FROM document WHERE did = 13;
+ did | cid | dlevel | dauthor | dtitle | dnotes
+-----+-----+--------+---------+--------+--------
+(0 rows)
+
+-- but not OK if RETURNING is used
+MERGE INTO document d
+USING (SELECT 14 as sdid) s
+ON did = s.sdid
+WHEN MATCHED THEN
+ UPDATE SET dnotes = dnotes || ' notes added by merge9 '
+WHEN NOT MATCHED THEN
+ INSERT VALUES (14, 44, 1, 'regress_rls_bob', 'new manga')
+RETURNING *;
+ERROR: new row violates row-level security policy for table "document"
+-- but OK if new row is visible
+MERGE INTO document d
+USING (SELECT 14 as sdid) s
+ON did = s.sdid
+WHEN MATCHED THEN
+ UPDATE SET dnotes = dnotes || ' notes added by merge10 '
+WHEN NOT MATCHED THEN
+ INSERT VALUES (14, 11, 1, 'regress_rls_bob', 'new novel')
+RETURNING *;
+ sdid | did | cid | dlevel | dauthor | dtitle | dnotes
+------+-----+-----+--------+-----------------+-----------+--------
+ 14 | 14 | 11 | 1 | regress_rls_bob | new novel |
+(1 row)
+
RESET SESSION AUTHORIZATION;
-- drop the restrictive SELECT policy so that we can look at the
-- final state of the table
@@ -2325,7 +2354,8 @@ SELECT * FROM document;
12 | 11 | 1 | regress_rls_bob | another novel |
1 | 11 | 1 | regress_rls_bob | my first novel | notes added by merge2 notes added by merge3 notes added by merge4 notes added by merge7
13 | 44 | 1 | regress_rls_bob | new manga |
-(15 rows)
+ 14 | 11 | 1 | regress_rls_bob | new novel |
+(16 rows)
--
-- ROLE/GROUP
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 0cd2c64fca1..84e359f6ed9 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -3638,7 +3638,7 @@ MERGE INTO rule_merge1 t USING (SELECT 1 AS a) s
-- test deparsing
CREATE TABLE sf_target(id int, data text, filling int[]);
CREATE FUNCTION merge_sf_test()
- RETURNS void
+ RETURNS TABLE(action text, a int, b text, id int, data text, filling int[])
LANGUAGE sql
BEGIN ATOMIC
MERGE INTO sf_target t
@@ -3675,11 +3675,13 @@ WHEN NOT MATCHED
VALUES (s.a, s.b, DEFAULT)
WHEN NOT MATCHED
THEN INSERT (filling[1], id)
- VALUES (s.a, s.a);
+ VALUES (s.a, s.a)
+RETURNING
+ merge_action() AS action, *;
END;
\sf merge_sf_test
CREATE OR REPLACE FUNCTION public.merge_sf_test()
- RETURNS void
+ RETURNS TABLE(action text, a integer, b text, id integer, data text, filling integer[])
LANGUAGE sql
BEGIN ATOMIC
MERGE INTO sf_target t
@@ -3716,7 +3718,13 @@ BEGIN ATOMIC
VALUES (s.a, s.b, DEFAULT)
WHEN NOT MATCHED
THEN INSERT (filling[1], id)
- VALUES (s.a, s.a);
+ VALUES (s.a, s.a)
+ RETURNING MERGE_ACTION() AS action,
+ s.a,
+ s.b,
+ t.id,
+ t.data,
+ t.filling;
END
DROP FUNCTION merge_sf_test;
DROP TABLE sf_target;
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 794cf9cf930..1062c341d8e 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -479,7 +479,15 @@ MERGE INTO rw_view1 t
(2, 'ROW 2'), (3, 'ROW 3')) AS v(a,b) ON t.a = v.a
WHEN MATCHED AND t.a <= 1 THEN UPDATE SET b = v.b
WHEN MATCHED THEN DELETE
- WHEN NOT MATCHED AND a > 0 THEN INSERT (a) VALUES (v.a);
+ WHEN NOT MATCHED AND a > 0 THEN INSERT (a) VALUES (v.a)
+ RETURNING merge_action(), v.*, t.*;
+ merge_action | a | b | a | b
+--------------+---+-------+---+-------------
+ UPDATE | 1 | ROW 1 | 1 | ROW 1
+ DELETE | 3 | ROW 3 | 3 | Row 3
+ INSERT | 2 | ROW 2 | 2 | Unspecified
+(3 rows)
+
SELECT * FROM base_tbl ORDER BY a;
a | b
----+-------------
@@ -631,7 +639,15 @@ MERGE INTO rw_view2 t
USING (VALUES (3, 'R3'), (4, 'R4'), (5, 'R5')) AS v(a,b) ON aaa = v.a
WHEN MATCHED AND aaa = 3 THEN DELETE
WHEN MATCHED THEN UPDATE SET bbb = v.b
- WHEN NOT MATCHED THEN INSERT (aaa) VALUES (v.a);
+ WHEN NOT MATCHED THEN INSERT (aaa) VALUES (v.a)
+ RETURNING merge_action(), v.*, t.*;
+ merge_action | a | b | aaa | bbb
+--------------+---+----+-----+-------------
+ DELETE | 3 | R3 | 3 | Row 3
+ UPDATE | 4 | R4 | 4 | R4
+ INSERT | 5 | R5 | 5 | Unspecified
+(3 rows)
+
SELECT * FROM rw_view2 ORDER BY aaa;
aaa | bbb
-----+-------------
@@ -1071,7 +1087,15 @@ MERGE INTO rw_view2 t
USING (SELECT x, 'R'||x FROM generate_series(0,3) x) AS s(a,b) ON t.a = s.a
WHEN MATCHED AND t.a <= 1 THEN DELETE
WHEN MATCHED THEN UPDATE SET b = s.b
- WHEN NOT MATCHED AND s.a > 0 THEN INSERT VALUES (s.a, s.b);
+ WHEN NOT MATCHED AND s.a > 0 THEN INSERT VALUES (s.a, s.b)
+ RETURNING merge_action(), s.*, t.*;
+ merge_action | a | b | a | b
+--------------+---+----+---+-------
+ DELETE | 1 | R1 | 1 | Row 1
+ UPDATE | 2 | R2 | 2 | R2
+ INSERT | 3 | R3 | 3 | R3
+(3 rows)
+
SELECT * FROM base_tbl ORDER BY a;
a | b
----+--------
diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out
index 7d796ea69cd..6de347b528d 100644
--- a/src/test/regress/expected/with.out
+++ b/src/test/regress/expected/with.out
@@ -3493,6 +3493,16 @@ SELECT * FROM t;
ERROR: WITH query "t" does not have a RETURNING clause
LINE 4: SELECT * FROM t;
^
+-- RETURNING tries to return its own output
+WITH RECURSIVE t(action, a) AS (
+ MERGE INTO y USING (VALUES (11)) v(a) ON y.a = v.a
+ WHEN NOT MATCHED THEN INSERT VALUES (v.a)
+ RETURNING merge_action(), (SELECT a FROM t)
+)
+SELECT * FROM t;
+ERROR: recursive query "t" must not contain data-modifying statements
+LINE 1: WITH RECURSIVE t(action, a) AS (
+ ^
-- data-modifying WITH allowed only at the top level
SELECT * FROM (
WITH t AS (UPDATE y SET a=a+1 RETURNING *)
diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql
index dbbba2a1cdd..875cf6f93d7 100644
--- a/src/test/regress/sql/merge.sql
+++ b/src/test/regress/sql/merge.sql
@@ -88,12 +88,12 @@ MERGE INTO target
USING target
ON tid = tid
WHEN MATCHED THEN DO NOTHING;
--- used in a CTE
+-- used in a CTE without RETURNING
WITH foo AS (
MERGE INTO target USING source ON (true)
WHEN MATCHED THEN DELETE
) SELECT * FROM foo;
--- used in COPY
+-- used in COPY without RETURNING
COPY (
MERGE INTO target USING source ON (true)
WHEN MATCHED THEN DELETE
@@ -817,7 +817,7 @@ BEGIN;
MERGE INTO sq_target
USING v
ON tid = sid
-WHEN MATCHED AND tid > 2 THEN
+WHEN MATCHED AND tid >= 2 THEN
UPDATE SET balance = balance + delta
WHEN NOT MATCHED THEN
INSERT (balance, tid) VALUES (balance + delta, sid)
@@ -830,7 +830,7 @@ INSERT INTO sq_source (sid, balance, delta) VALUES (-1, -1, -10);
MERGE INTO sq_target t
USING v
ON tid = sid
-WHEN MATCHED AND tid > 2 THEN
+WHEN MATCHED AND tid >= 2 THEN
UPDATE SET balance = t.balance + delta
WHEN NOT MATCHED THEN
INSERT (balance, tid) VALUES (balance + delta, sid)
@@ -848,7 +848,7 @@ WITH targq AS (
MERGE INTO sq_target t
USING v
ON tid = sid
-WHEN MATCHED AND tid > 2 THEN
+WHEN MATCHED AND tid >= 2 THEN
UPDATE SET balance = t.balance + delta
WHEN NOT MATCHED THEN
INSERT (balance, tid) VALUES (balance + delta, sid)
@@ -857,18 +857,149 @@ WHEN MATCHED AND tid < 2 THEN
ROLLBACK;
-- RETURNING
+SELECT * FROM sq_source ORDER BY sid;
+SELECT * FROM sq_target ORDER BY tid;
+
BEGIN;
-INSERT INTO sq_source (sid, balance, delta) VALUES (-1, -1, -10);
+CREATE TABLE merge_actions(action text, abbrev text);
+INSERT INTO merge_actions VALUES ('INSERT', 'ins'), ('UPDATE', 'upd'), ('DELETE', 'del');
MERGE INTO sq_target t
-USING v
+USING sq_source s
ON tid = sid
-WHEN MATCHED AND tid > 2 THEN
+WHEN MATCHED AND tid >= 2 THEN
UPDATE SET balance = t.balance + delta
WHEN NOT MATCHED THEN
- INSERT (balance, tid) VALUES (balance + delta, sid)
+ INSERT (balance, tid) VALUES (balance + delta, sid)
WHEN MATCHED AND tid < 2 THEN
- DELETE
-RETURNING *;
+ DELETE
+RETURNING (SELECT abbrev FROM merge_actions
+ WHERE action = merge_action()) AS action,
+ t.*,
+ CASE merge_action()
+ WHEN 'INSERT' THEN 'Inserted '||t
+ WHEN 'UPDATE' THEN 'Added '||delta||' to balance'
+ WHEN 'DELETE' THEN 'Removed '||t
+ END AS description;
+ROLLBACK;
+
+-- error when using merge_action() outside MERGE
+SELECT merge_action() FROM sq_target;
+UPDATE sq_target SET balance = balance + 1 RETURNING merge_action();
+
+-- RETURNING in CTEs
+CREATE TABLE sq_target_merge_log (tid integer NOT NULL, last_change text);
+INSERT INTO sq_target_merge_log VALUES (1, 'Original value');
+BEGIN;
+WITH m AS (
+ MERGE INTO sq_target t
+ USING sq_source s
+ ON tid = sid
+ WHEN MATCHED AND tid >= 2 THEN
+ UPDATE SET balance = t.balance + delta
+ WHEN NOT MATCHED THEN
+ INSERT (balance, tid) VALUES (balance + delta, sid)
+ WHEN MATCHED AND tid < 2 THEN
+ DELETE
+ RETURNING merge_action() AS action, t.*,
+ CASE merge_action()
+ WHEN 'INSERT' THEN 'Inserted '||t
+ WHEN 'UPDATE' THEN 'Added '||delta||' to balance'
+ WHEN 'DELETE' THEN 'Removed '||t
+ END AS description
+), m2 AS (
+ MERGE INTO sq_target_merge_log l
+ USING m
+ ON l.tid = m.tid
+ WHEN MATCHED THEN
+ UPDATE SET last_change = description
+ WHEN NOT MATCHED THEN
+ INSERT VALUES (m.tid, description)
+ RETURNING action, merge_action() AS log_action, l.*
+)
+SELECT * FROM m2;
+SELECT * FROM sq_target_merge_log ORDER BY tid;
+ROLLBACK;
+
+-- COPY (MERGE ... RETURNING) TO ...
+BEGIN;
+COPY (
+ MERGE INTO sq_target t
+ USING sq_source s
+ ON tid = sid
+ WHEN MATCHED AND tid >= 2 THEN
+ UPDATE SET balance = t.balance + delta
+ WHEN NOT MATCHED THEN
+ INSERT (balance, tid) VALUES (balance + delta, sid)
+ WHEN MATCHED AND tid < 2 THEN
+ DELETE
+ RETURNING merge_action(), t.*
+) TO stdout;
+ROLLBACK;
+
+-- SQL function with MERGE ... RETURNING
+BEGIN;
+CREATE FUNCTION merge_into_sq_target(sid int, balance int, delta int,
+ OUT action text, OUT tid int, OUT new_balance int)
+LANGUAGE sql AS
+$$
+ MERGE INTO sq_target t
+ USING (VALUES ($1, $2, $3)) AS v(sid, balance, delta)
+ ON tid = v.sid
+ WHEN MATCHED AND tid >= 2 THEN
+ UPDATE SET balance = t.balance + v.delta
+ WHEN NOT MATCHED THEN
+ INSERT (balance, tid) VALUES (v.balance + v.delta, v.sid)
+ WHEN MATCHED AND tid < 2 THEN
+ DELETE
+ RETURNING merge_action(), t.*;
+$$;
+SELECT m.*
+FROM (VALUES (1, 0, 0), (3, 0, 20), (4, 100, 10)) AS v(sid, balance, delta),
+LATERAL (SELECT action, tid, new_balance FROM merge_into_sq_target(sid, balance, delta)) m;
+ROLLBACK;
+
+-- SQL SRF with MERGE ... RETURNING
+BEGIN;
+CREATE FUNCTION merge_sq_source_into_sq_target()
+RETURNS TABLE (action text, tid int, balance int)
+LANGUAGE sql AS
+$$
+ MERGE INTO sq_target t
+ USING sq_source s
+ ON tid = sid
+ WHEN MATCHED AND tid >= 2 THEN
+ UPDATE SET balance = t.balance + delta
+ WHEN NOT MATCHED THEN
+ INSERT (balance, tid) VALUES (balance + delta, sid)
+ WHEN MATCHED AND tid < 2 THEN
+ DELETE
+ RETURNING merge_action(), t.*;
+$$;
+SELECT * FROM merge_sq_source_into_sq_target();
+ROLLBACK;
+
+-- PL/pgSQL function with MERGE ... RETURNING ... INTO
+BEGIN;
+CREATE FUNCTION merge_into_sq_target(sid int, balance int, delta int,
+ OUT r_action text, OUT r_tid int, OUT r_balance int)
+LANGUAGE plpgsql AS
+$$
+BEGIN
+ MERGE INTO sq_target t
+ USING (VALUES ($1, $2, $3)) AS v(sid, balance, delta)
+ ON tid = v.sid
+ WHEN MATCHED AND tid >= 2 THEN
+ UPDATE SET balance = t.balance + v.delta
+ WHEN NOT MATCHED THEN
+ INSERT (balance, tid) VALUES (v.balance + v.delta, v.sid)
+ WHEN MATCHED AND tid < 2 THEN
+ DELETE
+ RETURNING merge_action(), t.* INTO r_action, r_tid, r_balance;
+END;
+$$;
+SELECT m.*
+FROM (VALUES (1, 0, 0), (3, 0, 20), (4, 100, 10)) AS v(sid, balance, delta),
+LATERAL (SELECT r_action, r_tid, r_balance FROM merge_into_sq_target(sid, balance, delta)) m;
ROLLBACK;
-- EXPLAIN
@@ -984,7 +1115,7 @@ WHEN MATCHED THEN
SELECT * FROM sq_target WHERE tid = 1;
ROLLBACK;
-DROP TABLE sq_target, sq_source CASCADE;
+DROP TABLE sq_target, sq_target_merge_log, sq_source CASCADE;
CREATE TABLE pa_target (tid integer, balance float, val text)
PARTITION BY LIST (tid);
@@ -1051,6 +1182,17 @@ SELECT merge_func();
SELECT * FROM pa_target ORDER BY tid;
ROLLBACK;
+-- update partition key to partition not initially scanned
+BEGIN;
+MERGE INTO pa_target t
+ USING pa_source s
+ ON t.tid = s.sid AND t.tid = 1
+ WHEN MATCHED THEN
+ UPDATE SET tid = tid + 1, balance = balance + delta, val = val || ' updated by merge'
+ RETURNING merge_action(), t.*;
+SELECT * FROM pa_target ORDER BY tid;
+ROLLBACK;
+
DROP TABLE pa_target CASCADE;
-- The target table is partitioned in the same way, but this time by attaching
@@ -1227,7 +1369,8 @@ MERGE INTO pa_target t
WHEN MATCHED THEN
UPDATE SET balance = balance + delta, val = val || ' updated by merge'
WHEN NOT MATCHED THEN
- INSERT VALUES (slogts::timestamp, sid, delta, 'inserted by merge');
+ INSERT VALUES (slogts::timestamp, sid, delta, 'inserted by merge')
+ RETURNING merge_action(), t.*;
SELECT * FROM pa_target ORDER BY tid;
ROLLBACK;
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index dec7340538c..1d5ed0a647b 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -992,6 +992,27 @@ WHEN MATCHED THEN
UPDATE SET dnotes = dnotes || ' notes added by merge8 '
WHEN NOT MATCHED THEN
INSERT VALUES (13, 44, 1, 'regress_rls_bob', 'new manga');
+SELECT * FROM document WHERE did = 13;
+
+-- but not OK if RETURNING is used
+MERGE INTO document d
+USING (SELECT 14 as sdid) s
+ON did = s.sdid
+WHEN MATCHED THEN
+ UPDATE SET dnotes = dnotes || ' notes added by merge9 '
+WHEN NOT MATCHED THEN
+ INSERT VALUES (14, 44, 1, 'regress_rls_bob', 'new manga')
+RETURNING *;
+
+-- but OK if new row is visible
+MERGE INTO document d
+USING (SELECT 14 as sdid) s
+ON did = s.sdid
+WHEN MATCHED THEN
+ UPDATE SET dnotes = dnotes || ' notes added by merge10 '
+WHEN NOT MATCHED THEN
+ INSERT VALUES (14, 11, 1, 'regress_rls_bob', 'new novel')
+RETURNING *;
RESET SESSION AUTHORIZATION;
-- drop the restrictive SELECT policy so that we can look at the
diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql
index 6924012a10c..27340bacbc5 100644
--- a/src/test/regress/sql/rules.sql
+++ b/src/test/regress/sql/rules.sql
@@ -1294,7 +1294,7 @@ MERGE INTO rule_merge1 t USING (SELECT 1 AS a) s
CREATE TABLE sf_target(id int, data text, filling int[]);
CREATE FUNCTION merge_sf_test()
- RETURNS void
+ RETURNS TABLE(action text, a int, b text, id int, data text, filling int[])
LANGUAGE sql
BEGIN ATOMIC
MERGE INTO sf_target t
@@ -1331,7 +1331,9 @@ WHEN NOT MATCHED
VALUES (s.a, s.b, DEFAULT)
WHEN NOT MATCHED
THEN INSERT (filling[1], id)
- VALUES (s.a, s.a);
+ VALUES (s.a, s.a)
+RETURNING
+ merge_action() AS action, *;
END;
\sf merge_sf_test
diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql
index ae11e46da28..afdf331fed6 100644
--- a/src/test/regress/sql/updatable_views.sql
+++ b/src/test/regress/sql/updatable_views.sql
@@ -175,7 +175,8 @@ MERGE INTO rw_view1 t
(2, 'ROW 2'), (3, 'ROW 3')) AS v(a,b) ON t.a = v.a
WHEN MATCHED AND t.a <= 1 THEN UPDATE SET b = v.b
WHEN MATCHED THEN DELETE
- WHEN NOT MATCHED AND a > 0 THEN INSERT (a) VALUES (v.a);
+ WHEN NOT MATCHED AND a > 0 THEN INSERT (a) VALUES (v.a)
+ RETURNING merge_action(), v.*, t.*;
SELECT * FROM base_tbl ORDER BY a;
EXPLAIN (costs off) UPDATE rw_view1 SET a=6 WHERE a=5;
@@ -246,7 +247,8 @@ MERGE INTO rw_view2 t
USING (VALUES (3, 'R3'), (4, 'R4'), (5, 'R5')) AS v(a,b) ON aaa = v.a
WHEN MATCHED AND aaa = 3 THEN DELETE
WHEN MATCHED THEN UPDATE SET bbb = v.b
- WHEN NOT MATCHED THEN INSERT (aaa) VALUES (v.a);
+ WHEN NOT MATCHED THEN INSERT (aaa) VALUES (v.a)
+ RETURNING merge_action(), v.*, t.*;
SELECT * FROM rw_view2 ORDER BY aaa;
EXPLAIN (costs off) UPDATE rw_view2 SET aaa=5 WHERE aaa=4;
@@ -458,7 +460,8 @@ MERGE INTO rw_view2 t
USING (SELECT x, 'R'||x FROM generate_series(0,3) x) AS s(a,b) ON t.a = s.a
WHEN MATCHED AND t.a <= 1 THEN DELETE
WHEN MATCHED THEN UPDATE SET b = s.b
- WHEN NOT MATCHED AND s.a > 0 THEN INSERT VALUES (s.a, s.b);
+ WHEN NOT MATCHED AND s.a > 0 THEN INSERT VALUES (s.a, s.b)
+ RETURNING merge_action(), s.*, t.*;
SELECT * FROM base_tbl ORDER BY a;
EXPLAIN (costs off) UPDATE rw_view2 SET a=3 WHERE a=2;
diff --git a/src/test/regress/sql/with.sql b/src/test/regress/sql/with.sql
index f8a213e3570..037bc0a511b 100644
--- a/src/test/regress/sql/with.sql
+++ b/src/test/regress/sql/with.sql
@@ -1591,6 +1591,14 @@ WITH t AS (
)
SELECT * FROM t;
+-- RETURNING tries to return its own output
+WITH RECURSIVE t(action, a) AS (
+ MERGE INTO y USING (VALUES (11)) v(a) ON y.a = v.a
+ WHEN NOT MATCHED THEN INSERT VALUES (v.a)
+ RETURNING merge_action(), (SELECT a FROM t)
+)
+SELECT * FROM t;
+
-- data-modifying WITH allowed only at the top level
SELECT * FROM (
WITH t AS (UPDATE y SET a=a+1 RETURNING *)