Compare commits

...

97 Commits

Author SHA1 Message Date
drh b29e60c448 Catch vector size mismatch problems during name resolution to avoid later
problems.

FossilOrigin-Name: 56562a0346170cf7b72445976864b058437a8ac3
2016-09-05 12:02:34 +00:00
dan 80aa545337 Fix a problem causing the affinity of sub-select row-value elements to be ignored in some contextes.
FossilOrigin-Name: 7d9bd22c0715ede2592ee1fa7ebc215aded1ca1b
2016-09-03 19:52:12 +00:00
drh 8f9d1ad8d5 Merge the fuzzershell enhancement from trunk.
FossilOrigin-Name: ed206048484667e4fe6aaeadd65882537d74bd35
2016-09-03 16:24:43 +00:00
dan 2718960316 Consider the affinity of "b" when using an "a IN (SELECT b ...)" expression with an index on "a". Fix for [199df416].
FossilOrigin-Name: f5e49855412e389a8a410db5d7ffb2e3634c5fa3
2016-09-03 15:31:20 +00:00
drh 4910a76d45 Performance optimizations.
FossilOrigin-Name: f1d06c49ba0d814dc7ffb538aac3f4e6251fd8f0
2016-09-03 01:46:15 +00:00
drh 55a6f6dc72 Merge recent changes from trunk.
FossilOrigin-Name: c7271fbde1aebb15daaedb7f1fa75fe410fd46f6
2016-09-02 23:56:32 +00:00
drh 888e1faf95 Merge updates from trunk.
FossilOrigin-Name: 082fd5f8ac227dbb983da0a772485268af40a484
2016-08-27 14:13:58 +00:00
drh 88e665fd0b Fix the "Synopsis" on the OP_Lt, OP_Le, OP_Gt, and OP_Ge opcodes, which
has been backwards for time out of mind.

FossilOrigin-Name: f51248dcfa22e989dacde41021e96a65f12c5ca8
2016-08-27 01:41:53 +00:00
drh 106526e1bc Fix a minor problem in sqlite3FindInIndex() related to rowids being used
as part of the index.

FossilOrigin-Name: 829f802be7d4647dd815b739bbc9e1d1ac6a224d
2016-08-26 22:09:01 +00:00
drh a84a283d18 Fix the sqlite3FindInIndex() to ensure that it always uses a prefix of
the index and uses no repeated columns.  Enhanced comments.

FossilOrigin-Name: b9fc89e432fbe4e5b41959a42797641907e075e3
2016-08-26 21:15:35 +00:00
drh 363fb95bc3 Add an EXPLAIN QUERY PLAN line for when a index is used to implement
an IN operator.

FossilOrigin-Name: 171aa833a2e1650c3d9cf9bd6438ae46f6c35871
2016-08-26 19:54:12 +00:00
dan 4b4f51148a Add test cases to rowvalue2.test.
FossilOrigin-Name: 078bb69e99891ba3b76a39ac974990714c43908f
2016-08-26 19:47:30 +00:00
drh 6fc8f36490 Enhance sqlite3FindInIndex() so that it is able to make use of the
primary keys at the end of an index.

FossilOrigin-Name: 4b589fbfcc4265902de0f552961d2df497a184da
2016-08-26 19:31:29 +00:00
drh 0c36fca004 Fix a post-OOM crash in updateRangeAffinityStr(). Add several ALWAYS()
macros on unreachable branches.

FossilOrigin-Name: 87d40195ae5cc2abd9bae45073a615db81263285
2016-08-26 18:17:08 +00:00
dan b7ca2177d7 Fix a problem with affinity changes and vector range comparisons.
FossilOrigin-Name: b34413ac7e34369b4420e57b0132249dca68a7b0
2016-08-26 17:54:46 +00:00
drh 0dfa4f6fcc Allow ROWID values in indexed vector comparisons.
FossilOrigin-Name: b0cc6be4eb81f21b11796e1f14d4412bf21dea6e
2016-08-26 13:19:49 +00:00
drh 64bcb8cfbb Comment improvements. Put ALWAYS and NEVER macros on three unreachable
branches.

FossilOrigin-Name: 397617009e07004596476d6f5644fdf84c376f54
2016-08-26 03:42:57 +00:00
drh a28f85b076 Remove an unreachable branch from sqlite3ExprAffinity()
FossilOrigin-Name: 9d96f61481704e5ec399ee425f0ebb246902ecc5
2016-08-26 01:02:09 +00:00
drh 3cc19132da Merge recent changes from trunk.
FossilOrigin-Name: 5789aab8ef59ae1cdfdae123b078ee03da6e08ed
2016-08-25 22:31:44 +00:00
drh e347d3e813 Refactor the sqlite3ExprCodeIN() routine for improved maintainability.
FossilOrigin-Name: b56705ae6374db9db82613ef89faa1a1e6b00a18
2016-08-25 21:14:34 +00:00
drh 0a1082aec2 Another fix in the IN-operator algorithm description.
FossilOrigin-Name: f474aeac4fa62d87e73189868d7c7a295ffb7265
2016-08-25 17:47:36 +00:00
drh 1373c3a8c5 Further refinement of the in-operator.md documentation.
FossilOrigin-Name: df0648373a50006ca18d692e12552d1d53d549e3
2016-08-25 17:40:32 +00:00
drh ecb87ac88d Improvements to IN operator code generator comments. Avoid unnecessary
Copy operations on the LHS of the IN operator.

FossilOrigin-Name: b6344298783a1207cba3f635939ddc9ba922ab67
2016-08-25 15:46:25 +00:00
drh ee0f22fd3b Corrections to the IN-operator notes.
FossilOrigin-Name: 25033ee94538289ba7e0147da30a18300047123f
2016-08-25 14:23:59 +00:00
drh 539f2fef03 Add notes on the implementation of the IN operator.
FossilOrigin-Name: d256b2caeb9e3eb5dd88bb569ec71f91e9991c81
2016-08-25 14:00:15 +00:00
drh 18016ad2bb Simplified VDBE code for the vector NOT IN null-scanning loop.
FossilOrigin-Name: 7ae504e62e9bbbbd85a676f3c3922b7fd0cc73d2
2016-08-24 21:54:47 +00:00
drh 72e26dec86 Improved extended comments of comparison operators when the SQLITE_STOREP2
flags is set on P5.  No changes to non-debug code.

FossilOrigin-Name: bbc1b016164ed0793e07302614384d52119463e0
2016-08-24 21:24:04 +00:00
drh 62659b2a80 In sqlite3FindInIndex(), improve internal comments and avoid an
unreachable branch.

FossilOrigin-Name: 55945fc12f8157e32e6850e41575c0c6422d29e7
2016-08-24 18:51:23 +00:00
drh 321e828d03 Fix more unreachable branches.
FossilOrigin-Name: 6099c180db55396d6307538a5428ae5ef1b82d10
2016-08-24 17:49:07 +00:00
drh d0b67a8654 Add a NEVER() on an unreachable branch in comparisonAffinity().
FossilOrigin-Name: 505a2f20eac62d4e170f003255c8984e4f3b0918
2016-08-24 15:37:31 +00:00
dan 7887d7f24d Fix a buffer overrun in the code for handling IN(...) operators when the LHS of the operator contains indexed columns or expressions.
FossilOrigin-Name: f41a0391b732a8c4ad188163f34a0f4a22237bb5
2016-08-24 12:22:17 +00:00
drh ac6b47d164 The previous OOM fix was bad. Back it out and replace it with a better one.
FossilOrigin-Name: 1e3bc3698a4b779e6af8e3c727929c4dbddf3edb
2016-08-24 00:51:48 +00:00
drh 894241c847 Avoid a potential null-pointer dereference following an OOM.
FossilOrigin-Name: 25f6ed8de4df9c9890d4a352a6d11084433e82ea
2016-08-24 00:25:27 +00:00
drh e835bc1285 Simplify the row value misuse error message.
FossilOrigin-Name: 838c50a5bf46fd0340839d577fa28ba02b4f2034
2016-08-23 19:02:55 +00:00
drh 66860af3dd Fix a problem with rowvalue UPDATE when the rowvalue is not the left-most and
the RHS is a multi-column subquery.

FossilOrigin-Name: e149e6b93a9afb3d574309c0db60e221e24078f7
2016-08-23 18:30:10 +00:00
drh abb9d5f189 Fix an uninitialized variable in CASE expression code generation.
FossilOrigin-Name: c8ffae05e13033ec7425bf0f002df31f550bb7f1
2016-08-23 17:30:55 +00:00
drh 12abf408ff Fix the vector BETWEEN operator so that it only evaluates the left-most
vector expression once.  Add support for vector comparisons in the CASE
operator.

FossilOrigin-Name: 07e69f43a294d35b5145a2b0242ee42d50adab14
2016-08-22 14:30:05 +00:00
drh db45bd5e82 Reinstate the mechanism in BETWEEN that avoids evaluating the first expression
more than once, but fix the affinity extractor so that it works with this
mechanism.  The de-duplication of the first expression still does not work
for vector expressions, though.

FossilOrigin-Name: 2f39987f21bd6dae8d2be610a1fd5f06f8878e9e
2016-08-22 00:48:58 +00:00
drh 84b19a3da1 The docs promise the in "x BETWEEN y AND z" the x expression is only evaluated
once.  That is no longer true, and so some tests are failing.  This needs to
be fixed before merging to trunk.

FossilOrigin-Name: e50d264fdc2f08d19202c68f73f18df301cb233d
2016-08-20 22:49:28 +00:00
drh d832da7f40 Further comment enhancements. No changes to code.
FossilOrigin-Name: d4562a9e7b1eaff41466210e3a0caaf374ec5a92
2016-08-20 21:11:25 +00:00
drh 76dbe7a8d0 Clarification of code comments in expr.c. Clean up the implementations
of sqlite3ExprIsVector() and sqlite3ExprVectorSize() slightly.

FossilOrigin-Name: 4fb66d6592b141a4a71359250dbd1ac454569cb9
2016-08-20 21:02:38 +00:00
drh 8bd0d58e1c Fixes for problems following OOM errors.
FossilOrigin-Name: 9041ee4a6f0e8389297f887f1431ab5cfe783390
2016-08-20 18:06:14 +00:00
drh 9854260bca Do not duplicate the Expr.pLeft subtree of a TK_SELECT_COLUMN node.
FossilOrigin-Name: 8384c77ebb3f65fbc54c199885926f2066f0b140
2016-08-20 17:00:16 +00:00
dan f358009a93 Fix a segfault that could occur if a query that used a vector comparison contained certain types of syntax errors.
FossilOrigin-Name: 203f07c5e140e74cf91d0c1e20135c21236f0fc1
2016-08-20 15:01:24 +00:00
drh 9e730f0bb8 Improvements to the vector comparison splitter in exprAnalyze().
FossilOrigin-Name: a3ffd283bc931b04170ef737e56bced33d27f06d
2016-08-20 12:00:05 +00:00
drh 8762ec1935 Improvements to comments. No code changes.
FossilOrigin-Name: 4165d20f64d778a4e48f511a37cfced8579d4dbe
2016-08-20 01:06:22 +00:00
drh a1251bc40d Add support for vector assignments in the SET clause of an UPDATE statement.
FossilOrigin-Name: f320d47d6b7b08d9552c8444004bc256348bda90
2016-08-20 00:51:37 +00:00
drh fc7f27b9d2 Change the way TK_SELECT_COLUMN is handled so that the subquery is only
generated once even if part of the vector comparison is used for indexing
and the other part is now.  This change also is a pathway to vector assignment
in UPDATE statements.

FossilOrigin-Name: d8feea7dcde83179bff303072426561cfe825e58
2016-08-20 00:07:01 +00:00
drh 8d25cb90cd Replace the magic number (-2) with its symbol XN_EXPR in the
exprMightBeIndexed() routine.  No logic changes.

FossilOrigin-Name: d4a5af69cc3bde6f8c02075c07b4d47a0252392d
2016-08-19 19:58:06 +00:00
drh 39a11819f3 Improved comments on sqlite3CodeSubquery(). No changes to code.
FossilOrigin-Name: acea4ee136def4815d22eec240c5903a72bde9bd
2016-08-19 19:12:58 +00:00
drh a15a8bc822 Show the WhereTerm.iField value on debugging output, when it is non-zero.
FossilOrigin-Name: 931c95358d8bc02f3e3d8ee4e545a07b2d2c97e8
2016-08-19 18:40:17 +00:00
drh 4602b8e89d Fix an obsolete comment on codeEqualityTerm(). No changes to code.
FossilOrigin-Name: d07be5afb0a915769382dfd815403f8832cb3eec
2016-08-19 18:28:00 +00:00
drh b684fd5c4a Merge recent enhancements from trunk.
FossilOrigin-Name: b17872363b60edab05a5d382a44038aad91e4d9f
2016-08-19 15:41:24 +00:00
drh 03181c8c27 Fix a SQL NULL handling bug in the vector IN operator code generation.
FossilOrigin-Name: 936146b12e27784f15a68fe65732c6d92c3a12f3
2016-08-18 19:04:57 +00:00
drh 48cb3a7600 Display SELECT_COLUMN expressions in the .wheretrace debugging output.
FossilOrigin-Name: 3b27a5da100037f75a4efc15e0354a6aa94194f8
2016-08-18 18:09:10 +00:00
drh db97e56217 Display VECTOR expressions in .wheretrace and .selecttrace debugging output.
FossilOrigin-Name: 157347e2580e5078c4081d602e9d1a82d194e719
2016-08-18 17:55:57 +00:00
drh c8d0b0b9d4 Since the TK_VECTOR token is not actually used by the parser, make it an
extra token code added by the tool/addopcodes.tcl script during parser build.

FossilOrigin-Name: b40b202c1f82808d2240e87b22f1e78b2208cb26
2016-08-18 15:36:03 +00:00
drh 5ea494425f Merge recent enhancements from trunk.
FossilOrigin-Name: 4768a1066cb9c7627064d7efec44188d6755cb03
2016-08-18 15:21:16 +00:00
drh a79e10b665 Simplify the vector comparison code generator logic, and the resulting
VDBE code.

FossilOrigin-Name: e2ad0b5d8e1e83118c12889150aca2f2a6b2bdde
2016-08-18 15:15:29 +00:00
drh 0f825a7e26 Remove an unnecessary stack variable from sqlite3VdbeExec().
FossilOrigin-Name: c54bd9c82dd34951dc87848c0b19fcccaef928db
2016-08-13 14:17:02 +00:00
drh af69eda63e Improvements to commits. No code changes.
FossilOrigin-Name: 18f5a3bee4f870be4644a6042a20081c46edb7d0
2016-08-13 13:03:46 +00:00
drh a2f629251f Fix to the vector less-than operator. All legacy tests passing now.
FossilOrigin-Name: ec70a67ebc997f457be4d52d8affc37e142dc3ff
2016-08-13 12:37:47 +00:00
drh 79752b6e63 Attempt to simplify the logic and generated code for vector comparisons.
Basic comparison operators are working, but there are many indexing test
failures still to be worked through.

FossilOrigin-Name: dfc028cfbe7657d20727a2670ecadb1575eb8cbb
2016-08-13 10:02:17 +00:00
drh 471b4b92bd Add VdbeCoverage() macros on newly added VDBE branch operations.
FossilOrigin-Name: 381aa73141db8ec59adbcb09e71af660ee4ae5ce
2016-08-12 11:25:49 +00:00
drh a48d7e7749 Fix a post-OOM memory leak.
FossilOrigin-Name: 14009b32b955b42cfd5f0c2ce7d4b3ce19ce201e
2016-08-12 11:01:20 +00:00
drh 088645b090 Merge recent changes from trunk.
FossilOrigin-Name: 959677b97ba15f786936c762e2e68df1151d7ed0
2016-08-11 12:31:21 +00:00
dan 17994e3bca Fix some problems with handling "no such collation sequence" errors.
FossilOrigin-Name: 8278be06fa69e3266866220bdaf5be45a5fcfa23
2016-08-11 12:01:52 +00:00
dan 9cd4933ec1 Add rowvalue5.test, which should have been part of the previous commit on this branch.
FossilOrigin-Name: ea03e219ced87777f0c3c6bbb0274078dbb829e2
2016-08-09 05:48:40 +00:00
dan 6256c1c242 Fix some cases involving row values and virtual tables.
FossilOrigin-Name: 156a41f30a0afd9a70e6c26470dcc468a11bd402
2016-08-08 20:15:41 +00:00
dan 1d9bc9b7a0 Fix the EXPLAIN QUERY PLAN output for row value range constaints that use an index.
FossilOrigin-Name: bb60651163553c5e46bf7b2805490570cea647b8
2016-08-08 18:42:08 +00:00
dan adeb970da2 Merge trunk changes with this branch.
FossilOrigin-Name: 0e927a7e0250a65fd8e97b322cd69e93fadd13f0
2016-08-08 16:52:11 +00:00
dan 3d1fb1dd75 Fix a problem with estimating the number of rows visited by a query that uses a multi-column IN(SELECT...) constraint.
FossilOrigin-Name: 3c2f908f5b7312570cfa74afcf4252a857cb5237
2016-08-03 18:00:49 +00:00
dan 2c628ea9d9 Fix another problem involving vector range constraints and mixed ASC/DESC indexes.
FossilOrigin-Name: 1559f4c43473e107f7196eea3ee91c53ede22999
2016-08-03 16:39:04 +00:00
dan d66e5794d1 Fix stat4-based cost estimates for vector range constraints.
FossilOrigin-Name: 18af74abc8ceae47ab9fbee3e3e5bb37db8fcba5
2016-08-03 16:14:33 +00:00
dan 78f9bb6c71 Add new test file rowvaluefault.test.
FossilOrigin-Name: e496b2d63984311e6ae117677e6c2417ae24b6bc
2016-08-02 20:45:56 +00:00
dan 51d82d1d24 Add tests and fixes for vector operations that use sub-queries with different combinations of LIMIT, OFFSET and ORDER BY clauses.
FossilOrigin-Name: 092b1c5ff53c9f3cfed079c46e3353d93f99303e
2016-08-02 18:50:15 +00:00
dan f9b2e05c75 Fix SQLITE_OMIT_SUBQUERY builds.
FossilOrigin-Name: 339f85f414a484e44d2502d1ff7281caf9b7c838
2016-08-02 17:45:00 +00:00
dan d05a7144cd Fix a problem with vector range constraints and mixed ASC/DESC indexes.
FossilOrigin-Name: e2ad30c8b5366fd8e50f36c62345ed03ec613c47
2016-08-02 17:07:51 +00:00
dan 080508a186 Merge latest trunk changes into this branch.
FossilOrigin-Name: d468101b421e073e9debd7381bde1d36af31369e
2016-08-02 16:24:10 +00:00
dan 95a08c065a Add missing comments and make some code on this branch clearer.
FossilOrigin-Name: 6937677cc2c2db6b21f997559f88a339466cd15b
2016-08-02 16:18:35 +00:00
dan 553168c706 Fix a problem with vector range constraints involving the rowid column. And other issues.
FossilOrigin-Name: 3ef75d45ebcd8ede91596d69e55fe7d685008a60
2016-08-01 20:14:31 +00:00
dan 870a0705fe Fix a problem with IN(...) constraints where the LHS is a sub-select that is an aggregate query.
FossilOrigin-Name: 1f4dba87da4a44ad26223ad965731164c0d9bad9
2016-08-01 16:37:43 +00:00
dan 5c288b929a Fix problems with vector == comparisons and NULL values.
FossilOrigin-Name: 059d0d05354e6efab7892c97b339ffa0b5303587
2016-07-30 21:02:33 +00:00
dan cb17ce9300 Merge latest trunk with this branch.
FossilOrigin-Name: 63ae02d084a332250ff6fd8d8c80e53bf5422a68
2016-07-30 17:59:39 +00:00
dan 625015e0c9 Remove the EP_Vector expression flag.
FossilOrigin-Name: e9d9c6d46b46160fad6aa6e3441a65a09157515f
2016-07-30 16:39:28 +00:00
dan 19ff12dd76 Fix some issues with vector range constraints and the column cache. Also vector range constraints and rowid columns.
FossilOrigin-Name: 42607366bfc2dceb1013797a973b3b8df75dcb4d
2016-07-29 20:58:19 +00:00
dan 145b4ea519 Change the way "(a, b) = (SELECT *)" expressions are handled in where.c if there is an index on one of the columns only.
FossilOrigin-Name: 4dfebff2924f46284d5b9cda69175f79b29d6028
2016-07-29 18:12:12 +00:00
dan 7b35a77b1a Fix further issues with multi-column IN(...) operators. Also some error handling cases surrounding row values.
FossilOrigin-Name: cc3f6542bec99b00d2698889bcea2aa0b587efa0
2016-07-28 19:47:15 +00:00
dan 34c125747d Merge latest trunk changes into this branch.
FossilOrigin-Name: 9685880f7baeb670739fdcf2d9df08e22abaa699
2016-07-28 13:59:21 +00:00
dan d49fd4e89a Fix some problems with multi-column IN(SELECT...) processing.
FossilOrigin-Name: 719a3b2035a335ca8b9704646b1d641011e3ea0e
2016-07-27 19:33:04 +00:00
dan d9ed09d47b Merge latest trunk changes into this branch.
FossilOrigin-Name: d4f3d52c5a11fc7ad7e8cad76361edbcef13a12f
2016-07-26 18:15:35 +00:00
dan 8da209b169 Fix where.c handling of "IN (SELECT ...)" expressions when the SELECT returns more than one result column. Also error handling for other row value constructor cases.
FossilOrigin-Name: 061b8006034f06a0311b4304c8b14d2c8b0153df
2016-07-26 18:06:08 +00:00
dan ba00e30a3a Allow vector IN(SELECT ...) expressions to use an index if either all the indexed columns are declared NOT NULL or if there is no difference between the expression evaluating to 0 and NULL (as in a WHERE clause).
FossilOrigin-Name: e2fd6f49b1b145bec09c581cc982b89429643ae9
2016-07-23 20:24:06 +00:00
dan 4097d0ca79 Merge latest trunk changes with this branch.
FossilOrigin-Name: 60fed5cdd4a44aefa1b4d505adeb7936f2f0b952
2016-07-22 17:58:05 +00:00
dan cfbb5e82db Modifications towards better vector IN(...) support on this branch. Not activated yet.
FossilOrigin-Name: 34e35c71b25b0aa2d8931040feb260a78cc48c49
2016-07-13 19:48:13 +00:00
mistachkin 709947a315 Merge comment typo fixes from trunk.
FossilOrigin-Name: 728c5aa436a5f55c86b019c415a2b71d1b0a8fd6
2016-07-10 19:35:45 +00:00
dan 71c57db099 Add some support for using row value constructors in certain parts of SQL expressions. There are many bugs on this branch.
FossilOrigin-Name: b2204215b231202aef7a218411cc2ddaecf28f35
2016-07-09 20:23:55 +00:00
32 changed files with 3513 additions and 598 deletions
+38 -24
View File
@@ -1,5 +1,5 @@
C Build\sthe\sgenerate_series(START,END,STEP)\stable-valued\sfunction\sinto\nfuzzershell,\sto\smake\sit\seasier\sto\sconstruct\scompact\stest\scases\sthat\scontain\nactual\sdata.
D 2016-09-03T16:23:42.901
C Catch\svector\ssize\smismatch\sproblems\sduring\sname\sresolution\sto\savoid\slater\nproblems.
D 2016-09-05T12:02:34.649
F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c
@@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7
F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b
F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d
F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05
F src/expr.c 9c5eca8602f6c496e8d4eefefe2aae3d831dd510
F src/expr.c 942c417e84e835ce51c6b732afa0b4572110e1c1
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8
F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771
@@ -346,6 +346,7 @@ F src/global.c c45ea22aff29334f6a9ec549235ac3357c970015
F src/hash.c 55b5fb474100cee0b901edaf203e26c970940f36
F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c 3edb5a1bda44df13531fedfcde5fbcc2fc04c222
F src/legacy.c 75d3023be8f0d2b99d60f905090341a03358c58e
F src/loadext.c dd7a2b77902cc66c22555aef02e1a682554b7aec
@@ -373,7 +374,7 @@ F src/os_win.c 520f23475f1de530c435d30b67b7b15fe90874b0
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c bf5b71bde3e9b6110e7d6990607db881f6a471a2
F src/pager.h 966d2769e76ae347c8a32c4165faf6e6cb64546d
F src/parse.y ed6990c2d41eb0302eda90d5009c51fec792c850
F src/parse.y 0e0b6d46a990d01e4ca1e9d7e1d2d9b5a98f6bcb
F src/pcache.c 5583c8ade4b05075a60ba953ef471d1c1a9c05df
F src/pcache.h 2cedcd8407eb23017d92790b112186886e179490
F src/pcache1.c 4bb7a6a5300c67d0b033d25adb509c120c03e812
@@ -382,14 +383,14 @@ F src/pragma.h 64c78a648751b9f4f297276c4eb7507b14b4628c
F src/prepare.c 0fcf16eaacc90c1059055519a76b75b516a59a88
F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
F src/resolve.c d67b9a5cc33339256e2088c5a722745fc2ff5219
F src/resolve.c 24f40fd0c3475821d1ad762a3f2c3455cc839b42
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
F src/select.c fda7fd24b4d8e75479ae581329db0a0b0bf76633
F src/select.c 38216d0b2d42a0f475abf86a84c3499e6421ba29
F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7
F src/sqlite.h.in 4a030e254e204570444b34bf7d40fb4a5416089e
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae
F src/sqliteInt.h 153099746007418dfa03a99c8355c8d47b0a1cc9
F src/sqliteInt.h c9e010a79ab4ed7bdc910a24d8f08f3c6d5f822c
F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247
F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1
F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9
@@ -444,19 +445,19 @@ F src/test_windirent.h 7edc57e2faa727026dbd5d010dd0e2e665d5aa01
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 78c8085bc7af1922aa687f0f4bbd716821330de5
F src/treeview.c 70329ef46b86f94b16a98aacced793e0692217b5
F src/treeview.c f51b75a28b377adde9f79bc3deb6c7770bcf97c0
F src/trigger.c 11e20b3b12c847b3b9055594c0f1631266bb53fc
F src/update.c 8179e699dbd45b92934fd02d3d8e3732e8da8802
F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d
F src/vacuum.c 913970b9d86dd6c2b8063ef1af421880f1464ec3
F src/vdbe.c 751dd0a177615388f95123d723ce82395b938ce9
F src/vdbe.c d8fb59a3ef10636df2447f55c1e82be7b8ad1604
F src/vdbe.h 67bc551f7faf04c33493892e4b378aada823ed10
F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d
F src/vdbeapi.c a32d61b7dd05e6890d8fd44d2805f55e2f5ba9f3
F src/vdbeaux.c 83458783d241cfd6691141c8a105832ee50258e5
F src/vdbeblob.c 3e82a797b60c3b9fed7b8de8c539ca7607874937
F src/vdbemem.c 1ecaa5ee0caff07255f25d04e8dc88befb6f88d1
F src/vdbemem.c e67dc6d8177fd1830efb5d15e17793408251a187
F src/vdbesort.c 91fda3909326860382b0ca8aa251e609c6a9d62c
F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834
F src/vtab.c e02cacb5c7ae742631edeb9ae9f53d399f093fd8
@@ -464,10 +465,10 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a
F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2
F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09
F src/where.c 5c9df42d50888be8274a5a0eb062eb0629869bd3
F src/whereInt.h e5b939701a7ceffc5a3a8188a37f9746416ebcd0
F src/wherecode.c 99707d11907c71d289ee9553d2d1a22f1fd8ba41
F src/whereexpr.c 98ce9f3b8d92b6f741b9f3498f9619695739256e
F src/where.c 48d705e5196a0611a7be90698eade455ee238536
F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613
F src/wherecode.c 8a9a53cb52dd8a75e07c85e3bc12c1604c735954
F src/whereexpr.c c5ec87e234faf62ac2d4e7f7ce18fb1f4bd475ff
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
@@ -644,7 +645,7 @@ F test/e_createtable.test d4c6059d44dcd4b636de9aae322766062b471844
F test/e_delete.test ab39084f26ae1f033c940b70ebdbbd523dc4962e
F test/e_droptrigger.test 3cd080807622c13e5bbb61fc9a57bd7754da2412
F test/e_dropview.test 0c9f7f60989164a70a67a9d9c26d1083bc808306
F test/e_expr.test 03a84a6fa9bd3472112d6bd4599f5269f5f74803
F test/e_expr.test 1ffa8866d38e7becc76893a8829e9432050e5716
F test/e_fkey.test a1783fe1f759e1990e6a11adfcf0702dac4d0707
F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459
F test/e_insert.test 3de217e95094d3d165992a6de1164bbc4bd92dc7
@@ -823,7 +824,7 @@ F test/hook.test 3b7b99d0eece6d279812c2aef6fa08bdfabc633e
F test/icu.test 73956798bace8982909c00476b216714a6d0559a
F test/ieee754.test 806fc0ce7f305f57e3331eaceeddcfec9339e607
F test/imposter1.test c3f1db2d3db2c24611a6596a3fc0ffc14f1466c8
F test/in.test 61d940ced6817bee66e4e0b09d5bc8608f57134b
F test/in.test 20c5529986998949908f889c8208b2cd894b2cc9
F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
F test/in4.test d2b38cba404bc4320f4fe1b595b3d163f212c068
@@ -1019,6 +1020,16 @@ F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a
F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d
F test/rowvalue.test 753eb744b7efeb5ac643d35d6e1e5066452ccf79
F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b
F test/rowvalue3.test 01399b7bf150b0d41abce76c18072da777c2500c
F test/rowvalue4.test 9b60544dcd0231a9fcbe1ec095edb16cdaa7ae13
F test/rowvalue5.test a440d490c8c0bf606034c09d5c6bbf7840b98f95
F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087
F test/rowvalue7.test 5d06ff19d9e6969e574a2e662a531dd0c67801a8
F test/rowvalue8.test 5900eddad9e2c3c2e26f1a95f74aafc1232ee5e0
F test/rowvalue9.test 749a471f237f3fcfdfbd902f850adff2d9963560
F test/rowvaluefault.test 7b16485e3f2b371f3e3d05455b8ded6d0c090244
F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09
F test/savepoint.test c671fdbd34cd3bfe1518a777526ada595180cf8d
@@ -1042,7 +1053,7 @@ F test/select3.test 2ce595f8fb8e2ac10071d3b4e424cadd4634a054
F test/select4.test 5389d9895968d1196c457d59b3ee6515d771d328
F test/select5.test e758b8ef94f69b111df4cb819008856655dcd535
F test/select6.test 39eac4a5c03650b2b473c532882273283ee8b7a0
F test/select7.test 95e370c42d47c3c52377d05e9ffc01ccff7c1f61
F test/select7.test f659f231489349e8c5734e610803d7654207318f
F test/select8.test 8c8f5ae43894c891efc5755ed905467d1d67ad5d
F test/select9.test aebc2bb0c3bc44606125033cbcaac2c8d1f33a95
F test/selectA.test 101e722370ac6e84978c2958b8931c78b10a1709
@@ -1110,7 +1121,7 @@ F test/statfault.test f525a7bf633e50afd027700e9a486090684b1ac1
F test/stmt.test 64844332db69cf1a735fcb3e11548557fc95392f
F test/subquery.test d7268d193dd33d5505df965399d3a594e76ae13f
F test/subquery2.test 438f8a7da1457277b22e4176510f7659b286995f
F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
F test/subselect.test 0966aa8e720224dbd6a5e769a3ec2a723e332303
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8
F test/superlock.test ec94f0556b6488d97f71c79f9061ae08d9ab8f12
@@ -1130,7 +1141,7 @@ F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
F test/temptable2.test cd396beb41117a5302fff61767c35fa4270a0d5e
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
F test/temptrigger.test 8ec228b0db5d7ebc4ee9b458fc28cb9e7873f5e1
F test/tester.tcl 949b4a73bd0324b7c796818d0d6a6715712932b3
F test/tester.tcl 4ce5afd5e192db4cae178e1a983b060e0f08c5d6
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@@ -1313,7 +1324,7 @@ F test/tt3_lookaside1.c 0377e202c3c2a50d688cb65ba203afeda6fafeb9
F test/tt3_stress.c c57d804716165811d979d4a719e05baccd79277f
F test/tt3_vacuum.c 1753f45917699c9c1f66b64c717a717c9379f776
F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
F test/types2.test 1aeb81976841a91eef292723649b5c4fe3bc3cac
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264
F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2
@@ -1425,7 +1436,7 @@ F test/zerodamage.test e59a56443d6298ecf7435f618f0b27654f0c849e
F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5
F tool/GetTclKit.bat 629d87562e0487c386db630033931d12d62e6372
F tool/Replace.cs 02c67258801c2fb5f63231e0ac0f220b4b36ba91
F tool/addopcodes.tcl 2b089684eb8b7d0db64cf9d8e6d2fe1b6d279e8d
F tool/addopcodes.tcl 10c889c4a65ec6c5604e4a47306fa77ff57ae189
F tool/build-all-msvc.bat 3e4e4043b53f1aede4308e0d2567bbd773614630 x
F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367
F tool/cg_anno.tcl 692ce4b8693d59e3a3de77ca97f4139ecfa641b0 x
@@ -1511,7 +1522,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 9bdf7ca1b317fe0ba7efea38fb395bf6130ac89a
R f234dfb8eb81560f8891406102139ec1
P 7d9bd22c0715ede2592ee1fa7ebc215aded1ca1b
R 9a7c982f4a8ba0874174ff495f80018e
T *branch * early-vector-size-check
T *sym-early-vector-size-check *
T -sym-rowvalue *
U drh
Z 8e05ee287619d9d44d13c4eb0cda4de7
Z 17a06585da7241edb65fdad418303946
+1 -1
View File
@@ -1 +1 @@
672c21bcf09c5bfb67e061456a56be45409c4f34
56562a0346170cf7b72445976864b058437a8ac3
+942 -269
View File
File diff suppressed because it is too large Load Diff
+107
View File
@@ -0,0 +1,107 @@
IN-Operator Implementation Notes
================================
## Definitions:
An IN operator has one of the following formats:
>
x IN (y1,y2,y3,...,yN)
x IN (subquery)
The "x" is referred to as the LHS (left-hand side). The list or subquery
on the right is called the RHS (right-hand side). If the RHS is a list
it must be a non-empty list. But if the RHS is a subquery, it can be an
empty set.
The LHS can be a scalar (a single quantity) or a vector (a list of
two or or more values) or a subquery that returns one or more columns.
We use the term "vector" to mean an actually list of values or a
subquery that returns two or more columns. An isolated value or
a subquery that returns a single columns is called a scalar.
The RHS can be a subquery that returns a single column, a subquery
that returns two or more columns, or a list of scalars. It is not
currently support for the RHS to be a list of vectors.
The number of columns for LHS must match the number of columns for
the RHS. If the RHS is a list of values, then the LHS must be a
scalar. If the RHS is a subquery returning N columns, then the LHS
must be a vector of size N.
NULL values can occur in either or both of the LHS and RHS.
If the LHS contains only
NULL values then we say that it is a "total-NULL". If the LHS contains
some NULL values and some non-NULL values, then it is a "partial-NULL".
For a scalar, there is no difference between a partial-NULL and a total-NULL.
The RHS is a partial-NULL if any row contains a NULL value. The RHS is
a total-NULL if it contains one or more rows that contain only NULL values.
The LHS is called "non-NULL" if it contains no NULL values. The RHS is
called "non-NULL" if it contains no NULL values in any row.
The result of an IN operator is one of TRUE, FALSE, or NULL. A NULL result
means that it cannot be determined if the LHS is contained in the RHS due
to the presence of NULL values. In some contexts (for example, when the IN
operator occurs in a WHERE clause)
the system only needs a binary result: TRUE or NOT-TRUE. One can also
to define a binary result of FALSE and NOT-FALSE, but
it turns out that no extra optimizations are possible in that case, so if
the FALSE/NOT-FALSE binary is needed, we have to compute the three-state
TRUE/FALSE/NULL result and then combine the TRUE and NULL values into
NOT-FALSE.
A "NOT IN" operator is computed by first computing the equivalent IN
operator, then interchanging the TRUE and FALSE results.
## Simple Full-Scan Algorithm
The following algorithm always compute the correct answer. However, this
algorithm is suboptimal, especially if there are many rows on the RHS.
1. Set the null-flag to false
2. For each row in the RHS:
<ol type='a'>
<li> Compare the LHS against the RHS
<li> If the LHS exactly matches the RHS, immediately return TRUE
<li> If the comparison result is NULL, set the null-flag to true
</ol>
3. If the null-flag is true, return NULL.
4. Return FALSE
## Optimized Algorithm
The following procedure computes the same answer as the simple full-scan
algorithm, though it does so with less work in the common case. This
is the algorithm that is implemented in SQLite.
1. If the RHS is a constant list of length 1 or 2, then rewrite the
IN operator as a simple expression. Implement
x IN (y1,y2)
as if it were
x=y1 OR x=y2
This is the INDEX_NOOP optimization and is only undertaken if the
IN operator is used for membership testing. If the IN operator is
driving a loop, then skip this step entirely.
2. Check the LHS to see if it is a partial-NULL and if it is, jump
ahead to step 5.
3. Do a binary search of the RHS using the LHS as a probe. If
an exact match is found, return TRUE.
4. If the RHS is non-NULL then return FALSE.
5. If we do not need to distinguish between FALSE and NULL,
then return FALSE.
6. For each row in the RHS, compare that row against the LHS and
if the result is NULL, immediately return NULL. In the case
of a scalar IN operator, we only need to look at the very first
row the RHS because for a scalar RHS, all NULLs will always come
first. If the RHS is empty, this step is a no-op.
7. Return FALSE.
+17
View File
@@ -789,10 +789,16 @@ setlist(A) ::= setlist(A) COMMA nm(X) EQ expr(Y). {
A = sqlite3ExprListAppend(pParse, A, Y.pExpr);
sqlite3ExprListSetName(pParse, A, &X, 1);
}
setlist(A) ::= setlist(A) COMMA LP idlist(X) RP EQ expr(Y). {
A = sqlite3ExprListAppendVector(pParse, A, X, Y.pExpr);
}
setlist(A) ::= nm(X) EQ expr(Y). {
A = sqlite3ExprListAppend(pParse, 0, Y.pExpr);
sqlite3ExprListSetName(pParse, A, &X, 1);
}
setlist(A) ::= LP idlist(X) RP EQ expr(Y). {
A = sqlite3ExprListAppendVector(pParse, 0, X, Y.pExpr);
}
////////////////////////// The INSERT command /////////////////////////////////
//
@@ -946,6 +952,17 @@ term(A) ::= CTIME_KW(OP). {
}
}
expr(A) ::= LP(L) nexprlist(X) COMMA expr(Y) RP(R). {
ExprList *pList = sqlite3ExprListAppend(pParse, X, Y.pExpr);
A.pExpr = sqlite3PExpr(pParse, TK_VECTOR, 0, 0, 0);
if( A.pExpr ){
A.pExpr->x.pList = pList;
spanSet(&A, &L, &R);
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
}
expr(A) ::= expr(A) AND(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);}
expr(A) ::= expr(A) OR(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);}
expr(A) ::= expr(A) LT|GT|GE|LE(OP) expr(Y).
+27
View File
@@ -776,6 +776,33 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr);
break;
}
case TK_EQ:
case TK_NE:
case TK_LT:
case TK_LE:
case TK_GT:
case TK_GE:
case TK_IS:
case TK_ISNOT: {
int nLeft, nRight;
if( pParse->db->mallocFailed ) break;
assert( pExpr->pRight!=0 );
assert( pExpr->pLeft!=0 );
nLeft = sqlite3ExprVectorSize(pExpr->pLeft);
nRight = sqlite3ExprVectorSize(pExpr->pRight);
if( nLeft!=nRight ){
testcase( pExpr->op==TK_EQ );
testcase( pExpr->op==TK_NE );
testcase( pExpr->op==TK_LT );
testcase( pExpr->op==TK_LE );
testcase( pExpr->op==TK_GT );
testcase( pExpr->op==TK_GE );
testcase( pExpr->op==TK_IS );
testcase( pExpr->op==TK_ISNOT );
sqlite3ErrorMsg(pParse, "row value misused");
}
break;
}
}
return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue;
}
+29 -57
View File
@@ -88,7 +88,7 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){
void sqlite3SelectDestInit(SelectDest *pDest, int eDest, int iParm){
pDest->eDest = (u8)eDest;
pDest->iSDParm = iParm;
pDest->affSdst = 0;
pDest->zAffSdst = 0;
pDest->iSdst = 0;
pDest->nSdst = 0;
}
@@ -659,30 +659,6 @@ static void codeDistinct(
sqlite3ReleaseTempReg(pParse, r1);
}
#ifndef SQLITE_OMIT_SUBQUERY
/*
** Generate an error message when a SELECT is used within a subexpression
** (example: "a IN (SELECT * FROM table)") but it has more than 1 result
** column. We do this in a subroutine because the error used to occur
** in multiple places. (The error only occurs in one place now, but we
** retain the subroutine to minimize code disruption.)
*/
static int checkForMultiColumnSelectError(
Parse *pParse, /* Parse context. */
SelectDest *pDest, /* Destination of SELECT results */
int nExpr /* Number of result columns returned by SELECT */
){
int eDest = pDest->eDest;
if( nExpr>1 && (eDest==SRT_Mem || eDest==SRT_Set) ){
sqlite3ErrorMsg(pParse, "only a single result allowed for "
"a SELECT that is part of an expression");
return 1;
}else{
return 0;
}
}
#endif
/*
** This routine generates the code for the inside of the inner loop
** of a SELECT.
@@ -892,19 +868,19 @@ static void selectInnerLoop(
** item into the set table with bogus data.
*/
case SRT_Set: {
assert( nResultCol==1 );
pDest->affSdst =
sqlite3CompareAffinity(pEList->a[0].pExpr, pDest->affSdst);
if( pSort ){
/* At first glance you would think we could optimize out the
** ORDER BY in this case since the order of entries in the set
** does not matter. But there might be a LIMIT clause, in which
** case the order does matter */
pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg);
pushOntoSorter(
pParse, pSort, p, regResult, regResult, nResultCol, nPrefixReg);
}else{
int r1 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult,1,r1, &pDest->affSdst, 1);
sqlite3ExprCacheAffinityChange(pParse, regResult, 1);
assert( sqlite3Strlen30(pDest->zAffSdst)==nResultCol );
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, nResultCol,
r1, pDest->zAffSdst, nResultCol);
sqlite3ExprCacheAffinityChange(pParse, regResult, nResultCol);
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
sqlite3ReleaseTempReg(pParse, r1);
}
@@ -920,13 +896,14 @@ static void selectInnerLoop(
}
/* If this is a scalar select that is part of an expression, then
** store the results in the appropriate memory cell and break out
** of the scan loop.
** store the results in the appropriate memory cell or array of
** memory cells and break out of the scan loop.
*/
case SRT_Mem: {
assert( nResultCol==1 );
assert( nResultCol==pDest->nSdst );
if( pSort ){
pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg);
pushOntoSorter(
pParse, pSort, p, regResult, regResult, nResultCol, nPrefixReg);
}else{
assert( regResult==iParm );
/* The LIMIT clause will jump out of the loop for us */
@@ -1241,10 +1218,14 @@ static void generateSortTail(
sqlite3VdbeResolveLabel(v, pSort->labelBkOut);
}
iTab = pSort->iECursor;
if( eDest==SRT_Output || eDest==SRT_Coroutine ){
if( eDest==SRT_Output || eDest==SRT_Coroutine || eDest==SRT_Mem ){
regRowid = 0;
regRow = pDest->iSdst;
nSortData = nColumn;
}else if( eDest==SRT_Set ){
regRowid = sqlite3GetTempReg(pParse);
regRow = sqlite3GetTempRange(pParse, nColumn);
nSortData = nColumn;
}else{
regRowid = sqlite3GetTempReg(pParse);
regRow = sqlite3GetTempReg(pParse);
@@ -1283,16 +1264,14 @@ static void generateSortTail(
}
#ifndef SQLITE_OMIT_SUBQUERY
case SRT_Set: {
assert( nColumn==1 );
sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, 1, regRowid,
&pDest->affSdst, 1);
sqlite3ExprCacheAffinityChange(pParse, regRow, 1);
assert( nColumn==sqlite3Strlen30(pDest->zAffSdst) );
sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, nColumn, regRowid,
pDest->zAffSdst, nColumn);
sqlite3ExprCacheAffinityChange(pParse, regRow, nColumn);
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRowid);
break;
}
case SRT_Mem: {
assert( nColumn==1 );
sqlite3ExprCodeMove(pParse, regRow, iParm, 1);
/* The LIMIT clause will terminate the loop for us */
break;
}
@@ -1311,7 +1290,11 @@ static void generateSortTail(
}
}
if( regRowid ){
sqlite3ReleaseTempReg(pParse, regRow);
if( eDest==SRT_Set ){
sqlite3ReleaseTempRange(pParse, regRow, nColumn);
}else{
sqlite3ReleaseTempReg(pParse, regRow);
}
sqlite3ReleaseTempReg(pParse, regRowid);
}
/* The bottom of the loop
@@ -1813,7 +1796,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
*/
static SQLITE_NOINLINE Vdbe *allocVdbe(Parse *pParse){
Vdbe *v = pParse->pVdbe = sqlite3VdbeCreate(pParse);
if( v ) sqlite3VdbeAddOp0(v, OP_Init);
if( v ) sqlite3VdbeAddOp2(v, OP_Init, 0, 1);
if( pParse->pToplevel==0
&& OptimizationEnabled(pParse->db,SQLITE_FactorOutConst)
){
@@ -2659,10 +2642,9 @@ static int generateOutputSubroutine(
case SRT_Set: {
int r1;
assert( pIn->nSdst==1 || pParse->nErr>0 );
pDest->affSdst =
sqlite3CompareAffinity(p->pEList->a[0].pExpr, pDest->affSdst);
r1 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, 1, r1, &pDest->affSdst,1);
sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst,
r1, pDest->zAffSdst,1);
sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, 1);
sqlite3VdbeAddOp2(v, OP_IdxInsert, pDest->iSDParm, r1);
sqlite3ReleaseTempReg(pParse, r1);
@@ -4897,16 +4879,6 @@ int sqlite3Select(
}
#endif
/* If writing to memory or generating a set
** only a single column may be output.
*/
#ifndef SQLITE_OMIT_SUBQUERY
if( checkForMultiColumnSelectError(pParse, pDest, p->pEList->nExpr) ){
goto select_end;
}
#endif
/* Try to flatten subqueries in the FROM clause up into the main query
*/
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
+25 -6
View File
@@ -1733,6 +1733,7 @@ struct CollSeq {
** operator is NULL. It is added to certain comparison operators to
** prove that the operands are always NOT NULL.
*/
#define SQLITE_KEEPNULL 0x08 /* Used by vector == or <> */
#define SQLITE_JUMPIFNULL 0x10 /* jumps if either operand is NULL */
#define SQLITE_STOREP2 0x20 /* Store result in reg[P2] rather than jump */
#define SQLITE_NULLEQ 0x80 /* NULL=NULL */
@@ -2297,9 +2298,11 @@ struct Expr {
int iTable; /* TK_COLUMN: cursor number of table holding column
** TK_REGISTER: register number
** TK_TRIGGER: 1 -> new, 0 -> old
** EP_Unlikely: 134217728 times likelihood */
** EP_Unlikely: 134217728 times likelihood
** TK_SELECT: 1st register of result vector */
ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid.
** TK_VARIABLE: variable number (always >= 1). */
** TK_VARIABLE: variable number (always >= 1).
** TK_SELECT_COLUMN: column of the result vector */
i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */
u8 op2; /* TK_REGISTER: original value of Expr.op
@@ -2780,7 +2783,7 @@ struct Select {
*/
struct SelectDest {
u8 eDest; /* How to dispose of the results. On of SRT_* above. */
char affSdst; /* Affinity used when eDest==SRT_Set */
char *zAffSdst; /* Affinity used when eDest==SRT_Set */
int iSDParm; /* A parameter used by the eDest disposal method */
int iSdst; /* Base register where results are written */
int nSdst; /* Number of registers allocated */
@@ -3512,6 +3515,7 @@ char *sqlite3VMPrintf(sqlite3*,const char*, va_list);
#if defined(SQLITE_DEBUG)
void sqlite3TreeViewExpr(TreeView*, const Expr*, u8);
void sqlite3TreeViewBareExprList(TreeView*, const ExprList*, const char*);
void sqlite3TreeViewExprList(TreeView*, const ExprList*, u8, const char*);
void sqlite3TreeViewSelect(TreeView*, const Select*, u8);
void sqlite3TreeViewWith(TreeView*, const With*, u8);
@@ -3543,6 +3547,7 @@ Expr *sqlite3ExprFunction(Parse*,ExprList*, Token*);
void sqlite3ExprAssignVarNumber(Parse*, Expr*);
void sqlite3ExprDelete(sqlite3*, Expr*);
ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*);
ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*);
void sqlite3ExprListSetSortOrder(ExprList*,int);
void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int);
void sqlite3ExprListSetSpan(Parse*,ExprList*,ExprSpan*);
@@ -3878,6 +3883,7 @@ const char *sqlite3IndexAffinityStr(sqlite3*, Index*);
void sqlite3TableAffinity(Vdbe*, Table*, int);
char sqlite3CompareAffinity(Expr *pExpr, char aff2);
int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity);
char sqlite3TableColumnAffinity(Table*,int);
char sqlite3ExprAffinity(Expr *pExpr);
int sqlite3Atoi64(const char*, i64*, int, u8);
int sqlite3DecOrHexToI64(const char*, i64*);
@@ -3943,7 +3949,7 @@ void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
int sqlite3GetToken(const unsigned char *, int *);
void sqlite3NestedParse(Parse*, const char*, ...);
void sqlite3ExpirePreparedStatements(sqlite3*);
int sqlite3CodeSubselect(Parse *, Expr *, int, int);
int sqlite3CodeSubselect(Parse*, Expr *, int, int);
void sqlite3SelectPrep(Parse*, Select*, NameContext*);
void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p);
int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);
@@ -3998,12 +4004,20 @@ Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
void sqlite3BackupRestart(sqlite3_backup *);
void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *);
#ifndef SQLITE_OMIT_SUBQUERY
int sqlite3ExprCheckIN(Parse*, Expr*);
#else
# define sqlite3ExprCheckIN(x,y) SQLITE_OK
#endif
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
void sqlite3AnalyzeFunctions(void);
int sqlite3Stat4ProbeSetValue(Parse*,Index*,UnpackedRecord**,Expr*,u8,int,int*);
int sqlite3Stat4ProbeSetValue(
Parse*,Index*,UnpackedRecord**,Expr*,int,int,int*);
int sqlite3Stat4ValueFromExpr(Parse*, Expr*, u8, sqlite3_value**);
void sqlite3Stat4ProbeFree(UnpackedRecord*);
int sqlite3Stat4Column(sqlite3*, const void*, int, int, sqlite3_value**);
char sqlite3IndexColumnAffinity(sqlite3*, Index*, int);
#endif
/*
@@ -4156,7 +4170,7 @@ const char *sqlite3JournalModename(int);
#define IN_INDEX_NOOP_OK 0x0001 /* OK to return IN_INDEX_NOOP */
#define IN_INDEX_MEMBERSHIP 0x0002 /* IN operator used for membership test */
#define IN_INDEX_LOOP 0x0004 /* IN operator used as a loop */
int sqlite3FindInIndex(Parse *, Expr *, u32, int*);
int sqlite3FindInIndex(Parse *, Expr *, u32, int*, int*);
int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
int sqlite3JournalSize(sqlite3_vfs *);
@@ -4261,4 +4275,9 @@ int sqlite3ThreadJoin(SQLiteThread*, void**);
int sqlite3DbstatRegister(sqlite3*);
#endif
int sqlite3ExprVectorSize(Expr *pExpr);
int sqlite3ExprIsVector(Expr *pExpr);
Expr *sqlite3VectorFieldSubexpr(Expr*, int);
Expr *sqlite3ExprForVectorField(Parse*,Expr*,int);
#endif /* SQLITEINT_H */
+21 -4
View File
@@ -451,6 +451,15 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
sqlite3TreeViewExpr(pView, pExpr->pRight, 0);
break;
}
case TK_VECTOR: {
sqlite3TreeViewBareExprList(pView, pExpr->x.pList, "VECTOR");
break;
}
case TK_SELECT_COLUMN: {
sqlite3TreeViewLine(pView, "SELECT-COLUMN %d", pExpr->iColumn);
sqlite3TreeViewSelect(pView, pExpr->pLeft->x.pSelect, 0);
break;
}
default: {
sqlite3TreeViewLine(pView, "op=%d", pExpr->op);
break;
@@ -467,21 +476,20 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
sqlite3TreeViewPop(pView);
}
/*
** Generate a human-readable explanation of an expression list.
*/
void sqlite3TreeViewExprList(
void sqlite3TreeViewBareExprList(
TreeView *pView,
const ExprList *pList,
u8 moreToFollow,
const char *zLabel
){
int i;
pView = sqlite3TreeViewPush(pView, moreToFollow);
if( zLabel==0 || zLabel[0]==0 ) zLabel = "LIST";
if( pList==0 ){
sqlite3TreeViewLine(pView, "%s (empty)", zLabel);
}else{
int i;
sqlite3TreeViewLine(pView, "%s", zLabel);
for(i=0; i<pList->nExpr; i++){
int j = pList->a[i].u.x.iOrderByCol;
@@ -493,6 +501,15 @@ void sqlite3TreeViewExprList(
if( j ) sqlite3TreeViewPop(pView);
}
}
}
void sqlite3TreeViewExprList(
TreeView *pView,
const ExprList *pList,
u8 moreToFollow,
const char *zLabel
){
pView = sqlite3TreeViewPush(pView, moreToFollow);
sqlite3TreeViewBareExprList(pView, pList, zLabel);
sqlite3TreeViewPop(pView);
}
+112 -73
View File
@@ -573,7 +573,7 @@ int sqlite3VdbeExec(
sqlite3 *db = p->db; /* The database */
u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */
u8 encoding = ENC(db); /* The database encoding */
int iCompare = 0; /* Result of last OP_Compare operation */
int iCompare = 0; /* Result of last comparison */
unsigned nVmStep = 0; /* Number of virtual machine steps */
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
unsigned nProgressLimit = 0;/* Invoke xProgress() when nVmStep reaches this */
@@ -905,7 +905,7 @@ case OP_Yield: { /* in1, jump */
}
/* Opcode: HaltIfNull P1 P2 P3 P4 P5
** Synopsis: if r[P3]=null halt
** Synopsis: if r[P3]=null halt
**
** Check the value in register P3. If it is NULL then Halt using
** parameter P1, P2, and P4 as if this were a Halt instruction. If the
@@ -1118,7 +1118,7 @@ case OP_String: { /* out2 */
}
/* Opcode: Null P1 P2 P3 * *
** Synopsis: r[P2..P3]=NULL
** Synopsis: r[P2..P3]=NULL
**
** Write a NULL into registers P2. If P3 greater than P2, then also write
** NULL into register P3 and every register in between P2 and P3. If P3
@@ -1147,7 +1147,7 @@ case OP_Null: { /* out2 */
}
/* Opcode: SoftNull P1 * * * *
** Synopsis: r[P1]=NULL
** Synopsis: r[P1]=NULL
**
** Set register P1 to have the value NULL as seen by the OP_MakeRecord
** instruction, but do not free any string or blob memory associated with
@@ -1200,7 +1200,7 @@ case OP_Variable: { /* out2 */
}
/* Opcode: Move P1 P2 P3 * *
** Synopsis: r[P2@P3]=r[P1@P3]
** Synopsis: r[P2@P3]=r[P1@P3]
**
** Move the P3 values in register P1..P1+P3-1 over into
** registers P2..P2+P3-1. Registers P1..P1+P3-1 are
@@ -1310,7 +1310,7 @@ case OP_IntCopy: { /* out2 */
}
/* Opcode: ResultRow P1 P2 * * *
** Synopsis: output=r[P1@P2]
** Synopsis: output=r[P1@P2]
**
** The registers P1 through P1+P2-1 contain a single row of
** results. This opcode causes the sqlite3_step() call to terminate
@@ -1443,14 +1443,14 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
}
/* Opcode: Add P1 P2 P3 * *
** Synopsis: r[P3]=r[P1]+r[P2]
** Synopsis: r[P3]=r[P1]+r[P2]
**
** Add the value in register P1 to the value in register P2
** and store the result in register P3.
** If either input is NULL, the result is NULL.
*/
/* Opcode: Multiply P1 P2 P3 * *
** Synopsis: r[P3]=r[P1]*r[P2]
** Synopsis: r[P3]=r[P1]*r[P2]
**
**
** Multiply the value in register P1 by the value in register P2
@@ -1458,14 +1458,14 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
** If either input is NULL, the result is NULL.
*/
/* Opcode: Subtract P1 P2 P3 * *
** Synopsis: r[P3]=r[P2]-r[P1]
** Synopsis: r[P3]=r[P2]-r[P1]
**
** Subtract the value in register P1 from the value in register P2
** and store the result in register P3.
** If either input is NULL, the result is NULL.
*/
/* Opcode: Divide P1 P2 P3 * *
** Synopsis: r[P3]=r[P2]/r[P1]
** Synopsis: r[P3]=r[P2]/r[P1]
**
** Divide the value in register P1 by the value in register P2
** and store the result in register P3 (P3=P2/P1). If the value in
@@ -1473,7 +1473,7 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
** NULL, the result is NULL.
*/
/* Opcode: Remainder P1 P2 P3 * *
** Synopsis: r[P3]=r[P2]%r[P1]
** Synopsis: r[P3]=r[P2]%r[P1]
**
** Compute the remainder after integer register P2 is divided by
** register P1 and store the result in register P3.
@@ -1706,21 +1706,21 @@ case OP_Function: {
}
/* Opcode: BitAnd P1 P2 P3 * *
** Synopsis: r[P3]=r[P1]&r[P2]
** Synopsis: r[P3]=r[P1]&r[P2]
**
** Take the bit-wise AND of the values in register P1 and P2 and
** store the result in register P3.
** If either input is NULL, the result is NULL.
*/
/* Opcode: BitOr P1 P2 P3 * *
** Synopsis: r[P3]=r[P1]|r[P2]
** Synopsis: r[P3]=r[P1]|r[P2]
**
** Take the bit-wise OR of the values in register P1 and P2 and
** store the result in register P3.
** If either input is NULL, the result is NULL.
*/
/* Opcode: ShiftLeft P1 P2 P3 * *
** Synopsis: r[P3]=r[P2]<<r[P1]
** Synopsis: r[P3]=r[P2]<<r[P1]
**
** Shift the integer value in register P2 to the left by the
** number of bits specified by the integer in register P1.
@@ -1728,7 +1728,7 @@ case OP_Function: {
** If either input is NULL, the result is NULL.
*/
/* Opcode: ShiftRight P1 P2 P3 * *
** Synopsis: r[P3]=r[P2]>>r[P1]
** Synopsis: r[P3]=r[P2]>>r[P1]
**
** Shift the integer value in register P2 to the right by the
** number of bits specified by the integer in register P1.
@@ -1788,7 +1788,7 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */
}
/* Opcode: AddImm P1 P2 * * *
** Synopsis: r[P1]=r[P1]+P2
** Synopsis: r[P1]=r[P1]+P2
**
** Add the constant P2 to the value in register P1.
** The result is always an integer.
@@ -1880,14 +1880,59 @@ case OP_Cast: { /* in1 */
}
#endif /* SQLITE_OMIT_CAST */
/* Opcode: Eq P1 P2 P3 P4 P5
** Synopsis: IF r[P3]==r[P1]
**
** Compare the values in register P1 and P3. If reg(P3)==reg(P1) then
** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5, then
** store the result of comparison in register P2.
**
** The SQLITE_AFF_MASK portion of P5 must be an affinity character -
** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made
** to coerce both inputs according to this affinity before the
** comparison is made. If the SQLITE_AFF_MASK is 0x00, then numeric
** affinity is used. Note that the affinity conversions are stored
** back into the input registers P1 and P3. So this opcode can cause
** persistent changes to registers P1 and P3.
**
** Once any conversions have taken place, and neither value is NULL,
** the values are compared. If both values are blobs then memcmp() is
** used to determine the results of the comparison. If both values
** are text, then the appropriate collating function specified in
** P4 is used to do the comparison. If P4 is not specified then
** memcmp() is used to compare text string. If both values are
** numeric, then a numeric comparison is used. If the two values
** are of different types, then numbers are considered less than
** strings and strings are considered less than blobs.
**
** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either
** true or false and is never NULL. If both operands are NULL then the result
** of comparison is true. If either operand is NULL then the result is false.
** If neither operand is NULL the result is the same as it would be if
** the SQLITE_NULLEQ flag were omitted from P5.
**
** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the
** content of r[P2] is only set to 1 (true) if it was not previously NULL.
*/
/* Opcode: Ne P1 P2 P3 P4 P5
** Synopsis: IF r[P3]!=r[P1]
**
** This works just like the Eq opcode except that the jump is taken if
** the operands in registers P1 and P3 are not equal. See the Eq opcode for
** additional information.
**
** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the
** content of r[P2] is only set to 0 (false) if it was not previously NULL.
*/
/* Opcode: Lt P1 P2 P3 P4 P5
** Synopsis: IF r[P3]<r[P1]
**
** Compare the values in register P1 and P3. If reg(P3)<reg(P1) then
** jump to address P2.
** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5 store
** the result of comparison (0 or 1 or NULL) into register P2.
**
** If the SQLITE_JUMPIFNULL bit of P5 is set and either reg(P1) or
** reg(P3) is NULL then take the jump. If the SQLITE_JUMPIFNULL
** reg(P3) is NULL then the take the jump. If the SQLITE_JUMPIFNULL
** bit is clear then fall through if either operand is NULL.
**
** The SQLITE_AFF_MASK portion of P5 must be an affinity character -
@@ -1907,39 +1952,6 @@ case OP_Cast: { /* in1 */
** numeric, then a numeric comparison is used. If the two values
** are of different types, then numbers are considered less than
** strings and strings are considered less than blobs.
**
** If the SQLITE_STOREP2 bit of P5 is set, then do not jump. Instead,
** store a boolean result (either 0, or 1, or NULL) in register P2.
**
** If the SQLITE_NULLEQ bit is set in P5, then NULL values are considered
** equal to one another, provided that they do not have their MEM_Cleared
** bit set.
*/
/* Opcode: Ne P1 P2 P3 P4 P5
** Synopsis: IF r[P3]!=r[P1]
**
** This works just like the Lt opcode except that the jump is taken if
** the operands in registers P1 and P3 are not equal. See the Lt opcode for
** additional information.
**
** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either
** true or false and is never NULL. If both operands are NULL then the result
** of comparison is false. If either operand is NULL then the result is true.
** If neither operand is NULL the result is the same as it would be if
** the SQLITE_NULLEQ flag were omitted from P5.
*/
/* Opcode: Eq P1 P2 P3 P4 P5
** Synopsis: IF r[P3]==r[P1]
**
** This works just like the Lt opcode except that the jump is taken if
** the operands in registers P1 and P3 are equal.
** See the Lt opcode for additional information.
**
** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either
** true or false and is never NULL. If both operands are NULL then the result
** of comparison is true. If either operand is NULL then the result is false.
** If neither operand is NULL the result is the same as it would be if
** the SQLITE_NULLEQ flag were omitted from P5.
*/
/* Opcode: Le P1 P2 P3 P4 P5
** Synopsis: IF r[P3]<=r[P1]
@@ -1968,7 +1980,7 @@ case OP_Lt: /* same as TK_LT, jump, in1, in3 */
case OP_Le: /* same as TK_LE, jump, in1, in3 */
case OP_Gt: /* same as TK_GT, jump, in1, in3 */
case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
int res; /* Result of the comparison of pIn1 against pIn3 */
int res, res2; /* Result of the comparison of pIn1 against pIn3 */
char affinity; /* Affinity to use for comparison */
u16 flags1; /* Copy of initial value of pIn1->flags */
u16 flags3; /* Copy of initial value of pIn3->flags */
@@ -1991,9 +2003,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
&& (flags3&MEM_Null)!=0
&& (flags3&MEM_Cleared)==0
){
res = 0; /* Results are equal */
res = 0; /* Operands are equal */
}else{
res = 1; /* Results are not equal */
res = 1; /* Operands are not equal */
}
}else{
/* SQLITE_NULLEQ is clear and at least one operand is NULL,
@@ -2002,6 +2014,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
*/
if( pOp->p5 & SQLITE_STOREP2 ){
pOut = &aMem[pOp->p2];
iCompare = 1; /* Operands are not equal */
memAboutToChange(p, pOut);
MemSetTypeFlag(pOut, MEM_Null);
REGISTER_TRACE(pOp->p2, pOut);
@@ -2055,12 +2068,12 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl);
}
switch( pOp->opcode ){
case OP_Eq: res = res==0; break;
case OP_Ne: res = res!=0; break;
case OP_Lt: res = res<0; break;
case OP_Le: res = res<=0; break;
case OP_Gt: res = res>0; break;
default: res = res>=0; break;
case OP_Eq: res2 = res==0; break;
case OP_Ne: res2 = res; break;
case OP_Lt: res2 = res<0; break;
case OP_Le: res2 = res<=0; break;
case OP_Gt: res2 = res>0; break;
default: res2 = res>=0; break;
}
/* Undo any changes made by applyAffinity() to the input registers. */
@@ -2071,19 +2084,46 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
if( pOp->p5 & SQLITE_STOREP2 ){
pOut = &aMem[pOp->p2];
iCompare = res;
res2 = res2!=0; /* For this path res2 must be exactly 0 or 1 */
if( (pOp->p5 & SQLITE_KEEPNULL)!=0 && (pOut->flags & MEM_Null)!=0 ){
/* The KEEPNULL flag prevents OP_Eq from overwriting a NULL with 1
** and prevents OP_Ne from overwriting NULL with 0. */
assert( pOp->opcode==OP_Ne || pOp->opcode==OP_Eq );
assert( res2==0 || res2==1 );
if( (pOp->opcode==OP_Eq)==res2 ) break;
}
memAboutToChange(p, pOut);
MemSetTypeFlag(pOut, MEM_Int);
pOut->u.i = res;
pOut->u.i = res2;
REGISTER_TRACE(pOp->p2, pOut);
}else{
VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3);
if( res ){
if( res2 ){
goto jump_to_p2;
}
}
break;
}
/* Opcode: ElseNotEq * P2 * * *
**
** This opcode must immediately follow an Lt or Gt comparison operator.
** If the operands in that previous comparison had been used with an Eq
** operator and if the result of that Eq would be NULL or false (0), then
** then jump to P2. If the result of comparing the two previous operands
** using Eq would have been true (1), then fall through.
*/
case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */
assert( pOp>aOp );
assert( pOp[-1].opcode==OP_Lt || pOp[-1].opcode==OP_Gt );
assert( pOp[-1].p5 & SQLITE_STOREP2 );
VdbeBranchTaken(iCompare!=0, 2);
if( iCompare!=0 ) goto jump_to_p2;
break;
}
/* Opcode: Permutation * * * P4 *
**
** Set the permutation used by the OP_Compare operator to be the array
@@ -2333,7 +2373,7 @@ case OP_IfNot: { /* jump, in1 */
}
/* Opcode: IsNull P1 P2 * * *
** Synopsis: if r[P1]==NULL goto P2
** Synopsis: if r[P1]==NULL goto P2
**
** Jump to P2 if the value in register P1 is NULL.
*/
@@ -2361,7 +2401,7 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
}
/* Opcode: Column P1 P2 P3 P4 P5
** Synopsis: r[P3]=PX
** Synopsis: r[P3]=PX
**
** Interpret the data that cursor P1 points to as a structure built using
** the MakeRecord instruction. (See the MakeRecord opcode for additional
@@ -3871,7 +3911,6 @@ seek_not_found:
}
break;
}
/* Opcode: Found P1 P2 P3 P4 *
** Synopsis: key=r[P3@P4]
@@ -4304,7 +4343,7 @@ case OP_NewRowid: { /* out2 */
** for indices is OP_IdxInsert.
*/
/* Opcode: InsertInt P1 P2 P3 P4 P5
** Synopsis: intkey=P3 data=r[P2]
** Synopsis: intkey=P3 data=r[P2]
**
** This works exactly like OP_Insert except that the key is the
** integer value P3, not the value of the integer stored in register P3.
@@ -4535,7 +4574,7 @@ case OP_ResetCount: {
}
/* Opcode: SorterCompare P1 P2 P3 P4
** Synopsis: if key(P1)!=trim(r[P3],P4) goto P2
** Synopsis: if key(P1)!=trim(r[P3],P4) goto P2
**
** P1 is a sorter cursor. This instruction compares a prefix of the
** record blob in register P3 against a prefix of the entry that
@@ -5062,7 +5101,7 @@ case OP_IdxDelete: {
}
/* Opcode: Seek P1 * P3 P4 *
** Synopsis: Move P3 to P1.rowid
** Synopsis: Move P3 to P1.rowid
**
** P1 is an open index cursor and P3 is a cursor on the corresponding
** table. This opcode does a deferred seek of the P3 table cursor
@@ -5569,7 +5608,7 @@ case OP_IntegrityCk: {
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
/* Opcode: RowSetAdd P1 P2 * * *
** Synopsis: rowset(P1)=r[P2]
** Synopsis: rowset(P1)=r[P2]
**
** Insert the integer value held by register P2 into a boolean index
** held in register P1.
@@ -5589,7 +5628,7 @@ case OP_RowSetAdd: { /* in1, in2 */
}
/* Opcode: RowSetRead P1 P2 P3 * *
** Synopsis: r[P3]=rowset(P1)
** Synopsis: r[P3]=rowset(P1)
**
** Extract the smallest value from boolean index P1 and put that value into
** register P3. Or, if boolean index P1 is initially empty, leave P3
@@ -6768,7 +6807,7 @@ case OP_MaxPgcnt: { /* out2 */
/* Opcode: Init * P2 * P4 *
** Synopsis: Start at P2
** Synopsis: Start at P2
**
** Programs contain a single instance of this opcode as the very first
** opcode.
@@ -6828,8 +6867,8 @@ case OP_Init: { /* jump */
}
#endif /* SQLITE_DEBUG */
#endif /* SQLITE_OMIT_TRACE */
if( pOp->p2 ) goto jump_to_p2;
break;
assert( pOp->p2>0 );
goto jump_to_p2;
}
#ifdef SQLITE_ENABLE_CURSOR_HINTS
+34 -19
View File
@@ -1520,9 +1520,9 @@ static int stat4ValueFromExpr(
** structures intended to be compared against sample index keys stored
** in the sqlite_stat4 table.
**
** A single call to this function attempts to populates field iVal (leftmost
** is 0 etc.) of the unpacked record with a value extracted from expression
** pExpr. Extraction of values is possible if:
** A single call to this function populates zero or more fields of the
** record starting with field iVal (fields are numbered from left to
** right starting with 0). A single field is populated if:
**
** * (pExpr==0). In this case the value is assumed to be an SQL NULL,
**
@@ -1531,10 +1531,14 @@ static int stat4ValueFromExpr(
** * The sqlite3ValueFromExpr() function is able to extract a value
** from the expression (i.e. the expression is a literal value).
**
** If a value can be extracted, the affinity passed as the 5th argument
** is applied to it before it is copied into the UnpackedRecord. Output
** parameter *pbOk is set to true if a value is extracted, or false
** otherwise.
** Or, if pExpr is a TK_VECTOR, one field is populated for each of the
** vector components that match either of the two latter criteria listed
** above.
**
** Before any value is appended to the record, the affinity of the
** corresponding column within index pIdx is applied to it. Before
** this function returns, output parameter *pnExtract is set to the
** number of values appended to the record.
**
** When this function is called, *ppRec must either point to an object
** allocated by an earlier call to this function, or must be NULL. If it
@@ -1550,22 +1554,33 @@ int sqlite3Stat4ProbeSetValue(
Index *pIdx, /* Index being probed */
UnpackedRecord **ppRec, /* IN/OUT: Probe record */
Expr *pExpr, /* The expression to extract a value from */
u8 affinity, /* Affinity to use */
int nElem, /* Maximum number of values to append */
int iVal, /* Array element to populate */
int *pbOk /* OUT: True if value was extracted */
int *pnExtract /* OUT: Values appended to the record */
){
int rc;
sqlite3_value *pVal = 0;
struct ValueNewStat4Ctx alloc;
int rc = SQLITE_OK;
int nExtract = 0;
alloc.pParse = pParse;
alloc.pIdx = pIdx;
alloc.ppRec = ppRec;
alloc.iVal = iVal;
if( pExpr==0 || pExpr->op!=TK_SELECT ){
int i;
struct ValueNewStat4Ctx alloc;
rc = stat4ValueFromExpr(pParse, pExpr, affinity, &alloc, &pVal);
assert( pVal==0 || pVal->db==pParse->db );
*pbOk = (pVal!=0);
alloc.pParse = pParse;
alloc.pIdx = pIdx;
alloc.ppRec = ppRec;
for(i=0; i<nElem; i++){
sqlite3_value *pVal = 0;
Expr *pElem = (pExpr ? sqlite3VectorFieldSubexpr(pExpr, i) : 0);
u8 aff = sqlite3IndexColumnAffinity(pParse->db, pIdx, iVal+i);
alloc.iVal = iVal+i;
rc = stat4ValueFromExpr(pParse, pElem, aff, &alloc, &pVal);
if( !pVal ) break;
nExtract++;
}
}
*pnExtract = nExtract;
return rc;
}
+187 -44
View File
@@ -826,7 +826,8 @@ static sqlite3_index_info *allocateIndexInfo(
WhereClause *pWC,
Bitmask mUnusable, /* Ignore terms with these prereqs */
struct SrcList_item *pSrc,
ExprList *pOrderBy
ExprList *pOrderBy,
u16 *pmNoOmit /* Mask of terms not to omit */
){
int i, j;
int nTerm;
@@ -836,6 +837,7 @@ static sqlite3_index_info *allocateIndexInfo(
WhereTerm *pTerm;
int nOrderBy;
sqlite3_index_info *pIdxInfo;
u16 mNoOmit = 0;
/* Count the number of possible WHERE clause constraints referring
** to this virtual table */
@@ -924,6 +926,15 @@ static sqlite3_index_info *allocateIndexInfo(
assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE );
assert( WO_MATCH==SQLITE_INDEX_CONSTRAINT_MATCH );
assert( pTerm->eOperator & (WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) );
if( op & (WO_LT|WO_LE|WO_GT|WO_GE)
&& sqlite3ExprIsVector(pTerm->pExpr->pRight)
){
if( i<16 ) mNoOmit |= (1 << i);
if( op==WO_LT ) pIdxCons[j].op = WO_LE;
if( op==WO_GT ) pIdxCons[j].op = WO_GE;
}
j++;
}
for(i=0; i<nOrderBy; i++){
@@ -932,6 +943,7 @@ static sqlite3_index_info *allocateIndexInfo(
pIdxOrderBy[i].desc = pOrderBy->a[i].sortOrder;
}
*pmNoOmit = mNoOmit;
return pIdxInfo;
}
@@ -1207,7 +1219,7 @@ static LogEst whereRangeAdjust(WhereTerm *pTerm, LogEst nNew){
/*
** Return the affinity for a single column of an index.
*/
static char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCol){
char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCol){
assert( iCol>=0 && iCol<pIdx->nColumn );
if( !pIdx->zColAff ){
if( sqlite3IndexAffinityStr(db, pIdx)==0 ) return SQLITE_AFF_BLOB;
@@ -1384,7 +1396,8 @@ static int whereRangeScanEst(
if( nEq==pBuilder->nRecValid ){
UnpackedRecord *pRec = pBuilder->pRec;
tRowcnt a[2];
u8 aff;
int nBtm = pLoop->u.btree.nBtm;
int nTop = pLoop->u.btree.nTop;
/* Variable iLower will be set to the estimate of the number of rows in
** the index that are less than the lower bound of the range query. The
@@ -1414,8 +1427,6 @@ static int whereRangeScanEst(
testcase( pRec->nField!=pBuilder->nRecValid );
pRec->nField = pBuilder->nRecValid;
}
aff = sqlite3IndexColumnAffinity(pParse->db, p, nEq);
assert( nEq!=p->nKeyCol || aff==SQLITE_AFF_INTEGER );
/* Determine iLower and iUpper using ($P) only. */
if( nEq==0 ){
iLower = 0;
@@ -1434,17 +1445,20 @@ static int whereRangeScanEst(
if( p->aSortOrder[nEq] ){
/* The roles of pLower and pUpper are swapped for a DESC index */
SWAP(WhereTerm*, pLower, pUpper);
SWAP(int, nBtm, nTop);
}
/* If possible, improve on the iLower estimate using ($P:$L). */
if( pLower ){
int bOk; /* True if value is extracted from pExpr */
int n; /* Values extracted from pExpr */
Expr *pExpr = pLower->pExpr->pRight;
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk);
if( rc==SQLITE_OK && bOk ){
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, nBtm, nEq, &n);
if( rc==SQLITE_OK && n ){
tRowcnt iNew;
u16 mask = WO_GT|WO_LE;
if( sqlite3ExprVectorSize(pExpr)>n ) mask = (WO_LE|WO_LT);
iLwrIdx = whereKeyStats(pParse, p, pRec, 0, a);
iNew = a[0] + ((pLower->eOperator & (WO_GT|WO_LE)) ? a[1] : 0);
iNew = a[0] + ((pLower->eOperator & mask) ? a[1] : 0);
if( iNew>iLower ) iLower = iNew;
nOut--;
pLower = 0;
@@ -1453,13 +1467,15 @@ static int whereRangeScanEst(
/* If possible, improve on the iUpper estimate using ($P:$U). */
if( pUpper ){
int bOk; /* True if value is extracted from pExpr */
int n; /* Values extracted from pExpr */
Expr *pExpr = pUpper->pExpr->pRight;
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk);
if( rc==SQLITE_OK && bOk ){
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, nTop, nEq, &n);
if( rc==SQLITE_OK && n ){
tRowcnt iNew;
u16 mask = WO_GT|WO_LE;
if( sqlite3ExprVectorSize(pExpr)>n ) mask = (WO_LE|WO_LT);
iUprIdx = whereKeyStats(pParse, p, pRec, 1, a);
iNew = a[0] + ((pUpper->eOperator & (WO_GT|WO_LE)) ? a[1] : 0);
iNew = a[0] + ((pUpper->eOperator & mask) ? a[1] : 0);
if( iNew<iUpper ) iUpper = iNew;
nOut--;
pUpper = 0;
@@ -1549,7 +1565,6 @@ static int whereEqualScanEst(
Index *p = pBuilder->pNew->u.btree.pIndex;
int nEq = pBuilder->pNew->u.btree.nEq;
UnpackedRecord *pRec = pBuilder->pRec;
u8 aff; /* Column affinity */
int rc; /* Subfunction return code */
tRowcnt a[2]; /* Statistics */
int bOk;
@@ -1573,8 +1588,7 @@ static int whereEqualScanEst(
return SQLITE_OK;
}
aff = sqlite3IndexColumnAffinity(pParse->db, p, nEq-1);
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq-1, &bOk);
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, 1, nEq-1, &bOk);
pBuilder->pRec = pRec;
if( rc!=SQLITE_OK ) return rc;
if( bOk==0 ) return SQLITE_NOTFOUND;
@@ -1663,9 +1677,14 @@ static void whereTermPrint(WhereTerm *pTerm, int iTerm){
sqlite3_snprintf(sizeof(zLeft),zLeft,"left=%d", pTerm->leftCursor);
}
sqlite3DebugPrintf(
"TERM-%-3d %p %s %-12s prob=%-3d op=0x%03x wtFlags=0x%04x\n",
"TERM-%-3d %p %s %-12s prob=%-3d op=0x%03x wtFlags=0x%04x",
iTerm, pTerm, zType, zLeft, pTerm->truthProb,
pTerm->eOperator, pTerm->wtFlags);
if( pTerm->iField ){
sqlite3DebugPrintf(" iField=%d\n", pTerm->iField);
}else{
sqlite3DebugPrintf("\n");
}
sqlite3TreeViewExpr(0, pTerm->pExpr, 0);
}
}
@@ -2187,6 +2206,72 @@ static void whereLoopOutputAdjust(
if( pLoop->nOut > nRow-iReduce ) pLoop->nOut = nRow - iReduce;
}
/*
** Term pTerm is a vector range comparison operation. The first comparison
** in the vector can be optimized using column nEq of the index. This
** function returns the total number of vector elements that can be used
** as part of the range comparison.
**
** For example, if the query is:
**
** WHERE a = ? AND (b, c, d) > (?, ?, ?)
**
** and the index:
**
** CREATE INDEX ... ON (a, b, c, d, e)
**
** then this function would be invoked with nEq=1. The value returned in
** this case is 3.
*/
int whereRangeVectorLen(
Parse *pParse, /* Parsing context */
int iCur, /* Cursor open on pIdx */
Index *pIdx, /* The index to be used for a inequality constraint */
int nEq, /* Number of prior equality constraints on same index */
WhereTerm *pTerm /* The vector inequality constraint */
){
int nCmp = sqlite3ExprVectorSize(pTerm->pExpr->pLeft);
int i;
nCmp = MIN(nCmp, (pIdx->nColumn - nEq));
for(i=1; i<nCmp; i++){
/* Test if comparison i of pTerm is compatible with column (i+nEq)
** of the index. If not, exit the loop. */
char aff; /* Comparison affinity */
char idxaff = 0; /* Indexed columns affinity */
CollSeq *pColl; /* Comparison collation sequence */
Expr *pLhs = pTerm->pExpr->pLeft->x.pList->a[i].pExpr;
Expr *pRhs = pTerm->pExpr->pRight;
if( pRhs->flags & EP_xIsSelect ){
pRhs = pRhs->x.pSelect->pEList->a[i].pExpr;
}else{
pRhs = pRhs->x.pList->a[i].pExpr;
}
/* Check that the LHS of the comparison is a column reference to
** the right column of the right source table. And that the sort
** order of the index column is the same as the sort order of the
** leftmost index column. */
if( pLhs->op!=TK_COLUMN
|| pLhs->iTable!=iCur
|| pLhs->iColumn!=pIdx->aiColumn[i+nEq]
|| pIdx->aSortOrder[i+nEq]!=pIdx->aSortOrder[nEq]
){
break;
}
testcase( pLhs->iColumn==XN_ROWID );
aff = sqlite3CompareAffinity(pRhs, sqlite3ExprAffinity(pLhs));
idxaff = sqlite3TableColumnAffinity(pIdx->pTable, pLhs->iColumn);
if( aff!=idxaff ) break;
pColl = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs);
if( pColl==0 ) break;
if( sqlite3StrICmp(pColl->zName, pIdx->azColl[i+nEq]) ) break;
}
return i;
}
/*
** Adjust the cost C by the costMult facter T. This only occurs if
** compiled with -DSQLITE_ENABLE_COSTMULT
@@ -2225,6 +2310,8 @@ static int whereLoopAddBtreeIndex(
Bitmask saved_prereq; /* Original value of pNew->prereq */
u16 saved_nLTerm; /* Original value of pNew->nLTerm */
u16 saved_nEq; /* Original value of pNew->u.btree.nEq */
u16 saved_nBtm; /* Original value of pNew->u.btree.nBtm */
u16 saved_nTop; /* Original value of pNew->u.btree.nTop */
u16 saved_nSkip; /* Original value of pNew->nSkip */
u32 saved_wsFlags; /* Original value of pNew->wsFlags */
LogEst saved_nOut; /* Original value of pNew->nOut */
@@ -2241,6 +2328,7 @@ static int whereLoopAddBtreeIndex(
if( pNew->wsFlags & WHERE_BTM_LIMIT ){
opMask = WO_LT|WO_LE;
}else{
assert( pNew->u.btree.nBtm==0 );
opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
}
if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
@@ -2248,6 +2336,8 @@ static int whereLoopAddBtreeIndex(
assert( pNew->u.btree.nEq<pProbe->nColumn );
saved_nEq = pNew->u.btree.nEq;
saved_nBtm = pNew->u.btree.nBtm;
saved_nTop = pNew->u.btree.nTop;
saved_nSkip = pNew->nSkip;
saved_nLTerm = pNew->nLTerm;
saved_wsFlags = pNew->wsFlags;
@@ -2291,6 +2381,8 @@ static int whereLoopAddBtreeIndex(
pNew->wsFlags = saved_wsFlags;
pNew->u.btree.nEq = saved_nEq;
pNew->u.btree.nBtm = saved_nBtm;
pNew->u.btree.nTop = saved_nTop;
pNew->nLTerm = saved_nLTerm;
if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
pNew->aLTerm[pNew->nLTerm++] = pTerm;
@@ -2307,14 +2399,23 @@ static int whereLoopAddBtreeIndex(
pNew->wsFlags |= WHERE_COLUMN_IN;
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
/* "x IN (SELECT ...)": TUNING: the SELECT returns 25 rows */
int i;
nIn = 46; assert( 46==sqlite3LogEst(25) );
/* The expression may actually be of the form (x, y) IN (SELECT...).
** In this case there is a separate term for each of (x) and (y).
** However, the nIn multiplier should only be applied once, not once
** for each such term. The following loop checks that pTerm is the
** first such term in use, and sets nIn back to 0 if it is not. */
for(i=0; i<pNew->nLTerm-1; i++){
if( pNew->aLTerm[i]->pExpr==pExpr ) nIn = 0;
}
}else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
/* "x IN (value, value, ...)" */
nIn = sqlite3LogEst(pExpr->x.pList->nExpr);
assert( nIn>0 ); /* RHS always has 2 or more terms... The parser
** changes "x IN (?)" into "x=?". */
}
assert( nIn>0 ); /* RHS always has 2 or more terms... The parser
** changes "x IN (?)" into "x=?". */
}else if( eOp & (WO_EQ|WO_IS) ){
int iCol = pProbe->aiColumn[saved_nEq];
pNew->wsFlags |= WHERE_COLUMN_EQ;
@@ -2334,6 +2435,9 @@ static int whereLoopAddBtreeIndex(
testcase( eOp & WO_GT );
testcase( eOp & WO_GE );
pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT;
pNew->u.btree.nBtm = whereRangeVectorLen(
pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm
);
pBtm = pTerm;
pTop = 0;
if( pTerm->wtFlags & TERM_LIKEOPT ){
@@ -2346,12 +2450,16 @@ static int whereLoopAddBtreeIndex(
if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
pNew->aLTerm[pNew->nLTerm++] = pTop;
pNew->wsFlags |= WHERE_TOP_LIMIT;
pNew->u.btree.nTop = 1;
}
}else{
assert( eOp & (WO_LT|WO_LE) );
testcase( eOp & WO_LT );
testcase( eOp & WO_LE );
pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT;
pNew->u.btree.nTop = whereRangeVectorLen(
pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm
);
pTop = pTerm;
pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ?
pNew->aLTerm[pNew->nLTerm-2] : 0;
@@ -2451,6 +2559,8 @@ static int whereLoopAddBtreeIndex(
}
pNew->prereq = saved_prereq;
pNew->u.btree.nEq = saved_nEq;
pNew->u.btree.nBtm = saved_nBtm;
pNew->u.btree.nTop = saved_nTop;
pNew->nSkip = saved_nSkip;
pNew->wsFlags = saved_wsFlags;
pNew->nOut = saved_nOut;
@@ -2572,7 +2682,7 @@ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){
/*
** Add all WhereLoop objects for a single table of the join where the table
** is idenfied by pBuilder->pNew->iTab. That table is guaranteed to be
** is identified by pBuilder->pNew->iTab. That table is guaranteed to be
** a b-tree table, not a virtual table.
**
** The costs (WhereLoop.rRun) of the b-tree loops added by this function
@@ -2726,6 +2836,8 @@ static int whereLoopAddBtree(
}
rSize = pProbe->aiRowLogEst[0];
pNew->u.btree.nEq = 0;
pNew->u.btree.nBtm = 0;
pNew->u.btree.nTop = 0;
pNew->nSkip = 0;
pNew->nLTerm = 0;
pNew->iSortIdx = 0;
@@ -2854,6 +2966,7 @@ static int whereLoopAddVirtualOne(
Bitmask mUsable, /* Mask of usable tables */
u16 mExclude, /* Exclude terms using these operators */
sqlite3_index_info *pIdxInfo, /* Populated object for xBestIndex */
u16 mNoOmit, /* Do not omit these constraints */
int *pbIn /* OUT: True if plan uses an IN(...) op */
){
WhereClause *pWC = pBuilder->pWC;
@@ -2942,6 +3055,7 @@ static int whereLoopAddVirtualOne(
}
}
}
pNew->u.vtab.omitMask &= ~mNoOmit;
pNew->nLTerm = mxTerm+1;
assert( pNew->nLTerm<=pNew->nLSlot );
@@ -3015,6 +3129,7 @@ static int whereLoopAddVirtual(
int bIn; /* True if plan uses IN(...) operator */
WhereLoop *pNew;
Bitmask mBest; /* Tables used by best possible plan */
u16 mNoOmit;
assert( (mPrereq & mUnusable)==0 );
pWInfo = pBuilder->pWInfo;
@@ -3023,7 +3138,8 @@ static int whereLoopAddVirtual(
pNew = pBuilder->pNew;
pSrc = &pWInfo->pTabList->a[pNew->iTab];
assert( IsVirtual(pSrc->pTab) );
p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy);
p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy,
&mNoOmit);
if( p==0 ) return SQLITE_NOMEM_BKPT;
pNew->rSetup = 0;
pNew->wsFlags = WHERE_VIRTUALTABLE;
@@ -3037,7 +3153,7 @@ static int whereLoopAddVirtual(
/* First call xBestIndex() with all constraints usable. */
WHERETRACE(0x40, (" VirtualOne: all usable\n"));
rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, &bIn);
rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn);
/* If the call to xBestIndex() with all terms enabled produced a plan
** that does not require any source tables (IOW: a plan with mBest==0),
@@ -3054,7 +3170,8 @@ static int whereLoopAddVirtual(
** xBestIndex again, this time with IN(...) terms disabled. */
if( bIn ){
WHERETRACE(0x40, (" VirtualOne: all usable w/o IN\n"));
rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, WO_IN, p, &bIn);
rc = whereLoopAddVirtualOne(
pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn);
assert( bIn==0 );
mBestNoIn = pNew->prereq & ~mPrereq;
if( mBestNoIn==0 ){
@@ -3080,7 +3197,8 @@ static int whereLoopAddVirtual(
if( mNext==mBest || mNext==mBestNoIn ) continue;
WHERETRACE(0x40, (" VirtualOne: mPrev=%04llx mNext=%04llx\n",
(sqlite3_uint64)mPrev, (sqlite3_uint64)mNext));
rc = whereLoopAddVirtualOne(pBuilder, mPrereq, mNext|mPrereq, 0, p, &bIn);
rc = whereLoopAddVirtualOne(
pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn);
if( pNew->prereq==mPrereq ){
seenZero = 1;
if( bIn==0 ) seenZeroNoIN = 1;
@@ -3092,7 +3210,8 @@ static int whereLoopAddVirtual(
** usable), make a call here with all source tables disabled */
if( rc==SQLITE_OK && seenZero==0 ){
WHERETRACE(0x40, (" VirtualOne: all disabled\n"));
rc = whereLoopAddVirtualOne(pBuilder, mPrereq, mPrereq, 0, p, &bIn);
rc = whereLoopAddVirtualOne(
pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn);
if( bIn==0 ) seenZeroNoIN = 1;
}
@@ -3101,7 +3220,8 @@ static int whereLoopAddVirtual(
** operator, make a final call to obtain one here. */
if( rc==SQLITE_OK && seenZeroNoIN==0 ){
WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n"));
rc = whereLoopAddVirtualOne(pBuilder, mPrereq, mPrereq, WO_IN, p, &bIn);
rc = whereLoopAddVirtualOne(
pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn);
}
}
@@ -3437,20 +3557,42 @@ static i8 wherePathSatisfiesOrderBy(
rev = revSet = 0;
distinctColumns = 0;
for(j=0; j<nColumn; j++){
u8 bOnce; /* True to run the ORDER BY search loop */
u8 bOnce = 1; /* True to run the ORDER BY search loop */
/* Skip over == and IS and ISNULL 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) & eqOpMask)!=0
){
if( i & WO_ISNULL ){
testcase( isOrderDistinct );
isOrderDistinct = 0;
assert( j>=pLoop->u.btree.nEq
|| (pLoop->aLTerm[j]==0)==(j<pLoop->nSkip)
);
if( j<pLoop->u.btree.nEq && j>=pLoop->nSkip ){
u16 eOp = pLoop->aLTerm[j]->eOperator;
/* Skip over == and IS and ISNULL terms. (Also skip IN terms when
** doing WHERE_ORDERBY_LIMIT processing).
**
** If the current term is a column of an ((?,?) IN (SELECT...))
** expression for which the SELECT returns more than one column,
** check that it is the only column used by this loop. Otherwise,
** if it is one of two or more, none of the columns can be
** considered to match an ORDER BY term. */
if( (eOp & eqOpMask)!=0 ){
if( eOp & WO_ISNULL ){
testcase( isOrderDistinct );
isOrderDistinct = 0;
}
continue;
}else if( ALWAYS(eOp & WO_IN) ){
/* ALWAYS() justification: eOp is an equality operator due to the
** j<pLoop->u.btree.nEq constraint above. Any equality other
** than WO_IN is captured by the previous "if". So this one
** always has to be WO_IN. */
Expr *pX = pLoop->aLTerm[j]->pExpr;
for(i=j+1; i<pLoop->u.btree.nEq; i++){
if( pLoop->aLTerm[i]->pExpr==pX ){
assert( (pLoop->aLTerm[i]->eOperator & WO_IN) );
bOnce = 0;
break;
}
}
}
continue;
}
/* Get the column number in the table (iColumn) and sort order
@@ -3479,7 +3621,6 @@ static i8 wherePathSatisfiesOrderBy(
/* Find the ORDER BY term that corresponds to the j-th column
** of the index and mark that ORDER BY term off
*/
bOnce = 1;
isMatch = 0;
for(i=0; bOnce && i<nOrderBy; i++){
if( MASKBIT(i) & obSat ) continue;
@@ -4673,10 +4814,12 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
sqlite3VdbeResolveLabel(v, pLevel->addrNxt);
for(j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--){
sqlite3VdbeJumpHere(v, pIn->addrInTop+1);
sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop);
VdbeCoverage(v);
VdbeCoverageIf(v, pIn->eEndLoopOp==OP_PrevIfOpen);
VdbeCoverageIf(v, pIn->eEndLoopOp==OP_NextIfOpen);
if( pIn->eEndLoopOp!=OP_Noop ){
sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop);
VdbeCoverage(v);
VdbeCoverageIf(v, pIn->eEndLoopOp==OP_PrevIfOpen);
VdbeCoverageIf(v, pIn->eEndLoopOp==OP_NextIfOpen);
}
sqlite3VdbeJumpHere(v, pIn->addrInTop-1);
}
}
+3
View File
@@ -122,6 +122,8 @@ struct WhereLoop {
union {
struct { /* Information for internal btree tables */
u16 nEq; /* Number of equality constraints */
u16 nBtm; /* Size of BTM vector */
u16 nTop; /* Size of TOP vector */
Index *pIndex; /* Index used, or NULL */
} btree;
struct { /* Information for virtual tables */
@@ -246,6 +248,7 @@ struct WhereTerm {
Expr *pExpr; /* Pointer to the subexpression that is this term */
int iParent; /* Disable pWC->a[iParent] when this term disabled */
int leftCursor; /* Cursor number of X in "X <op> <expr>" */
int iField; /* Field in (?,?,?) IN (SELECT...) vector */
union {
int leftColumn; /* Column number of X in "X <op> <expr>" */
WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */
+244 -73
View File
@@ -21,6 +21,17 @@
#include "whereInt.h"
#ifndef SQLITE_OMIT_EXPLAIN
/*
** Return the name of the i-th column of the pIdx index.
*/
static const char *explainIndexColumnName(Index *pIdx, int i){
i = pIdx->aiColumn[i];
if( i==XN_EXPR ) return "<expr>";
if( i==XN_ROWID ) return "rowid";
return pIdx->pTable->aCol[i].zName;
}
/*
** This routine is a helper for explainIndexRange() below
**
@@ -31,24 +42,32 @@
*/
static void explainAppendTerm(
StrAccum *pStr, /* The text expression being built */
int iTerm, /* Index of this term. First is zero */
const char *zColumn, /* Name of the column */
Index *pIdx, /* Index to read column names from */
int nTerm, /* Number of terms */
int iTerm, /* Zero-based index of first term. */
int bAnd, /* Non-zero to append " AND " */
const char *zOp /* Name of the operator */
){
if( iTerm ) sqlite3StrAccumAppend(pStr, " AND ", 5);
sqlite3StrAccumAppendAll(pStr, zColumn);
sqlite3StrAccumAppend(pStr, zOp, 1);
sqlite3StrAccumAppend(pStr, "?", 1);
}
int i;
/*
** Return the name of the i-th column of the pIdx index.
*/
static const char *explainIndexColumnName(Index *pIdx, int i){
i = pIdx->aiColumn[i];
if( i==XN_EXPR ) return "<expr>";
if( i==XN_ROWID ) return "rowid";
return pIdx->pTable->aCol[i].zName;
assert( nTerm>=1 );
if( bAnd ) sqlite3StrAccumAppend(pStr, " AND ", 5);
if( nTerm>1 ) sqlite3StrAccumAppend(pStr, "(", 1);
for(i=0; i<nTerm; i++){
if( i ) sqlite3StrAccumAppend(pStr, ",", 1);
sqlite3StrAccumAppendAll(pStr, explainIndexColumnName(pIdx, iTerm+i));
}
if( nTerm>1 ) sqlite3StrAccumAppend(pStr, ")", 1);
sqlite3StrAccumAppend(pStr, zOp, 1);
if( nTerm>1 ) sqlite3StrAccumAppend(pStr, "(", 1);
for(i=0; i<nTerm; i++){
if( i ) sqlite3StrAccumAppend(pStr, ",", 1);
sqlite3StrAccumAppend(pStr, "?", 1);
}
if( nTerm>1 ) sqlite3StrAccumAppend(pStr, ")", 1);
}
/*
@@ -81,12 +100,11 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
j = i;
if( pLoop->wsFlags&WHERE_BTM_LIMIT ){
const char *z = explainIndexColumnName(pIndex, i);
explainAppendTerm(pStr, i++, z, ">");
explainAppendTerm(pStr, pIndex, pLoop->u.btree.nBtm, j, i, ">");
i = 1;
}
if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
const char *z = explainIndexColumnName(pIndex, j);
explainAppendTerm(pStr, i, z, "<");
explainAppendTerm(pStr, pIndex, pLoop->u.btree.nTop, j, i, "<");
}
sqlite3StrAccumAppend(pStr, ")", 1);
}
@@ -276,7 +294,7 @@ void sqlite3WhereAddScanStatus(
*/
static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
int nLoop = 0;
while( pTerm
while( ALWAYS(pTerm!=0)
&& (pTerm->wtFlags & TERM_CODED)==0
&& (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin))
&& (pLevel->notReady & pTerm->prereqAll)==0
@@ -332,6 +350,32 @@ static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){
}
}
/*
** Expression pRight, which is the RHS of a comparison operation, is
** either a vector of n elements or, if n==1, a scalar expression.
** Before the comparison operation, affinity zAff is to be applied
** to the pRight values. This function modifies characters within the
** affinity string to SQLITE_AFF_BLOB if either:
**
** * the comparison will be performed with no affinity, or
** * the affinity change in zAff is guaranteed not to change the value.
*/
static void updateRangeAffinityStr(
Parse *pParse, /* Parse context */
Expr *pRight, /* RHS of comparison */
int n, /* Number of vector elements in comparison */
char *zAff /* Affinity string to modify */
){
int i;
for(i=0; i<n; i++){
Expr *p = sqlite3VectorFieldSubexpr(pRight, i);
if( sqlite3CompareAffinity(p, zAff[i])==SQLITE_AFF_BLOB
|| sqlite3ExprNeedsNoAffinityChange(p, zAff[i])
){
zAff[i] = SQLITE_AFF_BLOB;
}
}
}
/*
** Generate code for a single equality term of the WHERE clause. An equality
@@ -340,8 +384,8 @@ static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){
**
** The current value for the constraint is left in register iReg.
**
** For a constraint of the form X=expr, the expression is evaluated and its
** result is left on the stack. For constraints of the form X IN (...)
** For a constraint of the form X=expr, the expression is evaluated in
** straight-line code. For constraints of the form X IN (...)
** this routine sets up a loop that will iterate over all values of X.
*/
static int codeEqualityTerm(
@@ -356,6 +400,7 @@ static int codeEqualityTerm(
Vdbe *v = pParse->pVdbe;
int iReg; /* Register holding results */
assert( pLevel->pWLoop->aLTerm[iEq]==pTerm );
assert( iTarget>0 );
if( pX->op==TK_EQ || pX->op==TK_IS ){
iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget);
@@ -364,10 +409,13 @@ static int codeEqualityTerm(
sqlite3VdbeAddOp2(v, OP_Null, 0, iReg);
#ifndef SQLITE_OMIT_SUBQUERY
}else{
int eType;
int eType = IN_INDEX_NOOP;
int iTab;
struct InLoop *pIn;
WhereLoop *pLoop = pLevel->pWLoop;
int i;
int nEq = 0;
int *aiMap = 0;
if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0
&& pLoop->u.btree.pIndex!=0
@@ -379,7 +427,52 @@ static int codeEqualityTerm(
}
assert( pX->op==TK_IN );
iReg = iTarget;
eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0);
for(i=0; i<iEq; i++){
if( pLoop->aLTerm[i] && pLoop->aLTerm[i]->pExpr==pX ){
disableTerm(pLevel, pTerm);
return iTarget;
}
}
for(i=iEq;i<pLoop->nLTerm; i++){
if( ALWAYS(pLoop->aLTerm[i]) && pLoop->aLTerm[i]->pExpr==pX ) nEq++;
}
if( nEq>1 ){
aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int) * nEq);
if( !aiMap ) return 0;
}
if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){
eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0);
}else{
sqlite3 *db = pParse->db;
ExprList *pOrigRhs = pX->x.pSelect->pEList;
ExprList *pOrigLhs = pX->pLeft->x.pList;
ExprList *pRhs = 0; /* New Select.pEList for RHS */
ExprList *pLhs = 0; /* New pX->pLeft vector */
for(i=iEq;i<pLoop->nLTerm; i++){
if( pLoop->aLTerm[i]->pExpr==pX ){
int iField = pLoop->aLTerm[i]->iField - 1;
Expr *pNewRhs = sqlite3ExprDup(db, pOrigRhs->a[iField].pExpr, 0);
Expr *pNewLhs = sqlite3ExprDup(db, pOrigLhs->a[iField].pExpr, 0);
pRhs = sqlite3ExprListAppend(pParse, pRhs, pNewRhs);
pLhs = sqlite3ExprListAppend(pParse, pLhs, pNewLhs);
}
}
if( !db->mallocFailed ){
pX->x.pSelect->pEList = pRhs;
pX->pLeft->x.pList = pLhs;
eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap);
pX->x.pSelect->pEList = pOrigRhs;
pX->pLeft->x.pList = pOrigLhs;
}
sqlite3ExprListDelete(pParse->db, pLhs);
sqlite3ExprListDelete(pParse->db, pRhs);
}
if( eType==IN_INDEX_INDEX_DESC ){
testcase( bRev );
bRev = !bRev;
@@ -389,28 +482,46 @@ static int codeEqualityTerm(
VdbeCoverageIf(v, bRev);
VdbeCoverageIf(v, !bRev);
assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 );
pLoop->wsFlags |= WHERE_IN_ABLE;
if( pLevel->u.in.nIn==0 ){
pLevel->addrNxt = sqlite3VdbeMakeLabel(v);
}
pLevel->u.in.nIn++;
i = pLevel->u.in.nIn;
pLevel->u.in.nIn += nEq;
pLevel->u.in.aInLoop =
sqlite3DbReallocOrFree(pParse->db, pLevel->u.in.aInLoop,
sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn);
pIn = pLevel->u.in.aInLoop;
if( pIn ){
pIn += pLevel->u.in.nIn - 1;
pIn->iCur = iTab;
if( eType==IN_INDEX_ROWID ){
pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iReg);
}else{
pIn->addrInTop = sqlite3VdbeAddOp3(v, OP_Column, iTab, 0, iReg);
int iMap = 0; /* Index in aiMap[] */
pIn += i;
for(i=iEq;i<pLoop->nLTerm; i++){
int iOut = iReg;
if( pLoop->aLTerm[i]->pExpr==pX ){
if( eType==IN_INDEX_ROWID ){
assert( nEq==1 && i==iEq );
pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iReg);
}else{
int iCol = aiMap ? aiMap[iMap++] : 0;
iOut = iReg + i - iEq;
pIn->addrInTop = sqlite3VdbeAddOp3(v,OP_Column,iTab, iCol, iOut);
}
sqlite3VdbeAddOp1(v, OP_IsNull, iOut); VdbeCoverage(v);
if( i==iEq ){
pIn->iCur = iTab;
pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen;
}else{
pIn->eEndLoopOp = OP_Noop;
}
pIn++;
}
}
pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen;
sqlite3VdbeAddOp1(v, OP_IsNull, iReg); VdbeCoverage(v);
}else{
pLevel->u.in.nIn = 0;
}
sqlite3DbFree(pParse->db, aiMap);
#endif
}
disableTerm(pLevel, pTerm);
@@ -538,13 +649,21 @@ static int codeAllEqualityTerms(
}
testcase( pTerm->eOperator & WO_ISNULL );
testcase( pTerm->eOperator & WO_IN );
if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){
Expr *pRight = pTerm->pExpr->pRight;
if( (pTerm->wtFlags & TERM_IS)==0 && sqlite3ExprCanBeNull(pRight) ){
sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk);
VdbeCoverage(v);
if( (pTerm->eOperator & WO_ISNULL)==0 ){
Expr *pRight = 0;
if( pTerm->eOperator & WO_IN ){
if( pTerm->pExpr->flags & EP_xIsSelect ){
int iField = pTerm->iField ? pTerm->iField-1 : 0;
pRight = pTerm->pExpr->x.pSelect->pEList->a[iField].pExpr;
}
}else{
pRight = pTerm->pExpr->pRight;
if( (pTerm->wtFlags & TERM_IS)==0 && sqlite3ExprCanBeNull(pRight) ){
sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk);
VdbeCoverage(v);
}
}
if( zAff ){
if( pRight && zAff ){
if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_BLOB ){
zAff[j] = SQLITE_AFF_BLOB;
}
@@ -861,6 +980,39 @@ static void codeDeferredSeek(
}
}
/*
** If the expression passed as the second argument is a vector, generate
** code to write the first nReg elements of the vector into an array
** of registers starting with iReg.
**
** If the expression is not a vector, then nReg must be passed 1. In
** this case, generate code to evaluate the expression and leave the
** result in register iReg.
*/
static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){
assert( nReg>0 );
if( sqlite3ExprIsVector(p) ){
#ifndef SQLITE_OMIT_SUBQUERY
if( (p->flags & EP_xIsSelect) ){
Vdbe *v = pParse->pVdbe;
int iSelect = sqlite3CodeSubselect(pParse, p, 0, 0);
sqlite3VdbeAddOp3(v, OP_Copy, iSelect, iReg, nReg-1);
}else
#endif
{
int i;
ExprList *pList = p->x.pList;
assert( nReg<=pList->nExpr );
for(i=0; i<nReg; i++){
sqlite3ExprCode(pParse, pList->a[i].pExpr, iReg+i);
}
}
}else{
assert( nReg==1 );
sqlite3ExprCode(pParse, p, iReg);
}
}
/*
** Generate code for the start of the iLevel-th loop in the WHERE clause
** implementation described by pWInfo.
@@ -956,7 +1108,8 @@ Bitmask sqlite3WhereCodeOneLoopStart(
codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget);
addrNotFound = pLevel->addrNxt;
}else{
sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget);
Expr *pRight = pTerm->pExpr->pRight;
codeExprOrVector(pParse, pRight, iTarget, 1);
}
}
sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg);
@@ -1070,6 +1223,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
if( pStart ){
Expr *pX; /* The expression that defines the start bound */
int r1, rTemp; /* Registers for holding the start boundary */
int op; /* Cursor seek operation */
/* The following constant maps TK_xx codes into corresponding
** seek opcodes. It depends on a particular ordering of TK_xx
@@ -1089,8 +1243,16 @@ Bitmask sqlite3WhereCodeOneLoopStart(
pX = pStart->pExpr;
assert( pX!=0 );
testcase( pStart->leftCursor!=iCur ); /* transitive constraints */
r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp);
sqlite3VdbeAddOp3(v, aMoveOp[pX->op-TK_GT], iCur, addrBrk, r1);
if( sqlite3ExprIsVector(pX->pRight) ){
r1 = rTemp = sqlite3GetTempReg(pParse);
codeExprOrVector(pParse, pX->pRight, r1, 1);
op = aMoveOp[(pX->op - TK_GT) | 0x0001];
}else{
r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp);
disableTerm(pLevel, pStart);
op = aMoveOp[(pX->op - TK_GT)];
}
sqlite3VdbeAddOp3(v, op, iCur, addrBrk, r1);
VdbeComment((v, "pk"));
VdbeCoverageIf(v, pX->op==TK_GT);
VdbeCoverageIf(v, pX->op==TK_LE);
@@ -1098,7 +1260,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
VdbeCoverageIf(v, pX->op==TK_GE);
sqlite3ExprCacheAffinityChange(pParse, r1, 1);
sqlite3ReleaseTempReg(pParse, rTemp);
disableTerm(pLevel, pStart);
}else{
sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrBrk);
VdbeCoverageIf(v, bRev==0);
@@ -1112,13 +1273,17 @@ Bitmask sqlite3WhereCodeOneLoopStart(
testcase( pEnd->leftCursor!=iCur ); /* Transitive constraints */
testcase( pEnd->wtFlags & TERM_VIRTUAL );
memEndValue = ++pParse->nMem;
sqlite3ExprCode(pParse, pX->pRight, memEndValue);
if( pX->op==TK_LT || pX->op==TK_GT ){
codeExprOrVector(pParse, pX->pRight, memEndValue, 1);
if( 0==sqlite3ExprIsVector(pX->pRight)
&& (pX->op==TK_LT || pX->op==TK_GT)
){
testOp = bRev ? OP_Le : OP_Ge;
}else{
testOp = bRev ? OP_Lt : OP_Gt;
}
disableTerm(pLevel, pEnd);
if( 0==sqlite3ExprIsVector(pX->pRight) ){
disableTerm(pLevel, pEnd);
}
}
start = sqlite3VdbeCurrentAddr(v);
pLevel->op = bRev ? OP_Prev : OP_Next;
@@ -1185,6 +1350,8 @@ Bitmask sqlite3WhereCodeOneLoopStart(
OP_IdxLT, /* 3: (end_constraints && bRev && endEq) */
};
u16 nEq = pLoop->u.btree.nEq; /* Number of == or IN terms */
u16 nBtm = pLoop->u.btree.nBtm; /* Length of BTM vector */
u16 nTop = pLoop->u.btree.nTop; /* Length of TOP vector */
int regBase; /* Base register holding constraint values */
WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */
WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */
@@ -1197,7 +1364,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
int nExtraReg = 0; /* Number of extra registers needed */
int op; /* Instruction opcode */
char *zStartAff; /* Affinity for start of range constraint */
char cEndAff = 0; /* Affinity for end of range constraint */
char *zEndAff = 0; /* Affinity for end of range constraint */
u8 bSeekPastNull = 0; /* True to seek past initial nulls */
u8 bStopAtNull = 0; /* Add condition to terminate at NULLs */
@@ -1231,14 +1398,14 @@ Bitmask sqlite3WhereCodeOneLoopStart(
j = nEq;
if( pLoop->wsFlags & WHERE_BTM_LIMIT ){
pRangeStart = pLoop->aLTerm[j++];
nExtraReg = 1;
nExtraReg = MAX(nExtraReg, pLoop->u.btree.nBtm);
/* Like optimization range constraints always occur in pairs */
assert( (pRangeStart->wtFlags & TERM_LIKEOPT)==0 ||
(pLoop->wsFlags & WHERE_TOP_LIMIT)!=0 );
}
if( pLoop->wsFlags & WHERE_TOP_LIMIT ){
pRangeEnd = pLoop->aLTerm[j++];
nExtraReg = 1;
nExtraReg = MAX(nExtraReg, pLoop->u.btree.nTop);
#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
if( (pRangeEnd->wtFlags & TERM_LIKEOPT)!=0 ){
assert( pRangeStart!=0 ); /* LIKE opt constraints */
@@ -1274,6 +1441,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
){
SWAP(WhereTerm *, pRangeEnd, pRangeStart);
SWAP(u8, bSeekPastNull, bStopAtNull);
SWAP(u8, nBtm, nTop);
}
/* Generate code to evaluate all constraint terms using == or IN
@@ -1283,7 +1451,9 @@ Bitmask sqlite3WhereCodeOneLoopStart(
codeCursorHint(pTabItem, pWInfo, pLevel, pRangeEnd);
regBase = codeAllEqualityTerms(pParse,pLevel,bRev,nExtraReg,&zStartAff);
assert( zStartAff==0 || sqlite3Strlen30(zStartAff)>=nEq );
if( zStartAff ) cEndAff = zStartAff[nEq];
if( zStartAff && nTop ){
zEndAff = sqlite3DbStrDup(db, &zStartAff[nEq]);
}
addrNxt = pLevel->addrNxt;
testcase( pRangeStart && (pRangeStart->eOperator & WO_LE)!=0 );
@@ -1298,7 +1468,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
nConstraint = nEq;
if( pRangeStart ){
Expr *pRight = pRangeStart->pExpr->pRight;
sqlite3ExprCode(pParse, pRight, regBase+nEq);
codeExprOrVector(pParse, pRight, regBase+nEq, nBtm);
whereLikeOptimizationStringFixup(v, pLevel, pRangeStart);
if( (pRangeStart->wtFlags & TERM_VNULL)==0
&& sqlite3ExprCanBeNull(pRight)
@@ -1307,18 +1477,15 @@ Bitmask sqlite3WhereCodeOneLoopStart(
VdbeCoverage(v);
}
if( zStartAff ){
if( sqlite3CompareAffinity(pRight, zStartAff[nEq])==SQLITE_AFF_BLOB){
/* Since the comparison is to be performed with no conversions
** applied to the operands, set the affinity to apply to pRight to
** SQLITE_AFF_BLOB. */
zStartAff[nEq] = SQLITE_AFF_BLOB;
}
if( sqlite3ExprNeedsNoAffinityChange(pRight, zStartAff[nEq]) ){
zStartAff[nEq] = SQLITE_AFF_BLOB;
}
updateRangeAffinityStr(pParse, pRight, nBtm, &zStartAff[nEq]);
}
nConstraint++;
nConstraint += nBtm;
testcase( pRangeStart->wtFlags & TERM_VIRTUAL );
if( sqlite3ExprIsVector(pRight)==0 ){
disableTerm(pLevel, pRangeStart);
}else{
startEq = 1;
}
bSeekPastNull = 0;
}else if( bSeekPastNull ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
@@ -1351,7 +1518,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
if( pRangeEnd ){
Expr *pRight = pRangeEnd->pExpr->pRight;
sqlite3ExprCacheRemove(pParse, regBase+nEq, 1);
sqlite3ExprCode(pParse, pRight, regBase+nEq);
codeExprOrVector(pParse, pRight, regBase+nEq, nTop);
whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd);
if( (pRangeEnd->wtFlags & TERM_VNULL)==0
&& sqlite3ExprCanBeNull(pRight)
@@ -1359,19 +1526,27 @@ Bitmask sqlite3WhereCodeOneLoopStart(
sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt);
VdbeCoverage(v);
}
if( sqlite3CompareAffinity(pRight, cEndAff)!=SQLITE_AFF_BLOB
&& !sqlite3ExprNeedsNoAffinityChange(pRight, cEndAff)
){
codeApplyAffinity(pParse, regBase+nEq, 1, &cEndAff);
if( zEndAff ){
updateRangeAffinityStr(pParse, pRight, nTop, zEndAff);
codeApplyAffinity(pParse, regBase+nEq, nTop, zEndAff);
}else{
assert( pParse->db->mallocFailed );
}
nConstraint++;
nConstraint += nTop;
testcase( pRangeEnd->wtFlags & TERM_VIRTUAL );
if( sqlite3ExprIsVector(pRight)==0 ){
disableTerm(pLevel, pRangeEnd);
}else{
endEq = 1;
}
}else if( bStopAtNull ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
endEq = 0;
nConstraint++;
}
sqlite3DbFree(db, zStartAff);
sqlite3DbFree(db, zEndAff);
/* Top of the loop body */
pLevel->p2 = sqlite3VdbeCurrentAddr(v);
@@ -1387,8 +1562,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
}
/* Seek the table cursor, if required */
disableTerm(pLevel, pRangeStart);
disableTerm(pLevel, pRangeEnd);
if( omitTable ){
/* pIdx is a covering index. No need to access the main table. */
}else if( HasRowid(pIdx->pTable) ){
@@ -1412,9 +1585,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
iRowidReg, pPk->nKeyCol); VdbeCoverage(v);
}
/* Record the instruction used to terminate the loop. Disable
** WHERE clause terms made redundant by the index range scan.
*/
/* Record the instruction used to terminate the loop. */
if( pLoop->wsFlags & WHERE_ONEROW ){
pLevel->op = OP_Noop;
}else if( bRev ){
@@ -1491,7 +1662,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
u16 wctrlFlags; /* Flags for sub-WHERE clause */
Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
Table *pTab = pTabItem->pTab;
pTerm = pLoop->aLTerm[0];
assert( pTerm!=0 );
assert( pTerm->eOperator & WO_OR );
+77 -6
View File
@@ -95,7 +95,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){
/*
** Return TRUE if the given operator is one of the operators that is
** allowed for an indexable WHERE clause term. The allowed operators are
** "=", "<", ">", "<=", ">=", "IN", and "IS NULL"
** "=", "<", ">", "<=", ">=", "IN", "IS", and "IS NULL"
*/
static int allowedOp(int op){
assert( TK_GT>TK_EQ && TK_GT<TK_GE );
@@ -823,7 +823,8 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){
** in any index. Return TRUE (1) if pExpr is an indexed term and return
** FALSE (0) if not. If TRUE is returned, also set *piCur to the cursor
** number of the table that is indexed and *piColumn to the column number
** of the column that is indexed, or -2 if an expression is being indexed.
** of the column that is indexed, or XN_EXPR (-2) if an expression is being
** indexed.
**
** If pExpr is a TK_COLUMN column reference, then this routine always returns
** true even if that particular column is not indexed, because the column
@@ -831,6 +832,7 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){
*/
static int exprMightBeIndexed(
SrcList *pFrom, /* The FROM clause */
int op, /* The specific comparison operator */
Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */
Expr *pExpr, /* An operand of a comparison operator */
int *piCur, /* Write the referenced table cursor number here */
@@ -839,6 +841,17 @@ static int exprMightBeIndexed(
Index *pIdx;
int i;
int iCur;
/* If this expression is a vector to the left or right of a
** inequality constraint (>, <, >= or <=), perform the processing
** on the first element of the vector. */
assert( TK_GT+1==TK_LE && TK_GT+2==TK_LT && TK_GT+3==TK_GE );
assert( TK_IS<TK_GE && TK_ISNULL<TK_GE && TK_IN<TK_GE );
assert( op<=TK_GE );
if( pExpr->op==TK_VECTOR && (op>=TK_GT && ALWAYS(op<=TK_GE)) ){
pExpr = pExpr->x.pList->a[0].pExpr;
}
if( pExpr->op==TK_COLUMN ){
*piCur = pExpr->iTable;
*piColumn = pExpr->iColumn;
@@ -851,10 +864,10 @@ static int exprMightBeIndexed(
for(pIdx=pFrom->a[i].pTab->pIndex; pIdx; pIdx=pIdx->pNext){
if( pIdx->aColExpr==0 ) continue;
for(i=0; i<pIdx->nKeyCol; i++){
if( pIdx->aiColumn[i]!=(-2) ) continue;
if( pIdx->aiColumn[i]!=XN_EXPR ) continue;
if( sqlite3ExprCompare(pExpr, pIdx->aColExpr->a[i].pExpr, iCur)==0 ){
*piCur = iCur;
*piColumn = -2;
*piColumn = XN_EXPR;
return 1;
}
}
@@ -911,6 +924,7 @@ static void exprAnalyze(
op = pExpr->op;
if( op==TK_IN ){
assert( pExpr->pRight==0 );
if( sqlite3ExprCheckIN(pParse, pExpr) ) return;
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
pTerm->prereqRight = exprSelectUsage(pMaskSet, pExpr->x.pSelect);
}else{
@@ -937,18 +951,26 @@ static void exprAnalyze(
Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft);
Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight);
u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV;
if( exprMightBeIndexed(pSrc, prereqLeft, pLeft, &iCur, &iColumn) ){
if( pTerm->iField>0 ){
assert( op==TK_IN );
assert( pLeft->op==TK_VECTOR );
pLeft = pLeft->x.pList->a[pTerm->iField-1].pExpr;
}
if( exprMightBeIndexed(pSrc, op, prereqLeft, pLeft, &iCur, &iColumn) ){
pTerm->leftCursor = iCur;
pTerm->u.leftColumn = iColumn;
pTerm->eOperator = operatorMask(op) & opMask;
}
if( op==TK_IS ) pTerm->wtFlags |= TERM_IS;
if( pRight
&& exprMightBeIndexed(pSrc, pTerm->prereqRight, pRight, &iCur, &iColumn)
&& exprMightBeIndexed(pSrc, op, pTerm->prereqRight, pRight, &iCur,&iColumn)
){
WhereTerm *pNew;
Expr *pDup;
u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */
assert( pTerm->iField==0 );
if( pTerm->leftCursor>=0 ){
int idxNew;
pDup = sqlite3ExprDup(db, pExpr, 0);
@@ -1152,6 +1174,55 @@ static void exprAnalyze(
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
/* If there is a vector == or IS term - e.g. "(a, b) == (?, ?)" - create
** new terms for each component comparison - "a = ?" and "b = ?". The
** new terms completely replace the original vector comparison, which is
** no longer used.
**
** This is only required if at least one side of the comparison operation
** is not a sub-select. */
if( pWC->op==TK_AND
&& (pExpr->op==TK_EQ || pExpr->op==TK_IS)
&& sqlite3ExprIsVector(pExpr->pLeft)
&& ( (pExpr->pLeft->flags & EP_xIsSelect)==0
|| (pExpr->pRight->flags & EP_xIsSelect)==0
)){
int nLeft = sqlite3ExprVectorSize(pExpr->pLeft);
int i;
assert( nLeft==sqlite3ExprVectorSize(pExpr->pRight) );
for(i=0; i<nLeft; i++){
int idxNew;
Expr *pNew;
Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i);
Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i);
pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0);
idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC);
exprAnalyze(pSrc, pWC, idxNew);
}
pTerm = &pWC->a[idxTerm];
pTerm->wtFlags = TERM_CODED|TERM_VIRTUAL; /* Disable the original */
pTerm->eOperator = 0;
}
/* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create
** a virtual term for each vector component. The expression object
** used by each such virtual term is pExpr (the full vector IN(...)
** expression). The WhereTerm.iField variable identifies the index within
** the vector on the LHS that the virtual term represents. */
if( pWC->op==TK_AND && pExpr->op==TK_IN && pTerm->iField==0
&& pExpr->pLeft->op==TK_VECTOR
){
int i;
for(i=0; i<sqlite3ExprVectorSize(pExpr->pLeft); i++){
int idxNew;
idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL);
pWC->a[idxNew].iField = i+1;
exprAnalyze(pSrc, pWC, idxNew);
markTermAsChild(pWC, idxNew, idxTerm);
}
}
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/* When sqlite_stat3 histogram data is available an operator of the
** form "x IS NOT NULL" can sometimes be evaluated more efficiently
+5 -2
View File
@@ -847,6 +847,9 @@ foreach {tn x expr res nEval} {
3 5 "x() >= 5 AND x() <= 5" 1 2
4 5 "x() BETWEEN 5 AND 5" 1 1
5 9 "(x(),8) >= (9,7) AND (x(),8)<=(9,10)" 1 2
6 9 "(x(),8) BETWEEN (9,7) AND (9,10)" 1 1
} {
do_test e_expr-13.1.$tn {
set ::xcount 0
@@ -1809,7 +1812,7 @@ do_expr_test e_expr-35.1.6 {
# The following block tests that errors are returned in a bunch of cases
# where a subquery returns more than one column.
#
set M {only a single result allowed for a SELECT that is part of an expression}
set M {/1 {sub-select returns [23] columns - expected 1}/}
foreach {tn sql} {
1 { SELECT (SELECT * FROM t2 UNION SELECT a+1, b+1 FROM t2) }
2 { SELECT (SELECT * FROM t2 UNION SELECT a+1, b+1 FROM t2 ORDER BY 1) }
@@ -1818,7 +1821,7 @@ foreach {tn sql} {
5 { SELECT (SELECT * FROM t2) }
6 { SELECT (SELECT * FROM (SELECT 1, 2, 3)) }
} {
do_catchsql_test e_expr-35.2.$tn $sql [list 1 $M]
do_catchsql_test e_expr-35.2.$tn $sql $M
}
# EVIDENCE-OF: R-35764-28041 The result of the expression is the value
+8 -7
View File
@@ -314,7 +314,7 @@ do_test in-9.4 {
catchsql {
SELECT b FROM t1 WHERE a NOT IN tb;
}
} {1 {only a single result allowed for a SELECT that is part of an expression}}
} {1 {sub-select returns 2 columns - expected 1}}
# IN clauses in CHECK constraints. Ticket #1645
#
@@ -391,28 +391,28 @@ do_test in-12.2 {
SELECT a, b FROM t3 UNION ALL SELECT a, b FROM t2
);
}
} {1 {only a single result allowed for a SELECT that is part of an expression}}
} {1 {sub-select returns 2 columns - expected 1}}
do_test in-12.3 {
catchsql {
SELECT * FROM t2 WHERE a IN (
SELECT a, b FROM t3 UNION SELECT a, b FROM t2
);
}
} {1 {only a single result allowed for a SELECT that is part of an expression}}
} {1 {sub-select returns 2 columns - expected 1}}
do_test in-12.4 {
catchsql {
SELECT * FROM t2 WHERE a IN (
SELECT a, b FROM t3 EXCEPT SELECT a, b FROM t2
);
}
} {1 {only a single result allowed for a SELECT that is part of an expression}}
} {1 {sub-select returns 2 columns - expected 1}}
do_test in-12.5 {
catchsql {
SELECT * FROM t2 WHERE a IN (
SELECT a, b FROM t3 INTERSECT SELECT a, b FROM t2
);
}
} {1 {only a single result allowed for a SELECT that is part of an expression}}
} {1 {sub-select returns 2 columns - expected 1}}
do_test in-12.6 {
catchsql {
SELECT * FROM t2 WHERE a IN (
@@ -478,7 +478,7 @@ do_test in-12.14 {
SELECT a, b FROM t3 UNION ALL SELECT a, b FROM t2
);
}
} {1 {only a single result allowed for a SELECT that is part of an expression}}
} {1 {sub-select returns 2 columns - expected 1}}
do_test in-12.15 {
catchsql {
SELECT * FROM t2 WHERE a IN (
@@ -629,11 +629,12 @@ do_test in-13.14 {
}
} {}
breakpoint
do_test in-13.15 {
catchsql {
SELECT 0 WHERE (SELECT 0,0) OR (0 IN (1,2));
}
} {1 {only a single result allowed for a SELECT that is part of an expression}}
} {1 {sub-select returns 2 columns - expected 1}}
do_test in-13.X {
+259
View File
@@ -0,0 +1,259 @@
# 2016 June 17
#
# 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 SELECT statement.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix rowvalue
do_execsql_test 0.0 {
CREATE TABLE one(o);
INSERT INTO one VALUES(1);
}
foreach {tn v1 v2 eq ne is isnot} {
1 "1, 2, 3" "1, 2, 3" 1 0 1 0
2 "1, 0, 3" "1, 2, 3" 0 1 0 1
3 "1, 2, NULL" "1, 2, 3" {} {} 0 1
4 "1, 2, NULL" "1, 2, NULL" {} {} 1 0
5 "NULL, NULL, NULL" "NULL, NULL, NULL" {} {} 1 0
6 "1, NULL, 1" "1, 1, 1" {} {} 0 1
7 "1, NULL, 1" "1, 1, 2" 0 1 0 1
} {
do_execsql_test 1.$tn.eq "SELECT ($v1) == ($v2)" [list $eq]
do_execsql_test 1.$tn.ne "SELECT ($v1) != ($v2)" [list $ne]
do_execsql_test 1.$tn.is "SELECT ($v1) IS ($v2)" [list $is]
do_execsql_test 1.$tn.isnot "SELECT ($v1) IS NOT ($v2)" [list $isnot]
do_execsql_test 1.$tn.2.eq "SELECT (SELECT $v1) == (SELECT $v2)" [list $eq]
do_execsql_test 1.$tn.2.ne "SELECT (SELECT $v1) != (SELECT $v2)" [list $ne]
}
foreach {tn v1 v2 lt gt le ge} {
1 "(1, 1, 3)" "(1, 2, 3)" 1 0 1 0
2 "(1, 2, 3)" "(1, 2, 3)" 0 0 1 1
3 "(1, 3, 3)" "(1, 2, 3)" 0 1 0 1
4 "(1, NULL, 3)" "(1, 2, 3)" {} {} {} {}
5 "(1, 3, 3)" "(1, NULL, 3)" {} {} {} {}
6 "(1, NULL, 3)" "(1, NULL, 3)" {} {} {} {}
} {
foreach {tn2 expr res} [list \
2.$tn.lt "$v1 < $v2" $lt \
2.$tn.gt "$v1 > $v2" $gt \
2.$tn.le "$v1 <= $v2" $le \
2.$tn.ge "$v1 >= $v2" $ge \
] {
do_execsql_test $tn2 "SELECT $expr" [list $res]
set map(0) [list]
set map() [list]
set map(1) [list 1]
do_execsql_test $tn2.where1 "SELECT * FROM one WHERE $expr" $map($res)
set map(0) [list 1]
set map() [list]
set map(1) [list]
do_execsql_test $tn2.where2 "SELECT * FROM one WHERE NOT $expr" $map($res)
}
}
do_execsql_test 3.0 {
CREATE TABLE t1(x, y);
INSERT INTO t1 VALUES(1, 1);
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 VALUES(2, 3);
INSERT INTO t1 VALUES(2, 4);
INSERT INTO t1 VALUES(3, 5);
INSERT INTO t1 VALUES(3, 6);
}
foreach {tn r order} {
1 "(1, 1)" "ORDER BY y"
2 "(1, 1)" "ORDER BY x, y"
3 "(1, 2)" "ORDER BY x, y DESC"
4 "(3, 6)" "ORDER BY x DESC, y DESC"
5 "((3, 5))" "ORDER BY x DESC, y"
6 "(SELECT 3, 5)" "ORDER BY x DESC, y"
} {
do_execsql_test 3.$tn.1 "SELECT $r == (SELECT x,y FROM t1 $order)" 1
do_execsql_test 3.$tn.2 "SELECT $r == (SELECT * FROM t1 $order)" 1
do_execsql_test 3.$tn.3 "
SELECT (SELECT * FROM t1 $order) == (SELECT * FROM t1 $order)
" 1
do_execsql_test 3.$tn.4 "
SELECT (SELECT 0, 0) == (SELECT * FROM t1 $order)
" 0
}
foreach {tn expr res} {
1 {(2, 2) BETWEEN (2, 2) AND (3, 3)} 1
2 {(2, 2) BETWEEN (2, NULL) AND (3, 3)} {}
3 {(2, 2) BETWEEN (3, NULL) AND (3, 3)} 0
} {
do_execsql_test 4.$tn "SELECT $expr" [list $res]
}
foreach {tn expr res} {
1 {(2, 4) IN (SELECT * FROM t1)} 1
2 {(3, 4) IN (SELECT * FROM t1)} 0
3 {(NULL, 4) IN (SELECT * FROM t1)} {}
4 {(NULL, 0) IN (SELECT * FROM t1)} 0
5 {(NULL, 4) NOT IN (SELECT * FROM t1)} {}
6 {(NULL, 0) NOT IN (SELECT * FROM t1)} 1
} {
do_execsql_test 5.$tn "SELECT $expr" [list $res]
}
do_execsql_test 6.0 {
CREATE TABLE hh(a, b, c);
INSERT INTO hh VALUES('abc', 1, 'i');
INSERT INTO hh VALUES('ABC', 1, 'ii');
INSERT INTO hh VALUES('def', 2, 'iii');
INSERT INTO hh VALUES('DEF', 2, 'iv');
INSERT INTO hh VALUES('GHI', 3, 'v');
INSERT INTO hh VALUES('ghi', 3, 'vi');
CREATE INDEX hh_ab ON hh(a, b);
}
do_execsql_test 6.1 {
SELECT c FROM hh WHERE (a, b) = (SELECT 'abc', 1);
} {i}
do_execsql_test 6.2 {
SELECT c FROM hh WHERE (a, b) = (SELECT 'abc' COLLATE nocase, 1);
} {i}
do_execsql_test 6.3 {
SELECT c FROM hh WHERE a = (SELECT 'abc' COLLATE nocase) AND b = (SELECT 1);
} {i}
do_execsql_test 6.4 {
SELECT c FROM hh WHERE +a = (SELECT 'abc' COLLATE nocase) AND b = (SELECT 1);
} {i}
do_execsql_test 6.5 {
SELECT c FROM hh WHERE a = (SELECT 'abc') COLLATE nocase AND b = (SELECT 1);
} {i ii}
do_catchsql_test 6.6 {
SELECT c FROM hh WHERE (a, b) = (SELECT 'abc', 1) COLLATE nocase;
} {1 {row value misused}}
do_catchsql_test 6.7 {
SELECT c FROM hh WHERE (a, b) = 1;
} {1 {row value misused}}
do_execsql_test 6.8 {
SELECT c FROM hh WHERE (a COLLATE nocase, b) = (SELECT 'def', 2);
} {iii iv}
do_execsql_test 6.9 {
SELECT c FROM hh WHERE (a COLLATE nocase, b) IS NOT (SELECT 'def', 2);
} {i ii v vi}
do_execsql_test 6.10 {
SELECT c FROM hh WHERE (b, a) = (SELECT 2, 'def');
} {iii}
do_execsql_test 7.0 {
CREATE TABLE xy(i INTEGER PRIMARY KEY, j, k);
INSERT INTO xy VALUES(1, 1, 1);
INSERT INTO xy VALUES(2, 2, 2);
INSERT INTO xy VALUES(3, 3, 3);
INSERT INTO xy VALUES(4, 4, 4);
}
foreach {tn sql res eqp} {
1 "SELECT * FROM xy WHERE (i, j) IS (2, 2)" {2 2 2}
"0 0 0 {SEARCH TABLE xy USING INTEGER PRIMARY KEY (rowid=?)}"
2 "SELECT * FROM xy WHERE (k, j) < (2, 3)" {1 1 1 2 2 2}
"0 0 0 {SCAN TABLE xy}"
3 "SELECT * FROM xy WHERE (i, j) < (2, 3)" {1 1 1 2 2 2}
"0 0 0 {SEARCH TABLE xy USING INTEGER PRIMARY KEY (rowid<?)}"
4 "SELECT * FROM xy WHERE (i, j) > (2, 1)" {2 2 2 3 3 3 4 4 4}
"0 0 0 {SEARCH TABLE xy USING INTEGER PRIMARY KEY (rowid>?)}"
5 "SELECT * FROM xy WHERE (i, j) > ('2', 1)" {2 2 2 3 3 3 4 4 4}
"0 0 0 {SEARCH TABLE xy USING INTEGER PRIMARY KEY (rowid>?)}"
} {
do_eqp_test 7.$tn.1 $sql $eqp
do_execsql_test 7.$tn.2 $sql $res
}
do_execsql_test 8.0 {
CREATE TABLE j1(a);
}
do_execsql_test 8.1 {
SELECT * FROM j1 WHERE (select min(a) FROM j1) IN (?, ?, ?)
}
do_execsql_test 9.0 {
CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c);
INSERT INTO t2 VALUES(1, 1, 1);
INSERT INTO t2 VALUES(2, 2, 2);
INSERT INTO t2 VALUES(3, 3, 3);
INSERT INTO t2 VALUES(4, 4, 4);
INSERT INTO t2 VALUES(5, 5, 5);
}
foreach {tn q res} {
1 "(a, b) > (2, 1)" {2 3 4 5}
2 "(a, b) > (2, 2)" {3 4 5}
3 "(a, b) < (4, 5)" {1 2 3 4}
4 "(a, b) < (4, 3)" {1 2 3}
} {
do_execsql_test 9.$tn "SELECT c FROM t2 WHERE $q" $res
}
do_execsql_test 10.0 {
CREATE TABLE dual(dummy); INSERT INTO dual(dummy) VALUES('X');
CREATE TABLE t3(a TEXT,b TEXT,c TEXT,d TEXT,e TEXT,f TEXT);
CREATE INDEX t3x ON t3(b,c,d,e,f);
SELECT a FROM t3
WHERE (c,d) IN (SELECT 'c','d' FROM dual)
AND (a,b,e) IN (SELECT 'a','b','d' FROM dual);
}
do_catchsql_test 11.1 {
CREATE TABLE t11(a);
SELECT * FROM t11 WHERE (a,a)<=1;
} {1 {row value misused}}
do_catchsql_test 11.2 {
SELECT * FROM t11 WHERE (a,a)<1;
} {1 {row value misused}}
do_catchsql_test 11.3 {
SELECT * FROM t11 WHERE (a,a)>=1;
} {1 {row value misused}}
do_catchsql_test 11.4 {
SELECT * FROM t11 WHERE (a,a)>1;
} {1 {row value misused}}
do_catchsql_test 11.5 {
SELECT * FROM t11 WHERE (a,a)==1;
} {1 {row value misused}}
do_catchsql_test 11.6 {
SELECT * FROM t11 WHERE (a,a)<>1;
} {1 {row value misused}}
do_catchsql_test 11.7 {
SELECT * FROM t11 WHERE (a,a) IS 1;
} {1 {row value misused}}
do_catchsql_test 11.8 {
SELECT * FROM t11 WHERE (a,a) IS NOT 1;
} {1 {row value misused}}
finish_test
+279
View File
@@ -0,0 +1,279 @@
# 2016 June 17
#
# 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 SELECT statement.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix rowvalue2
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c);
INSERT INTO t1 VALUES(0, 0, 0);
INSERT INTO t1 VALUES(0, 1, 1);
INSERT INTO t1 VALUES(1, 0, 2);
INSERT INTO t1 VALUES(1, 1, 3);
CREATE INDEX i1 ON t1(a, b);
}
do_execsql_test 1.1.1 { SELECT c FROM t1 WHERE (a, b) >= (1, 0) } {2 3}
do_execsql_test 1.1.2 { SELECT c FROM t1 WHERE (a, b) > (1, 0) } {3}
#-------------------------------------------------------------------------
do_execsql_test 2.0.1 {
CREATE TABLE t2(a INTEGER, b INTEGER, c INTEGER, d INTEGER);
CREATE INDEX i2 ON t2(a, b, c);
}
do_test 2.0.2 {
foreach a {0 1 2 3} {
foreach b {0 1 2 3} {
foreach c {0 1 2 3} {
execsql { INSERT INTO t2 VALUES($a, $b, $c, $c + $b*4 + $a*16); }
}}}
} {}
do_execsql_test 2.1 {
SELECT d FROM t2 WHERE (a, b) > (2, 2);
} [db eval { SELECT d FROM t2 WHERE a>2 OR (a=2 AND b>2) }]
do_execsql_test 2.2 {
SELECT d FROM t2 WHERE (a, b) >= (2, 2);
} [db eval { SELECT d FROM t2 WHERE a>2 OR (a=2 AND b>=2) }]
do_execsql_test 2.3 {
SELECT d FROM t2 WHERE a=1 AND (b, c) >= (1, 2);
} [db eval { SELECT d FROM t2 WHERE +a=1 AND (b>1 OR (b==1 AND c>=2)) }]
do_execsql_test 2.4 {
SELECT d FROM t2 WHERE a=1 AND (b, c) > (1, 2);
} [db eval { SELECT d FROM t2 WHERE +a=1 AND (b>1 OR (b==1 AND c>2)) }]
#-------------------------------------------------------------------------
set words {
airfare airfield airfields airflow airfoil
airfoils airframe airframes airily airing
airings airless airlift airlifts airline
airliner airlines airlock airlocks airmail
airmails airman airmen airplane airplanes
arraignment arraignments arraigns arrange arranged
arrangement arrangements arranger arrangers arranges
arranging arrant array arrayed arrays
arrears arrest arrested arrester arresters
arresting arrestingly arrestor arrestors arrests
edifices edit edited editing edition
editions editor editorial editorially editorials
editors edits educable educate educated
educates educating education educational educationally
educations educator educators eel eelgrass
}
do_test 3.0 {
execsql { CREATE TABLE t3(a, b, c, w); }
foreach w $words {
set a [string range $w 0 2]
set b [string range $w 3 5]
set c [string range $w 6 end]
execsql { INSERT INTO t3 VALUES($a, $b, $c, $w) }
}
} {}
foreach {tn idx} {
IDX1 {}
IDX2 { CREATE INDEX i3 ON t3(a, b, c); }
IDX3 { CREATE INDEX i3 ON t3(a, b); }
IDX4 { CREATE INDEX i3 ON t3(a); }
} {
execsql { DROP INDEX IF EXISTS i3 }
execsql $idx
foreach w $words {
set a [string range $w 0 2]
set b [string range $w 3 5]
set c [string range $w 6 end]
foreach op [list > >= < <= == IS] {
do_execsql_test 3.1.$tn.$w.$op [subst -novar {
SELECT rowid FROM t3 WHERE (a, b, c) [set op] ($a, $b, $c)
ORDER BY +rowid
}] [db eval [subst -novar {
SELECT rowid FROM t3 WHERE w [set op] $w ORDER BY +rowid
}]]
do_execsql_test 3.1.$tn.$w.$op.subselect [subst -novar {
SELECT rowid FROM t3 WHERE (a, b, c) [set op] (
SELECT a, b, c FROM t3 WHERE w = $w
)
ORDER BY +rowid
}] [db eval [subst -novar {
SELECT rowid FROM t3 WHERE w [set op] $w ORDER BY +rowid
}]]
}
}
}
#-------------------------------------------------------------------------
#
do_execsql_test 4.0 {
CREATE TABLE t4(a, b, c);
INSERT INTO t4 VALUES(NULL, NULL, NULL);
INSERT INTO t4 VALUES(NULL, NULL, 0);
INSERT INTO t4 VALUES(NULL, NULL, 1);
INSERT INTO t4 VALUES(NULL, 0, NULL);
INSERT INTO t4 VALUES(NULL, 0, 0);
INSERT INTO t4 VALUES(NULL, 0, 1);
INSERT INTO t4 VALUES(NULL, 1, NULL);
INSERT INTO t4 VALUES(NULL, 1, 0);
INSERT INTO t4 VALUES(NULL, 1, 1);
INSERT INTO t4 VALUES( 0, NULL, NULL);
INSERT INTO t4 VALUES( 0, NULL, 0);
INSERT INTO t4 VALUES( 0, NULL, 1);
INSERT INTO t4 VALUES( 0, 0, NULL);
INSERT INTO t4 VALUES( 0, 0, 0);
INSERT INTO t4 VALUES( 0, 0, 1);
INSERT INTO t4 VALUES( 0, 1, NULL);
INSERT INTO t4 VALUES( 0, 1, 0);
INSERT INTO t4 VALUES( 0, 1, 1);
INSERT INTO t4 VALUES( 1, NULL, NULL);
INSERT INTO t4 VALUES( 1, NULL, 0);
INSERT INTO t4 VALUES( 1, NULL, 1);
INSERT INTO t4 VALUES( 1, 0, NULL);
INSERT INTO t4 VALUES( 1, 0, 0);
INSERT INTO t4 VALUES( 1, 0, 1);
INSERT INTO t4 VALUES( 1, 1, NULL);
INSERT INTO t4 VALUES( 1, 1, 0);
INSERT INTO t4 VALUES( 1, 1, 1);
}
proc make_expr1 {cList vList op} {
return "([join $cList ,]) $op ([join $vList ,])"
}
proc make_expr3 {cList vList op} {
set n [llength $cList]
set aList [list]
foreach c [lrange $cList 0 end-1] v [lrange $vList 0 end-1] {
lappend aList "$c == $v"
}
lappend aList "[lindex $cList end] $op [lindex $vList end]"
return "([join $aList { AND }])"
}
proc make_expr2 {cList vList op} {
set ret ""
switch -- $op {
== - IS {
set aList [list]
foreach c $cList v $vList { lappend aList "($c $op $v)" }
set ret [join $aList " AND "]
}
< - > {
set oList [list]
for {set i 0} {$i < [llength $cList]} {incr i} {
lappend oList [make_expr3 [lrange $cList 0 $i] [lrange $vList 0 $i] $op]
}
set ret [join $oList " OR "]
}
<= - >= {
set o2 [string range $op 0 0]
set oList [list]
for {set i 0} {$i < [llength $cList]-1} {incr i} {
lappend oList [make_expr3 [lrange $cList 0 $i] [lrange $vList 0 $i] $o2]
}
lappend oList [make_expr3 $cList $vList $op]
set ret [join $oList " OR "]
}
default {
error "Unknown op: $op"
}
}
set ret
}
foreach {tn idx} {
IDX1 {}
IDX2 { CREATE INDEX i4 ON t4(a, b, c); }
IDX3 { CREATE INDEX i4 ON t4(a, b); }
IDX4 { CREATE INDEX i4 ON t4(a); }
} {
execsql { DROP INDEX IF EXISTS i4 }
execsql $idx
foreach {tn2 vector} {
1 {0 0 0}
2 {1 1 1}
3 {0 0 NULL}
4 {0 NULL 0}
5 {NULL 0 0}
6 {1 1 NULL}
7 {1 NULL 1}
8 {NULL 1 1}
} {
foreach op { IS == < <= > >= } {
set e1 [make_expr1 {a b c} $vector $op]
set e2 [make_expr2 {a b c} $vector $op]
do_execsql_test 4.$tn.$tn2.$op \
"SELECT rowid FROM t4 WHERE $e2 ORDER BY +rowid" [
db eval "SELECT rowid FROM t4 WHERE $e1 ORDER BY +rowid"
]
}
}
}
do_execsql_test 5.0 {
CREATE TABLE r1(a TEXT, iB TEXT);
CREATE TABLE r2(x TEXT, zY INTEGER);
CREATE INDEX r1ab ON r1(a, iB);
INSERT INTO r1 VALUES(35, 35);
INSERT INTO r2 VALUES(35, 36);
INSERT INTO r2 VALUES(35, 4);
INSERT INTO r2 VALUES(35, 35);
} {}
foreach {tn lhs rhs} {
1 {x +zY} {a iB}
2 {x zY} {a iB}
3 {x zY} {a +iB}
4 {+x zY} {a iB}
5 {x zY} {+a iB}
} {
foreach op { IS == < <= > >= } {
set e1 [make_expr1 $lhs $rhs $op]
set e2 [make_expr2 $lhs $rhs $op]
do_execsql_test 5.$tn.$op \
"SELECT * FROM r1, r2 WHERE $e2 ORDER BY iB" [db eval \
"SELECT * FROM r1, r2 WHERE $e1 ORDER BY iB"
]
}
}
finish_test
+206
View File
@@ -0,0 +1,206 @@
# 2016 June 17
#
# 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 "(...) IN (SELECT ...)" expressions
# where the SELECT statement returns more than one column.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix rowvalue3
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c);
CREATE INDEX i1 ON t1(a, b);
INSERT INTO t1 VALUES(1, 2, 3);
INSERT INTO t1 VALUES(4, 5, 6);
INSERT INTO t1 VALUES(7, 8, 9);
}
foreach {tn sql res} {
1 "SELECT 1 WHERE (4, 5) IN (SELECT a, b FROM t1)" 1
2 "SELECT 1 WHERE (5, 5) IN (SELECT a, b FROM t1)" {}
3 "SELECT 1 WHERE (5, 4) IN (SELECT a, b FROM t1)" {}
4 "SELECT 1 WHERE (5, 4) IN (SELECT b, a FROM t1)" 1
5 "SELECT 1 WHERE (SELECT a, b FROM t1 WHERE c=6) IN (SELECT a, b FROM t1)" 1
6 "SELECT (5, 4) IN (SELECT a, b FROM t1)" 0
7 "SELECT 1 WHERE (5, 4) IN (SELECT +b, +a FROM t1)" 1
8 "SELECT (5, 4) IN (SELECT +b, +a FROM t1)" 1
9 "SELECT (1, 2) IN (SELECT rowid, b FROM t1)" 1
10 "SELECT 1 WHERE (1, 2) IN (SELECT rowid, b FROM t1)" 1
11 "SELECT 1 WHERE (1, NULL) IN (SELECT rowid, b FROM t1)" {}
12 "SELECT 1 FROM t1 WHERE (a, b) = (SELECT +a, +b FROM t1)" {1}
} {
do_execsql_test 1.$tn $sql $res
}
#-------------------------------------------------------------------------
do_execsql_test 2.0 {
CREATE TABLE z1(x, y, z);
CREATE TABLE kk(a, b);
INSERT INTO z1 VALUES('a', 'b', 'c');
INSERT INTO z1 VALUES('d', 'e', 'f');
INSERT INTO z1 VALUES('g', 'h', 'i');
-- INSERT INTO kk VALUES('y', 'y');
INSERT INTO kk VALUES('d', 'e');
-- INSERT INTO kk VALUES('x', 'x');
}
foreach {tn idx} {
1 { }
2 { CREATE INDEX z1idx ON z1(x, y) }
3 { CREATE UNIQUE INDEX z1idx ON z1(x, y) }
4 { CREATE INDEX z1idx ON kk(a, b) }
} {
execsql "DROP INDEX IF EXISTS z1idx"
execsql $idx
do_execsql_test 2.$tn.1 {
SELECT * FROM z1 WHERE x IN (SELECT a FROM kk)
} {d e f}
do_execsql_test 2.$tn.2 {
SELECT * FROM z1 WHERE (x,y) IN (SELECT a, b FROM kk)
} {d e f}
do_execsql_test 2.$tn.3 {
SELECT * FROM z1 WHERE (x, +y) IN (SELECT a, b FROM kk)
} {d e f}
do_execsql_test 2.$tn.4 {
SELECT * FROM z1 WHERE (x, +y) IN (SELECT a, b||'x' FROM kk)
} {}
do_execsql_test 2.$tn.5 {
SELECT * FROM z1 WHERE (+x, y) IN (SELECT a, b FROM kk)
} {d e f}
}
#-------------------------------------------------------------------------
#
do_execsql_test 3.0 {
CREATE TABLE c1(a, b, c, d);
INSERT INTO c1(rowid, a, b) VALUES(1, NULL, 1);
INSERT INTO c1(rowid, a, b) VALUES(2, 2, NULL);
INSERT INTO c1(rowid, a, b) VALUES(3, 2, 2);
INSERT INTO c1(rowid, a, b) VALUES(4, 3, 3);
INSERT INTO c1(rowid, a, b, c, d) VALUES(101, 'a', 'b', 1, 1);
INSERT INTO c1(rowid, a, b, c, d) VALUES(102, 'a', 'b', 1, 2);
INSERT INTO c1(rowid, a, b, c, d) VALUES(103, 'a', 'b', 1, 3);
INSERT INTO c1(rowid, a, b, c, d) VALUES(104, 'a', 'b', 2, 1);
INSERT INTO c1(rowid, a, b, c, d) VALUES(105, 'a', 'b', 2, 2);
INSERT INTO c1(rowid, a, b, c, d) VALUES(106, 'a', 'b', 2, 3);
INSERT INTO c1(rowid, a, b, c, d) VALUES(107, 'a', 'b', 3, 1);
INSERT INTO c1(rowid, a, b, c, d) VALUES(108, 'a', 'b', 3, 2);
INSERT INTO c1(rowid, a, b, c, d) VALUES(109, 'a', 'b', 3, 3);
}
foreach {tn idx} {
1 { }
2 { CREATE INDEX c1ab ON c1(a, b); }
3 { CREATE INDEX c1ba ON c1(b, a); }
4 { CREATE INDEX c1cd ON c1(c, d); }
5 { CREATE INDEX c1dc ON c1(d, c); }
} {
drop_all_indexes
foreach {tn2 sql res} {
1 "SELECT (1, 2) IN (SELECT a, b FROM c1)" {0}
2 "SELECT (1, 1) IN (SELECT a, b FROM c1)" {{}}
3 "SELECT (2, 1) IN (SELECT a, b FROM c1)" {{}}
4 "SELECT (2, 2) IN (SELECT a, b FROM c1)" {1}
5 "SELECT c, d FROM c1 WHERE (c, d) IN (SELECT d, c FROM c1)"
{ 1 1 1 2 1 3 2 1 2 2 2 3 3 1 3 2 3 3 }
6 "SELECT c, d FROM c1 WHERE (c,d) IN (SELECT d, c FROM c1) ORDER BY c DESC"
{ 3 1 3 2 3 3 2 1 2 2 2 3 1 1 1 2 1 3 }
7 {
SELECT c, d FROM c1 WHERE (c,d) IN (SELECT d, c FROM c1)
ORDER BY c DESC, d ASC
} { 3 1 3 2 3 3 2 1 2 2 2 3 1 1 1 2 1 3 }
8 {
SELECT c, d FROM c1 WHERE (c,d) IN (SELECT d, c FROM c1)
ORDER BY c ASC, d DESC
} { 1 3 1 2 1 1 2 3 2 2 2 1 3 3 3 2 3 1 }
9 {
SELECT c, d FROM c1 WHERE (c,d) IN (SELECT d, c FROM c1)
ORDER BY c ASC, d ASC
} { 1 1 1 2 1 3 2 1 2 2 2 3 3 1 3 2 3 3 }
10 {
SELECT c, d FROM c1 WHERE (c,d) IN (SELECT d, c FROM c1)
ORDER BY c DESC, d DESC
} { 3 3 3 2 3 1 2 3 2 2 2 1 1 3 1 2 1 1 }
} {
do_execsql_test 3.$tn.$tn2 $sql $res
}
}
#-------------------------------------------------------------------------
do_execsql_test 4.0 {
CREATE TABLE hh(a, b, c);
INSERT INTO hh VALUES('a', 'a', 1);
INSERT INTO hh VALUES('a', 'b', 2);
INSERT INTO hh VALUES('b', 'a', 3);
INSERT INTO hh VALUES('b', 'b', 4);
CREATE TABLE k1(x, y);
INSERT INTO k1 VALUES('a', 'a');
INSERT INTO k1 VALUES('b', 'b');
INSERT INTO k1 VALUES('a', 'b');
INSERT INTO k1 VALUES('b', 'a');
}
foreach {tn idx} {
1 { }
2 { CREATE INDEX h1 ON hh(a, b); }
3 { CREATE UNIQUE INDEX k1idx ON k1(x, y) }
4 { CREATE UNIQUE INDEX k1idx ON k1(x, y DESC) }
5 {
CREATE INDEX h1 ON hh(a, b);
CREATE UNIQUE INDEX k1idx ON k1(x, y);
}
6 {
CREATE INDEX h1 ON hh(a, b);
CREATE UNIQUE INDEX k1idx ON k1(x, y DESC);
}
} {
drop_all_indexes
execsql $idx
foreach {tn2 orderby res} {
1 "a ASC, b ASC" {1 2 3 4}
2 "a ASC, b DESC" {2 1 4 3}
3 "a DESC, b ASC" {3 4 1 2}
4 "a DESC, b DESC" {4 3 2 1}
} {
do_execsql_test 4.$tn.$tn2 "
SELECT c FROM hh WHERE (a, b) in (SELECT x, y FROM k1) ORDER BY $orderby
" $res
}
}
#-------------------------------------------------------------------------
finish_test
+311
View File
@@ -0,0 +1,311 @@
# 2016 July 29
#
# 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 syntax errors involving row-value constructors
# and sub-selects that return multiple arguments.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix rowvalue4
#-------------------------------------------------------------------------
# Test some error conditions:
#
# * row values used where they are not supported,
# * row values or sub-selects that contain/return the wrong number
# of elements.
#
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c);
CREATE INDEX t1bac ON t1(b, a, c);
}
foreach {tn e} {
1 "(1, 2, 3)"
2 "1 + (1, 2)"
3 "(1,2,3) == (1, 2)"
} {
do_catchsql_test 1.$tn "SELECT $e" {1 {row value misused}}
}
foreach {tn s error} {
1 "SELECT * FROM t1 WHERE a = (1, 2)" {row value misused}
2 "SELECT * FROM t1 WHERE b = (1, 2)" {row value misused}
3 "SELECT * FROM t1 WHERE NOT (b = (1, 2))" {row value misused}
4 "SELECT * FROM t1 LIMIT (1, 2)" {row value misused}
5 "SELECT (a, b) IN (SELECT * FROM t1) FROM t1"
{sub-select returns 3 columns - expected 2}
6 "SELECT * FROM t1 WHERE (a, b) IN (SELECT * FROM t1)"
{sub-select returns 3 columns - expected 2}
} {
do_catchsql_test 2.$tn "$s" [list 1 $error]
}
#-------------------------------------------------------------------------
do_execsql_test 2.0 {
CREATE TABLE t2(a, b, c, d);
INSERT INTO t2 VALUES(1, 1, 1, 1);
INSERT INTO t2 VALUES(1, 1, 2, 2);
INSERT INTO t2 VALUES(1, 1, 3, 3);
INSERT INTO t2 VALUES(1, 2, 1, 4);
INSERT INTO t2 VALUES(1, 2, 2, 5);
INSERT INTO t2 VALUES(1, 2, 3, 6);
INSERT INTO t2 VALUES(1, 3, 1, 7);
INSERT INTO t2 VALUES(1, 3, 2, 8);
INSERT INTO t2 VALUES(1, 3, 3, 9);
INSERT INTO t2 VALUES(2, 1, 1, 10);
INSERT INTO t2 VALUES(2, 1, 2, 11);
INSERT INTO t2 VALUES(2, 1, 3, 12);
INSERT INTO t2 VALUES(2, 2, 1, 13);
INSERT INTO t2 VALUES(2, 2, 2, 14);
INSERT INTO t2 VALUES(2, 2, 3, 15);
INSERT INTO t2 VALUES(2, 3, 1, 16);
INSERT INTO t2 VALUES(2, 3, 2, 17);
INSERT INTO t2 VALUES(2, 3, 3, 18);
INSERT INTO t2 VALUES(3, 1, 1, 19);
INSERT INTO t2 VALUES(3, 1, 2, 20);
INSERT INTO t2 VALUES(3, 1, 3, 21);
INSERT INTO t2 VALUES(3, 2, 1, 22);
INSERT INTO t2 VALUES(3, 2, 2, 23);
INSERT INTO t2 VALUES(3, 2, 3, 24);
INSERT INTO t2 VALUES(3, 3, 1, 25);
INSERT INTO t2 VALUES(3, 3, 2, 26);
INSERT INTO t2 VALUES(3, 3, 3, 27);
}
foreach {nm idx} {
idx1 {}
idx2 { CREATE INDEX t2abc ON t2(a, b, c); }
idx3 { CREATE INDEX t2abc ON t2(a, b DESC, c); }
idx4 { CREATE INDEX t2abc ON t2(a DESC, b DESC, c DESC); }
idx5 { CREATE INDEX t2abc ON t2(a ASC, b ASC, c ASC); }
idx6 { CREATE INDEX t2abc ON t2(a DESC, b, c); }
idx7 { CREATE INDEX t2abc ON t2(a DESC, b DESC) }
idx8 { CREATE INDEX t2abc ON t2(c, b, a); }
idx9 { CREATE INDEX t2d ON t2(d); }
idx10 { CREATE INDEX t2abc ON t2(a DESC, b, c DESC); }
} {
drop_all_indexes
execsql $idx
foreach {tn where res} {
1 "(a, b, c) < (2, 2, 2)" {1 2 3 4 5 6 7 8 9 10 11 12 13}
2 "(a, b, c) <= (2, 2, 2)" {1 2 3 4 5 6 7 8 9 10 11 12 13 14}
3 "(a, b, c) > (2, 2, 2)" {15 16 17 18 19 20 21 22 23 24 25 26 27}
4 "(a, b, c) >= (2, 2, 2)" {14 15 16 17 18 19 20 21 22 23 24 25 26 27}
5 "(a, b, c) >= (2, 2, NULL)" {16 17 18 19 20 21 22 23 24 25 26 27}
6 "(a, b, c) <= (2, 2, NULL)" {1 2 3 4 5 6 7 8 9 10 11 12}
7 "(a, b, c) >= (2, NULL, NULL)" {19 20 21 22 23 24 25 26 27}
8 "(a, b, c) <= (2, NULL, NULL)" {1 2 3 4 5 6 7 8 9}
9 "(a, b, c) < (SELECT a, b, c FROM t2 WHERE d=14)"
{1 2 3 4 5 6 7 8 9 10 11 12 13}
10 "(a, b, c) = (SELECT a, b, c FROM t2 WHERE d=14)" 14
11 "a = 2 AND (b, c) > (2, 2)" {15 16 17 18}
12 "a = 2 AND (b, c) < (3, 3) AND (b, c) > (1, 1)" {11 12 13 14 15 16 17}
} {
set result [db eval "SELECT d FROM t2 WHERE $where"]
do_test 2.1.$nm.$tn { lsort -integer $result } $res
}
foreach {tn e res} {
1 "(2, 1) IN (SELECT a, b FROM t2)" 1
2 "(2, 1) IN (SELECT a, b FROM t2 ORDER BY d)" 1
3 "(2, 1) IN (SELECT a, b FROM t2 ORDER BY d LIMIT 9)" 0
4 "(2, 1) IN (SELECT a, b FROM t2 ORDER BY d LIMIT 10)" 1
5 "(3, 3) = (SELECT a, b FROM t2 ORDER BY d DESC LIMIT 1)" 1
6 "(3, 3) = (SELECT a, b FROM t2 ORDER BY d ASC LIMIT 1)" 0
7 "(1, NULL) = (SELECT a, b FROM t2 ORDER BY d ASC LIMIT 1)" {{}}
8 "(3, 1) = (SELECT b, c FROM t2 ORDER BY d DESC LIMIT 1 OFFSET 2)" 1
9 "(3, 1) = (SELECT b, c FROM t2 ORDER BY d ASC LIMIT 1 OFFSET 2)" 0
10 "(1, NULL) = (SELECT b, c FROM t2 ORDER BY d ASC LIMIT 1 OFFSET 2)" {{}}
11 "(3, 3) = (SELECT max(a), max(b) FROM t2)" 1
12 "(3, 1) = (SELECT max(a), min(b) FROM t2)" 1
13 "(NULL, NULL) = (SELECT max(a), min(b) FROM t2)" {{}}
14 "(2, 1) IN (SELECT a, b FROM t2 ORDER BY d LIMIT 5 OFFSET 11)" 1
15 "(2, 1) IN (SELECT a, b FROM t2 ORDER BY d LIMIT 5 OFFSET 12)" 0
} {
do_execsql_test 2.2.$nm.$tn "SELECT $e" $res
}
}
ifcapable stat4 {
do_execsql_test 3.0 {
CREATE TABLE c1(a, b, c, d);
INSERT INTO c1(a, b) VALUES(1, 'a');
INSERT INTO c1(a, b) VALUES(1, 'b');
INSERT INTO c1(a, b) VALUES(1, 'c');
INSERT INTO c1(a, b) VALUES(1, 'd');
INSERT INTO c1(a, b) VALUES(1, 'e');
INSERT INTO c1(a, b) VALUES(1, 'f');
INSERT INTO c1(a, b) VALUES(1, 'g');
INSERT INTO c1(a, b) VALUES(1, 'h');
INSERT INTO c1(a, b) VALUES(1, 'i');
INSERT INTO c1(a, b) VALUES(1, 'j');
INSERT INTO c1(a, b) VALUES(1, 'k');
INSERT INTO c1(a, b) VALUES(1, 'l');
INSERT INTO c1(a, b) VALUES(1, 'm');
INSERT INTO c1(a, b) VALUES(1, 'n');
INSERT INTO c1(a, b) VALUES(1, 'o');
INSERT INTO c1(a, b) VALUES(1, 'p');
INSERT INTO c1(a, b) VALUES(2, 'a');
INSERT INTO c1(a, b) VALUES(2, 'b');
INSERT INTO c1(a, b) VALUES(2, 'c');
INSERT INTO c1(a, b) VALUES(2, 'd');
INSERT INTO c1(a, b) VALUES(2, 'e');
INSERT INTO c1(a, b) VALUES(2, 'f');
INSERT INTO c1(a, b) VALUES(2, 'g');
INSERT INTO c1(a, b) VALUES(2, 'h');
INSERT INTO c1(c, d) SELECT a, b FROM c1;
CREATE INDEX c1ab ON c1(a, b);
CREATE INDEX c1cd ON c1(c, d);
ANALYZE;
}
do_eqp_test 3.1.1 { SELECT * FROM c1 WHERE a=1 AND c=2 } {
0 0 0 {SEARCH TABLE c1 USING INDEX c1cd (c=?)}
}
do_eqp_test 3.1.2 { SELECT * FROM c1 WHERE a=1 AND b>'d' AND c=2 } {
0 0 0 {SEARCH TABLE c1 USING INDEX c1cd (c=?)}
}
do_eqp_test 3.1.3 { SELECT * FROM c1 WHERE a=1 AND b>'l' AND c=2 } {
0 0 0 {SEARCH TABLE c1 USING INDEX c1ab (a=? AND b>?)}
}
do_eqp_test 3.2.1 { SELECT * FROM c1 WHERE a=1 AND c>1 } {
0 0 0 {SEARCH TABLE c1 USING INDEX c1cd (c>?)}
}
do_eqp_test 3.2.2 { SELECT * FROM c1 WHERE a=1 AND c>0 } {
0 0 0 {SEARCH TABLE c1 USING INDEX c1ab (a=?)}
}
do_eqp_test 3.2.3 { SELECT * FROM c1 WHERE a=1 AND c>=1 } {
0 0 0 {SEARCH TABLE c1 USING INDEX c1ab (a=?)}
}
do_eqp_test 3.2.4 { SELECT * FROM c1 WHERE a=1 AND (c, d)>(1, 'c') } {
0 0 0 {SEARCH TABLE c1 USING INDEX c1ab (a=?)}
}
do_eqp_test 3.2.5 { SELECT * FROM c1 WHERE a=1 AND (c, d)>(1, 'o') } {
0 0 0 {SEARCH TABLE c1 USING INDEX c1cd ((c,d)>(?,?))}
}
do_eqp_test 3.2.6 { SELECT * FROM c1 WHERE a=1 AND (c, +b)>(1, 'c') } {
0 0 0 {SEARCH TABLE c1 USING INDEX c1ab (a=?)}
}
}
#------------------------------------------------------------------------
do_execsql_test 5.0 {
CREATE TABLE d1(x, y);
CREATE TABLE d2(a, b, c);
CREATE INDEX d2ab ON d2(a, b);
CREATE INDEX d2c ON d2(c);
WITH i(i) AS (
VALUES(1) UNION ALL SELECT i+1 FROM i WHERE i<1000
)
INSERT INTO d2 SELECT i/3, i%3, i/3 FROM i;
ANALYZE;
}
do_eqp_test 5.1 {
SELECT * FROM d2 WHERE
(a, b) IN (SELECT x, y FROM d1) AND
(c) IN (SELECT y FROM d1)
} {
0 0 0 {SEARCH TABLE d2 USING INDEX d2ab (a=? AND b=?)}
0 0 0 {EXECUTE LIST SUBQUERY 1}
1 0 0 {SCAN TABLE d1}
0 0 0 {EXECUTE LIST SUBQUERY 2}
2 0 0 {SCAN TABLE d1}
}
do_execsql_test 6.0 {
CREATE TABLE e1(a, b, c, d, e);
CREATE INDEX e1ab ON e1(a, b);
CREATE INDEX e1cde ON e1(c, d, e);
}
do_eqp_test 6.1 {
SELECT * FROM e1 WHERE (a, b) > (?, ?)
} {
0 0 0 {SEARCH TABLE e1 USING INDEX e1ab ((a,b)>(?,?))}
}
do_eqp_test 6.2 {
SELECT * FROM e1 WHERE (a, b) < (?, ?)
} {
0 0 0 {SEARCH TABLE e1 USING INDEX e1ab ((a,b)<(?,?))}
}
do_eqp_test 6.3 {
SELECT * FROM e1 WHERE c = ? AND (d, e) > (?, ?)
} {
0 0 0 {SEARCH TABLE e1 USING INDEX e1cde (c=? AND (d,e)>(?,?))}
}
do_eqp_test 6.4 {
SELECT * FROM e1 WHERE c = ? AND (d, e) < (?, ?)
} {
0 0 0 {SEARCH TABLE e1 USING INDEX e1cde (c=? AND (d,e)<(?,?))}
}
do_eqp_test 6.5 {
SELECT * FROM e1 WHERE (d, e) BETWEEN (?, ?) AND (?, ?) AND c = ?
} {
0 0 0
{SEARCH TABLE e1 USING INDEX e1cde (c=? AND (d,e)>(?,?) AND (d,e)<(?,?))}
}
#-------------------------------------------------------------------------
do_execsql_test 7.1 {
CREATE TABLE f1(a, b, c);
CREATE INDEX f1ab ON f1(a, b);
}
do_catchsql_test 7.2 {
SELECT (a COLLATE nocase, b) IN (SELECT a, b FROM f1) FROM f1;
} {0 {}}
do_catchsql_test 7.3 {
SELECT (a COLLATE nose, b) IN (SELECT a, b FROM f1) FROM f1;
} {1 {no such collation sequence: nose}}
do_catchsql_test 7.4 {
SELECT * FROM f1 WHERE (?, ? COLLATE nose) > (a, b);
} {1 {no such collation sequence: nose}}
#-------------------------------------------------------------------------
drop_all_tables
do_execsql_test 8.1 {
CREATE TABLE c1(x, y);
CREATE TABLE c2(a, b, c);
CREATE INDEX c2ab ON c2(a, b);
CREATE INDEX c2c ON c2(c);
CREATE TABLE c3(d);
}
do_catchsql_test 8.2 {
SELECT * FROM c2 CROSS JOIN c3 WHERE
( (a, b) == (SELECT x, y FROM c1) AND c3.d = c ) OR
( c == (SELECT x, y FROM c1) AND c3.d = c )
} {1 {row value misused}}
finish_test
+112
View File
@@ -0,0 +1,112 @@
# 2016 July 29
#
# 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 syntax errors involving row-values and
# virtual tables.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix rowvalue5
proc vtab_command {method args} {
switch -- $method {
xConnect {
return "CREATE TABLE t1(a, b, c, d, expr)"
}
xBestIndex {
set COL(0) a
set COL(1) b
set COL(2) c
set COL(3) d
set COL(4) expr
set OP(eq) =
set OP(ne) !=
set OP(gt) >
set OP(le) <=
set OP(lt) <
set OP(ge) >=
set OP(match) MATCH
set OP(like) LIKE
set OP(glob) GLOB
set OP(regexp) REGEXP
set clist [lindex $args 0]
set ret [list]
set elist [list]
set i 0
foreach c $clist {
array set C $c
if {$C(usable)} {
lappend ret omit $i
lappend elist "$COL($C(column)) $OP($C(op)) %$i%"
}
incr i
}
lappend ret idxstr [join $elist " AND "]
#puts "xBestIndex: $ret"
return $ret
}
xFilter {
foreach {idxnum idxstr arglist} $args {}
set i 0
set ee $idxstr
foreach a $arglist {
if {[string is double $a]==0} {
set a "'[string map {' ''} $a]'"
}
set ee [string map [list "%$i%" $a] $ee]
incr i
}
set ee [string map [list "'" "''"] $ee]
set ret [list sql "SELECT 1, 'a', 'b', 'c', 'd', '$ee'"]
#puts "xFilter: $ret"
return $ret
}
}
return {}
}
register_tcl_module db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE x1 USING tcl(vtab_command);
} {}
foreach {tn where res} {
1 "1" {{}}
2 "a=1" {{a = 1}}
3 "a=1 AND 4 = b" {{a = 1 AND b = 4}}
4 "c>'hello'" {{c > 'hello'}}
5 "c<='hel''lo'" {{c <= 'hel''lo'}}
6 "(a, b) = (SELECT 9, 10)" {{a = 9 AND b = 10}}
7 "(+a, b) = (SELECT 'a', 'b')" {{b = 'b'}}
8 "(a, +b) = (SELECT 'a', 'b')" {{a = 'a'}}
10 "(a, b) IN (SELECT 9, 10 UNION SELECT 11, 12)"
{{a = 9 AND b = 10} {a = 11 AND b = 12}}
11 "(+a, b) IN (SELECT 'a', 'b')" {{b = 'b'}}
12 "(a, +b) IN (SELECT 'a', 'b')" {{a = 'a'}}
13 "(a, b) < ('d', 'e')" {{a <= 'd'}}
14 "(a, b) < ('a', 'c')" {{a <= 'a'}}
15 "(a, b) <= ('a', 'b')" {{a <= 'a'}}
16 "(a, b) < ('a', 'b')" {}
} {
do_execsql_test 1.$tn "SELECT expr FROM x1 WHERE $where" $res
}
finish_test
+36
View File
@@ -0,0 +1,36 @@
# 2016-08-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.
#
#***********************************************************************
# The focus of this file is handling of NULL values in row-value IN
# expressions.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix rowvalue6
do_execsql_test 1.1 {
CREATE TABLE t1(a,b,c);
CREATE INDEX t1x1 ON t1(a,b);
INSERT INTO t1 VALUES(1,NULL,200);
CREATE TABLE t2(x,y,z);
INSERT INTO t2 VALUES(1,NULL,55);
SELECT c FROM t1 WHERE (a,b) IN (SELECT x,y FROM t2 WHERE z==55);
} {}
do_execsql_test 1.2 {
INSERT INTO t1 VALUES(2,3,400);
INSERT INTO t2 VALUES(2,3,55);
SELECT c FROM t1 WHERE (a,b) IN (SELECT x,y FROM t2 WHERE z==55);
} {400}
finish_test
+58
View File
@@ -0,0 +1,58 @@
# 2016-08-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.
#
#***********************************************************************
# The focus of this file is vector assignments in the SET clause of
# an UPDATE statement.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix rowvalue7
do_execsql_test 1.1 {
CREATE TABLE t1(a,b,c,d);
CREATE INDEX t1x ON t1(a,b);
INSERT INTO t1(a,b,c,d) VALUES(1,2,0,0),(3,4,0,0),(5,6,0,0);
CREATE TABLE t2(w,x,y,z);
CREATE INDEX t2x ON t2(w,x);
INSERT INTO t2(w,x,y,z) VALUES(1,2,11,22),(8,9,88,99),(3,5,33,55),(5,6,55,66);
SELECT *,'|' FROM t1 ORDER BY a;
} {1 2 0 0 | 3 4 0 0 | 5 6 0 0 |}
do_execsql_test 1.2 {
UPDATE t1 SET (c,d) = (SELECT y,z FROM t2 WHERE (w,x)=(a,b));
SELECT *,'|' FROM t1 ORDER BY a;
} {1 2 11 22 | 3 4 {} {} | 5 6 55 66 |}
do_execsql_test 1.3 {
UPDATE t1 SET (c,d) = (SELECT y,z FROM t2 WHERE w=a);
SELECT *,'|' FROM t1 ORDER BY a;
} {1 2 11 22 | 3 4 33 55 | 5 6 55 66 |}
do_execsql_test 1.4 {
UPDATE t1 SET (c) = 99 WHERE a=3;
SELECT *,'|' FROM t1 ORDER BY a;
} {1 2 11 22 | 3 4 99 55 | 5 6 55 66 |}
do_execsql_test 1.5 {
UPDATE t1 SET b = 8, (c,d) = (SELECT 123,456) WHERE a=3;
SELECT *,'|' FROM t1 ORDER BY a;
} {1 2 11 22 | 3 8 123 456 | 5 6 55 66 |}
do_catchsql_test 2.1 {
UPDATE t1 SET (c,d) = (SELECT x,y,z FROM t2 WHERE w=a);
} {1 {2 columns assigned 3 values}}
do_catchsql_test 2.2 {
UPDATE t1 SET (b,c,d) = (SELECT x,y FROM t2 WHERE w=a);
} {1 {3 columns assigned 2 values}}
finish_test
+59
View File
@@ -0,0 +1,59 @@
# 2016-08-22
#
# 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.
#
#***********************************************************************
# Use of row values in CASE statements.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix rowvalue8
do_execsql_test 1.1 {
CREATE TABLE t1(a INTEGER PRIMARY KEY,b,c,d);
INSERT INTO t1(a,b,c,d) VALUES
(1,1,2,3),
(2,2,3,4),
(3,1,2,4),
(4,2,3,5),
(5,3,4,6),
(6,4,5,9);
SELECT a, CASE (b,c) WHEN (1,2) THEN 'aleph'
WHEN (2,3) THEN 'bet'
WHEN (3,4) THEN 'gimel'
ELSE '-' END,
'|'
FROM t1
ORDER BY a;
} {1 aleph | 2 bet | 3 aleph | 4 bet | 5 gimel | 6 - |}
do_execsql_test 1.2 {
SELECT a, CASE (b,c,d) WHEN (1,2,3) THEN 'aleph'
WHEN (2,3,4) THEN 'bet'
WHEN (3,4,6) THEN 'gimel'
ELSE '-' END,
'|'
FROM t1
ORDER BY a;
} {1 aleph | 2 bet | 3 - | 4 - | 5 gimel | 6 - |}
do_execsql_test 2.1 {
CREATE TABLE t2(x INTEGER PRIMARY KEY, y);
INSERT INTO t2(x,y) VALUES(1,6),(2,5),(3,4),(4,3),(5,2),(6,1);
SELECT x, CASE (SELECT b,c FROM t1 WHERE a=y)
WHEN (1,2) THEN 'aleph'
WHEN (2,3) THEN 'bet'
WHEN (3,4) THEN 'gimel'
ELSE '-' END,
'|'
FROM t2
ORDER BY +x;
} {1 - | 2 gimel | 3 bet | 4 aleph | 5 bet | 6 aleph |}
finish_test
+210
View File
@@ -0,0 +1,210 @@
# 2016 September 3
#
# 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 SQL statements that use row value
# constructors.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix rowvalue9
do_execsql_test 1.0.1 {
CREATE TABLE a1(c, b INTEGER, a TEXT, PRIMARY KEY(a, b));
INSERT INTO a1 (rowid, c, b, a) VALUES(3, '0x03', 1, 1);
INSERT INTO a1 (rowid, c, b, a) VALUES(14, '0x0E', 2, 2);
INSERT INTO a1 (rowid, c, b, a) VALUES(15, '0x0F', 3, 3);
INSERT INTO a1 (rowid, c, b, a) VALUES(92, '0x5C', 4, 4);
CREATE TABLE a2(x BLOB, y BLOB);
INSERT INTO a2(x, y) VALUES(1, 1);
INSERT INTO a2(x, y) VALUES(2, '2');
INSERT INTO a2(x, y) VALUES('3', 3);
INSERT INTO a2(x, y) VALUES('4', '4');
}
do_execsql_test 1.0.2 {
SELECT x, typeof(x), y, typeof(y) FROM a2 ORDER BY rowid
} {
1 integer 1 integer
2 integer 2 text
3 text 3 integer
4 text 4 text
}
do_execsql_test 1.1.1 {
SELECT (SELECT rowid FROM a1 WHERE a=x AND b=y) FROM a2
} {{} {} 15 92}
do_execsql_test 1.1.2 {
SELECT (SELECT rowid FROM a1 WHERE (a, b) = (x, y)) FROM a2
} {{} {} 15 92}
do_execsql_test 1.2.3 {
SELECT a1.rowid FROM a1, a2 WHERE a=x AND b=y;
} {15 92}
do_execsql_test 1.2.4 {
SELECT a1.rowid FROM a1, a2 WHERE (a, b) = (x, y)
} {15 92}
do_execsql_test 1.3.1 {
SELECT a1.rowid FROM a1, a2 WHERE coalesce(NULL,x)=a AND coalesce(NULL,y)=b
} {3 14 15 92}
do_execsql_test 1.3.2 {
SELECT a1.rowid FROM a1, a2
WHERE (coalesce(NULL,x), coalesce(NULL,y)) = (a, b)
} {3 14 15 92}
do_execsql_test 1.4.1 {
SELECT a1.rowid FROM a1, a2 WHERE +x=a AND +y=b
} {3 14 15 92}
do_execsql_test 1.4.2 {
SELECT a1.rowid FROM a1, a2 WHERE (+x, +y) = (a, b)
} {3 14 15 92}
do_execsql_test 1.5.1 {
SELECT (SELECT rowid FROM a1 WHERE a=+x AND b=+y) FROM a2
} {3 14 15 92}
do_execsql_test 1.5.2 {
SELECT (SELECT rowid FROM a1 WHERE (a, b) = (+x, +y)) FROM a2
} {3 14 15 92}
do_execsql_test 1.5.3 {
SELECT (SELECT rowid FROM a1 WHERE (+x, +y) = (a, b)) FROM a2
} {3 14 15 92}
do_execsql_test 1.6.1 {
SELECT a1.rowid FROM a1 WHERE (a, b) IN (SELECT x, y FROM a2)
} {15 92}
do_execsql_test 1.6.2 {
SELECT a1.rowid FROM a1, a2 WHERE EXISTS (
SELECT 1 FROM a1 WHERE a=x AND b=y
)
} {3 14 15 92 3 14 15 92}
# Test that [199df416] is fixed.
#
do_execsql_test 2.1 {
CREATE TABLE b1(a TEXT);
CREATE TABLE b2(x BLOB);
INSERT INTO b1 VALUES(1);
INSERT INTO b2 VALUES(1);
}
do_execsql_test 2.2 { SELECT * FROM b1, b2 WHERE a=x; } {}
do_execsql_test 2.3 { SELECT * FROM b1 WHERE a IN (SELECT x FROM b2) } {}
do_execsql_test 2.4 { CREATE UNIQUE INDEX b1a ON b1(a); }
do_execsql_test 2.5 { SELECT * FROM b1 WHERE a IN (SELECT x FROM b2) } {}
# Test that a multi-column version of the query that revealed problem
# [199df416] also works.
#
do_execsql_test 3.1 {
CREATE TABLE c1(a INTEGER, b TEXT);
INSERT INTO c1 VALUES(1, 1);
CREATE TABLE c2(x BLOB, y BLOB);
INSERT INTO c2 VALUES(1, 1);
}
do_execsql_test 3.2 {
SELECT * FROM c1 WHERE (a, b) IN (SELECT x, y FROM c2)
} {}
do_execsql_test 3.3 {
CREATE UNIQUE INDEX c1ab ON c1(a, b);
SELECT * FROM c1 WHERE (a, b) IN (SELECT x, y FROM c2)
} {}
do_execsql_test 3.4 {
SELECT * FROM c1 WHERE (a, +b) IN (SELECT x, y FROM c2)
} {}
do_execsql_test 3.5 {
SELECT c1.rowid FROM c1 WHERE b = (SELECT y FROM c2);
} {}
do_execsql_test 3.6 {
SELECT c1.rowid FROM c1 WHERE (a, b) = (SELECT x, y FROM c2);
} {}
#-------------------------------------------------------------------------
#
do_execsql_test 4.0 {
CREATE TABLE d1(a TEXT, b INTEGER, c NUMERIC);
CREATE TABLE d2(x BLOB, y BLOB);
INSERT INTO d1 VALUES(1, 1, 1);
INSERT INTO d1 VALUES(2, 2, 2);
INSERT INTO d1 VALUES(3, 3, 3);
INSERT INTO d1 VALUES(4, 4, 4);
INSERT INTO d2 VALUES (1, 1);
INSERT INTO d2 VALUES (2, '2');
INSERT INTO d2 VALUES ('3', 3);
INSERT INTO d2 VALUES ('4', '4');
}
foreach {tn idx} {
1 {}
2 { CREATE INDEX idx ON d1(a) }
3 { CREATE INDEX idx ON d1(a, c) }
4 { CREATE INDEX idx ON d1(c) }
5 { CREATE INDEX idx ON d1(c, a) }
6 {
CREATE INDEX idx ON d1(c, a) ;
CREATE INDEX idx1 ON d2(x, y);
}
7 {
CREATE INDEX idx ON d1(c, a) ;
CREATE UNIQUE INDEX idx2 ON d2(x, y) ;
}
8 {
CREATE INDEX idx ON d1(c) ;
CREATE UNIQUE INDEX idx2 ON d2(x);
}
} {
execsql { DROP INDEX IF EXISTS idx }
execsql { DROP INDEX IF EXISTS idx2 }
execsql { DROP INDEX IF EXISTS idx3 }
execsql $idx
do_execsql_test 4.$tn.1 {
SELECT rowid FROM d1 WHERE (a, c) IN (SELECT x, y FROM d2);
} {3 4}
do_execsql_test 4.$tn.2 {
SELECT rowid FROM d1 WHERE (c, a) IN (SELECT x, y FROM d2);
} {2 4}
do_execsql_test 4.$tn.3 {
SELECT rowid FROM d1 WHERE (+c, a) IN (SELECT x, y FROM d2);
} {2}
do_execsql_test 4.$tn.4 {
SELECT rowid FROM d1 WHERE (c, a) = (
SELECT x, y FROM d2 WHERE d2.rowid=d1.rowid
);
} {2 4}
do_execsql_test 4.$tn.5 {
SELECT d1.rowid FROM d1, d2 WHERE a = y;
} {2 4}
do_execsql_test 4.$tn.6 {
SELECT d1.rowid FROM d1 WHERE a = (
SELECT y FROM d2 where d2.rowid=d1.rowid
);
} {2 4}
}
finish_test
+72
View File
@@ -0,0 +1,72 @@
# 2016 June 17
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
set ::testprefix rowvaluefault
do_execsql_test 1.0 {
CREATE TABLE xyz(one, two, thr, fou);
INSERT INTO xyz VALUES('A', 'A', 'A', 1);
INSERT INTO xyz VALUES('B', 'B', 'B', 2);
INSERT INTO xyz VALUES('C', 'C', 'C', 3);
INSERT INTO xyz VALUES('D', 'D', 'D', 4);
CREATE UNIQUE INDEX xyz_one_two ON xyz(one, two);
}
do_faultsim_test 1 -faults oom* -body {
execsql { SELECT fou FROM xyz WHERE (one, two, thr) = ('B', 'B', 'B') }
} -test {
faultsim_test_result {0 2}
}
do_faultsim_test 2 -faults oom* -body {
execsql { SELECT fou FROM xyz WHERE (two, thr) IS ('C', 'C') }
} -test {
faultsim_test_result {0 3}
}
do_faultsim_test 3 -faults oom* -body {
execsql { SELECT fou FROM xyz WHERE (one, two, thr) > ('B', 'B', 'B') }
} -test {
faultsim_test_result {0 {3 4}}
}
do_faultsim_test 4 -faults oom* -body {
execsql { SELECT fou FROM xyz WHERE (one, two) IN (SELECT one, two FROM xyz) }
} -test {
faultsim_test_result {0 {1 2 3 4}}
}
do_faultsim_test 5 -faults oom* -body {
execsql {
SELECT fou FROM xyz
WHERE (one, two, thr) IN (SELECT one, two, thr FROM xyz)
}
} -test {
faultsim_test_result {0 {1 2 3 4}}
}
do_faultsim_test 6 -faults oom* -body {
execsql {
SELECT fou FROM xyz
WHERE (one, two, thr) BETWEEN ('B', 'B', 'B') AND ('C', 'C', 'C') }
} -test {
faultsim_test_result {0 {2 3}}
}
finish_test
+4 -8
View File
@@ -114,26 +114,22 @@ ifcapable {subquery && compound} {
CREATE TABLE t2(a,b);
SELECT 5 IN (SELECT a,b FROM t2);
}
} [list 1 \
{only a single result allowed for a SELECT that is part of an expression}]
} {1 {sub-select returns 2 columns - expected 1}}
do_test select7-5.2 {
catchsql {
SELECT 5 IN (SELECT * FROM t2);
}
} [list 1 \
{only a single result allowed for a SELECT that is part of an expression}]
} {1 {sub-select returns 2 columns - expected 1}}
do_test select7-5.3 {
catchsql {
SELECT 5 IN (SELECT a,b FROM t2 UNION SELECT b,a FROM t2);
}
} [list 1 \
{only a single result allowed for a SELECT that is part of an expression}]
} {1 {sub-select returns 2 columns - expected 1}}
do_test select7-5.4 {
catchsql {
SELECT 5 IN (SELECT * FROM t2 UNION SELECT * FROM t2);
}
} [list 1 \
{only a single result allowed for a SELECT that is part of an expression}]
} {1 {sub-select returns 2 columns - expected 1}}
}
# Verify that an error occurs if you have too many terms on a
+1 -1
View File
@@ -40,7 +40,7 @@ do_test subselect-1.1 {
do_test subselect-1.2 {
set v [catch {execsql {SELECT * FROM t1 WHERE a = (SELECT * FROM t1)}} msg]
lappend v $msg
} {1 {only a single result allowed for a SELECT that is part of an expression}}
} {1 {row value misused}}
# A subselect without an aggregate.
#
+26 -3
View File
@@ -25,6 +25,7 @@
# copy_file FROM TO
# delete_file FILENAME
# drop_all_tables ?DB?
# drop_all_indexes ?DB?
# forcecopy FROM TO
# forcedelete FILENAME
#
@@ -1289,9 +1290,9 @@ proc explain_i {sql {db db}} {
set D ""
}
foreach opcode {
Seek SeekGe SeekGt SeekLe SeekLt NotFound Last Rewind
Seek SeekGE SeekGT SeekLE SeekLT NotFound Last Rewind
NoConflict Next Prev VNext VPrev VFilter
SorterSort SorterNext
SorterSort SorterNext NextIfOpen
} {
set color($opcode) $B
}
@@ -1312,9 +1313,15 @@ proc explain_i {sql {db db}} {
set bSeenGoto 1
}
if {$opcode=="Once"} {
for {set i $addr} {$i<$p2} {incr i} {
set star($i) $addr
}
}
if {$opcode=="Next" || $opcode=="Prev"
|| $opcode=="VNext" || $opcode=="VPrev"
|| $opcode=="SorterNext"
|| $opcode=="SorterNext" || $opcode=="NextIfOpen"
} {
for {set i $p2} {$i<$addr} {incr i} {
incr x($i) 2
@@ -1338,6 +1345,12 @@ proc explain_i {sql {db db}} {
}
set I [string repeat " " $x($addr)]
if {[info exists star($addr)]} {
set ii [expr $x($star($addr))]
append I " "
set I [string replace $I $ii $ii *]
}
set col ""
catch { set col $color($opcode) }
@@ -1939,6 +1952,16 @@ proc drop_all_tables {{db db}} {
}
}
# Drop all auxiliary indexes from the main database opened by handle [db].
#
proc drop_all_indexes {{db db}} {
set L [$db eval {
SELECT name FROM sqlite_master WHERE type='index' AND sql LIKE 'create%'
}]
foreach idx $L { $db eval "DROP INDEX $idx" }
}
#-------------------------------------------------------------------------
# If a test script is executed with global variable $::G(perm:name) set to
# "wal", then the tests are run in WAL mode. Otherwise, they should be run
+1 -1
View File
@@ -333,7 +333,7 @@ ifcapable subquery {
test_boolset types2-8.8 {o IN (SELECT t FROM t4)} {7}
test_boolset types2-8.9 {i IN (SELECT o FROM t4)} {9 10 11 12}
test_boolset types2-8.6 {n IN (SELECT o FROM t4)} {9 10 11 12}
test_boolset types2-8.7 {t IN (SELECT o FROM t4)} {9 11}
test_boolset types2-8.7 {t IN (SELECT o FROM t4)} {}
test_boolset types2-8.8 {o IN (SELECT o FROM t4)} {9 10}
}
+2
View File
@@ -37,6 +37,8 @@ set extras {
UMINUS
UPLUS
REGISTER
VECTOR
SELECT_COLUMN
ASTERISK
SPAN
SPACE