Fix places that were using IsTransactionBlock() as an (inadequate) check
that they'd get to commit immediately on finishing. There's now a centralized routine PreventTransactionChain() that implements the necessary tests.
This commit is contained in:
parent
f724c164d3
commit
200b151615
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* Transaction aborts can now occur two ways:
|
* Transaction aborts can now occur two ways:
|
||||||
@ -1280,9 +1280,12 @@ CommitTransactionCommand(bool forceCommit)
|
|||||||
*
|
*
|
||||||
* Autocommit mode is forced by either a true forceCommit
|
* Autocommit mode is forced by either a true forceCommit
|
||||||
* parameter to me, or a true preventChain parameter to the
|
* parameter to me, or a true preventChain parameter to the
|
||||||
* preceding StartTransactionCommand call. This is needed so
|
* preceding StartTransactionCommand call, or a
|
||||||
* that commands like VACUUM can ensure that the right things
|
* PreventTransactionChain call during the transaction.
|
||||||
* happen.
|
* (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:
|
case TBLOCK_DEFAULT:
|
||||||
if (autocommit || forceCommit || suppressChain)
|
if (autocommit || forceCommit || suppressChain)
|
||||||
@ -1429,6 +1432,60 @@ AbortCurrentTransaction(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --------------------------------
|
||||||
|
* 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
|
* transaction block support
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* INTERFACE ROUTINES
|
||||||
@ -1917,19 +1917,8 @@ heap_truncate(Oid rid)
|
|||||||
Relation rel;
|
Relation rel;
|
||||||
|
|
||||||
/* Open relation for processing, and grab exclusive access on it. */
|
/* Open relation for processing, and grab exclusive access on it. */
|
||||||
|
|
||||||
rel = heap_open(rid, AccessExclusiveLock);
|
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
|
* Release any buffers associated with this relation. If they're
|
||||||
* dirty, they're just dropped without bothering to flush to disk.
|
* dirty, they're just dropped without bothering to flush to disk.
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* INTERFACE ROUTINES
|
||||||
@ -1740,16 +1740,6 @@ reindex_index(Oid indexId, bool force, bool inplace)
|
|||||||
Oid heapId;
|
Oid heapId;
|
||||||
bool old;
|
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.
|
* Open our index relation and get an exclusive lock on it.
|
||||||
*
|
*
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -152,8 +152,7 @@ createdb(const CreatedbStmt *stmt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* don't call this in a transaction block */
|
/* don't call this in a transaction block */
|
||||||
if (IsTransactionBlock())
|
PreventTransactionChain((void *) stmt, "CREATE DATABASE");
|
||||||
elog(ERROR, "CREATE DATABASE: may not be called in a transaction block");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check for db name conflict. There is a race condition here, since
|
* Check for db name conflict. There is a race condition here, since
|
||||||
@ -382,8 +381,7 @@ dropdb(const char *dbname)
|
|||||||
if (strcmp(dbname, DatabaseName) == 0)
|
if (strcmp(dbname, DatabaseName) == 0)
|
||||||
elog(ERROR, "DROP DATABASE: cannot be executed on the currently open database");
|
elog(ERROR, "DROP DATABASE: cannot be executed on the currently open database");
|
||||||
|
|
||||||
if (IsTransactionBlock())
|
PreventTransactionChain((void *) dbname, "DROP DATABASE");
|
||||||
elog(ERROR, "DROP DATABASE: may not be called in a transaction block");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Obtain exclusive lock on pg_database. We need this to ensure that
|
* Obtain exclusive lock on pg_database. We need this to ensure that
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -560,15 +560,10 @@ ReindexIndex(RangeVar *indexRelation, bool force /* currently unused */ )
|
|||||||
{
|
{
|
||||||
Oid indOid;
|
Oid indOid;
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
bool overwrite = false;
|
bool overwrite;
|
||||||
|
|
||||||
/*
|
/* Choose in-place-or-not mode */
|
||||||
* REINDEX within a transaction block is dangerous, because if the
|
overwrite = IsIgnoringSystemIndexes();
|
||||||
* 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");
|
|
||||||
|
|
||||||
indOid = RangeVarGetRelid(indexRelation, false);
|
indOid = RangeVarGetRelid(indexRelation, false);
|
||||||
tuple = SearchSysCache(RELOID,
|
tuple = SearchSysCache(RELOID,
|
||||||
@ -595,8 +590,14 @@ ReindexIndex(RangeVar *indexRelation, bool force /* currently unused */ )
|
|||||||
|
|
||||||
ReleaseSysCache(tuple);
|
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))
|
if (!reindex_index(indOid, force, overwrite))
|
||||||
elog(WARNING, "index \"%s\" wasn't reindexed", indexRelation->relname);
|
elog(WARNING, "index \"%s\" wasn't reindexed", indexRelation->relname);
|
||||||
}
|
}
|
||||||
@ -611,14 +612,6 @@ ReindexTable(RangeVar *relation, bool force)
|
|||||||
Oid heapOid;
|
Oid heapOid;
|
||||||
char relkind;
|
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);
|
heapOid = RangeVarGetRelid(relation, false);
|
||||||
relkind = get_rel_relkind(heapOid);
|
relkind = get_rel_relkind(heapOid);
|
||||||
|
|
||||||
@ -626,6 +619,17 @@ ReindexTable(RangeVar *relation, bool force)
|
|||||||
elog(ERROR, "relation \"%s\" is of type \"%c\"",
|
elog(ERROR, "relation \"%s\" is of type \"%c\"",
|
||||||
relation->relname, relkind);
|
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))
|
if (!reindex_relation(heapOid, force))
|
||||||
elog(WARNING, "table \"%s\" wasn't reindexed", relation->relname);
|
elog(WARNING, "table \"%s\" wasn't reindexed", relation->relname);
|
||||||
}
|
}
|
||||||
@ -666,12 +670,7 @@ ReindexDatabase(const char *dbname, bool force, bool all)
|
|||||||
* transaction, then our commit- and start-transaction-command calls
|
* transaction, then our commit- and start-transaction-command calls
|
||||||
* would not have the intended effect!
|
* would not have the intended effect!
|
||||||
*/
|
*/
|
||||||
if (IsTransactionBlock())
|
PreventTransactionChain((void *) dbname, "REINDEX");
|
||||||
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");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a memory context that will survive forced transaction
|
* Create a memory context that will survive forced transaction
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -375,6 +375,16 @@ TruncateRelation(const RangeVar *relation)
|
|||||||
if (!pg_class_ownercheck(relid, GetUserId()))
|
if (!pg_class_ownercheck(relid, GetUserId()))
|
||||||
aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
|
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
|
* Don't allow truncate on temp tables of other backends ... their
|
||||||
* local buffer manager is not going to cope.
|
* local buffer manager is not going to cope.
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -177,12 +177,8 @@ vacuum(VacuumStmt *vacstmt)
|
|||||||
* user's transaction too, which would certainly not be the desired
|
* user's transaction too, which would certainly not be the desired
|
||||||
* behavior.
|
* behavior.
|
||||||
*/
|
*/
|
||||||
if (vacstmt->vacuum && IsTransactionBlock())
|
if (vacstmt->vacuum)
|
||||||
elog(ERROR, "%s cannot run inside a BEGIN/END block", stmttype);
|
PreventTransactionChain((void *) vacstmt, 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);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send info about dead objects to the statistics collector
|
* Send info about dead objects to the statistics collector
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1631,16 +1631,6 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
|||||||
if (stmt->forUpdate)
|
if (stmt->forUpdate)
|
||||||
elog(ERROR, "DECLARE/UPDATE is not supported"
|
elog(ERROR, "DECLARE/UPDATE is not supported"
|
||||||
"\n\tCursors must be READ ONLY");
|
"\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 = makeNode(RangeVar);
|
||||||
qry->into->relname = stmt->portalname;
|
qry->into->relname = stmt->portalname;
|
||||||
qry->isPortal = TRUE;
|
qry->isPortal = TRUE;
|
||||||
@ -1849,16 +1839,6 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
|
|||||||
if (forUpdate)
|
if (forUpdate)
|
||||||
elog(ERROR, "DECLARE/UPDATE is not supported"
|
elog(ERROR, "DECLARE/UPDATE is not supported"
|
||||||
"\n\tCursors must be READ ONLY");
|
"\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 = makeNode(RangeVar);
|
||||||
qry->into->relname = portalname;
|
qry->into->relname = portalname;
|
||||||
qry->isPortal = TRUE;
|
qry->isPortal = TRUE;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -113,6 +113,7 @@ extern void EndTransactionBlock(void);
|
|||||||
extern bool IsTransactionBlock(void);
|
extern bool IsTransactionBlock(void);
|
||||||
extern void UserAbortTransactionBlock(void);
|
extern void UserAbortTransactionBlock(void);
|
||||||
extern void AbortOutOfAnyTransaction(void);
|
extern void AbortOutOfAnyTransaction(void);
|
||||||
|
extern void PreventTransactionChain(void *stmtNode, const char *stmtType);
|
||||||
|
|
||||||
extern void RecordTransactionCommit(void);
|
extern void RecordTransactionCommit(void);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user