YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

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