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 : 728 : 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 : 728 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
72 [ - + ]: 728 : if (!HeapTupleIsValid(tuple))
73 : 0 : return;
74 : :
75 : 728 : classform = (Form_pg_class) GETSTRUCT(tuple);
76 : 728 : relkind = classform->relkind;
77 : :
78 : : /* Must own relation. */
79 [ + + ]: 728 : 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 [ + + ]: 720 : 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 [ + + + + ]: 716 : 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 [ + + - + ]: 715 : 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 : 715 : 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 : 611 : parse_policy_command(const char *cmd_name)
121 : : {
122 : : char polcmd;
123 : :
124 [ - + ]: 611 : if (!cmd_name)
125 [ # # ]: 0 : elog(ERROR, "unrecognized policy command");
126 : :
127 [ + + ]: 611 : if (strcmp(cmd_name, "all") == 0)
128 : 355 : polcmd = '*';
129 [ + + ]: 256 : else if (strcmp(cmd_name, "select") == 0)
130 : 101 : polcmd = ACL_SELECT_CHR;
131 [ + + ]: 155 : else if (strcmp(cmd_name, "insert") == 0)
132 : 45 : polcmd = ACL_INSERT_CHR;
133 [ + + ]: 110 : else if (strcmp(cmd_name, "update") == 0)
134 : 71 : polcmd = ACL_UPDATE_CHR;
135 [ + - ]: 39 : else if (strcmp(cmd_name, "delete") == 0)
136 : 39 : polcmd = ACL_DELETE_CHR;
137 : : else
138 [ # # ]: 0 : elog(ERROR, "unrecognized policy command");
139 : :
140 : 611 : 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 : 619 : policy_role_list_to_array(List *roles, int *num_roles)
150 : : {
151 : : Datum *role_oids;
152 : : ListCell *cell;
153 : 619 : int i = 0;
154 : :
155 : : /* Handle no roles being passed in as being for public */
156 [ - + ]: 619 : 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 : 619 : *num_roles = list_length(roles);
166 : 619 : role_oids = (Datum *) palloc(*num_roles * sizeof(Datum));
167 : :
168 [ + - + + : 776 : foreach(cell, roles)
+ + ]
169 : : {
170 : 647 : RoleSpec *spec = lfirst(cell);
171 : :
172 : : /*
173 : : * PUBLIC covers all roles, so it only makes sense alone.
174 : : */
175 [ + + ]: 647 : if (spec->roletype == ROLESPEC_PUBLIC)
176 : : {
177 [ - + ]: 490 : 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 : 490 : role_oids[0] = ObjectIdGetDatum(ACL_ID_PUBLIC);
186 : :
187 : 490 : return role_oids;
188 : : }
189 : : else
190 : 157 : role_oids[i++] =
191 : 157 : ObjectIdGetDatum(get_rolespec_oid(spec, false));
192 : : }
193 : :
194 : 129 : 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 : 1903 : RelationBuildRowSecurity(Relation relation)
206 : : {
207 : : MemoryContext rscxt;
208 : 1903 : 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 : 1903 : rscxt = AllocSetContextCreate(CurrentMemoryContext,
222 : : "row security descriptor",
223 : : ALLOCSET_SMALL_SIZES);
224 : 1903 : MemoryContextCopyAndSetIdentifier(rscxt,
225 : : RelationGetRelationName(relation));
226 : :
227 : 1903 : rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc));
228 : 1903 : 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 : 1903 : catalog = table_open(PolicyRelationId, AccessShareLock);
237 : :
238 : 1903 : ScanKeyInit(&skey,
239 : : Anum_pg_policy_polrelid,
240 : : BTEqualStrategyNumber, F_OIDEQ,
241 : : ObjectIdGetDatum(RelationGetRelid(relation)));
242 : :
243 : 1903 : sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
244 : : NULL, 1, &skey);
245 : :
246 [ + + ]: 4597 : while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
247 : : {
248 : 2694 : 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 : 2694 : 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 : 2694 : policy->polcmd = policy_form->polcmd;
264 : :
265 : : /* Get policy, permissive or restrictive */
266 : 2694 : policy->permissive = policy_form->polpermissive;
267 : :
268 : : /* Get policy name */
269 : 2694 : policy->policy_name =
270 : 2694 : MemoryContextStrdup(rscxt, NameStr(policy_form->polname));
271 : :
272 : : /* Get policy roles */
273 : 2694 : datum = heap_getattr(tuple, Anum_pg_policy_polroles,
274 : : RelationGetDescr(catalog), &isnull);
275 : : /* shouldn't be null, but let's check for luck */
276 [ - + ]: 2694 : if (isnull)
277 [ # # ]: 0 : elog(ERROR, "unexpected null value in pg_policy.polroles");
278 : 2694 : MemoryContextSwitchTo(rscxt);
279 : 2694 : policy->roles = DatumGetArrayTypePCopy(datum);
280 : 2694 : MemoryContextSwitchTo(oldcxt);
281 : :
282 : : /* Get policy qual */
283 : 2694 : datum = heap_getattr(tuple, Anum_pg_policy_polqual,
284 : : RelationGetDescr(catalog), &isnull);
285 [ + + ]: 2694 : if (!isnull)
286 : : {
287 : 2377 : str_value = TextDatumGetCString(datum);
288 : 2377 : MemoryContextSwitchTo(rscxt);
289 : 2377 : policy->qual = (Expr *) stringToNode(str_value);
290 : 2377 : MemoryContextSwitchTo(oldcxt);
291 : 2377 : pfree(str_value);
292 : : }
293 : : else
294 : 317 : policy->qual = NULL;
295 : :
296 : : /* Get WITH CHECK qual */
297 : 2694 : datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
298 : : RelationGetDescr(catalog), &isnull);
299 [ + + ]: 2694 : if (!isnull)
300 : : {
301 : 743 : str_value = TextDatumGetCString(datum);
302 : 743 : MemoryContextSwitchTo(rscxt);
303 : 743 : policy->with_check_qual = (Expr *) stringToNode(str_value);
304 : 743 : MemoryContextSwitchTo(oldcxt);
305 : 743 : pfree(str_value);
306 : : }
307 : : else
308 : 1951 : policy->with_check_qual = NULL;
309 : :
310 : : /* We want to cache whether there are SubLinks in these expressions */
311 [ + + + + ]: 5100 : policy->hassublinks = checkExprHasSubLink((Node *) policy->qual) ||
312 : 2406 : 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 : 2694 : MemoryContextSwitchTo(rscxt);
319 : 2694 : rsdesc->policies = lcons(policy, rsdesc->policies);
320 : 2694 : MemoryContextSwitchTo(oldcxt);
321 : : }
322 : :
323 : 1903 : systable_endscan(sscan);
324 : 1903 : 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 : 1903 : MemoryContextSetParent(rscxt, CacheMemoryContext);
332 : :
333 : 1903 : relation->rd_rsdesc = rsdesc;
334 : 1903 : }
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 : 530 : 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 : 530 : pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
354 : :
355 : : /*
356 : : * Find the policy to delete.
357 : : */
358 : 530 : ScanKeyInit(&skey[0],
359 : : Anum_pg_policy_oid,
360 : : BTEqualStrategyNumber, F_OIDEQ,
361 : : ObjectIdGetDatum(policy_id));
362 : :
363 : 530 : sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
364 : : NULL, 1, skey);
365 : :
366 : 530 : tuple = systable_getnext(sscan);
367 : :
368 : : /* If the policy exists, then remove it, otherwise raise an error. */
369 [ - + ]: 530 : 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 : 530 : relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
379 : :
380 : 530 : rel = table_open(relid, AccessExclusiveLock);
381 [ + + ]: 530 : 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 [ + - - + ]: 530 : 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 : 530 : CatalogTupleDelete(pg_policy_rel, &tuple->t_self);
395 : :
396 : 530 : 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 : 530 : CacheInvalidateRelcache(rel);
407 : :
408 : 530 : table_close(rel, NoLock);
409 : :
410 : : /* Clean up */
411 : 530 : table_close(pg_policy_rel, RowExclusiveLock);
412 : 530 : }
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 : 611 : 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 : 611 : 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 : 611 : 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 [ + + + + ]: 611 : if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR)
612 [ - + ]: 140 : && 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 [ + + - + ]: 611 : 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 : 611 : role_oids = policy_role_list_to_array(stmt->roles, &nitems);
628 : 611 : role_ids = construct_array_builtin(role_oids, nitems, OIDOID);
629 : :
630 : : /* Parse the supplied clause */
631 : 611 : qual_pstate = make_parsestate(NULL);
632 : 611 : with_check_pstate = make_parsestate(NULL);
633 : :
634 : : /* zero-clear */
635 : 611 : memset(values, 0, sizeof(values));
636 : 611 : memset(isnull, 0, sizeof(isnull));
637 : :
638 : : /* Get id of table. Also handles permissions checks. */
639 : 611 : 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 : 606 : target_table = relation_open(table_id, NoLock);
646 : :
647 : : /* Add for the regular security quals */
648 : 606 : nsitem = addRangeTableEntryForRelation(qual_pstate, target_table,
649 : : AccessShareLock,
650 : : NULL, false, false);
651 : 606 : addNSItemToQuery(qual_pstate, nsitem, false, true, true);
652 : :
653 : : /* Add for the with-check quals */
654 : 606 : nsitem = addRangeTableEntryForRelation(with_check_pstate, target_table,
655 : : AccessShareLock,
656 : : NULL, false, false);
657 : 606 : addNSItemToQuery(with_check_pstate, nsitem, false, true, true);
658 : :
659 : 606 : qual = transformWhereClause(qual_pstate,
660 : : stmt->qual,
661 : : EXPR_KIND_POLICY,
662 : : "POLICY");
663 : :
664 : 602 : with_check_qual = transformWhereClause(with_check_pstate,
665 : : stmt->with_check,
666 : : EXPR_KIND_POLICY,
667 : : "POLICY");
668 : :
669 : : /* Fix up collation information */
670 : 602 : assign_expr_collations(qual_pstate, qual);
671 : 602 : assign_expr_collations(with_check_pstate, with_check_qual);
672 : :
673 : : /* Open pg_policy catalog */
674 : 602 : pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
675 : :
676 : : /* Set key - policy's relation id. */
677 : 602 : ScanKeyInit(&skey[0],
678 : : Anum_pg_policy_polrelid,
679 : : BTEqualStrategyNumber, F_OIDEQ,
680 : : ObjectIdGetDatum(table_id));
681 : :
682 : : /* Set key - policy's name. */
683 : 602 : ScanKeyInit(&skey[1],
684 : : Anum_pg_policy_polname,
685 : : BTEqualStrategyNumber, F_NAMEEQ,
686 : 602 : CStringGetDatum(stmt->policy_name));
687 : :
688 : 602 : sscan = systable_beginscan(pg_policy_rel,
689 : : PolicyPolrelidPolnameIndexId, true, NULL, 2,
690 : : skey);
691 : :
692 : 602 : policy_tuple = systable_getnext(sscan);
693 : :
694 : : /* Complain if the policy name already exists for the table */
695 [ + + ]: 602 : 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 : 598 : policy_id = GetNewOidWithIndex(pg_policy_rel, PolicyOidIndexId,
702 : : Anum_pg_policy_oid);
703 : 598 : values[Anum_pg_policy_oid - 1] = ObjectIdGetDatum(policy_id);
704 : 598 : values[Anum_pg_policy_polrelid - 1] = ObjectIdGetDatum(table_id);
705 : 598 : values[Anum_pg_policy_polname - 1] = DirectFunctionCall1(namein,
706 : : CStringGetDatum(stmt->policy_name));
707 : 598 : values[Anum_pg_policy_polcmd - 1] = CharGetDatum(polcmd);
708 : 598 : values[Anum_pg_policy_polpermissive - 1] = BoolGetDatum(stmt->permissive);
709 : 598 : values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
710 : :
711 : : /* Add qual if present. */
712 [ + + ]: 598 : if (qual)
713 : 548 : values[Anum_pg_policy_polqual - 1] = CStringGetTextDatum(nodeToString(qual));
714 : : else
715 : 50 : isnull[Anum_pg_policy_polqual - 1] = true;
716 : :
717 : : /* Add WITH CHECK qual if present */
718 [ + + ]: 598 : if (with_check_qual)
719 : 115 : values[Anum_pg_policy_polwithcheck - 1] = CStringGetTextDatum(nodeToString(with_check_qual));
720 : : else
721 : 483 : isnull[Anum_pg_policy_polwithcheck - 1] = true;
722 : :
723 : 598 : policy_tuple = heap_form_tuple(RelationGetDescr(pg_policy_rel), values,
724 : : isnull);
725 : :
726 : 598 : CatalogTupleInsert(pg_policy_rel, policy_tuple);
727 : :
728 : : /* Record Dependencies */
729 : 598 : target.classId = RelationRelationId;
730 : 598 : target.objectId = table_id;
731 : 598 : target.objectSubId = 0;
732 : :
733 : 598 : myself.classId = PolicyRelationId;
734 : 598 : myself.objectId = policy_id;
735 : 598 : myself.objectSubId = 0;
736 : :
737 : 598 : recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
738 : :
739 : 598 : CheckUsageOnTypesInExpr(qual, qual_pstate->p_rtable, GetUserId());
740 : 594 : recordDependencyOnExpr(&myself, qual, qual_pstate->p_rtable,
741 : : DEPENDENCY_NORMAL);
742 : :
743 : 594 : CheckUsageOnTypesInExpr(with_check_qual, with_check_pstate->p_rtable,
744 : : GetUserId());
745 : 594 : recordDependencyOnExpr(&myself, with_check_qual,
746 : : with_check_pstate->p_rtable, DEPENDENCY_NORMAL);
747 : :
748 : : /* Register role dependencies */
749 : 594 : target.classId = AuthIdRelationId;
750 : 594 : target.objectSubId = 0;
751 [ + + ]: 1212 : for (i = 0; i < nitems; i++)
752 : : {
753 : 618 : target.objectId = DatumGetObjectId(role_oids[i]);
754 : : /* no dependency if public */
755 [ + + ]: 618 : if (target.objectId != ACL_ID_PUBLIC)
756 : 145 : recordSharedDependencyOn(&myself, &target,
757 : : SHARED_DEPENDENCY_POLICY);
758 : : }
759 : :
760 [ - + ]: 594 : InvokeObjectPostCreateHook(PolicyRelationId, policy_id, 0);
761 : :
762 : : /* Invalidate Relation Cache */
763 : 594 : CacheInvalidateRelcache(target_table);
764 : :
765 : : /* Clean up. */
766 : 594 : heap_freetuple(policy_tuple);
767 : 594 : free_parsestate(qual_pstate);
768 : 594 : free_parsestate(with_check_pstate);
769 : 594 : systable_endscan(sscan);
770 : 594 : relation_close(target_table, NoLock);
771 : 594 : table_close(pg_policy_rel, RowExclusiveLock);
772 : :
773 : 594 : return myself;
774 : : }
775 : :
776 : : /*
777 : : * AlterPolicy -
778 : : * handles the execution of the ALTER POLICY command.
779 : : *
780 : : * stmt - the AlterPolicyStmt that describes the policy and how to alter it.
781 : : */
782 : : ObjectAddress
783 : 56 : AlterPolicy(AlterPolicyStmt *stmt)
784 : : {
785 : : Relation pg_policy_rel;
786 : : Oid policy_id;
787 : : Relation target_table;
788 : : Oid table_id;
789 : 56 : Datum *role_oids = NULL;
790 : 56 : int nitems = 0;
791 : 56 : ArrayType *role_ids = NULL;
792 : 56 : List *qual_parse_rtable = NIL;
793 : 56 : List *with_check_parse_rtable = NIL;
794 : 56 : Node *qual = NULL;
795 : 56 : Node *with_check_qual = NULL;
796 : : ScanKeyData skey[2];
797 : : SysScanDesc sscan;
798 : : HeapTuple policy_tuple;
799 : : HeapTuple new_tuple;
800 : : Datum values[Natts_pg_policy];
801 : : bool isnull[Natts_pg_policy];
802 : : bool replaces[Natts_pg_policy];
803 : : ObjectAddress target;
804 : : ObjectAddress myself;
805 : : Datum polcmd_datum;
806 : : char polcmd;
807 : : bool polcmd_isnull;
808 : : int i;
809 : :
810 : : /* Parse role_ids */
811 [ + + ]: 56 : if (stmt->roles != NULL)
812 : : {
813 : 8 : role_oids = policy_role_list_to_array(stmt->roles, &nitems);
814 : 8 : role_ids = construct_array_builtin(role_oids, nitems, OIDOID);
815 : : }
816 : :
817 : : /* Get id of table. Also handles permissions checks. */
818 : 56 : table_id = RangeVarGetRelidExtended(stmt->table, AccessExclusiveLock,
819 : : 0,
820 : : RangeVarCallbackForPolicy,
821 : : stmt);
822 : :
823 : 48 : target_table = relation_open(table_id, NoLock);
824 : :
825 : : /* Parse the using policy clause */
826 [ + + ]: 48 : if (stmt->qual)
827 : : {
828 : : ParseNamespaceItem *nsitem;
829 : 44 : ParseState *qual_pstate = make_parsestate(NULL);
830 : :
831 : 44 : nsitem = addRangeTableEntryForRelation(qual_pstate, target_table,
832 : : AccessShareLock,
833 : : NULL, false, false);
834 : :
835 : 44 : addNSItemToQuery(qual_pstate, nsitem, false, true, true);
836 : :
837 : 44 : qual = transformWhereClause(qual_pstate, stmt->qual,
838 : : EXPR_KIND_POLICY,
839 : : "POLICY");
840 : :
841 : : /* Fix up collation information */
842 : 44 : assign_expr_collations(qual_pstate, qual);
843 : :
844 : 44 : qual_parse_rtable = qual_pstate->p_rtable;
845 : 44 : free_parsestate(qual_pstate);
846 : : }
847 : :
848 : : /* Parse the with-check policy clause */
849 [ - + ]: 48 : if (stmt->with_check)
850 : : {
851 : : ParseNamespaceItem *nsitem;
852 : 0 : ParseState *with_check_pstate = make_parsestate(NULL);
853 : :
854 : 0 : nsitem = addRangeTableEntryForRelation(with_check_pstate, target_table,
855 : : AccessShareLock,
856 : : NULL, false, false);
857 : :
858 : 0 : addNSItemToQuery(with_check_pstate, nsitem, false, true, true);
859 : :
860 : 0 : with_check_qual = transformWhereClause(with_check_pstate,
861 : : stmt->with_check,
862 : : EXPR_KIND_POLICY,
863 : : "POLICY");
864 : :
865 : : /* Fix up collation information */
866 : 0 : assign_expr_collations(with_check_pstate, with_check_qual);
867 : :
868 : 0 : with_check_parse_rtable = with_check_pstate->p_rtable;
869 : 0 : free_parsestate(with_check_pstate);
870 : : }
871 : :
872 : : /* zero-clear */
873 : 48 : memset(values, 0, sizeof(values));
874 : 48 : memset(replaces, 0, sizeof(replaces));
875 : 48 : memset(isnull, 0, sizeof(isnull));
876 : :
877 : : /* Find policy to update. */
878 : 48 : pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
879 : :
880 : : /* Set key - policy's relation id. */
881 : 48 : ScanKeyInit(&skey[0],
882 : : Anum_pg_policy_polrelid,
883 : : BTEqualStrategyNumber, F_OIDEQ,
884 : : ObjectIdGetDatum(table_id));
885 : :
886 : : /* Set key - policy's name. */
887 : 48 : ScanKeyInit(&skey[1],
888 : : Anum_pg_policy_polname,
889 : : BTEqualStrategyNumber, F_NAMEEQ,
890 : 48 : CStringGetDatum(stmt->policy_name));
891 : :
892 : 48 : sscan = systable_beginscan(pg_policy_rel,
893 : : PolicyPolrelidPolnameIndexId, true, NULL, 2,
894 : : skey);
895 : :
896 : 48 : policy_tuple = systable_getnext(sscan);
897 : :
898 : : /* Check that the policy is found, raise an error if not. */
899 [ - + ]: 48 : if (!HeapTupleIsValid(policy_tuple))
900 [ # # ]: 0 : ereport(ERROR,
901 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
902 : : errmsg("policy \"%s\" for table \"%s\" does not exist",
903 : : stmt->policy_name,
904 : : RelationGetRelationName(target_table))));
905 : :
906 : : /* Get policy command */
907 : 48 : polcmd_datum = heap_getattr(policy_tuple, Anum_pg_policy_polcmd,
908 : : RelationGetDescr(pg_policy_rel),
909 : : &polcmd_isnull);
910 : : Assert(!polcmd_isnull);
911 : 48 : polcmd = DatumGetChar(polcmd_datum);
912 : :
913 : : /*
914 : : * If the command is SELECT or DELETE then WITH CHECK should be NULL.
915 : : */
916 [ + - - + ]: 48 : if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR)
917 [ # # ]: 0 : && stmt->with_check != NULL)
918 [ # # ]: 0 : ereport(ERROR,
919 : : (errcode(ERRCODE_SYNTAX_ERROR),
920 : : errmsg("only USING expression allowed for SELECT, DELETE")));
921 : :
922 : : /*
923 : : * If the command is INSERT then WITH CHECK should be the only expression
924 : : * provided.
925 : : */
926 [ - + ]: 48 : if ((polcmd == ACL_INSERT_CHR)
927 [ # # ]: 0 : && stmt->qual != NULL)
928 [ # # ]: 0 : ereport(ERROR,
929 : : (errcode(ERRCODE_SYNTAX_ERROR),
930 : : errmsg("only WITH CHECK expression allowed for INSERT")));
931 : :
932 : 48 : policy_id = ((Form_pg_policy) GETSTRUCT(policy_tuple))->oid;
933 : :
934 [ + + ]: 48 : if (role_ids != NULL)
935 : : {
936 : 8 : replaces[Anum_pg_policy_polroles - 1] = true;
937 : 8 : values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
938 : : }
939 : : else
940 : : {
941 : : Oid *roles;
942 : : Datum roles_datum;
943 : : bool attr_isnull;
944 : : ArrayType *policy_roles;
945 : :
946 : : /*
947 : : * We need to pull the set of roles this policy applies to from what's
948 : : * in the catalog, so that we can recreate the dependencies correctly
949 : : * for the policy.
950 : : */
951 : :
952 : 40 : roles_datum = heap_getattr(policy_tuple, Anum_pg_policy_polroles,
953 : : RelationGetDescr(pg_policy_rel),
954 : : &attr_isnull);
955 : : Assert(!attr_isnull);
956 : :
957 : 40 : policy_roles = DatumGetArrayTypePCopy(roles_datum);
958 : :
959 [ - + ]: 40 : roles = (Oid *) ARR_DATA_PTR(policy_roles);
960 : :
961 : 40 : nitems = ARR_DIMS(policy_roles)[0];
962 : :
963 : 40 : role_oids = palloc_array(Datum, nitems);
964 : :
965 [ + + ]: 84 : for (i = 0; i < nitems; i++)
966 : 44 : role_oids[i] = ObjectIdGetDatum(roles[i]);
967 : : }
968 : :
969 [ + + ]: 48 : if (qual != NULL)
970 : : {
971 : 44 : replaces[Anum_pg_policy_polqual - 1] = true;
972 : : values[Anum_pg_policy_polqual - 1]
973 : 44 : = CStringGetTextDatum(nodeToString(qual));
974 : : }
975 : : else
976 : : {
977 : : Datum value_datum;
978 : : bool attr_isnull;
979 : :
980 : : /*
981 : : * We need to pull the USING expression and build the range table for
982 : : * the policy from what's in the catalog, so that we can recreate the
983 : : * dependencies correctly for the policy.
984 : : */
985 : :
986 : : /* Check if the policy has a USING expr */
987 : 4 : value_datum = heap_getattr(policy_tuple, Anum_pg_policy_polqual,
988 : : RelationGetDescr(pg_policy_rel),
989 : : &attr_isnull);
990 [ + - ]: 4 : if (!attr_isnull)
991 : : {
992 : : char *qual_value;
993 : : ParseState *qual_pstate;
994 : :
995 : : /* parsestate is built just to build the range table */
996 : 4 : qual_pstate = make_parsestate(NULL);
997 : :
998 : 4 : qual_value = TextDatumGetCString(value_datum);
999 : 4 : qual = stringToNode(qual_value);
1000 : :
1001 : : /* Add this rel to the parsestate's rangetable, for dependencies */
1002 : 4 : (void) addRangeTableEntryForRelation(qual_pstate, target_table,
1003 : : AccessShareLock,
1004 : : NULL, false, false);
1005 : :
1006 : 4 : qual_parse_rtable = qual_pstate->p_rtable;
1007 : 4 : free_parsestate(qual_pstate);
1008 : : }
1009 : : }
1010 : :
1011 [ - + ]: 48 : if (with_check_qual != NULL)
1012 : : {
1013 : 0 : replaces[Anum_pg_policy_polwithcheck - 1] = true;
1014 : : values[Anum_pg_policy_polwithcheck - 1]
1015 : 0 : = CStringGetTextDatum(nodeToString(with_check_qual));
1016 : : }
1017 : : else
1018 : : {
1019 : : Datum value_datum;
1020 : : bool attr_isnull;
1021 : :
1022 : : /*
1023 : : * We need to pull the WITH CHECK expression and build the range table
1024 : : * for the policy from what's in the catalog, so that we can recreate
1025 : : * the dependencies correctly for the policy.
1026 : : */
1027 : :
1028 : : /* Check if the policy has a WITH CHECK expr */
1029 : 48 : value_datum = heap_getattr(policy_tuple, Anum_pg_policy_polwithcheck,
1030 : : RelationGetDescr(pg_policy_rel),
1031 : : &attr_isnull);
1032 [ - + ]: 48 : if (!attr_isnull)
1033 : : {
1034 : : char *with_check_value;
1035 : : ParseState *with_check_pstate;
1036 : :
1037 : : /* parsestate is built just to build the range table */
1038 : 0 : with_check_pstate = make_parsestate(NULL);
1039 : :
1040 : 0 : with_check_value = TextDatumGetCString(value_datum);
1041 : 0 : with_check_qual = stringToNode(with_check_value);
1042 : :
1043 : : /* Add this rel to the parsestate's rangetable, for dependencies */
1044 : 0 : (void) addRangeTableEntryForRelation(with_check_pstate,
1045 : : target_table,
1046 : : AccessShareLock,
1047 : : NULL, false, false);
1048 : :
1049 : 0 : with_check_parse_rtable = with_check_pstate->p_rtable;
1050 : 0 : free_parsestate(with_check_pstate);
1051 : : }
1052 : : }
1053 : :
1054 : 48 : new_tuple = heap_modify_tuple(policy_tuple,
1055 : : RelationGetDescr(pg_policy_rel),
1056 : : values, isnull, replaces);
1057 : 48 : CatalogTupleUpdate(pg_policy_rel, &new_tuple->t_self, new_tuple);
1058 : :
1059 : : /* Update Dependencies. */
1060 : 48 : deleteDependencyRecordsFor(PolicyRelationId, policy_id, false);
1061 : :
1062 : : /* Record Dependencies */
1063 : 48 : target.classId = RelationRelationId;
1064 : 48 : target.objectId = table_id;
1065 : 48 : target.objectSubId = 0;
1066 : :
1067 : 48 : myself.classId = PolicyRelationId;
1068 : 48 : myself.objectId = policy_id;
1069 : 48 : myself.objectSubId = 0;
1070 : :
1071 : 48 : recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
1072 : :
1073 [ + + ]: 48 : if (stmt->qual)
1074 : 44 : CheckUsageOnTypesInExpr(qual, qual_parse_rtable, GetUserId());
1075 : 48 : recordDependencyOnExpr(&myself, qual, qual_parse_rtable, DEPENDENCY_NORMAL);
1076 : :
1077 [ - + ]: 48 : if (stmt->with_check)
1078 : 0 : CheckUsageOnTypesInExpr(with_check_qual, with_check_parse_rtable,
1079 : : GetUserId());
1080 : 48 : recordDependencyOnExpr(&myself, with_check_qual, with_check_parse_rtable,
1081 : : DEPENDENCY_NORMAL);
1082 : :
1083 : : /* Register role dependencies */
1084 : 48 : deleteSharedDependencyRecordsFor(PolicyRelationId, policy_id, 0);
1085 : 48 : target.classId = AuthIdRelationId;
1086 : 48 : target.objectSubId = 0;
1087 [ + + ]: 104 : for (i = 0; i < nitems; i++)
1088 : : {
1089 : 56 : target.objectId = DatumGetObjectId(role_oids[i]);
1090 : : /* no dependency if public */
1091 [ + + ]: 56 : if (target.objectId != ACL_ID_PUBLIC)
1092 : 20 : recordSharedDependencyOn(&myself, &target,
1093 : : SHARED_DEPENDENCY_POLICY);
1094 : : }
1095 : :
1096 [ - + ]: 48 : InvokeObjectPostAlterHook(PolicyRelationId, policy_id, 0);
1097 : :
1098 : 48 : heap_freetuple(new_tuple);
1099 : :
1100 : : /* Invalidate Relation Cache */
1101 : 48 : CacheInvalidateRelcache(target_table);
1102 : :
1103 : : /* Clean up. */
1104 : 48 : systable_endscan(sscan);
1105 : 48 : relation_close(target_table, NoLock);
1106 : 48 : table_close(pg_policy_rel, RowExclusiveLock);
1107 : :
1108 : 48 : return myself;
1109 : : }
1110 : :
1111 : : /*
1112 : : * rename_policy -
1113 : : * change the name of a policy on a relation
1114 : : */
1115 : : ObjectAddress
1116 : 12 : rename_policy(RenameStmt *stmt)
1117 : : {
1118 : : Relation pg_policy_rel;
1119 : : Relation target_table;
1120 : : Oid table_id;
1121 : : Oid opoloid;
1122 : : ScanKeyData skey[2];
1123 : : SysScanDesc sscan;
1124 : : HeapTuple policy_tuple;
1125 : : ObjectAddress address;
1126 : :
1127 : : /* Get id of table. Also handles permissions checks. */
1128 : 12 : table_id = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
1129 : : 0,
1130 : : RangeVarCallbackForPolicy,
1131 : : stmt);
1132 : :
1133 : 12 : target_table = relation_open(table_id, NoLock);
1134 : :
1135 : 12 : pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
1136 : :
1137 : : /* First pass -- check for conflict */
1138 : :
1139 : : /* Add key - policy's relation id. */
1140 : 12 : ScanKeyInit(&skey[0],
1141 : : Anum_pg_policy_polrelid,
1142 : : BTEqualStrategyNumber, F_OIDEQ,
1143 : : ObjectIdGetDatum(table_id));
1144 : :
1145 : : /* Add key - policy's name. */
1146 : 12 : ScanKeyInit(&skey[1],
1147 : : Anum_pg_policy_polname,
1148 : : BTEqualStrategyNumber, F_NAMEEQ,
1149 : 12 : CStringGetDatum(stmt->newname));
1150 : :
1151 : 12 : sscan = systable_beginscan(pg_policy_rel,
1152 : : PolicyPolrelidPolnameIndexId, true, NULL, 2,
1153 : : skey);
1154 : :
1155 [ + + ]: 12 : if (HeapTupleIsValid(systable_getnext(sscan)))
1156 [ + - ]: 4 : ereport(ERROR,
1157 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
1158 : : errmsg("policy \"%s\" for table \"%s\" already exists",
1159 : : stmt->newname, RelationGetRelationName(target_table))));
1160 : :
1161 : 8 : systable_endscan(sscan);
1162 : :
1163 : : /* Second pass -- find existing policy and update */
1164 : : /* Add key - policy's relation id. */
1165 : 8 : ScanKeyInit(&skey[0],
1166 : : Anum_pg_policy_polrelid,
1167 : : BTEqualStrategyNumber, F_OIDEQ,
1168 : : ObjectIdGetDatum(table_id));
1169 : :
1170 : : /* Add key - policy's name. */
1171 : 8 : ScanKeyInit(&skey[1],
1172 : : Anum_pg_policy_polname,
1173 : : BTEqualStrategyNumber, F_NAMEEQ,
1174 : 8 : CStringGetDatum(stmt->subname));
1175 : :
1176 : 8 : sscan = systable_beginscan(pg_policy_rel,
1177 : : PolicyPolrelidPolnameIndexId, true, NULL, 2,
1178 : : skey);
1179 : :
1180 : 8 : policy_tuple = systable_getnext(sscan);
1181 : :
1182 : : /* Complain if we did not find the policy */
1183 [ - + ]: 8 : if (!HeapTupleIsValid(policy_tuple))
1184 [ # # ]: 0 : ereport(ERROR,
1185 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
1186 : : errmsg("policy \"%s\" for table \"%s\" does not exist",
1187 : : stmt->subname, RelationGetRelationName(target_table))));
1188 : :
1189 : 8 : opoloid = ((Form_pg_policy) GETSTRUCT(policy_tuple))->oid;
1190 : :
1191 : 8 : policy_tuple = heap_copytuple(policy_tuple);
1192 : :
1193 : 8 : namestrcpy(&((Form_pg_policy) GETSTRUCT(policy_tuple))->polname,
1194 : 8 : stmt->newname);
1195 : :
1196 : 8 : CatalogTupleUpdate(pg_policy_rel, &policy_tuple->t_self, policy_tuple);
1197 : :
1198 [ - + ]: 8 : InvokeObjectPostAlterHook(PolicyRelationId, opoloid, 0);
1199 : :
1200 : 8 : ObjectAddressSet(address, PolicyRelationId, opoloid);
1201 : :
1202 : : /*
1203 : : * Invalidate relation's relcache entry so that other backends (and this
1204 : : * one too!) are sent SI message to make them rebuild relcache entries.
1205 : : * (Ideally this should happen automatically...)
1206 : : */
1207 : 8 : CacheInvalidateRelcache(target_table);
1208 : :
1209 : : /* Clean up. */
1210 : 8 : systable_endscan(sscan);
1211 : 8 : table_close(pg_policy_rel, RowExclusiveLock);
1212 : 8 : relation_close(target_table, NoLock);
1213 : :
1214 : 8 : return address;
1215 : : }
1216 : :
1217 : : /*
1218 : : * get_relation_policy_oid - Look up a policy by name to find its OID
1219 : : *
1220 : : * If missing_ok is false, throw an error if policy not found. If
1221 : : * true, just return InvalidOid.
1222 : : */
1223 : : Oid
1224 : 166 : get_relation_policy_oid(Oid relid, const char *policy_name, bool missing_ok)
1225 : : {
1226 : : Relation pg_policy_rel;
1227 : : ScanKeyData skey[2];
1228 : : SysScanDesc sscan;
1229 : : HeapTuple policy_tuple;
1230 : : Oid policy_oid;
1231 : :
1232 : 166 : pg_policy_rel = table_open(PolicyRelationId, AccessShareLock);
1233 : :
1234 : : /* Add key - policy's relation id. */
1235 : 166 : ScanKeyInit(&skey[0],
1236 : : Anum_pg_policy_polrelid,
1237 : : BTEqualStrategyNumber, F_OIDEQ,
1238 : : ObjectIdGetDatum(relid));
1239 : :
1240 : : /* Add key - policy's name. */
1241 : 166 : ScanKeyInit(&skey[1],
1242 : : Anum_pg_policy_polname,
1243 : : BTEqualStrategyNumber, F_NAMEEQ,
1244 : : CStringGetDatum(policy_name));
1245 : :
1246 : 166 : sscan = systable_beginscan(pg_policy_rel,
1247 : : PolicyPolrelidPolnameIndexId, true, NULL, 2,
1248 : : skey);
1249 : :
1250 : 166 : policy_tuple = systable_getnext(sscan);
1251 : :
1252 [ + + ]: 166 : if (!HeapTupleIsValid(policy_tuple))
1253 : : {
1254 [ + - ]: 8 : if (!missing_ok)
1255 [ + - ]: 8 : ereport(ERROR,
1256 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
1257 : : errmsg("policy \"%s\" for table \"%s\" does not exist",
1258 : : policy_name, get_rel_name(relid))));
1259 : :
1260 : 0 : policy_oid = InvalidOid;
1261 : : }
1262 : : else
1263 : 158 : policy_oid = ((Form_pg_policy) GETSTRUCT(policy_tuple))->oid;
1264 : :
1265 : : /* Clean up. */
1266 : 158 : systable_endscan(sscan);
1267 : 158 : table_close(pg_policy_rel, AccessShareLock);
1268 : :
1269 : 158 : return policy_oid;
1270 : : }
1271 : :
1272 : : /*
1273 : : * relation_has_policies - Determine if relation has any policies
1274 : : */
1275 : : bool
1276 : 0 : relation_has_policies(Relation rel)
1277 : : {
1278 : : Relation catalog;
1279 : : ScanKeyData skey;
1280 : : SysScanDesc sscan;
1281 : : HeapTuple policy_tuple;
1282 : 0 : bool ret = false;
1283 : :
1284 : 0 : catalog = table_open(PolicyRelationId, AccessShareLock);
1285 : 0 : ScanKeyInit(&skey,
1286 : : Anum_pg_policy_polrelid,
1287 : : BTEqualStrategyNumber, F_OIDEQ,
1288 : : ObjectIdGetDatum(RelationGetRelid(rel)));
1289 : 0 : sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
1290 : : NULL, 1, &skey);
1291 : 0 : policy_tuple = systable_getnext(sscan);
1292 [ # # ]: 0 : if (HeapTupleIsValid(policy_tuple))
1293 : 0 : ret = true;
1294 : :
1295 : 0 : systable_endscan(sscan);
1296 : 0 : table_close(catalog, AccessShareLock);
1297 : :
1298 : 0 : return ret;
1299 : : }
|