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