1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-08 14:02:16 +03:00

Revisiting the IN-scan optimization to try to fix it for the corner case

where the statistics deceive the query planner into using a scan when
an indexed lookup would be better.  This check-in changes the code
generation to do the IN-scan using a new OP_SeekScan opcode.  That new
opcode is designed to abandon the scan and fall back to a seek if
it doesn't find a match quickly enough.  For this work-in-progress check-in, 
OP_SeekScan is still a no-op and OP_SeekGE still ends up doing all the work.

FossilOrigin-Name: d720b6981eeb0ffdb14494ca63eca298ee724ae4ad4863c7c7cbfdad7fa52519
This commit is contained in:
drh
2020-09-28 19:51:54 +00:00
parent 75fa266341
commit 68cf0ace3d
6 changed files with 74 additions and 11 deletions

View File

@@ -4383,6 +4383,59 @@ seek_not_found:
break;
}
/* Opcode: SeekScan
** Synopsis: Scan-ahead up to P1 rows
**
** This opcode is a prefix. It must be followed immediately by
** OP_SeekGE and then OP_IdxGT. This opcode should occur in no other
** context. That constraint is verified using assert() statements in
** the code.
**
** This opcode helps to optimize IN operators on a multi-column index
** where the IN operator is on the later terms of the index.
**
** The P3 and P4 operations of the OP_SeekGE opcode that follows this
** opcode identify an unpacked key which is the desired entry that
** we want to advance the cursor to. Call this the "target".
**
** If the OP_SeekGE opcode that immediately follows this opcode has
** never run before, then this opcode is a no-op and control passes
** through into the OP_SeekGE.
**
** If the subsequent OP_SeekGE opcode has run before, then that prior
** might OP_SeekGE might have left the cursor pointing any entry that
** is close to the target. This routine checks, and if possible
** bypasses the OP_SeekGE.
**
** If the cursor is past the target, jump immediately to the
** P2 of the subsequent OP_SeekGE.
**
** If the cursor is less than the target, then step forward up to P1
** times trying to find a match. If during these steps, the
** cursor moves past the target, then jump immediately to
** the P2 of the subsequent OP_SeekGE. If a match is found, jump
** to the first instruction past the OP_IdxGT that follows the
** OP_SeekGE. (In other words, skip over the next two opcodes).
** If P1 steps are performed and the cursor is still less than the
** target, then fall through into OP_SeekGE opcode.
**
** This opcode is an optimization. This opcode can be a no-op and
** the correct answer should still be obtained. The purpose of this
** opcode is to bypass unnecessary OP_SeekGE operations.
*/
case OP_SeekScan: {
assert( pOp[1].opcode==OP_SeekGE );
assert( pOp[2].opcode==OP_IdxGT );
assert( pOp[1].p1==pOp[2].p1 );
assert( pOp[1].p2==pOp[2].p2 );
assert( pOp[1].p3==pOp[2].p3 );
assert( pOp[1].p4.i==pOp[2].p4.i );
assert( pOp->p1>0 );
break; /* No-op for now. FIX ME. */
}
/* Opcode: SeekHit P1 P2 P3 * *
** Synopsis: set P2<=seekHit<=P3
**