Add support for RIGHT and FULL JOIN.

FossilOrigin-Name: f766dff012af0ea3c28a8ce4db850cd0205729a8283bce1e442992aded7c734b
This commit is contained in:
drh 2022-04-21 19:38:17 +00:00
commit 11140addbb
37 changed files with 9560 additions and 431 deletions

View File

@ -342,7 +342,7 @@ do_eqp_execsql_test 7.2 {
QUERY PLAN
|--SCAN xdir
|--SCAN rt VIRTUAL TABLE INDEX 2:B0D1
`--SCAN ydir
`--SCAN ydir LEFT-JOIN
} {
5 1 2 7 12 14 {}
5 2 2 7 8 12 10

View File

@ -1,5 +1,5 @@
C Fix\sa\sharmless\suninitialized\svariable\swarning\sin\sMSVC.
D 2022-04-21T19:20:38.804
C Add\ssupport\sfor\sRIGHT\sand\sFULL\sJOIN.
D 2022-04-21T19:38:17.167
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -409,7 +409,7 @@ F ext/rtree/rtree8.test 2d99006a1386663978c9e1df167554671e4f711c419175b39f332719
F ext/rtree/rtree9.test fd3c9384ef8aabbc127b3878764070398f136eebc551cd20484b570f2cc1956a
F ext/rtree/rtreeA.test a7fd235d8194115fa2e14d300337931eb2e960fe8a46cdfb66add2206412ea41
F ext/rtree/rtreeB.test 4cec297f8e5c588654bbf3c6ed0903f10612be8a2878055dd25faf8c71758bc9
F ext/rtree/rtreeC.test c4bfa9a61c6788c03e4a9ce40ab2cfc6100982559effd9842d1b658e1d47aa5f
F ext/rtree/rtreeC.test 2978b194d09b13e106bdb0e1c5b408b9d42eb338c1082bf43c87ef43bd626147
F ext/rtree/rtreeD.test fe46aa7f012e137bd58294409b16c0d43976c3bb92c8f710481e577c4a1100dc
F ext/rtree/rtreeE.test e65d3fc625da1800b412fc8785817327d43ccfec5f5973912d8c9e471928caa9
F ext/rtree/rtreeF.test 81ffa7ef51c4e4618d497a57328c265bf576990c7070633b623b23cd450ed331
@ -495,7 +495,7 @@ F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
F src/btree.c df695e953c2ce78ce4878ee5016751df9bc9a921dc0500a0f53ab3bb3196e505
F src/btree.h 74d64b8f28cfa4a894d14d4ed64fa432cd697b98b61708d4351482ae15913e22
F src/btreeInt.h 8ce1332edd89dfd2461d561ac10a0ab5601c8e06200cb5230596c3caaf54482e
F src/build.c ff119be98394a65bc8be7afc39d4a791a66f03a778d396de3ec456f5dfaf39e8
F src/build.c 470be339e458a48456cb317ad207ab3cb68ace8b2d76625d2a7eace660fc9674
F src/callback.c 4c19af69835787bfe790ac560f3071a824eb629f34e41f97b52ce5235c77de1c
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c 026dbdcdbd8c3cde98a88483ee88310ff43150ab164ad768f12cc700a11495ad
@ -503,7 +503,7 @@ F src/date.c 15082566229d4b1e5f24fdb490bf9bcc68824b911d70e3573ef075a1b9e2d26f
F src/dbpage.c 90661a87e1db8bfbc8d2ebbdcd3749651ddb287c555c07a28fb17c7c591ffb68
F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d
F src/delete.c a8e844af211a48b13b5b358be77a12c860c6a557c21990ad51a548e2536500ce
F src/expr.c 1d9a1eefd803c9f9b1cfed9015589157de173e0994b7a73e2d9cf5a267a77449
F src/expr.c 899c77da1cd4d836d93f94cf435fc761695938dda8932becfccfdb2eb7d6a7d9
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c d965ede15d8360c09ed59348940649ee647b192e784466837d7aefa836d1d91e
F src/func.c a3407a6fbb0d4088d8d502e46f0ace63e0aeae7467ae23a9ca9815bbf9239761
@ -512,7 +512,7 @@ F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c 221ae0496a53fcf74321d6dcdd7fa2d3c17ede1c17fa1d9020e2465da4a4505a
F src/insert.c 173845e5a6bac96ae937409e4f876b631f26b31dabb9df8fd0eb3b130b2bb3a7
F src/json.c 7749b98c62f691697c7ee536b570c744c0583cab4a89200fdd0fc2aa8cc8cbd6
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 2ecb1441f9b1c22e9e022ee0776e67d259facf34b56ba892b206f0a294ee6f8c
@ -541,7 +541,7 @@ F src/os_win.c a8ea80037e81127ca01959daa87387cc135f325c88dc745376c4f760de852a10
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 42120492784fc9bcd9082b5c9b5e329b7318c357f9f3574a1bbfcf7418910356
F src/pager.h f82e9844166e1585f5786837ddc7709966138ced17f568c16af7ccf946c2baa3
F src/parse.y 9130a936927658e7c574e97e5d4f0a747a3f6b9a42bc5c63e455c8e40cc74216
F src/parse.y b86d56b446afb9c203d8354dc6c422818a62b4bbab52b76ab3da06d7b1d07e44
F src/pcache.c 084e638432c610f95aea72b8509f0845d2791293f39d1b82f0c0a7e089c3bb6b
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
F src/pcache1.c 54881292a9a5db202b2c0ac541c5e3ef9a5e8c4f1c1383adb2601d5499a60e65
@ -550,14 +550,14 @@ F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7
F src/prepare.c fd940149c691684e7c1073c3787a7170e44852b02d1275d2e30a5b58e89cfcaf
F src/printf.c 05d8dfd2018bc4fc3ddb8b37eb97ccef7abf985643fa1caebdcf2916ca90fa32
F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c
F src/resolve.c 18d99e7146852d6064559561769fcca0743eb32b14a97da6dbed373a30ee0e76
F src/resolve.c f72bb13359dd5a74d440df25f320dc2c1baff5cde4fc9f0d1bc3feba90b8932a
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
F src/select.c 2df3b525acb48c4e005556771164cefca301e32aad14ffc51f5c9480ea04ff38
F src/select.c 51e8b778abdd89289e2f2a6733d5e7ac6082f6c56ca717fa15f64f202e66bf42
F src/shell.c.in ae0a6fae983caac6f8c824733f0599dfdf7b3a7e8efdef3cb5e3ab2e457ffc35
F src/sqlite.h.in 2a35f62185eb5e7ecc64a2f68442b538ce9be74f80f28a00abc24837edcf1c17
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h f49e28c25bd941e79794db5415fdf7b202deb3bc072ed6f1ed273d578703684e
F src/sqliteInt.h 5260c849fd1ac36d763f8efe9438437eb0bbde9a7956287e5e795cc514b30bc3
F src/sqliteInt.h 36b5d1cce15971fa71b53a950de3158197d85dbaf9b8b2f0bc6279347b09606a
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
F src/status.c 4a3da6d77eeb3531cb0dbdf7047772a2a1b99f98c69e90ce009c75fe6328b2c0
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@ -617,18 +617,18 @@ F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c a38f52058b517929e264094abd0b5fd1e8e145a1aa43bc6f6a72ae5218f96c98
F src/treeview.c 67fc7d8960e525026eb7f8f4afae0d3652cc3f1260f5adeab062079e25bf0879
F src/treeview.c c834e0c9030b1f9971152e049f44e2f973625c680f10aed2fbcc956da2301d0b
F src/trigger.c 372ada38f667c6823a3db15749eb668338e65c793394e55a37e56a489f2d1b55
F src/update.c 2cfaded82ca80ff56afb8c3ae5e88284e0824bfd86119827cc22481959f96f92
F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
F src/util.c 602fe229f32a96ceccae4f40824129669582096f7c355f53dbac156c9fecef23
F src/vacuum.c 6c38ddc52f0619865c91dae9c441d4d48bf3040d7dc1bc5b22da1e45547ed0b3
F src/vdbe.c a4d710ef5a5a5be60cbb0ae74bba569d2689b670a73e825276687d89559ab917
F src/vdbe.c 9527ab7f71c5b0291e5ed7727f213f4d7f6e0a82af019da5b365fd5a0f56bb96
F src/vdbe.h 89f5edb1422c8783a0b29db836e409876f2b3e847f78e2b21b1fbcc48a93f85f
F src/vdbeInt.h 5f3d0abcf30c2b7a6672ad4386f18be0fca9c9b2cefe18f85a2e3df74f2613bf
F src/vdbeInt.h ef43f7fdc5fde29fc3fd29c506c12830f366178fdb4edbbf0cbc3dfbd1278b5f
F src/vdbeapi.c 354c893f1500cf524cc45c32879b9c68893a28b77e3442c24668d6afe4236217
F src/vdbeaux.c ac98cd2fbf32fd1dec548d362f69103b64ec391a3926bb64ab2e9cfa875f6b05
F src/vdbeaux.c d1fce1d9c1045622d2a878ddcbec1c431eb186a17674b64ffe231e8d14db0451
F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd
F src/vdbemem.c 7189090b72baa025f945a1ac8c61ee420c645254476e8a191d555db76dfea5d4
F src/vdbesort.c 43756031ca7430f7aec3ef904824a7883c4ede783e51f280d99b9b65c0796e35
@ -639,16 +639,16 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d
F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
F src/where.c 3b23b363455f1d12cf36c7baa679b16bf1e48cfdd6cf0166bd52202823212f14
F src/whereInt.h 41ce0a8c0368372d8422e420e05a1e037624ce52fae139c3c19538ee491fb4c0
F src/wherecode.c b48476855e4802276e9d9aabb407609059220774b586c8c3a5a61e430aa0eb27
F src/whereexpr.c 346ca19fa89bcb966feb9ae42324fe7636130757f34890aaa6ef922b96be17a5
F src/where.c 45102d682f6efd450d89c0c5f09b838be6d69c84c6105336730ee38d3b452fad
F src/whereInt.h eecce79edc6f7005f91f35be6b18b7053f794e1b50e95bcd06a2d537fc176734
F src/wherecode.c 1bff158d8672524d0e9a398b01a8ed2d98fffb9ae38837a8335c7162500a1d8f
F src/whereexpr.c 174d4ad5be165c610c907abb779ef4a97974d22b84e1ce7898d2d9f6947249e5
F src/window.c 924e04fd6e0e113d4dba18b78d43fcb8e42b8ebffc8fc728da52cf3ab014cf3c
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
F test/affinity3.test eecb0dabee4b7765a8465439d5e99429279ffba23ca74a7eae270a452799f9e7
F test/affinity3.test b5c19d504dec222c0dc66642673d23dce915d35737b68e74d9f237b80493eb53
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/aggnested.test cc47afa5e11e0d6771a85a4993fa6ff721480ddb53ea538ec3fdbafb720bd505
F test/aggnested.test 7269d07ac879fce161cb26c8fabe65cba5715742fac8a1fccac570dcdaf28f00
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
F test/all.test 2ecb8bbd52416642e41c9081182a8df05d42c75637afd4488aace78cc4b69e13
F test/alter.test 313073774ab5c3f2ef1d3f0d03757c9d3a81284ae7e1b4a6ca34db088f886896
@ -704,10 +704,10 @@ F test/auth2.test 9eb7fce9f34bf1f50d3f366fb3e606be5a2000a1
F test/auth3.test 76d20a7fa136d63bcfcf8bcb65c0b1455ed71078d81f22bcd0550d3eb18594ab
F test/autoanalyze1.test b9cc3f32a990fa56669b668d237c6d53e983554ae80c0604992e18869a0b2dec
F test/autoinc.test 997d6f185f138229dc4251583a1d04816423dddc2fc034871a01aeb1d728cb39
F test/autoindex1.test fe27af92eaf884bd9c38f94be3e8afa04ec494e5eefb189902026181a6175f5e
F test/autoindex1.test 523b26034dc5e0c5ff0865055b4593f75863b82f17748dec9ca64bb8b267c502
F test/autoindex2.test 12ef578928102baaa0dc23ad397601a2f4ecb0df
F test/autoindex3.test 2d13958a5617e987624a428d7aed91bf51f322b49b476e3573fadec697ce6da5
F test/autoindex4.test 75cb1191a552b8201351f5a50d160fcb9387a0fbbfb820c77798bfee7da3f8cf
F test/autoindex4.test 5df39313526b6f22a26bd119bbd97ca69f28386ab3c671fc10568d921c41eb08
F test/autoindex5.test 2ee94f033b87ca0160e08d81034c507aff8e230df2627f0304fa309b2fee19a3
F test/autovacuum.test 00671369bbf96c6a49989a9425f5b78b94075d6a4b031e5e00000c2c32f365df
F test/autovacuum2.test 76f7eb4fe6a6bf6d33a196a7141dba98886d2fb53a268d7feca285d5da4759d7
@ -752,7 +752,7 @@ F test/boundary3.tcl 23361e108a125dca9c4080c2feb884fe54d69243
F test/boundary3.test 56ef82096b4329aca2be74fa1e2b0f762ea0eb45
F test/boundary4.tcl 0bb4b1a94f4fc5ae59b79b9a2b7a140c405e2983
F test/boundary4.test 89e02fa66397b8a325d5eb102b5806f961f8ec4b
F test/btree01.test 8e1ba2f857608ad8fbf9fcc11f33b15b083711162f9566b0a21fb573f2008593
F test/btree01.test fef17d9e999ac4f04095948e3438fbe674f4e07bb2c63bb1cad41d87baee077f
F test/btree02.test 7555a5440453d900410160a52554fe6478af4faf53098f7235f1f443d5a1d6cc
F test/btreefault.test c2bcb542685eea44621275cfedbd8a13f65201e3
F test/busy.test 510dc6daaad18bcbbc085bcc6217d6dc418def5e73f72ce1475eea0cb7834727
@ -777,7 +777,7 @@ F test/close.test eccbad8ecd611d974cbf47278c3d4e5874faf02d811338d5d348af42d56d64
F test/closure01.test 9905883f1b171a4638f98fc764879f154e214a306d3d8daf412a15e7f3a9b1e0
F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91
F test/collate1.test 71a6f27fdc93a92f14d8ab80c05e1937656a5a03197e1a10157314554d630ce8
F test/collate2.test 9aaa410a00734e48bcb27f3872617d6f69b2a621
F test/collate2.test 471c6f74573382b89b0f8b88a05256faa52f7964f9e4799e76708a3b1ece6ba4
F test/collate3.test 89defc49983ddfbf0a0555aca8c0521a676f56a5
F test/collate4.test c953715fb498b87163e3e73dd94356bff1f317bd
F test/collate5.test 65d928034d30d2d263a80f6359f7549ee1598ec6
@ -1008,7 +1008,7 @@ F test/fts3fault.test 798e45af84be7978ca33d5bdc94246eb44724db24174b5d8e9b1ac46c5
F test/fts3fault2.test 6a17a11d8034b1c4eca9f3091649273d56c49ff049e2173df8060f94341e9da0
F test/fts3first.test dbdedd20914c8d539aa3206c9b34a23775644641
F test/fts3fuzz001.test e3c7b0ce9b04cc02281dcc96812a277f02df03cd7dc082055d87e11eb18aaf56
F test/fts3join.test b285c919559af5b093c51abb2c07ce7ec0156dbc9573f444b78dabd9f3040db2
F test/fts3join.test ee25def5e763ea8879c19e74f862d5191410ccc7259338887a3685e97f512662
F test/fts3malloc.test b0e4c133b8d61d4f6d112d8110f8320e9e453ef6
F test/fts3matchinfo.test aa66cc50615578b30f6df9984819ae5b702511cf8a94251ec7c594096a703a4a
F test/fts3matchinfo2.test 00144e841704b8debfcdf6097969cd9f2a1cf759e2203cda42583648f2e6bf58
@ -1110,7 +1110,7 @@ F test/index2.test f835d5e13ca163bd78c4459ca15fd2e4ed487407
F test/index3.test 51685f39345462b84fcf77eb8537af847fdf438cc96b05c45d6aaca4e473ade0
F test/index4.test ab92e736d5946840236cd61ac3191f91a7856bf6
F test/index5.test 8621491915800ec274609e42e02a97d67e9b13e7
F test/index6.test 5a7ab531c692ff3b3d139ef8ea6709fab40f9c6862ed418b4976752a5481da3a
F test/index6.test 6e5b6943f6a97a34898e48c4d0d4990caf55c12c00465a43a9c33d2fd9a3a820
F test/index7.test b238344318e0b4e42126717f6554f0e7dfd0b39cecad4b736039b43e1e3b6eb3
F test/index8.test caa097735c91dbc23d8a402f5e63a2a03c83840ba3928733ed7f9a03f8a912a3
F test/index9.test 0aa3e509dddf81f93380396e40e9bb386904c1054924ba8fa9bcdfe85a8e7721
@ -1140,12 +1140,17 @@ F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c
F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4
F test/ioerr6.test a395a6ab144b26a9e3e21059a1ab6a7149cca65b
F test/istrue.test e7f285bb70282625c258e866ce6337d4c762922f5a300e1b50f958aef6e7d9c9
F test/join.test 85e9c88bf4700b45a48a6362cd47e0c0aefc572629827c31aa58a5978cabdfc5
F test/join2.test 9bdc615841b91c97a16d68bad9508aea11fa0c6b34e5689847bcc4dac70e4990
F test/join.test e5f165dfd84fd46406ddae6614b0122c3bfa23a26ef62966442e1503c40d96aa
F test/join2.test 466b07233820f5deee66a6c3bf6e4500c8bbf7b83649e67606f5f649c07928c0
F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
F test/join5.test 0d63c7e43b3160b9d4b93f196ef83b6efc7751b9edd0d18c53a46fbec7a49cfc
F test/join5.test c4df54e2e204d7f1417bfbdd21ca324b4b07415c647595cc47798eacfddc96d3
F test/join6.test f809c025fa253f9e150c0e9afd4cef8813257bceeb6f46e04041228c9403cc2c
F test/join7.test 8e72de4b45e5e930d18c305c7efe86015fb2552731e4e03ea226353036b0dab0
F test/join8.test f9b4e3be35d8546e934ea9e18244d3a26d6e22c51dadef9d721db7cd25c606b5
F test/join9.test 9056ddd3b0c0f4f9d658f4521038d9a37dc23ead8ca9a505d0b0db2b6a471e05
F test/joinA.test 7eab225dc1c1ab258a5e62513a4ed7cabbd3db971d59d5d92f4fb6fa14c12f6a
F test/joinB.test 1b2ba3fc8568b49411787fccbf540570c148e9b6a53a30f80691cb6268098ded
F test/journal1.test c7b768041b7f494471531e17abc2f4f5ebf9e5096984f43ed17c4eb80ba34497
F test/journal2.test 9dac6b4ba0ca79c3b21446bbae993a462c2397c4
F test/journal3.test 7c3cf23ffc77db06601c1fcfc9743de8441cb77db9d1aa931863d94f5ffa140e
@ -1711,7 +1716,7 @@ F test/vtab2.test 14d4ab26cee13ba6cf5c5601b158e4f57552d3b055cdd9406cf7f711e9c840
F test/vtab3.test b45f47d20f225ccc9c28dc915d92740c2dee311e
F test/vtab4.test 8e73ed268f3d596bc3590f45fc948fb40f28e9c3
F test/vtab5.test 889f444970393c73f1e077e2bdc5d845e157a391
F test/vtab6.test 7167e8e526bc2e719e7818e18b2fd7bb8c455fa018b74e611943a86782e10125
F test/vtab6.test fa609a4af96da30beceefa3cb624abe9be38c4747ab373d98179b24027d6b798
F test/vtab7.test 70c6f4a1d6177144a8236e4172d5fba92e683440374664ad1f04851fbb335d3c
F test/vtab8.test e19fa4a538fcd1bb66c22825fa8f71618fb13583
F test/vtab9.test ea58d2b95d61955f87226381716b2d0b1d4e4f9b
@ -1769,7 +1774,7 @@ F test/walthread.test 14b20fcfa6ae152f5d8e12f5dc8a8a724b7ef189f5d8ef1e2ceab79f2a
F test/walvfs.test bccb3e0d235ef85e276f491d34db32c9ada1ea67be8d9f10aabe7b30319ec656
F test/wapp.tcl b440cd8cf57953d3a49e7ee81e6a18f18efdaf113b69f7d8482b0710a64566ec
F test/wapptest.tcl 899594e25684861d5b0c0880fb012364def50ef8097041b8ddf74be5ba7fa270 x
F test/where.test f114842c1851d257a26770f2ad55119b084001c0e1b8c214f886f45152d37cd8
F test/where.test 8c6bbd0cae8feae142a7946e3484a802fa566bacf38452b1c3e48cb77321f9a4
F test/where2.test 03c21a11e7b90e2845fc3c8b4002fc44cc2797fa74c86ee47d70bd7ea4f29ed6
F test/where3.test 5b4ffc0ac2ea0fe92f02b1244b7531522fe4d7bccf6fa8741d54e82c10e67753
F test/where4.test 4a371bfcc607f41d233701bdec33ac2972908ba8
@ -1777,7 +1782,7 @@ F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
F test/where7.test 1c1bf436bf31b913d4764a2b62ac6e98b9681e5c7ae2b562605592a56b7e946b
F test/where8.test 461ca40265ed996a6305da99bb024b0e41602bb586acf544c08f95922358e49f
F test/where9.test 1ffb75edc50a8faa6e7bd77f8221d783febb00b44b0bdb32fb48cec6e38eca95
F test/where9.test 2db942671a002621eff4f713e347bb25243295f79d8990297cd160bebcfde3f7
F test/whereA.test 9d1077b117f1b68d5f739d94f36956c36cf995eb87bb19b77b2e81af020edd20
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
F test/whereC.test cae295158703cb3fc23bf1a108a9ab730efff0f6
@ -1945,8 +1950,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 dfd2100bc4f316825fd199b347849d1a2b941837f9eedcf36f3c3d280692b991
R b0caf88c4ea0c8c655e6322050b10cd7
P b870d2a2fa54ddab6ffc9b9d95f52420a5a6831aeb73dac75355ca1ab15a190e 29255664127a975e5b0d5767cd4e56d93ff2ea1994702cdfbdca1b700952502e
R aaacbf77275f81b4612909a20841ea0d
U drh
Z 411498f228eb3c02198e993caa11ad7e
Z 33a15e5f0b37eabbe920c3340c278cbb
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
b870d2a2fa54ddab6ffc9b9d95f52420a5a6831aeb73dac75355ca1ab15a190e
f766dff012af0ea3c28a8ce4db850cd0205729a8283bce1e442992aded7c734b

View File

@ -4672,18 +4672,17 @@ IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){
if( pList==0 ){
pList = sqlite3DbMallocZero(db, sizeof(IdList) );
if( pList==0 ) return 0;
}else{
IdList *pNew;
pNew = sqlite3DbRealloc(db, pList,
sizeof(IdList) + pList->nId*sizeof(pList->a));
if( pNew==0 ){
sqlite3IdListDelete(db, pList);
return 0;
}
pList = pNew;
}
pList->a = sqlite3ArrayAllocate(
db,
pList->a,
sizeof(pList->a[0]),
&pList->nId,
&i
);
if( i<0 ){
sqlite3IdListDelete(db, pList);
return 0;
}
i = pList->nId++;
pList->a[i].zName = sqlite3NameFromToken(db, pToken);
if( IN_RENAME_OBJECT && pList->a[i].zName ){
sqlite3RenameTokenMap(pParse, (void*)pList->a[i].zName, pToken);
@ -4697,10 +4696,10 @@ IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){
void sqlite3IdListDelete(sqlite3 *db, IdList *pList){
int i;
if( pList==0 ) return;
assert( pList->eU4!=EU4_EXPR ); /* EU4_EXPR mode is not currently used */
for(i=0; i<pList->nId; i++){
sqlite3DbFree(db, pList->a[i].zName);
}
sqlite3DbFree(db, pList->a);
sqlite3DbFreeNN(db, pList);
}
@ -4971,7 +4970,12 @@ SrcList *sqlite3SrcListAppendFromTerm(
if( pAlias->n ){
pItem->zAlias = sqlite3NameFromToken(db, pAlias);
}
pItem->pSelect = pSubquery;
if( pSubquery ){
pItem->pSelect = pSubquery;
if( pSubquery->selFlags & SF_NestedFrom ){
pItem->fg.isNestedFrom = 1;
}
}
assert( pOnUsing==0 || pOnUsing->pOn==0 || pOnUsing->pUsing==0 );
assert( pItem->fg.isUsing==0 );
if( pOnUsing==0 ){
@ -5031,6 +5035,7 @@ SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2){
p1 = pNew;
memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(SrcItem));
sqlite3DbFree(pParse->db, p2);
p1->a[0].fg.jointype |= (JT_LTORJ & p1->a[1].fg.jointype);
}
}
return p1;
@ -5067,14 +5072,34 @@ void sqlite3SrcListFuncArgs(Parse *pParse, SrcList *p, ExprList *pList){
** The operator is "natural cross join". The A and B operands are stored
** in p->a[0] and p->a[1], respectively. The parser initially stores the
** operator with A. This routine shifts that operator over to B.
**
** Additional changes:
**
** * All tables to the left of the right-most RIGHT JOIN are tagged with
** JT_LTORJ (mnemonic: Left Table Of Right Join) so that the
** code generator can easily tell that the table is part of
** the left operand of at least one RIGHT JOIN.
*/
void sqlite3SrcListShiftJoinType(SrcList *p){
if( p ){
int i;
for(i=p->nSrc-1; i>0; i--){
p->a[i].fg.jointype = p->a[i-1].fg.jointype;
}
void sqlite3SrcListShiftJoinType(Parse *pParse, SrcList *p){
(void)pParse;
if( p && p->nSrc>1 ){
int i = p->nSrc-1;
u8 allFlags = 0;
do{
allFlags |= p->a[i].fg.jointype = p->a[i-1].fg.jointype;
}while( (--i)>0 );
p->a[0].fg.jointype = 0;
/* All terms to the left of a RIGHT JOIN should be tagged with the
** JT_LTORJ flags */
if( allFlags & JT_RIGHT ){
for(i=p->nSrc-1; ALWAYS(i>0) && (p->a[i].fg.jointype&JT_RIGHT)==0; i--){}
i--;
assert( i>=0 );
do{
p->a[i].fg.jointype |= JT_LTORJ;
}while( (--i)>=0 );
}
}
}

View File

@ -1631,6 +1631,7 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, const ExprList *p, int flags){
pItem->eEName = pOldItem->eEName;
pItem->done = 0;
pItem->bNulls = pOldItem->bNulls;
pItem->bUsed = pOldItem->bUsed;
pItem->bSorterRef = pOldItem->bSorterRef;
pItem->u = pOldItem->u;
}
@ -1698,22 +1699,16 @@ IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){
int i;
assert( db!=0 );
if( p==0 ) return 0;
pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew) );
assert( p->eU4!=EU4_EXPR );
pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew)+(p->nId-1)*sizeof(p->a[0]) );
if( pNew==0 ) return 0;
pNew->nId = p->nId;
pNew->a = sqlite3DbMallocRawNN(db, p->nId*sizeof(p->a[0]) );
if( pNew->a==0 ){
sqlite3DbFreeNN(db, pNew);
return 0;
}
/* Note that because the size of the allocation for p->a[] is not
** necessarily a power of two, sqlite3IdListAppend() may not be called
** on the duplicate created by this function. */
pNew->eU4 = p->eU4;
for(i=0; i<p->nId; i++){
struct IdList_item *pNewItem = &pNew->a[i];
struct IdList_item *pOldItem = &p->a[i];
const struct IdList_item *pOldItem = &p->a[i];
pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
pNewItem->idx = pOldItem->idx;
pNewItem->u4 = pOldItem->u4;
}
return pNew;
}
@ -2179,7 +2174,7 @@ Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){
static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
/* If pWalker->eCode is 2 then any term of the expression that comes from
** the ON or USING clauses of a left join disqualifies the expression
** the ON or USING clauses of an outer join disqualifies the expression
** from being considered constant. */
if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_FromJoin) ){
pWalker->eCode = 0;
@ -3181,9 +3176,9 @@ void sqlite3CodeRhsOfIN(
assert( ExprUseYSub(pExpr) );
assert( sqlite3VdbeGetOp(v,pExpr->y.sub.iAddr-1)->opcode==OP_BeginSubrtn
|| pParse->nErr );
sqlite3VdbeAddOp2(v, OP_Return, pExpr->y.sub.regReturn,
pExpr->y.sub.iAddr);
sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, sqlite3VdbeCurrentAddr(v)-1);
sqlite3VdbeAddOp3(v, OP_Return, pExpr->y.sub.regReturn,
pExpr->y.sub.iAddr, 1);
VdbeCoverage(v);
sqlite3ClearTempRegCache(pParse);
}
}
@ -3312,9 +3307,9 @@ int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
assert( ExprUseYSub(pExpr) );
assert( sqlite3VdbeGetOp(v,pExpr->y.sub.iAddr-1)->opcode==OP_BeginSubrtn
|| pParse->nErr );
sqlite3VdbeAddOp2(v, OP_Return, pExpr->y.sub.regReturn,
pExpr->y.sub.iAddr);
sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, sqlite3VdbeCurrentAddr(v)-1);
sqlite3VdbeAddOp3(v, OP_Return, pExpr->y.sub.regReturn,
pExpr->y.sub.iAddr, 1);
VdbeCoverage(v);
sqlite3ClearTempRegCache(pParse);
return rReg;
}
@ -4501,16 +4496,18 @@ expr_code_doover:
}
case TK_SELECT_COLUMN: {
int n;
if( pExpr->pLeft->iTable==0 ){
pExpr->pLeft->iTable = sqlite3CodeSubselect(pParse, pExpr->pLeft);
Expr *pLeft = pExpr->pLeft;
if( pLeft->iTable==0 || pParse->withinRJSubrtn > pLeft->op2 ){
pLeft->iTable = sqlite3CodeSubselect(pParse, pLeft);
pLeft->op2 = pParse->withinRJSubrtn;
}
assert( pExpr->pLeft->op==TK_SELECT || pExpr->pLeft->op==TK_ERROR );
n = sqlite3ExprVectorSize(pExpr->pLeft);
assert( pLeft->op==TK_SELECT || pLeft->op==TK_ERROR );
n = sqlite3ExprVectorSize(pLeft);
if( pExpr->iTable!=n ){
sqlite3ErrorMsg(pParse, "%d columns assigned %d values",
pExpr->iTable, n);
}
return pExpr->pLeft->iTable + pExpr->iColumn;
return pLeft->iTable + pExpr->iColumn;
}
case TK_IN: {
int destIfFalse = sqlite3VdbeMakeLabel(pParse);
@ -5818,7 +5815,7 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
** in an incorrect answer.
**
** Terms of p that are marked with EP_FromJoin (and hence that come from
** the ON or USING clauses of LEFT JOINS) are excluded from the analysis.
** the ON or USING clauses of OUTER JOINS) are excluded from the analysis.
**
** This routine is used to check if a LEFT JOIN can be converted into
** an ordinary JOIN. The p argument is the WHERE clause. If the WHERE

View File

@ -851,13 +851,15 @@ void sqlite3Insert(
*/
bIdListInOrder = (pTab->tabFlags & (TF_OOOHidden|TF_HasStored))==0;
if( pColumn ){
assert( pColumn->eU4!=EU4_EXPR );
pColumn->eU4 = EU4_IDX;
for(i=0; i<pColumn->nId; i++){
pColumn->a[i].idx = -1;
pColumn->a[i].u4.idx = -1;
}
for(i=0; i<pColumn->nId; i++){
for(j=0; j<pTab->nCol; j++){
if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zCnName)==0 ){
pColumn->a[i].idx = j;
pColumn->a[i].u4.idx = j;
if( i!=j ) bIdListInOrder = 0;
if( j==pTab->iPKey ){
ipkColumn = i; assert( !withoutRowid );
@ -1159,7 +1161,8 @@ void sqlite3Insert(
}
}
if( pColumn ){
for(j=0; j<pColumn->nId && pColumn->a[j].idx!=i; j++){}
assert( pColumn->eU4==EU4_IDX );
for(j=0; j<pColumn->nId && pColumn->a[j].u4.idx!=i; j++){}
if( j>=pColumn->nId ){
/* A column not named in the insert column list gets its
** default value */

View File

@ -685,7 +685,7 @@ as(X) ::= . {X.n = 0; X.z = 0;}
from(A) ::= . {A = 0;}
from(A) ::= FROM seltablist(X). {
A = X;
sqlite3SrcListShiftJoinType(A);
sqlite3SrcListShiftJoinType(pParse,A);
}
// "seltablist" is a "Select Table List" - the content of the FROM clause
@ -721,6 +721,9 @@ seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) LP exprlist(E) RP as(Z) on_using(N
pNew->zName = pOld->zName;
pNew->zDatabase = pOld->zDatabase;
pNew->pSelect = pOld->pSelect;
if( pNew->pSelect && (pNew->pSelect->selFlags & SF_NestedFrom)!=0 ){
pNew->fg.isNestedFrom = 1;
}
if( pOld->fg.isTabFunc ){
pNew->u1.pFuncArg = pOld->u1.pFuncArg;
pOld->u1.pFuncArg = 0;
@ -733,7 +736,7 @@ seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) LP exprlist(E) RP as(Z) on_using(N
sqlite3SrcListDelete(pParse->db, F);
}else{
Select *pSubquery;
sqlite3SrcListShiftJoinType(F);
sqlite3SrcListShiftJoinType(pParse,F);
pSubquery = sqlite3SelectNew(pParse,0,F,0,0,0,0,SF_NestedFrom,0);
A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,pSubquery,&N);
}

View File

@ -115,22 +115,6 @@ static void resolveAlias(
}
}
/*
** Return TRUE if the name zCol occurs anywhere in the USING clause.
**
** Return FALSE if the USING clause is NULL or if it does not contain
** zCol.
*/
static int nameInUsingClause(IdList *pUsing, const char *zCol){
int k;
assert( pUsing!=0 );
for(k=0; k<pUsing->nId; k++){
if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ) return 1;
}
return 0;
}
/*
** Subqueries stores the original database, table and column names for their
** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN".
@ -207,6 +191,29 @@ Bitmask sqlite3ExprColUsed(Expr *pExpr){
}
}
/*
** Create a new expression term for the column specified by pMatch and
** iColumn. Append this new expression term to the FULL JOIN Match set
** in *ppList. Create a new *ppList if this is the first term in the
** set.
*/
static void extendFJMatch(
Parse *pParse, /* Parsing context */
ExprList **ppList, /* ExprList to extend */
SrcItem *pMatch, /* Source table containing the column */
i16 iColumn /* The column number */
){
Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLUMN, 0, 0);
if( pNew ){
pNew->iTable = pMatch->iCursor;
pNew->iColumn = iColumn;
pNew->y.pTab = pMatch->pTab;
assert( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 );
ExprSetProperty(pNew, EP_CanBeNull);
*ppList = sqlite3ExprListAppend(pParse, *ppList, pNew);
}
}
/*
** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
** that name in the set of source tables in pSrcList and make the pExpr
@ -252,11 +259,13 @@ static int lookupName(
NameContext *pTopNC = pNC; /* First namecontext in the list */
Schema *pSchema = 0; /* Schema of the expression */
int eNewExprOp = TK_COLUMN; /* New value for pExpr->op on success */
Table *pTab = 0; /* Table hold the row */
Table *pTab = 0; /* Table holding the row */
Column *pCol; /* A column of pTab */
ExprList *pFJMatch = 0; /* Matches for FULL JOIN .. USING */
assert( pNC ); /* the name context cannot be NULL. */
assert( zCol ); /* The Z in X.Y.Z cannot be NULL */
assert( zDb==0 || zTab!=0 );
assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
/* Initialize the node to no-match */
@ -305,26 +314,64 @@ static int lookupName(
pTab = pItem->pTab;
assert( pTab!=0 && pTab->zName!=0 );
assert( pTab->nCol>0 || pParse->nErr );
if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){
assert( pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) );
if( pItem->fg.isNestedFrom ){
/* In this case, pItem is a subquery that has been formed from a
** parenthesized subset of the FROM clause terms. Example:
** .... FROM t1 LEFT JOIN (t2 RIGHT JOIN t3 USING(x)) USING(y) ...
** \_________________________/
** This pItem -------------^
*/
int hit = 0;
assert( pItem->pSelect!=0 );
pEList = pItem->pSelect->pEList;
assert( pEList!=0 );
assert( pEList->nExpr==pTab->nCol );
for(j=0; j<pEList->nExpr; j++){
if( sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb) ){
cnt++;
cntTab = 2;
pMatch = pItem;
pExpr->iColumn = j;
hit = 1;
if( !sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb) ){
continue;
}
if( cnt>0 ){
if( pItem->fg.isUsing==0
|| sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0
){
/* Two or more tables have the same column name which is
** not joined by USING. This is an error. Signal as much
** by clearing pFJMatch and letting cnt go above 1. */
sqlite3ExprListDelete(db, pFJMatch);
pFJMatch = 0;
}else
if( (pItem->fg.jointype & JT_RIGHT)==0 ){
/* An INNER or LEFT JOIN. Use the left-most table */
continue;
}else
if( (pItem->fg.jointype & JT_LEFT)==0 ){
/* A RIGHT JOIN. Use the right-most table */
cnt = 0;
sqlite3ExprListDelete(db, pFJMatch);
pFJMatch = 0;
}else{
/* For a FULL JOIN, we must construct a coalesce() func */
extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn);
}
}
cnt++;
cntTab = 2;
pMatch = pItem;
pExpr->iColumn = j;
pEList->a[j].bUsed = 1;
hit = 1;
}
if( hit || zTab==0 ) continue;
}
if( zDb ){
if( pTab->pSchema!=pSchema ) continue;
if( pSchema==0 && strcmp(zDb,"*")!=0 ) continue;
}
assert( zDb==0 || zTab!=0 );
if( zTab ){
const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName;
const char *zTabName;
if( zDb ){
if( pTab->pSchema!=pSchema ) continue;
if( pSchema==0 && strcmp(zDb,"*")!=0 ) continue;
}
zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName;
assert( zTabName!=0 );
if( sqlite3StrICmp(zTabName, zTab)!=0 ){
continue;
@ -339,22 +386,37 @@ static int lookupName(
if( pCol->hName==hCol
&& sqlite3StrICmp(pCol->zCnName, zCol)==0
){
/* If there has been exactly one prior match and this match
** is for the right-hand table of a NATURAL JOIN or is in a
** USING clause, then skip this match.
*/
if( cnt==1 ){
if( pItem->fg.jointype & JT_NATURAL ) continue;
if( pItem->fg.isUsing
&& nameInUsingClause(pItem->u3.pUsing, zCol)
if( cnt>0 ){
if( pItem->fg.isUsing==0
|| sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0
){
/* Two or more tables have the same column name which is
** not joined by USING. This is an error. Signal as much
** by clearing pFJMatch and letting cnt go above 1. */
sqlite3ExprListDelete(db, pFJMatch);
pFJMatch = 0;
}else
if( (pItem->fg.jointype & JT_RIGHT)==0 ){
/* An INNER or LEFT JOIN. Use the left-most table */
continue;
}else
if( (pItem->fg.jointype & JT_LEFT)==0 ){
/* A RIGHT JOIN. Use the right-most table */
cnt = 0;
sqlite3ExprListDelete(db, pFJMatch);
pFJMatch = 0;
}else{
/* For a FULL JOIN, we must construct a coalesce() func */
extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn);
}
}
cnt++;
pMatch = pItem;
/* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j;
if( pItem->fg.isNestedFrom ){
sqlite3SrcItemColumnUsed(pItem, j);
}
break;
}
}
@ -367,9 +429,7 @@ static int lookupName(
pExpr->iTable = pMatch->iCursor;
assert( ExprUseYTab(pExpr) );
pExpr->y.pTab = pMatch->pTab;
/* RIGHT JOIN not (yet) supported */
assert( (pMatch->fg.jointype & JT_RIGHT)==0 );
if( (pMatch->fg.jointype & JT_LEFT)!=0 ){
if( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ){
ExprSetProperty(pExpr, EP_CanBeNull);
}
pSchema = pExpr->y.pTab->pSchema;
@ -610,11 +670,37 @@ static int lookupName(
}
/*
** cnt==0 means there was not match. cnt>1 means there were two or
** more matches. Either way, we have an error.
** cnt==0 means there was not match.
** cnt>1 means there were two or more matches.
**
** cnt==0 is always an error. cnt>1 is often an error, but might
** be multiple matches for a NATURAL LEFT JOIN or a LEFT JOIN USING.
*/
assert( pFJMatch==0 || cnt>0 );
assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) );
if( cnt!=1 ){
const char *zErr;
if( pFJMatch ){
if( pFJMatch->nExpr==cnt-1 ){
if( ExprHasProperty(pExpr,EP_Leaf) ){
ExprClearProperty(pExpr,EP_Leaf);
}else{
sqlite3ExprDelete(db, pExpr->pLeft);
pExpr->pLeft = 0;
sqlite3ExprDelete(db, pExpr->pRight);
pExpr->pRight = 0;
}
extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn);
pExpr->op = TK_FUNCTION;
pExpr->u.zToken = "coalesce";
pExpr->x.pList = pFJMatch;
cnt = 1;
goto lookupname_end;
}else{
sqlite3ExprListDelete(db, pFJMatch);
pFJMatch = 0;
}
}
zErr = cnt==0 ? "no such column" : "ambiguous column name";
if( zDb ){
sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol);
@ -627,6 +713,16 @@ static int lookupName(
pParse->checkSchema = 1;
pTopNC->nNcErr++;
}
assert( pFJMatch==0 );
/* Remove all substructure from pExpr */
if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){
sqlite3ExprDelete(db, pExpr->pLeft);
pExpr->pLeft = 0;
sqlite3ExprDelete(db, pExpr->pRight);
pExpr->pRight = 0;
ExprSetProperty(pExpr, EP_Leaf);
}
/* If a column from a table in pSrcList is referenced, then record
** this fact in the pSrcList.a[].colUsed bitmask. Column 0 causes
@ -646,16 +742,7 @@ static int lookupName(
pMatch->colUsed |= sqlite3ExprColUsed(pExpr);
}
/* Clean up and return
*/
if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){
sqlite3ExprDelete(db, pExpr->pLeft);
pExpr->pLeft = 0;
sqlite3ExprDelete(db, pExpr->pRight);
pExpr->pRight = 0;
}
pExpr->op = eNewExprOp;
ExprSetProperty(pExpr, EP_Leaf);
lookupname_end:
if( cnt==1 ){
assert( pNC!=0 );

View File

@ -298,13 +298,9 @@ int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){
const char *zSp2 = " ";
if( pB==0 ){ zSp1++; }
if( pC==0 ){ zSp2++; }
sqlite3ErrorMsg(pParse, "unknown or unsupported join type: "
sqlite3ErrorMsg(pParse, "unknown join type: "
"%T%s%T%s%T", pA, zSp1, pB, zSp2, pC);
jointype = JT_INNER;
}else if( (jointype & JT_RIGHT)!=0 ){
sqlite3ErrorMsg(pParse,
"RIGHT and FULL OUTER JOINs are not currently supported");
jointype = JT_INNER;
}
return jointype;
}
@ -324,8 +320,25 @@ int sqlite3ColumnIndex(Table *pTab, const char *zCol){
}
/*
** Search the first N tables in pSrc, from left to right, looking for a
** table that has a column named zCol.
** Mark a subquery result column as having been used.
*/
void sqlite3SrcItemColumnUsed(SrcItem *pItem, int iCol){
assert( pItem!=0 );
assert( pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) );
if( pItem->fg.isNestedFrom ){
ExprList *pResults;
assert( pItem->pSelect!=0 );
pResults = pItem->pSelect->pEList;
assert( pResults!=0 );
assert( iCol>=0 && iCol<pResults->nExpr );
pResults->a[iCol].bUsed = 1;
}
}
/*
** Search the tables iStart..iEnd (inclusive) in pSrc, looking for a
** table that has a column named zCol. The search is left-to-right.
** The first match found is returned.
**
** When found, set *piTab and *piCol to the table index and column index
** of the matching column and return TRUE.
@ -334,22 +347,27 @@ int sqlite3ColumnIndex(Table *pTab, const char *zCol){
*/
static int tableAndColumnIndex(
SrcList *pSrc, /* Array of tables to search */
int N, /* Number of tables in pSrc->a[] to search */
int iStart, /* First member of pSrc->a[] to check */
int iEnd, /* Last member of pSrc->a[] to check */
const char *zCol, /* Name of the column we are looking for */
int *piTab, /* Write index of pSrc->a[] here */
int *piCol, /* Write index of pSrc->a[*piTab].pTab->aCol[] here */
int bIgnoreHidden /* True to ignore hidden columns */
int bIgnoreHidden /* Ignore hidden columns */
){
int i; /* For looping over tables in pSrc */
int iCol; /* Index of column matching zCol */
assert( iEnd<pSrc->nSrc );
assert( iStart>=0 );
assert( (piTab==0)==(piCol==0) ); /* Both or neither are NULL */
for(i=0; i<N; i++){
for(i=iStart; i<=iEnd; i++){
iCol = sqlite3ColumnIndex(pSrc->a[i].pTab, zCol);
if( iCol>=0
&& (bIgnoreHidden==0 || IsHiddenColumn(&pSrc->a[i].pTab->aCol[iCol])==0)
){
if( piTab ){
sqlite3SrcItemColumnUsed(&pSrc->a[i], iCol);
*piTab = i;
*piCol = iCol;
}
@ -359,60 +377,13 @@ static int tableAndColumnIndex(
return 0;
}
/*
** This function is used to add terms implied by JOIN syntax to the
** WHERE clause expression of a SELECT statement. The new term, which
** is ANDed with the existing WHERE clause, is of the form:
**
** (tab1.col1 = tab2.col2)
**
** where tab1 is the iSrc'th table in SrcList pSrc and tab2 is the
** (iSrc+1)'th. Column col1 is column iColLeft of tab1, and col2 is
** column iColRight of tab2.
*/
static void addWhereTerm(
Parse *pParse, /* Parsing context */
SrcList *pSrc, /* List of tables in FROM clause */
int iLeft, /* Index of first table to join in pSrc */
int iColLeft, /* Index of column in first table */
int iRight, /* Index of second table in pSrc */
int iColRight, /* Index of column in second table */
int isOuterJoin, /* True if this is an OUTER join */
Expr **ppWhere /* IN/OUT: The WHERE clause to add to */
){
sqlite3 *db = pParse->db;
Expr *pE1;
Expr *pE2;
Expr *pEq;
assert( iLeft<iRight );
assert( pSrc->nSrc>iRight );
assert( pSrc->a[iLeft].pTab );
assert( pSrc->a[iRight].pTab );
pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iColLeft);
pE2 = sqlite3CreateColumnExpr(db, pSrc, iRight, iColRight);
pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2);
assert( pE2!=0 || pEq==0 ); /* Due to db->mallocFailed test
** in sqlite3DbMallocRawNN() called from
** sqlite3PExpr(). */
if( pEq && isOuterJoin ){
ExprSetProperty(pEq, EP_FromJoin);
assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) );
ExprSetVVAProperty(pEq, EP_NoReduce);
pEq->w.iJoin = pE2->iTable;
}
*ppWhere = sqlite3ExprAnd(pParse, *ppWhere, pEq);
}
/*
** Set the EP_FromJoin property on all terms of the given expression.
** And set the Expr.w.iJoin to iTable for every term in the
** expression.
**
** The EP_FromJoin property is used on terms of an expression to tell
** the LEFT OUTER JOIN processing logic that this term is part of the
** the OUTER JOIN processing logic that this term is part of the
** join restriction specified in the ON or USING clause and not a part
** of the more general WHERE clause. These terms are moved over to the
** WHERE clause during join processing but we need to remember that they
@ -432,9 +403,10 @@ static void addWhereTerm(
** after the t1 loop and rows with t1.x!=5 will never appear in
** the output, which is incorrect.
*/
void sqlite3SetJoinExpr(Expr *p, int iTable){
void sqlite3SetJoinExpr(Expr *p, int iTable, u32 joinFlag){
assert( joinFlag==EP_FromJoin || joinFlag==EP_InnerJoin );
while( p ){
ExprSetProperty(p, EP_FromJoin);
ExprSetProperty(p, joinFlag);
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
ExprSetVVAProperty(p, EP_NoReduce);
p->w.iJoin = iTable;
@ -443,11 +415,11 @@ void sqlite3SetJoinExpr(Expr *p, int iTable){
if( p->x.pList ){
int i;
for(i=0; i<p->x.pList->nExpr; i++){
sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable);
sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable, joinFlag);
}
}
}
sqlite3SetJoinExpr(p->pLeft, iTable);
sqlite3SetJoinExpr(p->pLeft, iTable, joinFlag);
p = p->pRight;
}
}
@ -463,6 +435,7 @@ static void unsetJoinExpr(Expr *p, int iTable){
if( ExprHasProperty(p, EP_FromJoin)
&& (iTable<0 || p->w.iJoin==iTable) ){
ExprClearProperty(p, EP_FromJoin);
ExprSetProperty(p, EP_InnerJoin);
}
if( p->op==TK_COLUMN && p->iTable==iTable ){
ExprClearProperty(p, EP_CanBeNull);
@ -483,19 +456,26 @@ static void unsetJoinExpr(Expr *p, int iTable){
/*
** This routine processes the join information for a SELECT statement.
** ON and USING clauses are converted into extra terms of the WHERE clause.
** NATURAL joins also create extra WHERE clause terms.
**
** * A NATURAL join is converted into a USING join. After that, we
** do not need to be concerned with NATURAL joins and we only have
** think about USING joins.
**
** * ON and USING clauses result in extra terms being added to the
** WHERE clause to enforce the specified constraints. The extra
** WHERE clause terms will be tagged with EP_FromJoin or
** EP_InnerJoin so that we know that they originated in ON/USING.
**
** The terms of a FROM clause are contained in the Select.pSrc structure.
** The left most table is the first entry in Select.pSrc. The right-most
** table is the last entry. The join operator is held in the entry to
** the left. Thus entry 0 contains the join operator for the join between
** the right. Thus entry 1 contains the join operator for the join between
** entries 0 and 1. Any ON or USING clauses associated with the join are
** also attached to the left entry.
** also attached to the right entry.
**
** This routine returns the number of errors encountered.
*/
static int sqliteProcessJoin(Parse *pParse, Select *p){
static int sqlite3ProcessJoin(Parse *pParse, Select *p){
SrcList *pSrc; /* All tables in the FROM clause */
int i, j; /* Loop counters */
SrcItem *pLeft; /* Left table being joined */
@ -506,15 +486,16 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
pRight = &pLeft[1];
for(i=0; i<pSrc->nSrc-1; i++, pRight++, pLeft++){
Table *pRightTab = pRight->pTab;
int isOuter;
u32 joinType;
if( NEVER(pLeft->pTab==0 || pRightTab==0) ) continue;
isOuter = (pRight->fg.jointype & JT_OUTER)!=0;
joinType = (pRight->fg.jointype & JT_OUTER)!=0 ? EP_FromJoin : EP_InnerJoin;
/* When the NATURAL keyword is present, add WHERE clause terms for
** every column that the two tables have in common.
/* If this is a NATURAL join, synthesize an approprate USING clause
** to specify which columns should be joined.
*/
if( pRight->fg.jointype & JT_NATURAL ){
IdList *pUsing = 0;
if( pRight->fg.isUsing || pRight->u3.pOn ){
sqlite3ErrorMsg(pParse, "a NATURAL join may not have "
"an ON or USING clause", 0);
@ -522,16 +503,24 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
}
for(j=0; j<pRightTab->nCol; j++){
char *zName; /* Name of column in the right table */
int iLeft; /* Matching left table */
int iLeftCol; /* Matching column in the left table */
if( IsHiddenColumn(&pRightTab->aCol[j]) ) continue;
zName = pRightTab->aCol[j].zCnName;
if( tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol, 1) ){
addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, j,
isOuter, &p->pWhere);
if( tableAndColumnIndex(pSrc, 0, i, zName, 0, 0, 1) ){
pUsing = sqlite3IdListAppend(pParse, pUsing, 0);
if( pUsing ){
assert( pUsing->nId>0 );
assert( pUsing->a[pUsing->nId-1].zName==0 );
pUsing->a[pUsing->nId-1].zName = sqlite3DbStrDup(pParse->db, zName);
}
}
}
if( pUsing ){
pRight->fg.isUsing = 1;
pRight->fg.isSynthUsing = 1;
pRight->u3.pUsing = pUsing;
}
if( pParse->nErr ) return 1;
}
/* Create extra terms on the WHERE clause for each column named
@ -543,24 +532,74 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
*/
if( pRight->fg.isUsing ){
IdList *pList = pRight->u3.pUsing;
sqlite3 *db = pParse->db;
assert( pList!=0 );
for(j=0; j<pList->nId; j++){
char *zName; /* Name of the term in the USING clause */
int iLeft; /* Table on the left with matching column name */
int iLeftCol; /* Column number of matching column on the left */
int iRightCol; /* Column number of matching column on the right */
Expr *pE1; /* Reference to the column on the LEFT of the join */
Expr *pE2; /* Reference to the column on the RIGHT of the join */
Expr *pEq; /* Equality constraint. pE1 == pE2 */
zName = pList->a[j].zName;
iRightCol = sqlite3ColumnIndex(pRightTab, zName);
if( iRightCol<0
|| !tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol, 0)
|| tableAndColumnIndex(pSrc, 0, i, zName, &iLeft, &iLeftCol,
pRight->fg.isSynthUsing)==0
){
sqlite3ErrorMsg(pParse, "cannot join using column %s - column "
"not present in both tables", zName);
return 1;
}
addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, iRightCol,
isOuter, &p->pWhere);
pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iLeftCol);
sqlite3SrcItemColumnUsed(&pSrc->a[iLeft], iLeftCol);
if( (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){
/* This branch runs if the query contains one or more RIGHT or FULL
** JOINs. If only a single table on the left side of this join
** contains the zName column, then this branch is a no-op.
** But if there are two or more tables on the left side
** of the join, construct a coalesce() function that gathers all
** such tables. Raise an error if more than one of those references
** to zName is not also within a prior USING clause.
**
** We really ought to raise an error if there are two or more
** non-USING references to zName on the left of an INNER or LEFT
** JOIN. But older versions of SQLite do not do that, so we avoid
** adding a new error so as to not break legacy applications.
*/
ExprList *pFuncArgs = 0; /* Arguments to the coalesce() */
static const Token tkCoalesce = { "coalesce", 8 };
while( tableAndColumnIndex(pSrc, iLeft+1, i, zName, &iLeft, &iLeftCol,
pRight->fg.isSynthUsing)!=0 ){
if( pSrc->a[iLeft].fg.isUsing==0
|| sqlite3IdListIndex(pSrc->a[iLeft].u3.pUsing, zName)<0
){
sqlite3ErrorMsg(pParse, "ambiguous reference to %s in USING()",
zName);
break;
}
pFuncArgs = sqlite3ExprListAppend(pParse, pFuncArgs, pE1);
pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iLeftCol);
sqlite3SrcItemColumnUsed(&pSrc->a[iLeft], iLeftCol);
}
if( pFuncArgs ){
pFuncArgs = sqlite3ExprListAppend(pParse, pFuncArgs, pE1);
pE1 = sqlite3ExprFunction(pParse, pFuncArgs, &tkCoalesce, 0);
}
}
pE2 = sqlite3CreateColumnExpr(db, pSrc, i+1, iRightCol);
sqlite3SrcItemColumnUsed(pRight, iRightCol);
pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2);
assert( pE2!=0 || pEq==0 );
if( pEq ){
ExprSetProperty(pEq, joinType);
assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) );
ExprSetVVAProperty(pEq, EP_NoReduce);
pEq->w.iJoin = pE2->iTable;
}
p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pEq);
}
}
@ -568,7 +607,7 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
** an AND operator.
*/
else if( pRight->u3.pOn ){
if( isOuter ) sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor);
sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor, joinType);
p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->u3.pOn);
pRight->u3.pOn = 0;
}
@ -2140,12 +2179,13 @@ int sqlite3ColumnsFromExprList(
*paCol = aCol;
for(i=0, pCol=aCol; i<nCol && !db->mallocFailed; i++, pCol++){
struct ExprList_item *pX = &pEList->a[i];
/* Get an appropriate name for the column
*/
if( (zName = pEList->a[i].zEName)!=0 && pEList->a[i].eEName==ENAME_NAME ){
if( (zName = pX->zEName)!=0 && pX->eEName==ENAME_NAME ){
/* If the column contains an "AS <name>" phrase, use <name> as the name */
}else{
Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr);
Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pX->pExpr);
while( ALWAYS(pColExpr!=0) && pColExpr->op==TK_DOT ){
pColExpr = pColExpr->pRight;
assert( pColExpr!=0 );
@ -2163,7 +2203,7 @@ int sqlite3ColumnsFromExprList(
zName = pColExpr->u.zToken;
}else{
/* Use the original text of the column expression as its name */
zName = pEList->a[i].zEName;
assert( zName==pX->zEName ); /* pointer comparison intended */
}
}
if( zName && !sqlite3IsTrueOrFalse(zName) ){
@ -3656,12 +3696,40 @@ static int multiSelectOrderBy(
**
** All references to columns in table iTable are to be replaced by corresponding
** expressions in pEList.
**
** ## About "isOuterJoin":
**
** The isOuterJoin column indicates that the replacement will occur into a
** position in the parent that NULL-able due to an OUTER JOIN. Either the
** target slot in the parent is the right operand of a LEFT JOIN, or one of
** the left operands of a RIGHT JOIN. In either case, we need to potentially
** bypass the substituted expression with OP_IfNullRow.
**
** Suppose the original expression integer constant. Even though the table
** has the nullRow flag set, because the expression is an integer constant,
** it will not be NULLed out. So instead, we insert an OP_IfNullRow opcode
** that checks to see if the nullRow flag is set on the table. If the nullRow
** flag is set, then the value in the register is set to NULL and the original
** expression is bypassed. If the nullRow flag is not set, then the original
** expression runs to populate the register.
**
** Example where this is needed:
**
** CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT);
** CREATE TABLE t2(x INT UNIQUE);
**
** SELECT a,b,m,x FROM t1 LEFT JOIN (SELECT 59 AS m,x FROM t2) ON b=x;
**
** When the subquery on the right side of the LEFT JOIN is flattened, we
** have to add OP_IfNullRow in front of the OP_Integer that implements the
** "m" value of the subquery so that a NULL will be loaded instead of 59
** when processing a non-matched row of the left.
*/
typedef struct SubstContext {
Parse *pParse; /* The parsing context */
int iTable; /* Replace references to this table */
int iNewTable; /* New table number */
int isLeftJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */
int isOuterJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */
ExprList *pEList; /* Replacement expressions */
} SubstContext;
@ -3711,7 +3779,7 @@ static Expr *substExpr(
sqlite3VectorErrorMsg(pSubst->pParse, pCopy);
}else{
sqlite3 *db = pSubst->pParse->db;
if( pSubst->isLeftJoin && pCopy->op!=TK_COLUMN ){
if( pSubst->isOuterJoin && pCopy->op!=TK_COLUMN ){
memset(&ifNullRow, 0, sizeof(ifNullRow));
ifNullRow.op = TK_IF_NULL_ROW;
ifNullRow.pLeft = pCopy;
@ -3725,11 +3793,12 @@ static Expr *substExpr(
sqlite3ExprDelete(db, pNew);
return pExpr;
}
if( pSubst->isLeftJoin ){
if( pSubst->isOuterJoin ){
ExprSetProperty(pNew, EP_CanBeNull);
}
if( ExprHasProperty(pExpr,EP_FromJoin) ){
sqlite3SetJoinExpr(pNew, pExpr->w.iJoin);
if( ExprHasProperty(pExpr,EP_FromJoin|EP_InnerJoin) ){
sqlite3SetJoinExpr(pNew, pExpr->w.iJoin,
pExpr->flags & (EP_FromJoin|EP_InnerJoin));
}
sqlite3ExprDelete(db, pExpr);
pExpr = pNew;
@ -3979,6 +4048,7 @@ static void renumberCursors(
** table and
** (3c) the outer query may not be an aggregate.
** (3d) the outer query may not be DISTINCT.
** See also (26) for restrictions on RIGHT JOIN.
**
** (4) The subquery can not be DISTINCT.
**
@ -4077,6 +4147,12 @@ static void renumberCursors(
** function in the select list or ORDER BY clause, flattening
** is not attempted.
**
** (26) The subquery may not be the right operand of a RIGHT JOIN.
** See also (3) for restrictions on LEFT JOIN.
**
** (27) The subquery may not contain a FULL or RIGHT JOIN unless it
** is the first element of the parent query.
**
**
** In this routine, the "p" parameter is a pointer to the outer query.
** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query
@ -4102,7 +4178,7 @@ static int flattenSubquery(
SrcList *pSubSrc; /* The FROM clause of the subquery */
int iParent; /* VDBE cursor number of the pSub result set temp table */
int iNewParent = -1;/* Replacement table for iParent */
int isLeftJoin = 0; /* True if pSub is the right side of a LEFT JOIN */
int isOuterJoin = 0; /* True if pSub is the right side of a LEFT JOIN */
int i; /* Loop counter */
Expr *pWhere; /* The WHERE clause */
SrcItem *pSubitem; /* The subquery */
@ -4175,26 +4251,32 @@ static int flattenSubquery(
**
** See also tickets #306, #350, and #3300.
*/
if( (pSubitem->fg.jointype & JT_OUTER)!=0 ){
isLeftJoin = 1;
if( pSubSrc->nSrc>1 /* (3a) */
|| isAgg /* (3b) */
|| IsVirtual(pSubSrc->a[0].pTab) /* (3c) */
|| (p->selFlags & SF_Distinct)!=0 /* (3d) */
if( (pSubitem->fg.jointype & (JT_OUTER|JT_LTORJ))!=0 ){
if( pSubSrc->nSrc>1 /* (3a) */
|| isAgg /* (3b) */
|| IsVirtual(pSubSrc->a[0].pTab) /* (3c) */
|| (p->selFlags & SF_Distinct)!=0 /* (3d) */
|| (pSubitem->fg.jointype & JT_RIGHT)!=0 /* (26) */
){
return 0;
}
isOuterJoin = 1;
}
#ifdef SQLITE_EXTRA_IFNULLROW
else if( iFrom>0 && !isAgg ){
/* Setting isLeftJoin to -1 causes OP_IfNullRow opcodes to be generated for
/* Setting isOuterJoin to -1 causes OP_IfNullRow opcodes to be generated for
** every reference to any result column from subquery in a join, even
** though they are not necessary. This will stress-test the OP_IfNullRow
** opcode. */
isLeftJoin = -1;
isOuterJoin = -1;
}
#endif
assert( pSubSrc->nSrc>0 ); /* True by restriction (7) */
if( iFrom>0 && (pSubSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){
return 0; /* Restriction (27) */
}
/* Restriction (17): If the sub-query is a compound SELECT, then it must
** use only the UNION ALL operator. And none of the simple select queries
** that make up the compound SELECT are allowed to be aggregate or distinct
@ -4204,7 +4286,7 @@ static int flattenSubquery(
if( pSub->pOrderBy ){
return 0; /* Restriction (20) */
}
if( isAgg || (p->selFlags & SF_Distinct)!=0 || isLeftJoin>0 ){
if( isAgg || (p->selFlags & SF_Distinct)!=0 || isOuterJoin>0 ){
return 0; /* (17d1), (17d2), or (17f) */
}
for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){
@ -4372,6 +4454,7 @@ static int flattenSubquery(
for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){
int nSubSrc;
u8 jointype = 0;
u8 ltorj = pSrc->a[iFrom].fg.jointype & JT_LTORJ;
assert( pSub!=0 );
pSubSrc = pSub->pSrc; /* FROM clause of subquery */
nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */
@ -4410,10 +4493,12 @@ static int flattenSubquery(
if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing);
assert( pItem->fg.isTabFunc==0 );
*pItem = pSubSrc->a[i];
pItem->fg.jointype |= ltorj;
iNewParent = pSubSrc->a[i].iCursor;
memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
}
pSrc->a[iFrom].fg.jointype = jointype;
pSrc->a[iFrom].fg.jointype &= JT_LTORJ;
pSrc->a[iFrom].fg.jointype |= jointype | ltorj;
/* Now begin substituting subquery result set expressions for
** references to the iParent in the outer query.
@ -4448,8 +4533,8 @@ static int flattenSubquery(
}
pWhere = pSub->pWhere;
pSub->pWhere = 0;
if( isLeftJoin>0 ){
sqlite3SetJoinExpr(pWhere, iNewParent);
if( isOuterJoin>0 ){
sqlite3SetJoinExpr(pWhere, iNewParent, EP_FromJoin);
}
if( pWhere ){
if( pParent->pWhere ){
@ -4463,7 +4548,7 @@ static int flattenSubquery(
x.pParse = pParse;
x.iTable = iParent;
x.iNewTable = iNewParent;
x.isLeftJoin = isLeftJoin;
x.isOuterJoin = isOuterJoin;
x.pEList = pSub->pEList;
substSelect(&x, pParent, 0);
}
@ -4924,7 +5009,7 @@ static int pushDownWhereTerms(
x.pParse = pParse;
x.iTable = iCursor;
x.iNewTable = iCursor;
x.isLeftJoin = 0;
x.isOuterJoin = 0;
x.pEList = pSubq->pEList;
pNew = substExpr(&x, pNew);
#ifndef SQLITE_OMIT_WINDOWFUNC
@ -5478,11 +5563,35 @@ int sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){
#else
pTab->tabFlags |= TF_Ephemeral; /* Legacy compatibility mode */
#endif
return pParse->nErr ? SQLITE_ERROR : SQLITE_OK;
}
/*
** Check the N SrcItem objects to the right of pBase. (N might be zero!)
** If any of those SrcItem objects have a USING clause containing zName
** then return true.
**
** If N is zero, or none of the N SrcItem objects to the right of pBase
** contains a USING clause, or if none of the USING clauses contain zName,
** then return false.
*/
static int inAnyUsingClause(
const char *zName, /* Name we are looking for */
SrcItem *pBase, /* The base SrcItem. Looking at pBase[1] and following */
int N /* How many SrcItems to check */
){
while( N>0 ){
N--;
pBase++;
if( pBase->fg.isUsing==0 ) continue;
if( NEVER(pBase->u3.pUsing==0) ) continue;
if( sqlite3IdListIndex(pBase->u3.pUsing, zName)>=0 ) return 1;
}
return 0;
}
/*
** This routine is a Walker callback for "expanding" a SELECT statement.
** "Expanding" means to do the following:
@ -5632,7 +5741,7 @@ static int selectExpander(Walker *pWalker, Select *p){
/* Process NATURAL keywords, and ON and USING clauses of joins.
*/
assert( db->mallocFailed==0 || pParse->nErr!=0 );
if( pParse->nErr || sqliteProcessJoin(pParse, p) ){
if( pParse->nErr || sqlite3ProcessJoin(pParse, p) ){
return WRC_Abort;
}
@ -5696,7 +5805,7 @@ static int selectExpander(Walker *pWalker, Select *p){
}
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
Table *pTab = pFrom->pTab;
Select *pSub = pFrom->pSelect;
Select *pSub;
char *zTabName = pFrom->zAlias;
const char *zSchemaName = 0;
int iDb;
@ -5704,7 +5813,12 @@ static int selectExpander(Walker *pWalker, Select *p){
zTabName = pTab->zName;
}
if( db->mallocFailed ) break;
if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){
assert( pFrom->fg.isNestedFrom == IsNestedFrom(pFrom->pSelect) );
if( pFrom->fg.isNestedFrom ){
pSub = pFrom->pSelect;
assert( pSub->pEList!=0 );
assert( pSub->pEList->nExpr==pTab->nCol );
}else{
pSub = 0;
if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
continue;
@ -5714,9 +5828,7 @@ static int selectExpander(Walker *pWalker, Select *p){
}
for(j=0; j<pTab->nCol; j++){
char *zName = pTab->aCol[j].zCnName;
char *zColname; /* The computed column name */
char *zToFree; /* Malloced string that needs to be freed */
Token sColname; /* Computed column name as a token */
struct ExprList_item *pX; /* Newly added ExprList term */
assert( zName );
if( zTName && pSub
@ -5737,13 +5849,6 @@ static int selectExpander(Walker *pWalker, Select *p){
tableSeen = 1;
if( i>0 && zTName==0 ){
if( (pFrom->fg.jointype & JT_NATURAL)!=0
&& tableAndColumnIndex(pTabList, i, zName, 0, 0, 1)
){
/* In a NATURAL join, omit the join columns from the
** table to the right of the join */
continue;
}
if( pFrom->fg.isUsing
&& sqlite3IdListIndex(pFrom->u3.pUsing, zName)>=0
){
@ -5753,9 +5858,13 @@ static int selectExpander(Walker *pWalker, Select *p){
}
}
pRight = sqlite3Expr(db, TK_ID, zName);
zColname = zName;
zToFree = 0;
if( longNames || pTabList->nSrc>1 || IN_RENAME_OBJECT ){
if( (pTabList->nSrc>1
&& ( (pFrom->fg.jointype & JT_LTORJ)==0
|| !inAnyUsingClause(zName,pFrom,pTabList->nSrc-i-1)
)
)
|| IN_RENAME_OBJECT
){
Expr *pLeft;
pLeft = sqlite3Expr(db, TK_ID, zTabName);
pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
@ -5766,31 +5875,32 @@ static int selectExpander(Walker *pWalker, Select *p){
pLeft = sqlite3Expr(db, TK_ID, zSchemaName);
pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr);
}
if( longNames ){
zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName);
zToFree = zColname;
}
}else{
pExpr = pRight;
}
pNew = sqlite3ExprListAppend(pParse, pNew, pExpr);
sqlite3TokenInit(&sColname, zColname);
sqlite3ExprListSetName(pParse, pNew, &sColname, 0);
if( pNew && (p->selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){
struct ExprList_item *pX = &pNew->a[pNew->nExpr-1];
sqlite3DbFree(db, pX->zEName);
if( pNew==0 ){
break; /* OOM */
}
pX = &pNew->a[pNew->nExpr-1];
assert( pX->zEName==0 );
if( (selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){
if( pSub ){
pX->zEName = sqlite3DbStrDup(db, pSub->pEList->a[j].zEName);
testcase( pX->zEName==0 );
}else{
pX->zEName = sqlite3MPrintf(db, "%s.%s.%s",
zSchemaName, zTabName, zColname);
zSchemaName, zTabName, zName);
testcase( pX->zEName==0 );
}
pX->eEName = ENAME_TAB;
}else if( longNames ){
pX->zEName = sqlite3MPrintf(db, "%s.%s", zTabName, zName);
pX->eEName = ENAME_NAME;
}else{
pX->zEName = sqlite3DbStrDup(db, zName);
pX->eEName = ENAME_NAME;
}
sqlite3DbFree(db, zToFree);
}
}
if( !tableSeen ){
@ -5814,6 +5924,12 @@ static int selectExpander(Walker *pWalker, Select *p){
p->selFlags |= SF_ComplexResult;
}
}
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x100 ){
SELECTTRACE(0x100,pParse,p,("After result-set wildcard expansion:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
return WRC_Continue;
}
@ -6507,7 +6623,7 @@ int sqlite3Select(
/* Convert LEFT JOIN into JOIN if there are terms of the right table
** of the LEFT JOIN used in the WHERE clause.
*/
if( (pItem->fg.jointype & JT_LEFT)!=0
if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==JT_LEFT
&& sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor)
&& OptimizationEnabled(db, SQLITE_SimplifyJoin)
){
@ -6593,7 +6709,7 @@ int sqlite3Select(
&& i==0
&& (p->selFlags & SF_ComplexResult)!=0
&& (pTabList->nSrc==1
|| (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0)
|| (pTabList->a[1].fg.jointype&(JT_OUTER|JT_CROSS))!=0)
){
continue;
}
@ -6715,6 +6831,7 @@ int sqlite3Select(
if( OptimizationEnabled(db, SQLITE_PushDown)
&& (pItem->fg.isCte==0
|| (pItem->u2.pCteUse->eM10d!=M10d_Yes && pItem->u2.pCteUse->nUse<2))
&& (pItem->fg.jointype & JT_RIGHT)==0
&& pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor,
(pItem->fg.jointype & JT_OUTER)!=0)
){
@ -6735,18 +6852,19 @@ int sqlite3Select(
/* Generate code to implement the subquery
**
** The subquery is implemented as a co-routine if:
** The subquery is implemented as a co-routine all of the following are
** true:
**
** (1) the subquery is guaranteed to be the outer loop (so that
** it does not need to be computed more than once), and
** (2) the subquery is not a CTE that should be materialized
**
** TODO: Are there other reasons beside (1) and (2) to use a co-routine
** implementation?
** (3) the subquery is not part of a left operand for a RIGHT JOIN
*/
if( i==0
&& (pTabList->nSrc==1
|| (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) /* (1) */
&& (pItem->fg.isCte==0 || pItem->u2.pCteUse->eM10d!=M10d_Yes) /* (2) */
|| (pTabList->a[1].fg.jointype&(JT_OUTER|JT_CROSS))!=0) /* (1) */
&& (pItem->fg.isCte==0 || pItem->u2.pCteUse->eM10d!=M10d_Yes) /* (2) */
&& (pTabList->a[0].fg.jointype & JT_LTORJ)==0 /* (3) */
){
/* Implement a co-routine that will return a single row of the result
** set on each invocation.
@ -6908,6 +7026,18 @@ int sqlite3Select(
*/
if( pDest->eDest==SRT_EphemTab ){
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iSDParm, pEList->nExpr);
if( p->selFlags & SF_NestedFrom ){
/* Delete or NULL-out result columns that will never be used */
int ii;
for(ii=pEList->nExpr-1; ii>0 && pEList->a[ii].bUsed==0; ii--){
sqlite3ExprDelete(db, pEList->a[ii].pExpr);
sqlite3DbFree(db, pEList->a[ii].zEName);
pEList->nExpr--;
}
for(ii=0; ii<pEList->nExpr; ii++){
if( pEList->a[ii].bUsed==0 ) pEList->a[ii].pExpr->op = TK_NULL;
}
}
}
/* Set the limiter.
@ -7524,7 +7654,9 @@ int sqlite3Select(
updateAccumulator(pParse, regAcc, pAggInfo, eDist);
if( eDist!=WHERE_DISTINCT_NOOP ){
struct AggInfo_func *pF = &pAggInfo->aFunc[0];
fixDistinctOpenEph(pParse, eDist, pF->iDistinct, pF->iDistAddr);
if( pF ){
fixDistinctOpenEph(pParse, eDist, pF->iDistinct, pF->iDistAddr);
}
}
if( regAcc ) sqlite3VdbeAddOp2(v, OP_Integer, 1, regAcc);

View File

@ -2887,7 +2887,7 @@ struct Expr {
#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
/* 0x400000 // Available */
#define EP_InnerJoin 0x400000 /* Originates in ON/USING of an inner join */
#define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */
#define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */
#define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */
@ -3004,6 +3004,7 @@ struct ExprList {
unsigned reusable :1; /* Constant expression is reusable */
unsigned bSorterRef :1; /* Defer evaluation until after sorting */
unsigned bNulls: 1; /* True if explicit "NULLS FIRST/LAST" */
unsigned bUsed: 1; /* This column used in a SF_NestedFrom subquery */
union {
struct { /* Used by any ExprList other than Parse.pConsExpr */
u16 iOrderByCol; /* For ORDER BY, column number in result set */
@ -3038,13 +3039,25 @@ struct ExprList {
** If "a" is the k-th column of table "t", then IdList.a[0].idx==k.
*/
struct IdList {
int nId; /* Number of identifiers on the list */
u8 eU4; /* Which element of a.u4 is valid */
struct IdList_item {
char *zName; /* Name of the identifier */
int idx; /* Index in some Table.aCol[] of a column named zName */
} *a;
int nId; /* Number of identifiers on the list */
union {
int idx; /* Index in some Table.aCol[] of a column named zName */
Expr *pExpr; /* Expr to implement a USING variable -- NOT USED */
} u4;
} a[1];
};
/*
** Allowed values for IdList.eType, which determines which value of the a.u4
** is valid.
*/
#define EU4_NONE 0 /* Does not use IdList.a.u4 */
#define EU4_IDX 1 /* Uses IdList.a.u4.idx */
#define EU4_EXPR 2 /* Uses IdList.a.u4.pExpr -- NOT CURRENTLY USED */
/*
** The SrcItem object represents a single term in the FROM clause of a query.
** The SrcList object is mostly an array of SrcItems.
@ -3078,6 +3091,8 @@ struct SrcItem {
unsigned isCte :1; /* This is a CTE */
unsigned notCte :1; /* This item may not match a CTE */
unsigned isUsing :1; /* u3.pUsing is valid */
unsigned isSynthUsing :1; /* u3.pUsing is synthensized from NATURAL */
unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */
} fg;
int iCursor; /* The VDBE cursor number used to access this table */
union {
@ -3132,14 +3147,15 @@ struct SrcList {
/*
** Permitted values of the SrcList.a.jointype field
*/
#define JT_INNER 0x0001 /* Any kind of inner or cross join */
#define JT_CROSS 0x0002 /* Explicit use of the CROSS keyword */
#define JT_NATURAL 0x0004 /* True for a "natural" join */
#define JT_LEFT 0x0008 /* Left outer join */
#define JT_RIGHT 0x0010 /* Right outer join */
#define JT_OUTER 0x0020 /* The "OUTER" keyword is present */
#define JT_ERROR 0x0040 /* unknown or unsupported join type */
#define JT_INNER 0x01 /* Any kind of inner or cross join */
#define JT_CROSS 0x02 /* Explicit use of the CROSS keyword */
#define JT_NATURAL 0x04 /* True for a "natural" join */
#define JT_LEFT 0x08 /* Left outer join */
#define JT_RIGHT 0x10 /* Right outer join */
#define JT_OUTER 0x20 /* The "OUTER" keyword is present */
#define JT_LTORJ 0x40 /* One of the LEFT operands of a RIGHT JOIN
** Mnemonic: Left Table Of Right Join */
#define JT_ERROR 0x80 /* unknown or unsupported join type */
/*
** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin()
@ -3162,7 +3178,7 @@ struct SrcList {
#define WHERE_SORTBYGROUP 0x0200 /* Support sqlite3WhereIsSorted() */
#define WHERE_AGG_DISTINCT 0x0400 /* Query is "SELECT agg(DISTINCT ...)" */
#define WHERE_ORDERBY_LIMIT 0x0800 /* ORDERBY+LIMIT on the inner loop */
/* 0x1000 not currently used */
#define WHERE_RIGHT_JOIN 0x1000 /* Processing a RIGHT JOIN */
/* 0x2000 not currently used */
#define WHERE_USE_LIMIT 0x4000 /* Use the LIMIT in cost estimates */
/* 0x8000 not currently used */
@ -3358,6 +3374,9 @@ struct Select {
#define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */
#define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */
/* True if S exists and has SF_NestedFrom */
#define IsNestedFrom(S) ((S)!=0 && ((S)->selFlags&SF_NestedFrom)!=0)
/*
** The results of a SELECT can be distributed in several ways, as defined
** by one of the following macros. The "SRT" prefix means "SELECT Result
@ -3569,6 +3588,7 @@ struct Parse {
u8 okConstFactor; /* OK to factor out constants */
u8 disableLookaside; /* Number of times lookaside has been disabled */
u8 disableVtab; /* Disable all virtual tables for this parse */
u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */
#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */
#endif
@ -4628,7 +4648,7 @@ SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*,
void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *);
void sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*);
int sqlite3IndexedByLookup(Parse *, SrcItem *);
void sqlite3SrcListShiftJoinType(SrcList*);
void sqlite3SrcListShiftJoinType(Parse*,SrcList*);
void sqlite3SrcListAssignCursors(Parse*, SrcList*);
void sqlite3IdListDelete(sqlite3*, IdList*);
void sqlite3ClearOnOrUsing(sqlite3*, OnOrUsing*);
@ -4828,7 +4848,8 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int);
int sqlite3JoinType(Parse*, Token*, Token*, Token*);
int sqlite3ColumnIndex(Table *pTab, const char *zCol);
void sqlite3SetJoinExpr(Expr*,int);
void sqlite3SrcItemColumnUsed(SrcItem*,int);
void sqlite3SetJoinExpr(Expr*,int,u32);
void sqlite3CreateForeignKey(Parse*, ExprList*, Token*, ExprList*, int);
void sqlite3DeferForeignKey(Parse*, int);
#ifndef SQLITE_OMIT_AUTHORIZATION

View File

@ -133,6 +133,7 @@ void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
*/
void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){
int i;
if( pSrc==0 ) return;
for(i=0; i<pSrc->nSrc; i++){
const SrcItem *pItem = &pSrc->a[i];
StrAccum x;
@ -144,11 +145,18 @@ void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){
sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx",
pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab, pItem->colUsed);
}
if( pItem->fg.jointype & JT_LEFT ){
if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==(JT_LEFT|JT_RIGHT) ){
sqlite3_str_appendf(&x, " FULL-OUTER-JOIN");
}else if( pItem->fg.jointype & JT_LEFT ){
sqlite3_str_appendf(&x, " LEFT-JOIN");
}else if( pItem->fg.jointype & JT_RIGHT ){
sqlite3_str_appendf(&x, " RIGHT-JOIN");
}else if( pItem->fg.jointype & JT_CROSS ){
sqlite3_str_appendf(&x, " CROSS-JOIN");
}
if( pItem->fg.jointype & JT_LTORJ ){
sqlite3_str_appendf(&x, " LTORJ");
}
if( pItem->fg.fromDDL ){
sqlite3_str_appendf(&x, " DDL");
}
@ -158,6 +166,7 @@ void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){
sqlite3StrAccumFinish(&x);
sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1);
if( pItem->pSelect ){
assert( pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) );
sqlite3TreeViewSelect(pView, pItem->pSelect, 0);
}
if( pItem->fg.isTabFunc ){
@ -786,13 +795,23 @@ void sqlite3TreeViewBareExprList(
int j = pList->a[i].u.x.iOrderByCol;
char *zName = pList->a[i].zEName;
int moreToFollow = i<pList->nExpr - 1;
if( pList->a[i].eEName!=ENAME_NAME ) zName = 0;
if( j || zName ){
sqlite3TreeViewPush(&pView, moreToFollow);
moreToFollow = 0;
sqlite3TreeViewLine(pView, 0);
if( zName ){
fprintf(stdout, "AS %s ", zName);
switch( pList->a[i].eEName ){
default:
fprintf(stdout, "AS %s ", zName);
break;
case ENAME_TAB:
fprintf(stdout, "TABLE-ALIAS-NAME(\"%s\") ", zName);
if( pList->a[i].bUsed==0 ) fprintf(stdout, "(unused) ");
break;
case ENAME_SPAN:
fprintf(stdout, "SPAN(\"%s\") ", zName);
break;
}
}
if( j ){
fprintf(stdout, "iOrderByCol=%d", j);
@ -838,7 +857,21 @@ void sqlite3TreeViewBareIdList(
if( zName==0 ) zName = "(null)";
sqlite3TreeViewPush(&pView, moreToFollow);
sqlite3TreeViewLine(pView, 0);
fprintf(stdout, "%s (%d)\n", zName, pList->a[i].idx);
if( pList->eU4==EU4_NONE ){
fprintf(stdout, "%s\n", zName);
}else if( pList->eU4==EU4_IDX ){
fprintf(stdout, "%s (%d)\n", zName, pList->a[i].u4.idx);
}else{
assert( pList->eU4==EU4_EXPR );
if( pList->a[i].u4.pExpr==0 ){
fprintf(stdout, "%s (pExpr=NULL)\n", zName);
}else{
fprintf(stdout, "%s\n", zName);
sqlite3TreeViewPush(&pView, i<pList->nId-1);
sqlite3TreeViewExpr(pView, pList->a[i].u4.pExpr, 0);
sqlite3TreeViewPop(&pView);
}
}
sqlite3TreeViewPop(&pView);
}
}

View File

@ -988,8 +988,17 @@ case OP_Gosub: { /* jump */
/* Opcode: Return P1 P2 P3 * *
**
** Jump to the next instruction after the address in register P1. After
** the jump, register P1 becomes undefined.
** Jump to the address stored in register P1. If P1 is a return address
** register, then this accomplishes a return from a subroutine.
**
** If P3 is 1, then the jump is only taken if register P1 holds an integer
** values, otherwise execution falls through to the next opcode, and the
** OP_Return becomes a no-op. If P3 is 0, then register P1 must hold an
** integer or else an assert() is raised. P3 should be set to 1 when
** this opcode is used in combination with OP_BeginSubrtn, and set to 0
** otherwise.
**
** The value in register P1 is unchanged by this opcode.
**
** P2 is not used by the byte-code engine. However, if P2 is positive
** and also less than the current address, then the "EXPLAIN" output
@ -998,16 +1007,15 @@ case OP_Gosub: { /* jump */
** in the subroutine from which this opcode is returnning. Thus the P2
** value is a byte-code indentation hint. See tag-20220407a in
** wherecode.c and shell.c.
**
** P3 is not used by the byte-code engine. However, the code generator
** sets P3 to address of the associated OP_BeginSubrtn opcode, if there is
** one.
*/
case OP_Return: { /* in1 */
pIn1 = &aMem[pOp->p1];
assert( pIn1->flags==MEM_Int );
pOp = &aOp[pIn1->u.i];
pIn1->flags = MEM_Undefined;
if( pIn1->flags & MEM_Int ){
if( pOp->p3 ){ VdbeBranchTaken(1, 2); }
pOp = &aOp[pIn1->u.i];
}else if( ALWAYS(pOp->p3) ){
VdbeBranchTaken(0, 2);
}
break;
}
@ -1193,22 +1201,11 @@ case OP_Halt: {
goto vdbe_return;
}
/* Opcode: BeginSubrtn P1 P2 * * *
** Synopsis: r[P2]=P1
**
** Mark the beginning of a subroutine by loading the integer value P1
** into register r[P2]. The P2 register is used to store the return
** address of the subroutine call.
**
** This opcode is identical to OP_Integer. It has a different name
** only to make the byte code easier to read and verify.
*/
/* Opcode: Integer P1 P2 * * *
** Synopsis: r[P2]=P1
**
** The 32-bit integer value P1 is written into register P2.
*/
case OP_BeginSubrtn:
case OP_Integer: { /* out2 */
pOut = out2Prerelease(p, pOp);
pOut->u.i = pOp->p1;
@ -1315,6 +1312,28 @@ case OP_String: { /* out2 */
break;
}
/* Opcode: BeginSubrtn * P2 * * *
** Synopsis: r[P2]=NULL
**
** Mark the beginning of a subroutine that can be entered in-line
** or that can be called using OP_Gosub. The subroutine should
** be terminated by an OP_Return instruction that has a P1 operand that
** is the same as the P2 operand to this opcode and that has P3 set to 1.
** If the subroutine is entered in-line, then the OP_Return will simply
** fall through. But if the subroutine is entered using OP_Gosub, then
** the OP_Return will jump back to the first instruction after the OP_Gosub.
**
** This routine works by loading a NULL into the P2 register. When the
** return address register contains a NULL, the OP_Return instruction is
** a no-op that simply falls through to the next instruction (assuming that
** the OP_Return opcode has a P3 value of 1). Thus if the subroutine is
** entered in-line, then the OP_Return will cause in-line execution to
** continue. But if the subroutine is entered via OP_Gosub, then the
** OP_Return will cause a return to the address following the OP_Gosub.
**
** This opcode is identical to OP_Null. It has a different name
** only to make the byte code easier to read and verify.
*/
/* Opcode: Null P1 P2 P3 * *
** Synopsis: r[P2..P3]=NULL
**
@ -1327,6 +1346,7 @@ case OP_String: { /* out2 */
** NULL values will not compare equal even if SQLITE_NULLEQ is set on
** OP_Ne or OP_Eq.
*/
case OP_BeginSubrtn:
case OP_Null: { /* out2 */
int cnt;
u16 nullFlag;
@ -2701,7 +2721,8 @@ case OP_Column: {
op_column_restart:
assert( pC!=0 );
assert( p2<(u32)pC->nField );
assert( p2<(u32)pC->nField
|| (pC->eCurType==CURTYPE_PSEUDO && pC->seekResult==0) );
aOffset = pC->aOffset;
assert( aOffset==pC->aType+pC->nField );
assert( pC->eCurType!=CURTYPE_VTAB );
@ -2710,10 +2731,9 @@ op_column_restart:
if( pC->cacheStatus!=p->cacheCtr ){ /*OPTIMIZATION-IF-FALSE*/
if( pC->nullRow ){
if( pC->eCurType==CURTYPE_PSEUDO ){
if( pC->eCurType==CURTYPE_PSEUDO && pC->seekResult>0 ){
/* For the special case of as pseudo-cursor, the seekResult field
** identifies the register that holds the record */
assert( pC->seekResult>0 );
pReg = &aMem[pC->seekResult];
assert( pReg->flags & MEM_Blob );
assert( memIsValid(pReg) );
@ -4990,15 +5010,14 @@ case OP_Found: { /* jump, in3 */
#ifdef SQLITE_DEBUG
pC->seekOp = pOp->opcode;
#endif
pIn3 = &aMem[pOp->p3];
r.aMem = &aMem[pOp->p3];
assert( pC->eCurType==CURTYPE_BTREE );
assert( pC->uc.pCursor!=0 );
assert( pC->isTable==0 );
if( pOp->p4.i>0 ){
r.nField = (u16)pOp->p4.i;
if( r.nField>0 ){
/* Key values in an array of registers */
r.nField = (u16)pOp->p4.i;
r.pKeyInfo = pC->pKeyInfo;
r.aMem = pIn3;
r.default_rc = 0;
#ifdef SQLITE_DEBUG
for(ii=0; ii<r.nField; ii++){
@ -5010,17 +5029,14 @@ case OP_Found: { /* jump, in3 */
rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, &r, &pC->seekResult);
}else{
/* Composite key generated by OP_MakeRecord */
assert( pIn3->flags & MEM_Blob );
assert( r.aMem->flags & MEM_Blob );
assert( pOp->opcode!=OP_NoConflict );
#if defined(_MSC_VER)
memset(&r, 0, sizeof(r)); /* Silence a harmless compiler warning */
#endif
rc = ExpandBlob(pIn3);
rc = ExpandBlob(r.aMem);
assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
if( rc ) goto no_mem;
pIdxKey = sqlite3VdbeAllocUnpackedRecord(pC->pKeyInfo);
if( pIdxKey==0 ) goto no_mem;
sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey);
sqlite3VdbeRecordUnpack(pC->pKeyInfo, r.aMem->n, r.aMem->z, pIdxKey);
pIdxKey->default_rc = 0;
rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, pIdxKey, &pC->seekResult);
sqlite3DbFreeNN(db, pIdxKey);
@ -5801,16 +5817,23 @@ case OP_Rowid: { /* out2 */
** that occur while the cursor is on the null row will always
** write a NULL.
**
** Or, if P1 is a Pseudo-Cursor (a cursor opened using OP_OpenPseudo)
** just reset the cache for that cursor. This causes the row of
** content held by the pseudo-cursor to be reparsed.
** If cursor P1 is not previously opened, open it now to a special
** pseudo-cursor that always returns NULL for every column.
*/
case OP_NullRow: {
VdbeCursor *pC;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
if( pC==0 ){
/* If the cursor is not already open, create a special kind of
** pseudo-cursor that always gives null rows. */
pC = allocateCursor(p, pOp->p1, 1, CURTYPE_PSEUDO);
if( pC==0 ) goto no_mem;
pC->seekResult = 0;
pC->isTable = 1;
pC->uc.pCursor = sqlite3BtreeFakeValidCursor();
}
pC->nullRow = 1;
pC->cacheStatus = CACHE_STALE;
if( pC->eCurType==CURTYPE_BTREE ){
@ -6257,9 +6280,9 @@ case OP_IdxRowid: { /* out2 */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( pC->eCurType==CURTYPE_BTREE );
assert( pC->eCurType==CURTYPE_BTREE || IsNullCursor(pC) );
assert( pC->uc.pCursor!=0 );
assert( pC->isTable==0 );
assert( pC->isTable==0 || IsNullCursor(pC) );
assert( pC->deferredMoveto==0 );
assert( !pC->nullRow || pOp->opcode==OP_IdxRowid );

View File

@ -134,6 +134,11 @@ struct VdbeCursor {
u32 aType[1]; /* Type values record decode. MUST BE LAST */
};
/* Return true if P is a null-only cursor
*/
#define IsNullCursor(P) \
((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0)
/*
** A value for VdbeCursor.cacheStatus that means the cache is always invalid.

View File

@ -3554,7 +3554,7 @@ int SQLITE_NOINLINE sqlite3VdbeHandleMovedCursor(VdbeCursor *p){
** if need be. Return any I/O error from the restore operation.
*/
int sqlite3VdbeCursorRestore(VdbeCursor *p){
assert( p->eCurType==CURTYPE_BTREE );
assert( p->eCurType==CURTYPE_BTREE || IsNullCursor(p) );
if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){
return sqlite3VdbeHandleMovedCursor(p);
}

View File

@ -756,13 +756,13 @@ static int termCanDriveIndex(
char aff;
if( pTerm->leftCursor!=pSrc->iCursor ) return 0;
if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) return 0;
if( (pSrc->fg.jointype & JT_LEFT)
if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ))!=0
&& !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
&& (pTerm->eOperator & WO_IS)
){
/* Cannot use an IS term from the WHERE clause as an index driver for
** the RHS of a LEFT JOIN. Such a term can only be used if it is from
** the ON clause. */
** the RHS of a LEFT JOIN or for the LHS of a RIGHT JOIN. Such a term
** can only be used if it is from the ON clause. */
return 0;
}
if( (pTerm->prereqRight & notReady)!=0 ) return 0;
@ -832,7 +832,8 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
** WHERE clause (or the ON clause of a LEFT join) that constrain which
** rows of the target table (pSrc) that can be used. */
if( (pTerm->wtFlags & TERM_VIRTUAL)==0
&& ((pSrc->fg.jointype&JT_LEFT)==0 || ExprHasProperty(pExpr,EP_FromJoin))
&& ((pSrc->fg.jointype&(JT_LEFT|JT_LTORJ))==0
|| ExprHasProperty(pExpr,EP_FromJoin))
&& sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor)
){
pPartial = sqlite3ExprAnd(pParse, pPartial,
@ -1105,7 +1106,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
const SrcItem *pTabItem;
pLevel = &pWInfo->a[iLevel];
pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
if( pTabItem->fg.jointype & JT_LEFT ) continue;
if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ) ) continue;
pLoop = pLevel->pWLoop;
if( NEVER(pLoop==0) ) continue;
if( pLoop->prereq & notReady ) continue;
@ -1178,9 +1179,10 @@ static sqlite3_index_info *allocateIndexInfo(
assert( pTerm->u.x.leftColumn<pTab->nCol );
/* tag-20191211-002: WHERE-clause constraints are not useful to the
** right-hand table of a LEFT JOIN. See tag-20191211-001 for the
** right-hand table of a LEFT JOIN nor to the left-hand table of a
** RIGHT JOIN. See tag-20191211-001 for the
** equivalent restriction for ordinary tables. */
if( (pSrc->fg.jointype & JT_LEFT)!=0
if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ))!=0
&& !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
){
continue;
@ -2621,10 +2623,11 @@ static void whereLoopOutputAdjust(
**
** 2022-03-24: Self-culling only applies if either the extra terms
** are straight comparison operators that are non-true with NULL
** operand, or if the loop is not a LEFT JOIN.
** operand, or if the loop is not an OUTER JOIN.
*/
if( (pTerm->eOperator & 0x3f)!=0
|| (pWC->pWInfo->pTabList->a[pLoop->iTab].fg.jointype & JT_LEFT)==0
|| (pWC->pWInfo->pTabList->a[pLoop->iTab].fg.jointype
& (JT_LEFT|JT_LTORJ))==0
){
pLoop->wsFlags |= WHERE_SELFCULL;
}
@ -2831,9 +2834,10 @@ static int whereLoopAddBtreeIndex(
if( pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT ) continue;
/* tag-20191211-001: Do not allow constraints from the WHERE clause to
** be used by the right table of a LEFT JOIN. Only constraints in the
** be used by the right table of a LEFT JOIN nor by the left table of a
** RIGHT JOIN. Only constraints in the
** ON clause are allowed. See tag-20191211-002 for the vtab equivalent. */
if( (pSrc->fg.jointype & JT_LEFT)!=0
if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ))!=0
&& !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
){
continue;
@ -3313,13 +3317,14 @@ static int whereLoopAddBtree(
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
/* Automatic indexes */
if( !pBuilder->pOrSet /* Not part of an OR optimization */
&& (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0
&& (pWInfo->wctrlFlags & (WHERE_RIGHT_JOIN|WHERE_OR_SUBCLAUSE))==0
&& (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0
&& !pSrc->fg.isIndexedBy /* Has no INDEXED BY clause */
&& !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */
&& HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */
&& !pSrc->fg.isCorrelated /* Not a correlated subquery */
&& !pSrc->fg.isRecursive /* Not a recursive common table expression. */
&& (pSrc->fg.jointype & JT_RIGHT)==0 /* Not the right tab of a RIGHT JOIN */
){
/* Generate auto-index WhereLoops */
LogEst rLogSize; /* Logarithm of the number of rows in the table */
@ -4011,6 +4016,9 @@ static int whereLoopAddOr(
pItem = pWInfo->pTabList->a + pNew->iTab;
iCur = pItem->iCursor;
/* The multi-index OR optimization does not work for RIGHT and FULL JOIN */
if( pItem->fg.jointype & JT_RIGHT ) return SQLITE_OK;
for(pTerm=pWC->a; pTerm<pWCEnd && rc==SQLITE_OK; pTerm++){
if( (pTerm->eOperator & WO_OR)!=0
&& (pTerm->u.pOrInfo->indexable & pNew->maskSelf)!=0
@ -4135,9 +4143,9 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
pNew->iTab = iTab;
pBuilder->iPlanLimit += SQLITE_QUERY_PLANNER_LIMIT_INCR;
pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor);
if( (pItem->fg.jointype & (JT_LEFT|JT_CROSS))!=0 ){
if( (pItem->fg.jointype & (JT_OUTER|JT_CROSS))!=0 ){
/* This condition is true when pItem is the FROM clause term on the
** right-hand-side of a LEFT or CROSS JOIN. */
** right-hand-side of a OUTER or CROSS JOIN. */
mPrereq = mPrior;
}else{
mPrereq = 0;
@ -4146,7 +4154,7 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
if( IsVirtual(pItem->pTab) ){
SrcItem *p;
for(p=&pItem[1]; p<pEnd; p++){
if( mUnusable || (p->fg.jointype & (JT_LEFT|JT_CROSS)) ){
if( mUnusable || (p->fg.jointype & (JT_OUTER|JT_CROSS)) ){
mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor);
}
}
@ -5762,8 +5770,10 @@ WhereInfo *sqlite3WhereBegin(
/* noop */
}else
#endif
if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
&& (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){
if( ((pLoop->wsFlags & WHERE_IDX_ONLY)==0
&& (wctrlFlags & WHERE_OR_SUBCLAUSE)==0)
|| (pTabItem->fg.jointype & (JT_LTORJ|JT_RIGHT))!=0
){
int op = OP_OpenRead;
if( pWInfo->eOnePass!=ONEPASS_OFF ){
op = OP_OpenWrite;
@ -5865,6 +5875,37 @@ WhereInfo *sqlite3WhereBegin(
}
}
if( iDb>=0 ) sqlite3CodeVerifySchema(pParse, iDb);
if( (pTabItem->fg.jointype & JT_RIGHT)!=0
&& (pLevel->pRJ = sqlite3WhereMalloc(pWInfo, sizeof(WhereRightJoin)))!=0
){
WhereRightJoin *pRJ = pLevel->pRJ;
pRJ->iMatch = pParse->nTab++;
pRJ->regBloom = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Blob, 65536, pRJ->regBloom);
pRJ->regReturn = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Null, 0, pRJ->regReturn);
assert( pTab==pTabItem->pTab );
if( HasRowid(pTab) ){
KeyInfo *pInfo;
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRJ->iMatch, 1);
pInfo = sqlite3KeyInfoAlloc(pParse->db, 1, 0);
if( pInfo ){
pInfo->aColl[0] = 0;
pInfo->aSortFlags[0] = 0;
sqlite3VdbeAppendP4(v, pInfo, P4_KEYINFO);
}
}else{
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRJ->iMatch, pPk->nKeyCol);
sqlite3VdbeSetP4KeyInfo(pParse, pPk);
}
pLoop->wsFlags &= ~WHERE_IDX_ONLY;
/* The nature of RIGHT JOIN processing is such that it messes up
** the output order. So omit any ORDER BY/GROUP BY elimination
** optimizations. We need to do an actual sort for RIGHT JOIN. */
pWInfo->nOBSat = 0;
pWInfo->eDistinct = WHERE_DISTINCT_UNORDERED;
}
}
pWInfo->iTop = sqlite3VdbeCurrentAddr(v);
if( db->mallocFailed ) goto whereBeginError;
@ -5977,6 +6018,17 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
for(i=pWInfo->nLevel-1; i>=0; i--){
int addr;
pLevel = &pWInfo->a[i];
if( pLevel->pRJ ){
/* Terminate the subroutine that forms the interior of the loop of
** the RIGHT JOIN table */
WhereRightJoin *pRJ = pLevel->pRJ;
sqlite3VdbeResolveLabel(v, pLevel->addrCont);
pLevel->addrCont = 0;
sqlite3VdbeAddOp3(v, OP_Return, pRJ->regReturn, pRJ->addrSubrtn, 1);
VdbeCoverage(v);
assert( pParse->withinRJSubrtn>0 );
pParse->withinRJSubrtn--;
}
pLoop = pLevel->pWLoop;
if( pLevel->op!=OP_Noop ){
#ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT
@ -6004,7 +6056,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
}
#endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */
/* The common case: Advance to the next row */
sqlite3VdbeResolveLabel(v, pLevel->addrCont);
if( pLevel->addrCont ) sqlite3VdbeResolveLabel(v, pLevel->addrCont);
sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3);
sqlite3VdbeChangeP5(v, pLevel->p5);
VdbeCoverage(v);
@ -6019,7 +6071,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
#ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT
if( addrSeek ) sqlite3VdbeJumpHere(v, addrSeek);
#endif
}else{
}else if( pLevel->addrCont ){
sqlite3VdbeResolveLabel(v, pLevel->addrCont);
}
if( (pLoop->wsFlags & WHERE_IN_ABLE)!=0 && pLevel->u.in.nIn>0 ){
@ -6069,6 +6121,10 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
}
}
sqlite3VdbeResolveLabel(v, pLevel->addrBrk);
if( pLevel->pRJ ){
sqlite3VdbeAddOp3(v, OP_Return, pLevel->pRJ->regReturn, 0, 1);
VdbeCoverage(v);
}
if( pLevel->addrSkip ){
sqlite3VdbeGoto(v, pLevel->addrSkip);
VdbeComment((v, "next skip-scan on %s", pLoop->u.btree.pIndex->zName));
@ -6112,11 +6168,6 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
pWInfo->pTabList->a[pLevel->iFrom].pTab->zName));
}
/* The "break" point is here, just past the end of the outer loop.
** Set it.
*/
sqlite3VdbeResolveLabel(v, pWInfo->iBreak);
assert( pWInfo->nLevel<=pTabList->nSrc );
for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){
int k, last;
@ -6127,6 +6178,15 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
assert( pTab!=0 );
pLoop = pLevel->pWLoop;
/* Do RIGHT JOIN processing. Generate code that will output the
** unmatched rows of the right operand of the RIGHT JOIN with
** all of the columns of the left operand set to NULL.
*/
if( pLevel->pRJ ){
sqlite3WhereRightJoinLoop(pWInfo, i, pLevel);
continue;
}
/* For a co-routine, change all OP_Column references to the table of
** the co-routine into OP_Copy of result contained in a register.
** OP_Rowid becomes OP_Null.
@ -6138,29 +6198,6 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
continue;
}
#ifdef SQLITE_ENABLE_EARLY_CURSOR_CLOSE
/* Close all of the cursors that were opened by sqlite3WhereBegin.
** Except, do not close cursors that will be reused by the OR optimization
** (WHERE_OR_SUBCLAUSE). And do not close the OP_OpenWrite cursors
** created for the ONEPASS optimization.
*/
if( (pTab->tabFlags & TF_Ephemeral)==0
&& !IsView(pTab)
&& (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0
){
int ws = pLoop->wsFlags;
if( pWInfo->eOnePass==ONEPASS_OFF && (ws & WHERE_IDX_ONLY)==0 ){
sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor);
}
if( (ws & WHERE_INDEXED)!=0
&& (ws & (WHERE_IPK|WHERE_AUTO_INDEX))==0
&& pLevel->iIdxCur!=pWInfo->aiCurOnePass[1]
){
sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur);
}
}
#endif
/* If this scan uses an index, make VDBE code substitutions to read data
** from the index instead of from the table where possible. In some cases
** this optimization prevents the table from ever being read, which can
@ -6261,6 +6298,11 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
}
}
/* The "break" point is here, just past the end of the outer loop.
** Set it.
*/
sqlite3VdbeResolveLabel(v, pWInfo->iBreak);
/* Final cleanup
*/
if( pWInfo->pExprMods ) whereUndoExprMods(pWInfo);

View File

@ -33,6 +33,7 @@ typedef struct WhereScan WhereScan;
typedef struct WhereOrCost WhereOrCost;
typedef struct WhereOrSet WhereOrSet;
typedef struct WhereMemBlock WhereMemBlock;
typedef struct WhereRightJoin WhereRightJoin;
/*
** This object is a header on a block of allocated memory that will be
@ -43,6 +44,16 @@ struct WhereMemBlock {
u8 sz; /* Bytes of space */
};
/*
** Extra information attached to a WhereLevel that is a RIGHT JOIN.
*/
struct WhereRightJoin {
int iMatch; /* Cursor used to determine prior matched rows */
int regBloom; /* Bloom filter for iRJMatch */
int regReturn; /* Return register for the interior subroutine */
int addrSubrtn; /* Starting address for the interior subroutine */
};
/*
** This object contains information needed to implement a single nested
** loop in WHERE clause.
@ -75,6 +86,7 @@ struct WhereLevel {
int addrLikeRep; /* LIKE range processing address */
#endif
int regFilter; /* Bloom filter */
WhereRightJoin *pRJ; /* Extra information for RIGHT JOIN */
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 end the loop */
@ -552,6 +564,11 @@ Bitmask sqlite3WhereCodeOneLoopStart(
WhereLevel *pLevel, /* The current level pointer */
Bitmask notReady /* Which tables are currently available */
);
SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
WhereInfo *pWInfo,
int iLevel,
WhereLevel *pLevel
);
/* whereexpr.c: */
void sqlite3WhereClauseInit(WhereClause*,WhereInfo*);

View File

@ -204,6 +204,9 @@ int sqlite3WhereExplainOneScan(
pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
}
#endif
if( pItem->fg.jointype & JT_LEFT ){
sqlite3_str_appendf(&str, " LEFT-JOIN");
}
#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
if( pLoop->nOut>=10 ){
sqlite3_str_appendf(&str, " (~%llu rows)",
@ -1150,7 +1153,7 @@ static void codeDeferredSeek(
pWInfo->bDeferredSeek = 1;
sqlite3VdbeAddOp3(v, OP_DeferredSeek, iIdxCur, 0, iCur);
if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)
if( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))
&& DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask)
){
int i;
@ -1502,7 +1505,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
** initialize a memory cell that records if this table matches any
** row of the left table of the join.
*/
assert( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)
assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))
|| pLevel->iFrom>0 || (pTabItem[0].fg.jointype & JT_LEFT)==0
);
if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){
@ -1513,7 +1516,10 @@ Bitmask sqlite3WhereCodeOneLoopStart(
/* Compute a safe address to jump to if we discover that the table for
** this loop is empty and can never contribute content. */
for(j=iLevel; j>0 && pWInfo->a[j].iLeftJoin==0; j--){}
for(j=iLevel; j>0; j--){
if( pWInfo->a[j].iLeftJoin ) break;
if( pWInfo->a[j].pRJ ) break;
}
addrHalt = pWInfo->a[j].addrBrk;
/* Special case of a FROM clause subquery implemented as a co-routine */
@ -2140,7 +2146,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
/* Seek the table cursor, if required */
omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0
&& (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0;
&& (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0;
if( omitTable ){
/* pIdx is a covering index. No need to access the main table. */
}else if( HasRowid(pIdx->pTable) ){
@ -2174,7 +2180,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
** move forward to the next index.
** https://sqlite.org/src/info/4e8e4857d32d401f
*/
if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){
if( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0 ){
whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo);
}
@ -2193,7 +2199,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
/* The following assert() is not a requirement, merely an observation:
** The OR-optimization doesn't work for the right hand table of
** a LEFT JOIN: */
assert( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 );
assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0 );
}
/* Record the instruction used to terminate the loop. */
@ -2605,7 +2611,9 @@ Bitmask sqlite3WhereCodeOneLoopStart(
}
pE = pTerm->pExpr;
assert( pE!=0 );
if( (pTabItem->fg.jointype&JT_LEFT) && !ExprHasProperty(pE,EP_FromJoin) ){
if( (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ))
&& !ExprHasProperty(pE,EP_FromJoin)
){
continue;
}
@ -2667,7 +2675,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) continue;
if( (pTerm->eOperator & WO_EQUIV)==0 ) continue;
if( pTerm->leftCursor!=iCur ) continue;
if( pTabItem->fg.jointype & JT_LEFT ) continue;
if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ) ) continue;
pE = pTerm->pExpr;
#ifdef WHERETRACE_ENABLED /* 0x800 */
if( sqlite3WhereTrace & 0x800 ){
@ -2698,6 +2706,47 @@ Bitmask sqlite3WhereCodeOneLoopStart(
pAlt->wtFlags |= TERM_CODED;
}
/* For a RIGHT OUTER JOIN, record the fact that the current row has
** been matched at least once.
*/
if( pLevel->pRJ ){
Table *pTab;
int nPk;
int r;
int jmp1 = 0;
WhereRightJoin *pRJ = pLevel->pRJ;
/* pTab is the right-hand table of the RIGHT JOIN. Generate code that
** will record that the current row of that table has been matched at
** least once. This is accomplished by storing the PK for the row in
** both the iMatch index and the regBloom Bloom filter.
*/
pTab = pWInfo->pTabList->a[pLevel->iFrom].pTab;
if( HasRowid(pTab) ){
r = sqlite3GetTempRange(pParse, 2);
sqlite3ExprCodeGetColumnOfTable(v, pTab, pLevel->iTabCur, -1, r+1);
nPk = 1;
}else{
int iPk;
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
nPk = pPk->nKeyCol;
r = sqlite3GetTempRange(pParse, nPk+1);
for(iPk=0; iPk<nPk; iPk++){
int iCol = pPk->aiColumn[iPk];
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol,r+1+iPk);
}
}
jmp1 = sqlite3VdbeAddOp4Int(v, OP_Found, pRJ->iMatch, 0, r+1, nPk);
VdbeCoverage(v);
VdbeComment((v, "match against %s", pTab->zName));
sqlite3VdbeAddOp3(v, OP_MakeRecord, r+1, nPk, r);
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pRJ->iMatch, r, r+1, nPk);
sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pRJ->regBloom, 0, r+1, nPk);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
sqlite3VdbeJumpHere(v, jmp1);
sqlite3ReleaseTempRange(pParse, r, nPk+1);
}
/* For a LEFT OUTER JOIN, generate code that will record the fact that
** at least one row of the right table has matched the left table.
*/
@ -2713,12 +2762,27 @@ Bitmask sqlite3WhereCodeOneLoopStart(
assert( pWInfo->untestedTerms );
continue;
}
if( pTabItem->fg.jointype & JT_LTORJ ) continue;
assert( pTerm->pExpr );
sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
pTerm->wtFlags |= TERM_CODED;
}
}
if( pLevel->pRJ ){
/* Create a subroutine used to process all interior loops and code
** of the RIGHT JOIN. During normal operation, the subroutine will
** be in-line with the rest of the code. But at the end, a separate
** loop will run that invokes this subroutine for unmatched rows
** of pTab, with all tables to left begin set to NULL.
*/
WhereRightJoin *pRJ = pLevel->pRJ;
sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pRJ->regReturn);
pRJ->addrSubrtn = sqlite3VdbeCurrentAddr(v);
assert( pParse->withinRJSubrtn < 255 );
pParse->withinRJSubrtn++;
}
#if WHERETRACE_ENABLED /* 0x20800 */
if( sqlite3WhereTrace & 0x20000 ){
sqlite3DebugPrintf("All WHERE-clause terms after coding level %d:\n",
@ -2732,3 +2796,87 @@ Bitmask sqlite3WhereCodeOneLoopStart(
#endif
return pLevel->notReady;
}
/*
** Generate the code for the loop that finds all non-matched terms
** for a RIGHT JOIN.
*/
SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
WhereInfo *pWInfo,
int iLevel,
WhereLevel *pLevel
){
Parse *pParse = pWInfo->pParse;
Vdbe *v = pParse->pVdbe;
WhereRightJoin *pRJ = pLevel->pRJ;
Expr *pSubWhere = 0;
WhereClause *pWC = &pWInfo->sWC;
WhereInfo *pSubWInfo;
WhereLoop *pLoop = pLevel->pWLoop;
SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
SrcList sFrom;
Bitmask mAll = 0;
int k;
ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pTab->zName));
for(k=0; k<iLevel; k++){
int iIdxCur;
mAll |= pWInfo->a[k].pWLoop->maskSelf;
sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur);
iIdxCur = pWInfo->a[k].iIdxCur;
if( iIdxCur ){
sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur);
}
}
if( (pTabItem->fg.jointype & JT_LTORJ)==0 ){
mAll |= pLoop->maskSelf;
for(k=0; k<pWC->nTerm; k++){
WhereTerm *pTerm = &pWC->a[k];
if( pTerm->wtFlags & TERM_VIRTUAL ) break;
if( pTerm->prereqAll & ~mAll ) continue;
if( ExprHasProperty(pTerm->pExpr, EP_FromJoin|EP_InnerJoin) ) continue;
pSubWhere = sqlite3ExprAnd(pParse, pSubWhere,
sqlite3ExprDup(pParse->db, pTerm->pExpr, 0));
}
}
sFrom.nSrc = 1;
sFrom.nAlloc = 1;
memcpy(&sFrom.a[0], pTabItem, sizeof(SrcItem));
sFrom.a[0].fg.jointype = 0;
assert( pParse->withinRJSubrtn < 100 );
pParse->withinRJSubrtn++;
pSubWInfo = sqlite3WhereBegin(pParse, &sFrom, pSubWhere, 0, 0, 0,
WHERE_RIGHT_JOIN, 0);
if( pSubWInfo ){
int iCur = pLevel->iTabCur;
int r = ++pParse->nMem;
int nPk;
int jmp;
int addrCont = sqlite3WhereContinueLabel(pSubWInfo);
Table *pTab = pTabItem->pTab;
if( HasRowid(pTab) ){
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, -1, r);
nPk = 1;
}else{
int iPk;
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
nPk = pPk->nKeyCol;
pParse->nMem += nPk - 1;
for(iPk=0; iPk<nPk; iPk++){
int iCol = pPk->aiColumn[iPk];
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol,r+iPk);
}
}
jmp = sqlite3VdbeAddOp4Int(v, OP_Filter, pRJ->regBloom, 0, r, nPk);
VdbeCoverage(v);
sqlite3VdbeAddOp4Int(v, OP_Found, pRJ->iMatch, addrCont, r, nPk);
VdbeCoverage(v);
sqlite3VdbeJumpHere(v, jmp);
sqlite3VdbeAddOp2(v, OP_Gosub, pRJ->regReturn, pRJ->addrSubrtn);
sqlite3WhereEnd(pSubWInfo);
}
sqlite3ExprDelete(pParse->db, pSubWhere);
ExplainQueryPlanPop(pParse);
assert( pParse->withinRJSubrtn>0 );
pParse->withinRJSubrtn--;
}

View File

@ -1809,6 +1809,7 @@ void sqlite3WhereTabFuncArgs(
if( pArgs==0 ) return;
for(j=k=0; j<pArgs->nExpr; j++){
Expr *pRhs;
u32 joinType;
while( k<pTab->nCol && (pTab->aCol[k].colFlags & COLFLAG_HIDDEN)==0 ){k++;}
if( k>=pTab->nCol ){
sqlite3ErrorMsg(pParse, "too many arguments on %s() - max %d",
@ -1825,9 +1826,12 @@ void sqlite3WhereTabFuncArgs(
pRhs = sqlite3PExpr(pParse, TK_UPLUS,
sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0);
pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs);
if( pItem->fg.jointype & JT_LEFT ){
sqlite3SetJoinExpr(pTerm, pItem->iCursor);
if( pItem->fg.jointype & (JT_LEFT|JT_LTORJ) ){
joinType = EP_FromJoin;
}else{
joinType = EP_InnerJoin;
}
sqlite3SetJoinExpr(pTerm, pItem->iCursor, joinType);
whereClauseInsert(pWC, pTerm, TERM_DYNAMIC);
}
}

View File

@ -30,11 +30,24 @@ do_execsql_test affinity3-100 {
FROM customer c
LEFT JOIN apr i ON i.id=c.id;
CREATE VIEW v1rj AS
SELECT c.id, i.apr
FROM apr i
RIGHT JOIN customer c ON i.id=c.id;
CREATE VIEW v2 AS
SELECT c.id, v1.apr
FROM customer c
LEFT JOIN v1 ON v1.id=c.id;
CREATE VIEW v2rj AS
SELECT c.id, v1.apr
FROM v1 RIGHT JOIN customer c ON v1.id=c.id;
CREATE VIEW v2rjrj AS
SELECT c.id, v1rj.apr
FROM v1rj RIGHT JOIN customer c ON v1rj.id=c.id;
INSERT INTO customer (id) VALUES (1);
INSERT INTO apr (id, apr) VALUES (1, 12);
INSERT INTO customer (id) VALUES (2);
@ -44,16 +57,35 @@ do_execsql_test affinity3-110 {
PRAGMA automatic_index=ON;
SELECT id, (apr / 100), typeof(apr) apr_type FROM v1;
} {1 0.12 real 2 0.1201 real}
do_execsql_test affinity3-111 {
PRAGMA automatic_index=ON;
SELECT id, (apr / 100), typeof(apr) apr_type FROM v1rj;
} {1 0.12 real 2 0.1201 real}
do_execsql_test affinity3-120 {
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2;
} {1 0.12 real 2 0.1201 real}
do_execsql_test affinity3-121 {
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2rj;
} {1 0.12 real 2 0.1201 real}
do_execsql_test affinity3-122 {
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2rjrj;
} {1 0.12 real 2 0.1201 real}
do_execsql_test affinity3-130 {
PRAGMA automatic_index=OFF;
SELECT id, (apr / 100), typeof(apr) apr_type FROM v1;
} {1 0.12 real 2 0.1201 real}
do_execsql_test affinity3-131 {
SELECT id, (apr / 100), typeof(apr) apr_type FROM v1rj;
} {1 0.12 real 2 0.1201 real}
do_execsql_test affinity3-140 {
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2;
} {1 0.12 real 2 0.1201 real}
do_execsql_test affinity3-141 {
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2rj;
} {1 0.12 real 2 0.1201 real}
do_execsql_test affinity3-142 {
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2rjrj;
} {1 0.12 real 2 0.1201 real}
# Ticket https://www.sqlite.org/src/info/7ffd1ca1d2ad4ecf (2017-01-16)
# Incorrect affinity when using automatic indexes

View File

@ -137,6 +137,17 @@ do_test aggnested-3.1 {
GROUP BY curr.id1);
}
} {1 1}
do_test aggnested-3.1-rj {
db eval {
SELECT
(SELECT sum(value2==xyz) FROM t2)
FROM
(SELECT curr.value1 as xyz
FROM t1 AS other RIGHT JOIN t1 AS curr
GROUP BY curr.id1);
}
} {1 1}
do_test aggnested-3.2 {
db eval {
DROP TABLE IF EXISTS t1;

View File

@ -283,7 +283,7 @@ do_eqp_test autoindex1-600a {
| `--CORRELATED SCALAR SUBQUERY xxxxxx
| `--SEARCH later USING COVERING INDEX sqlite_autoindex_flock_owner_1 (flock_no=? AND owner_change_date>? AND owner_change_date<?)
|--SCAN x USING INDEX sheep_reg_flock_index
`--SEARCH y USING AUTOMATIC COVERING INDEX (sheep_no=?)
`--SEARCH y USING AUTOMATIC COVERING INDEX (sheep_no=?) LEFT-JOIN
}

View File

@ -32,12 +32,21 @@ do_execsql_test autoindex4-1.1 {
do_execsql_test autoindex4-1.2 {
SELECT *, '|' FROM t1 LEFT JOIN t2 ON a=234 AND x=555;
} {123 abc {} {} | 234 def {} {} | 234 ghi {} {} | 345 jkl {} {} |}
do_execsql_test autoindex4-1.2-rj {
SELECT t1.*, t2.*, '|' FROM t2 RIGHT JOIN t1 ON a=234 AND x=555;
} {123 abc {} {} | 234 def {} {} | 234 ghi {} {} | 345 jkl {} {} |}
do_execsql_test autoindex4-1.3 {
SELECT *, '|' FROM t1 LEFT JOIN t2 ON x=555 WHERE a=234;
} {234 def {} {} | 234 ghi {} {} |}
do_execsql_test autoindex4-1.3-rj {
SELECT t1.*, t2.*, '|' FROM t2 RIGHT JOIN t1 ON x=555 WHERE a=234;
} {234 def {} {} | 234 ghi {} {} |}
do_execsql_test autoindex4-1.4 {
SELECT *, '|' FROM t1 LEFT JOIN t2 WHERE a=234 AND x=555;
} {}
do_execsql_test autoindex4-1.4-rj {
SELECT t1.*, t2.*, '|' FROM t2 RIGHT JOIN t1 WHERE a=234 AND x=555;
} {}
do_execsql_test autoindex4-2.0 {
@ -69,6 +78,14 @@ do_execsql_test autoindex4-3.0 {
ORDER BY Items.ItemName;
} {Item1 Item2}
do_execsql_test autoindex4-3.1 {
SELECT Items.ItemName
FROM A
RIGHT JOIN Items ON (A.Name = Items.ItemName and Items.ItemName = 'dummy')
LEFT JOIN B ON (B.Name = Items.ItemName)
WHERE Items.Name = 'Parent'
ORDER BY Items.ItemName;
} {Item1 Item2}
do_execsql_test autoindex4-3.10 {
CREATE INDEX Items_x1 ON Items(ItemName,Name) WHERE ItemName = 'dummy';
SELECT Items.ItemName
@ -78,6 +95,14 @@ do_execsql_test autoindex4-3.1 {
WHERE Items.Name = 'Parent'
ORDER BY Items.ItemName;
} {Item1 Item2}
do_execsql_test autoindex4-3.11 {
SELECT Items.ItemName
FROM A
RIGHT JOIN Items ON (A.Name = Items.ItemName and Items.ItemName = 'dummy')
LEFT JOIN B ON (B.Name = Items.ItemName)
WHERE Items.Name = 'Parent'
ORDER BY Items.ItemName;
} {Item1 Item2}
# 2021-11-30 - Enhancement to help the automatic index mechanism to
# create a partial index more often.

View File

@ -148,6 +148,9 @@ do_execsql_test btree01-2.1 {
INSERT INTO t2(y) VALUES(198),(187),(100);
SELECT y, c FROM t2 LEFT JOIN t1 ON y=a ORDER BY x;
} {198 99 187 {} 100 50}
do_execsql_test btree01-2.2 {
SELECT y, c FROM t1 RIGHT JOIN t2 ON y=a ORDER BY x;
} {198 99 187 {} 100 50}
finish_test

View File

@ -684,16 +684,34 @@ do_test collate2-5.3 {
SELECT collate2t1.b FROM collate2t2 NATURAL JOIN collate2t1;
}
} {aa}
do_test collate2-5.4 {
do_test collate2-5.4.1 {
execsql {
SELECT collate2t2.b FROM collate2t1 LEFT OUTER JOIN collate2t2 USING (b) order by collate2t1.oid;
SELECT collate2t2.b FROM collate2t1 LEFT JOIN collate2t2 USING (b) order by collate2t1.oid;
}
} {{} aa {} {} {} aa {} {} {} aa {} {} {} aa {} {} {}}
do_test collate2-5.5 {
do_test collate2-5.4.2 {
execsql {
SELECT collate2t2.b FROM collate2t2 RIGHT JOIN collate2t1 ON collate2t1.b=collate2t2.b
ORDER BY collate2t1.oid;
}
} {{} aa {} {} {} aa {} {} {} aa {} {} {} aa {} {} {}}
do_test collate2-5.4.3 {
execsql {
SELECT collate2t2.b FROM collate2t1 LEFT JOIN collate2t2 ON collate2t2.b=collate2t1.b
ORDER BY collate2t1.oid;
}
} {{} aa {} {} {} {} {} {} {} {} {} {} {} {} {} {} {}}
do_test collate2-5.5.1 {
execsql {
SELECT collate2t1.b, collate2t2.b FROM collate2t2 LEFT OUTER JOIN collate2t1 USING (b);
}
} {aa aa}
do_test collate2-5.5.2 {
execsql {
SELECT collate2t1.b, collate2t2.b
FROM collate2t1 RIGHT JOIN collate2t2 ON collate2t2.b=collate2t1.b
}
} {aa aa}
do_execsql_test 6.1 {
CREATE TABLE t1(x);

View File

@ -100,7 +100,7 @@ do_eqp_test 4.2 {
|--MATERIALIZE rr
| `--SCAN ft4 VIRTUAL TABLE INDEX 3:
|--SCAN t4
`--SEARCH rr USING AUTOMATIC COVERING INDEX (docid=?)
`--SEARCH rr USING AUTOMATIC COVERING INDEX (docid=?) LEFT-JOIN
}
finish_test

View File

@ -320,7 +320,7 @@ do_eqp_test index6-8.1 {
} {
QUERY PLAN
|--SCAN t8a
`--SEARCH t8b USING INDEX i8c (y=?)
`--SEARCH t8b USING INDEX i8c (y=?) LEFT-JOIN
}
do_execsql_test index6-8.2 {

View File

@ -272,11 +272,13 @@ do_test join-2.2 {
SELECT * FROM t2 NATURAL LEFT OUTER JOIN t1;
}
} {1 2 3 {} 2 3 4 1 3 4 5 2}
do_test join-2.3 {
catchsql {
SELECT * FROM t1 NATURAL RIGHT OUTER JOIN t2;
}
} {1 {RIGHT and FULL OUTER JOINs are not currently supported}}
#do_test join-2.3 {
# catchsql {
# SELECT * FROM t1 NATURAL RIGHT OUTER JOIN t2;
# }
#} {1 {RIGHT and FULL OUTER JOINs are not currently supported}}
do_test join-2.4 {
execsql {
SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.d
@ -330,32 +332,32 @@ do_test join-3.7 {
catchsql {
SELECT * FROM t1 INNER OUTER JOIN t2;
}
} {1 {unknown or unsupported join type: INNER OUTER}}
} {1 {unknown join type: INNER OUTER}}
do_test join-3.8 {
catchsql {
SELECT * FROM t1 INNER OUTER CROSS JOIN t2;
}
} {1 {unknown or unsupported join type: INNER OUTER CROSS}}
} {1 {unknown join type: INNER OUTER CROSS}}
do_test join-3.9 {
catchsql {
SELECT * FROM t1 OUTER NATURAL INNER JOIN t2;
}
} {1 {unknown or unsupported join type: OUTER NATURAL INNER}}
} {1 {unknown join type: OUTER NATURAL INNER}}
do_test join-3.10 {
catchsql {
SELECT * FROM t1 LEFT BOGUS JOIN t2;
}
} {1 {unknown or unsupported join type: LEFT BOGUS}}
} {1 {unknown join type: LEFT BOGUS}}
do_test join-3.11 {
catchsql {
SELECT * FROM t1 INNER BOGUS CROSS JOIN t2;
}
} {1 {unknown or unsupported join type: INNER BOGUS CROSS}}
} {1 {unknown join type: INNER BOGUS CROSS}}
do_test join-3.12 {
catchsql {
SELECT * FROM t1 NATURAL AWK SED JOIN t2;
}
} {1 {unknown or unsupported join type: NATURAL AWK SED}}
} {1 {unknown join type: NATURAL AWK SED}}
do_test join-4.1 {
execsql {
@ -1039,5 +1041,14 @@ do_execsql_test join-25.1 {
SELECT count(*) FROM v0 LEFT JOIN t0 ON v0.c0;
} {1}
# 2022-04-21 Parser issue detected by dbsqlfuzz
#
reset_db
do_catchsql_test join-26.1 {
CREATE TABLE t4(a,b);
CREATE TABLE t5(a,c);
CREATE TABLE t6(a,d);
SELECT * FROM t5 JOIN ((t4 JOIN (t5 JOIN t6)) t7);
} {/1 {.*}/}
finish_test

View File

@ -63,6 +63,12 @@ do_test join2-1.6 {
t1 NATURAL LEFT OUTER JOIN t2 NATURAL JOIN t3
}
} {1 11 111 1111}
do_test join2-1.6-rj {
execsql {
SELECT * FROM
t2 NATURAL RIGHT OUTER JOIN t1 NATURAL JOIN t3
}
} {11 111 1 1111}
ifcapable subquery {
do_test join2-1.7 {
execsql {
@ -70,6 +76,12 @@ ifcapable subquery {
t1 NATURAL LEFT OUTER JOIN (t2 NATURAL JOIN t3)
}
} {1 11 111 1111 2 22 {} {} 3 33 {} {}}
do_test join2-1.7-rj {
execsql {
SELECT a, b, c, d FROM
t2 NATURAL JOIN t3 NATURAL RIGHT JOIN t1
}
} {1 11 111 1111 2 22 {} {} 3 33 {} {}}
}
#-------------------------------------------------------------------------
@ -88,6 +100,9 @@ do_execsql_test 2.0 {
do_catchsql_test 2.1 {
SELECT * FROM aa LEFT JOIN cc ON (a=b) JOIN bb ON (b=coalesce(c,1));
} {1 {ON clause references tables to its right}}
do_catchsql_test 2.1b {
SELECT * FROM aa RIGHT JOIN cc ON (a=b) JOIN bb ON (b=coalesce(c,1));
} {1 {ON clause references tables to its right}}
do_catchsql_test 2.2 {
SELECT * FROM aa JOIN cc ON (a=b) JOIN bb ON (b=c);
} {0 {one one one}}
@ -114,7 +129,7 @@ do_eqp_test 3.1 {
} {
QUERY PLAN
|--SCAN t1
`--SEARCH t2 USING INTEGER PRIMARY KEY (rowid=?)
`--SEARCH t2 USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
}
do_eqp_test 3.2 {
@ -122,7 +137,7 @@ do_eqp_test 3.2 {
} {
QUERY PLAN
|--SCAN t1
`--SEARCH t2 USING INTEGER PRIMARY KEY (rowid=?)
`--SEARCH t2 USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
}
#-------------------------------------------------------------------------
@ -162,15 +177,15 @@ do_eqp_test 4.1.5 {
} {
QUERY PLAN
|--SCAN c1
|--SEARCH c2 USING INTEGER PRIMARY KEY (rowid=?)
`--SEARCH c3 USING INTEGER PRIMARY KEY (rowid=?)
|--SEARCH c2 USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
`--SEARCH c3 USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
}
do_eqp_test 4.1.6 {
SELECT v1, v3 FROM c1 LEFT JOIN c2 ON (c2.k=v1) LEFT JOIN c3 ON (c3.k=v1+1);
} {
QUERY PLAN
|--SCAN c1
`--SEARCH c3 USING INTEGER PRIMARY KEY (rowid=?)
`--SEARCH c3 USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
}
do_execsql_test 4.2.0 {
@ -209,15 +224,15 @@ do_eqp_test 4.2.5 {
} {
QUERY PLAN
|--SCAN c1
|--SEARCH c2 USING INDEX sqlite_autoindex_c2_1 (k=?)
`--SEARCH c3 USING INDEX sqlite_autoindex_c3_1 (k=?)
|--SEARCH c2 USING INDEX sqlite_autoindex_c2_1 (k=?) LEFT-JOIN
`--SEARCH c3 USING INDEX sqlite_autoindex_c3_1 (k=?) LEFT-JOIN
}
do_eqp_test 4.2.6 {
SELECT v1, v3 FROM c1 LEFT JOIN c2 ON (c2.k=v1) LEFT JOIN c3 ON (c3.k=v1+1);
} {
QUERY PLAN
|--SCAN c1
`--SEARCH c3 USING INDEX sqlite_autoindex_c3_1 (k=?)
`--SEARCH c3 USING INDEX sqlite_autoindex_c3_1 (k=?) LEFT-JOIN
}
# 2017-11-23 (Thanksgiving day)

View File

@ -280,9 +280,9 @@ do_eqp_test 7.2 {
|--SCAN t1
`--MULTI-INDEX OR
|--INDEX 1
| `--SEARCH t2 USING INDEX t2xy (x=? AND y=?)
| `--SEARCH t2 USING INDEX t2xy (x=? AND y=?) LEFT-JOIN
`--INDEX 2
`--SEARCH t2 USING INDEX t2xy (x=? AND y=?)
`--SEARCH t2 USING INDEX t2xy (x=? AND y=?) LEFT-JOIN
}
do_execsql_test 7.3 {
@ -303,7 +303,7 @@ do_eqp_test 7.4 {
} {
QUERY PLAN
|--SCAN t3
`--SEARCH t4 USING INDEX t4xz (x=?)
`--SEARCH t4 USING INDEX t4xz (x=?) LEFT-JOIN
}
do_eqp_test 7.4b {
SELECT * FROM t3 CROSS JOIN t4 ON (t4.x = t3.x) WHERE (+t4.y = ? OR t4.z = ?);

289
test/join7.test Normal file
View File

@ -0,0 +1,289 @@
# 2022-04-09
#
# 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.
#
# This file implements tests for RIGHT and FULL OUTER JOINs.
set testdir [file dirname $argv0]
source $testdir/tester.tcl
foreach {id schema} {
1 {
CREATE TABLE t1(a INT, b INT);
INSERT INTO t1 VALUES(1,2),(1,3),(1,4);
CREATE INDEX t1a ON t1(a);
CREATE TABLE t2(c INT, d INT);
INSERT INTO t2 VALUES(3,33),(4,44),(5,55);
CREATE INDEX t2c ON t2(c);
CREATE VIEW dual(dummy) AS VALUES('x');
}
2 {
CREATE TABLE t1(a INT, b INT);
INSERT INTO t1 VALUES(1,2),(1,3),(1,4);
CREATE INDEX t1ab ON t1(a,b);
CREATE TABLE t2(c INT, d INT);
INSERT INTO t2 VALUES(3,33),(4,44),(5,55);
CREATE INDEX t2cd ON t2(c,d);
CREATE VIEW dual(dummy) AS VALUES('x');
}
3 {
CREATE TABLE t1(a INT, b INT);
INSERT INTO t1 VALUES(1,2),(1,3),(1,4);
CREATE INDEX t1a ON t1(a);
CREATE TABLE t2(c INT, d INT PRIMARY KEY) WITHOUT ROWID;
INSERT INTO t2 VALUES(3,33),(4,44),(5,55);
CREATE INDEX t2c ON t2(c);
CREATE VIEW dual(dummy) AS VALUES('x');
}
4 {
CREATE TABLE t1(a INT, b INT);
INSERT INTO t1 VALUES(1,2),(1,3),(1,4);
CREATE TABLE t2(c INTEGER PRIMARY KEY, d INT);
INSERT INTO t2 VALUES(3,33),(4,44),(5,55);
CREATE VIEW dual(dummy) AS VALUES('x');
}
5 {
CREATE TABLE t1(a INT, b INT);
INSERT INTO t1 VALUES(1,2),(1,3),(1,4);
CREATE TABLE t2(c INT PRIMARY KEY, d INT) WITHOUT ROWID;
INSERT INTO t2 VALUES(3,33),(4,44),(5,55);
CREATE VIEW dual(dummy) AS VALUES('x');
}
6 {
CREATE TABLE t1(a INT, b INT);
INSERT INTO t1 VALUES(1,2),(1,3),(1,4);
CREATE VIEW t2(c,d) AS VALUES(3,33),(4,44),(5,55);
CREATE VIEW dual(dummy) AS VALUES('x');
}
7 {
CREATE VIEW t1(a,b) AS VALUES(1,2),(1,3),(1,4);
CREATE TABLE t2(c INTEGER PRIMARY KEY, d INT);
INSERT INTO t2 VALUES(3,33),(4,44),(5,55);
CREATE VIEW dual(dummy) AS VALUES('x');
}
8 {
CREATE TABLE t1(a INT, b INT);
INSERT INTO t1 VALUES(1,2),(1,3),(1,4);
CREATE TABLE t2(c INT, d INT);
INSERT INTO t2 VALUES(3,33),(4,44),(5,55);
CREATE VIEW dual(dummy) AS VALUES('x');
}
9 {
CREATE TABLE t1(a INT, b INT);
INSERT INTO t1 VALUES(1,2),(1,3),(1,4);
CREATE TABLE t2a(c INTEGER PRIMARY KEY, i1 INT);
CREATE TABLE t2b(i1 INTEGER PRIMARY KEY, d INT);
CREATE VIEW t2(c,d) AS SELECT c, d FROM t2a NATURAL JOIN t2b;
INSERT INTO t2a VALUES(3,93),(4,94),(5,95),(6,96),(7,97);
INSERT INTO t2b VALUES(91,11),(92,22),(93,33),(94,44),(95,55);
CREATE TABLE dual(dummy TEXT);
INSERT INTO dual(dummy) VALUES('x');
}
10 {
CREATE TABLE t1(a INT, b INT, PRIMARY KEY(a,b)) WITHOUT ROWID;
INSERT INTO t1 VALUES(1,2),(1,3),(1,4);
CREATE TABLE t2a(c INTEGER PRIMARY KEY, i1 INT);
CREATE TABLE t2b(i1 INTEGER PRIMARY KEY, d INT);
CREATE VIEW t2(c,d) AS SELECT c, d FROM t2a NATURAL JOIN t2b;
INSERT INTO t2a VALUES(3,93),(4,94),(5,95),(6,96),(7,97);
INSERT INTO t2b VALUES(91,11),(92,22),(93,33),(94,44),(95,55);
CREATE TABLE dual(dummy TEXT);
INSERT INTO dual(dummy) VALUES('x');
}
} {
reset_db
db nullvalue NULL
do_execsql_test join7-$id.setup $schema {}
# Verified against PG-14 for case 1
do_execsql_test join7-$id.10 {
SELECT b, d FROM t1 FULL OUTER JOIN t2 ON b=c ORDER BY +b;
} {
NULL 55
2 NULL
3 33
4 44
}
# Verified against PG-14 for case 1
do_execsql_test join7-$id.20 {
SELECT a, c FROM t1 FULL OUTER JOIN t2 ON b=c ORDER BY +b;
} {
NULL 5
1 NULL
1 3
1 4
}
do_execsql_test join7-$id.30 {
SELECT * FROM t1 FULL OUTER JOIN t2 ON b=c ORDER BY +b;
} {
NULL NULL 5 55
1 2 NULL NULL
1 3 3 33
1 4 4 44
}
do_execsql_test join7-$id.31 {
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c ORDER BY +b;
} {
NULL NULL 5 55
1 2 NULL NULL
1 3 3 33
1 4 4 44
}
do_execsql_test join7-$id.40 {
SELECT * FROM t1 RIGHT OUTER JOIN t2 ON b=c ORDER BY +b;
} {
NULL NULL 5 55
1 3 3 33
1 4 4 44
}
do_execsql_test join7-$id.50 {
SELECT t1.*, t2.* FROM t2 LEFT OUTER JOIN t1 ON b=c ORDER BY +b;
} {
NULL NULL 5 55
1 3 3 33
1 4 4 44
}
do_execsql_test join7-$id.60 {
SELECT * FROM dual JOIN t1 ON true RIGHT OUTER JOIN t2 ON b=c ORDER BY +b;
} {
NULL NULL NULL 5 55
x 1 3 3 33
x 1 4 4 44
}
do_execsql_test join7-$id.70 {
SELECT t1.*, t2.*
FROM t2 LEFT JOIN (dual JOIN t1 ON true) ON b=c ORDER BY +b;
} {
NULL NULL 5 55
1 3 3 33
1 4 4 44
}
do_execsql_test join7-$id.80 {
SELECT * FROM dual CROSS JOIN t1 RIGHT OUTER JOIN t2 ON b=c ORDER BY +b;
} {
NULL NULL NULL 5 55
x 1 3 3 33
x 1 4 4 44
}
do_execsql_test join7-$id.81 {
SELECT dual.*, t1.*, t2.*
FROM t1 CROSS JOIN dual RIGHT OUTER JOIN t2 ON b=c ORDER BY +b;
} {
NULL NULL NULL 5 55
x 1 3 3 33
x 1 4 4 44
}
do_execsql_test join7-$id.90 {
SELECT * FROM t1 LEFT OUTER JOIN t2 ON b=c ORDER BY +b;
} {
1 2 NULL NULL
1 3 3 33
1 4 4 44
}
do_execsql_test join7-$id.100 {
SELECT * FROM t1 FULL OUTER JOIN t2 ON b=c AND a=1 ORDER BY +b;
} {
NULL NULL 5 55
1 2 NULL NULL
1 3 3 33
1 4 4 44
}
do_execsql_test join7-$id.101 {
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c AND a=1 ORDER BY +b;
} {
NULL NULL 5 55
1 2 NULL NULL
1 3 3 33
1 4 4 44
}
# Verified against PG-14 for case 1
do_execsql_test join7-$id.110 {
SELECT * FROM t1 FULL OUTER JOIN t2 ON b=c WHERE a=1 ORDER BY +b;
} {
1 2 NULL NULL
1 3 3 33
1 4 4 44
}
do_execsql_test join7-$id.111 {
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c WHERE a=1 ORDER BY +b;
} {
1 2 NULL NULL
1 3 3 33
1 4 4 44
}
# Verified against PG-14 for case 1
do_execsql_test join7-$id.115 {
SELECT * FROM t1 FULL OUTER JOIN t2 ON b=c
WHERE a=1 OR a IS NULL ORDER BY +b;
} {
NULL NULL 5 55
1 2 NULL NULL
1 3 3 33
1 4 4 44
}
do_execsql_test join7-$id.116 {
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c
WHERE a=1 OR a IS NULL ORDER BY +b;
} {
NULL NULL 5 55
1 2 NULL NULL
1 3 3 33
1 4 4 44
}
# Verified against PG-14 for case 1:
do_execsql_test join7-$id.120 {
SELECT * FROM t1 FULL OUTER JOIN t2 ON b=c WHERE a IS NULL ORDER BY +d;
} {
NULL NULL 5 55
}
# Verified against PG-14 for case 1:
do_execsql_test join7-$id.130 {
SELECT * FROM t1 FULL OUTER JOIN t2 ON b=c AND d<=0 ORDER BY +b, +d;
} {
NULL NULL 3 33
NULL NULL 4 44
NULL NULL 5 55
1 2 NULL NULL
1 3 NULL NULL
1 4 NULL NULL
}
# Verified against PG-14 for case 1:
do_execsql_test join7-$id.140 {
SELECT a, b, c, d
FROM t2 FULL OUTER JOIN t1 ON b=c AND d<=0 ORDER BY +b, +d;
} {
NULL NULL 3 33
NULL NULL 4 44
NULL NULL 5 55
1 2 NULL NULL
1 3 NULL NULL
1 4 NULL NULL
}
do_execsql_test join7-$id.141 {
SELECT a, b, c, d
FROM t2 FULL OUTER JOIN t1 ON b=c AND d<=0
ORDER BY +b, +d LIMIT 2 OFFSET 2
} {
NULL NULL 5 55
1 2 NULL NULL
}
}
finish_test

140
test/join8.test Normal file
View File

@ -0,0 +1,140 @@
# 2022-04-12
#
# 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 tests for RIGHT and FULL OUTER JOINs.
set testdir [file dirname $argv0]
source $testdir/tester.tcl
db null NULL
do_execsql_test join8-10 {
CREATE TABLE t1(a,b,c);
CREATE TABLE t2(x,y);
CREATE INDEX t2x ON t2(x);
SELECT avg(DISTINCT b) FROM (SELECT * FROM t2 LEFT RIGHT JOIN t1 ON c);
} {NULL}
# Pending optimization opportunity:
# Row-value initialization subroutines must be called from with the
# RIGHT JOIN body subroutine before the first use of any register containing
# the results of that subroutine. This seems dodgy. Test case:
#
reset_db
do_execsql_test join8-1000 {
CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT,b,c,d,e,f,g,h,j,k,l,m,n,o,p,q,r,s);
CREATE INDEX t1x1 ON t1(g+h,j,k);
CREATE INDEX t1x2 ON t1(b);
INSERT INTO t1 DEFAULT VALUES;
} {}
do_catchsql_test join8-1010 {
SELECT a
FROM (
SELECT a
FROM (
SELECT a
FROM (
SELECT a FROM t1 NATURAL LEFT JOIN t1
WHERE (b, 2 ) IS ( SELECT 2 IN(2,2),2)
)
NATURAL LEFT FULL JOIN t1
WHERE ( rowid , 1 )<=(CASE 5 WHEN 619 THEN 841 ELSE 3374391096 END,0)
ORDER BY a ASC
)
NATURAL LEFT JOIN t1
WHERE (b, 2 ) IS ( SELECT 3 IN(3,3),3)
)
NATURAL LEFT FULL JOIN t1
WHERE ( rowid , 1 )<=(CASE 5 WHEN 619 THEN 841 ELSE 3374391096 END,0)
ORDER BY a ASC;
} {0 1}
# Pending issue #2: (now resolved)
# Jump to addrHalt inside the RIGHT JOIN body subroutine bypasses the
# OP_Return, resulting in a subroutine loop. Test case:
#
reset_db
do_execsql_test join8-2000 {
CREATE TABLE t1(a int, b int, c int);
INSERT INTO t1 VALUES(1,2,3),(4,5,6);
CREATE TABLE t2(d int, e int);
INSERT INTO t2 VALUES(3,333),(4,444);
CREATE TABLE t3(f int, g int);
PRAGMA automatic_index=off;
} {}
do_catchsql_test join8-2010 {
SELECT * FROM t1 RIGHT JOIN t2 ON c=d JOIN t3 ON f=e;
} {0 {}}
# Demonstrate that nested FULL JOINs and USING clauses work
#
reset_db
load_static_extension db series
do_execsql_test join8-3000 {
CREATE TABLE t1(id INTEGER PRIMARY KEY, a INT);
CREATE TABLE t2(id INTEGER PRIMARY KEY, b INT);
CREATE TABLE t3(id INTEGER PRIMARY KEY, c INT);
CREATE TABLE t4(id INTEGER PRIMARY KEY, d INT);
CREATE TABLE t5(id INTEGER PRIMARY KEY, e INT);
CREATE TABLE t6(id INTEGER PRIMARY KEY, f INT);
CREATE TABLE t7(id INTEGER PRIMARY KEY, g INT);
CREATE TABLE t8(id INTEGER PRIMARY KEY, h INT);
INSERT INTO t1 SELECT value, 1 FROM generate_series(1,256) WHERE value & 1;
INSERT INTO t2 SELECT value, 1 FROM generate_series(1,256) WHERE value & 2;
INSERT INTO t3 SELECT value, 1 FROM generate_series(1,256) WHERE value & 4;
INSERT INTO t4 SELECT value, 1 FROM generate_series(1,256) WHERE value & 8;
INSERT INTO t5 SELECT value, 1 FROM generate_series(1,256) WHERE value & 16;
INSERT INTO t6 SELECT value, 1 FROM generate_series(1,256) WHERE value & 32;
INSERT INTO t7 SELECT value, 1 FROM generate_series(1,256) WHERE value & 64;
INSERT INTO t8 SELECT value, 1 FROM generate_series(1,256) WHERE value & 128;
CREATE TABLE t9 AS
SELECT id, h, g, f, e, d, c, b, a
FROM t1
NATURAL FULL JOIN t2
NATURAL FULL JOIN t3
NATURAL FULL JOIN t4
NATURAL FULL JOIN t5
NATURAL FULL JOIN t6
NATURAL FULL JOIN t7
NATURAL FULL JOIN t8;
} {}
do_execsql_test join8-3010 {
SELECT count(*) FROM t9;
} {255}
do_execsql_test join8-3020 {
SELECT id, count(*) FROM t9 GROUP BY id HAVING count(*)!=1;
} {}
do_execsql_test join8-3030 {
UPDATE t9 SET a=0 WHERE a IS NULL;
UPDATE t9 SET b=0 WHERE b IS NULL;
UPDATE t9 SET c=0 WHERE c IS NULL;
UPDATE t9 SET d=0 WHERE d IS NULL;
UPDATE t9 SET e=0 WHERE e IS NULL;
UPDATE t9 SET f=0 WHERE f IS NULL;
UPDATE t9 SET g=0 WHERE g IS NULL;
UPDATE t9 SET h=0 WHERE h IS NULL;
SELECT count(*) FROM t9 WHERE id=128*h+64*g+32*f+16*e+8*d+4*c+2*b+a;
} {255}
do_execsql_test join8-3040 {
SELECT * FROM t9 WHERE id<>128*h+64*g+32*f+16*e+8*d+4*c+2*b+a;
} {}
# 2022-04-21 dbsqlfuzz find
#
reset_db
do_execsql_test join8-4000 {
CREATE TABLE t1(x INTEGER PRIMARY KEY, a, b);
INSERT INTO t1 VALUES(1,5555,4);
CREATE INDEX i1a ON t1(a);
CREATE INDEX i1b ON t1(b);
SELECT a FROM t1 NATURAL RIGHT JOIN t1 WHERE a=5555 OR (1,b)==(SELECT 2 IN (2,2),4);
} {5555}
finish_test

565
test/join9.test Normal file
View File

@ -0,0 +1,565 @@
# 2022-04-16
#
# 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.
#
# This file implements tests for RIGHT and FULL OUTER JOINs.
set testdir [file dirname $argv0]
source $testdir/tester.tcl
foreach {id schema} {
1 {
CREATE TABLE t3(id INTEGER PRIMARY KEY, w TEXT);
CREATE TABLE t4(id INTEGER PRIMARY KEY, x TEXT);
CREATE TABLE t5(id INTEGER PRIMARY KEY, y TEXT);
CREATE TABLE t6(id INTEGER PRIMARY KEY, z INT);
CREATE VIEW dual(dummy) AS VALUES('x');
INSERT INTO t3(id,w) VALUES(2,'two'),(3,'three'),(6,'six'),(7,'seven');
INSERT INTO t4(id,x) VALUES(2,'alice'),(4,'bob'),(6,'cindy'),(8,'dave');
INSERT INTO t5(id,y) VALUES(1,'red'),(2,'orange'),(3,'yellow'),(4,'green'),
(5,'blue');
INSERT INTO t6(id,z) VALUES(3,333),(4,444),(5,555),(0,1000),(9,999);
}
2 {
CREATE TABLE t3(id INT PRIMARY KEY, w TEXT) WITHOUT ROWID;
CREATE TABLE t4(id INT PRIMARY KEY, x TEXT) WITHOUT ROWID;
CREATE TABLE t5(id INT PRIMARY KEY, y TEXT) WITHOUT ROWID;
CREATE TABLE t6(id INT PRIMARY KEY, z INT) WITHOUT ROWID;
CREATE TABLE dual(dummy TEXT);
INSERT INTO dual(dummy) VALUES('x');
INSERT INTO t3(id,w) VALUES(2,'two'),(3,'three'),(6,'six'),(7,'seven');
INSERT INTO t4(id,x) VALUES(2,'alice'),(4,'bob'),(6,'cindy'),(8,'dave');
INSERT INTO t5(id,y) VALUES(1,'red'),(2,'orange'),(3,'yellow'),(4,'green'),
(5,'blue');
INSERT INTO t6(id,z) VALUES(3,333),(4,444),(5,555),(0,1000),(9,999);
}
3 {
CREATE TABLE t3x(id INTEGER PRIMARY KEY, w TEXT);
CREATE TABLE t4x(id INTEGER PRIMARY KEY, x TEXT);
CREATE TABLE t5x(id INTEGER PRIMARY KEY, y TEXT);
CREATE TABLE t6x(id INTEGER PRIMARY KEY, z INT);
CREATE VIEW dual(dummy) AS VALUES('x');
INSERT INTO t3x(id,w) VALUES(2,'two'),(3,'three'),(6,'six'),(7,'seven');
INSERT INTO t4x(id,x) VALUES(2,'alice'),(4,'bob'),(6,'cindy'),(8,'dave');
INSERT INTO t5x(id,y) VALUES(1,'red'),(2,'orange'),(3,'yellow'),(4,'green'),
(5,'blue');
INSERT INTO t6x(id,z) VALUES(3,333),(4,444),(5,555),(0,1000),(9,999);
CREATE VIEW t3 AS SELECT * FROM t3x LIMIT 1000;
CREATE VIEW t4 AS SELECT * FROM t4x LIMIT 1000;
CREATE VIEW t5 AS SELECT * FROM t5x LIMIT 1000;
CREATE VIEW t6 AS SELECT * FROM t6x LIMIT 1000;
}
4 {
CREATE TABLE t3a(id INTEGER PRIMARY KEY, w TEXT);
CREATE TABLE t3b(id INTEGER PRIMARY KEY, w TEXT);
CREATE TABLE t4a(id INTEGER PRIMARY KEY, x TEXT);
CREATE TABLE t4b(id INTEGER PRIMARY KEY, x TEXT);
CREATE TABLE t5a(id INTEGER PRIMARY KEY, y TEXT);
CREATE TABLE t5b(id INTEGER PRIMARY KEY, y TEXT);
CREATE TABLE t6a(id INTEGER PRIMARY KEY, z INT);
CREATE TABLE t6b(id INTEGER PRIMARY KEY, z INT);
CREATE VIEW dual(dummy) AS VALUES('x');
INSERT INTO t3a(id,w) VALUES(2,'two'),(3,'three');
INSERT INTO t3b(id,w) VALUES(6,'six'),(7,'seven');
INSERT INTO t4a(id,x) VALUES(2,'alice'),(4,'bob');
INSERT INTO t4b(id,x) VALUES(6,'cindy'),(8,'dave');
INSERT INTO t5a(id,y) VALUES(1,'red'),(2,'orange'),(3,'yellow');
INSERT INTO t5b(id,y) VALUES(4,'green'),(5,'blue');
INSERT INTO t6a(id,z) VALUES(3,333),(4,444);
INSERT INTO t6b(id,z) VALUES(5,555),(0,1000),(9,999);
CREATE VIEW t3 AS SELECT * FROM t3a UNION ALL SELECT * FROM t3b;
CREATE VIEW t4 AS SELECT * FROM t4a UNION ALL SELECT * FROM t4b;
CREATE VIEW t5 AS SELECT * FROM t5a UNION ALL SELECT * FROM t5b;
CREATE VIEW t6 AS SELECT * FROM t6a UNION ALL SELECT * FROM t6b;
}
5 {
CREATE TABLE t3a(id INTEGER PRIMARY KEY, w TEXT) WITHOUT ROWID;
CREATE TABLE t3b(id INTEGER PRIMARY KEY, w TEXT);
CREATE TABLE t4a(id INTEGER PRIMARY KEY, x TEXT) WITHOUT ROWID;
CREATE TABLE t4b(id INTEGER PRIMARY KEY, x TEXT) WITHOUT ROWID;
CREATE TABLE t5a(id INTEGER PRIMARY KEY, y TEXT);
CREATE TABLE t5b(id INTEGER PRIMARY KEY, y TEXT) WITHOUT ROWID;
CREATE TABLE t6a(id INTEGER PRIMARY KEY, z INT);
CREATE TABLE t6b(id INTEGER PRIMARY KEY, z INT);
CREATE VIEW dual(dummy) AS VALUES('x');
INSERT INTO t3a(id,w) VALUES(2,'two'),(3,'three');
INSERT INTO t3b(id,w) VALUES(6,'six'),(7,'seven');
INSERT INTO t4a(id,x) VALUES(2,'alice'),(4,'bob');
INSERT INTO t4b(id,x) VALUES(6,'cindy'),(8,'dave');
INSERT INTO t5a(id,y) VALUES(1,'red'),(2,'orange'),(3,'yellow');
INSERT INTO t5b(id,y) VALUES(4,'green'),(5,'blue');
INSERT INTO t6a(id,z) VALUES(3,333),(4,444);
INSERT INTO t6b(id,z) VALUES(5,555),(0,1000),(9,999);
CREATE VIEW t3 AS SELECT * FROM t3a UNION ALL SELECT * FROM t3b;
CREATE VIEW t4 AS SELECT * FROM t4a UNION ALL SELECT * FROM t4b LIMIT 50;
CREATE VIEW t5 AS SELECT * FROM t5a UNION ALL SELECT * FROM t5b LIMIT 100;
CREATE VIEW t6 AS SELECT * FROM t6a UNION ALL SELECT * FROM t6b;
}
} {
reset_db
db nullvalue -
do_execsql_test join9-$id.setup $schema {}
# Verifid by PG-14 for case 1
do_execsql_test join9-$id.100 {
SELECT *, t4.id, t5.id, t6.id
FROM t4 NATURAL LEFT JOIN t5 NATURAL LEFT JOIN t6
ORDER BY 1;
} {
2 alice orange - 2 2 -
4 bob green 444 4 4 4
6 cindy - - 6 - -
8 dave - - 8 - -
}
do_execsql_test join9-$id.101 {
SELECT *, t4.id, t5.id, t6.id
FROM t4 NATURAL LEFT JOIN t5 NATURAL LEFT JOIN t6
ORDER BY id;
} {
2 alice orange - 2 2 -
4 bob green 444 4 4 4
6 cindy - - 6 - -
8 dave - - 8 - -
}
do_execsql_test join9-$id.102 {
SELECT *, t4.id, t5.id, t6.id
FROM t4 LEFT JOIN t5 USING(id) LEFT JOIN t6 USING(id)
ORDER BY id;
} {
2 alice orange - 2 2 -
4 bob green 444 4 4 4
6 cindy - - 6 - -
8 dave - - 8 - -
}
# Verifid by PG-14 using case 1
do_execsql_test join9-$id.200 {
SELECT id, x, y, z, t4.id, t5.id, t6.id
FROM t5 NATURAL RIGHT JOIN t4 NATURAL LEFT JOIN t6
ORDER BY 1;
} {
2 alice orange - 2 2 -
4 bob green 444 4 4 4
6 cindy - - 6 - -
8 dave - - 8 - -
}
do_execsql_test join9-$id.201 {
SELECT id, x, y, z, t4.id, t5.id, t6.id
FROM t5 NATURAL RIGHT JOIN t4 NATURAL LEFT JOIN t6
ORDER BY id;
} {
2 alice orange - 2 2 -
4 bob green 444 4 4 4
6 cindy - - 6 - -
8 dave - - 8 - -
}
# Verified by PG-14 using case 1
do_execsql_test join9-$id.300 {
SELECT *, t4.id, t5.id, t6.id
FROM t4 NATURAL RIGHT JOIN t5 NATURAL RIGHT JOIN t6
ORDER BY 1;
} {
0 - - 1000 - - 0
3 - yellow 333 - 3 3
4 bob green 444 4 4 4
5 - blue 555 - 5 5
9 - - 999 - - 9
}
do_execsql_test join9-$id.301 {
SELECT *, t4.id, t5.id, t6.id
FROM t4 NATURAL RIGHT JOIN t5 NATURAL RIGHT JOIN t6
ORDER BY id;
} {
0 - - 1000 - - 0
3 - yellow 333 - 3 3
4 bob green 444 4 4 4
5 - blue 555 - 5 5
9 - - 999 - - 9
}
# Verified by PG-14 for case 1
do_execsql_test join9-$id.400 {
SELECT *, t4.id, t5.id, t6.id
FROM t4 NATURAL FULL JOIN t5 NATURAL FULL JOIN t6
ORDER BY 1;
} {
0 - - 1000 - - 0
1 - red - - 1 -
2 alice orange - 2 2 -
3 - yellow 333 - 3 3
4 bob green 444 4 4 4
5 - blue 555 - 5 5
6 cindy - - 6 - -
8 dave - - 8 - -
9 - - 999 - - 9
}
do_execsql_test join9-$id.401 {
SELECT *, t4.id, t5.id, t6.id
FROM t4 NATURAL FULL JOIN t5 NATURAL FULL JOIN t6
ORDER BY id;
} {
0 - - 1000 - - 0
1 - red - - 1 -
2 alice orange - 2 2 -
3 - yellow 333 - 3 3
4 bob green 444 4 4 4
5 - blue 555 - 5 5
6 cindy - - 6 - -
8 dave - - 8 - -
9 - - 999 - - 9
}
do_execsql_test join9-$id.402 {
SELECT id, x, y, z, t4.id, t5.id, t6.id
FROM t4 NATURAL FULL JOIN t6 NATURAL FULL JOIN t5
ORDER BY id;
} {
0 - - 1000 - - 0
1 - red - - 1 -
2 alice orange - 2 2 -
3 - yellow 333 - 3 3
4 bob green 444 4 4 4
5 - blue 555 - 5 5
6 cindy - - 6 - -
8 dave - - 8 - -
9 - - 999 - - 9
}
do_execsql_test join9-$id.403 {
SELECT id, x, y, z, t4.id, t5.id, t6.id
FROM t5 NATURAL FULL JOIN t4 NATURAL FULL JOIN t6
ORDER BY id;
} {
0 - - 1000 - - 0
1 - red - - 1 -
2 alice orange - 2 2 -
3 - yellow 333 - 3 3
4 bob green 444 4 4 4
5 - blue 555 - 5 5
6 cindy - - 6 - -
8 dave - - 8 - -
9 - - 999 - - 9
}
do_execsql_test join9-$id.404 {
SELECT id, x, y, z, t4.id, t5.id, t6.id
FROM t5 NATURAL FULL JOIN t6 NATURAL FULL JOIN t4
ORDER BY id;
} {
0 - - 1000 - - 0
1 - red - - 1 -
2 alice orange - 2 2 -
3 - yellow 333 - 3 3
4 bob green 444 4 4 4
5 - blue 555 - 5 5
6 cindy - - 6 - -
8 dave - - 8 - -
9 - - 999 - - 9
}
do_execsql_test join9-$id.405 {
SELECT id, x, y, z, t4.id, t5.id, t6.id
FROM t6 NATURAL FULL JOIN t4 NATURAL FULL JOIN t5
ORDER BY id;
} {
0 - - 1000 - - 0
1 - red - - 1 -
2 alice orange - 2 2 -
3 - yellow 333 - 3 3
4 bob green 444 4 4 4
5 - blue 555 - 5 5
6 cindy - - 6 - -
8 dave - - 8 - -
9 - - 999 - - 9
}
do_execsql_test join9-$id.406 {
SELECT id, x, y, z, t4.id, t5.id, t6.id
FROM t6 NATURAL FULL JOIN t5 NATURAL FULL JOIN t4
ORDER BY id;
} {
0 - - 1000 - - 0
1 - red - - 1 -
2 alice orange - 2 2 -
3 - yellow 333 - 3 3
4 bob green 444 4 4 4
5 - blue 555 - 5 5
6 cindy - - 6 - -
8 dave - - 8 - -
9 - - 999 - - 9
}
# Verified by PG-14 using case 1
do_execsql_test join9-$id.500 {
SELECT id, w, x, y, z
FROM t3 FULL JOIN t4 USING(id)
NATURAL FULL JOIN t5
FULL JOIN t6 USING(id)
ORDER BY 1;
} {
0 - - - 1000
1 - - red -
2 two alice orange -
3 three - yellow 333
4 - bob green 444
5 - - blue 555
6 six cindy - -
7 seven - - -
8 - dave - -
9 - - - 999
}
# Verified by PG-14 using case 1
do_execsql_test join9-$id.600 {
SELECT id, w, x, y, z
FROM t3 JOIN dual AS d1 ON true
FULL JOIN t4 USING(id)
JOIN dual AS d2 ON true
NATURAL FULL JOIN t5
JOIN dual AS d3 ON true
FULL JOIN t6 USING(id)
CROSS JOIN dual AS d4
ORDER BY 1;
} {
0 - - - 1000
1 - - red -
2 two alice orange -
3 three - yellow 333
4 - bob green 444
5 - - blue 555
6 six cindy - -
7 seven - - -
8 - dave - -
9 - - - 999
}
# Verified by PG-14 using case 1
do_execsql_test join9-$id.700 {
SELECT id, w, x, y, z
FROM t3 JOIN dual AS d1 ON true
FULL JOIN t4 USING(id)
JOIN dual AS d2 ON true
NATURAL FULL JOIN t5
JOIN dual AS d3 ON true
FULL JOIN t6 USING(id)
CROSS JOIN dual AS d4
WHERE x<>'bob' OR x IS NULL
ORDER BY 1;
} {
0 - - - 1000
1 - - red -
2 two alice orange -
3 three - yellow 333
5 - - blue 555
6 six cindy - -
7 seven - - -
8 - dave - -
9 - - - 999
}
# Verified by PG-14 using case 1
do_execsql_test join9-$id.800 {
WITH t7(id,a) AS MATERIALIZED (SELECT * FROM t4 WHERE false)
SELECT *
FROM t7
JOIN t7 AS t7b USING(id)
FULL JOIN t3 USING(id);
} {
2 - - two
3 - - three
6 - - six
7 - - seven
}
# Verified by PG-14
do_execsql_test join9-$id.900 {
SELECT *
FROM (t3 NATURAL FULL JOIN t4)
NATURAL FULL JOIN
(t5 NATURAL FULL JOIN t6)
ORDER BY 1;
} {
0 - - - 1000
1 - - red -
2 two alice orange -
3 three - yellow 333
4 - bob green 444
5 - - blue 555
6 six cindy - -
7 seven - - -
8 - dave - -
9 - - - 999
}
do_execsql_test join9-$id.910 {
SELECT *
FROM t3 NATURAL FULL JOIN
(t4 NATURAL FULL JOIN
(t5 NATURAL FULL JOIN t6))
ORDER BY 1;
} {
0 - - - 1000
1 - - red -
2 two alice orange -
3 three - yellow 333
4 - bob green 444
5 - - blue 555
6 six cindy - -
7 seven - - -
8 - dave - -
9 - - - 999
}
do_execsql_test join9-$id.920 {
SELECT *
FROM t3 FULL JOIN (
t4 FULL JOIN (
t5 FULL JOIN t6 USING (id)
) USING(id)
) USING(id)
ORDER BY 1;
} {
0 - - - 1000
1 - - red -
2 two alice orange -
3 three - yellow 333
4 - bob green 444
5 - - blue 555
6 six cindy - -
7 seven - - -
8 - dave - -
9 - - - 999
}
do_execsql_test join9-$id.920 {
SELECT *
FROM t3 FULL JOIN (
t4 FULL JOIN (
t5 FULL JOIN t6 USING (id)
) USING(id)
) USING(id)
ORDER BY 1;
} {
0 - - - 1000
1 - - red -
2 two alice orange -
3 three - yellow 333
4 - bob green 444
5 - - blue 555
6 six cindy - -
7 seven - - -
8 - dave - -
9 - - - 999
}
# Verified by PG-14
do_execsql_test join9-$id.930 {
SELECT *
FROM t3 FULL JOIN (
t4 FULL JOIN (
t5 FULL JOIN t6 USING(id)
) USING(id)
) AS j1 ON j1.id=t3.id
ORDER BY coalesce(t3.id,j1.id);
} {
- - 0 - - 1000
- - 1 - red -
2 two 2 alice orange -
3 three 3 - yellow 333
- - 4 bob green 444
- - 5 - blue 555
6 six 6 cindy - -
7 seven - - - -
- - 8 dave - -
- - 9 - - 999
}
# Verified by PG-14
do_execsql_test join9-$id.940 {
SELECT *
FROM t3 FULL JOIN (
t4 RIGHT JOIN (
t5 FULL JOIN t6 USING(id)
) USING(id)
) AS j1 ON j1.id=t3.id
ORDER BY coalesce(t3.id,j1.id);
} {
- - 0 - - 1000
- - 1 - red -
2 two 2 alice orange -
3 three 3 - yellow 333
- - 4 bob green 444
- - 5 - blue 555
6 six - - - -
7 seven - - - -
- - 9 - - 999
}
# Verified by PG-14
do_execsql_test join9-$id.950 {
SELECT *
FROM t3 FULL JOIN (
t4 LEFT JOIN (
t5 FULL JOIN t6 USING(id)
) USING(id)
) AS j1 ON j1.id=t3.id
ORDER BY coalesce(t3.id,j1.id);
} {
2 two 2 alice orange -
3 three - - - -
- - 4 bob green 444
6 six 6 cindy - -
7 seven - - - -
- - 8 dave - -
}
# Restriction (27) in the query flattener
# Verified by PG-14
do_execsql_test join9-$id.1000 {
WITH t56(id,y,z) AS (SELECT * FROM t5 FULL JOIN t6 USING(id) LIMIT 50)
SELECT id,x,y,z FROM t4 JOIN t56 USING(id)
ORDER BY 1;
} {
2 alice orange -
4 bob green 444
}
# Verified by PG-14
do_execsql_test join9-$id.1010 {
SELECT id,x,y,z
FROM t4 INNER JOIN (t5 FULL JOIN t6 USING(id)) USING(id)
ORDER BY 1;
} {
2 alice orange -
4 bob green 444
}
# Verified by PG-14
do_execsql_test join9-$id.1020 {
SELECT id,x,y,z
FROM t4 FULL JOIN t5 USING(id) INNER JOIN t6 USING(id)
ORDER BY 1;
} {
3 - yellow 333
4 bob green 444
5 - blue 555
}
# Verified by PG-14
do_execsql_test join9-$id.1030 {
WITH t45(id,x,y) AS (SELECT * FROM t4 FULL JOIN t5 USING(id) LIMIT 50)
SELECT id,x,y,z FROM t45 JOIN t6 USING(id)
ORDER BY 1;
} {
3 - yellow 333
4 bob green 444
5 - blue 555
}
}
finish_test

214
test/joinA.test Normal file
View File

@ -0,0 +1,214 @@
# 2022-04-18
#
# 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.
#
# This file implements tests for RIGHT and FULL OUTER JOINs.
set testdir [file dirname $argv0]
source $testdir/tester.tcl
foreach {id schema} {
1 {
CREATE TABLE t1(a INT, b INT, c INT, d INT);
CREATE TABLE t2(c INT, d INT, e INT, f INT);
CREATE TABLE t3(a INT, b INT, e INT, f INT);
CREATE TABLE t4(a INT, c INT, d INT, f INT);
INSERT INTO t1 VALUES(11,21,31,41),(12,22,32,42),(15,25,35,45),(18,28,38,48);
INSERT INTO t2 VALUES(12,22,32,42),(13,23,33,43),(15,25,35,45),(17,27,37,47);
INSERT INTO t3 VALUES(14,24,34,44),(15,25,35,45),(16,26,36,46);
INSERT INTO t4 VALUES(11,21,31,41),(13,23,33,43),(16,26,36,46),(19,29,39,49);
}
2 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT, c INT, d INT);
CREATE TABLE t2(c INT, d INTEGER PRIMARY KEY, e INT, f INT);
CREATE TABLE t3(a INT, b INT, e INTEGER PRIMARY KEY, f INT);
CREATE TABLE t4(a INT, c INT, d INT, f INT PRIMARY KEY) WITHOUT ROWID;
INSERT INTO t1 VALUES(11,21,31,41),(12,22,32,42),(15,25,35,45),(18,28,38,48);
INSERT INTO t2 VALUES(12,22,32,42),(13,23,33,43),(15,25,35,45),(17,27,37,47);
INSERT INTO t3 VALUES(14,24,34,44),(15,25,35,45),(16,26,36,46);
INSERT INTO t4 VALUES(11,21,31,41),(13,23,33,43),(16,26,36,46),(19,29,39,49);
}
3 {
CREATE TABLE t1a(a INT, b INT, c INT, d INT);
CREATE TABLE t2a(c INT, d INT, e INT, f INT);
CREATE TABLE t3a(a INT, b INT, e INT, f INT);
CREATE TABLE t4a(a INT, c INT, d INT, f INT);
INSERT INTO t1a VALUES(11,21,31,41),(12,22,32,42);
INSERT INTO t2a VALUES(12,22,32,42),(13,23,33,43);
INSERT INTO t3a VALUES(14,24,34,44),(15,25,35,45);
INSERT INTO t4a VALUES(11,21,31,41),(13,23,33,43);
CREATE TABLE t1b(a INT, b INT, c INT, d INT);
CREATE TABLE t2b(c INT, d INT, e INT, f INT);
CREATE TABLE t3b(a INT, b INT, e INT, f INT);
CREATE TABLE t4b(a INT, c INT, d INT, f INT);
INSERT INTO t1b VALUES(15,25,35,45),(18,28,38,48);
INSERT INTO t2b VALUES(15,25,35,45),(17,27,37,47);
INSERT INTO t3b VALUES(15,25,35,45),(16,26,36,46);
INSERT INTO t4b VALUES(16,26,36,46),(19,29,39,49);
CREATE VIEW t1 AS SELECT * FROM t1a UNION SELECT * FROM t1b;
CREATE VIEW t2 AS SELECT * FROM t2a UNION SELECT * FROM t2b;
CREATE VIEW t3 AS SELECT * FROM t3a UNION SELECT * FROM t3b;
CREATE VIEW t4 AS SELECT * FROM t4a UNION SELECT * FROM t4b;
}
} {
reset_db
db nullvalue -
do_execsql_test joinA-$id.setup $schema {}
# Verified by PG-14
do_execsql_test joinA-$id.100 {
SELECT a,b,c,d,t2.e,f,t3.e
FROM t1
INNER JOIN t2 USING(c,d)
INNER JOIN t3 USING(a,b,f)
INNER JOIN t4 USING(a,c,d,f)
ORDER BY 1 nulls first, 3 nulls first;
} {}
# Verified by PG-14
do_execsql_test joinA-$id.110 {
SELECT a,b,c,d,t2.e,f,t3.e
FROM t1
LEFT JOIN t2 USING(c,d)
LEFT JOIN t3 USING(a,b,f)
LEFT JOIN t4 USING(a,c,d,f)
ORDER BY 1 nulls first, 3 nulls first;
} {
11 21 31 41 - - -
12 22 32 42 - - -
15 25 35 45 - - -
18 28 38 48 - - -
}
# Verified by PG-14
do_execsql_test joinA-$id.120 {
SELECT a,b,c,d,t2.e,f,t3.e
FROM t1
LEFT JOIN t2 USING(c,d)
RIGHT JOIN t3 USING(a,b,f)
LEFT JOIN t4 USING(a,c,d,f)
ORDER BY 1 nulls first, 3 nulls first;
} {
14 24 - - - 44 34
15 25 - - - 45 35
16 26 - - - 46 36
}
# Verified by PG-14
do_execsql_test joinA-$id.130 {
SELECT a,b,c,d,t2.e,f,t3.e
FROM t1
RIGHT JOIN t2 USING(c,d)
LEFT JOIN t3 USING(a,b,f)
RIGHT JOIN t4 USING(a,c,d,f)
ORDER BY 1 nulls first, 3 nulls first;
} {
11 - 21 31 - 41 -
13 - 23 33 - 43 -
16 - 26 36 - 46 -
19 - 29 39 - 49 -
}
# Verified by PG-14
do_execsql_test joinA-$id.140 {
SELECT a,b,c,d,t2.e,f,t3.e
FROM t1
FULL JOIN t2 USING(c,d)
LEFT JOIN t3 USING(a,b,f)
RIGHT JOIN t4 USING(a,c,d,f)
ORDER BY 1 nulls first, 3 nulls first;
} {
11 - 21 31 - 41 -
13 - 23 33 - 43 -
16 - 26 36 - 46 -
19 - 29 39 - 49 -
}
# Verified by PG-14
do_execsql_test joinA-$id.150 {
SELECT a,b,c,d,t2.e,f,t3.e
FROM t1
RIGHT JOIN t2 USING(c,d)
FULL JOIN t3 USING(a,b,f)
RIGHT JOIN t4 USING(a,c,d,f)
ORDER BY 1 nulls first, 3 nulls first;
} {
11 - 21 31 - 41 -
13 - 23 33 - 43 -
16 - 26 36 - 46 -
19 - 29 39 - 49 -
}
# Verified by PG-14
do_execsql_test joinA-$id.160 {
SELECT a,b,c,d,t2.e,f,t3.e
FROM t1
RIGHT JOIN t2 USING(c,d)
LEFT JOIN t3 USING(a,b,f)
FULL JOIN t4 USING(a,c,d,f)
ORDER BY 1 nulls first, 3 nulls first;
} {
- - 12 22 32 42 -
- - 13 23 33 43 -
- - 15 25 35 45 -
- - 17 27 37 47 -
11 - 21 31 - 41 -
13 - 23 33 - 43 -
16 - 26 36 - 46 -
19 - 29 39 - 49 -
}
# Verified by PG-14
do_execsql_test joinA-$id.170 {
SELECT a,b,c,d,t2.e,f,t3.e
FROM t1
LEFT JOIN t2 USING(c,d)
RIGHT JOIN t3 USING(a,b,f)
FULL JOIN t4 USING(a,c,d,f)
ORDER BY 1 nulls first, 3 nulls first;
} {
11 - 21 31 - 41 -
13 - 23 33 - 43 -
14 24 - - - 44 34
15 25 - - - 45 35
16 26 - - - 46 36
16 - 26 36 - 46 -
19 - 29 39 - 49 -
}
# Verified by PG-14
do_execsql_test joinA-$id.200 {
SELECT a,b,c,d,t2.e,f,t3.e
FROM t1
FULL JOIN t2 USING(c,d)
FULL JOIN t3 USING(a,b,f)
FULL JOIN t4 USING(a,c,d,f)
ORDER BY 1 nulls first, 3 nulls first;
} {
- - 12 22 32 42 -
- - 13 23 33 43 -
- - 15 25 35 45 -
- - 17 27 37 47 -
11 - 21 31 - 41 -
11 21 31 41 - - -
12 22 32 42 - - -
13 - 23 33 - 43 -
14 24 - - - 44 34
15 25 - - - 45 35
15 25 35 45 - - -
16 26 - - - 46 36
16 - 26 36 - 46 -
18 28 38 48 - - -
19 - 29 39 - 49 -
}
}
finish_test

7252
test/joinB.test Normal file

File diff suppressed because it is too large Load Diff

View File

@ -223,11 +223,11 @@ do_test vtab6-2.2 {
SELECT * FROM t2 NATURAL LEFT OUTER JOIN t1;
}
} {1 2 3 {} 2 3 4 1 3 4 5 2}
do_test vtab6-2.3 {
catchsql {
SELECT * FROM t1 NATURAL RIGHT OUTER JOIN t2;
}
} {1 {RIGHT and FULL OUTER JOINs are not currently supported}}
#do_test vtab6-2.3 {
# catchsql {
# SELECT * FROM t1 NATURAL RIGHT OUTER JOIN t2;
# }
#} {1 {RIGHT and FULL OUTER JOINs are not currently supported}}
do_test vtab6-2.4 {
execsql {
SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.d
@ -281,12 +281,12 @@ do_test vtab6-3.7 {
catchsql {
SELECT * FROM t1 INNER OUTER JOIN t2;
}
} {1 {unknown or unsupported join type: INNER OUTER}}
} {1 {unknown join type: INNER OUTER}}
do_test vtab6-3.7 {
catchsql {
SELECT * FROM t1 LEFT BOGUS JOIN t2;
}
} {1 {unknown or unsupported join type: LEFT BOGUS}}
} {1 {unknown join type: LEFT BOGUS}}
do_test vtab6-4.1 {
execsql {

View File

@ -1348,16 +1348,25 @@ do_execsql_test where-18.1 {
INSERT INTO t181 VALUES(1);
SELECT DISTINCT a FROM t181 LEFT JOIN t182 ON a=b ORDER BY c IS NULL;
} {1}
do_execsql_test where-18.1rj {
SELECT DISTINCT a FROM t182 RIGHT JOIN t181 ON a=b ORDER BY c IS NULL;
} {1}
do_execsql_test where-18.2 {
SELECT DISTINCT a FROM t181 LEFT JOIN t182 ON a=b ORDER BY +c;
} {1}
do_execsql_test where-18.3 {
SELECT DISTINCT a FROM t181 LEFT JOIN t182 ON a=b ORDER BY c;
} {1}
do_execsql_test where-18.3rj {
SELECT DISTINCT a FROM t182 RIGHT JOIN t181 ON a=b ORDER BY c;
} {1}
do_execsql_test where-18.4 {
INSERT INTO t181 VALUES(1),(1),(1),(1);
SELECT DISTINCT a FROM t181 LEFT JOIN t182 ON a=b ORDER BY +c;
} {1}
do_execsql_test where-18.4rj {
SELECT DISTINCT a FROM t182 RIGHT JOIN t181 ON a=b ORDER BY +c;
} {1}
do_execsql_test where-18.5 {
INSERT INTO t181 VALUES(2);
SELECT DISTINCT a FROM t181 LEFT JOIN t182 ON a=b ORDER BY c IS NULL, +a;

View File

@ -378,9 +378,9 @@ ifcapable explain {
|--SEARCH t1 USING INTEGER PRIMARY KEY (rowid=?)
`--MULTI-INDEX OR
|--INDEX 1
| `--SEARCH t2 USING INDEX t2d (d=?)
| `--SEARCH t2 USING INDEX t2d (d=?) LEFT-JOIN
`--INDEX 2
`--SEARCH t2 USING COVERING INDEX t2f (f=?)
`--SEARCH t2 USING COVERING INDEX t2f (f=?) LEFT-JOIN
}]
}