Fix handling of "x IN (...)" and "x NOT IN (...)" expressions when the set contains an SQL NULL value. (CVS 5314)
FossilOrigin-Name: d45a97be71fa61ab4a692bd807ab762130f7f5b9
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
C Document\sthe\srules\sfor\swhen\san\ssqlite3_blob\sobject\sexpires.\s(CVS\s5313)
|
||||
D 2008-06-26T15:04:58
|
||||
C Fix\shandling\sof\s"x\sIN\s(...)"\sand\s"x\sNOT\sIN\s(...)"\sexpressions\swhen\sthe\sset\scontains\san\sSQL\sNULL\svalue.\s(CVS\s5314)
|
||||
D 2008-06-26T18:04:03
|
||||
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
||||
F Makefile.in 325dfac0a0dd1cb4d975f1ace6453157892e6042
|
||||
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
||||
@@ -103,7 +103,7 @@ F src/callback.c 3ba98ae46f60aa7c2c40eac7d18fe5ba9b706b83
|
||||
F src/complete.c cb14e06dbe79dee031031f0d9e686ff306afe07c
|
||||
F src/date.c e841168e5520bbbb2a1cbcdce7531d8b23017b4d
|
||||
F src/delete.c d3fc5987f2eb88f7b9549d58a5dfea079a83fe8b
|
||||
F src/expr.c 1a8fbd3dfc531fa8ec51319a0bdef014e28c0fdd
|
||||
F src/expr.c edcc8a30453f63fe9eab3a3d714589f44f875d8d
|
||||
F src/fault.c 3638519d1e0b82bccfafcb9f5ff491918b28f8e1
|
||||
F src/func.c 1e7d9569570134ac0771a00382d9d4b41c4aa052
|
||||
F src/global.c 2304cfa3288763bd2fed10caf8c6fbaa2b383f4e
|
||||
@@ -140,11 +140,11 @@ F src/pragma.c 9a95f5b3708f6d3ddd987eab5f369a19ffcb6795
|
||||
F src/prepare.c aba51dad52308e3d9d2074d8ff4e612e7f1cab51
|
||||
F src/printf.c 8b063da9dcde26b7c500a01444b718d86f21bc6e
|
||||
F src/random.c 5c754319d38abdd6acd74601ee0105504adc508a
|
||||
F src/select.c 79f60dc4a7e90bb907c7a2cca42f45276d1ead99
|
||||
F src/select.c e27de53be426254e1b75690c9c99482bb1431b79
|
||||
F src/shell.c 479807b87f0409289eec4a776cd6ae56d30544b1
|
||||
F src/sqlite.h.in 2b9e8de3378101ad8ec8de98e3679ad5c2f39427
|
||||
F src/sqlite3ext.h f162a72daef5ebf8b211fe8c0ec96e85d22fbf9b
|
||||
F src/sqliteInt.h 5ed69fd1affa577a47ce01baa42ee94f22f45ed7
|
||||
F src/sqliteInt.h 969acf22dbe79075e486074a8ffdc1e2fc2b8b1f
|
||||
F src/sqliteLimit.h f435e728c6b620ef7312814d660a81f9356eb5c8
|
||||
F src/status.c 6cb10377992505bd69f1ca1d75c1240a65f25a58
|
||||
F src/table.c 1fa8f8113ac9cbc09ae4801c6d2a7f0af82c5822
|
||||
@@ -181,7 +181,7 @@ F src/update.c 2d7143b9014e955509cc4f323f9a9584fb898f34
|
||||
F src/utf.c 8c94fa10efc78c2568d08d436acc59df4df7191b
|
||||
F src/util.c 920d6d5dfdf25f7b85d2093705d8716f9b387e3b
|
||||
F src/vacuum.c 14eb21b480924d87e791cd8ab6fb35ac563243ef
|
||||
F src/vdbe.c c0daf1d1fb4c3c79805004969d5d036f3d2381f8
|
||||
F src/vdbe.c b1528683ff7ca1faf017db5ebedbc403e92d009b
|
||||
F src/vdbe.h c46155c221418bea29ee3a749d5950fcf85a70e2
|
||||
F src/vdbeInt.h 30535c1d30ba1b5fb58d8f0e1d1261af976558aa
|
||||
F src/vdbeapi.c a7c6b8db324cf7eccff32de871dea36aa305c994
|
||||
@@ -190,7 +190,7 @@ F src/vdbeblob.c 9345f6dcd675fdcfdb537d2d2f487542d9ea136a
|
||||
F src/vdbefifo.c c46dae1194e4277bf007144d7e5b0c0b1c24f136
|
||||
F src/vdbemem.c a39a822e6ae61c4cab4a512df4a315888b206911
|
||||
F src/vtab.c 2096c03ec5540a43c8c73a8f43407dfd3549a982
|
||||
F src/where.c 767db25b4b92a5e0a6f1b75ba40abf377b65a212
|
||||
F src/where.c 6a22ceb86d774c4428ac53196f721bb14e6cbdfa
|
||||
F tclinstaller.tcl 4356d9d94d2b5ed5e68f9f0c80c4df3048dd7617
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/all.test ed6849e7a425620d5d4388409f3b15901b5bc2e7
|
||||
@@ -335,11 +335,11 @@ F test/fuzz_common.tcl ff4bc2dfc465f6878f8e2d819620914365382731
|
||||
F test/fuzz_malloc.test 166b58dfd77cc04f6afeeaef0cfc1087abf134d1
|
||||
F test/hook.test e17d4ed2843ba4ef9b234aa63e6f056bf88f9a19
|
||||
F test/icu.test e6bfae7f625c88fd14df6f540fe835bdfc1e4329
|
||||
F test/in.test ca4ea8ac1077f5221055fcb66969892612632ef7
|
||||
F test/in.test b35fc31caf26647cc4243c3af8ad29e45ed8776f
|
||||
F test/in2.test b1f447f4f0f67e9f83ff931e7e2e30873f9ea055
|
||||
F test/in3.test dc62b080ed79898121c61c91118b4d1e111f1438
|
||||
F test/incrblob.test 4455fffd08b2f9418a9257e18b135d72273eff3e
|
||||
F test/incrblob2.test a1db17c1f70bdbf9cae8415fb500df511569306a
|
||||
F test/incrblob2.test c9aad1e11f7726d8c49e66f2a1ecc2d04f9f6861
|
||||
F test/incrblob_err.test a3e3d9442d2993e8a449e791db4001d11a2f683f
|
||||
F test/incrvacuum.test 1a2b0bddc76629afeb41e3d8ea3e4563982d16b9
|
||||
F test/incrvacuum2.test 46ef65f377e3937cfd1ba66e818309dab46f590d
|
||||
@@ -594,7 +594,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
|
||||
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
F tool/speedtest8.c 1dbced29de5f59ba2ebf877edcadf171540374d1
|
||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||
P 1e3b8308021107d983d2152663f62b369cca091d
|
||||
R 5972f13e602e13f1fa109add837cf301
|
||||
U drh
|
||||
Z 6e75d867f7857f56f8d906b33d9926f0
|
||||
P e1de2287fd9b067f687cb8b427786b878af6c5b7
|
||||
R 01ffd027189ad04179892df9936c6090
|
||||
U danielk1977
|
||||
Z c4dcbe6b5c0897a85aa2b72a88c7084f
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
e1de2287fd9b067f687cb8b427786b878af6c5b7
|
||||
d45a97be71fa61ab4a692bd807ab762130f7f5b9
|
||||
+95
-13
@@ -12,7 +12,7 @@
|
||||
** This file contains routines used for analyzing expressions and
|
||||
** for generating VDBE code that evaluates expressions in SQLite.
|
||||
**
|
||||
** $Id: expr.c,v 1.376 2008/06/24 12:46:31 drh Exp $
|
||||
** $Id: expr.c,v 1.377 2008/06/26 18:04:03 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@@ -1673,22 +1673,46 @@ static int isCandidateForInOpt(Select *p){
|
||||
**
|
||||
** SELECT <column> FROM <table>
|
||||
**
|
||||
** If the mustBeUnique parameter is false, the structure will be used
|
||||
** for fast set membership tests. In this case an epheremal table must
|
||||
** be used unless <column> is an INTEGER PRIMARY KEY or an index can
|
||||
** be found with <column> as its left-most column.
|
||||
**
|
||||
** If mustBeUnique is true, then the structure will be used to iterate
|
||||
** If prNotFound parameter is 0, then the structure will be used to iterate
|
||||
** through the set members, skipping any duplicates. In this case an
|
||||
** epheremal table must be used unless the selected <column> is guaranteed
|
||||
** to be unique - either because it is an INTEGER PRIMARY KEY or it
|
||||
** is unique by virtue of a constraint or implicit index.
|
||||
**
|
||||
** If the prNotFound parameter is not 0, then the structure will be used
|
||||
** for fast set membership tests. In this case an epheremal table must
|
||||
** be used unless <column> is an INTEGER PRIMARY KEY or an index can
|
||||
** be found with <column> as its left-most column.
|
||||
**
|
||||
** When the structure is being used for set membership tests, the user
|
||||
** needs to know whether or not the structure contains an SQL NULL
|
||||
** value in order to correctly evaluate expressions like "X IN (Y, Z)".
|
||||
** If there is a chance that the structure may contain a NULL value at
|
||||
** runtime, then a register is allocated and the register number written
|
||||
** to *prNotFound. If there is no chance that the structure contains a
|
||||
** NULL value, then *prNotFound is left unchanged.
|
||||
**
|
||||
** If a register is allocated and its location stored in *prNotFound, then
|
||||
** its initial value is NULL. If the structure does not remain constant
|
||||
** for the duration of the query (i.e. the set is a correlated sub-select),
|
||||
** the value of the allocated register is reset to NULL each time the
|
||||
** structure is repopulated. This allows the caller to use vdbe code
|
||||
** equivalent to the following:
|
||||
**
|
||||
** if( register==NULL ){
|
||||
** has_null = <test if data structure contains null>
|
||||
** register = 1
|
||||
** }
|
||||
**
|
||||
** in order to avoid running the <test if data structure contains null>
|
||||
** test more often than is necessary.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
int sqlite3FindInIndex(Parse *pParse, Expr *pX, int mustBeUnique){
|
||||
int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
||||
Select *p;
|
||||
int eType = 0;
|
||||
int iTab = pParse->nTab++;
|
||||
int mustBeUnique = !prNotFound;
|
||||
|
||||
/* The follwing if(...) expression is true if the SELECT is of the
|
||||
** simple form:
|
||||
@@ -1764,13 +1788,20 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int mustBeUnique){
|
||||
eType = IN_INDEX_INDEX;
|
||||
|
||||
sqlite3VdbeJumpHere(v, iAddr);
|
||||
if( prNotFound && !pTab->aCol[iCol].notNull ){
|
||||
*prNotFound = ++pParse->nMem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( eType==0 ){
|
||||
sqlite3CodeSubselect(pParse, pX);
|
||||
int rMayHaveNull = 0;
|
||||
if( prNotFound ){
|
||||
*prNotFound = rMayHaveNull = ++pParse->nMem;
|
||||
}
|
||||
sqlite3CodeSubselect(pParse, pX, rMayHaveNull);
|
||||
eType = IN_INDEX_EPH;
|
||||
}else{
|
||||
pX->iTable = iTab;
|
||||
@@ -1792,7 +1823,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int mustBeUnique){
|
||||
** operator or subquery.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
|
||||
void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr, int rMayHaveNull){
|
||||
int testAddr = 0; /* One-time test address */
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
if( v==0 ) return;
|
||||
@@ -1821,6 +1852,10 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
|
||||
KeyInfo keyInfo;
|
||||
int addr; /* Address of OP_OpenEphemeral instruction */
|
||||
|
||||
if( rMayHaveNull ){
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, rMayHaveNull);
|
||||
}
|
||||
|
||||
affinity = sqlite3ExprAffinity(pExpr->pLeft);
|
||||
|
||||
/* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)'
|
||||
@@ -2535,17 +2570,22 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
testcase( op==TK_EXISTS );
|
||||
testcase( op==TK_SELECT );
|
||||
if( pExpr->iColumn==0 ){
|
||||
sqlite3CodeSubselect(pParse, pExpr);
|
||||
sqlite3CodeSubselect(pParse, pExpr, 0);
|
||||
}
|
||||
inReg = pExpr->iColumn;
|
||||
break;
|
||||
}
|
||||
case TK_IN: {
|
||||
int rNotFound = 0;
|
||||
int rMayHaveNull = 0;
|
||||
int j1, j2, j3, j4, j5;
|
||||
char affinity;
|
||||
int eType;
|
||||
|
||||
eType = sqlite3FindInIndex(pParse, pExpr, 0);
|
||||
eType = sqlite3FindInIndex(pParse, pExpr, &rMayHaveNull);
|
||||
if( rMayHaveNull ){
|
||||
rNotFound = ++pParse->nMem;
|
||||
}
|
||||
|
||||
/* Figure out the affinity to use to create a key from the results
|
||||
** of the expression. affinityStr stores a static string suitable for
|
||||
@@ -2570,13 +2610,55 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
j5 = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
sqlite3VdbeJumpHere(v, j3);
|
||||
sqlite3VdbeJumpHere(v, j4);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, target);
|
||||
}else{
|
||||
r2 = regFree2 = sqlite3GetTempReg(pParse);
|
||||
|
||||
/* Create a record and test for set membership. If the set contains
|
||||
** the value, then jump to the end of the test code. The target
|
||||
** register still contains the true (1) value written to it earlier.
|
||||
*/
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, r1, 1, r2, &affinity, 1);
|
||||
sqlite3ExprCacheAffinityChange(pParse, r1, 1);
|
||||
j5 = sqlite3VdbeAddOp3(v, OP_Found, pExpr->iTable, 0, r2);
|
||||
|
||||
/* If the set membership test fails, then the result of the
|
||||
** "x IN (...)" expression must be either 0 or NULL. If the set
|
||||
** contains no NULL values, then the result is 0. If the set
|
||||
** contains one or more NULL values, then the result of the
|
||||
** expression is also NULL.
|
||||
*/
|
||||
if( rNotFound==0 ){
|
||||
/* This branch runs if it is known at compile time (now) that
|
||||
** the set contains no NULL values. This happens as the result
|
||||
** of a "NOT NULL" constraint in the database schema. No need
|
||||
** to test the data structure at runtime in this case.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, target);
|
||||
}else{
|
||||
/* This block populates the rNotFound register with either NULL
|
||||
** or 0 (an integer value). If the data structure contains one
|
||||
** or more NULLs, then set rNotFound to NULL. Otherwise, set it
|
||||
** to 0. If register rMayHaveNull is already set to some value
|
||||
** other than NULL, then the test has already been run and
|
||||
** rNotFound is already populated.
|
||||
*/
|
||||
j3 = sqlite3VdbeAddOp1(v, OP_NotNull, rMayHaveNull);
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, rNotFound);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 1, rMayHaveNull);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, rNotFound, 1, r2, 0, 1);
|
||||
j4 = sqlite3VdbeAddOp3(v, OP_Found, pExpr->iTable, 0, r2);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, rNotFound);
|
||||
sqlite3VdbeJumpHere(v, j4);
|
||||
sqlite3VdbeJumpHere(v, j3);
|
||||
|
||||
/* Copy the value of register rNotFound (which is either NULL or 0)
|
||||
** into the target register. This will be the result of the
|
||||
** expression.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, rNotFound, target);
|
||||
}
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, target, -1);
|
||||
sqlite3VdbeJumpHere(v, j2);
|
||||
sqlite3VdbeJumpHere(v, j5);
|
||||
break;
|
||||
|
||||
+2
-9
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle SELECT statements in SQLite.
|
||||
**
|
||||
** $Id: select.c,v 1.436 2008/06/25 00:12:41 drh Exp $
|
||||
** $Id: select.c,v 1.437 2008/06/26 18:04:03 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -655,7 +655,6 @@ static void selectInnerLoop(
|
||||
int addr2;
|
||||
|
||||
assert( nColumn==1 );
|
||||
addr2 = sqlite3VdbeAddOp1(v, OP_IsNull, regResult);
|
||||
p->affinity = sqlite3CompareAffinity(pEList->a[0].pExpr, pDest->affinity);
|
||||
if( pOrderBy ){
|
||||
/* At first glance you would think we could optimize out the
|
||||
@@ -670,7 +669,6 @@ static void selectInnerLoop(
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, addr2);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -828,13 +826,10 @@ static void generateSortTail(
|
||||
}
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
case SRT_Set: {
|
||||
int j1;
|
||||
assert( nColumn==1 );
|
||||
j1 = sqlite3VdbeAddOp1(v, OP_IsNull, regRow);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, 1, regRowid, &p->affinity, 1);
|
||||
sqlite3ExprCacheAffinityChange(pParse, regRow, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRowid);
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
break;
|
||||
}
|
||||
case SRT_Mem: {
|
||||
@@ -2391,7 +2386,6 @@ static int generateOutputSubroutine(
|
||||
case SRT_Set: {
|
||||
int addr2, r1;
|
||||
assert( pIn->nMem==1 );
|
||||
addr2 = sqlite3VdbeAddOp1(v, OP_IsNull, pIn->iMem);
|
||||
p->affinity =
|
||||
sqlite3CompareAffinity(p->pEList->a[0].pExpr, pDest->affinity);
|
||||
r1 = sqlite3GetTempReg(pParse);
|
||||
@@ -2399,7 +2393,6 @@ static int generateOutputSubroutine(
|
||||
sqlite3ExprCacheAffinityChange(pParse, pIn->iMem, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, pDest->iParm, r1);
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
sqlite3VdbeJumpHere(v, addr2);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3577,7 +3570,7 @@ void sqlite3SelectMask(Parse *pParse, Select *p, u32 mask){
|
||||
**
|
||||
** SRT_Mem Store first result in memory cell pDest->iParm
|
||||
**
|
||||
** SRT_Set Store non-null results as keys of table pDest->iParm.
|
||||
** SRT_Set Store results as keys of table pDest->iParm.
|
||||
** Apply the affinity pDest->affinity before storing them.
|
||||
**
|
||||
** SRT_Union Store results as a key in a temporary table pDest->iParm.
|
||||
|
||||
+4
-4
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** Internal interface definitions for SQLite.
|
||||
**
|
||||
** @(#) $Id: sqliteInt.h,v 1.731 2008/06/26 10:54:12 danielk1977 Exp $
|
||||
** @(#) $Id: sqliteInt.h,v 1.732 2008/06/26 18:04:03 danielk1977 Exp $
|
||||
*/
|
||||
#ifndef _SQLITEINT_H_
|
||||
#define _SQLITEINT_H_
|
||||
@@ -1435,7 +1435,7 @@ struct Select {
|
||||
|
||||
#define SRT_Callback 5 /* Invoke a callback with each row of result */
|
||||
#define SRT_Mem 6 /* Store result in a memory cell */
|
||||
#define SRT_Set 7 /* Store non-null results as keys in an index */
|
||||
#define SRT_Set 7 /* Store results as keys in an index */
|
||||
#define SRT_Table 8 /* Store result as data with an automatic rowid */
|
||||
#define SRT_EphemTab 9 /* Create transient tab and store like SRT_Table */
|
||||
#define SRT_Coroutine 10 /* Generate a single row of result */
|
||||
@@ -2118,7 +2118,7 @@ void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
|
||||
int sqlite3GetToken(const unsigned char *, int *);
|
||||
void sqlite3NestedParse(Parse*, const char*, ...);
|
||||
void sqlite3ExpirePreparedStatements(sqlite3*);
|
||||
void sqlite3CodeSubselect(Parse *, Expr *);
|
||||
void sqlite3CodeSubselect(Parse *, Expr *, int);
|
||||
int sqlite3SelectResolve(Parse *, Select *, NameContext *);
|
||||
void sqlite3ColumnDefault(Vdbe *, Table *, int);
|
||||
void sqlite3AlterFinishAddColumn(Parse *, Token *);
|
||||
@@ -2224,7 +2224,7 @@ CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
|
||||
#define IN_INDEX_ROWID 1
|
||||
#define IN_INDEX_EPH 2
|
||||
#define IN_INDEX_INDEX 3
|
||||
int sqlite3FindInIndex(Parse *, Expr *, int);
|
||||
int sqlite3FindInIndex(Parse *, Expr *, int*);
|
||||
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
|
||||
|
||||
+2
-2
@@ -43,7 +43,7 @@
|
||||
** in this file for details. If in doubt, do not deviate from existing
|
||||
** commenting and indentation practices when changing or adding code.
|
||||
**
|
||||
** $Id: vdbe.c,v 1.755 2008/06/25 00:12:41 drh Exp $
|
||||
** $Id: vdbe.c,v 1.756 2008/06/26 18:04:03 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@@ -1428,7 +1428,7 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */
|
||||
|
||||
/* Opcode: AddImm P1 P2 * * *
|
||||
**
|
||||
** Add the constant P2 the value in register P1.
|
||||
** Add the constant P2 to the value in register P1.
|
||||
** The result is always an integer.
|
||||
**
|
||||
** To force any register to be an integer, just add 0.
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@
|
||||
** so is applicable. Because this module is responsible for selecting
|
||||
** indices, you might also think of this module as the "query optimizer".
|
||||
**
|
||||
** $Id: where.c,v 1.310 2008/06/25 02:47:57 drh Exp $
|
||||
** $Id: where.c,v 1.311 2008/06/26 18:04:03 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -1787,7 +1787,7 @@ static int codeEqualityTerm(
|
||||
|
||||
assert( pX->op==TK_IN );
|
||||
iReg = iTarget;
|
||||
eType = sqlite3FindInIndex(pParse, pX, 1);
|
||||
eType = sqlite3FindInIndex(pParse, pX, 0);
|
||||
iTab = pX->iTable;
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0);
|
||||
VdbeComment((v, "%.*s", pX->span.n, pX->span.z));
|
||||
|
||||
+147
-1
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the IN and BETWEEN operator.
|
||||
#
|
||||
# $Id: in.test,v 1.20 2008/06/24 12:46:31 drh Exp $
|
||||
# $Id: in.test,v 1.21 2008/06/26 18:04:03 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -429,4 +429,150 @@ do_test in-12.9 {
|
||||
}
|
||||
} {1 {SELECTs to the left and right of INTERSECT do not have the same number of result columns}}
|
||||
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
# The following tests check that NULL is handled correctly when it
|
||||
# appears as part of a set of values on the right-hand side of an
|
||||
# IN or NOT IN operator.
|
||||
#
|
||||
# When it appears in such a set, NULL is handled as an "unknown value".
|
||||
# If, because of the unknown value in the set, the result of the expression
|
||||
# cannot be determined, then it itself evaluates to NULL.
|
||||
#
|
||||
|
||||
# Warm body test to demonstrate the principles being tested:
|
||||
#
|
||||
do_test in-13.1 {
|
||||
db nullvalue "null"
|
||||
execsql { SELECT
|
||||
1 IN (NULL, 1, 2), -- The value 1 is a member of the set, return true.
|
||||
3 IN (NULL, 1, 2), -- Ambiguous, return NULL.
|
||||
1 NOT IN (NULL, 1, 2), -- The value 1 is a member of the set, return false.
|
||||
3 NOT IN (NULL, 1, 2) -- Ambiguous, return NULL.
|
||||
}
|
||||
} {1 null 0 null}
|
||||
|
||||
do_test in-13.2 {
|
||||
execsql {
|
||||
CREATE TABLE t7(a, b, c NOT NULL);
|
||||
INSERT INTO t7 VALUES(1, 1, 1);
|
||||
INSERT INTO t7 VALUES(2, 2, 2);
|
||||
INSERT INTO t7 VALUES(3, 3, 3);
|
||||
INSERT INTO t7 VALUES(NULL, 4, 4);
|
||||
INSERT INTO t7 VALUES(NULL, 5, 5);
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test in-13.3 {
|
||||
execsql { SELECT 2 IN (SELECT a FROM t7) }
|
||||
} {1}
|
||||
do_test in-13.4 {
|
||||
execsql { SELECT 6 IN (SELECT a FROM t7) }
|
||||
} {null}
|
||||
|
||||
do_test in-13.5 {
|
||||
execsql { SELECT 2 IN (SELECT b FROM t7) }
|
||||
} {1}
|
||||
do_test in-13.6 {
|
||||
execsql { SELECT 6 IN (SELECT b FROM t7) }
|
||||
} {0}
|
||||
|
||||
do_test in-13.7 {
|
||||
execsql { SELECT 2 IN (SELECT c FROM t7) }
|
||||
} {1}
|
||||
do_test in-13.8 {
|
||||
execsql { SELECT 6 IN (SELECT c FROM t7) }
|
||||
} {0}
|
||||
|
||||
do_test in-13.9 {
|
||||
execsql {
|
||||
SELECT
|
||||
2 NOT IN (SELECT a FROM t7),
|
||||
6 NOT IN (SELECT a FROM t7),
|
||||
2 NOT IN (SELECT b FROM t7),
|
||||
6 NOT IN (SELECT b FROM t7),
|
||||
2 NOT IN (SELECT c FROM t7),
|
||||
6 NOT IN (SELECT c FROM t7)
|
||||
}
|
||||
} {0 null 0 1 0 1}
|
||||
|
||||
do_test in-13.10 {
|
||||
execsql {
|
||||
SELECT b IN (
|
||||
SELECT inside.a
|
||||
FROM t7 AS inside
|
||||
WHERE inside.b BETWEEN outside.b+1 AND outside.b+2
|
||||
)
|
||||
FROM t7 AS outside ORDER BY b;
|
||||
}
|
||||
} {0 null null null 0}
|
||||
|
||||
do_test in-13.11 {
|
||||
execsql {
|
||||
SELECT b NOT IN (
|
||||
SELECT inside.a
|
||||
FROM t7 AS inside
|
||||
WHERE inside.b BETWEEN outside.b+1 AND outside.b+2
|
||||
)
|
||||
FROM t7 AS outside ORDER BY b;
|
||||
}
|
||||
} {1 null null null 1}
|
||||
|
||||
do_test in-13.12 {
|
||||
execsql {
|
||||
CREATE INDEX i1 ON t7(a);
|
||||
CREATE INDEX i2 ON t7(b);
|
||||
CREATE INDEX i3 ON t7(c);
|
||||
}
|
||||
execsql {
|
||||
SELECT
|
||||
2 IN (SELECT a FROM t7),
|
||||
6 IN (SELECT a FROM t7),
|
||||
2 IN (SELECT b FROM t7),
|
||||
6 IN (SELECT b FROM t7),
|
||||
2 IN (SELECT c FROM t7),
|
||||
6 IN (SELECT c FROM t7)
|
||||
}
|
||||
} {1 null 1 0 1 0}
|
||||
|
||||
do_test in-13.13 {
|
||||
execsql {
|
||||
SELECT
|
||||
2 NOT IN (SELECT a FROM t7),
|
||||
6 NOT IN (SELECT a FROM t7),
|
||||
2 NOT IN (SELECT b FROM t7),
|
||||
6 NOT IN (SELECT b FROM t7),
|
||||
2 NOT IN (SELECT c FROM t7),
|
||||
6 NOT IN (SELECT c FROM t7)
|
||||
}
|
||||
} {0 null 0 1 0 1}
|
||||
|
||||
do_test in-13.14 {
|
||||
execsql {
|
||||
BEGIN TRANSACTION;
|
||||
CREATE TABLE a(id INTEGER);
|
||||
INSERT INTO a VALUES(1);
|
||||
INSERT INTO a VALUES(2);
|
||||
INSERT INTO a VALUES(3);
|
||||
CREATE TABLE b(id INTEGER);
|
||||
INSERT INTO b VALUES(NULL);
|
||||
INSERT INTO b VALUES(3);
|
||||
INSERT INTO b VALUES(4);
|
||||
INSERT INTO b VALUES(5);
|
||||
COMMIT;
|
||||
SELECT * FROM a WHERE id NOT IN (SELECT id FROM b);
|
||||
}
|
||||
} {}
|
||||
do_test in-13.14 {
|
||||
execsql {
|
||||
CREATE INDEX i5 ON b(id);
|
||||
SELECT * FROM a WHERE id NOT IN (SELECT id FROM b);
|
||||
}
|
||||
} {}
|
||||
|
||||
|
||||
do_test in-13.X {
|
||||
db nullvalue ""
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
||||
+8
-1
@@ -12,7 +12,7 @@
|
||||
# Test that it is possible to have two open blob handles on a single
|
||||
# blob object.
|
||||
#
|
||||
# $Id: incrblob2.test,v 1.6 2008/06/25 14:31:53 drh Exp $
|
||||
# $Id: incrblob2.test,v 1.7 2008/06/26 18:04:03 danielk1977 Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@@ -353,4 +353,11 @@ do_test incrblob2-7.5 {
|
||||
close $h
|
||||
} {}
|
||||
|
||||
#do_test incrblob2-8.5 {
|
||||
#execsql BEGIN
|
||||
#db eval {SELECT * FROM t2} {
|
||||
#execsql "DROP TABLE t2"
|
||||
#}
|
||||
#} {}
|
||||
|
||||
finish_test
|
||||
|
||||
Reference in New Issue
Block a user