]> granicus.if.org Git - postgresql/blobdiff - src/backend/tcop/utility.c
Change representation of statement lists, and add statement location info.
[postgresql] / src / backend / tcop / utility.c
index 59f09dc93afb92852ccddd27fe5c8524ddef7c8f..149210133606443b3f3ba142f4df18836530bc4c 100644 (file)
@@ -5,7 +5,7 @@
  *       commands.  At one time acted as an interface between the Lisp and C
  *       systems.
  *
- * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
@@ -71,7 +71,8 @@
 ProcessUtility_hook_type ProcessUtility_hook = NULL;
 
 /* local function declarations */
-static void ProcessUtilitySlow(Node *parsetree,
+static void ProcessUtilitySlow(ParseState *pstate,
+                                  PlannedStmt *pstmt,
                                   const char *queryString,
                                   ProcessUtilityContext context,
                                   ParamListInfo params,
@@ -87,35 +88,33 @@ static void ExecDropStmt(DropStmt *stmt, bool isTopLevel);
  * the query must be *in truth* read-only, because the caller wishes
  * not to do CommandCounterIncrement for it.
  *
- * Note: currently no need to support Query nodes here
+ * Note: currently no need to support raw or analyzed queries here
  */
 bool
-CommandIsReadOnly(Node *parsetree)
+CommandIsReadOnly(PlannedStmt *pstmt)
 {
-       if (IsA(parsetree, PlannedStmt))
+       Assert(IsA(pstmt, PlannedStmt));
+       switch (pstmt->commandType)
        {
-               PlannedStmt *stmt = (PlannedStmt *) parsetree;
-
-               switch (stmt->commandType)
-               {
-                       case CMD_SELECT:
-                               if (stmt->rowMarks != NIL)
-                                       return false;           /* SELECT FOR [KEY] UPDATE/SHARE */
-                               else if (stmt->hasModifyingCTE)
-                                       return false;           /* data-modifying CTE */
-                               else
-                                       return true;
-                       case CMD_UPDATE:
-                       case CMD_INSERT:
-                       case CMD_DELETE:
-                               return false;
-                       default:
-                               elog(WARNING, "unrecognized commandType: %d",
-                                        (int) stmt->commandType);
-                               break;
-               }
+               case CMD_SELECT:
+                       if (pstmt->rowMarks != NIL)
+                               return false;   /* SELECT FOR [KEY] UPDATE/SHARE */
+                       else if (pstmt->hasModifyingCTE)
+                               return false;   /* data-modifying CTE */
+                       else
+                               return true;
+               case CMD_UPDATE:
+               case CMD_INSERT:
+               case CMD_DELETE:
+                       return false;
+               case CMD_UTILITY:
+                       /* For now, treat all utility commands as read/write */
+                       return false;
+               default:
+                       elog(WARNING, "unrecognized commandType: %d",
+                                (int) pstmt->commandType);
+                       break;
        }
-       /* For now, treat all utility commands as read/write */
        return false;
 }
 
@@ -135,8 +134,8 @@ check_xact_readonly(Node *parsetree)
        /*
         * Note: Commands that need to do more complicated checking are handled
         * elsewhere, in particular COPY and plannable statements do their own
-        * checking.  However they should all call PreventCommandIfReadOnly
-        * or PreventCommandIfParallelMode to actually throw the error.
+        * checking.  However they should all call PreventCommandIfReadOnly or
+        * PreventCommandIfParallelMode to actually throw the error.
         */
 
        switch (nodeTag(parsetree))
@@ -147,8 +146,10 @@ check_xact_readonly(Node *parsetree)
                case T_AlterFunctionStmt:
                case T_AlterRoleStmt:
                case T_AlterRoleSetStmt:
+               case T_AlterObjectDependsStmt:
                case T_AlterObjectSchemaStmt:
                case T_AlterOwnerStmt:
+               case T_AlterOperatorStmt:
                case T_AlterSeqStmt:
                case T_AlterTableMoveAllStmt:
                case T_AlterTableStmt:
@@ -294,7 +295,7 @@ CheckRestrictedOperation(const char *cmdname)
  * ProcessUtility
  *             general utility function invoker
  *
- *     parsetree: the parse tree for the utility statement
+ *     pstmt: PlannedStmt wrapper for the utility statement
  *     queryString: original source text of command
  *     context: identifies source of statement (toplevel client command,
  *             non-toplevel client command, subcommand of a larger utility command)
@@ -312,13 +313,15 @@ CheckRestrictedOperation(const char *cmdname)
  * completionTag may be NULL if caller doesn't want a status string.
  */
 void
-ProcessUtility(Node *parsetree,
+ProcessUtility(PlannedStmt *pstmt,
                           const char *queryString,
                           ProcessUtilityContext context,
                           ParamListInfo params,
                           DestReceiver *dest,
                           char *completionTag)
 {
+       Assert(IsA(pstmt, PlannedStmt));
+       Assert(pstmt->commandType == CMD_UTILITY);
        Assert(queryString != NULL);    /* required as of 8.4 */
 
        /*
@@ -327,11 +330,11 @@ ProcessUtility(Node *parsetree,
         * call standard_ProcessUtility().
         */
        if (ProcessUtility_hook)
-               (*ProcessUtility_hook) (parsetree, queryString,
+               (*ProcessUtility_hook) (pstmt, queryString,
                                                                context, params,
                                                                dest, completionTag);
        else
-               standard_ProcessUtility(parsetree, queryString,
+               standard_ProcessUtility(pstmt, queryString,
                                                                context, params,
                                                                dest, completionTag);
 }
@@ -348,20 +351,25 @@ ProcessUtility(Node *parsetree,
  * which requires being in a valid transaction.
  */
 void
-standard_ProcessUtility(Node *parsetree,
+standard_ProcessUtility(PlannedStmt *pstmt,
                                                const char *queryString,
                                                ProcessUtilityContext context,
                                                ParamListInfo params,
                                                DestReceiver *dest,
                                                char *completionTag)
 {
+       Node       *parsetree = pstmt->utilityStmt;
        bool            isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
+       ParseState *pstate;
 
        check_xact_readonly(parsetree);
 
        if (completionTag)
                completionTag[0] = '\0';
 
+       pstate = make_parsestate(NULL);
+       pstate->p_sourcetext = queryString;
+
        switch (nodeTag(parsetree))
        {
                        /*
@@ -479,20 +487,10 @@ standard_ProcessUtility(Node *parsetree,
 
                        /*
                         * Portal (cursor) manipulation
-                        *
-                        * Note: DECLARE CURSOR is processed mostly as a SELECT, and
-                        * therefore what we will get here is a PlannedStmt not a bare
-                        * DeclareCursorStmt.
                         */
-               case T_PlannedStmt:
-                       {
-                               PlannedStmt *stmt = (PlannedStmt *) parsetree;
-
-                               if (stmt->utilityStmt == NULL ||
-                                       !IsA(stmt->utilityStmt, DeclareCursorStmt))
-                                       elog(ERROR, "non-DECLARE CURSOR PlannedStmt passed to ProcessUtility");
-                               PerformCursorOpen(stmt, params, queryString, isTopLevel);
-                       }
+               case T_DeclareCursorStmt:
+                       PerformCursorOpen((DeclareCursorStmt *) parsetree, params,
+                                                         queryString, isTopLevel);
                        break;
 
                case T_ClosePortalStmt:
@@ -538,7 +536,9 @@ standard_ProcessUtility(Node *parsetree,
                        {
                                uint64          processed;
 
-                               DoCopy((CopyStmt *) parsetree, queryString, &processed);
+                               DoCopy(pstate, (CopyStmt *) parsetree,
+                                          pstmt->stmt_location, pstmt->stmt_len,
+                                          &processed);
                                if (completionTag)
                                        snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
                                                         "COPY " UINT64_FORMAT, processed);
@@ -547,7 +547,8 @@ standard_ProcessUtility(Node *parsetree,
 
                case T_PrepareStmt:
                        CheckRestrictedOperation("PREPARE");
-                       PrepareQuery((PrepareStmt *) parsetree, queryString);
+                       PrepareQuery((PrepareStmt *) parsetree, queryString,
+                                                pstmt->stmt_location, pstmt->stmt_len);
                        break;
 
                case T_ExecuteStmt:
@@ -569,12 +570,12 @@ standard_ProcessUtility(Node *parsetree,
                case T_CreatedbStmt:
                        /* no event triggers for global objects */
                        PreventTransactionChain(isTopLevel, "CREATE DATABASE");
-                       createdb((CreatedbStmt *) parsetree);
+                       createdb(pstate, (CreatedbStmt *) parsetree);
                        break;
 
                case T_AlterDatabaseStmt:
                        /* no event triggers for global objects */
-                       AlterDatabase((AlterDatabaseStmt *) parsetree, isTopLevel);
+                       AlterDatabase(pstate, (AlterDatabaseStmt *) parsetree, isTopLevel);
                        break;
 
                case T_AlterDatabaseSetStmt:
@@ -655,7 +656,7 @@ standard_ProcessUtility(Node *parsetree,
                        break;
 
                case T_ExplainStmt:
-                       ExplainQuery((ExplainStmt *) parsetree, queryString, params, dest);
+                       ExplainQuery(pstate, (ExplainStmt *) parsetree, queryString, params, dest);
                        break;
 
                case T_AlterSystemStmt:
@@ -696,7 +697,7 @@ standard_ProcessUtility(Node *parsetree,
                         */
                case T_CreateRoleStmt:
                        /* no event triggers for global objects */
-                       CreateRole((CreateRoleStmt *) parsetree);
+                       CreateRole(pstate, (CreateRoleStmt *) parsetree);
                        break;
 
                case T_AlterRoleStmt:
@@ -762,10 +763,10 @@ standard_ProcessUtility(Node *parsetree,
                                switch (stmt->kind)
                                {
                                        case REINDEX_OBJECT_INDEX:
-                                               ReindexIndex(stmt->relation);
+                                               ReindexIndex(stmt->relation, stmt->options);
                                                break;
                                        case REINDEX_OBJECT_TABLE:
-                                               ReindexTable(stmt->relation);
+                                               ReindexTable(stmt->relation, stmt->options);
                                                break;
                                        case REINDEX_OBJECT_SCHEMA:
                                        case REINDEX_OBJECT_SYSTEM:
@@ -781,7 +782,7 @@ standard_ProcessUtility(Node *parsetree,
                                                                                                (stmt->kind == REINDEX_OBJECT_SCHEMA) ? "REINDEX SCHEMA" :
                                                                                                (stmt->kind == REINDEX_OBJECT_SYSTEM) ? "REINDEX SYSTEM" :
                                                                                                "REINDEX DATABASE");
-                                               ReindexMultipleTables(stmt->name, stmt->kind);
+                                               ReindexMultipleTables(stmt->name, stmt->kind, stmt->options);
                                                break;
                                        default:
                                                elog(ERROR, "unrecognized object type: %d",
@@ -801,11 +802,11 @@ standard_ProcessUtility(Node *parsetree,
                                GrantStmt  *stmt = (GrantStmt *) parsetree;
 
                                if (EventTriggerSupportsGrantObjectType(stmt->objtype))
-                                       ProcessUtilitySlow(parsetree, queryString,
+                                       ProcessUtilitySlow(pstate, pstmt, queryString,
                                                                           context, params,
                                                                           dest, completionTag);
                                else
-                                       ExecuteGrantStmt((GrantStmt *) parsetree);
+                                       ExecuteGrantStmt(stmt);
                        }
                        break;
 
@@ -814,7 +815,7 @@ standard_ProcessUtility(Node *parsetree,
                                DropStmt   *stmt = (DropStmt *) parsetree;
 
                                if (EventTriggerSupportsObjectType(stmt->removeType))
-                                       ProcessUtilitySlow(parsetree, queryString,
+                                       ProcessUtilitySlow(pstate, pstmt, queryString,
                                                                           context, params,
                                                                           dest, completionTag);
                                else
@@ -827,7 +828,7 @@ standard_ProcessUtility(Node *parsetree,
                                RenameStmt *stmt = (RenameStmt *) parsetree;
 
                                if (EventTriggerSupportsObjectType(stmt->renameType))
-                                       ProcessUtilitySlow(parsetree, queryString,
+                                       ProcessUtilitySlow(pstate, pstmt, queryString,
                                                                           context, params,
                                                                           dest, completionTag);
                                else
@@ -835,12 +836,25 @@ standard_ProcessUtility(Node *parsetree,
                        }
                        break;
 
+               case T_AlterObjectDependsStmt:
+                       {
+                               AlterObjectDependsStmt *stmt = (AlterObjectDependsStmt *) parsetree;
+
+                               if (EventTriggerSupportsObjectType(stmt->objectType))
+                                       ProcessUtilitySlow(pstate, pstmt, queryString,
+                                                                          context, params,
+                                                                          dest, completionTag);
+                               else
+                                       ExecAlterObjectDependsStmt(stmt, NULL);
+                       }
+                       break;
+
                case T_AlterObjectSchemaStmt:
                        {
                                AlterObjectSchemaStmt *stmt = (AlterObjectSchemaStmt *) parsetree;
 
                                if (EventTriggerSupportsObjectType(stmt->objectType))
-                                       ProcessUtilitySlow(parsetree, queryString,
+                                       ProcessUtilitySlow(pstate, pstmt, queryString,
                                                                           context, params,
                                                                           dest, completionTag);
                                else
@@ -853,7 +867,7 @@ standard_ProcessUtility(Node *parsetree,
                                AlterOwnerStmt *stmt = (AlterOwnerStmt *) parsetree;
 
                                if (EventTriggerSupportsObjectType(stmt->objectType))
-                                       ProcessUtilitySlow(parsetree, queryString,
+                                       ProcessUtilitySlow(pstate, pstmt, queryString,
                                                                           context, params,
                                                                           dest, completionTag);
                                else
@@ -866,11 +880,11 @@ standard_ProcessUtility(Node *parsetree,
                                CommentStmt *stmt = (CommentStmt *) parsetree;
 
                                if (EventTriggerSupportsObjectType(stmt->objtype))
-                                       ProcessUtilitySlow(parsetree, queryString,
+                                       ProcessUtilitySlow(pstate, pstmt, queryString,
                                                                           context, params,
                                                                           dest, completionTag);
                                else
-                                       CommentObject((CommentStmt *) parsetree);
+                                       CommentObject(stmt);
                                break;
                        }
 
@@ -879,7 +893,7 @@ standard_ProcessUtility(Node *parsetree,
                                SecLabelStmt *stmt = (SecLabelStmt *) parsetree;
 
                                if (EventTriggerSupportsObjectType(stmt->objtype))
-                                       ProcessUtilitySlow(parsetree, queryString,
+                                       ProcessUtilitySlow(pstate, pstmt, queryString,
                                                                           context, params,
                                                                           dest, completionTag);
                                else
@@ -889,11 +903,13 @@ standard_ProcessUtility(Node *parsetree,
 
                default:
                        /* All other statement types have event trigger support */
-                       ProcessUtilitySlow(parsetree, queryString,
+                       ProcessUtilitySlow(pstate, pstmt, queryString,
                                                           context, params,
                                                           dest, completionTag);
                        break;
        }
+
+       free_parsestate(pstate);
 }
 
 /*
@@ -902,17 +918,21 @@ standard_ProcessUtility(Node *parsetree,
  * perform the trigger support calls if the context allows it.
  */
 static void
-ProcessUtilitySlow(Node *parsetree,
+ProcessUtilitySlow(ParseState *pstate,
+                                  PlannedStmt *pstmt,
                                   const char *queryString,
                                   ProcessUtilityContext context,
                                   ParamListInfo params,
                                   DestReceiver *dest,
                                   char *completionTag)
 {
+       Node       *parsetree = pstmt->utilityStmt;
        bool            isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
        bool            isCompleteQuery = (context <= PROCESS_UTILITY_QUERY);
        bool            needCleanup;
+       bool            commandCollected = false;
        ObjectAddress address;
+       ObjectAddress secondaryObject = InvalidObjectAddress;
 
        /* All event trigger calls are done only when isCompleteQuery is true */
        needCleanup = isCompleteQuery && EventTriggerBeginCompleteQuery();
@@ -930,7 +950,15 @@ ProcessUtilitySlow(Node *parsetree,
                                 */
                        case T_CreateSchemaStmt:
                                CreateSchemaCommand((CreateSchemaStmt *) parsetree,
-                                                                       queryString);
+                                                                       queryString,
+                                                                       pstmt->stmt_location,
+                                                                       pstmt->stmt_len);
+
+                               /*
+                                * EventTriggerCollectSimpleCommand called by
+                                * CreateSchemaCommand
+                                */
+                               commandCollected = true;
                                break;
 
                        case T_CreateStmt:
@@ -956,7 +984,11 @@ ProcessUtilitySlow(Node *parsetree,
                                                        /* Create the table itself */
                                                        address = DefineRelation((CreateStmt *) stmt,
                                                                                                         RELKIND_RELATION,
-                                                                                                        InvalidOid, NULL);
+                                                                                                        InvalidOid, NULL,
+                                                                                                        queryString);
+                                                       EventTriggerCollectSimpleCommand(address,
+                                                                                                                        secondaryObject,
+                                                                                                                        stmt);
 
                                                        /*
                                                         * Let NewRelationCreateToastTable decide if this
@@ -986,14 +1018,31 @@ ProcessUtilitySlow(Node *parsetree,
                                                        /* Create the table itself */
                                                        address = DefineRelation((CreateStmt *) stmt,
                                                                                                         RELKIND_FOREIGN_TABLE,
-                                                                                                        InvalidOid, NULL);
+                                                                                                        InvalidOid, NULL,
+                                                                                                        queryString);
                                                        CreateForeignTable((CreateForeignTableStmt *) stmt,
                                                                                           address.objectId);
+                                                       EventTriggerCollectSimpleCommand(address,
+                                                                                                                        secondaryObject,
+                                                                                                                        stmt);
                                                }
                                                else
                                                {
-                                                       /* Recurse for anything else */
-                                                       ProcessUtility(stmt,
+                                                       /*
+                                                        * Recurse for anything else.  Note the recursive
+                                                        * call will stash the objects so created into our
+                                                        * event trigger context.
+                                                        */
+                                                       PlannedStmt *wrapper;
+
+                                                       wrapper = makeNode(PlannedStmt);
+                                                       wrapper->commandType = CMD_UTILITY;
+                                                       wrapper->canSetTag = false;
+                                                       wrapper->utilityStmt = stmt;
+                                                       wrapper->stmt_location = pstmt->stmt_location;
+                                                       wrapper->stmt_len = pstmt->stmt_len;
+
+                                                       ProcessUtility(wrapper,
                                                                                   queryString,
                                                                                   PROCESS_UTILITY_SUBCOMMAND,
                                                                                   params,
@@ -1005,6 +1054,12 @@ ProcessUtilitySlow(Node *parsetree,
                                                if (lnext(l) != NULL)
                                                        CommandCounterIncrement();
                                        }
+
+                                       /*
+                                        * The multiple commands generated here are stashed
+                                        * individually, so disable collection below.
+                                        */
+                                       commandCollected = true;
                                }
                                break;
 
@@ -1031,6 +1086,10 @@ ProcessUtilitySlow(Node *parsetree,
                                                stmts = transformAlterTableStmt(relid, atstmt,
                                                                                                                queryString);
 
+                                               /* ... ensure we have an event trigger context ... */
+                                               EventTriggerAlterTableStart(parsetree);
+                                               EventTriggerAlterTableRelid(relid);
+
                                                /* ... and do it */
                                                foreach(l, stmts)
                                                {
@@ -1044,25 +1103,49 @@ ProcessUtilitySlow(Node *parsetree,
                                                        }
                                                        else
                                                        {
-                                                               /* Recurse for anything else */
-                                                               ProcessUtility(stmt,
+                                                               /*
+                                                                * 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.
+                                                                */
+                                                               PlannedStmt *wrapper;
+
+                                                               EventTriggerAlterTableEnd();
+                                                               wrapper = makeNode(PlannedStmt);
+                                                               wrapper->commandType = CMD_UTILITY;
+                                                               wrapper->canSetTag = false;
+                                                               wrapper->utilityStmt = stmt;
+                                                               wrapper->stmt_location = pstmt->stmt_location;
+                                                               wrapper->stmt_len = pstmt->stmt_len;
+                                                               ProcessUtility(wrapper,
                                                                                           queryString,
                                                                                           PROCESS_UTILITY_SUBCOMMAND,
                                                                                           params,
                                                                                           None_Receiver,
                                                                                           NULL);
+                                                               EventTriggerAlterTableStart(parsetree);
+                                                               EventTriggerAlterTableRelid(relid);
                                                        }
 
                                                        /* Need CCI between commands */
                                                        if (lnext(l) != NULL)
                                                                CommandCounterIncrement();
                                                }
+
+                                               /* done */
+                                               EventTriggerAlterTableEnd();
                                        }
                                        else
                                                ereport(NOTICE,
                                                  (errmsg("relation \"%s\" does not exist, skipping",
                                                                  atstmt->relation->relname)));
                                }
+
+                               /* ALTER TABLE stashes commands internally */
+                               commandCollected = true;
                                break;
 
                        case T_AlterDomainStmt:
@@ -1081,31 +1164,37 @@ ProcessUtilitySlow(Node *parsetree,
                                                         * Recursively alter column default for table and,
                                                         * if requested, for descendants
                                                         */
-                                                       AlterDomainDefault(stmt->typeName,
-                                                                                          stmt->def);
+                                                       address =
+                                                               AlterDomainDefault(stmt->typeName,
+                                                                                                  stmt->def);
                                                        break;
                                                case 'N':               /* ALTER DOMAIN DROP NOT NULL */
-                                                       AlterDomainNotNull(stmt->typeName,
-                                                                                          false);
+                                                       address =
+                                                               AlterDomainNotNull(stmt->typeName,
+                                                                                                  false);
                                                        break;
                                                case 'O':               /* ALTER DOMAIN SET NOT NULL */
-                                                       AlterDomainNotNull(stmt->typeName,
-                                                                                          true);
+                                                       address =
+                                                               AlterDomainNotNull(stmt->typeName,
+                                                                                                  true);
                                                        break;
                                                case 'C':               /* ADD CONSTRAINT */
-                                                       AlterDomainAddConstraint(stmt->typeName,
-                                                                                                        stmt->def,
-                                                                                                        NULL);
+                                                       address =
+                                                               AlterDomainAddConstraint(stmt->typeName,
+                                                                                                                stmt->def,
+                                                                                                                &secondaryObject);
                                                        break;
                                                case 'X':               /* DROP CONSTRAINT */
-                                                       AlterDomainDropConstraint(stmt->typeName,
-                                                                                                         stmt->name,
-                                                                                                         stmt->behavior,
-                                                                                                         stmt->missing_ok);
+                                                       address =
+                                                               AlterDomainDropConstraint(stmt->typeName,
+                                                                                                                 stmt->name,
+                                                                                                                 stmt->behavior,
+                                                                                                                 stmt->missing_ok);
                                                        break;
                                                case 'V':               /* VALIDATE CONSTRAINT */
-                                                       AlterDomainValidateConstraint(stmt->typeName,
-                                                                                                                 stmt->name);
+                                                       address =
+                                                               AlterDomainValidateConstraint(stmt->typeName,
+                                                                                                                         stmt->name);
                                                        break;
                                                default:                /* oops */
                                                        elog(ERROR, "unrecognized alter domain type: %d",
@@ -1125,41 +1214,48 @@ ProcessUtilitySlow(Node *parsetree,
                                        switch (stmt->kind)
                                        {
                                                case OBJECT_AGGREGATE:
-                                                       DefineAggregate(stmt->defnames, stmt->args,
-                                                                                       stmt->oldstyle, stmt->definition,
-                                                                                       queryString);
+                                                       address =
+                                                               DefineAggregate(pstate, stmt->defnames, stmt->args,
+                                                                                               stmt->oldstyle,
+                                                                                               stmt->definition);
                                                        break;
                                                case OBJECT_OPERATOR:
                                                        Assert(stmt->args == NIL);
-                                                       DefineOperator(stmt->defnames, stmt->definition);
+                                                       address = DefineOperator(stmt->defnames,
+                                                                                                        stmt->definition);
                                                        break;
                                                case OBJECT_TYPE:
                                                        Assert(stmt->args == NIL);
-                                                       DefineType(stmt->defnames, stmt->definition);
+                                                       address = DefineType(pstate,
+                                                                                                stmt->defnames,
+                                                                                                stmt->definition);
                                                        break;
                                                case OBJECT_TSPARSER:
                                                        Assert(stmt->args == NIL);
-                                                       DefineTSParser(stmt->defnames, stmt->definition);
+                                                       address = DefineTSParser(stmt->defnames,
+                                                                                                        stmt->definition);
                                                        break;
                                                case OBJECT_TSDICTIONARY:
                                                        Assert(stmt->args == NIL);
-                                                       DefineTSDictionary(stmt->defnames,
-                                                                                          stmt->definition);
+                                                       address = DefineTSDictionary(stmt->defnames,
+                                                                                                                stmt->definition);
                                                        break;
                                                case OBJECT_TSTEMPLATE:
                                                        Assert(stmt->args == NIL);
-                                                       DefineTSTemplate(stmt->defnames,
-                                                                                        stmt->definition);
+                                                       address = DefineTSTemplate(stmt->defnames,
+                                                                                                          stmt->definition);
                                                        break;
                                                case OBJECT_TSCONFIGURATION:
                                                        Assert(stmt->args == NIL);
-                                                       DefineTSConfiguration(stmt->defnames,
-                                                                                                 stmt->definition,
-                                                                                                 NULL);
+                                                       address = DefineTSConfiguration(stmt->defnames,
+                                                                                                                       stmt->definition,
+                                                                                                                       &secondaryObject);
                                                        break;
                                                case OBJECT_COLLATION:
                                                        Assert(stmt->args == NIL);
-                                                       DefineCollation(stmt->defnames, stmt->definition);
+                                                       address = DefineCollation(pstate,
+                                                                                                         stmt->defnames,
+                                                                                                         stmt->definition);
                                                        break;
                                                default:
                                                        elog(ERROR, "unrecognized define stmt type: %d",
@@ -1200,208 +1296,286 @@ ProcessUtilitySlow(Node *parsetree,
                                        stmt = transformIndexStmt(relid, stmt, queryString);
 
                                        /* ... and do it */
-                                       DefineIndex(relid,      /* OID of heap relation */
-                                                               stmt,
-                                                               InvalidOid,             /* no predefined OID */
-                                                               false,  /* is_alter_table */
-                                                               true,   /* check_rights */
-                                                               false,  /* skip_build */
-                                                               false); /* quiet */
+                                       EventTriggerAlterTableStart(parsetree);
+                                       address =
+                                               DefineIndex(relid,              /* OID of heap relation */
+                                                                       stmt,
+                                                                       InvalidOid, /* no predefined OID */
+                                                                       false,          /* is_alter_table */
+                                                                       true,           /* check_rights */
+                                                                       false,          /* skip_build */
+                                                                       false);         /* quiet */
+
+                                       /*
+                                        * Add the CREATE INDEX node itself to stash right away;
+                                        * if there were any commands stashed in the ALTER TABLE
+                                        * code, we need them to appear after this one.
+                                        */
+                                       EventTriggerCollectSimpleCommand(address, secondaryObject,
+                                                                                                        parsetree);
+                                       commandCollected = true;
+                                       EventTriggerAlterTableEnd();
                                }
                                break;
 
                        case T_CreateExtensionStmt:
-                               CreateExtension((CreateExtensionStmt *) parsetree);
+                               address = CreateExtension(pstate, (CreateExtensionStmt *) parsetree);
                                break;
 
                        case T_AlterExtensionStmt:
-                               ExecAlterExtensionStmt((AlterExtensionStmt *) parsetree);
+                               address = ExecAlterExtensionStmt(pstate, (AlterExtensionStmt *) parsetree);
                                break;
 
                        case T_AlterExtensionContentsStmt:
-                               ExecAlterExtensionContentsStmt((AlterExtensionContentsStmt *) parsetree,
-                                                                                          NULL);
+                               address = ExecAlterExtensionContentsStmt((AlterExtensionContentsStmt *) parsetree,
+                                                                                                                &secondaryObject);
                                break;
 
                        case T_CreateFdwStmt:
-                               CreateForeignDataWrapper((CreateFdwStmt *) parsetree);
+                               address = CreateForeignDataWrapper((CreateFdwStmt *) parsetree);
                                break;
 
                        case T_AlterFdwStmt:
-                               AlterForeignDataWrapper((AlterFdwStmt *) parsetree);
+                               address = AlterForeignDataWrapper((AlterFdwStmt *) parsetree);
                                break;
 
                        case T_CreateForeignServerStmt:
-                               CreateForeignServer((CreateForeignServerStmt *) parsetree);
+                               address = CreateForeignServer((CreateForeignServerStmt *) parsetree);
                                break;
 
                        case T_AlterForeignServerStmt:
-                               AlterForeignServer((AlterForeignServerStmt *) parsetree);
+                               address = AlterForeignServer((AlterForeignServerStmt *) parsetree);
                                break;
 
                        case T_CreateUserMappingStmt:
-                               CreateUserMapping((CreateUserMappingStmt *) parsetree);
+                               address = CreateUserMapping((CreateUserMappingStmt *) parsetree);
                                break;
 
                        case T_AlterUserMappingStmt:
-                               AlterUserMapping((AlterUserMappingStmt *) parsetree);
+                               address = AlterUserMapping((AlterUserMappingStmt *) parsetree);
                                break;
 
                        case T_DropUserMappingStmt:
                                RemoveUserMapping((DropUserMappingStmt *) parsetree);
+                               /* no commands stashed for DROP */
+                               commandCollected = true;
                                break;
 
                        case T_ImportForeignSchemaStmt:
                                ImportForeignSchema((ImportForeignSchemaStmt *) parsetree);
+                               /* commands are stashed inside ImportForeignSchema */
+                               commandCollected = true;
                                break;
 
                        case T_CompositeTypeStmt:       /* CREATE TYPE (composite) */
                                {
                                        CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree;
 
-                                       DefineCompositeType(stmt->typevar, stmt->coldeflist);
+                                       address = DefineCompositeType(stmt->typevar,
+                                                                                                 stmt->coldeflist);
                                }
                                break;
 
                        case T_CreateEnumStmt:          /* CREATE TYPE AS ENUM */
-                               DefineEnum((CreateEnumStmt *) parsetree);
+                               address = DefineEnum((CreateEnumStmt *) parsetree);
                                break;
 
                        case T_CreateRangeStmt:         /* CREATE TYPE AS RANGE */
-                               DefineRange((CreateRangeStmt *) parsetree);
+                               address = DefineRange((CreateRangeStmt *) parsetree);
                                break;
 
                        case T_AlterEnumStmt:           /* ALTER TYPE (enum) */
-                               AlterEnum((AlterEnumStmt *) parsetree, isTopLevel);
+                               address = AlterEnum((AlterEnumStmt *) parsetree);
                                break;
 
                        case T_ViewStmt:        /* CREATE VIEW */
-                               DefineView((ViewStmt *) parsetree, queryString);
+                               EventTriggerAlterTableStart(parsetree);
+                               address = DefineView((ViewStmt *) parsetree, queryString,
+                                                                        pstmt->stmt_location, pstmt->stmt_len);
+                               EventTriggerCollectSimpleCommand(address, secondaryObject,
+                                                                                                parsetree);
+                               /* stashed internally */
+                               commandCollected = true;
+                               EventTriggerAlterTableEnd();
                                break;
 
                        case T_CreateFunctionStmt:      /* CREATE FUNCTION */
-                               CreateFunction((CreateFunctionStmt *) parsetree, queryString);
+                               address = CreateFunction(pstate, (CreateFunctionStmt *) parsetree);
                                break;
 
                        case T_AlterFunctionStmt:       /* ALTER FUNCTION */
-                               AlterFunction((AlterFunctionStmt *) parsetree);
+                               address = AlterFunction(pstate, (AlterFunctionStmt *) parsetree);
                                break;
 
                        case T_RuleStmt:        /* CREATE RULE */
-                               DefineRule((RuleStmt *) parsetree, queryString);
+                               address = DefineRule((RuleStmt *) parsetree, queryString);
                                break;
 
                        case T_CreateSeqStmt:
-                               DefineSequence((CreateSeqStmt *) parsetree);
+                               address = DefineSequence(pstate, (CreateSeqStmt *) parsetree);
                                break;
 
                        case T_AlterSeqStmt:
-                               AlterSequence((AlterSeqStmt *) parsetree);
+                               address = AlterSequence(pstate, (AlterSeqStmt *) parsetree);
                                break;
 
                        case T_CreateTableAsStmt:
-                               ExecCreateTableAs((CreateTableAsStmt *) parsetree,
-                                                                 queryString, params, completionTag);
+                               address = ExecCreateTableAs((CreateTableAsStmt *) parsetree,
+                                                                                queryString, params, completionTag);
                                break;
 
                        case T_RefreshMatViewStmt:
-                               ExecRefreshMatView((RefreshMatViewStmt *) parsetree,
-                                                                  queryString, params, completionTag);
+
+                               /*
+                                * REFRSH CONCURRENTLY executes some DDL commands internally.
+                                * Inhibit DDL command collection here to avoid those commands
+                                * from showing up in the deparsed command queue.  The refresh
+                                * command itself is queued, which is enough.
+                                */
+                               EventTriggerInhibitCommandCollection();
+                               PG_TRY();
+                               {
+                                       address = ExecRefreshMatView((RefreshMatViewStmt *) parsetree,
+                                                                                queryString, params, completionTag);
+                               }
+                               PG_CATCH();
+                               {
+                                       EventTriggerUndoInhibitCommandCollection();
+                                       PG_RE_THROW();
+                               }
+                               PG_END_TRY();
+                               EventTriggerUndoInhibitCommandCollection();
                                break;
 
                        case T_CreateTrigStmt:
-                               (void) CreateTrigger((CreateTrigStmt *) parsetree, queryString,
-                                                                        InvalidOid, InvalidOid, InvalidOid,
-                                                                        InvalidOid, false);
+                               address = CreateTrigger((CreateTrigStmt *) parsetree,
+                                                                               queryString, InvalidOid, InvalidOid,
+                                                                               InvalidOid, InvalidOid, false);
                                break;
 
                        case T_CreatePLangStmt:
-                               CreateProceduralLanguage((CreatePLangStmt *) parsetree);
+                               address = CreateProceduralLanguage((CreatePLangStmt *) parsetree);
                                break;
 
                        case T_CreateDomainStmt:
-                               DefineDomain((CreateDomainStmt *) parsetree);
+                               address = DefineDomain((CreateDomainStmt *) parsetree);
                                break;
 
                        case T_CreateConversionStmt:
-                               CreateConversionCommand((CreateConversionStmt *) parsetree);
+                               address = CreateConversionCommand((CreateConversionStmt *) parsetree);
                                break;
 
                        case T_CreateCastStmt:
-                               CreateCast((CreateCastStmt *) parsetree);
+                               address = CreateCast((CreateCastStmt *) parsetree);
                                break;
 
                        case T_CreateOpClassStmt:
                                DefineOpClass((CreateOpClassStmt *) parsetree);
+                               /* command is stashed in DefineOpClass */
+                               commandCollected = true;
                                break;
 
                        case T_CreateOpFamilyStmt:
-                               DefineOpFamily((CreateOpFamilyStmt *) parsetree);
+                               address = DefineOpFamily((CreateOpFamilyStmt *) parsetree);
                                break;
 
                        case T_CreateTransformStmt:
-                               CreateTransform((CreateTransformStmt *) parsetree);
+                               address = CreateTransform((CreateTransformStmt *) parsetree);
                                break;
 
                        case T_AlterOpFamilyStmt:
                                AlterOpFamily((AlterOpFamilyStmt *) parsetree);
+                               /* commands are stashed in AlterOpFamily */
+                               commandCollected = true;
                                break;
 
                        case T_AlterTSDictionaryStmt:
-                               AlterTSDictionary((AlterTSDictionaryStmt *) parsetree);
+                               address = AlterTSDictionary((AlterTSDictionaryStmt *) parsetree);
                                break;
 
                        case T_AlterTSConfigurationStmt:
                                AlterTSConfiguration((AlterTSConfigurationStmt *) parsetree);
+
+                               /*
+                                * Commands are stashed in MakeConfigurationMapping and
+                                * DropConfigurationMapping, which are called from
+                                * AlterTSConfiguration
+                                */
+                               commandCollected = true;
                                break;
 
                        case T_AlterTableMoveAllStmt:
                                AlterTableMoveAll((AlterTableMoveAllStmt *) parsetree);
+                               /* commands are stashed in AlterTableMoveAll */
+                               commandCollected = true;
                                break;
 
                        case T_DropStmt:
                                ExecDropStmt((DropStmt *) parsetree, isTopLevel);
+                               /* no commands stashed for DROP */
+                               commandCollected = true;
                                break;
 
                        case T_RenameStmt:
-                               ExecRenameStmt((RenameStmt *) parsetree);
+                               address = ExecRenameStmt((RenameStmt *) parsetree);
+                               break;
+
+                       case T_AlterObjectDependsStmt:
+                               address =
+                                       ExecAlterObjectDependsStmt((AlterObjectDependsStmt *) parsetree,
+                                                                                          &secondaryObject);
                                break;
 
                        case T_AlterObjectSchemaStmt:
-                               ExecAlterObjectSchemaStmt((AlterObjectSchemaStmt *) parsetree,
-                                                                                 NULL);
+                               address =
+                                       ExecAlterObjectSchemaStmt((AlterObjectSchemaStmt *) parsetree,
+                                                                                         &secondaryObject);
                                break;
 
                        case T_AlterOwnerStmt:
-                               ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree);
+                               address = ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree);
+                               break;
+
+                       case T_AlterOperatorStmt:
+                               address = AlterOperator((AlterOperatorStmt *) parsetree);
                                break;
 
                        case T_CommentStmt:
-                               CommentObject((CommentStmt *) parsetree);
+                               address = CommentObject((CommentStmt *) parsetree);
                                break;
 
                        case T_GrantStmt:
                                ExecuteGrantStmt((GrantStmt *) parsetree);
+                               /* commands are stashed in ExecGrantStmt_oids */
+                               commandCollected = true;
                                break;
 
                        case T_DropOwnedStmt:
                                DropOwnedObjects((DropOwnedStmt *) parsetree);
+                               /* no commands stashed for DROP */
+                               commandCollected = true;
                                break;
 
                        case T_AlterDefaultPrivilegesStmt:
-                               ExecAlterDefaultPrivilegesStmt((AlterDefaultPrivilegesStmt *) parsetree);
+                               ExecAlterDefaultPrivilegesStmt(pstate, (AlterDefaultPrivilegesStmt *) parsetree);
+                               EventTriggerCollectAlterDefPrivs((AlterDefaultPrivilegesStmt *) parsetree);
+                               commandCollected = true;
                                break;
 
                        case T_CreatePolicyStmt:        /* CREATE POLICY */
-                               CreatePolicy((CreatePolicyStmt *) parsetree);
+                               address = CreatePolicy((CreatePolicyStmt *) parsetree);
                                break;
 
                        case T_AlterPolicyStmt:         /* ALTER POLICY */
-                               AlterPolicy((AlterPolicyStmt *) parsetree);
+                               address = AlterPolicy((AlterPolicyStmt *) parsetree);
                                break;
 
                        case T_SecLabelStmt:
-                               ExecSecLabelStmt((SecLabelStmt *) parsetree);
+                               address = ExecSecLabelStmt((SecLabelStmt *) parsetree);
+                               break;
+
+                       case T_CreateAmStmt:
+                               address = CreateAccessMethod((CreateAmStmt *) parsetree);
                                break;
 
                        default:
@@ -1410,6 +1584,14 @@ ProcessUtilitySlow(Node *parsetree,
                                break;
                }
 
+               /*
+                * Remember the object so that ddl_command_end event triggers have
+                * access to it.
+                */
+               if (!commandCollected)
+                       EventTriggerCollectSimpleCommand(address, secondaryObject,
+                                                                                        parsetree);
+
                if (isCompleteQuery)
                {
                        EventTriggerSQLDrop(parsetree);
@@ -1570,10 +1752,8 @@ QueryReturnsTuples(Query *parsetree)
        switch (parsetree->commandType)
        {
                case CMD_SELECT:
-                       /* returns tuples ... unless it's DECLARE CURSOR */
-                       if (parsetree->utilityStmt == NULL)
-                               return true;
-                       break;
+                       /* returns tuples */
+                       return true;
                case CMD_INSERT:
                case CMD_UPDATE:
                case CMD_DELETE:
@@ -1614,6 +1794,13 @@ UtilityContainsQuery(Node *parsetree)
 
        switch (nodeTag(parsetree))
        {
+               case T_DeclareCursorStmt:
+                       qry = (Query *) ((DeclareCursorStmt *) parsetree)->query;
+                       Assert(IsA(qry, Query));
+                       if (qry->commandType == CMD_UTILITY)
+                               return UtilityContainsQuery(qry->utilityStmt);
+                       return qry;
+
                case T_ExplainStmt:
                        qry = (Query *) ((ExplainStmt *) parsetree)->query;
                        Assert(IsA(qry, Query));
@@ -1765,7 +1952,8 @@ AlterObjectTypeCommandTag(ObjectType objtype)
 /*
  * CreateCommandTag
  *             utility to get a string representation of the command operation,
- *             given either a raw (un-analyzed) parsetree or a planned query.
+ *             given either a raw (un-analyzed) parsetree, an analyzed Query,
+ *             or a PlannedStmt.
  *
  * This must handle all command types, but since the vast majority
  * of 'em are utility commands, it seems sensible to keep it here.
@@ -1780,6 +1968,11 @@ CreateCommandTag(Node *parsetree)
 
        switch (nodeTag(parsetree))
        {
+                       /* recurse if we're given a RawStmt */
+               case T_RawStmt:
+                       tag = CreateCommandTag(((RawStmt *) parsetree)->stmt);
+                       break;
+
                        /* raw plannable queries */
                case T_InsertStmt:
                        tag = "INSERT";
@@ -2036,6 +2229,9 @@ CreateCommandTag(Node *parsetree)
                                case OBJECT_TRANSFORM:
                                        tag = "DROP TRANSFORM";
                                        break;
+                               case OBJECT_ACCESS_METHOD:
+                                       tag = "DROP ACCESS METHOD";
+                                       break;
                                default:
                                        tag = "???";
                        }
@@ -2061,6 +2257,10 @@ CreateCommandTag(Node *parsetree)
                        tag = AlterObjectTypeCommandTag(((RenameStmt *) parsetree)->renameType);
                        break;
 
+               case T_AlterObjectDependsStmt:
+                       tag = AlterObjectTypeCommandTag(((AlterObjectDependsStmt *) parsetree)->objectType);
+                       break;
+
                case T_AlterObjectSchemaStmt:
                        tag = AlterObjectTypeCommandTag(((AlterObjectSchemaStmt *) parsetree)->objectType);
                        break;
@@ -2132,6 +2332,9 @@ CreateCommandTag(Node *parsetree)
                                case OBJECT_COLLATION:
                                        tag = "CREATE COLLATION";
                                        break;
+                               case OBJECT_ACCESS_METHOD:
+                                       tag = "CREATE ACCESS METHOD";
+                                       break;
                                default:
                                        tag = "???";
                        }
@@ -2375,6 +2578,10 @@ CreateCommandTag(Node *parsetree)
                        tag = "ALTER OPERATOR FAMILY";
                        break;
 
+               case T_AlterOperatorStmt:
+                       tag = "ALTER OPERATOR";
+                       break;
+
                case T_AlterTSDictionaryStmt:
                        tag = "ALTER TEXT SEARCH DICTIONARY";
                        break;
@@ -2391,6 +2598,10 @@ CreateCommandTag(Node *parsetree)
                        tag = "ALTER POLICY";
                        break;
 
+               case T_CreateAmStmt:
+                       tag = "CREATE ACCESS METHOD";
+                       break;
+
                case T_PrepareStmt:
                        tag = "PREPARE";
                        break;
@@ -2424,12 +2635,7 @@ CreateCommandTag(Node *parsetree)
                                                 * will be useful for complaints about read-only
                                                 * statements
                                                 */
-                                               if (stmt->utilityStmt != NULL)
-                                               {
-                                                       Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
-                                                       tag = "DECLARE CURSOR";
-                                               }
-                                               else if (stmt->rowMarks != NIL)
+                                               if (stmt->rowMarks != NIL)
                                                {
                                                        /* not 100% but probably close enough */
                                                        switch (((PlanRowMark *) linitial(stmt->rowMarks))->strength)
@@ -2463,6 +2669,9 @@ CreateCommandTag(Node *parsetree)
                                        case CMD_DELETE:
                                                tag = "DELETE";
                                                break;
+                                       case CMD_UTILITY:
+                                               tag = CreateCommandTag(stmt->utilityStmt);
+                                               break;
                                        default:
                                                elog(WARNING, "unrecognized commandType: %d",
                                                         (int) stmt->commandType);
@@ -2486,12 +2695,7 @@ CreateCommandTag(Node *parsetree)
                                                 * will be useful for complaints about read-only
                                                 * statements
                                                 */
-                                               if (stmt->utilityStmt != NULL)
-                                               {
-                                                       Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
-                                                       tag = "DECLARE CURSOR";
-                                               }
-                                               else if (stmt->rowMarks != NIL)
+                                               if (stmt->rowMarks != NIL)
                                                {
                                                        /* not 100% but probably close enough */
                                                        switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength)
@@ -2551,7 +2755,8 @@ CreateCommandTag(Node *parsetree)
 /*
  * GetCommandLogLevel
  *             utility to get the minimum log_statement level for a command,
- *             given either a raw (un-analyzed) parsetree or a planned query.
+ *             given either a raw (un-analyzed) parsetree, an analyzed Query,
+ *             or a PlannedStmt.
  *
  * This must handle all command types, but since the vast majority
  * of 'em are utility commands, it seems sensible to keep it here.
@@ -2563,6 +2768,11 @@ GetCommandLogLevel(Node *parsetree)
 
        switch (nodeTag(parsetree))
        {
+                       /* recurse if we're given a RawStmt */
+               case T_RawStmt:
+                       lev = GetCommandLogLevel(((RawStmt *) parsetree)->stmt);
+                       break;
+
                        /* raw plannable queries */
                case T_InsertStmt:
                case T_DeleteStmt:
@@ -2666,7 +2876,7 @@ GetCommandLogLevel(Node *parsetree)
                                /* Look through an EXECUTE to the referenced stmt */
                                ps = FetchPreparedStatement(stmt->name, false);
                                if (ps && ps->plansource->raw_parse_tree)
-                                       lev = GetCommandLogLevel(ps->plansource->raw_parse_tree);
+                                       lev = GetCommandLogLevel(ps->plansource->raw_parse_tree->stmt);
                                else
                                        lev = LOGSTMT_ALL;
                        }
@@ -2680,6 +2890,10 @@ GetCommandLogLevel(Node *parsetree)
                        lev = LOGSTMT_DDL;
                        break;
 
+               case T_AlterObjectDependsStmt:
+                       lev = LOGSTMT_DDL;
+                       break;
+
                case T_AlterObjectSchemaStmt:
                        lev = LOGSTMT_DDL;
                        break;
@@ -2948,6 +3162,10 @@ GetCommandLogLevel(Node *parsetree)
                        lev = LOGSTMT_DDL;
                        break;
 
+               case T_CreateAmStmt:
+                       lev = LOGSTMT_DDL;
+                       break;
+
                        /* already-planned queries */
                case T_PlannedStmt:
                        {
@@ -2965,6 +3183,10 @@ GetCommandLogLevel(Node *parsetree)
                                                lev = LOGSTMT_MOD;
                                                break;
 
+                                       case CMD_UTILITY:
+                                               lev = GetCommandLogLevel(stmt->utilityStmt);
+                                               break;
+
                                        default:
                                                elog(WARNING, "unrecognized commandType: %d",
                                                         (int) stmt->commandType);