mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-21 08:29:39 +08:00
Add pg_trigger_depth() function
This reports the depth level of triggers currently in execution, or zero if not called from inside a trigger. No catversion bump in this patch, but you have to initdb if you want access to the new function. Author: Kevin Grittner
This commit is contained in:
parent
6d5aae7afa
commit
74ab96a45e
@ -12794,6 +12794,13 @@ postgres=# SELECT * FROM unnest2(ARRAY[[1,2],[3,4]]);
|
|||||||
<entry>server start time</entry>
|
<entry>server start time</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><literal><function>pg_trigger_depth()</function></literal></entry>
|
||||||
|
<entry><type>int</type></entry>
|
||||||
|
<entry>current nesting level of <productname>PostgreSQL</> triggers
|
||||||
|
(0 if not called, directly or indirectly, from inside a trigger)</entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><literal><function>session_user</function></literal></entry>
|
<entry><literal><function>session_user</function></literal></entry>
|
||||||
<entry><type>name</type></entry>
|
<entry><type>name</type></entry>
|
||||||
|
@ -59,6 +59,8 @@
|
|||||||
/* GUC variables */
|
/* GUC variables */
|
||||||
int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN;
|
int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN;
|
||||||
|
|
||||||
|
/* How many levels deep into trigger execution are we? */
|
||||||
|
static int MyTriggerDepth = 0;
|
||||||
|
|
||||||
#define GetModifiedColumns(relinfo, estate) \
|
#define GetModifiedColumns(relinfo, estate) \
|
||||||
(rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->modifiedCols)
|
(rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->modifiedCols)
|
||||||
@ -1838,7 +1840,18 @@ ExecCallTriggerFunc(TriggerData *trigdata,
|
|||||||
|
|
||||||
pgstat_init_function_usage(&fcinfo, &fcusage);
|
pgstat_init_function_usage(&fcinfo, &fcusage);
|
||||||
|
|
||||||
result = FunctionCallInvoke(&fcinfo);
|
MyTriggerDepth++;
|
||||||
|
PG_TRY();
|
||||||
|
{
|
||||||
|
result = FunctionCallInvoke(&fcinfo);
|
||||||
|
}
|
||||||
|
PG_CATCH();
|
||||||
|
{
|
||||||
|
MyTriggerDepth--;
|
||||||
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
|
PG_END_TRY();
|
||||||
|
MyTriggerDepth--;
|
||||||
|
|
||||||
pgstat_end_function_usage(&fcusage, true);
|
pgstat_end_function_usage(&fcusage, true);
|
||||||
|
|
||||||
@ -4632,3 +4645,9 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
|
|||||||
&new_event, &new_shared);
|
&new_event, &new_shared);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
pg_trigger_depth(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
PG_RETURN_INT32(MyTriggerDepth);
|
||||||
|
}
|
||||||
|
@ -2698,6 +2698,9 @@ DESCR("statistics: reset collected statistics for a single table or index in the
|
|||||||
DATA(insert OID = 3777 ( pg_stat_reset_single_function_counters PGNSP PGUID 12 1 0 0 0 f f f f f v 1 0 2278 "26" _null_ _null_ _null_ _null_ pg_stat_reset_single_function_counters _null_ _null_ _null_ ));
|
DATA(insert OID = 3777 ( pg_stat_reset_single_function_counters PGNSP PGUID 12 1 0 0 0 f f f f f v 1 0 2278 "26" _null_ _null_ _null_ _null_ pg_stat_reset_single_function_counters _null_ _null_ _null_ ));
|
||||||
DESCR("statistics: reset collected statistics for a single function in the current database");
|
DESCR("statistics: reset collected statistics for a single function in the current database");
|
||||||
|
|
||||||
|
DATA(insert OID = 3163 ( pg_trigger_depth PGNSP PGUID 12 1 0 0 0 f f f t f s 0 0 23 "" _null_ _null_ _null_ _null_ pg_trigger_depth _null_ _null_ _null_ ));
|
||||||
|
DESCR("current trigger depth");
|
||||||
|
|
||||||
DATA(insert OID = 3778 ( pg_tablespace_location PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 25 "26" _null_ _null_ _null_ _null_ pg_tablespace_location _null_ _null_ _null_ ));
|
DATA(insert OID = 3778 ( pg_tablespace_location PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 25 "26" _null_ _null_ _null_ _null_ pg_tablespace_location _null_ _null_ _null_ ));
|
||||||
DESCR("tablespace location");
|
DESCR("tablespace location");
|
||||||
|
|
||||||
|
@ -205,4 +205,6 @@ extern bool RI_Initial_Check(Trigger *trigger,
|
|||||||
|
|
||||||
extern int RI_FKey_trigger_type(Oid tgfoid);
|
extern int RI_FKey_trigger_type(Oid tgfoid);
|
||||||
|
|
||||||
|
extern Datum pg_trigger_depth(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
#endif /* TRIGGER_H */
|
#endif /* TRIGGER_H */
|
||||||
|
@ -1443,3 +1443,120 @@ NOTICE: drop cascades to 2 other objects
|
|||||||
DETAIL: drop cascades to view city_view
|
DETAIL: drop cascades to view city_view
|
||||||
drop cascades to view european_city_view
|
drop cascades to view european_city_view
|
||||||
DROP TABLE country_table;
|
DROP TABLE country_table;
|
||||||
|
-- Test pg_trigger_depth()
|
||||||
|
create table depth_a (id int not null primary key);
|
||||||
|
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "depth_a_pkey" for table "depth_a"
|
||||||
|
create table depth_b (id int not null primary key);
|
||||||
|
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "depth_b_pkey" for table "depth_b"
|
||||||
|
create table depth_c (id int not null primary key);
|
||||||
|
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "depth_c_pkey" for table "depth_c"
|
||||||
|
create function depth_a_tf() returns trigger
|
||||||
|
language plpgsql as $$
|
||||||
|
begin
|
||||||
|
raise notice '%: depth = %', tg_name, pg_trigger_depth();
|
||||||
|
insert into depth_b values (new.id);
|
||||||
|
raise notice '%: depth = %', tg_name, pg_trigger_depth();
|
||||||
|
return new;
|
||||||
|
end;
|
||||||
|
$$;
|
||||||
|
create trigger depth_a_tr before insert on depth_a
|
||||||
|
for each row execute procedure depth_a_tf();
|
||||||
|
create function depth_b_tf() returns trigger
|
||||||
|
language plpgsql as $$
|
||||||
|
begin
|
||||||
|
raise notice '%: depth = %', tg_name, pg_trigger_depth();
|
||||||
|
begin
|
||||||
|
execute 'insert into depth_c values (' || new.id::text || ')';
|
||||||
|
exception
|
||||||
|
when sqlstate 'U9999' then
|
||||||
|
raise notice 'SQLSTATE = U9999: depth = %', pg_trigger_depth();
|
||||||
|
end;
|
||||||
|
raise notice '%: depth = %', tg_name, pg_trigger_depth();
|
||||||
|
if new.id = 1 then
|
||||||
|
execute 'insert into depth_c values (' || new.id::text || ')';
|
||||||
|
end if;
|
||||||
|
return new;
|
||||||
|
end;
|
||||||
|
$$;
|
||||||
|
create trigger depth_b_tr before insert on depth_b
|
||||||
|
for each row execute procedure depth_b_tf();
|
||||||
|
create function depth_c_tf() returns trigger
|
||||||
|
language plpgsql as $$
|
||||||
|
begin
|
||||||
|
raise notice '%: depth = %', tg_name, pg_trigger_depth();
|
||||||
|
if new.id = 1 then
|
||||||
|
raise exception sqlstate 'U9999';
|
||||||
|
end if;
|
||||||
|
raise notice '%: depth = %', tg_name, pg_trigger_depth();
|
||||||
|
return new;
|
||||||
|
end;
|
||||||
|
$$;
|
||||||
|
create trigger depth_c_tr before insert on depth_c
|
||||||
|
for each row execute procedure depth_c_tf();
|
||||||
|
select pg_trigger_depth();
|
||||||
|
pg_trigger_depth
|
||||||
|
------------------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
insert into depth_a values (1);
|
||||||
|
NOTICE: depth_a_tr: depth = 1
|
||||||
|
NOTICE: depth_b_tr: depth = 2
|
||||||
|
CONTEXT: SQL statement "insert into depth_b values (new.id)"
|
||||||
|
PL/pgSQL function "depth_a_tf" line 4 at SQL statement
|
||||||
|
NOTICE: depth_c_tr: depth = 3
|
||||||
|
CONTEXT: SQL statement "insert into depth_c values (1)"
|
||||||
|
PL/pgSQL function "depth_b_tf" line 5 at EXECUTE statement
|
||||||
|
SQL statement "insert into depth_b values (new.id)"
|
||||||
|
PL/pgSQL function "depth_a_tf" line 4 at SQL statement
|
||||||
|
NOTICE: SQLSTATE = U9999: depth = 2
|
||||||
|
CONTEXT: SQL statement "insert into depth_b values (new.id)"
|
||||||
|
PL/pgSQL function "depth_a_tf" line 4 at SQL statement
|
||||||
|
NOTICE: depth_b_tr: depth = 2
|
||||||
|
CONTEXT: SQL statement "insert into depth_b values (new.id)"
|
||||||
|
PL/pgSQL function "depth_a_tf" line 4 at SQL statement
|
||||||
|
NOTICE: depth_c_tr: depth = 3
|
||||||
|
CONTEXT: SQL statement "insert into depth_c values (1)"
|
||||||
|
PL/pgSQL function "depth_b_tf" line 12 at EXECUTE statement
|
||||||
|
SQL statement "insert into depth_b values (new.id)"
|
||||||
|
PL/pgSQL function "depth_a_tf" line 4 at SQL statement
|
||||||
|
ERROR: U9999
|
||||||
|
CONTEXT: SQL statement "insert into depth_c values (1)"
|
||||||
|
PL/pgSQL function "depth_b_tf" line 12 at EXECUTE statement
|
||||||
|
SQL statement "insert into depth_b values (new.id)"
|
||||||
|
PL/pgSQL function "depth_a_tf" line 4 at SQL statement
|
||||||
|
select pg_trigger_depth();
|
||||||
|
pg_trigger_depth
|
||||||
|
------------------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
insert into depth_a values (2);
|
||||||
|
NOTICE: depth_a_tr: depth = 1
|
||||||
|
NOTICE: depth_b_tr: depth = 2
|
||||||
|
CONTEXT: SQL statement "insert into depth_b values (new.id)"
|
||||||
|
PL/pgSQL function "depth_a_tf" line 4 at SQL statement
|
||||||
|
NOTICE: depth_c_tr: depth = 3
|
||||||
|
CONTEXT: SQL statement "insert into depth_c values (2)"
|
||||||
|
PL/pgSQL function "depth_b_tf" line 5 at EXECUTE statement
|
||||||
|
SQL statement "insert into depth_b values (new.id)"
|
||||||
|
PL/pgSQL function "depth_a_tf" line 4 at SQL statement
|
||||||
|
NOTICE: depth_c_tr: depth = 3
|
||||||
|
CONTEXT: SQL statement "insert into depth_c values (2)"
|
||||||
|
PL/pgSQL function "depth_b_tf" line 5 at EXECUTE statement
|
||||||
|
SQL statement "insert into depth_b values (new.id)"
|
||||||
|
PL/pgSQL function "depth_a_tf" line 4 at SQL statement
|
||||||
|
NOTICE: depth_b_tr: depth = 2
|
||||||
|
CONTEXT: SQL statement "insert into depth_b values (new.id)"
|
||||||
|
PL/pgSQL function "depth_a_tf" line 4 at SQL statement
|
||||||
|
NOTICE: depth_a_tr: depth = 1
|
||||||
|
select pg_trigger_depth();
|
||||||
|
pg_trigger_depth
|
||||||
|
------------------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
drop table depth_a, depth_b, depth_c;
|
||||||
|
drop function depth_a_tf();
|
||||||
|
drop function depth_b_tf();
|
||||||
|
drop function depth_c_tf();
|
||||||
|
@ -961,3 +961,67 @@ SELECT * FROM city_view;
|
|||||||
|
|
||||||
DROP TABLE city_table CASCADE;
|
DROP TABLE city_table CASCADE;
|
||||||
DROP TABLE country_table;
|
DROP TABLE country_table;
|
||||||
|
|
||||||
|
|
||||||
|
-- Test pg_trigger_depth()
|
||||||
|
|
||||||
|
create table depth_a (id int not null primary key);
|
||||||
|
create table depth_b (id int not null primary key);
|
||||||
|
create table depth_c (id int not null primary key);
|
||||||
|
|
||||||
|
create function depth_a_tf() returns trigger
|
||||||
|
language plpgsql as $$
|
||||||
|
begin
|
||||||
|
raise notice '%: depth = %', tg_name, pg_trigger_depth();
|
||||||
|
insert into depth_b values (new.id);
|
||||||
|
raise notice '%: depth = %', tg_name, pg_trigger_depth();
|
||||||
|
return new;
|
||||||
|
end;
|
||||||
|
$$;
|
||||||
|
create trigger depth_a_tr before insert on depth_a
|
||||||
|
for each row execute procedure depth_a_tf();
|
||||||
|
|
||||||
|
create function depth_b_tf() returns trigger
|
||||||
|
language plpgsql as $$
|
||||||
|
begin
|
||||||
|
raise notice '%: depth = %', tg_name, pg_trigger_depth();
|
||||||
|
begin
|
||||||
|
execute 'insert into depth_c values (' || new.id::text || ')';
|
||||||
|
exception
|
||||||
|
when sqlstate 'U9999' then
|
||||||
|
raise notice 'SQLSTATE = U9999: depth = %', pg_trigger_depth();
|
||||||
|
end;
|
||||||
|
raise notice '%: depth = %', tg_name, pg_trigger_depth();
|
||||||
|
if new.id = 1 then
|
||||||
|
execute 'insert into depth_c values (' || new.id::text || ')';
|
||||||
|
end if;
|
||||||
|
return new;
|
||||||
|
end;
|
||||||
|
$$;
|
||||||
|
create trigger depth_b_tr before insert on depth_b
|
||||||
|
for each row execute procedure depth_b_tf();
|
||||||
|
|
||||||
|
create function depth_c_tf() returns trigger
|
||||||
|
language plpgsql as $$
|
||||||
|
begin
|
||||||
|
raise notice '%: depth = %', tg_name, pg_trigger_depth();
|
||||||
|
if new.id = 1 then
|
||||||
|
raise exception sqlstate 'U9999';
|
||||||
|
end if;
|
||||||
|
raise notice '%: depth = %', tg_name, pg_trigger_depth();
|
||||||
|
return new;
|
||||||
|
end;
|
||||||
|
$$;
|
||||||
|
create trigger depth_c_tr before insert on depth_c
|
||||||
|
for each row execute procedure depth_c_tf();
|
||||||
|
|
||||||
|
select pg_trigger_depth();
|
||||||
|
insert into depth_a values (1);
|
||||||
|
select pg_trigger_depth();
|
||||||
|
insert into depth_a values (2);
|
||||||
|
select pg_trigger_depth();
|
||||||
|
|
||||||
|
drop table depth_a, depth_b, depth_c;
|
||||||
|
drop function depth_a_tf();
|
||||||
|
drop function depth_b_tf();
|
||||||
|
drop function depth_c_tf();
|
||||||
|
Loading…
Reference in New Issue
Block a user