2019-01-10 18:54:31 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* 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"
|
|
|
|
|
2019-01-16 20:27:44 +01:00
|
|
|
#include "access/sysattr.h"
|
2019-01-21 19:18:20 +01:00
|
|
|
#include "access/table.h"
|
2019-01-10 18:54:31 +01:00
|
|
|
#include "catalog/partition.h"
|
|
|
|
#include "catalog/pg_inherits.h"
|
|
|
|
#include "miscadmin.h"
|
|
|
|
#include "optimizer/appendinfo.h"
|
|
|
|
#include "optimizer/inherit.h"
|
|
|
|
#include "optimizer/planner.h"
|
|
|
|
#include "optimizer/prep.h"
|
|
|
|
#include "utils/rel.h"
|
|
|
|
|
|
|
|
|
|
|
|
static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte,
|
|
|
|
Index rti);
|
|
|
|
static void expand_partitioned_rtentry(PlannerInfo *root,
|
|
|
|
RangeTblEntry *parentrte,
|
|
|
|
Index parentRTindex, Relation parentrel,
|
|
|
|
PlanRowMark *top_parentrc, LOCKMODE lockmode,
|
|
|
|
List **appinfos);
|
|
|
|
static void expand_single_inheritance_child(PlannerInfo *root,
|
|
|
|
RangeTblEntry *parentrte,
|
|
|
|
Index parentRTindex, Relation parentrel,
|
|
|
|
PlanRowMark *top_parentrc, Relation childrel,
|
|
|
|
List **appinfos, RangeTblEntry **childrte_p,
|
|
|
|
Index *childRTindex_p);
|
2019-01-16 20:27:44 +01:00
|
|
|
static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
|
|
|
|
List *translated_vars);
|
2019-01-10 18:54:31 +01:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* expand_inherited_tables
|
|
|
|
* Expand each rangetable entry that represents an inheritance set
|
|
|
|
* into an "append relation". At the conclusion of this process,
|
|
|
|
* the "inh" flag is set in all and only those RTEs that are append
|
|
|
|
* relation parents.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
expand_inherited_tables(PlannerInfo *root)
|
|
|
|
{
|
|
|
|
Index nrtes;
|
|
|
|
Index rti;
|
|
|
|
ListCell *rl;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* expand_inherited_rtentry may add RTEs to parse->rtable. The function is
|
|
|
|
* expected to recursively handle any RTEs that it creates with inh=true.
|
|
|
|
* So just scan as far as the original end of the rtable list.
|
|
|
|
*/
|
|
|
|
nrtes = list_length(root->parse->rtable);
|
|
|
|
rl = list_head(root->parse->rtable);
|
|
|
|
for (rti = 1; rti <= nrtes; rti++)
|
|
|
|
{
|
|
|
|
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl);
|
|
|
|
|
|
|
|
expand_inherited_rtentry(root, rte, rti);
|
|
|
|
rl = lnext(rl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* expand_inherited_rtentry
|
|
|
|
* Check whether a rangetable entry represents an inheritance set.
|
|
|
|
* If so, add entries for all the child tables to the query's
|
|
|
|
* rangetable, and build AppendRelInfo nodes for all the child tables
|
|
|
|
* and add them to root->append_rel_list. If not, clear the entry's
|
|
|
|
* "inh" flag to prevent later code from looking for AppendRelInfos.
|
|
|
|
*
|
|
|
|
* Note that the original RTE is considered to represent the whole
|
|
|
|
* inheritance set. 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.
|
|
|
|
*
|
|
|
|
* A childless table is never considered to be an inheritance set. For
|
|
|
|
* regular inheritance, a parent RTE must always have at least two associated
|
|
|
|
* AppendRelInfos: one corresponding to the parent table as a simple member of
|
|
|
|
* inheritance set and one or more corresponding to the actual children.
|
|
|
|
* Since a partitioned table is not scanned, it might have only one associated
|
|
|
|
* AppendRelInfo.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
|
|
|
|
{
|
|
|
|
Oid parentOID;
|
|
|
|
PlanRowMark *oldrc;
|
|
|
|
Relation oldrelation;
|
|
|
|
LOCKMODE lockmode;
|
|
|
|
List *inhOIDs;
|
|
|
|
ListCell *l;
|
|
|
|
|
|
|
|
/* Does RT entry allow inheritance? */
|
|
|
|
if (!rte->inh)
|
|
|
|
return;
|
|
|
|
/* Ignore any already-expanded UNION ALL nodes */
|
|
|
|
if (rte->rtekind != RTE_RELATION)
|
|
|
|
{
|
|
|
|
Assert(rte->rtekind == RTE_SUBQUERY);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Fast path for common case of childless table */
|
|
|
|
parentOID = rte->relid;
|
|
|
|
if (!has_subclass(parentOID))
|
|
|
|
{
|
|
|
|
/* Clear flag before returning */
|
|
|
|
rte->inh = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The rewriter should already have obtained an appropriate lock on each
|
|
|
|
* relation named in the query. 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.
|
|
|
|
*/
|
|
|
|
lockmode = rte->rellockmode;
|
|
|
|
|
|
|
|
/* Scan for all members of inheritance set, acquire needed locks */
|
|
|
|
inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check that there's at least one descendant, else treat as no-child
|
|
|
|
* case. This could happen despite above has_subclass() check, if table
|
|
|
|
* once had a child but no longer does.
|
|
|
|
*/
|
|
|
|
if (list_length(inhOIDs) < 2)
|
|
|
|
{
|
|
|
|
/* Clear flag before returning */
|
|
|
|
rte->inh = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
oldrc->isParent = true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Must open the parent relation to examine its tupdesc. We need not lock
|
|
|
|
* it; we assume the rewriter already did.
|
|
|
|
*/
|
2019-01-21 19:32:19 +01:00
|
|
|
oldrelation = table_open(parentOID, NoLock);
|
2019-01-10 18:54:31 +01:00
|
|
|
|
|
|
|
/* Scan the inheritance set and expand it */
|
|
|
|
if (RelationGetPartitionDesc(oldrelation) != NULL)
|
|
|
|
{
|
|
|
|
Assert(rte->relkind == RELKIND_PARTITIONED_TABLE);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this table has partitions, recursively expand them in the order
|
|
|
|
* in which they appear in the PartitionDesc. While at it, also
|
|
|
|
* extract the partition key columns of all the partitioned tables.
|
|
|
|
*/
|
|
|
|
expand_partitioned_rtentry(root, rte, rti, oldrelation, oldrc,
|
|
|
|
lockmode, &root->append_rel_list);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
List *appinfos = NIL;
|
|
|
|
RangeTblEntry *childrte;
|
|
|
|
Index childRTindex;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This table has no partitions. Expand any plain inheritance
|
|
|
|
* children in the order the OIDs were returned by
|
|
|
|
* find_all_inheritors.
|
|
|
|
*/
|
|
|
|
foreach(l, inhOIDs)
|
|
|
|
{
|
|
|
|
Oid childOID = lfirst_oid(l);
|
|
|
|
Relation newrelation;
|
|
|
|
|
|
|
|
/* Open rel if needed; we already have required locks */
|
|
|
|
if (childOID != parentOID)
|
2019-01-21 19:32:19 +01:00
|
|
|
newrelation = table_open(childOID, NoLock);
|
2019-01-10 18:54:31 +01:00
|
|
|
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))
|
|
|
|
{
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(newrelation, lockmode);
|
2019-01-10 18:54:31 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc,
|
|
|
|
newrelation,
|
|
|
|
&appinfos, &childrte,
|
|
|
|
&childRTindex);
|
|
|
|
|
|
|
|
/* Close child relations, but keep locks */
|
|
|
|
if (childOID != parentOID)
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(newrelation, NoLock);
|
2019-01-10 18:54:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If all the children were temp tables, pretend it's a
|
|
|
|
* non-inheritance situation; we don't need Append node in that case.
|
|
|
|
* The duplicate RTE we added for the parent table is harmless, so we
|
|
|
|
* don't bother to get rid of it; ditto for the useless PlanRowMark
|
|
|
|
* node.
|
|
|
|
*/
|
|
|
|
if (list_length(appinfos) < 2)
|
|
|
|
rte->inh = false;
|
|
|
|
else
|
|
|
|
root->append_rel_list = list_concat(root->append_rel_list,
|
|
|
|
appinfos);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(oldrelation, NoLock);
|
2019-01-10 18:54:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* expand_partitioned_rtentry
|
|
|
|
* Recursively expand an RTE for a partitioned table.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
|
|
|
|
Index parentRTindex, Relation parentrel,
|
|
|
|
PlanRowMark *top_parentrc, LOCKMODE lockmode,
|
|
|
|
List **appinfos)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
RangeTblEntry *childrte;
|
|
|
|
Index childRTindex;
|
|
|
|
PartitionDesc partdesc = RelationGetPartitionDesc(parentrel);
|
|
|
|
|
|
|
|
check_stack_depth();
|
|
|
|
|
|
|
|
/* A partitioned table should always have a partition descriptor. */
|
|
|
|
Assert(partdesc);
|
|
|
|
|
|
|
|
Assert(parentrte->inh);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
|
|
|
/* First expand the partitioned table itself. */
|
|
|
|
expand_single_inheritance_child(root, parentrte, parentRTindex, parentrel,
|
|
|
|
top_parentrc, parentrel,
|
|
|
|
appinfos, &childrte, &childRTindex);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the partitioned table has no partitions, treat this as the
|
|
|
|
* non-inheritance case.
|
|
|
|
*/
|
|
|
|
if (partdesc->nparts == 0)
|
|
|
|
{
|
|
|
|
parentrte->inh = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < partdesc->nparts; i++)
|
|
|
|
{
|
|
|
|
Oid childOID = partdesc->oids[i];
|
|
|
|
Relation childrel;
|
|
|
|
|
|
|
|
/* Open rel; we already have required locks */
|
2019-01-21 19:32:19 +01:00
|
|
|
childrel = table_open(childOID, NoLock);
|
2019-01-10 18:54:31 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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");
|
|
|
|
|
|
|
|
expand_single_inheritance_child(root, parentrte, parentRTindex,
|
|
|
|
parentrel, top_parentrc, childrel,
|
|
|
|
appinfos, &childrte, &childRTindex);
|
|
|
|
|
|
|
|
/* If this child is itself partitioned, recurse */
|
|
|
|
if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
|
|
|
|
expand_partitioned_rtentry(root, childrte, childRTindex,
|
|
|
|
childrel, top_parentrc, lockmode,
|
|
|
|
appinfos);
|
|
|
|
|
|
|
|
/* Close child relation, but keep locks */
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(childrel, NoLock);
|
2019-01-10 18:54:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* expand_single_inheritance_child
|
|
|
|
* Build a RangeTblEntry and an AppendRelInfo, if appropriate, 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,
|
|
|
|
List **appinfos, 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 and
|
|
|
|
* relkind, and set inh = false. 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 (childOID != parentOID &&
|
|
|
|
childrte->relkind == RELKIND_PARTITIONED_TABLE)
|
|
|
|
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;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We need an AppendRelInfo if paths will be built for the child RTE. If
|
|
|
|
* childrte->inh is true, then we'll always need to generate append paths
|
|
|
|
* for it. If childrte->inh is false, we must scan it if it's not a
|
|
|
|
* partitioned table; but if it is a partitioned table, then it never has
|
|
|
|
* any data of its own and need not be scanned.
|
|
|
|
*/
|
|
|
|
if (childrte->relkind != RELKIND_PARTITIONED_TABLE || childrte->inh)
|
|
|
|
{
|
|
|
|
appinfo = make_append_rel_info(parentrel, childrel,
|
|
|
|
parentRTindex, childRTindex);
|
|
|
|
*appinfos = lappend(*appinfos, 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, 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 be locked using 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);
|
|
|
|
}
|
|
|
|
}
|
2019-01-16 20:27:44 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|