diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/build.c | 37 | ||||
-rw-r--r-- | src/sqlite.h.in | 3 | ||||
-rw-r--r-- | src/sqliteInt.h | 11 | ||||
-rw-r--r-- | src/test8.c | 44 | ||||
-rw-r--r-- | src/tokenize.c | 13 | ||||
-rw-r--r-- | src/vtab.c | 107 |
6 files changed, 184 insertions, 31 deletions
diff --git a/src/build.c b/src/build.c index 0be7c92f9..b9fa185d5 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 @@ void sqlite3StartTable( /* 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; + } + 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; } - 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 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ ** 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 3f8a6d7be..ecd8450bc 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 @@ int sqlite3_create_module( 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 de904fb3b..efd588b34 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 @@ struct sqlite3 { #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 @@ struct Parse { 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 sqlite3VtabFinishParse(Parse*, Token*); 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 97e848346..b976f84e3 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 @@ static void appendToEchoModule(const sqlite3_module *pModule, const char *zArg){ 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 @@ static int echoCreate( sqlite3_vtab **ppVtab ){ int i; - Tcl_Interp *interp = pModule->pAux; *ppVtab = pModule->pAux; appendToEchoModule(pModule, "xCreate"); @@ -46,6 +83,7 @@ static int echoCreate( appendToEchoModule(pModule, argv[i]); } + echoDeclareVtab(db, argc, argv); return 0; } static int echoConnect( @@ -63,6 +101,8 @@ static int echoConnect( 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 2d49fe8cd..c50395aeb 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 @@ abort_parse: 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 bd43e567c..ae7b7930f 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 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ 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 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ ** ** 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 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ assert( pTab==pOld ); /* Malloc must have failed inside HashInsert() */ return; } + pParse->pNewTable = 0; } } @@ -239,6 +235,84 @@ void sqlite3VtabArgExtend(Parse *pParse, Token *p){ } /* +** 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 sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){ 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 @@ int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){ *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 ){ |