Add support for RIGHT and FULL JOIN.
FossilOrigin-Name: f766dff012af0ea3c28a8ce4db850cd0205729a8283bce1e442992aded7c734b
This commit is contained in:
commit
11140addbb
@ -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
|
||||
|
75
manifest
75
manifest
@ -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.
|
||||
|
@ -1 +1 @@
|
||||
b870d2a2fa54ddab6ffc9b9d95f52420a5a6831aeb73dac75355ca1ab15a190e
|
||||
f766dff012af0ea3c28a8ce4db850cd0205729a8283bce1e442992aded7c734b
|
63
src/build.c
63
src/build.c
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
45
src/expr.c
45
src/expr.c
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
189
src/resolve.c
189
src/resolve.c
@ -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 );
|
||||
|
416
src/select.c
416
src/select.c
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
101
src/vdbe.c
101
src/vdbe.c
@ -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 );
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
136
src/where.c
136
src/where.c
@ -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);
|
||||
|
@ -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*);
|
||||
|
164
src/wherecode.c
164
src/wherecode.c
@ -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--;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
289
test/join7.test
Normal 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
140
test/join8.test
Normal 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
565
test/join9.test
Normal 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
214
test/joinA.test
Normal 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
7252
test/joinB.test
Normal file
File diff suppressed because it is too large
Load Diff
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}]
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user