mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
Fix stale copies of PHVs in subqueries
When a subquery references an output of another subquery that gets pulled up, and that output must be wrapped in a PlaceHolderVar because of an intermediate outer join, the PHV expression is pushed down into the subquery. That copy is not preprocessed along with the outer query's expressions, so the two copies can diverge. This used to be harmless, but since commit2ebf25e7djoin removal edits the whole query tree, walking into subqueries, and can trip an assert in ChangeVarNodes if it removes a rel whose Var survives only in such a copy. To fix, preprocess these copies at their owning query level, early in subquery_planner, before anything can consume them (in particular before SubLinks are turned into SubPlans). This covers copies pushed into both LATERAL subquery RTEs and SubLink subselects, and handles nested copies innermost-first. extract_lateral_references no longer preprocesses the copies it pulls out. Correspondingly, the subquery's own processing must leave the contents of an upper-level PHV alone, since the owning level has already preprocessed them: eval_const_expressions returns such a PHV unchanged, and flatten_join_alias_vars no longer recurses into it. Back-patch to v16, as with commit2ebf25e7d. Reported-by: Tender Wang <tndrwang@gmail.com> Author: Richard Guo <guofenglinux@gmail.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://postgr.es/m/CAHewXN=kWGAXV537mKtSyBYobGdHhYJVDJJMXXZEmmPWE_zaPw@mail.gmail.com Backpatch-through: 16
This commit is contained in:
@@ -29,7 +29,6 @@
|
|||||||
#include "optimizer/paths.h"
|
#include "optimizer/paths.h"
|
||||||
#include "optimizer/placeholder.h"
|
#include "optimizer/placeholder.h"
|
||||||
#include "optimizer/planmain.h"
|
#include "optimizer/planmain.h"
|
||||||
#include "optimizer/planner.h"
|
|
||||||
#include "optimizer/restrictinfo.h"
|
#include "optimizer/restrictinfo.h"
|
||||||
#include "parser/analyze.h"
|
#include "parser/analyze.h"
|
||||||
#include "rewrite/rewriteManip.h"
|
#include "rewrite/rewriteManip.h"
|
||||||
@@ -1132,17 +1131,13 @@ extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex)
|
|||||||
PlaceHolderVar *phv = (PlaceHolderVar *) node;
|
PlaceHolderVar *phv = (PlaceHolderVar *) node;
|
||||||
int levelsup = phv->phlevelsup;
|
int levelsup = phv->phlevelsup;
|
||||||
|
|
||||||
/* Have to work harder to adjust the contained expression too */
|
/*
|
||||||
|
* Have to work harder to adjust the contained expression too.
|
||||||
|
* (Its expression has already been preprocessed by
|
||||||
|
* subquery_planner(), so we must not do that again here.)
|
||||||
|
*/
|
||||||
if (levelsup != 0)
|
if (levelsup != 0)
|
||||||
IncrementVarSublevelsUp(node, -levelsup, 0);
|
IncrementVarSublevelsUp(node, -levelsup, 0);
|
||||||
|
|
||||||
/*
|
|
||||||
* If we pulled the PHV out of a subquery RTE, its expression
|
|
||||||
* needs to be preprocessed. subquery_planner() already did this
|
|
||||||
* for level-zero PHVs in function and values RTEs, though.
|
|
||||||
*/
|
|
||||||
if (levelsup > 0)
|
|
||||||
phv->phexpr = preprocess_phv_expression(root, phv->phexpr);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
Assert(false);
|
Assert(false);
|
||||||
|
|||||||
@@ -144,8 +144,18 @@ typedef struct
|
|||||||
Index group_rtindex;
|
Index group_rtindex;
|
||||||
} having_grouping_ctx;
|
} having_grouping_ctx;
|
||||||
|
|
||||||
|
/* Context for preprocess_subquery_phvs_walker */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
PlannerInfo *root;
|
||||||
|
int sublevels_up;
|
||||||
|
} preprocess_subquery_phvs_context;
|
||||||
|
|
||||||
/* Local functions */
|
/* Local functions */
|
||||||
static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
|
static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
|
||||||
|
static void preprocess_subquery_phvs(PlannerInfo *root);
|
||||||
|
static bool preprocess_subquery_phvs_walker(Node *node,
|
||||||
|
preprocess_subquery_phvs_context *context);
|
||||||
static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);
|
static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);
|
||||||
static Bitmapset *find_having_conflicts(Query *parse, Index group_rtindex);
|
static Bitmapset *find_having_conflicts(Query *parse, Index group_rtindex);
|
||||||
static Oid having_var_grouping_eqop(Var *var, void *context);
|
static Oid having_var_grouping_eqop(Var *var, void *context);
|
||||||
@@ -1022,6 +1032,14 @@ subquery_planner(PlannerGlobal *glob, Query *parse, char *plan_name,
|
|||||||
*/
|
*/
|
||||||
root->hasHavingQual = (parse->havingQual != NULL);
|
root->hasHavingQual = (parse->havingQual != NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Preprocess any PlaceHolderVars of our level that were pushed down into
|
||||||
|
* subqueries. This must happen before anything can consume those copies,
|
||||||
|
* in particular before SubLinks below are turned into SubPlans.
|
||||||
|
*/
|
||||||
|
if (root->glob->lastPHId != 0)
|
||||||
|
preprocess_subquery_phvs(root);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do expression preprocessing on targetlist and quals, as well as other
|
* Do expression preprocessing on targetlist and quals, as well as other
|
||||||
* random expressions in the querytree. Note that we do not need to
|
* random expressions in the querytree. Note that we do not need to
|
||||||
@@ -1645,20 +1663,74 @@ group_var_eqop(Query *parse, Var *var)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* preprocess_phv_expression
|
* preprocess_subquery_phvs
|
||||||
* Do preprocessing on a PlaceHolderVar expression that's been pulled up.
|
* Preprocess copies of this level's PlaceHolderVars that were pushed
|
||||||
|
* down into subqueries.
|
||||||
*
|
*
|
||||||
* If a LATERAL subquery references an output of another subquery, and that
|
* When a subquery (a LATERAL RTE or a SubLink's subselect) references a
|
||||||
* output must be wrapped in a PlaceHolderVar because of an intermediate outer
|
* pulled-up output that must be wrapped in a PlaceHolderVar, the PHV
|
||||||
* join, then we'll push the PlaceHolderVar expression down into the subquery
|
* expression is pushed down into the subquery. The subquery's own planning
|
||||||
* and later pull it back up during find_lateral_references, which runs after
|
* leaves that copy alone, since it belongs to our level, so we need to
|
||||||
* subquery_planner has preprocessed all the expressions that were in the
|
* preprocess it. We modify the PHVs in place, temporarily adjusting each to
|
||||||
* current query level to start with. So we need to preprocess it then.
|
* our level, and handle nested copies innermost-first.
|
||||||
*/
|
*/
|
||||||
Expr *
|
static void
|
||||||
preprocess_phv_expression(PlannerInfo *root, Expr *expr)
|
preprocess_subquery_phvs(PlannerInfo *root)
|
||||||
{
|
{
|
||||||
return (Expr *) preprocess_expression(root, (Node *) expr, EXPRKIND_PHV);
|
preprocess_subquery_phvs_context context;
|
||||||
|
|
||||||
|
context.root = root;
|
||||||
|
context.sublevels_up = 0;
|
||||||
|
(void) query_tree_walker(root->parse, preprocess_subquery_phvs_walker,
|
||||||
|
&context, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
preprocess_subquery_phvs_walker(Node *node,
|
||||||
|
preprocess_subquery_phvs_context *context)
|
||||||
|
{
|
||||||
|
if (node == NULL)
|
||||||
|
return false;
|
||||||
|
if (IsA(node, Query))
|
||||||
|
{
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
context->sublevels_up++;
|
||||||
|
result = query_tree_walker((Query *) node,
|
||||||
|
preprocess_subquery_phvs_walker,
|
||||||
|
context, 0);
|
||||||
|
context->sublevels_up--;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (IsA(node, PlaceHolderVar))
|
||||||
|
{
|
||||||
|
PlaceHolderVar *phv = (PlaceHolderVar *) node;
|
||||||
|
|
||||||
|
/* Handle any nested copies within the expression first */
|
||||||
|
(void) expression_tree_walker(node, preprocess_subquery_phvs_walker,
|
||||||
|
context);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Is this a copy of one of our PHVs that is pushed down into a
|
||||||
|
* subquery?
|
||||||
|
*/
|
||||||
|
if (context->sublevels_up > 0 &&
|
||||||
|
phv->phlevelsup == context->sublevels_up)
|
||||||
|
{
|
||||||
|
int levelsup = phv->phlevelsup;
|
||||||
|
Node *expr;
|
||||||
|
|
||||||
|
/* Adjust the expression to our level, preprocess, adjust back */
|
||||||
|
expr = copyObject((Node *) phv->phexpr);
|
||||||
|
IncrementVarSublevelsUp(expr, -levelsup, 0);
|
||||||
|
expr = preprocess_expression(context->root, expr, EXPRKIND_PHV);
|
||||||
|
IncrementVarSublevelsUp(expr, levelsup, 0);
|
||||||
|
phv->phexpr = (Expr *) expr;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return expression_tree_walker(node, preprocess_subquery_phvs_walker,
|
||||||
|
context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*--------------------
|
/*--------------------
|
||||||
|
|||||||
@@ -371,13 +371,14 @@ build_subplan(PlannerInfo *root, Plan *plan, Path *path,
|
|||||||
* already been adjusted to have the correct varlevelsup, phlevelsup,
|
* already been adjusted to have the correct varlevelsup, phlevelsup,
|
||||||
* agglevelsup, or retlevelsup.
|
* agglevelsup, or retlevelsup.
|
||||||
*
|
*
|
||||||
* If it's a PlaceHolderVar, Aggref, GroupingFunc, or ReturningExpr,
|
* If it's an Aggref, GroupingFunc, or ReturningExpr, its arguments
|
||||||
* its arguments might contain SubLinks, which have not yet been
|
* might contain SubLinks, which have not yet been processed (see the
|
||||||
* processed (see the comments for SS_replace_correlation_vars). Do
|
* comments for SS_replace_correlation_vars). Do that now. A
|
||||||
* that now.
|
* PlaceHolderVar needs no such treatment: subquery_planner already
|
||||||
|
* preprocessed the PHVs of its owning level, so its expression is
|
||||||
|
* fully processed and may already contain SubPlans.
|
||||||
*/
|
*/
|
||||||
if (IsA(arg, PlaceHolderVar) ||
|
if (IsA(arg, Aggref) ||
|
||||||
IsA(arg, Aggref) ||
|
|
||||||
IsA(arg, GroupingFunc) ||
|
IsA(arg, GroupingFunc) ||
|
||||||
IsA(arg, ReturningExpr))
|
IsA(arg, ReturningExpr))
|
||||||
arg = SS_process_sublinks(root, arg, false);
|
arg = SS_process_sublinks(root, arg, false);
|
||||||
|
|||||||
@@ -3407,9 +3407,8 @@ eval_const_expressions_mutator(Node *node,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Return a SubPlan unchanged --- too late to do anything with it.
|
* Return a SubPlan unchanged --- too late to do anything with it.
|
||||||
*
|
* This can happen in estimation mode, which runs after SubPlans
|
||||||
* XXX should we ereport() here instead? Probably this routine
|
* have been created.
|
||||||
* should never be invoked after SubPlan creation.
|
|
||||||
*/
|
*/
|
||||||
return node;
|
return node;
|
||||||
case T_RelabelType:
|
case T_RelabelType:
|
||||||
@@ -4215,20 +4214,28 @@ eval_const_expressions_mutator(Node *node,
|
|||||||
return (Node *) newcdomain;
|
return (Node *) newcdomain;
|
||||||
}
|
}
|
||||||
case T_PlaceHolderVar:
|
case T_PlaceHolderVar:
|
||||||
|
|
||||||
/*
|
|
||||||
* In estimation mode, just strip the PlaceHolderVar node
|
|
||||||
* altogether; this amounts to estimating that the contained value
|
|
||||||
* won't be forced to null by an outer join. In regular mode we
|
|
||||||
* just use the default behavior (ie, simplify the expression but
|
|
||||||
* leave the PlaceHolderVar node intact).
|
|
||||||
*/
|
|
||||||
if (context->estimate)
|
|
||||||
{
|
{
|
||||||
PlaceHolderVar *phv = (PlaceHolderVar *) node;
|
PlaceHolderVar *phv = (PlaceHolderVar *) node;
|
||||||
|
|
||||||
return eval_const_expressions_mutator((Node *) phv->phexpr,
|
/*
|
||||||
context);
|
* Leave a PHV of an upper query level alone: its expression
|
||||||
|
* belongs to that level, which has already preprocessed it.
|
||||||
|
* But we do copy the subtree, just to conform to this
|
||||||
|
* function's API spec.
|
||||||
|
*/
|
||||||
|
if (phv->phlevelsup > 0)
|
||||||
|
return copyObject(node);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In estimation mode, just strip the PlaceHolderVar node
|
||||||
|
* altogether; this amounts to estimating that the contained
|
||||||
|
* value won't be forced to null by an outer join. In regular
|
||||||
|
* mode we just use the default behavior (ie, simplify the
|
||||||
|
* expression but leave the PlaceHolderVar node intact).
|
||||||
|
*/
|
||||||
|
if (context->estimate)
|
||||||
|
return eval_const_expressions_mutator((Node *) phv->phexpr,
|
||||||
|
context);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case T_ConvertRowtypeExpr:
|
case T_ConvertRowtypeExpr:
|
||||||
|
|||||||
@@ -677,19 +677,18 @@ identify_current_nestloop_params(PlannerInfo *root,
|
|||||||
/*
|
/*
|
||||||
* Deal with an edge case: if the PHV was pulled up out of a
|
* Deal with an edge case: if the PHV was pulled up out of a
|
||||||
* subquery and it contains a subquery that was originally
|
* subquery and it contains a subquery that was originally
|
||||||
* pushed down from this query level, then that will still be
|
* pushed down from this query level, then the copy of the PHV
|
||||||
* represented as a SubLink, because SS_process_sublinks won't
|
* we got from the subquery has its own SubPlan for that,
|
||||||
* recurse into outer PHVs, so it didn't get transformed
|
* distinct from the one in the current query level's
|
||||||
* during expression preprocessing in the subquery. We need a
|
* placeholder_list. We'd rather use the placeholder_list's
|
||||||
* version of the PHV that has a SubPlan, which we can get
|
* version of the PHV, so that we don't end up evaluating
|
||||||
* from the current query level's placeholder_list. This is
|
* duplicate SubPlans.
|
||||||
* quite grotty of course, but dealing with it earlier in the
|
*
|
||||||
* handling of subplan params would be just as grotty, and it
|
* Note that the subquery's copy cannot contain any SubLinks:
|
||||||
* might end up being a waste of cycles if we don't decide to
|
* subquery_planner preprocessed the PHVs of this query level
|
||||||
* treat the PHV as a NestLoopParam. (Perhaps that whole
|
* within the subquery before the subquery was planned.
|
||||||
* mechanism should be redesigned someday, but today is not
|
|
||||||
* that day.)
|
|
||||||
*/
|
*/
|
||||||
|
Assert(!checkExprHasSubLink((Node *) phv));
|
||||||
if (root->parse->hasSubLinks)
|
if (root->parse->hasSubLinks)
|
||||||
{
|
{
|
||||||
phv = copyObject(phinfo->ph_var);
|
phv = copyObject(phinfo->ph_var);
|
||||||
|
|||||||
@@ -932,12 +932,35 @@ flatten_join_alias_vars_mutator(Node *node,
|
|||||||
}
|
}
|
||||||
if (IsA(node, PlaceHolderVar))
|
if (IsA(node, PlaceHolderVar))
|
||||||
{
|
{
|
||||||
/* Copy the PlaceHolderVar node with correct mutation of subnodes */
|
PlaceHolderVar *phv = (PlaceHolderVar *) node;
|
||||||
PlaceHolderVar *phv;
|
|
||||||
|
|
||||||
phv = (PlaceHolderVar *) expression_tree_mutator(node,
|
/*
|
||||||
flatten_join_alias_vars_mutator,
|
* A PHV above the target level can't contain join aliases of the
|
||||||
context);
|
* target level and has no relid sets of ours to fix, so return it
|
||||||
|
* unchanged.
|
||||||
|
*/
|
||||||
|
if (phv->phlevelsup > context->sublevels_up)
|
||||||
|
return node; /* no need to copy, really */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A pushed-down copy of a target-level PHV was already preprocessed
|
||||||
|
* by preprocess_subquery_phvs, so it may contain SubPlans and holds
|
||||||
|
* no unflattened target-level aliases; shallow-copy it instead of
|
||||||
|
* recursing. Any other PHV is recursed into, as it may still hold
|
||||||
|
* target-level aliases.
|
||||||
|
*/
|
||||||
|
if (phv->phlevelsup == context->sublevels_up &&
|
||||||
|
context->sublevels_up > 0)
|
||||||
|
{
|
||||||
|
PlaceHolderVar *newphv = makeNode(PlaceHolderVar);
|
||||||
|
|
||||||
|
memcpy(newphv, phv, sizeof(PlaceHolderVar));
|
||||||
|
phv = newphv;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
phv = (PlaceHolderVar *) expression_tree_mutator(node,
|
||||||
|
flatten_join_alias_vars_mutator,
|
||||||
|
context);
|
||||||
/* now fix PlaceHolderVar's relid sets */
|
/* now fix PlaceHolderVar's relid sets */
|
||||||
if (phv->phlevelsup == context->sublevels_up)
|
if (phv->phlevelsup == context->sublevels_up)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ extern void mark_partial_aggref(Aggref *agg, AggSplit aggsplit);
|
|||||||
extern Path *get_cheapest_fractional_path(RelOptInfo *rel,
|
extern Path *get_cheapest_fractional_path(RelOptInfo *rel,
|
||||||
double tuple_fraction);
|
double tuple_fraction);
|
||||||
|
|
||||||
extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr);
|
|
||||||
|
|
||||||
extern RelOptInfo *create_unique_paths(PlannerInfo *root, RelOptInfo *rel,
|
extern RelOptInfo *create_unique_paths(PlannerInfo *root, RelOptInfo *rel,
|
||||||
SpecialJoinInfo *sjinfo);
|
SpecialJoinInfo *sjinfo);
|
||||||
|
|||||||
@@ -2387,12 +2387,12 @@ select (select grouping(v1)) from (values ((select 1))) v(v1) group by cube(v1);
|
|||||||
MixedAggregate
|
MixedAggregate
|
||||||
Hash Key: (InitPlan expr_3).col1
|
Hash Key: (InitPlan expr_3).col1
|
||||||
Group Key: ()
|
Group Key: ()
|
||||||
InitPlan expr_2
|
InitPlan expr_1
|
||||||
-> Result
|
-> Result
|
||||||
InitPlan expr_3
|
InitPlan expr_3
|
||||||
-> Result
|
-> Result
|
||||||
-> Result
|
-> Result
|
||||||
SubPlan expr_1
|
SubPlan expr_2
|
||||||
-> Result
|
-> Result
|
||||||
(10 rows)
|
(10 rows)
|
||||||
|
|
||||||
|
|||||||
@@ -5182,7 +5182,7 @@ where i8.q2 = 123;
|
|||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
Nested Loop
|
Nested Loop
|
||||||
Output: i8.q1, i8.q2, (InitPlan expr_1).col1, false, (i8.q2)
|
Output: i8.q1, i8.q2, (InitPlan expr_2).col1, false, (i8.q2)
|
||||||
InitPlan expr_1
|
InitPlan expr_1
|
||||||
-> Result
|
-> Result
|
||||||
Output: true
|
Output: true
|
||||||
@@ -5195,7 +5195,7 @@ where i8.q2 = 123;
|
|||||||
-> Nested Loop
|
-> Nested Loop
|
||||||
Output: i8.q1, i8.q2, (i8.q2)
|
Output: i8.q1, i8.q2, (i8.q2)
|
||||||
-> Subquery Scan on ss1
|
-> Subquery Scan on ss1
|
||||||
Output: ss1.y, (InitPlan expr_1).col1
|
Output: ss1.y, (InitPlan expr_2).col1
|
||||||
-> Limit
|
-> Limit
|
||||||
Output: NULL::integer
|
Output: NULL::integer
|
||||||
-> Result
|
-> Result
|
||||||
@@ -5209,7 +5209,7 @@ where i8.q2 = 123;
|
|||||||
Output: (i8.q2)
|
Output: (i8.q2)
|
||||||
-> Result
|
-> Result
|
||||||
Output: i8.q2
|
Output: i8.q2
|
||||||
One-Time Filter: ((InitPlan expr_1).col1)
|
One-Time Filter: ((InitPlan expr_2).col1)
|
||||||
(29 rows)
|
(29 rows)
|
||||||
|
|
||||||
explain (verbose, costs off)
|
explain (verbose, costs off)
|
||||||
@@ -5225,7 +5225,7 @@ where i8.q2 = 123;
|
|||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
Nested Loop
|
Nested Loop
|
||||||
Output: i8.q1, i8.q2, (InitPlan expr_1).col1, false, (i8.q2)
|
Output: i8.q1, i8.q2, (InitPlan expr_2).col1, false, (i8.q2)
|
||||||
InitPlan expr_1
|
InitPlan expr_1
|
||||||
-> Result
|
-> Result
|
||||||
Output: true
|
Output: true
|
||||||
@@ -5239,7 +5239,7 @@ where i8.q2 = 123;
|
|||||||
-> Nested Loop
|
-> Nested Loop
|
||||||
Output: i8.q1, i8.q2, (i8.q2)
|
Output: i8.q1, i8.q2, (i8.q2)
|
||||||
-> Seq Scan on public.int4_tbl i4
|
-> Seq Scan on public.int4_tbl i4
|
||||||
Output: i4.f1, (InitPlan expr_1).col1
|
Output: i4.f1, (InitPlan expr_2).col1
|
||||||
Filter: (i4.f1 = 0)
|
Filter: (i4.f1 = 0)
|
||||||
-> Nested Loop
|
-> Nested Loop
|
||||||
Output: i8.q1, i8.q2, (i8.q2)
|
Output: i8.q1, i8.q2, (i8.q2)
|
||||||
@@ -5250,7 +5250,7 @@ where i8.q2 = 123;
|
|||||||
Output: (i8.q2)
|
Output: (i8.q2)
|
||||||
-> Result
|
-> Result
|
||||||
Output: i8.q2
|
Output: i8.q2
|
||||||
One-Time Filter: ((InitPlan expr_1).col1)
|
One-Time Filter: ((InitPlan expr_2).col1)
|
||||||
(27 rows)
|
(27 rows)
|
||||||
|
|
||||||
-- Test proper handling of appendrel PHVs during useless-RTE removal
|
-- Test proper handling of appendrel PHVs during useless-RTE removal
|
||||||
@@ -7282,6 +7282,154 @@ on lhs.id = rhs.id;
|
|||||||
-> Result
|
-> Result
|
||||||
(5 rows)
|
(5 rows)
|
||||||
|
|
||||||
|
-- check handling of a removed Var that's pushed down into a subquery
|
||||||
|
-- (fallout from the fix for bug #19560)
|
||||||
|
explain (verbose, costs off)
|
||||||
|
select c1, c2
|
||||||
|
from (select case when false then remov.id end as c1
|
||||||
|
from int4_tbl i41 left join a remov on i41.f1 = remov.id) ss1
|
||||||
|
right join int4_tbl i42 on false,
|
||||||
|
lateral (select ss1.c1 as c2 from int4_tbl i43 offset 0) ss2;
|
||||||
|
QUERY PLAN
|
||||||
|
----------------------------------------------
|
||||||
|
Nested Loop
|
||||||
|
Output: (NULL::integer), ((NULL::integer))
|
||||||
|
-> Nested Loop Left Join
|
||||||
|
Output: (NULL::integer)
|
||||||
|
Join Filter: false
|
||||||
|
-> Seq Scan on public.int4_tbl i42
|
||||||
|
Output: i42.f1
|
||||||
|
-> Result
|
||||||
|
Output: NULL::integer
|
||||||
|
Replaces: Scan on i41
|
||||||
|
One-Time Filter: false
|
||||||
|
-> Memoize
|
||||||
|
Output: ((NULL::integer))
|
||||||
|
Cache Key: (NULL::integer)
|
||||||
|
Cache Mode: binary
|
||||||
|
-> Seq Scan on public.int4_tbl i43
|
||||||
|
Output: (NULL::integer)
|
||||||
|
(17 rows)
|
||||||
|
|
||||||
|
-- likewise, where the subquery is a UNION ALL whose arms are appendrel
|
||||||
|
-- children that are not simple enough to be pulled up
|
||||||
|
explain (verbose, costs off)
|
||||||
|
select c1, c2
|
||||||
|
from (select case when false then remov.id end as c1
|
||||||
|
from int4_tbl i41 left join a remov on i41.f1 = remov.id) ss1
|
||||||
|
right join int4_tbl i42 on false,
|
||||||
|
lateral ((select ss1.c1 as c2 from int4_tbl i43 offset 0)
|
||||||
|
union all
|
||||||
|
(select ss1.c1 from int4_tbl i44 offset 0)) ss2;
|
||||||
|
QUERY PLAN
|
||||||
|
---------------------------------------------------
|
||||||
|
Nested Loop
|
||||||
|
Output: (NULL::integer), ((NULL::integer))
|
||||||
|
-> Nested Loop Left Join
|
||||||
|
Output: (NULL::integer)
|
||||||
|
Join Filter: false
|
||||||
|
-> Seq Scan on public.int4_tbl i42
|
||||||
|
Output: i42.f1
|
||||||
|
-> Result
|
||||||
|
Output: NULL::integer
|
||||||
|
Replaces: Scan on i41
|
||||||
|
One-Time Filter: false
|
||||||
|
-> Memoize
|
||||||
|
Output: ((NULL::integer))
|
||||||
|
Cache Key: (NULL::integer)
|
||||||
|
Cache Mode: binary
|
||||||
|
-> Append
|
||||||
|
-> Seq Scan on public.int4_tbl i43
|
||||||
|
Output: (NULL::integer)
|
||||||
|
-> Seq Scan on public.int4_tbl i44
|
||||||
|
Output: (NULL::integer)
|
||||||
|
(20 rows)
|
||||||
|
|
||||||
|
-- likewise, where the PHV contains a SubPlan and the subquery has a join, so
|
||||||
|
-- that its planner runs flatten_join_alias_vars over the outer-level PHV
|
||||||
|
explain (verbose, costs off)
|
||||||
|
select c1, c2
|
||||||
|
from (select case when false then remov.id else (select i41.f1) end as c1
|
||||||
|
from int4_tbl i41 left join a remov on i41.f1 = remov.id) ss1
|
||||||
|
right join int4_tbl i42 on false,
|
||||||
|
lateral (select ss1.c1 as c2 from int4_tbl i43 join int4_tbl i44 on true
|
||||||
|
offset 0) ss2;
|
||||||
|
QUERY PLAN
|
||||||
|
---------------------------------------------------------
|
||||||
|
Nested Loop
|
||||||
|
Output: ((SubPlan expr_2)), (((SubPlan expr_2)))
|
||||||
|
-> Nested Loop Left Join
|
||||||
|
Output: ((SubPlan expr_2))
|
||||||
|
Join Filter: false
|
||||||
|
-> Seq Scan on public.int4_tbl i42
|
||||||
|
Output: i42.f1
|
||||||
|
-> Result
|
||||||
|
Output: (SubPlan expr_2)
|
||||||
|
Replaces: Scan on i41
|
||||||
|
One-Time Filter: false
|
||||||
|
SubPlan expr_2
|
||||||
|
-> Result
|
||||||
|
Output: i41.f1
|
||||||
|
-> Memoize
|
||||||
|
Output: (((SubPlan expr_2)))
|
||||||
|
Cache Key: ((SubPlan expr_2))
|
||||||
|
Cache Mode: binary
|
||||||
|
-> Nested Loop
|
||||||
|
Output: ((SubPlan expr_2))
|
||||||
|
-> Seq Scan on public.int4_tbl i43
|
||||||
|
Output: i43.f1
|
||||||
|
-> Materialize
|
||||||
|
-> Seq Scan on public.int4_tbl i44
|
||||||
|
(24 rows)
|
||||||
|
|
||||||
|
-- likewise, where the PHV copy is pushed into a SubLink's subselect rather
|
||||||
|
-- than a LATERAL subquery
|
||||||
|
explain (verbose, costs off)
|
||||||
|
select (select ss1.c1 from int4_tbl i43 offset 0) as c2
|
||||||
|
from (select case when false then remov.id end as c1
|
||||||
|
from int4_tbl i41 left join a remov on i41.f1 = remov.id) ss1
|
||||||
|
right join int4_tbl i42 on true;
|
||||||
|
QUERY PLAN
|
||||||
|
---------------------------------------------
|
||||||
|
Nested Loop Left Join
|
||||||
|
Output: (SubPlan expr_1)
|
||||||
|
-> Seq Scan on public.int4_tbl i42
|
||||||
|
Output: i42.f1
|
||||||
|
-> Materialize
|
||||||
|
Output: (NULL::integer)
|
||||||
|
-> Seq Scan on public.int4_tbl i41
|
||||||
|
Output: NULL::integer
|
||||||
|
SubPlan expr_1
|
||||||
|
-> Seq Scan on public.int4_tbl i43
|
||||||
|
Output: (NULL::integer)
|
||||||
|
(11 rows)
|
||||||
|
|
||||||
|
-- likewise, where the pushed-down PHV's expression itself contains a SubLink,
|
||||||
|
-- so it must be preprocessed once at the outer level rather than again while
|
||||||
|
-- building the SubPlan that references it
|
||||||
|
explain (verbose, costs off)
|
||||||
|
select (select ss1.c1 from int4_tbl i43 offset 0) as c2
|
||||||
|
from (select case when false then remov.id else (select i41.f1) end as c1
|
||||||
|
from int4_tbl i41 left join a remov on i41.f1 = remov.id) ss1
|
||||||
|
right join int4_tbl i42 on true;
|
||||||
|
QUERY PLAN
|
||||||
|
---------------------------------------------
|
||||||
|
Nested Loop Left Join
|
||||||
|
Output: (SubPlan expr_2)
|
||||||
|
-> Seq Scan on public.int4_tbl i42
|
||||||
|
Output: i42.f1
|
||||||
|
-> Materialize
|
||||||
|
Output: ((SubPlan expr_1))
|
||||||
|
-> Seq Scan on public.int4_tbl i41
|
||||||
|
Output: (SubPlan expr_1)
|
||||||
|
SubPlan expr_1
|
||||||
|
-> Result
|
||||||
|
Output: i41.f1
|
||||||
|
SubPlan expr_2
|
||||||
|
-> Seq Scan on public.int4_tbl i43
|
||||||
|
Output: ((SubPlan expr_1))
|
||||||
|
(14 rows)
|
||||||
|
|
||||||
-- More tests of correct placement of pseudoconstant quals
|
-- More tests of correct placement of pseudoconstant quals
|
||||||
-- simple constant-false condition
|
-- simple constant-false condition
|
||||||
explain (costs off)
|
explain (costs off)
|
||||||
|
|||||||
@@ -2699,6 +2699,53 @@ full join
|
|||||||
) as rhs
|
) as rhs
|
||||||
on lhs.id = rhs.id;
|
on lhs.id = rhs.id;
|
||||||
|
|
||||||
|
-- check handling of a removed Var that's pushed down into a subquery
|
||||||
|
-- (fallout from the fix for bug #19560)
|
||||||
|
explain (verbose, costs off)
|
||||||
|
select c1, c2
|
||||||
|
from (select case when false then remov.id end as c1
|
||||||
|
from int4_tbl i41 left join a remov on i41.f1 = remov.id) ss1
|
||||||
|
right join int4_tbl i42 on false,
|
||||||
|
lateral (select ss1.c1 as c2 from int4_tbl i43 offset 0) ss2;
|
||||||
|
|
||||||
|
-- likewise, where the subquery is a UNION ALL whose arms are appendrel
|
||||||
|
-- children that are not simple enough to be pulled up
|
||||||
|
explain (verbose, costs off)
|
||||||
|
select c1, c2
|
||||||
|
from (select case when false then remov.id end as c1
|
||||||
|
from int4_tbl i41 left join a remov on i41.f1 = remov.id) ss1
|
||||||
|
right join int4_tbl i42 on false,
|
||||||
|
lateral ((select ss1.c1 as c2 from int4_tbl i43 offset 0)
|
||||||
|
union all
|
||||||
|
(select ss1.c1 from int4_tbl i44 offset 0)) ss2;
|
||||||
|
|
||||||
|
-- likewise, where the PHV contains a SubPlan and the subquery has a join, so
|
||||||
|
-- that its planner runs flatten_join_alias_vars over the outer-level PHV
|
||||||
|
explain (verbose, costs off)
|
||||||
|
select c1, c2
|
||||||
|
from (select case when false then remov.id else (select i41.f1) end as c1
|
||||||
|
from int4_tbl i41 left join a remov on i41.f1 = remov.id) ss1
|
||||||
|
right join int4_tbl i42 on false,
|
||||||
|
lateral (select ss1.c1 as c2 from int4_tbl i43 join int4_tbl i44 on true
|
||||||
|
offset 0) ss2;
|
||||||
|
|
||||||
|
-- likewise, where the PHV copy is pushed into a SubLink's subselect rather
|
||||||
|
-- than a LATERAL subquery
|
||||||
|
explain (verbose, costs off)
|
||||||
|
select (select ss1.c1 from int4_tbl i43 offset 0) as c2
|
||||||
|
from (select case when false then remov.id end as c1
|
||||||
|
from int4_tbl i41 left join a remov on i41.f1 = remov.id) ss1
|
||||||
|
right join int4_tbl i42 on true;
|
||||||
|
|
||||||
|
-- likewise, where the pushed-down PHV's expression itself contains a SubLink,
|
||||||
|
-- so it must be preprocessed once at the outer level rather than again while
|
||||||
|
-- building the SubPlan that references it
|
||||||
|
explain (verbose, costs off)
|
||||||
|
select (select ss1.c1 from int4_tbl i43 offset 0) as c2
|
||||||
|
from (select case when false then remov.id else (select i41.f1) end as c1
|
||||||
|
from int4_tbl i41 left join a remov on i41.f1 = remov.id) ss1
|
||||||
|
right join int4_tbl i42 on true;
|
||||||
|
|
||||||
-- More tests of correct placement of pseudoconstant quals
|
-- More tests of correct placement of pseudoconstant quals
|
||||||
|
|
||||||
-- simple constant-false condition
|
-- simple constant-false condition
|
||||||
|
|||||||
@@ -4170,6 +4170,7 @@ postprocess_result_function
|
|||||||
pqbool
|
pqbool
|
||||||
pqsigfunc
|
pqsigfunc
|
||||||
pqsigfunc_legacy
|
pqsigfunc_legacy
|
||||||
|
preprocess_subquery_phvs_context
|
||||||
printQueryOpt
|
printQueryOpt
|
||||||
printTableContent
|
printTableContent
|
||||||
printTableFooter
|
printTableFooter
|
||||||
|
|||||||
Reference in New Issue
Block a user