@ -75,6 +75,7 @@
ProcessUtility_hook_type ProcessUtility_hook = NULL ;
ProcessUtility_hook_type ProcessUtility_hook = NULL ;
/* local function declarations */
/* local function declarations */
static int ClassifyUtilityCommandAsReadOnly ( Node * parsetree ) ;
static void ProcessUtilitySlow ( ParseState * pstate ,
static void ProcessUtilitySlow ( ParseState * pstate ,
PlannedStmt * pstmt ,
PlannedStmt * pstmt ,
const char * queryString ,
const char * queryString ,
@ -124,106 +125,269 @@ CommandIsReadOnly(PlannedStmt *pstmt)
}
}
/*
/*
* check_xact_readonly : is a utility command read - only ?
* Determine the degree to which a utility command is read only .
*
*
* Here we use the loose rules of XactReadOnly mode : no permanent effects
* Note the definitions of the relevant flags in src / include / utility / tcop . h .
* on the database are allowed .
*/
*/
stat ic void
int
check_xact_reado nly( Node * parsetree )
ClassifyUtilityCommandAsReadO nly( Node * parsetree )
{
{
/* Only perform the check if we have a reason to do so. */
if ( ! XactReadOnly & & ! IsInParallelMode ( ) )
return ;
/*
* Note : Commands that need to do more complicated checking are handled
* elsewhere , in particular COPY and plannable statements do their own
* checking . However they should all call PreventCommandIfReadOnly or
* PreventCommandIfParallelMode to actually throw the error .
*/
switch ( nodeTag ( parsetree ) )
switch ( nodeTag ( parsetree ) )
{
{
case T_AlterDatabase Stmt :
case T_AlterCollationStmt :
case T_AlterDatabaseSetStmt :
case T_AlterDatabaseSetStmt :
case T_AlterDatabaseStmt :
case T_AlterDefaultPrivilegesStmt :
case T_AlterDomainStmt :
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_AlterFunctionStmt :
case T_AlterRoleStmt :
case T_AlterRoleSetStmt :
case T_AlterObjectDependsStmt :
case T_AlterObjectDependsStmt :
case T_AlterObjectSchemaStmt :
case T_AlterObjectSchemaStmt :
case T_AlterOwnerStmt :
case T_AlterOpFamily Stmt :
case T_AlterOperatorStmt :
case T_AlterOperatorStmt :
case T_AlterOwnerStmt :
case T_AlterPolicyStmt :
case T_AlterPublicationStmt :
case T_AlterRoleSetStmt :
case T_AlterRoleStmt :
case T_AlterSeqStmt :
case T_AlterSeqStmt :
case T_AlterStatsStmt :
case T_AlterSubscriptionStmt :
case T_AlterTSConfigurationStmt :
case T_AlterTSDictionaryStmt :
case T_AlterTableMoveAllStmt :
case T_AlterTableMoveAllStmt :
case T_AlterTableSpaceOptionsStmt :
case T_AlterTableStmt :
case T_AlterTableStmt :
case T_RenameStmt :
case T_AlterUserMapping Stmt :
case T_CommentStmt :
case T_CommentStmt :
case T_DefineStmt :
case T_CompositeTypeStmt :
case T_CreateAmStmt :
case T_CreateCastStmt :
case T_CreateCastStmt :
case T_CreateEventTrigStmt :
case T_AlterEventTrigStmt :
case T_CreateConversionStmt :
case T_CreateConversionStmt :
case T_CreatedbStmt :
case T_CreateDomainStmt :
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_CreateFunctionStmt :
case T_CreateRoleStmt :
case T_IndexStmt :
case T_CreatePLangStmt :
case T_CreateOpClassStmt :
case T_CreateOpClassStmt :
case T_CreateOpFamilyStmt :
case T_CreateOpFamilyStmt :
case T_AlterOpFamilyStmt :
case T_CreatePLangStmt :
case T_RuleStmt :
case T_CreatePolicyStmt :
case T_CreatePublicationStmt :
case T_CreateRangeStmt :
case T_CreateRoleStmt :
case T_CreateSchemaStmt :
case T_CreateSchemaStmt :
case T_CreateSeqStmt :
case T_CreateSeqStmt :
case T_CreateStatsStmt :
case T_CreateStmt :
case T_CreateStmt :
case T_CreateSubscriptionStmt :
case T_CreateTableAsStmt :
case T_CreateTableAsStmt :
case T_RefreshMatViewStmt :
case T_CreateTableSpaceStmt :
case T_CreateTableSpaceStmt :
case T_CreateTransformStmt :
case T_CreateTransformStmt :
case T_CreateTrigStmt :
case T_CreateTrigStmt :
case T_CompositeType Stmt :
case T_CreateUserMapping Stmt :
case T_CreateEnum Stmt :
case T_Createdb Stmt :
case T_CreateRang eStmt :
case T_Defin eStmt :
case T_AlterEnum Stmt :
case T_DropOwned Stmt :
case T_View Stmt :
case T_DropRole Stmt :
case T_DropStmt :
case T_DropStmt :
case T_Dropdb Stmt :
case T_DropSubscription Stmt :
case T_DropTableSpaceStmt :
case T_DropTableSpaceStmt :
case T_DropRoleStmt :
case T_GrantStmt :
case T_GrantRoleStmt :
case T_AlterDefaultPrivilegesStmt :
case T_TruncateStmt :
case T_DropOwnedStmt :
case T_ReassignOwnedStmt :
case T_AlterTSDictionaryStmt :
case T_AlterTSConfigurationStmt :
case T_CreateExtensionStmt :
case T_AlterExtensionStmt :
case T_AlterExtensionContentsStmt :
case T_CreateFdwStmt :
case T_AlterFdwStmt :
case T_CreateForeignServerStmt :
case T_AlterForeignServerStmt :
case T_CreateUserMappingStmt :
case T_AlterUserMappingStmt :
case T_DropUserMappingStmt :
case T_DropUserMappingStmt :
case T_AlterTableSpaceOptionsStmt :
case T_DropdbStmt :
case T_CreateForeignTableStmt :
case T_GrantRoleStmt :
case T_GrantStmt :
case T_ImportForeignSchemaStmt :
case T_ImportForeignSchemaStmt :
case T_IndexStmt :
case T_ReassignOwnedStmt :
case T_RefreshMatViewStmt :
case T_RenameStmt :
case T_RuleStmt :
case T_SecLabelStmt :
case T_SecLabelStmt :
case T_CreatePublicationStmt :
case T_TruncateStmt :
case T_AlterPublicationStmt :
case T_ViewStmt :
case T_CreateSubscriptionStmt :
{
case T_AlterSubscriptionStmt :
/* DDL is not read-only, and neither is TRUNCATE. */
case T_DropSubscriptionStmt :
return COMMAND_IS_NOT_READ_ONLY ;
PreventCommandIfReadOnly ( CreateCommandTag ( parsetree ) ) ;
}
PreventCommandIfParallelMode ( CreateCommandTag ( parsetree ) ) ;
break ;
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 change
* 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 ;
}
}
default :
default :
/* do nothing */
elog ( ERROR , " unrecognized node type: %d " ,
( int ) nodeTag ( parsetree ) ) ;
break ;
break ;
}
}
}
}
@ -231,8 +395,8 @@ check_xact_readonly(Node *parsetree)
/*
/*
* PreventCommandIfReadOnly : throw error if XactReadOnly
* PreventCommandIfReadOnly : throw error if XactReadOnly
*
*
* This is useful main ly to ensure consistency of the error message wording ;
* This is useful part ly to ensure consistency of the error message wording ;
* most callers have checked XactReadOnly for themselves .
* some callers have checked XactReadOnly for themselves .
*/
*/
void
void
PreventCommandIfReadOnly ( const char * cmdname )
PreventCommandIfReadOnly ( const char * cmdname )
@ -249,8 +413,8 @@ PreventCommandIfReadOnly(const char *cmdname)
* PreventCommandIfParallelMode : throw error if current ( sub ) transaction is
* PreventCommandIfParallelMode : throw error if current ( sub ) transaction is
* in parallel mode .
* in parallel mode .
*
*
* This is useful main ly to ensure consistency of the error message wording ;
* This is useful part ly to ensure consistency of the error message wording ;
* most callers have checked IsInParallelMode ( ) for themselves .
* some callers have checked IsInParallelMode ( ) for themselves .
*/
*/
void
void
PreventCommandIfParallelMode ( const char * cmdname )
PreventCommandIfParallelMode ( const char * cmdname )
@ -385,11 +549,25 @@ standard_ProcessUtility(PlannedStmt *pstmt,
bool isTopLevel = ( context = = PROCESS_UTILITY_TOPLEVEL ) ;
bool isTopLevel = ( context = = PROCESS_UTILITY_TOPLEVEL ) ;
bool isAtomicContext = ( ! ( context = = PROCESS_UTILITY_TOPLEVEL | | context = = PROCESS_UTILITY_QUERY_NONATOMIC ) | | IsTransactionBlock ( ) ) ;
bool isAtomicContext = ( ! ( context = = PROCESS_UTILITY_TOPLEVEL | | context = = PROCESS_UTILITY_QUERY_NONATOMIC ) | | IsTransactionBlock ( ) ) ;
ParseState * pstate ;
ParseState * pstate ;
int readonly_flags ;
/* This can recurse, so check for excessive recursion */
/* This can recurse, so check for excessive recursion */
check_stack_depth ( ) ;
check_stack_depth ( ) ;
check_xact_readonly ( parsetree ) ;
/* Prohibit read/write commands in read-only states. */
readonly_flags = ClassifyUtilityCommandAsReadOnly ( parsetree ) ;
if ( readonly_flags ! = COMMAND_IS_STRICTLY_READ_ONLY & &
( XactReadOnly | | IsInParallelMode ( ) ) )
{
const char * commandtag = CreateCommandTag ( parsetree ) ;
if ( ( readonly_flags & COMMAND_OK_IN_READ_ONLY_TXN ) = = 0 )
PreventCommandIfReadOnly ( commandtag ) ;
if ( ( readonly_flags & COMMAND_OK_IN_PARALLEL_MODE ) = = 0 )
PreventCommandIfParallelMode ( commandtag ) ;
if ( ( readonly_flags & COMMAND_OK_IN_RECOVERY ) = = 0 )
PreventCommandDuringRecovery ( commandtag ) ;
}
if ( completionTag )
if ( completionTag )
completionTag [ 0 ] = ' \0 ' ;
completionTag [ 0 ] = ' \0 ' ;
@ -449,7 +627,6 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break ;
break ;
case TRANS_STMT_PREPARE :
case TRANS_STMT_PREPARE :
PreventCommandDuringRecovery ( " PREPARE TRANSACTION " ) ;
if ( ! PrepareTransactionBlock ( stmt - > gid ) )
if ( ! PrepareTransactionBlock ( stmt - > gid ) )
{
{
/* report unsuccessful commit in completionTag */
/* report unsuccessful commit in completionTag */
@ -460,13 +637,11 @@ standard_ProcessUtility(PlannedStmt *pstmt,
case TRANS_STMT_COMMIT_PREPARED :
case TRANS_STMT_COMMIT_PREPARED :
PreventInTransactionBlock ( isTopLevel , " COMMIT PREPARED " ) ;
PreventInTransactionBlock ( isTopLevel , " COMMIT PREPARED " ) ;
PreventCommandDuringRecovery ( " COMMIT PREPARED " ) ;
FinishPreparedTransaction ( stmt - > gid , true ) ;
FinishPreparedTransaction ( stmt - > gid , true ) ;
break ;
break ;
case TRANS_STMT_ROLLBACK_PREPARED :
case TRANS_STMT_ROLLBACK_PREPARED :
PreventInTransactionBlock ( isTopLevel , " ROLLBACK PREPARED " ) ;
PreventInTransactionBlock ( isTopLevel , " ROLLBACK PREPARED " ) ;
PreventCommandDuringRecovery ( " ROLLBACK PREPARED " ) ;
FinishPreparedTransaction ( stmt - > gid , false ) ;
FinishPreparedTransaction ( stmt - > gid , false ) ;
break ;
break ;
@ -607,7 +782,6 @@ standard_ProcessUtility(PlannedStmt *pstmt,
{
{
NotifyStmt * stmt = ( NotifyStmt * ) parsetree ;
NotifyStmt * stmt = ( NotifyStmt * ) parsetree ;
PreventCommandDuringRecovery ( " NOTIFY " ) ;
Async_Notify ( stmt - > conditionname , stmt - > payload ) ;
Async_Notify ( stmt - > conditionname , stmt - > payload ) ;
}
}
break ;
break ;
@ -616,7 +790,6 @@ standard_ProcessUtility(PlannedStmt *pstmt,
{
{
ListenStmt * stmt = ( ListenStmt * ) parsetree ;
ListenStmt * stmt = ( ListenStmt * ) parsetree ;
PreventCommandDuringRecovery ( " LISTEN " ) ;
CheckRestrictedOperation ( " LISTEN " ) ;
CheckRestrictedOperation ( " LISTEN " ) ;
Async_Listen ( stmt - > conditionname ) ;
Async_Listen ( stmt - > conditionname ) ;
}
}
@ -626,7 +799,6 @@ standard_ProcessUtility(PlannedStmt *pstmt,
{
{
UnlistenStmt * stmt = ( UnlistenStmt * ) parsetree ;
UnlistenStmt * stmt = ( UnlistenStmt * ) parsetree ;
/* we allow UNLISTEN during recovery, as it's a noop */
CheckRestrictedOperation ( " UNLISTEN " ) ;
CheckRestrictedOperation ( " UNLISTEN " ) ;
if ( stmt - > conditionname )
if ( stmt - > conditionname )
Async_Unlisten ( stmt - > conditionname ) ;
Async_Unlisten ( stmt - > conditionname ) ;
@ -650,22 +822,11 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break ;
break ;
case T_ClusterStmt :
case T_ClusterStmt :
/* we choose to allow this during "read only" transactions */
PreventCommandDuringRecovery ( " CLUSTER " ) ;
/* forbidden in parallel mode due to CommandIsReadOnly */
cluster ( ( ClusterStmt * ) parsetree , isTopLevel ) ;
cluster ( ( ClusterStmt * ) parsetree , isTopLevel ) ;
break ;
break ;
case T_VacuumStmt :
case T_VacuumStmt :
{
ExecVacuum ( pstate , ( VacuumStmt * ) parsetree , isTopLevel ) ;
VacuumStmt * stmt = ( VacuumStmt * ) parsetree ;
/* we choose to allow this during "read only" transactions */
PreventCommandDuringRecovery ( stmt - > is_vacuumcmd ?
" VACUUM " : " ANALYZE " ) ;
/* forbidden in parallel mode due to CommandIsReadOnly */
ExecVacuum ( pstate , stmt , isTopLevel ) ;
}
break ;
break ;
case T_ExplainStmt :
case T_ExplainStmt :
@ -740,7 +901,6 @@ standard_ProcessUtility(PlannedStmt *pstmt,
* outside a transaction block is presumed to be user error .
* outside a transaction block is presumed to be user error .
*/
*/
RequireTransactionBlock ( isTopLevel , " LOCK TABLE " ) ;
RequireTransactionBlock ( isTopLevel , " LOCK TABLE " ) ;
/* forbidden in parallel mode due to CommandIsReadOnly */
LockTableCommand ( ( LockStmt * ) parsetree ) ;
LockTableCommand ( ( LockStmt * ) parsetree ) ;
break ;
break ;
@ -755,13 +915,6 @@ standard_ProcessUtility(PlannedStmt *pstmt,
( errcode ( ERRCODE_INSUFFICIENT_PRIVILEGE ) ,
( errcode ( ERRCODE_INSUFFICIENT_PRIVILEGE ) ,
errmsg ( " must be superuser to do CHECKPOINT " ) ) ) ;
errmsg ( " must be superuser to do CHECKPOINT " ) ) ) ;
/*
* You might think we should have a PreventCommandDuringRecovery ( )
* here , but we interpret a CHECKPOINT command during recovery as
* a request for a restartpoint instead . We allow this since it
* can be a useful way of reducing switchover time when using
* various forms of replication .
*/
RequestCheckpoint ( CHECKPOINT_IMMEDIATE | CHECKPOINT_WAIT |
RequestCheckpoint ( CHECKPOINT_IMMEDIATE | CHECKPOINT_WAIT |
( RecoveryInProgress ( ) ? 0 : CHECKPOINT_FORCE ) ) ;
( RecoveryInProgress ( ) ? 0 : CHECKPOINT_FORCE ) ) ;
break ;
break ;
@ -774,9 +927,6 @@ standard_ProcessUtility(PlannedStmt *pstmt,
PreventInTransactionBlock ( isTopLevel ,
PreventInTransactionBlock ( isTopLevel ,
" REINDEX CONCURRENTLY " ) ;
" REINDEX CONCURRENTLY " ) ;
/* we choose to allow this during "read only" transactions */
PreventCommandDuringRecovery ( " REINDEX " ) ;
/* forbidden in parallel mode due to CommandIsReadOnly */
switch ( stmt - > kind )
switch ( stmt - > kind )
{
{
case REINDEX_OBJECT_INDEX :
case REINDEX_OBJECT_INDEX :