Progress toward CREATE VIRTUAL TABLE.  Still not even close to working... (CVS 3211)

FossilOrigin-Name: 898ec36b4102aaa03979f8f5c510936e57e2ae48
diff --git a/src/build.c b/src/build.c
index 74cf00a..0be7c92 100644
--- a/src/build.c
+++ b/src/build.c
@@ -22,7 +22,7 @@
 **     COMMIT
 **     ROLLBACK
 **
-** $Id: build.c,v 1.395 2006/06/10 13:29:32 drh Exp $
+** $Id: build.c,v 1.396 2006/06/11 23:41:55 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -533,12 +533,7 @@
 #ifndef SQLITE_OMIT_CHECK
   sqlite3ExprDelete(pTable->pCheck);
 #endif
-#ifndef SQLITE_OMIT_MODULE
-  sqliteFree(pTable->zModuleName);
-  if( pTable->pMod && pTable->pVTab ){
-    pTable->pMod->xDisconnect(pTable->pVTab);
-  }
-#endif SQLITE_OMIT_MODULE
+  sqlite3VtabClear(pTable);
   sqliteFree(pTable);
 }
 
@@ -810,10 +805,7 @@
     goto begin_table_error;
   }
   pTable->zName = zName;
-  pTable->nCol = 0;
-  pTable->aCol = 0;
   pTable->iPKey = -1;
-  pTable->pIndex = 0;
   pTable->pSchema = db->aDb[iDb].pSchema;
   pTable->nRef = 1;
   if( pParse->pNewTable ) sqlite3DeleteTable(db, pParse->pNewTable);
@@ -1378,7 +1370,7 @@
 
   assert( !db->init.busy || !pSelect );
 
-  iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema);
+  iDb = sqlite3SchemaToIndex(db, p->pSchema);
 
 #ifndef SQLITE_OMIT_CHECK
   /* Resolve names in all CHECK constraint expressions.
@@ -1974,7 +1966,14 @@
     /* Remove the table entry from SQLite's internal schema and modify
     ** the schema cookie.
     */
-    sqlite3VdbeOp3(v, OP_DropTable, iDb, 0, pTab->zName, 0);
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+    if( pTab->isEphem ){
+      sqlite3VdbeOp3(v, OP_VDestroy, iDb, 0, pTab->zName, 0);
+    }else
+#endif
+    {
+      sqlite3VdbeOp3(v, OP_DropTable, iDb, 0, pTab->zName, 0);
+    }
     sqlite3ChangeCookie(db, v, iDb);
   }
   sqliteViewResetAll(db, iDb);
diff --git a/src/delete.c b/src/delete.c
index 237c0f1..e16597d 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.122 2006/02/24 02:53:50 drh Exp $
+** $Id: delete.c,v 1.123 2006/06/11 23:41:55 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -190,7 +190,7 @@
   */
   if( isView ){
     Select *pView = sqlite3SelectDup(pTab->pSelect);
-    sqlite3Select(pParse, pView, SRT_VirtualTab, iCur, 0, 0, 0, 0);
+    sqlite3Select(pParse, pView, SRT_EphemTab, iCur, 0, 0, 0, 0);
     sqlite3SelectDelete(pView);
   }
 
diff --git a/src/expr.c b/src/expr.c
index 0577737..5a721ab 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -12,7 +12,7 @@
 ** This file contains routines used for analyzing expressions and
 ** for generating VDBE code that evaluates expressions in SQLite.
 **
-** $Id: expr.c,v 1.258 2006/05/23 23:22:29 drh Exp $
+** $Id: expr.c,v 1.259 2006/06/11 23:41:55 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -547,12 +547,12 @@
   pNew->iOffset = -1;
   pNew->isResolved = p->isResolved;
   pNew->isAgg = p->isAgg;
-  pNew->usesVirt = 0;
+  pNew->usesEphm = 0;
   pNew->disallowOrderBy = 0;
   pNew->pRightmost = 0;
-  pNew->addrOpenVirt[0] = -1;
-  pNew->addrOpenVirt[1] = -1;
-  pNew->addrOpenVirt[2] = -1;
+  pNew->addrOpenEphm[0] = -1;
+  pNew->addrOpenEphm[1] = -1;
+  pNew->addrOpenEphm[2] = -1;
   return pNew;
 }
 #else
@@ -1316,7 +1316,7 @@
     case TK_IN: {
       char affinity;
       KeyInfo keyInfo;
-      int addr;        /* Address of OP_OpenVirtual instruction */
+      int addr;        /* Address of OP_OpenEphemeral instruction */
 
       affinity = sqlite3ExprAffinity(pExpr->pLeft);
 
@@ -1334,7 +1334,7 @@
       ** is used.
       */
       pExpr->iTable = pParse->nTab++;
-      addr = sqlite3VdbeAddOp(v, OP_OpenVirtual, pExpr->iTable, 0);
+      addr = sqlite3VdbeAddOp(v, OP_OpenEphemeral, pExpr->iTable, 0);
       memset(&keyInfo, 0, sizeof(keyInfo));
       keyInfo.nField = 1;
       sqlite3VdbeAddOp(v, OP_SetNumColumns, pExpr->iTable, 1);
diff --git a/src/insert.c b/src/insert.c
index c8c5090..295d753 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.164 2006/03/15 16:26:10 drh Exp $
+** $Id: insert.c,v 1.165 2006/06/11 23:41:55 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -371,7 +371,7 @@
       ** back up and execute the SELECT code above.
       */
       sqlite3VdbeJumpHere(v, iInitCode);
-      sqlite3VdbeAddOp(v, OP_OpenVirtual, srcTab, 0);
+      sqlite3VdbeAddOp(v, OP_OpenEphemeral, srcTab, 0);
       sqlite3VdbeAddOp(v, OP_SetNumColumns, srcTab, nColumn);
       sqlite3VdbeAddOp(v, OP_Goto, 0, iSelectLoop);
       sqlite3VdbeResolveLabel(v, iCleanup);
diff --git a/src/main.c b/src/main.c
index 9d6cbd9..6eb047d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -14,7 +14,7 @@
 ** other files are for internal use by SQLite and should not be
 ** accessed by users of the library.
 **
-** $Id: main.c,v 1.341 2006/06/08 15:48:01 drh Exp $
+** $Id: main.c,v 1.342 2006/06/11 23:41:55 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -159,6 +159,9 @@
     sqliteFree(pColl);
   }
   sqlite3HashClear(&db->aCollSeq);
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+  sqlite3HashClear(&db->aModule);
+#endif
 
   sqlite3HashClear(&db->aFunc);
   sqlite3Error(db, SQLITE_OK, 0); /* Deallocates any cached error strings. */
@@ -818,6 +821,9 @@
   db->flags |= SQLITE_ShortColNames;
   sqlite3HashInit(&db->aFunc, SQLITE_HASH_STRING, 0);
   sqlite3HashInit(&db->aCollSeq, SQLITE_HASH_STRING, 0);
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+  sqlite3HashInit(&db->aModule, SQLITE_HASH_STRING, 0);
+#endif
 
   /* Add the default collation sequence BINARY. BINARY works for both UTF-8
   ** and UTF-16, so add a version for each to avoid any unnecessary
diff --git a/src/parse.y b/src/parse.y
index 922d473..ce3a69d 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -14,7 +14,7 @@
 ** the parser.  Lemon will also generate a header file containing
 ** numeric codes for all of the tokens.
 **
-** @(#) $Id: parse.y,v 1.201 2006/06/10 13:29:33 drh Exp $
+** @(#) $Id: parse.y,v 1.202 2006/06/11 23:41:55 drh Exp $
 */
 
 // All token codes are small integers with #defines that begin with "TK_"
@@ -1062,11 +1062,18 @@
 
 //////////////////////// CREATE VIRTUAL TABLE ... /////////////////////////////
 %ifndef SQLITE_OMIT_VIRTUALTABLE
-cmd ::= CREATE VIRTUAL TABLE nm(X) dbnm(Y) USING nm(Z) vtabargsopt.
-vtabargsopt ::= .
-vtabargsopt ::= LP vtabarglist RP.
+cmd ::= create_vtab.                       {sqlite3VtabFinishParse(pParse,0);}
+cmd ::= create_vtab LP vtabarglist RP(X).  {sqlite3VtabFinishParse(pParse,&X);}
+create_vtab ::= CREATE VIRTUAL TABLE nm(X) dbnm(Y) USING nm(Z). {
+    sqlite3VtabBeginParse(pParse, &X, &Y, &Z);
+}
 vtabarglist ::= vtabarg.
 vtabarglist ::= vtabarglist COMMA vtabarg.
-vtabarg ::= ANY.
-vtabarg ::= vtabarg ANY.
+vtabarg ::= .                       {sqlite3VtabArgInit(pParse);}
+vtabarg ::= vtabarg vtabargtoken.
+vtabargtoken ::= ANY(X).            {sqlite3VtabArgExtend(pParse,&X);}
+vtabargtoken ::= lp anylist RP(X).  {sqlite3VtabArgExtend(pParse,&X);}
+lp ::= LP(X).                       {sqlite3VtabArgExtend(pParse,&X);}
+anylist ::= .
+anylist ::= anylist ANY(X).         {sqlite3VtabArgExtend(pParse,&X);}
 %endif
diff --git a/src/select.c b/src/select.c
index 2fda134..5dd23d4 100644
--- a/src/select.c
+++ b/src/select.c
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle SELECT statements in SQLite.
 **
-** $Id: select.c,v 1.313 2006/04/26 17:39:34 drh Exp $
+** $Id: select.c,v 1.314 2006/06/11 23:41:56 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -72,9 +72,9 @@
   pNew->pOffset = pOffset;
   pNew->iLimit = -1;
   pNew->iOffset = -1;
-  pNew->addrOpenVirt[0] = -1;
-  pNew->addrOpenVirt[1] = -1;
-  pNew->addrOpenVirt[2] = -1;
+  pNew->addrOpenEphm[0] = -1;
+  pNew->addrOpenEphm[1] = -1;
+  pNew->addrOpenEphm[2] = -1;
   if( pNew==&standin) {
     clearSelect(pNew);
     pNew = 0;
@@ -522,7 +522,7 @@
     /* Store the result as data using a unique key.
     */
     case SRT_Table:
-    case SRT_VirtualTab: {
+    case SRT_EphemTab: {
       sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0);
       if( pOrderBy ){
         pushOntoSorter(pParse, pOrderBy, p);
@@ -705,7 +705,7 @@
   sqlite3VdbeAddOp(v, OP_Column, iTab, pOrderBy->nExpr + 1);
   switch( eDest ){
     case SRT_Table:
-    case SRT_VirtualTab: {
+    case SRT_EphemTab: {
       sqlite3VdbeAddOp(v, OP_NewRowid, iParm, 0);
       sqlite3VdbeAddOp(v, OP_Pull, 1, 0);
       sqlite3VdbeAddOp(v, OP_Insert, iParm, 0);
@@ -1201,11 +1201,11 @@
       if( pTab==0 ){
         return 1;
       }
-      /* The isTransient flag indicates that the Table structure has been
+      /* The isEphem flag indicates that the Table structure has been
       ** dynamically allocated and may be freed at any time.  In other words,
       ** pTab is not pointing to a persistent table structure that defines
       ** part of the schema. */
-      pTab->isTransient = 1;
+      pTab->isEphem = 1;
 #endif
     }else{
       /* An ordinary table or view name in the FROM clause */
@@ -1538,10 +1538,10 @@
     int addr;
     assert( pOrderBy->iECursor==0 );
     pOrderBy->iECursor = pParse->nTab++;
-    addr = sqlite3VdbeAddOp(pParse->pVdbe, OP_OpenVirtual,
+    addr = sqlite3VdbeAddOp(pParse->pVdbe, OP_OpenEphemeral,
                             pOrderBy->iECursor, pOrderBy->nExpr+1);
-    assert( p->addrOpenVirt[2] == -1 );
-    p->addrOpenVirt[2] = addr;
+    assert( p->addrOpenEphm[2] == -1 );
+    p->addrOpenEphm[2] = addr;
   }
 }
 
@@ -1647,10 +1647,10 @@
 
   /* Create the destination temporary table if necessary
   */
-  if( eDest==SRT_VirtualTab ){
+  if( eDest==SRT_EphemTab ){
     assert( p->pEList );
     assert( nSetP2<sizeof(aSetP2)/sizeof(aSetP2[0]) );
-    aSetP2[nSetP2++] = sqlite3VdbeAddOp(v, OP_OpenVirtual, iParm, 0);
+    aSetP2[nSetP2++] = sqlite3VdbeAddOp(v, OP_OpenEphemeral, iParm, 0);
     eDest = SRT_Table;
   }
 
@@ -1712,14 +1712,14 @@
           rc = 1;
           goto multi_select_end;
         }
-        addr = sqlite3VdbeAddOp(v, OP_OpenVirtual, unionTab, 0);
+        addr = sqlite3VdbeAddOp(v, OP_OpenEphemeral, unionTab, 0);
         if( priorOp==SRT_Table ){
           assert( nSetP2<sizeof(aSetP2)/sizeof(aSetP2[0]) );
           aSetP2[nSetP2++] = addr;
         }else{
-          assert( p->addrOpenVirt[0] == -1 );
-          p->addrOpenVirt[0] = addr;
-          p->pRightmost->usesVirt = 1;
+          assert( p->addrOpenEphm[0] == -1 );
+          p->addrOpenEphm[0] = addr;
+          p->pRightmost->usesEphm = 1;
         }
         createSortingIndex(pParse, p, pOrderBy);
         assert( p->pEList );
@@ -1808,10 +1808,10 @@
       }
       createSortingIndex(pParse, p, pOrderBy);
 
-      addr = sqlite3VdbeAddOp(v, OP_OpenVirtual, tab1, 0);
-      assert( p->addrOpenVirt[0] == -1 );
-      p->addrOpenVirt[0] = addr;
-      p->pRightmost->usesVirt = 1;
+      addr = sqlite3VdbeAddOp(v, OP_OpenEphemeral, tab1, 0);
+      assert( p->addrOpenEphm[0] == -1 );
+      p->addrOpenEphm[0] = addr;
+      p->pRightmost->usesEphm = 1;
       assert( p->pEList );
 
       /* Code the SELECTs to our left into temporary table "tab1".
@@ -1823,9 +1823,9 @@
 
       /* Code the current SELECT into temporary table "tab2"
       */
-      addr = sqlite3VdbeAddOp(v, OP_OpenVirtual, tab2, 0);
-      assert( p->addrOpenVirt[1] == -1 );
-      p->addrOpenVirt[1] = addr;
+      addr = sqlite3VdbeAddOp(v, OP_OpenEphemeral, tab2, 0);
+      assert( p->addrOpenEphm[1] == -1 );
+      p->addrOpenEphm[1] = addr;
       p->pPrior = 0;
       pLimit = p->pLimit;
       p->pLimit = 0;
@@ -1899,7 +1899,7 @@
   ** SELECT might also skip this part if it has no ORDER BY clause and
   ** no temp tables are required.
   */
-  if( pOrderBy || p->usesVirt ){
+  if( pOrderBy || p->usesEphm ){
     int i;                        /* Loop counter */
     KeyInfo *pKeyInfo;            /* Collating sequence for the result set */
     Select *pLoop;                /* For looping through SELECT statements */
@@ -1925,11 +1925,11 @@
 
     for(pLoop=p; pLoop; pLoop=pLoop->pPrior){
       for(i=0; i<2; i++){
-        int addr = pLoop->addrOpenVirt[i];
+        int addr = pLoop->addrOpenEphm[i];
         if( addr<0 ){
           /* If [0] is unused then [1] is also unused.  So we can
           ** always safely abort as soon as the first unused slot is found */
-          assert( pLoop->addrOpenVirt[1]<0 );
+          assert( pLoop->addrOpenEphm[1]<0 );
           break;
         }
         sqlite3VdbeChangeP2(v, addr, nCol);
@@ -1959,8 +1959,8 @@
         *pSortOrder = pOTerm->sortOrder;
       }
       assert( p->pRightmost==p );
-      assert( p->addrOpenVirt[2]>=0 );
-      addr = p->addrOpenVirt[2];
+      assert( p->addrOpenEphm[2]>=0 );
+      addr = p->addrOpenEphm[2];
       sqlite3VdbeChangeP2(v, addr, p->pEList->nExpr+2);
       pKeyInfo->nField = nOrderByExpr;
       sqlite3VdbeChangeP3(v, addr, (char*)pKeyInfo, P3_KEYINFO_HANDOFF);
@@ -2394,8 +2394,8 @@
 
   /* If the output is destined for a temporary table, open that table.
   */
-  if( eDest==SRT_VirtualTab ){
-    sqlite3VdbeAddOp(v, OP_OpenVirtual, iParm, 1);
+  if( eDest==SRT_EphemTab ){
+    sqlite3VdbeAddOp(v, OP_OpenEphemeral, iParm, 1);
   }
 
   /* Generating code to find the min or the max.  Basically all we have
@@ -2404,7 +2404,7 @@
   ** or last entry in the main table.
   */
   iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
-  assert( iDb>=0 || pTab->isTransient );
+  assert( iDb>=0 || pTab->isEphem );
   sqlite3CodeVerifySchema(pParse, iDb);
   sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
   base = pSrc->a[0].iCursor;
@@ -2631,7 +2631,7 @@
         pFunc->iDistinct = -1;
       }else{
         KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->pList);
-        sqlite3VdbeOp3(v, OP_OpenVirtual, pFunc->iDistinct, 0, 
+        sqlite3VdbeOp3(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 
                           (char*)pKeyInfo, P3_KEYINFO_HANDOFF);
       }
     }
@@ -2780,7 +2780,7 @@
   int isDistinct;        /* True if the DISTINCT keyword is present */
   int distinct;          /* Table to use for the distinct set */
   int rc = 1;            /* Value to return from this function */
-  int addrSortIndex;     /* Address of an OP_OpenVirtual instruction */
+  int addrSortIndex;     /* Address of an OP_OpenEphemeral instruction */
   AggInfo sAggInfo;      /* Information used by aggregate queries */
   int iEnd;              /* Address of the end of the query */
 
@@ -2868,7 +2868,7 @@
     }else{
       needRestoreContext = 0;
     }
-    sqlite3Select(pParse, pItem->pSelect, SRT_VirtualTab, 
+    sqlite3Select(pParse, pItem->pSelect, SRT_EphemTab, 
                  pItem->iCursor, p, i, &isAgg, 0);
     if( needRestoreContext ){
       pParse->zAuthContext = zSavedAuthContext;
@@ -2908,7 +2908,7 @@
   **
   ** This sorting index might end up being unused if the data can be 
   ** extracted in pre-sorted order.  If that is the case, then the
-  ** OP_OpenVirtual instruction will be changed to an OP_Noop once
+  ** OP_OpenEphemeral instruction will be changed to an OP_Noop once
   ** we figure out that the sorting index is not needed.  The addrSortIndex
   ** variable is used to facilitate that change.
   */
@@ -2925,8 +2925,8 @@
     }
     pKeyInfo = keyInfoFromExprList(pParse, pOrderBy);
     pOrderBy->iECursor = pParse->nTab++;
-    p->addrOpenVirt[2] = addrSortIndex =
-       sqlite3VdbeOp3(v, OP_OpenVirtual, pOrderBy->iECursor, pOrderBy->nExpr+2, 
+    p->addrOpenEphm[2] = addrSortIndex =
+       sqlite3VdbeOp3(v, OP_OpenEphemeral, pOrderBy->iECursor, pOrderBy->nExpr+2, 
                         (char*)pKeyInfo, P3_KEYINFO_HANDOFF);
   }else{
     addrSortIndex = -1;
@@ -2934,8 +2934,8 @@
 
   /* If the output is destined for a temporary table, open that table.
   */
-  if( eDest==SRT_VirtualTab ){
-    sqlite3VdbeAddOp(v, OP_OpenVirtual, iParm, pEList->nExpr);
+  if( eDest==SRT_EphemTab ){
+    sqlite3VdbeAddOp(v, OP_OpenEphemeral, iParm, pEList->nExpr);
   }
 
   /* Set the limiter.
@@ -2949,7 +2949,7 @@
     KeyInfo *pKeyInfo;
     distinct = pParse->nTab++;
     pKeyInfo = keyInfoFromExprList(pParse, p->pEList);
-    sqlite3VdbeOp3(v, OP_OpenVirtual, distinct, 0, 
+    sqlite3VdbeOp3(v, OP_OpenEphemeral, distinct, 0, 
                         (char*)pKeyInfo, P3_KEYINFO_HANDOFF);
   }else{
     distinct = -1;
@@ -2963,13 +2963,13 @@
     pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy);
     if( pWInfo==0 ) goto select_end;
 
-    /* If sorting index that was created by a prior OP_OpenVirtual 
-    ** instruction ended up not being needed, then change the OP_OpenVirtual
+    /* If sorting index that was created by a prior OP_OpenEphemeral 
+    ** instruction ended up not being needed, then change the OP_OpenEphemeral
     ** into an OP_Noop.
     */
     if( addrSortIndex>=0 && pOrderBy==0 ){
       sqlite3VdbeChangeToNoop(v, addrSortIndex, 1);
-      p->addrOpenVirt[2] = -1;
+      p->addrOpenEphm[2] = -1;
     }
 
     /* Use the standard inner loop
@@ -3003,7 +3003,7 @@
     int addrGroupByChange;  /* Code that runs when any GROUP BY term changes */
     int addrProcessRow;     /* Code to process a single input row */
     int addrEnd;            /* End of all processing */
-    int addrSortingIdx;     /* The OP_OpenVirtual for the sorting index */
+    int addrSortingIdx;     /* The OP_OpenEphemeral for the sorting index */
     int addrReset;          /* Subroutine for resetting the accumulator */
 
     addrEnd = sqlite3VdbeMakeLabel(v);
@@ -3050,13 +3050,13 @@
 
       /* If there is a GROUP BY clause we might need a sorting index to
       ** implement it.  Allocate that sorting index now.  If it turns out
-      ** that we do not need it after all, the OpenVirtual instruction
+      ** that we do not need it after all, the OpenEphemeral instruction
       ** will be converted into a Noop.  
       */
       sAggInfo.sortingIdx = pParse->nTab++;
       pKeyInfo = keyInfoFromExprList(pParse, pGroupBy);
       addrSortingIdx =
-          sqlite3VdbeOp3(v, OP_OpenVirtual, sAggInfo.sortingIdx,
+          sqlite3VdbeOp3(v, OP_OpenEphemeral, sAggInfo.sortingIdx,
                          sAggInfo.nSortingColumn,
                          (char*)pKeyInfo, P3_KEYINFO_HANDOFF);
 
@@ -3119,7 +3119,7 @@
       if( pWInfo==0 ) goto select_end;
       if( pGroupBy==0 ){
         /* The optimizer is able to deliver rows in group by order so
-        ** we do not have to sort.  The OP_OpenVirtual table will be
+        ** we do not have to sort.  The OP_OpenEphemeral table will be
         ** cancelled later because we still need to use the pKeyInfo
         */
         pGroupBy = p->pGroupBy;
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 6a25f67..3f8a6d7 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -12,7 +12,7 @@
 ** This header file defines the interface that the SQLite library
 ** presents to client programs.
 **
-** @(#) $Id: sqlite.h.in,v 1.167 2006/06/10 13:29:33 drh Exp $
+** @(#) $Id: sqlite.h.in,v 1.168 2006/06/11 23:41:56 drh Exp $
 */
 #ifndef _SQLITE3_H_
 #define _SQLITE3_H_
@@ -1517,6 +1517,7 @@
 typedef struct sqlite3_module sqlite3_module;
 struct sqlite3_module {
   int iVersion;
+  const char *zName;
   void *pAux;
   int (*xCreate)(sqlite3*, const sqlite3_module *pModule,
                int argc, char **argv,
@@ -1568,6 +1569,11 @@
 #define SQLITE_INDEX_CONSTRAINT_LT    5
 #define SQLITE_INDEX_CONSTRAINT_GE    6
 #define SQLITE_INDEX_CONSTRAINT_MATCH 7
+int sqlite3_create_module(
+  sqlite3 *db, 
+  const char *zName,
+  const sqlite3_module *
+);
 
 
 /*
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 6e78e07..9a2de48 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.496 2006/06/10 13:29:33 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.497 2006/06/11 23:41:56 drh Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -490,13 +490,13 @@
   void *pProgressArg;           /* Argument to the progress callback */
   int nProgressOps;             /* Number of opcodes for progress callback */
 #endif
-#ifndef SQLITE_OMIT_GLOBALRECOVER
-  sqlite3 *pNext;               /* Linked list of open db handles. */
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+  Hash aModule;                 /* populated by sqlite3_create_module() */
 #endif
   Hash aFunc;                   /* All functions that can be in SQL exprs */
   Hash aCollSeq;                /* All collating sequences */
   BusyHandler busyHandler;      /* Busy callback */
-  int busyTimeout;             /* Busy handler timeout, in msec */
+  int busyTimeout;              /* Busy handler timeout, in msec */
   Db aDbStatic[2];              /* Static space for the 2 default backends */
 #ifdef SQLITE_SSE
   sqlite3_stmt *pFetch;         /* Used by SSE to fetch stored statements */
@@ -672,7 +672,7 @@
 ** Table.tnum is the page number for the root BTree page of the table in the
 ** database file.  If Table.iDb is the index of the database table backend
 ** in sqlite.aDb[].  0 is for the main database and 1 is for the file that
-** holds temporary tables and indices.  If Table.isTransient
+** holds temporary tables and indices.  If Table.isEphem
 ** is true, then the table is stored in a file that is automatically deleted
 ** when the VDBE cursor to the table is closed.  In this case Table.tnum 
 ** refers VDBE cursor number that holds the table open, not to the root
@@ -689,7 +689,7 @@
   int tnum;        /* Root BTree node for this table (see note above) */
   Select *pSelect; /* NULL for tables.  Points to definition if a view. */
   u8 readOnly;     /* True if this table should not be written by the user */
-  u8 isTransient;  /* True if automatically deleted when VDBE finishes */
+  u8 isEphem;      /* True if created using OP_OpenEphermeral */
   u8 hasPrimKey;   /* True if there exists a primary key */
   u8 keyConf;      /* What to do in case of uniqueness conflict on iPKey */
   u8 autoInc;      /* True if the integer primary key is autoincrement */
@@ -704,10 +704,12 @@
   int addColOffset;  /* Offset in CREATE TABLE statement to add a new column */
 #endif
 #ifndef SQLITE_OMIT_VIRTUALTABLE
-  char *zModuleName;     /* Name of module implementing this virtual table */
-  sqlite3_module *pMod;  /* Pointer to the implementation of the module */
-  sqlite3_vtab *pVTab;   /* Pointer to the module instance */
-  u8 needCreate;         /* Need to call pMod->xCreate() */
+  sqlite3_module *pModule;  /* Pointer to the implementation of the module */
+  sqlite3_vtab *pVtab;      /* Pointer to the module instance */
+  int nModuleArg;           /* Number of arguments to the module */
+  char **azModuleArg;       /* Text of all module args. [0] is module name */
+  u8 needCreate;            /* Need to call pMod->xCreate() */
+  u8 isVirtual;             /* True if this is a virtual table */
 #endif
   Schema *pSchema;
 };
@@ -903,7 +905,7 @@
     Expr *pExpr;             /* Expression encoding the function */
     FuncDef *pFunc;          /* The aggregate function implementation */
     int iMem;                /* Memory location that acts as accumulator */
-    int iDistinct;           /* Virtual table used to enforce DISTINCT */
+    int iDistinct;           /* Ephermeral table used to enforce DISTINCT */
   } *aFunc;
   int nFunc;              /* Number of entries in aFunc[] */
   int nFuncAlloc;         /* Number of slots allocated for aFunc[] */
@@ -1171,14 +1173,14 @@
 ** offset).  But later on, nLimit and nOffset become the memory locations
 ** in the VDBE that record the limit and offset counters.
 **
-** addrOpenVirt[] entries contain the address of OP_OpenVirtual opcodes.
+** addrOpenEphm[] entries contain the address of OP_OpenEphemeral opcodes.
 ** These addresses must be stored so that we can go back and fill in
 ** the P3_KEYINFO and P2 parameters later.  Neither the KeyInfo nor
 ** the number of columns in P2 can be computed at the same time
-** as the OP_OpenVirtual instruction is coded because not
+** as the OP_OpenEphm instruction is coded because not
 ** enough information about the compound query is known at that point.
-** The KeyInfo for addrOpenVirt[0] and [1] contains collating sequences
-** for the result set.  The KeyInfo for addrOpenVirt[2] contains collating
+** The KeyInfo for addrOpenTran[0] and [1] contains collating sequences
+** for the result set.  The KeyInfo for addrOpenTran[2] contains collating
 ** sequences for the ORDER BY clause.
 */
 struct Select {
@@ -1187,7 +1189,7 @@
   u8 isDistinct;         /* True if the DISTINCT keyword is present */
   u8 isResolved;         /* True once sqlite3SelectResolve() has run. */
   u8 isAgg;              /* True if this is an aggregate query */
-  u8 usesVirt;           /* True if uses an OpenVirtual opcode */
+  u8 usesEphm;           /* True if uses an OpenEphemeral opcode */
   u8 disallowOrderBy;    /* Do not allow an ORDER BY to be attached if TRUE */
   SrcList *pSrc;         /* The FROM clause */
   Expr *pWhere;          /* The WHERE clause */
@@ -1199,7 +1201,7 @@
   Expr *pLimit;          /* LIMIT expression. NULL means not used. */
   Expr *pOffset;         /* OFFSET expression. NULL means not used. */
   int iLimit, iOffset;   /* Memory registers holding LIMIT & OFFSET counters */
-  int addrOpenVirt[3];   /* OP_OpenVirtual opcodes related to this select */
+  int addrOpenEphm[3];   /* OP_OpenEphem opcodes related to this select */
 };
 
 /*
@@ -1216,7 +1218,7 @@
 #define SRT_Mem          5  /* Store result in a memory cell */
 #define SRT_Set          6  /* Store non-null results as keys in an index */
 #define SRT_Table        7  /* Store result as data with an automatic rowid */
-#define SRT_VirtualTab   8  /* Create virtual table and store like SRT_Table */
+#define SRT_EphemTab     8  /* Create transient tab and store like SRT_Table */
 #define SRT_Subroutine   9  /* Call a subroutine to handle results */
 #define SRT_Exists      10  /* Store 1 if the result is not empty */
 
@@ -1277,6 +1279,11 @@
   Trigger *pNewTrigger;     /* Trigger under construct by a CREATE TRIGGER */
   TriggerStack *trigStack;  /* Trigger actions being coded */
   const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+  int nArgAlloc;            /* Number of bytes allocated for zArg[] */
+  int nArgUsed;             /* Number of bytes of zArg[] used so far */
+  char *zArg;               /* Complete text of a module argument */
+#endif
 };
 
 /*
@@ -1774,6 +1781,16 @@
   #define sqlite3ThreadSafeFree sqlite3FreeX
 #endif
 
+#ifdef SQLITE_OMIT_VIRTUALTABLE
+#  define sqlite3VtabClear(X)
+#else
+   void sqlite3VtabClear(Table*);
+#endif
+void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*);
+void sqlite3VtabFinishParse(Parse*, Token*);
+void sqlite3VtabArgInit(Parse*);
+void sqlite3VtabArgExtend(Parse*, Token*);
+
 #ifdef SQLITE_SSE
 #include "sseInt.h"
 #endif
diff --git a/src/tclsqlite.c b/src/tclsqlite.c
index 875890d..07783c1 100644
--- a/src/tclsqlite.c
+++ b/src/tclsqlite.c
@@ -11,7 +11,7 @@
 *************************************************************************
 ** A TCL Interface to SQLite
 **
-** $Id: tclsqlite.c,v 1.156 2006/05/10 14:39:14 drh Exp $
+** $Id: tclsqlite.c,v 1.157 2006/06/11 23:41:56 drh Exp $
 */
 #ifndef NO_TCL     /* Omit this whole file if TCL is unavailable */
 
@@ -2151,6 +2151,7 @@
     extern int Sqlitetest5_Init(Tcl_Interp*);
     extern int Sqlitetest6_Init(Tcl_Interp*);
     extern int Sqlitetest7_Init(Tcl_Interp*);
+    extern int Sqlitetest8_Init(Tcl_Interp*);
     extern int Md5_Init(Tcl_Interp*);
     extern int Sqlitetestsse_Init(Tcl_Interp*);
     extern int Sqlitetestasync_Init(Tcl_Interp*);
@@ -2162,6 +2163,7 @@
     Sqlitetest5_Init(interp);
     Sqlitetest6_Init(interp);
     Sqlitetest7_Init(interp);
+    Sqlitetest8_Init(interp);
     Sqlitetestasync_Init(interp);
     Md5_Init(interp);
 #ifdef SQLITE_SSE
diff --git a/src/test1.c b/src/test1.c
index 01dc6d7..0c18ea1 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test1.c,v 1.209 2006/03/19 13:00:25 drh Exp $
+** $Id: test1.c,v 1.210 2006/06/11 23:41:56 drh Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -3538,6 +3538,12 @@
 #else
   Tcl_SetVar2(interp, "sqlite_options", "view", "1", TCL_GLOBAL_ONLY);
 #endif
+
+#ifdef SQLITE_OMIT_VIRTUALTABLE
+  Tcl_SetVar2(interp, "sqlite_options", "vtab", "0", TCL_GLOBAL_ONLY);
+#else
+  Tcl_SetVar2(interp, "sqlite_options", "vtab", "1", TCL_GLOBAL_ONLY);
+#endif
 }
 
 /*
diff --git a/src/test8.c b/src/test8.c
new file mode 100644
index 0000000..c0d08a4
--- /dev/null
+++ b/src/test8.c
@@ -0,0 +1,136 @@
+/*
+** 2006 June 10
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+*************************************************************************
+** Code for testing the virtual table interfaces.  This code
+** is not included in the SQLite library.  It is used for automated
+** testing of the SQLite library.
+**
+** $Id: test8.c,v 1.1 2006/06/11 23:41:56 drh Exp $
+*/
+#include "sqliteInt.h"
+#include "tcl.h"
+#include "os.h"
+#include <stdlib.h>
+#include <string.h>
+
+/* Methods for the echo module */
+static int echoCreate(
+  sqlite3 *db,
+  const sqlite3_module *pModule,
+  int argc, char **argv,
+  sqlite3_vtab **ppVtab
+){
+  int i;
+  Tcl_Interp *interp = pModule->pAux;
+  *ppVtab = pModule->pAux;
+
+  Tcl_SetVar(interp, "echo_module", "xCreate", TCL_GLOBAL_ONLY);
+  for(i=0; i<argc; i++){
+    Tcl_SetVar(interp, "echo_module", argv[i],
+                TCL_APPEND_VALUE | TCL_LIST_ELEMENT | TCL_GLOBAL_ONLY);
+  }
+  return 0;
+}
+static int echoConnect(
+  sqlite3 *db,
+  const sqlite3_module *pModule,
+  int argc, char **argv,
+  sqlite3_vtab **ppVtab
+){
+  int i;
+  Tcl_Interp *interp = pModule->pAux;
+  *ppVtab = pModule->pAux;
+
+  Tcl_SetVar(interp, "echo_module", "xConnect", TCL_GLOBAL_ONLY);
+  for(i=0; i<argc; i++){
+    Tcl_SetVar(interp, "echo_module", argv[i],
+                TCL_APPEND_VALUE | TCL_LIST_ELEMENT | TCL_GLOBAL_ONLY);
+  }
+  return 0;
+}
+static int echoDisconnect(sqlite3_vtab *pVtab){
+  Tcl_Interp *interp = (Tcl_Interp*)pVtab;
+  Tcl_SetVar(interp, "echo_module", "xDisconnect",
+                TCL_APPEND_VALUE | TCL_LIST_ELEMENT | TCL_GLOBAL_ONLY);
+  return 0;
+}
+static int echoDestroy(sqlite3_vtab *pVtab){
+  Tcl_Interp *interp = (Tcl_Interp*)pVtab;
+  Tcl_SetVar(interp, "echo_module", "xDestroy",
+                TCL_APPEND_VALUE | TCL_LIST_ELEMENT | TCL_GLOBAL_ONLY);
+  return 0;
+}
+
+/*
+** A virtual table module that merely echos method calls into TCL
+** variables.
+*/
+static sqlite3_module echoModule = {
+  0,
+  "echo",
+  0,
+  echoCreate,
+  echoConnect,
+  0,
+  echoDisconnect, 
+  echoDestroy,
+};
+
+/*
+** Decode a pointer to an sqlite3 object.
+*/
+static int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb){
+  *ppDb = (sqlite3*)sqlite3TextToPtr(zA);
+  return TCL_OK;
+}
+
+
+/*
+** Register the echo virtual table module.
+*/
+static int register_echo_module(
+  ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int objc,              /* Number of arguments */
+  Tcl_Obj *CONST objv[]  /* Command arguments */
+){
+  sqlite3 *db;
+  if( objc!=2 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "DB");
+    return TCL_ERROR;
+  }
+  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+  echoModule.pAux = interp;
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+  sqlite3_create_module(db, "echo", &echoModule);
+#endif
+  return TCL_OK;
+}
+
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int Sqlitetest8_Init(Tcl_Interp *interp){
+  static struct {
+     char *zName;
+     Tcl_ObjCmdProc *xProc;
+     void *clientData;
+  } aObjCmd[] = {
+     { "register_echo_module",   register_echo_module, 0 },
+  };
+  int i;
+  for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
+    Tcl_CreateObjCommand(interp, aObjCmd[i].zName, 
+        aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
+  }
+  return TCL_OK;
+}
diff --git a/src/tokenize.c b/src/tokenize.c
index 6076e7c..2d49fe8 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.118 2006/04/04 01:54:55 drh Exp $
+** $Id: tokenize.c,v 1.119 2006/06/11 23:41:56 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -483,6 +483,9 @@
     pParse->nTableLock = 0;
   }
 #endif
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+  sqliteFree(pParse->zArg);
+#endif
   sqlite3DeleteTable(pParse->db, pParse->pNewTable);
   sqlite3DeleteTrigger(pParse->pNewTrigger);
   sqliteFree(pParse->apVarExpr);
diff --git a/src/update.c b/src/update.c
index b3cd7ae..f1662eb 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.123 2006/02/24 02:53:50 drh Exp $
+** $Id: update.c,v 1.124 2006/06/11 23:41:56 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -268,7 +268,7 @@
   if( isView ){
     Select *pView;
     pView = sqlite3SelectDup(pTab->pSelect);
-    sqlite3Select(pParse, pView, SRT_VirtualTab, iCur, 0, 0, 0, 0);
+    sqlite3Select(pParse, pView, SRT_EphemTab, iCur, 0, 0, 0, 0);
     sqlite3SelectDelete(pView);
   }
 
diff --git a/src/vdbe.c b/src/vdbe.c
index fc6fd97..a85d303 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -43,7 +43,7 @@
 ** in this file for details.  If in doubt, do not deviate from existing
 ** commenting and indentation practices when changing or adding code.
 **
-** $Id: vdbe.c,v 1.550 2006/06/03 18:04:17 drh Exp $
+** $Id: vdbe.c,v 1.551 2006/06/11 23:41:56 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -2640,9 +2640,9 @@
   break;
 }
 
-/* Opcode: OpenVirtual P1 P2 P3
+/* Opcode: OpenEphemeral P1 P2 P3
 **
-** Open a new cursor P1 to a transient or virtual table.
+** Open a new cursor P1 to a transient table.
 ** The cursor is always opened read/write even if 
 ** the main database is read-only.  The transient or virtual
 ** table is deleted automatically when the cursor is closed.
@@ -2651,8 +2651,14 @@
 ** The cursor points to a BTree table if P3==0 and to a BTree index
 ** if P3 is not 0.  If P3 is not NULL, it points to a KeyInfo structure
 ** that defines the format of keys in the index.
+**
+** This opcode was once called OpenTemp.  But that created
+** confusion because the term "temp table", might refer either
+** to a TEMP table at the SQL level, or to a table opened by
+** this opcode.  Then this opcode was call OpenVirtual.  But
+** that created confusion with the whole virtual-table idea.
 */
-case OP_OpenVirtual: {       /* no-push */
+case OP_OpenEphemeral: {       /* no-push */
   int i = pOp->p1;
   Cursor *pCx;
   assert( i>=0 );
@@ -4525,7 +4531,29 @@
   }
   break;
 }
-#endif /* SHARED_OMIT_SHARED_CACHE */
+#endif /* SQLITE_OMIT_SHARED_CACHE */
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+/* Opcode: VCreate * * P3
+**
+** P3 is the name of a virtual table.  Call the xCreate method for
+** that table.
+*/
+case OP_VCreate: {
+  break;
+}
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+/* Opcode: VDestroy * * P3
+**
+** P3 is the name of a virtual table.  Call the xCreate method for
+** that table.
+*/
+case OP_VDestroy: {
+  break;
+}
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
 
 /* An other opcode is illegal...
 */
diff --git a/src/vdbe.h b/src/vdbe.h
index 4604542..3097022 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -15,7 +15,7 @@
 ** or VDBE.  The VDBE implements an abstract machine that runs a
 ** simple program to access and modify the underlying database.
 **
-** $Id: vdbe.h,v 1.102 2006/03/17 13:56:34 drh Exp $
+** $Id: vdbe.h,v 1.103 2006/06/11 23:41:56 drh Exp $
 */
 #ifndef _SQLITE_VDBE_H_
 #define _SQLITE_VDBE_H_
@@ -69,7 +69,7 @@
 #define P3_KEYINFO  (-6)  /* P3 is a pointer to a KeyInfo structure */
 #define P3_VDBEFUNC (-7)  /* P3 is a pointer to a VdbeFunc structure */
 #define P3_MEM      (-8)  /* P3 is a pointer to a Mem*    structure */
-#define P3_TRANSIENT (-9)  /* P3 is a pointer to a transient string */
+#define P3_TRANSIENT (-9) /* P3 is a pointer to a transient string */
 
 /* When adding a P3 argument using P3_KEYINFO, a copy of the KeyInfo structure
 ** is made.  That copy is freed when the Vdbe is finalized.  But if the
diff --git a/src/vtab.c b/src/vtab.c
new file mode 100644
index 0000000..d7d7515
--- /dev/null
+++ b/src/vtab.c
@@ -0,0 +1,211 @@
+/*
+** 2006 June 10
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains code used to help implement virtual tables.
+**
+** $Id: vtab.c,v 1.1 2006/06/11 23:41:56 drh Exp $
+*/
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+#include "sqliteInt.h"
+
+/*
+** External API function used to create a new virtual-table module.
+*/
+int sqlite3_create_module(
+  sqlite3 *db,                    /* Database in which module is registered */
+  const char *zName,              /* Name assigned to this module */
+  const sqlite3_module *pModule   /* The definition of the module */
+){
+  sqlite3HashInsert(&db->aModule, zName, strlen(zName), (void*)pModule);
+  sqlite3ResetInternalSchema(db, 0);
+  return SQLITE_OK;
+}
+
+
+/*
+** Clear any and all virtual-table information from the Table record.
+** This routine is called, for example, just before deleting the Table
+** record.
+*/
+void sqlite3VtabClear(Table *p){
+  if( p->pVtab ){
+    assert( p->pModule!=0 );
+    p->pModule->xDisconnect(p->pVtab);
+  }
+  if( p->azModuleArg ){
+    int i;
+    for(i=0; i<p->nModuleArg; i++){
+      sqliteFree(p->azModuleArg[i]);
+    }
+    sqliteFree(p->azModuleArg);
+  }
+}
+
+/*
+** Add a new module argument to pTable->azModuleArg[].
+** The string is not copied - the pointer is stored.  The
+** string will be freed automatically when the table is
+** deleted.
+*/
+static void addModuleArgument(Table *pTable, char *zArg){
+  int i = pTable->nModuleArg++;
+  pTable->azModuleArg = sqliteRealloc(pTable->azModuleArg,
+                             sizeof(char*)*(pTable->nModuleArg+1));
+  if( pTable->azModuleArg==0 ){
+    pTable->nModuleArg = 0;
+    sqliteFree(zArg);
+  }else{
+    pTable->azModuleArg[i] = zArg;
+    pTable->azModuleArg[i+1] = 0;
+  }
+}
+
+/*
+** The parser calls this routine when it first sees a CREATE VIRTUAL TABLE
+** statement.  The module name has been parsed, but the optional list
+** of parameters that follow the module name are still pending.
+*/
+void sqlite3VtabBeginParse(
+  Parse *pParse,        /* Parsing context */
+  Token *pName1,        /* Name of new table, or database name */
+  Token *pName2,        /* Name of new table or NULL */
+  Token *pModuleName    /* Name of the module for the virtual table */
+){
+  Table *pTable;        /* The new virtual table */
+
+  sqlite3StartTable(pParse, pName1, pName2, 0, 0, 0);
+  pTable = pParse->pNewTable;
+  if( pTable==0 ) return;
+  pTable->isVirtual = 1;
+  pTable->nModuleArg = 0;
+  addModuleArgument(pTable, sqlite3NameFromToken(pModuleName));
+  pParse->sNameToken.n = pModuleName->z + pModuleName->n - pName1->z;
+}
+
+/*
+** This routine takes the module argument that has been accumulating
+** in pParse->zArg[] and appends it to the list of arguments on the
+** virtual table currently under construction in pParse->pTable.
+*/
+static void addArgumentToVtab(Parse *pParse){
+  if( pParse->nArgUsed && pParse->pNewTable ){
+    addModuleArgument(pParse->pNewTable, sqliteStrDup(pParse->zArg));
+  }
+  pParse->nArgUsed = 0;
+}
+
+/*
+** The parser calls this routine after the CREATE VIRTUAL TABLE statement
+** has been completely parsed.
+*/
+void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
+  Table *pTab;        /* The table being constructed */
+  sqlite3 *db;        /* The database connection */
+  char *zModule;      /* The module name of the table: USING modulename */
+
+  addArgumentToVtab(pParse);
+  sqliteFree(pParse->zArg);
+  pParse->zArg = 0;
+  pParse->nArgAlloc = 0;
+
+  /* Lookup the module name. */
+  pTab = pParse->pNewTable;
+  if( pTab==0 ) return;
+  db = pParse->db;
+  if( pTab->nModuleArg<1 ) return;
+  pParse->pNewTable = 0;
+  zModule = pTab->azModuleArg[0];
+  pTab->pModule = (sqlite3_module*)sqlite3HashFind(&db->aModule, 
+                     zModule, strlen(zModule));
+  
+  /* If the CREATE VIRTUAL TABLE statement is being entered for the
+  ** first time (in other words if the virtual table is actually being
+  ** created now instead of just being read out of sqlite_master) then
+  ** do additional initialization work and store the statement text
+  ** in the sqlite_master table.
+  */
+  if( !db->init.busy ){
+    char *zStmt;
+    int iDb;
+    Vdbe *v;
+    if( pTab->pModule==0 ){
+      sqlite3ErrorMsg(pParse, "unknown module: %s", zModule);
+    }
+
+    /* Compute the complete text of the CREATE VIRTUAL TABLE statement */
+    if( pEnd ){
+      pParse->sNameToken.n = pEnd->z - pParse->sNameToken.z + pEnd->n;
+    }
+    zStmt = sqlite3MPrintf("CREATE VIRTUAL TABLE %T", &pParse->sNameToken);
+
+    /* A slot for the record has already been allocated in the 
+    ** SQLITE_MASTER table.  We just need to update that slot with all
+    ** the information we've collected.  The rowid for the preallocated
+    ** slot is the top the stack.
+    */
+    iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+    sqlite3NestedParse(pParse,
+      "UPDATE %Q.%s "
+         "SET type='table', name=%Q, tbl_name=%Q, rootpage=NULL, sql=%Q "
+       "WHERE rowid=#0",
+      db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
+      pTab->zName,
+      pTab->zName,
+      zStmt
+    );
+    sqliteFree(zStmt);
+    v = sqlite3GetVdbe(pParse);
+    sqlite3VdbeOp3(v, OP_VCreate, 0, 0, pTab->zName, P3_DYNAMIC);
+    sqlite3ChangeCookie(db, v, iDb);
+  }
+
+  /* If we are rereading the sqlite_master table and we happen to
+  ** currently know the module for the new table, create an
+  ** sqlite3_vtab instance.
+  */
+  else if( pTab->pModule ){
+    sqlite3_module *pMod = pTab->pModule;
+    assert( pMod->xConnect );
+    pMod->xConnect(db, pMod, pTab->nModuleArg, pTab->azModuleArg, &pTab->pVtab);
+  }
+}
+
+/*
+** The parser calls this routine when it sees the first token
+** of an argument to the module name in a CREATE VIRTUAL TABLE statement.
+*/
+void sqlite3VtabArgInit(Parse *pParse){
+  addArgumentToVtab(pParse);
+  pParse->nArgUsed = 0;
+}
+
+/*
+** The parser calls this routine for each token after the first token
+** in an argument to the module name in a CREATE VIRTUAL TABLE statement.
+*/
+void sqlite3VtabArgExtend(Parse *pParse, Token *p){
+  if( pParse->nArgUsed + p->n + 2 >= pParse->nArgAlloc ){
+    pParse->nArgAlloc = pParse->nArgAlloc*2 + p->n + 200;
+    pParse->zArg = sqliteRealloc(pParse->zArg, pParse->nArgAlloc);
+    if( pParse->zArg==0 ){
+      pParse->nArgAlloc = 0;
+      return;
+    }
+  }
+  if( pParse->nArgUsed ){
+    pParse->zArg[pParse->nArgUsed++] = ' ';
+  }
+  memcpy(&pParse->zArg[pParse->nArgUsed], p->z, p->n);
+  pParse->nArgUsed += p->n;
+  pParse->zArg[pParse->nArgUsed] = 0;
+}
+
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
diff --git a/src/where.c b/src/where.c
index 4ba6338..515ed44 100644
--- a/src/where.c
+++ b/src/where.c
@@ -16,7 +16,7 @@
 ** so is applicable.  Because this module is responsible for selecting
 ** indices, you might also think of this module as the "query optimizer".
 **
-** $Id: where.c,v 1.209 2006/06/06 11:45:55 drh Exp $
+** $Id: where.c,v 1.210 2006/06/11 23:41:56 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -1601,7 +1601,7 @@
     pTabItem = &pTabList->a[pLevel->iFrom];
     pTab = pTabItem->pTab;
     iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
-    if( pTab->isTransient || pTab->pSelect ) continue;
+    if( pTab->isEphem || pTab->pSelect ) continue;
     if( (pLevel->flags & WHERE_IDX_ONLY)==0 ){
       sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, OP_OpenRead);
       if( pTab->nCol<(sizeof(Bitmask)*8) ){
@@ -2087,7 +2087,7 @@
     struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
     Table *pTab = pTabItem->pTab;
     assert( pTab!=0 );
-    if( pTab->isTransient || pTab->pSelect ) continue;
+    if( pTab->isEphem || pTab->pSelect ) continue;
     if( (pLevel->flags & WHERE_IDX_ONLY)==0 ){
       sqlite3VdbeAddOp(v, OP_Close, pTabItem->iCursor, 0);
     }