The BTree changes are now integrated and the whole thing compiles and
links. I have not yet tried to run it, though. (CVS 239) FossilOrigin-Name: a0a1e701abc52a164d9b09a5426eb12af1fe6a4c
This commit is contained in:
parent
5e00f6c7d5
commit
be0072d249
23
Makefile.in
23
Makefile.in
@ -47,7 +47,7 @@ LIBREADLINE = @TARGET_READLINE_LIBS@
|
||||
|
||||
# Object files for the SQLite library.
|
||||
#
|
||||
LIBOBJ = btree.o build.o dbbe.o dbbegdbm.o dbbemem.o delete.o expr.o insert.o \
|
||||
LIBOBJ = btree.o build.o delete.o expr.o insert.o \
|
||||
main.o pager.o parse.o printf.o random.o select.o table.o \
|
||||
tokenize.o update.o util.o vdbe.o where.o tclsqlite.o
|
||||
|
||||
@ -55,16 +55,14 @@ LIBOBJ = btree.o build.o dbbe.o dbbegdbm.o dbbemem.o delete.o expr.o insert.o \
|
||||
#
|
||||
SRC = \
|
||||
$(TOP)/src/btree.c \
|
||||
$(TOP)/src/btree.h \
|
||||
$(TOP)/src/build.c \
|
||||
$(TOP)/src/dbbe.c \
|
||||
$(TOP)/src/dbbe.h \
|
||||
$(TOP)/src/dbbegdbm.c \
|
||||
$(TOP)/src/dbbemem.c \
|
||||
$(TOP)/src/delete.c \
|
||||
$(TOP)/src/expr.c \
|
||||
$(TOP)/src/insert.c \
|
||||
$(TOP)/src/main.c \
|
||||
$(TOP)/src/pager.c \
|
||||
$(TOP)/src/pager.h \
|
||||
$(TOP)/src/parse.y \
|
||||
$(TOP)/src/printf.c \
|
||||
$(TOP)/src/random.c \
|
||||
@ -121,30 +119,21 @@ lemon: $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c
|
||||
#
|
||||
HDR = \
|
||||
sqlite.h \
|
||||
$(TOP)/src/btree.h \
|
||||
$(TOP)/src/sqliteInt.h \
|
||||
$(TOP)/src/dbbe.h \
|
||||
$(TOP)/src/vdbe.h \
|
||||
parse.h
|
||||
|
||||
btree.o: $(TOP)/src/btree.c $(HDR)
|
||||
btree.o: $(TOP)/src/btree.c $(HDR) $(TOP)/src/pager.h
|
||||
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/btree.c
|
||||
|
||||
build.o: $(TOP)/src/build.c $(HDR)
|
||||
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/build.c
|
||||
|
||||
dbbe.o: $(TOP)/src/dbbe.c $(HDR)
|
||||
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/dbbe.c
|
||||
|
||||
dbbegdbm.o: $(TOP)/src/dbbegdbm.c $(HDR)
|
||||
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/dbbegdbm.c
|
||||
|
||||
dbbemem.o: $(TOP)/src/dbbemem.c $(HDR)
|
||||
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/dbbemem.c
|
||||
|
||||
main.o: $(TOP)/src/main.c $(HDR)
|
||||
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/main.c
|
||||
|
||||
pager.o: $(TOP)/src/pager.c $(HDR)
|
||||
pager.o: $(TOP)/src/pager.c $(HDR) $(TOP)/src/pager.h
|
||||
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/pager.c
|
||||
|
||||
parse.o: parse.c $(HDR)
|
||||
|
45
manifest
45
manifest
@ -1,7 +1,7 @@
|
||||
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
|
||||
C The\sBTree\schanges\sare\snow\sintegrated\sand\sthe\swhole\sthing\scompiles\sand\nlinks.\sI\shave\snot\syet\stried\sto\srun\sit,\sthough.\s(CVS\s239)
|
||||
D 2001-09-13T14:46:10
|
||||
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
|
||||
F Makefile.in 9eea999e1d95531de4dd0a96a6ecf6ba0027b05b
|
||||
F Makefile.in 7ecb2370b5cb34d390af1fcb3118ea6d84a253ca
|
||||
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
|
||||
F VERSION 00453ed53ff28fe8e701e1609e81f1b9df12adab
|
||||
F configure d2051345f49f7e48604423da26e086a745c86a47 x
|
||||
@ -13,15 +13,10 @@ F notes/notes2.txt 7e3fafd5e25906c1fe1e95f13b089aa398ca403e
|
||||
F notes/notes2b.txt 1c17a5b7f6b44a75cd3eb98ed2c24db1eefb06c3
|
||||
F notes/notes3.txt 71e47be517e3d2578b3b9343a45b772d43b7ba16
|
||||
F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4
|
||||
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 bee9e20720436b74d7116735389ac81f7aa1d684
|
||||
F src/btree.c 9f22b51681bcc0026ea300134e4e2f1f40fc0800
|
||||
F src/btree.h 2427961c702dd0755ec70f529cf3db32707d689b
|
||||
F src/build.c 58020177a1b96455284899774442833a81c5ae9c
|
||||
F src/delete.c 769a28cf35ab75df4b2416982d8369520b28f3c5
|
||||
F src/ex/README b745b00acce2d892f60c40111dacdfc48e0c1c7a
|
||||
F src/ex/db.c f1419ae6c93e40b5ac6e39fe7efd95d868e6f9d7
|
||||
F src/ex/db.h 3f2933ee20c147fe494835786e4c6f3a562def4e
|
||||
@ -30,20 +25,20 @@ F src/ex/dbbemird.c b00aef85656fa0a101dac2c32e12922ad106715a
|
||||
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 51d21e65eba5b4bef017fec6af79266bc0065824
|
||||
F src/main.c afdb7ecb5de4e0eb0d69d1e0d5db7ad070c8e0d6
|
||||
F src/expr.c 83b6a7ed4cf502249f192b698517e9a9b8f05303
|
||||
F src/insert.c 1072c0dd7782c17af735df37f447630d4d577ba1
|
||||
F src/main.c b7a2da8375ca61d4464610368608e7fd0566b950
|
||||
F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c
|
||||
F src/pager.c 1928e68b5c00c24749b71f41eeabacd7217337a5
|
||||
F src/pager.c 05a2177c99a835c3efec1e4187556e2e29311d4a
|
||||
F src/pager.h 238aa88bafe33911bf9b0b365f35afd0a261cd46
|
||||
F src/parse.y 8fc096948994a7ffbf61ba13129cc589f794a9cb
|
||||
F src/printf.c b1e22a47be8cdf707815647239991e08e8cb69f9
|
||||
F src/random.c b36c3f57dc80c8f354e6bfbf39cf1e1de021d54a
|
||||
F src/select.c e5977916f59a79d67c40fae11e2c5736cddd1fa8
|
||||
F src/select.c 391921bcc612e15efecc451e42a93697fb826e46
|
||||
F src/shell.c 1fcdf8c4180098bcfdee12501e01b4c8eb21d726
|
||||
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
|
||||
F src/sqlite.h.in 8faa2fed0513d188ced16e5f9094e57694594e70
|
||||
F src/sqliteInt.h 1a3a7ac6db97c15ec6e80ee8df8a8f8eadf70316
|
||||
F src/sqliteInt.h f8b8417dde95419d2e4ab683ce4ac418f96913bd
|
||||
F src/table.c adcaf074f6c1075e86359174e68701fa2acfc4d6
|
||||
F src/tclsqlite.c d328970848c028e13e61e173bef79adcc379568a
|
||||
F src/test1.c abb3cb427e735ae87e6533f5b3b7164b7da91bc4
|
||||
@ -52,12 +47,12 @@ F src/test3.c 147b42ec368a10e9f267e7466d30c46e76d7f278
|
||||
F src/tokenize.c 0118b57702cb6550769316e8443b06760b067acf
|
||||
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 src/vdbe.c f502b592d1cc94f68e9e4b3fa130361cb3038b0c
|
||||
F src/vdbe.h 6ee941ecd78b7b224607517fd060d6547910dc10
|
||||
F src/where.c b9f6d2c029983e9db9521474c876f9a039e7fb6c
|
||||
F test/all.test 21d55a97e39e7ec5776751dc9dd8b1b51ef4a048
|
||||
F test/btree.test 5e1eeb03cda22161eec827dc5224ce6c500eaaf9
|
||||
F test/btree2.test a66add2093843d0e5617fed6924002667832f279
|
||||
F test/btree2.test a3c9ff1e4490357dd15c9a41f8aefd02f6e32fdc
|
||||
F test/copy.test b77a1214bd7756f2849d5c4fa6e715c0ff0c34eb
|
||||
F test/dbbe.test a022fe2d983848f786e17ef1fc6809cfd37fb02c
|
||||
F test/delete.test 50b9b1f06c843d591741dba7869433a105360dbf
|
||||
@ -113,7 +108,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f
|
||||
F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f
|
||||
F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2
|
||||
F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad
|
||||
P 2e6aff980287825b59d2ebb7005bb08dd601ff1c
|
||||
R 2654e388e3b472efb037b78486fd2a60
|
||||
P 6ecc8b20d4f402f45f03d46d8d4fa40dea666e97
|
||||
R 9f3dc1f51d7a55174d7eb7f5f17c9756
|
||||
U drh
|
||||
Z bd85e26e61c9c38cc5377a9e930cda9b
|
||||
Z 89f03479b34b99fd737e2528660926bd
|
||||
|
@ -1 +1 @@
|
||||
6ecc8b20d4f402f45f03d46d8d4fa40dea666e97
|
||||
a0a1e701abc52a164d9b09a5426eb12af1fe6a4c
|
@ -21,7 +21,7 @@
|
||||
** http://www.hwaci.com/drh/
|
||||
**
|
||||
*************************************************************************
|
||||
** $Id: btree.c,v 1.22 2001/09/13 13:46:56 drh Exp $
|
||||
** $Id: btree.c,v 1.23 2001/09/13 14:46:10 drh Exp $
|
||||
**
|
||||
** This file implements a external (disk-based) database using BTrees.
|
||||
** For a detailed discussion of BTrees, refer to
|
||||
@ -726,7 +726,7 @@ int sqliteBtreeBeginTrans(Btree *pBt){
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if( !sqlitepager_isreadonly(pBt) ){
|
||||
if( !sqlitepager_isreadonly(pBt->pPager) ){
|
||||
rc = sqlitepager_write(pBt->page1);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
@ -2012,7 +2012,7 @@ balance_cleanup:
|
||||
*/
|
||||
int sqliteBtreeInsert(
|
||||
BtCursor *pCur, /* Insert data into the table of this cursor */
|
||||
const void *pKey, int nKey, /* The key of the new record */
|
||||
const void *pKey, int nKey, /* The key of the new record */
|
||||
const void *pData, int nData /* The data of the new record */
|
||||
){
|
||||
Cell newCell;
|
||||
|
@ -24,8 +24,10 @@
|
||||
** This header file defines the interface that the sqlite B-Tree file
|
||||
** subsystem.
|
||||
**
|
||||
** @(#) $Id: btree.h,v 1.11 2001/09/13 13:46:56 drh Exp $
|
||||
** @(#) $Id: btree.h,v 1.12 2001/09/13 14:46:10 drh Exp $
|
||||
*/
|
||||
#ifndef _BTREE_H_
|
||||
#define _BTREE_H_
|
||||
|
||||
typedef struct Btree Btree;
|
||||
typedef struct BtCursor BtCursor;
|
||||
@ -62,6 +64,8 @@ int sqliteBtreeUpdateMeta(Btree*, int*);
|
||||
#ifdef SQLITE_TEST
|
||||
int sqliteBtreePageDump(Btree*, int, int);
|
||||
int sqliteBtreeCursorDump(BtCursor*, int*);
|
||||
Pager *sqliteBtreePager(Btree*);
|
||||
struct Pager *sqliteBtreePager(Btree*);
|
||||
char *sqliteBtreeSanityCheck(Btree*, int*, int);
|
||||
#endif
|
||||
|
||||
#endif /* _BTREE_H_ */
|
||||
|
83
src/build.c
83
src/build.c
@ -33,7 +33,7 @@
|
||||
** COPY
|
||||
** VACUUM
|
||||
**
|
||||
** $Id: build.c,v 1.29 2001/09/13 13:46:56 drh Exp $
|
||||
** $Id: build.c,v 1.30 2001/09/13 14:46:10 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -49,17 +49,18 @@
|
||||
*/
|
||||
void sqliteExec(Parse *pParse){
|
||||
int rc = SQLITE_OK;
|
||||
sqlite *db = pParse->db;
|
||||
if( sqlite_malloc_failed ) return;
|
||||
if( pParse->pVdbe ){
|
||||
if( pParse->explain ){
|
||||
rc = sqliteVdbeList(pParse->pVdbe, pParse->xCallback, pParse->pArg,
|
||||
&pParse->zErrMsg);
|
||||
}else{
|
||||
FILE *trace = (pParse->db->flags & SQLITE_VdbeTrace)!=0 ? stderr : 0;
|
||||
FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stderr : 0;
|
||||
sqliteVdbeTrace(pParse->pVdbe, trace);
|
||||
rc = sqliteVdbeExec(pParse->pVdbe, pParse->xCallback, pParse->pArg,
|
||||
&pParse->zErrMsg, pParse->db->pBusyArg,
|
||||
pParse->db->xBusyCallback);
|
||||
&pParse->zErrMsg, db->pBusyArg,
|
||||
db->xBusyCallback);
|
||||
}
|
||||
sqliteVdbeDelete(pParse->pVdbe);
|
||||
pParse->pVdbe = 0;
|
||||
@ -258,7 +259,7 @@ void sqliteCommitInternalChanges(sqlite *db){
|
||||
if( (db->flags & SQLITE_InternChanges)==0 ) return;
|
||||
for(i=0; i<N_HASH; i++){
|
||||
Table *pTable, *pNext;
|
||||
for(pTable = apTblHash[i]; pTable; pTable=pNext){
|
||||
for(pTable = db->apTblHash[i]; pTable; pTable=pNext){
|
||||
pNext = pTable->pHash;
|
||||
if( pTable->isDelete ){
|
||||
sqliteDeleteTable(db, pTable);
|
||||
@ -269,7 +270,7 @@ void sqliteCommitInternalChanges(sqlite *db){
|
||||
}
|
||||
for(i=0; i<N_HASH; i++){
|
||||
Index *pIndex, *pNext;
|
||||
for(pIndex = apIdxHash[i]; pIndex; pIndex=pNext){
|
||||
for(pIndex = db->apIdxHash[i]; pIndex; pIndex=pNext){
|
||||
pNext = pIndex->pHash;
|
||||
if( pIndex->isDelete ){
|
||||
sqliteUnlinkAndDeleteIndex(db, pIndex);
|
||||
@ -294,7 +295,7 @@ void sqliteRollbackInternalChanges(sqlite *db){
|
||||
if( (db->flags & SQLITE_InternChanges)==0 ) return;
|
||||
for(i=0; i<N_HASH; i++){
|
||||
Table *pTable, *pNext;
|
||||
for(pTable = apTblHash[i]; pTable; pTable=pNext){
|
||||
for(pTable = db->apTblHash[i]; pTable; pTable=pNext){
|
||||
pNext = pTable->pHash;
|
||||
if( !pTable->isCommit ){
|
||||
sqliteDeleteTable(db, pTable);
|
||||
@ -305,7 +306,7 @@ void sqliteRollbackInternalChanges(sqlite *db){
|
||||
}
|
||||
for(i=0; i<N_HASH; i++){
|
||||
Index *pIndex, *pNext;
|
||||
for(pIndex = apIdxHash[i]; pIndex; pIndex=pNext){
|
||||
for(pIndex = db->apIdxHash[i]; pIndex; pIndex=pNext){
|
||||
pNext = pIndex->pHash;
|
||||
if( !pIndex->isCommit ){
|
||||
sqliteUnlinkAndDeleteIndex(db, pIndex);
|
||||
@ -343,11 +344,12 @@ char *sqliteTableNameFromToken(Token *pName){
|
||||
void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){
|
||||
Table *pTable;
|
||||
char *zName;
|
||||
sqlite *db = pParse->db;
|
||||
|
||||
pParse->sFirstToken = *pStart;
|
||||
zName = sqliteTableNameFromToken(pName);
|
||||
if( zName==0 ) return;
|
||||
pTable = sqliteFindTable(pParse->db, zName);
|
||||
pTable = sqliteFindTable(db, zName);
|
||||
if( pTable!=0 ){
|
||||
sqliteSetNString(&pParse->zErrMsg, "table ", 0, pName->z, pName->n,
|
||||
" already exists", 0, 0);
|
||||
@ -355,7 +357,7 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){
|
||||
pParse->nErr++;
|
||||
return;
|
||||
}
|
||||
if( sqliteFindIndex(pParse->db, zName) ){
|
||||
if( sqliteFindIndex(db, zName) ){
|
||||
sqliteSetString(&pParse->zErrMsg, "there is already an index named ",
|
||||
zName, 0);
|
||||
sqliteFree(zName);
|
||||
@ -369,9 +371,9 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){
|
||||
pTable->nCol = 0;
|
||||
pTable->aCol = 0;
|
||||
pTable->pIndex = 0;
|
||||
if( pParse->pNewTable ) sqliteDeleteTable(pParse->db, pParse->pNewTable);
|
||||
if( pParse->pNewTable ) sqliteDeleteTable(db, pParse->pNewTable);
|
||||
pParse->pNewTable = pTable;
|
||||
if( !pParse->initFlag && (pParse->db->flags & SQLITE_InTrans)==0 ){
|
||||
if( !pParse->initFlag && (db->flags & SQLITE_InTrans)==0 ){
|
||||
Vdbe *v = sqliteGetVdbe(pParse);
|
||||
if( v ){
|
||||
sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0);
|
||||
@ -442,6 +444,7 @@ void sqliteAddDefaultValue(Parse *pParse, Token *pVal, int minusFlag){
|
||||
void sqliteEndTable(Parse *pParse, Token *pEnd){
|
||||
Table *p;
|
||||
int h;
|
||||
sqlite *db = pParse->db;
|
||||
|
||||
if( pEnd==0 || pParse->nErr || sqlite_malloc_failed ) return;
|
||||
p = pParse->pNewTable;
|
||||
@ -451,10 +454,10 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){
|
||||
*/
|
||||
if( pParse->explain==0 ){
|
||||
h = sqliteHashNoCase(p->zName, 0) % N_HASH;
|
||||
p->pHash = pParse->db->apTblHash[h];
|
||||
pParse->db->apTblHash[h] = p;
|
||||
p->pHash = db->apTblHash[h];
|
||||
db->apTblHash[h] = p;
|
||||
pParse->pNewTable = 0;
|
||||
pParse->db->nTable++;
|
||||
db->nTable++;
|
||||
db->flags |= SQLITE_InternChanges;
|
||||
}
|
||||
|
||||
@ -484,7 +487,7 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){
|
||||
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 ){
|
||||
if( (db->flags & SQLITE_InTrans)==0 ){
|
||||
sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
@ -515,7 +518,6 @@ Table *sqliteTableFromToken(Parse *pParse, Token *pTok){
|
||||
*/
|
||||
void sqliteDropTable(Parse *pParse, Token *pName){
|
||||
Table *pTable;
|
||||
int h;
|
||||
Vdbe *v;
|
||||
int base;
|
||||
|
||||
@ -569,7 +571,7 @@ void sqliteDropTable(Parse *pParse, Token *pName){
|
||||
*/
|
||||
if( !pParse->explain ){
|
||||
pTable->isDelete = 1;
|
||||
db->flags |= SQLITE_InternChanges;
|
||||
pParse->db->flags |= SQLITE_InternChanges;
|
||||
}
|
||||
}
|
||||
|
||||
@ -595,6 +597,7 @@ void sqliteCreateIndex(
|
||||
char *zName = 0;
|
||||
int i, j, h;
|
||||
Token nullId; /* Fake token for an empty ID list */
|
||||
sqlite *db = pParse->db;
|
||||
|
||||
if( pParse->nErr || sqlite_malloc_failed ) goto exit_create_index;
|
||||
|
||||
@ -625,13 +628,13 @@ void sqliteCreateIndex(
|
||||
sqliteSetString(&zName, pTab->zName, "__primary_key", 0);
|
||||
}
|
||||
if( zName==0 ) goto exit_create_index;
|
||||
if( sqliteFindIndex(pParse->db, zName) ){
|
||||
if( sqliteFindIndex(db, zName) ){
|
||||
sqliteSetString(&pParse->zErrMsg, "index ", zName,
|
||||
" already exists", 0);
|
||||
pParse->nErr++;
|
||||
goto exit_create_index;
|
||||
}
|
||||
if( sqliteFindTable(pParse->db, zName) ){
|
||||
if( sqliteFindTable(db, zName) ){
|
||||
sqliteSetString(&pParse->zErrMsg, "there is already a table named ",
|
||||
zName, 0);
|
||||
pParse->nErr++;
|
||||
@ -684,8 +687,8 @@ void sqliteCreateIndex(
|
||||
*/
|
||||
if( pParse->explain==0 ){
|
||||
h = sqliteHashNoCase(pIndex->zName, 0) % N_HASH;
|
||||
pIndex->pHash = pParse->db->apIdxHash[h];
|
||||
pParse->db->apIdxHash[h] = pIndex;
|
||||
pIndex->pHash = db->apIdxHash[h];
|
||||
db->apIdxHash[h] = pIndex;
|
||||
pIndex->pNext = pTab->pIndex;
|
||||
pTab->pIndex = pIndex;
|
||||
db->flags |= SQLITE_InternChanges;
|
||||
@ -721,7 +724,7 @@ void sqliteCreateIndex(
|
||||
|
||||
v = sqliteGetVdbe(pParse);
|
||||
if( v==0 ) goto exit_create_index;
|
||||
if( pTable!=0 && (pParse->db->flags & SQLITE_InTrans)==0 ){
|
||||
if( pTable!=0 && (db->flags & SQLITE_InTrans)==0 ){
|
||||
sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Open, 0, pTab->tnum, pTab->zName, 0);
|
||||
@ -738,7 +741,7 @@ void sqliteCreateIndex(
|
||||
lbl1 = sqliteVdbeMakeLabel(v);
|
||||
lbl2 = sqliteVdbeMakeLabel(v);
|
||||
sqliteVdbeAddOp(v, OP_Next, 0, lbl2, 0, lbl1);
|
||||
sqliteVdbeAddOp(v, OP_GetRecno, 0, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Recno, 0, 0, 0, 0);
|
||||
for(i=0; i<pIndex->nColumn; i++){
|
||||
sqliteVdbeAddOp(v, OP_Column, 0, pIndex->aiColumn[i], 0, 0);
|
||||
}
|
||||
@ -748,7 +751,7 @@ void sqliteCreateIndex(
|
||||
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 ){
|
||||
if( pTable!=0 && (db->flags & SQLITE_InTrans)==0 ){
|
||||
sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
@ -773,11 +776,12 @@ void sqliteDropIndex(Parse *pParse, Token *pName){
|
||||
Index *pIndex;
|
||||
char *zName;
|
||||
Vdbe *v;
|
||||
sqlite *db = pParse->db;
|
||||
|
||||
if( pParse->nErr || sqlite_malloc_failed ) return;
|
||||
zName = sqliteTableNameFromToken(pName);
|
||||
if( zName==0 ) return;
|
||||
pIndex = sqliteFindIndex(pParse->db, zName);
|
||||
pIndex = sqliteFindIndex(db, zName);
|
||||
sqliteFree(zName);
|
||||
if( pIndex==0 ){
|
||||
sqliteSetNString(&pParse->zErrMsg, "no such index: ", 0,
|
||||
@ -796,19 +800,19 @@ void sqliteDropIndex(Parse *pParse, Token *pName){
|
||||
{ OP_Dup, 0, 0, 0},
|
||||
{ OP_Column, 0, 1, 0},
|
||||
{ OP_Ne, 0, ADDR(2), 0},
|
||||
{ OP_Key, 0, 0, 0},
|
||||
{ OP_Recno, 0, 0, 0},
|
||||
{ OP_Delete, 0, 0, 0},
|
||||
{ OP_Destroy, 0, 0, 0}, /* 8 */
|
||||
{ OP_Close, 0, 0, 0},
|
||||
};
|
||||
int base;
|
||||
|
||||
if( (pParse->db->flags & SQLITE_InTrans)==0 ){
|
||||
if( (db->flags & SQLITE_InTrans)==0 ){
|
||||
sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0);
|
||||
}
|
||||
base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex);
|
||||
sqliteVdbeChangeP1(v, base+8, pIndex->tnum);
|
||||
if( (pParse->db->flags & SQLITE_InTrans)==0 ){
|
||||
if( (db->flags & SQLITE_InTrans)==0 ){
|
||||
sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
@ -952,10 +956,11 @@ void sqliteCopy(
|
||||
Vdbe *v;
|
||||
int addr, end;
|
||||
Index *pIdx;
|
||||
sqlite *db = pParse->db;
|
||||
|
||||
zTab = sqliteTableNameFromToken(pTableName);
|
||||
if( sqlite_malloc_failed || zTab==0 ) goto copy_cleanup;
|
||||
pTab = sqliteFindTable(pParse->db, zTab);
|
||||
pTab = sqliteFindTable(db, zTab);
|
||||
sqliteFree(zTab);
|
||||
if( pTab==0 ){
|
||||
sqliteSetNString(&pParse->zErrMsg, "no such table: ", 0,
|
||||
@ -971,7 +976,7 @@ void sqliteCopy(
|
||||
}
|
||||
v = sqliteGetVdbe(pParse);
|
||||
if( v ){
|
||||
if( (pParse->db->flags & SQLITE_InTrans)==0 ){
|
||||
if( (db->flags & SQLITE_InTrans)==0 ){
|
||||
sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0);
|
||||
}
|
||||
addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0, 0, 0);
|
||||
@ -1010,7 +1015,7 @@ void sqliteCopy(
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, end);
|
||||
if( (pParse->db->flags & SQLITE_InTrans)==0 ){
|
||||
if( (db->flags & SQLITE_InTrans)==0 ){
|
||||
sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
@ -1027,6 +1032,7 @@ copy_cleanup:
|
||||
void sqliteVacuum(Parse *pParse, Token *pTableName){
|
||||
char *zName;
|
||||
Vdbe *v;
|
||||
sqlite *db = pParse->db;
|
||||
|
||||
if( pParse->nErr || sqlite_malloc_failed ) return;
|
||||
if( pTableName ){
|
||||
@ -1034,15 +1040,15 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){
|
||||
}else{
|
||||
zName = 0;
|
||||
}
|
||||
if( zName && sqliteFindIndex(pParse->db, zName)==0
|
||||
&& sqliteFindTable(pParse->db, zName)==0 ){
|
||||
if( zName && sqliteFindIndex(db, zName)==0
|
||||
&& sqliteFindTable(db, zName)==0 ){
|
||||
sqliteSetString(&pParse->zErrMsg, "no such table or index: ", zName, 0);
|
||||
pParse->nErr++;
|
||||
goto vacuum_cleanup;
|
||||
}
|
||||
v = sqliteGetVdbe(pParse);
|
||||
if( v==0 ) goto vacuum_cleanup;
|
||||
if( (pParse->db->flags & SQLITE_InTrans)==0 ){
|
||||
if( (db->flags & SQLITE_InTrans)==0 ){
|
||||
sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0);
|
||||
}
|
||||
if( zName ){
|
||||
@ -1052,7 +1058,7 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){
|
||||
Table *pTab;
|
||||
Index *pIdx;
|
||||
for(h=0; h<N_HASH; h++){
|
||||
for(pTab=pParse->db->apTblHash[h]; pTab; pTab=pTab->pHash){
|
||||
for(pTab=db->apTblHash[h]; pTab; pTab=pTab->pHash){
|
||||
sqliteVdbeAddOp(v, OP_Reorganize, 0, 0, pTab->zName, 0);
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
sqliteVdbeAddOp(v, OP_Reorganize, 0, 0, pIdx->zName, 0);
|
||||
@ -1060,7 +1066,7 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){
|
||||
}
|
||||
}
|
||||
}
|
||||
if( (pParse->db->flags & SQLITE_InTrans)==0 ){
|
||||
if( (db->flags & SQLITE_InTrans)==0 ){
|
||||
sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
@ -1073,7 +1079,6 @@ vacuum_cleanup:
|
||||
** Begin a transaction
|
||||
*/
|
||||
void sqliteBeginTransaction(Parse *pParse){
|
||||
int rc;
|
||||
sqlite *db;
|
||||
Vdbe *v;
|
||||
|
||||
@ -1091,7 +1096,6 @@ void sqliteBeginTransaction(Parse *pParse){
|
||||
** Commit a transaction
|
||||
*/
|
||||
void sqliteCommitTransaction(Parse *pParse){
|
||||
int rc;
|
||||
sqlite *db;
|
||||
Vdbe *v;
|
||||
|
||||
@ -1109,7 +1113,6 @@ void sqliteCommitTransaction(Parse *pParse){
|
||||
** Rollback a transaction
|
||||
*/
|
||||
void sqliteRollbackTransaction(Parse *pParse){
|
||||
int rc;
|
||||
sqlite *db;
|
||||
Vdbe *v;
|
||||
|
||||
|
124
src/dbbe.c
124
src/dbbe.c
@ -1,124 +0,0 @@
|
||||
/*
|
||||
** Copyright (c) 1999, 2000 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 GDBM as the database backend. It should be
|
||||
** relatively simple to convert to a different database such
|
||||
** as NDBM, SDBM, or BerkeleyDB.
|
||||
**
|
||||
** $Id: dbbe.c,v 1.28 2001/04/28 16:52:41 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
** This routine opens a new database. It looks at the first
|
||||
** few characters of the database name to try to determine what
|
||||
** kind of database to open. If the first characters are "gdbm:",
|
||||
** then it uses the GDBM driver. If the first few characters are
|
||||
** "memory:" then it uses the in-memory driver. If there is no
|
||||
** match, the default to the GDBM driver.
|
||||
**
|
||||
** 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 *sqliteDbbeOpen(
|
||||
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 */
|
||||
){
|
||||
extern Dbbe *sqliteMemOpen(const char*,int,int,char**);
|
||||
#ifndef DISABLE_GDBM
|
||||
extern Dbbe *sqliteGdbmOpen(const char*,int,int,char**);
|
||||
if( strncmp(zName, "gdbm:", 5)==0 ){
|
||||
return sqliteGdbmOpen(&zName[5], writeFlag, createFlag, pzErrMsg);
|
||||
}
|
||||
#endif
|
||||
if( strncmp(zName, "memory:", 7)==0 ){
|
||||
return sqliteMemOpen(&zName[7], writeFlag, createFlag, pzErrMsg);
|
||||
}
|
||||
#ifndef DISABLE_GDBM
|
||||
return sqliteGdbmOpen(zName, writeFlag, createFlag, pzErrMsg);
|
||||
#else
|
||||
return sqliteMemOpen(zName, writeFlag, createFlag, pzErrMsg);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0 /* NOT USED */
|
||||
/*
|
||||
** Translate the name of an SQL table (or index) into the name
|
||||
** of a file that holds the key/data pairs for that table or
|
||||
** index. Space to hold the filename is obtained from
|
||||
** sqliteMalloc() and must be freed by the calling function.
|
||||
**
|
||||
** zDir is the name of the directory in which the file should
|
||||
** be located. zSuffix is the filename extension to use for
|
||||
** the file.
|
||||
*/
|
||||
char *sqliteDbbeNameToFile(
|
||||
const char *zDir, /* Directory containing the file */
|
||||
const char *zTable, /* Name of the SQL table that the file contains */
|
||||
const char *zSuffix /* Suffix for the file. Includes the "." */
|
||||
){
|
||||
char *zFile = 0;
|
||||
int i, k, c;
|
||||
int nChar = 0;
|
||||
|
||||
for(i=0; (c = zTable[i])!=0; i++){
|
||||
if( !isalnum(c) && c!='_' ){
|
||||
nChar += 3;
|
||||
}else{
|
||||
nChar ++;
|
||||
}
|
||||
}
|
||||
nChar += strlen(zDir) + strlen(zSuffix) + 2;
|
||||
zFile = sqliteMalloc( nChar );
|
||||
if( zFile==0 ) return 0;
|
||||
for(i=0; (c = zDir[i])!=0; i++){
|
||||
zFile[i] = c;
|
||||
}
|
||||
zFile[i++] = '/';
|
||||
for(k=0; (c = zTable[k])!=0; k++){
|
||||
if( isupper(c) ){
|
||||
zFile[i++] = tolower(c);
|
||||
}else if( isalnum(c) || c=='_' ){
|
||||
zFile[i++] = c;
|
||||
}else{
|
||||
zFile[i++] = '~';
|
||||
zFile[i++] = "0123456789abcdef"[c & 0xf];
|
||||
zFile[i++] = "0123456789abcdef"[(c>>8)&0xf];
|
||||
}
|
||||
}
|
||||
for(k=0; (c = zSuffix[k])!=0; k++){
|
||||
zFile[i++] = c;
|
||||
}
|
||||
zFile[i] = 0;
|
||||
return zFile;
|
||||
}
|
||||
#endif /* NOT USED */
|
197
src/dbbe.h
197
src/dbbe.h
@ -1,197 +0,0 @@
|
||||
/*
|
||||
** Copyright (c) 1999, 2000 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 defines the interface to the database backend (Dbbe).
|
||||
**
|
||||
** The database backend is designed to be as general as possible
|
||||
** so that it can easily be replaced by a different backend.
|
||||
** This library was originally designed to support the following
|
||||
** backends: GDBM, NDBM, SDBM, Berkeley DB.
|
||||
**
|
||||
** $Id: dbbe.h,v 1.14 2001/08/19 18:19:46 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITE_DBBE_H_
|
||||
#define _SQLITE_DBBE_H_
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
** The database backend supports two opaque structures. A Dbbe is
|
||||
** a context for the entire set of tables forming a complete
|
||||
** database. A DbbeCursor is a pointer into a single single table.
|
||||
**
|
||||
** Note that at this level, the term "table" can mean either an
|
||||
** SQL table or an SQL index. In this module, a table stores a
|
||||
** single arbitrary-length key and corresponding arbitrary-length
|
||||
** data. The differences between tables and indices, and the
|
||||
** segregation of data into various fields or columns is handled
|
||||
** by software at higher layers.
|
||||
**
|
||||
** The DbbeCursor structure holds some state information, such as
|
||||
** the key and data from the last retrieval. For this reason,
|
||||
** the backend must allow the creation of multiple independent
|
||||
** DbbeCursor structures for each table in the database.
|
||||
*/
|
||||
typedef struct Dbbe Dbbe;
|
||||
typedef struct DbbeCursor DbbeCursor;
|
||||
typedef struct DbbeMethods DbbeMethods;
|
||||
|
||||
/*
|
||||
** Open a complete database.
|
||||
**
|
||||
** If the database name begins with "gdbm:" the GDBM driver is used.
|
||||
** If the name begins with "memory:" the in-memory driver is used.
|
||||
** The default driver is GDBM.
|
||||
*/
|
||||
Dbbe *sqliteDbbeOpen(const char *zName, int write, int create, char **pzErr);
|
||||
|
||||
/*
|
||||
** Each of the various SQLite backends defines a set of methods for
|
||||
** accessing the database. Pointers to the methods are contained in
|
||||
** an instance of the following structure. A pointer to a static instance
|
||||
** of this structure is assigned to the Dbbe structure that sqlileDbbeOpen
|
||||
** returns.
|
||||
*/
|
||||
struct DbbeMethods {
|
||||
/* Close the whole database. */
|
||||
void (*Close)(Dbbe*);
|
||||
|
||||
/* Open a cursor into a particular table of a previously opened database.
|
||||
** Create the table if it doesn't already exist and writeable!=0. zName
|
||||
** is the base name of the table to be opened. If the database is
|
||||
** implement as one file per table, then this routine will add an
|
||||
** appropriate path and extension to the table name to locate the
|
||||
** actual file.
|
||||
**
|
||||
** The intKeyOnly parameter is TRUE if this table will only be accessed
|
||||
** using integer keys. This parameter allows the database backend to
|
||||
** use a faster algorithm for the special case of integer keys, if it
|
||||
** wants to.
|
||||
**
|
||||
** If zName is 0 or "", then a temporary table is created that
|
||||
** will be deleted when closed.
|
||||
*/
|
||||
int (*OpenCursor)(Dbbe*, const char *zName, int writeable,
|
||||
int intKeyOnly, DbbeCursor**);
|
||||
|
||||
/* Delete a table from the database */
|
||||
void (*DropTable)(Dbbe*, const char *zTableName);
|
||||
|
||||
/* Reorganize a table to speed access or reduce its disk usage */
|
||||
int (*ReorganizeTable)(Dbbe*, const char *zTableName);
|
||||
|
||||
/* Close a cursor */
|
||||
void (*CloseCursor)(DbbeCursor*);
|
||||
|
||||
/* Fetch an entry from a table with the given key. Return 1 if
|
||||
** successful and 0 if no such entry exists.
|
||||
*/
|
||||
int (*Fetch)(DbbeCursor*, int nKey, char *pKey);
|
||||
|
||||
/* Return 1 if the given key is already in the table. Return 0
|
||||
** if it is not.
|
||||
*/
|
||||
int (*Test)(DbbeCursor*, int nKey, char *pKey);
|
||||
|
||||
/* Retrieve the key or data used for the last fetch. Only size
|
||||
** bytes are read beginning with the offset-th byte. The return
|
||||
** value is the actual number of bytes read.
|
||||
*/
|
||||
int (*CopyKey)(DbbeCursor*, int offset, int size, char *zBuf);
|
||||
int (*CopyData)(DbbeCursor*, int offset, int size, char *zBuf);
|
||||
|
||||
/* Retrieve the key or data. The result is ephemeral. In other words,
|
||||
** the result is stored in a buffer that might be overwritten on the next
|
||||
** call to any DBBE routine. If the results are needed for longer than
|
||||
** that, you must make a copy.
|
||||
*/
|
||||
char *(*ReadKey)(DbbeCursor*, int offset);
|
||||
char *(*ReadData)(DbbeCursor*, int offset);
|
||||
|
||||
/* Return the length of the most recently fetched key or data. */
|
||||
int (*KeyLength)(DbbeCursor*);
|
||||
int (*DataLength)(DbbeCursor*);
|
||||
|
||||
/* Retrieve the next entry in the table. The first key is retrieved
|
||||
** the first time this routine is called, or after a call to
|
||||
** Dbbe.Rewind(). The return value is 1 if there is another
|
||||
** entry, or 0 if there are no more entries. */
|
||||
int (*NextKey)(DbbeCursor*);
|
||||
|
||||
/* Make it so that the next call to Dbbe.NextKey() returns
|
||||
** the first entry of the table. */
|
||||
int (*Rewind)(DbbeCursor*);
|
||||
|
||||
/* Get a new integer key for this table. */
|
||||
int (*New)(DbbeCursor*);
|
||||
|
||||
/* Write an entry into a table. If another entry already exists with
|
||||
** the same key, the old entry is discarded first.
|
||||
*/
|
||||
int (*Put)(DbbeCursor*, int nKey, char *pKey, int nData, char *pData);
|
||||
|
||||
/* Remove an entry from the table */
|
||||
int (*Delete)(DbbeCursor*, int nKey, char *pKey);
|
||||
|
||||
/* Begin a transaction. */
|
||||
int (*BeginTransaction)(Dbbe*);
|
||||
|
||||
/* Commit a transaction. */
|
||||
int (*Commit)(Dbbe*);
|
||||
|
||||
/* Rollback a transaction. */
|
||||
int (*Rollback)(Dbbe*);
|
||||
|
||||
/* Begin searching an index where the key is given. */
|
||||
int (*BeginIndex)(DbbeCursor*, int nKey, char *pKey);
|
||||
|
||||
/* Return the integer key for the next index entry, or return 0 if
|
||||
** there are no more index entries. */
|
||||
int (*NextIndex)(DbbeCursor*);
|
||||
|
||||
/* Add a new index entry to the file. The key and record number are
|
||||
** given. */
|
||||
int (*PutIndex)(DbbeCursor*, int nKey, char *pKey, int recno);
|
||||
|
||||
/* Delete an index entry from the file. The key and record number are
|
||||
** given. */
|
||||
int (*DeleteIndex)(DbbeCursor*, int nKey, char *pKey, int recno);
|
||||
};
|
||||
|
||||
/*
|
||||
** This is the structure returned by sqliteDbbeOpen(). It contains
|
||||
** information common to all the different backend drivers.
|
||||
**
|
||||
** The information in this structure (with the exception the method
|
||||
** pointers in the Dbbe.x field) is intended to be visible to
|
||||
** the backend drivers only. Users should not access or modify
|
||||
** this structure in any way other than the read the method pointers
|
||||
** in Dbbe.x.
|
||||
*/
|
||||
struct Dbbe {
|
||||
struct DbbeMethods *x; /* Backend-specific methods for database access */
|
||||
/* There used to be other information here, but it has since
|
||||
** been removed. We'll keep the same design, though, in case we
|
||||
** ever want to add some new fields in the future. */
|
||||
};
|
||||
|
||||
#endif /* defined(_SQLITE_DBBE_H_) */
|
676
src/dbbebtree.c
676
src/dbbebtree.c
@ -1,676 +0,0 @@
|
||||
/*
|
||||
** 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 */
|
792
src/dbbegdbm.c
792
src/dbbegdbm.c
@ -1,792 +0,0 @@
|
||||
/*
|
||||
** Copyright (c) 2000 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 GDBM as the database backend. It should be
|
||||
** relatively simple to convert to a different database such
|
||||
** as NDBM, SDBM, or BerkeleyDB.
|
||||
**
|
||||
** $Id: dbbegdbm.c,v 1.9 2001/08/19 18:19:46 drh Exp $
|
||||
*/
|
||||
#ifndef DISABLE_GDBM
|
||||
#include "sqliteInt.h"
|
||||
#include <gdbm.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
|
||||
/*
|
||||
** Information about each open disk file is an instance of this
|
||||
** structure. There will only be one such structure for each
|
||||
** disk file. If the VDBE opens the same file twice (as will happen
|
||||
** for a self-join, for example) then two DbbeCursor structures are
|
||||
** created but there is only a single BeFile structure with an
|
||||
** nRef of 2.
|
||||
**
|
||||
** This backend uses a separate disk file for each database table
|
||||
** and index.
|
||||
*/
|
||||
typedef struct BeFile BeFile;
|
||||
struct BeFile {
|
||||
char *zName; /* Name of the file */
|
||||
GDBM_FILE dbf; /* The file itself */
|
||||
int nRef; /* Number of references */
|
||||
int delOnClose; /* Delete when closing */
|
||||
int writeable; /* Opened for writing */
|
||||
BeFile *pNext, *pPrev; /* Next and previous on list of open files */
|
||||
};
|
||||
|
||||
/*
|
||||
** The following structure contains all information used by GDBM
|
||||
** 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 */
|
||||
BeFile *pOpen; /* List of open files */
|
||||
char *zDir; /* Directory hold the database */
|
||||
};
|
||||
|
||||
/*
|
||||
** An cursor into a database file is an instance of the following structure.
|
||||
** There can only be a single BeFile structure for each disk file, but
|
||||
** there can be multiple DbbeCursor structures. Each DbbeCursor represents
|
||||
** a cursor pointing to a particular part of the open BeFile. The
|
||||
** BeFile.nRef field hold a count of the number of DbbeCursor structures
|
||||
** associated with the same disk file.
|
||||
*/
|
||||
struct DbbeCursor {
|
||||
Dbbex *pBe; /* The database of which this record is a part */
|
||||
BeFile *pFile; /* The database file for this table */
|
||||
datum key; /* Most recently used key */
|
||||
datum data; /* Most recent data */
|
||||
int nextIndex; /* Next index entry to search */
|
||||
int needRewind; /* Next key should be the first */
|
||||
int readPending; /* The fetch hasn't actually been done yet */
|
||||
};
|
||||
|
||||
/*
|
||||
** The "mkdir()" function only takes one argument under Windows.
|
||||
*/
|
||||
#if OS_WIN
|
||||
# define mkdir(A,B) mkdir(A)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Forward declaration
|
||||
*/
|
||||
static void sqliteGdbmCloseCursor(DbbeCursor *pCursr);
|
||||
|
||||
/*
|
||||
** Completely shutdown the given database. Close all files. Free all memory.
|
||||
*/
|
||||
static void sqliteGdbmClose(Dbbe *pDbbe){
|
||||
Dbbex *pBe = (Dbbex*)pDbbe;
|
||||
BeFile *pFile, *pNext;
|
||||
for(pFile=pBe->pOpen; pFile; pFile=pNext){
|
||||
pNext = pFile->pNext;
|
||||
gdbm_close(pFile->dbf);
|
||||
memset(pFile, 0, sizeof(*pFile));
|
||||
sqliteFree(pFile);
|
||||
}
|
||||
memset(pBe, 0, sizeof(*pBe));
|
||||
sqliteFree(pBe);
|
||||
}
|
||||
|
||||
/*
|
||||
** Translate the name of an SQL table (or index) into the name
|
||||
** of a file that holds the key/data pairs for that table or
|
||||
** index. Space to hold the filename is obtained from
|
||||
** sqliteMalloc() and must be freed by the calling function.
|
||||
*/
|
||||
static char *sqliteFileOfTable(Dbbex *pBe, const char *zTable){
|
||||
char *zFile = 0;
|
||||
int i;
|
||||
sqliteSetString(&zFile, pBe->zDir, "/", zTable, ".tbl", 0);
|
||||
if( zFile==0 ) return 0;
|
||||
for(i=strlen(pBe->zDir)+1; zFile[i]; i++){
|
||||
int c = zFile[i];
|
||||
if( isupper(c) ){
|
||||
zFile[i] = tolower(c);
|
||||
}else if( !isalnum(c) && c!='-' && c!='_' && c!='.' ){
|
||||
zFile[i] = '+';
|
||||
}
|
||||
}
|
||||
return zFile;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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 zTable is 0 or "", then a temporary database file is created and
|
||||
** a cursor to that temporary file is opened. The temporary file
|
||||
** will be deleted from the disk when it is closed.
|
||||
*/
|
||||
static int sqliteGdbmOpenCursor(
|
||||
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 */
|
||||
BeFile *pFile; /* The underlying data file for this table */
|
||||
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;
|
||||
|
||||
if( pBe->inTrans ) writeable = 1;
|
||||
*ppCursr = 0;
|
||||
pCursr = sqliteMalloc( sizeof(*pCursr) );
|
||||
if( pCursr==0 ) return SQLITE_NOMEM;
|
||||
if( zTable ){
|
||||
zFile = sqliteFileOfTable(pBe, zTable);
|
||||
if( zFile==0 ) return SQLITE_NOMEM;
|
||||
for(pFile=pBe->pOpen; pFile; pFile=pFile->pNext){
|
||||
if( strcmp(pFile->zName,zFile)==0 ) break;
|
||||
}
|
||||
}else{
|
||||
pFile = 0;
|
||||
zFile = 0;
|
||||
}
|
||||
if( pFile==0 ){
|
||||
if( writeable ){
|
||||
rw_mask = GDBM_WRCREAT | GDBM_FAST;
|
||||
mode = 0640;
|
||||
}else{
|
||||
rw_mask = GDBM_READER;
|
||||
mode = 0640;
|
||||
}
|
||||
pFile = sqliteMalloc( sizeof(*pFile) );
|
||||
if( pFile==0 ){
|
||||
sqliteFree(zFile);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
if( zFile ){
|
||||
if( !writeable || pBe->write ){
|
||||
pFile->dbf = gdbm_open(zFile, 0, rw_mask, mode, 0);
|
||||
}else{
|
||||
pFile->dbf = 0;
|
||||
}
|
||||
}else{
|
||||
int limit;
|
||||
char zRandom[50];
|
||||
zFile = 0;
|
||||
limit = 5;
|
||||
do {
|
||||
sqliteRandomName(zRandom, "_temp_table_");
|
||||
sqliteFree(zFile);
|
||||
zFile = sqliteFileOfTable(pBe, zRandom);
|
||||
pFile->dbf = gdbm_open(zFile, 0, rw_mask, mode, 0);
|
||||
}while( pFile->dbf==0 && limit-- >= 0);
|
||||
pFile->delOnClose = 1;
|
||||
}
|
||||
pFile->writeable = writeable;
|
||||
pFile->zName = zFile;
|
||||
pFile->nRef = 1 + pBe->inTrans;
|
||||
pFile->pPrev = 0;
|
||||
if( pBe->pOpen ){
|
||||
pBe->pOpen->pPrev = pFile;
|
||||
}
|
||||
pFile->pNext = pBe->pOpen;
|
||||
pBe->pOpen = pFile;
|
||||
if( pFile->dbf==0 ){
|
||||
if( !writeable && access(zFile,0) ){
|
||||
/* Trying to read a non-existant file. This is OK. All the
|
||||
** reads will return empty, which is what we want. */
|
||||
rc = SQLITE_OK;
|
||||
}else if( pBe->write==0 ){
|
||||
rc = SQLITE_READONLY;
|
||||
}else if( access(zFile,W_OK|R_OK) ){
|
||||
rc = SQLITE_PERM;
|
||||
}else{
|
||||
rc = SQLITE_BUSY;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
sqliteFree(zFile);
|
||||
pFile->nRef++;
|
||||
if( writeable && !pFile->writeable ){
|
||||
rc = SQLITE_READONLY;
|
||||
}
|
||||
}
|
||||
pCursr->pBe = pBe;
|
||||
pCursr->pFile = pFile;
|
||||
pCursr->readPending = 0;
|
||||
pCursr->needRewind = 1;
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqliteGdbmCloseCursor(pCursr);
|
||||
*ppCursr = 0;
|
||||
}else{
|
||||
*ppCursr = pCursr;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Drop a table from the database. The file on the disk that corresponds
|
||||
** to this table is deleted.
|
||||
*/
|
||||
static void sqliteGdbmDropTable(Dbbe *pBe, const char *zTable){
|
||||
char *zFile; /* Name of the table file */
|
||||
|
||||
zFile = sqliteFileOfTable((Dbbex*)pBe, zTable);
|
||||
unlink(zFile);
|
||||
sqliteFree(zFile);
|
||||
}
|
||||
|
||||
/*
|
||||
** Unlink a file pointer
|
||||
*/
|
||||
static void sqliteUnlinkFile(Dbbex *pBe, BeFile *pFile){
|
||||
if( pFile->dbf!=NULL ){
|
||||
gdbm_close(pFile->dbf);
|
||||
}
|
||||
if( pFile->pPrev ){
|
||||
pFile->pPrev->pNext = pFile->pNext;
|
||||
}else{
|
||||
pBe->pOpen = pFile->pNext;
|
||||
}
|
||||
if( pFile->pNext ){
|
||||
pFile->pNext->pPrev = pFile->pPrev;
|
||||
}
|
||||
if( pFile->delOnClose ){
|
||||
unlink(pFile->zName);
|
||||
}
|
||||
sqliteFree(pFile->zName);
|
||||
memset(pFile, 0, sizeof(*pFile));
|
||||
sqliteFree(pFile);
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a cursor previously opened by sqliteGdbmOpenCursor().
|
||||
**
|
||||
** There can be multiple cursors pointing to the same open file.
|
||||
** The underlying file is not closed until all cursors have been
|
||||
** closed. This routine decrements the BeFile.nref field of the
|
||||
** underlying file and closes the file when nref reaches 0.
|
||||
*/
|
||||
static void sqliteGdbmCloseCursor(DbbeCursor *pCursr){
|
||||
BeFile *pFile;
|
||||
Dbbex *pBe;
|
||||
if( pCursr==0 ) return;
|
||||
pFile = pCursr->pFile;
|
||||
pBe = pCursr->pBe;
|
||||
pFile->nRef--;
|
||||
if( pFile->dbf!=NULL ){
|
||||
gdbm_sync(pFile->dbf);
|
||||
}
|
||||
if( pFile->nRef<=0 ){
|
||||
sqliteUnlinkFile(pBe, pFile);
|
||||
}
|
||||
if( pCursr->key.dptr ) free(pCursr->key.dptr);
|
||||
if( pCursr->data.dptr ) free(pCursr->data.dptr);
|
||||
memset(pCursr, 0, sizeof(*pCursr));
|
||||
sqliteFree(pCursr);
|
||||
}
|
||||
|
||||
/*
|
||||
** Reorganize a table to reduce search times and disk usage.
|
||||
*/
|
||||
static int sqliteGdbmReorganizeTable(Dbbe *pBe, const char *zTable){
|
||||
DbbeCursor *pCursr;
|
||||
int rc;
|
||||
|
||||
rc = sqliteGdbmOpenCursor(pBe, zTable, 1, 0, &pCursr);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
if( pCursr && pCursr->pFile && pCursr->pFile->dbf ){
|
||||
gdbm_reorganize(pCursr->pFile->dbf);
|
||||
}
|
||||
if( pCursr ){
|
||||
sqliteGdbmCloseCursor(pCursr);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Clear the given datum
|
||||
*/
|
||||
static void datumClear(datum *p){
|
||||
if( p->dptr ) free(p->dptr);
|
||||
p->dptr = 0;
|
||||
p->dsize = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Fetch a single record from an open cursor. Return 1 on success
|
||||
** and 0 on failure.
|
||||
*/
|
||||
static int sqliteGdbmFetch(DbbeCursor *pCursr, int nKey, char *pKey){
|
||||
datum key;
|
||||
key.dsize = nKey;
|
||||
key.dptr = pKey;
|
||||
datumClear(&pCursr->key);
|
||||
datumClear(&pCursr->data);
|
||||
if( pCursr->pFile && pCursr->pFile->dbf ){
|
||||
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, key);
|
||||
}
|
||||
return pCursr->data.dptr!=0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return 1 if the given key is already in the table. Return 0
|
||||
** if it is not.
|
||||
*/
|
||||
static int sqliteGdbmTest(DbbeCursor *pCursr, int nKey, char *pKey){
|
||||
datum key;
|
||||
int result = 0;
|
||||
key.dsize = nKey;
|
||||
key.dptr = pKey;
|
||||
if( pCursr->pFile && pCursr->pFile->dbf ){
|
||||
result = gdbm_exists(pCursr->pFile->dbf, key);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
** Copy bytes from the current key or data into a buffer supplied by
|
||||
** the calling function. Return the number of bytes copied.
|
||||
*/
|
||||
static
|
||||
int sqliteGdbmCopyKey(DbbeCursor *pCursr, int offset, int size, char *zBuf){
|
||||
int n;
|
||||
if( offset>=pCursr->key.dsize ) return 0;
|
||||
if( offset+size>pCursr->key.dsize ){
|
||||
n = pCursr->key.dsize - offset;
|
||||
}else{
|
||||
n = size;
|
||||
}
|
||||
memcpy(zBuf, &pCursr->key.dptr[offset], n);
|
||||
return n;
|
||||
}
|
||||
static
|
||||
int sqliteGdbmCopyData(DbbeCursor *pCursr, int offset, int size, char *zBuf){
|
||||
int n;
|
||||
if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
|
||||
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
|
||||
pCursr->readPending = 0;
|
||||
}
|
||||
if( offset>=pCursr->data.dsize ) return 0;
|
||||
if( offset+size>pCursr->data.dsize ){
|
||||
n = pCursr->data.dsize - offset;
|
||||
}else{
|
||||
n = size;
|
||||
}
|
||||
memcpy(zBuf, &pCursr->data.dptr[offset], n);
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to bytes from the key or data. The data returned
|
||||
** is ephemeral.
|
||||
*/
|
||||
static char *sqliteGdbmReadKey(DbbeCursor *pCursr, int offset){
|
||||
if( offset<0 || offset>=pCursr->key.dsize ) return "";
|
||||
return &pCursr->key.dptr[offset];
|
||||
}
|
||||
static char *sqliteGdbmReadData(DbbeCursor *pCursr, int offset){
|
||||
if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
|
||||
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
|
||||
pCursr->readPending = 0;
|
||||
}
|
||||
if( offset<0 || offset>=pCursr->data.dsize ) return "";
|
||||
return &pCursr->data.dptr[offset];
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the total number of bytes in either data or key.
|
||||
*/
|
||||
static int sqliteGdbmKeyLength(DbbeCursor *pCursr){
|
||||
return pCursr->key.dsize;
|
||||
}
|
||||
static int sqliteGdbmDataLength(DbbeCursor *pCursr){
|
||||
if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
|
||||
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
|
||||
pCursr->readPending = 0;
|
||||
}
|
||||
return pCursr->data.dsize;
|
||||
}
|
||||
|
||||
/*
|
||||
** Make is so that the next call to sqliteNextKey() finds the first
|
||||
** key of the table.
|
||||
*/
|
||||
static int sqliteGdbmRewind(DbbeCursor *pCursr){
|
||||
pCursr->needRewind = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read the next key from the table. Return 1 on success. Return
|
||||
** 0 if there are no more keys.
|
||||
*/
|
||||
static int sqliteGdbmNextKey(DbbeCursor *pCursr){
|
||||
datum nextkey;
|
||||
int rc;
|
||||
if( pCursr==0 || pCursr->pFile==0 || pCursr->pFile->dbf==0 ){
|
||||
pCursr->readPending = 0;
|
||||
return 0;
|
||||
}
|
||||
if( pCursr->needRewind ){
|
||||
nextkey = gdbm_firstkey(pCursr->pFile->dbf);
|
||||
pCursr->needRewind = 0;
|
||||
}else{
|
||||
nextkey = gdbm_nextkey(pCursr->pFile->dbf, pCursr->key);
|
||||
}
|
||||
datumClear(&pCursr->key);
|
||||
datumClear(&pCursr->data);
|
||||
pCursr->key = nextkey;
|
||||
if( pCursr->key.dptr ){
|
||||
pCursr->readPending = 1;
|
||||
rc = 1;
|
||||
}else{
|
||||
pCursr->needRewind = 1;
|
||||
pCursr->readPending = 0;
|
||||
rc = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Get a new integer key.
|
||||
*/
|
||||
static int sqliteGdbmNew(DbbeCursor *pCursr){
|
||||
int iKey;
|
||||
datum key;
|
||||
int go = 1;
|
||||
|
||||
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return 1;
|
||||
while( go ){
|
||||
iKey = sqliteRandomInteger() & 0x7fffffff;
|
||||
if( iKey==0 ) continue;
|
||||
key.dptr = (char*)&iKey;
|
||||
key.dsize = 4;
|
||||
go = gdbm_exists(pCursr->pFile->dbf, key);
|
||||
}
|
||||
return iKey;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write an entry into the table. Overwrite any prior entry with the
|
||||
** same key.
|
||||
*/
|
||||
static int sqliteGdbmPut(
|
||||
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 */
|
||||
){
|
||||
datum data, key;
|
||||
int rc;
|
||||
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return SQLITE_ERROR;
|
||||
data.dsize = nData;
|
||||
data.dptr = pData;
|
||||
key.dsize = nKey;
|
||||
key.dptr = pKey;
|
||||
rc = gdbm_store(pCursr->pFile->dbf, key, data, GDBM_REPLACE);
|
||||
if( rc ) rc = SQLITE_ERROR;
|
||||
datumClear(&pCursr->key);
|
||||
datumClear(&pCursr->data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Remove an entry from a table, if the entry exists.
|
||||
*/
|
||||
static int sqliteGdbmDelete(DbbeCursor *pCursr, int nKey, char *pKey){
|
||||
datum key;
|
||||
int rc;
|
||||
datumClear(&pCursr->key);
|
||||
datumClear(&pCursr->data);
|
||||
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return SQLITE_ERROR;
|
||||
key.dsize = nKey;
|
||||
key.dptr = pKey;
|
||||
rc = gdbm_delete(pCursr->pFile->dbf, key);
|
||||
if( rc ) rc = SQLITE_ERROR;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Begin a transaction.
|
||||
*/
|
||||
static int sqliteGdbmBeginTrans(Dbbe *pDbbe){
|
||||
Dbbex *pBe = (Dbbex*)pDbbe;
|
||||
BeFile *pFile;
|
||||
if( pBe->inTrans ) return SQLITE_OK;
|
||||
for(pFile=pBe->pOpen; pFile; pFile=pFile->pNext){
|
||||
pFile->nRef++;
|
||||
}
|
||||
pBe->inTrans = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** End a transaction.
|
||||
*/
|
||||
static int sqliteGdbmEndTrans(Dbbe *pDbbe){
|
||||
Dbbex *pBe = (Dbbex*)pDbbe;
|
||||
BeFile *pFile, *pNext;
|
||||
if( !pBe->inTrans ) return SQLITE_OK;
|
||||
for(pFile=pBe->pOpen; pFile; pFile=pNext){
|
||||
pNext = pFile->pNext;
|
||||
pFile->nRef--;
|
||||
if( pFile->nRef<=0 ){
|
||||
sqliteUnlinkFile(pBe, pFile);
|
||||
}
|
||||
}
|
||||
pBe->inTrans = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Begin scanning an index for the given key. Return 1 on success and
|
||||
** 0 on failure.
|
||||
*/
|
||||
static int sqliteGdbmBeginIndex(DbbeCursor *pCursr, int nKey, char *pKey){
|
||||
if( !sqliteGdbmFetch(pCursr, nKey, pKey) ) return 0;
|
||||
pCursr->nextIndex = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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 sqliteGdbmNextIndex(DbbeCursor *pCursr){
|
||||
int *aIdx;
|
||||
int nIdx;
|
||||
int k;
|
||||
nIdx = pCursr->data.dsize/sizeof(int);
|
||||
aIdx = (int*)pCursr->data.dptr;
|
||||
if( nIdx>1 ){
|
||||
k = *(aIdx++);
|
||||
if( k>nIdx-1 ) k = nIdx-1;
|
||||
}else{
|
||||
k = nIdx;
|
||||
}
|
||||
while( pCursr->nextIndex < k ){
|
||||
int recno = aIdx[pCursr->nextIndex++];
|
||||
if( recno!=0 ) return recno;
|
||||
}
|
||||
pCursr->nextIndex = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write a new record number and key into an index table. Return a status
|
||||
** code.
|
||||
*/
|
||||
static int sqliteGdbmPutIndex(DbbeCursor *pCursr, int nKey, char *pKey, int N){
|
||||
int r = sqliteGdbmFetch(pCursr, nKey, pKey);
|
||||
if( r==0 ){
|
||||
/* Create a new record for this index */
|
||||
sqliteGdbmPut(pCursr, nKey, pKey, sizeof(int), (char*)&N);
|
||||
}else{
|
||||
/* Extend the existing record */
|
||||
int nIdx;
|
||||
int *aIdx;
|
||||
int k;
|
||||
|
||||
nIdx = pCursr->data.dsize/sizeof(int);
|
||||
if( nIdx==1 ){
|
||||
aIdx = sqliteMalloc( sizeof(int)*4 );
|
||||
if( aIdx==0 ) return SQLITE_NOMEM;
|
||||
aIdx[0] = 2;
|
||||
sqliteGdbmCopyData(pCursr, 0, sizeof(int), (char*)&aIdx[1]);
|
||||
aIdx[2] = N;
|
||||
sqliteGdbmPut(pCursr, nKey, pKey, sizeof(int)*4, (char*)aIdx);
|
||||
sqliteFree(aIdx);
|
||||
}else{
|
||||
aIdx = (int*)sqliteGdbmReadData(pCursr, 0);
|
||||
k = aIdx[0];
|
||||
if( k<nIdx-1 ){
|
||||
aIdx[k+1] = N;
|
||||
aIdx[0]++;
|
||||
sqliteGdbmPut(pCursr, nKey, pKey, sizeof(int)*nIdx, (char*)aIdx);
|
||||
}else{
|
||||
nIdx *= 2;
|
||||
aIdx = sqliteMalloc( sizeof(int)*nIdx );
|
||||
if( aIdx==0 ) return SQLITE_NOMEM;
|
||||
sqliteGdbmCopyData(pCursr, 0, sizeof(int)*(k+1), (char*)aIdx);
|
||||
aIdx[k+1] = N;
|
||||
aIdx[0]++;
|
||||
sqliteGdbmPut(pCursr, nKey, pKey, sizeof(int)*nIdx, (char*)aIdx);
|
||||
sqliteFree(aIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete an index entry. Return a status code.
|
||||
*/
|
||||
static
|
||||
int sqliteGdbmDeleteIndex(DbbeCursor *pCursr, int nKey, char *pKey, int N){
|
||||
int *aIdx;
|
||||
int nIdx;
|
||||
int j, k;
|
||||
int rc;
|
||||
rc = sqliteGdbmFetch(pCursr, nKey, pKey);
|
||||
if( !rc ) return SQLITE_OK;
|
||||
nIdx = pCursr->data.dsize/sizeof(int);
|
||||
aIdx = (int*)sqliteGdbmReadData(pCursr, 0);
|
||||
if( (nIdx==1 && aIdx[0]==N) || (aIdx[0]==1 && aIdx[1]==N) ){
|
||||
sqliteGdbmDelete(pCursr, nKey, pKey);
|
||||
}else{
|
||||
k = aIdx[0];
|
||||
for(j=1; j<=k && aIdx[j]!=N; j++){}
|
||||
if( j>k ) return SQLITE_OK;
|
||||
aIdx[j] = aIdx[k];
|
||||
aIdx[k] = 0;
|
||||
aIdx[0]--;
|
||||
if( aIdx[0]*3 + 1 < nIdx ){
|
||||
nIdx /= 2;
|
||||
}
|
||||
sqliteGdbmPut(pCursr, nKey, pKey, sizeof(int)*nIdx, (char*)aIdx);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This variable contains pointers to all of the access methods
|
||||
** used to implement the GDBM backend.
|
||||
*/
|
||||
static struct DbbeMethods gdbmMethods = {
|
||||
/* Close */ sqliteGdbmClose,
|
||||
/* OpenCursor */ sqliteGdbmOpenCursor,
|
||||
/* DropTable */ sqliteGdbmDropTable,
|
||||
/* ReorganizeTable */ sqliteGdbmReorganizeTable,
|
||||
/* CloseCursor */ sqliteGdbmCloseCursor,
|
||||
/* Fetch */ sqliteGdbmFetch,
|
||||
/* Test */ sqliteGdbmTest,
|
||||
/* CopyKey */ sqliteGdbmCopyKey,
|
||||
/* CopyData */ sqliteGdbmCopyData,
|
||||
/* ReadKey */ sqliteGdbmReadKey,
|
||||
/* ReadData */ sqliteGdbmReadData,
|
||||
/* KeyLength */ sqliteGdbmKeyLength,
|
||||
/* DataLength */ sqliteGdbmDataLength,
|
||||
/* NextKey */ sqliteGdbmNextKey,
|
||||
/* Rewind */ sqliteGdbmRewind,
|
||||
/* New */ sqliteGdbmNew,
|
||||
/* Put */ sqliteGdbmPut,
|
||||
/* Delete */ sqliteGdbmDelete,
|
||||
/* BeginTrans */ sqliteGdbmBeginTrans,
|
||||
/* Commit */ sqliteGdbmEndTrans,
|
||||
/* Rollback */ sqliteGdbmEndTrans,
|
||||
/* BeginIndex */ sqliteGdbmBeginIndex,
|
||||
/* NextIndex */ sqliteGdbmNextIndex,
|
||||
/* PutIndex */ sqliteGdbmPutIndex,
|
||||
/* DeleteIndex */ sqliteGdbmDeleteIndex,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** This routine opens a new database. For the GDBM driver
|
||||
** implemented here, the database name is the name of the directory
|
||||
** containing all the files 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 *sqliteGdbmOpen(
|
||||
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;
|
||||
struct stat statbuf;
|
||||
char *zMaster;
|
||||
|
||||
if( !writeFlag ) createFlag = 0;
|
||||
if( stat(zName, &statbuf)!=0 ){
|
||||
if( createFlag ) mkdir(zName, 0750);
|
||||
if( stat(zName, &statbuf)!=0 ){
|
||||
sqliteSetString(pzErrMsg, createFlag ?
|
||||
"can't find or create directory \"" : "can't find directory \"",
|
||||
zName, "\"", 0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if( !S_ISDIR(statbuf.st_mode) ){
|
||||
sqliteSetString(pzErrMsg, "not a directory: \"", zName, "\"", 0);
|
||||
return 0;
|
||||
}
|
||||
if( access(zName, writeFlag ? (X_OK|W_OK|R_OK) : (X_OK|R_OK)) ){
|
||||
sqliteSetString(pzErrMsg, "access permission denied", 0);
|
||||
return 0;
|
||||
}
|
||||
zMaster = 0;
|
||||
sqliteSetString(&zMaster, zName, "/" MASTER_NAME ".tbl", 0);
|
||||
if( stat(zMaster, &statbuf)==0
|
||||
&& access(zMaster, writeFlag ? (W_OK|R_OK) : R_OK)!=0 ){
|
||||
sqliteSetString(pzErrMsg, "access permission denied for ", zMaster, 0);
|
||||
sqliteFree(zMaster);
|
||||
return 0;
|
||||
}
|
||||
sqliteFree(zMaster);
|
||||
pNew = sqliteMalloc(sizeof(Dbbex) + strlen(zName) + 1);
|
||||
if( pNew==0 ){
|
||||
sqliteSetString(pzErrMsg, "out of memory", 0);
|
||||
return 0;
|
||||
}
|
||||
pNew->dbbe.x = &gdbmMethods;
|
||||
pNew->zDir = (char*)&pNew[1];
|
||||
strcpy(pNew->zDir, zName);
|
||||
pNew->write = writeFlag;
|
||||
pNew->pOpen = 0;
|
||||
return &pNew->dbbe;
|
||||
}
|
||||
#endif /* DISABLE_GDBM */
|
894
src/dbbemem.c
894
src/dbbemem.c
@ -1,894 +0,0 @@
|
||||
/*
|
||||
** Copyright (c) 2000 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 an in-memory hash table as the database backend.
|
||||
** Nothing is ever written to disk using this backend. All information
|
||||
** is forgotten when the program exits.
|
||||
**
|
||||
** $Id: dbbemem.c,v 1.17 2001/08/20 00:33:58 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
typedef struct Array Array;
|
||||
typedef struct ArrayElem ArrayElem;
|
||||
typedef struct Datum Datum;
|
||||
|
||||
/* A complete associative array is an instance of the following structure.
|
||||
** The internals of this structure are intended to be opaque -- client
|
||||
** code should not attempt to access or modify the fields of this structure
|
||||
** directly. Change this structure only by using the routines below.
|
||||
** However, many of the "procedures" and "functions" for modifying and
|
||||
** accessing this structure are really macros, so we can't really make
|
||||
** this structure opaque.
|
||||
*/
|
||||
struct Array {
|
||||
int count; /* Number of entries in the array */
|
||||
ArrayElem *first; /* The first element of the array */
|
||||
int htsize; /* Number of buckets in the hash table */
|
||||
struct _Array_ht { /* the hash table */
|
||||
int count; /* Number of entries with this hash */
|
||||
ArrayElem *chain; /* Pointer to first entry with this hash */
|
||||
} *ht;
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the following structure stores a single key or
|
||||
** data element.
|
||||
*/
|
||||
struct Datum {
|
||||
int n;
|
||||
void *p;
|
||||
};
|
||||
|
||||
/* Each element in the associative array is an instance of the following
|
||||
** structure. All elements are stored on a single doubly-linked list.
|
||||
**
|
||||
** Again, this structure is intended to be opaque, but it can't really
|
||||
** be opaque because it is used by macros.
|
||||
*/
|
||||
struct ArrayElem {
|
||||
ArrayElem *next, *prev; /* Next and previous elements in the array */
|
||||
Datum key, data; /* Key and data for this element */
|
||||
};
|
||||
|
||||
/* Some routines are so simple that they can be implemented as macros
|
||||
** These are given first. */
|
||||
|
||||
/* Return the number of entries in the array */
|
||||
#define ArrayCount(X) ((X)->count)
|
||||
|
||||
/* Return a pointer to the first element of the array */
|
||||
#define ArrayFirst(X) ((X)->first)
|
||||
|
||||
/* Return a pointer to the next (or previous) element of the array */
|
||||
#define ArrayNext(X) ((X)->next)
|
||||
#define ArrayPrev(X) ((X)->prev)
|
||||
|
||||
/* Return TRUE if the element given is the last element in the array */
|
||||
#define ArrayIsLast(X) ((X)->next==0)
|
||||
#define ArrayIsFirst(X) ((X)->prev==0)
|
||||
|
||||
/* Return the data or key for an element of the array */
|
||||
#define ArrayData(X) ((X)->data.p)
|
||||
#define ArrayDataSize(X) ((X)->data.n)
|
||||
#define ArrayKey(X) ((X)->key.p)
|
||||
#define ArrayKeySize(X) ((X)->key.n)
|
||||
|
||||
/* Turn bulk memory into an associative array object by initializing the
|
||||
** fields of the Array structure.
|
||||
*/
|
||||
static void ArrayInit(Array *new){
|
||||
new->first = 0;
|
||||
new->count = 0;
|
||||
new->htsize = 0;
|
||||
new->ht = 0;
|
||||
}
|
||||
|
||||
/* Remove all entries from an associative array. Reclaim all memory.
|
||||
** This is the opposite of ArrayInit().
|
||||
*/
|
||||
static void ArrayClear(Array *array){
|
||||
ArrayElem *elem; /* For looping over all elements of the array */
|
||||
|
||||
elem = array->first;
|
||||
array->first = 0;
|
||||
array->count = 0;
|
||||
if( array->ht ) sqliteFree(array->ht);
|
||||
array->ht = 0;
|
||||
array->htsize = 0;
|
||||
while( elem ){
|
||||
ArrayElem *next_elem = elem->next;
|
||||
sqliteFree(elem);
|
||||
elem = next_elem;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate a hash from an N-byte key
|
||||
*/
|
||||
static int ArrayHash(Datum d){
|
||||
int h = 0;
|
||||
while( d.n-- > 0 ){
|
||||
/* The funky case "*(char**)&d.p" is to work around a bug the
|
||||
** c89 compiler of HPUX. */
|
||||
h = (h<<9) ^ (h<<3) ^ h ^ *((*(char**)&d.p)++);
|
||||
}
|
||||
if( h<0 ) h = -h;
|
||||
return h;
|
||||
}
|
||||
|
||||
/* Resize the hash table for a Array array
|
||||
*/
|
||||
static void ArrayRehash(Array *array, int new_size){
|
||||
struct _Array_ht *new_ht; /* The new hash table */
|
||||
ArrayElem *elem, *next_elem; /* For looping over existing elements */
|
||||
int i; /* Loop counter */
|
||||
ArrayElem *x; /* Element being copied to new hash table */
|
||||
|
||||
new_ht = sqliteMalloc( new_size*sizeof(struct _Array_ht) );
|
||||
if( new_ht==0 ){ ArrayClear(array); return; }
|
||||
if( array->ht ) sqliteFree(array->ht);
|
||||
array->ht = new_ht;
|
||||
array->htsize = new_size;
|
||||
for(i=new_size-1; i>=0; i--){
|
||||
new_ht[i].count = 0;
|
||||
new_ht[i].chain = 0;
|
||||
}
|
||||
for(elem=array->first, array->first=0; elem; elem = next_elem){
|
||||
int h = ArrayHash(elem->key) & (new_size-1);
|
||||
next_elem = elem->next;
|
||||
x = new_ht[h].chain;
|
||||
if( x ){
|
||||
elem->next = x;
|
||||
elem->prev = x->prev;
|
||||
if( x->prev ) x->prev->next = elem;
|
||||
else array->first = elem;
|
||||
x->prev = elem;
|
||||
}else{
|
||||
elem->next = array->first;
|
||||
if( array->first ) array->first->prev = elem;
|
||||
elem->prev = 0;
|
||||
array->first = elem;
|
||||
}
|
||||
new_ht[h].chain = elem;
|
||||
new_ht[h].count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* This function (for internal use only) locates an element in an
|
||||
** array that matches the given key. The hash for this key has
|
||||
** already been computed and is passed as the 3rd parameter.
|
||||
*/
|
||||
static ArrayElem *ArrayFindElementGivenHash(
|
||||
const Array *array, /* The array to be searched */
|
||||
const Datum key, /* The key we are searching for */
|
||||
int h /* The hash for this key. */
|
||||
){
|
||||
ArrayElem *elem; /* Used to loop thru the element list */
|
||||
int count; /* Number of elements left to test */
|
||||
|
||||
if( array->count ){
|
||||
elem = array->ht[h].chain;
|
||||
count = array->ht[h].count;
|
||||
while( count-- && elem ){
|
||||
if( elem->key.n==key.n && memcmp(elem->key.p,key.p,key.n)==0 ){
|
||||
return elem;
|
||||
}
|
||||
elem = elem->next;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Attempt to locate an element of the associative array with a key
|
||||
** that matches "key". Return the ArrayElement if found and NULL if
|
||||
** if no match.
|
||||
*/
|
||||
static ArrayElem *ArrayFindElement(const Array *array, Datum key){
|
||||
int h; /* A hash on key */
|
||||
if( array->count==0 ) return 0;
|
||||
h = ArrayHash(key);
|
||||
return ArrayFindElementGivenHash(array, key, h & (array->htsize-1));
|
||||
}
|
||||
|
||||
/* Remove a single entry from the array given a pointer to that
|
||||
** element and a hash on the element's key.
|
||||
*/
|
||||
static void ArrayRemoveElementGivenHash(
|
||||
Array *array, /* The array containing "elem" */
|
||||
ArrayElem* elem, /* The element to be removed from the array */
|
||||
int h /* Hash value for the element */
|
||||
){
|
||||
if( elem->prev ){
|
||||
elem->prev->next = elem->next;
|
||||
}else{
|
||||
array->first = elem->next;
|
||||
}
|
||||
if( elem->next ){
|
||||
elem->next->prev = elem->prev;
|
||||
}
|
||||
if( array->ht[h].chain==elem ){
|
||||
array->ht[h].chain = elem->next;
|
||||
}
|
||||
array->ht[h].count--;
|
||||
if( array->ht[h].count<=0 ){
|
||||
array->ht[h].chain = 0;
|
||||
}
|
||||
sqliteFree( elem );
|
||||
array->count--;
|
||||
}
|
||||
|
||||
/* Attempt to locate an element of the associative array with a key
|
||||
** that matches "key". Return the data for this element if it is
|
||||
** found, or NULL if no match is found.
|
||||
*/
|
||||
static Datum ArrayFind(const Array *array, Datum key){
|
||||
int h; /* A hash on key */
|
||||
ArrayElem *elem; /* The element that matches key */
|
||||
static Datum nil = {0, 0};
|
||||
|
||||
if( array->count==0 ) return nil;
|
||||
h = ArrayHash(key);
|
||||
elem = ArrayFindElementGivenHash(array, key, h & (array->htsize-1));
|
||||
return elem ? elem->data : nil;
|
||||
}
|
||||
|
||||
/* Insert an element into the array. The key will be "key" and
|
||||
** the data will be "data".
|
||||
**
|
||||
** If no array element exists with a matching key, then a new
|
||||
** array element is created. The key is copied into the new element.
|
||||
** But only a pointer to the data is stored. NULL is returned.
|
||||
**
|
||||
** If another element already exists with the same key, then the
|
||||
** new data replaces the old data and the old data is returned.
|
||||
** The key is not copied in this instance.
|
||||
**
|
||||
** If the "data" parameter to this function is NULL, then the
|
||||
** element corresponding to "key" is removed from the array.
|
||||
*/
|
||||
static Datum ArrayInsert(Array *array, Datum key, Datum data){
|
||||
int hraw; /* Raw hash value of the key */
|
||||
int h; /* the hash of the key modulo hash table size */
|
||||
ArrayElem *elem; /* Used to loop thru the element list */
|
||||
ArrayElem *new_elem; /* New element added to the array */
|
||||
Datum rv; /* Return value */
|
||||
static Datum nil = {0, 0};
|
||||
|
||||
hraw = ArrayHash(key);
|
||||
h = hraw & (array->htsize-1);
|
||||
elem = ArrayFindElementGivenHash(array,key,h);
|
||||
if( elem ){
|
||||
Datum old_data = elem->data;
|
||||
if( data.p==0 ){
|
||||
ArrayRemoveElementGivenHash(array,elem,h);
|
||||
}else{
|
||||
elem->data = data;
|
||||
}
|
||||
return old_data;
|
||||
}
|
||||
if( data.p==0 ) return nil;
|
||||
new_elem = (ArrayElem*)sqliteMalloc( sizeof(ArrayElem) + key.n );
|
||||
if( new_elem==0 ) return nil;
|
||||
new_elem->key.n = key.n;
|
||||
new_elem->key.p = (void*)&new_elem[1];
|
||||
memcpy(new_elem->key.p, key.p, key.n);
|
||||
array->count++;
|
||||
if( array->htsize==0 ) ArrayRehash(array,4);
|
||||
if( array->htsize==0 ) return nil;
|
||||
if( array->count > array->htsize ){
|
||||
ArrayRehash(array,array->htsize*2);
|
||||
if( array->htsize==0 ){
|
||||
sqliteFree(new_elem);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
h = hraw & (array->htsize-1);
|
||||
elem = array->ht[h].chain;
|
||||
if( elem ){
|
||||
new_elem->next = elem;
|
||||
new_elem->prev = elem->prev;
|
||||
if( elem->prev ){ elem->prev->next = new_elem; }
|
||||
else { array->first = new_elem; }
|
||||
elem->prev = new_elem;
|
||||
}else{
|
||||
new_elem->next = array->first;
|
||||
new_elem->prev = 0;
|
||||
if( array->first ){ array->first->prev = new_elem; }
|
||||
array->first = new_elem;
|
||||
}
|
||||
array->ht[h].count++;
|
||||
array->ht[h].chain = new_elem;
|
||||
new_elem->data = data;
|
||||
rv.p = 0;
|
||||
rv.n = 0;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
** Information about each open database table is an instance of this
|
||||
** structure. There will only be one such structure for each
|
||||
** table. If the VDBE opens the same table twice (as will happen
|
||||
** for a self-join, for example) then two DbbeCursor structures are
|
||||
** created but there is only a single MTable structure.
|
||||
*/
|
||||
typedef struct MTable MTable;
|
||||
struct MTable {
|
||||
char *zName; /* Name of the table */
|
||||
int delOnClose; /* Delete when closing */
|
||||
int intKeyOnly; /* Use only integer keys on this table */
|
||||
Array data; /* The data in this stable */
|
||||
};
|
||||
|
||||
/*
|
||||
** The following structure contains all information used by GDBM
|
||||
** database driver. This is a subclass of the Dbbe structure.
|
||||
*/
|
||||
typedef struct Dbbex Dbbex;
|
||||
struct Dbbex {
|
||||
Dbbe dbbe; /* The base class */
|
||||
Array tables; /* All tables of the database */
|
||||
};
|
||||
|
||||
/*
|
||||
** An cursor into a database file is an instance of the following structure.
|
||||
** There can only be a single MTable structure for each disk file, but
|
||||
** there can be multiple DbbeCursor structures. Each DbbeCursor represents
|
||||
** a cursor pointing to a particular part of the open MTable. The
|
||||
** MTable.nRef field hold a count of the number of DbbeCursor structures
|
||||
** associated with the same disk file.
|
||||
*/
|
||||
struct DbbeCursor {
|
||||
Dbbex *pBe; /* The database of which this record is a part */
|
||||
MTable *pTble; /* The database file for this table */
|
||||
ArrayElem *elem; /* Most recently accessed record */
|
||||
int needRewind; /* Next key should be the first */
|
||||
int nextIndex; /* Next recno in an index entry */
|
||||
};
|
||||
|
||||
/*
|
||||
** Forward declaration
|
||||
*/
|
||||
static void sqliteMemCloseCursor(DbbeCursor *pCursr);
|
||||
|
||||
/*
|
||||
** Erase all the memory of an MTable
|
||||
*/
|
||||
static void deleteMTable(MTable *p){
|
||||
ArrayElem *i;
|
||||
for(i=ArrayFirst(&p->data); i; i=ArrayNext(i)){
|
||||
void *data = ArrayData(i);
|
||||
sqliteFree(data);
|
||||
}
|
||||
ArrayClear(&p->data);
|
||||
sqliteFree(p->zName);
|
||||
sqliteFree(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Completely shutdown the given database. Close all files. Free all memory.
|
||||
*/
|
||||
static void sqliteMemClose(Dbbe *pDbbe){
|
||||
Dbbex *pBe = (Dbbex*)pDbbe;
|
||||
MTable *pTble;
|
||||
ArrayElem *j;
|
||||
for(j=ArrayFirst(&pBe->tables); j; j=ArrayNext(j)){
|
||||
pTble = ArrayData(j);
|
||||
deleteMTable(pTble);
|
||||
}
|
||||
ArrayClear(&pBe->tables);
|
||||
memset(pBe, 0, sizeof(*pBe));
|
||||
sqliteFree(pBe);
|
||||
}
|
||||
|
||||
/*
|
||||
** Translate the name of an SQL table (or index) into its
|
||||
** canonical name.
|
||||
**
|
||||
** Space to hold the canonical name is obtained from
|
||||
** sqliteMalloc() and must be freed by the calling function.
|
||||
*/
|
||||
static char *sqliteNameOfTable(const char *zTable){
|
||||
char *zNew = 0;
|
||||
int i, c;
|
||||
sqliteSetString(&zNew, zTable, 0);
|
||||
if( zNew==0 ) return 0;
|
||||
for(i=0; (c = zNew[i])!=0; i++){
|
||||
if( isupper(c) ){
|
||||
zNew[i] = tolower(c);
|
||||
}
|
||||
}
|
||||
return zNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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 zTable is 0 or "", then a temporary database file is created and
|
||||
** a cursor to that temporary file is opened. The temporary file
|
||||
** will be deleted from the disk when it is closed.
|
||||
*/
|
||||
static int sqliteMemOpenCursor(
|
||||
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 */
|
||||
){
|
||||
DbbeCursor *pCursr; /* The new table cursor */
|
||||
char *zName; /* Canonical table name */
|
||||
MTable *pTble; /* The underlying data file for this table */
|
||||
int rc = SQLITE_OK; /* Return value */
|
||||
Dbbex *pBe = (Dbbex*)pDbbe;
|
||||
|
||||
*ppCursr = 0;
|
||||
pCursr = sqliteMalloc( sizeof(*pCursr) );
|
||||
if( pCursr==0 ) return SQLITE_NOMEM;
|
||||
if( zTable ){
|
||||
Datum key;
|
||||
zName = sqliteNameOfTable(zTable);
|
||||
if( zName==0 ) return SQLITE_NOMEM;
|
||||
key.p = zName;
|
||||
key.n = strlen(zName);
|
||||
pTble = ArrayFind(&pBe->tables, key).p;
|
||||
}else{
|
||||
zName = 0;
|
||||
pTble = 0;
|
||||
}
|
||||
if( pTble==0 ){
|
||||
pTble = sqliteMalloc( sizeof(*pTble) );
|
||||
if( pTble==0 ){
|
||||
sqliteFree(zName);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
if( zName ){
|
||||
Datum ins_key, ins_data;
|
||||
pTble->zName = zName;
|
||||
pTble->delOnClose = 0;
|
||||
ins_data.p = pTble;
|
||||
ins_data.n = sizeof( *pTble );
|
||||
ins_key.p = zName;
|
||||
ins_key.n = strlen(zName);
|
||||
ArrayInsert(&pBe->tables, ins_key, ins_data);
|
||||
}else{
|
||||
pTble->zName = 0;
|
||||
pTble->delOnClose = 1;
|
||||
}
|
||||
pTble->intKeyOnly = intKeyOnly;
|
||||
ArrayInit(&pTble->data);
|
||||
}else{
|
||||
assert( pTble->intKeyOnly==intKeyOnly );
|
||||
sqliteFree(zName);
|
||||
}
|
||||
pCursr->pBe = pBe;
|
||||
pCursr->pTble = pTble;
|
||||
pCursr->needRewind = 1;
|
||||
*ppCursr = pCursr;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Drop a table from the database. The file on the disk that corresponds
|
||||
** to this table is deleted.
|
||||
*/
|
||||
static void sqliteMemDropTable(Dbbe *pDbbe, const char *zTable){
|
||||
char *zName; /* Name of the table file */
|
||||
Datum key, data;
|
||||
MTable *pTble;
|
||||
Dbbex *pBe = (Dbbex*)pDbbe;
|
||||
|
||||
zName = sqliteNameOfTable(zTable);
|
||||
key.p = zName;
|
||||
key.n = strlen(zName);
|
||||
pTble = ArrayFind(&pBe->tables, key).p;
|
||||
if( pTble ){
|
||||
data.p = 0;
|
||||
data.n = 0;
|
||||
ArrayInsert(&pBe->tables, key, data);
|
||||
deleteMTable(pTble);
|
||||
}
|
||||
sqliteFree(zName);
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a cursor previously opened by sqliteMemOpenCursor().
|
||||
**
|
||||
** There can be multiple cursors pointing to the same open file.
|
||||
** The underlying file is not closed until all cursors have been
|
||||
** closed. This routine decrements the MTable.nref field of the
|
||||
** underlying file and closes the file when nref reaches 0.
|
||||
*/
|
||||
static void sqliteMemCloseCursor(DbbeCursor *pCursr){
|
||||
MTable *pTble;
|
||||
Dbbex *pBe;
|
||||
if( pCursr==0 ) return;
|
||||
pTble = pCursr->pTble;
|
||||
pBe = pCursr->pBe;
|
||||
if( pTble->delOnClose ){
|
||||
deleteMTable(pTble);
|
||||
}
|
||||
sqliteFree(pCursr);
|
||||
}
|
||||
|
||||
/*
|
||||
** Reorganize a table to reduce search times and disk usage.
|
||||
*/
|
||||
static int sqliteMemReorganizeTable(Dbbe *pBe, const char *zTable){
|
||||
/* Do nothing */
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Fetch a single record from an open cursor. Return 1 on success
|
||||
** and 0 on failure.
|
||||
*/
|
||||
static int sqliteMemFetch(DbbeCursor *pCursr, int nKey, char *pKey){
|
||||
Datum key;
|
||||
key.n = nKey;
|
||||
key.p = pKey;
|
||||
assert( nKey==4 || pCursr->pTble->intKeyOnly==0 );
|
||||
pCursr->elem = ArrayFindElement(&pCursr->pTble->data, key);
|
||||
return pCursr->elem!=0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return 1 if the given key is already in the table. Return 0
|
||||
** if it is not.
|
||||
*/
|
||||
static int sqliteMemTest(DbbeCursor *pCursr, int nKey, char *pKey){
|
||||
return sqliteMemFetch(pCursr, nKey, pKey);
|
||||
}
|
||||
|
||||
/*
|
||||
** Copy bytes from the current key or data into a buffer supplied by
|
||||
** the calling function. Return the number of bytes copied.
|
||||
*/
|
||||
static
|
||||
int sqliteMemCopyKey(DbbeCursor *pCursr, int offset, int size, char *zBuf){
|
||||
int n;
|
||||
if( pCursr->elem==0 ) return 0;
|
||||
if( offset>=ArrayKeySize(pCursr->elem) ) return 0;
|
||||
if( offset+size>ArrayKeySize(pCursr->elem) ){
|
||||
n = ArrayKeySize(pCursr->elem) - offset;
|
||||
}else{
|
||||
n = size;
|
||||
}
|
||||
memcpy(zBuf, &((char*)ArrayKey(pCursr->elem))[offset], n);
|
||||
return n;
|
||||
}
|
||||
static
|
||||
int sqliteMemCopyData(DbbeCursor *pCursr, int offset, int size, char *zBuf){
|
||||
int n;
|
||||
if( pCursr->elem==0 ) return 0;
|
||||
if( offset>=ArrayDataSize(pCursr->elem) ) return 0;
|
||||
if( offset+size>ArrayDataSize(pCursr->elem) ){
|
||||
n = ArrayDataSize(pCursr->elem) - offset;
|
||||
}else{
|
||||
n = size;
|
||||
}
|
||||
memcpy(zBuf, &((char*)ArrayData(pCursr->elem))[offset], n);
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to bytes from the key or data. The data returned
|
||||
** is ephemeral.
|
||||
*/
|
||||
static char *sqliteMemReadKey(DbbeCursor *pCursr, int offset){
|
||||
if( pCursr->elem==0 || offset<0 || offset>=ArrayKeySize(pCursr->elem) ){
|
||||
return "";
|
||||
}
|
||||
return &((char*)ArrayKey(pCursr->elem))[offset];
|
||||
}
|
||||
static char *sqliteMemReadData(DbbeCursor *pCursr, int offset){
|
||||
if( pCursr->elem==0 || offset<0 || offset>=ArrayDataSize(pCursr->elem) ){
|
||||
return "";
|
||||
}
|
||||
return &((char*)ArrayData(pCursr->elem))[offset];
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the total number of bytes in either data or key.
|
||||
*/
|
||||
static int sqliteMemKeyLength(DbbeCursor *pCursr){
|
||||
return pCursr->elem ? ArrayKeySize(pCursr->elem) : 0;
|
||||
}
|
||||
static int sqliteMemDataLength(DbbeCursor *pCursr){
|
||||
return pCursr->elem ? ArrayDataSize(pCursr->elem) : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Make is so that the next call to sqliteNextKey() finds the first
|
||||
** key of the table.
|
||||
*/
|
||||
static int sqliteMemRewind(DbbeCursor *pCursr){
|
||||
pCursr->needRewind = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read the next key from the table. Return 1 on success. Return
|
||||
** 0 if there are no more keys.
|
||||
*/
|
||||
static int sqliteMemNextKey(DbbeCursor *pCursr){
|
||||
if( pCursr->needRewind || pCursr->elem==0 ){
|
||||
pCursr->elem = ArrayFirst(&pCursr->pTble->data);
|
||||
pCursr->needRewind = 0;
|
||||
}else{
|
||||
pCursr->elem = ArrayNext(pCursr->elem);
|
||||
}
|
||||
return pCursr->elem!=0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Get a new integer key.
|
||||
*/
|
||||
static int sqliteMemNew(DbbeCursor *pCursr){
|
||||
int iKey;
|
||||
Datum key;
|
||||
int go = 1;
|
||||
|
||||
while( go ){
|
||||
iKey = sqliteRandomInteger() & 0x7fffffff;
|
||||
if( iKey==0 ) continue;
|
||||
key.p = (char*)&iKey;
|
||||
key.n = 4;
|
||||
go = ArrayFindElement(&pCursr->pTble->data, key)!=0;
|
||||
}
|
||||
return iKey;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write an entry into the table. Overwrite any prior entry with the
|
||||
** same key.
|
||||
*/
|
||||
static int sqliteMemPut(
|
||||
DbbeCursor *pCursr, /* Write new entry into this database table */
|
||||
int nKey, char *pKey, /* The key of the new entry */
|
||||
int nData, char *pData /* The data of the new entry */
|
||||
){
|
||||
Datum data, key;
|
||||
data.n = nData;
|
||||
data.p = sqliteMalloc( data.n );
|
||||
if( data.p==0 ) return SQLITE_NOMEM;
|
||||
memcpy(data.p, pData, data.n);
|
||||
key.n = nKey;
|
||||
key.p = pKey;
|
||||
assert( nKey==4 || pCursr->pTble->intKeyOnly==0 );
|
||||
data = ArrayInsert(&pCursr->pTble->data, key, data);
|
||||
if( data.p ){
|
||||
sqliteFree(data.p);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Remove an entry from a table, if the entry exists.
|
||||
*/
|
||||
static int sqliteMemDelete(DbbeCursor *pCursr, int nKey, char *pKey){
|
||||
Datum key, data;
|
||||
key.n = nKey;
|
||||
key.p = pKey;
|
||||
data.p = 0;
|
||||
data.n = 0;
|
||||
data = ArrayInsert(&pCursr->pTble->data, key, data);
|
||||
if( data.p ){
|
||||
sqliteFree(data.p);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Begin scanning an index for the given key. Return 1 on success and
|
||||
** 0 on failure.
|
||||
*/
|
||||
static int sqliteMemBeginIndex(DbbeCursor *pCursr, int nKey, char *pKey){
|
||||
if( !sqliteMemFetch(pCursr, nKey, pKey) ) return 0;
|
||||
pCursr->nextIndex = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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 sqliteMemNextIndex(DbbeCursor *pCursr){
|
||||
int *aIdx;
|
||||
int nIdx;
|
||||
int k;
|
||||
nIdx = sqliteMemDataLength(pCursr)/sizeof(int);
|
||||
aIdx = (int*)sqliteMemReadData(pCursr, 0);
|
||||
if( nIdx>1 ){
|
||||
k = *(aIdx++);
|
||||
if( k>nIdx-1 ) k = nIdx-1;
|
||||
}else{
|
||||
k = nIdx;
|
||||
}
|
||||
while( pCursr->nextIndex < k ){
|
||||
int recno = aIdx[pCursr->nextIndex++];
|
||||
if( recno!=0 ) return recno;
|
||||
}
|
||||
pCursr->nextIndex = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write a new record number and key into an index table. Return a status
|
||||
** code.
|
||||
*/
|
||||
static int sqliteMemPutIndex(DbbeCursor *pCursr, int nKey, char *pKey, int N){
|
||||
int r = sqliteMemFetch(pCursr, nKey, pKey);
|
||||
if( r==0 ){
|
||||
/* Create a new record for this index */
|
||||
sqliteMemPut(pCursr, nKey, pKey, sizeof(int), (char*)&N);
|
||||
}else{
|
||||
/* Extend the existing record */
|
||||
int nIdx;
|
||||
int *aIdx;
|
||||
int k;
|
||||
|
||||
nIdx = sqliteMemDataLength(pCursr)/sizeof(int);
|
||||
if( nIdx==1 ){
|
||||
aIdx = sqliteMalloc( sizeof(int)*4 );
|
||||
if( aIdx==0 ) return SQLITE_NOMEM;
|
||||
aIdx[0] = 2;
|
||||
sqliteMemCopyData(pCursr, 0, sizeof(int), (char*)&aIdx[1]);
|
||||
aIdx[2] = N;
|
||||
sqliteMemPut(pCursr, nKey, pKey, sizeof(int)*4, (char*)aIdx);
|
||||
sqliteFree(aIdx);
|
||||
}else{
|
||||
aIdx = (int*)sqliteMemReadData(pCursr, 0);
|
||||
k = aIdx[0];
|
||||
if( k<nIdx-1 ){
|
||||
aIdx[k+1] = N;
|
||||
aIdx[0]++;
|
||||
sqliteMemPut(pCursr, nKey, pKey, sizeof(int)*nIdx, (char*)aIdx);
|
||||
}else{
|
||||
nIdx *= 2;
|
||||
aIdx = sqliteMalloc( sizeof(int)*nIdx );
|
||||
if( aIdx==0 ) return SQLITE_NOMEM;
|
||||
sqliteMemCopyData(pCursr, 0, sizeof(int)*(k+1), (char*)aIdx);
|
||||
aIdx[k+1] = N;
|
||||
aIdx[0]++;
|
||||
sqliteMemPut(pCursr, nKey, pKey, sizeof(int)*nIdx, (char*)aIdx);
|
||||
sqliteFree(aIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete an index entry. Return a status code.
|
||||
*/
|
||||
static int sqliteMemDeleteIndex(DbbeCursor *pCursr,int nKey,char *pKey, int N){
|
||||
int *aIdx;
|
||||
int nIdx;
|
||||
int j, k;
|
||||
int rc;
|
||||
rc = sqliteMemFetch(pCursr, nKey, pKey);
|
||||
if( !rc ) return SQLITE_OK;
|
||||
nIdx = sqliteMemDataLength(pCursr)/sizeof(int);
|
||||
if( nIdx==0 ) return SQLITE_OK;
|
||||
aIdx = (int*)sqliteMemReadData(pCursr, 0);
|
||||
if( (nIdx==1 && aIdx[0]==N) || (aIdx[0]==1 && aIdx[1]==N) ){
|
||||
sqliteMemDelete(pCursr, nKey, pKey);
|
||||
}else{
|
||||
k = aIdx[0];
|
||||
for(j=1; j<=k && aIdx[j]!=N; j++){}
|
||||
if( j>k ) return SQLITE_OK;
|
||||
aIdx[j] = aIdx[k];
|
||||
aIdx[k] = 0;
|
||||
aIdx[0]--;
|
||||
if( aIdx[0]*3 + 1 < nIdx ){
|
||||
nIdx /= 2;
|
||||
}
|
||||
sqliteMemPut(pCursr, nKey, pKey, sizeof(int)*nIdx, (char*)aIdx);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This variable contains pointers to all of the access methods
|
||||
** used to implement the MEMORY backend.
|
||||
*/
|
||||
static struct DbbeMethods memoryMethods = {
|
||||
/* Close */ sqliteMemClose,
|
||||
/* OpenCursor */ sqliteMemOpenCursor,
|
||||
/* DropTable */ sqliteMemDropTable,
|
||||
/* ReorganizeTable */ sqliteMemReorganizeTable,
|
||||
/* CloseCursor */ sqliteMemCloseCursor,
|
||||
/* Fetch */ sqliteMemFetch,
|
||||
/* Test */ sqliteMemTest,
|
||||
/* CopyKey */ sqliteMemCopyKey,
|
||||
/* CopyData */ sqliteMemCopyData,
|
||||
/* ReadKey */ sqliteMemReadKey,
|
||||
/* ReadData */ sqliteMemReadData,
|
||||
/* KeyLength */ sqliteMemKeyLength,
|
||||
/* DataLength */ sqliteMemDataLength,
|
||||
/* NextKey */ sqliteMemNextKey,
|
||||
/* Rewind */ sqliteMemRewind,
|
||||
/* New */ sqliteMemNew,
|
||||
/* Put */ sqliteMemPut,
|
||||
/* Delete */ sqliteMemDelete,
|
||||
/* BeginTrans */ 0,
|
||||
/* Commit */ 0,
|
||||
/* Rollback */ 0,
|
||||
/* BeginIndex */ sqliteMemBeginIndex,
|
||||
/* NextIndex */ sqliteMemNextIndex,
|
||||
/* PutIndex */ sqliteMemPutIndex,
|
||||
/* DeleteIndex */ sqliteMemDeleteIndex,
|
||||
};
|
||||
|
||||
/*
|
||||
** This routine opens a new database. For the MEMORY driver
|
||||
** implemented here, the database name is ignored. Every MEMORY database
|
||||
** is unique and is erased when the database is closed.
|
||||
**
|
||||
** 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 *sqliteMemOpen(
|
||||
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;
|
||||
|
||||
pNew = sqliteMalloc( sizeof(*pNew) );
|
||||
if( pNew==0 ){
|
||||
sqliteSetString(pzErrMsg, "out of memory", 0);
|
||||
return 0;
|
||||
}
|
||||
ArrayInit(&pNew->tables);
|
||||
pNew->dbbe.x = &memoryMethods;
|
||||
return &pNew->dbbe;
|
||||
}
|
@ -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.10 2001/09/13 13:46:56 drh Exp $
|
||||
** $Id: delete.c,v 1.11 2001/09/13 14:46:10 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -137,12 +137,12 @@ void sqliteDeleteFrom(
|
||||
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0);
|
||||
if( pTab->pIndex ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Fetch, base, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MoveTo, base, 0, 0, 0);
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
int j;
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
|
||||
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_MakeIdxKey, pIdx->nColumn, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_DeleteIdx, base+i, 0, 0, 0);
|
||||
|
@ -24,7 +24,7 @@
|
||||
** This file contains routines used for analyzing expressions and
|
||||
** for generating VDBE code that evaluates expressions.
|
||||
**
|
||||
** $Id: expr.c,v 1.25 2001/07/23 14:33:04 drh Exp $
|
||||
** $Id: expr.c,v 1.26 2001/09/13 14:46:10 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -246,7 +246,7 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
|
||||
** table. The cursor number of the temporary table has already
|
||||
** been put in iTable by sqliteExprResolveInSelect().
|
||||
*/
|
||||
sqliteVdbeAddOp(v, OP_OpenIdx, pExpr->iTable, 1, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenTemp, pExpr->iTable, 0, 0, 0);
|
||||
if( sqliteSelect(pParse, pExpr->pSelect, SRT_Set, pExpr->iTable) );
|
||||
}else if( pExpr->pList ){
|
||||
/* Case 2: expr IN (exprlist)
|
||||
@ -515,9 +515,9 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
||||
if( pParse->useAgg ){
|
||||
sqliteVdbeAddOp(v, OP_AggGet, 0, pExpr->iAgg, 0, 0);
|
||||
}else if( pExpr->iColumn>=0 ){
|
||||
sqliteVdbeAddOp(v, OP_Field, pExpr->iTable, pExpr->iColumn, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Column, pExpr->iTable, pExpr->iColumn, 0, 0);
|
||||
}else{
|
||||
sqliteVdbeAddOp(v, OP_Key, pExpr->iTable, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_FullKey, pExpr->iTable, 0, 0, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
14
src/insert.c
14
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.14 2001/09/13 13:46:56 drh Exp $
|
||||
** $Id: insert.c,v 1.15 2001/09/13 14:46:10 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -98,7 +98,7 @@ void sqliteInsert(
|
||||
if( pSelect ){
|
||||
int rc;
|
||||
srcTab = pParse->nTab++;
|
||||
sqliteVdbeAddOp(v, OP_OpenTbl, srcTab, 1, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenTemp, srcTab, 0, 0, 0);
|
||||
rc = sqliteSelect(pParse, pSelect, SRT_Table, srcTab);
|
||||
if( rc || pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup;
|
||||
assert( pSelect->pEList );
|
||||
@ -163,9 +163,9 @@ void sqliteInsert(
|
||||
** all indices of that table.
|
||||
*/
|
||||
base = pParse->nTab;
|
||||
sqliteVdbeAddOp(v, OP_OpenTbl, base, 1, pTab->zName, 0);
|
||||
sqliteVdbeAddOp(v, OP_Open, base, pTab->tnum, pTab->zName, 0);
|
||||
for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
|
||||
sqliteVdbeAddOp(v, OP_OpenIdx, idx+base, 1, pIdx->zName, 0);
|
||||
sqliteVdbeAddOp(v, OP_Open, idx+base, pIdx->tnum, pIdx->zName, 0);
|
||||
}
|
||||
|
||||
/* If the data source is a SELECT statement, then we have to create
|
||||
@ -181,7 +181,7 @@ void sqliteInsert(
|
||||
|
||||
/* Create a new entry in the table and fill it with data.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
@ -201,7 +201,7 @@ void sqliteInsert(
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0, zDflt, 0);
|
||||
}
|
||||
}else if( srcTab>=0 ){
|
||||
sqliteVdbeAddOp(v, OP_Field, srcTab, i, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Column, srcTab, i, 0, 0);
|
||||
}else{
|
||||
sqliteExprCode(pParse, pList->a[j].pExpr);
|
||||
}
|
||||
@ -233,7 +233,7 @@ void sqliteInsert(
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0, zDflt, 0);
|
||||
}
|
||||
}else if( srcTab>=0 ){
|
||||
sqliteVdbeAddOp(v, OP_Field, srcTab, idx, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Column, srcTab, idx, 0, 0);
|
||||
}else{
|
||||
sqliteExprCode(pParse, pList->a[j].pExpr);
|
||||
}
|
||||
|
@ -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.30 2001/09/13 13:46:56 drh Exp $
|
||||
** $Id: main.c,v 1.31 2001/09/13 14:46:10 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#if defined(HAVE_USLEEP) && HAVE_USLEEP
|
||||
@ -233,7 +233,7 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
|
||||
}
|
||||
}
|
||||
sqliteFree(db);
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Assume file format 1 unless the database says otherwise */
|
||||
|
10
src/pager.c
10
src/pager.c
@ -27,7 +27,7 @@
|
||||
** all writes in order to support rollback. Locking is used to limit
|
||||
** access to one or more reader or to one writer.
|
||||
**
|
||||
** @(#) $Id: pager.c,v 1.14 2001/09/13 13:46:57 drh Exp $
|
||||
** @(#) $Id: pager.c,v 1.15 2001/09/13 14:46:10 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "pager.h"
|
||||
@ -476,7 +476,7 @@ static const char *findTempDir(void){
|
||||
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) ){
|
||||
&& access(azDirs[i], W_OK) ){
|
||||
return azDirs[i];
|
||||
}
|
||||
}
|
||||
@ -515,11 +515,11 @@ int sqlitepager_open(
|
||||
tempFile = 0;
|
||||
}else{
|
||||
int cnt = 8;
|
||||
char *zDir = findTempDir();
|
||||
const char *zDir = findTempDir();
|
||||
if( zDir==0 ) return SQLITE_CANTOPEN;
|
||||
do{
|
||||
cnt--;
|
||||
sprintf(zTemp,"%s/_sqlite_%u",(unsigned)sqliteRandomInteger());
|
||||
sprintf(zTemp,"%s/_sqlite_%u", zDir, (unsigned)sqliteRandomInteger());
|
||||
fd = open(zTemp, O_RDWR|O_CREAT|O_EXCL, 0600);
|
||||
}while( cnt>0 && fd<0 );
|
||||
zFilename = zTemp;
|
||||
@ -1138,7 +1138,7 @@ int sqlitepager_rollback(Pager *pPager){
|
||||
** if the database is (in theory) writable.
|
||||
*/
|
||||
int sqlitepager_isreadonly(Pager *pPager){
|
||||
return pPager->readonly;
|
||||
return pPager->readOnly;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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.32 2001/09/13 13:46:57 drh Exp $
|
||||
** $Id: select.c,v 1.33 2001/09/13 14:46:10 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -122,7 +122,7 @@ static int selectInnerLoop(
|
||||
nColumn = pEList->nExpr;
|
||||
}else{
|
||||
for(i=0; i<nColumn; i++){
|
||||
sqliteVdbeAddOp(v, OP_Field, srcTab, i, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Column, srcTab, i, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,7 +171,7 @@ static int selectInnerLoop(
|
||||
*/
|
||||
if( eDest==SRT_Table ){
|
||||
sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_New, iParm, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_NewRecno, iParm, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Pull, 1, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
|
||||
}else
|
||||
|
@ -23,14 +23,12 @@
|
||||
*************************************************************************
|
||||
** Internal interface definitions for SQLite.
|
||||
**
|
||||
** @(#) $Id: sqliteInt.h,v 1.43 2001/09/13 13:46:57 drh Exp $
|
||||
** @(#) $Id: sqliteInt.h,v 1.44 2001/09/13 14:46:10 drh Exp $
|
||||
*/
|
||||
#include "sqlite.h"
|
||||
#include "vdbe.h"
|
||||
#include "parse.h"
|
||||
#ifndef DISABLE_GDBM
|
||||
#include <gdbm.h>
|
||||
#endif
|
||||
#include "btree.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -182,7 +180,7 @@ struct 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 */
|
||||
int isDelete; /* True if this table is being deleted */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -210,6 +208,7 @@ struct Index {
|
||||
int nColumn; /* Number of columns in the table used by this index */
|
||||
int *aiColumn; /* Which columns are used by this index. 1st is 0 */
|
||||
Table *pTable; /* The SQL table being indexed */
|
||||
int tnum; /* Page containing root of this index in database file */
|
||||
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 */
|
||||
@ -445,7 +444,6 @@ Vdbe *sqliteGetVdbe(Parse*);
|
||||
int sqliteRandomByte(void);
|
||||
int sqliteRandomInteger(void);
|
||||
void sqliteRandomName(char*,char*);
|
||||
char *sqliteDbbeNameToFile(const char*,const char*,const char*);
|
||||
void sqliteBeginTransaction(Parse*);
|
||||
void sqliteCommitTransaction(Parse*);
|
||||
void sqliteRollbackTransaction(Parse*);
|
||||
|
110
src/vdbe.c
110
src/vdbe.c
@ -41,7 +41,7 @@
|
||||
** But other routines are also provided to help in building up
|
||||
** a program instruction by instruction.
|
||||
**
|
||||
** $Id: vdbe.c,v 1.60 2001/09/13 13:46:57 drh Exp $
|
||||
** $Id: vdbe.c,v 1.61 2001/09/13 14:46:11 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@ -194,7 +194,7 @@ struct Keylist {
|
||||
*/
|
||||
struct Vdbe {
|
||||
sqlite *db; /* The whole database */
|
||||
Dbbe *pBe; /* Opaque context structure used by DB backend */
|
||||
Btree *pBt; /* Opaque context structure used by DB backend */
|
||||
FILE *trace; /* Write an execution trace here, if not NULL */
|
||||
int nOp; /* Number of instructions in the program */
|
||||
int nOpAlloc; /* Number of slots allocated for aOp[] */
|
||||
@ -235,7 +235,7 @@ Vdbe *sqliteVdbeCreate(sqlite *db){
|
||||
Vdbe *p;
|
||||
p = sqliteMalloc( sizeof(Vdbe) );
|
||||
if( p==0 ) return 0;
|
||||
p->pBe = db->pBe;
|
||||
p->pBt = db->pBe;
|
||||
p->db = db;
|
||||
return p;
|
||||
}
|
||||
@ -881,20 +881,20 @@ static char *zOpName[] = { 0,
|
||||
"ListRewind", "ListRead", "ListClose", "SortOpen",
|
||||
"SortPut", "SortMakeRec", "SortMakeKey", "Sort",
|
||||
"SortNext", "SortKey", "SortCallback", "SortClose",
|
||||
"FileOpen", "FileRead", "FileField", "FileClose",
|
||||
"FileOpen", "FileRead", "FileColumn", "FileClose",
|
||||
"AggReset", "AggFocus", "AggIncr", "AggNext",
|
||||
"AggSet", "AggGet", "SetInsert", "SetFound",
|
||||
"SetNotFound", "SetClear", "MakeRecord", "MakeKey",
|
||||
"Goto", "If", "Halt", "ColumnCount",
|
||||
"ColumnName", "Callback", "Integer", "String",
|
||||
"Null", "Pop", "Dup", "Pull",
|
||||
"Add", "AddImm", "Subtract", "Multiply",
|
||||
"Divide", "Min", "Max", "Like",
|
||||
"Glob", "Eq", "Ne", "Lt",
|
||||
"Le", "Gt", "Ge", "IsNull",
|
||||
"NotNull", "Negative", "And", "Or",
|
||||
"Not", "Concat", "Noop", "Strlen",
|
||||
"Substr",
|
||||
"MakeIdxKey", "Goto", "If", "Halt",
|
||||
"ColumnCount", "ColumnName", "Callback", "Integer",
|
||||
"String", "Null", "Pop", "Dup",
|
||||
"Pull", "Add", "AddImm", "Subtract",
|
||||
"Multiply", "Divide", "Min", "Max",
|
||||
"Like", "Glob", "Eq", "Ne",
|
||||
"Lt", "Le", "Gt", "Ge",
|
||||
"IsNull", "NotNull", "Negative", "And",
|
||||
"Or", "Not", "Concat", "Noop",
|
||||
"Strlen", "Substr",
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1038,9 +1038,8 @@ int sqliteVdbeExec(
|
||||
int pc; /* The program counter */
|
||||
Op *pOp; /* Current operation */
|
||||
int rc; /* Value to return */
|
||||
Dbbe *pBe = p->pBe; /* The backend driver */
|
||||
Btree *pBt = p->pBt; /* The backend driver */
|
||||
sqlite *db = p->db; /* The database */
|
||||
int rollbackOnError = 0; /* If TRUE, rollback if the script fails.
|
||||
char **zStack; /* Text stack */
|
||||
Stack *aStack; /* Additional stack information */
|
||||
char zBuf[100]; /* Space to sprintf() an integer */
|
||||
@ -1920,7 +1919,7 @@ case OP_MakeIdxKey: {
|
||||
}
|
||||
zNewKey[j++] = 0;
|
||||
Integerify(p, p->tos-nField);
|
||||
memcpy(&zNewKey[j], aStack[p->tos-nField].i, sizeof(int));
|
||||
memcpy(&zNewKey[j], &aStack[p->tos-nField].i, sizeof(int));
|
||||
PopStack(p, nField+1);
|
||||
VERIFY( NeedStack(p, p->tos+1); )
|
||||
p->tos++;
|
||||
@ -1940,7 +1939,7 @@ case OP_MakeIdxKey: {
|
||||
** database.
|
||||
*/
|
||||
case OP_Transaction: {
|
||||
rc = sqliteBtreeBeginTrans(pBe);
|
||||
rc = sqliteBtreeBeginTrans(pBt);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1951,7 +1950,7 @@ case OP_Transaction: {
|
||||
** are allowed until another transaction is started.
|
||||
*/
|
||||
case OP_Commit: {
|
||||
rc = sqliteBtreeCommit(pBe);
|
||||
rc = sqliteBtreeCommit(pBt);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqliteCommitInternalChanges(db);
|
||||
}else{
|
||||
@ -1968,7 +1967,7 @@ case OP_Commit: {
|
||||
** are allowed until another transaction is started.
|
||||
*/
|
||||
case OP_Rollback: {
|
||||
rc = sqliteBtreeRollback(pBe);
|
||||
rc = sqliteBtreeRollback(pBt);
|
||||
sqliteRollbackInternalChanges(db);
|
||||
break;
|
||||
}
|
||||
@ -1999,8 +1998,8 @@ case OP_Open: {
|
||||
sqliteBtreeCloseCursor(p->aCsr[i].pCursor);
|
||||
}
|
||||
memset(&p->aCsr[i], 0, sizeof(Cursor));
|
||||
do {
|
||||
rc = sqliteBtreeOpenCursor(pBe, pOp->p2, &p->aCsr[i].pCursor);
|
||||
do{
|
||||
rc = sqliteBtreeCursor(pBt, pOp->p2, &p->aCsr[i].pCursor);
|
||||
switch( rc ){
|
||||
case SQLITE_BUSY: {
|
||||
if( xBusy==0 || (*xBusy)(pBusyArg, pOp->p3, ++busy)==0 ){
|
||||
@ -2029,7 +2028,6 @@ case OP_Open: {
|
||||
** cursor is closed.
|
||||
*/
|
||||
case OP_OpenTemp: {
|
||||
int busy = 0;
|
||||
int i = pOp->p1;
|
||||
Cursor *pCx;
|
||||
VERIFY( if( i<0 ) goto bad_instruction; )
|
||||
@ -2046,7 +2044,7 @@ case OP_OpenTemp: {
|
||||
memset(pCx, 0, sizeof(*pCx));
|
||||
rc = sqliteBtreeOpen(0, 0, 100, &pCx->pBt);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqliteBtreeOpenCursor(pCx->pBt, 2, &pCx->pCursor);
|
||||
rc = sqliteBtreeCursor(pCx->pBt, 2, &pCx->pCursor);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqliteBtreeBeginTrans(pCx->pBt);
|
||||
@ -2091,14 +2089,13 @@ case OP_MoveTo: {
|
||||
if( i>=0 && i<p->nCursor && p->aCsr[i].pCursor ){
|
||||
int res;
|
||||
if( aStack[tos].flags & STK_Int ){
|
||||
sqliteBtreeMoveTo(p->aCsr[i].pCursor, sizeof(int),
|
||||
(char*)&aStack[tos].i, &res);
|
||||
sqliteBtreeMoveto(p->aCsr[i].pCursor,
|
||||
(char*)&aStack[tos].i, sizeof(int), &res);
|
||||
p->aCsr[i].lastRecno = aStack[tos].i;
|
||||
p->aCsr[i].recnoIsValid = 1;
|
||||
}else{
|
||||
if( Stringify(p, tos) ) goto no_mem;
|
||||
pBex->Fetch(p->aCsr[i].pCursor, aStack[tos].n,
|
||||
zStack[tos], &res);
|
||||
sqliteBtreeMoveto(p->aCsr[i].pCursor, zStack[tos], aStack[tos].n, &res);
|
||||
p->aCsr[i].recnoIsValid = 0;
|
||||
}
|
||||
p->nFetch++;
|
||||
@ -2161,12 +2158,12 @@ case OP_Found: {
|
||||
if( VERIFY( i>=0 && i<p->nCursor && ) p->aCsr[i].pCursor ){
|
||||
int res, rx;
|
||||
if( aStack[tos].flags & STK_Int ){
|
||||
rx = sqliteBtreeMoveTo(p->aCsr[i].pCursor, sizeof(int),
|
||||
(char*)&aStack[tos].i, &res);
|
||||
rx = sqliteBtreeMoveto(p->aCsr[i].pCursor,
|
||||
(char*)&aStack[tos].i, sizeof(int), &res);
|
||||
}else{
|
||||
if( Stringify(p, tos) ) goto no_mem;
|
||||
rx = sqliteBtreeMoveTo(p->aCsr[i].pCursor,aStack[tos].n,
|
||||
zStack[tos], &res);
|
||||
rx = sqliteBtreeMoveto(p->aCsr[i].pCursor,
|
||||
zStack[tos], aStack[tos].n, &res);
|
||||
}
|
||||
alreadyExists = rx==SQLITE_OK && res==0;
|
||||
}
|
||||
@ -2198,7 +2195,7 @@ case OP_NewRecno: {
|
||||
cnt = 0;
|
||||
do{
|
||||
v = sqliteRandomInteger();
|
||||
rx = sqliteBtreeMoveTo(p->aCsr[i].pCursor, sizeof(v), &v, &res);
|
||||
rx = sqliteBtreeMoveto(p->aCsr[i].pCursor, &v, sizeof(v), &res);
|
||||
cnt++;
|
||||
}while( cnt<10 && rx==SQLITE_OK && res==0 );
|
||||
}
|
||||
@ -2233,9 +2230,8 @@ case OP_Put: {
|
||||
nKey = sizeof(int);
|
||||
zKey = (char*)&aStack[nos].i;
|
||||
}
|
||||
rc = sqliteBtreeInsert(p->aCsr[i].pCursor, nKey, zKey,
|
||||
aStack[tos].n, zStack[tos]);
|
||||
if( rc!=SQLITE_OK ) goto abort_due_to_error;
|
||||
rc = sqliteBtreeInsert(p->aCsr[i].pCursor, zKey, nKey,
|
||||
zStack[tos], aStack[tos].n);
|
||||
}
|
||||
POPSTACK;
|
||||
POPSTACK;
|
||||
@ -2250,6 +2246,7 @@ case OP_Put: {
|
||||
case OP_Delete: {
|
||||
int tos = p->tos;
|
||||
int i = pOp->p1;
|
||||
int res;
|
||||
VERIFY( if( tos<0 ) goto not_enough_stack; )
|
||||
if( VERIFY( i>=0 && i<p->nCursor && ) p->aCsr[i].pCursor!=0 ){
|
||||
char *zKey;
|
||||
@ -2262,8 +2259,8 @@ case OP_Delete: {
|
||||
nKey = aStack[tos].n;
|
||||
zKey = zStack[tos];
|
||||
}
|
||||
rc = sqliteBtreeDelete(p->aCsr[i].pCursor, nKey, zKey);
|
||||
if( rc!=SQLITE_OK ) goto abort_due_to_error;
|
||||
rc = sqliteBtreeMoveto(p->aCsr[i].pCursor, zKey, nKey, &res);
|
||||
rc = sqliteBtreeDelete(p->aCsr[i].pCursor);
|
||||
}
|
||||
POPSTACK;
|
||||
break;
|
||||
@ -2301,7 +2298,6 @@ case OP_KeyAsData: {
|
||||
** data.
|
||||
*/
|
||||
case OP_Column: {
|
||||
int *pAddr;
|
||||
int amt, offset, nCol, payloadSize;
|
||||
int aHdr[10];
|
||||
const int mxHdr = sizeof(aHdr)/sizeof(aHdr[0]);
|
||||
@ -2314,7 +2310,7 @@ case OP_Column: {
|
||||
VERIFY( if( NeedStack(p, tos) ) goto no_mem; )
|
||||
if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){
|
||||
int (*xSize)(BtCursor*, int*);
|
||||
int (*xRead)(BtCursor*, int, int, void*);
|
||||
int (*xRead)(BtCursor*, int, int, char*);
|
||||
|
||||
/* Use different access functions depending on whether the information
|
||||
** is coming from the key or the data of the record.
|
||||
@ -2340,7 +2336,7 @@ case OP_Column: {
|
||||
goto abort_due_to_error;
|
||||
}
|
||||
if( p2+1<mxHdr ){
|
||||
(*xRead)(pCrsr, 0, sizeof(aHdr[0])*(p2+2), aHdr);
|
||||
(*xRead)(pCrsr, 0, sizeof(aHdr[0])*(p2+2), (char*)aHdr);
|
||||
nCol = aHdr[0];
|
||||
offset = aHdr[p2];
|
||||
if( p2 == nCol-1 ){
|
||||
@ -2349,13 +2345,13 @@ case OP_Column: {
|
||||
amt = aHdr[p2+1] - offset;
|
||||
}
|
||||
}else{
|
||||
sqliteBtreeData(pCrsr, 0, sizeof(int), &nCol);
|
||||
sqliteBtreeData(pCrsr, 0, sizeof(int), (char*)&nCol);
|
||||
nCol /= sizeof(int);
|
||||
if( p2 == nCol-1 ){
|
||||
(*xRead)(pCrsr, sizeof(int)*p2, sizeof(int), &offset);
|
||||
(*xRead)(pCrsr, sizeof(int)*p2, sizeof(int), (char*)&offset);
|
||||
amt = payloadSize - offset;
|
||||
}else{
|
||||
(*xRead)(pCrsr, sizeof(int)*p2, sizeof(int)*2, aHdr);
|
||||
(*xRead)(pCrsr, sizeof(int)*p2, sizeof(int)*2, (char*)aHdr);
|
||||
offset = aHdr[0];
|
||||
amt = aHdr[1] - offset;
|
||||
}
|
||||
@ -2396,7 +2392,7 @@ case OP_Recno: {
|
||||
if( p->aCsr[i].recnoIsValid ){
|
||||
v = p->aCsr[i].lastRecno;
|
||||
}else{
|
||||
sqliteBtreeKey(pCrsr, 0, sizeof(int), &v);
|
||||
sqliteBtreeKey(pCrsr, 0, sizeof(int), (char*)&v);
|
||||
}
|
||||
aStack[tos].i = v;
|
||||
aStack[tos].flags = STK_Int;
|
||||
@ -2506,7 +2502,7 @@ case OP_BeginIdx: {
|
||||
pCrsr->zBuf = &pCrsr->zKey[pCrsr->nKey+1];
|
||||
strncpy(pCrsr->zKey, zStack[tos], aStack[tos].n);
|
||||
pCrsr->zKey[aStack[tos].n] = 0;
|
||||
rx = sqliteBtreeMoveTo(pCrsr->pCursor, aStack[tos].n, zStack[tos], &res);
|
||||
rx = sqliteBtreeMoveto(pCrsr->pCursor, zStack[tos], aStack[tos].n, &res);
|
||||
pCrsr->atFirst = rx==SQLITE_OK && res>0;
|
||||
pCrsr->recnoIsValid = 0;
|
||||
}
|
||||
@ -2526,16 +2522,16 @@ case OP_NextIdx: {
|
||||
int i = pOp->p1;
|
||||
int tos = ++p->tos;
|
||||
Cursor *pCrsr;
|
||||
BtCursr *pCur;
|
||||
BtCursor *pCur;
|
||||
int rx, res, size;
|
||||
|
||||
VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; )
|
||||
zStack[tos] = 0;
|
||||
if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = &p->aCsr[i])->pCursor)!=0 ){
|
||||
if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = &p->aCsr[i])->pCursor!=0 ){
|
||||
pCur = pCrsr->pCursor;
|
||||
rx = sqliteBtreeNext(pCur, &res);
|
||||
if( rx!=SQLITE_OK ) goto abort_due_to_error;
|
||||
sqliteBtreeKeySzie(pCur, &size);
|
||||
sqliteBtreeKeySize(pCur, &size);
|
||||
if( res>0 || size!=pCrsr->nKey+sizeof(int) ||
|
||||
sqliteBtreeKey(pCur, 0, pCrsr->nKey, pCrsr->zBuf)!=pCrsr->nKey ||
|
||||
strncmp(pCrsr->zKey, pCrsr->zBuf, pCrsr->nKey)!=0
|
||||
@ -2544,7 +2540,7 @@ case OP_NextIdx: {
|
||||
POPSTACK;
|
||||
}else{
|
||||
int recno;
|
||||
sqliteBtreeKey(pCur, pCrsr->nKey, sizeof(int), &recno);
|
||||
sqliteBtreeKey(pCur, pCrsr->nKey, sizeof(int), (char*)&recno);
|
||||
p->aCsr[i].lastRecno = aStack[tos].i = recno;
|
||||
p->aCsr[i].recnoIsValid = 1;
|
||||
aStack[tos].flags = STK_Int;
|
||||
@ -2565,7 +2561,7 @@ case OP_PutIdx: {
|
||||
BtCursor *pCrsr;
|
||||
VERIFY( if( tos<0 ) goto not_enough_stack; )
|
||||
if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){
|
||||
sqliteBtreePut(pCrsr, aStack[tos].n, zStack[tos], 0, "");
|
||||
sqliteBtreeInsert(pCrsr, zStack[tos], aStack[tos].n, "", 0);
|
||||
}
|
||||
POPSTACK;
|
||||
break;
|
||||
@ -2583,7 +2579,7 @@ case OP_DeleteIdx: {
|
||||
VERIFY( if( tos<0 ) goto not_enough_stack; )
|
||||
if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){
|
||||
int rx, res;
|
||||
rx = sqliteBtreeMoveTo(pCrsr, aStack[tos].n, zStack[tos], &res);
|
||||
rx = sqliteBtreeMoveto(pCrsr, zStack[tos], aStack[tos].n, &res);
|
||||
if( rx==SQLITE_OK && res==0 ){
|
||||
sqliteBtreeDelete(pCrsr);
|
||||
}
|
||||
@ -2598,7 +2594,7 @@ case OP_DeleteIdx: {
|
||||
** file is given by P1.
|
||||
*/
|
||||
case OP_Destroy: {
|
||||
sqliteBtreeDropTable(pBe, pOp->p1);
|
||||
sqliteBtreeDropTable(pBt, pOp->p1);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -3115,12 +3111,12 @@ fileread_jump:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: FileField P1 * *
|
||||
/* Opcode: FileColumn P1 * *
|
||||
**
|
||||
** Push onto the stack the P1-th field of the most recently read line
|
||||
** from the input file.
|
||||
*/
|
||||
case OP_FileField: {
|
||||
case OP_FileColumn: {
|
||||
int i = pOp->p1;
|
||||
char *z;
|
||||
VERIFY( if( NeedStack(p, p->tos+1) ) goto no_mem; )
|
||||
@ -3628,7 +3624,7 @@ default: {
|
||||
cleanup:
|
||||
Cleanup(p);
|
||||
if( rc!=SQLITE_OK && (db->flags & SQLITE_InTrans)!=0 ){
|
||||
sqliteBtreeRollback(pBe);
|
||||
sqliteBtreeRollback(pBt);
|
||||
sqliteRollbackInternalChanges(db);
|
||||
db->flags &= ~SQLITE_InTrans;
|
||||
}
|
||||
@ -3645,7 +3641,7 @@ no_mem:
|
||||
/* Jump to here for any other kind of fatal error. The "rc" variable
|
||||
** should hold the error number.
|
||||
*/
|
||||
abort_due_to_err:
|
||||
abort_due_to_error:
|
||||
sqliteSetString(pzErrMsg, sqliteErrStr(rc), 0);
|
||||
goto cleanup;
|
||||
|
||||
|
@ -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.19 2001/09/13 13:46:57 drh Exp $
|
||||
** $Id: vdbe.h,v 1.20 2001/09/13 14:46:11 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITE_VDBE_H_
|
||||
#define _SQLITE_VDBE_H_
|
||||
@ -124,7 +124,7 @@ typedef struct VdbeOp VdbeOp;
|
||||
|
||||
#define OP_FileOpen 45
|
||||
#define OP_FileRead 46
|
||||
#define OP_FileField 47
|
||||
#define OP_FileColumn 47
|
||||
#define OP_FileClose 48
|
||||
|
||||
#define OP_AggReset 49
|
||||
|
12
src/where.c
12
src/where.c
@ -25,7 +25,7 @@
|
||||
** the WHERE clause of SQL statements. Also found here are subroutines
|
||||
** to generate VDBE code to evaluate expressions.
|
||||
**
|
||||
** $Id: where.c,v 1.16 2001/09/13 13:46:57 drh Exp $
|
||||
** $Id: where.c,v 1.17 2001/09/13 14:46:11 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -300,7 +300,7 @@ WhereInfo *sqliteWhereBegin(
|
||||
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_Open, base+pTabList->nId+i, aIdx[i]->tnum
|
||||
sqliteVdbeAddOp(v, OP_Open, base+pTabList->nId+i, aIdx[i]->tnum,
|
||||
aIdx[i]->zName, 0);
|
||||
}
|
||||
}
|
||||
@ -351,7 +351,7 @@ WhereInfo *sqliteWhereBegin(
|
||||
if( i==pTabList->nId-1 && pushKey ){
|
||||
haveKey = 1;
|
||||
}else{
|
||||
sqliteVdbeAddOp(v, OP_Fetch, base+idx, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MoveTo, base+idx, 0, 0, 0);
|
||||
haveKey = 0;
|
||||
}
|
||||
}else if( pIdx==0 ){
|
||||
@ -392,7 +392,7 @@ WhereInfo *sqliteWhereBegin(
|
||||
if( i==pTabList->nId-1 && pushKey ){
|
||||
haveKey = 1;
|
||||
}else{
|
||||
sqliteVdbeAddOp(v, OP_Fetch, base+idx, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MoveTo, base+idx, 0, 0, 0);
|
||||
haveKey = 0;
|
||||
}
|
||||
}
|
||||
@ -407,7 +407,7 @@ WhereInfo *sqliteWhereBegin(
|
||||
if( (aExpr[j].prereqLeft & loopMask)!=aExpr[j].prereqLeft ) continue;
|
||||
if( haveKey ){
|
||||
haveKey = 0;
|
||||
sqliteVdbeAddOp(v, OP_Fetch, base+idx, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MoveTo, base+idx, 0, 0, 0);
|
||||
}
|
||||
sqliteExprIfFalse(pParse, aExpr[j].p, cont);
|
||||
aExpr[j].p = 0;
|
||||
@ -416,7 +416,7 @@ WhereInfo *sqliteWhereBegin(
|
||||
}
|
||||
pWInfo->iContinue = cont;
|
||||
if( pushKey && !haveKey ){
|
||||
sqliteVdbeAddOp(v, OP_Key, base, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Recno, base, 0, 0, 0);
|
||||
}
|
||||
sqliteFree(aOrder);
|
||||
return pWInfo;
|
||||
|
@ -23,7 +23,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is btree database backend
|
||||
#
|
||||
# $Id: btree2.test,v 1.4 2001/08/20 00:33:58 drh Exp $
|
||||
# $Id: btree2.test,v 1.5 2001/09/13 14:46:11 drh Exp $
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@ -288,8 +288,8 @@ foreach {N L} {
|
||||
10 2
|
||||
50 2
|
||||
200 3
|
||||
2000 5
|
||||
} {
|
||||
# 2000 5
|
||||
puts "**** N=$N L=$L ****"
|
||||
set hash [md5file test2.bt]
|
||||
do_test btree2-$testno.1 [subst -nocommands {
|
||||
|
Loading…
Reference in New Issue
Block a user