postgres/src/include/tcop/utility.h
Robert Haas 2eb34ac369 Fix problems with "read only query" checks, and refactor the code.
Previously, check_xact_readonly() was responsible for determining
which types of queries could not be run in a read-only transaction,
standard_ProcessUtility() was responsibility for prohibiting things
which were allowed in read only transactions but not in recovery, and
utility commands were basically prohibited in bulk in parallel mode by
calls to CommandIsReadOnly() in functions.c and spi.c.  This situation
was confusing and error-prone. Accordingly, move all the checks to a
new function ClassifyUtilityCommandAsReadOnly(), which determines the
degree to which a given statement is read only.

In the old code, check_xact_readonly() inadvertently failed to handle
several statement types that actually should have been prohibited,
specifically T_CreatePolicyStmt, T_AlterPolicyStmt, T_CreateAmStmt,
T_CreateStatsStmt, T_AlterStatsStmt, and T_AlterCollationStmt.  As a
result, thes statements were erroneously allowed in read only
transactions, parallel queries, and standby operation. Generally, they
would fail anyway due to some lower-level error check, but we
shouldn't rely on that.  In the new code structure, future omissions
of this type should cause ClassifyUtilityCommandAsReadOnly() to
complain about an unrecognized node type.

As a fringe benefit, this means we can allow certain types of utility
commands in parallel mode, where it's safe to do so. This allows
ALTER SYSTEM, CALL, DO, CHECKPOINT, COPY FROM, EXPLAIN, and SHOW.
It might be possible to allow additional commands with more work
and thought.

Along the way, document the thinking process behind the current set
of checks, as per discussion especially with Peter Eisentraut. There
is some interest in revising some of these rules, but that seems
like a job for another patch.

Patch by me, reviewed by Tom Lane, Stephen Frost, and Peter
Eisentraut.

Discussion: http://postgr.es/m/CA+TgmoZ_rLqJt5sYkvh+JpQnfX0Y+B2R+qfi820xNih6x-FQOQ@mail.gmail.com
2020-01-16 12:11:31 -05:00

102 lines
3.7 KiB
C

/*-------------------------------------------------------------------------
*
* utility.h
* prototypes for utility.c.
*
*
* Portions Copyright (c) 1996-2020, 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/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 master.
*/
#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, ProcessUtilityContext context,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest, char *completionTag);
extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
extern void ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest, char *completionTag);
extern void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest, char *completionTag);
extern void ProcessUtilityForAlterTable(Node *stmt,
AlterTableUtilityContext *context);
extern bool UtilityReturnsTuples(Node *parsetree);
extern TupleDesc UtilityTupleDescriptor(Node *parsetree);
extern Query *UtilityContainsQuery(Node *parsetree);
extern const char *CreateCommandTag(Node *parsetree);
extern LogStmtLevel GetCommandLogLevel(Node *parsetree);
extern bool CommandIsReadOnly(PlannedStmt *pstmt);
#endif /* UTILITY_H */