mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
Correctly handle NULLs in IN operators. Ticket #2273.
The changes in where.c and in the WhereLevel.aInLoop structure are not strictly necessary to fix this problem - they just make the code easier to read. Only the change in OP_Next/OP_Prev operator of vdbe.c is required. (CVS 3735) FossilOrigin-Name: 26348556d824c032851e409ac510cddb55c200bf
This commit is contained in:
18
manifest
18
manifest
@ -1,5 +1,5 @@
|
|||||||
C Update\scomments\sin\ssqlite3.h.\s\sNo\schanges\sto\scode.\s(CVS\s3734)
|
C Correctly\shandle\sNULLs\sin\sIN\soperators.\s\sTicket\s#2273.\nThe\schanges\sin\swhere.c\sand\sin\sthe\sWhereLevel.aInLoop\sstructure\sare\nnot\sstrictly\snecessary\sto\sfix\sthis\sproblem\s-\sthey\sjust\smake\sthe\scode\neasier\sto\sread.\s\sOnly\sthe\schange\sin\sOP_Next/OP_Prev\soperator\sof\svdbe.c\nis\srequired.\s(CVS\s3735)
|
||||||
D 2007-03-28T13:07:41
|
D 2007-03-28T14:30:07
|
||||||
F Makefile.in 1fe3d0b46e40fd684e1e61f8e8056cefed16de9f
|
F Makefile.in 1fe3d0b46e40fd684e1e61f8e8056cefed16de9f
|
||||||
F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
|
F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
|
||||||
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
|
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
|
||||||
@ -98,7 +98,7 @@ F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96
|
|||||||
F src/shell.c 3ae4654560e91220a95738a73d135d91d937cda1
|
F src/shell.c 3ae4654560e91220a95738a73d135d91d937cda1
|
||||||
F src/sqlite.h.in 02d1159bc8f7387008df9766c79038fce8a9d3a7
|
F src/sqlite.h.in 02d1159bc8f7387008df9766c79038fce8a9d3a7
|
||||||
F src/sqlite3ext.h 832e13de075d920e2c76584e2b7af1054bb212df
|
F src/sqlite3ext.h 832e13de075d920e2c76584e2b7af1054bb212df
|
||||||
F src/sqliteInt.h 7a3d16cd517dfce73eeac10963275454d6421f82
|
F src/sqliteInt.h ace601a40db30d667422c79a7db57e7dabb48038
|
||||||
F src/table.c 6d0da66dde26ee75614ed8f584a1996467088d06
|
F src/table.c 6d0da66dde26ee75614ed8f584a1996467088d06
|
||||||
F src/tclsqlite.c cd2b3b86ab07c0e0779f6c6e71e72c6c7dc1e704
|
F src/tclsqlite.c cd2b3b86ab07c0e0779f6c6e71e72c6c7dc1e704
|
||||||
F src/test1.c 439bba8da10fbc61c731019cf2894e6057578878
|
F src/test1.c 439bba8da10fbc61c731019cf2894e6057578878
|
||||||
@ -122,7 +122,7 @@ F src/update.c 876b3142b8db9ce6ddf8374a2ced65e576acabc7
|
|||||||
F src/utf.c 67ecb1032bc0b42c105e88d65ef9d9f626eb0e1f
|
F src/utf.c 67ecb1032bc0b42c105e88d65ef9d9f626eb0e1f
|
||||||
F src/util.c 8e8180ee5597f2474c1da311ff3c464b6966c0f1
|
F src/util.c 8e8180ee5597f2474c1da311ff3c464b6966c0f1
|
||||||
F src/vacuum.c 8bd895d29e7074e78d4e80f948e35ddc9cf2beef
|
F src/vacuum.c 8bd895d29e7074e78d4e80f948e35ddc9cf2beef
|
||||||
F src/vdbe.c b68f3a86177fa4d10512a4e276a8961d5c1e2ccf
|
F src/vdbe.c 9fc4520600f364ec510e7c74733dc2b77430b92a
|
||||||
F src/vdbe.h 0025259af1939fb264a545816c69e4b5b8d52691
|
F src/vdbe.h 0025259af1939fb264a545816c69e4b5b8d52691
|
||||||
F src/vdbeInt.h b2ca85ca8abfbba86e380b06c7673dc81c6784c3
|
F src/vdbeInt.h b2ca85ca8abfbba86e380b06c7673dc81c6784c3
|
||||||
F src/vdbeapi.c 6cff63a5b3a52af04b2bef0f7e27ed7ea6f85183
|
F src/vdbeapi.c 6cff63a5b3a52af04b2bef0f7e27ed7ea6f85183
|
||||||
@ -130,7 +130,7 @@ F src/vdbeaux.c 1a50dce809a2f5c2d32e8096166b59016dab1b45
|
|||||||
F src/vdbefifo.c 3ca8049c561d5d67cbcb94dc909ae9bb68c0bf8f
|
F src/vdbefifo.c 3ca8049c561d5d67cbcb94dc909ae9bb68c0bf8f
|
||||||
F src/vdbemem.c 58a8be2231b0b13e2e77098debe173e79d22f791
|
F src/vdbemem.c 58a8be2231b0b13e2e77098debe173e79d22f791
|
||||||
F src/vtab.c 7fbda947e28cbe7adb3ba752a76ca9ef29936750
|
F src/vtab.c 7fbda947e28cbe7adb3ba752a76ca9ef29936750
|
||||||
F src/where.c 0ef9acc43cc696b01a3f1019bc07c8d8e0849999
|
F src/where.c df68bcbb07add015358a91b8412e2ea19dffdbc6
|
||||||
F tclinstaller.tcl 046e3624671962dc50f0481d7c25b38ef803eb42
|
F tclinstaller.tcl 046e3624671962dc50f0481d7c25b38ef803eb42
|
||||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||||
F test/all.test 60267b055e82de4fb8b841eabb014bc2f836a4eb
|
F test/all.test 60267b055e82de4fb8b841eabb014bc2f836a4eb
|
||||||
@ -367,7 +367,7 @@ F test/vtab_err.test 224cc80ad700797c48b9cd2c1e0bd7a8517d8609
|
|||||||
F test/where.test 1d020f50c77f37b2dbab9766ca959e6e3278ecdb
|
F test/where.test 1d020f50c77f37b2dbab9766ca959e6e3278ecdb
|
||||||
F test/where2.test 3249d426b3fc7a106713d784e1628307fc308d2e
|
F test/where2.test 3249d426b3fc7a106713d784e1628307fc308d2e
|
||||||
F test/where3.test 0a30fe9808b0fa01c46d0fcf4fac0bf6cf75bb30
|
F test/where3.test 0a30fe9808b0fa01c46d0fcf4fac0bf6cf75bb30
|
||||||
F test/where4.test 3fcf53c5ea7af1db3980b3293c2a45b56605f26a
|
F test/where4.test b68496500bff496e83e76ae4ffb493b99064eac6
|
||||||
F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
|
F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
|
||||||
F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439
|
F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439
|
||||||
F tool/lemon.c d3dff81e31c459dd18784d024a759b7229f66297
|
F tool/lemon.c d3dff81e31c459dd18784d024a759b7229f66297
|
||||||
@ -442,7 +442,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
|
|||||||
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
|
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
|
||||||
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
|
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
|
||||||
F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
|
F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
|
||||||
P 0f7fdb022ca7c94f7d264192e18b6e2bd1e8cff4
|
P 1c2656fdf6176a7365db4e11f4bbf47721da72b4
|
||||||
R b9541c43661a32a0d6639e1946203bc0
|
R ad4f14fb479cf3da73d29eb9f4dc5165
|
||||||
U drh
|
U drh
|
||||||
Z 44f123568a87e2be4a17739c7fa10306
|
Z 203008a1e44e27fee31001b2c9831dfe
|
||||||
|
@ -1 +1 @@
|
|||||||
1c2656fdf6176a7365db4e11f4bbf47721da72b4
|
26348556d824c032851e409ac510cddb55c200bf
|
@ -11,7 +11,7 @@
|
|||||||
*************************************************************************
|
*************************************************************************
|
||||||
** Internal interface definitions for SQLite.
|
** Internal interface definitions for SQLite.
|
||||||
**
|
**
|
||||||
** @(#) $Id: sqliteInt.h,v 1.545 2007/03/27 13:36:37 drh Exp $
|
** @(#) $Id: sqliteInt.h,v 1.546 2007/03/28 14:30:07 drh Exp $
|
||||||
*/
|
*/
|
||||||
#ifndef _SQLITEINT_H_
|
#ifndef _SQLITEINT_H_
|
||||||
#define _SQLITEINT_H_
|
#define _SQLITEINT_H_
|
||||||
@ -1160,12 +1160,16 @@ struct WhereLevel {
|
|||||||
int iTabCur; /* The VDBE cursor used to access the table */
|
int iTabCur; /* The VDBE cursor used to access the table */
|
||||||
int iIdxCur; /* The VDBE cursor used to acesss pIdx */
|
int iIdxCur; /* The VDBE cursor used to acesss pIdx */
|
||||||
int brk; /* Jump here to break out of the loop */
|
int brk; /* Jump here to break out of the loop */
|
||||||
|
int nxt; /* Jump here to start the next IN combination */
|
||||||
int cont; /* Jump here to continue with the next loop cycle */
|
int cont; /* Jump here to continue with the next loop cycle */
|
||||||
int top; /* First instruction of interior of the loop */
|
int top; /* First instruction of interior of the loop */
|
||||||
int op, p1, p2; /* Opcode used to terminate the loop */
|
int op, p1, p2; /* Opcode used to terminate the loop */
|
||||||
int nEq; /* Number of == or IN constraints on this loop */
|
int nEq; /* Number of == or IN constraints on this loop */
|
||||||
int nIn; /* Number of IN operators constraining this loop */
|
int nIn; /* Number of IN operators constraining this loop */
|
||||||
int *aInLoop; /* Loop terminators for IN operators */
|
struct InLoop {
|
||||||
|
int iCur; /* The VDBE cursor used by this IN operator */
|
||||||
|
int topAddr; /* Top of the IN loop */
|
||||||
|
} *aInLoop; /* Information about each nested IN operator */
|
||||||
sqlite3_index_info *pBestIdx; /* Index information for this level */
|
sqlite3_index_info *pBestIdx; /* Index information for this level */
|
||||||
|
|
||||||
/* The following field is really not part of the current level. But
|
/* The following field is really not part of the current level. But
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
** in this file for details. If in doubt, do not deviate from existing
|
** in this file for details. If in doubt, do not deviate from existing
|
||||||
** commenting and indentation practices when changing or adding code.
|
** commenting and indentation practices when changing or adding code.
|
||||||
**
|
**
|
||||||
** $Id: vdbe.c,v 1.594 2007/03/27 14:44:51 drh Exp $
|
** $Id: vdbe.c,v 1.595 2007/03/28 14:30:07 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
@ -3677,7 +3677,9 @@ case OP_Next: { /* no-push */
|
|||||||
CHECK_FOR_INTERRUPT;
|
CHECK_FOR_INTERRUPT;
|
||||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||||
pC = p->apCsr[pOp->p1];
|
pC = p->apCsr[pOp->p1];
|
||||||
assert( pC!=0 );
|
if( pC==0 ){
|
||||||
|
break; /* See ticket #2273 */
|
||||||
|
}
|
||||||
if( (pCrsr = pC->pCursor)!=0 ){
|
if( (pCrsr = pC->pCursor)!=0 ){
|
||||||
int res;
|
int res;
|
||||||
if( pC->nullRow ){
|
if( pC->nullRow ){
|
||||||
|
83
src/where.c
83
src/where.c
@ -16,7 +16,7 @@
|
|||||||
** so is applicable. Because this module is responsible for selecting
|
** so is applicable. Because this module is responsible for selecting
|
||||||
** indices, you might also think of this module as the "query optimizer".
|
** indices, you might also think of this module as the "query optimizer".
|
||||||
**
|
**
|
||||||
** $Id: where.c,v 1.241 2007/03/27 13:36:37 drh Exp $
|
** $Id: where.c,v 1.242 2007/03/28 14:30:09 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@ -1687,7 +1687,6 @@ static void buildIndexProbe(
|
|||||||
static void codeEqualityTerm(
|
static void codeEqualityTerm(
|
||||||
Parse *pParse, /* The parsing context */
|
Parse *pParse, /* The parsing context */
|
||||||
WhereTerm *pTerm, /* The term of the WHERE clause to be coded */
|
WhereTerm *pTerm, /* The term of the WHERE clause to be coded */
|
||||||
int brk, /* Jump here to abandon the loop */
|
|
||||||
WhereLevel *pLevel /* When level of the FROM clause we are working on */
|
WhereLevel *pLevel /* When level of the FROM clause we are working on */
|
||||||
){
|
){
|
||||||
Expr *pX = pTerm->pExpr;
|
Expr *pX = pTerm->pExpr;
|
||||||
@ -1699,21 +1698,25 @@ static void codeEqualityTerm(
|
|||||||
#ifndef SQLITE_OMIT_SUBQUERY
|
#ifndef SQLITE_OMIT_SUBQUERY
|
||||||
}else{
|
}else{
|
||||||
int iTab;
|
int iTab;
|
||||||
int *aIn;
|
struct InLoop *pIn;
|
||||||
|
|
||||||
assert( pX->op==TK_IN );
|
assert( pX->op==TK_IN );
|
||||||
sqlite3CodeSubselect(pParse, pX);
|
sqlite3CodeSubselect(pParse, pX);
|
||||||
iTab = pX->iTable;
|
iTab = pX->iTable;
|
||||||
sqlite3VdbeAddOp(v, OP_Rewind, iTab, 0);
|
sqlite3VdbeAddOp(v, OP_Rewind, iTab, 0);
|
||||||
VdbeComment((v, "# %.*s", pX->span.n, pX->span.z));
|
VdbeComment((v, "# %.*s", pX->span.n, pX->span.z));
|
||||||
|
if( pLevel->nIn==0 ){
|
||||||
|
pLevel->nxt = sqlite3VdbeMakeLabel(v);
|
||||||
|
}
|
||||||
pLevel->nIn++;
|
pLevel->nIn++;
|
||||||
pLevel->aInLoop = sqliteReallocOrFree(pLevel->aInLoop,
|
pLevel->aInLoop = sqliteReallocOrFree(pLevel->aInLoop,
|
||||||
sizeof(pLevel->aInLoop[0])*2*pLevel->nIn);
|
sizeof(pLevel->aInLoop[0])*pLevel->nIn);
|
||||||
aIn = pLevel->aInLoop;
|
pIn = pLevel->aInLoop;
|
||||||
if( aIn ){
|
if( pIn ){
|
||||||
aIn += pLevel->nIn*2 - 2;
|
pIn += pLevel->nIn - 1;
|
||||||
aIn[0] = iTab;
|
pIn->iCur = iTab;
|
||||||
aIn[1] = sqlite3VdbeAddOp(v, OP_Column, iTab, 0);
|
pIn->topAddr = sqlite3VdbeAddOp(v, OP_Column, iTab, 0);
|
||||||
|
sqlite3VdbeAddOp(v, OP_IsNull, -1, 0);
|
||||||
}else{
|
}else{
|
||||||
pLevel->nIn = 0;
|
pLevel->nIn = 0;
|
||||||
}
|
}
|
||||||
@ -1749,8 +1752,7 @@ static void codeAllEqualityTerms(
|
|||||||
Parse *pParse, /* Parsing context */
|
Parse *pParse, /* Parsing context */
|
||||||
WhereLevel *pLevel, /* Which nested loop of the FROM we are coding */
|
WhereLevel *pLevel, /* Which nested loop of the FROM we are coding */
|
||||||
WhereClause *pWC, /* The WHERE clause */
|
WhereClause *pWC, /* The WHERE clause */
|
||||||
Bitmask notReady, /* Which parts of FROM have not yet been coded */
|
Bitmask notReady /* Which parts of FROM have not yet been coded */
|
||||||
int brk /* Jump here to end the loop */
|
|
||||||
){
|
){
|
||||||
int nEq = pLevel->nEq; /* The number of == or IN constraints to code */
|
int nEq = pLevel->nEq; /* The number of == or IN constraints to code */
|
||||||
int termsInMem = 0; /* If true, store value in mem[] cells */
|
int termsInMem = 0; /* If true, store value in mem[] cells */
|
||||||
@ -1779,9 +1781,9 @@ static void codeAllEqualityTerms(
|
|||||||
pTerm = findTerm(pWC, iCur, k, notReady, pLevel->flags, pIdx);
|
pTerm = findTerm(pWC, iCur, k, notReady, pLevel->flags, pIdx);
|
||||||
if( pTerm==0 ) break;
|
if( pTerm==0 ) break;
|
||||||
assert( (pTerm->flags & TERM_CODED)==0 );
|
assert( (pTerm->flags & TERM_CODED)==0 );
|
||||||
codeEqualityTerm(pParse, pTerm, brk, pLevel);
|
codeEqualityTerm(pParse, pTerm, pLevel);
|
||||||
if( (pTerm->eOperator & WO_ISNULL)==0 ){
|
if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){
|
||||||
sqlite3VdbeAddOp(v, OP_IsNull, termsInMem ? -1 : -(j+1), brk);
|
sqlite3VdbeAddOp(v, OP_IsNull, termsInMem ? -1 : -(j+1), pLevel->brk);
|
||||||
}
|
}
|
||||||
if( termsInMem ){
|
if( termsInMem ){
|
||||||
sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem+j+1, 1);
|
sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem+j+1, 1);
|
||||||
@ -2184,6 +2186,7 @@ WhereInfo *sqlite3WhereBegin(
|
|||||||
int j;
|
int j;
|
||||||
int iCur = pTabItem->iCursor; /* The VDBE cursor for the table */
|
int iCur = pTabItem->iCursor; /* The VDBE cursor for the table */
|
||||||
Index *pIdx; /* The index we will be using */
|
Index *pIdx; /* The index we will be using */
|
||||||
|
int nxt; /* Where to jump to continue with the next IN case */
|
||||||
int iIdxCur; /* The VDBE cursor for the index */
|
int iIdxCur; /* The VDBE cursor for the index */
|
||||||
int omitTable; /* True if we use the index only */
|
int omitTable; /* True if we use the index only */
|
||||||
int bRev; /* True if we need to scan in reverse order */
|
int bRev; /* True if we need to scan in reverse order */
|
||||||
@ -2199,8 +2202,13 @@ WhereInfo *sqlite3WhereBegin(
|
|||||||
** for the current loop. Jump to brk to break out of a loop.
|
** for the current loop. Jump to brk to break out of a loop.
|
||||||
** Jump to cont to go immediately to the next iteration of the
|
** Jump to cont to go immediately to the next iteration of the
|
||||||
** loop.
|
** loop.
|
||||||
|
**
|
||||||
|
** When there is an IN operator, we also have a "nxt" label that
|
||||||
|
** means to continue with the next IN value combination. When
|
||||||
|
** there are no IN operators in the constraints, the "nxt" label
|
||||||
|
** is the same as "brk".
|
||||||
*/
|
*/
|
||||||
brk = pLevel->brk = sqlite3VdbeMakeLabel(v);
|
brk = pLevel->brk = pLevel->nxt = sqlite3VdbeMakeLabel(v);
|
||||||
cont = pLevel->cont = sqlite3VdbeMakeLabel(v);
|
cont = pLevel->cont = sqlite3VdbeMakeLabel(v);
|
||||||
|
|
||||||
/* If this is the right table of a LEFT OUTER JOIN, allocate and
|
/* If this is the right table of a LEFT OUTER JOIN, allocate and
|
||||||
@ -2266,9 +2274,10 @@ WhereInfo *sqlite3WhereBegin(
|
|||||||
assert( pTerm->pExpr!=0 );
|
assert( pTerm->pExpr!=0 );
|
||||||
assert( pTerm->leftCursor==iCur );
|
assert( pTerm->leftCursor==iCur );
|
||||||
assert( omitTable==0 );
|
assert( omitTable==0 );
|
||||||
codeEqualityTerm(pParse, pTerm, brk, pLevel);
|
codeEqualityTerm(pParse, pTerm, pLevel);
|
||||||
sqlite3VdbeAddOp(v, OP_MustBeInt, 1, brk);
|
nxt = pLevel->nxt;
|
||||||
sqlite3VdbeAddOp(v, OP_NotExists, iCur, brk);
|
sqlite3VdbeAddOp(v, OP_MustBeInt, 1, nxt);
|
||||||
|
sqlite3VdbeAddOp(v, OP_NotExists, iCur, nxt);
|
||||||
VdbeComment((v, "pk"));
|
VdbeComment((v, "pk"));
|
||||||
pLevel->op = OP_Noop;
|
pLevel->op = OP_Noop;
|
||||||
}else if( pLevel->flags & WHERE_ROWID_RANGE ){
|
}else if( pLevel->flags & WHERE_ROWID_RANGE ){
|
||||||
@ -2347,7 +2356,7 @@ WhereInfo *sqlite3WhereBegin(
|
|||||||
/* Generate code to evaluate all constraint terms using == or IN
|
/* Generate code to evaluate all constraint terms using == or IN
|
||||||
** and level the values of those terms on the stack.
|
** and level the values of those terms on the stack.
|
||||||
*/
|
*/
|
||||||
codeAllEqualityTerms(pParse, pLevel, &wc, notReady, brk);
|
codeAllEqualityTerms(pParse, pLevel, &wc, notReady);
|
||||||
|
|
||||||
/* Duplicate the equality term values because they will all be
|
/* Duplicate the equality term values because they will all be
|
||||||
** used twice: once to make the termination key and once to make the
|
** used twice: once to make the termination key and once to make the
|
||||||
@ -2378,6 +2387,7 @@ WhereInfo *sqlite3WhereBegin(
|
|||||||
** 2002-Dec-04: On a reverse-order scan, the so-called "termination"
|
** 2002-Dec-04: On a reverse-order scan, the so-called "termination"
|
||||||
** key computed here really ends up being the start key.
|
** key computed here really ends up being the start key.
|
||||||
*/
|
*/
|
||||||
|
nxt = pLevel->nxt;
|
||||||
if( topLimit ){
|
if( topLimit ){
|
||||||
Expr *pX;
|
Expr *pX;
|
||||||
int k = pIdx->aiColumn[j];
|
int k = pIdx->aiColumn[j];
|
||||||
@ -2386,7 +2396,7 @@ WhereInfo *sqlite3WhereBegin(
|
|||||||
pX = pTerm->pExpr;
|
pX = pTerm->pExpr;
|
||||||
assert( (pTerm->flags & TERM_CODED)==0 );
|
assert( (pTerm->flags & TERM_CODED)==0 );
|
||||||
sqlite3ExprCode(pParse, pX->pRight);
|
sqlite3ExprCode(pParse, pX->pRight);
|
||||||
sqlite3VdbeAddOp(v, OP_IsNull, -(nEq+1), brk);
|
sqlite3VdbeAddOp(v, OP_IsNull, -(nEq+1), nxt);
|
||||||
topEq = pTerm->eOperator & (WO_LE|WO_GE);
|
topEq = pTerm->eOperator & (WO_LE|WO_GE);
|
||||||
disableTerm(pLevel, pTerm);
|
disableTerm(pLevel, pTerm);
|
||||||
testOp = OP_IdxGE;
|
testOp = OP_IdxGE;
|
||||||
@ -2400,7 +2410,7 @@ WhereInfo *sqlite3WhereBegin(
|
|||||||
buildIndexProbe(v, nCol, pIdx);
|
buildIndexProbe(v, nCol, pIdx);
|
||||||
if( bRev ){
|
if( bRev ){
|
||||||
int op = topEq ? OP_MoveLe : OP_MoveLt;
|
int op = topEq ? OP_MoveLe : OP_MoveLt;
|
||||||
sqlite3VdbeAddOp(v, op, iIdxCur, brk);
|
sqlite3VdbeAddOp(v, op, iIdxCur, nxt);
|
||||||
}else{
|
}else{
|
||||||
sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 1);
|
sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 1);
|
||||||
}
|
}
|
||||||
@ -2425,7 +2435,7 @@ WhereInfo *sqlite3WhereBegin(
|
|||||||
pX = pTerm->pExpr;
|
pX = pTerm->pExpr;
|
||||||
assert( (pTerm->flags & TERM_CODED)==0 );
|
assert( (pTerm->flags & TERM_CODED)==0 );
|
||||||
sqlite3ExprCode(pParse, pX->pRight);
|
sqlite3ExprCode(pParse, pX->pRight);
|
||||||
sqlite3VdbeAddOp(v, OP_IsNull, -(nEq+1), brk);
|
sqlite3VdbeAddOp(v, OP_IsNull, -(nEq+1), nxt);
|
||||||
btmEq = pTerm->eOperator & (WO_LE|WO_GE);
|
btmEq = pTerm->eOperator & (WO_LE|WO_GE);
|
||||||
disableTerm(pLevel, pTerm);
|
disableTerm(pLevel, pTerm);
|
||||||
}else{
|
}else{
|
||||||
@ -2440,7 +2450,7 @@ WhereInfo *sqlite3WhereBegin(
|
|||||||
testOp = OP_IdxLT;
|
testOp = OP_IdxLT;
|
||||||
}else{
|
}else{
|
||||||
int op = btmEq ? OP_MoveGe : OP_MoveGt;
|
int op = btmEq ? OP_MoveGe : OP_MoveGt;
|
||||||
sqlite3VdbeAddOp(v, op, iIdxCur, brk);
|
sqlite3VdbeAddOp(v, op, iIdxCur, nxt);
|
||||||
}
|
}
|
||||||
}else if( bRev ){
|
}else if( bRev ){
|
||||||
testOp = OP_Noop;
|
testOp = OP_Noop;
|
||||||
@ -2455,7 +2465,7 @@ WhereInfo *sqlite3WhereBegin(
|
|||||||
start = sqlite3VdbeCurrentAddr(v);
|
start = sqlite3VdbeCurrentAddr(v);
|
||||||
if( testOp!=OP_Noop ){
|
if( testOp!=OP_Noop ){
|
||||||
sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
|
sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
|
||||||
sqlite3VdbeAddOp(v, testOp, iIdxCur, brk);
|
sqlite3VdbeAddOp(v, testOp, iIdxCur, nxt);
|
||||||
if( (topEq && !bRev) || (!btmEq && bRev) ){
|
if( (topEq && !bRev) || (!btmEq && bRev) ){
|
||||||
sqlite3VdbeChangeP3(v, -1, "+", P3_STATIC);
|
sqlite3VdbeChangeP3(v, -1, "+", P3_STATIC);
|
||||||
}
|
}
|
||||||
@ -2484,7 +2494,8 @@ WhereInfo *sqlite3WhereBegin(
|
|||||||
/* Generate code to evaluate all constraint terms using == or IN
|
/* Generate code to evaluate all constraint terms using == or IN
|
||||||
** and leave the values of those terms on the stack.
|
** and leave the values of those terms on the stack.
|
||||||
*/
|
*/
|
||||||
codeAllEqualityTerms(pParse, pLevel, &wc, notReady, brk);
|
codeAllEqualityTerms(pParse, pLevel, &wc, notReady);
|
||||||
|
nxt = pLevel->nxt;
|
||||||
|
|
||||||
/* Generate a single key that will be used to both start and terminate
|
/* Generate a single key that will be used to both start and terminate
|
||||||
** the search
|
** the search
|
||||||
@ -2493,21 +2504,21 @@ WhereInfo *sqlite3WhereBegin(
|
|||||||
sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 0);
|
sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 0);
|
||||||
|
|
||||||
/* Generate code (1) to move to the first matching element of the table.
|
/* Generate code (1) to move to the first matching element of the table.
|
||||||
** Then generate code (2) that jumps to "brk" after the cursor is past
|
** Then generate code (2) that jumps to "nxt" after the cursor is past
|
||||||
** the last matching element of the table. The code (1) is executed
|
** the last matching element of the table. The code (1) is executed
|
||||||
** once to initialize the search, the code (2) is executed before each
|
** once to initialize the search, the code (2) is executed before each
|
||||||
** iteration of the scan to see if the scan has finished. */
|
** iteration of the scan to see if the scan has finished. */
|
||||||
if( bRev ){
|
if( bRev ){
|
||||||
/* Scan in reverse order */
|
/* Scan in reverse order */
|
||||||
sqlite3VdbeAddOp(v, OP_MoveLe, iIdxCur, brk);
|
sqlite3VdbeAddOp(v, OP_MoveLe, iIdxCur, nxt);
|
||||||
start = sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
|
start = sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
|
||||||
sqlite3VdbeAddOp(v, OP_IdxLT, iIdxCur, brk);
|
sqlite3VdbeAddOp(v, OP_IdxLT, iIdxCur, nxt);
|
||||||
pLevel->op = OP_Prev;
|
pLevel->op = OP_Prev;
|
||||||
}else{
|
}else{
|
||||||
/* Scan in the forward order */
|
/* Scan in the forward order */
|
||||||
sqlite3VdbeAddOp(v, OP_MoveGe, iIdxCur, brk);
|
sqlite3VdbeAddOp(v, OP_MoveGe, iIdxCur, nxt);
|
||||||
start = sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
|
start = sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
|
||||||
sqlite3VdbeOp3(v, OP_IdxGE, iIdxCur, brk, "+", P3_STATIC);
|
sqlite3VdbeOp3(v, OP_IdxGE, iIdxCur, nxt, "+", P3_STATIC);
|
||||||
pLevel->op = OP_Next;
|
pLevel->op = OP_Next;
|
||||||
}
|
}
|
||||||
if( !omitTable ){
|
if( !omitTable ){
|
||||||
@ -2640,16 +2651,18 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
|||||||
if( pLevel->op!=OP_Noop ){
|
if( pLevel->op!=OP_Noop ){
|
||||||
sqlite3VdbeAddOp(v, pLevel->op, pLevel->p1, pLevel->p2);
|
sqlite3VdbeAddOp(v, pLevel->op, pLevel->p1, pLevel->p2);
|
||||||
}
|
}
|
||||||
sqlite3VdbeResolveLabel(v, pLevel->brk);
|
|
||||||
if( pLevel->nIn ){
|
if( pLevel->nIn ){
|
||||||
int *a;
|
struct InLoop *pIn;
|
||||||
int j;
|
int j;
|
||||||
for(j=pLevel->nIn, a=&pLevel->aInLoop[j*2-2]; j>0; j--, a-=2){
|
sqlite3VdbeResolveLabel(v, pLevel->nxt);
|
||||||
sqlite3VdbeAddOp(v, OP_Next, a[0], a[1]);
|
for(j=pLevel->nIn, pIn=&pLevel->aInLoop[j-1]; j>0; j--, pIn--){
|
||||||
sqlite3VdbeJumpHere(v, a[1]-1);
|
sqlite3VdbeJumpHere(v, pIn->topAddr+1);
|
||||||
|
sqlite3VdbeAddOp(v, OP_Next, pIn->iCur, pIn->topAddr);
|
||||||
|
sqlite3VdbeJumpHere(v, pIn->topAddr-1);
|
||||||
}
|
}
|
||||||
sqliteFree(pLevel->aInLoop);
|
sqliteFree(pLevel->aInLoop);
|
||||||
}
|
}
|
||||||
|
sqlite3VdbeResolveLabel(v, pLevel->brk);
|
||||||
if( pLevel->iLeftJoin ){
|
if( pLevel->iLeftJoin ){
|
||||||
int addr;
|
int addr;
|
||||||
addr = sqlite3VdbeAddOp(v, OP_IfMemPos, pLevel->iLeftJoin, 0);
|
addr = sqlite3VdbeAddOp(v, OP_IfMemPos, pLevel->iLeftJoin, 0);
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
# that IS NULL phrases are correctly optimized. But you can never
|
# that IS NULL phrases are correctly optimized. But you can never
|
||||||
# have too many tests, so some other tests are thrown in as well.
|
# have too many tests, so some other tests are thrown in as well.
|
||||||
#
|
#
|
||||||
# $Id: where4.test,v 1.2 2007/01/25 16:56:08 drh Exp $
|
# $Id: where4.test,v 1.3 2007/03/28 14:30:09 drh Exp $
|
||||||
|
|
||||||
set testdir [file dirname $argv0]
|
set testdir [file dirname $argv0]
|
||||||
source $testdir/tester.tcl
|
source $testdir/tester.tcl
|
||||||
@ -173,7 +173,60 @@ do_test where4-4.4 {
|
|||||||
WHERE t1.col1 IS NULL;
|
WHERE t1.col1 IS NULL;
|
||||||
}
|
}
|
||||||
} {}
|
} {}
|
||||||
|
|
||||||
|
# Ticket #2273. Problems with IN operators and NULLs.
|
||||||
|
#
|
||||||
|
do_test where4-5.1 {
|
||||||
|
execsql {
|
||||||
|
CREATE TABLE t4(x,y,z,PRIMARY KEY(x,y));
|
||||||
|
}
|
||||||
|
execsql {
|
||||||
|
SELECT *
|
||||||
|
FROM t2 LEFT JOIN t4 b1
|
||||||
|
LEFT JOIN t4 b2 ON b2.x=b1.x AND b2.y IN (b1.y);
|
||||||
|
}
|
||||||
|
} {1 {} {} {} {} {} {} 2 {} {} {} {} {} {} 3 {} {} {} {} {} {}}
|
||||||
|
do_test where4-5.2 {
|
||||||
|
execsql {
|
||||||
|
INSERT INTO t4 VALUES(1,1,11);
|
||||||
|
INSERT INTO t4 VALUES(1,2,12);
|
||||||
|
INSERT INTO t4 VALUES(1,3,13);
|
||||||
|
INSERT INTO t4 VALUES(2,2,22);
|
||||||
|
SELECT rowid FROM t4 WHERE x IN (1,9,2,5) AND y IN (1,3,NULL,2) AND z!=13;
|
||||||
|
}
|
||||||
|
} {1 2 4}
|
||||||
|
do_test where4-5.3 {
|
||||||
|
execsql {
|
||||||
|
SELECT rowid FROM t4 WHERE x IN (1,9,NULL,2) AND y IN (1,3,2) AND z!=13;
|
||||||
|
}
|
||||||
|
} {1 2 4}
|
||||||
|
do_test where4-6.1 {
|
||||||
|
execsql {
|
||||||
|
CREATE TABLE t5(a,b,c,d,e,f,UNIQUE(a,b,c,d,e,f));
|
||||||
|
INSERT INTO t5 VALUES(1,1,1,1,1,11111);
|
||||||
|
INSERT INTO t5 VALUES(2,2,2,2,2,22222);
|
||||||
|
INSERT INTO t5 VALUES(1,2,3,4,5,12345);
|
||||||
|
INSERT INTO t5 VALUES(2,3,4,5,6,23456);
|
||||||
|
}
|
||||||
|
execsql {
|
||||||
|
SELECT rowid FROM t5
|
||||||
|
WHERE a IN (1,9,2) AND b=2 AND c IN (1,2,3,4) AND d>0
|
||||||
|
}
|
||||||
|
} {3 2}
|
||||||
|
do_test where4-6.2 {
|
||||||
|
execsql {
|
||||||
|
SELECT rowid FROM t5
|
||||||
|
WHERE a IN (1,NULL,2) AND b=2 AND c IN (1,2,3,4) AND d>0
|
||||||
|
}
|
||||||
|
} {3 2}
|
||||||
|
do_test where4-7.1 {
|
||||||
|
execsql {
|
||||||
|
CREATE TABLE t6(y,z,PRIMARY KEY(y,z));
|
||||||
|
}
|
||||||
|
execsql {
|
||||||
|
SELECT * FROM t6 WHERE y=NULL AND z IN ('hello');
|
||||||
|
}
|
||||||
|
} {}
|
||||||
|
|
||||||
integrity_check {where4-99.0}
|
integrity_check {where4-99.0}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user