mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
Add some support for using row value constructors in certain parts of SQL expressions. There are many bugs on this branch.
FossilOrigin-Name: b2204215b231202aef7a218411cc2ddaecf28f35
This commit is contained in:
39
manifest
39
manifest
@ -1,5 +1,5 @@
|
|||||||
C Add\sthe\s"#/value-list/"\sstyle\sof\sresults\sfor\sapproximate\svalue\smatching\nin\sthe\sdo_test\scommand\sof\sthe\stest\sinfrastructure.\s\sUse\sthis\snew\sresult\sstyle\nto\smake\sthe\sSQLITE_DBSTATUS_CACHE_SIZE_SHARED\stests\scross-platform.
|
C Add\ssome\ssupport\sfor\susing\srow\svalue\sconstructors\sin\scertain\sparts\sof\sSQL\sexpressions.\sThere\sare\smany\sbugs\son\sthis\sbranch.
|
||||||
D 2016-07-09T17:47:01.013
|
D 2016-07-09T20:23:55.716
|
||||||
F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db
|
F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db
|
||||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||||
F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a
|
F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a
|
||||||
@ -337,7 +337,7 @@ F src/ctime.c 61949e83c4c36e37195a8398ebc752780b534d95
|
|||||||
F src/date.c 1cc9fb516ec9932c6fd4d2a0d2f8bc4480145c39
|
F src/date.c 1cc9fb516ec9932c6fd4d2a0d2f8bc4480145c39
|
||||||
F src/dbstat.c 4f6f7f52b49beb9636ffbd517cfe44a402ba4ad0
|
F src/dbstat.c 4f6f7f52b49beb9636ffbd517cfe44a402ba4ad0
|
||||||
F src/delete.c 4aba4214a377ce8ddde2d2e609777bcc8235200f
|
F src/delete.c 4aba4214a377ce8ddde2d2e609777bcc8235200f
|
||||||
F src/expr.c 523a5b1db2b6d88c6eefb224877bf635a3bcfc92
|
F src/expr.c 330854fe9fdea1d244abaef6d680f6b91df07cb4
|
||||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||||
F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413
|
F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413
|
||||||
F src/func.c 61a4114cf7004f10c542cfabbab9f2bcb9033045
|
F src/func.c 61a4114cf7004f10c542cfabbab9f2bcb9033045
|
||||||
@ -372,7 +372,7 @@ F src/os_win.c 520f23475f1de530c435d30b67b7b15fe90874b0
|
|||||||
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
||||||
F src/pager.c c368634b888b1c8740aea83b36bfd266f2443e60
|
F src/pager.c c368634b888b1c8740aea83b36bfd266f2443e60
|
||||||
F src/pager.h 031a87445e5e0afc85312d1c380e123ad6c7aeaf
|
F src/pager.h 031a87445e5e0afc85312d1c380e123ad6c7aeaf
|
||||||
F src/parse.y f374ab20106362eb3f5c01b3e6a002f0bbead7ff
|
F src/parse.y fa040d742fe4922b219143fe2e04f74061daabcb
|
||||||
F src/pcache.c 5583c8ade4b05075a60ba953ef471d1c1a9c05df
|
F src/pcache.c 5583c8ade4b05075a60ba953ef471d1c1a9c05df
|
||||||
F src/pcache.h 2cedcd8407eb23017d92790b112186886e179490
|
F src/pcache.h 2cedcd8407eb23017d92790b112186886e179490
|
||||||
F src/pcache1.c 7f51d2b541aab57596adf62db2c4bb025d34f04d
|
F src/pcache1.c 7f51d2b541aab57596adf62db2c4bb025d34f04d
|
||||||
@ -381,14 +381,14 @@ F src/pragma.h 64c78a648751b9f4f297276c4eb7507b14b4628c
|
|||||||
F src/prepare.c 22df6171aec1d86904ed2ad30c2348a5748aa04e
|
F src/prepare.c 22df6171aec1d86904ed2ad30c2348a5748aa04e
|
||||||
F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1
|
F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1
|
||||||
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
|
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
|
||||||
F src/resolve.c cca3aa77b95706df5d635a2141a4d1de60ae6598
|
F src/resolve.c 9680caadd54772699e5a0a8ebd680e014703e4ee
|
||||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||||
F src/select.c f3c6e9065fb34f6a23af27ec7f1f717ffbfc2ee4
|
F src/select.c 0115f5d222f5cf9b5511ec4072088417354d738a
|
||||||
F src/shell.c 14ff7f660530a52b117d110ba3390b7b2eb719b6
|
F src/shell.c 14ff7f660530a52b117d110ba3390b7b2eb719b6
|
||||||
F src/sqlite.h.in b9ba728c1083b7a8ab5f6a628b25cd2a00325fbf
|
F src/sqlite.h.in b9ba728c1083b7a8ab5f6a628b25cd2a00325fbf
|
||||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||||
F src/sqlite3ext.h 2a170163d121095c6ab1ef05ed0413722f391d01
|
F src/sqlite3ext.h 2a170163d121095c6ab1ef05ed0413722f391d01
|
||||||
F src/sqliteInt.h dcf43b8abc5605b70f54ba80f42b6ad054b8ba95
|
F src/sqliteInt.h dd2dd1d880ffd33137d20dc6da21f169836b8f5a
|
||||||
F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247
|
F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247
|
||||||
F src/status.c 5b18f9526900f61189ab0b83f1ef41d9f871a2ab
|
F src/status.c 5b18f9526900f61189ab0b83f1ef41d9f871a2ab
|
||||||
F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9
|
F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9
|
||||||
@ -449,7 +449,7 @@ F src/update.c 4f05ea8cddfa367d045e03589756c02199e8f9bd
|
|||||||
F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
|
F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
|
||||||
F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d
|
F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d
|
||||||
F src/vacuum.c feb1eabb20987983d9350cad98299b21fa811f52
|
F src/vacuum.c feb1eabb20987983d9350cad98299b21fa811f52
|
||||||
F src/vdbe.c 22b46c3b725e950e9f2760e2d76953d592600ad4
|
F src/vdbe.c 680c118a20b4b496644001e7ff4819c3e3ff8d85
|
||||||
F src/vdbe.h 67bc551f7faf04c33493892e4b378aada823ed10
|
F src/vdbe.h 67bc551f7faf04c33493892e4b378aada823ed10
|
||||||
F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d
|
F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d
|
||||||
F src/vdbeapi.c 02bcbc2ca5d2004b029088b05b468b394881e103
|
F src/vdbeapi.c 02bcbc2ca5d2004b029088b05b468b394881e103
|
||||||
@ -463,10 +463,10 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
|||||||
F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a
|
F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a
|
||||||
F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2
|
F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2
|
||||||
F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354
|
F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354
|
||||||
F src/where.c 48eed8ebe319c6cbc7bf7682018f32af0f5189f5
|
F src/where.c 898a45969bae1298cbaaaf05e6aeacfb45971293
|
||||||
F src/whereInt.h e5b939701a7ceffc5a3a8188a37f9746416ebcd0
|
F src/whereInt.h 1ad3be2a43cb821418e1100476a3a14cd57635c4
|
||||||
F src/wherecode.c e20cb381ff621e56a4684c71e31999aca2547ca6
|
F src/wherecode.c a66c589bdcaa9da45d9576d54663666083e0c109
|
||||||
F src/whereexpr.c d7dcbf14ce1b5876c1f76496162c30fcba669563
|
F src/whereexpr.c d88ee6ce356cb9fd986d0e81249a2cd66a513093
|
||||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||||
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
||||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||||
@ -1017,6 +1017,8 @@ F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a
|
|||||||
F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc
|
F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc
|
||||||
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
|
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
|
||||||
F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d
|
F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d
|
||||||
|
F test/rowvalue.test 979738b3d49f1d93e3fee56a71d4446217917abc
|
||||||
|
F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff
|
||||||
F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
|
F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
|
||||||
F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09
|
F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09
|
||||||
F test/savepoint.test c671fdbd34cd3bfe1518a777526ada595180cf8d
|
F test/savepoint.test c671fdbd34cd3bfe1518a777526ada595180cf8d
|
||||||
@ -1419,7 +1421,7 @@ F test/zerodamage.test e59a56443d6298ecf7435f618f0b27654f0c849e
|
|||||||
F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5
|
F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5
|
||||||
F tool/GetTclKit.bat 629d87562e0487c386db630033931d12d62e6372
|
F tool/GetTclKit.bat 629d87562e0487c386db630033931d12d62e6372
|
||||||
F tool/Replace.cs 02c67258801c2fb5f63231e0ac0f220b4b36ba91
|
F tool/Replace.cs 02c67258801c2fb5f63231e0ac0f220b4b36ba91
|
||||||
F tool/addopcodes.tcl 2b089684eb8b7d0db64cf9d8e6d2fe1b6d279e8d
|
F tool/addopcodes.tcl 7d4954564d7d4bfde5ba139ebced25542696fa52
|
||||||
F tool/build-all-msvc.bat 3e4e4043b53f1aede4308e0d2567bbd773614630 x
|
F tool/build-all-msvc.bat 3e4e4043b53f1aede4308e0d2567bbd773614630 x
|
||||||
F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367
|
F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367
|
||||||
F tool/cg_anno.tcl 692ce4b8693d59e3a3de77ca97f4139ecfa641b0 x
|
F tool/cg_anno.tcl 692ce4b8693d59e3a3de77ca97f4139ecfa641b0 x
|
||||||
@ -1505,7 +1507,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
|||||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||||
P 29fb988f1afc3fe623097acee1a5d08bf8386626
|
P c869bf34a8ee42ac6542862e59c7a4b89b042f79
|
||||||
R 767a7d8abd9b9525d1b45069b4c3dba2
|
R e70f91ca2d7d2c655198f54d385cd62e
|
||||||
U drh
|
T *branch * rowvalue
|
||||||
Z df36acb2648e26f22b53c52b73476f6c
|
T *sym-rowvalue *
|
||||||
|
T -sym-trunk *
|
||||||
|
U dan
|
||||||
|
Z e86f169a98e6cbe5718a428ba6e6f5e0
|
||||||
|
@ -1 +1 @@
|
|||||||
c869bf34a8ee42ac6542862e59c7a4b89b042f79
|
b2204215b231202aef7a218411cc2ddaecf28f35
|
375
src/expr.c
375
src/expr.c
@ -309,6 +309,114 @@ static int codeCompare(
|
|||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int sqlite3ExprVectorSize(Expr *pExpr){
|
||||||
|
if( (pExpr->flags & EP_Vector)==0 ) return 1;
|
||||||
|
if( pExpr->flags & EP_xIsSelect ){
|
||||||
|
return pExpr->x.pSelect->pEList->nExpr;
|
||||||
|
}
|
||||||
|
return pExpr->x.pList->nExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Expr *exprVectorField(Expr *pVector, int i){
|
||||||
|
if( pVector->flags & EP_xIsSelect ){
|
||||||
|
return pVector->x.pSelect->pEList->a[i].pExpr;
|
||||||
|
}
|
||||||
|
return pVector->x.pList->a[i].pExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void codeVectorCompare(Parse *pParse, Expr *pExpr, int dest){
|
||||||
|
Vdbe *v = pParse->pVdbe;
|
||||||
|
Expr *pLeft = pExpr->pLeft;
|
||||||
|
Expr *pRight = pExpr->pRight;
|
||||||
|
int nLeft = sqlite3ExprVectorSize(pLeft);
|
||||||
|
int nRight = sqlite3ExprVectorSize(pRight);
|
||||||
|
int addr = sqlite3VdbeMakeLabel(v);
|
||||||
|
|
||||||
|
/* Check that both sides of the comparison are vectors, and that
|
||||||
|
** both are the same length. */
|
||||||
|
if( nLeft!=nRight ){
|
||||||
|
sqlite3ErrorMsg(pParse, "invalid use of row value");
|
||||||
|
}else{
|
||||||
|
int p5 = (pExpr->op==TK_IS || pExpr->op==TK_ISNOT) ? SQLITE_NULLEQ : 0;
|
||||||
|
int opCmp;
|
||||||
|
int opTest;
|
||||||
|
int i;
|
||||||
|
int p3 = 1;
|
||||||
|
int regLeft = 0;
|
||||||
|
int regRight = 0;
|
||||||
|
|
||||||
|
assert( pExpr->op==TK_EQ || pExpr->op==TK_NE
|
||||||
|
|| pExpr->op==TK_IS || pExpr->op==TK_ISNOT
|
||||||
|
|| pExpr->op==TK_LT || pExpr->op==TK_GT
|
||||||
|
|| pExpr->op==TK_LE || pExpr->op==TK_GE
|
||||||
|
);
|
||||||
|
|
||||||
|
switch( pExpr->op ){
|
||||||
|
case TK_EQ:
|
||||||
|
case TK_IS:
|
||||||
|
opTest = OP_IfNot;
|
||||||
|
opCmp = OP_Eq;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TK_NE:
|
||||||
|
case TK_ISNOT:
|
||||||
|
opTest = OP_If;
|
||||||
|
opCmp = OP_Ne;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TK_LT:
|
||||||
|
case TK_LE:
|
||||||
|
case TK_GT:
|
||||||
|
case TK_GE:
|
||||||
|
opCmp = OP_Cmp;
|
||||||
|
opTest = OP_CmpTest;
|
||||||
|
p3 = pExpr->op;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( pLeft->flags & EP_xIsSelect ){
|
||||||
|
assert( pLeft->op==TK_SELECT || pLeft->op==TK_REGISTER );
|
||||||
|
regLeft = sqlite3ExprCodeTarget(pParse, pLeft, 1);
|
||||||
|
assert( regLeft!=1 );
|
||||||
|
}
|
||||||
|
if( pRight->flags & EP_xIsSelect ){
|
||||||
|
assert( pRight->op==TK_SELECT || pRight->op==TK_REGISTER );
|
||||||
|
regRight = sqlite3ExprCodeTarget(pParse, pRight, 1);
|
||||||
|
assert( regRight!=1 );
|
||||||
|
}
|
||||||
|
if( pParse->nErr ) return;
|
||||||
|
|
||||||
|
for(i=0; i<nLeft; i++){
|
||||||
|
int regFree1 = 0, regFree2 = 0;
|
||||||
|
Expr *pL, *pR;
|
||||||
|
int r1, r2;
|
||||||
|
|
||||||
|
if( regLeft ){
|
||||||
|
pL = pLeft->x.pSelect->pEList->a[i].pExpr;
|
||||||
|
r1 = regLeft+i;
|
||||||
|
}else{
|
||||||
|
pL = pLeft->x.pList->a[i].pExpr;
|
||||||
|
r1 = sqlite3ExprCodeTemp(pParse, pL, ®Free1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if( regRight ){
|
||||||
|
pR = pRight->x.pSelect->pEList->a[i].pExpr;
|
||||||
|
r2 = regRight+i;
|
||||||
|
}else{
|
||||||
|
pR = pRight->x.pList->a[i].pExpr;
|
||||||
|
r2 = sqlite3ExprCodeTemp(pParse, pR, ®Free1);
|
||||||
|
}
|
||||||
|
|
||||||
|
codeCompare(pParse, pL, pR, opCmp, r1, r2, dest, SQLITE_STOREP2 | p5);
|
||||||
|
sqlite3VdbeAddOp3(v, opTest, dest, addr, p3);
|
||||||
|
sqlite3ReleaseTempReg(pParse, regFree1);
|
||||||
|
sqlite3ReleaseTempReg(pParse, regFree2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3VdbeResolveLabel(v, addr);
|
||||||
|
}
|
||||||
|
|
||||||
#if SQLITE_MAX_EXPR_DEPTH>0
|
#if SQLITE_MAX_EXPR_DEPTH>0
|
||||||
/*
|
/*
|
||||||
** Check that argument nHeight is less than or equal to the maximum
|
** Check that argument nHeight is less than or equal to the maximum
|
||||||
@ -743,7 +851,7 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
|
|||||||
if( !ExprHasProperty(p, EP_TokenOnly) ){
|
if( !ExprHasProperty(p, EP_TokenOnly) ){
|
||||||
/* The Expr.x union is never used at the same time as Expr.pRight */
|
/* The Expr.x union is never used at the same time as Expr.pRight */
|
||||||
assert( p->x.pList==0 || p->pRight==0 );
|
assert( p->x.pList==0 || p->pRight==0 );
|
||||||
sqlite3ExprDelete(db, p->pLeft);
|
if( p->op!=TK_SELECT_COLUMN ) sqlite3ExprDelete(db, p->pLeft);
|
||||||
sqlite3ExprDelete(db, p->pRight);
|
sqlite3ExprDelete(db, p->pRight);
|
||||||
if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken);
|
if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken);
|
||||||
if( ExprHasProperty(p, EP_xIsSelect) ){
|
if( ExprHasProperty(p, EP_xIsSelect) ){
|
||||||
@ -1835,7 +1943,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){
|
|||||||
/* If no preexisting index is available for the IN clause
|
/* If no preexisting index is available for the IN clause
|
||||||
** and IN_INDEX_NOOP is an allowed reply
|
** and IN_INDEX_NOOP is an allowed reply
|
||||||
** and the RHS of the IN operator is a list, not a subquery
|
** and the RHS of the IN operator is a list, not a subquery
|
||||||
** and the RHS is not contant or has two or fewer terms,
|
** and the RHS is not constant or has two or fewer terms,
|
||||||
** then it is not worth creating an ephemeral table to evaluate
|
** then it is not worth creating an ephemeral table to evaluate
|
||||||
** the IN operator so return IN_INDEX_NOOP.
|
** the IN operator so return IN_INDEX_NOOP.
|
||||||
*/
|
*/
|
||||||
@ -1872,6 +1980,30 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static char *exprINAffinity(Parse *pParse, Expr *pExpr){
|
||||||
|
Expr *pLeft = pExpr->pLeft;
|
||||||
|
int nVal = sqlite3ExprVectorSize(pLeft);
|
||||||
|
char *zRet;
|
||||||
|
|
||||||
|
zRet = sqlite3DbMallocZero(pParse->db, nVal+1);
|
||||||
|
if( zRet ){
|
||||||
|
int i;
|
||||||
|
for(i=0; i<nVal; i++){
|
||||||
|
Expr *pA;
|
||||||
|
char a;
|
||||||
|
if( nVal==1 ){
|
||||||
|
pA = pLeft;
|
||||||
|
}else{
|
||||||
|
pA = exprVectorField(pLeft, i);
|
||||||
|
}
|
||||||
|
a = sqlite3ExprAffinity(pA);
|
||||||
|
zRet[i] = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[i].pExpr, a);
|
||||||
|
}
|
||||||
|
zRet[nVal] = '\0';
|
||||||
|
}
|
||||||
|
return zRet;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Generate code for scalar subqueries used as a subquery expression, EXISTS,
|
** Generate code for scalar subqueries used as a subquery expression, EXISTS,
|
||||||
** or IN operators. Examples:
|
** or IN operators. Examples:
|
||||||
@ -1939,12 +2071,12 @@ int sqlite3CodeSubselect(
|
|||||||
|
|
||||||
switch( pExpr->op ){
|
switch( pExpr->op ){
|
||||||
case TK_IN: {
|
case TK_IN: {
|
||||||
char affinity; /* Affinity of the LHS of the IN */
|
|
||||||
int addr; /* Address of OP_OpenEphemeral instruction */
|
int addr; /* Address of OP_OpenEphemeral instruction */
|
||||||
Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */
|
Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */
|
||||||
KeyInfo *pKeyInfo = 0; /* Key information */
|
KeyInfo *pKeyInfo = 0; /* Key information */
|
||||||
|
int nVal; /* Size of vector pLeft */
|
||||||
affinity = sqlite3ExprAffinity(pLeft);
|
|
||||||
|
nVal = sqlite3ExprVectorSize(pLeft);
|
||||||
|
|
||||||
/* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)'
|
/* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)'
|
||||||
** expression it is handled the same way. An ephemeral table is
|
** expression it is handled the same way. An ephemeral table is
|
||||||
@ -1960,8 +2092,9 @@ int sqlite3CodeSubselect(
|
|||||||
** is used.
|
** is used.
|
||||||
*/
|
*/
|
||||||
pExpr->iTable = pParse->nTab++;
|
pExpr->iTable = pParse->nTab++;
|
||||||
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid);
|
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral,
|
||||||
pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, 1, 1);
|
pExpr->iTable, (isRowid?0:nVal));
|
||||||
|
pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, nVal, 1);
|
||||||
|
|
||||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||||
/* Case 1: expr IN (SELECT ...)
|
/* Case 1: expr IN (SELECT ...)
|
||||||
@ -1970,27 +2103,38 @@ int sqlite3CodeSubselect(
|
|||||||
** table allocated and opened above.
|
** table allocated and opened above.
|
||||||
*/
|
*/
|
||||||
Select *pSelect = pExpr->x.pSelect;
|
Select *pSelect = pExpr->x.pSelect;
|
||||||
SelectDest dest;
|
ExprList *pEList = pSelect->pEList;
|
||||||
ExprList *pEList;
|
|
||||||
|
|
||||||
assert( !isRowid );
|
assert( !isRowid );
|
||||||
sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
|
if( pEList->nExpr!=nVal ){
|
||||||
dest.affSdst = (u8)affinity;
|
sqlite3ErrorMsg(pParse, "SELECT has %d columns - expected %d",
|
||||||
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
|
pEList->nExpr, nVal);
|
||||||
pSelect->iLimit = 0;
|
}else{
|
||||||
testcase( pSelect->selFlags & SF_Distinct );
|
SelectDest dest;
|
||||||
testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */
|
int i;
|
||||||
if( sqlite3Select(pParse, pSelect, &dest) ){
|
sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
|
||||||
sqlite3KeyInfoUnref(pKeyInfo);
|
dest.zAffSdst = exprINAffinity(pParse, pExpr);
|
||||||
return 0;
|
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
|
||||||
|
pSelect->iLimit = 0;
|
||||||
|
testcase( pSelect->selFlags & SF_Distinct );
|
||||||
|
testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */
|
||||||
|
if( sqlite3Select(pParse, pSelect, &dest) ){
|
||||||
|
sqlite3DbFree(pParse->db, dest.zAffSdst);
|
||||||
|
sqlite3KeyInfoUnref(pKeyInfo);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
sqlite3DbFree(pParse->db, dest.zAffSdst);
|
||||||
|
assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */
|
||||||
|
assert( pEList!=0 );
|
||||||
|
assert( pEList->nExpr>0 );
|
||||||
|
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
|
||||||
|
for(i=0; i<nVal; i++){
|
||||||
|
Expr *p = (nVal>1) ? exprVectorField(pLeft, i) : pLeft;
|
||||||
|
pKeyInfo->aColl[i] = sqlite3BinaryCompareCollSeq(
|
||||||
|
pParse, p, pEList->a[i].pExpr
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pEList = pSelect->pEList;
|
|
||||||
assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */
|
|
||||||
assert( pEList!=0 );
|
|
||||||
assert( pEList->nExpr>0 );
|
|
||||||
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
|
|
||||||
pKeyInfo->aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft,
|
|
||||||
pEList->a[0].pExpr);
|
|
||||||
}else if( ALWAYS(pExpr->x.pList!=0) ){
|
}else if( ALWAYS(pExpr->x.pList!=0) ){
|
||||||
/* Case 2: expr IN (exprlist)
|
/* Case 2: expr IN (exprlist)
|
||||||
**
|
**
|
||||||
@ -1999,11 +2143,13 @@ int sqlite3CodeSubselect(
|
|||||||
** that columns affinity when building index keys. If <expr> is not
|
** that columns affinity when building index keys. If <expr> is not
|
||||||
** a column, use numeric affinity.
|
** a column, use numeric affinity.
|
||||||
*/
|
*/
|
||||||
|
char affinity; /* Affinity of the LHS of the IN */
|
||||||
int i;
|
int i;
|
||||||
ExprList *pList = pExpr->x.pList;
|
ExprList *pList = pExpr->x.pList;
|
||||||
struct ExprList_item *pItem;
|
struct ExprList_item *pItem;
|
||||||
int r1, r2, r3;
|
int r1, r2, r3;
|
||||||
|
|
||||||
|
affinity = sqlite3ExprAffinity(pLeft);
|
||||||
if( !affinity ){
|
if( !affinity ){
|
||||||
affinity = SQLITE_AFF_BLOB;
|
affinity = SQLITE_AFF_BLOB;
|
||||||
}
|
}
|
||||||
@ -2067,18 +2213,22 @@ int sqlite3CodeSubselect(
|
|||||||
*/
|
*/
|
||||||
Select *pSel; /* SELECT statement to encode */
|
Select *pSel; /* SELECT statement to encode */
|
||||||
SelectDest dest; /* How to deal with SELECt result */
|
SelectDest dest; /* How to deal with SELECt result */
|
||||||
|
int nReg; /* Registers to allocate */
|
||||||
|
|
||||||
testcase( pExpr->op==TK_EXISTS );
|
testcase( pExpr->op==TK_EXISTS );
|
||||||
testcase( pExpr->op==TK_SELECT );
|
testcase( pExpr->op==TK_SELECT );
|
||||||
assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT );
|
assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT );
|
||||||
|
|
||||||
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
|
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||||
|
|
||||||
pSel = pExpr->x.pSelect;
|
pSel = pExpr->x.pSelect;
|
||||||
sqlite3SelectDestInit(&dest, 0, ++pParse->nMem);
|
nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1;
|
||||||
|
sqlite3SelectDestInit(&dest, 0, pParse->nMem+1);
|
||||||
|
pParse->nMem += nReg;
|
||||||
if( pExpr->op==TK_SELECT ){
|
if( pExpr->op==TK_SELECT ){
|
||||||
dest.eDest = SRT_Mem;
|
dest.eDest = SRT_Mem;
|
||||||
dest.iSdst = dest.iSDParm;
|
dest.iSdst = dest.iSDParm;
|
||||||
sqlite3VdbeAddOp2(v, OP_Null, 0, dest.iSDParm);
|
dest.nSdst = nReg;
|
||||||
|
sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, dest.iSDParm+nReg-1);
|
||||||
VdbeComment((v, "Init subquery result"));
|
VdbeComment((v, "Init subquery result"));
|
||||||
}else{
|
}else{
|
||||||
dest.eDest = SRT_Exists;
|
dest.eDest = SRT_Exists;
|
||||||
@ -2112,6 +2262,75 @@ int sqlite3CodeSubselect(
|
|||||||
}
|
}
|
||||||
#endif /* SQLITE_OMIT_SUBQUERY */
|
#endif /* SQLITE_OMIT_SUBQUERY */
|
||||||
|
|
||||||
|
#ifndef SQLITE_OMIT_SUBQUERY
|
||||||
|
void exprCodeVectorIN(
|
||||||
|
Parse *pParse, /* Parsing and code generating context */
|
||||||
|
Expr *pExpr, /* The IN expression */
|
||||||
|
int destIfFalse, /* Jump here if LHS is not contained in the RHS */
|
||||||
|
int destIfNull /* Jump here if the results are unknown due to NULLs */
|
||||||
|
){
|
||||||
|
int i;
|
||||||
|
int addrNext;
|
||||||
|
int iSkip;
|
||||||
|
int r1;
|
||||||
|
int r2 = sqlite3GetTempReg(pParse);
|
||||||
|
int r3 = sqlite3GetTempReg(pParse);
|
||||||
|
int r4 = sqlite3GetTempReg(pParse);
|
||||||
|
int regResult = sqlite3GetTempReg(pParse);
|
||||||
|
int nVal = sqlite3ExprVectorSize(pExpr->pLeft);
|
||||||
|
|
||||||
|
Expr *pLeft = pExpr->pLeft;
|
||||||
|
Vdbe *v = pParse->pVdbe;
|
||||||
|
|
||||||
|
/* Code the LHS, the <expr> from "<expr> IN (...)". Leave the results in
|
||||||
|
** an array of nVal registers starting at r1. */
|
||||||
|
sqlite3ExprCachePush(pParse);
|
||||||
|
if( pLeft->flags & EP_xIsSelect ){
|
||||||
|
r1 = sqlite3CodeSubselect(pParse, pLeft, 0, 0);
|
||||||
|
}else{
|
||||||
|
r1 = pParse->nMem + 1;
|
||||||
|
pParse->nMem += nVal;
|
||||||
|
sqlite3ExprCodeExprList(pParse, pLeft->x.pList, r1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate an epheremal index containing the contents of the SELECT
|
||||||
|
** to the right of the "<expr> IN (SELECT ...)" expression. The cursor
|
||||||
|
** number for the epheremal table is left in pExpr->iTable. */
|
||||||
|
assert( pExpr->flags & EP_xIsSelect );
|
||||||
|
sqlite3CodeSubselect(pParse, pExpr, 0, 0);
|
||||||
|
|
||||||
|
sqlite3VdbeAddOp2(v, OP_Integer, 0, regResult);
|
||||||
|
|
||||||
|
/* Iterate through the ephemeral table just populated */
|
||||||
|
addrNext = 1 + sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse);
|
||||||
|
for(i=0; i<nVal; i++){
|
||||||
|
Expr *p;
|
||||||
|
CollSeq *pColl;
|
||||||
|
p = exprVectorField(pLeft, i);
|
||||||
|
pColl = sqlite3ExprCollSeq(pParse, p);
|
||||||
|
sqlite3VdbeAddOp3(v, OP_Column, pExpr->iTable, i, r2);
|
||||||
|
sqlite3VdbeAddOp4(v, OP_Eq, r1+i, i==0?r3:r4, r2, (void*)pColl,P4_COLLSEQ);
|
||||||
|
sqlite3VdbeChangeP5(v, SQLITE_STOREP2);
|
||||||
|
VdbeCoverage(v);
|
||||||
|
if( i!=0 ){
|
||||||
|
sqlite3VdbeAddOp3(v, OP_And, r3, r4, r4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite3VdbeAddOp2(v, OP_If, r4, sqlite3VdbeCurrentAddr(v)+6);
|
||||||
|
sqlite3VdbeAddOp2(v, OP_IfNot, r4, sqlite3VdbeCurrentAddr(v)+2);
|
||||||
|
sqlite3VdbeAddOp2(v, OP_Null, 0, regResult);
|
||||||
|
sqlite3VdbeAddOp2(v, OP_Next, pExpr->iTable, addrNext);
|
||||||
|
sqlite3VdbeAddOp3(v, OP_If, regResult, destIfNull, 1);
|
||||||
|
sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse);
|
||||||
|
|
||||||
|
sqlite3ReleaseTempReg(pParse, r2);
|
||||||
|
sqlite3ReleaseTempReg(pParse, r3);
|
||||||
|
sqlite3ReleaseTempReg(pParse, r4);
|
||||||
|
sqlite3ReleaseTempReg(pParse, regResult);
|
||||||
|
sqlite3ExprCachePop(pParse);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef SQLITE_OMIT_SUBQUERY
|
#ifndef SQLITE_OMIT_SUBQUERY
|
||||||
/*
|
/*
|
||||||
** Generate code for an IN expression.
|
** Generate code for an IN expression.
|
||||||
@ -2142,6 +2361,10 @@ static void sqlite3ExprCodeIN(
|
|||||||
int r1; /* Temporary use register */
|
int r1; /* Temporary use register */
|
||||||
Vdbe *v; /* Statement under construction */
|
Vdbe *v; /* Statement under construction */
|
||||||
|
|
||||||
|
if( pExpr->pLeft->flags & EP_Vector ){
|
||||||
|
return exprCodeVectorIN(pParse, pExpr, destIfFalse, destIfNull);
|
||||||
|
}
|
||||||
|
|
||||||
/* Compute the RHS. After this step, the table with cursor
|
/* Compute the RHS. After this step, the table with cursor
|
||||||
** pExpr->iTable will contains the values that make up the RHS.
|
** pExpr->iTable will contains the values that make up the RHS.
|
||||||
*/
|
*/
|
||||||
@ -2669,6 +2892,8 @@ static void exprToRegister(Expr *p, int iReg){
|
|||||||
ExprClearProperty(p, EP_Skip);
|
ExprClearProperty(p, EP_Skip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void exprCodeBetween(Parse*,Expr*,int,void(*)(Parse*,Expr*,int,int),int);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Generate code into the current Vdbe to evaluate the given
|
** Generate code into the current Vdbe to evaluate the given
|
||||||
** expression. Attempt to store the results in register "target".
|
** expression. Attempt to store the results in register "target".
|
||||||
@ -2689,6 +2914,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||||||
int r1, r2, r3, r4; /* Various register numbers */
|
int r1, r2, r3, r4; /* Various register numbers */
|
||||||
sqlite3 *db = pParse->db; /* The database connection */
|
sqlite3 *db = pParse->db; /* The database connection */
|
||||||
Expr tempX; /* Temporary expression node */
|
Expr tempX; /* Temporary expression node */
|
||||||
|
int p5 = 0;
|
||||||
|
|
||||||
assert( target>0 && target<=pParse->nMem );
|
assert( target>0 && target<=pParse->nMem );
|
||||||
if( v==0 ){
|
if( v==0 ){
|
||||||
@ -2801,39 +3027,34 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif /* SQLITE_OMIT_CAST */
|
#endif /* SQLITE_OMIT_CAST */
|
||||||
|
case TK_IS:
|
||||||
|
case TK_ISNOT:
|
||||||
|
op = (op==TK_IS) ? TK_EQ : TK_NE;
|
||||||
|
p5 = SQLITE_NULLEQ;
|
||||||
|
/* fall-through */
|
||||||
case TK_LT:
|
case TK_LT:
|
||||||
case TK_LE:
|
case TK_LE:
|
||||||
case TK_GT:
|
case TK_GT:
|
||||||
case TK_GE:
|
case TK_GE:
|
||||||
case TK_NE:
|
case TK_NE:
|
||||||
case TK_EQ: {
|
case TK_EQ: {
|
||||||
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1);
|
Expr *pLeft = pExpr->pLeft;
|
||||||
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2);
|
if( (pLeft->flags & EP_Vector) ){
|
||||||
codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
|
codeVectorCompare(pParse, pExpr, target);
|
||||||
r1, r2, inReg, SQLITE_STOREP2);
|
}else{
|
||||||
assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
|
r1 = sqlite3ExprCodeTemp(pParse, pLeft, ®Free1);
|
||||||
assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
|
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2);
|
||||||
assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
|
codeCompare(pParse, pLeft, pExpr->pRight, op,
|
||||||
assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge);
|
r1, r2, inReg, SQLITE_STOREP2 | p5);
|
||||||
assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq);
|
assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
|
||||||
assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
|
assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
|
||||||
testcase( regFree1==0 );
|
assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
|
||||||
testcase( regFree2==0 );
|
assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge);
|
||||||
break;
|
assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq);
|
||||||
}
|
assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
|
||||||
case TK_IS:
|
testcase( regFree1==0 );
|
||||||
case TK_ISNOT: {
|
testcase( regFree2==0 );
|
||||||
testcase( op==TK_IS );
|
}
|
||||||
testcase( op==TK_ISNOT );
|
|
||||||
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1);
|
|
||||||
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2);
|
|
||||||
op = (op==TK_IS) ? TK_EQ : TK_NE;
|
|
||||||
codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
|
|
||||||
r1, r2, inReg, SQLITE_STOREP2 | SQLITE_NULLEQ);
|
|
||||||
VdbeCoverageIf(v, op==TK_EQ);
|
|
||||||
VdbeCoverageIf(v, op==TK_NE);
|
|
||||||
testcase( regFree1==0 );
|
|
||||||
testcase( regFree2==0 );
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TK_AND:
|
case TK_AND:
|
||||||
@ -3085,6 +3306,8 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||||||
** Z is stored in pExpr->pList->a[1].pExpr.
|
** Z is stored in pExpr->pList->a[1].pExpr.
|
||||||
*/
|
*/
|
||||||
case TK_BETWEEN: {
|
case TK_BETWEEN: {
|
||||||
|
exprCodeBetween(pParse, pExpr, target, 0, 0);
|
||||||
|
#if 0
|
||||||
Expr *pLeft = pExpr->pLeft;
|
Expr *pLeft = pExpr->pLeft;
|
||||||
struct ExprList_item *pLItem = pExpr->x.pList->a;
|
struct ExprList_item *pLItem = pExpr->x.pList->a;
|
||||||
Expr *pRight = pLItem->pExpr;
|
Expr *pRight = pLItem->pExpr;
|
||||||
@ -3107,6 +3330,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||||||
sqlite3VdbeAddOp3(v, OP_And, r3, r4, target);
|
sqlite3VdbeAddOp3(v, OP_And, r3, r4, target);
|
||||||
sqlite3ReleaseTempReg(pParse, r3);
|
sqlite3ReleaseTempReg(pParse, r3);
|
||||||
sqlite3ReleaseTempReg(pParse, r4);
|
sqlite3ReleaseTempReg(pParse, r4);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TK_SPAN:
|
case TK_SPAN:
|
||||||
@ -3172,6 +3396,22 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case TK_VECTOR: {
|
||||||
|
sqlite3ErrorMsg(pParse, "invalid use of row value (1)");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TK_SELECT_COLUMN: {
|
||||||
|
Expr *pLeft = pExpr->pLeft;
|
||||||
|
assert( pLeft );
|
||||||
|
assert( pLeft->op==TK_SELECT || pLeft->op==TK_REGISTER );
|
||||||
|
if( pLeft->op==TK_SELECT ){
|
||||||
|
pLeft->iTable = sqlite3CodeSubselect(pParse, pLeft, 0, 0);
|
||||||
|
pLeft->op = TK_REGISTER;
|
||||||
|
}
|
||||||
|
inReg = pLeft->iTable + pExpr->iColumn;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Form A:
|
** Form A:
|
||||||
@ -3500,7 +3740,7 @@ static void exprCodeBetween(
|
|||||||
Parse *pParse, /* Parsing and code generating context */
|
Parse *pParse, /* Parsing and code generating context */
|
||||||
Expr *pExpr, /* The BETWEEN expression */
|
Expr *pExpr, /* The BETWEEN expression */
|
||||||
int dest, /* Jump here if the jump is taken */
|
int dest, /* Jump here if the jump is taken */
|
||||||
int jumpIfTrue, /* Take the jump if the BETWEEN is true */
|
void (*xJumpIf)(Parse*,Expr*,int,int),
|
||||||
int jumpIfNull /* Take the jump if the BETWEEN is NULL */
|
int jumpIfNull /* Take the jump if the BETWEEN is NULL */
|
||||||
){
|
){
|
||||||
Expr exprAnd; /* The AND operator in x>=y AND x<=z */
|
Expr exprAnd; /* The AND operator in x>=y AND x<=z */
|
||||||
@ -3509,6 +3749,10 @@ static void exprCodeBetween(
|
|||||||
Expr exprX; /* The x subexpression */
|
Expr exprX; /* The x subexpression */
|
||||||
int regFree1 = 0; /* Temporary use register */
|
int regFree1 = 0; /* Temporary use register */
|
||||||
|
|
||||||
|
memset(&compLeft, 0, sizeof(Expr));
|
||||||
|
memset(&compRight, 0, sizeof(Expr));
|
||||||
|
memset(&exprAnd, 0, sizeof(Expr));
|
||||||
|
|
||||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||||
exprX = *pExpr->pLeft;
|
exprX = *pExpr->pLeft;
|
||||||
exprAnd.op = TK_AND;
|
exprAnd.op = TK_AND;
|
||||||
@ -3520,11 +3764,14 @@ static void exprCodeBetween(
|
|||||||
compRight.op = TK_LE;
|
compRight.op = TK_LE;
|
||||||
compRight.pLeft = &exprX;
|
compRight.pLeft = &exprX;
|
||||||
compRight.pRight = pExpr->x.pList->a[1].pExpr;
|
compRight.pRight = pExpr->x.pList->a[1].pExpr;
|
||||||
exprToRegister(&exprX, sqlite3ExprCodeTemp(pParse, &exprX, ®Free1));
|
if( (exprX.flags & EP_Vector)==0 ){
|
||||||
if( jumpIfTrue ){
|
exprToRegister(&exprX, sqlite3ExprCodeTemp(pParse, &exprX, ®Free1));
|
||||||
sqlite3ExprIfTrue(pParse, &exprAnd, dest, jumpIfNull);
|
}
|
||||||
|
if( xJumpIf ){
|
||||||
|
xJumpIf(pParse, &exprAnd, dest, jumpIfNull);
|
||||||
}else{
|
}else{
|
||||||
sqlite3ExprIfFalse(pParse, &exprAnd, dest, jumpIfNull);
|
exprX.flags |= EP_FromJoin;
|
||||||
|
sqlite3ExprCodeTarget(pParse, &exprAnd, dest);
|
||||||
}
|
}
|
||||||
sqlite3ReleaseTempReg(pParse, regFree1);
|
sqlite3ReleaseTempReg(pParse, regFree1);
|
||||||
|
|
||||||
@ -3564,7 +3811,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
|||||||
if( NEVER(v==0) ) return; /* Existence of VDBE checked by caller */
|
if( NEVER(v==0) ) return; /* Existence of VDBE checked by caller */
|
||||||
if( NEVER(pExpr==0) ) return; /* No way this can happen */
|
if( NEVER(pExpr==0) ) return; /* No way this can happen */
|
||||||
op = pExpr->op;
|
op = pExpr->op;
|
||||||
switch( op ){
|
switch( op | (pExpr->pLeft ? (pExpr->pLeft->flags & EP_Vector) : 0)){
|
||||||
case TK_AND: {
|
case TK_AND: {
|
||||||
int d2 = sqlite3VdbeMakeLabel(v);
|
int d2 = sqlite3VdbeMakeLabel(v);
|
||||||
testcase( jumpIfNull==0 );
|
testcase( jumpIfNull==0 );
|
||||||
@ -3633,7 +3880,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
|||||||
}
|
}
|
||||||
case TK_BETWEEN: {
|
case TK_BETWEEN: {
|
||||||
testcase( jumpIfNull==0 );
|
testcase( jumpIfNull==0 );
|
||||||
exprCodeBetween(pParse, pExpr, dest, 1, jumpIfNull);
|
exprCodeBetween(pParse, pExpr, dest, sqlite3ExprIfTrue, jumpIfNull);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#ifndef SQLITE_OMIT_SUBQUERY
|
#ifndef SQLITE_OMIT_SUBQUERY
|
||||||
@ -3716,7 +3963,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
|||||||
assert( pExpr->op!=TK_GT || op==OP_Le );
|
assert( pExpr->op!=TK_GT || op==OP_Le );
|
||||||
assert( pExpr->op!=TK_GE || op==OP_Lt );
|
assert( pExpr->op!=TK_GE || op==OP_Lt );
|
||||||
|
|
||||||
switch( pExpr->op ){
|
switch( pExpr->op | (pExpr->pLeft ? (pExpr->pLeft->flags & EP_Vector) : 0)){
|
||||||
case TK_AND: {
|
case TK_AND: {
|
||||||
testcase( jumpIfNull==0 );
|
testcase( jumpIfNull==0 );
|
||||||
sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
|
sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
|
||||||
@ -3783,7 +4030,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
|||||||
}
|
}
|
||||||
case TK_BETWEEN: {
|
case TK_BETWEEN: {
|
||||||
testcase( jumpIfNull==0 );
|
testcase( jumpIfNull==0 );
|
||||||
exprCodeBetween(pParse, pExpr, dest, 0, jumpIfNull);
|
exprCodeBetween(pParse, pExpr, dest, sqlite3ExprIfFalse, jumpIfNull);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#ifndef SQLITE_OMIT_SUBQUERY
|
#ifndef SQLITE_OMIT_SUBQUERY
|
||||||
|
@ -217,6 +217,7 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
|
|||||||
%left CONCAT.
|
%left CONCAT.
|
||||||
%left COLLATE.
|
%left COLLATE.
|
||||||
%right BITNOT.
|
%right BITNOT.
|
||||||
|
%right VECTOR.
|
||||||
|
|
||||||
// An IDENTIFIER can be a generic identifier, or one of several
|
// An IDENTIFIER can be a generic identifier, or one of several
|
||||||
// keywords. Any non-standard keyword can also be an identifier.
|
// keywords. Any non-standard keyword can also be an identifier.
|
||||||
@ -946,6 +947,14 @@ term(A) ::= CTIME_KW(OP). {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expr(A) ::= LP(L) nexprlist(X) COMMA expr(Y) RP(R). {
|
||||||
|
A.pExpr = sqlite3PExpr(pParse, TK_VECTOR, 0, 0, 0);
|
||||||
|
if( A.pExpr ){
|
||||||
|
A.pExpr->x.pList = sqlite3ExprListAppend(pParse, X, Y.pExpr);
|
||||||
|
spanSet(&A, &L, &R);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
expr(A) ::= expr(A) AND(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);}
|
expr(A) ::= expr(A) AND(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);}
|
||||||
expr(A) ::= expr(A) OR(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);}
|
expr(A) ::= expr(A) OR(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);}
|
||||||
expr(A) ::= expr(A) LT|GT|GE|LE(OP) expr(Y).
|
expr(A) ::= expr(A) LT|GT|GE|LE(OP) expr(Y).
|
||||||
|
@ -765,6 +765,17 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
|||||||
ExprSetProperty(pExpr, EP_VarSelect);
|
ExprSetProperty(pExpr, EP_VarSelect);
|
||||||
pNC->ncFlags |= NC_VarSelect;
|
pNC->ncFlags |= NC_VarSelect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( pExpr->op==TK_SELECT && pExpr->x.pSelect->pEList->nExpr>1 ){
|
||||||
|
if( !ExprHasProperty(pExpr, EP_VectorOk) ){
|
||||||
|
sqlite3ErrorMsg(pParse, "invalid use of row value");
|
||||||
|
}else{
|
||||||
|
ExprSetProperty(pExpr, EP_Vector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( pExpr->op==TK_IN ){
|
||||||
|
ExprSetProperty(pExpr->pLeft, EP_VectorOk);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -772,6 +783,30 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
|||||||
notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr);
|
notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case TK_BETWEEN: {
|
||||||
|
ExprSetProperty(pExpr->pLeft, EP_VectorOk);
|
||||||
|
ExprSetProperty(pExpr->x.pList->a[0].pExpr, EP_VectorOk);
|
||||||
|
ExprSetProperty(pExpr->x.pList->a[1].pExpr, EP_VectorOk);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TK_EQ: case TK_NE: case TK_IS: case TK_ISNOT:
|
||||||
|
case TK_LE: case TK_LT: case TK_GE: case TK_GT:
|
||||||
|
{
|
||||||
|
ExprSetProperty(pExpr->pLeft, EP_VectorOk);
|
||||||
|
ExprSetProperty(pExpr->pRight, EP_VectorOk);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
case TK_VECTOR: {
|
||||||
|
if( !ExprHasProperty(pExpr, EP_VectorOk) ){
|
||||||
|
sqlite3ErrorMsg(pParse, "invalid use of row value");
|
||||||
|
}else{
|
||||||
|
ExprSetProperty(pExpr, EP_Vector);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue;
|
return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue;
|
||||||
}
|
}
|
||||||
|
35
src/select.c
35
src/select.c
@ -88,7 +88,7 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){
|
|||||||
void sqlite3SelectDestInit(SelectDest *pDest, int eDest, int iParm){
|
void sqlite3SelectDestInit(SelectDest *pDest, int eDest, int iParm){
|
||||||
pDest->eDest = (u8)eDest;
|
pDest->eDest = (u8)eDest;
|
||||||
pDest->iSDParm = iParm;
|
pDest->iSDParm = iParm;
|
||||||
pDest->affSdst = 0;
|
pDest->zAffSdst = 0;
|
||||||
pDest->iSdst = 0;
|
pDest->iSdst = 0;
|
||||||
pDest->nSdst = 0;
|
pDest->nSdst = 0;
|
||||||
}
|
}
|
||||||
@ -673,7 +673,7 @@ static int checkForMultiColumnSelectError(
|
|||||||
int nExpr /* Number of result columns returned by SELECT */
|
int nExpr /* Number of result columns returned by SELECT */
|
||||||
){
|
){
|
||||||
int eDest = pDest->eDest;
|
int eDest = pDest->eDest;
|
||||||
if( nExpr>1 && (eDest==SRT_Mem || eDest==SRT_Set) ){
|
if( 0 && nExpr>1 && eDest==SRT_Set ){
|
||||||
sqlite3ErrorMsg(pParse, "only a single result allowed for "
|
sqlite3ErrorMsg(pParse, "only a single result allowed for "
|
||||||
"a SELECT that is part of an expression");
|
"a SELECT that is part of an expression");
|
||||||
return 1;
|
return 1;
|
||||||
@ -892,9 +892,6 @@ static void selectInnerLoop(
|
|||||||
** item into the set table with bogus data.
|
** item into the set table with bogus data.
|
||||||
*/
|
*/
|
||||||
case SRT_Set: {
|
case SRT_Set: {
|
||||||
assert( nResultCol==1 );
|
|
||||||
pDest->affSdst =
|
|
||||||
sqlite3CompareAffinity(pEList->a[0].pExpr, pDest->affSdst);
|
|
||||||
if( pSort ){
|
if( pSort ){
|
||||||
/* At first glance you would think we could optimize out the
|
/* At first glance you would think we could optimize out the
|
||||||
** ORDER BY in this case since the order of entries in the set
|
** ORDER BY in this case since the order of entries in the set
|
||||||
@ -903,8 +900,10 @@ static void selectInnerLoop(
|
|||||||
pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg);
|
pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg);
|
||||||
}else{
|
}else{
|
||||||
int r1 = sqlite3GetTempReg(pParse);
|
int r1 = sqlite3GetTempReg(pParse);
|
||||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult,1,r1, &pDest->affSdst, 1);
|
assert( sqlite3Strlen30(pDest->zAffSdst)==nResultCol );
|
||||||
sqlite3ExprCacheAffinityChange(pParse, regResult, 1);
|
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, nResultCol,
|
||||||
|
r1, pDest->zAffSdst, 1);
|
||||||
|
sqlite3ExprCacheAffinityChange(pParse, regResult, nResultCol);
|
||||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
|
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
|
||||||
sqlite3ReleaseTempReg(pParse, r1);
|
sqlite3ReleaseTempReg(pParse, r1);
|
||||||
}
|
}
|
||||||
@ -924,9 +923,9 @@ static void selectInnerLoop(
|
|||||||
** of the scan loop.
|
** of the scan loop.
|
||||||
*/
|
*/
|
||||||
case SRT_Mem: {
|
case SRT_Mem: {
|
||||||
assert( nResultCol==1 );
|
assert( nResultCol==pDest->nSdst );
|
||||||
if( pSort ){
|
if( pSort ){
|
||||||
pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg);
|
pushOntoSorter(pParse, pSort, p, regResult, regResult, nResultCol, nPrefixReg);
|
||||||
}else{
|
}else{
|
||||||
assert( regResult==iParm );
|
assert( regResult==iParm );
|
||||||
/* The LIMIT clause will jump out of the loop for us */
|
/* The LIMIT clause will jump out of the loop for us */
|
||||||
@ -1241,7 +1240,7 @@ static void generateSortTail(
|
|||||||
sqlite3VdbeResolveLabel(v, pSort->labelBkOut);
|
sqlite3VdbeResolveLabel(v, pSort->labelBkOut);
|
||||||
}
|
}
|
||||||
iTab = pSort->iECursor;
|
iTab = pSort->iECursor;
|
||||||
if( eDest==SRT_Output || eDest==SRT_Coroutine ){
|
if( eDest==SRT_Output || eDest==SRT_Coroutine || eDest==SRT_Mem ){
|
||||||
regRowid = 0;
|
regRowid = 0;
|
||||||
regRow = pDest->iSdst;
|
regRow = pDest->iSdst;
|
||||||
nSortData = nColumn;
|
nSortData = nColumn;
|
||||||
@ -1283,16 +1282,15 @@ static void generateSortTail(
|
|||||||
}
|
}
|
||||||
#ifndef SQLITE_OMIT_SUBQUERY
|
#ifndef SQLITE_OMIT_SUBQUERY
|
||||||
case SRT_Set: {
|
case SRT_Set: {
|
||||||
assert( nColumn==1 );
|
assert( nColumn==sqlite3Strlen30(pDest->zAffSdst) );
|
||||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, 1, regRowid,
|
sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, nColumn, regRowid,
|
||||||
&pDest->affSdst, 1);
|
pDest->zAffSdst, 1);
|
||||||
sqlite3ExprCacheAffinityChange(pParse, regRow, 1);
|
sqlite3ExprCacheAffinityChange(pParse, regRow, nColumn);
|
||||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRowid);
|
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRowid);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SRT_Mem: {
|
case SRT_Mem: {
|
||||||
assert( nColumn==1 );
|
/* sqlite3ExprCodeMove(pParse, regRow, iParm, nColumn); */
|
||||||
sqlite3ExprCodeMove(pParse, regRow, iParm, 1);
|
|
||||||
/* The LIMIT clause will terminate the loop for us */
|
/* The LIMIT clause will terminate the loop for us */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2659,10 +2657,9 @@ static int generateOutputSubroutine(
|
|||||||
case SRT_Set: {
|
case SRT_Set: {
|
||||||
int r1;
|
int r1;
|
||||||
assert( pIn->nSdst==1 || pParse->nErr>0 );
|
assert( pIn->nSdst==1 || pParse->nErr>0 );
|
||||||
pDest->affSdst =
|
|
||||||
sqlite3CompareAffinity(p->pEList->a[0].pExpr, pDest->affSdst);
|
|
||||||
r1 = sqlite3GetTempReg(pParse);
|
r1 = sqlite3GetTempReg(pParse);
|
||||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, 1, r1, &pDest->affSdst,1);
|
sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst,
|
||||||
|
r1, pDest->zAffSdst,1);
|
||||||
sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, 1);
|
sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, 1);
|
||||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, pDest->iSDParm, r1);
|
sqlite3VdbeAddOp2(v, OP_IdxInsert, pDest->iSDParm, r1);
|
||||||
sqlite3ReleaseTempReg(pParse, r1);
|
sqlite3ReleaseTempReg(pParse, r1);
|
||||||
|
@ -2317,6 +2317,8 @@ struct Expr {
|
|||||||
#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
|
#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
|
||||||
#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
|
#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
|
||||||
#define EP_Alias 0x400000 /* Is an alias for a result set column */
|
#define EP_Alias 0x400000 /* Is an alias for a result set column */
|
||||||
|
#define EP_VectorOk 0x800000 /* This expression may be a row value */
|
||||||
|
#define EP_Vector 0x1000000/* This expression is a row value */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Combinations of two or more EP_* flags
|
** Combinations of two or more EP_* flags
|
||||||
@ -2762,7 +2764,7 @@ struct Select {
|
|||||||
*/
|
*/
|
||||||
struct SelectDest {
|
struct SelectDest {
|
||||||
u8 eDest; /* How to dispose of the results. On of SRT_* above. */
|
u8 eDest; /* How to dispose of the results. On of SRT_* above. */
|
||||||
char affSdst; /* Affinity used when eDest==SRT_Set */
|
char *zAffSdst; /* Affinity used when eDest==SRT_Set */
|
||||||
int iSDParm; /* A parameter used by the eDest disposal method */
|
int iSDParm; /* A parameter used by the eDest disposal method */
|
||||||
int iSdst; /* Base register where results are written */
|
int iSdst; /* Base register where results are written */
|
||||||
int nSdst; /* Number of registers allocated */
|
int nSdst; /* Number of registers allocated */
|
||||||
@ -4237,4 +4239,6 @@ int sqlite3ThreadJoin(SQLiteThread*, void**);
|
|||||||
int sqlite3DbstatRegister(sqlite3*);
|
int sqlite3DbstatRegister(sqlite3*);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int sqlite3ExprVectorSize(Expr *pExpr);
|
||||||
|
|
||||||
#endif /* SQLITEINT_H */
|
#endif /* SQLITEINT_H */
|
||||||
|
40
src/vdbe.c
40
src/vdbe.c
@ -1958,6 +1958,7 @@ case OP_Cast: { /* in1 */
|
|||||||
** the content of register P3 is greater than or equal to the content of
|
** the content of register P3 is greater than or equal to the content of
|
||||||
** register P1. See the Lt opcode for additional information.
|
** register P1. See the Lt opcode for additional information.
|
||||||
*/
|
*/
|
||||||
|
case OP_Cmp: /* in1, in3 */
|
||||||
case OP_Eq: /* same as TK_EQ, jump, in1, in3 */
|
case OP_Eq: /* same as TK_EQ, jump, in1, in3 */
|
||||||
case OP_Ne: /* same as TK_NE, jump, in1, in3 */
|
case OP_Ne: /* same as TK_NE, jump, in1, in3 */
|
||||||
case OP_Lt: /* same as TK_LT, jump, in1, in3 */
|
case OP_Lt: /* same as TK_LT, jump, in1, in3 */
|
||||||
@ -2056,7 +2057,8 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
|
|||||||
case OP_Lt: res = res<0; break;
|
case OP_Lt: res = res<0; break;
|
||||||
case OP_Le: res = res<=0; break;
|
case OP_Le: res = res<=0; break;
|
||||||
case OP_Gt: res = res>0; break;
|
case OP_Gt: res = res>0; break;
|
||||||
default: res = res>=0; break;
|
case OP_Ge: res = res>=0; break;
|
||||||
|
default: assert( pOp->opcode==OP_Cmp ); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Undo any changes made by applyAffinity() to the input registers. */
|
/* Undo any changes made by applyAffinity() to the input registers. */
|
||||||
@ -2072,6 +2074,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
|
|||||||
pOut->u.i = res;
|
pOut->u.i = res;
|
||||||
REGISTER_TRACE(pOp->p2, pOut);
|
REGISTER_TRACE(pOp->p2, pOut);
|
||||||
}else{
|
}else{
|
||||||
|
assert( pOp->opcode!=OP_Cmp );
|
||||||
VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3);
|
VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3);
|
||||||
if( res ){
|
if( res ){
|
||||||
goto jump_to_p2;
|
goto jump_to_p2;
|
||||||
@ -3867,7 +3870,42 @@ seek_not_found:
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Opcode: CmpTest P1 P2 P3 * *
|
||||||
|
**
|
||||||
|
** P2 is a jump destination. Register P1 is guaranteed to contain either
|
||||||
|
** an integer value or a NULL. The jump is taken if P1 contains any value
|
||||||
|
** other than 0 (i.e. NULL does cause a jump).
|
||||||
|
**
|
||||||
|
** If P1 is not NULL, its value is modified to integer value 0 or 1
|
||||||
|
** according to the value of the P3 operand:
|
||||||
|
**
|
||||||
|
** P3 modification
|
||||||
|
** --------------------------
|
||||||
|
** OP_Lt (P1 = (P1 < 0))
|
||||||
|
** OP_Le (P1 = (P1 <= 0))
|
||||||
|
** OP_Gt (P1 = (P1 > 0))
|
||||||
|
** OP_Ge (P1 = (P1 >= 0))
|
||||||
|
*/
|
||||||
|
case OP_CmpTest: { /* in1, jump */
|
||||||
|
int bJump;
|
||||||
|
|
||||||
|
pIn1 = &aMem[pOp->p1];
|
||||||
|
if( (pIn1->flags & MEM_Int) ){
|
||||||
|
bJump = (pIn1->u.i!=0);
|
||||||
|
switch( pOp->p3 ){
|
||||||
|
case OP_Lt: pIn1->u.i = (pIn1->u.i < 0); break;
|
||||||
|
case OP_Le: pIn1->u.i = (pIn1->u.i <= 0); break;
|
||||||
|
case OP_Gt: pIn1->u.i = (pIn1->u.i > 0); break;
|
||||||
|
default: assert( pOp->p3==OP_Ge ); pIn1->u.i = (pIn1->u.i >= 0); break;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
bJump = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( bJump ) goto jump_to_p2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* Opcode: Found P1 P2 P3 P4 *
|
/* Opcode: Found P1 P2 P3 P4 *
|
||||||
** Synopsis: key=r[P3@P4]
|
** Synopsis: key=r[P3@P4]
|
||||||
|
65
src/where.c
65
src/where.c
@ -2187,6 +2187,51 @@ static void whereLoopOutputAdjust(
|
|||||||
if( pLoop->nOut > nRow-iReduce ) pLoop->nOut = nRow - iReduce;
|
if( pLoop->nOut > nRow-iReduce ) pLoop->nOut = nRow - iReduce;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Term pTerm is a vector range comparison operation. The first comparison
|
||||||
|
** in the vector can be optimized using column nEq of the index.
|
||||||
|
*/
|
||||||
|
int whereRangeVectorLen(
|
||||||
|
Parse *pParse, int iCur, Index *pIdx, int nEq, WhereTerm *pTerm
|
||||||
|
){
|
||||||
|
int nCmp = sqlite3ExprVectorSize(pTerm->pExpr->pLeft);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
nCmp = MIN(nCmp, (pIdx->nColumn - nEq));
|
||||||
|
for(i=1; i<nCmp; i++){
|
||||||
|
/* Test if comparison i of pTerm is compatible with column (i+nEq)
|
||||||
|
** of the index. If not, exit the loop. */
|
||||||
|
char aff; /* Comparison affinity */
|
||||||
|
char idxaff = 0; /* Indexed columns affinity */
|
||||||
|
CollSeq *pColl; /* Comparison collation sequence */
|
||||||
|
Expr *pLhs = pTerm->pExpr->pLeft->x.pList->a[i].pExpr;
|
||||||
|
Expr *pRhs = pTerm->pExpr->pRight;
|
||||||
|
if( pRhs->flags & EP_xIsSelect ){
|
||||||
|
pRhs = pRhs->x.pSelect->pEList->a[i].pExpr;
|
||||||
|
}else{
|
||||||
|
pRhs = pRhs->x.pList->a[i].pExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that the LHS of the comparison is a column reference to
|
||||||
|
** the right column of the right source table.
|
||||||
|
*/
|
||||||
|
if( pLhs->op!=TK_COLUMN
|
||||||
|
|| pLhs->iTable!=iCur
|
||||||
|
|| pLhs->iColumn!=pIdx->aiColumn[i+nEq]
|
||||||
|
){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
aff = sqlite3CompareAffinity(pRhs, sqlite3ExprAffinity(pLhs));
|
||||||
|
idxaff = pIdx->pTable->aCol[pLhs->iColumn].affinity;
|
||||||
|
if( aff!=idxaff ) break;
|
||||||
|
|
||||||
|
pColl = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs);
|
||||||
|
if( sqlite3StrICmp(pColl->zName, pIdx->azColl[i+nEq]) ) break;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Adjust the cost C by the costMult facter T. This only occurs if
|
** Adjust the cost C by the costMult facter T. This only occurs if
|
||||||
** compiled with -DSQLITE_ENABLE_COSTMULT
|
** compiled with -DSQLITE_ENABLE_COSTMULT
|
||||||
@ -2225,6 +2270,8 @@ static int whereLoopAddBtreeIndex(
|
|||||||
Bitmask saved_prereq; /* Original value of pNew->prereq */
|
Bitmask saved_prereq; /* Original value of pNew->prereq */
|
||||||
u16 saved_nLTerm; /* Original value of pNew->nLTerm */
|
u16 saved_nLTerm; /* Original value of pNew->nLTerm */
|
||||||
u16 saved_nEq; /* Original value of pNew->u.btree.nEq */
|
u16 saved_nEq; /* Original value of pNew->u.btree.nEq */
|
||||||
|
u16 saved_nBtm; /* Original value of pNew->u.btree.nBtm */
|
||||||
|
u16 saved_nTop; /* Original value of pNew->u.btree.nTop */
|
||||||
u16 saved_nSkip; /* Original value of pNew->nSkip */
|
u16 saved_nSkip; /* Original value of pNew->nSkip */
|
||||||
u32 saved_wsFlags; /* Original value of pNew->wsFlags */
|
u32 saved_wsFlags; /* Original value of pNew->wsFlags */
|
||||||
LogEst saved_nOut; /* Original value of pNew->nOut */
|
LogEst saved_nOut; /* Original value of pNew->nOut */
|
||||||
@ -2241,6 +2288,7 @@ static int whereLoopAddBtreeIndex(
|
|||||||
if( pNew->wsFlags & WHERE_BTM_LIMIT ){
|
if( pNew->wsFlags & WHERE_BTM_LIMIT ){
|
||||||
opMask = WO_LT|WO_LE;
|
opMask = WO_LT|WO_LE;
|
||||||
}else{
|
}else{
|
||||||
|
assert( pNew->u.btree.nBtm==0 );
|
||||||
opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
|
opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
|
||||||
}
|
}
|
||||||
if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
|
if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
|
||||||
@ -2248,6 +2296,8 @@ static int whereLoopAddBtreeIndex(
|
|||||||
assert( pNew->u.btree.nEq<pProbe->nColumn );
|
assert( pNew->u.btree.nEq<pProbe->nColumn );
|
||||||
|
|
||||||
saved_nEq = pNew->u.btree.nEq;
|
saved_nEq = pNew->u.btree.nEq;
|
||||||
|
saved_nBtm = pNew->u.btree.nBtm;
|
||||||
|
saved_nTop = pNew->u.btree.nTop;
|
||||||
saved_nSkip = pNew->nSkip;
|
saved_nSkip = pNew->nSkip;
|
||||||
saved_nLTerm = pNew->nLTerm;
|
saved_nLTerm = pNew->nLTerm;
|
||||||
saved_wsFlags = pNew->wsFlags;
|
saved_wsFlags = pNew->wsFlags;
|
||||||
@ -2291,6 +2341,8 @@ static int whereLoopAddBtreeIndex(
|
|||||||
|
|
||||||
pNew->wsFlags = saved_wsFlags;
|
pNew->wsFlags = saved_wsFlags;
|
||||||
pNew->u.btree.nEq = saved_nEq;
|
pNew->u.btree.nEq = saved_nEq;
|
||||||
|
pNew->u.btree.nBtm = saved_nBtm;
|
||||||
|
pNew->u.btree.nTop = saved_nTop;
|
||||||
pNew->nLTerm = saved_nLTerm;
|
pNew->nLTerm = saved_nLTerm;
|
||||||
if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
|
if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
|
||||||
pNew->aLTerm[pNew->nLTerm++] = pTerm;
|
pNew->aLTerm[pNew->nLTerm++] = pTerm;
|
||||||
@ -2334,6 +2386,9 @@ static int whereLoopAddBtreeIndex(
|
|||||||
testcase( eOp & WO_GT );
|
testcase( eOp & WO_GT );
|
||||||
testcase( eOp & WO_GE );
|
testcase( eOp & WO_GE );
|
||||||
pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT;
|
pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT;
|
||||||
|
pNew->u.btree.nBtm = whereRangeVectorLen(
|
||||||
|
pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm
|
||||||
|
);
|
||||||
pBtm = pTerm;
|
pBtm = pTerm;
|
||||||
pTop = 0;
|
pTop = 0;
|
||||||
if( pTerm->wtFlags & TERM_LIKEOPT ){
|
if( pTerm->wtFlags & TERM_LIKEOPT ){
|
||||||
@ -2346,12 +2401,16 @@ static int whereLoopAddBtreeIndex(
|
|||||||
if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
|
if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
|
||||||
pNew->aLTerm[pNew->nLTerm++] = pTop;
|
pNew->aLTerm[pNew->nLTerm++] = pTop;
|
||||||
pNew->wsFlags |= WHERE_TOP_LIMIT;
|
pNew->wsFlags |= WHERE_TOP_LIMIT;
|
||||||
|
pNew->u.btree.nTop = 1;
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
assert( eOp & (WO_LT|WO_LE) );
|
assert( eOp & (WO_LT|WO_LE) );
|
||||||
testcase( eOp & WO_LT );
|
testcase( eOp & WO_LT );
|
||||||
testcase( eOp & WO_LE );
|
testcase( eOp & WO_LE );
|
||||||
pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT;
|
pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT;
|
||||||
|
pNew->u.btree.nTop = whereRangeVectorLen(
|
||||||
|
pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm
|
||||||
|
);
|
||||||
pTop = pTerm;
|
pTop = pTerm;
|
||||||
pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ?
|
pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ?
|
||||||
pNew->aLTerm[pNew->nLTerm-2] : 0;
|
pNew->aLTerm[pNew->nLTerm-2] : 0;
|
||||||
@ -2451,6 +2510,8 @@ static int whereLoopAddBtreeIndex(
|
|||||||
}
|
}
|
||||||
pNew->prereq = saved_prereq;
|
pNew->prereq = saved_prereq;
|
||||||
pNew->u.btree.nEq = saved_nEq;
|
pNew->u.btree.nEq = saved_nEq;
|
||||||
|
pNew->u.btree.nBtm = saved_nBtm;
|
||||||
|
pNew->u.btree.nTop = saved_nTop;
|
||||||
pNew->nSkip = saved_nSkip;
|
pNew->nSkip = saved_nSkip;
|
||||||
pNew->wsFlags = saved_wsFlags;
|
pNew->wsFlags = saved_wsFlags;
|
||||||
pNew->nOut = saved_nOut;
|
pNew->nOut = saved_nOut;
|
||||||
@ -2572,7 +2633,7 @@ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
** Add all WhereLoop objects for a single table of the join where the table
|
** Add all WhereLoop objects for a single table of the join where the table
|
||||||
** is idenfied by pBuilder->pNew->iTab. That table is guaranteed to be
|
** is identified by pBuilder->pNew->iTab. That table is guaranteed to be
|
||||||
** a b-tree table, not a virtual table.
|
** a b-tree table, not a virtual table.
|
||||||
**
|
**
|
||||||
** The costs (WhereLoop.rRun) of the b-tree loops added by this function
|
** The costs (WhereLoop.rRun) of the b-tree loops added by this function
|
||||||
@ -2726,6 +2787,8 @@ static int whereLoopAddBtree(
|
|||||||
}
|
}
|
||||||
rSize = pProbe->aiRowLogEst[0];
|
rSize = pProbe->aiRowLogEst[0];
|
||||||
pNew->u.btree.nEq = 0;
|
pNew->u.btree.nEq = 0;
|
||||||
|
pNew->u.btree.nBtm = 0;
|
||||||
|
pNew->u.btree.nTop = 0;
|
||||||
pNew->nSkip = 0;
|
pNew->nSkip = 0;
|
||||||
pNew->nLTerm = 0;
|
pNew->nLTerm = 0;
|
||||||
pNew->iSortIdx = 0;
|
pNew->iSortIdx = 0;
|
||||||
|
@ -122,6 +122,8 @@ struct WhereLoop {
|
|||||||
union {
|
union {
|
||||||
struct { /* Information for internal btree tables */
|
struct { /* Information for internal btree tables */
|
||||||
u16 nEq; /* Number of equality constraints */
|
u16 nEq; /* Number of equality constraints */
|
||||||
|
u16 nBtm; /* Size of BTM vector */
|
||||||
|
u16 nTop; /* Size of TOP vector */
|
||||||
Index *pIndex; /* Index used, or NULL */
|
Index *pIndex; /* Index used, or NULL */
|
||||||
} btree;
|
} btree;
|
||||||
struct { /* Information for virtual tables */
|
struct { /* Information for virtual tables */
|
||||||
|
@ -861,6 +861,30 @@ static void codeDeferredSeek(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){
|
||||||
|
assert( nReg>0 );
|
||||||
|
if( p->flags & EP_Vector ){
|
||||||
|
int i;
|
||||||
|
if( (p->flags & EP_xIsSelect)==0 ){
|
||||||
|
ExprList *pList = p->x.pList;
|
||||||
|
assert( nReg<=pList->nExpr );
|
||||||
|
for(i=0; i<nReg; i++){
|
||||||
|
sqlite3ExprCode(pParse, pList->a[i].pExpr, iReg+i);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
Vdbe *v = pParse->pVdbe;
|
||||||
|
int iSelect = sqlite3CodeSubselect(pParse, p, 0, 0);
|
||||||
|
sqlite3VdbeAddOp3(v, OP_Copy, iSelect, iReg, nReg-1);
|
||||||
|
p->op2 = p->op;
|
||||||
|
p->op = TK_REGISTER;
|
||||||
|
p->iTable = iSelect;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
assert( nReg==1 );
|
||||||
|
sqlite3ExprCode(pParse, p, iReg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Generate code for the start of the iLevel-th loop in the WHERE clause
|
** Generate code for the start of the iLevel-th loop in the WHERE clause
|
||||||
** implementation described by pWInfo.
|
** implementation described by pWInfo.
|
||||||
@ -1185,6 +1209,8 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
|||||||
OP_IdxLT, /* 3: (end_constraints && bRev && endEq) */
|
OP_IdxLT, /* 3: (end_constraints && bRev && endEq) */
|
||||||
};
|
};
|
||||||
u16 nEq = pLoop->u.btree.nEq; /* Number of == or IN terms */
|
u16 nEq = pLoop->u.btree.nEq; /* Number of == or IN terms */
|
||||||
|
u16 nBtm = pLoop->u.btree.nBtm; /* Length of BTM vector */
|
||||||
|
u16 nTop = pLoop->u.btree.nTop; /* Length of TOP vector */
|
||||||
int regBase; /* Base register holding constraint values */
|
int regBase; /* Base register holding constraint values */
|
||||||
WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */
|
WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */
|
||||||
WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */
|
WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */
|
||||||
@ -1231,14 +1257,14 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
|||||||
j = nEq;
|
j = nEq;
|
||||||
if( pLoop->wsFlags & WHERE_BTM_LIMIT ){
|
if( pLoop->wsFlags & WHERE_BTM_LIMIT ){
|
||||||
pRangeStart = pLoop->aLTerm[j++];
|
pRangeStart = pLoop->aLTerm[j++];
|
||||||
nExtraReg = 1;
|
nExtraReg = MAX(nExtraReg, pLoop->u.btree.nBtm);
|
||||||
/* Like optimization range constraints always occur in pairs */
|
/* Like optimization range constraints always occur in pairs */
|
||||||
assert( (pRangeStart->wtFlags & TERM_LIKEOPT)==0 ||
|
assert( (pRangeStart->wtFlags & TERM_LIKEOPT)==0 ||
|
||||||
(pLoop->wsFlags & WHERE_TOP_LIMIT)!=0 );
|
(pLoop->wsFlags & WHERE_TOP_LIMIT)!=0 );
|
||||||
}
|
}
|
||||||
if( pLoop->wsFlags & WHERE_TOP_LIMIT ){
|
if( pLoop->wsFlags & WHERE_TOP_LIMIT ){
|
||||||
pRangeEnd = pLoop->aLTerm[j++];
|
pRangeEnd = pLoop->aLTerm[j++];
|
||||||
nExtraReg = 1;
|
nExtraReg = MAX(nExtraReg, pLoop->u.btree.nTop);
|
||||||
#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
|
#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
|
||||||
if( (pRangeEnd->wtFlags & TERM_LIKEOPT)!=0 ){
|
if( (pRangeEnd->wtFlags & TERM_LIKEOPT)!=0 ){
|
||||||
assert( pRangeStart!=0 ); /* LIKE opt constraints */
|
assert( pRangeStart!=0 ); /* LIKE opt constraints */
|
||||||
@ -1274,6 +1300,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
|||||||
){
|
){
|
||||||
SWAP(WhereTerm *, pRangeEnd, pRangeStart);
|
SWAP(WhereTerm *, pRangeEnd, pRangeStart);
|
||||||
SWAP(u8, bSeekPastNull, bStopAtNull);
|
SWAP(u8, bSeekPastNull, bStopAtNull);
|
||||||
|
SWAP(u8, nBtm, nTop);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Generate code to evaluate all constraint terms using == or IN
|
/* Generate code to evaluate all constraint terms using == or IN
|
||||||
@ -1298,7 +1325,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
|||||||
nConstraint = nEq;
|
nConstraint = nEq;
|
||||||
if( pRangeStart ){
|
if( pRangeStart ){
|
||||||
Expr *pRight = pRangeStart->pExpr->pRight;
|
Expr *pRight = pRangeStart->pExpr->pRight;
|
||||||
sqlite3ExprCode(pParse, pRight, regBase+nEq);
|
codeExprOrVector(pParse, pRight, regBase+nEq, nBtm);
|
||||||
whereLikeOptimizationStringFixup(v, pLevel, pRangeStart);
|
whereLikeOptimizationStringFixup(v, pLevel, pRangeStart);
|
||||||
if( (pRangeStart->wtFlags & TERM_VNULL)==0
|
if( (pRangeStart->wtFlags & TERM_VNULL)==0
|
||||||
&& sqlite3ExprCanBeNull(pRight)
|
&& sqlite3ExprCanBeNull(pRight)
|
||||||
@ -1317,8 +1344,13 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
|||||||
zStartAff[nEq] = SQLITE_AFF_BLOB;
|
zStartAff[nEq] = SQLITE_AFF_BLOB;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nConstraint++;
|
nConstraint += nBtm;
|
||||||
testcase( pRangeStart->wtFlags & TERM_VIRTUAL );
|
testcase( pRangeStart->wtFlags & TERM_VIRTUAL );
|
||||||
|
if( (pRight->flags & EP_Vector)==0 ){
|
||||||
|
disableTerm(pLevel, pRangeStart);
|
||||||
|
}else{
|
||||||
|
startEq = 1;
|
||||||
|
}
|
||||||
}else if( bSeekPastNull ){
|
}else if( bSeekPastNull ){
|
||||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
|
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
|
||||||
nConstraint++;
|
nConstraint++;
|
||||||
@ -1350,7 +1382,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
|||||||
if( pRangeEnd ){
|
if( pRangeEnd ){
|
||||||
Expr *pRight = pRangeEnd->pExpr->pRight;
|
Expr *pRight = pRangeEnd->pExpr->pRight;
|
||||||
sqlite3ExprCacheRemove(pParse, regBase+nEq, 1);
|
sqlite3ExprCacheRemove(pParse, regBase+nEq, 1);
|
||||||
sqlite3ExprCode(pParse, pRight, regBase+nEq);
|
codeExprOrVector(pParse, pRight, regBase+nEq, nTop);
|
||||||
whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd);
|
whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd);
|
||||||
if( (pRangeEnd->wtFlags & TERM_VNULL)==0
|
if( (pRangeEnd->wtFlags & TERM_VNULL)==0
|
||||||
&& sqlite3ExprCanBeNull(pRight)
|
&& sqlite3ExprCanBeNull(pRight)
|
||||||
@ -1363,8 +1395,14 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
|||||||
){
|
){
|
||||||
codeApplyAffinity(pParse, regBase+nEq, 1, &cEndAff);
|
codeApplyAffinity(pParse, regBase+nEq, 1, &cEndAff);
|
||||||
}
|
}
|
||||||
nConstraint++;
|
nConstraint += nTop;
|
||||||
testcase( pRangeEnd->wtFlags & TERM_VIRTUAL );
|
testcase( pRangeEnd->wtFlags & TERM_VIRTUAL );
|
||||||
|
|
||||||
|
if( (pRight->flags & EP_Vector)==0 ){
|
||||||
|
disableTerm(pLevel, pRangeEnd);
|
||||||
|
}else{
|
||||||
|
endEq = 1;
|
||||||
|
}
|
||||||
}else if( bStopAtNull ){
|
}else if( bStopAtNull ){
|
||||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
|
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
|
||||||
endEq = 0;
|
endEq = 0;
|
||||||
@ -1385,9 +1423,11 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
|||||||
testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE );
|
testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Disable the start and end range terms if possible */
|
||||||
|
/* disableTerm(pLevel, pRangeStart); */
|
||||||
|
/* disableTerm(pLevel, pRangeEnd); */
|
||||||
|
|
||||||
/* Seek the table cursor, if required */
|
/* Seek the table cursor, if required */
|
||||||
disableTerm(pLevel, pRangeStart);
|
|
||||||
disableTerm(pLevel, pRangeEnd);
|
|
||||||
if( omitTable ){
|
if( omitTable ){
|
||||||
/* pIdx is a covering index. No need to access the main table. */
|
/* pIdx is a covering index. No need to access the main table. */
|
||||||
}else if( HasRowid(pIdx->pTable) ){
|
}else if( HasRowid(pIdx->pTable) ){
|
||||||
@ -1411,9 +1451,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
|||||||
iRowidReg, pPk->nKeyCol); VdbeCoverage(v);
|
iRowidReg, pPk->nKeyCol); VdbeCoverage(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Record the instruction used to terminate the loop. Disable
|
/* Record the instruction used to terminate the loop. */
|
||||||
** WHERE clause terms made redundant by the index range scan.
|
|
||||||
*/
|
|
||||||
if( pLoop->wsFlags & WHERE_ONEROW ){
|
if( pLoop->wsFlags & WHERE_ONEROW ){
|
||||||
pLevel->op = OP_Noop;
|
pLevel->op = OP_Noop;
|
||||||
}else if( bRev ){
|
}else if( bRev ){
|
||||||
|
@ -95,7 +95,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){
|
|||||||
/*
|
/*
|
||||||
** Return TRUE if the given operator is one of the operators that is
|
** Return TRUE if the given operator is one of the operators that is
|
||||||
** allowed for an indexable WHERE clause term. The allowed operators are
|
** allowed for an indexable WHERE clause term. The allowed operators are
|
||||||
** "=", "<", ">", "<=", ">=", "IN", and "IS NULL"
|
** "=", "<", ">", "<=", ">=", "IN", "IS", and "IS NULL"
|
||||||
*/
|
*/
|
||||||
static int allowedOp(int op){
|
static int allowedOp(int op){
|
||||||
assert( TK_GT>TK_EQ && TK_GT<TK_GE );
|
assert( TK_GT>TK_EQ && TK_GT<TK_GE );
|
||||||
@ -831,6 +831,7 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){
|
|||||||
*/
|
*/
|
||||||
static int exprMightBeIndexed(
|
static int exprMightBeIndexed(
|
||||||
SrcList *pFrom, /* The FROM clause */
|
SrcList *pFrom, /* The FROM clause */
|
||||||
|
int op,
|
||||||
Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */
|
Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */
|
||||||
Expr *pExpr, /* An operand of a comparison operator */
|
Expr *pExpr, /* An operand of a comparison operator */
|
||||||
int *piCur, /* Write the referenced table cursor number here */
|
int *piCur, /* Write the referenced table cursor number here */
|
||||||
@ -839,6 +840,17 @@ static int exprMightBeIndexed(
|
|||||||
Index *pIdx;
|
Index *pIdx;
|
||||||
int i;
|
int i;
|
||||||
int iCur;
|
int iCur;
|
||||||
|
|
||||||
|
/* If this expression is a vector to the left or right of a
|
||||||
|
** inequality constraint (>, <, >= or <=), perform the processing
|
||||||
|
** on the first element of the vector. */
|
||||||
|
assert( TK_GT+1==TK_LE && TK_GT+2==TK_LT && TK_GT+3==TK_GE );
|
||||||
|
if( (pExpr->flags & (EP_Vector|EP_xIsSelect))==EP_Vector
|
||||||
|
&& (op>=TK_GT && op<=TK_GE)
|
||||||
|
){
|
||||||
|
pExpr = pExpr->x.pList->a[0].pExpr;
|
||||||
|
}
|
||||||
|
|
||||||
if( pExpr->op==TK_COLUMN ){
|
if( pExpr->op==TK_COLUMN ){
|
||||||
*piCur = pExpr->iTable;
|
*piCur = pExpr->iTable;
|
||||||
*piColumn = pExpr->iColumn;
|
*piColumn = pExpr->iColumn;
|
||||||
@ -862,6 +874,17 @@ static int exprMightBeIndexed(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Expr *exprVectorExpr(Parse *pParse, Expr *p, int iField){
|
||||||
|
Expr *pRet;
|
||||||
|
if( p->flags & EP_xIsSelect ){
|
||||||
|
pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, p, 0, 0);
|
||||||
|
if( pRet ) pRet->iColumn = iField;
|
||||||
|
}else{
|
||||||
|
pRet = sqlite3ExprDup(pParse->db, p->x.pList->a[iField].pExpr, 0);
|
||||||
|
}
|
||||||
|
return pRet;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** The input to this routine is an WhereTerm structure with only the
|
** The input to this routine is an WhereTerm structure with only the
|
||||||
** "pExpr" field filled in. The job of this routine is to analyze the
|
** "pExpr" field filled in. The job of this routine is to analyze the
|
||||||
@ -937,14 +960,14 @@ static void exprAnalyze(
|
|||||||
Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft);
|
Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft);
|
||||||
Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight);
|
Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight);
|
||||||
u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV;
|
u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV;
|
||||||
if( exprMightBeIndexed(pSrc, prereqLeft, pLeft, &iCur, &iColumn) ){
|
if( exprMightBeIndexed(pSrc, op, prereqLeft, pLeft, &iCur, &iColumn) ){
|
||||||
pTerm->leftCursor = iCur;
|
pTerm->leftCursor = iCur;
|
||||||
pTerm->u.leftColumn = iColumn;
|
pTerm->u.leftColumn = iColumn;
|
||||||
pTerm->eOperator = operatorMask(op) & opMask;
|
pTerm->eOperator = operatorMask(op) & opMask;
|
||||||
}
|
}
|
||||||
if( op==TK_IS ) pTerm->wtFlags |= TERM_IS;
|
if( op==TK_IS ) pTerm->wtFlags |= TERM_IS;
|
||||||
if( pRight
|
if( pRight
|
||||||
&& exprMightBeIndexed(pSrc, pTerm->prereqRight, pRight, &iCur, &iColumn)
|
&& exprMightBeIndexed(pSrc, op, pTerm->prereqRight, pRight, &iCur,&iColumn)
|
||||||
){
|
){
|
||||||
WhereTerm *pNew;
|
WhereTerm *pNew;
|
||||||
Expr *pDup;
|
Expr *pDup;
|
||||||
@ -1152,6 +1175,26 @@ static void exprAnalyze(
|
|||||||
}
|
}
|
||||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||||
|
|
||||||
|
if( pWC->op==TK_AND
|
||||||
|
&& (pExpr->op==TK_EQ || pExpr->op==TK_IS)
|
||||||
|
&& (pExpr->pLeft->flags & EP_Vector)
|
||||||
|
&& ( (pExpr->pLeft->flags & EP_xIsSelect)==0
|
||||||
|
|| (pExpr->pRight->flags & EP_xIsSelect)==0
|
||||||
|
)){
|
||||||
|
int i;
|
||||||
|
for(i=0; i<sqlite3ExprVectorSize(pExpr->pLeft); i++){
|
||||||
|
int idxNew;
|
||||||
|
Expr *pNew;
|
||||||
|
Expr *pLeft = exprVectorExpr(pParse, pExpr->pLeft, i);
|
||||||
|
Expr *pRight = exprVectorExpr(pParse, pExpr->pRight, i);
|
||||||
|
|
||||||
|
pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0);
|
||||||
|
idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||||
|
exprAnalyze(pSrc, pWC, idxNew);
|
||||||
|
markTermAsChild(pWC, idxNew, idxTerm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||||
/* When sqlite_stat3 histogram data is available an operator of the
|
/* When sqlite_stat3 histogram data is available an operator of the
|
||||||
** form "x IS NOT NULL" can sometimes be evaluated more efficiently
|
** form "x IS NOT NULL" can sometimes be evaluated more efficiently
|
||||||
|
123
test/rowvalue.test
Normal file
123
test/rowvalue.test
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
# 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 of this file is testing the SELECT statement.
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
set testdir [file dirname $argv0]
|
||||||
|
source $testdir/tester.tcl
|
||||||
|
set ::testprefix rowvalue
|
||||||
|
|
||||||
|
do_execsql_test 0.0 {
|
||||||
|
CREATE TABLE one(o);
|
||||||
|
INSERT INTO one VALUES(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach {tn v1 v2 eq ne is isnot} {
|
||||||
|
1 "1, 2, 3" "1, 2, 3" 1 0 1 0
|
||||||
|
2 "1, 0, 3" "1, 2, 3" 0 1 0 1
|
||||||
|
3 "1, 2, NULL" "1, 2, 3" {} {} 0 1
|
||||||
|
4 "1, 2, NULL" "1, 2, NULL" {} {} 1 0
|
||||||
|
5 "NULL, NULL, NULL" "NULL, NULL, NULL" {} {} 1 0
|
||||||
|
} {
|
||||||
|
do_execsql_test 1.$tn.eq "SELECT ($v1) == ($v2)" [list $eq]
|
||||||
|
do_execsql_test 1.$tn.ne "SELECT ($v1) != ($v2)" [list $ne]
|
||||||
|
|
||||||
|
do_execsql_test 1.$tn.is "SELECT ($v1) IS ($v2)" [list $is]
|
||||||
|
do_execsql_test 1.$tn.isnot "SELECT ($v1) IS NOT ($v2)" [list $isnot]
|
||||||
|
|
||||||
|
do_execsql_test 1.$tn.2.eq "SELECT (SELECT $v1) == (SELECT $v2)" [list $eq]
|
||||||
|
do_execsql_test 1.$tn.2.ne "SELECT (SELECT $v1) != (SELECT $v2)" [list $ne]
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach {tn v1 v2 lt gt le ge} {
|
||||||
|
1 "(1, 1, 3)" "(1, 2, 3)" 1 0 1 0
|
||||||
|
2 "(1, 2, 3)" "(1, 2, 3)" 0 0 1 1
|
||||||
|
3 "(1, 3, 3)" "(1, 2, 3)" 0 1 0 1
|
||||||
|
|
||||||
|
4 "(1, NULL, 3)" "(1, 2, 3)" {} {} {} {}
|
||||||
|
5 "(1, 3, 3)" "(1, NULL, 3)" {} {} {} {}
|
||||||
|
6 "(1, NULL, 3)" "(1, NULL, 3)" {} {} {} {}
|
||||||
|
} {
|
||||||
|
foreach {tn2 expr res} [list \
|
||||||
|
2.$tn.lt "$v1 < $v2" $lt \
|
||||||
|
2.$tn.gt "$v1 > $v2" $gt \
|
||||||
|
2.$tn.le "$v1 <= $v2" $le \
|
||||||
|
2.$tn.ge "$v1 >= $v2" $ge \
|
||||||
|
] {
|
||||||
|
do_execsql_test $tn2 "SELECT $expr" [list $res]
|
||||||
|
|
||||||
|
set map(0) [list]
|
||||||
|
set map() [list]
|
||||||
|
set map(1) [list 1]
|
||||||
|
do_execsql_test $tn2.where1 "SELECT * FROM one WHERE $expr" $map($res)
|
||||||
|
|
||||||
|
set map(0) [list 1]
|
||||||
|
set map() [list]
|
||||||
|
set map(1) [list]
|
||||||
|
do_execsql_test $tn2.where2 "SELECT * FROM one WHERE NOT $expr" $map($res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 3.0 {
|
||||||
|
CREATE TABLE t1(x, y);
|
||||||
|
INSERT INTO t1 VALUES(1, 1);
|
||||||
|
INSERT INTO t1 VALUES(1, 2);
|
||||||
|
INSERT INTO t1 VALUES(2, 3);
|
||||||
|
INSERT INTO t1 VALUES(2, 4);
|
||||||
|
INSERT INTO t1 VALUES(3, 5);
|
||||||
|
INSERT INTO t1 VALUES(3, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach {tn r order} {
|
||||||
|
1 "(1, 1)" "ORDER BY y"
|
||||||
|
2 "(1, 1)" "ORDER BY x, y"
|
||||||
|
3 "(1, 2)" "ORDER BY x, y DESC"
|
||||||
|
4 "(3, 6)" "ORDER BY x DESC, y DESC"
|
||||||
|
5 "((3, 5))" "ORDER BY x DESC, y"
|
||||||
|
6 "(SELECT 3, 5)" "ORDER BY x DESC, y"
|
||||||
|
} {
|
||||||
|
do_execsql_test 3.$tn.1 "SELECT $r == (SELECT x,y FROM t1 $order)" 1
|
||||||
|
do_execsql_test 3.$tn.2 "SELECT $r == (SELECT * FROM t1 $order)" 1
|
||||||
|
|
||||||
|
do_execsql_test 3.$tn.3 "
|
||||||
|
SELECT (SELECT * FROM t1 $order) == (SELECT * FROM t1 $order)
|
||||||
|
" 1
|
||||||
|
do_execsql_test 3.$tn.4 "
|
||||||
|
SELECT (SELECT 0, 0) == (SELECT * FROM t1 $order)
|
||||||
|
" 0
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach {tn expr res} {
|
||||||
|
1 {(2, 2) BETWEEN (2, 2) AND (3, 3)} 1
|
||||||
|
2 {(2, 2) BETWEEN (2, NULL) AND (3, 3)} {}
|
||||||
|
3 {(2, 2) BETWEEN (3, NULL) AND (3, 3)} 0
|
||||||
|
} {
|
||||||
|
do_execsql_test 4.$tn "SELECT $expr" [list $res]
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach {tn expr res} {
|
||||||
|
1 {(2, 4) IN (SELECT * FROM t1)} 1
|
||||||
|
2 {(3, 4) IN (SELECT * FROM t1)} 0
|
||||||
|
|
||||||
|
3 {(NULL, 4) IN (SELECT * FROM t1)} {}
|
||||||
|
4 {(NULL, 0) IN (SELECT * FROM t1)} 0
|
||||||
|
|
||||||
|
5 {(NULL, 4) NOT IN (SELECT * FROM t1)} {}
|
||||||
|
6 {(NULL, 0) NOT IN (SELECT * FROM t1)} 1
|
||||||
|
} {
|
||||||
|
do_execsql_test 5.$tn "SELECT $expr" [list $res]
|
||||||
|
}
|
||||||
|
|
||||||
|
finish_test
|
||||||
|
|
||||||
|
|
252
test/rowvalue2.test
Normal file
252
test/rowvalue2.test
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
# 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 of this file is testing the SELECT statement.
|
||||||
|
#
|
||||||
|
|
||||||
|
set testdir [file dirname $argv0]
|
||||||
|
source $testdir/tester.tcl
|
||||||
|
set ::testprefix rowvalue2
|
||||||
|
|
||||||
|
do_execsql_test 1.0 {
|
||||||
|
CREATE TABLE t1(a, b, c);
|
||||||
|
INSERT INTO t1 VALUES(0, 0, 0);
|
||||||
|
INSERT INTO t1 VALUES(0, 1, 1);
|
||||||
|
INSERT INTO t1 VALUES(1, 0, 2);
|
||||||
|
INSERT INTO t1 VALUES(1, 1, 3);
|
||||||
|
|
||||||
|
CREATE INDEX i1 ON t1(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 1.1.1 { SELECT c FROM t1 WHERE (a, b) >= (1, 0) } {2 3}
|
||||||
|
do_execsql_test 1.1.2 { SELECT c FROM t1 WHERE (a, b) > (1, 0) } {3}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
do_execsql_test 2.0.1 {
|
||||||
|
CREATE TABLE t2(a INTEGER, b INTEGER, c INTEGER, d INTEGER);
|
||||||
|
CREATE INDEX i2 ON t2(a, b, c);
|
||||||
|
}
|
||||||
|
do_test 2.0.2 {
|
||||||
|
foreach a {0 1 2 3} {
|
||||||
|
foreach b {0 1 2 3} {
|
||||||
|
foreach c {0 1 2 3} {
|
||||||
|
execsql { INSERT INTO t2 VALUES($a, $b, $c, $c + $b*4 + $a*16); }
|
||||||
|
}}}
|
||||||
|
} {}
|
||||||
|
|
||||||
|
do_execsql_test 2.1 {
|
||||||
|
SELECT d FROM t2 WHERE (a, b) > (2, 2);
|
||||||
|
} [db eval { SELECT d FROM t2 WHERE a>2 OR (a=2 AND b>2) }]
|
||||||
|
|
||||||
|
do_execsql_test 2.2 {
|
||||||
|
SELECT d FROM t2 WHERE (a, b) >= (2, 2);
|
||||||
|
} [db eval { SELECT d FROM t2 WHERE a>2 OR (a=2 AND b>=2) }]
|
||||||
|
|
||||||
|
do_execsql_test 2.3 {
|
||||||
|
SELECT d FROM t2 WHERE a=1 AND (b, c) >= (1, 2);
|
||||||
|
} [db eval { SELECT d FROM t2 WHERE +a=1 AND (b>1 OR (b==1 AND c>=2)) }]
|
||||||
|
|
||||||
|
do_execsql_test 2.4 {
|
||||||
|
SELECT d FROM t2 WHERE a=1 AND (b, c) > (1, 2);
|
||||||
|
} [db eval { SELECT d FROM t2 WHERE +a=1 AND (b>1 OR (b==1 AND c>2)) }]
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
set words {
|
||||||
|
airfare airfield airfields airflow airfoil
|
||||||
|
airfoils airframe airframes airily airing
|
||||||
|
airings airless airlift airlifts airline
|
||||||
|
airliner airlines airlock airlocks airmail
|
||||||
|
airmails airman airmen airplane airplanes
|
||||||
|
|
||||||
|
arraignment arraignments arraigns arrange arranged
|
||||||
|
arrangement arrangements arranger arrangers arranges
|
||||||
|
arranging arrant array arrayed arrays
|
||||||
|
arrears arrest arrested arrester arresters
|
||||||
|
arresting arrestingly arrestor arrestors arrests
|
||||||
|
|
||||||
|
edifices edit edited editing edition
|
||||||
|
editions editor editorial editorially editorials
|
||||||
|
editors edits educable educate educated
|
||||||
|
educates educating education educational educationally
|
||||||
|
educations educator educators eel eelgrass
|
||||||
|
}
|
||||||
|
|
||||||
|
do_test 3.0 {
|
||||||
|
execsql { CREATE TABLE t3(a, b, c, w); }
|
||||||
|
foreach w $words {
|
||||||
|
set a [string range $w 0 2]
|
||||||
|
set b [string range $w 3 5]
|
||||||
|
set c [string range $w 6 end]
|
||||||
|
execsql { INSERT INTO t3 VALUES($a, $b, $c, $w) }
|
||||||
|
}
|
||||||
|
} {}
|
||||||
|
|
||||||
|
|
||||||
|
foreach {tn idx} {
|
||||||
|
IDX1 {}
|
||||||
|
IDX2 { CREATE INDEX i3 ON t3(a, b, c); }
|
||||||
|
IDX3 { CREATE INDEX i3 ON t3(a, b); }
|
||||||
|
IDX4 { CREATE INDEX i3 ON t3(a); }
|
||||||
|
} {
|
||||||
|
execsql { DROP INDEX IF EXISTS i3 }
|
||||||
|
execsql $idx
|
||||||
|
|
||||||
|
foreach w $words {
|
||||||
|
set a [string range $w 0 2]
|
||||||
|
set b [string range $w 3 5]
|
||||||
|
set c [string range $w 6 end]
|
||||||
|
|
||||||
|
foreach op [list > >= < <= == IS] {
|
||||||
|
do_execsql_test 3.1.$tn.$w.$op [subst -novar {
|
||||||
|
SELECT rowid FROM t3 WHERE (a, b, c) [set op] ($a, $b, $c)
|
||||||
|
ORDER BY +rowid
|
||||||
|
}] [db eval [subst -novar {
|
||||||
|
SELECT rowid FROM t3 WHERE w [set op] $w ORDER BY +rowid
|
||||||
|
}]]
|
||||||
|
|
||||||
|
do_execsql_test 3.1.$tn.$w.$op.subselect [subst -novar {
|
||||||
|
SELECT rowid FROM t3 WHERE (a, b, c) [set op] (
|
||||||
|
SELECT a, b, c FROM t3 WHERE w = $w
|
||||||
|
)
|
||||||
|
ORDER BY +rowid
|
||||||
|
}] [db eval [subst -novar {
|
||||||
|
SELECT rowid FROM t3 WHERE w [set op] $w ORDER BY +rowid
|
||||||
|
}]]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
do_execsql_test 4.0 {
|
||||||
|
CREATE TABLE t4(a, b, c);
|
||||||
|
INSERT INTO t4 VALUES(NULL, NULL, NULL);
|
||||||
|
INSERT INTO t4 VALUES(NULL, NULL, 0);
|
||||||
|
INSERT INTO t4 VALUES(NULL, NULL, 1);
|
||||||
|
INSERT INTO t4 VALUES(NULL, 0, NULL);
|
||||||
|
INSERT INTO t4 VALUES(NULL, 0, 0);
|
||||||
|
INSERT INTO t4 VALUES(NULL, 0, 1);
|
||||||
|
INSERT INTO t4 VALUES(NULL, 1, NULL);
|
||||||
|
INSERT INTO t4 VALUES(NULL, 1, 0);
|
||||||
|
INSERT INTO t4 VALUES(NULL, 1, 1);
|
||||||
|
|
||||||
|
INSERT INTO t4 VALUES( 0, NULL, NULL);
|
||||||
|
INSERT INTO t4 VALUES( 0, NULL, 0);
|
||||||
|
INSERT INTO t4 VALUES( 0, NULL, 1);
|
||||||
|
INSERT INTO t4 VALUES( 0, 0, NULL);
|
||||||
|
INSERT INTO t4 VALUES( 0, 0, 0);
|
||||||
|
INSERT INTO t4 VALUES( 0, 0, 1);
|
||||||
|
INSERT INTO t4 VALUES( 0, 1, NULL);
|
||||||
|
INSERT INTO t4 VALUES( 0, 1, 0);
|
||||||
|
INSERT INTO t4 VALUES( 0, 1, 1);
|
||||||
|
|
||||||
|
INSERT INTO t4 VALUES( 1, NULL, NULL);
|
||||||
|
INSERT INTO t4 VALUES( 1, NULL, 0);
|
||||||
|
INSERT INTO t4 VALUES( 1, NULL, 1);
|
||||||
|
INSERT INTO t4 VALUES( 1, 0, NULL);
|
||||||
|
INSERT INTO t4 VALUES( 1, 0, 0);
|
||||||
|
INSERT INTO t4 VALUES( 1, 0, 1);
|
||||||
|
INSERT INTO t4 VALUES( 1, 1, NULL);
|
||||||
|
INSERT INTO t4 VALUES( 1, 1, 0);
|
||||||
|
INSERT INTO t4 VALUES( 1, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
proc make_expr1 {cList vList op} {
|
||||||
|
return "([join $cList ,]) $op ([join $vList ,])"
|
||||||
|
}
|
||||||
|
|
||||||
|
proc make_expr3 {cList vList op} {
|
||||||
|
set n [llength $cList]
|
||||||
|
|
||||||
|
set aList [list]
|
||||||
|
foreach c [lrange $cList 0 end-1] v [lrange $vList 0 end-1] {
|
||||||
|
lappend aList "$c == $v"
|
||||||
|
}
|
||||||
|
lappend aList "[lindex $cList end] $op [lindex $vList end]"
|
||||||
|
|
||||||
|
return "([join $aList { AND }])"
|
||||||
|
}
|
||||||
|
|
||||||
|
proc make_expr2 {cList vList op} {
|
||||||
|
set ret ""
|
||||||
|
|
||||||
|
switch -- $op {
|
||||||
|
== - IS {
|
||||||
|
set aList [list]
|
||||||
|
foreach c $cList v $vList { lappend aList "($c $op $v)" }
|
||||||
|
set ret [join $aList " AND "]
|
||||||
|
}
|
||||||
|
|
||||||
|
< - > {
|
||||||
|
set oList [list]
|
||||||
|
for {set i 0} {$i < [llength $cList]} {incr i} {
|
||||||
|
lappend oList [make_expr3 [lrange $cList 0 $i] [lrange $vList 0 $i] $op]
|
||||||
|
}
|
||||||
|
set ret [join $oList " OR "]
|
||||||
|
}
|
||||||
|
|
||||||
|
<= - >= {
|
||||||
|
set o2 [string range $op 0 0]
|
||||||
|
set oList [list]
|
||||||
|
for {set i 0} {$i < [llength $cList]-1} {incr i} {
|
||||||
|
lappend oList [make_expr3 [lrange $cList 0 $i] [lrange $vList 0 $i] $o2]
|
||||||
|
}
|
||||||
|
lappend oList [make_expr3 $cList $vList $op]
|
||||||
|
set ret [join $oList " OR "]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
default {
|
||||||
|
error "Unknown op: $op"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set ret
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach {tn idx} {
|
||||||
|
IDX1 {}
|
||||||
|
IDX2 { CREATE INDEX i4 ON t4(a, b, c); }
|
||||||
|
IDX3 { CREATE INDEX i4 ON t4(a, b); }
|
||||||
|
IDX4 { CREATE INDEX i4 ON t4(a); }
|
||||||
|
} {
|
||||||
|
execsql { DROP INDEX IF EXISTS i4 }
|
||||||
|
execsql $idx
|
||||||
|
|
||||||
|
foreach {tn2 vector} {
|
||||||
|
1 {0 0 0}
|
||||||
|
2 {1 1 1}
|
||||||
|
3 {0 0 NULL}
|
||||||
|
4 {0 NULL 0}
|
||||||
|
5 {NULL 0 0}
|
||||||
|
6 {1 1 NULL}
|
||||||
|
7 {1 NULL 1}
|
||||||
|
8 {NULL 1 1}
|
||||||
|
} {
|
||||||
|
foreach op { IS == < <= > >= } {
|
||||||
|
set e1 [make_expr1 {a b c} $vector $op]
|
||||||
|
set e2 [make_expr2 {a b c} $vector $op]
|
||||||
|
|
||||||
|
do_execsql_test 4.$tn.$tn2.$op \
|
||||||
|
"SELECT rowid FROM t4 WHERE $e2 ORDER BY +rowid" [
|
||||||
|
db eval "SELECT rowid FROM t4 WHERE $e1 ORDER BY +rowid"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
finish_test
|
||||||
|
|
@ -37,6 +37,7 @@ set extras {
|
|||||||
UMINUS
|
UMINUS
|
||||||
UPLUS
|
UPLUS
|
||||||
REGISTER
|
REGISTER
|
||||||
|
SELECT_COLUMN
|
||||||
ASTERISK
|
ASTERISK
|
||||||
SPAN
|
SPAN
|
||||||
SPACE
|
SPACE
|
||||||
|
Reference in New Issue
Block a user