Add support for TG_DEPTH to PL/pgSQL trigger functions.
authorKevin Grittner <Kevin.Grittner@wicourts.gov>
Fri, 28 Jan 2011 21:15:48 +0000 (15:15 -0600)
committerKevin Grittner <Kevin.Grittner@wicourts.gov>
Fri, 28 Jan 2011 21:15:48 +0000 (15:15 -0600)
src/backend/access/transam/xact.c
src/backend/commands/trigger.c
src/backend/tcop/pquery.c
src/include/commands/trigger.h
src/pl/plpgsql/src/pl_comp.c
src/pl/plpgsql/src/pl_exec.c
src/pl/plpgsql/src/plpgsql.h

index 1e31e07ec97510269676c2987bec4e0344c0eaf2..ddfbd3a1fddfe3e0ff9733291f0de3572295a218 100644 (file)
@@ -142,6 +142,7 @@ typedef struct TransactionStateData
    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,6 +172,7 @@ static TransactionStateData TopTransactionStateData = {
    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,6 +3998,8 @@ CommitSubTransaction(void)
     */
    XactReadOnly = s->prevXactReadOnly;
 
+   tg_depth = s->prevTgDepth;
+
    CurrentResourceOwner = s->parent->curTransactionOwner;
    CurTransactionResourceOwner = s->parent->curTransactionOwner;
    ResourceOwnerDelete(s->curTransactionOwner);
@@ -4114,6 +4118,8 @@ AbortSubTransaction(void)
     */
    XactReadOnly = s->prevXactReadOnly;
 
+   tg_depth = s->prevTgDepth;
+
    RESUME_INTERRUPTS();
 }
 
@@ -4196,6 +4202,7 @@ PushTransaction(void)
    s->blockState = TBLOCK_SUBBEGIN;
    GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext);
    s->prevXactReadOnly = XactReadOnly;
+   s->prevTgDepth = tg_depth;
 
    CurrentTransactionState = s;
 
index 48021388fc571c360d87b5d6eeb9e784479dd622..056b656fc2012a32629bd6646d6e44aea8d072ee 100644 (file)
 #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;
 
-
 #define GetModifiedColumns(relinfo, estate) \
    (rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->modifiedCols)
 
@@ -1807,6 +1809,8 @@ ExecCallTriggerFunc(TriggerData *trigdata,
    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,6 +1832,8 @@ ExecCallTriggerFunc(TriggerData *trigdata,
 
    MemoryContextSwitchTo(oldContext);
 
+   tg_depth--;
+
    /*
     * Trigger protocol allows function to return a null pointer, but NOT to
     * set the isnull result flag.
index 0b108ac134897a9bdce921adc9b995fbd14ff853..5b67346388a582f82a12e7436f84b2de972be093 100644 (file)
@@ -748,6 +748,8 @@ PortalRun(Portal portal, long count, bool isTopLevel,
                 errmsg("portal \"%s\" cannot be run", portal->name)));
    portal->status = PORTAL_ACTIVE;
 
+   tg_depth = 0;
+
    /*
     * Set up global portal context pointers.
     *
@@ -1371,6 +1373,8 @@ PortalRunFetch(Portal portal,
                 errmsg("portal \"%s\" cannot be run", portal->name)));
    portal->status = PORTAL_ACTIVE;
 
+   tg_depth = 0;
+
    /*
     * Set up global portal context pointers.
     */
index c213ac7a4efa32ab79e80560a4a040fc4e666636..90a3ea7a618ae883886f84a85c9810da0da37f21 100644 (file)
@@ -108,6 +108,8 @@ extern PGDLLIMPORT int SessionReplicationRole;
 #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);
index 41188a2369fd3632adbb7496c02d80e9ef964075..175ad7ab8535f402c10ef851fa62748eeb96892d 100644 (file)
@@ -633,6 +633,12 @@ do_compile(FunctionCallInfo fcinfo,
                                         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),
index 3e40945e35d21660897f31eeeebfb33904eca347..82b73a0490a1a8b3ca3e079ddffcde9a77d2f36c 100644 (file)
@@ -625,6 +625,11 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
    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;
index 0ad7e28136bfb802c0715426018946d410c6085e..2165526957be722ff47d17bbffa1a105f40d5518 100644 (file)
@@ -668,6 +668,7 @@ typedef struct PLpgSQL_function
    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;