Add optimizations for the IN operator in WHERE clauses. This is a partial

implementation of enhancement #63.  Still need to add test cases. (CVS 610)

FossilOrigin-Name: 8481e841ebdeabe07bf780246bda1aa053eb60b7
This commit is contained in:
drh 2002-06-08 23:25:08 +00:00
parent f5db2d3ea2
commit d99f70680f
7 changed files with 187 additions and 110 deletions

View File

@ -1,5 +1,5 @@
C Bug\sfix:\sdo\snot\ssegfault\sif\sa\sSELECT\swithout\sa\sFROM\sclause\sincludes\nthe\s*\swildcard\sin\sthe\sresult\scolumn\slist.\s(CVS\s609)
D 2002-06-06T23:42:28
C Add\soptimizations\sfor\sthe\sIN\soperator\sin\sWHERE\sclauses.\s\sThis\sis\sa\spartial\nimplementation\sof\senhancement\s#63.\s\sStill\sneed\sto\sadd\stest\scases.\s(CVS\s610)
D 2002-06-08T23:25:09
F Makefile.in 6291a33b87d2a395aafd7646ee1ed562c6f2c28c
F Makefile.template 4e11752e0b5c7a043ca50af4296ec562857ba495
F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
@ -26,7 +26,7 @@ F src/encode.c 346b12b46148506c32038524b95c4631ab46d760
F src/expr.c 8ce9c22655735ff62b1e33ab11ad9d44c4ab99c6
F src/func.c 061a520a122da7e4f9dcac15697bb996aac7d5df
F src/hash.c 6a6236b89c8c060c65dabd300a1c8ce7c10edb72
F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9
F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
F src/insert.c 4b0bd94296fea46ef1b2ed8bfd05e12a38ce2c90
F src/main.c 6e53c49a390fabd5fecce9e3b128c61c85208000
F src/md5.c 0ae1f3e2cac92d06fc6246d1b4b8f61a2fe66d3b
@ -41,7 +41,7 @@ F src/select.c 1d5cb1ae0bb3376bedfde7ae22e6e927e4d0b5e2
F src/shell.c 1d22fe870ee852cfb975fd000dbe3973713d0a15
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in 0038faa6d642de06b91143ee65a131bd831d020b
F src/sqliteInt.h 3fd61a32c101b10aea610de8e7d931744657712f
F src/sqliteInt.h 09f3e26d0368284965efef7d1a9999b0eba801d3
F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
F src/tclsqlite.c 9300c9606a38bc0c75d6c0bc8a6197ab979353d1
F src/test1.c 09d95048b66ce6dcd2bae90f443589043d7d631e
@ -52,9 +52,9 @@ F src/tokenize.c 35c63867d03fcaf81fe520f8d8206981d0c7270e
F src/trigger.c d02f8e3510c7c2ad948a0e8c3bb0cca8adaf80c5
F src/update.c f68375173bf5338cae3e97012708e10f206aedd9
F src/util.c 7cf46b5612f5d12601c697374b9c6b38b2332ce8
F src/vdbe.c 27b71e3c6cc77c071421b24462872f32047e2c20
F src/vdbe.h b8706429131c14b307a07aab7e47f95a9da53610
F src/where.c b054f2f23127bd57eb5f973bcd38764b875d73fe
F src/vdbe.c b315d7ad5086164bb8d8aee8bc9edeafcb68b8ea
F src/vdbe.h 1742d6f8b40f40879475b4c41cf4f9980ceb0e21
F src/where.c d5308069f8794ec7e9f5084ffd611fe0922ae9f0
F test/all.test e4d3821eeba751829b419cd47814bd20af4286d1
F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
F test/btree.test bf326f546a666617367a7033fa2c07451bd4f8e1
@ -136,7 +136,7 @@ F www/speed.tcl da8afcc1d3ccc5696cfb388a68982bc3d9f7f00f
F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
P a0abef62bfe1b0f8c6249ba520dd2735190783a5
R 5d70f1605b7b9ff2e858e45ab5b5947d
P d939294994e5f6c7862b66573301e111e56a2681
R 3ccd9ba147258740a1a4d6ac8604b91f
U drh
Z 6eb753fe6a2122abae187660dd07b07e
Z 0bb147d1fd50388db4609fcd1f1c4973

View File

@ -1 +1 @@
d939294994e5f6c7862b66573301e111e56a2681
8481e841ebdeabe07bf780246bda1aa053eb60b7

View File

@ -12,7 +12,7 @@
** This is the header file for the generic hash-table implemenation
** used in SQLite.
**
** $Id: hash.h,v 1.4 2002/02/23 23:45:45 drh Exp $
** $Id: hash.h,v 1.5 2002/06/08 23:25:09 drh Exp $
*/
#ifndef _SQLITE_HASH_H_
#define _SQLITE_HASH_H_
@ -99,6 +99,7 @@ void sqliteHashClear(Hash*);
#define sqliteHashNext(E) ((E)->next)
#define sqliteHashData(E) ((E)->data)
#define sqliteHashKey(E) ((E)->pKey)
#define sqliteHashKeysize(E) ((E)->nKey)
/*
** Number of entries in a hash table

View File

@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.120 2002/06/06 18:54:41 drh Exp $
** @(#) $Id: sqliteInt.h,v 1.121 2002/06/08 23:25:09 drh Exp $
*/
#include "sqlite.h"
#include "hash.h"
@ -411,7 +411,8 @@ struct Token {
struct Expr {
int op; /* Operation performed by this node */
Expr *pLeft, *pRight; /* Left and right subnodes */
ExprList *pList; /* A list of expressions used as a function argument */
ExprList *pList; /* A list of expressions used as function arguments
** or in "<expr> IN (<expr-list)" */
Token token; /* An operand token */
Token span; /* Complete text of the expression */
int iTable, iColumn; /* When op==TK_COLUMN, then this expr node means the
@ -419,7 +420,8 @@ struct Expr {
** op==TK_FUNCTION, iColumn holds the function id */
int iAgg; /* When op==TK_COLUMN and pParse->useAgg==TRUE, pull
** result from the iAgg-th element of the aggregator */
Select *pSelect; /* When the expression is a sub-select */
Select *pSelect; /* When the expression is a sub-select. Also the
** right side of "<expr> IN (<select>)" */
};
/*
@ -508,6 +510,7 @@ struct WhereLevel {
int op, p1, p2; /* Opcode used to terminate the loop */
int iLeftJoin; /* Memory cell used to implement LEFT OUTER JOIN */
int top; /* First instruction of interior of the loop */
int inOp, inP1, inP2;/* Opcode used to implement an IN operator */
};
/*

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.153 2002/06/06 23:16:06 drh Exp $
** $Id: vdbe.c,v 1.154 2002/06/08 23:25:09 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@ -198,6 +198,7 @@ struct AggElem {
typedef struct Set Set;
struct Set {
Hash hash; /* A set is just a hash table */
HashElem *prev; /* Previously accessed hash elemen */
};
/*
@ -1066,19 +1067,19 @@ static char *zOpName[] = { 0,
"FileOpen", "FileRead", "FileColumn", "AggReset",
"AggFocus", "AggNext", "AggSet", "AggGet",
"AggFunc", "AggInit", "AggPush", "AggPop",
"SetInsert", "SetFound", "SetNotFound", "MakeRecord",
"MakeKey", "MakeIdxKey", "IncrKey", "Goto",
"If", "IfNot", "Halt", "ColumnCount",
"ColumnName", "Callback", "NullCallback", "Integer",
"String", "Pop", "Dup", "Pull",
"Push", "MustBeInt", "Add", "AddImm",
"Subtract", "Multiply", "Divide", "Remainder",
"BitAnd", "BitOr", "BitNot", "ShiftLeft",
"ShiftRight", "AbsValue", "Eq", "Ne",
"Lt", "Le", "Gt", "Ge",
"IsNull", "NotNull", "Negative", "And",
"Or", "Not", "Concat", "Noop",
"Function", "Limit",
"SetInsert", "SetFound", "SetNotFound", "SetFirst",
"SetNext", "MakeRecord", "MakeKey", "MakeIdxKey",
"IncrKey", "Goto", "If", "IfNot",
"Halt", "ColumnCount", "ColumnName", "Callback",
"NullCallback", "Integer", "String", "Pop",
"Dup", "Pull", "Push", "MustBeInt",
"Add", "AddImm", "Subtract", "Multiply",
"Divide", "Remainder", "BitAnd", "BitOr",
"BitNot", "ShiftLeft", "ShiftRight", "AbsValue",
"Eq", "Ne", "Lt", "Le",
"Gt", "Ge", "IsNull", "NotNull",
"Negative", "And", "Or", "Not",
"Concat", "Noop", "Function", "Limit",
};
/*
@ -1957,7 +1958,7 @@ case OP_AddImm: {
/* 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
** stack is not an integer and cannot be converted into an integer
** with out data loss, then jump immediately to P2, or if P2==0
** raise an SQLITE_MISMATCH exception.
*/
@ -3018,7 +3019,7 @@ case OP_MoveTo: {
** This operation is similar to NotFound except that this operation
** does not pop the key from the stack.
**
** See also: Found, NotFound, MoveTo
** See also: Found, NotFound, MoveTo, IsUnique, NotExists
*/
/* Opcode: Found P1 P2 *
**
@ -3027,7 +3028,7 @@ case OP_MoveTo: {
** does not exist, then fall thru. The cursor is left pointing
** to the record if it exists. The key is popped from the stack.
**
** See also: Distinct, NotFound, MoveTo
** See also: Distinct, NotFound, MoveTo, IsUnique, NotExists
*/
/* Opcode: NotFound P1 P2 *
**
@ -3084,7 +3085,7 @@ case OP_Found: {
** number for that entry is pushed onto the stack and control
** falls through to the next instruction.
**
** See also: Distinct, NotFound, NotExists
** See also: Distinct, NotFound, NotExists, Found
*/
case OP_IsUnique: {
int i = pOp->p1;
@ -3167,7 +3168,7 @@ case OP_IsUnique: {
** operation assumes the key is an integer and NotFound assumes it
** is a string.
**
** See also: Distinct, Found, MoveTo, NotExists
** See also: Distinct, Found, MoveTo, NotFound, IsUnique
*/
case OP_NotExists: {
int i = pOp->p1;
@ -4775,6 +4776,46 @@ case OP_SetNotFound: {
break;
}
/* Opcode: SetFirst P1 P2 *
**
** Read the first element from set P1 and push it onto the stack. If the
** set is empty, push nothing and jump immediately to P2. This opcode is
** used in combination with OP_SetNext to loop over all elements of a set.
*/
/* Opcode: SetNext P1 P2 *
**
** Read the next element from set P1 and push it onto the stack. If there
** are no more elements in the set, do not do the push and fall through.
** Otherwise, jump to P2 after pushing the next set element.
*/
case OP_SetFirst:
case OP_SetNext: {
Set *pSet;
int tos;
VERIFY( if( pOp->p1<0 || pOp->p1>=p->nSet ) goto bad_instruction; )
pSet = &p->aSet[pOp->p1];
if( pOp->opcode==OP_SetFirst ){
pSet->prev = sqliteHashFirst(&pSet->hash);
if( pSet->prev==0 ){
pc = pOp->p2 - 1;
break;
}
}else{
VERIFY( if( pSet->prev==0 ) goto bad_instruction; )
pSet->prev = sqliteHashNext(pSet->prev);
if( pSet->prev==0 ){
break;
}else{
pc = pOp->p2 - 1;
}
}
tos = ++p->tos;
VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; )
zStack[tos] = sqliteHashKey(pSet->prev);
aStack[tos].n = sqliteHashKeysize(pSet->prev);
aStack[tos].flags = STK_Str | STK_Static;
break;
}
/* An other opcode is illegal...
*/

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.53 2002/05/26 20:54:34 drh Exp $
** $Id: vdbe.h,v 1.54 2002/06/08 23:25:09 drh Exp $
*/
#ifndef _SQLITE_VDBE_H_
#define _SQLITE_VDBE_H_
@ -149,62 +149,64 @@ typedef struct VdbeOp VdbeOp;
#define OP_SetInsert 69
#define OP_SetFound 70
#define OP_SetNotFound 71
#define OP_SetFirst 72
#define OP_SetNext 73
#define OP_MakeRecord 72
#define OP_MakeKey 73
#define OP_MakeIdxKey 74
#define OP_IncrKey 75
#define OP_MakeRecord 74
#define OP_MakeKey 75
#define OP_MakeIdxKey 76
#define OP_IncrKey 77
#define OP_Goto 76
#define OP_If 77
#define OP_IfNot 78
#define OP_Halt 79
#define OP_Goto 78
#define OP_If 79
#define OP_IfNot 80
#define OP_Halt 81
#define OP_ColumnCount 80
#define OP_ColumnName 81
#define OP_Callback 82
#define OP_NullCallback 83
#define OP_ColumnCount 82
#define OP_ColumnName 83
#define OP_Callback 84
#define OP_NullCallback 85
#define OP_Integer 84
#define OP_String 85
#define OP_Pop 86
#define OP_Dup 87
#define OP_Pull 88
#define OP_Push 89
#define OP_MustBeInt 90
#define OP_Integer 86
#define OP_String 87
#define OP_Pop 88
#define OP_Dup 89
#define OP_Pull 90
#define OP_Push 91
#define OP_MustBeInt 92
#define OP_Add 91
#define OP_AddImm 92
#define OP_Subtract 93
#define OP_Multiply 94
#define OP_Divide 95
#define OP_Remainder 96
#define OP_BitAnd 97
#define OP_BitOr 98
#define OP_BitNot 99
#define OP_ShiftLeft 100
#define OP_ShiftRight 101
#define OP_AbsValue 102
#define OP_Eq 103
#define OP_Ne 104
#define OP_Lt 105
#define OP_Le 106
#define OP_Gt 107
#define OP_Ge 108
#define OP_IsNull 109
#define OP_NotNull 110
#define OP_Negative 111
#define OP_And 112
#define OP_Or 113
#define OP_Not 114
#define OP_Concat 115
#define OP_Noop 116
#define OP_Function 117
#define OP_Add 93
#define OP_AddImm 94
#define OP_Subtract 95
#define OP_Multiply 96
#define OP_Divide 97
#define OP_Remainder 98
#define OP_BitAnd 99
#define OP_BitOr 100
#define OP_BitNot 101
#define OP_ShiftLeft 102
#define OP_ShiftRight 103
#define OP_AbsValue 104
#define OP_Eq 105
#define OP_Ne 106
#define OP_Lt 107
#define OP_Le 108
#define OP_Gt 109
#define OP_Ge 110
#define OP_IsNull 111
#define OP_NotNull 112
#define OP_Negative 113
#define OP_And 114
#define OP_Or 115
#define OP_Not 116
#define OP_Concat 117
#define OP_Noop 118
#define OP_Function 119
#define OP_Limit 118
#define OP_Limit 120
#define OP_MAX 118
#define OP_MAX 120
/*
** 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.48 2002/05/26 20:54:34 drh Exp $
** $Id: where.c,v 1.49 2002/06/08 23:25:10 drh Exp $
*/
#include "sqliteInt.h"
@ -112,6 +112,7 @@ static int allowedOp(int op){
case TK_GT:
case TK_GE:
case TK_EQ:
case TK_IN:
return 1;
default:
return 0;
@ -136,7 +137,7 @@ static void exprAnalyze(int base, ExprInfo *pInfo){
pInfo->idxLeft = -1;
pInfo->idxRight = -1;
if( allowedOp(pExpr->op) && (pInfo->prereqRight & pInfo->prereqLeft)==0 ){
if( pExpr->pRight->op==TK_COLUMN ){
if( pExpr->pRight && pExpr->pRight->op==TK_COLUMN ){
pInfo->idxRight = pExpr->pRight->iTable - base;
pInfo->indexable = 1;
}
@ -288,6 +289,7 @@ WhereInfo *sqliteWhereBegin(
if( aExpr[j].idxLeft==idx && aExpr[j].p->pLeft->iColumn<0
&& (aExpr[j].prereqRight & loopMask)==aExpr[j].prereqRight ){
switch( aExpr[j].p->op ){
case TK_IN:
case TK_EQ: iDirectEq[i] = j; break;
case TK_LE:
case TK_LT: iDirectLt[i] = j; break;
@ -329,6 +331,9 @@ WhereInfo *sqliteWhereBegin(
** there is an inequality used as a termination key. (ex: "x<...")
** If score&2 is not 0 then there is an inequality used as the
** start key. (ex: "x>...");
**
** The IN operator as in "<expr> IN (...)" is treated the same as
** an equality comparison.
*/
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int eqMask = 0; /* Index columns covered by an x=... constraint */
@ -346,6 +351,7 @@ WhereInfo *sqliteWhereBegin(
for(k=0; k<pIdx->nColumn; k++){
if( pIdx->aiColumn[k]==iColumn ){
switch( aExpr[j].p->op ){
case TK_IN:
case TK_EQ: {
eqMask |= 1<<k;
break;
@ -467,6 +473,7 @@ WhereInfo *sqliteWhereBegin(
}
pIdx = pLevel->pIdx;
pLevel->inOp = OP_Noop;
if( i<ARRAYSIZE(iDirectEq) && iDirectEq[i]>=0 ){
/* Case 1: We can directly reference a single row using an
** equality comparison against the ROWID field.
@ -475,30 +482,31 @@ WhereInfo *sqliteWhereBegin(
assert( k<nExpr );
assert( aExpr[k].p!=0 );
assert( aExpr[k].idxLeft==idx || aExpr[k].idxRight==idx );
brk = pLevel->brk = sqliteVdbeMakeLabel(v);
if( aExpr[k].idxLeft==idx ){
sqliteExprCode(pParse, aExpr[k].p->pRight);
Expr *pX = aExpr[k].p;
if( pX->op!=TK_IN ){
sqliteExprCode(pParse, aExpr[k].p->pRight);
}else if( pX->pList ){
sqliteVdbeAddOp(v, OP_SetFirst, pX->iTable, brk);
pLevel->inOp = OP_SetNext;
pLevel->inP1 = pX->iTable;
pLevel->inP2 = sqliteVdbeCurrentAddr(v);
}else{
assert( pX->pSelect );
sqliteVdbeAddOp(v, OP_Rewind, pX->iTable, brk);
sqliteVdbeAddOp(v, OP_KeyAsData, pX->iTable, 1);
pLevel->inP2 = sqliteVdbeAddOp(v, OP_FullKey, pX->iTable, 0);
pLevel->inOp = OP_Next;
pLevel->inP1 = pX->iTable;
}
}else{
sqliteExprCode(pParse, aExpr[k].p->pLeft);
}
aExpr[k].p = 0;
brk = pLevel->brk = sqliteVdbeMakeLabel(v);
cont = pLevel->cont = brk;
cont = pLevel->cont = sqliteVdbeMakeLabel(v);
sqliteVdbeAddOp(v, OP_MustBeInt, 0, brk);
if( i==pTabList->nSrc-1 && pushKey ){
/* Note: The OP_Dup below will cause the recno to be left on the
** stack if the record does not exists and the OP_NotExists jump is
** taken. This violates a general rule of the VDBE that you should
** never leave values on the stack in order to avoid a stack overflow.
** But in this case, the OP_Dup will never happen inside of a loop,
** because the pushKey flag is only true for UPDATE and DELETE, not
** for SELECT, and nested loops only occur on a SELECT.
** So it is safe to leave the recno on the stack.
*/
haveKey = 1;
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
}else{
haveKey = 0;
}
haveKey = 0;
sqliteVdbeAddOp(v, OP_NotExists, base+idx, brk);
pLevel->op = OP_Noop;
}else if( pIdx!=0 && pLevel->score%4==0 ){
@ -507,17 +515,37 @@ WhereInfo *sqliteWhereBegin(
int start;
int testOp;
int nColumn = pLevel->score/4;
brk = pLevel->brk = sqliteVdbeMakeLabel(v);
for(j=0; j<nColumn; j++){
for(k=0; k<nExpr; k++){
if( aExpr[k].p==0 ) continue;
Expr *pX = aExpr[k].p;
if( pX==0 ) continue;
if( aExpr[k].idxLeft==idx
&& aExpr[k].p->op==TK_EQ
&& (aExpr[k].prereqRight & loopMask)==aExpr[k].prereqRight
&& aExpr[k].p->pLeft->iColumn==pIdx->aiColumn[j]
&& pX->pLeft->iColumn==pIdx->aiColumn[j]
){
sqliteExprCode(pParse, aExpr[k].p->pRight);
aExpr[k].p = 0;
break;
if( pX->op==TK_EQ ){
sqliteExprCode(pParse, pX->pRight);
aExpr[k].p = 0;
break;
}
if( pX->op==TK_IN && nColumn==1 ){
if( pX->pList ){
sqliteVdbeAddOp(v, OP_SetFirst, pX->iTable, brk);
pLevel->inOp = OP_SetNext;
pLevel->inP1 = pX->iTable;
pLevel->inP2 = sqliteVdbeCurrentAddr(v);
}else{
assert( pX->pSelect );
sqliteVdbeAddOp(v, OP_Rewind, pX->iTable, brk);
sqliteVdbeAddOp(v, OP_KeyAsData, pX->iTable, 1);
pLevel->inP2 = sqliteVdbeAddOp(v, OP_FullKey, pX->iTable, 0);
pLevel->inOp = OP_Next;
pLevel->inP1 = pX->iTable;
}
aExpr[k].p = 0;
break;
}
}
if( aExpr[k].idxRight==idx
&& aExpr[k].p->op==TK_EQ
@ -531,7 +559,6 @@ WhereInfo *sqliteWhereBegin(
}
}
pLevel->iMem = pParse->nMem++;
brk = pLevel->brk = sqliteVdbeMakeLabel(v);
cont = pLevel->cont = sqliteVdbeMakeLabel(v);
sqliteVdbeAddOp(v, OP_MakeKey, nColumn, 0);
if( nColumn==pIdx->nColumn ){
@ -834,6 +861,9 @@ void sqliteWhereEnd(WhereInfo *pWInfo){
sqliteVdbeAddOp(v, pLevel->op, pLevel->p1, pLevel->p2);
}
sqliteVdbeResolveLabel(v, pLevel->brk);
if( pLevel->inOp!=OP_Noop ){
sqliteVdbeAddOp(v, pLevel->inOp, pLevel->inP1, pLevel->inP2);
}
if( pLevel->iLeftJoin ){
int addr;
addr = sqliteVdbeAddOp(v, OP_MemLoad, pLevel->iLeftJoin, 0);