From: danielk1977 Date: Thu, 26 Jun 2008 18:04:03 +0000 (+0000) Subject: Fix handling of "x IN (...)" and "x NOT IN (...)" expressions when the set contains... X-Git-Tag: version-3.6.10~863 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0cdc022e88617d70c1c49f246c2e84e9177274a9;p=thirdparty%2Fsqlite.git Fix handling of "x IN (...)" and "x NOT IN (...)" expressions when the set contains an SQL NULL value. (CVS 5314) FossilOrigin-Name: d45a97be71fa61ab4a692bd807ab762130f7f5b9 --- diff --git a/manifest b/manifest index 769a500573..712f6c42c0 100644 --- a/manifest +++ b/manifest @@ -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 diff --git a/manifest.uuid b/manifest.uuid index b2d89f7a25..4cd97bfb7c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e1de2287fd9b067f687cb8b427786b878af6c5b7 \ No newline at end of file +d45a97be71fa61ab4a692bd807ab762130f7f5b9 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 877469eafd..96836ed682 100644 --- a/src/expr.c +++ b/src/expr.c @@ -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 @@ -1673,22 +1673,46 @@ static int isCandidateForInOpt(Select *p){ ** ** SELECT FROM ** -** 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 is an INTEGER PRIMARY KEY or an index can -** be found with 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 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 is an INTEGER PRIMARY KEY or an index can +** be found with 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 = +** register = 1 +** } +** +** in order to avoid running the +** 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()' @@ -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; diff --git a/src/select.c b/src/select.c index 7045c32b26..0f937226ef 100644 --- a/src/select.c +++ b/src/select.c @@ -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. diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 7757f6915b..2d65817614 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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); diff --git a/src/vdbe.c b/src/vdbe.c index 20207343d4..79e5e33da5 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -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 @@ -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. diff --git a/src/where.c b/src/where.c index a5ba55f0db..009dd4848d 100644 --- a/src/where.c +++ b/src/where.c @@ -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)); diff --git a/test/in.test b/test/in.test index 2f7201cc5b..b864172161 100644 --- a/test/in.test +++ b/test/in.test @@ -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 diff --git a/test/incrblob2.test b/test/incrblob2.test index 0f2346d032..7757930b49 100644 --- a/test/incrblob2.test +++ b/test/incrblob2.test @@ -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