diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c index 4b4e469493..d3f8e8f06c 100644 --- a/src/backend/commands/policy.c +++ b/src/backend/commands/policy.c @@ -187,159 +187,139 @@ policy_role_list_to_array(List *roles, int *num_roles) /* * Load row security policy from the catalog, and store it in * the relation's relcache entry. + * + * Note that caller should have verified that pg_class.relrowsecurity + * is true for this relation. */ void RelationBuildRowSecurity(Relation relation) { MemoryContext rscxt; MemoryContext oldcxt = CurrentMemoryContext; - RowSecurityDesc *volatile rsdesc = NULL; + RowSecurityDesc *rsdesc; + Relation catalog; + ScanKeyData skey; + SysScanDesc sscan; + HeapTuple tuple; /* * Create a memory context to hold everything associated with this * relation's row security policy. This makes it easy to clean up during - * a relcache flush. + * a relcache flush. However, to cover the possibility of an error + * partway through, we don't make the context long-lived till we're done. */ - rscxt = AllocSetContextCreate(CacheMemoryContext, + rscxt = AllocSetContextCreate(CurrentMemoryContext, "row security descriptor", ALLOCSET_SMALL_SIZES); + MemoryContextCopyAndSetIdentifier(rscxt, + RelationGetRelationName(relation)); + + rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc)); + rsdesc->rscxt = rscxt; /* - * Since rscxt lives under CacheMemoryContext, it is long-lived. Use a - * PG_TRY block to ensure it'll get freed if we fail partway through. + * Now scan pg_policy for RLS policies associated with this relation. + * Because we use the index on (polrelid, polname), we should consistently + * visit the rel's policies in name order, at least when system indexes + * aren't disabled. This simplifies equalRSDesc(). */ - PG_TRY(); + catalog = table_open(PolicyRelationId, AccessShareLock); + + ScanKeyInit(&skey, + Anum_pg_policy_polrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(RelationGetRelid(relation))); + + sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true, + NULL, 1, &skey); + + while (HeapTupleIsValid(tuple = systable_getnext(sscan))) { - Relation catalog; - ScanKeyData skey; - SysScanDesc sscan; - HeapTuple tuple; + Form_pg_policy policy_form = (Form_pg_policy) GETSTRUCT(tuple); + RowSecurityPolicy *policy; + Datum datum; + bool isnull; + char *str_value; - MemoryContextCopyAndSetIdentifier(rscxt, - RelationGetRelationName(relation)); - - rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc)); - rsdesc->rscxt = rscxt; - - catalog = table_open(PolicyRelationId, AccessShareLock); - - ScanKeyInit(&skey, - Anum_pg_policy_polrelid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(RelationGetRelid(relation))); - - sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true, - NULL, 1, &skey); + policy = MemoryContextAllocZero(rscxt, sizeof(RowSecurityPolicy)); /* - * Loop through the row level security policies for this relation, if - * any. + * Note: we must be sure that pass-by-reference data gets copied into + * rscxt. We avoid making that context current over wider spans than + * we have to, though. */ - while (HeapTupleIsValid(tuple = systable_getnext(sscan))) - { - Datum value_datum; - char cmd_value; - bool permissive_value; - Datum roles_datum; - char *qual_value; - Expr *qual_expr; - char *with_check_value; - Expr *with_check_qual; - char *policy_name_value; - bool isnull; - RowSecurityPolicy *policy; - /* - * Note: all the pass-by-reference data we collect here is either - * still stored in the tuple, or constructed in the caller's - * short-lived memory context. We must copy it into rscxt - * explicitly below. - */ + /* Get policy command */ + policy->polcmd = policy_form->polcmd; - /* Get policy command */ - value_datum = heap_getattr(tuple, Anum_pg_policy_polcmd, - RelationGetDescr(catalog), &isnull); - Assert(!isnull); - cmd_value = DatumGetChar(value_datum); + /* Get policy, permissive or restrictive */ + policy->permissive = policy_form->polpermissive; - /* Get policy permissive or restrictive */ - value_datum = heap_getattr(tuple, Anum_pg_policy_polpermissive, - RelationGetDescr(catalog), &isnull); - Assert(!isnull); - permissive_value = DatumGetBool(value_datum); + /* Get policy name */ + policy->policy_name = + MemoryContextStrdup(rscxt, NameStr(policy_form->polname)); - /* Get policy name */ - value_datum = heap_getattr(tuple, Anum_pg_policy_polname, - RelationGetDescr(catalog), &isnull); - Assert(!isnull); - policy_name_value = NameStr(*(DatumGetName(value_datum))); - - /* Get policy roles */ - roles_datum = heap_getattr(tuple, Anum_pg_policy_polroles, - RelationGetDescr(catalog), &isnull); - /* shouldn't be null, but initdb doesn't mark it so, so check */ - if (isnull) - elog(ERROR, "unexpected null value in pg_policy.polroles"); - - /* Get policy qual */ - value_datum = heap_getattr(tuple, Anum_pg_policy_polqual, - RelationGetDescr(catalog), &isnull); - if (!isnull) - { - qual_value = TextDatumGetCString(value_datum); - qual_expr = (Expr *) stringToNode(qual_value); - } - else - qual_expr = NULL; - - /* Get WITH CHECK qual */ - value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck, - RelationGetDescr(catalog), &isnull); - if (!isnull) - { - with_check_value = TextDatumGetCString(value_datum); - with_check_qual = (Expr *) stringToNode(with_check_value); - } - else - with_check_qual = NULL; - - /* Now copy everything into the cache context */ - MemoryContextSwitchTo(rscxt); - - policy = palloc0(sizeof(RowSecurityPolicy)); - policy->policy_name = pstrdup(policy_name_value); - policy->polcmd = cmd_value; - policy->permissive = permissive_value; - policy->roles = DatumGetArrayTypePCopy(roles_datum); - policy->qual = copyObject(qual_expr); - policy->with_check_qual = copyObject(with_check_qual); - policy->hassublinks = checkExprHasSubLink((Node *) qual_expr) || - checkExprHasSubLink((Node *) with_check_qual); - - rsdesc->policies = lcons(policy, rsdesc->policies); - - MemoryContextSwitchTo(oldcxt); - - /* clean up some (not all) of the junk ... */ - if (qual_expr != NULL) - pfree(qual_expr); - if (with_check_qual != NULL) - pfree(with_check_qual); - } - - systable_endscan(sscan); - table_close(catalog, AccessShareLock); - } - PG_CATCH(); - { - /* Delete rscxt, first making sure it isn't active */ + /* Get policy roles */ + datum = heap_getattr(tuple, Anum_pg_policy_polroles, + RelationGetDescr(catalog), &isnull); + /* shouldn't be null, but let's check for luck */ + if (isnull) + elog(ERROR, "unexpected null value in pg_policy.polroles"); + MemoryContextSwitchTo(rscxt); + policy->roles = DatumGetArrayTypePCopy(datum); MemoryContextSwitchTo(oldcxt); - MemoryContextDelete(rscxt); - PG_RE_THROW(); - } - PG_END_TRY(); - /* Success --- attach the policy descriptor to the relcache entry */ + /* Get policy qual */ + datum = heap_getattr(tuple, Anum_pg_policy_polqual, + RelationGetDescr(catalog), &isnull); + if (!isnull) + { + str_value = TextDatumGetCString(datum); + MemoryContextSwitchTo(rscxt); + policy->qual = (Expr *) stringToNode(str_value); + MemoryContextSwitchTo(oldcxt); + pfree(str_value); + } + else + policy->qual = NULL; + + /* Get WITH CHECK qual */ + datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck, + RelationGetDescr(catalog), &isnull); + if (!isnull) + { + str_value = TextDatumGetCString(datum); + MemoryContextSwitchTo(rscxt); + policy->with_check_qual = (Expr *) stringToNode(str_value); + MemoryContextSwitchTo(oldcxt); + pfree(str_value); + } + else + policy->with_check_qual = NULL; + + /* We want to cache whether there are SubLinks in these expressions */ + policy->hassublinks = checkExprHasSubLink((Node *) policy->qual) || + checkExprHasSubLink((Node *) policy->with_check_qual); + + /* + * Add this object to list. For historical reasons, the list is built + * in reverse order. + */ + MemoryContextSwitchTo(rscxt); + rsdesc->policies = lcons(policy, rsdesc->policies); + MemoryContextSwitchTo(oldcxt); + } + + systable_endscan(sscan); + table_close(catalog, AccessShareLock); + + /* + * Success. Reparent the descriptor's memory context under + * CacheMemoryContext so that it will live indefinitely, then attach the + * policy descriptor to the relcache entry. + */ + MemoryContextSetParent(rscxt, CacheMemoryContext); + relation->rd_rsdesc = rsdesc; }