From 4b51e8bd68ed3422412e0d0611855e190b3b6ca9 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 27 Jan 2022 13:52:01 +0000 Subject: [PATCH 01/40] Enforce the restriction that 'unixepoch' only works as the first modifier after the time-value. This has been documented since 2004, but has never actually been enforced before. Also add new test cases for date/time functions with evidence marks. FossilOrigin-Name: 64fa9e8c87179211cec248e6dfd7578502e6e969a19e91a4f0e21ed9b972a6bc --- manifest | 13 +++-- manifest.uuid | 2 +- src/date.c | 3 +- test/date3.test | 149 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 159 insertions(+), 8 deletions(-) create mode 100644 test/date3.test diff --git a/manifest b/manifest index f8d24f4d54..616d06e0b5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Minor\sadjustment\sto\serror\shandling\sin\ssqlite3FinishCoding(). -D 2022-01-25T00:03:25.602 +C Enforce\sthe\srestriction\sthat\s'unixepoch'\sonly\sworks\sas\sthe\sfirst\smodifier\nafter\sthe\stime-value.\s\sThis\shas\sbeen\sdocumented\ssince\s2004,\sbut\shas\snever\nactually\sbeen\senforced\sbefore.\s\sAlso\sadd\snew\stest\scases\sfor\sdate/time\nfunctions\swith\sevidence\smarks. +D 2022-01-27T13:52:01.555 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -499,7 +499,7 @@ F src/build.c 9329120c4522d1ad881b9e62108870c8a5e994e31f4d813d0eb3de323d25e362 F src/callback.c 4c19af69835787bfe790ac560f3071a824eb629f34e41f97b52ce5235c77de1c F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c 2cce39df1a13e05b7633e6d21b651f21492471f991dd7b323a4ee4e7b7f0b7f1 -F src/date.c e25773f06a8f9043bfa1e5fa0bee93483c41933adfff0891752f00eadd12ab1c +F src/date.c 41627dec396f3d33e2c317a065f9d59bb535982b2ea3a561c96e4d4cf1137b65 F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d F src/delete.c 52897a8516dc40753503c25eed0e305f09cc50ae474f22b0b4fd31d3b879cc08 @@ -837,6 +837,7 @@ F test/cursorhint2.test 6f3aa9cb19e7418967a10ec6905209bcbb5968054da855fc36c8beee F test/dataversion1.test 6e5e86ac681f0782e766ebcb56c019ae001522d114e0e111e5ebf68ccf2a7bb8 F test/date.test 9b73bbeb1b82d9c1f44dec5cf563bf7da58d2373 F test/date2.test 7e12ec14aaf4d5e6294b4ba140445b0eca06ea50062a9c3a69c4ee13d0b6f8b1 +F test/date3.test a1b77abf05c6772fe5ca2337cac1398892f2a41e62bce7e6be0f4a08a0e64ae5 F test/dbdata.test 042f49acff3438f940eeba5868d3af080ae64ddf26ae78f80c92bec3ca7d8603 F test/dbfuzz.c 73047c920d6210e5912c87cdffd9a1c281d4252e F test/dbfuzz001.test 55e1a3504f8dea84155e09912fe3b1c3ad77e0b1a938ec42ca03b8e51b321e30 @@ -1941,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e9361d72f362b390a31f667363b01cf9d4b78aa19ed5c97f21afe9da764b89c0 -R 2f204671367b290a50dcca5128a83ae4 +P a8db69411b0d1275909adeb21027784ada17af24efe3a59ae0ae2a897659ff17 +R a92b0ae1c265a80dc4f7e5c12c916bf4 U drh -Z 303c6274f1d08ea5df8933a6d290bc89 +Z 47b9e1bb1e6a61a53486ee72f7ca6190 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b1380388fa..40297e7019 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a8db69411b0d1275909adeb21027784ada17af24efe3a59ae0ae2a897659ff17 \ No newline at end of file +64fa9e8c87179211cec248e6dfd7578502e6e969a19e91a4f0e21ed9b972a6bc \ No newline at end of file diff --git a/src/date.c b/src/date.c index 2117b1b853..ef4442aad5 100644 --- a/src/date.c +++ b/src/date.c @@ -697,7 +697,7 @@ static int parseModifier( ** SQLite (0..5373484.5) then the result will be NULL. */ if( sqlite3_stricmp(z, "julianday")==0 ){ - if( idx>1 ) return 1; + if( idx>1 ) return 1; /* IMP: R-31176-64601 */ if( p->validJD && p->rawS ){ rc = 0; p->rawS = 0; @@ -728,6 +728,7 @@ static int parseModifier( ** seconds since 1970. Convert to a real julian day number. */ if( sqlite3_stricmp(z, "unixepoch")==0 && p->rawS ){ + if( idx>1 ) return 1; /* IMP: R-49255-55373 */ r = p->s*1000.0 + 210866760000000.0; if( r>=0.0 && r<464269060800000.0 ){ clearYMD_HMS_TZ(p); diff --git a/test/date3.test b/test/date3.test new file mode 100644 index 0000000000..1798a3478a --- /dev/null +++ b/test/date3.test @@ -0,0 +1,149 @@ +# 2022-01-27 +# +# 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 date and time functions. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Skip this whole file if date and time functions are omitted +# at compile-time +# +ifcapable {!datetime} { + finish_test + return +} + +proc datetest {tnum expr result} { + do_test date3-$tnum [subst { + execsql "SELECT coalesce($expr,'NULL')" + }] [list $result] +} +set tcl_precision 15 + +# EVIDENCE-OF: R-45708-63005 unixepoch(time-value, modifier, modifier, +# ...) +# +datetest 1.1 {unixepoch('1970-01-01')} {0} +datetest 1.2 {unixepoch('1969-12-31 23:59:59')} {-1} +datetest 1.3 {unixepoch('2106-02-07 06:28:15')} {4294967295} +datetest 1.4 {unixepoch('2106-02-07 06:28:16')} {4294967296} +datetest 1.5 {unixepoch('9999-12-31 23:59:59')} {253402300799} +datetest 1.6 {unixepoch('0000-01-01 00:00:00')} {-62167219200} + +# EVIDENCE-OF: R-30877-63179 The unixepoch() function returns a unix +# timestamp - the number of seconds since 1970-01-01 00:00:00 UTC. +# +for {set i 1} {$i<=100} {incr i} { + set x [expr {int(rand()*0xfffffffff)-0xffffffff}] + datetest 1.7.$i "unixepoch($x,'unixepoch')==$x" {1} +} + +# EVIDENCE-OF: R-62992-54137 The unixepoch() always returns an integer, +# even if the input time-value has millisecond precision. +# +datetest 1.8 {unixepoch('2022-01-27 12:59:28.052')} {1643288368} + +# EVIDENCE-OF: R-05412-24332 If the time-value is numeric (the +# DDDDDDDDDD format) then the 'auto' modifier causes the time-value to +# interpreted as either a julian day number or a unix timestamp, +# depending on its magnitude. +# +# EVIDENCE-OF: R-56763-40111 If the value is between 0.0 and +# 5373484.499999, then it is interpreted as a julian day number +# (corresponding to dates between -4713-11-24 12:00:00 and 9999-12-31 +# 23:59:59, inclusive). +# +# EVIDENCE-OF: R-07289-49223 For numeric values outside of the range of +# valid julian day numbers, but within the range of -210866760000 to +# 253402300799, the 'auto' modifier causes the value to be interpreted +# as a unix timestamp. +# +# EVIDENCE-OF: R-20795-34947 Other numeric values are out of range and +# cause a NULL return. +# +foreach {tn jd date} { + 2.1 0.0 {-4713-11-24 12:00:00} + 2.2 5373484.4999999 {9999-12-31 23:59:59} + 2.3 2440587.5 {1970-01-01 00:00:00} + 2.4 2440587.49998843 {1969-12-31 23:59:59} + 2.5 2440615.7475463 {1970-01-29 05:56:28} + + 2.10 -1 {1969-12-31 23:59:59} + 2.11 5373485 {1970-03-04 04:38:05} + 2.12 -210866760000 {-4713-11-24 12:00:00} + 2.13 253402300799 {9999-12-31 23:59:59} + + 2.20 -210866760001 {NULL} + 2.21 253402300800 {NULL} +} { + datetest $tn "datetime($jd,'auto')" $date +} + +# EVIDENCE-OF: R-38886-35357 The 'auto' modifier is a no-op for text +# time-values. +# +datetest 2.30 {date('2022-01-29','auto')==date('2022-01-29')} {1} + +# EVIDENCE-OF: R-53132-26856 The 'auto' modifier can be used to work +# with date/time values even in cases where it is not known if the +# julian day number or unix timestamp formats are in use. +# +do_execsql_test date3-2.40 { + WITH tx(timeval,datetime) AS ( + VALUES('2022-01-27 13:15:44','2022-01-27 13:15:44'), + (2459607.05260275,'2022-01-27 13:15:44'), + (1643289344,'2022-01-27 13:15:44') + ) + SELECT datetime(timeval,'auto') == datetime FROM tx; +} {1 1 1} + +# EVIDENCE-OF: R-49255-55373 The "unixepoch" modifier (11) only works if +# it immediately follows a time value in the DDDDDDDDDD format. +# +# EVIDENCE-OF: R-23075-39245 This modifier causes the DDDDDDDDDD to be +# interpreted not as a Julian day number as it normally would be, but as +# Unix Time - the number of seconds since 1970. +# +datetest 3.1 {datetime(2459607.05,'+1 hour','unixepoch')} {NULL} +datetest 3.2 {datetime(2459607.05,'unixepoch','+1 hour')} {1970-01-29 12:13:27} + +# EVIDENCE-OF: R-21150-52363 The "julianday" modifier must immediately +# follow the initial time-value which must be of the form DDDDDDDDD. +# +# EVIDENCE-OF: R-31176-64601 Any other use of the 'julianday' modifier +# is an error and causes the function to return NULL. +# +# EVIDENCE-OF: R-32483-36353 The 'julianday' modifier forces the +# time-value number to be interpreted as a julian-day number. +# +# EVIDENCE-OF: R-25859-20124 The only difference is that adding +# 'julianday' forces the DDDDDDDDD time-value format, and causes a NULL +# to be returned if any other time-value format is used. +# +datetest 4.1 {datetime(2459607,'julianday')} {2022-01-27 12:00:00} +datetest 4.2 {datetime(2459607,'+1 hour','julianday')} {NULL} +datetest 4.3 {datetime('2022-01-27','julianday')} {NULL} + + + +# EVIDENCE-OF: R-33431-18865 Unix timestamps for the first 63 days of +# 1970 will be interpreted as julian day numbers. +# +do_execsql_test date3-5.0 { + WITH inc(x) AS (VALUES(-10) UNION ALL SELECT x+1 FROM inc WHERE x<100) + SELECT count(*) FROM inc + WHERE datetime('1970-01-01',format('%+d days',x)) + <> datetime(unixepoch('1970-01-01',format('%+d days',x)),'auto'); +} {63} + +finish_test From 895bab33836282dcb9b4e225c1195230067e1b2c Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 27 Jan 2022 16:14:50 +0000 Subject: [PATCH 02/40] Give the virtual table xBestIndex method access to (some) OFFSET and LIMIT clauses. FossilOrigin-Name: 74fa5757ee0a8499bcd6546dac1a3ecc6048ba9cff9e3e574e28e6e82e894a3d --- ext/misc/qpvtab.c | 12 +++++-- manifest | 37 ++++++++++++---------- manifest.uuid | 2 +- src/delete.c | 2 +- src/fkey.c | 2 +- src/select.c | 6 ++-- src/sqlite.h.in | 2 ++ src/sqliteInt.h | 3 +- src/test_bestindex.c | 4 +++ src/update.c | 6 ++-- src/where.c | 53 +++++++++++++++++++++++++++---- src/whereInt.h | 4 +++ src/wherecode.c | 2 +- src/whereexpr.c | 74 ++++++++++++++++++++++++++++++++++++++++++++ 14 files changed, 174 insertions(+), 35 deletions(-) diff --git a/ext/misc/qpvtab.c b/ext/misc/qpvtab.c index 828a5ba1b8..67f3cfc519 100644 --- a/ext/misc/qpvtab.c +++ b/ext/misc/qpvtab.c @@ -103,7 +103,8 @@ static const char *azColname[] = { "ux", "rhs", "a", "b", "c", "d", "e", - "flags" + "flags", + "" }; /* @@ -143,6 +144,7 @@ static int qpvtabConnect( #define QPVTAB_D 9 #define QPVTAB_E 10 #define QPVTAB_FLAGS 11 +#define QPVTAB_NONE 12 if( rc==SQLITE_OK ){ pNew = sqlite3_malloc( sizeof(*pNew) ); *ppVtab = (sqlite3_vtab*)pNew; @@ -338,6 +340,7 @@ static int qpvtabBestIndex( for(i=0; inConstraint; i++){ sqlite3_value *pVal; int iCol = pIdxInfo->aConstraint[i].iColumn; + int op = pIdxInfo->aConstraint[i].op; if( iCol==QPVTAB_FLAGS && pIdxInfo->aConstraint[i].usable ){ pVal = 0; rc = sqlite3_vtab_rhs_value(pIdxInfo, i, &pVal); @@ -347,10 +350,15 @@ static int qpvtabBestIndex( if( pIdxInfo->idxNum & 2 ) pIdxInfo->orderByConsumed = 1; } } + if( op==SQLITE_INDEX_CONSTRAINT_LIMIT + || op==SQLITE_INDEX_CONSTRAINT_OFFSET + ){ + iCol = QPVTAB_NONE; + } sqlite3_str_appendf(pStr,"aConstraint,%d,%s,%d,%d,", i, azColname[iCol], - pIdxInfo->aConstraint[i].op, + op, pIdxInfo->aConstraint[i].usable); pVal = 0; rc = sqlite3_vtab_rhs_value(pIdxInfo, i, &pVal); diff --git a/manifest b/manifest index 616d06e0b5..81522fbabb 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enforce\sthe\srestriction\sthat\s'unixepoch'\sonly\sworks\sas\sthe\sfirst\smodifier\nafter\sthe\stime-value.\s\sThis\shas\sbeen\sdocumented\ssince\s2004,\sbut\shas\snever\nactually\sbeen\senforced\sbefore.\s\sAlso\sadd\snew\stest\scases\sfor\sdate/time\nfunctions\swith\sevidence\smarks. -D 2022-01-27T13:52:01.555 +C Give\sthe\svirtual\stable\sxBestIndex\smethod\saccess\sto\s(some)\sOFFSET\sand\sLIMIT\nclauses. +D 2022-01-27T16:14:50.962 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -316,7 +316,7 @@ F ext/misc/noop.c 81efe4cad9ec740e64388b14281cb983e6e2c223fed43eb77ab3e34946e0c1 F ext/misc/normalize.c bd84355c118e297522aba74de34a4fd286fc775524e0499b14473918d09ea61f F ext/misc/percentile.c b9086e223d583bdaf8cb73c98a6539d501a2fc4282654adbfea576453d82e691 F ext/misc/prefixes.c 0f4f8cff5aebc00a7e3ac4021fd59cfe1a8e17c800ceaf592859ecb9cbc38196 -F ext/misc/qpvtab.c c662fa0a452ad286e49b6c83ac917600656b2eb47d2225ff6185c56bf80cf8d2 +F ext/misc/qpvtab.c 03986dca441274933dd924170ceee50ad248d24a4cd6a686ff45d5e65e5deac4 F ext/misc/regexp.c b267fd05ff8d38b22f4c2809d7b7a2c61d522e9faf2feb928dbb9662e4a3a386 F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c @@ -502,10 +502,10 @@ F src/ctime.c 2cce39df1a13e05b7633e6d21b651f21492471f991dd7b323a4ee4e7b7f0b7f1 F src/date.c 41627dec396f3d33e2c317a065f9d59bb535982b2ea3a561c96e4d4cf1137b65 F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d -F src/delete.c 52897a8516dc40753503c25eed0e305f09cc50ae474f22b0b4fd31d3b879cc08 +F src/delete.c b5f1716b4d723db48254ee0f896e362cd029e865e05414139ea7f539f3884e1d F src/expr.c 9658bccd1598211ace848c8ca9480dbf8be08dfee1db5cf03897b34b7b6e8fef F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 -F src/fkey.c 5b73f7a7c00f06017531a5bd258cbc2c7a294e55a7f84a729fe27aa525242560 +F src/fkey.c 06e4ac33031b02dde7130c12e79cddf4dc5cfa72b23d8e63a3c26878fc9c1d3c F src/func.c 8fddc42bce95d17938252a543f86fe29e479366e80fbd112a1822913b6247776 F src/global.c 1f56aead86e8a18c4415638f5e6c4d0a0550427f4b3f5d065ba5164cc09c22e8 F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19 @@ -552,12 +552,12 @@ F src/printf.c 975f1f5417f2526365b6e6d7f22332e3e11806dad844701d92846292b654ba9a F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 -F src/select.c 5799f5b15e27bcb8262caec7821f3ef89575e539237db3141be0d26cf8efd22c +F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2 F src/shell.c.in e80a140e92e342e2f92d405a77155c8e3a67c9b1d0bdbacb92885960cd4fc8f2 -F src/sqlite.h.in 31c2c8d737814369bd3b71f3849c4a97ef7ede0aa3ce976ecb11632fa5f1f863 +F src/sqlite.h.in da0e94f140f3f054f0eca8fc4a183167bedc7ae9c51822390c62369455eae5f8 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a -F src/sqliteInt.h 33fbafb55b48f63b791c563378345ebdbbcb73e8fd63a1c22e4df05f435d4714 +F src/sqliteInt.h 8ef2996e02476f73e41ba977f819bda0cc68b7ce238cf404b9b8930df57bc1d0 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -574,7 +574,7 @@ F src/test9.c 12e5ba554d2d1cbe0158f6ab3f7ffcd7a86ee4e5 F src/test_async.c 195ab49da082053fdb0f949c114b806a49ca770a F src/test_autoext.c 915d245e736652a219a907909bb6710f0d587871 F src/test_backup.c bf5da90c9926df0a4b941f2d92825a01bbe090a0 -F src/test_bestindex.c 78809f11026f18a93fcfd798d9479cba37e1201c830260bf1edc674b2fa9b857 +F src/test_bestindex.c ba0314d3bca8b00fa0daaf113a314986f73ea8ad85a7562f983638d09a29045c F src/test_blob.c ae4a0620b478548afb67963095a7417cd06a4ec0a56adb453542203bfdcb31ce F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274 F src/test_config.c 284c29912736f68b0a583a920bf63fd8f9125dffb8a75cb0676e58502b2f7908 @@ -619,7 +619,7 @@ F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c F src/tokenize.c b74d878aa7c82ec8460779468061a96185e22257f68ab785b69abce354b70446 F src/treeview.c 9dfdb7ff7f6645d0a6458dbdf4ffac041c071c4533a6db8bb6e502b979ac67bc F src/trigger.c 692972e4393dfc8017a1a527c1ea1b96ce3d101e84584cd832fcfb83d22b50b2 -F src/update.c 7dfa3866cdfb28bf952c38054c1722720d8ff64750dea421790aecc91f640516 +F src/update.c f875b0d59da5c3055a0b2ac20560e1650229c6787e78de5e9836267b5cbb8359 F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c 602fe229f32a96ceccae4f40824129669582096f7c355f53dbac156c9fecef23 @@ -639,10 +639,10 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b -F src/where.c a14990c7b35e95f8f9cb0dc0d6d2e32fa99135a716a04027cefa48138d280ecb -F src/whereInt.h 8a215acde0f833a4dea3d30a7bbed9f48b4b547b5d5e34cd02acee366476ab80 -F src/wherecode.c 8da0f873278ed6aad42bf2028404d7178dd9cfcdc7179ecc61a87591a15a07d2 -F src/whereexpr.c 9f64c39e53070584e99e4d20c1dd3397e125fabbae8fd414ffec574c410ac7d3 +F src/where.c f89c21296cdf23bbc73333c77a01e0bfdccc9d1a767d38a145e3a8658456b66c +F src/whereInt.h e23780eb06810dc8bd7d855c97a667bf116cb929d8aa107ce1db91df8e47aaa8 +F src/wherecode.c 37878a0a6f790b77f41e7b3d77c42f14bded09aef3d363eaf19b3ee02e765666 +F src/whereexpr.c 50b09b3583301d0a5aef0e5b5d225f4d2b0eba2e36e3f9d0b914e110d4b2f6ce F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 @@ -1942,8 +1942,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P a8db69411b0d1275909adeb21027784ada17af24efe3a59ae0ae2a897659ff17 -R a92b0ae1c265a80dc4f7e5c12c916bf4 +P 64fa9e8c87179211cec248e6dfd7578502e6e969a19e91a4f0e21ed9b972a6bc +R c3554501c3177117c353a66ed5c4eb7c +T *branch * vtab-limit-offset +T *sym-vtab-limit-offset * +T -sym-trunk * U drh -Z 47b9e1bb1e6a61a53486ee72f7ca6190 +Z 853246eb39d3c5347b902f7c653bc6b9 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 40297e7019..173289557e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -64fa9e8c87179211cec248e6dfd7578502e6e969a19e91a4f0e21ed9b972a6bc \ No newline at end of file +74fa5757ee0a8499bcd6546dac1a3ecc6048ba9cff9e3e574e28e6e82e894a3d \ No newline at end of file diff --git a/src/delete.c b/src/delete.c index b51e038918..7b769e6a18 100644 --- a/src/delete.c +++ b/src/delete.c @@ -478,7 +478,7 @@ void sqlite3DeleteFrom( ** ONEPASS_SINGLE: One-pass approach - at most one row deleted. ** ONEPASS_MULTI: One-pass approach - any number of rows may be deleted. */ - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, wcf, iTabCur+1); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,0,wcf,iTabCur+1); if( pWInfo==0 ) goto delete_from_cleanup; eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI ); diff --git a/src/fkey.c b/src/fkey.c index 6ee35bf320..b464743c78 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -651,7 +651,7 @@ static void fkScanChildren( ** clause. For each row found, increment either the deferred or immediate ** foreign key constraint counter. */ if( pParse->nErr==0 ){ - pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0); + pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0, 0); sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); if( pWInfo ){ sqlite3WhereEnd(pWInfo); diff --git a/src/select.c b/src/select.c index b280ed2b3f..a3985b70af 100644 --- a/src/select.c +++ b/src/select.c @@ -6898,7 +6898,7 @@ int sqlite3Select( /* Begin the database scan. */ SELECTTRACE(1,pParse,p,("WhereBegin\n")); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy, - p->pEList, wctrlFlags, p->nSelectRow); + p->pEList, p, wctrlFlags, p->nSelectRow); if( pWInfo==0 ) goto select_end; if( sqlite3WhereOutputRowCount(pWInfo) < p->nSelectRow ){ p->nSelectRow = sqlite3WhereOutputRowCount(pWInfo); @@ -7162,7 +7162,7 @@ int sqlite3Select( sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); SELECTTRACE(1,pParse,p,("WhereBegin\n")); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, pDistinct, - WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0) | distFlag, 0 + 0, (WHERE_GROUPBY|(orderByGrp ? WHERE_SORTBYGROUP : 0)|distFlag), 0 ); if( pWInfo==0 ){ sqlite3ExprListDelete(db, pDistinct); @@ -7460,7 +7460,7 @@ int sqlite3Select( SELECTTRACE(1,pParse,p,("WhereBegin\n")); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy, - pDistinct, minMaxFlag|distFlag, 0); + pDistinct, 0, minMaxFlag|distFlag, 0); if( pWInfo==0 ){ goto select_end; } diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 4b36233b37..d549a1ba4b 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -7148,6 +7148,8 @@ struct sqlite3_index_info { #define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 #define SQLITE_INDEX_CONSTRAINT_ISNULL 71 #define SQLITE_INDEX_CONSTRAINT_IS 72 +#define SQLITE_INDEX_CONSTRAINT_LIMIT 73 +#define SQLITE_INDEX_CONSTRAINT_OFFSET 74 #define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 /* diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 7a266403fb..6d93650713 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4577,7 +4577,8 @@ void sqlite3CodeChangeCount(Vdbe*,int,const char*); void sqlite3DeleteFrom(Parse*, SrcList*, Expr*, ExprList*, Expr*); void sqlite3Update(Parse*, SrcList*, ExprList*,Expr*,int,ExprList*,Expr*, Upsert*); -WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int); +WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*, + ExprList*,Select*,u16,int); void sqlite3WhereEnd(WhereInfo*); LogEst sqlite3WhereOutputRowCount(WhereInfo*); int sqlite3WhereIsDistinct(WhereInfo*); diff --git a/src/test_bestindex.c b/src/test_bestindex.c index 2cd79baf2b..6cda889e5e 100644 --- a/src/test_bestindex.c +++ b/src/test_bestindex.c @@ -424,6 +424,10 @@ static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ zOp = "isnull"; break; case SQLITE_INDEX_CONSTRAINT_IS: zOp = "is"; break; + case SQLITE_INDEX_CONSTRAINT_LIMIT: + zOp = "limit"; break; + case SQLITE_INDEX_CONSTRAINT_OFFSET: + zOp = "offset"; break; } Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("op", -1)); diff --git a/src/update.c b/src/update.c index ccad38df49..a43d0eac53 100644 --- a/src/update.c +++ b/src/update.c @@ -723,7 +723,7 @@ void sqlite3Update( if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){ flags |= WHERE_ONEPASS_MULTIROW; } - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags,iIdxCur); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,0,0,0,flags,iIdxCur); if( pWInfo==0 ) goto update_cleanup; /* A one-pass strategy that might update more than one row may not @@ -1245,7 +1245,9 @@ static void updateVirtualTable( regRowid = ++pParse->nMem; /* Start scanning the virtual table */ - pWInfo = sqlite3WhereBegin(pParse, pSrc,pWhere,0,0,WHERE_ONEPASS_DESIRED,0); + pWInfo = sqlite3WhereBegin( + pParse, pSrc, pWhere, 0, 0, 0, WHERE_ONEPASS_DESIRED, 0 + ); if( pWInfo==0 ) return; /* Populate the argument registers. */ diff --git a/src/where.c b/src/where.c index 32ec17ec42..2afc012f60 100644 --- a/src/where.c +++ b/src/where.c @@ -3459,6 +3459,16 @@ static int whereLoopAddBtree( #ifndef SQLITE_OMIT_VIRTUALTABLE +/* +** Return true if pTerm is a virtual table LIMIT or OFFSET term. +*/ +static int isLimitTerm(WhereTerm *pTerm){ + return pTerm->eOperator==WO_AUX && ( + pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_LIMIT + || pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET + ); +} + /* ** Argument pIdxInfo is already populated with all constraints that may ** be used by the virtual table identified by pBuilder->pNew->iTab. This @@ -3486,7 +3496,8 @@ static int whereLoopAddVirtualOne( u16 mExclude, /* Exclude terms using these operators */ sqlite3_index_info *pIdxInfo, /* Populated object for xBestIndex */ u16 mNoOmit, /* Do not omit these constraints */ - int *pbIn /* OUT: True if plan uses an IN(...) op */ + int *pbIn, /* OUT: True if plan uses an IN(...) op */ + int *pbRetryLimit /* OUT: Retry without LIMIT/OFFSET */ ){ WhereClause *pWC = pBuilder->pWC; struct sqlite3_index_constraint *pIdxCons; @@ -3511,6 +3522,7 @@ static int whereLoopAddVirtualOne( pIdxCons->usable = 0; if( (pTerm->prereqRight & mUsable)==pTerm->prereqRight && (pTerm->eOperator & mExclude)==0 + && (pbRetryLimit || !isLimitTerm(pTerm)) ){ pIdxCons->usable = 1; } @@ -3589,6 +3601,21 @@ static int whereLoopAddVirtualOne( pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; *pbIn = 1; assert( (mExclude & WO_IN)==0 ); } + + if( isLimitTerm(pTerm) && *pbIn ){ + /* If there is an IN(...) term handled as an == (separate call to + ** xFilter for each value on the RHS of the IN) and a LIMIT or + ** OFFSET term handled as well, the plan is unusable. Set output + ** variable *pbRetryLimit to true to tell the caller to retry with + ** LIMIT and OFFSET disabled. */ + if( pIdxInfo->needToFreeIdxStr ){ + sqlite3_free(pIdxInfo->idxStr); + pIdxInfo->idxStr = 0; + pIdxInfo->needToFreeIdxStr = 0; + } + *pbRetryLimit = 1; + return SQLITE_OK; + } } } @@ -3751,6 +3778,7 @@ static int whereLoopAddVirtual( WhereLoop *pNew; Bitmask mBest; /* Tables used by best possible plan */ u16 mNoOmit; + int bRetry = 0; /* True to retry with LIMIT/OFFSET disabled */ assert( (mPrereq & mUnusable)==0 ); pWInfo = pBuilder->pWInfo; @@ -3774,7 +3802,15 @@ static int whereLoopAddVirtual( /* First call xBestIndex() with all constraints usable. */ WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName)); WHERETRACE(0x40, (" VirtualOne: all usable\n")); - rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn); + rc = whereLoopAddVirtualOne( + pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, &bRetry + ); + if( bRetry ){ + assert( rc==SQLITE_OK ); + rc = whereLoopAddVirtualOne( + pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, 0 + ); + } /* If the call to xBestIndex() with all terms enabled produced a plan ** that does not require any source tables (IOW: a plan with mBest==0) @@ -3792,7 +3828,7 @@ static int whereLoopAddVirtual( if( bIn ){ WHERETRACE(0x40, (" VirtualOne: all usable w/o IN\n")); rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn); + pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn, 0); assert( bIn==0 ); mBestNoIn = pNew->prereq & ~mPrereq; if( mBestNoIn==0 ){ @@ -3819,7 +3855,7 @@ static int whereLoopAddVirtual( WHERETRACE(0x40, (" VirtualOne: mPrev=%04llx mNext=%04llx\n", (sqlite3_uint64)mPrev, (sqlite3_uint64)mNext)); rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn); + pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn, 0); if( pNew->prereq==mPrereq ){ seenZero = 1; if( bIn==0 ) seenZeroNoIN = 1; @@ -3832,7 +3868,7 @@ static int whereLoopAddVirtual( if( rc==SQLITE_OK && seenZero==0 ){ WHERETRACE(0x40, (" VirtualOne: all disabled\n")); rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn); + pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn, 0); if( bIn==0 ) seenZeroNoIN = 1; } @@ -3842,7 +3878,7 @@ static int whereLoopAddVirtual( if( rc==SQLITE_OK && seenZeroNoIN==0 ){ WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n")); rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn); + pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn, 0); } } @@ -5248,6 +5284,7 @@ WhereInfo *sqlite3WhereBegin( Expr *pWhere, /* The WHERE clause */ ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */ ExprList *pResultSet, /* Query result set. Req'd for DISTINCT */ + Select *pLimit, /* Use this LIMIT/OFFSET clause, if any */ u16 wctrlFlags, /* The WHERE_* flags defined in sqliteInt.h */ int iAuxArg /* If WHERE_OR_SUBCLAUSE is set, index cursor number ** If WHERE_USE_LIMIT, then the limit amount */ @@ -5324,6 +5361,9 @@ WhereInfo *sqlite3WhereBegin( pWInfo->wctrlFlags = wctrlFlags; pWInfo->iLimit = iAuxArg; pWInfo->savedNQueryLoop = pParse->nQueryLoop; +#ifndef SQLITE_OMIT_VIRTUALTABLE + pWInfo->pLimit = pLimit; +#endif memset(&pWInfo->nOBSat, 0, offsetof(WhereInfo,sWC) - offsetof(WhereInfo,nOBSat)); memset(&pWInfo->a[0], 0, sizeof(WhereLoop)+nTabList*sizeof(WhereLevel)); @@ -5392,6 +5432,7 @@ WhereInfo *sqlite3WhereBegin( /* Analyze all of the subexpressions. */ sqlite3WhereExprAnalyze(pTabList, &pWInfo->sWC); + sqlite3WhereAddLimit(&pWInfo->sWC, pLimit); if( db->mallocFailed ) goto whereBeginError; /* Special case: WHERE terms that do not refer to any tables in the join diff --git a/src/whereInt.h b/src/whereInt.h index 1304a40a77..cdddc10524 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -453,6 +453,9 @@ struct WhereInfo { ExprList *pOrderBy; /* The ORDER BY clause or NULL */ ExprList *pResultSet; /* Result set of the query */ Expr *pWhere; /* The complete WHERE clause */ +#ifndef SQLITE_OMIT_VIRTUALTABLE + Select *pLimit; /* Used to access LIMIT expr/registers for vtabs */ +#endif int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */ int iContinue; /* Jump here to continue with next record */ int iBreak; /* Jump here to break out of the loop */ @@ -538,6 +541,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( void sqlite3WhereClauseInit(WhereClause*,WhereInfo*); void sqlite3WhereClauseClear(WhereClause*); void sqlite3WhereSplit(WhereClause*,Expr*,u8); +void sqlite3WhereAddLimit(WhereClause*, Select*); Bitmask sqlite3WhereExprUsage(WhereMaskSet*, Expr*); Bitmask sqlite3WhereExprUsageNN(WhereMaskSet*, Expr*); Bitmask sqlite3WhereExprListUsage(WhereMaskSet*, ExprList*); diff --git a/src/wherecode.c b/src/wherecode.c index b3ae06cae2..4990cda0ce 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -2358,7 +2358,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( /* Loop through table entries that match term pOrTerm. */ ExplainQueryPlan((pParse, 1, "INDEX %d", ii+1)); WHERETRACE(0xffff, ("Subplan for OR-clause:\n")); - pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, + pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, 0, WHERE_OR_SUBCLAUSE, iCovCur); assert( pSubWInfo || pParse->nErr ); if( pSubWInfo ){ diff --git a/src/whereexpr.c b/src/whereexpr.c index f0660a990e..1cc5ce5370 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1522,6 +1522,80 @@ void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){ } } +/* +** Add either a LIMIT (if eMatchOp==SQLITE_INDEX_CONSTRAINT_LIMIT) or +** OFFSET (if eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET) term to the +** where-clause passed as the first argument. The value for the term +** is found in register iReg. +*/ +void whereAddLimitExpr(WhereClause *pWC, int iReg, int iCsr, int eMatchOp){ + Parse *pParse = pWC->pWInfo->pParse; + sqlite3 *db = pParse->db; + Expr *pExpr; + + pExpr = sqlite3PExpr(pParse, TK_MATCH, 0, sqlite3Expr(db,TK_REGISTER,0)); + if( pExpr ){ + WhereTerm *pTerm; + int idx; + pExpr->pRight->iTable = iReg; + idx = whereClauseInsert(pWC, pExpr, TERM_DYNAMIC|TERM_VIRTUAL); + pTerm = &pWC->a[idx]; + pTerm->leftCursor = iCsr; + pTerm->eOperator = WO_AUX; + pTerm->eMatchOp = eMatchOp; + } +} + +/* +** Possibly add terms corresponding to the LIMIT and OFFSET clauses of the +** SELECT statement passed as the second argument. These terms are only +** added if: +** +** 1. The SELECT statement has a LIMIT clause, and +** 2. The SELECT statement is not an aggregate or DISTINCT query, and +** 3. The SELECT statement has exactly one object in its from clause, and +** that object is a virtual table, and +** 4. There are no terms in the WHERE clause that will not be passed +** to the virtual table xBestIndex method. +** 5. The ORDER BY clause, if any, will be made available to the xBestIndex +** method. +** +** LIMIT and OFFSET terms are ignored by most of the planner code. They +** exist only so that they may be passed to the xBestIndex method of the +** single virtual table in the FROM clause of the SELECT. +*/ +void sqlite3WhereAddLimit(WhereClause *pWC, Select *p){ + assert( p==0 || (p->pGroupBy==0 && (p->selFlags & SF_Aggregate)==0) ); + if( (p && p->pLimit) /* 1 */ + && (p->selFlags & (SF_Distinct|SF_Aggregate))==0 /* 2 */ + && (p->pSrc->nSrc==1 && IsVirtual(p->pSrc->a[0].pTab)) /* 3 */ + ){ + ExprList *pOrderBy = p->pOrderBy; + int iCsr = p->pSrc->a[0].iCursor; + int ii; + + /* Check condition (4). Return early if it is not met. */ + for(ii=0; iinTerm; ii++){ + if( pWC->a[ii].leftCursor!=iCsr ) return; + } + + /* Check condition (5). Return early if it is not met. */ + if( pOrderBy ){ + for(ii=0; iinExpr; ii++){ + Expr *pExpr = pOrderBy->a[ii].pExpr; + if( pExpr->op!=TK_COLUMN || pExpr->iTable!=iCsr ) return; + if( pOrderBy->a[ii].sortFlags & KEYINFO_ORDER_BIGNULL ) return; + } + } + + /* All conditions are met. Add the terms to the where-clause object. */ + whereAddLimitExpr(pWC, p->iLimit, iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); + if( p->iOffset>0 ){ + whereAddLimitExpr(pWC, p->iOffset, iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET); + } + } +} + /* ** Initialize a preallocated WhereClause structure. */ From 233ba1b8eb0dc3e87942d08455ed43f32f25441c Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 27 Jan 2022 18:45:26 +0000 Subject: [PATCH 03/40] Make LIMIT and OFFSET values available to sqlite3_vtab_rhs_value(). FossilOrigin-Name: b2d37c086c87fe4aee013bba4eab26f642207cd2a797fe4888a6ef868884c191 --- manifest | 15 ++++++--------- manifest.uuid | 2 +- src/whereexpr.c | 41 +++++++++++++++++++++++++++++++++-------- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/manifest b/manifest index 81522fbabb..55c1838999 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Give\sthe\svirtual\stable\sxBestIndex\smethod\saccess\sto\s(some)\sOFFSET\sand\sLIMIT\nclauses. -D 2022-01-27T16:14:50.962 +C Make\sLIMIT\sand\sOFFSET\svalues\savailable\sto\ssqlite3_vtab_rhs_value(). +D 2022-01-27T18:45:26.825 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -642,7 +642,7 @@ F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b F src/where.c f89c21296cdf23bbc73333c77a01e0bfdccc9d1a767d38a145e3a8658456b66c F src/whereInt.h e23780eb06810dc8bd7d855c97a667bf116cb929d8aa107ce1db91df8e47aaa8 F src/wherecode.c 37878a0a6f790b77f41e7b3d77c42f14bded09aef3d363eaf19b3ee02e765666 -F src/whereexpr.c 50b09b3583301d0a5aef0e5b5d225f4d2b0eba2e36e3f9d0b914e110d4b2f6ce +F src/whereexpr.c 455b5eeef49693ac6f7303e4529bb997fd4e0f0bd594a6f8e49c0ea7e399368f F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 @@ -1942,11 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 64fa9e8c87179211cec248e6dfd7578502e6e969a19e91a4f0e21ed9b972a6bc -R c3554501c3177117c353a66ed5c4eb7c -T *branch * vtab-limit-offset -T *sym-vtab-limit-offset * -T -sym-trunk * +P 74fa5757ee0a8499bcd6546dac1a3ecc6048ba9cff9e3e574e28e6e82e894a3d +R 4f1dec533377c1a483f6b7fee31b9110 U drh -Z 853246eb39d3c5347b902f7c653bc6b9 +Z 8464b51a4c3f5e11d93482ca57b3dd6d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 173289557e..93e902aff7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -74fa5757ee0a8499bcd6546dac1a3ecc6048ba9cff9e3e574e28e6e82e894a3d \ No newline at end of file +b2d37c086c87fe4aee013bba4eab26f642207cd2a797fe4888a6ef868884c191 \ No newline at end of file diff --git a/src/whereexpr.c b/src/whereexpr.c index 1cc5ce5370..183f22f599 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1527,18 +1527,40 @@ void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){ ** OFFSET (if eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET) term to the ** where-clause passed as the first argument. The value for the term ** is found in register iReg. +** +** In the common case where the value is a simple integer +** (example: "LIMIT 5 OFFSET 10") then the expression codes as a +** TK_INTEGER so that it will be available to sqlite3_vtab_rhs_value(). +** If not, then it codes as a TK_REGISTER expression. */ -void whereAddLimitExpr(WhereClause *pWC, int iReg, int iCsr, int eMatchOp){ +void whereAddLimitExpr( + WhereClause *pWC, /* Add the constraint to this WHERE clause */ + int iReg, /* Register that will hold value of the limit/offset */ + Expr *pExpr, /* Expression that defines the limit/offset */ + int iCsr, /* Cursor to which the constraint applies */ + int eMatchOp /* SQLITE_INDEX_CONSTRAINT_LIMIT or _OFFSET */ +){ Parse *pParse = pWC->pWInfo->pParse; sqlite3 *db = pParse->db; - Expr *pExpr; + Expr *pNew; + int iVal = 0; - pExpr = sqlite3PExpr(pParse, TK_MATCH, 0, sqlite3Expr(db,TK_REGISTER,0)); - if( pExpr ){ + if( sqlite3ExprIsInteger(pExpr, &iVal) ){ + Expr *pVal = sqlite3Expr(db, TK_INTEGER, 0); + if( pVal==0 ) return; + ExprSetProperty(pVal, EP_IntValue); + pVal->u.iValue = iVal; + pNew = sqlite3PExpr(pParse, TK_MATCH, 0, pVal); + }else{ + Expr *pVal = sqlite3Expr(db, TK_REGISTER, 0); + if( pVal==0 ) return; + pVal->iTable = iReg; + pNew = sqlite3PExpr(pParse, TK_MATCH, 0, pVal); + } + if( pNew ){ WhereTerm *pTerm; int idx; - pExpr->pRight->iTable = iReg; - idx = whereClauseInsert(pWC, pExpr, TERM_DYNAMIC|TERM_VIRTUAL); + idx = whereClauseInsert(pWC, pNew, TERM_DYNAMIC|TERM_VIRTUAL); pTerm = &pWC->a[idx]; pTerm->leftCursor = iCsr; pTerm->eOperator = WO_AUX; @@ -1589,9 +1611,12 @@ void sqlite3WhereAddLimit(WhereClause *pWC, Select *p){ } /* All conditions are met. Add the terms to the where-clause object. */ - whereAddLimitExpr(pWC, p->iLimit, iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); + assert( p->pLimit->op==TK_LIMIT ); + whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft, + iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); if( p->iOffset>0 ){ - whereAddLimitExpr(pWC, p->iOffset, iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET); + whereAddLimitExpr(pWC, p->iOffset, p->pLimit->pRight, + iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET); } } } From 8f2c0b59dae1b8f194b73004d73047ce4f79a544 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 27 Jan 2022 21:18:14 +0000 Subject: [PATCH 04/40] Query planner always honors the aConstraintUsage[].omit flag for the SQLITE_INDEX_CONSTRAINT_OFFSET constraint. FossilOrigin-Name: 38c5151eb8cc789ad082a1b40d16b2ddb55454219bd685b6e420995af6a15f55 --- ext/misc/qpvtab.c | 27 ++++++++++++++++++++++----- manifest | 20 ++++++++++---------- manifest.uuid | 2 +- src/where.c | 14 ++++++++------ src/whereInt.h | 4 +++- src/wherecode.c | 9 +++++++++ src/whereexpr.c | 1 + 7 files changed, 54 insertions(+), 23 deletions(-) diff --git a/ext/misc/qpvtab.c b/ext/misc/qpvtab.c index 67f3cfc519..fb0c155a27 100644 --- a/ext/misc/qpvtab.c +++ b/ext/misc/qpvtab.c @@ -40,7 +40,7 @@ ** ** Option 1 is the default behavior. 2 is use if there is a usable ** constraint on "flags" with an integer right-hand side that where the -** value of the right-hand side has its 0x01 bit set. +** value of the right-hand side has its 0x001 bit set. ** ** All constraints on columns "a" through "e" are marked as "omit". ** @@ -48,6 +48,12 @@ ** is an integer and that integer has its 0x02 bit set, then the ** orderByConsumed flag is set. ** +** FLAGS SUMMARY: +** +** 0x001 Columns 'a' through 'e' have INT values +** 0x002 orderByConsumed is set +** 0x004 OFFSET and LIMIT have omit set +** ** COMPILE: ** ** gcc -Wall -g -shared -fPIC -I. qpvtab.c -o qqvtab.so @@ -228,7 +234,7 @@ static int qpvtabColumn( sqlite3_result_text64(ctx, z, zEnd-z, SQLITE_TRANSIENT, SQLITE_UTF8); } }else if( i>=QPVTAB_A && i<=QPVTAB_E ){ - if( pCur->flags & 1 ){ + if( pCur->flags & 0x001 ){ sqlite3_result_int(ctx, i-QPVTAB_A+1); }else{ char x = 'a'+i-QPVTAB_A; @@ -347,7 +353,7 @@ static int qpvtabBestIndex( assert( rc==SQLITE_OK || pVal==0 ); if( pVal ){ pIdxInfo->idxNum = sqlite3_value_int(pVal); - if( pIdxInfo->idxNum & 2 ) pIdxInfo->orderByConsumed = 1; + if( pIdxInfo->idxNum & 0x002 ) pIdxInfo->orderByConsumed = 1; } } if( op==SQLITE_INDEX_CONSTRAINT_LIMIT @@ -367,9 +373,20 @@ static int qpvtabBestIndex( qpvtabStrAppendValue(pStr, pVal); } sqlite3_str_append(pStr, "\n", 1); + } + for(i=0; inConstraint; i++){ + int iCol = pIdxInfo->aConstraint[i].iColumn; + int op = pIdxInfo->aConstraint[i].op; + if( op==SQLITE_INDEX_CONSTRAINT_LIMIT + || op==SQLITE_INDEX_CONSTRAINT_OFFSET + ){ + iCol = QPVTAB_NONE; + } if( iCol>=QPVTAB_A && pIdxInfo->aConstraint[i].usable ){ - pIdxInfo->aConstraintUsage[i].argvIndex = ++k; - pIdxInfo->aConstraintUsage[i].omit = 1; + pIdxInfo->aConstraintUsage[i].argvIndex = ++k; + if( iCol<=QPVTAB_FLAGS || (pIdxInfo->idxNum & 0x004)!=0 ){ + pIdxInfo->aConstraintUsage[i].omit = 1; + } } } sqlite3_str_appendf(pStr, "nOrderBy,%d,,,,\n", pIdxInfo->nOrderBy); diff --git a/manifest b/manifest index 55c1838999..f1f49fa502 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Make\sLIMIT\sand\sOFFSET\svalues\savailable\sto\ssqlite3_vtab_rhs_value(). -D 2022-01-27T18:45:26.825 +C Query\splanner\salways\shonors\sthe\saConstraintUsage[].omit\sflag\sfor\sthe\nSQLITE_INDEX_CONSTRAINT_OFFSET\sconstraint. +D 2022-01-27T21:18:14.656 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -316,7 +316,7 @@ F ext/misc/noop.c 81efe4cad9ec740e64388b14281cb983e6e2c223fed43eb77ab3e34946e0c1 F ext/misc/normalize.c bd84355c118e297522aba74de34a4fd286fc775524e0499b14473918d09ea61f F ext/misc/percentile.c b9086e223d583bdaf8cb73c98a6539d501a2fc4282654adbfea576453d82e691 F ext/misc/prefixes.c 0f4f8cff5aebc00a7e3ac4021fd59cfe1a8e17c800ceaf592859ecb9cbc38196 -F ext/misc/qpvtab.c 03986dca441274933dd924170ceee50ad248d24a4cd6a686ff45d5e65e5deac4 +F ext/misc/qpvtab.c 09738419e25f603a35c0ac8bd0a04daab794f48d08a9bc07a6085b9057b99009 F ext/misc/regexp.c b267fd05ff8d38b22f4c2809d7b7a2c61d522e9faf2feb928dbb9662e4a3a386 F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c @@ -639,10 +639,10 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b -F src/where.c f89c21296cdf23bbc73333c77a01e0bfdccc9d1a767d38a145e3a8658456b66c -F src/whereInt.h e23780eb06810dc8bd7d855c97a667bf116cb929d8aa107ce1db91df8e47aaa8 -F src/wherecode.c 37878a0a6f790b77f41e7b3d77c42f14bded09aef3d363eaf19b3ee02e765666 -F src/whereexpr.c 455b5eeef49693ac6f7303e4529bb997fd4e0f0bd594a6f8e49c0ea7e399368f +F src/where.c f61ca6dbb4bbb7171aa5f5459982d8d98aac24852572bcee250f9c469a1963ba +F src/whereInt.h 0748a6fce98b41862445906922a809146ff7ef4de16ed9022b0bc4e5c43aa60a +F src/wherecode.c c313ccf5ed13dc7e88c64f93733f414dee369a212508a866878696d83c64fc36 +F src/whereexpr.c 391d04108e4a5a701d2fad374ff54db8629bce12b55a7ee30a478d440ea4bfe0 F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 74fa5757ee0a8499bcd6546dac1a3ecc6048ba9cff9e3e574e28e6e82e894a3d -R 4f1dec533377c1a483f6b7fee31b9110 +P b2d37c086c87fe4aee013bba4eab26f642207cd2a797fe4888a6ef868884c191 +R 87c4173ca514b2a053765a7d35c4d8eb U drh -Z 8464b51a4c3f5e11d93482ca57b3dd6d +Z 05d67635fa5a91d132911c78dd294cd6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 93e902aff7..5e209241f6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b2d37c086c87fe4aee013bba4eab26f642207cd2a797fe4888a6ef868884c191 \ No newline at end of file +38c5151eb8cc789ad082a1b40d16b2ddb55454219bd685b6e420995af6a15f55 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 2afc012f60..ce6ae30709 100644 --- a/src/where.c +++ b/src/where.c @@ -3463,10 +3463,9 @@ static int whereLoopAddBtree( ** Return true if pTerm is a virtual table LIMIT or OFFSET term. */ static int isLimitTerm(WhereTerm *pTerm){ - return pTerm->eOperator==WO_AUX && ( - pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_LIMIT - || pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET - ); + assert( pTerm->eOperator==WO_AUX || pTerm->eMatchOp==0 ); + return pTerm->eMatchOp>=SQLITE_INDEX_CONSTRAINT_LIMIT + && pTerm->eMatchOp<=SQLITE_INDEX_CONSTRAINT_OFFSET; } /* @@ -3555,8 +3554,8 @@ static int whereLoopAddVirtualOne( mxTerm = -1; assert( pNew->nLSlot>=nConstraint ); - for(i=0; iaLTerm[i] = 0; - pNew->u.vtab.omitMask = 0; + memset(pNew->aLTerm, 0, sizeof(pNew->aLTerm[0])*nConstraint ); + memset(&pNew->u.vtab, 0, sizeof(pNew->u.vtab)); pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; for(i=0; ieMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET ){ + pNew->u.vtab.bOmitOffset = 1; + } } if( (pTerm->eOperator & WO_IN)!=0 ){ /* A virtual table that is constrained by an IN clause may not diff --git a/src/whereInt.h b/src/whereInt.h index cdddc10524..14c40fcb43 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -123,7 +123,8 @@ struct WhereLoop { } btree; struct { /* Information for virtual tables */ int idxNum; /* Index number */ - u8 needFree; /* True if sqlite3_free(idxStr) is needed */ + u8 needFree : 1; /* True if sqlite3_free(idxStr) is needed */ + u8 bOmitOffset : 1; /* True to let virtual table handle offset */ i8 isOrdered; /* True if satisfies ORDER BY */ u16 omitMask; /* Terms that may be omitted */ char *idxStr; /* Index identifier string */ @@ -612,5 +613,6 @@ void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*); #define WHERE_TRANSCONS 0x00200000 /* Uses a transitive constraint */ #define WHERE_BLOOMFILTER 0x00400000 /* Consider using a Bloom-filter */ #define WHERE_SELFCULL 0x00800000 /* nOut reduced by extra WHERE terms */ +#define WHERE_OMIT_OFFSET 0x01000000 /* Set offset counter to zero */ #endif /* !defined(SQLITE_WHEREINT_H) */ diff --git a/src/wherecode.c b/src/wherecode.c index 4990cda0ce..2beb596e69 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -1537,6 +1537,15 @@ Bitmask sqlite3WhereCodeOneLoopStart( }else{ Expr *pRight = pTerm->pExpr->pRight; codeExprOrVector(pParse, pRight, iTarget, 1); + if( pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET + && pLoop->u.vtab.bOmitOffset + ){ + assert( pTerm->eOperator==WO_AUX ); + assert( pWInfo->pLimit!=0 ); + assert( pWInfo->pLimit->iOffset>0 ); + sqlite3VdbeAddOp2(v, OP_Integer, 0, pWInfo->pLimit->iOffset); + VdbeComment((v,"Zero OFFSET counter")); + } } } sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg); diff --git a/src/whereexpr.c b/src/whereexpr.c index 183f22f599..8381994e2b 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1656,6 +1656,7 @@ void sqlite3WhereClauseClear(WhereClause *pWC){ } #endif while(1){ + assert( a->eMatchOp==0 || a->eOperator==WO_AUX ); if( a->wtFlags & TERM_DYNAMIC ){ sqlite3ExprDelete(db, a->pExpr); } From 0ae56fbdd01bfefe64c33d105b5407125b11c7ef Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 28 Jan 2022 13:18:40 +0000 Subject: [PATCH 05/40] Enable SQLITE_INDEX_CONSTRAINT_LIMIT/OFFSET even if there are vector comparison operators in the WHERE clause. Also: Do not enable sqlite3_vtab_rhs_value() for LIMIT/OFFSET if the value is negative, as this violates an invariant on Expr.u.iValue. FossilOrigin-Name: 140480b3ec5466513b165d4818f1180cff6e4a5edcd47efb54cbff74a7107507 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/whereexpr.c | 14 ++++++++++++-- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index f1f49fa502..278f5cdb0a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Query\splanner\salways\shonors\sthe\saConstraintUsage[].omit\sflag\sfor\sthe\nSQLITE_INDEX_CONSTRAINT_OFFSET\sconstraint. -D 2022-01-27T21:18:14.656 +C Enable\sSQLITE_INDEX_CONSTRAINT_LIMIT/OFFSET\seven\sif\sthere\sare\svector\ncomparison\soperators\sin\sthe\sWHERE\sclause.\s\sAlso:\s\sDo\snot\senable\nsqlite3_vtab_rhs_value()\sfor\sLIMIT/OFFSET\sif\sthe\svalue\sis\snegative,\sas\sthis\nviolates\san\sinvariant\son\sExpr.u.iValue. +D 2022-01-28T13:18:40.313 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -642,7 +642,7 @@ F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b F src/where.c f61ca6dbb4bbb7171aa5f5459982d8d98aac24852572bcee250f9c469a1963ba F src/whereInt.h 0748a6fce98b41862445906922a809146ff7ef4de16ed9022b0bc4e5c43aa60a F src/wherecode.c c313ccf5ed13dc7e88c64f93733f414dee369a212508a866878696d83c64fc36 -F src/whereexpr.c 391d04108e4a5a701d2fad374ff54db8629bce12b55a7ee30a478d440ea4bfe0 +F src/whereexpr.c 869596d69a498604fe3e00ff06475038560732227bc42de0cf5dc92248740a4f F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P b2d37c086c87fe4aee013bba4eab26f642207cd2a797fe4888a6ef868884c191 -R 87c4173ca514b2a053765a7d35c4d8eb +P 38c5151eb8cc789ad082a1b40d16b2ddb55454219bd685b6e420995af6a15f55 +R 05c94023401bdef7db1e43fcf139e4c2 U drh -Z 05d67635fa5a91d132911c78dd294cd6 +Z b99686de01cbc6d8d45082533904b9d3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5e209241f6..a2fc430536 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -38c5151eb8cc789ad082a1b40d16b2ddb55454219bd685b6e420995af6a15f55 \ No newline at end of file +140480b3ec5466513b165d4818f1180cff6e4a5edcd47efb54cbff74a7107507 \ No newline at end of file diff --git a/src/whereexpr.c b/src/whereexpr.c index 8381994e2b..d47cfd7e92 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1379,7 +1379,10 @@ static void exprAnalyze( ** no longer used. ** ** This is only required if at least one side of the comparison operation - ** is not a sub-select. */ + ** is not a sub-select. + ** + ** tag-20220128a + */ if( (pExpr->op==TK_EQ || pExpr->op==TK_IS) && (nLeft = sqlite3ExprVectorSize(pExpr->pLeft))>1 && sqlite3ExprVectorSize(pExpr->pRight)==nLeft @@ -1545,7 +1548,7 @@ void whereAddLimitExpr( Expr *pNew; int iVal = 0; - if( sqlite3ExprIsInteger(pExpr, &iVal) ){ + if( sqlite3ExprIsInteger(pExpr, &iVal) && iVal>=0 ){ Expr *pVal = sqlite3Expr(db, TK_INTEGER, 0); if( pVal==0 ) return; ExprSetProperty(pVal, EP_IntValue); @@ -1598,6 +1601,13 @@ void sqlite3WhereAddLimit(WhereClause *pWC, Select *p){ /* Check condition (4). Return early if it is not met. */ for(ii=0; iinTerm; ii++){ + if( pWC->a[ii].wtFlags & TERM_CODED ){ + /* This term is a vector operation that has been decomposed into + ** other, subsequent terms. It can be ignored. See tag-20220128a */ + assert( pWC->a[ii].wtFlags & TERM_VIRTUAL ); + assert( pWC->a[ii].eOperator==0 ); + continue; + } if( pWC->a[ii].leftCursor!=iCsr ) return; } From d0c7baf5050ec9e9c51b496a5b00bd4b1f716aa1 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 28 Jan 2022 18:43:33 +0000 Subject: [PATCH 06/40] Remove a NEVER() that has become reachable now that we have the sqlite3_vtab_rhs_value() interface. FossilOrigin-Name: c4c30df7cd858315359bd9bfa90a07152d87a2301eaec865c5bc1c65e272eec8 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/vdbemem.c | 4 ---- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 278f5cdb0a..995fa2e5f4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enable\sSQLITE_INDEX_CONSTRAINT_LIMIT/OFFSET\seven\sif\sthere\sare\svector\ncomparison\soperators\sin\sthe\sWHERE\sclause.\s\sAlso:\s\sDo\snot\senable\nsqlite3_vtab_rhs_value()\sfor\sLIMIT/OFFSET\sif\sthe\svalue\sis\snegative,\sas\sthis\nviolates\san\sinvariant\son\sExpr.u.iValue. -D 2022-01-28T13:18:40.313 +C Remove\sa\sNEVER()\sthat\shas\sbecome\sreachable\snow\sthat\swe\shave\sthe\nsqlite3_vtab_rhs_value()\sinterface. +D 2022-01-28T18:43:33.716 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -630,7 +630,7 @@ F src/vdbeInt.h d89d5d2150500cfb08615329fd20aea9d746bba5f2c3ecb8a17e2d2d9be029e5 F src/vdbeapi.c 22c79072ae7d8a01e9bcae8ba16e918d60d202eaa9553b5fda38f99f7464d99a F src/vdbeaux.c e761b8011baec7a4773f0a7594783f2cd71f699ab187c4aad917529ab8acd3fe F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd -F src/vdbemem.c da4d594084d581be6436582bb44bb128feeb138a3e6c313eda6749ebdc3a65ec +F src/vdbemem.c eb6042667c02c3ef1f968235b4a170e31b23a4b6a57f65a8454eab4d36f14b7f F src/vdbesort.c 43756031ca7430f7aec3ef904824a7883c4ede783e51f280d99b9b65c0796e35 F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf823 F src/vdbevtab.c f99b275366c5fc5e2d99f734729880994ab9500bdafde7fae3b02d562b9d323c @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 38c5151eb8cc789ad082a1b40d16b2ddb55454219bd685b6e420995af6a15f55 -R 05c94023401bdef7db1e43fcf139e4c2 +P 140480b3ec5466513b165d4818f1180cff6e4a5edcd47efb54cbff74a7107507 +R b5457dcd74cf42bf2b317f01e195dfec U drh -Z b99686de01cbc6d8d45082533904b9d3 +Z 9ee62f5941b268da60926e062d6f8568 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a2fc430536..4c6ca64435 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -140480b3ec5466513b165d4818f1180cff6e4a5edcd47efb54cbff74a7107507 \ No newline at end of file +c4c30df7cd858315359bd9bfa90a07152d87a2301eaec865c5bc1c65e272eec8 \ No newline at end of file diff --git a/src/vdbemem.c b/src/vdbemem.c index 5a9d15f465..5d2d69268e 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -1530,11 +1530,7 @@ static int valueFromExpr( assert( pExpr!=0 ); while( (op = pExpr->op)==TK_UPLUS || op==TK_SPAN ) pExpr = pExpr->pLeft; -#if defined(SQLITE_ENABLE_STAT4) if( op==TK_REGISTER ) op = pExpr->op2; -#else - if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; -#endif /* Compressed expressions only appear when parsing the DEFAULT clause ** on a table column definition, and hence only when pCtx==0. This From cee5cb484de348ef8aa798ed7e740d03d4e8218d Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 28 Jan 2022 19:53:37 +0000 Subject: [PATCH 07/40] Include hidden columns of table valued functions that are constrained by the function arguments in the colUsed bitmask. FossilOrigin-Name: 8f8cab0d0e219f0c4bc2b8061c0013d90a2a5e598dd6cf420d302dfd1b7cbe11 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/whereexpr.c | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 995fa2e5f4..5ac5027c9a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sa\sNEVER()\sthat\shas\sbecome\sreachable\snow\sthat\swe\shave\sthe\nsqlite3_vtab_rhs_value()\sinterface. -D 2022-01-28T18:43:33.716 +C Include\shidden\scolumns\sof\stable\svalued\sfunctions\sthat\sare\sconstrained\nby\sthe\sfunction\sarguments\sin\sthe\scolUsed\sbitmask. +D 2022-01-28T19:53:37.840 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -642,7 +642,7 @@ F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b F src/where.c f61ca6dbb4bbb7171aa5f5459982d8d98aac24852572bcee250f9c469a1963ba F src/whereInt.h 0748a6fce98b41862445906922a809146ff7ef4de16ed9022b0bc4e5c43aa60a F src/wherecode.c c313ccf5ed13dc7e88c64f93733f414dee369a212508a866878696d83c64fc36 -F src/whereexpr.c 869596d69a498604fe3e00ff06475038560732227bc42de0cf5dc92248740a4f +F src/whereexpr.c 42195849443c3648fa9900c82837b7592c91bd43934ad9369d30f5d55d7fe19e F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 140480b3ec5466513b165d4818f1180cff6e4a5edcd47efb54cbff74a7107507 -R b5457dcd74cf42bf2b317f01e195dfec +P c4c30df7cd858315359bd9bfa90a07152d87a2301eaec865c5bc1c65e272eec8 +R b21847307144bd6983c5bb4141c2b106 U drh -Z 9ee62f5941b268da60926e062d6f8568 +Z c180a25bc067b4731882777cea55422e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4c6ca64435..1fe4679ade 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c4c30df7cd858315359bd9bfa90a07152d87a2301eaec865c5bc1c65e272eec8 \ No newline at end of file +8f8cab0d0e219f0c4bc2b8061c0013d90a2a5e598dd6cf420d302dfd1b7cbe11 \ No newline at end of file diff --git a/src/whereexpr.c b/src/whereexpr.c index d47cfd7e92..e4ddf49328 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1824,6 +1824,7 @@ void sqlite3WhereTabFuncArgs( pColRef->iColumn = k++; assert( ExprUseYTab(pColRef) ); pColRef->y.pTab = pTab; + pItem->colUsed |= sqlite3ExprColUsed(pColRef); pRhs = sqlite3PExpr(pParse, TK_UPLUS, sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0); pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs); From 75863ec11c30d0d9c6278257b0cf48d506564636 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 28 Jan 2022 21:39:29 +0000 Subject: [PATCH 08/40] When nesting Parse objects, make sure the new one has nErr set if there has been an OOM error. FossilOrigin-Name: ae088cbc968a565c3e0a8dd74ce150cac4a87978b593a3204f475fa196f1603c --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/prepare.c | 3 ++- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 616d06e0b5..b35d09ae47 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enforce\sthe\srestriction\sthat\s'unixepoch'\sonly\sworks\sas\sthe\sfirst\smodifier\nafter\sthe\stime-value.\s\sThis\shas\sbeen\sdocumented\ssince\s2004,\sbut\shas\snever\nactually\sbeen\senforced\sbefore.\s\sAlso\sadd\snew\stest\scases\sfor\sdate/time\nfunctions\swith\sevidence\smarks. -D 2022-01-27T13:52:01.555 +C When\snesting\sParse\sobjects,\smake\ssure\sthe\snew\sone\shas\snErr\sset\sif\sthere\nhas\sbeen\san\sOOM\serror. +D 2022-01-28T21:39:29.986 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -547,7 +547,7 @@ F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586 F src/pcache1.c 54881292a9a5db202b2c0ac541c5e3ef9a5e8c4f1c1383adb2601d5499a60e65 F src/pragma.c 7c024d690a3dc93f61830f11f900e4af2357f31d081b0c79099ca5e28919cba7 F src/pragma.h 87330ed2fbfa2a1274de93ca0ab850fba336189228cb256089202c3b52766fad -F src/prepare.c 1e23522c934d90ff42de1b9b4f782fdf0fb690b06b92d7480b471ccb2b5899ea +F src/prepare.c a187dade741c1f09ae118fcbbf0302511807bfc0355880927d7152eb75b8260d F src/printf.c 975f1f5417f2526365b6e6d7f22332e3e11806dad844701d92846292b654ba9a F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P a8db69411b0d1275909adeb21027784ada17af24efe3a59ae0ae2a897659ff17 -R a92b0ae1c265a80dc4f7e5c12c916bf4 +P 64fa9e8c87179211cec248e6dfd7578502e6e969a19e91a4f0e21ed9b972a6bc +R 667bad7a5761faade1541e669d6ff48d U drh -Z 47b9e1bb1e6a61a53486ee72f7ca6190 +Z 316eee84140bfead401536d9ec3a7c75 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 40297e7019..302012315c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -64fa9e8c87179211cec248e6dfd7578502e6e969a19e91a4f0e21ed9b972a6bc \ No newline at end of file +ae088cbc968a565c3e0a8dd74ce150cac4a87978b593a3204f475fa196f1603c \ No newline at end of file diff --git a/src/prepare.c b/src/prepare.c index 4499e9a413..7e08a5a674 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -660,6 +660,7 @@ void sqlite3ParseObjectInit(Parse *pParse, sqlite3 *db){ pParse->pOuterParse = db->pParse; db->pParse = pParse; pParse->db = db; + if( db->mallocFailed ) sqlite3ErrorMsg(pParse, "out of memory"); } /* @@ -686,7 +687,7 @@ static int sqlite3Prepare( sParse.db = db; sParse.pReprepare = pReprepare; assert( ppStmt && *ppStmt==0 ); - /* assert( !db->mallocFailed ); // not true with SQLITE_USE_ALLOCA */ + if( db->mallocFailed ) sqlite3ErrorMsg(&sParse, "out of memory"); assert( sqlite3_mutex_held(db->mutex) ); /* For a long-term use prepared statement avoid the use of From f6f7888622e01e0d5326a82d17d42f682d3a5ae8 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 28 Jan 2022 23:19:01 +0000 Subject: [PATCH 09/40] Add a NEVER() on an unreachable branch. FossilOrigin-Name: 388926254b9da6169da7267bd4d5a1a66a26576be435c88269ab8db9eaedc079 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/whereexpr.c | 3 ++- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 5ac5027c9a..ac1e0268cc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Include\shidden\scolumns\sof\stable\svalued\sfunctions\sthat\sare\sconstrained\nby\sthe\sfunction\sarguments\sin\sthe\scolUsed\sbitmask. -D 2022-01-28T19:53:37.840 +C Add\sa\sNEVER()\son\san\sunreachable\sbranch. +D 2022-01-28T23:19:01.285 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -642,7 +642,7 @@ F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b F src/where.c f61ca6dbb4bbb7171aa5f5459982d8d98aac24852572bcee250f9c469a1963ba F src/whereInt.h 0748a6fce98b41862445906922a809146ff7ef4de16ed9022b0bc4e5c43aa60a F src/wherecode.c c313ccf5ed13dc7e88c64f93733f414dee369a212508a866878696d83c64fc36 -F src/whereexpr.c 42195849443c3648fa9900c82837b7592c91bd43934ad9369d30f5d55d7fe19e +F src/whereexpr.c becee225f104abd0253e0ceecd2a6253589f269395d98fc3ff98fdd64fecb34c F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P c4c30df7cd858315359bd9bfa90a07152d87a2301eaec865c5bc1c65e272eec8 -R b21847307144bd6983c5bb4141c2b106 +P 8f8cab0d0e219f0c4bc2b8061c0013d90a2a5e598dd6cf420d302dfd1b7cbe11 +R 2886f1ce9cf06aa83d320b384893e940 U drh -Z c180a25bc067b4731882777cea55422e +Z 20c096da0533f9910b77ed2d57db0dcc # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1fe4679ade..ae1add637c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8f8cab0d0e219f0c4bc2b8061c0013d90a2a5e598dd6cf420d302dfd1b7cbe11 \ No newline at end of file +388926254b9da6169da7267bd4d5a1a66a26576be435c88269ab8db9eaedc079 \ No newline at end of file diff --git a/src/whereexpr.c b/src/whereexpr.c index e4ddf49328..45e0ea007c 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1615,7 +1615,8 @@ void sqlite3WhereAddLimit(WhereClause *pWC, Select *p){ if( pOrderBy ){ for(ii=0; iinExpr; ii++){ Expr *pExpr = pOrderBy->a[ii].pExpr; - if( pExpr->op!=TK_COLUMN || pExpr->iTable!=iCsr ) return; + if( pExpr->op!=TK_COLUMN ) return; + if( NEVER(pExpr->iTable!=iCsr) ) return; if( pOrderBy->a[ii].sortFlags & KEYINFO_ORDER_BIGNULL ) return; } } From 86c756015bc57a729ce2e3e188792d798d8747aa Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 29 Jan 2022 21:41:15 +0000 Subject: [PATCH 10/40] Documentation enhancements. No code changes. FossilOrigin-Name: 312642d982f7861fd4db416e5eb24d863535b3ade40539a32f2dfe3f6fc45d46 --- manifest | 13 +++-- manifest.uuid | 2 +- src/sqlite.h.in | 123 +++++++++++++++++++++++++++++++++--------------- 3 files changed, 92 insertions(+), 46 deletions(-) diff --git a/manifest b/manifest index 6f2af601ef..cd6efcc842 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\sSQLITE_INDEX_CONSTRAINT_LIMIT\sand\sSQLITE_INDEX_CONSTRAINT_OFFSET\nconstraints\sto\sthe\ssqlite3_index_info\sfor\sthe\sxBestIndex\smethod\sof\svirtual\ntables. -D 2022-01-28T23:44:27.760 +C Documentation\senhancements.\s\sNo\scode\schanges. +D 2022-01-29T21:41:15.826 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -554,7 +554,7 @@ F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2 F src/shell.c.in e80a140e92e342e2f92d405a77155c8e3a67c9b1d0bdbacb92885960cd4fc8f2 -F src/sqlite.h.in da0e94f140f3f054f0eca8fc4a183167bedc7ae9c51822390c62369455eae5f8 +F src/sqlite.h.in eaade58049152dac850d57415bcced885ca27ae9582f8aea2cfb7f1db78a521b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a F src/sqliteInt.h 8ef2996e02476f73e41ba977f819bda0cc68b7ce238cf404b9b8930df57bc1d0 @@ -1942,9 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P ae088cbc968a565c3e0a8dd74ce150cac4a87978b593a3204f475fa196f1603c 388926254b9da6169da7267bd4d5a1a66a26576be435c88269ab8db9eaedc079 -R 9225571e858504f378a02e825bc79ee6 -T +closed 388926254b9da6169da7267bd4d5a1a66a26576be435c88269ab8db9eaedc079 +P 1e227ad9f413227f767b45b91e5439b82c98a3368fb20643414dab5c0f4818c6 +R f13cee5dfcfac643ce5de584b78c3f4d U drh -Z b46b82d7c9b67bbb72c40f2f1d6b1914 +Z 04ccdcba53141070401957d8ba0c3d32 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 37b532e462..b4ed546f5b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1e227ad9f413227f767b45b91e5439b82c98a3368fb20643414dab5c0f4818c6 \ No newline at end of file +312642d982f7861fd4db416e5eb24d863535b3ade40539a32f2dfe3f6fc45d46 \ No newline at end of file diff --git a/src/sqlite.h.in b/src/sqlite.h.in index d549a1ba4b..82f844d6a7 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -4352,6 +4352,8 @@ int sqlite3_stmt_busy(sqlite3_stmt*); ** ** ^The sqlite3_value objects that are passed as parameters into the ** implementation of [application-defined SQL functions] are protected. +** ^The sqlite3_value objects returned by [sqlite3_vtab_rhs_value()] +** are protected. ** ^The sqlite3_value object returned by ** [sqlite3_column_value()] is unprotected. ** Unprotected sqlite3_value objects may only be used as arguments @@ -7131,26 +7133,56 @@ struct sqlite3_index_info { ** ** These macros define the allowed values for the ** [sqlite3_index_info].aConstraint[].op field. Each value represents -** an operator that is part of a constraint term in the wHERE clause of +** an operator that is part of a constraint term in the WHERE clause of ** a query that uses a [virtual table]. +** +** ^The left-hand operand of the operator is given by the corresponding +** aConstraint[].iColumn field. ^An iColumn of -1 indicates the left-hand +** operand is the rowid. +** The SQLITE_INDEX_CONSTRAINT_LIMIT and SQLITE_INDEX_CONSTRAINT_OFFSET +** operators have no left-hand operand, and so for those operators the +** corresponding aConstraint[].iColumn is meaningless and should not be +** used. +** +** All operator values from SQLITE_INDEX_CONSTRAINT_FUNCTION through +** value 255 are reserved to represent functions that are overloaded +** by the [xFindFunction|xFindFunction method] of the virtual table +** implementation. +** +** The right-hand operands for each constraint might be accessible using +** the [sqlite3_vtab_rhs_value()] interface. Usually the right-hand +** operand is only available if it appears as a single constant literal +** in the input SQL. If the right-hand operand is another column or an +** expression (even a constant expression) or a parameter, then the +** sqlite3_vtab_rhs_value() probably will not be able to extract it. +** ^The SQLITE_INDEX_CONSTRAINT_ISNULL and +** SQLITE_INDEX_CONSTRAINT_ISNOTNULL operators have no right-hand operand +** and hence calls to sqlite3_vtab_rhs_value() for those operators will +** always return SQLITE_NOTFOUND. +** +** The collating sequence to be used for comparison can be found using +** the [sqlite3_vtab_collation()] interface. For most real-world virtual +** tables, the collating sequence of constraints does not matter (for example +** because the constraints are numeric) and so the sqlite3_vtab_collation() +** interface is no commonly needed. */ -#define SQLITE_INDEX_CONSTRAINT_EQ 2 -#define SQLITE_INDEX_CONSTRAINT_GT 4 -#define SQLITE_INDEX_CONSTRAINT_LE 8 -#define SQLITE_INDEX_CONSTRAINT_LT 16 -#define SQLITE_INDEX_CONSTRAINT_GE 32 -#define SQLITE_INDEX_CONSTRAINT_MATCH 64 -#define SQLITE_INDEX_CONSTRAINT_LIKE 65 -#define SQLITE_INDEX_CONSTRAINT_GLOB 66 -#define SQLITE_INDEX_CONSTRAINT_REGEXP 67 -#define SQLITE_INDEX_CONSTRAINT_NE 68 -#define SQLITE_INDEX_CONSTRAINT_ISNOT 69 -#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 -#define SQLITE_INDEX_CONSTRAINT_ISNULL 71 -#define SQLITE_INDEX_CONSTRAINT_IS 72 -#define SQLITE_INDEX_CONSTRAINT_LIMIT 73 -#define SQLITE_INDEX_CONSTRAINT_OFFSET 74 -#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 +#define SQLITE_INDEX_CONSTRAINT_EQ 2 +#define SQLITE_INDEX_CONSTRAINT_GT 4 +#define SQLITE_INDEX_CONSTRAINT_LE 8 +#define SQLITE_INDEX_CONSTRAINT_LT 16 +#define SQLITE_INDEX_CONSTRAINT_GE 32 +#define SQLITE_INDEX_CONSTRAINT_MATCH 64 +#define SQLITE_INDEX_CONSTRAINT_LIKE 65 +#define SQLITE_INDEX_CONSTRAINT_GLOB 66 +#define SQLITE_INDEX_CONSTRAINT_REGEXP 67 +#define SQLITE_INDEX_CONSTRAINT_NE 68 +#define SQLITE_INDEX_CONSTRAINT_ISNOT 69 +#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 +#define SQLITE_INDEX_CONSTRAINT_ISNULL 71 +#define SQLITE_INDEX_CONSTRAINT_IS 72 +#define SQLITE_INDEX_CONSTRAINT_LIMIT 73 +#define SQLITE_INDEX_CONSTRAINT_OFFSET 74 +#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 /* ** CAPI3REF: Register A Virtual Table Implementation @@ -9510,15 +9542,16 @@ SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int); ** CAPI3REF: Determine if a virtual table query is DISTINCT ** METHOD: sqlite3_index_info ** -** This API may only be used from within an xBestIndex() callback. The -** results of calling it from outside of an xBestIndex() callback are -** undefined and probably harmful. +** This API may only be used from within an [xBestIndex|xBestIndex method] +** of a [virtual table] implementation. The result of calling this +** interface from outside of xBestIndex() is undefined and probably harmful. ** -** ^The sqlite3_vtab_distinct() returns an integer that is either 0, 1, or -** 2. The integer returned by sqlite3_vtab_distinct() gives the virtual table -** additional information about how the query planner wants the output to be -** ordered. As long as the virtual table can meet the ordering requirements -** of the query planner, it may set the "orderByConsumed" flag. +** ^The sqlite3_vtab_distinct() interface returns an integer that is +** either 0, 1, or 2. The integer returned by sqlite3_vtab_distinct() +** gives the virtual table additional information about how the query +** planner wants the output to be ordered. As long as the virtual table +** can meet the ordering requirements of the query planner, it may set +** the "orderByConsumed" flag. ** **
  1. ** ^If the sqlite3_vtab_distinct() interface returns 0, that means @@ -9527,7 +9560,7 @@ SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int); ** [sqlite3_index_info] object. This is the default expectation. If the ** virtual table outputs all rows in sorted order, then it is always safe for ** the xBestIndex method to set the "orderByConsumed" flag, regardless of -** what the return value from sqlite3_vtab_distinct(). +** the return value from sqlite3_vtab_distinct(). **

  2. ** ^(If the sqlite3_vtab_distinct() interface returns 1, that means ** that the query planner does not need the rows to be returned in sorted order @@ -9540,7 +9573,7 @@ SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int); ** order, as long as rows with the same values in all "aOrderBy" columns ** are adjacent.)^ ^(Furthermore, only a single row for each particular ** combination of values in the columns identified by the "aOrderBy" field -** needs to be returned.)^ ^It is ok always for two or more rows with the same +** needs to be returned.)^ ^It is always ok for two or more rows with the same ** values in all "aOrderBy" columns to be returned, as long as all such rows ** are adjacent. ^The virtual table may, if it chooses, omit extra rows ** that have the same value for all columns identified by "aOrderBy". @@ -9575,27 +9608,41 @@ int sqlite3_vtab_distinct(sqlite3_index_info*); ** CAPI3REF: Constraint values in xBestIndex() ** METHOD: sqlite3_index_info ** -** This API may only be used from within an xBestIndex() callback. The -** results of calling it from outside of an xBestIndex() callback are -** undefined and probably harmful. +** This API may only be used from within the [xBestIndex|xBestIndex method] +** of a [virtual table] implementation. The result of calling this interface +** from outside of an xBestIndex method are undefined and probably harmful. ** ** ^When the sqlite3_vtab_rhs_value(P,J,V) interface is invoked from within ** the [xBestIndex] method of a [virtual table] implementation, with P being ** a copy of the [sqlite3_index_info] object pointer passed into xBestIndex and ** J being a 0-based index into P->aConstraint[], then this routine -** attempts to set *V to be the value on the right-hand side of -** that constraint if the right-hand side is a known constant. ^If the -** right-hand side of the constraint is not known, then *V is set to a NULL -** pointer. ^The sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if +** attempts to set *V to the value of the right-hand operand of +** that constraint if the right-hand operand is known. ^If the +** right-hand operand is not known, then *V is set to a NULL pointer. +** ^The sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if ** and only if *V is set to a value. ^The sqlite3_vtab_rhs_value(P,J,V) ** inteface returns SQLITE_NOTFOUND if the right-hand side of the J-th ** constraint is not available. ^The sqlite3_vtab_rhs_value() interface ** can return an result code other than SQLITE_OK or SQLITE_NOTFOUND if ** something goes wrong. ** -** ^The sqlite3_value object returned in *V remains valid for the duration of -** the xBestIndex method code. ^When xBestIndex returns, the sqlite3_value -** object returned by sqlite3_vtab_rhs_value() is automatically deallocated. +** The sqlite3_vtab_rhs_value() interface is usually only successful if +** the right-hand operand of a constraint is a literal value in the original +** SQL statement. If the right-hand operand is an expression or a reference +** to some other column or a [host parameter], then sqlite3_vtab_rhs_value() +** will probably return [SQLITE_NOTFOUND]. +** +** ^(Some constraints, such as [SQLITE_INDEX_CONSTRAINT_ISNULL] and +** [SQLITE_INDEX_CONSTRAINT_ISNOTNULL], have no right-hand operand. For such +** constraints, sqlite3_vtab_rhs_value() always returns SQLITE_NOTFOUND.)^ +** +** ^The [sqlite3_value] object returned in *V is a protected sqlite3_value +** and remains valid for the duration of the xBestIndex method call. +** ^When xBestIndex returns, the sqlite3_value object returned by +** sqlite3_vtab_rhs_value() is automatically deallocated. +** +** The "_rhs_" in the name of this routine is an appreviation for +** "Right-Hand Side". */ int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal); From ddd166a350b64fe3c9a91a740c1e42286ef507ec Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 30 Jan 2022 11:42:56 +0000 Subject: [PATCH 11/40] Remove a faulty NEVER(). Fix for dbsqlfuzz 4678cf825d27f87c9b8343720121e12cf944b71a FossilOrigin-Name: d4e402458dd4cadb623a30158eb9ff5f24f011240b3b1bc5b1d6ae3c5b855892 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/whereexpr.c | 2 +- test/json101.test | 13 ++++++++----- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/manifest b/manifest index cd6efcc842..592652eb77 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Documentation\senhancements.\s\sNo\scode\schanges. -D 2022-01-29T21:41:15.826 +C Remove\sa\sfaulty\sNEVER().\s\sFix\sfor\sdbsqlfuzz\s4678cf825d27f87c9b8343720121e12cf944b71a +D 2022-01-30T11:42:56.029 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -642,7 +642,7 @@ F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b F src/where.c f61ca6dbb4bbb7171aa5f5459982d8d98aac24852572bcee250f9c469a1963ba F src/whereInt.h 0748a6fce98b41862445906922a809146ff7ef4de16ed9022b0bc4e5c43aa60a F src/wherecode.c c313ccf5ed13dc7e88c64f93733f414dee369a212508a866878696d83c64fc36 -F src/whereexpr.c becee225f104abd0253e0ceecd2a6253589f269395d98fc3ff98fdd64fecb34c +F src/whereexpr.c ddb6ab49f745154c37dbdb291433c933e00175929647290a11f487af701d0392 F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 @@ -1149,7 +1149,7 @@ F test/journal3.test 7c3cf23ffc77db06601c1fcfc9743de8441cb77db9d1aa931863d94f5ff F test/jrnlmode.test 9b5bc01dac22223cb60ec2d5f97acf568d73820794386de5634dcadbea9e1946 F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd724363d F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa -F test/json101.test bb71538005f2d9e18620bdd3b76839a93ca0be61903eb8d751a64e78cf99b8fb +F test/json101.test d7c84854acafaf80f883e183ac4248ea2742615086c94a61a46ad7d7382ce123 F test/json102.test 86edc7d283085addff8593b178997e75875530d1385f5926717543d3475e6b01 F test/json103.test aff6b7a4c17d5a20b487a7bc1a274bfdc63b829413bdfb83bedac42ec7f67e3b F test/json104.test 2cb7ff2cca2c8214d3e5260eeb9ce45faec0926f68b3e40c1aaa6ca247284144 @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 1e227ad9f413227f767b45b91e5439b82c98a3368fb20643414dab5c0f4818c6 -R f13cee5dfcfac643ce5de584b78c3f4d +P 312642d982f7861fd4db416e5eb24d863535b3ade40539a32f2dfe3f6fc45d46 +R e850a6aba3d4d0d771274e3608e2198b U drh -Z 04ccdcba53141070401957d8ba0c3d32 +Z 4d3a7d78a0c2943263a9b80fe942bb50 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b4ed546f5b..0c64cdda9d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -312642d982f7861fd4db416e5eb24d863535b3ade40539a32f2dfe3f6fc45d46 \ No newline at end of file +d4e402458dd4cadb623a30158eb9ff5f24f011240b3b1bc5b1d6ae3c5b855892 \ No newline at end of file diff --git a/src/whereexpr.c b/src/whereexpr.c index 45e0ea007c..f54729767a 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1616,7 +1616,7 @@ void sqlite3WhereAddLimit(WhereClause *pWC, Select *p){ for(ii=0; iinExpr; ii++){ Expr *pExpr = pOrderBy->a[ii].pExpr; if( pExpr->op!=TK_COLUMN ) return; - if( NEVER(pExpr->iTable!=iCsr) ) return; + if( pExpr->iTable!=iCsr ) return; if( pOrderBy->a[ii].sortFlags & KEYINFO_ORDER_BIGNULL ) return; } } diff --git a/test/json101.test b/test/json101.test index 0d59f2e83d..1845279179 100644 --- a/test/json101.test +++ b/test/json101.test @@ -15,11 +15,6 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -ifcapable !json1 { - finish_test - return -} - do_execsql_test json101-1.1.00 { SELECT json_array(1,2.5,null,'hello'); } {[1,2.5,null,"hello"]} @@ -846,5 +841,13 @@ do_execsql_test json-16.30 { SELECT unicode(json_extract('"\uD834\uDD1E"','$')); } {119070} +# 2022-01-30 dbsqlfuzz 4678cf825d27f87c9b8343720121e12cf944b71a +do_execsql_test json-17.1 { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + CREATE TABLE t1(a,b,c); + CREATE TABLE t2(d); + SELECT * FROM t1 LEFT JOIN t2 ON (SELECT b FROM json_each ORDER BY 1); +} {} finish_test From e229ca03077f30caccd47a72d22928168aa7ef46 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 30 Jan 2022 11:44:17 +0000 Subject: [PATCH 12/40] Always enable all JSON tests, now that JSON is included by default. FossilOrigin-Name: 8c9f350182140604a71e11d226acb3f80d1f2b4f75e5c3b55ec8f8a7c02941f3 --- manifest | 18 +++++++++--------- manifest.uuid | 2 +- test/json102.test | 5 ----- test/json103.test | 5 ----- test/json104.test | 5 ----- test/json105.test | 5 ----- 6 files changed, 10 insertions(+), 30 deletions(-) diff --git a/manifest b/manifest index 592652eb77..e41788870f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sa\sfaulty\sNEVER().\s\sFix\sfor\sdbsqlfuzz\s4678cf825d27f87c9b8343720121e12cf944b71a -D 2022-01-30T11:42:56.029 +C Always\senable\sall\sJSON\stests,\snow\sthat\sJSON\sis\sincluded\sby\sdefault. +D 2022-01-30T11:44:17.962 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1150,10 +1150,10 @@ F test/jrnlmode.test 9b5bc01dac22223cb60ec2d5f97acf568d73820794386de5634dcadbea9 F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd724363d F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa F test/json101.test d7c84854acafaf80f883e183ac4248ea2742615086c94a61a46ad7d7382ce123 -F test/json102.test 86edc7d283085addff8593b178997e75875530d1385f5926717543d3475e6b01 -F test/json103.test aff6b7a4c17d5a20b487a7bc1a274bfdc63b829413bdfb83bedac42ec7f67e3b -F test/json104.test 2cb7ff2cca2c8214d3e5260eeb9ce45faec0926f68b3e40c1aaa6ca247284144 -F test/json105.test 45f7d6a9a54c85f8a9589b68d3e7a1f42d02f2359911a8cdbad1f9988f571173 +F test/json102.test 327e77275f338c028faefa2da5164daf6b142a165e3015ff2a6e4251ddc6a0ac +F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe +F test/json104.test a502dc01853aada95d721b3b275afbe2dc18fffdac1fea6e96fb20c13586bbb5 +F test/json105.test 11670a4387f4308ae0318cadcbd6a918ea7edcd19fbafde020720a073952675d F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff F test/kvtest.c feb4358fb022da8ebd098c45811f2f6507688bb6c43aa72b3e840df19026317b F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63 @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 312642d982f7861fd4db416e5eb24d863535b3ade40539a32f2dfe3f6fc45d46 -R e850a6aba3d4d0d771274e3608e2198b +P d4e402458dd4cadb623a30158eb9ff5f24f011240b3b1bc5b1d6ae3c5b855892 +R c6388bc98f6a84d7097f143004850d0c U drh -Z 4d3a7d78a0c2943263a9b80fe942bb50 +Z 103c157b43e90dd4721cb0f6fef6c37e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0c64cdda9d..5316c2c8e7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d4e402458dd4cadb623a30158eb9ff5f24f011240b3b1bc5b1d6ae3c5b855892 \ No newline at end of file +8c9f350182140604a71e11d226acb3f80d1f2b4f75e5c3b55ec8f8a7c02941f3 \ No newline at end of file diff --git a/test/json102.test b/test/json102.test index 9f8f482b85..f551c4b823 100644 --- a/test/json102.test +++ b/test/json102.test @@ -18,11 +18,6 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -ifcapable !json1 { - finish_test - return -} - do_execsql_test json102-100 { SELECT json_object('ex','[52,3.14159]'); } {{{"ex":"[52,3.14159]"}}} diff --git a/test/json103.test b/test/json103.test index 35580ce4ea..e7483073b6 100644 --- a/test/json103.test +++ b/test/json103.test @@ -14,11 +14,6 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -ifcapable !json1 { - finish_test - return -} - do_execsql_test json103-100 { CREATE TABLE t1(a,b,c); WITH RECURSIVE c(x) AS (VALUES(1) UNION SELECT x+1 FROM c WHERE x<100) diff --git a/test/json104.test b/test/json104.test index 8d95e60140..56dd2738c3 100644 --- a/test/json104.test +++ b/test/json104.test @@ -15,11 +15,6 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix json104 -ifcapable !json1 { - finish_test - return -} - # This is the example from pages 2 and 3 of RFC-7396 do_execsql_test json104-100 { SELECT json_patch('{ diff --git a/test/json105.test b/test/json105.test index 46d36774f7..83face188e 100644 --- a/test/json105.test +++ b/test/json105.test @@ -15,11 +15,6 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix json104 -ifcapable !json1 { - finish_test - return -} - # This is the example from pages 2 and 3 of RFC-7396 db eval { CREATE TABLE t1(j); From 09a39ed52285778b12123b3889b429250a36d100 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 30 Jan 2022 21:09:03 +0000 Subject: [PATCH 13/40] CLI: For columnar modes ("box", "column", "table", "markdown") the ".width" is now both the minimum and maximum width of the column. Text that spans multiple lines or that contains tabs is properly formatted. If any part of the output contains multi-line text, then extra separators are provided between each row. FossilOrigin-Name: c10ed4a7fe33fd9330967ab714b1f09ad57c972997160dfc71477e43b905f69c --- manifest | 12 ++--- manifest.uuid | 2 +- src/shell.c.in | 133 +++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 125 insertions(+), 22 deletions(-) diff --git a/manifest b/manifest index e41788870f..af137deae4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Always\senable\sall\sJSON\stests,\snow\sthat\sJSON\sis\sincluded\sby\sdefault. -D 2022-01-30T11:44:17.962 +C CLI:\sFor\scolumnar\smodes\s("box",\s"column",\s"table",\s"markdown")\sthe\s".width"\nis\snow\sboth\sthe\sminimum\sand\smaximum\swidth\sof\sthe\scolumn.\s\sText\sthat\sspans\nmultiple\slines\sor\sthat\scontains\stabs\sis\sproperly\sformatted.\s\sIf\sany\spart\sof\nthe\soutput\scontains\smulti-line\stext,\sthen\sextra\sseparators\sare\sprovided\sbetween\neach\srow. +D 2022-01-30T21:09:03.044 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -553,7 +553,7 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2 -F src/shell.c.in e80a140e92e342e2f92d405a77155c8e3a67c9b1d0bdbacb92885960cd4fc8f2 +F src/shell.c.in b33ead0e7e22264d229ed7a5aa61a65c4564fd4ef21ff0321ff7b6f02ccf6c9b F src/sqlite.h.in eaade58049152dac850d57415bcced885ca27ae9582f8aea2cfb7f1db78a521b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P d4e402458dd4cadb623a30158eb9ff5f24f011240b3b1bc5b1d6ae3c5b855892 -R c6388bc98f6a84d7097f143004850d0c +P 8c9f350182140604a71e11d226acb3f80d1f2b4f75e5c3b55ec8f8a7c02941f3 +R 22a2c9cbde362213afbf92ae8fdaba40 U drh -Z 103c157b43e90dd4721cb0f6fef6c37e +Z 0aba3faa9f23b01df6c28283e34cf7d3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5316c2c8e7..39dcc4d130 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8c9f350182140604a71e11d226acb3f80d1f2b4f75e5c3b55ec8f8a7c02941f3 \ No newline at end of file +c10ed4a7fe33fd9330967ab714b1f09ad57c972997160dfc71477e43b905f69c \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 0a903672cc..7e1f504a95 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -3164,6 +3164,75 @@ static void print_box_row_separator( fputs("\n", p->out); } +/* +** z[] is a line of text that is to be displayed the .mode box or table or +** similar tabular formats. z[] might contain control characters such +** as \n, \t, \f, or \r. +** +** Compute characters to display on the first line of z[]. Stop at the +** first \r, \n, or \f. Expand \t into spaces. Return a copy (obtained +** from malloc()) of that first line. Write anything to display +** on the next line into *pzTail. If this is the last line, write a NULL +** into *pzTail. +*/ +static char *translateForDisplayAndDup( + const unsigned char *z, + const unsigned char **pzTail, + int mxWidth +){ + int i, n; + unsigned char *zOut; + if( z==0 ){ + *pzTail = 0; + return 0; + } + if( mxWidth<0 ) mxWidth = -mxWidth; + if( mxWidth==0 ) mxWidth = 1000000; + i = n = 0; + while( n=' ' ){ + n++; + i++; + continue; + } + if( z[i]=='\t' ){ + n += 8; + n &= ~7; + i++; + continue; + } + break; + } + if( n>=mxWidth && z[i]>=' ' ){ + *pzTail = &z[i]; + }else if( z[i]=='\r' && z[i+1]=='\n' ){ + *pzTail = z[i+2] ? &z[i+2] : 0; + }else if( z[i]==0 ){ + *pzTail = 0; + }else{ + *pzTail = &z[i+1]; + } + zOut = malloc( n+1 ); + shell_check_oom(zOut); + i = n = 0; + while( n=' ' ){ + zOut[n++] = z[i++]; + continue; + } + if( z[i]=='\t' ){ + do{ + zOut[n++] = ' '; + }while( (n&7)!=0 && n= nAlloc ){ - nAlloc *= 2; - azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*)); - shell_check_oom(azData); - } - nRow++; - for(i=0; ip->nWidth ){ p->colWidth = realloc(p->colWidth, (nColumn+1)*2*sizeof(int)); shell_check_oom(p->colWidth); @@ -3226,6 +3290,36 @@ static void exec_prepared_stmt_columnar( if( w<0 ) w = -w; p->actualWidth[i] = w; } + for(i=0; i= nAlloc ){ + nAlloc *= 2; + azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*)); + shell_check_oom(azData); + abRowDiv = sqlite3_realloc64(abRowDiv, nAlloc/nColumn); + shell_check_oom(abRowDiv); + } + abRowDiv[nRow] = 1; + nRow++; + for(i=0; icolWidth[i]); + if( azNextLine[i] ){ + bNextLine = 1; + abRowDiv[nRow-1] = 0; + bMultiLineRowExists = 1; + } + } + }while( bNextLine || sqlite3_step(pStmt)==SQLITE_ROW ); nTotal = nColumn*(nRow+1); for(i=0; iout, w, z); if( j==nColumn-1 ){ utf8_printf(p->out, "%s", rowSep); + if( bMultiLineRowExists && abRowDiv[i/nColumn-1] && i+1cMode==MODE_Table ){ + print_row_separator(p, nColumn, "+"); + }else if( p->cMode==MODE_Box ){ + print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); + } + } j = -1; if( seenInterrupt ) goto columnar_end; }else{ @@ -3326,6 +3427,8 @@ columnar_end: nData = (nRow+1)*nColumn; for(i=0; i Date: Mon, 31 Jan 2022 10:55:50 +0000 Subject: [PATCH 14/40] CLI: Fix the new ".mode box" output so that it works with unicode characters. FossilOrigin-Name: 454af48724c78bf8e06379a426e01b1597f56fcc83211ee062ff0dbeb6a140b1 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/shell.c.in | 24 +++++++++++++++--------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index af137deae4..0d0679fac4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C CLI:\sFor\scolumnar\smodes\s("box",\s"column",\s"table",\s"markdown")\sthe\s".width"\nis\snow\sboth\sthe\sminimum\sand\smaximum\swidth\sof\sthe\scolumn.\s\sText\sthat\sspans\nmultiple\slines\sor\sthat\scontains\stabs\sis\sproperly\sformatted.\s\sIf\sany\spart\sof\nthe\soutput\scontains\smulti-line\stext,\sthen\sextra\sseparators\sare\sprovided\sbetween\neach\srow. -D 2022-01-30T21:09:03.044 +C CLI:\sFix\sthe\snew\s".mode\sbox"\soutput\sso\sthat\sit\sworks\swith\sunicode\scharacters. +D 2022-01-31T10:55:50.702 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -553,7 +553,7 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2 -F src/shell.c.in b33ead0e7e22264d229ed7a5aa61a65c4564fd4ef21ff0321ff7b6f02ccf6c9b +F src/shell.c.in c0fa21db3a4b0f6204b5cc6b14eb452933f5588ae0b4840144380ebd876e8e90 F src/sqlite.h.in eaade58049152dac850d57415bcced885ca27ae9582f8aea2cfb7f1db78a521b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 8c9f350182140604a71e11d226acb3f80d1f2b4f75e5c3b55ec8f8a7c02941f3 -R 22a2c9cbde362213afbf92ae8fdaba40 +P c10ed4a7fe33fd9330967ab714b1f09ad57c972997160dfc71477e43b905f69c +R 8e4ffa89b298574bd8cb466fdf156b79 U drh -Z 0aba3faa9f23b01df6c28283e34cf7d3 +Z 789ef5c7438d14872b9f52b0dc5ffba5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 39dcc4d130..5355010056 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c10ed4a7fe33fd9330967ab714b1f09ad57c972997160dfc71477e43b905f69c \ No newline at end of file +454af48724c78bf8e06379a426e01b1597f56fcc83211ee062ff0dbeb6a140b1 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 7e1f504a95..73dee2dc8e 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -3180,7 +3180,7 @@ static char *translateForDisplayAndDup( const unsigned char **pzTail, int mxWidth ){ - int i, n; + int i, j, n; unsigned char *zOut; if( z==0 ){ *pzTail = 0; @@ -3188,14 +3188,18 @@ static char *translateForDisplayAndDup( } if( mxWidth<0 ) mxWidth = -mxWidth; if( mxWidth==0 ) mxWidth = 1000000; - i = n = 0; + i = j= n = 0; while( n=' ' ){ n++; - i++; + do{ i++; j++; }while( (z[i]&0xc0)==0x80 ); continue; } if( z[i]=='\t' ){ + do{ + n++; + j++; + }while( (n&7)!=0 && n=' ' ){ - zOut[n++] = z[i++]; + n++; + do{ zOut[j++] = z[i++]; }while( (z[i]&0xc0)==0x80 ); continue; } if( z[i]=='\t' ){ do{ - zOut[n++] = ' '; + n++; + zOut[j++] = ' '; }while( (n&7)!=0 && n Date: Mon, 31 Jan 2022 12:29:14 +0000 Subject: [PATCH 15/40] Fix an obscure problem in sqlite3_backup_init() caused by [6a45d8fe8bfbc11a|check-in 6a45d8fe8bfbc11a]. See [forum:/forumpost/8b39fbf3e7b5c278|forum post 8b39fbf3e7] for the original bug report. FossilOrigin-Name: 639fc7633bd740421d6b48617b9f68e6be525107e4049673fe720ea6158a393f --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/backup.c | 2 +- test/backup.test | 9 +++++++++ 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 0d0679fac4..920122a823 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C CLI:\sFix\sthe\snew\s".mode\sbox"\soutput\sso\sthat\sit\sworks\swith\sunicode\scharacters. -D 2022-01-31T10:55:50.702 +C Fix\san\sobscure\sproblem\sin\ssqlite3_backup_init()\scaused\sby\n[6a45d8fe8bfbc11a|check-in\s6a45d8fe8bfbc11a].\s\sSee\n[forum:/forumpost/8b39fbf3e7b5c278|forum\spost\s8b39fbf3e7]\sfor\nthe\soriginal\sbug\sreport. +D 2022-01-31T12:29:14.283 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -489,7 +489,7 @@ F src/alter.c e8ac1df663bf4ec74920edd1299435f2a616d2404de0ac4013c151ea4e7a11f2 F src/analyze.c 7518b99e07c5494111fe3bd867f28f804b6c5c1ad0703ec3d116de9bab3fa516 F src/attach.c f26d400f3ffe2cdca01406bca70e5f58c5488bf165b4fc37c228136dfcf1b583 F src/auth.c f4fa91b6a90bbc8e0d0f738aa284551739c9543a367071f55574681e0f24f8cf -F src/backup.c 58880b9a9adf88f1a57cb3b0db6b891626ae76113ebd0f2417a87c2634edfc65 +F src/backup.c a2891172438e385fdbe97c11c9745676bec54f518d4447090af97189fd8e52d7 F src/bitvec.c 7c849aac407230278445cb069bebc5f89bf2ddd87c5ed9459b070a9175707b3d F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6 F src/btree.c ddab31c38d5f16114bc68392430556b1063fe14e0020f9a56d2c35ddd58ba7e3 @@ -715,7 +715,7 @@ F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4 F test/avfs.test 0c3a38e03cccb0fc3127838462dc05dc3f4c1480d770c084b388304c25de3652 F test/avtrans.test b7dc25459ecbd86c6fa9c606ee3068f59d81e225118617dcf2bbb6ded2ade89e F test/backcompat.test 3e64cedda754c778ef6bbe417b6e7a295e662a4d -F test/backup.test dd4a5ff756e3df3931dacb1791db0584d4bad989 +F test/backup.test fc1ecefce723fad5199b55cec7a5a992ec8c3ad6873419e5e8919066dec457f3 F test/backup2.test 8facb54df1388419d34b362ab1f7e233310ff3a3af64e8ad5ec47ba3c2bbe5cf F test/backup4.test 8f6fd48e0dfde77b9a3bb26dc471ede3e101df32 F test/backup5.test ee5da6d7fe5082f5b9b0bbfa31d016f52412a2e4 @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P c10ed4a7fe33fd9330967ab714b1f09ad57c972997160dfc71477e43b905f69c -R 8e4ffa89b298574bd8cb466fdf156b79 +P 454af48724c78bf8e06379a426e01b1597f56fcc83211ee062ff0dbeb6a140b1 +R 20eccfa39f79364ced00d16db0de3987 U drh -Z 789ef5c7438d14872b9f52b0dc5ffba5 +Z 97bbf817faea1d9cef9cd8d644f59252 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5355010056..cff7a176f1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -454af48724c78bf8e06379a426e01b1597f56fcc83211ee062ff0dbeb6a140b1 \ No newline at end of file +639fc7633bd740421d6b48617b9f68e6be525107e4049673fe720ea6158a393f \ No newline at end of file diff --git a/src/backup.c b/src/backup.c index bfd155bcac..d9671750f4 100644 --- a/src/backup.c +++ b/src/backup.c @@ -85,7 +85,7 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){ if( i==1 ){ Parse sParse; int rc = 0; - sqlite3ParseObjectInit(&sParse,pErrorDb); + sqlite3ParseObjectInit(&sParse,pDb); if( sqlite3OpenTempDatabase(&sParse) ){ sqlite3ErrorWithMsg(pErrorDb, sParse.rc, "%s", sParse.zErrMsg); rc = SQLITE_ERROR; diff --git a/test/backup.test b/test/backup.test index 829b32452d..1572ded554 100644 --- a/test/backup.test +++ b/test/backup.test @@ -969,4 +969,13 @@ foreach {tn file rc} { db2 close } +# 2021-01-31 https://sqlite.org/forum/forumpost/8b39fbf3e7 +# +do_test backup-11.1 { + sqlite3 db1 :memory: + sqlite3 db2 :memory: + sqlite3_backup B db1 main db2 temp + B finish +} {SQLITE_OK} + finish_test From e40f28618cd00332ef9de8e674f06577c35f2d3a Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 31 Jan 2022 14:14:29 +0000 Subject: [PATCH 16/40] CLI: Enhancements to columnar output modes. (See [forum/forumpost/5b53cd851f66441f9|forum post 5b53cd851] for discussion.) New output mode "qbox". New output options "--quote" and "--wrap N". FossilOrigin-Name: 539cef5214446a7181614793e9cf323e95ba00ba0f888585b14b598dd2ff0808 --- manifest | 14 ++-- manifest.uuid | 2 +- src/shell.c.in | 197 +++++++++++++++++++++++++++++++++++++---------- test/shell1.test | 19 +---- 4 files changed, 167 insertions(+), 65 deletions(-) diff --git a/manifest b/manifest index 920122a823..ab413a27cf 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\sobscure\sproblem\sin\ssqlite3_backup_init()\scaused\sby\n[6a45d8fe8bfbc11a|check-in\s6a45d8fe8bfbc11a].\s\sSee\n[forum:/forumpost/8b39fbf3e7b5c278|forum\spost\s8b39fbf3e7]\sfor\nthe\soriginal\sbug\sreport. -D 2022-01-31T12:29:14.283 +C CLI:\sEnhancements\sto\scolumnar\soutput\smodes.\s\s(See\n[forum/forumpost/5b53cd851f66441f9|forum\spost\s5b53cd851]\sfor\sdiscussion.)\nNew\soutput\smode\s"qbox".\s\sNew\soutput\soptions\s"--quote"\sand\s"--wrap\sN". +D 2022-01-31T14:14:29.581 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -553,7 +553,7 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2 -F src/shell.c.in c0fa21db3a4b0f6204b5cc6b14eb452933f5588ae0b4840144380ebd876e8e90 +F src/shell.c.in d53f77fd4733a6cada8e0d2eb2936fcae3fbe885ac08e48d1e525f22206bf579 F src/sqlite.h.in eaade58049152dac850d57415bcced885ca27ae9582f8aea2cfb7f1db78a521b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a @@ -1386,7 +1386,7 @@ F test/sharedA.test 49d87ec54ab640fbbc3786ee3c01de94aaa482a3a9f834ad3fe92770eb69 F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304 -F test/shell1.test 70f46b5d07776a107335c3c2c9cbd0431d44637bfeae1f6b9ded5e33b4c7c0bf +F test/shell1.test ce2f370886645f38fabdde44976c14a004400f166edea8fdd9741079b645fef6 F test/shell2.test f00a0501c00583cbc46f7510e1d713366326b2b3e63d06d15937284171a8787c F test/shell3.test cb4b835a901742c9719437a89171172ecc4a8823ad97349af8e4e841e6f82566 F test/shell4.test 8f6c0fce4abed19a8a7f7262517149812a04caa905d01bdc8f5e92573504b759 @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 454af48724c78bf8e06379a426e01b1597f56fcc83211ee062ff0dbeb6a140b1 -R 20eccfa39f79364ced00d16db0de3987 +P 639fc7633bd740421d6b48617b9f68e6be525107e4049673fe720ea6158a393f +R fd24c84f38ee6d8d42b7ffa22962306f U drh -Z 97bbf817faea1d9cef9cd8d644f59252 +Z 929d8043c6f32e2330ec9eb4d8badd53 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index cff7a176f1..b8928a0374 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -639fc7633bd740421d6b48617b9f68e6be525107e4049673fe720ea6158a393f \ No newline at end of file +539cef5214446a7181614793e9cf323e95ba00ba0f888585b14b598dd2ff0808 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 73dee2dc8e..314adb9a11 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -1084,6 +1084,8 @@ struct ShellState { u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ u8 bSafeMode; /* True to prohibit unsafe operations */ u8 bSafeModePersist; /* The long-term value of bSafeMode */ + u8 bQuote; /* Quote results for .mode box and table */ + int iWrap; /* Wrap lines this long or longer in some output modes */ unsigned statsOn; /* True to display memory stats before each finalize */ unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */ int inputNesting; /* Track nesting level of .read and other redirects */ @@ -3239,7 +3241,37 @@ static char *translateForDisplayAndDup( return (char*)zOut; } - +/* Extract the value of the i-th current column for pStmt as an SQL literal +** value. Memory is obtained from sqlite3_malloc64() and must be freed by +** the caller. +*/ +static char *quoted_column(sqlite3_stmt *pStmt, int i){ + switch( sqlite3_column_type(pStmt, i) ){ + case SQLITE_NULL: { + return sqlite3_mprintf("NULL"); + } + case SQLITE_INTEGER: + case SQLITE_FLOAT: { + return sqlite3_mprintf("%s",sqlite3_column_text(pStmt,i)); + } + case SQLITE_TEXT: { + return sqlite3_mprintf("%Q",sqlite3_column_text(pStmt,i)); + } + case SQLITE_BLOB: { + int j; + sqlite3_str *pStr = sqlite3_str_new(0); + const unsigned char *a = sqlite3_column_blob(pStmt,i); + int n = sqlite3_column_bytes(pStmt,i); + sqlite3_str_append(pStr, "x'", 2); + for(j=0; jbQuote ){ + azQuoted = sqlite3_malloc64( nColumn*sizeof(char*) ); + shell_check_oom(azQuoted); + memset(azQuoted, 0, nColumn*sizeof(char*) ); + } abRowDiv = sqlite3_malloc64( nAlloc/nColumn ); shell_check_oom(abRowDiv); if( nColumn>p->nWidth ){ @@ -3312,13 +3350,18 @@ static void exec_prepared_stmt_columnar( abRowDiv[nRow] = 1; nRow++; for(i=0; icolWidth[i]; + if( w==0 ) w = p->iWrap; if( useNextLine ){ uz = azNextLine[i]; + }else if( p->bQuote ){ + sqlite3_free(azQuoted[i]); + azQuoted[i] = quoted_column(pStmt,i); + uz = (const unsigned char*)azQuoted[i]; }else{ uz = (const unsigned char*)sqlite3_column_text(pStmt,i); } - azData[nRow*nColumn + i] = - translateForDisplayAndDup(uz, &azNextLine[i], p->colWidth[i]); + azData[nRow*nColumn + i] = translateForDisplayAndDup(uz, &azNextLine[i], w); if( azNextLine[i] ){ bNextLine = 1; abRowDiv[nRow-1] = 0; @@ -3435,6 +3478,10 @@ columnar_end: sqlite3_free(azData); sqlite3_free(azNextLine); sqlite3_free(abRowDiv); + if( azQuoted ){ + for(i=0; i code", - " insert SQL insert statements for TABLE", - " json Results in a JSON array", - " line One value per line", - " list Values delimited by \"|\"", - " markdown Markdown table format", - " quote Escape answers as for SQL", - " table ASCII-art table", - " tabs Tab-separated values", - " tcl TCL list elements", + " ascii Columns/rows delimited by 0x1F and 0x1E", + " box Tables using unicode box-drawing characters", + " csv Comma-separated values", + " column Output in columns. (See .width)", + " html HTML code", + " insert SQL insert statements for TABLE", + " json Results in a JSON array", + " line One value per line", + " list Values delimited by \"|\"", + " markdown Markdown table format", + " qbox Shorthand for \"box --width 60 --quote\"", + " quote Escape answers as for SQL", + " table ASCII-art table", + " tabs Tab-separated values", + " tcl TCL list elements", + " OPTIONS: (value for columnar modes only):", + " --wrap N Wrap output lines longer than N character", + " --quote Quote output text as SQL literals", + " --noquote Do not quote output text", ".nonce STRING Disable safe mode for one command if the nonce matches", ".nullvalue STRING Use STRING in place of NULL values", ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", @@ -9008,64 +9060,118 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( c=='m' && strncmp(azArg[0], "mode", n)==0 ){ - const char *zMode = nArg>=2 ? azArg[1] : ""; - int n2 = strlen30(zMode); - int c2 = zMode[0]; - if( c2=='l' && n2>2 && strncmp(azArg[1],"lines",n2)==0 ){ + const char *zMode = 0; + const char *zTabname = 0; + int i, n2; + int bQuoteChng = 0; + int bWrapChng = 0; + for(i=1; iiWrap = integerValue(azArg[++i]); + bWrapChng = 1; + }else if( optionMatch(z,"quote") ){ + p->bQuote = 1; + bQuoteChng = 1; + }else if( optionMatch(z,"noquote") ){ + p->bQuote = 0; + bQuoteChng = 1; + }else if( zMode==0 ){ + zMode = z; + }else if( zTabname==0 ){ + zTabname = z; + }else if( z[0]=='-' ){ + utf8_printf(stderr, "unknown option: %s\n", z); + utf8_printf(stderr, "options:\n" + " --noquote\n" + " --quote\n" + " --wrap N\n"); + rc = 1; + goto meta_command_exit; + }else{ + utf8_printf(stderr, "extra argument: \"%s\"\n", z); + rc = 1; + goto meta_command_exit; + } + } + if( zMode==0 ){ + if( p->mode==MODE_Column + || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) + ){ + raw_printf(p->out, "current output mode: %s --wrap %d --%squote\n", + modeDescr[p->mode], p->iWrap, p->bQuote ? "" : "no"); + }else{ + raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]); + } + bWrapChng = bQuoteChng = 1; + zMode = modeDescr[p->mode]; + } + n2 = strlen30(zMode); + if( strncmp(zMode,"lines",n2)==0 ){ p->mode = MODE_Line; sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); - }else if( c2=='c' && strncmp(azArg[1],"columns",n2)==0 ){ + }else if( strncmp(zMode,"columns",n2)==0 ){ p->mode = MODE_Column; if( (p->shellFlgs & SHFLG_HeaderSet)==0 ){ p->showHeader = 1; } sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); - }else if( c2=='l' && n2>2 && strncmp(azArg[1],"list",n2)==0 ){ + if( !bWrapChng ) p->iWrap = 0; + if( !bQuoteChng ) p->bQuote = 0; + }else if( strncmp(zMode,"list",n2)==0 ){ p->mode = MODE_List; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Column); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); - }else if( c2=='h' && strncmp(azArg[1],"html",n2)==0 ){ + }else if( strncmp(zMode,"html",n2)==0 ){ p->mode = MODE_Html; - }else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){ + }else if( strncmp(zMode,"tcl",n2)==0 ){ p->mode = MODE_Tcl; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Space); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); - }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ + }else if( strncmp(zMode,"csv",n2)==0 ){ p->mode = MODE_Csv; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); - }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ + }else if( strncmp(zMode,"tabs",n2)==0 ){ p->mode = MODE_List; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab); - }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ + }else if( strncmp(zMode,"insert",n2)==0 ){ p->mode = MODE_Insert; - set_table_name(p, nArg>=3 ? azArg[2] : "table"); - }else if( c2=='q' && strncmp(azArg[1],"quote",n2)==0 ){ + set_table_name(p, zTabname ? zTabname : "table"); + }else if( strncmp(zMode,"quote",n2)==0 ){ p->mode = MODE_Quote; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); - }else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){ + }else if( strncmp(zMode,"ascii",n2)==0 ){ p->mode = MODE_Ascii; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record); - }else if( c2=='m' && strncmp(azArg[1],"markdown",n2)==0 ){ + }else if( strncmp(zMode,"markdown",n2)==0 ){ p->mode = MODE_Markdown; - }else if( c2=='t' && strncmp(azArg[1],"table",n2)==0 ){ + if( !bWrapChng ) p->iWrap = 0; + if( !bQuoteChng ) p->bQuote = 0; + }else if( strncmp(zMode,"table",n2)==0 ){ p->mode = MODE_Table; - }else if( c2=='b' && strncmp(azArg[1],"box",n2)==0 ){ + if( !bWrapChng ) p->iWrap = 0; + if( !bQuoteChng ) p->bQuote = 0; + }else if( strncmp(zMode,"box",n2)==0 ){ p->mode = MODE_Box; - }else if( c2=='c' && strncmp(azArg[1],"count",n2)==0 ){ + if( !bWrapChng ) p->iWrap = 0; + if( !bQuoteChng ) p->bQuote = 0; + }else if( strcmp(zMode,"qbox")==0 ){ + p->mode = MODE_Box; + if( !bWrapChng ) p->iWrap = 60; + if( !bQuoteChng ) p->bQuote = 1; + }else if( strncmp(zMode,"count",n2)==0 ){ p->mode = MODE_Count; - }else if( c2=='o' && strncmp(azArg[1],"off",n2)==0 ){ + }else if( strncmp(zMode,"off",n2)==0 ){ p->mode = MODE_Off; - }else if( c2=='j' && strncmp(azArg[1],"json",n2)==0 ){ + }else if( strncmp(zMode,"json",n2)==0 ){ p->mode = MODE_Json; - }else if( nArg==1 ){ - raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]); }else{ raw_printf(stderr, "Error: mode should be one of: " "ascii box column csv html insert json line list markdown " - "quote table tabs tcl\n"); + "qbox quote table tabs tcl\n"); rc = 1; } p->cMode = p->mode; @@ -10213,7 +10319,14 @@ static int do_meta_command(char *zLine, ShellState *p){ utf8_printf(p->out, "%12.12s: %s\n","explain", p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off"); utf8_printf(p->out,"%12.12s: %s\n","headers", azBool[p->showHeader!=0]); - utf8_printf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]); + if( p->mode==MODE_Column + || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) + ){ + utf8_printf(p->out, "%12.12s: %s --wrap %d --%squote\n", "mode", + modeDescr[p->mode], p->iWrap, p->bQuote ? "" : "no"); + }else{ + utf8_printf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]); + } utf8_printf(p->out, "%12.12s: ", "nullvalue"); output_c_string(p->out, p->nullValue); raw_printf(p->out, "\n"); diff --git a/test/shell1.test b/test/shell1.test index 75118b9419..745990a7c9 100644 --- a/test/shell1.test +++ b/test/shell1.test @@ -205,10 +205,10 @@ do_test shell1-2.2.4 { } {0 {}} do_test shell1-2.2.5 { catchcmd "test.db" ".mode \"insert FOO" -} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}} do_test shell1-2.2.6 { catchcmd "test.db" ".mode \'insert FOO" -} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}} # check multiple tokens, and quoted tokens do_test shell1-2.3.1 { @@ -236,7 +236,7 @@ do_test shell1-2.3.7 { # check quoted args are unquoted do_test shell1-2.4.1 { catchcmd "test.db" ".mode FOO" -} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}} do_test shell1-2.4.2 { catchcmd "test.db" ".mode csv" } {0 {}} @@ -437,7 +437,7 @@ do_test shell1-3.13.1 { } {0 {current output mode: list}} do_test shell1-3.13.2 { catchcmd "test.db" ".mode FOO" -} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}} do_test shell1-3.13.3 { catchcmd "test.db" ".mode csv" } {0 {}} @@ -467,17 +467,6 @@ do_test shell1-3.13.11 { catchcmd "test.db" ".mode tcl BAD" } {0 {}} -# don't allow partial mode type matches -do_test shell1-3.13.12 { - catchcmd "test.db" ".mode l" -} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}} -do_test shell1-3.13.13 { - catchcmd "test.db" ".mode li" -} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}} -do_test shell1-3.13.14 { - catchcmd "test.db" ".mode lin" -} {0 {}} - # .nullvalue STRING Print STRING in place of NULL values do_test shell1-3.14.1 { catchcmd "test.db" ".nullvalue" From 87fb37efce811fdbd7353a4c5fa0340e8d75668e Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 31 Jan 2022 15:59:43 +0000 Subject: [PATCH 17/40] Do not attempt to limit the number of columns used in a table to a prefix if the table will be used to construct an automatic index or bloom filter. dbsqlfuzz 787d9bd73164c6f0c85469e2e48b2aff19af6938. FossilOrigin-Name: f8766231d2a77bb8b95726b514736d4c2d20b056f7fe60bdbc98ebf5e5b15ae9 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/where.c | 1 + test/join5.test | 14 ++++++++++++++ 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index ab413a27cf..14f6421ccf 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C CLI:\sEnhancements\sto\scolumnar\soutput\smodes.\s\s(See\n[forum/forumpost/5b53cd851f66441f9|forum\spost\s5b53cd851]\sfor\sdiscussion.)\nNew\soutput\smode\s"qbox".\s\sNew\soutput\soptions\s"--quote"\sand\s"--wrap\sN". -D 2022-01-31T14:14:29.581 +C Do\snot\sattempt\sto\slimit\sthe\snumber\sof\scolumns\sused\sin\sa\stable\sto\sa\sprefix\nif\sthe\stable\swill\sbe\sused\sto\sconstruct\san\sautomatic\sindex\sor\sbloom\sfilter.\ndbsqlfuzz\s787d9bd73164c6f0c85469e2e48b2aff19af6938. +D 2022-01-31T15:59:43.574 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -639,7 +639,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b -F src/where.c f61ca6dbb4bbb7171aa5f5459982d8d98aac24852572bcee250f9c469a1963ba +F src/where.c c4a80044708b1000a2b875a6623acfb636acd1c740063b60ea50e359d305829e F src/whereInt.h 0748a6fce98b41862445906922a809146ff7ef4de16ed9022b0bc4e5c43aa60a F src/wherecode.c c313ccf5ed13dc7e88c64f93733f414dee369a212508a866878696d83c64fc36 F src/whereexpr.c ddb6ab49f745154c37dbdb291433c933e00175929647290a11f487af701d0392 @@ -1141,7 +1141,7 @@ F test/join.test 25cf0ac11c3b81fedfd166f9062166bdb39dea92f5a7c16cacbf6dc1f7f6702 F test/join2.test 9bdc615841b91c97a16d68bad9508aea11fa0c6b34e5689847bcc4dac70e4990 F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0 F test/join4.test 1a352e4e267114444c29266ce79e941af5885916 -F test/join5.test d22395f7d4020a58cabbc8316f300a5cfef84aee9e8ba7ce79b33cc43a3e1e2e +F test/join5.test 37864d567928652cab79a7872ebde74b3c67a1feb0366d98bb3bc7832885f388 F test/join6.test f809c025fa253f9e150c0e9afd4cef8813257bceeb6f46e04041228c9403cc2c F test/journal1.test c7b768041b7f494471531e17abc2f4f5ebf9e5096984f43ed17c4eb80ba34497 F test/journal2.test 9dac6b4ba0ca79c3b21446bbae993a462c2397c4 @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 639fc7633bd740421d6b48617b9f68e6be525107e4049673fe720ea6158a393f -R fd24c84f38ee6d8d42b7ffa22962306f +P 539cef5214446a7181614793e9cf323e95ba00ba0f888585b14b598dd2ff0808 +R 6e072b79ef4477bdbbb5ab26dae387dc U drh -Z 929d8043c6f32e2330ec9eb4d8badd53 +Z 399b13224f40b8933599bc4e15b107b4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b8928a0374..26d6d8a73e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -539cef5214446a7181614793e9cf323e95ba00ba0f888585b14b598dd2ff0808 \ No newline at end of file +f8766231d2a77bb8b95726b514736d4c2d20b056f7fe60bdbc98ebf5e5b15ae9 \ No newline at end of file diff --git a/src/where.c b/src/where.c index ce6ae30709..4efb7084da 100644 --- a/src/where.c +++ b/src/where.c @@ -5681,6 +5681,7 @@ WhereInfo *sqlite3WhereBegin( if( pWInfo->eOnePass==ONEPASS_OFF && pTab->nColtabFlags & (TF_HasGenerated|TF_WithoutRowid))==0 + && (pLoop->wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))==0 ){ /* If we know that only a prefix of the record will be used, ** it is advantageous to reduce the "column count" field in diff --git a/test/join5.test b/test/join5.test index 0ae4ca1127..e3f997737b 100644 --- a/test/join5.test +++ b/test/join5.test @@ -328,4 +328,18 @@ do_execsql_test 8.1 { 2 10 2 } + +# 2022-01-31 dbsqlfuzz 787d9bd73164c6f0c85469e2e48b2aff19af6938 +# +reset_db +do_execsql_test 9.1 { + CREATE TABLE t1(a ,b FLOAT); + INSERT INTO t1 VALUES(1,1); + CREATE INDEX t1x1 ON t1(a,b,a,a,a,a,a,a,a,a,a,b); + ANALYZE sqlite_schema; + INSERT INTO sqlite_stat1 VALUES('t1','t1x1','648 324 81 81 81 81 81 81 81081 81 81 81'); + ANALYZE sqlite_schema; + SELECT a FROM (SELECT a FROM t1 NATURAL LEFT JOIN t1) NATURAL LEFT JOIN t1 WHERE (rowid,1)<=(5,0); +} {1} + finish_test From 5dce6f96d1834a0019c22fae4c0871e5db92f059 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 31 Jan 2022 16:29:06 +0000 Subject: [PATCH 18/40] Fix harmless compiler warnings in MSVC. FossilOrigin-Name: 3ec6141c41a71eea0d96a65aa35c828e4d852d60e090513c312b925d0e257f9a --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/shell.c.in | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/manifest b/manifest index 14f6421ccf..3fd894d1bc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\sattempt\sto\slimit\sthe\snumber\sof\scolumns\sused\sin\sa\stable\sto\sa\sprefix\nif\sthe\stable\swill\sbe\sused\sto\sconstruct\san\sautomatic\sindex\sor\sbloom\sfilter.\ndbsqlfuzz\s787d9bd73164c6f0c85469e2e48b2aff19af6938. -D 2022-01-31T15:59:43.574 +C Fix\sharmless\scompiler\swarnings\sin\sMSVC. +D 2022-01-31T16:29:06.213 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -553,7 +553,7 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2 -F src/shell.c.in d53f77fd4733a6cada8e0d2eb2936fcae3fbe885ac08e48d1e525f22206bf579 +F src/shell.c.in 252de95a2ba4ab1808b2bacc00644389a72c61ffd61c7193e5b319cc126a5c52 F src/sqlite.h.in eaade58049152dac850d57415bcced885ca27ae9582f8aea2cfb7f1db78a521b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 539cef5214446a7181614793e9cf323e95ba00ba0f888585b14b598dd2ff0808 -R 6e072b79ef4477bdbbb5ab26dae387dc +P f8766231d2a77bb8b95726b514736d4c2d20b056f7fe60bdbc98ebf5e5b15ae9 +R 6c51010444b3de981e88513b2160fc76 U drh -Z 399b13224f40b8933599bc4e15b107b4 +Z 405a35ed61d67534b5e5df8c9cf5fafe # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 26d6d8a73e..0fd822b091 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f8766231d2a77bb8b95726b514736d4c2d20b056f7fe60bdbc98ebf5e5b15ae9 \ No newline at end of file +3ec6141c41a71eea0d96a65aa35c828e4d852d60e090513c312b925d0e257f9a \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 314adb9a11..c53b9104ff 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -3312,8 +3312,8 @@ static void exec_prepared_stmt_columnar( azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); shell_check_oom(azData); azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) ); - shell_check_oom(azNextLine); - memset(azNextLine, 0, nColumn*sizeof(char*) ); + shell_check_oom((void*)azNextLine); + memset((void*)azNextLine, 0, nColumn*sizeof(char*) ); if( p->bQuote ){ azQuoted = sqlite3_malloc64( nColumn*sizeof(char*) ); shell_check_oom(azQuoted); @@ -3350,8 +3350,8 @@ static void exec_prepared_stmt_columnar( abRowDiv[nRow] = 1; nRow++; for(i=0; icolWidth[i]; - if( w==0 ) w = p->iWrap; + int wx = p->colWidth[i]; + if( wx==0 ) wx = p->iWrap; if( useNextLine ){ uz = azNextLine[i]; }else if( p->bQuote ){ @@ -3361,7 +3361,7 @@ static void exec_prepared_stmt_columnar( }else{ uz = (const unsigned char*)sqlite3_column_text(pStmt,i); } - azData[nRow*nColumn + i] = translateForDisplayAndDup(uz, &azNextLine[i], w); + azData[nRow*nColumn + i] = translateForDisplayAndDup(uz, &azNextLine[i], wx); if( azNextLine[i] ){ bNextLine = 1; abRowDiv[nRow-1] = 0; @@ -3476,7 +3476,7 @@ columnar_end: nData = (nRow+1)*nColumn; for(i=0; i Date: Mon, 31 Jan 2022 20:39:27 +0000 Subject: [PATCH 19/40] CLI: Fix a problem with tabs in the new qbox mode. FossilOrigin-Name: ca96ab3ef78479d95e831cacdb80f23c119cbaa77b3821eac7a3f110bfe20cc9 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/shell.c.in | 2 -- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 3fd894d1bc..1f7354f999 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sharmless\scompiler\swarnings\sin\sMSVC. -D 2022-01-31T16:29:06.213 +C CLI:\sFix\sa\sproblem\swith\stabs\sin\sthe\snew\sqbox\smode. +D 2022-01-31T20:39:27.381 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -553,7 +553,7 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2 -F src/shell.c.in 252de95a2ba4ab1808b2bacc00644389a72c61ffd61c7193e5b319cc126a5c52 +F src/shell.c.in 52a69e2611100fbd0cb4adea8d669cb4ae692de2e1ee989eea90b9d2216fc0b7 F src/sqlite.h.in eaade58049152dac850d57415bcced885ca27ae9582f8aea2cfb7f1db78a521b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P f8766231d2a77bb8b95726b514736d4c2d20b056f7fe60bdbc98ebf5e5b15ae9 -R 6c51010444b3de981e88513b2160fc76 +P 3ec6141c41a71eea0d96a65aa35c828e4d852d60e090513c312b925d0e257f9a +R 653501eaadaf515fee417911e957f02f U drh -Z 405a35ed61d67534b5e5df8c9cf5fafe +Z 96fbc4796acfbba99607741d48514123 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0fd822b091..1a434190f8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3ec6141c41a71eea0d96a65aa35c828e4d852d60e090513c312b925d0e257f9a \ No newline at end of file +ca96ab3ef78479d95e831cacdb80f23c119cbaa77b3821eac7a3f110bfe20cc9 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index c53b9104ff..d44048aa3f 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -3202,8 +3202,6 @@ static char *translateForDisplayAndDup( n++; j++; }while( (n&7)!=0 && n Date: Mon, 31 Jan 2022 22:14:53 +0000 Subject: [PATCH 20/40] CLI: for columnar output modes, make sure the header is not too big. Also, improve the help text. FossilOrigin-Name: 070fae3a09cea675e722340870cb2bee9e1ac96954d3baacfdb7f5400ddb1b20 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/shell.c.in | 11 +++++++++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 1f7354f999..941a125836 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C CLI:\sFix\sa\sproblem\swith\stabs\sin\sthe\snew\sqbox\smode. -D 2022-01-31T20:39:27.381 +C CLI:\sfor\scolumnar\soutput\smodes,\smake\ssure\sthe\sheader\sis\snot\stoo\sbig.\nAlso,\simprove\sthe\shelp\stext. +D 2022-01-31T22:14:53.379 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -553,7 +553,7 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2 -F src/shell.c.in 52a69e2611100fbd0cb4adea8d669cb4ae692de2e1ee989eea90b9d2216fc0b7 +F src/shell.c.in 5f848589c29cd03a9ccde31f1507379eb926ff8352a92ecf7ea4baf00823138d F src/sqlite.h.in eaade58049152dac850d57415bcced885ca27ae9582f8aea2cfb7f1db78a521b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 3ec6141c41a71eea0d96a65aa35c828e4d852d60e090513c312b925d0e257f9a -R 653501eaadaf515fee417911e957f02f +P ca96ab3ef78479d95e831cacdb80f23c119cbaa77b3821eac7a3f110bfe20cc9 +R 7edb6446ecfd92c5152a5ff12c329ed4 U drh -Z 96fbc4796acfbba99607741d48514123 +Z ad9f462a73710576b722f55f5a4feb61 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1a434190f8..9706a31c8a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ca96ab3ef78479d95e831cacdb80f23c119cbaa77b3821eac7a3f110bfe20cc9 \ No newline at end of file +070fae3a09cea675e722340870cb2bee9e1ac96954d3baacfdb7f5400ddb1b20 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index d44048aa3f..f412f345d9 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -3333,7 +3333,12 @@ static void exec_prepared_stmt_columnar( p->actualWidth[i] = w; } for(i=0; icolWidth[i]; + if( wx==0 ) wx = p->iWrap; + if( wx<0 ) wx = -wx; + uz = (const unsigned char*)sqlite3_column_name(pStmt,i); + azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx); } do{ int useNextLine = bNextLine; @@ -3350,6 +3355,7 @@ static void exec_prepared_stmt_columnar( for(i=0; icolWidth[i]; if( wx==0 ) wx = p->iWrap; + if( wx<0 ) wx = -wx; if( useNextLine ){ uz = azNextLine[i]; }else if( p->bQuote ){ @@ -4257,7 +4263,7 @@ static const char *(azHelp[]) = { ".load FILE ?ENTRY? Load an extension library", #endif ".log FILE|off Turn logging on or off. FILE can be stderr/stdout", - ".mode MODE ?TABLE? ?OPTIONS? Set output mode", + ".mode MODE ?OPTIONS? Set output mode", " MODE is one of:", " ascii Columns/rows delimited by 0x1F and 0x1E", " box Tables using unicode box-drawing characters", @@ -4278,6 +4284,7 @@ static const char *(azHelp[]) = { " --wrap N Wrap output lines longer than N character", " --quote Quote output text as SQL literals", " --noquote Do not quote output text", + " TABLE The name of SQL table used for \"insert\" mode", ".nonce STRING Disable safe mode for one command if the nonce matches", ".nullvalue STRING Use STRING in place of NULL values", ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", From 5aabdaebd516b9d0a5b07f2f10f721d6cd580f06 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 1 Feb 2022 00:00:08 +0000 Subject: [PATCH 21/40] CLI: In ".mode column" output, if any row contains a newline or wraps, then put a single blank line in between each pair of rows to provide additional visual separately. FossilOrigin-Name: fd42f4c304079356358e606dd96d4b84cf211c4334c586118b99fe9ad20e20ea --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/shell.c.in | 2 ++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 941a125836..9d4ac64543 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C CLI:\sfor\scolumnar\soutput\smodes,\smake\ssure\sthe\sheader\sis\snot\stoo\sbig.\nAlso,\simprove\sthe\shelp\stext. -D 2022-01-31T22:14:53.379 +C CLI:\sIn\s".mode\scolumn"\soutput,\sif\sany\srow\scontains\sa\snewline\sor\swraps,\sthen\nput\sa\ssingle\sblank\sline\sin\sbetween\seach\spair\sof\srows\sto\sprovide\sadditional\nvisual\sseparately. +D 2022-02-01T00:00:08.128 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -553,7 +553,7 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2 -F src/shell.c.in 5f848589c29cd03a9ccde31f1507379eb926ff8352a92ecf7ea4baf00823138d +F src/shell.c.in 8ae0e33c265c14a6932233b2eb37b090a67a3cb816671da66d26319b1d98cbfd F src/sqlite.h.in eaade58049152dac850d57415bcced885ca27ae9582f8aea2cfb7f1db78a521b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P ca96ab3ef78479d95e831cacdb80f23c119cbaa77b3821eac7a3f110bfe20cc9 -R 7edb6446ecfd92c5152a5ff12c329ed4 +P 070fae3a09cea675e722340870cb2bee9e1ac96954d3baacfdb7f5400ddb1b20 +R da80598e94d04369e4a04ce4c1915aa8 U drh -Z ad9f462a73710576b722f55f5a4feb61 +Z 366282a4b771bc969cd1f7f43a0d9728 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9706a31c8a..30a779ddff 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -070fae3a09cea675e722340870cb2bee9e1ac96954d3baacfdb7f5400ddb1b20 \ No newline at end of file +fd42f4c304079356358e606dd96d4b84cf211c4334c586118b99fe9ad20e20ea \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index f412f345d9..f59122c717 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -3460,6 +3460,8 @@ static void exec_prepared_stmt_columnar( print_row_separator(p, nColumn, "+"); }else if( p->cMode==MODE_Box ){ print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); + }else if( p->cMode==MODE_Column ){ + raw_printf(p->out, "\n"); } } j = -1; From cc4d55c9cb14df12fd4625645e7d7065824e0a1e Mon Sep 17 00:00:00 2001 From: larrybr Date: Tue, 1 Feb 2022 02:50:45 +0000 Subject: [PATCH 22/40] Add "--wordwrap on/off" option for CLI columnar modes, qwbox shortcut FossilOrigin-Name: 10dbc278708cd2cce7fef90738082dbe31750d93e44b5fa5413a9a32dae7703a --- manifest | 17 ++++--- manifest.uuid | 2 +- src/shell.c.in | 130 +++++++++++++++++++++++++++++++---------------- test/shell1.test | 8 +-- 4 files changed, 99 insertions(+), 58 deletions(-) diff --git a/manifest b/manifest index 9d4ac64543..6128f7de2e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C CLI:\sIn\s".mode\scolumn"\soutput,\sif\sany\srow\scontains\sa\snewline\sor\swraps,\sthen\nput\sa\ssingle\sblank\sline\sin\sbetween\seach\spair\sof\srows\sto\sprovide\sadditional\nvisual\sseparately. -D 2022-02-01T00:00:08.128 +C Add\s"--wordwrap\son/off"\soption\sfor\sCLI\scolumnar\smodes,\sqwbox\sshortcut +D 2022-02-01T02:50:45.569 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -553,7 +553,7 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2 -F src/shell.c.in 8ae0e33c265c14a6932233b2eb37b090a67a3cb816671da66d26319b1d98cbfd +F src/shell.c.in 4f5e0a9f38aa648ca529efb66f338308262b47a72b6c5c95a1f704619fa1aef0 F src/sqlite.h.in eaade58049152dac850d57415bcced885ca27ae9582f8aea2cfb7f1db78a521b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a @@ -1386,7 +1386,7 @@ F test/sharedA.test 49d87ec54ab640fbbc3786ee3c01de94aaa482a3a9f834ad3fe92770eb69 F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304 -F test/shell1.test ce2f370886645f38fabdde44976c14a004400f166edea8fdd9741079b645fef6 +F test/shell1.test 1859ba21623b6a804ab2527b2a7c10b54513e8c4e40cec2b462d0e9f082d4fec F test/shell2.test f00a0501c00583cbc46f7510e1d713366326b2b3e63d06d15937284171a8787c F test/shell3.test cb4b835a901742c9719437a89171172ecc4a8823ad97349af8e4e841e6f82566 F test/shell4.test 8f6c0fce4abed19a8a7f7262517149812a04caa905d01bdc8f5e92573504b759 @@ -1942,8 +1942,9 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 070fae3a09cea675e722340870cb2bee9e1ac96954d3baacfdb7f5400ddb1b20 -R da80598e94d04369e4a04ce4c1915aa8 -U drh -Z 366282a4b771bc969cd1f7f43a0d9728 +P fd42f4c304079356358e606dd96d4b84cf211c4334c586118b99fe9ad20e20ea +Q +f51a17b6271a8dd7c48725e4ec2df1fde0460866c81c7225dc27216ab389591e +R 9664e5ccc659386bbaeacc1cefed4830 +U larrybr +Z c1cf64e5874e5b6ab186498f765ac9eb # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 30a779ddff..53a5bb21c2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fd42f4c304079356358e606dd96d4b84cf211c4334c586118b99fe9ad20e20ea \ No newline at end of file +10dbc278708cd2cce7fef90738082dbe31750d93e44b5fa5413a9a32dae7703a \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index f59122c717..25afa79910 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -1066,6 +1066,16 @@ struct EQPGraph { char zPrefix[100]; /* Graph prefix */ }; +/* Parameters affecting columnar mode result display (defaulting together) */ +typedef struct ColModeOpts { + int iWrap; /* In columnar modes, wrap lines reaching this limit */ + u8 bQuote; /* Quote results for .mode box and table */ + u8 bWordWrap; /* In columnar modes, wrap at word boundaries */ +} ColModeOpts; +#define ColModeOpts_default { 60, 0, 0 } +#define ColModeOpts_default_qbox { 60, 1, 0 } +#define ColModeOpts_default_qwbox { 60, 1, 1 } + /* ** State information about the database connection is contained in an ** instance of the following structure. @@ -1084,8 +1094,7 @@ struct ShellState { u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ u8 bSafeMode; /* True to prohibit unsafe operations */ u8 bSafeModePersist; /* The long-term value of bSafeMode */ - u8 bQuote; /* Quote results for .mode box and table */ - int iWrap; /* Wrap lines this long or longer in some output modes */ + ColModeOpts cmOpts; /* Option values affecting columnar mode output */ unsigned statsOn; /* True to display memory stats before each finalize */ unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */ int inputNesting; /* Track nesting level of .read and other redirects */ @@ -3173,16 +3182,18 @@ static void print_box_row_separator( ** ** Compute characters to display on the first line of z[]. Stop at the ** first \r, \n, or \f. Expand \t into spaces. Return a copy (obtained -** from malloc()) of that first line. Write anything to display -** on the next line into *pzTail. If this is the last line, write a NULL -** into *pzTail. +** from malloc()) of that first line, which caller should free sometime. +** Write anything to display on the next line into *pzTail. If this is +** the last line, write a NULL into *pzTail. (*pzTail is not allocated.) */ static char *translateForDisplayAndDup( const unsigned char *z, const unsigned char **pzTail, - int mxWidth + int mxWidth, + u8 bWordWrap ){ - int i, j, n; + int i, j, n; /* in-index, code-skip, code-count */ + int iLastWhite = 0, nLastWhite = 0; unsigned char *zOut; if( z==0 ){ *pzTail = 0; @@ -3194,6 +3205,10 @@ static char *translateForDisplayAndDup( while( n=' ' ){ n++; + if( IsSpace(z[i]) ){ + iLastWhite = i; + nLastWhite = n; + } do{ i++; j++; }while( (z[i]&0xc0)==0x80 ); continue; } @@ -3207,6 +3222,11 @@ static char *translateForDisplayAndDup( } break; } + if( bWordWrap && iLastWhite>0 && n>=mxWidth ){ + /* Will word wrap only if it is requested and can do any good. */ + mxWidth = nLastWhite; + i = iLastWhite; + } if( n>=mxWidth && z[i]>=' ' ){ *pzTail = &z[i]; }else if( z[i]=='\r' && z[i+1]=='\n' ){ @@ -3312,7 +3332,7 @@ static void exec_prepared_stmt_columnar( azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) ); shell_check_oom((void*)azNextLine); memset((void*)azNextLine, 0, nColumn*sizeof(char*) ); - if( p->bQuote ){ + if( p->cmOpts.bQuote ){ azQuoted = sqlite3_malloc64( nColumn*sizeof(char*) ); shell_check_oom(azQuoted); memset(azQuoted, 0, nColumn*sizeof(char*) ); @@ -3334,11 +3354,15 @@ static void exec_prepared_stmt_columnar( } for(i=0; icolWidth[i]; - if( wx==0 ) wx = p->iWrap; + if( wx==0 ){ + wx = p->cmOpts.iWrap; + bw = p->cmOpts.bWordWrap; + } if( wx<0 ) wx = -wx; uz = (const unsigned char*)sqlite3_column_name(pStmt,i); - azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx); + azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx, bw); } do{ int useNextLine = bNextLine; @@ -3353,19 +3377,24 @@ static void exec_prepared_stmt_columnar( abRowDiv[nRow] = 1; nRow++; for(i=0; icolWidth[i]; - if( wx==0 ) wx = p->iWrap; + if( wx==0 ){ + wx = p->cmOpts.iWrap; + bw = p->cmOpts.bWordWrap; + } if( wx<0 ) wx = -wx; if( useNextLine ){ uz = azNextLine[i]; - }else if( p->bQuote ){ + }else if( p->cmOpts.bQuote ){ sqlite3_free(azQuoted[i]); azQuoted[i] = quoted_column(pStmt,i); uz = (const unsigned char*)azQuoted[i]; }else{ uz = (const unsigned char*)sqlite3_column_text(pStmt,i); } - azData[nRow*nColumn + i] = translateForDisplayAndDup(uz, &azNextLine[i], wx); + azData[nRow*nColumn + i] + = translateForDisplayAndDup(uz, &azNextLine[i], wx, bw); if( azNextLine[i] ){ bNextLine = 1; abRowDiv[nRow-1] = 0; @@ -4278,16 +4307,18 @@ static const char *(azHelp[]) = { " list Values delimited by \"|\"", " markdown Markdown table format", " qbox Shorthand for \"box --width 60 --quote\"", + " qwbox Shorthand for \"box --width 60 --wordwrap on --quote\"", " quote Escape answers as for SQL", " table ASCII-art table", " tabs Tab-separated values", " tcl TCL list elements", - " OPTIONS: (value for columnar modes only):", - " --wrap N Wrap output lines longer than N character", - " --quote Quote output text as SQL literals", - " --noquote Do not quote output text", - " TABLE The name of SQL table used for \"insert\" mode", - ".nonce STRING Disable safe mode for one command if the nonce matches", + " OPTIONS: (for columnar modes or insert mode):", + " --wrap N Wrap output lines to no longer than N characters", + " --wordwrap B Wrap or not at word boundaries per B (on/off) ", + " --quote Quote output text as SQL literals", + " --noquote Do not quote output text", + " TABLE The name of SQL table used for \"insert\" mode", + ".nonce STRING Suspend safe mode for one command if nonce matches", ".nullvalue STRING Use STRING in place of NULL values", ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", " If FILE begins with '|' then open as a pipe", @@ -9070,21 +9101,31 @@ static int do_meta_command(char *zLine, ShellState *p){ const char *zMode = 0; const char *zTabname = 0; int i, n2; - int bQuoteChng = 0; - int bWrapChng = 0; + ColModeOpts cmOpts = ColModeOpts_default; for(i=1; iiWrap = integerValue(azArg[++i]); - bWrapChng = 1; + cmOpts.iWrap = integerValue(azArg[++i]); + }else if( optionMatch(z,"wordwrap") && i+1bQuote = 1; - bQuoteChng = 1; + cmOpts.bQuote = 1; }else if( optionMatch(z,"noquote") ){ - p->bQuote = 0; - bQuoteChng = 1; + cmOpts.bQuote = 0; }else if( zMode==0 ){ zMode = z; + /* Apply defaults for qbox and qwbox pseudo-modes. If that + * overwrites already-set values, user was informed of this. + */ + if( strcmp(z, "qbox")==0 ){ + ColModeOpts cmo = ColModeOpts_default_qbox; + zMode = "box"; + cmOpts = cmo; + }else if( strcmp(z, "qwbox")==0 ){ + ColModeOpts cmo = ColModeOpts_default_qwbox; + zMode = "box"; + cmOpts = cmo; + } }else if( zTabname==0 ){ zTabname = z; }else if( z[0]=='-' ){ @@ -9092,6 +9133,7 @@ static int do_meta_command(char *zLine, ShellState *p){ utf8_printf(stderr, "options:\n" " --noquote\n" " --quote\n" + " --wordwrap on/off\n" " --wrap N\n"); rc = 1; goto meta_command_exit; @@ -9105,12 +9147,15 @@ static int do_meta_command(char *zLine, ShellState *p){ if( p->mode==MODE_Column || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) ){ - raw_printf(p->out, "current output mode: %s --wrap %d --%squote\n", - modeDescr[p->mode], p->iWrap, p->bQuote ? "" : "no"); + raw_printf + (p->out, + "current output mode: %s --wrap %d --wordwrap %s --%squote\n", + modeDescr[p->mode], p->cmOpts.iWrap, + p->cmOpts.bWordWrap ? "on" : "off", + p->cmOpts.bQuote ? "" : "no"); }else{ raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]); } - bWrapChng = bQuoteChng = 1; zMode = modeDescr[p->mode]; } n2 = strlen30(zMode); @@ -9123,8 +9168,7 @@ static int do_meta_command(char *zLine, ShellState *p){ p->showHeader = 1; } sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); - if( !bWrapChng ) p->iWrap = 0; - if( !bQuoteChng ) p->bQuote = 0; + p->cmOpts = cmOpts; }else if( strncmp(zMode,"list",n2)==0 ){ p->mode = MODE_List; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Column); @@ -9155,20 +9199,13 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record); }else if( strncmp(zMode,"markdown",n2)==0 ){ p->mode = MODE_Markdown; - if( !bWrapChng ) p->iWrap = 0; - if( !bQuoteChng ) p->bQuote = 0; + p->cmOpts = cmOpts; }else if( strncmp(zMode,"table",n2)==0 ){ p->mode = MODE_Table; - if( !bWrapChng ) p->iWrap = 0; - if( !bQuoteChng ) p->bQuote = 0; + p->cmOpts = cmOpts; }else if( strncmp(zMode,"box",n2)==0 ){ p->mode = MODE_Box; - if( !bWrapChng ) p->iWrap = 0; - if( !bQuoteChng ) p->bQuote = 0; - }else if( strcmp(zMode,"qbox")==0 ){ - p->mode = MODE_Box; - if( !bWrapChng ) p->iWrap = 60; - if( !bQuoteChng ) p->bQuote = 1; + p->cmOpts = cmOpts; }else if( strncmp(zMode,"count",n2)==0 ){ p->mode = MODE_Count; }else if( strncmp(zMode,"off",n2)==0 ){ @@ -9178,7 +9215,7 @@ static int do_meta_command(char *zLine, ShellState *p){ }else{ raw_printf(stderr, "Error: mode should be one of: " "ascii box column csv html insert json line list markdown " - "qbox quote table tabs tcl\n"); + "qbox quote qwbox table tabs tcl\n"); rc = 1; } p->cMode = p->mode; @@ -10329,8 +10366,11 @@ static int do_meta_command(char *zLine, ShellState *p){ if( p->mode==MODE_Column || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) ){ - utf8_printf(p->out, "%12.12s: %s --wrap %d --%squote\n", "mode", - modeDescr[p->mode], p->iWrap, p->bQuote ? "" : "no"); + utf8_printf + (p->out, "%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode", + modeDescr[p->mode], p->cmOpts.iWrap, + p->cmOpts.bWordWrap ? "on" : "off", + p->cmOpts.bQuote ? "" : "no"); }else{ utf8_printf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]); } diff --git a/test/shell1.test b/test/shell1.test index 745990a7c9..122d38dfa7 100644 --- a/test/shell1.test +++ b/test/shell1.test @@ -205,10 +205,10 @@ do_test shell1-2.2.4 { } {0 {}} do_test shell1-2.2.5 { catchcmd "test.db" ".mode \"insert FOO" -} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote qwbox table tabs tcl}} do_test shell1-2.2.6 { catchcmd "test.db" ".mode \'insert FOO" -} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote qwbox table tabs tcl}} # check multiple tokens, and quoted tokens do_test shell1-2.3.1 { @@ -236,7 +236,7 @@ do_test shell1-2.3.7 { # check quoted args are unquoted do_test shell1-2.4.1 { catchcmd "test.db" ".mode FOO" -} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote qwbox table tabs tcl}} do_test shell1-2.4.2 { catchcmd "test.db" ".mode csv" } {0 {}} @@ -437,7 +437,7 @@ do_test shell1-3.13.1 { } {0 {current output mode: list}} do_test shell1-3.13.2 { catchcmd "test.db" ".mode FOO" -} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote qwbox table tabs tcl}} do_test shell1-3.13.3 { catchcmd "test.db" ".mode csv" } {0 {}} From ca1776b5f3af281ded8e847e311f984a608a84ec Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 1 Feb 2022 12:28:17 +0000 Subject: [PATCH 23/40] CLI: Add the --ww option as an alias for --wordwrap. Improve the wordwrap algorithm so that it breaks at punctuation if it cannot find space. Always wordwrap with --ww even if there is a .width setting for the column. FossilOrigin-Name: 1b528e31f8c62797e0814568b520c0680ff23a2ee877ca6aa70a167d40ebdf80 --- manifest | 17 ++++++------ manifest.uuid | 2 +- src/shell.c.in | 70 +++++++++++++++++++++++++++--------------------- test/shell1.test | 8 +++--- 4 files changed, 52 insertions(+), 45 deletions(-) diff --git a/manifest b/manifest index 6128f7de2e..e07a89b93b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\s"--wordwrap\son/off"\soption\sfor\sCLI\scolumnar\smodes,\sqwbox\sshortcut -D 2022-02-01T02:50:45.569 +C CLI:\sAdd\sthe\s--ww\soption\sas\san\salias\sfor\s--wordwrap.\s\sImprove\sthe\swordwrap\nalgorithm\sso\sthat\sit\sbreaks\sat\spunctuation\sif\sit\scannot\sfind\sspace.\s\sAlways\nwordwrap\swith\s--ww\seven\sif\sthere\sis\sa\s.width\ssetting\sfor\sthe\scolumn. +D 2022-02-01T12:28:17.432 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -553,7 +553,7 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2 -F src/shell.c.in 4f5e0a9f38aa648ca529efb66f338308262b47a72b6c5c95a1f704619fa1aef0 +F src/shell.c.in b701feb3867d8293aafbb7164d3fd09f76c07a99184bfcac719351976b19e0ef F src/sqlite.h.in eaade58049152dac850d57415bcced885ca27ae9582f8aea2cfb7f1db78a521b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a @@ -1386,7 +1386,7 @@ F test/sharedA.test 49d87ec54ab640fbbc3786ee3c01de94aaa482a3a9f834ad3fe92770eb69 F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304 -F test/shell1.test 1859ba21623b6a804ab2527b2a7c10b54513e8c4e40cec2b462d0e9f082d4fec +F test/shell1.test ce2f370886645f38fabdde44976c14a004400f166edea8fdd9741079b645fef6 F test/shell2.test f00a0501c00583cbc46f7510e1d713366326b2b3e63d06d15937284171a8787c F test/shell3.test cb4b835a901742c9719437a89171172ecc4a8823ad97349af8e4e841e6f82566 F test/shell4.test 8f6c0fce4abed19a8a7f7262517149812a04caa905d01bdc8f5e92573504b759 @@ -1942,9 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P fd42f4c304079356358e606dd96d4b84cf211c4334c586118b99fe9ad20e20ea -Q +f51a17b6271a8dd7c48725e4ec2df1fde0460866c81c7225dc27216ab389591e -R 9664e5ccc659386bbaeacc1cefed4830 -U larrybr -Z c1cf64e5874e5b6ab186498f765ac9eb +P 10dbc278708cd2cce7fef90738082dbe31750d93e44b5fa5413a9a32dae7703a +R 69e29afaa5f754bae2ca14b8682cbcc3 +U drh +Z 4e6f65c63d4836409df930e76a99a677 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 53a5bb21c2..ea3c3bc912 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -10dbc278708cd2cce7fef90738082dbe31750d93e44b5fa5413a9a32dae7703a \ No newline at end of file +1b528e31f8c62797e0814568b520c0680ff23a2ee877ca6aa70a167d40ebdf80 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 25afa79910..bd65c6f2b8 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -1074,7 +1074,6 @@ typedef struct ColModeOpts { } ColModeOpts; #define ColModeOpts_default { 60, 0, 0 } #define ColModeOpts_default_qbox { 60, 1, 0 } -#define ColModeOpts_default_qwbox { 60, 1, 1 } /* ** State information about the database connection is contained in an @@ -3187,28 +3186,27 @@ static void print_box_row_separator( ** the last line, write a NULL into *pzTail. (*pzTail is not allocated.) */ static char *translateForDisplayAndDup( - const unsigned char *z, - const unsigned char **pzTail, - int mxWidth, - u8 bWordWrap + const unsigned char *z, /* Input text to be transformed */ + const unsigned char **pzTail, /* OUT: Tail of the input for next line */ + int mxWidth, /* Max width. 0 means no limit */ + u8 bWordWrap /* If true, avoid breaking mid-word */ ){ - int i, j, n; /* in-index, code-skip, code-count */ - int iLastWhite = 0, nLastWhite = 0; - unsigned char *zOut; + int i; /* Input bytes consumed */ + int j; /* Output bytes generated */ + int k; /* Input bytes to be displayed */ + int n; /* Output column number */ + unsigned char *zOut; /* Output text */ + if( z==0 ){ *pzTail = 0; return 0; } if( mxWidth<0 ) mxWidth = -mxWidth; if( mxWidth==0 ) mxWidth = 1000000; - i = j= n = 0; + i = j = n = 0; while( n=' ' ){ n++; - if( IsSpace(z[i]) ){ - iLastWhite = i; - nLastWhite = n; - } do{ i++; j++; }while( (z[i]&0xc0)==0x80 ); continue; } @@ -3222,10 +3220,24 @@ static char *translateForDisplayAndDup( } break; } - if( bWordWrap && iLastWhite>0 && n>=mxWidth ){ - /* Will word wrap only if it is requested and can do any good. */ - mxWidth = nLastWhite; - i = iLastWhite; + if( n>=mxWidth && bWordWrap ){ + /* Perhaps try to back up to a better place to break the line */ + for(k=i; k>i/2; k--){ + if( isspace(z[k-1]) ) break; + } + if( k<=i/2 ){ + for(k=i; k>i/2; k--){ + if( isalnum(z[k-1])!=isalnum(z[k]) ) break; + } + } + if( k<=i/2 ){ + k = i; + }else{ + i = k; + while( z[i]==' ' ) i++; + } + }else{ + k = i; } if( n>=mxWidth && z[i]>=' ' ){ *pzTail = &z[i]; @@ -3239,7 +3251,7 @@ static char *translateForDisplayAndDup( zOut = malloc( j+1 ); shell_check_oom(zOut); i = j = n = 0; - while( n=' ' ){ n++; do{ zOut[j++] = z[i++]; }while( (z[i]&0xc0)==0x80 ); @@ -3321,6 +3333,7 @@ static void exec_prepared_stmt_columnar( const unsigned char **azNextLine = 0; int bNextLine = 0; int bMultiLineRowExists = 0; + int bw = p->cmOpts.bWordWrap; rc = sqlite3_step(pStmt); if( rc!=SQLITE_ROW ) return; @@ -3354,11 +3367,9 @@ static void exec_prepared_stmt_columnar( } for(i=0; icolWidth[i]; if( wx==0 ){ wx = p->cmOpts.iWrap; - bw = p->cmOpts.bWordWrap; } if( wx<0 ) wx = -wx; uz = (const unsigned char*)sqlite3_column_name(pStmt,i); @@ -3377,11 +3388,9 @@ static void exec_prepared_stmt_columnar( abRowDiv[nRow] = 1; nRow++; for(i=0; icolWidth[i]; if( wx==0 ){ wx = p->cmOpts.iWrap; - bw = p->cmOpts.bWordWrap; } if( wx<0 ) wx = -wx; if( useNextLine ){ @@ -4307,14 +4316,14 @@ static const char *(azHelp[]) = { " list Values delimited by \"|\"", " markdown Markdown table format", " qbox Shorthand for \"box --width 60 --quote\"", - " qwbox Shorthand for \"box --width 60 --wordwrap on --quote\"", " quote Escape answers as for SQL", " table ASCII-art table", " tabs Tab-separated values", " tcl TCL list elements", " OPTIONS: (for columnar modes or insert mode):", " --wrap N Wrap output lines to no longer than N characters", - " --wordwrap B Wrap or not at word boundaries per B (on/off) ", + " --wordwrap B Wrap or not at word boundaries per B (on/off)", + " --ww Shorthand for \"--wordwrap 1\"", " --quote Quote output text as SQL literals", " --noquote Do not quote output text", " TABLE The name of SQL table used for \"insert\" mode", @@ -9106,6 +9115,8 @@ static int do_meta_command(char *zLine, ShellState *p){ const char *z = azArg[i]; if( optionMatch(z,"wrap") && i+1cMode = p->mode; diff --git a/test/shell1.test b/test/shell1.test index 122d38dfa7..745990a7c9 100644 --- a/test/shell1.test +++ b/test/shell1.test @@ -205,10 +205,10 @@ do_test shell1-2.2.4 { } {0 {}} do_test shell1-2.2.5 { catchcmd "test.db" ".mode \"insert FOO" -} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote qwbox table tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}} do_test shell1-2.2.6 { catchcmd "test.db" ".mode \'insert FOO" -} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote qwbox table tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}} # check multiple tokens, and quoted tokens do_test shell1-2.3.1 { @@ -236,7 +236,7 @@ do_test shell1-2.3.7 { # check quoted args are unquoted do_test shell1-2.4.1 { catchcmd "test.db" ".mode FOO" -} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote qwbox table tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}} do_test shell1-2.4.2 { catchcmd "test.db" ".mode csv" } {0 {}} @@ -437,7 +437,7 @@ do_test shell1-3.13.1 { } {0 {current output mode: list}} do_test shell1-3.13.2 { catchcmd "test.db" ".mode FOO" -} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote qwbox table tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}} do_test shell1-3.13.3 { catchcmd "test.db" ".mode csv" } {0 {}} From e66532a63b934135ec9ebd72bdddcf79a3fb97a0 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 1 Feb 2022 13:17:11 +0000 Subject: [PATCH 24/40] CLI: Take extra care to not split a multi-byte unicode character when doing wordwrap. FossilOrigin-Name: 00b1b7020a564976da3237532434e47ccf17eb5d620e6ac45f3e70b5d5739200 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/shell.c.in | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index e07a89b93b..1b9116fe1a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C CLI:\sAdd\sthe\s--ww\soption\sas\san\salias\sfor\s--wordwrap.\s\sImprove\sthe\swordwrap\nalgorithm\sso\sthat\sit\sbreaks\sat\spunctuation\sif\sit\scannot\sfind\sspace.\s\sAlways\nwordwrap\swith\s--ww\seven\sif\sthere\sis\sa\s.width\ssetting\sfor\sthe\scolumn. -D 2022-02-01T12:28:17.432 +C CLI:\sTake\sextra\scare\sto\snot\ssplit\sa\smulti-byte\sunicode\scharacter\swhen\sdoing\nwordwrap. +D 2022-02-01T13:17:11.049 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -553,7 +553,7 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2 -F src/shell.c.in b701feb3867d8293aafbb7164d3fd09f76c07a99184bfcac719351976b19e0ef +F src/shell.c.in 2f58e6aa6b3d2012db32f1c5fa4591e9d12fd582904632b4fc8f57a382b98fd3 F src/sqlite.h.in eaade58049152dac850d57415bcced885ca27ae9582f8aea2cfb7f1db78a521b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 10dbc278708cd2cce7fef90738082dbe31750d93e44b5fa5413a9a32dae7703a -R 69e29afaa5f754bae2ca14b8682cbcc3 +P 1b528e31f8c62797e0814568b520c0680ff23a2ee877ca6aa70a167d40ebdf80 +R fe91994efd36a88115500529351b7e2e U drh -Z 4e6f65c63d4836409df930e76a99a677 +Z 4d3a3178c78a3baab7f87b0b2d56c876 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ea3c3bc912..0347e193b4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1b528e31f8c62797e0814568b520c0680ff23a2ee877ca6aa70a167d40ebdf80 \ No newline at end of file +00b1b7020a564976da3237532434e47ccf17eb5d620e6ac45f3e70b5d5739200 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index bd65c6f2b8..ef8afe026d 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -3227,7 +3227,7 @@ static char *translateForDisplayAndDup( } if( k<=i/2 ){ for(k=i; k>i/2; k--){ - if( isalnum(z[k-1])!=isalnum(z[k]) ) break; + if( isalnum(z[k-1])!=isalnum(z[k]) && (z[k]&0xc0)!=0x80 ) break; } } if( k<=i/2 ){ From 0fe7e7d9242dfccb1240c613a2e4608c0023259c Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 1 Feb 2022 14:58:29 +0000 Subject: [PATCH 25/40] Add new interfaces to enable virtual table to process IN operator constraints all at once, rather than one element at a time. FossilOrigin-Name: eb84b80e1f6d8c32bf0c9e1731f0233de0160a13f714f766779ae01fdf504e7b --- manifest | 33 ++++++++++--------- manifest.uuid | 2 +- src/loadext.c | 3 ++ src/sqlite.h.in | 84 ++++++++++++++++++++++++++++++++++++++++++++++++ src/sqlite3ext.h | 6 ++++ src/sqliteInt.h | 9 +++--- src/vdbe.c | 21 ++++++++++++ src/vdbeInt.h | 7 ++++ src/vdbeapi.c | 77 ++++++++++++++++++++++++++++++++++++++++++++ src/where.c | 37 +++++++++++++++++++-- src/whereInt.h | 1 + src/wherecode.c | 25 ++++++++++---- 12 files changed, 277 insertions(+), 28 deletions(-) diff --git a/manifest b/manifest index 1b9116fe1a..6161852d73 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C CLI:\sTake\sextra\scare\sto\snot\ssplit\sa\smulti-byte\sunicode\scharacter\swhen\sdoing\nwordwrap. -D 2022-02-01T13:17:11.049 +C Add\snew\sinterfaces\sto\senable\svirtual\stable\sto\sprocess\sIN\soperator\sconstraints\nall\sat\sonce,\srather\sthan\sone\selement\sat\sa\stime. +D 2022-02-01T14:58:29.660 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -515,7 +515,7 @@ F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 1eea44389de3768ac98588c1410171cd53e7c6ad1af74049983dcbac82093de0 F src/json.c 78fdec9af3a8bfb5ae685707b2701276fec1942b8f5f26689b2701debe32bcd2 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa -F src/loadext.c 657534339585ac234839e5187aa51d8802f292e0771c4f874b3af1f1223f81e2 +F src/loadext.c aa919a6a7884f8b34d7b791841b24d14b1b0ab43f45b3940f4851043b2855c0c F src/main.c 2b6b0dbfeb14d4bb57e368604b0736b2aa42b51b00339d399b01d6b1fc9b4960 F src/malloc.c fec841aa0a0400a6f7d20706178a5d8e8219a6bf562b6fe712c17f6c26813266 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 @@ -554,10 +554,10 @@ F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2 F src/shell.c.in 2f58e6aa6b3d2012db32f1c5fa4591e9d12fd582904632b4fc8f57a382b98fd3 -F src/sqlite.h.in eaade58049152dac850d57415bcced885ca27ae9582f8aea2cfb7f1db78a521b +F src/sqlite.h.in 0aed2b88e91d03314121cd1e546441e37513929793c3cf7584b5b7ce445a9128 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 -F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a -F src/sqliteInt.h 8ef2996e02476f73e41ba977f819bda0cc68b7ce238cf404b9b8930df57bc1d0 +F src/sqlite3ext.h a95cb9ed106e3d39e2118e4dcc15a14faec3fa50d0093425083d340d9dfd96e6 +F src/sqliteInt.h 838df3e9ba9390058076d2f50c7efde9e0e8747303e788cf5bbe05402ab10924 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -624,10 +624,10 @@ F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c 602fe229f32a96ceccae4f40824129669582096f7c355f53dbac156c9fecef23 F src/vacuum.c 6c38ddc52f0619865c91dae9c441d4d48bf3040d7dc1bc5b22da1e45547ed0b3 -F src/vdbe.c cfe1980fbeb87eb35297b4a41808034761f26277cf45c9cf3e4eac20edcba1d5 +F src/vdbe.c d6694187a2819df7c2df3bd568fd059617c3edef4aa87e28a8121b02818f4ebf F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe -F src/vdbeInt.h d89d5d2150500cfb08615329fd20aea9d746bba5f2c3ecb8a17e2d2d9be029e5 -F src/vdbeapi.c 22c79072ae7d8a01e9bcae8ba16e918d60d202eaa9553b5fda38f99f7464d99a +F src/vdbeInt.h 24d58f12f642dcac102fa75d08e99ad06b6cbc66bf4948bb61e2e223ef9518b6 +F src/vdbeapi.c 4d26cc9eb1a0f937e8d360578dc56d00145bac08afd503ba6ac843007f7d8c1f F src/vdbeaux.c e761b8011baec7a4773f0a7594783f2cd71f699ab187c4aad917529ab8acd3fe F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd F src/vdbemem.c eb6042667c02c3ef1f968235b4a170e31b23a4b6a57f65a8454eab4d36f14b7f @@ -639,9 +639,9 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b -F src/where.c c4a80044708b1000a2b875a6623acfb636acd1c740063b60ea50e359d305829e -F src/whereInt.h 0748a6fce98b41862445906922a809146ff7ef4de16ed9022b0bc4e5c43aa60a -F src/wherecode.c c313ccf5ed13dc7e88c64f93733f414dee369a212508a866878696d83c64fc36 +F src/where.c 45173f696570d3bc5a939c80c147afe7b79763c6402d3ba7f0dc5477153afa59 +F src/whereInt.h 1d821657238a0bd12b3c8f2926c7f8f9294bc5efe20af53c7c50d53a0a026cb9 +F src/wherecode.c 5879604677f0bdfb8d95ff616d834daecc12256346b7d9ad96a7e84a1cb08fdc F src/whereexpr.c ddb6ab49f745154c37dbdb291433c933e00175929647290a11f487af701d0392 F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 @@ -1942,8 +1942,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 1b528e31f8c62797e0814568b520c0680ff23a2ee877ca6aa70a167d40ebdf80 -R fe91994efd36a88115500529351b7e2e +P 00b1b7020a564976da3237532434e47ccf17eb5d620e6ac45f3e70b5d5739200 +R c34f556d2df8080b897efb591747c003 +T *branch * batch-in-operator +T *sym-batch-in-operator * +T -sym-trunk * U drh -Z 4d3a3178c78a3baab7f87b0b2d56c876 +Z 972db892fdcb2bcf2d566ac41a6d13d0 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0347e193b4..5d98a43c51 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -00b1b7020a564976da3237532434e47ccf17eb5d620e6ac45f3e70b5d5739200 \ No newline at end of file +eb84b80e1f6d8c32bf0c9e1731f0233de0160a13f714f766779ae01fdf504e7b \ No newline at end of file diff --git a/src/loadext.c b/src/loadext.c index a4aad7e748..603516e18a 100644 --- a/src/loadext.c +++ b/src/loadext.c @@ -489,6 +489,9 @@ static const sqlite3_api_routines sqlite3Apis = { sqlite3_error_offset, sqlite3_vtab_rhs_value, sqlite3_vtab_distinct, + sqlite3_vtab_in, + sqlite3_vtab_in_first, + sqlite3_vtab_in_next }; /* True if x is the directory separator character diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 82f844d6a7..40aa917757 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -9604,6 +9604,90 @@ SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int); */ int sqlite3_vtab_distinct(sqlite3_index_info*); +/* +** CAPI3REF: Identify and handle IN(...) constraints in xBestIndex +** +** This API may only be used from within an xBestIndex() callback. The +** results of calling it from outside of an xBestIndex() callback are +** undefined. +** +** When a column of a virtual table is subject to a "col IN (...)" +** constraint, this is passed through to the xBestIndex method of the +** virtual table as an SQLITE_INDEX_CONSTRAINT_EQ constraint. Then, if +** the virtual table indicates that it can handle the constraint, SQLite +** uses the xFilter and xNext methods of the virtual table to query +** separately for each distinct element in RHS of the IN(...) expression. +** This API allows a virtual table implementation to determine which +** SQLITE_INDEX_CONSTRAINT_EQ constraints are actually IN(...) constraints, +** and to handle them using a single xFilter scan. +** +** If the second argument passed to this function is not the index of an +** SQLITE_INDEX_CONSTRAINT_EQ constraint in the aConstraint[] array of the +** sqlite3_index_info object, or if the SQLITE_INDEX_CONSTRAINT_EQ is not +** really an IN(...) expression, then this function is a no-op. Zero is +** returned in this case. +** +** Otherwise, if parameter iCons is the index of an SQLITE_INDEX_CONSTRAINT_EQ +** constraint that is really an IN(...) expression, then this function +** returns non-zero. In this case, the call also specifies whether SQLite +** should invoke xFilter() once for each element on the RHS of the IN(...) +** expression (the default, if bHandle is zero), or just once for the entire +** list (if bHandle is non-zero), should the associated +** aConstraintUsage[].argvIndex variable be set by xBestIndex. +** +** In cases where the list on the RHS of an IN(...) constraint is passed to a +** single xFilter() call, the (sqlite3_value*) passed appears in most +** respects to be a NULL value. Except, it may be used with the +** sqlite3_vtab_in_first() and sqlite3_vtab_in_next() APIs to interate through +** the list of values. +*/ +int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle); + +/* +** CAPI3REF: Visit the first element of an IN(...) list passed to xFilter +** +** This API may only be used from within an xFilter() callback. The +** results of calling it from outside of an xFilter() callback are +** undefined. +** +** If the (sqlite3_value*) passed as the first argument to this function +** is not a value representing the RHS of an IN(...) operator (see +** API function sqlite3_vtab_in()), of if the RHS of the IN(...) operator +** is an empty set, this function sets (*ppOut) to NULL and returns +** SQLITE_OK. +** +** Otherwise, if no error occurs, it sets (*ppOut) to point to an object +** containing the first value in the list and returns SQLITE_OK. The +** pointer is valid until either the xFilter() call returns or until the +** next call to sqlite3_vtab_in_first() or sqlite3_vtab_in_next() with +** the same first argument. +** +** If an error occurs, (*ppOut) is set to NULL and an SQLite error code +** returned. +*/ +int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut); + +/* +** CAPI3REF: Visit the next element of an IN(...) list passed to xFilter +** +** This function is called after a successful call to sqlite3_vtab_in_first() +** to visit the next and subsequent elements of an IN(...) list passed +** to an xFilter callback. Example usage: +** +**
    +**   for(rc=sqlite3_vtab_in_first(pList, &pVal);
    +**       rc==SQLITE_OK && pVal
    +**       rc=sqlite3_vtab_in_next(pList, &pVal)
    +**   ){
    +**     // do something with pVal
    +**   }
    +**   if( rc!=SQLITE_OK ){
    +**     // an error has occurred
    +**   }
    +** 
    +*/ +int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut); + /* ** CAPI3REF: Constraint values in xBestIndex() ** METHOD: sqlite3_index_info diff --git a/src/sqlite3ext.h b/src/sqlite3ext.h index 88010b9d8d..2eac4f3f05 100644 --- a/src/sqlite3ext.h +++ b/src/sqlite3ext.h @@ -348,6 +348,9 @@ struct sqlite3_api_routines { int (*error_offset)(sqlite3*); int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**); int (*vtab_distinct)(sqlite3_index_info*); + int (*vtab_in)(sqlite3_index_info*,int,int); + int (*vtab_in_first)(sqlite3_value*,sqlite3_value**); + int (*vtab_in_next)(sqlite3_value*,sqlite3_value**); }; /* @@ -663,6 +666,9 @@ typedef int (*sqlite3_loadext_entry)( #define sqlite3_error_offset sqlite3_api->error_offset #define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value #define sqlite3_vtab_distinct sqlite3_api->vtab_distinct +#define sqlite3_vtab_in sqlite3_api->vtab_in +#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first +#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 6d93650713..374fc95541 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1231,10 +1231,11 @@ typedef struct With With; /* ** A bit in a Bitmask */ -#define MASKBIT(n) (((Bitmask)1)<<(n)) -#define MASKBIT64(n) (((u64)1)<<(n)) -#define MASKBIT32(n) (((unsigned int)1)<<(n)) -#define ALLBITS ((Bitmask)-1) +#define MASKBIT(n) (((Bitmask)1)<<(n)) +#define MASKBIT64(n) (((u64)1)<<(n)) +#define MASKBIT32(n) (((unsigned int)1)<<(n)) +#define SMASKBIT32(n) ((n)<=31?((unsigned int)1)<<(n):0) +#define ALLBITS ((Bitmask)-1) /* A VList object records a mapping between parameters/variables/wildcards ** in the SQL statement (such as $abc, @pqr, or :xyz) and the integer diff --git a/src/vdbe.c b/src/vdbe.c index d4ff33141b..5643d43ce9 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -7734,6 +7734,27 @@ case OP_VOpen: { } #endif /* SQLITE_OMIT_VIRTUALTABLE */ +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* Opcode: VInitIn P1 P2 P3 * * +** Synopsis: r[P2]=cursor over eph table P1. +** +** Initialize register P2 as a value that can be used as an iterator over +** the contents of ephemeral table P1 by an xFilter callback implementation. +** Register P3 is used as a cache by the iterator. +*/ +case OP_VInitIn: { /* out2 */ + VdbeCursor *pC; + pC = p->apCsr[pOp->p1]; + pOut = out2Prerelease(p, pOp); + pOut->z = (char*)(pC->uc.pCursor); + pOut->u.pVal = &aMem[pOp->p3]; + pOut->uTemp = SQLITE_VTAB_IN_MAGIC; + pOut->flags = MEM_Null; + break; +} +#endif /* SQLITE_OMIT_VIRTUALTABLE */ + + #ifndef SQLITE_OMIT_VIRTUALTABLE /* Opcode: VFilter P1 P2 P3 P4 * ** Synopsis: iplan=r[P3] zplan='P4' diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 376c9edac9..9ddb742a49 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -195,6 +195,12 @@ struct VdbeFrame { */ #define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))]) +/* Magic number for Mem.uTemp when it is acting as as the cache for the +** IN(...) iterator for sqlite3_vtab_in_next() +*/ +#define SQLITE_VTAB_IN_MAGIC 0xd3ab12ec + + /* ** Internally, the vdbe manipulates nearly all SQL values as Mem ** structures. Each Mem struct may cache multiple representations (string, @@ -207,6 +213,7 @@ struct sqlite3_value { int nZero; /* Extra zero bytes when MEM_Zero and MEM_Blob set */ const char *zPType; /* Pointer type when MEM_Term|MEM_Subtype|MEM_Null */ FuncDef *pDef; /* Used only when flags==MEM_Agg */ + sqlite3_value *pVal;/* Current value for xFilter IN(...) iterator */ } u; u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */ diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 17df807de4..1521eee79b 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -846,6 +846,83 @@ int sqlite3_vtab_nochange(sqlite3_context *p){ return sqlite3_value_nochange(p->pOut); } +/* +** The first argument is an iterator value created by VDBE instruction +** OP_VInitIn. The iterator is guaranteed to point to a valid entry. This +** function attempts to load the current value from the iterator into +** object pVal->u.pVal. If successful, (*ppOut) is set to point to +** pVal->u.pVal and SQLITE_OK is returned. Otherwise, if an error +** occurs, an SQLite error code is returned and (*ppOut) is left unchanged. +*/ +static int vtabInLoadValue(sqlite3_value *pVal, sqlite3_value **ppOut){ + BtCursor *pCsr = (BtCursor*)pVal->z; + sqlite3_value *pOut = pVal->u.pVal; + int sz; + int rc; + + sz = (int)sqlite3BtreePayloadSize(pCsr); + if( sz>pVal->szMalloc ){ + if( pVal->szMalloc==0 ) pVal->zMalloc = 0; + pVal->zMalloc = sqlite3DbReallocOrFree(pVal->db, pVal->zMalloc, sz*2); + if( pVal->zMalloc ){ + pVal->szMalloc = sqlite3DbMallocSize(pVal->db, pVal->zMalloc); + }else{ + pVal->szMalloc = 0; + return SQLITE_NOMEM_BKPT; + } + } + + rc = sqlite3BtreePayload(pCsr, 0, sz, pVal->zMalloc); + if( rc==SQLITE_OK ){ + u32 iSerial; + int iOff = 1 + getVarint32((const u8*)&pVal->zMalloc[1], iSerial); + sqlite3VdbeSerialGet((const u8*)&pVal->zMalloc[iOff], iSerial, pOut); + pOut->enc = ENC(pVal->db); + *ppOut = pOut; + } + return rc; +} + +/* +** Implementation of sqlite3_vtab_in_first() (if bNext==0) and +** sqlite3_vtab_in_next() (if bNext!=0). +*/ +static int vtabInOp(sqlite3_value *pVal, sqlite3_value **ppOut, int bNext){ + int rc = SQLITE_OK; + *ppOut = 0; + if( pVal && pVal->uTemp==SQLITE_VTAB_IN_MAGIC ){ + BtCursor *pCsr = (BtCursor*)pVal->z; + + if( bNext ){ + rc = sqlite3BtreeNext(pCsr, 0); + }else{ + int dummy = 0; + rc = sqlite3BtreeFirst(pCsr, &dummy); + } + + if( rc==SQLITE_OK && sqlite3BtreeEof(pCsr)==0 ){ + rc = vtabInLoadValue(pVal, ppOut); + } + } + return rc; +} + +/* +** Set the iterator value pVal to point to the first value in the set. +** Set (*ppOut) to point to this value before returning. +*/ +int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut){ + return vtabInOp(pVal, ppOut, 0); +} + +/* +** Set the iterator value pVal to point to the next value in the set. +** Set (*ppOut) to point to this value before returning. +*/ +int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut){ + return vtabInOp(pVal, ppOut, 1); +} + /* ** Return the current time for a statement. If the current time ** is requested more than once within the same run of a single prepared diff --git a/src/where.c b/src/where.c index 4efb7084da..0db38ed7cb 100644 --- a/src/where.c +++ b/src/where.c @@ -33,6 +33,8 @@ struct HiddenIndexInfo { WhereClause *pWC; /* The Where clause being analyzed */ Parse *pParse; /* The parsing context */ int eDistinct; /* Value to return from sqlite3_vtab_distinct() */ + u32 mIn; /* Mask of terms that are
    IN (...) */ + u32 mHandleIn; /* Terms that vtab will handle as IN (...) */ sqlite3_value *aRhs[1]; /* RHS values for constraints. MUST BE LAST ** because extra space is allocated to hold up ** to nTerm such values */ @@ -1119,6 +1121,7 @@ static sqlite3_index_info *allocateIndexInfo( int nOrderBy; sqlite3_index_info *pIdxInfo; u16 mNoOmit = 0; + u32 mIn = 0; const Table *pTab; int eDistinct = 0; ExprList *pOrderBy = pWInfo->pOrderBy; @@ -1143,6 +1146,12 @@ static sqlite3_index_info *allocateIndexInfo( testcase( pTerm->eOperator & WO_ALL ); if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue; if( pTerm->wtFlags & TERM_VNULL ) continue; + if( (pTerm->eOperator & WO_IN)!=0 + && ExprHasProperty(pTerm->pExpr, EP_xIsSelect)==0 + ){ + mIn |= SMASKBIT32(i); + } + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); assert( pTerm->u.x.leftColumn>=XN_ROWID ); assert( pTerm->u.x.leftColumnnCol ); @@ -1204,7 +1213,7 @@ static sqlite3_index_info *allocateIndexInfo( /* No matches cause a break out of the loop */ break; } - if( i==n){ + if( i==n ){ nOrderBy = n; if( (pWInfo->wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY)) ){ eDistinct = 1 + ((pWInfo->wctrlFlags & WHERE_DISTINCTBY)!=0); @@ -1232,6 +1241,7 @@ static sqlite3_index_info *allocateIndexInfo( pHidden->pWC = pWC; pHidden->pParse = pParse; pHidden->eDistinct = eDistinct; + pHidden->mIn = mIn; for(i=j=0, pTerm=pWC->a; inTerm; i++, pTerm++){ u16 op; if( (pTerm->wtFlags & TERM_OK)==0 ) continue; @@ -3499,6 +3509,7 @@ static int whereLoopAddVirtualOne( int *pbRetryLimit /* OUT: Retry without LIMIT/OFFSET */ ){ WhereClause *pWC = pBuilder->pWC; + HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; struct sqlite3_index_constraint *pIdxCons; struct sqlite3_index_constraint_usage *pUsage = pIdxInfo->aConstraintUsage; int i; @@ -3537,6 +3548,7 @@ static int whereLoopAddVirtualOne( pIdxInfo->estimatedRows = 25; pIdxInfo->idxFlags = 0; pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed; + pHidden->mHandleIn = 0; /* Invoke the virtual table xBestIndex() method */ rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo); @@ -3593,7 +3605,9 @@ static int whereLoopAddVirtualOne( pNew->u.vtab.bOmitOffset = 1; } } - if( (pTerm->eOperator & WO_IN)!=0 ){ + if( SMASKBIT32(i) & pHidden->mHandleIn ){ + pNew->u.vtab.mHandleIn |= SMASKBIT32(iTerm); + }else if( (pTerm->eOperator & WO_IN)!=0 ){ /* A virtual table that is constrained by an IN clause may not ** consume the ORDER BY clause because (1) the order of IN terms ** is not necessarily related to the order of output terms and @@ -3691,6 +3705,25 @@ const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int iCons){ return zRet; } +/* +** Return true if constraint iCons is really an IN(...) constraint, or +** false otherwise. If iCons is an IN(...) constraint, set (if bHandle!=0) +** or clear (if bHandle==0) the flag to handle it using an iterator. +*/ +int sqlite3_vtab_in(sqlite3_index_info *pIdxInfo, int iCons, int bHandle){ + HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; + u32 m = SMASKBIT32(iCons); + if( m & pHidden->mIn ){ + if( bHandle==0 ){ + pHidden->mHandleIn &= ~m; + }else{ + pHidden->mHandleIn |= m; + } + return 1; + } + return 0; +} + /* ** This interface is callable from within the xBestIndex callback only. ** diff --git a/src/whereInt.h b/src/whereInt.h index 14c40fcb43..fc3740f519 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -128,6 +128,7 @@ struct WhereLoop { i8 isOrdered; /* True if satisfies ORDER BY */ u16 omitMask; /* Terms that may be omitted */ char *idxStr; /* Index identifier string */ + u32 mHandleIn; /* Terms to handle as IN(...) instead of == */ } vtab; } u; u32 wsFlags; /* WHERE_* flags describing the plan */ diff --git a/src/wherecode.c b/src/wherecode.c index 2beb596e69..8d7163ce08 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -1532,8 +1532,15 @@ Bitmask sqlite3WhereCodeOneLoopStart( pTerm = pLoop->aLTerm[j]; if( NEVER(pTerm==0) ) continue; if( pTerm->eOperator & WO_IN ){ - codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget); - addrNotFound = pLevel->addrNxt; + if( SMASKBIT32(j) & pLoop->u.vtab.mHandleIn ){ + int iTab = pParse->nTab++; + int iCache = ++pParse->nMem; + sqlite3CodeRhsOfIN(pParse, pTerm->pExpr, iTab); + sqlite3VdbeAddOp3(v, OP_VInitIn, iTab, iTarget, iCache); + }else{ + codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget); + addrNotFound = pLevel->addrNxt; + } }else{ Expr *pRight = pTerm->pExpr->pRight; codeExprOrVector(pParse, pRight, iTarget, 1); @@ -1568,13 +1575,19 @@ Bitmask sqlite3WhereCodeOneLoopStart( iIn = 0; } for(j=nConstraint-1; j>=0; j--){ + int bIn; /* True to generate byte code to loop over RHS IN values */ pTerm = pLoop->aLTerm[j]; - if( (pTerm->eOperator & WO_IN)!=0 ) iIn--; + if( (pTerm->eOperator & WO_IN)!=0 + && (SMASKBIT32(j) & pLoop->u.vtab.mHandleIn)==0 + ){ + bIn = 1; + }else{ + bIn = 0; + } + if( bIn ) iIn--; if( j<16 && (pLoop->u.vtab.omitMask>>j)&1 ){ disableTerm(pLevel, pTerm); - }else if( (pTerm->eOperator & WO_IN)!=0 - && sqlite3ExprVectorSize(pTerm->pExpr->pLeft)==1 - ){ + }else if( bIn && sqlite3ExprVectorSize(pTerm->pExpr->pLeft)==1 ){ Expr *pCompare; /* The comparison operator */ Expr *pRight; /* RHS of the comparison */ VdbeOp *pOp; /* Opcode to access the value of the IN constraint */ From a9f18f0172d01cf1da8e962dcd33010fac28ef56 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 1 Feb 2022 16:30:57 +0000 Subject: [PATCH 26/40] Index in 2nd argument to sqlite3_vtab_in() should be on the aConstraint[] array, not the internal array of all constraints. FossilOrigin-Name: 5acf90a931b27b7d627c0a8fee68170430e09b028d6643b959b0ec14fd59f7ac --- manifest | 15 ++++++--------- manifest.uuid | 2 +- src/where.c | 15 +++++++-------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/manifest b/manifest index 6161852d73..7b186939fd 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\snew\sinterfaces\sto\senable\svirtual\stable\sto\sprocess\sIN\soperator\sconstraints\nall\sat\sonce,\srather\sthan\sone\selement\sat\sa\stime. -D 2022-02-01T14:58:29.660 +C Index\sin\s2nd\sargument\sto\ssqlite3_vtab_in()\sshould\sbe\son\sthe\saConstraint[]\narray,\snot\sthe\sinternal\sarray\sof\sall\sconstraints. +D 2022-02-01T16:30:57.444 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -639,7 +639,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b -F src/where.c 45173f696570d3bc5a939c80c147afe7b79763c6402d3ba7f0dc5477153afa59 +F src/where.c cbf02091ed71784f7972ca39aaecb22822781f3b96905ece1a30e80161629769 F src/whereInt.h 1d821657238a0bd12b3c8f2926c7f8f9294bc5efe20af53c7c50d53a0a026cb9 F src/wherecode.c 5879604677f0bdfb8d95ff616d834daecc12256346b7d9ad96a7e84a1cb08fdc F src/whereexpr.c ddb6ab49f745154c37dbdb291433c933e00175929647290a11f487af701d0392 @@ -1942,11 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 00b1b7020a564976da3237532434e47ccf17eb5d620e6ac45f3e70b5d5739200 -R c34f556d2df8080b897efb591747c003 -T *branch * batch-in-operator -T *sym-batch-in-operator * -T -sym-trunk * +P eb84b80e1f6d8c32bf0c9e1731f0233de0160a13f714f766779ae01fdf504e7b +R 320e3be3dedfcb44854348cea4a95b96 U drh -Z 972db892fdcb2bcf2d566ac41a6d13d0 +Z 4a8e49183eb36a5777c7e6fa2fd8d0f2 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5d98a43c51..93bc0e7791 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -eb84b80e1f6d8c32bf0c9e1731f0233de0160a13f714f766779ae01fdf504e7b \ No newline at end of file +5acf90a931b27b7d627c0a8fee68170430e09b028d6643b959b0ec14fd59f7ac \ No newline at end of file diff --git a/src/where.c b/src/where.c index 0db38ed7cb..4f65e934fc 100644 --- a/src/where.c +++ b/src/where.c @@ -1121,7 +1121,6 @@ static sqlite3_index_info *allocateIndexInfo( int nOrderBy; sqlite3_index_info *pIdxInfo; u16 mNoOmit = 0; - u32 mIn = 0; const Table *pTab; int eDistinct = 0; ExprList *pOrderBy = pWInfo->pOrderBy; @@ -1146,11 +1145,6 @@ static sqlite3_index_info *allocateIndexInfo( testcase( pTerm->eOperator & WO_ALL ); if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue; if( pTerm->wtFlags & TERM_VNULL ) continue; - if( (pTerm->eOperator & WO_IN)!=0 - && ExprHasProperty(pTerm->pExpr, EP_xIsSelect)==0 - ){ - mIn |= SMASKBIT32(i); - } assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); assert( pTerm->u.x.leftColumn>=XN_ROWID ); @@ -1241,14 +1235,19 @@ static sqlite3_index_info *allocateIndexInfo( pHidden->pWC = pWC; pHidden->pParse = pParse; pHidden->eDistinct = eDistinct; - pHidden->mIn = mIn; + pHidden->mIn = 0; for(i=j=0, pTerm=pWC->a; inTerm; i++, pTerm++){ u16 op; if( (pTerm->wtFlags & TERM_OK)==0 ) continue; pIdxCons[j].iColumn = pTerm->u.x.leftColumn; pIdxCons[j].iTermOffset = i; op = pTerm->eOperator & WO_ALL; - if( op==WO_IN ) op = WO_EQ; + if( op==WO_IN ){ + if( ExprHasProperty(pTerm->pExpr, EP_xIsSelect)==0 ){ + pHidden->mIn |= SMASKBIT32(j); + } + op = WO_EQ; + } if( op==WO_AUX ){ pIdxCons[j].op = pTerm->eMatchOp; }else if( op & (WO_ISNULL|WO_IS) ){ From b30298d3ea05b73fb9c7d1556f6b2b054dd33d7a Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 1 Feb 2022 21:59:43 +0000 Subject: [PATCH 27/40] Tweaks to the sqlite3_vtab_in() interface. FossilOrigin-Name: 75040183b8e14f20bfedfdcc1a9fb968f2f0193bc698605d1b4791a3699b93d9 --- manifest | 16 +++--- manifest.uuid | 2 +- src/sqlite.h.in | 144 +++++++++++++++++++++++++++++------------------- src/vdbeapi.c | 28 +++++----- src/where.c | 4 +- 5 files changed, 112 insertions(+), 82 deletions(-) diff --git a/manifest b/manifest index 7b186939fd..6f0bf53a93 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Index\sin\s2nd\sargument\sto\ssqlite3_vtab_in()\sshould\sbe\son\sthe\saConstraint[]\narray,\snot\sthe\sinternal\sarray\sof\sall\sconstraints. -D 2022-02-01T16:30:57.444 +C Tweaks\sto\sthe\ssqlite3_vtab_in()\sinterface. +D 2022-02-01T21:59:43.937 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -554,7 +554,7 @@ F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2 F src/shell.c.in 2f58e6aa6b3d2012db32f1c5fa4591e9d12fd582904632b4fc8f57a382b98fd3 -F src/sqlite.h.in 0aed2b88e91d03314121cd1e546441e37513929793c3cf7584b5b7ce445a9128 +F src/sqlite.h.in 72f3e57c4c0b4284ab9238312f7fd797967cc43f44558a80469a3d9b86a7be2b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h a95cb9ed106e3d39e2118e4dcc15a14faec3fa50d0093425083d340d9dfd96e6 F src/sqliteInt.h 838df3e9ba9390058076d2f50c7efde9e0e8747303e788cf5bbe05402ab10924 @@ -627,7 +627,7 @@ F src/vacuum.c 6c38ddc52f0619865c91dae9c441d4d48bf3040d7dc1bc5b22da1e45547ed0b3 F src/vdbe.c d6694187a2819df7c2df3bd568fd059617c3edef4aa87e28a8121b02818f4ebf F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe F src/vdbeInt.h 24d58f12f642dcac102fa75d08e99ad06b6cbc66bf4948bb61e2e223ef9518b6 -F src/vdbeapi.c 4d26cc9eb1a0f937e8d360578dc56d00145bac08afd503ba6ac843007f7d8c1f +F src/vdbeapi.c 84e7e8d161c8fb7259eaa5fe7234f2334ef9fb013674ce34705b56166052b5fa F src/vdbeaux.c e761b8011baec7a4773f0a7594783f2cd71f699ab187c4aad917529ab8acd3fe F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd F src/vdbemem.c eb6042667c02c3ef1f968235b4a170e31b23a4b6a57f65a8454eab4d36f14b7f @@ -639,7 +639,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b -F src/where.c cbf02091ed71784f7972ca39aaecb22822781f3b96905ece1a30e80161629769 +F src/where.c 392d552fa6636e94d242954247e277eb9b0d9c45446afb0c0a57f8c6fcb3f792 F src/whereInt.h 1d821657238a0bd12b3c8f2926c7f8f9294bc5efe20af53c7c50d53a0a026cb9 F src/wherecode.c 5879604677f0bdfb8d95ff616d834daecc12256346b7d9ad96a7e84a1cb08fdc F src/whereexpr.c ddb6ab49f745154c37dbdb291433c933e00175929647290a11f487af701d0392 @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P eb84b80e1f6d8c32bf0c9e1731f0233de0160a13f714f766779ae01fdf504e7b -R 320e3be3dedfcb44854348cea4a95b96 +P 5acf90a931b27b7d627c0a8fee68170430e09b028d6643b959b0ec14fd59f7ac +R e3dc8c48dc1f78fd77820f7d72429762 U drh -Z 4a8e49183eb36a5777c7e6fa2fd8d0f2 +Z 89712fde261ff7ce2e71753d08fff668 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 93bc0e7791..bfff22a8a9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5acf90a931b27b7d627c0a8fee68170430e09b028d6643b959b0ec14fd59f7ac \ No newline at end of file +75040183b8e14f20bfedfdcc1a9fb968f2f0193bc698605d1b4791a3699b93d9 \ No newline at end of file diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 40aa917757..e8122912ee 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -9607,72 +9607,94 @@ int sqlite3_vtab_distinct(sqlite3_index_info*); /* ** CAPI3REF: Identify and handle IN(...) constraints in xBestIndex ** -** This API may only be used from within an xBestIndex() callback. The -** results of calling it from outside of an xBestIndex() callback are -** undefined. +** This interface may only be used from within an +** [xBestIndex|xBestIndex() method of a [virtual table] implementation. +** The result of invoking this interface from any other context is +** undefined and probably harmful. ** -** When a column of a virtual table is subject to a "col IN (...)" -** constraint, this is passed through to the xBestIndex method of the -** virtual table as an SQLITE_INDEX_CONSTRAINT_EQ constraint. Then, if -** the virtual table indicates that it can handle the constraint, SQLite -** uses the xFilter and xNext methods of the virtual table to query -** separately for each distinct element in RHS of the IN(...) expression. -** This API allows a virtual table implementation to determine which -** SQLITE_INDEX_CONSTRAINT_EQ constraints are actually IN(...) constraints, -** and to handle them using a single xFilter scan. +** A constraint on a virtual table of the form "column IN (...)" is +** communicated to the xBestIndex method as a +** [SQLITE_INDEX_CONSTRAINT_EQ] constraint. If xBestIndex wants to use +** this constraint, it must set the corresponding +** aConstraintUsage[].argvIndex to a postive integer. Then, under +** the usual mode of handling IN operators, SQLite generate bytecode +** that invokes the [xFilter|xFilter() method] once for each value +** on the right-hand side of the IN operator. Thus the virtual table +** only sees a single value from the right-hand side of the IN operator +** at a time. +** +** In some cases, however, it would be advantageous for the virtual +** table to see all values on the right-hand of the IN operator all at +** once. The sqlite3_vtab_in() interfaces facilitates this in two ways: +** +**
      +**
    1. +** A call to sqlite3_vtab_in(P,I,-1) will return true (non-zero) +** if and only if the I-th constraint in P->aConstraint[] is +** an IN operator that can be processed all at once. In other words, +** sqlite3_vtab_in() with -1 in the third argument is a mechanism +** by which the virtual table can ask SQLite if all-at-once processing +** of the IN operator is even possible. +** +**

    2. +** A call to sqlite3_vtab_in(P,I,F) with F set to 1 or 0 indicates +** to SQLite that the virtual table does or does not want to process +** the IN operator all-at-once. Thus when the third parameter (F) is +** non-negative, this interface is the mechanism by which the virtual +** table tells SQLite how it wants to process in IN operator. +**

    +** +** The sqlite3_vtab_in(P,I,F) interface can be invoked multiple times +** within the same xBestIndex method call. For any given P and I parameters, +** the return value from sqlite3_vtab_in(P,I,F) will always be the same +** for every invocation within the same xBestIndex call. If the interface +** returns true (non-zero), that means that the constraint is an IN operator +** that can be processed all-at-once. If the constraint is not an IN +** operator or cannot be processed all-at-once, then the interface returns +** false. +** +** All-at-once processing of the IN operator is selected if both of the +** following conditions are met: +** +**
      +**
    1. The P->aConstraintUsage[I].argvIndex value is set to a positive +** integer. This is how the virtual table tells SQLite that it wants to +** use the I-th constraint. +** +**

    2. The last call to sqlite3_vtab_in(P,I,F) for which F was +** non-negative had F>=1. +**

    ** -** If the second argument passed to this function is not the index of an -** SQLITE_INDEX_CONSTRAINT_EQ constraint in the aConstraint[] array of the -** sqlite3_index_info object, or if the SQLITE_INDEX_CONSTRAINT_EQ is not -** really an IN(...) expression, then this function is a no-op. Zero is -** returned in this case. -** -** Otherwise, if parameter iCons is the index of an SQLITE_INDEX_CONSTRAINT_EQ -** constraint that is really an IN(...) expression, then this function -** returns non-zero. In this case, the call also specifies whether SQLite -** should invoke xFilter() once for each element on the RHS of the IN(...) -** expression (the default, if bHandle is zero), or just once for the entire -** list (if bHandle is non-zero), should the associated -** aConstraintUsage[].argvIndex variable be set by xBestIndex. -** -** In cases where the list on the RHS of an IN(...) constraint is passed to a -** single xFilter() call, the (sqlite3_value*) passed appears in most -** respects to be a NULL value. Except, it may be used with the -** sqlite3_vtab_in_first() and sqlite3_vtab_in_next() APIs to interate through -** the list of values. +** If either or both of the conditions above are false, then uses the +** traditional one-at-a-time processing strategy for IN constraint. +** If both conditions are true, then the argvIndex-th parameter to the +** xFilter method will be an [sqlite3_value] that appears to be NULL, +** but which can be passed to [sqlite3_vtab_in_first()] and +** [sqlite3_vtab_in_next()] to find all values on the right-hand side +** of the IN constraint. */ int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle); /* -** CAPI3REF: Visit the first element of an IN(...) list passed to xFilter +** CAPI3REF: Find all elements on the right-hand side of an IN constraint. ** -** This API may only be used from within an xFilter() callback. The -** results of calling it from outside of an xFilter() callback are -** undefined. +** These interfaces are only useful from within the +** [xFilter|xFilter() method] of a virtual table implementation. +** The result of invoking these interfaces from any other context +** is undefined and probably harmful. ** -** If the (sqlite3_value*) passed as the first argument to this function -** is not a value representing the RHS of an IN(...) operator (see -** API function sqlite3_vtab_in()), of if the RHS of the IN(...) operator -** is an empty set, this function sets (*ppOut) to NULL and returns -** SQLITE_OK. +** The X parameter in a call to sqlite3_vtab_in_first(X,P) or +** sqlite3_vtab_in_next(X,P) must be one of the parameters to the +** xFilter method which invokes those routines, and specifically +** a parameter that was previously selected for all-at-once IN constraint +** processing use the [sqlite3_vtab_in()] interface in the +** [xBestIndex|xBestIndex method]. If the X parameter is not +** an xFilter argument that was selected for all-at-once IN constraint +** processing, then these routines return SQLITE_MISUSE or perhaps +** exhibit some other undefined or harmful behavior. ** -** Otherwise, if no error occurs, it sets (*ppOut) to point to an object -** containing the first value in the list and returns SQLITE_OK. The -** pointer is valid until either the xFilter() call returns or until the -** next call to sqlite3_vtab_in_first() or sqlite3_vtab_in_next() with -** the same first argument. -** -** If an error occurs, (*ppOut) is set to NULL and an SQLite error code -** returned. -*/ -int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut); - -/* -** CAPI3REF: Visit the next element of an IN(...) list passed to xFilter -** -** This function is called after a successful call to sqlite3_vtab_in_first() -** to visit the next and subsequent elements of an IN(...) list passed -** to an xFilter callback. Example usage: +** Use these routines to access all values on the right-hand side +** of the IN constraint using code like the following: ** **
     **   for(rc=sqlite3_vtab_in_first(pList, &pVal);
    @@ -9685,7 +9707,15 @@ int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut);
     **     // an error has occurred
     **   }
     ** 
    +** +** On success, the sqlite3_vtab_in_first(X,P) and sqlite3_vtab_in_next(X,P) +** routines return SQLITE_OK and set *P to point to the first or next value +** on the RHS of the IN constraint. If there are no more values on the +** right hand side of the IN constraint, then *P is set to NULL and these +** routines return [SQLITE_DONE]. The return value might be +** some other value, such as SQLITE_NOMEM, in the event of a malfunction. */ +int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut); int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut); /* diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 1521eee79b..46b466b6bb 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -888,21 +888,21 @@ static int vtabInLoadValue(sqlite3_value *pVal, sqlite3_value **ppOut){ ** sqlite3_vtab_in_next() (if bNext!=0). */ static int vtabInOp(sqlite3_value *pVal, sqlite3_value **ppOut, int bNext){ - int rc = SQLITE_OK; + int rc; + BtCursor *pCsr; *ppOut = 0; - if( pVal && pVal->uTemp==SQLITE_VTAB_IN_MAGIC ){ - BtCursor *pCsr = (BtCursor*)pVal->z; - - if( bNext ){ - rc = sqlite3BtreeNext(pCsr, 0); - }else{ - int dummy = 0; - rc = sqlite3BtreeFirst(pCsr, &dummy); - } - - if( rc==SQLITE_OK && sqlite3BtreeEof(pCsr)==0 ){ - rc = vtabInLoadValue(pVal, ppOut); - } + if( pVal==0 ) return SQLITE_MISUSE; + if( pVal->uTemp!=SQLITE_VTAB_IN_MAGIC ) return SQLITE_MISUSE; + pCsr = (BtCursor*)pVal->z; + if( bNext ){ + rc = sqlite3BtreeNext(pCsr, 0); + }else{ + int dummy = 0; + rc = sqlite3BtreeFirst(pCsr, &dummy); + if( rc==SQLITE_OK && sqlite3BtreeEof(pCsr) ) rc = SQLITE_DONE; + } + if( rc==SQLITE_OK ){ + rc = vtabInLoadValue(pVal, ppOut); } return rc; } diff --git a/src/where.c b/src/where.c index 4f65e934fc..169c4ccf14 100644 --- a/src/where.c +++ b/src/where.c @@ -3605,7 +3605,7 @@ static int whereLoopAddVirtualOne( } } if( SMASKBIT32(i) & pHidden->mHandleIn ){ - pNew->u.vtab.mHandleIn |= SMASKBIT32(iTerm); + pNew->u.vtab.mHandleIn |= MASKBIT32(iTerm); }else if( (pTerm->eOperator & WO_IN)!=0 ){ /* A virtual table that is constrained by an IN clause may not ** consume the ORDER BY clause because (1) the order of IN terms @@ -3715,7 +3715,7 @@ int sqlite3_vtab_in(sqlite3_index_info *pIdxInfo, int iCons, int bHandle){ if( m & pHidden->mIn ){ if( bHandle==0 ){ pHidden->mHandleIn &= ~m; - }else{ + }else if( bHandle>0 ){ pHidden->mHandleIn |= m; } return 1; From 9ede896ac15ef680f107aa77df6ab3e7163b0093 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 2 Feb 2022 11:37:49 +0000 Subject: [PATCH 28/40] Fix a harmless code indentation issue. FossilOrigin-Name: 41d8d26e48a440da44582763f6b0c097febe3b8ac16adb662f688c3662c301bf --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/whereexpr.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 1b9116fe1a..a781f61737 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C CLI:\sTake\sextra\scare\sto\snot\ssplit\sa\smulti-byte\sunicode\scharacter\swhen\sdoing\nwordwrap. -D 2022-02-01T13:17:11.049 +C Fix\sa\sharmless\scode\sindentation\sissue. +D 2022-02-02T11:37:49.562 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -642,7 +642,7 @@ F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b F src/where.c c4a80044708b1000a2b875a6623acfb636acd1c740063b60ea50e359d305829e F src/whereInt.h 0748a6fce98b41862445906922a809146ff7ef4de16ed9022b0bc4e5c43aa60a F src/wherecode.c c313ccf5ed13dc7e88c64f93733f414dee369a212508a866878696d83c64fc36 -F src/whereexpr.c ddb6ab49f745154c37dbdb291433c933e00175929647290a11f487af701d0392 +F src/whereexpr.c fa8017f9fed4b50c59b4f105139679dd59ed7fea43179ebb0f6b8b5796a30515 F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 1b528e31f8c62797e0814568b520c0680ff23a2ee877ca6aa70a167d40ebdf80 -R fe91994efd36a88115500529351b7e2e +P 00b1b7020a564976da3237532434e47ccf17eb5d620e6ac45f3e70b5d5739200 +R e3feaf8ec370122d862f692465c41194 U drh -Z 4d3a3178c78a3baab7f87b0b2d56c876 +Z d6d32499a022b83aad108caf851b3d80 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0347e193b4..98e7184004 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -00b1b7020a564976da3237532434e47ccf17eb5d620e6ac45f3e70b5d5739200 \ No newline at end of file +41d8d26e48a440da44582763f6b0c097febe3b8ac16adb662f688c3662c301bf \ No newline at end of file diff --git a/src/whereexpr.c b/src/whereexpr.c index f54729767a..e212bcb932 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -422,7 +422,7 @@ static int isAuxiliaryVtabOperator( assert( pVtab!=0 ); assert( pVtab->pModule!=0 ); assert( !ExprHasProperty(pExpr, EP_IntValue) ); - pMod = (sqlite3_module *)pVtab->pModule; + pMod = (sqlite3_module *)pVtab->pModule; if( pMod->xFindFunction!=0 ){ i = pMod->xFindFunction(pVtab,2, pExpr->u.zToken, &xNotUsed, &pNotUsed); if( i>=SQLITE_INDEX_CONSTRAINT_FUNCTION ){ From 30e314e4cbc7614d322b13703295af961c5e38c6 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 2 Feb 2022 14:36:58 +0000 Subject: [PATCH 29/40] Refactor sqlite3_vtab_in() to make use of the existing sqlite3_value_pointer() mechanism for passing the list of IN operator RHS values into xFilter, for improved memory safety. FossilOrigin-Name: 8965929be236fe1a6994f31b94c1b7590c7c1e809470c542a76f3e0e275d032f --- manifest | 16 +++++------ manifest.uuid | 2 +- src/vdbe.c | 23 +++++++++------ src/vdbeInt.h | 25 +++++++++++----- src/vdbeapi.c | 79 +++++++++++++++++++++------------------------------ 5 files changed, 74 insertions(+), 71 deletions(-) diff --git a/manifest b/manifest index 6f0bf53a93..225a935344 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Tweaks\sto\sthe\ssqlite3_vtab_in()\sinterface. -D 2022-02-01T21:59:43.937 +C Refactor\ssqlite3_vtab_in()\sto\smake\suse\sof\sthe\sexisting\nsqlite3_value_pointer()\smechanism\sfor\spassing\sthe\slist\sof\sIN\soperator\nRHS\svalues\sinto\sxFilter,\sfor\simproved\smemory\ssafety. +D 2022-02-02T14:36:58.288 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -624,10 +624,10 @@ F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c 602fe229f32a96ceccae4f40824129669582096f7c355f53dbac156c9fecef23 F src/vacuum.c 6c38ddc52f0619865c91dae9c441d4d48bf3040d7dc1bc5b22da1e45547ed0b3 -F src/vdbe.c d6694187a2819df7c2df3bd568fd059617c3edef4aa87e28a8121b02818f4ebf +F src/vdbe.c 13a4de20ee07bdfb3dc74ab49b7912208e309caf762a8d1678fb111e2223af35 F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe -F src/vdbeInt.h 24d58f12f642dcac102fa75d08e99ad06b6cbc66bf4948bb61e2e223ef9518b6 -F src/vdbeapi.c 84e7e8d161c8fb7259eaa5fe7234f2334ef9fb013674ce34705b56166052b5fa +F src/vdbeInt.h b45599a2b59f1ce042512ab6786b0b82a8cf3002f6b0fa60b4834e2cd3ac61d8 +F src/vdbeapi.c a6ae9ef8180b2c51555cc96b8c5b928d45738a90f667a28a1959bcd09646643d F src/vdbeaux.c e761b8011baec7a4773f0a7594783f2cd71f699ab187c4aad917529ab8acd3fe F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd F src/vdbemem.c eb6042667c02c3ef1f968235b4a170e31b23a4b6a57f65a8454eab4d36f14b7f @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 5acf90a931b27b7d627c0a8fee68170430e09b028d6643b959b0ec14fd59f7ac -R e3dc8c48dc1f78fd77820f7d72429762 +P 75040183b8e14f20bfedfdcc1a9fb968f2f0193bc698605d1b4791a3699b93d9 +R 685a004fac313bda3614fce6fec3f1d8 U drh -Z 89712fde261ff7ce2e71753d08fff668 +Z f2a69210d29b19f1b295e7a7f5450a22 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bfff22a8a9..55846307b1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -75040183b8e14f20bfedfdcc1a9fb968f2f0193bc698605d1b4791a3699b93d9 \ No newline at end of file +8965929be236fe1a6994f31b94c1b7590c7c1e809470c542a76f3e0e275d032f \ No newline at end of file diff --git a/src/vdbe.c b/src/vdbe.c index 5643d43ce9..5410a79127 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -7736,20 +7736,27 @@ case OP_VOpen: { #ifndef SQLITE_OMIT_VIRTUALTABLE /* Opcode: VInitIn P1 P2 P3 * * -** Synopsis: r[P2]=cursor over eph table P1. +** Synopsis: r[P2]=ValueList(P1,P3) ** -** Initialize register P2 as a value that can be used as an iterator over -** the contents of ephemeral table P1 by an xFilter callback implementation. -** Register P3 is used as a cache by the iterator. +** Set register P2 to be a pointer to a ValueList object for cursor P1 +** with cache register P3 and output register P3+1. This ValueList object +** can be used as the first argument to sqlite3_vtab_in_first() and +** sqlite3_vtab_in_next() to extract all of the values stored in the P1 +** cursor. Register P3 is used to hold the values returned by +** sqlite3_vtab_in_first() and sqlite3_vtab_in_next(). */ case OP_VInitIn: { /* out2 */ - VdbeCursor *pC; + VdbeCursor *pC; /* The cursor containing the RHS values */ + ValueList *pRhs; /* New ValueList object to put in reg[P2] */ + pC = p->apCsr[pOp->p1]; + pRhs = sqlite3_malloc64( sizeof(*pRhs) ); + if( pRhs==0 ) goto no_mem; + pRhs->pCsr = pC->uc.pCursor; + pRhs->pOut = &aMem[pOp->p3]; pOut = out2Prerelease(p, pOp); - pOut->z = (char*)(pC->uc.pCursor); - pOut->u.pVal = &aMem[pOp->p3]; - pOut->uTemp = SQLITE_VTAB_IN_MAGIC; pOut->flags = MEM_Null; + sqlite3VdbeMemSetPointer(pOut, pRhs, "ValueList", sqlite3_free); break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 9ddb742a49..f02b37c6a2 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -195,12 +195,6 @@ struct VdbeFrame { */ #define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))]) -/* Magic number for Mem.uTemp when it is acting as as the cache for the -** IN(...) iterator for sqlite3_vtab_in_next() -*/ -#define SQLITE_VTAB_IN_MAGIC 0xd3ab12ec - - /* ** Internally, the vdbe manipulates nearly all SQL values as Mem ** structures. Each Mem struct may cache multiple representations (string, @@ -213,7 +207,6 @@ struct sqlite3_value { int nZero; /* Extra zero bytes when MEM_Zero and MEM_Blob set */ const char *zPType; /* Pointer type when MEM_Term|MEM_Subtype|MEM_Null */ FuncDef *pDef; /* Used only when flags==MEM_Agg */ - sqlite3_value *pVal;/* Current value for xFilter IN(...) iterator */ } u; u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */ @@ -489,6 +482,24 @@ struct PreUpdate { Index *pPk; /* PK index if pTab is WITHOUT ROWID */ }; +/* +** An instance of this object is used to pass an vector of values into +** OP_VFilter, the xFilter method of a virtual table. The vector is the +** set of values on the right-hand side of an IN constraint. +** +** The value as passed into xFilter is an sqlite3_value with a "pointer" +** type, such as is generated by sqlite3_result_pointer() and read by +** sqlite3_value_pointer. Such values have MEM_Term|MEM_Subtype|MEM_Null +** and a subtype of 'p'. The sqlite3_vtab_in_first() and _next() interfaces +** know how to use this object to step through all the values in the +** right operand of the IN constraint. +*/ +typedef struct ValueList ValueList; +struct ValueList { + BtCursor *pCsr; /* An ephemeral table holding all values */ + sqlite3_value *pOut; /* Register to hold each decoded output value */ +}; + /* ** Function prototypes */ diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 46b466b6bb..75d7bf15ce 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -846,63 +846,48 @@ int sqlite3_vtab_nochange(sqlite3_context *p){ return sqlite3_value_nochange(p->pOut); } -/* -** The first argument is an iterator value created by VDBE instruction -** OP_VInitIn. The iterator is guaranteed to point to a valid entry. This -** function attempts to load the current value from the iterator into -** object pVal->u.pVal. If successful, (*ppOut) is set to point to -** pVal->u.pVal and SQLITE_OK is returned. Otherwise, if an error -** occurs, an SQLite error code is returned and (*ppOut) is left unchanged. -*/ -static int vtabInLoadValue(sqlite3_value *pVal, sqlite3_value **ppOut){ - BtCursor *pCsr = (BtCursor*)pVal->z; - sqlite3_value *pOut = pVal->u.pVal; - int sz; - int rc; - - sz = (int)sqlite3BtreePayloadSize(pCsr); - if( sz>pVal->szMalloc ){ - if( pVal->szMalloc==0 ) pVal->zMalloc = 0; - pVal->zMalloc = sqlite3DbReallocOrFree(pVal->db, pVal->zMalloc, sz*2); - if( pVal->zMalloc ){ - pVal->szMalloc = sqlite3DbMallocSize(pVal->db, pVal->zMalloc); - }else{ - pVal->szMalloc = 0; - return SQLITE_NOMEM_BKPT; - } - } - - rc = sqlite3BtreePayload(pCsr, 0, sz, pVal->zMalloc); - if( rc==SQLITE_OK ){ - u32 iSerial; - int iOff = 1 + getVarint32((const u8*)&pVal->zMalloc[1], iSerial); - sqlite3VdbeSerialGet((const u8*)&pVal->zMalloc[iOff], iSerial, pOut); - pOut->enc = ENC(pVal->db); - *ppOut = pOut; - } - return rc; -} - /* ** Implementation of sqlite3_vtab_in_first() (if bNext==0) and ** sqlite3_vtab_in_next() (if bNext!=0). */ -static int vtabInOp(sqlite3_value *pVal, sqlite3_value **ppOut, int bNext){ +static int valueFromValueList( + sqlite3_value *pVal, /* Pointer to the ValueList object */ + sqlite3_value **ppOut, /* Store the next value from the list here */ + int bNext /* 1 for _next(). 0 for _first() */ +){ int rc; - BtCursor *pCsr; + ValueList *pRhs; + *ppOut = 0; if( pVal==0 ) return SQLITE_MISUSE; - if( pVal->uTemp!=SQLITE_VTAB_IN_MAGIC ) return SQLITE_MISUSE; - pCsr = (BtCursor*)pVal->z; + pRhs = (ValueList*)sqlite3_value_pointer(pVal, "ValueList"); + if( pRhs==0 ) return SQLITE_MISUSE; if( bNext ){ - rc = sqlite3BtreeNext(pCsr, 0); + rc = sqlite3BtreeNext(pRhs->pCsr, 0); }else{ int dummy = 0; - rc = sqlite3BtreeFirst(pCsr, &dummy); - if( rc==SQLITE_OK && sqlite3BtreeEof(pCsr) ) rc = SQLITE_DONE; + rc = sqlite3BtreeFirst(pRhs->pCsr, &dummy); + if( rc==SQLITE_OK && sqlite3BtreeEof(pRhs->pCsr) ) rc = SQLITE_DONE; } if( rc==SQLITE_OK ){ - rc = vtabInLoadValue(pVal, ppOut); + u32 sz; /* Size of current row in bytes */ + Mem sMem; /* Raw content of current row */ + memset(&sMem, 0, sizeof(sMem)); + sz = sqlite3BtreePayloadSize(pRhs->pCsr); + rc = sqlite3VdbeMemFromBtreeZeroOffset(pRhs->pCsr,(int)sz,&sMem); + if( rc==SQLITE_OK ){ + u8 *zBuf = (u8*)sMem.z; + u32 iSerial; + sqlite3_value *pOut = pRhs->pOut; + int iOff = 1 + getVarint32(&zBuf[1], iSerial); + sqlite3VdbeSerialGet(&zBuf[iOff], iSerial, pOut); + if( (pOut->flags & MEM_Ephem)!=0 && sqlite3VdbeMemMakeWriteable(pOut) ){ + rc = SQLITE_NOMEM; + }else{ + *ppOut = pOut; + } + } + sqlite3VdbeMemRelease(&sMem); } return rc; } @@ -912,7 +897,7 @@ static int vtabInOp(sqlite3_value *pVal, sqlite3_value **ppOut, int bNext){ ** Set (*ppOut) to point to this value before returning. */ int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut){ - return vtabInOp(pVal, ppOut, 0); + return valueFromValueList(pVal, ppOut, 0); } /* @@ -920,7 +905,7 @@ int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut){ ** Set (*ppOut) to point to this value before returning. */ int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut){ - return vtabInOp(pVal, ppOut, 1); + return valueFromValueList(pVal, ppOut, 1); } /* From 38d1e443501f41f895c34a38dfa247d2ec6d4359 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 2 Feb 2022 15:10:45 +0000 Subject: [PATCH 30/40] Be sure that sqlite3_vtab_in_first() and _next() set the correct encoding. FossilOrigin-Name: 04edf36ee8e043c83235a5169a7ced23f211edd2f7ef3290d96413d5fd229ad7 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/vdbeapi.c | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 225a935344..1ec679a8f9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Refactor\ssqlite3_vtab_in()\sto\smake\suse\sof\sthe\sexisting\nsqlite3_value_pointer()\smechanism\sfor\spassing\sthe\slist\sof\sIN\soperator\nRHS\svalues\sinto\sxFilter,\sfor\simproved\smemory\ssafety. -D 2022-02-02T14:36:58.288 +C Be\ssure\sthat\ssqlite3_vtab_in_first()\sand\s_next()\sset\sthe\scorrect\sencoding. +D 2022-02-02T15:10:45.512 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -627,7 +627,7 @@ F src/vacuum.c 6c38ddc52f0619865c91dae9c441d4d48bf3040d7dc1bc5b22da1e45547ed0b3 F src/vdbe.c 13a4de20ee07bdfb3dc74ab49b7912208e309caf762a8d1678fb111e2223af35 F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe F src/vdbeInt.h b45599a2b59f1ce042512ab6786b0b82a8cf3002f6b0fa60b4834e2cd3ac61d8 -F src/vdbeapi.c a6ae9ef8180b2c51555cc96b8c5b928d45738a90f667a28a1959bcd09646643d +F src/vdbeapi.c 32d353cec191cd3e3d3c3457dd219ce10f43d430d9ccb9649abca81f5f622031 F src/vdbeaux.c e761b8011baec7a4773f0a7594783f2cd71f699ab187c4aad917529ab8acd3fe F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd F src/vdbemem.c eb6042667c02c3ef1f968235b4a170e31b23a4b6a57f65a8454eab4d36f14b7f @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 75040183b8e14f20bfedfdcc1a9fb968f2f0193bc698605d1b4791a3699b93d9 -R 685a004fac313bda3614fce6fec3f1d8 +P 8965929be236fe1a6994f31b94c1b7590c7c1e809470c542a76f3e0e275d032f +R 577d7ced4e44dba6062d7cb550bb1c26 U drh -Z f2a69210d29b19f1b295e7a7f5450a22 +Z d0000616f45072bd895fd0e1857966da # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 55846307b1..c4199e1bff 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8965929be236fe1a6994f31b94c1b7590c7c1e809470c542a76f3e0e275d032f \ No newline at end of file +04edf36ee8e043c83235a5169a7ced23f211edd2f7ef3290d96413d5fd229ad7 \ No newline at end of file diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 75d7bf15ce..95fb72c3c9 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -881,6 +881,7 @@ static int valueFromValueList( sqlite3_value *pOut = pRhs->pOut; int iOff = 1 + getVarint32(&zBuf[1], iSerial); sqlite3VdbeSerialGet(&zBuf[iOff], iSerial, pOut); + pOut->enc = ENC(pOut->db); if( (pOut->flags & MEM_Ephem)!=0 && sqlite3VdbeMemMakeWriteable(pOut) ){ rc = SQLITE_NOMEM; }else{ From 3d7a69e5ef1c94a74ef01de2a817e7544abcf2a8 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 2 Feb 2022 16:24:01 +0000 Subject: [PATCH 31/40] Relax the restriction that the RHS of the IN operator must be a list in order for sqlite3_vtab_in() to work. Change an unreachable branch into an assert(). FossilOrigin-Name: 3bf2153440dce0e8c0572c4fd39e6b9f34ead75ccab2cea80a646d4ff9d19146 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/vdbeapi.c | 3 ++- src/where.c | 4 +--- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 1ec679a8f9..7221a4a0c8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Be\ssure\sthat\ssqlite3_vtab_in_first()\sand\s_next()\sset\sthe\scorrect\sencoding. -D 2022-02-02T15:10:45.512 +C Relax\sthe\srestriction\sthat\sthe\sRHS\sof\sthe\sIN\soperator\smust\sbe\sa\slist\sin\sorder\nfor\ssqlite3_vtab_in()\sto\swork.\s\sChange\san\sunreachable\sbranch\sinto\san\sassert(). +D 2022-02-02T16:24:01.752 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -627,7 +627,7 @@ F src/vacuum.c 6c38ddc52f0619865c91dae9c441d4d48bf3040d7dc1bc5b22da1e45547ed0b3 F src/vdbe.c 13a4de20ee07bdfb3dc74ab49b7912208e309caf762a8d1678fb111e2223af35 F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe F src/vdbeInt.h b45599a2b59f1ce042512ab6786b0b82a8cf3002f6b0fa60b4834e2cd3ac61d8 -F src/vdbeapi.c 32d353cec191cd3e3d3c3457dd219ce10f43d430d9ccb9649abca81f5f622031 +F src/vdbeapi.c 06bff35393ca5daa3e02e38fb516df320bd52720a2781eb70c2db23ea1c746dd F src/vdbeaux.c e761b8011baec7a4773f0a7594783f2cd71f699ab187c4aad917529ab8acd3fe F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd F src/vdbemem.c eb6042667c02c3ef1f968235b4a170e31b23a4b6a57f65a8454eab4d36f14b7f @@ -639,7 +639,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b -F src/where.c 392d552fa6636e94d242954247e277eb9b0d9c45446afb0c0a57f8c6fcb3f792 +F src/where.c d7d996a5d4501fbf14997e965d7f641fe7b2a0dd89b2296e5310b506da3fb822 F src/whereInt.h 1d821657238a0bd12b3c8f2926c7f8f9294bc5efe20af53c7c50d53a0a026cb9 F src/wherecode.c 5879604677f0bdfb8d95ff616d834daecc12256346b7d9ad96a7e84a1cb08fdc F src/whereexpr.c ddb6ab49f745154c37dbdb291433c933e00175929647290a11f487af701d0392 @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 8965929be236fe1a6994f31b94c1b7590c7c1e809470c542a76f3e0e275d032f -R 577d7ced4e44dba6062d7cb550bb1c26 +P 04edf36ee8e043c83235a5169a7ced23f211edd2f7ef3290d96413d5fd229ad7 +R 2c3c1902f73f372b9534bd85d910dc24 U drh -Z d0000616f45072bd895fd0e1857966da +Z bd0932878b05cf48df93c8b67c69411f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c4199e1bff..3180a6bf73 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -04edf36ee8e043c83235a5169a7ced23f211edd2f7ef3290d96413d5fd229ad7 \ No newline at end of file +3bf2153440dce0e8c0572c4fd39e6b9f34ead75ccab2cea80a646d4ff9d19146 \ No newline at end of file diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 95fb72c3c9..9cc200298e 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -867,7 +867,8 @@ static int valueFromValueList( }else{ int dummy = 0; rc = sqlite3BtreeFirst(pRhs->pCsr, &dummy); - if( rc==SQLITE_OK && sqlite3BtreeEof(pRhs->pCsr) ) rc = SQLITE_DONE; + assert( rc==SQLITE_OK || sqlite3BtreeEof(pRhs->pCsr) ); + if( sqlite3BtreeEof(pRhs->pCsr) ) rc = SQLITE_DONE; } if( rc==SQLITE_OK ){ u32 sz; /* Size of current row in bytes */ diff --git a/src/where.c b/src/where.c index 169c4ccf14..11eae60e6a 100644 --- a/src/where.c +++ b/src/where.c @@ -1243,9 +1243,7 @@ static sqlite3_index_info *allocateIndexInfo( pIdxCons[j].iTermOffset = i; op = pTerm->eOperator & WO_ALL; if( op==WO_IN ){ - if( ExprHasProperty(pTerm->pExpr, EP_xIsSelect)==0 ){ - pHidden->mIn |= SMASKBIT32(j); - } + pHidden->mIn |= SMASKBIT32(j); op = WO_EQ; } if( op==WO_AUX ){ From 2725db836b4544f0748b2665d2632d4550c442ff Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 2 Feb 2022 18:47:56 +0000 Subject: [PATCH 32/40] Improved documentation for sqlite3_vtab_in(). No code changes. FossilOrigin-Name: c99df4ab5db2c32b044366c5b0ac70fd8887d1456d53323e75fede23cc61c236 --- manifest | 12 +++--- manifest.uuid | 2 +- src/sqlite.h.in | 102 ++++++++++++++++++++++++++---------------------- 3 files changed, 62 insertions(+), 54 deletions(-) diff --git a/manifest b/manifest index 7221a4a0c8..1e9ca0f15e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Relax\sthe\srestriction\sthat\sthe\sRHS\sof\sthe\sIN\soperator\smust\sbe\sa\slist\sin\sorder\nfor\ssqlite3_vtab_in()\sto\swork.\s\sChange\san\sunreachable\sbranch\sinto\san\sassert(). -D 2022-02-02T16:24:01.752 +C Improved\sdocumentation\sfor\ssqlite3_vtab_in().\s\sNo\scode\schanges. +D 2022-02-02T18:47:56.749 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -554,7 +554,7 @@ F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2 F src/shell.c.in 2f58e6aa6b3d2012db32f1c5fa4591e9d12fd582904632b4fc8f57a382b98fd3 -F src/sqlite.h.in 72f3e57c4c0b4284ab9238312f7fd797967cc43f44558a80469a3d9b86a7be2b +F src/sqlite.h.in a6e6fd9defb576af6444a85f446aaa738dea3384c48db4fe9007fb8ff954b7c5 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h a95cb9ed106e3d39e2118e4dcc15a14faec3fa50d0093425083d340d9dfd96e6 F src/sqliteInt.h 838df3e9ba9390058076d2f50c7efde9e0e8747303e788cf5bbe05402ab10924 @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 04edf36ee8e043c83235a5169a7ced23f211edd2f7ef3290d96413d5fd229ad7 -R 2c3c1902f73f372b9534bd85d910dc24 +P 3bf2153440dce0e8c0572c4fd39e6b9f34ead75ccab2cea80a646d4ff9d19146 +R f229e1cc04b78384226f7f6c44d75044 U drh -Z bd0932878b05cf48df93c8b67c69411f +Z 5557e45c47098a153d05816f88cff23e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3180a6bf73..472918b743 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3bf2153440dce0e8c0572c4fd39e6b9f34ead75ccab2cea80a646d4ff9d19146 \ No newline at end of file +c99df4ab5db2c32b044366c5b0ac70fd8887d1456d53323e75fede23cc61c236 \ No newline at end of file diff --git a/src/sqlite.h.in b/src/sqlite.h.in index e8122912ee..ead938a7dd 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -9605,21 +9605,22 @@ SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int); int sqlite3_vtab_distinct(sqlite3_index_info*); /* -** CAPI3REF: Identify and handle IN(...) constraints in xBestIndex +** CAPI3REF: Identify and handle IN constraints in xBestIndex ** ** This interface may only be used from within an -** [xBestIndex|xBestIndex() method of a [virtual table] implementation. +** [xBestIndex|xBestIndex() method] of a [virtual table] implementation. ** The result of invoking this interface from any other context is ** undefined and probably harmful. ** -** A constraint on a virtual table of the form "column IN (...)" is +** ^(A constraint on a virtual table of the form +** "[IN operator|column IN (...)]" is ** communicated to the xBestIndex method as a -** [SQLITE_INDEX_CONSTRAINT_EQ] constraint. If xBestIndex wants to use +** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use ** this constraint, it must set the corresponding -** aConstraintUsage[].argvIndex to a postive integer. Then, under -** the usual mode of handling IN operators, SQLite generate bytecode +** aConstraintUsage[].argvIndex to a postive integer. ^(Then, under +** the usual mode of handling IN operators, SQLite generates [bytecode] ** that invokes the [xFilter|xFilter() method] once for each value -** on the right-hand side of the IN operator. Thus the virtual table +** on the right-hand side of the IN operator.)^ Thus the virtual table ** only sees a single value from the right-hand side of the IN operator ** at a time. ** @@ -9629,45 +9630,46 @@ int sqlite3_vtab_distinct(sqlite3_index_info*); ** **
      **
    1. -** A call to sqlite3_vtab_in(P,I,-1) will return true (non-zero) -** if and only if the I-th constraint in P->aConstraint[] is -** an IN operator that can be processed all at once. In other words, +** ^A call to sqlite3_vtab_in(P,N,-1) will return true (non-zero) +** if and only if the [sqlite3_index_info|P->aConstraint][N] constraint +** is an [IN operator] that can be processed all at once. ^In other words, ** sqlite3_vtab_in() with -1 in the third argument is a mechanism ** by which the virtual table can ask SQLite if all-at-once processing ** of the IN operator is even possible. ** **

    2. -** A call to sqlite3_vtab_in(P,I,F) with F set to 1 or 0 indicates +** ^A call to sqlite3_vtab_in(P,N,F) with F==1 or F==0 indicates ** to SQLite that the virtual table does or does not want to process -** the IN operator all-at-once. Thus when the third parameter (F) is -** non-negative, this interface is the mechanism by which the virtual -** table tells SQLite how it wants to process in IN operator. +** the IN operator all-at-once, respectively. ^Thus when the third +** parameter (F) is non-negative, this interface is the mechanism by +** which the virtual table tells SQLite how it wants to process in +** IN operator. **

    ** -** The sqlite3_vtab_in(P,I,F) interface can be invoked multiple times -** within the same xBestIndex method call. For any given P and I parameters, -** the return value from sqlite3_vtab_in(P,I,F) will always be the same -** for every invocation within the same xBestIndex call. If the interface -** returns true (non-zero), that means that the constraint is an IN operator -** that can be processed all-at-once. If the constraint is not an IN +** ^The sqlite3_vtab_in(P,N,F) interface can be invoked multiple times +** within the same xBestIndex method call. ^For any given P,N pair, +** the return value from sqlite3_vtab_in(P,N,F) will always be the same +** within the same xBestIndex call. ^If the interface returns true +** (non-zero), that means that the constraint is an IN operator +** that can be processed all-at-once. ^If the constraint is not an IN ** operator or cannot be processed all-at-once, then the interface returns ** false. ** -** All-at-once processing of the IN operator is selected if both of the +** ^(All-at-once processing of the IN operator is selected if both of the ** following conditions are met: ** **
      -**
    1. The P->aConstraintUsage[I].argvIndex value is set to a positive +**

    2. The P->aConstraintUsage[N].argvIndex value is set to a positive ** integer. This is how the virtual table tells SQLite that it wants to -** use the I-th constraint. +** use the N-th constraint. ** -**

    3. The last call to sqlite3_vtab_in(P,I,F) for which F was +**

    4. The last call to sqlite3_vtab_in(P,N,F) for which F was ** non-negative had F>=1. -**

    +** )^ ** -** If either or both of the conditions above are false, then uses the -** traditional one-at-a-time processing strategy for IN constraint. -** If both conditions are true, then the argvIndex-th parameter to the +** ^If either or both of the conditions above are false, then SQLite uses +** the traditional one-at-a-time processing strategy for IN constraint. +** ^If both conditions are true, then the argvIndex-th parameter to the ** xFilter method will be an [sqlite3_value] that appears to be NULL, ** but which can be passed to [sqlite3_vtab_in_first()] and ** [sqlite3_vtab_in_next()] to find all values on the right-hand side @@ -9679,41 +9681,47 @@ int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle); ** CAPI3REF: Find all elements on the right-hand side of an IN constraint. ** ** These interfaces are only useful from within the -** [xFilter|xFilter() method] of a virtual table implementation. +** [xFilter|xFilter() method] of a [virtual table] implementation. ** The result of invoking these interfaces from any other context ** is undefined and probably harmful. ** ** The X parameter in a call to sqlite3_vtab_in_first(X,P) or ** sqlite3_vtab_in_next(X,P) must be one of the parameters to the -** xFilter method which invokes those routines, and specifically +** xFilter method which invokes these routines, and specifically ** a parameter that was previously selected for all-at-once IN constraint ** processing use the [sqlite3_vtab_in()] interface in the -** [xBestIndex|xBestIndex method]. If the X parameter is not +** [xBestIndex|xBestIndex method]. ^(If the X parameter is not ** an xFilter argument that was selected for all-at-once IN constraint -** processing, then these routines return SQLITE_MISUSE or perhaps +** processing, then these routines return [SQLITE_MISUSE])^ or perhaps ** exhibit some other undefined or harmful behavior. ** -** Use these routines to access all values on the right-hand side +** ^(Use these routines to access all values on the right-hand side ** of the IN constraint using code like the following: ** -**
    -**   for(rc=sqlite3_vtab_in_first(pList, &pVal);
    -**       rc==SQLITE_OK && pVal
    -**       rc=sqlite3_vtab_in_next(pList, &pVal)
    -**   ){
    -**     // do something with pVal
    -**   }
    -**   if( rc!=SQLITE_OK ){
    -**     // an error has occurred
    -**   }
    -** 
    +**
    +**    for(rc=sqlite3_vtab_in_first(pList, &pVal);
    +**        rc==SQLITE_OK && pVal
    +**        rc=sqlite3_vtab_in_next(pList, &pVal)
    +**    ){
    +**      // do something with pVal
    +**    }
    +**    if( rc!=SQLITE_OK ){
    +**      // an error has occurred
    +**    }
    +** 
    )^ ** -** On success, the sqlite3_vtab_in_first(X,P) and sqlite3_vtab_in_next(X,P) +** ^On success, the sqlite3_vtab_in_first(X,P) and sqlite3_vtab_in_next(X,P) ** routines return SQLITE_OK and set *P to point to the first or next value -** on the RHS of the IN constraint. If there are no more values on the +** on the RHS of the IN constraint. ^If there are no more values on the ** right hand side of the IN constraint, then *P is set to NULL and these -** routines return [SQLITE_DONE]. The return value might be +** routines return [SQLITE_DONE]. ^The return value might be ** some other value, such as SQLITE_NOMEM, in the event of a malfunction. +** +** The *ppOut values returned by these routines are only valid until the +** next call to either of these routines or until the end of the xFilter +** method from which these routines were called. If the virtual table +** implementation needs to retain the *ppOut values for longer, it must make +** copies. */ int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut); int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut); From d96ab99588bb30e2d0e42e7302c0490011d315e5 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 2 Feb 2022 19:15:53 +0000 Subject: [PATCH 33/40] Test cases for sqlite3_vtab_in() and sqlite3_vtab_distinct(). FossilOrigin-Name: 21afb81d0a73af39aacd9329b1441faa2b535a52a52036daec89fd303a8b344f --- manifest | 29 +-- manifest.uuid | 2 +- src/test_bestindex.c | 191 +++++++++++++++--- test/bestindex1.test | 24 ++- test/bestindex2.test | 5 +- test/bestindex3.test | 5 +- test/bestindex4.test | 11 +- test/bestindex5.test | 7 +- test/bestindex6.test | 6 +- test/bestindex7.test | 6 +- test/bestindex8.test | 460 +++++++++++++++++++++++++++++++++++++++++++ test/rowvalue5.test | 4 +- 12 files changed, 699 insertions(+), 51 deletions(-) create mode 100644 test/bestindex8.test diff --git a/manifest b/manifest index 1e9ca0f15e..95ed26d241 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improved\sdocumentation\sfor\ssqlite3_vtab_in().\s\sNo\scode\schanges. -D 2022-02-02T18:47:56.749 +C Test\scases\sfor\ssqlite3_vtab_in()\sand\ssqlite3_vtab_distinct(). +D 2022-02-02T19:15:53.331 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -574,7 +574,7 @@ F src/test9.c 12e5ba554d2d1cbe0158f6ab3f7ffcd7a86ee4e5 F src/test_async.c 195ab49da082053fdb0f949c114b806a49ca770a F src/test_autoext.c 915d245e736652a219a907909bb6710f0d587871 F src/test_backup.c bf5da90c9926df0a4b941f2d92825a01bbe090a0 -F src/test_bestindex.c ba0314d3bca8b00fa0daaf113a314986f73ea8ad85a7562f983638d09a29045c +F src/test_bestindex.c 8294d8223b7f18a3ddb7f9a0e30815dcca4e61681f78b538c870f7d934f88b81 F src/test_blob.c ae4a0620b478548afb67963095a7417cd06a4ec0a56adb453542203bfdcb31ce F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274 F src/test_config.c 284c29912736f68b0a583a920bf63fd8f9125dffb8a75cb0676e58502b2f7908 @@ -724,13 +724,14 @@ F test/backup_malloc.test 0c9abdf74c51e7bedb66d504cd684f28d4bd4027 F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f F test/badutf2.test f310fd3b24a491b6b77bccdf14923b85d6ebcce751068c180d93a6b8ff854399 F test/bc_common.tcl b5e42d80305be95697e6370e015af571e5333a1c -F test/bestindex1.test 7cc626f1f4a7483bb6b38487d467db4477083be5cd93958aeda5d5127640dc81 -F test/bestindex2.test 60266e2854055788459cbfd86cef575601eabe74a2c61faba72601739fea4398 -F test/bestindex3.test e061a6ed0f519beee037ba7e7a4c37f80c8a7e4a303e2559ed1f760e4b0235eb -F test/bestindex4.test 82250e7dcc6d5b15244edc9d6554b1760583af1b8548c2a255a1c4f28e744c0e -F test/bestindex5.test 67c1166131bb59f9e47c00118f7d432ca5491e6cae6ca3f87ca9db20103a78f9 -F test/bestindex6.test d856a9bb63d927493575823eed44053bc36251e241aa364e54d0f2a2d302e1d4 -F test/bestindex7.test a11348824aed0de2bb9030f092636929000cd72882bdf919adacc3792f67ccbd +F test/bestindex1.test 856a453dff8c68b4568601eed5a8b5e20b4763af9229f3947c215729ed878db0 +F test/bestindex2.test 394ff8fbf34703391247116d6a44e1c50ee7282236ee77909044573cefc37bc0 +F test/bestindex3.test 34bea272b0e0f835651b16a3931dbe7ac927039be6b2e1cb617bbe1d584b492b +F test/bestindex4.test 3039894f2dad50f3a68443dffad1b44c9b067ac03870102df1ce3d9a46ea602e +F test/bestindex5.test a0c90b2dad7836e80a01379e200e5f8ec9476d49b349af02c0dbff2fb75dc98d +F test/bestindex6.test 16942535b551273f3ad9df8d7cc4b7f22b1fcd8882714358859eb049a6f99dd4 +F test/bestindex7.test f094c669a6400777f4d2ddc3ed28e39169f1adb5be3d59b55f22ccf8c414b71e +F test/bestindex8.test 025477dd9bdb462f4faef3dd8838306f4e230620677778f4fe27e6736789e1b8 F test/between.test b9a65fb065391980119e8a781a7409d3fcf059d89968279c750e190a9a1d5263 F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59 F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc @@ -1327,7 +1328,7 @@ F test/rowvalue.test 02214016f747854ef636e64ff204778649937aa801ca78e2495a960f8e0 F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b F test/rowvalue3.test 3068f508753af69884b12125995f023da0dbb256 F test/rowvalue4.test 441e7e366ac6d939a3a95a574031c56ec2a854077a91d66eee5ff1d86cb5be58 -F test/rowvalue5.test c81c7d8cf36711ab37675ad7376084ae2a359cb6 +F test/rowvalue5.test 00740304ea6a53a8704640c7405690f0045d5d2a6b4b04dde7bccc14c3068ea7 F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087 F test/rowvalue7.test c1cbdbf407029db01f87764097c6ac02a1c5a37efd2776eff32a9cdfdf6f2dba F test/rowvalue8.test 5900eddad9e2c3c2e26f1a95f74aafc1232ee5e0 @@ -1942,8 +1943,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 3bf2153440dce0e8c0572c4fd39e6b9f34ead75ccab2cea80a646d4ff9d19146 -R f229e1cc04b78384226f7f6c44d75044 +P c99df4ab5db2c32b044366c5b0ac70fd8887d1456d53323e75fede23cc61c236 +R 73bbb4581fa3d24d1ee5b28ca166750f U drh -Z 5557e45c47098a153d05816f88cff23e +Z 0537364a2100dee8e5b21a6d334bfbef # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 472918b743..ef06714857 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c99df4ab5db2c32b044366c5b0ac70fd8887d1456d53323e75fede23cc61c236 \ No newline at end of file +21afb81d0a73af39aacd9329b1441faa2b535a52a52036daec89fd303a8b344f \ No newline at end of file diff --git a/src/test_bestindex.c b/src/test_bestindex.c index 6cda889e5e..67a0c8258d 100644 --- a/src/test_bestindex.c +++ b/src/test_bestindex.c @@ -299,7 +299,21 @@ static int tclFilter( const char *zVal = (const char*)sqlite3_value_text(argv[ii]); Tcl_Obj *pVal; if( zVal==0 ){ + sqlite3_value *pMem; pVal = Tcl_NewObj(); + for(rc=sqlite3_vtab_in_first(argv[ii], &pMem); + rc==SQLITE_OK && pMem; + rc=sqlite3_vtab_in_next(argv[ii], &pMem) + ){ + Tcl_Obj *pVal2 = 0; + zVal = (const char*)sqlite3_value_text(pMem); + if( zVal ){ + pVal2 = Tcl_NewStringObj(zVal, -1); + }else{ + pVal2 = Tcl_NewObj(); + } + Tcl_ListObjAppendElement(interp, pVal, pVal2); + } }else{ pVal = Tcl_NewStringObj(zVal, -1); } @@ -374,20 +388,13 @@ static int tclEof(sqlite3_vtab_cursor *pVtabCursor){ return (pCsr->pStmt==0); } -static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ - tcl_vtab *pTab = (tcl_vtab*)tab; - Tcl_Interp *interp = pTab->interp; - Tcl_Obj *pArg; - Tcl_Obj *pScript; +static void testBestIndexObjConstraints( + Tcl_Interp *interp, + sqlite3_index_info *pIdxInfo +){ int ii; - int rc = SQLITE_OK; - - pScript = Tcl_DuplicateObj(pTab->pCmd); - Tcl_IncrRefCount(pScript); - Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xBestIndex", -1)); - - pArg = Tcl_NewObj(); - Tcl_IncrRefCount(pArg); + Tcl_Obj *pRes = Tcl_NewObj(); + Tcl_IncrRefCount(pRes); for(ii=0; iinConstraint; ii++){ struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii]; Tcl_Obj *pElem = Tcl_NewObj(); @@ -437,15 +444,21 @@ static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("usable", -1)); Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->usable)); - Tcl_ListObjAppendElement(0, pArg, pElem); + Tcl_ListObjAppendElement(0, pRes, pElem); Tcl_DecrRefCount(pElem); } - Tcl_ListObjAppendElement(0, pScript, pArg); - Tcl_DecrRefCount(pArg); + Tcl_SetObjResult(interp, pRes); + Tcl_DecrRefCount(pRes); +} - pArg = Tcl_NewObj(); - Tcl_IncrRefCount(pArg); +static void testBestIndexObjOrderby( + Tcl_Interp *interp, + sqlite3_index_info *pIdxInfo +){ + int ii; + Tcl_Obj *pRes = Tcl_NewObj(); + Tcl_IncrRefCount(pRes); for(ii=0; iinOrderBy; ii++){ struct sqlite3_index_orderby const *pOrder = &pIdxInfo->aOrderBy[ii]; Tcl_Obj *pElem = Tcl_NewObj(); @@ -456,17 +469,150 @@ static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("desc", -1)); Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pOrder->desc)); - Tcl_ListObjAppendElement(0, pArg, pElem); + Tcl_ListObjAppendElement(0, pRes, pElem); Tcl_DecrRefCount(pElem); } - Tcl_ListObjAppendElement(0, pScript, pArg); - Tcl_DecrRefCount(pArg); + Tcl_SetObjResult(interp, pRes); + Tcl_DecrRefCount(pRes); +} - Tcl_ListObjAppendElement(0, pScript, Tcl_NewWideIntObj(pIdxInfo->colUsed)); +/* +** Implementation of the handle passed to each xBestIndex callback. This +** object features the following sub-commands: +** +** $hdl constraints +** $hdl orderby +** $hdl mask +** +** $hdl distinct +** Return the result (an integer) of calling sqlite3_vtab_distinct() +** on the index-info structure. +** +** $hdl in IDX BOOLEAN +** Wrapper around sqlite3_vtab_in(). Returns an integer. +** +** $hdl rhs_value IDX ?DEFAULT? +** Wrapper around sqlite3_vtab_rhs_value(). +*/ +static int SQLITE_TCLAPI testBestIndexObj( + ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + const char *azSub[] = { + "constraints", /* 0 */ + "orderby", /* 1 */ + "mask", /* 2 */ + "distinct", /* 3 */ + "in", /* 4 */ + "rhs_value", /* 5 */ + 0 + }; + int ii; + sqlite3_index_info *pIdxInfo = (sqlite3_index_info*)clientData; + if( objc<2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND"); + return TCL_ERROR; + } + if( Tcl_GetIndexFromObj(interp, objv[1], azSub, "sub-command", 0, &ii) ){ + return TCL_ERROR; + } + + if( ii<4 && objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + if( ii==4 && objc!=4 ){ + Tcl_WrongNumArgs(interp, 2, objv, "INDEX BOOLEAN"); + return TCL_ERROR; + } + if( ii==5 && objc!=3 && objc!=4 ){ + Tcl_WrongNumArgs(interp, 2, objv, "INDEX ?DEFAULT?"); + return TCL_ERROR; + } + + switch( ii ){ + case 0: assert( sqlite3_stricmp(azSub[ii], "constraints")==0 ); + testBestIndexObjConstraints(interp, pIdxInfo); + break; + + case 1: assert( sqlite3_stricmp(azSub[ii], "orderby")==0 ); + testBestIndexObjOrderby(interp, pIdxInfo); + break; + + case 2: assert( sqlite3_stricmp(azSub[ii], "mask")==0 ); + Tcl_SetObjResult(interp, Tcl_NewWideIntObj(pIdxInfo->colUsed)); + break; + + case 3: assert( sqlite3_stricmp(azSub[ii], "distinct")==0 ); { + int bDistinct = sqlite3_vtab_distinct(pIdxInfo); + Tcl_SetObjResult(interp, Tcl_NewIntObj(bDistinct)); + break; + } + + case 4: assert( sqlite3_stricmp(azSub[ii], "in")==0 ); { + int iCons; + int bHandle; + if( Tcl_GetIntFromObj(interp, objv[2], &iCons) + || Tcl_GetBooleanFromObj(interp, objv[3], &bHandle) + ){ + return TCL_ERROR; + } + Tcl_SetObjResult(interp, + Tcl_NewIntObj(sqlite3_vtab_in(pIdxInfo, iCons, bHandle)) + ); + break; + } + + case 5: assert( sqlite3_stricmp(azSub[ii], "rhs_value")==0 ); { + int iCons = 0; + int rc; + sqlite3_value *pVal = 0; + const char *zVal = ""; + if( Tcl_GetIntFromObj(interp, objv[2], &iCons) ){ + return TCL_ERROR; + } + rc = sqlite3_vtab_rhs_value(pIdxInfo, iCons, &pVal); + if( rc!=SQLITE_OK && rc!=SQLITE_NOTFOUND ){ + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); + return TCL_ERROR; + } + if( pVal ){ + zVal = (const char*)sqlite3_value_text(pVal); + }else if( objc==4 ){ + zVal = Tcl_GetString(objv[3]); + } + Tcl_SetObjResult(interp, Tcl_NewStringObj(zVal, -1)); + break; + } + } + + return TCL_OK; +} + +static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + tcl_vtab *pTab = (tcl_vtab*)tab; + Tcl_Interp *interp = pTab->interp; + int rc = SQLITE_OK; + + static int iNext = 43; + char zHdl[24]; + Tcl_Obj *pScript; + + pScript = Tcl_DuplicateObj(pTab->pCmd); + Tcl_IncrRefCount(pScript); + Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xBestIndex", -1)); + + sqlite3_snprintf(sizeof(zHdl), zHdl, "bestindex%d", iNext++); + Tcl_CreateObjCommand(interp, zHdl, testBestIndexObj, pIdxInfo, 0); + Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(zHdl, -1)); rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL); + Tcl_DeleteCommand(interp, zHdl); Tcl_DecrRefCount(pScript); + if( rc!=TCL_OK ){ const char *zErr = Tcl_GetStringResult(interp); rc = SQLITE_ERROR; @@ -493,6 +639,7 @@ static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ rc = SQLITE_ERROR; pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr); }else{ + int ii; int iArgv = 1; for(ii=0; rc==SQLITE_OK && ii=0} + } $idxinsert +} + +#------------------------------------------------------------------------- +reset_db +register_tcl_module db + +proc vtab_command {src method args} { + switch -- $method { + xConnect { + return "CREATE TABLE xxx(a, b)" + } + + xBestIndex { + set hdl [lindex $args 0] + set ret [list] + + set iCons 0 + foreach cons [$hdl constraints] { + array set C $cons + if {($C(op)=="limit" || $C(op)=="offset") && $C(usable)} { + lappend ret use $iCons + } + incr iCons + } + + return $ret + } + + xFilter { + lappend ::lFilterArgs [lindex $args 2] + return [list sql "SELECT rowid, a, b FROM $src"] + } + + } + + return {} +} + +do_execsql_test 2.0 { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(a, b); + CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1); +} + +do_test 2.1 { + set ::lFilterArgs [list] + execsql { SELECT * FROM vt1 LIMIT 10 } + set ::lFilterArgs +} {10} + +do_test 2.2 { + set ::lFilterArgs [list] + execsql { SELECT * FROM vt1 LIMIT 5 OFFSET 50 } + set ::lFilterArgs +} {{5 50}} + +do_test 2.3 { + set ::lFilterArgs [list] + execsql { SELECT * FROM vt1 ORDER BY a, b LIMIT 1 OFFSET 1 } + set ::lFilterArgs +} {{1 1}} + +do_test 2.4 { + set ::lFilterArgs [list] + execsql { SELECT * FROM vt1 ORDER BY a, +b LIMIT 1 OFFSET 1 } + set ::lFilterArgs +} {{}} + +#------------------------------------------------------------------------- +reset_db +register_tcl_module db + +proc vtab_command {src method args} { + switch -- $method { + xConnect { + return "CREATE TABLE xxx(a, b)" + } + + xBestIndex { + set hdl [lindex $args 0] + set lCons [$hdl constraints] + + set ret [list] + for {set i 0} {$i < [llength $lCons]} {incr i} { + array set C [lindex $lCons $i] + if {$C(usable)} { + lappend ret use $i + $hdl in $i 1 + } + } + return $ret + } + + xFilter { + set lArg [lindex $args 2] + lappend ::lFilterArg {*}$lArg + return [list sql "SELECT rowid, a, b FROM $src"] + } + + } + + return {} +} + +do_execsql_test 3.0 { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(a, b); + CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1); +} + +foreach {tn sql lfa} { + 1 "SELECT * FROM vt1 WHERE b IN (10, 20, 30)" {{10 20 30}} + 2 "SELECT * FROM vt1 WHERE b IN ('abc', 'def')" {{abc def}} + 3 "SELECT * FROM vt1 WHERE a IS NULL AND b IN ('abc', 'def')" {{} {abc def}} + 4 "SELECT * FROM vt1 WHERE a IN (1,2,3) AND b IN ('abc', 'def')" + {{1 2 3} {abc def}} + + 5 "SELECT * FROM vt1 + WHERE a IN (SELECT 1 UNION SELECT 2) AND b IN ('abc', 'def')" + {{1 2} {abc def}} + + 6 "SELECT * FROM vt1 + WHERE b IN ('abc', 'def') AND a IN (SELECT 1 UNION SELECT 2)" + {{abc def} {1 2}} +} { + do_test 3.$tn { + set ::lFilterArg [list] + execsql $sql + set ::lFilterArg + } $lfa +} + +#explain_i { SELECT * FROM vt1 WHERE b IN (10, 20, 30) } + +#------------------------------------------------------------------------- +reset_db +register_tcl_module db + +proc vtab_command {src method args} { + switch -- $method { + xConnect { + return "CREATE TABLE xxx(a, b, c)" + } + + xBestIndex { + set hdl [lindex $args 0] + set lCons [$hdl constraints] + + set ret [list] + for {set i 0} {$i < [llength $lCons]} {incr i} { + lappend ::lBestIndexRhs [$hdl rhs_value $i -] + } + return $ret + } + + xFilter { + return [list sql "SELECT rowid, a, b, c FROM $src"] + } + + } + + return {} +} + +do_execsql_test 4.0 { + CREATE TABLE t1(a, b, c); + CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1); +} + +foreach {tn sql lbir} { + 1 "SELECT * FROM vt1 WHERE b = 10" {10} + 2 "SELECT * FROM vt1 WHERE a = 'abc' AND b < 30" {abc 30} + 3 "SELECT * FROM vt1 WHERE a = 'abc' AND b < 30+2" {abc -} + 4 "SELECT * FROM vt1 WHERE a IN (1,2,3) AND b < 30+2" {- -} + 5 "SELECT * FROM vt1 WHERE a IS 111 AND b < 30+2" {111 -} +} { + do_test 4.$tn { + set ::lBestIndexRhs [list] + execsql $sql + set ::lBestIndexRhs + } $lbir +} + +#------------------------------------------------------------------------- +reset_db +db cache size 0 +register_tcl_module db + +set ::vtab_handle_in 1 +proc vtab_command {src method args} { + switch -- $method { + xConnect { + return "CREATE TABLE xxx(a, b, c)" + } + + xBestIndex { + set lCols [list a b c] + + set hdl [lindex $args 0] + set lCons [$hdl constraints] + set lOrder [$hdl order] + + set L "" + set O "" + set W [list] + set a 0 + for {set i 0} {$i < [llength $lCons]} {incr i} { + array set C [lindex $lCons $i] + if {$C(usable)} { + if { $C(op)=="eq" } { + set bIn 0 + if {$::vtab_handle_in} { set bIn [$hdl in $i 1] } + if {$bIn} { + lappend W "[lindex $lCols $C(column)] IN (%I$a%)" + } else { + lappend W "[lindex $lCols $C(column)] = %$a%" + } + lappend ret omit $i + } + if { $C(op)=="limit" } { set L " LIMIT %$a%" ; lappend ret use $i } + if { $C(op)=="offset" } { set O " OFFSET %$a%" ; lappend ret use $i } + incr a + } + } + + set order "" + set selectlist "rowid, a, b, c" + if {[llength $lOrder]} { + array set sl [list] + set lO [list] + foreach s $lOrder { + array set C $s + set ad "" + if {$C(desc)} { set ad " DESC" } + lappend lO "[lindex $lCols $C(column)]$ad" + set sl($C(column)) 1 + } + if {[$hdl distinct]==2} { + set selectlist "DISTINCT 0" + foreach i {0 1 2} { + if {[info exists sl($i)]} { + append selectlist ", [lindex $lCols $i]" + } else { + append selectlist ", 0" + } + } + } else { + set order " ORDER BY [join $lO ,]" + } + } + + set where "" + if {[llength $W]} { set where " WHERE [join $W { AND }]" } + set sql "SELECT $selectlist FROM $src$where$order$L$O" + + lappend ret idxStr $sql + return $ret + } + + xFilter { + foreach {idxnum idxstr lArg} $args {} + set ii 0 + set sql $idxstr + foreach a $lArg { + set sql [string map [list %$ii% $a] $sql] + set sql [string map [list %I$ii% [join $a ,]] $sql] + incr ii + } + lappend ::lFilterSql $sql + + if {[regexp {OFFSET (.*)$} $sql -> off]} { + set real_sql " + WITH c(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM c WHERE i<$off ) + SELECT 0,0,0,0 FROM c + UNION ALL SELECT * FROM ( + $sql + ) + " + } else { + set real_sql $sql + } + + return [list sql $real_sql] + } + + } + + return {} +} + +do_execsql_test 5.0 { + CREATE TABLE t1(a, b, c); + CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1); + INSERT INTO t1 VALUES(1, 2, 3); + INSERT INTO t1 VALUES(2, 3, 4); + INSERT INTO t1 VALUES(3, 4, 5); + INSERT INTO t1 VALUES(1, 5, 6); + INSERT INTO t1 VALUES(2, 6, 7); + INSERT INTO t1 VALUES(3, 7, 8); + INSERT INTO t1 VALUES(1, 8, 9); + INSERT INTO t1 VALUES(2, 9, 0); +} + +proc do_vtab_test {tn sql vtsql {res {}}} { + set ::lFilterSql [list] + uplevel [list do_execsql_test $tn.1 $sql $res] + uplevel [list do_test $tn.2 {set ::lFilterSql} [list {*}$vtsql]] +} + +do_vtab_test 5.1.1 { + SELECT DISTINCT a FROM vt1 +} { + {SELECT DISTINCT 0, a, 0, 0 FROM t1} +} {1 2 3} + +do_vtab_test 5.1.2 { + SELECT DISTINCT a FROM vt1 ORDER BY a +} { + {SELECT rowid, a, b, c FROM t1 ORDER BY a} +} {1 2 3} + +do_vtab_test 5.1.3 { + SELECT DISTINCT a FROM vt1 WHERE c IN (4,5,6,7,8) +} { + {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c IN (4,5,6,7,8)} +} {2 3 1} + +set ::vtab_handle_in 0 +do_vtab_test 5.1.4 { + SELECT DISTINCT a FROM vt1 WHERE c IN (4,5,6,7,8) +} { + {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 4} + {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 5} + {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 6} + {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 7} + {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 8} +} {2 3 1} + +set ::vtab_handle_in 1 +do_vtab_test 5.1.5a { + SELECT a, b, c FROM vt1 WHERE c IN (4,5,6,7,8) LIMIT 2 OFFSET 2 +} { + {SELECT rowid, a, b, c FROM t1 WHERE c IN (4,5,6,7,8) LIMIT 2 OFFSET 2} +} {1 5 6 2 6 7} + +set ::vtab_handle_in 0 +do_vtab_test 5.1.5b { + SELECT a, b, c FROM vt1 WHERE c IN (4,5,6,7,8) LIMIT 2 OFFSET 2 +} { + {SELECT rowid, a, b, c FROM t1 WHERE c = 4} + {SELECT rowid, a, b, c FROM t1 WHERE c = 5} + {SELECT rowid, a, b, c FROM t1 WHERE c = 6} + {SELECT rowid, a, b, c FROM t1 WHERE c = 7} +} {1 5 6 2 6 7} +set ::vtab_handle_in 1 + +finish_test diff --git a/test/rowvalue5.test b/test/rowvalue5.test index 58b39f2793..b91dfa49ac 100644 --- a/test/rowvalue5.test +++ b/test/rowvalue5.test @@ -46,7 +46,9 @@ proc vtab_command {method args} { set OP(glob) GLOB set OP(regexp) REGEXP - set clist [lindex $args 0] + set hdl [lindex $args 0] + set clist [$hdl constraints] + set ret [list] set elist [list] set i 0 From cc0db61364b9eec3d91c069e280a258a555aa65d Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 2 Feb 2022 19:30:24 +0000 Subject: [PATCH 34/40] Additional test cases. FossilOrigin-Name: 733d81c3a6a513b0b893a7d14894f36aebbbca9da375c326db8a72df4f0c6238 --- manifest | 12 ++++++------ manifest.uuid | 2 +- test/bestindex8.test | 31 +++++++++++++++++-------------- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/manifest b/manifest index 95ed26d241..89b1c257a5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Test\scases\sfor\ssqlite3_vtab_in()\sand\ssqlite3_vtab_distinct(). -D 2022-02-02T19:15:53.331 +C Additional\stest\scases. +D 2022-02-02T19:30:24.554 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -731,7 +731,7 @@ F test/bestindex4.test 3039894f2dad50f3a68443dffad1b44c9b067ac03870102df1ce3d9a4 F test/bestindex5.test a0c90b2dad7836e80a01379e200e5f8ec9476d49b349af02c0dbff2fb75dc98d F test/bestindex6.test 16942535b551273f3ad9df8d7cc4b7f22b1fcd8882714358859eb049a6f99dd4 F test/bestindex7.test f094c669a6400777f4d2ddc3ed28e39169f1adb5be3d59b55f22ccf8c414b71e -F test/bestindex8.test 025477dd9bdb462f4faef3dd8838306f4e230620677778f4fe27e6736789e1b8 +F test/bestindex8.test abd0016fc04f19dc382976750b06df5463d2757e11e78a8ba7d7dc50671f3337 F test/between.test b9a65fb065391980119e8a781a7409d3fcf059d89968279c750e190a9a1d5263 F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59 F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc @@ -1943,8 +1943,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P c99df4ab5db2c32b044366c5b0ac70fd8887d1456d53323e75fede23cc61c236 -R 73bbb4581fa3d24d1ee5b28ca166750f +P 21afb81d0a73af39aacd9329b1441faa2b535a52a52036daec89fd303a8b344f +R bd0b74bd91f16e8219ac183cf658cda6 U drh -Z 0537364a2100dee8e5b21a6d334bfbef +Z 4de6a8e85d90e316804149623317b18b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ef06714857..e0d2343378 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -21afb81d0a73af39aacd9329b1441faa2b535a52a52036daec89fd303a8b344f \ No newline at end of file +733d81c3a6a513b0b893a7d14894f36aebbbca9da375c326db8a72df4f0c6238 \ No newline at end of file diff --git a/test/bestindex8.test b/test/bestindex8.test index a9cc56a806..6816400771 100644 --- a/test/bestindex8.test +++ b/test/bestindex8.test @@ -49,7 +49,7 @@ proc vtab_command {src method args} { } { lappend ret orderby 1 lappend ret idxnum 1 - #puts "ORDER-BY-CONSUMED" + set ::lOrderByConsumed 1 } return $ret } @@ -78,22 +78,22 @@ do_execsql_test 1.0 { INSERT INTO t0(c0) VALUES (1), (0); } -foreach {tn sql bDistinct idxinsert res} { - 1 "SELECT a, b FROM vt1" 0 0 {a b c d a b c d} - 2 "SELECT DISTINCT a, b FROM vt1" 2 1 {a b c d} - 3 "SELECT DISTINCT a FROM vt1" 2 1 {a c} - 4 "SELECT DISTINCT b FROM vt1" 2 1 {b d} - 5 "SELECT DISTINCT b FROM vt1 ORDER BY a" 0 1 {b d} - 6 "SELECT DISTINCT t0.c0 FROM vt1, t0 ORDER BY vt1.a" 0 1 {1 0} - 7 "SELECT DISTINCT a, b FROM vt1 ORDER BY a, b" 1 0 {a b c d} - 8 "SELECT DISTINCT a, b FROM vt1 ORDER BY a" 0 1 {a b c d} - 9 "SELECT DISTINCT a FROM vt1 ORDER BY a, b" 0 1 {a c} +foreach {tn sql bDistinct idxinsert bConsumed res} { + 1 "SELECT a, b FROM vt1" 0 0 0 {a b c d a b c d} + 2 "SELECT DISTINCT a, b FROM vt1" 2 1 1 {a b c d} + 3 "SELECT DISTINCT a FROM vt1" 2 1 1 {a c} + 4 "SELECT DISTINCT b FROM vt1" 2 1 0 {b d} + 5 "SELECT DISTINCT b FROM vt1 ORDER BY a" 0 1 1 {b d} + 6 "SELECT DISTINCT t0.c0 FROM vt1, t0 ORDER BY vt1.a" 0 1 1 {1 0} + 7 "SELECT DISTINCT a, b FROM vt1 ORDER BY a, b" 1 0 1 {a b c d} + 8 "SELECT DISTINCT a, b FROM vt1 ORDER BY a" 0 1 1 {a b c d} + 9 "SELECT DISTINCT a FROM vt1 ORDER BY a, b" 0 1 1 {a c} - 10 "SELECT DISTINCT a, b FROM vt1 WHERE b='b'" 2 1 {a b} - 11 "SELECT DISTINCT a, b FROM vt1 WHERE +b='b'" 2 1 {a b} + 10 "SELECT DISTINCT a, b FROM vt1 WHERE b='b'" 2 1 1 {a b} + 11 "SELECT DISTINCT a, b FROM vt1 WHERE +b='b'" 2 1 1 {a b} } { set ::lBestIndexDistinct "" -if {$tn==10} breakpoint + set ::lOrderByConsumed 0 do_execsql_test 1.$tn.1 $sql $res do_test 1.$tn.2 { set ::lBestIndexDistinct @@ -101,6 +101,9 @@ if {$tn==10} breakpoint do_test 1.$tn.3 { expr {[lsearch [execsql "explain $sql"] IdxInsert]>=0} } $idxinsert + do_test 1.$tn.4 { + set ::lOrderByConsumed + } $bConsumed } #------------------------------------------------------------------------- From 19eef9af64bd20a680d476808c76695d24d26891 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 2 Feb 2022 21:06:40 +0000 Subject: [PATCH 35/40] Improvement to the sqlite3_vtab_in_first() documentation. FossilOrigin-Name: 403e7ac900e53407282393747b784dc0cd4303958394534e3936fce138bc1bc5 --- manifest | 13 ++++++------- manifest.uuid | 2 +- src/sqlite.h.in | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 7370aa8b6f..3f090a198b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\ssqlite3_vtab_in()\sinterface\sthat\sallows\svirtual\stables\sto\sprocess\nIN\sconstraints\sall\sat\sonce,\srather\sthan\sone\svalue\sat\sa\stime. -D 2022-02-02T19:51:44.684 +C Improvement\sto\sthe\ssqlite3_vtab_in_first()\sdocumentation. +D 2022-02-02T21:06:40.067 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -554,7 +554,7 @@ F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2 F src/shell.c.in 2f58e6aa6b3d2012db32f1c5fa4591e9d12fd582904632b4fc8f57a382b98fd3 -F src/sqlite.h.in a6e6fd9defb576af6444a85f446aaa738dea3384c48db4fe9007fb8ff954b7c5 +F src/sqlite.h.in d4daed3b2deb2e43df7f12be119b1b17a2954feadc33d521565bacbd072bfcb6 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h a95cb9ed106e3d39e2118e4dcc15a14faec3fa50d0093425083d340d9dfd96e6 F src/sqliteInt.h 838df3e9ba9390058076d2f50c7efde9e0e8747303e788cf5bbe05402ab10924 @@ -1943,9 +1943,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 41d8d26e48a440da44582763f6b0c097febe3b8ac16adb662f688c3662c301bf 733d81c3a6a513b0b893a7d14894f36aebbbca9da375c326db8a72df4f0c6238 -R bea83344da84fc60a55eb54290dfe95b -T +closed 733d81c3a6a513b0b893a7d14894f36aebbbca9da375c326db8a72df4f0c6238 +P 52559af093809b572082b5ebaacf97b727ee1860ae118530761b62e937545163 +R 36ca82b24ac7898d0154e5e4119378ab U drh -Z 2ffca9e5143ef0b555ac148560cccc3c +Z 3656a02abcf77fbfe8a689ff58f19714 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ff5b967a96..9200e8bedd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -52559af093809b572082b5ebaacf97b727ee1860ae118530761b62e937545163 \ No newline at end of file +403e7ac900e53407282393747b784dc0cd4303958394534e3936fce138bc1bc5 \ No newline at end of file diff --git a/src/sqlite.h.in b/src/sqlite.h.in index ead938a7dd..42cc332962 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -9721,7 +9721,7 @@ int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle); ** next call to either of these routines or until the end of the xFilter ** method from which these routines were called. If the virtual table ** implementation needs to retain the *ppOut values for longer, it must make -** copies. +** copies. The *ppOut values are [protected sqlite3_value|protected]. */ int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut); int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut); From 3832c3c10f6b3c1d62440c43fb31dfad29ae5d5f Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 3 Feb 2022 14:19:26 +0000 Subject: [PATCH 36/40] Prohibit schema changes within xBestIndex callbacks. FossilOrigin-Name: bb0f056b3f2ecc3893e5e48e99c060788550ea6a8ed02b715953af051bafbad0 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/where.c | 2 ++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 3f090a198b..849f155ef8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improvement\sto\sthe\ssqlite3_vtab_in_first()\sdocumentation. -D 2022-02-02T21:06:40.067 +C Prohibit\sschema\schanges\swithin\sxBestIndex\scallbacks. +D 2022-02-03T14:19:26.747 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -639,7 +639,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b -F src/where.c d7d996a5d4501fbf14997e965d7f641fe7b2a0dd89b2296e5310b506da3fb822 +F src/where.c 31bc1f43b0bf679e93c3e7a7d67cbcaddc7ae746694b149b282427d337b06caa F src/whereInt.h 1d821657238a0bd12b3c8f2926c7f8f9294bc5efe20af53c7c50d53a0a026cb9 F src/wherecode.c 5879604677f0bdfb8d95ff616d834daecc12256346b7d9ad96a7e84a1cb08fdc F src/whereexpr.c fa8017f9fed4b50c59b4f105139679dd59ed7fea43179ebb0f6b8b5796a30515 @@ -1943,8 +1943,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 52559af093809b572082b5ebaacf97b727ee1860ae118530761b62e937545163 -R 36ca82b24ac7898d0154e5e4119378ab +P 403e7ac900e53407282393747b784dc0cd4303958394534e3936fce138bc1bc5 +R 41eaf90a96fe65f5bd93b76bd7a9c7b0 U drh -Z 3656a02abcf77fbfe8a689ff58f19714 +Z 4e2814b092bdcbebe8f5fecedd9981df # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9200e8bedd..1fc1027fa6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -403e7ac900e53407282393747b784dc0cd4303958394534e3936fce138bc1bc5 \ No newline at end of file +bb0f056b3f2ecc3893e5e48e99c060788550ea6a8ed02b715953af051bafbad0 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 11eae60e6a..bd42789770 100644 --- a/src/where.c +++ b/src/where.c @@ -1335,7 +1335,9 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ int rc; whereTraceIndexInfoInputs(p); + pParse->db->nSchemaLock++; rc = pVtab->pModule->xBestIndex(pVtab, p); + pParse->db->nSchemaLock--; whereTraceIndexInfoOutputs(p); if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){ From 697c50b947af978dfb9997328b289455a97e8743 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 3 Feb 2022 14:37:06 +0000 Subject: [PATCH 37/40] Do not allow the vdbe-compress.tcl script to generate structures named "do" or "if". FossilOrigin-Name: e35ab16dd0fd40b07a71d26e1cdb19ef664a2a628a4abcb2e130f5e555de8ca9 --- manifest | 12 ++++++------ manifest.uuid | 2 +- tool/vdbe-compress.tcl | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 849f155ef8..228338465d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Prohibit\sschema\schanges\swithin\sxBestIndex\scallbacks. -D 2022-02-03T14:19:26.747 +C Do\snot\sallow\sthe\svdbe-compress.tcl\sscript\sto\sgenerate\sstructures\snamed\n"do"\sor\s"if". +D 2022-02-03T14:37:06.680 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1917,7 +1917,7 @@ F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43 F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d F tool/symbols.sh 1612bd947750e21e7b47befad5f6b3825b06cce0705441f903bf35ced65ae9b9 F tool/varint.c 5d94cb5003db9dbbcbcc5df08d66f16071aee003 -F tool/vdbe-compress.tcl 5926c71f9c12d2ab73ef35c29376e756eb68361c +F tool/vdbe-compress.tcl 1dcb7632e57cf57105248029e6e162fddaf6c0fccb3bb9e6215603752c5a2d4a F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh 09311479bdc290e20ec8e35a3d1b14b096bbd96222277cfd6274c3a99b3d012f @@ -1943,8 +1943,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 403e7ac900e53407282393747b784dc0cd4303958394534e3936fce138bc1bc5 -R 41eaf90a96fe65f5bd93b76bd7a9c7b0 +P bb0f056b3f2ecc3893e5e48e99c060788550ea6a8ed02b715953af051bafbad0 +R b66cc5b551133399f9bcd1024e701d2a U drh -Z 4e2814b092bdcbebe8f5fecedd9981df +Z 9f9648682364ae609fa9e4c552fa2be1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1fc1027fa6..2ad651df77 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bb0f056b3f2ecc3893e5e48e99c060788550ea6a8ed02b715953af051bafbad0 \ No newline at end of file +e35ab16dd0fd40b07a71d26e1cdb19ef664a2a628a4abcb2e130f5e555de8ca9 \ No newline at end of file diff --git a/tool/vdbe-compress.tcl b/tool/vdbe-compress.tcl index 9477f4afe6..5e63c96a9a 100644 --- a/tool/vdbe-compress.tcl +++ b/tool/vdbe-compress.tcl @@ -65,7 +65,7 @@ while {![eof stdin]} { # set vlist {} set seenDecl 0 -set namechars {abcdefghijklmnopqrstuvwxyz} +set namechars {abcefghjklmnopqrstuvwxyz} set nnc [string length $namechars] while {![eof stdin]} { set line [gets stdin] From 7f1c11194825583ffee08ef1a57e6e0d0082c33b Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 3 Feb 2022 18:14:22 +0000 Subject: [PATCH 38/40] Minor typo fix in comment. No code changes. FossilOrigin-Name: f9b763042c5c4446115c5375a8e4731e522e36ad9fc53e9ef775de9bc297ea19 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/resolve.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 228338465d..844ec5dafa 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\sallow\sthe\svdbe-compress.tcl\sscript\sto\sgenerate\sstructures\snamed\n"do"\sor\s"if". -D 2022-02-03T14:37:06.680 +C Minor\stypo\sfix\sin\scomment.\sNo\scode\schanges. +D 2022-02-03T18:14:22.642 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -550,7 +550,7 @@ F src/pragma.h 87330ed2fbfa2a1274de93ca0ab850fba336189228cb256089202c3b52766fad F src/prepare.c a187dade741c1f09ae118fcbbf0302511807bfc0355880927d7152eb75b8260d F src/printf.c 975f1f5417f2526365b6e6d7f22332e3e11806dad844701d92846292b654ba9a F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c -F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 +F src/resolve.c 0dd8e23fda88411d63b2e6650f2380fdb08e88112e9a095fc23d5a08de4b2641 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2 F src/shell.c.in 2f58e6aa6b3d2012db32f1c5fa4591e9d12fd582904632b4fc8f57a382b98fd3 @@ -1943,8 +1943,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P bb0f056b3f2ecc3893e5e48e99c060788550ea6a8ed02b715953af051bafbad0 -R b66cc5b551133399f9bcd1024e701d2a +P e35ab16dd0fd40b07a71d26e1cdb19ef664a2a628a4abcb2e130f5e555de8ca9 +R 20ef9d61f41a89bdff8a0b9888d3988c U drh -Z 9f9648682364ae609fa9e4c552fa2be1 +Z bc10ba616693d19ec8e6518f2199687e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2ad651df77..a441058899 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e35ab16dd0fd40b07a71d26e1cdb19ef664a2a628a4abcb2e130f5e555de8ca9 \ No newline at end of file +f9b763042c5c4446115c5375a8e4731e522e36ad9fc53e9ef775de9bc297ea19 \ No newline at end of file diff --git a/src/resolve.c b/src/resolve.c index aae046e760..78135cf3b4 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -995,7 +995,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ /* Internal-use-only functions are disallowed unless the ** SQL is being compiled using sqlite3NestedParse() or ** the SQLITE_TESTCTRL_INTERNAL_FUNCTIONS test-control has be - ** used to activate internal functionsn for testing purposes */ + ** used to activate internal functions for testing purposes */ no_such_func = 1; pDef = 0; }else From 02e3e041343e6ac950771bb5ef813f1876f5036b Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 4 Feb 2022 13:05:29 +0000 Subject: [PATCH 39/40] For the MULTI-INDEX-OR optimization, when pushing down WHERE clause terms from the main query into the various OR-term subqueries, do not push down slices of a vector comparison, since the right-hand operand of the comparison might have only been initialized in a different OR branch that was not taken. dbsqlfuzz 80a9fade844b4fb43564efc972bcb2c68270f5d1. FossilOrigin-Name: 9f67ad00cd38b7c5ec6d14b379e1a611777bbdf6901d843a80712ba7d94d6d33 --- manifest | 18 +++++++++--------- manifest.uuid | 2 +- src/whereInt.h | 1 + src/wherecode.c | 13 +++++++++++-- src/whereexpr.c | 2 +- test/rowvalue.test | 18 ++++++++++++++++++ 6 files changed, 41 insertions(+), 13 deletions(-) diff --git a/manifest b/manifest index 844ec5dafa..3f02ec9d75 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Minor\stypo\sfix\sin\scomment.\sNo\scode\schanges. -D 2022-02-03T18:14:22.642 +C For\sthe\sMULTI-INDEX-OR\soptimization,\swhen\spushing\sdown\sWHERE\sclause\sterms\sfrom\nthe\smain\squery\sinto\sthe\svarious\sOR-term\ssubqueries,\sdo\snot\spush\sdown\sslices\nof\sa\svector\scomparison,\ssince\sthe\sright-hand\soperand\sof\sthe\scomparison\smight\nhave\sonly\sbeen\sinitialized\sin\sa\sdifferent\sOR\sbranch\sthat\swas\snot\staken.\ndbsqlfuzz\s80a9fade844b4fb43564efc972bcb2c68270f5d1. +D 2022-02-04T13:05:29.719 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -640,9 +640,9 @@ F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b F src/where.c 31bc1f43b0bf679e93c3e7a7d67cbcaddc7ae746694b149b282427d337b06caa -F src/whereInt.h 1d821657238a0bd12b3c8f2926c7f8f9294bc5efe20af53c7c50d53a0a026cb9 -F src/wherecode.c 5879604677f0bdfb8d95ff616d834daecc12256346b7d9ad96a7e84a1cb08fdc -F src/whereexpr.c fa8017f9fed4b50c59b4f105139679dd59ed7fea43179ebb0f6b8b5796a30515 +F src/whereInt.h 099c7fa273f34afa5adc7f25dcacc48df70392ce224089b641b1c9c5b01d5598 +F src/wherecode.c 4a0dd0403e1c9b628a420eefbe1d60da0003356de6ee18e6707480c9b995bae7 +F src/whereexpr.c 2da56404a024dc8dc41a31d8b498eed2c7c6b0bb412150d88cb3327516aa3b9f F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 @@ -1324,7 +1324,7 @@ F test/round1.test 768018b04522ca420b1aba8a24bd76091d269f3bce3902af3ec6ebcee41ab F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test e29025be95baf6b32f0d5edef59a7633028325896a98f1caa8019559ca910350 -F test/rowvalue.test 02214016f747854ef636e64ff204778649937aa801ca78e2495a960f8e0d509d +F test/rowvalue.test 228b312f8526ed000ecda559a6a9adf30aa2d79dcb12afa5c04eebaafcf55eae F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b F test/rowvalue3.test 3068f508753af69884b12125995f023da0dbb256 F test/rowvalue4.test 441e7e366ac6d939a3a95a574031c56ec2a854077a91d66eee5ff1d86cb5be58 @@ -1943,8 +1943,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e35ab16dd0fd40b07a71d26e1cdb19ef664a2a628a4abcb2e130f5e555de8ca9 -R 20ef9d61f41a89bdff8a0b9888d3988c +P f9b763042c5c4446115c5375a8e4731e522e36ad9fc53e9ef775de9bc297ea19 +R 928b302c497557d2f34fa5bbc849c67c U drh -Z bc10ba616693d19ec8e6518f2199687e +Z 80d96bf6fd3b06fc995fb22f494a3906 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a441058899..9993c3d6b7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f9b763042c5c4446115c5375a8e4731e522e36ad9fc53e9ef775de9bc297ea19 \ No newline at end of file +9f67ad00cd38b7c5ec6d14b379e1a611777bbdf6901d843a80712ba7d94d6d33 \ No newline at end of file diff --git a/src/whereInt.h b/src/whereInt.h index fc3740f519..8cd4144874 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -285,6 +285,7 @@ struct WhereTerm { #else # define TERM_HIGHTRUTH 0 /* Only used with STAT4 */ #endif +#define TERM_SLICE 0x8000 /* One slice of a row-value/vector comparison */ /* ** An instance of the WhereScan object is used as an iterator for locating diff --git a/src/wherecode.c b/src/wherecode.c index 8d7163ce08..603fcdfd76 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -2317,7 +2317,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn); /* If the original WHERE clause is z of the form: (x1 OR x2 OR ...) AND y - ** Then for every term xN, evaluate as the subexpression: xN AND z + ** Then for every term xN, evaluate as the subexpression: xN AND y ** That way, terms in y that are factored into the disjunction will ** be picked up by the recursive calls to sqlite3WhereBegin() below. ** @@ -2329,6 +2329,12 @@ Bitmask sqlite3WhereCodeOneLoopStart( ** This optimization also only applies if the (x1 OR x2 OR ...) term ** is not contained in the ON clause of a LEFT JOIN. ** See ticket http://www.sqlite.org/src/info/f2369304e4 + ** + ** 2022-02-04: Do not push down slices of a row-value comparison. + ** In other words, "w" or "y" may not be a slice of a vector. Otherwise, + ** the initialization of the right-hand operand of the vector comparison + ** might not occur, or might occur only in an OR branch that is not + ** taken. dbsqlfuzz 80a9fade844b4fb43564efc972bcb2c68270f5d1. */ if( pWC->nTerm>1 ){ int iTerm; @@ -2337,7 +2343,10 @@ Bitmask sqlite3WhereCodeOneLoopStart( if( &pWC->a[iTerm] == pTerm ) continue; testcase( pWC->a[iTerm].wtFlags & TERM_VIRTUAL ); testcase( pWC->a[iTerm].wtFlags & TERM_CODED ); - if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED))!=0 ) continue; + testcase( pWC->a[iTerm].wtFlags & TERM_SLICE ); + if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED|TERM_SLICE))!=0 ){ + continue; + } if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue; testcase( pWC->a[iTerm].wtFlags & TERM_ORINFO ); pExpr = sqlite3ExprDup(db, pExpr, 0); diff --git a/src/whereexpr.c b/src/whereexpr.c index e212bcb932..ac4f2e1fbe 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1399,7 +1399,7 @@ static void exprAnalyze( pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight); transferJoinMarkings(pNew, pExpr); - idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC); + idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC|TERM_SLICE); exprAnalyze(pSrc, pWC, idxNew); } pTerm = &pWC->a[idxTerm]; diff --git a/test/rowvalue.test b/test/rowvalue.test index 7c101d9b3e..92d3260e6c 100644 --- a/test/rowvalue.test +++ b/test/rowvalue.test @@ -708,5 +708,23 @@ do_execsql_test 31.2 { SELECT * FROM t1 LEFT JOIN t2 ON b=NULL WHERE (c,d)==(SELECT 123, 456+a); } {} +# 2022-02-03 dbsqlfuzz 80a9fade844b4fb43564efc972bcb2c68270f5d1 +reset_db +do_execsql_test 32.1 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT, c INT); + CREATE TABLE t2(d INTEGER PRIMARY KEY); + INSERT INTO t1(a,b,c) VALUES(500,654,456); + INSERT INTO t1(a,b,c) VALUES(501,655,456); + INSERT INTO t1(a,b,c) VALUES(502,654,122); + INSERT INTO t1(a,b,c) VALUES(503,654,221); + INSERT INTO t1(a,b,c) VALUES(601,654,122); + INSERT INTO t2(d) VALUES(456); + INSERT INTO t2(d) VALUES(122); + SELECT a FROM ( + SELECT t1.a FROM t2, t1 + WHERE (987, t1.b) = ( SELECT 987, 654 ) AND t2.d=t1.c + ) AS t3 + WHERE a=1234 OR a<=567; +} {500 502} finish_test From 69b0ce33fd4b1be019be2d64dd99c6bc7ccf5693 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 4 Feb 2022 13:15:01 +0000 Subject: [PATCH 40/40] Fix various harmless compiler warnings. FossilOrigin-Name: 70049342d5ad57ea3e863bba19253934b868bacdd1c26c9371bac024a829badf --- manifest | 18 +++++++++--------- manifest.uuid | 2 +- src/expr.c | 1 - src/func.c | 1 + src/json.c | 2 +- src/whereInt.h | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/manifest b/manifest index 3f02ec9d75..4eafb3043f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C For\sthe\sMULTI-INDEX-OR\soptimization,\swhen\spushing\sdown\sWHERE\sclause\sterms\sfrom\nthe\smain\squery\sinto\sthe\svarious\sOR-term\ssubqueries,\sdo\snot\spush\sdown\sslices\nof\sa\svector\scomparison,\ssince\sthe\sright-hand\soperand\sof\sthe\scomparison\smight\nhave\sonly\sbeen\sinitialized\sin\sa\sdifferent\sOR\sbranch\sthat\swas\snot\staken.\ndbsqlfuzz\s80a9fade844b4fb43564efc972bcb2c68270f5d1. -D 2022-02-04T13:05:29.719 +C Fix\svarious\sharmless\scompiler\swarnings. +D 2022-02-04T13:15:01.508 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -503,17 +503,17 @@ F src/date.c 41627dec396f3d33e2c317a065f9d59bb535982b2ea3a561c96e4d4cf1137b65 F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d F src/delete.c b5f1716b4d723db48254ee0f896e362cd029e865e05414139ea7f539f3884e1d -F src/expr.c 9658bccd1598211ace848c8ca9480dbf8be08dfee1db5cf03897b34b7b6e8fef +F src/expr.c 31d23e6b57827b4cb8f0054f44e2ed86feb1dfcaa9b0c4d6c960b42536a17ca0 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c 06e4ac33031b02dde7130c12e79cddf4dc5cfa72b23d8e63a3c26878fc9c1d3c -F src/func.c 8fddc42bce95d17938252a543f86fe29e479366e80fbd112a1822913b6247776 +F src/func.c 5a0379450bd19ca5cb2d65327b0df6466a14fa0f06f9355329c3e0eec483519a F src/global.c 1f56aead86e8a18c4415638f5e6c4d0a0550427f4b3f5d065ba5164cc09c22e8 F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19 F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 1eea44389de3768ac98588c1410171cd53e7c6ad1af74049983dcbac82093de0 -F src/json.c 78fdec9af3a8bfb5ae685707b2701276fec1942b8f5f26689b2701debe32bcd2 +F src/json.c 225b00422112ecd7094a555f3ace16b25d7d5894062b823269ed03899907c2a2 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c aa919a6a7884f8b34d7b791841b24d14b1b0ab43f45b3940f4851043b2855c0c F src/main.c 2b6b0dbfeb14d4bb57e368604b0736b2aa42b51b00339d399b01d6b1fc9b4960 @@ -640,7 +640,7 @@ F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b F src/where.c 31bc1f43b0bf679e93c3e7a7d67cbcaddc7ae746694b149b282427d337b06caa -F src/whereInt.h 099c7fa273f34afa5adc7f25dcacc48df70392ce224089b641b1c9c5b01d5598 +F src/whereInt.h 15d2975c3b4c193c78c26674400a840da8647fe1777ae3b026e2d15937b38a03 F src/wherecode.c 4a0dd0403e1c9b628a420eefbe1d60da0003356de6ee18e6707480c9b995bae7 F src/whereexpr.c 2da56404a024dc8dc41a31d8b498eed2c7c6b0bb412150d88cb3327516aa3b9f F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d @@ -1943,8 +1943,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P f9b763042c5c4446115c5375a8e4731e522e36ad9fc53e9ef775de9bc297ea19 -R 928b302c497557d2f34fa5bbc849c67c +P 9f67ad00cd38b7c5ec6d14b379e1a611777bbdf6901d843a80712ba7d94d6d33 +R ed2fd6b08e6120318ad847a61dee5a51 U drh -Z 80d96bf6fd3b06fc995fb22f494a3906 +Z 871646465b8062211df8c4f3cf586a8c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9993c3d6b7..2dd02dd9db 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9f67ad00cd38b7c5ec6d14b379e1a611777bbdf6901d843a80712ba7d94d6d33 \ No newline at end of file +70049342d5ad57ea3e863bba19253934b868bacdd1c26c9371bac024a829badf \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index d58097cb61..af28fd6d29 100644 --- a/src/expr.c +++ b/src/expr.c @@ -3497,7 +3497,6 @@ static void sqlite3ExprCodeIN( }else{ destStep2 = destStep6 = sqlite3VdbeMakeLabel(pParse); } -// if( pParse->nErr ) goto sqlite3ExprCodeIN_finished; for(i=0; ipLeft, i); if( pParse->nErr ) goto sqlite3ExprCodeIN_oom_error; diff --git a/src/func.c b/src/func.c index 81accc9cc3..d7ca756de8 100644 --- a/src/func.c +++ b/src/func.c @@ -106,6 +106,7 @@ static void subtypeFunc( int argc, sqlite3_value **argv ){ + UNUSED_PARAMETER(argc); sqlite3_result_int(context, sqlite3_value_subtype(argv[0])); } diff --git a/src/json.c b/src/json.c index 3f12f03fd1..ab1d32d6a5 100644 --- a/src/json.c +++ b/src/json.c @@ -2666,7 +2666,7 @@ int sqlite3JsonTableFunctions(sqlite3 *db){ { "json_each", &jsonEachModule }, { "json_tree", &jsonTreeModule }, }; - int i; + unsigned int i; for(i=0; i