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