depth-1.patch

text/plain

Filename: depth-1.patch
Type: text/plain
Part: 0
Message: TG_DEPTH patch v1

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: context
File+
doc/src/sgml/plpgsql.sgml 10 0
src/backend/access/transam/xact.c 7 0
src/backend/commands/trigger.c 7 0
src/backend/tcop/pquery.c 4 0
src/include/commands/trigger.h 2 0
src/pl/plpgsql/src/pl_comp.c 6 0
src/pl/plpgsql/src/pl_exec.c 5 0
src/pl/plpgsql/src/plpgsql.h 1 0
src/test/regress/expected/plpgsql.out 75 0
src/test/regress/sql/plpgsql.sql 54 0
*** 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 ----
      </varlistentry>
  
      <varlistentry>
+      <term><varname>TG_DEPTH</varname></term>
+      <listitem>
+       <para>
+        Data type <type>integer</type>; the current number of levels of
+        nesting within trigger execution.
+       </para>
+      </listitem>
+     </varlistentry>
+ 
+     <varlistentry>
       <term><varname>TG_NARGS</varname></term>
       <listitem>
        <para>
*** 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();