mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-05 15:55:57 +03:00
Enable the OR optimization for WITHOUT ROWID tables. Use a temp table instead of the RowSet object to track the rows that have already been included in the result set.
FossilOrigin-Name: 2c7e277bbebd5c93dec53d381d9737909d40d846
This commit is contained in:
16
manifest
16
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Fix\sa\sproblem\sin\sthe\sshell\swhen\simporting\sCSV\sfiles.\sIf\sthe\sleftmost\sfield\sof\sthe\sfirst\srow\sin\sthe\sCSV\sfile\swas\sboth\szero\sbytes\sin\ssize\sand\sunquoted,\sno\sdata\swas\simported.
|
C Enable\sthe\sOR\soptimization\sfor\sWITHOUT\sROWID\stables.\sUse\sa\stemp\stable\sinstead\sof\sthe\sRowSet\sobject\sto\strack\sthe\srows\sthat\shave\salready\sbeen\sincluded\sin\sthe\sresult\sset.
|
||||||
D 2014-05-26T18:27:12.472
|
D 2014-05-26T20:06:45.481
|
||||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||||
F Makefile.in dd2b1aba364ff9b05de41086f74407f285c57670
|
F Makefile.in dd2b1aba364ff9b05de41086f74407f285c57670
|
||||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||||
@@ -294,7 +294,7 @@ F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd
|
|||||||
F src/wal.c 76e7fc6de229bea8b30bb2539110f03a494dc3a8
|
F src/wal.c 76e7fc6de229bea8b30bb2539110f03a494dc3a8
|
||||||
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
||||||
F src/walker.c 11edb74d587bc87b33ca96a5173e3ec1b8389e45
|
F src/walker.c 11edb74d587bc87b33ca96a5173e3ec1b8389e45
|
||||||
F src/where.c 9d351a5ecefdcc104d0bb7d9b5631dce4d60757c
|
F src/where.c a911a484ed86fc4a3d52219c8adb64b19f7afeb7
|
||||||
F src/whereInt.h 6804c2e5010378568c2bb1350477537755296a46
|
F src/whereInt.h 6804c2e5010378568c2bb1350477537755296a46
|
||||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||||
@@ -1106,6 +1106,7 @@ F test/whereE.test b3a055eef928c992b0a33198a7b8dc10eea5ad2f
|
|||||||
F test/whereF.test 5b2ba0dbe8074aa13e416b37c753991f0a2492d7
|
F test/whereF.test 5b2ba0dbe8074aa13e416b37c753991f0a2492d7
|
||||||
F test/whereG.test 0ac23e5e8311b69d87245f4a85112de321031658
|
F test/whereG.test 0ac23e5e8311b69d87245f4a85112de321031658
|
||||||
F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2
|
F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2
|
||||||
|
F test/whereI.test 6cc613df883a17ca3df36021c6d6e514f5f3bd23
|
||||||
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
|
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
|
||||||
F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
|
F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
|
||||||
F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
|
F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
|
||||||
@@ -1172,7 +1173,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
|||||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||||
F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
|
F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
|
||||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||||
P d90c4964fcf46b0b043dbfd58267098582267001
|
P 856d44a206d82e96265103556dedda39ca3602b1
|
||||||
R 4f49cf43bba70559a864b8b79fde7303
|
R 9e568de169ca65397feb1345691b1a68
|
||||||
|
T *branch * without-rowid-or-opt
|
||||||
|
T *sym-without-rowid-or-opt *
|
||||||
|
T -sym-trunk *
|
||||||
U dan
|
U dan
|
||||||
Z b7657af7a09e4e39091c8340eac623cc
|
Z 2a22ab55186c5c60531d14f3d4d18429
|
||||||
|
@@ -1 +1 @@
|
|||||||
856d44a206d82e96265103556dedda39ca3602b1
|
2c7e277bbebd5c93dec53d381d9737909d40d846
|
64
src/where.c
64
src/where.c
@@ -3307,6 +3307,7 @@ static Bitmask codeOneLoopStart(
|
|||||||
int untestedTerms = 0; /* Some terms not completely tested */
|
int untestedTerms = 0; /* Some terms not completely tested */
|
||||||
int ii; /* Loop counter */
|
int ii; /* Loop counter */
|
||||||
Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
|
Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
|
||||||
|
Table *pTab = pTabItem->pTab;
|
||||||
|
|
||||||
pTerm = pLoop->aLTerm[0];
|
pTerm = pLoop->aLTerm[0];
|
||||||
assert( pTerm!=0 );
|
assert( pTerm!=0 );
|
||||||
@@ -3350,9 +3351,16 @@ static Bitmask codeOneLoopStart(
|
|||||||
** called on an uninitialized cursor.
|
** called on an uninitialized cursor.
|
||||||
*/
|
*/
|
||||||
if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
|
if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
|
||||||
regRowset = ++pParse->nMem;
|
if( HasRowid(pTab) ){
|
||||||
|
regRowset = ++pParse->nMem;
|
||||||
|
sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset);
|
||||||
|
}else{
|
||||||
|
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||||
|
regRowset = pParse->nTab++;
|
||||||
|
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, regRowset, pPk->nKeyCol);
|
||||||
|
sqlite3VdbeSetP4KeyInfo(pParse, pPk);
|
||||||
|
}
|
||||||
regRowid = ++pParse->nMem;
|
regRowid = ++pParse->nMem;
|
||||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset);
|
|
||||||
}
|
}
|
||||||
iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn);
|
iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn);
|
||||||
|
|
||||||
@@ -3408,13 +3416,52 @@ static Bitmask codeOneLoopStart(
|
|||||||
pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
|
pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
|
||||||
);
|
);
|
||||||
if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
|
if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
|
||||||
int iSet = ((ii==pOrWc->nTerm-1)?-1:ii);
|
|
||||||
int r;
|
int r;
|
||||||
r = sqlite3ExprCodeGetColumn(pParse, pTabItem->pTab, -1, iCur,
|
int addr; /* Address just past Gosub coded below */
|
||||||
regRowid, 0);
|
int iSet = ((ii==pOrWc->nTerm-1)?-1:ii);
|
||||||
sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset,
|
if( HasRowid(pTab) ){
|
||||||
sqlite3VdbeCurrentAddr(v)+2, r, iSet);
|
r = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, regRowid, 0);
|
||||||
VdbeCoverage(v);
|
addr = sqlite3VdbeCurrentAddr(v)+2;
|
||||||
|
sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset, addr, r, iSet);
|
||||||
|
VdbeCoverage(v);
|
||||||
|
}else{
|
||||||
|
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||||
|
int nPk = pPk->nKeyCol;
|
||||||
|
int iPk;
|
||||||
|
|
||||||
|
/* Read the PK into an array of temp registers. */
|
||||||
|
r = sqlite3GetTempRange(pParse, nPk);
|
||||||
|
for(iPk=0; iPk<nPk; iPk++){
|
||||||
|
int iCol = pPk->aiColumn[iPk];
|
||||||
|
sqlite3ExprCodeGetColumn(pParse, pTab, iCol, iCur, r+iPk, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if the temp table already contains this key. If so,
|
||||||
|
** the row has already been included in the result set and
|
||||||
|
** can be ignored (by jumping past the Gosub below). Otherwise,
|
||||||
|
** insert the key into the temp table and proceed with processing
|
||||||
|
** the row.
|
||||||
|
**
|
||||||
|
** Use some of the same optimizations as OP_RowSetTest: If iSet
|
||||||
|
** is zero, assume that the key cannot already be present in
|
||||||
|
** the temp table. And if iSet is -1, assume that there is no
|
||||||
|
** need to insert the key into the temp table, as it will never
|
||||||
|
** be tested for. */
|
||||||
|
if( iSet ){
|
||||||
|
addr = sqlite3VdbeCurrentAddr(v) + 2 + ((iSet>0) ? 2 : 0);
|
||||||
|
sqlite3VdbeAddOp4Int(v, OP_Found, regRowset, addr, r, nPk);
|
||||||
|
}
|
||||||
|
if( iSet>=0 ){
|
||||||
|
sqlite3VdbeAddOp3(v, OP_MakeRecord, r, nPk, regRowid);
|
||||||
|
sqlite3VdbeAddOp3(v, OP_IdxInsert, regRowset, regRowid, 0);
|
||||||
|
if( iSet ) sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Release the array of temp registers */
|
||||||
|
sqlite3ReleaseTempRange(pParse, r, nPk);
|
||||||
|
}
|
||||||
|
assert( db->mallocFailed
|
||||||
|
|| iSet==0 || addr==(sqlite3VdbeCurrentAddr(v)+1) );
|
||||||
}
|
}
|
||||||
sqlite3VdbeAddOp2(v, OP_Gosub, regReturn, iLoopBody);
|
sqlite3VdbeAddOp2(v, OP_Gosub, regReturn, iLoopBody);
|
||||||
|
|
||||||
@@ -4766,7 +4813,6 @@ static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){
|
|||||||
pNew = pBuilder->pNew;
|
pNew = pBuilder->pNew;
|
||||||
memset(&sSum, 0, sizeof(sSum));
|
memset(&sSum, 0, sizeof(sSum));
|
||||||
pItem = pWInfo->pTabList->a + pNew->iTab;
|
pItem = pWInfo->pTabList->a + pNew->iTab;
|
||||||
if( !HasRowid(pItem->pTab) ) return SQLITE_OK;
|
|
||||||
iCur = pItem->iCursor;
|
iCur = pItem->iCursor;
|
||||||
|
|
||||||
for(pTerm=pWC->a; pTerm<pWCEnd && rc==SQLITE_OK; pTerm++){
|
for(pTerm=pWC->a; pTerm<pWCEnd && rc==SQLITE_OK; pTerm++){
|
||||||
|
74
test/whereI.test
Normal file
74
test/whereI.test
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# 2014-03-31
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
#***********************************************************************
|
||||||
|
# The focus of this file is testing the OR optimization on WITHOUT ROWID
|
||||||
|
# tables.
|
||||||
|
#
|
||||||
|
|
||||||
|
set testdir [file dirname $argv0]
|
||||||
|
source $testdir/tester.tcl
|
||||||
|
set ::testprefix whereI
|
||||||
|
|
||||||
|
do_execsql_test 1.0 {
|
||||||
|
CREATE TABLE t1(a, b, c, PRIMARY KEY(a)) WITHOUT ROWID;
|
||||||
|
INSERT INTO t1 VALUES(1, 'a', 'z');
|
||||||
|
INSERT INTO t1 VALUES(2, 'b', 'y');
|
||||||
|
INSERT INTO t1 VALUES(3, 'c', 'x');
|
||||||
|
INSERT INTO t1 VALUES(4, 'd', 'w');
|
||||||
|
CREATE INDEX i1 ON t1(b);
|
||||||
|
CREATE INDEX i2 ON t1(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_eqp_test 1.1 {
|
||||||
|
SELECT a FROM t1 WHERE b='b' OR c='x'
|
||||||
|
} {
|
||||||
|
0 0 0 {SEARCH TABLE t1 USING INDEX i1 (b=?)}
|
||||||
|
0 0 0 {SEARCH TABLE t1 USING INDEX i2 (c=?)}
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 1.2 {
|
||||||
|
SELECT a FROM t1 WHERE b='b' OR c='x'
|
||||||
|
} {2 3}
|
||||||
|
|
||||||
|
do_execsql_test 1.3 {
|
||||||
|
SELECT a FROM t1 WHERE b='a' OR c='z'
|
||||||
|
} {1}
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
# Try that again, this time with non integer PRIMARY KEY values.
|
||||||
|
#
|
||||||
|
do_execsql_test 2.0 {
|
||||||
|
CREATE TABLE t2(a, b, c, PRIMARY KEY(a)) WITHOUT ROWID;
|
||||||
|
INSERT INTO t2 VALUES('i', 'a', 'z');
|
||||||
|
INSERT INTO t2 VALUES('ii', 'b', 'y');
|
||||||
|
INSERT INTO t2 VALUES('iii', 'c', 'x');
|
||||||
|
INSERT INTO t2 VALUES('iv', 'd', 'w');
|
||||||
|
CREATE INDEX i3 ON t2(b);
|
||||||
|
CREATE INDEX i4 ON t2(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_eqp_test 2.1 {
|
||||||
|
SELECT a FROM t2 WHERE b='b' OR c='x'
|
||||||
|
} {
|
||||||
|
0 0 0 {SEARCH TABLE t2 USING INDEX i3 (b=?)}
|
||||||
|
0 0 0 {SEARCH TABLE t2 USING INDEX i4 (c=?)}
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 2.2 {
|
||||||
|
SELECT a FROM t2 WHERE b='b' OR c='x'
|
||||||
|
} {ii iii}
|
||||||
|
|
||||||
|
do_execsql_test 2.3 {
|
||||||
|
SELECT a FROM t2 WHERE b='a' OR c='z'
|
||||||
|
} {i}
|
||||||
|
|
||||||
|
finish_test
|
||||||
|
|
||||||
|
|
Reference in New Issue
Block a user