Bug fixing in the new integer primary key code. (CVS 334)

FossilOrigin-Name: 29cab124b4f7eae9d9feb60d2f3a2c443fd9b9aa
This commit is contained in:
drh 2001-12-22 14:49:24 +00:00
parent 4a32431ce7
commit 8aff10153e
15 changed files with 551 additions and 157 deletions

View File

@ -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

View File

@ -1 +1 @@
236a54d289e858a1e0505a20d907a2a40c01b521
29cab124b4f7eae9d9feb60d2f3a2c443fd9b9aa

View File

@ -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 <ctype.h>
@ -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; 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);

View File

@ -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);
}

View File

@ -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);}

View File

@ -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 && 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);
}

View File

@ -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

View File

@ -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.

View File

@ -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 <stdarg.h>
@ -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;

View File

@ -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 <ctype.h>
@ -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 && 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);
@ -2606,7 +2676,7 @@ case OP_Found: {
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;
@ -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;
}

View File

@ -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

View File

@ -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 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[]. */
@ -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 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;
@ -394,43 +416,27 @@ WhereInfo *sqliteWhereBegin(
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{
@ -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<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;

View File

@ -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}}

View File

@ -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<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

View File

@ -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}