From: drh Date: Sat, 15 Sep 2001 00:57:28 +0000 (+0000) Subject: Everything is working on Linux. This is release 2.0-Alpha-1. (CVS 246) X-Git-Tag: version-3.6.10~5801 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=50e5dadf2c10afd7add44378d04c886d3598e518;p=thirdparty%2Fsqlite.git Everything is working on Linux. This is release 2.0-Alpha-1. (CVS 246) FossilOrigin-Name: 14474fa144fe7c5dc63e0990d6cc92d769e6013e --- diff --git a/manifest b/manifest index bcf51987e8..5e0c6287e8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Added\sa\sPRAGMA\sstatement.\s\sTook\sout\sthe\sspecial\scomment\sparsing.\s(CVS\s245) -D 2001-09-14T18:54:08 +C Everything\sis\sworking\son\sLinux.\s\sThis\sis\srelease\s2.0-Alpha-1.\s(CVS\s246) +D 2001-09-15T00:57:28 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4 F Makefile.in 7ecb2370b5cb34d390af1fcb3118ea6d84a253ca F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958 @@ -12,11 +12,11 @@ F notes/notes1.txt b7c0812b704a022e88c621146ae50955c923d464 F notes/notes2.txt 7e3fafd5e25906c1fe1e95f13b089aa398ca403e F notes/notes2b.txt 1c17a5b7f6b44a75cd3eb98ed2c24db1eefb06c3 F notes/notes3.txt 71e47be517e3d2578b3b9343a45b772d43b7ba16 -F src/TODO f0ea267ab55c4d15127c1ac1667edbf781147438 +F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6 F src/btree.c 3adf545b8e072000923f7e0f7f91d33072a9f869 F src/btree.h a3d9c20fa876e837680745ac60500be697026b7b -F src/build.c b5c682960b5889555cd059f3b5157668778b8834 -F src/delete.c 62500a09606c0f714b651756475cd42979ef08e8 +F src/build.c 8359e553db8138d09f44957e2d1bcc9b8720117b +F src/delete.c c84b5a26e29fda3c3de51345073a76bb161271fd F src/ex/README b745b00acce2d892f60c40111dacdfc48e0c1c7a F src/ex/db.c f1419ae6c93e40b5ac6e39fe7efd95d868e6f9d7 F src/ex/db.h 3f2933ee20c147fe494835786e4c6f3a562def4e @@ -26,10 +26,10 @@ F src/ex/pg.c 2bbf6a94f37226d06337868b6bf4d7affc60197f F src/ex/pg.h 23a4ac807b0546ec2bb6239ec8bd3e06926572cd F src/ex/sizes.tcl f54bad4a2ac567624be59131a6ee42d71b41a3d7 F src/expr.c bcd91d0487c71cfa44413a46efe5e2c2244901b6 -F src/insert.c edf098ecbbe00e3ecde6b5f22404a8230590c9fd -F src/main.c a2c142626b46e3eb3e01436626df6c2d0a8f3ae6 +F src/insert.c 750a44c0d205779b2c42b0791a163937cfb00e74 +F src/main.c 73be8d00a8a9bbec715a6260840a19020a074090 F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c -F src/pager.c bb0891d49b9068711e4b8bab14db2959f56f5be9 +F src/pager.c 048c20ac85485ca87ed33d6b7711375a3444f817 F src/pager.h bb9136e833de46bc84aafd8403713d3c46fcbfdf F src/parse.y 8b30e072208c3dfabd97c7d06f0924f194919533 F src/printf.c b1e22a47be8cdf707815647239991e08e8cb69f9 @@ -37,19 +37,19 @@ F src/random.c b626726c4f0066610739e52e7431adae7ccd9651 F src/select.c f1673b4d06c24665097faf28d76c4533bce18b84 F src/shell.c 1fcdf8c4180098bcfdee12501e01b4c8eb21d726 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e -F src/sqlite.h.in 8faa2fed0513d188ced16e5f9094e57694594e70 -F src/sqliteInt.h 11d74bfd90777afafc529434b86c413fed44f0bf +F src/sqlite.h.in 1d6a7d13284c3861e61bd0b71491fda613613c68 +F src/sqliteInt.h c7c0580ceb9b5ce92c6fc7ef9434320952b14dc0 F src/table.c adcaf074f6c1075e86359174e68701fa2acfc4d6 F src/tclsqlite.c d328970848c028e13e61e173bef79adcc379568a F src/test1.c abb3cb427e735ae87e6533f5b3b7164b7da91bc4 F src/test2.c b3177e061fabd20d48e4b1b4bca610a0d2b28670 F src/test3.c 1fc103f198cbd0447d1a12c3ce48795755ec1a53 F src/tokenize.c 2d4d1534b321422384de0f311d417ffce14fedc6 -F src/update.c ea8f2c0712cd4cd19314a26ef4766866013facda -F src/util.c c77668fef860cfd2e4e682ef4f3ed8f9e68c551b -F src/vdbe.c 244c86e406170c76b89ab07de4c7258f716c36ff -F src/vdbe.h 9f32bd12c47bd2b4bdd7e93092bb796f2a3b649f -F src/where.c fef978a9a2234b01e30e36833ab63e14bbc626d3 +F src/update.c 8a9d514c7f3bfe5d99fe3dfc1ad92ed3e9daea47 +F src/util.c f3f1550fb7a02348c3d0a0969951e489806e055b +F src/vdbe.c d5bb5d8dda994779e4d20de5e4a31edf995269ff +F src/vdbe.h b9d60d90aeb3acb5dbc1cdac6b0201991921b272 +F src/where.c b831b506e17cb592d9781ed066f3459cef768318 F test/all.test 5cefdb035b45639ddbb9f80324185b0f0a9ebda2 F test/btree.test 5e1eeb03cda22161eec827dc5224ce6c500eaaf9 F test/btree2.test 061365dfc2a6cd784e17a014b67b277a4cd374ee @@ -107,7 +107,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2 F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad -P 7da856cd94d2572070e40762e5bc477679e60042 -R 07caee6e3ed361d3efc95a7817451754 +P 5e3724603e6f52bb74deb1c62e6e8f323d7b64b7 +R f702824af0e6fb733e46f0ad203329d6 U drh -Z 4e11150d8699ec673e8745d72c606c4d +Z 7366e408d799dcad27172164daf780cc diff --git a/manifest.uuid b/manifest.uuid index af847db2fc..29687e51d5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5e3724603e6f52bb74deb1c62e6e8f323d7b64b7 \ No newline at end of file +14474fa144fe7c5dc63e0990d6cc92d769e6013e \ No newline at end of file diff --git a/src/TODO b/src/TODO index fcfd592180..bc0b6bae3e 100644 --- a/src/TODO +++ b/src/TODO @@ -3,5 +3,4 @@ * "OPTIMIZE select" statement to automatically create indices and/or invoke a CLUSTER command. * "CREATE INDEX FOR select" to automatically generate needed indices. - * Implement a PRAGMA command * Parse and use constraints. diff --git a/src/build.c b/src/build.c index 9befe97cf9..eb9ea05a0f 100644 --- a/src/build.c +++ b/src/build.c @@ -33,7 +33,7 @@ ** COPY ** VACUUM ** -** $Id: build.c,v 1.34 2001/09/14 18:54:08 drh Exp $ +** $Id: build.c,v 1.35 2001/09/15 00:57:28 drh Exp $ */ #include "sqliteInt.h" #include @@ -67,6 +67,7 @@ void sqliteExec(Parse *pParse){ pParse->pVdbe = 0; pParse->colNamesSet = 0; pParse->rc = rc; + pParse->schemaVerified = 0; } } @@ -278,6 +279,7 @@ static void sqliteUnlinkAndDeleteTable(sqlite *db, Table *pTable){ void sqliteCommitInternalChanges(sqlite *db){ int i; if( (db->flags & SQLITE_InternChanges)==0 ) return; + db->schema_cookie = db->next_cookie; for(i=0; iapTblHash[i]; pTable; pTable=pNext){ @@ -314,6 +316,7 @@ void sqliteCommitInternalChanges(sqlite *db){ void sqliteRollbackInternalChanges(sqlite *db){ int i; if( (db->flags & SQLITE_InternChanges)==0 ) return; + db->next_cookie = db->schema_cookie; for(i=0; iapTblHash[i]; pTable; pTable=pNext){ @@ -398,6 +401,7 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){ Vdbe *v = sqliteGetVdbe(pParse); if( v ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); } } } @@ -450,6 +454,30 @@ void sqliteAddDefaultValue(Parse *pParse, Token *pVal, int minusFlag){ sqliteDequote(*pz); } +/* +** Come up with a new random value for the schema cookie. Make sure +** the new value is different from the old. +** +** The schema cookie is used to determine when the schema for the +** database changes. After each schema change, the cookie value +** changes. When a process first reads the schema it records the +** cookie. Thereafter, whenever it goes to access the database, +** it checks the cookie to make sure the schema has not changed +** since it was last read. +** +** This plan is not completely bullet-proof. It is possible for +** the schema to change multiple times and for the cookie to be +** set back to prior value. But schema changes are infrequent +** and the probability of hitting the same cookie value is only +** 1 chance in 2^32. So we're safe enough. +*/ +static void changeCookie(sqlite *db){ + if( db->next_cookie==db->schema_cookie ){ + db->next_cookie = db->schema_cookie + sqliteRandomByte() + 1; + db->flags |= SQLITE_InternChanges; + } +} + /* ** This routine is called to report the final ")" that terminates ** a CREATE TABLE statement. @@ -503,6 +531,7 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){ { OP_String, 0, 0, 0}, /* 6 */ { OP_MakeRecord, 5, 0, 0}, { OP_Put, 0, 0, 0}, + { OP_SetCookie, 0, 0, 0}, /* 9 */ }; int n, base; Vdbe *v; @@ -515,6 +544,8 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){ sqliteVdbeTableRootAddr(v, &p->tnum); sqliteVdbeChangeP3(v, base+5, p->zName, 0); sqliteVdbeChangeP3(v, base+6, pParse->sFirstToken.z, n); + changeCookie(db); + sqliteVdbeChangeP1(v, base+9, db->next_cookie); sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0); if( p->pIndex ){ /* If the table has a primary key, create an index in the database @@ -586,15 +617,19 @@ void sqliteDropTable(Parse *pParse, Token *pName){ { OP_Delete, 0, 0, 0}, { OP_Goto, 0, ADDR(3), 0}, { OP_Destroy, 0, 0, 0}, /* 9 */ + { OP_SetCookie, 0, 0, 0}, /* 10 */ { OP_Close, 0, 0, 0}, }; Index *pIdx; if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); } base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable); sqliteVdbeChangeP3(v, base+2, pTable->zName, 0); sqliteVdbeChangeP1(v, base+9, pTable->tnum); + changeCookie(db); + sqliteVdbeChangeP1(v, base+10, db->next_cookie); for(pIdx=pTable->pIndex; pIdx; pIdx=pIdx->pNext){ sqliteVdbeAddOp(v, OP_Destroy, pIdx->tnum, 0, 0, 0); } @@ -772,6 +807,7 @@ void sqliteCreateIndex( { OP_String, 0, 0, 0}, /* 8 */ { OP_MakeRecord, 5, 0, 0}, { OP_Put, 2, 0, 0}, + { OP_SetCookie, 0, 0, 0}, /* 11 */ { OP_Close, 2, 0, 0}, }; int n; @@ -783,6 +819,7 @@ void sqliteCreateIndex( if( v==0 ) goto exit_create_index; if( pTable!=0 && (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); } if( pStart && pEnd ){ int base; @@ -793,6 +830,8 @@ void sqliteCreateIndex( sqliteVdbeChangeP3(v, base+6, pIndex->zName, 0); sqliteVdbeChangeP3(v, base+7, pTab->zName, 0); sqliteVdbeChangeP3(v, base+8, pStart->z, n); + changeCookie(db); + sqliteVdbeChangeP1(v, base+11, db->next_cookie); } sqliteVdbeAddOp(v, OP_Open, 0, pTab->tnum, pTab->zName, 0); lbl1 = sqliteVdbeMakeLabel(v); @@ -861,16 +900,20 @@ void sqliteDropIndex(Parse *pParse, Token *pName){ { OP_Ne, 0, ADDR(3), 0}, { OP_Delete, 0, 0, 0}, { OP_Destroy, 0, 0, 0}, /* 8 */ + { OP_SetCookie, 0, 0, 0}, /* 9 */ { OP_Close, 0, 0, 0}, }; int base; if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); } base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex); sqliteVdbeChangeP3(v, base+2, pIndex->zName, 0); sqliteVdbeChangeP1(v, base+8, pIndex->tnum); + changeCookie(db); + sqliteVdbeChangeP1(v, base+9, db->next_cookie); if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0); } @@ -1037,6 +1080,7 @@ void sqliteCopy( if( v ){ if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); } addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0, 0, 0); sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n); @@ -1109,6 +1153,7 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){ if( v==0 ) goto vacuum_cleanup; if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); } if( zName ){ sqliteVdbeAddOp(v, OP_Reorganize, 0, 0, zName, 0); @@ -1147,6 +1192,7 @@ void sqliteBeginTransaction(Parse *pParse){ v = sqliteGetVdbe(pParse); if( v ){ sqliteVdbeAddOp(v, OP_Transaction, 1, 0, 0, 0); + sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); } db->flags |= SQLITE_InTrans; } diff --git a/src/delete.c b/src/delete.c index 523bc2ec04..3d2cff99b2 100644 --- a/src/delete.c +++ b/src/delete.c @@ -24,7 +24,7 @@ ** This file contains C code routines that are called by the parser ** to handle DELETE FROM statements. ** -** $Id: delete.c,v 1.12 2001/09/13 21:53:10 drh Exp $ +** $Id: delete.c,v 1.13 2001/09/15 00:57:29 drh Exp $ */ #include "sqliteInt.h" @@ -92,6 +92,7 @@ void sqliteDeleteFrom( if( v==0 ) goto delete_from_cleanup; if( (pParse->db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0, 0, 0); } diff --git a/src/insert.c b/src/insert.c index b8f110c62b..28a13cdd01 100644 --- a/src/insert.c +++ b/src/insert.c @@ -24,7 +24,7 @@ ** This file contains C code routines that are called by the parser ** to handle INSERT statements. ** -** $Id: insert.c,v 1.16 2001/09/14 03:24:25 drh Exp $ +** $Id: insert.c,v 1.17 2001/09/15 00:57:29 drh Exp $ */ #include "sqliteInt.h" @@ -87,6 +87,7 @@ void sqliteInsert( if( v==0 ) goto insert_cleanup; if( (pParse->db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0, 0, 0); } /* Figure out how many columns of data are supplied. If the data diff --git a/src/main.c b/src/main.c index cbbe5dc8f7..e3863e2131 100644 --- a/src/main.c +++ b/src/main.c @@ -26,7 +26,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.35 2001/09/14 16:42:12 drh Exp $ +** $Id: main.c,v 1.36 2001/09/15 00:57:29 drh Exp $ */ #include "sqliteInt.h" #if defined(HAVE_USLEEP) && HAVE_USLEEP @@ -51,7 +51,12 @@ static int sqliteOpenCb(void *pDb, int argc, char **argv, char **azColName){ assert( argc==4 ); switch( argv[0][0] ){ case 'm': { /* Meta information */ - sscanf(argv[1],"file format %d",&db->file_format); + if( strcmp(argv[1],"file-format")==0 ){ + db->file_format = atoi(argv[3]); + }else if( strcmp(argv[1],"schema-cookie")==0 ){ + db->schema_cookie = atoi(argv[3]); + db->next_cookie = db->schema_cookie; + } break; } case 'i': @@ -170,7 +175,12 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){ { OP_Column, 0, 4, 0}, { OP_Callback, 4, 0, 0}, { OP_Goto, 0, 24, 0}, - { OP_Close, 0, 0, 0}, /* 34 */ + { OP_String, 0, 0, "meta"}, /* 34 */ + { OP_String, 0, 0, "schema-cookie"}, + { OP_String, 0, 0, ""}, + { OP_ReadCookie,0,0, 0}, + { OP_Callback, 4, 0, 0}, + { OP_Close, 0, 0, 0}, { OP_Halt, 0, 0, 0}, }; @@ -281,11 +291,16 @@ no_mem_on_open: } /* -** Close an existing SQLite database +** Erase all schema information from the schema hash table. +** +** The database schema is normally read in once when the database +** is first opened and stored in a hash table in the sqlite structure. +** This routine erases the stored schema. This erasure occurs because +** either the database is being closed or because some other process +** changed the schema and this process needs to reread it. */ -void sqlite_close(sqlite *db){ +static void clearHashTable(sqlite *db){ int i; - sqliteBtreeClose(db->pBe); for(i=0; iapTblHash[i]; db->apTblHash[i] = 0; @@ -296,6 +311,15 @@ void sqlite_close(sqlite *db){ pList = pNext; } } + db->flags &= ~SQLITE_Initialized; +} + +/* +** Close an existing SQLite database +*/ +void sqlite_close(sqlite *db){ + sqliteBtreeClose(db->pBe); + clearHashTable(db); sqliteFree(db); } @@ -387,6 +411,9 @@ int sqlite_exec( sParse.rc = SQLITE_NOMEM; } sqliteStrRealloc(pzErrMsg); + if( sParse.rc==SQLITE_SCHEMA ){ + clearHashTable(db); + } return sParse.rc; } diff --git a/src/pager.c b/src/pager.c index 8d4f9696d9..4ca2b20a17 100644 --- a/src/pager.c +++ b/src/pager.c @@ -27,7 +27,7 @@ ** all writes in order to support rollback. Locking is used to limit ** access to one or more reader or to one writer. ** -** @(#) $Id: pager.c,v 1.18 2001/09/14 18:54:09 drh Exp $ +** @(#) $Id: pager.c,v 1.19 2001/09/15 00:57:29 drh Exp $ */ #include "sqliteInt.h" #include "pager.h" @@ -683,6 +683,28 @@ int sqlitepager_ref(void *pData){ return SQLITE_OK; } +/* +** Sync the journal and write all free dirty pages to the database file. +*/ +static int syncAllPages(Pager *pPager){ + PgHdr *pPg; + int rc = SQLITE_OK; + if( pPager->needSync ){ + rc = fsync(pPager->jfd); + if( rc!=0 ) return rc; + pPager->needSync = 0; + } + for(pPg=pPager->pFirst; pPg; pPg=pPg->pNextFree){ + if( pPg->dirty ){ + pager_seek(pPager->fd, (pPg->pgno-1)*SQLITE_PAGE_SIZE); + rc = pager_write(pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE); + if( rc!=SQLITE_OK ) break; + pPg->dirty = 0; + } + } + return SQLITE_OK; +} + /* ** Acquire a page. ** @@ -791,15 +813,28 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){ /* Recycle an older page. First locate the page to be recycled. ** Try to find one that is not dirty and is near the head of ** of the free list */ - /* int cnt = pPager->mxPage/2; */ - int cnt = 10; + int cnt = pPager->mxPage/2; pPg = pPager->pFirst; while( pPg->dirty && 0pNextFree ){ pPg = pPg->pNextFree; } - if( pPg==0 || pPg->dirty ) pPg = pPager->pFirst; + if( pPg==0 || pPg->dirty ){ + int rc = syncAllPages(pPager); + if( rc!=0 ){ + sqlitepager_rollback(pPager); + *ppPage = 0; + return SQLITE_IOERR; + } + pPg = pPager->pFirst; + } assert( pPg->nRef==0 ); + +#if 0 + /**** Since putting in the call to syncAllPages() above, this code + ** is no longer used. I've kept it here for historical reference + ** only. + */ /* If the page to be recycled is dirty, sync the journal and write ** the old page into the database. */ if( pPg->dirty ){ @@ -825,6 +860,8 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){ return rc; } } +#endif + assert( pPg->dirty==0 ); /* Unlink the old page from the free list and the hash table */ diff --git a/src/sqlite.h.in b/src/sqlite.h.in index d8baf6beae..60aa491bf2 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -24,7 +24,7 @@ ** This header file defines the interface that the sqlite library ** presents to client programs. ** -** @(#) $Id: sqlite.h.in,v 1.14 2001/09/13 13:46:57 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.15 2001/09/15 00:57:29 drh Exp $ */ #ifndef _SQLITE_H_ #define _SQLITE_H_ @@ -160,6 +160,7 @@ int sqlite_exec( #define SQLITE_CANTOPEN 13 /* Unable to open the database file */ #define SQLITE_PROTOCOL 14 /* Database lock protocol error */ #define SQLITE_EMPTY 15 /* Database table is empty */ +#define SQLITE_SCHEMA 16 /* The database schema changed */ /* This function causes any pending database operation to abort and ** return at its earliest opportunity. This routine is typically diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 7a69341358..134653bc7f 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -23,7 +23,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.47 2001/09/14 18:54:09 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.48 2001/09/15 00:57:29 drh Exp $ */ #include "sqlite.h" #include "vdbe.h" @@ -143,6 +143,8 @@ struct sqlite { Btree *pBe; /* The B*Tree backend */ int flags; /* Miscellanous flags. See below */ int file_format; /* What file format version is this database? */ + int schema_cookie; /* Magic number that changes with the schema */ + int next_cookie; /* Value of schema_cookie after commit */ int nTable; /* Number of tables in the database */ void *pBusyArg; /* 1st Argument to the busy callback */ int (*xBusyCallback)(void *,const char*,int); /* The busy callback */ @@ -376,6 +378,8 @@ struct Parse { int iAggCount; /* Index of the count(*) aggregate in aAgg[] */ int useAgg; /* If true, extract field values from the aggregator ** while generating expressions. Normally false */ + int schemaVerified; /* True if an OP_VerifySchema has been coded someplace + ** other than after an OP_Transaction */ }; /* diff --git a/src/update.c b/src/update.c index ccdecc9c07..d84aaa5b9b 100644 --- a/src/update.c +++ b/src/update.c @@ -24,7 +24,7 @@ ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** -** $Id: update.c,v 1.12 2001/09/13 13:46:57 drh Exp $ +** $Id: update.c,v 1.13 2001/09/15 00:57:29 drh Exp $ */ #include "sqliteInt.h" @@ -146,6 +146,7 @@ void sqliteUpdate( if( v==0 ) goto update_cleanup; if( (pParse->db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0, 0, 0); } /* Begin the database scan diff --git a/src/util.c b/src/util.c index 77e854fad9..b894f2300a 100644 --- a/src/util.c +++ b/src/util.c @@ -26,7 +26,7 @@ ** This file contains functions for allocating memory, comparing ** strings, and stuff like that. ** -** $Id: util.c,v 1.22 2001/09/13 13:46:57 drh Exp $ +** $Id: util.c,v 1.23 2001/09/15 00:57:29 drh Exp $ */ #include "sqliteInt.h" #include @@ -978,25 +978,26 @@ sqliteLikeCompare(const unsigned char *zPattern, const unsigned char *zString){ ** argument. */ const char *sqliteErrStr(int rc){ - char *z = 0; + const char *z; switch( rc ){ - case SQLITE_OK: z = "not an error"; break; - case SQLITE_ERROR: z = "SQL logic error or missing database"; break; - case SQLITE_INTERNAL: z = "internal SQLite implementation flaw"; break; - case SQLITE_PERM: z = "access permission denied"; break; - case SQLITE_ABORT: z = "callback requested query abort"; break; - case SQLITE_BUSY: z = "database in use by another process"; break; - case SQLITE_NOMEM: z = "out of memory"; break; - case SQLITE_READONLY: z = "attempt to write a readonly database"; break; - case SQLITE_INTERRUPT: z = "interrupted"; break; - case SQLITE_IOERR: z = "disk I/O error"; break; - case SQLITE_CORRUPT: z = "database disk image is malformed"; break; - case SQLITE_NOTFOUND: z = "table or record not found"; break; - case SQLITE_FULL: z = "database is full"; break; - case SQLITE_CANTOPEN: z = "unable to open database file"; break; - case SQLITE_PROTOCOL: z = "database locking protocol failure"; break; - case SQLITE_EMPTY: z = "table contains no data"; - default: + case SQLITE_OK: z = "not an error"; break; + case SQLITE_ERROR: z = "SQL logic error or missing database"; break; + case SQLITE_INTERNAL: z = "internal SQLite implementation flaw"; break; + case SQLITE_PERM: z = "access permission denied"; break; + case SQLITE_ABORT: z = "callback requested query abort"; break; + case SQLITE_BUSY: z = "database in use by another process"; break; + case SQLITE_NOMEM: z = "out of memory"; break; + case SQLITE_READONLY: z = "attempt to write a readonly database"; break; + case SQLITE_INTERRUPT: z = "interrupted"; break; + case SQLITE_IOERR: z = "disk I/O error"; break; + case SQLITE_CORRUPT: z = "database disk image is malformed"; break; + case SQLITE_NOTFOUND: z = "table or record not found"; break; + case SQLITE_FULL: z = "database is full"; break; + case SQLITE_CANTOPEN: z = "unable to open database file"; break; + case SQLITE_PROTOCOL: z = "database locking protocol failure"; break; + case SQLITE_EMPTY: z = "table contains no data"; break; + case SQLITE_SCHEMA: z = "database schema has changed"; break; + default: z = "unknown error"; break; } return z; } diff --git a/src/vdbe.c b/src/vdbe.c index 604fa93605..e25dca4646 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -41,7 +41,7 @@ ** But other routines are also provided to help in building up ** a program instruction by instruction. ** -** $Id: vdbe.c,v 1.66 2001/09/14 16:42:12 drh Exp $ +** $Id: vdbe.c,v 1.67 2001/09/15 00:57:29 drh Exp $ */ #include "sqliteInt.h" #include @@ -759,6 +759,23 @@ static void KeylistFree(Keylist *p){ } } +/* +** Close a cursor and release all the resources that cursor happens +** to hold. +*/ +static void cleanupCursor(Cursor *pCx){ + if( pCx->pCursor ){ + sqliteBtreeCloseCursor(pCx->pCursor); + } + if( pCx->zKey ){ + sqliteFree(pCx->zKey); + } + if( pCx->pBt ){ + sqliteBtreeClose(pCx->pBt); + } + memset(pCx, 0, sizeof(Cursor)); +} + /* ** Clean up the VM after execution. ** @@ -771,19 +788,7 @@ static void Cleanup(Vdbe *p){ sqliteFree(p->azColName); p->azColName = 0; for(i=0; inCursor; i++){ - Cursor *pCx = &p->aCsr[i]; - if( pCx->pCursor ){ - sqliteBtreeCloseCursor(pCx->pCursor); - pCx->pCursor = 0; - } - if( pCx->zKey ){ - sqliteFree(pCx->zKey); - pCx->zKey = 0; - } - if( pCx->pBt ){ - sqliteBtreeClose(pCx->pBt); - pCx->pBt = 0; - } + cleanupCursor(&p->aCsr[i]); } sqliteFree(p->aCsr); p->aCsr = 0; @@ -871,31 +876,32 @@ void sqliteVdbeDelete(Vdbe *p){ ** this array, then copy and paste it into this file, if you want. */ static char *zOpName[] = { 0, - "Transaction", "Commit", "Rollback", "Open", - "OpenTemp", "Close", "MoveTo", "Fcnt", - "NewRecno", "Put", "Distinct", "Found", - "NotFound", "Delete", "Column", "KeyAsData", - "Recno", "FullKey", "Rewind", "Next", - "Destroy", "Clear", "CreateIndex", "CreateTable", - "Reorganize", "BeginIdx", "NextIdx", "PutIdx", - "DeleteIdx", "MemLoad", "MemStore", "ListOpen", - "ListWrite", "ListRewind", "ListRead", "ListClose", - "SortOpen", "SortPut", "SortMakeRec", "SortMakeKey", - "Sort", "SortNext", "SortKey", "SortCallback", - "SortClose", "FileOpen", "FileRead", "FileColumn", - "FileClose", "AggReset", "AggFocus", "AggIncr", - "AggNext", "AggSet", "AggGet", "SetInsert", - "SetFound", "SetNotFound", "SetClear", "MakeRecord", - "MakeKey", "MakeIdxKey", "Goto", "If", - "Halt", "ColumnCount", "ColumnName", "Callback", - "Integer", "String", "Null", "Pop", - "Dup", "Pull", "Add", "AddImm", - "Subtract", "Multiply", "Divide", "Min", - "Max", "Like", "Glob", "Eq", - "Ne", "Lt", "Le", "Gt", - "Ge", "IsNull", "NotNull", "Negative", - "And", "Or", "Not", "Concat", - "Noop", "Strlen", "Substr", + "Transaction", "Commit", "Rollback", "ReadCookie", + "SetCookie", "VerifyCookie", "Open", "OpenTemp", + "Close", "MoveTo", "Fcnt", "NewRecno", + "Put", "Distinct", "Found", "NotFound", + "Delete", "Column", "KeyAsData", "Recno", + "FullKey", "Rewind", "Next", "Destroy", + "Clear", "CreateIndex", "CreateTable", "Reorganize", + "BeginIdx", "NextIdx", "PutIdx", "DeleteIdx", + "MemLoad", "MemStore", "ListOpen", "ListWrite", + "ListRewind", "ListRead", "ListClose", "SortOpen", + "SortPut", "SortMakeRec", "SortMakeKey", "Sort", + "SortNext", "SortKey", "SortCallback", "SortClose", + "FileOpen", "FileRead", "FileColumn", "FileClose", + "AggReset", "AggFocus", "AggIncr", "AggNext", + "AggSet", "AggGet", "SetInsert", "SetFound", + "SetNotFound", "SetClear", "MakeRecord", "MakeKey", + "MakeIdxKey", "Goto", "If", "Halt", + "ColumnCount", "ColumnName", "Callback", "Integer", + "String", "Null", "Pop", "Dup", + "Pull", "Add", "AddImm", "Subtract", + "Multiply", "Divide", "Min", "Max", + "Like", "Glob", "Eq", "Ne", + "Lt", "Le", "Gt", "Ge", + "IsNull", "NotNull", "Negative", "And", + "Or", "Not", "Concat", "Noop", + "Strlen", "Substr", }; /* @@ -1974,6 +1980,73 @@ case OP_Rollback: { break; } +/* Opcode: ReadCookie * * * +** +** Read the magic cookie from the database file and push it onto the +** stack. The magic cookie is an integer that is used like a version +** number for the database schema. Everytime the schema changes, the +** cookie changes to a new random value. This opcode is used during +** initialization to read the initial cookie value so that subsequent +** database accesses can verify that the cookie has not changed. +** +** There must be a read-lock on the database (either a transaction +** must be started or there must be a prior OP_Open opcode) before +** executing this instruction. +*/ +case OP_ReadCookie: { + int i = ++p->tos; + int aMeta[SQLITE_N_BTREE_META]; + VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) + rc = sqliteBtreeGetMeta(pBt, aMeta); + aStack[i].i = aMeta[1]; + aStack[i].flags = STK_Int; + break; +} + +/* Opcode: SetCookie P1 * * +** +** This operation changes the value of the cookie on the database. +** The new value is P1. +** +** The cookie changes its value whenever the database schema changes. +** That way, other processes can recognize when the schema has changed +** and reread it. +** +** A transaction must be started before executing this opcode. +*/ +case OP_SetCookie: { + int aMeta[SQLITE_N_BTREE_META]; + rc = sqliteBtreeGetMeta(pBt, aMeta); + if( rc==SQLITE_OK ){ + aMeta[1] = pOp->p1; + rc = sqliteBtreeUpdateMeta(pBt, aMeta); + } + break; +} + +/* Opcode: VerifyCookie P1 * * +** +** Check the current value of the database cookie and make sure it is +** equal to P1. If it is not, abort with an SQLITE_SCHEMA error. +** +** The cookie changes its value whenever the database schema changes. +** This operation is used to detech when that the cookie has changed +** and that the current process needs to reread the schema. +** +** Either a transaction needs to have been started or an OP_Open needs +** to be executed (to establish a read lock) before this opcode is +** invoked. +*/ +case OP_VerifyCookie: { + int aMeta[SQLITE_N_BTREE_META]; + rc = sqliteBtreeGetMeta(pBt, aMeta); + if( rc==SQLITE_OK && aMeta[1]!=pOp->p1 ){ + sqliteSetString(pzErrMsg, "database schema has changed", 0); + rc = SQLITE_SCHEMA; + } + break; +} + /* Opcode: Open P1 P2 P3 ** ** Open a new cursor for the database table whose root page is @@ -2013,9 +2086,8 @@ case OP_Open: { memset(&p->aCsr[j], 0, sizeof(Cursor)); } p->nCursor = i+1; - }else if( p->aCsr[i].pCursor ){ - sqliteBtreeCloseCursor(p->aCsr[i].pCursor); } + cleanupCursor(&p->aCsr[i]); memset(&p->aCsr[i], 0, sizeof(Cursor)); do{ rc = sqliteBtreeCursor(pBt, p2, &p->aCsr[i].pCursor); @@ -2058,10 +2130,9 @@ case OP_OpenTemp: { memset(&p->aCsr[j], 0, sizeof(Cursor)); } p->nCursor = i+1; - }else if( p->aCsr[i].pCursor ){ - sqliteBtreeCloseCursor(p->aCsr[i].pCursor); } pCx = &p->aCsr[i]; + cleanupCursor(pCx); memset(pCx, 0, sizeof(*pCx)); rc = sqliteBtreeOpen(0, 0, TEMP_PAGES, &pCx->pBt); if( rc==SQLITE_OK ){ @@ -2081,17 +2152,7 @@ case OP_OpenTemp: { case OP_Close: { int i = pOp->p1; if( i>=0 && inCursor && p->aCsr[i].pCursor ){ - Cursor *pCx = &p->aCsr[i]; - sqliteBtreeCloseCursor(pCx->pCursor); - pCx->pCursor = 0; - if( pCx->zKey ){ - sqliteFree(pCx->zKey); - pCx->zKey = 0; - } - if( pCx->pBt ){ - sqliteBtreeClose(pCx->pBt); - pCx->pBt = 0; - } + cleanupCursor(&p->aCsr[i]); } break; } @@ -2523,6 +2584,7 @@ case OP_BeginIdx: { VERIFY( if( tos<0 ) goto not_enough_stack; ) if( i>=0 && inCursor && (pCrsr = &p->aCsr[i])->pCursor!=0 ){ if( Stringify(p, tos) ) goto no_mem; + if( pCrsr->zKey ) sqliteFree(pCrsr->zKey); pCrsr->nKey = aStack[tos].n; pCrsr->zKey = sqliteMalloc( 2*(pCrsr->nKey + 1) ); if( pCrsr->zKey==0 ) goto no_mem; diff --git a/src/vdbe.h b/src/vdbe.h index cbf7e08c97..46ebf99555 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -27,7 +27,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.21 2001/09/13 21:53:10 drh Exp $ +** $Id: vdbe.h,v 1.22 2001/09/15 00:57:29 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -75,118 +75,122 @@ typedef struct VdbeOp VdbeOp; #define OP_Commit 2 #define OP_Rollback 3 -#define OP_Open 4 -#define OP_OpenTemp 5 -#define OP_Close 6 -#define OP_MoveTo 7 -#define OP_Fcnt 8 -#define OP_NewRecno 9 -#define OP_Put 10 -#define OP_Distinct 11 -#define OP_Found 12 -#define OP_NotFound 13 -#define OP_Delete 14 -#define OP_Column 15 -#define OP_KeyAsData 16 -#define OP_Recno 17 -#define OP_FullKey 18 -#define OP_Rewind 19 -#define OP_Next 20 - -#define OP_Destroy 21 -#define OP_Clear 22 -#define OP_CreateIndex 23 -#define OP_CreateTable 24 -#define OP_Reorganize 25 - -#define OP_BeginIdx 26 -#define OP_NextIdx 27 -#define OP_PutIdx 28 -#define OP_DeleteIdx 29 - -#define OP_MemLoad 30 -#define OP_MemStore 31 - -#define OP_ListOpen 32 -#define OP_ListWrite 33 -#define OP_ListRewind 34 -#define OP_ListRead 35 -#define OP_ListClose 36 - -#define OP_SortOpen 37 -#define OP_SortPut 38 -#define OP_SortMakeRec 39 -#define OP_SortMakeKey 40 -#define OP_Sort 41 -#define OP_SortNext 42 -#define OP_SortKey 43 -#define OP_SortCallback 44 -#define OP_SortClose 45 - -#define OP_FileOpen 46 -#define OP_FileRead 47 -#define OP_FileColumn 48 -#define OP_FileClose 49 - -#define OP_AggReset 50 -#define OP_AggFocus 51 -#define OP_AggIncr 52 -#define OP_AggNext 53 -#define OP_AggSet 54 -#define OP_AggGet 55 - -#define OP_SetInsert 56 -#define OP_SetFound 57 -#define OP_SetNotFound 58 -#define OP_SetClear 59 - -#define OP_MakeRecord 60 -#define OP_MakeKey 61 -#define OP_MakeIdxKey 62 - -#define OP_Goto 63 -#define OP_If 64 -#define OP_Halt 65 - -#define OP_ColumnCount 66 -#define OP_ColumnName 67 -#define OP_Callback 68 - -#define OP_Integer 69 -#define OP_String 70 -#define OP_Null 71 -#define OP_Pop 72 -#define OP_Dup 73 -#define OP_Pull 74 - -#define OP_Add 75 -#define OP_AddImm 76 -#define OP_Subtract 77 -#define OP_Multiply 78 -#define OP_Divide 79 -#define OP_Min 80 -#define OP_Max 81 -#define OP_Like 82 -#define OP_Glob 83 -#define OP_Eq 84 -#define OP_Ne 85 -#define OP_Lt 86 -#define OP_Le 87 -#define OP_Gt 88 -#define OP_Ge 89 -#define OP_IsNull 90 -#define OP_NotNull 91 -#define OP_Negative 92 -#define OP_And 93 -#define OP_Or 94 -#define OP_Not 95 -#define OP_Concat 96 -#define OP_Noop 97 - -#define OP_Strlen 98 -#define OP_Substr 99 - -#define OP_MAX 99 +#define OP_ReadCookie 4 +#define OP_SetCookie 5 +#define OP_VerifyCookie 6 + +#define OP_Open 7 +#define OP_OpenTemp 8 +#define OP_Close 9 +#define OP_MoveTo 10 +#define OP_Fcnt 11 +#define OP_NewRecno 12 +#define OP_Put 13 +#define OP_Distinct 14 +#define OP_Found 15 +#define OP_NotFound 16 +#define OP_Delete 17 +#define OP_Column 18 +#define OP_KeyAsData 19 +#define OP_Recno 20 +#define OP_FullKey 21 +#define OP_Rewind 22 +#define OP_Next 23 + +#define OP_Destroy 24 +#define OP_Clear 25 +#define OP_CreateIndex 26 +#define OP_CreateTable 27 +#define OP_Reorganize 28 + +#define OP_BeginIdx 29 +#define OP_NextIdx 30 +#define OP_PutIdx 31 +#define OP_DeleteIdx 32 + +#define OP_MemLoad 33 +#define OP_MemStore 34 + +#define OP_ListOpen 35 +#define OP_ListWrite 36 +#define OP_ListRewind 37 +#define OP_ListRead 38 +#define OP_ListClose 39 + +#define OP_SortOpen 40 +#define OP_SortPut 41 +#define OP_SortMakeRec 42 +#define OP_SortMakeKey 43 +#define OP_Sort 44 +#define OP_SortNext 45 +#define OP_SortKey 46 +#define OP_SortCallback 47 +#define OP_SortClose 48 + +#define OP_FileOpen 49 +#define OP_FileRead 50 +#define OP_FileColumn 51 +#define OP_FileClose 52 + +#define OP_AggReset 53 +#define OP_AggFocus 54 +#define OP_AggIncr 55 +#define OP_AggNext 56 +#define OP_AggSet 57 +#define OP_AggGet 58 + +#define OP_SetInsert 59 +#define OP_SetFound 60 +#define OP_SetNotFound 61 +#define OP_SetClear 62 + +#define OP_MakeRecord 63 +#define OP_MakeKey 64 +#define OP_MakeIdxKey 65 + +#define OP_Goto 66 +#define OP_If 67 +#define OP_Halt 68 + +#define OP_ColumnCount 69 +#define OP_ColumnName 70 +#define OP_Callback 71 + +#define OP_Integer 72 +#define OP_String 73 +#define OP_Null 74 +#define OP_Pop 75 +#define OP_Dup 76 +#define OP_Pull 77 + +#define OP_Add 78 +#define OP_AddImm 79 +#define OP_Subtract 80 +#define OP_Multiply 81 +#define OP_Divide 82 +#define OP_Min 83 +#define OP_Max 84 +#define OP_Like 85 +#define OP_Glob 86 +#define OP_Eq 87 +#define OP_Ne 88 +#define OP_Lt 89 +#define OP_Le 90 +#define OP_Gt 91 +#define OP_Ge 92 +#define OP_IsNull 93 +#define OP_NotNull 94 +#define OP_Negative 95 +#define OP_And 96 +#define OP_Or 97 +#define OP_Not 98 +#define OP_Concat 99 +#define OP_Noop 100 + +#define OP_Strlen 101 +#define OP_Substr 102 + +#define OP_MAX 102 /* ** Prototypes for the VDBE interface. See comments on the implementation diff --git a/src/where.c b/src/where.c index 91ffa143ee..bc99942d65 100644 --- a/src/where.c +++ b/src/where.c @@ -25,7 +25,7 @@ ** the WHERE clause of SQL statements. Also found here are subroutines ** to generate VDBE code to evaluate expressions. ** -** $Id: where.c,v 1.18 2001/09/13 16:18:55 drh Exp $ +** $Id: where.c,v 1.19 2001/09/15 00:57:29 drh Exp $ */ #include "sqliteInt.h" @@ -299,6 +299,11 @@ WhereInfo *sqliteWhereBegin( for(i=0; inId; i++){ sqliteVdbeAddOp(v, OP_Open, base+i, pTabList->a[i].pTab->tnum, pTabList->a[i].pTab->zName, 0); + if( i==0 && !pParse->schemaVerified && + (pParse->db->flags & SQLITE_InTrans)==0 ){ + sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0, 0, 0); + pParse->schemaVerified = 1; + } if( inId+i, aIdx[i]->tnum, aIdx[i]->zName, 0);