*** a/doc/src/sgml/plpgsql.sgml
--- b/doc/src/sgml/plpgsql.sgml
***************
*** 3207,3212 **** RAISE unique_violation USING MESSAGE = 'Duplicate user ID: ' || user_id;
--- 3207,3222 ----
+ TG_DEPTH
+
+
+ Data type integer; the current number of levels of
+ nesting within trigger execution.
+
+
+
+
+
TG_NARGS
*** a/src/backend/access/transam/xact.c
--- b/src/backend/access/transam/xact.c
***************
*** 142,147 **** typedef struct TransactionStateData
--- 142,148 ----
Oid prevUser; /* previous CurrentUserId setting */
int prevSecContext; /* previous SecurityRestrictionContext */
bool prevXactReadOnly; /* entry-time xact r/o state */
+ int prevTgDepth; /* previous trigger depth */
bool startedInRecovery; /* did we start in recovery? */
struct TransactionStateData *parent; /* back link to parent */
} TransactionStateData;
***************
*** 171,176 **** static TransactionStateData TopTransactionStateData = {
--- 172,178 ----
InvalidOid, /* previous CurrentUserId setting */
0, /* previous SecurityRestrictionContext */
false, /* entry-time xact r/o state */
+ 0, /* previous trigger depth */
false, /* startedInRecovery */
NULL /* link to parent state block */
};
***************
*** 3996,4001 **** CommitSubTransaction(void)
--- 3998,4005 ----
*/
XactReadOnly = s->prevXactReadOnly;
+ tg_depth = s->prevTgDepth;
+
CurrentResourceOwner = s->parent->curTransactionOwner;
CurTransactionResourceOwner = s->parent->curTransactionOwner;
ResourceOwnerDelete(s->curTransactionOwner);
***************
*** 4114,4119 **** AbortSubTransaction(void)
--- 4118,4125 ----
*/
XactReadOnly = s->prevXactReadOnly;
+ tg_depth = s->prevTgDepth;
+
RESUME_INTERRUPTS();
}
***************
*** 4196,4201 **** PushTransaction(void)
--- 4202,4208 ----
s->blockState = TBLOCK_SUBBEGIN;
GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext);
s->prevXactReadOnly = XactReadOnly;
+ s->prevTgDepth = tg_depth;
CurrentTransactionState = s;
*** a/src/backend/commands/trigger.c
--- b/src/backend/commands/trigger.c
***************
*** 55,60 ****
--- 55,63 ----
#include "utils/tqual.h"
+ /* How many levels deep into trigger execution are we? */
+ int tg_depth = 0;
+
/* GUC variables */
int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN;
***************
*** 1807,1812 **** ExecCallTriggerFunc(TriggerData *trigdata,
--- 1810,1817 ----
if (instr)
InstrStartNode(instr + tgindx);
+ tg_depth++;
+
/*
* Do the function evaluation in the per-tuple memory context, so that
* leaked memory will be reclaimed once per tuple. Note in particular that
***************
*** 1828,1833 **** ExecCallTriggerFunc(TriggerData *trigdata,
--- 1833,1840 ----
MemoryContextSwitchTo(oldContext);
+ tg_depth--;
+
/*
* Trigger protocol allows function to return a null pointer, but NOT to
* set the isnull result flag.
*** a/src/backend/tcop/pquery.c
--- b/src/backend/tcop/pquery.c
***************
*** 748,753 **** PortalRun(Portal portal, long count, bool isTopLevel,
--- 748,755 ----
errmsg("portal \"%s\" cannot be run", portal->name)));
portal->status = PORTAL_ACTIVE;
+ tg_depth = 0;
+
/*
* Set up global portal context pointers.
*
***************
*** 1371,1376 **** PortalRunFetch(Portal portal,
--- 1373,1380 ----
errmsg("portal \"%s\" cannot be run", portal->name)));
portal->status = PORTAL_ACTIVE;
+ tg_depth = 0;
+
/*
* Set up global portal context pointers.
*/
*** a/src/include/commands/trigger.h
--- b/src/include/commands/trigger.h
***************
*** 108,113 **** extern PGDLLIMPORT int SessionReplicationRole;
--- 108,115 ----
#define TRIGGER_FIRES_ON_REPLICA 'R'
#define TRIGGER_DISABLED 'D'
+ extern int tg_depth;
+
extern Oid CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
Oid constraintOid, Oid indexOid,
bool isInternal);
*** a/src/pl/plpgsql/src/pl_comp.c
--- b/src/pl/plpgsql/src/pl_comp.c
***************
*** 633,638 **** do_compile(FunctionCallInfo fcinfo,
--- 633,644 ----
true);
function->tg_table_schema_varno = var->dno;
+ /* add the variable tg_depth */
+ var = plpgsql_build_variable("tg_depth", 0,
+ plpgsql_build_datatype(INT4OID, -1),
+ true);
+ function->tg_depth_varno = var->dno;
+
/* Add the variable tg_nargs */
var = plpgsql_build_variable("tg_nargs", 0,
plpgsql_build_datatype(INT4OID, -1),
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
***************
*** 625,630 **** plpgsql_exec_trigger(PLpgSQL_function *func,
--- 625,635 ----
var->isnull = false;
var->freeval = true;
+ var = (PLpgSQL_var *) (estate.datums[func->tg_depth_varno]);
+ var->value = Int16GetDatum(tg_depth);
+ var->isnull = false;
+ var->freeval = false;
+
var = (PLpgSQL_var *) (estate.datums[func->tg_nargs_varno]);
var->value = Int16GetDatum(trigdata->tg_trigger->tgnargs);
var->isnull = false;
*** a/src/pl/plpgsql/src/plpgsql.h
--- b/src/pl/plpgsql/src/plpgsql.h
***************
*** 668,673 **** typedef struct PLpgSQL_function
--- 668,674 ----
int tg_relname_varno;
int tg_table_name_varno;
int tg_table_schema_varno;
+ int tg_depth_varno;
int tg_nargs_varno;
int tg_argv_varno;
*** a/src/test/regress/expected/plpgsql.out
--- b/src/test/regress/expected/plpgsql.out
***************
*** 4240,4242 **** select unreserved_test();
--- 4240,4317 ----
(1 row)
drop function unreserved_test();
+ -- Test TG_DEPTH
+ create table tg_depth_a (id int not null primary key);
+ NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "tg_depth_a_pkey" for table "tg_depth_a"
+ create table tg_depth_b (id int not null primary key);
+ NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "tg_depth_b_pkey" for table "tg_depth_b"
+ create table tg_depth_c (id int not null primary key);
+ NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "tg_depth_c_pkey" for table "tg_depth_c"
+ create function tg_depth_a_tf() returns trigger
+ language plpgsql as $$
+ begin
+ raise notice '%: tg_depth = %', tg_name, tg_depth;
+ insert into tg_depth_b values (new.id);
+ raise notice '%: tg_depth = %', tg_name, tg_depth;
+ return new;
+ end;
+ $$;
+ create trigger tg_depth_a_tr before insert on tg_depth_a
+ for each row execute procedure tg_depth_a_tf();
+ create function tg_depth_b_tf() returns trigger
+ language plpgsql as $$
+ begin
+ raise notice '%: tg_depth = %', tg_name, tg_depth;
+ begin
+ execute 'insert into tg_depth_c values (' || new.id::text || ')';
+ exception
+ when sqlstate 'U9999' then
+ raise notice 'SQLSTATE = U9999: tg_depth = %', tg_depth;
+ end;
+ raise notice '%: tg_depth = %', tg_name, tg_depth;
+ execute 'insert into tg_depth_c values (' || new.id::text || ')';
+ return new;
+ end;
+ $$;
+ create trigger tg_depth_b_tr before insert on tg_depth_b
+ for each row execute procedure tg_depth_b_tf();
+ create function tg_depth_c_tf() returns trigger
+ language plpgsql as $$
+ begin
+ raise notice '%: tg_depth = %', tg_name, tg_depth;
+ raise exception sqlstate 'U9999';
+ return new;
+ end;
+ $$;
+ create trigger tg_depth_c_tr before insert on tg_depth_c
+ for each row execute procedure tg_depth_c_tf();
+ insert into tg_depth_a values (999);
+ NOTICE: tg_depth_a_tr: tg_depth = 1
+ NOTICE: tg_depth_b_tr: tg_depth = 2
+ CONTEXT: SQL statement "insert into tg_depth_b values (new.id)"
+ PL/pgSQL function "tg_depth_a_tf" line 4 at SQL statement
+ NOTICE: tg_depth_c_tr: tg_depth = 3
+ CONTEXT: SQL statement "insert into tg_depth_c values (999)"
+ PL/pgSQL function "tg_depth_b_tf" line 5 at EXECUTE statement
+ SQL statement "insert into tg_depth_b values (new.id)"
+ PL/pgSQL function "tg_depth_a_tf" line 4 at SQL statement
+ NOTICE: SQLSTATE = U9999: tg_depth = 2
+ CONTEXT: SQL statement "insert into tg_depth_b values (new.id)"
+ PL/pgSQL function "tg_depth_a_tf" line 4 at SQL statement
+ NOTICE: tg_depth_b_tr: tg_depth = 2
+ CONTEXT: SQL statement "insert into tg_depth_b values (new.id)"
+ PL/pgSQL function "tg_depth_a_tf" line 4 at SQL statement
+ NOTICE: tg_depth_c_tr: tg_depth = 3
+ CONTEXT: SQL statement "insert into tg_depth_c values (999)"
+ PL/pgSQL function "tg_depth_b_tf" line 11 at EXECUTE statement
+ SQL statement "insert into tg_depth_b values (new.id)"
+ PL/pgSQL function "tg_depth_a_tf" line 4 at SQL statement
+ ERROR: U9999
+ CONTEXT: SQL statement "insert into tg_depth_c values (999)"
+ PL/pgSQL function "tg_depth_b_tf" line 11 at EXECUTE statement
+ SQL statement "insert into tg_depth_b values (new.id)"
+ PL/pgSQL function "tg_depth_a_tf" line 4 at SQL statement
+ drop table tg_depth_a, tg_depth_b, tg_depth_c;
+ drop function tg_depth_a_tf();
+ drop function tg_depth_b_tf();
+ drop function tg_depth_c_tf();
*** a/src/test/regress/sql/plpgsql.sql
--- b/src/test/regress/sql/plpgsql.sql
***************
*** 3375,3377 **** $$ language plpgsql;
--- 3375,3431 ----
select unreserved_test();
drop function unreserved_test();
+
+ -- Test TG_DEPTH
+
+ create table tg_depth_a (id int not null primary key);
+ create table tg_depth_b (id int not null primary key);
+ create table tg_depth_c (id int not null primary key);
+
+ create function tg_depth_a_tf() returns trigger
+ language plpgsql as $$
+ begin
+ raise notice '%: tg_depth = %', tg_name, tg_depth;
+ insert into tg_depth_b values (new.id);
+ raise notice '%: tg_depth = %', tg_name, tg_depth;
+ return new;
+ end;
+ $$;
+ create trigger tg_depth_a_tr before insert on tg_depth_a
+ for each row execute procedure tg_depth_a_tf();
+
+ create function tg_depth_b_tf() returns trigger
+ language plpgsql as $$
+ begin
+ raise notice '%: tg_depth = %', tg_name, tg_depth;
+ begin
+ execute 'insert into tg_depth_c values (' || new.id::text || ')';
+ exception
+ when sqlstate 'U9999' then
+ raise notice 'SQLSTATE = U9999: tg_depth = %', tg_depth;
+ end;
+ raise notice '%: tg_depth = %', tg_name, tg_depth;
+ execute 'insert into tg_depth_c values (' || new.id::text || ')';
+ return new;
+ end;
+ $$;
+ create trigger tg_depth_b_tr before insert on tg_depth_b
+ for each row execute procedure tg_depth_b_tf();
+
+ create function tg_depth_c_tf() returns trigger
+ language plpgsql as $$
+ begin
+ raise notice '%: tg_depth = %', tg_name, tg_depth;
+ raise exception sqlstate 'U9999';
+ return new;
+ end;
+ $$;
+ create trigger tg_depth_c_tr before insert on tg_depth_c
+ for each row execute procedure tg_depth_c_tf();
+
+ insert into tg_depth_a values (999);
+
+ drop table tg_depth_a, tg_depth_b, tg_depth_c;
+ drop function tg_depth_a_tf();
+ drop function tg_depth_b_tf();
+ drop function tg_depth_c_tf();