1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-07 02:42:48 +03:00

Add support for "ORDER BY ... NULLS FIRST" and "ORDER BY ... NULLS LAST". Use this to fix ticket [f8a7060e].

FossilOrigin-Name: 94085fb3e756bc984237b74b6e29c68462ad860870c64dcb5124feaeec387660
This commit is contained in:
dan
2019-08-27 19:59:21 +00:00
24 changed files with 2129 additions and 245 deletions

View File

@@ -134,6 +134,7 @@ do_setup_rec_test $tn.5 {
SEARCH TABLE t1 USING COVERING INDEX t1_idx_000123a7 (a=?)
}
if 0 {
do_setup_rec_test $tn.6 {
CREATE TABLE t1(a, b, c);
} {
@@ -142,6 +143,7 @@ do_setup_rec_test $tn.6 {
CREATE INDEX t1_idx_00000061 ON t1(a);
SEARCH TABLE t1 USING COVERING INDEX t1_idx_00000061
}
}
do_setup_rec_test $tn.7 {
CREATE TABLE t1(a, b, c);

View File

@@ -1,5 +1,5 @@
C The\sALWAYS()\sadded\sby\sthe\sprevious\scheck-in\swas\sincorrect.\s\sTake\sit\sback\sout.
D 2019-08-27T17:59:01.529
C Add\ssupport\sfor\s"ORDER\sBY\s...\sNULLS\sFIRST"\sand\s"ORDER\sBY\s...\sNULLS\sLAST".\sUse\sthis\sto\sfix\sticket\s[f8a7060e].
D 2019-08-27T19:59:21.714
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -47,7 +47,7 @@ F ext/async/sqlite3async.c 0f3070cc3f5ede78f2b9361fb3b629ce200d7d74
F ext/async/sqlite3async.h f489b080af7e72aec0e1ee6f1d98ab6cf2e4dcef
F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3
F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4
F ext/expert/expert1.test 358e416877a5693fb99d5514f5d88452b5239dc2196b74e0e926718502faef6d
F ext/expert/expert1.test e2afc53a27610e8251e44c7f961806607a5490ff204b3db342740d558e052662
F ext/expert/sqlite3expert.c 3da865f2286433588260f41e796422c611bceaca3a0bbf9139a619cf7d062c19
F ext/expert/sqlite3expert.h af6354f8ee5c9e025024e63fec3bd640a802afcc3099a44d804752cf0791d811
F ext/expert/test_expert.c d56c194b769bdc90cf829a14c9ecbc1edca9c850b837a4d0b13be14095c32a72
@@ -467,7 +467,7 @@ F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
F src/btree.c fdc4389b271bca30138db27dc2dfb9f52c2a7baaa44845aaf31a3c54663d837f
F src/btree.h c11446f07ec0e9dc85af8041cb0855c52f5359c8b2a43e47e02a685282504d89
F src/btreeInt.h 6111c15868b90669f79081039d19e7ea8674013f907710baa3c814dc3f8bfd3f
F src/build.c 7fb6ad35d162517d6bfa196f4fb2a1d7c3a362531e84c59f3a0479e0de511556
F src/build.c da5d5d82eb53cb004e9120277cfe93a9c3dd294871eae3d728ebd0faee84d969
F src/callback.c 25dda5e1c2334a367b94a64077b1d06b2553369f616261ca6783c48bcb6bda73
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c 1b0724e66f95f33b160b1af85caaf9cceb325d22abf39bd24df4f54a73982251
@@ -475,7 +475,7 @@ F src/date.c e1d8ac7102f3f283e63e13867acb0efa33861cf34f0faf4cdbaf9fa7a1eb7041
F src/dbpage.c 135eb3b5e74f9ef74bde5cec2571192c90c86984fa534c88bf4a055076fa19b7
F src/dbstat.c c12833de69cb655751487d2c5a59607e36be1c58ba1f4bd536609909ad47b319
F src/delete.c d08c9e01a2664afd12edcfa3a9c6578517e8ff8735f35509582693adbe0edeaf
F src/expr.c 18b6d8b5fea8151fae6d67fb12f2fff1f3abe8d5fe70365ecb3671a0aaf51c27
F src/expr.c 0f3aaaac5c1bd6a34fece3308ad572ef846c1601c58957902dc2a36f75373364
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 6b79f4c2447691aa9ac86e2a6a774b65f3b3dd053d4220a4893051a0de20f82e
F src/func.c 4ee36219698d50d672a28eca4adb0fd6b92e607a1883d318315e0d2fd5044467
@@ -484,7 +484,7 @@ F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38
F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c c2194dd2886337b870226fcb31c13e7df8c4b5e0ea85140e510a6f1daf1ad65b
F src/insert.c 81eec6acf4fbf0942bbab6804fe50df3e109acba40b8bbfb00fec9a14d0715a6
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c a045bb3425a9a633cc0f78e93d9beda6866f4c0f15bfdee735aba7c6b39f5cc4
F src/main.c 51c55eb579eac4180bfcc6242741084710911350d2cd0c3fdd0f9fde55442128
@@ -512,7 +512,7 @@ F src/os_win.c 035a813cbd17f355bdcad7ab894af214a9c13a1db8aeac902365350b98cd45a7
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 422fd8cfa59fb9173eff36a95878904a0eeb0dcc62ba49350acc8b1e51c4dc7b
F src/pager.h 217921e81eb5fe455caa5cda96061959706bcdd29ddb57166198645ef7822ac3
F src/parse.y 152a72755398be8f36e097bbab9fd3eeebc638b31ed1ec134e49284f9d7f013a
F src/parse.y 6f284f7488ad9db9ae2762161e52bec2e9609462b98a3354fcc6e0ce7aa7be8d
F src/pcache.c 385ff064bca69789d199a98e2169445dc16e4291fa807babd61d4890c3b34177
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
F src/pcache1.c 62714cbd1b7299a6e6a27a587b66b4fd3a836a84e1181e7f96f5c34a50917848
@@ -523,12 +523,12 @@ F src/printf.c 9be6945837c839ba57837b4bc3af349eba630920fa5532aa518816defe42a7d4
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c 9891cf5fd155bb199f8b1ff5d1429b9f70484487f4c455bba94348d4cb6f829f
F src/rowset.c d977b011993aaea002cab3e0bb2ce50cf346000dff94e944d547b989f4b1fe93
F src/select.c c1dfbd699a6dce14c2e6f30370a57753294d6b725cc75551b26146e0c1d25076
F src/select.c e4fe08c3da81a38061454fb7de2c1b31c36ed76cd1a4bbefd2b5fc4ebb472a5b
F src/shell.c.in e5fb91505f29ae9458cabf1a63bbd1faf6b4b34eabca33d0f75a06aacecca21b
F src/sqlite.h.in 50fc0914ccd347437db9a0278a47d7541df3a45eb6e641e9680750c6f98dad27
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h cef696ce3293242c67b2339763608427bf72ee66f1f3a05389ac2a7b46001c31
F src/sqliteInt.h ce52cf59718f4affee3057381f5cf7acefe42d2ab6b43c6da93f4b1f6a3c704b
F src/sqliteInt.h b1fca535f01f02ae6927dd44e760c6ab54c7a181e88f813d16b55a1bc95d13c0
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
@@ -589,31 +589,31 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c d3615f0cbe4db5949503bf5916f3cd4fa5de855d5b4ef560f3b6dd5629423a1e
F src/treeview.c fc8c6c0a8a26afb3a97e3f844d65403dd27cf1450baf4415034fa4ccf00c4d7e
F src/trigger.c f964d85935d0b9675b4aa2f74999cabee3ac1ac705f71b38a8b7e8ccd4ad474b
F src/trigger.c 845ccc08f60716c58aa28fe6470385c18ef8c4e1d88c93dcf449bc13d464eb2e
F src/update.c 3cb9150d2cf661d938e2f1b1749945f3faa767f88febdb739ab1793bbf895ff2
F src/upsert.c 0dd81b40206841814d46942a7337786932475f085716042d0cb2fc7791bf8ca4
F src/utf.c 2f0fac345c7660d5c5bd3df9e9d8d33d4c27f366bcfb09e07443064d751a0507
F src/util.c fffdfa627be74d69ef425f92db124e7148af449bb8a3286e79577c42bca84061
F src/vacuum.c 82dcec9e7b1afa980288718ad11bc499651c722d7b9f32933c4d694d91cb6ebf
F src/vdbe.c cdd90f4c4773a08504c66815faa505570257619a0bafcc9b486a24844be51357
F src/vdbe.c fca0f3762bc5cb7698a0bf754886503e703f66bf28013479c2177345053cab17
F src/vdbe.h 3f2b571e702e77e6bf031f0236e554aedfae643e991f69000320f481408455cf
F src/vdbeInt.h e95de5129124d77f01439e6635012adfaf23c0017bff47296126143cf18bd0c6
F src/vdbeapi.c 95001d0f84ee3cda344fed98ca0d7961deb4fc836b83495630d0af1f7cc4789e
F src/vdbeaux.c 8eeb9799d80bc6b37f2bcb23b3519234b596c530046c2cd0261f9ef1a1ba6c37
F src/vdbeaux.c 7ccf418141df1c7f87b0d69510523ae522abbe47c769d1b2c15120e88fac3eb9
F src/vdbeblob.c 253ed82894924c362a7fa3079551d3554cd1cdace39aa833da77d3bc67e7c1b1
F src/vdbemem.c 920285c3b7f5c64369e02da437dab71e9e91862df9c486541c14633739f91d75
F src/vdbesort.c 3531ae3a431ad6b98b67bd891fb42ec9d66867157188a2b1a9e58c55da6151b2
F src/vdbesort.c da75f505aba230060ce6472605a4aa6494f73eeb1071e1cc2643c3d4035e671b
F src/vdbetrace.c fa3bf238002f0bbbdfb66cc8afb0cea284ff9f148d6439bc1f6f2b4c3b7143f0
F src/vtab.c 5a0b7193d586991b3db30e343d6b59959906bfe8658a6a0a85709b20ca50bb49
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c bbd6838bd79c0a32144d482fb0b6a9d2d1a252fb3b16d5005ec30f2f80413b0d
F src/wal.h 606292549f5a7be50b6227bd685fa76e3a4affad71bb8ac5ce4cb5c79f6a176a
F src/walker.c d5a94907dcac990e31976be9dc769d17f6a806782593d6aec9d760ee01ec22cd
F src/where.c 6cc2708ab9a386c0aef3fa50ed0f5bdfed22848e492960a4504eb25bdb79c8d1
F src/whereInt.h 2082fc2bd1eb66cb236a1a3c4b250e33d2bad9e43a0486a2cf9e4e211c58f3eb
F src/wherecode.c e1131fe94c8728cbecc707f6455afbda9418896497bdca2d49a04ce6c57999f6
F src/where.c 6f8bd0b4ade152571e960504a3042a31fd80e8adcef834db0717af3fbac4acb5
F src/whereInt.h 4a296fd4fa79fdcbc2b5e8c1b898901617655811223e1082b899c23ecb092217
F src/wherecode.c 535c8e228478fd971b9a5b6cb6773995b0fbf7020d5989508a5094ce5b8cd95b
F src/whereexpr.c 2757afbd5cfdbb420f9d0392e1bd5f5c0e33dee50a8c692befc7e502308e449f
F src/window.c 07e1c15081a735750218185c6b17053c87ecb764d06ab2c0a1ce568a2b4688e5
F src/window.c 3ea716bb0dd5ffc8cdbaa48baffc525958b51bc61b2afd73a56baccfcd7ceb2f
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test b03930d288e38b07f55023a58538ad174605695e98934bdab1facf6bd9ecc436
F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
@@ -1150,7 +1150,7 @@ F test/memsubsys2.test 3e4a8d0c05fd3e5fa92017c64666730a520c7e08
F test/minmax.test 6751e87b409fe11b02e70a306d846fa544e25a41
F test/minmax2.test dae92964ac87c1d2ef978c582e81a95e11c00f1cbef68980bfb2abaf10315063
F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354
F test/minmax4.test 838fe32b812dc50778be3799767cefb5ff59bb04cff81d4f12c0708642f65151
F test/minmax4.test 272ca395257f05937dc96441c9dde4bc9fbf116a8d4fa02baeb0d13d50e36c87
F test/misc1.test 7ce84b25df9872e7d7878613a96815d2ba5bc974ac4e15a50118dde8f3917599
F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d
F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d
@@ -1182,6 +1182,7 @@ F test/notify2.test 2ecabaa1305083856b7c39cf32816b612740c161
F test/notify3.test 10ff25cde502e72a92053a2f215d64bece4ef934
F test/notnull.test a37b663d5bb728d66fc182016613fb8e4a0a4bbf3d75b8876a7527f7d4ed3f18
F test/null.test 0dcce4f04284ec66108c503327ad6d224c0752b3
F test/nulls1.test 5b978fbae9c73b497608c16f2636a2f71de6e2c914e4c78955cf022e39b7a0d5
F test/numcast.test 5d126f7f581432e86a90d1e35cac625164aec4a1
F test/numindex1.test 20a5450d4b056e48cd5db30e659f13347a099823
F test/offset1.test f06b83657bcf26f9ce805e67450e189e282143b2
@@ -1216,7 +1217,7 @@ F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
F test/permutations.test 4282fb00e5ac0f8c2cd1be62652f6da4ac03ce3c58b7d785fa17f4684492a0e0
F test/pg_common.tcl 4740dc35190d6acdab14c097783331361301ab504a94d948f6afbb56ce0a51e8
F test/pg_common.tcl 222a1bad1c41c308fa366313cd7b51b3be7e9b21c8736a421b974ac941693b54
F test/pragma.test cf066fe0f7f5d49f4758de4986407b8676c61aaa7871599340d64f42a8edc352
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
F test/pragma3.test 8300aa9c63cff1027006ca34bf413a148abbd6dcd471fa9a1ded322fe18c0df9
@@ -1709,9 +1710,9 @@ F test/window4.test 807f3e6b15f9338e5b9742b87c5c7ca825b42b9657fde6096e8901193708
F test/window5.test d328dd18221217c49c144181975eea17339eaeaf0e9aa558cee3afb84652821e
F test/window6.test f8d674254b23289cc17c84d79dec7eda7caa1dfb7836c43122cfdf3640d1df32
F test/window7.tcl 6a1210f05d40ec89c22960213a22cd3f98d4e2f2eb20646c83c8c30d4d76108f
F test/window7.test ce7f865241fdd1c5c4db869cd7bb2986c3be836bc2e73649a6846dd920f63e0f
F test/window8.tcl 9e9a82ae9eea90a4a83481d641a812b974980c38f9247f3b89a6e3c8bed45518
F test/window8.test df187dc19921f7be0ab709d531d681bd80ccaac96a913a89ecee8b272b91d43f
F test/window7.test 1d31276961ae7801edc72173edaf7593e3cbc79c06d1f1f09e20d8418af403cd
F test/window8.tcl 5884cc884f9605bf88c0d18a534894bf9342f72687bf1bc43ed0cab4c8af7973
F test/window8.test 48590f3737d17eec503d77769c13ead15d12e8819820b1dc68afe8a3c5bc3250
F test/window9.test 20a6b590be718b6bc98a5356d4396d6cdf19329c547da084fa225b92d68e1693
F test/windowerr.tcl f5acd6fbc210d7b5546c0e879d157888455cd4a17a1d3f28f07c1c8a387019e0
F test/windowerr.test a8b752402109c15aa1c5efe1b93ccb0ce1ef84fa964ae1cd6684dd0b3cc1819b
@@ -1764,7 +1765,7 @@ F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
F tool/mkautoconfamal.sh 422fc365358a2e92876ffc62971a0ff28ed472fc8bcf9de0df921c736fdeca5e
F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
F tool/mkctimec.tcl dd183b73ae1c28249669741c250525f0407e579a70482371668fd5f130d9feb3
F tool/mkkeywordhash.c 537b1a11ec1829b51b633da3ba2cc889b4a3f7356b06a07423b6d4cce92c2350
F tool/mkkeywordhash.c bc5bcc92ebcaf15345346be7cf2204b83ed649b5208234adb5e543c061209bbf
F tool/mkmsvcmin.tcl cad0c7b54d7dd92bc87d59f36d4cc4f070eb2e625f14159dc2f5c4204e6a13ea
F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
F tool/mkopcodeh.tcl 352a4319c0ad869eb26442bf7c3b015aa15594c21f1cce5a6420dbe999367c21
@@ -1836,7 +1837,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 aff209804722ac902c7abfde80ad2677e0f51beb2c7f28f65d51105d984a1640
R ab1237ad66e99f2406a94be2d43ffa7b
U drh
Z 919069f1dca2ee3915ff3c6a1bd5e156
P 336235db2b1167cdb2feb64b47eb6368c97c43ee9641b7bfccc5775a41dd0d0e ad816d01d4bd3908ff2c574d79d1a29b6d732df308f2f1b1cdf7c0bc7c1bd7cf
R 5139358becb963924affc3638fcd0d5e
T +closed ad816d01d4bd3908ff2c574d79d1a29b6d732df308f2f1b1cdf7c0bc7c1bd7cf
U dan
Z 534b1b1d3945585b80108a8c2d91a394

View File

@@ -1 +1 @@
336235db2b1167cdb2feb64b47eb6368c97c43ee9641b7bfccc5775a41dd0d0e
94085fb3e756bc984237b74b6e29c68462ad860870c64dcb5124feaeec387660

View File

@@ -1443,7 +1443,7 @@ void sqlite3AddPrimaryKey(
pTab->keyConf = (u8)onError;
assert( autoInc==0 || autoInc==1 );
pTab->tabFlags |= autoInc*TF_Autoincrement;
if( pList ) pParse->iPkSortOrder = pList->a[0].sortOrder;
if( pList ) pParse->iPkSortOrder = pList->a[0].sortFlags;
}else if( autoInc ){
#ifndef SQLITE_OMIT_AUTOINCREMENT
sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an "
@@ -1894,7 +1894,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
if( IN_RENAME_OBJECT ){
sqlite3RenameTokenRemap(pParse, pList->a[0].pExpr, &pTab->iPKey);
}
pList->a[0].sortOrder = pParse->iPkSortOrder;
pList->a[0].sortFlags = pParse->iPkSortOrder;
assert( pParse->pNewTable==pTab );
pTab->iPKey = -1;
sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0,
@@ -3153,6 +3153,27 @@ Index *sqlite3AllocateIndexObject(
return p;
}
/*
** If expression list pList contains an expression that was parsed with
** an explicit "NULLS FIRST" or "NULLS LAST" clause, leave an error in
** pParse and return non-zero. Otherwise, return zero.
*/
int sqlite3HasExplicitNulls(Parse *pParse, ExprList *pList){
if( pList ){
int i;
for(i=0; i<pList->nExpr; i++){
if( pList->a[i].bNulls ){
u8 sf = pList->a[i].sortFlags;
sqlite3ErrorMsg(pParse, "unsupported use of NULLS %s",
(sf==0 || sf==3) ? "FIRST" : "LAST"
);
return 1;
}
}
}
return 0;
}
/*
** Create a new index for an SQL table. pName1.pName2 is the name of the index
** and pTblList is the name of the table that is to be indexed. Both will
@@ -3204,6 +3225,9 @@ void sqlite3CreateIndex(
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
goto exit_create_index;
}
if( sqlite3HasExplicitNulls(pParse, pList) ){
goto exit_create_index;
}
/*
** Find the table that is to be indexed. Return early if not found.
@@ -3368,7 +3392,7 @@ void sqlite3CreateIndex(
sqlite3ExprAlloc(db, TK_ID, &prevCol, 0));
if( pList==0 ) goto exit_create_index;
assert( pList->nExpr==1 );
sqlite3ExprListSetSortOrder(pList, sortOrder);
sqlite3ExprListSetSortOrder(pList, sortOrder, SQLITE_SO_UNDEFINED);
}else{
sqlite3ExprListCheckLength(pParse, pList, "index");
if( pParse->nErr ) goto exit_create_index;
@@ -3486,7 +3510,7 @@ void sqlite3CreateIndex(
goto exit_create_index;
}
pIndex->azColl[i] = zColl;
requestedSortOrder = pListItem->sortOrder & sortOrderMask;
requestedSortOrder = pListItem->sortFlags & sortOrderMask;
pIndex->aSortOrder[i] = (u8)requestedSortOrder;
}
@@ -4704,7 +4728,8 @@ KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){
const char *zColl = pIdx->azColl[i];
pKey->aColl[i] = zColl==sqlite3StrBINARY ? 0 :
sqlite3LocateCollSeq(pParse, zColl);
pKey->aSortOrder[i] = pIdx->aSortOrder[i];
pKey->aSortFlags[i] = pIdx->aSortOrder[i];
assert( 0==(pKey->aSortFlags[i] & KEYINFO_ORDER_BIGNULL) );
}
if( pParse->nErr ){
assert( pParse->rc==SQLITE_ERROR_MISSING_COLLSEQ );

View File

@@ -1409,8 +1409,9 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
}
pItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
pItem->zSpan = sqlite3DbStrDup(db, pOldItem->zSpan);
pItem->sortOrder = pOldItem->sortOrder;
pItem->sortFlags = pOldItem->sortFlags;
pItem->done = 0;
pItem->bNulls = pOldItem->bNulls;
pItem->bSpanIsTab = pOldItem->bSpanIsTab;
pItem->bSorterRef = pOldItem->bSorterRef;
pItem->u = pOldItem->u;
@@ -1666,15 +1667,34 @@ vector_append_error:
/*
** Set the sort order for the last element on the given ExprList.
*/
void sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder){
void sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder, int eNulls){
struct ExprList_item *pItem;
if( p==0 ) return;
assert( SQLITE_SO_UNDEFINED<0 && SQLITE_SO_ASC>=0 && SQLITE_SO_DESC>0 );
assert( p->nExpr>0 );
if( iSortOrder<0 ){
assert( p->a[p->nExpr-1].sortOrder==SQLITE_SO_ASC );
return;
assert( SQLITE_SO_UNDEFINED<0 && SQLITE_SO_ASC==0 && SQLITE_SO_DESC>0 );
assert( iSortOrder==SQLITE_SO_UNDEFINED
|| iSortOrder==SQLITE_SO_ASC
|| iSortOrder==SQLITE_SO_DESC
);
assert( eNulls==SQLITE_SO_UNDEFINED
|| eNulls==SQLITE_SO_ASC
|| eNulls==SQLITE_SO_DESC
);
pItem = &p->a[p->nExpr-1];
assert( pItem->bNulls==0 );
if( iSortOrder==SQLITE_SO_UNDEFINED ){
iSortOrder = SQLITE_SO_ASC;
}
pItem->sortFlags = (u8)iSortOrder;
if( eNulls!=SQLITE_SO_UNDEFINED ){
pItem->bNulls = 1;
if( iSortOrder!=eNulls ){
pItem->sortFlags |= KEYINFO_ORDER_BIGNULL;
}
}
p->a[p->nExpr-1].sortOrder = (u8)iSortOrder;
}
/*
@@ -4935,7 +4955,7 @@ int sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){
for(i=0; i<pA->nExpr; i++){
Expr *pExprA = pA->a[i].pExpr;
Expr *pExprB = pB->a[i].pExpr;
if( pA->a[i].sortOrder!=pB->a[i].sortOrder ) return 1;
if( pA->a[i].sortFlags!=pB->a[i].sortFlags ) return 1;
if( sqlite3ExprCompare(0, pExprA, pExprB, iTab) ) return 1;
}
return 0;

View File

@@ -833,6 +833,9 @@ void sqlite3Insert(
pTab->zName);
goto insert_cleanup;
}
if( sqlite3HasExplicitNulls(pParse, pUpsert->pUpsertTarget) ){
goto insert_cleanup;
}
pTabList->a[0].iCursor = iDataCur;
pUpsert->pUpsertSrc = pTabList;
pUpsert->regData = regData;

View File

@@ -211,6 +211,7 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH NO PLAN
QUERY KEY OF OFFSET PRAGMA RAISE RECURSIVE RELEASE REPLACE RESTRICT ROW ROWS
ROLLBACK SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL WITH WITHOUT
NULLS FIRST LAST
%ifdef SQLITE_OMIT_COMPOUND_SELECT
EXCEPT INTERSECT UNION
%endif SQLITE_OMIT_COMPOUND_SELECT
@@ -781,13 +782,13 @@ using_opt(U) ::= . {U = 0;}
orderby_opt(A) ::= . {A = 0;}
orderby_opt(A) ::= ORDER BY sortlist(X). {A = X;}
sortlist(A) ::= sortlist(A) COMMA expr(Y) sortorder(Z). {
sortlist(A) ::= sortlist(A) COMMA expr(Y) sortorder(Z) nulls(X). {
A = sqlite3ExprListAppend(pParse,A,Y);
sqlite3ExprListSetSortOrder(A,Z);
sqlite3ExprListSetSortOrder(A,Z,X);
}
sortlist(A) ::= expr(Y) sortorder(Z). {
sortlist(A) ::= expr(Y) sortorder(Z) nulls(X). {
A = sqlite3ExprListAppend(pParse,0,Y); /*A-overwrites-Y*/
sqlite3ExprListSetSortOrder(A,Z);
sqlite3ExprListSetSortOrder(A,Z,X);
}
%type sortorder {int}
@@ -796,6 +797,11 @@ sortorder(A) ::= ASC. {A = SQLITE_SO_ASC;}
sortorder(A) ::= DESC. {A = SQLITE_SO_DESC;}
sortorder(A) ::= . {A = SQLITE_SO_UNDEFINED;}
%type nulls {int}
nulls(A) ::= NULLS FIRST. {A = SQLITE_SO_ASC;}
nulls(A) ::= NULLS LAST. {A = SQLITE_SO_DESC;}
nulls(A) ::= . {A = SQLITE_SO_UNDEFINED;}
%type groupby_opt {ExprList*}
%destructor groupby_opt {sqlite3ExprListDelete(pParse->db, $$);}
groupby_opt(A) ::= . {A = 0;}
@@ -1740,12 +1746,12 @@ filter_clause(A) ::= FILTER LP WHERE expr(X) RP. { A = X; }
** are synthesized and do not actually appear in the grammar:
*/
%token
TRUEFALSE /* True or false keyword */
ISNOT /* Combination of IS and NOT */
FUNCTION /* A function invocation */
COLUMN /* Reference to a table column */
AGG_FUNCTION /* An aggregate function */
AGG_COLUMN /* An aggregated column */
TRUEFALSE /* True or false keyword */
ISNOT /* Combination of IS and NOT */
FUNCTION /* A function invocation */
UMINUS /* Unary minus */
UPLUS /* Unary plus */
TRUTH /* IS TRUE or IS FALSE or IS NOT TRUE or IS NOT FALSE */

View File

@@ -664,7 +664,7 @@ static void pushOntoSorter(
if( pParse->db->mallocFailed ) return;
pOp->p2 = nKey + nData;
pKI = pOp->p4.pKeyInfo;
memset(pKI->aSortOrder, 0, pKI->nKeyField); /* Makes OP_Jump testable */
memset(pKI->aSortFlags, 0, pKI->nKeyField); /* Makes OP_Jump testable */
sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO);
testcase( pKI->nAllField > pKI->nKeyField+2 );
pOp->p4.pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pSort->pOrderBy,nOBSat,
@@ -1275,7 +1275,7 @@ KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
int nExtra = (N+X)*(sizeof(CollSeq*)+1) - sizeof(CollSeq*);
KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra);
if( p ){
p->aSortOrder = (u8*)&p->aColl[N+X];
p->aSortFlags = (u8*)&p->aColl[N+X];
p->nKeyField = (u16)N;
p->nAllField = (u16)(N+X);
p->enc = ENC(db);
@@ -1352,7 +1352,7 @@ KeyInfo *sqlite3KeyInfoFromExprList(
assert( sqlite3KeyInfoIsWriteable(pInfo) );
for(i=iStart, pItem=pList->a+iStart; i<nExpr; i++, pItem++){
pInfo->aColl[i-iStart] = sqlite3ExprNNCollSeq(pParse, pItem->pExpr);
pInfo->aSortOrder[i-iStart] = pItem->sortOrder;
pInfo->aSortFlags[i-iStart] = pItem->sortFlags;
}
}
return pInfo;
@@ -2253,7 +2253,7 @@ static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){
}
assert( sqlite3KeyInfoIsWriteable(pRet) );
pRet->aColl[i] = pColl;
pRet->aSortOrder[i] = pOrderBy->a[i].sortOrder;
pRet->aSortFlags[i] = pOrderBy->a[i].sortFlags;
}
}
@@ -3228,7 +3228,7 @@ static int multiSelectOrderBy(
assert( sqlite3KeyInfoIsWriteable(pKeyDup) );
for(i=0; i<nExpr; i++){
pKeyDup->aColl[i] = multiSelectCollSeq(pParse, p, i);
pKeyDup->aSortOrder[i] = 0;
pKeyDup->aSortFlags[i] = 0;
}
}
}
@@ -4402,7 +4402,7 @@ static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){
ExprList *pEList = pFunc->x.pList; /* Arguments to agg function */
const char *zFunc; /* Name of aggregate function pFunc */
ExprList *pOrderBy;
u8 sortOrder;
u8 sortFlags;
assert( *ppMinMax==0 );
assert( pFunc->op==TK_AGG_FUNCTION );
@@ -4413,16 +4413,16 @@ static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){
zFunc = pFunc->u.zToken;
if( sqlite3StrICmp(zFunc, "min")==0 ){
eRet = WHERE_ORDERBY_MIN;
sortOrder = SQLITE_SO_ASC;
sortFlags = KEYINFO_ORDER_BIGNULL;
}else if( sqlite3StrICmp(zFunc, "max")==0 ){
eRet = WHERE_ORDERBY_MAX;
sortOrder = SQLITE_SO_DESC;
sortFlags = KEYINFO_ORDER_DESC;
}else{
return eRet;
}
*ppMinMax = pOrderBy = sqlite3ExprListDup(db, pEList, 0);
assert( pOrderBy!=0 || db->mallocFailed );
if( pOrderBy ) pOrderBy->a[0].sortOrder = sortOrder;
if( pOrderBy ) pOrderBy->a[0].sortFlags = sortFlags;
return eRet;
}

View File

@@ -2138,10 +2138,13 @@ struct KeyInfo {
u16 nKeyField; /* Number of key columns in the index */
u16 nAllField; /* Total columns, including key plus others */
sqlite3 *db; /* The database connection */
u8 *aSortOrder; /* Sort order for each column. */
u8 *aSortFlags; /* Sort order for each column. */
CollSeq *aColl[1]; /* Collating sequence for each term of the key */
};
#define KEYINFO_ORDER_DESC 0x01
#define KEYINFO_ORDER_BIGNULL 0x02
/*
** This object holds a record which has been parsed out into individual
** fields, for the purposes of doing a comparison.
@@ -2603,11 +2606,12 @@ struct ExprList {
Expr *pExpr; /* The parse tree for this expression */
char *zName; /* Token associated with this expression */
char *zSpan; /* Original text of the expression */
u8 sortOrder; /* 1 for DESC or 0 for ASC */
u8 sortFlags; /* Mask of KEYINFO_ORDER_* flags */
unsigned done :1; /* A flag to indicate when processing is finished */
unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */
unsigned reusable :1; /* Constant expression is reusable */
unsigned bSorterRef :1; /* Defer evaluation until after sorting */
unsigned bNulls: 1; /* True if explicit "NULLS FIRST/LAST" */
union {
struct {
u16 iOrderByCol; /* For ORDER BY, column number in result set */
@@ -3888,7 +3892,7 @@ void sqlite3ExprDelete(sqlite3*, Expr*);
void sqlite3ExprUnmapAndDelete(Parse*, Expr*);
ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*);
ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*);
void sqlite3ExprListSetSortOrder(ExprList*,int);
void sqlite3ExprListSetSortOrder(ExprList*,int,int);
void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int);
void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*);
void sqlite3ExprListDelete(sqlite3*, ExprList*);
@@ -4365,6 +4369,7 @@ void sqlite3KeyInfoUnref(KeyInfo*);
KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
KeyInfo *sqlite3KeyInfoFromExprList(Parse*, ExprList*, int, int);
int sqlite3HasExplicitNulls(Parse*, ExprList*);
#ifdef SQLITE_DEBUG
int sqlite3KeyInfoIsWriteable(KeyInfo*);

View File

@@ -463,6 +463,9 @@ TriggerStep *sqlite3TriggerInsertStep(
pTriggerStep->pIdList = pColumn;
pTriggerStep->pUpsert = pUpsert;
pTriggerStep->orconf = orconf;
if( pUpsert ){
sqlite3HasExplicitNulls(pParse, pUpsert->pUpsertTarget);
}
}else{
testcase( pColumn );
sqlite3IdListDelete(db, pColumn);

View File

@@ -2227,9 +2227,14 @@ case OP_Compare: {
REGISTER_TRACE(p2+idx, &aMem[p2+idx]);
assert( i<pKeyInfo->nKeyField );
pColl = pKeyInfo->aColl[i];
bRev = pKeyInfo->aSortOrder[i];
bRev = (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_DESC);
iCompare = sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl);
if( iCompare ){
if( (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_BIGNULL)
&& ((aMem[p1+idx].flags & MEM_Null) || (aMem[p2+idx].flags & MEM_Null))
){
iCompare = -iCompare;
}
if( bRev ) iCompare = -iCompare;
break;
}

View File

@@ -1493,14 +1493,16 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
case P4_KEYINFO: {
int j;
KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
assert( pKeyInfo->aSortOrder!=0 );
assert( pKeyInfo->aSortFlags!=0 );
sqlite3_str_appendf(&x, "k(%d", pKeyInfo->nKeyField);
for(j=0; j<pKeyInfo->nKeyField; j++){
CollSeq *pColl = pKeyInfo->aColl[j];
const char *zColl = pColl ? pColl->zName : "";
if( strcmp(zColl, "BINARY")==0 ) zColl = "B";
sqlite3_str_appendf(&x, ",%s%s",
pKeyInfo->aSortOrder[j] ? "-" : "", zColl);
sqlite3_str_appendf(&x, ",%s%s%s",
(pKeyInfo->aSortFlags[j] & KEYINFO_ORDER_DESC) ? "-" : "",
(pKeyInfo->aSortFlags[j] & KEYINFO_ORDER_BIGNULL)? "N." : "",
zColl);
}
sqlite3_str_append(&x, ")", 1);
break;
@@ -3813,7 +3815,7 @@ UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(
p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte);
if( !p ) return 0;
p->aMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))];
assert( pKeyInfo->aSortOrder!=0 );
assert( pKeyInfo->aSortFlags!=0 );
p->pKeyInfo = pKeyInfo;
p->nField = pKeyInfo->nKeyField + 1;
return p;
@@ -3912,7 +3914,7 @@ static int vdbeRecordCompareDebug(
if( szHdr1>98307 ) return SQLITE_CORRUPT;
d1 = szHdr1;
assert( pKeyInfo->nAllField>=pPKey2->nField || CORRUPT_DB );
assert( pKeyInfo->aSortOrder!=0 );
assert( pKeyInfo->aSortFlags!=0 );
assert( pKeyInfo->nKeyField>0 );
assert( idx1<=szHdr1 || CORRUPT_DB );
do{
@@ -3943,7 +3945,12 @@ static int vdbeRecordCompareDebug(
pKeyInfo->nAllField>i ? pKeyInfo->aColl[i] : 0);
if( rc!=0 ){
assert( mem1.szMalloc==0 ); /* See comment below */
if( pKeyInfo->aSortOrder[i] ){
if( (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_BIGNULL)
&& ((mem1.flags & MEM_Null) || (pPKey2->aMem[i].flags & MEM_Null))
){
rc = -rc;
}
if( pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_DESC ){
rc = -rc; /* Invert the result for DESC sort order. */
}
goto debugCompareEnd;
@@ -4319,7 +4326,7 @@ int sqlite3VdbeRecordCompareWithSkip(
VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */
assert( pPKey2->pKeyInfo->nAllField>=pPKey2->nField
|| CORRUPT_DB );
assert( pPKey2->pKeyInfo->aSortOrder!=0 );
assert( pPKey2->pKeyInfo->aSortFlags!=0 );
assert( pPKey2->pKeyInfo->nKeyField>0 );
assert( idx1<=szHdr1 || CORRUPT_DB );
do{
@@ -4442,8 +4449,14 @@ int sqlite3VdbeRecordCompareWithSkip(
}
if( rc!=0 ){
if( pPKey2->pKeyInfo->aSortOrder[i] ){
rc = -rc;
int sortFlags = pPKey2->pKeyInfo->aSortFlags[i];
if( sortFlags ){
if( (sortFlags & KEYINFO_ORDER_BIGNULL)==0
|| ((sortFlags & KEYINFO_ORDER_DESC)
!=(serial_type==0 || (pRhs->flags&MEM_Null)))
){
rc = -rc;
}
}
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, rc) );
assert( mem1.szMalloc==0 ); /* See comment below */
@@ -4660,7 +4673,10 @@ RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *p){
** header size is (12*5 + 1 + 1) bytes. */
if( p->pKeyInfo->nAllField<=13 ){
int flags = p->aMem[0].flags;
if( p->pKeyInfo->aSortOrder[0] ){
if( p->pKeyInfo->aSortFlags[0] ){
if( p->pKeyInfo->aSortFlags[0] & KEYINFO_ORDER_BIGNULL ){
return sqlite3VdbeRecordCompare;
}
p->r1 = 1;
p->r2 = -1;
}else{
@@ -5006,7 +5022,7 @@ void sqlite3VdbePreUpdateHook(
preupdate.keyinfo.db = db;
preupdate.keyinfo.enc = ENC(db);
preupdate.keyinfo.nKeyField = pTab->nCol;
preupdate.keyinfo.aSortOrder = (u8*)&fakeSortOrder;
preupdate.keyinfo.aSortFlags = (u8*)&fakeSortOrder;
preupdate.iKey1 = iKey1;
preupdate.iKey2 = iKey2;
preupdate.pTab = pTab;

View File

@@ -829,7 +829,8 @@ static int vdbeSorterCompareText(
);
}
}else{
if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){
assert( !(pTask->pSorter->pKeyInfo->aSortFlags[0]&KEYINFO_ORDER_BIGNULL) );
if( pTask->pSorter->pKeyInfo->aSortFlags[0] ){
res = res * -1;
}
}
@@ -897,7 +898,8 @@ static int vdbeSorterCompareInt(
pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
);
}
}else if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){
}else if( pTask->pSorter->pKeyInfo->aSortFlags[0] ){
assert( !(pTask->pSorter->pKeyInfo->aSortFlags[0]&KEYINFO_ORDER_BIGNULL) );
res = res * -1;
}
@@ -1012,6 +1014,7 @@ int sqlite3VdbeSorterInit(
if( pKeyInfo->nAllField<13
&& (pKeyInfo->aColl[0]==0 || pKeyInfo->aColl[0]==db->pDfltColl)
&& (pKeyInfo->aSortFlags[0] & KEYINFO_ORDER_BIGNULL)==0
){
pSorter->typeMask = SORTER_TYPE_INTEGER | SORTER_TYPE_TEXT;
}

View File

@@ -934,6 +934,7 @@ static sqlite3_index_info *allocateIndexInfo(
for(i=0; i<n; i++){
Expr *pExpr = pOrderBy->a[i].pExpr;
if( pExpr->op!=TK_COLUMN || pExpr->iTable!=pSrc->iCursor ) break;
if( pOrderBy->a[i].sortFlags & KEYINFO_ORDER_BIGNULL ) break;
}
if( i==n){
nOrderBy = n;
@@ -1032,7 +1033,7 @@ static sqlite3_index_info *allocateIndexInfo(
for(i=0; i<nOrderBy; i++){
Expr *pExpr = pOrderBy->a[i].pExpr;
pIdxOrderBy[i].iColumn = pExpr->iColumn;
pIdxOrderBy[i].desc = pOrderBy->a[i].sortOrder;
pIdxOrderBy[i].desc = pOrderBy->a[i].sortFlags & KEYINFO_ORDER_DESC;
}
*pmNoOmit = mNoOmit;
@@ -3804,7 +3805,7 @@ static i8 wherePathSatisfiesOrderBy(
*/
if( pIndex ){
iColumn = pIndex->aiColumn[j];
revIdx = pIndex->aSortOrder[j];
revIdx = pIndex->aSortOrder[j] & KEYINFO_ORDER_DESC;
if( iColumn==pIndex->pTable->iPKey ) iColumn = XN_ROWID;
}else{
iColumn = XN_ROWID;
@@ -3856,22 +3857,28 @@ static i8 wherePathSatisfiesOrderBy(
/* Make sure the sort order is compatible in an ORDER BY clause.
** Sort order is irrelevant for a GROUP BY clause. */
if( revSet ){
if( (rev ^ revIdx)!=pOrderBy->a[i].sortOrder ) isMatch = 0;
if( (rev ^ revIdx)!=(pOrderBy->a[i].sortFlags&KEYINFO_ORDER_DESC) ){
isMatch = 0;
}
}else{
rev = revIdx ^ pOrderBy->a[i].sortOrder;
rev = revIdx ^ (pOrderBy->a[i].sortFlags & KEYINFO_ORDER_DESC);
if( rev ) *pRevMask |= MASKBIT(iLoop);
revSet = 1;
}
}
if( isMatch && (pOrderBy->a[i].sortFlags & KEYINFO_ORDER_BIGNULL) ){
if( j==pLoop->u.btree.nEq ){
pLoop->wsFlags |= WHERE_BIGNULL_SORT;
}else{
isMatch = 0;
}
}
if( isMatch ){
if( iColumn==XN_ROWID ){
testcase( distinctColumns==0 );
distinctColumns = 1;
}
obSat |= MASKBIT(i);
if( (wctrlFlags & WHERE_ORDERBY_MIN) && j==pLoop->u.btree.nEq ){
pLoop->wsFlags |= WHERE_MIN_ORDERED;
}
}else{
/* No match found */
if( j==0 || j<nKeyCol ){
@@ -5065,6 +5072,7 @@ WhereInfo *sqlite3WhereBegin(
sqlite3VdbeSetP4KeyInfo(pParse, pIx);
if( (pLoop->wsFlags & WHERE_CONSTRAINT)!=0
&& (pLoop->wsFlags & (WHERE_COLUMN_RANGE|WHERE_SKIPSCAN))==0
&& (pLoop->wsFlags & WHERE_BIGNULL_SORT)==0
&& (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0
&& pWInfo->eDistinct!=WHERE_DISTINCT_ORDERED
){
@@ -5206,6 +5214,10 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
VdbeCoverageIf(v, pLevel->op==OP_Next);
VdbeCoverageIf(v, pLevel->op==OP_Prev);
VdbeCoverageIf(v, pLevel->op==OP_VNext);
if( pLevel->regBignull ){
sqlite3VdbeResolveLabel(v, pLevel->addrBignull);
sqlite3VdbeAddOp2(v, OP_IfNotZero, pLevel->regBignull, pLevel->p2-1);
}
#ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT
if( addrSeek ) sqlite3VdbeJumpHere(v, addrSeek);
#endif

View File

@@ -71,13 +71,15 @@ struct WhereLevel {
int addrCont; /* Jump here to continue with the next loop cycle */
int addrFirst; /* First instruction of interior of the loop */
int addrBody; /* Beginning of the body of this loop */
int regBignull; /* big-null flag reg. True if a NULL-scan is needed */
int addrBignull; /* Jump here for next part of big-null scan */
#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
u32 iLikeRepCntr; /* LIKE range processing counter register (times 2) */
int addrLikeRep; /* LIKE range processing address */
#endif
u8 iFrom; /* Which entry in the FROM clause */
u8 op, p3, p5; /* Opcode, P3 & P5 of the opcode that ends the loop */
int p1, p2; /* Operands of the opcode used to ends the loop */
int p1, p2; /* Operands of the opcode used to end the loop */
union { /* Information that depends on pWLoop->wsFlags */
struct {
int nIn; /* Number of entries in aInLoop[] */
@@ -586,6 +588,6 @@ void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereClause*);
#define WHERE_UNQ_WANTED 0x00010000 /* WHERE_ONEROW would have been helpful*/
#define WHERE_PARTIALIDX 0x00020000 /* The automatic index is partial */
#define WHERE_IN_EARLYOUT 0x00040000 /* Perhaps quit IN loops early */
#define WHERE_MIN_ORDERED 0x00080000 /* Column nEq of index is min() expr */
#define WHERE_BIGNULL_SORT 0x00080000 /* Column nEq of index is BIGNULL */
#endif /* !defined(SQLITE_WHEREINT_H) */

View File

@@ -1550,31 +1550,12 @@ Bitmask sqlite3WhereCodeOneLoopStart(
u8 bSeekPastNull = 0; /* True to seek past initial nulls */
u8 bStopAtNull = 0; /* Add condition to terminate at NULLs */
int omitTable; /* True if we use the index only */
int regBignull = 0; /* big-null flag register */
pIdx = pLoop->u.btree.pIndex;
iIdxCur = pLevel->iIdxCur;
assert( nEq>=pLoop->nSkip );
/* If this loop satisfies a sort order (pOrderBy) request that
** was passed to this function to implement a "SELECT min(x) ..."
** query, then the caller will only allow the loop to run for
** a single iteration. This means that the first row returned
** should not have a NULL value stored in 'x'. If column 'x' is
** the first one after the nEq equality constraints in the index,
** this requires some special handling.
*/
assert( (pWInfo->pOrderBy!=0 && pWInfo->pOrderBy->nExpr==1)
|| (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0 );
if( pLoop->wsFlags & WHERE_MIN_ORDERED ){
assert( (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN) );
assert( pWInfo->nOBSat );
assert( pIdx->nColumn>nEq );
assert( pLoop->nSkip==0 );
bSeekPastNull = 1;
nExtraReg = 1;
}
/* Find any inequality constraint terms for the start and end
** of the range.
*/
@@ -1615,6 +1596,25 @@ Bitmask sqlite3WhereCodeOneLoopStart(
}
assert( pRangeEnd==0 || (pRangeEnd->wtFlags & TERM_VNULL)==0 );
/* If the WHERE_BIGNULL_SORT flag is set, then index column nEq uses
** a non-default "big-null" sort (either ASC NULLS LAST or DESC NULLS
** FIRST). In both cases separate ordered scans are made of those
** index entries for which the column is null and for those for which
** it is not. For an ASC sort, the non-NULL entries are scanned first.
** For DESC, NULL entries are scanned first.
*/
if( (pLoop->wsFlags & (WHERE_TOP_LIMIT|WHERE_BTM_LIMIT))==0
&& (pLoop->wsFlags & WHERE_BIGNULL_SORT)!=0
){
assert( bSeekPastNull==0 && nExtraReg==0 && nBtm==0 && nTop==0 );
assert( pRangeEnd==0 && pRangeStart==0 );
assert( pLoop->nSkip==0 );
nExtraReg = 1;
bSeekPastNull = 1;
pLevel->regBignull = regBignull = ++pParse->nMem;
pLevel->addrBignull = sqlite3VdbeMakeLabel(pParse);
}
/* If we are doing a reverse order scan on an ascending index, or
** a forward order scan on a descending index, interchange the
** start and end terms (pRangeStart and pRangeEnd).
@@ -1637,7 +1637,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
if( zStartAff && nTop ){
zEndAff = sqlite3DbStrDup(db, &zStartAff[nEq]);
}
addrNxt = pLevel->addrNxt;
addrNxt = (regBignull ? pLevel->addrBignull : pLevel->addrNxt);
testcase( pRangeStart && (pRangeStart->eOperator & WO_LE)!=0 );
testcase( pRangeStart && (pRangeStart->eOperator & WO_GE)!=0 );
@@ -1671,10 +1671,14 @@ Bitmask sqlite3WhereCodeOneLoopStart(
}
bSeekPastNull = 0;
}else if( bSeekPastNull ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
nConstraint++;
startEq = 0;
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
start_constraints = 1;
nConstraint++;
}else if( regBignull ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
start_constraints = 1;
nConstraint++;
}
codeApplyAffinity(pParse, regBase, nConstraint - bSeekPastNull, zStartAff);
if( pLoop->nSkip>0 && nConstraint==pLoop->nSkip ){
@@ -1685,6 +1689,11 @@ Bitmask sqlite3WhereCodeOneLoopStart(
if( pLoop->wsFlags & WHERE_IN_EARLYOUT ){
sqlite3VdbeAddOp1(v, OP_SeekHit, iIdxCur);
}
if( regBignull ){
sqlite3VdbeAddOp2(v, OP_Integer, 1, regBignull);
VdbeComment((v, "NULL-scan pass ctr"));
}
op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev];
assert( op!=0 );
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
@@ -1696,23 +1705,21 @@ Bitmask sqlite3WhereCodeOneLoopStart(
VdbeCoverageIf(v, op==OP_SeekLE); testcase( op==OP_SeekLE );
VdbeCoverageIf(v, op==OP_SeekLT); testcase( op==OP_SeekLT );
if( bSeekPastNull && (pLoop->wsFlags & WHERE_TOP_LIMIT)==0 ){
/* If bSeekPastNull is set only to skip past the NULL values for
** a query like "SELECT min(a), b FROM t1", then add code so that
** if there are no rows with (a IS NOT NULL), then do the seek
** without jumping past NULLs instead. This allows the code in
** select.c to pick a value for "b" in the above query. */
assert( startEq==0 && (op==OP_SeekGT || op==OP_SeekLT) );
assert( (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)!=0 && pWInfo->nOBSat>0 );
sqlite3VdbeChangeP2(v, -1, sqlite3VdbeCurrentAddr(v)+1);
assert( bSeekPastNull==0 || bStopAtNull==0 );
if( regBignull ){
assert( bSeekPastNull==1 || bStopAtNull==1 );
assert( bSeekPastNull==!bStopAtNull );
assert( bStopAtNull==startEq );
sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+2);
op = aStartOp[(start_constraints<<2) + (1<<1) + bRev];
assert( op==OP_SeekGE || op==OP_SeekLE );
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
op = aStartOp[(nConstraint>1)*4 + 2 + bRev];
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase,
nConstraint-startEq);
VdbeCoverage(v);
VdbeCoverageIf(v, op==OP_Rewind); testcase( op==OP_Rewind );
VdbeCoverageIf(v, op==OP_Last); testcase( op==OP_Last );
VdbeCoverageIf(v, op==OP_SeekGE); testcase( op==OP_SeekGE );
VdbeCoverageIf(v, op==OP_SeekLE); testcase( op==OP_SeekLE );
assert( op==OP_Rewind || op==OP_Last || op==OP_SeekGE || op==OP_SeekLE);
}
}
@@ -1745,8 +1752,10 @@ Bitmask sqlite3WhereCodeOneLoopStart(
endEq = 1;
}
}else if( bStopAtNull ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
endEq = 0;
if( regBignull==0 ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
endEq = 0;
}
nConstraint++;
}
sqlite3DbFree(db, zStartAff);
@@ -1757,6 +1766,12 @@ Bitmask sqlite3WhereCodeOneLoopStart(
/* Check if the index cursor is past the end of the range. */
if( nConstraint ){
if( regBignull ){
/* Except, skip the end-of-range check while doing the NULL-scan */
sqlite3VdbeAddOp2(v, OP_IfNot, regBignull, sqlite3VdbeCurrentAddr(v)+3);
VdbeComment((v, "If NULL-scan 2nd pass"));
VdbeCoverage(v);
}
op = aEndOp[bRev*2 + endEq];
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
testcase( op==OP_IdxGT ); VdbeCoverageIf(v, op==OP_IdxGT );
@@ -1764,6 +1779,23 @@ Bitmask sqlite3WhereCodeOneLoopStart(
testcase( op==OP_IdxLT ); VdbeCoverageIf(v, op==OP_IdxLT );
testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE );
}
if( regBignull ){
/* During a NULL-scan, check to see if we have reached the end of
** the NULLs */
assert( bSeekPastNull==!bStopAtNull );
assert( bSeekPastNull+bStopAtNull==1 );
assert( nConstraint+bSeekPastNull>0 );
sqlite3VdbeAddOp2(v, OP_If, regBignull, sqlite3VdbeCurrentAddr(v)+2);
VdbeComment((v, "If NULL-scan 1st pass"));
VdbeCoverage(v);
op = aEndOp[bRev*2 + bSeekPastNull];
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase,
nConstraint+bSeekPastNull);
testcase( op==OP_IdxGT ); VdbeCoverageIf(v, op==OP_IdxGT );
testcase( op==OP_IdxGE ); VdbeCoverageIf(v, op==OP_IdxGE );
testcase( op==OP_IdxLT ); VdbeCoverageIf(v, op==OP_IdxLT );
testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE );
}
if( pLoop->wsFlags & WHERE_IN_EARLYOUT ){
sqlite3VdbeAddOp2(v, OP_SeekHit, iIdxCur, 1);

View File

@@ -888,7 +888,7 @@ static ExprList *exprListAppendList(
pDup->flags &= ~(EP_IntValue|EP_IsTrue|EP_IsFalse);
}
pList = sqlite3ExprListAppend(pParse, pList, pDup);
if( pList ) pList->a[nInit+i].sortOrder = pAppend->a[i].sortOrder;
if( pList ) pList->a[nInit+i].sortFlags = pAppend->a[i].sortFlags;
}
}
return pList;
@@ -1315,8 +1315,8 @@ void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){
pWin->regApp = pParse->nMem+1;
pParse->nMem += 3;
if( pKeyInfo && pWin->pFunc->zName[1]=='i' ){
assert( pKeyInfo->aSortOrder[0]==0 );
pKeyInfo->aSortOrder[0] = 1;
assert( pKeyInfo->aSortFlags[0]==0 );
pKeyInfo->aSortFlags[0] = KEYINFO_ORDER_DESC;
}
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pWin->csrApp, 2);
sqlite3VdbeAppendP4(v, pKeyInfo, P4_KEYINFO);
@@ -1876,12 +1876,13 @@ static void windowCodeRangeTest(
int reg2 = sqlite3GetTempReg(pParse);
int arith = OP_Add;
int addrGe;
ExprList *pOrderBy = p->pMWin->pOrderBy;
int regString = ++pParse->nMem;
assert( op==OP_Ge || op==OP_Gt || op==OP_Le );
assert( p->pMWin->pOrderBy && p->pMWin->pOrderBy->nExpr==1 );
if( p->pMWin->pOrderBy->a[0].sortOrder ){
assert( pOrderBy && pOrderBy->nExpr==1 );
if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_DESC ){
switch( op ){
case OP_Ge: op = OP_Le; break;
case OP_Gt: op = OP_Lt; break;
@@ -1901,6 +1902,28 @@ static void windowCodeRangeTest(
VdbeCoverage(v);
sqlite3VdbeAddOp3(v, arith, regVal, reg1, reg1);
sqlite3VdbeJumpHere(v, addrGe);
if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_BIGNULL ){
int addr;
addr = sqlite3VdbeAddOp1(v, OP_NotNull, reg1); VdbeCoverage(v);
switch( op ){
case OP_Ge: sqlite3VdbeAddOp2(v, OP_Goto, 0, lbl); break;
case OP_Gt:
sqlite3VdbeAddOp2(v, OP_NotNull, reg2, lbl);
VdbeCoverage(v);
break;
case OP_Le:
sqlite3VdbeAddOp2(v, OP_IsNull, reg2, lbl);
VdbeCoverage(v);
break;
default: assert( op==OP_Lt ); /* no-op */
}
sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+2);
sqlite3VdbeJumpHere(v, addr);
sqlite3VdbeAddOp2(v, OP_IsNull, reg2, lbl); VdbeCoverage(v);
if( op==OP_Gt || op==OP_Ge ){
sqlite3VdbeChangeP2(v, -1, sqlite3VdbeCurrentAddr(v)+1);
}
}
sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v);
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le );

View File

@@ -200,4 +200,37 @@ do_execsql_test 5.1 {
SELECT MIN(a) FROM t1 WHERE a=123;
} {123}
#-------------------------------------------------------------------------
# Tests for ticket f8a7060ece.
#
reset_db
do_execsql_test 6.1.0 {
CREATE TABLE t1(a, b, c);
INSERT INTO t1 VALUES(NULL, 1, 'x');
CREATE INDEX i1 ON t1(a);
}
do_execsql_test 6.1.1 {
SELECT min(a), b, c FROM t1 WHERE c='x';
} {{} 1 x}
do_execsql_test 6.1.2 {
INSERT INTO t1 VALUES(1, 2, 'y');
} {}
do_execsql_test 6.1.3 {
SELECT min(a), b, c FROM t1 WHERE c='x';
} {{} 1 x}
do_execsql_test 6.2.0 {
CREATE TABLE t0(c0 UNIQUE, c1);
INSERT INTO t0(c1) VALUES (0);
INSERT INTO t0(c0) VALUES (0);
CREATE VIEW v0(c0, c1) AS
SELECT t0.c1, t0.c0 FROM t0 WHERE CAST(t0.rowid AS INT) = 1;
}
do_execsql_test 6.2.1 {
SELECT c0, c1 FROM v0;
} {0 {}}
do_execsql_test 6.2.2 {
SELECT v0.c0, MIN(v0.c1) FROM v0;
} {0 {}}
finish_test

242
test/nulls1.test Normal file
View File

@@ -0,0 +1,242 @@
# 2019 August 10
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix nulls1
do_execsql_test 1.0 {
DROP TABLE IF EXISTS t3;
CREATE TABLE t3(a INTEGER);
INSERT INTO t3 VALUES(NULL), (10), (30), (20), (NULL);
} {}
for {set a 0} {$a < 3} {incr a} {
foreach {tn limit} {
1 ""
2 "LIMIT 10"
} {
do_execsql_test 1.$a.$tn.1 "
SELECT a FROM t3 ORDER BY a nULLS FIRST $limit
" {{} {} 10 20 30}
do_execsql_test 1.$a.$tn.2 "
SELECT a FROM t3 ORDER BY a nULLS LAST $limit
" {10 20 30 {} {}}
do_execsql_test 1.$a.$tn.3 "
SELECT a FROM t3 ORDER BY a DESC nULLS FIRST $limit
" {{} {} 30 20 10}
do_execsql_test 1.$a.$tn.4 "
SELECT a FROM t3 ORDER BY a DESC nULLS LAST $limit
" {30 20 10 {} {}}
}
switch $a {
0 {
execsql { CREATE INDEX i1 ON t3(a) }
}
1 {
execsql { DROP INDEX i1 ; CREATE INDEX i1 ON t3(a DESC) }
}
}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
CREATE TABLE t2(a, b, c);
CREATE INDEX i2 ON t2(a, b);
INSERT INTO t2 VALUES(1, 1, 1);
INSERT INTO t2 VALUES(1, NULL, 2);
INSERT INTO t2 VALUES(1, NULL, 3);
INSERT INTO t2 VALUES(1, 4, 4);
}
do_execsql_test 2.1 {
SELECT * FROM t2 WHERE a=1 ORDER BY b NULLS LAST
} {
1 1 1 1 4 4 1 {} 2 1 {} 3
}
do_execsql_test 2.2 {
SELECT * FROM t2 WHERE a=1 ORDER BY b DESC NULLS FIRST
} {
1 {} 3
1 {} 2
1 4 4
1 1 1
}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 3.0 {
CREATE TABLE t1(a, b, c, d, UNIQUE (b));
}
foreach {tn sql err} {
1 { CREATE INDEX i1 ON t1(a ASC NULLS LAST) } LAST
2 { CREATE INDEX i1 ON t1(a ASC NULLS FIRST) } FIRST
3 { CREATE INDEX i1 ON t1(a, b ASC NULLS LAST) } LAST
4 { CREATE INDEX i1 ON t1(a, b ASC NULLS FIRST) } FIRST
5 { CREATE INDEX i1 ON t1(a DESC NULLS LAST) } LAST
6 { CREATE INDEX i1 ON t1(a DESC NULLS FIRST) } FIRST
7 { CREATE INDEX i1 ON t1(a, b DESC NULLS LAST) } LAST
8 { CREATE INDEX i1 ON t1(a, b DESC NULLS FIRST) } FIRST
9 { CREATE TABLE t2(a, b, PRIMARY KEY(a DESC, b NULLS FIRST)) } FIRST
10 { CREATE TABLE t2(a, b, UNIQUE(a DESC NULLS FIRST, b)) } FIRST
11 { INSERT INTO t1 VALUES(1, 2, 3, 4)
ON CONFLICT (b DESC NULLS LAST) DO UPDATE SET a = a+1 } LAST
12 {
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
INSERT INTO t1 VALUES(1, 2, 3, 4)
ON CONFLICT (b DESC NULLS FIRST) DO UPDATE SET a = a+1;
END
} FIRST
} {
do_catchsql_test 3.1.$tn $sql "1 {unsupported use of NULLS $err}"
}
do_execsql_test 3.2 {
CREATE TABLE first(nulls, last);
INSERT INTO first(last, nulls) VALUES(100,200), (300,400), (200,300);
SELECT * FROM first ORDER BY nulls;
} {
200 100
300 200
400 300
}
#-------------------------------------------------------------------------
#
ifcapable vtab {
register_echo_module db
do_execsql_test 4.0 {
CREATE TABLE tx(a INTEGER PRIMARY KEY, b, c);
CREATE INDEX i1 ON tx(b);
INSERT INTO tx VALUES(1, 1, 1);
INSERT INTO tx VALUES(2, NULL, 2);
INSERT INTO tx VALUES(3, 3, 3);
INSERT INTO tx VALUES(4, NULL, 4);
INSERT INTO tx VALUES(5, 5, 5);
CREATE VIRTUAL TABLE te USING echo(tx);
}
do_execsql_test 4.1 {
SELECT * FROM tx ORDER BY b NULLS FIRST;
} {2 {} 2 4 {} 4 1 1 1 3 3 3 5 5 5}
do_execsql_test 4.2 {
SELECT * FROM te ORDER BY b NULLS FIRST;
} {2 {} 2 4 {} 4 1 1 1 3 3 3 5 5 5}
do_execsql_test 4.3 {
SELECT * FROM tx ORDER BY b NULLS LAST;
} {1 1 1 3 3 3 5 5 5 2 {} 2 4 {} 4}
do_execsql_test 4.4 {
SELECT * FROM te ORDER BY b NULLS LAST;
} {1 1 1 3 3 3 5 5 5 2 {} 2 4 {} 4}
}
#-------------------------------------------------------------------------
#
do_execsql_test 5.0 {
CREATE TABLE t4(a, b, c);
INSERT INTO t4 VALUES(1, 1, 11);
INSERT INTO t4 VALUES(1, 2, 12);
INSERT INTO t4 VALUES(1, NULL, 1);
INSERT INTO t4 VALUES(2, NULL, 1);
INSERT INTO t4 VALUES(2, 2, 12);
INSERT INTO t4 VALUES(2, 1, 11);
INSERT INTO t4 VALUES(3, NULL, 1);
INSERT INTO t4 VALUES(3, 2, 12);
INSERT INTO t4 VALUES(3, NULL, 3);
}
do_execsql_test 5.1 {
SELECT * FROM t4 WHERE a IN (1, 2, 3) ORDER BY a, b NULLS LAST
} {
1 1 11 1 2 12 1 {} 1
2 1 11 2 2 12 2 {} 1
3 2 12 3 {} 1 3 {} 3
}
do_execsql_test 5.2 {
CREATE INDEX t4ab ON t4(a, b);
SELECT * FROM t4 WHERE a IN (1, 2, 3) ORDER BY a, b NULLS LAST
} {
1 1 11 1 2 12 1 {} 1
2 1 11 2 2 12 2 {} 1
3 2 12 3 {} 1 3 {} 3
}
do_eqp_test 5.3 {
SELECT * FROM t4 WHERE a IN (1, 2, 3) ORDER BY a, b NULLS LAST
} {
QUERY PLAN
`--SEARCH TABLE t4 USING INDEX t4ab (a=?)
}
do_execsql_test 5.4 {
SELECT * FROM t4 WHERE a IN (1, 2, 3) ORDER BY a DESC, b DESC NULLS FIRST
} {
3 {} 3 3 {} 1 3 2 12
2 {} 1 2 2 12 2 1 11
1 {} 1 1 2 12 1 1 11
}
do_eqp_test 5.5 {
SELECT * FROM t4 WHERE a IN (1, 2, 3) ORDER BY a DESC, b DESC NULLS FIRST
} {
QUERY PLAN
`--SEARCH TABLE t4 USING INDEX t4ab (a=?)
}
#-------------------------------------------------------------------------
#
do_execsql_test 6.0 {
CREATE TABLE t5(a, b, c);
WITH s(i) AS (
VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<200
)
INSERT INTO t5 SELECT i%2, CASE WHEN (i%10)==0 THEN NULL ELSE i END, i FROM s;
}
set res1 [db eval { SELECT a,b FROM t5 WHERE a=1 ORDER BY b NULLS LAST, c }]
set res2 [db eval {
SELECT a,b FROM t5 WHERE a=1 ORDER BY b DESC NULLS FIRST, c DESC
}]
do_execsql_test 6.1.1 {
CREATE INDEX t5ab ON t5(a, b, c);
SELECT a,b FROM t5 WHERE a=1 ORDER BY b NULLS LAST, c;
} $res1
do_eqp_test 6.1.2 {
SELECT a,b FROM t5 WHERE a=1 ORDER BY b NULLS LAST, c;
} {
QUERY PLAN
`--SEARCH TABLE t5 USING COVERING INDEX t5ab (a=?)
}
do_execsql_test 6.2.1 {
SELECT a,b FROM t5 WHERE a=1 ORDER BY b DESC NULLS FIRST, c DESC
} $res2
do_eqp_test 6.2.2 {
SELECT a,b FROM t5 WHERE a=1 ORDER BY b DESC NULLS FIRST, c DESC
} {
QUERY PLAN
`--SEARCH TABLE t5 USING COVERING INDEX t5ab (a=?)
}
finish_test

View File

@@ -70,8 +70,8 @@ proc execsql {sql} {
proc execsql_test {tn sql} {
set res [execsql $sql]
set sql [string map {string_agg group_concat} $sql]
set sql [string map [list {NULLS FIRST} {}] $sql]
set sql [string map [list {NULLS LAST} {}] $sql]
# set sql [string map [list {NULLS FIRST} {}] $sql]
# set sql [string map [list {NULLS LAST} {}] $sql]
puts $::fd "do_execsql_test $tn {"
puts $::fd " [string trim $sql]"
puts $::fd "} {$res}"

View File

@@ -41,54 +41,146 @@ do_execsql_test 1.0 {
do_execsql_test 1.1 {
SELECT a, sum(b) FROM t3 GROUP BY a ORDER BY 1;
} {0 550 1 460 2 470 3 480 4 490 5 500 6 510 7 520 8 530 9 540}
} {0 550 1 460 2 470 3 480 4 490 5 500 6 510 7 520 8 530
9 540}
do_execsql_test 1.2 {
SELECT a, sum(b) OVER (
ORDER BY a GROUPS BETWEEN CURRENT ROW AND CURRENT ROW
) FROM t3 ORDER BY 1;
} {0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460 2 470 2 470 2 470 2 470 2 470 2 470 2 470 2 470 2 470 2 470 3 480 3 480 3 480 3 480 3 480 3 480 3 480 3 480 3 480 3 480 4 490 4 490 4 490 4 490 4 490 4 490 4 490 4 490 4 490 4 490 5 500 5 500 5 500 5 500 5 500 5 500 5 500 5 500 5 500 5 500 6 510 6 510 6 510 6 510 6 510 6 510 6 510 6 510 6 510 6 510 7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540}
} {0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550
0 550 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460
1 460 1 460 2 470 2 470 2 470 2 470 2 470 2 470 2 470
2 470 2 470 2 470 3 480 3 480 3 480 3 480 3 480 3 480
3 480 3 480 3 480 3 480 4 490 4 490 4 490 4 490 4 490
4 490 4 490 4 490 4 490 4 490 5 500 5 500 5 500 5 500
5 500 5 500 5 500 5 500 5 500 5 500 6 510 6 510 6 510
6 510 6 510 6 510 6 510 6 510 6 510 6 510 7 520 7 520
7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 8 530
8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530
9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540
9 540}
do_execsql_test 1.3 {
SELECT a, sum(b) OVER (
ORDER BY a GROUPS BETWEEN 0 PRECEDING AND 0 FOLLOWING
) FROM t3 ORDER BY 1;
} {0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460 2 470 2 470 2 470 2 470 2 470 2 470 2 470 2 470 2 470 2 470 3 480 3 480 3 480 3 480 3 480 3 480 3 480 3 480 3 480 3 480 4 490 4 490 4 490 4 490 4 490 4 490 4 490 4 490 4 490 4 490 5 500 5 500 5 500 5 500 5 500 5 500 5 500 5 500 5 500 5 500 6 510 6 510 6 510 6 510 6 510 6 510 6 510 6 510 6 510 6 510 7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540}
} {0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550
0 550 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460
1 460 1 460 2 470 2 470 2 470 2 470 2 470 2 470 2 470
2 470 2 470 2 470 3 480 3 480 3 480 3 480 3 480 3 480
3 480 3 480 3 480 3 480 4 490 4 490 4 490 4 490 4 490
4 490 4 490 4 490 4 490 4 490 5 500 5 500 5 500 5 500
5 500 5 500 5 500 5 500 5 500 5 500 6 510 6 510 6 510
6 510 6 510 6 510 6 510 6 510 6 510 6 510 7 520 7 520
7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 8 530
8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530
9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540
9 540}
do_execsql_test 1.4 {
SELECT a, sum(b) OVER (
ORDER BY a GROUPS BETWEEN 2 PRECEDING AND 2 FOLLOWING
) FROM t3 ORDER BY 1;
} {0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590}
} {0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480
0 1480 0 1480 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960
1 1960 1 1960 1 1960 1 1960 2 2450 2 2450 2 2450 2 2450
2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 3 2400 3 2400
3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400
4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450
4 2450 4 2450 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500
5 2500 5 2500 5 2500 5 2500 6 2550 6 2550 6 2550 6 2550
6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 7 2600 7 2600
7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600
8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100
8 2100 8 2100 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590
9 1590 9 1590 9 1590 9 1590}
do_execsql_test 1.5 {
SELECT a, sum(b) OVER (
ORDER BY a RANGE BETWEEN 0 PRECEDING AND 0 FOLLOWING
) FROM t3 ORDER BY 1;
} {0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460 2 470 2 470 2 470 2 470 2 470 2 470 2 470 2 470 2 470 2 470 3 480 3 480 3 480 3 480 3 480 3 480 3 480 3 480 3 480 3 480 4 490 4 490 4 490 4 490 4 490 4 490 4 490 4 490 4 490 4 490 5 500 5 500 5 500 5 500 5 500 5 500 5 500 5 500 5 500 5 500 6 510 6 510 6 510 6 510 6 510 6 510 6 510 6 510 6 510 6 510 7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540}
} {0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550
0 550 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460
1 460 1 460 2 470 2 470 2 470 2 470 2 470 2 470 2 470
2 470 2 470 2 470 3 480 3 480 3 480 3 480 3 480 3 480
3 480 3 480 3 480 3 480 4 490 4 490 4 490 4 490 4 490
4 490 4 490 4 490 4 490 4 490 5 500 5 500 5 500 5 500
5 500 5 500 5 500 5 500 5 500 5 500 6 510 6 510 6 510
6 510 6 510 6 510 6 510 6 510 6 510 6 510 7 520 7 520
7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 8 530
8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530
9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540
9 540}
do_execsql_test 1.6 {
SELECT a, sum(b) OVER (
ORDER BY a RANGE BETWEEN 2 PRECEDING AND 2 FOLLOWING
) FROM t3 ORDER BY 1;
} {0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590}
} {0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480
0 1480 0 1480 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960
1 1960 1 1960 1 1960 1 1960 2 2450 2 2450 2 2450 2 2450
2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 3 2400 3 2400
3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400
4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450
4 2450 4 2450 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500
5 2500 5 2500 5 2500 5 2500 6 2550 6 2550 6 2550 6 2550
6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 7 2600 7 2600
7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600
8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100
8 2100 8 2100 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590
9 1590 9 1590 9 1590 9 1590}
do_execsql_test 1.7 {
SELECT a, sum(b) OVER (
ORDER BY a RANGE BETWEEN 2 PRECEDING AND 1 FOLLOWING
) FROM t3 ORDER BY 1;
} {0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 1 1480 1 1480 1 1480 1 1480 1 1480 1 1480 1 1480 1 1480 1 1480 1 1480 2 1960 2 1960 2 1960 2 1960 2 1960 2 1960 2 1960 2 1960 2 1960 2 1960 3 1900 3 1900 3 1900 3 1900 3 1900 3 1900 3 1900 3 1900 3 1900 3 1900 4 1940 4 1940 4 1940 4 1940 4 1940 4 1940 4 1940 4 1940 4 1940 4 1940 5 1980 5 1980 5 1980 5 1980 5 1980 5 1980 5 1980 5 1980 5 1980 5 1980 6 2020 6 2020 6 2020 6 2020 6 2020 6 2020 6 2020 6 2020 6 2020 6 2020 7 2060 7 2060 7 2060 7 2060 7 2060 7 2060 7 2060 7 2060 7 2060 7 2060 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590}
} {0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010
0 1010 0 1010 1 1480 1 1480 1 1480 1 1480 1 1480 1 1480
1 1480 1 1480 1 1480 1 1480 2 1960 2 1960 2 1960 2 1960
2 1960 2 1960 2 1960 2 1960 2 1960 2 1960 3 1900 3 1900
3 1900 3 1900 3 1900 3 1900 3 1900 3 1900 3 1900 3 1900
4 1940 4 1940 4 1940 4 1940 4 1940 4 1940 4 1940 4 1940
4 1940 4 1940 5 1980 5 1980 5 1980 5 1980 5 1980 5 1980
5 1980 5 1980 5 1980 5 1980 6 2020 6 2020 6 2020 6 2020
6 2020 6 2020 6 2020 6 2020 6 2020 6 2020 7 2060 7 2060
7 2060 7 2060 7 2060 7 2060 7 2060 7 2060 7 2060 7 2060
8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100
8 2100 8 2100 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590
9 1590 9 1590 9 1590 9 1590}
do_execsql_test 1.8.1 {
SELECT a, sum(b) OVER (
ORDER BY a RANGE BETWEEN 0 PRECEDING AND 1 FOLLOWING
) FROM t3 ORDER BY 1;
} {0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 1 930 1 930 1 930 1 930 1 930 1 930 1 930 1 930 1 930 1 930 2 950 2 950 2 950 2 950 2 950 2 950 2 950 2 950 2 950 2 950 3 970 3 970 3 970 3 970 3 970 3 970 3 970 3 970 3 970 3 970 4 990 4 990 4 990 4 990 4 990 4 990 4 990 4 990 4 990 4 990 5 1010 5 1010 5 1010 5 1010 5 1010 5 1010 5 1010 5 1010 5 1010 5 1010 6 1030 6 1030 6 1030 6 1030 6 1030 6 1030 6 1030 6 1030 6 1030 6 1030 7 1050 7 1050 7 1050 7 1050 7 1050 7 1050 7 1050 7 1050 7 1050 7 1050 8 1070 8 1070 8 1070 8 1070 8 1070 8 1070 8 1070 8 1070 8 1070 8 1070 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540}
} {0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010
0 1010 0 1010 1 930 1 930 1 930 1 930 1 930 1 930 1 930
1 930 1 930 1 930 2 950 2 950 2 950 2 950 2 950 2 950
2 950 2 950 2 950 2 950 3 970 3 970 3 970 3 970 3 970
3 970 3 970 3 970 3 970 3 970 4 990 4 990 4 990 4 990
4 990 4 990 4 990 4 990 4 990 4 990 5 1010 5 1010 5 1010
5 1010 5 1010 5 1010 5 1010 5 1010 5 1010 5 1010 6 1030
6 1030 6 1030 6 1030 6 1030 6 1030 6 1030 6 1030 6 1030
6 1030 7 1050 7 1050 7 1050 7 1050 7 1050 7 1050 7 1050
7 1050 7 1050 7 1050 8 1070 8 1070 8 1070 8 1070 8 1070
8 1070 8 1070 8 1070 8 1070 8 1070 9 540 9 540 9 540 9 540
9 540 9 540 9 540 9 540 9 540 9 540}
do_execsql_test 1.8.2 {
SELECT a, sum(b) OVER (
ORDER BY a DESC RANGE BETWEEN 0 PRECEDING AND 1 FOLLOWING
) FROM t3 ORDER BY 1;
} {0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 1 1010 1 1010 1 1010 1 1010 1 1010 1 1010 1 1010 1 1010 1 1010 1 1010 2 930 2 930 2 930 2 930 2 930 2 930 2 930 2 930 2 930 2 930 3 950 3 950 3 950 3 950 3 950 3 950 3 950 3 950 3 950 3 950 4 970 4 970 4 970 4 970 4 970 4 970 4 970 4 970 4 970 4 970 5 990 5 990 5 990 5 990 5 990 5 990 5 990 5 990 5 990 5 990 6 1010 6 1010 6 1010 6 1010 6 1010 6 1010 6 1010 6 1010 6 1010 6 1010 7 1030 7 1030 7 1030 7 1030 7 1030 7 1030 7 1030 7 1030 7 1030 7 1030 8 1050 8 1050 8 1050 8 1050 8 1050 8 1050 8 1050 8 1050 8 1050 8 1050 9 1070 9 1070 9 1070 9 1070 9 1070 9 1070 9 1070 9 1070 9 1070 9 1070}
} {0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550
0 550 1 1010 1 1010 1 1010 1 1010 1 1010 1 1010 1 1010
1 1010 1 1010 1 1010 2 930 2 930 2 930 2 930 2 930 2 930
2 930 2 930 2 930 2 930 3 950 3 950 3 950 3 950 3 950
3 950 3 950 3 950 3 950 3 950 4 970 4 970 4 970 4 970
4 970 4 970 4 970 4 970 4 970 4 970 5 990 5 990 5 990
5 990 5 990 5 990 5 990 5 990 5 990 5 990 6 1010 6 1010
6 1010 6 1010 6 1010 6 1010 6 1010 6 1010 6 1010 6 1010
7 1030 7 1030 7 1030 7 1030 7 1030 7 1030 7 1030 7 1030
7 1030 7 1030 8 1050 8 1050 8 1050 8 1050 8 1050 8 1050
8 1050 8 1050 8 1050 8 1050 9 1070 9 1070 9 1070 9 1070
9 1070 9 1070 9 1070 9 1070 9 1070 9 1070}
finish_test

View File

@@ -197,30 +197,55 @@ execsql_test 4.2.1 {
ORDER BY a RANGE BETWEEN 5 FOLLOWING AND 10 FOLLOWING
) FROM t1 ORDER BY 1 NULLS FIRST;
}
execsql_test 4.2.2 {
SELECT sum(b) OVER (
ORDER BY a RANGE BETWEEN 5 FOLLOWING AND 10 FOLLOWING
) FROM t1 ORDER BY 1 NULLS LAST;
}
execsql_test 4.2.3 {
SELECT sum(b) OVER (
ORDER BY a DESC RANGE BETWEEN 5 FOLLOWING AND 10 FOLLOWING
) FROM t1 ORDER BY 1 NULLS FIRST;
}
execsql_test 4.2.4 {
SELECT sum(b) OVER (
ORDER BY a DESC RANGE BETWEEN 5 FOLLOWING AND 10 FOLLOWING
) FROM t1 ORDER BY 1 NULLS LAST;
}
execsql_test 4.3.1 {
SELECT sum(b) OVER (
ORDER BY a NULLS FIRST RANGE BETWEEN UNBOUNDED PRECEDING AND 10 FOLLOWING
) FROM t1 ORDER BY 1 NULLS FIRST;
}
execsql_test 4.3.2 {
SELECT sum(b) OVER (
ORDER BY a NULLS LAST RANGE BETWEEN UNBOUNDED PRECEDING AND 10 FOLLOWING
) FROM t1 ORDER BY 1 NULLS LAST;
}
execsql_test 4.4.1 {
SELECT sum(b) OVER (
ORDER BY a NULLS FIRST ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
) FROM t1 ORDER BY 1 NULLS FIRST;
}
execsql_test 4.4.2 {
SELECT sum(b) OVER (
ORDER BY a NULLS LAST ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
) FROM t1 ORDER BY 1 NULLS LAST;
}
execsql_test 4.4.3 {
SELECT sum(b) OVER (
ORDER BY a DESC NULLS LAST ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
) FROM t1 ORDER BY 1 NULLS FIRST;
}
execsql_test 4.4.4 {
SELECT sum(b) OVER (
ORDER BY a DESC NULLS LAST ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
) FROM t1 ORDER BY 1 NULLS LAST;
}
==========
@@ -248,6 +273,17 @@ foreach {tn ex} {
6 { ORDER BY c NULLS FIRST RANGE BETWEEN 0 PRECEDING AND 0 FOLLOWING }
7 { ORDER BY c NULLS FIRST, b NULLS FIRST, a NULLS FIRST
ROWS BETWEEN 6 PRECEDING AND UNBOUNDED FOLLOWING }
8 { RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING }
9 { ORDER BY a NULLS LAST
RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING }
10 { PARTITION BY coalesce(a, '')
RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING }
11 { ORDER BY a NULLS LAST GROUPS 6 PRECEDING }
12 { ORDER BY c NULLS LAST RANGE BETWEEN 6 PRECEDING AND 7 FOLLOWING }
13 { ORDER BY c NULLS LAST RANGE BETWEEN 0 PRECEDING AND 0 FOLLOWING }
14 { ORDER BY c NULLS LAST, b NULLS LAST, a NULLS LAST
ROWS BETWEEN 6 PRECEDING AND UNBOUNDED FOLLOWING }
} {
execsql_test 5.$tn.$tn2.1 "
SELECT max(c) OVER win,
@@ -294,6 +330,7 @@ execsql_test 6.2 {
}
finish_test

File diff suppressed because it is too large Load Diff

View File

@@ -210,6 +210,7 @@ static Keyword aKeywordTable[] = {
{ "EXPLAIN", "TK_EXPLAIN", EXPLAIN },
{ "FAIL", "TK_FAIL", CONFLICT|TRIGGER },
{ "FILTER", "TK_FILTER", WINDOWFUNC },
{ "FIRST", "TK_FIRST", ALWAYS },
{ "FOLLOWING", "TK_FOLLOWING", WINDOWFUNC },
{ "FOR", "TK_FOR", TRIGGER },
{ "FOREIGN", "TK_FOREIGN", FKEY },
@@ -235,6 +236,7 @@ static Keyword aKeywordTable[] = {
{ "ISNULL", "TK_ISNULL", ALWAYS },
{ "JOIN", "TK_JOIN", ALWAYS },
{ "KEY", "TK_KEY", ALWAYS },
{ "LAST", "TK_LAST", ALWAYS },
{ "LEFT", "TK_JOIN_KW", ALWAYS },
{ "LIKE", "TK_LIKE_KW", ALWAYS },
{ "LIMIT", "TK_LIMIT", ALWAYS },
@@ -245,6 +247,7 @@ static Keyword aKeywordTable[] = {
{ "NOTHING", "TK_NOTHING", UPSERT },
{ "NOTNULL", "TK_NOTNULL", ALWAYS },
{ "NULL", "TK_NULL", ALWAYS },
{ "NULLS", "TK_NULLS", ALWAYS },
{ "OF", "TK_OF", ALWAYS },
{ "OFFSET", "TK_OFFSET", ALWAYS },
{ "ON", "TK_ON", ALWAYS },