diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2021-03-31 11:52:34 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2021-03-31 11:52:37 -0400 |
commit | 86dc90056dfdbd9d1b891718d2e5614e3e432f35 (patch) | |
tree | 8d281c58f67e90961688fd311673fbdb2f8c35c7 /src/test/regress | |
parent | 055fee7eb4dcc78e58672aef146334275e1cc40d (diff) | |
download | postgresql-86dc90056dfdbd9d1b891718d2e5614e3e432f35.tar.gz |
Rework planning and execution of UPDATE and DELETE.
This patch makes two closely related sets of changes:
1. For UPDATE, the subplan of the ModifyTable node now only delivers
the new values of the changed columns (i.e., the expressions computed
in the query's SET clause) plus row identity information such as CTID.
ModifyTable must re-fetch the original tuple to merge in the old
values of any unchanged columns. The core advantage of this is that
the changed columns are uniform across all tables of an inherited or
partitioned target relation, whereas the other columns might not be.
A secondary advantage, when the UPDATE involves joins, is that less
data needs to pass through the plan tree. The disadvantage of course
is an extra fetch of each tuple to be updated. However, that seems to
be very nearly free in context; even worst-case tests don't show it to
add more than a couple percent to the total query cost. At some point
it might be interesting to combine the re-fetch with the tuple access
that ModifyTable must do anyway to mark the old tuple dead; but that
would require a good deal of refactoring and it seems it wouldn't buy
all that much, so this patch doesn't attempt it.
2. For inherited UPDATE/DELETE, instead of generating a separate
subplan for each target relation, we now generate a single subplan
that is just exactly like a SELECT's plan, then stick ModifyTable
on top of that. To let ModifyTable know which target relation a
given incoming row refers to, a tableoid junk column is added to
the row identity information. This gets rid of the horrid hack
that was inheritance_planner(), eliminating O(N^2) planning cost
and memory consumption in cases where there were many unprunable
target relations.
Point 2 of course requires point 1, so that there is a uniform
definition of the non-junk columns to be returned by the subplan.
We can't insist on uniform definition of the row identity junk
columns however, if we want to keep the ability to have both
plain and foreign tables in a partitioning hierarchy. Since
it wouldn't scale very far to have every child table have its
own row identity column, this patch includes provisions to merge
similar row identity columns into one column of the subplan result.
In particular, we can merge the whole-row Vars typically used as
row identity by FDWs into one column by pretending they are type
RECORD. (It's still okay for the actual composite Datums to be
labeled with the table's rowtype OID, though.)
There is more that can be done to file down residual inefficiencies
in this patch, but it seems to be committable now.
FDW authors should note several API changes:
* The argument list for AddForeignUpdateTargets() has changed, and so
has the method it must use for adding junk columns to the query. Call
add_row_identity_var() instead of manipulating the parse tree directly.
You might want to reconsider exactly what you're adding, too.
* PlanDirectModify() must now work a little harder to find the
ForeignScan plan node; if the foreign table is part of a partitioning
hierarchy then the ForeignScan might not be the direct child of
ModifyTable. See postgres_fdw for sample code.
* To check whether a relation is a target relation, it's no
longer sufficient to compare its relid to root->parse->resultRelation.
Instead, check it against all_result_relids or leaf_result_relids,
as appropriate.
Amit Langote and Tom Lane
Discussion: https://postgr.es/m/CA+HiwqHpHdqdDn48yCEhynnniahH78rwcrv1rEX65-fsZGBOLQ@mail.gmail.com
Diffstat (limited to 'src/test/regress')
-rw-r--r-- | src/test/regress/expected/inherit.out | 22 | ||||
-rw-r--r-- | src/test/regress/expected/insert_conflict.out | 2 | ||||
-rw-r--r-- | src/test/regress/expected/partition_join.out | 42 | ||||
-rw-r--r-- | src/test/regress/expected/partition_prune.out | 199 | ||||
-rw-r--r-- | src/test/regress/expected/rowsecurity.out | 135 | ||||
-rw-r--r-- | src/test/regress/expected/updatable_views.out | 149 | ||||
-rw-r--r-- | src/test/regress/expected/update.out | 43 | ||||
-rw-r--r-- | src/test/regress/expected/with.out | 56 |
8 files changed, 283 insertions, 365 deletions
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index 2b68aef654..1c703c351f 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -545,27 +545,25 @@ create table some_tab_child () inherits (some_tab); insert into some_tab_child values(1,2); explain (verbose, costs off) update some_tab set a = a + 1 where false; - QUERY PLAN ----------------------------------- + QUERY PLAN +-------------------------------------------------------- Update on public.some_tab - Update on public.some_tab -> Result - Output: (a + 1), b, ctid + Output: (some_tab.a + 1), NULL::oid, NULL::tid One-Time Filter: false -(5 rows) +(4 rows) update some_tab set a = a + 1 where false; explain (verbose, costs off) update some_tab set a = a + 1 where false returning b, a; - QUERY PLAN ----------------------------------- + QUERY PLAN +-------------------------------------------------------- Update on public.some_tab - Output: b, a - Update on public.some_tab + Output: some_tab.b, some_tab.a -> Result - Output: (a + 1), b, ctid + Output: (some_tab.a + 1), NULL::oid, NULL::tid One-Time Filter: false -(6 rows) +(5 rows) update some_tab set a = a + 1 where false returning b, a; b | a @@ -670,7 +668,7 @@ explain update parted_tab set a = 2 where false; QUERY PLAN -------------------------------------------------------- Update on parted_tab (cost=0.00..0.00 rows=0 width=0) - -> Result (cost=0.00..0.00 rows=0 width=0) + -> Result (cost=0.00..0.00 rows=0 width=10) One-Time Filter: false (3 rows) diff --git a/src/test/regress/expected/insert_conflict.out b/src/test/regress/expected/insert_conflict.out index 499245068a..a54a51e5c7 100644 --- a/src/test/regress/expected/insert_conflict.out +++ b/src/test/regress/expected/insert_conflict.out @@ -213,7 +213,7 @@ explain (costs off, format json) insert into insertconflicttest values (0, 'Bilb "Plans": [ + { + "Node Type": "Result", + - "Parent Relationship": "Member", + + "Parent Relationship": "Outer", + "Parallel Aware": false, + "Async Capable": false + } + diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out index 0057f41caa..27f7525b3e 100644 --- a/src/test/regress/expected/partition_join.out +++ b/src/test/regress/expected/partition_join.out @@ -1926,37 +1926,27 @@ WHERE EXISTS ( FROM int4_tbl, LATERAL (SELECT int4_tbl.f1 FROM int8_tbl LIMIT 2) ss WHERE prt1_l.c IS NULL); - QUERY PLAN ---------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------- Delete on prt1_l Delete on prt1_l_p1 prt1_l_1 Delete on prt1_l_p3_p1 prt1_l_2 Delete on prt1_l_p3_p2 prt1_l_3 -> Nested Loop Semi Join - -> Seq Scan on prt1_l_p1 prt1_l_1 - Filter: (c IS NULL) - -> Nested Loop - -> Seq Scan on int4_tbl - -> Subquery Scan on ss - -> Limit - -> Seq Scan on int8_tbl - -> Nested Loop Semi Join - -> Seq Scan on prt1_l_p3_p1 prt1_l_2 - Filter: (c IS NULL) - -> Nested Loop - -> Seq Scan on int4_tbl - -> Subquery Scan on ss_1 - -> Limit - -> Seq Scan on int8_tbl int8_tbl_1 - -> Nested Loop Semi Join - -> Seq Scan on prt1_l_p3_p2 prt1_l_3 - Filter: (c IS NULL) - -> Nested Loop - -> Seq Scan on int4_tbl - -> Subquery Scan on ss_2 - -> Limit - -> Seq Scan on int8_tbl int8_tbl_2 -(28 rows) + -> Append + -> Seq Scan on prt1_l_p1 prt1_l_1 + Filter: (c IS NULL) + -> Seq Scan on prt1_l_p3_p1 prt1_l_2 + Filter: (c IS NULL) + -> Seq Scan on prt1_l_p3_p2 prt1_l_3 + Filter: (c IS NULL) + -> Materialize + -> Nested Loop + -> Seq Scan on int4_tbl + -> Subquery Scan on ss + -> Limit + -> Seq Scan on int8_tbl +(18 rows) -- -- negative testcases diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index bde29e38a9..c4e827caec 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -2463,74 +2463,43 @@ deallocate ab_q6; insert into ab values (1,2); explain (analyze, costs off, summary off, timing off) update ab_a1 set b = 3 from ab where ab.a = 1 and ab.a = ab_a1.a; - QUERY PLAN -------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------- Update on ab_a1 (actual rows=0 loops=1) Update on ab_a1_b1 ab_a1_1 Update on ab_a1_b2 ab_a1_2 Update on ab_a1_b3 ab_a1_3 - -> Nested Loop (actual rows=0 loops=1) - -> Append (actual rows=1 loops=1) - -> Bitmap Heap Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1) - Recheck Cond: (a = 1) - -> Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1) - Index Cond: (a = 1) - -> Bitmap Heap Scan on ab_a1_b2 ab_2 (actual rows=1 loops=1) - Recheck Cond: (a = 1) - Heap Blocks: exact=1 - -> Bitmap Index Scan on ab_a1_b2_a_idx (actual rows=1 loops=1) - Index Cond: (a = 1) - -> Bitmap Heap Scan on ab_a1_b3 ab_3 (actual rows=0 loops=1) - Recheck Cond: (a = 1) - -> Bitmap Index Scan on ab_a1_b3_a_idx (actual rows=0 loops=1) - Index Cond: (a = 1) - -> Materialize (actual rows=0 loops=1) - -> Bitmap Heap Scan on ab_a1_b1 ab_a1_1 (actual rows=0 loops=1) - Recheck Cond: (a = 1) - -> Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1) - Index Cond: (a = 1) -> Nested Loop (actual rows=1 loops=1) -> Append (actual rows=1 loops=1) - -> Bitmap Heap Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1) + -> Bitmap Heap Scan on ab_a1_b1 ab_a1_1 (actual rows=0 loops=1) Recheck Cond: (a = 1) -> Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1) Index Cond: (a = 1) - -> Bitmap Heap Scan on ab_a1_b2 ab_2 (actual rows=1 loops=1) - Recheck Cond: (a = 1) - Heap Blocks: exact=1 - -> Bitmap Index Scan on ab_a1_b2_a_idx (actual rows=1 loops=1) - Index Cond: (a = 1) - -> Bitmap Heap Scan on ab_a1_b3 ab_3 (actual rows=0 loops=1) - Recheck Cond: (a = 1) - -> Bitmap Index Scan on ab_a1_b3_a_idx (actual rows=1 loops=1) - Index Cond: (a = 1) - -> Materialize (actual rows=1 loops=1) -> Bitmap Heap Scan on ab_a1_b2 ab_a1_2 (actual rows=1 loops=1) Recheck Cond: (a = 1) Heap Blocks: exact=1 -> Bitmap Index Scan on ab_a1_b2_a_idx (actual rows=1 loops=1) Index Cond: (a = 1) - -> Nested Loop (actual rows=0 loops=1) - -> Append (actual rows=1 loops=1) - -> Bitmap Heap Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1) - Recheck Cond: (a = 1) - -> Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1) - Index Cond: (a = 1) - -> Bitmap Heap Scan on ab_a1_b2 ab_2 (actual rows=1 loops=1) - Recheck Cond: (a = 1) - Heap Blocks: exact=1 - -> Bitmap Index Scan on ab_a1_b2_a_idx (actual rows=1 loops=1) - Index Cond: (a = 1) - -> Bitmap Heap Scan on ab_a1_b3 ab_3 (actual rows=0 loops=1) - Recheck Cond: (a = 1) - -> Bitmap Index Scan on ab_a1_b3_a_idx (actual rows=1 loops=1) - Index Cond: (a = 1) - -> Materialize (actual rows=0 loops=1) -> Bitmap Heap Scan on ab_a1_b3 ab_a1_3 (actual rows=0 loops=1) Recheck Cond: (a = 1) -> Bitmap Index Scan on ab_a1_b3_a_idx (actual rows=1 loops=1) Index Cond: (a = 1) -(65 rows) + -> Materialize (actual rows=1 loops=1) + -> Append (actual rows=1 loops=1) + -> Bitmap Heap Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1) + Recheck Cond: (a = 1) + -> Bitmap Index Scan on ab_a1_b1_a_idx (actual rows=0 loops=1) + Index Cond: (a = 1) + -> Bitmap Heap Scan on ab_a1_b2 ab_2 (actual rows=1 loops=1) + Recheck Cond: (a = 1) + Heap Blocks: exact=1 + -> Bitmap Index Scan on ab_a1_b2_a_idx (actual rows=1 loops=1) + Index Cond: (a = 1) + -> Bitmap Heap Scan on ab_a1_b3 ab_3 (actual rows=0 loops=1) + Recheck Cond: (a = 1) + -> Bitmap Index Scan on ab_a1_b3_a_idx (actual rows=1 loops=1) + Index Cond: (a = 1) +(34 rows) table ab; a | b @@ -2551,29 +2520,12 @@ update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 1); Update on ab_a1_b3 ab_a1_3 InitPlan 1 (returns $0) -> Result (actual rows=1 loops=1) - -> Nested Loop (actual rows=1 loops=1) - -> Seq Scan on ab_a1_b1 ab_a1_1 (actual rows=1 loops=1) - -> Materialize (actual rows=1 loops=1) - -> Append (actual rows=1 loops=1) - -> Seq Scan on ab_a2_b1 ab_a2_1 (actual rows=1 loops=1) - Filter: (b = $0) - -> Seq Scan on ab_a2_b2 ab_a2_2 (never executed) - Filter: (b = $0) - -> Seq Scan on ab_a2_b3 ab_a2_3 (never executed) - Filter: (b = $0) - -> Nested Loop (actual rows=1 loops=1) - -> Seq Scan on ab_a1_b2 ab_a1_2 (actual rows=1 loops=1) - -> Materialize (actual rows=1 loops=1) - -> Append (actual rows=1 loops=1) - -> Seq Scan on ab_a2_b1 ab_a2_1 (actual rows=1 loops=1) - Filter: (b = $0) - -> Seq Scan on ab_a2_b2 ab_a2_2 (never executed) - Filter: (b = $0) - -> Seq Scan on ab_a2_b3 ab_a2_3 (never executed) - Filter: (b = $0) - -> Nested Loop (actual rows=1 loops=1) - -> Seq Scan on ab_a1_b3 ab_a1_3 (actual rows=1 loops=1) - -> Materialize (actual rows=1 loops=1) + -> Nested Loop (actual rows=3 loops=1) + -> Append (actual rows=3 loops=1) + -> Seq Scan on ab_a1_b1 ab_a1_1 (actual rows=1 loops=1) + -> Seq Scan on ab_a1_b2 ab_a1_2 (actual rows=1 loops=1) + -> Seq Scan on ab_a1_b3 ab_a1_3 (actual rows=1 loops=1) + -> Materialize (actual rows=1 loops=3) -> Append (actual rows=1 loops=1) -> Seq Scan on ab_a2_b1 ab_a2_1 (actual rows=1 loops=1) Filter: (b = $0) @@ -2581,7 +2533,7 @@ update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 1); Filter: (b = $0) -> Seq Scan on ab_a2_b3 ab_a2_3 (never executed) Filter: (b = $0) -(36 rows) +(19 rows) select tableoid::regclass, * from ab; tableoid | a | b @@ -3420,28 +3372,30 @@ explain (costs off) select * from pp_lp where a = 1; (5 rows) explain (costs off) update pp_lp set value = 10 where a = 1; - QUERY PLAN ----------------------------------- + QUERY PLAN +---------------------------------------- Update on pp_lp Update on pp_lp1 pp_lp_1 Update on pp_lp2 pp_lp_2 - -> Seq Scan on pp_lp1 pp_lp_1 - Filter: (a = 1) - -> Seq Scan on pp_lp2 pp_lp_2 - Filter: (a = 1) -(7 rows) + -> Append + -> Seq Scan on pp_lp1 pp_lp_1 + Filter: (a = 1) + -> Seq Scan on pp_lp2 pp_lp_2 + Filter: (a = 1) +(8 rows) explain (costs off) delete from pp_lp where a = 1; - QUERY PLAN ----------------------------------- + QUERY PLAN +---------------------------------------- Delete on pp_lp Delete on pp_lp1 pp_lp_1 Delete on pp_lp2 pp_lp_2 - -> Seq Scan on pp_lp1 pp_lp_1 - Filter: (a = 1) - -> Seq Scan on pp_lp2 pp_lp_2 - Filter: (a = 1) -(7 rows) + -> Append + -> Seq Scan on pp_lp1 pp_lp_1 + Filter: (a = 1) + -> Seq Scan on pp_lp2 pp_lp_2 + Filter: (a = 1) +(8 rows) set constraint_exclusion = 'off'; -- this should not affect the result. explain (costs off) select * from pp_lp where a = 1; @@ -3455,28 +3409,30 @@ explain (costs off) select * from pp_lp where a = 1; (5 rows) explain (costs off) update pp_lp set value = 10 where a = 1; - QUERY PLAN ----------------------------------- + QUERY PLAN +---------------------------------------- Update on pp_lp Update on pp_lp1 pp_lp_1 Update on pp_lp2 pp_lp_2 - -> Seq Scan on pp_lp1 pp_lp_1 - Filter: (a = 1) - -> Seq Scan on pp_lp2 pp_lp_2 - Filter: (a = 1) -(7 rows) + -> Append + -> Seq Scan on pp_lp1 pp_lp_1 + Filter: (a = 1) + -> Seq Scan on pp_lp2 pp_lp_2 + Filter: (a = 1) +(8 rows) explain (costs off) delete from pp_lp where a = 1; - QUERY PLAN ----------------------------------- + QUERY PLAN +---------------------------------------- Delete on pp_lp Delete on pp_lp1 pp_lp_1 Delete on pp_lp2 pp_lp_2 - -> Seq Scan on pp_lp1 pp_lp_1 - Filter: (a = 1) - -> Seq Scan on pp_lp2 pp_lp_2 - Filter: (a = 1) -(7 rows) + -> Append + -> Seq Scan on pp_lp1 pp_lp_1 + Filter: (a = 1) + -> Seq Scan on pp_lp2 pp_lp_2 + Filter: (a = 1) +(8 rows) drop table pp_lp; -- Ensure enable_partition_prune does not affect non-partitioned tables. @@ -3500,28 +3456,31 @@ explain (costs off) select * from inh_lp where a = 1; (5 rows) explain (costs off) update inh_lp set value = 10 where a = 1; - QUERY PLAN ------------------------------------- + QUERY PLAN +------------------------------------------------ Update on inh_lp - Update on inh_lp - Update on inh_lp1 inh_lp_1 - -> Seq Scan on inh_lp - Filter: (a = 1) - -> Seq Scan on inh_lp1 inh_lp_1 - Filter: (a = 1) -(7 rows) + Update on inh_lp inh_lp_1 + Update on inh_lp1 inh_lp_2 + -> Result + -> Append + -> Seq Scan on inh_lp inh_lp_1 + Filter: (a = 1) + -> Seq Scan on inh_lp1 inh_lp_2 + Filter: (a = 1) +(9 rows) explain (costs off) delete from inh_lp where a = 1; - QUERY PLAN ------------------------------------- + QUERY PLAN +------------------------------------------ Delete on inh_lp - Delete on inh_lp - Delete on inh_lp1 inh_lp_1 - -> Seq Scan on inh_lp - Filter: (a = 1) - -> Seq Scan on inh_lp1 inh_lp_1 - Filter: (a = 1) -(7 rows) + Delete on inh_lp inh_lp_1 + Delete on inh_lp1 inh_lp_2 + -> Append + -> Seq Scan on inh_lp inh_lp_1 + Filter: (a = 1) + -> Seq Scan on inh_lp1 inh_lp_2 + Filter: (a = 1) +(8 rows) -- Ensure we don't exclude normal relations when we only expect to exclude -- inheritance children diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index 9506aaef82..b02a682471 100644 --- a/src/test/regress/expected/rowsecurity.out +++ b/src/test/regress/expected/rowsecurity.out @@ -1632,19 +1632,21 @@ EXPLAIN (COSTS OFF) EXECUTE p2(2); -- SET SESSION AUTHORIZATION regress_rls_bob; EXPLAIN (COSTS OFF) UPDATE t1 SET b = b || b WHERE f_leak(b); - QUERY PLAN ------------------------------------------------ + QUERY PLAN +----------------------------------------------------------- Update on t1 - Update on t1 - Update on t2 t1_1 - Update on t3 t1_2 - -> Seq Scan on t1 - Filter: (((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 t1_1 - Filter: (((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t3 t1_2 - Filter: (((a % 2) = 0) AND f_leak(b)) -(10 rows) + Update on t1 t1_1 + Update on t2 t1_2 + Update on t3 t1_3 + -> Result + -> Append + -> Seq Scan on t1 t1_1 + Filter: (((a % 2) = 0) AND f_leak(b)) + -> Seq Scan on t2 t1_2 + Filter: (((a % 2) = 0) AND f_leak(b)) + -> Seq Scan on t3 t1_3 + Filter: (((a % 2) = 0) AND f_leak(b)) +(12 rows) UPDATE t1 SET b = b || b WHERE f_leak(b); NOTICE: f_leak => bbb @@ -1722,31 +1724,27 @@ NOTICE: f_leak => cde NOTICE: f_leak => yyyyyy EXPLAIN (COSTS OFF) UPDATE t1 SET b=t1.b FROM t2 WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b); - QUERY PLAN ------------------------------------------------------------------ + QUERY PLAN +----------------------------------------------------------------------- Update on t1 - Update on t1 - Update on t2 t1_1 - Update on t3 t1_2 - -> Nested Loop - -> Seq Scan on t1 - Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 - Filter: ((a = 3) AND ((a % 2) = 1) AND f_leak(b)) - -> Nested Loop - -> Seq Scan on t2 t1_1 - Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 - Filter: ((a = 3) AND ((a % 2) = 1) AND f_leak(b)) + Update on t1 t1_1 + Update on t2 t1_2 + Update on t3 t1_3 -> Nested Loop - -> Seq Scan on t3 t1_2 - Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b)) -> Seq Scan on t2 Filter: ((a = 3) AND ((a % 2) = 1) AND f_leak(b)) -(19 rows) + -> Append + -> Seq Scan on t1 t1_1 + Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b)) + -> Seq Scan on t2 t1_2 + Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b)) + -> Seq Scan on t3 t1_3 + Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b)) +(14 rows) UPDATE t1 SET b=t1.b FROM t2 WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b); +NOTICE: f_leak => cde EXPLAIN (COSTS OFF) UPDATE t2 SET b=t2.b FROM t1 WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b); QUERY PLAN @@ -1795,46 +1793,30 @@ NOTICE: f_leak => cde EXPLAIN (COSTS OFF) UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2 WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2; - QUERY PLAN ------------------------------------------------------------------------ + QUERY PLAN +----------------------------------------------------------------------------- Update on t1 t1_1 - Update on t1 t1_1 - Update on t2 t1_1_1 - Update on t3 t1_1_2 + Update on t1 t1_1_1 + Update on t2 t1_1_2 + Update on t3 t1_1_3 -> Nested Loop Join Filter: (t1_1.b = t1_2.b) - -> Seq Scan on t1 t1_1 - Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Append - -> Seq Scan on t1 t1_2_1 - Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 t1_2_2 - Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t3 t1_2_3 - Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Nested Loop - Join Filter: (t1_1_1.b = t1_2.b) - -> Seq Scan on t2 t1_1_1 - Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) -> Append - -> Seq Scan on t1 t1_2_1 - Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 t1_2_2 - Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t3 t1_2_3 + -> Seq Scan on t1 t1_1_1 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Nested Loop - Join Filter: (t1_1_2.b = t1_2.b) - -> Seq Scan on t3 t1_1_2 - Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Append - -> Seq Scan on t1 t1_2_1 + -> Seq Scan on t2 t1_1_2 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 t1_2_2 + -> Seq Scan on t3 t1_1_3 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t3 t1_2_3 - Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) -(37 rows) + -> Materialize + -> Append + -> Seq Scan on t1 t1_2_1 + Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) + -> Seq Scan on t2 t1_2_2 + Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) + -> Seq Scan on t3 t1_2_3 + Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b)) +(21 rows) UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2 WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b @@ -1843,8 +1825,6 @@ NOTICE: f_leak => daddad_updt NOTICE: f_leak => daddad_updt NOTICE: f_leak => defdef NOTICE: f_leak => defdef -NOTICE: f_leak => daddad_updt -NOTICE: f_leak => defdef id | a | b | id | a | b | t1_1 | t1_2 -----+---+-------------+-----+---+-------------+---------------------+--------------------- 104 | 4 | daddad_updt | 104 | 4 | daddad_updt | (104,4,daddad_updt) | (104,4,daddad_updt) @@ -1880,19 +1860,20 @@ EXPLAIN (COSTS OFF) DELETE FROM only t1 WHERE f_leak(b); (3 rows) EXPLAIN (COSTS OFF) DELETE FROM t1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------ + QUERY PLAN +----------------------------------------------------- Delete on t1 - Delete on t1 - Delete on t2 t1_1 - Delete on t3 t1_2 - -> Seq Scan on t1 - Filter: (((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t2 t1_1 - Filter: (((a % 2) = 0) AND f_leak(b)) - -> Seq Scan on t3 t1_2 - Filter: (((a % 2) = 0) AND f_leak(b)) -(10 rows) + Delete on t1 t1_1 + Delete on t2 t1_2 + Delete on t3 t1_3 + -> Append + -> Seq Scan on t1 t1_1 + Filter: (((a % 2) = 0) AND f_leak(b)) + -> Seq Scan on t2 t1_2 + Filter: (((a % 2) = 0) AND f_leak(b)) + -> Seq Scan on t3 t1_3 + Filter: (((a % 2) = 0) AND f_leak(b)) +(11 rows) DELETE FROM only t1 WHERE f_leak(b) RETURNING tableoid::regclass, *, t1; NOTICE: f_leak => bbbbbb_updt diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index 24905332b1..cdff914b93 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1283,12 +1283,12 @@ SELECT * FROM rw_view1; (4 rows) EXPLAIN (verbose, costs off) UPDATE rw_view1 SET b = b + 1 RETURNING *; - QUERY PLAN -------------------------------------------------------------- + QUERY PLAN +------------------------------------------------- Update on public.base_tbl Output: base_tbl.a, base_tbl.b -> Seq Scan on public.base_tbl - Output: base_tbl.a, (base_tbl.b + 1), base_tbl.ctid + Output: (base_tbl.b + 1), base_tbl.ctid (4 rows) UPDATE rw_view1 SET b = b + 1 RETURNING *; @@ -1607,26 +1607,21 @@ UPDATE rw_view1 SET a = a + 1000 FROM other_tbl_parent WHERE a = id; QUERY PLAN ------------------------------------------------------------------------- Update on base_tbl_parent - Update on base_tbl_parent - Update on base_tbl_child base_tbl_parent_1 - -> Hash Join - Hash Cond: (other_tbl_parent.id = base_tbl_parent.a) - -> Append - -> Seq Scan on other_tbl_parent other_tbl_parent_1 - -> Seq Scan on other_tbl_child other_tbl_parent_2 - -> Hash - -> Seq Scan on base_tbl_parent + Update on base_tbl_parent base_tbl_parent_1 + Update on base_tbl_child base_tbl_parent_2 -> Merge Join - Merge Cond: (base_tbl_parent_1.a = other_tbl_parent.id) + Merge Cond: (base_tbl_parent.a = other_tbl_parent.id) -> Sort - Sort Key: base_tbl_parent_1.a - -> Seq Scan on base_tbl_child base_tbl_parent_1 + Sort Key: base_tbl_parent.a + -> Append + -> Seq Scan on base_tbl_parent base_tbl_parent_1 + -> Seq Scan on base_tbl_child base_tbl_parent_2 -> Sort Sort Key: other_tbl_parent.id -> Append -> Seq Scan on other_tbl_parent other_tbl_parent_1 -> Seq Scan on other_tbl_child other_tbl_parent_2 -(20 rows) +(15 rows) UPDATE rw_view1 SET a = a + 1000 FROM other_tbl_parent WHERE a = id; SELECT * FROM ONLY base_tbl_parent ORDER BY a; @@ -2332,36 +2327,39 @@ SELECT * FROM v1 WHERE a=8; EXPLAIN (VERBOSE, COSTS OFF) UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; - QUERY PLAN ------------------------------------------------------------------------------------------ + QUERY PLAN +----------------------------------------------------------------------------------------------------- Update on public.t1 - Update on public.t1 - Update on public.t11 t1_1 - Update on public.t12 t1_2 - Update on public.t111 t1_3 - -> Index Scan using t1_a_idx on public.t1 - Output: 100, t1.b, t1.c, t1.ctid - Index Cond: ((t1.a > 5) AND (t1.a < 7)) - Filter: ((t1.a <> 6) AND (SubPlan 1) AND snoop(t1.a) AND leakproof(t1.a)) - SubPlan 1 - -> Append - -> Seq Scan on public.t12 t12_1 - Filter: (t12_1.a = t1.a) - -> Seq Scan on public.t111 t12_2 - Filter: (t12_2.a = t1.a) - -> Index Scan using t11_a_idx on public.t11 t1_1 - Output: 100, t1_1.b, t1_1.c, t1_1.d, t1_1.ctid - Index Cond: ((t1_1.a > 5) AND (t1_1.a < 7)) - Filter: ((t1_1.a <> 6) AND (SubPlan 1) AND snoop(t1_1.a) AND leakproof(t1_1.a)) - -> Index Scan using t12_a_idx on public.t12 t1_2 - Output: 100, t1_2.b, t1_2.c, t1_2.e, t1_2.ctid - Index Cond: ((t1_2.a > 5) AND (t1_2.a < 7)) - Filter: ((t1_2.a <> 6) AND (SubPlan 1) AND snoop(t1_2.a) AND leakproof(t1_2.a)) - -> Index Scan using t111_a_idx on public.t111 t1_3 - Output: 100, t1_3.b, t1_3.c, t1_3.d, t1_3.e, t1_3.ctid - Index Cond: ((t1_3.a > 5) AND (t1_3.a < 7)) - Filter: ((t1_3.a <> 6) AND (SubPlan 1) AND snoop(t1_3.a) AND leakproof(t1_3.a)) -(27 rows) + Update on public.t1 t1_1 + Update on public.t11 t1_2 + Update on public.t12 t1_3 + Update on public.t111 t1_4 + -> Result + Output: 100, t1.tableoid, t1.ctid + -> Append + -> Index Scan using t1_a_idx on public.t1 t1_1 + Output: t1_1.tableoid, t1_1.ctid + Index Cond: ((t1_1.a > 5) AND (t1_1.a < 7)) + Filter: ((t1_1.a <> 6) AND (SubPlan 1) AND snoop(t1_1.a) AND leakproof(t1_1.a)) + SubPlan 1 + -> Append + -> Seq Scan on public.t12 t12_1 + Filter: (t12_1.a = t1_1.a) + -> Seq Scan on public.t111 t12_2 + Filter: (t12_2.a = t1_1.a) + -> Index Scan using t11_a_idx on public.t11 t1_2 + Output: t1_2.tableoid, t1_2.ctid + Index Cond: ((t1_2.a > 5) AND (t1_2.a < 7)) + Filter: ((t1_2.a <> 6) AND (SubPlan 1) AND snoop(t1_2.a) AND leakproof(t1_2.a)) + -> Index Scan using t12_a_idx on public.t12 t1_3 + Output: t1_3.tableoid, t1_3.ctid + Index Cond: ((t1_3.a > 5) AND (t1_3.a < 7)) + Filter: ((t1_3.a <> 6) AND (SubPlan 1) AND snoop(t1_3.a) AND leakproof(t1_3.a)) + -> Index Scan using t111_a_idx on public.t111 t1_4 + Output: t1_4.tableoid, t1_4.ctid + Index Cond: ((t1_4.a > 5) AND (t1_4.a < 7)) + Filter: ((t1_4.a <> 6) AND (SubPlan 1) AND snoop(t1_4.a) AND leakproof(t1_4.a)) +(30 rows) UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; SELECT * FROM v1 WHERE a=100; -- Nothing should have been changed to 100 @@ -2376,36 +2374,39 @@ SELECT * FROM t1 WHERE a=100; -- Nothing should have been changed to 100 EXPLAIN (VERBOSE, COSTS OFF) UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; - QUERY PLAN -------------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------- Update on public.t1 - Update on public.t1 - Update on public.t11 t1_1 - Update on public.t12 t1_2 - Update on public.t111 t1_3 - -> Index Scan using t1_a_idx on public.t1 - Output: (t1.a + 1), t1.b, t1.c, t1.ctid - Index Cond: ((t1.a > 5) AND (t1.a = 8)) - Filter: ((SubPlan 1) AND snoop(t1.a) AND leakproof(t1.a)) - SubPlan 1 - -> Append - -> Seq Scan on public.t12 t12_1 - Filter: (t12_1.a = t1.a) - -> Seq Scan on public.t111 t12_2 - Filter: (t12_2.a = t1.a) - -> Index Scan using t11_a_idx on public.t11 t1_1 - Output: (t1_1.a + 1), t1_1.b, t1_1.c, t1_1.d, t1_1.ctid - Index Cond: ((t1_1.a > 5) AND (t1_1.a = 8)) - Filter: ((SubPlan 1) AND snoop(t1_1.a) AND leakproof(t1_1.a)) - -> Index Scan using t12_a_idx on public.t12 t1_2 - Output: (t1_2.a + 1), t1_2.b, t1_2.c, t1_2.e, t1_2.ctid - Index Cond: ((t1_2.a > 5) AND (t1_2.a = 8)) - Filter: ((SubPlan 1) AND snoop(t1_2.a) AND leakproof(t1_2.a)) - -> Index Scan using t111_a_idx on public.t111 t1_3 - Output: (t1_3.a + 1), t1_3.b, t1_3.c, t1_3.d, t1_3.e, t1_3.ctid - Index Cond: ((t1_3.a > 5) AND (t1_3.a = 8)) - Filter: ((SubPlan 1) AND snoop(t1_3.a) AND leakproof(t1_3.a)) -(27 rows) + Update on public.t1 t1_1 + Update on public.t11 t1_2 + Update on public.t12 t1_3 + Update on public.t111 t1_4 + -> Result + Output: (t1.a + 1), t1.tableoid, t1.ctid + -> Append + -> Index Scan using t1_a_idx on public.t1 t1_1 + Output: t1_1.a, t1_1.tableoid, t1_1.ctid + Index Cond: ((t1_1.a > 5) AND (t1_1.a = 8)) + Filter: ((SubPlan 1) AND snoop(t1_1.a) AND leakproof(t1_1.a)) + SubPlan 1 + -> Append + -> Seq Scan on public.t12 t12_1 + Filter: (t12_1.a = t1_1.a) + -> Seq Scan on public.t111 t12_2 + Filter: (t12_2.a = t1_1.a) + -> Index Scan using t11_a_idx on public.t11 t1_2 + Output: t1_2.a, t1_2.tableoid, t1_2.ctid + Index Cond: ((t1_2.a > 5) AND (t1_2.a = 8)) + Filter: ((SubPlan 1) AND snoop(t1_2.a) AND leakproof(t1_2.a)) + -> Index Scan using t12_a_idx on public.t12 t1_3 + Output: t1_3.a, t1_3.tableoid, t1_3.ctid + Index Cond: ((t1_3.a > 5) AND (t1_3.a = 8)) + Filter: ((SubPlan 1) AND snoop(t1_3.a) AND leakproof(t1_3.a)) + -> Index Scan using t111_a_idx on public.t111 t1_4 + Output: t1_4.a, t1_4.tableoid, t1_4.ctid + Index Cond: ((t1_4.a > 5) AND (t1_4.a = 8)) + Filter: ((SubPlan 1) AND snoop(t1_4.a) AND leakproof(t1_4.a)) +(30 rows) UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; NOTICE: snooped value: 8 diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out index bf939d79f6..dc34ac67b3 100644 --- a/src/test/regress/expected/update.out +++ b/src/test/regress/expected/update.out @@ -172,14 +172,14 @@ EXPLAIN (VERBOSE, COSTS OFF) UPDATE update_test t SET (a, b) = (SELECT b, a FROM update_test s WHERE s.a = t.a) WHERE CURRENT_USER = SESSION_USER; - QUERY PLAN ------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------- Update on public.update_test t -> Result - Output: $1, $2, t.c, (SubPlan 1 (returns $1,$2)), t.ctid + Output: $1, $2, (SubPlan 1 (returns $1,$2)), t.ctid One-Time Filter: (CURRENT_USER = SESSION_USER) -> Seq Scan on public.update_test t - Output: t.c, t.a, t.ctid + Output: t.a, t.ctid SubPlan 1 (returns $1,$2) -> Seq Scan on public.update_test s Output: s.b, s.a @@ -308,8 +308,8 @@ ALTER TABLE part_b_10_b_20 ATTACH PARTITION part_c_1_100 FOR VALUES FROM (1) TO -- The order of subplans should be in bound order EXPLAIN (costs off) UPDATE range_parted set c = c - 50 WHERE c > 97; - QUERY PLAN -------------------------------------------------- + QUERY PLAN +------------------------------------------------------- Update on range_parted Update on part_a_1_a_10 range_parted_1 Update on part_a_10_a_20 range_parted_2 @@ -318,21 +318,22 @@ EXPLAIN (costs off) UPDATE range_parted set c = c - 50 WHERE c > 97; Update on part_d_1_15 range_parted_5 Update on part_d_15_20 range_parted_6 Update on part_b_20_b_30 range_parted_7 - -> Seq Scan on part_a_1_a_10 range_parted_1 - Filter: (c > '97'::numeric) - -> Seq Scan on part_a_10_a_20 range_parted_2 - Filter: (c > '97'::numeric) - -> Seq Scan on part_b_1_b_10 range_parted_3 - Filter: (c > '97'::numeric) - -> Seq Scan on part_c_1_100 range_parted_4 - Filter: (c > '97'::numeric) - -> Seq Scan on part_d_1_15 range_parted_5 - Filter: (c > '97'::numeric) - -> Seq Scan on part_d_15_20 range_parted_6 - Filter: (c > '97'::numeric) - -> Seq Scan on part_b_20_b_30 range_parted_7 - Filter: (c > '97'::numeric) -(22 rows) + -> Append + -> Seq Scan on part_a_1_a_10 range_parted_1 + Filter: (c > '97'::numeric) + -> Seq Scan on part_a_10_a_20 range_parted_2 + Filter: (c > '97'::numeric) + -> Seq Scan on part_b_1_b_10 range_parted_3 + Filter: (c > '97'::numeric) + -> Seq Scan on part_c_1_100 range_parted_4 + Filter: (c > '97'::numeric) + -> Seq Scan on part_d_1_15 range_parted_5 + Filter: (c > '97'::numeric) + -> Seq Scan on part_d_15_20 range_parted_6 + Filter: (c > '97'::numeric) + -> Seq Scan on part_b_20_b_30 range_parted_7 + Filter: (c > '97'::numeric) +(23 rows) -- fail, row movement happens only within the partition subtree. UPDATE part_c_100_200 set c = c - 20, d = c WHERE c = 105; diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 9a6b716ddc..0affacc191 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -2906,47 +2906,35 @@ SELECT * FROM parent; EXPLAIN (VERBOSE, COSTS OFF) WITH wcte AS ( INSERT INTO int8_tbl VALUES ( 42, 47 ) RETURNING q2 ) DELETE FROM a USING wcte WHERE aa = q2; - QUERY PLAN ----------------------------------------------------- + QUERY PLAN +------------------------------------------------------------ Delete on public.a - Delete on public.a - Delete on public.b a_1 - Delete on public.c a_2 - Delete on public.d a_3 + Delete on public.a a_1 + Delete on public.b a_2 + Delete on public.c a_3 + Delete on public.d a_4 CTE wcte -> Insert on public.int8_tbl Output: int8_tbl.q2 -> Result Output: '42'::bigint, '47'::bigint - -> Nested Loop - Output: a.ctid, wcte.* - Join Filter: (a.aa = wcte.q2) - -> Seq Scan on public.a - Output: a.ctid, a.aa - -> CTE Scan on wcte + -> Hash Join + Output: wcte.*, a.tableoid, a.ctid + Hash Cond: (a.aa = wcte.q2) + -> Append + -> Seq Scan on public.a a_1 + Output: a_1.aa, a_1.tableoid, a_1.ctid + -> Seq Scan on public.b a_2 + Output: a_2.aa, a_2.tableoid, a_2.ctid + -> Seq Scan on public.c a_3 + Output: a_3.aa, a_3.tableoid, a_3.ctid + -> Seq Scan on public.d a_4 + Output: a_4.aa, a_4.tableoid, a_4.ctid + -> Hash Output: wcte.*, wcte.q2 - -> Nested Loop - Output: a_1.ctid, wcte.* - Join Filter: (a_1.aa = wcte.q2) - -> Seq Scan on public.b a_1 - Output: a_1.ctid, a_1.aa - -> CTE Scan on wcte - Output: wcte.*, wcte.q2 - -> Nested Loop - Output: a_2.ctid, wcte.* - Join Filter: (a_2.aa = wcte.q2) - -> Seq Scan on public.c a_2 - Output: a_2.ctid, a_2.aa - -> CTE Scan on wcte - Output: wcte.*, wcte.q2 - -> Nested Loop - Output: a_3.ctid, wcte.* - Join Filter: (a_3.aa = wcte.q2) - -> Seq Scan on public.d a_3 - Output: a_3.ctid, a_3.aa - -> CTE Scan on wcte - Output: wcte.*, wcte.q2 -(38 rows) + -> CTE Scan on wcte + Output: wcte.*, wcte.q2 +(26 rows) -- error cases -- data-modifying WITH tries to use its own output |