aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/trigger.c20
-rw-r--r--src/test/regress/expected/triggers.out67
-rw-r--r--src/test/regress/sql/triggers.sql63
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;