diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/commands/trigger.c | 20 | ||||
-rw-r--r-- | src/test/regress/expected/triggers.out | 67 | ||||
-rw-r--r-- | src/test/regress/sql/triggers.sql | 63 |
3 files changed, 150 insertions, 0 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 711c09a1030..7a5ffe32f60 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -3635,6 +3635,7 @@ typedef struct AfterTriggerSharedData TriggerEvent ats_event; /* event type indicator, see trigger.h */ Oid ats_tgoid; /* the trigger's ID */ Oid ats_relid; /* the relation it's on */ + Oid ats_rolid; /* role to execute the trigger */ CommandId ats_firing_id; /* ID for firing cycle */ struct AfterTriggersTableData *ats_table; /* transition table access */ Bitmapset *ats_modifiedcols; /* modified columns */ @@ -4117,6 +4118,7 @@ afterTriggerAddEvent(AfterTriggerEventList *events, newshared->ats_firing_id == 0 && newshared->ats_table == evtshared->ats_table && newshared->ats_relid == evtshared->ats_relid && + newshared->ats_rolid == evtshared->ats_rolid && bms_equal(newshared->ats_modifiedcols, evtshared->ats_modifiedcols)) break; @@ -4289,6 +4291,8 @@ AfterTriggerExecute(EState *estate, AfterTriggerShared evtshared = GetTriggerSharedData(event); Oid tgoid = evtshared->ats_tgoid; TriggerData LocTriggerData = {0}; + Oid save_rolid; + int save_sec_context; HeapTuple rettuple; int tgindx; bool should_free_trig = false; @@ -4493,6 +4497,17 @@ AfterTriggerExecute(EState *estate, MemoryContextReset(per_tuple_context); /* + * If necessary, become the role that was active when the trigger got + * queued. Note that the role might have been dropped since the trigger + * was queued, but if that is a problem, we will get an error later. + * Checking here would still leave a race condition. + */ + GetUserIdAndSecContext(&save_rolid, &save_sec_context); + if (save_rolid != evtshared->ats_rolid) + SetUserIdAndSecContext(evtshared->ats_rolid, + save_sec_context | SECURITY_LOCAL_USERID_CHANGE); + + /* * Call the trigger and throw away any possibly returned updated tuple. * (Don't let ExecCallTriggerFunc measure EXPLAIN time.) */ @@ -4506,6 +4521,10 @@ AfterTriggerExecute(EState *estate, rettuple != LocTriggerData.tg_newtuple) heap_freetuple(rettuple); + /* Restore the current role if necessary */ + if (save_rolid != evtshared->ats_rolid) + SetUserIdAndSecContext(save_rolid, save_sec_context); + /* * Release resources */ @@ -6431,6 +6450,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, (trigger->tginitdeferred ? AFTER_TRIGGER_INITDEFERRED : 0); new_shared.ats_tgoid = trigger->tgoid; new_shared.ats_relid = RelationGetRelid(rel); + new_shared.ats_rolid = GetUserId(); new_shared.ats_firing_id = 0; if ((trigger->tgoldtable || trigger->tgnewtable) && transition_capture != NULL) diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index 0657da17577..e38304bee50 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -3748,3 +3748,70 @@ Inherits: parent drop table parent, child; drop function f(); +-- Test who runs deferred trigger functions +-- setup +create role regress_groot; +create role regress_outis; +create function whoami() returns trigger language plpgsql +as $$ +begin + raise notice 'I am %', current_user; + return null; +end; +$$; +alter function whoami() owner to regress_outis; +create table defer_trig (id integer); +grant insert on defer_trig to public; +create constraint trigger whoami after insert on defer_trig + deferrable initially deferred + for each row + execute function whoami(); +-- deferred triggers must run as the user that queued the trigger +begin; +set role regress_groot; +insert into defer_trig values (1); +reset role; +set role regress_outis; +insert into defer_trig values (2); +reset role; +commit; +NOTICE: I am regress_groot +NOTICE: I am regress_outis +-- security definer functions override the user who queued the trigger +alter function whoami() security definer; +begin; +set role regress_groot; +insert into defer_trig values (3); +reset role; +commit; +NOTICE: I am regress_outis +alter function whoami() security invoker; +-- make sure the current user is restored after error +create or replace function whoami() returns trigger language plpgsql +as $$ +begin + raise notice 'I am %', current_user; + perform 1 / 0; + return null; +end; +$$; +begin; +set role regress_groot; +insert into defer_trig values (4); +reset role; +commit; -- error expected +NOTICE: I am regress_groot +ERROR: division by zero +CONTEXT: SQL statement "SELECT 1 / 0" +PL/pgSQL function whoami() line 4 at PERFORM +select current_user = session_user; + ?column? +---------- + t +(1 row) + +-- clean up +drop table defer_trig; +drop function whoami(); +drop role regress_outis; +drop role regress_groot; diff --git a/src/test/regress/sql/triggers.sql b/src/test/regress/sql/triggers.sql index 7e2f7597c10..b79fecb8fe6 100644 --- a/src/test/regress/sql/triggers.sql +++ b/src/test/regress/sql/triggers.sql @@ -2839,3 +2839,66 @@ alter trigger parenttrig on parent rename to anothertrig; drop table parent, child; drop function f(); + +-- Test who runs deferred trigger functions + +-- setup +create role regress_groot; +create role regress_outis; +create function whoami() returns trigger language plpgsql +as $$ +begin + raise notice 'I am %', current_user; + return null; +end; +$$; +alter function whoami() owner to regress_outis; + +create table defer_trig (id integer); +grant insert on defer_trig to public; +create constraint trigger whoami after insert on defer_trig + deferrable initially deferred + for each row + execute function whoami(); + +-- deferred triggers must run as the user that queued the trigger +begin; +set role regress_groot; +insert into defer_trig values (1); +reset role; +set role regress_outis; +insert into defer_trig values (2); +reset role; +commit; + +-- security definer functions override the user who queued the trigger +alter function whoami() security definer; +begin; +set role regress_groot; +insert into defer_trig values (3); +reset role; +commit; +alter function whoami() security invoker; + +-- make sure the current user is restored after error +create or replace function whoami() returns trigger language plpgsql +as $$ +begin + raise notice 'I am %', current_user; + perform 1 / 0; + return null; +end; +$$; + +begin; +set role regress_groot; +insert into defer_trig values (4); +reset role; +commit; -- error expected +select current_user = session_user; + +-- clean up +drop table defer_trig; +drop function whoami(); +drop role regress_outis; +drop role regress_groot; |