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