postgresql/src/backend/optimizer/util/inherit.c

741 lines
24 KiB
C

/*-------------------------------------------------------------------------
*
* inherit.c
* Routines to process child relations in inheritance trees
*
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/optimizer/path/inherit.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "catalog/partition.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "optimizer/appendinfo.h"
#include "optimizer/inherit.h"
#include "optimizer/optimizer.h"
#include "optimizer/pathnode.h"
#include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
#include "parser/parsetree.h"
#include "partitioning/partdesc.h"
#include "partitioning/partprune.h"
#include "utils/rel.h"
static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
RangeTblEntry *parentrte,
Index parentRTindex, Relation parentrel,
PlanRowMark *top_parentrc, LOCKMODE lockmode);
static void expand_single_inheritance_child(PlannerInfo *root,
RangeTblEntry *parentrte,
Index parentRTindex, Relation parentrel,
PlanRowMark *top_parentrc, Relation childrel,
RangeTblEntry **childrte_p,
Index *childRTindex_p);
static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
List *translated_vars);
static void expand_appendrel_subquery(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte, Index rti);
/*
* expand_inherited_rtentry
* Expand a rangetable entry that has the "inh" bit set.
*
* "inh" is only allowed in two cases: RELATION and SUBQUERY RTEs.
*
* "inh" on a plain RELATION RTE means that it is a partitioned table or the
* parent of a traditional-inheritance set. In this case we must add entries
* for all the interesting child tables to the query's rangetable, and build
* additional planner data structures for them, including RelOptInfos,
* AppendRelInfos, and possibly PlanRowMarks.
*
* Note that the original RTE is considered to represent the whole inheritance
* set. In the case of traditional inheritance, the first of the generated
* RTEs is an RTE for the same table, but with inh = false, to represent the
* parent table in its role as a simple member of the inheritance set. For
* partitioning, we don't need a second RTE because the partitioned table
* itself has no data and need not be scanned.
*
* "inh" on a SUBQUERY RTE means that it's the parent of a UNION ALL group,
* which is treated as an appendrel similarly to inheritance cases; however,
* we already made RTEs and AppendRelInfos for the subqueries. We only need
* to build RelOptInfos for them, which is done by expand_appendrel_subquery.
*/
void
expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte, Index rti)
{
Oid parentOID;
Relation oldrelation;
LOCKMODE lockmode;
PlanRowMark *oldrc;
bool old_isParent = false;
int old_allMarkTypes = 0;
Assert(rte->inh); /* else caller error */
if (rte->rtekind == RTE_SUBQUERY)
{
expand_appendrel_subquery(root, rel, rte, rti);
return;
}
Assert(rte->rtekind == RTE_RELATION);
parentOID = rte->relid;
/*
* We used to check has_subclass() here, but there's no longer any need
* to, because subquery_planner already did.
*/
/*
* The rewriter should already have obtained an appropriate lock on each
* relation named in the query, so we can open the parent relation without
* locking it. However, for each child relation we add to the query, we
* must obtain an appropriate lock, because this will be the first use of
* those relations in the parse/rewrite/plan pipeline. Child rels should
* use the same lockmode as their parent.
*/
oldrelation = table_open(parentOID, NoLock);
lockmode = rte->rellockmode;
/*
* If parent relation is selected FOR UPDATE/SHARE, we need to mark its
* PlanRowMark as isParent = true, and generate a new PlanRowMark for each
* child.
*/
oldrc = get_plan_rowmark(root->rowMarks, rti);
if (oldrc)
{
old_isParent = oldrc->isParent;
oldrc->isParent = true;
/* Save initial value of allMarkTypes before children add to it */
old_allMarkTypes = oldrc->allMarkTypes;
}
/* Scan the inheritance set and expand it */
if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
{
/*
* Partitioned table, so set up for partitioning.
*/
Assert(rte->relkind == RELKIND_PARTITIONED_TABLE);
/*
* Recursively expand and lock the partitions. While at it, also
* extract the partition key columns of all the partitioned tables.
*/
expand_partitioned_rtentry(root, rel, rte, rti,
oldrelation, oldrc, lockmode);
}
else
{
/*
* Ordinary table, so process traditional-inheritance children. (Note
* that partitioned tables are not allowed to have inheritance
* children, so it's not possible for both cases to apply.)
*/
List *inhOIDs;
ListCell *l;
/* Scan for all members of inheritance set, acquire needed locks */
inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);
/*
* We used to special-case the situation where the table no longer has
* any children, by clearing rte->inh and exiting. That no longer
* works, because this function doesn't get run until after decisions
* have been made that depend on rte->inh. We have to treat such
* situations as normal inheritance. The table itself should always
* have been found, though.
*/
Assert(inhOIDs != NIL);
Assert(linitial_oid(inhOIDs) == parentOID);
/* Expand simple_rel_array and friends to hold child objects. */
expand_planner_arrays(root, list_length(inhOIDs));
/*
* Expand inheritance children in the order the OIDs were returned by
* find_all_inheritors.
*/
foreach(l, inhOIDs)
{
Oid childOID = lfirst_oid(l);
Relation newrelation;
RangeTblEntry *childrte;
Index childRTindex;
/* Open rel if needed; we already have required locks */
if (childOID != parentOID)
newrelation = table_open(childOID, NoLock);
else
newrelation = oldrelation;
/*
* It is possible that the parent table has children that are temp
* tables of other backends. We cannot safely access such tables
* (because of buffering issues), and the best thing to do seems
* to be to silently ignore them.
*/
if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation))
{
table_close(newrelation, lockmode);
continue;
}
/* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */
expand_single_inheritance_child(root, rte, rti, oldrelation,
oldrc, newrelation,
&childrte, &childRTindex);
/* Create the otherrel RelOptInfo too. */
(void) build_simple_rel(root, childRTindex, rel);
/* Close child relations, but keep locks */
if (childOID != parentOID)
table_close(newrelation, NoLock);
}
}
/*
* Some children might require different mark types, which would've been
* reported into oldrc. If so, add relevant entries to the top-level
* targetlist and update parent rel's reltarget. This should match what
* preprocess_targetlist() would have added if the mark types had been
* requested originally.
*/
if (oldrc)
{
int new_allMarkTypes = oldrc->allMarkTypes;
Var *var;
TargetEntry *tle;
char resname[32];
List *newvars = NIL;
/* The old PlanRowMark should already have necessitated adding TID */
Assert(old_allMarkTypes & ~(1 << ROW_MARK_COPY));
/* Add whole-row junk Var if needed, unless we had it already */
if ((new_allMarkTypes & (1 << ROW_MARK_COPY)) &&
!(old_allMarkTypes & (1 << ROW_MARK_COPY)))
{
var = makeWholeRowVar(planner_rt_fetch(oldrc->rti, root),
oldrc->rti,
0,
false);
snprintf(resname, sizeof(resname), "wholerow%u", oldrc->rowmarkId);
tle = makeTargetEntry((Expr *) var,
list_length(root->processed_tlist) + 1,
pstrdup(resname),
true);
root->processed_tlist = lappend(root->processed_tlist, tle);
newvars = lappend(newvars, var);
}
/* Add tableoid junk Var, unless we had it already */
if (!old_isParent)
{
var = makeVar(oldrc->rti,
TableOidAttributeNumber,
OIDOID,
-1,
InvalidOid,
0);
snprintf(resname, sizeof(resname), "tableoid%u", oldrc->rowmarkId);
tle = makeTargetEntry((Expr *) var,
list_length(root->processed_tlist) + 1,
pstrdup(resname),
true);
root->processed_tlist = lappend(root->processed_tlist, tle);
newvars = lappend(newvars, var);
}
/*
* Add the newly added Vars to parent's reltarget. We needn't worry
* about the children's reltargets, they'll be made later.
*/
add_vars_to_targetlist(root, newvars, bms_make_singleton(0), false);
}
table_close(oldrelation, NoLock);
}
/*
* expand_partitioned_rtentry
* Recursively expand an RTE for a partitioned table.
*/
static void
expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
RangeTblEntry *parentrte,
Index parentRTindex, Relation parentrel,
PlanRowMark *top_parentrc, LOCKMODE lockmode)
{
PartitionDesc partdesc;
Bitmapset *live_parts;
int num_live_parts;
int i;
check_stack_depth();
Assert(parentrte->inh);
partdesc = PartitionDirectoryLookup(root->glob->partition_directory,
parentrel);
/* A partitioned table should always have a partition descriptor. */
Assert(partdesc);
/*
* Note down whether any partition key cols are being updated. Though it's
* the root partitioned table's updatedCols we are interested in, we
* instead use parentrte to get the updatedCols. This is convenient
* because parentrte already has the root partrel's updatedCols translated
* to match the attribute ordering of parentrel.
*/
if (!root->partColsUpdated)
root->partColsUpdated =
has_partition_attrs(parentrel, parentrte->updatedCols, NULL);
/*
* There shouldn't be any generated columns in the partition key.
*/
Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL));
/* Nothing further to do here if there are no partitions. */
if (partdesc->nparts == 0)
return;
/*
* Perform partition pruning using restriction clauses assigned to parent
* relation. live_parts will contain PartitionDesc indexes of partitions
* that survive pruning. Below, we will initialize child objects for the
* surviving partitions.
*/
live_parts = prune_append_rel_partitions(relinfo);
/* Expand simple_rel_array and friends to hold child objects. */
num_live_parts = bms_num_members(live_parts);
if (num_live_parts > 0)
expand_planner_arrays(root, num_live_parts);
/*
* We also store partition RelOptInfo pointers in the parent relation.
* Since we're palloc0'ing, slots corresponding to pruned partitions will
* contain NULL.
*/
Assert(relinfo->part_rels == NULL);
relinfo->part_rels = (RelOptInfo **)
palloc0(relinfo->nparts * sizeof(RelOptInfo *));
/*
* Create a child RTE for each live partition. Note that unlike
* traditional inheritance, we don't need a child RTE for the partitioned
* table itself, because it's not going to be scanned.
*/
i = -1;
while ((i = bms_next_member(live_parts, i)) >= 0)
{
Oid childOID = partdesc->oids[i];
Relation childrel;
RangeTblEntry *childrte;
Index childRTindex;
RelOptInfo *childrelinfo;
/* Open rel, acquiring required locks */
childrel = table_open(childOID, lockmode);
/*
* Temporary partitions belonging to other sessions should have been
* disallowed at definition, but for paranoia's sake, let's double
* check.
*/
if (RELATION_IS_OTHER_TEMP(childrel))
elog(ERROR, "temporary relation from another session found as partition");
/* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */
expand_single_inheritance_child(root, parentrte, parentRTindex,
parentrel, top_parentrc, childrel,
&childrte, &childRTindex);
/* Create the otherrel RelOptInfo too. */
childrelinfo = build_simple_rel(root, childRTindex, relinfo);
relinfo->part_rels[i] = childrelinfo;
/* If this child is itself partitioned, recurse */
if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
expand_partitioned_rtentry(root, childrelinfo,
childrte, childRTindex,
childrel, top_parentrc, lockmode);
/* Close child relation, but keep locks */
table_close(childrel, NoLock);
}
}
/*
* expand_single_inheritance_child
* Build a RangeTblEntry and an AppendRelInfo, plus maybe a PlanRowMark.
*
* We now expand the partition hierarchy level by level, creating a
* corresponding hierarchy of AppendRelInfos and RelOptInfos, where each
* partitioned descendant acts as a parent of its immediate partitions.
* (This is a difference from what older versions of PostgreSQL did and what
* is still done in the case of table inheritance for unpartitioned tables,
* where the hierarchy is flattened during RTE expansion.)
*
* PlanRowMarks still carry the top-parent's RTI, and the top-parent's
* allMarkTypes field still accumulates values from all descendents.
*
* "parentrte" and "parentRTindex" are immediate parent's RTE and
* RTI. "top_parentrc" is top parent's PlanRowMark.
*
* The child RangeTblEntry and its RTI are returned in "childrte_p" and
* "childRTindex_p" resp.
*/
static void
expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
Index parentRTindex, Relation parentrel,
PlanRowMark *top_parentrc, Relation childrel,
RangeTblEntry **childrte_p,
Index *childRTindex_p)
{
Query *parse = root->parse;
Oid parentOID = RelationGetRelid(parentrel);
Oid childOID = RelationGetRelid(childrel);
RangeTblEntry *childrte;
Index childRTindex;
AppendRelInfo *appinfo;
/*
* Build an RTE for the child, and attach to query's rangetable list. We
* copy most fields of the parent's RTE, but replace relation OID,
* relkind, and inh for the child. Also, set requiredPerms to zero since
* all required permissions checks are done on the original RTE. Likewise,
* set the child's securityQuals to empty, because we only want to apply
* the parent's RLS conditions regardless of what RLS properties
* individual children may have. (This is an intentional choice to make
* inherited RLS work like regular permissions checks.) The parent
* securityQuals will be propagated to children along with other base
* restriction clauses, so we don't need to do it here.
*/
childrte = copyObject(parentrte);
*childrte_p = childrte;
childrte->relid = childOID;
childrte->relkind = childrel->rd_rel->relkind;
/* A partitioned child will need to be expanded further. */
if (childrte->relkind == RELKIND_PARTITIONED_TABLE)
{
Assert(childOID != parentOID);
childrte->inh = true;
}
else
childrte->inh = false;
childrte->requiredPerms = 0;
childrte->securityQuals = NIL;
parse->rtable = lappend(parse->rtable, childrte);
childRTindex = list_length(parse->rtable);
*childRTindex_p = childRTindex;
/*
* Build an AppendRelInfo struct for each parent/child pair.
*/
appinfo = make_append_rel_info(parentrel, childrel,
parentRTindex, childRTindex);
root->append_rel_list = lappend(root->append_rel_list, appinfo);
/*
* Translate the column permissions bitmaps to the child's attnums (we
* have to build the translated_vars list before we can do this). But if
* this is the parent table, we can leave copyObject's result alone.
*
* Note: we need to do this even though the executor won't run any
* permissions checks on the child RTE. The insertedCols/updatedCols
* bitmaps may be examined for trigger-firing purposes.
*/
if (childOID != parentOID)
{
childrte->selectedCols = translate_col_privs(parentrte->selectedCols,
appinfo->translated_vars);
childrte->insertedCols = translate_col_privs(parentrte->insertedCols,
appinfo->translated_vars);
childrte->updatedCols = translate_col_privs(parentrte->updatedCols,
appinfo->translated_vars);
childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols,
appinfo->translated_vars);
}
/*
* Store the RTE and appinfo in the respective PlannerInfo arrays, which
* the caller must already have allocated space for.
*/
Assert(childRTindex < root->simple_rel_array_size);
Assert(root->simple_rte_array[childRTindex] == NULL);
root->simple_rte_array[childRTindex] = childrte;
Assert(root->append_rel_array[childRTindex] == NULL);
root->append_rel_array[childRTindex] = appinfo;
/*
* Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
*/
if (top_parentrc)
{
PlanRowMark *childrc = makeNode(PlanRowMark);
childrc->rti = childRTindex;
childrc->prti = top_parentrc->rti;
childrc->rowmarkId = top_parentrc->rowmarkId;
/* Reselect rowmark type, because relkind might not match parent */
childrc->markType = select_rowmark_type(childrte,
top_parentrc->strength);
childrc->allMarkTypes = (1 << childrc->markType);
childrc->strength = top_parentrc->strength;
childrc->waitPolicy = top_parentrc->waitPolicy;
/*
* We mark RowMarks for partitioned child tables as parent RowMarks so
* that the executor ignores them (except their existence means that
* the child tables will be locked using the appropriate mode).
*/
childrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE);
/* Include child's rowmark type in top parent's allMarkTypes */
top_parentrc->allMarkTypes |= childrc->allMarkTypes;
root->rowMarks = lappend(root->rowMarks, childrc);
}
}
/*
* translate_col_privs
* Translate a bitmapset representing per-column privileges from the
* parent rel's attribute numbering to the child's.
*
* The only surprise here is that we don't translate a parent whole-row
* reference into a child whole-row reference. That would mean requiring
* permissions on all child columns, which is overly strict, since the
* query is really only going to reference the inherited columns. Instead
* we set the per-column bits for all inherited columns.
*/
static Bitmapset *
translate_col_privs(const Bitmapset *parent_privs,
List *translated_vars)
{
Bitmapset *child_privs = NULL;
bool whole_row;
int attno;
ListCell *lc;
/* System attributes have the same numbers in all tables */
for (attno = FirstLowInvalidHeapAttributeNumber + 1; attno < 0; attno++)
{
if (bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,
parent_privs))
child_privs = bms_add_member(child_privs,
attno - FirstLowInvalidHeapAttributeNumber);
}
/* Check if parent has whole-row reference */
whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber,
parent_privs);
/* And now translate the regular user attributes, using the vars list */
attno = InvalidAttrNumber;
foreach(lc, translated_vars)
{
Var *var = lfirst_node(Var, lc);
attno++;
if (var == NULL) /* ignore dropped columns */
continue;
if (whole_row ||
bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,
parent_privs))
child_privs = bms_add_member(child_privs,
var->varattno - FirstLowInvalidHeapAttributeNumber);
}
return child_privs;
}
/*
* expand_appendrel_subquery
* Add "other rel" RelOptInfos for the children of an appendrel baserel
*
* "rel" is a subquery relation that has the rte->inh flag set, meaning it
* is a UNION ALL subquery that's been flattened into an appendrel, with
* child subqueries listed in root->append_rel_list. We need to build
* a RelOptInfo for each child relation so that we can plan scans on them.
*/
static void
expand_appendrel_subquery(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte, Index rti)
{
ListCell *l;
foreach(l, root->append_rel_list)
{
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
Index childRTindex = appinfo->child_relid;
RangeTblEntry *childrte;
RelOptInfo *childrel;
/* append_rel_list contains all append rels; ignore others */
if (appinfo->parent_relid != rti)
continue;
/* find the child RTE, which should already exist */
Assert(childRTindex < root->simple_rel_array_size);
childrte = root->simple_rte_array[childRTindex];
Assert(childrte != NULL);
/* Build the child RelOptInfo. */
childrel = build_simple_rel(root, childRTindex, rel);
/* Child may itself be an inherited rel, either table or subquery. */
if (childrte->inh)
expand_inherited_rtentry(root, childrel, childrte, childRTindex);
}
}
/*
* apply_child_basequals
* Populate childrel's base restriction quals from parent rel's quals,
* translating them using appinfo.
*
* If any of the resulting clauses evaluate to constant false or NULL, we
* return false and don't apply any quals. Caller should mark the relation as
* a dummy rel in this case, since it doesn't need to be scanned.
*/
bool
apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel,
RelOptInfo *childrel, RangeTblEntry *childRTE,
AppendRelInfo *appinfo)
{
List *childquals;
Index cq_min_security;
ListCell *lc;
/*
* The child rel's targetlist might contain non-Var expressions, which
* means that substitution into the quals could produce opportunities for
* const-simplification, and perhaps even pseudoconstant quals. Therefore,
* transform each RestrictInfo separately to see if it reduces to a
* constant or pseudoconstant. (We must process them separately to keep
* track of the security level of each qual.)
*/
childquals = NIL;
cq_min_security = UINT_MAX;
foreach(lc, parentrel->baserestrictinfo)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
Node *childqual;
ListCell *lc2;
Assert(IsA(rinfo, RestrictInfo));
childqual = adjust_appendrel_attrs(root,
(Node *) rinfo->clause,
1, &appinfo);
childqual = eval_const_expressions(root, childqual);
/* check for flat-out constant */
if (childqual && IsA(childqual, Const))
{
if (((Const *) childqual)->constisnull ||
!DatumGetBool(((Const *) childqual)->constvalue))
{
/* Restriction reduces to constant FALSE or NULL */
return false;
}
/* Restriction reduces to constant TRUE, so drop it */
continue;
}
/* might have gotten an AND clause, if so flatten it */
foreach(lc2, make_ands_implicit((Expr *) childqual))
{
Node *onecq = (Node *) lfirst(lc2);
bool pseudoconstant;
/* check for pseudoconstant (no Vars or volatile functions) */
pseudoconstant =
!contain_vars_of_level(onecq, 0) &&
!contain_volatile_functions(onecq);
if (pseudoconstant)
{
/* tell createplan.c to check for gating quals */
root->hasPseudoConstantQuals = true;
}
/* reconstitute RestrictInfo with appropriate properties */
childquals = lappend(childquals,
make_restrictinfo((Expr *) onecq,
rinfo->is_pushed_down,
rinfo->outerjoin_delayed,
pseudoconstant,
rinfo->security_level,
NULL, NULL, NULL));
/* track minimum security level among child quals */
cq_min_security = Min(cq_min_security, rinfo->security_level);
}
}
/*
* In addition to the quals inherited from the parent, we might have
* securityQuals associated with this particular child node. (Currently
* this can only happen in appendrels originating from UNION ALL;
* inheritance child tables don't have their own securityQuals, see
* expand_single_inheritance_child().) Pull any such securityQuals up
* into the baserestrictinfo for the child. This is similar to
* process_security_barrier_quals() for the parent rel, except that we
* can't make any general deductions from such quals, since they don't
* hold for the whole appendrel.
*/
if (childRTE->securityQuals)
{
Index security_level = 0;
foreach(lc, childRTE->securityQuals)
{
List *qualset = (List *) lfirst(lc);
ListCell *lc2;
foreach(lc2, qualset)
{
Expr *qual = (Expr *) lfirst(lc2);
/* not likely that we'd see constants here, so no check */
childquals = lappend(childquals,
make_restrictinfo(qual,
true, false, false,
security_level,
NULL, NULL, NULL));
cq_min_security = Min(cq_min_security, security_level);
}
security_level++;
}
Assert(security_level <= root->qual_security_level);
}
/*
* OK, we've got all the baserestrictinfo quals for this child.
*/
childrel->baserestrictinfo = childquals;
childrel->baserestrict_min_security = cq_min_security;
return true;
}