* Copyright (c) 2000-2010, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.89 2010/02/17 03:10:33 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.90 2010/02/20 21:24:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
return BootstrapTransactionId;
}
+ /* safety check, we should never get this far in a HS slave */
+ if (RecoveryInProgress())
+ elog(ERROR, "cannot assign TransactionIds during recovery");
+
LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
xid = ShmemVariableCache->nextXid;
{
Oid result;
+ /* safety check, we should never get this far in a HS slave */
+ if (RecoveryInProgress())
+ elog(ERROR, "cannot assign OIDs during recovery");
+
LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
/*
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.287 2010/02/17 04:19:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.288 2010/02/20 21:24:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
bool isSubXact = (s->parent != NULL);
ResourceOwner currentOwner;
- if (RecoveryInProgress())
- elog(ERROR, "cannot assign TransactionIds during recovery");
-
/* Assert that caller didn't screw up */
Assert(!TransactionIdIsValid(s->transactionId));
Assert(s->state == TRANS_INPROGRESS);
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.123 2010/02/14 18:42:13 rhaas Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.124 2010/02/20 21:24:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
errmsg("permission denied to create temporary tables in database \"%s\"",
get_database_name(MyDatabaseId))));
+ /*
+ * Do not allow a Hot Standby slave session to make temp tables. Aside
+ * from problems with modifying the system catalogs, there is a naming
+ * conflict: pg_temp_N belongs to the session with BackendId N on the
+ * master, not to a slave session with the same BackendId. We should
+ * not be able to get here anyway due to XactReadOnly checks, but let's
+ * just make real sure. Note that this also backstops various operations
+ * that allow XactReadOnly transactions to modify temp tables; they'd need
+ * RecoveryInProgress checks if not for this.
+ */
+ if (RecoveryInProgress())
+ ereport(ERROR,
+ (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
+ errmsg("cannot create temporary tables during recovery")));
+
snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId);
namespaceId = GetSysCacheOid1(NAMESPACENAME,
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.153 2010/02/17 16:54:06 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.154 2010/02/20 21:24:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
else
payload = text_to_cstring(PG_GETARG_TEXT_PP(1));
+ /* For NOTIFY as a statement, this is checked in ProcessUtility */
+ PreventCommandDuringRecovery("NOTIFY");
+
Async_Notify(channel, payload);
PG_RETURN_VOID();
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.324 2010/02/08 04:33:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.325 2010/02/20 21:24:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* check read-only transaction */
if (XactReadOnly && is_from && !cstate->rel->rd_islocaltemp)
- ereport(ERROR,
- (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
- errmsg("transaction is read-only")));
+ PreventCommandIfReadOnly("COPY FROM");
/* Don't allow COPY w/ OIDs to or from a table without them */
if (cstate->oids && !cstate->rel->rd_rel->relhasoids)
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/lockcmds.c,v 1.27 2010/01/02 16:57:37 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/lockcmds.c,v 1.28 2010/02/20 21:24:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* This test must match the restrictions defined in LockAcquire()
*/
if (lockstmt->mode > RowExclusiveLock)
- PreventCommandDuringRecovery();
+ PreventCommandDuringRecovery("LOCK TABLE");
LockTableRecurse(reloid, relation,
lockstmt->mode, lockstmt->nowait, recurse);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.167 2010/02/19 06:29:19 heikki Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.168 2010/02/20 21:24:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
rescnt = 0;
bool logit = false;
- /* nextval() writes to database and must be prevented during recovery */
- PreventCommandDuringRecovery();
-
/* open and AccessShareLock sequence */
init_sequence(relid, &elm, &seqrel);
errmsg("permission denied for sequence %s",
RelationGetRelationName(seqrel))));
+ /* read-only transactions may only modify temp sequences */
+ if (!seqrel->rd_islocaltemp)
+ PreventCommandIfReadOnly("nextval()");
+
if (elm->last != elm->cached) /* some numbers were cached */
{
Assert(elm->last_valid);
Buffer buf;
Form_pg_sequence seq;
- /* setval() writes to database and must be prevented during recovery */
- PreventCommandDuringRecovery();
-
/* open and AccessShareLock sequence */
init_sequence(relid, &elm, &seqrel);
errmsg("permission denied for sequence %s",
RelationGetRelationName(seqrel))));
+ /* read-only transactions may only modify temp sequences */
+ if (!seqrel->rd_islocaltemp)
+ PreventCommandIfReadOnly("setval()");
+
/* lock page' buffer and read tuple */
seq = read_info(elm, seqrel, &buf);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.346 2010/02/09 21:43:30 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.347 2010/02/20 21:24:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/smgr.h"
+#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
/*
* Check that the query does not imply any writes to non-temp tables.
+ *
+ * Note: in a Hot Standby slave this would need to reject writes to temp
+ * tables as well; but an HS slave can't have created any temp tables
+ * in the first place, so no need to check that.
*/
static void
ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
/*
* CREATE TABLE AS or SELECT INTO?
*
- * XXX should we allow this if the destination is temp?
+ * XXX should we allow this if the destination is temp? Considering
+ * that it would still require catalog changes, probably not.
*/
if (plannedstmt->intoClause != NULL)
- goto fail;
+ PreventCommandIfReadOnly(CreateCommandTag((Node *) plannedstmt));
/* Fail if write permissions are requested on any non-temp table */
foreach(l, plannedstmt->rtable)
if (isTempNamespace(get_rel_namespace(rte->relid)))
continue;
- goto fail;
+ PreventCommandIfReadOnly(CreateCommandTag((Node *) plannedstmt));
}
-
- return;
-
-fail:
- ereport(ERROR,
- (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
- errmsg("transaction is read-only")));
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.333 2010/02/16 22:34:50 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.334 2010/02/20 21:24:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* Note: Commands that need to do more complicated checking are handled
* elsewhere, in particular COPY and plannable statements do their own
- * checking.
+ * checking. However they should all call PreventCommandIfReadOnly to
+ * actually throw the error.
*/
switch (nodeTag(parsetree))
case T_AlterUserMappingStmt:
case T_DropUserMappingStmt:
case T_AlterTableSpaceOptionsStmt:
- ereport(ERROR,
- (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
- errmsg("transaction is read-only")));
+ PreventCommandIfReadOnly(CreateCommandTag(parsetree));
break;
default:
/* do nothing */
}
}
+/*
+ * PreventCommandIfReadOnly: throw error if XactReadOnly
+ *
+ * This is useful mainly to ensure consistency of the error message wording;
+ * most callers have checked XactReadOnly for themselves.
+ */
+void
+PreventCommandIfReadOnly(const char *cmdname)
+{
+ if (XactReadOnly)
+ ereport(ERROR,
+ (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
+ /* translator: %s is name of a SQL command, eg CREATE */
+ errmsg("cannot execute %s in a read-only transaction",
+ cmdname)));
+}
+
+/*
+ * PreventCommandDuringRecovery: throw error if RecoveryInProgress
+ *
+ * The majority of operations that are unsafe in a Hot Standby slave
+ * 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
break;
case TRANS_STMT_PREPARE:
- PreventCommandDuringRecovery();
+ PreventCommandDuringRecovery("PREPARE TRANSACTION");
if (!PrepareTransactionBlock(stmt->gid))
{
/* report unsuccessful commit in completionTag */
break;
case TRANS_STMT_COMMIT_PREPARED:
- PreventCommandDuringRecovery();
PreventTransactionChain(isTopLevel, "COMMIT PREPARED");
+ PreventCommandDuringRecovery("COMMIT PREPARED");
FinishPreparedTransaction(stmt->gid, true);
break;
case TRANS_STMT_ROLLBACK_PREPARED:
- PreventCommandDuringRecovery();
PreventTransactionChain(isTopLevel, "ROLLBACK PREPARED");
+ PreventCommandDuringRecovery("ROLLBACK PREPARED");
FinishPreparedTransaction(stmt->gid, false);
break;
break;
case T_GrantStmt:
- PreventCommandDuringRecovery();
ExecuteGrantStmt((GrantStmt *) parsetree);
break;
{
NotifyStmt *stmt = (NotifyStmt *) parsetree;
- PreventCommandDuringRecovery();
+ PreventCommandDuringRecovery("NOTIFY");
Async_Notify(stmt->conditionname, stmt->payload);
}
break;
{
ListenStmt *stmt = (ListenStmt *) parsetree;
- PreventCommandDuringRecovery();
+ PreventCommandDuringRecovery("LISTEN");
CheckRestrictedOperation("LISTEN");
Async_Listen(stmt->conditionname);
}
{
UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
- PreventCommandDuringRecovery();
+ PreventCommandDuringRecovery("UNLISTEN");
CheckRestrictedOperation("UNLISTEN");
if (stmt->conditionname)
Async_Unlisten(stmt->conditionname);
break;
case T_ClusterStmt:
- PreventCommandDuringRecovery();
+ /* we choose to allow this during "read only" transactions */
+ PreventCommandDuringRecovery("CLUSTER");
cluster((ClusterStmt *) parsetree, isTopLevel);
break;
case T_VacuumStmt:
- PreventCommandDuringRecovery();
+ /* we choose to allow this during "read only" transactions */
+ PreventCommandDuringRecovery("VACUUM");
vacuum((VacuumStmt *) parsetree, InvalidOid, true, NULL, false,
isTopLevel);
break;
* using various forms of replication.
*/
RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_WAIT |
- (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
+ (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
break;
case T_ReindexStmt:
{
ReindexStmt *stmt = (ReindexStmt *) parsetree;
- PreventCommandDuringRecovery();
+ /* we choose to allow this during "read only" transactions */
+ PreventCommandDuringRecovery("REINDEX");
switch (stmt->kind)
{
case OBJECT_INDEX:
return lev;
}
-
-void
-PreventCommandDuringRecovery(void)
-{
- if (RecoveryInProgress())
- ereport(ERROR,
- (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
- errmsg("cannot be executed during recovery")));
-}
* Author: Jan Wieck, Afilias USA INC.
* 64-bit txids: Marko Kreen, Skype Technologies
*
- * $PostgreSQL: pgsql/src/backend/utils/adt/txid.c,v 1.11 2010/01/07 04:53:34 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/txid.c,v 1.12 2010/02/20 21:24:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* return a valid current xid, so we should not change
* this to return NULL or similar invalid xid.
*/
- PreventCommandDuringRecovery();
+ PreventCommandDuringRecovery("txid_current()");
load_xid_epoch(&state);
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.218 2010/02/07 20:48:13 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.219 2010/02/20 21:24:02 tgl Exp $
*
* NOTES
* some of the information in this file should be moved to other files.
extern void check_stack_depth(void);
/* in tcop/utility.c */
-extern void PreventCommandDuringRecovery(void);
+extern void PreventCommandIfReadOnly(const char *cmdname);
+extern void PreventCommandDuringRecovery(const char *cmdname);
/* in utils/misc/guc.c */
extern int trace_recovery_messages;
CREATE TEMPORARY TABLE temptest (a int);
SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY;
DROP TABLE writetest; -- fail
-ERROR: transaction is read-only
+ERROR: cannot execute DROP TABLE in a read-only transaction
INSERT INTO writetest VALUES (1); -- fail
-ERROR: transaction is read-only
+ERROR: cannot execute INSERT in a read-only transaction
SELECT * FROM writetest; -- ok
a
---
UPDATE temptest SET a = 0 FROM writetest WHERE temptest.a = 1 AND writetest.a = temptest.a; -- ok
PREPARE test AS UPDATE writetest SET a = 0; -- ok
EXECUTE test; -- fail
-ERROR: transaction is read-only
+ERROR: cannot execute UPDATE in a read-only transaction
SELECT * FROM writetest, temptest; -- ok
a | a
---+---
(0 rows)
CREATE TABLE test AS SELECT * FROM writetest; -- fail
-ERROR: transaction is read-only
+ERROR: cannot execute SELECT INTO in a read-only transaction
START TRANSACTION READ WRITE;
DROP TABLE writetest; -- ok
COMMIT;