From 9289f5103df03e794808e2224657ac7688106936 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 6 Jul 2021 20:44:32 +0000 Subject: [PATCH] Handle " IN (, ...)" in the same way as " IN (VALUES(, , ...)". FossilOrigin-Name: 981d230ece98ce89502dab02aa44f73699a9d0e4fce3e9e9dfd47444a5a9990f --- manifest | 19 +++++----- manifest.uuid | 2 +- src/expr.c | 49 ++++++++++++++++++++++++++ src/parse.y | 29 +++++++++------- src/sqliteInt.h | 1 + test/rowvalueA.test | 77 +++++++++++++++++++++++++++++++++++++++++ test/rowvaluefault.test | 18 ++++++++++ 7 files changed, 173 insertions(+), 22 deletions(-) create mode 100644 test/rowvalueA.test diff --git a/manifest b/manifest index 1597e13051..727760136a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\sallow\swrites\sto\san\sfts5\stable\sif\sthere\sare\sany\sopen\sfts5vocab\scursors. -D 2021-07-05T19:01:09.054 +C Handle\s"\sIN\s(,\s\s...)"\sin\sthe\ssame\sway\sas\s"\sIN\s(VALUES(,\s,\s...)". +D 2021-07-06T20:44:32.552 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -496,7 +496,7 @@ F src/date.c e0632f335952b32401482d099321bbf12716b29d6e72836b53ae49683ebae4bf F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a F src/dbstat.c 3aa79fc3aed7ce906e4ea6c10e85d657299e304f6049861fe300053ac57de36c F src/delete.c 62451bba9fe641159e9c0b7d9d2bab1c48d0cff11e16de2d14000603d2af1fcf -F src/expr.c 5c532072d91855d1b91b2581c2d4447e517932713f583ed02020b7129f1cfbd7 +F src/expr.c ab3935a79da7f0aa0f2f26b16a7fdee527cd21e457876f1d53f009c472a43226 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c e9063648396c58778f77583a678342fe4a9bc82436bf23c5f9f444f2df0fdaa4 F src/func.c c96ac6f7c4f2d684217c4673a80446e1b50e25b5ea79366f333f484622d010a0 @@ -533,7 +533,7 @@ F src/os_win.c 77d39873836f1831a9b0b91894fec45ab0e9ca8e067dc8c549e1d1eca1566fe9 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 95c255256b13827caf038c8f963d334784073f38ab6ef9d70371d9d04f3c43e0 F src/pager.h 4bf9b3213a4b2bebbced5eaa8b219cf25d4a82f385d093cd64b7e93e5285f66f -F src/parse.y 8920f4444957d7827ca458029b2e41ffa32dd3b72917be0b52cae0aace3eadb5 +F src/parse.y 50ef70ca8c7797a405139bbf92ee34812eaa4c0aff10822792672940de424e36 F src/pcache.c 385ff064bca69789d199a98e2169445dc16e4291fa807babd61d4890c3b34177 F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586 F src/pcache1.c 388304fd2d91c39591080b5e0f3c62cfba87db20370e7e0554062bfb29740e9f @@ -549,7 +549,7 @@ F src/shell.c.in 699910739eb7296fd47be19db71f6e5d15d0760f4352c62639d4d6cc7bd8d4c F src/sqlite.h.in ecf5aa981da30c33da3e9f353bf3ebf055d3c380c80d6a4f954e58d18ccd6df1 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h e97f4e9b509408fea4c4e9bef5a41608dfac343b4d3c7a990dedde1e19af9510 -F src/sqliteInt.h b5e0732368e4a4f925b7994538cd3fd3b85027a1c4dcadf3fc944b36f35ce8cd +F src/sqliteInt.h c78fbfc341832bad59299bfc1f932a48e3631d652ae7f443fed969d7c2de76ad F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -1317,7 +1317,8 @@ F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087 F test/rowvalue7.test c1cbdbf407029db01f87764097c6ac02a1c5a37efd2776eff32a9cdfdf6f2dba F test/rowvalue8.test 5900eddad9e2c3c2e26f1a95f74aafc1232ee5e0 F test/rowvalue9.test cb5380df82dca9db463081e26952c1e097b34fc2c2ac87453970c048d97df687 -F test/rowvaluefault.test 7cd9ccc6c2fbdd881672984087aad0491bb75504 +F test/rowvalueA.test 1e73cc7d35491bb46352adaade23cecc9add6094434d85445a3251fc20079ebe +F test/rowvaluefault.test 1dbe9e2a57c4b1a2ee4fb78c7a08c13d4c62885125019713190594aa4847f740 F test/rowvaluevtab.test cd9747bb3f308086944c07968f547ad6b05022e698d80b9ffbdfe09ce0b8da6f F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 @@ -1919,7 +1920,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e306952690bfb140e2c404a74b05ff2d070c487f7e52c62d62a004505fba0e15 -R 97f692da529fbbc0053b9965bcfd2834 +P c49a6ed78a917d4972e048e2a9bbe4d400691f97ce7e022f0e4436ceaed7fb73 +R 088831e8438f8281d3a5f02f1fe2374b U dan -Z 35b3ccac90623680edbe1ba22c3e426c +Z e8e70fe2aaf0c6a219c124f4d93d407b diff --git a/manifest.uuid b/manifest.uuid index c8f8f60c70..66bb1c993e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c49a6ed78a917d4972e048e2a9bbe4d400691f97ce7e022f0e4436ceaed7fb73 \ No newline at end of file +981d230ece98ce89502dab02aa44f73699a9d0e4fce3e9e9dfd47444a5a9990f \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 637ac9131a..8351723f0d 100644 --- a/src/expr.c +++ b/src/expr.c @@ -945,6 +945,55 @@ void sqlite3PExprAddSelect(Parse *pParse, Expr *pExpr, Select *pSelect){ } } +/* +** Expression list pEList is a list of vector values. This function +** converts the contents of pEList to a VALUES(...) Select statement +** returning 1 row for each element of the list. If there are any +** non-vector expressions in the list, the corresponding row returned +** by the values statement contains a single column, the value of +** the non-vector expression itself. +** +** For example, the expression list: +** +** ( (1, 2), 3, (3, 4, 5) ) +** +** is translated to: +** +** VALUES(1, 2), (3), (3, 4, 5) +** +** This is used as part of processing IN(...) expressions with a list +** of vectors on the RHS. e.g. "... IN ((1,2), (3,4), (5,6))". +*/ +Select *sqlite3ExprListToValues(Parse *pParse, ExprList *pEList){ + int ii; + Select *pRet = 0; + for(ii=0; iinExpr; ii++){ + Select *pSel; + ExprList *pList; + Expr *pExpr = pEList->a[ii].pExpr; + if( pExpr->op==TK_VECTOR ){ + pList = pExpr->x.pList; + pExpr->x.pList = 0; + }else{ + pList = sqlite3ExprListAppend(pParse, 0, pExpr); + pEList->a[ii].pExpr = 0; + } + pSel = sqlite3SelectNew(pParse, pList, 0, 0, 0, 0, 0, SF_Values, 0); + if( pSel ){ + if( pRet ){ + pSel->op = TK_ALL; + pSel->pPrior = pRet; + } + pRet = pSel; + } + } + + if( pRet && pRet->pPrior ){ + pRet->selFlags |= SF_MultiValue; + } + sqlite3ExprListDelete(pParse->db, pEList); + return pRet; +} /* ** Join two expressions using an AND operator. If either expression is diff --git a/src/parse.y b/src/parse.y index 359b02bc4e..efc390ca03 100644 --- a/src/parse.y +++ b/src/parse.y @@ -1254,20 +1254,25 @@ expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] { */ sqlite3ExprUnmapAndDelete(pParse, A); A = sqlite3Expr(pParse->db, TK_INTEGER, N ? "1" : "0"); - }else if( Y->nExpr==1 && sqlite3ExprIsConstant(Y->a[0].pExpr) ){ - Expr *pRHS = Y->a[0].pExpr; - Y->a[0].pExpr = 0; - sqlite3ExprListDelete(pParse->db, Y); - pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); - A = sqlite3PExpr(pParse, TK_EQ, A, pRHS); - if( N ) A = sqlite3PExpr(pParse, TK_NOT, A, 0); }else{ - A = sqlite3PExpr(pParse, TK_IN, A, 0); - if( A ){ - A->x.pList = Y; - sqlite3ExprSetHeightAndFlags(pParse, A); - }else{ + Expr *pRHS = Y->a[0].pExpr; + if( Y->nExpr==1 && sqlite3ExprIsConstant(pRHS) && pRHS->op!=TK_VECTOR ){ + Y->a[0].pExpr = 0; sqlite3ExprListDelete(pParse->db, Y); + pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); + A = sqlite3PExpr(pParse, TK_EQ, A, pRHS); + }else{ + A = sqlite3PExpr(pParse, TK_IN, A, 0); + if( pRHS->op==TK_VECTOR || A==0 ){ + Select *pRHS = sqlite3ExprListToValues(pParse, Y); + if( pRHS ){ + parserDoubleLinkSelect(pParse, pRHS); + sqlite3PExprAddSelect(pParse, A, pRHS); + } + }else{ + A->x.pList = Y; + sqlite3ExprSetHeightAndFlags(pParse, A); + } } if( N ) A = sqlite3PExpr(pParse, TK_NOT, A, 0); } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 23eccd8fcb..935ee09535 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4324,6 +4324,7 @@ void sqlite3ExprDeferredDelete(Parse*, Expr*); void sqlite3ExprUnmapAndDelete(Parse*, Expr*); ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*); +Select *sqlite3ExprListToValues(Parse*, ExprList*); void sqlite3ExprListSetSortOrder(ExprList*,int,int); void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int); void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*); diff --git a/test/rowvalueA.test b/test/rowvalueA.test new file mode 100644 index 0000000000..5cd33d4bda --- /dev/null +++ b/test/rowvalueA.test @@ -0,0 +1,77 @@ +# 2021 July 6 +# +# 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. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix rowvalueA + +do_execsql_test 1.0 { + SELECT (1, 2) IN ( (3, 4), (5, 6), (1, 3) ); +} {0} + +do_execsql_test 1.1 { + SELECT (1, 2) IN ( (3, 4), (5, 6), (1, 2) ); +} {1} + +do_execsql_test 1.2 { + SELECT (1, 2) IN ( (3, 2) ); +} {0} + +do_execsql_test 1.3 { + SELECT (1, 2) IN ( (1, 2) ); +} {1} + +do_execsql_test 1.4 { + SELECT (1, 2) IN ( ); +} {0} + +do_execsql_test 1.5 { + SELECT (1, 2) NOT IN ( ); +} {1} + +for {set ii 0} {$ii < 2000} {incr ii} { + lappend L "($ii, $ii)" +} + +do_execsql_test 1.6.1 " + SELECT (400,400) IN ( [join $L ,] ) +" 1 + +do_execsql_test 1.6.2 " + SELECT (1500,1500) IN ( [join $L ,] ) +" 1 + +do_execsql_test 1.6.2 " + SELECT (1500,1499) IN ( [join $L ,] ) +" 0 + +#------------------------------------------------------------------------- + +do_catchsql_test 2.0 { + SELECT (1, 2) IN ( (1, 2), (3, 4, 5), (5, 6) ) +} {1 {all VALUES must have the same number of terms}} + +do_catchsql_test 2.1 { + SELECT (1, 2) IN ( (1, 2), 4, (5, 6) ) +} {1 {all VALUES must have the same number of terms}} + +do_catchsql_test 2.2 { + SELECT (1, 2, 3) IN ( (1, 2), (3, 4), (5, 6) ) +} {1 {sub-select returns 2 columns - expected 3}} + +do_catchsql_test 2.3 { + SELECT 2 IN ( (1, 2), (3, 4), (5, 6) ) +} {1 {sub-select returns 2 columns - expected 1}} + +finish_test + diff --git a/test/rowvaluefault.test b/test/rowvaluefault.test index ac1b236ba4..3ca5b34183 100644 --- a/test/rowvaluefault.test +++ b/test/rowvaluefault.test @@ -68,4 +68,22 @@ do_faultsim_test 6 -faults oom* -body { faultsim_test_result {0 {2 3}} } +do_faultsim_test 7 -faults oom* -body { + execsql { + SELECT fou FROM xyz + WHERE (one, two, thr) IN ( ('a','b','c'), ('A','A','A'), (1,2,3) ); + } +} -test { + faultsim_test_result {0 1} +} + +do_faultsim_test 8 -faults oom* -body { + execsql { + SELECT fou FROM xyz + WHERE two IN ( ('a','b','c'), ('A','A','A'), (1,2,3) ); + } +} -test { + faultsim_test_result {1 {sub-select returns 3 columns - expected 1}} +} + finish_test