Thread: Improving EXPLAIN's display of SubPlan nodes
EXPLAIN has always been really poor at displaying SubPlan nodes in expressions: you don't get much more than "(SubPlan N)". This is mostly because every time I thought about it, I despaired of trying to represent all the information in a SubPlan accurately. However, a recent discussion[1] made me realize that we could do a lot better just by displaying the SubLinkType and the testexpr (if relevant). So here's a proposed patch. You can see what it does by examining the regression test changes. There's plenty of room to bikeshed about exactly how to display this stuff, and I'm open to suggestions. BTW, I was somewhat depressed to discover that we have exactly zero regression coverage of the ROWCOMPARE_SUBLINK code paths; not only was EXPLAIN output not covered, but the planner and executor too. So I added some simple tests for that. Otherwise I think existing coverage is enough for this. regards, tom lane [1] https://www.postgresql.org/message-id/flat/149c5c2f-4267-44e3-a177-d1fd24c53f6d%40universite-paris-saclay.fr From 30d4c77641876db2ffd81a188bd2e41856a7a99e Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Fri, 19 Jan 2024 14:16:09 -0500 Subject: [PATCH v1] Improve EXPLAIN's display of SubPlan nodes. Represent the SubLinkType as best we can, and show the testexpr where relevant. To aid in interpreting testexprs, add the output parameter IDs to the subplan name for subplans as well as initplans. --- .../postgres_fdw/expected/postgres_fdw.out | 12 +- src/backend/optimizer/plan/subselect.c | 30 ++-- src/backend/utils/adt/ruleutils.c | 45 +++++- src/test/regress/expected/aggregates.out | 2 +- src/test/regress/expected/insert_conflict.out | 2 +- src/test/regress/expected/join.out | 24 ++-- src/test/regress/expected/memoize.out | 2 +- src/test/regress/expected/rowsecurity.out | 56 ++++---- src/test/regress/expected/select_parallel.out | 22 +-- src/test/regress/expected/subselect.out | 135 ++++++++++++------ src/test/regress/expected/updatable_views.out | 24 ++-- src/test/regress/sql/subselect.sql | 14 ++ 12 files changed, 241 insertions(+), 127 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index d83f6ae8cb..2b5e56cae9 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -3264,14 +3264,14 @@ select sum(c1) filter (where (c1 / c1) * random() <= 1) from ft1 group by c2 ord explain (verbose, costs off) select sum(c2) filter (where c2 in (select c2 from ft1 where c2 < 5)) from ft1; - QUERY PLAN -------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------------- Aggregate - Output: sum(ft1.c2) FILTER (WHERE (hashed SubPlan 1)) + Output: sum(ft1.c2) FILTER (WHERE (ANY (ft1.c2 = $0) FROM HASHED SubPlan 1 (returns $0))) -> Foreign Scan on public.ft1 Output: ft1.c2 Remote SQL: SELECT c2 FROM "S 1"."T 1" - SubPlan 1 + SubPlan 1 (returns $0) -> Foreign Scan on public.ft1 ft1_1 Output: ft1_1.c2 Remote SQL: SELECT c2 FROM "S 1"."T 1" WHERE ((c2 < 5)) @@ -11899,8 +11899,8 @@ SELECT a FROM base_tbl WHERE a IN (SELECT a FROM foreign_tbl); ----------------------------------------------------------------------------- Seq Scan on public.base_tbl Output: base_tbl.a - Filter: (SubPlan 1) - SubPlan 1 + Filter: (ANY (base_tbl.a = $1) FROM SubPlan 1 (returns $1)) + SubPlan 1 (returns $1) -> Result Output: base_tbl.a -> Append diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 3115d79ad9..9ceac6234d 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -326,6 +326,7 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot, Node *result; SubPlan *splan; bool isInitPlan; + StringInfoData splanname; ListCell *lc; /* @@ -560,22 +561,31 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot, splan->plan_id); /* Label the subplan for EXPLAIN purposes */ - splan->plan_name = palloc(32 + 12 * list_length(splan->setParam)); - sprintf(splan->plan_name, "%s %d", - isInitPlan ? "InitPlan" : "SubPlan", - splan->plan_id); + initStringInfo(&splanname); + appendStringInfo(&splanname, "%s %d", + isInitPlan ? "InitPlan" : "SubPlan", + splan->plan_id); if (splan->setParam) { - char *ptr = splan->plan_name + strlen(splan->plan_name); - - ptr += sprintf(ptr, " (returns "); + appendStringInfoString(&splanname, " (returns "); foreach(lc, splan->setParam) { - ptr += sprintf(ptr, "$%d%s", - lfirst_int(lc), - lnext(splan->setParam, lc) ? "," : ")"); + appendStringInfo(&splanname, "$%d%s", + lfirst_int(lc), + lnext(splan->setParam, lc) ? "," : ")"); + } + } + else if (splan->paramIds) + { + appendStringInfoString(&splanname, " (returns "); + foreach(lc, splan->paramIds) + { + appendStringInfo(&splanname, "$%d%s", + lfirst_int(lc), + lnext(splan->paramIds, lc) ? "," : ")"); } } + splan->plan_name = splanname.data; /* Lastly, fill in the cost estimates for use later */ cost_subplan(root, splan, plan); diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 0b2a164057..9ee35640ba 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -8869,12 +8869,51 @@ get_rule_expr(Node *node, deparse_context *context, * We cannot see an already-planned subplan in rule deparsing, * only while EXPLAINing a query plan. We don't try to * reconstruct the original SQL, just reference the subplan - * that appears elsewhere in EXPLAIN's result. + * that appears elsewhere in EXPLAIN's result. It does seem + * useful to show the subLinkType and testexpr, however, and + * we also note whether the subplan will be hashed. */ + switch (subplan->subLinkType) + { + case EXISTS_SUBLINK: + appendStringInfoString(buf, "EXISTS("); + Assert(subplan->testexpr == NULL); + break; + case ALL_SUBLINK: + appendStringInfoString(buf, "(ALL "); + get_rule_expr(subplan->testexpr, context, showimplicit); + appendStringInfoString(buf, " FROM "); + break; + case ANY_SUBLINK: + appendStringInfoString(buf, "(ANY "); + get_rule_expr(subplan->testexpr, context, showimplicit); + appendStringInfoString(buf, " FROM "); + break; + case ROWCOMPARE_SUBLINK: + appendStringInfoString(buf, "(ROWCOMPARE "); + get_rule_expr(subplan->testexpr, context, showimplicit); + appendStringInfoString(buf, " FROM "); + break; + case EXPR_SUBLINK: + case MULTIEXPR_SUBLINK: + /* No need to decorate these subplan references */ + appendStringInfoString(buf, "("); + Assert(subplan->testexpr == NULL); + break; + case ARRAY_SUBLINK: + appendStringInfoString(buf, "ARRAY("); + Assert(subplan->testexpr == NULL); + break; + case CTE_SUBLINK: + /* This case is unreachable within expressions */ + appendStringInfoString(buf, "CTE("); + Assert(subplan->testexpr == NULL); + break; + } if (subplan->useHashTable) - appendStringInfo(buf, "(hashed %s)", subplan->plan_name); + appendStringInfo(buf, "HASHED %s)", subplan->plan_name); else - appendStringInfo(buf, "(%s)", subplan->plan_name); + appendStringInfo(buf, "%s)", subplan->plan_name); } break; diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index f635c5a1af..1e2f3c44f5 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -738,7 +738,7 @@ select array(select sum(x+y) s QUERY PLAN ------------------------------------------------------------------- Function Scan on pg_catalog.generate_series x - Output: (SubPlan 1) + Output: ARRAY(SubPlan 1) Function Call: generate_series(1, 3) SubPlan 1 -> Sort diff --git a/src/test/regress/expected/insert_conflict.out b/src/test/regress/expected/insert_conflict.out index 9e9e3bd00c..9a1e64dbf0 100644 --- a/src/test/regress/expected/insert_conflict.out +++ b/src/test/regress/expected/insert_conflict.out @@ -50,7 +50,7 @@ explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on con Insert on insertconflicttest Conflict Resolution: UPDATE Conflict Arbiter Indexes: op_index_key, collation_index_key, both_index_key - Conflict Filter: (SubPlan 1) + Conflict Filter: EXISTS(SubPlan 1) -> Result SubPlan 1 -> Index Only Scan using both_index_expr_key on insertconflicttest ii diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index a2fad81d7a..451f21698f 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -3028,11 +3028,11 @@ where unique1 in (select unique2 from tenk1 b); explain (costs off) select a.* from tenk1 a where unique1 not in (select unique2 from tenk1 b); - QUERY PLAN --------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------- Seq Scan on tenk1 a - Filter: (NOT (hashed SubPlan 1)) - SubPlan 1 + Filter: (NOT (ANY (unique1 = $0) FROM HASHED SubPlan 1 (returns $0))) + SubPlan 1 (returns $0) -> Index Only Scan using tenk1_unique2 on tenk1 b (4 rows) @@ -5278,13 +5278,13 @@ explain (costs off) select a.unique1, b.unique2 from onek a left join onek b on a.unique1 = b.unique2 where b.unique2 = any (select q1 from int8_tbl c where c.q1 < b.unique1); - QUERY PLAN ----------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------ Hash Join Hash Cond: (b.unique2 = a.unique1) -> Seq Scan on onek b - Filter: (SubPlan 1) - SubPlan 1 + Filter: (ANY (unique2 = $1) FROM SubPlan 1 (returns $1)) + SubPlan 1 (returns $1) -> Seq Scan on int8_tbl c Filter: (q1 < b.unique1) -> Hash @@ -8219,8 +8219,8 @@ lateral (select * from int8_tbl t1, where q2 = (select greatest(t1.q1,t2.q2)) and (select v.id=0)) offset 0) ss2) ss where t1.q1 = ss.q2) ss0; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------- Nested Loop Output: "*VALUES*".column1, t1.q1, t1.q2, ss2.q1, ss2.q2 -> Seq Scan on public.int8_tbl t1 @@ -8234,8 +8234,8 @@ lateral (select * from int8_tbl t1, Filter: (t1.q1 = ss2.q2) -> Seq Scan on public.int8_tbl t2 Output: t2.q1, t2.q2 - Filter: (SubPlan 3) - SubPlan 3 + Filter: (ANY (t2.q1 = $5) FROM SubPlan 3 (returns $5)) + SubPlan 3 (returns $5) -> Result Output: t3.q2 One-Time Filter: $4 diff --git a/src/test/regress/expected/memoize.out b/src/test/regress/expected/memoize.out index f5202430f8..e7f0d20fc9 100644 --- a/src/test/regress/expected/memoize.out +++ b/src/test/regress/expected/memoize.out @@ -276,7 +276,7 @@ WHERE unique1 < 3 ---------------------------------------------------------------- Index Scan using tenk1_unique1 on tenk1 t0 Index Cond: (unique1 < 3) - Filter: (SubPlan 1) + Filter: EXISTS(SubPlan 1) SubPlan 1 -> Nested Loop -> Index Scan using tenk1_hundred on tenk1 t2 diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index 6988128aa4..3f3815a9cf 100644 --- a/src/test/regress/expected/rowsecurity.out +++ b/src/test/regress/expected/rowsecurity.out @@ -1437,11 +1437,11 @@ NOTICE: f_leak => 03b26944890929ff751653acb2f2af79 (1 row) EXPLAIN (COSTS OFF) SELECT * FROM only s1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------------------ + QUERY PLAN +----------------------------------------------------------------------------- Seq Scan on s1 - Filter: ((hashed SubPlan 1) AND f_leak(b)) - SubPlan 1 + Filter: ((ANY (a = $0) FROM HASHED SubPlan 1 (returns $0)) AND f_leak(b)) + SubPlan 1 (returns $0) -> Seq Scan on s2 Filter: (((x % 2) = 0) AND (y ~~ '%2f%'::text)) (5 rows) @@ -1457,11 +1457,11 @@ NOTICE: f_leak => 03b26944890929ff751653acb2f2af79 (1 row) EXPLAIN (COSTS OFF) SELECT * FROM s1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------------------ + QUERY PLAN +----------------------------------------------------------------------------- Seq Scan on s1 - Filter: ((hashed SubPlan 1) AND f_leak(b)) - SubPlan 1 + Filter: ((ANY (a = $0) FROM HASHED SubPlan 1 (returns $0)) AND f_leak(b)) + SubPlan 1 (returns $0) -> Seq Scan on s2 Filter: (((x % 2) = 0) AND (y ~~ '%af%'::text)) (5 rows) @@ -1473,15 +1473,15 @@ SELECT (SELECT x FROM s1 LIMIT 1) xx, * FROM s2 WHERE y like '%28%'; (1 row) EXPLAIN (COSTS OFF) SELECT (SELECT x FROM s1 LIMIT 1) xx, * FROM s2 WHERE y like '%28%'; - QUERY PLAN -------------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------- Seq Scan on s2 Filter: (((x % 2) = 0) AND (y ~~ '%28%'::text)) SubPlan 2 -> Limit -> Seq Scan on s1 - Filter: (hashed SubPlan 1) - SubPlan 1 + Filter: (ANY (a = $1) FROM HASHED SubPlan 1 (returns $1)) + SubPlan 1 (returns $1) -> Seq Scan on s2 s2_1 Filter: (((x % 2) = 0) AND (y ~~ '%af%'::text)) (9 rows) @@ -2687,11 +2687,11 @@ NOTICE: f_leak => bbb (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 0) AND f_leak(b)) - SubPlan 1 + Filter: ((NOT (ANY (a = $0) FROM HASHED SubPlan 1 (returns $0))) AND ((a % 2) = 0) AND f_leak(b)) + SubPlan 1 (returns $0) -> Seq Scan on z1_blacklist (4 rows) @@ -2705,11 +2705,11 @@ NOTICE: f_leak => bbb (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 0) AND f_leak(b)) - SubPlan 1 + Filter: ((NOT (ANY (a = $0) FROM HASHED SubPlan 1 (returns $0))) AND ((a % 2) = 0) AND f_leak(b)) + SubPlan 1 (returns $0) -> Seq Scan on z1_blacklist (4 rows) @@ -2877,11 +2877,11 @@ NOTICE: f_leak => bbb (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 0) AND f_leak(b)) - SubPlan 1 + Filter: ((NOT (ANY (a = $0) FROM HASHED SubPlan 1 (returns $0))) AND ((a % 2) = 0) AND f_leak(b)) + SubPlan 1 (returns $0) -> Seq Scan on z1_blacklist (4 rows) @@ -2903,11 +2903,11 @@ NOTICE: f_leak => aba (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 1) AND f_leak(b)) - SubPlan 1 + Filter: ((NOT (ANY (a = $0) FROM HASHED SubPlan 1 (returns $0))) AND ((a % 2) = 1) AND f_leak(b)) + SubPlan 1 (returns $0) -> Seq Scan on z1_blacklist (4 rows) diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out index d88353d496..09a90cd78f 100644 --- a/src/test/regress/expected/select_parallel.out +++ b/src/test/regress/expected/select_parallel.out @@ -291,15 +291,15 @@ alter table tenk2 set (parallel_workers = 0); explain (costs off) select count(*) from tenk1 where (two, four) not in (select hundred, thousand from tenk2 where thousand > 100); - QUERY PLAN ------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------ Finalize Aggregate -> Gather Workers Planned: 4 -> Partial Aggregate -> Parallel Seq Scan on tenk1 - Filter: (NOT (hashed SubPlan 1)) - SubPlan 1 + Filter: (NOT (ANY ((two = $0) AND (four = $1)) FROM HASHED SubPlan 1 (returns $0,$1))) + SubPlan 1 (returns $0,$1) -> Seq Scan on tenk2 Filter: (thousand > 100) (9 rows) @@ -315,11 +315,11 @@ select count(*) from tenk1 where (two, four) not in explain (costs off) select * from tenk1 where (unique1 + random())::integer not in (select ten from tenk2); - QUERY PLAN ------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------- Seq Scan on tenk1 - Filter: (NOT (hashed SubPlan 1)) - SubPlan 1 + Filter: (NOT (ANY ((((unique1)::double precision + random()))::integer = $0) FROM HASHED SubPlan 1 (returns $0))) + SubPlan 1 (returns $0) -> Seq Scan on tenk2 (4 rows) @@ -1129,10 +1129,10 @@ ORDER BY 1, 2, 3; EXPLAIN (VERBOSE, COSTS OFF) SELECT generate_series(1, two), array(select generate_series(1, two)) FROM tenk1 ORDER BY tenthous; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------- ProjectSet - Output: generate_series(1, tenk1.two), (SubPlan 1), tenk1.tenthous + Output: generate_series(1, tenk1.two), ARRAY(SubPlan 1), tenk1.tenthous -> Gather Merge Output: tenk1.two, tenk1.tenthous Workers Planned: 4 diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out index a3a4d03d10..57cfa10030 100644 --- a/src/test/regress/expected/subselect.out +++ b/src/test/regress/expected/subselect.out @@ -202,6 +202,57 @@ SELECT f1 AS "Correlated Field" 3 (5 rows) +-- Check ROWCOMPARE cases, both correlated and not +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + QUERY PLAN +------------------------------------------------------------------------------- + Seq Scan on public.subselect_tbl + Output: (ROWCOMPARE ((1 = $2) AND (2 = $3)) FROM SubPlan 1 (returns $2,$3)) + SubPlan 1 (returns $2,$3) + -> Result + Output: subselect_tbl.f1, subselect_tbl.f2 +(5 rows) + +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + eq +---- + t + f + f + f + f + f + f + f +(8 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + QUERY PLAN +----------------------------------- + Seq Scan on public.subselect_tbl + Output: ((1 = $0) AND (2 = $1)) + InitPlan 1 (returns $0,$1) + -> Result + Output: 3, 4 +(5 rows) + +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + eq +---- + f + f + f + f + f + f + f + f +(8 rows) + +SELECT ROW(1, 2) = (SELECT f1, f2 FROM SUBSELECT_TBL); -- error +ERROR: more than one row returned by a subquery used as an expression -- Subselects without aliases SELECT count FROM (SELECT COUNT(DISTINCT name) FROM road); count @@ -324,11 +375,11 @@ explain (verbose, costs off) select '42' union all select 43; -- check materialization of an initplan reference (bug #14524) explain (verbose, costs off) select 1 = all (select (select 1)); - QUERY PLAN ------------------------------------ + QUERY PLAN +------------------------------------------------------ Result - Output: (SubPlan 2) - SubPlan 2 + Output: (ALL (1 = $1) FROM SubPlan 2 (returns $1)) + SubPlan 2 (returns $1) -> Materialize Output: ($0) InitPlan 1 (returns $0) @@ -377,7 +428,7 @@ select * from int4_tbl o where exists QUERY PLAN -------------------------------------- Seq Scan on int4_tbl o - Filter: (SubPlan 1) + Filter: EXISTS(SubPlan 1) SubPlan 1 -> Limit -> Seq Scan on int4_tbl i @@ -840,11 +891,11 @@ select * from outer_text where (f1, f2) not in (select * from inner_text); -- explain (verbose, costs off) select 'foo'::text in (select 'bar'::name union all select 'bar'::name); - QUERY PLAN -------------------------------------- + QUERY PLAN +----------------------------------------------------------------------- Result - Output: (hashed SubPlan 1) - SubPlan 1 + Output: (ANY ('foo'::text = $0) FROM HASHED SubPlan 1 (returns $0)) + SubPlan 1 (returns $0) -> Append -> Result Output: 'bar'::name @@ -864,11 +915,11 @@ select 'foo'::text in (select 'bar'::name union all select 'bar'::name); -- explain (verbose, costs off) select row(row(row(1))) = any (select row(row(1))); - QUERY PLAN -------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------- Result - Output: (SubPlan 1) - SubPlan 1 + Output: (ANY ('("(1)")'::record = $0) FROM SubPlan 1 (returns $0)) + SubPlan 1 (returns $0) -> Materialize Output: '("(1)")'::record -> Result @@ -907,11 +958,11 @@ language sql as 'select $1::text = $2'; create operator = (procedure=bogus_int8_text_eq, leftarg=int8, rightarg=text); explain (costs off) select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------- + QUERY PLAN +---------------------------------------------------------------------- Seq Scan on int8_tbl - Filter: (hashed SubPlan 1) - SubPlan 1 + Filter: (ANY ((q1)::text = $0) FROM HASHED SubPlan 1 (returns $0)) + SubPlan 1 (returns $0) -> Seq Scan on inner_text (4 rows) @@ -928,11 +979,11 @@ create or replace function bogus_int8_text_eq(int8, text) returns boolean language sql as 'select $1::text = $2 and $1::text = $2'; explain (costs off) select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------------- Seq Scan on int8_tbl - Filter: (hashed SubPlan 1) - SubPlan 1 + Filter: (ANY (((q1)::text = $0) AND ((q1)::text = $0)) FROM HASHED SubPlan 1 (returns $0)) + SubPlan 1 (returns $0) -> Seq Scan on inner_text (4 rows) @@ -949,11 +1000,11 @@ create or replace function bogus_int8_text_eq(int8, text) returns boolean language sql as 'select $2 = $1::text'; explain (costs off) select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------------- + QUERY PLAN +--------------------------------------------------------------- Seq Scan on int8_tbl - Filter: (SubPlan 1) - SubPlan 1 + Filter: (ANY ($0 = (q1)::text) FROM SubPlan 1 (returns $0)) + SubPlan 1 (returns $0) -> Materialize -> Seq Scan on inner_text (5 rows) @@ -972,12 +1023,12 @@ rollback; -- to get rid of the bogus operator explain (costs off) select count(*) from tenk1 t where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0); - QUERY PLAN --------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------- Aggregate -> Seq Scan on tenk1 t - Filter: ((hashed SubPlan 2) OR (ten < 0)) - SubPlan 2 + Filter: ((ANY (unique2 = $1) FROM HASHED SubPlan 2 (returns $1)) OR (ten < 0)) + SubPlan 2 (returns $1) -> Index Only Scan using tenk1_unique1 on tenk1 k (5 rows) @@ -997,7 +1048,7 @@ where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0) Aggregate -> Bitmap Heap Scan on tenk1 t Recheck Cond: (thousand = 1) - Filter: ((SubPlan 1) OR (ten < 0)) + Filter: (EXISTS(SubPlan 1) OR (ten < 0)) -> Bitmap Index Scan on tenk1_thous_tenthous Index Cond: (thousand = 1) SubPlan 1 @@ -1022,11 +1073,11 @@ analyze exists_tbl; explain (costs off) select * from exists_tbl t1 where (exists(select 1 from exists_tbl t2 where t1.c1 = t2.c2) or c3 < 0); - QUERY PLAN ------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------- Append -> Seq Scan on exists_tbl_null t1_1 - Filter: ((SubPlan 1) OR (c3 < 0)) + Filter: (EXISTS(SubPlan 1) OR (c3 < 0)) SubPlan 1 -> Append -> Seq Scan on exists_tbl_null t2_1 @@ -1034,8 +1085,8 @@ select * from exists_tbl t1 -> Seq Scan on exists_tbl_def t2_2 Filter: (t1_1.c1 = c2) -> Seq Scan on exists_tbl_def t1_2 - Filter: ((hashed SubPlan 2) OR (c3 < 0)) - SubPlan 2 + Filter: ((ANY (c1 = $1) FROM HASHED SubPlan 2 (returns $1)) OR (c3 < 0)) + SubPlan 2 (returns $1) -> Append -> Seq Scan on exists_tbl_null t2_4 -> Seq Scan on exists_tbl_def t2_5 @@ -1143,16 +1194,16 @@ where o.ten = 0; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Aggregate - Output: sum((((hashed SubPlan 1)))::integer) + Output: sum((((ANY (i.ten = $1) FROM HASHED SubPlan 1 (returns $1))))::integer) -> Nested Loop - Output: ((hashed SubPlan 1)) + Output: ((ANY (i.ten = $1) FROM HASHED SubPlan 1 (returns $1))) -> Seq Scan on public.onek o Output: o.unique1, o.unique2, o.two, o.four, o.ten, o.twenty, o.hundred, o.thousand, o.twothousand, o.fivethous,o.tenthous, o.odd, o.even, o.stringu1, o.stringu2, o.string4 Filter: (o.ten = 0) -> Index Scan using onek_unique1 on public.onek i - Output: (hashed SubPlan 1), random() + Output: (ANY (i.ten = $1) FROM HASHED SubPlan 1 (returns $1)), random() Index Cond: (i.unique1 = o.unique1) - SubPlan 1 + SubPlan 1 (returns $1) -> Seq Scan on public.int4_tbl Output: int4_tbl.f1 Filter: (int4_tbl.f1 <= o.hundred) @@ -1319,7 +1370,7 @@ select * from ---------------------------------------- Values Scan on "*VALUES*" Output: "*VALUES*".column1 - SubPlan 1 + SubPlan 1 (returns $0) -> Values Scan on "*VALUES*_1" Output: "*VALUES*_1".column1 (5 rows) @@ -1346,12 +1397,12 @@ select * from int4_tbl where --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Nested Loop Semi Join Output: int4_tbl.f1 - Join Filter: (CASE WHEN (hashed SubPlan 1) THEN int4_tbl.f1 ELSE NULL::integer END = b.ten) + Join Filter: (CASE WHEN (ANY (int4_tbl.f1 = $0) FROM HASHED SubPlan 1 (returns $0)) THEN int4_tbl.f1 ELSE NULL::integerEND = b.ten) -> Seq Scan on public.int4_tbl Output: int4_tbl.f1 -> Seq Scan on public.tenk1 b Output: b.unique1, b.unique2, b.two, b.four, b.ten, b.twenty, b.hundred, b.thousand, b.twothousand, b.fivethous,b.tenthous, b.odd, b.even, b.stringu1, b.stringu2, b.string4 - SubPlan 1 + SubPlan 1 (returns $0) -> Index Only Scan using tenk1_unique1 on public.tenk1 a Output: a.unique1 (10 rows) diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index 1950e6f281..86f09b6284 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -2586,8 +2586,8 @@ 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 t1_1 Update on public.t11 t1_2 @@ -2599,7 +2599,7 @@ UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; -> 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)) + Filter: ((t1_1.a <> 6) AND EXISTS(SubPlan 1) AND snoop(t1_1.a) AND leakproof(t1_1.a)) SubPlan 1 -> Append -> Seq Scan on public.t12 t12_1 @@ -2609,15 +2609,15 @@ UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; -> 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)) + Filter: ((t1_2.a <> 6) AND EXISTS(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)) + Filter: ((t1_3.a <> 6) AND EXISTS(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)) + Filter: ((t1_4.a <> 6) AND EXISTS(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; @@ -2633,8 +2633,8 @@ 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 t1_1 Update on public.t11 t1_2 @@ -2646,7 +2646,7 @@ UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; -> 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)) + Filter: (EXISTS(SubPlan 1) AND snoop(t1_1.a) AND leakproof(t1_1.a)) SubPlan 1 -> Append -> Seq Scan on public.t12 t12_1 @@ -2656,15 +2656,15 @@ UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; -> 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)) + Filter: (EXISTS(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)) + Filter: (EXISTS(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)) + Filter: (EXISTS(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; diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql index bd2e39efd4..91f9e15c6a 100644 --- a/src/test/regress/sql/subselect.sql +++ b/src/test/regress/sql/subselect.sql @@ -82,6 +82,20 @@ SELECT f1 AS "Correlated Field" WHERE (f1, f2) IN (SELECT f2, CAST(f3 AS int4) FROM SUBSELECT_TBL WHERE f3 IS NOT NULL); +-- Check ROWCOMPARE cases, both correlated and not + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + +SELECT ROW(1, 2) = (SELECT f1, f2 FROM SUBSELECT_TBL); -- error + -- Subselects without aliases SELECT count FROM (SELECT COUNT(DISTINCT name) FROM road); -- 2.39.3
Hi, > EXPLAIN has always been really poor at displaying SubPlan nodes > in expressions: you don't get much more than "(SubPlan N)". > This is mostly because every time I thought about it, I despaired > of trying to represent all the information in a SubPlan accurately. > However, a recent discussion[1] made me realize that we could do > a lot better just by displaying the SubLinkType and the testexpr > (if relevant). So here's a proposed patch. You can see what > it does by examining the regression test changes. > > There's plenty of room to bikeshed about exactly how to display > this stuff, and I'm open to suggestions. > > BTW, I was somewhat depressed to discover that we have exactly > zero regression coverage of the ROWCOMPARE_SUBLINK code paths; > not only was EXPLAIN output not covered, but the planner and > executor too. So I added some simple tests for that. Otherwise > I think existing coverage is enough for this. I reviewed the code and tested the patch on MacOS. It looks good to me. Although something like: ``` + Filter: (ANY (base_tbl.a = $1) FROM SubPlan 1 (returns $1)) + SubPlan 1 (returns $1) ``` ... arguably doesn't give much more information to the user comparing to what we have now: ``` - Filter: (SubPlan 1) - SubPlan 1 ``` ... I believe this is the right step toward more detailed EXPLAINs, and perhaps could be useful for debugging and/or educational purposes. Also the patch improves code coverage. -- Best regards, Aleksander Alekseev
Aleksander Alekseev <aleksander@timescale.com> writes: > Although something like: > ``` > + Filter: (ANY (base_tbl.a = $1) FROM SubPlan 1 (returns $1)) > + SubPlan 1 (returns $1) > ``` > ... arguably doesn't give much more information to the user comparing > to what we have now: > ``` > - Filter: (SubPlan 1) > - SubPlan 1 > ``` Yeah, I would probably not have thought to do this on my own; but we had an actual user request for it. I think arguably the main benefit is to confirm "yes, this is the sub-select you think it is". The main thing that's still missing compared to what is in the plan data structure is information about which Param is which. I think we have the subplan output Params relatively well covered through the expedient of listing them in the generated plan_name, but it's still not apparent to me how we could shoehorn subplan input Params into this (or whether it's worth doing). regards, tom lane
Hi Aleksander and Tom I do confirm that I requested to get this information, in order to recover the formula to filter on. Thanks to both of you Chantal Le 22/01/2024 à 18:07, Tom Lane a écrit : > Aleksander Alekseev <aleksander@timescale.com> writes: >> Although something like: > >> ``` >> + Filter: (ANY (base_tbl.a = $1) FROM SubPlan 1 (returns $1)) >> + SubPlan 1 (returns $1) >> ``` > >> ... arguably doesn't give much more information to the user comparing >> to what we have now: > >> ``` >> - Filter: (SubPlan 1) >> - SubPlan 1 >> ``` > > Yeah, I would probably not have thought to do this on my own; but > we had an actual user request for it. I think arguably the main > benefit is to confirm "yes, this is the sub-select you think it is". > > The main thing that's still missing compared to what is in the plan > data structure is information about which Param is which. I think > we have the subplan output Params relatively well covered through > the expedient of listing them in the generated plan_name, but it's > still not apparent to me how we could shoehorn subplan input > Params into this (or whether it's worth doing). > > regards, tom lane
I wrote: > The main thing that's still missing compared to what is in the plan > data structure is information about which Param is which. I think > we have the subplan output Params relatively well covered through > the expedient of listing them in the generated plan_name, but it's > still not apparent to me how we could shoehorn subplan input > Params into this (or whether it's worth doing). Actually ... it looks like it probably isn't worth doing, because it's already the case that we don't expose input Params as such. EXPLAIN searches for the referent of an input Param and displays that (cf find_param_referent()). Just for experimental purposes, I wrote a follow-on patch to add printout of the parParam and args list (attached, as .txt so the cfbot doesn't think it's a patch). This produces results like explain (verbose, costs off) select array(select sum(x+y) s from generate_series(1,3) y group by y order by s) from generate_series(1,3) x; QUERY PLAN ------------------------------------------------------------------- Function Scan on pg_catalog.generate_series x Output: ARRAY(SubPlan 1 PASSING $0 := x.x) ^^^^^^^^^^^^^^^^^ added by delta patch Function Call: generate_series(1, 3) SubPlan 1 -> Sort Output: (sum((x.x + y.y))), y.y Sort Key: (sum((x.x + y.y))) -> HashAggregate Output: sum((x.x + y.y)), y.y ^^^ actual reference to $0 Group Key: y.y -> Function Scan on pg_catalog.generate_series y Output: y.y Function Call: generate_series(1, 3) (13 rows) As you can see, it's not necessary to explain what $0 is because that name isn't shown anywhere else --- the references to "x.x" in the subplan are actually uses of $0. So now I'm thinking that we do have enough detail in the present proposal, and we just need to think about whether there's some nicer way to present it than the particular spelling I used here. One idea I considered briefly is to pull the same trick with regards to output parameters --- that is, instead of adding all the "returns $n" annotations to subplans, drill down and print the subplan's relevant targetlist expression instead of "$n". On balance I think that might be more confusing not less so, though. SQL users are used to the idea that a sub-select can "see" variables from the outer query, but not at all vice-versa. I think it probably wouldn't be formally ambiguous, because ruleutils already de-duplicates table aliases across the whole tree, but it still seems likely to be confusing. Also, people are already pretty used to seeing $n to represent the outputs of InitPlans, and I've not heard many complaints suggesting that we should change that. regards, tom lane diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 9ee35640ba..b3da98a2f7 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -8911,9 +8911,30 @@ get_rule_expr(Node *node, deparse_context *context, break; } if (subplan->useHashTable) - appendStringInfo(buf, "HASHED %s)", subplan->plan_name); + appendStringInfo(buf, "HASHED %s", subplan->plan_name); else - appendStringInfo(buf, "%s)", subplan->plan_name); + appendStringInfo(buf, "%s", subplan->plan_name); + if (subplan->parParam) + { + ListCell *lc3; + ListCell *lc4; + bool first = true; + + appendStringInfoString(buf, " PASSING "); + forboth(lc3, subplan->parParam, lc4, subplan->args) + { + int paramid = lfirst_int(lc3); + Node *arg = (Node *) lfirst(lc4); + + if (first) + first = false; + else + appendStringInfoString(buf, ", "); + appendStringInfo(buf, "$%d := ", paramid); + get_rule_expr(arg, context, showimplicit); + } + } + appendStringInfoChar(buf, ')'); } break;
I wrote: > So now I'm thinking that we do have enough detail in the present > proposal, and we just need to think about whether there's some > nicer way to present it than the particular spelling I used here. Here's a rebase over 9f1337639 --- no code changes, but this affects some of the new or changed expected outputs from that commit. regards, tom lane From f84343864e74501df627f986e326f766072398cd Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Fri, 16 Feb 2024 14:32:23 -0500 Subject: [PATCH v2] Improve EXPLAIN's display of SubPlan nodes. Represent the SubLinkType as best we can, and show the testexpr where relevant. To aid in interpreting testexprs, add the output parameter IDs to the subplan name for subplans as well as initplans. Discussion: https://postgr.es/m/2838538.1705692747@sss.pgh.pa.us --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/optimizer/plan/subselect.c | 30 ++-- src/backend/utils/adt/ruleutils.c | 45 ++++- src/test/regress/expected/aggregates.out | 2 +- src/test/regress/expected/insert_conflict.out | 2 +- src/test/regress/expected/join.out | 24 +-- src/test/regress/expected/memoize.out | 2 +- src/test/regress/expected/rowsecurity.out | 56 +++--- src/test/regress/expected/select_parallel.out | 22 +-- src/test/regress/expected/subselect.out | 159 ++++++++++++------ src/test/regress/expected/updatable_views.out | 24 +-- src/test/regress/sql/subselect.sql | 14 ++ 12 files changed, 255 insertions(+), 141 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index c355e8f3f7..bf067738e3 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -3264,14 +3264,14 @@ select sum(c1) filter (where (c1 / c1) * random() <= 1) from ft1 group by c2 ord explain (verbose, costs off) select sum(c2) filter (where c2 in (select c2 from ft1 where c2 < 5)) from ft1; - QUERY PLAN -------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------------- Aggregate - Output: sum(ft1.c2) FILTER (WHERE (hashed SubPlan 1)) + Output: sum(ft1.c2) FILTER (WHERE (ANY (ft1.c2 = $0) FROM HASHED SubPlan 1 (returns $0))) -> Foreign Scan on public.ft1 Output: ft1.c2 Remote SQL: SELECT c2 FROM "S 1"."T 1" - SubPlan 1 + SubPlan 1 (returns $0) -> Foreign Scan on public.ft1 ft1_1 Output: ft1_1.c2 Remote SQL: SELECT c2 FROM "S 1"."T 1" WHERE ((c2 < 5)) @@ -11895,12 +11895,12 @@ CREATE FOREIGN TABLE foreign_tbl2 () INHERITS (foreign_tbl) SERVER loopback OPTIONS (table_name 'base_tbl'); EXPLAIN (VERBOSE, COSTS OFF) SELECT a FROM base_tbl WHERE (a, random() > 0) IN (SELECT a, random() > 0 FROM foreign_tbl); - QUERY PLAN ------------------------------------------------------------------------------ + QUERY PLAN +------------------------------------------------------------------------------------------------------------------ Seq Scan on public.base_tbl Output: base_tbl.a - Filter: (SubPlan 1) - SubPlan 1 + Filter: (ANY ((base_tbl.a = $1) AND ((random() > '0'::double precision) = $2)) FROM SubPlan 1 (returns $1,$2)) + SubPlan 1 (returns $1,$2) -> Result Output: base_tbl.a, (random() > '0'::double precision) -> Append diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 47e14723d2..d5919a9164 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -326,6 +326,7 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot, Node *result; SubPlan *splan; bool isInitPlan; + StringInfoData splanname; ListCell *lc; /* @@ -560,22 +561,31 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot, splan->plan_id); /* Label the subplan for EXPLAIN purposes */ - splan->plan_name = palloc(32 + 12 * list_length(splan->setParam)); - sprintf(splan->plan_name, "%s %d", - isInitPlan ? "InitPlan" : "SubPlan", - splan->plan_id); + initStringInfo(&splanname); + appendStringInfo(&splanname, "%s %d", + isInitPlan ? "InitPlan" : "SubPlan", + splan->plan_id); if (splan->setParam) { - char *ptr = splan->plan_name + strlen(splan->plan_name); - - ptr += sprintf(ptr, " (returns "); + appendStringInfoString(&splanname, " (returns "); foreach(lc, splan->setParam) { - ptr += sprintf(ptr, "$%d%s", - lfirst_int(lc), - lnext(splan->setParam, lc) ? "," : ")"); + appendStringInfo(&splanname, "$%d%s", + lfirst_int(lc), + lnext(splan->setParam, lc) ? "," : ")"); + } + } + else if (splan->paramIds) + { + appendStringInfoString(&splanname, " (returns "); + foreach(lc, splan->paramIds) + { + appendStringInfo(&splanname, "$%d%s", + lfirst_int(lc), + lnext(splan->paramIds, lc) ? "," : ")"); } } + splan->plan_name = splanname.data; /* Lastly, fill in the cost estimates for use later */ cost_subplan(root, splan, plan); diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index a928a8c55d..255498a58d 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -8869,12 +8869,51 @@ get_rule_expr(Node *node, deparse_context *context, * We cannot see an already-planned subplan in rule deparsing, * only while EXPLAINing a query plan. We don't try to * reconstruct the original SQL, just reference the subplan - * that appears elsewhere in EXPLAIN's result. + * that appears elsewhere in EXPLAIN's result. It does seem + * useful to show the subLinkType and testexpr, however, and + * we also note whether the subplan will be hashed. */ + switch (subplan->subLinkType) + { + case EXISTS_SUBLINK: + appendStringInfoString(buf, "EXISTS("); + Assert(subplan->testexpr == NULL); + break; + case ALL_SUBLINK: + appendStringInfoString(buf, "(ALL "); + get_rule_expr(subplan->testexpr, context, showimplicit); + appendStringInfoString(buf, " FROM "); + break; + case ANY_SUBLINK: + appendStringInfoString(buf, "(ANY "); + get_rule_expr(subplan->testexpr, context, showimplicit); + appendStringInfoString(buf, " FROM "); + break; + case ROWCOMPARE_SUBLINK: + appendStringInfoString(buf, "(ROWCOMPARE "); + get_rule_expr(subplan->testexpr, context, showimplicit); + appendStringInfoString(buf, " FROM "); + break; + case EXPR_SUBLINK: + case MULTIEXPR_SUBLINK: + /* No need to decorate these subplan references */ + appendStringInfoString(buf, "("); + Assert(subplan->testexpr == NULL); + break; + case ARRAY_SUBLINK: + appendStringInfoString(buf, "ARRAY("); + Assert(subplan->testexpr == NULL); + break; + case CTE_SUBLINK: + /* This case is unreachable within expressions */ + appendStringInfoString(buf, "CTE("); + Assert(subplan->testexpr == NULL); + break; + } if (subplan->useHashTable) - appendStringInfo(buf, "(hashed %s)", subplan->plan_name); + appendStringInfo(buf, "HASHED %s)", subplan->plan_name); else - appendStringInfo(buf, "(%s)", subplan->plan_name); + appendStringInfo(buf, "%s)", subplan->plan_name); } break; diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index f736bab67e..56f6154ffc 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -738,7 +738,7 @@ select array(select sum(x+y) s QUERY PLAN ------------------------------------------------------------------- Function Scan on pg_catalog.generate_series x - Output: (SubPlan 1) + Output: ARRAY(SubPlan 1) Function Call: generate_series(1, 3) SubPlan 1 -> Sort diff --git a/src/test/regress/expected/insert_conflict.out b/src/test/regress/expected/insert_conflict.out index 563c5eb52a..701217ddbc 100644 --- a/src/test/regress/expected/insert_conflict.out +++ b/src/test/regress/expected/insert_conflict.out @@ -50,7 +50,7 @@ explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on con Insert on insertconflicttest Conflict Resolution: UPDATE Conflict Arbiter Indexes: op_index_key, collation_index_key, both_index_key - Conflict Filter: (SubPlan 1) + Conflict Filter: EXISTS(SubPlan 1) -> Result SubPlan 1 -> Index Only Scan using both_index_expr_key on insertconflicttest ii diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 0c2cba8921..6a7a6aa839 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -3028,11 +3028,11 @@ where unique1 in (select unique2 from tenk1 b); explain (costs off) select a.* from tenk1 a where unique1 not in (select unique2 from tenk1 b); - QUERY PLAN --------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------- Seq Scan on tenk1 a - Filter: (NOT (hashed SubPlan 1)) - SubPlan 1 + Filter: (NOT (ANY (unique1 = $0) FROM HASHED SubPlan 1 (returns $0))) + SubPlan 1 (returns $0) -> Index Only Scan using tenk1_unique2 on tenk1 b (4 rows) @@ -5278,13 +5278,13 @@ explain (costs off) select a.unique1, b.unique2 from onek a left join onek b on a.unique1 = b.unique2 where (b.unique2, random() > 0) = any (select q1, random() > 0 from int8_tbl c where c.q1 < b.unique1); - QUERY PLAN ----------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------- Hash Join Hash Cond: (b.unique2 = a.unique1) -> Seq Scan on onek b - Filter: (SubPlan 1) - SubPlan 1 + Filter: (ANY ((unique2 = $1) AND ((random() > '0'::double precision) = $2)) FROM SubPlan 1 (returns $1,$2)) + SubPlan 1 (returns $1,$2) -> Seq Scan on int8_tbl c Filter: (q1 < b.unique1) -> Hash @@ -8214,8 +8214,8 @@ lateral (select * from int8_tbl t1, where q2 = (select greatest(t1.q1,t2.q2)) and (select v.id=0)) offset 0) ss2) ss where t1.q1 = ss.q2) ss0; - QUERY PLAN -------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------- Nested Loop Output: "*VALUES*".column1, t1.q1, t1.q2, ss2.q1, ss2.q2 -> Seq Scan on public.int8_tbl t1 @@ -8229,8 +8229,8 @@ lateral (select * from int8_tbl t1, Filter: (t1.q1 = ss2.q2) -> Seq Scan on public.int8_tbl t2 Output: t2.q1, t2.q2 - Filter: (SubPlan 3) - SubPlan 3 + Filter: (ANY ((t2.q1 = $5) AND ((random() > '0'::double precision) = $6)) FROM SubPlan 3 (returns $5,$6)) + SubPlan 3 (returns $5,$6) -> Result Output: t3.q2, (random() > '0'::double precision) One-Time Filter: $4 diff --git a/src/test/regress/expected/memoize.out b/src/test/regress/expected/memoize.out index cf6886a288..8fb24063b6 100644 --- a/src/test/regress/expected/memoize.out +++ b/src/test/regress/expected/memoize.out @@ -313,7 +313,7 @@ WHERE unique1 < 3 ---------------------------------------------------------------- Index Scan using tenk1_unique1 on tenk1 t0 Index Cond: (unique1 < 3) - Filter: (SubPlan 1) + Filter: EXISTS(SubPlan 1) SubPlan 1 -> Nested Loop -> Index Scan using tenk1_hundred on tenk1 t2 diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index 6988128aa4..3f3815a9cf 100644 --- a/src/test/regress/expected/rowsecurity.out +++ b/src/test/regress/expected/rowsecurity.out @@ -1437,11 +1437,11 @@ NOTICE: f_leak => 03b26944890929ff751653acb2f2af79 (1 row) EXPLAIN (COSTS OFF) SELECT * FROM only s1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------------------ + QUERY PLAN +----------------------------------------------------------------------------- Seq Scan on s1 - Filter: ((hashed SubPlan 1) AND f_leak(b)) - SubPlan 1 + Filter: ((ANY (a = $0) FROM HASHED SubPlan 1 (returns $0)) AND f_leak(b)) + SubPlan 1 (returns $0) -> Seq Scan on s2 Filter: (((x % 2) = 0) AND (y ~~ '%2f%'::text)) (5 rows) @@ -1457,11 +1457,11 @@ NOTICE: f_leak => 03b26944890929ff751653acb2f2af79 (1 row) EXPLAIN (COSTS OFF) SELECT * FROM s1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------------------ + QUERY PLAN +----------------------------------------------------------------------------- Seq Scan on s1 - Filter: ((hashed SubPlan 1) AND f_leak(b)) - SubPlan 1 + Filter: ((ANY (a = $0) FROM HASHED SubPlan 1 (returns $0)) AND f_leak(b)) + SubPlan 1 (returns $0) -> Seq Scan on s2 Filter: (((x % 2) = 0) AND (y ~~ '%af%'::text)) (5 rows) @@ -1473,15 +1473,15 @@ SELECT (SELECT x FROM s1 LIMIT 1) xx, * FROM s2 WHERE y like '%28%'; (1 row) EXPLAIN (COSTS OFF) SELECT (SELECT x FROM s1 LIMIT 1) xx, * FROM s2 WHERE y like '%28%'; - QUERY PLAN -------------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------- Seq Scan on s2 Filter: (((x % 2) = 0) AND (y ~~ '%28%'::text)) SubPlan 2 -> Limit -> Seq Scan on s1 - Filter: (hashed SubPlan 1) - SubPlan 1 + Filter: (ANY (a = $1) FROM HASHED SubPlan 1 (returns $1)) + SubPlan 1 (returns $1) -> Seq Scan on s2 s2_1 Filter: (((x % 2) = 0) AND (y ~~ '%af%'::text)) (9 rows) @@ -2687,11 +2687,11 @@ NOTICE: f_leak => bbb (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 0) AND f_leak(b)) - SubPlan 1 + Filter: ((NOT (ANY (a = $0) FROM HASHED SubPlan 1 (returns $0))) AND ((a % 2) = 0) AND f_leak(b)) + SubPlan 1 (returns $0) -> Seq Scan on z1_blacklist (4 rows) @@ -2705,11 +2705,11 @@ NOTICE: f_leak => bbb (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 0) AND f_leak(b)) - SubPlan 1 + Filter: ((NOT (ANY (a = $0) FROM HASHED SubPlan 1 (returns $0))) AND ((a % 2) = 0) AND f_leak(b)) + SubPlan 1 (returns $0) -> Seq Scan on z1_blacklist (4 rows) @@ -2877,11 +2877,11 @@ NOTICE: f_leak => bbb (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 0) AND f_leak(b)) - SubPlan 1 + Filter: ((NOT (ANY (a = $0) FROM HASHED SubPlan 1 (returns $0))) AND ((a % 2) = 0) AND f_leak(b)) + SubPlan 1 (returns $0) -> Seq Scan on z1_blacklist (4 rows) @@ -2903,11 +2903,11 @@ NOTICE: f_leak => aba (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 1) AND f_leak(b)) - SubPlan 1 + Filter: ((NOT (ANY (a = $0) FROM HASHED SubPlan 1 (returns $0))) AND ((a % 2) = 1) AND f_leak(b)) + SubPlan 1 (returns $0) -> Seq Scan on z1_blacklist (4 rows) diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out index 7a0d78dfe3..f8dd144c7e 100644 --- a/src/test/regress/expected/select_parallel.out +++ b/src/test/regress/expected/select_parallel.out @@ -291,15 +291,15 @@ alter table tenk2 set (parallel_workers = 0); explain (costs off) select count(*) from tenk1 where (two, four) not in (select hundred, thousand from tenk2 where thousand > 100); - QUERY PLAN ------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------ Finalize Aggregate -> Gather Workers Planned: 4 -> Partial Aggregate -> Parallel Seq Scan on tenk1 - Filter: (NOT (hashed SubPlan 1)) - SubPlan 1 + Filter: (NOT (ANY ((two = $0) AND (four = $1)) FROM HASHED SubPlan 1 (returns $0,$1))) + SubPlan 1 (returns $0,$1) -> Seq Scan on tenk2 Filter: (thousand > 100) (9 rows) @@ -315,11 +315,11 @@ select count(*) from tenk1 where (two, four) not in explain (costs off) select * from tenk1 where (unique1 + random())::integer not in (select ten from tenk2); - QUERY PLAN ------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------- Seq Scan on tenk1 - Filter: (NOT (hashed SubPlan 1)) - SubPlan 1 + Filter: (NOT (ANY ((((unique1)::double precision + random()))::integer = $0) FROM HASHED SubPlan 1 (returns $0))) + SubPlan 1 (returns $0) -> Seq Scan on tenk2 (4 rows) @@ -1182,10 +1182,10 @@ ORDER BY 1, 2, 3; EXPLAIN (VERBOSE, COSTS OFF) SELECT generate_series(1, two), array(select generate_series(1, two)) FROM tenk1 ORDER BY tenthous; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------- ProjectSet - Output: generate_series(1, tenk1.two), (SubPlan 1), tenk1.tenthous + Output: generate_series(1, tenk1.two), ARRAY(SubPlan 1), tenk1.tenthous -> Gather Merge Output: tenk1.two, tenk1.tenthous Workers Planned: 4 diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out index e41b728df8..fb4e2a4d53 100644 --- a/src/test/regress/expected/subselect.out +++ b/src/test/regress/expected/subselect.out @@ -202,6 +202,57 @@ SELECT f1 AS "Correlated Field" 3 (5 rows) +-- Check ROWCOMPARE cases, both correlated and not +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + QUERY PLAN +------------------------------------------------------------------------------- + Seq Scan on public.subselect_tbl + Output: (ROWCOMPARE ((1 = $2) AND (2 = $3)) FROM SubPlan 1 (returns $2,$3)) + SubPlan 1 (returns $2,$3) + -> Result + Output: subselect_tbl.f1, subselect_tbl.f2 +(5 rows) + +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + eq +---- + t + f + f + f + f + f + f + f +(8 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + QUERY PLAN +----------------------------------- + Seq Scan on public.subselect_tbl + Output: ((1 = $0) AND (2 = $1)) + InitPlan 1 (returns $0,$1) + -> Result + Output: 3, 4 +(5 rows) + +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + eq +---- + f + f + f + f + f + f + f + f +(8 rows) + +SELECT ROW(1, 2) = (SELECT f1, f2 FROM SUBSELECT_TBL); -- error +ERROR: more than one row returned by a subquery used as an expression -- Subselects without aliases SELECT count FROM (SELECT COUNT(DISTINCT name) FROM road); count @@ -324,11 +375,11 @@ explain (verbose, costs off) select '42' union all select 43; -- check materialization of an initplan reference (bug #14524) explain (verbose, costs off) select 1 = all (select (select 1)); - QUERY PLAN ------------------------------------ + QUERY PLAN +------------------------------------------------------ Result - Output: (SubPlan 2) - SubPlan 2 + Output: (ALL (1 = $1) FROM SubPlan 2 (returns $1)) + SubPlan 2 (returns $1) -> Materialize Output: ($0) InitPlan 1 (returns $0) @@ -377,7 +428,7 @@ select * from int4_tbl o where exists QUERY PLAN -------------------------------------- Seq Scan on int4_tbl o - Filter: (SubPlan 1) + Filter: EXISTS(SubPlan 1) SubPlan 1 -> Limit -> Seq Scan on int4_tbl i @@ -840,11 +891,11 @@ select * from outer_text where (f1, f2) not in (select * from inner_text); -- explain (verbose, costs off) select 'foo'::text in (select 'bar'::name union all select 'bar'::name); - QUERY PLAN -------------------------------------- + QUERY PLAN +----------------------------------------------------------------------- Result - Output: (hashed SubPlan 1) - SubPlan 1 + Output: (ANY ('foo'::text = $0) FROM HASHED SubPlan 1 (returns $0)) + SubPlan 1 (returns $0) -> Append -> Result Output: 'bar'::name @@ -864,11 +915,11 @@ select 'foo'::text in (select 'bar'::name union all select 'bar'::name); -- explain (verbose, costs off) select row(row(row(1))) = any (select row(row(1))); - QUERY PLAN -------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------- Result - Output: (SubPlan 1) - SubPlan 1 + Output: (ANY ('("(1)")'::record = $0) FROM SubPlan 1 (returns $0)) + SubPlan 1 (returns $0) -> Materialize Output: '("(1)")'::record -> Result @@ -907,11 +958,11 @@ language sql as 'select $1::text = $2'; create operator = (procedure=bogus_int8_text_eq, leftarg=int8, rightarg=text); explain (costs off) select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------- + QUERY PLAN +---------------------------------------------------------------------- Seq Scan on int8_tbl - Filter: (hashed SubPlan 1) - SubPlan 1 + Filter: (ANY ((q1)::text = $0) FROM HASHED SubPlan 1 (returns $0)) + SubPlan 1 (returns $0) -> Seq Scan on inner_text (4 rows) @@ -928,11 +979,11 @@ create or replace function bogus_int8_text_eq(int8, text) returns boolean language sql as 'select $1::text = $2 and $1::text = $2'; explain (costs off) select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------------- Seq Scan on int8_tbl - Filter: (hashed SubPlan 1) - SubPlan 1 + Filter: (ANY (((q1)::text = $0) AND ((q1)::text = $0)) FROM HASHED SubPlan 1 (returns $0)) + SubPlan 1 (returns $0) -> Seq Scan on inner_text (4 rows) @@ -949,11 +1000,11 @@ create or replace function bogus_int8_text_eq(int8, text) returns boolean language sql as 'select $2 = $1::text'; explain (costs off) select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------------- + QUERY PLAN +--------------------------------------------------------------- Seq Scan on int8_tbl - Filter: (SubPlan 1) - SubPlan 1 + Filter: (ANY ($0 = (q1)::text) FROM SubPlan 1 (returns $0)) + SubPlan 1 (returns $0) -> Materialize -> Seq Scan on inner_text (5 rows) @@ -972,12 +1023,12 @@ rollback; -- to get rid of the bogus operator explain (costs off) select count(*) from tenk1 t where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0); - QUERY PLAN --------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------- Aggregate -> Seq Scan on tenk1 t - Filter: ((hashed SubPlan 2) OR (ten < 0)) - SubPlan 2 + Filter: ((ANY (unique2 = $1) FROM HASHED SubPlan 2 (returns $1)) OR (ten < 0)) + SubPlan 2 (returns $1) -> Index Only Scan using tenk1_unique1 on tenk1 k (5 rows) @@ -997,7 +1048,7 @@ where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0) Aggregate -> Bitmap Heap Scan on tenk1 t Recheck Cond: (thousand = 1) - Filter: ((SubPlan 1) OR (ten < 0)) + Filter: (EXISTS(SubPlan 1) OR (ten < 0)) -> Bitmap Index Scan on tenk1_thous_tenthous Index Cond: (thousand = 1) SubPlan 1 @@ -1022,11 +1073,11 @@ analyze exists_tbl; explain (costs off) select * from exists_tbl t1 where (exists(select 1 from exists_tbl t2 where t1.c1 = t2.c2) or c3 < 0); - QUERY PLAN ------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------- Append -> Seq Scan on exists_tbl_null t1_1 - Filter: ((SubPlan 1) OR (c3 < 0)) + Filter: (EXISTS(SubPlan 1) OR (c3 < 0)) SubPlan 1 -> Append -> Seq Scan on exists_tbl_null t2_1 @@ -1034,8 +1085,8 @@ select * from exists_tbl t1 -> Seq Scan on exists_tbl_def t2_2 Filter: (t1_1.c1 = c2) -> Seq Scan on exists_tbl_def t1_2 - Filter: ((hashed SubPlan 2) OR (c3 < 0)) - SubPlan 2 + Filter: ((ANY (c1 = $1) FROM HASHED SubPlan 2 (returns $1)) OR (c3 < 0)) + SubPlan 2 (returns $1) -> Append -> Seq Scan on exists_tbl_null t2_4 -> Seq Scan on exists_tbl_def t2_5 @@ -1143,16 +1194,16 @@ where o.ten = 0; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Aggregate - Output: sum((((hashed SubPlan 1)))::integer) + Output: sum((((ANY (i.ten = $1) FROM HASHED SubPlan 1 (returns $1))))::integer) -> Nested Loop - Output: ((hashed SubPlan 1)) + Output: ((ANY (i.ten = $1) FROM HASHED SubPlan 1 (returns $1))) -> Seq Scan on public.onek o Output: o.unique1, o.unique2, o.two, o.four, o.ten, o.twenty, o.hundred, o.thousand, o.twothousand, o.fivethous,o.tenthous, o.odd, o.even, o.stringu1, o.stringu2, o.string4 Filter: (o.ten = 0) -> Index Scan using onek_unique1 on public.onek i - Output: (hashed SubPlan 1), random() + Output: (ANY (i.ten = $1) FROM HASHED SubPlan 1 (returns $1)), random() Index Cond: (i.unique1 = o.unique1) - SubPlan 1 + SubPlan 1 (returns $1) -> Seq Scan on public.int4_tbl Output: int4_tbl.f1 Filter: (int4_tbl.f1 <= o.hundred) @@ -1319,7 +1370,7 @@ select * from ---------------------------------------- Values Scan on "*VALUES*" Output: "*VALUES*".column1 - SubPlan 1 + SubPlan 1 (returns $0) -> Values Scan on "*VALUES*_1" Output: "*VALUES*_1".column1 (5 rows) @@ -1346,12 +1397,12 @@ select * from int4_tbl where --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Nested Loop Semi Join Output: int4_tbl.f1 - Join Filter: (CASE WHEN (hashed SubPlan 1) THEN int4_tbl.f1 ELSE NULL::integer END = b.ten) + Join Filter: (CASE WHEN (ANY (int4_tbl.f1 = $0) FROM HASHED SubPlan 1 (returns $0)) THEN int4_tbl.f1 ELSE NULL::integerEND = b.ten) -> Seq Scan on public.int4_tbl Output: int4_tbl.f1 -> Seq Scan on public.tenk1 b Output: b.unique1, b.unique2, b.two, b.four, b.ten, b.twenty, b.hundred, b.thousand, b.twothousand, b.fivethous,b.tenthous, b.odd, b.even, b.stringu1, b.stringu2, b.string4 - SubPlan 1 + SubPlan 1 (returns $0) -> Index Only Scan using tenk1_unique1 on public.tenk1 a Output: a.unique1 (10 rows) @@ -1945,14 +1996,14 @@ select * from tenk1 A where exists (select 1 from tenk2 B where A.hundred in (select C.hundred FROM tenk2 C WHERE c.odd = b.odd)); - QUERY PLAN ---------------------------------- + QUERY PLAN +------------------------------------------------------------------- Nested Loop Semi Join - Join Filter: (SubPlan 1) + Join Filter: (ANY (a.hundred = $1) FROM SubPlan 1 (returns $1)) -> Seq Scan on tenk1 a -> Materialize -> Seq Scan on tenk2 b - SubPlan 1 + SubPlan 1 (returns $1) -> Seq Scan on tenk2 c Filter: (odd = b.odd) (8 rows) @@ -1962,14 +2013,14 @@ WHERE c.odd = b.odd)); explain (costs off) SELECT * FROM tenk1 A LEFT JOIN tenk2 B ON A.hundred in (SELECT c.hundred FROM tenk2 C WHERE c.odd = b.odd); - QUERY PLAN ---------------------------------- + QUERY PLAN +------------------------------------------------------------------- Nested Loop Left Join - Join Filter: (SubPlan 1) + Join Filter: (ANY (a.hundred = $1) FROM SubPlan 1 (returns $1)) -> Seq Scan on tenk1 a -> Materialize -> Seq Scan on tenk2 b - SubPlan 1 + SubPlan 1 (returns $1) -> Seq Scan on tenk2 c Filter: (odd = b.odd) (8 rows) @@ -1979,14 +2030,14 @@ ON A.hundred in (SELECT c.hundred FROM tenk2 C WHERE c.odd = b.odd); explain (costs off) SELECT * FROM tenk1 A LEFT JOIN tenk2 B ON B.hundred in (SELECT c.hundred FROM tenk2 C WHERE c.odd = a.odd); - QUERY PLAN ---------------------------------- + QUERY PLAN +------------------------------------------------------------------- Nested Loop Left Join - Join Filter: (SubPlan 1) + Join Filter: (ANY (b.hundred = $1) FROM SubPlan 1 (returns $1)) -> Seq Scan on tenk1 a -> Materialize -> Seq Scan on tenk2 b - SubPlan 1 + SubPlan 1 (returns $1) -> Seq Scan on tenk2 c Filter: (odd = a.odd) (8 rows) diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index 1950e6f281..86f09b6284 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -2586,8 +2586,8 @@ 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 t1_1 Update on public.t11 t1_2 @@ -2599,7 +2599,7 @@ UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; -> 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)) + Filter: ((t1_1.a <> 6) AND EXISTS(SubPlan 1) AND snoop(t1_1.a) AND leakproof(t1_1.a)) SubPlan 1 -> Append -> Seq Scan on public.t12 t12_1 @@ -2609,15 +2609,15 @@ UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; -> 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)) + Filter: ((t1_2.a <> 6) AND EXISTS(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)) + Filter: ((t1_3.a <> 6) AND EXISTS(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)) + Filter: ((t1_4.a <> 6) AND EXISTS(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; @@ -2633,8 +2633,8 @@ 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 t1_1 Update on public.t11 t1_2 @@ -2646,7 +2646,7 @@ UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; -> 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)) + Filter: (EXISTS(SubPlan 1) AND snoop(t1_1.a) AND leakproof(t1_1.a)) SubPlan 1 -> Append -> Seq Scan on public.t12 t12_1 @@ -2656,15 +2656,15 @@ UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; -> 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)) + Filter: (EXISTS(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)) + Filter: (EXISTS(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)) + Filter: (EXISTS(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; diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql index 2f3601a058..7c42ebc36f 100644 --- a/src/test/regress/sql/subselect.sql +++ b/src/test/regress/sql/subselect.sql @@ -82,6 +82,20 @@ SELECT f1 AS "Correlated Field" WHERE (f1, f2) IN (SELECT f2, CAST(f3 AS int4) FROM SUBSELECT_TBL WHERE f3 IS NOT NULL); +-- Check ROWCOMPARE cases, both correlated and not + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + +SELECT ROW(1, 2) = (SELECT f1, f2 FROM SUBSELECT_TBL); -- error + -- Subselects without aliases SELECT count FROM (SELECT COUNT(DISTINCT name) FROM road); -- 2.39.3
> On 17 Feb 2024, at 00:38, Tom Lane <tgl@sss.pgh.pa.us> wrote: > > Here's a rebase over 9f1337639 --- no code changes, but this affects > some of the new or changed expected outputs from that commit. Aleksander, as long as your was reviewing this previously, I’ve added you to reviewers of this CF entry [0]. Please, pingme or remove yourself, it it’s actually not a case. BTW, as long as there’s a new version and some input from Tom, would you be willing to post fleshier review? Thanks for working on this! Best regards, Andrey Borodin. [0] https://commitfest.postgresql.org/47/4782/
On Fri, 16 Feb 2024 at 19:39, Tom Lane <tgl@sss.pgh.pa.us> wrote: > > > So now I'm thinking that we do have enough detail in the present > > proposal, and we just need to think about whether there's some > > nicer way to present it than the particular spelling I used here. > One thing that concerns me about making even greater use of "$n" is the potential for confusion with generic plan parameters. Maybe it's always possible to work out which is which from context, but still it looks messy: drop table if exists foo; create table foo(id int, x int, y int); explain (verbose, costs off, generic_plan) select row($3,$4) = (select x,y from foo where id=y) and row($1,$2) = (select min(x+y),max(x+y) from generate_series(1,3) x) from generate_series(1,3) y; QUERY PLAN --------------------------------------------------------------------------------------------------------------- Function Scan on pg_catalog.generate_series y Output: (($3 = $0) AND ($4 = $1) AND (ROWCOMPARE (($1 = $3) AND ($2 = $4)) FROM SubPlan 2 (returns $3,$4))) Function Call: generate_series(1, 3) InitPlan 1 (returns $0,$1) -> Seq Scan on public.foo Output: foo.x, foo.y Filter: (foo.id = foo.y) SubPlan 2 (returns $3,$4) -> Aggregate Output: min((x.x + y.y)), max((x.x + y.y)) -> Function Scan on pg_catalog.generate_series x Output: x.x Function Call: generate_series(1, 3) Another odd thing about that is the inconsistency between how the SubPlan and InitPlan expressions are displayed. I think "ROWCOMPARE" is really just an internal detail that could be omitted without losing anything. But the "FROM SubPlan ..." is useful to work out where it's coming from. Should it also output "FROM InitPlan ..."? I think that would risk making it harder to read. Another possibility is to put the SubPlan and InitPlan names inline, rather than outputting "FROM SubPlan ...". I had a go at hacking that up and this was the result: QUERY PLAN ----------------------------------------------------------------------------------------------------------------------- Function Scan on pg_catalog.generate_series y Output: (($3 = (InitPlan 1).$0) AND ($4 = (InitPlan 1).$1) AND ((($1 = (SubPlan 2).$3) AND ($2 = (SubPlan 2).$4)))) Function Call: generate_series(1, 3) InitPlan 1 (returns $0,$1) -> Seq Scan on public.foo Output: foo.x, foo.y Filter: (foo.id = foo.y) SubPlan 2 (returns $3,$4) -> Aggregate Output: min((x.x + y.y)), max((x.x + y.y)) -> Function Scan on pg_catalog.generate_series x Output: x.x Function Call: generate_series(1, 3) It's a little more verbose in this case, but in a lot of other cases it ended up being more compact. The code is a bit messy, but I think the regression test output (attached) is clearer and easier to interpret. SubPlans and InitPlans are displayed consistently, and it's easier to distinguish SubPlan/InitPlan outputs from external parameters. There are a few more regression test changes, corresponding to cases where InitPlans are referenced, such as: Seq Scan on document - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).$0) AND f_leak(dtitle)) InitPlan 1 (returns $0) -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) but I think that's useful extra clarification. Regards, Dean
Attachment
Dean Rasheed <dean.a.rasheed@gmail.com> writes: > One thing that concerns me about making even greater use of "$n" is > the potential for confusion with generic plan parameters. True. > Another possibility is to put the SubPlan and InitPlan names inline, > rather than outputting "FROM SubPlan ...". I had a go at hacking that > up and this was the result: > Output: (($3 = (InitPlan 1).$0) AND ($4 = (InitPlan 1).$1) AND > ((($1 = (SubPlan 2).$3) AND ($2 = (SubPlan 2).$4)))) Hmm. I guess what bothers me about that is that it could be read to suggest that the initplan or subplan is evaluated again for each output parameter. Perhaps it'll be sufficiently clear as long as we keep the labeling > InitPlan 1 (returns $0,$1) > SubPlan 2 (returns $3,$4) but I'm not sure. Anybody else have an opinion? (I didn't read your changes to the code yet --- I think at this point we can just debate proposed output without worrying about how to implement it.) regards, tom lane
I wrote: > Dean Rasheed <dean.a.rasheed@gmail.com> writes: >> One thing that concerns me about making even greater use of "$n" is >> the potential for confusion with generic plan parameters. > True. After looking at your draft some more, it occurred to me that we're not that far from getting rid of showing "$n" entirely in this context. Instead of (subplan_name).$n, we could write something like (subplan_name).colN or (subplan_name).columnN or (subplan_name).fN, depending on your taste for verboseness. "fN" would correspond to the names we assign to columns of anonymous record types, but it hasn't got much else to recommend it. In the attached I used "colN"; "columnN" would be my next choice. You could also imagine trying to use the sub-SELECT's actual output column names, but I fear that would be ambiguous --- too often it'd be "?column?" or some other non-unique name. > Hmm. I guess what bothers me about that is that it could be read to > suggest that the initplan or subplan is evaluated again for each > output parameter. This objection seems like it could be solved through documentation, so I wrote some. The attached proof-of-concept is incomplete: it fails to replace some $n occurrences with subplan references, as is visible in some of the test cases. I believe your quick hack in get_parameter() is not correct in detail, but for the moment I didn't bother to debug it. I'm just presenting this as a POC to see if this is the direction people would like to go in. If there's not objections, I'll try to make a bulletproof implementation. regards, tom lane diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 58a603ac56..585ba60ae3 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -3062,10 +3062,10 @@ select exists(select 1 from pg_enum), sum(c1) from ft1; QUERY PLAN -------------------------------------------------- Foreign Scan - Output: $0, (sum(ft1.c1)) + Output: (InitPlan 1).col1, (sum(ft1.c1)) Relations: Aggregate on (public.ft1) Remote SQL: SELECT sum("C 1") FROM "S 1"."T 1" - InitPlan 1 (returns $0) + InitPlan 1 -> Seq Scan on pg_catalog.pg_enum (6 rows) @@ -3080,8 +3080,8 @@ select exists(select 1 from pg_enum), sum(c1) from ft1 group by 1; QUERY PLAN --------------------------------------------------- GroupAggregate - Output: $0, sum(ft1.c1) - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1, sum(ft1.c1) + InitPlan 1 -> Seq Scan on pg_catalog.pg_enum -> Foreign Scan on public.ft1 Output: ft1.c1 @@ -3305,10 +3305,10 @@ select sum(c1) filter (where (c1 / c1) * random() <= 1) from ft1 group by c2 ord explain (verbose, costs off) select sum(c2) filter (where c2 in (select c2 from ft1 where c2 < 5)) from ft1; - QUERY PLAN -------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------- Aggregate - Output: sum(ft1.c2) FILTER (WHERE (hashed SubPlan 1)) + Output: sum(ft1.c2) FILTER (WHERE (ANY (ft1.c2 = (hashed SubPlan 1).col1))) -> Foreign Scan on public.ft1 Output: ft1.c2 Remote SQL: SELECT c2 FROM "S 1"."T 1" @@ -6171,9 +6171,9 @@ UPDATE ft2 AS target SET (c2, c7) = ( Update on public.ft2 target Remote SQL: UPDATE "S 1"."T 1" SET c2 = $2, c7 = $3 WHERE ctid = $1 -> Foreign Scan on public.ft2 target - Output: $1, $2, (SubPlan 1 (returns $1,$2)), target.ctid, target.* + Output: $1, $2, (SubPlan 1), target.ctid, target.* Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8, ctid FROM "S 1"."T 1" WHERE (("C 1" > 1100)) FOR UPDATE - SubPlan 1 (returns $1,$2) + SubPlan 1 -> Foreign Scan on public.ft2 src Output: (src.c2 * 10), src.c7 Remote SQL: SELECT c2, c7 FROM "S 1"."T 1" WHERE (($1::integer = "C 1")) @@ -11687,7 +11687,7 @@ SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt W Nested Loop Left Join Output: t1.a, t1.b, t1.c, async_pt.a, async_pt.b, async_pt.c, ($0) Join Filter: (t1.a = async_pt.a) - InitPlan 1 (returns $0) + InitPlan 1 -> Aggregate Output: count(*) -> Append @@ -11713,7 +11713,7 @@ SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt W Nested Loop Left Join (actual rows=1 loops=1) Join Filter: (t1.a = async_pt.a) Rows Removed by Join Filter: 399 - InitPlan 1 (returns $0) + InitPlan 1 -> Aggregate (actual rows=1 loops=1) -> Append (actual rows=400 loops=1) -> Async Foreign Scan on async_p1 async_pt_4 (actual rows=200 loops=1) @@ -11936,11 +11936,11 @@ CREATE FOREIGN TABLE foreign_tbl2 () INHERITS (foreign_tbl) SERVER loopback OPTIONS (table_name 'base_tbl'); EXPLAIN (VERBOSE, COSTS OFF) SELECT a FROM base_tbl WHERE (a, random() > 0) IN (SELECT a, random() > 0 FROM foreign_tbl); - QUERY PLAN ------------------------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- Seq Scan on public.base_tbl Output: base_tbl.a - Filter: (SubPlan 1) + Filter: (ANY ((base_tbl.a = (SubPlan 1).col1) AND ((random() > '0'::double precision) = (SubPlan 1).col2))) SubPlan 1 -> Result Output: base_tbl.a, (random() > '0'::double precision) diff --git a/doc/src/sgml/perform.sgml b/doc/src/sgml/perform.sgml index 90822b3f4c..a3ed0aff1f 100644 --- a/doc/src/sgml/perform.sgml +++ b/doc/src/sgml/perform.sgml @@ -573,8 +573,70 @@ WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2; which shows that the planner thinks that sorting <literal>onek</literal> by index-scanning is about 12% more expensive than sequential-scan-and-sort. Of course, the next question is whether it's right about that. - We can investigate that using <command>EXPLAIN ANALYZE</command>, as discussed - below. + We can investigate that using <command>EXPLAIN ANALYZE</command>, as + discussed <link linkend="using-explain-analyze">below</link>. + </para> + + <para> + Some query plans involve <firstterm>subplans</firstterm>, which arise + from sub-<literal>SELECT</literal>s in the original query. Such + queries can sometimes be transformed into ordinary join plans, but + when they cannot be, we get plans like: + +<screen> +EXPLAIN VERBOSE SELECT unique1 +FROM tenk1 t1 WHERE ROW(1, 2) = (SELECT t1.ten, t1.four); + + QUERY PLAN +------------------------------------------------------------&zwsp;-------- + Seq Scan on public.tenk1 t1 (cost=0.00..595.00 rows=5000 width=4) + Output: t1.unique1 + Filter: (((1 = (SubPlan 1).col1) AND (2 = (SubPlan 1).col2))) + SubPlan 1 + -> Result (cost=0.00..0.01 rows=1 width=8) + Output: t1.ten, t1.four +</screen> + + This simple example is artificial, of course; it could easily be + rewritten to not use a sub-<literal>SELECT</literal>. But it serves + to illustrate a couple of points: values from the outer plan level can + be passed down into a subplan (here, <literal>t1.ten</literal> + and <literal>t1.four</literal> are passed down) and the results of the + sub-select are available to the outer plan. Those result values are + shown by <command>EXPLAIN</command> with notations like + <literal>(<replaceable>subplan_name</replaceable>).col<replaceable>N</replaceable></literal>, + which refers to the <replaceable>N</replaceable>'th output column of + the sub-<literal>SELECT</literal>. + </para> + + <para> + <indexterm> + <primary>initplan</primary> + </indexterm> + A subplan like the above is run once for each row processed by the + outer plan. However, if the sub-<literal>SELECT</literal> does not + reference any variables of the outer query, it may instead be + implemented as an <firstterm>initplan</firstterm>: + +<screen> +EXPLAIN VERBOSE SELECT unique1 +FROM tenk1 t1 WHERE t1.ten = (SELECT (random() * 10)::integer); + + QUERY PLAN +------------------------------------------------------------&zwsp;-------- + Seq Scan on public.tenk1 t1 (cost=0.02..470.02 rows=1000 width=4) + Output: t1.unique1 + Filter: (t1.ten = (InitPlan 1).col1) + InitPlan 1 + -> Result (cost=0.00..0.02 rows=1 width=4) + Output: ((random() * '10'::double precision))::integer +</screen> + + An initplan is run only once per execution of the outer plan, so in + this example <literal>random()</literal> is evaluated only once and + all the values of <literal>t1.ten</literal> are compared to the same + randomly-chosen integer. That's quite different from what would + happen without the sub-<literal>SELECT</literal> construct. </para> </sect2> diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 47e14723d2..d4360c5659 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -560,22 +560,9 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot, splan->plan_id); /* Label the subplan for EXPLAIN purposes */ - splan->plan_name = palloc(32 + 12 * list_length(splan->setParam)); - sprintf(splan->plan_name, "%s %d", - isInitPlan ? "InitPlan" : "SubPlan", - splan->plan_id); - if (splan->setParam) - { - char *ptr = splan->plan_name + strlen(splan->plan_name); - - ptr += sprintf(ptr, " (returns "); - foreach(lc, splan->setParam) - { - ptr += sprintf(ptr, "$%d%s", - lfirst_int(lc), - lnext(splan->setParam, lc) ? "," : ")"); - } - } + splan->plan_name = psprintf("%s %d", + isInitPlan ? "InitPlan" : "SubPlan", + splan->plan_id); /* Lastly, fill in the cost estimates for use later */ cost_subplan(root, splan, plan); @@ -3040,8 +3027,7 @@ SS_make_initplan_from_plan(PlannerInfo *root, node = makeNode(SubPlan); node->subLinkType = EXPR_SUBLINK; node->plan_id = list_length(root->glob->subplans); - node->plan_name = psprintf("InitPlan %d (returns $%d)", - node->plan_id, prm->paramid); + node->plan_name = psprintf("InitPlan %d", node->plan_id); get_first_col_type(plan, &node->firstColType, &node->firstColTypmod, &node->firstColCollation); node->parallel_safe = plan->parallel_safe; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 2a1ee69970..3fc9d2ca0d 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -171,6 +171,7 @@ typedef struct List *using_names; /* List of assigned names for USING columns */ /* Remaining fields are used only when deparsing a Plan tree: */ Plan *plan; /* immediate parent of current expression */ + SubPlan *in_subplan; /* subplan containing current expression */ List *ancestors; /* ancestors of plan */ Plan *outer_plan; /* outer subnode, or NULL if none */ Plan *inner_plan; /* inner subnode, or NULL if none */ @@ -8184,6 +8185,84 @@ get_parameter(Param *param, deparse_context *context) return; } + /* Is it a subplan output? */ + if (param->paramkind == PARAM_EXEC) + { + SubPlan *subplan = NULL; + int colno = 0; + + dpns = (deparse_namespace *) linitial(context->namespaces); + + /* subplan containing this expression? */ + if (dpns->in_subplan) + { + if (dpns->in_subplan->setParam) + { + foreach_int(paramid, dpns->in_subplan->setParam) + { + if (paramid == param->paramid) + { + subplan = dpns->in_subplan; + colno = foreach_current_index(paramid); + break; + } + } + } + else if (dpns->in_subplan->paramIds) + { + foreach_int(paramid, dpns->in_subplan->paramIds) + { + if (paramid == param->paramid) + { + subplan = dpns->in_subplan; + colno = foreach_current_index(paramid); + break; + } + } + } + } + + /* else initplan output? */ + if (subplan == NULL) + { + foreach_node(SubPlan, initplan, dpns->plan->initPlan) + { + if (initplan->setParam) + { + foreach_int(paramid, initplan->setParam) + { + if (paramid == param->paramid) + { + subplan = initplan; + colno = foreach_current_index(paramid); + break; + } + } + } + else if (initplan->paramIds) + { + foreach_int(paramid, initplan->paramIds) + { + if (paramid == param->paramid) + { + subplan = initplan; + colno = foreach_current_index(paramid); + break; + } + } + } + } + } + + if (subplan) + { + appendStringInfo(context->buf, "(%s%s).col%d", + subplan->useHashTable ? "hashed " : "", + subplan->plan_name, colno + 1); + return; + } + } + /* * If it's an external parameter, see if the outermost namespace provides * function argument names. @@ -8863,17 +8942,68 @@ get_rule_expr(Node *node, deparse_context *context, case T_SubPlan: { SubPlan *subplan = (SubPlan *) node; + bool show_subplan_name = true; + deparse_namespace *dpns; /* * We cannot see an already-planned subplan in rule deparsing, * only while EXPLAINing a query plan. We don't try to * reconstruct the original SQL, just reference the subplan - * that appears elsewhere in EXPLAIN's result. + * that appears elsewhere in EXPLAIN's result. It does seem + * useful to show the subLinkType and testexpr, however, and + * we also note whether the subplan will be hashed. */ - if (subplan->useHashTable) - appendStringInfo(buf, "(hashed %s)", subplan->plan_name); - else - appendStringInfo(buf, "(%s)", subplan->plan_name); + dpns = linitial(context->namespaces); + dpns->in_subplan = subplan; + + switch (subplan->subLinkType) + { + case EXISTS_SUBLINK: + appendStringInfoString(buf, "EXISTS("); + Assert(subplan->testexpr == NULL); + break; + case ALL_SUBLINK: + appendStringInfoString(buf, "(ALL "); + get_rule_expr(subplan->testexpr, context, showimplicit); + appendStringInfoString(buf, ")"); + show_subplan_name = false; + break; + case ANY_SUBLINK: + appendStringInfoString(buf, "(ANY "); + get_rule_expr(subplan->testexpr, context, showimplicit); + appendStringInfoString(buf, ")"); + show_subplan_name = false; + break; + case ROWCOMPARE_SUBLINK: + appendStringInfoString(buf, "("); + get_rule_expr(subplan->testexpr, context, showimplicit); + appendStringInfoString(buf, ")"); + show_subplan_name = false; + break; + case EXPR_SUBLINK: + case MULTIEXPR_SUBLINK: + /* No need to decorate these subplan references */ + appendStringInfoString(buf, "("); + Assert(subplan->testexpr == NULL); + break; + case ARRAY_SUBLINK: + appendStringInfoString(buf, "ARRAY("); + Assert(subplan->testexpr == NULL); + break; + case CTE_SUBLINK: + /* This case is unreachable within expressions */ + appendStringInfoString(buf, "CTE("); + Assert(subplan->testexpr == NULL); + break; + } + dpns->in_subplan = NULL; + if (show_subplan_name) + { + if (subplan->useHashTable) + appendStringInfo(buf, "hashed %s)", subplan->plan_name); + else + appendStringInfo(buf, "%s)", subplan->plan_name); + } } break; diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index f86cf8d258..c838e006f6 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -738,7 +738,7 @@ select array(select sum(x+y) s QUERY PLAN ------------------------------------------------------------------- Function Scan on pg_catalog.generate_series x - Output: (SubPlan 1) + Output: ARRAY(SubPlan 1) Function Call: generate_series(1, 3) SubPlan 1 -> Sort @@ -915,7 +915,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan using tenk1_unique1 on tenk1 Index Cond: (unique1 IS NOT NULL) @@ -932,7 +932,7 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique1 on tenk1 Index Cond: (unique1 IS NOT NULL) @@ -949,7 +949,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique1 on tenk1 Index Cond: ((unique1 IS NOT NULL) AND (unique1 < 42)) @@ -966,7 +966,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique1 on tenk1 Index Cond: ((unique1 IS NOT NULL) AND (unique1 > 42)) @@ -989,7 +989,7 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique1 on tenk1 Index Cond: ((unique1 IS NOT NULL) AND (unique1 > 42000)) @@ -1008,7 +1008,7 @@ explain (costs off) QUERY PLAN ---------------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_thous_tenthous on tenk1 Index Cond: ((thousand = 33) AND (tenthous IS NOT NULL)) @@ -1025,7 +1025,7 @@ explain (costs off) QUERY PLAN -------------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan using tenk1_thous_tenthous on tenk1 Index Cond: ((thousand = 33) AND (tenthous IS NOT NULL)) @@ -1046,7 +1046,7 @@ explain (costs off) Seq Scan on int4_tbl SubPlan 2 -> Result - InitPlan 1 (returns $1) + InitPlan 1 -> Limit -> Index Only Scan using tenk1_unique1 on tenk1 Index Cond: ((unique1 IS NOT NULL) AND (unique1 > int4_tbl.f1)) @@ -1070,7 +1070,7 @@ explain (costs off) --------------------------------------------------------------------- HashAggregate Group Key: $0 - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1089,7 +1089,7 @@ explain (costs off) --------------------------------------------------------------------- Sort Sort Key: ($0) - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1108,7 +1108,7 @@ explain (costs off) --------------------------------------------------------------------- Sort Sort Key: ($0) - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1127,7 +1127,7 @@ explain (costs off) --------------------------------------------------------------------- Sort Sort Key: (($0 + 1)) - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1146,7 +1146,7 @@ explain (costs off) --------------------------------------------------------------------- Sort Sort Key: (generate_series(1, 3)) DESC - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1168,7 +1168,7 @@ explain (costs off) QUERY PLAN ---------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Result One-Time Filter: (100 IS NOT NULL) @@ -1199,7 +1199,7 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Merge Append Sort Key: minmaxtest.f1 @@ -1210,7 +1210,7 @@ explain (costs off) -> Index Only Scan Backward using minmaxtest2i on minmaxtest2 minmaxtest_3 Index Cond: (f1 IS NOT NULL) -> Index Only Scan using minmaxtest3i on minmaxtest3 minmaxtest_4 - InitPlan 2 (returns $1) + InitPlan 2 -> Limit -> Merge Append Sort Key: minmaxtest_5.f1 DESC @@ -1235,7 +1235,7 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------------------------------- Unique - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Merge Append Sort Key: minmaxtest.f1 @@ -1246,7 +1246,7 @@ explain (costs off) -> Index Only Scan Backward using minmaxtest2i on minmaxtest2 minmaxtest_3 Index Cond: (f1 IS NOT NULL) -> Index Only Scan using minmaxtest3i on minmaxtest3 minmaxtest_4 - InitPlan 2 (returns $1) + InitPlan 2 -> Limit -> Merge Append Sort Key: minmaxtest_5.f1 DESC diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index a3b9aaca84..7c8094f7c7 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -559,7 +559,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan using tenk1_unique1 on tenk1 Index Cond: (unique1 IS NOT NULL) @@ -2109,14 +2109,14 @@ order by a, b, c; -- test handling of outer GroupingFunc within subqueries explain (costs off) select (select grouping(v1)) from (values ((select 1))) v(v1) group by cube(v1); - QUERY PLAN ---------------------------- + QUERY PLAN +----------------- MixedAggregate Hash Key: $2 Group Key: () - InitPlan 1 (returns $1) + InitPlan 1 -> Result - InitPlan 3 (returns $2) + InitPlan 3 -> Result -> Result SubPlan 2 @@ -2132,12 +2132,12 @@ select (select grouping(v1)) from (values ((select 1))) v(v1) group by cube(v1); explain (costs off) select (select grouping(v1)) from (values ((select 1))) v(v1) group by v1; - QUERY PLAN ---------------------------- + QUERY PLAN +---------------- GroupAggregate - InitPlan 1 (returns $1) + InitPlan 1 -> Result - InitPlan 3 (returns $2) + InitPlan 3 -> Result -> Result SubPlan 2 diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index 130a924228..e0dbfb83a9 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -1643,8 +1643,8 @@ explain (verbose, costs off) select min(1-id) from matest0; QUERY PLAN --------------------------------------------------------------------------------- Result - Output: $0 - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1 + InitPlan 1 -> Limit Output: ((1 - matest0.id)) -> Result @@ -1800,7 +1800,7 @@ SELECT min(x) FROM QUERY PLAN -------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Merge Append Sort Key: a.unique1 @@ -1818,7 +1818,7 @@ SELECT min(y) FROM QUERY PLAN -------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Merge Append Sort Key: a.unique1 @@ -1898,19 +1898,19 @@ insert into inhpar select x, x::text from generate_series(1,5) x; insert into inhcld select x::text, x from generate_series(6,10) x; explain (verbose, costs off) update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1); - QUERY PLAN -------------------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------- Update on public.inhpar i Update on public.inhpar i_1 Update on public.inhcld i_2 -> Result - Output: $2, $3, (SubPlan 1 (returns $2,$3)), i.tableoid, i.ctid + Output: $2, $3, (SubPlan 1), i.tableoid, i.ctid -> Append -> Seq Scan on public.inhpar i_1 Output: i_1.f1, i_1.f2, i_1.tableoid, i_1.ctid -> Seq Scan on public.inhcld i_2 Output: i_2.f1, i_2.f2, i_2.tableoid, i_2.ctid - SubPlan 1 (returns $2,$3) + SubPlan 1 -> Limit Output: (i.f1), (((i.f2)::text || '-'::text)) -> Seq Scan on public.int4_tbl @@ -1946,21 +1946,21 @@ alter table inhpar attach partition inhcld2 for values from (5) to (100); insert into inhpar select x, x::text from generate_series(1,10) x; explain (verbose, costs off) update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1); - QUERY PLAN ------------------------------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------------------- Update on public.inhpar i Update on public.inhcld1 i_1 Update on public.inhcld2 i_2 -> Append -> Seq Scan on public.inhcld1 i_1 - Output: $2, $3, (SubPlan 1 (returns $2,$3)), i_1.tableoid, i_1.ctid - SubPlan 1 (returns $2,$3) + Output: $2, $3, (SubPlan 1), i_1.tableoid, i_1.ctid + SubPlan 1 -> Limit Output: (i_1.f1), (((i_1.f2)::text || '-'::text)) -> Seq Scan on public.int4_tbl Output: i_1.f1, ((i_1.f2)::text || '-'::text) -> Seq Scan on public.inhcld2 i_2 - Output: $2, $3, (SubPlan 1 (returns $2,$3)), i_2.tableoid, i_2.ctid + Output: $2, $3, (SubPlan 1), i_2.tableoid, i_2.ctid (13 rows) update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1); @@ -2845,11 +2845,11 @@ explain (costs off) select min(a), max(a) from parted_minmax where b = '12345'; QUERY PLAN ------------------------------------------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan using parted_minmax1i on parted_minmax1 parted_minmax Index Cond: ((a IS NOT NULL) AND (b = '12345'::text)) - InitPlan 2 (returns $1) + InitPlan 2 -> Limit -> Index Only Scan Backward using parted_minmax1i on parted_minmax1 parted_minmax_1 Index Cond: ((a IS NOT NULL) AND (b = '12345'::text)) diff --git a/src/test/regress/expected/insert_conflict.out b/src/test/regress/expected/insert_conflict.out index 563c5eb52a..701217ddbc 100644 --- a/src/test/regress/expected/insert_conflict.out +++ b/src/test/regress/expected/insert_conflict.out @@ -50,7 +50,7 @@ explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on con Insert on insertconflicttest Conflict Resolution: UPDATE Conflict Arbiter Indexes: op_index_key, collation_index_key, both_index_key - Conflict Filter: (SubPlan 1) + Conflict Filter: EXISTS(SubPlan 1) -> Result SubPlan 1 -> Index Only Scan using both_index_expr_key on insertconflicttest ii diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 9605400021..4cb22f783b 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -3028,10 +3028,10 @@ where unique1 in (select unique2 from tenk1 b); explain (costs off) select a.* from tenk1 a where unique1 not in (select unique2 from tenk1 b); - QUERY PLAN --------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------- Seq Scan on tenk1 a - Filter: (NOT (hashed SubPlan 1)) + Filter: (NOT (ANY (unique1 = (hashed SubPlan 1).col1))) SubPlan 1 -> Index Only Scan using tenk1_unique2 on tenk1 b (4 rows) @@ -5278,12 +5278,12 @@ explain (costs off) select a.unique1, b.unique2 from onek a left join onek b on a.unique1 = b.unique2 where (b.unique2, random() > 0) = any (select q1, random() > 0 from int8_tbl c where c.q1 < b.unique1); - QUERY PLAN ----------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------------ Hash Join Hash Cond: (b.unique2 = a.unique1) -> Seq Scan on onek b - Filter: (SubPlan 1) + Filter: (ANY ((unique2 = (SubPlan 1).col1) AND ((random() > '0'::double precision) = (SubPlan 1).col2))) SubPlan 1 -> Seq Scan on int8_tbl c Filter: (q1 < b.unique1) @@ -8262,8 +8262,8 @@ lateral (select * from int8_tbl t1, where q2 = (select greatest(t1.q1,t2.q2)) and (select v.id=0)) offset 0) ss2) ss where t1.q1 = ss.q2) ss0; - QUERY PLAN -------------------------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------- Nested Loop Output: "*VALUES*".column1, t1.q1, t1.q2, ss2.q1, ss2.q2 -> Seq Scan on public.int8_tbl t1 @@ -8277,15 +8277,15 @@ lateral (select * from int8_tbl t1, Filter: (t1.q1 = ss2.q2) -> Seq Scan on public.int8_tbl t2 Output: t2.q1, t2.q2 - Filter: (SubPlan 3) + Filter: (ANY ((t2.q1 = (SubPlan 3).col1) AND ((random() > '0'::double precision) = (SubPlan 3).col2))) SubPlan 3 -> Result Output: t3.q2, (random() > '0'::double precision) - One-Time Filter: $4 - InitPlan 1 (returns $2) + One-Time Filter: (InitPlan 2).col1 + InitPlan 1 -> Result Output: GREATEST(t1.q1, t2.q2) - InitPlan 2 (returns $4) + InitPlan 2 -> Result Output: ("*VALUES*".column1 = 0) -> Seq Scan on public.int8_tbl t3 diff --git a/src/test/regress/expected/memoize.out b/src/test/regress/expected/memoize.out index 1c8d996740..0fd103c06b 100644 --- a/src/test/regress/expected/memoize.out +++ b/src/test/regress/expected/memoize.out @@ -342,7 +342,7 @@ WHERE unique1 < 3 ---------------------------------------------------------------- Index Scan using tenk1_unique1 on tenk1 t0 Index Cond: (unique1 < 3) - Filter: (SubPlan 1) + Filter: EXISTS(SubPlan 1) SubPlan 1 -> Nested Loop -> Index Scan using tenk1_hundred on tenk1 t2 diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index bf0657b9f2..e6f732f5e9 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -2180,7 +2180,7 @@ explain (analyze, costs off, summary off, timing off) execute ab_q2 (2, 2); --------------------------------------------------------- Append (actual rows=0 loops=1) Subplans Removed: 6 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on ab_a2_b1 ab_1 (actual rows=0 loops=1) Filter: ((a >= $1) AND (a <= $2) AND (b < $0)) @@ -2198,7 +2198,7 @@ explain (analyze, costs off, summary off, timing off) execute ab_q3 (2, 2); --------------------------------------------------------- Append (actual rows=0 loops=1) Subplans Removed: 6 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on ab_a1_b2 ab_1 (actual rows=0 loops=1) Filter: ((b >= $1) AND (b <= $2) AND (a < $0)) @@ -2414,9 +2414,9 @@ select explain_parallel_append('select count(*) from ab where (a = (select 1) or explain_parallel_append ------------------------------------------------------------------------------ Aggregate (actual rows=N loops=N) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=N loops=N) - InitPlan 2 (returns $1) + InitPlan 2 -> Result (actual rows=N loops=N) -> Gather (actual rows=N loops=N) Workers Planned: 2 @@ -2629,10 +2629,10 @@ select * from ab where a = (select max(a) from lprt_a) and b = (select max(a)-1 QUERY PLAN ------------------------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Aggregate (actual rows=1 loops=1) -> Seq Scan on lprt_a (actual rows=102 loops=1) - InitPlan 2 (returns $1) + InitPlan 2 -> Aggregate (actual rows=1 loops=1) -> Seq Scan on lprt_a lprt_a_1 (actual rows=102 loops=1) -> Bitmap Heap Scan on ab_a1_b1 ab_1 (never executed) @@ -2688,7 +2688,7 @@ select * from (select * from ab where a = 1 union all select * from ab) ab where QUERY PLAN ------------------------------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Append (actual rows=0 loops=1) -> Bitmap Heap Scan on ab_a1_b1 ab_11 (actual rows=0 loops=1) @@ -2732,7 +2732,7 @@ select * from (select * from ab where a = 1 union all (values(10,5)) union all s QUERY PLAN ------------------------------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Append (actual rows=0 loops=1) -> Bitmap Heap Scan on ab_a1_b1 ab_11 (actual rows=0 loops=1) @@ -2791,7 +2791,7 @@ explain (analyze, costs off, summary off, timing off) execute ab_q6(1); -------------------------------------------------- Append (actual rows=0 loops=1) Subplans Removed: 12 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on ab_a1_b1 ab_1 (never executed) Filter: ((a = $1) AND (b = $0)) @@ -2884,7 +2884,7 @@ update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 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 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Nested Loop (actual rows=3 loops=1) -> Append (actual rows=3 loops=1) @@ -3232,7 +3232,7 @@ select * from listp where a = (select null::int); QUERY PLAN ------------------------------------------------------ Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on listp_1_1 listp_1 (never executed) Filter: (a = $0) @@ -3379,7 +3379,7 @@ execute ps1(1); -------------------------------------------------------- Append (actual rows=1 loops=1) Subplans Removed: 2 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on mc3p1 mc3p_1 (actual rows=1 loops=1) Filter: ((a = $1) AND (abs(b) < $0)) @@ -3394,7 +3394,7 @@ execute ps2(1); -------------------------------------------------------- Append (actual rows=2 loops=1) Subplans Removed: 1 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on mc3p0 mc3p_1 (actual rows=1 loops=1) Filter: ((a <= $1) AND (abs(b) < $0)) @@ -3415,7 +3415,7 @@ select * from boolp where a = (select value from boolvalues where value); QUERY PLAN ----------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Seq Scan on boolvalues (actual rows=1 loops=1) Filter: value Rows Removed by Filter: 1 @@ -3430,7 +3430,7 @@ select * from boolp where a = (select value from boolvalues where not value); QUERY PLAN ----------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Seq Scan on boolvalues (actual rows=1 loops=1) Filter: (NOT value) Rows Removed by Filter: 1 @@ -3526,9 +3526,9 @@ explain (analyze, costs off, summary off, timing off) select * from ma_test wher ----------------------------------------------------------------------------------------------- Merge Append (actual rows=20 loops=1) Sort Key: ma_test.b - InitPlan 2 (returns $1) + InitPlan 2 -> Result (actual rows=1 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Limit (actual rows=1 loops=1) -> Index Scan using ma_test_p2_b_idx on ma_test_p2 (actual rows=1 loops=1) Index Cond: (b IS NOT NULL) @@ -3909,7 +3909,7 @@ where s.a = 1 and s.b = 1 and s.c = (select 1); QUERY PLAN ---------------------------------------------------- Append - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Seq Scan on p1 p Filter: ((a = 1) AND (b = 1) AND (c = $0)) @@ -3948,7 +3948,7 @@ explain (costs off) execute q (1, 1); --------------------------------------------------------------- Append Subplans Removed: 1 - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Seq Scan on p1 p Filter: ((a = $1) AND (b = $2) AND (c = $0)) @@ -3973,11 +3973,11 @@ create table listp2 partition of listp for values in(2) partition by list(b); create table listp2_10 partition of listp2 for values in (10); explain (analyze, costs off, summary off, timing off) select * from listp where a = (select 2) and b <> 10; - QUERY PLAN --------------------------------------------------- + QUERY PLAN +--------------------------------------------------- Seq Scan on listp1 listp (actual rows=0 loops=1) - Filter: ((b <> 10) AND (a = $0)) - InitPlan 1 (returns $0) + Filter: ((b <> 10) AND (a = (InitPlan 1).col1)) + InitPlan 1 -> Result (never executed) (4 rows) @@ -4044,7 +4044,7 @@ select explain_parallel_append('select * from listp where a = (select 1);'); Workers Planned: 2 Params Evaluated: $0 Workers Launched: N - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=N loops=N) -> Parallel Append (actual rows=N loops=N) -> Seq Scan on listp_12_1 listp_1 (actual rows=N loops=N) @@ -4067,14 +4067,14 @@ select * from listp where a = (select 2);'); Workers Launched: N -> Parallel Append (actual rows=N loops=N) -> Parallel Append (actual rows=N loops=N) - InitPlan 2 (returns $1) + InitPlan 2 -> Result (actual rows=N loops=N) -> Seq Scan on listp_12_1 listp_1 (never executed) Filter: (a = $1) -> Parallel Seq Scan on listp_12_2 listp_2 (actual rows=N loops=N) Filter: (a = $1) -> Parallel Append (actual rows=N loops=N) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=N loops=N) -> Seq Scan on listp_12_1 listp_4 (actual rows=N loops=N) Filter: (a = $0) @@ -4102,9 +4102,9 @@ select * from rangep where b IN((select 1),(select 2)) order by a; QUERY PLAN ------------------------------------------------------------------------------------------------------------ Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) - InitPlan 2 (returns $1) + InitPlan 2 -> Result (actual rows=1 loops=1) -> Merge Append (actual rows=0 loops=1) Sort Key: rangep_2.a diff --git a/src/test/regress/expected/portals.out b/src/test/regress/expected/portals.out index f71e0b3d41..06726ed4ab 100644 --- a/src/test/regress/expected/portals.out +++ b/src/test/regress/expected/portals.out @@ -1472,18 +1472,18 @@ rollback; -- Check handling of non-backwards-scan-capable plans with scroll cursors begin; explain (costs off) declare c1 cursor for select (select 42) as x; - QUERY PLAN ---------------------------- + QUERY PLAN +---------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Result (3 rows) explain (costs off) declare c1 scroll cursor for select (select 42) as x; - QUERY PLAN ---------------------------- + QUERY PLAN +---------------- Materialize - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Result (4 rows) diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index 4538f0c37d..d5bdc87dde 100644 --- a/src/test/regress/expected/rowsecurity.out +++ b/src/test/regress/expected/rowsecurity.out @@ -265,11 +265,11 @@ NOTICE: f_leak => awesome science fiction (5 rows) EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle); - QUERY PLAN ----------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------- Seq Scan on document - Filter: ((dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) (5 rows) @@ -279,7 +279,7 @@ EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dt ----------------------------------------------------------- Hash Join Hash Cond: (category.cid = document.cid) - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on category @@ -329,11 +329,11 @@ NOTICE: f_leak => awesome technology book (7 rows) EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle); - QUERY PLAN ----------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------- Seq Scan on document - Filter: ((cid <> 44) AND (cid <> 44) AND (cid < 50) AND (dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) + Filter: ((cid <> 44) AND (cid <> 44) AND (cid < 50) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) (5 rows) @@ -343,7 +343,7 @@ EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dt ---------------------------------------------------------------------------------------------------------- Hash Join Hash Cond: (category.cid = document.cid) - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on category @@ -990,7 +990,7 @@ EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); QUERY PLAN ------------------------------------------------------------ Append - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on part_document_fiction part_document_1 @@ -1032,7 +1032,7 @@ EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); QUERY PLAN ------------------------------------------------------------ Append - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on part_document_fiction part_document_1 @@ -1059,11 +1059,11 @@ NOTICE: f_leak => awesome science fiction (4 rows) EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN --------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------- Seq Scan on part_document_fiction part_document - Filter: ((cid < 55) AND (dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) + Filter: ((cid < 55) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) (5 rows) @@ -1137,11 +1137,11 @@ NOTICE: f_leak => awesome science fiction (4 rows) EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN --------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------- Seq Scan on part_document_fiction part_document - Filter: ((cid < 55) AND (dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) + Filter: ((cid < 55) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) (5 rows) @@ -1179,7 +1179,7 @@ EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); QUERY PLAN ------------------------------------------------------------ Append - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on part_document_fiction part_document_1 @@ -1437,10 +1437,10 @@ NOTICE: f_leak => 03b26944890929ff751653acb2f2af79 (1 row) EXPLAIN (COSTS OFF) SELECT * FROM only s1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------- Seq Scan on s1 - Filter: ((hashed SubPlan 1) AND f_leak(b)) + Filter: ((ANY (a = (hashed SubPlan 1).col1)) AND f_leak(b)) SubPlan 1 -> Seq Scan on s2 Filter: (((x % 2) = 0) AND (y ~~ '%2f%'::text)) @@ -1457,10 +1457,10 @@ NOTICE: f_leak => 03b26944890929ff751653acb2f2af79 (1 row) EXPLAIN (COSTS OFF) SELECT * FROM s1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------- Seq Scan on s1 - Filter: ((hashed SubPlan 1) AND f_leak(b)) + Filter: ((ANY (a = (hashed SubPlan 1).col1)) AND f_leak(b)) SubPlan 1 -> Seq Scan on s2 Filter: (((x % 2) = 0) AND (y ~~ '%af%'::text)) @@ -1480,7 +1480,7 @@ EXPLAIN (COSTS OFF) SELECT (SELECT x FROM s1 LIMIT 1) xx, * FROM s2 WHERE y like SubPlan 2 -> Limit -> Seq Scan on s1 - Filter: (hashed SubPlan 1) + Filter: (ANY (a = (hashed SubPlan 1).col1)) SubPlan 1 -> Seq Scan on s2 s2_1 Filter: (((x % 2) = 0) AND (y ~~ '%af%'::text)) @@ -2687,10 +2687,10 @@ NOTICE: f_leak => bbb (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 0) AND f_leak(b)) + Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 0) AND f_leak(b)) SubPlan 1 -> Seq Scan on z1_blacklist (4 rows) @@ -2705,10 +2705,10 @@ NOTICE: f_leak => bbb (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 0) AND f_leak(b)) + Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 0) AND f_leak(b)) SubPlan 1 -> Seq Scan on z1_blacklist (4 rows) @@ -2877,10 +2877,10 @@ NOTICE: f_leak => bbb (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 0) AND f_leak(b)) + Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 0) AND f_leak(b)) SubPlan 1 -> Seq Scan on z1_blacklist (4 rows) @@ -2903,10 +2903,10 @@ NOTICE: f_leak => aba (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 1) AND f_leak(b)) + Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 1) AND f_leak(b)) SubPlan 1 -> Seq Scan on z1_blacklist (4 rows) diff --git a/src/test/regress/expected/rowtypes.out b/src/test/regress/expected/rowtypes.out index 8f3c153bac..b400b58f76 100644 --- a/src/test/regress/expected/rowtypes.out +++ b/src/test/regress/expected/rowtypes.out @@ -1249,8 +1249,8 @@ with cte(c) as materialized (select row(1, 2)), select * from cte2 as t where (select * from (select c as c1) s where (select (c1).f1 > 0)) is not null; - QUERY PLAN --------------------------------------------- + QUERY PLAN +---------------------------------------------- CTE Scan on cte Output: cte.c Filter: ((SubPlan 3) IS NOT NULL) @@ -1260,8 +1260,8 @@ where (select * from (select c as c1) s SubPlan 3 -> Result Output: cte.c - One-Time Filter: $2 - InitPlan 2 (returns $2) + One-Time Filter: (InitPlan 2).col1 + InitPlan 2 -> Result Output: ((cte.c).f1 > 0) (13 rows) diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out index 7a0d78dfe3..eea019737e 100644 --- a/src/test/regress/expected/select_parallel.out +++ b/src/test/regress/expected/select_parallel.out @@ -291,14 +291,14 @@ alter table tenk2 set (parallel_workers = 0); explain (costs off) select count(*) from tenk1 where (two, four) not in (select hundred, thousand from tenk2 where thousand > 100); - QUERY PLAN ------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------------------------------- Finalize Aggregate -> Gather Workers Planned: 4 -> Partial Aggregate -> Parallel Seq Scan on tenk1 - Filter: (NOT (hashed SubPlan 1)) + Filter: (NOT (ANY ((two = (hashed SubPlan 1).col1) AND (four = (hashed SubPlan 1).col2)))) SubPlan 1 -> Seq Scan on tenk2 Filter: (thousand > 100) @@ -315,10 +315,10 @@ select count(*) from tenk1 where (two, four) not in explain (costs off) select * from tenk1 where (unique1 + random())::integer not in (select ten from tenk2); - QUERY PLAN ------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------- Seq Scan on tenk1 - Filter: (NOT (hashed SubPlan 1)) + Filter: (NOT (ANY ((((unique1)::double precision + random()))::integer = (hashed SubPlan 1).col1))) SubPlan 1 -> Seq Scan on tenk2 (4 rows) @@ -335,7 +335,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------ Aggregate - InitPlan 1 (returns $2) + InitPlan 1 -> Finalize Aggregate -> Gather Workers Planned: 2 @@ -1151,7 +1151,7 @@ ORDER BY 1; -> Gather Workers Planned: 4 Params Evaluated: $1 - InitPlan 1 (returns $1) + InitPlan 1 -> Limit -> Gather Workers Planned: 4 @@ -1162,7 +1162,7 @@ ORDER BY 1; -> Gather Workers Planned: 4 Params Evaluated: $3 - InitPlan 2 (returns $3) + InitPlan 2 -> Limit -> Gather Workers Planned: 4 @@ -1182,10 +1182,10 @@ ORDER BY 1, 2, 3; EXPLAIN (VERBOSE, COSTS OFF) SELECT generate_series(1, two), array(select generate_series(1, two)) FROM tenk1 ORDER BY tenthous; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------- ProjectSet - Output: generate_series(1, tenk1.two), (SubPlan 1), tenk1.tenthous + Output: generate_series(1, tenk1.two), ARRAY(SubPlan 1), tenk1.tenthous -> Gather Merge Output: tenk1.two, tenk1.tenthous Workers Planned: 4 diff --git a/src/test/regress/expected/sqljson.out b/src/test/regress/expected/sqljson.out index 5e7da96be5..cbf8542d8d 100644 --- a/src/test/regress/expected/sqljson.out +++ b/src/test/regress/expected/sqljson.out @@ -1064,8 +1064,8 @@ SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i) RETURNING QUERY PLAN --------------------------------------------------------------------- Result - Output: $0 - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1 + InitPlan 1 -> Aggregate Output: JSON_ARRAYAGG("*VALUES*".column1 RETURNING jsonb) -> Values Scan on "*VALUES*" diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out index e41b728df8..eb0efa60d9 100644 --- a/src/test/regress/expected/subselect.out +++ b/src/test/regress/expected/subselect.out @@ -202,6 +202,57 @@ SELECT f1 AS "Correlated Field" 3 (5 rows) +-- Check ROWCOMPARE cases, both correlated and not +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + QUERY PLAN +----------------------------------------------------------------- + Seq Scan on public.subselect_tbl + Output: (((1 = (SubPlan 1).col1) AND (2 = (SubPlan 1).col2))) + SubPlan 1 + -> Result + Output: subselect_tbl.f1, subselect_tbl.f2 +(5 rows) + +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + eq +---- + t + f + f + f + f + f + f + f +(8 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + QUERY PLAN +----------------------------------------------------------------- + Seq Scan on public.subselect_tbl + Output: ((1 = (InitPlan 1).col1) AND (2 = (InitPlan 1).col2)) + InitPlan 1 + -> Result + Output: 3, 4 +(5 rows) + +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + eq +---- + f + f + f + f + f + f + f + f +(8 rows) + +SELECT ROW(1, 2) = (SELECT f1, f2 FROM SUBSELECT_TBL); -- error +ERROR: more than one row returned by a subquery used as an expression -- Subselects without aliases SELECT count FROM (SELECT COUNT(DISTINCT name) FROM road); count @@ -324,14 +375,14 @@ explain (verbose, costs off) select '42' union all select 43; -- check materialization of an initplan reference (bug #14524) explain (verbose, costs off) select 1 = all (select (select 1)); - QUERY PLAN ------------------------------------ + QUERY PLAN +---------------------------------------- Result - Output: (SubPlan 2) + Output: (ALL (1 = (SubPlan 2).col1)) SubPlan 2 -> Materialize Output: ($0) - InitPlan 1 (returns $0) + InitPlan 1 -> Result Output: 1 -> Result @@ -377,7 +428,7 @@ select * from int4_tbl o where exists QUERY PLAN -------------------------------------- Seq Scan on int4_tbl o - Filter: (SubPlan 1) + Filter: EXISTS(SubPlan 1) SubPlan 1 -> Limit -> Seq Scan on int4_tbl i @@ -840,10 +891,10 @@ select * from outer_text where (f1, f2) not in (select * from inner_text); -- explain (verbose, costs off) select 'foo'::text in (select 'bar'::name union all select 'bar'::name); - QUERY PLAN -------------------------------------- + QUERY PLAN +--------------------------------------------------------- Result - Output: (hashed SubPlan 1) + Output: (ANY ('foo'::text = (hashed SubPlan 1).col1)) SubPlan 1 -> Append -> Result @@ -864,10 +915,10 @@ select 'foo'::text in (select 'bar'::name union all select 'bar'::name); -- explain (verbose, costs off) select row(row(row(1))) = any (select row(row(1))); - QUERY PLAN -------------------------------------------- + QUERY PLAN +-------------------------------------------------------- Result - Output: (SubPlan 1) + Output: (ANY ('("(1)")'::record = (SubPlan 1).col1)) SubPlan 1 -> Materialize Output: '("(1)")'::record @@ -907,10 +958,10 @@ language sql as 'select $1::text = $2'; create operator = (procedure=bogus_int8_text_eq, leftarg=int8, rightarg=text); explain (costs off) select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------- + QUERY PLAN +-------------------------------------------------------- Seq Scan on int8_tbl - Filter: (hashed SubPlan 1) + Filter: (ANY ((q1)::text = (hashed SubPlan 1).col1)) SubPlan 1 -> Seq Scan on inner_text (4 rows) @@ -928,10 +979,10 @@ create or replace function bogus_int8_text_eq(int8, text) returns boolean language sql as 'select $1::text = $2 and $1::text = $2'; explain (costs off) select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------- Seq Scan on int8_tbl - Filter: (hashed SubPlan 1) + Filter: (ANY (((q1)::text = (hashed SubPlan 1).col1) AND ((q1)::text = (hashed SubPlan 1).col1))) SubPlan 1 -> Seq Scan on inner_text (4 rows) @@ -949,10 +1000,10 @@ create or replace function bogus_int8_text_eq(int8, text) returns boolean language sql as 'select $2 = $1::text'; explain (costs off) select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------------- + QUERY PLAN +------------------------------------------------- Seq Scan on int8_tbl - Filter: (SubPlan 1) + Filter: (ANY ((SubPlan 1).col1 = (q1)::text)) SubPlan 1 -> Materialize -> Seq Scan on inner_text @@ -972,11 +1023,11 @@ rollback; -- to get rid of the bogus operator explain (costs off) select count(*) from tenk1 t where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0); - QUERY PLAN --------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------------- Aggregate -> Seq Scan on tenk1 t - Filter: ((hashed SubPlan 2) OR (ten < 0)) + Filter: ((ANY (unique2 = (hashed SubPlan 2).col1)) OR (ten < 0)) SubPlan 2 -> Index Only Scan using tenk1_unique1 on tenk1 k (5 rows) @@ -997,7 +1048,7 @@ where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0) Aggregate -> Bitmap Heap Scan on tenk1 t Recheck Cond: (thousand = 1) - Filter: ((SubPlan 1) OR (ten < 0)) + Filter: (EXISTS(SubPlan 1) OR (ten < 0)) -> Bitmap Index Scan on tenk1_thous_tenthous Index Cond: (thousand = 1) SubPlan 1 @@ -1022,11 +1073,11 @@ analyze exists_tbl; explain (costs off) select * from exists_tbl t1 where (exists(select 1 from exists_tbl t2 where t1.c1 = t2.c2) or c3 < 0); - QUERY PLAN ------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------- Append -> Seq Scan on exists_tbl_null t1_1 - Filter: ((SubPlan 1) OR (c3 < 0)) + Filter: (EXISTS(SubPlan 1) OR (c3 < 0)) SubPlan 1 -> Append -> Seq Scan on exists_tbl_null t2_1 @@ -1034,7 +1085,7 @@ select * from exists_tbl t1 -> Seq Scan on exists_tbl_def t2_2 Filter: (t1_1.c1 = c2) -> Seq Scan on exists_tbl_def t1_2 - Filter: ((hashed SubPlan 2) OR (c3 < 0)) + Filter: ((ANY (c1 = (hashed SubPlan 2).col1)) OR (c3 < 0)) SubPlan 2 -> Append -> Seq Scan on exists_tbl_null t2_4 @@ -1071,14 +1122,14 @@ where a.thousand = b.thousand explain (verbose, costs off) select x, x from (select (select now()) as x from (values(1),(2)) v(y)) ss; - QUERY PLAN ---------------------------- + QUERY PLAN +------------------------------------------------ Values Scan on "*VALUES*" - Output: $0, $1 - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1, (InitPlan 2).col1 + InitPlan 1 -> Result Output: now() - InitPlan 2 (returns $1) + InitPlan 2 -> Result Output: now() (8 rows) @@ -1086,13 +1137,13 @@ explain (verbose, costs off) explain (verbose, costs off) select x, x from (select (select random()) as x from (values(1),(2)) v(y)) ss; - QUERY PLAN ----------------------------------- + QUERY PLAN +----------------------------------- Subquery Scan on ss Output: ss.x, ss.x -> Values Scan on "*VALUES*" - Output: $0 - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1 + InitPlan 1 -> Result Output: random() (7 rows) @@ -1143,14 +1194,14 @@ where o.ten = 0; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Aggregate - Output: sum((((hashed SubPlan 1)))::integer) + Output: sum((((ANY (i.ten = (hashed SubPlan 1).col1))))::integer) -> Nested Loop - Output: ((hashed SubPlan 1)) + Output: ((ANY (i.ten = (hashed SubPlan 1).col1))) -> Seq Scan on public.onek o Output: o.unique1, o.unique2, o.two, o.four, o.ten, o.twenty, o.hundred, o.thousand, o.twothousand, o.fivethous,o.tenthous, o.odd, o.even, o.stringu1, o.stringu2, o.string4 Filter: (o.ten = 0) -> Index Scan using onek_unique1 on public.onek i - Output: (hashed SubPlan 1), random() + Output: (ANY (i.ten = (hashed SubPlan 1).col1)), random() Index Cond: (i.unique1 = o.unique1) SubPlan 1 -> Seq Scan on public.int4_tbl @@ -1346,7 +1397,7 @@ select * from int4_tbl where --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Nested Loop Semi Join Output: int4_tbl.f1 - Join Filter: (CASE WHEN (hashed SubPlan 1) THEN int4_tbl.f1 ELSE NULL::integer END = b.ten) + Join Filter: (CASE WHEN (ANY (int4_tbl.f1 = (hashed SubPlan 1).col1)) THEN int4_tbl.f1 ELSE NULL::integer END = b.ten) -> Seq Scan on public.int4_tbl Output: int4_tbl.f1 -> Seq Scan on public.tenk1 b @@ -1945,10 +1996,10 @@ select * from tenk1 A where exists (select 1 from tenk2 B where A.hundred in (select C.hundred FROM tenk2 C WHERE c.odd = b.odd)); - QUERY PLAN ---------------------------------- + QUERY PLAN +----------------------------------------------------- Nested Loop Semi Join - Join Filter: (SubPlan 1) + Join Filter: (ANY (a.hundred = (SubPlan 1).col1)) -> Seq Scan on tenk1 a -> Materialize -> Seq Scan on tenk2 b @@ -1962,10 +2013,10 @@ WHERE c.odd = b.odd)); explain (costs off) SELECT * FROM tenk1 A LEFT JOIN tenk2 B ON A.hundred in (SELECT c.hundred FROM tenk2 C WHERE c.odd = b.odd); - QUERY PLAN ---------------------------------- + QUERY PLAN +----------------------------------------------------- Nested Loop Left Join - Join Filter: (SubPlan 1) + Join Filter: (ANY (a.hundred = (SubPlan 1).col1)) -> Seq Scan on tenk1 a -> Materialize -> Seq Scan on tenk2 b @@ -1979,10 +2030,10 @@ ON A.hundred in (SELECT c.hundred FROM tenk2 C WHERE c.odd = b.odd); explain (costs off) SELECT * FROM tenk1 A LEFT JOIN tenk2 B ON B.hundred in (SELECT c.hundred FROM tenk2 C WHERE c.odd = a.odd); - QUERY PLAN ---------------------------------- + QUERY PLAN +----------------------------------------------------- Nested Loop Left Join - Join Filter: (SubPlan 1) + Join Filter: (ANY (b.hundred = (SubPlan 1).col1)) -> Seq Scan on tenk1 a -> Materialize -> Seq Scan on tenk2 b @@ -2045,7 +2096,7 @@ ON B.hundred in (SELECT min(c.hundred) FROM tenk2 C WHERE c.odd = b.odd); -> Subquery Scan on "ANY_subquery" Filter: (b.hundred = "ANY_subquery".min) -> Result - InitPlan 1 (returns $1) + InitPlan 1 -> Limit -> Index Scan using tenk2_hundred on tenk2 c Index Cond: (hundred IS NOT NULL) diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index 794cf9cf93..1666a57775 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -3014,14 +3014,14 @@ EXPLAIN (costs off) INSERT INTO rw_view1 VALUES (2, 'New row 2'); QUERY PLAN ----------------------------------------------------------- Insert on base_tbl - InitPlan 1 (returns $0) + InitPlan 1 -> Index Only Scan using base_tbl_pkey on base_tbl t Index Cond: (id = 2) -> Result One-Time Filter: ($0 IS NOT TRUE) Update on base_tbl - InitPlan 1 (returns $0) + InitPlan 1 -> Index Only Scan using base_tbl_pkey on base_tbl t Index Cond: (id = 2) -> Result @@ -3084,8 +3084,8 @@ 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 t1_1 Update on public.t11 t1_2 @@ -3097,7 +3097,7 @@ UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; -> 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)) + Filter: ((t1_1.a <> 6) AND EXISTS(SubPlan 1) AND snoop(t1_1.a) AND leakproof(t1_1.a)) SubPlan 1 -> Append -> Seq Scan on public.t12 t12_1 @@ -3107,15 +3107,15 @@ UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; -> 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)) + Filter: ((t1_2.a <> 6) AND EXISTS(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)) + Filter: ((t1_3.a <> 6) AND EXISTS(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)) + Filter: ((t1_4.a <> 6) AND EXISTS(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; @@ -3131,8 +3131,8 @@ 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 t1_1 Update on public.t11 t1_2 @@ -3144,7 +3144,7 @@ UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; -> 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)) + Filter: (EXISTS(SubPlan 1) AND snoop(t1_1.a) AND leakproof(t1_1.a)) SubPlan 1 -> Append -> Seq Scan on public.t12 t12_1 @@ -3154,15 +3154,15 @@ UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; -> 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)) + Filter: (EXISTS(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)) + Filter: (EXISTS(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)) + Filter: (EXISTS(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; @@ -3349,7 +3349,7 @@ EXPLAIN (COSTS OFF) UPDATE v2 SET a = 1; QUERY PLAN --------------------------------------------------- Update on t1 - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Merge Join Merge Cond: (t1.a = v1.a) diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out index cb0b033da6..153934bf90 100644 --- a/src/test/regress/expected/update.out +++ b/src/test/regress/expected/update.out @@ -178,15 +178,15 @@ 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, (SubPlan 1 (returns $1,$2)), t.ctid + Output: $1, $2, (SubPlan 1), t.ctid One-Time Filter: (CURRENT_USER = SESSION_USER) -> Seq Scan on public.update_test t Output: t.a, t.ctid - SubPlan 1 (returns $1,$2) + SubPlan 1 -> Seq Scan on public.update_test s Output: s.b, s.a Filter: (s.a = t.a) diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index 60de2cbf96..e46710cf31 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -4279,7 +4279,7 @@ WHERE c = 1; Subquery Scan on emp Filter: (emp.c = 1) -> WindowAgg - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Sort Sort Key: empsalary.empno DESC diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 7d796ea69c..1e3512c21d 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -3100,7 +3100,7 @@ WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v); CTE cte_init -> Result Output: 1, 'cte_init val'::text - InitPlan 2 (returns $1) + InitPlan 2 -> Limit Output: ((cte_init.b || ' merge update'::text)) -> CTE Scan on cte_init @@ -3143,11 +3143,11 @@ WHEN NOT MATCHED THEN INSERT VALUES(o.a, o.b || (SELECT merge_source_cte.*::text CTE merge_source_cte -> Result Output: 15, 'merge_source_cte val'::text - InitPlan 2 (returns $1) + InitPlan 2 -> CTE Scan on merge_source_cte merge_source_cte_1 Output: ((merge_source_cte_1.b || (merge_source_cte_1.*)::text) || ' merge update'::text) Filter: (merge_source_cte_1.a = 15) - InitPlan 3 (returns $2) + InitPlan 3 -> CTE Scan on merge_source_cte merge_source_cte_2 Output: ((merge_source_cte_2.*)::text || ' merge insert'::text) -> Hash Right Join diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql index 2f3601a058..7c42ebc36f 100644 --- a/src/test/regress/sql/subselect.sql +++ b/src/test/regress/sql/subselect.sql @@ -82,6 +82,20 @@ SELECT f1 AS "Correlated Field" WHERE (f1, f2) IN (SELECT f2, CAST(f3 AS int4) FROM SUBSELECT_TBL WHERE f3 IS NOT NULL); +-- Check ROWCOMPARE cases, both correlated and not + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + +SELECT ROW(1, 2) = (SELECT f1, f2 FROM SUBSELECT_TBL); -- error + -- Subselects without aliases SELECT count FROM (SELECT COUNT(DISTINCT name) FROM road);
On Sat, 16 Mar 2024 at 17:25, Tom Lane <tgl@sss.pgh.pa.us> wrote: > > After looking at your draft some more, it occurred to me that we're > not that far from getting rid of showing "$n" entirely in this > context. Instead of (subplan_name).$n, we could write something like > (subplan_name).colN or (subplan_name).columnN or (subplan_name).fN, > depending on your taste for verboseness. "fN" would correspond to the > names we assign to columns of anonymous record types, but it hasn't > got much else to recommend it. In the attached I used "colN"; > "columnN" would be my next choice. Using the column number rather than the parameter index looks a lot neater, especially in output with multiple subplans. Of those choices, "colN" looks nicest, however... I think it would be confusing if there were tables whose columns are named "colN". In that case, a SQL qual like "t1.col2 = t2.col2" might be output as something like "t1.col2 = (SubPlan 1).col3", since the subplan's targetlist wouldn't necessarily just output the table columns in order. I actually think "$n" is not so bad (especially if we make n the column number). The fact that it's qualified by the subplan name ought to be sufficient to avoid it being confused with an external parameter. Maybe there are other options, but I think it's important to choose something that's unlikely to be confused with a real column name. Whatever name is chosen, I think we should still output "(returns ...)" on the subplan nodes. In a complex query there might be a lot of output columns, and the expressions might be quite complex, making it hard to see how many columns the subplan is returning. Besides, without that, it might not be obvious to everyone what "colN" (or whatever we settle on) means in places that refer to the subplan. > You could also imagine trying to use the sub-SELECT's actual output > column names, but I fear that would be ambiguous --- too often it'd > be "?column?" or some other non-unique name. Yeah, I think that's a non-starter, because the output column names aren't necessarily unique. > The attached proof-of-concept is incomplete: it fails to replace some > $n occurrences with subplan references, as is visible in some of the > test cases. I believe your quick hack in get_parameter() is not > correct in detail, but for the moment I didn't bother to debug it. Yeah, that's exactly what it was, a quick hack. I just wanted to get some output to see what it would look like in a few real cases. Overall, I think this is heading in the right direction. I think we just need a good way to say "the n'th output column of the subplan", that can't be confused with anything else in the output. Regards, Dean
Dean Rasheed <dean.a.rasheed@gmail.com> writes: > On Sat, 16 Mar 2024 at 17:25, Tom Lane <tgl@sss.pgh.pa.us> wrote: >> After looking at your draft some more, it occurred to me that we're >> not that far from getting rid of showing "$n" entirely in this >> context. Instead of (subplan_name).$n, we could write something like >> (subplan_name).colN or (subplan_name).columnN or (subplan_name).fN, > I think it would be confusing if there were tables whose columns are > named "colN". In that case, a SQL qual like "t1.col2 = t2.col2" might > be output as something like "t1.col2 = (SubPlan 1).col3", since the > subplan's targetlist wouldn't necessarily just output the table > columns in order. Perhaps. I think people who are using columns named like that are already accustomed to having to pay close attention to which table the column is shown as qualified by. So I'm not sure there'd really be much problem in practice. > I actually think "$n" is not so bad (especially if we make n the > column number). The fact that it's qualified by the subplan name ought > to be sufficient to avoid it being confused with an external > parameter. That's an interesting compromise position, but I'm not sure that it buys much compared to the approach shown in your draft (that is, continuing to use the real param IDs). The real IDs at least have the advantage of being unique. > Whatever name is chosen, I think we should still output "(returns > ...)" on the subplan nodes. We should do that if we continue to show real param IDs, but if we change to using column numbers then I think it's pointless. Output like SubPlan 1 (returns $1,$2) ... SubPlan 2 (returns $1,$2,$3) seems to me that it'd be more confusing not less so. Does SubPlan 2 return the same values as SubPlan 1 plus more? > Overall, I think this is heading in the right direction. I think we > just need a good way to say "the n'th output column of the subplan", > that can't be confused with anything else in the output. We could consider notations like "(SubPlan 1 column 2)", which couldn't be confused with anything else, if only because a name like that would have to be double-quoted. It's a little more verbose but not that much. I fear "(SubPlan 1 col 2)" is too short to be clear. regards, tom lane
I wrote: > Dean Rasheed <dean.a.rasheed@gmail.com> writes: >> Overall, I think this is heading in the right direction. I think we >> just need a good way to say "the n'th output column of the subplan", >> that can't be confused with anything else in the output. > We could consider notations like "(SubPlan 1 column 2)", which > couldn't be confused with anything else, if only because a name > like that would have to be double-quoted. It's a little more > verbose but not that much. I fear "(SubPlan 1 col 2)" is too > short to be clear. Here's a cleaned-up version that seems to successfully resolve PARAM_EXEC references in all cases. I haven't changed the "(plan_name).colN" notation, but that's an easy fix once we have consensus on the spelling. There are two other loose ends bothering me: 1. I see that Gather nodes sometimes print things like -> Gather (actual rows=N loops=N) Workers Planned: 2 Params Evaluated: $0, $1 Workers Launched: N This now sticks out like a sore thumb, because there's no other reference to $0 or $1 in the EXPLAIN output. We could possibly adjust the code to print something like Params Evaluated: (InitPlan 1).col1, (InitPlan 2).col1 but I think that's pretty silly. This looks to me like a code debugging aid that shouldn't have survived past initial development. It's of zero use to end users, and it doesn't correspond to anything we bother to mention in EXPLAIN output in any other case: initplans just magically get evaluated at the right times. I propose we nuke the "Params Evaluated" output altogether. 2. MULTIEXPR_SUBLINK subplans now result in EXPLAIN output like explain (verbose, costs off) update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1); ... -> Result Output: (SubPlan 1).col1, (SubPlan 1).col2, (SubPlan 1), i.tableoid, i.ctid The undecorated reference to (SubPlan 1) is fairly confusing, since it doesn't correspond to anything that will actually get output. I suggest that perhaps instead this should read Output: (SubPlan 1).col1, (SubPlan 1).col2, IGNORE(SubPlan 1), i.tableoid, i.ctid or Output: (SubPlan 1).col1, (SubPlan 1).col2, RESET(SubPlan 1), i.tableoid, i.ctid the point of "RESET()" being that what the executor actually does there is to re-arm the SubPlan to be evaluated again on the next pass through the targetlist. I'm not greatly in love with either of those ideas, though. Any thoughts? regards, tom lane diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 58a603ac56..2609255da6 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -3062,10 +3062,10 @@ select exists(select 1 from pg_enum), sum(c1) from ft1; QUERY PLAN -------------------------------------------------- Foreign Scan - Output: $0, (sum(ft1.c1)) + Output: (InitPlan 1).col1, (sum(ft1.c1)) Relations: Aggregate on (public.ft1) Remote SQL: SELECT sum("C 1") FROM "S 1"."T 1" - InitPlan 1 (returns $0) + InitPlan 1 -> Seq Scan on pg_catalog.pg_enum (6 rows) @@ -3080,8 +3080,8 @@ select exists(select 1 from pg_enum), sum(c1) from ft1 group by 1; QUERY PLAN --------------------------------------------------- GroupAggregate - Output: $0, sum(ft1.c1) - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1, sum(ft1.c1) + InitPlan 1 -> Seq Scan on pg_catalog.pg_enum -> Foreign Scan on public.ft1 Output: ft1.c1 @@ -3305,10 +3305,10 @@ select sum(c1) filter (where (c1 / c1) * random() <= 1) from ft1 group by c2 ord explain (verbose, costs off) select sum(c2) filter (where c2 in (select c2 from ft1 where c2 < 5)) from ft1; - QUERY PLAN -------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------- Aggregate - Output: sum(ft1.c2) FILTER (WHERE (hashed SubPlan 1)) + Output: sum(ft1.c2) FILTER (WHERE (ANY (ft1.c2 = (hashed SubPlan 1).col1))) -> Foreign Scan on public.ft1 Output: ft1.c2 Remote SQL: SELECT c2 FROM "S 1"."T 1" @@ -6171,9 +6171,9 @@ UPDATE ft2 AS target SET (c2, c7) = ( Update on public.ft2 target Remote SQL: UPDATE "S 1"."T 1" SET c2 = $2, c7 = $3 WHERE ctid = $1 -> Foreign Scan on public.ft2 target - Output: $1, $2, (SubPlan 1 (returns $1,$2)), target.ctid, target.* + Output: (SubPlan 1).col1, (SubPlan 1).col2, (SubPlan 1), target.ctid, target.* Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8, ctid FROM "S 1"."T 1" WHERE (("C 1" > 1100)) FOR UPDATE - SubPlan 1 (returns $1,$2) + SubPlan 1 -> Foreign Scan on public.ft2 src Output: (src.c2 * 10), src.c7 Remote SQL: SELECT c2, c7 FROM "S 1"."T 1" WHERE (($1::integer = "C 1")) @@ -11685,9 +11685,9 @@ SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt W QUERY PLAN ---------------------------------------------------------------------------------------- Nested Loop Left Join - Output: t1.a, t1.b, t1.c, async_pt.a, async_pt.b, async_pt.c, ($0) + Output: t1.a, t1.b, t1.c, async_pt.a, async_pt.b, async_pt.c, ((InitPlan 1).col1) Join Filter: (t1.a = async_pt.a) - InitPlan 1 (returns $0) + InitPlan 1 -> Aggregate Output: count(*) -> Append @@ -11699,10 +11699,10 @@ SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt W Output: t1.a, t1.b, t1.c -> Append -> Async Foreign Scan on public.async_p1 async_pt_1 - Output: async_pt_1.a, async_pt_1.b, async_pt_1.c, $0 + Output: async_pt_1.a, async_pt_1.b, async_pt_1.c, (InitPlan 1).col1 Remote SQL: SELECT a, b, c FROM public.base_tbl1 WHERE ((a < 3000)) -> Async Foreign Scan on public.async_p2 async_pt_2 - Output: async_pt_2.a, async_pt_2.b, async_pt_2.c, $0 + Output: async_pt_2.a, async_pt_2.b, async_pt_2.c, (InitPlan 1).col1 Remote SQL: SELECT a, b, c FROM public.base_tbl2 WHERE ((a < 3000)) (20 rows) @@ -11713,7 +11713,7 @@ SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt W Nested Loop Left Join (actual rows=1 loops=1) Join Filter: (t1.a = async_pt.a) Rows Removed by Join Filter: 399 - InitPlan 1 (returns $0) + InitPlan 1 -> Aggregate (actual rows=1 loops=1) -> Append (actual rows=400 loops=1) -> Async Foreign Scan on async_p1 async_pt_4 (actual rows=200 loops=1) @@ -11936,11 +11936,11 @@ CREATE FOREIGN TABLE foreign_tbl2 () INHERITS (foreign_tbl) SERVER loopback OPTIONS (table_name 'base_tbl'); EXPLAIN (VERBOSE, COSTS OFF) SELECT a FROM base_tbl WHERE (a, random() > 0) IN (SELECT a, random() > 0 FROM foreign_tbl); - QUERY PLAN ------------------------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- Seq Scan on public.base_tbl Output: base_tbl.a - Filter: (SubPlan 1) + Filter: (ANY ((base_tbl.a = (SubPlan 1).col1) AND ((random() > '0'::double precision) = (SubPlan 1).col2))) SubPlan 1 -> Result Output: base_tbl.a, (random() > '0'::double precision) diff --git a/doc/src/sgml/perform.sgml b/doc/src/sgml/perform.sgml index 90822b3f4c..a3ed0aff1f 100644 --- a/doc/src/sgml/perform.sgml +++ b/doc/src/sgml/perform.sgml @@ -573,8 +573,70 @@ WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2; which shows that the planner thinks that sorting <literal>onek</literal> by index-scanning is about 12% more expensive than sequential-scan-and-sort. Of course, the next question is whether it's right about that. - We can investigate that using <command>EXPLAIN ANALYZE</command>, as discussed - below. + We can investigate that using <command>EXPLAIN ANALYZE</command>, as + discussed <link linkend="using-explain-analyze">below</link>. + </para> + + <para> + Some query plans involve <firstterm>subplans</firstterm>, which arise + from sub-<literal>SELECT</literal>s in the original query. Such + queries can sometimes be transformed into ordinary join plans, but + when they cannot be, we get plans like: + +<screen> +EXPLAIN VERBOSE SELECT unique1 +FROM tenk1 t1 WHERE ROW(1, 2) = (SELECT t1.ten, t1.four); + + QUERY PLAN +------------------------------------------------------------&zwsp;-------- + Seq Scan on public.tenk1 t1 (cost=0.00..595.00 rows=5000 width=4) + Output: t1.unique1 + Filter: (((1 = (SubPlan 1).col1) AND (2 = (SubPlan 1).col2))) + SubPlan 1 + -> Result (cost=0.00..0.01 rows=1 width=8) + Output: t1.ten, t1.four +</screen> + + This simple example is artificial, of course; it could easily be + rewritten to not use a sub-<literal>SELECT</literal>. But it serves + to illustrate a couple of points: values from the outer plan level can + be passed down into a subplan (here, <literal>t1.ten</literal> + and <literal>t1.four</literal> are passed down) and the results of the + sub-select are available to the outer plan. Those result values are + shown by <command>EXPLAIN</command> with notations like + <literal>(<replaceable>subplan_name</replaceable>).col<replaceable>N</replaceable></literal>, + which refers to the <replaceable>N</replaceable>'th output column of + the sub-<literal>SELECT</literal>. + </para> + + <para> + <indexterm> + <primary>initplan</primary> + </indexterm> + A subplan like the above is run once for each row processed by the + outer plan. However, if the sub-<literal>SELECT</literal> does not + reference any variables of the outer query, it may instead be + implemented as an <firstterm>initplan</firstterm>: + +<screen> +EXPLAIN VERBOSE SELECT unique1 +FROM tenk1 t1 WHERE t1.ten = (SELECT (random() * 10)::integer); + + QUERY PLAN +------------------------------------------------------------&zwsp;-------- + Seq Scan on public.tenk1 t1 (cost=0.02..470.02 rows=1000 width=4) + Output: t1.unique1 + Filter: (t1.ten = (InitPlan 1).col1) + InitPlan 1 + -> Result (cost=0.00..0.02 rows=1 width=4) + Output: ((random() * '10'::double precision))::integer +</screen> + + An initplan is run only once per execution of the outer plan, so in + this example <literal>random()</literal> is evaluated only once and + all the values of <literal>t1.ten</literal> are compared to the same + randomly-chosen integer. That's quite different from what would + happen without the sub-<literal>SELECT</literal> construct. </para> </sect2> diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index b9e0c960bd..d6954a7e86 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -560,22 +560,9 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot, splan->plan_id); /* Label the subplan for EXPLAIN purposes */ - splan->plan_name = palloc(32 + 12 * list_length(splan->setParam)); - sprintf(splan->plan_name, "%s %d", - isInitPlan ? "InitPlan" : "SubPlan", - splan->plan_id); - if (splan->setParam) - { - char *ptr = splan->plan_name + strlen(splan->plan_name); - - ptr += sprintf(ptr, " (returns "); - foreach(lc, splan->setParam) - { - ptr += sprintf(ptr, "$%d%s", - lfirst_int(lc), - lnext(splan->setParam, lc) ? "," : ")"); - } - } + splan->plan_name = psprintf("%s %d", + isInitPlan ? "InitPlan" : "SubPlan", + splan->plan_id); /* Lastly, fill in the cost estimates for use later */ cost_subplan(root, splan, plan); @@ -3047,8 +3034,7 @@ SS_make_initplan_from_plan(PlannerInfo *root, node = makeNode(SubPlan); node->subLinkType = EXPR_SUBLINK; node->plan_id = list_length(root->glob->subplans); - node->plan_name = psprintf("InitPlan %d (returns $%d)", - node->plan_id, prm->paramid); + node->plan_name = psprintf("InitPlan %d", node->plan_id); get_first_col_type(plan, &node->firstColType, &node->firstColTypmod, &node->firstColCollation); node->parallel_safe = plan->parallel_safe; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index f2893d4086..564a29c097 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -437,6 +437,10 @@ static void resolve_special_varno(Node *node, deparse_context *context, rsv_callback callback, void *callback_arg); static Node *find_param_referent(Param *param, deparse_context *context, deparse_namespace **dpns_p, ListCell **ancestor_cell_p); +static SubPlan *find_param_generator(Param *param, deparse_context *context, + int *column_p); +static SubPlan *find_param_generator_initplan(Param *param, Plan *plan, + int *column_p); static void get_parameter(Param *param, deparse_context *context); static const char *get_simple_binary_op_name(OpExpr *expr); static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags); @@ -8134,6 +8138,128 @@ find_param_referent(Param *param, deparse_context *context, return NULL; } +/* + * Try to find a subplan/initplan that emits the value for a PARAM_EXEC Param. + * + * If successful, return the generating subplan/initplan and set *column_p + * to the subplan's 0-based output column number. + * Otherwise, return NULL. + */ +static SubPlan * +find_param_generator(Param *param, deparse_context *context, int *column_p) +{ + /* Initialize output parameter to prevent compiler warnings */ + *column_p = 0; + + /* + * If it's a PARAM_EXEC parameter, search the current plan node as well as + * ancestor nodes looking for a subplan or initplan that emits the value + * for the Param. It could appear in the setParams of an initplan or + * MULTIEXPR_SUBLINK subplan, or in the paramIds of an ancestral SubPlan. + */ + if (param->paramkind == PARAM_EXEC) + { + SubPlan *result; + deparse_namespace *dpns; + ListCell *lc; + + dpns = (deparse_namespace *) linitial(context->namespaces); + + /* First check the innermost plan node's initplans */ + result = find_param_generator_initplan(param, dpns->plan, column_p); + if (result) + return result; + + /* + * The plan's targetlist might contain MULTIEXPR_SUBLINK SubPlans, + * which can be referenced by Params elsewhere in the targetlist. + * (Such Params should always be in the same targetlist, so there's no + * need to do this work at upper plan nodes.) + */ + foreach_node(TargetEntry, tle, dpns->plan->targetlist) + { + if (tle->expr && IsA(tle->expr, SubPlan)) + { + SubPlan *subplan = (SubPlan *) tle->expr; + + if (subplan->subLinkType == MULTIEXPR_SUBLINK) + { + foreach_int(paramid, subplan->setParam) + { + if (paramid == param->paramid) + { + /* Found a match, so return it. */ + *column_p = foreach_current_index(paramid); + return subplan; + } + } + } + } + } + + /* No luck, so check the ancestor nodes */ + foreach(lc, dpns->ancestors) + { + Node *ancestor = (Node *) lfirst(lc); + + /* + * If ancestor is a SubPlan, check the paramIds it provides. + */ + if (IsA(ancestor, SubPlan)) + { + SubPlan *subplan = (SubPlan *) ancestor; + + foreach_int(paramid, subplan->paramIds) + { + if (paramid == param->paramid) + { + /* Found a match, so return it. */ + *column_p = foreach_current_index(paramid); + return subplan; + } + } + + /* SubPlan isn't a kind of Plan, so skip the rest */ + continue; + } + + /* + * Otherwise, it's some kind of Plan node, so check its initplans. + */ + result = find_param_generator_initplan(param, (Plan *) ancestor, + column_p); + if (result) + return result; + + /* No luck, crawl up to next ancestor */ + } + } + + /* No referent found */ + return NULL; +} + +/* + * Subroutine for find_param_generator: search one Plan node's initplans + */ +static SubPlan * +find_param_generator_initplan(Param *param, Plan *plan, int *column_p) +{ + foreach_node(SubPlan, subplan, plan->initPlan) + { + foreach_int(paramid, subplan->setParam) + { + if (paramid == param->paramid) + { + /* Found a match, so return it. */ + *column_p = foreach_current_index(paramid); + return subplan; + } + } + } + return NULL; +} + /* * Display a Param appropriately. */ @@ -8143,12 +8269,13 @@ get_parameter(Param *param, deparse_context *context) Node *expr; deparse_namespace *dpns; ListCell *ancestor_cell; + SubPlan *subplan; + int column; /* * If it's a PARAM_EXEC parameter, try to locate the expression from which - * the parameter was computed. Note that failing to find a referent isn't - * an error, since the Param might well be a subplan output rather than an - * input. + * the parameter was computed. This stanza handles only cases in which + * the Param represents an input to the subplan we are currently in. */ expr = find_param_referent(param, context, &dpns, &ancestor_cell); if (expr) @@ -8192,6 +8319,22 @@ get_parameter(Param *param, deparse_context *context) return; } + /* + * Alternatively, maybe it's a subplan output, which we print as a + * reference to the subplan. (We could drill down into the subplan and + * print the relevant targetlist expression, but that has been deemed too + * confusing since it would violate normal SQL scope rules.) + */ + subplan = find_param_generator(param, context, &column); + if (subplan) + { + appendStringInfo(context->buf, "(%s%s).col%d", + subplan->useHashTable ? "hashed " : "", + subplan->plan_name, column + 1); + + return; + } + /* * If it's an external parameter, see if the outermost namespace provides * function argument names. @@ -8240,7 +8383,12 @@ get_parameter(Param *param, deparse_context *context) /* * Not PARAM_EXEC, or couldn't find referent: just print $N. + * + * It's a bug if we get here for anything except PARAM_EXTERN Params, but + * in production builds printing $N seems more useful than failing. */ + Assert(param->paramkind == PARAM_EXTERN); + appendStringInfo(context->buf, "$%d", param->paramid); } @@ -8876,17 +9024,73 @@ get_rule_expr(Node *node, deparse_context *context, case T_SubPlan: { SubPlan *subplan = (SubPlan *) node; + bool show_subplan_name = true; + deparse_namespace *dpns; /* * We cannot see an already-planned subplan in rule deparsing, * only while EXPLAINing a query plan. We don't try to * reconstruct the original SQL, just reference the subplan - * that appears elsewhere in EXPLAIN's result. + * that appears elsewhere in EXPLAIN's result. It does seem + * useful to show the subLinkType and testexpr, however, and + * we also note whether the subplan will be hashed. */ - if (subplan->useHashTable) - appendStringInfo(buf, "(hashed %s)", subplan->plan_name); - else - appendStringInfo(buf, "(%s)", subplan->plan_name); + + /* Push SubPlan into ancestors list while deparsing testexpr */ + dpns = (deparse_namespace *) linitial(context->namespaces); + dpns->ancestors = lcons(subplan, dpns->ancestors); + + switch (subplan->subLinkType) + { + case EXISTS_SUBLINK: + appendStringInfoString(buf, "EXISTS("); + Assert(subplan->testexpr == NULL); + break; + case ALL_SUBLINK: + appendStringInfoString(buf, "(ALL "); + get_rule_expr(subplan->testexpr, context, showimplicit); + appendStringInfoChar(buf, ')'); + show_subplan_name = false; + break; + case ANY_SUBLINK: + appendStringInfoString(buf, "(ANY "); + get_rule_expr(subplan->testexpr, context, showimplicit); + appendStringInfoChar(buf, ')'); + show_subplan_name = false; + break; + case ROWCOMPARE_SUBLINK: + appendStringInfoChar(buf, '('); + get_rule_expr(subplan->testexpr, context, showimplicit); + appendStringInfoChar(buf, ')'); + show_subplan_name = false; + break; + case EXPR_SUBLINK: + case MULTIEXPR_SUBLINK: + /* No need to decorate these subplan references */ + appendStringInfoChar(buf, '('); + Assert(subplan->testexpr == NULL); + break; + case ARRAY_SUBLINK: + appendStringInfoString(buf, "ARRAY("); + Assert(subplan->testexpr == NULL); + break; + case CTE_SUBLINK: + /* This case is unreachable within expressions */ + appendStringInfoString(buf, "CTE("); + Assert(subplan->testexpr == NULL); + break; + } + + if (show_subplan_name) + { + if (subplan->useHashTable) + appendStringInfo(buf, "hashed %s)", subplan->plan_name); + else + appendStringInfo(buf, "%s)", subplan->plan_name); + } + + /* Restore state of ancestors list */ + dpns->ancestors = list_delete_first(dpns->ancestors); } break; diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index f86cf8d258..d54a255e58 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -738,7 +738,7 @@ select array(select sum(x+y) s QUERY PLAN ------------------------------------------------------------------- Function Scan on pg_catalog.generate_series x - Output: (SubPlan 1) + Output: ARRAY(SubPlan 1) Function Call: generate_series(1, 3) SubPlan 1 -> Sort @@ -915,7 +915,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan using tenk1_unique1 on tenk1 Index Cond: (unique1 IS NOT NULL) @@ -932,7 +932,7 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique1 on tenk1 Index Cond: (unique1 IS NOT NULL) @@ -949,7 +949,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique1 on tenk1 Index Cond: ((unique1 IS NOT NULL) AND (unique1 < 42)) @@ -966,7 +966,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique1 on tenk1 Index Cond: ((unique1 IS NOT NULL) AND (unique1 > 42)) @@ -989,7 +989,7 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique1 on tenk1 Index Cond: ((unique1 IS NOT NULL) AND (unique1 > 42000)) @@ -1008,7 +1008,7 @@ explain (costs off) QUERY PLAN ---------------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_thous_tenthous on tenk1 Index Cond: ((thousand = 33) AND (tenthous IS NOT NULL)) @@ -1025,7 +1025,7 @@ explain (costs off) QUERY PLAN -------------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan using tenk1_thous_tenthous on tenk1 Index Cond: ((thousand = 33) AND (tenthous IS NOT NULL)) @@ -1046,7 +1046,7 @@ explain (costs off) Seq Scan on int4_tbl SubPlan 2 -> Result - InitPlan 1 (returns $1) + InitPlan 1 -> Limit -> Index Only Scan using tenk1_unique1 on tenk1 Index Cond: ((unique1 IS NOT NULL) AND (unique1 > int4_tbl.f1)) @@ -1069,8 +1069,8 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------- HashAggregate - Group Key: $0 - InitPlan 1 (returns $0) + Group Key: (InitPlan 1).col1 + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1088,8 +1088,8 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------- Sort - Sort Key: ($0) - InitPlan 1 (returns $0) + Sort Key: ((InitPlan 1).col1) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1107,8 +1107,8 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------- Sort - Sort Key: ($0) - InitPlan 1 (returns $0) + Sort Key: ((InitPlan 1).col1) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1126,8 +1126,8 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------- Sort - Sort Key: (($0 + 1)) - InitPlan 1 (returns $0) + Sort Key: (((InitPlan 1).col1 + 1)) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1146,7 +1146,7 @@ explain (costs off) --------------------------------------------------------------------- Sort Sort Key: (generate_series(1, 3)) DESC - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1168,7 +1168,7 @@ explain (costs off) QUERY PLAN ---------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Result One-Time Filter: (100 IS NOT NULL) @@ -1199,7 +1199,7 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Merge Append Sort Key: minmaxtest.f1 @@ -1210,7 +1210,7 @@ explain (costs off) -> Index Only Scan Backward using minmaxtest2i on minmaxtest2 minmaxtest_3 Index Cond: (f1 IS NOT NULL) -> Index Only Scan using minmaxtest3i on minmaxtest3 minmaxtest_4 - InitPlan 2 (returns $1) + InitPlan 2 -> Limit -> Merge Append Sort Key: minmaxtest_5.f1 DESC @@ -1235,7 +1235,7 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------------------------------- Unique - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Merge Append Sort Key: minmaxtest.f1 @@ -1246,7 +1246,7 @@ explain (costs off) -> Index Only Scan Backward using minmaxtest2i on minmaxtest2 minmaxtest_3 Index Cond: (f1 IS NOT NULL) -> Index Only Scan using minmaxtest3i on minmaxtest3 minmaxtest_4 - InitPlan 2 (returns $1) + InitPlan 2 -> Limit -> Merge Append Sort Key: minmaxtest_5.f1 DESC @@ -1258,7 +1258,7 @@ explain (costs off) Index Cond: (f1 IS NOT NULL) -> Index Only Scan Backward using minmaxtest3i on minmaxtest3 minmaxtest_9 -> Sort - Sort Key: ($0), ($1) + Sort Key: ((InitPlan 1).col1), ((InitPlan 2).col1) -> Result (26 rows) diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index a3b9aaca84..e1f0660810 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -559,7 +559,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan using tenk1_unique1 on tenk1 Index Cond: (unique1 IS NOT NULL) @@ -2109,14 +2109,14 @@ order by a, b, c; -- test handling of outer GroupingFunc within subqueries explain (costs off) select (select grouping(v1)) from (values ((select 1))) v(v1) group by cube(v1); - QUERY PLAN ---------------------------- + QUERY PLAN +------------------------------- MixedAggregate - Hash Key: $2 + Hash Key: (InitPlan 3).col1 Group Key: () - InitPlan 1 (returns $1) + InitPlan 1 -> Result - InitPlan 3 (returns $2) + InitPlan 3 -> Result -> Result SubPlan 2 @@ -2132,12 +2132,12 @@ select (select grouping(v1)) from (values ((select 1))) v(v1) group by cube(v1); explain (costs off) select (select grouping(v1)) from (values ((select 1))) v(v1) group by v1; - QUERY PLAN ---------------------------- + QUERY PLAN +---------------- GroupAggregate - InitPlan 1 (returns $1) + InitPlan 1 -> Result - InitPlan 3 (returns $2) + InitPlan 3 -> Result -> Result SubPlan 2 diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index 130a924228..e7de471511 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -1643,8 +1643,8 @@ explain (verbose, costs off) select min(1-id) from matest0; QUERY PLAN --------------------------------------------------------------------------------- Result - Output: $0 - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1 + InitPlan 1 -> Limit Output: ((1 - matest0.id)) -> Result @@ -1800,7 +1800,7 @@ SELECT min(x) FROM QUERY PLAN -------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Merge Append Sort Key: a.unique1 @@ -1818,7 +1818,7 @@ SELECT min(y) FROM QUERY PLAN -------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Merge Append Sort Key: a.unique1 @@ -1898,19 +1898,19 @@ insert into inhpar select x, x::text from generate_series(1,5) x; insert into inhcld select x::text, x from generate_series(6,10) x; explain (verbose, costs off) update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1); - QUERY PLAN -------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------- Update on public.inhpar i Update on public.inhpar i_1 Update on public.inhcld i_2 -> Result - Output: $2, $3, (SubPlan 1 (returns $2,$3)), i.tableoid, i.ctid + Output: (SubPlan 1).col1, (SubPlan 1).col2, (SubPlan 1), i.tableoid, i.ctid -> Append -> Seq Scan on public.inhpar i_1 Output: i_1.f1, i_1.f2, i_1.tableoid, i_1.ctid -> Seq Scan on public.inhcld i_2 Output: i_2.f1, i_2.f2, i_2.tableoid, i_2.ctid - SubPlan 1 (returns $2,$3) + SubPlan 1 -> Limit Output: (i.f1), (((i.f2)::text || '-'::text)) -> Seq Scan on public.int4_tbl @@ -1946,21 +1946,21 @@ alter table inhpar attach partition inhcld2 for values from (5) to (100); insert into inhpar select x, x::text from generate_series(1,10) x; explain (verbose, costs off) update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1); - QUERY PLAN ------------------------------------------------------------------------------------ + QUERY PLAN +----------------------------------------------------------------------------------------------- Update on public.inhpar i Update on public.inhcld1 i_1 Update on public.inhcld2 i_2 -> Append -> Seq Scan on public.inhcld1 i_1 - Output: $2, $3, (SubPlan 1 (returns $2,$3)), i_1.tableoid, i_1.ctid - SubPlan 1 (returns $2,$3) + Output: (SubPlan 1).col1, (SubPlan 1).col2, (SubPlan 1), i_1.tableoid, i_1.ctid + SubPlan 1 -> Limit Output: (i_1.f1), (((i_1.f2)::text || '-'::text)) -> Seq Scan on public.int4_tbl Output: i_1.f1, ((i_1.f2)::text || '-'::text) -> Seq Scan on public.inhcld2 i_2 - Output: $2, $3, (SubPlan 1 (returns $2,$3)), i_2.tableoid, i_2.ctid + Output: (SubPlan 1).col1, (SubPlan 1).col2, (SubPlan 1), i_2.tableoid, i_2.ctid (13 rows) update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1); @@ -2845,11 +2845,11 @@ explain (costs off) select min(a), max(a) from parted_minmax where b = '12345'; QUERY PLAN ------------------------------------------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan using parted_minmax1i on parted_minmax1 parted_minmax Index Cond: ((a IS NOT NULL) AND (b = '12345'::text)) - InitPlan 2 (returns $1) + InitPlan 2 -> Limit -> Index Only Scan Backward using parted_minmax1i on parted_minmax1 parted_minmax_1 Index Cond: ((a IS NOT NULL) AND (b = '12345'::text)) diff --git a/src/test/regress/expected/insert_conflict.out b/src/test/regress/expected/insert_conflict.out index 563c5eb52a..701217ddbc 100644 --- a/src/test/regress/expected/insert_conflict.out +++ b/src/test/regress/expected/insert_conflict.out @@ -50,7 +50,7 @@ explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on con Insert on insertconflicttest Conflict Resolution: UPDATE Conflict Arbiter Indexes: op_index_key, collation_index_key, both_index_key - Conflict Filter: (SubPlan 1) + Conflict Filter: EXISTS(SubPlan 1) -> Result SubPlan 1 -> Index Only Scan using both_index_expr_key on insertconflicttest ii diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 9605400021..63cddac0d6 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -3028,10 +3028,10 @@ where unique1 in (select unique2 from tenk1 b); explain (costs off) select a.* from tenk1 a where unique1 not in (select unique2 from tenk1 b); - QUERY PLAN --------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------- Seq Scan on tenk1 a - Filter: (NOT (hashed SubPlan 1)) + Filter: (NOT (ANY (unique1 = (hashed SubPlan 1).col1))) SubPlan 1 -> Index Only Scan using tenk1_unique2 on tenk1 b (4 rows) @@ -5278,12 +5278,12 @@ explain (costs off) select a.unique1, b.unique2 from onek a left join onek b on a.unique1 = b.unique2 where (b.unique2, random() > 0) = any (select q1, random() > 0 from int8_tbl c where c.q1 < b.unique1); - QUERY PLAN ----------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------------ Hash Join Hash Cond: (b.unique2 = a.unique1) -> Seq Scan on onek b - Filter: (SubPlan 1) + Filter: (ANY ((unique2 = (SubPlan 1).col1) AND ((random() > '0'::double precision) = (SubPlan 1).col2))) SubPlan 1 -> Seq Scan on int8_tbl c Filter: (q1 < b.unique1) @@ -8262,8 +8262,8 @@ lateral (select * from int8_tbl t1, where q2 = (select greatest(t1.q1,t2.q2)) and (select v.id=0)) offset 0) ss2) ss where t1.q1 = ss.q2) ss0; - QUERY PLAN -------------------------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------- Nested Loop Output: "*VALUES*".column1, t1.q1, t1.q2, ss2.q1, ss2.q2 -> Seq Scan on public.int8_tbl t1 @@ -8277,20 +8277,20 @@ lateral (select * from int8_tbl t1, Filter: (t1.q1 = ss2.q2) -> Seq Scan on public.int8_tbl t2 Output: t2.q1, t2.q2 - Filter: (SubPlan 3) + Filter: (ANY ((t2.q1 = (SubPlan 3).col1) AND ((random() > '0'::double precision) = (SubPlan 3).col2))) SubPlan 3 -> Result Output: t3.q2, (random() > '0'::double precision) - One-Time Filter: $4 - InitPlan 1 (returns $2) + One-Time Filter: (InitPlan 2).col1 + InitPlan 1 -> Result Output: GREATEST(t1.q1, t2.q2) - InitPlan 2 (returns $4) + InitPlan 2 -> Result Output: ("*VALUES*".column1 = 0) -> Seq Scan on public.int8_tbl t3 Output: t3.q1, t3.q2 - Filter: (t3.q2 = $2) + Filter: (t3.q2 = (InitPlan 1).col1) (27 rows) select * from (values (0), (1)) v(id), diff --git a/src/test/regress/expected/memoize.out b/src/test/regress/expected/memoize.out index 1c8d996740..0fd103c06b 100644 --- a/src/test/regress/expected/memoize.out +++ b/src/test/regress/expected/memoize.out @@ -342,7 +342,7 @@ WHERE unique1 < 3 ---------------------------------------------------------------- Index Scan using tenk1_unique1 on tenk1 t0 Index Cond: (unique1 < 3) - Filter: (SubPlan 1) + Filter: EXISTS(SubPlan 1) SubPlan 1 -> Nested Loop -> Index Scan using tenk1_hundred on tenk1 t2 diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out index 07561f0240..cec7f11f9f 100644 --- a/src/test/regress/expected/merge.out +++ b/src/test/regress/expected/merge.out @@ -1697,10 +1697,10 @@ WHEN MATCHED AND t.c > s.cnt THEN -> Seq Scan on public.ref r_2 Output: r_2.ab, r_2.cd Filter: ((r_2.ab = (s.a + s.b)) AND (r_2.cd = (s.c - s.d))) - SubPlan 3 (returns $9,$10) + SubPlan 3 -> Result - Output: s.b, $8 - InitPlan 2 (returns $8) + Output: s.b, (InitPlan 2).col1 + InitPlan 2 -> Aggregate Output: count(*) -> Seq Scan on public.ref r_1 diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index bf0657b9f2..a856348ebd 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -2176,36 +2176,36 @@ explain (analyze, costs off, summary off, timing off) execute ab_q1 (2, 4); prepare ab_q2 (int, int) as select a from ab where a between $1 and $2 and b < (select 3); explain (analyze, costs off, summary off, timing off) execute ab_q2 (2, 2); - QUERY PLAN ---------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------- Append (actual rows=0 loops=1) Subplans Removed: 6 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on ab_a2_b1 ab_1 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b < $0)) + Filter: ((a >= $1) AND (a <= $2) AND (b < (InitPlan 1).col1)) -> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b < $0)) + Filter: ((a >= $1) AND (a <= $2) AND (b < (InitPlan 1).col1)) -> Seq Scan on ab_a2_b3 ab_3 (never executed) - Filter: ((a >= $1) AND (a <= $2) AND (b < $0)) + Filter: ((a >= $1) AND (a <= $2) AND (b < (InitPlan 1).col1)) (10 rows) -- As above, but swap the PARAM_EXEC Param to the first partition level prepare ab_q3 (int, int) as select a from ab where b between $1 and $2 and a < (select 3); explain (analyze, costs off, summary off, timing off) execute ab_q3 (2, 2); - QUERY PLAN ---------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------- Append (actual rows=0 loops=1) Subplans Removed: 6 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on ab_a1_b2 ab_1 (actual rows=0 loops=1) - Filter: ((b >= $1) AND (b <= $2) AND (a < $0)) + Filter: ((b >= $1) AND (b <= $2) AND (a < (InitPlan 1).col1)) -> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1) - Filter: ((b >= $1) AND (b <= $2) AND (a < $0)) + Filter: ((b >= $1) AND (b <= $2) AND (a < (InitPlan 1).col1)) -> Seq Scan on ab_a3_b2 ab_3 (never executed) - Filter: ((b >= $1) AND (b <= $2) AND (a < $0)) + Filter: ((b >= $1) AND (b <= $2) AND (a < (InitPlan 1).col1)) (10 rows) -- @@ -2411,12 +2411,12 @@ select explain_parallel_append('execute ab_q5 (33, 44, 55)'); -- Test Parallel Append with PARAM_EXEC Params select explain_parallel_append('select count(*) from ab where (a = (select 1) or a = (select 3)) and b = 2'); - explain_parallel_append ------------------------------------------------------------------------------- + explain_parallel_append +------------------------------------------------------------------------------------------------ Aggregate (actual rows=N loops=N) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=N loops=N) - InitPlan 2 (returns $1) + InitPlan 2 -> Result (actual rows=N loops=N) -> Gather (actual rows=N loops=N) Workers Planned: 2 @@ -2424,11 +2424,11 @@ select explain_parallel_append('select count(*) from ab where (a = (select 1) or Workers Launched: N -> Parallel Append (actual rows=N loops=N) -> Parallel Seq Scan on ab_a1_b2 ab_1 (actual rows=N loops=N) - Filter: ((b = 2) AND ((a = $0) OR (a = $1))) + Filter: ((b = 2) AND ((a = (InitPlan 1).col1) OR (a = (InitPlan 2).col1))) -> Parallel Seq Scan on ab_a2_b2 ab_2 (never executed) - Filter: ((b = 2) AND ((a = $0) OR (a = $1))) + Filter: ((b = 2) AND ((a = (InitPlan 1).col1) OR (a = (InitPlan 2).col1))) -> Parallel Seq Scan on ab_a3_b2 ab_3 (actual rows=N loops=N) - Filter: ((b = 2) AND ((a = $0) OR (a = $1))) + Filter: ((b = 2) AND ((a = (InitPlan 1).col1) OR (a = (InitPlan 2).col1))) (16 rows) -- Test pruning during parallel nested loop query @@ -2629,57 +2629,57 @@ select * from ab where a = (select max(a) from lprt_a) and b = (select max(a)-1 QUERY PLAN ------------------------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Aggregate (actual rows=1 loops=1) -> Seq Scan on lprt_a (actual rows=102 loops=1) - InitPlan 2 (returns $1) + InitPlan 2 -> Aggregate (actual rows=1 loops=1) -> Seq Scan on lprt_a lprt_a_1 (actual rows=102 loops=1) -> Bitmap Heap Scan on ab_a1_b1 ab_1 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a1_b1_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a1_b2 ab_2 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a1_b2_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a1_b3 ab_3 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a1_b3_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a2_b1 ab_4 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a2_b1_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a2_b2 ab_5 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a2_b2_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a2_b3 ab_6 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a2_b3_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a3_b1 ab_7 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a3_b1_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a3_b2 ab_8 (actual rows=0 loops=1) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a3_b2_a_idx (actual rows=0 loops=1) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a3_b3 ab_9 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a3_b3_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) (52 rows) -- Test run-time partition pruning with UNION ALL parents @@ -2688,42 +2688,42 @@ select * from (select * from ab where a = 1 union all select * from ab) ab where QUERY PLAN ------------------------------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Append (actual rows=0 loops=1) -> Bitmap Heap Scan on ab_a1_b1 ab_11 (actual rows=0 loops=1) Recheck Cond: (a = 1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> 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_12 (never executed) Recheck Cond: (a = 1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Bitmap Index Scan on ab_a1_b2_a_idx (never executed) Index Cond: (a = 1) -> Bitmap Heap Scan on ab_a1_b3 ab_13 (never executed) Recheck Cond: (a = 1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Bitmap Index Scan on ab_a1_b3_a_idx (never executed) Index Cond: (a = 1) -> Seq Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a1_b2 ab_2 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a1_b3 ab_3 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b1 ab_4 (actual rows=0 loops=1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b2 ab_5 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b3 ab_6 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a3_b1 ab_7 (actual rows=0 loops=1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a3_b2 ab_8 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a3_b3 ab_9 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) (37 rows) -- A case containing a UNION ALL with a non-partitioned child. @@ -2732,44 +2732,44 @@ select * from (select * from ab where a = 1 union all (values(10,5)) union all s QUERY PLAN ------------------------------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Append (actual rows=0 loops=1) -> Bitmap Heap Scan on ab_a1_b1 ab_11 (actual rows=0 loops=1) Recheck Cond: (a = 1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> 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_12 (never executed) Recheck Cond: (a = 1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Bitmap Index Scan on ab_a1_b2_a_idx (never executed) Index Cond: (a = 1) -> Bitmap Heap Scan on ab_a1_b3 ab_13 (never executed) Recheck Cond: (a = 1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Bitmap Index Scan on ab_a1_b3_a_idx (never executed) Index Cond: (a = 1) -> Result (actual rows=0 loops=1) - One-Time Filter: (5 = $0) + One-Time Filter: (5 = (InitPlan 1).col1) -> Seq Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a1_b2 ab_2 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a1_b3 ab_3 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b1 ab_4 (actual rows=0 loops=1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b2 ab_5 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b3 ab_6 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a3_b1 ab_7 (actual rows=0 loops=1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a3_b2 ab_8 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a3_b3 ab_9 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) (39 rows) -- Another UNION ALL test, but containing a mix of exec init and exec run-time pruning. @@ -2787,27 +2787,27 @@ union all ) ab where a = $1 and b = (select -10); -- Ensure the xy_1 subplan is not pruned. explain (analyze, costs off, summary off, timing off) execute ab_q6(1); - QUERY PLAN --------------------------------------------------- + QUERY PLAN +-------------------------------------------------------- Append (actual rows=0 loops=1) Subplans Removed: 12 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on ab_a1_b1 ab_1 (never executed) - Filter: ((a = $1) AND (b = $0)) + Filter: ((a = $1) AND (b = (InitPlan 1).col1)) -> Seq Scan on ab_a1_b2 ab_2 (never executed) - Filter: ((a = $1) AND (b = $0)) + Filter: ((a = $1) AND (b = (InitPlan 1).col1)) -> Seq Scan on ab_a1_b3 ab_3 (never executed) - Filter: ((a = $1) AND (b = $0)) + Filter: ((a = $1) AND (b = (InitPlan 1).col1)) -> Seq Scan on xy_1 (actual rows=0 loops=1) - Filter: ((x = $1) AND (y = $0)) + Filter: ((x = $1) AND (y = (InitPlan 1).col1)) Rows Removed by Filter: 1 -> Seq Scan on ab_a1_b1 ab_4 (never executed) - Filter: ((a = $1) AND (b = $0)) + Filter: ((a = $1) AND (b = (InitPlan 1).col1)) -> Seq Scan on ab_a1_b2 ab_5 (never executed) - Filter: ((a = $1) AND (b = $0)) + Filter: ((a = $1) AND (b = (InitPlan 1).col1)) -> Seq Scan on ab_a1_b3 ab_6 (never executed) - Filter: ((a = $1) AND (b = $0)) + Filter: ((a = $1) AND (b = (InitPlan 1).col1)) (19 rows) -- Ensure we see just the xy_1 row. @@ -2884,7 +2884,7 @@ update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 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 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Nested Loop (actual rows=3 loops=1) -> Append (actual rows=3 loops=1) @@ -2894,11 +2894,11 @@ update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 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) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b2 ab_a2_2 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b3 ab_a2_3 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) (19 rows) select tableoid::regclass, * from ab; @@ -3232,12 +3232,12 @@ select * from listp where a = (select null::int); QUERY PLAN ------------------------------------------------------ Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on listp_1_1 listp_1 (never executed) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) -> Seq Scan on listp_2_1 listp_2 (never executed) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) (7 rows) drop table listp; @@ -3375,14 +3375,14 @@ prepare ps1 as select * from mc3p where a = $1 and abs(b) < (select 3); explain (analyze, costs off, summary off, timing off) execute ps1(1); - QUERY PLAN --------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------- Append (actual rows=1 loops=1) Subplans Removed: 2 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on mc3p1 mc3p_1 (actual rows=1 loops=1) - Filter: ((a = $1) AND (abs(b) < $0)) + Filter: ((a = $1) AND (abs(b) < (InitPlan 1).col1)) (6 rows) deallocate ps1; @@ -3390,16 +3390,16 @@ prepare ps2 as select * from mc3p where a <= $1 and abs(b) < (select 3); explain (analyze, costs off, summary off, timing off) execute ps2(1); - QUERY PLAN --------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------- Append (actual rows=2 loops=1) Subplans Removed: 1 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on mc3p0 mc3p_1 (actual rows=1 loops=1) - Filter: ((a <= $1) AND (abs(b) < $0)) + Filter: ((a <= $1) AND (abs(b) < (InitPlan 1).col1)) -> Seq Scan on mc3p1 mc3p_2 (actual rows=1 loops=1) - Filter: ((a <= $1) AND (abs(b) < $0)) + Filter: ((a <= $1) AND (abs(b) < (InitPlan 1).col1)) (8 rows) deallocate ps2; @@ -3415,14 +3415,14 @@ select * from boolp where a = (select value from boolvalues where value); QUERY PLAN ----------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Seq Scan on boolvalues (actual rows=1 loops=1) Filter: value Rows Removed by Filter: 1 -> Seq Scan on boolp_f boolp_1 (never executed) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) -> Seq Scan on boolp_t boolp_2 (actual rows=0 loops=1) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) (9 rows) explain (analyze, costs off, summary off, timing off) @@ -3430,14 +3430,14 @@ select * from boolp where a = (select value from boolvalues where not value); QUERY PLAN ----------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Seq Scan on boolvalues (actual rows=1 loops=1) Filter: (NOT value) Rows Removed by Filter: 1 -> Seq Scan on boolp_f boolp_1 (actual rows=0 loops=1) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) -> Seq Scan on boolp_t boolp_2 (never executed) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) (9 rows) drop table boolp; @@ -3526,18 +3526,18 @@ explain (analyze, costs off, summary off, timing off) select * from ma_test wher ----------------------------------------------------------------------------------------------- Merge Append (actual rows=20 loops=1) Sort Key: ma_test.b - InitPlan 2 (returns $1) + InitPlan 2 -> Result (actual rows=1 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Limit (actual rows=1 loops=1) -> Index Scan using ma_test_p2_b_idx on ma_test_p2 (actual rows=1 loops=1) Index Cond: (b IS NOT NULL) -> Index Scan using ma_test_p1_b_idx on ma_test_p1 ma_test_1 (never executed) - Filter: (a >= $1) + Filter: (a >= (InitPlan 2).col1) -> Index Scan using ma_test_p2_b_idx on ma_test_p2 ma_test_2 (actual rows=10 loops=1) - Filter: (a >= $1) + Filter: (a >= (InitPlan 2).col1) -> Index Scan using ma_test_p3_b_idx on ma_test_p3 ma_test_3 (actual rows=10 loops=1) - Filter: (a >= $1) + Filter: (a >= (InitPlan 2).col1) (14 rows) reset enable_seqscan; @@ -3906,17 +3906,17 @@ from ( select 1, 1, 1 ) s(a, b, c) where s.a = 1 and s.b = 1 and s.c = (select 1); - QUERY PLAN ----------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------- Append - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Seq Scan on p1 p - Filter: ((a = 1) AND (b = 1) AND (c = $0)) + Filter: ((a = 1) AND (b = 1) AND (c = (InitPlan 1).col1)) -> Seq Scan on q111 q1 - Filter: ((a = 1) AND (b = 1) AND (c = $0)) + Filter: ((a = 1) AND (b = 1) AND (c = (InitPlan 1).col1)) -> Result - One-Time Filter: (1 = $0) + One-Time Filter: (1 = (InitPlan 1).col1) (9 rows) select * @@ -3944,18 +3944,18 @@ from ( ) s(a, b, c) where s.a = $1 and s.b = $2 and s.c = (select 1); explain (costs off) execute q (1, 1); - QUERY PLAN ---------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------ Append Subplans Removed: 1 - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Seq Scan on p1 p - Filter: ((a = $1) AND (b = $2) AND (c = $0)) + Filter: ((a = $1) AND (b = $2) AND (c = (InitPlan 1).col1)) -> Seq Scan on q111 q1 - Filter: ((a = $1) AND (b = $2) AND (c = $0)) + Filter: ((a = $1) AND (b = $2) AND (c = (InitPlan 1).col1)) -> Result - One-Time Filter: ((1 = $1) AND (1 = $2) AND (1 = $0)) + One-Time Filter: ((1 = $1) AND (1 = $2) AND (1 = (InitPlan 1).col1)) (10 rows) execute q (1, 1); @@ -3973,11 +3973,11 @@ create table listp2 partition of listp for values in(2) partition by list(b); create table listp2_10 partition of listp2 for values in (10); explain (analyze, costs off, summary off, timing off) select * from listp where a = (select 2) and b <> 10; - QUERY PLAN --------------------------------------------------- + QUERY PLAN +--------------------------------------------------- Seq Scan on listp1 listp (actual rows=0 loops=1) - Filter: ((b <> 10) AND (a = $0)) - InitPlan 1 (returns $0) + Filter: ((b <> 10) AND (a = (InitPlan 1).col1)) + InitPlan 1 -> Result (never executed) (4 rows) @@ -4044,13 +4044,13 @@ select explain_parallel_append('select * from listp where a = (select 1);'); Workers Planned: 2 Params Evaluated: $0 Workers Launched: N - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=N loops=N) -> Parallel Append (actual rows=N loops=N) -> Seq Scan on listp_12_1 listp_1 (actual rows=N loops=N) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) -> Parallel Seq Scan on listp_12_2 listp_2 (never executed) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) (11 rows) -- Like the above but throw some more complexity at the planner by adding @@ -4067,19 +4067,19 @@ select * from listp where a = (select 2);'); Workers Launched: N -> Parallel Append (actual rows=N loops=N) -> Parallel Append (actual rows=N loops=N) - InitPlan 2 (returns $1) + InitPlan 2 -> Result (actual rows=N loops=N) -> Seq Scan on listp_12_1 listp_1 (never executed) - Filter: (a = $1) + Filter: (a = (InitPlan 2).col1) -> Parallel Seq Scan on listp_12_2 listp_2 (actual rows=N loops=N) - Filter: (a = $1) + Filter: (a = (InitPlan 2).col1) -> Parallel Append (actual rows=N loops=N) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=N loops=N) -> Seq Scan on listp_12_1 listp_4 (actual rows=N loops=N) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) -> Parallel Seq Scan on listp_12_2 listp_5 (never executed) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) (18 rows) drop table listp; @@ -4102,20 +4102,20 @@ select * from rangep where b IN((select 1),(select 2)) order by a; QUERY PLAN ------------------------------------------------------------------------------------------------------------ Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) - InitPlan 2 (returns $1) + InitPlan 2 -> Result (actual rows=1 loops=1) -> Merge Append (actual rows=0 loops=1) Sort Key: rangep_2.a -> Index Scan using rangep_0_to_100_1_a_idx on rangep_0_to_100_1 rangep_2 (actual rows=0 loops=1) - Filter: (b = ANY (ARRAY[$0, $1])) + Filter: (b = ANY (ARRAY[(InitPlan 1).col1, (InitPlan 2).col1])) -> Index Scan using rangep_0_to_100_2_a_idx on rangep_0_to_100_2 rangep_3 (actual rows=0 loops=1) - Filter: (b = ANY (ARRAY[$0, $1])) + Filter: (b = ANY (ARRAY[(InitPlan 1).col1, (InitPlan 2).col1])) -> Index Scan using rangep_0_to_100_3_a_idx on rangep_0_to_100_3 rangep_4 (never executed) - Filter: (b = ANY (ARRAY[$0, $1])) + Filter: (b = ANY (ARRAY[(InitPlan 1).col1, (InitPlan 2).col1])) -> Index Scan using rangep_100_to_200_a_idx on rangep_100_to_200 rangep_5 (actual rows=0 loops=1) - Filter: (b = ANY (ARRAY[$0, $1])) + Filter: (b = ANY (ARRAY[(InitPlan 1).col1, (InitPlan 2).col1])) (15 rows) reset enable_sort; diff --git a/src/test/regress/expected/portals.out b/src/test/regress/expected/portals.out index f71e0b3d41..06726ed4ab 100644 --- a/src/test/regress/expected/portals.out +++ b/src/test/regress/expected/portals.out @@ -1472,18 +1472,18 @@ rollback; -- Check handling of non-backwards-scan-capable plans with scroll cursors begin; explain (costs off) declare c1 cursor for select (select 42) as x; - QUERY PLAN ---------------------------- + QUERY PLAN +---------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Result (3 rows) explain (costs off) declare c1 scroll cursor for select (select 42) as x; - QUERY PLAN ---------------------------- + QUERY PLAN +---------------- Materialize - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Result (4 rows) diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index d507a2c8ca..527cf7e7bf 100644 --- a/src/test/regress/expected/rowsecurity.out +++ b/src/test/regress/expected/rowsecurity.out @@ -265,27 +265,27 @@ NOTICE: f_leak => awesome science fiction (5 rows) EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle); - QUERY PLAN ----------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------- Seq Scan on document - Filter: ((dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) (5 rows) EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle); - QUERY PLAN ------------------------------------------------------------ + QUERY PLAN +-------------------------------------------------------------------------- Hash Join Hash Cond: (category.cid = document.cid) - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on category -> Hash -> Seq Scan on document - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) (9 rows) -- viewpoint from regress_rls_dave @@ -329,27 +329,27 @@ NOTICE: f_leak => awesome technology book (7 rows) EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle); - QUERY PLAN ----------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------- Seq Scan on document - Filter: ((cid <> 44) AND (cid <> 44) AND (cid < 50) AND (dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) + Filter: ((cid <> 44) AND (cid <> 44) AND (cid < 50) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) (5 rows) EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle); - QUERY PLAN ----------------------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------- Hash Join Hash Cond: (category.cid = document.cid) - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on category -> Hash -> Seq Scan on document - Filter: ((cid <> 44) AND (cid <> 44) AND (cid < 50) AND (dlevel <= $0) AND f_leak(dtitle)) + Filter: ((cid <> 44) AND (cid <> 44) AND (cid < 50) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) (9 rows) -- 44 would technically fail for both p2r and p1r, but we should get an error @@ -987,18 +987,18 @@ NOTICE: f_leak => my first satire (4 rows) EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN ------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------- Append - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on part_document_fiction part_document_1 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) -> Seq Scan on part_document_satire part_document_2 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) -> Seq Scan on part_document_nonfiction part_document_3 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) (10 rows) -- viewpoint from regress_rls_carol @@ -1029,18 +1029,18 @@ NOTICE: f_leak => awesome technology book (10 rows) EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN ------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------- Append - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on part_document_fiction part_document_1 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) -> Seq Scan on part_document_satire part_document_2 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) -> Seq Scan on part_document_nonfiction part_document_3 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) (10 rows) -- viewpoint from regress_rls_dave @@ -1059,11 +1059,11 @@ NOTICE: f_leak => awesome science fiction (4 rows) EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN --------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------- Seq Scan on part_document_fiction part_document - Filter: ((cid < 55) AND (dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) + Filter: ((cid < 55) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) (5 rows) @@ -1137,11 +1137,11 @@ NOTICE: f_leak => awesome science fiction (4 rows) EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN --------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------- Seq Scan on part_document_fiction part_document - Filter: ((cid < 55) AND (dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) + Filter: ((cid < 55) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) (5 rows) @@ -1176,18 +1176,18 @@ NOTICE: f_leak => awesome technology book (11 rows) EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN ------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------- Append - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on part_document_fiction part_document_1 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) -> Seq Scan on part_document_satire part_document_2 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) -> Seq Scan on part_document_nonfiction part_document_3 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) (10 rows) -- only owner can change policies @@ -1437,10 +1437,10 @@ NOTICE: f_leak => 03b26944890929ff751653acb2f2af79 (1 row) EXPLAIN (COSTS OFF) SELECT * FROM only s1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------- Seq Scan on s1 - Filter: ((hashed SubPlan 1) AND f_leak(b)) + Filter: ((ANY (a = (hashed SubPlan 1).col1)) AND f_leak(b)) SubPlan 1 -> Seq Scan on s2 Filter: (((x % 2) = 0) AND (y ~~ '%2f%'::text)) @@ -1457,10 +1457,10 @@ NOTICE: f_leak => 03b26944890929ff751653acb2f2af79 (1 row) EXPLAIN (COSTS OFF) SELECT * FROM s1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------- Seq Scan on s1 - Filter: ((hashed SubPlan 1) AND f_leak(b)) + Filter: ((ANY (a = (hashed SubPlan 1).col1)) AND f_leak(b)) SubPlan 1 -> Seq Scan on s2 Filter: (((x % 2) = 0) AND (y ~~ '%af%'::text)) @@ -1480,7 +1480,7 @@ EXPLAIN (COSTS OFF) SELECT (SELECT x FROM s1 LIMIT 1) xx, * FROM s2 WHERE y like SubPlan 2 -> Limit -> Seq Scan on s1 - Filter: (hashed SubPlan 1) + Filter: (ANY (a = (hashed SubPlan 1).col1)) SubPlan 1 -> Seq Scan on s2 s2_1 Filter: (((x % 2) = 0) AND (y ~~ '%af%'::text)) @@ -2717,10 +2717,10 @@ NOTICE: f_leak => bbb (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 0) AND f_leak(b)) + Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 0) AND f_leak(b)) SubPlan 1 -> Seq Scan on z1_blacklist (4 rows) @@ -2735,10 +2735,10 @@ NOTICE: f_leak => bbb (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 0) AND f_leak(b)) + Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 0) AND f_leak(b)) SubPlan 1 -> Seq Scan on z1_blacklist (4 rows) @@ -2907,10 +2907,10 @@ NOTICE: f_leak => bbb (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 0) AND f_leak(b)) + Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 0) AND f_leak(b)) SubPlan 1 -> Seq Scan on z1_blacklist (4 rows) @@ -2933,10 +2933,10 @@ NOTICE: f_leak => aba (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 1) AND f_leak(b)) + Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 1) AND f_leak(b)) SubPlan 1 -> Seq Scan on z1_blacklist (4 rows) diff --git a/src/test/regress/expected/rowtypes.out b/src/test/regress/expected/rowtypes.out index 8f3c153bac..b400b58f76 100644 --- a/src/test/regress/expected/rowtypes.out +++ b/src/test/regress/expected/rowtypes.out @@ -1249,8 +1249,8 @@ with cte(c) as materialized (select row(1, 2)), select * from cte2 as t where (select * from (select c as c1) s where (select (c1).f1 > 0)) is not null; - QUERY PLAN --------------------------------------------- + QUERY PLAN +---------------------------------------------- CTE Scan on cte Output: cte.c Filter: ((SubPlan 3) IS NOT NULL) @@ -1260,8 +1260,8 @@ where (select * from (select c as c1) s SubPlan 3 -> Result Output: cte.c - One-Time Filter: $2 - InitPlan 2 (returns $2) + One-Time Filter: (InitPlan 2).col1 + InitPlan 2 -> Result Output: ((cte.c).f1 > 0) (13 rows) diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out index 7a0d78dfe3..923d7c830b 100644 --- a/src/test/regress/expected/select_parallel.out +++ b/src/test/regress/expected/select_parallel.out @@ -291,14 +291,14 @@ alter table tenk2 set (parallel_workers = 0); explain (costs off) select count(*) from tenk1 where (two, four) not in (select hundred, thousand from tenk2 where thousand > 100); - QUERY PLAN ------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------------------------------- Finalize Aggregate -> Gather Workers Planned: 4 -> Partial Aggregate -> Parallel Seq Scan on tenk1 - Filter: (NOT (hashed SubPlan 1)) + Filter: (NOT (ANY ((two = (hashed SubPlan 1).col1) AND (four = (hashed SubPlan 1).col2)))) SubPlan 1 -> Seq Scan on tenk2 Filter: (thousand > 100) @@ -315,10 +315,10 @@ select count(*) from tenk1 where (two, four) not in explain (costs off) select * from tenk1 where (unique1 + random())::integer not in (select ten from tenk2); - QUERY PLAN ------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------- Seq Scan on tenk1 - Filter: (NOT (hashed SubPlan 1)) + Filter: (NOT (ANY ((((unique1)::double precision + random()))::integer = (hashed SubPlan 1).col1))) SubPlan 1 -> Seq Scan on tenk2 (4 rows) @@ -335,7 +335,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------ Aggregate - InitPlan 1 (returns $2) + InitPlan 1 -> Finalize Aggregate -> Gather Workers Planned: 2 @@ -345,7 +345,7 @@ explain (costs off) Workers Planned: 4 Params Evaluated: $2 -> Parallel Seq Scan on tenk1 - Filter: (unique1 = $2) + Filter: (unique1 = (InitPlan 1).col1) (12 rows) select count(*) from tenk1 @@ -1151,25 +1151,25 @@ ORDER BY 1; -> Gather Workers Planned: 4 Params Evaluated: $1 - InitPlan 1 (returns $1) + InitPlan 1 -> Limit -> Gather Workers Planned: 4 -> Parallel Seq Scan on tenk1 tenk1_2 Filter: (fivethous = 1) -> Parallel Seq Scan on tenk1 - Filter: (fivethous = $1) + Filter: (fivethous = (InitPlan 1).col1) -> Gather Workers Planned: 4 Params Evaluated: $3 - InitPlan 2 (returns $3) + InitPlan 2 -> Limit -> Gather Workers Planned: 4 -> Parallel Seq Scan on tenk1 tenk1_3 Filter: (fivethous = 1) -> Parallel Seq Scan on tenk1 tenk1_1 - Filter: (fivethous = $3) + Filter: (fivethous = (InitPlan 2).col1) (25 rows) -- test interaction with SRFs @@ -1182,10 +1182,10 @@ ORDER BY 1, 2, 3; EXPLAIN (VERBOSE, COSTS OFF) SELECT generate_series(1, two), array(select generate_series(1, two)) FROM tenk1 ORDER BY tenthous; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------- ProjectSet - Output: generate_series(1, tenk1.two), (SubPlan 1), tenk1.tenthous + Output: generate_series(1, tenk1.two), ARRAY(SubPlan 1), tenk1.tenthous -> Gather Merge Output: tenk1.two, tenk1.tenthous Workers Planned: 4 diff --git a/src/test/regress/expected/sqljson.out b/src/test/regress/expected/sqljson.out index 5e7da96be5..cbf8542d8d 100644 --- a/src/test/regress/expected/sqljson.out +++ b/src/test/regress/expected/sqljson.out @@ -1064,8 +1064,8 @@ SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i) RETURNING QUERY PLAN --------------------------------------------------------------------- Result - Output: $0 - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1 + InitPlan 1 -> Aggregate Output: JSON_ARRAYAGG("*VALUES*".column1 RETURNING jsonb) -> Values Scan on "*VALUES*" diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out index e41b728df8..29b11f11aa 100644 --- a/src/test/regress/expected/subselect.out +++ b/src/test/regress/expected/subselect.out @@ -202,6 +202,57 @@ SELECT f1 AS "Correlated Field" 3 (5 rows) +-- Check ROWCOMPARE cases, both correlated and not +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + QUERY PLAN +----------------------------------------------------------------- + Seq Scan on public.subselect_tbl + Output: (((1 = (SubPlan 1).col1) AND (2 = (SubPlan 1).col2))) + SubPlan 1 + -> Result + Output: subselect_tbl.f1, subselect_tbl.f2 +(5 rows) + +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + eq +---- + t + f + f + f + f + f + f + f +(8 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + QUERY PLAN +----------------------------------------------------------------- + Seq Scan on public.subselect_tbl + Output: ((1 = (InitPlan 1).col1) AND (2 = (InitPlan 1).col2)) + InitPlan 1 + -> Result + Output: 3, 4 +(5 rows) + +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + eq +---- + f + f + f + f + f + f + f + f +(8 rows) + +SELECT ROW(1, 2) = (SELECT f1, f2 FROM SUBSELECT_TBL); -- error +ERROR: more than one row returned by a subquery used as an expression -- Subselects without aliases SELECT count FROM (SELECT COUNT(DISTINCT name) FROM road); count @@ -324,18 +375,18 @@ explain (verbose, costs off) select '42' union all select 43; -- check materialization of an initplan reference (bug #14524) explain (verbose, costs off) select 1 = all (select (select 1)); - QUERY PLAN ------------------------------------ + QUERY PLAN +------------------------------------------- Result - Output: (SubPlan 2) + Output: (ALL (1 = (SubPlan 2).col1)) SubPlan 2 -> Materialize - Output: ($0) - InitPlan 1 (returns $0) + Output: ((InitPlan 1).col1) + InitPlan 1 -> Result Output: 1 -> Result - Output: $0 + Output: (InitPlan 1).col1 (10 rows) select 1 = all (select (select 1)); @@ -377,7 +428,7 @@ select * from int4_tbl o where exists QUERY PLAN -------------------------------------- Seq Scan on int4_tbl o - Filter: (SubPlan 1) + Filter: EXISTS(SubPlan 1) SubPlan 1 -> Limit -> Seq Scan on int4_tbl i @@ -840,10 +891,10 @@ select * from outer_text where (f1, f2) not in (select * from inner_text); -- explain (verbose, costs off) select 'foo'::text in (select 'bar'::name union all select 'bar'::name); - QUERY PLAN -------------------------------------- + QUERY PLAN +--------------------------------------------------------- Result - Output: (hashed SubPlan 1) + Output: (ANY ('foo'::text = (hashed SubPlan 1).col1)) SubPlan 1 -> Append -> Result @@ -864,10 +915,10 @@ select 'foo'::text in (select 'bar'::name union all select 'bar'::name); -- explain (verbose, costs off) select row(row(row(1))) = any (select row(row(1))); - QUERY PLAN -------------------------------------------- + QUERY PLAN +-------------------------------------------------------- Result - Output: (SubPlan 1) + Output: (ANY ('("(1)")'::record = (SubPlan 1).col1)) SubPlan 1 -> Materialize Output: '("(1)")'::record @@ -907,10 +958,10 @@ language sql as 'select $1::text = $2'; create operator = (procedure=bogus_int8_text_eq, leftarg=int8, rightarg=text); explain (costs off) select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------- + QUERY PLAN +-------------------------------------------------------- Seq Scan on int8_tbl - Filter: (hashed SubPlan 1) + Filter: (ANY ((q1)::text = (hashed SubPlan 1).col1)) SubPlan 1 -> Seq Scan on inner_text (4 rows) @@ -928,10 +979,10 @@ create or replace function bogus_int8_text_eq(int8, text) returns boolean language sql as 'select $1::text = $2 and $1::text = $2'; explain (costs off) select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------- Seq Scan on int8_tbl - Filter: (hashed SubPlan 1) + Filter: (ANY (((q1)::text = (hashed SubPlan 1).col1) AND ((q1)::text = (hashed SubPlan 1).col1))) SubPlan 1 -> Seq Scan on inner_text (4 rows) @@ -949,10 +1000,10 @@ create or replace function bogus_int8_text_eq(int8, text) returns boolean language sql as 'select $2 = $1::text'; explain (costs off) select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------------- + QUERY PLAN +------------------------------------------------- Seq Scan on int8_tbl - Filter: (SubPlan 1) + Filter: (ANY ((SubPlan 1).col1 = (q1)::text)) SubPlan 1 -> Materialize -> Seq Scan on inner_text @@ -972,11 +1023,11 @@ rollback; -- to get rid of the bogus operator explain (costs off) select count(*) from tenk1 t where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0); - QUERY PLAN --------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------------- Aggregate -> Seq Scan on tenk1 t - Filter: ((hashed SubPlan 2) OR (ten < 0)) + Filter: ((ANY (unique2 = (hashed SubPlan 2).col1)) OR (ten < 0)) SubPlan 2 -> Index Only Scan using tenk1_unique1 on tenk1 k (5 rows) @@ -997,7 +1048,7 @@ where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0) Aggregate -> Bitmap Heap Scan on tenk1 t Recheck Cond: (thousand = 1) - Filter: ((SubPlan 1) OR (ten < 0)) + Filter: (EXISTS(SubPlan 1) OR (ten < 0)) -> Bitmap Index Scan on tenk1_thous_tenthous Index Cond: (thousand = 1) SubPlan 1 @@ -1022,11 +1073,11 @@ analyze exists_tbl; explain (costs off) select * from exists_tbl t1 where (exists(select 1 from exists_tbl t2 where t1.c1 = t2.c2) or c3 < 0); - QUERY PLAN ------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------- Append -> Seq Scan on exists_tbl_null t1_1 - Filter: ((SubPlan 1) OR (c3 < 0)) + Filter: (EXISTS(SubPlan 1) OR (c3 < 0)) SubPlan 1 -> Append -> Seq Scan on exists_tbl_null t2_1 @@ -1034,7 +1085,7 @@ select * from exists_tbl t1 -> Seq Scan on exists_tbl_def t2_2 Filter: (t1_1.c1 = c2) -> Seq Scan on exists_tbl_def t1_2 - Filter: ((hashed SubPlan 2) OR (c3 < 0)) + Filter: ((ANY (c1 = (hashed SubPlan 2).col1)) OR (c3 < 0)) SubPlan 2 -> Append -> Seq Scan on exists_tbl_null t2_4 @@ -1071,14 +1122,14 @@ where a.thousand = b.thousand explain (verbose, costs off) select x, x from (select (select now()) as x from (values(1),(2)) v(y)) ss; - QUERY PLAN ---------------------------- + QUERY PLAN +------------------------------------------------ Values Scan on "*VALUES*" - Output: $0, $1 - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1, (InitPlan 2).col1 + InitPlan 1 -> Result Output: now() - InitPlan 2 (returns $1) + InitPlan 2 -> Result Output: now() (8 rows) @@ -1086,13 +1137,13 @@ explain (verbose, costs off) explain (verbose, costs off) select x, x from (select (select random()) as x from (values(1),(2)) v(y)) ss; - QUERY PLAN ----------------------------------- + QUERY PLAN +----------------------------------- Subquery Scan on ss Output: ss.x, ss.x -> Values Scan on "*VALUES*" - Output: $0 - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1 + InitPlan 1 -> Result Output: random() (7 rows) @@ -1143,14 +1194,14 @@ where o.ten = 0; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Aggregate - Output: sum((((hashed SubPlan 1)))::integer) + Output: sum((((ANY (i.ten = (hashed SubPlan 1).col1))))::integer) -> Nested Loop - Output: ((hashed SubPlan 1)) + Output: ((ANY (i.ten = (hashed SubPlan 1).col1))) -> Seq Scan on public.onek o Output: o.unique1, o.unique2, o.two, o.four, o.ten, o.twenty, o.hundred, o.thousand, o.twothousand, o.fivethous,o.tenthous, o.odd, o.even, o.stringu1, o.stringu2, o.string4 Filter: (o.ten = 0) -> Index Scan using onek_unique1 on public.onek i - Output: (hashed SubPlan 1), random() + Output: (ANY (i.ten = (hashed SubPlan 1).col1)), random() Index Cond: (i.unique1 = o.unique1) SubPlan 1 -> Seq Scan on public.int4_tbl @@ -1346,7 +1397,7 @@ select * from int4_tbl where --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Nested Loop Semi Join Output: int4_tbl.f1 - Join Filter: (CASE WHEN (hashed SubPlan 1) THEN int4_tbl.f1 ELSE NULL::integer END = b.ten) + Join Filter: (CASE WHEN (ANY (int4_tbl.f1 = (hashed SubPlan 1).col1)) THEN int4_tbl.f1 ELSE NULL::integer END = b.ten) -> Seq Scan on public.int4_tbl Output: int4_tbl.f1 -> Seq Scan on public.tenk1 b @@ -1945,10 +1996,10 @@ select * from tenk1 A where exists (select 1 from tenk2 B where A.hundred in (select C.hundred FROM tenk2 C WHERE c.odd = b.odd)); - QUERY PLAN ---------------------------------- + QUERY PLAN +----------------------------------------------------- Nested Loop Semi Join - Join Filter: (SubPlan 1) + Join Filter: (ANY (a.hundred = (SubPlan 1).col1)) -> Seq Scan on tenk1 a -> Materialize -> Seq Scan on tenk2 b @@ -1962,10 +2013,10 @@ WHERE c.odd = b.odd)); explain (costs off) SELECT * FROM tenk1 A LEFT JOIN tenk2 B ON A.hundred in (SELECT c.hundred FROM tenk2 C WHERE c.odd = b.odd); - QUERY PLAN ---------------------------------- + QUERY PLAN +----------------------------------------------------- Nested Loop Left Join - Join Filter: (SubPlan 1) + Join Filter: (ANY (a.hundred = (SubPlan 1).col1)) -> Seq Scan on tenk1 a -> Materialize -> Seq Scan on tenk2 b @@ -1979,10 +2030,10 @@ ON A.hundred in (SELECT c.hundred FROM tenk2 C WHERE c.odd = b.odd); explain (costs off) SELECT * FROM tenk1 A LEFT JOIN tenk2 B ON B.hundred in (SELECT c.hundred FROM tenk2 C WHERE c.odd = a.odd); - QUERY PLAN ---------------------------------- + QUERY PLAN +----------------------------------------------------- Nested Loop Left Join - Join Filter: (SubPlan 1) + Join Filter: (ANY (b.hundred = (SubPlan 1).col1)) -> Seq Scan on tenk1 a -> Materialize -> Seq Scan on tenk2 b @@ -2045,7 +2096,7 @@ ON B.hundred in (SELECT min(c.hundred) FROM tenk2 C WHERE c.odd = b.odd); -> Subquery Scan on "ANY_subquery" Filter: (b.hundred = "ANY_subquery".min) -> Result - InitPlan 1 (returns $1) + InitPlan 1 -> Limit -> Index Scan using tenk2_hundred on tenk2 c Index Cond: (hundred IS NOT NULL) diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index 1062c341d8..713bf84c70 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -3038,18 +3038,18 @@ EXPLAIN (costs off) INSERT INTO rw_view1 VALUES (2, 'New row 2'); QUERY PLAN ----------------------------------------------------------- Insert on base_tbl - InitPlan 1 (returns $0) + InitPlan 1 -> Index Only Scan using base_tbl_pkey on base_tbl t Index Cond: (id = 2) -> Result - One-Time Filter: ($0 IS NOT TRUE) + One-Time Filter: ((InitPlan 1).col1 IS NOT TRUE) Update on base_tbl - InitPlan 1 (returns $0) + InitPlan 1 -> Index Only Scan using base_tbl_pkey on base_tbl t Index Cond: (id = 2) -> Result - One-Time Filter: $0 + One-Time Filter: (InitPlan 1).col1 -> Index Scan using base_tbl_pkey on base_tbl Index Cond: (id = 2) (15 rows) @@ -3108,8 +3108,8 @@ 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 t1_1 Update on public.t11 t1_2 @@ -3121,7 +3121,7 @@ UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; -> 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)) + Filter: ((t1_1.a <> 6) AND EXISTS(SubPlan 1) AND snoop(t1_1.a) AND leakproof(t1_1.a)) SubPlan 1 -> Append -> Seq Scan on public.t12 t12_1 @@ -3131,15 +3131,15 @@ UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; -> 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)) + Filter: ((t1_2.a <> 6) AND EXISTS(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)) + Filter: ((t1_3.a <> 6) AND EXISTS(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)) + Filter: ((t1_4.a <> 6) AND EXISTS(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; @@ -3155,8 +3155,8 @@ 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 t1_1 Update on public.t11 t1_2 @@ -3168,7 +3168,7 @@ UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; -> 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)) + Filter: (EXISTS(SubPlan 1) AND snoop(t1_1.a) AND leakproof(t1_1.a)) SubPlan 1 -> Append -> Seq Scan on public.t12 t12_1 @@ -3178,15 +3178,15 @@ UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; -> 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)) + Filter: (EXISTS(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)) + Filter: (EXISTS(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)) + Filter: (EXISTS(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; @@ -3370,10 +3370,10 @@ CREATE RULE v1_upd_rule AS ON UPDATE TO v1 DO INSTEAD CREATE VIEW v2 WITH (security_barrier = true) AS SELECT * FROM v1 WHERE EXISTS (SELECT 1); EXPLAIN (COSTS OFF) UPDATE v2 SET a = 1; - QUERY PLAN ---------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------- Update on t1 - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Merge Join Merge Cond: (t1.a = v1.a) @@ -3384,7 +3384,7 @@ EXPLAIN (COSTS OFF) UPDATE v2 SET a = 1; Sort Key: v1.a -> Subquery Scan on v1 -> Result - One-Time Filter: $0 + One-Time Filter: (InitPlan 1).col1 -> Seq Scan on t1 t1_1 (14 rows) diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out index cb0b033da6..f04e172f59 100644 --- a/src/test/regress/expected/update.out +++ b/src/test/regress/expected/update.out @@ -178,15 +178,15 @@ 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, (SubPlan 1 (returns $1,$2)), t.ctid + Output: (SubPlan 1).col1, (SubPlan 1).col2, (SubPlan 1), t.ctid One-Time Filter: (CURRENT_USER = SESSION_USER) -> Seq Scan on public.update_test t Output: t.a, t.ctid - SubPlan 1 (returns $1,$2) + SubPlan 1 -> Seq Scan on public.update_test s Output: s.b, s.a Filter: (s.a = t.a) diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index 60de2cbf96..e46710cf31 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -4279,7 +4279,7 @@ WHERE c = 1; Subquery Scan on emp Filter: (emp.c = 1) -> WindowAgg - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Sort Sort Key: empsalary.empno DESC diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 6de347b528..7bef181d78 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -3100,7 +3100,7 @@ WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v); CTE cte_init -> Result Output: 1, 'cte_init val'::text - InitPlan 2 (returns $1) + InitPlan 2 -> Limit Output: ((cte_init.b || ' merge update'::text)) -> CTE Scan on cte_init @@ -3143,11 +3143,11 @@ WHEN NOT MATCHED THEN INSERT VALUES(o.a, o.b || (SELECT merge_source_cte.*::text CTE merge_source_cte -> Result Output: 15, 'merge_source_cte val'::text - InitPlan 2 (returns $1) + InitPlan 2 -> CTE Scan on merge_source_cte merge_source_cte_1 Output: ((merge_source_cte_1.b || (merge_source_cte_1.*)::text) || ' merge update'::text) Filter: (merge_source_cte_1.a = 15) - InitPlan 3 (returns $2) + InitPlan 3 -> CTE Scan on merge_source_cte merge_source_cte_2 Output: ((merge_source_cte_2.*)::text || ' merge insert'::text) -> Hash Right Join diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql index 2f3601a058..7c42ebc36f 100644 --- a/src/test/regress/sql/subselect.sql +++ b/src/test/regress/sql/subselect.sql @@ -82,6 +82,20 @@ SELECT f1 AS "Correlated Field" WHERE (f1, f2) IN (SELECT f2, CAST(f3 AS int4) FROM SUBSELECT_TBL WHERE f3 IS NOT NULL); +-- Check ROWCOMPARE cases, both correlated and not + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + +SELECT ROW(1, 2) = (SELECT f1, f2 FROM SUBSELECT_TBL); -- error + -- Subselects without aliases SELECT count FROM (SELECT COUNT(DISTINCT name) FROM road);
On Sun, 17 Mar 2024 at 19:39, Tom Lane <tgl@sss.pgh.pa.us> wrote: > > Here's a cleaned-up version that seems to successfully resolve > PARAM_EXEC references in all cases. I haven't changed the > "(plan_name).colN" notation, but that's an easy fix once we have > consensus on the spelling. I took a more detailed look at this and the code and doc changes all look good to me. There's a comment at the end of find_param_generator() that should probably say "No generator found", rather than "No referent found". The get_rule_expr() code could perhaps be simplified a bit, getting rid of the show_subplan_name variable and moving the recursive calls to get_rule_expr() to after the switch statement -- if testexpr is non-NULL, print it, else print the subplan name probably works for all subplan types. The "colN" notation has grown on me, especially when you look at examples like those in partition_prune.out with a mix of Param types. Not using "$n" for 2 different purposes is good, and I much prefer this to the original "FROM [HASHED] SubPlan N (returns ...)" notation. > There are two other loose ends bothering me: > > 1. I see that Gather nodes sometimes print things like > > -> Gather (actual rows=N loops=N) > Workers Planned: 2 > Params Evaluated: $0, $1 > Workers Launched: N > > I propose we nuke the "Params Evaluated" output altogether. +1 > 2. MULTIEXPR_SUBLINK subplans now result in EXPLAIN output like > > -> Result > Output: (SubPlan 1).col1, (SubPlan 1).col2, (SubPlan 1), i.tableoid, i.ctid > > The undecorated reference to (SubPlan 1) is fairly confusing, since > it doesn't correspond to anything that will actually get output. > I suggest that perhaps instead this should read > > Output: (SubPlan 1).col1, (SubPlan 1).col2, IGNORE(SubPlan 1), i.tableoid, i.ctid > > or > > Output: (SubPlan 1).col1, (SubPlan 1).col2, RESET(SubPlan 1), i.tableoid, i.ctid I think "RESET()" or "RESCAN()" or something like that is better than "INGORE()", because it indicates that it is actually doing something. I don't really have a better idea. Perhaps not all uppercase though, since that seems to go against the rest of the EXPLAIN output. Regards, Dean
Dean Rasheed <dean.a.rasheed@gmail.com> writes: > The get_rule_expr() code could perhaps be simplified a bit, getting > rid of the show_subplan_name variable and moving the recursive calls > to get_rule_expr() to after the switch statement -- if testexpr is > non-NULL, print it, else print the subplan name probably works for all > subplan types. Oooh, good idea. The symmetry wasn't apparent when we started, but it's there now, and the code does look nicer this way. > The "colN" notation has grown on me, especially when you look at > examples like those in partition_prune.out with a mix of Param types. OK, I've left it like that in the attached v5, but I'm still open to other opinions. >> The undecorated reference to (SubPlan 1) is fairly confusing, since >> it doesn't correspond to anything that will actually get output. >> I suggest that perhaps instead this should read >> Output: (SubPlan 1).col1, (SubPlan 1).col2, IGNORE(SubPlan 1), i.tableoid, i.ctid >> or >> Output: (SubPlan 1).col1, (SubPlan 1).col2, RESET(SubPlan 1), i.tableoid, i.ctid > I think "RESET()" or "RESCAN()" or something like that is better than > "INGORE()", because it indicates that it is actually doing something. > I don't really have a better idea. Perhaps not all uppercase though, > since that seems to go against the rest of the EXPLAIN output. Hm. I used "rescan(SubPlan)" in the attached, but it still looks a bit odd to my eye. I did some more work on the documentation too, to show the difference between hashed and not-hashed subplans. I feel like we're pretty close here, with the possible exception of how to show MULTIEXPR. regards, tom lane diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 58a603ac56..8d75ca56c9 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -3062,10 +3062,10 @@ select exists(select 1 from pg_enum), sum(c1) from ft1; QUERY PLAN -------------------------------------------------- Foreign Scan - Output: $0, (sum(ft1.c1)) + Output: (InitPlan 1).col1, (sum(ft1.c1)) Relations: Aggregate on (public.ft1) Remote SQL: SELECT sum("C 1") FROM "S 1"."T 1" - InitPlan 1 (returns $0) + InitPlan 1 -> Seq Scan on pg_catalog.pg_enum (6 rows) @@ -3080,8 +3080,8 @@ select exists(select 1 from pg_enum), sum(c1) from ft1 group by 1; QUERY PLAN --------------------------------------------------- GroupAggregate - Output: $0, sum(ft1.c1) - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1, sum(ft1.c1) + InitPlan 1 -> Seq Scan on pg_catalog.pg_enum -> Foreign Scan on public.ft1 Output: ft1.c1 @@ -3305,10 +3305,10 @@ select sum(c1) filter (where (c1 / c1) * random() <= 1) from ft1 group by c2 ord explain (verbose, costs off) select sum(c2) filter (where c2 in (select c2 from ft1 where c2 < 5)) from ft1; - QUERY PLAN -------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------- Aggregate - Output: sum(ft1.c2) FILTER (WHERE (hashed SubPlan 1)) + Output: sum(ft1.c2) FILTER (WHERE (ANY (ft1.c2 = (hashed SubPlan 1).col1))) -> Foreign Scan on public.ft1 Output: ft1.c2 Remote SQL: SELECT c2 FROM "S 1"."T 1" @@ -6171,9 +6171,9 @@ UPDATE ft2 AS target SET (c2, c7) = ( Update on public.ft2 target Remote SQL: UPDATE "S 1"."T 1" SET c2 = $2, c7 = $3 WHERE ctid = $1 -> Foreign Scan on public.ft2 target - Output: $1, $2, (SubPlan 1 (returns $1,$2)), target.ctid, target.* + Output: (SubPlan 1).col1, (SubPlan 1).col2, rescan(SubPlan 1), target.ctid, target.* Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8, ctid FROM "S 1"."T 1" WHERE (("C 1" > 1100)) FOR UPDATE - SubPlan 1 (returns $1,$2) + SubPlan 1 -> Foreign Scan on public.ft2 src Output: (src.c2 * 10), src.c7 Remote SQL: SELECT c2, c7 FROM "S 1"."T 1" WHERE (($1::integer = "C 1")) @@ -11685,9 +11685,9 @@ SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt W QUERY PLAN ---------------------------------------------------------------------------------------- Nested Loop Left Join - Output: t1.a, t1.b, t1.c, async_pt.a, async_pt.b, async_pt.c, ($0) + Output: t1.a, t1.b, t1.c, async_pt.a, async_pt.b, async_pt.c, ((InitPlan 1).col1) Join Filter: (t1.a = async_pt.a) - InitPlan 1 (returns $0) + InitPlan 1 -> Aggregate Output: count(*) -> Append @@ -11699,10 +11699,10 @@ SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt W Output: t1.a, t1.b, t1.c -> Append -> Async Foreign Scan on public.async_p1 async_pt_1 - Output: async_pt_1.a, async_pt_1.b, async_pt_1.c, $0 + Output: async_pt_1.a, async_pt_1.b, async_pt_1.c, (InitPlan 1).col1 Remote SQL: SELECT a, b, c FROM public.base_tbl1 WHERE ((a < 3000)) -> Async Foreign Scan on public.async_p2 async_pt_2 - Output: async_pt_2.a, async_pt_2.b, async_pt_2.c, $0 + Output: async_pt_2.a, async_pt_2.b, async_pt_2.c, (InitPlan 1).col1 Remote SQL: SELECT a, b, c FROM public.base_tbl2 WHERE ((a < 3000)) (20 rows) @@ -11713,7 +11713,7 @@ SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt W Nested Loop Left Join (actual rows=1 loops=1) Join Filter: (t1.a = async_pt.a) Rows Removed by Join Filter: 399 - InitPlan 1 (returns $0) + InitPlan 1 -> Aggregate (actual rows=1 loops=1) -> Append (actual rows=400 loops=1) -> Async Foreign Scan on async_p1 async_pt_4 (actual rows=200 loops=1) @@ -11936,11 +11936,11 @@ CREATE FOREIGN TABLE foreign_tbl2 () INHERITS (foreign_tbl) SERVER loopback OPTIONS (table_name 'base_tbl'); EXPLAIN (VERBOSE, COSTS OFF) SELECT a FROM base_tbl WHERE (a, random() > 0) IN (SELECT a, random() > 0 FROM foreign_tbl); - QUERY PLAN ------------------------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- Seq Scan on public.base_tbl Output: base_tbl.a - Filter: (SubPlan 1) + Filter: (ANY ((base_tbl.a = (SubPlan 1).col1) AND ((random() > '0'::double precision) = (SubPlan 1).col2))) SubPlan 1 -> Result Output: base_tbl.a, (random() > '0'::double precision) diff --git a/doc/src/sgml/perform.sgml b/doc/src/sgml/perform.sgml index 90822b3f4c..4b831a6206 100644 --- a/doc/src/sgml/perform.sgml +++ b/doc/src/sgml/perform.sgml @@ -573,8 +573,106 @@ WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2; which shows that the planner thinks that sorting <literal>onek</literal> by index-scanning is about 12% more expensive than sequential-scan-and-sort. Of course, the next question is whether it's right about that. - We can investigate that using <command>EXPLAIN ANALYZE</command>, as discussed - below. + We can investigate that using <command>EXPLAIN ANALYZE</command>, as + discussed <link linkend="using-explain-analyze">below</link>. + </para> + + <para> + <indexterm> + <primary>subplan</primary> + </indexterm> + Some query plans involve <firstterm>subplans</firstterm>, which arise + from sub-<literal>SELECT</literal>s in the original query. Such + queries can sometimes be transformed into ordinary join plans, but + when they cannot be, we get plans like: + +<screen> +EXPLAIN VERBOSE SELECT unique1 +FROM tenk1 t +WHERE t.ten < ALL (SELECT o.ten FROM onek o WHERE o.four = t.four); + + QUERY PLAN +-------------------------------------------------------------------&zwsp;------ + Seq Scan on public.tenk1 t (cost=0.00..586095.00 rows=5000 width=4) + Output: t.unique1 + Filter: (ALL (t.ten < (SubPlan 1).col1)) + SubPlan 1 + -> Seq Scan on public.onek o (cost=0.00..116.50 rows=250 width=4) + Output: o.ten + Filter: (o.four = t.four) +</screen> + + This rather artificial example serves to illustrate a couple of + points: values from the outer plan level can be passed down into a + subplan (here, <literal>t.four</literal> is passed down) and the + results of the sub-select are available to the outer plan. Those + result values are shown by <command>EXPLAIN</command> with notations + like + <literal>(<replaceable>subplan_name</replaceable>).col<replaceable>N</replaceable></literal>, + which refers to the <replaceable>N</replaceable>'th output column of + the sub-<literal>SELECT</literal>. + </para> + + <para> + <indexterm> + <primary>subplan</primary> + <secondary>hashed</secondary> + </indexterm> + In the example above, the <literal>ALL</literal> operator runs the + subplan again for each row of the outer query (which accounts for the + high estimated cost). Some queries can use a <firstterm>hashed + subplan</firstterm> to avoid that: + +<screen> +EXPLAIN SELECT * +FROM tenk1 t +WHERE t.unique1 NOT IN (SELECT o.unique1 FROM onek o); + + QUERY PLAN +-------------------------------------------------------------------&zwsp;------------------------- + Seq Scan on tenk1 t (cost=61.77..531.77 rows=5000 width=244) + Filter: (NOT (ANY (unique1 = (hashed SubPlan 1).col1))) + SubPlan 1 + -> Index Only Scan using onek_unique1 on onek o (cost=0.28..59.27 rows=1000 width=4) +(4 rows) +</screen> + + Here, the subplan is run a single time and its output is loaded into + an in-memory hash table, which is then probed by the + outer <literal>ANY</literal> operator. This requires that the + sub-<literal>SELECT</literal> not reference any variables of the outer + query, and that the <literal>ANY</literal>'s comparison operator be + amenable to hashing. + </para> + + <para> + <indexterm> + <primary>initplan</primary> + </indexterm> + If, in addition to not referencing any variables of the outer query, + the sub-<literal>SELECT</literal> cannot return more than one row, + it may instead be implemented as an <firstterm>initplan</firstterm>: + +<screen> +EXPLAIN VERBOSE SELECT unique1 +FROM tenk1 t1 WHERE t1.ten = (SELECT (random() * 10)::integer); + + QUERY PLAN +------------------------------------------------------------&zwsp;-------- + Seq Scan on public.tenk1 t1 (cost=0.02..470.02 rows=1000 width=4) + Output: t1.unique1 + Filter: (t1.ten = (InitPlan 1).col1) + InitPlan 1 + -> Result (cost=0.00..0.02 rows=1 width=4) + Output: ((random() * '10'::double precision))::integer +</screen> + + An initplan is run only once per execution of the outer plan, and its + results are saved for re-use in later rows of the outer plan. So in + this example <literal>random()</literal> is evaluated only once and + all the values of <literal>t1.ten</literal> are compared to the same + randomly-chosen integer. That's quite different from what would + happen without the sub-<literal>SELECT</literal> construct. </para> </sect2> diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index a9d5056af4..926d70afaf 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -116,7 +116,6 @@ static void show_tidbitmap_info(BitmapHeapScanState *planstate, static void show_instrumentation_count(const char *qlabel, int which, PlanState *planstate, ExplainState *es); static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es); -static void show_eval_params(Bitmapset *bms_params, ExplainState *es); static const char *explain_get_index_name(Oid indexId); static bool peek_buffer_usage(ExplainState *es, const BufferUsage *usage); static void show_buffer_usage(ExplainState *es, const BufferUsage *usage); @@ -1914,10 +1913,6 @@ ExplainNode(PlanState *planstate, List *ancestors, ExplainPropertyInteger("Workers Planned", NULL, gather->num_workers, es); - /* Show params evaluated at gather node */ - if (gather->initParam) - show_eval_params(gather->initParam, es); - if (es->analyze) { int nworkers; @@ -1942,10 +1937,6 @@ ExplainNode(PlanState *planstate, List *ancestors, ExplainPropertyInteger("Workers Planned", NULL, gm->num_workers, es); - /* Show params evaluated at gather-merge node */ - if (gm->initParam) - show_eval_params(gm->initParam, es); - if (es->analyze) { int nworkers; @@ -3550,29 +3541,6 @@ show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es) } } -/* - * Show initplan params evaluated at Gather or Gather Merge node. - */ -static void -show_eval_params(Bitmapset *bms_params, ExplainState *es) -{ - int paramid = -1; - List *params = NIL; - - Assert(bms_params); - - while ((paramid = bms_next_member(bms_params, paramid)) >= 0) - { - char param[32]; - - snprintf(param, sizeof(param), "$%d", paramid); - params = lappend(params, pstrdup(param)); - } - - if (params) - ExplainPropertyList("Params Evaluated", params, es); -} - /* * Fetch the name of an index in an EXPLAIN * diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index b9e0c960bd..d6954a7e86 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -560,22 +560,9 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot, splan->plan_id); /* Label the subplan for EXPLAIN purposes */ - splan->plan_name = palloc(32 + 12 * list_length(splan->setParam)); - sprintf(splan->plan_name, "%s %d", - isInitPlan ? "InitPlan" : "SubPlan", - splan->plan_id); - if (splan->setParam) - { - char *ptr = splan->plan_name + strlen(splan->plan_name); - - ptr += sprintf(ptr, " (returns "); - foreach(lc, splan->setParam) - { - ptr += sprintf(ptr, "$%d%s", - lfirst_int(lc), - lnext(splan->setParam, lc) ? "," : ")"); - } - } + splan->plan_name = psprintf("%s %d", + isInitPlan ? "InitPlan" : "SubPlan", + splan->plan_id); /* Lastly, fill in the cost estimates for use later */ cost_subplan(root, splan, plan); @@ -3047,8 +3034,7 @@ SS_make_initplan_from_plan(PlannerInfo *root, node = makeNode(SubPlan); node->subLinkType = EXPR_SUBLINK; node->plan_id = list_length(root->glob->subplans); - node->plan_name = psprintf("InitPlan %d (returns $%d)", - node->plan_id, prm->paramid); + node->plan_name = psprintf("InitPlan %d", node->plan_id); get_first_col_type(plan, &node->firstColType, &node->firstColTypmod, &node->firstColCollation); node->parallel_safe = plan->parallel_safe; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index f2893d4086..64122f4ad7 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -437,6 +437,10 @@ static void resolve_special_varno(Node *node, deparse_context *context, rsv_callback callback, void *callback_arg); static Node *find_param_referent(Param *param, deparse_context *context, deparse_namespace **dpns_p, ListCell **ancestor_cell_p); +static SubPlan *find_param_generator(Param *param, deparse_context *context, + int *column_p); +static SubPlan *find_param_generator_initplan(Param *param, Plan *plan, + int *column_p); static void get_parameter(Param *param, deparse_context *context); static const char *get_simple_binary_op_name(OpExpr *expr); static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags); @@ -8134,6 +8138,128 @@ find_param_referent(Param *param, deparse_context *context, return NULL; } +/* + * Try to find a subplan/initplan that emits the value for a PARAM_EXEC Param. + * + * If successful, return the generating subplan/initplan and set *column_p + * to the subplan's 0-based output column number. + * Otherwise, return NULL. + */ +static SubPlan * +find_param_generator(Param *param, deparse_context *context, int *column_p) +{ + /* Initialize output parameter to prevent compiler warnings */ + *column_p = 0; + + /* + * If it's a PARAM_EXEC parameter, search the current plan node as well as + * ancestor nodes looking for a subplan or initplan that emits the value + * for the Param. It could appear in the setParams of an initplan or + * MULTIEXPR_SUBLINK subplan, or in the paramIds of an ancestral SubPlan. + */ + if (param->paramkind == PARAM_EXEC) + { + SubPlan *result; + deparse_namespace *dpns; + ListCell *lc; + + dpns = (deparse_namespace *) linitial(context->namespaces); + + /* First check the innermost plan node's initplans */ + result = find_param_generator_initplan(param, dpns->plan, column_p); + if (result) + return result; + + /* + * The plan's targetlist might contain MULTIEXPR_SUBLINK SubPlans, + * which can be referenced by Params elsewhere in the targetlist. + * (Such Params should always be in the same targetlist, so there's no + * need to do this work at upper plan nodes.) + */ + foreach_node(TargetEntry, tle, dpns->plan->targetlist) + { + if (tle->expr && IsA(tle->expr, SubPlan)) + { + SubPlan *subplan = (SubPlan *) tle->expr; + + if (subplan->subLinkType == MULTIEXPR_SUBLINK) + { + foreach_int(paramid, subplan->setParam) + { + if (paramid == param->paramid) + { + /* Found a match, so return it. */ + *column_p = foreach_current_index(paramid); + return subplan; + } + } + } + } + } + + /* No luck, so check the ancestor nodes */ + foreach(lc, dpns->ancestors) + { + Node *ancestor = (Node *) lfirst(lc); + + /* + * If ancestor is a SubPlan, check the paramIds it provides. + */ + if (IsA(ancestor, SubPlan)) + { + SubPlan *subplan = (SubPlan *) ancestor; + + foreach_int(paramid, subplan->paramIds) + { + if (paramid == param->paramid) + { + /* Found a match, so return it. */ + *column_p = foreach_current_index(paramid); + return subplan; + } + } + + /* SubPlan isn't a kind of Plan, so skip the rest */ + continue; + } + + /* + * Otherwise, it's some kind of Plan node, so check its initplans. + */ + result = find_param_generator_initplan(param, (Plan *) ancestor, + column_p); + if (result) + return result; + + /* No luck, crawl up to next ancestor */ + } + } + + /* No generator found */ + return NULL; +} + +/* + * Subroutine for find_param_generator: search one Plan node's initplans + */ +static SubPlan * +find_param_generator_initplan(Param *param, Plan *plan, int *column_p) +{ + foreach_node(SubPlan, subplan, plan->initPlan) + { + foreach_int(paramid, subplan->setParam) + { + if (paramid == param->paramid) + { + /* Found a match, so return it. */ + *column_p = foreach_current_index(paramid); + return subplan; + } + } + } + return NULL; +} + /* * Display a Param appropriately. */ @@ -8143,12 +8269,13 @@ get_parameter(Param *param, deparse_context *context) Node *expr; deparse_namespace *dpns; ListCell *ancestor_cell; + SubPlan *subplan; + int column; /* * If it's a PARAM_EXEC parameter, try to locate the expression from which - * the parameter was computed. Note that failing to find a referent isn't - * an error, since the Param might well be a subplan output rather than an - * input. + * the parameter was computed. This stanza handles only cases in which + * the Param represents an input to the subplan we are currently in. */ expr = find_param_referent(param, context, &dpns, &ancestor_cell); if (expr) @@ -8192,6 +8319,22 @@ get_parameter(Param *param, deparse_context *context) return; } + /* + * Alternatively, maybe it's a subplan output, which we print as a + * reference to the subplan. (We could drill down into the subplan and + * print the relevant targetlist expression, but that has been deemed too + * confusing since it would violate normal SQL scope rules.) + */ + subplan = find_param_generator(param, context, &column); + if (subplan) + { + appendStringInfo(context->buf, "(%s%s).col%d", + subplan->useHashTable ? "hashed " : "", + subplan->plan_name, column + 1); + + return; + } + /* * If it's an external parameter, see if the outermost namespace provides * function argument names. @@ -8240,7 +8383,12 @@ get_parameter(Param *param, deparse_context *context) /* * Not PARAM_EXEC, or couldn't find referent: just print $N. + * + * It's a bug if we get here for anything except PARAM_EXTERN Params, but + * in production builds printing $N seems more useful than failing. */ + Assert(param->paramkind == PARAM_EXTERN); + appendStringInfo(context->buf, "$%d", param->paramid); } @@ -8881,12 +9029,78 @@ get_rule_expr(Node *node, deparse_context *context, * We cannot see an already-planned subplan in rule deparsing, * only while EXPLAINing a query plan. We don't try to * reconstruct the original SQL, just reference the subplan - * that appears elsewhere in EXPLAIN's result. + * that appears elsewhere in EXPLAIN's result. It does seem + * useful to show the subLinkType and testexpr, however, and + * we also note whether the subplan will be hashed. */ - if (subplan->useHashTable) - appendStringInfo(buf, "(hashed %s)", subplan->plan_name); + switch (subplan->subLinkType) + { + case EXISTS_SUBLINK: + appendStringInfoString(buf, "EXISTS("); + Assert(subplan->testexpr == NULL); + break; + case ALL_SUBLINK: + appendStringInfoString(buf, "(ALL "); + Assert(subplan->testexpr != NULL); + break; + case ANY_SUBLINK: + appendStringInfoString(buf, "(ANY "); + Assert(subplan->testexpr != NULL); + break; + case ROWCOMPARE_SUBLINK: + appendStringInfoChar(buf, '('); + Assert(subplan->testexpr != NULL); + break; + case EXPR_SUBLINK: + /* No need to decorate these subplan references */ + appendStringInfoChar(buf, '('); + Assert(subplan->testexpr == NULL); + break; + case MULTIEXPR_SUBLINK: + /* MULTIEXPR isn't executed in the normal way */ + appendStringInfoString(buf, "rescan("); + Assert(subplan->testexpr == NULL); + break; + case ARRAY_SUBLINK: + appendStringInfoString(buf, "ARRAY("); + Assert(subplan->testexpr == NULL); + break; + case CTE_SUBLINK: + /* This case is unreachable within expressions */ + appendStringInfoString(buf, "CTE("); + Assert(subplan->testexpr == NULL); + break; + } + + if (subplan->testexpr != NULL) + { + deparse_namespace *dpns; + + /* + * Push SubPlan into ancestors list while deparsing + * testexpr, so that we can handle PARAM_EXEC references + * to the SubPlan's paramIds. (This makes it look like + * the SubPlan is an "ancestor" of the current plan node, + * which is a little weird, but it does no harm.) In this + * path, we don't need to mention the SubPlan explicitly, + * because the referencing Params will show its existence. + */ + dpns = (deparse_namespace *) linitial(context->namespaces); + dpns->ancestors = lcons(subplan, dpns->ancestors); + + get_rule_expr(subplan->testexpr, context, showimplicit); + appendStringInfoChar(buf, ')'); + + dpns->ancestors = list_delete_first(dpns->ancestors); + } else - appendStringInfo(buf, "(%s)", subplan->plan_name); + { + /* No referencing Params, so show the SubPlan's name */ + if (subplan->useHashTable) + appendStringInfo(buf, "hashed %s)", subplan->plan_name); + else + appendStringInfo(buf, "%s)", subplan->plan_name); + } } break; diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index f86cf8d258..d54a255e58 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -738,7 +738,7 @@ select array(select sum(x+y) s QUERY PLAN ------------------------------------------------------------------- Function Scan on pg_catalog.generate_series x - Output: (SubPlan 1) + Output: ARRAY(SubPlan 1) Function Call: generate_series(1, 3) SubPlan 1 -> Sort @@ -915,7 +915,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan using tenk1_unique1 on tenk1 Index Cond: (unique1 IS NOT NULL) @@ -932,7 +932,7 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique1 on tenk1 Index Cond: (unique1 IS NOT NULL) @@ -949,7 +949,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique1 on tenk1 Index Cond: ((unique1 IS NOT NULL) AND (unique1 < 42)) @@ -966,7 +966,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique1 on tenk1 Index Cond: ((unique1 IS NOT NULL) AND (unique1 > 42)) @@ -989,7 +989,7 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique1 on tenk1 Index Cond: ((unique1 IS NOT NULL) AND (unique1 > 42000)) @@ -1008,7 +1008,7 @@ explain (costs off) QUERY PLAN ---------------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_thous_tenthous on tenk1 Index Cond: ((thousand = 33) AND (tenthous IS NOT NULL)) @@ -1025,7 +1025,7 @@ explain (costs off) QUERY PLAN -------------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan using tenk1_thous_tenthous on tenk1 Index Cond: ((thousand = 33) AND (tenthous IS NOT NULL)) @@ -1046,7 +1046,7 @@ explain (costs off) Seq Scan on int4_tbl SubPlan 2 -> Result - InitPlan 1 (returns $1) + InitPlan 1 -> Limit -> Index Only Scan using tenk1_unique1 on tenk1 Index Cond: ((unique1 IS NOT NULL) AND (unique1 > int4_tbl.f1)) @@ -1069,8 +1069,8 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------- HashAggregate - Group Key: $0 - InitPlan 1 (returns $0) + Group Key: (InitPlan 1).col1 + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1088,8 +1088,8 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------- Sort - Sort Key: ($0) - InitPlan 1 (returns $0) + Sort Key: ((InitPlan 1).col1) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1107,8 +1107,8 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------- Sort - Sort Key: ($0) - InitPlan 1 (returns $0) + Sort Key: ((InitPlan 1).col1) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1126,8 +1126,8 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------- Sort - Sort Key: (($0 + 1)) - InitPlan 1 (returns $0) + Sort Key: (((InitPlan 1).col1 + 1)) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1146,7 +1146,7 @@ explain (costs off) --------------------------------------------------------------------- Sort Sort Key: (generate_series(1, 3)) DESC - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1168,7 +1168,7 @@ explain (costs off) QUERY PLAN ---------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Result One-Time Filter: (100 IS NOT NULL) @@ -1199,7 +1199,7 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Merge Append Sort Key: minmaxtest.f1 @@ -1210,7 +1210,7 @@ explain (costs off) -> Index Only Scan Backward using minmaxtest2i on minmaxtest2 minmaxtest_3 Index Cond: (f1 IS NOT NULL) -> Index Only Scan using minmaxtest3i on minmaxtest3 minmaxtest_4 - InitPlan 2 (returns $1) + InitPlan 2 -> Limit -> Merge Append Sort Key: minmaxtest_5.f1 DESC @@ -1235,7 +1235,7 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------------------------------- Unique - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Merge Append Sort Key: minmaxtest.f1 @@ -1246,7 +1246,7 @@ explain (costs off) -> Index Only Scan Backward using minmaxtest2i on minmaxtest2 minmaxtest_3 Index Cond: (f1 IS NOT NULL) -> Index Only Scan using minmaxtest3i on minmaxtest3 minmaxtest_4 - InitPlan 2 (returns $1) + InitPlan 2 -> Limit -> Merge Append Sort Key: minmaxtest_5.f1 DESC @@ -1258,7 +1258,7 @@ explain (costs off) Index Cond: (f1 IS NOT NULL) -> Index Only Scan Backward using minmaxtest3i on minmaxtest3 minmaxtest_9 -> Sort - Sort Key: ($0), ($1) + Sort Key: ((InitPlan 1).col1), ((InitPlan 2).col1) -> Result (26 rows) diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index a3b9aaca84..e1f0660810 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -559,7 +559,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan using tenk1_unique1 on tenk1 Index Cond: (unique1 IS NOT NULL) @@ -2109,14 +2109,14 @@ order by a, b, c; -- test handling of outer GroupingFunc within subqueries explain (costs off) select (select grouping(v1)) from (values ((select 1))) v(v1) group by cube(v1); - QUERY PLAN ---------------------------- + QUERY PLAN +------------------------------- MixedAggregate - Hash Key: $2 + Hash Key: (InitPlan 3).col1 Group Key: () - InitPlan 1 (returns $1) + InitPlan 1 -> Result - InitPlan 3 (returns $2) + InitPlan 3 -> Result -> Result SubPlan 2 @@ -2132,12 +2132,12 @@ select (select grouping(v1)) from (values ((select 1))) v(v1) group by cube(v1); explain (costs off) select (select grouping(v1)) from (values ((select 1))) v(v1) group by v1; - QUERY PLAN ---------------------------- + QUERY PLAN +---------------- GroupAggregate - InitPlan 1 (returns $1) + InitPlan 1 -> Result - InitPlan 3 (returns $2) + InitPlan 3 -> Result -> Result SubPlan 2 diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index 130a924228..2cd6695697 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -1643,8 +1643,8 @@ explain (verbose, costs off) select min(1-id) from matest0; QUERY PLAN --------------------------------------------------------------------------------- Result - Output: $0 - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1 + InitPlan 1 -> Limit Output: ((1 - matest0.id)) -> Result @@ -1800,7 +1800,7 @@ SELECT min(x) FROM QUERY PLAN -------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Merge Append Sort Key: a.unique1 @@ -1818,7 +1818,7 @@ SELECT min(y) FROM QUERY PLAN -------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Merge Append Sort Key: a.unique1 @@ -1898,19 +1898,19 @@ insert into inhpar select x, x::text from generate_series(1,5) x; insert into inhcld select x::text, x from generate_series(6,10) x; explain (verbose, costs off) update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1); - QUERY PLAN -------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------- Update on public.inhpar i Update on public.inhpar i_1 Update on public.inhcld i_2 -> Result - Output: $2, $3, (SubPlan 1 (returns $2,$3)), i.tableoid, i.ctid + Output: (SubPlan 1).col1, (SubPlan 1).col2, rescan(SubPlan 1), i.tableoid, i.ctid -> Append -> Seq Scan on public.inhpar i_1 Output: i_1.f1, i_1.f2, i_1.tableoid, i_1.ctid -> Seq Scan on public.inhcld i_2 Output: i_2.f1, i_2.f2, i_2.tableoid, i_2.ctid - SubPlan 1 (returns $2,$3) + SubPlan 1 -> Limit Output: (i.f1), (((i.f2)::text || '-'::text)) -> Seq Scan on public.int4_tbl @@ -1946,21 +1946,21 @@ alter table inhpar attach partition inhcld2 for values from (5) to (100); insert into inhpar select x, x::text from generate_series(1,10) x; explain (verbose, costs off) update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1); - QUERY PLAN ------------------------------------------------------------------------------------ + QUERY PLAN +----------------------------------------------------------------------------------------------------- Update on public.inhpar i Update on public.inhcld1 i_1 Update on public.inhcld2 i_2 -> Append -> Seq Scan on public.inhcld1 i_1 - Output: $2, $3, (SubPlan 1 (returns $2,$3)), i_1.tableoid, i_1.ctid - SubPlan 1 (returns $2,$3) + Output: (SubPlan 1).col1, (SubPlan 1).col2, rescan(SubPlan 1), i_1.tableoid, i_1.ctid + SubPlan 1 -> Limit Output: (i_1.f1), (((i_1.f2)::text || '-'::text)) -> Seq Scan on public.int4_tbl Output: i_1.f1, ((i_1.f2)::text || '-'::text) -> Seq Scan on public.inhcld2 i_2 - Output: $2, $3, (SubPlan 1 (returns $2,$3)), i_2.tableoid, i_2.ctid + Output: (SubPlan 1).col1, (SubPlan 1).col2, rescan(SubPlan 1), i_2.tableoid, i_2.ctid (13 rows) update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1); @@ -2845,11 +2845,11 @@ explain (costs off) select min(a), max(a) from parted_minmax where b = '12345'; QUERY PLAN ------------------------------------------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan using parted_minmax1i on parted_minmax1 parted_minmax Index Cond: ((a IS NOT NULL) AND (b = '12345'::text)) - InitPlan 2 (returns $1) + InitPlan 2 -> Limit -> Index Only Scan Backward using parted_minmax1i on parted_minmax1 parted_minmax_1 Index Cond: ((a IS NOT NULL) AND (b = '12345'::text)) diff --git a/src/test/regress/expected/insert_conflict.out b/src/test/regress/expected/insert_conflict.out index 563c5eb52a..701217ddbc 100644 --- a/src/test/regress/expected/insert_conflict.out +++ b/src/test/regress/expected/insert_conflict.out @@ -50,7 +50,7 @@ explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on con Insert on insertconflicttest Conflict Resolution: UPDATE Conflict Arbiter Indexes: op_index_key, collation_index_key, both_index_key - Conflict Filter: (SubPlan 1) + Conflict Filter: EXISTS(SubPlan 1) -> Result SubPlan 1 -> Index Only Scan using both_index_expr_key on insertconflicttest ii diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 9605400021..63cddac0d6 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -3028,10 +3028,10 @@ where unique1 in (select unique2 from tenk1 b); explain (costs off) select a.* from tenk1 a where unique1 not in (select unique2 from tenk1 b); - QUERY PLAN --------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------- Seq Scan on tenk1 a - Filter: (NOT (hashed SubPlan 1)) + Filter: (NOT (ANY (unique1 = (hashed SubPlan 1).col1))) SubPlan 1 -> Index Only Scan using tenk1_unique2 on tenk1 b (4 rows) @@ -5278,12 +5278,12 @@ explain (costs off) select a.unique1, b.unique2 from onek a left join onek b on a.unique1 = b.unique2 where (b.unique2, random() > 0) = any (select q1, random() > 0 from int8_tbl c where c.q1 < b.unique1); - QUERY PLAN ----------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------------ Hash Join Hash Cond: (b.unique2 = a.unique1) -> Seq Scan on onek b - Filter: (SubPlan 1) + Filter: (ANY ((unique2 = (SubPlan 1).col1) AND ((random() > '0'::double precision) = (SubPlan 1).col2))) SubPlan 1 -> Seq Scan on int8_tbl c Filter: (q1 < b.unique1) @@ -8262,8 +8262,8 @@ lateral (select * from int8_tbl t1, where q2 = (select greatest(t1.q1,t2.q2)) and (select v.id=0)) offset 0) ss2) ss where t1.q1 = ss.q2) ss0; - QUERY PLAN -------------------------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------- Nested Loop Output: "*VALUES*".column1, t1.q1, t1.q2, ss2.q1, ss2.q2 -> Seq Scan on public.int8_tbl t1 @@ -8277,20 +8277,20 @@ lateral (select * from int8_tbl t1, Filter: (t1.q1 = ss2.q2) -> Seq Scan on public.int8_tbl t2 Output: t2.q1, t2.q2 - Filter: (SubPlan 3) + Filter: (ANY ((t2.q1 = (SubPlan 3).col1) AND ((random() > '0'::double precision) = (SubPlan 3).col2))) SubPlan 3 -> Result Output: t3.q2, (random() > '0'::double precision) - One-Time Filter: $4 - InitPlan 1 (returns $2) + One-Time Filter: (InitPlan 2).col1 + InitPlan 1 -> Result Output: GREATEST(t1.q1, t2.q2) - InitPlan 2 (returns $4) + InitPlan 2 -> Result Output: ("*VALUES*".column1 = 0) -> Seq Scan on public.int8_tbl t3 Output: t3.q1, t3.q2 - Filter: (t3.q2 = $2) + Filter: (t3.q2 = (InitPlan 1).col1) (27 rows) select * from (values (0), (1)) v(id), diff --git a/src/test/regress/expected/memoize.out b/src/test/regress/expected/memoize.out index 1c8d996740..0fd103c06b 100644 --- a/src/test/regress/expected/memoize.out +++ b/src/test/regress/expected/memoize.out @@ -342,7 +342,7 @@ WHERE unique1 < 3 ---------------------------------------------------------------- Index Scan using tenk1_unique1 on tenk1 t0 Index Cond: (unique1 < 3) - Filter: (SubPlan 1) + Filter: EXISTS(SubPlan 1) SubPlan 1 -> Nested Loop -> Index Scan using tenk1_hundred on tenk1 t2 diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out index 07561f0240..cec7f11f9f 100644 --- a/src/test/regress/expected/merge.out +++ b/src/test/regress/expected/merge.out @@ -1697,10 +1697,10 @@ WHEN MATCHED AND t.c > s.cnt THEN -> Seq Scan on public.ref r_2 Output: r_2.ab, r_2.cd Filter: ((r_2.ab = (s.a + s.b)) AND (r_2.cd = (s.c - s.d))) - SubPlan 3 (returns $9,$10) + SubPlan 3 -> Result - Output: s.b, $8 - InitPlan 2 (returns $8) + Output: s.b, (InitPlan 2).col1 + InitPlan 2 -> Aggregate Output: count(*) -> Seq Scan on public.ref r_1 diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index 9c20a24982..7ca98397ae 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -2176,36 +2176,36 @@ explain (analyze, costs off, summary off, timing off) execute ab_q1 (2, 4); prepare ab_q2 (int, int) as select a from ab where a between $1 and $2 and b < (select 3); explain (analyze, costs off, summary off, timing off) execute ab_q2 (2, 2); - QUERY PLAN ---------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------- Append (actual rows=0 loops=1) Subplans Removed: 6 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on ab_a2_b1 ab_1 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b < $0)) + Filter: ((a >= $1) AND (a <= $2) AND (b < (InitPlan 1).col1)) -> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b < $0)) + Filter: ((a >= $1) AND (a <= $2) AND (b < (InitPlan 1).col1)) -> Seq Scan on ab_a2_b3 ab_3 (never executed) - Filter: ((a >= $1) AND (a <= $2) AND (b < $0)) + Filter: ((a >= $1) AND (a <= $2) AND (b < (InitPlan 1).col1)) (10 rows) -- As above, but swap the PARAM_EXEC Param to the first partition level prepare ab_q3 (int, int) as select a from ab where b between $1 and $2 and a < (select 3); explain (analyze, costs off, summary off, timing off) execute ab_q3 (2, 2); - QUERY PLAN ---------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------- Append (actual rows=0 loops=1) Subplans Removed: 6 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on ab_a1_b2 ab_1 (actual rows=0 loops=1) - Filter: ((b >= $1) AND (b <= $2) AND (a < $0)) + Filter: ((b >= $1) AND (b <= $2) AND (a < (InitPlan 1).col1)) -> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1) - Filter: ((b >= $1) AND (b <= $2) AND (a < $0)) + Filter: ((b >= $1) AND (b <= $2) AND (a < (InitPlan 1).col1)) -> Seq Scan on ab_a3_b2 ab_3 (never executed) - Filter: ((b >= $1) AND (b <= $2) AND (a < $0)) + Filter: ((b >= $1) AND (b <= $2) AND (a < (InitPlan 1).col1)) (10 rows) -- @@ -2411,25 +2411,24 @@ select explain_parallel_append('execute ab_q5 (33, 44, 55)'); -- Test Parallel Append with PARAM_EXEC Params select explain_parallel_append('select count(*) from ab where (a = (select 1) or a = (select 3)) and b = 2'); - explain_parallel_append ------------------------------------------------------------------------------- + explain_parallel_append +------------------------------------------------------------------------------------------------ Aggregate (actual rows=N loops=N) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=N loops=N) - InitPlan 2 (returns $1) + InitPlan 2 -> Result (actual rows=N loops=N) -> Gather (actual rows=N loops=N) Workers Planned: 2 - Params Evaluated: $0, $1 Workers Launched: N -> Parallel Append (actual rows=N loops=N) -> Parallel Seq Scan on ab_a1_b2 ab_1 (actual rows=N loops=N) - Filter: ((b = 2) AND ((a = $0) OR (a = $1))) + Filter: ((b = 2) AND ((a = (InitPlan 1).col1) OR (a = (InitPlan 2).col1))) -> Parallel Seq Scan on ab_a2_b2 ab_2 (never executed) - Filter: ((b = 2) AND ((a = $0) OR (a = $1))) + Filter: ((b = 2) AND ((a = (InitPlan 1).col1) OR (a = (InitPlan 2).col1))) -> Parallel Seq Scan on ab_a3_b2 ab_3 (actual rows=N loops=N) - Filter: ((b = 2) AND ((a = $0) OR (a = $1))) -(16 rows) + Filter: ((b = 2) AND ((a = (InitPlan 1).col1) OR (a = (InitPlan 2).col1))) +(15 rows) -- Test pruning during parallel nested loop query create table lprt_a (a int not null); @@ -2629,57 +2628,57 @@ select * from ab where a = (select max(a) from lprt_a) and b = (select max(a)-1 QUERY PLAN ------------------------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Aggregate (actual rows=1 loops=1) -> Seq Scan on lprt_a (actual rows=102 loops=1) - InitPlan 2 (returns $1) + InitPlan 2 -> Aggregate (actual rows=1 loops=1) -> Seq Scan on lprt_a lprt_a_1 (actual rows=102 loops=1) -> Bitmap Heap Scan on ab_a1_b1 ab_1 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a1_b1_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a1_b2 ab_2 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a1_b2_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a1_b3 ab_3 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a1_b3_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a2_b1 ab_4 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a2_b1_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a2_b2 ab_5 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a2_b2_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a2_b3 ab_6 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a2_b3_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a3_b1 ab_7 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a3_b1_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a3_b2 ab_8 (actual rows=0 loops=1) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a3_b2_a_idx (actual rows=0 loops=1) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a3_b3 ab_9 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a3_b3_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) (52 rows) -- Test run-time partition pruning with UNION ALL parents @@ -2688,42 +2687,42 @@ select * from (select * from ab where a = 1 union all select * from ab) ab where QUERY PLAN ------------------------------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Append (actual rows=0 loops=1) -> Bitmap Heap Scan on ab_a1_b1 ab_11 (actual rows=0 loops=1) Recheck Cond: (a = 1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> 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_12 (never executed) Recheck Cond: (a = 1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Bitmap Index Scan on ab_a1_b2_a_idx (never executed) Index Cond: (a = 1) -> Bitmap Heap Scan on ab_a1_b3 ab_13 (never executed) Recheck Cond: (a = 1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Bitmap Index Scan on ab_a1_b3_a_idx (never executed) Index Cond: (a = 1) -> Seq Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a1_b2 ab_2 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a1_b3 ab_3 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b1 ab_4 (actual rows=0 loops=1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b2 ab_5 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b3 ab_6 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a3_b1 ab_7 (actual rows=0 loops=1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a3_b2 ab_8 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a3_b3 ab_9 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) (37 rows) -- A case containing a UNION ALL with a non-partitioned child. @@ -2732,44 +2731,44 @@ select * from (select * from ab where a = 1 union all (values(10,5)) union all s QUERY PLAN ------------------------------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Append (actual rows=0 loops=1) -> Bitmap Heap Scan on ab_a1_b1 ab_11 (actual rows=0 loops=1) Recheck Cond: (a = 1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> 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_12 (never executed) Recheck Cond: (a = 1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Bitmap Index Scan on ab_a1_b2_a_idx (never executed) Index Cond: (a = 1) -> Bitmap Heap Scan on ab_a1_b3 ab_13 (never executed) Recheck Cond: (a = 1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Bitmap Index Scan on ab_a1_b3_a_idx (never executed) Index Cond: (a = 1) -> Result (actual rows=0 loops=1) - One-Time Filter: (5 = $0) + One-Time Filter: (5 = (InitPlan 1).col1) -> Seq Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a1_b2 ab_2 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a1_b3 ab_3 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b1 ab_4 (actual rows=0 loops=1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b2 ab_5 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b3 ab_6 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a3_b1 ab_7 (actual rows=0 loops=1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a3_b2 ab_8 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a3_b3 ab_9 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) (39 rows) -- Another UNION ALL test, but containing a mix of exec init and exec run-time pruning. @@ -2787,27 +2786,27 @@ union all ) ab where a = $1 and b = (select -10); -- Ensure the xy_1 subplan is not pruned. explain (analyze, costs off, summary off, timing off) execute ab_q6(1); - QUERY PLAN --------------------------------------------------- + QUERY PLAN +-------------------------------------------------------- Append (actual rows=0 loops=1) Subplans Removed: 12 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on ab_a1_b1 ab_1 (never executed) - Filter: ((a = $1) AND (b = $0)) + Filter: ((a = $1) AND (b = (InitPlan 1).col1)) -> Seq Scan on ab_a1_b2 ab_2 (never executed) - Filter: ((a = $1) AND (b = $0)) + Filter: ((a = $1) AND (b = (InitPlan 1).col1)) -> Seq Scan on ab_a1_b3 ab_3 (never executed) - Filter: ((a = $1) AND (b = $0)) + Filter: ((a = $1) AND (b = (InitPlan 1).col1)) -> Seq Scan on xy_1 (actual rows=0 loops=1) - Filter: ((x = $1) AND (y = $0)) + Filter: ((x = $1) AND (y = (InitPlan 1).col1)) Rows Removed by Filter: 1 -> Seq Scan on ab_a1_b1 ab_4 (never executed) - Filter: ((a = $1) AND (b = $0)) + Filter: ((a = $1) AND (b = (InitPlan 1).col1)) -> Seq Scan on ab_a1_b2 ab_5 (never executed) - Filter: ((a = $1) AND (b = $0)) + Filter: ((a = $1) AND (b = (InitPlan 1).col1)) -> Seq Scan on ab_a1_b3 ab_6 (never executed) - Filter: ((a = $1) AND (b = $0)) + Filter: ((a = $1) AND (b = (InitPlan 1).col1)) (19 rows) -- Ensure we see just the xy_1 row. @@ -2886,7 +2885,7 @@ update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 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 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Nested Loop (actual rows=3 loops=1) -> Append (actual rows=3 loops=1) @@ -2896,11 +2895,11 @@ update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 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) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b2 ab_a2_2 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b3 ab_a2_3 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) (19 rows) select tableoid::regclass, * from ab; @@ -3234,12 +3233,12 @@ select * from listp where a = (select null::int); QUERY PLAN ------------------------------------------------------ Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on listp_1_1 listp_1 (never executed) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) -> Seq Scan on listp_2_1 listp_2 (never executed) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) (7 rows) drop table listp; @@ -3377,14 +3376,14 @@ prepare ps1 as select * from mc3p where a = $1 and abs(b) < (select 3); explain (analyze, costs off, summary off, timing off) execute ps1(1); - QUERY PLAN --------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------- Append (actual rows=1 loops=1) Subplans Removed: 2 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on mc3p1 mc3p_1 (actual rows=1 loops=1) - Filter: ((a = $1) AND (abs(b) < $0)) + Filter: ((a = $1) AND (abs(b) < (InitPlan 1).col1)) (6 rows) deallocate ps1; @@ -3392,16 +3391,16 @@ prepare ps2 as select * from mc3p where a <= $1 and abs(b) < (select 3); explain (analyze, costs off, summary off, timing off) execute ps2(1); - QUERY PLAN --------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------- Append (actual rows=2 loops=1) Subplans Removed: 1 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on mc3p0 mc3p_1 (actual rows=1 loops=1) - Filter: ((a <= $1) AND (abs(b) < $0)) + Filter: ((a <= $1) AND (abs(b) < (InitPlan 1).col1)) -> Seq Scan on mc3p1 mc3p_2 (actual rows=1 loops=1) - Filter: ((a <= $1) AND (abs(b) < $0)) + Filter: ((a <= $1) AND (abs(b) < (InitPlan 1).col1)) (8 rows) deallocate ps2; @@ -3417,14 +3416,14 @@ select * from boolp where a = (select value from boolvalues where value); QUERY PLAN ----------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Seq Scan on boolvalues (actual rows=1 loops=1) Filter: value Rows Removed by Filter: 1 -> Seq Scan on boolp_f boolp_1 (never executed) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) -> Seq Scan on boolp_t boolp_2 (actual rows=0 loops=1) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) (9 rows) explain (analyze, costs off, summary off, timing off) @@ -3432,14 +3431,14 @@ select * from boolp where a = (select value from boolvalues where not value); QUERY PLAN ----------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Seq Scan on boolvalues (actual rows=1 loops=1) Filter: (NOT value) Rows Removed by Filter: 1 -> Seq Scan on boolp_f boolp_1 (actual rows=0 loops=1) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) -> Seq Scan on boolp_t boolp_2 (never executed) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) (9 rows) drop table boolp; @@ -3528,18 +3527,18 @@ explain (analyze, costs off, summary off, timing off) select * from ma_test wher ----------------------------------------------------------------------------------------------- Merge Append (actual rows=20 loops=1) Sort Key: ma_test.b - InitPlan 2 (returns $1) + InitPlan 2 -> Result (actual rows=1 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Limit (actual rows=1 loops=1) -> Index Scan using ma_test_p2_b_idx on ma_test_p2 (actual rows=1 loops=1) Index Cond: (b IS NOT NULL) -> Index Scan using ma_test_p1_b_idx on ma_test_p1 ma_test_1 (never executed) - Filter: (a >= $1) + Filter: (a >= (InitPlan 2).col1) -> Index Scan using ma_test_p2_b_idx on ma_test_p2 ma_test_2 (actual rows=10 loops=1) - Filter: (a >= $1) + Filter: (a >= (InitPlan 2).col1) -> Index Scan using ma_test_p3_b_idx on ma_test_p3 ma_test_3 (actual rows=10 loops=1) - Filter: (a >= $1) + Filter: (a >= (InitPlan 2).col1) (14 rows) reset enable_seqscan; @@ -3908,17 +3907,17 @@ from ( select 1, 1, 1 ) s(a, b, c) where s.a = 1 and s.b = 1 and s.c = (select 1); - QUERY PLAN ----------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------- Append - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Seq Scan on p1 p - Filter: ((a = 1) AND (b = 1) AND (c = $0)) + Filter: ((a = 1) AND (b = 1) AND (c = (InitPlan 1).col1)) -> Seq Scan on q111 q1 - Filter: ((a = 1) AND (b = 1) AND (c = $0)) + Filter: ((a = 1) AND (b = 1) AND (c = (InitPlan 1).col1)) -> Result - One-Time Filter: (1 = $0) + One-Time Filter: (1 = (InitPlan 1).col1) (9 rows) select * @@ -3946,18 +3945,18 @@ from ( ) s(a, b, c) where s.a = $1 and s.b = $2 and s.c = (select 1); explain (costs off) execute q (1, 1); - QUERY PLAN ---------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------ Append Subplans Removed: 1 - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Seq Scan on p1 p - Filter: ((a = $1) AND (b = $2) AND (c = $0)) + Filter: ((a = $1) AND (b = $2) AND (c = (InitPlan 1).col1)) -> Seq Scan on q111 q1 - Filter: ((a = $1) AND (b = $2) AND (c = $0)) + Filter: ((a = $1) AND (b = $2) AND (c = (InitPlan 1).col1)) -> Result - One-Time Filter: ((1 = $1) AND (1 = $2) AND (1 = $0)) + One-Time Filter: ((1 = $1) AND (1 = $2) AND (1 = (InitPlan 1).col1)) (10 rows) execute q (1, 1); @@ -3975,11 +3974,11 @@ create table listp2 partition of listp for values in(2) partition by list(b); create table listp2_10 partition of listp2 for values in (10); explain (analyze, costs off, summary off, timing off) select * from listp where a = (select 2) and b <> 10; - QUERY PLAN --------------------------------------------------- + QUERY PLAN +--------------------------------------------------- Seq Scan on listp1 listp (actual rows=0 loops=1) - Filter: ((b <> 10) AND (a = $0)) - InitPlan 1 (returns $0) + Filter: ((b <> 10) AND (a = (InitPlan 1).col1)) + InitPlan 1 -> Result (never executed) (4 rows) @@ -4044,16 +4043,15 @@ select explain_parallel_append('select * from listp where a = (select 1);'); ---------------------------------------------------------------------- Gather (actual rows=N loops=N) Workers Planned: 2 - Params Evaluated: $0 Workers Launched: N - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=N loops=N) -> Parallel Append (actual rows=N loops=N) -> Seq Scan on listp_12_1 listp_1 (actual rows=N loops=N) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) -> Parallel Seq Scan on listp_12_2 listp_2 (never executed) - Filter: (a = $0) -(11 rows) + Filter: (a = (InitPlan 1).col1) +(10 rows) -- Like the above but throw some more complexity at the planner by adding -- a UNION ALL. We expect both sides of the union not to scan the @@ -4069,19 +4067,19 @@ select * from listp where a = (select 2);'); Workers Launched: N -> Parallel Append (actual rows=N loops=N) -> Parallel Append (actual rows=N loops=N) - InitPlan 2 (returns $1) + InitPlan 2 -> Result (actual rows=N loops=N) -> Seq Scan on listp_12_1 listp_1 (never executed) - Filter: (a = $1) + Filter: (a = (InitPlan 2).col1) -> Parallel Seq Scan on listp_12_2 listp_2 (actual rows=N loops=N) - Filter: (a = $1) + Filter: (a = (InitPlan 2).col1) -> Parallel Append (actual rows=N loops=N) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=N loops=N) -> Seq Scan on listp_12_1 listp_4 (actual rows=N loops=N) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) -> Parallel Seq Scan on listp_12_2 listp_5 (never executed) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) (18 rows) drop table listp; @@ -4104,20 +4102,20 @@ select * from rangep where b IN((select 1),(select 2)) order by a; QUERY PLAN ------------------------------------------------------------------------------------------------------------ Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) - InitPlan 2 (returns $1) + InitPlan 2 -> Result (actual rows=1 loops=1) -> Merge Append (actual rows=0 loops=1) Sort Key: rangep_2.a -> Index Scan using rangep_0_to_100_1_a_idx on rangep_0_to_100_1 rangep_2 (actual rows=0 loops=1) - Filter: (b = ANY (ARRAY[$0, $1])) + Filter: (b = ANY (ARRAY[(InitPlan 1).col1, (InitPlan 2).col1])) -> Index Scan using rangep_0_to_100_2_a_idx on rangep_0_to_100_2 rangep_3 (actual rows=0 loops=1) - Filter: (b = ANY (ARRAY[$0, $1])) + Filter: (b = ANY (ARRAY[(InitPlan 1).col1, (InitPlan 2).col1])) -> Index Scan using rangep_0_to_100_3_a_idx on rangep_0_to_100_3 rangep_4 (never executed) - Filter: (b = ANY (ARRAY[$0, $1])) + Filter: (b = ANY (ARRAY[(InitPlan 1).col1, (InitPlan 2).col1])) -> Index Scan using rangep_100_to_200_a_idx on rangep_100_to_200 rangep_5 (actual rows=0 loops=1) - Filter: (b = ANY (ARRAY[$0, $1])) + Filter: (b = ANY (ARRAY[(InitPlan 1).col1, (InitPlan 2).col1])) (15 rows) reset enable_sort; diff --git a/src/test/regress/expected/portals.out b/src/test/regress/expected/portals.out index f71e0b3d41..06726ed4ab 100644 --- a/src/test/regress/expected/portals.out +++ b/src/test/regress/expected/portals.out @@ -1472,18 +1472,18 @@ rollback; -- Check handling of non-backwards-scan-capable plans with scroll cursors begin; explain (costs off) declare c1 cursor for select (select 42) as x; - QUERY PLAN ---------------------------- + QUERY PLAN +---------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Result (3 rows) explain (costs off) declare c1 scroll cursor for select (select 42) as x; - QUERY PLAN ---------------------------- + QUERY PLAN +---------------- Materialize - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Result (4 rows) diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index d507a2c8ca..527cf7e7bf 100644 --- a/src/test/regress/expected/rowsecurity.out +++ b/src/test/regress/expected/rowsecurity.out @@ -265,27 +265,27 @@ NOTICE: f_leak => awesome science fiction (5 rows) EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle); - QUERY PLAN ----------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------- Seq Scan on document - Filter: ((dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) (5 rows) EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle); - QUERY PLAN ------------------------------------------------------------ + QUERY PLAN +-------------------------------------------------------------------------- Hash Join Hash Cond: (category.cid = document.cid) - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on category -> Hash -> Seq Scan on document - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) (9 rows) -- viewpoint from regress_rls_dave @@ -329,27 +329,27 @@ NOTICE: f_leak => awesome technology book (7 rows) EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle); - QUERY PLAN ----------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------- Seq Scan on document - Filter: ((cid <> 44) AND (cid <> 44) AND (cid < 50) AND (dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) + Filter: ((cid <> 44) AND (cid <> 44) AND (cid < 50) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) (5 rows) EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle); - QUERY PLAN ----------------------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------- Hash Join Hash Cond: (category.cid = document.cid) - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on category -> Hash -> Seq Scan on document - Filter: ((cid <> 44) AND (cid <> 44) AND (cid < 50) AND (dlevel <= $0) AND f_leak(dtitle)) + Filter: ((cid <> 44) AND (cid <> 44) AND (cid < 50) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) (9 rows) -- 44 would technically fail for both p2r and p1r, but we should get an error @@ -987,18 +987,18 @@ NOTICE: f_leak => my first satire (4 rows) EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN ------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------- Append - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on part_document_fiction part_document_1 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) -> Seq Scan on part_document_satire part_document_2 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) -> Seq Scan on part_document_nonfiction part_document_3 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) (10 rows) -- viewpoint from regress_rls_carol @@ -1029,18 +1029,18 @@ NOTICE: f_leak => awesome technology book (10 rows) EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN ------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------- Append - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on part_document_fiction part_document_1 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) -> Seq Scan on part_document_satire part_document_2 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) -> Seq Scan on part_document_nonfiction part_document_3 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) (10 rows) -- viewpoint from regress_rls_dave @@ -1059,11 +1059,11 @@ NOTICE: f_leak => awesome science fiction (4 rows) EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN --------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------- Seq Scan on part_document_fiction part_document - Filter: ((cid < 55) AND (dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) + Filter: ((cid < 55) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) (5 rows) @@ -1137,11 +1137,11 @@ NOTICE: f_leak => awesome science fiction (4 rows) EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN --------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------- Seq Scan on part_document_fiction part_document - Filter: ((cid < 55) AND (dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) + Filter: ((cid < 55) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) (5 rows) @@ -1176,18 +1176,18 @@ NOTICE: f_leak => awesome technology book (11 rows) EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN ------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------- Append - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on part_document_fiction part_document_1 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) -> Seq Scan on part_document_satire part_document_2 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) -> Seq Scan on part_document_nonfiction part_document_3 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) (10 rows) -- only owner can change policies @@ -1437,10 +1437,10 @@ NOTICE: f_leak => 03b26944890929ff751653acb2f2af79 (1 row) EXPLAIN (COSTS OFF) SELECT * FROM only s1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------- Seq Scan on s1 - Filter: ((hashed SubPlan 1) AND f_leak(b)) + Filter: ((ANY (a = (hashed SubPlan 1).col1)) AND f_leak(b)) SubPlan 1 -> Seq Scan on s2 Filter: (((x % 2) = 0) AND (y ~~ '%2f%'::text)) @@ -1457,10 +1457,10 @@ NOTICE: f_leak => 03b26944890929ff751653acb2f2af79 (1 row) EXPLAIN (COSTS OFF) SELECT * FROM s1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------- Seq Scan on s1 - Filter: ((hashed SubPlan 1) AND f_leak(b)) + Filter: ((ANY (a = (hashed SubPlan 1).col1)) AND f_leak(b)) SubPlan 1 -> Seq Scan on s2 Filter: (((x % 2) = 0) AND (y ~~ '%af%'::text)) @@ -1480,7 +1480,7 @@ EXPLAIN (COSTS OFF) SELECT (SELECT x FROM s1 LIMIT 1) xx, * FROM s2 WHERE y like SubPlan 2 -> Limit -> Seq Scan on s1 - Filter: (hashed SubPlan 1) + Filter: (ANY (a = (hashed SubPlan 1).col1)) SubPlan 1 -> Seq Scan on s2 s2_1 Filter: (((x % 2) = 0) AND (y ~~ '%af%'::text)) @@ -2717,10 +2717,10 @@ NOTICE: f_leak => bbb (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 0) AND f_leak(b)) + Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 0) AND f_leak(b)) SubPlan 1 -> Seq Scan on z1_blacklist (4 rows) @@ -2735,10 +2735,10 @@ NOTICE: f_leak => bbb (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 0) AND f_leak(b)) + Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 0) AND f_leak(b)) SubPlan 1 -> Seq Scan on z1_blacklist (4 rows) @@ -2907,10 +2907,10 @@ NOTICE: f_leak => bbb (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 0) AND f_leak(b)) + Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 0) AND f_leak(b)) SubPlan 1 -> Seq Scan on z1_blacklist (4 rows) @@ -2933,10 +2933,10 @@ NOTICE: f_leak => aba (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 1) AND f_leak(b)) + Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 1) AND f_leak(b)) SubPlan 1 -> Seq Scan on z1_blacklist (4 rows) diff --git a/src/test/regress/expected/rowtypes.out b/src/test/regress/expected/rowtypes.out index 8f3c153bac..b400b58f76 100644 --- a/src/test/regress/expected/rowtypes.out +++ b/src/test/regress/expected/rowtypes.out @@ -1249,8 +1249,8 @@ with cte(c) as materialized (select row(1, 2)), select * from cte2 as t where (select * from (select c as c1) s where (select (c1).f1 > 0)) is not null; - QUERY PLAN --------------------------------------------- + QUERY PLAN +---------------------------------------------- CTE Scan on cte Output: cte.c Filter: ((SubPlan 3) IS NOT NULL) @@ -1260,8 +1260,8 @@ where (select * from (select c as c1) s SubPlan 3 -> Result Output: cte.c - One-Time Filter: $2 - InitPlan 2 (returns $2) + One-Time Filter: (InitPlan 2).col1 + InitPlan 2 -> Result Output: ((cte.c).f1 > 0) (13 rows) diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out index 7a0d78dfe3..4ffc5b4c56 100644 --- a/src/test/regress/expected/select_parallel.out +++ b/src/test/regress/expected/select_parallel.out @@ -291,14 +291,14 @@ alter table tenk2 set (parallel_workers = 0); explain (costs off) select count(*) from tenk1 where (two, four) not in (select hundred, thousand from tenk2 where thousand > 100); - QUERY PLAN ------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------------------------------- Finalize Aggregate -> Gather Workers Planned: 4 -> Partial Aggregate -> Parallel Seq Scan on tenk1 - Filter: (NOT (hashed SubPlan 1)) + Filter: (NOT (ANY ((two = (hashed SubPlan 1).col1) AND (four = (hashed SubPlan 1).col2)))) SubPlan 1 -> Seq Scan on tenk2 Filter: (thousand > 100) @@ -315,10 +315,10 @@ select count(*) from tenk1 where (two, four) not in explain (costs off) select * from tenk1 where (unique1 + random())::integer not in (select ten from tenk2); - QUERY PLAN ------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------- Seq Scan on tenk1 - Filter: (NOT (hashed SubPlan 1)) + Filter: (NOT (ANY ((((unique1)::double precision + random()))::integer = (hashed SubPlan 1).col1))) SubPlan 1 -> Seq Scan on tenk2 (4 rows) @@ -335,7 +335,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------ Aggregate - InitPlan 1 (returns $2) + InitPlan 1 -> Finalize Aggregate -> Gather Workers Planned: 2 @@ -343,10 +343,9 @@ explain (costs off) -> Parallel Seq Scan on tenk2 -> Gather Workers Planned: 4 - Params Evaluated: $2 -> Parallel Seq Scan on tenk1 - Filter: (unique1 = $2) -(12 rows) + Filter: (unique1 = (InitPlan 1).col1) +(11 rows) select count(*) from tenk1 where tenk1.unique1 = (Select max(tenk2.unique1) from tenk2); @@ -1150,27 +1149,25 @@ ORDER BY 1; -> Append -> Gather Workers Planned: 4 - Params Evaluated: $1 - InitPlan 1 (returns $1) + InitPlan 1 -> Limit -> Gather Workers Planned: 4 -> Parallel Seq Scan on tenk1 tenk1_2 Filter: (fivethous = 1) -> Parallel Seq Scan on tenk1 - Filter: (fivethous = $1) + Filter: (fivethous = (InitPlan 1).col1) -> Gather Workers Planned: 4 - Params Evaluated: $3 - InitPlan 2 (returns $3) + InitPlan 2 -> Limit -> Gather Workers Planned: 4 -> Parallel Seq Scan on tenk1 tenk1_3 Filter: (fivethous = 1) -> Parallel Seq Scan on tenk1 tenk1_1 - Filter: (fivethous = $3) -(25 rows) + Filter: (fivethous = (InitPlan 2).col1) +(23 rows) -- test interaction with SRFs SELECT * FROM information_schema.foreign_data_wrapper_options @@ -1182,10 +1179,10 @@ ORDER BY 1, 2, 3; EXPLAIN (VERBOSE, COSTS OFF) SELECT generate_series(1, two), array(select generate_series(1, two)) FROM tenk1 ORDER BY tenthous; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------- ProjectSet - Output: generate_series(1, tenk1.two), (SubPlan 1), tenk1.tenthous + Output: generate_series(1, tenk1.two), ARRAY(SubPlan 1), tenk1.tenthous -> Gather Merge Output: tenk1.two, tenk1.tenthous Workers Planned: 4 diff --git a/src/test/regress/expected/sqljson.out b/src/test/regress/expected/sqljson.out index 5e7da96be5..cbf8542d8d 100644 --- a/src/test/regress/expected/sqljson.out +++ b/src/test/regress/expected/sqljson.out @@ -1064,8 +1064,8 @@ SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i) RETURNING QUERY PLAN --------------------------------------------------------------------- Result - Output: $0 - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1 + InitPlan 1 -> Aggregate Output: JSON_ARRAYAGG("*VALUES*".column1 RETURNING jsonb) -> Values Scan on "*VALUES*" diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out index e41b728df8..29b11f11aa 100644 --- a/src/test/regress/expected/subselect.out +++ b/src/test/regress/expected/subselect.out @@ -202,6 +202,57 @@ SELECT f1 AS "Correlated Field" 3 (5 rows) +-- Check ROWCOMPARE cases, both correlated and not +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + QUERY PLAN +----------------------------------------------------------------- + Seq Scan on public.subselect_tbl + Output: (((1 = (SubPlan 1).col1) AND (2 = (SubPlan 1).col2))) + SubPlan 1 + -> Result + Output: subselect_tbl.f1, subselect_tbl.f2 +(5 rows) + +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + eq +---- + t + f + f + f + f + f + f + f +(8 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + QUERY PLAN +----------------------------------------------------------------- + Seq Scan on public.subselect_tbl + Output: ((1 = (InitPlan 1).col1) AND (2 = (InitPlan 1).col2)) + InitPlan 1 + -> Result + Output: 3, 4 +(5 rows) + +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + eq +---- + f + f + f + f + f + f + f + f +(8 rows) + +SELECT ROW(1, 2) = (SELECT f1, f2 FROM SUBSELECT_TBL); -- error +ERROR: more than one row returned by a subquery used as an expression -- Subselects without aliases SELECT count FROM (SELECT COUNT(DISTINCT name) FROM road); count @@ -324,18 +375,18 @@ explain (verbose, costs off) select '42' union all select 43; -- check materialization of an initplan reference (bug #14524) explain (verbose, costs off) select 1 = all (select (select 1)); - QUERY PLAN ------------------------------------ + QUERY PLAN +------------------------------------------- Result - Output: (SubPlan 2) + Output: (ALL (1 = (SubPlan 2).col1)) SubPlan 2 -> Materialize - Output: ($0) - InitPlan 1 (returns $0) + Output: ((InitPlan 1).col1) + InitPlan 1 -> Result Output: 1 -> Result - Output: $0 + Output: (InitPlan 1).col1 (10 rows) select 1 = all (select (select 1)); @@ -377,7 +428,7 @@ select * from int4_tbl o where exists QUERY PLAN -------------------------------------- Seq Scan on int4_tbl o - Filter: (SubPlan 1) + Filter: EXISTS(SubPlan 1) SubPlan 1 -> Limit -> Seq Scan on int4_tbl i @@ -840,10 +891,10 @@ select * from outer_text where (f1, f2) not in (select * from inner_text); -- explain (verbose, costs off) select 'foo'::text in (select 'bar'::name union all select 'bar'::name); - QUERY PLAN -------------------------------------- + QUERY PLAN +--------------------------------------------------------- Result - Output: (hashed SubPlan 1) + Output: (ANY ('foo'::text = (hashed SubPlan 1).col1)) SubPlan 1 -> Append -> Result @@ -864,10 +915,10 @@ select 'foo'::text in (select 'bar'::name union all select 'bar'::name); -- explain (verbose, costs off) select row(row(row(1))) = any (select row(row(1))); - QUERY PLAN -------------------------------------------- + QUERY PLAN +-------------------------------------------------------- Result - Output: (SubPlan 1) + Output: (ANY ('("(1)")'::record = (SubPlan 1).col1)) SubPlan 1 -> Materialize Output: '("(1)")'::record @@ -907,10 +958,10 @@ language sql as 'select $1::text = $2'; create operator = (procedure=bogus_int8_text_eq, leftarg=int8, rightarg=text); explain (costs off) select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------- + QUERY PLAN +-------------------------------------------------------- Seq Scan on int8_tbl - Filter: (hashed SubPlan 1) + Filter: (ANY ((q1)::text = (hashed SubPlan 1).col1)) SubPlan 1 -> Seq Scan on inner_text (4 rows) @@ -928,10 +979,10 @@ create or replace function bogus_int8_text_eq(int8, text) returns boolean language sql as 'select $1::text = $2 and $1::text = $2'; explain (costs off) select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------- Seq Scan on int8_tbl - Filter: (hashed SubPlan 1) + Filter: (ANY (((q1)::text = (hashed SubPlan 1).col1) AND ((q1)::text = (hashed SubPlan 1).col1))) SubPlan 1 -> Seq Scan on inner_text (4 rows) @@ -949,10 +1000,10 @@ create or replace function bogus_int8_text_eq(int8, text) returns boolean language sql as 'select $2 = $1::text'; explain (costs off) select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------------- + QUERY PLAN +------------------------------------------------- Seq Scan on int8_tbl - Filter: (SubPlan 1) + Filter: (ANY ((SubPlan 1).col1 = (q1)::text)) SubPlan 1 -> Materialize -> Seq Scan on inner_text @@ -972,11 +1023,11 @@ rollback; -- to get rid of the bogus operator explain (costs off) select count(*) from tenk1 t where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0); - QUERY PLAN --------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------------- Aggregate -> Seq Scan on tenk1 t - Filter: ((hashed SubPlan 2) OR (ten < 0)) + Filter: ((ANY (unique2 = (hashed SubPlan 2).col1)) OR (ten < 0)) SubPlan 2 -> Index Only Scan using tenk1_unique1 on tenk1 k (5 rows) @@ -997,7 +1048,7 @@ where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0) Aggregate -> Bitmap Heap Scan on tenk1 t Recheck Cond: (thousand = 1) - Filter: ((SubPlan 1) OR (ten < 0)) + Filter: (EXISTS(SubPlan 1) OR (ten < 0)) -> Bitmap Index Scan on tenk1_thous_tenthous Index Cond: (thousand = 1) SubPlan 1 @@ -1022,11 +1073,11 @@ analyze exists_tbl; explain (costs off) select * from exists_tbl t1 where (exists(select 1 from exists_tbl t2 where t1.c1 = t2.c2) or c3 < 0); - QUERY PLAN ------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------- Append -> Seq Scan on exists_tbl_null t1_1 - Filter: ((SubPlan 1) OR (c3 < 0)) + Filter: (EXISTS(SubPlan 1) OR (c3 < 0)) SubPlan 1 -> Append -> Seq Scan on exists_tbl_null t2_1 @@ -1034,7 +1085,7 @@ select * from exists_tbl t1 -> Seq Scan on exists_tbl_def t2_2 Filter: (t1_1.c1 = c2) -> Seq Scan on exists_tbl_def t1_2 - Filter: ((hashed SubPlan 2) OR (c3 < 0)) + Filter: ((ANY (c1 = (hashed SubPlan 2).col1)) OR (c3 < 0)) SubPlan 2 -> Append -> Seq Scan on exists_tbl_null t2_4 @@ -1071,14 +1122,14 @@ where a.thousand = b.thousand explain (verbose, costs off) select x, x from (select (select now()) as x from (values(1),(2)) v(y)) ss; - QUERY PLAN ---------------------------- + QUERY PLAN +------------------------------------------------ Values Scan on "*VALUES*" - Output: $0, $1 - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1, (InitPlan 2).col1 + InitPlan 1 -> Result Output: now() - InitPlan 2 (returns $1) + InitPlan 2 -> Result Output: now() (8 rows) @@ -1086,13 +1137,13 @@ explain (verbose, costs off) explain (verbose, costs off) select x, x from (select (select random()) as x from (values(1),(2)) v(y)) ss; - QUERY PLAN ----------------------------------- + QUERY PLAN +----------------------------------- Subquery Scan on ss Output: ss.x, ss.x -> Values Scan on "*VALUES*" - Output: $0 - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1 + InitPlan 1 -> Result Output: random() (7 rows) @@ -1143,14 +1194,14 @@ where o.ten = 0; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Aggregate - Output: sum((((hashed SubPlan 1)))::integer) + Output: sum((((ANY (i.ten = (hashed SubPlan 1).col1))))::integer) -> Nested Loop - Output: ((hashed SubPlan 1)) + Output: ((ANY (i.ten = (hashed SubPlan 1).col1))) -> Seq Scan on public.onek o Output: o.unique1, o.unique2, o.two, o.four, o.ten, o.twenty, o.hundred, o.thousand, o.twothousand, o.fivethous,o.tenthous, o.odd, o.even, o.stringu1, o.stringu2, o.string4 Filter: (o.ten = 0) -> Index Scan using onek_unique1 on public.onek i - Output: (hashed SubPlan 1), random() + Output: (ANY (i.ten = (hashed SubPlan 1).col1)), random() Index Cond: (i.unique1 = o.unique1) SubPlan 1 -> Seq Scan on public.int4_tbl @@ -1346,7 +1397,7 @@ select * from int4_tbl where --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Nested Loop Semi Join Output: int4_tbl.f1 - Join Filter: (CASE WHEN (hashed SubPlan 1) THEN int4_tbl.f1 ELSE NULL::integer END = b.ten) + Join Filter: (CASE WHEN (ANY (int4_tbl.f1 = (hashed SubPlan 1).col1)) THEN int4_tbl.f1 ELSE NULL::integer END = b.ten) -> Seq Scan on public.int4_tbl Output: int4_tbl.f1 -> Seq Scan on public.tenk1 b @@ -1945,10 +1996,10 @@ select * from tenk1 A where exists (select 1 from tenk2 B where A.hundred in (select C.hundred FROM tenk2 C WHERE c.odd = b.odd)); - QUERY PLAN ---------------------------------- + QUERY PLAN +----------------------------------------------------- Nested Loop Semi Join - Join Filter: (SubPlan 1) + Join Filter: (ANY (a.hundred = (SubPlan 1).col1)) -> Seq Scan on tenk1 a -> Materialize -> Seq Scan on tenk2 b @@ -1962,10 +2013,10 @@ WHERE c.odd = b.odd)); explain (costs off) SELECT * FROM tenk1 A LEFT JOIN tenk2 B ON A.hundred in (SELECT c.hundred FROM tenk2 C WHERE c.odd = b.odd); - QUERY PLAN ---------------------------------- + QUERY PLAN +----------------------------------------------------- Nested Loop Left Join - Join Filter: (SubPlan 1) + Join Filter: (ANY (a.hundred = (SubPlan 1).col1)) -> Seq Scan on tenk1 a -> Materialize -> Seq Scan on tenk2 b @@ -1979,10 +2030,10 @@ ON A.hundred in (SELECT c.hundred FROM tenk2 C WHERE c.odd = b.odd); explain (costs off) SELECT * FROM tenk1 A LEFT JOIN tenk2 B ON B.hundred in (SELECT c.hundred FROM tenk2 C WHERE c.odd = a.odd); - QUERY PLAN ---------------------------------- + QUERY PLAN +----------------------------------------------------- Nested Loop Left Join - Join Filter: (SubPlan 1) + Join Filter: (ANY (b.hundred = (SubPlan 1).col1)) -> Seq Scan on tenk1 a -> Materialize -> Seq Scan on tenk2 b @@ -2045,7 +2096,7 @@ ON B.hundred in (SELECT min(c.hundred) FROM tenk2 C WHERE c.odd = b.odd); -> Subquery Scan on "ANY_subquery" Filter: (b.hundred = "ANY_subquery".min) -> Result - InitPlan 1 (returns $1) + InitPlan 1 -> Limit -> Index Scan using tenk2_hundred on tenk2 c Index Cond: (hundred IS NOT NULL) diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index 1062c341d8..713bf84c70 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -3038,18 +3038,18 @@ EXPLAIN (costs off) INSERT INTO rw_view1 VALUES (2, 'New row 2'); QUERY PLAN ----------------------------------------------------------- Insert on base_tbl - InitPlan 1 (returns $0) + InitPlan 1 -> Index Only Scan using base_tbl_pkey on base_tbl t Index Cond: (id = 2) -> Result - One-Time Filter: ($0 IS NOT TRUE) + One-Time Filter: ((InitPlan 1).col1 IS NOT TRUE) Update on base_tbl - InitPlan 1 (returns $0) + InitPlan 1 -> Index Only Scan using base_tbl_pkey on base_tbl t Index Cond: (id = 2) -> Result - One-Time Filter: $0 + One-Time Filter: (InitPlan 1).col1 -> Index Scan using base_tbl_pkey on base_tbl Index Cond: (id = 2) (15 rows) @@ -3108,8 +3108,8 @@ 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 t1_1 Update on public.t11 t1_2 @@ -3121,7 +3121,7 @@ UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; -> 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)) + Filter: ((t1_1.a <> 6) AND EXISTS(SubPlan 1) AND snoop(t1_1.a) AND leakproof(t1_1.a)) SubPlan 1 -> Append -> Seq Scan on public.t12 t12_1 @@ -3131,15 +3131,15 @@ UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; -> 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)) + Filter: ((t1_2.a <> 6) AND EXISTS(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)) + Filter: ((t1_3.a <> 6) AND EXISTS(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)) + Filter: ((t1_4.a <> 6) AND EXISTS(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; @@ -3155,8 +3155,8 @@ 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 t1_1 Update on public.t11 t1_2 @@ -3168,7 +3168,7 @@ UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; -> 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)) + Filter: (EXISTS(SubPlan 1) AND snoop(t1_1.a) AND leakproof(t1_1.a)) SubPlan 1 -> Append -> Seq Scan on public.t12 t12_1 @@ -3178,15 +3178,15 @@ UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; -> 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)) + Filter: (EXISTS(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)) + Filter: (EXISTS(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)) + Filter: (EXISTS(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; @@ -3370,10 +3370,10 @@ CREATE RULE v1_upd_rule AS ON UPDATE TO v1 DO INSTEAD CREATE VIEW v2 WITH (security_barrier = true) AS SELECT * FROM v1 WHERE EXISTS (SELECT 1); EXPLAIN (COSTS OFF) UPDATE v2 SET a = 1; - QUERY PLAN ---------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------- Update on t1 - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Merge Join Merge Cond: (t1.a = v1.a) @@ -3384,7 +3384,7 @@ EXPLAIN (COSTS OFF) UPDATE v2 SET a = 1; Sort Key: v1.a -> Subquery Scan on v1 -> Result - One-Time Filter: $0 + One-Time Filter: (InitPlan 1).col1 -> Seq Scan on t1 t1_1 (14 rows) diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out index cb0b033da6..2e19791f46 100644 --- a/src/test/regress/expected/update.out +++ b/src/test/regress/expected/update.out @@ -178,15 +178,15 @@ 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, (SubPlan 1 (returns $1,$2)), t.ctid + Output: (SubPlan 1).col1, (SubPlan 1).col2, rescan(SubPlan 1), t.ctid One-Time Filter: (CURRENT_USER = SESSION_USER) -> Seq Scan on public.update_test t Output: t.a, t.ctid - SubPlan 1 (returns $1,$2) + SubPlan 1 -> Seq Scan on public.update_test s Output: s.b, s.a Filter: (s.a = t.a) diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index 60de2cbf96..e46710cf31 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -4279,7 +4279,7 @@ WHERE c = 1; Subquery Scan on emp Filter: (emp.c = 1) -> WindowAgg - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Sort Sort Key: empsalary.empno DESC diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 6de347b528..7bef181d78 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -3100,7 +3100,7 @@ WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v); CTE cte_init -> Result Output: 1, 'cte_init val'::text - InitPlan 2 (returns $1) + InitPlan 2 -> Limit Output: ((cte_init.b || ' merge update'::text)) -> CTE Scan on cte_init @@ -3143,11 +3143,11 @@ WHEN NOT MATCHED THEN INSERT VALUES(o.a, o.b || (SELECT merge_source_cte.*::text CTE merge_source_cte -> Result Output: 15, 'merge_source_cte val'::text - InitPlan 2 (returns $1) + InitPlan 2 -> CTE Scan on merge_source_cte merge_source_cte_1 Output: ((merge_source_cte_1.b || (merge_source_cte_1.*)::text) || ' merge update'::text) Filter: (merge_source_cte_1.a = 15) - InitPlan 3 (returns $2) + InitPlan 3 -> CTE Scan on merge_source_cte merge_source_cte_2 Output: ((merge_source_cte_2.*)::text || ' merge insert'::text) -> Hash Right Join diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql index 2f3601a058..7c42ebc36f 100644 --- a/src/test/regress/sql/subselect.sql +++ b/src/test/regress/sql/subselect.sql @@ -82,6 +82,20 @@ SELECT f1 AS "Correlated Field" WHERE (f1, f2) IN (SELECT f2, CAST(f3 AS int4) FROM SUBSELECT_TBL WHERE f3 IS NOT NULL); +-- Check ROWCOMPARE cases, both correlated and not + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + +SELECT ROW(1, 2) = (SELECT f1, f2 FROM SUBSELECT_TBL); -- error + -- Subselects without aliases SELECT count FROM (SELECT COUNT(DISTINCT name) FROM road);
I wrote: > Hm. I used "rescan(SubPlan)" in the attached, but it still looks > a bit odd to my eye. After thinking a bit more, I understood what was bothering me about that notation: it looks too much like a call of a user-defined function named "rescan()". I think we'd be better off with the all-caps "RESCAN()". regards, tom lane
On Mon, 18 Mar 2024 at 23:19, Tom Lane <tgl@sss.pgh.pa.us> wrote: > > > Hm. I used "rescan(SubPlan)" in the attached, but it still looks > > a bit odd to my eye. > > After thinking a bit more, I understood what was bothering me about > that notation: it looks too much like a call of a user-defined > function named "rescan()". I think we'd be better off with the > all-caps "RESCAN()". > Or perhaps move the parentheses, and write "(rescan SubPlan N)" or "(reset SubPlan N)". Dunno. Regards, Dean
Dean Rasheed <dean.a.rasheed@gmail.com> writes: > On Mon, 18 Mar 2024 at 23:19, Tom Lane <tgl@sss.pgh.pa.us> wrote: >> After thinking a bit more, I understood what was bothering me about >> that notation: it looks too much like a call of a user-defined >> function named "rescan()". I think we'd be better off with the >> all-caps "RESCAN()". > Or perhaps move the parentheses, and write "(rescan SubPlan N)" or > "(reset SubPlan N)". Dunno. Oh, I like that! It seems rather parallel to the existing "hashed" annotation. If I had it to do over, I'd likely do the "hashed" bit differently --- but as the proposal currently stands, we are not changing "hashed", so we might as well double down on that. I won't update the patch right now, but "(rescan SubPlan N)" seems like a winner to me. regards, tom lane
I wrote: > I won't update the patch right now, but "(rescan SubPlan N)" > seems like a winner to me. Here's a hopefully-final version that makes that adjustment and tweaks a couple of comments. regards, tom lane diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 58a603ac56..acbbf3b56c 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -3062,10 +3062,10 @@ select exists(select 1 from pg_enum), sum(c1) from ft1; QUERY PLAN -------------------------------------------------- Foreign Scan - Output: $0, (sum(ft1.c1)) + Output: (InitPlan 1).col1, (sum(ft1.c1)) Relations: Aggregate on (public.ft1) Remote SQL: SELECT sum("C 1") FROM "S 1"."T 1" - InitPlan 1 (returns $0) + InitPlan 1 -> Seq Scan on pg_catalog.pg_enum (6 rows) @@ -3080,8 +3080,8 @@ select exists(select 1 from pg_enum), sum(c1) from ft1 group by 1; QUERY PLAN --------------------------------------------------- GroupAggregate - Output: $0, sum(ft1.c1) - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1, sum(ft1.c1) + InitPlan 1 -> Seq Scan on pg_catalog.pg_enum -> Foreign Scan on public.ft1 Output: ft1.c1 @@ -3305,10 +3305,10 @@ select sum(c1) filter (where (c1 / c1) * random() <= 1) from ft1 group by c2 ord explain (verbose, costs off) select sum(c2) filter (where c2 in (select c2 from ft1 where c2 < 5)) from ft1; - QUERY PLAN -------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------- Aggregate - Output: sum(ft1.c2) FILTER (WHERE (hashed SubPlan 1)) + Output: sum(ft1.c2) FILTER (WHERE (ANY (ft1.c2 = (hashed SubPlan 1).col1))) -> Foreign Scan on public.ft1 Output: ft1.c2 Remote SQL: SELECT c2 FROM "S 1"."T 1" @@ -6171,9 +6171,9 @@ UPDATE ft2 AS target SET (c2, c7) = ( Update on public.ft2 target Remote SQL: UPDATE "S 1"."T 1" SET c2 = $2, c7 = $3 WHERE ctid = $1 -> Foreign Scan on public.ft2 target - Output: $1, $2, (SubPlan 1 (returns $1,$2)), target.ctid, target.* + Output: (SubPlan 1).col1, (SubPlan 1).col2, (rescan SubPlan 1), target.ctid, target.* Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8, ctid FROM "S 1"."T 1" WHERE (("C 1" > 1100)) FOR UPDATE - SubPlan 1 (returns $1,$2) + SubPlan 1 -> Foreign Scan on public.ft2 src Output: (src.c2 * 10), src.c7 Remote SQL: SELECT c2, c7 FROM "S 1"."T 1" WHERE (($1::integer = "C 1")) @@ -11685,9 +11685,9 @@ SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt W QUERY PLAN ---------------------------------------------------------------------------------------- Nested Loop Left Join - Output: t1.a, t1.b, t1.c, async_pt.a, async_pt.b, async_pt.c, ($0) + Output: t1.a, t1.b, t1.c, async_pt.a, async_pt.b, async_pt.c, ((InitPlan 1).col1) Join Filter: (t1.a = async_pt.a) - InitPlan 1 (returns $0) + InitPlan 1 -> Aggregate Output: count(*) -> Append @@ -11699,10 +11699,10 @@ SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt W Output: t1.a, t1.b, t1.c -> Append -> Async Foreign Scan on public.async_p1 async_pt_1 - Output: async_pt_1.a, async_pt_1.b, async_pt_1.c, $0 + Output: async_pt_1.a, async_pt_1.b, async_pt_1.c, (InitPlan 1).col1 Remote SQL: SELECT a, b, c FROM public.base_tbl1 WHERE ((a < 3000)) -> Async Foreign Scan on public.async_p2 async_pt_2 - Output: async_pt_2.a, async_pt_2.b, async_pt_2.c, $0 + Output: async_pt_2.a, async_pt_2.b, async_pt_2.c, (InitPlan 1).col1 Remote SQL: SELECT a, b, c FROM public.base_tbl2 WHERE ((a < 3000)) (20 rows) @@ -11713,7 +11713,7 @@ SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt W Nested Loop Left Join (actual rows=1 loops=1) Join Filter: (t1.a = async_pt.a) Rows Removed by Join Filter: 399 - InitPlan 1 (returns $0) + InitPlan 1 -> Aggregate (actual rows=1 loops=1) -> Append (actual rows=400 loops=1) -> Async Foreign Scan on async_p1 async_pt_4 (actual rows=200 loops=1) @@ -11936,11 +11936,11 @@ CREATE FOREIGN TABLE foreign_tbl2 () INHERITS (foreign_tbl) SERVER loopback OPTIONS (table_name 'base_tbl'); EXPLAIN (VERBOSE, COSTS OFF) SELECT a FROM base_tbl WHERE (a, random() > 0) IN (SELECT a, random() > 0 FROM foreign_tbl); - QUERY PLAN ------------------------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- Seq Scan on public.base_tbl Output: base_tbl.a - Filter: (SubPlan 1) + Filter: (ANY ((base_tbl.a = (SubPlan 1).col1) AND ((random() > '0'::double precision) = (SubPlan 1).col2))) SubPlan 1 -> Result Output: base_tbl.a, (random() > '0'::double precision) diff --git a/doc/src/sgml/perform.sgml b/doc/src/sgml/perform.sgml index 90822b3f4c..4b831a6206 100644 --- a/doc/src/sgml/perform.sgml +++ b/doc/src/sgml/perform.sgml @@ -573,8 +573,106 @@ WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2; which shows that the planner thinks that sorting <literal>onek</literal> by index-scanning is about 12% more expensive than sequential-scan-and-sort. Of course, the next question is whether it's right about that. - We can investigate that using <command>EXPLAIN ANALYZE</command>, as discussed - below. + We can investigate that using <command>EXPLAIN ANALYZE</command>, as + discussed <link linkend="using-explain-analyze">below</link>. + </para> + + <para> + <indexterm> + <primary>subplan</primary> + </indexterm> + Some query plans involve <firstterm>subplans</firstterm>, which arise + from sub-<literal>SELECT</literal>s in the original query. Such + queries can sometimes be transformed into ordinary join plans, but + when they cannot be, we get plans like: + +<screen> +EXPLAIN VERBOSE SELECT unique1 +FROM tenk1 t +WHERE t.ten < ALL (SELECT o.ten FROM onek o WHERE o.four = t.four); + + QUERY PLAN +-------------------------------------------------------------------&zwsp;------ + Seq Scan on public.tenk1 t (cost=0.00..586095.00 rows=5000 width=4) + Output: t.unique1 + Filter: (ALL (t.ten < (SubPlan 1).col1)) + SubPlan 1 + -> Seq Scan on public.onek o (cost=0.00..116.50 rows=250 width=4) + Output: o.ten + Filter: (o.four = t.four) +</screen> + + This rather artificial example serves to illustrate a couple of + points: values from the outer plan level can be passed down into a + subplan (here, <literal>t.four</literal> is passed down) and the + results of the sub-select are available to the outer plan. Those + result values are shown by <command>EXPLAIN</command> with notations + like + <literal>(<replaceable>subplan_name</replaceable>).col<replaceable>N</replaceable></literal>, + which refers to the <replaceable>N</replaceable>'th output column of + the sub-<literal>SELECT</literal>. + </para> + + <para> + <indexterm> + <primary>subplan</primary> + <secondary>hashed</secondary> + </indexterm> + In the example above, the <literal>ALL</literal> operator runs the + subplan again for each row of the outer query (which accounts for the + high estimated cost). Some queries can use a <firstterm>hashed + subplan</firstterm> to avoid that: + +<screen> +EXPLAIN SELECT * +FROM tenk1 t +WHERE t.unique1 NOT IN (SELECT o.unique1 FROM onek o); + + QUERY PLAN +-------------------------------------------------------------------&zwsp;------------------------- + Seq Scan on tenk1 t (cost=61.77..531.77 rows=5000 width=244) + Filter: (NOT (ANY (unique1 = (hashed SubPlan 1).col1))) + SubPlan 1 + -> Index Only Scan using onek_unique1 on onek o (cost=0.28..59.27 rows=1000 width=4) +(4 rows) +</screen> + + Here, the subplan is run a single time and its output is loaded into + an in-memory hash table, which is then probed by the + outer <literal>ANY</literal> operator. This requires that the + sub-<literal>SELECT</literal> not reference any variables of the outer + query, and that the <literal>ANY</literal>'s comparison operator be + amenable to hashing. + </para> + + <para> + <indexterm> + <primary>initplan</primary> + </indexterm> + If, in addition to not referencing any variables of the outer query, + the sub-<literal>SELECT</literal> cannot return more than one row, + it may instead be implemented as an <firstterm>initplan</firstterm>: + +<screen> +EXPLAIN VERBOSE SELECT unique1 +FROM tenk1 t1 WHERE t1.ten = (SELECT (random() * 10)::integer); + + QUERY PLAN +------------------------------------------------------------&zwsp;-------- + Seq Scan on public.tenk1 t1 (cost=0.02..470.02 rows=1000 width=4) + Output: t1.unique1 + Filter: (t1.ten = (InitPlan 1).col1) + InitPlan 1 + -> Result (cost=0.00..0.02 rows=1 width=4) + Output: ((random() * '10'::double precision))::integer +</screen> + + An initplan is run only once per execution of the outer plan, and its + results are saved for re-use in later rows of the outer plan. So in + this example <literal>random()</literal> is evaluated only once and + all the values of <literal>t1.ten</literal> are compared to the same + randomly-chosen integer. That's quite different from what would + happen without the sub-<literal>SELECT</literal> construct. </para> </sect2> diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index a9d5056af4..926d70afaf 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -116,7 +116,6 @@ static void show_tidbitmap_info(BitmapHeapScanState *planstate, static void show_instrumentation_count(const char *qlabel, int which, PlanState *planstate, ExplainState *es); static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es); -static void show_eval_params(Bitmapset *bms_params, ExplainState *es); static const char *explain_get_index_name(Oid indexId); static bool peek_buffer_usage(ExplainState *es, const BufferUsage *usage); static void show_buffer_usage(ExplainState *es, const BufferUsage *usage); @@ -1914,10 +1913,6 @@ ExplainNode(PlanState *planstate, List *ancestors, ExplainPropertyInteger("Workers Planned", NULL, gather->num_workers, es); - /* Show params evaluated at gather node */ - if (gather->initParam) - show_eval_params(gather->initParam, es); - if (es->analyze) { int nworkers; @@ -1942,10 +1937,6 @@ ExplainNode(PlanState *planstate, List *ancestors, ExplainPropertyInteger("Workers Planned", NULL, gm->num_workers, es); - /* Show params evaluated at gather-merge node */ - if (gm->initParam) - show_eval_params(gm->initParam, es); - if (es->analyze) { int nworkers; @@ -3550,29 +3541,6 @@ show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es) } } -/* - * Show initplan params evaluated at Gather or Gather Merge node. - */ -static void -show_eval_params(Bitmapset *bms_params, ExplainState *es) -{ - int paramid = -1; - List *params = NIL; - - Assert(bms_params); - - while ((paramid = bms_next_member(bms_params, paramid)) >= 0) - { - char param[32]; - - snprintf(param, sizeof(param), "$%d", paramid); - params = lappend(params, pstrdup(param)); - } - - if (params) - ExplainPropertyList("Params Evaluated", params, es); -} - /* * Fetch the name of an index in an EXPLAIN * diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index b9e0c960bd..d6954a7e86 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -560,22 +560,9 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot, splan->plan_id); /* Label the subplan for EXPLAIN purposes */ - splan->plan_name = palloc(32 + 12 * list_length(splan->setParam)); - sprintf(splan->plan_name, "%s %d", - isInitPlan ? "InitPlan" : "SubPlan", - splan->plan_id); - if (splan->setParam) - { - char *ptr = splan->plan_name + strlen(splan->plan_name); - - ptr += sprintf(ptr, " (returns "); - foreach(lc, splan->setParam) - { - ptr += sprintf(ptr, "$%d%s", - lfirst_int(lc), - lnext(splan->setParam, lc) ? "," : ")"); - } - } + splan->plan_name = psprintf("%s %d", + isInitPlan ? "InitPlan" : "SubPlan", + splan->plan_id); /* Lastly, fill in the cost estimates for use later */ cost_subplan(root, splan, plan); @@ -3047,8 +3034,7 @@ SS_make_initplan_from_plan(PlannerInfo *root, node = makeNode(SubPlan); node->subLinkType = EXPR_SUBLINK; node->plan_id = list_length(root->glob->subplans); - node->plan_name = psprintf("InitPlan %d (returns $%d)", - node->plan_id, prm->paramid); + node->plan_name = psprintf("InitPlan %d", node->plan_id); get_first_col_type(plan, &node->firstColType, &node->firstColTypmod, &node->firstColCollation); node->parallel_safe = plan->parallel_safe; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index f2893d4086..752757be11 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -437,6 +437,10 @@ static void resolve_special_varno(Node *node, deparse_context *context, rsv_callback callback, void *callback_arg); static Node *find_param_referent(Param *param, deparse_context *context, deparse_namespace **dpns_p, ListCell **ancestor_cell_p); +static SubPlan *find_param_generator(Param *param, deparse_context *context, + int *column_p); +static SubPlan *find_param_generator_initplan(Param *param, Plan *plan, + int *column_p); static void get_parameter(Param *param, deparse_context *context); static const char *get_simple_binary_op_name(OpExpr *expr); static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags); @@ -8134,6 +8138,128 @@ find_param_referent(Param *param, deparse_context *context, return NULL; } +/* + * Try to find a subplan/initplan that emits the value for a PARAM_EXEC Param. + * + * If successful, return the generating subplan/initplan and set *column_p + * to the subplan's 0-based output column number. + * Otherwise, return NULL. + */ +static SubPlan * +find_param_generator(Param *param, deparse_context *context, int *column_p) +{ + /* Initialize output parameter to prevent compiler warnings */ + *column_p = 0; + + /* + * If it's a PARAM_EXEC parameter, search the current plan node as well as + * ancestor nodes looking for a subplan or initplan that emits the value + * for the Param. It could appear in the setParams of an initplan or + * MULTIEXPR_SUBLINK subplan, or in the paramIds of an ancestral SubPlan. + */ + if (param->paramkind == PARAM_EXEC) + { + SubPlan *result; + deparse_namespace *dpns; + ListCell *lc; + + dpns = (deparse_namespace *) linitial(context->namespaces); + + /* First check the innermost plan node's initplans */ + result = find_param_generator_initplan(param, dpns->plan, column_p); + if (result) + return result; + + /* + * The plan's targetlist might contain MULTIEXPR_SUBLINK SubPlans, + * which can be referenced by Params elsewhere in the targetlist. + * (Such Params should always be in the same targetlist, so there's no + * need to do this work at upper plan nodes.) + */ + foreach_node(TargetEntry, tle, dpns->plan->targetlist) + { + if (tle->expr && IsA(tle->expr, SubPlan)) + { + SubPlan *subplan = (SubPlan *) tle->expr; + + if (subplan->subLinkType == MULTIEXPR_SUBLINK) + { + foreach_int(paramid, subplan->setParam) + { + if (paramid == param->paramid) + { + /* Found a match, so return it. */ + *column_p = foreach_current_index(paramid); + return subplan; + } + } + } + } + } + + /* No luck, so check the ancestor nodes */ + foreach(lc, dpns->ancestors) + { + Node *ancestor = (Node *) lfirst(lc); + + /* + * If ancestor is a SubPlan, check the paramIds it provides. + */ + if (IsA(ancestor, SubPlan)) + { + SubPlan *subplan = (SubPlan *) ancestor; + + foreach_int(paramid, subplan->paramIds) + { + if (paramid == param->paramid) + { + /* Found a match, so return it. */ + *column_p = foreach_current_index(paramid); + return subplan; + } + } + + /* SubPlan isn't a kind of Plan, so skip the rest */ + continue; + } + + /* + * Otherwise, it's some kind of Plan node, so check its initplans. + */ + result = find_param_generator_initplan(param, (Plan *) ancestor, + column_p); + if (result) + return result; + + /* No luck, crawl up to next ancestor */ + } + } + + /* No generator found */ + return NULL; +} + +/* + * Subroutine for find_param_generator: search one Plan node's initplans + */ +static SubPlan * +find_param_generator_initplan(Param *param, Plan *plan, int *column_p) +{ + foreach_node(SubPlan, subplan, plan->initPlan) + { + foreach_int(paramid, subplan->setParam) + { + if (paramid == param->paramid) + { + /* Found a match, so return it. */ + *column_p = foreach_current_index(paramid); + return subplan; + } + } + } + return NULL; +} + /* * Display a Param appropriately. */ @@ -8143,12 +8269,13 @@ get_parameter(Param *param, deparse_context *context) Node *expr; deparse_namespace *dpns; ListCell *ancestor_cell; + SubPlan *subplan; + int column; /* * If it's a PARAM_EXEC parameter, try to locate the expression from which - * the parameter was computed. Note that failing to find a referent isn't - * an error, since the Param might well be a subplan output rather than an - * input. + * the parameter was computed. This stanza handles only cases in which + * the Param represents an input to the subplan we are currently in. */ expr = find_param_referent(param, context, &dpns, &ancestor_cell); if (expr) @@ -8192,6 +8319,24 @@ get_parameter(Param *param, deparse_context *context) return; } + /* + * Alternatively, maybe it's a subplan output, which we print as a + * reference to the subplan. (We could drill down into the subplan and + * print the relevant targetlist expression, but that has been deemed too + * confusing since it would violate normal SQL scope rules. Also, we're + * relying on this reference to show that the testexpr containing the + * Param has anything to do with that subplan at all.) + */ + subplan = find_param_generator(param, context, &column); + if (subplan) + { + appendStringInfo(context->buf, "(%s%s).col%d", + subplan->useHashTable ? "hashed " : "", + subplan->plan_name, column + 1); + + return; + } + /* * If it's an external parameter, see if the outermost namespace provides * function argument names. @@ -8240,7 +8385,12 @@ get_parameter(Param *param, deparse_context *context) /* * Not PARAM_EXEC, or couldn't find referent: just print $N. + * + * It's a bug if we get here for anything except PARAM_EXTERN Params, but + * in production builds printing $N seems more useful than failing. */ + Assert(param->paramkind == PARAM_EXTERN); + appendStringInfo(context->buf, "$%d", param->paramid); } @@ -8881,12 +9031,79 @@ get_rule_expr(Node *node, deparse_context *context, * We cannot see an already-planned subplan in rule deparsing, * only while EXPLAINing a query plan. We don't try to * reconstruct the original SQL, just reference the subplan - * that appears elsewhere in EXPLAIN's result. + * that appears elsewhere in EXPLAIN's result. It does seem + * useful to show the subLinkType and testexpr (if any), and + * we also note whether the subplan will be hashed. */ - if (subplan->useHashTable) - appendStringInfo(buf, "(hashed %s)", subplan->plan_name); + switch (subplan->subLinkType) + { + case EXISTS_SUBLINK: + appendStringInfoString(buf, "EXISTS("); + Assert(subplan->testexpr == NULL); + break; + case ALL_SUBLINK: + appendStringInfoString(buf, "(ALL "); + Assert(subplan->testexpr != NULL); + break; + case ANY_SUBLINK: + appendStringInfoString(buf, "(ANY "); + Assert(subplan->testexpr != NULL); + break; + case ROWCOMPARE_SUBLINK: + /* Parenthesizing the testexpr seems sufficient */ + appendStringInfoChar(buf, '('); + Assert(subplan->testexpr != NULL); + break; + case EXPR_SUBLINK: + /* No need to decorate these subplan references */ + appendStringInfoChar(buf, '('); + Assert(subplan->testexpr == NULL); + break; + case MULTIEXPR_SUBLINK: + /* MULTIEXPR isn't executed in the normal way */ + appendStringInfoString(buf, "(rescan "); + Assert(subplan->testexpr == NULL); + break; + case ARRAY_SUBLINK: + appendStringInfoString(buf, "ARRAY("); + Assert(subplan->testexpr == NULL); + break; + case CTE_SUBLINK: + /* This case is unreachable within expressions */ + appendStringInfoString(buf, "CTE("); + Assert(subplan->testexpr == NULL); + break; + } + + if (subplan->testexpr != NULL) + { + deparse_namespace *dpns; + + /* + * Push SubPlan into ancestors list while deparsing + * testexpr, so that we can handle PARAM_EXEC references + * to the SubPlan's paramIds. (This makes it look like + * the SubPlan is an "ancestor" of the current plan node, + * which is a little weird, but it does no harm.) In this + * path, we don't need to mention the SubPlan explicitly, + * because the referencing Params will show its existence. + */ + dpns = (deparse_namespace *) linitial(context->namespaces); + dpns->ancestors = lcons(subplan, dpns->ancestors); + + get_rule_expr(subplan->testexpr, context, showimplicit); + appendStringInfoChar(buf, ')'); + + dpns->ancestors = list_delete_first(dpns->ancestors); + } else - appendStringInfo(buf, "(%s)", subplan->plan_name); + { + /* No referencing Params, so show the SubPlan's name */ + if (subplan->useHashTable) + appendStringInfo(buf, "hashed %s)", subplan->plan_name); + else + appendStringInfo(buf, "%s)", subplan->plan_name); + } } break; diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index f86cf8d258..d54a255e58 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -738,7 +738,7 @@ select array(select sum(x+y) s QUERY PLAN ------------------------------------------------------------------- Function Scan on pg_catalog.generate_series x - Output: (SubPlan 1) + Output: ARRAY(SubPlan 1) Function Call: generate_series(1, 3) SubPlan 1 -> Sort @@ -915,7 +915,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan using tenk1_unique1 on tenk1 Index Cond: (unique1 IS NOT NULL) @@ -932,7 +932,7 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique1 on tenk1 Index Cond: (unique1 IS NOT NULL) @@ -949,7 +949,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique1 on tenk1 Index Cond: ((unique1 IS NOT NULL) AND (unique1 < 42)) @@ -966,7 +966,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique1 on tenk1 Index Cond: ((unique1 IS NOT NULL) AND (unique1 > 42)) @@ -989,7 +989,7 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique1 on tenk1 Index Cond: ((unique1 IS NOT NULL) AND (unique1 > 42000)) @@ -1008,7 +1008,7 @@ explain (costs off) QUERY PLAN ---------------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_thous_tenthous on tenk1 Index Cond: ((thousand = 33) AND (tenthous IS NOT NULL)) @@ -1025,7 +1025,7 @@ explain (costs off) QUERY PLAN -------------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan using tenk1_thous_tenthous on tenk1 Index Cond: ((thousand = 33) AND (tenthous IS NOT NULL)) @@ -1046,7 +1046,7 @@ explain (costs off) Seq Scan on int4_tbl SubPlan 2 -> Result - InitPlan 1 (returns $1) + InitPlan 1 -> Limit -> Index Only Scan using tenk1_unique1 on tenk1 Index Cond: ((unique1 IS NOT NULL) AND (unique1 > int4_tbl.f1)) @@ -1069,8 +1069,8 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------- HashAggregate - Group Key: $0 - InitPlan 1 (returns $0) + Group Key: (InitPlan 1).col1 + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1088,8 +1088,8 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------- Sort - Sort Key: ($0) - InitPlan 1 (returns $0) + Sort Key: ((InitPlan 1).col1) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1107,8 +1107,8 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------- Sort - Sort Key: ($0) - InitPlan 1 (returns $0) + Sort Key: ((InitPlan 1).col1) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1126,8 +1126,8 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------- Sort - Sort Key: (($0 + 1)) - InitPlan 1 (returns $0) + Sort Key: (((InitPlan 1).col1 + 1)) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1146,7 +1146,7 @@ explain (costs off) --------------------------------------------------------------------- Sort Sort Key: (generate_series(1, 3)) DESC - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 Index Cond: (unique2 IS NOT NULL) @@ -1168,7 +1168,7 @@ explain (costs off) QUERY PLAN ---------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Result One-Time Filter: (100 IS NOT NULL) @@ -1199,7 +1199,7 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Merge Append Sort Key: minmaxtest.f1 @@ -1210,7 +1210,7 @@ explain (costs off) -> Index Only Scan Backward using minmaxtest2i on minmaxtest2 minmaxtest_3 Index Cond: (f1 IS NOT NULL) -> Index Only Scan using minmaxtest3i on minmaxtest3 minmaxtest_4 - InitPlan 2 (returns $1) + InitPlan 2 -> Limit -> Merge Append Sort Key: minmaxtest_5.f1 DESC @@ -1235,7 +1235,7 @@ explain (costs off) QUERY PLAN --------------------------------------------------------------------------------------------- Unique - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Merge Append Sort Key: minmaxtest.f1 @@ -1246,7 +1246,7 @@ explain (costs off) -> Index Only Scan Backward using minmaxtest2i on minmaxtest2 minmaxtest_3 Index Cond: (f1 IS NOT NULL) -> Index Only Scan using minmaxtest3i on minmaxtest3 minmaxtest_4 - InitPlan 2 (returns $1) + InitPlan 2 -> Limit -> Merge Append Sort Key: minmaxtest_5.f1 DESC @@ -1258,7 +1258,7 @@ explain (costs off) Index Cond: (f1 IS NOT NULL) -> Index Only Scan Backward using minmaxtest3i on minmaxtest3 minmaxtest_9 -> Sort - Sort Key: ($0), ($1) + Sort Key: ((InitPlan 1).col1), ((InitPlan 2).col1) -> Result (26 rows) diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index a3b9aaca84..e1f0660810 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -559,7 +559,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan using tenk1_unique1 on tenk1 Index Cond: (unique1 IS NOT NULL) @@ -2109,14 +2109,14 @@ order by a, b, c; -- test handling of outer GroupingFunc within subqueries explain (costs off) select (select grouping(v1)) from (values ((select 1))) v(v1) group by cube(v1); - QUERY PLAN ---------------------------- + QUERY PLAN +------------------------------- MixedAggregate - Hash Key: $2 + Hash Key: (InitPlan 3).col1 Group Key: () - InitPlan 1 (returns $1) + InitPlan 1 -> Result - InitPlan 3 (returns $2) + InitPlan 3 -> Result -> Result SubPlan 2 @@ -2132,12 +2132,12 @@ select (select grouping(v1)) from (values ((select 1))) v(v1) group by cube(v1); explain (costs off) select (select grouping(v1)) from (values ((select 1))) v(v1) group by v1; - QUERY PLAN ---------------------------- + QUERY PLAN +---------------- GroupAggregate - InitPlan 1 (returns $1) + InitPlan 1 -> Result - InitPlan 3 (returns $2) + InitPlan 3 -> Result -> Result SubPlan 2 diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index 130a924228..7837126bd2 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -1643,8 +1643,8 @@ explain (verbose, costs off) select min(1-id) from matest0; QUERY PLAN --------------------------------------------------------------------------------- Result - Output: $0 - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1 + InitPlan 1 -> Limit Output: ((1 - matest0.id)) -> Result @@ -1800,7 +1800,7 @@ SELECT min(x) FROM QUERY PLAN -------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Merge Append Sort Key: a.unique1 @@ -1818,7 +1818,7 @@ SELECT min(y) FROM QUERY PLAN -------------------------------------------------------------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Merge Append Sort Key: a.unique1 @@ -1898,19 +1898,19 @@ insert into inhpar select x, x::text from generate_series(1,5) x; insert into inhcld select x::text, x from generate_series(6,10) x; explain (verbose, costs off) update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1); - QUERY PLAN -------------------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------------------------------- Update on public.inhpar i Update on public.inhpar i_1 Update on public.inhcld i_2 -> Result - Output: $2, $3, (SubPlan 1 (returns $2,$3)), i.tableoid, i.ctid + Output: (SubPlan 1).col1, (SubPlan 1).col2, (rescan SubPlan 1), i.tableoid, i.ctid -> Append -> Seq Scan on public.inhpar i_1 Output: i_1.f1, i_1.f2, i_1.tableoid, i_1.ctid -> Seq Scan on public.inhcld i_2 Output: i_2.f1, i_2.f2, i_2.tableoid, i_2.ctid - SubPlan 1 (returns $2,$3) + SubPlan 1 -> Limit Output: (i.f1), (((i.f2)::text || '-'::text)) -> Seq Scan on public.int4_tbl @@ -1946,21 +1946,21 @@ alter table inhpar attach partition inhcld2 for values from (5) to (100); insert into inhpar select x, x::text from generate_series(1,10) x; explain (verbose, costs off) update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1); - QUERY PLAN ------------------------------------------------------------------------------------ + QUERY PLAN +------------------------------------------------------------------------------------------------------ Update on public.inhpar i Update on public.inhcld1 i_1 Update on public.inhcld2 i_2 -> Append -> Seq Scan on public.inhcld1 i_1 - Output: $2, $3, (SubPlan 1 (returns $2,$3)), i_1.tableoid, i_1.ctid - SubPlan 1 (returns $2,$3) + Output: (SubPlan 1).col1, (SubPlan 1).col2, (rescan SubPlan 1), i_1.tableoid, i_1.ctid + SubPlan 1 -> Limit Output: (i_1.f1), (((i_1.f2)::text || '-'::text)) -> Seq Scan on public.int4_tbl Output: i_1.f1, ((i_1.f2)::text || '-'::text) -> Seq Scan on public.inhcld2 i_2 - Output: $2, $3, (SubPlan 1 (returns $2,$3)), i_2.tableoid, i_2.ctid + Output: (SubPlan 1).col1, (SubPlan 1).col2, (rescan SubPlan 1), i_2.tableoid, i_2.ctid (13 rows) update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1); @@ -2845,11 +2845,11 @@ explain (costs off) select min(a), max(a) from parted_minmax where b = '12345'; QUERY PLAN ------------------------------------------------------------------------------------------------ Result - InitPlan 1 (returns $0) + InitPlan 1 -> Limit -> Index Only Scan using parted_minmax1i on parted_minmax1 parted_minmax Index Cond: ((a IS NOT NULL) AND (b = '12345'::text)) - InitPlan 2 (returns $1) + InitPlan 2 -> Limit -> Index Only Scan Backward using parted_minmax1i on parted_minmax1 parted_minmax_1 Index Cond: ((a IS NOT NULL) AND (b = '12345'::text)) diff --git a/src/test/regress/expected/insert_conflict.out b/src/test/regress/expected/insert_conflict.out index 563c5eb52a..701217ddbc 100644 --- a/src/test/regress/expected/insert_conflict.out +++ b/src/test/regress/expected/insert_conflict.out @@ -50,7 +50,7 @@ explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on con Insert on insertconflicttest Conflict Resolution: UPDATE Conflict Arbiter Indexes: op_index_key, collation_index_key, both_index_key - Conflict Filter: (SubPlan 1) + Conflict Filter: EXISTS(SubPlan 1) -> Result SubPlan 1 -> Index Only Scan using both_index_expr_key on insertconflicttest ii diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 9605400021..63cddac0d6 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -3028,10 +3028,10 @@ where unique1 in (select unique2 from tenk1 b); explain (costs off) select a.* from tenk1 a where unique1 not in (select unique2 from tenk1 b); - QUERY PLAN --------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------- Seq Scan on tenk1 a - Filter: (NOT (hashed SubPlan 1)) + Filter: (NOT (ANY (unique1 = (hashed SubPlan 1).col1))) SubPlan 1 -> Index Only Scan using tenk1_unique2 on tenk1 b (4 rows) @@ -5278,12 +5278,12 @@ explain (costs off) select a.unique1, b.unique2 from onek a left join onek b on a.unique1 = b.unique2 where (b.unique2, random() > 0) = any (select q1, random() > 0 from int8_tbl c where c.q1 < b.unique1); - QUERY PLAN ----------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------------ Hash Join Hash Cond: (b.unique2 = a.unique1) -> Seq Scan on onek b - Filter: (SubPlan 1) + Filter: (ANY ((unique2 = (SubPlan 1).col1) AND ((random() > '0'::double precision) = (SubPlan 1).col2))) SubPlan 1 -> Seq Scan on int8_tbl c Filter: (q1 < b.unique1) @@ -8262,8 +8262,8 @@ lateral (select * from int8_tbl t1, where q2 = (select greatest(t1.q1,t2.q2)) and (select v.id=0)) offset 0) ss2) ss where t1.q1 = ss.q2) ss0; - QUERY PLAN -------------------------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------- Nested Loop Output: "*VALUES*".column1, t1.q1, t1.q2, ss2.q1, ss2.q2 -> Seq Scan on public.int8_tbl t1 @@ -8277,20 +8277,20 @@ lateral (select * from int8_tbl t1, Filter: (t1.q1 = ss2.q2) -> Seq Scan on public.int8_tbl t2 Output: t2.q1, t2.q2 - Filter: (SubPlan 3) + Filter: (ANY ((t2.q1 = (SubPlan 3).col1) AND ((random() > '0'::double precision) = (SubPlan 3).col2))) SubPlan 3 -> Result Output: t3.q2, (random() > '0'::double precision) - One-Time Filter: $4 - InitPlan 1 (returns $2) + One-Time Filter: (InitPlan 2).col1 + InitPlan 1 -> Result Output: GREATEST(t1.q1, t2.q2) - InitPlan 2 (returns $4) + InitPlan 2 -> Result Output: ("*VALUES*".column1 = 0) -> Seq Scan on public.int8_tbl t3 Output: t3.q1, t3.q2 - Filter: (t3.q2 = $2) + Filter: (t3.q2 = (InitPlan 1).col1) (27 rows) select * from (values (0), (1)) v(id), diff --git a/src/test/regress/expected/memoize.out b/src/test/regress/expected/memoize.out index 1c8d996740..0fd103c06b 100644 --- a/src/test/regress/expected/memoize.out +++ b/src/test/regress/expected/memoize.out @@ -342,7 +342,7 @@ WHERE unique1 < 3 ---------------------------------------------------------------- Index Scan using tenk1_unique1 on tenk1 t0 Index Cond: (unique1 < 3) - Filter: (SubPlan 1) + Filter: EXISTS(SubPlan 1) SubPlan 1 -> Nested Loop -> Index Scan using tenk1_hundred on tenk1 t2 diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out index 07561f0240..cec7f11f9f 100644 --- a/src/test/regress/expected/merge.out +++ b/src/test/regress/expected/merge.out @@ -1697,10 +1697,10 @@ WHEN MATCHED AND t.c > s.cnt THEN -> Seq Scan on public.ref r_2 Output: r_2.ab, r_2.cd Filter: ((r_2.ab = (s.a + s.b)) AND (r_2.cd = (s.c - s.d))) - SubPlan 3 (returns $9,$10) + SubPlan 3 -> Result - Output: s.b, $8 - InitPlan 2 (returns $8) + Output: s.b, (InitPlan 2).col1 + InitPlan 2 -> Aggregate Output: count(*) -> Seq Scan on public.ref r_1 diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index 9c20a24982..7ca98397ae 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -2176,36 +2176,36 @@ explain (analyze, costs off, summary off, timing off) execute ab_q1 (2, 4); prepare ab_q2 (int, int) as select a from ab where a between $1 and $2 and b < (select 3); explain (analyze, costs off, summary off, timing off) execute ab_q2 (2, 2); - QUERY PLAN ---------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------- Append (actual rows=0 loops=1) Subplans Removed: 6 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on ab_a2_b1 ab_1 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b < $0)) + Filter: ((a >= $1) AND (a <= $2) AND (b < (InitPlan 1).col1)) -> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1) - Filter: ((a >= $1) AND (a <= $2) AND (b < $0)) + Filter: ((a >= $1) AND (a <= $2) AND (b < (InitPlan 1).col1)) -> Seq Scan on ab_a2_b3 ab_3 (never executed) - Filter: ((a >= $1) AND (a <= $2) AND (b < $0)) + Filter: ((a >= $1) AND (a <= $2) AND (b < (InitPlan 1).col1)) (10 rows) -- As above, but swap the PARAM_EXEC Param to the first partition level prepare ab_q3 (int, int) as select a from ab where b between $1 and $2 and a < (select 3); explain (analyze, costs off, summary off, timing off) execute ab_q3 (2, 2); - QUERY PLAN ---------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------- Append (actual rows=0 loops=1) Subplans Removed: 6 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on ab_a1_b2 ab_1 (actual rows=0 loops=1) - Filter: ((b >= $1) AND (b <= $2) AND (a < $0)) + Filter: ((b >= $1) AND (b <= $2) AND (a < (InitPlan 1).col1)) -> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1) - Filter: ((b >= $1) AND (b <= $2) AND (a < $0)) + Filter: ((b >= $1) AND (b <= $2) AND (a < (InitPlan 1).col1)) -> Seq Scan on ab_a3_b2 ab_3 (never executed) - Filter: ((b >= $1) AND (b <= $2) AND (a < $0)) + Filter: ((b >= $1) AND (b <= $2) AND (a < (InitPlan 1).col1)) (10 rows) -- @@ -2411,25 +2411,24 @@ select explain_parallel_append('execute ab_q5 (33, 44, 55)'); -- Test Parallel Append with PARAM_EXEC Params select explain_parallel_append('select count(*) from ab where (a = (select 1) or a = (select 3)) and b = 2'); - explain_parallel_append ------------------------------------------------------------------------------- + explain_parallel_append +------------------------------------------------------------------------------------------------ Aggregate (actual rows=N loops=N) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=N loops=N) - InitPlan 2 (returns $1) + InitPlan 2 -> Result (actual rows=N loops=N) -> Gather (actual rows=N loops=N) Workers Planned: 2 - Params Evaluated: $0, $1 Workers Launched: N -> Parallel Append (actual rows=N loops=N) -> Parallel Seq Scan on ab_a1_b2 ab_1 (actual rows=N loops=N) - Filter: ((b = 2) AND ((a = $0) OR (a = $1))) + Filter: ((b = 2) AND ((a = (InitPlan 1).col1) OR (a = (InitPlan 2).col1))) -> Parallel Seq Scan on ab_a2_b2 ab_2 (never executed) - Filter: ((b = 2) AND ((a = $0) OR (a = $1))) + Filter: ((b = 2) AND ((a = (InitPlan 1).col1) OR (a = (InitPlan 2).col1))) -> Parallel Seq Scan on ab_a3_b2 ab_3 (actual rows=N loops=N) - Filter: ((b = 2) AND ((a = $0) OR (a = $1))) -(16 rows) + Filter: ((b = 2) AND ((a = (InitPlan 1).col1) OR (a = (InitPlan 2).col1))) +(15 rows) -- Test pruning during parallel nested loop query create table lprt_a (a int not null); @@ -2629,57 +2628,57 @@ select * from ab where a = (select max(a) from lprt_a) and b = (select max(a)-1 QUERY PLAN ------------------------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Aggregate (actual rows=1 loops=1) -> Seq Scan on lprt_a (actual rows=102 loops=1) - InitPlan 2 (returns $1) + InitPlan 2 -> Aggregate (actual rows=1 loops=1) -> Seq Scan on lprt_a lprt_a_1 (actual rows=102 loops=1) -> Bitmap Heap Scan on ab_a1_b1 ab_1 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a1_b1_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a1_b2 ab_2 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a1_b2_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a1_b3 ab_3 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a1_b3_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a2_b1 ab_4 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a2_b1_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a2_b2 ab_5 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a2_b2_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a2_b3 ab_6 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a2_b3_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a3_b1 ab_7 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a3_b1_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a3_b2 ab_8 (actual rows=0 loops=1) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a3_b2_a_idx (actual rows=0 loops=1) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) -> Bitmap Heap Scan on ab_a3_b3 ab_9 (never executed) - Recheck Cond: (a = $0) - Filter: (b = $1) + Recheck Cond: (a = (InitPlan 1).col1) + Filter: (b = (InitPlan 2).col1) -> Bitmap Index Scan on ab_a3_b3_a_idx (never executed) - Index Cond: (a = $0) + Index Cond: (a = (InitPlan 1).col1) (52 rows) -- Test run-time partition pruning with UNION ALL parents @@ -2688,42 +2687,42 @@ select * from (select * from ab where a = 1 union all select * from ab) ab where QUERY PLAN ------------------------------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Append (actual rows=0 loops=1) -> Bitmap Heap Scan on ab_a1_b1 ab_11 (actual rows=0 loops=1) Recheck Cond: (a = 1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> 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_12 (never executed) Recheck Cond: (a = 1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Bitmap Index Scan on ab_a1_b2_a_idx (never executed) Index Cond: (a = 1) -> Bitmap Heap Scan on ab_a1_b3 ab_13 (never executed) Recheck Cond: (a = 1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Bitmap Index Scan on ab_a1_b3_a_idx (never executed) Index Cond: (a = 1) -> Seq Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a1_b2 ab_2 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a1_b3 ab_3 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b1 ab_4 (actual rows=0 loops=1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b2 ab_5 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b3 ab_6 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a3_b1 ab_7 (actual rows=0 loops=1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a3_b2 ab_8 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a3_b3 ab_9 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) (37 rows) -- A case containing a UNION ALL with a non-partitioned child. @@ -2732,44 +2731,44 @@ select * from (select * from ab where a = 1 union all (values(10,5)) union all s QUERY PLAN ------------------------------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Append (actual rows=0 loops=1) -> Bitmap Heap Scan on ab_a1_b1 ab_11 (actual rows=0 loops=1) Recheck Cond: (a = 1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> 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_12 (never executed) Recheck Cond: (a = 1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Bitmap Index Scan on ab_a1_b2_a_idx (never executed) Index Cond: (a = 1) -> Bitmap Heap Scan on ab_a1_b3 ab_13 (never executed) Recheck Cond: (a = 1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Bitmap Index Scan on ab_a1_b3_a_idx (never executed) Index Cond: (a = 1) -> Result (actual rows=0 loops=1) - One-Time Filter: (5 = $0) + One-Time Filter: (5 = (InitPlan 1).col1) -> Seq Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a1_b2 ab_2 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a1_b3 ab_3 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b1 ab_4 (actual rows=0 loops=1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b2 ab_5 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b3 ab_6 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a3_b1 ab_7 (actual rows=0 loops=1) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a3_b2 ab_8 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a3_b3 ab_9 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) (39 rows) -- Another UNION ALL test, but containing a mix of exec init and exec run-time pruning. @@ -2787,27 +2786,27 @@ union all ) ab where a = $1 and b = (select -10); -- Ensure the xy_1 subplan is not pruned. explain (analyze, costs off, summary off, timing off) execute ab_q6(1); - QUERY PLAN --------------------------------------------------- + QUERY PLAN +-------------------------------------------------------- Append (actual rows=0 loops=1) Subplans Removed: 12 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on ab_a1_b1 ab_1 (never executed) - Filter: ((a = $1) AND (b = $0)) + Filter: ((a = $1) AND (b = (InitPlan 1).col1)) -> Seq Scan on ab_a1_b2 ab_2 (never executed) - Filter: ((a = $1) AND (b = $0)) + Filter: ((a = $1) AND (b = (InitPlan 1).col1)) -> Seq Scan on ab_a1_b3 ab_3 (never executed) - Filter: ((a = $1) AND (b = $0)) + Filter: ((a = $1) AND (b = (InitPlan 1).col1)) -> Seq Scan on xy_1 (actual rows=0 loops=1) - Filter: ((x = $1) AND (y = $0)) + Filter: ((x = $1) AND (y = (InitPlan 1).col1)) Rows Removed by Filter: 1 -> Seq Scan on ab_a1_b1 ab_4 (never executed) - Filter: ((a = $1) AND (b = $0)) + Filter: ((a = $1) AND (b = (InitPlan 1).col1)) -> Seq Scan on ab_a1_b2 ab_5 (never executed) - Filter: ((a = $1) AND (b = $0)) + Filter: ((a = $1) AND (b = (InitPlan 1).col1)) -> Seq Scan on ab_a1_b3 ab_6 (never executed) - Filter: ((a = $1) AND (b = $0)) + Filter: ((a = $1) AND (b = (InitPlan 1).col1)) (19 rows) -- Ensure we see just the xy_1 row. @@ -2886,7 +2885,7 @@ update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 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 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Nested Loop (actual rows=3 loops=1) -> Append (actual rows=3 loops=1) @@ -2896,11 +2895,11 @@ update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 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) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b2 ab_a2_2 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) -> Seq Scan on ab_a2_b3 ab_a2_3 (never executed) - Filter: (b = $0) + Filter: (b = (InitPlan 1).col1) (19 rows) select tableoid::regclass, * from ab; @@ -3234,12 +3233,12 @@ select * from listp where a = (select null::int); QUERY PLAN ------------------------------------------------------ Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on listp_1_1 listp_1 (never executed) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) -> Seq Scan on listp_2_1 listp_2 (never executed) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) (7 rows) drop table listp; @@ -3377,14 +3376,14 @@ prepare ps1 as select * from mc3p where a = $1 and abs(b) < (select 3); explain (analyze, costs off, summary off, timing off) execute ps1(1); - QUERY PLAN --------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------- Append (actual rows=1 loops=1) Subplans Removed: 2 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on mc3p1 mc3p_1 (actual rows=1 loops=1) - Filter: ((a = $1) AND (abs(b) < $0)) + Filter: ((a = $1) AND (abs(b) < (InitPlan 1).col1)) (6 rows) deallocate ps1; @@ -3392,16 +3391,16 @@ prepare ps2 as select * from mc3p where a <= $1 and abs(b) < (select 3); explain (analyze, costs off, summary off, timing off) execute ps2(1); - QUERY PLAN --------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------- Append (actual rows=2 loops=1) Subplans Removed: 1 - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) -> Seq Scan on mc3p0 mc3p_1 (actual rows=1 loops=1) - Filter: ((a <= $1) AND (abs(b) < $0)) + Filter: ((a <= $1) AND (abs(b) < (InitPlan 1).col1)) -> Seq Scan on mc3p1 mc3p_2 (actual rows=1 loops=1) - Filter: ((a <= $1) AND (abs(b) < $0)) + Filter: ((a <= $1) AND (abs(b) < (InitPlan 1).col1)) (8 rows) deallocate ps2; @@ -3417,14 +3416,14 @@ select * from boolp where a = (select value from boolvalues where value); QUERY PLAN ----------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Seq Scan on boolvalues (actual rows=1 loops=1) Filter: value Rows Removed by Filter: 1 -> Seq Scan on boolp_f boolp_1 (never executed) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) -> Seq Scan on boolp_t boolp_2 (actual rows=0 loops=1) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) (9 rows) explain (analyze, costs off, summary off, timing off) @@ -3432,14 +3431,14 @@ select * from boolp where a = (select value from boolvalues where not value); QUERY PLAN ----------------------------------------------------------- Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Seq Scan on boolvalues (actual rows=1 loops=1) Filter: (NOT value) Rows Removed by Filter: 1 -> Seq Scan on boolp_f boolp_1 (actual rows=0 loops=1) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) -> Seq Scan on boolp_t boolp_2 (never executed) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) (9 rows) drop table boolp; @@ -3528,18 +3527,18 @@ explain (analyze, costs off, summary off, timing off) select * from ma_test wher ----------------------------------------------------------------------------------------------- Merge Append (actual rows=20 loops=1) Sort Key: ma_test.b - InitPlan 2 (returns $1) + InitPlan 2 -> Result (actual rows=1 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Limit (actual rows=1 loops=1) -> Index Scan using ma_test_p2_b_idx on ma_test_p2 (actual rows=1 loops=1) Index Cond: (b IS NOT NULL) -> Index Scan using ma_test_p1_b_idx on ma_test_p1 ma_test_1 (never executed) - Filter: (a >= $1) + Filter: (a >= (InitPlan 2).col1) -> Index Scan using ma_test_p2_b_idx on ma_test_p2 ma_test_2 (actual rows=10 loops=1) - Filter: (a >= $1) + Filter: (a >= (InitPlan 2).col1) -> Index Scan using ma_test_p3_b_idx on ma_test_p3 ma_test_3 (actual rows=10 loops=1) - Filter: (a >= $1) + Filter: (a >= (InitPlan 2).col1) (14 rows) reset enable_seqscan; @@ -3908,17 +3907,17 @@ from ( select 1, 1, 1 ) s(a, b, c) where s.a = 1 and s.b = 1 and s.c = (select 1); - QUERY PLAN ----------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------- Append - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Seq Scan on p1 p - Filter: ((a = 1) AND (b = 1) AND (c = $0)) + Filter: ((a = 1) AND (b = 1) AND (c = (InitPlan 1).col1)) -> Seq Scan on q111 q1 - Filter: ((a = 1) AND (b = 1) AND (c = $0)) + Filter: ((a = 1) AND (b = 1) AND (c = (InitPlan 1).col1)) -> Result - One-Time Filter: (1 = $0) + One-Time Filter: (1 = (InitPlan 1).col1) (9 rows) select * @@ -3946,18 +3945,18 @@ from ( ) s(a, b, c) where s.a = $1 and s.b = $2 and s.c = (select 1); explain (costs off) execute q (1, 1); - QUERY PLAN ---------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------ Append Subplans Removed: 1 - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Seq Scan on p1 p - Filter: ((a = $1) AND (b = $2) AND (c = $0)) + Filter: ((a = $1) AND (b = $2) AND (c = (InitPlan 1).col1)) -> Seq Scan on q111 q1 - Filter: ((a = $1) AND (b = $2) AND (c = $0)) + Filter: ((a = $1) AND (b = $2) AND (c = (InitPlan 1).col1)) -> Result - One-Time Filter: ((1 = $1) AND (1 = $2) AND (1 = $0)) + One-Time Filter: ((1 = $1) AND (1 = $2) AND (1 = (InitPlan 1).col1)) (10 rows) execute q (1, 1); @@ -3975,11 +3974,11 @@ create table listp2 partition of listp for values in(2) partition by list(b); create table listp2_10 partition of listp2 for values in (10); explain (analyze, costs off, summary off, timing off) select * from listp where a = (select 2) and b <> 10; - QUERY PLAN --------------------------------------------------- + QUERY PLAN +--------------------------------------------------- Seq Scan on listp1 listp (actual rows=0 loops=1) - Filter: ((b <> 10) AND (a = $0)) - InitPlan 1 (returns $0) + Filter: ((b <> 10) AND (a = (InitPlan 1).col1)) + InitPlan 1 -> Result (never executed) (4 rows) @@ -4044,16 +4043,15 @@ select explain_parallel_append('select * from listp where a = (select 1);'); ---------------------------------------------------------------------- Gather (actual rows=N loops=N) Workers Planned: 2 - Params Evaluated: $0 Workers Launched: N - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=N loops=N) -> Parallel Append (actual rows=N loops=N) -> Seq Scan on listp_12_1 listp_1 (actual rows=N loops=N) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) -> Parallel Seq Scan on listp_12_2 listp_2 (never executed) - Filter: (a = $0) -(11 rows) + Filter: (a = (InitPlan 1).col1) +(10 rows) -- Like the above but throw some more complexity at the planner by adding -- a UNION ALL. We expect both sides of the union not to scan the @@ -4069,19 +4067,19 @@ select * from listp where a = (select 2);'); Workers Launched: N -> Parallel Append (actual rows=N loops=N) -> Parallel Append (actual rows=N loops=N) - InitPlan 2 (returns $1) + InitPlan 2 -> Result (actual rows=N loops=N) -> Seq Scan on listp_12_1 listp_1 (never executed) - Filter: (a = $1) + Filter: (a = (InitPlan 2).col1) -> Parallel Seq Scan on listp_12_2 listp_2 (actual rows=N loops=N) - Filter: (a = $1) + Filter: (a = (InitPlan 2).col1) -> Parallel Append (actual rows=N loops=N) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=N loops=N) -> Seq Scan on listp_12_1 listp_4 (actual rows=N loops=N) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) -> Parallel Seq Scan on listp_12_2 listp_5 (never executed) - Filter: (a = $0) + Filter: (a = (InitPlan 1).col1) (18 rows) drop table listp; @@ -4104,20 +4102,20 @@ select * from rangep where b IN((select 1),(select 2)) order by a; QUERY PLAN ------------------------------------------------------------------------------------------------------------ Append (actual rows=0 loops=1) - InitPlan 1 (returns $0) + InitPlan 1 -> Result (actual rows=1 loops=1) - InitPlan 2 (returns $1) + InitPlan 2 -> Result (actual rows=1 loops=1) -> Merge Append (actual rows=0 loops=1) Sort Key: rangep_2.a -> Index Scan using rangep_0_to_100_1_a_idx on rangep_0_to_100_1 rangep_2 (actual rows=0 loops=1) - Filter: (b = ANY (ARRAY[$0, $1])) + Filter: (b = ANY (ARRAY[(InitPlan 1).col1, (InitPlan 2).col1])) -> Index Scan using rangep_0_to_100_2_a_idx on rangep_0_to_100_2 rangep_3 (actual rows=0 loops=1) - Filter: (b = ANY (ARRAY[$0, $1])) + Filter: (b = ANY (ARRAY[(InitPlan 1).col1, (InitPlan 2).col1])) -> Index Scan using rangep_0_to_100_3_a_idx on rangep_0_to_100_3 rangep_4 (never executed) - Filter: (b = ANY (ARRAY[$0, $1])) + Filter: (b = ANY (ARRAY[(InitPlan 1).col1, (InitPlan 2).col1])) -> Index Scan using rangep_100_to_200_a_idx on rangep_100_to_200 rangep_5 (actual rows=0 loops=1) - Filter: (b = ANY (ARRAY[$0, $1])) + Filter: (b = ANY (ARRAY[(InitPlan 1).col1, (InitPlan 2).col1])) (15 rows) reset enable_sort; diff --git a/src/test/regress/expected/portals.out b/src/test/regress/expected/portals.out index f71e0b3d41..06726ed4ab 100644 --- a/src/test/regress/expected/portals.out +++ b/src/test/regress/expected/portals.out @@ -1472,18 +1472,18 @@ rollback; -- Check handling of non-backwards-scan-capable plans with scroll cursors begin; explain (costs off) declare c1 cursor for select (select 42) as x; - QUERY PLAN ---------------------------- + QUERY PLAN +---------------- Result - InitPlan 1 (returns $0) + InitPlan 1 -> Result (3 rows) explain (costs off) declare c1 scroll cursor for select (select 42) as x; - QUERY PLAN ---------------------------- + QUERY PLAN +---------------- Materialize - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Result (4 rows) diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index d507a2c8ca..527cf7e7bf 100644 --- a/src/test/regress/expected/rowsecurity.out +++ b/src/test/regress/expected/rowsecurity.out @@ -265,27 +265,27 @@ NOTICE: f_leak => awesome science fiction (5 rows) EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle); - QUERY PLAN ----------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------- Seq Scan on document - Filter: ((dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) (5 rows) EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle); - QUERY PLAN ------------------------------------------------------------ + QUERY PLAN +-------------------------------------------------------------------------- Hash Join Hash Cond: (category.cid = document.cid) - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on category -> Hash -> Seq Scan on document - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) (9 rows) -- viewpoint from regress_rls_dave @@ -329,27 +329,27 @@ NOTICE: f_leak => awesome technology book (7 rows) EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle); - QUERY PLAN ----------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------- Seq Scan on document - Filter: ((cid <> 44) AND (cid <> 44) AND (cid < 50) AND (dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) + Filter: ((cid <> 44) AND (cid <> 44) AND (cid < 50) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) (5 rows) EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle); - QUERY PLAN ----------------------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------- Hash Join Hash Cond: (category.cid = document.cid) - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on category -> Hash -> Seq Scan on document - Filter: ((cid <> 44) AND (cid <> 44) AND (cid < 50) AND (dlevel <= $0) AND f_leak(dtitle)) + Filter: ((cid <> 44) AND (cid <> 44) AND (cid < 50) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) (9 rows) -- 44 would technically fail for both p2r and p1r, but we should get an error @@ -987,18 +987,18 @@ NOTICE: f_leak => my first satire (4 rows) EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN ------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------- Append - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on part_document_fiction part_document_1 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) -> Seq Scan on part_document_satire part_document_2 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) -> Seq Scan on part_document_nonfiction part_document_3 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) (10 rows) -- viewpoint from regress_rls_carol @@ -1029,18 +1029,18 @@ NOTICE: f_leak => awesome technology book (10 rows) EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN ------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------- Append - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on part_document_fiction part_document_1 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) -> Seq Scan on part_document_satire part_document_2 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) -> Seq Scan on part_document_nonfiction part_document_3 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) (10 rows) -- viewpoint from regress_rls_dave @@ -1059,11 +1059,11 @@ NOTICE: f_leak => awesome science fiction (4 rows) EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN --------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------- Seq Scan on part_document_fiction part_document - Filter: ((cid < 55) AND (dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) + Filter: ((cid < 55) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) (5 rows) @@ -1137,11 +1137,11 @@ NOTICE: f_leak => awesome science fiction (4 rows) EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN --------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------- Seq Scan on part_document_fiction part_document - Filter: ((cid < 55) AND (dlevel <= $0) AND f_leak(dtitle)) - InitPlan 1 (returns $0) + Filter: ((cid < 55) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) (5 rows) @@ -1176,18 +1176,18 @@ NOTICE: f_leak => awesome technology book (11 rows) EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); - QUERY PLAN ------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------- Append - InitPlan 1 (returns $0) + InitPlan 1 -> Index Scan using uaccount_pkey on uaccount Index Cond: (pguser = CURRENT_USER) -> Seq Scan on part_document_fiction part_document_1 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) -> Seq Scan on part_document_satire part_document_2 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) -> Seq Scan on part_document_nonfiction part_document_3 - Filter: ((dlevel <= $0) AND f_leak(dtitle)) + Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle)) (10 rows) -- only owner can change policies @@ -1437,10 +1437,10 @@ NOTICE: f_leak => 03b26944890929ff751653acb2f2af79 (1 row) EXPLAIN (COSTS OFF) SELECT * FROM only s1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------- Seq Scan on s1 - Filter: ((hashed SubPlan 1) AND f_leak(b)) + Filter: ((ANY (a = (hashed SubPlan 1).col1)) AND f_leak(b)) SubPlan 1 -> Seq Scan on s2 Filter: (((x % 2) = 0) AND (y ~~ '%2f%'::text)) @@ -1457,10 +1457,10 @@ NOTICE: f_leak => 03b26944890929ff751653acb2f2af79 (1 row) EXPLAIN (COSTS OFF) SELECT * FROM s1 WHERE f_leak(b); - QUERY PLAN ------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------- Seq Scan on s1 - Filter: ((hashed SubPlan 1) AND f_leak(b)) + Filter: ((ANY (a = (hashed SubPlan 1).col1)) AND f_leak(b)) SubPlan 1 -> Seq Scan on s2 Filter: (((x % 2) = 0) AND (y ~~ '%af%'::text)) @@ -1480,7 +1480,7 @@ EXPLAIN (COSTS OFF) SELECT (SELECT x FROM s1 LIMIT 1) xx, * FROM s2 WHERE y like SubPlan 2 -> Limit -> Seq Scan on s1 - Filter: (hashed SubPlan 1) + Filter: (ANY (a = (hashed SubPlan 1).col1)) SubPlan 1 -> Seq Scan on s2 s2_1 Filter: (((x % 2) = 0) AND (y ~~ '%af%'::text)) @@ -2717,10 +2717,10 @@ NOTICE: f_leak => bbb (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 0) AND f_leak(b)) + Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 0) AND f_leak(b)) SubPlan 1 -> Seq Scan on z1_blacklist (4 rows) @@ -2735,10 +2735,10 @@ NOTICE: f_leak => bbb (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 0) AND f_leak(b)) + Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 0) AND f_leak(b)) SubPlan 1 -> Seq Scan on z1_blacklist (4 rows) @@ -2907,10 +2907,10 @@ NOTICE: f_leak => bbb (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 0) AND f_leak(b)) + Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 0) AND f_leak(b)) SubPlan 1 -> Seq Scan on z1_blacklist (4 rows) @@ -2933,10 +2933,10 @@ NOTICE: f_leak => aba (1 row) EXPLAIN (COSTS OFF) SELECT * FROM rls_view; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------- Seq Scan on z1 - Filter: ((NOT (hashed SubPlan 1)) AND ((a % 2) = 1) AND f_leak(b)) + Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 1) AND f_leak(b)) SubPlan 1 -> Seq Scan on z1_blacklist (4 rows) diff --git a/src/test/regress/expected/rowtypes.out b/src/test/regress/expected/rowtypes.out index 8f3c153bac..b400b58f76 100644 --- a/src/test/regress/expected/rowtypes.out +++ b/src/test/regress/expected/rowtypes.out @@ -1249,8 +1249,8 @@ with cte(c) as materialized (select row(1, 2)), select * from cte2 as t where (select * from (select c as c1) s where (select (c1).f1 > 0)) is not null; - QUERY PLAN --------------------------------------------- + QUERY PLAN +---------------------------------------------- CTE Scan on cte Output: cte.c Filter: ((SubPlan 3) IS NOT NULL) @@ -1260,8 +1260,8 @@ where (select * from (select c as c1) s SubPlan 3 -> Result Output: cte.c - One-Time Filter: $2 - InitPlan 2 (returns $2) + One-Time Filter: (InitPlan 2).col1 + InitPlan 2 -> Result Output: ((cte.c).f1 > 0) (13 rows) diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out index 7a0d78dfe3..4ffc5b4c56 100644 --- a/src/test/regress/expected/select_parallel.out +++ b/src/test/regress/expected/select_parallel.out @@ -291,14 +291,14 @@ alter table tenk2 set (parallel_workers = 0); explain (costs off) select count(*) from tenk1 where (two, four) not in (select hundred, thousand from tenk2 where thousand > 100); - QUERY PLAN ------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------------------------------- Finalize Aggregate -> Gather Workers Planned: 4 -> Partial Aggregate -> Parallel Seq Scan on tenk1 - Filter: (NOT (hashed SubPlan 1)) + Filter: (NOT (ANY ((two = (hashed SubPlan 1).col1) AND (four = (hashed SubPlan 1).col2)))) SubPlan 1 -> Seq Scan on tenk2 Filter: (thousand > 100) @@ -315,10 +315,10 @@ select count(*) from tenk1 where (two, four) not in explain (costs off) select * from tenk1 where (unique1 + random())::integer not in (select ten from tenk2); - QUERY PLAN ------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------- Seq Scan on tenk1 - Filter: (NOT (hashed SubPlan 1)) + Filter: (NOT (ANY ((((unique1)::double precision + random()))::integer = (hashed SubPlan 1).col1))) SubPlan 1 -> Seq Scan on tenk2 (4 rows) @@ -335,7 +335,7 @@ explain (costs off) QUERY PLAN ------------------------------------------------------ Aggregate - InitPlan 1 (returns $2) + InitPlan 1 -> Finalize Aggregate -> Gather Workers Planned: 2 @@ -343,10 +343,9 @@ explain (costs off) -> Parallel Seq Scan on tenk2 -> Gather Workers Planned: 4 - Params Evaluated: $2 -> Parallel Seq Scan on tenk1 - Filter: (unique1 = $2) -(12 rows) + Filter: (unique1 = (InitPlan 1).col1) +(11 rows) select count(*) from tenk1 where tenk1.unique1 = (Select max(tenk2.unique1) from tenk2); @@ -1150,27 +1149,25 @@ ORDER BY 1; -> Append -> Gather Workers Planned: 4 - Params Evaluated: $1 - InitPlan 1 (returns $1) + InitPlan 1 -> Limit -> Gather Workers Planned: 4 -> Parallel Seq Scan on tenk1 tenk1_2 Filter: (fivethous = 1) -> Parallel Seq Scan on tenk1 - Filter: (fivethous = $1) + Filter: (fivethous = (InitPlan 1).col1) -> Gather Workers Planned: 4 - Params Evaluated: $3 - InitPlan 2 (returns $3) + InitPlan 2 -> Limit -> Gather Workers Planned: 4 -> Parallel Seq Scan on tenk1 tenk1_3 Filter: (fivethous = 1) -> Parallel Seq Scan on tenk1 tenk1_1 - Filter: (fivethous = $3) -(25 rows) + Filter: (fivethous = (InitPlan 2).col1) +(23 rows) -- test interaction with SRFs SELECT * FROM information_schema.foreign_data_wrapper_options @@ -1182,10 +1179,10 @@ ORDER BY 1, 2, 3; EXPLAIN (VERBOSE, COSTS OFF) SELECT generate_series(1, two), array(select generate_series(1, two)) FROM tenk1 ORDER BY tenthous; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------- ProjectSet - Output: generate_series(1, tenk1.two), (SubPlan 1), tenk1.tenthous + Output: generate_series(1, tenk1.two), ARRAY(SubPlan 1), tenk1.tenthous -> Gather Merge Output: tenk1.two, tenk1.tenthous Workers Planned: 4 diff --git a/src/test/regress/expected/sqljson.out b/src/test/regress/expected/sqljson.out index 5e7da96be5..cbf8542d8d 100644 --- a/src/test/regress/expected/sqljson.out +++ b/src/test/regress/expected/sqljson.out @@ -1064,8 +1064,8 @@ SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i) RETURNING QUERY PLAN --------------------------------------------------------------------- Result - Output: $0 - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1 + InitPlan 1 -> Aggregate Output: JSON_ARRAYAGG("*VALUES*".column1 RETURNING jsonb) -> Values Scan on "*VALUES*" diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out index e41b728df8..29b11f11aa 100644 --- a/src/test/regress/expected/subselect.out +++ b/src/test/regress/expected/subselect.out @@ -202,6 +202,57 @@ SELECT f1 AS "Correlated Field" 3 (5 rows) +-- Check ROWCOMPARE cases, both correlated and not +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + QUERY PLAN +----------------------------------------------------------------- + Seq Scan on public.subselect_tbl + Output: (((1 = (SubPlan 1).col1) AND (2 = (SubPlan 1).col2))) + SubPlan 1 + -> Result + Output: subselect_tbl.f1, subselect_tbl.f2 +(5 rows) + +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + eq +---- + t + f + f + f + f + f + f + f +(8 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + QUERY PLAN +----------------------------------------------------------------- + Seq Scan on public.subselect_tbl + Output: ((1 = (InitPlan 1).col1) AND (2 = (InitPlan 1).col2)) + InitPlan 1 + -> Result + Output: 3, 4 +(5 rows) + +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + eq +---- + f + f + f + f + f + f + f + f +(8 rows) + +SELECT ROW(1, 2) = (SELECT f1, f2 FROM SUBSELECT_TBL); -- error +ERROR: more than one row returned by a subquery used as an expression -- Subselects without aliases SELECT count FROM (SELECT COUNT(DISTINCT name) FROM road); count @@ -324,18 +375,18 @@ explain (verbose, costs off) select '42' union all select 43; -- check materialization of an initplan reference (bug #14524) explain (verbose, costs off) select 1 = all (select (select 1)); - QUERY PLAN ------------------------------------ + QUERY PLAN +------------------------------------------- Result - Output: (SubPlan 2) + Output: (ALL (1 = (SubPlan 2).col1)) SubPlan 2 -> Materialize - Output: ($0) - InitPlan 1 (returns $0) + Output: ((InitPlan 1).col1) + InitPlan 1 -> Result Output: 1 -> Result - Output: $0 + Output: (InitPlan 1).col1 (10 rows) select 1 = all (select (select 1)); @@ -377,7 +428,7 @@ select * from int4_tbl o where exists QUERY PLAN -------------------------------------- Seq Scan on int4_tbl o - Filter: (SubPlan 1) + Filter: EXISTS(SubPlan 1) SubPlan 1 -> Limit -> Seq Scan on int4_tbl i @@ -840,10 +891,10 @@ select * from outer_text where (f1, f2) not in (select * from inner_text); -- explain (verbose, costs off) select 'foo'::text in (select 'bar'::name union all select 'bar'::name); - QUERY PLAN -------------------------------------- + QUERY PLAN +--------------------------------------------------------- Result - Output: (hashed SubPlan 1) + Output: (ANY ('foo'::text = (hashed SubPlan 1).col1)) SubPlan 1 -> Append -> Result @@ -864,10 +915,10 @@ select 'foo'::text in (select 'bar'::name union all select 'bar'::name); -- explain (verbose, costs off) select row(row(row(1))) = any (select row(row(1))); - QUERY PLAN -------------------------------------------- + QUERY PLAN +-------------------------------------------------------- Result - Output: (SubPlan 1) + Output: (ANY ('("(1)")'::record = (SubPlan 1).col1)) SubPlan 1 -> Materialize Output: '("(1)")'::record @@ -907,10 +958,10 @@ language sql as 'select $1::text = $2'; create operator = (procedure=bogus_int8_text_eq, leftarg=int8, rightarg=text); explain (costs off) select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------- + QUERY PLAN +-------------------------------------------------------- Seq Scan on int8_tbl - Filter: (hashed SubPlan 1) + Filter: (ANY ((q1)::text = (hashed SubPlan 1).col1)) SubPlan 1 -> Seq Scan on inner_text (4 rows) @@ -928,10 +979,10 @@ create or replace function bogus_int8_text_eq(int8, text) returns boolean language sql as 'select $1::text = $2 and $1::text = $2'; explain (costs off) select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------- Seq Scan on int8_tbl - Filter: (hashed SubPlan 1) + Filter: (ANY (((q1)::text = (hashed SubPlan 1).col1) AND ((q1)::text = (hashed SubPlan 1).col1))) SubPlan 1 -> Seq Scan on inner_text (4 rows) @@ -949,10 +1000,10 @@ create or replace function bogus_int8_text_eq(int8, text) returns boolean language sql as 'select $2 = $1::text'; explain (costs off) select * from int8_tbl where q1 in (select c1 from inner_text); - QUERY PLAN --------------------------------------- + QUERY PLAN +------------------------------------------------- Seq Scan on int8_tbl - Filter: (SubPlan 1) + Filter: (ANY ((SubPlan 1).col1 = (q1)::text)) SubPlan 1 -> Materialize -> Seq Scan on inner_text @@ -972,11 +1023,11 @@ rollback; -- to get rid of the bogus operator explain (costs off) select count(*) from tenk1 t where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0); - QUERY PLAN --------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------------- Aggregate -> Seq Scan on tenk1 t - Filter: ((hashed SubPlan 2) OR (ten < 0)) + Filter: ((ANY (unique2 = (hashed SubPlan 2).col1)) OR (ten < 0)) SubPlan 2 -> Index Only Scan using tenk1_unique1 on tenk1 k (5 rows) @@ -997,7 +1048,7 @@ where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0) Aggregate -> Bitmap Heap Scan on tenk1 t Recheck Cond: (thousand = 1) - Filter: ((SubPlan 1) OR (ten < 0)) + Filter: (EXISTS(SubPlan 1) OR (ten < 0)) -> Bitmap Index Scan on tenk1_thous_tenthous Index Cond: (thousand = 1) SubPlan 1 @@ -1022,11 +1073,11 @@ analyze exists_tbl; explain (costs off) select * from exists_tbl t1 where (exists(select 1 from exists_tbl t2 where t1.c1 = t2.c2) or c3 < 0); - QUERY PLAN ------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------- Append -> Seq Scan on exists_tbl_null t1_1 - Filter: ((SubPlan 1) OR (c3 < 0)) + Filter: (EXISTS(SubPlan 1) OR (c3 < 0)) SubPlan 1 -> Append -> Seq Scan on exists_tbl_null t2_1 @@ -1034,7 +1085,7 @@ select * from exists_tbl t1 -> Seq Scan on exists_tbl_def t2_2 Filter: (t1_1.c1 = c2) -> Seq Scan on exists_tbl_def t1_2 - Filter: ((hashed SubPlan 2) OR (c3 < 0)) + Filter: ((ANY (c1 = (hashed SubPlan 2).col1)) OR (c3 < 0)) SubPlan 2 -> Append -> Seq Scan on exists_tbl_null t2_4 @@ -1071,14 +1122,14 @@ where a.thousand = b.thousand explain (verbose, costs off) select x, x from (select (select now()) as x from (values(1),(2)) v(y)) ss; - QUERY PLAN ---------------------------- + QUERY PLAN +------------------------------------------------ Values Scan on "*VALUES*" - Output: $0, $1 - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1, (InitPlan 2).col1 + InitPlan 1 -> Result Output: now() - InitPlan 2 (returns $1) + InitPlan 2 -> Result Output: now() (8 rows) @@ -1086,13 +1137,13 @@ explain (verbose, costs off) explain (verbose, costs off) select x, x from (select (select random()) as x from (values(1),(2)) v(y)) ss; - QUERY PLAN ----------------------------------- + QUERY PLAN +----------------------------------- Subquery Scan on ss Output: ss.x, ss.x -> Values Scan on "*VALUES*" - Output: $0 - InitPlan 1 (returns $0) + Output: (InitPlan 1).col1 + InitPlan 1 -> Result Output: random() (7 rows) @@ -1143,14 +1194,14 @@ where o.ten = 0; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Aggregate - Output: sum((((hashed SubPlan 1)))::integer) + Output: sum((((ANY (i.ten = (hashed SubPlan 1).col1))))::integer) -> Nested Loop - Output: ((hashed SubPlan 1)) + Output: ((ANY (i.ten = (hashed SubPlan 1).col1))) -> Seq Scan on public.onek o Output: o.unique1, o.unique2, o.two, o.four, o.ten, o.twenty, o.hundred, o.thousand, o.twothousand, o.fivethous,o.tenthous, o.odd, o.even, o.stringu1, o.stringu2, o.string4 Filter: (o.ten = 0) -> Index Scan using onek_unique1 on public.onek i - Output: (hashed SubPlan 1), random() + Output: (ANY (i.ten = (hashed SubPlan 1).col1)), random() Index Cond: (i.unique1 = o.unique1) SubPlan 1 -> Seq Scan on public.int4_tbl @@ -1346,7 +1397,7 @@ select * from int4_tbl where --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Nested Loop Semi Join Output: int4_tbl.f1 - Join Filter: (CASE WHEN (hashed SubPlan 1) THEN int4_tbl.f1 ELSE NULL::integer END = b.ten) + Join Filter: (CASE WHEN (ANY (int4_tbl.f1 = (hashed SubPlan 1).col1)) THEN int4_tbl.f1 ELSE NULL::integer END = b.ten) -> Seq Scan on public.int4_tbl Output: int4_tbl.f1 -> Seq Scan on public.tenk1 b @@ -1945,10 +1996,10 @@ select * from tenk1 A where exists (select 1 from tenk2 B where A.hundred in (select C.hundred FROM tenk2 C WHERE c.odd = b.odd)); - QUERY PLAN ---------------------------------- + QUERY PLAN +----------------------------------------------------- Nested Loop Semi Join - Join Filter: (SubPlan 1) + Join Filter: (ANY (a.hundred = (SubPlan 1).col1)) -> Seq Scan on tenk1 a -> Materialize -> Seq Scan on tenk2 b @@ -1962,10 +2013,10 @@ WHERE c.odd = b.odd)); explain (costs off) SELECT * FROM tenk1 A LEFT JOIN tenk2 B ON A.hundred in (SELECT c.hundred FROM tenk2 C WHERE c.odd = b.odd); - QUERY PLAN ---------------------------------- + QUERY PLAN +----------------------------------------------------- Nested Loop Left Join - Join Filter: (SubPlan 1) + Join Filter: (ANY (a.hundred = (SubPlan 1).col1)) -> Seq Scan on tenk1 a -> Materialize -> Seq Scan on tenk2 b @@ -1979,10 +2030,10 @@ ON A.hundred in (SELECT c.hundred FROM tenk2 C WHERE c.odd = b.odd); explain (costs off) SELECT * FROM tenk1 A LEFT JOIN tenk2 B ON B.hundred in (SELECT c.hundred FROM tenk2 C WHERE c.odd = a.odd); - QUERY PLAN ---------------------------------- + QUERY PLAN +----------------------------------------------------- Nested Loop Left Join - Join Filter: (SubPlan 1) + Join Filter: (ANY (b.hundred = (SubPlan 1).col1)) -> Seq Scan on tenk1 a -> Materialize -> Seq Scan on tenk2 b @@ -2045,7 +2096,7 @@ ON B.hundred in (SELECT min(c.hundred) FROM tenk2 C WHERE c.odd = b.odd); -> Subquery Scan on "ANY_subquery" Filter: (b.hundred = "ANY_subquery".min) -> Result - InitPlan 1 (returns $1) + InitPlan 1 -> Limit -> Index Scan using tenk2_hundred on tenk2 c Index Cond: (hundred IS NOT NULL) diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index 1062c341d8..713bf84c70 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -3038,18 +3038,18 @@ EXPLAIN (costs off) INSERT INTO rw_view1 VALUES (2, 'New row 2'); QUERY PLAN ----------------------------------------------------------- Insert on base_tbl - InitPlan 1 (returns $0) + InitPlan 1 -> Index Only Scan using base_tbl_pkey on base_tbl t Index Cond: (id = 2) -> Result - One-Time Filter: ($0 IS NOT TRUE) + One-Time Filter: ((InitPlan 1).col1 IS NOT TRUE) Update on base_tbl - InitPlan 1 (returns $0) + InitPlan 1 -> Index Only Scan using base_tbl_pkey on base_tbl t Index Cond: (id = 2) -> Result - One-Time Filter: $0 + One-Time Filter: (InitPlan 1).col1 -> Index Scan using base_tbl_pkey on base_tbl Index Cond: (id = 2) (15 rows) @@ -3108,8 +3108,8 @@ 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 t1_1 Update on public.t11 t1_2 @@ -3121,7 +3121,7 @@ UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; -> 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)) + Filter: ((t1_1.a <> 6) AND EXISTS(SubPlan 1) AND snoop(t1_1.a) AND leakproof(t1_1.a)) SubPlan 1 -> Append -> Seq Scan on public.t12 t12_1 @@ -3131,15 +3131,15 @@ UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; -> 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)) + Filter: ((t1_2.a <> 6) AND EXISTS(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)) + Filter: ((t1_3.a <> 6) AND EXISTS(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)) + Filter: ((t1_4.a <> 6) AND EXISTS(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; @@ -3155,8 +3155,8 @@ 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 t1_1 Update on public.t11 t1_2 @@ -3168,7 +3168,7 @@ UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; -> 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)) + Filter: (EXISTS(SubPlan 1) AND snoop(t1_1.a) AND leakproof(t1_1.a)) SubPlan 1 -> Append -> Seq Scan on public.t12 t12_1 @@ -3178,15 +3178,15 @@ UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; -> 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)) + Filter: (EXISTS(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)) + Filter: (EXISTS(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)) + Filter: (EXISTS(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; @@ -3370,10 +3370,10 @@ CREATE RULE v1_upd_rule AS ON UPDATE TO v1 DO INSTEAD CREATE VIEW v2 WITH (security_barrier = true) AS SELECT * FROM v1 WHERE EXISTS (SELECT 1); EXPLAIN (COSTS OFF) UPDATE v2 SET a = 1; - QUERY PLAN ---------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------- Update on t1 - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Merge Join Merge Cond: (t1.a = v1.a) @@ -3384,7 +3384,7 @@ EXPLAIN (COSTS OFF) UPDATE v2 SET a = 1; Sort Key: v1.a -> Subquery Scan on v1 -> Result - One-Time Filter: $0 + One-Time Filter: (InitPlan 1).col1 -> Seq Scan on t1 t1_1 (14 rows) diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out index cb0b033da6..1b27d132d7 100644 --- a/src/test/regress/expected/update.out +++ b/src/test/regress/expected/update.out @@ -178,15 +178,15 @@ 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, (SubPlan 1 (returns $1,$2)), t.ctid + Output: (SubPlan 1).col1, (SubPlan 1).col2, (rescan SubPlan 1), t.ctid One-Time Filter: (CURRENT_USER = SESSION_USER) -> Seq Scan on public.update_test t Output: t.a, t.ctid - SubPlan 1 (returns $1,$2) + SubPlan 1 -> Seq Scan on public.update_test s Output: s.b, s.a Filter: (s.a = t.a) diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index 60de2cbf96..e46710cf31 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -4279,7 +4279,7 @@ WHERE c = 1; Subquery Scan on emp Filter: (emp.c = 1) -> WindowAgg - InitPlan 1 (returns $0) + InitPlan 1 -> Result -> Sort Sort Key: empsalary.empno DESC diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 6de347b528..7bef181d78 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -3100,7 +3100,7 @@ WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v); CTE cte_init -> Result Output: 1, 'cte_init val'::text - InitPlan 2 (returns $1) + InitPlan 2 -> Limit Output: ((cte_init.b || ' merge update'::text)) -> CTE Scan on cte_init @@ -3143,11 +3143,11 @@ WHEN NOT MATCHED THEN INSERT VALUES(o.a, o.b || (SELECT merge_source_cte.*::text CTE merge_source_cte -> Result Output: 15, 'merge_source_cte val'::text - InitPlan 2 (returns $1) + InitPlan 2 -> CTE Scan on merge_source_cte merge_source_cte_1 Output: ((merge_source_cte_1.b || (merge_source_cte_1.*)::text) || ' merge update'::text) Filter: (merge_source_cte_1.a = 15) - InitPlan 3 (returns $2) + InitPlan 3 -> CTE Scan on merge_source_cte merge_source_cte_2 Output: ((merge_source_cte_2.*)::text || ' merge insert'::text) -> Hash Right Join diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql index 2f3601a058..7c42ebc36f 100644 --- a/src/test/regress/sql/subselect.sql +++ b/src/test/regress/sql/subselect.sql @@ -82,6 +82,20 @@ SELECT f1 AS "Correlated Field" WHERE (f1, f2) IN (SELECT f2, CAST(f3 AS int4) FROM SUBSELECT_TBL WHERE f3 IS NOT NULL); +-- Check ROWCOMPARE cases, both correlated and not + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + +SELECT ROW(1, 2) = (SELECT f1, f2) AS eq FROM SUBSELECT_TBL; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + +SELECT ROW(1, 2) = (SELECT 3, 4) AS eq FROM SUBSELECT_TBL; + +SELECT ROW(1, 2) = (SELECT f1, f2 FROM SUBSELECT_TBL); -- error + -- Subselects without aliases SELECT count FROM (SELECT COUNT(DISTINCT name) FROM road);
On Tue, 19 Mar 2024 at 16:42, Tom Lane <tgl@sss.pgh.pa.us> wrote: > > Here's a hopefully-final version that makes that adjustment and > tweaks a couple of comments. > This looks very good to me. One final case that could possibly be improved is this one from aggregates.out: explain (verbose, costs off) select array(select sum(x+y) s from generate_series(1,3) y group by y order by s) from generate_series(1,3) x; QUERY PLAN ------------------------------------------------------------------- Function Scan on pg_catalog.generate_series x Output: ARRAY(SubPlan 1) Function Call: generate_series(1, 3) SubPlan 1 -> Sort Output: (sum((x.x + y.y))), y.y Sort Key: (sum((x.x + y.y))) -> HashAggregate Output: sum((x.x + y.y)), y.y Group Key: y.y -> Function Scan on pg_catalog.generate_series y Output: y.y Function Call: generate_series(1, 3) ARRAY operates on a SELECT with a single targetlist item, but in this case it looks like the subplan output has 2 columns, which might confuse people. I wonder if we should output "ARRAY((SubPlan 1).col1)" to make it clearer. Since ARRAY_SUBLINK is a special case, which always collects the first column's values, we could just always output "col1" for ARRAY. Regards, Dean
Dean Rasheed <dean.a.rasheed@gmail.com> writes: > One final case that could possibly be improved is this one from aggregates.out: > explain (verbose, costs off) > select array(select sum(x+y) s > from generate_series(1,3) y group by y order by s) > from generate_series(1,3) x; > QUERY PLAN > ------------------------------------------------------------------- > Function Scan on pg_catalog.generate_series x > Output: ARRAY(SubPlan 1) > Function Call: generate_series(1, 3) > SubPlan 1 > -> Sort > Output: (sum((x.x + y.y))), y.y > Sort Key: (sum((x.x + y.y))) > -> HashAggregate > Output: sum((x.x + y.y)), y.y > Group Key: y.y > -> Function Scan on pg_catalog.generate_series y > Output: y.y > Function Call: generate_series(1, 3) > ARRAY operates on a SELECT with a single targetlist item, but in this > case it looks like the subplan output has 2 columns, which might > confuse people. I'm inclined to leave that alone. The actual source sub-SELECT could only have had one column, so no real confusion is possible. Yeah, there's a resjunk grouping column visible in the plan as well, but those exist in many other queries, and we've not gotten questions about them. (Perhaps some documentation about junk columns needs to be added? I'm not eager to write it though.) I had actually had a similar thought about sticking ".col1" onto EXPR_SUBLINK cases, but I concluded it was mostly pedantry. Nobody's likely to get confused. regards, tom lane
On Tue, 19 Mar 2024 at 21:40, Tom Lane <tgl@sss.pgh.pa.us> wrote: > > I'm inclined to leave that alone. The actual source sub-SELECT > could only have had one column, so no real confusion is possible. > Yeah, there's a resjunk grouping column visible in the plan as well, > but those exist in many other queries, and we've not gotten questions > about them. > Fair enough. I have no further comments. Regards, Dean
Dean Rasheed <dean.a.rasheed@gmail.com> writes: > Fair enough. I have no further comments. Pushed then. Thanks for reviewing! I gave you credit as co-author. regards, tom lane