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