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