1
0
mirror of https://github.com/postgres/postgres.git synced 2025-10-19 15:49:24 +03:00

Replace lateral references to removed rels in subqueries

This commit introduces a new field 'sublevels_up' in ReplaceVarnoContext,
and enhances replace_varno_walker() to:
  1) recurse into subselects with sublevels_up increased, and
  2) perform the replacement only when varlevelsup is equal to sublevels_up.

This commit also fixes some outdated comments.  And besides adding relevant
test cases, it makes some unification over existing SJE test cases.

Discussion: https://postgr.es/m/CAMbWs4-%3DPO6Mm9gNnySbx0VHyXjgnnYYwbN9dth%3DTLQweZ-M%2Bg%40mail.gmail.com
Author: Richard Guo
Reviewed-by: Andrei Lepikhov, Alexander Korotkov
This commit is contained in:
Alexander Korotkov
2024-02-24 00:34:52 +02:00
parent a6b2a51e16
commit 466979ef03
3 changed files with 340 additions and 309 deletions

View File

@@ -34,15 +34,6 @@
#include "optimizer/tlist.h"
#include "utils/lsyscache.h"
/*
* The context for replace_varno_walker() containing source and target relids.
*/
typedef struct
{
int from;
int to;
} ReplaceVarnoContext;
/*
* The struct containing self-join candidate. Used to find duplicate reloids.
*/
@@ -75,13 +66,11 @@ static bool is_innerrel_unique_for(PlannerInfo *root,
JoinType jointype,
List *restrictlist,
List **extra_clauses);
static Bitmapset *replace_relid(Relids relids, int oldId, int newId);
static void replace_varno(Node *node, int from, int to);
static bool replace_varno_walker(Node *node, ReplaceVarnoContext *ctx);
static Bitmapset *replace_relid(Relids relids, int oldId, int newId);
static int self_join_candidates_cmp(const void *a, const void *b);
/*
* remove_useless_joins
* Check for relations that don't actually need to be joined at all,
@@ -367,7 +356,8 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
ListCell *l;
/*
* Remove references to the rel from other baserels' attr_needed arrays.
* Remove references to the rel from other baserels' attr_needed arrays
* and lateral_vars lists.
*/
for (rti = 1; rti < root->simple_rel_array_size; rti++)
{
@@ -394,35 +384,8 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
replace_relid(otherrel->attr_needed[attroff], ojrelid, subst);
}
/* Update lateral references. */
if (root->hasLateralRTEs)
{
RangeTblEntry *rte = root->simple_rte_array[rti];
ReplaceVarnoContext ctx = {.from = relid,.to = subst};
if (rte->lateral)
{
replace_varno((Node *) otherrel->lateral_vars, relid, subst);
/*
* Although we pass root->parse through cleanup procedure, but
* parse->rtable and rte contains refs to different copies of
* the subquery.
*/
if (otherrel->rtekind == RTE_SUBQUERY)
query_tree_walker(rte->subquery, replace_varno_walker, &ctx,
QTW_EXAMINE_SORTGROUP);
#ifdef USE_ASSERT_CHECKING
/* Just check possibly hidden non-replaced relids */
Assert(!bms_is_member(relid, pull_varnos(root, (Node *) rte->tablesample)));
Assert(!bms_is_member(relid, pull_varnos(root, (Node *) rte->functions)));
Assert(!bms_is_member(relid, pull_varnos(root, (Node *) rte->tablefunc)));
Assert(!bms_is_member(relid, pull_varnos(root, (Node *) rte->values_lists)));
#endif
}
}
/* Update lateral_vars list. */
replace_varno((Node *) otherrel->lateral_vars, relid, subst);
}
/*
@@ -1462,35 +1425,32 @@ is_innerrel_unique_for(PlannerInfo *root,
}
/*
* Replace each occurrence of removing relid with the keeping one
* replace_varno - find in the given tree any Vars, PlaceHolderVar, and Relids
* that reference the removing relid, and change them to the reference to
* the replacement relid.
*
* NOTE: although this has the form of a walker, we cheat and modify the
* nodes in-place.
*/
static void
replace_varno(Node *node, int from, int to)
typedef struct
{
ReplaceVarnoContext ctx;
int from;
int to;
int sublevels_up;
} ReplaceVarnoContext;
if (to <= 0)
return;
ctx.from = from;
ctx.to = to;
replace_varno_walker(node, &ctx);
}
/*
* Walker function for replace_varno()
*/
static bool
replace_varno_walker(Node *node, ReplaceVarnoContext *ctx)
{
if (node == NULL)
return false;
if (IsA(node, Var))
{
Var *var = (Var *) node;
if (var->varno == ctx->from)
if (var->varno == ctx->from &&
var->varlevelsup == ctx->sublevels_up)
{
var->varno = ctx->to;
var->varnosyn = ctx->to;
@@ -1501,11 +1461,29 @@ replace_varno_walker(Node *node, ReplaceVarnoContext *ctx)
{
PlaceHolderVar *phv = (PlaceHolderVar *) node;
phv->phrels = replace_relid(phv->phrels, ctx->from, ctx->to);
phv->phnullingrels = replace_relid(phv->phnullingrels, ctx->from, ctx->to);
if (phv->phlevelsup == ctx->sublevels_up)
{
phv->phrels =
replace_relid(phv->phrels, ctx->from, ctx->to);
phv->phnullingrels =
replace_relid(phv->phnullingrels, ctx->from, ctx->to);
}
/* fall through to recurse into the placeholder's expression */
}
else if (IsA(node, Query))
{
/* Recurse into subselects */
bool result;
ctx->sublevels_up++;
result = query_tree_walker((Query *) node,
replace_varno_walker,
(void *) ctx,
QTW_EXAMINE_SORTGROUP);
ctx->sublevels_up--;
return result;
}
else if (IsA(node, RestrictInfo))
{
RestrictInfo *rinfo = (RestrictInfo *) node;
@@ -1517,18 +1495,24 @@ replace_varno_walker(Node *node, ReplaceVarnoContext *ctx)
{
replace_varno((Node *) rinfo->clause, ctx->from, ctx->to);
replace_varno((Node *) rinfo->orclause, ctx->from, ctx->to);
rinfo->clause_relids = replace_relid(rinfo->clause_relids, ctx->from, ctx->to);
rinfo->left_relids = replace_relid(rinfo->left_relids, ctx->from, ctx->to);
rinfo->right_relids = replace_relid(rinfo->right_relids, ctx->from, ctx->to);
rinfo->clause_relids =
replace_relid(rinfo->clause_relids, ctx->from, ctx->to);
rinfo->left_relids =
replace_relid(rinfo->left_relids, ctx->from, ctx->to);
rinfo->right_relids =
replace_relid(rinfo->right_relids, ctx->from, ctx->to);
}
if (is_req_equal)
rinfo->required_relids = rinfo->clause_relids;
else
rinfo->required_relids = replace_relid(rinfo->required_relids, ctx->from, ctx->to);
rinfo->required_relids =
replace_relid(rinfo->required_relids, ctx->from, ctx->to);
rinfo->outer_relids = replace_relid(rinfo->outer_relids, ctx->from, ctx->to);
rinfo->incompatible_relids = replace_relid(rinfo->incompatible_relids, ctx->from, ctx->to);
rinfo->outer_relids =
replace_relid(rinfo->outer_relids, ctx->from, ctx->to);
rinfo->incompatible_relids =
replace_relid(rinfo->incompatible_relids, ctx->from, ctx->to);
if (rinfo->mergeopfamilies &&
bms_get_singleton_member(rinfo->clause_relids, &relid) &&
@@ -1556,7 +1540,30 @@ replace_varno_walker(Node *node, ReplaceVarnoContext *ctx)
return false;
}
return expression_tree_walker(node, replace_varno_walker, (void *) ctx);
return expression_tree_walker(node, replace_varno_walker,
(void *) ctx);
}
static void
replace_varno(Node *node, int from, int to)
{
ReplaceVarnoContext ctx;
if (to <= 0)
return;
ctx.from = from;
ctx.to = to;
ctx.sublevels_up = 0;
/*
* Must be prepared to start with a Query or a bare expression tree.
*/
query_or_expression_tree_walker(node,
replace_varno_walker,
(void *) &ctx,
QTW_EXAMINE_SORTGROUP);
}
/*
@@ -1748,7 +1755,6 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
int i;
List *jinfo_candidates = NIL;
List *binfo_candidates = NIL;
ReplaceVarnoContext ctx = {.from = toRemove->relid,.to = toKeep->relid};
Assert(toKeep->relid != -1);
@@ -1925,8 +1931,7 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
}
/* Replace varno in all the query structures */
query_tree_walker(root->parse, replace_varno_walker, &ctx,
QTW_EXAMINE_SORTGROUP);
replace_varno((Node *) root->parse, toRemove->relid, toKeep->relid);
/* See remove_self_joins_one_group() */
Assert(root->parse->resultRelation != toRemove->relid);