1 /*-------------------------------------------------------------------------
4 * Commands for manipulating policies.
6 * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
9 * src/backend/commands/policy.c
11 *-------------------------------------------------------------------------
15 #include "access/genam.h"
16 #include "access/heapam.h"
17 #include "access/htup.h"
18 #include "access/htup_details.h"
19 #include "access/sysattr.h"
20 #include "catalog/catalog.h"
21 #include "catalog/dependency.h"
22 #include "catalog/indexing.h"
23 #include "catalog/namespace.h"
24 #include "catalog/objectaccess.h"
25 #include "catalog/pg_authid.h"
26 #include "catalog/pg_policy.h"
27 #include "catalog/pg_type.h"
28 #include "commands/policy.h"
29 #include "miscadmin.h"
30 #include "nodes/makefuncs.h"
31 #include "nodes/pg_list.h"
32 #include "parser/parse_clause.h"
33 #include "parser/parse_collate.h"
34 #include "parser/parse_node.h"
35 #include "parser/parse_relation.h"
36 #include "rewrite/rewriteManip.h"
37 #include "rewrite/rowsecurity.h"
38 #include "storage/lock.h"
39 #include "utils/acl.h"
40 #include "utils/array.h"
41 #include "utils/builtins.h"
42 #include "utils/fmgroids.h"
43 #include "utils/inval.h"
44 #include "utils/lsyscache.h"
45 #include "utils/memutils.h"
46 #include "utils/rel.h"
47 #include "utils/syscache.h"
49 static void RangeVarCallbackForPolicy(const RangeVar *rv,
50 Oid relid, Oid oldrelid, void *arg);
51 static char parse_policy_command(const char *cmd_name);
52 static Datum *policy_role_list_to_array(List *roles, int *num_roles);
55 * Callback to RangeVarGetRelidExtended().
57 * Checks the following:
58 * - the relation specified is a table.
59 * - current user owns the table.
60 * - the table is not a system table.
62 * If any of these checks fails then an error is raised.
65 RangeVarCallbackForPolicy(const RangeVar *rv, Oid relid, Oid oldrelid,
69 Form_pg_class classform;
72 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
73 if (!HeapTupleIsValid(tuple))
76 classform = (Form_pg_class) GETSTRUCT(tuple);
77 relkind = classform->relkind;
79 /* Must own relation. */
80 if (!pg_class_ownercheck(relid, GetUserId()))
81 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, rv->relname);
83 /* No system table modifications unless explicitly allowed. */
84 if (!allowSystemTableMods && IsSystemClass(relid, classform))
86 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
87 errmsg("permission denied: \"%s\" is a system catalog",
90 /* Relation type MUST be a table. */
91 if (relkind != RELKIND_RELATION)
93 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
94 errmsg("\"%s\" is not a table", rv->relname)));
96 ReleaseSysCache(tuple);
100 * parse_policy_command -
101 * helper function to convert full command strings to their char
104 * cmd_name - full string command name. Valid values are 'all', 'select',
105 * 'insert', 'update' and 'delete'.
109 parse_policy_command(const char *cmd_name)
114 elog(ERROR, "unrecognized policy command");
116 if (strcmp(cmd_name, "all") == 0)
118 else if (strcmp(cmd_name, "select") == 0)
119 polcmd = ACL_SELECT_CHR;
120 else if (strcmp(cmd_name, "insert") == 0)
121 polcmd = ACL_INSERT_CHR;
122 else if (strcmp(cmd_name, "update") == 0)
123 polcmd = ACL_UPDATE_CHR;
124 else if (strcmp(cmd_name, "delete") == 0)
125 polcmd = ACL_DELETE_CHR;
127 elog(ERROR, "unrecognized policy command");
133 * policy_role_list_to_array
134 * helper function to convert a list of RoleSpecs to an array of
138 policy_role_list_to_array(List *roles, int *num_roles)
144 /* Handle no roles being passed in as being for public */
148 role_oids = (Datum *) palloc(*num_roles * sizeof(Datum));
149 role_oids[0] = ObjectIdGetDatum(ACL_ID_PUBLIC);
154 *num_roles = list_length(roles);
155 role_oids = (Datum *) palloc(*num_roles * sizeof(Datum));
159 RoleSpec *spec = lfirst(cell);
162 * PUBLIC covers all roles, so it only makes sense alone.
164 if (spec->roletype == ROLESPEC_PUBLIC)
169 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
170 errmsg("ignoring specified roles other than PUBLIC"),
171 errhint("All roles are members of the PUBLIC role.")));
174 role_oids[0] = ObjectIdGetDatum(ACL_ID_PUBLIC);
180 ObjectIdGetDatum(get_rolespec_oid((Node *) spec, false));
187 * Load row security policy from the catalog, and store it in
188 * the relation's relcache entry.
191 RelationBuildRowSecurity(Relation relation)
194 MemoryContext oldcxt = CurrentMemoryContext;
195 RowSecurityDesc *volatile rsdesc = NULL;
198 * Create a memory context to hold everything associated with this
199 * relation's row security policy. This makes it easy to clean up during
202 rscxt = AllocSetContextCreate(CacheMemoryContext,
203 "row security descriptor",
204 ALLOCSET_SMALL_MINSIZE,
205 ALLOCSET_SMALL_INITSIZE,
206 ALLOCSET_SMALL_MAXSIZE);
209 * Since rscxt lives under CacheMemoryContext, it is long-lived. Use a
210 * PG_TRY block to ensure it'll get freed if we fail partway through.
219 rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc));
220 rsdesc->rscxt = rscxt;
222 catalog = heap_open(PolicyRelationId, AccessShareLock);
225 Anum_pg_policy_polrelid,
226 BTEqualStrategyNumber, F_OIDEQ,
227 ObjectIdGetDatum(RelationGetRelid(relation)));
229 sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
233 * Loop through the row level security policies for this relation, if
236 while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
243 char *with_check_value;
244 Expr *with_check_qual;
245 char *policy_name_value;
247 RowSecurityPolicy *policy;
250 * Note: all the pass-by-reference data we collect here is either
251 * still stored in the tuple, or constructed in the caller's
252 * short-lived memory context. We must copy it into rscxt
256 /* Get policy command */
257 value_datum = heap_getattr(tuple, Anum_pg_policy_polcmd,
258 RelationGetDescr(catalog), &isnull);
260 cmd_value = DatumGetChar(value_datum);
262 /* Get policy name */
263 value_datum = heap_getattr(tuple, Anum_pg_policy_polname,
264 RelationGetDescr(catalog), &isnull);
266 policy_name_value = NameStr(*(DatumGetName(value_datum)));
268 /* Get policy roles */
269 roles_datum = heap_getattr(tuple, Anum_pg_policy_polroles,
270 RelationGetDescr(catalog), &isnull);
271 /* shouldn't be null, but initdb doesn't mark it so, so check */
273 elog(ERROR, "unexpected null value in pg_policy.polroles");
275 /* Get policy qual */
276 value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
277 RelationGetDescr(catalog), &isnull);
280 qual_value = TextDatumGetCString(value_datum);
281 qual_expr = (Expr *) stringToNode(qual_value);
286 /* Get WITH CHECK qual */
287 value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
288 RelationGetDescr(catalog), &isnull);
291 with_check_value = TextDatumGetCString(value_datum);
292 with_check_qual = (Expr *) stringToNode(with_check_value);
295 with_check_qual = NULL;
297 /* Now copy everything into the cache context */
298 MemoryContextSwitchTo(rscxt);
300 policy = palloc0(sizeof(RowSecurityPolicy));
301 policy->policy_name = pstrdup(policy_name_value);
302 policy->polcmd = cmd_value;
303 policy->roles = DatumGetArrayTypePCopy(roles_datum);
304 policy->qual = copyObject(qual_expr);
305 policy->with_check_qual = copyObject(with_check_qual);
306 policy->hassublinks = checkExprHasSubLink((Node *) qual_expr) ||
307 checkExprHasSubLink((Node *) with_check_qual);
309 rsdesc->policies = lcons(policy, rsdesc->policies);
311 MemoryContextSwitchTo(oldcxt);
313 /* clean up some (not all) of the junk ... */
314 if (qual_expr != NULL)
316 if (with_check_qual != NULL)
317 pfree(with_check_qual);
320 systable_endscan(sscan);
321 heap_close(catalog, AccessShareLock);
325 /* Delete rscxt, first making sure it isn't active */
326 MemoryContextSwitchTo(oldcxt);
327 MemoryContextDelete(rscxt);
332 /* Success --- attach the policy descriptor to the relcache entry */
333 relation->rd_rsdesc = rsdesc;
338 * remove a policy by its OID. If a policy does not exist with the provided
339 * oid, then an error is raised.
341 * policy_id - the oid of the policy.
344 RemovePolicyById(Oid policy_id)
346 Relation pg_policy_rel;
353 pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
356 * Find the policy to delete.
358 ScanKeyInit(&skey[0],
359 ObjectIdAttributeNumber,
360 BTEqualStrategyNumber, F_OIDEQ,
361 ObjectIdGetDatum(policy_id));
363 sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
366 tuple = systable_getnext(sscan);
368 /* If the policy exists, then remove it, otherwise raise an error. */
369 if (!HeapTupleIsValid(tuple))
370 elog(ERROR, "could not find tuple for policy %u", policy_id);
373 * Open and exclusive-lock the relation the policy belong to.
375 relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
377 rel = heap_open(relid, AccessExclusiveLock);
378 if (rel->rd_rel->relkind != RELKIND_RELATION)
380 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
381 errmsg("\"%s\" is not a table",
382 RelationGetRelationName(rel))));
384 if (!allowSystemTableMods && IsSystemRelation(rel))
386 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
387 errmsg("permission denied: \"%s\" is a system catalog",
388 RelationGetRelationName(rel))));
390 simple_heap_delete(pg_policy_rel, &tuple->t_self);
392 systable_endscan(sscan);
393 heap_close(rel, AccessExclusiveLock);
396 * Note that, unlike some of the other flags in pg_class, relrowsecurity
397 * is not just an indication of if policies exist. When relrowsecurity is
398 * set by a user, then all access to the relation must be through a
399 * policy. If no policy is defined for the relation then a default-deny
400 * policy is created and all records are filtered (except for queries from
404 CacheInvalidateRelcache(rel);
407 heap_close(pg_policy_rel, RowExclusiveLock);
411 * RemoveRoleFromObjectPolicy -
412 * remove a role from a policy by its OID. If the role is not a member of
413 * the policy then an error is raised. False is returned to indicate that
414 * the role could not be removed due to being the only role on the policy
415 * and therefore the entire policy should be removed.
417 * Note that a warning will be thrown and true will be returned on a
418 * permission error, as the policy should not be removed in that case.
420 * roleid - the oid of the role to remove
421 * classid - should always be PolicyRelationId
422 * policy_id - the oid of the policy.
425 RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
427 Relation pg_policy_rel;
433 ArrayType *policy_roles;
439 Assert(classid == PolicyRelationId);
441 pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
444 * Find the policy to update.
446 ScanKeyInit(&skey[0],
447 ObjectIdAttributeNumber,
448 BTEqualStrategyNumber, F_OIDEQ,
449 ObjectIdGetDatum(policy_id));
451 sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
454 tuple = systable_getnext(sscan);
456 /* Raise an error if we don't find the policy. */
457 if (!HeapTupleIsValid(tuple))
458 elog(ERROR, "could not find tuple for policy %u", policy_id);
461 * Open and exclusive-lock the relation the policy belongs to.
463 relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
465 rel = relation_open(relid, AccessExclusiveLock);
467 if (rel->rd_rel->relkind != RELKIND_RELATION)
469 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
470 errmsg("\"%s\" is not a table",
471 RelationGetRelationName(rel))));
473 if (!allowSystemTableMods && IsSystemRelation(rel))
475 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
476 errmsg("permission denied: \"%s\" is a system catalog",
477 RelationGetRelationName(rel))));
479 /* Get the current set of roles */
480 roles_datum = heap_getattr(tuple,
481 Anum_pg_policy_polroles,
482 RelationGetDescr(pg_policy_rel),
485 Assert(!attr_isnull);
487 policy_roles = DatumGetArrayTypePCopy(roles_datum);
489 /* We should be removing exactly one entry from the roles array */
490 num_roles = ARR_DIMS(policy_roles)[0] - 1;
492 Assert(num_roles >= 0);
494 /* Must own relation. */
495 if (pg_class_ownercheck(relid, GetUserId()))
496 noperm = false; /* user is allowed to modify this policy */
499 (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
500 errmsg("role \"%s\" could not be removed from policy \"%s\" on \"%s\"",
501 GetUserNameFromId(roleid, false),
502 NameStr(((Form_pg_policy) GETSTRUCT(tuple))->polname),
503 RelationGetRelationName(rel))));
506 * If multiple roles exist on this policy, then remove the one we were
507 * asked to and leave the rest.
509 if (!noperm && num_roles > 0)
512 Oid *roles = (Oid *) ARR_DATA_PTR(policy_roles);
516 List *qual_parse_rtable = NIL;
517 char *with_check_value;
518 Node *with_check_qual;
519 List *with_check_parse_rtable = NIL;
520 Datum values[Natts_pg_policy];
521 bool isnull[Natts_pg_policy];
522 bool replaces[Natts_pg_policy];
526 ObjectAddress target;
527 ObjectAddress myself;
530 memset(values, 0, sizeof(values));
531 memset(replaces, 0, sizeof(replaces));
532 memset(isnull, 0, sizeof(isnull));
535 * All of the dependencies will be removed from the policy and then
536 * re-added. In order to get them correct, we need to extract out
537 * the expressions in the policy and construct a parsestate just
538 * enough to build the range table(s) to then pass to
539 * recordDependencyOnExpr().
542 /* Get policy qual, to update dependencies */
543 value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
544 RelationGetDescr(pg_policy_rel), &attr_isnull);
547 ParseState *qual_pstate;
549 /* parsestate is built just to build the range table */
550 qual_pstate = make_parsestate(NULL);
552 qual_value = TextDatumGetCString(value_datum);
553 qual_expr = stringToNode(qual_value);
555 /* Add this rel to the parsestate's rangetable, for dependencies */
556 addRangeTableEntryForRelation(qual_pstate, rel, NULL, false, false);
558 qual_parse_rtable = qual_pstate->p_rtable;
559 free_parsestate(qual_pstate);
564 /* Get WITH CHECK qual, to update dependencies */
565 value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
566 RelationGetDescr(pg_policy_rel), &attr_isnull);
569 ParseState *with_check_pstate;
571 /* parsestate is built just to build the range table */
572 with_check_pstate = make_parsestate(NULL);
574 with_check_value = TextDatumGetCString(value_datum);
575 with_check_qual = stringToNode(with_check_value);
577 /* Add this rel to the parsestate's rangetable, for dependencies */
578 addRangeTableEntryForRelation(with_check_pstate, rel, NULL, false,
581 with_check_parse_rtable = with_check_pstate->p_rtable;
582 free_parsestate(with_check_pstate);
585 with_check_qual = NULL;
587 /* Rebuild the roles array to then update the pg_policy tuple with */
588 role_oids = (Datum *) palloc(num_roles * sizeof(Datum));
589 for (i = 0, j = 0; i < ARR_DIMS(policy_roles)[0]; i++)
590 /* Copy over all of the roles which are not the one being removed */
591 if (roles[i] != roleid)
592 role_oids[j++] = ObjectIdGetDatum(roles[i]);
594 /* We should have only removed the one role */
595 Assert(j == num_roles);
597 /* This is the array for the new tuple */
598 role_ids = construct_array(role_oids, num_roles, OIDOID,
599 sizeof(Oid), true, 'i');
601 replaces[Anum_pg_policy_polroles - 1] = true;
602 values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
604 new_tuple = heap_modify_tuple(tuple,
605 RelationGetDescr(pg_policy_rel),
606 values, isnull, replaces);
607 simple_heap_update(pg_policy_rel, &new_tuple->t_self, new_tuple);
609 /* Update Catalog Indexes */
610 CatalogUpdateIndexes(pg_policy_rel, new_tuple);
612 /* Remove all old dependencies. */
613 deleteDependencyRecordsFor(PolicyRelationId, policy_id, false);
615 /* Record the new set of dependencies */
616 target.classId = RelationRelationId;
617 target.objectId = relid;
618 target.objectSubId = 0;
620 myself.classId = PolicyRelationId;
621 myself.objectId = policy_id;
622 myself.objectSubId = 0;
624 recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
627 recordDependencyOnExpr(&myself, qual_expr, qual_parse_rtable,
631 recordDependencyOnExpr(&myself, with_check_qual,
632 with_check_parse_rtable,
635 /* Remove all the old shared dependencies (roles) */
636 deleteSharedDependencyRecordsFor(PolicyRelationId, policy_id, 0);
638 /* Record the new shared dependencies (roles) */
639 target.classId = AuthIdRelationId;
640 target.objectSubId = 0;
641 for (i = 0; i < num_roles; i++)
643 target.objectId = DatumGetObjectId(role_oids[i]);
644 /* no need for dependency on the public role */
645 if (target.objectId != ACL_ID_PUBLIC)
646 recordSharedDependencyOn(&myself, &target,
647 SHARED_DEPENDENCY_POLICY);
650 InvokeObjectPostAlterHook(PolicyRelationId, policy_id, 0);
652 heap_freetuple(new_tuple);
654 /* Invalidate Relation Cache */
655 CacheInvalidateRelcache(rel);
659 systable_endscan(sscan);
660 relation_close(rel, AccessExclusiveLock);
661 heap_close(pg_policy_rel, RowExclusiveLock);
663 return(noperm || num_roles > 0);
668 * handles the execution of the CREATE POLICY command.
670 * stmt - the CreatePolicyStmt that describes the policy to create.
673 CreatePolicy(CreatePolicyStmt *stmt)
675 Relation pg_policy_rel;
677 Relation target_table;
683 ParseState *qual_pstate;
684 ParseState *with_check_pstate;
687 Node *with_check_qual;
690 HeapTuple policy_tuple;
691 Datum values[Natts_pg_policy];
692 bool isnull[Natts_pg_policy];
693 ObjectAddress target;
694 ObjectAddress myself;
698 polcmd = parse_policy_command(stmt->cmd_name);
701 * If the command is SELECT or DELETE then WITH CHECK should be NULL.
703 if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR)
704 && stmt->with_check != NULL)
706 (errcode(ERRCODE_SYNTAX_ERROR),
707 errmsg("WITH CHECK cannot be applied to SELECT or DELETE")));
710 * If the command is INSERT then WITH CHECK should be the only expression
713 if (polcmd == ACL_INSERT_CHR && stmt->qual != NULL)
715 (errcode(ERRCODE_SYNTAX_ERROR),
716 errmsg("only WITH CHECK expression allowed for INSERT")));
718 /* Collect role ids */
719 role_oids = policy_role_list_to_array(stmt->roles, &nitems);
720 role_ids = construct_array(role_oids, nitems, OIDOID,
721 sizeof(Oid), true, 'i');
723 /* Parse the supplied clause */
724 qual_pstate = make_parsestate(NULL);
725 with_check_pstate = make_parsestate(NULL);
728 memset(values, 0, sizeof(values));
729 memset(isnull, 0, sizeof(isnull));
731 /* Get id of table. Also handles permissions checks. */
732 table_id = RangeVarGetRelidExtended(stmt->table, AccessExclusiveLock,
734 RangeVarCallbackForPolicy,
737 /* Open target_table to build quals. No lock is necessary. */
738 target_table = relation_open(table_id, NoLock);
740 /* Add for the regular security quals */
741 rte = addRangeTableEntryForRelation(qual_pstate, target_table,
743 addRTEtoQuery(qual_pstate, rte, false, true, true);
745 /* Add for the with-check quals */
746 rte = addRangeTableEntryForRelation(with_check_pstate, target_table,
748 addRTEtoQuery(with_check_pstate, rte, false, true, true);
750 qual = transformWhereClause(qual_pstate,
751 copyObject(stmt->qual),
755 with_check_qual = transformWhereClause(with_check_pstate,
756 copyObject(stmt->with_check),
760 /* Fix up collation information */
761 assign_expr_collations(qual_pstate, qual);
762 assign_expr_collations(with_check_pstate, with_check_qual);
764 /* Open pg_policy catalog */
765 pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
767 /* Set key - policy's relation id. */
768 ScanKeyInit(&skey[0],
769 Anum_pg_policy_polrelid,
770 BTEqualStrategyNumber, F_OIDEQ,
771 ObjectIdGetDatum(table_id));
773 /* Set key - policy's name. */
774 ScanKeyInit(&skey[1],
775 Anum_pg_policy_polname,
776 BTEqualStrategyNumber, F_NAMEEQ,
777 CStringGetDatum(stmt->policy_name));
779 sscan = systable_beginscan(pg_policy_rel,
780 PolicyPolrelidPolnameIndexId, true, NULL, 2,
783 policy_tuple = systable_getnext(sscan);
785 /* Complain if the policy name already exists for the table */
786 if (HeapTupleIsValid(policy_tuple))
788 (errcode(ERRCODE_DUPLICATE_OBJECT),
789 errmsg("policy \"%s\" for table \"%s\" already exists",
790 stmt->policy_name, RelationGetRelationName(target_table))));
792 values[Anum_pg_policy_polrelid - 1] = ObjectIdGetDatum(table_id);
793 values[Anum_pg_policy_polname - 1] = DirectFunctionCall1(namein,
794 CStringGetDatum(stmt->policy_name));
795 values[Anum_pg_policy_polcmd - 1] = CharGetDatum(polcmd);
796 values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
798 /* Add qual if present. */
800 values[Anum_pg_policy_polqual - 1] = CStringGetTextDatum(nodeToString(qual));
802 isnull[Anum_pg_policy_polqual - 1] = true;
804 /* Add WITH CHECK qual if present */
806 values[Anum_pg_policy_polwithcheck - 1] = CStringGetTextDatum(nodeToString(with_check_qual));
808 isnull[Anum_pg_policy_polwithcheck - 1] = true;
810 policy_tuple = heap_form_tuple(RelationGetDescr(pg_policy_rel), values,
813 policy_id = simple_heap_insert(pg_policy_rel, policy_tuple);
816 CatalogUpdateIndexes(pg_policy_rel, policy_tuple);
818 /* Record Dependencies */
819 target.classId = RelationRelationId;
820 target.objectId = table_id;
821 target.objectSubId = 0;
823 myself.classId = PolicyRelationId;
824 myself.objectId = policy_id;
825 myself.objectSubId = 0;
827 recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
829 recordDependencyOnExpr(&myself, qual, qual_pstate->p_rtable,
832 recordDependencyOnExpr(&myself, with_check_qual,
833 with_check_pstate->p_rtable, DEPENDENCY_NORMAL);
835 /* Register role dependencies */
836 target.classId = AuthIdRelationId;
837 target.objectSubId = 0;
838 for (i = 0; i < nitems; i++)
840 target.objectId = DatumGetObjectId(role_oids[i]);
841 /* no dependency if public */
842 if (target.objectId != ACL_ID_PUBLIC)
843 recordSharedDependencyOn(&myself, &target,
844 SHARED_DEPENDENCY_POLICY);
847 InvokeObjectPostCreateHook(PolicyRelationId, policy_id, 0);
849 /* Invalidate Relation Cache */
850 CacheInvalidateRelcache(target_table);
853 heap_freetuple(policy_tuple);
854 free_parsestate(qual_pstate);
855 free_parsestate(with_check_pstate);
856 systable_endscan(sscan);
857 relation_close(target_table, NoLock);
858 heap_close(pg_policy_rel, RowExclusiveLock);
865 * handles the execution of the ALTER POLICY command.
867 * stmt - the AlterPolicyStmt that describes the policy and how to alter it.
870 AlterPolicy(AlterPolicyStmt *stmt)
872 Relation pg_policy_rel;
874 Relation target_table;
876 Datum *role_oids = NULL;
878 ArrayType *role_ids = NULL;
879 List *qual_parse_rtable = NIL;
880 List *with_check_parse_rtable = NIL;
882 Node *with_check_qual = NULL;
885 HeapTuple policy_tuple;
887 Datum values[Natts_pg_policy];
888 bool isnull[Natts_pg_policy];
889 bool replaces[Natts_pg_policy];
890 ObjectAddress target;
891 ObjectAddress myself;
898 if (stmt->roles != NULL)
900 role_oids = policy_role_list_to_array(stmt->roles, &nitems);
901 role_ids = construct_array(role_oids, nitems, OIDOID,
902 sizeof(Oid), true, 'i');
905 /* Get id of table. Also handles permissions checks. */
906 table_id = RangeVarGetRelidExtended(stmt->table, AccessExclusiveLock,
908 RangeVarCallbackForPolicy,
911 target_table = relation_open(table_id, NoLock);
913 /* Parse the using policy clause */
917 ParseState *qual_pstate = make_parsestate(NULL);
919 rte = addRangeTableEntryForRelation(qual_pstate, target_table,
922 addRTEtoQuery(qual_pstate, rte, false, true, true);
924 qual = transformWhereClause(qual_pstate, copyObject(stmt->qual),
928 /* Fix up collation information */
929 assign_expr_collations(qual_pstate, qual);
931 qual_parse_rtable = qual_pstate->p_rtable;
932 free_parsestate(qual_pstate);
935 /* Parse the with-check policy clause */
936 if (stmt->with_check)
939 ParseState *with_check_pstate = make_parsestate(NULL);
941 rte = addRangeTableEntryForRelation(with_check_pstate, target_table,
944 addRTEtoQuery(with_check_pstate, rte, false, true, true);
946 with_check_qual = transformWhereClause(with_check_pstate,
947 copyObject(stmt->with_check),
951 /* Fix up collation information */
952 assign_expr_collations(with_check_pstate, with_check_qual);
954 with_check_parse_rtable = with_check_pstate->p_rtable;
955 free_parsestate(with_check_pstate);
959 memset(values, 0, sizeof(values));
960 memset(replaces, 0, sizeof(replaces));
961 memset(isnull, 0, sizeof(isnull));
963 /* Find policy to update. */
964 pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
966 /* Set key - policy's relation id. */
967 ScanKeyInit(&skey[0],
968 Anum_pg_policy_polrelid,
969 BTEqualStrategyNumber, F_OIDEQ,
970 ObjectIdGetDatum(table_id));
972 /* Set key - policy's name. */
973 ScanKeyInit(&skey[1],
974 Anum_pg_policy_polname,
975 BTEqualStrategyNumber, F_NAMEEQ,
976 CStringGetDatum(stmt->policy_name));
978 sscan = systable_beginscan(pg_policy_rel,
979 PolicyPolrelidPolnameIndexId, true, NULL, 2,
982 policy_tuple = systable_getnext(sscan);
984 /* Check that the policy is found, raise an error if not. */
985 if (!HeapTupleIsValid(policy_tuple))
987 (errcode(ERRCODE_UNDEFINED_OBJECT),
988 errmsg("policy \"%s\" for table \"%s\" does not exist",
990 RelationGetRelationName(target_table))));
992 /* Get policy command */
993 polcmd_datum = heap_getattr(policy_tuple, Anum_pg_policy_polcmd,
994 RelationGetDescr(pg_policy_rel),
996 Assert(!polcmd_isnull);
997 polcmd = DatumGetChar(polcmd_datum);
1000 * If the command is SELECT or DELETE then WITH CHECK should be NULL.
1002 if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR)
1003 && stmt->with_check != NULL)
1005 (errcode(ERRCODE_SYNTAX_ERROR),
1006 errmsg("only USING expression allowed for SELECT, DELETE")));
1009 * If the command is INSERT then WITH CHECK should be the only expression
1012 if ((polcmd == ACL_INSERT_CHR)
1013 && stmt->qual != NULL)
1015 (errcode(ERRCODE_SYNTAX_ERROR),
1016 errmsg("only WITH CHECK expression allowed for INSERT")));
1018 policy_id = HeapTupleGetOid(policy_tuple);
1020 if (role_ids != NULL)
1022 replaces[Anum_pg_policy_polroles - 1] = true;
1023 values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
1030 ArrayType *policy_roles;
1033 * We need to pull the set of roles this policy applies to from
1034 * what's in the catalog, so that we can recreate the dependencies
1035 * correctly for the policy.
1038 roles_datum = heap_getattr(policy_tuple, Anum_pg_policy_polroles,
1039 RelationGetDescr(pg_policy_rel),
1041 Assert(!attr_isnull);
1043 policy_roles = DatumGetArrayTypePCopy(roles_datum);
1045 roles = (Oid *) ARR_DATA_PTR(policy_roles);
1047 nitems = ARR_DIMS(policy_roles)[0];
1049 role_oids = (Datum *) palloc(nitems * sizeof(Datum));
1051 for (i = 0; i < nitems; i++)
1052 role_oids[i] = ObjectIdGetDatum(roles[i]);
1057 replaces[Anum_pg_policy_polqual - 1] = true;
1058 values[Anum_pg_policy_polqual - 1]
1059 = CStringGetTextDatum(nodeToString(qual));
1067 * We need to pull the USING expression and build the range table for
1068 * the policy from what's in the catalog, so that we can recreate
1069 * the dependencies correctly for the policy.
1072 /* Check if the policy has a USING expr */
1073 value_datum = heap_getattr(policy_tuple, Anum_pg_policy_polqual,
1074 RelationGetDescr(pg_policy_rel),
1079 ParseState *qual_pstate = make_parsestate(NULL);
1081 /* parsestate is built just to build the range table */
1082 qual_pstate = make_parsestate(NULL);
1084 qual_value = TextDatumGetCString(value_datum);
1085 qual = stringToNode(qual_value);
1087 /* Add this rel to the parsestate's rangetable, for dependencies */
1088 addRangeTableEntryForRelation(qual_pstate, target_table, NULL,
1091 qual_parse_rtable = qual_pstate->p_rtable;
1092 free_parsestate(qual_pstate);
1096 if (with_check_qual != NULL)
1098 replaces[Anum_pg_policy_polwithcheck - 1] = true;
1099 values[Anum_pg_policy_polwithcheck - 1]
1100 = CStringGetTextDatum(nodeToString(with_check_qual));
1108 * We need to pull the WITH CHECK expression and build the range table
1109 * for the policy from what's in the catalog, so that we can recreate
1110 * the dependencies correctly for the policy.
1113 /* Check if the policy has a WITH CHECK expr */
1114 value_datum = heap_getattr(policy_tuple, Anum_pg_policy_polwithcheck,
1115 RelationGetDescr(pg_policy_rel),
1119 char *with_check_value;
1120 ParseState *with_check_pstate = make_parsestate(NULL);
1122 /* parsestate is built just to build the range table */
1123 with_check_pstate = make_parsestate(NULL);
1125 with_check_value = TextDatumGetCString(value_datum);
1126 with_check_qual = stringToNode(with_check_value);
1128 /* Add this rel to the parsestate's rangetable, for dependencies */
1129 addRangeTableEntryForRelation(with_check_pstate, target_table, NULL,
1132 with_check_parse_rtable = with_check_pstate->p_rtable;
1133 free_parsestate(with_check_pstate);
1137 new_tuple = heap_modify_tuple(policy_tuple,
1138 RelationGetDescr(pg_policy_rel),
1139 values, isnull, replaces);
1140 simple_heap_update(pg_policy_rel, &new_tuple->t_self, new_tuple);
1142 /* Update Catalog Indexes */
1143 CatalogUpdateIndexes(pg_policy_rel, new_tuple);
1145 /* Update Dependencies. */
1146 deleteDependencyRecordsFor(PolicyRelationId, policy_id, false);
1148 /* Record Dependencies */
1149 target.classId = RelationRelationId;
1150 target.objectId = table_id;
1151 target.objectSubId = 0;
1153 myself.classId = PolicyRelationId;
1154 myself.objectId = policy_id;
1155 myself.objectSubId = 0;
1157 recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
1159 recordDependencyOnExpr(&myself, qual, qual_parse_rtable, DEPENDENCY_NORMAL);
1161 recordDependencyOnExpr(&myself, with_check_qual, with_check_parse_rtable,
1164 /* Register role dependencies */
1165 deleteSharedDependencyRecordsFor(PolicyRelationId, policy_id, 0);
1166 target.classId = AuthIdRelationId;
1167 target.objectSubId = 0;
1168 for (i = 0; i < nitems; i++)
1170 target.objectId = DatumGetObjectId(role_oids[i]);
1171 /* no dependency if public */
1172 if (target.objectId != ACL_ID_PUBLIC)
1173 recordSharedDependencyOn(&myself, &target,
1174 SHARED_DEPENDENCY_POLICY);
1177 InvokeObjectPostAlterHook(PolicyRelationId, policy_id, 0);
1179 heap_freetuple(new_tuple);
1181 /* Invalidate Relation Cache */
1182 CacheInvalidateRelcache(target_table);
1185 systable_endscan(sscan);
1186 relation_close(target_table, NoLock);
1187 heap_close(pg_policy_rel, RowExclusiveLock);
1194 * change the name of a policy on a relation
1197 rename_policy(RenameStmt *stmt)
1199 Relation pg_policy_rel;
1200 Relation target_table;
1203 ScanKeyData skey[2];
1205 HeapTuple policy_tuple;
1206 ObjectAddress address;
1208 /* Get id of table. Also handles permissions checks. */
1209 table_id = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
1211 RangeVarCallbackForPolicy,
1214 target_table = relation_open(table_id, NoLock);
1216 pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
1218 /* First pass -- check for conflict */
1220 /* Add key - policy's relation id. */
1221 ScanKeyInit(&skey[0],
1222 Anum_pg_policy_polrelid,
1223 BTEqualStrategyNumber, F_OIDEQ,
1224 ObjectIdGetDatum(table_id));
1226 /* Add key - policy's name. */
1227 ScanKeyInit(&skey[1],
1228 Anum_pg_policy_polname,
1229 BTEqualStrategyNumber, F_NAMEEQ,
1230 CStringGetDatum(stmt->newname));
1232 sscan = systable_beginscan(pg_policy_rel,
1233 PolicyPolrelidPolnameIndexId, true, NULL, 2,
1236 if (HeapTupleIsValid(systable_getnext(sscan)))
1238 (errcode(ERRCODE_DUPLICATE_OBJECT),
1239 errmsg("policy \"%s\" for table \"%s\" already exists",
1240 stmt->newname, RelationGetRelationName(target_table))));
1242 systable_endscan(sscan);
1244 /* Second pass -- find existing policy and update */
1245 /* Add key - policy's relation id. */
1246 ScanKeyInit(&skey[0],
1247 Anum_pg_policy_polrelid,
1248 BTEqualStrategyNumber, F_OIDEQ,
1249 ObjectIdGetDatum(table_id));
1251 /* Add key - policy's name. */
1252 ScanKeyInit(&skey[1],
1253 Anum_pg_policy_polname,
1254 BTEqualStrategyNumber, F_NAMEEQ,
1255 CStringGetDatum(stmt->subname));
1257 sscan = systable_beginscan(pg_policy_rel,
1258 PolicyPolrelidPolnameIndexId, true, NULL, 2,
1261 policy_tuple = systable_getnext(sscan);
1263 /* Complain if we did not find the policy */
1264 if (!HeapTupleIsValid(policy_tuple))
1266 (errcode(ERRCODE_UNDEFINED_OBJECT),
1267 errmsg("policy \"%s\" for table \"%s\" does not exist",
1268 stmt->subname, RelationGetRelationName(target_table))));
1270 opoloid = HeapTupleGetOid(policy_tuple);
1272 policy_tuple = heap_copytuple(policy_tuple);
1274 namestrcpy(&((Form_pg_policy) GETSTRUCT(policy_tuple))->polname,
1277 simple_heap_update(pg_policy_rel, &policy_tuple->t_self, policy_tuple);
1279 /* keep system catalog indexes current */
1280 CatalogUpdateIndexes(pg_policy_rel, policy_tuple);
1282 InvokeObjectPostAlterHook(PolicyRelationId,
1283 HeapTupleGetOid(policy_tuple), 0);
1285 ObjectAddressSet(address, PolicyRelationId, opoloid);
1288 * Invalidate relation's relcache entry so that other backends (and this
1289 * one too!) are sent SI message to make them rebuild relcache entries.
1290 * (Ideally this should happen automatically...)
1292 CacheInvalidateRelcache(target_table);
1295 systable_endscan(sscan);
1296 heap_close(pg_policy_rel, RowExclusiveLock);
1297 relation_close(target_table, NoLock);
1303 * get_relation_policy_oid - Look up a policy by name to find its OID
1305 * If missing_ok is false, throw an error if policy not found. If
1306 * true, just return InvalidOid.
1309 get_relation_policy_oid(Oid relid, const char *policy_name, bool missing_ok)
1311 Relation pg_policy_rel;
1312 ScanKeyData skey[2];
1314 HeapTuple policy_tuple;
1317 pg_policy_rel = heap_open(PolicyRelationId, AccessShareLock);
1319 /* Add key - policy's relation id. */
1320 ScanKeyInit(&skey[0],
1321 Anum_pg_policy_polrelid,
1322 BTEqualStrategyNumber, F_OIDEQ,
1323 ObjectIdGetDatum(relid));
1325 /* Add key - policy's name. */
1326 ScanKeyInit(&skey[1],
1327 Anum_pg_policy_polname,
1328 BTEqualStrategyNumber, F_NAMEEQ,
1329 CStringGetDatum(policy_name));
1331 sscan = systable_beginscan(pg_policy_rel,
1332 PolicyPolrelidPolnameIndexId, true, NULL, 2,
1335 policy_tuple = systable_getnext(sscan);
1337 if (!HeapTupleIsValid(policy_tuple))
1341 (errcode(ERRCODE_UNDEFINED_OBJECT),
1342 errmsg("policy \"%s\" for table \"%s\" does not exist",
1343 policy_name, get_rel_name(relid))));
1345 policy_oid = InvalidOid;
1348 policy_oid = HeapTupleGetOid(policy_tuple);
1351 systable_endscan(sscan);
1352 heap_close(pg_policy_rel, AccessShareLock);
1358 * relation_has_policies - Determine if relation has any policies
1361 relation_has_policies(Relation rel)
1366 HeapTuple policy_tuple;
1369 catalog = heap_open(PolicyRelationId, AccessShareLock);
1371 Anum_pg_policy_polrelid,
1372 BTEqualStrategyNumber, F_OIDEQ,
1373 ObjectIdGetDatum(RelationGetRelid(rel)));
1374 sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
1376 policy_tuple = systable_getnext(sscan);
1377 if (HeapTupleIsValid(policy_tuple))
1380 systable_endscan(sscan);
1381 heap_close(catalog, AccessShareLock);