diff --git a/ext/qrf/dev-notes.md b/ext/qrf/dev-notes.md new file mode 100644 index 0000000000..a46aada834 --- /dev/null +++ b/ext/qrf/dev-notes.md @@ -0,0 +1,14 @@ +# Developer Notes + +## Measuring Test Coverage On Linux + +On Mint Linux, as of 2025-12-02: + +> ~~~ +./configure --dev CFLAGS='-O0 -g -fprofile-arcs -ftest-coverage' +make clean testfixture +./testfixture test/qrf*.test +gcov -b -c testfixture-tclsqlite-ex.c +~~~ + +View results in tclsqlite-ex.c.gcov diff --git a/ext/qrf/qrf.c b/ext/qrf/qrf.c index 1e3a3a44ba..82df33876c 100644 --- a/ext/qrf/qrf.c +++ b/ext/qrf/qrf.c @@ -537,13 +537,20 @@ static int qrfIsVt100(const unsigned char *z){ /* ** Return the length of a string in display characters. -** Multibyte UTF8 characters count as a single character -** for single-width characters, or as two characters for -** double-width characters. +** +** Most characters of the input string count as 1, including +** multi-byte UTF8 characters. However, zero-width unicode +** characters and VT100 escape sequences count as zero, and +** double-width characters count as two. +** +** The definition of "zero-width" and "double-width" characters +** is not precise. It depends on the output device, to some extent, +** and it varies according to the Unicode version. This routine +** makes the best guess that it can. */ -static int qrfDisplayLength(const char *zIn){ +size_t sqlite3_qrf_wcswidth(const char *zIn){ const unsigned char *z = (const unsigned char*)zIn; - int n = 0; + size_t n = 0; while( *z ){ if( z[0]<' ' ){ int k; @@ -2198,7 +2205,7 @@ static void qrfExplain(Qrf *p){ int len; if( i==nArg-1 ) w = 0; if( zVal==0 ) zVal = ""; - len = qrfDisplayLength(zVal); + len = (int)sqlite3_qrf_wcswidth(zVal); if( len>w ){ w = len; zSep = " "; @@ -2388,7 +2395,7 @@ static void qrfOneSimpleRow(Qrf *p){ int sz; p->u.sLine.azCol[i] = sqlite3_column_name(p->pStmt, i); if( p->u.sLine.azCol[i]==0 ) p->u.sLine.azCol[i] = "unknown"; - sz = qrfDisplayLength(p->u.sLine.azCol[i]); + sz = (int)sqlite3_qrf_wcswidth(p->u.sLine.azCol[i]); if( sz > p->u.sLine.mxColWth ) p->u.sLine.mxColWth = sz; } } diff --git a/ext/qrf/qrf.h b/ext/qrf/qrf.h index baaeb4b18d..5e28d1e9cd 100644 --- a/ext/qrf/qrf.h +++ b/ext/qrf/qrf.h @@ -176,13 +176,21 @@ int sqlite3_format_query_result( ** character c. For normal characters, the answer is always 1. But the ** estimate might be 0 or 2 for zero-width and double-width characters. ** -** Different display devices display unicode using different widths. So +** Different devices display unicode using different widths. So ** it is impossible to know that true display width with 100% accuracy. ** Inaccuracies in the width estimates might cause columns to be misaligned. ** Unfortunately, there is nothing we can do about that. */ int sqlite3_qrf_wcwidth(int c); +/* +** Return an estimate of the number of display columns used by the +** string in the argument. The width of individual characters is +** determined as for sqlite3_qrf_wcwidth(). VT100 escape code sequences +** are assigned a width of zero. +*/ +size_t sqlite3_qrf_wcswidth(const char*); + #ifdef __cplusplus } diff --git a/ext/wasm/api/sqlite3-api-oo1.c-pp.js b/ext/wasm/api/sqlite3-api-oo1.c-pp.js index 780648d356..d150cd7ba3 100644 --- a/ext/wasm/api/sqlite3-api-oo1.c-pp.js +++ b/ext/wasm/api/sqlite3-api-oo1.c-pp.js @@ -1107,11 +1107,11 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ sqlite3.config.warn("DB.exec() is propagating exception",opt,e); throw e; }*/finally{ - wasm.scopedAllocPop(stack); if(stmt){ __execLock.delete(stmt); stmt.finalize(); } + wasm.scopedAllocPop(stack); } return arg.returnVal(); }/*exec()*/, diff --git a/manifest b/manifest index 8e98ef3c2d..1dfad5d679 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\smemory\sleak\sthat\scould\sfollow\san\sOOM\son\sthis\sbranch. -D 2025-12-01T18:11:08.093 +C Merge\sall\sthe\slatest\strunk\senhancements\sand\sfixes\sinto\sthe\stemp-trigger-refs\nbranch. +D 2025-12-04T11:20:01.398 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -417,8 +417,9 @@ F ext/misc/windirent.h 02211ce51f3034c675f2dbf4d228194d51b3ee05734678bad5106fff6 F ext/misc/zipfile.c 09e6e3a3ff40a99677de3c0bc6569bd5f4709b1844ac3d1c1452a456c5a62f1c F ext/misc/zorder.c bddff2e1b9661a90c95c2a9a9c7ecd8908afab5763256294dd12d609d4664eee F ext/qrf/README.md 86fc5c3c5e3eddbe54fc1235cbdc52b8c2c0732791d224345c3014cd45c4c0e7 -F ext/qrf/qrf.c 425d02cffcc5b5fe9ff5817794bf751b1fdd6912f570c354272429ce1262b866 -F ext/qrf/qrf.h 322d48537a5aa39c206c2ec0764a7938ea7662a8c25be1c4e9d742789609ba1e +F ext/qrf/dev-notes.md e68a6d91ce4c7eb296ef2daadc2bb79c95c317ad15b9fafe40850c67b29c2430 +F ext/qrf/qrf.c c50368ea2be3ab67bd6698d8b34bf70210d3fbf6b3659ed328543e5ec8d69eff +F ext/qrf/qrf.h 2cd7bd43579c522d3ae5c16034c7fdf41085684189811ae8e18ebc932a1cc79f F ext/rbu/rbu.c 801450b24eaf14440d8fd20385aacc751d5c9d6123398df41b1b5aa804bf4ce8 F ext/rbu/rbu1.test 25870dd7db7eb5597e2b4d6e29e7a7e095abf332660f67d89959552ce8f8f255 F ext/rbu/rbu10.test 7c22caa32c2ff26983ca8320779a31495a6555737684af7aba3daaf762ef3363 @@ -594,7 +595,7 @@ F ext/wasm/api/post-js-footer.js a50c1a2c4d008aede7b2aa1f18891a7ee71437c2f415b8a F ext/wasm/api/post-js-header.js d24bd0d065f3489c8b78ddf3ead6321e5d047187a162cd503c41700e03dd1f06 F ext/wasm/api/pre-js.c-pp.js d73088472f91856a27837c0588ac355516cea441ff67d2990effc2ca0627bf21 F ext/wasm/api/sqlite3-api-glue.c-pp.js 9b33e3ee467791dec4fd1b444b12a8545dfbb6c8b28ac651c7bdc7661a3b5a5c -F ext/wasm/api/sqlite3-api-oo1.c-pp.js 7a6c85b260082d72a5c355caf37876763d8770a29d9e0587751e584c31bf2777 +F ext/wasm/api/sqlite3-api-oo1.c-pp.js b2fa08dc02a0d852d88c59ce4a77cce3af033982f7eb41eeb81f1ba80f5f1b10 F ext/wasm/api/sqlite3-api-prologue.js fd836265adefbe4ed8a978326e0273bafcfb7d42f754b69a5d131a9031428c9f F ext/wasm/api/sqlite3-api-worker1.c-pp.js 1041dd645e8e821c082b628cd8d9acf70c667430f9d45167569633ffc7567938 F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89 @@ -668,7 +669,7 @@ F mptest/multiwrite01.test dab5c5f8f9534971efce679152c5146da265222d F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 1b9c24374a85dfc7eb8fa7c4266ee0db4f9609cceecfc5481cd8307e5af04366 F sqlite3.pc.in e6dee284fba59ef500092fdc1843df3be8433323a3733c91da96690a50a5b398 -F src/alter.c 06a27ab1cc064f5c6647e7fadfb12c8d7cafd35de1751f12419e1f2b28037540 +F src/alter.c 6c7791c7d95f66df7c510ed006bd8b0a58badfd7106ad2aa97f2d10ae3c3e06d F src/analyze.c 03bcfc083fc0cccaa9ded93604e1d4244ea245c17285d463ef6a60425fcb247d F src/attach.c 7cf07d4fa42b9fc8662237c60c40b730326c30aa90ae5fffc0b18b2d726ebf61 F src/auth.c 54ab9c6c5803b47c0d45b76ce27eff22a03b4b1f767c5945a3a4eb13aa4c78dc @@ -686,7 +687,7 @@ F src/date.c e19e0cfff9a41bfdd884c655755f6f00bca4c1a22272b56e0dd6667b7ea893a2 F src/dbpage.c c9ea81c11727f27e02874611e92773e68e2a90a875ef2404b084564c235fd91f F src/dbstat.c 73362c0df0f40ad5523a6f5501224959d0976757b511299bf892313e79d14f5c F src/delete.c 03a77ba20e54f0f42ebd8eddf15411ed6bdb06a2c472ac4b6b336521bf7cea42 -F src/expr.c 17b0cbe08e004c1653030f5de9b6b050e84feaa112239f7f576af2dc5e53a5fb +F src/expr.c b4530f3e23d8ea38c7de4c7b55af52d43156c79128c042a580e670776f59a9bc F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c c065da737307a29e4d240ac727758dbf4102cb3218a1f651eb689b6a6fa12531 F src/func.c 0b802107498048d3dcac0b757720bcb8506507ce02159e213ab8161458eb293b @@ -725,7 +726,7 @@ F src/os_win.c a89b501fc195085c7d6c9eec7f5bd782625e94bb2a96b000f4d009703df1083f F src/os_win.h 4c247cdb6d407c75186c94a1e84d5a22cbae4adcec93fcae8d2bc1f956fd1f19 F src/pager.c a81461de271ac4886ad75b7ca2cca8157a48635820c4646cd2714acdc2c17e5f F src/pager.h 6137149346e6c8a3ddc1eeb40aee46381e9bc8b0fcc6dda8a1efde993c2275b8 -F src/parse.y e31046636c50ba4a1cf4d12c7a6112a31b11975b1ade84a45f2109a30e8f358d +F src/parse.y 015375decff21fa3be5688189d58988458db8e3ccb821041b0fb7de4cd2fcf30 F src/pcache.c 588cc3c5ccaaadde689ed35ce5c5c891a1f7b1f4d1f56f6cf0143b74d8ee6484 F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5 F src/pcache1.c 131ca0daf4e66b4608d2945ae76d6ed90de3f60539afbd5ef9ec65667a5f2fcd @@ -733,18 +734,18 @@ F src/pragma.c ecec75795c1821520266e4f93fa8840cce48979af532db06f085e36a7813860f F src/prepare.c f6a6e28a281bd1d1da12f47d370a81af46159b40f73bf7fa0b276b664f9c8b7d F src/printf.c b1b29b5e58e1530d5daeee5963d3c318d8ab2d7e38437580e28755753e0c1ded F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c -F src/resolve.c 5616fbcf3b833c7c705b24371828215ad0925d0c0073216c4f153348d5753f0a +F src/resolve.c 8d53771eb51a4ab5f970150c3a70969d8db79cd04a8774c2d296bbcf471a0dd0 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c 6a509cddd815d64f6141e539fff633a518a393772a44dffb4490f7fc3f0d83a9 -F src/shell.c.in 8ec8bf61a3322c5e29c286d4aecbc82e25157df9244cb1a1a1c4e2c49de8b64d +F src/select.c 344518c1bba9c4636bf651b7642304abd2e7075ba35feb4bae42a51e5efe991f +F src/shell.c.in 62b286951404fd72c116bb3b96b5ee9330de4dc4c8753ca33967ca1a47b3b972 F src/sqlite.h.in 706cacea5308b0244fb6cec92e08310fb427a125375c64137cc1f878ae4cf5c0 F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 F src/sqlite3ext.h 5d5330f5f8461f5ce74960436ddcfa53ecd09c2b8b23901e22ae38aec3243998 -F src/sqliteInt.h f8b6a6bdb92aaeeedd768d27650a39f543c4b7cc66534c564e2ec249ed80e210 +F src/sqliteInt.h af67bc95fa6b66cd3c7f3d18d2d040ad386e4cbb02965ee318cc721ee9d5fa45 F src/sqliteLimit.h 0a5516b4ec192a205c541e05f67009028a9451dc6678aae4cf8e68596903c246 F src/status.c 7565d63a79aa2f326339a24a0461a60096d0bd2bce711fefb50b5c89335f3592 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 -F src/tclsqlite.c ec2ad3fbf5390c3d4307bc9d1f2a37a9c5d726b5309769dc770ab37f6812152d +F src/tclsqlite.c 4b6f6b142b951a8a127e0cbd2e37284fc71f8e6a391ec8c6188fb831b51ec0b8 F src/tclsqlite.h 614b3780a62522bc9f8f2b9fb22689e8009958e7aa77e572d0f3149050af348a F src/test1.c 0e71fbcb484a271564e98e0158192c28c24f5521594218c3ba48bcb4cf634f91 F src/test2.c 62f0830958f9075692c29c6de51b495ae8969e1bef85f239ffcd9ba5fb44a5ff @@ -820,8 +821,8 @@ F src/walker.c d5006d6b005e4ea7302ad390957a8d41ed83faa177e412f89bc5600a7462a014 F src/where.c 1b554a868134cbc9ca2192385403c0b63e5073ff01a6cdd600a846c09f843165 F src/whereInt.h 8d94cb116c9e06205c3d5ac87af065fc044f8cf08bfdccd94b6ea1c1308e65da F src/wherecode.c 71c5c6804b7f882dec8ec858758accae02fcfca13df3cc720f1f258e663ec7c5 -F src/whereexpr.c 403a44eeec1a0f0914fccc6a59376b6924bc00ef6728fe6ffce4cf3051b320fc -F src/window.c 538195bbc75bb924e18e368fbd4ed731a3fe3f901351b44f6466ec486f53affe +F src/whereexpr.c 5f412da6616e26b8000dd736a5ce5da0018dff0933cd656bbb25f407db16bf93 +F src/window.c c0a38cd32473e8e8e7bc435039f914a36ca42465506dc491c65870c01ddac9fb F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test 4d7a34d328e58ca2a2d78fd76c27614a41ca7ddf4312ded9c68c04f430b3b47d F test/affinity3.test 9b7d1133e11d5edd7805573c4ab6f3ba73b0b74a1f280d5b130d4bf3506a93ff @@ -851,7 +852,7 @@ F test/altermalloc3.test 8040e486368403f2fdd6fc3998258b499bd4cc2f3ddbb5f8f874cd4 F test/alterqf.test 8ec03d776de9c391daa0078ea8f838903bdcfb11dfae4ba3576b48436834ccba F test/altertab.test 8a2712f9076da5012a002d0b5cc0a421398a5bf61c25bab41b77c427586a7a27 F test/altertab2.test 0889ba0700cc1cdb7bc7d25975aa61fece34f621de963d0886e2395716b38576 -F test/altertab3.test 471b8898d10bbc6488db9c23dc76811f405de6707d2d342b1b8b6fd1f13cd3c8 +F test/altertab3.test 575e771e2f02b13eb98798dc92eabacd187d6dbcf596e70f11d699b0b6b5d0b2 F test/altertrig.test b1590647076add5a47aea0f2236c609ca0bc8a7a2462463edd3e5882c7894802 F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f F test/analyze.test 2fb21d7d64748636384e6cb8998dbf83968caf644c07fcb4f76c18f2e7ede94b @@ -1514,6 +1515,8 @@ F test/qrf01.test 48c86e90f606739e67b73937babb662ef0edf1ede06954489c588815fa17b5 F test/qrf02.test 39b4afdc000bedccdafc0aecf17638df67a67aaa2d2942865ae6abcc48ba0e92 F test/qrf03.test ad7fa339fb46b26b08625f3a77a4827bdee9f0dad402d8ddc1fcb8f05aa64a54 F test/qrf04.test 0894692c998d2401dcc33449c02051b503ecce0c94217be54fb007c82d2d1379 +F test/qrf05.test 8ade5bfa7ef0b448e531687203fa8ae9ef41f1d7e4c11d5ba0c4846af75b13d5 +F test/qrf06.test 3907f81c79fd884802cc7552d4a07fd8d73c381ade9d5108e6d7f56fd962b99b F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca F test/quick.test 1681febc928d686362d50057c642f77a02c62e57 F test/quickcheck.test a4b7e878cd97e46108291c409b0bf8214f29e18fddd68a42bc5c1375ad1fb80a @@ -1607,7 +1610,7 @@ F test/sharedA.test 64bdd21216dda2c6a3bd3475348ccdc108160f34682c97f2f51c19fc0e21 F test/sharedB.test 1a84863d7a2204e0d42f2e1606577c5e92e4473fa37ea0f5bdf829e4bf8ee707 F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304 -F test/shell1.test f377091779c4099619e78e1f55a0a12513eaac55d5073a14ca6545c6742c746c +F test/shell1.test b49a89913c194c37ca10c9ded1b6b6866506104621c6cc599e557e4cb5ed8cc2 F test/shell2.test 103140814bdc7508aa41dd3462413cbc4aa84b4261112cb8d501d74275cb7d48 F test/shell3.test 840192774cc4edf7653520c0434a311c7477b9bc324abbc7bd2887915792fa8c F test/shell4.test e25580a792b7b54560c3a76b6968bd8189261f38979fe28e6bc6312c5db280db @@ -1672,7 +1675,7 @@ F test/strict1.test a7f9091603fe71cdc62baab0766684cba12a97ec69bfbb70be965532669c F test/strict2.test b22c7a98b5000aef937f1990776497f0e979b1a23bc4f63e2d53b00e59b20070 F test/subjournal.test 8d4e2572c0ee9a15549f0d8e40863161295107e52f07a3e8012a2e1fdd093c49 F test/subquery.test 23087f9b1c15ab9cc5231d04946bdebc51db527c95eb9d7434a2222127e17a84 -F test/subquery2.test 5f06ec2dbce42a3f595ab1b73b146592f9ce001cd4ff023d887d643d3560c281 +F test/subquery2.test ab96ff3fa9c4e3dce0d699f74e61c50250ed4335bc8f400e127707d552a8999e F test/subselect.test 0966aa8e720224dbd6a5e769a3ec2a723e332303 F test/substr.test a673e3763e247e9b5e497a6cacbaf3da2bd8ec8921c0677145c109f2e633f36b F test/subtype1.test 96fd2a59bfc845c955b5f339d23b37ef4d50de5f8a04acd1450a68605fa2e3e7 @@ -2181,8 +2184,8 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 75c49267ec6b84c8aad0e264c34506e4b24f69475ae5def0ee7530ba3822c15d -R 2e866f8f96424c36ead738f7cc32a381 -U dan -Z b33421e6e016ff0d1ecfa51911613b2c +P 3223fee0b93bbbe277b955a98c13bb583ded78109f8e601c9ec0a7bd3749fd55 aef5397569d65d2971367b0278fe3a6f42544cf771572e7d046e2472f052364d +R 7195c1541663c387532db21aed885d33 +U drh +Z 19135e17dd3bb54a10cd401bdceed81e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7c57f38d88..4d8fccc6d1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3223fee0b93bbbe277b955a98c13bb583ded78109f8e601c9ec0a7bd3749fd55 +8e8a27c77aa0ea5891968231e13a8caeade48b817419bbf6eda18ad217f1df63 diff --git a/src/alter.c b/src/alter.c index bd124d0f0e..f24b838b4e 100644 --- a/src/alter.c +++ b/src/alter.c @@ -2110,6 +2110,57 @@ static void renameTableTest( #endif } + +/* +** Return the number of bytes until the end of the next non-whitespace and +** non-comment token. For the purpose of this function, a "(" token includes +** all of the bytes through and including the matching ")", or until the +** first illegal token, whichever comes first. +** +** Write the token type into *piToken. +** +** The value returned is the number of bytes in the token itself plus +** the number of bytes of leading whitespace and comments skipped plus +** all bytes through the next matching ")" if the token is TK_LP. +** +** Example: (Note: '.' used in place of '*' in the example z[] text) +** +** ,--------- *piToken := TK_RP +** v +** z[] = " /.comment./ --comment\n (two three four) five" +** | | +** |<-------------------------------------->| +** | +** `--- return value +*/ +static int getConstraintToken(const u8 *z, int *piToken){ + int iOff = 0; + int t = 0; + do { + iOff += sqlite3GetToken(&z[iOff], &t); + }while( t==TK_SPACE || t==TK_COMMENT ); + + *piToken = t; + + if( t==TK_LP ){ + int nNest = 1; + while( nNest>0 ){ + iOff += sqlite3GetToken(&z[iOff], &t); + if( t==TK_LP ){ + nNest++; + }else if( t==TK_RP ){ + t = TK_LP; + nNest--; + }else if( t==TK_ILLEGAL ){ + break; + } + } + } + + *piToken = t; + return iOff; +} + /* ** The implementation of internal UDF sqlite_drop_column(). ** @@ -2154,15 +2205,24 @@ static void dropColumnFunc( goto drop_column_done; } - pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol].zCnName); if( iColnCol-1 ){ RenameToken *pEnd; + pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol].zCnName); pEnd = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol+1].zCnName); zEnd = (const char*)pEnd->t.z; }else{ + int eTok; assert( IsOrdinaryTable(pTab) ); + assert( iCol!=0 ); + /* Point pCol->t.z at the "," immediately preceding the definition of + ** the column being dropped. To do this, start at the name of the + ** previous column, and tokenize until the next ",". */ + pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol-1].zCnName); + do { + pCol->t.z += getConstraintToken((const u8*)pCol->t.z, &eTok); + }while( eTok!=TK_COMMA ); + pCol->t.z--; zEnd = (const char*)&zSql[pTab->u.tab.addColOffset]; - while( ALWAYS(pCol->t.z[0]!=0) && pCol->t.z[0]!=',' ) pCol->t.z--; } zNew = sqlite3MPrintf(db, "%.*s%s", pCol->t.z-zSql, zSql, zEnd); @@ -2346,56 +2406,6 @@ static int getWhitespace(const u8 *z){ } -/* -** Return the number of bytes until the end of the next non-whitespace and -** non-comment token. For the purpose of this function, a "(" token includes -** all of the bytes through and including the matching ")", or until the -** first illegal token, whichever comes first. -** -** Write the token type into *piToken. -** -** The value returned is the number of bytes in the token itself plus -** the number of bytes of leading whitespace and comments skipped plus -** all bytes through the next matching ")" if the token is TK_LP. -** -** Example: (Note: '.' used in place of '*' in the example z[] text) -** -** ,--------- *piToken := TK_RP -** v -** z[] = " /.comment./ --comment\n (two three four) five" -** | | -** |<-------------------------------------->| -** | -** `--- return value -*/ -static int getConstraintToken(const u8 *z, int *piToken){ - int iOff = 0; - int t = 0; - do { - iOff += sqlite3GetToken(&z[iOff], &t); - }while( t==TK_SPACE || t==TK_COMMENT ); - - *piToken = t; - - if( t==TK_LP ){ - int nNest = 1; - while( nNest>0 ){ - iOff += sqlite3GetToken(&z[iOff], &t); - if( t==TK_LP ){ - nNest++; - }else if( t==TK_RP ){ - t = TK_LP; - nNest--; - }else if( t==TK_ILLEGAL ){ - break; - } - } - } - - *piToken = t; - return iOff; -} - /* ** Argument z points into the body of a constraint - specifically the ** second token of the constraint definition. For a named constraint, diff --git a/src/expr.c b/src/expr.c index 1a7b273fd1..3eea5439ee 100644 --- a/src/expr.c +++ b/src/expr.c @@ -935,34 +935,22 @@ Expr *sqlite3ExprAlloc( int dequote /* True to dequote */ ){ Expr *pNew; - int nExtra = 0; - int iValue = 0; + int nExtra = pToken ? pToken->n+1 : 0; assert( db!=0 ); - if( pToken ){ - if( op!=TK_INTEGER || pToken->z==0 - || sqlite3GetInt32(pToken->z, &iValue)==0 ){ - nExtra = pToken->n+1; /* tag-20240227-a */ - assert( iValue>=0 ); - } - } pNew = sqlite3DbMallocRawNN(db, sizeof(Expr)+nExtra); if( pNew ){ memset(pNew, 0, sizeof(Expr)); pNew->op = (u8)op; pNew->iAgg = -1; - if( pToken ){ - if( nExtra==0 ){ - pNew->flags |= EP_IntValue|EP_Leaf|(iValue?EP_IsTrue:EP_IsFalse); - pNew->u.iValue = iValue; - }else{ - pNew->u.zToken = (char*)&pNew[1]; - assert( pToken->z!=0 || pToken->n==0 ); - if( pToken->n ) memcpy(pNew->u.zToken, pToken->z, pToken->n); - pNew->u.zToken[pToken->n] = 0; - if( dequote && sqlite3Isquote(pNew->u.zToken[0]) ){ - sqlite3DequoteExpr(pNew); - } + if( nExtra ){ + assert( pToken!=0 ); + pNew->u.zToken = (char*)&pNew[1]; + assert( pToken->z!=0 || pToken->n==0 ); + if( pToken->n ) memcpy(pNew->u.zToken, pToken->z, pToken->n); + pNew->u.zToken[pToken->n] = 0; + if( dequote && sqlite3Isquote(pNew->u.zToken[0]) ){ + sqlite3DequoteExpr(pNew); } } #if SQLITE_MAX_EXPR_DEPTH>0 @@ -987,6 +975,24 @@ Expr *sqlite3Expr( return sqlite3ExprAlloc(db, op, &x, 0); } +/* +** Allocate an expression for a 32-bit signed integer literal. +*/ +Expr *sqlite3ExprInt32(sqlite3 *db, int iVal){ + Expr *pNew = sqlite3DbMallocRawNN(db, sizeof(Expr)); + if( pNew ){ + memset(pNew, 0, sizeof(Expr)); + pNew->op = TK_INTEGER; + pNew->iAgg = -1; + pNew->flags = EP_IntValue|EP_Leaf|(iVal?EP_IsTrue:EP_IsFalse); + pNew->u.iValue = iVal; +#if SQLITE_MAX_EXPR_DEPTH>0 + pNew->nHeight = 1; +#endif + } + return pNew; +} + /* ** Attach subtrees pLeft and pRight to the Expr node pRoot. ** @@ -1149,7 +1155,7 @@ Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){ ){ sqlite3ExprDeferredDelete(pParse, pLeft); sqlite3ExprDeferredDelete(pParse, pRight); - return sqlite3Expr(db, TK_INTEGER, "0"); + return sqlite3ExprInt32(db, 0); }else{ return sqlite3PExpr(pParse, TK_AND, pLeft, pRight); } @@ -2624,7 +2630,7 @@ static int exprIsConst(Parse *pParse, Expr *p, int initFlag){ /* ** Walk an expression tree. Return non-zero if the expression is constant -** and 0 if it involves variables or function calls. +** or return zero if the expression involves variables or function calls. ** ** For the purposes of this function, a double-quoted string (ex: "abc") ** is considered a variable but a single-quoted string (ex: 'abc') is @@ -3891,9 +3897,22 @@ int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ pParse->nMem += nReg; if( pExpr->op==TK_SELECT ){ dest.eDest = SRT_Mem; - dest.iSdst = dest.iSDParm; + if( (pSel->selFlags&SF_Distinct) && pSel->pLimit && pSel->pLimit->pRight ){ + /* If there is both a DISTINCT and an OFFSET clause, then allocate + ** a separate dest.iSdst array for sqlite3Select() and other + ** routines to populate. In this case results will be copied over + ** into the dest.iSDParm array only after OFFSET processing. This + ** ensures that in the case where OFFSET excludes all rows, the + ** dest.iSDParm array is not left populated with the contents of the + ** last row visited - it should be all NULLs if all rows were + ** excluded by OFFSET. */ + dest.iSdst = pParse->nMem+1; + pParse->nMem += nReg; + }else{ + dest.iSdst = dest.iSDParm; + } dest.nSdst = nReg; - sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, dest.iSDParm+nReg-1); + sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, pParse->nMem); VdbeComment((v, "Init subquery result")); }else{ dest.eDest = SRT_Exists; @@ -3909,7 +3928,7 @@ int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ || (pLeft->u.iValue!=1 && pLeft->u.iValue!=0) ){ sqlite3 *db = pParse->db; - pLimit = sqlite3Expr(db, TK_INTEGER, "0"); + pLimit = sqlite3ExprInt32(db, 0); if( pLimit ){ pLimit->affExpr = SQLITE_AFF_NUMERIC; pLimit = sqlite3PExpr(pParse, TK_NE, @@ -3920,7 +3939,7 @@ int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ } }else{ /* If there is no pre-existing limit add a limit of 1 */ - pLimit = sqlite3Expr(pParse->db, TK_INTEGER, "1"); + pLimit = sqlite3ExprInt32(pParse->db, 1); pSel->pLimit = sqlite3PExpr(pParse, TK_LIMIT, pLimit, 0); } pSel->iLimit = 0; diff --git a/src/parse.y b/src/parse.y index 90bf751bd4..a148171a95 100644 --- a/src/parse.y +++ b/src/parse.y @@ -1169,7 +1169,12 @@ expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). { term(A) ::= NULL|FLOAT|BLOB(X). {A=tokenExpr(pParse,@X,X); /*A-overwrites-X*/} term(A) ::= STRING(X). {A=tokenExpr(pParse,@X,X); /*A-overwrites-X*/} term(A) ::= INTEGER(X). { - A = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &X, 1); + int iValue; + if( sqlite3GetInt32(X.z, &iValue)==0 ){ + A = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &X, 0); + }else{ + A = sqlite3ExprInt32(pParse->db, iValue); + } if( A ) A->w.iOfst = (int)(X.z - pParse->zTail); } expr(A) ::= VARIABLE(X). { @@ -1351,43 +1356,67 @@ expr(A) ::= expr(A) likeop(OP) expr(Y) ESCAPE expr(E). [LIKE_KW] { if( A ) A->flags |= EP_InfixFunc; } -expr(A) ::= expr(A) ISNULL|NOTNULL(E). {A = sqlite3PExpr(pParse,@E,A,0);} -expr(A) ::= expr(A) NOT NULL. {A = sqlite3PExpr(pParse,TK_NOTNULL,A,0);} - %include { - /* A routine to convert a binary TK_IS or TK_ISNOT expression into a - ** unary TK_ISNULL or TK_NOTNULL expression. */ - static void binaryToUnaryIfNull(Parse *pParse, Expr *pY, Expr *pA, int op){ - sqlite3 *db = pParse->db; - if( pA && pY && pY->op==TK_NULL && !IN_RENAME_OBJECT ){ - pA->op = (u8)op; - sqlite3ExprDelete(db, pA->pRight); - pA->pRight = 0; + /* Create a TK_ISNULL or TK_NOTNULL expression, perhaps optimized to + ** to TK_TRUEFALSE, if possible */ + static Expr *sqlite3PExprIsNull( + Parse *pParse, /* Parsing context */ + int op, /* TK_ISNULL or TK_NOTNULL */ + Expr *pLeft /* Operand */ + ){ + Expr *p = pLeft; + assert( op==TK_ISNULL || op==TK_NOTNULL ); + assert( pLeft!=0 ); + while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ + p = p->pLeft; + assert( p!=0 ); } + switch( p->op ){ + case TK_INTEGER: + case TK_STRING: + case TK_FLOAT: + case TK_BLOB: + sqlite3ExprDeferredDelete(pParse, pLeft); + return sqlite3ExprInt32(pParse->db, op==TK_NOTNULL); + default: + break; + } + return sqlite3PExpr(pParse, op, pLeft, 0); + } + + /* Create a TK_IS or TK_ISNOT operator, perhaps optimized to + ** TK_ISNULL or TK_NOTNULL or TK_TRUEFALSE. */ + static Expr *sqlite3PExprIs( + Parse *pParse, /* Parsing context */ + int op, /* TK_IS or TK_ISNOT */ + Expr *pLeft, /* Left operand */ + Expr *pRight /* Right operand */ + ){ + if( pRight && pRight->op==TK_NULL ){ + sqlite3ExprDeferredDelete(pParse, pRight); + return sqlite3PExprIsNull(pParse, op==TK_IS ? TK_ISNULL : TK_NOTNULL, pLeft); + } + return sqlite3PExpr(pParse, op, pLeft, pRight); } } -// expr1 IS expr2 -// expr1 IS NOT expr2 +expr(A) ::= expr(A) ISNULL|NOTNULL(E). {A = sqlite3PExprIsNull(pParse,@E,A);} +expr(A) ::= expr(A) NOT NULL. {A = sqlite3PExprIsNull(pParse,TK_NOTNULL,A);} + +// expr1 IS expr2 same as expr1 IS NOT DISTICT FROM expr2 +// expr1 IS NOT expr2 same as expr1 IS DISTINCT FROM expr2 // -// If expr2 is NULL then code as TK_ISNULL or TK_NOTNULL. If expr2 -// is any other expression, code as TK_IS or TK_ISNOT. -// expr(A) ::= expr(A) IS expr(Y). { - A = sqlite3PExpr(pParse,TK_IS,A,Y); - binaryToUnaryIfNull(pParse, Y, A, TK_ISNULL); + A = sqlite3PExprIs(pParse, TK_IS, A, Y); } expr(A) ::= expr(A) IS NOT expr(Y). { - A = sqlite3PExpr(pParse,TK_ISNOT,A,Y); - binaryToUnaryIfNull(pParse, Y, A, TK_NOTNULL); + A = sqlite3PExprIs(pParse, TK_ISNOT, A, Y); } expr(A) ::= expr(A) IS NOT DISTINCT FROM expr(Y). { - A = sqlite3PExpr(pParse,TK_IS,A,Y); - binaryToUnaryIfNull(pParse, Y, A, TK_ISNULL); + A = sqlite3PExprIs(pParse, TK_IS, A, Y); } expr(A) ::= expr(A) IS DISTINCT FROM expr(Y). { - A = sqlite3PExpr(pParse,TK_ISNOT,A,Y); - binaryToUnaryIfNull(pParse, Y, A, TK_NOTNULL); + A = sqlite3PExprIs(pParse, TK_ISNOT, A, Y); } expr(A) ::= NOT(B) expr(X). diff --git a/src/resolve.c b/src/resolve.c index 16c193ca23..0b9000fb9b 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -1656,10 +1656,8 @@ static int resolveCompoundOrderBy( /* Convert the ORDER BY term into an integer column number iCol, ** taking care to preserve the COLLATE clause if it exists. */ if( !IN_RENAME_OBJECT ){ - Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0); + Expr *pNew = sqlite3ExprInt32(db, iCol); if( pNew==0 ) return 1; - pNew->flags |= EP_IntValue; - pNew->u.iValue = iCol; if( pItem->pExpr==pE ){ pItem->pExpr = pNew; }else{ diff --git a/src/select.c b/src/select.c index 0a00e769ba..29383bd330 100644 --- a/src/select.c +++ b/src/select.c @@ -1445,9 +1445,14 @@ static void selectInnerLoop( assert( nResultCol<=pDest->nSdst ); pushOntoSorter( pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg); + pDest->iSDParm = regResult; }else{ assert( nResultCol==pDest->nSdst ); - assert( regResult==iParm ); + if( regResult!=iParm ){ + /* This occurs in cases where the SELECT had both a DISTINCT and + ** an OFFSET clause. */ + sqlite3VdbeAddOp3(v, OP_Copy, regResult, iParm, nResultCol-1); + } /* The LIMIT clause will jump out of the loop for us */ } break; @@ -3612,10 +3617,8 @@ static int multiSelectOrderBy( if( pItem->u.x.iOrderByCol==i ) break; } if( j==nOrderBy ){ - Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0); + Expr *pNew = sqlite3ExprInt32(db, i); if( pNew==0 ) return SQLITE_NOMEM_BKPT; - pNew->flags |= EP_IntValue; - pNew->u.iValue = i; p->pOrderBy = pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew); if( pOrderBy ) pOrderBy->a[nOrderBy++].u.x.iOrderByCol = (u16)i; } @@ -7131,7 +7134,7 @@ static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){ && pExpr->pAggInfo==0 ){ sqlite3 *db = pWalker->pParse->db; - Expr *pNew = sqlite3Expr(db, TK_INTEGER, "1"); + Expr *pNew = sqlite3ExprInt32(db, 1); if( pNew ){ Expr *pWhere = pS->pWhere; SWAP(Expr, *pNew, *pExpr); diff --git a/src/shell.c.in b/src/shell.c.in index f5d81d001e..0ff7135757 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -2391,6 +2391,8 @@ static void shellFormatSchema( for(i=j=0; (c = z[i])!=0; i++){ /* Copy from z[i] back to z[j] */ if( c==cEnd ){ cEnd = 0; + }else if( cEnd!=0){ + /* No-op */ }else if( c=='"' || c=='\'' || c=='`' ){ cEnd = c; }else if( c=='[' ){ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 753271348d..185b36fd4c 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4933,6 +4933,7 @@ int sqlite3NoTempsInRange(Parse*,int,int); #endif Expr *sqlite3ExprAlloc(sqlite3*,int,const Token*,int); Expr *sqlite3Expr(sqlite3*,int,const char*); +Expr *sqlite3ExprInt32(sqlite3*,int); void sqlite3ExprAttachSubtrees(sqlite3*,Expr*,Expr*,Expr*); Expr *sqlite3PExpr(Parse*, int, Expr*, Expr*); void sqlite3PExprAddSelect(Parse*, Expr*, Select*); diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 3cedbf9c30..147a64745b 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -2361,6 +2361,10 @@ static int dbQrf(SqliteDb *pDb, int objc, Tcl_Obj *const*objv){ }else if( strcmp(zArg,"-null")==0 ){ qrf.zNull = Tcl_GetString(objv[i+1]); i++; + }else if( strcmp(zArg,"-version")==0 ){ + /* Undocumented. Testing use only */ + qrf.iVersion = atoi(Tcl_GetString(objv[i+1])); + i++; }else{ Tcl_AppendResult(pDb->interp, "unknown option: ", zArg, (char*)0); rc = TCL_ERROR; diff --git a/src/whereexpr.c b/src/whereexpr.c index 0d99ca85e6..91386de3f6 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1609,13 +1609,11 @@ static void whereAddLimitExpr( int iVal = 0; if( sqlite3ExprIsInteger(pExpr, &iVal, pParse) && iVal>=0 ){ - Expr *pVal = sqlite3Expr(db, TK_INTEGER, 0); + Expr *pVal = sqlite3ExprInt32(db, iVal); 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); + Expr *pVal = sqlite3ExprAlloc(db, TK_REGISTER, 0, 0); if( pVal==0 ) return; pVal->iTable = iReg; pNew = sqlite3PExpr(pParse, TK_MATCH, 0, pVal); diff --git a/src/window.c b/src/window.c index 1f22ab1945..ea2781864b 100644 --- a/src/window.c +++ b/src/window.c @@ -717,7 +717,7 @@ void sqlite3WindowUpdate( pWin->eEnd = aUp[i].eEnd; pWin->eExclude = 0; if( pWin->eStart==TK_FOLLOWING ){ - pWin->pStart = sqlite3Expr(db, TK_INTEGER, "1"); + pWin->pStart = sqlite3ExprInt32(db, 1); } break; } @@ -1062,9 +1062,7 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){ ** keep everything legal in this case. */ if( pSublist==0 ){ - pSublist = sqlite3ExprListAppend(pParse, 0, - sqlite3Expr(db, TK_INTEGER, "0") - ); + pSublist = sqlite3ExprListAppend(pParse, 0, sqlite3ExprInt32(db, 0)); } pSub = sqlite3SelectNew( diff --git a/test/altertab3.test b/test/altertab3.test index 92060fb41c..36e08c7694 100644 --- a/test/altertab3.test +++ b/test/altertab3.test @@ -778,4 +778,52 @@ do_execsql_test 31.2 { SELECT rr FROM t1 LIMIT 1 } {5.0} +#------------------------------------------------------------------------- +reset_db +do_execsql_test 32.1.0 { + CREATE TABLE t1( + a INT, + b INT, + -- comment with comma + c INT + ); +} +do_execsql_test 32.1.1 { + ALTER TABLE t1 DROP COLUMN c; +} +do_execsql_test 32.1.2 { + SELECT sql FROM sqlite_schema +} {{CREATE TABLE t1( + a INT, + b INT)}} + +reset_db +do_execsql_test 32.2.0 { + CREATE TABLE t1( + a INT, + b INT, + -- comment with, comma + c INT + ); +} +do_execsql_test 32.2.1 { + ALTER TABLE t1 DROP COLUMN c; +} +do_execsql_test 32.2.2 { + SELECT sql FROM sqlite_schema +} {{CREATE TABLE t1( + a INT, + b INT)}} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 33.1 { + CREATE TABLE x1(a TEXT, b INTEGER, c CHECK(c!=0)); +} + +do_execsql_test 33.2 { + ALTER TABLE x1 DROP COLUMN b; + SELECT sql FROM sqlite_schema; +} {{CREATE TABLE x1(a TEXT, c CHECK(c!=0))}} + finish_test diff --git a/test/qrf05.test b/test/qrf05.test new file mode 100644 index 0000000000..0d5a4d7f94 --- /dev/null +++ b/test/qrf05.test @@ -0,0 +1,37 @@ +# 2025-12-02 +# +# 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. +# +#*********************************************************************** +# +# Test cases for the Query Result Formatter (QRF) +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix qrf05 + +do_execsql_test 1.0 { + CREATE TABLE t1(a INT NOT NULL); +} +do_test 1.1 { + set rc [catch {db format -style list \ + {INSERT INTO t1 VALUES(123) RETURNING *}} msg] + list $rc [string trim $msg] +} {0 123} +do_test 1.2 { + set rc [catch {db format -style list \ + {INSERT INTO t1 VALUES(NULL) RETURNING *}} msg] + list $rc [string trim $msg] +} {1 {NOT NULL constraint failed: t1.a}} +do_test 1.3 { + set rc [catch {db format -version 99 {SELECT * FROM t1}} msg] + list $rc [string trim $msg] +} {1 {unusable sqlite3_qrf_spec.iVersion (99)}} + +finish_test diff --git a/test/qrf06.test b/test/qrf06.test new file mode 100644 index 0000000000..578a43f0a2 --- /dev/null +++ b/test/qrf06.test @@ -0,0 +1,576 @@ +# 2025-12-02 +# +# 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. +# +#*********************************************************************** +# +# Test cases for the Query Result Formatter (QRF), and especially +# the sqlite3_qrf_wcwidth() function and its utilization. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix qrf06 + +# Data +db eval { + BEGIN TRANSACTION; + CREATE TABLE language(name TEXT); + INSERT INTO language(name) VALUES + ('العربية'), + ('Deutsch'), + ('English'), + ('Español'), + ('فارسی'), + ('Français'), + ('Italiano'), + ('مصرى'), + ('Nederlands'), + ('日本語'), + ('Polski'), + ('Português'), + ('Sinugboanong Binisaya'), + ('Svenska'), + ('Українська'), + ('Tiếng Việt'), + ('Winaray'), + ('中文'), + ('Русский'), + ('Afrikaans'), + ('Shqip'), + ('Asturianu'), + ('Azərbaycanca'), + ('Български'), + ('閩南語 / Bân-lâm-gú'), + ('বাংলা'), + ('Беларуская'), + ('Català'), + ('Čeština'), + ('Cymraeg'), + ('Dansk'), + ('Eesti'), + ('Ελληνικά'), + ('Esperanto'), + ('Euskara'), + ('Galego'), + ('한국어'), + ('Հայերեն'), + ('हिन्दी'), + ('Hrvatski'), + ('Bahasa Indonesia'), + ('עברית'), + ('ქართული'), + ('Ladin'), + ('Latina'), + ('Latviešu'), + ('Lietuvių'), + ('Magyar'), + ('Македонски'), + ('Malagasy'), + ('मराठी'), + ('Bahasa Melayu'), + ('Bahaso Minangkabau'), + ('မြန်မာဘာသာ'), + ('Norskbokmålnynorsk'), + ('Нохчийн'), + ('Oʻzbekcha / Ўзбекча'), + ('Қазақша / Qazaqşa / قازاقشا'), + ('Română'), + ('Simple English'), + ('Slovenčina'), + ('Slovenščina'), + ('Српски / Srpski'), + ('Srpskohrvatski / Српскохрватски'), + ('Suomi'), + ('Kiswahili'), + ('தமிழ்'), + ('Татарча / Tatarça'), + ('తెలుగు'), + ('ภาษาไทย'), + ('Тоҷикӣ'), + ('تۆرکجه'), + ('Türkçe'), + ('اردو'), + ('粵語'), + ('Bahsa Acèh'), + ('Alemannisch'), + ('አማርኛ'), + ('Aragonés'), + ('Արեւմտահայերէն'), + ('Bahasa Hulontalo'), + ('Basa Bali'), + ('Bahasa Banjar'), + ('Basa Banyumasan'), + ('Башҡортса'), + ('Беларуская (тарашкевіца)'), + ('Bikol Central'), + ('বিষ্ণুপ্রিয়া মণিপুরী'), + ('Boarisch'), + ('Bosanski'), + ('Brezhoneg'), + ('Чӑвашла'), + ('Dagbanli'), + ('الدارجة'), + ('Diné Bizaad'), + ('Emigliàn–Rumagnòl'), + ('Fiji Hindi'), + ('Føroyskt'), + ('Frysk'), + ('Fulfulde'), + ('Gaeilge'), + ('Gàidhlig'), + ('گیلکی'), + ('ગુજરાતી'), + ('Hak-kâ-ngî / 客家語'), + ('Hausa'), + ('Hornjoserbsce'), + ('Ido'), + ('Igbo'), + ('Ilokano'), + ('Interlingua'), + ('Interlingue'), + ('Ирон'), + ('Íslenska'), + ('Jawa'), + ('ಕನ್ನಡ'), + ('Kapampangan'), + ('ភាសាខ្មែរ'), + ('Kotava'), + ('Kreyòl Ayisyen'), + ('Kurdî / كوردی'), + ('کوردیی ناوەندی'), + ('Кыргызча'), + ('Кырык мары'), + ('Lëtzebuergesch'), + ('Lìgure'), + ('Limburgs'), + ('Lombard'), + ('मैथिली'), + ('മലയാളം'), + ('მარგალური'), + ('مازِرونی'), + ('Mìng-dĕ̤ng-ngṳ̄ / 閩東語'), + ('Монгол'), + ('Napulitano'), + ('नेपाल भाषा'), + ('Nordfriisk'), + ('Occitan'), + ('Олык марий'), + ('ଓଡି଼ଆ'), + ('অসমীযা়'), + ('ਪੰਜਾਬੀ'), + ('پنجابی (شاہ مکھی)'), + ('پښتو'), + ('Piemontèis'), + ('Plattdüütsch'), + ('Qaraqalpaqsha'), + ('Qırımtatarca'), + ('Runa Simi'), + ('Русиньскый'), + ('संस्कृतम्'), + ('ᱥᱟᱱᱛᱟᱲᱤ'), + ('سرائیکی'), + ('Саха Тыла'), + ('Scots'), + ('ChiShona'), + ('Sicilianu'), + ('සිංහල'), + ('سنڌي'), + ('Ślůnski'), + ('Basa Sunda'), + ('Taclḥit'), + ('Tagalog'), + ('ၽႃႇသႃႇတႆး'), + ('ⵜⴰⵎⴰⵣⵉⵖⵜ ⵜⴰⵏⴰⵡⴰⵢⵜ'), + ('tolışi'), + ('chiTumbuka'), + ('Basa Ugi'), + ('Vèneto'), + ('Volapük'), + ('Walon'), + ('文言'), + ('吴语'), + ('ייִדיש'), + ('Yorùbá'), + ('Zazaki'), + ('žemaitėška'), + ('isiZulu'), + ('नेपाली'), + ('ꯃꯤꯇꯩ ꯂꯣꯟ'), + ('Dzhudezmo / לאדינו'), + ('Адыгэбзэ'), + ('Ænglisc'), + ('Anarâškielâ'), + ('अंगिका'), + ('Аԥсшәа'), + ('armãneashti'), + ('Arpitan'), + ('atikamekw'), + ('ܐܬܘܪܝܐ'), + ('Avañe’ẽ'), + ('Авар'), + ('Aymar'), + ('Batak Toba'), + ('Betawi'), + ('भोजपुरी'), + ('Bislama'), + ('བོད་ཡིག'), + ('Буряад'), + ('Chavacano de Zamboanga'), + ('Chichewa'), + ('Corsu'), + ('Vahcuengh / 話僮'), + ('Dagaare'), + ('Davvisámegiella'), + ('Deitsch'), + ('ދިވެހިބަސް'), + ('Dolnoserbski'), + ('Dusun Bundu-liwan'), + ('Эрзянь'), + ('Estremeñu'), + ('Eʋegbe'), + ('Farefare'), + ('Fɔ̀ngbè'), + ('Furlan'), + ('Gaelg'), + ('Gagauz'), + ('ГӀалгӀай'), + ('Ghanaian Pidgin'), + ('Gĩkũyũ'), + ('赣语 / 贛語'), + ('Gungbe'), + ('Хальмг'), + ('ʻŌlelo Hawaiʻi'), + ('Ikinyarwanda'), + ('Jaku Iban'), + ('Kabɩyɛ'), + ('Yerwa Kanuri'), + ('Kaszëbsczi'), + ('Kernewek'), + ('Коми'), + ('Перем коми'), + ('Kongo'), + ('कोंकणी / Konknni'), + ('كٲشُر'), + ('Kriyòl Gwiyannen'), + ('Kumoring'), + ('Kʋsaal'), + ('ພາສາລາວ'), + ('Лакку'), + ('Latgaļu'), + ('Лезги'), + ('Li Niha'), + ('Lingála'), + ('Lingua Franca Nova'), + ('livvinkarjala'), + ('lojban'), + ('Luganda'), + ('Madhurâ'), + ('Malti'), + ('Mandailing'), + ('Māori'), + ('Mfantse'), + ('Mirandés'), + ('Мокшень'), + ('ဘာသာ မန်'), + ('Moore'), + ('ߒߞߏ'), + ('Na Vosa Vaka-Viti'), + ('Nāhuatlahtōlli'), + ('Naijá'), + ('Nedersaksisch'), + ('Nouormand / Normaund'), + ('Novial'), + ('Afaan Oromoo'), + ('ပအိုဝ်ႏဘာႏသာႏ'), + ('Pangasinán'), + ('Pangcah'), + ('Papiamentu'), + ('Patois'), + ('Pfälzisch'), + ('Picard'), + ('Къарачай–малкъар'), + ('Ripoarisch'), + ('Rumantsch'), + ('Sakizaya'), + ('Gagana Sāmoa'), + ('Sardu'), + ('Seediq'), + ('Seeltersk'), + ('Sesotho'), + ('Sesotho sa Leboa'), + ('Setswana'), + ('ꠍꠤꠟꠐꠤ'), + ('Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ'), + ('Soomaaliga'), + ('Sranantongo'), + ('SiSwati'), + ('Reo tahiti'), + ('Taqbaylit'), + ('Tarandíne'), + ('Tayal'), + ('Tetun'), + ('Tok Pisin'), + ('faka Tonga'), + ('Türkmençe'), + ('Twi'), + ('Tyap'), + ('Тыва дыл'), + ('Удмурт'), + ('ئۇيغۇرچه'), + ('Vepsän'), + ('võro'), + ('West-Vlams'), + ('Wolof'), + ('isiXhosa'), + ('Zeêuws'), + ('алтай тил'), + ('अवधी'), + ('डोटेली'), + ('ತುಳು'), + ('ရခိုင်'), + ('Bajau Sama'), + ('Bamanankan'), + ('Chamoru'), + ('རྫོང་ཁ'), + ('𐌲𐌿𐍄𐌹𐍃𐌺x'), + ('Igala'), + ('ᐃᓄᒃᑎᑐᑦ / Inuktitut'), + ('Iñupiak'), + ('isiNdebele seSewula'), + ('Kalaallisut'), + ('Nupe'), + ('Obolo'), + ('पालि'), + ('pinayuanan'), + ('Ποντιακά'), + ('romani čhib'), + ('Ikirundi'), + ('руски'), + ('Sängö'), + ('ᥖᥭᥰᥖᥬᥳᥑᥨᥒᥰ'), + ('ትግርኛ'), + ('Thuɔŋjäŋ'), + ('ᏣᎳᎩ'), + ('Tsėhesenėstsestotse'), + ('Xitsonga'), + ('Tshivenḓa'), + ('Wayuunaiki'), + ('адыгабзэ'); + COMMIT; +} + +do_test 1.2 { + set res \n[db format -style box { + SELECT name, rowid AS id FROM language + WHERE length(name)=2 + ORDER BY name + }] + set exp { +┌──────┬─────┐ +│ name │ id │ +├──────┼─────┤ +│ 中文 │ 18 │ +│ 吴语 │ 173 │ +│ 文言 │ 172 │ +│ 粵語 │ 75 │ +└──────┴─────┘ +} + if {$res ne $exp} { + puts [list $res] + puts [list $exp] + } + string compare $res $exp +} {0} + +do_test 1.3 { + set res \n[db format -style box { + SELECT name, rowid AS id FROM language + WHERE length(name)=3 + ORDER BY name + }] + set exp { +┌────────┬─────┐ +│ name │ id │ +├────────┼─────┤ +│ Ido │ 108 │ +│ Twi │ 297 │ +│ ߒߞߏ │ 258 │ +│ ᏣᎳᎩ │ 335 │ +│ 日本語 │ 10 │ +│ 한국어 │ 37 │ +└────────┴─────┘ +} + if {$res ne $exp} { + puts [list $res] + puts [list $exp] + } + string compare $res $exp +} {0} + +do_test 1.4 { + set res \n[db format -style box { + SELECT name, rowid AS id FROM language + WHERE length(name)=4 + ORDER BY name + }] + set exp { +┌──────┬─────┐ +│ name │ id │ +├──────┼─────┤ +│ Igbo │ 109 │ +│ Jawa │ 115 │ +│ Nupe │ 323 │ +│ Tyap │ 298 │ +│ võro │ 303 │ +│ Авар │ 192 │ +│ Ирон │ 113 │ +│ Коми │ 231 │ +│ اردو │ 74 │ +│ سنڌي │ 159 │ +│ مصرى │ 8 │ +│ پښتو │ 144 │ +│ अवधी │ 309 │ +│ पालि │ 325 │ +│ ತುಳು │ 311 │ +│ ትግርኛ │ 333 │ +│ አማርኛ │ 78 │ +└──────┴─────┘ +} + if {$res ne $exp} { + puts [list $res] + puts [list $exp] + } + string compare $res $exp +} {0} + +do_test 1.5 { + set res \n[db format -style box { + SELECT name, rowid AS id FROM language + WHERE length(name)=5 + ORDER BY name + }] + set exp { +┌───────┬─────┐ +│ name │ id │ +├───────┼─────┤ +│ Aymar │ 193 │ +│ Corsu │ 202 │ +│ Dansk │ 31 │ +│ Eesti │ 32 │ +│ Frysk │ 99 │ +│ Gaelg │ 216 │ +│ Hausa │ 106 │ +│ Igala │ 318 │ +│ Kongo │ 233 │ +│ Ladin │ 44 │ +│ Malti │ 250 │ +│ Moore │ 257 │ +│ Māori │ 252 │ +│ Naijá │ 261 │ +│ Obolo │ 324 │ +│ Sardu │ 278 │ +│ Scots │ 155 │ +│ Shqip │ 21 │ +│ Suomi │ 65 │ +│ Sängö │ 331 │ +│ Tayal │ 292 │ +│ Tetun │ 293 │ +│ Walon │ 171 │ +│ Wolof │ 305 │ +│ Лакку │ 240 │ +│ Лезги │ 242 │ +│ руски │ 330 │ +│ עברית │ 42 │ +│ فارسی │ 5 │ +│ كٲشُر │ 235 │ +│ گیلکی │ 103 │ +│ मराठी │ 51 │ +│ বাংলা │ 26 │ +│ ଓଡି଼ଆ │ 140 │ +│ தமிழ் │ 67 │ +│ ಕನ್ನಡ │ 116 │ +│ සිංහල │ 158 │ +│ ꠍꠤꠟꠐꠤ │ 284 │ +└───────┴─────┘ +} + if {$res ne $exp} { + puts [list $res] + puts [list $exp] + } + string compare $res $exp +} {0} + +do_test 1.6 { + set res \n[db format -style box { + SELECT name, rowid AS id FROM language + WHERE length(name)=6 + ORDER BY name + }] + set exp { +┌────────┬─────┐ +│ name │ id │ +├────────┼─────┤ +│ Betawi │ 195 │ +│ Català │ 28 │ +│ Eʋegbe │ 212 │ +│ Furlan │ 215 │ +│ Gagauz │ 217 │ +│ Galego │ 36 │ +│ Gungbe │ 222 │ +│ Gĩkũyũ │ 220 │ +│ Kabɩyɛ │ 227 │ +│ Kotava │ 119 │ +│ Kʋsaal │ 238 │ +│ Latina │ 45 │ +│ Lìgure │ 126 │ +│ Magyar │ 48 │ +│ Novial │ 264 │ +│ Patois │ 270 │ +│ Picard │ 272 │ +│ Polski │ 11 │ +│ Română │ 59 │ +│ Seediq │ 279 │ +│ Türkçe │ 73 │ +│ Vepsän │ 302 │ +│ Vèneto │ 169 │ +│ Yorùbá │ 175 │ +│ Zazaki │ 176 │ +│ Zeêuws │ 307 │ +│ lojban │ 247 │ +│ tolışi │ 166 │ +│ Аԥсшәа │ 186 │ +│ Буряад │ 199 │ +│ Монгол │ 134 │ +│ Тоҷикӣ │ 71 │ +│ Удмурт │ 300 │ +│ Хальмг │ 223 │ +│ Эрзянь │ 210 │ +│ ייִדיש │ 174 │ +│ تۆرکجه │ 72 │ +│ ܐܬܘܪܝܐ │ 190 │ +│ अंगिका │ 185 │ +│ डोटेली │ 310 │ +│ नेपाली │ 179 │ +│ मैथिली │ 129 │ +│ हिन्दी │ 39 │ +│ ਪੰਜਾਬੀ │ 142 │ +│ తెలుగు │ 69 │ +│ മലയാളം │ 130 │ +│ རྫོང་ཁ │ 316 │ +│ ရခိုင် │ 312 │ +└────────┴─────┘ +} + if {$res ne $exp} { + puts [list $res] + puts [list $exp] + } + string compare $res $exp +} {0} + +finish_test diff --git a/test/shell1.test b/test/shell1.test index 9c1abf9916..01896044e9 100644 --- a/test/shell1.test +++ b/test/shell1.test @@ -615,6 +615,20 @@ CREATE VIEW v1 AS SELECT y+1 FROM v2 catch {db eval {DROP VIEW v1; DROP VIEW v2; DROP TABLE t1;}} } +do_test shell1-3.21.5 { + exec {*}$CLI -noinit test.db \ + {CREATE TABLE t2(a INTEGER PRIMARY KEY, b BLOB DEFAULT(jsonb('[]')),c TEXT NOT NULL)STRICT;} \ + {.schema -indent t2} +} {CREATE TABLE t2( + a INTEGER PRIMARY KEY, + b BLOB DEFAULT(jsonb('[]')), + c TEXT NOT NULL +)STRICT;} +do_test shell1-3.21.6 { + exec {*}$CLI -noinit test.db \ + {DROP TABLE t2;} \ + {.schema -indent t2} +} {} # .separator STRING Change column separator used by output and .import do_test shell1-3.22.1 { diff --git a/test/subquery2.test b/test/subquery2.test index 8513dc75c8..99a1e903f8 100644 --- a/test/subquery2.test +++ b/test/subquery2.test @@ -215,4 +215,72 @@ do_execsql_test 5.1 { SELECT ( SELECT y FROM t2 WHERE x = y ORDER BY y, z) FROM t1; } {ALFKI ANATR} +#------------------------------------------------------------------------- +reset_db +do_execsql_test 6.0 { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(1234); +} + +do_execsql_test 6.1 { + SELECT DISTINCT 'string' FROM t1 LIMIT 1 OFFSET 5; +} + +do_execsql_test 6.2 { + SELECT ( + SELECT 'string' FROM t1 LIMIT 1 OFFSET 5 + ); +} {{}} + +do_execsql_test 6.3 { + SELECT ( + SELECT DISTINCT 'string' FROM t1 LIMIT 1 OFFSET 5 + ); +} {{}} + +do_execsql_test 6.4 { + SELECT ( + SELECT DISTINCT 'string' FROM t1 ORDER BY 1 LIMIT 1 OFFSET 5 + ); +} {{}} + +do_execsql_test 6.5 { + SELECT ( + SELECT 'string' FROM t1 ORDER BY 1 LIMIT 1 OFFSET 5 + ); +} {{}} + +do_execsql_test 6.6 { + SELECT (SELECT DISTINCT x, x FROM t1 LIMIT 1 OFFSET 5)==(1234, 1234) +} {{}} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 7.0 { + CREATE TABLE t1(x); + CREATE INDEX i1 ON t1(x); + INSERT INTO t1 VALUES(1234); +} + +do_execsql_test 7.1 { + SELECT ( + SELECT DISTINCT 'string' FROM t1 ORDER BY x LIMIT 1 OFFSET 5 + ); +} {{}} + +do_execsql_test 7.2 { + DROP INDEX i1; + CREATE UNIQUE INDEX i1 ON t1(x); +} + +do_execsql_test 7.3 { + SELECT ( + SELECT DISTINCT x FROM t1 ORDER BY 1 LIMIT 1 OFFSET 5 + ); +} {{}} + +do_execsql_test 7.4 { + SELECT (SELECT DISTINCT x FROM t1 ORDER BY +x LIMIT 1 OFFSET 0); +} {1234} + finish_test