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:
17
manifest
17
manifest
@ -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
|
||||||
|
@ -1 +1 @@
|
|||||||
aabde23fe19bd95371fdebdde66ac553dfd53f8e
|
803a1736d56b3c07b8ad38715fe0e39196ecc507
|
48
src/expr.c
48
src/expr.c
@ -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);
|
||||||
|
@ -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
93
test/in4.test
Normal 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
|
||||||
|
|
Reference in New Issue
Block a user