@@ -401,13 +401,12 @@ RemovePolicyById(Oid policy_id)
401
401
402
402
/*
403
403
* RemoveRoleFromObjectPolicy -
404
- * remove a role from a policy by its OID. If the role is not a member of
405
- * the policy then an error is raised. False is returned to indicate that
406
- * the role could not be removed due to being the only role on the policy
407
- * and therefore the entire policy should be removed.
404
+ * remove a role from a policy's applicable-roles list.
408
405
*
409
- * Note that a warning will be thrown and true will be returned on a
410
- * permission error, as the policy should not be removed in that case.
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.
411
410
*
412
411
* roleid - the oid of the role to remove
413
412
* classid - should always be PolicyRelationId
@@ -421,11 +420,15 @@ RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
421
420
ScanKeyData skey [1 ];
422
421
HeapTuple tuple ;
423
422
Oid relid ;
424
- Relation rel ;
425
423
ArrayType * policy_roles ;
426
424
Datum roles_datum ;
425
+ Oid * roles ;
426
+ int num_roles ;
427
+ Datum * role_oids ;
427
428
bool attr_isnull ;
428
429
bool keep_policy = true;
430
+ int i ,
431
+ j ;
429
432
430
433
Assert (classid == PolicyRelationId );
431
434
@@ -448,26 +451,9 @@ RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
448
451
if (!HeapTupleIsValid (tuple ))
449
452
elog (ERROR , "could not find tuple for policy %u" , policy_id );
450
453
451
- /*
452
- * Open and exclusive-lock the relation the policy belongs to.
453
- */
454
+ /* Identify rel the policy belongs to */
454
455
relid = ((Form_pg_policy ) GETSTRUCT (tuple ))-> polrelid ;
455
456
456
- rel = relation_open (relid , AccessExclusiveLock );
457
-
458
- if (rel -> rd_rel -> relkind != RELKIND_RELATION &&
459
- rel -> rd_rel -> relkind != RELKIND_PARTITIONED_TABLE )
460
- ereport (ERROR ,
461
- (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
462
- errmsg ("\"%s\" is not a table" ,
463
- RelationGetRelationName (rel ))));
464
-
465
- if (!allowSystemTableMods && IsSystemRelation (rel ))
466
- ereport (ERROR ,
467
- (errcode (ERRCODE_INSUFFICIENT_PRIVILEGE ),
468
- errmsg ("permission denied: \"%s\" is a system catalog" ,
469
- RelationGetRelationName (rel ))));
470
-
471
457
/* Get the current set of roles */
472
458
roles_datum = heap_getattr (tuple ,
473
459
Anum_pg_policy_polroles ,
@@ -477,34 +463,31 @@ RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
477
463
Assert (!attr_isnull );
478
464
479
465
policy_roles = DatumGetArrayTypePCopy (roles_datum );
466
+ roles = (Oid * ) ARR_DATA_PTR (policy_roles );
467
+ num_roles = ARR_DIMS (policy_roles )[0 ];
480
468
481
- /* Must own relation. */
482
- if (!pg_class_ownercheck (relid , GetUserId ()))
483
- ereport (WARNING ,
484
- (errcode (ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED ),
485
- errmsg ("role \"%s\" could not be removed from policy \"%s\" on \"%s\"" ,
486
- GetUserNameFromId (roleid , false),
487
- NameStr (((Form_pg_policy ) GETSTRUCT (tuple ))-> polname ),
488
- RelationGetRelationName (rel ))));
489
- else
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
+ role_oids = (Datum * ) palloc (num_roles * sizeof (Datum ));
475
+ for (i = 0 , j = 0 ; i < num_roles ; i ++ )
476
+ {
477
+ if (roles [i ] != roleid )
478
+ role_oids [j ++ ] = ObjectIdGetDatum (roles [i ]);
479
+ }
480
+ num_roles = j ;
481
+
482
+ /* If any roles remain, update the policy entry. */
483
+ if (num_roles > 0 )
490
484
{
491
- int i ,
492
- j ;
493
- Oid * roles = (Oid * ) ARR_DATA_PTR (policy_roles );
494
- int num_roles = ARR_DIMS (policy_roles )[0 ];
495
- Datum * role_oids ;
496
- char * qual_value ;
497
- Node * qual_expr ;
498
- List * qual_parse_rtable = NIL ;
499
- char * with_check_value ;
500
- Node * with_check_qual ;
501
- List * with_check_parse_rtable = NIL ;
485
+ ArrayType * role_ids ;
502
486
Datum values [Natts_pg_policy ];
503
487
bool isnull [Natts_pg_policy ];
504
488
bool replaces [Natts_pg_policy ];
505
- Datum value_datum ;
506
- ArrayType * role_ids ;
507
489
HeapTuple new_tuple ;
490
+ HeapTuple reltup ;
508
491
ObjectAddress target ;
509
492
ObjectAddress myself ;
510
493
@@ -513,74 +496,6 @@ RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
513
496
memset (replaces , 0 , sizeof (replaces ));
514
497
memset (isnull , 0 , sizeof (isnull ));
515
498
516
- /*
517
- * All of the dependencies will be removed from the policy and then
518
- * re-added. In order to get them correct, we need to extract out the
519
- * expressions in the policy and construct a parsestate just enough to
520
- * build the range table(s) to then pass to recordDependencyOnExpr().
521
- */
522
-
523
- /* Get policy qual, to update dependencies */
524
- value_datum = heap_getattr (tuple , Anum_pg_policy_polqual ,
525
- RelationGetDescr (pg_policy_rel ), & attr_isnull );
526
- if (!attr_isnull )
527
- {
528
- ParseState * qual_pstate ;
529
-
530
- /* parsestate is built just to build the range table */
531
- qual_pstate = make_parsestate (NULL );
532
-
533
- qual_value = TextDatumGetCString (value_datum );
534
- qual_expr = stringToNode (qual_value );
535
-
536
- /* Add this rel to the parsestate's rangetable, for dependencies */
537
- addRangeTableEntryForRelation (qual_pstate , rel , NULL , false, false);
538
-
539
- qual_parse_rtable = qual_pstate -> p_rtable ;
540
- free_parsestate (qual_pstate );
541
- }
542
- else
543
- qual_expr = NULL ;
544
-
545
- /* Get WITH CHECK qual, to update dependencies */
546
- value_datum = heap_getattr (tuple , Anum_pg_policy_polwithcheck ,
547
- RelationGetDescr (pg_policy_rel ), & attr_isnull );
548
- if (!attr_isnull )
549
- {
550
- ParseState * with_check_pstate ;
551
-
552
- /* parsestate is built just to build the range table */
553
- with_check_pstate = make_parsestate (NULL );
554
-
555
- with_check_value = TextDatumGetCString (value_datum );
556
- with_check_qual = stringToNode (with_check_value );
557
-
558
- /* Add this rel to the parsestate's rangetable, for dependencies */
559
- addRangeTableEntryForRelation (with_check_pstate , rel , NULL , false,
560
- false);
561
-
562
- with_check_parse_rtable = with_check_pstate -> p_rtable ;
563
- free_parsestate (with_check_pstate );
564
- }
565
- else
566
- with_check_qual = NULL ;
567
-
568
- /*
569
- * Rebuild the roles array, without any mentions of the target role.
570
- * Ordinarily there'd be exactly one, but we must cope with duplicate
571
- * mentions, since CREATE/ALTER POLICY historically have allowed that.
572
- */
573
- role_oids = (Datum * ) palloc (num_roles * sizeof (Datum ));
574
- for (i = 0 , j = 0 ; i < num_roles ; i ++ )
575
- {
576
- if (roles [i ] != roleid )
577
- role_oids [j ++ ] = ObjectIdGetDatum (roles [i ]);
578
- }
579
- num_roles = j ;
580
-
581
- /* If any roles remain, update the policy entry. */
582
- if (num_roles > 0 )
583
- {
584
499
/* This is the array for the new tuple */
585
500
role_ids = construct_array (role_oids , num_roles , OIDOID ,
586
501
sizeof (Oid ), true, 'i' );
@@ -593,33 +508,14 @@ RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
593
508
values , isnull , replaces );
594
509
CatalogTupleUpdate (pg_policy_rel , & new_tuple -> t_self , new_tuple );
595
510
596
- /* Remove all old dependencies. */
597
- deleteDependencyRecordsFor (PolicyRelationId , policy_id , false);
598
-
599
- /* Record the new set of dependencies */
600
- target .classId = RelationRelationId ;
601
- target .objectId = relid ;
602
- target .objectSubId = 0 ;
511
+ /* Remove all the old shared dependencies (roles) */
512
+ deleteSharedDependencyRecordsFor (PolicyRelationId , policy_id , 0 );
603
513
514
+ /* Record the new shared dependencies (roles) */
604
515
myself .classId = PolicyRelationId ;
605
516
myself .objectId = policy_id ;
606
517
myself .objectSubId = 0 ;
607
518
608
- recordDependencyOn (& myself , & target , DEPENDENCY_AUTO );
609
-
610
- if (qual_expr )
611
- recordDependencyOnExpr (& myself , qual_expr , qual_parse_rtable ,
612
- DEPENDENCY_NORMAL );
613
-
614
- if (with_check_qual )
615
- recordDependencyOnExpr (& myself , with_check_qual ,
616
- with_check_parse_rtable ,
617
- DEPENDENCY_NORMAL );
618
-
619
- /* Remove all the old shared dependencies (roles) */
620
- deleteSharedDependencyRecordsFor (PolicyRelationId , policy_id , 0 );
621
-
622
- /* Record the new shared dependencies (roles) */
623
519
target .classId = AuthIdRelationId ;
624
520
target .objectSubId = 0 ;
625
521
for (i = 0 ; i < num_roles ; i ++ )
@@ -638,21 +534,27 @@ RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
638
534
/* Make updates visible */
639
535
CommandCounterIncrement ();
640
536
641
- /* Invalidate Relation Cache */
642
- CacheInvalidateRelcache (rel );
643
- }
644
- else
537
+ /*
538
+ * Invalidate relcache entry for rel the policy belongs to, to force
539
+ * redoing any dependent plans. In case of a race condition where the
540
+ * rel was just dropped, we need do nothing.
541
+ */
542
+ reltup = SearchSysCache1 (RELOID , ObjectIdGetDatum (relid ));
543
+ if (HeapTupleIsValid (reltup ))
645
544
{
646
- /* No roles would remain, so drop the policy instead */
647
- keep_policy = false ;
545
+ CacheInvalidateRelcacheByTuple ( reltup );
546
+ ReleaseSysCache ( reltup ) ;
648
547
}
649
548
}
549
+ else
550
+ {
551
+ /* No roles would remain, so drop the policy instead. */
552
+ keep_policy = false;
553
+ }
650
554
651
555
/* Clean up. */
652
556
systable_endscan (sscan );
653
557
654
- relation_close (rel , NoLock );
655
-
656
558
heap_close (pg_policy_rel , RowExclusiveLock );
657
559
658
560
return keep_policy ;
0 commit comments