Add first cut of sqlite3_declare_vtab(). Not at all well tested yet. (CVS 3213)

FossilOrigin-Name: bbeb93b5bb26ba83ee7b7ae439ca5ceebebac9a0
diff --git a/src/build.c b/src/build.c
index 0be7c92..b9fa185 100644
--- a/src/build.c
+++ b/src/build.c
@@ -22,7 +22,7 @@
 **     COMMIT
 **     ROLLBACK
 **
-** $Id: build.c,v 1.396 2006/06/11 23:41:55 drh Exp $
+** $Id: build.c,v 1.397 2006/06/12 11:24:37 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -782,22 +782,28 @@
 
   /* Make sure the new table name does not collide with an existing
   ** index or table name in the same database.  Issue an error message if
-  ** it does.
+  ** it does. The exception is if the statement being parsed was passed
+  ** to an sqlite3_declare_vtab() call. In that case only the column names
+  ** and types will be used, so there is no need to test for namespace
+  ** collisions.
   */
-  if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
-    goto begin_table_error;
-  }
-  pTable = sqlite3FindTable(db, zName, db->aDb[iDb].zName);
-  if( pTable ){
-    if( !noErr ){
-      sqlite3ErrorMsg(pParse, "table %T already exists", pName);
+  if( !IN_DECLARE_VTAB ){
+    if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
+      goto begin_table_error;
     }
-    goto begin_table_error;
+    pTable = sqlite3FindTable(db, zName, db->aDb[iDb].zName);
+    if( pTable ){
+      if( !noErr ){
+        sqlite3ErrorMsg(pParse, "table %T already exists", pName);
+      }
+      goto begin_table_error;
+    }
+    if( sqlite3FindIndex(db, zName, 0)!=0 && (iDb==0 || !db->init.busy) ){
+      sqlite3ErrorMsg(pParse, "there is already an index named %s", zName);
+      goto begin_table_error;
+    }
   }
-  if( sqlite3FindIndex(db, zName, 0)!=0 && (iDb==0 || !db->init.busy) ){
-    sqlite3ErrorMsg(pParse, "there is already an index named %s", zName);
-    goto begin_table_error;
-  }
+
   pTable = sqliteMalloc( sizeof(Table) );
   if( pTable==0 ){
     pParse->rc = SQLITE_NOMEM;
@@ -1649,6 +1655,9 @@
   ** already known.
   */
   if( pTable->nCol>0 ) return 0;
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+  if( pTable->isVirtual ) return 0;
+#endif
 
   /* A negative nCol is a special marker meaning that we are currently
   ** trying to compute the column names.  If we enter this routine with
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 3f8a6d7..ecd8450 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.168 2006/06/11 23:41:56 drh Exp $
+** @(#) $Id: sqlite.h.in,v 1.169 2006/06/12 11:24:37 danielk1977 Exp $
 */
 #ifndef _SQLITE3_H_
 #define _SQLITE3_H_
@@ -1575,6 +1575,7 @@
   const sqlite3_module *
 );
 
+int sqlite3_declare_vtab(sqlite3*, const char *zCreateTable);
 
 /*
 ** Undo the hack that converts floating point types to integer for
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index de904fb..efd588b 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.498 2006/06/12 06:09:18 danielk1977 Exp $
+** @(#) $Id: sqliteInt.h,v 1.499 2006/06/12 11:24:37 danielk1977 Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -492,6 +492,7 @@
 #endif
 #ifndef SQLITE_OMIT_VIRTUALTABLE
   Hash aModule;                 /* populated by sqlite3_create_module() */
+  Table *pVTab;                 /* vtab with active Connect/Create method */
 #endif
   Hash aFunc;                   /* All functions that can be in SQL exprs */
   Hash aCollSeq;                /* All collating sequences */
@@ -1283,9 +1284,16 @@
   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 */
+  u8 declareVtab;           /* True if inside sqlite3_declare_vtab() */
 #endif
 };
 
+#ifdef SQLITE_OMIT_VIRTUALTABLE
+  #define IN_DECLARE_VTAB 0
+#else
+  #define IN_DECLARE_VTAB (pParse->declareVtab)
+#endif
+
 /*
 ** An instance of the following structure can be declared on a stack and used
 ** to save the Parse.zAuthContext value so that it can be restored later.
@@ -1791,6 +1799,7 @@
 void sqlite3VtabArgInit(Parse*);
 void sqlite3VtabArgExtend(Parse*, Token*);
 int sqlite3VtabCallCreate(sqlite3*, int, const char *, char **);
+int sqlite3VtabCallConnect(Parse*, Table*);
 
 #ifdef SQLITE_SSE
 #include "sseInt.h"
diff --git a/src/test8.c b/src/test8.c
index 97e8483..b976f84 100644
--- a/src/test8.c
+++ b/src/test8.c
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test8.c,v 1.2 2006/06/12 06:09:19 danielk1977 Exp $
+** $Id: test8.c,v 1.3 2006/06/12 11:24:37 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -30,6 +30,44 @@
   Tcl_SetVar((Tcl_Interp *)(pModule->pAux), "echo_module", zArg, flags);
 }
 
+/*
+** This function is called from within the echo-modules xCreate and
+** xConnect methods. The argc and argv arguments are copies of those 
+** passed to the calling method. This function is responsible for
+** calling sqlite3_declare_vtab() to declare the schema of the virtual
+** table being created or connected.
+**
+** If the constructor was passed just one argument, i.e.:
+**
+**   CREATE TABLE t1 AS echo(t2);
+**
+** Then t2 is assumed to be the name of a *real* database table. The
+** schema of the virtual table is declared by passing a copy of the 
+** CREATE TABLE statement for the real table to sqlite3_declare_vtab().
+** Hence, the virtual table should have exactly the same column names and 
+** types as the real table.
+*/
+static int echoDeclareVtab(sqlite3 *db, int argc, char **argv){
+  int rc = SQLITE_OK;
+
+  if( argc==2 ){
+    sqlite3_stmt *pStmt = 0;
+    sqlite3_prepare(db, 
+        "SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?",
+        -1, &pStmt, 0);
+    sqlite3_bind_text(pStmt, 1, argv[1], -1, 0);
+    if( sqlite3_step(pStmt)==SQLITE_ROW ){
+      const char *zCreateTable = sqlite3_column_text(pStmt, 0);
+      sqlite3_declare_vtab(db, zCreateTable);
+    } else {
+      rc = SQLITE_ERROR;
+    }
+    sqlite3_finalize(pStmt);
+  }
+
+  return rc;
+}
+
 /* Methods for the echo module */
 static int echoCreate(
   sqlite3 *db,
@@ -38,7 +76,6 @@
   sqlite3_vtab **ppVtab
 ){
   int i;
-  Tcl_Interp *interp = pModule->pAux;
   *ppVtab = pModule->pAux;
 
   appendToEchoModule(pModule, "xCreate");
@@ -46,6 +83,7 @@
     appendToEchoModule(pModule, argv[i]);
   }
 
+  echoDeclareVtab(db, argc, argv);
   return 0;
 }
 static int echoConnect(
@@ -63,6 +101,8 @@
     Tcl_SetVar(interp, "echo_module", argv[i],
                 TCL_APPEND_VALUE | TCL_LIST_ELEMENT | TCL_GLOBAL_ONLY);
   }
+
+  echoDeclareVtab(db, argc, argv);
   return 0;
 }
 static int echoDisconnect(sqlite3_vtab *pVtab){
diff --git a/src/tokenize.c b/src/tokenize.c
index 2d49fe8..c50395a 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.119 2006/06/11 23:41:56 drh Exp $
+** $Id: tokenize.c,v 1.120 2006/06/12 11:24:37 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -483,10 +483,19 @@
     pParse->nTableLock = 0;
   }
 #endif
+
 #ifndef SQLITE_OMIT_VIRTUALTABLE
   sqliteFree(pParse->zArg);
 #endif
-  sqlite3DeleteTable(pParse->db, pParse->pNewTable);
+
+  if( !IN_DECLARE_VTAB ){
+    /* If the pParse->declareVtab flag is set, do not delete any table 
+    ** structure built up in pParse->pNewTable. The calling code (see vtab.c)
+    ** will take responsibility for freeing the Table structure.
+    */
+    sqlite3DeleteTable(pParse->db, pParse->pNewTable);
+  }
+
   sqlite3DeleteTrigger(pParse->pNewTrigger);
   sqliteFree(pParse->apVarExpr);
   if( nErr>0 && (pParse->rc==SQLITE_OK || pParse->rc==SQLITE_DONE) ){
diff --git a/src/vtab.c b/src/vtab.c
index bd43e56..ae7b793 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.2 2006/06/12 06:09:19 danielk1977 Exp $
+** $Id: vtab.c,v 1.3 2006/06/12 11:24:37 danielk1977 Exp $
 */
 #ifndef SQLITE_OMIT_VIRTUALTABLE
 #include "sqliteInt.h"
@@ -128,7 +128,6 @@
   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));
@@ -188,14 +187,10 @@
   **
   ** TODO: If the module is already registered, should we call xConnect()
   ** here, or should it wait until the table is first referenced. Maybe
-  ** it's better to be lazy here, in case xConnect() is expensive to call.
+  ** it's better to be lazy here, in case xConnect() is expensive to call
+  ** and the schema is reparsed a number of times.
   */
   else {
-#if 0
-    sqlite3_module *pMod = pTab->pModule;
-    assert( pMod->xConnect );
-    pMod->xConnect(db, pMod, pTab->nModuleArg, pTab->azModuleArg, &pTab->pVtab);
-#endif
     Table *pOld;
     Schema *pSchema = pTab->pSchema;
     const char *zName = pTab->zName;
@@ -205,6 +200,7 @@
       assert( pTab==pOld );  /* Malloc must have failed inside HashInsert() */
       return;
     }
+    pParse->pNewTable = 0;
   }
 }
 
@@ -239,6 +235,84 @@
 }
 
 /*
+** This function is invoked by the parser to call the xConnect() method
+** of table pTab. If an error occurs, an error code is returned and an error
+** left in pParse.
+*/
+int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){
+  sqlite3_module *pModule;
+  const char *zModule;
+  int rc = SQLITE_OK;
+
+  assert(pTab && pTab->isVirtual);
+  if( pTab->pVtab ){
+    return SQLITE_OK;
+  }
+
+  pModule = pTab->pModule;
+  zModule = pTab->azModuleArg[0];
+  if( !pModule || !pModule->xConnect ){
+    const char *zModule = pTab->azModuleArg[0];
+    sqlite3ErrorMsg(pParse, "unknown module: %s", zModule);
+    rc = SQLITE_ERROR;
+  } else {
+    char **azArg = pTab->azModuleArg;
+    int nArg = pTab->nModuleArg;
+    assert( !pParse->db->pVTab );
+    pParse->db->pVTab = pTab;
+    rc = pModule->xConnect(pParse->db, pModule, nArg, azArg, &pTab->pVtab);
+    pParse->db->pVTab = 0;
+    if( rc ){
+      sqlite3ErrorMsg(pParse, "module connect failed: %s", zModule);
+    }
+  }
+
+  return rc;
+}
+
+int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
+  Parse sParse;
+
+  int rc = SQLITE_OK;
+  Table *pTab = db->pVTab;
+  char *zErr = 0;
+
+  if( !pTab ){
+    sqlite3Error(db, SQLITE_MISUSE, 0);
+    return SQLITE_MISUSE;
+  }
+  assert(pTab->isVirtual && pTab->nCol==0 && pTab->aCol==0);
+
+  memset(&sParse, 0, sizeof(Parse));
+  sParse.declareVtab = 1;
+  sParse.db = db;
+
+  if( 
+      SQLITE_OK == sqlite3RunParser(&sParse, zCreateTable, &zErr) && 
+      sParse.pNewTable && 
+      !sParse.pNewTable->pSelect && 
+      !sParse.pNewTable->isVirtual 
+  ){
+    pTab->aCol = sParse.pNewTable->aCol;
+    pTab->nCol = sParse.pNewTable->nCol;
+    sParse.pNewTable->nCol = 0;
+    sParse.pNewTable->aCol = 0;
+  } else {
+    sqlite3Error(db, SQLITE_ERROR, zErr);
+    sqliteFree(zErr);
+    rc = SQLITE_ERROR;
+  }
+  sParse.declareVtab = 0;
+
+  sqlite3_finalize((sqlite3_stmt*)sParse.pVdbe);
+  sqlite3DeleteTable(0, sParse.pNewTable);
+  sParse.pNewTable = 0;
+  db->pVTab = 0;
+
+  return rc;
+}
+
+/*
 ** This function is invoked by the vdbe to call the xCreate method
 ** of the virtual table named zTab in database iDb. 
 **
@@ -250,11 +324,12 @@
   int rc = SQLITE_OK;
   Table *pTab;
   sqlite3_module *pModule;
+  const char *zModule;
 
   pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName);
-  assert(pTab && pTab->isVirtual);
+  assert(pTab && pTab->isVirtual && !pTab->pVtab);
   pModule = pTab->pModule;
-  const char *zModule = pTab->azModuleArg[0];
+  zModule = pTab->azModuleArg[0];
 
   /* If the module has been registered and includes a Create method, 
   ** invoke it now. If the module has not been registered, return an 
@@ -264,10 +339,20 @@
     *pzErr = sqlite3MPrintf("unknown module: %s", zModule);
     rc = SQLITE_ERROR;
   }else if( pModule->xCreate ){
-    /* TODO: Maybe the above condition should refer to pTable->needCreate. */
     char **azArg = pTab->azModuleArg;
     int nArg = pTab->nModuleArg;
+    assert( !db->pVTab );
+    db->pVTab = pTab;
+    rc = sqlite3SafetyOff(db);
+    assert( rc==SQLITE_OK );
     rc = pModule->xCreate(db, pModule, nArg, azArg, &pTab->pVtab);
+    db->pVTab = 0;
+    if( rc ){
+      *pzErr = sqlite3MPrintf("module create failed: %s", zModule);
+      sqlite3SafetyOn(db);
+    } else {
+      rc = sqlite3SafetyOn(db);
+    }
   }
 
   if( SQLITE_OK==rc ){