diff --git a/manifest b/manifest index 823fad00db..06fbfbb137 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Incremental\swork\son\sparsing/storing\sand\sinvoking\sthe\sxCreate\scallback\sfor\svirtual\stables.\s(CVS\s3212) -D 2006-06-12T06:09:18 +C Add\sfirst\scut\sof\ssqlite3_declare_vtab().\sNot\sat\sall\swell\stested\syet.\s(CVS\s3213) +D 2006-06-12T11:24:37 F Makefile.in 56fd6261e83f60724e6dcd764e06ab68cbd53909 F Makefile.linux-gcc 74ba0eadf88748a9ce3fd03d2a3ede2e6715baec F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -36,7 +36,7 @@ F src/attach.c 27a31d3b89d7ebb5b358847607b1ec795384123c F src/auth.c 9ae84d2d94eb96195e04515715e08e85963e96c2 F src/btree.c ed343b3dbcbc7da9ac481ef2b98c4239fe6d9629 F src/btree.h 40055cfc09defd1146bc5b922399c035f969e56d -F src/build.c f541d3e9afed5eb8a397353b2b54c23d8d531e97 +F src/build.c a72a709541c62402665e053e2e173dfdc62ce049 F src/callback.c fd9bb39f7ff6b52bad8365617abc61c720640429 F src/complete.c 7d1a44be8f37de125fcafd3d3a018690b3799675 F src/date.c cd2bd5d1ebc6fa12d6312f69789ae5b0a2766f2e @@ -72,9 +72,9 @@ F src/random.c d40f8d356cecbd351ccfab6eaedd7ec1b54f5261 F src/select.c 38eda11d950ed5e631ea9054f84a4a8b9e9b39d8 F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96 F src/shell.c b9eb3ed4d3ab41fbf630eabb602f3c9d20fc737a -F src/sqlite.h.in ca30260d7815ee68be410e1fa0bd1f131c86cf10 +F src/sqlite.h.in cad97e59cfad8337f9f43ea9662714a256ca80b2 F src/sqlite3ext.h 127bd394c8eea481f2ac9b754bf399dbfc818b75 -F src/sqliteInt.h 4ac8c35fec748b1886bed00f1086fa2520faea80 +F src/sqliteInt.h ef7b936e38858c98c35ab3cad55eea0859c64a94 F src/table.c f64ec4fbfe333f8df925bc6ba494f55e05b0e75e F src/tclsqlite.c 0b2a04cfc1b4298adfbe90a754cfbbe207aca11a F src/test1.c 88291fa6674dcd409b1c9d76d3119151d4b81a50 @@ -84,11 +84,11 @@ F src/test4.c 8b784cd82de158a2317cb4ac4bc86f91ad315e25 F src/test5.c 7162f8526affb771c4ed256826eee7bb9eca265f F src/test6.c 60a02961ceb7b3edc25f5dc5c1ac2556622a76de F src/test7.c 03fa8d787f6aebc6d1f72504d52f33013ad2c8e3 -F src/test8.c c1a91a3307e4d8d8a46211498111d1c5778b9294 +F src/test8.c 9eb1d2e41b81df15952ee8ee54f95fa214f8813e F src/test_async.c e3deaedd4d86a56391b81808fde9e44fbd92f1d3 F src/test_md5.c 6c42bc0a3c0b54be34623ff77a0eec32b2fa96e3 F src/test_server.c a6460daed0b92ecbc2531b6dc73717470e7a648c -F src/tokenize.c 279c62792222e0ae6efc4f7609bc47301dde1ef3 +F src/tokenize.c 6ebcafa6622839968dda4418a7b6945f277a128f F src/trigger.c 48bbb94c11954c8e132efcc04478efe8304c4196 F src/update.c 0186f09414a6578156d40666becc964f85c2a616 F src/utf.c ab81ac59084ff1c07d421eb1a0a84ec809603b44 @@ -101,7 +101,7 @@ F src/vdbeapi.c 7dc662e7c905ce666bb506dced932e0307115cbf F src/vdbeaux.c 4002e6b19d7c9719cb81f9797316b9ad118e4370 F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5 F src/vdbemem.c 5f0afe3b92bb2c037f8d5d697f7c151fa50783a3 -F src/vtab.c 6872f6a6ca33aac85b62bcd66d883f4deffab00d +F src/vtab.c 36778506d9af9b750e01eeea1e116f3c80b7fa01 F src/where.c 3dc5269ba552c0db39247f6bbc98b312ae786863 F tclinstaller.tcl 046e3624671962dc50f0481d7c25b38ef803eb42 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -287,7 +287,7 @@ F test/vacuum.test 37f998b841cb335397c26d9bbc3457182af2565f F test/vacuum2.test 5aea8c88a65cb29f7d175296e7c819c6158d838c F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 F test/view.test 16e2774fe35e47a07ac4471b7f0bcc948b1aa6d5 -F test/vtab1.test 5bf2bebe96272953928596b256cefb21d9c765b1 +F test/vtab1.test 5c7f4a2a53f3894d571239099c0253e27263cc84 F test/where.test ee7c9a6659b07e1ee61177f6e7ff71565ee2c9df F test/where2.test a16476a5913e75cf65b38f2daa6157a6b7791394 F test/where3.test 3b5ad2c58069e12be2bd86bc5e211a82810521aa @@ -363,7 +363,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P 898ec36b4102aaa03979f8f5c510936e57e2ae48 -R fd0d79bfdfee01e76c00d486659426af +P 8ffbab79d5a76dea0f87cf551d5b6ad4f0fab337 +R 26c5c53e0c5dda8d367ac8454cb93c3e U danielk1977 -Z 21ad209dbac4dc09cf972c3f2b10f101 +Z dfbf7809fd6098db180fe009efbe7300 diff --git a/manifest.uuid b/manifest.uuid index adbc054d49..4d6107d6e8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8ffbab79d5a76dea0f87cf551d5b6ad4f0fab337 \ No newline at end of file +bbeb93b5bb26ba83ee7b7ae439ca5ceebebac9a0 \ No newline at end of file diff --git a/src/build.c b/src/build.c index 0be7c92f94..b9fa185d59 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 @@ -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 3f8a6d7be9..ecd8450bc0 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 de904fb3b9..efd588b346 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 97e8483460..b976f84e39 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 2d49fe8cdf..c50395aeb3 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 bd43e567ce..ae7b7930fc 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; } } @@ -238,6 +234,84 @@ void sqlite3VtabArgExtend(Parse *pParse, Token *p){ pParse->zArg[pParse->nArgUsed] = 0; } +/* +** 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 ){ diff --git a/test/vtab1.test b/test/vtab1.test index 84e20048bf..b0210f09d2 100644 --- a/test/vtab1.test +++ b/test/vtab1.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is creating and dropping virtual tables. # -# $Id: vtab1.test,v 1.2 2006/06/12 06:09:19 danielk1977 Exp $ +# $Id: vtab1.test,v 1.3 2006/06/12 11:24:38 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -48,4 +48,31 @@ do_test vtab1-1.5 { } } {t1 {CREATE VIRTUAL TABLE t1 USING echo}} +# If a single argument is passed to the echo module during table +# creation, it is assumed to be the name of a table in the same +# database. The echo module attempts to set the schema of the +# new virtual table to be the same as the existing database table. +# +do_test vtab1-2.1 { + execsql { + CREATE TABLE template(a, b, c); + } + execsql { PRAGMA table_info(template); } +} [list \ + 0 a {} 0 {} 0 \ + 1 b {} 0 {} 0 \ + 2 c {} 0 {} 0 \ +] +do_test vtab1-2.2 { + execsql { + CREATE VIRTUAL TABLE t2 USING echo(template); + } + execsql { PRAGMA table_info(t2); } +} [list \ + 0 a {} 0 {} 0 \ + 1 b {} 0 {} 0 \ + 2 c {} 0 {} 0 \ +] + finish_test +