1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

Optimize queries that contain "WHERE rowid IN (x, y, z...)" by using an intkey btree to store the (x, y, z...) set instead of an index btree. (CVS 5760)

FossilOrigin-Name: 803a1736d56b3c07b8ad38715fe0e39196ecc507
This commit is contained in:
danielk1977
2008-10-02 13:50:55 +00:00
parent 0368072905
commit 41a05b7bee
5 changed files with 141 additions and 23 deletions

View File

@ -1,5 +1,5 @@
C Adjust\sthe\smemory\susage\sbounds\son\sthe\smemsubsys1.test\sscript\sso\sthat\nit\sworks\son\samd64.\s(CVS\s5759) C Optimize\squeries\sthat\scontain\s"WHERE\srowid\sIN\s(x,\sy,\sz...)"\sby\susing\san\sintkey\sbtree\sto\sstore\sthe\s(x,\sy,\sz...)\sset\sinstead\sof\san\sindex\sbtree.\s(CVS\s5760)
D 2008-10-01T13:55:51 D 2008-10-02T13:50:56
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
F Makefile.in e4ab842f9a64ef61d57093539a8aab76b12810db F Makefile.in e4ab842f9a64ef61d57093539a8aab76b12810db
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@ -107,7 +107,7 @@ F src/callback.c 7a40fd44da3eb89e7f6eff30aa6f940c45d73a97
F src/complete.c cb14e06dbe79dee031031f0d9e686ff306afe07c F src/complete.c cb14e06dbe79dee031031f0d9e686ff306afe07c
F src/date.c 5c092296c03d658e84884121a694150964d6861d F src/date.c 5c092296c03d658e84884121a694150964d6861d
F src/delete.c bae6684aa02e1f7cf6328023157c91d9cf94200b F src/delete.c bae6684aa02e1f7cf6328023157c91d9cf94200b
F src/expr.c 04822d5c7af85fd195a5f72501ab5e8d99768fae F src/expr.c 61f5afca422f34194225cfd9bca9f2efd96c6179
F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff
F src/func.c 8431b40a7843d1024145684d303c55b4ee087bbe F src/func.c 8431b40a7843d1024145684d303c55b4ee087bbe
F src/global.c 20a3fe46c8287a01ba3a7442558f0eb70c66b19a F src/global.c 20a3fe46c8287a01ba3a7442558f0eb70c66b19a
@ -151,7 +151,7 @@ F src/select.c f118f8db2ce91a4cf972e6ae88c8e7cc8543f513
F src/shell.c d83b578a8ccdd3e0e7fef4388a0887ce9f810967 F src/shell.c d83b578a8ccdd3e0e7fef4388a0887ce9f810967
F src/sqlite.h.in 2be75cc1f2a5515db910e8d0e2d5954b493fd1f3 F src/sqlite.h.in 2be75cc1f2a5515db910e8d0e2d5954b493fd1f3
F src/sqlite3ext.h 1e3887c9bd3ae66cb599e922824b04cd0d0f2c3e F src/sqlite3ext.h 1e3887c9bd3ae66cb599e922824b04cd0d0f2c3e
F src/sqliteInt.h dbe2797806c48f4a90adfea69dfbea95edd722ae F src/sqliteInt.h 437b408a7473293a903eb63f3c1d6ca814a13cdb
F src/sqliteLimit.h f435e728c6b620ef7312814d660a81f9356eb5c8 F src/sqliteLimit.h f435e728c6b620ef7312814d660a81f9356eb5c8
F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76 F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76
F src/table.c 22744786199c9195720c15a7a42cb97b2e2728d8 F src/table.c 22744786199c9195720c15a7a42cb97b2e2728d8
@ -362,6 +362,7 @@ F test/icu.test f51d0f4407ff8bb1e872f51a0e8b08fdc5a6897e
F test/in.test d49419c6df515852f477fa513f3317181d46bc92 F test/in.test d49419c6df515852f477fa513f3317181d46bc92
F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75 F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0 F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
F test/in4.test c043f75147295e9f6ad5040a5cda2c485736c2c8
F test/incrblob.test e50cf41ac64e76ca4f8881ecb8d28fd988503ad5 F test/incrblob.test e50cf41ac64e76ca4f8881ecb8d28fd988503ad5
F test/incrblob2.test c82a780356bdf4d0c77f1adf0ea888248904fc07 F test/incrblob2.test c82a780356bdf4d0c77f1adf0ea888248904fc07
F test/incrblob_err.test c577c91d4ed9e8336cdb188b15d6ee2a6fe9604e F test/incrblob_err.test c577c91d4ed9e8336cdb188b15d6ee2a6fe9604e
@ -637,7 +638,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 1dbced29de5f59ba2ebf877edcadf171540374d1 F tool/speedtest8.c 1dbced29de5f59ba2ebf877edcadf171540374d1
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
P 59d2e89e2181c26b18eac68ccc80ea3018f70a5e P aabde23fe19bd95371fdebdde66ac553dfd53f8e
R 2287b309c0341dad7ec54713b965b29d R 280f60e55a9bc8f499b588b678dbf711
U drh U danielk1977
Z d9d819f707dc5f486bedf318813a096b Z 0496772435641c7d88e45dbde24176e8

View File

@ -1 +1 @@
aabde23fe19bd95371fdebdde66ac553dfd53f8e 803a1736d56b3c07b8ad38715fe0e39196ecc507

View File

@ -12,7 +12,7 @@
** This file contains routines used for analyzing expressions and ** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite. ** for generating VDBE code that evaluates expressions in SQLite.
** **
** $Id: expr.c,v 1.394 2008/09/17 00:13:12 drh Exp $ ** $Id: expr.c,v 1.395 2008/10/02 13:50:56 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include <ctype.h> #include <ctype.h>
@ -1214,11 +1214,13 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
if( eType==0 ){ if( eType==0 ){
int rMayHaveNull = 0; int rMayHaveNull = 0;
eType = IN_INDEX_EPH;
if( prNotFound ){ if( prNotFound ){
*prNotFound = rMayHaveNull = ++pParse->nMem; *prNotFound = rMayHaveNull = ++pParse->nMem;
}else if( pX->pLeft->iColumn<0 && pX->pSelect==0 ){
eType = IN_INDEX_ROWID;
} }
sqlite3CodeSubselect(pParse, pX, rMayHaveNull); sqlite3CodeSubselect(pParse, pX, rMayHaveNull, eType==IN_INDEX_ROWID);
eType = IN_INDEX_EPH;
}else{ }else{
pX->iTable = iTab; pX->iTable = iTab;
} }
@ -1237,9 +1239,20 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
** **
** The pExpr parameter describes the expression that contains the IN ** The pExpr parameter describes the expression that contains the IN
** operator or subquery. ** operator or subquery.
**
** If parameter isRowid is non-zero, then expression pExpr is guaranteed
** to be of the form "<rowid> IN (?, ?, ?)", where <rowid> is a reference
** to some integer key column of a table B-Tree. In this case, use an
** intkey B-Tree to store the set of IN(...) values instead of the usual
** (slower) variable length keys B-Tree.
*/ */
#ifndef SQLITE_OMIT_SUBQUERY #ifndef SQLITE_OMIT_SUBQUERY
void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr, int rMayHaveNull){ void sqlite3CodeSubselect(
Parse *pParse,
Expr *pExpr,
int rMayHaveNull,
int isRowid
){
int testAddr = 0; /* One-time test address */ int testAddr = 0; /* One-time test address */
Vdbe *v = sqlite3GetVdbe(pParse); Vdbe *v = sqlite3GetVdbe(pParse);
if( v==0 ) return; if( v==0 ) return;
@ -1267,12 +1280,13 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr, int rMayHaveNull){
char affinity; char affinity;
KeyInfo keyInfo; KeyInfo keyInfo;
int addr; /* Address of OP_OpenEphemeral instruction */ int addr; /* Address of OP_OpenEphemeral instruction */
Expr *pLeft = pExpr->pLeft;
if( rMayHaveNull ){ if( rMayHaveNull ){
sqlite3VdbeAddOp2(v, OP_Null, 0, rMayHaveNull); sqlite3VdbeAddOp2(v, OP_Null, 0, rMayHaveNull);
} }
affinity = sqlite3ExprAffinity(pExpr->pLeft); affinity = sqlite3ExprAffinity(pLeft);
/* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)' /* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)'
** expression it is handled the same way. A virtual table is ** expression it is handled the same way. A virtual table is
@ -1288,7 +1302,7 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr, int rMayHaveNull){
** is used. ** is used.
*/ */
pExpr->iTable = pParse->nTab++; pExpr->iTable = pParse->nTab++;
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, 1); addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid);
memset(&keyInfo, 0, sizeof(keyInfo)); memset(&keyInfo, 0, sizeof(keyInfo));
keyInfo.nField = 1; keyInfo.nField = 1;
@ -1301,6 +1315,7 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr, int rMayHaveNull){
SelectDest dest; SelectDest dest;
ExprList *pEList; ExprList *pEList;
assert( !isRowid );
sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable); sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
dest.affinity = (int)affinity; dest.affinity = (int)affinity;
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable ); assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
@ -1351,14 +1366,23 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr, int rMayHaveNull){
r3 = sqlite3ExprCodeTarget(pParse, pE2, r1); r3 = sqlite3ExprCodeTarget(pParse, pE2, r1);
assert( pParse->disableColCache>0 ); assert( pParse->disableColCache>0 );
pParse->disableColCache--; pParse->disableColCache--;
sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
sqlite3ExprCacheAffinityChange(pParse, r3, 1); if( isRowid ){
sqlite3VdbeAddOp2(v, OP_IdxInsert, pExpr->iTable, r2); sqlite3VdbeAddOp2(v, OP_Null, 0, r2);
sqlite3VdbeAddOp2(v, OP_MustBeInt, r3, sqlite3VdbeCurrentAddr(v)+2);
sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3);
}else{
sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
sqlite3ExprCacheAffinityChange(pParse, r3, 1);
sqlite3VdbeAddOp2(v, OP_IdxInsert, pExpr->iTable, r2);
}
} }
sqlite3ReleaseTempReg(pParse, r1); sqlite3ReleaseTempReg(pParse, r1);
sqlite3ReleaseTempReg(pParse, r2); sqlite3ReleaseTempReg(pParse, r2);
} }
sqlite3VdbeChangeP4(v, addr, (void *)&keyInfo, P4_KEYINFO); if( !isRowid ){
sqlite3VdbeChangeP4(v, addr, (void *)&keyInfo, P4_KEYINFO);
}
break; break;
} }
@ -2026,7 +2050,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
testcase( op==TK_EXISTS ); testcase( op==TK_EXISTS );
testcase( op==TK_SELECT ); testcase( op==TK_SELECT );
if( pExpr->iColumn==0 ){ if( pExpr->iColumn==0 ){
sqlite3CodeSubselect(pParse, pExpr, 0); sqlite3CodeSubselect(pParse, pExpr, 0, 0);
} }
inReg = pExpr->iColumn; inReg = pExpr->iColumn;
break; break;
@ -2109,7 +2133,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
sqlite3VdbeJumpHere(v, j3); sqlite3VdbeJumpHere(v, j3);
/* Copy the value of register rNotFound (which is either NULL or 0) /* Copy the value of register rNotFound (which is either NULL or 0)
** into the target register. This will be the result of the ** into the target register. This will be the result of the
** expression. ** expression.
*/ */
sqlite3VdbeAddOp2(v, OP_Copy, rNotFound, target); sqlite3VdbeAddOp2(v, OP_Copy, rNotFound, target);

View File

@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** Internal interface definitions for SQLite. ** Internal interface definitions for SQLite.
** **
** @(#) $Id: sqliteInt.h,v 1.772 2008/09/12 16:03:48 drh Exp $ ** @(#) $Id: sqliteInt.h,v 1.773 2008/10/02 13:50:56 danielk1977 Exp $
*/ */
#ifndef _SQLITEINT_H_ #ifndef _SQLITEINT_H_
#define _SQLITEINT_H_ #define _SQLITEINT_H_
@ -2367,7 +2367,7 @@ void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
int sqlite3GetToken(const unsigned char *, int *); int sqlite3GetToken(const unsigned char *, int *);
void sqlite3NestedParse(Parse*, const char*, ...); void sqlite3NestedParse(Parse*, const char*, ...);
void sqlite3ExpirePreparedStatements(sqlite3*); void sqlite3ExpirePreparedStatements(sqlite3*);
void sqlite3CodeSubselect(Parse *, Expr *, int); void sqlite3CodeSubselect(Parse *, Expr *, int, int);
void sqlite3SelectPrep(Parse*, Select*, NameContext*); void sqlite3SelectPrep(Parse*, Select*, NameContext*);
int sqlite3ResolveExprNames(NameContext*, Expr*); int sqlite3ResolveExprNames(NameContext*, Expr*);
void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*);

93
test/in4.test Normal file
View File

@ -0,0 +1,93 @@
# 2008 September 1
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# $Id: in4.test,v 1.1 2008/10/02 13:50:56 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_test in4-1.1 {
execsql {
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a);
}
} {}
do_test in4-1.2 {
execsql {
SELECT * FROM t1 WHERE a IN ('aaa', 'bbb', 'ccc');
}
} {}
do_test in4-1.3 {
execsql {
INSERT INTO t1 VALUES('aaa', 1);
INSERT INTO t1 VALUES('ddd', 2);
INSERT INTO t1 VALUES('ccc', 3);
INSERT INTO t1 VALUES('eee', 4);
SELECT b FROM t1 WHERE a IN ('aaa', 'bbb', 'ccc');
}
} {1 3}
do_test in4-1.4 {
execsql {
SELECT a FROM t1 WHERE rowid IN (1, 3);
}
} {aaa ccc}
do_test in4-1.5 {
execsql {
SELECT a FROM t1 WHERE rowid IN ();
}
} {}
do_test in4-1.6 {
execsql {
SELECT a FROM t1 WHERE a IN ('ddd');
}
} {ddd}
do_test in4-2.1 {
execsql {
CREATE TABLE t2(a INTEGER PRIMARY KEY, b TEXT);
INSERT INTO t2 VALUES(-1, '-one');
INSERT INTO t2 VALUES(0, 'zero');
INSERT INTO t2 VALUES(1, 'one');
INSERT INTO t2 VALUES(2, 'two');
INSERT INTO t2 VALUES(3, 'three');
}
} {}
do_test in4-2.2 {
execsql { SELECT b FROM t2 WHERE a IN (0, 2) }
} {zero two}
do_test in4-2.3 {
execsql { SELECT b FROM t2 WHERE a IN (2, 0) }
} {zero two}
do_test in4-2.4 {
execsql { SELECT b FROM t2 WHERE a IN (2, -1) }
} {-one two}
do_test in4-2.5 {
execsql { SELECT b FROM t2 WHERE a IN (NULL, 3) }
} {three}
do_test in4-2.6 {
execsql { SELECT b FROM t2 WHERE a IN (1.0, 2.1) }
} {one}
do_test in4-2.7 {
execsql { SELECT b FROM t2 WHERE a IN ('1', '2') }
} {one two}
do_test in4-2.8 {
execsql { SELECT b FROM t2 WHERE a IN ('', '0.0.0', '2') }
} {two}
finish_test