diff --git a/manifest b/manifest index 84223197e9..4837e0f490 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Added\ssupport\sfor\sthe\sINTEGER\sPRIMARY\sKEY\scolumn\stype.\s(CVS\s333) -D 2001-12-21T14:30:43 +C Bug\sfixing\sin\sthe\snew\sinteger\sprimary\skey\scode.\s(CVS\s334) +D 2001-12-22T14:49:25 F Makefile.in 352fed589f09dd94347e0bb391d047118ebd6105 F Makefile.template 0fbf0ee1fe38183d760170a13e91fffec64e73f5 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0 @@ -21,25 +21,25 @@ F publish.sh cb0f8f7bcb65b8360d0f6668a216a9ac9d5da892 F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6 F src/btree.c c3c36b3b5f07c3efdabf76df9ea423086b1ce142 F src/btree.h 8767bd4ecf841c4999b7aee6876906bd607546e7 -F src/build.c 36b3bf95bb2f0bdf1d19436b8af5125997c95735 +F src/build.c 6c01002e98204ad2b993d0d043ee56c8c7dc8692 F src/delete.c f7690efc09ad6a2f1f3f0490e1b0cbb676bb95cf F src/expr.c ef1c365c5d558fa691878830501d3c36ed7edb25 F src/hash.c 6f1a7712ae3aac8351662969aec5693740a2fbf7 F src/hash.h a5f5b3ce2d086a172c5879b0b06a27a82eac9fac -F src/insert.c 18353ee08ee29241e147187c63ade3669b0af007 +F src/insert.c 8119b42a4858bc4401bd5dfae8e9f28b8205871d F src/main.c 00a9f5603e130fc0b1a05f731731c9c99ebdc2dc F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c F src/os.c 07882cde5c61f26751b8ee76fd84726c1f7e453c F src/os.h 00a18e0ae1139a64f1d3ead465ae2b9ff43f3db2 F src/pager.c dde0eb5bf9af0ac0ff8a4429b2bee2aec2194ec9 F src/pager.h f78d064c780855ff70beacbeba0e2324471b26fe -F src/parse.y c62f32e332c291612a3a2e856ab48b636c82ee03 +F src/parse.y f050644e7a2586227686e8c1709aa2662b9bcf9c F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d F src/random.c 2a9cc2c9716d14815fd4c2accf89d87a1143e46b -F src/select.c 76a8fafb29935865ddbef263ee90f1398d950d8b +F src/select.c bb7bf8d6e6154269145158952e041755cc4d9873 F src/shell.c 407095aaeeae78f42deb3e846b1ad77f8ed3b4ef F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e -F src/sqlite.h.in 934de9112747ad8d8e7d5fec44876246b24ca5a3 +F src/sqlite.h.in a4c11d38d62b1bfbd50a5804edee8ca54c1adc9b F src/sqliteInt.h 0b1e8ba2738440e2f06a4e01bb89230492bc203b F src/table.c c89698bd5bb4b8d14722d6ee7e9be014c383d24a F src/tclsqlite.c b82e4faeae89fdb7304b3c970979ade299336a1f @@ -47,11 +47,11 @@ F src/test1.c 41eabe255970ef947263b94145c9b2766bab8675 F src/test2.c e9f99aa5ee73872819259d6612c11e55e1644321 F src/test3.c d6775f95fd91f5b3cf0e2382a28e5aaeb68f745b F src/tokenize.c 830e9ef684334070a26583d94770bb869e2727bf -F src/update.c 9c266e5c9d1beba74475fd2fb8078dc3d5b23182 -F src/util.c 13dcd870ee0e424f5427e8178480ca1b1833a706 -F src/vdbe.c 49227b52911dcc6811d5c71d36024172feb22195 -F src/vdbe.h cd4c8647051a0c22c0e133c375f1cd17bb8b1e06 -F src/where.c 05d27a01e53c20b8cd10589b7e789b2a64367988 +F src/update.c 6a77d1459d2e829fd9dd244ce8b5ada7ac485317 +F src/util.c 8e9ca72d8288cae39c57c6f397abd14a56b14a38 +F src/vdbe.c f97e2d5bc6db936a2d001e0a1a94102e99ece821 +F src/vdbe.h e5cc6fb13d1905a4339db4d6dba4ab393c0765fa +F src/where.c 178a908a40cc6d72150a747db69638a97bd86487 F test/all.test 2a51e5395ac7c2c539689b123b9782a05e3837fe F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578 F test/btree.test 6ab4dc5f595905a276ef588fad3c9236dc07a47b @@ -60,12 +60,12 @@ F test/btree3.test 9caa9e22491dd8cd8aa36d7ac3b48b089817c895 F test/copy.test 768e6f1701a07d08090e1ca7f7dcce0a7a72b43e F test/delete.test c904a62129fe102b314a96111a8417f10249e4d8 F test/expr.test d350ef5b21cc26599357fb93d15b8a5f7b524769 -F test/func.test 9012f7fc5369422c890e93549aa61d762e0c8bb3 +F test/func.test 51dbe3f8a4c28972751697423e6acc5d6b551df1 F test/in.test 9323681388be301dc73f370b4cd62c5a33f79d1e F test/index.test c8a471243bbf878974b99baf5badd59407237cf3 F test/insert.test a5c122aa726f1cef6f07d6767e8fd6f220994c11 F test/insert2.test d6901ca931e308fea7fca8c95ebe7dc957cc9fc2 -F test/intpkey.test 79be8360e6f0a5506b513f3ab4399da797cd8b3e +F test/intpkey.test 1f3b36bcf772597809fb445202af77d1d17bb39f F test/ioerr.test 57d9bffaca18b34f9e976f786eadc2591d6efc6a F test/limit.test a930f3eba2a7691c8397ccab33710b931589566a F test/lock.test 19593689260c419efe7ced55b1418653a4b7bcd1 @@ -93,7 +93,7 @@ F test/trans.test 855337b8a178c73c433fcf8ee88e4b2f5efff0d9 F test/unique.test 07776624b82221a80c8b4138ce0dd8b0853bb3ea F test/update.test 3cf1ca0565f678063c2dfa9a7948d2d66ae1a778 F test/vacuum.test 8acf8669f3b627e54149b25165b034aa06c2432e -F test/where.test 20b19475fe894b86b06d2979592260dd16beeb17 +F test/where.test 032d581c3de4893eba33b569e581c46b941bb02a F tool/lemon.c bfd036ab9309c7f34e1357d9a065ad137814e741 F tool/lempar.c 9b604e6a8b3d55c0b9cbcb130a7302fb8bafe2b9 F tool/memleak.awk 296dfbce7a9ca499b95ce04e30334e64a50052e0 @@ -118,7 +118,7 @@ F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279 F www/tclsqlite.tcl 880ef67cb4f2797b95bf1368fc4e0d8ca0fda956 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 -P ffbdd43f5de62e7bf81631c83473aca29c3a6c98 -R 56465183b3eaba81097d58ab4fff365b +P 236a54d289e858a1e0505a20d907a2a40c01b521 +R 5707cbf46f7d3e66441cb3379d943ba9 U drh -Z 8ca8d028861bf23d8008c82aec39ab1e +Z 49b0682a1be06cb764aeb1e921e472ac diff --git a/manifest.uuid b/manifest.uuid index 09d004ee09..dc823da4a4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -236a54d289e858a1e0505a20d907a2a40c01b521 \ No newline at end of file +29cab124b4f7eae9d9feb60d2f3a2c443fd9b9aa \ No newline at end of file diff --git a/src/build.c b/src/build.c index 1d0654b1ba..32e7e5fa42 100644 --- a/src/build.c +++ b/src/build.c @@ -25,7 +25,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.60 2001/12/21 14:30:43 drh Exp $ +** $Id: build.c,v 1.61 2001/12/22 14:49:25 drh Exp $ */ #include "sqliteInt.h" #include @@ -1314,12 +1314,23 @@ void sqliteCopy( }else{ sqliteVdbeChangeP3(v, addr, "\t", 1); } - sqliteVdbeAddOp(v, OP_NewRecno, 0, 0); + if( pTab->iPKey>=0 ){ + sqliteVdbeAddOp(v, OP_FileColumn, pTab->iPKey, 0); + sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0); + }else{ + sqliteVdbeAddOp(v, OP_NewRecno, 0, 0); + } if( pTab->pIndex ){ sqliteVdbeAddOp(v, OP_Dup, 0, 0); } for(i=0; inCol; i++){ - sqliteVdbeAddOp(v, OP_FileColumn, i, 0); + if( i==pTab->iPKey ){ + /* The integer primary key column is filled with NULL since its + ** value is always pulled from the record number */ + sqliteVdbeAddOp(v, OP_String, 0, 0); + }else{ + sqliteVdbeAddOp(v, OP_FileColumn, i, 0); + } } sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); sqliteVdbeAddOp(v, OP_Put, 0, 0); diff --git a/src/insert.c b/src/insert.c index d51a5791f6..f8cb25451b 100644 --- a/src/insert.c +++ b/src/insert.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** -** $Id: insert.c,v 1.27 2001/12/21 14:30:43 drh Exp $ +** $Id: insert.c,v 1.28 2001/12/22 14:49:25 drh Exp $ */ #include "sqliteInt.h" @@ -200,7 +200,7 @@ void sqliteInsert( }else{ sqliteExprCode(pParse, pList->a[keyColumn].pExpr); } - sqliteVdbeAddOp(v, OP_AddImm, 0, 0); /* Make sure ROWID is an integer */ + sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0); }else{ sqliteVdbeAddOp(v, OP_NewRecno, base, 0); } diff --git a/src/parse.y b/src/parse.y index 525ed4d3fe..c21b7c8daa 100644 --- a/src/parse.y +++ b/src/parse.y @@ -14,7 +14,7 @@ ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** -** @(#) $Id: parse.y,v 1.40 2001/12/21 14:30:43 drh Exp $ +** @(#) $Id: parse.y,v 1.41 2001/12/22 14:49:25 drh Exp $ */ %token_prefix TK_ %token_type {Token} @@ -515,13 +515,13 @@ idxlist(A) ::= idxitem(Y). {A = sqliteIdListAppend(0,&Y);} idxitem(A) ::= ids(X). {A = X;} -///////////////////////////// The CREATE INDEX command /////////////////////// +///////////////////////////// The DROP INDEX command ///////////////////////// // cmd ::= DROP INDEX ids(X). {sqliteDropIndex(pParse, &X);} -///////////////////////////// The DROP INDEX command ///////////////////////// +///////////////////////////// The COPY command /////////////////////////////// // cmd ::= COPY ids(X) FROM ids(Y) USING DELIMITERS STRING(Z). {sqliteCopy(pParse,&X,&Y,&Z);} diff --git a/src/select.c b/src/select.c index 2179cb5b4f..cc6e174bf6 100644 --- a/src/select.c +++ b/src/select.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements in SQLite. ** -** $Id: select.c,v 1.50 2001/12/16 20:05:06 drh Exp $ +** $Id: select.c,v 1.51 2001/12/22 14:49:25 drh Exp $ */ #include "sqliteInt.h" @@ -258,20 +258,22 @@ void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){ sqliteVdbeChangeP3(v, -1, p->span.z, p->span.n); sqliteVdbeCompressSpace(v, addr); }else if( p->op==TK_COLUMN && pTabList ){ + Table *pTab = pTabList->a[p->iTable - pParse->nTab].pTab; + int iCol = p->iColumn; + if( iCol<0 ) iCol = pTab->iPKey; + assert( iCol>=0 && iColnCol ); if( pTabList->nId>1 || showFullNames ){ char *zName = 0; - Table *pTab = pTabList->a[p->iTable - pParse->nTab].pTab; char *zTab; zTab = pTabList->a[p->iTable - pParse->nTab].zAlias; if( showFullNames || zTab==0 ) zTab = pTab->zName; - sqliteSetString(&zName, zTab, ".", pTab->aCol[p->iColumn].zName, 0); + sqliteSetString(&zName, zTab, ".", pTab->aCol[iCol].zName, 0); sqliteVdbeAddOp(v, OP_ColumnName, i, 0); sqliteVdbeChangeP3(v, -1, zName, strlen(zName)); sqliteFree(zName); }else{ - Table *pTab = pTabList->a[0].pTab; - char *zName = pTab->aCol[p->iColumn].zName; + char *zName = pTab->aCol[iCol].zName; sqliteVdbeAddOp(v, OP_ColumnName, i, 0); sqliteVdbeChangeP3(v, -1, zName, P3_STATIC); } diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 8a13902121..4e2601f06d 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -12,7 +12,7 @@ ** This header file defines the interface that the SQLite library ** presents to client programs. ** -** @(#) $Id: sqlite.h.in,v 1.23 2001/11/03 23:57:09 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.24 2001/12/22 14:49:25 drh Exp $ */ #ifndef _SQLITE_H_ #define _SQLITE_H_ @@ -160,6 +160,7 @@ int sqlite_exec( #define SQLITE_SCHEMA 17 /* The database schema changed */ #define SQLITE_TOOBIG 18 /* Too much data for one row of a table */ #define SQLITE_CONSTRAINT 19 /* Abort due to contraint violation */ +#define SQLITE_MISMATCH 20 /* Data type mismatch */ /* If the parameter to this routine is one of the return value constants ** defined above, then this routine returns a constant text string which diff --git a/src/update.c b/src/update.c index 4c2bbd96b3..7ea634e6c8 100644 --- a/src/update.c +++ b/src/update.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** -** $Id: update.c,v 1.23 2001/12/21 14:30:43 drh Exp $ +** $Id: update.c,v 1.24 2001/12/22 14:49:25 drh Exp $ */ #include "sqliteInt.h" @@ -215,7 +215,7 @@ void sqliteUpdate( if( chngRecno ){ sqliteVdbeAddOp(v, OP_Pop, 1, 0); sqliteExprCode(pParse, pRecnoExpr); - sqliteVdbeAddOp(v, OP_AddImm, 0, 0); + sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0); } /* Compute new data for this record. diff --git a/src/util.c b/src/util.c index 166660a1e1..90f9bb97a4 100644 --- a/src/util.c +++ b/src/util.c @@ -14,7 +14,7 @@ ** This file contains functions for allocating memory, comparing ** strings, and stuff like that. ** -** $Id: util.c,v 1.33 2001/11/24 00:31:46 drh Exp $ +** $Id: util.c,v 1.34 2001/12/22 14:49:25 drh Exp $ */ #include "sqliteInt.h" #include @@ -1070,6 +1070,7 @@ const char *sqlite_error_string(int rc){ case SQLITE_SCHEMA: z = "database schema has changed"; break; case SQLITE_TOOBIG: z = "too much data for one table row"; break; case SQLITE_CONSTRAINT: z = "constraint failed"; break; + case SQLITE_MISMATCH: z = "datatype mismatch"; break; default: z = "unknown error"; break; } return z; diff --git a/src/vdbe.c b/src/vdbe.c index 88cc6e9c37..c8c237609c 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -30,7 +30,7 @@ ** But other routines are also provided to help in building up ** a program instruction by instruction. ** -** $Id: vdbe.c,v 1.101 2001/12/21 14:30:43 drh Exp $ +** $Id: vdbe.c,v 1.102 2001/12/22 14:49:25 drh Exp $ */ #include "sqliteInt.h" #include @@ -709,6 +709,15 @@ static int isNumber(const char *zNum){ return *zNum==0; } +/* +** Return TRUE if zNum is an integer. +*/ +static int isInteger(const char *zNum){ + if( *zNum=='-' || *zNum=='+' ) zNum++; + while( isdigit(*zNum) ) zNum++; + return *zNum==0; +} + /* ** Delete a keylist */ @@ -860,15 +869,15 @@ static char *zOpName[] = { 0, "Goto", "If", "Halt", "ColumnCount", "ColumnName", "Callback", "NullCallback", "Integer", "String", "Pop", "Dup", "Pull", - "Add", "AddImm", "Subtract", "Multiply", - "Divide", "Remainder", "BitAnd", "BitOr", - "BitNot", "ShiftLeft", "ShiftRight", "AbsValue", - "Precision", "Min", "Max", "Like", - "Glob", "Eq", "Ne", "Lt", - "Le", "Gt", "Ge", "IsNull", - "NotNull", "Negative", "And", "Or", - "Not", "Concat", "Noop", "Strlen", - "Substr", "Limit", + "MustBeInt", "Add", "AddImm", "Subtract", + "Multiply", "Divide", "Remainder", "BitAnd", + "BitOr", "BitNot", "ShiftLeft", "ShiftRight", + "AbsValue", "Precision", "Min", "Max", + "Like", "Glob", "Eq", "Ne", + "Lt", "Le", "Gt", "Ge", + "IsNull", "NotNull", "Negative", "And", + "Or", "Not", "Concat", "Noop", + "Strlen", "Substr", "Limit", }; /* @@ -966,10 +975,21 @@ static Sorter *Merge(Sorter *pLeft, Sorter *pRight){ } /* -** Convert an integer into a big-endian integer. In other words, -** make sure the most significant byte comes first. +** Convert an integer in between the native integer format and +** the bigEndian format used as the record number for tables. +** +** The bigEndian format (most significant byte first) is used for +** record numbers so that records will sort into the correct order +** even though memcmp() is used to compare the keys. On machines +** whose native integer format is little endian (ex: i486) the +** order of bytes is reversed. On native big-endian machines +** (ex: Alpha, Sparc, Motorola) the byte order is the same. +** +** This function is its own inverse. In other words +** +** X == byteSwap(byteSwap(X)) */ -static int bigEndian(int x){ +static int byteSwap(int x){ union { char zBuf[sizeof(int)]; int i; @@ -981,6 +1001,15 @@ static int bigEndian(int x){ return ux.i; } +/* +** When converting from the native format to the key format and back +** again, in addition to changing the byte order we invert the high-order +** bit of the most significant byte. This causes negative numbers to +** sort before positive numbers in the memcmp() function. +*/ +#define keyToInt(X) (byteSwap(X) ^ 0x80000000) +#define intToKey(X) (byteSwap((X) ^ 0x80000000)) + /* ** Code contained within the VERIFY() macro is not needed for correct ** execution. It is there only to catch errors. So when we compile @@ -1667,6 +1696,47 @@ case OP_AddImm: { break; } +/* Opcode: MustBeInt * P2 * +** +** Force the top of the stack to be an integer. If the top of the +** stack is not an integer and cannot be comverted into an integer +** with out data loss, then jump immediately to P2, or if P2==0 +** raise an SQLITE_MISMATCH exception. +*/ +case OP_MustBeInt: { + int tos = p->tos; + VERIFY( if( tos<0 ) goto not_enough_stack; ) + if( aStack[tos].flags & STK_Int ){ + /* Do nothing */ + }else if( aStack[tos].flags & STK_Real ){ + int i = aStack[tos].r; + double r = i; + if( r!=aStack[tos].r ){ + goto mismatch; + } + aStack[tos].i = i; + }else if( aStack[tos].flags & STK_Str ){ + if( !isInteger(zStack[tos]) ){ + goto mismatch; + } + p->aStack[tos].i = atoi(p->zStack[tos]); + }else{ + goto mismatch; + } + Release(p, tos); + p->aStack[tos].flags = STK_Int; + break; + +mismatch: + if( pOp->p2==0 ){ + rc = SQLITE_MISMATCH; + goto abort_due_to_error; + }else{ + pc = pOp->p2 - 1; + } + break; +} + /* Opcode: Eq * P2 * ** ** Pop the top two elements from the stack. If they are equal, then @@ -2155,7 +2225,7 @@ case OP_MakeKey: { if( addRowid ){ u32 iKey; Integerify(p, p->tos-nField); - iKey = bigEndian(aStack[p->tos-nField].i); + iKey = intToKey(aStack[p->tos-nField].i); memcpy(&zNewKey[j], &iKey, sizeof(u32)); } if( pOp->p2==0 ) PopStack(p, nField+addRowid); @@ -2540,10 +2610,10 @@ case OP_MoveTo: { if( i>=0 && inCursor && (pC = &p->aCsr[i])->pCursor!=0 ){ int res; if( aStack[tos].flags & STK_Int ){ - int iKey = bigEndian(aStack[tos].i); + int iKey = intToKey(aStack[tos].i); sqliteBtreeMoveto(pC->pCursor, (char*)&iKey, sizeof(int), &res); pC->lastRecno = aStack[tos].i; - pC->recnoIsValid = 1; + pC->recnoIsValid = res==0; }else{ if( Stringify(p, tos) ) goto no_mem; sqliteBtreeMoveto(pC->pCursor, zStack[tos], aStack[tos].n, &res); @@ -2606,7 +2676,7 @@ case OP_Found: { if( VERIFY( i>=0 && inCursor && ) (pC = &p->aCsr[i])->pCursor!=0 ){ int res, rx; if( aStack[tos].flags & STK_Int ){ - int iKey = bigEndian(aStack[tos].i); + int iKey = intToKey(aStack[tos].i); rx = sqliteBtreeMoveto(pC->pCursor, (char*)&iKey, sizeof(int), &res); }else{ if( Stringify(p, tos) ) goto no_mem; @@ -2665,7 +2735,7 @@ case OP_NewRecno: { v += sqliteRandomByte() + 1; } if( v==0 ) continue; - x = bigEndian(v); + x = intToKey(v); rx = sqliteBtreeMoveto(pC->pCursor, &x, sizeof(int), &res); cnt++; }while( cnt<1000 && rx==SQLITE_OK && res==0 ); @@ -2707,7 +2777,7 @@ case OP_Put: { zKey = zStack[nos]; }else{ nKey = sizeof(int); - iKey = bigEndian(aStack[nos].i); + iKey = intToKey(aStack[nos].i); zKey = (char*)&iKey; } if( pOp->p2 ){ @@ -2875,7 +2945,7 @@ case OP_Recno: { v = p->aCsr[i].lastRecno; }else{ sqliteBtreeKey(pCrsr, 0, sizeof(u32), (char*)&v); - v = bigEndian(v); + v = keyToInt(v); } aStack[tos].i = v; aStack[tos].flags = STK_Int; @@ -3060,7 +3130,7 @@ case OP_IdxRecno: { int sz; sqliteBtreeKeySize(pCrsr, &sz); sqliteBtreeKey(pCrsr, sz - sizeof(u32), sizeof(u32), (char*)&v); - v = bigEndian(v); + v = keyToInt(v); aStack[tos].i = v; aStack[tos].flags = STK_Int; } diff --git a/src/vdbe.h b/src/vdbe.h index 2c21dbbe14..4f48f23a07 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -15,7 +15,7 @@ ** or VDBE. The VDBE implements an abstract machine that runs a ** simple program to access and modify the underlying database. ** -** $Id: vdbe.h,v 1.36 2001/11/08 00:45:22 drh Exp $ +** $Id: vdbe.h,v 1.37 2001/12/22 14:49:26 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -158,45 +158,46 @@ typedef struct VdbeOp VdbeOp; #define OP_Pop 74 #define OP_Dup 75 #define OP_Pull 76 +#define OP_MustBeInt 77 -#define OP_Add 77 -#define OP_AddImm 78 -#define OP_Subtract 79 -#define OP_Multiply 80 -#define OP_Divide 81 -#define OP_Remainder 82 -#define OP_BitAnd 83 -#define OP_BitOr 84 -#define OP_BitNot 85 -#define OP_ShiftLeft 86 -#define OP_ShiftRight 87 -#define OP_AbsValue 88 -#define OP_Precision 89 -#define OP_Min 90 -#define OP_Max 91 -#define OP_Like 92 -#define OP_Glob 93 -#define OP_Eq 94 -#define OP_Ne 95 -#define OP_Lt 96 -#define OP_Le 97 -#define OP_Gt 98 -#define OP_Ge 99 -#define OP_IsNull 100 -#define OP_NotNull 101 -#define OP_Negative 102 -#define OP_And 103 -#define OP_Or 104 -#define OP_Not 105 -#define OP_Concat 106 -#define OP_Noop 107 +#define OP_Add 78 +#define OP_AddImm 79 +#define OP_Subtract 80 +#define OP_Multiply 81 +#define OP_Divide 82 +#define OP_Remainder 83 +#define OP_BitAnd 84 +#define OP_BitOr 85 +#define OP_BitNot 86 +#define OP_ShiftLeft 87 +#define OP_ShiftRight 88 +#define OP_AbsValue 89 +#define OP_Precision 90 +#define OP_Min 91 +#define OP_Max 92 +#define OP_Like 93 +#define OP_Glob 94 +#define OP_Eq 95 +#define OP_Ne 96 +#define OP_Lt 97 +#define OP_Le 98 +#define OP_Gt 99 +#define OP_Ge 100 +#define OP_IsNull 101 +#define OP_NotNull 102 +#define OP_Negative 103 +#define OP_And 104 +#define OP_Or 105 +#define OP_Not 106 +#define OP_Concat 107 +#define OP_Noop 108 -#define OP_Strlen 108 -#define OP_Substr 109 +#define OP_Strlen 109 +#define OP_Substr 110 -#define OP_Limit 110 +#define OP_Limit 111 -#define OP_MAX 110 +#define OP_MAX 111 /* ** Prototypes for the VDBE interface. See comments on the implementation diff --git a/src/where.c b/src/where.c index 89a7258c90..54a2cddcbd 100644 --- a/src/where.c +++ b/src/where.c @@ -13,7 +13,7 @@ ** the WHERE clause of SQL statements. Also found here are subroutines ** to generate VDBE code to evaluate expressions. ** -** $Id: where.c,v 1.28 2001/11/12 13:51:43 drh Exp $ +** $Id: where.c,v 1.29 2001/12/22 14:49:26 drh Exp $ */ #include "sqliteInt.h" @@ -167,6 +167,9 @@ WhereInfo *sqliteWhereBegin( int base; /* First available index for OP_Open opcodes */ int nCur; /* Next unused cursor number */ int aDirect[32]; /* If TRUE, then index this table using ROWID */ + int iDirectEq[32]; /* Term of the form ROWID==X for the N-th table */ + int iDirectLt[32]; /* Term of the form ROWIDX or ROWID>=X */ ExprInfo aExpr[50]; /* The WHERE clause is divided into these expressions */ /* Allocate space for aOrder[] and aiMem[]. */ @@ -218,8 +221,13 @@ WhereInfo *sqliteWhereBegin( /* Figure out what index to use (if any) for each nested loop. ** Make pWInfo->a[i].pIdx point to the index to use for the i-th nested ** loop where i==0 is the outer loop and i==pTabList->nId-1 is the inner - ** loop. If the expression uses only the ROWID field, then set - ** aDirect[i] to 1. + ** loop. + ** + ** If terms exist that use the ROWID of any table, then set the + ** iDirectEq[], iDirectLt[], or iDirectGt[] elements for that table + ** to the index of the term containing the ROWID. We always prefer + ** to use a ROWID which can directly access a table rather than an + ** index which requires two accesses. ** ** Actually, if there are more than 32 tables in the join, only the ** first 32 tables are candidates for indices. @@ -234,23 +242,37 @@ WhereInfo *sqliteWhereBegin( int bestScore = 0; /* Check to see if there is an expression that uses only the - ** ROWID field of this table. If so, set aDirect[i] to 1. - ** If not, set aDirect[i] to 0. + ** ROWID field of this table. For terms of the form ROWID==expr + ** set iDirectEq[i] to the index of the term. For terms of the + ** form ROWIDexpr or ROWID>=expr set iDirectGt[i]. */ - aDirect[i] = 0; + iDirectEq[i] = -1; + iDirectLt[i] = -1; + iDirectGt[i] = -1; for(j=0; jpLeft->iColumn<0 && (aExpr[j].prereqRight & loopMask)==aExpr[j].prereqRight ){ - aDirect[i] = 1; - break; + switch( aExpr[j].p->op ){ + case TK_EQ: iDirectEq[i] = j; break; + case TK_LE: + case TK_LT: iDirectLt[i] = j; break; + case TK_GE: + case TK_GT: iDirectGt[i] = j; break; + } } if( aExpr[j].idxRight==idx && aExpr[j].p->pRight->iColumn<0 && (aExpr[j].prereqLeft & loopMask)==aExpr[j].prereqLeft ){ - aDirect[i] = 1; - break; + switch( aExpr[j].p->op ){ + case TK_EQ: iDirectEq[i] = j; break; + case TK_LE: + case TK_LT: iDirectGt[i] = j; break; + case TK_GE: + case TK_GT: iDirectLt[i] = j; break; + } } } - if( aDirect[i] ){ + if( iDirectEq[i]>=0 ){ loopMask |= 1<a[i].pIdx = 0; continue; @@ -394,43 +416,27 @@ WhereInfo *sqliteWhereBegin( for(i=0; inId; i++){ int j, k; int idx = aOrder[i]; - int goDirect; Index *pIdx; WhereLevel *pLevel = &pWInfo->a[i]; - if( ipIdx; - goDirect = aDirect[i]; - }else{ - pIdx = 0; - goDirect = 0; - } - - if( goDirect ){ - /* Case 1: We can directly reference a single row using the ROWID field. + pIdx = pLevel->pIdx; + if( i=0 ){ + /* Case 1: We can directly reference a single row using an + ** equality comparison against the ROWID field. */ - for(k=0; kpLeft->iColumn<0 - ){ - sqliteExprCode(pParse, aExpr[k].p->pRight); - aExpr[k].p = 0; - break; - } - if( aExpr[k].idxRight==idx - && (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft - && aExpr[k].p->pRight->iColumn<0 - ){ - sqliteExprCode(pParse, aExpr[k].p->pLeft); - aExpr[k].p = 0; - break; - } + k = iDirectEq[i]; + assert( kpRight); + }else{ + sqliteExprCode(pParse, aExpr[k].p->pLeft); } - sqliteVdbeAddOp(v, OP_AddImm, 0, 0); + aExpr[k].p = 0; brk = pLevel->brk = sqliteVdbeMakeLabel(v); cont = pLevel->cont = brk; + sqliteVdbeAddOp(v, OP_MustBeInt, 0, brk); if( i==pTabList->nId-1 && pushKey ){ haveKey = 1; }else{ @@ -438,22 +444,8 @@ WhereInfo *sqliteWhereBegin( haveKey = 0; } pLevel->op = OP_Noop; - }else if( pIdx==0 ){ - /* Case 2: There was no usable index. We must do a complete - ** scan of the entire database table. - */ - int start; - - brk = pLevel->brk = sqliteVdbeMakeLabel(v); - cont = pLevel->cont = sqliteVdbeMakeLabel(v); - sqliteVdbeAddOp(v, OP_Rewind, base+idx, brk); - start = sqliteVdbeCurrentAddr(v); - pLevel->op = OP_Next; - pLevel->p1 = base+idx; - pLevel->p2 = start; - haveKey = 0; - }else if( pLevel->score%4==0 ){ - /* Case 3: All index constraints are equality operators. + }else if( pIdx!=0 && pLevel->score%4==0 ){ + /* Case 2: All index constraints are equality operators. */ int start; int testOp; @@ -507,8 +499,79 @@ WhereInfo *sqliteWhereBegin( pLevel->op = OP_Next; pLevel->p1 = pLevel->iCur; pLevel->p2 = start; + }else if( i=0 || iDirectGt[i]>=0) ){ + /* Case 3: We have an inequality comparison against the ROWID field. + */ + int testOp = OP_Noop; + int start; + + brk = pLevel->brk = sqliteVdbeMakeLabel(v); + cont = pLevel->cont = sqliteVdbeMakeLabel(v); + if( iDirectGt[i]>=0 ){ + k = iDirectGt[i]; + assert( kpRight); + }else{ + sqliteExprCode(pParse, aExpr[k].p->pLeft); + } + sqliteVdbeAddOp(v, OP_MustBeInt, 0, brk); + if( aExpr[k].p->op==TK_LT || aExpr[k].p->op==TK_GT ){ + sqliteVdbeAddOp(v, OP_AddImm, 1, 0); + } + sqliteVdbeAddOp(v, OP_MoveTo, base+idx, brk); + aExpr[k].p = 0; + }else{ + sqliteVdbeAddOp(v, OP_Rewind, base+idx, brk); + } + if( iDirectLt[i]>=0 ){ + k = iDirectLt[i]; + assert( kpRight); + }else{ + sqliteExprCode(pParse, aExpr[k].p->pLeft); + } + sqliteVdbeAddOp(v, OP_MustBeInt, 0, sqliteVdbeCurrentAddr(v)+1); + pLevel->iMem = pParse->nMem++; + sqliteVdbeAddOp(v, OP_MemStore, pLevel->iMem, 0); + if( aExpr[k].p->op==TK_LT || aExpr[k].p->op==TK_GT ){ + testOp = OP_Ge; + }else{ + testOp = OP_Gt; + } + aExpr[k].p = 0; + } + start = sqliteVdbeCurrentAddr(v); + pLevel->op = OP_Next; + pLevel->p1 = base+idx; + pLevel->p2 = start; + if( testOp!=OP_Noop ){ + sqliteVdbeAddOp(v, OP_Recno, base+idx, 0); + sqliteVdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0); + sqliteVdbeAddOp(v, testOp, 0, brk); + } + haveKey = 0; + }else if( pIdx==0 ){ + /* Case 4: There was no usable index. We must do a complete + ** scan of the entire database table. + */ + int start; + + brk = pLevel->brk = sqliteVdbeMakeLabel(v); + cont = pLevel->cont = sqliteVdbeMakeLabel(v); + sqliteVdbeAddOp(v, OP_Rewind, base+idx, brk); + start = sqliteVdbeCurrentAddr(v); + pLevel->op = OP_Next; + pLevel->p1 = base+idx; + pLevel->p2 = start; + haveKey = 0; }else{ - /* Case 4: The contraints on the right-most index field are + /* Case 5: The contraints on the right-most index field are ** inequalities. */ int score = pLevel->score; diff --git a/test/func.test b/test/func.test index 884b599fbe..066a00fc6b 100644 --- a/test/func.test +++ b/test/func.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing built-in functions. # -# $Id: func.test,v 1.6 2001/10/20 12:30:12 drh Exp $ +# $Id: func.test,v 1.7 2001/12/22 14:49:26 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -146,8 +146,8 @@ do_test func-4.5 { catchsql {SELECT round(a,b,c) FROM t1} } {1 {too many arguments to function round()}} do_test func-4.6 { - catchsql {SELECT round(b,2) FROM t1} -} {0 {2.00 1.23 -2.00}} + catchsql {SELECT round(b,2) FROM t1 ORDER BY b} +} {0 {-2.00 1.23 2.00}} do_test func-4.7 { catchsql {SELECT round(b,0) FROM t1 ORDER BY a} } {0 {2 1 -2}} diff --git a/test/intpkey.test b/test/intpkey.test index 9868b7326c..abee972daf 100644 --- a/test/intpkey.test +++ b/test/intpkey.test @@ -13,7 +13,7 @@ # This file implements tests for the special processing associated # with INTEGER PRIMARY KEY columns. # -# $Id: intpkey.test,v 1.1 2001/12/21 14:30:44 drh Exp $ +# $Id: intpkey.test,v 1.2 2001/12/22 14:49:26 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -121,5 +121,250 @@ do_test intpkey-1.12 { } } {7 one two} +# Try to insert a non-integer value into the primary key field. This +# should result in a data type mismatch. +# +do_test intpkey-1.13 { + set r [catch {execsql { + INSERT INTO t1 VALUES('x','y','z'); + }} msg] + lappend r $msg +} {1 {datatype mismatch}} +do_test intpkey-1.14 { + set r [catch {execsql { + INSERT INTO t1 VALUES(3.4,'y','z'); + }} msg] + lappend r $msg +} {1 {datatype mismatch}} +do_test intpkey-1.15 { + set r [catch {execsql { + INSERT INTO t1 VALUES(-3,'y','z'); + }} msg] + lappend r $msg +} {0 {}} +do_test intpkey-1.16 { + execsql {SELECT * FROM t1} +} {-3 y z 5 hello world 6 second entry 7 one two} + +#### INDICES +# Check to make sure indices work correctly with integer primary keys +# +do_test intpkey-2.1 { + execsql { + CREATE INDEX i1 ON t1(b); + SELECT * FROM t1 WHERE b=='y' + } +} {-3 y z} +do_test intpkey-2.1.1 { + execsql { + SELECT * FROM t1 WHERE b=='y' AND rowid<0 + } +} {-3 y z} +do_test intpkey-2.1.2 { + execsql { + SELECT * FROM t1 WHERE b=='y' AND rowid<0 AND rowid>=-20 + } +} {-3 y z} +do_test intpkey-2.1.3 { + execsql { + SELECT * FROM t1 WHERE b>='y' + } +} {-3 y z} +do_test intpkey-2.1.4 { + execsql { + SELECT * FROM t1 WHERE b>='y' AND rowid<10 + } +} {-3 y z} +do_test intpkey-2.2 { + execsql { + UPDATE t1 SET a=8 WHERE b=='y'; + SELECT * FROM t1 WHERE b=='y'; + } +} {8 y z} +do_test intpkey-2.3 { + execsql { + SELECT rowid, * FROM t1; + } +} {5 5 hello world 6 6 second entry 7 7 one two 8 8 y z} +do_test intpkey-2.4 { + execsql { + SELECT rowid, * FROM t1 WHERE b<'second' + } +} {5 5 hello world 7 7 one two} +do_test intpkey-2.4.1 { + execsql { + SELECT rowid, * FROM t1 WHERE 'second'>b + } +} {5 5 hello world 7 7 one two} +do_test intpkey-2.4.2 { + execsql { + SELECT rowid, * FROM t1 WHERE 8>rowid AND 'second'>b + } +} {5 5 hello world 7 7 one two} +do_test intpkey-2.4.3 { + execsql { + SELECT rowid, * FROM t1 WHERE 8>rowid AND 'second'>b AND 0'a' + } +} {5 5 hello world 7 7 one two 6 6 second entry 8 8 y z} +do_test intpkey-2.6 { + execsql { + DELETE FROM t1 WHERE rowid=7; + SELECT * FROM t1 WHERE b>'a'; + } +} {5 hello world 6 second entry 8 y z} +do_test intpkey-2.7 { + execsql { + UPDATE t1 SET a=-4 WHERE rowid=8; + SELECT * FROM t1 WHERE b>'a'; + } +} {5 hello world 6 second entry -4 y z} +do_test intpkey-2.7 { + execsql { + SELECT * FROM t1 + } +} {-4 y z 5 hello world 6 second entry} + +# Do an SQL statement. Append the search count to the end of the result. +# +proc count sql { + set ::sqlite_search_count 0 + return [concat [execsql $sql] $::sqlite_search_count] +} + +# Create indices that include the integer primary key as one of their +# columns. +# +do_test intpkey-3.1 { + execsql { + CREATE INDEX i2 ON t1(a); + } +} {} +do_test intpkey-3.2 { + count { + SELECT * FROM t1 WHERE a=5; + } +} {5 hello world 0} +do_test intpkey-3.3 { + count { + SELECT * FROM t1 WHERE a>4 AND a<6; + } +} {5 hello world 2} +do_test intpkey-3.4 { + count { + SELECT * FROM t1 WHERE b>='hello' AND b<'hello2'; + } +} {5 hello world 3} +do_test intpkey-3.5 { + execsql { + CREATE INDEX i3 ON t1(c,a); + } +} {} +do_test intpkey-3.6 { + count { + SELECT * FROM t1 WHERE c=='world'; + } +} {5 hello world 3} +do_test intpkey-3.7 { + execsql {INSERT INTO t1 VALUES(11,'hello','world')} + count { + SELECT * FROM t1 WHERE c=='world'; + } +} {5 hello world 11 hello world 5} +do_test intpkey-3.8 { + count { + SELECT * FROM t1 WHERE c=='world' AND a>7; + } +} {11 hello world 5} +do_test intpkey-3.9 { + count { + SELECT * FROM t1 WHERE 7=oid; + } +} {11 hello world 1} +do_test intpkey-4.9 { + count { + SELECT * FROM t1 WHERE 11<=_rowid_ AND 12>=a; + } +} {11 hello world 1} +do_test intpkey-4.10 { + count { + SELECT * FROM t1 WHERE 0>=_rowid_; + } +} {-4 y z 1} +do_test intpkey-4.11 { + count { + SELECT * FROM t1 WHERE a<0; + } +} {-4 y z 1} +do_test intpkey-4.12 { + count { + SELECT * FROM t1 WHERE a<0 AND a>10; + } +} {1} + +# Make sure it is OK to insert a rowid of 0 +# +do_test intpkey-5.1 { + execsql { + INSERT INTO t1 VALUES(0,'zero','entry'); + } + count { + SELECT * FROM t1 WHERE a=0; + } +} {0 zero entry 0} +do_test intpkey=5.2 { + execsql { + SELECT rowid, a FROM t1 + } +} {-4 -4 0 0 5 5 6 6 11 11} + finish_test diff --git a/test/where.test b/test/where.test index 85096f7c15..4ab658b144 100644 --- a/test/where.test +++ b/test/where.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the use of indices in WHERE clases. # -# $Id: where.test,v 1.4 2001/11/08 00:45:22 drh Exp $ +# $Id: where.test,v 1.5 2001/12/22 14:49:26 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -167,7 +167,7 @@ do_test where-1.36 { count {SELECT w FROM t1 WHERE w<=3} } {1 2 3 6} do_test where-1.37 { - count {SELECT w FROM t1 WHERE w+1<=4} + count {SELECT w FROM t1 WHERE w+1<=4 ORDER BY w} } {1 2 3 99}