mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-01 06:27:03 +03:00
:-) (CVS 104)
FossilOrigin-Name: e1bf96a467b739373191bf75e6a097fc0f24bffc
This commit is contained in:
56
manifest
56
manifest
@ -1,50 +1,50 @@
|
||||
C :-)\s(CVS\s103)
|
||||
D 2000-06-19T19:10:29
|
||||
C :-)\s(CVS\s104)
|
||||
D 2000-06-21T13:59:11
|
||||
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
|
||||
F Makefile.in 4dc16840f68e3b599915e1ec8463d365474dd286
|
||||
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
|
||||
F configure c366a0402bce79ef11fe1bf703ad6ce4ff6afbb0 x
|
||||
F configure.in 1085ff994a334b131325de906ed318e926673588
|
||||
F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
|
||||
F src/build.c 9ce11eafbab395b7d19bc5722d1a8955961889b0
|
||||
F src/dbbe.c ae5e77f010ba1b68a65aa3cd55c8578eda523dd2
|
||||
F src/dbbe.h 9a678ae524c2daad22e959111edd4494e6144dbc
|
||||
F src/delete.c 2d5758055ff546453385524d856feb1b51ea7b8a
|
||||
F src/expr.c 88ff9ea12a23a3f0dfaf117670524bdc160af597
|
||||
F src/insert.c b1434c7c7c387c69e467d993e9d05460f1047bcc
|
||||
F src/main.c e3297835b8e38ca726ac73f2c2bdb7cf08103197
|
||||
F src/parse.y 974ed07702bda4f04171ef22776eccbb5dae81ca
|
||||
F src/select.c 2a91f683d64de0362834248cb291bc601cd2950b
|
||||
F src/shell.c 78a35607a88b3d557e1666ae9d0c2c03cbb3553e
|
||||
F src/build.c 55edb404bbf4476c73c81604ddb9738281a689a4
|
||||
F src/dbbe.c 99aa6daca9a039eebb284dd459bef712ea3843f9
|
||||
F src/dbbe.h 8718b718b36d37584e9bbdfccec10588fa91271f
|
||||
F src/delete.c 4d491eaf61b515516749c7ed68fa3b2ee8a09065
|
||||
F src/expr.c 2fa63f086707176d09092e71832f9bbdc6a8ac85
|
||||
F src/insert.c f146f149ad2422a1dc3bfa7a1651a25940f98958
|
||||
F src/main.c 30b33b6e0cdd5ae1c0af9f626e78a1dc7b835e26
|
||||
F src/parse.y 86e268c29a0f00ffc062bbe934d95ea0d6308b0a
|
||||
F src/select.c aaf23d4a6ef44e4378840ec94b6aa64641c01b5c
|
||||
F src/shell.c 8387580e44878022c88c02b189bf23bff1862bda
|
||||
F src/sqlite.h 58da0a8590133777b741f9836beaef3d58f40268
|
||||
F src/sqliteInt.h 19954bd2f75632849b265b9d7163a67391ec5148
|
||||
F src/sqliteInt.h ddc6f8081ef469ede272cf6a382773dac5758dfc
|
||||
F src/tclsqlite.c 9f358618ae803bedf4fb96da5154fd45023bc1f7
|
||||
F src/tokenize.c 77ff8164a8751994bc9926ce282847f653ac0c16
|
||||
F src/update.c 162fc0b86dcd61d164dedc77081a5e7daf6b8fb0
|
||||
F src/util.c 38e4bb5edf6fa92e677698c45785bf73c69b9e9f
|
||||
F src/vdbe.c 00b2ab7e0c0df2ac6eb4bf659656afc30e76c66b
|
||||
F src/update.c 51b9ef7434b15e31096155da920302e9db0d27fc
|
||||
F src/util.c fcd7ac9d2be8353f746e52f665e6c4f5d6b3b805
|
||||
F src/vdbe.c 38cec3e88db70b7689018377c1594ac18f746b19
|
||||
F src/vdbe.h 5f58611b19799de2dbcdefa4eef33a255cfa8d0d
|
||||
F src/where.c c9b90e7672f4662a83ef9a27a193020d69fe034c
|
||||
F src/where.c 420f666a38b405cd58bd7af832ed99f1dbc7d336
|
||||
F test/all.test 0950c135cab7e60c07bd745ccfad1476211e5bd7
|
||||
F test/copy.test b77a1214bd7756f2849d5c4fa6e715c0ff0c34eb
|
||||
F test/dbbe.test 0a8e4293cf816e590dcbb01be4cd4e8f7f95bdc8
|
||||
F test/delete.test 30edd2c7484274fb2e7dbc4a1ac769bb330b322e
|
||||
F test/delete.test 402ee3ccb6e544582d24c573ef70b34d09583ae7
|
||||
F test/expr.test 09b55ccf81cb8cc2f9cd83d592a2ba187ee48ba8
|
||||
F test/in.test 962a605b6a3a619214f84d1950dfc44fcf0d8b8f
|
||||
F test/in.test 2c560c0f55fb777029fd9bb5378f2997582aa603
|
||||
F test/index.test 620ceab7165dd078d1266bdc2cac6147f04534ac
|
||||
F test/insert.test 66f4c3bd600fec8eb1e733b928cbe6fa885eff0c
|
||||
F test/insert2.test 732405e30331635af8d159fccabe835eea5cd0c6
|
||||
F test/main.test b7366cc6f3690915a11834bc1090deeff08acaf9
|
||||
F test/select1.test 64703852af34c85bb31b0a74bd73b340e8267f42
|
||||
F test/select2.test 5e2783a48360b83956366ea24b2c5f0293015a84
|
||||
F test/select1.test 4e57b0b5eae0c991d9cc51d1288be0476110e6f6
|
||||
F test/select2.test ed6e7fc3437079686d7ae4390a00347bbd5f7bf8
|
||||
F test/select3.test a9234b8424b6c6d71de534f43b91ade9be68e9cc
|
||||
F test/select4.test cb5374d7c87680e294ac749307459a5cc547609d
|
||||
F test/select5.test 80ea257ce35e736c559bc6cd342361974e60a979
|
||||
F test/select5.test e2b9d51d88cbd6c307c2c05b0ef55fe7ba811ac2
|
||||
F test/sort.test d582086c4bb7df3fbf50aa72e69d7e235e9f8e31
|
||||
F test/subselect.test bf8b251a92fb091973c1c469ce499dc9648a41d5
|
||||
F test/table.test d3e01e4916a99ade7d8f1d534ee1b36d57c00490
|
||||
F test/tester.tcl 95b286791e6256bb6db0165f9342c70fff549a62
|
||||
F test/update.test 0f763adc3d84e85224e1dadff72237bcabab1938
|
||||
F test/update.test 62f6ce99ff31756aab0ca832ff6d34c5a87b6250
|
||||
F test/vacuum.test 8becf5cfeb897108b35cdd996793e7f1df2f28fd
|
||||
F test/where.test bbab5a308055fb6087dc23d600b4ad2b72797397
|
||||
F tool/gdbmdump.c 529e67c78d920606ba196326ea55b57b75fcc82b
|
||||
@ -57,14 +57,14 @@ F tool/renumberOps.awk 6d067177ad5f8d711b79577b462da9b3634bd0a9
|
||||
F www/arch.fig 4e26e9dca3c49724fc8f554c695ddea9f2413156
|
||||
F www/arch.png c4d908b79065a72e7dcf19317f36d1324c550e87
|
||||
F www/arch.tcl 4f6a9afecc099a27bba17b4f8cc9561abc15dc40
|
||||
F www/c_interface.tcl 9ac800854272db5fe439e07b7435b243a5422293
|
||||
F www/c_interface.tcl 8eb800f67e6896b1894d666b81c0b418cea09fc7
|
||||
F www/changes.tcl 160f7522145efaf49961f6cf3671c66c02f2207b
|
||||
F www/fileformat.tcl b11435fcd2cf2238a1c5e6d16fe5e83bcd14d434
|
||||
F www/index.tcl 4116afce6a8c63d68882d2b00aa10b079e0129cd
|
||||
F www/lang.tcl 1645e9107d75709be4c6099b643db235bbe0a151
|
||||
F www/opcode.tcl 3cdc4bb2515fcfcbe853e3f0c91cd9199e82dadd
|
||||
F www/sqlite.tcl 5420eab24b539928f80ea9b3088e2549d34f438d
|
||||
P 8cce4d279de00da45c5970c8f0946f49e03e6846
|
||||
R 24ae8084d8386391fc8e3403683f09be
|
||||
F www/sqlite.tcl 9fdcfd48fe9e5669116d02f29b2608903f295740
|
||||
P af14a5b3ba4a13665142b700e8864bf63d591d95
|
||||
R 4b875fc1d381ebbdab00fc05e8ad8c74
|
||||
U drh
|
||||
Z 34d7058e222643fe967104d6dc2aa522
|
||||
Z e8d8e8697e4d556ecd0e3a4c2a206a66
|
||||
|
@ -1 +1 @@
|
||||
af14a5b3ba4a13665142b700e8864bf63d591d95
|
||||
e1bf96a467b739373191bf75e6a097fc0f24bffc
|
28
src/build.c
28
src/build.c
@ -33,7 +33,7 @@
|
||||
** COPY
|
||||
** VACUUM
|
||||
**
|
||||
** $Id: build.c,v 1.18 2000/06/17 13:12:39 drh Exp $
|
||||
** $Id: build.c,v 1.19 2000/06/21 13:59:11 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -151,8 +151,8 @@ Index *sqliteFindIndex(sqlite *db, char *zName){
|
||||
** its memory structures.
|
||||
**
|
||||
** The index is removed from the database hash table, but it is
|
||||
** not unlinked from the table that is being indexed. Unlinking
|
||||
** from the table must be done by the calling function.
|
||||
** not unlinked from the Table that is being indexed. Unlinking
|
||||
** from the Table must be done by the calling function.
|
||||
*/
|
||||
static void sqliteDeleteIndex(sqlite *db, Index *pIndex){
|
||||
int h;
|
||||
@ -173,7 +173,7 @@ static void sqliteDeleteIndex(sqlite *db, Index *pIndex){
|
||||
|
||||
/*
|
||||
** Remove the memory data structures associated with the given
|
||||
** table. No changes are made to disk by this routine.
|
||||
** Table. No changes are made to disk by this routine.
|
||||
**
|
||||
** This routine just deletes the data structure. It does not unlink
|
||||
** the table data structure from the hash table. But does it destroy
|
||||
@ -512,11 +512,11 @@ void sqliteCreateIndex(
|
||||
pParse->nErr++;
|
||||
goto exit_create_index;
|
||||
}
|
||||
pIndex->aiField = (int*)&pIndex[1];
|
||||
pIndex->zName = (char*)&pIndex->aiField[pList->nId];
|
||||
pIndex->aiColumn = (int*)&pIndex[1];
|
||||
pIndex->zName = (char*)&pIndex->aiColumn[pList->nId];
|
||||
strcpy(pIndex->zName, zName);
|
||||
pIndex->pTable = pTab;
|
||||
pIndex->nField = pList->nId;
|
||||
pIndex->nColumn = pList->nId;
|
||||
|
||||
/* Scan the names of the columns of the table to be indexed and
|
||||
** load the column indices into the Index structure. Report an error
|
||||
@ -533,7 +533,7 @@ void sqliteCreateIndex(
|
||||
sqliteFree(pIndex);
|
||||
goto exit_create_index;
|
||||
}
|
||||
pIndex->aiField[i] = j;
|
||||
pIndex->aiColumn[i] = j;
|
||||
}
|
||||
|
||||
/* Link the new Index structure to its table and to the other
|
||||
@ -590,10 +590,10 @@ void sqliteCreateIndex(
|
||||
lbl2 = sqliteVdbeMakeLabel(v);
|
||||
sqliteVdbeAddOp(v, OP_Next, 0, lbl2, 0, lbl1);
|
||||
sqliteVdbeAddOp(v, OP_Key, 0, 0, 0, 0);
|
||||
for(i=0; i<pIndex->nField; i++){
|
||||
sqliteVdbeAddOp(v, OP_Field, 0, pIndex->aiField[i], 0, 0);
|
||||
for(i=0; i<pIndex->nColumn; i++){
|
||||
sqliteVdbeAddOp(v, OP_Field, 0, pIndex->aiColumn[i], 0, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, pIndex->nField, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, pIndex->nColumn, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_PutIdx, 1, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, lbl1, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, lbl2);
|
||||
@ -834,10 +834,10 @@ void sqliteCopy(
|
||||
if( pIdx->pNext ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
|
||||
}
|
||||
for(j=0; j<pIdx->nField; j++){
|
||||
sqliteVdbeAddOp(v, OP_FileField, pIdx->aiField[j], 0, 0, 0);
|
||||
for(j=0; j<pIdx->nColumn; j++){
|
||||
sqliteVdbeAddOp(v, OP_FileField, pIdx->aiColumn[j], 0, 0, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nColumn, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_PutIdx, i, 0, 0, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
|
||||
|
219
src/dbbe.c
219
src/dbbe.c
@ -30,7 +30,7 @@
|
||||
** relatively simple to convert to a different database such
|
||||
** as NDBM, SDBM, or BerkeleyDB.
|
||||
**
|
||||
** $Id: dbbe.c,v 1.14 2000/06/17 13:12:39 drh Exp $
|
||||
** $Id: dbbe.c,v 1.15 2000/06/21 13:59:11 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <gdbm.h>
|
||||
@ -43,7 +43,7 @@
|
||||
** 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 DbbeTable structures are
|
||||
** 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.
|
||||
*/
|
||||
@ -87,12 +87,12 @@ struct Dbbe {
|
||||
/*
|
||||
** 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 DbbeTable structures. Each DbbeTable represents
|
||||
** 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 DbbeTable structures
|
||||
** BeFile.nRef field hold a count of the number of DbbeCursor structures
|
||||
** associated with the same disk file.
|
||||
*/
|
||||
struct DbbeTable {
|
||||
struct DbbeCursor {
|
||||
Dbbe *pBe; /* The database of which this record is a part */
|
||||
BeFile *pFile; /* The database file for this table */
|
||||
datum key; /* Most recently used key */
|
||||
@ -226,8 +226,9 @@ void sqliteDbbeClose(Dbbe *pBe){
|
||||
}
|
||||
|
||||
/*
|
||||
** Translate the name of a table into the name of a file that holds
|
||||
** that table. Space to hold the filename is obtained from
|
||||
** 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(Dbbe *pBe, const char *zTable){
|
||||
@ -269,7 +270,7 @@ static void randomName(struct rc4 *pRc4, char *zBuf, char *zPrefix){
|
||||
|
||||
/*
|
||||
** Open a new table cursor. Write a pointer to the corresponding
|
||||
** DbbeTable structure into *ppTable. Return an integer success
|
||||
** DbbeCursor structure into *ppCursr. Return an integer success
|
||||
** code:
|
||||
**
|
||||
** SQLITE_OK It worked!
|
||||
@ -287,25 +288,26 @@ static void randomName(struct rc4 *pRc4, char *zBuf, char *zPrefix){
|
||||
** (This can happen if a SELECT callback tries to
|
||||
** do an UPDATE or DELETE.)
|
||||
**
|
||||
** If zTable is 0 or "", then a temporary table is created and opened.
|
||||
** This table will be deleted from the disk when it is closed.
|
||||
** 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.
|
||||
*/
|
||||
int sqliteDbbeOpenTable(
|
||||
int sqliteDbbeOpenCursor(
|
||||
Dbbe *pBe, /* The database the table belongs to */
|
||||
const char *zTable, /* The name of the table */
|
||||
const char *zTable, /* The SQL name of the file to be opened */
|
||||
int writeable, /* True to open for writing */
|
||||
DbbeTable **ppTable /* Write the resulting table pointer here */
|
||||
DbbeCursor **ppCursr /* Write the resulting table pointer here */
|
||||
){
|
||||
char *zFile; /* Name of the table file */
|
||||
DbbeTable *pTable; /* The new table cursor */
|
||||
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 */
|
||||
|
||||
*ppTable = 0;
|
||||
pTable = sqliteMalloc( sizeof(*pTable) );
|
||||
if( pTable==0 ) return SQLITE_NOMEM;
|
||||
*ppCursr = 0;
|
||||
pCursr = sqliteMalloc( sizeof(*pCursr) );
|
||||
if( pCursr==0 ) return SQLITE_NOMEM;
|
||||
if( zTable ){
|
||||
zFile = sqliteFileOfTable(pBe, zTable);
|
||||
for(pFile=pBe->pOpen; pFile; pFile=pFile->pNext){
|
||||
@ -376,11 +378,11 @@ int sqliteDbbeOpenTable(
|
||||
rc = SQLITE_READONLY;
|
||||
}
|
||||
}
|
||||
pTable->pBe = pBe;
|
||||
pTable->pFile = pFile;
|
||||
pTable->readPending = 0;
|
||||
pTable->needRewind = 1;
|
||||
*ppTable = pTable;
|
||||
pCursr->pBe = pBe;
|
||||
pCursr->pFile = pFile;
|
||||
pCursr->readPending = 0;
|
||||
pCursr->needRewind = 1;
|
||||
*ppCursr = pCursr;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -400,28 +402,33 @@ void sqliteDbbeDropTable(Dbbe *pBe, const char *zTable){
|
||||
** Reorganize a table to reduce search times and disk usage.
|
||||
*/
|
||||
void sqliteDbbeReorganizeTable(Dbbe *pBe, const char *zTable){
|
||||
DbbeTable *pTab;
|
||||
DbbeCursor *pCrsr;
|
||||
|
||||
if( sqliteDbbeOpenTable(pBe, zTable, 1, &pTab)!=SQLITE_OK ){
|
||||
if( sqliteDbbeOpenCursor(pBe, zTable, 1, &pCrsr)!=SQLITE_OK ){
|
||||
return;
|
||||
}
|
||||
if( pTab && pTab->pFile && pTab->pFile->dbf ){
|
||||
gdbm_reorganize(pTab->pFile->dbf);
|
||||
if( pCrsr && pCrsr->pFile && pCrsr->pFile->dbf ){
|
||||
gdbm_reorganize(pCrsr->pFile->dbf);
|
||||
}
|
||||
if( pTab ){
|
||||
sqliteDbbeCloseTable(pTab);
|
||||
if( pCrsr ){
|
||||
sqliteDbbeCloseCursor(pCrsr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a table previously opened by sqliteDbbeOpenTable().
|
||||
** Close a cursor previously opened by sqliteDbbeOpenCursor().
|
||||
**
|
||||
** 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.
|
||||
*/
|
||||
void sqliteDbbeCloseTable(DbbeTable *pTable){
|
||||
void sqliteDbbeCloseCursor(DbbeCursor *pCursr){
|
||||
BeFile *pFile;
|
||||
Dbbe *pBe;
|
||||
if( pTable==0 ) return;
|
||||
pFile = pTable->pFile;
|
||||
pBe = pTable->pBe;
|
||||
if( pCursr==0 ) return;
|
||||
pFile = pCursr->pFile;
|
||||
pBe = pCursr->pBe;
|
||||
pFile->nRef--;
|
||||
if( pFile->dbf!=NULL ){
|
||||
gdbm_sync(pFile->dbf);
|
||||
@ -445,10 +452,10 @@ void sqliteDbbeCloseTable(DbbeTable *pTable){
|
||||
memset(pFile, 0, sizeof(*pFile));
|
||||
sqliteFree(pFile);
|
||||
}
|
||||
if( pTable->key.dptr ) free(pTable->key.dptr);
|
||||
if( pTable->data.dptr ) free(pTable->data.dptr);
|
||||
memset(pTable, 0, sizeof(*pTable));
|
||||
sqliteFree(pTable);
|
||||
if( pCursr->key.dptr ) free(pCursr->key.dptr);
|
||||
if( pCursr->data.dptr ) free(pCursr->data.dptr);
|
||||
memset(pCursr, 0, sizeof(*pCursr));
|
||||
sqliteFree(pCursr);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -461,32 +468,32 @@ static void datumClear(datum *p){
|
||||
}
|
||||
|
||||
/*
|
||||
** Fetch a single record from an open table. Return 1 on success
|
||||
** Fetch a single record from an open cursor. Return 1 on success
|
||||
** and 0 on failure.
|
||||
*/
|
||||
int sqliteDbbeFetch(DbbeTable *pTable, int nKey, char *pKey){
|
||||
int sqliteDbbeFetch(DbbeCursor *pCursr, int nKey, char *pKey){
|
||||
datum key;
|
||||
key.dsize = nKey;
|
||||
key.dptr = pKey;
|
||||
datumClear(&pTable->key);
|
||||
datumClear(&pTable->data);
|
||||
if( pTable->pFile && pTable->pFile->dbf ){
|
||||
pTable->data = gdbm_fetch(pTable->pFile->dbf, key);
|
||||
datumClear(&pCursr->key);
|
||||
datumClear(&pCursr->data);
|
||||
if( pCursr->pFile && pCursr->pFile->dbf ){
|
||||
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, key);
|
||||
}
|
||||
return pTable->data.dptr!=0;
|
||||
return pCursr->data.dptr!=0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return 1 if the given key is already in the table. Return 0
|
||||
** if it is not.
|
||||
*/
|
||||
int sqliteDbbeTest(DbbeTable *pTable, int nKey, char *pKey){
|
||||
int sqliteDbbeTest(DbbeCursor *pCursr, int nKey, char *pKey){
|
||||
datum key;
|
||||
int result = 0;
|
||||
key.dsize = nKey;
|
||||
key.dptr = pKey;
|
||||
if( pTable->pFile && pTable->pFile->dbf ){
|
||||
result = gdbm_exists(pTable->pFile->dbf, key);
|
||||
if( pCursr->pFile && pCursr->pFile->dbf ){
|
||||
result = gdbm_exists(pCursr->pFile->dbf, key);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -495,30 +502,30 @@ int sqliteDbbeTest(DbbeTable *pTable, int nKey, char *pKey){
|
||||
** Copy bytes from the current key or data into a buffer supplied by
|
||||
** the calling function. Return the number of bytes copied.
|
||||
*/
|
||||
int sqliteDbbeCopyKey(DbbeTable *pTable, int offset, int size, char *zBuf){
|
||||
int sqliteDbbeCopyKey(DbbeCursor *pCursr, int offset, int size, char *zBuf){
|
||||
int n;
|
||||
if( offset>=pTable->key.dsize ) return 0;
|
||||
if( offset+size>pTable->key.dsize ){
|
||||
n = pTable->key.dsize - offset;
|
||||
if( offset>=pCursr->key.dsize ) return 0;
|
||||
if( offset+size>pCursr->key.dsize ){
|
||||
n = pCursr->key.dsize - offset;
|
||||
}else{
|
||||
n = size;
|
||||
}
|
||||
memcpy(zBuf, &pTable->key.dptr[offset], n);
|
||||
memcpy(zBuf, &pCursr->key.dptr[offset], n);
|
||||
return n;
|
||||
}
|
||||
int sqliteDbbeCopyData(DbbeTable *pTable, int offset, int size, char *zBuf){
|
||||
int sqliteDbbeCopyData(DbbeCursor *pCursr, int offset, int size, char *zBuf){
|
||||
int n;
|
||||
if( pTable->readPending && pTable->pFile && pTable->pFile->dbf ){
|
||||
pTable->data = gdbm_fetch(pTable->pFile->dbf, pTable->key);
|
||||
pTable->readPending = 0;
|
||||
if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
|
||||
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
|
||||
pCursr->readPending = 0;
|
||||
}
|
||||
if( offset>=pTable->data.dsize ) return 0;
|
||||
if( offset+size>pTable->data.dsize ){
|
||||
n = pTable->data.dsize - offset;
|
||||
if( offset>=pCursr->data.dsize ) return 0;
|
||||
if( offset+size>pCursr->data.dsize ){
|
||||
n = pCursr->data.dsize - offset;
|
||||
}else{
|
||||
n = size;
|
||||
}
|
||||
memcpy(zBuf, &pTable->data.dptr[offset], n);
|
||||
memcpy(zBuf, &pCursr->data.dptr[offset], n);
|
||||
return n;
|
||||
}
|
||||
|
||||
@ -526,39 +533,39 @@ int sqliteDbbeCopyData(DbbeTable *pTable, int offset, int size, char *zBuf){
|
||||
** Return a pointer to bytes from the key or data. The data returned
|
||||
** is ephemeral.
|
||||
*/
|
||||
char *sqliteDbbeReadKey(DbbeTable *pTable, int offset){
|
||||
if( offset<0 || offset>=pTable->key.dsize ) return "";
|
||||
return &pTable->key.dptr[offset];
|
||||
char *sqliteDbbeReadKey(DbbeCursor *pCursr, int offset){
|
||||
if( offset<0 || offset>=pCursr->key.dsize ) return "";
|
||||
return &pCursr->key.dptr[offset];
|
||||
}
|
||||
char *sqliteDbbeReadData(DbbeTable *pTable, int offset){
|
||||
if( pTable->readPending && pTable->pFile && pTable->pFile->dbf ){
|
||||
pTable->data = gdbm_fetch(pTable->pFile->dbf, pTable->key);
|
||||
pTable->readPending = 0;
|
||||
char *sqliteDbbeReadData(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>=pTable->data.dsize ) return "";
|
||||
return &pTable->data.dptr[offset];
|
||||
if( offset<0 || offset>=pCursr->data.dsize ) return "";
|
||||
return &pCursr->data.dptr[offset];
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the total number of bytes in either data or key.
|
||||
*/
|
||||
int sqliteDbbeKeyLength(DbbeTable *pTable){
|
||||
return pTable->key.dsize;
|
||||
int sqliteDbbeKeyLength(DbbeCursor *pCursr){
|
||||
return pCursr->key.dsize;
|
||||
}
|
||||
int sqliteDbbeDataLength(DbbeTable *pTable){
|
||||
if( pTable->readPending && pTable->pFile && pTable->pFile->dbf ){
|
||||
pTable->data = gdbm_fetch(pTable->pFile->dbf, pTable->key);
|
||||
pTable->readPending = 0;
|
||||
int sqliteDbbeDataLength(DbbeCursor *pCursr){
|
||||
if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
|
||||
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
|
||||
pCursr->readPending = 0;
|
||||
}
|
||||
return pTable->data.dsize;
|
||||
return pCursr->data.dsize;
|
||||
}
|
||||
|
||||
/*
|
||||
** Make is so that the next call to sqliteNextKey() finds the first
|
||||
** key of the table.
|
||||
*/
|
||||
int sqliteDbbeRewind(DbbeTable *pTable){
|
||||
pTable->needRewind = 1;
|
||||
int sqliteDbbeRewind(DbbeCursor *pCursr){
|
||||
pCursr->needRewind = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -566,28 +573,28 @@ int sqliteDbbeRewind(DbbeTable *pTable){
|
||||
** Read the next key from the table. Return 1 on success. Return
|
||||
** 0 if there are no more keys.
|
||||
*/
|
||||
int sqliteDbbeNextKey(DbbeTable *pTable){
|
||||
int sqliteDbbeNextKey(DbbeCursor *pCursr){
|
||||
datum nextkey;
|
||||
int rc;
|
||||
if( pTable==0 || pTable->pFile==0 || pTable->pFile->dbf==0 ){
|
||||
pTable->readPending = 0;
|
||||
if( pCursr==0 || pCursr->pFile==0 || pCursr->pFile->dbf==0 ){
|
||||
pCursr->readPending = 0;
|
||||
return 0;
|
||||
}
|
||||
if( pTable->needRewind ){
|
||||
nextkey = gdbm_firstkey(pTable->pFile->dbf);
|
||||
pTable->needRewind = 0;
|
||||
if( pCursr->needRewind ){
|
||||
nextkey = gdbm_firstkey(pCursr->pFile->dbf);
|
||||
pCursr->needRewind = 0;
|
||||
}else{
|
||||
nextkey = gdbm_nextkey(pTable->pFile->dbf, pTable->key);
|
||||
nextkey = gdbm_nextkey(pCursr->pFile->dbf, pCursr->key);
|
||||
}
|
||||
datumClear(&pTable->key);
|
||||
datumClear(&pTable->data);
|
||||
pTable->key = nextkey;
|
||||
if( pTable->key.dptr ){
|
||||
pTable->readPending = 1;
|
||||
datumClear(&pCursr->key);
|
||||
datumClear(&pCursr->data);
|
||||
pCursr->key = nextkey;
|
||||
if( pCursr->key.dptr ){
|
||||
pCursr->readPending = 1;
|
||||
rc = 1;
|
||||
}else{
|
||||
pTable->needRewind = 1;
|
||||
pTable->readPending = 0;
|
||||
pCursr->needRewind = 1;
|
||||
pCursr->readPending = 0;
|
||||
rc = 0;
|
||||
}
|
||||
return rc;
|
||||
@ -596,15 +603,15 @@ int sqliteDbbeNextKey(DbbeTable *pTable){
|
||||
/*
|
||||
** Get a new integer key.
|
||||
*/
|
||||
int sqliteDbbeNew(DbbeTable *pTable){
|
||||
int sqliteDbbeNew(DbbeCursor *pCursr){
|
||||
int iKey;
|
||||
datum key;
|
||||
int go = 1;
|
||||
int i;
|
||||
struct rc4 *pRc4;
|
||||
|
||||
if( pTable->pFile==0 || pTable->pFile->dbf==0 ) return 1;
|
||||
pRc4 = &pTable->pBe->rc4;
|
||||
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return 1;
|
||||
pRc4 = &pCursr->pBe->rc4;
|
||||
while( go ){
|
||||
iKey = 0;
|
||||
for(i=0; i<4; i++){
|
||||
@ -612,7 +619,7 @@ int sqliteDbbeNew(DbbeTable *pTable){
|
||||
}
|
||||
key.dptr = (char*)&iKey;
|
||||
key.dsize = 4;
|
||||
go = gdbm_exists(pTable->pFile->dbf, key);
|
||||
go = gdbm_exists(pCursr->pFile->dbf, key);
|
||||
}
|
||||
return iKey;
|
||||
}
|
||||
@ -621,33 +628,33 @@ int sqliteDbbeNew(DbbeTable *pTable){
|
||||
** Write an entry into the table. Overwrite any prior entry with the
|
||||
** same key.
|
||||
*/
|
||||
int sqliteDbbePut(DbbeTable *pTable, int nKey,char *pKey,int nData,char *pData){
|
||||
int sqliteDbbePut(DbbeCursor *pCursr, int nKey,char *pKey,int nData,char *pData){
|
||||
datum data, key;
|
||||
int rc;
|
||||
if( pTable->pFile==0 || pTable->pFile->dbf==0 ) return SQLITE_ERROR;
|
||||
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(pTable->pFile->dbf, key, data, GDBM_REPLACE);
|
||||
rc = gdbm_store(pCursr->pFile->dbf, key, data, GDBM_REPLACE);
|
||||
if( rc ) rc = SQLITE_ERROR;
|
||||
datumClear(&pTable->key);
|
||||
datumClear(&pTable->data);
|
||||
datumClear(&pCursr->key);
|
||||
datumClear(&pCursr->data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Remove an entry from a table, if the entry exists.
|
||||
*/
|
||||
int sqliteDbbeDelete(DbbeTable *pTable, int nKey, char *pKey){
|
||||
int sqliteDbbeDelete(DbbeCursor *pCursr, int nKey, char *pKey){
|
||||
datum key;
|
||||
int rc;
|
||||
datumClear(&pTable->key);
|
||||
datumClear(&pTable->data);
|
||||
if( pTable->pFile==0 || pTable->pFile->dbf==0 ) return SQLITE_ERROR;
|
||||
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(pTable->pFile->dbf, key);
|
||||
rc = gdbm_delete(pCursr->pFile->dbf, key);
|
||||
if( rc ) rc = SQLITE_ERROR;
|
||||
return rc;
|
||||
}
|
||||
|
51
src/dbbe.h
51
src/dbbe.h
@ -28,7 +28,7 @@
|
||||
** This library was originally designed to support the following
|
||||
** backends: GDBM, NDBM, SDBM, Berkeley DB.
|
||||
**
|
||||
** $Id: dbbe.h,v 1.5 2000/06/17 13:12:39 drh Exp $
|
||||
** $Id: dbbe.h,v 1.6 2000/06/21 13:59:11 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITE_DBBE_H_
|
||||
#define _SQLITE_DBBE_H_
|
||||
@ -37,7 +37,7 @@
|
||||
/*
|
||||
** The database backend supports two opaque structures. A Dbbe is
|
||||
** a context for the entire set of tables forming a complete
|
||||
** database. A DbbeTable is a single table.
|
||||
** 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
|
||||
@ -46,13 +46,13 @@
|
||||
** segregation of data into various fields or columns is handled
|
||||
** by software at higher layers.
|
||||
**
|
||||
** The DbbeTable structure holds some state information, such as
|
||||
** 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
|
||||
** DbbeTable structures for each table in the database.
|
||||
** DbbeCursor structures for each table in the database.
|
||||
*/
|
||||
typedef struct Dbbe Dbbe;
|
||||
typedef struct DbbeTable DbbeTable;
|
||||
typedef struct DbbeCursor DbbeCursor;
|
||||
|
||||
/*
|
||||
** The 18 interface routines.
|
||||
@ -64,13 +64,16 @@ Dbbe *sqliteDbbeOpen(const char *zName, int write, int create, char **pzErr);
|
||||
/* Close the whole database. */
|
||||
void sqliteDbbeClose(Dbbe*);
|
||||
|
||||
/* Open a particular table of a previously opened database.
|
||||
** Create the table if it doesn't already exist and writeable!=0.
|
||||
/* Open a cursor into particular file of a previously opened database.
|
||||
** Create the file if it doesn't already exist and writeable!=0. zName
|
||||
** is the base name of the file to be opened. This routine will add
|
||||
** an appropriate path and extension to the filename to locate the
|
||||
** actual file.
|
||||
**
|
||||
** If zTableName is 0 or "", then a temporary table is created that
|
||||
** If zName is 0 or "", then a temporary file is created that
|
||||
** will be deleted when closed.
|
||||
*/
|
||||
int sqliteDbbeOpenTable(Dbbe*, const char *zName, int writeable, DbbeTable **);
|
||||
int sqliteDbbeOpenCursor(Dbbe*, const char *zName, int writeable, DbbeCursor**);
|
||||
|
||||
/* Delete a table from the database */
|
||||
void sqliteDbbeDropTable(Dbbe*, const char *zTableName);
|
||||
@ -78,58 +81,58 @@ void sqliteDbbeDropTable(Dbbe*, const char *zTableName);
|
||||
/* Reorganize a table to speed access or reduce its disk usage */
|
||||
void sqliteDbbeReorganizeTable(Dbbe*, const char *zTableName);
|
||||
|
||||
/* Close a table */
|
||||
void sqliteDbbeCloseTable(DbbeTable*);
|
||||
/* Close a cursor */
|
||||
void sqliteDbbeCloseCursor(DbbeCursor*);
|
||||
|
||||
/* Fetch an entry from a table with the given key. Return 1 if
|
||||
** successful and 0 if no such entry exists.
|
||||
*/
|
||||
int sqliteDbbeFetch(DbbeTable*, int nKey, char *pKey);
|
||||
int sqliteDbbeFetch(DbbeCursor*, int nKey, char *pKey);
|
||||
|
||||
/* Return 1 if the given key is already in the table. Return 0
|
||||
** if it is not.
|
||||
*/
|
||||
int sqliteDbbeTest(DbbeTable*, int nKey, char *pKey);
|
||||
int sqliteDbbeTest(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 sqliteDbbeCopyKey(DbbeTable*, int offset, int size, char *zBuf);
|
||||
int sqliteDbbeCopyData(DbbeTable*, int offset, int size, char *zBuf);
|
||||
int sqliteDbbeCopyKey(DbbeCursor*, int offset, int size, char *zBuf);
|
||||
int sqliteDbbeCopyData(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 *sqliteDbbeReadKey(DbbeTable*, int offset);
|
||||
char *sqliteDbbeReadData(DbbeTable*, int offset);
|
||||
char *sqliteDbbeReadKey(DbbeCursor*, int offset);
|
||||
char *sqliteDbbeReadData(DbbeCursor*, int offset);
|
||||
|
||||
/* Return the length of the most recently fetched key or data. */
|
||||
int sqliteDbbeKeyLength(DbbeTable*);
|
||||
int sqliteDbbeDataLength(DbbeTable*);
|
||||
int sqliteDbbeKeyLength(DbbeCursor*);
|
||||
int sqliteDbbeDataLength(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
|
||||
** sqliteDbbeRewind(). The return value is 1 if there is another
|
||||
** entry, or 0 if there are no more entries. */
|
||||
int sqliteDbbeNextKey(DbbeTable*);
|
||||
int sqliteDbbeNextKey(DbbeCursor*);
|
||||
|
||||
/* Make it so that the next call to sqliteDbbeNextKey() returns
|
||||
** the first entry of the table. */
|
||||
int sqliteDbbeRewind(DbbeTable*);
|
||||
int sqliteDbbeRewind(DbbeCursor*);
|
||||
|
||||
/* Get a new integer key for this table. */
|
||||
int sqliteDbbeNew(DbbeTable*);
|
||||
int sqliteDbbeNew(DbbeCursor*);
|
||||
|
||||
/* Write an entry into a table. If another entry already exists with
|
||||
** the same key, the old entry is discarded first.
|
||||
*/
|
||||
int sqliteDbbePut(DbbeTable*, int nKey, char *pKey, int nData, char *pData);
|
||||
int sqliteDbbePut(DbbeCursor*, int nKey, char *pKey, int nData, char *pData);
|
||||
|
||||
/* Remove an entry from the table */
|
||||
int sqliteDbbeDelete(DbbeTable*, int nKey, char *pKey);
|
||||
int sqliteDbbeDelete(DbbeCursor*, int nKey, char *pKey);
|
||||
|
||||
/* Open a file suitable for temporary storage */
|
||||
int sqliteDbbeOpenTempFile(Dbbe*, FILE**);
|
||||
|
10
src/delete.c
10
src/delete.c
@ -24,7 +24,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle DELETE FROM statements.
|
||||
**
|
||||
** $Id: delete.c,v 1.5 2000/06/17 13:12:39 drh Exp $
|
||||
** $Id: delete.c,v 1.6 2000/06/21 13:59:11 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -68,7 +68,7 @@ void sqliteDeleteFrom(
|
||||
}
|
||||
pTab = pTabList->a[0].pTab;
|
||||
|
||||
/* Resolve the field names in all the expressions.
|
||||
/* Resolve the column names in all the expressions.
|
||||
*/
|
||||
if( pWhere ){
|
||||
sqliteExprResolveInSelect(pParse, pWhere);
|
||||
@ -117,10 +117,10 @@ void sqliteDeleteFrom(
|
||||
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->nField; j++){
|
||||
sqliteVdbeAddOp(v, OP_Field, base, pIdx->aiField[j], 0, 0);
|
||||
for(j=0; j<pIdx->nColumn; j++){
|
||||
sqliteVdbeAddOp(v, OP_Field, base, pIdx->aiColumn[j], 0, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nColumn, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_DeleteIdx, base+i, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
62
src/expr.c
62
src/expr.c
@ -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.17 2000/06/17 13:12:40 drh Exp $
|
||||
** $Id: expr.c,v 1.18 2000/06/21 13:59:11 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
static int isConstant(Expr *p){
|
||||
switch( p->op ){
|
||||
case TK_ID:
|
||||
case TK_FIELD:
|
||||
case TK_COLUMN:
|
||||
case TK_DOT:
|
||||
return 0;
|
||||
default: {
|
||||
@ -58,10 +58,10 @@ static int isConstant(Expr *p){
|
||||
**
|
||||
** expr IN (SELECT ...)
|
||||
**
|
||||
** These operators have to be processed before field names are
|
||||
** These operators have to be processed before column names are
|
||||
** resolved because each such operator increments pParse->nTab
|
||||
** to reserve cursor numbers for its own use. But pParse->nTab
|
||||
** needs to be constant once we begin resolving field names.
|
||||
** needs to be constant once we begin resolving column names.
|
||||
**
|
||||
** Actually, the processing of IN-SELECT is only started by this
|
||||
** routine. This routine allocates a cursor number to the IN-SELECT
|
||||
@ -87,11 +87,11 @@ void sqliteExprResolveInSelect(Parse *pParse, Expr *pExpr){
|
||||
|
||||
/*
|
||||
** This routine walks an expression tree and resolves references to
|
||||
** table fields. Nodes of the form ID.ID or ID resolve into an
|
||||
** index to the table in the table list and a field offset. The opcode
|
||||
** for such nodes is changed to TK_FIELD. The iTable value is changed
|
||||
** table columns. Nodes of the form ID.ID or ID resolve into an
|
||||
** index to the table in the table list and a column offset. The opcode
|
||||
** for such nodes is changed to TK_COLUMN. The iTable value is changed
|
||||
** to the index of the referenced table in pTabList plus the pParse->nTab
|
||||
** value. The iField value is changed to the index of the field of the
|
||||
** value. The iColumn value is changed to the index of the column of the
|
||||
** referenced table.
|
||||
**
|
||||
** We also check for instances of the IN operator. IN comes in two
|
||||
@ -109,7 +109,7 @@ void sqliteExprResolveInSelect(Parse *pParse, Expr *pExpr){
|
||||
** If it finds any, it generates code to write the value of that select
|
||||
** into a memory cell.
|
||||
**
|
||||
** Unknown fields or tables provoke an error. The function returns
|
||||
** Unknown columns or tables provoke an error. The function returns
|
||||
** the number of errors seen and leaves an error message on pParse->zErrMsg.
|
||||
*/
|
||||
int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
|
||||
@ -128,27 +128,27 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
|
||||
if( sqliteStrICmp(pTab->aCol[j].zName, z)==0 ){
|
||||
cnt++;
|
||||
pExpr->iTable = i + pParse->nTab;
|
||||
pExpr->iField = j;
|
||||
pExpr->iColumn = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqliteFree(z);
|
||||
if( cnt==0 ){
|
||||
sqliteSetNString(&pParse->zErrMsg, "no such field: ", -1,
|
||||
sqliteSetNString(&pParse->zErrMsg, "no such column: ", -1,
|
||||
pExpr->token.z, pExpr->token.n, 0);
|
||||
pParse->nErr++;
|
||||
return 1;
|
||||
}else if( cnt>1 ){
|
||||
sqliteSetNString(&pParse->zErrMsg, "ambiguous field name: ", -1,
|
||||
sqliteSetNString(&pParse->zErrMsg, "ambiguous column name: ", -1,
|
||||
pExpr->token.z, pExpr->token.n, 0);
|
||||
pParse->nErr++;
|
||||
return 1;
|
||||
}
|
||||
pExpr->op = TK_FIELD;
|
||||
pExpr->op = TK_COLUMN;
|
||||
break;
|
||||
}
|
||||
|
||||
/* A table name and field name: ID.ID */
|
||||
/* A table name and column name: ID.ID */
|
||||
case TK_DOT: {
|
||||
int cnt = 0; /* Number of matches */
|
||||
int i; /* Loop counter */
|
||||
@ -176,20 +176,20 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
|
||||
if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){
|
||||
cnt++;
|
||||
pExpr->iTable = i + pParse->nTab;
|
||||
pExpr->iField = j;
|
||||
pExpr->iColumn = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqliteFree(zLeft);
|
||||
sqliteFree(zRight);
|
||||
if( cnt==0 ){
|
||||
sqliteSetNString(&pParse->zErrMsg, "no such field: ", -1,
|
||||
sqliteSetNString(&pParse->zErrMsg, "no such column: ", -1,
|
||||
pLeft->token.z, pLeft->token.n, ".", 1,
|
||||
pRight->token.z, pRight->token.n, 0);
|
||||
pParse->nErr++;
|
||||
return 1;
|
||||
}else if( cnt>1 ){
|
||||
sqliteSetNString(&pParse->zErrMsg, "ambiguous field name: ", -1,
|
||||
sqliteSetNString(&pParse->zErrMsg, "ambiguous column name: ", -1,
|
||||
pLeft->token.z, pLeft->token.n, ".", 1,
|
||||
pRight->token.z, pRight->token.n, 0);
|
||||
pParse->nErr++;
|
||||
@ -199,7 +199,7 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
|
||||
pExpr->pLeft = 0;
|
||||
sqliteExprDelete(pRight);
|
||||
pExpr->pRight = 0;
|
||||
pExpr->op = TK_FIELD;
|
||||
pExpr->op = TK_COLUMN;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -263,10 +263,10 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
|
||||
case TK_SELECT: {
|
||||
/* This has to be a scalar SELECT. Generate code to put the
|
||||
** value of this select in a memory cell and record the number
|
||||
** of the memory cell in iField.
|
||||
** of the memory cell in iColumn.
|
||||
*/
|
||||
pExpr->iField = pParse->nMem++;
|
||||
if( sqliteSelect(pParse, pExpr->pSelect, SRT_Mem, pExpr->iField) ){
|
||||
pExpr->iColumn = pParse->nMem++;
|
||||
if( sqliteSelect(pParse, pExpr->pSelect, SRT_Mem, pExpr->iColumn) ){
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
@ -355,7 +355,7 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
|
||||
int too_few_args = 0;
|
||||
int is_agg = 0;
|
||||
int i;
|
||||
pExpr->iField = id;
|
||||
pExpr->iColumn = id;
|
||||
switch( id ){
|
||||
case FN_Unknown: {
|
||||
no_such_func = 1;
|
||||
@ -467,11 +467,11 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
||||
default: break;
|
||||
}
|
||||
switch( pExpr->op ){
|
||||
case TK_FIELD: {
|
||||
case TK_COLUMN: {
|
||||
if( pParse->useAgg ){
|
||||
sqliteVdbeAddOp(v, OP_AggGet, 0, pExpr->iAgg, 0, 0);
|
||||
}else{
|
||||
sqliteVdbeAddOp(v, OP_Field, pExpr->iTable, pExpr->iField, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Field, pExpr->iTable, pExpr->iColumn, 0, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -562,7 +562,7 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
||||
}
|
||||
case TK_AGG_FUNCTION: {
|
||||
sqliteVdbeAddOp(v, OP_AggGet, 0, pExpr->iAgg, 0, 0);
|
||||
if( pExpr->iField==FN_Avg ){
|
||||
if( pExpr->iColumn==FN_Avg ){
|
||||
assert( pParse->iAggCount>=0 && pParse->iAggCount<pParse->nAgg );
|
||||
sqliteVdbeAddOp(v, OP_AggGet, 0, pParse->iAggCount, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Divide, 0, 0, 0, 0);
|
||||
@ -570,7 +570,7 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
||||
break;
|
||||
}
|
||||
case TK_FUNCTION: {
|
||||
int id = pExpr->iField;
|
||||
int id = pExpr->iColumn;
|
||||
int op;
|
||||
int i;
|
||||
ExprList *pList = pExpr->pList;
|
||||
@ -588,7 +588,7 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
||||
break;
|
||||
}
|
||||
case TK_SELECT: {
|
||||
sqliteVdbeAddOp(v, OP_MemLoad, pExpr->iField, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MemLoad, pExpr->iColumn, 0, 0, 0);
|
||||
break;
|
||||
}
|
||||
case TK_IN: {
|
||||
@ -864,12 +864,12 @@ int sqliteExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){
|
||||
|
||||
if( pExpr==0 ) return 0;
|
||||
switch( pExpr->op ){
|
||||
case TK_FIELD: {
|
||||
case TK_COLUMN: {
|
||||
aAgg = pParse->aAgg;
|
||||
for(i=0; i<pParse->nAgg; i++){
|
||||
if( aAgg[i].isAgg ) continue;
|
||||
if( aAgg[i].pExpr->iTable==pExpr->iTable
|
||||
&& aAgg[i].pExpr->iField==pExpr->iField ){
|
||||
&& aAgg[i].pExpr->iColumn==pExpr->iColumn ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -883,7 +883,7 @@ int sqliteExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){
|
||||
break;
|
||||
}
|
||||
case TK_AGG_FUNCTION: {
|
||||
if( pExpr->iField==FN_Count || pExpr->iField==FN_Avg ){
|
||||
if( pExpr->iColumn==FN_Count || pExpr->iColumn==FN_Avg ){
|
||||
if( pParse->iAggCount>=0 ){
|
||||
i = pParse->iAggCount;
|
||||
}else{
|
||||
@ -893,7 +893,7 @@ int sqliteExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){
|
||||
pParse->aAgg[i].pExpr = 0;
|
||||
pParse->iAggCount = i;
|
||||
}
|
||||
if( pExpr->iField==FN_Count ){
|
||||
if( pExpr->iColumn==FN_Count ){
|
||||
pExpr->iAgg = i;
|
||||
break;
|
||||
}
|
||||
|
60
src/insert.c
60
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.10 2000/06/17 13:12:40 drh Exp $
|
||||
** $Id: insert.c,v 1.11 2000/06/21 13:59:12 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
**
|
||||
** The IDLIST following the table name is always optional. If omitted,
|
||||
** then a list of all columns for the table is substituted. The IDLIST
|
||||
** appears in the pField parameter. pField is NULL if IDLIST is omitted.
|
||||
** appears in the pColumn parameter. pColumn is NULL if IDLIST is omitted.
|
||||
**
|
||||
** The pList parameter holds EXPRLIST in the first form of the INSERT
|
||||
** statement above, and pSelect is NULL. For the second form, pList is
|
||||
@ -48,7 +48,7 @@ void sqliteInsert(
|
||||
Token *pTableName, /* Name of table into which we are inserting */
|
||||
ExprList *pList, /* List of values to be inserted */
|
||||
Select *pSelect, /* A SELECT statement to use as the data source */
|
||||
IdList *pField /* Field names corresponding to IDLIST. */
|
||||
IdList *pColumn /* Column names corresponding to IDLIST. */
|
||||
){
|
||||
Table *pTab; /* The table to insert into */
|
||||
char *zTab; /* Name of the table into which we are inserting */
|
||||
@ -56,7 +56,7 @@ void sqliteInsert(
|
||||
Vdbe *v; /* Generate code into this virtual machine */
|
||||
Index *pIdx; /* For looping over indices of the table */
|
||||
int srcTab; /* Date comes from this temporary cursor if >=0 */
|
||||
int nField; /* Number of columns in the data */
|
||||
int nColumn; /* Number of columns in the data */
|
||||
int base; /* First available cursor */
|
||||
int iCont, iBreak; /* Beginning and end of the loop over srcTab */
|
||||
|
||||
@ -96,20 +96,20 @@ void sqliteInsert(
|
||||
rc = sqliteSelect(pParse, pSelect, SRT_Table, srcTab);
|
||||
if( rc ) goto insert_cleanup;
|
||||
assert( pSelect->pEList );
|
||||
nField = pSelect->pEList->nExpr;
|
||||
nColumn = pSelect->pEList->nExpr;
|
||||
}else{
|
||||
srcTab = -1;
|
||||
assert( pList );
|
||||
nField = pList->nExpr;
|
||||
nColumn = pList->nExpr;
|
||||
}
|
||||
|
||||
/* Make sure the number of columns in the source data matches the number
|
||||
** of columns to be inserted into the table.
|
||||
*/
|
||||
if( pField==0 && nField!=pTab->nCol ){
|
||||
if( pColumn==0 && nColumn!=pTab->nCol ){
|
||||
char zNum1[30];
|
||||
char zNum2[30];
|
||||
sprintf(zNum1,"%d", nField);
|
||||
sprintf(zNum1,"%d", nColumn);
|
||||
sprintf(zNum2,"%d", pTab->nCol);
|
||||
sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
|
||||
" has ", zNum2, " columns but ",
|
||||
@ -117,11 +117,11 @@ void sqliteInsert(
|
||||
pParse->nErr++;
|
||||
goto insert_cleanup;
|
||||
}
|
||||
if( pField!=0 && nField!=pField->nId ){
|
||||
if( pColumn!=0 && nColumn!=pColumn->nId ){
|
||||
char zNum1[30];
|
||||
char zNum2[30];
|
||||
sprintf(zNum1,"%d", nField);
|
||||
sprintf(zNum2,"%d", pField->nId);
|
||||
sprintf(zNum1,"%d", nColumn);
|
||||
sprintf(zNum2,"%d", pColumn->nId);
|
||||
sqliteSetString(&pParse->zErrMsg, zNum1, " values for ",
|
||||
zNum2, " columns", 0);
|
||||
pParse->nErr++;
|
||||
@ -132,20 +132,20 @@ void sqliteInsert(
|
||||
** all elements of the IDLIST really are columns of the table and
|
||||
** remember the column indices.
|
||||
*/
|
||||
if( pField ){
|
||||
for(i=0; i<pField->nId; i++){
|
||||
pField->a[i].idx = -1;
|
||||
if( pColumn ){
|
||||
for(i=0; i<pColumn->nId; i++){
|
||||
pColumn->a[i].idx = -1;
|
||||
}
|
||||
for(i=0; i<pField->nId; i++){
|
||||
for(i=0; i<pColumn->nId; i++){
|
||||
for(j=0; j<pTab->nCol; j++){
|
||||
if( sqliteStrICmp(pField->a[i].zName, pTab->aCol[j].zName)==0 ){
|
||||
pField->a[i].idx = j;
|
||||
if( sqliteStrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){
|
||||
pColumn->a[i].idx = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( j>=pTab->nCol ){
|
||||
sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
|
||||
" has no column named ", pField->a[i].zName, 0);
|
||||
" has no column named ", pColumn->a[i].zName, 0);
|
||||
pParse->nErr++;
|
||||
goto insert_cleanup;
|
||||
}
|
||||
@ -179,14 +179,14 @@ void sqliteInsert(
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
|
||||
}
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( pField==0 ){
|
||||
if( pColumn==0 ){
|
||||
j = i;
|
||||
}else{
|
||||
for(j=0; j<pField->nId; j++){
|
||||
if( pField->a[j].idx==i ) break;
|
||||
for(j=0; j<pColumn->nId; j++){
|
||||
if( pColumn->a[j].idx==i ) break;
|
||||
}
|
||||
}
|
||||
if( pField && j>=pField->nId ){
|
||||
if( pColumn && j>=pColumn->nId ){
|
||||
char *zDflt = pTab->aCol[i].zDflt;
|
||||
if( zDflt==0 ){
|
||||
sqliteVdbeAddOp(v, OP_Null, 0, 0, 0, 0);
|
||||
@ -209,16 +209,16 @@ void sqliteInsert(
|
||||
if( pIdx->pNext ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
|
||||
}
|
||||
for(i=0; i<pIdx->nField; i++){
|
||||
int idx = pIdx->aiField[i];
|
||||
if( pField==0 ){
|
||||
for(i=0; i<pIdx->nColumn; i++){
|
||||
int idx = pIdx->aiColumn[i];
|
||||
if( pColumn==0 ){
|
||||
j = idx;
|
||||
}else{
|
||||
for(j=0; j<pField->nId; j++){
|
||||
if( pField->a[j].idx==idx ) break;
|
||||
for(j=0; j<pColumn->nId; j++){
|
||||
if( pColumn->a[j].idx==idx ) break;
|
||||
}
|
||||
}
|
||||
if( pField && j>=pField->nId ){
|
||||
if( pColumn && j>=pColumn->nId ){
|
||||
char *zDflt = pTab->aCol[idx].zDflt;
|
||||
if( zDflt==0 ){
|
||||
sqliteVdbeAddOp(v, OP_Null, 0, 0, 0, 0);
|
||||
@ -231,7 +231,7 @@ void sqliteInsert(
|
||||
sqliteExprCode(pParse, pList->a[j].pExpr);
|
||||
}
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nColumn, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_PutIdx, idx+base, 0, 0, 0);
|
||||
}
|
||||
|
||||
@ -245,5 +245,5 @@ void sqliteInsert(
|
||||
insert_cleanup:
|
||||
if( pList ) sqliteExprListDelete(pList);
|
||||
if( pSelect ) sqliteSelectDelete(pSelect);
|
||||
sqliteIdListDelete(pField);
|
||||
sqliteIdListDelete(pColumn);
|
||||
}
|
||||
|
12
src/main.c
12
src/main.c
@ -26,7 +26,7 @@
|
||||
** other files are for internal use by SQLite and should not be
|
||||
** accessed by users of the library.
|
||||
**
|
||||
** $Id: main.c,v 1.12 2000/06/07 23:51:50 drh Exp $
|
||||
** $Id: main.c,v 1.13 2000/06/21 13:59:12 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -90,12 +90,12 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){
|
||||
** );
|
||||
**
|
||||
** The sqlite_master table contains a single entry for each table
|
||||
** and each index. The "type" field tells whether the entry is
|
||||
** a table or index. The "name" field is the name of the object.
|
||||
** and each index. The "type" column tells whether the entry is
|
||||
** a table or index. The "name" column is the name of the object.
|
||||
** The "tbl_name" is the name of the associated table. For tables,
|
||||
** the tbl_name field is always the same as name. For indices, the
|
||||
** tbl_name field contains the name of the table that the index
|
||||
** indexes. Finally, the sql field contains the complete text of
|
||||
** the tbl_name column is always the same as name. For indices, the
|
||||
** tbl_name column contains the name of the table that the index
|
||||
** indexes. Finally, the "sql" column contains the complete text of
|
||||
** the CREATE TABLE or CREATE INDEX statement that originally created
|
||||
** the table or index.
|
||||
**
|
||||
|
24
src/parse.y
24
src/parse.y
@ -26,7 +26,7 @@
|
||||
** the parser. Lemon will also generate a header file containing
|
||||
** numeric codes for all of the tokens.
|
||||
**
|
||||
** @(#) $Id: parse.y,v 1.22 2000/06/19 19:09:09 drh Exp $
|
||||
** @(#) $Id: parse.y,v 1.23 2000/06/21 13:59:12 drh Exp $
|
||||
*/
|
||||
%token_prefix TK_
|
||||
%token_type {Token}
|
||||
@ -50,7 +50,7 @@ input ::= cmdlist.
|
||||
// add them to the parse.h output file.
|
||||
//
|
||||
input ::= END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION
|
||||
UMINUS FIELD AGG_FUNCTION.
|
||||
UMINUS COLUMN AGG_FUNCTION.
|
||||
|
||||
// A list of commands is zero or more commands
|
||||
//
|
||||
@ -250,9 +250,9 @@ setlist(A) ::= id(X) EQ expr(Y) COMMA setlist(Z).
|
||||
{A = sqliteExprListAppend(Z,Y,&X);}
|
||||
setlist(A) ::= id(X) EQ expr(Y). {A = sqliteExprListAppend(0,Y,&X);}
|
||||
|
||||
cmd ::= INSERT INTO id(X) fieldlist_opt(F) VALUES LP itemlist(Y) RP.
|
||||
cmd ::= INSERT INTO id(X) inscollist_opt(F) VALUES LP itemlist(Y) RP.
|
||||
{sqliteInsert(pParse, &X, Y, 0, F);}
|
||||
cmd ::= INSERT INTO id(X) fieldlist_opt(F) select(S).
|
||||
cmd ::= INSERT INTO id(X) inscollist_opt(F) select(S).
|
||||
{sqliteInsert(pParse, &X, 0, S, F);}
|
||||
|
||||
|
||||
@ -278,15 +278,15 @@ item(A) ::= MINUS FLOAT(X). {
|
||||
item(A) ::= STRING(X). {A = sqliteExpr(TK_STRING, 0, 0, &X);}
|
||||
item(A) ::= NULL. {A = sqliteExpr(TK_NULL, 0, 0, 0);}
|
||||
|
||||
%type fieldlist_opt {IdList*}
|
||||
%destructor fieldlist_opt {sqliteIdListDelete($$);}
|
||||
%type fieldlist {IdList*}
|
||||
%destructor fieldlist {sqliteIdListDelete($$);}
|
||||
%type inscollist_opt {IdList*}
|
||||
%destructor inscollist_opt {sqliteIdListDelete($$);}
|
||||
%type inscollist {IdList*}
|
||||
%destructor inscollist {sqliteIdListDelete($$);}
|
||||
|
||||
fieldlist_opt(A) ::= . {A = 0;}
|
||||
fieldlist_opt(A) ::= LP fieldlist(X) RP. {A = X;}
|
||||
fieldlist(A) ::= fieldlist(X) COMMA id(Y). {A = sqliteIdListAppend(X,&Y);}
|
||||
fieldlist(A) ::= id(Y). {A = sqliteIdListAppend(0,&Y);}
|
||||
inscollist_opt(A) ::= . {A = 0;}
|
||||
inscollist_opt(A) ::= LP inscollist(X) RP. {A = X;}
|
||||
inscollist(A) ::= inscollist(X) COMMA id(Y). {A = sqliteIdListAppend(X,&Y);}
|
||||
inscollist(A) ::= id(Y). {A = sqliteIdListAppend(0,&Y);}
|
||||
|
||||
%left OR.
|
||||
%left AND.
|
||||
|
54
src/select.c
54
src/select.c
@ -24,7 +24,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle SELECT statements.
|
||||
**
|
||||
** $Id: select.c,v 1.24 2000/06/19 19:09:09 drh Exp $
|
||||
** $Id: select.c,v 1.25 2000/06/21 13:59:12 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -86,14 +86,14 @@ void sqliteParseInfoReset(Parse *pParse){
|
||||
** of a SELECT.
|
||||
**
|
||||
** The pEList is used to determine the values for each column in the
|
||||
** result row. Except if pEList==NULL, then we just read nField
|
||||
** result row. Except if pEList==NULL, then we just read nColumn
|
||||
** elements from the srcTab table.
|
||||
*/
|
||||
static int selectInnerLoop(
|
||||
Parse *pParse, /* The parser context */
|
||||
ExprList *pEList, /* List of values being extracted */
|
||||
int srcTab, /* Pull data from this table */
|
||||
int nField, /* Number of fields in the source table */
|
||||
int nColumn, /* Number of columns in the source table */
|
||||
ExprList *pOrderBy, /* If not NULL, sort results using this key */
|
||||
int distinct, /* If >=0, make sure results are distinct */
|
||||
int eDest, /* How to dispose of the results */
|
||||
@ -104,15 +104,15 @@ static int selectInnerLoop(
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int i;
|
||||
|
||||
/* Pull the requested fields.
|
||||
/* Pull the requested columns.
|
||||
*/
|
||||
if( pEList ){
|
||||
for(i=0; i<pEList->nExpr; i++){
|
||||
sqliteExprCode(pParse, pEList->a[i].pExpr);
|
||||
}
|
||||
nField = pEList->nExpr;
|
||||
nColumn = pEList->nExpr;
|
||||
}else{
|
||||
for(i=0; i<nField; i++){
|
||||
for(i=0; i<nColumn; i++){
|
||||
sqliteVdbeAddOp(v, OP_Field, srcTab, i, 0, 0);
|
||||
}
|
||||
}
|
||||
@ -135,7 +135,7 @@ static int selectInnerLoop(
|
||||
*/
|
||||
if( pOrderBy ){
|
||||
char *zSortOrder;
|
||||
sqliteVdbeAddOp(v, OP_SortMakeRec, nField, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_SortMakeRec, nColumn, 0, 0, 0);
|
||||
zSortOrder = sqliteMalloc( pOrderBy->nExpr + 1 );
|
||||
if( zSortOrder==0 ) return 1;
|
||||
for(i=0; i<pOrderBy->nExpr; i++){
|
||||
@ -152,7 +152,7 @@ static int selectInnerLoop(
|
||||
** table iParm.
|
||||
*/
|
||||
if( eDest==SRT_Union ){
|
||||
sqliteVdbeAddOp(v, OP_MakeRecord, nField, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, iParm, 0, "", 0);
|
||||
sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
|
||||
}else
|
||||
@ -160,7 +160,7 @@ static int selectInnerLoop(
|
||||
/* Store the result as data using a unique key.
|
||||
*/
|
||||
if( eDest==SRT_Table ){
|
||||
sqliteVdbeAddOp(v, OP_MakeRecord, nField, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_New, iParm, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Pull, 1, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
|
||||
@ -171,7 +171,7 @@ static int selectInnerLoop(
|
||||
** the temporary table iParm.
|
||||
*/
|
||||
if( eDest==SRT_Except ){
|
||||
sqliteVdbeAddOp(v, OP_MakeRecord, nField, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Delete, iParm, 0, 0, 0);
|
||||
}else
|
||||
|
||||
@ -180,7 +180,7 @@ static int selectInnerLoop(
|
||||
** item into the set table with bogus data.
|
||||
*/
|
||||
if( eDest==SRT_Set ){
|
||||
assert( nField==1 );
|
||||
assert( nColumn==1 );
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
|
||||
sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
|
||||
}else
|
||||
@ -191,7 +191,7 @@ static int selectInnerLoop(
|
||||
** of the scan loop.
|
||||
*/
|
||||
if( eDest==SRT_Mem ){
|
||||
assert( nField==1 );
|
||||
assert( nColumn==1 );
|
||||
sqliteVdbeAddOp(v, OP_MemStore, iParm, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, iBreak, 0, 0);
|
||||
}else
|
||||
@ -199,7 +199,7 @@ static int selectInnerLoop(
|
||||
/* If none of the above, send the data to the callback function.
|
||||
*/
|
||||
{
|
||||
sqliteVdbeAddOp(v, OP_Callback, nField, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Callback, nColumn, 0, 0, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -210,12 +210,12 @@ static int selectInnerLoop(
|
||||
** we need to run the sorter and output the results. The following
|
||||
** routine generates the code needed to do that.
|
||||
*/
|
||||
static void generateSortTail(Vdbe *v, int nField){
|
||||
static void generateSortTail(Vdbe *v, int nColumn){
|
||||
int end = sqliteVdbeMakeLabel(v);
|
||||
int addr;
|
||||
sqliteVdbeAddOp(v, OP_Sort, 0, 0, 0, 0);
|
||||
addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_SortCallback, nField, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_SortCallback, nColumn, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_SortClose, 0, 0, 0, end);
|
||||
}
|
||||
@ -240,7 +240,7 @@ void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){
|
||||
continue;
|
||||
}
|
||||
p = pEList->a[i].pExpr;
|
||||
if( p->op!=TK_FIELD || pTabList==0 ){
|
||||
if( p->op!=TK_COLUMN || pTabList==0 ){
|
||||
char zName[30];
|
||||
sprintf(zName, "column%d", i+1);
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
|
||||
@ -252,12 +252,12 @@ void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){
|
||||
|
||||
zTab = pTabList->a[p->iTable].zAlias;
|
||||
if( zTab==0 ) zTab = pTab->zName;
|
||||
sqliteSetString(&zName, zTab, ".", pTab->aCol[p->iField].zName, 0);
|
||||
sqliteSetString(&zName, zTab, ".", pTab->aCol[p->iColumn].zName, 0);
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
|
||||
sqliteFree(zName);
|
||||
}else{
|
||||
Table *pTab = pTabList->a[0].pTab;
|
||||
char *zName = pTab->aCol[p->iField].zName;
|
||||
char *zName = pTab->aCol[p->iColumn].zName;
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
|
||||
}
|
||||
}
|
||||
@ -281,8 +281,8 @@ static const char *selectOpName(int id){
|
||||
/*
|
||||
** For the given SELECT statement, do two things.
|
||||
**
|
||||
** (1) Fill in the pTab fields of the IdList that defines the set
|
||||
** of tables we are scanning.
|
||||
** (1) Fill in the pTabList->a[].pTab fields in the IdList that
|
||||
** defines the set of tables that should be scanned.
|
||||
**
|
||||
** (2) If the columns to be extracted variable (pEList) is NULL
|
||||
** (meaning that a "*" was used in the SQL statement) then
|
||||
@ -338,7 +338,7 @@ static int fillInColumnList(Parse *pParse, Select *p){
|
||||
/*
|
||||
** This routine associates entries in an ORDER BY expression list with
|
||||
** columns in a result. For each ORDER BY expression, the opcode of
|
||||
** the top-level node is changed to TK_FIELD and the iField value of
|
||||
** the top-level node is changed to TK_COLUMN and the iColumn value of
|
||||
** the top-level node is filled in with column number and the iTable
|
||||
** value of the top-level node is filled with iTable parameter.
|
||||
**
|
||||
@ -390,8 +390,8 @@ static int matchOrderbyToColumn(
|
||||
match = 1;
|
||||
}
|
||||
if( match ){
|
||||
pE->op = TK_FIELD;
|
||||
pE->iField = j;
|
||||
pE->op = TK_COLUMN;
|
||||
pE->iColumn = j;
|
||||
pE->iTable = iTable;
|
||||
pOrderBy->a[i].done = 1;
|
||||
break;
|
||||
@ -630,7 +630,7 @@ int sqliteSelect(
|
||||
WhereInfo *pWInfo;
|
||||
Vdbe *v;
|
||||
int isAgg = 0; /* True for select lists like "count(*)" */
|
||||
ExprList *pEList; /* List of fields to extract. NULL means "*" */
|
||||
ExprList *pEList; /* List of columns to extract. NULL means "*" */
|
||||
IdList *pTabList; /* List of tables to select from */
|
||||
Expr *pWhere; /* The WHERE clause. May be NULL */
|
||||
ExprList *pOrderBy; /* The ORDER BY clause. May be NULL */
|
||||
@ -669,7 +669,7 @@ int sqliteSelect(
|
||||
|
||||
/* Look up every table in the table list and create an appropriate
|
||||
** columnlist in pEList if there isn't one already. (The parser leaves
|
||||
** a NULL in the pEList field if the SQL said "SELECT * FROM ...")
|
||||
** a NULL in the p->pEList if the SQL said "SELECT * FROM ...")
|
||||
*/
|
||||
if( fillInColumnList(pParse, p) ){
|
||||
return 1;
|
||||
@ -724,7 +724,7 @@ int sqliteSelect(
|
||||
** need to handle subquerys and temporary tables. From here on we
|
||||
** are committed to keeping the same value for pParse->nTab.
|
||||
**
|
||||
** Resolve the field names and do a semantics check on all the expressions.
|
||||
** Resolve the column names and do a semantics check on all the expressions.
|
||||
*/
|
||||
for(i=0; i<pEList->nExpr; i++){
|
||||
if( sqliteExprResolveIds(pParse, pTabList, pEList->a[i].pExpr) ){
|
||||
@ -906,7 +906,7 @@ int sqliteSelect(
|
||||
assert( pE->pList!=0 && pE->pList->nExpr==1 );
|
||||
sqliteExprCode(pParse, pE->pList->a[0].pExpr);
|
||||
sqliteVdbeAddOp(v, OP_AggGet, 0, i, 0, 0);
|
||||
switch( pE->iField ){
|
||||
switch( pE->iColumn ){
|
||||
case FN_Min: op = OP_Min; break;
|
||||
case FN_Max: op = OP_Max; break;
|
||||
case FN_Avg: op = OP_Add; break;
|
||||
|
@ -24,7 +24,7 @@
|
||||
** This file contains code to implement the "sqlite" command line
|
||||
** utility for accessing SQLite databases.
|
||||
**
|
||||
** $Id: shell.c,v 1.14 2000/06/15 16:49:49 drh Exp $
|
||||
** $Id: shell.c,v 1.15 2000/06/21 13:59:12 drh Exp $
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -136,7 +136,7 @@ struct callback_data {
|
||||
/*
|
||||
** These are the allowed modes.
|
||||
*/
|
||||
#define MODE_Line 0 /* One field per line. Blank line between records */
|
||||
#define MODE_Line 0 /* One column per line. Blank line between records */
|
||||
#define MODE_Column 1 /* One record per line in neat columns */
|
||||
#define MODE_List 2 /* One record per line with a separator */
|
||||
#define MODE_Html 3 /* Generate an XHTML table */
|
||||
|
@ -23,7 +23,7 @@
|
||||
*************************************************************************
|
||||
** Internal interface definitions for SQLite.
|
||||
**
|
||||
** @(#) $Id: sqliteInt.h,v 1.24 2000/06/11 23:50:13 drh Exp $
|
||||
** @(#) $Id: sqliteInt.h,v 1.25 2000/06/21 13:59:12 drh Exp $
|
||||
*/
|
||||
#include "sqlite.h"
|
||||
#include "dbbe.h"
|
||||
@ -35,7 +35,20 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* #define MEMORY_DEBUG 1 */
|
||||
/*
|
||||
** If memory allocation problems are found, recompile with
|
||||
**
|
||||
** -DMEMORY_DEBUG=1
|
||||
**
|
||||
** to enable some sanity checking on malloc() and free(). To
|
||||
** check for memory leaks, recompile with
|
||||
**
|
||||
** -DMEMORY_DEBUG=2
|
||||
**
|
||||
** and a line of text will be written to standard error for
|
||||
** each malloc() and free(). This output can be analyzed
|
||||
** by an AWK script to determine if there are any leaks.
|
||||
*/
|
||||
#ifdef MEMORY_DEBUG
|
||||
# define sqliteMalloc(X) sqliteMalloc_(X,__FILE__,__LINE__)
|
||||
# define sqliteFree(X) sqliteFree_(X,__FILE__,__LINE__)
|
||||
@ -58,8 +71,8 @@ int sqlite_iMallocFail; /* Fail sqliteMalloc() after this many calls */
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The number of entries in the in-memory hash table holding the
|
||||
** schema.
|
||||
** The number of entries in the in-memory hash array holding the
|
||||
** database schema.
|
||||
*/
|
||||
#define N_HASH 51
|
||||
|
||||
@ -77,7 +90,7 @@ int sqlite_iMallocFail; /* Fail sqliteMalloc() after this many calls */
|
||||
#define ArraySize(X) (sizeof(X)/sizeof(X[0]))
|
||||
|
||||
/*
|
||||
** Integer identifiers for functions.
|
||||
** Integer identifiers for built-in SQL functions.
|
||||
*/
|
||||
#define FN_Unknown 0
|
||||
#define FN_Count 1
|
||||
@ -114,24 +127,24 @@ struct sqlite {
|
||||
};
|
||||
|
||||
/*
|
||||
** Possible values for the flags field of sqlite
|
||||
** Possible values for the sqlite.flags.
|
||||
*/
|
||||
#define SQLITE_VdbeTrace 0x00000001
|
||||
#define SQLITE_Initialized 0x00000002
|
||||
|
||||
/*
|
||||
** information about each column of a table is held in an instance
|
||||
** information about each column of an SQL table is held in an instance
|
||||
** of this structure.
|
||||
*/
|
||||
struct Column {
|
||||
char *zName; /* Name of this column */
|
||||
char *zDflt; /* Default value of this column */
|
||||
int notNull; /* True if there is a NOT NULL constraing */
|
||||
int notNull; /* True if there is a NOT NULL constraint */
|
||||
};
|
||||
|
||||
/*
|
||||
** Each table is represented in memory by
|
||||
** an instance of the following structure
|
||||
** Each SQL table is represented in memory by
|
||||
** an instance of the following structure.
|
||||
*/
|
||||
struct Table {
|
||||
char *zName; /* Name of the table */
|
||||
@ -139,19 +152,34 @@ struct Table {
|
||||
int nCol; /* Number of columns in this table */
|
||||
Column *aCol; /* Information about each column */
|
||||
int readOnly; /* True if this table should not be written by the user */
|
||||
Index *pIndex; /* List of indices on this table. */
|
||||
Index *pIndex; /* List of SQL indexes on this table. */
|
||||
};
|
||||
|
||||
/*
|
||||
** Each index is represented in memory by and
|
||||
** Each SQL index is represented in memory by and
|
||||
** instance of the following structure.
|
||||
**
|
||||
** The columns of the table that are to be indexed are described
|
||||
** by the aiColumn[] field of this structure. For example, suppose
|
||||
** we have the following table and index:
|
||||
**
|
||||
** CREATE TABLE Ex1(c1 int, c2 int, c3 text);
|
||||
** CREATE INDEX Ex2 ON Ex1(c3,c1);
|
||||
**
|
||||
** In the Table structure describing Ex1, nCol==3 because there are
|
||||
** three columns in the table. In the Index structure describing
|
||||
** Ex2, nColumn==2 since 2 of the 3 columns of Ex1 are indexed.
|
||||
** The value of aiColumn is {2, 0}. aiColumn[0]==2 because the
|
||||
** first column to be indexed (c3) has an index of 2 in Ex1.aCol[].
|
||||
** The second column to be indexed (c1) has an index of 0 in
|
||||
** Ex1.aCol[], hence Ex2.aiColumn[1]==0.
|
||||
*/
|
||||
struct Index {
|
||||
char *zName; /* Name of this index */
|
||||
Index *pHash; /* Next index with the same hash on zName */
|
||||
int nField; /* Number of fields in the table indexed by this index */
|
||||
int *aiField; /* Indices of fields used by this index. 1st is 0 */
|
||||
Table *pTable; /* The table being indexed */
|
||||
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 isUnique; /* True if keys must all be unique */
|
||||
Index *pNext; /* The next index associated with the same table */
|
||||
};
|
||||
@ -174,11 +202,11 @@ struct Expr {
|
||||
Expr *pLeft, *pRight; /* Left and right subnodes */
|
||||
ExprList *pList; /* A list of expressions used as a function argument */
|
||||
Token token; /* An operand token */
|
||||
int iTable, iField; /* When op==TK_FIELD, then this node means the
|
||||
** iField-th field of the iTable-th table. When
|
||||
** op==TK_FUNCTION, iField holds the function id */
|
||||
int iAgg; /* When op==TK_FIELD and pParse->useAgg==TRUE, pull
|
||||
** value from these element of the aggregator */
|
||||
int iTable, iColumn; /* When op==TK_COLUMN, then this expr node means the
|
||||
** iColumn-th field of the iTable-th table. When
|
||||
** op==TK_FUNCTION, iColumn holds the function id */
|
||||
int iAgg; /* When op==TK_COLUMN and pParse->useAgg==TRUE, pull
|
||||
** result from the iAgg-th element of the aggregator */
|
||||
Select *pSelect; /* When the expression is a sub-select */
|
||||
};
|
||||
|
||||
@ -209,8 +237,8 @@ struct IdList {
|
||||
struct {
|
||||
char *zName; /* Text of the identifier. */
|
||||
char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
|
||||
Table *pTab; /* Table corresponding to zName */
|
||||
int idx; /* Index of a field named zName in a table */
|
||||
Table *pTab; /* An SQL table corresponding to zName */
|
||||
int idx; /* Index in some Table.aCol[] of a column named zName */
|
||||
} *a; /* One entry for each identifier on the list */
|
||||
};
|
||||
|
||||
@ -243,7 +271,7 @@ struct Select {
|
||||
Expr *pHaving; /* The HAVING clause */
|
||||
ExprList *pOrderBy; /* The ORDER BY clause */
|
||||
int op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */
|
||||
Select *pPrior; /* Prior select to which this one joins */
|
||||
Select *pPrior; /* Prior select in a compound select statement */
|
||||
};
|
||||
|
||||
/*
|
||||
|
42
src/update.c
42
src/update.c
@ -24,7 +24,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle UPDATE statements.
|
||||
**
|
||||
** $Id: update.c,v 1.7 2000/06/19 19:09:09 drh Exp $
|
||||
** $Id: update.c,v 1.8 2000/06/21 13:59:12 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -48,8 +48,8 @@ void sqliteUpdate(
|
||||
int base; /* Index of first available table cursor */
|
||||
Index **apIdx = 0; /* An array of indices that need updating too */
|
||||
int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the
|
||||
** an expression for the i-th field of the table.
|
||||
** aXRef[i]==-1 if the i-th field is not changed. */
|
||||
** an expression for the i-th column of the table.
|
||||
** aXRef[i]==-1 if the i-th column is not changed. */
|
||||
|
||||
/* Locate the table which we want to update. This table has to be
|
||||
** put in an IdList structure because some of the subroutines we
|
||||
@ -77,9 +77,9 @@ void sqliteUpdate(
|
||||
if( aXRef==0 ) goto update_cleanup;
|
||||
for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
|
||||
|
||||
/* Resolve the field names in all the expressions in both the
|
||||
** WHERE clause and in the new values. Also find the field index
|
||||
** for each field to be updated in the pChanges array.
|
||||
/* Resolve the column names in all the expressions in both the
|
||||
** WHERE clause and in the new values. Also find the column index
|
||||
** for each column to be updated in the pChanges array.
|
||||
*/
|
||||
if( pWhere ){
|
||||
sqliteExprResolveInSelect(pParse, pWhere);
|
||||
@ -109,7 +109,7 @@ void sqliteUpdate(
|
||||
}
|
||||
}
|
||||
if( j>=pTab->nCol ){
|
||||
sqliteSetString(&pParse->zErrMsg, "no such field: ",
|
||||
sqliteSetString(&pParse->zErrMsg, "no such column: ",
|
||||
pChanges->a[i].zName, 0);
|
||||
pParse->nErr++;
|
||||
goto update_cleanup;
|
||||
@ -118,21 +118,21 @@ void sqliteUpdate(
|
||||
|
||||
/* Allocate memory for the array apIdx[] and fill it pointers to every
|
||||
** index that needs to be updated. Indices only need updating if their
|
||||
** key includes one of the fields named in pChanges.
|
||||
** key includes one of the columns named in pChanges.
|
||||
*/
|
||||
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
for(i=0; i<pIdx->nField; i++){
|
||||
if( aXRef[pIdx->aiField[i]]>=0 ) break;
|
||||
for(i=0; i<pIdx->nColumn; i++){
|
||||
if( aXRef[pIdx->aiColumn[i]]>=0 ) break;
|
||||
}
|
||||
if( i<pIdx->nField ) nIdx++;
|
||||
if( i<pIdx->nColumn ) nIdx++;
|
||||
}
|
||||
apIdx = sqliteMalloc( sizeof(Index*) * nIdx );
|
||||
if( apIdx==0 ) goto update_cleanup;
|
||||
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
for(i=0; i<pIdx->nField; i++){
|
||||
if( aXRef[pIdx->aiField[i]]>=0 ) break;
|
||||
for(i=0; i<pIdx->nColumn; i++){
|
||||
if( aXRef[pIdx->aiColumn[i]]>=0 ) break;
|
||||
}
|
||||
if( i<pIdx->nField ) apIdx[nIdx++] = pIdx;
|
||||
if( i<pIdx->nColumn ) apIdx[nIdx++] = pIdx;
|
||||
}
|
||||
|
||||
/* Begin generating code.
|
||||
@ -165,7 +165,7 @@ void sqliteUpdate(
|
||||
}
|
||||
|
||||
/* Loop over every record that needs updating. We have to load
|
||||
** the old data for each record to be updated because some fields
|
||||
** the old data for each record to be updated because some columns
|
||||
** might not change and we will need to copy the old value.
|
||||
** Also, the old data is needed to delete the old index entires.
|
||||
*/
|
||||
@ -179,10 +179,10 @@ void sqliteUpdate(
|
||||
for(i=0; i<nIdx; i++){
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
|
||||
pIdx = apIdx[i];
|
||||
for(j=0; j<pIdx->nField; j++){
|
||||
sqliteVdbeAddOp(v, OP_Field, base, pIdx->aiField[j], 0, 0);
|
||||
for(j=0; j<pIdx->nColumn; j++){
|
||||
sqliteVdbeAddOp(v, OP_Field, base, pIdx->aiColumn[j], 0, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nColumn, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_DeleteIdx, base+i+1, 0, 0, 0);
|
||||
}
|
||||
|
||||
@ -202,10 +202,10 @@ void sqliteUpdate(
|
||||
for(i=0; i<nIdx; i++){
|
||||
sqliteVdbeAddOp(v, OP_Dup, pTab->nCol, 0, 0, 0); /* The KEY */
|
||||
pIdx = apIdx[i];
|
||||
for(j=0; j<pIdx->nField; j++){
|
||||
sqliteVdbeAddOp(v, OP_Dup, j+pTab->nCol-pIdx->aiField[j], 0, 0, 0);
|
||||
for(j=0; j<pIdx->nColumn; j++){
|
||||
sqliteVdbeAddOp(v, OP_Dup, j+pTab->nCol-pIdx->aiColumn[j], 0, 0, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nColumn, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_PutIdx, base+i+1, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
** This file contains functions for allocating memory, comparing
|
||||
** strings, and stuff like that.
|
||||
**
|
||||
** $Id: util.c,v 1.12 2000/06/08 13:36:41 drh Exp $
|
||||
** $Id: util.c,v 1.13 2000/06/21 13:59:12 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <stdarg.h>
|
||||
@ -360,7 +360,7 @@ int sqliteHashNoCase(const char *z, int n){
|
||||
}
|
||||
|
||||
/*
|
||||
** Some system shave stricmp(). Others have strcasecmp(). Because
|
||||
** Some systems have stricmp(). Others have strcasecmp(). Because
|
||||
** there is no consistency, we will define our own.
|
||||
*/
|
||||
int sqliteStrICmp(const char *zLeft, const char *zRight){
|
||||
|
326
src/vdbe.c
326
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.33 2000/06/11 23:50:13 drh Exp $
|
||||
** $Id: vdbe.c,v 1.34 2000/06/21 13:59:13 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <unistd.h>
|
||||
@ -54,15 +54,21 @@
|
||||
typedef struct VdbeOp Op;
|
||||
|
||||
/*
|
||||
** Every table that the virtual machine has open is represented by an
|
||||
** A cursor is a pointer into a database file. The database file
|
||||
** can represent either an SQL table or an SQL index. Each file is
|
||||
** a bag of key/data pairs. The cursor can loop over all key/data
|
||||
** pairs (in an arbitrary order) or it can retrieve a particular
|
||||
** key/data pair given a copy of the key.
|
||||
**
|
||||
** Every cursor that the virtual machine has open is represented by an
|
||||
** instance of the following structure.
|
||||
*/
|
||||
struct VdbeTable {
|
||||
DbbeTable *pTable; /* The table structure of the backend */
|
||||
struct Cursor {
|
||||
DbbeCursor *pCursor; /* The cursor structure of the backend */
|
||||
int index; /* The next index to extract */
|
||||
int keyAsData; /* The OP_Field command works on key instead of data */
|
||||
};
|
||||
typedef struct VdbeTable VdbeTable;
|
||||
typedef struct Cursor Cursor;
|
||||
|
||||
/*
|
||||
** A sorter builds a list of elements to be sorted. Each element of
|
||||
@ -117,7 +123,7 @@ typedef struct Mem Mem;
|
||||
#define STK_Dyn 0x0010 /* Need to call sqliteFree() on zStack[*] */
|
||||
|
||||
/*
|
||||
** An Agg structure describes and Aggregator. Each Agg consists of
|
||||
** An Agg structure describes an Aggregator. Each Agg consists of
|
||||
** zero or more Aggregator elements (AggElem). Each AggElem contains
|
||||
** a key and one or more values. The values are used in processing
|
||||
** aggregate functions in a SELECT. The key is used to implement
|
||||
@ -130,7 +136,7 @@ struct Agg {
|
||||
AggElem *pCurrent; /* The AggElem currently in focus */
|
||||
int nElem; /* The number of AggElems */
|
||||
int nHash; /* Number of slots in apHash[] */
|
||||
AggElem **apHash; /* A hash table for looking up AggElems by zKey */
|
||||
AggElem **apHash; /* A hash array for looking up AggElems by zKey */
|
||||
AggElem *pFirst; /* A list of all AggElems */
|
||||
};
|
||||
struct AggElem {
|
||||
@ -150,7 +156,7 @@ typedef struct Set Set;
|
||||
typedef struct SetElem SetElem;
|
||||
struct Set {
|
||||
SetElem *pAll; /* All elements of this set */
|
||||
SetElem *apHash[41]; /* A hash table for all elements in this set */
|
||||
SetElem *apHash[41]; /* A hash array for all elements in this set */
|
||||
};
|
||||
struct SetElem {
|
||||
SetElem *pHash; /* Next element with the same hash on zKey */
|
||||
@ -175,8 +181,8 @@ struct Vdbe {
|
||||
Stack *aStack; /* The operand stack, except string values */
|
||||
char **zStack; /* Text or binary values of the stack */
|
||||
char **azColName; /* Becomes the 4th parameter to callbacks */
|
||||
int nTable; /* Number of slots in aTab[] */
|
||||
VdbeTable *aTab; /* On element of this array for each open table */
|
||||
int nCursor; /* Number of slots in aCsr[] */
|
||||
Cursor *aCsr; /* On element of this array for each open cursor */
|
||||
int nList; /* Number of slots in apList[] */
|
||||
FILE **apList; /* An open file for each list */
|
||||
int nSort; /* Number of slots in apSort[] */
|
||||
@ -394,7 +400,7 @@ static void AggReset(Agg *p){
|
||||
}
|
||||
|
||||
/*
|
||||
** Add the given AggElem to the hash table
|
||||
** Add the given AggElem to the hash array
|
||||
*/
|
||||
static void AggEnhash(Agg *p, AggElem *pElem){
|
||||
int h = sqliteHashNoCase(pElem->zKey, 0) % p->nHash;
|
||||
@ -403,7 +409,7 @@ static void AggEnhash(Agg *p, AggElem *pElem){
|
||||
}
|
||||
|
||||
/*
|
||||
** Change the size of the hash table to the amount given.
|
||||
** Change the size of the hash array to the amount given.
|
||||
*/
|
||||
static void AggRehash(Agg *p, int nHash){
|
||||
int size;
|
||||
@ -634,7 +640,7 @@ static int hardNeedStack(Vdbe *p, int N){
|
||||
/*
|
||||
** Clean up the VM after execution.
|
||||
**
|
||||
** This routine will automatically close any tables, list, and/or
|
||||
** This routine will automatically close any cursors, list, and/or
|
||||
** sorters that were left open.
|
||||
*/
|
||||
static void Cleanup(Vdbe *p){
|
||||
@ -642,15 +648,15 @@ static void Cleanup(Vdbe *p){
|
||||
PopStack(p, p->tos+1);
|
||||
sqliteFree(p->azColName);
|
||||
p->azColName = 0;
|
||||
for(i=0; i<p->nTable; i++){
|
||||
if( p->aTab[i].pTable ){
|
||||
sqliteDbbeCloseTable(p->aTab[i].pTable);
|
||||
p->aTab[i].pTable = 0;
|
||||
for(i=0; i<p->nCursor; i++){
|
||||
if( p->aCsr[i].pCursor ){
|
||||
sqliteDbbeCloseCursor(p->aCsr[i].pCursor);
|
||||
p->aCsr[i].pCursor = 0;
|
||||
}
|
||||
}
|
||||
sqliteFree(p->aTab);
|
||||
p->aTab = 0;
|
||||
p->nTable = 0;
|
||||
sqliteFree(p->aCsr);
|
||||
p->aCsr = 0;
|
||||
p->nCursor = 0;
|
||||
for(i=0; i<p->nMem; i++){
|
||||
if( p->aMem[i].s.flags & STK_Dyn ){
|
||||
sqliteFree(p->aMem[i].z);
|
||||
@ -786,7 +792,7 @@ int sqliteVdbeList(
|
||||
char **pzErrMsg /* Error msg written here */
|
||||
){
|
||||
int i, rc;
|
||||
char *azField[6];
|
||||
char *azValue[6];
|
||||
char zAddr[20];
|
||||
char zP1[20];
|
||||
char zP2[20];
|
||||
@ -795,19 +801,19 @@ int sqliteVdbeList(
|
||||
};
|
||||
|
||||
if( xCallback==0 ) return 0;
|
||||
azField[0] = zAddr;
|
||||
azField[2] = zP1;
|
||||
azField[3] = zP2;
|
||||
azField[5] = 0;
|
||||
azValue[0] = zAddr;
|
||||
azValue[2] = zP1;
|
||||
azValue[3] = zP2;
|
||||
azValue[5] = 0;
|
||||
rc = SQLITE_OK;
|
||||
/* if( pzErrMsg ){ *pzErrMsg = 0; } */
|
||||
for(i=0; rc==SQLITE_OK && i<p->nOp; i++){
|
||||
sprintf(zAddr,"%d",i);
|
||||
sprintf(zP1,"%d", p->aOp[i].p1);
|
||||
sprintf(zP2,"%d", p->aOp[i].p2);
|
||||
azField[4] = p->aOp[i].p3;
|
||||
azField[1] = zOpName[p->aOp[i].opcode];
|
||||
if( xCallback(pArg, 5, azField, azColumnNames) ){
|
||||
azValue[4] = p->aOp[i].p3;
|
||||
azValue[1] = zOpName[p->aOp[i].opcode];
|
||||
if( xCallback(pArg, 5, azValue, azColumnNames) ){
|
||||
rc = SQLITE_ABORT;
|
||||
}
|
||||
}
|
||||
@ -1492,7 +1498,7 @@ int sqliteVdbeExec(
|
||||
|
||||
/* Opcode: Not * * *
|
||||
**
|
||||
** Treat the top of the stack as a boolean value. Replace it
|
||||
** Interpret the top of the stack as a boolean value. Replace it
|
||||
** with its complement.
|
||||
*/
|
||||
case OP_Not: {
|
||||
@ -1676,33 +1682,34 @@ int sqliteVdbeExec(
|
||||
|
||||
/* Opcode: Open P1 P2 P3
|
||||
**
|
||||
** Open a new cursor for the database table named P3. Give the
|
||||
** cursor an identifier P1.
|
||||
** Open readonly if P2==0 and for reading and writing if P2!=0.
|
||||
** The table is created if it does not already exist and P2!=0.
|
||||
** If there is already another cursor opened with identifier P1,
|
||||
** then the old cursor is closed first.
|
||||
** All cursors are automatically closed when
|
||||
** the VDBE finishes execution. The P1 values need not be
|
||||
** Open a new cursor for the database file named P3. Give the
|
||||
** cursor an identifier P1. The P1 values need not be
|
||||
** contiguous but all P1 values should be small integers. It is
|
||||
** an error for P1 to be negative.
|
||||
**
|
||||
** If P3 is null or an empty string, a temporary table created.
|
||||
** This table is automatically deleted when the cursor is closed.
|
||||
** Open readonly if P2==0 and for reading and writing if P2!=0.
|
||||
** The file is created if it does not already exist and P2!=0.
|
||||
** If there is already another cursor opened with identifier P1,
|
||||
** then the old cursor is closed first. All cursors are
|
||||
** automatically closed when the VDBE finishes execution.
|
||||
**
|
||||
** If P3 is null or an empty string, a temporary database file
|
||||
** is created. This temporary database file is automatically
|
||||
** deleted when the cursor is closed.
|
||||
*/
|
||||
case OP_Open: {
|
||||
int i = pOp->p1;
|
||||
if( i<0 ) goto bad_instruction;
|
||||
if( i>=p->nTable ){
|
||||
if( i>=p->nCursor ){
|
||||
int j;
|
||||
p->aTab = sqliteRealloc( p->aTab, (i+1)*sizeof(VdbeTable) );
|
||||
if( p->aTab==0 ){ p->nTable = 0; goto no_mem; }
|
||||
for(j=p->nTable; j<=i; j++) p->aTab[j].pTable = 0;
|
||||
p->nTable = i+1;
|
||||
}else if( p->aTab[i].pTable ){
|
||||
sqliteDbbeCloseTable(p->aTab[i].pTable);
|
||||
p->aCsr = sqliteRealloc( p->aCsr, (i+1)*sizeof(Cursor) );
|
||||
if( p->aCsr==0 ){ p->nCursor = 0; goto no_mem; }
|
||||
for(j=p->nCursor; j<=i; j++) p->aCsr[j].pCursor = 0;
|
||||
p->nCursor = i+1;
|
||||
}else if( p->aCsr[i].pCursor ){
|
||||
sqliteDbbeCloseCursor(p->aCsr[i].pCursor);
|
||||
}
|
||||
rc = sqliteDbbeOpenTable(p->pBe, pOp->p3, pOp->p2, &p->aTab[i].pTable);
|
||||
rc = sqliteDbbeOpenCursor(p->pBe, pOp->p3, pOp->p2,&p->aCsr[i].pCursor);
|
||||
switch( rc ){
|
||||
case SQLITE_BUSY: {
|
||||
sqliteSetString(pzErrMsg,"table ", pOp->p3, " is locked", 0);
|
||||
@ -1722,21 +1729,21 @@ int sqliteVdbeExec(
|
||||
goto no_mem;
|
||||
}
|
||||
}
|
||||
p->aTab[i].index = 0;
|
||||
p->aTab[i].keyAsData = 0;
|
||||
p->aCsr[i].index = 0;
|
||||
p->aCsr[i].keyAsData = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Close P1 * *
|
||||
**
|
||||
** Close a database table previously opened as P1. If P1 is not
|
||||
** Close a cursor previously opened as P1. If P1 is not
|
||||
** currently open, this instruction is a no-op.
|
||||
*/
|
||||
case OP_Close: {
|
||||
int i = pOp->p1;
|
||||
if( i>=0 && i<p->nTable && p->aTab[i].pTable ){
|
||||
sqliteDbbeCloseTable(p->aTab[i].pTable);
|
||||
p->aTab[i].pTable = 0;
|
||||
if( i>=0 && i<p->nCursor && p->aCsr[i].pCursor ){
|
||||
sqliteDbbeCloseCursor(p->aCsr[i].pCursor);
|
||||
p->aCsr[i].pCursor = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1744,21 +1751,20 @@ int sqliteVdbeExec(
|
||||
/* Opcode: Fetch P1 * *
|
||||
**
|
||||
** Pop the top of the stack and use its value as a key to fetch
|
||||
** a record from database table or index P1. The data is held
|
||||
** in the P1 cursor until needed. The data is not pushed onto the
|
||||
** stack.
|
||||
** a record from cursor P1. The key/data pair is held
|
||||
** in the P1 cursor until needed.
|
||||
*/
|
||||
case OP_Fetch: {
|
||||
int i = pOp->p1;
|
||||
int tos = p->tos;
|
||||
if( tos<0 ) goto not_enough_stack;
|
||||
if( i>=0 && i<p->nTable && p->aTab[i].pTable ){
|
||||
if( i>=0 && i<p->nCursor && p->aCsr[i].pCursor ){
|
||||
if( p->aStack[tos].flags & STK_Int ){
|
||||
sqliteDbbeFetch(p->aTab[i].pTable, sizeof(int),
|
||||
sqliteDbbeFetch(p->aCsr[i].pCursor, sizeof(int),
|
||||
(char*)&p->aStack[tos].i);
|
||||
}else{
|
||||
if( Stringify(p, tos) ) goto no_mem;
|
||||
sqliteDbbeFetch(p->aTab[i].pTable, p->aStack[tos].n,
|
||||
sqliteDbbeFetch(p->aCsr[i].pCursor, p->aStack[tos].n,
|
||||
p->zStack[tos]);
|
||||
}
|
||||
p->nFetch++;
|
||||
@ -1787,7 +1793,7 @@ int sqliteVdbeExec(
|
||||
/* Opcode: Distinct P1 P2 *
|
||||
**
|
||||
** Use the top of the stack as a key. If a record with that key
|
||||
** does not exist in table P1, then jump to P2. If the record
|
||||
** does not exist in file P1, then jump to P2. If the record
|
||||
** does already exist, then fall thru. The record is not retrieved.
|
||||
** The key is not popped from the stack.
|
||||
**
|
||||
@ -1797,14 +1803,14 @@ int sqliteVdbeExec(
|
||||
/* Opcode: Found P1 P2 *
|
||||
**
|
||||
** Use the top of the stack as a key. If a record with that key
|
||||
** does exist in table P1, then jump to P2. If the record
|
||||
** does exist in file P1, then jump to P2. If the record
|
||||
** does not exist, then fall thru. The record is not retrieved.
|
||||
** The key is popped from the stack.
|
||||
*/
|
||||
/* Opcode: NotFound P1 P2 *
|
||||
**
|
||||
** Use the top of the stack as a key. If a record with that key
|
||||
** does not exist in table P1, then jump to P2. If the record
|
||||
** does not exist in file P1, then jump to P2. If the record
|
||||
** does exist, then fall thru. The record is not retrieved.
|
||||
** The key is popped from the stack.
|
||||
**
|
||||
@ -1818,13 +1824,13 @@ int sqliteVdbeExec(
|
||||
int tos = p->tos;
|
||||
int alreadyExists = 0;
|
||||
if( tos<0 ) goto not_enough_stack;
|
||||
if( i>=0 && i<p->nTable && p->aTab[i].pTable ){
|
||||
if( i>=0 && i<p->nCursor && p->aCsr[i].pCursor ){
|
||||
if( p->aStack[tos].flags & STK_Int ){
|
||||
alreadyExists = sqliteDbbeTest(p->aTab[i].pTable, sizeof(int),
|
||||
alreadyExists = sqliteDbbeTest(p->aCsr[i].pCursor, sizeof(int),
|
||||
(char*)&p->aStack[tos].i);
|
||||
}else{
|
||||
if( Stringify(p, tos) ) goto no_mem;
|
||||
alreadyExists = sqliteDbbeTest(p->aTab[i].pTable, p->aStack[tos].n,
|
||||
alreadyExists = sqliteDbbeTest(p->aCsr[i].pCursor,p->aStack[tos].n,
|
||||
p->zStack[tos]);
|
||||
}
|
||||
}
|
||||
@ -1841,16 +1847,16 @@ int sqliteVdbeExec(
|
||||
|
||||
/* Opcode: New P1 * *
|
||||
**
|
||||
** Get a new integer key not previous used by table P1 and
|
||||
** push it onto the stack.
|
||||
** Get a new integer key not previous used by the database file
|
||||
** associated with cursor P1 and push it onto the stack.
|
||||
*/
|
||||
case OP_New: {
|
||||
int i = pOp->p1;
|
||||
int v;
|
||||
if( i<0 || i>=p->nTable || p->aTab[i].pTable==0 ){
|
||||
if( i<0 || i>=p->nCursor || p->aCsr[i].pCursor==0 ){
|
||||
v = 0;
|
||||
}else{
|
||||
v = sqliteDbbeNew(p->aTab[i].pTable);
|
||||
v = sqliteDbbeNew(p->aCsr[i].pCursor);
|
||||
}
|
||||
NeedStack(p, p->tos+1);
|
||||
p->tos++;
|
||||
@ -1861,7 +1867,7 @@ int sqliteVdbeExec(
|
||||
|
||||
/* Opcode: Put P1 * *
|
||||
**
|
||||
** Write an entry into the database table P1. A new entry is
|
||||
** Write an entry into the database file P1. A new entry is
|
||||
** created if it doesn't already exist, or the data for an existing
|
||||
** entry is overwritten. The data is the value on the top of the
|
||||
** stack. The key is the next value down on the stack. The stack
|
||||
@ -1872,7 +1878,7 @@ int sqliteVdbeExec(
|
||||
int nos = p->tos-1;
|
||||
int i = pOp->p1;
|
||||
if( nos<0 ) goto not_enough_stack;
|
||||
if( i>=0 && i<p->nTable && p->aTab[i].pTable!=0 ){
|
||||
if( i>=0 && i<p->nCursor && p->aCsr[i].pCursor!=0 ){
|
||||
char *zKey;
|
||||
int nKey;
|
||||
if( (p->aStack[nos].flags & STK_Int)==0 ){
|
||||
@ -1883,7 +1889,7 @@ int sqliteVdbeExec(
|
||||
nKey = sizeof(int);
|
||||
zKey = (char*)&p->aStack[nos].i;
|
||||
}
|
||||
sqliteDbbePut(p->aTab[i].pTable, nKey, zKey,
|
||||
sqliteDbbePut(p->aCsr[i].pCursor, nKey, zKey,
|
||||
p->aStack[tos].n, p->zStack[tos]);
|
||||
}
|
||||
PopStack(p, 2);
|
||||
@ -1893,13 +1899,13 @@ int sqliteVdbeExec(
|
||||
/* Opcode: Delete P1 * *
|
||||
**
|
||||
** The top of the stack is a key. Remove this key and its data
|
||||
** from database table P1. Then pop the stack to discard the key.
|
||||
** from database file P1. Then pop the stack to discard the key.
|
||||
*/
|
||||
case OP_Delete: {
|
||||
int tos = p->tos;
|
||||
int i = pOp->p1;
|
||||
if( tos<0 ) goto not_enough_stack;
|
||||
if( i>=0 && i<p->nTable && p->aTab[i].pTable!=0 ){
|
||||
if( i>=0 && i<p->nCursor && p->aCsr[i].pCursor!=0 ){
|
||||
char *zKey;
|
||||
int nKey;
|
||||
if( p->aStack[tos].flags & STK_Int ){
|
||||
@ -1910,7 +1916,7 @@ int sqliteVdbeExec(
|
||||
nKey = p->aStack[tos].n;
|
||||
zKey = p->zStack[tos];
|
||||
}
|
||||
sqliteDbbeDelete(p->aTab[i].pTable, nKey, zKey);
|
||||
sqliteDbbeDelete(p->aCsr[i].pCursor, nKey, zKey);
|
||||
}
|
||||
PopStack(p, 1);
|
||||
break;
|
||||
@ -1925,16 +1931,18 @@ int sqliteVdbeExec(
|
||||
*/
|
||||
case OP_KeyAsData: {
|
||||
int i = pOp->p1;
|
||||
if( i>=0 && i<p->nTable && p->aTab[i].pTable!=0 ){
|
||||
p->aTab[i].keyAsData = pOp->p2;
|
||||
if( i>=0 && i<p->nCursor && p->aCsr[i].pCursor!=0 ){
|
||||
p->aCsr[i].keyAsData = pOp->p2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Field P1 P2 *
|
||||
**
|
||||
** Push onto the stack the value of the P2-th field from the
|
||||
** most recent Fetch from table P1.
|
||||
** Interpret the data in the most recent fetch from cursor P1
|
||||
** is a structure built using the MakeRecord instruction.
|
||||
** Push onto the stack the value of the P2-th field of that
|
||||
** structure.
|
||||
**
|
||||
** The value pushed is just a pointer to the data in the cursor.
|
||||
** The value will go away the next time a record is fetched from P1,
|
||||
@ -1944,6 +1952,11 @@ int sqliteVdbeExec(
|
||||
** If the KeyAsData opcode has previously executed on this cursor,
|
||||
** then the field might be extracted from the key rather than the
|
||||
** data.
|
||||
**
|
||||
** Viewed from a higher level, this instruction retrieves the
|
||||
** data from a single column in a particular row of an SQL table
|
||||
** file. Perhaps the name of this instruction should be
|
||||
** "Column" instead of "Field"...
|
||||
*/
|
||||
case OP_Field: {
|
||||
int *pAddr;
|
||||
@ -1951,35 +1964,35 @@ int sqliteVdbeExec(
|
||||
int i = pOp->p1;
|
||||
int p2 = pOp->p2;
|
||||
int tos = ++p->tos;
|
||||
DbbeTable *pTab;
|
||||
DbbeCursor *pCrsr;
|
||||
char *z;
|
||||
|
||||
if( NeedStack(p, tos) ) goto no_mem;
|
||||
if( i>=0 && i<p->nTable && (pTab = p->aTab[i].pTable)!=0 ){
|
||||
if( p->aTab[i].keyAsData ){
|
||||
amt = sqliteDbbeKeyLength(pTab);
|
||||
if( i>=0 && i<p->nCursor && (pCrsr = p->aCsr[i].pCursor)!=0 ){
|
||||
if( p->aCsr[i].keyAsData ){
|
||||
amt = sqliteDbbeKeyLength(pCrsr);
|
||||
if( amt<=sizeof(int)*(p2+1) ){
|
||||
p->aStack[tos].flags = STK_Null;
|
||||
break;
|
||||
}
|
||||
pAddr = (int*)sqliteDbbeReadKey(pTab, sizeof(int)*p2);
|
||||
pAddr = (int*)sqliteDbbeReadKey(pCrsr, sizeof(int)*p2);
|
||||
if( *pAddr==0 ){
|
||||
p->aStack[tos].flags = STK_Null;
|
||||
break;
|
||||
}
|
||||
z = sqliteDbbeReadKey(pTab, *pAddr);
|
||||
z = sqliteDbbeReadKey(pCrsr, *pAddr);
|
||||
}else{
|
||||
amt = sqliteDbbeDataLength(pTab);
|
||||
amt = sqliteDbbeDataLength(pCrsr);
|
||||
if( amt<=sizeof(int)*(p2+1) ){
|
||||
p->aStack[tos].flags = STK_Null;
|
||||
break;
|
||||
}
|
||||
pAddr = (int*)sqliteDbbeReadData(pTab, sizeof(int)*p2);
|
||||
pAddr = (int*)sqliteDbbeReadData(pCrsr, sizeof(int)*p2);
|
||||
if( *pAddr==0 ){
|
||||
p->aStack[tos].flags = STK_Null;
|
||||
break;
|
||||
}
|
||||
z = sqliteDbbeReadData(pTab, *pAddr);
|
||||
z = sqliteDbbeReadData(pCrsr, *pAddr);
|
||||
}
|
||||
p->zStack[tos] = z;
|
||||
p->aStack[tos].n = strlen(z) + 1;
|
||||
@ -1991,21 +2004,22 @@ int sqliteVdbeExec(
|
||||
/* Opcode: Key P1 * *
|
||||
**
|
||||
** Push onto the stack an integer which is the first 4 bytes of the
|
||||
** the key to the current entry in a sequential scan of the table P1.
|
||||
** A sequential scan is started using the Next opcode.
|
||||
** the key to the current entry in a sequential scan of the database
|
||||
** file P1. The sequential scan should have been started using the
|
||||
** Next opcode.
|
||||
*/
|
||||
case OP_Key: {
|
||||
int i = pOp->p1;
|
||||
int tos = ++p->tos;
|
||||
DbbeTable *pTab;
|
||||
DbbeCursor *pCrsr;
|
||||
|
||||
if( NeedStack(p, p->tos) ) goto no_mem;
|
||||
if( i>=0 && i<p->nTable && (pTab = p->aTab[i].pTable)!=0 ){
|
||||
char *z = sqliteDbbeReadKey(pTab, 0);
|
||||
if( p->aTab[i].keyAsData ){
|
||||
if( i>=0 && i<p->nCursor && (pCrsr = p->aCsr[i].pCursor)!=0 ){
|
||||
char *z = sqliteDbbeReadKey(pCrsr, 0);
|
||||
if( p->aCsr[i].keyAsData ){
|
||||
p->zStack[tos] = z;
|
||||
p->aStack[tos].flags = STK_Str;
|
||||
p->aStack[tos].n = sqliteDbbeKeyLength(pTab);
|
||||
p->aStack[tos].n = sqliteDbbeKeyLength(pCrsr);
|
||||
}else{
|
||||
memcpy(&p->aStack[tos].i, z, sizeof(int));
|
||||
p->aStack[tos].flags = STK_Int;
|
||||
@ -2017,25 +2031,25 @@ int sqliteVdbeExec(
|
||||
/* Opcode: Rewind P1 * *
|
||||
**
|
||||
** The next use of the Key or Field or Next instruction for P1
|
||||
** will refer to the first entry in the table.
|
||||
** will refer to the first entry in the database file.
|
||||
*/
|
||||
case OP_Rewind: {
|
||||
int i = pOp->p1;
|
||||
if( i>=0 && i<p->nTable && p->aTab[i].pTable!=0 ){
|
||||
sqliteDbbeRewind(p->aTab[i].pTable);
|
||||
if( i>=0 && i<p->nCursor && p->aCsr[i].pCursor!=0 ){
|
||||
sqliteDbbeRewind(p->aCsr[i].pCursor);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Next P1 P2 *
|
||||
**
|
||||
** Advance P1 to the next entry in the table. Or, if there are no
|
||||
** more entries, rewind P1 and jump to location P2.
|
||||
** Advance P1 to the next key/data pair in the file. Or, if there are no
|
||||
** more key/data pairs, rewind P1 and jump to location P2.
|
||||
*/
|
||||
case OP_Next: {
|
||||
int i = pOp->p1;
|
||||
if( i>=0 && i<p->nTable && p->aTab[i].pTable!=0 ){
|
||||
if( sqliteDbbeNextKey(p->aTab[i].pTable)==0 ){
|
||||
if( i>=0 && i<p->nCursor && p->aCsr[i].pCursor!=0 ){
|
||||
if( sqliteDbbeNextKey(p->aCsr[i].pCursor)==0 ){
|
||||
pc = pOp->p2 - 1;
|
||||
}else{
|
||||
p->nFetch++;
|
||||
@ -2046,37 +2060,48 @@ int sqliteVdbeExec(
|
||||
|
||||
/* Opcode: ResetIdx P1 * *
|
||||
**
|
||||
** Begin treating the current row of table P1 as an index. The next
|
||||
** NextIdx instruction will refer to the first index in the table.
|
||||
** Begin treating the current data in cursor P1 as a bunch of integer
|
||||
** keys to records of a (separate) SQL table file. This instruction
|
||||
** causes the new NextIdx instruction push the first integer table
|
||||
** key in the data.
|
||||
*/
|
||||
case OP_ResetIdx: {
|
||||
int i = pOp->p1;
|
||||
if( i>=0 && i<p->nTable ){
|
||||
p->aTab[i].index = 0;
|
||||
if( i>=0 && i<p->nCursor ){
|
||||
p->aCsr[i].index = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: NextIdx P1 P2 *
|
||||
**
|
||||
** Push the next index from the current entry of table P1 onto the
|
||||
** stack and advance the pointer. If there are no more indices, then
|
||||
** reset the table entry and jump to P2
|
||||
** The P1 cursor points to an SQL index. The data from the most
|
||||
** recent fetch on that cursor consists of a bunch of integers where
|
||||
** each integer is the key to a record in an SQL table file.
|
||||
** This instruction grabs the next integer table key from the data
|
||||
** of P1 and pushes that integer onto the stack. The first time
|
||||
** this instruction is executed after a fetch, the first integer
|
||||
** table key is pushed. Subsequent integer table keys are pushed
|
||||
** in each subsequent execution of this instruction.
|
||||
**
|
||||
** If there are no more integer table keys in the data of P1
|
||||
** when this instruction is executed, then nothing gets pushed and
|
||||
** there is an immediate jump to instruction P2.
|
||||
*/
|
||||
case OP_NextIdx: {
|
||||
int i = pOp->p1;
|
||||
int tos = ++p->tos;
|
||||
DbbeTable *pTab;
|
||||
DbbeCursor *pCrsr;
|
||||
|
||||
if( NeedStack(p, p->tos) ) goto no_mem;
|
||||
p->zStack[tos] = 0;
|
||||
if( i>=0 && i<p->nTable && (pTab = p->aTab[i].pTable)!=0 ){
|
||||
if( i>=0 && i<p->nCursor && (pCrsr = p->aCsr[i].pCursor)!=0 ){
|
||||
int *aIdx;
|
||||
int nIdx;
|
||||
int j;
|
||||
nIdx = sqliteDbbeDataLength(pTab)/sizeof(int);
|
||||
aIdx = (int*)sqliteDbbeReadData(pTab, 0);
|
||||
for(j=p->aTab[i].index; j<nIdx; j++){
|
||||
nIdx = sqliteDbbeDataLength(pCrsr)/sizeof(int);
|
||||
aIdx = (int*)sqliteDbbeReadData(pCrsr, 0);
|
||||
for(j=p->aCsr[i].index; j<nIdx; j++){
|
||||
if( aIdx[j]!=0 ){
|
||||
p->aStack[tos].i = aIdx[j];
|
||||
p->aStack[tos].flags = STK_Int;
|
||||
@ -2088,47 +2113,47 @@ int sqliteVdbeExec(
|
||||
pc = pOp->p2 - 1;
|
||||
PopStack(p, 1);
|
||||
}
|
||||
p->aTab[i].index = j+1;
|
||||
p->aCsr[i].index = j+1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: PutIdx P1 * *
|
||||
**
|
||||
** The top of the stack hold an index key (probably made using the
|
||||
** MakeKey instruction) and next on stack holds an index value for
|
||||
** a table. Locate the record in the index P1 that has the key
|
||||
** and insert the index value into its
|
||||
** data. Write the results back to the index.
|
||||
** If the key doesn't exist it is created.
|
||||
** The top of the stack hold an SQL index key (probably made using the
|
||||
** MakeKey instruction) and next on stack holds an integer which
|
||||
** the key to an SQL table entry. Locate the record in cursor P1
|
||||
** that has the same key as on the TOS. Create a new record if
|
||||
** necessary. Then append the integer table key to the data for that
|
||||
** record and write it back to the P1 file.
|
||||
*/
|
||||
case OP_PutIdx: {
|
||||
int i = pOp->p1;
|
||||
int tos = p->tos;
|
||||
int nos = tos - 1;
|
||||
DbbeTable *pTab;
|
||||
DbbeCursor *pCrsr;
|
||||
if( nos<0 ) goto not_enough_stack;
|
||||
if( i>=0 && i<p->nTable && (pTab = p->aTab[i].pTable)!=0 ){
|
||||
if( i>=0 && i<p->nCursor && (pCrsr = p->aCsr[i].pCursor)!=0 ){
|
||||
int r;
|
||||
int newVal;
|
||||
Integerify(p, nos);
|
||||
newVal = p->aStack[nos].i;
|
||||
if( Stringify(p, tos) ) goto no_mem;
|
||||
r = sqliteDbbeFetch(pTab, p->aStack[tos].n, p->zStack[tos]);
|
||||
r = sqliteDbbeFetch(pCrsr, p->aStack[tos].n, p->zStack[tos]);
|
||||
if( r==0 ){
|
||||
/* Create a new record for this index */
|
||||
sqliteDbbePut(pTab, p->aStack[tos].n, p->zStack[tos],
|
||||
sqliteDbbePut(pCrsr, p->aStack[tos].n, p->zStack[tos],
|
||||
sizeof(int), (char*)&newVal);
|
||||
}else{
|
||||
/* Extend the existing record */
|
||||
int nIdx;
|
||||
int *aIdx;
|
||||
nIdx = sqliteDbbeDataLength(pTab)/sizeof(int);
|
||||
nIdx = sqliteDbbeDataLength(pCrsr)/sizeof(int);
|
||||
aIdx = sqliteMalloc( sizeof(int)*(nIdx+1) );
|
||||
if( aIdx==0 ) goto no_mem;
|
||||
sqliteDbbeCopyData(pTab, 0, nIdx*sizeof(int), (char*)aIdx);
|
||||
sqliteDbbeCopyData(pCrsr, 0, nIdx*sizeof(int), (char*)aIdx);
|
||||
aIdx[nIdx] = newVal;
|
||||
sqliteDbbePut(pTab, p->aStack[tos].n, p->zStack[tos],
|
||||
sqliteDbbePut(pCrsr, p->aStack[tos].n, p->zStack[tos],
|
||||
sizeof(int)*(nIdx+1), (char*)aIdx);
|
||||
sqliteFree(aIdx);
|
||||
}
|
||||
@ -2139,20 +2164,24 @@ int sqliteVdbeExec(
|
||||
|
||||
/* Opcode: DeleteIdx P1 * *
|
||||
**
|
||||
** The top of the stack is a key and next on stack is an index value.
|
||||
** Locate the record
|
||||
** in index P1 that has the key and remove the index value from its
|
||||
** data. Write the results back to the table. If after removing
|
||||
** the index value no more indices remain in the record, then the
|
||||
** record is removed from the table.
|
||||
** The top of the stack is a key and next on stack is integer
|
||||
** which is the key to a record in an SQL table.
|
||||
** Locate the record in the cursor P1 (P1 represents an SQL index)
|
||||
** that has the same key as the top of stack. Then look through
|
||||
** the integer table-keys contained in the data of the P1 record.
|
||||
** Remove the integer table-key that matches the NOS and write the
|
||||
** revised data back to P1 with the same key.
|
||||
**
|
||||
** If this routine removes the very last integer table-key from
|
||||
** the P1 data, then the corresponding P1 record is deleted.
|
||||
*/
|
||||
case OP_DeleteIdx: {
|
||||
int i = pOp->p1;
|
||||
int tos = p->tos;
|
||||
int nos = tos - 1;
|
||||
DbbeTable *pTab;
|
||||
DbbeCursor *pCrsr;
|
||||
if( nos<0 ) goto not_enough_stack;
|
||||
if( i>=0 && i<p->nTable && (pTab = p->aTab[i].pTable)!=0 ){
|
||||
if( i>=0 && i<p->nCursor && (pCrsr = p->aCsr[i].pCursor)!=0 ){
|
||||
int *aIdx;
|
||||
int nIdx;
|
||||
int j;
|
||||
@ -2161,17 +2190,17 @@ int sqliteVdbeExec(
|
||||
Integerify(p, nos);
|
||||
oldVal = p->aStack[nos].i;
|
||||
if( Stringify(p, tos) ) goto no_mem;
|
||||
r = sqliteDbbeFetch(pTab, p->aStack[tos].n, p->zStack[tos]);
|
||||
r = sqliteDbbeFetch(pCrsr, p->aStack[tos].n, p->zStack[tos]);
|
||||
if( r==0 ) break;
|
||||
nIdx = sqliteDbbeDataLength(pTab)/sizeof(int);
|
||||
aIdx = (int*)sqliteDbbeReadData(pTab, 0);
|
||||
nIdx = sqliteDbbeDataLength(pCrsr)/sizeof(int);
|
||||
aIdx = (int*)sqliteDbbeReadData(pCrsr, 0);
|
||||
for(j=0; j<nIdx && aIdx[j]!=oldVal; j++){}
|
||||
if( j>=nIdx ) break;
|
||||
aIdx[j] = aIdx[nIdx-1];
|
||||
if( nIdx==1 ){
|
||||
sqliteDbbeDelete(pTab, p->aStack[tos].n, p->zStack[tos]);
|
||||
sqliteDbbeDelete(pCrsr, p->aStack[tos].n, p->zStack[tos]);
|
||||
}else{
|
||||
sqliteDbbePut(pTab, p->aStack[tos].n, p->zStack[tos],
|
||||
sqliteDbbePut(pCrsr, p->aStack[tos].n, p->zStack[tos],
|
||||
sizeof(int)*(nIdx-1), (char*)aIdx);
|
||||
}
|
||||
}
|
||||
@ -2181,8 +2210,9 @@ int sqliteVdbeExec(
|
||||
|
||||
/* Opcode: Destroy * * P3
|
||||
**
|
||||
** Drop the table whose name is P3. The file that holds this table
|
||||
** is removed from the disk drive.
|
||||
** Drop the disk file whose name is P3. All key/data pairs in
|
||||
** the file are deleted and the file itself is removed
|
||||
** from the disk.
|
||||
*/
|
||||
case OP_Destroy: {
|
||||
sqliteDbbeDropTable(p->pBe, pOp->p3);
|
||||
@ -2200,7 +2230,7 @@ int sqliteVdbeExec(
|
||||
|
||||
/* Opcode: ListOpen P1 * *
|
||||
**
|
||||
** Open a file used for temporary storage of index numbers. P1
|
||||
** Open a file used for temporary storage of integer table keys. P1
|
||||
** will server as a handle to this temporary file for future
|
||||
** interactions. If another temporary file with the P1 handle is
|
||||
** already opened, the prior file is closed and a new one opened
|
||||
@ -2260,7 +2290,7 @@ int sqliteVdbeExec(
|
||||
/* Opcode: ListRead P1 P2 *
|
||||
**
|
||||
** Attempt to read an integer from temporary storage buffer P1
|
||||
** and push it onto the stack. If the storage buffer is empty
|
||||
** and push it onto the stack. If the storage buffer is empty,
|
||||
** push nothing but instead jump to P2.
|
||||
*/
|
||||
case OP_ListRead: {
|
||||
@ -2693,7 +2723,7 @@ int sqliteVdbeExec(
|
||||
/* Opcode: FileField P1 * *
|
||||
**
|
||||
** Push onto the stack the P1-th field of the most recently read line
|
||||
** from the file.
|
||||
** from the input file.
|
||||
*/
|
||||
case OP_FileField: {
|
||||
int i = pOp->p1;
|
||||
@ -2828,8 +2858,8 @@ int sqliteVdbeExec(
|
||||
|
||||
/* Opcode: AggIncr P1 P2 *
|
||||
**
|
||||
** Increment the P2-th field of the aggregate element current
|
||||
** in focus by an amount P1.
|
||||
** Increase the integer value in the P2-th field of the aggregate
|
||||
** element current in focus by an amount P1.
|
||||
*/
|
||||
case OP_AggIncr: {
|
||||
AggElem *pFocus = AggInFocus(p->agg);
|
||||
|
52
src/where.c
52
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.7 2000/06/06 13:54:16 drh Exp $
|
||||
** $Id: where.c,v 1.8 2000/06/21 13:59:13 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -38,10 +38,10 @@ typedef struct ExprInfo ExprInfo;
|
||||
struct ExprInfo {
|
||||
Expr *p; /* Pointer to the subexpression */
|
||||
int indexable; /* True if this subexprssion is usable by an index */
|
||||
int idxLeft; /* p->pLeft is a field in this table number. -1 if
|
||||
** p->pLeft is not the field of any table */
|
||||
int idxRight; /* p->pRight is a field in this table number. -1 if
|
||||
** p->pRight is not the field of any table */
|
||||
int idxLeft; /* p->pLeft is a column in this table number. -1 if
|
||||
** p->pLeft is not the column of any table */
|
||||
int idxRight; /* p->pRight is a column in this table number. -1 if
|
||||
** p->pRight is not the column of any table */
|
||||
unsigned prereqLeft; /* Tables referenced by p->pLeft */
|
||||
unsigned prereqRight; /* Tables referenced by p->pRight */
|
||||
};
|
||||
@ -94,7 +94,7 @@ static int exprSplit(int nSlot, ExprInfo *aSlot, Expr *pExpr){
|
||||
static int exprTableUsage(int base, Expr *p){
|
||||
unsigned int mask = 0;
|
||||
if( p==0 ) return 0;
|
||||
if( p->op==TK_FIELD ){
|
||||
if( p->op==TK_COLUMN ){
|
||||
return 1<< (p->iTable - base);
|
||||
}
|
||||
if( p->pRight ){
|
||||
@ -124,11 +124,11 @@ static void exprAnalyze(int base, ExprInfo *pInfo){
|
||||
pInfo->idxLeft = -1;
|
||||
pInfo->idxRight = -1;
|
||||
if( pExpr->op==TK_EQ && (pInfo->prereqRight & pInfo->prereqLeft)==0 ){
|
||||
if( pExpr->pRight->op==TK_FIELD ){
|
||||
if( pExpr->pRight->op==TK_COLUMN ){
|
||||
pInfo->idxRight = pExpr->pRight->iTable - base;
|
||||
pInfo->indexable = 1;
|
||||
}
|
||||
if( pExpr->pLeft->op==TK_FIELD ){
|
||||
if( pExpr->pLeft->op==TK_COLUMN ){
|
||||
pInfo->idxLeft = pExpr->pLeft->iTable - base;
|
||||
pInfo->indexable = 1;
|
||||
}
|
||||
@ -225,41 +225,41 @@ WhereInfo *sqliteWhereBegin(
|
||||
** the most specific usable index.
|
||||
**
|
||||
** "Most specific" means that pBestIdx is the usable index that
|
||||
** has the largest value for nField. A usable index is one for
|
||||
** which there are subexpressions to compute every field of the
|
||||
** has the largest value for nColumn. A usable index is one for
|
||||
** which there are subexpressions to compute every column of the
|
||||
** index.
|
||||
*/
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
int j;
|
||||
int fieldMask = 0;
|
||||
int columnMask = 0;
|
||||
|
||||
if( pIdx->nField>32 ) continue;
|
||||
if( pIdx->nColumn>32 ) continue;
|
||||
for(j=0; j<nExpr; j++){
|
||||
if( aExpr[j].idxLeft==idx
|
||||
&& (aExpr[j].prereqRight & loopMask)==aExpr[j].prereqRight ){
|
||||
int iField = aExpr[j].p->pLeft->iField;
|
||||
int iColumn = aExpr[j].p->pLeft->iColumn;
|
||||
int k;
|
||||
for(k=0; k<pIdx->nField; k++){
|
||||
if( pIdx->aiField[k]==iField ){
|
||||
fieldMask |= 1<<k;
|
||||
for(k=0; k<pIdx->nColumn; k++){
|
||||
if( pIdx->aiColumn[k]==iColumn ){
|
||||
columnMask |= 1<<k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( aExpr[j].idxRight==idx
|
||||
&& (aExpr[j].prereqLeft & loopMask)==aExpr[j].prereqLeft ){
|
||||
int iField = aExpr[j].p->pRight->iField;
|
||||
int iColumn = aExpr[j].p->pRight->iColumn;
|
||||
int k;
|
||||
for(k=0; k<pIdx->nField; k++){
|
||||
if( pIdx->aiField[k]==iField ){
|
||||
fieldMask |= 1<<k;
|
||||
for(k=0; k<pIdx->nColumn; k++){
|
||||
if( pIdx->aiColumn[k]==iColumn ){
|
||||
columnMask |= 1<<k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if( fieldMask + 1 == (1<<pIdx->nField) ){
|
||||
if( pBestIdx==0 || pBestIdx->nField<pIdx->nField ){
|
||||
if( columnMask + 1 == (1<<pIdx->nColumn) ){
|
||||
if( pBestIdx==0 || pBestIdx->nColumn<pIdx->nColumn ){
|
||||
pBestIdx = pIdx;
|
||||
}
|
||||
}
|
||||
@ -297,12 +297,12 @@ WhereInfo *sqliteWhereBegin(
|
||||
}else{
|
||||
/* Case 2: We do have a usable index in pIdx.
|
||||
*/
|
||||
for(j=0; j<pIdx->nField; j++){
|
||||
for(j=0; j<pIdx->nColumn; j++){
|
||||
for(k=0; k<nExpr; k++){
|
||||
if( aExpr[k].p==0 ) continue;
|
||||
if( aExpr[k].idxLeft==idx
|
||||
&& (aExpr[k].prereqRight & loopMask)==aExpr[k].prereqRight
|
||||
&& aExpr[k].p->pLeft->iField==pIdx->aiField[j]
|
||||
&& aExpr[k].p->pLeft->iColumn==pIdx->aiColumn[j]
|
||||
){
|
||||
sqliteExprCode(pParse, aExpr[k].p->pRight);
|
||||
aExpr[k].p = 0;
|
||||
@ -310,7 +310,7 @@ WhereInfo *sqliteWhereBegin(
|
||||
}
|
||||
if( aExpr[k].idxRight==idx
|
||||
&& (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft
|
||||
&& aExpr[k].p->pRight->iField==pIdx->aiField[j]
|
||||
&& aExpr[k].p->pRight->iColumn==pIdx->aiColumn[j]
|
||||
){
|
||||
sqliteExprCode(pParse, aExpr[k].p->pLeft);
|
||||
aExpr[k].p = 0;
|
||||
@ -318,7 +318,7 @@ WhereInfo *sqliteWhereBegin(
|
||||
}
|
||||
}
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nColumn, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Fetch, base+pTabList->nId+i, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_NextIdx, base+pTabList->nId+i, brk, 0, cont);
|
||||
if( i==pTabList->nId-1 && pushKey ){
|
||||
|
@ -23,7 +23,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the DELETE FROM statement.
|
||||
#
|
||||
# $Id: delete.test,v 1.5 2000/06/08 15:10:48 drh Exp $
|
||||
# $Id: delete.test,v 1.6 2000/06/21 13:59:13 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@ -72,7 +72,7 @@ do_test delete-4.1 {
|
||||
execsql {CREATE TABLE table2(f1 int, f2 int)}
|
||||
set v [catch {execsql {DELETE FROM table2 WHERE f3=5}} msg]
|
||||
lappend v $msg
|
||||
} {1 {no such field: f3}}
|
||||
} {1 {no such column: f3}}
|
||||
|
||||
do_test delete-4.2 {
|
||||
set v [catch {execsql {DELETE FROM table2 WHERE xyzzy(f1+4)}} msg]
|
||||
|
@ -23,7 +23,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the IN and BETWEEN operator.
|
||||
#
|
||||
# $Id: in.test,v 1.2 2000/06/07 23:51:51 drh Exp $
|
||||
# $Id: in.test,v 1.3 2000/06/21 13:59:13 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@ -106,7 +106,7 @@ do_test in-2.10 {
|
||||
do_test in-2.11 {
|
||||
set v [catch {execsql {SELECT a FROM t1 WHERE c IN (10,20)}} msg]
|
||||
lappend v $msg
|
||||
} {1 {no such field: c}}
|
||||
} {1 {no such column: c}}
|
||||
|
||||
# Testing the IN operator where the right-hand side is a SELECT
|
||||
#
|
||||
|
@ -23,7 +23,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the SELECT statement.
|
||||
#
|
||||
# $Id: select1.test,v 1.5 2000/06/07 15:11:27 drh Exp $
|
||||
# $Id: select1.test,v 1.6 2000/06/21 13:59:13 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@ -49,7 +49,7 @@ do_test select1-1.3 {
|
||||
execsql {INSERT INTO test1(f1,f2) VALUES(11,22)}
|
||||
|
||||
|
||||
# Make sure the fields are extracted correctly.
|
||||
# Make sure the columns are extracted correctly.
|
||||
#
|
||||
do_test select1-1.4 {
|
||||
execsql {SELECT f1 FROM test1}
|
||||
@ -244,7 +244,7 @@ do_test select1-5.1 {
|
||||
execsql {CREATE TABLE test2(t1 test, t2 text)}
|
||||
execsql {INSERT INTO test2 VALUES('abc','xyz')}
|
||||
|
||||
# Check for field naming
|
||||
# Check for column naming
|
||||
#
|
||||
do_test select1-6.1 {
|
||||
set v [catch {execsql2 {SELECT f1 FROM test1 ORDER BY f2}} msg]
|
||||
@ -280,17 +280,17 @@ do_test select1-6.8 {
|
||||
set v [catch {execsql2 {SELECT A.f1, f1 FROM test1 as A, test1 as B
|
||||
ORDER BY f2}} msg]
|
||||
lappend v $msg
|
||||
} {1 {ambiguous field name: f1}}
|
||||
} {1 {ambiguous column name: f1}}
|
||||
do_test select1-6.8b {
|
||||
set v [catch {execsql2 {SELECT A.f1, B.f1 FROM test1 as A, test1 as B
|
||||
ORDER BY f2}} msg]
|
||||
lappend v $msg
|
||||
} {1 {ambiguous field name: f2}}
|
||||
} {1 {ambiguous column name: f2}}
|
||||
do_test select1-6.8c {
|
||||
set v [catch {execsql2 {SELECT A.f1, f1 FROM test1 as A, test1 as A
|
||||
ORDER BY f2}} msg]
|
||||
lappend v $msg
|
||||
} {1 {ambiguous field name: A.f1}}
|
||||
} {1 {ambiguous column name: A.f1}}
|
||||
do_test select1-6.9 {
|
||||
set v [catch {execsql2 {SELECT A.f1, B.f1 FROM test1 as A, test1 as B
|
||||
ORDER BY A.f1, B.f1}} msg]
|
||||
|
@ -23,7 +23,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the SELECT statement.
|
||||
#
|
||||
# $Id: select2.test,v 1.6 2000/06/08 16:54:40 drh Exp $
|
||||
# $Id: select2.test,v 1.7 2000/06/21 13:59:13 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@ -106,7 +106,7 @@ do_test select2-3.2c {
|
||||
do_test select2-3.2d {
|
||||
set t1 [lindex [time {execsql {SELECT f1 FROM tbl2 WHERE 1000=f2}} 1] 0]
|
||||
set t2 [lindex [time {execsql {SELECT f1 FROM tbl2 WHERE f2=1000}} 1] 0]
|
||||
expr {$t1*0.9<$t2 && $t2*0.9<$t1}
|
||||
expr {$t1*0.8<$t2 && $t2*0.8<$t1}
|
||||
} {1}
|
||||
|
||||
# Make sure queries run faster with an index than without
|
||||
|
@ -24,7 +24,7 @@
|
||||
# focus of this file is testing aggregate functions and the
|
||||
# GROUP BY and HAVING clauses of SELECT statements.
|
||||
#
|
||||
# $Id: select5.test,v 1.3 2000/06/08 16:26:25 drh Exp $
|
||||
# $Id: select5.test,v 1.4 2000/06/21 13:59:13 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@ -66,7 +66,7 @@ do_test select5-2.1 {
|
||||
SELECT y, count(*) FROM t1 GROUP BY z ORDER BY y
|
||||
}} msg]
|
||||
lappend v $msg
|
||||
} {1 {no such field: z}}
|
||||
} {1 {no such column: z}}
|
||||
do_test select5-2.2 {
|
||||
set v [catch {execsql {
|
||||
SELECT y, count(*) FROM t1 GROUP BY z(y) ORDER BY y
|
||||
@ -90,7 +90,7 @@ do_test select5-2.5 {
|
||||
SELECT y, count(*) FROM t1 GROUP BY y HAVING count(*)<z ORDER BY y
|
||||
}} msg]
|
||||
lappend v $msg
|
||||
} {1 {no such field: z}}
|
||||
} {1 {no such column: z}}
|
||||
|
||||
# Get the Agg function to rehash in vdbe.c
|
||||
#
|
||||
|
@ -23,7 +23,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the UPDATE statement.
|
||||
#
|
||||
# $Id: update.test,v 1.3 2000/06/19 19:09:09 drh Exp $
|
||||
# $Id: update.test,v 1.4 2000/06/21 13:59:14 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@ -54,20 +54,20 @@ do_test update-3.1 {
|
||||
execsql {SELECT * FROM test1 ORDER BY f1}
|
||||
} {1 2 2 4 3 8 4 16 5 32 6 64 7 128 8 256 9 512 10 1024}
|
||||
|
||||
# Unknown field name in an expression
|
||||
# Unknown column name in an expression
|
||||
#
|
||||
do_test update-3.2 {
|
||||
set v [catch {execsql {UPDATE test1 SET f1=f3*2 WHERE f2==32}} msg]
|
||||
lappend v $msg
|
||||
} {1 {no such field: f3}}
|
||||
} {1 {no such column: f3}}
|
||||
do_test update-3.3 {
|
||||
set v [catch {execsql {UPDATE test1 SET f1=test2.f1*2 WHERE f2==32}} msg]
|
||||
lappend v $msg
|
||||
} {1 {no such field: test2.f1}}
|
||||
} {1 {no such column: test2.f1}}
|
||||
do_test update-3.4 {
|
||||
set v [catch {execsql {UPDATE test1 SET f3=f1*2 WHERE f2==32}} msg]
|
||||
lappend v $msg
|
||||
} {1 {no such field: f3}}
|
||||
} {1 {no such column: f3}}
|
||||
|
||||
# Actually do some updates
|
||||
#
|
||||
@ -117,7 +117,7 @@ do_test update-4.1 {
|
||||
UPDATE test1 SET x=11 WHERE f1=1025
|
||||
}} msg]
|
||||
lappend v $msg
|
||||
} {1 {no such field: x}}
|
||||
} {1 {no such column: x}}
|
||||
do_test update-4.2 {
|
||||
set v [catch {execsql {
|
||||
UPDATE test1 SET f1=x(11) WHERE f1=1025
|
||||
@ -129,7 +129,7 @@ do_test update-4.3 {
|
||||
UPDATE test1 SET f1=11 WHERE x=1025
|
||||
}} msg]
|
||||
lappend v $msg
|
||||
} {1 {no such field: x}}
|
||||
} {1 {no such column: x}}
|
||||
do_test update-4.4 {
|
||||
set v [catch {execsql {
|
||||
UPDATE test1 SET f1=11 WHERE x(f1)=1025
|
||||
|
@ -1,7 +1,7 @@
|
||||
#
|
||||
# Run this Tcl script to generate the sqlite.html file.
|
||||
#
|
||||
set rcsid {$Id: c_interface.tcl,v 1.4 2000/06/06 18:24:42 drh Exp $}
|
||||
set rcsid {$Id: c_interface.tcl,v 1.5 2000/06/21 13:59:14 drh Exp $}
|
||||
|
||||
puts {<html>
|
||||
<head>
|
||||
@ -88,7 +88,7 @@ for any reason.</p>
|
||||
|
||||
<h2>Closing the database</h2>
|
||||
|
||||
<p>To close an SQLite database, just call the <b>sqlite_close()</b>
|
||||
<p>To close an SQLite database, call the <b>sqlite_close()</b>
|
||||
function passing it the sqlite structure pointer that was obtained
|
||||
from a prior call to <b>sqlite_open</b>.
|
||||
|
||||
@ -147,6 +147,62 @@ argv[i] == 0
|
||||
function returns non-zero, the query is immediately aborted and
|
||||
<b>sqlite_exec()</b> will return SQLITE_ABORT.</p>
|
||||
|
||||
<p>The <b>sqlite_exec()</b> function returns an integer to indicate
|
||||
success or failure of the operation. The following are possible
|
||||
return values:</p>
|
||||
|
||||
<blockquote>
|
||||
<dl>
|
||||
<dt>SQLITE_OK</dt>
|
||||
<dd><p>This value is returned if everything worked and there were no errors.
|
||||
</p></dd>
|
||||
<dt>SQLITE_INTERNAL</dt>
|
||||
<dd><p>This value indicates that an internal consistency check within
|
||||
the SQLite library failed. This can only happen if there is a bug in
|
||||
the SQLite library. If you ever get an SQLITE_INTERNAL reply from
|
||||
an <b>sqlite_exec()</b> call, please report the problem on the SQLite
|
||||
mailing list.
|
||||
</p></dd>
|
||||
<dt>SQLITE_ERROR</dt>
|
||||
<dd><p>This return value indicates that there was an error in the SQL
|
||||
that was passed into the <b>sqlite_exec()</b>.
|
||||
</p></dd>
|
||||
<dt>SQLITE_PERM</dt>
|
||||
<dd><p>This return value says that the access permissions on one of the
|
||||
GDBM files is such that the file cannot be opened.
|
||||
</p></dd>
|
||||
<dt>SQLITE_ABORT</dt>
|
||||
<dd><p>This value is returned if the callback function returns non-zero.
|
||||
</p></dd>
|
||||
<dt>SQLITE_BUSY</dt>
|
||||
<dd><p>This return code indicates that one of the underlying GDBM files
|
||||
is locked because it is currently being accessed by another thread or
|
||||
process. GDBM allows mutiple readers of the same file, but only one
|
||||
writer. So multiple processes can query an SQLite database at once.
|
||||
But only a single process can write to an SQLite database at one time.
|
||||
If an attempt is made to write to an SQLite database that another
|
||||
process is currently reading, the write is not performed and
|
||||
<b>sqlite_exec()</b> returns SQLITE_BUSY. Similarly, an attempt to read
|
||||
an SQLite database that is currently being written by another process
|
||||
will return SQLITE_BUSY. In both cases, the write or query attempt
|
||||
can be retried after the other process finishes.</p>
|
||||
<p>Note that locking is done at the file level. One process can
|
||||
write to table ABC (for example) while another process simultaneously
|
||||
reads from a different table XYZ. But you cannot have two processes reading
|
||||
and writing table ABC at the same time.
|
||||
</p></dd>
|
||||
<dt>SQLITE_NOMEM</dt>
|
||||
<dd><p>This value is returned if a call to <b>malloc()</b> fails.
|
||||
</p></dd>
|
||||
<dt>SQLITE_READONLY</dt>
|
||||
<dd><p>This return code indicates that an attempt was made to write to
|
||||
a database file that was originally opened for reading only. This can
|
||||
happen if the callback from a query attempts to update the table
|
||||
being queried.
|
||||
</p></dd>
|
||||
</dl>
|
||||
</blockquote>
|
||||
|
||||
<h2>Testing for a complete SQL statement</h2>
|
||||
|
||||
<p>The last interface routine to SQLite is a convenience function used
|
||||
|
@ -1,7 +1,7 @@
|
||||
#
|
||||
# Run this Tcl script to generate the sqlite.html file.
|
||||
#
|
||||
set rcsid {$Id: sqlite.tcl,v 1.8 2000/06/08 19:43:40 drh Exp $}
|
||||
set rcsid {$Id: sqlite.tcl,v 1.9 2000/06/21 13:59:14 drh Exp $}
|
||||
|
||||
puts {<html>
|
||||
<head>
|
||||
@ -150,6 +150,7 @@ sqlite> (((.help)))
|
||||
.help Show this message
|
||||
.indices TABLE Show names of all indices on TABLE
|
||||
.mode MODE Set mode to one of "line", "column", "list", or "html"
|
||||
.mode insert TABLE Generate SQL insert statements for TABLE
|
||||
.output FILENAME Send output to FILENAME
|
||||
.output stdout Send output to the screen
|
||||
.schema ?TABLE? Show the CREATE statements
|
||||
@ -163,13 +164,13 @@ puts {
|
||||
<h2>Changing Output Formats</h2>
|
||||
|
||||
<p>The sqlite program is able to show the results of a query
|
||||
in four different formats: "line", "column", "list", and "html".
|
||||
in five different formats: "line", "column", "list", "html", and "insert".
|
||||
You can use the ".mode" dot command to switch between these three output
|
||||
formats.</p>
|
||||
|
||||
<p>The default output mode is "list". In
|
||||
list mode, each record of a query result is written on one line of
|
||||
output and each field within that record is separated by a specific
|
||||
output and each column within that record is separated by a specific
|
||||
separator string. The default separator is a pipe symbol ("|").
|
||||
List mode is especially useful when you are going to send the output
|
||||
of a query to another program (such as AWK) for additional processing.</p>}
|
||||
@ -196,9 +197,9 @@ sqlite>
|
||||
}
|
||||
|
||||
puts {
|
||||
<p>In "line" mode, each field in a record of the database
|
||||
is shown on a line by itself. Each line consists of the field
|
||||
name, an equal sign and the field data. Successive records are
|
||||
<p>In "line" mode, each column in a row of the database
|
||||
is shown on a line by itself. Each line consists of the column
|
||||
name, an equal sign and the column data. Successive records are
|
||||
separated by a blank line. Here is an example of line mode
|
||||
output:</p>}
|
||||
|
||||
@ -263,6 +264,11 @@ sqlite>
|
||||
}
|
||||
|
||||
puts {
|
||||
<p>Another useful output mode is "insert". In insert mode, the output
|
||||
is formatted to look like SQL INSERT statements. You can use insert
|
||||
mode to generate text that can later be used to input data into a
|
||||
different database.</p>
|
||||
|
||||
<p>The last output mode is "html". In this mode, sqlite writes
|
||||
the results of the query as an XHTML table. The beginning
|
||||
<TABLE> and the ending </TABLE> are not written, but
|
||||
|
Reference in New Issue
Block a user