@@ -407,6 +407,262 @@ RemovePolicyById(Oid policy_id)
407407heap_close (pg_policy_rel ,RowExclusiveLock );
408408}
409409
410+ /*
411+ * RemoveRoleFromObjectPolicy -
412+ * remove a role from a policy by its OID. If the role is not a member of
413+ * the policy then an error is raised. False is returned to indicate that
414+ * the role could not be removed due to being the only role on the policy
415+ * and therefore the entire policy should be removed.
416+ *
417+ * Note that a warning will be thrown and true will be returned on a
418+ * permission error, as the policy should not be removed in that case.
419+ *
420+ * roleid - the oid of the role to remove
421+ * classid - should always be PolicyRelationId
422+ * policy_id - the oid of the policy.
423+ */
424+ bool
425+ RemoveRoleFromObjectPolicy (Oid roleid ,Oid classid ,Oid policy_id )
426+ {
427+ Relation pg_policy_rel ;
428+ SysScanDesc sscan ;
429+ ScanKeyData skey [1 ];
430+ HeapTuple tuple ;
431+ Oid relid ;
432+ Relation rel ;
433+ ArrayType * policy_roles ;
434+ int num_roles ;
435+ Datum roles_datum ;
436+ bool attr_isnull ;
437+ bool noperm = true;
438+
439+ Assert (classid == PolicyRelationId );
440+
441+ pg_policy_rel = heap_open (PolicyRelationId ,RowExclusiveLock );
442+
443+ /*
444+ * Find the policy to update.
445+ */
446+ ScanKeyInit (& skey [0 ],
447+ ObjectIdAttributeNumber ,
448+ BTEqualStrategyNumber ,F_OIDEQ ,
449+ ObjectIdGetDatum (policy_id ));
450+
451+ sscan = systable_beginscan (pg_policy_rel ,PolicyOidIndexId , true,
452+ NULL ,1 ,skey );
453+
454+ tuple = systable_getnext (sscan );
455+
456+ /* Raise an error if we don't find the policy. */
457+ if (!HeapTupleIsValid (tuple ))
458+ elog (ERROR ,"could not find tuple for policy %u" ,policy_id );
459+
460+ /*
461+ * Open and exclusive-lock the relation the policy belongs to.
462+ */
463+ relid = ((Form_pg_policy )GETSTRUCT (tuple ))-> polrelid ;
464+
465+ rel = relation_open (relid ,AccessExclusiveLock );
466+
467+ if (rel -> rd_rel -> relkind != RELKIND_RELATION )
468+ ereport (ERROR ,
469+ (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
470+ errmsg ("\"%s\" is not a table" ,
471+ RelationGetRelationName (rel ))));
472+
473+ if (!allowSystemTableMods && IsSystemRelation (rel ))
474+ ereport (ERROR ,
475+ (errcode (ERRCODE_INSUFFICIENT_PRIVILEGE ),
476+ errmsg ("permission denied: \"%s\" is a system catalog" ,
477+ RelationGetRelationName (rel ))));
478+
479+ /* Get the current set of roles */
480+ roles_datum = heap_getattr (tuple ,
481+ Anum_pg_policy_polroles ,
482+ RelationGetDescr (pg_policy_rel ),
483+ & attr_isnull );
484+
485+ Assert (!attr_isnull );
486+
487+ policy_roles = DatumGetArrayTypePCopy (roles_datum );
488+
489+ /* We should be removing exactly one entry from the roles array */
490+ num_roles = ARR_DIMS (policy_roles )[0 ]- 1 ;
491+
492+ Assert (num_roles >=0 );
493+
494+ /* Must own relation. */
495+ if (pg_class_ownercheck (relid ,GetUserId ()))
496+ noperm = false;/* user is allowed to modify this policy */
497+ else
498+ ereport (WARNING ,
499+ (errcode (ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED ),
500+ errmsg ("role \"%s\" could not be removed from policy \"%s\" on \"%s\"" ,
501+ GetUserNameFromId (roleid , false),
502+ NameStr (((Form_pg_policy )GETSTRUCT (tuple ))-> polname ),
503+ RelationGetRelationName (rel ))));
504+
505+ /*
506+ * If multiple roles exist on this policy, then remove the one we were
507+ * asked to and leave the rest.
508+ */
509+ if (!noperm && num_roles > 0 )
510+ {
511+ int i ,j ;
512+ Oid * roles = (Oid * )ARR_DATA_PTR (policy_roles );
513+ Datum * role_oids ;
514+ char * qual_value ;
515+ Node * qual_expr ;
516+ List * qual_parse_rtable = NIL ;
517+ char * with_check_value ;
518+ Node * with_check_qual ;
519+ List * with_check_parse_rtable = NIL ;
520+ Datum values [Natts_pg_policy ];
521+ bool isnull [Natts_pg_policy ];
522+ bool replaces [Natts_pg_policy ];
523+ Datum value_datum ;
524+ ArrayType * role_ids ;
525+ HeapTuple new_tuple ;
526+ ObjectAddress target ;
527+ ObjectAddress myself ;
528+
529+ /* zero-clear */
530+ memset (values ,0 ,sizeof (values ));
531+ memset (replaces ,0 ,sizeof (replaces ));
532+ memset (isnull ,0 ,sizeof (isnull ));
533+
534+ /*
535+ * All of the dependencies will be removed from the policy and then
536+ * re-added. In order to get them correct, we need to extract out
537+ * the expressions in the policy and construct a parsestate just
538+ * enough to build the range table(s) to then pass to
539+ * recordDependencyOnExpr().
540+ */
541+
542+ /* Get policy qual, to update dependencies */
543+ value_datum = heap_getattr (tuple ,Anum_pg_policy_polqual ,
544+ RelationGetDescr (pg_policy_rel ),& attr_isnull );
545+ if (!attr_isnull )
546+ {
547+ ParseState * qual_pstate ;
548+
549+ /* parsestate is built just to build the range table */
550+ qual_pstate = make_parsestate (NULL );
551+
552+ qual_value = TextDatumGetCString (value_datum );
553+ qual_expr = stringToNode (qual_value );
554+
555+ /* Add this rel to the parsestate's rangetable, for dependencies */
556+ addRangeTableEntryForRelation (qual_pstate ,rel ,NULL , false, false);
557+
558+ qual_parse_rtable = qual_pstate -> p_rtable ;
559+ free_parsestate (qual_pstate );
560+ }
561+ else
562+ qual_expr = NULL ;
563+
564+ /* Get WITH CHECK qual, to update dependencies */
565+ value_datum = heap_getattr (tuple ,Anum_pg_policy_polwithcheck ,
566+ RelationGetDescr (pg_policy_rel ),& attr_isnull );
567+ if (!attr_isnull )
568+ {
569+ ParseState * with_check_pstate ;
570+
571+ /* parsestate is built just to build the range table */
572+ with_check_pstate = make_parsestate (NULL );
573+
574+ with_check_value = TextDatumGetCString (value_datum );
575+ with_check_qual = stringToNode (with_check_value );
576+
577+ /* Add this rel to the parsestate's rangetable, for dependencies */
578+ addRangeTableEntryForRelation (with_check_pstate ,rel ,NULL , false,
579+ false);
580+
581+ with_check_parse_rtable = with_check_pstate -> p_rtable ;
582+ free_parsestate (with_check_pstate );
583+ }
584+ else
585+ with_check_qual = NULL ;
586+
587+ /* Rebuild the roles array to then update the pg_policy tuple with */
588+ role_oids = (Datum * )palloc (num_roles * sizeof (Datum ));
589+ for (i = 0 ,j = 0 ;i < ARR_DIMS (policy_roles )[0 ];i ++ )
590+ /* Copy over all of the roles which are not the one being removed */
591+ if (roles [i ]!= roleid )
592+ role_oids [j ++ ]= ObjectIdGetDatum (roles [i ]);
593+
594+ /* We should have only removed the one role */
595+ Assert (j == num_roles );
596+
597+ /* This is the array for the new tuple */
598+ role_ids = construct_array (role_oids ,num_roles ,OIDOID ,
599+ sizeof (Oid ), true,'i' );
600+
601+ replaces [Anum_pg_policy_polroles - 1 ]= true;
602+ values [Anum_pg_policy_polroles - 1 ]= PointerGetDatum (role_ids );
603+
604+ new_tuple = heap_modify_tuple (tuple ,
605+ RelationGetDescr (pg_policy_rel ),
606+ values ,isnull ,replaces );
607+ simple_heap_update (pg_policy_rel ,& new_tuple -> t_self ,new_tuple );
608+
609+ /* Update Catalog Indexes */
610+ CatalogUpdateIndexes (pg_policy_rel ,new_tuple );
611+
612+ /* Remove all old dependencies. */
613+ deleteDependencyRecordsFor (PolicyRelationId ,policy_id , false);
614+
615+ /* Record the new set of dependencies */
616+ target .classId = RelationRelationId ;
617+ target .objectId = relid ;
618+ target .objectSubId = 0 ;
619+
620+ myself .classId = PolicyRelationId ;
621+ myself .objectId = policy_id ;
622+ myself .objectSubId = 0 ;
623+
624+ recordDependencyOn (& myself ,& target ,DEPENDENCY_AUTO );
625+
626+ if (qual_expr )
627+ recordDependencyOnExpr (& myself ,qual_expr ,qual_parse_rtable ,
628+ DEPENDENCY_NORMAL );
629+
630+ if (with_check_qual )
631+ recordDependencyOnExpr (& myself ,with_check_qual ,
632+ with_check_parse_rtable ,
633+ DEPENDENCY_NORMAL );
634+
635+ /* Remove all the old shared dependencies (roles) */
636+ deleteSharedDependencyRecordsFor (PolicyRelationId ,policy_id ,0 );
637+
638+ /* Record the new shared dependencies (roles) */
639+ target .classId = AuthIdRelationId ;
640+ target .objectSubId = 0 ;
641+ for (i = 0 ;i < num_roles ;i ++ )
642+ {
643+ target .objectId = DatumGetObjectId (role_oids [i ]);
644+ /* no need for dependency on the public role */
645+ if (target .objectId != ACL_ID_PUBLIC )
646+ recordSharedDependencyOn (& myself ,& target ,
647+ SHARED_DEPENDENCY_POLICY );
648+ }
649+
650+ InvokeObjectPostAlterHook (PolicyRelationId ,policy_id ,0 );
651+
652+ heap_freetuple (new_tuple );
653+
654+ /* Invalidate Relation Cache */
655+ CacheInvalidateRelcache (rel );
656+ }
657+
658+ /* Clean up. */
659+ systable_endscan (sscan );
660+ relation_close (rel ,AccessExclusiveLock );
661+ heap_close (pg_policy_rel ,RowExclusiveLock );
662+
663+ return (noperm || num_roles > 0 );
664+ }
665+
410666/*
411667 * CreatePolicy -
412668 * handles the execution of the CREATE POLICY command.