diff --git a/manifest b/manifest index f9f331bf8b..50f453860c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Restore\sbtree\sto\sthe\smain\sline.\s(CVS\s237) -D 2001-08-20T00:33:58 +C The\scode\sis\sin\splace\sto\sreplace\sGDBM\swith\sBTree.\s\sBut\sI\shave\snot\syet\nattempted\sto\scompile\sit.\s\sI\sam\ssure\sthe\scode\scontains\sbugs.\s(CVS\s238) +D 2001-09-13T13:46:56 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4 F Makefile.in 9eea999e1d95531de4dd0a96a6ecf6ba0027b05b F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958 @@ -13,14 +13,15 @@ F notes/notes2.txt 7e3fafd5e25906c1fe1e95f13b089aa398ca403e F notes/notes2b.txt 1c17a5b7f6b44a75cd3eb98ed2c24db1eefb06c3 F notes/notes3.txt 71e47be517e3d2578b3b9343a45b772d43b7ba16 F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4 -F src/btree.c 049c6a8d6c308b1945bd2f32746f3df187c7f18b -F src/btree.h 6617284287be90afe41bcd5047e44f109ecd1b48 -F src/build.c 4f6a2d551c56342cd4a0420654835be3ad179651 +F src/btree.c af587cc36f36ea9e7544accfcedf4ea55460f61a +F src/btree.h 1fe9710a1d2ed79bda8efbbb324cfb80ce6f53e7 +F src/build.c 5a990a295413887bd873258f1ca7b78cb086d04d F src/dbbe.c b18259f99d87240cbe751021cf14dd3aa83a48af F src/dbbe.h bbb53eafcd1e3186597f6ee4a17ef2501f1b0628 +F src/dbbebtree.c 7a0292e1f1578973646f4f51cd9066ed5b4ee282 F src/dbbegdbm.c cbb6ebc79a7100324f07b67d4e867faca9f9efa9 F src/dbbemem.c 910ad3bb82fc065a95a762b34593b3386b4833d5 -F src/delete.c 40ddb169ee98013d976b2dadd140d98f7876f54f +F src/delete.c bee9e20720436b74d7116735389ac81f7aa1d684 F src/ex/README b745b00acce2d892f60c40111dacdfc48e0c1c7a F src/ex/db.c f1419ae6c93e40b5ac6e39fe7efd95d868e6f9d7 F src/ex/db.h 3f2933ee20c147fe494835786e4c6f3a562def4e @@ -30,30 +31,30 @@ F src/ex/pg.c 2bbf6a94f37226d06337868b6bf4d7affc60197f F src/ex/pg.h 23a4ac807b0546ec2bb6239ec8bd3e06926572cd F src/ex/sizes.tcl f54bad4a2ac567624be59131a6ee42d71b41a3d7 F src/expr.c f64760004afc10c1c1232ae7ece2947452aa70dd -F src/insert.c aa528e20a787af85432a61daaea6df394bd251d7 -F src/main.c 0a13c7a2beb8ce36aee43daf8c95989b200727a7 +F src/insert.c 51d21e65eba5b4bef017fec6af79266bc0065824 +F src/main.c afdb7ecb5de4e0eb0d69d1e0d5db7ad070c8e0d6 F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c -F src/pager.c fbb1f1d8d2fd71333dfb9014852fd60194320732 -F src/pager.h ee84c00ca56ff6f0c53bbf216ede342cc99c701a +F src/pager.c 1928e68b5c00c24749b71f41eeabacd7217337a5 +F src/pager.h 238aa88bafe33911bf9b0b365f35afd0a261cd46 F src/parse.y 8fc096948994a7ffbf61ba13129cc589f794a9cb F src/printf.c b1e22a47be8cdf707815647239991e08e8cb69f9 F src/random.c b36c3f57dc80c8f354e6bfbf39cf1e1de021d54a -F src/select.c 52bb7d081ac00dfad3687d52c917d2d90165331d -F src/shell.c d9c64418765d90909e9e200b207ff9e355afb5c4 +F src/select.c e5977916f59a79d67c40fae11e2c5736cddd1fa8 +F src/shell.c 1fcdf8c4180098bcfdee12501e01b4c8eb21d726 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e -F src/sqlite.h.in 3e5906f72608f0fd4394dfbb1d7e8d35b8353677 -F src/sqliteInt.h 47845c60e2e196b5409d774936a56700b1611f00 +F src/sqlite.h.in 8faa2fed0513d188ced16e5f9094e57694594e70 +F src/sqliteInt.h 1a3a7ac6db97c15ec6e80ee8df8a8f8eadf70316 F src/table.c adcaf074f6c1075e86359174e68701fa2acfc4d6 F src/tclsqlite.c d328970848c028e13e61e173bef79adcc379568a F src/test1.c abb3cb427e735ae87e6533f5b3b7164b7da91bc4 F src/test2.c b3177e061fabd20d48e4b1b4bca610a0d2b28670 F src/test3.c 147b42ec368a10e9f267e7466d30c46e76d7f278 F src/tokenize.c 0118b57702cb6550769316e8443b06760b067acf -F src/update.c 0cf789656a936d4356668393267692fa4b03ffc6 -F src/util.c 1b396ac34e30dd6222d82e996c17b161bbc906bc -F src/vdbe.c b019394ebe0de12917a93ec06d787d8d909726cc -F src/vdbe.h 5331b9a3963d13af01a9cf749f57ac46727bdbe6 -F src/where.c a49083e59358bac83c80cf0d19626d09bab629bd +F src/update.c ea8f2c0712cd4cd19314a26ef4766866013facda +F src/util.c c77668fef860cfd2e4e682ef4f3ed8f9e68c551b +F src/vdbe.c 16dce6e16b63840d8e74d7ffed0fb7bf9e43b999 +F src/vdbe.h 533068ed67e3d8519be49b6ed50f6229c73b6b36 +F src/where.c ddf119d879fbfa2abb0b0f5963be0560dfa30247 F test/all.test 21d55a97e39e7ec5776751dc9dd8b1b51ef4a048 F test/btree.test 5e1eeb03cda22161eec827dc5224ce6c500eaaf9 F test/btree2.test a66add2093843d0e5617fed6924002667832f279 @@ -112,7 +113,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2 F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad -P c15f6ffc4d41f30a06d750c8015226713ae0126b -R 7b718c617d1cbd654e712509bb5deb19 +P 2e6aff980287825b59d2ebb7005bb08dd601ff1c +R 2654e388e3b472efb037b78486fd2a60 U drh -Z a087795b1966c95abd2653a136093358 +Z bd85e26e61c9c38cc5377a9e930cda9b diff --git a/manifest.uuid b/manifest.uuid index 0981288553..a3d6fe45fc 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2e6aff980287825b59d2ebb7005bb08dd601ff1c \ No newline at end of file +6ecc8b20d4f402f45f03d46d8d4fa40dea666e97 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 98410be63b..b34f59ccd5 100644 --- a/src/btree.c +++ b/src/btree.c @@ -21,7 +21,7 @@ ** http://www.hwaci.com/drh/ ** ************************************************************************* -** $Id: btree.c,v 1.21 2001/08/20 00:33:58 drh Exp $ +** $Id: btree.c,v 1.22 2001/09/13 13:46:56 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -43,8 +43,8 @@ ** on Ptr(N+1) and its subpages have values greater than Key(N). And ** so forth. ** -** Finding a particular key requires reading O(log(M)) pages from the file -** where M is the number of entries in the tree. +** Finding a particular key requires reading O(log(M)) pages from the +** disk where M is the number of entries in the tree. ** ** In this implementation, a single file can hold one or more separate ** BTrees. Each BTree is identified by the index of its root page. The @@ -112,7 +112,7 @@ static const char zMagicHeader[] = #define MAGIC_SIZE (sizeof(zMagicHeader)) /* -** This is a magic integer also used to the integrety of the database +** This is a magic integer also used to test the integrity of the database ** file. This integer is used in addition to the string above so that ** if the file is written on a little-endian architecture and read ** on a big-endian architectures (or vice versa) we can detect the @@ -726,20 +726,28 @@ int sqliteBtreeBeginTrans(Btree *pBt){ return rc; } } - rc = sqlitepager_write(pBt->page1); - if( rc!=SQLITE_OK ){ - return rc; + if( !sqlitepager_isreadonly(pBt) ){ + rc = sqlitepager_write(pBt->page1); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = newDatabase(pBt); } pBt->inTrans = 1; - rc = newDatabase(pBt); return rc; } /* -** Remove the last reference to the database file. This will -** remove the read lock. +** If there are no outstanding cursors and we are not in the middle +** of a transaction but there is a read lock on the database, then +** this routine unrefs the first page of the database file which +** has the effect of releasing the read lock. +** +** If there are any outstanding cursors, this routine is a no-op. +** +** If there is a transaction in progress, this routine is a no-op. */ -static void unlockBtree(Btree *pBt){ +static void unlockBtreeIfUnused(Btree *pBt){ if( pBt->inTrans==0 && pBt->pCursor==0 && pBt->page1!=0 ){ sqlitepager_unref(pBt->page1); pBt->page1 = 0; @@ -749,19 +757,25 @@ static void unlockBtree(Btree *pBt){ /* ** Commit the transaction currently in progress. +** +** This will release the write lock on the database file. If there +** are no active cursors, it also releases the read lock. */ int sqliteBtreeCommit(Btree *pBt){ int rc; if( pBt->inTrans==0 ) return SQLITE_ERROR; rc = sqlitepager_commit(pBt->pPager); pBt->inTrans = 0; - unlockBtree(pBt); + unlockBtreeIfUnused(pBt); return rc; } /* ** Rollback the transaction in progress. All cursors must be ** closed before this routine is called. +** +** This will release the write lock on the database file. If there +** are no active cursors, it also releases the read lock. */ int sqliteBtreeRollback(Btree *pBt){ int rc; @@ -769,7 +783,7 @@ int sqliteBtreeRollback(Btree *pBt){ if( pBt->inTrans==0 ) return SQLITE_OK; pBt->inTrans = 0; rc = sqlitepager_rollback(pBt->pPager); - unlockBtree(pBt); + unlockBtreeIfUnused(pBt); return rc; } @@ -819,12 +833,12 @@ create_cursor_exception: if( pCur->pPage ) sqlitepager_unref(pCur->pPage); sqliteFree(pCur); } - unlockBtree(pBt); + unlockBtreeIfUnused(pBt); return rc; } /* -** Close a cursor. The lock on the database file is released +** Close a cursor. The read lock on the database file is released ** when the last cursor is closed. */ int sqliteBtreeCloseCursor(BtCursor *pCur){ @@ -838,7 +852,7 @@ int sqliteBtreeCloseCursor(BtCursor *pCur){ pCur->pNext->pPrev = pCur->pPrev; } sqlitepager_unref(pCur->pPage); - unlockBtree(pBt); + unlockBtreeIfUnused(pBt); sqliteFree(pCur); return SQLITE_OK; } @@ -942,29 +956,34 @@ static int getPayload(BtCursor *pCur, int offset, int amt, char *zBuf){ } /* -** Read part of the key associated with cursor pCur. A total +** Read part of the key associated with cursor pCur. A maximum ** of "amt" bytes will be transfered into zBuf[]. The transfer -** begins at "offset". If the key does not contain enough data -** to satisfy the request, no data is fetched and this routine -** returns SQLITE_ERROR. +** begins at "offset". The number of bytes actually read is +** returned. The amount returned will be smaller than the +** amount requested if there are not enough bytes in the key +** to satisfy the request. */ int sqliteBtreeKey(BtCursor *pCur, int offset, int amt, char *zBuf){ Cell *pCell; MemPage *pPage; - if( amt<0 ) return SQLITE_ERROR; - if( offset<0 ) return SQLITE_ERROR; - if( amt==0 ) return SQLITE_OK; + if( amt<0 ) return 0; + if( offset<0 ) return 0; + if( amt==0 ) return 0; pPage = pCur->pPage; assert( pPage!=0 ); if( pCur->idx >= pPage->nCell ){ - return SQLITE_ERROR; + return 0; } pCell = pPage->apCell[pCur->idx]; if( amt+offset > pCell->h.nKey ){ - return SQLITE_ERROR; + amt = pCell->h.nKey - offset; + if( amt<=0 ){ + return 0; + } } - return getPayload(pCur, offset, amt, zBuf); + getPayload(pCur, offset, amt, zBuf); + return amt; } /* @@ -990,29 +1009,34 @@ int sqliteBtreeDataSize(BtCursor *pCur, int *pSize){ } /* -** Read part of the data associated with cursor pCur. A total +** Read part of the data associated with cursor pCur. A maximum ** of "amt" bytes will be transfered into zBuf[]. The transfer -** begins at "offset". If the size of the data in the record -** is insufficent to satisfy this request then no data is read -** and this routine returns SQLITE_ERROR. +** begins at "offset". The number of bytes actually read is +** returned. The amount returned will be smaller than the +** amount requested if there are not enough bytes in the data +** to satisfy the request. */ int sqliteBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf){ Cell *pCell; MemPage *pPage; - if( amt<0 ) return SQLITE_ERROR; - if( offset<0 ) return SQLITE_ERROR; - if( amt==0 ) return SQLITE_OK; + if( amt<0 ) return 0; + if( offset<0 ) return 0; + if( amt==0 ) return 0; pPage = pCur->pPage; assert( pPage!=0 ); if( pCur->idx >= pPage->nCell ){ - return SQLITE_ERROR; + return 0; } pCell = pPage->apCell[pCur->idx]; if( amt+offset > pCell->h.nData ){ - return SQLITE_ERROR; + amt = pCell->h.nData - offset; + if( amt<=0 ){ + return 0; + } } - return getPayload(pCur, offset + pCell->h.nKey, amt, zBuf); + getPayload(pCur, offset + pCell->h.nKey, amt, zBuf); + return amt; } /* @@ -1160,6 +1184,22 @@ static int moveToLeftmost(BtCursor *pCur){ return SQLITE_OK; } +/* Move the cursor to the first entry in the table. Return SQLITE_OK +** on success. Set *pRes to 0 if the cursor actually points to something +** or set *pRes to 1 if the table is empty and there is no first element. +*/ +int sqliteBtreeFirst(BtCursor *pCur, int *pRes){ + int rc; + rc = moveToRoot(pCur); + if( rc ) return rc; + if( pCur->pPage->nCell==0 ){ + *pRes = 1; + return SQLITE_OK; + } + *pRes = 0; + rc = moveToLeftmost(pCur); + return rc; +} /* Move the cursor so that it points to an entry near pKey. ** Return a success code. @@ -1471,7 +1511,7 @@ static void reparentPage(Pager *pPager, Pgno pgno, MemPage *pNewParent){ /* ** Reparent all children of the given page to be the given page. ** In other words, for every child of pPage, invoke reparentPage() -** to make sure that child knows that pPage is its parent. +** to make sure that each child knows that pPage is its parent. ** ** This routine gets called after you memcpy() one page into ** another. @@ -1563,7 +1603,7 @@ static void relinkCellList(MemPage *pPage){ /* ** Make a copy of the contents of pFrom into pTo. The pFrom->apCell[] -** pointers that point intto pFrom->u.aDisk[] must be adjusted to point +** pointers that point into pFrom->u.aDisk[] must be adjusted to point ** into pTo->u.aDisk[] instead. But some pFrom->apCell[] entries might ** not point to pFrom->u.aDisk[]. Those are unchanged. */ @@ -1624,8 +1664,9 @@ static void copyPage(MemPage *pTo, MemPage *pFrom){ ** might become overfull or underfull. If that happens, then this routine ** is called recursively on the parent. ** -** If this routine fails for any reason, it means the database may have -** been left in a corrupted state and should be rolled back. +** If this routine fails for any reason, it might leave the database +** in a corrupted state. So if this routine fails, the database should +** be rolled back. */ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){ MemPage *pParent; /* The parent of pPage */ @@ -2037,7 +2078,7 @@ int sqliteBtreeDelete(BtCursor *pCur){ clearCell(pCur->pBt, pCell); if( pgnoChild ){ /* - ** If the entry we are about to delete is not a leaf so if we do not + ** The entry we are about to delete is not a leaf so if we do not ** do something we will leave a hole on an internal page. ** We have to fill the hole by moving in a cell from a leaf. The ** next Cell after the one to be deleted is guaranteed to exist and @@ -2603,7 +2644,7 @@ char *sqliteBtreeSanityCheck(Btree *pBt, int *aRoot, int nRoot){ /* Make sure this analysis did not leave any unref() pages */ - unlockBtree(pBt); + unlockBtreeIfUnused(pBt); if( nRef != *sqlitepager_stats(pBt->pPager) ){ char zBuf[100]; sprintf(zBuf, diff --git a/src/btree.h b/src/btree.h index 2e98f4213f..6d9230d5cd 100644 --- a/src/btree.h +++ b/src/btree.h @@ -24,7 +24,7 @@ ** This header file defines the interface that the sqlite B-Tree file ** subsystem. ** -** @(#) $Id: btree.h,v 1.10 2001/08/20 00:33:58 drh Exp $ +** @(#) $Id: btree.h,v 1.11 2001/09/13 13:46:56 drh Exp $ */ typedef struct Btree Btree; @@ -46,6 +46,7 @@ int sqliteBtreeMoveto(BtCursor*, const void *pKey, int nKey, int *pRes); int sqliteBtreeDelete(BtCursor*); int sqliteBtreeInsert(BtCursor*, const void *pKey, int nKey, const void *pData, int nData); +int sqliteBtreeFirst(BtCursor*, int *pRes); int sqliteBtreeNext(BtCursor*, int *pRes); int sqliteBtreeKeySize(BtCursor*, int *pSize); int sqliteBtreeKey(BtCursor*, int offset, int amt, char *zBuf); diff --git a/src/build.c b/src/build.c index b3a5eff57f..a11c7a243a 100644 --- a/src/build.c +++ b/src/build.c @@ -33,7 +33,7 @@ ** COPY ** VACUUM ** -** $Id: build.c,v 1.28 2001/04/15 00:37:09 drh Exp $ +** $Id: build.c,v 1.29 2001/09/13 13:46:56 drh Exp $ */ #include "sqliteInt.h" @@ -191,6 +191,24 @@ static void sqliteDeleteIndex(sqlite *db, Index *pIndex){ sqliteFree(pIndex); } +/* +** Unlink the given index from its table, then remove +** the index from the index hash table, and free its memory +** structures. +*/ +static void sqliteUnlinkAndDeleteIndex(sqlite *db, Index *pIndex){ + if( pIndex->pTable->pIndex==pIndex ){ + pIndex->pTable->pIndex = pIndex->pNext; + }else{ + Index *p; + for(p=pIndex->pTable->pIndex; p && p->pNext!=pIndex; p=p->pNext){} + if( p && p->pNext==pIndex ){ + p->pNext = pIndex->pNext; + } + } + sqliteDeleteIndex(db, pIndex); +} + /* ** Remove the memory data structures associated with the given ** Table. No changes are made to disk by this routine. @@ -221,6 +239,84 @@ void sqliteDeleteTable(sqlite *db, Table *pTable){ sqliteFree(pTable); } +/* +** Check all Tables and Indexes in the internal hash table and commit +** any additions or deletions to those hash tables. +** +** When executing CREATE TABLE and CREATE INDEX statements, the Table +** and Index structures are created and added to the hash tables, but +** the "isCommit" field is not set. This routine sets those fields. +** When executing DROP TABLE and DROP INDEX, the "isDelete" fields of +** Table and Index structures is set but the structures are not unlinked +** from the hash tables nor deallocated. This routine handles that +** deallocation. +** +** See also: sqliteRollbackInternalChanges() +*/ +void sqliteCommitInternalChanges(sqlite *db){ + int i; + if( (db->flags & SQLITE_InternChanges)==0 ) return; + for(i=0; ipHash; + if( pTable->isDelete ){ + sqliteDeleteTable(db, pTable); + }else if( pTable->isCommit==0 ){ + pTable->isCommit = 1; + } + } + } + for(i=0; ipHash; + if( pIndex->isDelete ){ + sqliteUnlinkAndDeleteIndex(db, pIndex); + }else if( pIndex->isCommit==0 ){ + pIndex->isCommit = 1; + } + } + } + db->flags &= ~SQLITE_InternChanges; +} + +/* +** This routine runs when one or more CREATE TABLE, CREATE INDEX, +** DROP TABLE, or DROP INDEX statements get rolled back. The +** additions or deletions of Table and Index structures in the +** internal hash tables are undone. +** +** See also: sqliteCommitInternalChanges() +*/ +void sqliteRollbackInternalChanges(sqlite *db){ + int i; + if( (db->flags & SQLITE_InternChanges)==0 ) return; + for(i=0; ipHash; + if( !pTable->isCommit ){ + sqliteDeleteTable(db, pTable); + }else if( pTable->isDelete ){ + pTable->isDelete = 0; + } + } + } + for(i=0; ipHash; + if( !pIndex->isCommit ){ + sqliteUnlinkAndDeleteIndex(db, pIndex); + }else if( pIndex->isDelete ){ + pIndex->isDelete = 0; + } + } + } + db->flags &= ~SQLITE_InternChanges; +} + /* ** Construct the name of a user table or index from a token. ** @@ -275,6 +371,12 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){ pTable->pIndex = 0; if( pParse->pNewTable ) sqliteDeleteTable(pParse->db, pParse->pNewTable); pParse->pNewTable = pTable; + if( !pParse->initFlag && (pParse->db->flags & SQLITE_InTrans)==0 ){ + Vdbe *v = sqliteGetVdbe(pParse); + if( v ){ + sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); + } + } } /* @@ -340,12 +442,10 @@ void sqliteAddDefaultValue(Parse *pParse, Token *pVal, int minusFlag){ void sqliteEndTable(Parse *pParse, Token *pEnd){ Table *p; int h; - int addMeta; /* True to insert a meta records into the file */ if( pEnd==0 || pParse->nErr || sqlite_malloc_failed ) return; p = pParse->pNewTable; if( p==0 ) return; - addMeta = pParse->db->nTable==1; /* Add the table to the in-memory representation of the database */ @@ -355,27 +455,20 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){ pParse->db->apTblHash[h] = p; pParse->pNewTable = 0; pParse->db->nTable++; + db->flags |= SQLITE_InternChanges; } /* If not initializing, then create the table on disk. */ if( !pParse->initFlag ){ static VdbeOp addTable[] = { - { OP_OpenTbl, 0, 1, MASTER_NAME }, - { OP_New, 0, 0, 0}, + { OP_Open, 0, 2, 0}, + { OP_NewRecno, 0, 0, 0}, { OP_String, 0, 0, "table" }, { OP_String, 0, 0, 0}, /* 3 */ - { OP_String, 0, 0, 0}, /* 4 */ + { OP_CreateTable, 0, 0, 0}, { OP_String, 0, 0, 0}, /* 5 */ - { OP_MakeRecord, 4, 0, 0}, - { OP_Put, 0, 0, 0}, - }; - static VdbeOp addVersion[] = { - { OP_New, 0, 0, 0}, - { OP_String, 0, 0, "meta" }, - { OP_String, 0, 0, "" }, - { OP_String, 0, 0, "" }, - { OP_String, 0, 0, "file format 2" }, + { OP_String, 0, 0, 0}, /* 6 */ { OP_MakeRecord, 4, 0, 0}, { OP_Put, 0, 0, 0}, }; @@ -387,12 +480,13 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){ n = (int)pEnd->z - (int)pParse->sFirstToken.z + 1; base = sqliteVdbeAddOpList(v, ArraySize(addTable), addTable); sqliteVdbeChangeP3(v, base+3, p->zName, 0); - sqliteVdbeChangeP3(v, base+4, p->zName, 0); - sqliteVdbeChangeP3(v, base+5, pParse->sFirstToken.z, n); - if( addMeta ){ - sqliteVdbeAddOpList(v, ArraySize(addVersion), addVersion); - } + sqliteVdbeTableRootAddr(v, &p->tnum); + sqliteVdbeChangeP3(v, base+5, p->zName, 0); + sqliteVdbeChangeP3(v, base+6, pParse->sFirstToken.z, n); sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0); + if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0); + } } } @@ -441,50 +535,41 @@ void sqliteDropTable(Parse *pParse, Token *pName){ v = sqliteGetVdbe(pParse); if( v ){ static VdbeOp dropTable[] = { - { OP_OpenTbl, 0, 1, MASTER_NAME }, - { OP_ListOpen, 0, 0, 0}, - { OP_String, 0, 0, 0}, /* 2 */ - { OP_Next, 0, ADDR(10), 0}, /* 3 */ + { OP_Open, 0, 2, 0}, + { OP_String, 0, 0, 0}, /* 1 */ + { OP_Next, 0, ADDR(9), 0}, /* 2 */ { OP_Dup, 0, 0, 0}, - { OP_Field, 0, 2, 0}, - { OP_Ne, 0, ADDR(3), 0}, - { OP_Key, 0, 0, 0}, - { OP_ListWrite, 0, 0, 0}, - { OP_Goto, 0, ADDR(3), 0}, - { OP_ListRewind, 0, 0, 0}, /* 10 */ - { OP_ListRead, 0, ADDR(14), 0}, /* 11 */ + { OP_Column, 0, 3, 0}, + { OP_Ne, 0, ADDR(2), 0}, + { OP_Recno, 0, 0, 0}, { OP_Delete, 0, 0, 0}, - { OP_Goto, 0, ADDR(11), 0}, - { OP_Destroy, 0, 0, 0}, /* 14 */ + { OP_Goto, 0, ADDR(2), 0}, + { OP_Destroy, 0, 0, 0}, /* 9 */ { OP_Close, 0, 0, 0}, }; Index *pIdx; + if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); + } base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable); - sqliteVdbeChangeP3(v, base+2, pTable->zName, 0); - sqliteVdbeChangeP3(v, base+14, pTable->zName, 0); + sqliteVdbeChangeP1(v, base+9, pTable->tnum); for(pIdx=pTable->pIndex; pIdx; pIdx=pIdx->pNext){ - sqliteVdbeAddOp(v, OP_Destroy, 0, 0, pIdx->zName, 0); + sqliteVdbeAddOp(v, OP_Destroy, pIdx->tnum, 0, 0, 0); + } + if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0); } } - /* Remove the in-memory table structure and free its memory. + /* Mark the in-memory Table structure as being deleted. The actually + ** deletion occurs inside of sqliteCommitInternalChanges(). ** ** Exception: if the SQL statement began with the EXPLAIN keyword, - ** then no changes are made. + ** then no changes should be made. */ if( !pParse->explain ){ - h = sqliteHashNoCase(pTable->zName, 0) % N_HASH; - if( pParse->db->apTblHash[h]==pTable ){ - pParse->db->apTblHash[h] = pTable->pHash; - }else{ - Table *p; - for(p=pParse->db->apTblHash[h]; p && p->pHash!=pTable; p=p->pHash){} - if( p && p->pHash==pTable ){ - p->pHash = pTable->pHash; - } - } - pParse->db->nTable--; - sqliteDeleteTable(pParse->db, pTable); + pTable->isDelete = 1; + db->flags |= SQLITE_InternChanges; } } @@ -603,6 +688,7 @@ void sqliteCreateIndex( pParse->db->apIdxHash[h] = pIndex; pIndex->pNext = pTab->pIndex; pTab->pIndex = pIndex; + db->flags |= SQLITE_InternChanges; } /* If the initFlag is 0 then create the index on disk. This @@ -617,13 +703,14 @@ void sqliteCreateIndex( */ if( pParse->initFlag==0 ){ static VdbeOp addTable[] = { - { OP_OpenTbl, 2, 1, MASTER_NAME}, - { OP_New, 2, 0, 0}, + { OP_Open, 2, 2, 0}, + { OP_NewRecno, 2, 0, 0}, { OP_String, 0, 0, "index"}, { OP_String, 0, 0, 0}, /* 3 */ - { OP_String, 0, 0, 0}, /* 4 */ + { OP_CreateIndex, 0, 0, 0}, { OP_String, 0, 0, 0}, /* 5 */ - { OP_MakeRecord, 4, 0, 0}, + { OP_String, 0, 0, 0}, /* 6 */ + { OP_MakeRecord, 5, 0, 0}, { OP_Put, 2, 0, 0}, { OP_Close, 2, 0, 0}, }; @@ -634,29 +721,36 @@ void sqliteCreateIndex( v = sqliteGetVdbe(pParse); if( v==0 ) goto exit_create_index; - sqliteVdbeAddOp(v, OP_OpenTbl, 0, 0, pTab->zName, 0); - sqliteVdbeAddOp(v, OP_OpenIdx, 1, 1, pIndex->zName, 0); + if( pTable!=0 && (pParse->db->flags & SQLITE_InTrans)==0 ){ + sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); + } + sqliteVdbeAddOp(v, OP_Open, 0, pTab->tnum, pTab->zName, 0); + sqliteVdbeAddOp(v, OP_Open, 1, pIndex->tnum, pIndex->zName, 0); if( pStart && pEnd ){ int base; n = (int)pEnd->z - (int)pStart->z + 1; base = sqliteVdbeAddOpList(v, ArraySize(addTable), addTable); sqliteVdbeChangeP3(v, base+3, pIndex->zName, 0); - sqliteVdbeChangeP3(v, base+4, pTab->zName, 0); - sqliteVdbeChangeP3(v, base+5, pStart->z, n); + sqliteVdbeIndexRootAddr(v, &pIndex->tnum); + sqliteVdbeChangeP3(v, base+5, pTab->zName, 0); + sqliteVdbeChangeP3(v, base+6, pStart->z, n); } lbl1 = sqliteVdbeMakeLabel(v); lbl2 = sqliteVdbeMakeLabel(v); sqliteVdbeAddOp(v, OP_Next, 0, lbl2, 0, lbl1); - sqliteVdbeAddOp(v, OP_Key, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_GetRecno, 0, 0, 0, 0); for(i=0; inColumn; i++){ - sqliteVdbeAddOp(v, OP_Field, 0, pIndex->aiColumn[i], 0, 0); + sqliteVdbeAddOp(v, OP_Column, 0, pIndex->aiColumn[i], 0, 0); } - sqliteVdbeAddOp(v, OP_MakeKey, pIndex->nColumn, 0, 0, 0); + sqliteVdbeAddOp(v, OP_MakeIdxKey, pIndex->nColumn, 0, 0, 0); sqliteVdbeAddOp(v, OP_PutIdx, 1, 0, 0, 0); sqliteVdbeAddOp(v, OP_Goto, 0, lbl1, 0, 0); sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, lbl2); sqliteVdbeAddOp(v, OP_Close, 1, 0, 0, 0); sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0); + if( pTable!=0 && (pParse->db->flags & SQLITE_InTrans)==0 ){ + sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0); + } } /* Reclaim memory on an EXPLAIN call. @@ -696,39 +790,35 @@ void sqliteDropIndex(Parse *pParse, Token *pName){ v = sqliteGetVdbe(pParse); if( v ){ static VdbeOp dropIndex[] = { - { OP_OpenTbl, 0, 1, MASTER_NAME}, - { OP_ListOpen, 0, 0, 0}, - { OP_String, 0, 0, 0}, /* 2 */ - { OP_Next, 0, ADDR(9), 0}, /* 3 */ + { OP_Open, 0, 2, 0}, + { OP_String, 0, 0, 0}, /* 1 */ + { OP_Next, 0, ADDR(8), 0}, /* 2 */ { OP_Dup, 0, 0, 0}, - { OP_Field, 0, 1, 0}, - { OP_Ne, 0, ADDR(3), 0}, + { OP_Column, 0, 1, 0}, + { OP_Ne, 0, ADDR(2), 0}, { OP_Key, 0, 0, 0}, { OP_Delete, 0, 0, 0}, - { OP_Destroy, 0, 0, 0}, /* 9 */ + { OP_Destroy, 0, 0, 0}, /* 8 */ { OP_Close, 0, 0, 0}, }; int base; + if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); + } base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex); - sqliteVdbeChangeP3(v, base+2, pIndex->zName, 0); - sqliteVdbeChangeP3(v, base+9, pIndex->zName, 0); + sqliteVdbeChangeP1(v, base+8, pIndex->tnum); + if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0); + } } - /* Remove the index structure and free its memory. Except if the - ** EXPLAIN keyword is present, no changes are made. + /* Mark the internal Index structure for deletion by the + ** sqliteCommitInternalChanges routine. */ if( !pParse->explain ){ - if( pIndex->pTable->pIndex==pIndex ){ - pIndex->pTable->pIndex = pIndex->pNext; - }else{ - Index *p; - for(p=pIndex->pTable->pIndex; p && p->pNext!=pIndex; p=p->pNext){} - if( p && p->pNext==pIndex ){ - p->pNext = pIndex->pNext; - } - } - sqliteDeleteIndex(pParse->db, pIndex); + pIndex->isDelete = 1; + db->flags |= SQLITE_InternChanges; } } @@ -881,12 +971,15 @@ void sqliteCopy( } v = sqliteGetVdbe(pParse); if( v ){ + if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); + } addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0, 0, 0); sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n); sqliteVdbeDequoteP3(v, addr); - sqliteVdbeAddOp(v, OP_OpenTbl, 0, 1, pTab->zName, 0); + sqliteVdbeAddOp(v, OP_Open, 0, pTab->tnum, pTab->zName, 0); for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - sqliteVdbeAddOp(v, OP_OpenIdx, i, 1, pIdx->zName, 0); + sqliteVdbeAddOp(v, OP_Open, i, pIdx->tnum, pIdx->zName, 0); } end = sqliteVdbeMakeLabel(v); addr = sqliteVdbeAddOp(v, OP_FileRead, pTab->nCol, end, 0, 0); @@ -896,12 +989,12 @@ void sqliteCopy( }else{ sqliteVdbeChangeP3(v, addr, "\t", 1); } - sqliteVdbeAddOp(v, OP_New, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_NewRecno, 0, 0, 0, 0); if( pTab->pIndex ){ sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); } for(i=0; inCol; i++){ - sqliteVdbeAddOp(v, OP_FileField, i, 0, 0, 0); + sqliteVdbeAddOp(v, OP_FileColumn, i, 0, 0, 0); } sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0, 0, 0); sqliteVdbeAddOp(v, OP_Put, 0, 0, 0, 0); @@ -910,13 +1003,16 @@ void sqliteCopy( sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); } for(j=0; jnColumn; j++){ - sqliteVdbeAddOp(v, OP_FileField, pIdx->aiColumn[j], 0, 0, 0); + sqliteVdbeAddOp(v, OP_FileColumn, pIdx->aiColumn[j], 0, 0, 0); } - sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nColumn, 0, 0, 0); + sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0, 0, 0); sqliteVdbeAddOp(v, OP_PutIdx, i, 0, 0, 0); } sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0); sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, end); + if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0); + } } copy_cleanup: @@ -946,6 +1042,9 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){ } v = sqliteGetVdbe(pParse); if( v==0 ) goto vacuum_cleanup; + if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); + } if( zName ){ sqliteVdbeAddOp(v, OP_Reorganize, 0, 0, zName, 0); }else{ @@ -961,6 +1060,9 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){ } } } + if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0); + } vacuum_cleanup: sqliteFree(zName); @@ -972,20 +1074,17 @@ vacuum_cleanup: */ void sqliteBeginTransaction(Parse *pParse){ int rc; - DbbeMethods *pM; sqlite *db; + Vdbe *v; + if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return; if( pParse->nErr || sqlite_malloc_failed ) return; if( db->flags & SQLITE_InTrans ) return; - pM = pParse->db->pBe->x; - if( pM && pM->BeginTransaction ){ - rc = (*pM->BeginTransaction)(pParse->db->pBe); - }else{ - rc = SQLITE_OK; - } - if( rc==SQLITE_OK ){ - db->flags |= SQLITE_InTrans; + v = sqliteGetVdbe(pParse); + if( v ){ + sqliteVdbeAddOp(v, OP_Transaction, 1, 0, 0, 0); } + db->flags |= SQLITE_InTrans; } /* @@ -993,20 +1092,17 @@ void sqliteBeginTransaction(Parse *pParse){ */ void sqliteCommitTransaction(Parse *pParse){ int rc; - DbbeMethods *pM; sqlite *db; + Vdbe *v; + if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return; if( pParse->nErr || sqlite_malloc_failed ) return; if( (db->flags & SQLITE_InTrans)==0 ) return; - pM = pParse->db->pBe->x; - if( pM && pM->Commit ){ - rc = (*pM->Commit)(pParse->db->pBe); - }else{ - rc = SQLITE_OK; - } - if( rc==SQLITE_OK ){ - db->flags &= ~SQLITE_InTrans; + v = sqliteGetVdbe(pParse); + if( v ){ + sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0); } + db->flags &= ~SQLITE_InTrans; } /* @@ -1014,18 +1110,15 @@ void sqliteCommitTransaction(Parse *pParse){ */ void sqliteRollbackTransaction(Parse *pParse){ int rc; - DbbeMethods *pM; sqlite *db; + Vdbe *v; + if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return; if( pParse->nErr || sqlite_malloc_failed ) return; if( (db->flags & SQLITE_InTrans)==0 ) return; - pM = pParse->db->pBe->x; - if( pM && pM->Rollback ){ - rc = (*pM->Rollback)(pParse->db->pBe); - }else{ - rc = SQLITE_OK; - } - if( rc==SQLITE_OK ){ - db->flags &= ~SQLITE_InTrans; + v = sqliteGetVdbe(pParse); + if( v ){ + sqliteVdbeAddOp(v, OP_Rollback, 0, 0, 0, 0); } + db->flags &= ~SQLITE_InTrans; } diff --git a/src/dbbebtree.c b/src/dbbebtree.c new file mode 100644 index 0000000000..123b62ee48 --- /dev/null +++ b/src/dbbebtree.c @@ -0,0 +1,676 @@ +/* +** Copyright (c) 2001 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public +** License as published by the Free Software Foundation; either +** version 2 of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public +** License along with this library; if not, write to the +** Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +************************************************************************* +** This file contains code to implement the database backend (DBBE) +** for sqlite. The database backend is the interface between +** sqlite and the code that does the actually reading and writing +** of information to the disk. +** +** This file uses a custom B-Tree implementation as the database backend. +** +** $Id: dbbebtree.c,v 1.1 2001/09/13 13:46:56 drh Exp $ +*/ +#include "sqliteInt.h" +#include "btree.h" + +/* +** The following structure contains all information used by B-Tree +** database driver. This is a subclass of the Dbbe structure. +*/ +typedef struct Dbbex Dbbex; +struct Dbbex { + Dbbe dbbe; /* The base class */ + int write; /* True for write permission */ + int inTrans; /* Currently in a transaction */ + char *zFile; /* File containing the database */ + Btree *pBt; /* Pointer to the open database */ + BtCursor *pCur; /* Cursor for the main database table */ + DbbeCursor *pDCur; /* List of all Dbbe cursors */ +}; + +/* +** An cursor into a database table is an instance of the following +** structure. +*/ +struct DbbeCursor { + DbbeCursor *pNext; /* Next on list of all cursors */ + DbbeCursor *pPrev; /* Previous on list of all cursors */ + Dbbex *pBe; /* The database of which this record is a part */ + BtCursor *pCur; /* The cursor */ + char *zTempFile; /* Name of file if referring to a temporary table */ + Btree *pTempBt; /* Database handle, if this is a temporary table */ + char *zKey; /* Most recent key. Memory obtained from sqliteMalloc() */ + int nKey; /* Size of the key */ + char *zKeyBuf; /* Space used during NextIndex() processing */ + char *zData; /* Most recent data. Memory from sqliteMalloc() */ + int needRewind; /* Next call to Next() returns first entry in table */ + int skipNext; /* Do not advance cursor for next NextIndex() call */ +}; + +/* +** Forward declaration +*/ +static void sqliteBtbeCloseCursor(DbbeCursor *pCursr); + +/* +** Completely shutdown the given database. Close all files. Free all memory. +*/ +static void sqliteBtbeClose(Dbbe *pDbbe){ + Dbbex *pBe = (Dbbex*)pDbbe; + assert( pBe->pDCur==0 ); + if( pBe->pCur ){ + sqliteBtreeCloseCursor(pBe->pCur); + } + sqliteBtreeClose(pBe->pBt); + sqliteFree(pBe->zFile); + sqliteFree(pBe); +} + +/* +** Translate a database table name into the table number for the database. +** The pBe->pCur cursor points to table number 2 of the database and that +** table maps all other database names into database number. Return the +** database number of the table, or return 0 if not found. +*/ +static int mapTableNameToNumber(Dbbex *pBe, char *zName){ + int nName = strlen(zName); + int rc; + int res; + if( pBe->pCur==0 ){ + rc = sqliteBtreeCursor(pBe, 2, &pBe->pCur); + if( rc!=SQLITE_OK ) return 0; + } + rc = sqliteBtreeMoveto(pBe->pCur, zName, nName, &res); + if( rc!=SQLITE_OK || res!=0 ) return 0; + rc = sqliteBtreeData(pBe->pCur, 0, sizeof(res), &res); + if( rc!=SQLITE_OK ) return 0; + return res; +} + +/* +** Locate a directory where we can potentially create a temporary +** file. +*/ +static const char *findTempDir(void){ + static const char *azDirs[] = { + "/var/tmp", + "/usr/tmp", + "/tmp", + "/temp", + ".", + "./temp", + }; + int i; + struct stat buf; + for(i=0; ipCur==0 ){ + rc = sqliteBtreeCursor(pBe->pBt, 2, &pBe->pCur); + if( rc!=SQLITE_OK ) return rc; + } + pCursr = sqliteMalloc( sizeof(*pCursr) ); + if( pCursr==0 ) return SQLITE_NOMEM; + if( zTable ){ + char *zTab; + int tabId, i; + + if( writeable && pBe->inTrans==0 ){ + rc = sqliteBeginTrans(pBe->pBt); + if( rc!=SQLITE_OK ){ + sqliteFree(pCursr); + return rc; + } + pBe->inTrans = 1; + } + zTab = sqliteStrDup(zTable); + for(i=0; zTab[i]; i++){ + if( isupper(zTab[i]) ) zTab[i] = tolower(zTab[i]); + } + tabId = mapTableNameToNumber(pBe, zTab); + if( tabId==0 ){ + if( writeable==0 ){ + pCursr->pCur = 0; + }else{ + rc = sqliteBtreeCreateTable(pBe->pBt, &tabId); + if( rc!=SQLITE_OK ){ + sqliteFree(pCursr); + sqliteFree(zTab); + return rc; + } + sqliteBtreeInsert(pBe->pCur, zTab, strlen(zTab), tabId, sizeof(tabId)); + } + } + sqliteFree(zTab); + rc = sqliteBtreeCursor(pBe->pBt, tabId, &pCursr->pCur); + if( rc!=SQLITE_OK ){ + sqliteFree(pCursr); + return rc; + } + pCursr->zTempFile = 0; + pCursr->pTempBt = 0; + }else{ + int nTry = 5; + char zFileName[200]; + while( nTry>0 ){ + nTry--; + sprintf(zFileName,"%s/_sqlite_temp_file_%d", + findTempDir(), sqliteRandomInteger()); + rc = sqliteBtreeOpen(zFileName, 0, 100, &pCursr->pTempBt); + if( rc!=SQLITE_OK ) continue; + rc = sqliteBtreeCursor(pCursr->pTempBt, 2, &pCursr->pCur***** + pFile = 0; + zFile = 0; + } + pCursr->pNext = pBe->pDCur; + if( pBe->pDCur ){ + pBe->pDCur->pPrev = pCursr; + } + pCursr->pPrev = 0; + pCursr->pBe = pBe; + pCursr->skipNext = 0; + pCursr->needRewind = 1; + return SQLITE_OK; +} + +/* +** Drop a table from the database. +*/ +static void sqliteBtbeDropTable(Dbbe *pDbbe, const char *zTable){ + int iTable; + Dbbex *pBe = (Dbbex*)pDbbe; + + iTable = mapTableNameToNumber(zTable); + if( iTable>0 ){ + sqliteBtreeDelete(pBe->pCur); + sqliteBtreeDropTable(pBe->pBt, iTable); + } +} + +/* +** Clear the remembered key and data from the cursor. +*/ +static void clearCursorCache(DbbeCursor *pCursr){ + if( pCursr->zKey ){ + sqliteFree(pCursr->zKey); + pCursr->zKey = 0; + pCursr->nKey = 0; + pCursr->zKeyBuf = 0; + } + if( pCursr->zData ){ + sqliteFree(pCursr->zData); + pCursr->zData = 0; + } +} + +/* +** Close a cursor previously opened by sqliteBtbeOpenCursor(). +*/ +static void sqliteBtbeCloseCursor(DbbeCursor *pCursr){ + Dbbex *pBe; + if( pCursr==0 ) return; + if( pCursr->pCur ){ + sqliteBtreeCloseCursor(pCursr->pCur); + } + if( pCursr->pTemp ){ + sqliteBtreeClose(pCursr->pTemp); + } + if( pCursr->zTempFile ){ + unlink(pCursr->zTempFile); + sqliteFree(pCursr->zTempFile); + } + clearCursorCache(pCursr); + pBe = pCursr->pBe; + if( pCursr->pPrev ){ + pCursr->pPrev->pNext = pCursr->pNext; + }else{ + pBe->pDCur = pCur->pNext; + } + if( pCursr->pNext ){ + pCursr->pNext->pPrev = pCursr->pPrev; + } + if( pBe->pDCur==0 && pBe->inTrans==0 && pBe->pCur!=0 ){ + sqliteBtreeCloseCursor(pBe->pCur); + pBe->pCur = 0; + } + memset(pCursr, 0, sizeof(*pCursr)); + sqliteFree(pCursr); +} + +/* +** Reorganize a table to reduce search times and disk usage. +*/ +static int sqliteBtbeReorganizeTable(Dbbe *pBe, const char *zTable){ + return SQLITE_OK; +} + +/* +** Move the cursor so that it points to the entry with a key that +** matches the argument. Return 1 on success and 0 if no keys match +** the argument. +*/ +static int sqliteBtbeFetch(DbbeCursor *pCursr, int nKey, char *pKey){ + int rc, res; + clearCursorCache(pCursr); + if( pCursr->pCur==0 ) return 0; + rc = sqliteBtreeMoveto(pCursr->pCur, pKey, nKey, &res); + return rc==SQLITE_OK && res==0; +} + +/* +** Copy bytes from the current key or data into a buffer supplied by +** the calling function. Return the number of bytes copied. +*/ +static +int sqliteBtbeCopyKey(DbbeCursor *pCursr, int offset, int size, char *zBuf){ + if( pCursr->pCur==0 ) return 0; + int rc = sqliteBtreeKey(pCursr->pCur, offset, amt, zBuf); + if( rc!=SQLITE_OK ) amt = 0; + return amt; +} +static +int sqliteBtbeCopyData(DbbeCursor *pCursr, int offset, int size, char *zBuf){ + if( pCursr->pCur==0 ) return 0; + int rc = sqliteBtreeData(pCursr->pCur, offset, amt, zBuf); + if( rc!=SQLITE_OK ) amt = 0; + return amt; +} + +/* +** Return a pointer to bytes from the key or data. The data returned +** is ephemeral. +*/ +static char *sqliteBtbeReadKey(DbbeCursor *pCursr, int offset){ + if( pCursr->zKey==0 && pCursr->pCur!=0 ){ + sqliteBtreeKeySize(pCursr->pCur, &pCursr->nKey); + pCursr->zKey = sqliteMalloc( pCursr->nKey + 1 ); + if( pCursr->zKey==0 ) return 0; + sqliteBtreeKey(pCursr->pCur, 0, pCursr->nKey, pCursr->zKey); + pCursr->zKey[pCursor->nKey] = 0; + } + return pCursr->zKey; +} +static char *sqliteBtbeReadData(DbbeCursor *pCursr, int offset){ + if( pCursr->zData==0 && pCursr->pCur!=0 ){ + int nData; + sqliteBtreeDataSize(pCursr->pCur, &nData); + pCursr->zData = sqliteMalloc( nData + 1 ); + if( pCursr->zData==0 ) return 0; + sqliteBtreeData(pCursr->pCur, 0, nData, pCursr->zData); + pCursr->zData[nData] = 0; + } + return pCursr->zData; +} + +/* +** Return the total number of bytes in either data or key. +*/ +static int sqliteBtbeKeyLength(DbbeCursor *pCursr){ + int n; + if( pCursr->pCur==0 ) return 0; + sqliteBtreeKeySize(pCursr->pCur, &n); + return n; +} +static int sqliteBtbeDataLength(DbbeCursor *pCursr){ + int n; + if( pCursr->pCur==0 ) return 0; + sqliteBtreeDataSize(pCursr->pCur, &n); + return n; +} + +/* +** Make is so that the next call to sqliteNextKey() finds the first +** key of the table. +*/ +static int sqliteBtbeRewind(DbbeCursor *pCursr){ + pCursr->needRewind = 1; + return SQLITE_OK; +} + +/* +** Move the cursor so that it points to the next key in the table. +** Return 1 on success. Return 0 if there are no more keys in this +** table. +** +** If the pCursr->needRewind flag is set, then move the cursor so +** that it points to the first key of the table. +*/ +static int sqliteBtbeNextKey(DbbeCursor *pCursr){ + int rc, res; + static char zNullKey[1] = { '\000' }; + assert( pCursr!=0 ); + clearCursorCache(pCursr); + if( pCursr->pCur==0 ) return 0; + if( pCursr->needRewind ){ + rc = sqliteBtreeFirst(pCursr->pCur, &res); + return rc==SQLITE_OK && res==0; + } + rc = sqliteBtreeNext(pCursr->pCur); + return rc==SQLITE_OK && res==0; +} + +/* +** Get a new integer key. +*/ +static int sqliteBtbeNew(DbbeCursor *pCursr){ + int rc; + int res = 0; + + assert( pCursr->pCur!=0 ); + while( res==0 ){ + iKey = sqliteRandomInteger() & 0x7fffffff; + if( iKey==0 ) continue; + rc = sqliteBtreeMoveto(pCursr->pCur, &iKey, sizeof(iKey), &res); + assert( rc==SQLITE_OK ); + } + clearCursorCache(pCursr); + return iKey; +} + +/* +** Write an entry into the table. Overwrite any prior entry with the +** same key. +*/ +static int sqliteBtbePut( + DbbeCursor *pCursr, /* Write to the database associated with this cursor */ + int nKey, /* Number of bytes in the key */ + char *pKey, /* The data for the key */ + int nData, /* Number of bytes of data */ + char *pData /* The data */ +){ + clearCursorCache(pCursr); + assert( pCursr->pCur!=0 ); + return sqliteBtreeInsert(pCursr->pCur, pKey, nKey, pData, nData); +} + +/* +** Remove an entry from a table, if the entry exists. +*/ +static int sqliteBtbeDelete(DbbeCursor *pCursr, int nKey, char *pKey){ + int rc; + int res; + clearCursorCache(pCursr); + assert( pCursr->pCur!=0 ); + rc = sqliteBtreeMoveto(pCursr->pCur, pKey, nKey, &res); + if( rc==SQLITE_OK && res==0 ){ + rc = sqliteBtreeDelete(pCursr->pCur); + } + return rc; +} + +/* +** Begin a transaction. +*/ +static int sqliteBtbeBeginTrans(Dbbe *pDbbe){ + Dbbex *pBe = (Dbbex*)pDbbe; + if( pBe->inTrans ) return SQLITE_OK; + sqliteBtreeBeginTrans(pBe->pBt); + pBe->inTrans = 1; + return SQLITE_OK; +} + +/* +** Commit a transaction. +*/ +static int sqliteBtbeCommit(Dbbe *pDbbe){ + Dbbex *pBe = (Dbbex*)pDbbe; + if( !pBe->inTrans ) return SQLITE_OK; + pBe->inTrans = 0; + return sqliteBtreeCommit(pBe->pBt); +} + +/* +** Rollback a transaction. +*/ +static int sqliteBtbeRollback(Dbbe *pDbbe){ + Dbbex *pBe = (Dbbex*)pDbbe; + if( !pBe->inTrans ) return SQLITE_OK; + if( pBt->pDCur!=0 ) return SQLITE_INTERNAL; + pBe->inTrans = 0; + if( pBe->pCur ){ + sqliteBtreeCloseCursor(pBe->pCur); + pBe->pCur = 0; + } + return sqliteBtreeRollback(pBe->pBt); +} + +/* +** Begin scanning an index for the given key. Return 1 on success and +** 0 on failure. (Vdbe ignores the return value.) +*/ +static int sqliteBtbeBeginIndex(DbbeCursor *pCursr, int nKey, char *pKey){ + int rc; + int res; + clearCursorCache(pCursr); + if( pCursr->pCur==0 ) return 0; + pCursr->nKey = nKey; + pCursr->zKey = sqliteMalloc( 2*(nKey + 1) ); + if( pCursr->zKey==0 ) return 0; + pCursr->zKeyBuf = &pCursr->zKey[nKey+1]; + memcpy(pCursr->zKey, zKey, nKey); + pCursr->zKey[nKey] = 0; + rc = sqliteBtreeMoveTo(pCursr->pCur, pKey, nKey, res); + pCursr->skipNext = res<0; + return rc==SQLITE_OK; +} + +/* +** Return an integer key which is the next record number in the index search +** that was started by a prior call to BeginIndex. Return 0 if all records +** have already been searched. +*/ +static int sqliteBtbeNextIndex(DbbeCursor *pCursr){ + int rc, res; + int iRecno; + BtCursor *pCur = pCursr->pCur; + if( pCur==0 ) return 0; + if( pCursr->zKey==0 || pCursr->zKeyBuf==0 ) return 0; + if( !pCursr->skipNext ){ + rc = sqliteBtreeNext(pCur, &res); + pCursr->skipNext = 0; + if( res ) return 0; + } + if( sqliteBtreeKeySize(pCur)!=pCursr->nKey+4 ){ + return 0; + } + rc = sqliteBtreeKey(pCur, 0, pCursr->nKey, pCursr->zKeyBuf); + if( rc!=SQLITE_OK || memcmp(pCursr->zKey, pCursr->zKeyBuf, pCursr->nKey)!=0 ){ + return 0; + } + sqliteBtreeKey(pCur, pCursr->nKey, 4, &iRecno); + return iRecno; +} + +/* +** Write a new record number and key into an index table. Return a status +** code. +*/ +static int sqliteBtbePutIndex(DbbeCursor *pCursr, int nKey, char *pKey, int N){ + char *zBuf; + int rc; + char zStaticSpace[200]; + + assert( pCursr->pCur!=0 ); + if( nKey+4>sizeof(zStaticSpace){ + zBuf = sqliteMalloc( nKey + 4 ); + if( zBuf==0 ) return SQLITE_NOMEM; + }else{ + zBuf = zStaticSpace; + } + memcpy(zBuf, pKey, nKey); + memcpy(&zBuf[nKey], N, 4); + rc = sqliteBtreeInsert(pCursr->pCur, zBuf, nKey+4, "", 0); + if( zBuf!=zStaticSpace ){ + sqliteFree(zBuf); + } +} + +/* +** Delete an index entry. Return a status code. +*/ +static +int sqliteBtbeDeleteIndex(DbbeCursor *pCursr, int nKey, char *pKey, int N){ + char *zBuf; + int rc; + char zStaticSpace[200]; + + assert( pCursr->pCur!=0 ); + if( nKey+4>sizeof(zStaticSpace){ + zBuf = sqliteMalloc( nKey + 4 ); + if( zBuf==0 ) return SQLITE_NOMEM; + }else{ + zBuf = zStaticSpace; + } + memcpy(zBuf, pKey, nKey); + memcpy(&zBuf[nKey], N, 4); + rc = sqliteBtreeMoveto(pCursr->pCur, zBuf, nKey+4, &res); + if( rc==SQLITE_OK && res==0 ){ + sqliteBtreeDelete(pCursr->pCur); + } + if( zBuf!=zStaticSpace ){ + sqliteFree(zBuf); + } + return SQLITE_OK; +} + +/* +** This variable contains pointers to all of the access methods +** used to implement the GDBM backend. +*/ +static struct DbbeMethods btbeMethods = { + /* Close */ sqliteBtbeClose, + /* OpenCursor */ sqliteBtbeOpenCursor, + /* DropTable */ sqliteBtbeDropTable, + /* ReorganizeTable */ sqliteBtbeReorganizeTable, + /* CloseCursor */ sqliteBtbeCloseCursor, + /* Fetch */ sqliteBtbeFetch, + /* Test */ sqliteBtbeFetch, + /* CopyKey */ sqliteBtbeCopyKey, + /* CopyData */ sqliteBtbeCopyData, + /* ReadKey */ sqliteBtbeReadKey, + /* ReadData */ sqliteBtbeReadData, + /* KeyLength */ sqliteBtbeKeyLength, + /* DataLength */ sqliteBtbeDataLength, + /* NextKey */ sqliteBtbeNextKey, + /* Rewind */ sqliteBtbeRewind, + /* New */ sqliteBtbeNew, + /* Put */ sqliteBtbePut, + /* Delete */ sqliteBtbeDelete, + /* BeginTrans */ sqliteBtbeBeginTrans, + /* Commit */ sqliteBtbeCommit, + /* Rollback */ sqliteBtbeRollback, + /* BeginIndex */ sqliteBtbeBeginIndex, + /* NextIndex */ sqliteBtbeNextIndex, + /* PutIndex */ sqliteBtbePutIndex, + /* DeleteIndex */ sqliteBtbeDeleteIndex, +}; + + +/* +** This routine opens a new database. For the BTree driver +** implemented here, the database name is the name of a single +** file that contains all tables of the database. +** +** If successful, a pointer to the Dbbe structure is returned. +** If there are errors, an appropriate error message is left +** in *pzErrMsg and NULL is returned. +*/ +Dbbe *sqliteBtbeOpen( + const char *zName, /* The name of the database */ + int writeFlag, /* True if we will be writing to the database */ + int createFlag, /* True to create database if it doesn't exist */ + char **pzErrMsg /* Write error messages (if any) here */ +){ + Dbbex *pNew; + char *zTemp; + Btree *pBt; + int rc; + + rc = sqliteBtreeOpen(zName, 0, 100, &pBt); + if( rc!=SQLITE_OK ){ + sqliteSetString(pzErrMsg, "unable to open database file \"", zName, "\"",0); + return 0; + } + pNew = sqliteMalloc(sizeof(Dbbex) + strlen(zName) + 1); + if( pNew==0 ){ + sqliteBtreeCloseCursor(pCur); + sqliteBtreeClose(pBt); + sqliteSetString(pzErrMsg, "out of memory", 0); + return 0; + } + pNew->dbbe.x = &btbeMethods; + pNew->write = writeFlag; + pNew->inTrans = 0; + pNew->zFile = (char*)&pNew[1]; + strcpy(pNew->zFile, zName); + pNew->pBt = pBt; + pNew->pCur = 0; + return &pNew->dbbe; +} +#endif /* DISABLE_GDBM */ diff --git a/src/delete.c b/src/delete.c index 75c6407471..06e7d82bd0 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.9 2001/04/11 14:28:42 drh Exp $ +** $Id: delete.c,v 1.10 2001/09/13 13:46:56 drh Exp $ */ #include "sqliteInt.h" @@ -90,14 +90,18 @@ void sqliteDeleteFrom( */ v = sqliteGetVdbe(pParse); if( v==0 ) goto delete_from_cleanup; + if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); + } + /* Special case: A DELETE without a WHERE clause deletes everything. ** It is easier just to deleted the database files directly. */ if( pWhere==0 ){ - sqliteVdbeAddOp(v, OP_Destroy, 0, 0, pTab->zName, 0); + sqliteVdbeAddOp(v, OP_Destroy, pTab->tnum, 0, 0, 0); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - sqliteVdbeAddOp(v, OP_Destroy, 0, 0, pIdx->zName, 0); + sqliteVdbeAddOp(v, OP_Destroy, pIdx->tnum, 0, 0, 0); } } @@ -125,9 +129,9 @@ void sqliteDeleteFrom( */ base = pParse->nTab; sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0); - sqliteVdbeAddOp(v, OP_OpenTbl, base, 1, pTab->zName, 0); + sqliteVdbeAddOp(v, OP_Open, base, pTab->tnum, 0, 0); for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ - sqliteVdbeAddOp(v, OP_OpenIdx, base+i, 1, pIdx->zName, 0); + sqliteVdbeAddOp(v, OP_Open, base+i, pIdx->tnum, 0, 0); } end = sqliteVdbeMakeLabel(v); addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0); @@ -140,7 +144,7 @@ void sqliteDeleteFrom( for(j=0; jnColumn; j++){ sqliteVdbeAddOp(v, OP_Field, base, pIdx->aiColumn[j], 0, 0); } - sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nColumn, 0, 0, 0); + sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0, 0, 0); sqliteVdbeAddOp(v, OP_DeleteIdx, base+i, 0, 0, 0); } } @@ -148,6 +152,10 @@ void sqliteDeleteFrom( sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0); sqliteVdbeAddOp(v, OP_ListClose, 0, 0, 0, end); } + if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0); + } + delete_from_cleanup: sqliteIdListDelete(pTabList); diff --git a/src/insert.c b/src/insert.c index 7756b10b94..97a21046fc 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.13 2001/04/11 14:28:42 drh Exp $ +** $Id: insert.c,v 1.14 2001/09/13 13:46:56 drh Exp $ */ #include "sqliteInt.h" @@ -85,6 +85,9 @@ void sqliteInsert( */ v = sqliteGetVdbe(pParse); if( v==0 ) goto insert_cleanup; + if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); + } /* Figure out how many columns of data are supplied. If the data ** is comming from a SELECT statement, then this step has to generate @@ -235,7 +238,7 @@ void sqliteInsert( sqliteExprCode(pParse, pList->a[j].pExpr); } } - sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nColumn, 0, 0, 0); + sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0, 0, 0); sqliteVdbeAddOp(v, OP_PutIdx, idx+base, 0, 0, 0); } @@ -245,6 +248,10 @@ void sqliteInsert( sqliteVdbeAddOp(v, OP_Goto, 0, iCont, 0, 0); sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, iBreak); } + if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0); + } + insert_cleanup: if( pList ) sqliteExprListDelete(pList); diff --git a/src/main.c b/src/main.c index f32850b6c0..4c48c0e7f3 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.29 2001/04/28 16:52:42 drh Exp $ +** $Id: main.c,v 1.30 2001/09/13 13:46:56 drh Exp $ */ #include "sqliteInt.h" #if defined(HAVE_USLEEP) && HAVE_USLEEP @@ -87,6 +87,7 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){ "CREATE TABLE " MASTER_NAME " (\n" " type text,\n" " name text,\n" + " tnum integer,\n" " tbl_name text,\n" " sql text\n" ")" @@ -100,6 +101,7 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){ ** CREATE TABLE sqlite_master ( ** type text, -- Either "table" or "index" or "meta" ** name text, -- Name of table or index + ** tnum integer, -- The integer page number of root page ** tbl_name text, -- Associated table ** sql text -- The CREATE statement for this object ** ); @@ -126,32 +128,33 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){ ** database scheme. */ static VdbeOp initProg[] = { - { OP_OpenTbl, 0, 0, MASTER_NAME}, + { OP_Open, 0, 2, 0}, { OP_Next, 0, 9, 0}, /* 1 */ - { OP_Field, 0, 0, 0}, + { OP_Column, 0, 0, 0}, { OP_String, 0, 0, "meta"}, { OP_Ne, 0, 1, 0}, - { OP_Field, 0, 0, 0}, - { OP_Field, 0, 3, 0}, + { OP_Column, 0, 0, 0}, + { OP_Column, 0, 4, 0}, { OP_Callback, 2, 0, 0}, { OP_Goto, 0, 1, 0}, { OP_Rewind, 0, 0, 0}, /* 9 */ { OP_Next, 0, 17, 0}, /* 10 */ - { OP_Field, 0, 0, 0}, + { OP_Column, 0, 0, 0}, { OP_String, 0, 0, "table"}, { OP_Ne, 0, 10, 0}, - { OP_Field, 0, 3, 0}, + { OP_Column, 0, 4, 0}, { OP_Callback, 1, 0, 0}, { OP_Goto, 0, 10, 0}, { OP_Rewind, 0, 0, 0}, /* 17 */ { OP_Next, 0, 25, 0}, /* 18 */ - { OP_Field, 0, 0, 0}, + { OP_Column, 0, 0, 0}, { OP_String, 0, 0, "index"}, { OP_Ne, 0, 18, 0}, - { OP_Field, 0, 3, 0}, + { OP_Column, 0, 4, 0}, { OP_Callback, 1, 0, 0}, { OP_Goto, 0, 18, 0}, - { OP_Halt, 0, 0, 0}, /* 25 */ + { OP_Close, 2, 0, 0}, /* 25 */ + { OP_Halt, 0, 0, 0}, }; /* Create a virtual machine to run the initialization program. Run @@ -181,6 +184,7 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){ pTab->readOnly = 1; } db->flags |= SQLITE_Initialized; + sqliteCommitInternalChanges(db); } return rc; } @@ -219,11 +223,17 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){ if( db==0 ) goto no_mem_on_open; /* Open the backend database driver */ - db->pBe = sqliteDbbeOpen(zFilename, (mode&0222)!=0, mode!=0, pzErrMsg); - if( db->pBe==0 ){ + rc = sqliteBtreeOpen(zFilename, mode, 100, &db->pBe); + if( rc!=SQLITE_OK ){ + switch( rc ){ + default: { + if( pzErrMsg ){ + sqliteSetString(pzErrMsg, "unable to open database: ", zFilename, 0); + } + } + } sqliteFree(db); - sqliteStrRealloc(pzErrMsg); - return 0; + return rc; } /* Assume file format 1 unless the database says otherwise */ @@ -253,7 +263,7 @@ no_mem_on_open: */ void sqlite_close(sqlite *db){ int i; - db->pBe->x->Close(db->pBe); + sqliteBtreeClose(db->pBe); for(i=0; iapTblHash[i]; db->apTblHash[i] = 0; @@ -346,6 +356,7 @@ int sqlite_exec( } memset(&sParse, 0, sizeof(sParse)); sParse.db = db; + sParse.pBe = db->pBe; sParse.xCallback = xCallback; sParse.pArg = pArg; sqliteRunParser(&sParse, zSql, pzErrMsg); diff --git a/src/pager.c b/src/pager.c index 57866918a4..71eaf9a7d5 100644 --- a/src/pager.c +++ b/src/pager.c @@ -25,9 +25,9 @@ ** ** The page cache is used to access a database file. The pager journals ** all writes in order to support rollback. Locking is used to limit -** access to one or more reader or one writer. +** access to one or more reader or to one writer. ** -** @(#) $Id: pager.c,v 1.13 2001/07/02 17:51:46 drh Exp $ +** @(#) $Id: pager.c,v 1.14 2001/09/13 13:46:57 drh Exp $ */ #include "sqliteInt.h" #include "pager.h" @@ -122,6 +122,8 @@ struct Pager { int nHit, nMiss, nOvfl; /* Cache hits, missing, and LRU overflows */ unsigned char state; /* SQLITE_UNLOCK, _READLOCK or _WRITELOCK */ unsigned char errMask; /* One of several kinds of errors */ + unsigned char tempFile; /* zFilename is a temporary file */ + unsigned char readOnly; /* True for a read-only database */ unsigned char *aInJournal; /* One bit for each page in the database file */ PgHdr *pFirst, *pLast; /* List of free pages */ PgHdr *pAll; /* List of all pages */ @@ -147,8 +149,8 @@ struct PageRecord { }; /* -** Journal files begin with the following magic string. This data -** is completely random. It is used only as a sanity check. +** Journal files begin with the following magic string. The data +** was obtained from /dev/random. It is used only as a sanity check. */ static const unsigned char aJournalMagic[] = { 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd4, @@ -163,7 +165,7 @@ static const unsigned char aJournalMagic[] = { ** Enable reference count tracking here: */ #if SQLITE_TEST -int pager_refinfo_enable = 0; + int pager_refinfo_enable = 0; static void pager_refinfo(PgHdr *p){ static int cnt = 0; if( !pager_refinfo_enable ) return; @@ -457,9 +459,33 @@ static int pager_playback(Pager *pPager){ return rc; } +/* +** Locate a directory where we can potentially create a temporary +** file. +*/ +static const char *findTempDir(void){ + static const char *azDirs[] = { + ".", + "/var/tmp", + "/usr/tmp", + "/tmp", + "/temp", + "./temp", + }; + int i; + struct stat buf; + for(i=0; i0 && fd<0 ); + zFilename = zTemp; + tempFile = 1; + } if( fd<0 ){ return SQLITE_CANTOPEN; } @@ -500,6 +547,8 @@ int sqlitepager_open( pPager->mxPage = mxPage>5 ? mxPage : 10; pPager->state = SQLITE_UNLOCK; pPager->errMask = 0; + pPager->tempFile = tempFile; + pPager->readOnly = readOnly; pPager->pFirst = 0; pPager->pLast = 0; pPager->nExtra = nExtra; @@ -510,7 +559,8 @@ int sqlitepager_open( /* ** Set the destructor for this pager. If not NULL, the destructor is called -** when the reference count on the page reaches zero. +** when the reference count on each page reaches zero. The destructor can +** be used to clean up information in the extra segment appended to each page. ** ** The destructor is not called as a result sqlitepager_close(). ** Destructors are only called by sqlitepager_unref(). @@ -520,7 +570,8 @@ void sqlitepager_set_destructor(Pager *pPager, void (*xDesc)(void*)){ } /* -** Return the total number of pages in the file opened by pPager. +** Return the total number of pages in the disk file associated with +** pPager. */ int sqlitepager_pagecount(Pager *pPager){ int n; @@ -572,12 +623,15 @@ int sqlitepager_close(Pager *pPager){ } if( pPager->fd>=0 ) close(pPager->fd); assert( pPager->jfd<0 ); + if( pPager->tempFile ){ + unlink(pPager->zFilename); + } sqliteFree(pPager); return SQLITE_OK; } /* -** Return the page number for the given page data +** Return the page number for the given page data. */ Pgno sqlitepager_pagenumber(void *pData){ PgHdr *p = DATA_TO_PGHDR(pData); @@ -621,8 +675,8 @@ int sqlitepager_ref(void *pData){ /* ** Acquire a page. ** -** A read lock is obtained for the first page acquired. The lock -** is dropped when the last page is released. +** A read lock on the disk file is obtained when the first page acquired. +** This read lock is dropped when the last page is released. ** ** A _get works for any page number greater than 0. If the database ** file is smaller than the requested page, then no actual disk @@ -635,7 +689,7 @@ int sqlitepager_ref(void *pData){ ** ** See also sqlitepager_lookup(). Both this routine and _lookup() attempt ** to find a page in the in-memory cache first. If the page is not already -** in cache, this routine goes to disk to read it in whereas _lookup() +** in memory, this routine goes to disk to read it in whereas _lookup() ** just returns 0. This routine acquires a read-lock the first time it ** has to go to disk, and could also playback an old journal if necessary. ** Since _lookup() never goes to disk, it never has to deal with locks @@ -829,8 +883,8 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){ ** See also sqlitepager_get(). The difference between this routine ** and sqlitepager_get() is that _get() will go to the disk and read ** in the page if the page is not already in cache. This routine -** returns NULL if the page is not in cache of if a disk I/O has ever -** happened. +** returns NULL if the page is not in cache or if a disk I/O error +** has ever happened. */ void *sqlitepager_lookup(Pager *pPager, Pgno pgno){ PgHdr *pPg; @@ -925,6 +979,9 @@ int sqlitepager_write(void *pData){ if( pPager->errMask ){ return pager_errcode(pPager); } + if( pPager->readOnly ){ + return SQLITE_PERM; + } pPg->dirty = 1; if( pPg->inJournal ){ return SQLITE_OK; } assert( pPager->state!=SQLITE_UNLOCK ); @@ -1076,6 +1133,14 @@ int sqlitepager_rollback(Pager *pPager){ return rc; }; +/* +** Return TRUE if the database file is opened read-only. Return FALSE +** if the database is (in theory) writable. +*/ +int sqlitepager_isreadonly(Pager *pPager){ + return pPager->readonly; +} + /* ** This routine is used for testing and analysis only. */ diff --git a/src/pager.h b/src/pager.h index 1fefbec461..1254af413a 100644 --- a/src/pager.h +++ b/src/pager.h @@ -25,7 +25,7 @@ ** subsystem. The page cache subsystem reads and writes a file a page ** at a time and provides a journal for rollback. ** -** @(#) $Id: pager.h,v 1.7 2001/07/02 17:51:46 drh Exp $ +** @(#) $Id: pager.h,v 1.8 2001/09/13 13:46:57 drh Exp $ */ /* @@ -57,6 +57,7 @@ int sqlitepager_iswriteable(void*); int sqlitepager_pagecount(Pager*); int sqlitepager_commit(Pager*); int sqlitepager_rollback(Pager*); +int sqlitepager_isreadonly(Pager*); int *sqlitepager_stats(Pager*); #ifdef SQLITE_TEST diff --git a/src/select.c b/src/select.c index 1a6eb4fefd..e2b834f339 100644 --- a/src/select.c +++ b/src/select.c @@ -24,7 +24,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements. ** -** $Id: select.c,v 1.31 2001/04/11 14:28:42 drh Exp $ +** $Id: select.c,v 1.32 2001/09/13 13:46:57 drh Exp $ */ #include "sqliteInt.h" @@ -515,10 +515,10 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ return 1; } if( p->op!=TK_ALL ){ - sqliteVdbeAddOp(v, OP_OpenIdx, unionTab, 1, 0, 0); + sqliteVdbeAddOp(v, OP_OpenTemp, unionTab, 0, 0, 0); sqliteVdbeAddOp(v, OP_KeyAsData, unionTab, 1, 0, 0); }else{ - sqliteVdbeAddOp(v, OP_OpenTbl, unionTab, 1, 0, 0); + sqliteVdbeAddOp(v, OP_OpenTemp, unionTab, 0, 0, 0); } } @@ -576,7 +576,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ if( p->pOrderBy && matchOrderbyToColumn(pParse,p,p->pOrderBy,tab1,1) ){ return 1; } - sqliteVdbeAddOp(v, OP_OpenIdx, tab1, 1, 0, 0); + sqliteVdbeAddOp(v, OP_OpenTemp, tab1, 0, 0, 0); sqliteVdbeAddOp(v, OP_KeyAsData, tab1, 1, 0, 0); /* Code the SELECTs to our left into temporary table "tab1". @@ -586,7 +586,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ /* Code the current SELECT into temporary table "tab2" */ - sqliteVdbeAddOp(v, OP_OpenIdx, tab2, 1, 0, 0); + sqliteVdbeAddOp(v, OP_OpenTemp, tab2, 0, 0, 0); sqliteVdbeAddOp(v, OP_KeyAsData, tab2, 1, 0, 0); p->pPrior = 0; rc = sqliteSelect(pParse, p, SRT_Union, tab2); @@ -877,7 +877,7 @@ int sqliteSelect( /* Begin the database scan */ if( isDistinct ){ - sqliteVdbeAddOp(v, OP_OpenIdx, distinct, 1, 0, 0); + sqliteVdbeAddOp(v, OP_OpenTemp, distinct, 0, 0, 0); } pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0); if( pWInfo==0 ) return 1; diff --git a/src/shell.c b/src/shell.c index d3f43f6718..7548e6819f 100644 --- a/src/shell.c +++ b/src/shell.c @@ -24,7 +24,7 @@ ** This file contains code to implement the "sqlite" command line ** utility for accessing SQLite databases. ** -** $Id: shell.c,v 1.31 2001/04/11 14:28:42 drh Exp $ +** $Id: shell.c,v 1.32 2001/09/13 13:46:57 drh Exp $ */ #include #include @@ -40,7 +40,7 @@ # include # include #else -# define readline getline +# define readline(p) getline(p,stdin) # define add_history(X) #endif diff --git a/src/sqlite.h.in b/src/sqlite.h.in index b606dae830..d8baf6beae 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.13 2001/04/07 15:24:33 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.14 2001/09/13 13:46:57 drh Exp $ */ #ifndef _SQLITE_H_ #define _SQLITE_H_ @@ -159,6 +159,7 @@ int sqlite_exec( #define SQLITE_FULL 12 /* Insertion failed because database is full */ #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 */ /* 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 10a077bb1c..03a5e70587 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -23,10 +23,9 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.42 2001/04/28 16:52:42 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.43 2001/09/13 13:46:57 drh Exp $ */ #include "sqlite.h" -#include "dbbe.h" #include "vdbe.h" #include "parse.h" #ifndef DISABLE_GDBM @@ -136,23 +135,24 @@ typedef struct AggExpr AggExpr; ** Each database is an instance of the following structure */ struct sqlite { - Dbbe *pBe; /* The backend driver */ - int flags; /* Miscellanous flags. See below */ - int file_format; /* What file format version is this database? */ - int nTable; /* Number of tables in the database */ - void *pBusyArg; /* 1st Argument to the busy callback */ + Btree *pBe; /* The B*Tree backend */ + int flags; /* Miscellanous flags. See below */ + int file_format; /* What file format version is this database? */ + 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 */ - Table *apTblHash[N_HASH]; /* All tables of the database */ - Index *apIdxHash[N_HASH]; /* All indices of the database */ + Table *apTblHash[N_HASH]; /* All tables of the database */ + Index *apIdxHash[N_HASH]; /* All indices of the database */ }; /* ** Possible values for the sqlite.flags. */ -#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */ -#define SQLITE_Initialized 0x00000002 /* True after initialization */ -#define SQLITE_Interrupt 0x00000004 /* Cancel current operation */ -#define SQLITE_InTrans 0x00000008 /* True if in a transaction */ +#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */ +#define SQLITE_Initialized 0x00000002 /* True after initialization */ +#define SQLITE_Interrupt 0x00000004 /* Cancel current operation */ +#define SQLITE_InTrans 0x00000008 /* True if in a transaction */ +#define SQLITE_InternChanges 0x00000010 /* Uncommitted Hash table changes */ /* ** Current file format version @@ -178,8 +178,11 @@ struct Table { Table *pHash; /* Next table with same hash on zName */ int nCol; /* Number of columns in this table */ Column *aCol; /* Information about each column */ - int readOnly; /* True if this table should not be written by the user */ Index *pIndex; /* List of SQL indexes on this table. */ + int tnum; /* Page containing root for this table */ + int readOnly; /* True if this table should not be written by the user */ + int isCommit; /* True if creation of this table has been committed */ + int isDelete; /* True if deletion of this table has not been comitted */ }; /* @@ -208,6 +211,8 @@ struct Index { int *aiColumn; /* Which columns are used by this index. 1st is 0 */ Table *pTable; /* The SQL table being indexed */ int isUnique; /* True if keys must all be unique */ + int isCommit; /* True if creation of this index has been committed */ + int isDelete; /* True if deletion of this index has not been comitted */ Index *pNext; /* The next index associated with the same table */ }; @@ -342,6 +347,7 @@ struct AggExpr { */ struct Parse { sqlite *db; /* The main database structure */ + Btree *pBe; /* The database backend */ int rc; /* Return code from execution */ sqlite_callback xCallback; /* The callback function */ void *pArg; /* First argument to the callback function */ @@ -398,6 +404,8 @@ Expr *sqliteExprFunction(ExprList*, Token*); void sqliteExprDelete(Expr*); ExprList *sqliteExprListAppend(ExprList*,Expr*,Token*); void sqliteExprListDelete(ExprList*); +void sqliteCommitInternalChanges(sqlite*); +void sqliteRollbackInternalChanges(sqlite*); void sqliteStartTable(Parse*,Token*,Token*); void sqliteAddColumn(Parse*,Token*); void sqliteAddDefaultValue(Parse*,Token*,int); @@ -442,3 +450,4 @@ void sqliteBeginTransaction(Parse*); void sqliteCommitTransaction(Parse*); void sqliteRollbackTransaction(Parse*); char *sqlite_mprintf(const char *, ...); +const char *sqliteErrStr(int); diff --git a/src/update.c b/src/update.c index dbe7af7388..ccdecc9c07 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.11 2001/04/11 14:28:43 drh Exp $ +** $Id: update.c,v 1.12 2001/09/13 13:46:57 drh Exp $ */ #include "sqliteInt.h" @@ -144,6 +144,9 @@ void sqliteUpdate( */ v = sqliteGetVdbe(pParse); if( v==0 ) goto update_cleanup; + if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); + } /* Begin the database scan */ @@ -164,9 +167,9 @@ void sqliteUpdate( */ sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0); base = pParse->nTab; - sqliteVdbeAddOp(v, OP_OpenTbl, base, 1, pTab->zName, 0); + sqliteVdbeAddOp(v, OP_Open, base, pTab->tnum, 0, 0); for(i=0; izName, 0); + sqliteVdbeAddOp(v, OP_Open, base+i+1, apIdx[i]->tnum, 0, 0); } /* Loop over every record that needs updating. We have to load @@ -177,7 +180,7 @@ void sqliteUpdate( end = sqliteVdbeMakeLabel(v); addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0); sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); - sqliteVdbeAddOp(v, OP_Fetch, base, 0, 0, 0); + sqliteVdbeAddOp(v, OP_MoveTo, base, 0, 0, 0); /* Delete the old indices for the current record. */ @@ -185,9 +188,9 @@ void sqliteUpdate( sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); pIdx = apIdx[i]; for(j=0; jnColumn; j++){ - sqliteVdbeAddOp(v, OP_Field, base, pIdx->aiColumn[j], 0, 0); + sqliteVdbeAddOp(v, OP_Column, base, pIdx->aiColumn[j], 0, 0); } - sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nColumn, 0, 0, 0); + sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0, 0, 0); sqliteVdbeAddOp(v, OP_DeleteIdx, base+i+1, 0, 0, 0); } @@ -196,7 +199,7 @@ void sqliteUpdate( for(i=0; inCol; i++){ j = aXRef[i]; if( j<0 ){ - sqliteVdbeAddOp(v, OP_Field, base, i, 0, 0); + sqliteVdbeAddOp(v, OP_Column, base, i, 0, 0); }else{ sqliteExprCode(pParse, pChanges->a[j].pExpr); } @@ -210,7 +213,7 @@ void sqliteUpdate( for(j=0; jnColumn; j++){ sqliteVdbeAddOp(v, OP_Dup, j+pTab->nCol-pIdx->aiColumn[j], 0, 0, 0); } - sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nColumn, 0, 0, 0); + sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0, 0, 0); sqliteVdbeAddOp(v, OP_PutIdx, base+i+1, 0, 0, 0); } @@ -224,6 +227,9 @@ void sqliteUpdate( */ sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0); sqliteVdbeAddOp(v, OP_ListClose, 0, 0, 0, end); + if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0); + } update_cleanup: sqliteFree(apIdx); diff --git a/src/util.c b/src/util.c index eed1f97e73..77e854fad9 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.21 2001/04/11 14:28:43 drh Exp $ +** $Id: util.c,v 1.22 2001/09/13 13:46:57 drh Exp $ */ #include "sqliteInt.h" #include @@ -972,3 +972,31 @@ sqliteLikeCompare(const unsigned char *zPattern, const unsigned char *zString){ } return *zString==0; } + +/* +** Return a static string that describes the kind of error specified in the +** argument. +*/ +const char *sqliteErrStr(int rc){ + char *z = 0; + 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: + } + return z; +} diff --git a/src/vdbe.c b/src/vdbe.c index e13e32bdb8..8083ae5149 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.59 2001/08/19 18:19:46 drh Exp $ +** $Id: vdbe.c,v 1.60 2001/09/13 13:46:57 drh Exp $ */ #include "sqliteInt.h" #include @@ -53,6 +53,11 @@ */ typedef struct VdbeOp Op; +/* +** Boolean values +*/ +typedef unsigned char Bool; + /* ** A cursor is a pointer into a database file. The database file ** can represent either an SQL table or an SQL index. Each file is @@ -64,11 +69,15 @@ typedef struct VdbeOp Op; ** instance of the following structure. */ struct Cursor { - DbbeCursor *pCursor; /* The cursor structure of the backend */ - int index; /* The next index to extract */ - int lastKey; /* Last key from a Next or NextIdx operation */ - int keyIsValid; /* True if lastKey is valid */ - int keyAsData; /* The OP_Field command works on key instead of data */ + BtCursor *pCursor; /* The cursor structure of the backend */ + int lastRecno; /* Last recno from a Next or NextIdx operation */ + Bool recnoIsValid; /* True if lastRecno is valid */ + Bool keyAsData; /* The OP_Column command works on key instead of data */ + Bool atFirst; /* True if pointing to first entry */ + Btree *pBt; /* Separate file holding temporary table */ + char *zKey; /* Key used in BeginIdx and NextIdx operators */ + int nKey; /* Number of bytes in zKey[] */ + char *zBuf; /* Buffer space used to hold a copy of zKey[] */ }; typedef struct Cursor Cursor; @@ -214,6 +223,8 @@ struct Vdbe { Agg agg; /* Aggregate information */ int nSet; /* Number of sets allocated */ Set *aSet; /* An array of sets */ + int *pTableRoot; /* Write root page no. for new tables to this addr */ + int *pIndexRoot; /* Write root page no. for new indices to this addr */ int nFetch; /* Number of OP_Fetch instructions executed */ }; @@ -236,6 +247,24 @@ void sqliteVdbeTrace(Vdbe *p, FILE *trace){ p->trace = trace; } +/* +** Cause the next OP_CreateTable or OP_CreateIndex instruction that executes +** to write the page number of the root page for the new table or index it +** creates into the memory location *pAddr. +** +** The pointer to the place to write the page number is cleared after +** the OP_Create* statement. If OP_Create* is executed and the pointer +** is NULL, an error results. Hence the address can only be used once. +** If the root address fields are set but OP_Create* operations never +** execute, that too is an error. +*/ +void sqliteVdbeTableRootAddr(Vdbe *p, int *pAddr){ + p->pTableRoot = pAddr; +} +void sqliteVdbeIndexRootAddr(Vdbe *p, int *pAddr){ + p->pIndexRoot = pAddr; +} + /* ** Add a new instruction to the list of instructions current in the ** VDBE. Return the address of the new instruction. @@ -342,6 +371,18 @@ int sqliteVdbeAddOpList(Vdbe *p, int nOp, VdbeOp const *aOp){ return addr; } +/* +** Change the value of the P1 operand for a specific instruction. +** This routine is useful when a large program is loaded from a +** static array using sqliteVdbeAddOpList but we want to make a +** few minor changes to the program. +*/ +void sqliteVdbeChangeP1(Vdbe *p, int addr, int val){ + if( p && addr>=0 && p->nOp>addr ){ + p->aOp[addr].p1 = val; + } +} + /* ** Change the value of the P3 operand for a specific instruction. ** This routine is useful when a large program is loaded from a @@ -720,7 +761,7 @@ static void KeylistFree(Keylist *p){ /* ** Clean up the VM after execution. ** -** This routine will automatically close any cursors, list, and/or +** This routine will automatically close any cursors, lists, and/or ** sorters that were left open. */ static void Cleanup(Vdbe *p){ @@ -729,9 +770,18 @@ static void Cleanup(Vdbe *p){ sqliteFree(p->azColName); p->azColName = 0; for(i=0; inCursor; i++){ - if( p->aCsr[i].pCursor ){ - p->pBe->x->CloseCursor(p->aCsr[i].pCursor); - p->aCsr[i].pCursor = 0; + 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; } } sqliteFree(p->aCsr); @@ -785,6 +835,8 @@ static void Cleanup(Vdbe *p){ sqliteFree(p->aSet); p->aSet = 0; p->nSet = 0; + p->pTableRoot = 0; + p->pIndexRoot = 0; } /* @@ -818,29 +870,31 @@ void sqliteVdbeDelete(Vdbe *p){ ** this array, then copy and paste it into this file, if you want. */ static char *zOpName[] = { 0, - "OpenIdx", "OpenTbl", "Close", "Fetch", - "Fcnt", "New", "Put", "Distinct", - "Found", "NotFound", "Delete", "Field", - "KeyAsData", "Key", "FullKey", "Rewind", - "Next", "Destroy", "Reorganize", "BeginIdx", - "NextIdx", "PutIdx", "DeleteIdx", "MemLoad", - "MemStore", "ListOpen", "ListWrite", "ListRewind", - "ListRead", "ListClose", "SortOpen", "SortPut", - "SortMakeRec", "SortMakeKey", "Sort", "SortNext", - "SortKey", "SortCallback", "SortClose", "FileOpen", - "FileRead", "FileField", "FileClose", "AggReset", - "AggFocus", "AggIncr", "AggNext", "AggSet", - "AggGet", "SetInsert", "SetFound", "SetNotFound", - "SetClear", "MakeRecord", "MakeKey", "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", "Open", + "OpenTemp", "Close", "MoveTo", "Fcnt", + "NewRecno", "Put", "Distinct", "Found", + "NotFound", "Delete", "Column", "KeyAsData", + "Recno", "FullKey", "Rewind", "Next", + "Destroy", "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", "FileField", "FileClose", + "AggReset", "AggFocus", "AggIncr", "AggNext", + "AggSet", "AggGet", "SetInsert", "SetFound", + "SetNotFound", "SetClear", "MakeRecord", "MakeKey", + "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", }; /* @@ -985,11 +1039,11 @@ int sqliteVdbeExec( Op *pOp; /* Current operation */ int rc; /* Value to return */ Dbbe *pBe = p->pBe; /* The backend driver */ - DbbeMethods *pBex = pBe->x; /* The backend driver methods */ sqlite *db = p->db; /* The database */ - char **zStack; - Stack *aStack; - char zBuf[100]; /* Space to sprintf() and integer */ + int rollbackOnError = 0; /* If TRUE, rollback if the script fails. + char **zStack; /* Text stack */ + Stack *aStack; /* Additional stack information */ + char zBuf[100]; /* Space to sprintf() an integer */ /* No instruction ever pushes more than a single element onto the @@ -1035,2302 +1089,2506 @@ int sqliteVdbeExec( #endif switch( pOp->opcode ){ - /* Opcode: Goto P2 * * - ** - ** An unconditional jump to address P2. - ** The next instruction executed will be - ** the one at index P2 from the beginning of - ** the program. - */ - case OP_Goto: { - pc = pOp->p2 - 1; - break; - } - /* Opcode: Halt * * * - ** - ** Exit immediately. All open DBs, Lists, Sorts, etc are closed - ** automatically. - */ - case OP_Halt: { - pc = p->nOp-1; - break; - } - - /* Opcode: Integer P1 * * - ** - ** The integer value P1 is pushed onto the stack. - */ - case OP_Integer: { - int i = ++p->tos; - VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) - aStack[i].i = pOp->p1; - aStack[i].flags = STK_Int; - break; - } - - /* Opcode: String * * P3 - ** - ** The string value P3 is pushed onto the stack. - */ - case OP_String: { - int i = ++p->tos; - char *z; - VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) - z = pOp->p3; - if( z==0 ) z = ""; - zStack[i] = z; - aStack[i].n = strlen(z) + 1; - aStack[i].flags = STK_Str; - break; - } - - /* Opcode: Null * * * - ** - ** Push a NULL value onto the stack. - */ - case OP_Null: { - int i = ++p->tos; - VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) - zStack[i] = 0; - aStack[i].flags = STK_Null; - break; - } - - /* Opcode: Pop P1 * * - ** - ** P1 elements are popped off of the top of stack and discarded. - */ - case OP_Pop: { - PopStack(p, pOp->p1); - break; - } - - /* Opcode: Dup P1 * * - ** - ** A copy of the P1-th element of the stack - ** is made and pushed onto the top of the stack. - ** The top of the stack is element 0. So the - ** instruction "Dup 0 0 0" will make a copy of the - ** top of the stack. - */ - case OP_Dup: { - int i = p->tos - pOp->p1; - int j = ++p->tos; - VERIFY( if( i<0 ) goto not_enough_stack; ) - VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) - aStack[j] = aStack[i]; - if( aStack[i].flags & STK_Dyn ){ - zStack[j] = sqliteMalloc( aStack[j].n ); - if( zStack[j]==0 ) goto no_mem; - memcpy(zStack[j], zStack[i], aStack[j].n); - }else{ - zStack[j] = zStack[i]; - } - break; - } - - /* Opcode: Pull P1 * * - ** - ** The P1-th element is removed from its current location on - ** the stack and pushed back on top of the stack. The - ** top of the stack is element 0, so "Pull 0 0 0" is - ** a no-op. - */ - case OP_Pull: { - int from = p->tos - pOp->p1; - int to = p->tos; - int i; - Stack ts; - char *tz; - VERIFY( if( from<0 ) goto not_enough_stack; ) - ts = aStack[from]; - tz = zStack[from]; - for(i=from; iazColName = sqliteRealloc(p->azColName, (pOp->p1+1)*sizeof(char*)); - if( p->azColName==0 ) goto no_mem; - p->azColName[pOp->p1] = 0; - break; - } - - /* Opcode: ColumnName P1 * P3 - ** - ** P3 becomes the P1-th column name (first is 0). An array of pointers - ** to all column names is passed as the 4th parameter to the callback. - ** The ColumnCount opcode must be executed first to allocate space to - ** hold the column names. Failure to do this will likely result in - ** a coredump. - */ - case OP_ColumnName: { - p->azColName[pOp->p1] = pOp->p3 ? pOp->p3 : ""; - break; - } - - /* Opcode: Callback P1 * * - ** - ** Pop P1 values off the stack and form them into an array. Then - ** invoke the callback function using the newly formed array as the - ** 3rd parameter. - */ - case OP_Callback: { - int i = p->tos - pOp->p1 + 1; - int j; - VERIFY( if( i<0 ) goto not_enough_stack; ) - VERIFY( if( NeedStack(p, p->tos+2) ) goto no_mem; ) - for(j=i; j<=p->tos; j++){ - if( (aStack[j].flags & STK_Null)==0 ){ - if( Stringify(p, j) ) goto no_mem; - } - } - zStack[p->tos+1] = 0; - if( xCallback!=0 ){ - if( xCallback(pArg, pOp->p1, &zStack[i], p->azColName)!=0 ){ - rc = SQLITE_ABORT; - } - } - PopStack(p, pOp->p1); - break; - } - - /* Opcode: Concat P1 P2 P3 - ** - ** Look at the first P1 elements of the stack. Append them all - ** together with the lowest element first. Use P3 as a separator. - ** Put the result on the top of the stack. The original P1 elements - ** are popped from the stack if P2==0 and retained if P2==1. - ** - ** If P3 is NULL, then use no separator. When P1==1, this routine - ** makes a copy of the top stack element into memory obtained - ** from sqliteMalloc(). - */ - case OP_Concat: { - char *zNew; - int nByte; - int nField; - int i, j; - char *zSep; - int nSep; - - nField = pOp->p1; - zSep = pOp->p3; - if( zSep==0 ) zSep = ""; - nSep = strlen(zSep); - VERIFY( if( p->tos+1tos-nField+1; i<=p->tos; i++){ - if( aStack[i].flags & STK_Null ){ - nByte += nSep; - }else{ - if( Stringify(p, i) ) goto no_mem; - nByte += aStack[i].n - 1 + nSep; - } - } - zNew = sqliteMalloc( nByte ); - if( zNew==0 ) goto no_mem; - j = 0; - for(i=p->tos-nField+1; i<=p->tos; i++){ - if( (aStack[i].flags & STK_Null)==0 ){ - memcpy(&zNew[j], zStack[i], aStack[i].n-1); - j += aStack[i].n-1; - } - if( nSep>0 && itos ){ - memcpy(&zNew[j], zSep, nSep); - j += nSep; - } - } - zNew[j] = 0; - if( pOp->p2==0 ) PopStack(p, nField); - VERIFY( NeedStack(p, p->tos+1); ) - p->tos++; - aStack[p->tos].n = nByte; - aStack[p->tos].flags = STK_Str|STK_Dyn; - zStack[p->tos] = zNew; - break; - } - - /* Opcode: Add * * * - ** - ** Pop the top two elements from the stack, add them together, - ** and push the result back onto the stack. If either element - ** is a string then it is converted to a double using the atof() - ** function before the addition. - */ - /* Opcode: Multiply * * * - ** - ** Pop the top two elements from the stack, multiply them together, - ** and push the result back onto the stack. If either element - ** is a string then it is converted to a double using the atof() - ** function before the multiplication. - */ - /* Opcode: Subtract * * * - ** - ** Pop the top two elements from the stack, subtract the - ** first (what was on top of the stack) from the second (the - ** next on stack) - ** and push the result back onto the stack. If either element - ** is a string then it is converted to a double using the atof() - ** function before the subtraction. - */ - /* Opcode: Divide * * * - ** - ** Pop the top two elements from the stack, divide the - ** first (what was on top of the stack) from the second (the - ** next on stack) - ** and push the result back onto the stack. If either element - ** is a string then it is converted to a double using the atof() - ** function before the division. Division by zero returns NULL. - */ - case OP_Add: - case OP_Subtract: - case OP_Multiply: - case OP_Divide: { - int tos = p->tos; - int nos = tos - 1; - VERIFY( if( nos<0 ) goto not_enough_stack; ) - if( (aStack[tos].flags & aStack[nos].flags & STK_Int)==STK_Int ){ - int a, b; - a = aStack[tos].i; - b = aStack[nos].i; - switch( pOp->opcode ){ - case OP_Add: b += a; break; - case OP_Subtract: b -= a; break; - case OP_Multiply: b *= a; break; - default: { - if( a==0 ) goto divide_by_zero; - b /= a; - break; - } - } - POPSTACK; - Release(p, nos); - aStack[nos].i = b; - aStack[nos].flags = STK_Int; - }else{ - double a, b; - Realify(p, tos); - Realify(p, nos); - a = aStack[tos].r; - b = aStack[nos].r; - switch( pOp->opcode ){ - case OP_Add: b += a; break; - case OP_Subtract: b -= a; break; - case OP_Multiply: b *= a; break; - default: { - if( a==0.0 ) goto divide_by_zero; - b /= a; - break; - } - } - POPSTACK; - Release(p, nos); - aStack[nos].r = b; - aStack[nos].flags = STK_Real; - } - break; - - divide_by_zero: - PopStack(p, 2); - p->tos = nos; - aStack[nos].flags = STK_Null; - break; - } - - /* Opcode: Max * * * - ** - ** Pop the top two elements from the stack then push back the - ** largest of the two. - */ - case OP_Max: { - int tos = p->tos; - int nos = tos - 1; - int ft, fn; - int copy = 0; - VERIFY( if( nos<0 ) goto not_enough_stack; ) - ft = aStack[tos].flags; - fn = aStack[nos].flags; - if( fn & STK_Null ){ - copy = 1; - }else if( (ft & fn & STK_Int)==STK_Int ){ - copy = aStack[nos].iaStack[nos].r; - }else{ - if( Stringify(p, tos) || Stringify(p, nos) ) goto no_mem; - copy = sqliteCompare(zStack[tos],zStack[nos])>0; - } - if( copy ){ - Release(p, nos); - aStack[nos] = aStack[tos]; - zStack[nos] = zStack[tos]; - zStack[tos] = 0; - aStack[tos].flags = 0; - }else{ - Release(p, tos); - } - p->tos = nos; - break; - } - - /* Opcode: Min * * * - ** - ** Pop the top two elements from the stack then push back the - ** smaller of the two. - */ - case OP_Min: { - int tos = p->tos; - int nos = tos - 1; - int ft, fn; - int copy = 0; - VERIFY( if( nos<0 ) goto not_enough_stack; ) - ft = aStack[tos].flags; - fn = aStack[nos].flags; - if( fn & STK_Null ){ - copy = 1; - }else if( ft & STK_Null ){ - copy = 0; - }else if( (ft & fn & STK_Int)==STK_Int ){ - copy = aStack[nos].i>aStack[tos].i; - }else if( ( (ft|fn) & (STK_Int|STK_Real) ) !=0 ){ - Realify(p, tos); - Realify(p, nos); - copy = aStack[tos].rtos = nos; - break; - } - - /* Opcode: AddImm P1 * * - ** - ** Add the value P1 to whatever is on top of the stack. - */ - case OP_AddImm: { - int tos = p->tos; - VERIFY( if( tos<0 ) goto not_enough_stack; ) - Integerify(p, tos); - aStack[tos].i += pOp->p1; - break; - } - - /* Opcode: Eq * P2 * - ** - ** Pop the top two elements from the stack. If they are equal, then - ** jump to instruction P2. Otherwise, continue to the next instruction. - */ - /* Opcode: Ne * P2 * - ** - ** Pop the top two elements from the stack. If they are not equal, then - ** jump to instruction P2. Otherwise, continue to the next instruction. - */ - /* Opcode: Lt * P2 * - ** - ** Pop the top two elements from the stack. If second element (the - ** next on stack) is less than the first (the top of stack), then - ** jump to instruction P2. Otherwise, continue to the next instruction. - ** In other words, jump if NOSTOS. - */ - /* Opcode: Ge * P2 * - ** - ** Pop the top two elements from the stack. If second element (the next - ** on stack) is greater than or equal to the first (the top of stack), - ** then jump to instruction P2. In other words, jump if NOS>=TOS. - */ - case OP_Eq: - case OP_Ne: - case OP_Lt: - case OP_Le: - case OP_Gt: - case OP_Ge: { - int tos = p->tos; - int nos = tos - 1; - int c; - int ft, fn; - VERIFY( if( nos<0 ) goto not_enough_stack; ) - ft = aStack[tos].flags; - fn = aStack[nos].flags; - if( (ft & fn)==STK_Int ){ - c = aStack[nos].i - aStack[tos].i; - }else{ - if( Stringify(p, tos) || Stringify(p, nos) ) goto no_mem; - c = sqliteCompare(zStack[nos], zStack[tos]); - } - switch( pOp->opcode ){ - case OP_Eq: c = c==0; break; - case OP_Ne: c = c!=0; break; - case OP_Lt: c = c<0; break; - case OP_Le: c = c<=0; break; - case OP_Gt: c = c>0; break; - default: c = c>=0; break; - } - POPSTACK; - POPSTACK; - if( c ) pc = pOp->p2-1; - break; - } - - /* Opcode: Like P1 P2 * - ** - ** Pop the top two elements from the stack. The top-most is a - ** "like" pattern -- the right operand of the SQL "LIKE" operator. - ** The lower element is the string to compare against the like - ** pattern. Jump to P2 if the two compare, and fall through without - ** jumping if they do not. The '%' in the top-most element matches - ** any sequence of zero or more characters in the lower element. The - ** '_' character in the topmost matches any single character of the - ** lower element. Case is ignored for this comparison. - ** - ** If P1 is not zero, the sense of the test is inverted and we - ** have a "NOT LIKE" operator. The jump is made if the two values - ** are different. - */ - case OP_Like: { - int tos = p->tos; - int nos = tos - 1; - int c; - VERIFY( if( nos<0 ) goto not_enough_stack; ) - if( Stringify(p, tos) || Stringify(p, nos) ) goto no_mem; - c = sqliteLikeCompare(zStack[tos], zStack[nos]); - POPSTACK; - POPSTACK; - if( pOp->p1 ) c = !c; - if( c ) pc = pOp->p2-1; - break; - } - - /* Opcode: Glob P1 P2 * - ** - ** Pop the top two elements from the stack. The top-most is a - ** "glob" pattern. The lower element is the string to compare - ** against the glob pattern. - ** - ** Jump to P2 if the two compare, and fall through without - ** jumping if they do not. The '*' in the top-most element matches - ** any sequence of zero or more characters in the lower element. The - ** '?' character in the topmost matches any single character of the - ** lower element. [...] matches a range of characters. [^...] - ** matches any character not in the range. Case is significant - ** for globs. - ** - ** If P1 is not zero, the sense of the test is inverted and we - ** have a "NOT GLOB" operator. The jump is made if the two values - ** are different. - */ - case OP_Glob: { - int tos = p->tos; - int nos = tos - 1; - int c; - VERIFY( if( nos<0 ) goto not_enough_stack; ) - if( Stringify(p, tos) || Stringify(p, nos) ) goto no_mem; - c = sqliteGlobCompare(zStack[tos], zStack[nos]); - POPSTACK; - POPSTACK; - if( pOp->p1 ) c = !c; - if( c ) pc = pOp->p2-1; - break; - } - - /* Opcode: And * * * - ** - ** Pop two values off the stack. Take the logical AND of the - ** two values and push the resulting boolean value back onto the - ** stack. - */ - /* Opcode: Or * * * - ** - ** Pop two values off the stack. Take the logical OR of the - ** two values and push the resulting boolean value back onto the - ** stack. - */ - case OP_And: - case OP_Or: { - int tos = p->tos; - int nos = tos - 1; - int c; - VERIFY( if( nos<0 ) goto not_enough_stack; ) - Integerify(p, tos); - Integerify(p, nos); - if( pOp->opcode==OP_And ){ - c = aStack[tos].i && aStack[nos].i; - }else{ - c = aStack[tos].i || aStack[nos].i; - } - POPSTACK; - Release(p, nos); - aStack[nos].i = c; - aStack[nos].flags = STK_Int; - break; - } - - /* Opcode: Negative * * * - ** - ** Treat the top of the stack as a numeric quantity. Replace it - ** with its additive inverse. - */ - case OP_Negative: { - int tos = p->tos; - VERIFY( if( tos<0 ) goto not_enough_stack; ) - if( aStack[tos].flags & STK_Real ){ - Release(p, tos); - aStack[tos].r = -aStack[tos].r; - aStack[tos].flags = STK_Real; - }else if( aStack[tos].flags & STK_Int ){ - Release(p, tos); - aStack[tos].i = -aStack[tos].i; - aStack[tos].flags = STK_Int; - }else{ - Realify(p, tos); - Release(p, tos); - aStack[tos].r = -aStack[tos].r; - aStack[tos].flags = STK_Real; - } - break; - } - - /* Opcode: Not * * * - ** - ** Interpret the top of the stack as a boolean value. Replace it - ** with its complement. - */ - case OP_Not: { - int tos = p->tos; - VERIFY( if( p->tos<0 ) goto not_enough_stack; ) - Integerify(p, tos); - Release(p, tos); - aStack[tos].i = !aStack[tos].i; - aStack[tos].flags = STK_Int; - break; - } - - /* Opcode: Noop * * * - ** - ** Do nothing. This instruction is often useful as a jump - ** destination. - */ - case OP_Noop: { - break; - } - - /* Opcode: If * P2 * - ** - ** Pop a single boolean from the stack. If the boolean popped is - ** true, then jump to p2. Otherwise continue to the next instruction. - ** An integer is false if zero and true otherwise. A string is - ** false if it has zero length and true otherwise. - */ - case OP_If: { - int c; - VERIFY( if( p->tos<0 ) goto not_enough_stack; ) - Integerify(p, p->tos); - c = aStack[p->tos].i; - POPSTACK; - if( c ) pc = pOp->p2-1; - break; - } - - /* Opcode: IsNull * P2 * - ** - ** Pop a single value from the stack. If the value popped is NULL - ** then jump to p2. Otherwise continue to the next - ** instruction. - */ - case OP_IsNull: { - int c; - VERIFY( if( p->tos<0 ) goto not_enough_stack; ) - c = (aStack[p->tos].flags & STK_Null)!=0; - POPSTACK; - if( c ) pc = pOp->p2-1; - break; - } - - /* Opcode: NotNull * P2 * - ** - ** Pop a single value from the stack. If the value popped is not an - ** empty string, then jump to p2. Otherwise continue to the next - ** instruction. - */ - case OP_NotNull: { - int c; - VERIFY( if( p->tos<0 ) goto not_enough_stack; ) - c = (aStack[p->tos].flags & STK_Null)==0; - POPSTACK; - if( c ) pc = pOp->p2-1; - break; - } - - /* Opcode: MakeRecord P1 * * - ** - ** Convert the top P1 entries of the stack into a single entry - ** suitable for use as a data record in the database. To do this - ** all entries (except NULLs) are converted to strings and - ** concatenated. The null-terminators are preserved by the concatation - ** and serve as a boundry marker between fields. The lowest entry - ** on the stack is the first in the concatenation and the top of - ** the stack is the last. After all fields are concatenated, an - ** index header is added. The index header consists of P1 integers - ** which hold the offset of the beginning of each field from the - ** beginning of the completed record including the header. The - ** index for NULL entries is 0. - */ - case OP_MakeRecord: { - char *zNewRecord; - int nByte; - int nField; - int i, j; - int addr; - - nField = pOp->p1; - VERIFY( if( p->tos+1tos-nField+1; i<=p->tos; i++){ - if( (aStack[i].flags & STK_Null)==0 ){ - if( Stringify(p, i) ) goto no_mem; - nByte += aStack[i].n; - } - } - nByte += sizeof(int)*nField; - zNewRecord = sqliteMalloc( nByte ); - if( zNewRecord==0 ) goto no_mem; - j = 0; - addr = sizeof(int)*nField; - for(i=p->tos-nField+1; i<=p->tos; i++){ - if( aStack[i].flags & STK_Null ){ - int zero = 0; - memcpy(&zNewRecord[j], (char*)&zero, sizeof(int)); - }else{ - memcpy(&zNewRecord[j], (char*)&addr, sizeof(int)); - addr += aStack[i].n; - } - j += sizeof(int); - } - for(i=p->tos-nField+1; i<=p->tos; i++){ - if( (aStack[i].flags & STK_Null)==0 ){ - memcpy(&zNewRecord[j], zStack[i], aStack[i].n); - j += aStack[i].n; - } - } - PopStack(p, nField); - VERIFY( NeedStack(p, p->tos+1); ) - p->tos++; - aStack[p->tos].n = nByte; - aStack[p->tos].flags = STK_Str | STK_Dyn; - zStack[p->tos] = zNewRecord; - break; - } - - /* Opcode: MakeKey P1 P2 * - ** - ** Convert the top P1 entries of the stack into a single entry suitable - ** for use as the key in an index or a sort. The top P1 records are - ** concatenated with a tab character (ASCII 0x09) used as a record - ** separator. The entire concatenation is null-terminated. The - ** lowest entry in the stack is the first field and the top of the - ** stack becomes the last. - ** - ** If P2 is not zero, then the original entries remain on the stack - ** and the new key is pushed on top. If P2 is zero, the original - ** data is popped off the stack first then the new key is pushed - ** back in its place. - ** - ** See also the SortMakeKey opcode. - */ - case OP_MakeKey: { - char *zNewKey; - int nByte; - int nField; - int i, j; - - nField = pOp->p1; - VERIFY( if( p->tos+1tos-nField+1; i<=p->tos; i++){ - if( aStack[i].flags & STK_Null ){ - nByte++; - }else{ - if( Stringify(p, i) ) goto no_mem; - nByte += aStack[i].n; - } - } - zNewKey = sqliteMalloc( nByte ); - if( zNewKey==0 ) goto no_mem; - j = 0; - for(i=p->tos-nField+1; i<=p->tos; i++){ - if( (aStack[i].flags & STK_Null)==0 ){ - memcpy(&zNewKey[j], zStack[i], aStack[i].n-1); - j += aStack[i].n-1; - } - if( itos ) zNewKey[j++] = '\t'; - } - zNewKey[j] = 0; - if( pOp->p2==0 ) PopStack(p, nField); - VERIFY( NeedStack(p, p->tos+1); ) - p->tos++; - aStack[p->tos].n = nByte; - aStack[p->tos].flags = STK_Str|STK_Dyn; - zStack[p->tos] = zNewKey; - break; - } - - /* Opcode: OpenIdx P1 P2 P3 - ** - ** Open a new cursor for the database file named P3. Give the - ** cursor an identifier P1. The P1 values need not be - ** contiguous but all P1 values should be small integers. It is - ** an error for P1 to be negative. - ** - ** Open readonly if P2==0 and for reading and writing if P2!=0. - ** The file is created if it does not already exist and P2!=0. - ** If there is already another cursor opened with identifier P1, - ** then the old cursor is closed first. All cursors are - ** automatically closed when the VDBE finishes execution. - ** - ** If P3 is null or an empty string, a temporary database file - ** is created. This temporary database file is automatically - ** deleted when the cursor is closed. - ** - ** The database file opened must be able to map arbitrary length - ** keys into arbitrary data. A similar opcode, OpenTbl, opens - ** a database file that maps integer keys into arbitrary length - ** data. This opcode opens database files used as - ** SQL indices and OpenTbl opens database files used for SQL - ** tables. - */ - /* Opcode: OpenTbl P1 P2 P3 - ** - ** This works just like the OpenIdx operation except that the database - ** file that is opened is one that will only accept integers as - ** keys. Some database backends are able to operate more efficiently - ** if keys are always integers. So if SQLite knows in advance that - ** all keys will be integers, it uses this opcode rather than Open - ** in order to give the backend an opportunity to run faster. - ** - ** This opcode opens database files used for storing SQL tables. - ** The OpenIdx opcode opens files used for SQL indices. - */ - case OP_OpenIdx: - case OP_OpenTbl: { - int busy = 0; - int i = pOp->p1; - VERIFY( if( i<0 ) goto bad_instruction; ) - if( i>=p->nCursor ){ - int j; - p->aCsr = sqliteRealloc( p->aCsr, (i+1)*sizeof(Cursor) ); - if( p->aCsr==0 ){ p->nCursor = 0; goto no_mem; } - for(j=p->nCursor; j<=i; j++) p->aCsr[j].pCursor = 0; - p->nCursor = i+1; - }else if( p->aCsr[i].pCursor ){ - pBex->CloseCursor(p->aCsr[i].pCursor); - } - do { - rc = pBex->OpenCursor(pBe,pOp->p3, pOp->p2, - pOp->opcode==OP_OpenTbl, &p->aCsr[i].pCursor); - switch( rc ){ - case SQLITE_BUSY: { - if( xBusy==0 || (*xBusy)(pBusyArg, pOp->p3, ++busy)==0 ){ - sqliteSetString(pzErrMsg,"table ", pOp->p3, " is locked", 0); - busy = 0; - } - break; - } - case SQLITE_PERM: { - sqliteSetString(pzErrMsg, pOp->p2 ? "write" : "read", - " permission denied for table ", pOp->p3, 0); - break; - } - case SQLITE_READONLY: { - sqliteSetString(pzErrMsg,"table ", pOp->p3, - " is readonly", 0); - break; - } - case SQLITE_NOMEM: { - goto no_mem; - } - case SQLITE_OK: { - busy = 0; - break; - } - } - }while( busy ); - p->aCsr[i].index = 0; - p->aCsr[i].keyAsData = 0; - break; - } - - /* Opcode: Close P1 * * - ** - ** Close a cursor previously opened as P1. If P1 is not - ** currently open, this instruction is a no-op. - */ - case OP_Close: { - int i = pOp->p1; - if( i>=0 && inCursor && p->aCsr[i].pCursor ){ - pBex->CloseCursor(p->aCsr[i].pCursor); - p->aCsr[i].pCursor = 0; - } - break; - } - - /* Opcode: Fetch P1 * * - ** - ** Pop the top of the stack and use its value as a key to fetch - ** a record from cursor P1. The key/data pair is held - ** in the P1 cursor until needed. - */ - case OP_Fetch: { - int i = pOp->p1; - int tos = p->tos; - VERIFY( if( tos<0 ) goto not_enough_stack; ) - if( i>=0 && inCursor && p->aCsr[i].pCursor ){ - if( aStack[tos].flags & STK_Int ){ - pBex->Fetch(p->aCsr[i].pCursor, sizeof(int), - (char*)&aStack[tos].i); - p->aCsr[i].lastKey = aStack[tos].i; - p->aCsr[i].keyIsValid = 1; - }else{ - if( Stringify(p, tos) ) goto no_mem; - pBex->Fetch(p->aCsr[i].pCursor, aStack[tos].n, - zStack[tos]); - p->aCsr[i].keyIsValid = 0; - } - p->nFetch++; - } - POPSTACK; - break; - } - - /* Opcode: Fcnt * * * - ** - ** Push an integer onto the stack which is the total number of - ** OP_Fetch opcodes that have been executed by this virtual machine. - ** - ** This instruction is used to implement the special fcnt() function - ** in the SQL dialect that SQLite understands. fcnt() is used for - ** testing purposes. - */ - case OP_Fcnt: { - int i = ++p->tos; - VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) - aStack[i].i = p->nFetch; - aStack[i].flags = STK_Int; - break; - } - - /* Opcode: Distinct P1 P2 * - ** - ** Use the top of the stack as a key. If a record with that key - ** does not exist in file P1, then jump to P2. If the record - ** does already exist, then fall thru. The record is not retrieved. - ** The key is not popped from the stack. - ** - ** This operation is similar to NotFound except that this operation - ** does not pop the key from the stack. - */ - /* Opcode: Found P1 P2 * - ** - ** Use the top of the stack as a key. If a record with that key - ** does exist in file P1, then jump to P2. If the record - ** does not exist, then fall thru. The record is not retrieved. - ** The key is popped from the stack. - */ - /* Opcode: NotFound P1 P2 * - ** - ** Use the top of the stack as a key. If a record with that key - ** does not exist in file P1, then jump to P2. If the record - ** does exist, then fall thru. The record is not retrieved. - ** The key is popped from the stack. - ** - ** The difference between this operation and Distinct is that - ** Distinct does not pop the key from the stack. - */ - case OP_Distinct: - case OP_NotFound: - case OP_Found: { - int i = pOp->p1; - int tos = p->tos; - int alreadyExists = 0; - VERIFY( if( tos<0 ) goto not_enough_stack; ) - if( VERIFY( i>=0 && inCursor && ) p->aCsr[i].pCursor ){ - if( aStack[tos].flags & STK_Int ){ - alreadyExists = pBex->Test(p->aCsr[i].pCursor, sizeof(int), - (char*)&aStack[tos].i); - }else{ - if( Stringify(p, tos) ) goto no_mem; - alreadyExists = pBex->Test(p->aCsr[i].pCursor,aStack[tos].n, - zStack[tos]); - } - } - if( pOp->opcode==OP_Found ){ - if( alreadyExists ) pc = pOp->p2 - 1; - }else{ - if( !alreadyExists ) pc = pOp->p2 - 1; - } - if( pOp->opcode!=OP_Distinct ){ - POPSTACK; - } - break; - } - - /* Opcode: New P1 * * - ** - ** Get a new integer key not previous used by the database file - ** associated with cursor P1 and push it onto the stack. - */ - case OP_New: { - int i = pOp->p1; - int v; - if( VERIFY( i<0 || i>=p->nCursor || ) p->aCsr[i].pCursor==0 ){ - v = 0; - }else{ - v = pBex->New(p->aCsr[i].pCursor); - } - VERIFY( NeedStack(p, p->tos+1); ) - p->tos++; - aStack[p->tos].i = v; - aStack[p->tos].flags = STK_Int; - break; - } - - /* Opcode: Put P1 * * - ** - ** Write an entry into the database file P1. A new entry is - ** created if it doesn't already exist, or the data for an existing - ** entry is overwritten. The data is the value on the top of the - ** stack. The key is the next value down on the stack. The stack - ** is popped twice by this instruction. - */ - case OP_Put: { - int tos = p->tos; - int nos = p->tos-1; - int i = pOp->p1; - VERIFY( if( nos<0 ) goto not_enough_stack; ) - if( VERIFY( i>=0 && inCursor && ) p->aCsr[i].pCursor!=0 ){ - char *zKey; - int nKey; - if( (aStack[nos].flags & STK_Int)==0 ){ - if( Stringify(p, nos) ) goto no_mem; - nKey = aStack[nos].n; - zKey = zStack[nos]; - }else{ - nKey = sizeof(int); - zKey = (char*)&aStack[nos].i; - } - pBex->Put(p->aCsr[i].pCursor, nKey, zKey, - aStack[tos].n, zStack[tos]); - } - POPSTACK; - POPSTACK; - break; - } - - /* Opcode: Delete P1 * * - ** - ** The top of the stack is a key. Remove this key and its data - ** from database file P1. Then pop the stack to discard the key. - */ - case OP_Delete: { - int tos = p->tos; - int i = pOp->p1; - VERIFY( if( tos<0 ) goto not_enough_stack; ) - if( VERIFY( i>=0 && inCursor && ) p->aCsr[i].pCursor!=0 ){ - char *zKey; - int nKey; - if( aStack[tos].flags & STK_Int ){ - nKey = sizeof(int); - zKey = (char*)&aStack[tos].i; - }else{ - if( Stringify(p, tos) ) goto no_mem; - nKey = aStack[tos].n; - zKey = zStack[tos]; - } - pBex->Delete(p->aCsr[i].pCursor, nKey, zKey); - } - POPSTACK; - break; - } - - /* Opcode: KeyAsData P1 P2 * - ** - ** Turn the key-as-data mode for cursor P1 either on (if P2==1) or - ** off (if P2==0). In key-as-data mode, the OP_Field opcode pulls - ** data off of the key rather than the data. This is useful for - ** processing compound selects. - */ - case OP_KeyAsData: { - int i = pOp->p1; - if( VERIFY( i>=0 && inCursor && ) p->aCsr[i].pCursor!=0 ){ - p->aCsr[i].keyAsData = pOp->p2; - } - break; - } - - /* Opcode: Field P1 P2 * - ** - ** Interpret the data in the most recent fetch from cursor P1 - ** is a structure built using the MakeRecord instruction. - ** Push onto the stack the value of the P2-th field of that - ** structure. - ** - ** The value pushed is just a pointer to the data in the cursor. - ** The value will go away the next time a record is fetched from P1, - ** or when P1 is closed. Make a copy of the string (using - ** "Concat 1 0 0") if it needs to persist longer than that. - ** - ** If the KeyAsData opcode has previously executed on this cursor, - ** then the field might be extracted from the key rather than the - ** data. - ** - ** Viewed from a higher level, this instruction retrieves the - ** data from a single column in a particular row of an SQL table - ** file. Perhaps the name of this instruction should be - ** "Column" instead of "Field"... - */ - case OP_Field: { - int *pAddr; - int amt; - int i = pOp->p1; - int p2 = pOp->p2; - int tos = ++p->tos; - DbbeCursor *pCrsr; - char *z; - - VERIFY( if( NeedStack(p, tos) ) goto no_mem; ) - if( VERIFY( i>=0 && inCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ - if( p->aCsr[i].keyAsData ){ - amt = pBex->KeyLength(pCrsr); - if( amt<=sizeof(int)*(p2+1) ){ - aStack[tos].flags = STK_Null; - break; - } - pAddr = (int*)pBex->ReadKey(pCrsr, sizeof(int)*p2); - if( *pAddr==0 ){ - aStack[tos].flags = STK_Null; - break; - } - z = pBex->ReadKey(pCrsr, *pAddr); - }else{ - amt = pBex->DataLength(pCrsr); - if( amt<=sizeof(int)*(p2+1) ){ - aStack[tos].flags = STK_Null; - break; - } - pAddr = (int*)pBex->ReadData(pCrsr, sizeof(int)*p2); - if( *pAddr==0 ){ - aStack[tos].flags = STK_Null; - break; - } - z = pBex->ReadData(pCrsr, *pAddr); - } - zStack[tos] = z; - aStack[tos].n = strlen(z) + 1; - aStack[tos].flags = STK_Str; - } - break; - } - - /* Opcode: Key P1 * * - ** - ** Push onto the stack an integer which is the first 4 bytes of the - ** the key to the current entry in a sequential scan of the database - ** file P1. The sequential scan should have been started using the - ** Next opcode. - */ - case OP_Key: { - int i = pOp->p1; - int tos = ++p->tos; - DbbeCursor *pCrsr; - - VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) - if( VERIFY( i>=0 && inCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ - int v; - if( p->aCsr[i].keyIsValid ){ - v = p->aCsr[i].lastKey; - }else{ - memcpy(&v, pBex->ReadKey(pCrsr,0), sizeof(int)); - } - aStack[tos].i = v; - aStack[tos].flags = STK_Int; - } - break; - } - - /* Opcode: FullKey P1 * * - ** - ** Push a string onto the stack which is the full text key associated - ** with the last Next operation on file P1. Compare this with the - ** Key operator which pushs an integer key. - */ - case OP_FullKey: { - int i = pOp->p1; - int tos = ++p->tos; - DbbeCursor *pCrsr; - - VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) - VERIFY( if( !p->aCsr[i].keyAsData ) goto bad_instruction; ) - if( VERIFY( i>=0 && inCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ - char *z = pBex->ReadKey(pCrsr, 0); - zStack[tos] = z; - aStack[tos].flags = STK_Str; - aStack[tos].n = pBex->KeyLength(pCrsr); - } - break; - } - - /* Opcode: Rewind P1 * * - ** - ** The next use of the Key or Field or Next instruction for P1 - ** will refer to the first entry in the database file. - */ - case OP_Rewind: { - int i = pOp->p1; - if( VERIFY( i>=0 && inCursor && ) p->aCsr[i].pCursor!=0 ){ - pBex->Rewind(p->aCsr[i].pCursor); - } - break; - } - - /* Opcode: Next P1 P2 * - ** - ** Advance P1 to the next key/data pair in the file. Or, if there are no - ** more key/data pairs, rewind P1 and jump to location P2. - */ - case OP_Next: { - int i = pOp->p1; - if( VERIFY( i>=0 && inCursor && ) p->aCsr[i].pCursor!=0 ){ - if( pBex->NextKey(p->aCsr[i].pCursor)==0 ){ - pc = pOp->p2 - 1; - }else{ - p->nFetch++; - } - } - p->aCsr[i].keyIsValid = 0; - break; - } - - /* Opcode: BeginIdx P1 * * - ** - ** Begin searching an index for records with the key found on the - ** top of the stack. The stack is popped once. Subsequent calls - ** to NextIdx will push record numbers onto the stack until all - ** records with the same key have been returned. - */ - case OP_BeginIdx: { - int i = pOp->p1; - int tos = p->tos; - VERIFY( if( tos<0 ) goto not_enough_stack; ) - if( i>=0 && inCursor && p->aCsr[i].pCursor ){ - if( Stringify(p, tos) ) goto no_mem; - pBex->BeginIndex(p->aCsr[i].pCursor, aStack[tos].n, zStack[tos]); - p->aCsr[i].keyIsValid = 0; - } - POPSTACK; - break; - } - - /* Opcode: NextIdx P1 P2 * - ** - ** The P1 cursor points to an SQL index for which a BeginIdx operation - ** has been issued. This operation retrieves the next record number and - ** pushes that record number onto the stack. Or, if there are no more - ** record numbers for the given key, this opcode pushes nothing onto the - ** stack but instead jumps to instruction P2. - */ - case OP_NextIdx: { - int i = pOp->p1; - int tos = ++p->tos; - DbbeCursor *pCrsr; - - VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) - zStack[tos] = 0; - if( VERIFY( i>=0 && inCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ - int recno = pBex->NextIndex(pCrsr); - if( recno!=0 ){ - p->aCsr[i].lastKey = aStack[tos].i = recno; - p->aCsr[i].keyIsValid = 1; - aStack[tos].flags = STK_Int; - }else{ - pc = pOp->p2 - 1; - POPSTACK; - } - } - break; - } - - /* Opcode: PutIdx P1 * * - ** - ** The top of the stack hold an SQL index key (probably made using the - ** MakeKey instruction) and next on stack holds an integer which - ** the record number for an SQL table entry. This opcode makes an entry - ** in the index table P1 that associates the key with the record number. - ** But the record number and the key are popped from the stack. - */ - case OP_PutIdx: { - int i = pOp->p1; - int tos = p->tos; - int nos = tos - 1; - DbbeCursor *pCrsr; - VERIFY( if( nos<0 ) goto not_enough_stack; ) - if( VERIFY( i>=0 && inCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ - Integerify(p, nos); - if( Stringify(p, tos) ) goto no_mem; - pBex->PutIndex(pCrsr, aStack[tos].n, zStack[tos], aStack[nos].i); - } - POPSTACK; - POPSTACK; - break; - } - - /* Opcode: DeleteIdx P1 * * - ** - ** The top of the stack is a key and next on stack is integer - ** which is a record number for an SQL table. The operation removes - ** any entry to the index table P1 that associates the key with the - ** record number. - */ - case OP_DeleteIdx: { - int i = pOp->p1; - int tos = p->tos; - int nos = tos - 1; - DbbeCursor *pCrsr; - VERIFY( if( nos<0 ) goto not_enough_stack; ) - if( VERIFY( i>=0 && inCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ - Integerify(p, nos); - if( Stringify(p, tos) ) goto no_mem; - pBex->DeleteIndex(pCrsr, aStack[tos].n, zStack[tos], aStack[nos].i); - } - POPSTACK; - POPSTACK; - break; - } - - /* Opcode: Destroy * * P3 - ** - ** Drop the disk file whose name is P3. All key/data pairs in - ** the file are deleted and the file itself is removed - ** from the disk. - */ - case OP_Destroy: { - pBex->DropTable(pBe, pOp->p3); - break; - } - - /* Opcode: Reorganize * * P3 - ** - ** Compress, optimize, and tidy up the GDBM file named by P3. - */ - case OP_Reorganize: { - pBex->ReorganizeTable(pBe, pOp->p3); - break; - } - - /* Opcode: ListOpen P1 * * - ** - ** Open a "List" structure used for temporary storage of integer - ** table keys. P1 - ** will server as a handle to this list for future - ** interactions. If another list with the P1 handle is - ** already opened, the prior list is closed and a new one opened - ** in its place. - */ - case OP_ListOpen: { - int i = pOp->p1; - VERIFY( if( i<0 ) goto bad_instruction; ) - if( i>=p->nList ){ - int j; - p->apList = sqliteRealloc( p->apList, (i+1)*sizeof(Keylist*) ); - if( p->apList==0 ){ p->nList = 0; goto no_mem; } - for(j=p->nList; j<=i; j++) p->apList[j] = 0; - p->nList = i+1; - }else if( p->apList[i] ){ - KeylistFree(p->apList[i]); - p->apList[i] = 0; - } - break; - } - - /* Opcode: ListWrite P1 * * - ** - ** Write the integer on the top of the stack - ** into the temporary storage list P1. - */ - case OP_ListWrite: { - int i = pOp->p1; - Keylist *pKeylist; - VERIFY( if( i<0 || i>=p->nList ) goto bad_instruction; ) - VERIFY( if( p->tos<0 ) goto not_enough_stack; ) - pKeylist = p->apList[i]; - if( pKeylist==0 || pKeylist->nUsed>=pKeylist->nKey ){ - pKeylist = sqliteMalloc( sizeof(Keylist)+999*sizeof(int) ); - if( pKeylist==0 ) goto no_mem; - pKeylist->nKey = 1000; - pKeylist->nRead = 0; - pKeylist->nUsed = 0; - pKeylist->pNext = p->apList[i]; - p->apList[i] = pKeylist; - } - Integerify(p, p->tos); - pKeylist->aKey[pKeylist->nUsed++] = aStack[p->tos].i; - POPSTACK; - break; - } - - /* Opcode: ListRewind P1 * * - ** - ** Rewind the temporary buffer P1 back to the beginning. - */ - case OP_ListRewind: { - int i = pOp->p1; - VERIFY( if( i<0 ) goto bad_instruction; ) - /* This is now a no-op */ - break; - } - - /* Opcode: ListRead P1 P2 * - ** - ** Attempt to read an integer from temporary storage buffer P1 - ** and push it onto the stack. If the storage buffer is empty, - ** push nothing but instead jump to P2. - */ - case OP_ListRead: { - int i = pOp->p1; - Keylist *pKeylist; - VERIFY(if( i<0 || i>=p->nList ) goto bad_instruction;) - pKeylist = p->apList[i]; - if( pKeylist!=0 ){ - VERIFY( - if( pKeylist->nRead<0 - || pKeylist->nRead>=pKeylist->nUsed - || pKeylist->nRead>=pKeylist->nKey ) goto bad_instruction; - ) - p->tos++; - if( NeedStack(p, p->tos) ) goto no_mem; - aStack[p->tos].i = pKeylist->aKey[pKeylist->nRead++]; - aStack[p->tos].flags = STK_Int; - zStack[p->tos] = 0; - if( pKeylist->nRead>=pKeylist->nUsed ){ - p->apList[i] = pKeylist->pNext; - sqliteFree(pKeylist); - } - }else{ - pc = pOp->p2 - 1; - } - break; - } - - /* Opcode: ListClose P1 * * - ** - ** Close the temporary storage buffer and discard its contents. - */ - case OP_ListClose: { - int i = pOp->p1; - VERIFY( if( i<0 ) goto bad_instruction; ) - VERIFY( if( i>=p->nList ) goto bad_instruction; ) - KeylistFree(p->apList[i]); - p->apList[i] = 0; - break; - } - - /* Opcode: SortOpen P1 * * - ** - ** Create a new sorter with index P1 - */ - case OP_SortOpen: { - int i = pOp->p1; - VERIFY( if( i<0 ) goto bad_instruction; ) - if( i>=p->nSort ){ - int j; - p->apSort = sqliteRealloc( p->apSort, (i+1)*sizeof(Sorter*) ); - if( p->apSort==0 ){ p->nSort = 0; goto no_mem; } - for(j=p->nSort; j<=i; j++) p->apSort[j] = 0; - p->nSort = i+1; - } - break; - } - - /* Opcode: SortPut P1 * * - ** - ** The TOS is the key and the NOS is the data. Pop both from the stack - ** and put them on the sorter. - */ - case OP_SortPut: { - int i = pOp->p1; - int tos = p->tos; - int nos = tos - 1; - Sorter *pSorter; - VERIFY( if( i<0 || i>=p->nSort ) goto bad_instruction; ) - VERIFY( if( tos<1 ) goto not_enough_stack; ) - if( Stringify(p, tos) || Stringify(p, nos) ) goto no_mem; - pSorter = sqliteMalloc( sizeof(Sorter) ); - if( pSorter==0 ) goto no_mem; - pSorter->pNext = p->apSort[i]; - p->apSort[i] = pSorter; - pSorter->nKey = aStack[tos].n; - pSorter->zKey = zStack[tos]; - pSorter->nData = aStack[nos].n; - pSorter->pData = zStack[nos]; - aStack[tos].flags = 0; - aStack[nos].flags = 0; - zStack[tos] = 0; - zStack[nos] = 0; - p->tos -= 2; - break; - } - - /* Opcode: SortMakeRec P1 * * - ** - ** The top P1 elements are the arguments to a callback. Form these - ** elements into a single data entry that can be stored on a sorter - ** using SortPut and later fed to a callback using SortCallback. - */ - case OP_SortMakeRec: { - char *z; - char **azArg; - int nByte; - int nField; - int i, j; - - nField = pOp->p1; - VERIFY( if( p->tos+1tos-nField+1; i<=p->tos; i++){ - if( (aStack[i].flags & STK_Null)==0 ){ - if( Stringify(p, i) ) goto no_mem; - nByte += aStack[i].n; - } - } - nByte += sizeof(char*)*(nField+1); - azArg = sqliteMalloc( nByte ); - if( azArg==0 ) goto no_mem; - z = (char*)&azArg[nField+1]; - for(j=0, i=p->tos-nField+1; i<=p->tos; i++, j++){ - if( aStack[i].flags & STK_Null ){ - azArg[j] = 0; - }else{ - azArg[j] = z; - strcpy(z, zStack[i]); - z += aStack[i].n; - } - } - PopStack(p, nField); - VERIFY( NeedStack(p, p->tos+1); ) - p->tos++; - aStack[p->tos].n = nByte; - zStack[p->tos] = (char*)azArg; - aStack[p->tos].flags = STK_Str|STK_Dyn; - break; - } - - /* Opcode: SortMakeKey P1 * P3 - ** - ** Convert the top few entries of the stack into a sort key. The - ** number of stack entries consumed is the number of characters in - ** the string P3. One character from P3 is prepended to each entry. - ** The first character of P3 is prepended to the element lowest in - ** the stack and the last character of P3 is appended to the top of - ** the stack. All stack entries are separated by a \000 character - ** in the result. The whole key is terminated by two \000 characters - ** in a row. - ** - ** See also the MakeKey opcode. - */ - case OP_SortMakeKey: { - char *zNewKey; - int nByte; - int nField; - int i, j, k; - - nField = strlen(pOp->p3); - VERIFY( if( p->tos+1tos-nField+1; i<=p->tos; i++){ - if( Stringify(p, i) ) goto no_mem; - nByte += aStack[i].n+2; - } - zNewKey = sqliteMalloc( nByte ); - if( zNewKey==0 ) goto no_mem; - j = 0; - k = 0; - for(i=p->tos-nField+1; i<=p->tos; i++){ - zNewKey[j++] = pOp->p3[k++]; - memcpy(&zNewKey[j], zStack[i], aStack[i].n-1); - j += aStack[i].n-1; - zNewKey[j++] = 0; - } - zNewKey[j] = 0; - PopStack(p, nField); - VERIFY( NeedStack(p, p->tos+1); ) - p->tos++; - aStack[p->tos].n = nByte; - aStack[p->tos].flags = STK_Str|STK_Dyn; - zStack[p->tos] = zNewKey; - break; - } - - /* Opcode: Sort P1 * * - ** - ** Sort all elements on the given sorter. The algorithm is a - ** mergesort. - */ - case OP_Sort: { - int j; - j = pOp->p1; - VERIFY( if( j<0 ) goto bad_instruction; ) - if( jnSort ){ - int i; - Sorter *pElem; - Sorter *apSorter[NSORT]; - for(i=0; iapSort[j] ){ - pElem = p->apSort[j]; - p->apSort[j] = pElem->pNext; - pElem->pNext = 0; - for(i=0; i=NSORT-1 ){ - apSorter[NSORT-1] = Merge(apSorter[NSORT-1],pElem); - } - } - pElem = 0; - for(i=0; iapSort[j] = pElem; - } - break; - } - - /* Opcode: SortNext P1 P2 * - ** - ** Push the data for the topmost element in the given sorter onto the - ** stack, then remove the element from the sorter. - */ - case OP_SortNext: { - int i = pOp->p1; - VERIFY( if( i<0 ) goto bad_instruction; ) - if( VERIFY( inSort && ) p->apSort[i]!=0 ){ - Sorter *pSorter = p->apSort[i]; - p->apSort[i] = pSorter->pNext; - p->tos++; - VERIFY( NeedStack(p, p->tos); ) - zStack[p->tos] = pSorter->pData; - aStack[p->tos].n = pSorter->nData; - aStack[p->tos].flags = STK_Str|STK_Dyn; - sqliteFree(pSorter->zKey); - sqliteFree(pSorter); - }else{ - pc = pOp->p2 - 1; - } - break; - } - - /* Opcode: SortKey P1 * * - ** - ** Push the key for the topmost element of the sorter onto the stack. - ** But don't change the sorter an any other way. - */ - case OP_SortKey: { - int i = pOp->p1; - VERIFY( if( i<0 ) goto bad_instruction; ) - if( inSort && p->apSort[i]!=0 ){ - Sorter *pSorter = p->apSort[i]; - p->tos++; - VERIFY( NeedStack(p, p->tos); ) - sqliteSetString(&zStack[p->tos], pSorter->zKey, 0); - aStack[p->tos].n = pSorter->nKey; - aStack[p->tos].flags = STK_Str|STK_Dyn; - } - break; - } - - /* Opcode: SortCallback P1 P2 * - ** - ** The top of the stack contains a callback record built using - ** the SortMakeRec operation with the same P1 value as this - ** instruction. Pop this record from the stack and invoke the - ** callback on it. - */ - case OP_SortCallback: { - int i = p->tos; - VERIFY( if( i<0 ) goto not_enough_stack; ) - if( xCallback!=0 ){ - if( xCallback(pArg, pOp->p1, (char**)zStack[i], p->azColName) ){ - rc = SQLITE_ABORT; - } - } - POPSTACK; - break; - } - - /* Opcode: SortClose P1 * * - ** - ** Close the given sorter and remove all its elements. - */ - case OP_SortClose: { - Sorter *pSorter; - int i = pOp->p1; - VERIFY( if( i<0 ) goto bad_instruction; ) - if( inSort ){ - while( (pSorter = p->apSort[i])!=0 ){ - p->apSort[i] = pSorter->pNext; - sqliteFree(pSorter->zKey); - sqliteFree(pSorter->pData); - sqliteFree(pSorter); - } - } - break; - } - - /* Opcode: FileOpen * * P3 - ** - ** Open the file named by P3 for reading using the FileRead opcode. - ** If P3 is "stdin" then open standard input for reading. - */ - case OP_FileOpen: { - VERIFY( if( pOp->p3==0 ) goto bad_instruction; ) - if( p->pFile ){ - if( p->pFile!=stdin ) fclose(p->pFile); - p->pFile = 0; - } - if( sqliteStrICmp(pOp->p3,"stdin")==0 ){ - p->pFile = stdin; - }else{ - p->pFile = fopen(pOp->p3, "r"); - } - if( p->pFile==0 ){ - sqliteSetString(pzErrMsg,"unable to open file: ", pOp->p3, 0); - rc = SQLITE_ERROR; - goto cleanup; - } - break; - } - - /* Opcode: FileClose * * * - ** - ** Close a file previously opened using FileOpen. This is a no-op - ** if there is no prior FileOpen call. - */ - case OP_FileClose: { - if( p->pFile ){ - if( p->pFile!=stdin ) fclose(p->pFile); - p->pFile = 0; - } - if( p->azField ){ - sqliteFree(p->azField); - p->azField = 0; - } - p->nField = 0; - if( p->zLine ){ - sqliteFree(p->zLine); - p->zLine = 0; - } - p->nLineAlloc = 0; - break; - } - - /* Opcode: FileRead P1 P2 P3 - ** - ** Read a single line of input from the open file (the file opened using - ** FileOpen). If we reach end-of-file, jump immediately to P2. If - ** we are able to get another line, split the line apart using P3 as - ** a delimiter. There should be P1 fields. If the input line contains - ** more than P1 fields, ignore the excess. If the input line contains - ** fewer than P1 fields, assume the remaining fields contain an - ** empty string. - */ - case OP_FileRead: { - int n, eol, nField, i, c, nDelim; - char *zDelim, *z; - if( p->pFile==0 ) goto fileread_jump; - nField = pOp->p1; - if( nField<=0 ) goto fileread_jump; - if( nField!=p->nField || p->azField==0 ){ - p->azField = sqliteRealloc(p->azField, sizeof(char*)*nField+1); - if( p->azField==0 ){ - p->nField = 0; - goto fileread_jump; - } - p->nField = nField; - } - n = 0; - eol = 0; - while( eol==0 ){ - if( p->zLine==0 || n+200>p->nLineAlloc ){ - p->nLineAlloc = p->nLineAlloc*2 + 300; - p->zLine = sqliteRealloc(p->zLine, p->nLineAlloc); - if( p->zLine==0 ){ - p->nLineAlloc = 0; - goto fileread_jump; - } - } - if( fgets(&p->zLine[n], p->nLineAlloc-n, p->pFile)==0 ){ - eol = 1; - p->zLine[n] = 0; - }else{ - while( p->zLine[n] ){ n++; } - if( n>0 && p->zLine[n-1]=='\n' ){ - n--; - p->zLine[n] = 0; - eol = 1; - } - } - } - if( n==0 ) goto fileread_jump; - z = p->zLine; - if( z[0]=='\\' && z[1]=='.' && z[2]==0 ){ - goto fileread_jump; - } - zDelim = pOp->p3; - if( zDelim==0 ) zDelim = "\t"; - c = zDelim[0]; - nDelim = strlen(zDelim); - p->azField[0] = z; - for(i=1; *z!=0 && i<=nField; i++){ - int from, to; - from = to = 0; - while( z[from] ){ - if( z[from]=='\\' && z[from+1]!=0 ){ - z[to++] = z[from+1]; - from += 2; - continue; - } - if( z[from]==c && strncmp(&z[from],zDelim,nDelim)==0 ) break; - z[to++] = z[from++]; - } - if( z[from] ){ - z[to] = 0; - z += from + nDelim; - if( iazField[i] = z; - }else{ - z[to] = 0; - z = ""; - } - } - while( iazField[i++] = ""; - } - break; - - /* If we reach end-of-file, or if anything goes wrong, jump here. - ** This code will cause a jump to P2 */ - fileread_jump: - pc = pOp->p2 - 1; - break; - } - - /* Opcode: FileField P1 * * - ** - ** Push onto the stack the P1-th field of the most recently read line - ** from the input file. - */ - case OP_FileField: { - int i = pOp->p1; - char *z; - VERIFY( if( NeedStack(p, p->tos+1) ) goto no_mem; ) - if( VERIFY( i>=0 && inField && ) p->azField ){ - z = p->azField[i]; - }else{ - z = 0; - } - if( z==0 ) z = ""; - p->tos++; - aStack[p->tos].n = strlen(z) + 1; - zStack[p->tos] = z; - aStack[p->tos].flags = STK_Str; - break; - } - - /* Opcode: MemStore P1 * * - ** - ** Pop a single value of the stack and store that value into memory - ** location P1. P1 should be a small integer since space is allocated - ** for all memory locations between 0 and P1 inclusive. - */ - case OP_MemStore: { - int i = pOp->p1; - int tos = p->tos; - Mem *pMem; - char *zOld; - VERIFY( if( tos<0 ) goto not_enough_stack; ) - if( i>=p->nMem ){ - int nOld = p->nMem; - p->nMem = i + 5; - p->aMem = sqliteRealloc(p->aMem, p->nMem*sizeof(p->aMem[0])); - if( p->aMem==0 ) goto no_mem; - if( nOldnMem ){ - memset(&p->aMem[nOld], 0, sizeof(p->aMem[0])*(p->nMem-nOld)); - } - } - pMem = &p->aMem[i]; - if( pMem->s.flags & STK_Dyn ){ - zOld = pMem->z; - }else{ - zOld = 0; - } - pMem->s = aStack[tos]; - if( pMem->s.flags & STK_Str ){ - pMem->z = sqliteStrNDup(zStack[tos], pMem->s.n); - pMem->s.flags |= STK_Dyn; - } - if( zOld ) sqliteFree(zOld); - POPSTACK; - break; - } - - /* Opcode: MemLoad P1 * * - ** - ** Push a copy of the value in memory location P1 onto the stack. - */ - case OP_MemLoad: { - int tos = ++p->tos; - int i = pOp->p1; - VERIFY( if( NeedStack(p, tos) ) goto no_mem; ) - if( i<0 || i>=p->nMem ){ - aStack[tos].flags = STK_Null; - zStack[tos] = 0; - }else{ - aStack[tos] = p->aMem[i].s; - if( aStack[tos].flags & STK_Str ){ - char *z = sqliteMalloc(aStack[tos].n); - if( z==0 ) goto no_mem; - memcpy(z, p->aMem[i].z, aStack[tos].n); - zStack[tos] = z; - aStack[tos].flags |= STK_Dyn; - } - } - break; - } - - /* Opcode: AggReset * P2 * - ** - ** Reset the aggregator so that it no longer contains any data. - ** Future aggregator elements will contain P2 values each. - */ - case OP_AggReset: { - AggReset(&p->agg); - p->agg.nMem = pOp->p2; - break; - } - - /* Opcode: AggFocus * P2 * - ** - ** Pop the top of the stack and use that as an aggregator key. If - ** an aggregator with that same key already exists, then make the - ** aggregator the current aggregator and jump to P2. If no aggregator - ** with the given key exists, create one and make it current but - ** do not jump. - ** - ** The order of aggregator opcodes is important. The order is: - ** AggReset AggFocus AggNext. In other words, you must execute - ** AggReset first, then zero or more AggFocus operations, then - ** zero or more AggNext operations. You must not execute an AggFocus - ** in between an AggNext and an AggReset. - */ - case OP_AggFocus: { - int tos = p->tos; - AggElem *pElem; - char *zKey; - int nKey; - - VERIFY( if( tos<0 ) goto not_enough_stack; ) - if( Stringify(p, tos) ) goto no_mem; - zKey = zStack[tos]; - nKey = aStack[tos].n; - if( p->agg.nHash<=0 ){ - pElem = 0; - }else{ - int h = sqliteHashNoCase(zKey, nKey-1) % p->agg.nHash; - for(pElem=p->agg.apHash[h]; pElem; pElem=pElem->pHash){ - if( strcmp(pElem->zKey, zKey)==0 ) break; - } - } - if( pElem ){ - p->agg.pCurrent = pElem; - pc = pOp->p2 - 1; - }else{ - AggInsert(&p->agg, zKey); - if( sqlite_malloc_failed ) goto no_mem; - } - POPSTACK; - break; - } - - /* Opcode: AggIncr P1 P2 * - ** - ** Increase the integer value in the P2-th field of the aggregate - ** element current in focus by an amount P1. - */ - case OP_AggIncr: { - AggElem *pFocus = AggInFocus(p->agg); - int i = pOp->p2; - if( pFocus==0 ) goto no_mem; - if( i>=0 && iagg.nMem ){ - Mem *pMem = &pFocus->aMem[i]; - if( pMem->s.flags!=STK_Int ){ - if( pMem->s.flags & STK_Int ){ - /* Do nothing */ - }else if( pMem->s.flags & STK_Real ){ - pMem->s.i = pMem->s.r; - }else if( pMem->s.flags & STK_Str ){ - pMem->s.i = atoi(pMem->z); - }else{ - pMem->s.i = 0; - } - if( pMem->s.flags & STK_Dyn ) sqliteFree(pMem->z); - pMem->z = 0; - pMem->s.flags = STK_Int; - } - pMem->s.i += pOp->p1; - } - break; - } - - /* Opcode: AggSet * P2 * - ** - ** Move the top of the stack into the P2-th field of the current - ** aggregate. String values are duplicated into new memory. - */ - case OP_AggSet: { - AggElem *pFocus = AggInFocus(p->agg); - int i = pOp->p2; - int tos = p->tos; - VERIFY( if( tos<0 ) goto not_enough_stack; ) - if( pFocus==0 ) goto no_mem; - if( VERIFY( i>=0 && ) iagg.nMem ){ - Mem *pMem = &pFocus->aMem[i]; - char *zOld; - if( pMem->s.flags & STK_Dyn ){ - zOld = pMem->z; - }else{ - zOld = 0; - } - pMem->s = aStack[tos]; - if( pMem->s.flags & STK_Str ){ - pMem->z = sqliteMalloc( aStack[tos].n ); - if( pMem->z==0 ) goto no_mem; - memcpy(pMem->z, zStack[tos], pMem->s.n); - pMem->s.flags |= STK_Str|STK_Dyn; - } - if( zOld ) sqliteFree(zOld); - } - POPSTACK; - break; - } - - /* Opcode: AggGet * P2 * - ** - ** Push a new entry onto the stack which is a copy of the P2-th field - ** of the current aggregate. Strings are not duplicated so - ** string values will be ephemeral. - */ - case OP_AggGet: { - AggElem *pFocus = AggInFocus(p->agg); - int i = pOp->p2; - int tos = ++p->tos; - VERIFY( if( NeedStack(p, tos) ) goto no_mem; ) - if( pFocus==0 ) goto no_mem; - if( VERIFY( i>=0 && ) iagg.nMem ){ - Mem *pMem = &pFocus->aMem[i]; - aStack[tos] = pMem->s; - zStack[tos] = pMem->z; - aStack[tos].flags &= ~STK_Dyn; - } - break; - } - - /* Opcode: AggNext * P2 * - ** - ** Make the next aggregate value the current aggregate. The prior - ** aggregate is deleted. If all aggregate values have been consumed, - ** jump to P2. - ** - ** The order of aggregator opcodes is important. The order is: - ** AggReset AggFocus AggNext. In other words, you must execute - ** AggReset first, then zero or more AggFocus operations, then - ** zero or more AggNext operations. You must not execute an AggFocus - ** in between an AggNext and an AggReset. - */ - case OP_AggNext: { - if( p->agg.nHash ){ - p->agg.nHash = 0; - sqliteFree(p->agg.apHash); - p->agg.apHash = 0; - p->agg.pCurrent = p->agg.pFirst; - }else if( p->agg.pCurrent==p->agg.pFirst && p->agg.pCurrent!=0 ){ - int i; - AggElem *pElem = p->agg.pCurrent; - for(i=0; iagg.nMem; i++){ - if( pElem->aMem[i].s.flags & STK_Dyn ){ - sqliteFree(pElem->aMem[i].z); - } - } - p->agg.pCurrent = p->agg.pFirst = pElem->pNext; - sqliteFree(pElem); - p->agg.nElem--; - } - if( p->agg.pCurrent==0 ){ - pc = pOp->p2-1; - } - break; - } - - /* Opcode: SetClear P1 * * - ** - ** Remove all elements from the P1-th Set. - */ - case OP_SetClear: { - int i = pOp->p1; - if( i>=0 && inSet ){ - SetClear(&p->aSet[i]); - } - break; - } - - /* Opcode: SetInsert P1 * P3 - ** - ** If Set P1 does not exist then create it. Then insert value - ** P3 into that set. If P3 is NULL, then insert the top of the - ** stack into the set. - */ - case OP_SetInsert: { - int i = pOp->p1; - if( p->nSet<=i ){ - p->aSet = sqliteRealloc(p->aSet, (i+1)*sizeof(p->aSet[0]) ); - if( p->aSet==0 ) goto no_mem; - memset(&p->aSet[p->nSet], 0, sizeof(p->aSet[0])*(i+1 - p->nSet)); - p->nSet = i+1; - } - if( pOp->p3 ){ - SetInsert(&p->aSet[i], pOp->p3); - }else{ - int tos = p->tos; - if( tos<0 ) goto not_enough_stack; - if( Stringify(p, tos) ) goto no_mem; - SetInsert(&p->aSet[i], zStack[tos]); - POPSTACK; - } - if( sqlite_malloc_failed ) goto no_mem; - break; - } - - /* Opcode: SetFound P1 P2 * - ** - ** Pop the stack once and compare the value popped off with the - ** contents of set P1. If the element popped exists in set P1, - ** then jump to P2. Otherwise fall through. - */ - case OP_SetFound: { - int i = pOp->p1; - int tos = p->tos; - VERIFY( if( tos<0 ) goto not_enough_stack; ) - if( Stringify(p, tos) ) goto no_mem; - if( VERIFY( i>=0 && inSet &&) SetTest(&p->aSet[i], zStack[tos])){ - pc = pOp->p2 - 1; - } - POPSTACK; - break; - } - - /* Opcode: SetNotFound P1 P2 * - ** - ** Pop the stack once and compare the value popped off with the - ** contents of set P1. If the element popped does not exists in - ** set P1, then jump to P2. Otherwise fall through. - */ - case OP_SetNotFound: { - int i = pOp->p1; - int tos = p->tos; - VERIFY( if( tos<0 ) goto not_enough_stack; ) - if( Stringify(p, tos) ) goto no_mem; - if(VERIFY( i>=0 && inSet &&) !SetTest(&p->aSet[i], zStack[tos])){ - pc = pOp->p2 - 1; - } - POPSTACK; - break; - } - - /* Opcode: Strlen * * * - ** - ** Interpret the top of the stack as a string. Replace the top of - ** stack with an integer which is the length of the string. - */ - case OP_Strlen: { - int tos = p->tos; - int len; - VERIFY( if( tos<0 ) goto not_enough_stack; ) - if( Stringify(p, tos) ) goto no_mem; -#ifdef SQLITE_UTF8 - { - char *z = zStack[tos]; - for(len=0; *z; z++){ if( (0xc0&*z)!=0x80 ) len++; } - } -#else - len = aStack[tos].n-1; -#endif - POPSTACK; - p->tos++; - aStack[tos].i = len; - aStack[tos].flags = STK_Int; - break; - } - - /* Opcode: Substr P1 P2 * - ** - ** This operation pops between 1 and 3 elements from the stack and - ** pushes back a single element. The bottom-most element popped from - ** the stack is a string and the element pushed back is also a string. - ** The other two elements popped are integers. The integers are taken - ** from the stack only if P1 and/or P2 are 0. When P1 or P2 are - ** not zero, the value of the operand is used rather than the integer - ** from the stack. In the sequel, we will use P1 and P2 to describe - ** the two integers, even if those integers are really taken from the - ** stack. - ** - ** The string pushed back onto the stack is a substring of the string - ** that was popped. There are P2 characters in the substring. The - ** first character of the substring is the P1-th character of the - ** original string where the left-most character is 1 (not 0). If P1 - ** is negative, then counting begins at the right instead of at the - ** left. - */ - case OP_Substr: { - int cnt; - int start; - int n; - char *z; - - if( pOp->p2==0 ){ - VERIFY( if( p->tos<0 ) goto not_enough_stack; ) - Integerify(p, p->tos); - cnt = aStack[p->tos].i; - POPSTACK; - }else{ - cnt = pOp->p2; - } - if( pOp->p1==0 ){ - VERIFY( if( p->tos<0 ) goto not_enough_stack; ) - Integerify(p, p->tos); - start = aStack[p->tos].i - 1; - POPSTACK; - }else{ - start = pOp->p1 - 1; - } - VERIFY( if( p->tos<0 ) goto not_enough_stack; ) - if( Stringify(p, p->tos) ) goto no_mem; - - /* "n" will be the number of characters in the input string. - ** For iso8859, the number of characters is the number of bytes. - ** Buf for UTF-8, some characters can use multiple bytes and the - ** situation is more complex. - */ -#ifdef SQLITE_UTF8 - z = zStack[p->tos]; - for(n=0; *z; z++){ if( (0xc0&*z)!=0x80 ) n++; } -#else - n = aStack[p->tos].n - 1; -#endif - if( start<0 ){ - start += n + 1; - if( start<0 ){ - cnt += start; - start = 0; - } - } - if( start>n ){ - start = n; - } - if( cnt<0 ) cnt = 0; - if( cnt > n ){ - cnt = n; - } - - /* At this point, "start" is the index of the first character to - ** extract and "cnt" is the number of characters to extract. We - ** need to convert units on these variable from characters into - ** bytes. For iso8859, the conversion is a no-op, but for UTF-8 - ** we have to do a little work. - */ -#ifdef SQLITE_UTF8 - { - int c_start = start; - int c_cnt = cnt; - int i; - z = zStack[p->tos]; - for(start=i=0; itos][start], cnt); - z[cnt] = 0; - POPSTACK; - p->tos++; - zStack[p->tos] = z; - aStack[p->tos].n = cnt + 1; - aStack[p->tos].flags = STK_Str|STK_Dyn; - break; - } - - /* An other opcode is illegal... - */ +/***************************************************************************** +** What follows is a massive switch statement where each case implements a +** separate instruction in the virtual machine. If we follow the usual +** indentation conventions, each case should be indented by 6 spaces. But +** that is a lot of wasted space on the left margin. So the code within +** the switch statement will break with convention and be flush-left. Another +** big comment (similar to this one) will mark the point in the code where +** we transition back to normal indentation. +*****************************************************************************/ + +/* Opcode: Goto P2 * * +** +** An unconditional jump to address P2. +** The next instruction executed will be +** the one at index P2 from the beginning of +** the program. +*/ +case OP_Goto: { + pc = pOp->p2 - 1; + break; +} + +/* Opcode: Halt * * * +** +** Exit immediately. All open DBs, Lists, Sorts, etc are closed +** automatically. +*/ +case OP_Halt: { + pc = p->nOp-1; + break; +} + +/* Opcode: Integer P1 * * +** +** The integer value P1 is pushed onto the stack. +*/ +case OP_Integer: { + int i = ++p->tos; + VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) + aStack[i].i = pOp->p1; + aStack[i].flags = STK_Int; + break; +} + +/* Opcode: String * * P3 +** +** The string value P3 is pushed onto the stack. +*/ +case OP_String: { + int i = ++p->tos; + char *z; + VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) + z = pOp->p3; + if( z==0 ) z = ""; + zStack[i] = z; + aStack[i].n = strlen(z) + 1; + aStack[i].flags = STK_Str; + break; +} + +/* Opcode: Null * * * +** +** Push a NULL value onto the stack. +*/ +case OP_Null: { + int i = ++p->tos; + VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) + zStack[i] = 0; + aStack[i].flags = STK_Null; + break; +} + +/* Opcode: Pop P1 * * +** +** P1 elements are popped off of the top of stack and discarded. +*/ +case OP_Pop: { + PopStack(p, pOp->p1); + break; +} + +/* Opcode: Dup P1 * * +** +** A copy of the P1-th element of the stack +** is made and pushed onto the top of the stack. +** The top of the stack is element 0. So the +** instruction "Dup 0 0 0" will make a copy of the +** top of the stack. +*/ +case OP_Dup: { + int i = p->tos - pOp->p1; + int j = ++p->tos; + VERIFY( if( i<0 ) goto not_enough_stack; ) + VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) + aStack[j] = aStack[i]; + if( aStack[i].flags & STK_Dyn ){ + zStack[j] = sqliteMalloc( aStack[j].n ); + if( zStack[j]==0 ) goto no_mem; + memcpy(zStack[j], zStack[i], aStack[j].n); + }else{ + zStack[j] = zStack[i]; + } + break; +} + +/* Opcode: Pull P1 * * +** +** The P1-th element is removed from its current location on +** the stack and pushed back on top of the stack. The +** top of the stack is element 0, so "Pull 0 0 0" is +** a no-op. +*/ +case OP_Pull: { + int from = p->tos - pOp->p1; + int to = p->tos; + int i; + Stack ts; + char *tz; + VERIFY( if( from<0 ) goto not_enough_stack; ) + ts = aStack[from]; + tz = zStack[from]; + for(i=from; iazColName = sqliteRealloc(p->azColName, (pOp->p1+1)*sizeof(char*)); + if( p->azColName==0 ) goto no_mem; + p->azColName[pOp->p1] = 0; + break; +} + +/* Opcode: ColumnName P1 * P3 +** +** P3 becomes the P1-th column name (first is 0). An array of pointers +** to all column names is passed as the 4th parameter to the callback. +** The ColumnCount opcode must be executed first to allocate space to +** hold the column names. Failure to do this will likely result in +** a coredump. +*/ +case OP_ColumnName: { + p->azColName[pOp->p1] = pOp->p3 ? pOp->p3 : ""; + break; +} + +/* Opcode: Callback P1 * * +** +** Pop P1 values off the stack and form them into an array. Then +** invoke the callback function using the newly formed array as the +** 3rd parameter. +*/ +case OP_Callback: { + int i = p->tos - pOp->p1 + 1; + int j; + VERIFY( if( i<0 ) goto not_enough_stack; ) + VERIFY( if( NeedStack(p, p->tos+2) ) goto no_mem; ) + for(j=i; j<=p->tos; j++){ + if( (aStack[j].flags & STK_Null)==0 ){ + if( Stringify(p, j) ) goto no_mem; + } + } + zStack[p->tos+1] = 0; + if( xCallback!=0 ){ + if( xCallback(pArg, pOp->p1, &zStack[i], p->azColName)!=0 ){ + rc = SQLITE_ABORT; + } + } + PopStack(p, pOp->p1); + break; +} + +/* Opcode: Concat P1 P2 P3 +** +** Look at the first P1 elements of the stack. Append them all +** together with the lowest element first. Use P3 as a separator. +** Put the result on the top of the stack. The original P1 elements +** are popped from the stack if P2==0 and retained if P2==1. +** +** If P3 is NULL, then use no separator. When P1==1, this routine +** makes a copy of the top stack element into memory obtained +** from sqliteMalloc(). +*/ +case OP_Concat: { + char *zNew; + int nByte; + int nField; + int i, j; + char *zSep; + int nSep; + + nField = pOp->p1; + zSep = pOp->p3; + if( zSep==0 ) zSep = ""; + nSep = strlen(zSep); + VERIFY( if( p->tos+1tos-nField+1; i<=p->tos; i++){ + if( aStack[i].flags & STK_Null ){ + nByte += nSep; + }else{ + if( Stringify(p, i) ) goto no_mem; + nByte += aStack[i].n - 1 + nSep; + } + } + zNew = sqliteMalloc( nByte ); + if( zNew==0 ) goto no_mem; + j = 0; + for(i=p->tos-nField+1; i<=p->tos; i++){ + if( (aStack[i].flags & STK_Null)==0 ){ + memcpy(&zNew[j], zStack[i], aStack[i].n-1); + j += aStack[i].n-1; + } + if( nSep>0 && itos ){ + memcpy(&zNew[j], zSep, nSep); + j += nSep; + } + } + zNew[j] = 0; + if( pOp->p2==0 ) PopStack(p, nField); + VERIFY( NeedStack(p, p->tos+1); ) + p->tos++; + aStack[p->tos].n = nByte; + aStack[p->tos].flags = STK_Str|STK_Dyn; + zStack[p->tos] = zNew; + break; +} + +/* Opcode: Add * * * +** +** Pop the top two elements from the stack, add them together, +** and push the result back onto the stack. If either element +** is a string then it is converted to a double using the atof() +** function before the addition. +*/ +/* Opcode: Multiply * * * +** +** Pop the top two elements from the stack, multiply them together, +** and push the result back onto the stack. If either element +** is a string then it is converted to a double using the atof() +** function before the multiplication. +*/ +/* Opcode: Subtract * * * +** +** Pop the top two elements from the stack, subtract the +** first (what was on top of the stack) from the second (the +** next on stack) +** and push the result back onto the stack. If either element +** is a string then it is converted to a double using the atof() +** function before the subtraction. +*/ +/* Opcode: Divide * * * +** +** Pop the top two elements from the stack, divide the +** first (what was on top of the stack) from the second (the +** next on stack) +** and push the result back onto the stack. If either element +** is a string then it is converted to a double using the atof() +** function before the division. Division by zero returns NULL. +*/ +case OP_Add: +case OP_Subtract: +case OP_Multiply: +case OP_Divide: { + int tos = p->tos; + int nos = tos - 1; + VERIFY( if( nos<0 ) goto not_enough_stack; ) + if( (aStack[tos].flags & aStack[nos].flags & STK_Int)==STK_Int ){ + int a, b; + a = aStack[tos].i; + b = aStack[nos].i; + switch( pOp->opcode ){ + case OP_Add: b += a; break; + case OP_Subtract: b -= a; break; + case OP_Multiply: b *= a; break; default: { - sprintf(zBuf,"%d",pOp->opcode); - sqliteSetString(pzErrMsg, "unknown opcode ", zBuf, 0); - rc = SQLITE_INTERNAL; + if( a==0 ) goto divide_by_zero; + b /= a; break; } } + POPSTACK; + Release(p, nos); + aStack[nos].i = b; + aStack[nos].flags = STK_Int; + }else{ + double a, b; + Realify(p, tos); + Realify(p, nos); + a = aStack[tos].r; + b = aStack[nos].r; + switch( pOp->opcode ){ + case OP_Add: b += a; break; + case OP_Subtract: b -= a; break; + case OP_Multiply: b *= a; break; + default: { + if( a==0.0 ) goto divide_by_zero; + b /= a; + break; + } + } + POPSTACK; + Release(p, nos); + aStack[nos].r = b; + aStack[nos].flags = STK_Real; + } + break; + +divide_by_zero: + PopStack(p, 2); + p->tos = nos; + aStack[nos].flags = STK_Null; + break; +} + +/* Opcode: Max * * * +** +** Pop the top two elements from the stack then push back the +** largest of the two. +*/ +case OP_Max: { + int tos = p->tos; + int nos = tos - 1; + int ft, fn; + int copy = 0; + VERIFY( if( nos<0 ) goto not_enough_stack; ) + ft = aStack[tos].flags; + fn = aStack[nos].flags; + if( fn & STK_Null ){ + copy = 1; + }else if( (ft & fn & STK_Int)==STK_Int ){ + copy = aStack[nos].iaStack[nos].r; + }else{ + if( Stringify(p, tos) || Stringify(p, nos) ) goto no_mem; + copy = sqliteCompare(zStack[tos],zStack[nos])>0; + } + if( copy ){ + Release(p, nos); + aStack[nos] = aStack[tos]; + zStack[nos] = zStack[tos]; + zStack[tos] = 0; + aStack[tos].flags = 0; + }else{ + Release(p, tos); + } + p->tos = nos; + break; +} + +/* Opcode: Min * * * +** +** Pop the top two elements from the stack then push back the +** smaller of the two. +*/ +case OP_Min: { + int tos = p->tos; + int nos = tos - 1; + int ft, fn; + int copy = 0; + VERIFY( if( nos<0 ) goto not_enough_stack; ) + ft = aStack[tos].flags; + fn = aStack[nos].flags; + if( fn & STK_Null ){ + copy = 1; + }else if( ft & STK_Null ){ + copy = 0; + }else if( (ft & fn & STK_Int)==STK_Int ){ + copy = aStack[nos].i>aStack[tos].i; + }else if( ( (ft|fn) & (STK_Int|STK_Real) ) !=0 ){ + Realify(p, tos); + Realify(p, nos); + copy = aStack[tos].rtos = nos; + break; +} + +/* Opcode: AddImm P1 * * +** +** Add the value P1 to whatever is on top of the stack. +*/ +case OP_AddImm: { + int tos = p->tos; + VERIFY( if( tos<0 ) goto not_enough_stack; ) + Integerify(p, tos); + aStack[tos].i += pOp->p1; + break; +} + +/* Opcode: Eq * P2 * +** +** Pop the top two elements from the stack. If they are equal, then +** jump to instruction P2. Otherwise, continue to the next instruction. +*/ +/* Opcode: Ne * P2 * +** +** Pop the top two elements from the stack. If they are not equal, then +** jump to instruction P2. Otherwise, continue to the next instruction. +*/ +/* Opcode: Lt * P2 * +** +** Pop the top two elements from the stack. If second element (the +** next on stack) is less than the first (the top of stack), then +** jump to instruction P2. Otherwise, continue to the next instruction. +** In other words, jump if NOSTOS. +*/ +/* Opcode: Ge * P2 * +** +** Pop the top two elements from the stack. If second element (the next +** on stack) is greater than or equal to the first (the top of stack), +** then jump to instruction P2. In other words, jump if NOS>=TOS. +*/ +case OP_Eq: +case OP_Ne: +case OP_Lt: +case OP_Le: +case OP_Gt: +case OP_Ge: { + int tos = p->tos; + int nos = tos - 1; + int c; + int ft, fn; + VERIFY( if( nos<0 ) goto not_enough_stack; ) + ft = aStack[tos].flags; + fn = aStack[nos].flags; + if( (ft & fn)==STK_Int ){ + c = aStack[nos].i - aStack[tos].i; + }else{ + if( Stringify(p, tos) || Stringify(p, nos) ) goto no_mem; + c = sqliteCompare(zStack[nos], zStack[tos]); + } + switch( pOp->opcode ){ + case OP_Eq: c = c==0; break; + case OP_Ne: c = c!=0; break; + case OP_Lt: c = c<0; break; + case OP_Le: c = c<=0; break; + case OP_Gt: c = c>0; break; + default: c = c>=0; break; + } + POPSTACK; + POPSTACK; + if( c ) pc = pOp->p2-1; + break; +} + +/* Opcode: Like P1 P2 * +** +** Pop the top two elements from the stack. The top-most is a +** "like" pattern -- the right operand of the SQL "LIKE" operator. +** The lower element is the string to compare against the like +** pattern. Jump to P2 if the two compare, and fall through without +** jumping if they do not. The '%' in the top-most element matches +** any sequence of zero or more characters in the lower element. The +** '_' character in the topmost matches any single character of the +** lower element. Case is ignored for this comparison. +** +** If P1 is not zero, the sense of the test is inverted and we +** have a "NOT LIKE" operator. The jump is made if the two values +** are different. +*/ +case OP_Like: { + int tos = p->tos; + int nos = tos - 1; + int c; + VERIFY( if( nos<0 ) goto not_enough_stack; ) + if( Stringify(p, tos) || Stringify(p, nos) ) goto no_mem; + c = sqliteLikeCompare(zStack[tos], zStack[nos]); + POPSTACK; + POPSTACK; + if( pOp->p1 ) c = !c; + if( c ) pc = pOp->p2-1; + break; +} + +/* Opcode: Glob P1 P2 * +** +** Pop the top two elements from the stack. The top-most is a +** "glob" pattern. The lower element is the string to compare +** against the glob pattern. +** +** Jump to P2 if the two compare, and fall through without +** jumping if they do not. The '*' in the top-most element matches +** any sequence of zero or more characters in the lower element. The +** '?' character in the topmost matches any single character of the +** lower element. [...] matches a range of characters. [^...] +** matches any character not in the range. Case is significant +** for globs. +** +** If P1 is not zero, the sense of the test is inverted and we +** have a "NOT GLOB" operator. The jump is made if the two values +** are different. +*/ +case OP_Glob: { + int tos = p->tos; + int nos = tos - 1; + int c; + VERIFY( if( nos<0 ) goto not_enough_stack; ) + if( Stringify(p, tos) || Stringify(p, nos) ) goto no_mem; + c = sqliteGlobCompare(zStack[tos], zStack[nos]); + POPSTACK; + POPSTACK; + if( pOp->p1 ) c = !c; + if( c ) pc = pOp->p2-1; + break; +} + +/* Opcode: And * * * +** +** Pop two values off the stack. Take the logical AND of the +** two values and push the resulting boolean value back onto the +** stack. +*/ +/* Opcode: Or * * * +** +** Pop two values off the stack. Take the logical OR of the +** two values and push the resulting boolean value back onto the +** stack. +*/ +case OP_And: +case OP_Or: { + int tos = p->tos; + int nos = tos - 1; + int c; + VERIFY( if( nos<0 ) goto not_enough_stack; ) + Integerify(p, tos); + Integerify(p, nos); + if( pOp->opcode==OP_And ){ + c = aStack[tos].i && aStack[nos].i; + }else{ + c = aStack[tos].i || aStack[nos].i; + } + POPSTACK; + Release(p, nos); + aStack[nos].i = c; + aStack[nos].flags = STK_Int; + break; +} + +/* Opcode: Negative * * * +** +** Treat the top of the stack as a numeric quantity. Replace it +** with its additive inverse. +*/ +case OP_Negative: { + int tos = p->tos; + VERIFY( if( tos<0 ) goto not_enough_stack; ) + if( aStack[tos].flags & STK_Real ){ + Release(p, tos); + aStack[tos].r = -aStack[tos].r; + aStack[tos].flags = STK_Real; + }else if( aStack[tos].flags & STK_Int ){ + Release(p, tos); + aStack[tos].i = -aStack[tos].i; + aStack[tos].flags = STK_Int; + }else{ + Realify(p, tos); + Release(p, tos); + aStack[tos].r = -aStack[tos].r; + aStack[tos].flags = STK_Real; + } + break; +} + +/* Opcode: Not * * * +** +** Interpret the top of the stack as a boolean value. Replace it +** with its complement. +*/ +case OP_Not: { + int tos = p->tos; + VERIFY( if( p->tos<0 ) goto not_enough_stack; ) + Integerify(p, tos); + Release(p, tos); + aStack[tos].i = !aStack[tos].i; + aStack[tos].flags = STK_Int; + break; +} + +/* Opcode: Noop * * * +** +** Do nothing. This instruction is often useful as a jump +** destination. +*/ +case OP_Noop: { + break; +} + +/* Opcode: If * P2 * +** +** Pop a single boolean from the stack. If the boolean popped is +** true, then jump to p2. Otherwise continue to the next instruction. +** An integer is false if zero and true otherwise. A string is +** false if it has zero length and true otherwise. +*/ +case OP_If: { + int c; + VERIFY( if( p->tos<0 ) goto not_enough_stack; ) + Integerify(p, p->tos); + c = aStack[p->tos].i; + POPSTACK; + if( c ) pc = pOp->p2-1; + break; +} + +/* Opcode: IsNull * P2 * +** +** Pop a single value from the stack. If the value popped is NULL +** then jump to p2. Otherwise continue to the next +** instruction. +*/ +case OP_IsNull: { + int c; + VERIFY( if( p->tos<0 ) goto not_enough_stack; ) + c = (aStack[p->tos].flags & STK_Null)!=0; + POPSTACK; + if( c ) pc = pOp->p2-1; + break; +} + +/* Opcode: NotNull * P2 * +** +** Pop a single value from the stack. If the value popped is not an +** empty string, then jump to p2. Otherwise continue to the next +** instruction. +*/ +case OP_NotNull: { + int c; + VERIFY( if( p->tos<0 ) goto not_enough_stack; ) + c = (aStack[p->tos].flags & STK_Null)==0; + POPSTACK; + if( c ) pc = pOp->p2-1; + break; +} + +/* Opcode: MakeRecord P1 * * +** +** Convert the top P1 entries of the stack into a single entry +** suitable for use as a data record in a database table. To do this +** all entries (except NULLs) are converted to strings and +** concatenated. The null-terminators are preserved by the concatation +** and serve as a boundry marker between columns. The lowest entry +** on the stack is the first in the concatenation and the top of +** the stack is the last. After all columns are concatenated, an +** index header is added. The index header consists of P1 integers +** which hold the offset of the beginning of each column data from the +** beginning of the completed record including the header. Header +** entries for NULL fields point to where the first byte of the column +** would have been stored if the column had held any bytes. +*/ +case OP_MakeRecord: { + char *zNewRecord; + int nByte; + int nField; + int i, j; + int addr; + + nField = pOp->p1; + VERIFY( if( p->tos+1tos-nField+1; i<=p->tos; i++){ + if( (aStack[i].flags & STK_Null)==0 ){ + if( Stringify(p, i) ) goto no_mem; + nByte += aStack[i].n; + } + } + nByte += sizeof(int)*nField; + zNewRecord = sqliteMalloc( nByte ); + if( zNewRecord==0 ) goto no_mem; + j = 0; + addr = sizeof(int)*nField; + for(i=p->tos-nField+1; i<=p->tos; i++){ + if( (aStack[i].flags & STK_Null)==0 ){ + addr += aStack[i].n; + } + memcpy(&zNewRecord[j], (char*)&addr, sizeof(int)); + j += sizeof(int); + } + for(i=p->tos-nField+1; i<=p->tos; i++){ + if( (aStack[i].flags & STK_Null)==0 ){ + memcpy(&zNewRecord[j], zStack[i], aStack[i].n); + j += aStack[i].n; + } + } + PopStack(p, nField); + VERIFY( NeedStack(p, p->tos+1); ) + p->tos++; + aStack[p->tos].n = nByte; + aStack[p->tos].flags = STK_Str | STK_Dyn; + zStack[p->tos] = zNewRecord; + break; +} + +/* Opcode: MakeKey P1 P2 * +** +** Convert the top P1 entries of the stack into a single entry suitable +** for use as the key in an index or a sort. The top P1 records are +** concatenated with a tab character (ASCII 0x09) used as a record +** separator. The entire concatenation is null-terminated. The +** lowest entry in the stack is the first field and the top of the +** stack becomes the last. +** +** If P2 is not zero, then the original entries remain on the stack +** and the new key is pushed on top. If P2 is zero, the original +** data is popped off the stack first then the new key is pushed +** back in its place. +** +** See also: MakeIdxKey, SortMakeKey +*/ +case OP_MakeKey: { + char *zNewKey; + int nByte; + int nField; + int i, j; + + nField = pOp->p1; + VERIFY( if( p->tos+1tos-nField+1; i<=p->tos; i++){ + if( aStack[i].flags & STK_Null ){ + nByte++; + }else{ + if( Stringify(p, i) ) goto no_mem; + nByte += aStack[i].n; + } + } + zNewKey = sqliteMalloc( nByte ); + if( zNewKey==0 ) goto no_mem; + j = 0; + for(i=p->tos-nField+1; i<=p->tos; i++){ + if( (aStack[i].flags & STK_Null)==0 ){ + memcpy(&zNewKey[j], zStack[i], aStack[i].n-1); + j += aStack[i].n-1; + } + if( itos ) zNewKey[j++] = '\t'; + } + zNewKey[j] = 0; + if( pOp->p2==0 ) PopStack(p, nField); + VERIFY( NeedStack(p, p->tos+1); ) + p->tos++; + aStack[p->tos].n = nByte; + aStack[p->tos].flags = STK_Str|STK_Dyn; + zStack[p->tos] = zNewKey; + break; +} + +/* Opcode: MakeIdxKey P1 * * +** +** Convert the top P1 entries of the stack into a single entry suitable +** for use as the key in an index. In addition, take one additional integer +** off of the stack, treat that integer as a four-byte record number, and +** append the four bytes to the key. Thus a total of P1+1 entries are +** popped from the stack for this instruction and a single entry is pushed +** back. The first P1 entries that are popped are strings and the last +** entry (the lowest on the stack) is an integer record number. +** +** The converstion of the first P1 string entries occurs just like in +** MakeKey. Each entry is separated from the others by a tab (ASCII 0x09). +** The entire concatenation is null-terminated. The lowest entry +** in the stack is the first field and the top of the stack becomes the +** last. +** +** See also: MakeKey, SortMakeKey +*/ +case OP_MakeIdxKey: { + char *zNewKey; + int nByte; + int nField; + int i, j; + + nField = pOp->p1; + VERIFY( if( p->tos+1tos-nField+1; i<=p->tos; i++){ + if( aStack[i].flags & STK_Null ){ + nByte++; + }else{ + if( Stringify(p, i) ) goto no_mem; + nByte += aStack[i].n; + } + } + zNewKey = sqliteMalloc( nByte ); + if( zNewKey==0 ) goto no_mem; + j = 0; + for(i=p->tos-nField+1; i<=p->tos; i++){ + if( (aStack[i].flags & STK_Null)==0 ){ + memcpy(&zNewKey[j], zStack[i], aStack[i].n-1); + j += aStack[i].n-1; + } + if( itos ) zNewKey[j++] = '\t'; + } + zNewKey[j++] = 0; + Integerify(p, p->tos-nField); + memcpy(&zNewKey[j], aStack[p->tos-nField].i, sizeof(int)); + PopStack(p, nField+1); + VERIFY( NeedStack(p, p->tos+1); ) + p->tos++; + aStack[p->tos].n = nByte; + aStack[p->tos].flags = STK_Str|STK_Dyn; + zStack[p->tos] = zNewKey; + break; +} + +/* Opcode: Transaction * * * +** +** Begin a transaction. The transaction ends when a Commit or Rollback +** opcode is encountered or whenever there is an execution error that causes +** a script to abort. +** +** A transaction must be started before any changes can be made to the +** database. +*/ +case OP_Transaction: { + rc = sqliteBtreeBeginTrans(pBe); + break; +} + +/* Opcode: Commit * * * +** +** Cause all modifications to the database that have been made since the +** last Transaction to actually take effect. No additional modifications +** are allowed until another transaction is started. +*/ +case OP_Commit: { + rc = sqliteBtreeCommit(pBe); + if( rc==SQLITE_OK ){ + sqliteCommitInternalChanges(db); + }else{ + sqliteRollbackInternalChanges(db); + } + break; +} + +/* Opcode: Rollback * * * +** +** Cause all modifications to the database that have been made since the +** last Transaction to be undone. The database is restored to its state +** before the Transaction opcode was executed. No additional modifications +** are allowed until another transaction is started. +*/ +case OP_Rollback: { + rc = sqliteBtreeRollback(pBe); + sqliteRollbackInternalChanges(db); + break; +} + +/* Opcode: Open P1 P2 P3 +** +** Open a new cursor for the database table whose root page is +** P2 in the main database file. Give the new cursor an identifier +** of P1. The P1 values need not be contiguous but all P1 values +** should be small integers. It is an error for P1 to be negative. +** +** The P3 value is the name of the table or index being opened. +** The P3 value is not actually used by this opcode and may be +** omitted. But the code generator usually inserts the index or +** table name into P3 to make the code easier to read. +*/ +case OP_Open: { + int busy = 0; + int i = pOp->p1; + VERIFY( if( i<0 ) goto bad_instruction; ) + if( i>=p->nCursor ){ + int j; + p->aCsr = sqliteRealloc( p->aCsr, (i+1)*sizeof(Cursor) ); + if( p->aCsr==0 ){ p->nCursor = 0; goto no_mem; } + for(j=p->nCursor; j<=i; j++) p->aCsr[j].pCursor = 0; + p->nCursor = i+1; + }else if( p->aCsr[i].pCursor ){ + sqliteBtreeCloseCursor(p->aCsr[i].pCursor); + } + memset(&p->aCsr[i], 0, sizeof(Cursor)); + do { + rc = sqliteBtreeOpenCursor(pBe, pOp->p2, &p->aCsr[i].pCursor); + switch( rc ){ + case SQLITE_BUSY: { + if( xBusy==0 || (*xBusy)(pBusyArg, pOp->p3, ++busy)==0 ){ + sqliteSetString(pzErrMsg, sqliteErrStr(rc), 0); + busy = 0; + } + break; + } + case SQLITE_OK: { + busy = 0; + break; + } + default: { + goto abort_due_to_error; + } + } + }while( busy ); + break; +} + +/* Opcode: OpenTemp P1 * * +** +** Open a new cursor that points to a table in a temporary database +** file. The temporary file is opened read/write event if the main +** database is read-only. The temporary file is deleted when the +** cursor is closed. +*/ +case OP_OpenTemp: { + int busy = 0; + int i = pOp->p1; + Cursor *pCx; + VERIFY( if( i<0 ) goto bad_instruction; ) + if( i>=p->nCursor ){ + int j; + p->aCsr = sqliteRealloc( p->aCsr, (i+1)*sizeof(Cursor) ); + if( p->aCsr==0 ){ p->nCursor = 0; goto no_mem; } + for(j=p->nCursor; j<=i; j++) p->aCsr[j].pCursor = 0; + p->nCursor = i+1; + }else if( p->aCsr[i].pCursor ){ + sqliteBtreeCloseCursor(p->aCsr[i].pCursor); + } + pCx = &p->aCsr[i]; + memset(pCx, 0, sizeof(*pCx)); + rc = sqliteBtreeOpen(0, 0, 100, &pCx->pBt); + if( rc==SQLITE_OK ){ + rc = sqliteBtreeOpenCursor(pCx->pBt, 2, &pCx->pCursor); + } + if( rc==SQLITE_OK ){ + rc = sqliteBtreeBeginTrans(pCx->pBt); + } + break; +} + +/* Opcode: Close P1 * * +** +** Close a cursor previously opened as P1. If P1 is not +** currently open, this instruction is a no-op. +*/ +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; + } + } + break; +} + +/* Opcode: MoveTo P1 * * +** +** Pop the top of the stack and use its value as a key. Reposition +** cursor P1 so that it points to an entry with a matching key. If +** the table contains no record with a matching key, then the cursor +** is left pointing at a nearby record. +*/ +case OP_MoveTo: { + int i = pOp->p1; + int tos = p->tos; + VERIFY( if( tos<0 ) goto not_enough_stack; ) + if( i>=0 && inCursor && p->aCsr[i].pCursor ){ + int res; + if( aStack[tos].flags & STK_Int ){ + sqliteBtreeMoveTo(p->aCsr[i].pCursor, sizeof(int), + (char*)&aStack[tos].i, &res); + p->aCsr[i].lastRecno = aStack[tos].i; + p->aCsr[i].recnoIsValid = 1; + }else{ + if( Stringify(p, tos) ) goto no_mem; + pBex->Fetch(p->aCsr[i].pCursor, aStack[tos].n, + zStack[tos], &res); + p->aCsr[i].recnoIsValid = 0; + } + p->nFetch++; + } + POPSTACK; + break; +} + +/* Opcode: Fcnt * * * +** +** Push an integer onto the stack which is the total number of +** OP_Fetch opcodes that have been executed by this virtual machine. +** +** This instruction is used to implement the special fcnt() function +** in the SQL dialect that SQLite understands. fcnt() is used for +** testing purposes. +*/ +case OP_Fcnt: { + int i = ++p->tos; + VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) + aStack[i].i = p->nFetch; + aStack[i].flags = STK_Int; + break; +} + +/* Opcode: Distinct P1 P2 * +** +** Use the top of the stack as a key. If a record with that key +** does not exist in file P1, then jump to P2. If the record +** does already exist, then fall thru. The record is not retrieved. +** The key is not popped from the stack. +** +** This operation is similar to NotFound except that this operation +** does not pop the key from the stack. +*/ +/* Opcode: Found P1 P2 * +** +** Use the top of the stack as a key. If a record with that key +** does exist in file P1, then jump to P2. If the record +** does not exist, then fall thru. The record is not retrieved. +** The key is popped from the stack. +*/ +/* Opcode: NotFound P1 P2 * +** +** Use the top of the stack as a key. If a record with that key +** does not exist in file P1, then jump to P2. If the record +** does exist, then fall thru. The record is not retrieved. +** The key is popped from the stack. +** +** The difference between this operation and Distinct is that +** Distinct does not pop the key from the stack. +*/ +case OP_Distinct: +case OP_NotFound: +case OP_Found: { + int i = pOp->p1; + int tos = p->tos; + int alreadyExists = 0; + VERIFY( if( tos<0 ) goto not_enough_stack; ) + if( VERIFY( i>=0 && inCursor && ) p->aCsr[i].pCursor ){ + int res, rx; + if( aStack[tos].flags & STK_Int ){ + rx = sqliteBtreeMoveTo(p->aCsr[i].pCursor, sizeof(int), + (char*)&aStack[tos].i, &res); + }else{ + if( Stringify(p, tos) ) goto no_mem; + rx = sqliteBtreeMoveTo(p->aCsr[i].pCursor,aStack[tos].n, + zStack[tos], &res); + } + alreadyExists = rx==SQLITE_OK && res==0; + } + if( pOp->opcode==OP_Found ){ + if( alreadyExists ) pc = pOp->p2 - 1; + }else{ + if( !alreadyExists ) pc = pOp->p2 - 1; + } + if( pOp->opcode!=OP_Distinct ){ + POPSTACK; + } + break; +} + +/* Opcode: NewRecno P1 * * +** +** Get a new integer record number used as the key to a table. +** The record number is not previous used by the database file +** associated with cursor P1. The new record number pushed +** onto the stack. +*/ +case OP_NewRecno: { + int i = pOp->p1; + int v; + if( VERIFY( i<0 || i>=p->nCursor || ) p->aCsr[i].pCursor==0 ){ + v = 0; + }else{ + int res, rx, cnt; + cnt = 0; + do{ + v = sqliteRandomInteger(); + rx = sqliteBtreeMoveTo(p->aCsr[i].pCursor, sizeof(v), &v, &res); + cnt++; + }while( cnt<10 && rx==SQLITE_OK && res==0 ); + } + VERIFY( NeedStack(p, p->tos+1); ) + p->tos++; + aStack[p->tos].i = v; + aStack[p->tos].flags = STK_Int; + break; +} + +/* Opcode: Put P1 * * +** +** Write an entry into the database file P1. A new entry is +** created if it doesn't already exist, or the data for an existing +** entry is overwritten. The data is the value on the top of the +** stack. The key is the next value down on the stack. The stack +** is popped twice by this instruction. +*/ +case OP_Put: { + int tos = p->tos; + int nos = p->tos-1; + int i = pOp->p1; + VERIFY( if( nos<0 ) goto not_enough_stack; ) + if( VERIFY( i>=0 && inCursor && ) p->aCsr[i].pCursor!=0 ){ + char *zKey; + int nKey; + if( (aStack[nos].flags & STK_Int)==0 ){ + if( Stringify(p, nos) ) goto no_mem; + nKey = aStack[nos].n; + zKey = zStack[nos]; + }else{ + nKey = sizeof(int); + zKey = (char*)&aStack[nos].i; + } + rc = sqliteBtreeInsert(p->aCsr[i].pCursor, nKey, zKey, + aStack[tos].n, zStack[tos]); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + } + POPSTACK; + POPSTACK; + break; +} + +/* Opcode: Delete P1 * * +** +** The top of the stack is a key. Remove this key and its data +** from database file P1. Then pop the stack to discard the key. +*/ +case OP_Delete: { + int tos = p->tos; + int i = pOp->p1; + VERIFY( if( tos<0 ) goto not_enough_stack; ) + if( VERIFY( i>=0 && inCursor && ) p->aCsr[i].pCursor!=0 ){ + char *zKey; + int nKey; + if( aStack[tos].flags & STK_Int ){ + nKey = sizeof(int); + zKey = (char*)&aStack[tos].i; + }else{ + if( Stringify(p, tos) ) goto no_mem; + nKey = aStack[tos].n; + zKey = zStack[tos]; + } + rc = sqliteBtreeDelete(p->aCsr[i].pCursor, nKey, zKey); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + } + POPSTACK; + break; +} + +/* Opcode: KeyAsData P1 P2 * +** +** Turn the key-as-data mode for cursor P1 either on (if P2==1) or +** off (if P2==0). In key-as-data mode, the OP_Field opcode pulls +** data off of the key rather than the data. This is useful for +** processing compound selects. +*/ +case OP_KeyAsData: { + int i = pOp->p1; + if( VERIFY( i>=0 && inCursor && ) p->aCsr[i].pCursor!=0 ){ + p->aCsr[i].keyAsData = pOp->p2; + } + break; +} + +/* Opcode: Column P1 P2 * +** +** Interpret the data in the most recent fetch from cursor P1 +** is a structure built using the MakeRecord instruction. +** Push onto the stack the value of the P2-th field of that +** structure. +** +** The value pushed is a pointer to the data stored in the cursor. +** The value will go away the next time the cursor is modified in +** any way. Make a copy of the string (using +** "Concat 1 0 0") if it needs to persist longer than that. +** +** If the KeyAsData opcode has previously executed on this cursor, +** then the field might be extracted from the key rather than the +** data. +*/ +case OP_Column: { + int *pAddr; + int amt, offset, nCol, payloadSize; + int aHdr[10]; + const int mxHdr = sizeof(aHdr)/sizeof(aHdr[0]); + int i = pOp->p1; + int p2 = pOp->p2; + int tos = ++p->tos; + BtCursor *pCrsr; + char *z; + + VERIFY( if( NeedStack(p, tos) ) goto no_mem; ) + if( VERIFY( i>=0 && inCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ + int (*xSize)(BtCursor*, int*); + int (*xRead)(BtCursor*, int, int, void*); + + /* Use different access functions depending on whether the information + ** is coming from the key or the data of the record. + */ + if( p->aCsr[i].keyAsData ){ + xSize = sqliteBtreeKeySize; + xRead = sqliteBtreeKey; + }else{ + xSize = sqliteBtreeDataSize; + xRead = sqliteBtreeData; + } + + /* + ** The code is complicated by efforts to minimize the number + ** of invocations of xRead() since that call can be expensive. + ** For the common case where P2 is small, xRead() is invoked + ** twice. For larger values of P2, it has to be called + ** three times. + */ + (*xSize)(pCrsr, &payloadSize); + if( payloadSize < sizeof(int)*(p2+1) ){ + rc = SQLITE_CORRUPT; + goto abort_due_to_error; + } + if( p2+1p1; + int tos = ++p->tos; + BtCursor *pCrsr; + + VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) + if( VERIFY( i>=0 && inCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ + int v; + if( p->aCsr[i].recnoIsValid ){ + v = p->aCsr[i].lastRecno; + }else{ + sqliteBtreeKey(pCrsr, 0, sizeof(int), &v); + } + aStack[tos].i = v; + aStack[tos].flags = STK_Int; + } + break; +} + +/* Opcode: FullKey P1 * * +** +** Push a string onto the stack which is the full text key associated +** with the last Next operation on file P1. Compare this with the +** Key operator which pushs an integer key. +*/ +case OP_FullKey: { + int i = pOp->p1; + int tos = ++p->tos; + BtCursor *pCrsr; + + VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) + VERIFY( if( !p->aCsr[i].keyAsData ) goto bad_instruction; ) + if( VERIFY( i>=0 && inCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ + int amt; + char *z; + + sqliteBtreeKeySize(pCrsr, &amt); + if( amt<=0 ){ + rc = SQLITE_CORRUPT; + goto abort_due_to_error; + } + z = sqliteMalloc( amt ); + sqliteBtreeKey(pCrsr, 0, amt, z); + zStack[tos] = z; + aStack[tos].flags = STK_Str | STK_Dyn; + aStack[tos].n = amt; + } + break; +} + +/* Opcode: Rewind P1 * * +** +** The next use of the Recno or Column or Next instruction for P1 +** will refer to the first entry in the database file. +*/ +case OP_Rewind: { + int i = pOp->p1; + BtCursor *pCrsr; + + if( VERIFY( i>=0 && inCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ + int res; + sqliteBtreeFirst(pCrsr, &res); + p->aCsr[i].atFirst = res==0; + } + break; +} + +/* Opcode: Next P1 P2 * +** +** Advance cursor P1 so that it points to the next key/data pair in its +** table. Or, if there are no more key/data pairs, jump to location P2. +*/ +case OP_Next: { + int i = pOp->p1; + BtCursor *pCrsr; + + if( VERIFY( i>=0 && inCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ + if( !p->aCsr[i].atFirst ){ + int res; + sqliteBtreeNext(pCrsr, &res); + if( res ){ + pc = pOp->p2 - 1; + }else{ + p->nFetch++; + } + } + p->aCsr[i].atFirst = 0; + p->aCsr[i].recnoIsValid = 0; + } + break; +} + +/* Opcode: BeginIdx P1 * * +** +** Begin searching an index for records with the key found on the +** top of the stack. The key on the top of the stack should be built +** using the MakeKey opcode. Subsequent calls to NextIdx will push +** record numbers onto the stack until all records with the same key +** have been returned. +** +** Note that the key for this opcode should be built using MakeKey +** but the key used for PutIdx and DeleteIdx should be built using +** MakeIdxKey. The difference is that MakeIdxKey adds a 4-bytes +** record number to the end of the key in order to specify a particular +** entry in the index. MakeKey specifies zero or more entries in the +** index that all have common values. +*/ +case OP_BeginIdx: { + int i = pOp->p1; + int tos = p->tos; + int res, rx; + Cursor *pCrsr; + 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; + pCrsr->nKey = aStack[tos].n; + pCrsr->zKey = sqliteMalloc( 2*(pCrsr->nKey + 1) ); + if( pCrsr->zKey==0 ) goto no_mem; + pCrsr->zBuf = &pCrsr->zKey[pCrsr->nKey+1]; + strncpy(pCrsr->zKey, zStack[tos], aStack[tos].n); + pCrsr->zKey[aStack[tos].n] = 0; + rx = sqliteBtreeMoveTo(pCrsr->pCursor, aStack[tos].n, zStack[tos], &res); + pCrsr->atFirst = rx==SQLITE_OK && res>0; + pCrsr->recnoIsValid = 0; + } + POPSTACK; + break; +} + +/* Opcode: NextIdx P1 P2 * +** +** The P1 cursor points to an SQL index for which a BeginIdx operation +** has been issued. This operation retrieves the next record number and +** pushes that record number onto the stack. Or, if there are no more +** record numbers for the given key, this opcode pushes nothing onto the +** stack but instead jumps to instruction P2. +*/ +case OP_NextIdx: { + int i = pOp->p1; + int tos = ++p->tos; + Cursor *pCrsr; + BtCursr *pCur; + int rx, res, size; + + VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) + zStack[tos] = 0; + if( VERIFY( i>=0 && inCursor && ) (pCrsr = &p->aCsr[i])->pCursor)!=0 ){ + pCur = pCrsr->pCursor; + rx = sqliteBtreeNext(pCur, &res); + if( rx!=SQLITE_OK ) goto abort_due_to_error; + sqliteBtreeKeySzie(pCur, &size); + if( res>0 || size!=pCrsr->nKey+sizeof(int) || + sqliteBtreeKey(pCur, 0, pCrsr->nKey, pCrsr->zBuf)!=pCrsr->nKey || + strncmp(pCrsr->zKey, pCrsr->zBuf, pCrsr->nKey)!=0 + ){ + pc = pOp->p2 - 1; + POPSTACK; + }else{ + int recno; + sqliteBtreeKey(pCur, pCrsr->nKey, sizeof(int), &recno); + p->aCsr[i].lastRecno = aStack[tos].i = recno; + p->aCsr[i].recnoIsValid = 1; + aStack[tos].flags = STK_Int; + } + } + break; +} + +/* Opcode: PutIdx P1 * * +** +** The top of the stack hold an SQL index key made using the +** MakeIdxKey instruction. This opcode writes that key into the +** index P1. Data for the entry is nil. +*/ +case OP_PutIdx: { + int i = pOp->p1; + int tos = p->tos; + BtCursor *pCrsr; + VERIFY( if( tos<0 ) goto not_enough_stack; ) + if( VERIFY( i>=0 && inCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ + sqliteBtreePut(pCrsr, aStack[tos].n, zStack[tos], 0, ""); + } + POPSTACK; + break; +} + +/* Opcode: DeleteIdx P1 * * +** +** The top of the stack is an index key built using the MakeIdxKey opcode. +** This opcode removes that entry from the index. +*/ +case OP_DeleteIdx: { + int i = pOp->p1; + int tos = p->tos; + BtCursor *pCrsr; + VERIFY( if( tos<0 ) goto not_enough_stack; ) + if( VERIFY( i>=0 && inCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ + int rx, res; + rx = sqliteBtreeMoveTo(pCrsr, aStack[tos].n, zStack[tos], &res); + if( rx==SQLITE_OK && res==0 ){ + sqliteBtreeDelete(pCrsr); + } + } + POPSTACK; + break; +} + +/* Opcode: Destroy P1 * * +** +** Delete an entire database table or index whose root page in the database +** file is given by P1. +*/ +case OP_Destroy: { + sqliteBtreeDropTable(pBe, pOp->p1); + break; +} + +/* Opcode: Reorganize P1 * * +** +** Compress, optimize, and tidy up table or index whose root page in the +** database file is P1. +*/ +case OP_Reorganize: { + /* This is currently a no-op */ + break; +} + +/* Opcode: ListOpen P1 * * +** +** Open a "List" structure used for temporary storage of integer +** table keys. P1 +** will server as a handle to this list for future +** interactions. If another list with the P1 handle is +** already opened, the prior list is closed and a new one opened +** in its place. +*/ +case OP_ListOpen: { + int i = pOp->p1; + VERIFY( if( i<0 ) goto bad_instruction; ) + if( i>=p->nList ){ + int j; + p->apList = sqliteRealloc( p->apList, (i+1)*sizeof(Keylist*) ); + if( p->apList==0 ){ p->nList = 0; goto no_mem; } + for(j=p->nList; j<=i; j++) p->apList[j] = 0; + p->nList = i+1; + }else if( p->apList[i] ){ + KeylistFree(p->apList[i]); + p->apList[i] = 0; + } + break; +} + +/* Opcode: ListWrite P1 * * +** +** Write the integer on the top of the stack +** into the temporary storage list P1. +*/ +case OP_ListWrite: { + int i = pOp->p1; + Keylist *pKeylist; + VERIFY( if( i<0 || i>=p->nList ) goto bad_instruction; ) + VERIFY( if( p->tos<0 ) goto not_enough_stack; ) + pKeylist = p->apList[i]; + if( pKeylist==0 || pKeylist->nUsed>=pKeylist->nKey ){ + pKeylist = sqliteMalloc( sizeof(Keylist)+999*sizeof(int) ); + if( pKeylist==0 ) goto no_mem; + pKeylist->nKey = 1000; + pKeylist->nRead = 0; + pKeylist->nUsed = 0; + pKeylist->pNext = p->apList[i]; + p->apList[i] = pKeylist; + } + Integerify(p, p->tos); + pKeylist->aKey[pKeylist->nUsed++] = aStack[p->tos].i; + POPSTACK; + break; +} + +/* Opcode: ListRewind P1 * * +** +** Rewind the temporary buffer P1 back to the beginning. +*/ +case OP_ListRewind: { + int i = pOp->p1; + VERIFY( if( i<0 ) goto bad_instruction; ) + /* This is now a no-op */ + break; +} + +/* Opcode: ListRead P1 P2 * +** +** Attempt to read an integer from temporary storage buffer P1 +** and push it onto the stack. If the storage buffer is empty, +** push nothing but instead jump to P2. +*/ +case OP_ListRead: { + int i = pOp->p1; + Keylist *pKeylist; + VERIFY(if( i<0 || i>=p->nList ) goto bad_instruction;) + pKeylist = p->apList[i]; + if( pKeylist!=0 ){ + VERIFY( + if( pKeylist->nRead<0 + || pKeylist->nRead>=pKeylist->nUsed + || pKeylist->nRead>=pKeylist->nKey ) goto bad_instruction; + ) + p->tos++; + if( NeedStack(p, p->tos) ) goto no_mem; + aStack[p->tos].i = pKeylist->aKey[pKeylist->nRead++]; + aStack[p->tos].flags = STK_Int; + zStack[p->tos] = 0; + if( pKeylist->nRead>=pKeylist->nUsed ){ + p->apList[i] = pKeylist->pNext; + sqliteFree(pKeylist); + } + }else{ + pc = pOp->p2 - 1; + } + break; +} + +/* Opcode: ListClose P1 * * +** +** Close the temporary storage buffer and discard its contents. +*/ +case OP_ListClose: { + int i = pOp->p1; + VERIFY( if( i<0 ) goto bad_instruction; ) + VERIFY( if( i>=p->nList ) goto bad_instruction; ) + KeylistFree(p->apList[i]); + p->apList[i] = 0; + break; +} + +/* Opcode: SortOpen P1 * * +** +** Create a new sorter with index P1 +*/ +case OP_SortOpen: { + int i = pOp->p1; + VERIFY( if( i<0 ) goto bad_instruction; ) + if( i>=p->nSort ){ + int j; + p->apSort = sqliteRealloc( p->apSort, (i+1)*sizeof(Sorter*) ); + if( p->apSort==0 ){ p->nSort = 0; goto no_mem; } + for(j=p->nSort; j<=i; j++) p->apSort[j] = 0; + p->nSort = i+1; + } + break; +} + +/* Opcode: SortPut P1 * * +** +** The TOS is the key and the NOS is the data. Pop both from the stack +** and put them on the sorter. +*/ +case OP_SortPut: { + int i = pOp->p1; + int tos = p->tos; + int nos = tos - 1; + Sorter *pSorter; + VERIFY( if( i<0 || i>=p->nSort ) goto bad_instruction; ) + VERIFY( if( tos<1 ) goto not_enough_stack; ) + if( Stringify(p, tos) || Stringify(p, nos) ) goto no_mem; + pSorter = sqliteMalloc( sizeof(Sorter) ); + if( pSorter==0 ) goto no_mem; + pSorter->pNext = p->apSort[i]; + p->apSort[i] = pSorter; + pSorter->nKey = aStack[tos].n; + pSorter->zKey = zStack[tos]; + pSorter->nData = aStack[nos].n; + pSorter->pData = zStack[nos]; + aStack[tos].flags = 0; + aStack[nos].flags = 0; + zStack[tos] = 0; + zStack[nos] = 0; + p->tos -= 2; + break; +} + +/* Opcode: SortMakeRec P1 * * +** +** The top P1 elements are the arguments to a callback. Form these +** elements into a single data entry that can be stored on a sorter +** using SortPut and later fed to a callback using SortCallback. +*/ +case OP_SortMakeRec: { + char *z; + char **azArg; + int nByte; + int nField; + int i, j; + + nField = pOp->p1; + VERIFY( if( p->tos+1tos-nField+1; i<=p->tos; i++){ + if( (aStack[i].flags & STK_Null)==0 ){ + if( Stringify(p, i) ) goto no_mem; + nByte += aStack[i].n; + } + } + nByte += sizeof(char*)*(nField+1); + azArg = sqliteMalloc( nByte ); + if( azArg==0 ) goto no_mem; + z = (char*)&azArg[nField+1]; + for(j=0, i=p->tos-nField+1; i<=p->tos; i++, j++){ + if( aStack[i].flags & STK_Null ){ + azArg[j] = 0; + }else{ + azArg[j] = z; + strcpy(z, zStack[i]); + z += aStack[i].n; + } + } + PopStack(p, nField); + VERIFY( NeedStack(p, p->tos+1); ) + p->tos++; + aStack[p->tos].n = nByte; + zStack[p->tos] = (char*)azArg; + aStack[p->tos].flags = STK_Str|STK_Dyn; + break; +} + +/* Opcode: SortMakeKey P1 * P3 +** +** Convert the top few entries of the stack into a sort key. The +** number of stack entries consumed is the number of characters in +** the string P3. One character from P3 is prepended to each entry. +** The first character of P3 is prepended to the element lowest in +** the stack and the last character of P3 is appended to the top of +** the stack. All stack entries are separated by a \000 character +** in the result. The whole key is terminated by two \000 characters +** in a row. +** +** See also the MakeKey opcode. +*/ +case OP_SortMakeKey: { + char *zNewKey; + int nByte; + int nField; + int i, j, k; + + nField = strlen(pOp->p3); + VERIFY( if( p->tos+1tos-nField+1; i<=p->tos; i++){ + if( Stringify(p, i) ) goto no_mem; + nByte += aStack[i].n+2; + } + zNewKey = sqliteMalloc( nByte ); + if( zNewKey==0 ) goto no_mem; + j = 0; + k = 0; + for(i=p->tos-nField+1; i<=p->tos; i++){ + zNewKey[j++] = pOp->p3[k++]; + memcpy(&zNewKey[j], zStack[i], aStack[i].n-1); + j += aStack[i].n-1; + zNewKey[j++] = 0; + } + zNewKey[j] = 0; + PopStack(p, nField); + VERIFY( NeedStack(p, p->tos+1); ) + p->tos++; + aStack[p->tos].n = nByte; + aStack[p->tos].flags = STK_Str|STK_Dyn; + zStack[p->tos] = zNewKey; + break; +} + +/* Opcode: Sort P1 * * +** +** Sort all elements on the given sorter. The algorithm is a +** mergesort. +*/ +case OP_Sort: { + int j; + j = pOp->p1; + VERIFY( if( j<0 ) goto bad_instruction; ) + if( jnSort ){ + int i; + Sorter *pElem; + Sorter *apSorter[NSORT]; + for(i=0; iapSort[j] ){ + pElem = p->apSort[j]; + p->apSort[j] = pElem->pNext; + pElem->pNext = 0; + for(i=0; i=NSORT-1 ){ + apSorter[NSORT-1] = Merge(apSorter[NSORT-1],pElem); + } + } + pElem = 0; + for(i=0; iapSort[j] = pElem; + } + break; +} + +/* Opcode: SortNext P1 P2 * +** +** Push the data for the topmost element in the given sorter onto the +** stack, then remove the element from the sorter. +*/ +case OP_SortNext: { + int i = pOp->p1; + VERIFY( if( i<0 ) goto bad_instruction; ) + if( VERIFY( inSort && ) p->apSort[i]!=0 ){ + Sorter *pSorter = p->apSort[i]; + p->apSort[i] = pSorter->pNext; + p->tos++; + VERIFY( NeedStack(p, p->tos); ) + zStack[p->tos] = pSorter->pData; + aStack[p->tos].n = pSorter->nData; + aStack[p->tos].flags = STK_Str|STK_Dyn; + sqliteFree(pSorter->zKey); + sqliteFree(pSorter); + }else{ + pc = pOp->p2 - 1; + } + break; +} + +/* Opcode: SortKey P1 * * +** +** Push the key for the topmost element of the sorter onto the stack. +** But don't change the sorter an any other way. +*/ +case OP_SortKey: { + int i = pOp->p1; + VERIFY( if( i<0 ) goto bad_instruction; ) + if( inSort && p->apSort[i]!=0 ){ + Sorter *pSorter = p->apSort[i]; + p->tos++; + VERIFY( NeedStack(p, p->tos); ) + sqliteSetString(&zStack[p->tos], pSorter->zKey, 0); + aStack[p->tos].n = pSorter->nKey; + aStack[p->tos].flags = STK_Str|STK_Dyn; + } + break; +} + +/* Opcode: SortCallback P1 P2 * +** +** The top of the stack contains a callback record built using +** the SortMakeRec operation with the same P1 value as this +** instruction. Pop this record from the stack and invoke the +** callback on it. +*/ +case OP_SortCallback: { + int i = p->tos; + VERIFY( if( i<0 ) goto not_enough_stack; ) + if( xCallback!=0 ){ + if( xCallback(pArg, pOp->p1, (char**)zStack[i], p->azColName) ){ + rc = SQLITE_ABORT; + } + } + POPSTACK; + break; +} + +/* Opcode: SortClose P1 * * +** +** Close the given sorter and remove all its elements. +*/ +case OP_SortClose: { + Sorter *pSorter; + int i = pOp->p1; + VERIFY( if( i<0 ) goto bad_instruction; ) + if( inSort ){ + while( (pSorter = p->apSort[i])!=0 ){ + p->apSort[i] = pSorter->pNext; + sqliteFree(pSorter->zKey); + sqliteFree(pSorter->pData); + sqliteFree(pSorter); + } + } + break; +} + +/* Opcode: FileOpen * * P3 +** +** Open the file named by P3 for reading using the FileRead opcode. +** If P3 is "stdin" then open standard input for reading. +*/ +case OP_FileOpen: { + VERIFY( if( pOp->p3==0 ) goto bad_instruction; ) + if( p->pFile ){ + if( p->pFile!=stdin ) fclose(p->pFile); + p->pFile = 0; + } + if( sqliteStrICmp(pOp->p3,"stdin")==0 ){ + p->pFile = stdin; + }else{ + p->pFile = fopen(pOp->p3, "r"); + } + if( p->pFile==0 ){ + sqliteSetString(pzErrMsg,"unable to open file: ", pOp->p3, 0); + rc = SQLITE_ERROR; + goto cleanup; + } + break; +} + +/* Opcode: FileClose * * * +** +** Close a file previously opened using FileOpen. This is a no-op +** if there is no prior FileOpen call. +*/ +case OP_FileClose: { + if( p->pFile ){ + if( p->pFile!=stdin ) fclose(p->pFile); + p->pFile = 0; + } + if( p->azField ){ + sqliteFree(p->azField); + p->azField = 0; + } + p->nField = 0; + if( p->zLine ){ + sqliteFree(p->zLine); + p->zLine = 0; + } + p->nLineAlloc = 0; + break; +} + +/* Opcode: FileRead P1 P2 P3 +** +** Read a single line of input from the open file (the file opened using +** FileOpen). If we reach end-of-file, jump immediately to P2. If +** we are able to get another line, split the line apart using P3 as +** a delimiter. There should be P1 fields. If the input line contains +** more than P1 fields, ignore the excess. If the input line contains +** fewer than P1 fields, assume the remaining fields contain an +** empty string. +*/ +case OP_FileRead: { + int n, eol, nField, i, c, nDelim; + char *zDelim, *z; + if( p->pFile==0 ) goto fileread_jump; + nField = pOp->p1; + if( nField<=0 ) goto fileread_jump; + if( nField!=p->nField || p->azField==0 ){ + p->azField = sqliteRealloc(p->azField, sizeof(char*)*nField+1); + if( p->azField==0 ){ + p->nField = 0; + goto fileread_jump; + } + p->nField = nField; + } + n = 0; + eol = 0; + while( eol==0 ){ + if( p->zLine==0 || n+200>p->nLineAlloc ){ + p->nLineAlloc = p->nLineAlloc*2 + 300; + p->zLine = sqliteRealloc(p->zLine, p->nLineAlloc); + if( p->zLine==0 ){ + p->nLineAlloc = 0; + goto fileread_jump; + } + } + if( fgets(&p->zLine[n], p->nLineAlloc-n, p->pFile)==0 ){ + eol = 1; + p->zLine[n] = 0; + }else{ + while( p->zLine[n] ){ n++; } + if( n>0 && p->zLine[n-1]=='\n' ){ + n--; + p->zLine[n] = 0; + eol = 1; + } + } + } + if( n==0 ) goto fileread_jump; + z = p->zLine; + if( z[0]=='\\' && z[1]=='.' && z[2]==0 ){ + goto fileread_jump; + } + zDelim = pOp->p3; + if( zDelim==0 ) zDelim = "\t"; + c = zDelim[0]; + nDelim = strlen(zDelim); + p->azField[0] = z; + for(i=1; *z!=0 && i<=nField; i++){ + int from, to; + from = to = 0; + while( z[from] ){ + if( z[from]=='\\' && z[from+1]!=0 ){ + z[to++] = z[from+1]; + from += 2; + continue; + } + if( z[from]==c && strncmp(&z[from],zDelim,nDelim)==0 ) break; + z[to++] = z[from++]; + } + if( z[from] ){ + z[to] = 0; + z += from + nDelim; + if( iazField[i] = z; + }else{ + z[to] = 0; + z = ""; + } + } + while( iazField[i++] = ""; + } + break; + + /* If we reach end-of-file, or if anything goes wrong, jump here. + ** This code will cause a jump to P2 */ +fileread_jump: + pc = pOp->p2 - 1; + break; +} + +/* Opcode: FileField P1 * * +** +** Push onto the stack the P1-th field of the most recently read line +** from the input file. +*/ +case OP_FileField: { + int i = pOp->p1; + char *z; + VERIFY( if( NeedStack(p, p->tos+1) ) goto no_mem; ) + if( VERIFY( i>=0 && inField && ) p->azField ){ + z = p->azField[i]; + }else{ + z = 0; + } + if( z==0 ) z = ""; + p->tos++; + aStack[p->tos].n = strlen(z) + 1; + zStack[p->tos] = z; + aStack[p->tos].flags = STK_Str; + break; +} + +/* Opcode: MemStore P1 * * +** +** Pop a single value of the stack and store that value into memory +** location P1. P1 should be a small integer since space is allocated +** for all memory locations between 0 and P1 inclusive. +*/ +case OP_MemStore: { + int i = pOp->p1; + int tos = p->tos; + Mem *pMem; + char *zOld; + VERIFY( if( tos<0 ) goto not_enough_stack; ) + if( i>=p->nMem ){ + int nOld = p->nMem; + p->nMem = i + 5; + p->aMem = sqliteRealloc(p->aMem, p->nMem*sizeof(p->aMem[0])); + if( p->aMem==0 ) goto no_mem; + if( nOldnMem ){ + memset(&p->aMem[nOld], 0, sizeof(p->aMem[0])*(p->nMem-nOld)); + } + } + pMem = &p->aMem[i]; + if( pMem->s.flags & STK_Dyn ){ + zOld = pMem->z; + }else{ + zOld = 0; + } + pMem->s = aStack[tos]; + if( pMem->s.flags & STK_Str ){ + pMem->z = sqliteStrNDup(zStack[tos], pMem->s.n); + pMem->s.flags |= STK_Dyn; + } + if( zOld ) sqliteFree(zOld); + POPSTACK; + break; +} + +/* Opcode: MemLoad P1 * * +** +** Push a copy of the value in memory location P1 onto the stack. +*/ +case OP_MemLoad: { + int tos = ++p->tos; + int i = pOp->p1; + VERIFY( if( NeedStack(p, tos) ) goto no_mem; ) + if( i<0 || i>=p->nMem ){ + aStack[tos].flags = STK_Null; + zStack[tos] = 0; + }else{ + aStack[tos] = p->aMem[i].s; + if( aStack[tos].flags & STK_Str ){ + char *z = sqliteMalloc(aStack[tos].n); + if( z==0 ) goto no_mem; + memcpy(z, p->aMem[i].z, aStack[tos].n); + zStack[tos] = z; + aStack[tos].flags |= STK_Dyn; + } + } + break; +} + +/* Opcode: AggReset * P2 * +** +** Reset the aggregator so that it no longer contains any data. +** Future aggregator elements will contain P2 values each. +*/ +case OP_AggReset: { + AggReset(&p->agg); + p->agg.nMem = pOp->p2; + break; +} + +/* Opcode: AggFocus * P2 * +** +** Pop the top of the stack and use that as an aggregator key. If +** an aggregator with that same key already exists, then make the +** aggregator the current aggregator and jump to P2. If no aggregator +** with the given key exists, create one and make it current but +** do not jump. +** +** The order of aggregator opcodes is important. The order is: +** AggReset AggFocus AggNext. In other words, you must execute +** AggReset first, then zero or more AggFocus operations, then +** zero or more AggNext operations. You must not execute an AggFocus +** in between an AggNext and an AggReset. +*/ +case OP_AggFocus: { + int tos = p->tos; + AggElem *pElem; + char *zKey; + int nKey; + + VERIFY( if( tos<0 ) goto not_enough_stack; ) + if( Stringify(p, tos) ) goto no_mem; + zKey = zStack[tos]; + nKey = aStack[tos].n; + if( p->agg.nHash<=0 ){ + pElem = 0; + }else{ + int h = sqliteHashNoCase(zKey, nKey-1) % p->agg.nHash; + for(pElem=p->agg.apHash[h]; pElem; pElem=pElem->pHash){ + if( strcmp(pElem->zKey, zKey)==0 ) break; + } + } + if( pElem ){ + p->agg.pCurrent = pElem; + pc = pOp->p2 - 1; + }else{ + AggInsert(&p->agg, zKey); + if( sqlite_malloc_failed ) goto no_mem; + } + POPSTACK; + break; +} + +/* Opcode: AggIncr P1 P2 * +** +** Increase the integer value in the P2-th field of the aggregate +** element current in focus by an amount P1. +*/ +case OP_AggIncr: { + AggElem *pFocus = AggInFocus(p->agg); + int i = pOp->p2; + if( pFocus==0 ) goto no_mem; + if( i>=0 && iagg.nMem ){ + Mem *pMem = &pFocus->aMem[i]; + if( pMem->s.flags!=STK_Int ){ + if( pMem->s.flags & STK_Int ){ + /* Do nothing */ + }else if( pMem->s.flags & STK_Real ){ + pMem->s.i = pMem->s.r; + }else if( pMem->s.flags & STK_Str ){ + pMem->s.i = atoi(pMem->z); + }else{ + pMem->s.i = 0; + } + if( pMem->s.flags & STK_Dyn ) sqliteFree(pMem->z); + pMem->z = 0; + pMem->s.flags = STK_Int; + } + pMem->s.i += pOp->p1; + } + break; +} + +/* Opcode: AggSet * P2 * +** +** Move the top of the stack into the P2-th field of the current +** aggregate. String values are duplicated into new memory. +*/ +case OP_AggSet: { + AggElem *pFocus = AggInFocus(p->agg); + int i = pOp->p2; + int tos = p->tos; + VERIFY( if( tos<0 ) goto not_enough_stack; ) + if( pFocus==0 ) goto no_mem; + if( VERIFY( i>=0 && ) iagg.nMem ){ + Mem *pMem = &pFocus->aMem[i]; + char *zOld; + if( pMem->s.flags & STK_Dyn ){ + zOld = pMem->z; + }else{ + zOld = 0; + } + pMem->s = aStack[tos]; + if( pMem->s.flags & STK_Str ){ + pMem->z = sqliteMalloc( aStack[tos].n ); + if( pMem->z==0 ) goto no_mem; + memcpy(pMem->z, zStack[tos], pMem->s.n); + pMem->s.flags |= STK_Str|STK_Dyn; + } + if( zOld ) sqliteFree(zOld); + } + POPSTACK; + break; +} + +/* Opcode: AggGet * P2 * +** +** Push a new entry onto the stack which is a copy of the P2-th field +** of the current aggregate. Strings are not duplicated so +** string values will be ephemeral. +*/ +case OP_AggGet: { + AggElem *pFocus = AggInFocus(p->agg); + int i = pOp->p2; + int tos = ++p->tos; + VERIFY( if( NeedStack(p, tos) ) goto no_mem; ) + if( pFocus==0 ) goto no_mem; + if( VERIFY( i>=0 && ) iagg.nMem ){ + Mem *pMem = &pFocus->aMem[i]; + aStack[tos] = pMem->s; + zStack[tos] = pMem->z; + aStack[tos].flags &= ~STK_Dyn; + } + break; +} + +/* Opcode: AggNext * P2 * +** +** Make the next aggregate value the current aggregate. The prior +** aggregate is deleted. If all aggregate values have been consumed, +** jump to P2. +** +** The order of aggregator opcodes is important. The order is: +** AggReset AggFocus AggNext. In other words, you must execute +** AggReset first, then zero or more AggFocus operations, then +** zero or more AggNext operations. You must not execute an AggFocus +** in between an AggNext and an AggReset. +*/ +case OP_AggNext: { + if( p->agg.nHash ){ + p->agg.nHash = 0; + sqliteFree(p->agg.apHash); + p->agg.apHash = 0; + p->agg.pCurrent = p->agg.pFirst; + }else if( p->agg.pCurrent==p->agg.pFirst && p->agg.pCurrent!=0 ){ + int i; + AggElem *pElem = p->agg.pCurrent; + for(i=0; iagg.nMem; i++){ + if( pElem->aMem[i].s.flags & STK_Dyn ){ + sqliteFree(pElem->aMem[i].z); + } + } + p->agg.pCurrent = p->agg.pFirst = pElem->pNext; + sqliteFree(pElem); + p->agg.nElem--; + } + if( p->agg.pCurrent==0 ){ + pc = pOp->p2-1; + } + break; +} + +/* Opcode: SetClear P1 * * +** +** Remove all elements from the P1-th Set. +*/ +case OP_SetClear: { + int i = pOp->p1; + if( i>=0 && inSet ){ + SetClear(&p->aSet[i]); + } + break; +} + +/* Opcode: SetInsert P1 * P3 +** +** If Set P1 does not exist then create it. Then insert value +** P3 into that set. If P3 is NULL, then insert the top of the +** stack into the set. +*/ +case OP_SetInsert: { + int i = pOp->p1; + if( p->nSet<=i ){ + p->aSet = sqliteRealloc(p->aSet, (i+1)*sizeof(p->aSet[0]) ); + if( p->aSet==0 ) goto no_mem; + memset(&p->aSet[p->nSet], 0, sizeof(p->aSet[0])*(i+1 - p->nSet)); + p->nSet = i+1; + } + if( pOp->p3 ){ + SetInsert(&p->aSet[i], pOp->p3); + }else{ + int tos = p->tos; + if( tos<0 ) goto not_enough_stack; + if( Stringify(p, tos) ) goto no_mem; + SetInsert(&p->aSet[i], zStack[tos]); + POPSTACK; + } + if( sqlite_malloc_failed ) goto no_mem; + break; +} + +/* Opcode: SetFound P1 P2 * +** +** Pop the stack once and compare the value popped off with the +** contents of set P1. If the element popped exists in set P1, +** then jump to P2. Otherwise fall through. +*/ +case OP_SetFound: { + int i = pOp->p1; + int tos = p->tos; + VERIFY( if( tos<0 ) goto not_enough_stack; ) + if( Stringify(p, tos) ) goto no_mem; + if( VERIFY( i>=0 && inSet &&) SetTest(&p->aSet[i], zStack[tos])){ + pc = pOp->p2 - 1; + } + POPSTACK; + break; +} + +/* Opcode: SetNotFound P1 P2 * +** +** Pop the stack once and compare the value popped off with the +** contents of set P1. If the element popped does not exists in +** set P1, then jump to P2. Otherwise fall through. +*/ +case OP_SetNotFound: { + int i = pOp->p1; + int tos = p->tos; + VERIFY( if( tos<0 ) goto not_enough_stack; ) + if( Stringify(p, tos) ) goto no_mem; + if(VERIFY( i>=0 && inSet &&) !SetTest(&p->aSet[i], zStack[tos])){ + pc = pOp->p2 - 1; + } + POPSTACK; + break; +} + +/* Opcode: Strlen * * * +** +** Interpret the top of the stack as a string. Replace the top of +** stack with an integer which is the length of the string. +*/ +case OP_Strlen: { + int tos = p->tos; + int len; + VERIFY( if( tos<0 ) goto not_enough_stack; ) + if( Stringify(p, tos) ) goto no_mem; +#ifdef SQLITE_UTF8 + { + char *z = zStack[tos]; + for(len=0; *z; z++){ if( (0xc0&*z)!=0x80 ) len++; } + } +#else + len = aStack[tos].n-1; +#endif + POPSTACK; + p->tos++; + aStack[tos].i = len; + aStack[tos].flags = STK_Int; + break; +} + +/* Opcode: Substr P1 P2 * +** +** This operation pops between 1 and 3 elements from the stack and +** pushes back a single element. The bottom-most element popped from +** the stack is a string and the element pushed back is also a string. +** The other two elements popped are integers. The integers are taken +** from the stack only if P1 and/or P2 are 0. When P1 or P2 are +** not zero, the value of the operand is used rather than the integer +** from the stack. In the sequel, we will use P1 and P2 to describe +** the two integers, even if those integers are really taken from the +** stack. +** +** The string pushed back onto the stack is a substring of the string +** that was popped. There are P2 characters in the substring. The +** first character of the substring is the P1-th character of the +** original string where the left-most character is 1 (not 0). If P1 +** is negative, then counting begins at the right instead of at the +** left. +*/ +case OP_Substr: { + int cnt; + int start; + int n; + char *z; + + if( pOp->p2==0 ){ + VERIFY( if( p->tos<0 ) goto not_enough_stack; ) + Integerify(p, p->tos); + cnt = aStack[p->tos].i; + POPSTACK; + }else{ + cnt = pOp->p2; + } + if( pOp->p1==0 ){ + VERIFY( if( p->tos<0 ) goto not_enough_stack; ) + Integerify(p, p->tos); + start = aStack[p->tos].i - 1; + POPSTACK; + }else{ + start = pOp->p1 - 1; + } + VERIFY( if( p->tos<0 ) goto not_enough_stack; ) + if( Stringify(p, p->tos) ) goto no_mem; + + /* "n" will be the number of characters in the input string. + ** For iso8859, the number of characters is the number of bytes. + ** Buf for UTF-8, some characters can use multiple bytes and the + ** situation is more complex. + */ +#ifdef SQLITE_UTF8 + z = zStack[p->tos]; + for(n=0; *z; z++){ if( (0xc0&*z)!=0x80 ) n++; } +#else + n = aStack[p->tos].n - 1; +#endif + if( start<0 ){ + start += n + 1; + if( start<0 ){ + cnt += start; + start = 0; + } + } + if( start>n ){ + start = n; + } + if( cnt<0 ) cnt = 0; + if( cnt > n ){ + cnt = n; + } + + /* At this point, "start" is the index of the first character to + ** extract and "cnt" is the number of characters to extract. We + ** need to convert units on these variable from characters into + ** bytes. For iso8859, the conversion is a no-op, but for UTF-8 + ** we have to do a little work. + */ +#ifdef SQLITE_UTF8 + { + int c_start = start; + int c_cnt = cnt; + int i; + z = zStack[p->tos]; + for(start=i=0; itos][start], cnt); + z[cnt] = 0; + POPSTACK; + p->tos++; + zStack[p->tos] = z; + aStack[p->tos].n = cnt + 1; + aStack[p->tos].flags = STK_Str|STK_Dyn; + break; +} + +/* An other opcode is illegal... +*/ +default: { + sprintf(zBuf,"%d",pOp->opcode); + sqliteSetString(pzErrMsg, "unknown opcode ", zBuf, 0); + rc = SQLITE_INTERNAL; + break; +} + +/***************************************************************************** +** The cases of the switch statement above this line should all be indented +** by 6 spaces. But the left-most 6 spaces have been removed to improve the +** readability. From this point on down, the normal indentation rules are +** restored. +*****************************************************************************/ + } /* The following code adds nothing to the actual functionality ** of the program. It is only here for testing and debugging. @@ -3369,15 +3627,27 @@ int sqliteVdbeExec( cleanup: Cleanup(p); + if( rc!=SQLITE_OK && (db->flags & SQLITE_InTrans)!=0 ){ + sqliteBtreeRollback(pBe); + sqliteRollbackInternalChanges(db); + db->flags &= ~SQLITE_InTrans; + } return rc; /* Jump to here if a malloc() fails. It's hard to get a malloc() ** to fail on a modern VM computer, so this code is untested. */ no_mem: - Cleanup(p); sqliteSetString(pzErrMsg, "out or memory", 0); - return 1; + rc = SQLITE_NOMEM; + goto cleanup; + + /* Jump to here for any other kind of fatal error. The "rc" variable + ** should hold the error number. + */ +abort_due_to_err: + sqliteSetString(pzErrMsg, sqliteErrStr(rc), 0); + goto cleanup; /* Jump to here if a operator is encountered that requires more stack ** operands than are currently available on the stack. diff --git a/src/vdbe.h b/src/vdbe.h index d8459bef71..e939232cc0 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.18 2001/08/19 18:19:46 drh Exp $ +** $Id: vdbe.h,v 1.19 2001/09/13 13:46:57 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -71,122 +71,133 @@ typedef struct VdbeOp VdbeOp; ** The source tree contains an AWK script named renumberOps.awk that ** can be used to renumber these opcodes when new opcodes are inserted. */ -#define OP_OpenIdx 1 -#define OP_OpenTbl 2 -#define OP_Close 3 -#define OP_Fetch 4 -#define OP_Fcnt 5 -#define OP_New 6 -#define OP_Put 7 -#define OP_Distinct 8 -#define OP_Found 9 -#define OP_NotFound 10 -#define OP_Delete 11 -#define OP_Field 12 -#define OP_KeyAsData 13 -#define OP_Key 14 -#define OP_FullKey 15 -#define OP_Rewind 16 -#define OP_Next 17 +#define OP_Transaction 1 +#define OP_Commit 2 +#define OP_Rollback 3 -#define OP_Destroy 18 -#define OP_Reorganize 19 +#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_BeginIdx 20 -#define OP_NextIdx 21 -#define OP_PutIdx 22 -#define OP_DeleteIdx 23 +#define OP_Destroy 21 +#define OP_CreateIndex 22 +#define OP_CreateTable 23 +#define OP_Reorganize 24 -#define OP_MemLoad 24 -#define OP_MemStore 25 +#define OP_BeginIdx 25 +#define OP_NextIdx 26 +#define OP_PutIdx 27 +#define OP_DeleteIdx 28 -#define OP_ListOpen 26 -#define OP_ListWrite 27 -#define OP_ListRewind 28 -#define OP_ListRead 29 -#define OP_ListClose 30 +#define OP_MemLoad 29 +#define OP_MemStore 30 -#define OP_SortOpen 31 -#define OP_SortPut 32 -#define OP_SortMakeRec 33 -#define OP_SortMakeKey 34 -#define OP_Sort 35 -#define OP_SortNext 36 -#define OP_SortKey 37 -#define OP_SortCallback 38 -#define OP_SortClose 39 +#define OP_ListOpen 31 +#define OP_ListWrite 32 +#define OP_ListRewind 33 +#define OP_ListRead 34 +#define OP_ListClose 35 -#define OP_FileOpen 40 -#define OP_FileRead 41 -#define OP_FileField 42 -#define OP_FileClose 43 +#define OP_SortOpen 36 +#define OP_SortPut 37 +#define OP_SortMakeRec 38 +#define OP_SortMakeKey 39 +#define OP_Sort 40 +#define OP_SortNext 41 +#define OP_SortKey 42 +#define OP_SortCallback 43 +#define OP_SortClose 44 -#define OP_AggReset 44 -#define OP_AggFocus 45 -#define OP_AggIncr 46 -#define OP_AggNext 47 -#define OP_AggSet 48 -#define OP_AggGet 49 +#define OP_FileOpen 45 +#define OP_FileRead 46 +#define OP_FileField 47 +#define OP_FileClose 48 -#define OP_SetInsert 50 -#define OP_SetFound 51 -#define OP_SetNotFound 52 -#define OP_SetClear 53 +#define OP_AggReset 49 +#define OP_AggFocus 50 +#define OP_AggIncr 51 +#define OP_AggNext 52 +#define OP_AggSet 53 +#define OP_AggGet 54 -#define OP_MakeRecord 54 -#define OP_MakeKey 55 +#define OP_SetInsert 55 +#define OP_SetFound 56 +#define OP_SetNotFound 57 +#define OP_SetClear 58 -#define OP_Goto 56 -#define OP_If 57 -#define OP_Halt 58 +#define OP_MakeRecord 59 +#define OP_MakeKey 60 +#define OP_MakeIdxKey 61 -#define OP_ColumnCount 59 -#define OP_ColumnName 60 -#define OP_Callback 61 +#define OP_Goto 62 +#define OP_If 63 +#define OP_Halt 64 -#define OP_Integer 62 -#define OP_String 63 -#define OP_Null 64 -#define OP_Pop 65 -#define OP_Dup 66 -#define OP_Pull 67 +#define OP_ColumnCount 65 +#define OP_ColumnName 66 +#define OP_Callback 67 -#define OP_Add 68 -#define OP_AddImm 69 -#define OP_Subtract 70 -#define OP_Multiply 71 -#define OP_Divide 72 -#define OP_Min 73 -#define OP_Max 74 -#define OP_Like 75 -#define OP_Glob 76 -#define OP_Eq 77 -#define OP_Ne 78 -#define OP_Lt 79 -#define OP_Le 80 -#define OP_Gt 81 -#define OP_Ge 82 -#define OP_IsNull 83 -#define OP_NotNull 84 -#define OP_Negative 85 -#define OP_And 86 -#define OP_Or 87 -#define OP_Not 88 -#define OP_Concat 89 -#define OP_Noop 90 +#define OP_Integer 68 +#define OP_String 69 +#define OP_Null 70 +#define OP_Pop 71 +#define OP_Dup 72 +#define OP_Pull 73 -#define OP_Strlen 91 -#define OP_Substr 92 +#define OP_Add 74 +#define OP_AddImm 75 +#define OP_Subtract 76 +#define OP_Multiply 77 +#define OP_Divide 78 +#define OP_Min 79 +#define OP_Max 80 +#define OP_Like 81 +#define OP_Glob 82 +#define OP_Eq 83 +#define OP_Ne 84 +#define OP_Lt 85 +#define OP_Le 86 +#define OP_Gt 87 +#define OP_Ge 88 +#define OP_IsNull 89 +#define OP_NotNull 90 +#define OP_Negative 91 +#define OP_And 92 +#define OP_Or 93 +#define OP_Not 94 +#define OP_Concat 95 +#define OP_Noop 96 -#define OP_MAX 93 +#define OP_Strlen 97 +#define OP_Substr 98 + +#define OP_MAX 98 /* ** Prototypes for the VDBE interface. See comments on the implementation ** for a description of what each of these routines does. */ Vdbe *sqliteVdbeCreate(sqlite*); +void sqliteVdbeCreateCallback(Vdbe*, int*); +void sqliteVdbeTableRootAddr(Vdbe*, int*); +void sqliteVdbeIndexRootAddr(Vdbe*, int*); int sqliteVdbeAddOp(Vdbe*,int,int,int,const char*,int); int sqliteVdbeAddOpList(Vdbe*, int nOp, VdbeOp const *aOp); +void sqliteVdbeChangeP1(Vdbe*, int addr, int P1); void sqliteVdbeChangeP3(Vdbe*, int addr, const char *zP1, int N); void sqliteVdbeDequoteP3(Vdbe*, int addr); int sqliteVdbeMakeLabel(Vdbe*); diff --git a/src/where.c b/src/where.c index 9f8a92433a..4b0e8ed8b0 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.15 2001/08/19 18:19:46 drh Exp $ +** $Id: where.c,v 1.16 2001/09/13 13:46:57 drh Exp $ */ #include "sqliteInt.h" @@ -297,9 +297,11 @@ WhereInfo *sqliteWhereBegin( /* Open all tables in the pTabList and all indices in aIdx[]. */ for(i=0; inId; i++){ - sqliteVdbeAddOp(v, OP_OpenTbl, base+i, 0, pTabList->a[i].pTab->zName, 0); + sqliteVdbeAddOp(v, OP_Open, base+i, pTabList->a[i].pTab->tnum, + pTabList->a[i].pTab->zName, 0); if( inId+i, 0, aIdx[i]->zName,0); + sqliteVdbeAddOp(v, OP_Open, base+pTabList->nId+i, aIdx[i]->tnum + aIdx[i]->zName, 0); } } memcpy(pWInfo->aIdx, aIdx, sizeof(aIdx));