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)
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
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"
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"))
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
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)
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)
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)
which shows that the planner thinks that sorting onek 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 EXPLAIN ANALYZE , as discussed
- below.
+ We can investigate that using EXPLAIN ANALYZE , as
+ discussed below.
+
+
+
+
+ Some query plans involve subplans , which arise
+ from sub-SELECT s in the original query. Such
+ queries can sometimes be transformed into ordinary join plans, but
+ when they cannot be, we get plans like:
+
+
+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)
+
+
+ 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, t.four is passed down) and the
+ results of the sub-select are available to the outer plan. Those
+ result values are shown by EXPLAIN with notations
+ like
+ (subplan_name ).colN ,
+ which refers to the N 'th output column of
+ the sub-SELECT .
+
+
+
+ hashed
+
+ In the example above, the ALL operator runs the
+ subplan again for each row of the outer query (which accounts for the
+ high estimated cost). Some queries can use a hashed
+ subplan to avoid that:
+
+
+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)
+
+
+ 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 ANY operator. This requires that the
+ sub-SELECT not reference any variables of the outer
+ query, and that the ANY 's comparison operator be
+ amenable to hashing.
+
+
+
+
+ If, in addition to not referencing any variables of the outer query,
+ the sub-SELECT cannot return more than one row,
+ it may instead be implemented as an initplan :
+
+
+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
+
+
+ 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 random() is evaluated only once and
+ all the values of t1.ten are compared to the same
+ randomly-chosen integer. That's quite different from what would
+ happen without the sub-SELECT construct.
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);
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;
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;
}
}
-/*
- * 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
*
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);
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;
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);
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.
*/
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)
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.
/*
* 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);
}
* 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;
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
QUERY PLAN
------------------------------------------------------------
Result
- InitPlan 1 (returns $0)
+ InitPlan 1
-> Limit
-> Index Only Scan using tenk1_unique1 on tenk1
Index Cond: (unique1 IS NOT NULL)
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)
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))
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))
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))
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))
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))
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))
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)
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)
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)
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)
---------------------------------------------------------------------
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)
QUERY PLAN
----------------------------------------------------
Result
- InitPlan 1 (returns $0)
+ InitPlan 1
-> Limit
-> Result
One-Time Filter: (100 IS NOT NULL)
QUERY PLAN
---------------------------------------------------------------------------------------------
Result
- InitPlan 1 (returns $0)
+ InitPlan 1
-> Limit
-> Merge Append
Sort Key: minmaxtest.f1
-> 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
QUERY PLAN
---------------------------------------------------------------------------------------------
Unique
- InitPlan 1 (returns $0)
+ InitPlan 1
-> Limit
-> Merge Append
Sort Key: minmaxtest.f1
-> 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
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).col 1)
-> Result
(26 rows)
QUERY PLAN
------------------------------------------------------------
Result
- InitPlan 1 (returns $0)
+ InitPlan 1
-> Limit
-> Index Only Scan using tenk1_unique1 on tenk1
Index Cond: (unique1 IS NOT NULL)
-- 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
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
QUERY PLAN
---------------------------------------------------------------------------------
Result
- Output: $0
- InitPlan 1 (returns $0)
+ Output: (InitPlan 1).col1
+ InitPlan 1
-> Limit
Output: ((1 - matest0.id))
-> Result
QUERY PLAN
--------------------------------------------------------------------
Result
- InitPlan 1 (returns $0)
+ InitPlan 1
-> Limit
-> Merge Append
Sort Key: a.unique1
QUERY PLAN
--------------------------------------------------------------------
Result
- InitPlan 1 (returns $0)
+ InitPlan 1
-> Limit
-> Merge Append
Sort Key: a.unique1
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
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);
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))
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
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)
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)
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
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),
----------------------------------------------------------------
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
-> 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
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)
--
-- 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).col 1)))
-> 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).col 1)))
-> 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).col 1)))
+(15 rows)
-- Test pruning during parallel nested loop query
create table lprt_a (a int not null);
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).col 1)
-> 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).col 1)
-> 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).col 1)
-> 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).col 1)
-> 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).col 1)
-> 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).col 1)
-> 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).col 1)
-> 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).col 1)
-> 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).col 1)
-> 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
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.
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.
) 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.
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)
-> 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;
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;
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;
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;
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)
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;
-----------------------------------------------------------------------------------------------
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).col 1)
-> 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).col 1)
-> 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).col 1)
(14 rows)
reset enable_seqscan;
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 *
) 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);
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)
----------------------------------------------------------------------
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
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).col 1)
-> Parallel Seq Scan on listp_12_2 listp_2 (actual rows=N loops=N)
- Filter: (a = $ 1)
+ Filter: (a = (InitPlan 2).col 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 )
+ 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;
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).col 1]))
-> 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).col 1]))
-> 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).col 1]))
-> 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).col 1]))
(15 rows)
reset enable_sort;
-- 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)
(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
(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
(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
(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
(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)
(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)
(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
(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))
(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))
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))
(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)
(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)
(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)
(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)
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)
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)
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)
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)
QUERY PLAN
------------------------------------------------------
Aggregate
- InitPlan 1 (returns $2)
+ InitPlan 1
-> Finalize Aggregate
-> Gather
Workers Planned: 2
-> 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);
-> 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).col 1)
-> 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
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
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*"
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
-- 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));
QUERY PLAN
--------------------------------------
Seq Scan on int4_tbl o
- Filter: (SubPlan 1)
+ Filter: EXISTS (SubPlan 1)
SubPlan 1
-> Limit
-> Seq Scan on int4_tbl i
--
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
--
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
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)
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)
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
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)
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
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
-> 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
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).col 1
+ InitPlan 1
-> Result
Output: now()
- InitPlan 2 (returns $1)
+ InitPlan 2
-> Result
Output: now()
(8 rows)
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)
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
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
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
(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
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
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
-> 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)
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)
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
-> 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
-> 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;
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
-> 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
-> 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;
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)
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)
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)
Subquery Scan on emp
Filter: (emp.c = 1)
-> WindowAgg
- InitPlan 1 (returns $0)
+ InitPlan 1
-> Result
-> Sort
Sort Key: empsalary.empno DESC
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
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
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);