The code is in place to replace GDBM with BTree. But I have not yet
attempted to compile it. I am sure the code contains bugs. (CVS 238) FossilOrigin-Name: 6ecc8b20d4f402f45f03d46d8d4fa40dea666e97
This commit is contained in:
parent
5c4d9703d9
commit
5e00f6c7d5
45
manifest
45
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
|
||||
|
@ -1 +1 @@
|
||||
2e6aff980287825b59d2ebb7005bb08dd601ff1c
|
||||
6ecc8b20d4f402f45f03d46d8d4fa40dea666e97
|
125
src/btree.c
125
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,
|
||||
|
@ -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);
|
||||
|
323
src/build.c
323
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; i<N_HASH; i++){
|
||||
Table *pTable, *pNext;
|
||||
for(pTable = apTblHash[i]; pTable; pTable=pNext){
|
||||
pNext = pTable->pHash;
|
||||
if( pTable->isDelete ){
|
||||
sqliteDeleteTable(db, pTable);
|
||||
}else if( pTable->isCommit==0 ){
|
||||
pTable->isCommit = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(i=0; i<N_HASH; i++){
|
||||
Index *pIndex, *pNext;
|
||||
for(pIndex = apIdxHash[i]; pIndex; pIndex=pNext){
|
||||
pNext = pIndex->pHash;
|
||||
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; i<N_HASH; i++){
|
||||
Table *pTable, *pNext;
|
||||
for(pTable = apTblHash[i]; pTable; pTable=pNext){
|
||||
pNext = pTable->pHash;
|
||||
if( !pTable->isCommit ){
|
||||
sqliteDeleteTable(db, pTable);
|
||||
}else if( pTable->isDelete ){
|
||||
pTable->isDelete = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(i=0; i<N_HASH; i++){
|
||||
Index *pIndex, *pNext;
|
||||
for(pIndex = apIdxHash[i]; pIndex; pIndex=pNext){
|
||||
pNext = pIndex->pHash;
|
||||
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; i<pIndex->nColumn; 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; i<pTab->nCol; 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; j<pIdx->nColumn; 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;
|
||||
}
|
||||
|
676
src/dbbebtree.c
Normal file
676
src/dbbebtree.c
Normal file
@ -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; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
|
||||
if( stat(azDirs[i], &buf)==0 && S_ISDIR(buf.st_mode)
|
||||
&& S_IWUSR(buf.st_mode) ){
|
||||
return azDirs[i];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new table cursor. Write a pointer to the corresponding
|
||||
** DbbeCursor structure into *ppCursr. Return an integer success
|
||||
** code:
|
||||
**
|
||||
** SQLITE_OK It worked!
|
||||
**
|
||||
** SQLITE_NOMEM sqliteMalloc() failed
|
||||
**
|
||||
** SQLITE_PERM Attempt to access a file for which file
|
||||
** access permission is denied
|
||||
**
|
||||
** SQLITE_BUSY Another thread or process is already using
|
||||
** the corresponding file and has that file locked.
|
||||
**
|
||||
** SQLITE_READONLY The current thread already has this file open
|
||||
** readonly but you are trying to open for writing.
|
||||
** (This can happen if a SELECT callback tries to
|
||||
** do an UPDATE or DELETE.)
|
||||
**
|
||||
** If the table does not previously exist and writeable is TRUE then
|
||||
** a new table is created. If zTable is 0 or "", then a temporary
|
||||
** database table is created and a cursor to that temporary file is
|
||||
** opened. The temporary file will be deleted when it is closed.
|
||||
*/
|
||||
static int sqliteBtbeOpenCursor(
|
||||
Dbbe *pDbbe, /* The database the table belongs to */
|
||||
const char *zTable, /* The SQL name of the file to be opened */
|
||||
int writeable, /* True to open for writing */
|
||||
int intKeyOnly, /* True if only integer keys are used */
|
||||
DbbeCursor **ppCursr /* Write the resulting table pointer here */
|
||||
){
|
||||
char *zFile; /* Name of the table file */
|
||||
DbbeCursor *pCursr; /* The new table cursor */
|
||||
int rc = SQLITE_OK; /* Return value */
|
||||
int rw_mask; /* Permissions mask for opening a table */
|
||||
int mode; /* Mode for opening a table */
|
||||
Dbbex *pBe = (Dbbex*)pDbbe;
|
||||
|
||||
*ppCursr = 0;
|
||||
if( pBe->pCur==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 */
|
20
src/delete.c
20
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; j<pIdx->nColumn; 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);
|
||||
|
11
src/insert.c
11
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);
|
||||
|
41
src/main.c
41
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; i<N_HASH; i++){
|
||||
Table *pNext, *pList = db->apTblHash[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);
|
||||
|
95
src/pager.c
95
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; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
|
||||
if( stat(azDirs[i], &buf)==0 && S_ISDIR(buf.st_mode)
|
||||
&& S_IWUSR(buf.st_mode) ){
|
||||
return azDirs[i];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a new page cache and put a pointer to the page cache in *ppPager.
|
||||
** The file to be cached need not exist. The file is not opened until
|
||||
** The file to be cached need not exist. The file is not locked until
|
||||
** the first call to sqlitepager_get() and is only held open until the
|
||||
** last page is released using sqlitepager_unref().
|
||||
*/
|
||||
@ -472,12 +498,33 @@ int sqlitepager_open(
|
||||
Pager *pPager;
|
||||
int nameLen;
|
||||
int fd;
|
||||
int tempFile;
|
||||
int readOnly = 0;
|
||||
char zTemp[300];
|
||||
|
||||
*ppPager = 0;
|
||||
if( sqlite_malloc_failed ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
fd = open(zFilename, O_RDWR|O_CREAT, 0644);
|
||||
if( zFilename ){
|
||||
fd = open(zFilename, O_RDWR|O_CREAT, 0644);
|
||||
if( fd<0 ){
|
||||
fd = open(zFilename, O_RDONLY, 0);
|
||||
readOnly = 1;
|
||||
}
|
||||
tempFile = 0;
|
||||
}else{
|
||||
int cnt = 8;
|
||||
char *zDir = findTempDir();
|
||||
if( zDir==0 ) return SQLITE_CANTOPEN;
|
||||
do{
|
||||
cnt--;
|
||||
sprintf(zTemp,"%s/_sqlite_%u",(unsigned)sqliteRandomInteger());
|
||||
fd = open(zTemp, O_RDWR|O_CREAT|O_EXCL, 0600);
|
||||
}while( cnt>0 && 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.
|
||||
*/
|
||||
|
@ -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
|
||||
|
12
src/select.c
12
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;
|
||||
|
@ -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 <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -40,7 +40,7 @@
|
||||
# include <readline/readline.h>
|
||||
# include <readline/history.h>
|
||||
#else
|
||||
# define readline getline
|
||||
# define readline(p) getline(p,stdin)
|
||||
# define add_history(X)
|
||||
#endif
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
22
src/update.c
22
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; i<nIdx; i++){
|
||||
sqliteVdbeAddOp(v, OP_OpenIdx, base+i+1, 1, apIdx[i]->zName, 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; j<pIdx->nColumn; 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; i<pTab->nCol; 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; j<pIdx->nColumn; 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);
|
||||
|
30
src/util.c
30
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 <stdarg.h>
|
||||
@ -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;
|
||||
}
|
||||
|
4930
src/vdbe.c
4930
src/vdbe.c
File diff suppressed because it is too large
Load Diff
199
src/vdbe.h
199
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*);
|
||||
|
@ -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; i<pTabList->nId; 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( i<ARRAYSIZE(aIdx) && aIdx[i]!=0 ){
|
||||
sqliteVdbeAddOp(v, OP_OpenIdx, base+pTabList->nId+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));
|
||||
|
Loading…
Reference in New Issue
Block a user