text: /*-------------------------------------------------------------------------
   *
   * utility.c
   *	  Contains functions which control the execution of the POSTGRES utility
   *	  commands.  At one time acted as an interface between the Lisp and C
   *	  systems.
   *
   * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
   *
   *
   * IDENTIFICATION
   *	  src/backend/tcop/utility.c
   *
   *-------------------------------------------------------------------------
   */
  #include "postgres.h"
  
  #include "access/htup_details.h"
  #include "access/reloptions.h"
  #include "access/twophase.h"
  #include "access/xact.h"
  #include "access/xlog.h"
  #include "catalog/catalog.h"
  #include "catalog/namespace.h"
  #include "catalog/pg_inherits.h"
  #include "catalog/toasting.h"
  #include "commands/alter.h"
  #include "commands/async.h"
  #include "commands/cluster.h"
  #include "commands/comment.h"
  #include "commands/collationcmds.h"
  #include "commands/conversioncmds.h"
  #include "commands/copy.h"
  #include "commands/createas.h"
  #include "commands/dbcommands.h"
  #include "commands/defrem.h"
  #include "commands/discard.h"
  #include "commands/event_trigger.h"
  #include "commands/explain.h"
  #include "commands/extension.h"
  #include "commands/matview.h"
  #include "commands/lockcmds.h"
  #include "commands/policy.h"
  #include "commands/portalcmds.h"
  #include "commands/prepare.h"
  #include "commands/proclang.h"
  #include "commands/publicationcmds.h"
  #include "commands/schemacmds.h"
  #include "commands/seclabel.h"
  #include "commands/sequence.h"
  #include "commands/subscriptioncmds.h"
  #include "commands/tablecmds.h"
  #include "commands/tablespace.h"
  #include "commands/tablegroup.h"
  #include "commands/trigger.h"
  #include "commands/typecmds.h"
  #include "commands/user.h"
  #include "commands/vacuum.h"
  #include "commands/view.h"
  #include "miscadmin.h"
  #include "parser/parse_utilcmd.h"
  #include "postmaster/bgwriter.h"
  #include "rewrite/rewriteDefine.h"
  #include "rewrite/rewriteRemove.h"
  #include "storage/fd.h"
  #include "tcop/pquery.h"
  #include "tcop/utility.h"
  #include "utils/acl.h"
  #include "utils/guc.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
  #include "utils/rel.h"
  
  #include "pg_yb_utils.h"
  
  static void YBProcessUtilityDefaultHook(PlannedStmt *pstmt,
                                          const char *queryString,
                                          ProcessUtilityContext context,
                                          ParamListInfo params,
                                          QueryEnvironment *queryEnv,
                                          DestReceiver *dest,
                                          char *completionTag);
  
  /* Hook for plugins to get control in ProcessUtility() */
  
  /*
   * Setting YBProcessUtilityDefaultHook directly guaranties it will be the first one.
   * It will be called after all plugins hooks.
   */
  ProcessUtility_hook_type ProcessUtility_hook = &YBProcessUtilityDefaultHook;
  
  /* local function declarations */
  static void ProcessUtilitySlow(ParseState *pstate,
  				   PlannedStmt *pstmt,
  				   const char *queryString,
  				   ProcessUtilityContext context,
  				   ParamListInfo params,
  				   QueryEnvironment *queryEnv,
  				   DestReceiver *dest,
  				   char *completionTag);
  static void ExecDropStmt(DropStmt *stmt, bool isTopLevel);
  
  
  /*
   * CommandIsReadOnly: is an executable query read-only?
   *
   * This is a much stricter test than we apply for XactReadOnly mode;
   * the query must be *in truth* read-only, because the caller wishes
   * not to do CommandCounterIncrement for it.
   *
   * Note: currently no need to support raw or analyzed queries here
   */
  bool
  CommandIsReadOnly(PlannedStmt *pstmt)
  {
  	Assert(IsA(pstmt, PlannedStmt));
  	switch (pstmt->commandType)
  	{
  		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;
  	}
  	return false;
  }
  
  /*
   * check_xact_readonly: is a utility command read-only?
   *
   * Here we use the loose rules of XactReadOnly mode: no permanent effects
   * on the database are allowed.
   */
  static void
  check_xact_readonly(Node *parsetree)
  {
  	/* Only perform the check if we have a reason to do so. */
  	if (!XactReadOnly && !IsInParallelMode())
  		return;
  
  	/*
  	 * 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.
  	 */
  
  	switch (nodeTag(parsetree))
  	{
  		case T_AlterDatabaseStmt:
  		case T_AlterDatabaseSetStmt:
  		case T_AlterDomainStmt:
  		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:
  		case T_RenameStmt:
  		case T_CommentStmt:
  		case T_DefineStmt:
  		case T_CreateCastStmt:
  		case T_CreateEventTrigStmt:
  		case T_AlterEventTrigStmt:
  		case T_CreateConversionStmt:
  		case T_CreatedbStmt:
  		case T_CreateDomainStmt:
  		case T_CreateFunctionStmt:
  		case T_CreateRoleStmt:
  		case T_IndexStmt:
  		case T_CreatePLangStmt:
  		case T_CreateOpClassStmt:
  		case T_CreateOpFamilyStmt:
  		case T_AlterOpFamilyStmt:
  		case T_RuleStmt:
  		case T_CreateSchemaStmt:
  		case T_CreateSeqStmt:
  		case T_CreateStmt:
  		case T_CreateTableAsStmt:
  		case T_RefreshMatViewStmt:
  		case T_CreateTableGroupStmt:
  		case T_CreateTableSpaceStmt:
  		case T_CreateTransformStmt:
  		case T_CreateTrigStmt:
  		case T_CompositeTypeStmt:
  		case T_CreateEnumStmt:
  		case T_CreateRangeStmt:
  		case T_AlterEnumStmt:
  		case T_ViewStmt:
  		case T_DropStmt:
  		case T_DropdbStmt:
  		case T_DropTableGroupStmt:
  		case T_DropTableSpaceStmt:
  		case T_DropRoleStmt:
  		case T_GrantStmt:
  		case T_GrantRoleStmt:
  		case T_AlterDefaultPrivilegesStmt:
  		case T_TruncateStmt:
  		case T_DropOwnedStmt:
  		case T_ReassignOwnedStmt:
  		case T_AlterTSDictionaryStmt:
  		case T_AlterTSConfigurationStmt:
  		case T_CreateExtensionStmt:
  		case T_AlterExtensionStmt:
  		case T_AlterExtensionContentsStmt:
  		case T_CreateFdwStmt:
  		case T_AlterFdwStmt:
  		case T_CreateForeignServerStmt:
  		case T_AlterForeignServerStmt:
  		case T_CreateUserMappingStmt:
  		case T_AlterUserMappingStmt:
  		case T_DropUserMappingStmt:
  		case T_AlterTableSpaceOptionsStmt:
  		case T_CreateForeignTableStmt:
  		case T_ImportForeignSchemaStmt:
  		case T_SecLabelStmt:
  		case T_CreatePublicationStmt:
  		case T_AlterPublicationStmt:
  		case T_CreateSubscriptionStmt:
  		case T_AlterSubscriptionStmt:
  		case T_DropSubscriptionStmt:
  			PreventCommandIfReadOnly(CreateCommandTag(parsetree));
  			PreventCommandIfParallelMode(CreateCommandTag(parsetree));
  			break;
  		default:
  			/* do nothing */
  			break;
  	}
  }
  
  /*
   * PreventCommandIfReadOnly: throw error if XactReadOnly
   *
   * This is useful mainly to ensure consistency of the error message wording;
   * most callers have checked XactReadOnly for themselves.
   */
  void
  PreventCommandIfReadOnly(const char *cmdname)
  {
  	if (XactReadOnly)
  		ereport(ERROR,
  				(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
  		/* translator: %s is name of a SQL command, eg CREATE */
  				 errmsg("cannot execute %s in a read-only transaction",
  						cmdname)));
  }
  
  /*
   * PreventCommandIfParallelMode: throw error if current (sub)transaction is
   * in parallel mode.
   *
   * This is useful mainly to ensure consistency of the error message wording;
   * most callers have checked IsInParallelMode() for themselves.
   */
  void
  PreventCommandIfParallelMode(const char *cmdname)
  {
  	if (IsInParallelMode())
  		ereport(ERROR,
  				(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
  		/* translator: %s is name of a SQL command, eg CREATE */
  				 errmsg("cannot execute %s during a parallel operation",
  						cmdname)));
  }
  
  /*
   * PreventCommandDuringRecovery: throw error if RecoveryInProgress
   *
   * The majority of operations that are unsafe in a Hot Standby
   * will be rejected by XactReadOnly tests.  However there are a few
   * commands that are allowed in "read-only" xacts but cannot be allowed
   * in Hot Standby mode.  Those commands should call this function.
   */
  void
  PreventCommandDuringRecovery(const char *cmdname)
  {
  	if (RecoveryInProgress())
  		ereport(ERROR,
  				(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
  		/* translator: %s is name of a SQL command, eg CREATE */
  				 errmsg("cannot execute %s during recovery",
  						cmdname)));
  }
  
  /*
   * CheckRestrictedOperation: throw error for hazardous command if we're
   * inside a security restriction context.
   *
   * This is needed to protect session-local state for which there is not any
   * better-defined protection mechanism, such as ownership.
   */
  static void
  CheckRestrictedOperation(const char *cmdname)
  {
  	if (InSecurityRestrictedOperation())
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  		/* translator: %s is name of a SQL command, eg PREPARE */
  				 errmsg("cannot execute %s within security-restricted operation",
  						cmdname)));
  }
  
  
  /*
   * ProcessUtility
   *		general utility function invoker
   *
   *	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)
   *	params: parameters to use during execution
   *	queryEnv: environment for parse through execution (e.g., ephemeral named
   *		tables like trigger transition tables).  May be NULL.
   *	dest: where to send results
   *	completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
   *		in which to store a command completion status string.
   *
   * Caller MUST supply a queryString; it is not allowed (anymore) to pass NULL.
   * If you really don't have source text, you can pass a constant string,
   * perhaps "(query not available)".
   *
   * completionTag is only set nonempty if we want to return a nondefault status.
   *
   * completionTag may be NULL if caller doesn't want a status string.
   *
   * Note for users of ProcessUtility_hook: the same queryString may be passed
   * to multiple invocations of ProcessUtility when processing a query string
   * containing multiple semicolon-separated statements.  One should use
   * pstmt->stmt_location and pstmt->stmt_len to identify the substring
   * containing the current statement.  Keep in mind also that some utility
   * statements (e.g., CREATE SCHEMA) will recurse to ProcessUtility to process
   * sub-statements, often passing down the same queryString, stmt_location,
   * and stmt_len that were given for the whole statement.
   */
  void
  ProcessUtility(PlannedStmt *pstmt,
  			   const char *queryString,
  			   ProcessUtilityContext context,
  			   ParamListInfo params,
  			   QueryEnvironment *queryEnv,
  			   DestReceiver *dest,
  			   char *completionTag)
  {
  	Assert(IsA(pstmt, PlannedStmt));
  	Assert(pstmt->commandType == CMD_UTILITY);
  	Assert(queryString != NULL);	/* required as of 8.4 */
  
  	/*
  	 * We provide a function hook variable that lets loadable plugins get
  	 * control when ProcessUtility is called.  Such a plugin would normally
  	 * call standard_ProcessUtility().
  	 */
  	if (ProcessUtility_hook)
  		(*ProcessUtility_hook) (pstmt, queryString,
  								context, params, queryEnv,
  								dest, completionTag);
  	else
  		standard_ProcessUtility(pstmt, queryString,
  								context, params, queryEnv,
  								dest, completionTag);
  }
  
  /*
   * standard_ProcessUtility itself deals only with utility commands for
   * which we do not provide event trigger support.  Commands that do have
   * such support are passed down to ProcessUtilitySlow, which contains the
   * necessary infrastructure for such triggers.
   *
   * This division is not just for performance: it's critical that the
   * event trigger code not be invoked when doing START TRANSACTION for
   * example, because we might need to refresh the event trigger cache,
   * which requires being in a valid transaction.
   */
  void
  standard_ProcessUtility(PlannedStmt *pstmt,
  						const char *queryString,
  						ProcessUtilityContext context,
  						ParamListInfo params,
  						QueryEnvironment *queryEnv,
  						DestReceiver *dest,
  						char *completionTag)
  {
  	Node	   *parsetree = pstmt->utilityStmt;
  	bool		isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
  	bool		isAtomicContext = (!(context == PROCESS_UTILITY_TOPLEVEL || context == PROCESS_UTILITY_QUERY_NONATOMIC) || IsTransactionBlock());
  	ParseState *pstate;
  
  	/* This can recurse, so check for excessive recursion */
  	check_stack_depth();
  
  	check_xact_readonly(parsetree);
  
  	if (completionTag)
  		completionTag[0] = '\0';
  
  	pstate = make_parsestate(NULL);
  	pstate->p_sourcetext = queryString;
  
  	switch (nodeTag(parsetree))
  	{
  			/*
  			 * ******************** transactions ********************
  			 */
  		case T_TransactionStmt:
  			{
  				TransactionStmt *stmt = (TransactionStmt *) parsetree;
  
  				switch (stmt->kind)
  				{
  						/*
  						 * START TRANSACTION, as defined by SQL99: Identical
  						 * to BEGIN.  Same code for both.
  						 */
  					case TRANS_STMT_BEGIN:
  					case TRANS_STMT_START:
  						{
  							ListCell   *lc;
  
  							BeginTransactionBlock();
  							foreach(lc, stmt->options)
  							{
  								DefElem    *item = (DefElem *) lfirst(lc);
  
  								if (strcmp(item->defname, "transaction_isolation") == 0)
  									SetPGVariable("transaction_isolation",
  												  list_make1(item->arg),
  												  true);
  								else if (strcmp(item->defname, "transaction_read_only") == 0)
  									SetPGVariable("transaction_read_only",
  												  list_make1(item->arg),
  												  true);
  								else if (strcmp(item->defname, "transaction_deferrable") == 0)
  									SetPGVariable("transaction_deferrable",
  												  list_make1(item->arg),
  												  true);
  							}
  						}
  						break;
  
  					case TRANS_STMT_COMMIT:
  						if (!EndTransactionBlock())
  						{
  							/* report unsuccessful commit in completionTag */
  							if (completionTag)
  								strcpy(completionTag, "ROLLBACK");
  						}
  						break;
  
  					case TRANS_STMT_PREPARE:
  						if  (IsYugaByteEnabled()) {
  							ereport(ERROR,
  									(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  									errmsg("PREPARE not supported by YugaByte yet")));
  						}
  						PreventCommandDuringRecovery("PREPARE TRANSACTION");
  						if (!PrepareTransactionBlock(stmt->gid))
  						{
  							/* report unsuccessful commit in completionTag */
  							if (completionTag)
  								strcpy(completionTag, "ROLLBACK");
  						}
  						break;
  
  					case TRANS_STMT_COMMIT_PREPARED:
  						PreventInTransactionBlock(isTopLevel, "COMMIT PREPARED");
  						PreventCommandDuringRecovery("COMMIT PREPARED");
  						FinishPreparedTransaction(stmt->gid, true);
  						break;
  
  					case TRANS_STMT_ROLLBACK_PREPARED:
  						PreventInTransactionBlock(isTopLevel, "ROLLBACK PREPARED");
  						PreventCommandDuringRecovery("ROLLBACK PREPARED");
  						FinishPreparedTransaction(stmt->gid, false);
  						break;
  
  					case TRANS_STMT_ROLLBACK:
  						UserAbortTransactionBlock();
  						break;
  
  					case TRANS_STMT_SAVEPOINT:
  						RequireTransactionBlock(isTopLevel, "SAVEPOINT");
  						DefineSavepoint(stmt->savepoint_name);
  						break;
  
  					case TRANS_STMT_RELEASE:
  						RequireTransactionBlock(isTopLevel, "RELEASE SAVEPOINT");
  						ReleaseSavepoint(stmt->savepoint_name);
  						break;
  
  					case TRANS_STMT_ROLLBACK_TO:
  						RequireTransactionBlock(isTopLevel, "ROLLBACK TO SAVEPOINT");
  						RollbackToSavepoint(stmt->savepoint_name);
  
  						/*
  						 * CommitTransactionCommand is in charge of
  						 * re-defining the savepoint again
  						 */
  						break;
  				}
  			}
  			break;
  
  			/*
  			 * Portal (cursor) manipulation
  			 */
  		case T_DeclareCursorStmt:
  			PerformCursorOpen((DeclareCursorStmt *) parsetree, params,
  							  queryString, isTopLevel);
  			break;
  
  		case T_ClosePortalStmt:
  			{
  				ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
  
  				CheckRestrictedOperation("CLOSE");
  				PerformPortalClose(stmt->portalname);
  			}
  			break;
  
  		case T_FetchStmt:
  			PerformPortalFetch((FetchStmt *) parsetree, dest,
  							   completionTag);
  			break;
  
  		case T_DoStmt:
  			ExecuteDoStmt((DoStmt *) parsetree, isAtomicContext);
  			break;
  
  		case T_CreateTableGroupStmt:
  			PreventInTransactionBlock(isTopLevel, "CREATE TABLEGROUP");
  			CreateTableGroup((CreateTableGroupStmt *) parsetree);
  			break;
  
  		case T_DropTableGroupStmt:
  			PreventInTransactionBlock(isTopLevel, "DROP TABLEGROUP");
  			DropTableGroup((DropTableGroupStmt *) parsetree);
  			break;
  
  		case T_CreateTableSpaceStmt:
  			/* no event triggers for global objects */
  			PreventInTransactionBlock(isTopLevel, "CREATE TABLESPACE");
  			CreateTableSpace((CreateTableSpaceStmt *) parsetree);
  			break;
  
  		case T_DropTableSpaceStmt:
  			/* no event triggers for global objects */
  			PreventInTransactionBlock(isTopLevel, "DROP TABLESPACE");
  			DropTableSpace((DropTableSpaceStmt *) parsetree);
  			break;
  
  		case T_AlterTableSpaceOptionsStmt:
  			/* no event triggers for global objects */
  			AlterTableSpaceOptions((AlterTableSpaceOptionsStmt *) parsetree);
  			break;
  
  		case T_TruncateStmt:
  			ExecuteTruncate((TruncateStmt *) parsetree);
  			break;
  
  		case T_CopyStmt:
  			{
  				uint64		processed;
  
  				DoCopy(pstate, (CopyStmt *) parsetree,
  					   pstmt->stmt_location, pstmt->stmt_len,
  					   &processed);
  				if (completionTag)
  					snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
  							 "COPY " UINT64_FORMAT, processed);
  			}
  			break;
  
  		case T_PrepareStmt:
  			CheckRestrictedOperation("PREPARE");
  			PrepareQuery((PrepareStmt *) parsetree, queryString,
  						 pstmt->stmt_location, pstmt->stmt_len);
  			break;
  
  		case T_ExecuteStmt:
  			ExecuteQuery((ExecuteStmt *) parsetree, NULL,
  						 queryString, params,
  						 dest, completionTag);
  			break;
  
  		case T_DeallocateStmt:
  			CheckRestrictedOperation("DEALLOCATE");
  			DeallocateQuery((DeallocateStmt *) parsetree);
  			break;
  
  		case T_GrantRoleStmt:
  			/* no event triggers for global objects */
  			GrantRole((GrantRoleStmt *) parsetree);
  			break;
  
  		case T_CreatedbStmt:
  			/* no event triggers for global objects */
  			PreventInTransactionBlock(isTopLevel, "CREATE DATABASE");
  			createdb(pstate, (CreatedbStmt *) parsetree);
  			break;
  
  		case T_AlterDatabaseStmt:
  			/* no event triggers for global objects */
  			AlterDatabase(pstate, (AlterDatabaseStmt *) parsetree, isTopLevel);
  			break;
  
  		case T_AlterDatabaseSetStmt:
  			/* no event triggers for global objects */
  			AlterDatabaseSet((AlterDatabaseSetStmt *) parsetree);
  			break;
  
  		case T_DropdbStmt:
  			{
  				DropdbStmt *stmt = (DropdbStmt *) parsetree;
  
  				/* no event triggers for global objects */
  				PreventInTransactionBlock(isTopLevel, "DROP DATABASE");
  				dropdb(stmt->dbname, stmt->missing_ok);
  			}
  			break;
  
  			/* Query-level asynchronous notification */
  		case T_NotifyStmt:
  			{
  				NotifyStmt *stmt = (NotifyStmt *) parsetree;
  
  				PreventCommandDuringRecovery("NOTIFY");
  				Async_Notify(stmt->conditionname, stmt->payload);
  			}
  			break;
  
  		case T_ListenStmt:
  			{
  				ListenStmt *stmt = (ListenStmt *) parsetree;
  
  				PreventCommandDuringRecovery("LISTEN");
  				CheckRestrictedOperation("LISTEN");
  				Async_Listen(stmt->conditionname);
  			}
  			break;
  
  		case T_UnlistenStmt:
  			{
  				UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
  
  				/* we allow UNLISTEN during recovery, as it's a noop */
  				CheckRestrictedOperation("UNLISTEN");
  				if (stmt->conditionname)
  					Async_Unlisten(stmt->conditionname);
  				else
  					Async_UnlistenAll();
  			}
  			break;
  
  		case T_LoadStmt:
  			{
  				LoadStmt   *stmt = (LoadStmt *) parsetree;
  
  				closeAllVfds(); /* probably not necessary... */
  				/* Allowed names are restricted if you're not superuser */
  				load_file(stmt->filename, !superuser());
  			}
  			break;
  
  		case T_CallStmt:
  			ExecuteCallStmt(castNode(CallStmt, parsetree), params, isAtomicContext, dest);
  			break;
  
  		case T_ClusterStmt:
  			/* we choose to allow this during "read only" transactions */
  			PreventCommandDuringRecovery("CLUSTER");
  			/* forbidden in parallel mode due to CommandIsReadOnly */
  			cluster((ClusterStmt *) parsetree, isTopLevel);
  			break;
  
  		case T_VacuumStmt:
  			{
  				VacuumStmt *stmt = (VacuumStmt *) parsetree;
  
  				/* we choose to allow this during "read only" transactions */
  				PreventCommandDuringRecovery((stmt->options & VACOPT_VACUUM) ?
  											 "VACUUM" : "ANALYZE");
  				/* forbidden in parallel mode due to CommandIsReadOnly */
  				ExecVacuum(stmt, isTopLevel);
  			}
  			break;
  
  		case T_ExplainStmt:
  			ExplainQuery(pstate, (ExplainStmt *) parsetree, queryString, params,
  						 queryEnv, dest);
  			break;
  
  		case T_AlterSystemStmt:
  			PreventInTransactionBlock(isTopLevel, "ALTER SYSTEM");
  			AlterSystemSetConfigFile((AlterSystemStmt *) parsetree);
  			break;
  
  		case T_VariableSetStmt:
  			ExecSetVariableStmt((VariableSetStmt *) parsetree, isTopLevel);
  			break;
  
  		case T_VariableShowStmt:
  			{
  				VariableShowStmt *n = (VariableShowStmt *) parsetree;
  
  				GetPGVariable(n->name, dest);
  			}
  			break;
  
  		case T_DiscardStmt:
  			/* should we allow DISCARD PLANS? */
  			CheckRestrictedOperation("DISCARD");
  			DiscardCommand((DiscardStmt *) parsetree, isTopLevel);
  			break;
  
  		case T_CreateEventTrigStmt:
  			/* no event triggers on event triggers */
  			CreateEventTrigger((CreateEventTrigStmt *) parsetree);
  			break;
  
  		case T_AlterEventTrigStmt:
  			/* no event triggers on event triggers */
  			AlterEventTrigger((AlterEventTrigStmt *) parsetree);
  			break;
  
  			/*
  			 * ******************************** ROLE statements ****
  			 */
  		case T_CreateRoleStmt:
  			/* no event triggers for global objects */
  			CreateRole(pstate, (CreateRoleStmt *) parsetree);
  			break;
  
  		case T_AlterRoleStmt:
  			/* no event triggers for global objects */
  			AlterRole((AlterRoleStmt *) parsetree);
  			break;
  
  		case T_AlterRoleSetStmt:
  			/* no event triggers for global objects */
  			AlterRoleSet((AlterRoleSetStmt *) parsetree);
  			break;
  
  		case T_DropRoleStmt:
  			/* no event triggers for global objects */
  			DropRole((DropRoleStmt *) parsetree);
  			break;
  
  		case T_ReassignOwnedStmt:
  			/* no event triggers for global objects */
  			ReassignOwnedObjects((ReassignOwnedStmt *) parsetree);
  			break;
  
  		case T_LockStmt:
  
  			/*
  			 * Since the lock would just get dropped immediately, LOCK TABLE
  			 * outside a transaction block is presumed to be user error.
  			 */
  			RequireTransactionBlock(isTopLevel, "LOCK TABLE");
  			/* forbidden in parallel mode due to CommandIsReadOnly */
  			LockTableCommand((LockStmt *) parsetree);
  			break;
  
  		case T_ConstraintsSetStmt:
  			WarnNoTransactionBlock(isTopLevel, "SET CONSTRAINTS");
  			AfterTriggerSetState((ConstraintsSetStmt *) parsetree);
  			break;
  
  		case T_CheckPointStmt:
  			if (!superuser())
  				ereport(ERROR,
  						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  						 errmsg("must be superuser to do CHECKPOINT")));
  
  			/*
  			 * You might think we should have a PreventCommandDuringRecovery()
  			 * here, but we interpret a CHECKPOINT command during recovery as
  			 * a request for a restartpoint instead. We allow this since it
  			 * can be a useful way of reducing switchover time when using
  			 * various forms of replication.
  			 */
  			RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_WAIT |
  							  (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
  			break;
  
  		case T_ReindexStmt:
  			{
  				ReindexStmt *stmt = (ReindexStmt *) parsetree;
  
  				/* we choose to allow this during "read only" transactions */
  				PreventCommandDuringRecovery("REINDEX");
  				/* forbidden in parallel mode due to CommandIsReadOnly */
  				switch (stmt->kind)
  				{
  					case REINDEX_OBJECT_INDEX:
  						ReindexIndex(stmt->relation, stmt->options);
  						break;
  					case REINDEX_OBJECT_TABLE:
  						ReindexTable(stmt->relation, stmt->options);
  						break;
  					case REINDEX_OBJECT_SCHEMA:
  					case REINDEX_OBJECT_SYSTEM:
  					case REINDEX_OBJECT_DATABASE:
  
  						/*
  						 * This cannot run inside a user transaction block; if
  						 * we were inside a transaction, then its commit- and
  						 * start-transaction-command calls would not have the
  						 * intended effect!
  						 */
  						PreventInTransactionBlock(isTopLevel,
  												  (stmt->kind == REINDEX_OBJECT_SCHEMA) ? "REINDEX SCHEMA" :
  												  (stmt->kind == REINDEX_OBJECT_SYSTEM) ? "REINDEX SYSTEM" :
  												  "REINDEX DATABASE");
  						ReindexMultipleTables(stmt->name, stmt->kind, stmt->options);
  						break;
  					default:
  						elog(ERROR, "unrecognized object type: %d",
  							 (int) stmt->kind);
  						break;
  				}
  			}
  			break;
  
  		case T_BackfillIndexStmt:
  			{
  				BackfillIndexStmt *stmt = (BackfillIndexStmt *) parsetree;
  				BackfillIndex(stmt);
  			}
  			break;
  
  			/*
  			 * The following statements are supported by Event Triggers only
  			 * in some cases, so we "fast path" them in the other cases.
  			 */
  
  		case T_GrantStmt:
  			{
  				GrantStmt  *stmt = (GrantStmt *) parsetree;
  
  				if (EventTriggerSupportsObjectType(stmt->objtype))
  					ProcessUtilitySlow(pstate, pstmt, queryString,
  									   context, params, queryEnv,
  									   dest, completionTag);
  				else
  					ExecuteGrantStmt(stmt);
  			}
  			break;
  
  		case T_DropStmt:
  			{
  				DropStmt   *stmt = (DropStmt *) parsetree;
  
  				if (EventTriggerSupportsObjectType(stmt->removeType))
  					ProcessUtilitySlow(pstate, pstmt, queryString,
  									   context, params, queryEnv,
  									   dest, completionTag);
  				else
  					ExecDropStmt(stmt, isTopLevel);
  			}
  			break;
  
  		case T_RenameStmt:
  			{
  				RenameStmt *stmt = (RenameStmt *) parsetree;
  
  				if (EventTriggerSupportsObjectType(stmt->renameType))
  					ProcessUtilitySlow(pstate, pstmt, queryString,
  									   context, params, queryEnv,
  									   dest, completionTag);
  				else
  					ExecRenameStmt(stmt);
  			}
  			break;
  
  		case T_AlterObjectDependsStmt:
  			{
  				AlterObjectDependsStmt *stmt = (AlterObjectDependsStmt *) parsetree;
  
  				if (EventTriggerSupportsObjectType(stmt->objectType))
  					ProcessUtilitySlow(pstate, pstmt, queryString,
  									   context, params, queryEnv,
  									   dest, completionTag);
  				else
  					ExecAlterObjectDependsStmt(stmt, NULL);
  			}
  			break;
  
  		case T_AlterObjectSchemaStmt:
  			{
  				AlterObjectSchemaStmt *stmt = (AlterObjectSchemaStmt *) parsetree;
  
  				if (EventTriggerSupportsObjectType(stmt->objectType))
  					ProcessUtilitySlow(pstate, pstmt, queryString,
  									   context, params, queryEnv,
  									   dest, completionTag);
  				else
  					ExecAlterObjectSchemaStmt(stmt, NULL);
  			}
  			break;
  
  		case T_AlterOwnerStmt:
  			{
  				AlterOwnerStmt *stmt = (AlterOwnerStmt *) parsetree;
  
  				if (EventTriggerSupportsObjectType(stmt->objectType))
  					ProcessUtilitySlow(pstate, pstmt, queryString,
  									   context, params, queryEnv,
  									   dest, completionTag);
  				else
  					ExecAlterOwnerStmt(stmt);
  			}
  			break;
  
  		case T_CommentStmt:
  			{
  				CommentStmt *stmt = (CommentStmt *) parsetree;
  
  				if (EventTriggerSupportsObjectType(stmt->objtype))
  					ProcessUtilitySlow(pstate, pstmt, queryString,
  									   context, params, queryEnv,
  									   dest, completionTag);
  				else
  					CommentObject(stmt);
  				break;
  			}
  
  		case T_SecLabelStmt:
  			{
  				SecLabelStmt *stmt = (SecLabelStmt *) parsetree;
  
  				if (EventTriggerSupportsObjectType(stmt->objtype))
  					ProcessUtilitySlow(pstate, pstmt, queryString,
  									   context, params, queryEnv,
  									   dest, completionTag);
  				else
  					ExecSecLabelStmt(stmt);
  				break;
  			}
  
  		default:
  			/* All other statement types have event trigger support */
  			ProcessUtilitySlow(pstate, pstmt, queryString,
  							   context, params, queryEnv,
  							   dest, completionTag);
  			break;
  	}
  
  	free_parsestate(pstate);
  }
  
  /*
   * The "Slow" variant of ProcessUtility should only receive statements
   * supported by the event triggers facility.  Therefore, we always
   * perform the trigger support calls if the context allows it.
   */
  static void
  ProcessUtilitySlow(ParseState *pstate,
  				   PlannedStmt *pstmt,
  				   const char *queryString,
  				   ProcessUtilityContext context,
  				   ParamListInfo params,
  				   QueryEnvironment *queryEnv,
  				   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();
  
  	/* PG_TRY block is to ensure we call EventTriggerEndCompleteQuery */
  	PG_TRY();
  	{
  		if (isCompleteQuery)
  			EventTriggerDDLCommandStart(parsetree);
  
  		switch (nodeTag(parsetree))
  		{
  				/*
  				 * relation and attribute manipulation
  				 */
  			case T_CreateSchemaStmt:
  				CreateSchemaCommand((CreateSchemaStmt *) parsetree,
  									queryString,
  									pstmt->stmt_location,
  									pstmt->stmt_len);
  
  				/*
  				 * EventTriggerCollectSimpleCommand called by
  				 * CreateSchemaCommand
  				 */
  				commandCollected = true;
  				break;
  
  			case T_CreateStmt:
  			case T_CreateForeignTableStmt:
  				{
  					List	   *stmts;
  					ListCell   *l;
  
  					/* Run parse analysis ... */
  					stmts = transformCreateStmt((CreateStmt *) parsetree,
  												queryString);
  
  					/* ... and do it */
  					foreach(l, stmts)
  					{
  						Node	   *stmt = (Node *) lfirst(l);
  
  						if (IsA(stmt, CreateStmt))
  						{
  							Datum		toast_options;
  							static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
  
  							/* Create the table itself */
  							address = DefineRelation((CreateStmt *) stmt,
  													 RELKIND_RELATION,
  													 InvalidOid, NULL,
  													 queryString);
  							EventTriggerCollectSimpleCommand(address,
  															 secondaryObject,
  															 stmt);
  
  							/* No need for toasting attributes in YB mode */
  							if (!IsYugaByteEnabled())
  							{
  								/*
  								* Let NewRelationCreateToastTable decide if this
  								* one needs a secondary relation too.
  								*/
  								CommandCounterIncrement();
  
  								/*
  								* parse and validate reloptions for the toast
  								* table
  								*/
  								toast_options = transformRelOptions((Datum) 0,
  																	((CreateStmt *) stmt)->options,
  																	"toast",
  																	validnsps,
  																	true,
  																	false);
  								(void) heap_reloptions(RELKIND_TOASTVALUE,
  													toast_options,
  													true);
  
  								NewRelationCreateToastTable(address.objectId,
  								                            toast_options);
  							}
  						}
  						else if (IsA(stmt, CreateForeignTableStmt))
  						{
  							/* Create the table itself */
  							address = DefineRelation((CreateStmt *) stmt,
  													 RELKIND_FOREIGN_TABLE,
  													 InvalidOid, NULL,
  													 queryString);
  							CreateForeignTable((CreateForeignTableStmt *) stmt,
  											   address.objectId);
  							EventTriggerCollectSimpleCommand(address,
  															 secondaryObject,
  															 stmt);
  						}
  						else
  						{
  							/*
  							 * 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,
  										   NULL,
  										   None_Receiver,
  										   NULL);
  						}
  
  						/* Need CCI between commands */
  						if (lnext(l) != NULL)
  							CommandCounterIncrement();
  					}
  
  					/*
  					 * The multiple commands generated here are stashed
  					 * individually, so disable collection below.
  					 */
  					commandCollected = true;
  				}
  				break;
  
  			case T_AlterTableStmt:
  				{
  					AlterTableStmt *atstmt = (AlterTableStmt *) parsetree;
  					Oid			relid;
  					List	   *stmts;
  					ListCell   *l;
  					LOCKMODE	lockmode;
  
  					/*
  					 * Figure out lock mode, and acquire lock.  This also does
  					 * basic permissions checks, so that we won't wait for a
  					 * lock on (for example) a relation on which we have no
  					 * permissions.
  					 */
  					lockmode = AlterTableGetLockLevel(atstmt->cmds);
  					relid = AlterTableLookupRelation(atstmt, lockmode);
  
  					if (OidIsValid(relid))
  					{
  						/* Run parse analysis ... */
  						stmts = transformAlterTableStmt(relid, atstmt,
  														queryString);
  
  						/* ... ensure we have an event trigger context ... */
  						EventTriggerAlterTableStart(parsetree);
  						EventTriggerAlterTableRelid(relid);
  
  						/* ... and do it */
  						foreach(l, stmts)
  						{
  							Node	   *stmt = (Node *) lfirst(l);
  
  							if (IsA(stmt, AlterTableStmt))
  							{
  								/* Do the table alteration proper */
  								AlterTable(relid, lockmode,
  										   (AlterTableStmt *) stmt);
  							}
  							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.
  								 */
  								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,
  											   NULL,
  											   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:
  				{
  					AlterDomainStmt *stmt = (AlterDomainStmt *) parsetree;
  
  					/*
  					 * Some or all of these functions are recursive to cover
  					 * inherited things, so permission checks are done there.
  					 */
  					switch (stmt->subtype)
  					{
  						case 'T':	/* ALTER DOMAIN DEFAULT */
  
  							/*
  							 * Recursively alter column default for table and,
  							 * if requested, for descendants
  							 */
  							address =
  								AlterDomainDefault(stmt->typeName,
  												   stmt->def);
  							break;
  						case 'N':	/* ALTER DOMAIN DROP NOT NULL */
  							address =
  								AlterDomainNotNull(stmt->typeName,
  												   false);
  							break;
  						case 'O':	/* ALTER DOMAIN SET NOT NULL */
  							address =
  								AlterDomainNotNull(stmt->typeName,
  												   true);
  							break;
  						case 'C':	/* ADD CONSTRAINT */
  							address =
  								AlterDomainAddConstraint(stmt->typeName,
  														 stmt->def,
  														 &secondaryObject);
  							break;
  						case 'X':	/* DROP CONSTRAINT */
  							address =
  								AlterDomainDropConstraint(stmt->typeName,
  														  stmt->name,
  														  stmt->behavior,
  														  stmt->missing_ok);
  							break;
  						case 'V':	/* VALIDATE CONSTRAINT */
  							address =
  								AlterDomainValidateConstraint(stmt->typeName,
  															  stmt->name);
  							break;
  						default:	/* oops */
  							elog(ERROR, "unrecognized alter domain type: %d",
  								 (int) stmt->subtype);
  							break;
  					}
  				}
  				break;
  
  				/*
  				 * ************* object creation / destruction **************
  				 */
  			case T_DefineStmt:
  				{
  					DefineStmt *stmt = (DefineStmt *) parsetree;
  
  					switch (stmt->kind)
  					{
  						case OBJECT_AGGREGATE:
  							address =
  								DefineAggregate(pstate, stmt->defnames, stmt->args,
  												stmt->oldstyle,
  												stmt->definition);
  							break;
  						case OBJECT_OPERATOR:
  							Assert(stmt->args == NIL);
  							address = DefineOperator(stmt->defnames,
  													 stmt->definition);
  							break;
  						case OBJECT_TYPE:
  							Assert(stmt->args == NIL);
  							address = DefineType(pstate,
  												 stmt->defnames,
  												 stmt->definition);
  							break;
  						case OBJECT_TSPARSER:
  							Assert(stmt->args == NIL);
  							address = DefineTSParser(stmt->defnames,
  													 stmt->definition);
  							break;
  						case OBJECT_TSDICTIONARY:
  							Assert(stmt->args == NIL);
  							address = DefineTSDictionary(stmt->defnames,
  														 stmt->definition);
  							break;
  						case OBJECT_TSTEMPLATE:
  							Assert(stmt->args == NIL);
  							address = DefineTSTemplate(stmt->defnames,
  													   stmt->definition);
  							break;
  						case OBJECT_TSCONFIGURATION:
  							Assert(stmt->args == NIL);
  							address = DefineTSConfiguration(stmt->defnames,
  															stmt->definition,
  															&secondaryObject);
  							break;
  						case OBJECT_COLLATION:
  							Assert(stmt->args == NIL);
  							address = DefineCollation(pstate,
  													  stmt->defnames,
  													  stmt->definition,
  													  stmt->if_not_exists);
  							break;
  						default:
  							elog(ERROR, "unrecognized define stmt type: %d",
  								 (int) stmt->kind);
  							break;
  					}
  				}
  				break;
  
  			case T_IndexStmt:	/* CREATE INDEX */
  				{
  					IndexStmt  *stmt = (IndexStmt *) parsetree;
  					Oid			relid;
  					LOCKMODE	lockmode;
  
  					if (stmt->concurrent)
  						PreventInTransactionBlock(isTopLevel,
  												  "CREATE INDEX CONCURRENTLY");
  
  					/*
  					 * Look up the relation OID just once, right here at the
  					 * beginning, so that we don't end up repeating the name
  					 * lookup later and latching onto a different relation
  					 * partway through.  To avoid lock upgrade hazards, it's
  					 * important that we take the strongest lock that will
  					 * eventually be needed here, so the lockmode calculation
  					 * needs to match what DefineIndex() does.
  					 */
  					lockmode = stmt->concurrent ? ShareUpdateExclusiveLock
  						: ShareLock;
  					relid =
  						RangeVarGetRelidExtended(stmt->relation, lockmode,
  												 0,
  												 RangeVarCallbackOwnsRelation,
  												 NULL);
  
  					/*
  					 * CREATE INDEX on partitioned tables (but not regular
  					 * inherited tables) recurses to partitions, so we must
  					 * acquire locks early to avoid deadlocks.
  					 *
  					 * We also take the opportunity to verify that all
  					 * partitions are something we can put an index on, to
  					 * avoid building some indexes only to fail later.
  					 */
  					if (stmt->relation->inh &&
  						get_rel_relkind(relid) == RELKIND_PARTITIONED_TABLE)
  					{
  						ListCell   *lc;
  						List	   *inheritors = NIL;
  
  						inheritors = find_all_inheritors(relid, lockmode, NULL);
  						foreach(lc, inheritors)
  						{
  							char		relkind = get_rel_relkind(lfirst_oid(lc));
  
  							if (relkind != RELKIND_RELATION &&
  								relkind != RELKIND_MATVIEW &&
  								relkind != RELKIND_PARTITIONED_TABLE)
  								ereport(ERROR,
  										(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
  										 errmsg("cannot create index on partitioned table \"%s\"",
  												stmt->relation->relname),
  										 errdetail("Table \"%s\" contains partitions that are foreign tables.",
  												   stmt->relation->relname)));
  						}
  						list_free(inheritors);
  					}
  
  					/* Run parse analysis ... */
  					stmt = transformIndexStmt(relid, stmt, queryString);
  
  					/* ... and do it */
  					EventTriggerAlterTableStart(parsetree);
  					address =
  						DefineIndex(relid,	/* OID of heap relation */
  									stmt,
  									InvalidOid, /* no predefined OID */
  									InvalidOid, /* no parent index */
  									InvalidOid, /* no parent constraint */
  									false,	/* is_alter_table */
  									true,	/* check_rights */
  									true,	/* check_not_in_use */
  									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:
  				address = CreateExtension(pstate, (CreateExtensionStmt *) parsetree);
  				break;
  
  			case T_AlterExtensionStmt:
  				address = ExecAlterExtensionStmt(pstate, (AlterExtensionStmt *) parsetree);
  				break;
  
  			case T_AlterExtensionContentsStmt:
  				address = ExecAlterExtensionContentsStmt((AlterExtensionContentsStmt *) parsetree,
  														 &secondaryObject);
  				break;
  
  			case T_CreateFdwStmt:
  				address = CreateForeignDataWrapper((CreateFdwStmt *) parsetree);
  				break;
  
  			case T_AlterFdwStmt:
  				address = AlterForeignDataWrapper((AlterFdwStmt *) parsetree);
  				break;
  
  			case T_CreateForeignServerStmt:
  				address = CreateForeignServer((CreateForeignServerStmt *) parsetree);
  				break;
  
  			case T_AlterForeignServerStmt:
  				address = AlterForeignServer((AlterForeignServerStmt *) parsetree);
  				break;
  
  			case T_CreateUserMappingStmt:
  				address = CreateUserMapping((CreateUserMappingStmt *) parsetree);
  				break;
  
  			case T_AlterUserMappingStmt:
  				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;
  
  					address = DefineCompositeType(stmt->typevar,
  												  stmt->coldeflist);
  				}
  				break;
  
  			case T_CreateEnumStmt:	/* CREATE TYPE AS ENUM */
  				address = DefineEnum((CreateEnumStmt *) parsetree);
  				break;
  
  			case T_CreateRangeStmt: /* CREATE TYPE AS RANGE */
  				address = DefineRange((CreateRangeStmt *) parsetree);
  				break;
  
  			case T_AlterEnumStmt:	/* ALTER TYPE (enum) */
  				address = AlterEnum((AlterEnumStmt *) parsetree, isTopLevel);
  				break;
  
  			case T_ViewStmt:	/* CREATE VIEW */
  				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 */
  				address = CreateFunction(pstate, (CreateFunctionStmt *) parsetree);
  				break;
  
  			case T_AlterFunctionStmt:	/* ALTER FUNCTION */
  				address = AlterFunction(pstate, (AlterFunctionStmt *) parsetree);
  				break;
  
  			case T_RuleStmt:	/* CREATE RULE */
  				address = DefineRule((RuleStmt *) parsetree, queryString);
  				break;
  
  			case T_CreateSeqStmt:
  				address = DefineSequence(pstate, (CreateSeqStmt *) parsetree);
  				break;
  
  			case T_AlterSeqStmt:
  				address = AlterSequence(pstate, (AlterSeqStmt *) parsetree);
  				break;
  
  			case T_CreateTableAsStmt:
  				address = ExecCreateTableAs((CreateTableAsStmt *) parsetree,
  											queryString, params, queryEnv,
  											completionTag);
  				break;
  
  			case T_RefreshMatViewStmt:
  
  				/*
  				 * REFRESH 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:
  				address = CreateTrigger((CreateTrigStmt *) parsetree,
  										queryString, InvalidOid, InvalidOid,
  										InvalidOid, InvalidOid, InvalidOid,
  										InvalidOid, NULL, false, false);
  				break;
  
  			case T_CreatePLangStmt:
  				address = CreateProceduralLanguage((CreatePLangStmt *) parsetree);
  				break;
  
  			case T_CreateDomainStmt:
  				address = DefineDomain((CreateDomainStmt *) parsetree);
  				break;
  
  			case T_CreateConversionStmt:
  				address = CreateConversionCommand((CreateConversionStmt *) parsetree);
  				break;
  
  			case T_CreateCastStmt:
  				address = CreateCast((CreateCastStmt *) parsetree);
  				break;
  
  			case T_CreateOpClassStmt:
  				DefineOpClass((CreateOpClassStmt *) parsetree);
  				/* command is stashed in DefineOpClass */
  				commandCollected = true;
  				break;
  
  			case T_CreateOpFamilyStmt:
  				address = DefineOpFamily((CreateOpFamilyStmt *) parsetree);
  				break;
  
  			case T_CreateTransformStmt:
  				address = CreateTransform((CreateTransformStmt *) parsetree);
  				break;
  
  			case T_AlterOpFamilyStmt:
  				AlterOpFamily((AlterOpFamilyStmt *) parsetree);
  				/* commands are stashed in AlterOpFamily */
  				commandCollected = true;
  				break;
  
  			case T_AlterTSDictionaryStmt:
  				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:
  				address = ExecRenameStmt((RenameStmt *) parsetree);
  				break;
  
  			case T_AlterObjectDependsStmt:
  				address =
  					ExecAlterObjectDependsStmt((AlterObjectDependsStmt *) parsetree,
  											   &secondaryObject);
  				break;
  
  			case T_AlterObjectSchemaStmt:
  				address =
  					ExecAlterObjectSchemaStmt((AlterObjectSchemaStmt *) parsetree,
  											  &secondaryObject);
  				break;
  
  			case T_AlterOwnerStmt:
  				address = ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree);
  				break;
  
  			case T_AlterOperatorStmt:
  				address = AlterOperator((AlterOperatorStmt *) parsetree);
  				break;
  
  			case T_CommentStmt:
  				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(pstate, (AlterDefaultPrivilegesStmt *) parsetree);
  				EventTriggerCollectAlterDefPrivs((AlterDefaultPrivilegesStmt *) parsetree);
  				commandCollected = true;
  				break;
  
  			case T_CreatePolicyStmt:	/* CREATE POLICY */
  				address = CreatePolicy((CreatePolicyStmt *) parsetree);
  				break;
  
  			case T_AlterPolicyStmt: /* ALTER POLICY */
  				address = AlterPolicy((AlterPolicyStmt *) parsetree);
  				break;
  
  			case T_SecLabelStmt:
  				address = ExecSecLabelStmt((SecLabelStmt *) parsetree);
  				break;
  
  			case T_CreateAmStmt:
  				address = CreateAccessMethod((CreateAmStmt *) parsetree);
  				break;
  
  			case T_CreatePublicationStmt:
  				address = CreatePublication((CreatePublicationStmt *) parsetree);
  				break;
  
  			case T_AlterPublicationStmt:
  				AlterPublication((AlterPublicationStmt *) parsetree);
  
  				/*
  				 * AlterPublication calls EventTriggerCollectSimpleCommand
  				 * directly
  				 */
  				commandCollected = true;
  				break;
  
  			case T_CreateSubscriptionStmt:
  				address = CreateSubscription((CreateSubscriptionStmt *) parsetree,
  											 isTopLevel);
  				break;
  
  			case T_AlterSubscriptionStmt:
  				address = AlterSubscription((AlterSubscriptionStmt *) parsetree);
  				break;
  
  			case T_DropSubscriptionStmt:
  				DropSubscription((DropSubscriptionStmt *) parsetree, isTopLevel);
  				/* no commands stashed for DROP */
  				commandCollected = true;
  				break;
  
  			case T_CreateStatsStmt:
  				address = CreateStatistics((CreateStatsStmt *) parsetree);
  				break;
  
  			case T_AlterCollationStmt:
  				address = AlterCollation((AlterCollationStmt *) parsetree);
  				break;
  
  			default:
  				elog(ERROR, "unrecognized node type: %d",
  					 (int) nodeTag(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);
  			EventTriggerDDLCommandEnd(parsetree);
  		}
  	}
  	PG_CATCH();
  	{
  		if (needCleanup)
  			EventTriggerEndCompleteQuery();
  		PG_RE_THROW();
  	}
  	PG_END_TRY();
  
  	if (needCleanup)
  		EventTriggerEndCompleteQuery();
  }
  
  /*
   * Dispatch function for DropStmt
   */
  static void
  ExecDropStmt(DropStmt *stmt, bool isTopLevel)
  {
  	switch (stmt->removeType)
  	{
  		case OBJECT_INDEX:
  			if (stmt->concurrent)
  				PreventInTransactionBlock(isTopLevel,
  										  "DROP INDEX CONCURRENTLY");
  			switch_fallthrough();
  
  		case OBJECT_TABLE:
  		case OBJECT_SEQUENCE:
  		case OBJECT_VIEW:
  		case OBJECT_MATVIEW:
  		case OBJECT_FOREIGN_TABLE:
  			RemoveRelations(stmt);
  			break;
  		default:
  			RemoveObjects(stmt);
  			break;
  	}
  }
  
  
  /*
   * UtilityReturnsTuples
   *		Return "true" if this utility statement will send output to the
   *		destination.
   *
   * Generally, there should be a case here for each case in ProcessUtility
   * where "dest" is passed on.
   */
  bool
  UtilityReturnsTuples(Node *parsetree)
  {
  	switch (nodeTag(parsetree))
  	{
  		case T_CallStmt:
  			{
  				CallStmt   *stmt = (CallStmt *) parsetree;
  
  				return (stmt->funcexpr->funcresulttype == RECORDOID);
  			}
  		case T_FetchStmt:
  			{
  				FetchStmt  *stmt = (FetchStmt *) parsetree;
  				Portal		portal;
  
  				if (stmt->ismove)
  					return false;
  				portal = GetPortalByName(stmt->portalname);
  				if (!PortalIsValid(portal))
  					return false;	/* not our business to raise error */
  				return portal->tupDesc ? true : false;
  			}
  
  		case T_ExecuteStmt:
  			{
  				ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
  				PreparedStatement *entry;
  
  				entry = FetchPreparedStatement(stmt->name, false);
  				if (!entry)
  					return false;	/* not our business to raise error */
  				if (entry->plansource->resultDesc)
  					return true;
  				return false;
  			}
  
  		case T_ExplainStmt:
  			return true;
  
  		case T_VariableShowStmt:
  			return true;
  
  		default:
  			return false;
  	}
  }
  
  /*
   * UtilityTupleDescriptor
   *		Fetch the actual output tuple descriptor for a utility statement
   *		for which UtilityReturnsTuples() previously returned "true".
   *
   * The returned descriptor is created in (or copied into) the current memory
   * context.
   */
  TupleDesc
  UtilityTupleDescriptor(Node *parsetree)
  {
  	switch (nodeTag(parsetree))
  	{
  		case T_CallStmt:
  			return CallStmtResultDesc((CallStmt *) parsetree);
  
  		case T_FetchStmt:
  			{
  				FetchStmt  *stmt = (FetchStmt *) parsetree;
  				Portal		portal;
  
  				if (stmt->ismove)
  					return NULL;
  				portal = GetPortalByName(stmt->portalname);
  				if (!PortalIsValid(portal))
  					return NULL;	/* not our business to raise error */
  				return CreateTupleDescCopy(portal->tupDesc);
  			}
  
  		case T_ExecuteStmt:
  			{
  				ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
  				PreparedStatement *entry;
  
  				entry = FetchPreparedStatement(stmt->name, false);
  				if (!entry)
  					return NULL;	/* not our business to raise error */
  				return FetchPreparedStatementResultDesc(entry);
  			}
  
  		case T_ExplainStmt:
  			return ExplainResultDesc((ExplainStmt *) parsetree);
  
  		case T_VariableShowStmt:
  			{
  				VariableShowStmt *n = (VariableShowStmt *) parsetree;
  
  				return GetPGVariableResultDesc(n->name);
  			}
  
  		default:
  			return NULL;
  	}
  }
  
  
  /*
   * QueryReturnsTuples
   *		Return "true" if this Query will send output to the destination.
   */
  #ifdef NOT_USED
  bool
  QueryReturnsTuples(Query *parsetree)
  {
  	switch (parsetree->commandType)
  	{
  		case CMD_SELECT:
  			/* returns tuples */
  			return true;
  		case CMD_INSERT:
  		case CMD_UPDATE:
  		case CMD_DELETE:
  			/* the forms with RETURNING return tuples */
  			if (parsetree->returningList)
  				return true;
  			break;
  		case CMD_UTILITY:
  			return UtilityReturnsTuples(parsetree->utilityStmt);
  		case CMD_UNKNOWN:
  		case CMD_NOTHING:
  			/* probably shouldn't get here */
  			break;
  	}
  	return false;				/* default */
  }
  #endif
  
  
  /*
   * UtilityContainsQuery
   *		Return the contained Query, or NULL if there is none
   *
   * Certain utility statements, such as EXPLAIN, contain a plannable Query.
   * This function encapsulates knowledge of exactly which ones do.
   * We assume it is invoked only on already-parse-analyzed statements
   * (else the contained parsetree isn't a Query yet).
   *
   * In some cases (currently, only EXPLAIN of CREATE TABLE AS/SELECT INTO and
   * CREATE MATERIALIZED VIEW), potentially Query-containing utility statements
   * can be nested.  This function will drill down to a non-utility Query, or
   * return NULL if none.
   */
  Query *
  UtilityContainsQuery(Node *parsetree)
  {
  	Query	   *qry;
  
  	switch (nodeTag(parsetree))
  	{
  		case T_DeclareCursorStmt:
  			qry = castNode(Query, ((DeclareCursorStmt *) parsetree)->query);
  			if (qry->commandType == CMD_UTILITY)
  				return UtilityContainsQuery(qry->utilityStmt);
  			return qry;
  
  		case T_ExplainStmt:
  			qry = castNode(Query, ((ExplainStmt *) parsetree)->query);
  			if (qry->commandType == CMD_UTILITY)
  				return UtilityContainsQuery(qry->utilityStmt);
  			return qry;
  
  		case T_CreateTableAsStmt:
  			qry = castNode(Query, ((CreateTableAsStmt *) parsetree)->query);
  			if (qry->commandType == CMD_UTILITY)
  				return UtilityContainsQuery(qry->utilityStmt);
  			return qry;
  
  		default:
  			return NULL;
  	}
  }
  
  
  /*
   * AlterObjectTypeCommandTag
   *		helper function for CreateCommandTag
   *
   * This covers most cases where ALTER is used with an ObjectType enum.
   */
  static const char *
  AlterObjectTypeCommandTag(ObjectType objtype)
  {
  	const char *tag;
  
  	switch (objtype)
  	{
  		case OBJECT_AGGREGATE:
  			tag = "ALTER AGGREGATE";
  			break;
  		case OBJECT_ATTRIBUTE:
  			tag = "ALTER TYPE";
  			break;
  		case OBJECT_CAST:
  			tag = "ALTER CAST";
  			break;
  		case OBJECT_COLLATION:
  			tag = "ALTER COLLATION";
  			break;
  		case OBJECT_COLUMN:
  			tag = "ALTER TABLE";
  			break;
  		case OBJECT_CONVERSION:
  			tag = "ALTER CONVERSION";
  			break;
  		case OBJECT_DATABASE:
  			tag = "ALTER DATABASE";
  			break;
  		case OBJECT_DOMAIN:
  		case OBJECT_DOMCONSTRAINT:
  			tag = "ALTER DOMAIN";
  			break;
  		case OBJECT_EXTENSION:
  			tag = "ALTER EXTENSION";
  			break;
  		case OBJECT_FDW:
  			tag = "ALTER FOREIGN DATA WRAPPER";
  			break;
  		case OBJECT_FOREIGN_SERVER:
  			tag = "ALTER SERVER";
  			break;
  		case OBJECT_FOREIGN_TABLE:
  			tag = "ALTER FOREIGN TABLE";
  			break;
  		case OBJECT_FUNCTION:
  			tag = "ALTER FUNCTION";
  			break;
  		case OBJECT_INDEX:
  			tag = "ALTER INDEX";
  			break;
  		case OBJECT_LANGUAGE:
  			tag = "ALTER LANGUAGE";
  			break;
  		case OBJECT_LARGEOBJECT:
  			tag = "ALTER LARGE OBJECT";
  			break;
  		case OBJECT_OPCLASS:
  			tag = "ALTER OPERATOR CLASS";
  			break;
  		case OBJECT_OPERATOR:
  			tag = "ALTER OPERATOR";
  			break;
  		case OBJECT_OPFAMILY:
  			tag = "ALTER OPERATOR FAMILY";
  			break;
  		case OBJECT_POLICY:
  			tag = "ALTER POLICY";
  			break;
  		case OBJECT_PROCEDURE:
  			tag = "ALTER PROCEDURE";
  			break;
  		case OBJECT_ROLE:
  			tag = "ALTER ROLE";
  			break;
  		case OBJECT_ROUTINE:
  			tag = "ALTER ROUTINE";
  			break;
  		case OBJECT_RULE:
  			tag = "ALTER RULE";
  			break;
  		case OBJECT_SCHEMA:
  			tag = "ALTER SCHEMA";
  			break;
  		case OBJECT_SEQUENCE:
  			tag = "ALTER SEQUENCE";
  			break;
  		case OBJECT_TABLE:
  		case OBJECT_TABCONSTRAINT:
  			tag = "ALTER TABLE";
  			break;
  		case OBJECT_TABLEGROUP:
  			tag = "ALTER TABLEGROUP";
  			break;
  		case OBJECT_TABLESPACE:
  			tag = "ALTER TABLESPACE";
  			break;
  		case OBJECT_TRIGGER:
  			tag = "ALTER TRIGGER";
  			break;
  		case OBJECT_EVENT_TRIGGER:
  			tag = "ALTER EVENT TRIGGER";
  			break;
  		case OBJECT_TSCONFIGURATION:
  			tag = "ALTER TEXT SEARCH CONFIGURATION";
  			break;
  		case OBJECT_TSDICTIONARY:
  			tag = "ALTER TEXT SEARCH DICTIONARY";
  			break;
  		case OBJECT_TSPARSER:
  			tag = "ALTER TEXT SEARCH PARSER";
  			break;
  		case OBJECT_TSTEMPLATE:
  			tag = "ALTER TEXT SEARCH TEMPLATE";
  			break;
  		case OBJECT_TYPE:
  			tag = "ALTER TYPE";
  			break;
  		case OBJECT_VIEW:
  			tag = "ALTER VIEW";
  			break;
  		case OBJECT_MATVIEW:
  			tag = "ALTER MATERIALIZED VIEW";
  			break;
  		case OBJECT_PUBLICATION:
  			tag = "ALTER PUBLICATION";
  			break;
  		case OBJECT_SUBSCRIPTION:
  			tag = "ALTER SUBSCRIPTION";
  			break;
  		case OBJECT_STATISTIC_EXT:
  			tag = "ALTER STATISTICS";
  			break;
  		default:
  			tag = "???";
  			break;
  	}
  
  	return tag;
  }
  
  /*
   * CreateCommandTag
   *		utility to get a string representation of the command operation,
   *		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.
   *
   * NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE.
   * Also, the result must point at a true constant (permanent storage).
   */
  const char *
  CreateCommandTag(Node *parsetree)
  {
  	const char *tag;
  
  	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";
  			break;
  
  		case T_DeleteStmt:
  			tag = "DELETE";
  			break;
  
  		case T_UpdateStmt:
  			tag = "UPDATE";
  			break;
  
  		case T_SelectStmt:
  			tag = "SELECT";
  			break;
  
  			/* utility statements --- same whether raw or cooked */
  		case T_TransactionStmt:
  			{
  				TransactionStmt *stmt = (TransactionStmt *) parsetree;
  
  				switch (stmt->kind)
  				{
  					case TRANS_STMT_BEGIN:
  						tag = "BEGIN";
  						break;
  
  					case TRANS_STMT_START:
  						tag = "START TRANSACTION";
  						break;
  
  					case TRANS_STMT_COMMIT:
  						tag = "COMMIT";
  						break;
  
  					case TRANS_STMT_ROLLBACK:
  					case TRANS_STMT_ROLLBACK_TO:
  						tag = "ROLLBACK";
  						break;
  
  					case TRANS_STMT_SAVEPOINT:
  						tag = "SAVEPOINT";
  						break;
  
  					case TRANS_STMT_RELEASE:
  						tag = "RELEASE";
  						break;
  
  					case TRANS_STMT_PREPARE:
  						tag = "PREPARE TRANSACTION";
  						break;
  
  					case TRANS_STMT_COMMIT_PREPARED:
  						tag = "COMMIT PREPARED";
  						break;
  
  					case TRANS_STMT_ROLLBACK_PREPARED:
  						tag = "ROLLBACK PREPARED";
  						break;
  
  					default:
  						tag = "???";
  						break;
  				}
  			}
  			break;
  
  		case T_DeclareCursorStmt:
  			tag = "DECLARE CURSOR";
  			break;
  
  		case T_ClosePortalStmt:
  			{
  				ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
  
  				if (stmt->portalname == NULL)
  					tag = "CLOSE CURSOR ALL";
  				else
  					tag = "CLOSE CURSOR";
  			}
  			break;
  
  		case T_FetchStmt:
  			{
  				FetchStmt  *stmt = (FetchStmt *) parsetree;
  
  				tag = (stmt->ismove) ? "MOVE" : "FETCH";
  			}
  			break;
  
  		case T_CreateDomainStmt:
  			tag = "CREATE DOMAIN";
  			break;
  
  		case T_CreateSchemaStmt:
  			tag = "CREATE SCHEMA";
  			break;
  
  		case T_CreateStmt:
  			tag = "CREATE TABLE";
  			break;
  
  		case T_CreateTableGroupStmt:
  			tag = "CREATE TABLEGROUP";
  			break;
  
  		case T_DropTableGroupStmt:
  			tag = "DROP TABLEGROUP";
  			break;
  
  		case T_CreateTableSpaceStmt:
  			tag = "CREATE TABLESPACE";
  			break;
  
  		case T_DropTableSpaceStmt:
  			tag = "DROP TABLESPACE";
  			break;
  
  		case T_AlterTableSpaceOptionsStmt:
  			tag = "ALTER TABLESPACE";
  			break;
  
  		case T_CreateExtensionStmt:
  			tag = "CREATE EXTENSION";
  			break;
  
  		case T_AlterExtensionStmt:
  			tag = "ALTER EXTENSION";
  			break;
  
  		case T_AlterExtensionContentsStmt:
  			tag = "ALTER EXTENSION";
  			break;
  
  		case T_CreateFdwStmt:
  			tag = "CREATE FOREIGN DATA WRAPPER";
  			break;
  
  		case T_AlterFdwStmt:
  			tag = "ALTER FOREIGN DATA WRAPPER";
  			break;
  
  		case T_CreateForeignServerStmt:
  			tag = "CREATE SERVER";
  			break;
  
  		case T_AlterForeignServerStmt:
  			tag = "ALTER SERVER";
  			break;
  
  		case T_CreateUserMappingStmt:
  			tag = "CREATE USER MAPPING";
  			break;
  
  		case T_AlterUserMappingStmt:
  			tag = "ALTER USER MAPPING";
  			break;
  
  		case T_DropUserMappingStmt:
  			tag = "DROP USER MAPPING";
  			break;
  
  		case T_CreateForeignTableStmt:
  			tag = "CREATE FOREIGN TABLE";
  			break;
  
  		case T_ImportForeignSchemaStmt:
  			tag = "IMPORT FOREIGN SCHEMA";
  			break;
  
  		case T_DropStmt:
  			switch (((DropStmt *) parsetree)->removeType)
  			{
  				case OBJECT_TABLE:
  					tag = "DROP TABLE";
  					break;
  				case OBJECT_SEQUENCE:
  					tag = "DROP SEQUENCE";
  					break;
  				case OBJECT_VIEW:
  					tag = "DROP VIEW";
  					break;
  				case OBJECT_MATVIEW:
  					tag = "DROP MATERIALIZED VIEW";
  					break;
  				case OBJECT_INDEX:
  					tag = "DROP INDEX";
  					break;
  				case OBJECT_TYPE:
  					tag = "DROP TYPE";
  					break;
  				case OBJECT_DOMAIN:
  					tag = "DROP DOMAIN";
  					break;
  				case OBJECT_COLLATION:
  					tag = "DROP COLLATION";
  					break;
  				case OBJECT_CONVERSION:
  					tag = "DROP CONVERSION";
  					break;
  				case OBJECT_SCHEMA:
  					tag = "DROP SCHEMA";
  					break;
  				case OBJECT_TSPARSER:
  					tag = "DROP TEXT SEARCH PARSER";
  					break;
  				case OBJECT_TSDICTIONARY:
  					tag = "DROP TEXT SEARCH DICTIONARY";
  					break;
  				case OBJECT_TSTEMPLATE:
  					tag = "DROP TEXT SEARCH TEMPLATE";
  					break;
  				case OBJECT_TSCONFIGURATION:
  					tag = "DROP TEXT SEARCH CONFIGURATION";
  					break;
  				case OBJECT_FOREIGN_TABLE:
  					tag = "DROP FOREIGN TABLE";
  					break;
  				case OBJECT_EXTENSION:
  					tag = "DROP EXTENSION";
  					break;
  				case OBJECT_FUNCTION:
  					tag = "DROP FUNCTION";
  					break;
  				case OBJECT_PROCEDURE:
  					tag = "DROP PROCEDURE";
  					break;
  				case OBJECT_ROUTINE:
  					tag = "DROP ROUTINE";
  					break;
  				case OBJECT_AGGREGATE:
  					tag = "DROP AGGREGATE";
  					break;
  				case OBJECT_OPERATOR:
  					tag = "DROP OPERATOR";
  					break;
  				case OBJECT_LANGUAGE:
  					tag = "DROP LANGUAGE";
  					break;
  				case OBJECT_CAST:
  					tag = "DROP CAST";
  					break;
  				case OBJECT_TRIGGER:
  					tag = "DROP TRIGGER";
  					break;
  				case OBJECT_EVENT_TRIGGER:
  					tag = "DROP EVENT TRIGGER";
  					break;
  				case OBJECT_RULE:
  					tag = "DROP RULE";
  					break;
  				case OBJECT_FDW:
  					tag = "DROP FOREIGN DATA WRAPPER";
  					break;
  				case OBJECT_FOREIGN_SERVER:
  					tag = "DROP SERVER";
  					break;
  				case OBJECT_OPCLASS:
  					tag = "DROP OPERATOR CLASS";
  					break;
  				case OBJECT_OPFAMILY:
  					tag = "DROP OPERATOR FAMILY";
  					break;
  				case OBJECT_POLICY:
  					tag = "DROP POLICY";
  					break;
  				case OBJECT_TRANSFORM:
  					tag = "DROP TRANSFORM";
  					break;
  				case OBJECT_ACCESS_METHOD:
  					tag = "DROP ACCESS METHOD";
  					break;
  				case OBJECT_PUBLICATION:
  					tag = "DROP PUBLICATION";
  					break;
  				case OBJECT_STATISTIC_EXT:
  					tag = "DROP STATISTICS";
  					break;
  				default:
  					tag = "???";
  			}
  			break;
  
  		case T_TruncateStmt:
  			tag = "TRUNCATE TABLE";
  			break;
  
  		case T_CommentStmt:
  			tag = "COMMENT";
  			break;
  
  		case T_SecLabelStmt:
  			tag = "SECURITY LABEL";
  			break;
  
  		case T_CopyStmt:
  			tag = "COPY";
  			break;
  
  		case T_RenameStmt:
  			tag = AlterObjectTypeCommandTag(((RenameStmt *) parsetree)->renameType);
  			break;
  
  		case T_AlterObjectDependsStmt:
  			tag = AlterObjectTypeCommandTag(((AlterObjectDependsStmt *) parsetree)->objectType);
  			break;
  
  		case T_AlterObjectSchemaStmt:
  			tag = AlterObjectTypeCommandTag(((AlterObjectSchemaStmt *) parsetree)->objectType);
  			break;
  
  		case T_AlterOwnerStmt:
  			tag = AlterObjectTypeCommandTag(((AlterOwnerStmt *) parsetree)->objectType);
  			break;
  
  		case T_AlterTableMoveAllStmt:
  			tag = AlterObjectTypeCommandTag(((AlterTableMoveAllStmt *) parsetree)->objtype);
  			break;
  
  		case T_AlterTableStmt:
  			tag = AlterObjectTypeCommandTag(((AlterTableStmt *) parsetree)->relkind);
  			break;
  
  		case T_AlterDomainStmt:
  			tag = "ALTER DOMAIN";
  			break;
  
  		case T_AlterFunctionStmt:
  			switch (((AlterFunctionStmt *) parsetree)->objtype)
  			{
  				case OBJECT_FUNCTION:
  					tag = "ALTER FUNCTION";
  					break;
  				case OBJECT_PROCEDURE:
  					tag = "ALTER PROCEDURE";
  					break;
  				case OBJECT_ROUTINE:
  					tag = "ALTER ROUTINE";
  					break;
  				default:
  					tag = "???";
  			}
  			break;
  
  		case T_GrantStmt:
  			{
  				GrantStmt  *stmt = (GrantStmt *) parsetree;
  
  				tag = (stmt->is_grant) ? "GRANT" : "REVOKE";
  			}
  			break;
  
  		case T_GrantRoleStmt:
  			{
  				GrantRoleStmt *stmt = (GrantRoleStmt *) parsetree;
  
  				tag = (stmt->is_grant) ? "GRANT ROLE" : "REVOKE ROLE";
  			}
  			break;
  
  		case T_AlterDefaultPrivilegesStmt:
  			tag = "ALTER DEFAULT PRIVILEGES";
  			break;
  
  		case T_DefineStmt:
  			switch (((DefineStmt *) parsetree)->kind)
  			{
  				case OBJECT_AGGREGATE:
  					tag = "CREATE AGGREGATE";
  					break;
  				case OBJECT_OPERATOR:
  					tag = "CREATE OPERATOR";
  					break;
  				case OBJECT_TYPE:
  					tag = "CREATE TYPE";
  					break;
  				case OBJECT_TSPARSER:
  					tag = "CREATE TEXT SEARCH PARSER";
  					break;
  				case OBJECT_TSDICTIONARY:
  					tag = "CREATE TEXT SEARCH DICTIONARY";
  					break;
  				case OBJECT_TSTEMPLATE:
  					tag = "CREATE TEXT SEARCH TEMPLATE";
  					break;
  				case OBJECT_TSCONFIGURATION:
  					tag = "CREATE TEXT SEARCH CONFIGURATION";
  					break;
  				case OBJECT_COLLATION:
  					tag = "CREATE COLLATION";
  					break;
  				case OBJECT_ACCESS_METHOD:
  					tag = "CREATE ACCESS METHOD";
  					break;
  				default:
  					tag = "???";
  			}
  			break;
  
  		case T_CompositeTypeStmt:
  			tag = "CREATE TYPE";
  			break;
  
  		case T_CreateEnumStmt:
  			tag = "CREATE TYPE";
  			break;
  
  		case T_CreateRangeStmt:
  			tag = "CREATE TYPE";
  			break;
  
  		case T_AlterEnumStmt:
  			tag = "ALTER TYPE";
  			break;
  
  		case T_ViewStmt:
  			tag = "CREATE VIEW";
  			break;
  
  		case T_CreateFunctionStmt:
  			if (((CreateFunctionStmt *) parsetree)->is_procedure)
  				tag = "CREATE PROCEDURE";
  			else
  				tag = "CREATE FUNCTION";
  			break;
  
  		case T_IndexStmt:
  			tag = "CREATE INDEX";
  			break;
  
  		case T_RuleStmt:
  			tag = "CREATE RULE";
  			break;
  
  		case T_CreateSeqStmt:
  			tag = "CREATE SEQUENCE";
  			break;
  
  		case T_AlterSeqStmt:
  			tag = "ALTER SEQUENCE";
  			break;
  
  		case T_DoStmt:
  			tag = "DO";
  			break;
  
  		case T_CreatedbStmt:
  			tag = "CREATE DATABASE";
  			break;
  
  		case T_AlterDatabaseStmt:
  			tag = "ALTER DATABASE";
  			break;
  
  		case T_AlterDatabaseSetStmt:
  			tag = "ALTER DATABASE";
  			break;
  
  		case T_DropdbStmt:
  			tag = "DROP DATABASE";
  			break;
  
  		case T_NotifyStmt:
  			tag = "NOTIFY";
  			break;
  
  		case T_ListenStmt:
  			tag = "LISTEN";
  			break;
  
  		case T_UnlistenStmt:
  			tag = "UNLISTEN";
  			break;
  
  		case T_LoadStmt:
  			tag = "LOAD";
  			break;
  
  		case T_CallStmt:
  			tag = "CALL";
  			break;
  
  		case T_ClusterStmt:
  			tag = "CLUSTER";
  			break;
  
  		case T_VacuumStmt:
  			if (((VacuumStmt *) parsetree)->options & VACOPT_VACUUM)
  				tag = "VACUUM";
  			else
  				tag = "ANALYZE";
  			break;
  
  		case T_ExplainStmt:
  			tag = "EXPLAIN";
  			break;
  
  		case T_CreateTableAsStmt:
  			switch (((CreateTableAsStmt *) parsetree)->relkind)
  			{
  				case OBJECT_TABLE:
  					if (((CreateTableAsStmt *) parsetree)->is_select_into)
  						tag = "SELECT INTO";
  					else
  						tag = "CREATE TABLE AS";
  					break;
  				case OBJECT_MATVIEW:
  					tag = "CREATE MATERIALIZED VIEW";
  					break;
  				default:
  					tag = "???";
  			}
  			break;
  
  		case T_RefreshMatViewStmt:
  			tag = "REFRESH MATERIALIZED VIEW";
  			break;
  
  		case T_AlterSystemStmt:
  			tag = "ALTER SYSTEM";
  			break;
  
  		case T_VariableSetStmt:
  			switch (((VariableSetStmt *) parsetree)->kind)
  			{
  				case VAR_SET_VALUE:
  				case VAR_SET_CURRENT:
  				case VAR_SET_DEFAULT:
  				case VAR_SET_MULTI:
  					tag = "SET";
  					break;
  				case VAR_RESET:
  				case VAR_RESET_ALL:
  					tag = "RESET";
  					break;
  				default:
  					tag = "???";
  			}
  			break;
  
  		case T_VariableShowStmt:
  			tag = "SHOW";
  			break;
  
  		case T_DiscardStmt:
  			switch (((DiscardStmt *) parsetree)->target)
  			{
  				case DISCARD_ALL:
  					tag = "DISCARD ALL";
  					break;
  				case DISCARD_PLANS:
  					tag = "DISCARD PLANS";
  					break;
  				case DISCARD_TEMP:
  					tag = "DISCARD TEMP";
  					break;
  				case DISCARD_SEQUENCES:
  					tag = "DISCARD SEQUENCES";
  					break;
  				default:
  					tag = "???";
  			}
  			break;
  
  		case T_CreateTransformStmt:
  			tag = "CREATE TRANSFORM";
  			break;
  
  		case T_CreateTrigStmt:
  			tag = "CREATE TRIGGER";
  			break;
  
  		case T_CreateEventTrigStmt:
  			tag = "CREATE EVENT TRIGGER";
  			break;
  
  		case T_AlterEventTrigStmt:
  			tag = "ALTER EVENT TRIGGER";
  			break;
  
  		case T_CreatePLangStmt:
  			tag = "CREATE LANGUAGE";
  			break;
  
  		case T_CreateRoleStmt:
  			tag = "CREATE ROLE";
  			break;
  
  		case T_AlterRoleStmt:
  			tag = "ALTER ROLE";
  			break;
  
  		case T_AlterRoleSetStmt:
  			tag = "ALTER ROLE";
  			break;
  
  		case T_DropRoleStmt:
  			tag = "DROP ROLE";
  			break;
  
  		case T_DropOwnedStmt:
  			tag = "DROP OWNED";
  			break;
  
  		case T_ReassignOwnedStmt:
  			tag = "REASSIGN OWNED";
  			break;
  
  		case T_LockStmt:
  			tag = "LOCK TABLE";
  			break;
  
  		case T_ConstraintsSetStmt:
  			tag = "SET CONSTRAINTS";
  			break;
  
  		case T_CheckPointStmt:
  			tag = "CHECKPOINT";
  			break;
  
  		case T_ReindexStmt:
  			tag = "REINDEX";
  			break;
  
  		case T_BackfillIndexStmt:
  			tag = "BACKFILL INDEX";
  			break;
  
  		case T_CreateConversionStmt:
  			tag = "CREATE CONVERSION";
  			break;
  
  		case T_CreateCastStmt:
  			tag = "CREATE CAST";
  			break;
  
  		case T_CreateOpClassStmt:
  			tag = "CREATE OPERATOR CLASS";
  			break;
  
  		case T_CreateOpFamilyStmt:
  			tag = "CREATE OPERATOR FAMILY";
  			break;
  
  		case T_AlterOpFamilyStmt:
  			tag = "ALTER OPERATOR FAMILY";
  			break;
  
  		case T_AlterOperatorStmt:
  			tag = "ALTER OPERATOR";
  			break;
  
  		case T_AlterTSDictionaryStmt:
  			tag = "ALTER TEXT SEARCH DICTIONARY";
  			break;
  
  		case T_AlterTSConfigurationStmt:
  			tag = "ALTER TEXT SEARCH CONFIGURATION";
  			break;
  
  		case T_CreatePolicyStmt:
  			tag = "CREATE POLICY";
  			break;
  
  		case T_AlterPolicyStmt:
  			tag = "ALTER POLICY";
  			break;
  
  		case T_CreateAmStmt:
  			tag = "CREATE ACCESS METHOD";
  			break;
  
  		case T_CreatePublicationStmt:
  			tag = "CREATE PUBLICATION";
  			break;
  
  		case T_AlterPublicationStmt:
  			tag = "ALTER PUBLICATION";
  			break;
  
  		case T_CreateSubscriptionStmt:
  			tag = "CREATE SUBSCRIPTION";
  			break;
  
  		case T_AlterSubscriptionStmt:
  			tag = "ALTER SUBSCRIPTION";
  			break;
  
  		case T_DropSubscriptionStmt:
  			tag = "DROP SUBSCRIPTION";
  			break;
  
  		case T_AlterCollationStmt:
  			tag = "ALTER COLLATION";
  			break;
  
  		case T_PrepareStmt:
  			tag = "PREPARE";
  			break;
  
  		case T_ExecuteStmt:
  			tag = "EXECUTE";
  			break;
  
  		case T_CreateStatsStmt:
  			tag = "CREATE STATISTICS";
  			break;
  
  		case T_DeallocateStmt:
  			{
  				DeallocateStmt *stmt = (DeallocateStmt *) parsetree;
  
  				if (stmt->name == NULL)
  					tag = "DEALLOCATE ALL";
  				else
  					tag = "DEALLOCATE";
  			}
  			break;
  
  			/* already-planned queries */
  		case T_PlannedStmt:
  			{
  				PlannedStmt *stmt = (PlannedStmt *) parsetree;
  
  				switch (stmt->commandType)
  				{
  					case CMD_SELECT:
  
  						/*
  						 * We take a little extra care here so that the result
  						 * will be useful for complaints about read-only
  						 * statements
  						 */
  						if (stmt->rowMarks != NIL)
  						{
  							/* not 100% but probably close enough */
  							switch (((PlanRowMark *) linitial(stmt->rowMarks))->strength)
  							{
  								case LCS_FORKEYSHARE:
  									tag = "SELECT FOR KEY SHARE";
  									break;
  								case LCS_FORSHARE:
  									tag = "SELECT FOR SHARE";
  									break;
  								case LCS_FORNOKEYUPDATE:
  									tag = "SELECT FOR NO KEY UPDATE";
  									break;
  								case LCS_FORUPDATE:
  									tag = "SELECT FOR UPDATE";
  									break;
  								default:
  									tag = "SELECT";
  									break;
  							}
  						}
  						else
  							tag = "SELECT";
  						break;
  					case CMD_UPDATE:
  						tag = "UPDATE";
  						break;
  					case CMD_INSERT:
  						tag = "INSERT";
  						break;
  					case CMD_DELETE:
  						tag = "DELETE";
  						break;
  					case CMD_UTILITY:
  						tag = CreateCommandTag(stmt->utilityStmt);
  						break;
  					default:
  						elog(WARNING, "unrecognized commandType: %d",
  							 (int) stmt->commandType);
  						tag = "???";
  						break;
  				}
  			}
  			break;
  
  			/* parsed-and-rewritten-but-not-planned queries */
  		case T_Query:
  			{
  				Query	   *stmt = (Query *) parsetree;
  
  				switch (stmt->commandType)
  				{
  					case CMD_SELECT:
  
  						/*
  						 * We take a little extra care here so that the result
  						 * will be useful for complaints about read-only
  						 * statements
  						 */
  						if (stmt->rowMarks != NIL)
  						{
  							/* not 100% but probably close enough */
  							switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength)
  							{
  								case LCS_FORKEYSHARE:
  									tag = "SELECT FOR KEY SHARE";
  									break;
  								case LCS_FORSHARE:
  									tag = "SELECT FOR SHARE";
  									break;
  								case LCS_FORNOKEYUPDATE:
  									tag = "SELECT FOR NO KEY UPDATE";
  									break;
  								case LCS_FORUPDATE:
  									tag = "SELECT FOR UPDATE";
  									break;
  								default:
  									tag = "???";
  									break;
  							}
  						}
  						else
  							tag = "SELECT";
  						break;
  					case CMD_UPDATE:
  						tag = "UPDATE";
  						break;
  					case CMD_INSERT:
  						tag = "INSERT";
  						break;
  					case CMD_DELETE:
  						tag = "DELETE";
  						break;
  					case CMD_UTILITY:
  						tag = CreateCommandTag(stmt->utilityStmt);
  						break;
  					default:
  						elog(WARNING, "unrecognized commandType: %d",
  							 (int) stmt->commandType);
  						tag = "???";
  						break;
  				}
  			}
  			break;
  
  		default:
  			elog(WARNING, "unrecognized node type: %d",
  				 (int) nodeTag(parsetree));
  			tag = "???";
  			break;
  	}
  
  	return tag;
  }
  
  
  /*
   * GetCommandLogLevel
   *		utility to get the minimum log_statement level for a command,
   *		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.
   */
  LogStmtLevel
  GetCommandLogLevel(Node *parsetree)
  {
  	LogStmtLevel lev;
  
  	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:
  		case T_UpdateStmt:
  			lev = LOGSTMT_MOD;
  			break;
  
  		case T_SelectStmt:
  			if (((SelectStmt *) parsetree)->intoClause)
  				lev = LOGSTMT_DDL;	/* SELECT INTO */
  			else
  				lev = LOGSTMT_ALL;
  			break;
  
  			/* utility statements --- same whether raw or cooked */
  		case T_TransactionStmt:
  			lev = LOGSTMT_ALL;
  			break;
  
  		case T_DeclareCursorStmt:
  			lev = LOGSTMT_ALL;
  			break;
  
  		case T_ClosePortalStmt:
  			lev = LOGSTMT_ALL;
  			break;
  
  		case T_FetchStmt:
  			lev = LOGSTMT_ALL;
  			break;
  
  		case T_CreateSchemaStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreateStmt:
  		case T_CreateForeignTableStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreateTableSpaceStmt:
  		case T_DropTableSpaceStmt:
  		case T_AlterTableSpaceOptionsStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreateExtensionStmt:
  		case T_AlterExtensionStmt:
  		case T_AlterExtensionContentsStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreateFdwStmt:
  		case T_AlterFdwStmt:
  		case T_CreateForeignServerStmt:
  		case T_AlterForeignServerStmt:
  		case T_CreateUserMappingStmt:
  		case T_AlterUserMappingStmt:
  		case T_DropUserMappingStmt:
  		case T_ImportForeignSchemaStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_DropStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_TruncateStmt:
  			lev = LOGSTMT_MOD;
  			break;
  
  		case T_CommentStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_SecLabelStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CopyStmt:
  			if (((CopyStmt *) parsetree)->is_from)
  				lev = LOGSTMT_MOD;
  			else
  				lev = LOGSTMT_ALL;
  			break;
  
  		case T_PrepareStmt:
  			{
  				PrepareStmt *stmt = (PrepareStmt *) parsetree;
  
  				/* Look through a PREPARE to the contained stmt */
  				lev = GetCommandLogLevel(stmt->query);
  			}
  			break;
  
  		case T_ExecuteStmt:
  			{
  				ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
  				PreparedStatement *ps;
  
  				/* 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->stmt);
  				else
  					lev = LOGSTMT_ALL;
  			}
  			break;
  
  		case T_DeallocateStmt:
  			lev = LOGSTMT_ALL;
  			break;
  
  		case T_RenameStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterObjectDependsStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterObjectSchemaStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterOwnerStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterOperatorStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterTableMoveAllStmt:
  		case T_AlterTableStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterDomainStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_GrantStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_GrantRoleStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterDefaultPrivilegesStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_DefineStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CompositeTypeStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreateEnumStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreateRangeStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterEnumStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_ViewStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreateFunctionStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterFunctionStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_IndexStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_RuleStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreateSeqStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterSeqStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_DoStmt:
  			lev = LOGSTMT_ALL;
  			break;
  
  		case T_CreatedbStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterDatabaseStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterDatabaseSetStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_DropdbStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_NotifyStmt:
  			lev = LOGSTMT_ALL;
  			break;
  
  		case T_ListenStmt:
  			lev = LOGSTMT_ALL;
  			break;
  
  		case T_UnlistenStmt:
  			lev = LOGSTMT_ALL;
  			break;
  
  		case T_LoadStmt:
  			lev = LOGSTMT_ALL;
  			break;
  
  		case T_CallStmt:
  			lev = LOGSTMT_ALL;
  			break;
  
  		case T_ClusterStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_VacuumStmt:
  			lev = LOGSTMT_ALL;
  			break;
  
  		case T_ExplainStmt:
  			{
  				ExplainStmt *stmt = (ExplainStmt *) parsetree;
  				bool		analyze = false;
  				ListCell   *lc;
  
  				/* Look through an EXPLAIN ANALYZE to the contained stmt */
  				foreach(lc, stmt->options)
  				{
  					DefElem    *opt = (DefElem *) lfirst(lc);
  
  					if (strcmp(opt->defname, "analyze") == 0)
  						analyze = defGetBoolean(opt);
  					/* don't "break", as explain.c will use the last value */
  				}
  				if (analyze)
  					return GetCommandLogLevel(stmt->query);
  
  				/* Plain EXPLAIN isn't so interesting */
  				lev = LOGSTMT_ALL;
  			}
  			break;
  
  		case T_CreateTableAsStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_RefreshMatViewStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterSystemStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_VariableSetStmt:
  			lev = LOGSTMT_ALL;
  			break;
  
  		case T_VariableShowStmt:
  			lev = LOGSTMT_ALL;
  			break;
  
  		case T_DiscardStmt:
  			lev = LOGSTMT_ALL;
  			break;
  
  		case T_CreateTrigStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreateEventTrigStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterEventTrigStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreatePLangStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreateDomainStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreateRoleStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterRoleStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterRoleSetStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_DropRoleStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_DropOwnedStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_ReassignOwnedStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_LockStmt:
  			lev = LOGSTMT_ALL;
  			break;
  
  		case T_ConstraintsSetStmt:
  			lev = LOGSTMT_ALL;
  			break;
  
  		case T_CheckPointStmt:
  			lev = LOGSTMT_ALL;
  			break;
  
  		case T_ReindexStmt:
  			lev = LOGSTMT_ALL;	/* should this be DDL? */
  			break;
  
  		case T_BackfillIndexStmt:
  			lev = LOGSTMT_ALL;	/* should this be DDL? */
  			break;
  
  		case T_CreateConversionStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreateCastStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreateOpClassStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreateOpFamilyStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreateTransformStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterOpFamilyStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreatePolicyStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterPolicyStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterTSDictionaryStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterTSConfigurationStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreateAmStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreatePublicationStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterPublicationStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreateSubscriptionStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterSubscriptionStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_DropSubscriptionStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_CreateStatsStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_AlterCollationStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  			/* already-planned queries */
  		case T_PlannedStmt:
  			{
  				PlannedStmt *stmt = (PlannedStmt *) parsetree;
  
  				switch (stmt->commandType)
  				{
  					case CMD_SELECT:
  						lev = LOGSTMT_ALL;
  						break;
  
  					case CMD_UPDATE:
  					case CMD_INSERT:
  					case CMD_DELETE:
  						lev = LOGSTMT_MOD;
  						break;
  
  					case CMD_UTILITY:
  						lev = GetCommandLogLevel(stmt->utilityStmt);
  						break;
  
  					default:
  						elog(WARNING, "unrecognized commandType: %d",
  							 (int) stmt->commandType);
  						lev = LOGSTMT_ALL;
  						break;
  				}
  			}
  			break;
  
  			/* parsed-and-rewritten-but-not-planned queries */
  		case T_Query:
  			{
  				Query	   *stmt = (Query *) parsetree;
  
  				switch (stmt->commandType)
  				{
  					case CMD_SELECT:
  						lev = LOGSTMT_ALL;
  						break;
  
  					case CMD_UPDATE:
  					case CMD_INSERT:
  					case CMD_DELETE:
  						lev = LOGSTMT_MOD;
  						break;
  
  					case CMD_UTILITY:
  						lev = GetCommandLogLevel(stmt->utilityStmt);
  						break;
  
  					default:
  						elog(WARNING, "unrecognized commandType: %d",
  							 (int) stmt->commandType);
  						lev = LOGSTMT_ALL;
  						break;
  				}
  
  			}
  			break;
  
  		case T_CreateTableGroupStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		case T_DropTableGroupStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
  		default:
  			elog(WARNING, "unrecognized node type: %d",
  				 (int) nodeTag(parsetree));
  			lev = LOGSTMT_ALL;
  			break;
  	}
  
  	return lev;
  }
  
  void
  YBProcessUtilityDefaultHook(PlannedStmt *pstmt,
                              const char *queryString,
                              ProcessUtilityContext context,
                              ParamListInfo params,
                              QueryEnvironment *queryEnv,
                              DestReceiver *dest,
                              char *completionTag)
  {
  	if (IsYugaByteEnabled() && !(IsA(pstmt->utilityStmt, ExecuteStmt) ||
  			IsA(pstmt->utilityStmt, PrepareStmt) || IsA(pstmt->utilityStmt, DeallocateStmt) ||
  			IsA(pstmt->utilityStmt, ExplainStmt))) {
  		YBBeginOperationsBuffering();
  		standard_ProcessUtility(pstmt, queryString, context, params, queryEnv, dest, completionTag);
  		YBEndOperationsBuffering();
    } else {
  		standard_ProcessUtility(pstmt, queryString, context, params, queryEnv, dest, completionTag);
  	}
  }
  