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
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 735ed92..64b4a12 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 @@
     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 e3fd44c..70cdfc9 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 @@
   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 @@
   ** 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 @@
         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;
   }