Compare commits

...

19 Commits

Author SHA1 Message Date
drh e2528001be Add a missing OP_ColumnsUsed opcode to code for expressions like "? IN (SELECT ...)" in cases where expression can use an index that may contain NULL values.
(Backported fix from version 3.14.0).

FossilOrigin-Name: 11368e673cc5a9a3ccf96d1c6fa37213215405ed32fea969ea85d6e7b2234b59
2018-12-14 20:28:12 +00:00
drh 0f2654ecd9 Add the OP_ColumnsUsed opcode (when compiled with
SQLITE_ENABLE_COLUMN_USED_MASK) as a hint to the b-tree layer as to which
columns of a btree cursor will be used.
(Backport from 3.8.11)

FossilOrigin-Name: b29e02f877d7b85f783a9255794883a85590ff8eafac130a3234eb65d0f717f1
2018-12-14 20:20:37 +00:00
dan e317bd4024 Fix the SQLITE_ENABLE_UPDATE_DELETE_LIMIT functionality so that it works with
views and WITHOUT ROWID tables. This is a modified cherrypick of [dae4a97a].

FossilOrigin-Name: b2679d3b7ad090cbcb536317989f3ad1a9470e9f39daef561934d9d18dc69240
2017-11-14 18:26:22 +00:00
drh e5b6978916 Disallow ORDER BY and LIMIT on UPDATE and DELETE of views and WITHOUT ROWID
tables.  This is a temporary fix for ticket [d4beea1633f1b88f] until a better
solution can be found.

FossilOrigin-Name: 30aa941fc16c1affe3821ff2d9a4955e14ab18cd4ece27037bd6bb532fcaa052
2017-11-09 04:13:54 +00:00
drh 86fbc2a890 Enhance the like optimization so that it works with an ESCAPE clause.
FossilOrigin-Name: 2495acf71017fa2ffada18824590ead593c47dabe2312701a25adc517cbf72eb
2017-07-27 22:16:26 +00:00
drh 2aab27a19a Try to push relevant WHERE clause terms of the outer query down
into the subquery in order to help the subquery run faster and/or 
use less memory.   This is a cherry-pick of [6df18e949d36] with
bug fixes.

FossilOrigin-Name: cd6ac0784898609ddca887cfb87c042cf409acbbe4e4e84bc5bc1f8528de74fd
2017-07-17 19:37:23 +00:00
drh eb4e3f8348 When checking for the WHERE-clause push-down optimization, verify that
all terms of the compound inner SELECT are non-aggregate, not just the
last term.  Fix for ticket [f7f8c97e97597].

FossilOrigin-Name: adc082c1461e0237cd42653b529fbc136f5899baeff6ee32ee943d76184080c1
2017-07-17 19:25:10 +00:00
drh 3aeec23c29 Do not apply the WHERE-clause pushdown optimization to terms that originate
in the ON or USING clause of a LEFT JOIN.  Fix for ticket
[c2a19d81652f40568c].

FossilOrigin-Name: 52674f948c3e74e5cc32874d4885f2302ad1d5dd0bc45ff6bfda18cf4bea904a
2017-07-17 19:14:11 +00:00
drh 9da8bf9ae7 For FROM-clause subqueries that cannot be flattened, try to push relevant
WHERE clause terms of the outer query down into the subquery in order to help
the subquery run faster and/or use less memory.
Cherry-pick from [6df18e949d36].  Still need to backport bug fixes associated
with that check-in.

FossilOrigin-Name: 043d6ce8ad85f8044735747b8938ea5ce9a08e71b860c2b7179b824021bb7a62
2017-07-17 19:07:14 +00:00
drh 3a907ce696 Fix the covering index OR optimization (check-in [fcbd6abd]) so that
it works with SQLITE_MAX_ATTACHED>30.  Broken by a bad cherry-pick merge.

FossilOrigin-Name: d227de8ad9cf757f30f5415ca8fccff3b5959621d09244bd1f444d3282c5b2ef
2017-07-08 01:01:08 +00:00
drh b792d9ef4c Make use of covering indexes in the OR optimization.
FossilOrigin-Name: fcbd6abdb1a4cf622ff7e85625b9c2a9bbae92410359872924b7fc1e35046a75
2017-07-05 16:20:49 +00:00
drh 23a09adc0b Add the count-of-view optimization when compiled using
SQLITE_COUNTOFVIEW_OPTIMIZATION.

FossilOrigin-Name: b7ae4b879fc086e9543493843377ae90ceff1fe49c97b4c23367012034c3c9d5
2017-07-05 14:54:24 +00:00
drh 1adf686f0c Add the SQLITE_DEFAULT_ROWEST compile-time option for changing the estimated
number of rows in tables that lack sqlite_stat1 entries.

FossilOrigin-Name: 802b82f342328762e3995825aed1b22e61361ef24b673cd5d66b55756ce2a461
2017-06-06 18:22:05 +00:00
drh 607e10998d Fix a problem in STAT4 equality estimation for multi-column indexes
introduced by check-in [3e0590dee0e68cc1599].

FossilOrigin-Name: 19dad0a720c41d1d111f42cb2cb1f291c559957e76e3f88dc6cfc065e4845e77
2017-05-24 04:32:09 +00:00
drh b3785b8322 When planning a query using sorting, resolve ties in the solver by selecting
loop plans with the smaller unsorted cost.

FossilOrigin-Name: 962531e7c1f3ff604271ddf9f47b6234dfd47702ccf24849f55b80814e7be267
2017-05-22 00:27:20 +00:00
drh 80354c6630 In the STAT4 computations, ensure that the aAvgEq values do not go negative.
FossilOrigin-Name: 4f83f6806aa6816656668feb181369500cc2cf0f
2017-01-11 14:21:20 +00:00
drh 66d89de8b4 When the [https://www.sqlite.org/queryplanner.html#partialsort|block sorting optimization]
is used in a scalar subquery, be sure to exit the loop as soon as the first
valid output row is received.  Fix for ticket [cb3aa0641d9a4] backported
to the 3.8.9 branch.

FossilOrigin-Name: 8e4ba115ededca6da78f0679bc9eff08af9365a8
2016-11-02 16:29:31 +00:00
drh 4427e597b2 Fix the ORDER BY LIMIT optimization backport so that it works when the
ORDER BY uses the DESC direction.

FossilOrigin-Name: 0c3cafb7ebb887dbfa2652f8bf69b52ed265c344
2016-09-23 18:06:22 +00:00
drh b5c7f2f235 Backport the ORDER BY LIMIT optimization to version 3.8.9.
FossilOrigin-Name: db3614825fa02ddacc76b85d76a37aad9d2a9dc8
2016-09-14 01:43:24 +00:00
29 changed files with 1415 additions and 203 deletions
+33 -32
View File
@@ -1,5 +1,5 @@
C Version\s3.8.9
D 2015-04-08T12:16:33.323
C Add\sa\smissing\sOP_ColumnsUsed\sopcode\sto\scode\sfor\sexpressions\slike\s"?\sIN\s(SELECT\s...)"\sin\scases\swhere\sexpression\scan\suse\san\sindex\sthat\smay\scontain\sNULL\svalues.\n(Backported\sfix\sfrom\sversion\s3.14.0).
D 2018-12-14T20:28:12.680
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 00d12636df7a5b08af09116bcd6c7bfd49b8b3b4
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -167,7 +167,7 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
F src/alter.c d23d6b6991f66b383934f137fd4384d93fb98c81
F src/analyze.c 91540f835163d5369ccbae78e2e6c74d0dd53c1d
F src/analyze.c 92d7ab85ded7d16f424ee388d250bd99c64302de
F src/attach.c 880f9b8641a829c563e52dd13c452ce457ae4dd8
F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240
F src/backup.c ff743689c4d6c5cb55ad42ed9d174b2b3e71f1e3
@@ -176,16 +176,16 @@ F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79
F src/btree.c 2caf598165f3608fde8abac2b243826616ce54b7
F src/btree.h 969adc948e89e449220ff0ff724c94bb2a52e9f1
F src/btreeInt.h 973a22a6fd61350b454ad614832b1f0a5e25a1e4
F src/build.c 0419bba592c22f6d00e6d57a2ca7136720d02c1a
F src/build.c 3d1abf90ecf488d49eb0c0a673a9b1f22b3ef4e81b1bffa6cf3dfdb832125d3c
F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
F src/complete.c 198a0066ba60ab06fc00fba1998d870a4d575463
F src/ctime.c 98f89724adc891a1a4c655bee04e33e716e05887
F src/date.c e4d50b3283696836ec1036b695ead9a19e37a5ac
F src/delete.c 37964e6c1d73ff49cbea9ff690c9605fb15f600e
F src/expr.c d09dac67d53c78880ba31d56e8ba2be3a6490553
F src/delete.c 7a9df72134437c7f556c2002e3df6d9cba6488aa2a264d05e3ef2b5e49c308d4
F src/expr.c 0f996b684f975f35b09f53f0d1b375aaec4cf1af7594a1d5b279b81c8ad491cd
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c e0444b61bed271a76840cbe6182df93a9baa3f12
F src/func.c 1414c24c873c48796ad45942257a179a423ba42f
F src/fkey.c f19ee86f39aac8069761b3c2acd65ace057d33f793b048734e5d203ef038be9e
F src/func.c 8028a8f79becc879268a114729263a24733ecdb610e7b7ec6ddb228bc2c13cf1
F src/global.c 4f77cadbc5427d00139ba43d0f3979804cbb700e
F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5
F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094
@@ -219,7 +219,7 @@ F src/os_win.c 03d27be3a20048ef52a648d5f0a15f5edda9f2a3
F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca
F src/pager.c 4120a49ecd37697e28f5ed807f470b9c0b88410c
F src/pager.h c3476e7c89cdf1c6914e50a11f3714e30b4e0a77
F src/parse.y 1299c66e7b1707322ccd8af43a359b8fb0d46d72
F src/parse.y 69d085edcf0c9cdc92ae6eb2cec91ae9322f280d3c3989a3c5eeba394b754972
F src/pcache.c 10539fb959849ad6efff80050541cab3d25089d4
F src/pcache.h b44658c9c932d203510279439d891a2a83e12ba8
F src/pcache1.c 69d137620a305f814398bd29a0c998038c0695e9
@@ -228,14 +228,14 @@ F src/pragma.h 09c89bca58e9a44de2116cc8272b8d454657129f
F src/prepare.c 173a5a499138451b2561614ecb87d78f9f4644b9
F src/printf.c 8ae1fa9d30c1200a9268a390ba9e9cea9197b27a
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
F src/resolve.c 41aa91af56d960e9414ce1d7c17cfb68e0d1c6cb
F src/resolve.c 49f8e3709bc80bde91297007965cf6aa6f4f1e3d9c469aa3756247a5cffddb28
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
F src/select.c c28c52e353287434fac8473e56ee4be848d12c9d
F src/select.c 7922b1e1aaceb8eea4b921d0e6d062e32cfef8d897c6b30015fdd546c9b9f57f
F src/shell.c 84a1593bd86aaa14f4da8a8f9b16fbc239d262aa
F src/sqlite.h.in 278602140d49575e8708e643161f4263e428a02a
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d
F src/sqliteInt.h 107b02ed6c64162b653acc2368e982de529e14f6
F src/sqliteInt.h 3b807b71e7170f2c1e8859bd4663f1a758af8dc52677907318244e71c0ca0651
F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46
F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179
F src/table.c e7a09215315a978057fb42c640f890160dbcc45e
@@ -288,16 +288,16 @@ F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 6bbcc9fe50c917864d48287b4792d46d6e873481
F src/tokenize.c a8d270b06e5f709930f7b67cf70a847969cb5bf3
F src/trigger.c 25571661fdeae8c7f975ff40ffec205520a3f92f
F src/update.c 3c4ecc282accf12d39edb8d524cf089645e55a13
F src/trigger.c bdee9c6f068620908f1483e09be5941aaf532a158274ce13a181a8b39753e916
F src/update.c 262a470e3aa82bf57648cc34eb8930d602fc3eeaf5c6039ac362a63b7c1f8072
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
F src/util.c 98a7627ca48ad3265b6940915a1d08355eb3fc7e
F src/vacuum.c 9460b9de7b2d4e34b0d374894aa6c8a0632be8ec
F src/vdbe.c 86ae6f4774410868af41bd839b72b7081ff03e78
F src/vdbe.h 6fc69d9c5e146302c56e163cb4b31d1ee64a18c3
F src/vdbeInt.h 9cbaa84f53ddd2d09a0cf61a94337a3a035d08a0
F src/vdbe.c 6ea32aa0789fca11f02b067805b72c84794d4757af08bfa497d7ef19406a785a
F src/vdbe.h 87fd8b2c5b3af40f9e1a210d928e23e593c22b383ddc7215c76d76e4bc612331
F src/vdbeInt.h 538f6a69152c0a6cfab17161854b793918eae73981b6c82d39d8b46c8878cc77
F src/vdbeapi.c 583d56b129dd27f12bed518270de9ebe521e6a75
F src/vdbeaux.c 413dc496248ac18eb0c19e35e86bb1ffd47b8907
F src/vdbeaux.c eda1c639cec26e648de92ae61e703a4f6f857504e799c355e6e48b8ad2166f71
F src/vdbeblob.c 4f2e8e075d238392df98c5e03a64342465b03f90
F src/vdbemem.c c0dc81285b7571b0a31c40f17846fe2397ec1cd9
F src/vdbesort.c 919717d7599fa31d343ec28bffd0f9e91a4ff5f6
@@ -307,8 +307,8 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
F src/wal.c 878c8e1a51cb2ec45c395d26b7d5cd9e1a098e4a
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
F src/where.c 85d832efa5ef57de542db7f430b72fecd3af8b38
F src/whereInt.h cbe4aa57326998d89e7698ca65bb7c28541d483c
F src/where.c 8a445862f98a0318d4dc4db78d4834dc8188a439dcb2fc02cb86bd58686bcddf
F src/whereInt.h 1d1fd0b3b9b56e08f5d3583c70a2c785a3c43941
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/aggnested.test b35b4cd69fc913f90d39a575e171e1116c3a4bb7
@@ -683,7 +683,7 @@ F test/join.test 52d4d49f86d0cf46926672878c4eaf0da399104a
F test/join2.test f2171c265e57ee298a27e57e7051d22962f9f324
F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
F test/join5.test 5df23eba184f159ed9705a954957e765a10c141d
F test/join5.test 8a5c0be6f0c260a5c7177c3b8f07c7856141038a
F test/join6.test cfe6503791ceb0cbb509966740286ec423cbf10b
F test/journal1.test 69abc726c51b4a0409189f9a85191205297c0577
F test/journal2.test ae06f566c28552c313ded3fee79a6c69e6d049b1
@@ -694,10 +694,11 @@ F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa
F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff
F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
F test/like.test 4f2a71d36a536233727f71995fef900756705e56
F test/like.test 1908852fc4a7bd2ebbe358648934f0e77b62569eca3ab0692ec2f1fa5ff59d0e
F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da
F test/like3.test 7b0525a39e4f25c4fd113de7e2e28eb712dcdedf
F test/limit.test 0c99a27a87b14c646a9d583c7c89fd06c352663e
F test/limit2.test 7e7744cc548d7e4c92e1dd3edc30dd0f86adafd5
F test/loadext.test 648cb95f324d1775c54a55c12271b2d1156b633b
F test/loadext2.test 0408380b57adca04004247179837a18e866a74f7
F test/lock.test b984ab9034e7389be0d863fe4e64cbbc4d2028f5
@@ -771,7 +772,7 @@ F test/notnull.test f8fcf58669ddba79274daa2770d61dfad8274f62
F test/null.test a8b09b8ed87852742343b33441a9240022108993
F test/numcast.test 5d126f7f581432e86a90d1e35cac625164aec4a1
F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
F test/orderby1.test eb246e377612b21a418fbea57047ba8ea88aaa6b
F test/orderby1.test 66ca14d7860a2da02411106968afee910ab1e657
F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04
F test/orderby3.test 8619d06a3debdcd80a27c0fdea5c40b468854b99
F test/orderby4.test 4d39bfbaaa3ae64d026ca2ff166353d2edca4ba4
@@ -839,7 +840,7 @@ F test/securedel2.test 2d54c28e46eb1fd6902089958b20b1b056c6f1c5
F test/select1.test fc2a61f226a649393664ad54bc5376631801517c
F test/select2.test 352480e0e9c66eda9c3044e412abdf5be0215b56
F test/select3.test 2ce595f8fb8e2ac10071d3b4e424cadd4634a054
F test/select4.test e20e8ce47b558de80616102ef273704cf0d48a3b
F test/select4.test ddcaffe16252e84b85550d775b7a03bcf0d390b688122ca653d7199e469e3d5e
F test/select5.test e758b8ef94f69b111df4cb819008856655dcd535
F test/select6.test 39eac4a5c03650b2b473c532882273283ee8b7a0
F test/select7.test 7fd2ef598cfabb6b9ff6ac13973b91d0527df49d
@@ -1171,7 +1172,7 @@ F test/where9.test 729c3ba9b47e8f9f1aab96bae7dad2a524f1d1a2
F test/whereA.test 4d253178d135ec46d1671e440cd8f2b916aa6e6b
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
F test/whereC.test d6f4ecd4fa2d9429681a5b22a25d2bda8e86ab8a
F test/whereD.test fd9120e262f9da3c45940f52aefeef4d15b904e5
F test/whereD.test ac6926e3f518d5b75b61e81c2b7c327565e07a4bd919b6c5c3a003ca0cf19021
F test/whereE.test b3a055eef928c992b0a33198a7b8dc10eea5ad2f
F test/whereF.test 5b2ba0dbe8074aa13e416b37c753991f0a2492d7
F test/whereG.test 69f5ec4b15760a8c860f80e2d55525669390aab3
@@ -1179,7 +1180,9 @@ F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2
F test/whereI.test 1d89199697919d4930be05a71e7fe620f114e622
F test/whereJ.test 55a3221706a7ab706293f17cc8f96da563bf0767
F test/whereK.test f8e3cf26a8513ecc7f514f54df9f0572c046c42b
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
F test/wherelfault.test c6627fcae6eda4defe3df1d74ec54634bad7121a8816f988f44c2068dccb5abc
F test/wherelimit.test 5f147fb5a589c2ac6477d09e119d07bfd50fca4a36e7d40d83e204f22326bf6d
F test/wherelimit2.test d01d10b9ee9eb6f00ea916866c5d026438eadd555387e3aecfb626eda33c2d73
F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972
@@ -1249,10 +1252,8 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P 8e4ac2ce24415926247961b00a62425ae85d6ffb
R 5875861747bb686954783d9ce4259b86
T +bgcolor * #d0c0ff
T +sym-release *
T +sym-version-3.8.9 *
P b29e02f877d7b85f783a9255794883a85590ff8eafac130a3234eb65d0f717f1
Q +0b1579caf06a2c42433b8bc9dc28c9ad381aa07c
R 58b03fadc224b39bfc051e3df4cb7c15
U drh
Z 05045ab8d5dbbaefca88cc23b8dca09c
Z f38d66f5b67fbce437220858b84fec1b
+1 -1
View File
@@ -1 +1 @@
8a8ffc862e96f57aa698f93de10dee28e69f6e09
11368e673cc5a9a3ccf96d1c6fa37213215405ed32fea969ea85d6e7b2234b59
+1 -1
View File
@@ -1619,7 +1619,7 @@ static void initAvgEq(Index *pIdx){
}
}
if( nDist100>nSum100 ){
if( nDist100>nSum100 && sumEq<nRow ){
avgEq = ((i64)100 * (nRow - sumEq))/(nDist100 - nSum100);
}
if( avgEq==0 ) avgEq = 1;
+4
View File
@@ -949,7 +949,11 @@ void sqlite3StartTable(
pTable->iPKey = -1;
pTable->pSchema = db->aDb[iDb].pSchema;
pTable->nRef = 1;
#ifdef SQLITE_DEFAULT_ROWEST
pTable->nRowLogEst = sqlite3LogEst(SQLITE_DEFAULT_ROWEST);
#else
pTable->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
#endif
assert( pParse->pNewTable==0 );
pParse->pNewTable = pTable;
+69 -33
View File
@@ -90,6 +90,9 @@ void sqlite3MaterializeView(
Parse *pParse, /* Parsing context */
Table *pView, /* View definition */
Expr *pWhere, /* Optional WHERE clause to be added */
ExprList *pOrderBy, /* Optional ORDER BY clause */
Expr *pLimit, /* Optional LIMIT clause */
Expr *pOffset, /* Optional OFFSET clause */
int iCur /* Cursor number for ephemeral table */
){
SelectDest dest;
@@ -106,7 +109,8 @@ void sqlite3MaterializeView(
assert( pFrom->a[0].pOn==0 );
assert( pFrom->a[0].pUsing==0 );
}
pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0);
pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, pOrderBy, 0,
pLimit, pOffset);
sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
sqlite3Select(pParse, pSel, &dest);
sqlite3SelectDelete(db, pSel);
@@ -131,18 +135,19 @@ Expr *sqlite3LimitWhere(
Expr *pOffset, /* The OFFSET clause. May be null */
char *zStmtType /* Either DELETE or UPDATE. For err msgs. */
){
Expr *pWhereRowid = NULL; /* WHERE rowid .. */
sqlite3 *db = pParse->db;
Expr *pLhs = NULL; /* LHS of IN(SELECT...) operator */
Expr *pInClause = NULL; /* WHERE rowid IN ( select ) */
Expr *pSelectRowid = NULL; /* SELECT rowid ... */
ExprList *pEList = NULL; /* Expression list contaning only pSelectRowid */
SrcList *pSelectSrc = NULL; /* SELECT rowid FROM x ... (dup of pSrc) */
Select *pSelect = NULL; /* Complete SELECT tree */
Table *pTab;
/* Check that there isn't an ORDER BY without a LIMIT clause.
*/
if( pOrderBy && (pLimit == 0) ) {
if( pOrderBy && pLimit==0 ) {
sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType);
goto limit_where_cleanup_2;
goto limit_where_cleanup;
}
/* We only need to generate a select expression if there
@@ -162,42 +167,50 @@ Expr *sqlite3LimitWhere(
** SELECT rowid FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1
** );
*/
pSelectRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0, 0);
if( pSelectRowid == 0 ) goto limit_where_cleanup_2;
pEList = sqlite3ExprListAppend(pParse, 0, pSelectRowid);
if( pEList == 0 ) goto limit_where_cleanup_2;
pTab = pSrc->a[0].pTab;
if( HasRowid(pTab) ){
pLhs = sqlite3PExpr(pParse, TK_ROW, 0, 0, 0);
pEList = sqlite3ExprListAppend(
pParse, 0, sqlite3PExpr(pParse, TK_ROW, 0, 0, 0)
);
}else{
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
if( pPk->nKeyCol==1 ){
const char *zName = pTab->aCol[pPk->aiColumn[0]].zName;
pLhs = sqlite3Expr(db, TK_ID, zName);
pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, zName));
}else{
sqlite3ErrorMsg(pParse,
"ORDER BY and LIMIT are not supported for table %s", pTab->zName
);
goto limit_where_cleanup;
}
}
/* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
** and the SELECT subtree. */
pSrc->a[0].pTab = 0;
pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0);
if( pSelectSrc == 0 ) {
sqlite3ExprListDelete(pParse->db, pEList);
goto limit_where_cleanup_2;
}
pSrc->a[0].pTab = pTab;
pSrc->a[0].pIndex = 0;
/* generate the SELECT expression tree. */
pSelect = sqlite3SelectNew(pParse,pEList,pSelectSrc,pWhere,0,0,
pOrderBy,0,pLimit,pOffset);
if( pSelect == 0 ) return 0;
pSelect = sqlite3SelectNew(pParse, pEList, pSelectSrc, pWhere, 0 ,0,
pOrderBy,0,pLimit,pOffset
);
/* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */
pWhereRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0, 0);
if( pWhereRowid == 0 ) goto limit_where_cleanup_1;
pInClause = sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0, 0);
if( pInClause == 0 ) goto limit_where_cleanup_1;
pInClause->x.pSelect = pSelect;
pInClause->flags |= EP_xIsSelect;
sqlite3ExprSetHeightAndFlags(pParse, pInClause);
pInClause = sqlite3PExpr(pParse, TK_IN, pLhs, 0, 0);
if( pInClause ){
pInClause->x.pSelect = pSelect;
ExprSetProperty(pInClause, EP_xIsSelect|EP_Subquery);
sqlite3ExprSetHeightAndFlags(pParse, pInClause);
}else{
sqlite3SelectDelete(pParse->db, pSelect);
}
return pInClause;
/* something went wrong. clean up anything allocated. */
limit_where_cleanup_1:
sqlite3SelectDelete(pParse->db, pSelect);
return 0;
limit_where_cleanup_2:
limit_where_cleanup:
sqlite3ExprDelete(pParse->db, pWhere);
sqlite3ExprListDelete(pParse->db, pOrderBy);
sqlite3ExprDelete(pParse->db, pLimit);
@@ -217,7 +230,10 @@ limit_where_cleanup_2:
void sqlite3DeleteFrom(
Parse *pParse, /* The parser context */
SrcList *pTabList, /* The table from which we should delete things */
Expr *pWhere /* The WHERE clause. May be null */
Expr *pWhere, /* The WHERE clause. May be null */
ExprList *pOrderBy, /* ORDER BY clause. May be null */
Expr *pLimit, /* LIMIT clause. May be null */
Expr *pOffset /* OFFSET clause. May be null */
){
Vdbe *v; /* The virtual database engine */
Table *pTab; /* The table from which records will be deleted */
@@ -262,6 +278,7 @@ void sqlite3DeleteFrom(
}
assert( pTabList->nSrc==1 );
/* Locate the table which we want to delete. This table has to be
** put in an SrcList structure because some of the subroutines we
** will be calling are designed to work with multiple tables and expect
@@ -285,6 +302,16 @@ void sqlite3DeleteFrom(
# define isView 0
#endif
#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
if( !isView ){
pWhere = sqlite3LimitWhere(
pParse, pTabList, pWhere, pOrderBy, pLimit, pOffset, "DELETE"
);
pOrderBy = 0;
pLimit = pOffset = 0;
}
#endif
/* If pTab is really a view, make sure it has been initialized.
*/
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
@@ -332,8 +359,12 @@ void sqlite3DeleteFrom(
*/
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
if( isView ){
sqlite3MaterializeView(pParse, pTab, pWhere, iTabCur);
sqlite3MaterializeView(pParse, pTab,
pWhere, pOrderBy, pLimit, pOffset, iTabCur
);
iDataCur = iIdxCur = iTabCur;
pOrderBy = 0;
pLimit = pOffset = 0;
}
#endif
@@ -553,6 +584,11 @@ delete_from_cleanup:
sqlite3AuthContextPop(&sContext);
sqlite3SrcListDelete(db, pTabList);
sqlite3ExprDelete(db, pWhere);
#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)
sqlite3ExprListDelete(db, pOrderBy);
sqlite3ExprDelete(db, pLimit);
sqlite3ExprDelete(db, pOffset);
#endif
sqlite3DbFree(db, aToOpen);
return;
}
+7 -17
View File
@@ -1751,6 +1751,11 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){
eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0];
if( prRhsHasNull && !pTab->aCol[iCol].notNull ){
#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
const i64 sOne = 1;
sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed,
iTab, 0, 0, (u8*)&sOne, P4_INT64);
#endif
*prRhsHasNull = ++pParse->nMem;
sqlite3SetHasNullFlag(v, iTab, *prRhsHasNull);
}
@@ -2206,17 +2211,6 @@ static void sqlite3ExprCodeIN(
}
#endif /* SQLITE_OMIT_SUBQUERY */
/*
** Duplicate an 8-byte value
*/
static char *dup8bytes(Vdbe *v, const char *in){
char *out = sqlite3DbMallocRaw(sqlite3VdbeDb(v), 8);
if( out ){
memcpy(out, in, 8);
}
return out;
}
#ifndef SQLITE_OMIT_FLOATING_POINT
/*
** Generate an instruction that will put the floating point
@@ -2229,12 +2223,10 @@ static char *dup8bytes(Vdbe *v, const char *in){
static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){
if( ALWAYS(z!=0) ){
double value;
char *zV;
sqlite3AtoF(z, &value, sqlite3Strlen30(z), SQLITE_UTF8);
assert( !sqlite3IsNaN(value) ); /* The new AtoF never returns NaN */
if( negateFlag ) value = -value;
zV = dup8bytes(v, (char*)&value);
sqlite3VdbeAddOp4(v, OP_Real, 0, iMem, 0, zV, P4_REAL);
sqlite3VdbeAddOp4Dup8(v, OP_Real, 0, iMem, 0, (u8*)&value, P4_REAL);
}
}
#endif
@@ -2260,10 +2252,8 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){
assert( z!=0 );
c = sqlite3DecOrHexToI64(z, &value);
if( c==0 || (c==2 && negFlag) ){
char *zV;
if( negFlag ){ value = c==2 ? SMALLEST_INT64 : -value; }
zV = dup8bytes(v, (char*)&value);
sqlite3VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64);
sqlite3VdbeAddOp4Dup8(v, OP_Int64, 0, iMem, 0, (u8*)&value, P4_INT64);
}else{
#ifdef SQLITE_OMIT_FLOATING_POINT
sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z);
+1 -1
View File
@@ -721,7 +721,7 @@ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
}
pParse->disableTriggers = 1;
sqlite3DeleteFrom(pParse, sqlite3SrcListDup(db, pName, 0), 0);
sqlite3DeleteFrom(pParse, sqlite3SrcListDup(db, pName, 0), 0, 0, 0, 0);
pParse->disableTriggers = 0;
/* If the DELETE has generated immediate foreign key constraint
+22 -8
View File
@@ -1649,9 +1649,14 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
/*
** pExpr points to an expression which implements a function. If
** it is appropriate to apply the LIKE optimization to that function
** then set aWc[0] through aWc[2] to the wildcard characters and
** return TRUE. If the function is not a LIKE-style function then
** return FALSE.
** then set aWc[0] through aWc[2] to the wildcard characters and the
** escape character and then return TRUE. If the function is not a
** LIKE-style function then return FALSE.
**
** The expression "a LIKE b ESCAPE c" is only considered a valid LIKE
** operator if c is a string literal that is exactly one byte in length.
** That one byte is stored in aWc[3]. aWc[3] is set to zero if there is
** no ESCAPE clause.
**
** *pIsNocase is set to true if uppercase and lowercase are equivalent for
** the function (default for LIKE). If the function makes the distinction
@@ -1660,19 +1665,28 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
*/
int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
FuncDef *pDef;
if( pExpr->op!=TK_FUNCTION
|| !pExpr->x.pList
|| pExpr->x.pList->nExpr!=2
){
int nExpr;
if( pExpr->op!=TK_FUNCTION || !pExpr->x.pList ){
return 0;
}
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
nExpr = pExpr->x.pList->nExpr;
pDef = sqlite3FindFunction(db, pExpr->u.zToken,
sqlite3Strlen30(pExpr->u.zToken),
2, SQLITE_UTF8, 0);
nExpr, SQLITE_UTF8, 0);
if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_FUNC_LIKE)==0 ){
return 0;
}
if( nExpr<3 ){
aWc[3] = 0;
}else{
Expr *pEscape = pExpr->x.pList->a[2].pExpr;
char *zEscape;
if( pEscape->op!=TK_STRING ) return 0;
zEscape = pEscape->u.zToken;
if( zEscape[0]==0 || zEscape[1]!=0 ) return 0;
aWc[3] = zEscape[0];
}
/* The memcpy() statement assumes that the wildcard characters are
** the first three statements in the compareInfo structure. The
+4 -6
View File
@@ -711,15 +711,14 @@ cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W)
orderby_opt(O) limit_opt(L). {
sqlite3WithPush(pParse, C, 1);
sqlite3SrcListIndexedBy(pParse, X, &I);
W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "DELETE");
sqlite3DeleteFrom(pParse,X,W);
sqlite3DeleteFrom(pParse,X,W,O,L.pLimit,L.pOffset);
}
%endif
%ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W). {
sqlite3WithPush(pParse, C, 1);
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3DeleteFrom(pParse,X,W);
sqlite3DeleteFrom(pParse,X,W,0,0,0);
}
%endif
@@ -737,8 +736,7 @@ cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
sqlite3WithPush(pParse, C, 1);
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3ExprListCheckLength(pParse,Y,"set list");
W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "UPDATE");
sqlite3Update(pParse,X,Y,W,R);
sqlite3Update(pParse,X,Y,W,R,O,L.pLimit,L.pOffset);
}
%endif
%ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
@@ -747,7 +745,7 @@ cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
sqlite3WithPush(pParse, C, 1);
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3ExprListCheckLength(pParse,Y,"set list");
sqlite3Update(pParse,X,Y,W,R);
sqlite3Update(pParse,X,Y,W,R,0,0,0);
}
%endif
+2 -1
View File
@@ -635,7 +635,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
SrcList *pSrcList = pNC->pSrcList;
struct SrcList_item *pItem;
assert( pSrcList && pSrcList->nSrc==1 );
pItem = pSrcList->a;
pItem = pSrcList->a;
assert( HasRowid(pItem->pTab) && pItem->pTab->pSelect==0 );
pExpr->op = TK_COLUMN;
pExpr->pTab = pItem->pTab;
pExpr->iTable = pItem->iCursor;
+275 -61
View File
@@ -53,7 +53,9 @@ struct SortCtx {
int regReturn; /* Register holding block-output return address */
int labelBkOut; /* Start label for the block-output subroutine */
int addrSortIndex; /* Address of the OP_SorterOpen or OP_OpenEphemeral */
int labelDone; /* Jump here when done, ex: LIMIT reached */
u8 sortFlags; /* Zero or more SORTFLAG_* bits */
u8 bOrderedInnerLoop; /* ORDER BY correctly sorts the inner loop */
};
#define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */
@@ -501,6 +503,7 @@ static void pushOntoSorter(
int regRecord = ++pParse->nMem; /* Assembled sorter record */
int nOBSat = pSort->nOBSat; /* ORDER BY terms to skip */
int op; /* Opcode to add sorter record to sorter */
int iLimit; /* LIMIT counter */
assert( bSeq==0 || bSeq==1 );
if( nPrefixReg ){
@@ -510,6 +513,9 @@ static void pushOntoSorter(
regBase = pParse->nMem + 1;
pParse->nMem += nBase;
}
assert( pSelect->iOffset==0 || pSelect->iLimit!=0 );
iLimit = pSelect->iOffset ? pSelect->iOffset+1 : pSelect->iLimit;
pSort->labelDone = sqlite3VdbeMakeLabel(v);
sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, SQLITE_ECEL_DUP);
if( bSeq ){
sqlite3VdbeAddOp2(v, OP_Sequence, pSort->iECursor, regBase+nExpr);
@@ -517,7 +523,6 @@ static void pushOntoSorter(
if( nPrefixReg==0 ){
sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+bSeq, nData);
}
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nBase-nOBSat, regRecord);
if( nOBSat>0 ){
int regPrevKey; /* The first nOBSat columns of the previous row */
@@ -552,6 +557,10 @@ static void pushOntoSorter(
pSort->regReturn = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut);
sqlite3VdbeAddOp1(v, OP_ResetSorter, pSort->iECursor);
if( iLimit ){
sqlite3VdbeAddOp2(v, OP_IfNot, iLimit, pSort->labelDone);
VdbeCoverage(v);
}
sqlite3VdbeJumpHere(v, addrFirst);
sqlite3ExprCodeMove(pParse, regBase, regPrevKey, pSort->nOBSat);
sqlite3VdbeJumpHere(v, addrJmp);
@@ -562,17 +571,32 @@ static void pushOntoSorter(
op = OP_IdxInsert;
}
sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord);
if( pSelect->iLimit ){
if( iLimit ){
int addr;
int iLimit;
if( pSelect->iOffset ){
iLimit = pSelect->iOffset+1;
}else{
iLimit = pSelect->iLimit;
}
int r1 = 0;
/* Fill the sorter until it contains LIMIT+OFFSET entries. (The iLimit
** register is initialized with value of LIMIT+OFFSET.) After the sorter
** fills up, delete the least entry in the sorter after each insert.
** Thus we never hold more than the LIMIT+OFFSET rows in memory at once */
addr = sqlite3VdbeAddOp3(v, OP_IfNotZero, iLimit, 0, -1); VdbeCoverage(v);
sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor);
if( pSort->bOrderedInnerLoop ){
r1 = ++pParse->nMem;
sqlite3VdbeAddOp3(v, OP_Column, pSort->iECursor, nExpr, r1);
VdbeComment((v, "seq"));
}
sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor);
if( pSort->bOrderedInnerLoop ){
/* If the inner loop is driven by an index such that values from
** the same iteration of the inner loop are in sorted order, then
** immediately jump to the next iteration of an inner loop if the
** entry from the current iteration does not fit into the top
** LIMIT+OFFSET entries of the sorter. */
int iBrk = sqlite3VdbeCurrentAddr(v) + 2;
sqlite3VdbeAddOp3(v, OP_Eq, regBase+nExpr, iBrk, r1);
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
VdbeCoverage(v);
}
sqlite3VdbeJumpHere(v, addr);
}
}
@@ -1168,7 +1192,7 @@ static void generateSortTail(
SelectDest *pDest /* Write the sorted results here */
){
Vdbe *v = pParse->pVdbe; /* The prepared statement */
int addrBreak = sqlite3VdbeMakeLabel(v); /* Jump here to exit loop */
int addrBreak = pSort->labelDone; /* Jump here to exit loop */
int addrContinue = sqlite3VdbeMakeLabel(v); /* Jump here for next cycle */
int addr;
int addrOnce = 0;
@@ -1187,6 +1211,7 @@ static void generateSortTail(
struct ExprList_item *aOutEx = p->pEList->a;
#endif
assert( addrBreak<0 );
if( pSort->labelBkOut ){
sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut);
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrBreak);
@@ -2856,10 +2881,11 @@ static int multiSelectOrderBy(
** to the right and the left are evaluated, they use the correct
** collation.
*/
aPermute = sqlite3DbMallocRaw(db, sizeof(int)*nOrderBy);
aPermute = sqlite3DbMallocRaw(db, sizeof(int)*(nOrderBy + 1));
if( aPermute ){
struct ExprList_item *pItem;
for(i=0, pItem=pOrderBy->a; i<nOrderBy; i++, pItem++){
aPermute[0] = nOrderBy;
for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){
assert( pItem->u.x.iOrderByCol>0
&& pItem->u.x.iOrderByCol<=p->pEList->nExpr );
aPermute[i] = pItem->u.x.iOrderByCol - 1;
@@ -3723,6 +3749,83 @@ static int flattenSubquery(
}
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
** Make copies of relevant WHERE clause terms of the outer query into
** the WHERE clause of subquery. Example:
**
** SELECT * FROM (SELECT a AS x, c-d AS y FROM t1) WHERE x=5 AND y=10;
**
** Transformed into:
**
** SELECT * FROM (SELECT a AS x, c-d AS y FROM t1 WHERE a=5 AND c-d=10)
** WHERE x=5 AND y=10;
**
** The hope is that the terms added to the inner query will make it more
** efficient.
**
** Do not attempt this optimization if:
**
** (1) The inner query is an aggregate. (In that case, we'd really want
** to copy the outer WHERE-clause terms onto the HAVING clause of the
** inner query. But they probably won't help there so do not bother.)
**
** (2) The inner query is the recursive part of a common table expression.
**
** (3) The inner query has a LIMIT clause (since the changes to the WHERE
** close would change the meaning of the LIMIT).
**
** (4) The inner query is the right operand of a LEFT JOIN. (The caller
** enforces this restriction since this routine does not have enough
** information to know.)
**
** (5) The WHERE clause expression originates in the ON or USING clause
** of a LEFT JOIN.
**
** Return 0 if no changes are made and non-zero if one or more WHERE clause
** terms are duplicated into the subquery.
*/
static int pushDownWhereTerms(
sqlite3 *db, /* The database connection (for malloc()) */
Select *pSubq, /* The subquery whose WHERE clause is to be augmented */
Expr *pWhere, /* The WHERE clause of the outer query */
int iCursor /* Cursor number of the subquery */
){
Expr *pNew;
int nChng = 0;
Select *pX; /* For looping over compound SELECTs in pSubq */
if( pWhere==0 ) return 0;
for(pX=pSubq; pX; pX=pX->pPrior){
if( (pX->selFlags & (SF_Aggregate|SF_Recursive))!=0 ){
testcase( pX->selFlags & SF_Aggregate );
testcase( pX->selFlags & SF_Recursive );
testcase( pX!=pSubq );
return 0; /* restrictions (1) and (2) */
}
}
if( pSubq->pLimit!=0 ){
return 0; /* restriction (3) */
}
while( pWhere->op==TK_AND ){
nChng += pushDownWhereTerms(db, pSubq, pWhere->pRight, iCursor);
pWhere = pWhere->pLeft;
}
if( ExprHasProperty(pWhere,EP_FromJoin) ) return 0; /* restriction 5 */
if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){
nChng++;
while( pSubq ){
pNew = sqlite3ExprDup(db, pWhere, 0);
pNew = substExpr(db, pNew, iCursor, pSubq->pEList);
pSubq->pWhere = sqlite3ExprAnd(db, pSubq->pWhere, pNew);
pSubq = pSubq->pPrior;
}
}
return nChng;
}
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
/*
** Based on the contents of the AggInfo structure indicated by the first
** argument, this function checks if the following are true:
@@ -4685,6 +4788,94 @@ static void explainSimpleCount(
# define explainSimpleCount(a,b,c)
#endif
#ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION
/*
** Attempt to transform a query of the form
**
** SELECT count(*) FROM (SELECT x FROM t1 UNION ALL SELECT y FROM t2)
**
** Into this:
**
** SELECT (SELECT count(*) FROM t1)+(SELECT count(*) FROM t2)
**
** The transformation only works if all of the following are true:
**
** * The subquery is a UNION ALL of two or more terms
** * There is no WHERE or GROUP BY or HAVING clauses on the subqueries
** * The outer query is a simple count(*)
**
** Return TRUE if the optimization is undertaken.
*/
static int countOfViewOptimization(Parse *pParse, Select *p){
Select *pSub, *pPrior;
Expr *pExpr;
Expr *pCount;
sqlite3 *db;
if( (p->selFlags & SF_Aggregate)==0 ) return 0; /* This is an aggregate query */
if( p->pEList->nExpr!=1 ) return 0; /* Single result column */
pExpr = p->pEList->a[0].pExpr;
if( pExpr->op!=TK_AGG_FUNCTION ) return 0; /* Result is an aggregate */
if( sqlite3_stricmp(pExpr->u.zToken,"count") ) return 0; /* Must be count() */
if( pExpr->x.pList!=0 ) return 0; /* Must be count(*) */
if( p->pSrc->nSrc!=1 ) return 0; /* One table in the FROM clause */
pSub = p->pSrc->a[0].pSelect;
if( pSub==0 ) return 0; /* The FROM is a subquery */
if( pSub->pPrior==0 ) return 0; /* Must be a compound subquery */
do{
if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */
if( pSub->pWhere ) return 0; /* No WHERE clause */
if( pSub->selFlags & SF_Aggregate ) return 0; /* Not an aggregate */
pSub = pSub->pPrior; /* Repeat over compound terms */
}while( pSub );
/* If we reach this point, that means it is OK to perform the transformation */
db = pParse->db;
pCount = pExpr;
pExpr = 0;
pSub = p->pSrc->a[0].pSelect;
p->pSrc->a[0].pSelect = 0;
sqlite3SrcListDelete(db, p->pSrc);
p->pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*p->pSrc));
while( pSub ){
Expr *pTerm;
pPrior = pSub->pPrior;
pSub->pPrior = 0;
pSub->pNext = 0;
pSub->selFlags |= SF_Aggregate;
pSub->selFlags &= ~SF_Compound;
pSub->nSelectRow = 0;
sqlite3ExprListDelete(db, pSub->pEList);
pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount;
pSub->pEList = sqlite3ExprListAppend(pParse, 0, pTerm);
pTerm = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0);
if( pTerm ){
pTerm->x.pSelect = pSub;
ExprSetProperty(pTerm, EP_xIsSelect|EP_Subquery);
sqlite3ExprSetHeightAndFlags(pParse, pTerm);
if( pExpr==0 ){
pExpr = pTerm;
}else{
pExpr = sqlite3PExpr(pParse, TK_PLUS, pTerm, pExpr, 0);
}
}else{
sqlite3SelectDelete(db, pSub);
}
pSub = pPrior;
}
p->pEList->a[0].pExpr = pExpr;
p->selFlags &= ~SF_Aggregate;
#if SELECTTRACE_ENABLED
if( sqlite3SelectTrace & 0x400 ){
SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
return 1;
}
#endif /* SQLITE_COUNTOFVIEW_OPTIMIZATION */
/*
** Generate code for the SELECT statement given in the p argument.
**
@@ -4826,59 +5017,71 @@ int sqlite3Select(
p->selFlags |= SF_Aggregate;
}
i = -1;
}else if( pTabList->nSrc==1
&& OptimizationEnabled(db, SQLITE_SubqCoroutine)
){
/* Implement a co-routine that will return a single row of the result
** set on each invocation.
*/
int addrTop = sqlite3VdbeCurrentAddr(v)+1;
pItem->regReturn = ++pParse->nMem;
sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn, 0, addrTop);
VdbeComment((v, "%s", pItem->pTab->zName));
pItem->addrFillSub = addrTop;
sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn);
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
sqlite3Select(pParse, pSub, &dest);
pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
pItem->viaCoroutine = 1;
pItem->regResult = dest.iSdst;
sqlite3VdbeAddOp1(v, OP_EndCoroutine, pItem->regReturn);
sqlite3VdbeJumpHere(v, addrTop-1);
sqlite3ClearTempRegCache(pParse);
}else{
/* Generate a subroutine that will fill an ephemeral table with
** the content of this subquery. pItem->addrFillSub will point
** to the address of the generated subroutine. pItem->regReturn
** is a register allocated to hold the subroutine return address
*/
int topAddr;
int onceAddr = 0;
int retAddr;
assert( pItem->addrFillSub==0 );
pItem->regReturn = ++pParse->nMem;
topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
pItem->addrFillSub = topAddr+1;
if( pItem->isCorrelated==0 ){
/* If the subquery is not correlated and if we are not inside of
** a trigger, then we only need to compute the value of the subquery
** once. */
onceAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v);
VdbeComment((v, "materialize \"%s\"", pItem->pTab->zName));
}else{
VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName));
if( (pItem->jointype & JT_OUTER)==0
&& pushDownWhereTerms(db, pSub, p->pWhere, pItem->iCursor)
){
#if SELECTTRACE_ENABLED
if( sqlite3SelectTrace & 0x100 ){
sqlite3DebugPrintf("After WHERE-clause push-down:\n");
sqlite3TreeViewSelect(0, p, 0);
}
#endif
}
if( pTabList->nSrc==1
&& OptimizationEnabled(db, SQLITE_SubqCoroutine)
){
/* Implement a co-routine that will return a single row of the result
** set on each invocation.
*/
int addrTop = sqlite3VdbeCurrentAddr(v)+1;
pItem->regReturn = ++pParse->nMem;
sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn, 0, addrTop);
VdbeComment((v, "%s", pItem->pTab->zName));
pItem->addrFillSub = addrTop;
sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn);
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
sqlite3Select(pParse, pSub, &dest);
pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
pItem->viaCoroutine = 1;
pItem->regResult = dest.iSdst;
sqlite3VdbeAddOp1(v, OP_EndCoroutine, pItem->regReturn);
sqlite3VdbeJumpHere(v, addrTop-1);
sqlite3ClearTempRegCache(pParse);
}else{
/* Generate a subroutine that will fill an ephemeral table with
** the content of this subquery. pItem->addrFillSub will point
** to the address of the generated subroutine. pItem->regReturn
** is a register allocated to hold the subroutine return address
*/
int topAddr;
int onceAddr = 0;
int retAddr;
assert( pItem->addrFillSub==0 );
pItem->regReturn = ++pParse->nMem;
topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
pItem->addrFillSub = topAddr+1;
if( pItem->isCorrelated==0 ){
/* If the subquery is not correlated and if we are not inside of
** a trigger, then we only need to compute the value of the subquery
** once. */
onceAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v);
VdbeComment((v, "materialize \"%s\"", pItem->pTab->zName));
}else{
VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName));
}
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
sqlite3Select(pParse, pSub, &dest);
pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
VdbeComment((v, "end %s", pItem->pTab->zName));
sqlite3VdbeChangeP1(v, topAddr, retAddr);
sqlite3ClearTempRegCache(pParse);
}
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
sqlite3Select(pParse, pSub, &dest);
pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
VdbeComment((v, "end %s", pItem->pTab->zName));
sqlite3VdbeChangeP1(v, topAddr, retAddr);
sqlite3ClearTempRegCache(pParse);
}
if( /*pParse->nErr ||*/ db->mallocFailed ){
if( db->mallocFailed ){
goto select_end;
}
pParse->nHeight -= sqlite3SelectExprHeight(p);
@@ -4908,6 +5111,16 @@ int sqlite3Select(
}
#endif
#ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION
if( OptimizationEnabled(db, SQLITE_QueryFlattener|SQLITE_CountOfView)
&& countOfViewOptimization(pParse, p)
){
if( db->mallocFailed ) goto select_end;
pEList = p->pEList;
pTabList = p->pSrc;
}
#endif
/* If the query is DISTINCT with an ORDER BY but is not an aggregate, and
** if the select-list is the same as the ORDER BY list, then this query
** can be rewritten as a GROUP BY. In other words, this:
@@ -5001,6 +5214,7 @@ int sqlite3Select(
}
if( sSort.pOrderBy ){
sSort.nOBSat = sqlite3WhereIsOrdered(pWInfo);
sSort.bOrderedInnerLoop = sqlite3WhereOrderedInnerLoop(pWInfo);
if( sSort.nOBSat==sSort.pOrderBy->nExpr ){
sSort.pOrderBy = 0;
}
+6 -3
View File
@@ -1245,6 +1245,7 @@ struct sqlite3 {
#define SQLITE_Transitive 0x0200 /* Transitive constraints */
#define SQLITE_OmitNoopJoin 0x0400 /* Omit unused tables in joins */
#define SQLITE_Stat34 0x0800 /* Use STAT3 or STAT4 data */
#define SQLITE_CountOfView 0x1000 /* The count-of-view optimization */
#define SQLITE_AllOpts 0xffff /* All optimizations */
/*
@@ -2276,6 +2277,7 @@ struct SrcList {
#define WHERE_WANT_DISTINCT 0x0400 /* All output needs to be distinct */
#define WHERE_SORTBYGROUP 0x0800 /* Support sqlite3WhereIsSorted() */
#define WHERE_REOPEN_IDX 0x1000 /* Try to use OP_ReopenIdx */
#define WHERE_ORDERBY_LIMIT 0x2000 /* ORDERBY+LIMIT on the inner loop */
/* Allowed return values from sqlite3WhereIsDistinct()
*/
@@ -3282,13 +3284,14 @@ void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)
Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,Expr*,char*);
#endif
void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
void sqlite3DeleteFrom(Parse*, SrcList*, Expr*, ExprList*, Expr*, Expr*);
void sqlite3Update(Parse*, SrcList*, ExprList*,Expr*,int,ExprList*,Expr*,Expr*);
WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int);
void sqlite3WhereEnd(WhereInfo*);
u64 sqlite3WhereOutputRowCount(WhereInfo*);
int sqlite3WhereIsDistinct(WhereInfo*);
int sqlite3WhereIsOrdered(WhereInfo*);
int sqlite3WhereOrderedInnerLoop(WhereInfo*);
int sqlite3WhereIsSorted(WhereInfo*);
int sqlite3WhereContinueLabel(WhereInfo*);
int sqlite3WhereBreakLabel(WhereInfo*);
@@ -3382,7 +3385,7 @@ int sqlite3SafetyCheckSickOrOk(sqlite3*);
void sqlite3ChangeCookie(Parse*, int);
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,Expr*,int);
#endif
#ifndef SQLITE_OMIT_TRIGGER
+2 -2
View File
@@ -730,7 +730,7 @@ static int codeTriggerProgram(
targetSrcList(pParse, pStep),
sqlite3ExprListDup(db, pStep->pExprList, 0),
sqlite3ExprDup(db, pStep->pWhere, 0),
pParse->eOrconf
pParse->eOrconf, 0, 0, 0
);
break;
}
@@ -746,7 +746,7 @@ static int codeTriggerProgram(
case TK_DELETE: {
sqlite3DeleteFrom(pParse,
targetSrcList(pParse, pStep),
sqlite3ExprDup(db, pStep->pWhere, 0)
sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0, 0
);
break;
}
+24 -2
View File
@@ -91,7 +91,10 @@ void sqlite3Update(
SrcList *pTabList, /* The table in which we should change things */
ExprList *pChanges, /* Things to be changed */
Expr *pWhere, /* The WHERE clause. May be null */
int onError /* How to handle constraint errors */
int onError, /* How to handle constraint errors */
ExprList *pOrderBy, /* ORDER BY clause. May be null */
Expr *pLimit, /* LIMIT clause. May be null */
Expr *pOffset /* OFFSET clause. May be null */
){
int i, j; /* Loop counters */
Table *pTab; /* The table to be updated */
@@ -171,6 +174,16 @@ void sqlite3Update(
# define isView 0
#endif
#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
if( !isView ){
pWhere = sqlite3LimitWhere(
pParse, pTabList, pWhere, pOrderBy, pLimit, pOffset, "UPDATE"
);
pOrderBy = 0;
pLimit = pOffset = 0;
}
#endif
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
goto update_cleanup;
}
@@ -331,7 +344,11 @@ void sqlite3Update(
*/
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
if( isView ){
sqlite3MaterializeView(pParse, pTab, pWhere, iDataCur);
sqlite3MaterializeView(pParse, pTab,
pWhere, pOrderBy, pLimit, pOffset, iDataCur
);
pOrderBy = 0;
pLimit = pOffset = 0;
}
#endif
@@ -665,6 +682,11 @@ update_cleanup:
sqlite3SrcListDelete(db, pTabList);
sqlite3ExprListDelete(db, pChanges);
sqlite3ExprDelete(db, pWhere);
#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)
sqlite3ExprListDelete(db, pOrderBy);
sqlite3ExprDelete(db, pLimit);
sqlite3ExprDelete(db, pOffset);
#endif
return;
}
/* Make sure "isView" and other macros defined above are undefined. Otherwise
+40 -5
View File
@@ -1980,11 +1980,14 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
** The permutation is only valid until the next OP_Compare that has
** the OPFLAG_PERMUTE bit set in P5. Typically the OP_Permutation should
** occur immediately prior to the OP_Compare.
**
** The first integer in the P4 integer array is the length of the array
** and does not become part of the permutation.
*/
case OP_Permutation: {
assert( pOp->p4type==P4_INTARRAY );
assert( pOp->p4.ai );
aPermute = pOp->p4.ai;
aPermute = pOp->p4.ai + 1;
break;
}
@@ -2290,12 +2293,16 @@ case OP_Column: {
u16 fx; /* pDest->flags value */
Mem *pReg; /* PseudoTable input register */
pC = p->apCsr[pOp->p1];
p2 = pOp->p2;
/* If the cursor cache is stale, bring it up-to-date */
rc = sqlite3VdbeCursorMoveto(&pC, &p2);
assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
pDest = &aMem[pOp->p3];
memAboutToChange(p, pDest);
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( p2<pC->nField );
aOffset = pC->aOffset;
@@ -2306,8 +2313,6 @@ case OP_Column: {
assert( pCrsr!=0 || pC->pseudoTableReg>0 ); /* pCrsr NULL on PseudoTables */
assert( pCrsr!=0 || pC->nullRow ); /* pC->nullRow on PseudoTables */
/* If the cursor cache is stale, bring it up-to-date */
rc = sqlite3VdbeCursorMoveto(pC);
if( rc ) goto abort_due_to_error;
if( pC->cacheStatus!=p->cacheCtr ){
if( pC->nullRow ){
@@ -3499,6 +3504,26 @@ case OP_Close: {
break;
}
#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
/* Opcode: ColumnsUsed P1 * * P4 *
**
** This opcode (which only exists if SQLite was compiled with
** SQLITE_ENABLE_COLUMN_USED_MASK) identifies which columns of the
** table or index for cursor P1 are used. P4 is a 64-bit integer
** (P4_INT64) in which the first 63 bits are one for each of the
** first 63 columns of the table or index that are actually used
** by the cursor. The high-order bit is set if any column after
** the 64th is used.
*/
case OP_ColumnsUsed: {
VdbeCursor *pC;
pC = p->apCsr[pOp->p1];
assert( pC->pCursor );
pC->maskUsed = *(u64*)pOp->p4.pI64;
break;
}
#endif
/* Opcode: SeekGE P1 P2 P3 P4 *
** Synopsis: key=r[P3@P4]
**
@@ -3725,7 +3750,7 @@ case OP_SeekGT: { /* jump, in3 */
break;
}
/* Opcode: Seek P1 P2 * * *
/* Opcode: Seek P1 P2 P3 P4 *
** Synopsis: intkey=r[P2]
**
** P1 is an open table cursor and P2 is a rowid integer. Arrange
@@ -3734,6 +3759,13 @@ case OP_SeekGT: { /* jump, in3 */
** This is actually a deferred seek. Nothing actually happens until
** the cursor is used to read a record. That way, if no reads
** occur, no unnecessary I/O happens.
**
** P4 may contain an array of integers (type P4_INTARRAY) containing
** one entry for each column in the table P1 is open on. If so, then
** parameter P3 is a cursor open on a database index. If array entry
** a[i] is non-zero, then reading column (a[i]-1) from cursor P3 is
** equivalent to performing the deferred seek and then reading column i
** from P1.
*/
case OP_Seek: { /* in2 */
VdbeCursor *pC;
@@ -3747,6 +3779,9 @@ case OP_Seek: { /* in2 */
pIn2 = &aMem[pOp->p2];
pC->movetoTarget = sqlite3VdbeIntValue(pIn2);
pC->deferredMoveto = 1;
assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 );
pC->aAltMap = pOp->p4.ai;
pC->pAltCursor = p->apCsr[pOp->p3];
break;
}
+1
View File
@@ -169,6 +169,7 @@ int sqlite3VdbeAddOp1(Vdbe*,int,int);
int sqlite3VdbeAddOp2(Vdbe*,int,int,int);
int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int);
int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int);
int sqlite3VdbeAddOp4Dup8(Vdbe*,int,int,int,int,const u8*,int);
int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int);
int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp, int iLineno);
void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
+7 -3
View File
@@ -60,6 +60,7 @@ typedef struct AuxData AuxData;
** Every cursor that the virtual machine has open is represented by an
** instance of the following structure.
*/
typedef struct VdbeCursor VdbeCursor;
struct VdbeCursor {
BtCursor *pCursor; /* The cursor structure of the backend */
Btree *pBt; /* Separate file holding temporary table */
@@ -83,7 +84,11 @@ struct VdbeCursor {
i64 seqCount; /* Sequence counter */
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */
VdbeCursor *pAltCursor; /* Associated index cursor from which to read */
int *aAltMap; /* Mapping from table to index column numbers */
#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
u64 maskUsed; /* Mask of columns used by this cursor */
#endif
/* Cached information about the header for the data record that the
** cursor is currently pointing to. Only valid if cacheStatus matches
** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of
@@ -104,7 +109,6 @@ struct VdbeCursor {
** static element declared in the structure. nField total array slots for
** aType[] and nField+1 array slots for aOffset[] */
};
typedef struct VdbeCursor VdbeCursor;
/*
** When a sub-program is executed (OP_Program), a structure of this type
@@ -393,7 +397,7 @@ struct Vdbe {
*/
void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*);
void sqliteVdbePopStack(Vdbe*,int);
int sqlite3VdbeCursorMoveto(VdbeCursor*);
int sqlite3VdbeCursorMoveto(VdbeCursor**, int*);
int sqlite3VdbeCursorRestore(VdbeCursor*);
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
void sqlite3VdbePrintOp(FILE*, int, Op*);
+40 -2
View File
@@ -222,6 +222,23 @@ int sqlite3VdbeAddOp4(
return addr;
}
/*
** Add an opcode that includes the p4 value with a P4_INT64 type.
*/
int sqlite3VdbeAddOp4Dup8(
Vdbe *p, /* Add the opcode to this VM */
int op, /* The new opcode */
int p1, /* The P1 operand */
int p2, /* The P2 operand */
int p3, /* The P3 operand */
const u8 *zP4, /* The P4 operand */
int p4type /* P4 operand type */
){
char *p4copy = sqlite3DbMallocRaw(sqlite3VdbeDb(p), 8);
if( p4copy ) memcpy(p4copy, zP4, 8);
return sqlite3VdbeAddOp4(p, op, p1, p2, p3, p4copy, p4type);
}
/*
** Add an OP_ParseSchema opcode. This routine is broken out from
** sqlite3VdbeAddOp4() since it needs to also needs to mark all btrees
@@ -1123,7 +1140,21 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
}
#endif
case P4_INTARRAY: {
sqlite3_snprintf(nTemp, zTemp, "intarray");
int i, j;
int *ai = pOp->p4.ai;
int n = ai[0]; /* The first element of an INTARRAY is always the
** count of the number of elements to follow */
zTemp[0] = '[';
for(i=j=1; i<n && j<nTemp-7; i++){
if( j>1 ) zTemp[j++] = ',';
sqlite3_snprintf(nTemp-j, zTemp+j, "%d", ai[i]);
j += sqlite3Strlen30(zTemp+j);
}
if( i<n ){
memcpy(zTemp+j, ",...]", 6);
}else{
memcpy(zTemp+j, "]", 2);
}
break;
}
case P4_SUBPROGRAM: {
@@ -2818,8 +2849,15 @@ int sqlite3VdbeCursorRestore(VdbeCursor *p){
** If the cursor is already pointing to the correct row and that row has
** not been deleted out from under the cursor, then this routine is a no-op.
*/
int sqlite3VdbeCursorMoveto(VdbeCursor *p){
int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){
VdbeCursor *p = *pp;
if( p->deferredMoveto ){
int iMap;
if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 ){
*pp = p->pAltCursor;
*piCol = iMap - 1;
return SQLITE_OK;
}
return handleDeferredMoveto(p);
}
if( p->pCursor && sqlite3BtreeCursorHasMoved(p->pCursor) ){
+171 -24
View File
@@ -42,6 +42,18 @@ int sqlite3WhereIsOrdered(WhereInfo *pWInfo){
return pWInfo->nOBSat;
}
/*
** Return TRUE if the innermost loop of the WHERE clause implementation
** returns rows in ORDER BY order for complete run of the inner loop.
**
** Across multiple iterations of outer loops, the output rows need not be
** sorted. As long as rows are sorted for just the innermost loop, this
** routine can return TRUE.
*/
int sqlite3WhereOrderedInnerLoop(WhereInfo *pWInfo){
return pWInfo->bOrderedInnerLoop;
}
/*
** Return the VDBE address or label to jump to in order to continue
** immediately with the next row of a WHERE clause.
@@ -645,7 +657,7 @@ static int isLikeOrGlob(
ExprList *pList; /* List of operands to the LIKE operator */
int c; /* One character in z[] */
int cnt; /* Number of non-wildcard prefix characters */
char wc[3]; /* Wildcard characters */
char wc[4]; /* Wildcard characters */
sqlite3 *db = pParse->db; /* Database connection */
sqlite3_value *pVal = 0;
int op; /* Opcode of pRight */
@@ -683,16 +695,43 @@ static int isLikeOrGlob(
z = pRight->u.zToken;
}
if( z ){
/* Count the number of prefix characters prior to the first wildcard */
cnt = 0;
while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){
cnt++;
if( c==wc[3] && z[cnt]!=0 ){
if( z[cnt++]>0xc0 ) while( (z[cnt]&0xc0)==0x80 ){ cnt++; }
}
}
/* The optimization is possible only if (1) the pattern does not begin
** with a wildcard and if (2) the non-wildcard prefix does not end with
** an (illegal 0xff) character. The second condition is necessary so
** that we can increment the prefix key to find an upper bound for the
** range search.
*/
if( cnt!=0 && 255!=(u8)z[cnt-1] ){
Expr *pPrefix;
/* A "complete" match if the pattern ends with "*" or "%" */
*pisComplete = c==wc[0] && z[cnt+1]==0;
/* Get the pattern prefix. Remove all escapes from the prefix. */
pPrefix = sqlite3Expr(db, TK_STRING, z);
if( pPrefix ) pPrefix->u.zToken[cnt] = 0;
if( pPrefix ){
int iFrom, iTo;
char *zNew = pPrefix->u.zToken;
zNew[cnt] = 0;
for(iFrom=iTo=0; iFrom<cnt; iFrom++){
if( zNew[iFrom]==wc[3] ) iFrom++;
zNew[iTo++] = zNew[iFrom];
}
zNew[iTo] = 0;
}
*ppPrefix = pPrefix;
/* If the RHS pattern is a bound parameter, make arrangements to
** reprepare the statement when that parameter is rebound */
if( op==TK_VARIABLE ){
Vdbe *v = pParse->pVdbe;
sqlite3VdbeSetVarmask(v, pRight->iColumn);
@@ -2209,7 +2248,7 @@ static int whereKeyStats(
iGap = iGap/3;
}
aStat[0] = iLower + iGap;
aStat[1] = pIdx->aAvgEq[iCol];
aStat[1] = pIdx->aAvgEq[nField-1];
}
/* Restore the pRec->nField value before returning. */
@@ -3236,6 +3275,55 @@ static void whereLikeOptimizationStringFixup(
}
}
/*
** Cursor iCur is open on an intkey b-tree (a table). Register iRowid contains
** a rowid value just read from cursor iIdxCur, open on index pIdx. This
** function generates code to do a deferred seek of cursor iCur to the
** rowid stored in register iRowid.
**
** Normally, this is just:
**
** OP_Seek $iCur $iRowid
**
** However, if the scan currently being coded is a branch of an OR-loop and
** the statement currently being coded is a SELECT, then P3 of the OP_Seek
** is set to iIdxCur and P4 is set to point to an array of integers
** containing one entry for each column of the table cursor iCur is open
** on. For each table column, if the column is the i'th column of the
** index, then the corresponding array entry is set to (i+1). If the column
** does not appear in the index at all, the array entry is set to 0.
*/
static void codeDeferredSeek(
WhereInfo *pWInfo, /* Where clause context */
Index *pIdx, /* Index scan is using */
int iCur, /* Cursor for IPK b-tree */
int iRowid, /* Register containing rowid to seek to */
int iIdxCur /* Index cursor */
){
Parse *pParse = pWInfo->pParse; /* Parse context */
Vdbe *v = pParse->pVdbe; /* Vdbe to generate code within */
assert( iIdxCur>0 );
assert( pIdx->aiColumn[pIdx->nColumn-1]==-1 );
sqlite3VdbeAddOp3(v, OP_Seek, iCur, iRowid, iIdxCur);
if( (pWInfo->wctrlFlags & WHERE_FORCE_TABLE)
&& DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask)
){
int i;
Table *pTab = pIdx->pTable;
int *ai = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*(pTab->nCol+1));
if( ai ){
ai[0] = pTab->nCol;
for(i=0; i<pIdx->nColumn-1; i++){
assert( pIdx->aiColumn[i]<pTab->nCol );
if( pIdx->aiColumn[i]>=0 ) ai[pIdx->aiColumn[i]+1] = i+1;
}
sqlite3VdbeChangeP4(v, -1, (char*)ai, P4_INTARRAY);
}
}
}
/*
** Generate code for the start of the iLevel-th loop in the WHERE clause
** implementation described by pWInfo.
@@ -3714,7 +3802,7 @@ static Bitmask codeOneLoopStart(
iRowidReg = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg);
sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */
codeDeferredSeek(pWInfo, pIdx, iCur, iRowidReg, iIdxCur);
}else if( iCur!=iIdxCur ){
Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
iRowidReg = sqlite3GetTempRange(pParse, pPk->nKeyCol);
@@ -5602,7 +5690,7 @@ static i8 wherePathSatisfiesOrderBy(
WhereInfo *pWInfo, /* The WHERE clause */
ExprList *pOrderBy, /* ORDER BY or GROUP BY or DISTINCT clause to check */
WherePath *pPath, /* The WherePath to check */
u16 wctrlFlags, /* Might contain WHERE_GROUPBY or WHERE_DISTINCTBY */
u16 wctrlFlags, /* WHERE_GROUPBY, _DISTINCTBY or _ORDERBY_LIMIT */
u16 nLoop, /* Number of entries in pPath->aLoop[] */
WhereLoop *pLast, /* Add this WhereLoop to the end of pPath->aLoop[] */
Bitmask *pRevMask /* OUT: Mask of WhereLoops to run in reverse order */
@@ -5613,6 +5701,7 @@ static i8 wherePathSatisfiesOrderBy(
u8 isOrderDistinct; /* All prior WhereLoops are order-distinct */
u8 distinctColumns; /* True if the loop has UNIQUE NOT NULL columns */
u8 isMatch; /* iColumn matches a term of the ORDER BY clause */
u16 eqOpMask; /* Allowed equality operators */
u16 nKeyCol; /* Number of key columns in pIndex */
u16 nColumn; /* Total number of ordered columns in the index */
u16 nOrderBy; /* Number terms in the ORDER BY clause */
@@ -5663,8 +5752,16 @@ static i8 wherePathSatisfiesOrderBy(
obDone = MASKBIT(nOrderBy)-1;
orderDistinctMask = 0;
ready = 0;
eqOpMask = WO_EQ | WO_ISNULL;
if( wctrlFlags & WHERE_ORDERBY_LIMIT ) eqOpMask |= WO_IN;
for(iLoop=0; isOrderDistinct && obSat<obDone && iLoop<=nLoop; iLoop++){
if( iLoop>0 ) ready |= pLoop->maskSelf;
if( iLoop<nLoop ){
pLoop = pPath->aLoop[iLoop];
if( wctrlFlags & WHERE_ORDERBY_LIMIT ) continue;
}else{
pLoop = pLast;
}
pLoop = iLoop<nLoop ? pPath->aLoop[iLoop] : pLast;
if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
if( pLoop->u.vtab.isOrdered ) obSat = obDone;
@@ -5683,8 +5780,16 @@ static i8 wherePathSatisfiesOrderBy(
if( pOBExpr->op!=TK_COLUMN ) continue;
if( pOBExpr->iTable!=iCur ) continue;
pTerm = findTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn,
~ready, WO_EQ|WO_ISNULL, 0);
~ready, eqOpMask, 0);
if( pTerm==0 ) continue;
if( pTerm->eOperator==WO_IN ){
/* IN terms are only valid for sorting in the ORDER BY LIMIT
** optimization, and then only if they are actually used
** by the query plan */
assert( wctrlFlags & WHERE_ORDERBY_LIMIT );
for(j=0; j<pLoop->nLTerm && pTerm!=pLoop->aLTerm[j]; j++){}
if( j>=pLoop->nLTerm ) continue;
}
if( (pTerm->eOperator&WO_EQ)!=0 && pOBExpr->iColumn>=0 ){
const char *z1, *z2;
pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr);
@@ -5721,10 +5826,12 @@ static i8 wherePathSatisfiesOrderBy(
for(j=0; j<nColumn; j++){
u8 bOnce; /* True to run the ORDER BY search loop */
/* Skip over == and IS NULL terms */
/* Skip over == and IS NULL terms
** (Also skip IN terms when doing WHERE_ORDERBY_LIMIT processing)
*/
if( j<pLoop->u.btree.nEq
&& pLoop->nSkip==0
&& ((i = pLoop->aLTerm[j]->eOperator) & (WO_EQ|WO_ISNULL))!=0
&& ((i = pLoop->aLTerm[j]->eOperator) & eqOpMask)!=0
){
if( i & WO_ISNULL ){
testcase( isOrderDistinct );
@@ -6090,8 +6197,8 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
** this candidate as not viable. */
#ifdef WHERETRACE_ENABLED /* 0x4 */
if( sqlite3WhereTrace&0x4 ){
sqlite3DebugPrintf("Skip %s cost=%-3d,%3d order=%c\n",
wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
sqlite3DebugPrintf("Skip %s cost=%-3d,%3d,%3d order=%c\n",
wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted,
isOrdered>=0 ? isOrdered+'0' : '?');
}
#endif
@@ -6109,26 +6216,36 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
pTo = &aTo[jj];
#ifdef WHERETRACE_ENABLED /* 0x4 */
if( sqlite3WhereTrace&0x4 ){
sqlite3DebugPrintf("New %s cost=%-3d,%3d order=%c\n",
wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
sqlite3DebugPrintf("New %s cost=%-3d,%3d,%3d order=%c\n",
wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted,
isOrdered>=0 ? isOrdered+'0' : '?');
}
#endif
}else{
/* Control reaches here if best-so-far path pTo=aTo[jj] covers the
** same set of loops and has the sam isOrdered setting as the
** same set of loops and has the same isOrdered setting as the
** candidate path. Check to see if the candidate should replace
** pTo or if the candidate should be skipped */
if( pTo->rCost<rCost || (pTo->rCost==rCost && pTo->nRow<=nOut) ){
** pTo or if the candidate should be skipped.
**
** The conditional is an expanded vector comparison equivalent to:
** (pTo->rCost,pTo->nRow,pTo->rUnsorted) <= (rCost,nOut,rUnsorted)
*/
if( pTo->rCost<rCost
|| (pTo->rCost==rCost
&& (pTo->nRow<nOut
|| (pTo->nRow==nOut && pTo->rUnsorted<=rUnsorted)
)
)
){
#ifdef WHERETRACE_ENABLED /* 0x4 */
if( sqlite3WhereTrace&0x4 ){
sqlite3DebugPrintf(
"Skip %s cost=%-3d,%3d order=%c",
wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
"Skip %s cost=%-3d,%3d,%3d order=%c",
wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted,
isOrdered>=0 ? isOrdered+'0' : '?');
sqlite3DebugPrintf(" vs %s cost=%-3d,%d order=%c\n",
sqlite3DebugPrintf(" vs %s cost=%-3d,%3d,%3d order=%c\n",
wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow,
pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?');
pTo->rUnsorted, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?');
}
#endif
/* Discard the candidate path from further consideration */
@@ -6141,12 +6258,12 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
#ifdef WHERETRACE_ENABLED /* 0x4 */
if( sqlite3WhereTrace&0x4 ){
sqlite3DebugPrintf(
"Update %s cost=%-3d,%3d order=%c",
wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
"Update %s cost=%-3d,%3d,%3d order=%c",
wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted,
isOrdered>=0 ? isOrdered+'0' : '?');
sqlite3DebugPrintf(" was %s cost=%-3d,%3d order=%c\n",
sqlite3DebugPrintf(" was %s cost=%-3d,%3d,%3d order=%c\n",
wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow,
pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?');
pTo->rUnsorted, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?');
}
#endif
}
@@ -6237,8 +6354,19 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
}
}else{
pWInfo->nOBSat = pFrom->isOrdered;
if( pWInfo->nOBSat<0 ) pWInfo->nOBSat = 0;
pWInfo->revMask = pFrom->revLoop;
if( pWInfo->nOBSat<=0 ){
pWInfo->nOBSat = 0;
if( nLoop>0 && (pFrom->aLoop[nLoop-1]->wsFlags & WHERE_ONEROW)==0 ){
Bitmask m = 0;
int rc = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy, pFrom,
WHERE_ORDERBY_LIMIT, nLoop-1, pFrom->aLoop[nLoop-1], &m);
if( rc==pWInfo->pOrderBy->nExpr ){
pWInfo->bOrderedInnerLoop = 1;
pWInfo->revMask = m;
}
}
}
}
if( (pWInfo->wctrlFlags & WHERE_SORTBYGROUP)
&& pWInfo->nOBSat==pWInfo->pOrderBy->nExpr
@@ -6763,6 +6891,10 @@ WhereInfo *sqlite3WhereBegin(
SQLITE_INT_TO_PTR(n), P4_INT32);
assert( n<=pTab->nCol );
}
#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, pTabItem->iCursor, 0, 0,
(const u8*)&pTabItem->colUsed, P4_INT64);
#endif
}else{
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
}
@@ -6808,6 +6940,21 @@ WhereInfo *sqlite3WhereBegin(
sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ); /* Hint to COMDB2 */
}
VdbeComment((v, "%s", pIx->zName));
#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
{
u64 colUsed = 0;
int ii, jj;
for(ii=0; ii<pIx->nColumn; ii++){
jj = pIx->aiColumn[ii];
if( jj<0 ) continue;
if( jj>63 ) jj = 63;
if( (pTabItem->colUsed & MASKBIT(jj))==0 ) continue;
colUsed |= ((u64)1)<<(ii<63 ? ii : 63);
}
sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, iIndexCur, 0, 0,
(u8*)&colUsed, P4_INT64);
}
#endif /* SQLITE_ENABLE_COLUMN_USED_MASK */
}
}
if( iDb>=0 ) sqlite3CodeVerifySchema(pParse, iDb);
+1
View File
@@ -412,6 +412,7 @@ struct WhereInfo {
u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */
u8 eDistinct; /* One of the WHERE_DISTINCT_* values below */
u8 nLevel; /* Number of nested loop */
u8 bOrderedInnerLoop; /* True if only the inner-most loop is ordered */
int iTop; /* The very beginning of the WHERE loop */
int iContinue; /* Jump here to continue with next record */
int iBreak; /* Jump here to break out of the loop */
+23
View File
@@ -161,4 +161,27 @@ do_execsql_test join5-3.3 {
SELECT * FROM x1 LEFT JOIN x2 JOIN x3 WHERE x3.d = x2.b;
} {}
# Ticket https://www.sqlite.org/src/tktview/c2a19d81652f40568c770c43 on
# 2015-08-20. LEFT JOIN and the push-down optimization.
#
do_execsql_test join6-4.1 {
SELECT *
FROM (
SELECT 'apple' fruit
UNION ALL SELECT 'banana'
) a
JOIN (
SELECT 'apple' fruit
UNION ALL SELECT 'banana'
) b ON a.fruit=b.fruit
LEFT JOIN (
SELECT 1 isyellow
) c ON b.fruit='banana';
} {apple apple {} banana banana 1}
do_execsql_test join6-4.2 {
SELECT *
FROM (SELECT 'apple' fruit UNION ALL SELECT 'banana')
LEFT JOIN (SELECT 1) ON fruit='banana';
} {apple {} banana 1}
finish_test
+45
View File
@@ -949,4 +949,49 @@ do_execsql_test like-12.16 {
} {/SCAN/}
# As of 2017-07-27 (3.21.0) the LIKE optimization works with ESCAPE as
# long as the ESCAPE is a single-byte literal.
#
db close
sqlite3 db :memory:
do_execsql_test like-15.100 {
CREATE TABLE t15(x TEXT COLLATE nocase, y, PRIMARY KEY(x));
INSERT INTO t15(x,y) VALUES
('abcde',1), ('ab%de',2), ('a_cde',3),
('uvwxy',11),('uvwx%',12),('uvwx_',13),
('_bcde',21),('%bcde',22),
('abcd_',31),('abcd%',32),
('ab%xy',41);
SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE '/';
} {2}
do_execsql_test like-15.101 {
EXPLAIN QUERY PLAN
SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE '/';
} {/SEARCH/}
do_execsql_test like-15.102 {
EXPLAIN QUERY PLAN
SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE '//';
} {/SCAN/}
do_execsql_test like-15.103 {
EXPLAIN QUERY PLAN
SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE '';
} {/SCAN/}
do_execsql_test like-15.110 {
SELECT y FROM t15 WHERE x LIKE 'abcdx%%' ESCAPE 'x';
} {32}
do_execsql_test like-15.111 {
SELECT y FROM t15 WHERE x LIKE 'abx%%' ESCAPE 'x' ORDER BY +y
} {2 41}
do_execsql_test like-15.112 {
EXPLAIN QUERY PLAN
SELECT y FROM t15 WHERE x LIKE 'abx%%' ESCAPE 'x' ORDER BY +y
} {/SEARCH/}
do_execsql_test like-15.120 {
SELECT y FROM t15 WHERE x LIKE '/%bc%' ESCAPE '/';
} {22}
do_execsql_test like-15.121 {
EXPLAIN QUERY PLAN
SELECT y FROM t15 WHERE x LIKE '/%bc%' ESCAPE '/';
} {/SEARCH/}
finish_test
+123
View File
@@ -0,0 +1,123 @@
# 2016-05-20
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing the LIMIT in combination with ORDER BY
# and in particular, the optimizations in the inner loop that cause an
# early exit of the inner loop when the LIMIT is reached and the inner
# loop is emitting rows in ORDER BY order.
set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_execsql_test limit2-100 {
CREATE TABLE t1(a,b);
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<1000)
INSERT INTO t1(a,b) SELECT 1, (x*17)%1000 + 1000 FROM c;
INSERT INTO t1(a,b) VALUES(2,2),(3,1006),(4,4),(5,9999);
CREATE INDEX t1ab ON t1(a,b);
}
set sqlite_search_count 0
do_execsql_test limit2-100.1 {
SELECT a, b, '|' FROM t1 WHERE a IN (2,4,5,3,1) ORDER BY b LIMIT 5;
} {2 2 | 4 4 | 1 1000 | 1 1001 | 1 1002 |}
set fast_count $sqlite_search_count
set sqlite_search_count 0
do_execsql_test limit2-100.2 {
SELECT a, b, '|' FROM t1 WHERE a IN (2,4,5,3,1) ORDER BY +b LIMIT 5;
} {2 2 | 4 4 | 1 1000 | 1 1001 | 1 1002 |}
do_test limit2-100.3 {
set slow_count $sqlite_search_count
expr {$fast_count < 0.02*$slow_count}
} {1}
do_execsql_test limit2-110 {
CREATE TABLE t2(x,y);
INSERT INTO t2(x,y) VALUES('a',1),('a',2),('a',3),('a',4);
INSERT INTO t2(x,y) VALUES('b',1),('c',2),('d',3),('e',4);
CREATE INDEX t2xy ON t2(x,y);
}
set sqlite_search_count 0
do_execsql_test limit2-110.1 {
SELECT a, b, '|' FROM t2, t1 WHERE t2.x='a' AND t1.a=t2.y ORDER BY t1.b LIMIT 5;
} {2 2 | 4 4 | 1 1000 | 1 1001 | 1 1002 |}
set fast_count $sqlite_search_count
set sqlite_search_count 0
do_execsql_test limit2-110.2 {
SELECT a, b, '|' FROM t2, t1 WHERE t2.x='a' AND t1.a=t2.y ORDER BY +t1.b LIMIT 5;
} {2 2 | 4 4 | 1 1000 | 1 1001 | 1 1002 |}
set slow_count $sqlite_search_count
do_test limit2-110.3 {
expr {$fast_count < 0.02*$slow_count}
} {1}
do_execsql_test limit2-120 {
DROP INDEX t1ab;
CREATE INDEX t1ab ON t1(a,b DESC);
}
set sqlite_search_count 0
do_execsql_test limit2-120.1 {
SELECT a, b, '|' FROM t1 WHERE a IN (2,4,5,3,1) ORDER BY b DESC LIMIT 5;
} {5 9999 | 1 1999 | 1 1998 | 1 1997 | 1 1996 |}
set fast_count $sqlite_search_count
set sqlite_search_count 0
do_execsql_test limit2-120.2 {
SELECT a, b, '|' FROM t1 WHERE a IN (2,4,5,3,1) ORDER BY +b DESC LIMIT 5;
} {5 9999 | 1 1999 | 1 1998 | 1 1997 | 1 1996 |}
do_test limit2-120.3 {
set slow_count $sqlite_search_count
expr {$fast_count < 0.02*$slow_count}
} {1}
# Bug report against the new ORDER BY LIMIT optimization just prior to
# release. (Unreleased so there is no ticket).
#
# Make sure the optimization is not applied if the inner loop can only
# provide a single row of output.
#
do_execsql_test limit2-200 {
CREATE TABLE t200(a, b);
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<1000)
INSERT INTO t200(a,b) SELECT x, x FROM c;
CREATE TABLE t201(x INTEGER PRIMARY KEY, y);
INSERT INTO t201(x,y) VALUES(2,12345);
SELECT *, '|' FROM t200, t201 WHERE x=b ORDER BY y LIMIT 3;
} {2 2 2 12345 |}
do_execsql_test limit2-210 {
SELECT *, '|' FROM t200 LEFT JOIN t201 ON x=b ORDER BY y LIMIT 3;
} {1 1 {} {} | 3 3 {} {} | 4 4 {} {} |}
# Bug in the ORDER BY LIMIT optimization reported on 2016-09-06.
# Ticket https://www.sqlite.org/src/info/559733b09e96
#
do_execsql_test limit2-300 {
CREATE TABLE t300(a,b,c);
CREATE INDEX t300x ON t300(a,b,c);
INSERT INTO t300 VALUES(0,1,99),(0,1,0),(0,0,0);
SELECT *,'.' FROM t300 WHERE a=0 AND (c=0 OR c=99) ORDER BY c DESC;
} {0 1 99 . 0 0 0 . 0 1 0 .}
do_execsql_test limit2-310 {
SELECT *,'.' FROM t300 WHERE a=0 AND (c=0 OR c=99) ORDER BY c DESC LIMIT 1;
} {0 1 99 .}
# Make sure the SELECT loop is ordered correctly for the direction of
# the ORDER BY
#
do_execsql_test limit2-400 {
CREATE TABLE t400(a,b);
CREATE INDEX t400_ab ON t400(a,b);
INSERT INTO t400(a,b) VALUES(1,90),(1,40),(2,80),(2,30),(3,70),(3,20);
SELECT *,'x' FROM t400 WHERE a IN (1,2,3) ORDER BY b DESC LIMIT 3;
SELECT *,'y' FROM t400 WHERE a IN (1,2,3) ORDER BY +b DESC LIMIT 3;
} {1 90 x 2 80 x 3 70 x 1 90 y 2 80 y 3 70 y}
finish_test
+17
View File
@@ -496,4 +496,21 @@ do_execsql_test 7.0 {
} {~/ORDER BY/}
#---------------------------------------------------------------------------
# https://www.sqlite.org/src/tktview/cb3aa0641d9a413841c004293a4fc06cdc122029
#
# Adverse interaction between scalar subqueries and the partial-sorting
# logic.
#
do_execsql_test 9.0 {
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(x INTEGER PRIMARY KEY);
INSERT INTO t1 VALUES(1),(2);
DROP TABLE IF EXISTS t2;
CREATE TABLE t2(y);
INSERT INTO t2 VALUES(9),(8),(3),(4);
SELECT (SELECT x||y FROM t2, t1 ORDER BY x, y);
} {13}
finish_test
+37
View File
@@ -864,4 +864,41 @@ do_execsql_test select4-14.9 {
SELECT * FROM t14 UNION ALL VALUES(3,2,1),(2,3,1),(1,2,3),(2,1,3);
} {1 2 3 4 5 6 3 2 1 2 3 1 1 2 3 2 1 3}
# Ticket https://www.sqlite.org/src/tktview/f7f8c97e975978d45 on 2016-04-25
#
# The where push-down optimization from 2015-06-02 is suppose to disable
# on aggregate subqueries. But if the subquery is a compound where the
# last SELECT is non-aggregate but some other SELECT is an aggregate, the
# test is incomplete and the optimization is not properly disabled.
#
# The following test cases verify that the fix works.
#
do_execsql_test select4-17.1 {
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(a int, b int);
INSERT INTO t1 VALUES(1,2),(1,18),(2,19);
SELECT x, y FROM (
SELECT 98 AS x, 99 AS y
UNION
SELECT a AS x, sum(b) AS y FROM t1 GROUP BY a
) AS w WHERE y>=20
ORDER BY +x;
} {1 20 98 99}
do_execsql_test select4-17.2 {
SELECT x, y FROM (
SELECT a AS x, sum(b) AS y FROM t1 GROUP BY a
UNION
SELECT 98 AS x, 99 AS y
) AS w WHERE y>=20
ORDER BY +x;
} {1 20 98 99}
do_catchsql_test select4-17.3 {
SELECT x, y FROM (
SELECT a AS x, sum(b) AS y FROM t1 GROUP BY a LIMIT 3
UNION
SELECT 98 AS x, 99 AS y
) AS w WHERE y>=20
ORDER BY +x;
} {1 {LIMIT clause should come after UNION not before}}
finish_test
+72 -1
View File
@@ -156,7 +156,7 @@ do_searchcount_test 3.4.4 {
do_searchcount_test 3.5.1 {
SELECT a, b FROM t3 WHERE (a=1 AND b='one') OR rowid=4
} {1 one 2 two search 2}
} {1 one 2 two search 1}
do_searchcount_test 3.5.2 {
SELECT a, c FROM t3 WHERE (a=1 AND b='one') OR rowid=4
} {1 i 2 ii search 2}
@@ -271,5 +271,76 @@ do_execsql_test 5.3 {
c16=1 or c17=1;
} {1 {} {} {} {} {} {} {} {} {} {} {} {} {} {} 1 {} {}}
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
do_execsql_test 6.1 {
CREATE TABLE x1(a, b, c, d, e);
CREATE INDEX x1a ON x1(a);
CREATE INDEX x1bc ON x1(b, c);
CREATE INDEX x1cd ON x1(c, d);
INSERT INTO x1 VALUES(1, 2, 3, 4, 'A');
INSERT INTO x1 VALUES(5, 6, 7, 8, 'B');
INSERT INTO x1 VALUES(9, 10, 11, 12, 'C');
INSERT INTO x1 VALUES(13, 14, 15, 16, 'D');
}
do_searchcount_test 6.2.1 {
SELECT e FROM x1 WHERE b=2 OR c=7;
} {A B search 6}
do_searchcount_test 6.2.2 {
SELECT c FROM x1 WHERE b=2 OR c=7;
} {3 7 search 4}
do_searchcount_test 6.3.1 {
SELECT e FROM x1 WHERE a=1 OR b=10;
} {A C search 6}
do_searchcount_test 6.3.2 {
SELECT c FROM x1 WHERE a=1 OR b=10;
} {3 11 search 5}
do_searchcount_test 6.3.3 {
SELECT rowid FROM x1 WHERE a=1 OR b=10;
} {1 3 search 4}
do_searchcount_test 6.4.1 {
SELECT a FROM x1 WHERE b BETWEEN 1 AND 4 OR c BETWEEN 8 AND 12
} {1 9 search 6}
do_searchcount_test 6.4.2 {
SELECT b, c FROM x1 WHERE b BETWEEN 1 AND 4 OR c BETWEEN 8 AND 12
} {2 3 10 11 search 5}
do_searchcount_test 6.4.3 {
SELECT rowid, c FROM x1 WHERE b BETWEEN 1 AND 4 OR c BETWEEN 8 AND 12
} {1 3 3 11 search 4}
db eval {
WITH RECURSIVE c(x) AS (VALUES(1000) UNION ALL SELECT x+1 FROM c WHERE x<2000)
INSERT INTO x1(rowid,a,b,c,d,e) SELECT x,x,x,x,x,x FROM c;
}
do_searchcount_test 6.5.1 {
SELECT a FROM x1 WHERE rowid = 2 OR c=11
} {5 9 search 3}
do_searchcount_test 6.5.2 {
SELECT d FROM x1 WHERE rowid = 2 OR c=11
} {8 12 search 2}
do_searchcount_test 6.5.3 {
SELECT d FROM x1 WHERE c=11 OR rowid = 2
} {12 8 search 2}
do_searchcount_test 6.5.4 {
SELECT a FROM x1 WHERE c=11 OR rowid = 2
} {9 5 search 3}
do_searchcount_test 6.6.1 {
SELECT rowid FROM x1 WHERE a=1 OR b=6 OR c=11
} {1 2 3 search 6}
do_searchcount_test 6.6.2 {
SELECT c FROM x1 WHERE a=1 OR b=6 OR c=11
} {3 7 11 search 7}
do_searchcount_test 6.6.3 {
SELECT c FROM x1 WHERE c=11 OR a=1 OR b=6
} {11 3 7 search 7}
do_searchcount_test 6.6.4 {
SELECT c FROM x1 WHERE b=6 OR c=11 OR a=1
} {7 11 3 search 7}
finish_test
+82
View File
@@ -0,0 +1,82 @@
# 2008 October 6
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing fault-injection with the
# LIMIT ... OFFSET ... clause of UPDATE and DELETE statements.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
set testprefix wherelfault
ifcapable !update_delete_limit {
finish_test
return
}
do_execsql_test 1.0 {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 'f');
INSERT INTO t1 VALUES(2, 'e');
INSERT INTO t1 VALUES(3, 'd');
INSERT INTO t1 VALUES(4, 'c');
INSERT INTO t1 VALUES(5, 'b');
INSERT INTO t1 VALUES(6, 'a');
CREATE VIEW v1 AS SELECT a,b FROM t1;
CREATE TABLE log(op, a);
CREATE TRIGGER v1del INSTEAD OF DELETE ON v1 BEGIN
INSERT INTO log VALUES('delete', old.a);
END;
CREATE TRIGGER v1upd INSTEAD OF UPDATE ON v1 BEGIN
INSERT INTO log VALUES('update', old.a);
END;
}
faultsim_save_and_close
do_faultsim_test 1.1 -prep {
faultsim_restore_and_reopen
db eval {SELECT * FROM sqlite_master}
} -body {
execsql { DELETE FROM v1 ORDER BY a LIMIT 3; }
} -test {
faultsim_test_result {0 {}}
}
do_faultsim_test 1.2 -prep {
faultsim_restore_and_reopen
db eval {SELECT * FROM sqlite_master}
} -body {
execsql { UPDATE v1 SET b = 555 ORDER BY a LIMIT 3 }
} -test {
faultsim_test_result {0 {}}
}
#-------------------------------------------------------------------------
sqlite3 db test.db
do_execsql_test 2.1.0 {
CREATE TABLE t2(a, b, c, PRIMARY KEY(a)) WITHOUT ROWID;
}
faultsim_save_and_close
do_faultsim_test 2.1 -prep {
faultsim_restore_and_reopen
db eval {SELECT * FROM sqlite_master}
} -body {
execsql { DELETE FROM t2 WHERE c=? ORDER BY a DESC LIMIT 10 }
} -test {
faultsim_test_result {0 {}}
}
finish_test
+41
View File
@@ -38,6 +38,8 @@ proc create_test_data {size} {
ifcapable {update_delete_limit} {
execsql { CREATE TABLE t1(x, y) }
# check syntax error support
do_test wherelimit-0.1 {
catchsql {DELETE FROM t1 ORDER BY x}
@@ -49,6 +51,8 @@ ifcapable {update_delete_limit} {
catchsql {UPDATE t1 SET y=1 WHERE x=1 ORDER BY x}
} {1 {ORDER BY without LIMIT on UPDATE}}
execsql { DROP TABLE t1 }
# no AS on table sources
do_test wherelimit-0.4 {
catchsql {DELETE FROM t1 AS a WHERE x=1}
@@ -279,6 +283,43 @@ ifcapable {update_delete_limit} {
execsql {SELECT count(*) FROM t1 WHERE y=1}
} {6}
# Cannot use a LIMIT for UPDATE or DELETE against a WITHOUT ROWID table
# or a VIEW. (We should fix this someday).
#
db close
sqlite3 db :memory:
do_execsql_test wherelimit-4.1 {
CREATE TABLE t1(a int);
INSERT INTO t1 VALUES(1);
INSERT INTO t1 VALUES(2);
INSERT INTO t1 VALUES(3);
CREATE TABLE t2(a int);
INSERT INTO t2 SELECT a+100 FROM t1;
CREATE VIEW tv AS
SELECT rowid AS r, a FROM t2 UNION ALL SELECT rowid, a FROM t1;
CREATE TRIGGER tv_del INSTEAD OF DELETE ON tv
BEGIN
DELETE FROM t1 WHERE rowid=old.r;
DELETE FROM t2 WHERE rowid=old.r;
END;
} {}
do_catchsql_test wherelimit-4.2 {
DELETE FROM tv WHERE 1 LIMIT 2;
} {0 {}}
do_catchsql_test wherelimit-4.3 {
DELETE FROM tv WHERE 1 ORDER BY a LIMIT 2;
} {0 {}}
do_execsql_test wherelimit-4.10 {
CREATE TABLE t3(a,b,c,d TEXT, PRIMARY KEY(a)) WITHOUT ROWID;
INSERT INTO t3(a,b,c,d) VALUES(1,2,3,4),(5,6,7,8),(9,10,11,12);
} {}
do_catchsql_test wherelimit-4.11 {
DELETE FROM t3 WHERE a=5 LIMIT 2;
} {0 {}}
do_execsql_test wherelimit-4.12 {
SELECT a,b,c,d FROM t3 ORDER BY 1;
} {1 2 3 4 9 10 11 12}
}
finish_test
+264
View File
@@ -0,0 +1,264 @@
# 2008 October 6
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing the LIMIT ... OFFSET ... clause
# of UPDATE and DELETE statements.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix wherelimit2
ifcapable !update_delete_limit {
finish_test
return
}
#-------------------------------------------------------------------------
# Test with views and INSTEAD OF triggers.
#
do_execsql_test 1.0 {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 'f');
INSERT INTO t1 VALUES(2, 'e');
INSERT INTO t1 VALUES(3, 'd');
INSERT INTO t1 VALUES(4, 'c');
INSERT INTO t1 VALUES(5, 'b');
INSERT INTO t1 VALUES(6, 'a');
CREATE VIEW v1 AS SELECT a,b FROM t1;
CREATE TABLE log(op, a);
CREATE TRIGGER v1del INSTEAD OF DELETE ON v1 BEGIN
INSERT INTO log VALUES('delete', old.a);
END;
CREATE TRIGGER v1upd INSTEAD OF UPDATE ON v1 BEGIN
INSERT INTO log VALUES('update', old.a);
END;
}
do_execsql_test 1.1 {
DELETE FROM v1 ORDER BY a LIMIT 3;
SELECT * FROM log; DELETE FROM log;
} {
delete 1 delete 2 delete 3
}
do_execsql_test 1.2 {
DELETE FROM v1 ORDER BY b LIMIT 3;
SELECT * FROM log; DELETE FROM log;
} {
delete 6 delete 5 delete 4
}
do_execsql_test 1.3 {
UPDATE v1 SET b = 555 ORDER BY a LIMIT 3;
SELECT * FROM log; DELETE FROM log;
} {
update 1 update 2 update 3
}
do_execsql_test 1.4 {
UPDATE v1 SET b = 555 ORDER BY b LIMIT 3;
SELECT * FROM log; DELETE FROM log;
} {
update 6 update 5 update 4
}
#-------------------------------------------------------------------------
# Simple test using WITHOUT ROWID table.
#
do_execsql_test 2.2.0 {
CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c) WITHOUT ROWID;
INSERT INTO t2 VALUES(1, 1, 'h');
INSERT INTO t2 VALUES(2, 2, 'g');
INSERT INTO t2 VALUES(3, 1, 'f');
INSERT INTO t2 VALUES(4, 2, 'e');
INSERT INTO t2 VALUES(5, 1, 'd');
INSERT INTO t2 VALUES(6, 2, 'c');
INSERT INTO t2 VALUES(7, 1, 'b');
INSERT INTO t2 VALUES(8, 2, 'a');
}
do_execsql_test 2.2.1 {
BEGIN;
DELETE FROM t2 WHERE b=1 ORDER BY c LIMIT 2;
SELECT c FROM t2 ORDER BY 1;
ROLLBACK;
} {a c e f g h}
do_execsql_test 2.2.2 {
BEGIN;
UPDATE t2 SET c=NULL ORDER BY a DESC LIMIT 3 OFFSET 1;
SELECT a, b, c FROM t2;
ROLLBACK;
} {
1 1 h
2 2 g
3 1 f
4 2 e
5 1 {}
6 2 {}
7 1 {}
8 2 a
}
#-------------------------------------------------------------------------
# Test using a virtual table
#
ifcapable fts3 {
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE ft USING fts3(x);
INSERT INTO ft(rowid, x) VALUES(-45, 'a a');
INSERT INTO ft(rowid, x) VALUES(12, 'a b');
INSERT INTO ft(rowid, x) VALUES(444, 'a c');
INSERT INTO ft(rowid, x) VALUES(12300, 'a d');
INSERT INTO ft(rowid, x) VALUES(25400, 'a c');
INSERT INTO ft(rowid, x) VALUES(25401, 'a b');
INSERT INTO ft(rowid, x) VALUES(50000, 'a a');
}
do_execsql_test 3.1.1 {
BEGIN;
DELETE FROM ft ORDER BY rowid LIMIT 3;
SELECT x FROM ft;
ROLLBACK;
} {{a d} {a c} {a b} {a a}}
do_execsql_test 3.1.2 {
BEGIN;
DELETE FROM ft WHERE ft MATCH 'a' ORDER BY rowid LIMIT 3;
SELECT x FROM ft;
ROLLBACK;
} {{a d} {a c} {a b} {a a}}
do_execsql_test 3.1.3 {
BEGIN;
DELETE FROM ft WHERE ft MATCH 'b' ORDER BY rowid ASC LIMIT 1 OFFSET 1;
SELECT rowid FROM ft;
ROLLBACK;
} {-45 12 444 12300 25400 50000}
do_execsql_test 3.2.1 {
BEGIN;
UPDATE ft SET x='hello' ORDER BY rowid LIMIT 2 OFFSET 2;
SELECT x FROM ft;
ROLLBACK;
} {{a a} {a b} hello hello {a c} {a b} {a a}}
do_execsql_test 3.2.2 {
BEGIN;
UPDATE ft SET x='hello' WHERE ft MATCH 'a'
ORDER BY rowid DESC LIMIT 2 OFFSET 2;
SELECT x FROM ft;
ROLLBACK;
} {{a a} {a b} {a c} hello hello {a b} {a a}}
} ;# fts5
#-------------------------------------------------------------------------
# Test using INDEXED BY clauses.
#
do_execsql_test 4.0 {
CREATE TABLE x1(a INTEGER PRIMARY KEY, b, c, d);
CREATE INDEX x1bc ON x1(b, c);
INSERT INTO x1 VALUES(1,1,1,1);
INSERT INTO x1 VALUES(2,1,2,2);
INSERT INTO x1 VALUES(3,2,1,3);
INSERT INTO x1 VALUES(4,2,2,3);
INSERT INTO x1 VALUES(5,3,1,2);
INSERT INTO x1 VALUES(6,3,2,1);
}
do_execsql_test 4.1 {
BEGIN;
DELETE FROM x1 ORDER BY a LIMIT 2;
SELECT a FROM x1;
ROLLBACK;
} {3 4 5 6}
do_catchsql_test 4.2 {
DELETE FROM x1 INDEXED BY x1bc WHERE d=3 LIMIT 1;
} {1 {no query solution}}
do_execsql_test 4.3 {
DELETE FROM x1 INDEXED BY x1bc WHERE b=3 LIMIT 1;
SELECT a FROM x1;
} {1 2 3 4 6}
do_catchsql_test 4.4 {
UPDATE x1 INDEXED BY x1bc SET d=5 WHERE d=3 LIMIT 1;
} {1 {no query solution}}
do_execsql_test 4.5 {
UPDATE x1 INDEXED BY x1bc SET d=5 WHERE b=2 LIMIT 1;
SELECT a, d FROM x1;
} {1 1 2 2 3 5 4 3 6 1}
#-------------------------------------------------------------------------
# Test using object names that require quoting.
#
do_execsql_test 5.0 {
CREATE TABLE "x y"("a b" PRIMARY KEY, "c d") WITHOUT ROWID;
CREATE INDEX xycd ON "x y"("c d");
INSERT INTO "x y" VALUES('a', 'a');
INSERT INTO "x y" VALUES('b', 'b');
INSERT INTO "x y" VALUES('c', 'c');
INSERT INTO "x y" VALUES('d', 'd');
INSERT INTO "x y" VALUES('e', 'a');
INSERT INTO "x y" VALUES('f', 'b');
INSERT INTO "x y" VALUES('g', 'c');
INSERT INTO "x y" VALUES('h', 'd');
}
do_execsql_test 5.1 {
BEGIN;
DELETE FROM "x y" WHERE "c d"!='e' ORDER BY "c d" LIMIT 2 OFFSET 2;
SELECT * FROM "x y" ORDER BY 1;
ROLLBACK;
} {
a a c c d d e a g c h d
}
do_execsql_test 5.2 {
BEGIN;
UPDATE "x y" SET "c d"='e' WHERE "c d"!='e' ORDER BY "c d" LIMIT 2 OFFSET 2;
SELECT * FROM "x y" ORDER BY 1;
ROLLBACK;
} {
a a b e c c d d e a f e g c h d
}
proc log {args} { lappend ::log {*}$args }
db func log log
do_execsql_test 5.3 {
CREATE VIEW "v w" AS SELECT * FROM "x y";
CREATE TRIGGER tr1 INSTEAD OF DELETE ON "v w" BEGIN
SELECT log(old."a b", old."c d");
END;
CREATE TRIGGER tr2 INSTEAD OF UPDATE ON "v w" BEGIN
SELECT log(new."a b", new."c d");
END;
}
do_test 5.4 {
set ::log {}
execsql { DELETE FROM "v w" ORDER BY "a b" LIMIT 3 }
set ::log
} {a a b b c c}
do_test 5.5 {
set ::log {}
execsql { UPDATE "v w" SET "a b" = "a b" || 'x' ORDER BY "a b" LIMIT 5; }
set ::log
} {ax a bx b cx c dx d ex a}
finish_test