/* * find_all_inheritors - * Returns a list of relation OIDs including the given rel plus * all relations that inherit from it, directly or indirectly. * Optionally, it also returns the number of parents found for * each such relation within the inheritance tree rooted at the * given rel. * * The specified lock type is acquired on all child relations (but not on the * given rel; caller should already have locked it). If lockmode is NoLock * then no locks are acquired, but caller must beware of race conditions * against possible DROPs of child relations. */ List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents) { List *rels_list, *rel_numparents; ListCell *l; /* * We build a list starting with the given rel and adding all direct and * indirect children. We can use a single list as both the record of * already-found rels and the agenda of rels yet to be scanned for more * children. This is a bit tricky but works because the foreach() macro * doesn't fetch the next list element until the bottom of the loop. */ rels_list = list_make1_oid(parentrelId); rel_numparents = list_make1_int(0); foreach(l, rels_list) { Oid currentrel = lfirst_oid(l); List *currentchildren; ListCell *lc; /* Get the direct children of this rel */ currentchildren = find_inheritance_children(currentrel, lockmode); /* * Add to the queue only those children not already seen. This avoids * making duplicate entries in case of multiple inheritance paths from * the same parent. (It'll also keep us from getting into an infinite * loop, though theoretically there can't be any cycles in the * inheritance graph anyway.) */ foreach(lc, currentchildren) { Oid child_oid = lfirst_oid(lc); bool found = false; ListCell *lo; ListCell *li; /* if the rel is already there, bump number-of-parents counter */ forboth(lo, rels_list, li, rel_numparents) { if (lfirst_oid(lo) == child_oid) { lfirst_int(li)++; found = true; break; } } /* if it's not there, add it. expect 1 parent, initially. */ if (!found) { rels_list = lappend_oid(rels_list, child_oid); rel_numparents = lappend_int(rel_numparents, 1); } }
/* * Create a TsmRoutine descriptor for the SYSTEM method. */ Datum tsm_system_handler(PG_FUNCTION_ARGS) { TsmRoutine *tsm = makeNode(TsmRoutine); tsm->parameterTypes = list_make1_oid(FLOAT4OID); tsm->repeatable_across_queries = true; tsm->repeatable_across_scans = true; tsm->SampleScanGetSampleSize = system_samplescangetsamplesize; tsm->InitSampleScan = system_initsamplescan; tsm->BeginSampleScan = system_beginsamplescan; tsm->NextSampleBlock = system_nextsampleblock; tsm->NextSampleTuple = system_nextsampletuple; tsm->EndSampleScan = NULL; PG_RETURN_POINTER(tsm); }
/* * find_all_inheritors - * Returns a list of relation OIDs including the given rel plus * all relations that inherit from it, directly or indirectly. * Optionally, it also returns the number of parents found for * each such relation within the inheritance tree rooted at the * given rel. * * The specified lock type is acquired on all child relations (but not on the * given rel; caller should already have locked it). If lockmode is NoLock * then no locks are acquired, but caller must beware of race conditions * against possible DROPs of child relations. */ List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents) { /* hash table for O(1) rel_oid -> rel_numparents cell lookup */ HTAB *seen_rels; HASHCTL ctl; List *rels_list, *rel_numparents; ListCell *l; memset(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(Oid); ctl.entrysize = sizeof(SeenRelsEntry); ctl.hcxt = CurrentMemoryContext; seen_rels = hash_create("find_all_inheritors temporary table", 32, /* start small and extend */ &ctl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT); /* * We build a list starting with the given rel and adding all direct and * indirect children. We can use a single list as both the record of * already-found rels and the agenda of rels yet to be scanned for more * children. This is a bit tricky but works because the foreach() macro * doesn't fetch the next list element until the bottom of the loop. */ rels_list = list_make1_oid(parentrelId); rel_numparents = list_make1_int(0); foreach(l, rels_list) { Oid currentrel = lfirst_oid(l); List *currentchildren; ListCell *lc; /* Get the direct children of this rel */ currentchildren = find_inheritance_children(currentrel, lockmode); /* * Add to the queue only those children not already seen. This avoids * making duplicate entries in case of multiple inheritance paths from * the same parent. (It'll also keep us from getting into an infinite * loop, though theoretically there can't be any cycles in the * inheritance graph anyway.) */ foreach(lc, currentchildren) { Oid child_oid = lfirst_oid(lc); bool found; SeenRelsEntry *hash_entry; hash_entry = hash_search(seen_rels, &child_oid, HASH_ENTER, &found); if (found) { /* if the rel is already there, bump number-of-parents counter */ lfirst_int(hash_entry->numparents_cell)++; } else { /* if it's not there, add it. expect 1 parent, initially. */ rels_list = lappend_oid(rels_list, child_oid); rel_numparents = lappend_int(rel_numparents, 1); hash_entry->numparents_cell = rel_numparents->tail; } }
/* * 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; }
/* * write_auth_file: update the flat auth file */ static void write_auth_file(Relation rel_authid, Relation rel_authmem) { StringInfoData buffer; BlockNumber totalblocks; HeapScanDesc scan; HeapTuple tuple; int curr_role = 0; int total_roles = 0; int curr_mem = 0; int total_mem = 0; int est_rows; auth_entry *auth_info; authmem_entry *authmem_info; MirroredFlatFileOpen mirroredOpen; initStringInfo(&buffer); load_auth_entries(rel_authid, &auth_info, &total_roles); /* * Read pg_auth_members into temporary data structure, too */ totalblocks = RelationGetNumberOfBlocks(rel_authmem); totalblocks = totalblocks ? totalblocks : 1; est_rows = totalblocks * (BLCKSZ / (sizeof(HeapTupleHeaderData) + sizeof(FormData_pg_auth_members))); authmem_info = (authmem_entry *) palloc(est_rows * sizeof(authmem_entry)); scan = heap_beginscan(rel_authmem, SnapshotNow, 0, NULL); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Form_pg_auth_members memform = (Form_pg_auth_members) GETSTRUCT(tuple); if (curr_mem >= est_rows) { est_rows *= 2; authmem_info = (authmem_entry *) repalloc(authmem_info, est_rows * sizeof(authmem_entry)); } authmem_info[curr_mem].roleid = memform->roleid; authmem_info[curr_mem].memberid = memform->member; curr_mem++; total_mem++; } heap_endscan(scan); /* * Search for memberships. We can skip all this if pg_auth_members is * empty. */ if (total_mem > 0) { /* * Sort auth_info by roleid and authmem_info by memberid. */ qsort(auth_info, total_roles, sizeof(auth_entry), oid_compar); qsort(authmem_info, total_mem, sizeof(authmem_entry), mem_compar); /* * For each role, find what it belongs to. */ for (curr_role = 0; curr_role < total_roles; curr_role++) { List *roles_list; List *roles_names_list = NIL; ListCell *mem; /* We can skip this for non-login roles */ if (!auth_info[curr_role].rolcanlogin) continue; /* * This search algorithm is the same as in is_member_of_role; we * are just working with a different input data structure. */ roles_list = list_make1_oid(auth_info[curr_role].roleid); foreach(mem, roles_list) { authmem_entry key; authmem_entry *found_mem; int first_found, last_found, i; key.memberid = lfirst_oid(mem); found_mem = bsearch(&key, authmem_info, total_mem, sizeof(authmem_entry), mem_compar); if (!found_mem) continue; /* * bsearch found a match for us; but if there were multiple * matches it could have found any one of them. Locate first * and last match. */ first_found = last_found = (found_mem - authmem_info); while (first_found > 0 && mem_compar(&key, &authmem_info[first_found - 1]) == 0) first_found--; while (last_found + 1 < total_mem && mem_compar(&key, &authmem_info[last_found + 1]) == 0) last_found++; /* * Now add all the new roles to roles_list. */ for (i = first_found; i <= last_found; i++) roles_list = list_append_unique_oid(roles_list, authmem_info[i].roleid); } /* * Convert list of role Oids to list of role names. We must do * this before re-sorting auth_info. * * We skip the first list element (curr_role itself) since there * is no point in writing that a role is a member of itself. */ for_each_cell(mem, lnext(list_head(roles_list))) { auth_entry key_auth; auth_entry *found_role; key_auth.roleid = lfirst_oid(mem); found_role = bsearch(&key_auth, auth_info, total_roles, sizeof(auth_entry), oid_compar); if (found_role) /* paranoia */ roles_names_list = lappend(roles_names_list, found_role->rolname); }