Coverage Report

Created: 2025-06-15 06:31

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