*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.133 2002/10/21 19:46:45 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.134 2002/10/21 22:06:18 tgl Exp $
*
* NOTES
* Transaction aborts can now occur two ways:
*
* Autocommit mode is forced by either a true forceCommit
* parameter to me, or a true preventChain parameter to the
- * preceding StartTransactionCommand call. This is needed so
- * that commands like VACUUM can ensure that the right things
- * happen.
+ * preceding StartTransactionCommand call, or a
+ * PreventTransactionChain call during the transaction.
+ * (The parameters could be omitted, but it turns out most
+ * callers of StartTransactionCommand/CommitTransactionCommand
+ * want to force autocommit, so making them all call
+ * PreventTransactionChain would just be extra notation.)
*/
case TBLOCK_DEFAULT:
if (autocommit || forceCommit || suppressChain)
}
}
+/* --------------------------------
+ * PreventTransactionChain
+ *
+ * This routine is to be called by statements that must not run inside
+ * a transaction block, typically because they have non-rollback-able
+ * side effects or do internal commits.
+ *
+ * If we have already started a transaction block, issue an error; also issue
+ * an error if we appear to be running inside a user-defined function (which
+ * could issue more commands and possibly cause a failure after the statement
+ * completes). In autocommit-off mode, we allow the statement if a block is
+ * not already started, and force the statement to be autocommitted despite
+ * the mode.
+ *
+ * stmtNode: pointer to parameter block for statement; this is used in
+ * a very klugy way to determine whether we are inside a function.
+ * stmtType: statement type name for error messages.
+ * --------------------------------
+ */
+void
+PreventTransactionChain(void *stmtNode, const char *stmtType)
+{
+ /*
+ * xact block already started?
+ */
+ if (IsTransactionBlock())
+ {
+ /* translator: %s represents an SQL statement name */
+ elog(ERROR, "%s cannot run inside a transaction block", stmtType);
+ }
+ /*
+ * Are we inside a function call? If the statement's parameter block
+ * was allocated in QueryContext, assume it is an interactive command.
+ * Otherwise assume it is coming from a function.
+ */
+ if (!MemoryContextContains(QueryContext, stmtNode))
+ {
+ /* translator: %s represents an SQL statement name */
+ elog(ERROR, "%s cannot be executed from a function", stmtType);
+ }
+ /* If we got past IsTransactionBlock test, should be in default state */
+ if (CurrentTransactionState->blockState != TBLOCK_DEFAULT)
+ elog(ERROR, "PreventTransactionChain: can't prevent chain");
+ /* okay to set the flag */
+ suppressChain = true;
+ /* If we're in autocommit-off node, generate a notice */
+ if (!autocommit)
+ {
+ /* translator: %s represents an SQL statement name */
+ elog(NOTICE, "%s will be committed automatically", stmtType);
+ }
+}
+
+
/* ----------------------------------------------------------------
* transaction block support
* ----------------------------------------------------------------
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.231 2002/09/22 19:42:50 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.232 2002/10/21 22:06:18 tgl Exp $
*
*
* INTERFACE ROUTINES
Relation rel;
/* Open relation for processing, and grab exclusive access on it. */
-
rel = heap_open(rid, AccessExclusiveLock);
- /*
- * TRUNCATE TABLE within a transaction block is dangerous, because if
- * the transaction is later rolled back we have no way to undo
- * truncation of the relation's physical file. Disallow it except for
- * a rel created in the current xact (which would be deleted on abort,
- * anyway).
- */
- if (IsTransactionBlock() && !rel->rd_isnew)
- elog(ERROR, "TRUNCATE TABLE cannot run inside a transaction block");
-
/*
* Release any buffers associated with this relation. If they're
* dirty, they're just dropped without bothering to flush to disk.
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.201 2002/09/27 15:05:23 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.202 2002/10/21 22:06:19 tgl Exp $
*
*
* INTERFACE ROUTINES
Oid heapId;
bool old;
- /*
- * REINDEX within a transaction block is dangerous, because if the
- * transaction is later rolled back we have no way to undo truncation
- * of the index's physical file. Disallow it.
- *
- * XXX if we're not doing an inplace rebuild, wouldn't this be okay?
- */
- if (IsTransactionBlock())
- elog(ERROR, "REINDEX cannot run inside a transaction block");
-
/*
* Open our index relation and get an exclusive lock on it.
*
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.105 2002/09/04 20:31:15 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.106 2002/10/21 22:06:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
}
/* don't call this in a transaction block */
- if (IsTransactionBlock())
- elog(ERROR, "CREATE DATABASE: may not be called in a transaction block");
+ PreventTransactionChain((void *) stmt, "CREATE DATABASE");
/*
* Check for db name conflict. There is a race condition here, since
if (strcmp(dbname, DatabaseName) == 0)
elog(ERROR, "DROP DATABASE: cannot be executed on the currently open database");
- if (IsTransactionBlock())
- elog(ERROR, "DROP DATABASE: may not be called in a transaction block");
+ PreventTransactionChain((void *) dbname, "DROP DATABASE");
/*
* Obtain exclusive lock on pg_database. We need this to ensure that
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.91 2002/10/19 20:15:08 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.92 2002/10/21 22:06:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
Oid indOid;
HeapTuple tuple;
- bool overwrite = false;
+ bool overwrite;
- /*
- * REINDEX within a transaction block is dangerous, because if the
- * transaction is later rolled back we have no way to undo truncation
- * of the index's physical file. Disallow it.
- */
- if (IsTransactionBlock())
- elog(ERROR, "REINDEX cannot run inside a BEGIN/END block");
+ /* Choose in-place-or-not mode */
+ overwrite = IsIgnoringSystemIndexes();
indOid = RangeVarGetRelid(indexRelation, false);
tuple = SearchSysCache(RELOID,
ReleaseSysCache(tuple);
- if (IsIgnoringSystemIndexes())
- overwrite = true;
+ /*
+ * In-place REINDEX within a transaction block is dangerous, because
+ * if the transaction is later rolled back we have no way to undo
+ * truncation of the index's physical file. Disallow it.
+ */
+ if (overwrite)
+ PreventTransactionChain((void *) indexRelation, "REINDEX");
+
if (!reindex_index(indOid, force, overwrite))
elog(WARNING, "index \"%s\" wasn't reindexed", indexRelation->relname);
}
Oid heapOid;
char relkind;
- /*
- * REINDEX within a transaction block is dangerous, because if the
- * transaction is later rolled back we have no way to undo truncation
- * of the index's physical file. Disallow it.
- */
- if (IsTransactionBlock())
- elog(ERROR, "REINDEX cannot run inside a BEGIN/END block");
-
heapOid = RangeVarGetRelid(relation, false);
relkind = get_rel_relkind(heapOid);
elog(ERROR, "relation \"%s\" is of type \"%c\"",
relation->relname, relkind);
+ /*
+ * In-place REINDEX within a transaction block is dangerous, because
+ * if the transaction is later rolled back we have no way to undo
+ * truncation of the index's physical file. Disallow it.
+ *
+ * XXX we assume that in-place reindex will only be done if
+ * IsIgnoringSystemIndexes() is true.
+ */
+ if (IsIgnoringSystemIndexes())
+ PreventTransactionChain((void *) relation, "REINDEX");
+
if (!reindex_relation(heapOid, force))
elog(WARNING, "table \"%s\" wasn't reindexed", relation->relname);
}
* transaction, then our commit- and start-transaction-command calls
* would not have the intended effect!
*/
- if (IsTransactionBlock())
- elog(ERROR, "REINDEX DATABASE cannot run inside a BEGIN/END block");
-
- /* Running this from a function would free the function context */
- if (!MemoryContextContains(QueryContext, (void *) dbname))
- elog(ERROR, "REINDEX DATABASE cannot be executed from a function");
+ PreventTransactionChain((void *) dbname, "REINDEX");
/*
* Create a memory context that will survive forced transaction
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.49 2002/10/21 20:31:51 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.50 2002/10/21 22:06:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (!pg_class_ownercheck(relid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
+ /*
+ * Truncate within a transaction block is dangerous, because if
+ * the transaction is later rolled back we have no way to undo
+ * truncation of the relation's physical file. Disallow it except for
+ * a rel created in the current xact (which would be deleted on abort,
+ * anyway).
+ */
+ if (!rel->rd_isnew)
+ PreventTransactionChain((void *) relation, "TRUNCATE TABLE");
+
/*
* Don't allow truncate on temp tables of other backends ... their
* local buffer manager is not going to cope.
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.242 2002/10/19 20:15:09 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.243 2002/10/21 22:06:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* user's transaction too, which would certainly not be the desired
* behavior.
*/
- if (vacstmt->vacuum && IsTransactionBlock())
- elog(ERROR, "%s cannot run inside a BEGIN/END block", stmttype);
-
- /* Running VACUUM from a function would free the function context */
- if (vacstmt->vacuum && !MemoryContextContains(QueryContext, vacstmt))
- elog(ERROR, "%s cannot be executed from a function", stmttype);
+ if (vacstmt->vacuum)
+ PreventTransactionChain((void *) vacstmt, stmttype);
/*
* Send info about dead objects to the statistics collector
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.252 2002/10/20 00:31:53 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.253 2002/10/21 22:06:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (stmt->forUpdate)
elog(ERROR, "DECLARE/UPDATE is not supported"
"\n\tCursors must be READ ONLY");
-
- /*
- * 15 august 1991 -- since 3.0 postgres does locking right, we
- * discovered that portals were violating locking protocol. portal
- * locks cannot span xacts. as a short-term fix, we installed the
- * check here. -- mao
- */
- if (!IsTransactionBlock())
- elog(ERROR, "DECLARE CURSOR may only be used in begin/end transaction blocks");
-
qry->into = makeNode(RangeVar);
qry->into->relname = stmt->portalname;
qry->isPortal = TRUE;
if (forUpdate)
elog(ERROR, "DECLARE/UPDATE is not supported"
"\n\tCursors must be READ ONLY");
-
- /*
- * 15 august 1991 -- since 3.0 postgres does locking right, we
- * discovered that portals were violating locking protocol. portal
- * locks cannot span xacts. as a short-term fix, we installed the
- * check here. -- mao
- */
- if (!IsTransactionBlock())
- elog(ERROR, "DECLARE CURSOR may only be used in begin/end transaction blocks");
-
qry->into = makeNode(RangeVar);
qry->into->relname = portalname;
qry->isPortal = TRUE;
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: xact.h,v 1.45 2002/08/30 22:18:07 tgl Exp $
+ * $Id: xact.h,v 1.46 2002/10/21 22:06:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern bool IsTransactionBlock(void);
extern void UserAbortTransactionBlock(void);
extern void AbortOutOfAnyTransaction(void);
+extern void PreventTransactionChain(void *stmtNode, const char *stmtType);
extern void RecordTransactionCommit(void);