mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-28 21:01:48 +02:00
58b088a9b3
When an implicit operator family is created, it wasn't getting reported. Make it do so. This has always been missing. Backpatch to 10. Author: Masahiko Sawada <sawada.mshk@gmail.com> Reported-by: Leslie LEMAIRE <leslie.lemaire@developpement-durable.gouv.fr> Reviewed-by: Amit Kapila <amit.kapila16@gmail.com> Reviewed-by: Michael Paquiër <michael@paquier.xyz> Discussion: https://postgr.es/m/f74d69e151b22171e8829551b1159e77@developpement-durable.gouv.fr
3742 lines
90 KiB
C
3742 lines
90 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* 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-2021, 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/index.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/collationcmds.h"
|
|
#include "commands/comment.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/lockcmds.h"
|
|
#include "commands/matview.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/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/rel.h"
|
|
#include "utils/syscache.h"
|
|
|
|
/* Hook for plugins to get control in ProcessUtility() */
|
|
ProcessUtility_hook_type ProcessUtility_hook = NULL;
|
|
|
|
/* local function declarations */
|
|
static int ClassifyUtilityCommandAsReadOnly(Node *parsetree);
|
|
static void ProcessUtilitySlow(ParseState *pstate,
|
|
PlannedStmt *pstmt,
|
|
const char *queryString,
|
|
ProcessUtilityContext context,
|
|
ParamListInfo params,
|
|
QueryEnvironment *queryEnv,
|
|
DestReceiver *dest,
|
|
QueryCompletion *qc);
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* Determine the degree to which a utility command is read only.
|
|
*
|
|
* Note the definitions of the relevant flags in src/include/utility/tcop.h.
|
|
*/
|
|
static int
|
|
ClassifyUtilityCommandAsReadOnly(Node *parsetree)
|
|
{
|
|
switch (nodeTag(parsetree))
|
|
{
|
|
case T_AlterCollationStmt:
|
|
case T_AlterDatabaseSetStmt:
|
|
case T_AlterDatabaseStmt:
|
|
case T_AlterDefaultPrivilegesStmt:
|
|
case T_AlterDomainStmt:
|
|
case T_AlterEnumStmt:
|
|
case T_AlterEventTrigStmt:
|
|
case T_AlterExtensionContentsStmt:
|
|
case T_AlterExtensionStmt:
|
|
case T_AlterFdwStmt:
|
|
case T_AlterForeignServerStmt:
|
|
case T_AlterFunctionStmt:
|
|
case T_AlterObjectDependsStmt:
|
|
case T_AlterObjectSchemaStmt:
|
|
case T_AlterOpFamilyStmt:
|
|
case T_AlterOperatorStmt:
|
|
case T_AlterOwnerStmt:
|
|
case T_AlterPolicyStmt:
|
|
case T_AlterPublicationStmt:
|
|
case T_AlterRoleSetStmt:
|
|
case T_AlterRoleStmt:
|
|
case T_AlterSeqStmt:
|
|
case T_AlterStatsStmt:
|
|
case T_AlterSubscriptionStmt:
|
|
case T_AlterTSConfigurationStmt:
|
|
case T_AlterTSDictionaryStmt:
|
|
case T_AlterTableMoveAllStmt:
|
|
case T_AlterTableSpaceOptionsStmt:
|
|
case T_AlterTableStmt:
|
|
case T_AlterTypeStmt:
|
|
case T_AlterUserMappingStmt:
|
|
case T_CommentStmt:
|
|
case T_CompositeTypeStmt:
|
|
case T_CreateAmStmt:
|
|
case T_CreateCastStmt:
|
|
case T_CreateConversionStmt:
|
|
case T_CreateDomainStmt:
|
|
case T_CreateEnumStmt:
|
|
case T_CreateEventTrigStmt:
|
|
case T_CreateExtensionStmt:
|
|
case T_CreateFdwStmt:
|
|
case T_CreateForeignServerStmt:
|
|
case T_CreateForeignTableStmt:
|
|
case T_CreateFunctionStmt:
|
|
case T_CreateOpClassStmt:
|
|
case T_CreateOpFamilyStmt:
|
|
case T_CreatePLangStmt:
|
|
case T_CreatePolicyStmt:
|
|
case T_CreatePublicationStmt:
|
|
case T_CreateRangeStmt:
|
|
case T_CreateRoleStmt:
|
|
case T_CreateSchemaStmt:
|
|
case T_CreateSeqStmt:
|
|
case T_CreateStatsStmt:
|
|
case T_CreateStmt:
|
|
case T_CreateSubscriptionStmt:
|
|
case T_CreateTableAsStmt:
|
|
case T_CreateTableSpaceStmt:
|
|
case T_CreateTransformStmt:
|
|
case T_CreateTrigStmt:
|
|
case T_CreateUserMappingStmt:
|
|
case T_CreatedbStmt:
|
|
case T_DefineStmt:
|
|
case T_DropOwnedStmt:
|
|
case T_DropRoleStmt:
|
|
case T_DropStmt:
|
|
case T_DropSubscriptionStmt:
|
|
case T_DropTableSpaceStmt:
|
|
case T_DropUserMappingStmt:
|
|
case T_DropdbStmt:
|
|
case T_GrantRoleStmt:
|
|
case T_GrantStmt:
|
|
case T_ImportForeignSchemaStmt:
|
|
case T_IndexStmt:
|
|
case T_ReassignOwnedStmt:
|
|
case T_RefreshMatViewStmt:
|
|
case T_RenameStmt:
|
|
case T_RuleStmt:
|
|
case T_SecLabelStmt:
|
|
case T_TruncateStmt:
|
|
case T_ViewStmt:
|
|
{
|
|
/* DDL is not read-only, and neither is TRUNCATE. */
|
|
return COMMAND_IS_NOT_READ_ONLY;
|
|
}
|
|
|
|
case T_AlterSystemStmt:
|
|
{
|
|
/*
|
|
* Surprisingly, ALTER SYSTEM meets all our definitions of
|
|
* read-only: it changes nothing that affects the output of
|
|
* pg_dump, it doesn't write WAL or imperil the application of
|
|
* future WAL, and it doesn't depend on any state that needs
|
|
* to be synchronized with parallel workers.
|
|
*
|
|
* So, despite the fact that it writes to a file, it's read
|
|
* only!
|
|
*/
|
|
return COMMAND_IS_STRICTLY_READ_ONLY;
|
|
}
|
|
|
|
case T_CallStmt:
|
|
case T_DoStmt:
|
|
{
|
|
/*
|
|
* Commands inside the DO block or the called procedure might
|
|
* not be read only, but they'll be checked separately when we
|
|
* try to execute them. Here we only need to worry about the
|
|
* DO or CALL command itself.
|
|
*/
|
|
return COMMAND_IS_STRICTLY_READ_ONLY;
|
|
}
|
|
|
|
case T_CheckPointStmt:
|
|
{
|
|
/*
|
|
* You might think that this should not be permitted in
|
|
* recovery, 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.
|
|
*/
|
|
return COMMAND_IS_STRICTLY_READ_ONLY;
|
|
}
|
|
|
|
case T_ClosePortalStmt:
|
|
case T_ConstraintsSetStmt:
|
|
case T_DeallocateStmt:
|
|
case T_DeclareCursorStmt:
|
|
case T_DiscardStmt:
|
|
case T_ExecuteStmt:
|
|
case T_FetchStmt:
|
|
case T_LoadStmt:
|
|
case T_PrepareStmt:
|
|
case T_UnlistenStmt:
|
|
case T_VariableSetStmt:
|
|
{
|
|
/*
|
|
* These modify only backend-local state, so they're OK to run
|
|
* in a read-only transaction or on a standby. However, they
|
|
* are disallowed in parallel mode, because they either rely
|
|
* upon or modify backend-local state that might not be
|
|
* synchronized among cooperating backends.
|
|
*/
|
|
return COMMAND_OK_IN_RECOVERY | COMMAND_OK_IN_READ_ONLY_TXN;
|
|
}
|
|
|
|
case T_ClusterStmt:
|
|
case T_ReindexStmt:
|
|
case T_VacuumStmt:
|
|
{
|
|
/*
|
|
* These commands write WAL, so they're not strictly
|
|
* read-only, and running them in parallel workers isn't
|
|
* supported.
|
|
*
|
|
* However, they don't change the database state in a way that
|
|
* would affect pg_dump output, so it's fine to run them in a
|
|
* read-only transaction. (CLUSTER might change the order of
|
|
* rows on disk, which could affect the ordering of pg_dump
|
|
* output, but that's not semantically significant.)
|
|
*/
|
|
return COMMAND_OK_IN_READ_ONLY_TXN;
|
|
}
|
|
|
|
case T_CopyStmt:
|
|
{
|
|
CopyStmt *stmt = (CopyStmt *) parsetree;
|
|
|
|
/*
|
|
* You might think that COPY FROM is not at all read only, but
|
|
* it's OK to copy into a temporary table, because that
|
|
* wouldn't change the output of pg_dump. If the target table
|
|
* turns out to be non-temporary, DoCopy itself will call
|
|
* PreventCommandIfReadOnly.
|
|
*/
|
|
if (stmt->is_from)
|
|
return COMMAND_OK_IN_READ_ONLY_TXN;
|
|
else
|
|
return COMMAND_IS_STRICTLY_READ_ONLY;
|
|
}
|
|
|
|
case T_ExplainStmt:
|
|
case T_VariableShowStmt:
|
|
{
|
|
/*
|
|
* These commands don't modify any data and are safe to run in
|
|
* a parallel worker.
|
|
*/
|
|
return COMMAND_IS_STRICTLY_READ_ONLY;
|
|
}
|
|
|
|
case T_ListenStmt:
|
|
case T_NotifyStmt:
|
|
{
|
|
/*
|
|
* NOTIFY requires an XID assignment, so it can't be permitted
|
|
* on a standby. Perhaps LISTEN could, since without NOTIFY it
|
|
* would be OK to just do nothing, at least until promotion,
|
|
* but we currently prohibit it lest the user get the wrong
|
|
* idea.
|
|
*
|
|
* (We do allow T_UnlistenStmt on a standby, though, because
|
|
* it's a no-op.)
|
|
*/
|
|
return COMMAND_OK_IN_READ_ONLY_TXN;
|
|
}
|
|
|
|
case T_LockStmt:
|
|
{
|
|
LockStmt *stmt = (LockStmt *) parsetree;
|
|
|
|
/*
|
|
* Only weaker locker modes are allowed during recovery. The
|
|
* restrictions here must match those in
|
|
* LockAcquireExtended().
|
|
*/
|
|
if (stmt->mode > RowExclusiveLock)
|
|
return COMMAND_OK_IN_READ_ONLY_TXN;
|
|
else
|
|
return COMMAND_IS_STRICTLY_READ_ONLY;
|
|
}
|
|
|
|
case T_TransactionStmt:
|
|
{
|
|
TransactionStmt *stmt = (TransactionStmt *) parsetree;
|
|
|
|
/*
|
|
* PREPARE, COMMIT PREPARED, and ROLLBACK PREPARED all write
|
|
* WAL, so they're not read-only in the strict sense; but the
|
|
* first and third do not change pg_dump output, so they're OK
|
|
* in a read-only transactions.
|
|
*
|
|
* We also consider COMMIT PREPARED to be OK in a read-only
|
|
* transaction environment, by way of exception.
|
|
*/
|
|
switch (stmt->kind)
|
|
{
|
|
case TRANS_STMT_BEGIN:
|
|
case TRANS_STMT_START:
|
|
case TRANS_STMT_COMMIT:
|
|
case TRANS_STMT_ROLLBACK:
|
|
case TRANS_STMT_SAVEPOINT:
|
|
case TRANS_STMT_RELEASE:
|
|
case TRANS_STMT_ROLLBACK_TO:
|
|
return COMMAND_IS_STRICTLY_READ_ONLY;
|
|
|
|
case TRANS_STMT_PREPARE:
|
|
case TRANS_STMT_COMMIT_PREPARED:
|
|
case TRANS_STMT_ROLLBACK_PREPARED:
|
|
return COMMAND_OK_IN_READ_ONLY_TXN;
|
|
}
|
|
elog(ERROR, "unrecognized TransactionStmtKind: %d",
|
|
(int) stmt->kind);
|
|
return 0; /* silence stupider compilers */
|
|
}
|
|
|
|
default:
|
|
elog(ERROR, "unrecognized node type: %d",
|
|
(int) nodeTag(parsetree));
|
|
return 0; /* silence stupider compilers */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* PreventCommandIfReadOnly: throw error if XactReadOnly
|
|
*
|
|
* This is useful partly to ensure consistency of the error message wording;
|
|
* some 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 partly to ensure consistency of the error message wording;
|
|
* some 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
|
|
* readOnlyTree: if true, pstmt's node tree must not be modified
|
|
* 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
|
|
* qc: where to store command completion status data. May be NULL,
|
|
* but if not, then caller must have initialized it.
|
|
*
|
|
* 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)".
|
|
*
|
|
* 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,
|
|
bool readOnlyTree,
|
|
ProcessUtilityContext context,
|
|
ParamListInfo params,
|
|
QueryEnvironment *queryEnv,
|
|
DestReceiver *dest,
|
|
QueryCompletion *qc)
|
|
{
|
|
Assert(IsA(pstmt, PlannedStmt));
|
|
Assert(pstmt->commandType == CMD_UTILITY);
|
|
Assert(queryString != NULL); /* required as of 8.4 */
|
|
Assert(qc == NULL || qc->commandTag == CMDTAG_UNKNOWN);
|
|
|
|
/*
|
|
* 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, readOnlyTree,
|
|
context, params, queryEnv,
|
|
dest, qc);
|
|
else
|
|
standard_ProcessUtility(pstmt, queryString, readOnlyTree,
|
|
context, params, queryEnv,
|
|
dest, qc);
|
|
}
|
|
|
|
/*
|
|
* 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,
|
|
bool readOnlyTree,
|
|
ProcessUtilityContext context,
|
|
ParamListInfo params,
|
|
QueryEnvironment *queryEnv,
|
|
DestReceiver *dest,
|
|
QueryCompletion *qc)
|
|
{
|
|
Node *parsetree;
|
|
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
|
|
bool isAtomicContext = (!(context == PROCESS_UTILITY_TOPLEVEL || context == PROCESS_UTILITY_QUERY_NONATOMIC) || IsTransactionBlock());
|
|
ParseState *pstate;
|
|
int readonly_flags;
|
|
|
|
/* This can recurse, so check for excessive recursion */
|
|
check_stack_depth();
|
|
|
|
/*
|
|
* If the given node tree is read-only, make a copy to ensure that parse
|
|
* transformations don't damage the original tree. This could be
|
|
* refactored to avoid making unnecessary copies in more cases, but it's
|
|
* not clear that it's worth a great deal of trouble over. Statements
|
|
* that are complex enough to be expensive to copy are exactly the ones
|
|
* we'd need to copy, so that only marginal savings seem possible.
|
|
*/
|
|
if (readOnlyTree)
|
|
pstmt = copyObject(pstmt);
|
|
parsetree = pstmt->utilityStmt;
|
|
|
|
/* Prohibit read/write commands in read-only states. */
|
|
readonly_flags = ClassifyUtilityCommandAsReadOnly(parsetree);
|
|
if (readonly_flags != COMMAND_IS_STRICTLY_READ_ONLY &&
|
|
(XactReadOnly || IsInParallelMode()))
|
|
{
|
|
CommandTag commandtag = CreateCommandTag(parsetree);
|
|
|
|
if ((readonly_flags & COMMAND_OK_IN_READ_ONLY_TXN) == 0)
|
|
PreventCommandIfReadOnly(GetCommandTagName(commandtag));
|
|
if ((readonly_flags & COMMAND_OK_IN_PARALLEL_MODE) == 0)
|
|
PreventCommandIfParallelMode(GetCommandTagName(commandtag));
|
|
if ((readonly_flags & COMMAND_OK_IN_RECOVERY) == 0)
|
|
PreventCommandDuringRecovery(GetCommandTagName(commandtag));
|
|
}
|
|
|
|
pstate = make_parsestate(NULL);
|
|
pstate->p_sourcetext = queryString;
|
|
pstate->p_queryEnv = queryEnv;
|
|
|
|
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(stmt->chain))
|
|
{
|
|
/* report unsuccessful commit in qc */
|
|
if (qc)
|
|
SetQueryCompletion(qc, CMDTAG_ROLLBACK, 0);
|
|
}
|
|
break;
|
|
|
|
case TRANS_STMT_PREPARE:
|
|
if (!PrepareTransactionBlock(stmt->gid))
|
|
{
|
|
/* report unsuccessful commit in qc */
|
|
if (qc)
|
|
SetQueryCompletion(qc, CMDTAG_ROLLBACK, 0);
|
|
}
|
|
break;
|
|
|
|
case TRANS_STMT_COMMIT_PREPARED:
|
|
PreventInTransactionBlock(isTopLevel, "COMMIT PREPARED");
|
|
FinishPreparedTransaction(stmt->gid, true);
|
|
break;
|
|
|
|
case TRANS_STMT_ROLLBACK_PREPARED:
|
|
PreventInTransactionBlock(isTopLevel, "ROLLBACK PREPARED");
|
|
FinishPreparedTransaction(stmt->gid, false);
|
|
break;
|
|
|
|
case TRANS_STMT_ROLLBACK:
|
|
UserAbortTransactionBlock(stmt->chain);
|
|
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(pstate, (DeclareCursorStmt *) parsetree, params,
|
|
isTopLevel);
|
|
break;
|
|
|
|
case T_ClosePortalStmt:
|
|
{
|
|
ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
|
|
|
|
CheckRestrictedOperation("CLOSE");
|
|
PerformPortalClose(stmt->portalname);
|
|
}
|
|
break;
|
|
|
|
case T_FetchStmt:
|
|
PerformPortalFetch((FetchStmt *) parsetree, dest, qc);
|
|
break;
|
|
|
|
case T_DoStmt:
|
|
ExecuteDoStmt((DoStmt *) parsetree, isAtomicContext);
|
|
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 (qc)
|
|
SetQueryCompletion(qc, CMDTAG_COPY, processed);
|
|
}
|
|
break;
|
|
|
|
case T_PrepareStmt:
|
|
CheckRestrictedOperation("PREPARE");
|
|
PrepareQuery(pstate, (PrepareStmt *) parsetree,
|
|
pstmt->stmt_location, pstmt->stmt_len);
|
|
break;
|
|
|
|
case T_ExecuteStmt:
|
|
ExecuteQuery(pstate,
|
|
(ExecuteStmt *) parsetree, NULL,
|
|
params,
|
|
dest, qc);
|
|
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:
|
|
/* no event triggers for global objects */
|
|
PreventInTransactionBlock(isTopLevel, "DROP DATABASE");
|
|
DropDatabase(pstate, (DropdbStmt *) parsetree);
|
|
break;
|
|
|
|
/* Query-level asynchronous notification */
|
|
case T_NotifyStmt:
|
|
{
|
|
NotifyStmt *stmt = (NotifyStmt *) parsetree;
|
|
|
|
Async_Notify(stmt->conditionname, stmt->payload);
|
|
}
|
|
break;
|
|
|
|
case T_ListenStmt:
|
|
{
|
|
ListenStmt *stmt = (ListenStmt *) parsetree;
|
|
|
|
CheckRestrictedOperation("LISTEN");
|
|
|
|
/*
|
|
* We don't allow LISTEN in background processes, as there is
|
|
* no mechanism for them to collect NOTIFY messages, so they'd
|
|
* just block cleanout of the async SLRU indefinitely.
|
|
* (Authors of custom background workers could bypass this
|
|
* restriction by calling Async_Listen directly, but then it's
|
|
* on them to provide some mechanism to process the message
|
|
* queue.) Note there seems no reason to forbid UNLISTEN.
|
|
*/
|
|
if (MyBackendType != B_BACKEND)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
/* translator: %s is name of a SQL command, eg LISTEN */
|
|
errmsg("cannot execute %s within a background process",
|
|
"LISTEN")));
|
|
|
|
Async_Listen(stmt->conditionname);
|
|
}
|
|
break;
|
|
|
|
case T_UnlistenStmt:
|
|
{
|
|
UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
|
|
|
|
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:
|
|
cluster(pstate, (ClusterStmt *) parsetree, isTopLevel);
|
|
break;
|
|
|
|
case T_VacuumStmt:
|
|
ExecVacuum(pstate, (VacuumStmt *) parsetree, isTopLevel);
|
|
break;
|
|
|
|
case T_ExplainStmt:
|
|
ExplainQuery(pstate, (ExplainStmt *) parsetree, params, 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");
|
|
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")));
|
|
|
|
RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_WAIT |
|
|
(RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
|
|
break;
|
|
|
|
case T_ReindexStmt:
|
|
ExecReindex(pstate, (ReindexStmt *) parsetree, isTopLevel);
|
|
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, qc);
|
|
else
|
|
ExecuteGrantStmt(stmt);
|
|
}
|
|
break;
|
|
|
|
case T_DropStmt:
|
|
{
|
|
DropStmt *stmt = (DropStmt *) parsetree;
|
|
|
|
if (EventTriggerSupportsObjectType(stmt->removeType))
|
|
ProcessUtilitySlow(pstate, pstmt, queryString,
|
|
context, params, queryEnv,
|
|
dest, qc);
|
|
else
|
|
ExecDropStmt(stmt, isTopLevel);
|
|
}
|
|
break;
|
|
|
|
case T_RenameStmt:
|
|
{
|
|
RenameStmt *stmt = (RenameStmt *) parsetree;
|
|
|
|
if (EventTriggerSupportsObjectType(stmt->renameType))
|
|
ProcessUtilitySlow(pstate, pstmt, queryString,
|
|
context, params, queryEnv,
|
|
dest, qc);
|
|
else
|
|
ExecRenameStmt(stmt);
|
|
}
|
|
break;
|
|
|
|
case T_AlterObjectDependsStmt:
|
|
{
|
|
AlterObjectDependsStmt *stmt = (AlterObjectDependsStmt *) parsetree;
|
|
|
|
if (EventTriggerSupportsObjectType(stmt->objectType))
|
|
ProcessUtilitySlow(pstate, pstmt, queryString,
|
|
context, params, queryEnv,
|
|
dest, qc);
|
|
else
|
|
ExecAlterObjectDependsStmt(stmt, NULL);
|
|
}
|
|
break;
|
|
|
|
case T_AlterObjectSchemaStmt:
|
|
{
|
|
AlterObjectSchemaStmt *stmt = (AlterObjectSchemaStmt *) parsetree;
|
|
|
|
if (EventTriggerSupportsObjectType(stmt->objectType))
|
|
ProcessUtilitySlow(pstate, pstmt, queryString,
|
|
context, params, queryEnv,
|
|
dest, qc);
|
|
else
|
|
ExecAlterObjectSchemaStmt(stmt, NULL);
|
|
}
|
|
break;
|
|
|
|
case T_AlterOwnerStmt:
|
|
{
|
|
AlterOwnerStmt *stmt = (AlterOwnerStmt *) parsetree;
|
|
|
|
if (EventTriggerSupportsObjectType(stmt->objectType))
|
|
ProcessUtilitySlow(pstate, pstmt, queryString,
|
|
context, params, queryEnv,
|
|
dest, qc);
|
|
else
|
|
ExecAlterOwnerStmt(stmt);
|
|
}
|
|
break;
|
|
|
|
case T_CommentStmt:
|
|
{
|
|
CommentStmt *stmt = (CommentStmt *) parsetree;
|
|
|
|
if (EventTriggerSupportsObjectType(stmt->objtype))
|
|
ProcessUtilitySlow(pstate, pstmt, queryString,
|
|
context, params, queryEnv,
|
|
dest, qc);
|
|
else
|
|
CommentObject(stmt);
|
|
break;
|
|
}
|
|
|
|
case T_SecLabelStmt:
|
|
{
|
|
SecLabelStmt *stmt = (SecLabelStmt *) parsetree;
|
|
|
|
if (EventTriggerSupportsObjectType(stmt->objtype))
|
|
ProcessUtilitySlow(pstate, pstmt, queryString,
|
|
context, params, queryEnv,
|
|
dest, qc);
|
|
else
|
|
ExecSecLabelStmt(stmt);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
/* All other statement types have event trigger support */
|
|
ProcessUtilitySlow(pstate, pstmt, queryString,
|
|
context, params, queryEnv,
|
|
dest, qc);
|
|
break;
|
|
}
|
|
|
|
free_parsestate(pstate);
|
|
|
|
/*
|
|
* Make effects of commands visible, for instance so that
|
|
* PreCommit_on_commit_actions() can see them (see for example bug
|
|
* #15631).
|
|
*/
|
|
CommandCounterIncrement();
|
|
}
|
|
|
|
/*
|
|
* 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,
|
|
QueryCompletion *qc)
|
|
{
|
|
Node *parsetree = pstmt->utilityStmt;
|
|
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
|
|
bool isCompleteQuery = (context != PROCESS_UTILITY_SUBCOMMAND);
|
|
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;
|
|
RangeVar *table_rv = NULL;
|
|
|
|
/* Run parse analysis ... */
|
|
stmts = transformCreateStmt((CreateStmt *) parsetree,
|
|
queryString);
|
|
|
|
/*
|
|
* ... and do it. We can't use foreach() because we may
|
|
* modify the list midway through, so pick off the
|
|
* elements one at a time, the hard way.
|
|
*/
|
|
while (stmts != NIL)
|
|
{
|
|
Node *stmt = (Node *) linitial(stmts);
|
|
|
|
stmts = list_delete_first(stmts);
|
|
|
|
if (IsA(stmt, CreateStmt))
|
|
{
|
|
CreateStmt *cstmt = (CreateStmt *) stmt;
|
|
Datum toast_options;
|
|
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
|
|
|
|
/* Remember transformed RangeVar for LIKE */
|
|
table_rv = cstmt->relation;
|
|
|
|
/* Create the table itself */
|
|
address = DefineRelation(cstmt,
|
|
RELKIND_RELATION,
|
|
InvalidOid, NULL,
|
|
queryString);
|
|
EventTriggerCollectSimpleCommand(address,
|
|
secondaryObject,
|
|
stmt);
|
|
|
|
/*
|
|
* 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,
|
|
cstmt->options,
|
|
"toast",
|
|
validnsps,
|
|
true,
|
|
false);
|
|
(void) heap_reloptions(RELKIND_TOASTVALUE,
|
|
toast_options,
|
|
true);
|
|
|
|
NewRelationCreateToastTable(address.objectId,
|
|
toast_options);
|
|
}
|
|
else if (IsA(stmt, CreateForeignTableStmt))
|
|
{
|
|
CreateForeignTableStmt *cstmt = (CreateForeignTableStmt *) stmt;
|
|
|
|
/* Remember transformed RangeVar for LIKE */
|
|
table_rv = cstmt->base.relation;
|
|
|
|
/* Create the table itself */
|
|
address = DefineRelation(&cstmt->base,
|
|
RELKIND_FOREIGN_TABLE,
|
|
InvalidOid, NULL,
|
|
queryString);
|
|
CreateForeignTable(cstmt,
|
|
address.objectId);
|
|
EventTriggerCollectSimpleCommand(address,
|
|
secondaryObject,
|
|
stmt);
|
|
}
|
|
else if (IsA(stmt, TableLikeClause))
|
|
{
|
|
/*
|
|
* Do delayed processing of LIKE options. This
|
|
* will result in additional sub-statements for us
|
|
* to process. Those should get done before any
|
|
* remaining actions, so prepend them to "stmts".
|
|
*/
|
|
TableLikeClause *like = (TableLikeClause *) stmt;
|
|
List *morestmts;
|
|
|
|
Assert(table_rv != NULL);
|
|
|
|
morestmts = expandTableLikeClause(table_rv, like);
|
|
stmts = list_concat(morestmts, stmts);
|
|
}
|
|
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,
|
|
false,
|
|
PROCESS_UTILITY_SUBCOMMAND,
|
|
params,
|
|
NULL,
|
|
None_Receiver,
|
|
NULL);
|
|
}
|
|
|
|
/* Need CCI between commands */
|
|
if (stmts != NIL)
|
|
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;
|
|
LOCKMODE lockmode;
|
|
ListCell *cell;
|
|
|
|
/*
|
|
* Disallow ALTER TABLE .. DETACH CONCURRENTLY in a
|
|
* transaction block or function. (Perhaps it could be
|
|
* allowed in a procedure, but don't hold your breath.)
|
|
*/
|
|
foreach(cell, atstmt->cmds)
|
|
{
|
|
AlterTableCmd *cmd = (AlterTableCmd *) lfirst(cell);
|
|
|
|
/* Disallow DETACH CONCURRENTLY in a transaction block */
|
|
if (cmd->subtype == AT_DetachPartition)
|
|
{
|
|
if (((PartitionCmd *) cmd->def)->concurrent)
|
|
PreventInTransactionBlock(isTopLevel,
|
|
"ALTER TABLE ... DETACH CONCURRENTLY");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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))
|
|
{
|
|
AlterTableUtilityContext atcontext;
|
|
|
|
/* Set up info needed for recursive callbacks ... */
|
|
atcontext.pstmt = pstmt;
|
|
atcontext.queryString = queryString;
|
|
atcontext.relid = relid;
|
|
atcontext.params = params;
|
|
atcontext.queryEnv = queryEnv;
|
|
|
|
/* ... ensure we have an event trigger context ... */
|
|
EventTriggerAlterTableStart(parsetree);
|
|
EventTriggerAlterTableRelid(relid);
|
|
|
|
/* ... and do it */
|
|
AlterTable(atstmt, lockmode, &atcontext);
|
|
|
|
/* 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,
|
|
stmt->replace);
|
|
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;
|
|
bool is_alter_table;
|
|
|
|
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 &&
|
|
relkind != RELKIND_FOREIGN_TABLE)
|
|
elog(ERROR, "unexpected relkind \"%c\" on partition \"%s\"",
|
|
relkind, stmt->relation->relname);
|
|
|
|
if (relkind == RELKIND_FOREIGN_TABLE &&
|
|
(stmt->unique || stmt->primary))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
errmsg("cannot create unique index on partitioned table \"%s\"",
|
|
stmt->relation->relname),
|
|
errdetail("Table \"%s\" contains partitions that are foreign tables.",
|
|
stmt->relation->relname)));
|
|
}
|
|
list_free(inheritors);
|
|
}
|
|
|
|
/*
|
|
* If the IndexStmt is already transformed, it must have
|
|
* come from generateClonedIndexStmt, which in current
|
|
* usage means it came from expandTableLikeClause rather
|
|
* than from original parse analysis. And that means we
|
|
* must treat it like ALTER TABLE ADD INDEX, not CREATE.
|
|
* (This is a bit grotty, but currently it doesn't seem
|
|
* worth adding a separate bool field for the purpose.)
|
|
*/
|
|
is_alter_table = stmt->transformed;
|
|
|
|
/* 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 */
|
|
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);
|
|
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(pstate, (CreateTableAsStmt *) parsetree,
|
|
params, queryEnv, qc);
|
|
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, qc);
|
|
}
|
|
PG_FINALLY();
|
|
{
|
|
EventTriggerUndoInhibitCommandCollection();
|
|
}
|
|
PG_END_TRY();
|
|
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);
|
|
|
|
/*
|
|
* DefineOpFamily calls EventTriggerCollectSimpleCommand
|
|
* directly.
|
|
*/
|
|
commandCollected = true;
|
|
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_AlterTypeStmt:
|
|
address = AlterType((AlterTypeStmt *) 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,
|
|
isTopLevel);
|
|
break;
|
|
|
|
case T_DropSubscriptionStmt:
|
|
DropSubscription((DropSubscriptionStmt *) parsetree, isTopLevel);
|
|
/* no commands stashed for DROP */
|
|
commandCollected = true;
|
|
break;
|
|
|
|
case T_CreateStatsStmt:
|
|
{
|
|
Oid relid;
|
|
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
|
|
RangeVar *rel = (RangeVar *) linitial(stmt->relations);
|
|
|
|
if (!IsA(rel, RangeVar))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("only a single relation is allowed in CREATE STATISTICS")));
|
|
|
|
/*
|
|
* CREATE STATISTICS will influence future execution plans
|
|
* but does not interfere with currently executing plans.
|
|
* So it should be enough to take ShareUpdateExclusiveLock
|
|
* on relation, conflicting with ANALYZE and other DDL
|
|
* that sets statistical information, but not with normal
|
|
* queries.
|
|
*
|
|
* XXX RangeVarCallbackOwnsRelation not needed here, to
|
|
* keep the same behavior as before.
|
|
*/
|
|
relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
|
|
|
|
/* Run parse analysis ... */
|
|
stmt = transformStatsStmt(relid, stmt, queryString);
|
|
|
|
address = CreateStatistics(stmt);
|
|
}
|
|
break;
|
|
|
|
case T_AlterStatsStmt:
|
|
address = AlterStatistics((AlterStatsStmt *) 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_FINALLY();
|
|
{
|
|
if (needCleanup)
|
|
EventTriggerEndCompleteQuery();
|
|
}
|
|
PG_END_TRY();
|
|
}
|
|
|
|
/*
|
|
* ProcessUtilityForAlterTable
|
|
* Recursive entry from ALTER TABLE
|
|
*
|
|
* ALTER TABLE sometimes generates subcommands such as CREATE INDEX.
|
|
* It calls this, not the main entry point ProcessUtility, to execute
|
|
* such subcommands.
|
|
*
|
|
* stmt: the utility command to execute
|
|
* context: opaque passthrough struct with the info we need
|
|
*
|
|
* It's caller's responsibility to do CommandCounterIncrement after
|
|
* calling this, if needed.
|
|
*/
|
|
void
|
|
ProcessUtilityForAlterTable(Node *stmt, AlterTableUtilityContext *context)
|
|
{
|
|
PlannedStmt *wrapper;
|
|
|
|
/*
|
|
* For event triggers, we must "close" the current complex-command set,
|
|
* and start a new one afterwards; this is needed to ensure the ordering
|
|
* of command events is consistent with the way they were executed.
|
|
*/
|
|
EventTriggerAlterTableEnd();
|
|
|
|
/* Create a suitable wrapper */
|
|
wrapper = makeNode(PlannedStmt);
|
|
wrapper->commandType = CMD_UTILITY;
|
|
wrapper->canSetTag = false;
|
|
wrapper->utilityStmt = stmt;
|
|
wrapper->stmt_location = context->pstmt->stmt_location;
|
|
wrapper->stmt_len = context->pstmt->stmt_len;
|
|
|
|
ProcessUtility(wrapper,
|
|
context->queryString,
|
|
false,
|
|
PROCESS_UTILITY_SUBCOMMAND,
|
|
context->params,
|
|
context->queryEnv,
|
|
None_Receiver,
|
|
NULL);
|
|
|
|
EventTriggerAlterTableStart(context->pstmt->utilityStmt);
|
|
EventTriggerAlterTableRelid(context->relid);
|
|
}
|
|
|
|
/*
|
|
* 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");
|
|
/* fall through */
|
|
|
|
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 CommandTag
|
|
AlterObjectTypeCommandTag(ObjectType objtype)
|
|
{
|
|
CommandTag tag;
|
|
|
|
switch (objtype)
|
|
{
|
|
case OBJECT_AGGREGATE:
|
|
tag = CMDTAG_ALTER_AGGREGATE;
|
|
break;
|
|
case OBJECT_ATTRIBUTE:
|
|
tag = CMDTAG_ALTER_TYPE;
|
|
break;
|
|
case OBJECT_CAST:
|
|
tag = CMDTAG_ALTER_CAST;
|
|
break;
|
|
case OBJECT_COLLATION:
|
|
tag = CMDTAG_ALTER_COLLATION;
|
|
break;
|
|
case OBJECT_COLUMN:
|
|
tag = CMDTAG_ALTER_TABLE;
|
|
break;
|
|
case OBJECT_CONVERSION:
|
|
tag = CMDTAG_ALTER_CONVERSION;
|
|
break;
|
|
case OBJECT_DATABASE:
|
|
tag = CMDTAG_ALTER_DATABASE;
|
|
break;
|
|
case OBJECT_DOMAIN:
|
|
case OBJECT_DOMCONSTRAINT:
|
|
tag = CMDTAG_ALTER_DOMAIN;
|
|
break;
|
|
case OBJECT_EXTENSION:
|
|
tag = CMDTAG_ALTER_EXTENSION;
|
|
break;
|
|
case OBJECT_FDW:
|
|
tag = CMDTAG_ALTER_FOREIGN_DATA_WRAPPER;
|
|
break;
|
|
case OBJECT_FOREIGN_SERVER:
|
|
tag = CMDTAG_ALTER_SERVER;
|
|
break;
|
|
case OBJECT_FOREIGN_TABLE:
|
|
tag = CMDTAG_ALTER_FOREIGN_TABLE;
|
|
break;
|
|
case OBJECT_FUNCTION:
|
|
tag = CMDTAG_ALTER_FUNCTION;
|
|
break;
|
|
case OBJECT_INDEX:
|
|
tag = CMDTAG_ALTER_INDEX;
|
|
break;
|
|
case OBJECT_LANGUAGE:
|
|
tag = CMDTAG_ALTER_LANGUAGE;
|
|
break;
|
|
case OBJECT_LARGEOBJECT:
|
|
tag = CMDTAG_ALTER_LARGE_OBJECT;
|
|
break;
|
|
case OBJECT_OPCLASS:
|
|
tag = CMDTAG_ALTER_OPERATOR_CLASS;
|
|
break;
|
|
case OBJECT_OPERATOR:
|
|
tag = CMDTAG_ALTER_OPERATOR;
|
|
break;
|
|
case OBJECT_OPFAMILY:
|
|
tag = CMDTAG_ALTER_OPERATOR_FAMILY;
|
|
break;
|
|
case OBJECT_POLICY:
|
|
tag = CMDTAG_ALTER_POLICY;
|
|
break;
|
|
case OBJECT_PROCEDURE:
|
|
tag = CMDTAG_ALTER_PROCEDURE;
|
|
break;
|
|
case OBJECT_ROLE:
|
|
tag = CMDTAG_ALTER_ROLE;
|
|
break;
|
|
case OBJECT_ROUTINE:
|
|
tag = CMDTAG_ALTER_ROUTINE;
|
|
break;
|
|
case OBJECT_RULE:
|
|
tag = CMDTAG_ALTER_RULE;
|
|
break;
|
|
case OBJECT_SCHEMA:
|
|
tag = CMDTAG_ALTER_SCHEMA;
|
|
break;
|
|
case OBJECT_SEQUENCE:
|
|
tag = CMDTAG_ALTER_SEQUENCE;
|
|
break;
|
|
case OBJECT_TABLE:
|
|
case OBJECT_TABCONSTRAINT:
|
|
tag = CMDTAG_ALTER_TABLE;
|
|
break;
|
|
case OBJECT_TABLESPACE:
|
|
tag = CMDTAG_ALTER_TABLESPACE;
|
|
break;
|
|
case OBJECT_TRIGGER:
|
|
tag = CMDTAG_ALTER_TRIGGER;
|
|
break;
|
|
case OBJECT_EVENT_TRIGGER:
|
|
tag = CMDTAG_ALTER_EVENT_TRIGGER;
|
|
break;
|
|
case OBJECT_TSCONFIGURATION:
|
|
tag = CMDTAG_ALTER_TEXT_SEARCH_CONFIGURATION;
|
|
break;
|
|
case OBJECT_TSDICTIONARY:
|
|
tag = CMDTAG_ALTER_TEXT_SEARCH_DICTIONARY;
|
|
break;
|
|
case OBJECT_TSPARSER:
|
|
tag = CMDTAG_ALTER_TEXT_SEARCH_PARSER;
|
|
break;
|
|
case OBJECT_TSTEMPLATE:
|
|
tag = CMDTAG_ALTER_TEXT_SEARCH_TEMPLATE;
|
|
break;
|
|
case OBJECT_TYPE:
|
|
tag = CMDTAG_ALTER_TYPE;
|
|
break;
|
|
case OBJECT_VIEW:
|
|
tag = CMDTAG_ALTER_VIEW;
|
|
break;
|
|
case OBJECT_MATVIEW:
|
|
tag = CMDTAG_ALTER_MATERIALIZED_VIEW;
|
|
break;
|
|
case OBJECT_PUBLICATION:
|
|
tag = CMDTAG_ALTER_PUBLICATION;
|
|
break;
|
|
case OBJECT_SUBSCRIPTION:
|
|
tag = CMDTAG_ALTER_SUBSCRIPTION;
|
|
break;
|
|
case OBJECT_STATISTIC_EXT:
|
|
tag = CMDTAG_ALTER_STATISTICS;
|
|
break;
|
|
default:
|
|
tag = CMDTAG_UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
return tag;
|
|
}
|
|
|
|
/*
|
|
* CreateCommandTag
|
|
* utility to get a CommandTag for 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.
|
|
*/
|
|
CommandTag
|
|
CreateCommandTag(Node *parsetree)
|
|
{
|
|
CommandTag 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 = CMDTAG_INSERT;
|
|
break;
|
|
|
|
case T_DeleteStmt:
|
|
tag = CMDTAG_DELETE;
|
|
break;
|
|
|
|
case T_UpdateStmt:
|
|
tag = CMDTAG_UPDATE;
|
|
break;
|
|
|
|
case T_SelectStmt:
|
|
tag = CMDTAG_SELECT;
|
|
break;
|
|
|
|
case T_PLAssignStmt:
|
|
tag = CMDTAG_SELECT;
|
|
break;
|
|
|
|
/* utility statements --- same whether raw or cooked */
|
|
case T_TransactionStmt:
|
|
{
|
|
TransactionStmt *stmt = (TransactionStmt *) parsetree;
|
|
|
|
switch (stmt->kind)
|
|
{
|
|
case TRANS_STMT_BEGIN:
|
|
tag = CMDTAG_BEGIN;
|
|
break;
|
|
|
|
case TRANS_STMT_START:
|
|
tag = CMDTAG_START_TRANSACTION;
|
|
break;
|
|
|
|
case TRANS_STMT_COMMIT:
|
|
tag = CMDTAG_COMMIT;
|
|
break;
|
|
|
|
case TRANS_STMT_ROLLBACK:
|
|
case TRANS_STMT_ROLLBACK_TO:
|
|
tag = CMDTAG_ROLLBACK;
|
|
break;
|
|
|
|
case TRANS_STMT_SAVEPOINT:
|
|
tag = CMDTAG_SAVEPOINT;
|
|
break;
|
|
|
|
case TRANS_STMT_RELEASE:
|
|
tag = CMDTAG_RELEASE;
|
|
break;
|
|
|
|
case TRANS_STMT_PREPARE:
|
|
tag = CMDTAG_PREPARE_TRANSACTION;
|
|
break;
|
|
|
|
case TRANS_STMT_COMMIT_PREPARED:
|
|
tag = CMDTAG_COMMIT_PREPARED;
|
|
break;
|
|
|
|
case TRANS_STMT_ROLLBACK_PREPARED:
|
|
tag = CMDTAG_ROLLBACK_PREPARED;
|
|
break;
|
|
|
|
default:
|
|
tag = CMDTAG_UNKNOWN;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case T_DeclareCursorStmt:
|
|
tag = CMDTAG_DECLARE_CURSOR;
|
|
break;
|
|
|
|
case T_ClosePortalStmt:
|
|
{
|
|
ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
|
|
|
|
if (stmt->portalname == NULL)
|
|
tag = CMDTAG_CLOSE_CURSOR_ALL;
|
|
else
|
|
tag = CMDTAG_CLOSE_CURSOR;
|
|
}
|
|
break;
|
|
|
|
case T_FetchStmt:
|
|
{
|
|
FetchStmt *stmt = (FetchStmt *) parsetree;
|
|
|
|
tag = (stmt->ismove) ? CMDTAG_MOVE : CMDTAG_FETCH;
|
|
}
|
|
break;
|
|
|
|
case T_CreateDomainStmt:
|
|
tag = CMDTAG_CREATE_DOMAIN;
|
|
break;
|
|
|
|
case T_CreateSchemaStmt:
|
|
tag = CMDTAG_CREATE_SCHEMA;
|
|
break;
|
|
|
|
case T_CreateStmt:
|
|
tag = CMDTAG_CREATE_TABLE;
|
|
break;
|
|
|
|
case T_CreateTableSpaceStmt:
|
|
tag = CMDTAG_CREATE_TABLESPACE;
|
|
break;
|
|
|
|
case T_DropTableSpaceStmt:
|
|
tag = CMDTAG_DROP_TABLESPACE;
|
|
break;
|
|
|
|
case T_AlterTableSpaceOptionsStmt:
|
|
tag = CMDTAG_ALTER_TABLESPACE;
|
|
break;
|
|
|
|
case T_CreateExtensionStmt:
|
|
tag = CMDTAG_CREATE_EXTENSION;
|
|
break;
|
|
|
|
case T_AlterExtensionStmt:
|
|
tag = CMDTAG_ALTER_EXTENSION;
|
|
break;
|
|
|
|
case T_AlterExtensionContentsStmt:
|
|
tag = CMDTAG_ALTER_EXTENSION;
|
|
break;
|
|
|
|
case T_CreateFdwStmt:
|
|
tag = CMDTAG_CREATE_FOREIGN_DATA_WRAPPER;
|
|
break;
|
|
|
|
case T_AlterFdwStmt:
|
|
tag = CMDTAG_ALTER_FOREIGN_DATA_WRAPPER;
|
|
break;
|
|
|
|
case T_CreateForeignServerStmt:
|
|
tag = CMDTAG_CREATE_SERVER;
|
|
break;
|
|
|
|
case T_AlterForeignServerStmt:
|
|
tag = CMDTAG_ALTER_SERVER;
|
|
break;
|
|
|
|
case T_CreateUserMappingStmt:
|
|
tag = CMDTAG_CREATE_USER_MAPPING;
|
|
break;
|
|
|
|
case T_AlterUserMappingStmt:
|
|
tag = CMDTAG_ALTER_USER_MAPPING;
|
|
break;
|
|
|
|
case T_DropUserMappingStmt:
|
|
tag = CMDTAG_DROP_USER_MAPPING;
|
|
break;
|
|
|
|
case T_CreateForeignTableStmt:
|
|
tag = CMDTAG_CREATE_FOREIGN_TABLE;
|
|
break;
|
|
|
|
case T_ImportForeignSchemaStmt:
|
|
tag = CMDTAG_IMPORT_FOREIGN_SCHEMA;
|
|
break;
|
|
|
|
case T_DropStmt:
|
|
switch (((DropStmt *) parsetree)->removeType)
|
|
{
|
|
case OBJECT_TABLE:
|
|
tag = CMDTAG_DROP_TABLE;
|
|
break;
|
|
case OBJECT_SEQUENCE:
|
|
tag = CMDTAG_DROP_SEQUENCE;
|
|
break;
|
|
case OBJECT_VIEW:
|
|
tag = CMDTAG_DROP_VIEW;
|
|
break;
|
|
case OBJECT_MATVIEW:
|
|
tag = CMDTAG_DROP_MATERIALIZED_VIEW;
|
|
break;
|
|
case OBJECT_INDEX:
|
|
tag = CMDTAG_DROP_INDEX;
|
|
break;
|
|
case OBJECT_TYPE:
|
|
tag = CMDTAG_DROP_TYPE;
|
|
break;
|
|
case OBJECT_DOMAIN:
|
|
tag = CMDTAG_DROP_DOMAIN;
|
|
break;
|
|
case OBJECT_COLLATION:
|
|
tag = CMDTAG_DROP_COLLATION;
|
|
break;
|
|
case OBJECT_CONVERSION:
|
|
tag = CMDTAG_DROP_CONVERSION;
|
|
break;
|
|
case OBJECT_SCHEMA:
|
|
tag = CMDTAG_DROP_SCHEMA;
|
|
break;
|
|
case OBJECT_TSPARSER:
|
|
tag = CMDTAG_DROP_TEXT_SEARCH_PARSER;
|
|
break;
|
|
case OBJECT_TSDICTIONARY:
|
|
tag = CMDTAG_DROP_TEXT_SEARCH_DICTIONARY;
|
|
break;
|
|
case OBJECT_TSTEMPLATE:
|
|
tag = CMDTAG_DROP_TEXT_SEARCH_TEMPLATE;
|
|
break;
|
|
case OBJECT_TSCONFIGURATION:
|
|
tag = CMDTAG_DROP_TEXT_SEARCH_CONFIGURATION;
|
|
break;
|
|
case OBJECT_FOREIGN_TABLE:
|
|
tag = CMDTAG_DROP_FOREIGN_TABLE;
|
|
break;
|
|
case OBJECT_EXTENSION:
|
|
tag = CMDTAG_DROP_EXTENSION;
|
|
break;
|
|
case OBJECT_FUNCTION:
|
|
tag = CMDTAG_DROP_FUNCTION;
|
|
break;
|
|
case OBJECT_PROCEDURE:
|
|
tag = CMDTAG_DROP_PROCEDURE;
|
|
break;
|
|
case OBJECT_ROUTINE:
|
|
tag = CMDTAG_DROP_ROUTINE;
|
|
break;
|
|
case OBJECT_AGGREGATE:
|
|
tag = CMDTAG_DROP_AGGREGATE;
|
|
break;
|
|
case OBJECT_OPERATOR:
|
|
tag = CMDTAG_DROP_OPERATOR;
|
|
break;
|
|
case OBJECT_LANGUAGE:
|
|
tag = CMDTAG_DROP_LANGUAGE;
|
|
break;
|
|
case OBJECT_CAST:
|
|
tag = CMDTAG_DROP_CAST;
|
|
break;
|
|
case OBJECT_TRIGGER:
|
|
tag = CMDTAG_DROP_TRIGGER;
|
|
break;
|
|
case OBJECT_EVENT_TRIGGER:
|
|
tag = CMDTAG_DROP_EVENT_TRIGGER;
|
|
break;
|
|
case OBJECT_RULE:
|
|
tag = CMDTAG_DROP_RULE;
|
|
break;
|
|
case OBJECT_FDW:
|
|
tag = CMDTAG_DROP_FOREIGN_DATA_WRAPPER;
|
|
break;
|
|
case OBJECT_FOREIGN_SERVER:
|
|
tag = CMDTAG_DROP_SERVER;
|
|
break;
|
|
case OBJECT_OPCLASS:
|
|
tag = CMDTAG_DROP_OPERATOR_CLASS;
|
|
break;
|
|
case OBJECT_OPFAMILY:
|
|
tag = CMDTAG_DROP_OPERATOR_FAMILY;
|
|
break;
|
|
case OBJECT_POLICY:
|
|
tag = CMDTAG_DROP_POLICY;
|
|
break;
|
|
case OBJECT_TRANSFORM:
|
|
tag = CMDTAG_DROP_TRANSFORM;
|
|
break;
|
|
case OBJECT_ACCESS_METHOD:
|
|
tag = CMDTAG_DROP_ACCESS_METHOD;
|
|
break;
|
|
case OBJECT_PUBLICATION:
|
|
tag = CMDTAG_DROP_PUBLICATION;
|
|
break;
|
|
case OBJECT_STATISTIC_EXT:
|
|
tag = CMDTAG_DROP_STATISTICS;
|
|
break;
|
|
default:
|
|
tag = CMDTAG_UNKNOWN;
|
|
}
|
|
break;
|
|
|
|
case T_TruncateStmt:
|
|
tag = CMDTAG_TRUNCATE_TABLE;
|
|
break;
|
|
|
|
case T_CommentStmt:
|
|
tag = CMDTAG_COMMENT;
|
|
break;
|
|
|
|
case T_SecLabelStmt:
|
|
tag = CMDTAG_SECURITY_LABEL;
|
|
break;
|
|
|
|
case T_CopyStmt:
|
|
tag = CMDTAG_COPY;
|
|
break;
|
|
|
|
case T_RenameStmt:
|
|
|
|
/*
|
|
* When the column is renamed, the command tag is created from its
|
|
* relation type
|
|
*/
|
|
tag = AlterObjectTypeCommandTag(((RenameStmt *) parsetree)->renameType == OBJECT_COLUMN ?
|
|
((RenameStmt *) parsetree)->relationType :
|
|
((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)->objtype);
|
|
break;
|
|
|
|
case T_AlterDomainStmt:
|
|
tag = CMDTAG_ALTER_DOMAIN;
|
|
break;
|
|
|
|
case T_AlterFunctionStmt:
|
|
switch (((AlterFunctionStmt *) parsetree)->objtype)
|
|
{
|
|
case OBJECT_FUNCTION:
|
|
tag = CMDTAG_ALTER_FUNCTION;
|
|
break;
|
|
case OBJECT_PROCEDURE:
|
|
tag = CMDTAG_ALTER_PROCEDURE;
|
|
break;
|
|
case OBJECT_ROUTINE:
|
|
tag = CMDTAG_ALTER_ROUTINE;
|
|
break;
|
|
default:
|
|
tag = CMDTAG_UNKNOWN;
|
|
}
|
|
break;
|
|
|
|
case T_GrantStmt:
|
|
{
|
|
GrantStmt *stmt = (GrantStmt *) parsetree;
|
|
|
|
tag = (stmt->is_grant) ? CMDTAG_GRANT : CMDTAG_REVOKE;
|
|
}
|
|
break;
|
|
|
|
case T_GrantRoleStmt:
|
|
{
|
|
GrantRoleStmt *stmt = (GrantRoleStmt *) parsetree;
|
|
|
|
tag = (stmt->is_grant) ? CMDTAG_GRANT_ROLE : CMDTAG_REVOKE_ROLE;
|
|
}
|
|
break;
|
|
|
|
case T_AlterDefaultPrivilegesStmt:
|
|
tag = CMDTAG_ALTER_DEFAULT_PRIVILEGES;
|
|
break;
|
|
|
|
case T_DefineStmt:
|
|
switch (((DefineStmt *) parsetree)->kind)
|
|
{
|
|
case OBJECT_AGGREGATE:
|
|
tag = CMDTAG_CREATE_AGGREGATE;
|
|
break;
|
|
case OBJECT_OPERATOR:
|
|
tag = CMDTAG_CREATE_OPERATOR;
|
|
break;
|
|
case OBJECT_TYPE:
|
|
tag = CMDTAG_CREATE_TYPE;
|
|
break;
|
|
case OBJECT_TSPARSER:
|
|
tag = CMDTAG_CREATE_TEXT_SEARCH_PARSER;
|
|
break;
|
|
case OBJECT_TSDICTIONARY:
|
|
tag = CMDTAG_CREATE_TEXT_SEARCH_DICTIONARY;
|
|
break;
|
|
case OBJECT_TSTEMPLATE:
|
|
tag = CMDTAG_CREATE_TEXT_SEARCH_TEMPLATE;
|
|
break;
|
|
case OBJECT_TSCONFIGURATION:
|
|
tag = CMDTAG_CREATE_TEXT_SEARCH_CONFIGURATION;
|
|
break;
|
|
case OBJECT_COLLATION:
|
|
tag = CMDTAG_CREATE_COLLATION;
|
|
break;
|
|
case OBJECT_ACCESS_METHOD:
|
|
tag = CMDTAG_CREATE_ACCESS_METHOD;
|
|
break;
|
|
default:
|
|
tag = CMDTAG_UNKNOWN;
|
|
}
|
|
break;
|
|
|
|
case T_CompositeTypeStmt:
|
|
tag = CMDTAG_CREATE_TYPE;
|
|
break;
|
|
|
|
case T_CreateEnumStmt:
|
|
tag = CMDTAG_CREATE_TYPE;
|
|
break;
|
|
|
|
case T_CreateRangeStmt:
|
|
tag = CMDTAG_CREATE_TYPE;
|
|
break;
|
|
|
|
case T_AlterEnumStmt:
|
|
tag = CMDTAG_ALTER_TYPE;
|
|
break;
|
|
|
|
case T_ViewStmt:
|
|
tag = CMDTAG_CREATE_VIEW;
|
|
break;
|
|
|
|
case T_CreateFunctionStmt:
|
|
if (((CreateFunctionStmt *) parsetree)->is_procedure)
|
|
tag = CMDTAG_CREATE_PROCEDURE;
|
|
else
|
|
tag = CMDTAG_CREATE_FUNCTION;
|
|
break;
|
|
|
|
case T_IndexStmt:
|
|
tag = CMDTAG_CREATE_INDEX;
|
|
break;
|
|
|
|
case T_RuleStmt:
|
|
tag = CMDTAG_CREATE_RULE;
|
|
break;
|
|
|
|
case T_CreateSeqStmt:
|
|
tag = CMDTAG_CREATE_SEQUENCE;
|
|
break;
|
|
|
|
case T_AlterSeqStmt:
|
|
tag = CMDTAG_ALTER_SEQUENCE;
|
|
break;
|
|
|
|
case T_DoStmt:
|
|
tag = CMDTAG_DO;
|
|
break;
|
|
|
|
case T_CreatedbStmt:
|
|
tag = CMDTAG_CREATE_DATABASE;
|
|
break;
|
|
|
|
case T_AlterDatabaseStmt:
|
|
tag = CMDTAG_ALTER_DATABASE;
|
|
break;
|
|
|
|
case T_AlterDatabaseSetStmt:
|
|
tag = CMDTAG_ALTER_DATABASE;
|
|
break;
|
|
|
|
case T_DropdbStmt:
|
|
tag = CMDTAG_DROP_DATABASE;
|
|
break;
|
|
|
|
case T_NotifyStmt:
|
|
tag = CMDTAG_NOTIFY;
|
|
break;
|
|
|
|
case T_ListenStmt:
|
|
tag = CMDTAG_LISTEN;
|
|
break;
|
|
|
|
case T_UnlistenStmt:
|
|
tag = CMDTAG_UNLISTEN;
|
|
break;
|
|
|
|
case T_LoadStmt:
|
|
tag = CMDTAG_LOAD;
|
|
break;
|
|
|
|
case T_CallStmt:
|
|
tag = CMDTAG_CALL;
|
|
break;
|
|
|
|
case T_ClusterStmt:
|
|
tag = CMDTAG_CLUSTER;
|
|
break;
|
|
|
|
case T_VacuumStmt:
|
|
if (((VacuumStmt *) parsetree)->is_vacuumcmd)
|
|
tag = CMDTAG_VACUUM;
|
|
else
|
|
tag = CMDTAG_ANALYZE;
|
|
break;
|
|
|
|
case T_ExplainStmt:
|
|
tag = CMDTAG_EXPLAIN;
|
|
break;
|
|
|
|
case T_CreateTableAsStmt:
|
|
switch (((CreateTableAsStmt *) parsetree)->objtype)
|
|
{
|
|
case OBJECT_TABLE:
|
|
if (((CreateTableAsStmt *) parsetree)->is_select_into)
|
|
tag = CMDTAG_SELECT_INTO;
|
|
else
|
|
tag = CMDTAG_CREATE_TABLE_AS;
|
|
break;
|
|
case OBJECT_MATVIEW:
|
|
tag = CMDTAG_CREATE_MATERIALIZED_VIEW;
|
|
break;
|
|
default:
|
|
tag = CMDTAG_UNKNOWN;
|
|
}
|
|
break;
|
|
|
|
case T_RefreshMatViewStmt:
|
|
tag = CMDTAG_REFRESH_MATERIALIZED_VIEW;
|
|
break;
|
|
|
|
case T_AlterSystemStmt:
|
|
tag = CMDTAG_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 = CMDTAG_SET;
|
|
break;
|
|
case VAR_RESET:
|
|
case VAR_RESET_ALL:
|
|
tag = CMDTAG_RESET;
|
|
break;
|
|
default:
|
|
tag = CMDTAG_UNKNOWN;
|
|
}
|
|
break;
|
|
|
|
case T_VariableShowStmt:
|
|
tag = CMDTAG_SHOW;
|
|
break;
|
|
|
|
case T_DiscardStmt:
|
|
switch (((DiscardStmt *) parsetree)->target)
|
|
{
|
|
case DISCARD_ALL:
|
|
tag = CMDTAG_DISCARD_ALL;
|
|
break;
|
|
case DISCARD_PLANS:
|
|
tag = CMDTAG_DISCARD_PLANS;
|
|
break;
|
|
case DISCARD_TEMP:
|
|
tag = CMDTAG_DISCARD_TEMP;
|
|
break;
|
|
case DISCARD_SEQUENCES:
|
|
tag = CMDTAG_DISCARD_SEQUENCES;
|
|
break;
|
|
default:
|
|
tag = CMDTAG_UNKNOWN;
|
|
}
|
|
break;
|
|
|
|
case T_CreateTransformStmt:
|
|
tag = CMDTAG_CREATE_TRANSFORM;
|
|
break;
|
|
|
|
case T_CreateTrigStmt:
|
|
tag = CMDTAG_CREATE_TRIGGER;
|
|
break;
|
|
|
|
case T_CreateEventTrigStmt:
|
|
tag = CMDTAG_CREATE_EVENT_TRIGGER;
|
|
break;
|
|
|
|
case T_AlterEventTrigStmt:
|
|
tag = CMDTAG_ALTER_EVENT_TRIGGER;
|
|
break;
|
|
|
|
case T_CreatePLangStmt:
|
|
tag = CMDTAG_CREATE_LANGUAGE;
|
|
break;
|
|
|
|
case T_CreateRoleStmt:
|
|
tag = CMDTAG_CREATE_ROLE;
|
|
break;
|
|
|
|
case T_AlterRoleStmt:
|
|
tag = CMDTAG_ALTER_ROLE;
|
|
break;
|
|
|
|
case T_AlterRoleSetStmt:
|
|
tag = CMDTAG_ALTER_ROLE;
|
|
break;
|
|
|
|
case T_DropRoleStmt:
|
|
tag = CMDTAG_DROP_ROLE;
|
|
break;
|
|
|
|
case T_DropOwnedStmt:
|
|
tag = CMDTAG_DROP_OWNED;
|
|
break;
|
|
|
|
case T_ReassignOwnedStmt:
|
|
tag = CMDTAG_REASSIGN_OWNED;
|
|
break;
|
|
|
|
case T_LockStmt:
|
|
tag = CMDTAG_LOCK_TABLE;
|
|
break;
|
|
|
|
case T_ConstraintsSetStmt:
|
|
tag = CMDTAG_SET_CONSTRAINTS;
|
|
break;
|
|
|
|
case T_CheckPointStmt:
|
|
tag = CMDTAG_CHECKPOINT;
|
|
break;
|
|
|
|
case T_ReindexStmt:
|
|
tag = CMDTAG_REINDEX;
|
|
break;
|
|
|
|
case T_CreateConversionStmt:
|
|
tag = CMDTAG_CREATE_CONVERSION;
|
|
break;
|
|
|
|
case T_CreateCastStmt:
|
|
tag = CMDTAG_CREATE_CAST;
|
|
break;
|
|
|
|
case T_CreateOpClassStmt:
|
|
tag = CMDTAG_CREATE_OPERATOR_CLASS;
|
|
break;
|
|
|
|
case T_CreateOpFamilyStmt:
|
|
tag = CMDTAG_CREATE_OPERATOR_FAMILY;
|
|
break;
|
|
|
|
case T_AlterOpFamilyStmt:
|
|
tag = CMDTAG_ALTER_OPERATOR_FAMILY;
|
|
break;
|
|
|
|
case T_AlterOperatorStmt:
|
|
tag = CMDTAG_ALTER_OPERATOR;
|
|
break;
|
|
|
|
case T_AlterTypeStmt:
|
|
tag = CMDTAG_ALTER_TYPE;
|
|
break;
|
|
|
|
case T_AlterTSDictionaryStmt:
|
|
tag = CMDTAG_ALTER_TEXT_SEARCH_DICTIONARY;
|
|
break;
|
|
|
|
case T_AlterTSConfigurationStmt:
|
|
tag = CMDTAG_ALTER_TEXT_SEARCH_CONFIGURATION;
|
|
break;
|
|
|
|
case T_CreatePolicyStmt:
|
|
tag = CMDTAG_CREATE_POLICY;
|
|
break;
|
|
|
|
case T_AlterPolicyStmt:
|
|
tag = CMDTAG_ALTER_POLICY;
|
|
break;
|
|
|
|
case T_CreateAmStmt:
|
|
tag = CMDTAG_CREATE_ACCESS_METHOD;
|
|
break;
|
|
|
|
case T_CreatePublicationStmt:
|
|
tag = CMDTAG_CREATE_PUBLICATION;
|
|
break;
|
|
|
|
case T_AlterPublicationStmt:
|
|
tag = CMDTAG_ALTER_PUBLICATION;
|
|
break;
|
|
|
|
case T_CreateSubscriptionStmt:
|
|
tag = CMDTAG_CREATE_SUBSCRIPTION;
|
|
break;
|
|
|
|
case T_AlterSubscriptionStmt:
|
|
tag = CMDTAG_ALTER_SUBSCRIPTION;
|
|
break;
|
|
|
|
case T_DropSubscriptionStmt:
|
|
tag = CMDTAG_DROP_SUBSCRIPTION;
|
|
break;
|
|
|
|
case T_AlterCollationStmt:
|
|
tag = CMDTAG_ALTER_COLLATION;
|
|
break;
|
|
|
|
case T_PrepareStmt:
|
|
tag = CMDTAG_PREPARE;
|
|
break;
|
|
|
|
case T_ExecuteStmt:
|
|
tag = CMDTAG_EXECUTE;
|
|
break;
|
|
|
|
case T_CreateStatsStmt:
|
|
tag = CMDTAG_CREATE_STATISTICS;
|
|
break;
|
|
|
|
case T_AlterStatsStmt:
|
|
tag = CMDTAG_ALTER_STATISTICS;
|
|
break;
|
|
|
|
case T_DeallocateStmt:
|
|
{
|
|
DeallocateStmt *stmt = (DeallocateStmt *) parsetree;
|
|
|
|
if (stmt->name == NULL)
|
|
tag = CMDTAG_DEALLOCATE_ALL;
|
|
else
|
|
tag = CMDTAG_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 = CMDTAG_SELECT_FOR_KEY_SHARE;
|
|
break;
|
|
case LCS_FORSHARE:
|
|
tag = CMDTAG_SELECT_FOR_SHARE;
|
|
break;
|
|
case LCS_FORNOKEYUPDATE:
|
|
tag = CMDTAG_SELECT_FOR_NO_KEY_UPDATE;
|
|
break;
|
|
case LCS_FORUPDATE:
|
|
tag = CMDTAG_SELECT_FOR_UPDATE;
|
|
break;
|
|
default:
|
|
tag = CMDTAG_SELECT;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
tag = CMDTAG_SELECT;
|
|
break;
|
|
case CMD_UPDATE:
|
|
tag = CMDTAG_UPDATE;
|
|
break;
|
|
case CMD_INSERT:
|
|
tag = CMDTAG_INSERT;
|
|
break;
|
|
case CMD_DELETE:
|
|
tag = CMDTAG_DELETE;
|
|
break;
|
|
case CMD_UTILITY:
|
|
tag = CreateCommandTag(stmt->utilityStmt);
|
|
break;
|
|
default:
|
|
elog(WARNING, "unrecognized commandType: %d",
|
|
(int) stmt->commandType);
|
|
tag = CMDTAG_UNKNOWN;
|
|
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 = CMDTAG_SELECT_FOR_KEY_SHARE;
|
|
break;
|
|
case LCS_FORSHARE:
|
|
tag = CMDTAG_SELECT_FOR_SHARE;
|
|
break;
|
|
case LCS_FORNOKEYUPDATE:
|
|
tag = CMDTAG_SELECT_FOR_NO_KEY_UPDATE;
|
|
break;
|
|
case LCS_FORUPDATE:
|
|
tag = CMDTAG_SELECT_FOR_UPDATE;
|
|
break;
|
|
default:
|
|
tag = CMDTAG_UNKNOWN;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
tag = CMDTAG_SELECT;
|
|
break;
|
|
case CMD_UPDATE:
|
|
tag = CMDTAG_UPDATE;
|
|
break;
|
|
case CMD_INSERT:
|
|
tag = CMDTAG_INSERT;
|
|
break;
|
|
case CMD_DELETE:
|
|
tag = CMDTAG_DELETE;
|
|
break;
|
|
case CMD_UTILITY:
|
|
tag = CreateCommandTag(stmt->utilityStmt);
|
|
break;
|
|
default:
|
|
elog(WARNING, "unrecognized commandType: %d",
|
|
(int) stmt->commandType);
|
|
tag = CMDTAG_UNKNOWN;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
elog(WARNING, "unrecognized node type: %d",
|
|
(int) nodeTag(parsetree));
|
|
tag = CMDTAG_UNKNOWN;
|
|
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;
|
|
|
|
case T_PLAssignStmt:
|
|
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_AlterTypeStmt:
|
|
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_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_AlterStatsStmt:
|
|
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;
|
|
|
|
default:
|
|
elog(WARNING, "unrecognized node type: %d",
|
|
(int) nodeTag(parsetree));
|
|
lev = LOGSTMT_ALL;
|
|
break;
|
|
}
|
|
|
|
return lev;
|
|
}
|