mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-27 20:41:58 +03:00
If a table is on the rhs of a LEFT JOIN, include only terms from the joins ON(...) clause in the cursor-hint passed via OP_CursorHint.
FossilOrigin-Name: 2a2346b04235c6d0b7a8e64c92ee31018285c29f
This commit is contained in:
16
manifest
16
manifest
@ -1,5 +1,5 @@
|
||||
C Add\sa\smissing\sOP_ColumnsUsed\sopcode\sto\scode\sfor\sexpressions\slike\s"?\sIN\s(SELECT\s...)"\sin\scases\swhere\sexpression\scan\suse\san\sindex\sthat\smay\scontain\sNULL\svalues.
|
||||
D 2016-06-16T17:14:02.375
|
||||
C If\sa\stable\sis\son\sthe\srhs\sof\sa\sLEFT\sJOIN,\sinclude\sonly\sterms\sfrom\sthe\sjoins\sON(...)\sclause\sin\sthe\scursor-hint\spassed\svia\sOP_CursorHint.
|
||||
D 2016-06-17T14:33:32.727
|
||||
F Makefile.in f3f7d2060ce03af4584e711ef3a626ef0b1d6340
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc 50149765ef72f4e652b9a0f1f6462c4784bb9423
|
||||
@ -464,7 +464,7 @@ F src/wal.h 2f7c831cf3b071fa548bf2d5cac640846a7ff19c
|
||||
F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354
|
||||
F src/where.c 74f0798525b6306682d7234f230ea93f86959b9b
|
||||
F src/whereInt.h e5b939701a7ceffc5a3a8188a37f9746416ebcd0
|
||||
F src/wherecode.c ba71a4e4bada29aa9842200e6299714bf18c812c
|
||||
F src/wherecode.c b9d5f7d8ebed2e09376fd785a111ffd9dfd59ffc
|
||||
F src/whereexpr.c c32d47085dbaca0b8fd013210f56693c7d220d48
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
||||
@ -617,6 +617,7 @@ F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c
|
||||
F test/csv01.test 0929a9ce47021519512be92861f29e32d2538e5f
|
||||
F test/ctime.test 7bd009071e242aac4f18521581536b652b789a47
|
||||
F test/cursorhint.test 7bc346788390475e77a345da2b92270d04d35856
|
||||
F test/cursorhint2.test 532fdc678653f73e30770b59af0a47f9af0124ee
|
||||
F test/date.test 984ac1e3e5e031386866f034006148d3972b4a65
|
||||
F test/dbstatus.test 8de104bb5606f19537d23cd553b41349b5ab1204
|
||||
F test/dbstatus2.test e93ab03bfae6d62d4d935f20de928c19ca0ed0ab
|
||||
@ -1501,7 +1502,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 48b555c42de1cbc031fb6c2c93ef170e491c7d76
|
||||
R 7c056b92500e212b6fa7144618e1e6e0
|
||||
P 0b1579caf06a2c42433b8bc9dc28c9ad381aa07c
|
||||
R 2d1831333befb414525bf922ba35c127
|
||||
T *branch * cursor-hints
|
||||
T *sym-cursor-hints *
|
||||
T -sym-trunk *
|
||||
U dan
|
||||
Z 6cf13efbb72ea4474e244da36e8952f1
|
||||
Z b45fbbd37d7e68d5f1357e6143b6a1f1
|
||||
|
@ -1 +1 @@
|
||||
0b1579caf06a2c42433b8bc9dc28c9ad381aa07c
|
||||
2a2346b04235c6d0b7a8e64c92ee31018285c29f
|
@ -678,6 +678,7 @@ static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){
|
||||
** Insert an OP_CursorHint instruction if it is appropriate to do so.
|
||||
*/
|
||||
static void codeCursorHint(
|
||||
struct SrcList_item *pTabItem, /* FROM clause item */
|
||||
WhereInfo *pWInfo, /* The where clause */
|
||||
WhereLevel *pLevel, /* Which loop to provide hints for */
|
||||
WhereTerm *pEndRange /* Hint this end-of-scan boundary term if not NULL */
|
||||
@ -708,7 +709,31 @@ static void codeCursorHint(
|
||||
pTerm = &pWC->a[i];
|
||||
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
|
||||
if( pTerm->prereqAll & pLevel->notReady ) continue;
|
||||
|
||||
/* Any terms specified as part of the ON(...) clause for any LEFT
|
||||
** JOIN for which the current table is not the rhs are omitted
|
||||
** from the cursor-hint.
|
||||
**
|
||||
** If this table is the rhs of a LEFT JOIN, only terms that were
|
||||
** specified as part of the ON(...) clause may be included in the
|
||||
** hint. This is to address the following:
|
||||
**
|
||||
** SELECT ... t1 LEFT JOIN t2 ON (t1.a=t2.b) WHERE t2.c IS NULL;
|
||||
**
|
||||
** If the (t2.c IS NULL) constraint is pushed down to the cursor, it
|
||||
** might filter out all rows that match (t1.a=t2.b), causing SQLite
|
||||
** to add a row of NULL values to the output that should not be
|
||||
** present (since the ON clause does actually match rows within t2).
|
||||
*/
|
||||
if( pTabItem->fg.jointype & JT_LEFT ){
|
||||
if( !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
|
||||
|| pTerm->pExpr->iRightJoinTable!=pTabItem->iCursor
|
||||
){
|
||||
continue;
|
||||
}
|
||||
}else{
|
||||
if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) continue;
|
||||
}
|
||||
|
||||
/* All terms in pWLoop->aLTerm[] except pEndRange are used to initialize
|
||||
** the cursor. These terms are not needed as hints for a pure range
|
||||
@ -742,7 +767,7 @@ static void codeCursorHint(
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define codeCursorHint(A,B,C) /* No-op */
|
||||
# define codeCursorHint(A,B,C,D) /* No-op */
|
||||
#endif /* SQLITE_ENABLE_CURSOR_HINTS */
|
||||
|
||||
/*
|
||||
@ -998,7 +1023,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
pStart = pEnd;
|
||||
pEnd = pTerm;
|
||||
}
|
||||
codeCursorHint(pWInfo, pLevel, pEnd);
|
||||
codeCursorHint(pTabItem, pWInfo, pLevel, pEnd);
|
||||
if( pStart ){
|
||||
Expr *pX; /* The expression that defines the start bound */
|
||||
int r1, rTemp; /* Registers for holding the start boundary */
|
||||
@ -1212,7 +1237,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
** and store the values of those terms in an array of registers
|
||||
** starting at regBase.
|
||||
*/
|
||||
codeCursorHint(pWInfo, pLevel, pRangeEnd);
|
||||
codeCursorHint(pTabItem, pWInfo, pLevel, pRangeEnd);
|
||||
regBase = codeAllEqualityTerms(pParse,pLevel,bRev,nExtraReg,&zStartAff);
|
||||
assert( zStartAff==0 || sqlite3Strlen30(zStartAff)>=nEq );
|
||||
if( zStartAff ) cEndAff = zStartAff[nEq];
|
||||
@ -1660,7 +1685,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
** a pseudo-cursor. No need to Rewind or Next such cursors. */
|
||||
pLevel->op = OP_Noop;
|
||||
}else{
|
||||
codeCursorHint(pWInfo, pLevel, 0);
|
||||
codeCursorHint(pTabItem, pWInfo, pLevel, 0);
|
||||
pLevel->op = aStep[bRev];
|
||||
pLevel->p1 = iCur;
|
||||
pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk);
|
||||
|
101
test/cursorhint2.test
Normal file
101
test/cursorhint2.test
Normal file
@ -0,0 +1,101 @@
|
||||
# 2016 June 17
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus is on testing that cursor-hints are correct for queries
|
||||
# involving LEFT JOIN.
|
||||
#
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix cursorhint2
|
||||
|
||||
ifcapable !cursorhints {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
proc extract_hints {sql} {
|
||||
|
||||
db eval "SELECT tbl_name, rootpage FROM sqlite_master where rootpage" {
|
||||
set lookup($rootpage) $tbl_name
|
||||
}
|
||||
|
||||
set ret [list]
|
||||
db eval "EXPLAIN $sql" a {
|
||||
switch -- $a(opcode) {
|
||||
OpenRead {
|
||||
set csr($a(p1)) $lookup($a(p2))
|
||||
}
|
||||
CursorHint {
|
||||
lappend ret $csr($a(p1)) $a(p4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set ret
|
||||
}
|
||||
|
||||
proc do_extract_hints_test {tn sql ret} {
|
||||
uplevel [list do_test $tn [list extract_hints $sql] [list {*}$ret]]
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
PRAGMA automatic_index = 0;
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(c, d);
|
||||
CREATE TABLE t3(e, f);
|
||||
}
|
||||
|
||||
do_extract_hints_test $tn.1.1 {
|
||||
SELECT * FROM t1 WHERE a=1;
|
||||
} {
|
||||
t1 EQ(c0,1)
|
||||
}
|
||||
|
||||
do_extract_hints_test $tn.1.2 {
|
||||
SELECT * FROM t1 CROSS JOIN t2 ON (a=c) WHERE d IS NULL;
|
||||
} {
|
||||
t2 {AND(ISNULL(c1),EQ(r[1],c0))}
|
||||
}
|
||||
|
||||
do_extract_hints_test $tn.1.3 {
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON (a=c) WHERE d IS NULL;
|
||||
} {
|
||||
t2 {EQ(r[2],c0)}
|
||||
}
|
||||
|
||||
do_extract_hints_test $tn.1.4 {
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON (a=c AND a=10) WHERE d IS NULL;
|
||||
} {
|
||||
t2 {AND(EQ(r[2],c0),EQ(r[3],10))}
|
||||
}
|
||||
|
||||
do_extract_hints_test $tn.1.5 {
|
||||
SELECT * FROM t1 CROSS JOIN t2 ON (a=c AND a=10) WHERE d IS NULL;
|
||||
} {
|
||||
t1 EQ(c0,10) t2 {AND(ISNULL(c1),EQ(r[3],c0))}
|
||||
}
|
||||
|
||||
do_extract_hints_test $tn.1.6 {
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON (a=c) LEFT JOIN t3 ON (d=f);
|
||||
} {
|
||||
t2 {EQ(r[2],c0)} t3 {EQ(r[6],c1)}
|
||||
}
|
||||
|
||||
do_extract_hints_test $tn.1.6 {
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON (a=c AND d=e) LEFT JOIN t3 ON (d=f);
|
||||
} {
|
||||
t2 {EQ(r[2],c0)} t3 {EQ(r[6],c1)}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
Reference in New Issue
Block a user