Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * policy.c
4 : * Commands for manipulating policies.
5 : *
6 : * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : * src/backend/commands/policy.c
10 : *
11 : *-------------------------------------------------------------------------
12 : */
13 : #include "postgres.h"
14 :
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"
48 :
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);
53 :
54 : /*
55 : * Callback to RangeVarGetRelidExtended().
56 : *
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.
61 : *
62 : * If any of these checks fails then an error is raised.
63 : */
64 : static void
65 100 : RangeVarCallbackForPolicy(const RangeVar *rv, Oid relid, Oid oldrelid,
66 : void *arg)
67 : {
68 : HeapTuple tuple;
69 : Form_pg_class classform;
70 : char relkind;
71 :
72 100 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
73 100 : if (!HeapTupleIsValid(tuple))
74 98 : return;
75 :
76 100 : classform = (Form_pg_class) GETSTRUCT(tuple);
77 100 : relkind = classform->relkind;
78 :
79 : /* Must own relation. */
80 100 : if (!pg_class_ownercheck(relid, GetUserId()))
81 2 : aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, rv->relname);
82 :
83 : /* No system table modifications unless explicitly allowed. */
84 98 : if (!allowSystemTableMods && IsSystemClass(relid, classform))
85 0 : ereport(ERROR,
86 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
87 : errmsg("permission denied: \"%s\" is a system catalog",
88 : rv->relname)));
89 :
90 : /* Relation type MUST be a table. */
91 98 : if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
92 0 : ereport(ERROR,
93 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
94 : errmsg("\"%s\" is not a table", rv->relname)));
95 :
96 98 : ReleaseSysCache(tuple);
97 : }
98 :
99 : /*
100 : * parse_policy_command -
101 : * helper function to convert full command strings to their char
102 : * representation.
103 : *
104 : * cmd_name - full string command name. Valid values are 'all', 'select',
105 : * 'insert', 'update' and 'delete'.
106 : *
107 : */
108 : static char
109 81 : parse_policy_command(const char *cmd_name)
110 : {
111 : char polcmd;
112 :
113 81 : if (!cmd_name)
114 0 : elog(ERROR, "unrecognized policy command");
115 :
116 81 : if (strcmp(cmd_name, "all") == 0)
117 52 : polcmd = '*';
118 29 : else if (strcmp(cmd_name, "select") == 0)
119 11 : polcmd = ACL_SELECT_CHR;
120 18 : else if (strcmp(cmd_name, "insert") == 0)
121 5 : polcmd = ACL_INSERT_CHR;
122 13 : else if (strcmp(cmd_name, "update") == 0)
123 8 : polcmd = ACL_UPDATE_CHR;
124 5 : else if (strcmp(cmd_name, "delete") == 0)
125 5 : polcmd = ACL_DELETE_CHR;
126 : else
127 0 : elog(ERROR, "unrecognized policy command");
128 :
129 81 : return polcmd;
130 : }
131 :
132 : /*
133 : * policy_role_list_to_array
134 : * helper function to convert a list of RoleSpecs to an array of
135 : * role id Datums.
136 : */
137 : static Datum *
138 83 : policy_role_list_to_array(List *roles, int *num_roles)
139 : {
140 : Datum *role_oids;
141 : ListCell *cell;
142 83 : int i = 0;
143 :
144 : /* Handle no roles being passed in as being for public */
145 83 : if (roles == NIL)
146 : {
147 0 : *num_roles = 1;
148 0 : role_oids = (Datum *) palloc(*num_roles * sizeof(Datum));
149 0 : role_oids[0] = ObjectIdGetDatum(ACL_ID_PUBLIC);
150 :
151 0 : return role_oids;
152 : }
153 :
154 83 : *num_roles = list_length(roles);
155 83 : role_oids = (Datum *) palloc(*num_roles * sizeof(Datum));
156 :
157 101 : foreach(cell, roles)
158 : {
159 87 : RoleSpec *spec = lfirst(cell);
160 :
161 : /*
162 : * PUBLIC covers all roles, so it only makes sense alone.
163 : */
164 87 : if (spec->roletype == ROLESPEC_PUBLIC)
165 : {
166 69 : if (*num_roles != 1)
167 : {
168 0 : ereport(WARNING,
169 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
170 : errmsg("ignoring specified roles other than PUBLIC"),
171 : errhint("All roles are members of the PUBLIC role.")));
172 0 : *num_roles = 1;
173 : }
174 69 : role_oids[0] = ObjectIdGetDatum(ACL_ID_PUBLIC);
175 :
176 69 : return role_oids;
177 : }
178 : else
179 36 : role_oids[i++] =
180 18 : ObjectIdGetDatum(get_rolespec_oid(spec, false));
181 : }
182 :
183 14 : return role_oids;
184 : }
185 :
186 : /*
187 : * Load row security policy from the catalog, and store it in
188 : * the relation's relcache entry.
189 : */
190 : void
191 210 : RelationBuildRowSecurity(Relation relation)
192 : {
193 : MemoryContext rscxt;
194 210 : MemoryContext oldcxt = CurrentMemoryContext;
195 210 : RowSecurityDesc *volatile rsdesc = NULL;
196 :
197 : /*
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
200 : * a relcache flush.
201 : */
202 210 : rscxt = AllocSetContextCreate(CacheMemoryContext,
203 : "row security descriptor",
204 : ALLOCSET_SMALL_SIZES);
205 :
206 : /*
207 : * Since rscxt lives under CacheMemoryContext, it is long-lived. Use a
208 : * PG_TRY block to ensure it'll get freed if we fail partway through.
209 : */
210 210 : PG_TRY();
211 : {
212 : Relation catalog;
213 : ScanKeyData skey;
214 : SysScanDesc sscan;
215 : HeapTuple tuple;
216 :
217 210 : rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc));
218 210 : rsdesc->rscxt = rscxt;
219 :
220 210 : catalog = heap_open(PolicyRelationId, AccessShareLock);
221 :
222 210 : ScanKeyInit(&skey,
223 : Anum_pg_policy_polrelid,
224 : BTEqualStrategyNumber, F_OIDEQ,
225 : ObjectIdGetDatum(RelationGetRelid(relation)));
226 :
227 210 : sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
228 : NULL, 1, &skey);
229 :
230 : /*
231 : * Loop through the row level security policies for this relation, if
232 : * any.
233 : */
234 653 : while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
235 : {
236 : Datum value_datum;
237 : char cmd_value;
238 : bool permissive_value;
239 : Datum roles_datum;
240 : char *qual_value;
241 : Expr *qual_expr;
242 : char *with_check_value;
243 : Expr *with_check_qual;
244 : char *policy_name_value;
245 : bool isnull;
246 : RowSecurityPolicy *policy;
247 :
248 : /*
249 : * Note: all the pass-by-reference data we collect here is either
250 : * still stored in the tuple, or constructed in the caller's
251 : * short-lived memory context. We must copy it into rscxt
252 : * explicitly below.
253 : */
254 :
255 : /* Get policy command */
256 233 : value_datum = heap_getattr(tuple, Anum_pg_policy_polcmd,
257 : RelationGetDescr(catalog), &isnull);
258 233 : Assert(!isnull);
259 233 : cmd_value = DatumGetChar(value_datum);
260 :
261 : /* Get policy permissive or restrictive */
262 233 : value_datum = heap_getattr(tuple, Anum_pg_policy_polpermissive,
263 : RelationGetDescr(catalog), &isnull);
264 233 : Assert(!isnull);
265 233 : permissive_value = DatumGetBool(value_datum);
266 :
267 : /* Get policy name */
268 233 : value_datum = heap_getattr(tuple, Anum_pg_policy_polname,
269 : RelationGetDescr(catalog), &isnull);
270 233 : Assert(!isnull);
271 233 : policy_name_value = NameStr(*(DatumGetName(value_datum)));
272 :
273 : /* Get policy roles */
274 233 : roles_datum = heap_getattr(tuple, Anum_pg_policy_polroles,
275 : RelationGetDescr(catalog), &isnull);
276 : /* shouldn't be null, but initdb doesn't mark it so, so check */
277 233 : if (isnull)
278 0 : elog(ERROR, "unexpected null value in pg_policy.polroles");
279 :
280 : /* Get policy qual */
281 233 : value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
282 : RelationGetDescr(catalog), &isnull);
283 233 : if (!isnull)
284 : {
285 216 : qual_value = TextDatumGetCString(value_datum);
286 216 : qual_expr = (Expr *) stringToNode(qual_value);
287 : }
288 : else
289 17 : qual_expr = NULL;
290 :
291 : /* Get WITH CHECK qual */
292 233 : value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
293 : RelationGetDescr(catalog), &isnull);
294 233 : if (!isnull)
295 : {
296 37 : with_check_value = TextDatumGetCString(value_datum);
297 37 : with_check_qual = (Expr *) stringToNode(with_check_value);
298 : }
299 : else
300 196 : with_check_qual = NULL;
301 :
302 : /* Now copy everything into the cache context */
303 233 : MemoryContextSwitchTo(rscxt);
304 :
305 233 : policy = palloc0(sizeof(RowSecurityPolicy));
306 233 : policy->policy_name = pstrdup(policy_name_value);
307 233 : policy->polcmd = cmd_value;
308 233 : policy->permissive = permissive_value;
309 233 : policy->roles = DatumGetArrayTypePCopy(roles_datum);
310 233 : policy->qual = copyObject(qual_expr);
311 233 : policy->with_check_qual = copyObject(with_check_qual);
312 438 : policy->hassublinks = checkExprHasSubLink((Node *) qual_expr) ||
313 205 : checkExprHasSubLink((Node *) with_check_qual);
314 :
315 233 : rsdesc->policies = lcons(policy, rsdesc->policies);
316 :
317 233 : MemoryContextSwitchTo(oldcxt);
318 :
319 : /* clean up some (not all) of the junk ... */
320 233 : if (qual_expr != NULL)
321 216 : pfree(qual_expr);
322 233 : if (with_check_qual != NULL)
323 37 : pfree(with_check_qual);
324 : }
325 :
326 210 : systable_endscan(sscan);
327 210 : heap_close(catalog, AccessShareLock);
328 : }
329 0 : PG_CATCH();
330 : {
331 : /* Delete rscxt, first making sure it isn't active */
332 0 : MemoryContextSwitchTo(oldcxt);
333 0 : MemoryContextDelete(rscxt);
334 0 : PG_RE_THROW();
335 : }
336 210 : PG_END_TRY();
337 :
338 : /* Success --- attach the policy descriptor to the relcache entry */
339 210 : relation->rd_rsdesc = rsdesc;
340 210 : }
341 :
342 : /*
343 : * RemovePolicyById -
344 : * remove a policy by its OID. If a policy does not exist with the provided
345 : * oid, then an error is raised.
346 : *
347 : * policy_id - the oid of the policy.
348 : */
349 : void
350 69 : RemovePolicyById(Oid policy_id)
351 : {
352 : Relation pg_policy_rel;
353 : SysScanDesc sscan;
354 : ScanKeyData skey[1];
355 : HeapTuple tuple;
356 : Oid relid;
357 : Relation rel;
358 :
359 69 : pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
360 :
361 : /*
362 : * Find the policy to delete.
363 : */
364 69 : ScanKeyInit(&skey[0],
365 : ObjectIdAttributeNumber,
366 : BTEqualStrategyNumber, F_OIDEQ,
367 : ObjectIdGetDatum(policy_id));
368 :
369 69 : sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
370 : NULL, 1, skey);
371 :
372 69 : tuple = systable_getnext(sscan);
373 :
374 : /* If the policy exists, then remove it, otherwise raise an error. */
375 69 : if (!HeapTupleIsValid(tuple))
376 0 : elog(ERROR, "could not find tuple for policy %u", policy_id);
377 :
378 : /*
379 : * Open and exclusive-lock the relation the policy belongs to. (We need
380 : * exclusive lock to lock out queries that might otherwise depend on the
381 : * set of policies the rel has; furthermore we've got to hold the lock
382 : * till commit.)
383 : */
384 69 : relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
385 :
386 69 : rel = heap_open(relid, AccessExclusiveLock);
387 73 : if (rel->rd_rel->relkind != RELKIND_RELATION &&
388 4 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
389 0 : ereport(ERROR,
390 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
391 : errmsg("\"%s\" is not a table",
392 : RelationGetRelationName(rel))));
393 :
394 69 : if (!allowSystemTableMods && IsSystemRelation(rel))
395 0 : ereport(ERROR,
396 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
397 : errmsg("permission denied: \"%s\" is a system catalog",
398 : RelationGetRelationName(rel))));
399 :
400 69 : CatalogTupleDelete(pg_policy_rel, &tuple->t_self);
401 :
402 69 : systable_endscan(sscan);
403 :
404 : /*
405 : * Note that, unlike some of the other flags in pg_class, relrowsecurity
406 : * is not just an indication of if policies exist. When relrowsecurity is
407 : * set by a user, then all access to the relation must be through a
408 : * policy. If no policy is defined for the relation then a default-deny
409 : * policy is created and all records are filtered (except for queries from
410 : * the owner).
411 : */
412 69 : CacheInvalidateRelcache(rel);
413 :
414 69 : heap_close(rel, NoLock);
415 :
416 : /* Clean up */
417 69 : heap_close(pg_policy_rel, RowExclusiveLock);
418 69 : }
419 :
420 : /*
421 : * RemoveRoleFromObjectPolicy -
422 : * remove a role from a policy by its OID. If the role is not a member of
423 : * the policy then an error is raised. False is returned to indicate that
424 : * the role could not be removed due to being the only role on the policy
425 : * and therefore the entire policy should be removed.
426 : *
427 : * Note that a warning will be thrown and true will be returned on a
428 : * permission error, as the policy should not be removed in that case.
429 : *
430 : * roleid - the oid of the role to remove
431 : * classid - should always be PolicyRelationId
432 : * policy_id - the oid of the policy.
433 : */
434 : bool
435 3 : RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
436 : {
437 : Relation pg_policy_rel;
438 : SysScanDesc sscan;
439 : ScanKeyData skey[1];
440 : HeapTuple tuple;
441 : Oid relid;
442 : Relation rel;
443 : ArrayType *policy_roles;
444 : int num_roles;
445 : Datum roles_datum;
446 : bool attr_isnull;
447 3 : bool noperm = true;
448 :
449 3 : Assert(classid == PolicyRelationId);
450 :
451 3 : pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
452 :
453 : /*
454 : * Find the policy to update.
455 : */
456 3 : ScanKeyInit(&skey[0],
457 : ObjectIdAttributeNumber,
458 : BTEqualStrategyNumber, F_OIDEQ,
459 : ObjectIdGetDatum(policy_id));
460 :
461 3 : sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
462 : NULL, 1, skey);
463 :
464 3 : tuple = systable_getnext(sscan);
465 :
466 : /* Raise an error if we don't find the policy. */
467 3 : if (!HeapTupleIsValid(tuple))
468 0 : elog(ERROR, "could not find tuple for policy %u", policy_id);
469 :
470 : /*
471 : * Open and exclusive-lock the relation the policy belongs to.
472 : */
473 3 : relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
474 :
475 3 : rel = relation_open(relid, AccessExclusiveLock);
476 :
477 4 : if (rel->rd_rel->relkind != RELKIND_RELATION &&
478 1 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
479 0 : ereport(ERROR,
480 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
481 : errmsg("\"%s\" is not a table",
482 : RelationGetRelationName(rel))));
483 :
484 3 : if (!allowSystemTableMods && IsSystemRelation(rel))
485 0 : ereport(ERROR,
486 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
487 : errmsg("permission denied: \"%s\" is a system catalog",
488 : RelationGetRelationName(rel))));
489 :
490 : /* Get the current set of roles */
491 3 : roles_datum = heap_getattr(tuple,
492 : Anum_pg_policy_polroles,
493 : RelationGetDescr(pg_policy_rel),
494 : &attr_isnull);
495 :
496 3 : Assert(!attr_isnull);
497 :
498 3 : policy_roles = DatumGetArrayTypePCopy(roles_datum);
499 :
500 : /* We should be removing exactly one entry from the roles array */
501 3 : num_roles = ARR_DIMS(policy_roles)[0] - 1;
502 :
503 3 : Assert(num_roles >= 0);
504 :
505 : /* Must own relation. */
506 3 : if (pg_class_ownercheck(relid, GetUserId()))
507 3 : noperm = false; /* user is allowed to modify this policy */
508 : else
509 0 : ereport(WARNING,
510 : (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
511 : errmsg("role \"%s\" could not be removed from policy \"%s\" on \"%s\"",
512 : GetUserNameFromId(roleid, false),
513 : NameStr(((Form_pg_policy) GETSTRUCT(tuple))->polname),
514 : RelationGetRelationName(rel))));
515 :
516 : /*
517 : * If multiple roles exist on this policy, then remove the one we were
518 : * asked to and leave the rest.
519 : */
520 3 : if (!noperm && num_roles > 0)
521 : {
522 : int i,
523 : j;
524 2 : Oid *roles = (Oid *) ARR_DATA_PTR(policy_roles);
525 : Datum *role_oids;
526 : char *qual_value;
527 : Node *qual_expr;
528 2 : List *qual_parse_rtable = NIL;
529 : char *with_check_value;
530 : Node *with_check_qual;
531 2 : List *with_check_parse_rtable = NIL;
532 : Datum values[Natts_pg_policy];
533 : bool isnull[Natts_pg_policy];
534 : bool replaces[Natts_pg_policy];
535 : Datum value_datum;
536 : ArrayType *role_ids;
537 : HeapTuple new_tuple;
538 : ObjectAddress target;
539 : ObjectAddress myself;
540 :
541 : /* zero-clear */
542 2 : memset(values, 0, sizeof(values));
543 2 : memset(replaces, 0, sizeof(replaces));
544 2 : memset(isnull, 0, sizeof(isnull));
545 :
546 : /*
547 : * All of the dependencies will be removed from the policy and then
548 : * re-added. In order to get them correct, we need to extract out the
549 : * expressions in the policy and construct a parsestate just enough to
550 : * build the range table(s) to then pass to recordDependencyOnExpr().
551 : */
552 :
553 : /* Get policy qual, to update dependencies */
554 2 : value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
555 : RelationGetDescr(pg_policy_rel), &attr_isnull);
556 2 : if (!attr_isnull)
557 : {
558 : ParseState *qual_pstate;
559 :
560 : /* parsestate is built just to build the range table */
561 2 : qual_pstate = make_parsestate(NULL);
562 :
563 2 : qual_value = TextDatumGetCString(value_datum);
564 2 : qual_expr = stringToNode(qual_value);
565 :
566 : /* Add this rel to the parsestate's rangetable, for dependencies */
567 2 : addRangeTableEntryForRelation(qual_pstate, rel, NULL, false, false);
568 :
569 2 : qual_parse_rtable = qual_pstate->p_rtable;
570 2 : free_parsestate(qual_pstate);
571 : }
572 : else
573 0 : qual_expr = NULL;
574 :
575 : /* Get WITH CHECK qual, to update dependencies */
576 2 : value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
577 : RelationGetDescr(pg_policy_rel), &attr_isnull);
578 2 : if (!attr_isnull)
579 : {
580 : ParseState *with_check_pstate;
581 :
582 : /* parsestate is built just to build the range table */
583 0 : with_check_pstate = make_parsestate(NULL);
584 :
585 0 : with_check_value = TextDatumGetCString(value_datum);
586 0 : with_check_qual = stringToNode(with_check_value);
587 :
588 : /* Add this rel to the parsestate's rangetable, for dependencies */
589 0 : addRangeTableEntryForRelation(with_check_pstate, rel, NULL, false,
590 : false);
591 :
592 0 : with_check_parse_rtable = with_check_pstate->p_rtable;
593 0 : free_parsestate(with_check_pstate);
594 : }
595 : else
596 2 : with_check_qual = NULL;
597 :
598 : /* Rebuild the roles array to then update the pg_policy tuple with */
599 2 : role_oids = (Datum *) palloc(num_roles * sizeof(Datum));
600 6 : for (i = 0, j = 0; i < ARR_DIMS(policy_roles)[0]; i++)
601 : /* Copy over all of the roles which are not the one being removed */
602 4 : if (roles[i] != roleid)
603 2 : role_oids[j++] = ObjectIdGetDatum(roles[i]);
604 :
605 : /* We should have only removed the one role */
606 2 : Assert(j == num_roles);
607 :
608 : /* This is the array for the new tuple */
609 2 : role_ids = construct_array(role_oids, num_roles, OIDOID,
610 : sizeof(Oid), true, 'i');
611 :
612 2 : replaces[Anum_pg_policy_polroles - 1] = true;
613 2 : values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
614 :
615 2 : new_tuple = heap_modify_tuple(tuple,
616 : RelationGetDescr(pg_policy_rel),
617 : values, isnull, replaces);
618 2 : CatalogTupleUpdate(pg_policy_rel, &new_tuple->t_self, new_tuple);
619 :
620 : /* Remove all old dependencies. */
621 2 : deleteDependencyRecordsFor(PolicyRelationId, policy_id, false);
622 :
623 : /* Record the new set of dependencies */
624 2 : target.classId = RelationRelationId;
625 2 : target.objectId = relid;
626 2 : target.objectSubId = 0;
627 :
628 2 : myself.classId = PolicyRelationId;
629 2 : myself.objectId = policy_id;
630 2 : myself.objectSubId = 0;
631 :
632 2 : recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
633 :
634 2 : if (qual_expr)
635 2 : recordDependencyOnExpr(&myself, qual_expr, qual_parse_rtable,
636 : DEPENDENCY_NORMAL);
637 :
638 2 : if (with_check_qual)
639 0 : recordDependencyOnExpr(&myself, with_check_qual,
640 : with_check_parse_rtable,
641 : DEPENDENCY_NORMAL);
642 :
643 : /* Remove all the old shared dependencies (roles) */
644 2 : deleteSharedDependencyRecordsFor(PolicyRelationId, policy_id, 0);
645 :
646 : /* Record the new shared dependencies (roles) */
647 2 : target.classId = AuthIdRelationId;
648 2 : target.objectSubId = 0;
649 4 : for (i = 0; i < num_roles; i++)
650 : {
651 2 : target.objectId = DatumGetObjectId(role_oids[i]);
652 : /* no need for dependency on the public role */
653 2 : if (target.objectId != ACL_ID_PUBLIC)
654 2 : recordSharedDependencyOn(&myself, &target,
655 : SHARED_DEPENDENCY_POLICY);
656 : }
657 :
658 2 : InvokeObjectPostAlterHook(PolicyRelationId, policy_id, 0);
659 :
660 2 : heap_freetuple(new_tuple);
661 :
662 : /* Invalidate Relation Cache */
663 2 : CacheInvalidateRelcache(rel);
664 : }
665 :
666 : /* Clean up. */
667 3 : systable_endscan(sscan);
668 :
669 3 : relation_close(rel, NoLock);
670 :
671 3 : heap_close(pg_policy_rel, RowExclusiveLock);
672 :
673 3 : return (noperm || num_roles > 0);
674 : }
675 :
676 : /*
677 : * CreatePolicy -
678 : * handles the execution of the CREATE POLICY command.
679 : *
680 : * stmt - the CreatePolicyStmt that describes the policy to create.
681 : */
682 : ObjectAddress
683 81 : CreatePolicy(CreatePolicyStmt *stmt)
684 : {
685 : Relation pg_policy_rel;
686 : Oid policy_id;
687 : Relation target_table;
688 : Oid table_id;
689 : char polcmd;
690 : Datum *role_oids;
691 81 : int nitems = 0;
692 : ArrayType *role_ids;
693 : ParseState *qual_pstate;
694 : ParseState *with_check_pstate;
695 : RangeTblEntry *rte;
696 : Node *qual;
697 : Node *with_check_qual;
698 : ScanKeyData skey[2];
699 : SysScanDesc sscan;
700 : HeapTuple policy_tuple;
701 : Datum values[Natts_pg_policy];
702 : bool isnull[Natts_pg_policy];
703 : ObjectAddress target;
704 : ObjectAddress myself;
705 : int i;
706 :
707 : /* Parse command */
708 81 : polcmd = parse_policy_command(stmt->cmd_name);
709 :
710 : /*
711 : * If the command is SELECT or DELETE then WITH CHECK should be NULL.
712 : */
713 81 : if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR)
714 16 : && stmt->with_check != NULL)
715 0 : ereport(ERROR,
716 : (errcode(ERRCODE_SYNTAX_ERROR),
717 : errmsg("WITH CHECK cannot be applied to SELECT or DELETE")));
718 :
719 : /*
720 : * If the command is INSERT then WITH CHECK should be the only expression
721 : * provided.
722 : */
723 81 : if (polcmd == ACL_INSERT_CHR && stmt->qual != NULL)
724 0 : ereport(ERROR,
725 : (errcode(ERRCODE_SYNTAX_ERROR),
726 : errmsg("only WITH CHECK expression allowed for INSERT")));
727 :
728 : /* Collect role ids */
729 81 : role_oids = policy_role_list_to_array(stmt->roles, &nitems);
730 81 : role_ids = construct_array(role_oids, nitems, OIDOID,
731 : sizeof(Oid), true, 'i');
732 :
733 : /* Parse the supplied clause */
734 81 : qual_pstate = make_parsestate(NULL);
735 81 : with_check_pstate = make_parsestate(NULL);
736 :
737 : /* zero-clear */
738 81 : memset(values, 0, sizeof(values));
739 81 : memset(isnull, 0, sizeof(isnull));
740 :
741 : /* Get id of table. Also handles permissions checks. */
742 81 : table_id = RangeVarGetRelidExtended(stmt->table, AccessExclusiveLock,
743 : false, false,
744 : RangeVarCallbackForPolicy,
745 : (void *) stmt);
746 :
747 : /* Open target_table to build quals. No additional lock is necessary. */
748 81 : target_table = relation_open(table_id, NoLock);
749 :
750 : /* Add for the regular security quals */
751 81 : rte = addRangeTableEntryForRelation(qual_pstate, target_table,
752 : NULL, false, false);
753 81 : addRTEtoQuery(qual_pstate, rte, false, true, true);
754 :
755 : /* Add for the with-check quals */
756 81 : rte = addRangeTableEntryForRelation(with_check_pstate, target_table,
757 : NULL, false, false);
758 81 : addRTEtoQuery(with_check_pstate, rte, false, true, true);
759 :
760 81 : qual = transformWhereClause(qual_pstate,
761 81 : copyObject(stmt->qual),
762 : EXPR_KIND_POLICY,
763 : "POLICY");
764 :
765 80 : with_check_qual = transformWhereClause(with_check_pstate,
766 80 : copyObject(stmt->with_check),
767 : EXPR_KIND_POLICY,
768 : "POLICY");
769 :
770 : /* Fix up collation information */
771 80 : assign_expr_collations(qual_pstate, qual);
772 80 : assign_expr_collations(with_check_pstate, with_check_qual);
773 :
774 : /* Open pg_policy catalog */
775 80 : pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
776 :
777 : /* Set key - policy's relation id. */
778 80 : ScanKeyInit(&skey[0],
779 : Anum_pg_policy_polrelid,
780 : BTEqualStrategyNumber, F_OIDEQ,
781 : ObjectIdGetDatum(table_id));
782 :
783 : /* Set key - policy's name. */
784 80 : ScanKeyInit(&skey[1],
785 : Anum_pg_policy_polname,
786 : BTEqualStrategyNumber, F_NAMEEQ,
787 80 : CStringGetDatum(stmt->policy_name));
788 :
789 80 : sscan = systable_beginscan(pg_policy_rel,
790 : PolicyPolrelidPolnameIndexId, true, NULL, 2,
791 : skey);
792 :
793 80 : policy_tuple = systable_getnext(sscan);
794 :
795 : /* Complain if the policy name already exists for the table */
796 80 : if (HeapTupleIsValid(policy_tuple))
797 1 : ereport(ERROR,
798 : (errcode(ERRCODE_DUPLICATE_OBJECT),
799 : errmsg("policy \"%s\" for table \"%s\" already exists",
800 : stmt->policy_name, RelationGetRelationName(target_table))));
801 :
802 79 : values[Anum_pg_policy_polrelid - 1] = ObjectIdGetDatum(table_id);
803 79 : values[Anum_pg_policy_polname - 1] = DirectFunctionCall1(namein,
804 : CStringGetDatum(stmt->policy_name));
805 79 : values[Anum_pg_policy_polcmd - 1] = CharGetDatum(polcmd);
806 79 : values[Anum_pg_policy_polpermissive - 1] = BoolGetDatum(stmt->permissive);
807 79 : values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
808 :
809 : /* Add qual if present. */
810 79 : if (qual)
811 73 : values[Anum_pg_policy_polqual - 1] = CStringGetTextDatum(nodeToString(qual));
812 : else
813 6 : isnull[Anum_pg_policy_polqual - 1] = true;
814 :
815 : /* Add WITH CHECK qual if present */
816 79 : if (with_check_qual)
817 12 : values[Anum_pg_policy_polwithcheck - 1] = CStringGetTextDatum(nodeToString(with_check_qual));
818 : else
819 67 : isnull[Anum_pg_policy_polwithcheck - 1] = true;
820 :
821 79 : policy_tuple = heap_form_tuple(RelationGetDescr(pg_policy_rel), values,
822 : isnull);
823 :
824 79 : policy_id = CatalogTupleInsert(pg_policy_rel, policy_tuple);
825 :
826 : /* Record Dependencies */
827 79 : target.classId = RelationRelationId;
828 79 : target.objectId = table_id;
829 79 : target.objectSubId = 0;
830 :
831 79 : myself.classId = PolicyRelationId;
832 79 : myself.objectId = policy_id;
833 79 : myself.objectSubId = 0;
834 :
835 79 : recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
836 :
837 79 : recordDependencyOnExpr(&myself, qual, qual_pstate->p_rtable,
838 : DEPENDENCY_NORMAL);
839 :
840 79 : recordDependencyOnExpr(&myself, with_check_qual,
841 : with_check_pstate->p_rtable, DEPENDENCY_NORMAL);
842 :
843 : /* Register role dependencies */
844 79 : target.classId = AuthIdRelationId;
845 79 : target.objectSubId = 0;
846 161 : for (i = 0; i < nitems; i++)
847 : {
848 82 : target.objectId = DatumGetObjectId(role_oids[i]);
849 : /* no dependency if public */
850 82 : if (target.objectId != ACL_ID_PUBLIC)
851 15 : recordSharedDependencyOn(&myself, &target,
852 : SHARED_DEPENDENCY_POLICY);
853 : }
854 :
855 79 : InvokeObjectPostCreateHook(PolicyRelationId, policy_id, 0);
856 :
857 : /* Invalidate Relation Cache */
858 79 : CacheInvalidateRelcache(target_table);
859 :
860 : /* Clean up. */
861 79 : heap_freetuple(policy_tuple);
862 79 : free_parsestate(qual_pstate);
863 79 : free_parsestate(with_check_pstate);
864 79 : systable_endscan(sscan);
865 79 : relation_close(target_table, NoLock);
866 79 : heap_close(pg_policy_rel, RowExclusiveLock);
867 :
868 79 : return myself;
869 : }
870 :
871 : /*
872 : * AlterPolicy -
873 : * handles the execution of the ALTER POLICY command.
874 : *
875 : * stmt - the AlterPolicyStmt that describes the policy and how to alter it.
876 : */
877 : ObjectAddress
878 14 : AlterPolicy(AlterPolicyStmt *stmt)
879 : {
880 : Relation pg_policy_rel;
881 : Oid policy_id;
882 : Relation target_table;
883 : Oid table_id;
884 14 : Datum *role_oids = NULL;
885 14 : int nitems = 0;
886 14 : ArrayType *role_ids = NULL;
887 14 : List *qual_parse_rtable = NIL;
888 14 : List *with_check_parse_rtable = NIL;
889 14 : Node *qual = NULL;
890 14 : Node *with_check_qual = NULL;
891 : ScanKeyData skey[2];
892 : SysScanDesc sscan;
893 : HeapTuple policy_tuple;
894 : HeapTuple new_tuple;
895 : Datum values[Natts_pg_policy];
896 : bool isnull[Natts_pg_policy];
897 : bool replaces[Natts_pg_policy];
898 : ObjectAddress target;
899 : ObjectAddress myself;
900 : Datum polcmd_datum;
901 : char polcmd;
902 : bool polcmd_isnull;
903 : int i;
904 :
905 : /* Parse role_ids */
906 14 : if (stmt->roles != NULL)
907 : {
908 2 : role_oids = policy_role_list_to_array(stmt->roles, &nitems);
909 2 : role_ids = construct_array(role_oids, nitems, OIDOID,
910 : sizeof(Oid), true, 'i');
911 : }
912 :
913 : /* Get id of table. Also handles permissions checks. */
914 14 : table_id = RangeVarGetRelidExtended(stmt->table, AccessExclusiveLock,
915 : false, false,
916 : RangeVarCallbackForPolicy,
917 : (void *) stmt);
918 :
919 12 : target_table = relation_open(table_id, NoLock);
920 :
921 : /* Parse the using policy clause */
922 12 : if (stmt->qual)
923 : {
924 : RangeTblEntry *rte;
925 11 : ParseState *qual_pstate = make_parsestate(NULL);
926 :
927 11 : rte = addRangeTableEntryForRelation(qual_pstate, target_table,
928 : NULL, false, false);
929 :
930 11 : addRTEtoQuery(qual_pstate, rte, false, true, true);
931 :
932 11 : qual = transformWhereClause(qual_pstate, copyObject(stmt->qual),
933 : EXPR_KIND_POLICY,
934 : "POLICY");
935 :
936 : /* Fix up collation information */
937 11 : assign_expr_collations(qual_pstate, qual);
938 :
939 11 : qual_parse_rtable = qual_pstate->p_rtable;
940 11 : free_parsestate(qual_pstate);
941 : }
942 :
943 : /* Parse the with-check policy clause */
944 12 : if (stmt->with_check)
945 : {
946 : RangeTblEntry *rte;
947 0 : ParseState *with_check_pstate = make_parsestate(NULL);
948 :
949 0 : rte = addRangeTableEntryForRelation(with_check_pstate, target_table,
950 : NULL, false, false);
951 :
952 0 : addRTEtoQuery(with_check_pstate, rte, false, true, true);
953 :
954 0 : with_check_qual = transformWhereClause(with_check_pstate,
955 0 : copyObject(stmt->with_check),
956 : EXPR_KIND_POLICY,
957 : "POLICY");
958 :
959 : /* Fix up collation information */
960 0 : assign_expr_collations(with_check_pstate, with_check_qual);
961 :
962 0 : with_check_parse_rtable = with_check_pstate->p_rtable;
963 0 : free_parsestate(with_check_pstate);
964 : }
965 :
966 : /* zero-clear */
967 12 : memset(values, 0, sizeof(values));
968 12 : memset(replaces, 0, sizeof(replaces));
969 12 : memset(isnull, 0, sizeof(isnull));
970 :
971 : /* Find policy to update. */
972 12 : pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
973 :
974 : /* Set key - policy's relation id. */
975 12 : ScanKeyInit(&skey[0],
976 : Anum_pg_policy_polrelid,
977 : BTEqualStrategyNumber, F_OIDEQ,
978 : ObjectIdGetDatum(table_id));
979 :
980 : /* Set key - policy's name. */
981 12 : ScanKeyInit(&skey[1],
982 : Anum_pg_policy_polname,
983 : BTEqualStrategyNumber, F_NAMEEQ,
984 12 : CStringGetDatum(stmt->policy_name));
985 :
986 12 : sscan = systable_beginscan(pg_policy_rel,
987 : PolicyPolrelidPolnameIndexId, true, NULL, 2,
988 : skey);
989 :
990 12 : policy_tuple = systable_getnext(sscan);
991 :
992 : /* Check that the policy is found, raise an error if not. */
993 12 : if (!HeapTupleIsValid(policy_tuple))
994 0 : ereport(ERROR,
995 : (errcode(ERRCODE_UNDEFINED_OBJECT),
996 : errmsg("policy \"%s\" for table \"%s\" does not exist",
997 : stmt->policy_name,
998 : RelationGetRelationName(target_table))));
999 :
1000 : /* Get policy command */
1001 12 : polcmd_datum = heap_getattr(policy_tuple, Anum_pg_policy_polcmd,
1002 : RelationGetDescr(pg_policy_rel),
1003 : &polcmd_isnull);
1004 12 : Assert(!polcmd_isnull);
1005 12 : polcmd = DatumGetChar(polcmd_datum);
1006 :
1007 : /*
1008 : * If the command is SELECT or DELETE then WITH CHECK should be NULL.
1009 : */
1010 12 : if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR)
1011 0 : && stmt->with_check != NULL)
1012 0 : ereport(ERROR,
1013 : (errcode(ERRCODE_SYNTAX_ERROR),
1014 : errmsg("only USING expression allowed for SELECT, DELETE")));
1015 :
1016 : /*
1017 : * If the command is INSERT then WITH CHECK should be the only expression
1018 : * provided.
1019 : */
1020 12 : if ((polcmd == ACL_INSERT_CHR)
1021 0 : && stmt->qual != NULL)
1022 0 : ereport(ERROR,
1023 : (errcode(ERRCODE_SYNTAX_ERROR),
1024 : errmsg("only WITH CHECK expression allowed for INSERT")));
1025 :
1026 12 : policy_id = HeapTupleGetOid(policy_tuple);
1027 :
1028 12 : if (role_ids != NULL)
1029 : {
1030 2 : replaces[Anum_pg_policy_polroles - 1] = true;
1031 2 : values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
1032 : }
1033 : else
1034 : {
1035 : Oid *roles;
1036 : Datum roles_datum;
1037 : bool attr_isnull;
1038 : ArrayType *policy_roles;
1039 :
1040 : /*
1041 : * We need to pull the set of roles this policy applies to from what's
1042 : * in the catalog, so that we can recreate the dependencies correctly
1043 : * for the policy.
1044 : */
1045 :
1046 10 : roles_datum = heap_getattr(policy_tuple, Anum_pg_policy_polroles,
1047 : RelationGetDescr(pg_policy_rel),
1048 : &attr_isnull);
1049 10 : Assert(!attr_isnull);
1050 :
1051 10 : policy_roles = DatumGetArrayTypePCopy(roles_datum);
1052 :
1053 10 : roles = (Oid *) ARR_DATA_PTR(policy_roles);
1054 :
1055 10 : nitems = ARR_DIMS(policy_roles)[0];
1056 :
1057 10 : role_oids = (Datum *) palloc(nitems * sizeof(Datum));
1058 :
1059 21 : for (i = 0; i < nitems; i++)
1060 11 : role_oids[i] = ObjectIdGetDatum(roles[i]);
1061 : }
1062 :
1063 12 : if (qual != NULL)
1064 : {
1065 11 : replaces[Anum_pg_policy_polqual - 1] = true;
1066 : values[Anum_pg_policy_polqual - 1]
1067 11 : = CStringGetTextDatum(nodeToString(qual));
1068 : }
1069 : else
1070 : {
1071 : Datum value_datum;
1072 : bool attr_isnull;
1073 :
1074 : /*
1075 : * We need to pull the USING expression and build the range table for
1076 : * the policy from what's in the catalog, so that we can recreate the
1077 : * dependencies correctly for the policy.
1078 : */
1079 :
1080 : /* Check if the policy has a USING expr */
1081 1 : value_datum = heap_getattr(policy_tuple, Anum_pg_policy_polqual,
1082 : RelationGetDescr(pg_policy_rel),
1083 : &attr_isnull);
1084 1 : if (!attr_isnull)
1085 : {
1086 : char *qual_value;
1087 : ParseState *qual_pstate;
1088 :
1089 : /* parsestate is built just to build the range table */
1090 1 : qual_pstate = make_parsestate(NULL);
1091 :
1092 1 : qual_value = TextDatumGetCString(value_datum);
1093 1 : qual = stringToNode(qual_value);
1094 :
1095 : /* Add this rel to the parsestate's rangetable, for dependencies */
1096 1 : addRangeTableEntryForRelation(qual_pstate, target_table, NULL,
1097 : false, false);
1098 :
1099 1 : qual_parse_rtable = qual_pstate->p_rtable;
1100 1 : free_parsestate(qual_pstate);
1101 : }
1102 : }
1103 :
1104 12 : if (with_check_qual != NULL)
1105 : {
1106 0 : replaces[Anum_pg_policy_polwithcheck - 1] = true;
1107 : values[Anum_pg_policy_polwithcheck - 1]
1108 0 : = CStringGetTextDatum(nodeToString(with_check_qual));
1109 : }
1110 : else
1111 : {
1112 : Datum value_datum;
1113 : bool attr_isnull;
1114 :
1115 : /*
1116 : * We need to pull the WITH CHECK expression and build the range table
1117 : * for the policy from what's in the catalog, so that we can recreate
1118 : * the dependencies correctly for the policy.
1119 : */
1120 :
1121 : /* Check if the policy has a WITH CHECK expr */
1122 12 : value_datum = heap_getattr(policy_tuple, Anum_pg_policy_polwithcheck,
1123 : RelationGetDescr(pg_policy_rel),
1124 : &attr_isnull);
1125 12 : if (!attr_isnull)
1126 : {
1127 : char *with_check_value;
1128 : ParseState *with_check_pstate;
1129 :
1130 : /* parsestate is built just to build the range table */
1131 0 : with_check_pstate = make_parsestate(NULL);
1132 :
1133 0 : with_check_value = TextDatumGetCString(value_datum);
1134 0 : with_check_qual = stringToNode(with_check_value);
1135 :
1136 : /* Add this rel to the parsestate's rangetable, for dependencies */
1137 0 : addRangeTableEntryForRelation(with_check_pstate, target_table, NULL,
1138 : false, false);
1139 :
1140 0 : with_check_parse_rtable = with_check_pstate->p_rtable;
1141 0 : free_parsestate(with_check_pstate);
1142 : }
1143 : }
1144 :
1145 12 : new_tuple = heap_modify_tuple(policy_tuple,
1146 : RelationGetDescr(pg_policy_rel),
1147 : values, isnull, replaces);
1148 12 : CatalogTupleUpdate(pg_policy_rel, &new_tuple->t_self, new_tuple);
1149 :
1150 : /* Update Dependencies. */
1151 12 : deleteDependencyRecordsFor(PolicyRelationId, policy_id, false);
1152 :
1153 : /* Record Dependencies */
1154 12 : target.classId = RelationRelationId;
1155 12 : target.objectId = table_id;
1156 12 : target.objectSubId = 0;
1157 :
1158 12 : myself.classId = PolicyRelationId;
1159 12 : myself.objectId = policy_id;
1160 12 : myself.objectSubId = 0;
1161 :
1162 12 : recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
1163 :
1164 12 : recordDependencyOnExpr(&myself, qual, qual_parse_rtable, DEPENDENCY_NORMAL);
1165 :
1166 12 : recordDependencyOnExpr(&myself, with_check_qual, with_check_parse_rtable,
1167 : DEPENDENCY_NORMAL);
1168 :
1169 : /* Register role dependencies */
1170 12 : deleteSharedDependencyRecordsFor(PolicyRelationId, policy_id, 0);
1171 12 : target.classId = AuthIdRelationId;
1172 12 : target.objectSubId = 0;
1173 26 : for (i = 0; i < nitems; i++)
1174 : {
1175 14 : target.objectId = DatumGetObjectId(role_oids[i]);
1176 : /* no dependency if public */
1177 14 : if (target.objectId != ACL_ID_PUBLIC)
1178 5 : recordSharedDependencyOn(&myself, &target,
1179 : SHARED_DEPENDENCY_POLICY);
1180 : }
1181 :
1182 12 : InvokeObjectPostAlterHook(PolicyRelationId, policy_id, 0);
1183 :
1184 12 : heap_freetuple(new_tuple);
1185 :
1186 : /* Invalidate Relation Cache */
1187 12 : CacheInvalidateRelcache(target_table);
1188 :
1189 : /* Clean up. */
1190 12 : systable_endscan(sscan);
1191 12 : relation_close(target_table, NoLock);
1192 12 : heap_close(pg_policy_rel, RowExclusiveLock);
1193 :
1194 12 : return myself;
1195 : }
1196 :
1197 : /*
1198 : * rename_policy -
1199 : * change the name of a policy on a relation
1200 : */
1201 : ObjectAddress
1202 3 : rename_policy(RenameStmt *stmt)
1203 : {
1204 : Relation pg_policy_rel;
1205 : Relation target_table;
1206 : Oid table_id;
1207 : Oid opoloid;
1208 : ScanKeyData skey[2];
1209 : SysScanDesc sscan;
1210 : HeapTuple policy_tuple;
1211 : ObjectAddress address;
1212 :
1213 : /* Get id of table. Also handles permissions checks. */
1214 3 : table_id = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
1215 : false, false,
1216 : RangeVarCallbackForPolicy,
1217 : (void *) stmt);
1218 :
1219 3 : target_table = relation_open(table_id, NoLock);
1220 :
1221 3 : pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
1222 :
1223 : /* First pass -- check for conflict */
1224 :
1225 : /* Add key - policy's relation id. */
1226 3 : ScanKeyInit(&skey[0],
1227 : Anum_pg_policy_polrelid,
1228 : BTEqualStrategyNumber, F_OIDEQ,
1229 : ObjectIdGetDatum(table_id));
1230 :
1231 : /* Add key - policy's name. */
1232 3 : ScanKeyInit(&skey[1],
1233 : Anum_pg_policy_polname,
1234 : BTEqualStrategyNumber, F_NAMEEQ,
1235 3 : CStringGetDatum(stmt->newname));
1236 :
1237 3 : sscan = systable_beginscan(pg_policy_rel,
1238 : PolicyPolrelidPolnameIndexId, true, NULL, 2,
1239 : skey);
1240 :
1241 3 : if (HeapTupleIsValid(systable_getnext(sscan)))
1242 1 : ereport(ERROR,
1243 : (errcode(ERRCODE_DUPLICATE_OBJECT),
1244 : errmsg("policy \"%s\" for table \"%s\" already exists",
1245 : stmt->newname, RelationGetRelationName(target_table))));
1246 :
1247 2 : systable_endscan(sscan);
1248 :
1249 : /* Second pass -- find existing policy and update */
1250 : /* Add key - policy's relation id. */
1251 2 : ScanKeyInit(&skey[0],
1252 : Anum_pg_policy_polrelid,
1253 : BTEqualStrategyNumber, F_OIDEQ,
1254 : ObjectIdGetDatum(table_id));
1255 :
1256 : /* Add key - policy's name. */
1257 2 : ScanKeyInit(&skey[1],
1258 : Anum_pg_policy_polname,
1259 : BTEqualStrategyNumber, F_NAMEEQ,
1260 2 : CStringGetDatum(stmt->subname));
1261 :
1262 2 : sscan = systable_beginscan(pg_policy_rel,
1263 : PolicyPolrelidPolnameIndexId, true, NULL, 2,
1264 : skey);
1265 :
1266 2 : policy_tuple = systable_getnext(sscan);
1267 :
1268 : /* Complain if we did not find the policy */
1269 2 : if (!HeapTupleIsValid(policy_tuple))
1270 0 : ereport(ERROR,
1271 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1272 : errmsg("policy \"%s\" for table \"%s\" does not exist",
1273 : stmt->subname, RelationGetRelationName(target_table))));
1274 :
1275 2 : opoloid = HeapTupleGetOid(policy_tuple);
1276 :
1277 2 : policy_tuple = heap_copytuple(policy_tuple);
1278 :
1279 2 : namestrcpy(&((Form_pg_policy) GETSTRUCT(policy_tuple))->polname,
1280 2 : stmt->newname);
1281 :
1282 2 : CatalogTupleUpdate(pg_policy_rel, &policy_tuple->t_self, policy_tuple);
1283 :
1284 2 : InvokeObjectPostAlterHook(PolicyRelationId,
1285 : HeapTupleGetOid(policy_tuple), 0);
1286 :
1287 2 : ObjectAddressSet(address, PolicyRelationId, opoloid);
1288 :
1289 : /*
1290 : * Invalidate relation's relcache entry so that other backends (and this
1291 : * one too!) are sent SI message to make them rebuild relcache entries.
1292 : * (Ideally this should happen automatically...)
1293 : */
1294 2 : CacheInvalidateRelcache(target_table);
1295 :
1296 : /* Clean up. */
1297 2 : systable_endscan(sscan);
1298 2 : heap_close(pg_policy_rel, RowExclusiveLock);
1299 2 : relation_close(target_table, NoLock);
1300 :
1301 2 : return address;
1302 : }
1303 :
1304 : /*
1305 : * get_relation_policy_oid - Look up a policy by name to find its OID
1306 : *
1307 : * If missing_ok is false, throw an error if policy not found. If
1308 : * true, just return InvalidOid.
1309 : */
1310 : Oid
1311 19 : get_relation_policy_oid(Oid relid, const char *policy_name, bool missing_ok)
1312 : {
1313 : Relation pg_policy_rel;
1314 : ScanKeyData skey[2];
1315 : SysScanDesc sscan;
1316 : HeapTuple policy_tuple;
1317 : Oid policy_oid;
1318 :
1319 19 : pg_policy_rel = heap_open(PolicyRelationId, AccessShareLock);
1320 :
1321 : /* Add key - policy's relation id. */
1322 19 : ScanKeyInit(&skey[0],
1323 : Anum_pg_policy_polrelid,
1324 : BTEqualStrategyNumber, F_OIDEQ,
1325 : ObjectIdGetDatum(relid));
1326 :
1327 : /* Add key - policy's name. */
1328 19 : ScanKeyInit(&skey[1],
1329 : Anum_pg_policy_polname,
1330 : BTEqualStrategyNumber, F_NAMEEQ,
1331 : CStringGetDatum(policy_name));
1332 :
1333 19 : sscan = systable_beginscan(pg_policy_rel,
1334 : PolicyPolrelidPolnameIndexId, true, NULL, 2,
1335 : skey);
1336 :
1337 19 : policy_tuple = systable_getnext(sscan);
1338 :
1339 19 : if (!HeapTupleIsValid(policy_tuple))
1340 : {
1341 1 : if (!missing_ok)
1342 1 : ereport(ERROR,
1343 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1344 : errmsg("policy \"%s\" for table \"%s\" does not exist",
1345 : policy_name, get_rel_name(relid))));
1346 :
1347 0 : policy_oid = InvalidOid;
1348 : }
1349 : else
1350 18 : policy_oid = HeapTupleGetOid(policy_tuple);
1351 :
1352 : /* Clean up. */
1353 18 : systable_endscan(sscan);
1354 18 : heap_close(pg_policy_rel, AccessShareLock);
1355 :
1356 18 : return policy_oid;
1357 : }
1358 :
1359 : /*
1360 : * relation_has_policies - Determine if relation has any policies
1361 : */
1362 : bool
1363 3 : relation_has_policies(Relation rel)
1364 : {
1365 : Relation catalog;
1366 : ScanKeyData skey;
1367 : SysScanDesc sscan;
1368 : HeapTuple policy_tuple;
1369 3 : bool ret = false;
1370 :
1371 3 : catalog = heap_open(PolicyRelationId, AccessShareLock);
1372 3 : ScanKeyInit(&skey,
1373 : Anum_pg_policy_polrelid,
1374 : BTEqualStrategyNumber, F_OIDEQ,
1375 : ObjectIdGetDatum(RelationGetRelid(rel)));
1376 3 : sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
1377 : NULL, 1, &skey);
1378 3 : policy_tuple = systable_getnext(sscan);
1379 3 : if (HeapTupleIsValid(policy_tuple))
1380 1 : ret = true;
1381 :
1382 3 : systable_endscan(sscan);
1383 3 : heap_close(catalog, AccessShareLock);
1384 :
1385 3 : return ret;
1386 : }
|