mirror of
https://github.com/postgres/postgres.git
synced 2026-04-27 17:16:43 -04:00
In the "simple Query" code path, it's fine for parse analysis or execution of a utility statement to scribble on the statement's node tree, since that'll just be thrown away afterwards. However it's not fine if the node tree is in the plan cache, as then it'd be corrupted for subsequent executions. Up to now we've dealt with that by having individual utility-statement functions apply copyObject() if they were going to modify the tree. But that's prone to errors of omission. Bug #17053 from Charles Samborski shows that CREATE/ALTER DOMAIN didn't get this memo, and can crash if executed repeatedly from plan cache. In the back branches, we'll just apply a narrow band-aid for that, but in HEAD it seems prudent to have a more principled fix that will close off the possibility of other similar bugs in future. Hence, let's hoist the responsibility for doing copyObject up into ProcessUtility from its children, thus ensuring that it happens for all utility statement types. Also, modify ProcessUtility's API so that its callers can tell it whether a copy step is necessary. It turns out that in all cases, the immediate caller knows whether the node tree is transient, so this doesn't involve a huge amount of code thrashing. In this way, while we lose a little bit in the execute-from-cache code path due to sometimes copying node trees that wouldn't be mutated anyway, we gain something in the simple-Query code path by not copying throwaway node trees. Statements that are complex enough to be expensive to copy are almost certainly ones that would have to be copied anyway, so the loss in the cache code path shouldn't be much. (Note that this whole problem applies only to utility statements. Optimizable statements don't have the issue because we long ago made the executor treat Plan trees as read-only. Perhaps someday we will make utility statement execution act likewise, but I'm not holding my breath.) Discussion: https://postgr.es/m/931771.1623893989@sss.pgh.pa.us Discussion: https://postgr.es/m/17053-3ca3f501bbc212b4@postgresql.org
112 lines
3.9 KiB
C
112 lines
3.9 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* utility.h
|
|
* prototypes for utility.c.
|
|
*
|
|
*
|
|
* Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* src/include/tcop/utility.h
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#ifndef UTILITY_H
|
|
#define UTILITY_H
|
|
|
|
#include "tcop/cmdtag.h"
|
|
#include "tcop/tcopprot.h"
|
|
|
|
typedef enum
|
|
{
|
|
PROCESS_UTILITY_TOPLEVEL, /* toplevel interactive command */
|
|
PROCESS_UTILITY_QUERY, /* a complete query, but not toplevel */
|
|
PROCESS_UTILITY_QUERY_NONATOMIC, /* a complete query, nonatomic
|
|
* execution context */
|
|
PROCESS_UTILITY_SUBCOMMAND /* a portion of a query */
|
|
} ProcessUtilityContext;
|
|
|
|
/* Info needed when recursing from ALTER TABLE */
|
|
typedef struct AlterTableUtilityContext
|
|
{
|
|
PlannedStmt *pstmt; /* PlannedStmt for outer ALTER TABLE command */
|
|
const char *queryString; /* its query string */
|
|
Oid relid; /* OID of ALTER's target table */
|
|
ParamListInfo params; /* any parameters available to ALTER TABLE */
|
|
QueryEnvironment *queryEnv; /* execution environment for ALTER TABLE */
|
|
} AlterTableUtilityContext;
|
|
|
|
/*
|
|
* These constants are used to describe the extent to which a particular
|
|
* command is read-only.
|
|
*
|
|
* COMMAND_OK_IN_READ_ONLY_TXN means that the command is permissible even when
|
|
* XactReadOnly is set. This bit should be set for commands that don't change
|
|
* the state of the database (data or schema) in a way that would affect the
|
|
* output of pg_dump.
|
|
*
|
|
* COMMAND_OK_IN_PARALLEL_MODE means that the command is permissible even
|
|
* when in parallel mode. Writing tuples is forbidden, as is anything that
|
|
* might confuse cooperating processes.
|
|
*
|
|
* COMMAND_OK_IN_RECOVERY means that the command is permissible even when in
|
|
* recovery. It can't write WAL, nor can it do things that would imperil
|
|
* replay of future WAL received from the primary.
|
|
*/
|
|
#define COMMAND_OK_IN_READ_ONLY_TXN 0x0001
|
|
#define COMMAND_OK_IN_PARALLEL_MODE 0x0002
|
|
#define COMMAND_OK_IN_RECOVERY 0x0004
|
|
|
|
/*
|
|
* We say that a command is strictly read-only if it is sufficiently read-only
|
|
* for all purposes. For clarity, we also have a constant for commands that are
|
|
* in no way read-only.
|
|
*/
|
|
#define COMMAND_IS_STRICTLY_READ_ONLY \
|
|
(COMMAND_OK_IN_READ_ONLY_TXN | COMMAND_OK_IN_RECOVERY | \
|
|
COMMAND_OK_IN_PARALLEL_MODE)
|
|
#define COMMAND_IS_NOT_READ_ONLY 0
|
|
|
|
/* Hook for plugins to get control in ProcessUtility() */
|
|
typedef void (*ProcessUtility_hook_type) (PlannedStmt *pstmt,
|
|
const char *queryString,
|
|
bool readOnlyTree,
|
|
ProcessUtilityContext context,
|
|
ParamListInfo params,
|
|
QueryEnvironment *queryEnv,
|
|
DestReceiver *dest, QueryCompletion *qc);
|
|
extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
|
|
|
|
extern void ProcessUtility(PlannedStmt *pstmt, const char *queryString,
|
|
bool readOnlyTree,
|
|
ProcessUtilityContext context, ParamListInfo params,
|
|
QueryEnvironment *queryEnv,
|
|
DestReceiver *dest, QueryCompletion *qc);
|
|
extern void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
|
|
bool readOnlyTree,
|
|
ProcessUtilityContext context, ParamListInfo params,
|
|
QueryEnvironment *queryEnv,
|
|
DestReceiver *dest, QueryCompletion *qc);
|
|
|
|
extern void ProcessUtilityForAlterTable(Node *stmt,
|
|
AlterTableUtilityContext *context);
|
|
|
|
extern bool UtilityReturnsTuples(Node *parsetree);
|
|
|
|
extern TupleDesc UtilityTupleDescriptor(Node *parsetree);
|
|
|
|
extern Query *UtilityContainsQuery(Node *parsetree);
|
|
|
|
extern CommandTag CreateCommandTag(Node *parsetree);
|
|
|
|
static inline const char *
|
|
CreateCommandName(Node *parsetree)
|
|
{
|
|
return GetCommandTagName(CreateCommandTag(parsetree));
|
|
}
|
|
|
|
extern LogStmtLevel GetCommandLogLevel(Node *parsetree);
|
|
|
|
extern bool CommandIsReadOnly(PlannedStmt *pstmt);
|
|
|
|
#endif /* UTILITY_H */
|