aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordanielk1977 <danielk1977@noemail.net>2009-01-02 17:33:46 +0000
committerdanielk1977 <danielk1977@noemail.net>2009-01-02 17:33:46 +0000
commitcd38d520d12fde9c98facda0b7fd176342c95c06 (patch)
tree772bbe5285786cea82a0c3ab1be937e77413a371 /src
parent5df7c0f96bf7659906d5b3bbb1924d1978148bb9 (diff)
downloadsqlite-cd38d520d12fde9c98facda0b7fd176342c95c06.tar.gz
sqlite-cd38d520d12fde9c98facda0b7fd176342c95c06.zip
Modify the (transaction) method of the tcl interface to use savepoints. This makes nested calls to (transaction) work more intuitively. (CVS 6101)
FossilOrigin-Name: f047758de9b499866aa4ddf16011498b12a7b963
Diffstat (limited to 'src')
-rw-r--r--src/sqliteInt.h4
-rw-r--r--src/tclsqlite.c71
2 files changed, 52 insertions, 23 deletions
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 735ed92e6..64b4a12d5 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.816 2008/12/28 16:55:25 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.817 2009/01/02 17:33:46 danielk1977 Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_
@@ -1503,7 +1503,7 @@ struct SrcList {
int iCursor; /* The VDBE cursor number used to access this table */
Expr *pOn; /* The ON clause of a join */
IdList *pUsing; /* The USING clause of a join */
- Bitmask colUsed; /* Bit N (1<<N) set if column N or pTab is used */
+ Bitmask colUsed; /* Bit N (1<<N) set if column N of pTab is used */
char *zIndex; /* Identifier from "INDEXED BY <zIndex>" clause */
Index *pIndex; /* Index structure corresponding to zIndex, if any */
} a[1]; /* One entry for each identifier on the list */
diff --git a/src/tclsqlite.c b/src/tclsqlite.c
index e3fd44c1a..70cdfc9f4 100644
--- a/src/tclsqlite.c
+++ b/src/tclsqlite.c
@@ -12,7 +12,7 @@
** A TCL Interface to SQLite. Append this file to sqlite3.c and
** compile the whole thing to build a TCL-enabled version of SQLite.
**
-** $Id: tclsqlite.c,v 1.232 2008/12/30 06:24:58 danielk1977 Exp $
+** $Id: tclsqlite.c,v 1.233 2009/01/02 17:33:46 danielk1977 Exp $
*/
#include "tcl.h"
#include <errno.h>
@@ -118,6 +118,7 @@ struct SqliteDb {
int nStmt; /* Number of statements in stmtList */
IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */
int nStep, nSort; /* Statistics for most recent operation */
+ int nTransaction; /* Number of nested [transaction] methods */
};
struct IncrblobChannel {
@@ -2261,16 +2262,17 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
** 2005 O'Reilly Open Source Convention (OSCON).
*/
case DB_TRANSACTION: {
- int inTrans;
Tcl_Obj *pScript;
- const char *zBegin = "BEGIN";
+ const char *zBegin = "SAVEPOINT _tcl_transaction";
+ const char *zEnd;
if( objc!=3 && objc!=4 ){
Tcl_WrongNumArgs(interp, 2, objv, "[TYPE] SCRIPT");
return TCL_ERROR;
}
- if( objc==3 ){
- pScript = objv[2];
- } else {
+
+ if( pDb->nTransaction ){
+ zBegin = "SAVEPOINT _tcl_transaction";
+ }else if( pDb->nTransaction==0 && objc==4 ){
static const char *TTYPE_strs[] = {
"deferred", "exclusive", "immediate", 0
};
@@ -2287,28 +2289,55 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
case TTYPE_EXCLUSIVE: zBegin = "BEGIN EXCLUSIVE"; break;
case TTYPE_IMMEDIATE: zBegin = "BEGIN IMMEDIATE"; break;
}
- pScript = objv[3];
}
- inTrans = !sqlite3_get_autocommit(pDb->db);
- if( !inTrans ){
- pDb->disableAuth++;
- (void)sqlite3_exec(pDb->db, zBegin, 0, 0, 0);
- pDb->disableAuth--;
+ pScript = objv[objc-1];
+
+ pDb->disableAuth++;
+ rc = sqlite3_exec(pDb->db, zBegin, 0, 0, 0);
+ pDb->disableAuth--;
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0);
+ return TCL_ERROR;
}
+
+ pDb->nTransaction++;
rc = Tcl_EvalObjEx(interp, pScript, 0);
- if( !inTrans ){
- const char *zEnd;
- if( rc==TCL_ERROR ){
- zEnd = "ROLLBACK";
- } else {
+ pDb->nTransaction--;
+
+ if( rc!=TCL_ERROR ){
+ if( pDb->nTransaction ){
+ zEnd = "RELEASE _tcl_transaction";
+ }else{
zEnd = "COMMIT";
}
- pDb->disableAuth++;
- if( sqlite3_exec(pDb->db, zEnd, 0, 0, 0) ){
- sqlite3_exec(pDb->db, "ROLLBACK", 0, 0, 0);
+ }else{
+ if( pDb->nTransaction ){
+ zEnd = "ROLLBACK TO _tcl_transaction ; RELEASE _tcl_transaction";
+ }else{
+ zEnd = "ROLLBACK";
}
- pDb->disableAuth--;
}
+
+ pDb->disableAuth++;
+ if( sqlite3_exec(pDb->db, zEnd, 0, 0, 0) ){
+ /* This is a tricky scenario to handle. The most likely cause of an
+ ** error is that the exec() above was an attempt to commit the
+ ** top-level transaction that returned SQLITE_BUSY. Or, less likely,
+ ** that an IO-error has occured. In either case, throw a Tcl exception
+ ** and try to rollback the transaction.
+ **
+ ** But it could also be that the user executed one or more BEGIN,
+ ** COMMIT, SAVEPOINT, RELEASE or ROLLBACK commands that are confusing
+ ** this method's logic. Not clear how this would be best handled.
+ */
+ if( rc!=TCL_ERROR ){
+ Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0);
+ rc = TCL_ERROR;
+ }
+ sqlite3_exec(pDb->db, "ROLLBACK", 0, 0, 0);
+ }
+ pDb->disableAuth--;
+
break;
}