From 555b5d66893f573875cebb03bfbe98902fd3ed4c Mon Sep 17 00:00:00 2001 From: jian he Date: Tue, 2 Jul 2024 01:45:54 +0800 Subject: [PATCH v3 3/3] use SJE conditionally with delete/update/merge RETURNING ExecProcessReturning can only return the newly changed slot, cannot have the old/previously unchanged slot. update/delete/merge returning can only return newly/changed value. for this reason, the SJE can only apply to RETURNING, where the var's varno is associated the original Query->resultRelation. for example: ``` drop table if exists sj; create table sj (a int unique, b int, c int unique); insert into sj values (3, 1, 21); --this can apply SJE. explain(costs off, verbose) UPDATE sj sq SET a = 4 FROM sj as sz WHERE sq.a = sz.a and sq.a = 3 returning sq.a; --this can not apply SJE. explain(costs off, verbose) UPDATE sj sq SET a = 4 FROM sj as sz WHERE sq.a = sz.a and sq.a = 3 returning sz.a; ``` so based on this, check if root->parse->returningList associated var's varno is not equal to root->parse->resultRelation, then not apply SJE. MERGE: if you specified "merge when matched" and "merge when not matched", it will use left join, so we don't need worry about "WHEN NOT MATCHED BY SOURCE" and "WHEN NOT MATCHED BY TARGET". so overall the merge, we only need take care of merge_update, merge_delete. --- src/backend/optimizer/plan/analyzejoins.c | 74 ++++- src/test/regress/expected/join.out | 334 ++++++++++++++++++++++ src/test/regress/sql/join.sql | 148 ++++++++++ 3 files changed, 554 insertions(+), 2 deletions(-) diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c index eab39ee0..9aece739 100644 --- a/src/backend/optimizer/plan/analyzejoins.c +++ b/src/backend/optimizer/plan/analyzejoins.c @@ -2331,6 +2331,69 @@ self_join_candidates_cmp(const void *a, const void *b) return 0; } +typedef struct +{ + int rt_varno; + bool rt_varno_exists; /* require all var nodes varno == rt_varno */ + int sublevels_up; +} returning_var_context; + +static bool +returning_list_var_walker(Node *node, returning_var_context *context) +{ + if (node == NULL) + return false; + + if (IsA(node, Var)) + { + Var *var = (Var *) node; + + if (context->sublevels_up == 0 && + var->varno != context->rt_varno) + { + context->rt_varno_exists = false; + return false; + } + } + + if (IsA(node, Query)) + { + bool result; + + context->sublevels_up++; + result = query_tree_walker((Query *) node, returning_list_var_walker, + (void *) context, 0); + context->sublevels_up--; + return result; + } + return expression_tree_walker(node, returning_list_var_walker, + (void *) context); +} + +/* + * check if only rt_varno exists in returninglist var + * +*/ +static bool +varno_in_returninglist(Node *node, int rt_varno, int sublevels_up) +{ + returning_var_context context; + context.rt_varno = rt_varno; + context.rt_varno_exists = true; + context.sublevels_up = 0; + + if (node && IsA(node, Query)) + { + Query *qry = (Query *) node; + query_tree_walker(qry, returning_list_var_walker, + (void *) &context, 0); + } + else + returning_list_var_walker(node, &context); + + return context.rt_varno_exists; +} + /* * Find and remove useless self joins. * @@ -2377,9 +2440,16 @@ remove_useless_self_joins(PlannerInfo *root, List *joinlist) return joinlist; /* - * we don't accept RETURNING clause with SJE. + * to make SJE be appliable, for the UPDATE/DELETE/MERGE RETURNING related var(s) + * we can only contain the original resultRelation associated rel. + * if others rel varno is in RETURNING var list, then applying SJE, + * we will seeing old (before changed) value, + * but RETURNING we can only see the newly changed value now. + * */ - if (root->parse && root->parse->returningList != NIL) + if (root->parse && + root->parse->returningList != NIL && + !varno_in_returninglist((Node *) root->parse->returningList, root->parse->resultRelation, 0)) return joinlist; /* diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 9f89b351..c523ee0d 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -6577,6 +6577,157 @@ EXPLAIN (COSTS OFF) WITH t1 AS (SELECT * FROM sj) DELETE FROM sj sq USING sj as -> Seq Scan on sj t1 (6 rows) +---UPDATE/DELETE RETURNING SJE applicable cases. +truncate sj; +insert into sj(a,b,c) select g, g % 10, g + 10 from generate_series(1, 10) g; +analyze sj; +----test query that can use SJE +explain(costs off) +UPDATE sj sq +SET c = sz.a + sq.a + sz.b + sq.b + sz.c + sq.c, a = sz.a + 15, b = sq.a + 15 +FROM sj sz WHERE sq.a = sz.a and sz.a = 2 and sq.a = 2 +returning sq.a, sq.b, (select sq.c); + QUERY PLAN +------------------------- + Update on sj sz + -> Seq Scan on sj sz + Filter: (a = 2) + SubPlan 1 + -> Result +(5 rows) + +explain(costs off) +UPDATE sj sq SET c = sz.a + sq.a + sz.b + sq.b + sz.c + sq.c, a = sz.a + 15, b = sq.a + 15 +FROM (select s1.b, s1.a as a, s2.a as a1, s2.c from sj s1 join sj s2 on s1.a = s2.a) as sz +WHERE sq.a = sz.a and sz.a = 3 +returning sq.*; + QUERY PLAN +------------------------- + Update on sj s2 + -> Seq Scan on sj s2 + Filter: (a = 3) +(3 rows) + +explain(costs off) +UPDATE sj sq SET c = sz.a + sq.a + sz.b + sq.b + sz.c + sq.c, a = sz.a + 15, b = sq.a + 15 +FROM (select s1.b, s1.a as a, s2.a as a1, s2.c from sj s1 join sj s2 on s1.b = s2.b) as sz +WHERE sz.a = 4 and sq.a = 4 +returning sq.*, (select sq.a); + QUERY PLAN +------------------------------------ + Update on sj s1 + -> Nested Loop + Join Filter: (s1.b = s2.b) + -> Seq Scan on sj s1 + Filter: (a = 4) + -> Seq Scan on sj s2 + SubPlan 1 + -> Result +(8 rows) + +explain(costs off) +WITH t1 AS (SELECT *, (select count(*) from sj) FROM sj) DELETE FROM sj sq using t1 as sz where sq.a = sz.a and sz.a = 5 +returning sq.a, sq.a * sq.b, (select sq.a + sq.b); + QUERY PLAN +------------------------- + Delete on sj + -> Seq Scan on sj + Filter: (a = 5) + SubPlan 1 + -> Result +(5 rows) + +explain(costs off) +delete from sj sq using (select s1.b, s1.a as a, s2.a as a1, s2.c from sj s1 join sj s2 on s1.b = s2.b) as sz +WHERE sq.a = sz.a and sq.a = 6 +returning sq.a, sq.b, sq.c, sq.tableoid::regclass as tbl; + QUERY PLAN +------------------------------------ + Delete on sj s1 + -> Nested Loop + Join Filter: (s1.b = s2.b) + -> Seq Scan on sj s1 + Filter: (a = 6) + -> Seq Scan on sj s2 +(6 rows) + +explain(costs off) +delete from sj sq using (select s1.b, s1.a as a, s2.a as a1, s2.c from sj s1 join sj s2 on s1.a = s2.a) as sz +WHERE sq.a = sz.b and sz.a = 7 +returning sq.a, sq.b, sq.c, sq.tableoid::regclass as tbl; + QUERY PLAN +------------------------------------ + Delete on sj sq + -> Nested Loop + Join Filter: (sq.a = s2.b) + -> Seq Scan on sj s2 + Filter: (a = 7) + -> Seq Scan on sj sq +(6 rows) + +----actually execute the query to validate the result +UPDATE sj sq +SET c = sz.a + sq.a + sz.b + sq.b + sz.c + sq.c, a = sz.a + 15, b = sq.a + 15 +FROM sj sz WHERE sq.a = sz.a and sz.a = 2 and sq.a = 2 +returning sq.a, sq.b, (select sq.c); + a | b | c +----+----+---- + 17 | 17 | 32 +(1 row) + +UPDATE sj sq SET c = sz.a + sq.a + sz.b + sq.b + sz.c + sq.c, a = sz.a + 15, b = sq.a + 15 +FROM (select s1.b, s1.a as a, s2.a as a1, s2.c from sj s1 join sj s2 on s1.a = s2.a) as sz +WHERE sq.a = sz.a and sz.a = 3 +returning sq.*; + a | b | c +----+----+---- + 18 | 18 | 38 +(1 row) + +UPDATE sj sq SET c = sz.a + sq.a + sz.b + sq.b + sz.c + sq.c, a = sz.a + 15, b = sq.a + 15 +FROM (select s1.b, s1.a as a, s2.a as a1, s2.c from sj s1 join sj s2 on s1.b = s2.b) as sz +WHERE sz.a = 4 and sq.a = 4 +returning sq.*, (select sq.a); + a | b | c | a +----+----+----+---- + 19 | 19 | 44 | 19 +(1 row) + +WITH t1 AS (SELECT *, (select count(*) from sj) FROM sj) DELETE FROM sj sq using t1 as sz where sq.a = sz.a and sz.a = 5 +returning sq.a, sq.a * sq.b, (select sq.a + sq.b); + a | ?column? | ?column? +---+----------+---------- + 5 | 25 | 10 +(1 row) + +delete from sj sq using (select s1.b, s1.a as a, s2.a as a1, s2.c from sj s1 join sj s2 on s1.b = s2.b) as sz +WHERE sq.a = sz.a and sq.a = 6 +returning sq.a, sq.b, sq.c, sq.tableoid::regclass as tbl; + a | b | c | tbl +---+---+----+----- + 6 | 6 | 16 | sj +(1 row) + +delete from sj sq using (select s1.b, s1.a as a, s2.a as a1, s2.c from sj s1 join sj s2 on s1.a = s2.a) as sz +WHERE sq.a = sz.b and sz.a = 7 +returning sq.a, sq.b, sq.c, sq.tableoid::regclass as tbl; + a | b | c | tbl +---+---+----+----- + 7 | 7 | 17 | sj +(1 row) + +select * from sj order by a; + a | b | c +----+----+---- + 1 | 1 | 11 + 8 | 8 | 18 + 9 | 9 | 19 + 10 | 0 | 20 + 17 | 17 | 32 + 18 | 18 | 38 + 19 | 19 | 44 +(7 rows) + ------------------- MERGE SJE table setup CREATE TABLE sj_target (tid integer primary key, balance integer) WITH (autovacuum_enabled=off); INSERT INTO sj_target VALUES (1, 10),(2, 20), (3, 30), (4, 40),(5, 50), (6, 60); @@ -6702,6 +6853,189 @@ select * from sj_target order by tid; 6 | 66 (5 rows) +-------------------MERGRE RETURNING SJE TEST-------------------------------- +TRUNCATE sj_target; +INSERT INTO sj_target VALUES (1, 10),(2, 20), (3, 30), (4, 40),(5, 50), (6, 60); +--"when matched" and "when not matched" both specified +--because of "when not matched" then can only use left join +--therefore cannot apply SJE +EXPLAIN (COSTS OFF) +MERGE INTO sj_target t USING sj_target AS s ON t.tid = s.tid + WHEN MATCHED AND t.balance = 10 + THEN UPDATE SET balance = t.balance + s.balance, tid = t.tid + s.tid + WHEN NOT MATCHED BY SOURCE AND t.balance = 20 THEN DELETE + RETURNING t.*, (select t.balance), merge_action(); + QUERY PLAN +------------------------------------------------------------ + Merge on sj_target t + -> Nested Loop Left Join + -> Seq Scan on sj_target t + -> Index Scan using sj_target_pkey on sj_target s + Index Cond: (tid = t.tid) + SubPlan 1 + -> Result +(7 rows) + +---"when not matched" using left join, SJE cannot be applied +EXPLAIN (COSTS OFF) +MERGE INTO sj_target t USING sj_target AS s ON t.tid = s.tid +WHEN NOT MATCHED BY SOURCE AND t.balance = 10 THEN + DELETE +returning t.*; + QUERY PLAN +------------------------------------------------------------ + Merge on sj_target t + -> Nested Loop Left Join + -> Seq Scan on sj_target t + -> Index Scan using sj_target_pkey on sj_target s + Index Cond: (tid = t.tid) +(5 rows) + +---"when not matched by target" using left join, SJE cannot be applied +EXPLAIN (COSTS OFF) +MERGE INTO sj_target t USING sj_target s ON t.tid = s.tid + WHEN NOT MATCHED THEN + INSERT VALUES (s.tid, 11) + RETURNING t.*, merge_action(); + QUERY PLAN +------------------------------------------------------------ + Merge on sj_target t + -> Nested Loop Left Join + -> Seq Scan on sj_target s + -> Index Scan using sj_target_pkey on sj_target t + Index Cond: (tid = s.tid) +(5 rows) + +---returning with multiple rel, merge returning cannot be applied. +EXPLAIN (COSTS OFF) +MERGE INTO sj_target t USING sj_target AS s ON t.tid = s.tid + WHEN MATCHED AND t.balance = 50 + THEN UPDATE SET balance = t.balance + s.balance + 50 + RETURNING s.*, t.balance, merge_action(); + QUERY PLAN +------------------------------------------------------------ + Merge on sj_target t + -> Nested Loop + -> Seq Scan on sj_target t + -> Index Scan using sj_target_pkey on sj_target s + Index Cond: (tid = t.tid) +(5 rows) + +--------the following (explain) query can use SJE. +EXPLAIN (COSTS OFF) +MERGE INTO sj_target t USING sj_target AS s ON t.tid = s.tid + WHEN MATCHED AND t.balance = 10 + THEN UPDATE SET balance = t.balance + s.balance + 100, tid = t.tid + s.tid + 10 + RETURNING t.*, (select t.balance), merge_action(); + QUERY PLAN +------------------------------- + Merge on sj_target s + -> Seq Scan on sj_target s + SubPlan 1 + -> Result +(4 rows) + +EXPLAIN (COSTS OFF) +MERGE INTO rw_sj_target t USING (select s0.tid, s1.balance from sj_target s0 join sj_target s1 on s0.balance = s1.balance) s ON t.tid = s.tid + WHEN MATCHED AND s.balance = 20 + THEN UPDATE SET balance = t.balance + s.balance + 100, tid = t.tid + s.tid + 10 + RETURNING t.*, merge_action(); + QUERY PLAN +------------------------------------------------------------- + Merge on sj_target s0 + -> Nested Loop + Join Filter: (s0.balance = s1.balance) + -> Seq Scan on sj_target s1 + -> Materialize + -> Bitmap Heap Scan on sj_target s0 + Recheck Cond: (tid >= 2) + -> Bitmap Index Scan on sj_target_pkey + Index Cond: (tid >= 2) +(9 rows) + +EXPLAIN (COSTS OFF) +MERGE INTO rw_sj_target t USING (select s1.tid, s1.balance from sj_target s0 join rw_sj_target s1 on s0.balance = s1.balance) s ON t.tid = s.tid + WHEN MATCHED AND t.balance = 30 + THEN UPDATE SET balance = t.balance + s.balance + 100, tid = t.tid + s.tid + 10 + RETURNING t.*, merge_action(); + QUERY PLAN +------------------------------------------------------------- + Merge on sj_target + -> Nested Loop + Join Filter: (s0.balance = sj_target.balance) + -> Seq Scan on sj_target s0 + -> Materialize + -> Bitmap Heap Scan on sj_target + Recheck Cond: (tid >= 2) + -> Bitmap Index Scan on sj_target_pkey + Index Cond: (tid >= 2) +(9 rows) + +EXPLAIN (COSTS OFF) +MERGE INTO sj_target t USING (select s0.tid, s1.balance from sj_target s0 join sj_target s1 on s0.balance = s1.balance) s ON t.tid = s.tid + WHEN MATCHED AND t.balance = 40 AND s.balance = 40 + THEN UPDATE SET balance = t.balance + s.balance + 100, tid = t.tid + s.tid + 10 + RETURNING t.*, (SELECT t.tableoid::regclass), merge_action(); + QUERY PLAN +------------------------------------------------ + Merge on sj_target s0 + -> Nested Loop + Join Filter: (s0.balance = s1.balance) + -> Seq Scan on sj_target s0 + -> Materialize + -> Seq Scan on sj_target s1 + SubPlan 1 + -> Result +(8 rows) + +--------and actually running query validate the result. +MERGE INTO sj_target t USING sj_target AS s ON t.tid = s.tid + WHEN MATCHED AND t.balance = 10 + THEN UPDATE SET balance = t.balance + s.balance + 100, tid = t.tid + s.tid + 10 + RETURNING t.*, (select t.balance), merge_action(); + tid | balance | balance | merge_action +-----+---------+---------+-------------- + 12 | 120 | 120 | UPDATE +(1 row) + +MERGE INTO rw_sj_target t USING (select s0.tid, s1.balance from sj_target s0 join sj_target s1 on s0.balance = s1.balance) s ON t.tid = s.tid + WHEN MATCHED AND s.balance = 20 + THEN UPDATE SET balance = t.balance + s.balance + 100, tid = t.tid + s.tid + 10 + RETURNING t.*, merge_action(); + tid | balance | merge_action +-----+---------+-------------- + 14 | 140 | UPDATE +(1 row) + +MERGE INTO rw_sj_target t USING (select s1.tid, s1.balance from sj_target s0 join rw_sj_target s1 on s0.balance = s1.balance) s ON t.tid = s.tid + WHEN MATCHED AND t.balance = 30 + THEN UPDATE SET balance = t.balance + s.balance + 100, tid = t.tid + s.tid + 10 + RETURNING t.*, merge_action(); + tid | balance | merge_action +-----+---------+-------------- + 16 | 160 | UPDATE +(1 row) + +MERGE INTO sj_target t USING (select s0.tid, s1.balance from sj_target s0 join sj_target s1 on s0.balance = s1.balance) s ON t.tid = s.tid + WHEN MATCHED AND t.balance = 40 AND s.balance = 40 + THEN UPDATE SET balance = t.balance + s.balance + 100, tid = t.tid + s.tid + 10 + RETURNING t.*, (SELECT t.tableoid::regclass), merge_action(); + tid | balance | tableoid | merge_action +-----+---------+-----------+-------------- + 18 | 180 | sj_target | UPDATE +(1 row) + +select * from sj_target; + tid | balance +-----+--------- + 5 | 50 + 6 | 60 + 12 | 120 + 14 | 140 + 16 | 160 + 18 | 180 +(6 rows) + DROP VIEW no_rw_sj_target; DROP VIEW rw_sj_target; DROP TABLE sj_target; diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index 4afbaf79..0d5cb057 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -2484,6 +2484,72 @@ EXPLAIN (COSTS OFF) WITH t1 AS (SELECT * FROM sj) DELETE FROM sj sq USING sj as EXPLAIN (COSTS OFF) WITH t1 AS (SELECT * FROM sj) DELETE FROM sj sq USING sj as t1 WHERE t1.a = sq.c; +---UPDATE/DELETE RETURNING SJE applicable cases. +truncate sj; +insert into sj(a,b,c) select g, g % 10, g + 10 from generate_series(1, 10) g; +analyze sj; + +----test query that can use SJE +explain(costs off) +UPDATE sj sq +SET c = sz.a + sq.a + sz.b + sq.b + sz.c + sq.c, a = sz.a + 15, b = sq.a + 15 +FROM sj sz WHERE sq.a = sz.a and sz.a = 2 and sq.a = 2 +returning sq.a, sq.b, (select sq.c); + +explain(costs off) +UPDATE sj sq SET c = sz.a + sq.a + sz.b + sq.b + sz.c + sq.c, a = sz.a + 15, b = sq.a + 15 +FROM (select s1.b, s1.a as a, s2.a as a1, s2.c from sj s1 join sj s2 on s1.a = s2.a) as sz +WHERE sq.a = sz.a and sz.a = 3 +returning sq.*; + +explain(costs off) +UPDATE sj sq SET c = sz.a + sq.a + sz.b + sq.b + sz.c + sq.c, a = sz.a + 15, b = sq.a + 15 +FROM (select s1.b, s1.a as a, s2.a as a1, s2.c from sj s1 join sj s2 on s1.b = s2.b) as sz +WHERE sz.a = 4 and sq.a = 4 +returning sq.*, (select sq.a); + +explain(costs off) +WITH t1 AS (SELECT *, (select count(*) from sj) FROM sj) DELETE FROM sj sq using t1 as sz where sq.a = sz.a and sz.a = 5 +returning sq.a, sq.a * sq.b, (select sq.a + sq.b); + +explain(costs off) +delete from sj sq using (select s1.b, s1.a as a, s2.a as a1, s2.c from sj s1 join sj s2 on s1.b = s2.b) as sz +WHERE sq.a = sz.a and sq.a = 6 +returning sq.a, sq.b, sq.c, sq.tableoid::regclass as tbl; + +explain(costs off) +delete from sj sq using (select s1.b, s1.a as a, s2.a as a1, s2.c from sj s1 join sj s2 on s1.a = s2.a) as sz +WHERE sq.a = sz.b and sz.a = 7 +returning sq.a, sq.b, sq.c, sq.tableoid::regclass as tbl; + +----actually execute the query to validate the result +UPDATE sj sq +SET c = sz.a + sq.a + sz.b + sq.b + sz.c + sq.c, a = sz.a + 15, b = sq.a + 15 +FROM sj sz WHERE sq.a = sz.a and sz.a = 2 and sq.a = 2 +returning sq.a, sq.b, (select sq.c); + +UPDATE sj sq SET c = sz.a + sq.a + sz.b + sq.b + sz.c + sq.c, a = sz.a + 15, b = sq.a + 15 +FROM (select s1.b, s1.a as a, s2.a as a1, s2.c from sj s1 join sj s2 on s1.a = s2.a) as sz +WHERE sq.a = sz.a and sz.a = 3 +returning sq.*; + +UPDATE sj sq SET c = sz.a + sq.a + sz.b + sq.b + sz.c + sq.c, a = sz.a + 15, b = sq.a + 15 +FROM (select s1.b, s1.a as a, s2.a as a1, s2.c from sj s1 join sj s2 on s1.b = s2.b) as sz +WHERE sz.a = 4 and sq.a = 4 +returning sq.*, (select sq.a); + +WITH t1 AS (SELECT *, (select count(*) from sj) FROM sj) DELETE FROM sj sq using t1 as sz where sq.a = sz.a and sz.a = 5 +returning sq.a, sq.a * sq.b, (select sq.a + sq.b); + +delete from sj sq using (select s1.b, s1.a as a, s2.a as a1, s2.c from sj s1 join sj s2 on s1.b = s2.b) as sz +WHERE sq.a = sz.a and sq.a = 6 +returning sq.a, sq.b, sq.c, sq.tableoid::regclass as tbl; + +delete from sj sq using (select s1.b, s1.a as a, s2.a as a1, s2.c from sj s1 join sj s2 on s1.a = s2.a) as sz +WHERE sq.a = sz.b and sz.a = 7 +returning sq.a, sq.b, sq.c, sq.tableoid::regclass as tbl; + +select * from sj order by a; ------------------- MERGE SJE table setup CREATE TABLE sj_target (tid integer primary key, balance integer) WITH (autovacuum_enabled=off); INSERT INTO sj_target VALUES (1, 10),(2, 20), (3, 30), (4, 40),(5, 50), (6, 60); @@ -2541,6 +2607,88 @@ MERGE INTO rw_sj_target t USING sj_target AS s ON t.tid = s.tid WHEN MATCHED AND t.balance = 10 THEN update set balance = t.balance + 1; select * from sj_target order by tid; +-------------------MERGRE RETURNING SJE TEST-------------------------------- +TRUNCATE sj_target; +INSERT INTO sj_target VALUES (1, 10),(2, 20), (3, 30), (4, 40),(5, 50), (6, 60); + +--"when matched" and "when not matched" both specified +--because of "when not matched" then can only use left join +--therefore cannot apply SJE +EXPLAIN (COSTS OFF) +MERGE INTO sj_target t USING sj_target AS s ON t.tid = s.tid + WHEN MATCHED AND t.balance = 10 + THEN UPDATE SET balance = t.balance + s.balance, tid = t.tid + s.tid + WHEN NOT MATCHED BY SOURCE AND t.balance = 20 THEN DELETE + RETURNING t.*, (select t.balance), merge_action(); + +---"when not matched" using left join, SJE cannot be applied +EXPLAIN (COSTS OFF) +MERGE INTO sj_target t USING sj_target AS s ON t.tid = s.tid +WHEN NOT MATCHED BY SOURCE AND t.balance = 10 THEN + DELETE +returning t.*; + +---"when not matched by target" using left join, SJE cannot be applied +EXPLAIN (COSTS OFF) +MERGE INTO sj_target t USING sj_target s ON t.tid = s.tid + WHEN NOT MATCHED THEN + INSERT VALUES (s.tid, 11) + RETURNING t.*, merge_action(); + +---returning with multiple rel, merge returning cannot be applied. +EXPLAIN (COSTS OFF) +MERGE INTO sj_target t USING sj_target AS s ON t.tid = s.tid + WHEN MATCHED AND t.balance = 50 + THEN UPDATE SET balance = t.balance + s.balance + 50 + RETURNING s.*, t.balance, merge_action(); + +--------the following (explain) query can use SJE. +EXPLAIN (COSTS OFF) +MERGE INTO sj_target t USING sj_target AS s ON t.tid = s.tid + WHEN MATCHED AND t.balance = 10 + THEN UPDATE SET balance = t.balance + s.balance + 100, tid = t.tid + s.tid + 10 + RETURNING t.*, (select t.balance), merge_action(); + +EXPLAIN (COSTS OFF) +MERGE INTO rw_sj_target t USING (select s0.tid, s1.balance from sj_target s0 join sj_target s1 on s0.balance = s1.balance) s ON t.tid = s.tid + WHEN MATCHED AND s.balance = 20 + THEN UPDATE SET balance = t.balance + s.balance + 100, tid = t.tid + s.tid + 10 + RETURNING t.*, merge_action(); + +EXPLAIN (COSTS OFF) +MERGE INTO rw_sj_target t USING (select s1.tid, s1.balance from sj_target s0 join rw_sj_target s1 on s0.balance = s1.balance) s ON t.tid = s.tid + WHEN MATCHED AND t.balance = 30 + THEN UPDATE SET balance = t.balance + s.balance + 100, tid = t.tid + s.tid + 10 + RETURNING t.*, merge_action(); + +EXPLAIN (COSTS OFF) +MERGE INTO sj_target t USING (select s0.tid, s1.balance from sj_target s0 join sj_target s1 on s0.balance = s1.balance) s ON t.tid = s.tid + WHEN MATCHED AND t.balance = 40 AND s.balance = 40 + THEN UPDATE SET balance = t.balance + s.balance + 100, tid = t.tid + s.tid + 10 + RETURNING t.*, (SELECT t.tableoid::regclass), merge_action(); +--------and actually running query validate the result. +MERGE INTO sj_target t USING sj_target AS s ON t.tid = s.tid + WHEN MATCHED AND t.balance = 10 + THEN UPDATE SET balance = t.balance + s.balance + 100, tid = t.tid + s.tid + 10 + RETURNING t.*, (select t.balance), merge_action(); + +MERGE INTO rw_sj_target t USING (select s0.tid, s1.balance from sj_target s0 join sj_target s1 on s0.balance = s1.balance) s ON t.tid = s.tid + WHEN MATCHED AND s.balance = 20 + THEN UPDATE SET balance = t.balance + s.balance + 100, tid = t.tid + s.tid + 10 + RETURNING t.*, merge_action(); + +MERGE INTO rw_sj_target t USING (select s1.tid, s1.balance from sj_target s0 join rw_sj_target s1 on s0.balance = s1.balance) s ON t.tid = s.tid + WHEN MATCHED AND t.balance = 30 + THEN UPDATE SET balance = t.balance + s.balance + 100, tid = t.tid + s.tid + 10 + RETURNING t.*, merge_action(); + +MERGE INTO sj_target t USING (select s0.tid, s1.balance from sj_target s0 join sj_target s1 on s0.balance = s1.balance) s ON t.tid = s.tid + WHEN MATCHED AND t.balance = 40 AND s.balance = 40 + THEN UPDATE SET balance = t.balance + s.balance + 100, tid = t.tid + s.tid + 10 + RETURNING t.*, (SELECT t.tableoid::regclass), merge_action(); + +select * from sj_target; + DROP VIEW no_rw_sj_target; DROP VIEW rw_sj_target; DROP TABLE sj_target; -- 2.34.1