From 1c2d84148a6bb2f8d231debd65bc09e948abf029 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 31 Mar 2003 00:30:47 +0000 Subject: [PATCH] The ATTACH and DETACH statements are now coded but are still mostly untested. (CVS 890) FossilOrigin-Name: c7c5e927a54f0fbc2ca625754787aff4d9c4eff1 --- manifest | 20 +++--- manifest.uuid | 2 +- src/build.c | 167 +++++++++++++++++++++++++++++++++++++++------- src/main.c | 171 +++++++++++++++++++++++++++++++----------------- src/parse.y | 12 ++-- src/sqlite.h.in | 3 +- src/sqliteInt.h | 8 ++- 7 files changed, 279 insertions(+), 104 deletions(-) diff --git a/manifest b/manifest index 5eea80ebd0..2e38bb3ef8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sthe\sexperimental\ssqlite_open_aux_file()\sAPI.\s\sIt\swill\ssoon\sbe\sreplaced\nby\sATTACH\sand\sDETACH\sSQL\scommands.\s(CVS\s1732) -D 2003-03-30T19:17:03 +C The\sATTACH\sand\sDETACH\sstatements\sare\snow\scoded\sbut\sare\sstill\smostly\suntested.\s(CVS\s890) +D 2003-03-31T00:30:48 F Makefile.in 3c4ba24253e61c954d67adbbb4245e7117c5357e F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd @@ -22,7 +22,7 @@ F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2 F src/auth.c f37bfc9451b8c1fa52f34adff474560018892729 F src/btree.c dba4d12945228dd7e94de7da0e1d8638b70d99f2 F src/btree.h 8209bfadf5845d4fdaa60f471bb360f894cd4095 -F src/build.c afe256e29f62733d8d8816c220eb822a6317a444 +F src/build.c e56df7b1ccf923e75707953181c91380134253be F src/delete.c 923497248e0ff9097a595c6333ec6d67fe6650b5 F src/encode.c faf03741efe921755ec371cf4a6984536de00042 F src/expr.c eae205a27ec45232f234f281f8827c3be58b303d @@ -30,20 +30,20 @@ F src/func.c 882c3ed5a02be18cd904715c7ec62947a34a3605 F src/hash.c 4fc39feb7b7711f6495ee9f2159559bedb043e1f F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8 F src/insert.c 95e7ab3fb51909351273a6c6f8aa831201130ce7 -F src/main.c df729d604e3055b8081e3d4348983dfd30d2835d +F src/main.c 6d9a38491fdc40c041df64a7399244c364481a09 F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565 F src/os.c dfed46091f69cd2d1e601f8a214d41344f2b00b6 F src/os.h aa52f0c9da321ff6134d19f2ca959e18e33615d0 F src/pager.c dd1dfa4d929a58b44175f3117360ff1553671173 F src/pager.h 97d9a8cc5103750efd8037d71ebfb41849ef2f2f -F src/parse.y 243cfc277d0e5ce87086c015e717dec98d6648a7 +F src/parse.y 3be47fa18323aa2e3364fc42bf7a6ba5b3cc0a81 F src/printf.c fc5fdef6e92ad205005263661fe9716f55a49f3e F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe F src/select.c afdc06d4606d14ab5793ef480206def6b02a2f19 F src/shell.c c13ff46e905a59eb1c7dbea7c1850f8f115e6395 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e -F src/sqlite.h.in 91f94c73514477ffef54aaa452f7fcb62fc59d1e -F src/sqliteInt.h 5335f694cba9f07538cf8207aab11d48e0b9a2f8 +F src/sqlite.h.in be3e56214fecc73d72195ca62d8a3d6663602ff4 +F src/sqliteInt.h b86c03c496bdc9ff543a12a1d88766fbcf28cd0c F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63 F src/tclsqlite.c 4cb0ffa863123ae037db359849a231ff5cebfed4 F src/test1.c 7ad4e6308dde0bf5a0f0775ce20cb2ec37a328f8 @@ -155,7 +155,7 @@ F www/speed.tcl cb4c10a722614aea76d2c51f32ee43400d5951be F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 -P b6d6e07f3a5cb493f2cf0675bc6061c5afe5c078 -R 359232b433a24865017ceb4c15ffe33b +P 0a358844e40020557c1aeea5779b194670350930 +R b07af9b76a439a5b405050fd15558aa2 U drh -Z b46b224b9a3ce8983137da5d0635bd02 +Z 8c6d8642908a4e47e8f8d0600ce4a4a6 diff --git a/manifest.uuid b/manifest.uuid index 9257c97277..b6b9aa9aec 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0a358844e40020557c1aeea5779b194670350930 \ No newline at end of file +c7c5e927a54f0fbc2ca625754787aff4d9c4eff1 \ No newline at end of file diff --git a/src/build.c b/src/build.c index 141d15f14b..059c69be85 100644 --- a/src/build.c +++ b/src/build.c @@ -25,7 +25,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.136 2003/03/30 00:19:50 drh Exp $ +** $Id: build.c,v 1.137 2003/03/31 00:30:48 drh Exp $ */ #include "sqliteInt.h" #include @@ -186,14 +186,20 @@ void sqliteUnlinkAndDeleteIndex(sqlite *db, Index *pIndex){ ** database connection. This routine is called to reclaim memory ** before the connection closes. It is also called during a rollback ** if there were schema changes during the transaction. +** +** If iDb<=0 then reset the internal schema tables for all database +** files. If iDb>=2 then reset the internal schema for only the +** single file indicates. */ -void sqliteResetInternalSchema(sqlite *db){ +void sqliteResetInternalSchema(sqlite *db, int iDb){ HashElem *pElem; Hash temp1; Hash temp2; - int i; + int i, j; - for(i=0; inDb; i++){ + assert( iDb>=0 && iDbnDb ); + db->flags &= ~SQLITE_Initialized; + for(i=iDb; inDb; i++){ Db *pDb = &db->aDb[i]; temp1 = pDb->tblHash; temp2 = pDb->trigHash; @@ -211,8 +217,35 @@ void sqliteResetInternalSchema(sqlite *db){ sqliteDeleteTable(db, pTab); } sqliteHashClear(&temp1); + db->aDb[i].flags &= ~SQLITE_Initialized; + if( iDb>0 ) return; + } + assert( iDb==0 ); + db->flags &= ~SQLITE_InternChanges; + + /* If one or more of the auxiliary database files has been closed, + ** then remove then from the auxiliary database list. We take the + ** opportunity to do this here since we have just deleted all of the + ** schema hash tables and therefore do not have to make any changes + ** to any of those tables. + */ + for(i=j=2; inDb; i++){ + if( db->aDb[i].pBt==0 ){ + sqliteFree(db->aDb[i].zName); + db->aDb[i].zName = 0; + continue; + } + if( jaDb[j++] = db->aDb[i]; + } + } + memset(&db->aDb[j], 0, (db->nDb-j)*sizeof(db->aDb[j])); + db->nDb = j; + if( db->nDb<=2 && db->aDb!=db->aDbStatic ){ + memcpy(db->aDbStatic, db->aDb, 2*sizeof(db->aDb[0])); + sqliteFree(db->aDb); + db->aDb = db->aDbStatic; } - db->flags &= ~(SQLITE_Initialized|SQLITE_InternChanges); } /* @@ -222,7 +255,7 @@ void sqliteResetInternalSchema(sqlite *db){ */ void sqliteRollbackInternalChanges(sqlite *db){ if( db->flags & SQLITE_InternChanges ){ - sqliteResetInternalSchema(db); + sqliteResetInternalSchema(db, 0); } } @@ -366,6 +399,7 @@ void sqliteStartTable( char *zName; sqlite *db = pParse->db; Vdbe *v; + int iDb; pParse->sFirstToken = *pStart; zName = sqliteTableNameFromToken(pName); @@ -430,7 +464,8 @@ void sqliteStartTable( ** an existing temporary table, that is not an error. */ pTable = sqliteFindTable(db, zName, 0); - if( pTable!=0 && (pTable->iDb==isTemp || !pParse->initFlag) ){ + iDb = isTemp ? 1 : pParse->iDb; + if( pTable!=0 && (pTable->iDb==iDb || !pParse->initFlag) ){ sqliteSetNString(&pParse->zErrMsg, "table ", 0, pName->z, pName->n, " already exists", 0, 0); sqliteFree(zName); @@ -455,7 +490,7 @@ void sqliteStartTable( pTable->aCol = 0; pTable->iPKey = -1; pTable->pIndex = 0; - pTable->iDb = isTemp ? 1 : pParse->iDb; + pTable->iDb = iDb; if( pParse->pNewTable ) sqliteDeleteTable(db, pParse->pNewTable); pParse->pNewTable = pTable; @@ -1458,7 +1493,7 @@ void sqliteCreateIndex( pParse->nErr++; goto exit_create_index; } - if( !isTemp && pTab->iDb>=2 ){ + if( !isTemp && pTab->iDb>=2 && pParse->initFlag==0 ){ sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName, " may not have non-temporary indices added", 0); pParse->nErr++; @@ -1473,20 +1508,6 @@ void sqliteCreateIndex( isTemp = 1; } - -#if 0 - /* If this index is created while re-reading the schema from sqlite_master - ** but the table associated with this index is a temporary table, it can - ** only mean that the table that this index is really associated with is - ** one whose name is hidden behind a temporary table with the same name. - ** Since its table has been suppressed, we need to also suppress the - ** index. - */ - if( pParse->initFlag && !pParse->isTemp && pTab->iDb ){ - goto exit_create_index; - } -#endif - /* ** Find the name of the index. Make sure there is not already another ** index or table with the same name. @@ -2151,7 +2172,7 @@ void sqliteCodeVerifySchema(Parse *pParse){ Vdbe *v = sqliteGetVdbe(pParse); for(i=0; inDb; i++){ if( i==1 || db->aDb[i].pBt==0 ) continue; - sqliteVdbeAddOp(v, OP_VerifyCookie, 0, db->aDb[i].schema_cookie); + sqliteVdbeAddOp(v, OP_VerifyCookie, i, db->aDb[i].schema_cookie); } pParse->schemaVerified = 1; } @@ -2641,3 +2662,101 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){ sqliteFree(zLeft); sqliteFree(zRight); } + +/* +** This routine is called by the parser to process an ATTACH statement: +** +** ATTACH DATABASE filename AS dbname +** +** The pFilename and pDbname arguments are the tokens that define the +** filename and dbname in the ATTACH statement. +*/ +void sqliteAttach(Parse *pParse, Token *pFilename, Token *pDbname){ + Db *aNew; + int rc, i; + char *zFile, *zName; + sqlite *db; + + if( pParse->explain ) return; + db = pParse->db; + if( db->aDb==db->aDbStatic ){ + aNew = sqliteMalloc( sizeof(db->aDb[0])*3 ); + if( aNew==0 ) return; + memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); + }else{ + aNew = sqliteRealloc(db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); + if( aNew==0 ) return; + } + db->aDb = aNew; + aNew = &db->aDb[db->nDb++]; + memset(aNew, 0, sizeof(*aNew)); + sqliteHashInit(&aNew->tblHash, SQLITE_HASH_STRING, 0); + sqliteHashInit(&aNew->idxHash, SQLITE_HASH_STRING, 0); + sqliteHashInit(&aNew->trigHash, SQLITE_HASH_STRING, 0); + sqliteHashInit(&aNew->aFKey, SQLITE_HASH_STRING, 1); + + zName = 0; + sqliteSetNString(&zName, pDbname->z, pDbname->n, 0); + if( zName==0 ) return; + sqliteDequote(zName); + for(i=0; inDb; i++){ + if( db->aDb[i].zName && sqliteStrICmp(db->aDb[i].zName, zName)==0 ){ + sqliteSetString(&pParse->zErrMsg, "database \"", zName, + "\" already in use", 0); + sqliteFree(zName); + pParse->nErr++; + return; + } + } + aNew->zName = zName; + zFile = 0; + sqliteSetNString(&zFile, pFilename->z, pFilename->n, 0); + if( zFile==0 ) return; + sqliteDequote(zFile); + rc = sqliteBtreeOpen(zFile, 0, MAX_PAGES, &aNew->pBt); + if( rc ){ + sqliteSetString(&pParse->zErrMsg, "unable to open database: ", zFile, 0); + pParse->nErr++; + } + sqliteFree(zFile); + db->flags &= ~SQLITE_Initialized; + if( pParse->nErr ) return; + rc = sqliteInit(pParse->db, &pParse->zErrMsg); + if( rc ){ + pParse->nErr++; + } +} + +/* +** This routine is called by the parser to process a DETACH statement: +** +** DETACH DATABASE dbname +** +** The pDbname argument is the name of the database in the DETACH statement. +*/ +void sqliteDetach(Parse *pParse, Token *pDbname){ + int i; + sqlite *db; + + if( pParse->explain ) return; + db = pParse->db; + for(i=0; inDb; i++){ + if( db->aDb[i].pBt==0 || db->aDb[i].zName==0 ) continue; + if( strlen(db->aDb[i].zName)!=pDbname->n ) continue; + if( sqliteStrNICmp(db->aDb[i].zName, pDbname->z, pDbname->n)==0 ) break; + } + if( i>=db->nDb ){ + sqliteSetNString(&pParse->zErrMsg, "no such database: ", -1, + pDbname->z, pDbname->n, 0); + pParse->nErr++; + return; + } + if( i<2 ){ + sqliteSetString(&pParse->zErrMsg, "cannot detached \"main\" or \"temp\"",0); + pParse->nErr++; + return; + } + sqliteBtreeClose(db->aDb[i].pBt); + db->aDb[i].pBt = 0; + sqliteResetInternalSchema(db, 0); +} diff --git a/src/main.c b/src/main.c index ee1985fc22..aa4e4b3cf1 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.118 2003/03/30 19:17:02 drh Exp $ +** $Id: main.c,v 1.119 2003/03/31 00:30:48 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -40,7 +40,8 @@ typedef struct { ** argv[1] = table or index name or meta statement type. ** argv[2] = root page number for table or index. NULL for meta. ** argv[3] = SQL text for a CREATE TABLE or CREATE INDEX statement. -** argv[4] = "1" for temporary files, "0" for main database +** argv[4] = "1" for temporary files, "0" for main database, "2" or more +** for auxiliary database files. ** */ static @@ -158,22 +159,19 @@ int upgrade_3_callback(void *pInit, int argc, char **argv, char **NotUsed){ /* ** Attempt to read the database schema and initialize internal -** data structures. Return one of the SQLITE_ error codes to +** data structures for a single database file. The index of the +** database file is given by iDb. iDb==0 is used for the main +** database. iDb==1 should never be used. iDb>=2 is used for +** auxiliary databases. Return one of the SQLITE_ error codes to ** indicate success or failure. -** -** After the database is initialized, the SQLITE_Initialized -** bit is set in the flags field of the sqlite structure. An -** attempt is made to initialize the database as soon as it -** is opened. If that fails (perhaps because another process -** has the sqlite_master table locked) than another attempt -** is made the first time the database is accessed. */ -int sqliteInit(sqlite *db, char **pzErrMsg){ +static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){ int rc; BtCursor *curMain; int size; Table *pTab; char *azArg[6]; + char zDbNum[30]; int meta[SQLITE_N_BTREE_META]; Parse sParse; InitData initData; @@ -228,13 +226,16 @@ int sqliteInit(sqlite *db, char **pzErrMsg){ "WHERE type='index'"; + assert( iDb>=0 && iDb!=1 && iDbnDb ); + /* Construct the schema tables: sqlite_master and sqlite_temp_master */ azArg[0] = "table"; azArg[1] = MASTER_NAME; azArg[2] = "2"; azArg[3] = master_schema; - azArg[4] = "0"; + sprintf(zDbNum, "%d", iDb); + azArg[4] = zDbNum; azArg[5] = 0; initData.db = db; initData.pzErrMsg = pzErrMsg; @@ -243,59 +244,68 @@ int sqliteInit(sqlite *db, char **pzErrMsg){ if( pTab ){ pTab->readOnly = 1; } - azArg[1] = TEMP_MASTER_NAME; - azArg[3] = temp_master_schema; - azArg[4] = "1"; - sqliteInitCallback(&initData, 5, azArg, 0); - pTab = sqliteFindTable(db, TEMP_MASTER_NAME, "temp"); - if( pTab ){ - pTab->readOnly = 1; + if( iDb==0 ){ + azArg[1] = TEMP_MASTER_NAME; + azArg[3] = temp_master_schema; + azArg[4] = "1"; + sqliteInitCallback(&initData, 5, azArg, 0); + pTab = sqliteFindTable(db, TEMP_MASTER_NAME, "temp"); + if( pTab ){ + pTab->readOnly = 1; + } } /* Create a cursor to hold the database open */ - if( db->aDb[0].pBt==0 ) return SQLITE_OK; - rc = sqliteBtreeCursor(db->aDb[0].pBt, 2, 0, &curMain); + if( db->aDb[iDb].pBt==0 ) return SQLITE_OK; + rc = sqliteBtreeCursor(db->aDb[iDb].pBt, 2, 0, &curMain); if( rc ){ sqliteSetString(pzErrMsg, sqlite_error_string(rc), 0); - sqliteResetInternalSchema(db); return rc; } /* Get the database meta information */ - rc = sqliteBtreeGetMeta(db->aDb[0].pBt, meta); + rc = sqliteBtreeGetMeta(db->aDb[iDb].pBt, meta); if( rc ){ sqliteSetString(pzErrMsg, sqlite_error_string(rc), 0); - sqliteResetInternalSchema(db); sqliteBtreeCloseCursor(curMain); return rc; } - db->next_cookie = db->aDb[0].schema_cookie = meta[1]; - db->file_format = meta[2]; - size = meta[3]; - if( size==0 ){ size = MAX_PAGES; } - db->cache_size = size; - sqliteBtreeSetCacheSize(db->aDb[0].pBt, size); - db->safety_level = meta[4]; - if( db->safety_level==0 ) db->safety_level = 2; - sqliteBtreeSetSafetyLevel(db->aDb[0].pBt, db->safety_level); + db->aDb[iDb].schema_cookie = meta[1]; + if( iDb==0 ){ + db->next_cookie = meta[1]; + db->file_format = meta[2]; + size = meta[3]; + if( size==0 ){ size = MAX_PAGES; } + db->cache_size = size; + db->safety_level = meta[4]; + if( db->safety_level==0 ) db->safety_level = 2; - /* - ** file_format==1 Version 2.1.0. - ** file_format==2 Version 2.2.0. Add support for INTEGER PRIMARY KEY. - ** file_format==3 Version 2.6.0. Fix empty-string index bug. - ** file_format==4 Version 2.7.0. Add support for separate numeric and - ** text datatypes. - */ - if( db->file_format==0 ){ - /* This happens if the database was initially empty */ - db->file_format = 4; - }else if( db->file_format>4 ){ - sqliteBtreeCloseCursor(curMain); - sqliteSetString(pzErrMsg, "unsupported file format", 0); - return SQLITE_ERROR; + /* + ** file_format==1 Version 2.1.0. + ** file_format==2 Version 2.2.0. Add support for INTEGER PRIMARY KEY. + ** file_format==3 Version 2.6.0. Fix empty-string index bug. + ** file_format==4 Version 2.7.0. Add support for separate numeric and + ** text datatypes. + */ + if( db->file_format==0 ){ + /* This happens if the database was initially empty */ + db->file_format = 4; + }else if( db->file_format>4 ){ + sqliteBtreeCloseCursor(curMain); + sqliteSetString(pzErrMsg, "unsupported file format", 0); + return SQLITE_ERROR; + } + }else if( db->file_format<4 || db->file_format!=meta[2] ){ + sqliteSetString(pzErrMsg, "incompatible file format in auxiliary " + "database \"", db->aDb[iDb].zName, "\"", 0); + sqliteBtreeClose(db->aDb[iDb].pBt); + db->aDb[iDb].pBt = 0; + return SQLITE_FORMAT; } + sqliteBtreeSetCacheSize(db->aDb[iDb].pBt, size); + sqliteBtreeSetSafetyLevel(db->aDb[iDb].pBt, meta[4]==0 ? 2 : meta[4]); /* Read the schema information out of the schema tables */ @@ -305,24 +315,62 @@ int sqliteInit(sqlite *db, char **pzErrMsg){ sParse.pArg = (void*)&initData; sParse.initFlag = 1; sParse.useCallback = 1; - sqliteRunParser(&sParse, - db->file_format>=2 ? init_script : older_init_script, - pzErrMsg); + if( iDb==0 ){ + sqliteRunParser(&sParse, + db->file_format>=2 ? init_script : older_init_script, + pzErrMsg); + }else{ + char *zSql = 0; + sqliteSetString(&zSql, + "SELECT type, name, rootpage, sql, ", zDbNum, " FROM \"", + db->aDb[iDb].zName, "\".sqlite_master", 0); + sqliteRunParser(&sParse, zSql, pzErrMsg); + sqliteFree(zSql); + } + sqliteBtreeCloseCursor(curMain); if( sqlite_malloc_failed ){ sqliteSetString(pzErrMsg, "out of memory", 0); sParse.rc = SQLITE_NOMEM; - sqliteBtreeRollback(db->aDb[0].pBt); - sqliteResetInternalSchema(db); + sqliteResetInternalSchema(db, 0); } if( sParse.rc==SQLITE_OK ){ + db->aDb[iDb].flags |= SQLITE_Initialized; + }else{ + sqliteResetInternalSchema(db, iDb); + } + return sParse.rc; +} + +/* +** Initialize all database files - the main database file, the file +** used to store temporary tables, and any additional database files +** created using ATTACH statements. Return a success code. If an +** error occurs, write an error message into *pzErrMsg. +** +** After the database is initialized, the SQLITE_Initialized +** bit is set in the flags field of the sqlite structure. An +** attempt is made to initialize the database as soon as it +** is opened. If that fails (perhaps because another process +** has the sqlite_master table locked) than another attempt +** is made the first time the database is accessed. +*/ +int sqliteInit(sqlite *db, char **pzErrMsg){ + int i, rc; + + assert( (db->flags & SQLITE_Initialized)==0 ); + rc = SQLITE_OK; + for(i=0; rc==SQLITE_OK && inDb; i++){ + if( db->aDb[i].flags & SQLITE_Initialized ) continue; + if( i==1 ) continue; /* Skip the temp database - initialized with 0 */ + rc = sqliteInitOne(db, i, pzErrMsg); + } + if( rc==SQLITE_OK ){ db->flags |= SQLITE_Initialized; sqliteCommitInternalChanges(db); }else{ db->flags &= ~SQLITE_Initialized; - sqliteResetInternalSchema(db); } - sqliteBtreeCloseCursor(curMain); - return sParse.rc; + return rc; } /* @@ -476,15 +524,16 @@ void sqlite_close(sqlite *db){ for(j=0; jnDb; j++){ if( db->aDb[j].pBt ){ sqliteBtreeClose(db->aDb[j].pBt); + db->aDb[j].pBt = 0; } if( j>=2 ){ sqliteFree(db->aDb[j].zName); + db->aDb[j].zName = 0; } } - if( db->aDb!=db->aDbStatic ){ - sqliteFree(db->aDb); - } - sqliteResetInternalSchema(db); + sqliteResetInternalSchema(db, 0); + assert( db->nDb<=2 ); + assert( db->aDb==db->aDbStatic ); for(i=sqliteHashFirst(&db->aFunc); i; i=sqliteHashNext(i)){ FuncDef *pFunc, *pNext; for(pFunc = (FuncDef*)sqliteHashData(i); pFunc; pFunc=pNext){ @@ -673,7 +722,7 @@ static int sqliteMain( sqliteSetString(pzErrMsg, "out of memory", 0); sParse.rc = SQLITE_NOMEM; sqliteRollbackAll(db); - sqliteResetInternalSchema(db); + sqliteResetInternalSchema(db, 0); db->flags &= ~SQLITE_InTrans; } if( sParse.rc==SQLITE_DONE ) sParse.rc = SQLITE_OK; @@ -682,7 +731,7 @@ static int sqliteMain( } sqliteStrRealloc(pzErrMsg); if( sParse.rc==SQLITE_SCHEMA ){ - sqliteResetInternalSchema(db); + sqliteResetInternalSchema(db, 0); } if( sParse.useCallback==0 ){ assert( ppVm ); diff --git a/src/parse.y b/src/parse.y index e1feb992f3..605cc6cd72 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.93 2003/03/27 12:51:25 drh Exp $ +** @(#) $Id: parse.y,v 1.94 2003/03/31 00:30:48 drh Exp $ */ %token_prefix TK_ %token_type {Token} @@ -841,14 +841,18 @@ expr(A) ::= RAISE(X) LP FAIL COMMA nm(Z) RP(Y). { //////////////////////// DROP TRIGGER statement ////////////////////////////// cmd ::= DROP TRIGGER nm(X) dbnm(D). { - sqliteDropTrigger(pParse,sqliteSrcListAppend(0,&X,&D),0); + sqliteDropTrigger(pParse,sqliteSrcListAppend(0,&X,&D),0); } //////////////////////// ATTACH DATABASE file AS name ///////////////////////// -cmd ::= ATTACH database_kw_opt ids AS nm. +cmd ::= ATTACH database_kw_opt ids(F) AS nm(D). { + sqliteAttach(pParse, &F, &D); +} database_kw_opt ::= DATABASE. database_kw_opt ::= . //////////////////////// DETACH DATABASE name ///////////////////////////////// -cmd ::= DETACH database_kw_opt nm. +cmd ::= DETACH database_kw_opt nm(D). { + sqliteDetach(pParse, &D); +} diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 552f351b40..7b224ea526 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.42 2003/03/30 19:17:03 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.43 2003/03/31 00:30:49 drh Exp $ */ #ifndef _SQLITE_H_ #define _SQLITE_H_ @@ -165,6 +165,7 @@ int sqlite_exec( #define SQLITE_MISUSE 21 /* Library used incorrectly */ #define SQLITE_NOLFS 22 /* Uses OS features not supported on host */ #define SQLITE_AUTH 23 /* Authorization denied */ +#define SQLITE_FORMAT 24 /* Auxiliary database format error */ #define SQLITE_ROW 100 /* sqlite_step() has another row ready */ #define SQLITE_DONE 101 /* sqlite_step() has finished executing */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index dcc325f755..bffef93a49 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.166 2003/03/27 13:50:00 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.167 2003/03/31 00:30:49 drh Exp $ */ #include "config.h" #include "sqlite.h" @@ -971,9 +971,9 @@ Expr *sqliteExprFunction(ExprList*, Token*); void sqliteExprDelete(Expr*); ExprList *sqliteExprListAppend(ExprList*,Expr*,Token*); void sqliteExprListDelete(ExprList*); -void sqlitePragma(Parse*,Token*,Token*,int); -void sqliteResetInternalSchema(sqlite*); int sqliteInit(sqlite*, char**); +void sqlitePragma(Parse*,Token*,Token*,int); +void sqliteResetInternalSchema(sqlite*, int); void sqliteBeginParse(Parse*,int); void sqliteRollbackInternalChanges(sqlite*); void sqliteCommitInternalChanges(sqlite*); @@ -1082,3 +1082,5 @@ void sqliteDeferForeignKey(Parse*, int); # define sqliteAuthRead(a,b,c,d) # define sqliteAuthCheck(a,b,c,d) SQLITE_OK #endif +void sqliteAttach(Parse*, Token*, Token*); +void sqliteDetach(Parse*, Token*);