mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
Centralize collection of catalog info needed early in the planner
There are several pieces of catalog information that need to be retrieved for a relation during the early stage of planning. These include relhassubclass, which is used to clear the inh flag if the relation has no children, as well as a column's attgenerated and default value, which are needed to expand virtual generated columns. More such information may be required in the future. Currently, these pieces of catalog data are collected in multiple places, resulting in repeated table_open/table_close calls for each relation in the rangetable. This patch centralizes the collection of all required early-stage catalog information into a single loop over the rangetable, allowing each relation to be opened and closed only once. Author: Richard Guo <guofenglinux@gmail.com> Reviewed-by: Robert Haas <robertmhaas@gmail.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://postgr.es/m/CAMbWs4-bFJ1At4btk5wqbezdu8PLtQ3zv-aiaY3ry9Ymm=jgFQ@mail.gmail.com
This commit is contained in:
@ -721,13 +721,15 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
|
|||||||
transform_MERGE_to_join(parse);
|
transform_MERGE_to_join(parse);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Scan the rangetable for relations with virtual generated columns, and
|
* Scan the rangetable for relation RTEs and retrieve the necessary
|
||||||
* replace all Var nodes in the query that reference these columns with
|
* catalog information for each relation. Using this information, clear
|
||||||
* the generation expressions. Note that this step does not descend into
|
* the inh flag for any relation that has no children, and expand virtual
|
||||||
* sublinks and subqueries; if we pull up any sublinks or subqueries
|
* generated columns for any relation that contains them. Note that this
|
||||||
* below, their rangetables are processed just before pulling them up.
|
* step does not descend into sublinks and subqueries; if we pull up any
|
||||||
|
* sublinks or subqueries below, their relation RTEs are processed just
|
||||||
|
* before pulling them up.
|
||||||
*/
|
*/
|
||||||
parse = root->parse = expand_virtual_generated_columns(root);
|
parse = root->parse = preprocess_relation_rtes(root);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the FROM clause is empty, replace it with a dummy RTE_RESULT RTE, so
|
* If the FROM clause is empty, replace it with a dummy RTE_RESULT RTE, so
|
||||||
@ -788,23 +790,6 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
|
|||||||
|
|
||||||
switch (rte->rtekind)
|
switch (rte->rtekind)
|
||||||
{
|
{
|
||||||
case RTE_RELATION:
|
|
||||||
if (rte->inh)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Check to see if the relation actually has any children;
|
|
||||||
* if not, clear the inh flag so we can treat it as a
|
|
||||||
* plain base relation.
|
|
||||||
*
|
|
||||||
* Note: this could give a false-positive result, if the
|
|
||||||
* rel once had children but no longer does. We used to
|
|
||||||
* be able to clear rte->inh later on when we discovered
|
|
||||||
* that, but no more; we have to handle such cases as
|
|
||||||
* full-fledged inheritance.
|
|
||||||
*/
|
|
||||||
rte->inh = has_subclass(rte->relid);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RTE_JOIN:
|
case RTE_JOIN:
|
||||||
root->hasJoinRTEs = true;
|
root->hasJoinRTEs = true;
|
||||||
if (IS_OUTER_JOIN(rte->jointype))
|
if (IS_OUTER_JOIN(rte->jointype))
|
||||||
|
@ -1517,9 +1517,10 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Scan the rangetable for relations with virtual generated columns, and
|
* Scan the rangetable for relation RTEs and retrieve the necessary
|
||||||
* replace all Var nodes in the subquery that reference these columns with
|
* catalog information for each relation. Using this information, clear
|
||||||
* the generation expressions.
|
* the inh flag for any relation that has no children, and expand virtual
|
||||||
|
* generated columns for any relation that contains them.
|
||||||
*
|
*
|
||||||
* Note: we construct up an entirely dummy PlannerInfo for use here. This
|
* Note: we construct up an entirely dummy PlannerInfo for use here. This
|
||||||
* is fine because only the "glob" and "parse" links will be used in this
|
* is fine because only the "glob" and "parse" links will be used in this
|
||||||
@ -1534,7 +1535,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
|
|||||||
subroot.glob = root->glob;
|
subroot.glob = root->glob;
|
||||||
subroot.parse = subselect;
|
subroot.parse = subselect;
|
||||||
subselect->jointree->quals = whereClause;
|
subselect->jointree->quals = whereClause;
|
||||||
subselect = expand_virtual_generated_columns(&subroot);
|
subselect = preprocess_relation_rtes(&subroot);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now separate out the WHERE clause again.
|
* Now separate out the WHERE clause again.
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* Planner preprocessing for subqueries and join tree manipulation.
|
* Planner preprocessing for subqueries and join tree manipulation.
|
||||||
*
|
*
|
||||||
* NOTE: the intended sequence for invoking these operations is
|
* NOTE: the intended sequence for invoking these operations is
|
||||||
* expand_virtual_generated_columns
|
* preprocess_relation_rtes
|
||||||
* replace_empty_jointree
|
* replace_empty_jointree
|
||||||
* pull_up_sublinks
|
* pull_up_sublinks
|
||||||
* preprocess_function_rtes
|
* preprocess_function_rtes
|
||||||
@ -102,6 +102,9 @@ typedef struct reduce_outer_joins_partial_state
|
|||||||
Relids unreduced_side; /* relids in its still-nullable side */
|
Relids unreduced_side; /* relids in its still-nullable side */
|
||||||
} reduce_outer_joins_partial_state;
|
} reduce_outer_joins_partial_state;
|
||||||
|
|
||||||
|
static Query *expand_virtual_generated_columns(PlannerInfo *root, Query *parse,
|
||||||
|
RangeTblEntry *rte, int rt_index,
|
||||||
|
Relation relation);
|
||||||
static Node *pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
|
static Node *pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
|
||||||
Relids *relids);
|
Relids *relids);
|
||||||
static Node *pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
|
static Node *pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
|
||||||
@ -392,6 +395,173 @@ transform_MERGE_to_join(Query *parse)
|
|||||||
parse->mergeJoinCondition = NULL; /* join condition not needed */
|
parse->mergeJoinCondition = NULL; /* join condition not needed */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* preprocess_relation_rtes
|
||||||
|
* Do the preprocessing work for any relation RTEs in the FROM clause.
|
||||||
|
*
|
||||||
|
* This scans the rangetable for relation RTEs and retrieves the necessary
|
||||||
|
* catalog information for each relation. Using this information, it clears
|
||||||
|
* the inh flag for any relation that has no children, and expands virtual
|
||||||
|
* generated columns for any relation that contains them.
|
||||||
|
*
|
||||||
|
* Note that expanding virtual generated columns may cause the query tree to
|
||||||
|
* have new copies of rangetable entries. Therefore, we have to use list_nth
|
||||||
|
* instead of foreach when iterating over the query's rangetable.
|
||||||
|
*
|
||||||
|
* Returns a modified copy of the query tree, if any relations with virtual
|
||||||
|
* generated columns are present.
|
||||||
|
*/
|
||||||
|
Query *
|
||||||
|
preprocess_relation_rtes(PlannerInfo *root)
|
||||||
|
{
|
||||||
|
Query *parse = root->parse;
|
||||||
|
int rtable_size;
|
||||||
|
int rt_index;
|
||||||
|
|
||||||
|
rtable_size = list_length(parse->rtable);
|
||||||
|
|
||||||
|
for (rt_index = 0; rt_index < rtable_size; rt_index++)
|
||||||
|
{
|
||||||
|
RangeTblEntry *rte = rt_fetch(rt_index + 1, parse->rtable);
|
||||||
|
Relation relation;
|
||||||
|
|
||||||
|
/* We only care about relation RTEs. */
|
||||||
|
if (rte->rtekind != RTE_RELATION)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need not lock the relation since it was already locked by the
|
||||||
|
* rewriter.
|
||||||
|
*/
|
||||||
|
relation = table_open(rte->relid, NoLock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check to see if the relation actually has any children; if not,
|
||||||
|
* clear the inh flag so we can treat it as a plain base relation.
|
||||||
|
*
|
||||||
|
* Note: this could give a false-positive result, if the rel once had
|
||||||
|
* children but no longer does. We used to be able to clear rte->inh
|
||||||
|
* later on when we discovered that, but no more; we have to handle
|
||||||
|
* such cases as full-fledged inheritance.
|
||||||
|
*/
|
||||||
|
if (rte->inh)
|
||||||
|
rte->inh = relation->rd_rel->relhassubclass;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check to see if the relation has any virtual generated columns; if
|
||||||
|
* so, replace all Var nodes in the query that reference these columns
|
||||||
|
* with the generation expressions.
|
||||||
|
*/
|
||||||
|
parse = expand_virtual_generated_columns(root, parse,
|
||||||
|
rte, rt_index + 1,
|
||||||
|
relation);
|
||||||
|
|
||||||
|
table_close(relation, NoLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* expand_virtual_generated_columns
|
||||||
|
* Expand virtual generated columns for the given relation.
|
||||||
|
*
|
||||||
|
* This checks whether the given relation has any virtual generated columns,
|
||||||
|
* and if so, replaces all Var nodes in the query that reference those columns
|
||||||
|
* with their generation expressions.
|
||||||
|
*
|
||||||
|
* Returns a modified copy of the query tree if the relation contains virtual
|
||||||
|
* generated columns.
|
||||||
|
*/
|
||||||
|
static Query *
|
||||||
|
expand_virtual_generated_columns(PlannerInfo *root, Query *parse,
|
||||||
|
RangeTblEntry *rte, int rt_index,
|
||||||
|
Relation relation)
|
||||||
|
{
|
||||||
|
TupleDesc tupdesc;
|
||||||
|
|
||||||
|
/* Only normal relations can have virtual generated columns */
|
||||||
|
Assert(rte->rtekind == RTE_RELATION);
|
||||||
|
|
||||||
|
tupdesc = RelationGetDescr(relation);
|
||||||
|
if (tupdesc->constr && tupdesc->constr->has_generated_virtual)
|
||||||
|
{
|
||||||
|
List *tlist = NIL;
|
||||||
|
pullup_replace_vars_context rvcontext;
|
||||||
|
|
||||||
|
for (int i = 0; i < tupdesc->natts; i++)
|
||||||
|
{
|
||||||
|
Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
|
||||||
|
TargetEntry *tle;
|
||||||
|
|
||||||
|
if (attr->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
|
||||||
|
{
|
||||||
|
Node *defexpr;
|
||||||
|
|
||||||
|
defexpr = build_generation_expression(relation, i + 1);
|
||||||
|
ChangeVarNodes(defexpr, 1, rt_index, 0);
|
||||||
|
|
||||||
|
tle = makeTargetEntry((Expr *) defexpr, i + 1, 0, false);
|
||||||
|
tlist = lappend(tlist, tle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Var *var;
|
||||||
|
|
||||||
|
var = makeVar(rt_index,
|
||||||
|
i + 1,
|
||||||
|
attr->atttypid,
|
||||||
|
attr->atttypmod,
|
||||||
|
attr->attcollation,
|
||||||
|
0);
|
||||||
|
|
||||||
|
tle = makeTargetEntry((Expr *) var, i + 1, 0, false);
|
||||||
|
tlist = lappend(tlist, tle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert(list_length(tlist) > 0);
|
||||||
|
Assert(!rte->lateral);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The relation's targetlist items are now in the appropriate form to
|
||||||
|
* insert into the query, except that we may need to wrap them in
|
||||||
|
* PlaceHolderVars. Set up required context data for
|
||||||
|
* pullup_replace_vars.
|
||||||
|
*/
|
||||||
|
rvcontext.root = root;
|
||||||
|
rvcontext.targetlist = tlist;
|
||||||
|
rvcontext.target_rte = rte;
|
||||||
|
rvcontext.result_relation = parse->resultRelation;
|
||||||
|
/* won't need these values */
|
||||||
|
rvcontext.relids = NULL;
|
||||||
|
rvcontext.nullinfo = NULL;
|
||||||
|
/* pass NULL for outer_hasSubLinks */
|
||||||
|
rvcontext.outer_hasSubLinks = NULL;
|
||||||
|
rvcontext.varno = rt_index;
|
||||||
|
/* this flag will be set below, if needed */
|
||||||
|
rvcontext.wrap_option = REPLACE_WRAP_NONE;
|
||||||
|
/* initialize cache array with indexes 0 .. length(tlist) */
|
||||||
|
rvcontext.rv_cache = palloc0((list_length(tlist) + 1) *
|
||||||
|
sizeof(Node *));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the query uses grouping sets, we need a PlaceHolderVar for each
|
||||||
|
* expression of the relation's targetlist items. (See comments in
|
||||||
|
* pull_up_simple_subquery().)
|
||||||
|
*/
|
||||||
|
if (parse->groupingSets)
|
||||||
|
rvcontext.wrap_option = REPLACE_WRAP_ALL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Apply pullup variable replacement throughout the query tree.
|
||||||
|
*/
|
||||||
|
parse = (Query *) pullup_replace_vars((Node *) parse, &rvcontext);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parse;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* replace_empty_jointree
|
* replace_empty_jointree
|
||||||
* If the Query's jointree is empty, replace it with a dummy RTE_RESULT
|
* If the Query's jointree is empty, replace it with a dummy RTE_RESULT
|
||||||
@ -949,124 +1119,6 @@ preprocess_function_rtes(PlannerInfo *root)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* expand_virtual_generated_columns
|
|
||||||
* Expand all virtual generated column references in a query.
|
|
||||||
*
|
|
||||||
* This scans the rangetable for relations with virtual generated columns, and
|
|
||||||
* replaces all Var nodes in the query that reference these columns with the
|
|
||||||
* generation expressions. Note that we do not descend into subqueries; that
|
|
||||||
* is taken care of when the subqueries are planned.
|
|
||||||
*
|
|
||||||
* Returns a modified copy of the query tree, if any relations with virtual
|
|
||||||
* generated columns are present.
|
|
||||||
*/
|
|
||||||
Query *
|
|
||||||
expand_virtual_generated_columns(PlannerInfo *root)
|
|
||||||
{
|
|
||||||
Query *parse = root->parse;
|
|
||||||
int rt_index;
|
|
||||||
ListCell *lc;
|
|
||||||
|
|
||||||
rt_index = 0;
|
|
||||||
foreach(lc, parse->rtable)
|
|
||||||
{
|
|
||||||
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
|
|
||||||
Relation rel;
|
|
||||||
TupleDesc tupdesc;
|
|
||||||
|
|
||||||
++rt_index;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Only normal relations can have virtual generated columns.
|
|
||||||
*/
|
|
||||||
if (rte->rtekind != RTE_RELATION)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
rel = table_open(rte->relid, NoLock);
|
|
||||||
|
|
||||||
tupdesc = RelationGetDescr(rel);
|
|
||||||
if (tupdesc->constr && tupdesc->constr->has_generated_virtual)
|
|
||||||
{
|
|
||||||
List *tlist = NIL;
|
|
||||||
pullup_replace_vars_context rvcontext;
|
|
||||||
|
|
||||||
for (int i = 0; i < tupdesc->natts; i++)
|
|
||||||
{
|
|
||||||
Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
|
|
||||||
TargetEntry *tle;
|
|
||||||
|
|
||||||
if (attr->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
|
|
||||||
{
|
|
||||||
Node *defexpr;
|
|
||||||
|
|
||||||
defexpr = build_generation_expression(rel, i + 1);
|
|
||||||
ChangeVarNodes(defexpr, 1, rt_index, 0);
|
|
||||||
|
|
||||||
tle = makeTargetEntry((Expr *) defexpr, i + 1, 0, false);
|
|
||||||
tlist = lappend(tlist, tle);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Var *var;
|
|
||||||
|
|
||||||
var = makeVar(rt_index,
|
|
||||||
i + 1,
|
|
||||||
attr->atttypid,
|
|
||||||
attr->atttypmod,
|
|
||||||
attr->attcollation,
|
|
||||||
0);
|
|
||||||
|
|
||||||
tle = makeTargetEntry((Expr *) var, i + 1, 0, false);
|
|
||||||
tlist = lappend(tlist, tle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert(list_length(tlist) > 0);
|
|
||||||
Assert(!rte->lateral);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The relation's targetlist items are now in the appropriate form
|
|
||||||
* to insert into the query, except that we may need to wrap them
|
|
||||||
* in PlaceHolderVars. Set up required context data for
|
|
||||||
* pullup_replace_vars.
|
|
||||||
*/
|
|
||||||
rvcontext.root = root;
|
|
||||||
rvcontext.targetlist = tlist;
|
|
||||||
rvcontext.target_rte = rte;
|
|
||||||
rvcontext.result_relation = parse->resultRelation;
|
|
||||||
/* won't need these values */
|
|
||||||
rvcontext.relids = NULL;
|
|
||||||
rvcontext.nullinfo = NULL;
|
|
||||||
/* pass NULL for outer_hasSubLinks */
|
|
||||||
rvcontext.outer_hasSubLinks = NULL;
|
|
||||||
rvcontext.varno = rt_index;
|
|
||||||
/* this flag will be set below, if needed */
|
|
||||||
rvcontext.wrap_option = REPLACE_WRAP_NONE;
|
|
||||||
/* initialize cache array with indexes 0 .. length(tlist) */
|
|
||||||
rvcontext.rv_cache = palloc0((list_length(tlist) + 1) *
|
|
||||||
sizeof(Node *));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the query uses grouping sets, we need a PlaceHolderVar for
|
|
||||||
* each expression of the relation's targetlist items. (See
|
|
||||||
* comments in pull_up_simple_subquery().)
|
|
||||||
*/
|
|
||||||
if (parse->groupingSets)
|
|
||||||
rvcontext.wrap_option = REPLACE_WRAP_ALL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Apply pullup variable replacement throughout the query tree.
|
|
||||||
*/
|
|
||||||
parse = (Query *) pullup_replace_vars((Node *) parse, &rvcontext);
|
|
||||||
}
|
|
||||||
|
|
||||||
table_close(rel, NoLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parse;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pull_up_subqueries
|
* pull_up_subqueries
|
||||||
* Look for subqueries in the rangetable that can be pulled up into
|
* Look for subqueries in the rangetable that can be pulled up into
|
||||||
@ -1330,11 +1382,12 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
|
|||||||
Assert(subquery->cteList == NIL);
|
Assert(subquery->cteList == NIL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Scan the rangetable for relations with virtual generated columns, and
|
* Scan the rangetable for relation RTEs and retrieve the necessary
|
||||||
* replace all Var nodes in the subquery that reference these columns with
|
* catalog information for each relation. Using this information, clear
|
||||||
* the generation expressions.
|
* the inh flag for any relation that has no children, and expand virtual
|
||||||
|
* generated columns for any relation that contains them.
|
||||||
*/
|
*/
|
||||||
subquery = subroot->parse = expand_virtual_generated_columns(subroot);
|
subquery = subroot->parse = preprocess_relation_rtes(subroot);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the FROM clause is empty, replace it with a dummy RTE_RESULT RTE, so
|
* If the FROM clause is empty, replace it with a dummy RTE_RESULT RTE, so
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
* prototypes for prepjointree.c
|
* prototypes for prepjointree.c
|
||||||
*/
|
*/
|
||||||
extern void transform_MERGE_to_join(Query *parse);
|
extern void transform_MERGE_to_join(Query *parse);
|
||||||
extern Query *expand_virtual_generated_columns(PlannerInfo *root);
|
extern Query *preprocess_relation_rtes(PlannerInfo *root);
|
||||||
extern void replace_empty_jointree(Query *parse);
|
extern void replace_empty_jointree(Query *parse);
|
||||||
extern void pull_up_sublinks(PlannerInfo *root);
|
extern void pull_up_sublinks(PlannerInfo *root);
|
||||||
extern void preprocess_function_rtes(PlannerInfo *root);
|
extern void preprocess_function_rtes(PlannerInfo *root);
|
||||||
|
Reference in New Issue
Block a user