deparse: initial support for ALTER TABLE
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Fri, 25 Apr 2014 19:32:20 +0000 (16:32 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 18 Jun 2014 15:15:35 +0000 (11:15 -0400)
src/backend/commands/event_trigger.c
src/backend/commands/tablecmds.c
src/backend/tcop/deparse_utility.c
src/backend/tcop/utility.c
src/include/commands/event_trigger.h
src/include/tcop/deparse_utility.h

index 303ab81d19707da4e75a4b7e1840e7d38e7da219..dca8a4dce8068761b92559c6ccfd6975afdedf92 100644 (file)
@@ -50,6 +50,7 @@ typedef struct EventTriggerQueryState
    slist_head  SQLDropList;
    bool        in_sql_drop;
    MemoryContext cxt;
+   StashedCommand *curcmd;
    List       *stash;      /* list of StashedCommand; see deparse_utility.h */
    bool        in_extension;
    struct EventTriggerQueryState *previous;
@@ -1041,6 +1042,7 @@ EventTriggerBeginCompleteQuery(void)
    state->cxt = cxt;
    slist_init(&(state->SQLDropList));
    state->in_sql_drop = false;
+   state->curcmd = NULL;
    state->stash = NIL;
    state->in_extension = currentEventTriggerState ? currentEventTriggerState->in_extension : false;
 
@@ -1340,6 +1342,113 @@ EventTriggerStashExtensionStop(void)
        currentEventTriggerState->in_extension = false;
 }
 
+/*
+ * EventTriggerStartRecordingSubcmds
+ *         Prepare to receive data on a complex DDL command about to be executed
+ *
+ * Note we don't actually stash the object we create here into the "stashed"
+ * list; instead we keep it in curcmd, and only when we're done processing the
+ * subcommands we will add it to the actual stash.
+ *
+ * FIXME -- this API isn't considering the possibility of an ALTER TABLE command
+ * being called reentrantly by an event trigger function.  Do we need stackable
+ * commands at this level?
+ */
+void
+EventTriggerComplexCmdStart(Node *parsetree, ObjectType objtype)
+{
+   MemoryContext   oldcxt;
+   StashedCommand *stashed;
+
+   oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
+
+   stashed = palloc(sizeof(StashedCommand));
+
+   stashed->type = SCT_AlterTable;
+   stashed->in_extension = currentEventTriggerState->in_extension;
+
+   stashed->d.alterTable.objectId = InvalidOid;
+   stashed->d.alterTable.objtype = objtype;
+   stashed->d.alterTable.subcmds = NIL;
+   /* XXX is it necessary to have the whole parsetree? probably not ... */
+   stashed->parsetree = copyObject(parsetree);
+
+   currentEventTriggerState->curcmd = stashed;
+
+   MemoryContextSwitchTo(oldcxt);
+}
+
+void
+EventTriggerComplexCmdSetOid(Oid objectId)
+{
+   currentEventTriggerState->curcmd->d.alterTable.objectId = objectId;
+}
+
+/*
+ * EventTriggerRecordSubcmd
+ *         Save data about a single part of a complex DDL command
+ *
+ * Right now we only support ALTER TABLE; there are no other DDL commands that
+ * require this.  (ALTER TYPE can also generate multiple subcommands, but it's
+ * actually parsed as ALTER TABLE, so there is no difference at this level.)
+ */
+void
+EventTriggerRecordSubcmd(Node *subcmd, Oid relid, AttrNumber attnum,
+                        Oid newoid)
+{
+   MemoryContext   oldcxt;
+   StashedATSubcmd *newsub;
+
+   Assert(IsA(subcmd, AlterTableCmd));
+   Assert(OidIsValid(currentEventTriggerState->curcmd->d.alterTable.objectId));
+
+   /*
+    * If we receive a subcommand intended for a relation other than the one
+    * we've started the complex command for, ignore it.  This is chiefly
+    * concerned with inheritance situations: in such cases, alter table
+    * would dispatch multiple copies of the same command for various things,
+    * but we're only concerned with the one for the main table.
+    */
+   if (relid != currentEventTriggerState->curcmd->d.alterTable.objectId)
+       return;
+
+   oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
+
+   newsub = palloc(sizeof(StashedATSubcmd));
+   newsub->attnum = attnum;
+   newsub->oid = newoid;
+   newsub->parsetree = copyObject(subcmd);
+
+   currentEventTriggerState->curcmd->d.alterTable.subcmds =
+       lappend(currentEventTriggerState->curcmd->d.alterTable.subcmds, newsub);
+
+   MemoryContextSwitchTo(oldcxt);
+}
+
+/*
+ * EventTriggerEndRecordingSubcmds
+ *         Finish up saving a complex DDL command
+ *
+ * FIXME this API isn't considering the possibility that a xact/subxact is
+ * aborted partway through.  Probably it's best to add an
+ * AtEOSubXact_EventTriggers() to fix this.
+ */
+void
+EventTriggerComplexCmdEnd(void)
+{
+   /* If no subcommands, don't stash anything */
+   if (list_length(currentEventTriggerState->curcmd->d.alterTable.subcmds) != 0)
+   {
+       currentEventTriggerState->stash =
+           lappend(currentEventTriggerState->stash,
+                   currentEventTriggerState->curcmd);
+   }
+   else
+       pfree(currentEventTriggerState->curcmd);
+
+   currentEventTriggerState->curcmd = NULL;
+}
+
 Datum
 pg_event_trigger_get_creation_commands(PG_FUNCTION_ARGS)
 {
@@ -1418,7 +1527,8 @@ pg_event_trigger_get_creation_commands(PG_FUNCTION_ARGS)
 
            MemSet(nulls, 0, sizeof(nulls));
 
-           if (cmd->type == SCT_Basic)
+           if (cmd->type == SCT_Basic ||
+               cmd->type == SCT_AlterTable)
            {
                Oid         classId;
                Oid         objId;
@@ -1432,6 +1542,11 @@ pg_event_trigger_get_creation_commands(PG_FUNCTION_ARGS)
                    classId = get_objtype_catalog_oid(cmd->d.basic.objtype);
                    objId = cmd->d.basic.objectId;
                }
+               else if (cmd->type == SCT_AlterTable)
+               {
+                   classId = get_objtype_catalog_oid(cmd->d.alterTable.objtype);
+                   objId = cmd->d.alterTable.objectId;
+               }
 
                tag = CreateCommandTag(cmd->parsetree);
                addr.classId = classId;
index 46dcf0ee5db2cfe3f14d6bf73eadf3314841679d..cab0b0b649fc2cb9e6acc220ac2c1c6b98224dd1 100644 (file)
@@ -2728,6 +2728,8 @@ AlterTableInternal(Oid relid, List *cmds, bool recurse)
 
    rel = relation_open(relid, lockmode);
 
+   EventTriggerComplexCmdSetOid(relid);
+
    ATController(rel, cmds, recurse, lockmode);
 }
 
@@ -3553,6 +3555,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
            break;
    }
 
+   EventTriggerRecordSubcmd((Node *) cmd, RelationGetRelid(rel),
+                            colno, newoid);
+
    /*
     * Bump the command counter to ensure the next subcommand in the sequence
     * can see the changes so far
@@ -5615,6 +5620,8 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
  * There is no such command in the grammar, but parse_utilcmd.c converts
  * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands.  This lets
  * us schedule creation of the index at the appropriate time during ALTER.
+ *
+ * Return value is the OID of the new index.
  */
 static Oid
 ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
index 2412843f16fdc9ebd462cff04f4be64052fe1319..040c721ac2b3d850bf83937b546955cd3d4e6e24 100644 (file)
@@ -2845,6 +2845,396 @@ deparse_AlterEnumStmt(Oid objectId, Node *parsetree)
    return command;
 }
 
+static char *
+deparse_AlterTableStmt(StashedCommand *cmd)
+{
+   ObjTree    *alterTableStmt;
+   ObjTree    *tmp;
+   ObjTree    *tmp2;
+   List       *dpcontext;
+   Relation    rel;
+   List       *subcmds = NIL;
+   ListCell   *cell;
+   char       *command;
+
+   rel = heap_open(cmd->d.alterTable.objectId, AccessShareLock);
+   dpcontext = deparse_context_for(RelationGetRelationName(rel),
+                                   cmd->d.alterTable.objectId);
+
+   alterTableStmt =
+       new_objtree_VA("ALTER TABLE %{identity}D %{subcmds:, }s", 0);
+   tmp = new_objtree_for_qualname(rel->rd_rel->relnamespace,
+                                  RelationGetRelationName(rel));
+   append_object_object(alterTableStmt, "identity", tmp);
+
+   foreach(cell, cmd->d.alterTable.subcmds)
+   {
+       StashedATSubcmd *substashed = (StashedATSubcmd *) lfirst(cell);
+       AlterTableCmd   *subcmd = (AlterTableCmd *) substashed->parsetree;
+       ObjTree    *tree;
+
+       Assert(IsA(subcmd, AlterTableCmd));
+
+       switch (subcmd->subtype)
+       {
+           case AT_AddColumn:
+           case AT_AddColumnRecurse:
+               /* XXX need to set the "recurse" bit somewhere? */
+               Assert(IsA(subcmd->def, ColumnDef));
+               tree = deparse_ColumnDef(rel, dpcontext, false,
+                                        (ColumnDef *) subcmd->def);
+               tmp = new_objtree_VA("ADD COLUMN %{definition}s",
+                                    2, "type", ObjTypeString, "add column",
+                                    "definition", ObjTypeObject, tree);
+               subcmds = lappend(subcmds,
+                                 new_object_object(NULL, tmp));
+               break;
+
+           case AT_DropColumnRecurse:
+           case AT_ValidateConstraintRecurse:
+           case AT_DropConstraintRecurse:
+           case AT_AddOidsRecurse:
+           case AT_AddIndexConstraint:
+           case AT_ReAddIndex:
+           case AT_ReAddConstraint:
+           case AT_ProcessedConstraint:
+           case AT_ReplaceRelOptions:
+               /* Subtypes used for internal operations; nothing to do here */
+               break;
+
+           case AT_AddColumnToView:
+               /* CREATE OR REPLACE VIEW -- nothing to do here */
+               break;
+
+           case AT_ColumnDefault:
+               if (subcmd->def == NULL)
+               {
+                   tmp = new_objtree_VA("ALTER COLUMN %{column}I DROP DEFAULT",
+                                        1, "type", ObjTypeString, "drop default");
+               }
+               else
+               {
+                   List       *dpcontext;
+                   HeapTuple   attrtup;
+                   AttrNumber  attno;
+
+                   tmp = new_objtree_VA("ALTER COLUMN %{column}I SET DEFAULT %{definition}s",
+                                        1, "type", ObjTypeString, "set default");
+
+                   dpcontext = deparse_context_for(RelationGetRelationName(rel),
+                                                   RelationGetRelid(rel));
+                   attrtup = SearchSysCacheAttName(RelationGetRelid(rel), subcmd->name);
+                   attno = ((Form_pg_attribute) GETSTRUCT(attrtup))->attnum;
+                   append_string_object(tmp, "definition",
+                                        RelationGetColumnDefault(rel, attno, dpcontext));
+                   ReleaseSysCache(attrtup);
+               }
+               append_string_object(tmp, "column", subcmd->name);
+
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_DropNotNull:
+               tmp = new_objtree_VA("ALTER COLUMN %{column}I DROP NOT NULL",
+                                    1, "type", ObjTypeString, "drop not null");
+               append_string_object(tmp, "column", subcmd->name);
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_SetNotNull:
+               tmp = new_objtree_VA("ALTER COLUMN %{column}I SET NOT NULL",
+                                    1, "type", ObjTypeString, "set not null");
+               append_string_object(tmp, "column", subcmd->name);
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_SetStatistics:
+               /* not yet */
+               break;
+
+           case AT_SetOptions:
+               /* not yet */
+               break;
+
+           case AT_ResetOptions:
+               /* not yet */
+               break;
+
+           case AT_SetStorage:
+               Assert(IsA(subcmd->def, String));
+               tmp = new_objtree_VA("ALTER COLUMN %{column}I SET STORAGE %{storage}s",
+                                    3, "type", ObjTypeString, "set storage",
+                                    "column", ObjTypeString, subcmd->name,
+                                    "storage", ObjTypeString,
+                                    strVal((Value *) subcmd->def));
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_DropColumn:
+               tmp = new_objtree_VA("DROP COLUMN %{column}I",
+                                    2, "type", ObjTypeString, "drop column",
+                                "column", ObjTypeString, subcmd->name);
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_AddIndex:
+               {
+                   Oid         idxOid = substashed->oid;
+                   IndexStmt  *istmt;
+                   Relation    idx;
+                   const char *idxname;
+                   Oid         constrOid;
+
+                   Assert(IsA(subcmd->def, IndexStmt));
+                   istmt = (IndexStmt *) subcmd->def;
+
+                   if (!istmt->isconstraint)
+                       break;
+
+                   idx = relation_open(idxOid, AccessShareLock);
+                   idxname = RelationGetRelationName(idx);
+
+                   constrOid = get_relation_constraint_oid(
+                       cmd->d.alterTable.objectId, idxname, false);
+
+                   tmp = new_objtree_VA("ADD CONSTRAINT %{name}I %{definition}s",
+                                        3, "type", ObjTypeString, "add constraint",
+                                        "name", ObjTypeString, idxname,
+                                        "definition", ObjTypeString,
+                                        pg_get_constraintdef_string(constrOid, false));
+                   subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+
+                   relation_close(idx, AccessShareLock);
+               }
+               break;
+
+           case AT_AddConstraint:
+           case AT_AddConstraintRecurse:
+               {
+                   /* XXX need to set the "recurse" bit somewhere? */
+                   Oid         constrOid = substashed->oid;
+
+                   tmp = new_objtree_VA("ADD CONSTRAINT %{name}I %{definition}s",
+                                        3, "type", ObjTypeString, "add constraint",
+                                        "name", ObjTypeString, get_constraint_name(constrOid),
+                                        "definition", ObjTypeString,
+                                        pg_get_constraintdef_string(constrOid, false));
+                   subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               }
+               break;
+
+           case AT_AlterConstraint:
+               break;
+
+           case AT_ValidateConstraint:
+               tmp = new_objtree_VA("VALIDATE CONSTRAINT %{constraint}I", 2,
+                                    "type", ObjTypeString, "validate constraint",
+                                    "constraint", ObjTypeString, subcmd->name);
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_DropConstraint:
+               tmp = new_objtree_VA("DROP CONSTRAINT %{constraint}I", 2,
+                                    "type", ObjTypeString, "drop constraint",
+                                    "constraint", ObjTypeString, subcmd->name);
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_AlterColumnType:
+               tmp = new_objtree_VA("ALTER COLUMN %{column}I SET DATA TYPE %{datatype}T collate_clause using_clause",
+                                    2, "type", ObjTypeString, "alter column type",
+                                    "column", ObjTypeString, subcmd->name);
+               /* FIXME figure out correct typid/typmod , collate clause, using_clause */
+               append_object_object(tmp, "datatype",
+                                    new_objtree_for_type(INT4OID, -1));
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_AlterColumnGenericOptions:
+               break;
+
+           case AT_ChangeOwner:
+               tmp = new_objtree_VA("OWNER TO %{owner}I",
+                                    2, "type", ObjTypeString, "change owner",
+                                    "owner",  ObjTypeString, subcmd->name);
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_ClusterOn:
+               tmp = new_objtree_VA("CLUSTER ON %{index}I", 2,
+                                    "type", ObjTypeString, "cluster on",
+                                    "index", ObjTypeString, subcmd->name);
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_DropCluster:
+               tmp = new_objtree_VA("SET WITHOUT CLUSTER", 1,
+                                    "type", ObjTypeString, "set without cluster");
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_AddOids:
+               tmp = new_objtree_VA("SET WITH OIDS", 1,
+                                    "type", ObjTypeString, "set with oids");
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_DropOids:
+               tmp = new_objtree_VA("SET WITHOUT OIDS", 1,
+                                    "type", ObjTypeString, "set without oids");
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_SetTableSpace:
+               tmp = new_objtree_VA("SET TABLESPACE %{tablespace}I", 2,
+                                    "type", ObjTypeString, "set tablespace",
+                                    "tablespace", ObjTypeString, subcmd->name);
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_SetRelOptions:
+               break;
+
+           case AT_ResetRelOptions:
+               break;
+
+               /*
+                * FIXME --- should we unify representation of all these
+                * ENABLE/DISABLE TRIGGER commands??
+                */
+           case AT_EnableTrig:
+               tmp = new_objtree_VA("ENABLE TRIGGER %{trigger}I", 2,
+                                    "type", ObjTypeString, "enable trigger",
+                                    "trigger", ObjTypeString, subcmd->name);
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_EnableAlwaysTrig:
+               tmp = new_objtree_VA("ENABLE ALWAYS TRIGGER %{trigger}I", 2,
+                                    "type", ObjTypeString, "enable always trigger",
+                                    "trigger", ObjTypeString, subcmd->name);
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_EnableReplicaTrig:
+               tmp = new_objtree_VA("ENABLE REPLICA TRIGGER %{trigger}I", 2,
+                                    "type", ObjTypeString, "enable replica trigger",
+                                    "trigger", ObjTypeString, subcmd->name);
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_DisableTrig:
+               tmp = new_objtree_VA("DISABLE TRIGGER %{trigger}I", 2,
+                                    "type", ObjTypeString, "disable trigger",
+                                    "trigger", ObjTypeString, subcmd->name);
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_EnableTrigAll:
+               tmp = new_objtree_VA("ENABLE TRIGGER ALL", 1,
+                                    "type", ObjTypeString, "enable trigger all");
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_DisableTrigAll:
+               tmp = new_objtree_VA("DISABLE TRIGGER ALL", 1,
+                                    "type", ObjTypeString, "disable trigger all");
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_EnableTrigUser:
+               tmp = new_objtree_VA("ENABLE TRIGGER USER", 1,
+                                    "type", ObjTypeString, "enable trigger user");
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_DisableTrigUser:
+               tmp = new_objtree_VA("DISABLE TRIGGER USER", 1,
+                                    "type", ObjTypeString, "disable trigger user");
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_EnableRule:
+               break;
+
+           case AT_EnableAlwaysRule:
+               break;
+
+           case AT_EnableReplicaRule:
+               break;
+
+           case AT_DisableRule:
+               break;
+
+           case AT_AddInherit:
+               /*
+                * XXX this case is interesting: we cannot rely on parse node
+                * because parent name might be unqualified; but there's no way
+                * to extract it from catalog either, since we don't know which
+                * of the parents is the new one.
+                */
+               break;
+
+           case AT_DropInherit:
+               /* XXX ditto ... */
+               break;
+
+           case AT_AddOf:
+               break;
+
+           case AT_DropOf:
+               break;
+
+           case AT_ReplicaIdentity:
+               tmp = new_objtree_VA("REPLICA IDENTITY %{ident}s", 1,
+                                    "type", ObjTypeString, "replica identity");
+               switch (((ReplicaIdentityStmt *) subcmd->def)->identity_type)
+               {
+                   case REPLICA_IDENTITY_DEFAULT:
+                       append_string_object(tmp, "ident", "DEFAULT");
+                       break;
+                   case REPLICA_IDENTITY_FULL:
+                       append_string_object(tmp, "ident", "FULL");
+                       break;
+                   case REPLICA_IDENTITY_NOTHING:
+                       append_string_object(tmp, "ident", "NOTHING");
+                       break;
+                   case REPLICA_IDENTITY_INDEX:
+                       tmp2 = new_objtree_VA("USING INDEX %{index}I", 1,
+                                             "index", ObjTypeString,
+                                             ((ReplicaIdentityStmt *) subcmd->def)->name);
+                       append_object_object(tmp, "ident", tmp2);
+                       break;
+               }
+               subcmds = lappend(subcmds, new_object_object(NULL, tmp));
+               break;
+
+           case AT_GenericOptions:
+               break;
+
+           default:
+               elog(WARNING, "unsupported alter table subtype %d",
+                    subcmd->subtype);
+               break;
+       }
+   }
+
+   if (list_length(subcmds) == 0)
+   {
+       command = NULL;
+   }
+   else
+   {
+       append_array_object(alterTableStmt, "subcmds", subcmds);
+       command = jsonize_objtree(alterTableStmt);
+   }
+
+   free_objtree(alterTableStmt);
+   heap_close(rel, AccessShareLock);
+
+   return command;
+}
+
 static char *
 deparse_parsenode_cmd(StashedCommand *cmd)
 {
@@ -2859,6 +3249,10 @@ deparse_parsenode_cmd(StashedCommand *cmd)
        case SCT_Basic:
            objectId = cmd->d.basic.objectId;
            break;
+       case SCT_AlterTable:
+           /* XXX needed? */
+           objectId = cmd->d.alterTable.objectId;
+           break;
        default:
            elog(ERROR, "unexpected deparse node type %d", cmd->type);
    }
@@ -2954,7 +3348,7 @@ deparse_parsenode_cmd(StashedCommand *cmd)
            break;
 
        case T_AlterTableStmt:
-           command = NULL;
+           command = deparse_AlterTableStmt(cmd);
            break;
 
        case T_AlterEnumStmt:
@@ -3013,6 +3407,7 @@ deparse_utility_command(StashedCommand *cmd)
    switch (cmd->type)
    {
        case SCT_Basic:
+       case SCT_AlterTable:
            command = deparse_parsenode_cmd(cmd);
            break;
        default:
index beedb01e220633a5356d950d5ac487e9e88dc481..03547a650626238fc078a55069d381b3a7001131 100644 (file)
@@ -982,6 +982,10 @@ ProcessUtilitySlow(Node *parsetree,
                        stmts = transformAlterTableStmt(relid, atstmt,
                                                        queryString);
 
+                       /* ... ensure we have an event trigger context ... */
+                       EventTriggerComplexCmdStart(parsetree, atstmt->relkind);
+                       EventTriggerComplexCmdSetOid(relid);
+
                        /* ... and do it */
                        foreach(l, stmts)
                        {
@@ -995,19 +999,32 @@ ProcessUtilitySlow(Node *parsetree,
                            }
                            else
                            {
-                               /* Recurse for anything else */
+                               /*
+                                * Recurse for anything else.  If we need to do
+                                * so, "close" the current complex-command set,
+                                * and start a new one at the bottom; this is
+                                * needed to ensure the ordering of queued
+                                * commands is consistent with the way they are
+                                * executed here.
+                                */
+                               EventTriggerComplexCmdEnd();
                                ProcessUtility(stmt,
                                               queryString,
                                               PROCESS_UTILITY_SUBCOMMAND,
                                               params,
                                               None_Receiver,
                                               NULL);
+                               EventTriggerComplexCmdStart(parsetree, atstmt->relkind);
+                               EventTriggerComplexCmdSetOid(relid);
                            }
 
                            /* Need CCI between commands */
                            if (lnext(l) != NULL)
                                CommandCounterIncrement();
                        }
+
+                       /* done */
+                       EventTriggerComplexCmdEnd();
                    }
                    else
                        ereport(NOTICE,
@@ -1156,6 +1173,7 @@ ProcessUtilitySlow(Node *parsetree,
                    stmt = transformIndexStmt(relid, stmt, queryString);
 
                    /* ... and do it */
+                   EventTriggerComplexCmdStart(parsetree, OBJECT_INDEX);   /* relkind? */
                    objectId =
                        DefineIndex(relid,  /* OID of heap relation */
                                    stmt,
@@ -1166,6 +1184,7 @@ ProcessUtilitySlow(Node *parsetree,
                                    false); /* quiet */
                    EventTriggerStashCommand(objectId, OBJECT_INDEX,
                                             parsetree);
+                   EventTriggerComplexCmdEnd();
                }
                break;
 
@@ -1249,8 +1268,10 @@ ProcessUtilitySlow(Node *parsetree,
                break;
 
            case T_ViewStmt:    /* CREATE VIEW */
+               EventTriggerComplexCmdStart(parsetree, OBJECT_VIEW);    /* XXX relkind? */
                objectId = DefineView((ViewStmt *) parsetree, queryString);
                EventTriggerStashCommand(objectId, OBJECT_VIEW, parsetree);
+               EventTriggerComplexCmdEnd();
                break;
 
            case T_CreateFunctionStmt:  /* CREATE FUNCTION */
@@ -1377,7 +1398,9 @@ ProcessUtilitySlow(Node *parsetree,
                break;
 
            case T_AlterTableSpaceMoveStmt:
+               EventTriggerComplexCmdStart(parsetree, OBJECT_TABLE);   /* XXX relkind? */
                AlterTableSpaceMove((AlterTableSpaceMoveStmt *) parsetree);
+               EventTriggerComplexCmdEnd();
                break;
 
            case T_AlterOwnerStmt:
index 8ddca3d31fc52659e689a2ab6c9b8900845af520..3e84cf79cff51dda90fec7002fc0aa840405a65b 100644 (file)
@@ -56,5 +56,10 @@ extern void EventTriggerStashExtensionStart(void);
 extern void EventTriggerStashExtensionStop(void);
 extern void EventTriggerStashCommand(Oid objectId, ObjectType objtype,
                         Node *parsetree);
+extern void EventTriggerComplexCmdStart(Node *parsetree, ObjectType objtype);
+extern void EventTriggerComplexCmdSetOid(Oid objectId);
+extern void EventTriggerRecordSubcmd(Node *subcmd, Oid relid,
+                        AttrNumber attnum, Oid newoid);
+extern void EventTriggerComplexCmdEnd(void);
 
 #endif   /* EVENT_TRIGGER_H */
index 865e018e27443d9e3714f6a00f63e1fcd13d100c..37b38f00a898fb73dd473056b1c2fc1dc899e366 100644 (file)
@@ -26,6 +26,7 @@
 typedef enum StashedCommandType
 {
    SCT_Basic,
+   SCT_AlterTable
 } StashedCommandType;
 
 /*
@@ -42,6 +43,7 @@ typedef struct StashedCommand
 {
    StashedCommandType type;
    bool        in_extension;
+   List       *subcmds;    /* list of StashedATSubcmd */
    Node       *parsetree;
 
    union
@@ -51,6 +53,13 @@ typedef struct StashedCommand
            Oid         objectId;
            ObjectType  objtype;
        } basic;
+
+       struct AlterTableCommand
+       {
+           Oid     objectId;
+           ObjectType objtype;
+           List   *subcmds;
+       } alterTable;
    } d;
 } StashedCommand;