Make sure that transactions are started on all virtual tables that
changes in a single statement, not just the first.  Ticket #3083.
Need to add test cases. (CVS 5063)

FossilOrigin-Name: 133b7ee50ea6012739ebe0e334374c5d9b1fcc7f
diff --git a/src/build.c b/src/build.c
index 770218f..91f7057 100644
--- a/src/build.c
+++ b/src/build.c
@@ -22,7 +22,7 @@
 **     COMMIT
 **     ROLLBACK
 **
-** $Id: build.c,v 1.482 2008/04/27 18:40:12 drh Exp $
+** $Id: build.c,v 1.483 2008/04/28 18:46:43 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -159,7 +159,7 @@
     */
     if( pParse->cookieGoto>0 ){
       u32 mask;
-      int iDb;
+      int iDb, i;
       sqlite3VdbeJumpHere(v, pParse->cookieGoto-1);
       for(iDb=0, mask=1; iDb<db->nDb; mask<<=1, iDb++){
         if( (mask & pParse->cookieMask)==0 ) continue;
@@ -168,10 +168,11 @@
         sqlite3VdbeAddOp2(v,OP_VerifyCookie, iDb, pParse->cookieValue[iDb]);
       }
 #ifndef SQLITE_OMIT_VIRTUALTABLE
-      if( pParse->pVirtualLock ){
-        char *vtab = (char *)pParse->pVirtualLock->pVtab;
+      for(i=0; i<pParse->nVtabLock; i++){
+        char *vtab = (char *)pParse->apVtabLock[i]->pVtab;
         sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB);
       }
+      pParse->nVtabLock = 0;
 #endif
 
       /* Once all the cookies have been verified and transactions opened, 
diff --git a/src/delete.c b/src/delete.c
index 3c288d3..61b3f8b 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** in order to generate code for DELETE FROM statements.
 **
-** $Id: delete.c,v 1.168 2008/04/15 14:36:42 drh Exp $
+** $Id: delete.c,v 1.169 2008/04/28 18:46:43 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -371,7 +371,7 @@
 #ifndef SQLITE_OMIT_VIRTUALTABLE
       if( IsVirtual(pTab) ){
         const char *pVtab = (const char *)pTab->pVtab;
-        pParse->pVirtualLock = pTab;
+        sqlite3VtabMakeWritable(pParse, pTab);
         sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVtab, P4_VTAB);
       }else
 #endif
diff --git a/src/insert.c b/src/insert.c
index 5a97a8a..2640763 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle INSERT statements in SQLite.
 **
-** $Id: insert.c,v 1.237 2008/04/27 18:40:12 drh Exp $
+** $Id: insert.c,v 1.238 2008/04/28 18:46:43 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -846,7 +846,7 @@
     */
 #ifndef SQLITE_OMIT_VIRTUALTABLE
     if( IsVirtual(pTab) ){
-      pParse->pVirtualLock = pTab;
+      sqlite3VtabMakeWritable(pParse, pTab);
       sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns,
                      (const char*)pTab->pVtab, P4_VTAB);
     }else
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 733ffd4..9a53aaa 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.699 2008/04/28 17:41:31 shane Exp $
+** @(#) $Id: sqliteInt.h,v 1.700 2008/04/28 18:46:43 drh Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -1519,7 +1519,8 @@
 #ifndef SQLITE_OMIT_VIRTUALTABLE
   Token sArg;                /* Complete text of a module argument */
   u8 declareVtab;            /* True if inside sqlite3_declare_vtab() */
-  Table *pVirtualLock;       /* Require virtual table lock on this table */
+  int nVtabLock;             /* Number of virtual tables to lock */
+  Table **apVtabLock;        /* Pointer to virtual tables needing locking */
 #endif
 #if defined(SQLITE_TEST) || SQLITE_MAX_EXPR_DEPTH>0
   int nHeight;            /* Expression tree height of current sub-select */
@@ -2124,6 +2125,7 @@
    int sqlite3VtabRollback(sqlite3 *db);
    int sqlite3VtabCommit(sqlite3 *db);
 #endif
+void sqlite3VtabMakeWritable(Parse*,Table*);
 void sqlite3VtabLock(sqlite3_vtab*);
 void sqlite3VtabUnlock(sqlite3*, sqlite3_vtab*);
 void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*);
diff --git a/src/tokenize.c b/src/tokenize.c
index 6a2beb0..63cf786 100644
--- a/src/tokenize.c
+++ b/src/tokenize.c
@@ -15,7 +15,7 @@
 ** individual tokens and sends those tokens one-by-one over to the
 ** parser for analysis.
 **
-** $Id: tokenize.c,v 1.141 2008/04/05 18:41:43 drh Exp $
+** $Id: tokenize.c,v 1.142 2008/04/28 18:46:43 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -483,6 +483,9 @@
     pParse->nTableLock = 0;
   }
 #endif
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+  sqlite3_free(pParse->apVtabLock);
+#endif
 
   if( !IN_DECLARE_VTAB ){
     /* If the pParse->declareVtab flag is set, do not delete any table 
diff --git a/src/update.c b/src/update.c
index 2a17036..5ce03bf 100644
--- a/src/update.c
+++ b/src/update.c
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle UPDATE statements.
 **
-** $Id: update.c,v 1.177 2008/04/15 14:36:42 drh Exp $
+** $Id: update.c,v 1.178 2008/04/28 18:46:43 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -661,7 +661,7 @@
   for(i=0; i<pTab->nCol; i++){
     sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i+1+(pRowid!=0), iReg+2+i);
   }
-  pParse->pVirtualLock = pTab;
+  sqlite3VtabMakeWritable(pParse, pTab);
   sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVtab, P4_VTAB);
   sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr);
   sqlite3VdbeJumpHere(v, addr-1);
diff --git a/src/vtab.c b/src/vtab.c
index 2817162..c3d7ee2 100644
--- a/src/vtab.c
+++ b/src/vtab.c
@@ -11,7 +11,7 @@
 *************************************************************************
 ** This file contains code used to help implement virtual tables.
 **
-** $Id: vtab.c,v 1.67 2008/04/27 18:40:12 drh Exp $
+** $Id: vtab.c,v 1.68 2008/04/28 18:46:43 drh Exp $
 */
 #ifndef SQLITE_OMIT_VIRTUALTABLE
 #include "sqliteInt.h"
@@ -802,4 +802,23 @@
   return pNew;
 }
 
+/*
+** Make sure virtual table pTab is contained in the pParse->apVirtualLock[]
+** array so that an OP_VBegin will get generated for it.  Add pTab to the
+** array if it is missing.  If pTab is already in the array, this routine
+** is a no-op.
+*/
+void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){
+  int i, n;
+  assert( IsVirtual(pTab) );
+  for(i=0; i<pParse->nVtabLock; i++){
+    if( pTab==pParse->apVtabLock[i] ) return;
+  }
+  n = (pParse->nVtabLock+1)*sizeof(pParse->apVtabLock[0]);
+  pParse->apVtabLock = sqlite3_realloc(pParse->apVtabLock, n);
+  if( pParse->apVtabLock ){
+    pParse->apVtabLock[pParse->nVtabLock++] = pTab;
+  }
+}
+
 #endif /* SQLITE_OMIT_VIRTUALTABLE */