/** * @fn Datum get_table_and_inheritors(PG_FUNCTION_ARGS) * @brief Return array containing Oids of parent table and its children. * Note that this function does not release relation locks. * * get_table_and_inheritors(table) * * @param table parent table. * @retval regclass[] */ Datum repack_get_table_and_inheritors(PG_FUNCTION_ARGS) { Oid parent = PG_GETARG_OID(0); List *relations; Datum *relations_array; int relations_array_size; ArrayType *result; ListCell *lc; int i; LockRelationOid(parent, AccessShareLock); /* Check that parent table exists */ if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(parent))) PG_RETURN_ARRAYTYPE_P(construct_empty_array(OIDOID)); /* Also check that children exist */ relations = find_all_inheritors(parent, AccessShareLock, NULL); relations_array_size = list_length(relations); if (relations_array_size == 0) PG_RETURN_ARRAYTYPE_P(construct_empty_array(OIDOID)); relations_array = palloc(relations_array_size * sizeof(Datum)); i = 0; foreach (lc, relations) relations_array[i++] = ObjectIdGetDatum(lfirst_oid(lc)); result = construct_array(relations_array, relations_array_size, OIDOID, sizeof(Oid), true, 'i'); pfree(relations_array); PG_RETURN_ARRAYTYPE_P(result); }
void LockSegfilesOnMaster(Relation rel, int32 segno) { List *children = NIL; ListCell *lc; children = find_all_inheritors(rel->rd_id); foreach(lc, children) { Relation child_rel = rel; Oid oid = lfirst_oid(lc); if (rel_is_partitioned(oid)) continue; /* open/close the child relation. */ if (oid != rel->rd_id) child_rel = heap_open(oid, AccessShareLock); LockSegfilesOnMasterForSingleRel(child_rel, segno); if (oid != rel->rd_id) heap_close(child_rel, AccessShareLock); }
/* * sepgsql_dml_privileges * * Entrypoint of the DML permission checks */ bool sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) { ListCell *lr; foreach(lr, rangeTabls) { RangeTblEntry *rte = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; /* * Only regular relations shall be checked */ if (rte->rtekind != RTE_RELATION) continue; /* * Find out required permissions */ if (rte->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; if (rte->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; if (rte->requiredPerms & ACL_UPDATE) { if (!bms_is_empty(rte->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } if (rte->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* * Skip, if nothing to be checked */ if (required == 0) continue; /* * If this RangeTblEntry is also supposed to reference inherited * tables, we need to check security label of the child tables. So, we * expand rte->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ if (!rte->inh) tableIds = list_make1_oid(rte->relid); else tableIds = find_all_inheritors(rte->relid, NoLock, NULL); foreach(li, tableIds) { Oid tableOid = lfirst_oid(li); Bitmapset *selectedCols; Bitmapset *insertedCols; Bitmapset *updatedCols; /* * child table has different attribute numbers, so we need to fix * up them. */ selectedCols = fixup_inherited_columns(rte->relid, tableOid, rte->selectedCols); insertedCols = fixup_inherited_columns(rte->relid, tableOid, rte->insertedCols); updatedCols = fixup_inherited_columns(rte->relid, tableOid, rte->updatedCols); /* * check permissions on individual tables */ if (!check_relation_privileges(tableOid, selectedCols, insertedCols, updatedCols, required, abort_on_violation)) return false; }
/* * 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. */ oldrelation = heap_open(parentOID, NoLock); /* 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) newrelation = heap_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)) { heap_close(newrelation, lockmode); continue; } expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc, newrelation, &appinfos, &childrte, &childRTindex); /* Close child relations, but keep locks */ if (childOID != parentOID) heap_close(newrelation, NoLock); } /* * 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); }