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