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