-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
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
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
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
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
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
-236a54d289e858a1e0505a20d907a2a40c01b521
\ No newline at end of file
+29cab124b4f7eae9d9feb60d2f3a2c443fd9b9aa
\ No newline at end of file
** 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 <ctype.h>
}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; i<pTab->nCol; 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);
** 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"
}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);
}
** 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}
{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);}
** 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"
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 && iCol<pTab->nCol );
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);
}
** 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_
#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
** 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"
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.
** 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 <stdarg.h>
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;
** 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 <ctype.h>
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
*/
"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",
};
/*
}
/*
-** 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;
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
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
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);
if( i>=0 && i<p->nCursor && (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);
if( VERIFY( i>=0 && i<p->nCursor && ) (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;
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 );
zKey = zStack[nos];
}else{
nKey = sizeof(int);
- iKey = bigEndian(aStack[nos].i);
+ iKey = intToKey(aStack[nos].i);
zKey = (char*)&iKey;
}
if( pOp->p2 ){
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;
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;
}
** 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_
#define OP_Pop 74
#define OP_Dup 75
#define OP_Pull 76
-
-#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_Strlen 108
-#define OP_Substr 109
-
-#define OP_Limit 110
-
-#define OP_MAX 110
+#define OP_MustBeInt 77
+
+#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 109
+#define OP_Substr 110
+
+#define OP_Limit 111
+
+#define OP_MAX 111
/*
** Prototypes for the VDBE interface. See comments on the implementation
** 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"
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 ROWID<X or ROWID<=X */
+ int iDirectGt[32]; /* Term of the form ROWID>X or ROWID>=X */
ExprInfo aExpr[50]; /* The WHERE clause is divided into these expressions */
/* Allocate space for aOrder[] and aiMem[]. */
/* 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.
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 ROWID<expr or ROWID<=expr set iDirectLt[i] to the term index.
+ ** For terms like ROWID>expr or ROWID>=expr set iDirectGt[i].
*/
- aDirect[i] = 0;
+ iDirectEq[i] = -1;
+ iDirectLt[i] = -1;
+ iDirectGt[i] = -1;
for(j=0; j<nExpr; j++){
if( aExpr[j].idxLeft==idx && aExpr[j].p->pLeft->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<<idx;
pWInfo->a[i].pIdx = 0;
continue;
for(i=0; i<pTabList->nId; i++){
int j, k;
int idx = aOrder[i];
- int goDirect;
Index *pIdx;
WhereLevel *pLevel = &pWInfo->a[i];
- if( i<ARRAYSIZE(aDirect) ){
- pIdx = pLevel->pIdx;
- 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<ARRAYSIZE(iDirectEq) && iDirectEq[i]>=0 ){
+ /* Case 1: We can directly reference a single row using an
+ ** equality comparison against the ROWID field.
*/
- 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->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( k<nExpr );
+ assert( aExpr[k].p!=0 );
+ assert( aExpr[k].idxLeft==idx || aExpr[k].idxRight==idx );
+ if( aExpr[k].idxLeft==idx ){
+ sqliteExprCode(pParse, aExpr[k].p->pRight);
+ }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{
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;
pLevel->op = OP_Next;
pLevel->p1 = pLevel->iCur;
pLevel->p2 = start;
+ }else if( i<ARRAYSIZE(iDirectLt) && (iDirectLt[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( k<nExpr );
+ assert( aExpr[k].p!=0 );
+ assert( aExpr[k].idxLeft==idx || aExpr[k].idxRight==idx );
+ if( aExpr[k].idxLeft==idx ){
+ sqliteExprCode(pParse, aExpr[k].p->pRight);
+ }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( k<nExpr );
+ assert( aExpr[k].p!=0 );
+ assert( aExpr[k].idxLeft==idx || aExpr[k].idxRight==idx );
+ if( aExpr[k].idxLeft==idx ){
+ sqliteExprCode(pParse, aExpr[k].p->pRight);
+ }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;
# 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
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}}
# 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
}
} {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<rowid
+ }
+} {5 5 hello world 7 7 one two}
+do_test intpkey-2.5 {
+ execsql {
+ SELECT rowid, * FROM t1 WHERE b>'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<a;
+ }
+} {11 hello world 1}
+
+# Test inequality constraints on integer primary keys and rowids
+#
+do_test intpkey-4.1 {
+ count {
+ SELECT * FROM t1 WHERE 11=rowid
+ }
+} {11 hello world 0}
+do_test intpkey-4.2 {
+ count {
+ SELECT * FROM t1 WHERE 11=rowid AND b=='hello'
+ }
+} {11 hello world 0}
+do_test intpkey-4.3 {
+ count {
+ SELECT * FROM t1 WHERE 11=rowid AND b=='hello' AND c IS NOT NULL;
+ }
+} {11 hello world 0}
+do_test intpkey-4.4 {
+ count {
+ SELECT * FROM t1 WHERE rowid==11
+ }
+} {11 hello world 0}
+do_test intpkey-4.5 {
+ count {
+ SELECT * FROM t1 WHERE oid==11 AND b=='hello'
+ }
+} {11 hello world 0}
+do_test intpkey-4.6 {
+ count {
+ SELECT * FROM t1 WHERE a==11 AND b=='hello' AND c IS NOT NULL;
+ }
+} {11 hello world 0}
+
+do_test intpkey-4.7 {
+ count {
+ SELECT * FROM t1 WHERE 8<rowid;
+ }
+} {11 hello world 1}
+do_test intpkey-4.8 {
+ count {
+ SELECT * FROM t1 WHERE 8<rowid AND 11>=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
# 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
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}