diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index eea6ec5..5be8086 100644
*** a/doc/src/sgml/plpgsql.sgml
--- b/doc/src/sgml/plpgsql.sgml
*************** RAISE unique_violation USING MESSAGE = '
*** 3383,3388 ****
--- 3383,3398 ----
+ TG_DEPTH
+
+
+ Data type integer; the current number of levels of
+ nesting within trigger execution.
+
+
+
+
+
TG_NARGS
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 2ca1c14..273fcae 100644
*** a/src/backend/access/transam/xact.c
--- b/src/backend/access/transam/xact.c
*************** typedef struct TransactionStateData
*** 147,152 ****
--- 147,153 ----
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;
*************** static TransactionStateData TopTransacti
*** 176,181 ****
--- 177,183 ----
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 */
};
*************** CommitSubTransaction(void)
*** 4039,4044 ****
--- 4041,4048 ----
*/
XactReadOnly = s->prevXactReadOnly;
+ tg_depth = s->prevTgDepth;
+
CurrentResourceOwner = s->parent->curTransactionOwner;
CurTransactionResourceOwner = s->parent->curTransactionOwner;
ResourceOwnerDelete(s->curTransactionOwner);
*************** AbortSubTransaction(void)
*** 4157,4162 ****
--- 4161,4168 ----
*/
XactReadOnly = s->prevXactReadOnly;
+ tg_depth = s->prevTgDepth;
+
RESUME_INTERRUPTS();
}
*************** PushTransaction(void)
*** 4239,4244 ****
--- 4245,4251 ----
s->blockState = TBLOCK_SUBBEGIN;
GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext);
s->prevXactReadOnly = XactReadOnly;
+ s->prevTgDepth = tg_depth;
CurrentTransactionState = s;
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index ce36ea8..a9cb0a0 100644
*** a/src/backend/commands/trigger.c
--- b/src/backend/commands/trigger.c
***************
*** 56,61 ****
--- 56,64 ----
#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;
*************** ExecCallTriggerFunc(TriggerData *trigdat
*** 1811,1816 ****
--- 1814,1821 ----
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
*************** ExecCallTriggerFunc(TriggerData *trigdat
*** 1833,1838 ****
--- 1838,1845 ----
MemoryContextSwitchTo(oldContext);
+ tg_depth--;
+
/*
* Trigger protocol allows function to return a null pointer, but NOT to
* set the isnull result flag.
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index b7649c6..a784b32 100644
*** a/src/backend/tcop/pquery.c
--- b/src/backend/tcop/pquery.c
*************** PortalRun(Portal portal, long count, boo
*** 738,743 ****
--- 738,745 ----
errmsg("portal \"%s\" cannot be run", portal->name)));
portal->status = PORTAL_ACTIVE;
+ tg_depth = 0;
+
/*
* Set up global portal context pointers.
*
*************** PortalRunFetch(Portal portal,
*** 1401,1406 ****
--- 1403,1410 ----
errmsg("portal \"%s\" cannot be run", portal->name)));
portal->status = PORTAL_ACTIVE;
+ tg_depth = 0;
+
/*
* Set up global portal context pointers.
*/
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index ad97871..349341e 100644
*** a/src/include/commands/trigger.h
--- b/src/include/commands/trigger.h
*************** extern PGDLLIMPORT int SessionReplicatio
*** 108,113 ****
--- 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);
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 75098ec..83c60e8 100644
*** a/src/pl/plpgsql/src/pl_comp.c
--- b/src/pl/plpgsql/src/pl_comp.c
*************** do_compile(FunctionCallInfo fcinfo,
*** 655,660 ****
--- 655,668 ----
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,
+ InvalidOid),
+ true);
+ function->tg_depth_varno = var->dno;
+
/* Add the variable tg_nargs */
var = plpgsql_build_variable("tg_nargs", 0,
plpgsql_build_datatype(INT4OID,
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 906a485..13f7238 100644
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
*************** plpgsql_exec_trigger(PLpgSQL_function *f
*** 627,632 ****
--- 627,637 ----
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;
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 89103ae..a5512fb 100644
*** a/src/pl/plpgsql/src/plpgsql.h
--- b/src/pl/plpgsql/src/plpgsql.h
*************** typedef struct PLpgSQL_function
*** 690,695 ****
--- 690,696 ----
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;
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index bfabcbc..1f36944 100644
*** a/src/test/regress/expected/plpgsql.out
--- b/src/test/regress/expected/plpgsql.out
*************** NOTICE: {"(35,78)","(88,76)"}
*** 4434,4436 ****
--- 4434,4511 ----
drop function foreach_test(anyarray);
drop type xy_tuple;
+ -- 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();
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 14fb457..46ebef1 100644
*** a/src/test/regress/sql/plpgsql.sql
--- b/src/test/regress/sql/plpgsql.sql
*************** select foreach_test(ARRAY[[(10,20),(40,6
*** 3489,3491 ****
--- 3489,3547 ----
drop function foreach_test(anyarray);
drop type xy_tuple;
+
+
+ -- 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();
+