Compare commits

...

102 Commits

Author SHA1 Message Date
drh e5dea28482 Prevent subtype values from slipping across a subquery boundry when the
subquery is implemented as a co-routine.

FossilOrigin-Name: 9e51a6c0fbfb1899b2b01888430125fba6d4da9bad9eeaa3ad41e29fca54bbe5
2022-06-09 17:17:14 +00:00
drh 8878f8a8d3 The subtype of a value should not propagate across a subquery boundary.
Proposed fix for the problem reported by
[forum:/forumpost/3d9caa45cbe38c78|forum post 3d9caa45cbe38c78].
Additional works is needed as not all cases are covered.

FossilOrigin-Name: 08af1fe27ebd0edf6e0f1ac477deea033e7f7c813f1016b75196836daf02d2e4
2022-06-09 16:19:01 +00:00
drh 0483668007 Move an #ifdef in shell.c to avoid a harmless "unused function"
compiler warning.

FossilOrigin-Name: 5abb5ef500f52c52dac33e54d824cf89481fec1643c27943f34f0ca4560a7e00
2022-06-08 18:29:23 +00:00
dan 0a841a2b85 Avoid zeroing the value returned by sqlite3_changes() when a DML statement is automatically reprepared in sqlite3_step().
FossilOrigin-Name: 09c8f9f1970cd5b369d98a2b38f0b04d44ed095cb0bda80f7968bb6be4e0263b
2022-06-08 18:20:36 +00:00
drh 8a5469b6f5 Improved comments on the new query flattener restriction of the previous
check-in.  Also a NEVER() macro on an unreachable branch.

FossilOrigin-Name: 8c9e2d6315fde014bd6e25a7fa58ba054f5328c8a56e9d3d1c2d069c025a1b03
2022-06-08 17:48:14 +00:00
drh 40357f0d5d Fix the query flattener so that it refuses a flattening that might leave both
an inner-join and outer-join ON-clause constraint (or equivalent) on the same
term of the FROM clause.

FossilOrigin-Name: f6c4fb48b65c2e8659aa0a1119c330e8bad5e42b2db2030850bfc9c04afef5c8
2022-06-08 15:38:16 +00:00
drh 5c118e3946 Add restriction (29) to the query flattener - do not allow flattening that
would leave both EP_InnerON and EP_OuterON constraints on the same join term.

FossilOrigin-Name: c585d6a4678b04f4cedc08852d01c44cdf52ae2c8ccd1174c3d5a395088bf528
2022-06-08 15:30:39 +00:00
drh a341bae948 New test cases refute check-in [3f45007d544e5f78].
FossilOrigin-Name: 12d3c96c39d12f6b01b77456eb874fff445ebfc60f50aada51b06ed37af364ea
2022-06-08 13:13:12 +00:00
drh 093dd41e97 Do not allow a partial index scan on the left table of a RIGHT JOIN, because
since the index is partial, some rows will be omitted from the scan, and
those rows will subsequently be picked up by the no-match logic in the
right-join post-processing loop.
[forum:/forumpost/c4676c4956|forum post c4676c4956].

FossilOrigin-Name: 615c0026119f7870c3b6ef9dcb57ce4ecf5acedea3e2b5cfc25aa450eb8f17a0
2022-06-08 12:46:58 +00:00
drh f6bee94e22 Minor fix to test/index9.test so that it can be invoked with other
index tests using a wildcard.

FossilOrigin-Name: ccbd6e774903f9202f9311954979f6fcc3b13eba1f3402a549a7aee6ed1d55eb
2022-06-08 12:35:39 +00:00
drh 644817721e Update the routine that determines whether or not a partial index can be used
so that it is not specific to LEFT JOIN.

FossilOrigin-Name: 5a107fd7fa01554d73fefc0611e5797b8c23e782ce0df3aeba7e2f288675b2ce
2022-06-08 12:20:49 +00:00
drh 33b2cb9aec Fix the query flattener to deal with a RIGHT JOIN corner case described
by [forum:/forumpost/323f86cc30|forum post 323f86cc30].

FossilOrigin-Name: 3f45007d544e5f787d5837b4d9f484ba473d69cdba83c229228e9c2f6b972b75
2022-06-07 13:09:58 +00:00
drh bb1369037b Test cases for [forum:/forumpost/323f86cc30|forum post 323f86cc30] added
to join8.test, though commented out so that they do not (yet) run.

FossilOrigin-Name: 7f97cb67d01a11f1b7e5b5f05824f9adbc6e4689d1809d60ceda50a595ba6d4a
2022-06-07 11:02:43 +00:00
drh d580bea7d4 Preserve the database encoding on the CAST operator in the
sqlite3ValueFromExpr() routine.
[forum:/forumpost/800eecf5e6cdc3f4|Forum thread 800eecf5e6cdc3f4].
Test case in TH3.

FossilOrigin-Name: 3f6a442099b8264cc788e8aa2b12cc583439a5263c4fe433fd22b7af1be2458e
2022-06-07 10:14:22 +00:00
drh 6fdac75127 More precise determination of when a WHERE clause can be used to drive an
index on an outer join.

FossilOrigin-Name: 318543a91108c6fd0764d7ee7beee49838f54d44c54dae3dc9d4cb8336d52fe1
2022-06-06 23:22:06 +00:00
drh 16828c8c12 More precise determination of when a WHERE/ON clause term can be used as an
indexed join constraint.  Testcase macros added to ensure test coverage.

FossilOrigin-Name: f419e98c515e704efa11728192b2594e962cb0e6d2d1837f99a8caf65f4d93cb
2022-06-06 22:33:44 +00:00
dan 93c4087ffe Extra test cases for outer joins.
FossilOrigin-Name: c7505765280fb27ec2c8c4da6f87229a4078e3a4a27525226b6bf55864caa04b
2022-06-06 21:08:27 +00:00
drh c18fc6134e Do not allow a WHERE clause constraint to be used to drive an index for
the right operand of a RIGHT JOIN, since this can cause problem if the
constraint implies a not-NULL value for one of the columns for the left
operand of the same join.  See
[forum:/forumpost/206d99a16dd9212f|forum post 206d99a16dd9212f].

FossilOrigin-Name: 4a31b7942a15c9c4363477365784d6d4ac5b1bbe8ff8aeaf2dd3d6532bf8bc96
2022-06-06 15:27:42 +00:00
larrybr 8a4d2bc34f In CLI, drop .dbinfo command when build options prevent it from working.
FossilOrigin-Name: 4b50a55165f4603674a250df89cfc5ac1fef8aabaf433df8400df14d53bfbbe0
2022-06-06 15:22:11 +00:00
larrybr 0dbb3db431 Sync w/trunk, pickup fts3corrupt6.test change.
FossilOrigin-Name: 268072f636b25c24ec47e34f2a8356f466ec200b80487b1041e89e0ae909c8bd
2022-06-06 14:00:41 +00:00
stephan 8fca1c7044 fiddle: replaced the huge file selection widget with a smaller proxy button.
FossilOrigin-Name: f7c17449ea0685e322f50abe7a59b7afcde0f7e38e03645ec5b13753a6d0dcf3
2022-06-06 06:29:09 +00:00
dan fd80fa9b6d Fix a test case in fts3corrupt6.test. Was failing due to [f1dfbc4f].
FossilOrigin-Name: 04b2e47784238008ce9057e2761ded91d895dc16d3bc8a3a3508df98a4769602
2022-06-06 06:17:54 +00:00
stephan 626aa48c26 shell: in WASM mode, permit ATTACH because the filesystem is a virtual sandbox and ATTACH can be used to provide more import/export options. Minor doc updates in sqlite3-api.js.
FossilOrigin-Name: f28de5b726999b913b442fa51355d020ba1f1662d2f7978380623c16438eb238
2022-06-06 04:09:44 +00:00
larrybr f3d6e8fd6e In CLI, drop .dbinfo command when build options prevent it from working.
FossilOrigin-Name: 2512d2f68dfff4d74c881a4102b63ca528e690ad93ab2b4eb1f03ce1d9ac155c
2022-06-05 22:58:40 +00:00
larrybr b61c2424d7 Create new branch named "dbinfo-guard"
FossilOrigin-Name: 8d942ba72ea995d0e8d1f8694bb428e5f919fc75afd3e059a69547e27991d580
2022-06-05 22:40:49 +00:00
dan 93a4b411ef Fix to test case in fts3corrupt.test. No changes to source code.
FossilOrigin-Name: d18818afc6021a32989499c26ab38fa442e5a55e662e39bb06e5c0daa9c65e25
2022-06-03 14:08:40 +00:00
dan 181d75ef9e Fix a 1-byte overread in fts3 that could occur when processing corrupt records.
FossilOrigin-Name: f1dfbc4f7452154ca5190ac22e17fdabebb92833967b11f7dae21b8d3a5857bd
2022-06-03 13:52:53 +00:00
dan 410fac35d7 Fix a problem with flattening and window functions causing an "IS <column>" to be transformed to "IS TRUE" or "IS FALSE" when <column> is a view or sub-select expression that is the literal value "TRUE" or "FALSE".
FossilOrigin-Name: 2a952c7738d94e70024e06600fee8c3a49f317f2d02774468019bd7cf9488c8b
2022-06-02 16:26:21 +00:00
mistachkin bf154369e7 Avoid a direct use of abort() in debug builds to prevent a compilation issue for Windows CE.
FossilOrigin-Name: 8eb9a7dd07afc0aef0b7c67054d73e7e821138867d115407b71c985e90d44d59
2022-06-02 02:45:59 +00:00
drh 486191603f New test cases for row values and RIGHT JOIN.
FossilOrigin-Name: 5f0582b90fab9111e4a645c692549c35480e62635c596f8c7fa3466bd22f7235
2022-06-01 20:08:22 +00:00
drh f89f4efb28 Ensure that subqueries associated with row-values are invoked before being
used when processing a RIGHT JOIN.  Fix for the problem described by
[forum:/forumpost/087de2d9ec87305b|forum post 087de2d9ec87305b].

FossilOrigin-Name: 5a9465dcc0c23fc2c66cd4898bcdfd5086fe4c71ec19a95db7221fdf7c0bbbbd
2022-06-01 16:05:25 +00:00
stephan 4e5aeb54f2 fiddle: added another UI element to the list of those which are disabled during long-running activities. Added DB.close() binding to the Worker-based wasm binding.
FossilOrigin-Name: 5933163ed1a8f996e81023c7c5822655dc6411d30016f37fe8863f760530dc5b
2022-06-01 16:04:29 +00:00
drh a5d06a3d54 Ensure that all necessary row-value subqueries are invoked while running
the right-join post processing.

FossilOrigin-Name: e4e71b4cd4c026fc36f0da1703e6237e6ddd8ff2e08dc3c0011632b9e63098cd
2022-06-01 15:53:11 +00:00
stephan 23aa8ff4de Minor tweaks to, and consolidation of, the wasm-related build flags.
FossilOrigin-Name: f0ad6b1b324f9c0f4340f6cf9584884d1206b56fe1010db43bcbef324e713ea3
2022-06-01 14:52:23 +00:00
dan f74f4ed5b5 Fix a minor problem in the Tcl "incrblob" command. This does not affect the SQLite core.
FossilOrigin-Name: e96feccc21d9b858f076960e029b615809243d0cc3f95db75180feb01887451f
2022-06-01 14:32:05 +00:00
drh 0f4b534b60 Candidate fix for the problem with row-value WHERE clause constraints on
a RIGHT JOIN.

FossilOrigin-Name: fdd782a7bb217e2aceda97a588c0d7874ee19885abfcf0658472d6c3387da6ee
2022-06-01 13:32:47 +00:00
drh 6d013d890e Only include the code for sqlite_offset() if compiled with
-DSQLITE_ENABLE_OFFSET_SQL_FUNC.

FossilOrigin-Name: 382cd7e10b2df4216da8f25b55546bc74a963d9834af240f75ec8b8321ba106e
2022-06-01 13:32:05 +00:00
drh 1324b2830e Fix a typo in a comment in a test case. No changes to code.
FossilOrigin-Name: 570efa45dd4c6848dcbd550f78fcaa00b6d84dcffe0a2d12e65ea8ee4468c0e8
2022-06-01 13:01:57 +00:00
drh 0286cdbca6 Additional enhancements to the test cases.
FossilOrigin-Name: 55cfad74cace74e541b354228e3ad46e17028213a9f639edf2007c6a8f3ba8fc
2022-06-01 11:58:11 +00:00
drh 47b4969e2d Fix test cases in the previous check-in to make them postgres-compatible.
FossilOrigin-Name: f223ba72b6e02a7ccfd894b4b135f4609b1eb79f547ec279b1c52a6996cd5a73
2022-06-01 11:46:39 +00:00
drh 36c19f7799 Test case for [forum:/forumpost/087de2d9ec|forum post 087de2d9ec] showing
a problem when a row-value constraint is used with RIGHT JOIN.

FossilOrigin-Name: d1e3e0d26e302587a6cfcb7aa4fcdefe2b3891037732a0df0d0fe10a14454e00
2022-06-01 11:43:57 +00:00
stephan ea7a4e4fb3 The Worker-specific variants of the most significant DB-class JS bindings are implemented, most notably various uses of DB.exec().
FossilOrigin-Name: 371d6f7497016ca9bf82c8524b4d701ddf1e614b3fb4e69ff63935da0d51ed05
2022-06-01 11:20:07 +00:00
drh 645682a7c7 Move the sqlite_offset() function implementation to be an in-line function,
thereby avoiding special case code and freeing up a bit in the
FuncDef.flags field.

FossilOrigin-Name: 1c9812c458bd229c862efe5df1b64fae333da9871c8756b5ae4605a81bcda4b5
2022-06-01 11:05:59 +00:00
stephan 6ffa895884 Initial proof of concept code for a JavaScript binding which runs in the main window thread but acts on a db handle running in a Worker thread. Expanded the DB.exec() and DB() constructor to simplify certain use cases.
FossilOrigin-Name: d9efe3e92d1c95aee6f5ae37a8ba28d8cf4891d746744ce4aa2464f766821a0b
2022-06-01 08:09:06 +00:00
stephan bff17db433 Initial bits for a JS API variant in which the client operates in the main thread and sqlite3 in a Worker. This is far from complete.
FossilOrigin-Name: f6d6f969791f0d2367ae5418623b4794f6df657d9d7d9002fb5aec4206dcfd4c
2022-06-01 00:00:59 +00:00
drh c7fc08f69a New FULL JOIN test cases. No changes to code.
FossilOrigin-Name: 13e89ef6649475815d3f4e4aef73a4be1157dd388e55c7f856faeb4b7387774b
2022-05-31 18:18:09 +00:00
drh aa03c69591 More JOIN test cases inspired by the problem fixed in [3869fd9a2b9483cb].
FossilOrigin-Name: f2d224c5fa06de70f6f22e159a3b7065d4c6b004f9accc13004b9ac1f2fd5549
2022-05-31 17:24:56 +00:00
drh f51672a945 Fix an assert that is incorrect for a corrupt database.
FossilOrigin-Name: fe0a840805b435efd38a953a8aae60e29a35289f9d9955472a8bab2bce7051c8
2022-05-31 15:18:55 +00:00
drh d69820c42b Many new PG-confirmed test cases in joinD.test.
FossilOrigin-Name: 481e89c764ad8906cd21c8fbe58df57ae40bb3f261fa4892ea7ce7762786ad26
2022-05-31 14:19:04 +00:00
drh 9f67cc26c1 Restore an ON-clause test case (removed by [3869fd9a2b9483cb]) that turns out
to be necessary for correct operation.  Fix for
[forum:/forumpost/c2554d560bac97c5|forum post c2554d560bac97c5].

FossilOrigin-Name: f407ec678f5bf5bfd20b7ba596472c9a85e6f21383a4cde49af1dae542fff582
2022-05-31 11:22:30 +00:00
drh c9dfe2b808 Add back in a test case for ON-clauses that was removed yesterday by
[3869fd9a2b9483cbf] but which turned out to be necessary after all.

FossilOrigin-Name: aba52a90eef2a5d06e7c499fe84b2efcde7f01e67129058f8223a6a0272aacb9
2022-05-31 11:13:55 +00:00
drh b1f08bc690 Add test cases for
[forum:/forumpost/c2554d560bac97c5|forum post c2554d560bac97c5].

FossilOrigin-Name: e66d5ae86c09cbe581b8f4f1bff33376cc680bba3da24f85384dd6c3eb85ddcf
2022-05-31 10:48:02 +00:00
stephan 5fd9f03291 Updated the wasm builds to generate gzip'ed copies of relevant files to take advantage of althttpd's new capability of substituting gzip files in place of requested files. This cuts over-the-wire size of the fiddle app by more than half.
FossilOrigin-Name: 40925585f1dab25babac46a0f4b9d633315110b8d4ccb04cc6df115d167e5ab4
2022-05-31 02:03:29 +00:00
drh 404bf6bac6 For an outer join, then ON-clause constraints need to be evaluated at just
the right moment - not too early and not too late.  Fix for the problem
reported by [forum:/forumpost/3902c7b833|forum post 3902c7b833].

FossilOrigin-Name: 3869fd9a2b9483cbbf83d8f369c1744abc42f12e63abba402be35dd7e136161c
2022-05-30 17:33:22 +00:00
drh 1943005f62 Apply the UPDATE-FROM file from check-in [98b3816bbaf539ea] to
update-delete-limit builds.

FossilOrigin-Name: 7e87892c249f023ee9ed1d5f75a9ad8db10fb38f14dd9e6954b12b9b28400b07
2022-05-28 14:03:23 +00:00
stephan 1a66ff359f fiddle: changed the internal key of the session/local storage to avoid conflicts with the pikchr fiddle app (which is derived from this one) when running from the same HTTP origin as an instance of that app in a dev environment.
FossilOrigin-Name: fd668da5ccf037c2ad8e61e381dd1eb398a8deab42a00593e551c30bd176890d
2022-05-28 11:59:46 +00:00
stephan e9ab04a446 fiddle: corrected a piece of far-corner-case error handling and made the various checkbox config options persistent.
FossilOrigin-Name: 2ba429a4f8300b981b23d54c2bdb54bd4863522c1c18bf9a67a82e3dce845b10
2022-05-28 11:29:00 +00:00
drh 5af8a86d62 Mark an always-true conditional as ALWAYS().
FossilOrigin-Name: 3492fe8a212cbe02b9016866e2499b99c3b566a4b0bc91fba267e6e1fe1b8943
2022-05-27 18:06:49 +00:00
drh abe1ff38dd Minor simplification to ALTER TABLE logic.
FossilOrigin-Name: 01beb0365c529481605f1864b1b6760e2484fad08d56a72e00e34acff37e23f8
2022-05-27 17:36:21 +00:00
stephan 23b34bbc25 Corrected a broken HTML comment (was using a C-style comment closer).
FossilOrigin-Name: db742e3e7d1caeff8d9df1d86abf54fcb2f2263db7a433ffacf3cd3777e533c5
2022-05-27 17:13:56 +00:00
dan ca29bbce2f Fix another problem with ALTER TABLE and UPDATE...FROM in triggers.
FossilOrigin-Name: 33cf12235e6469ba17cfb72cef0e480dfd0dea81ed412fb1fb24b05dbeb8dc02
2022-05-27 15:33:51 +00:00
dan 4209d553e7 Fix further issues with ALTER TABLE and triggers that contain UPDATE..FROM statements.
FossilOrigin-Name: 53fbc269ddbabc4a97d297e881e5f9cd2bfbcd24af4af1b7cf9db412a3a51813
2022-05-27 15:04:43 +00:00
drh 8d2f661ab8 Omit an unnecessary branch.
FossilOrigin-Name: f56473f9cc1b476b3219fe5b70ba2e4b269b20c880b99ba34c744ed893042081
2022-05-27 14:41:48 +00:00
stephan cd69784646 fiddle: added an option to completely wipe/reset the db. The Options area can now be toggled on/off via tapping the label at its top. Running the input SQL is now limited to the selected area if any text is currently selected, per suggestion in the forum.
FossilOrigin-Name: d100283e378f2d8e353399848184a4ab8ccf6316218fffc28b90a656cf81c279
2022-05-27 03:27:10 +00:00
dan b8bbe3e2db Fix issues with ALTER TABLE and triggers containing "UPDATE...FROM" statements.
FossilOrigin-Name: 2fba0d41b781d226915fa2bf888a7bc640c046ce22670ceb53f62a09f3975259
2022-05-26 19:10:11 +00:00
drh 26c4df0fb0 Correct handling of outer joins in the FROM clause of an UPDATE statement
that occurs inside of a trigger.  Follow-on to [98b3816bbaf539ea].

FossilOrigin-Name: 664a49fa813144b6fa5a7ae3f65af5412f150dd5def261c4d581d706b39f7846
2022-05-26 17:33:42 +00:00
drh e91bde4655 Clarification of the meaning of the N parameter in sqlite3_db_name().
Documentation only.  No changes to code.

FossilOrigin-Name: f22f41d2c8a676b9a339e7f00c29c163bbb7079b1a4a76ee1a6a96aaf7de5f9e
2022-05-26 14:46:09 +00:00
stephan 78907bba3e Fixed a minor cut/paste mistake in the previous checkin. Affects only internal docs.
FossilOrigin-Name: 37e3764839b968456f576fad67d3d99007773f5c0689165ecbdc610fcabef1ca
2022-05-26 05:18:33 +00:00
stephan c121e087f3 Makefile.in: added explanations, intended for maintainers and hackers, of the various emcc flags used for building the wasm files. No code or build rule changes.
FossilOrigin-Name: 1a159159094d6357b3cadbb8e5499cec4de35ef382c03fcc45c11daee906c3d6
2022-05-26 05:08:25 +00:00
drh 724e298e9c Slight increase in the accuracy of log10().
FossilOrigin-Name: c48a735bd4a1dbd541aed5937c25fc0c606c4263d8ee94cae30a06b1a7b26d9a
2022-05-25 13:10:29 +00:00
drh 7bfbd2508c Do not allow the right operand of a RIGHT JOIN to be reordered with other
FROM clause terms that are even further to the right.  Fix for the issue
identified by [forum:/forumpost/5cfe08eed6|forum post 5cfe08eed6].

FossilOrigin-Name: 4be0c60e38edc5d5bfd72bb35a3c91c55240b4e6313a40614beb60f1ab9d9f4c
2022-05-25 12:49:59 +00:00
drh 361e0ede47 Add in a VdbeCoverage() macro accidentally omitted from [d64ae49a1f251317].
FossilOrigin-Name: 18b5cea0392a28f694b8931a80e93518f8d3d297d787fa44d1544e373f76838e
2022-05-25 11:17:13 +00:00
drh ddcdf0b0dd Fix the UPDATE-FROM logic so that it works the same as PostgreSQL when the
FROM clause contains an outer join.

FossilOrigin-Name: 98b3816bbaf539ea745456e1c0064e47d2903d33ee0ceb029bdb97d56fcde937
2022-05-25 11:09:07 +00:00
stephan 26542e722b Got the sqlite3-api JS bits wrapped up in deferred-load module. Whether that's going to be easier to use, in practice, remains to be seen. Consolidated two of the test-related JS files.
FossilOrigin-Name: dd83cc05f2522d221641807dd66b33df48ac9264f27e5b6f63f312084f109801
2022-05-25 08:51:07 +00:00
stephan e599cc427f Renamed EXPORTED_FUNCTIONS.sqlite3 to EXPORTED_FUNCTIONS.sqlite3-api to avoid any potential confusion about that file (not) being an sqlite3 database.
FossilOrigin-Name: 3d6245c6f9f2ef4ca6746639d300cc5795598b119034439dfed671de3da638fb
2022-05-25 04:38:35 +00:00
stephan 085c5c6b43 Add the ability to import/export db files into/from fiddle.
FossilOrigin-Name: e0c30438a4f1372afb93a0488bae17b3f85d535717b215f494a83ae909871d2c
2022-05-25 04:35:22 +00:00
stephan d0b22b81c8 Further minor cleanups and docs in the fiddle app and worker.
FossilOrigin-Name: 199e01799dfa48e3fddafb7f2ae5360604150a44186d5c5a977e158ad8e7e657
2022-05-25 04:20:08 +00:00
stephan 79669c25c4 fiddle: refactored so that it no longer exposes any global symbols. Doing so with the main sqlite3.api module will be much tricker.
FossilOrigin-Name: cd227be805d0cd4b6e3c72ed0992ad3aec3db9c366909d9d82c6d3a29009c6eb
2022-05-25 03:08:22 +00:00
drh fb98dac04f An attempt to get UPDATE FROM working when the FROM clause contains a
RIGHT or FULL JOIN.

FossilOrigin-Name: a124e4f96f883d8682ba7a253d33a9565ed0fc3580525225b95733bd3782a806
2022-05-25 02:32:11 +00:00
stephan 80bf86967f fiddle: added support for exporting (downloading) the current db file. To do this we had to fall back to named dbs, instead of defaulting to an in-memory one, but the virtual filesystem is an in-memory FS, so the end effect is the same.
FossilOrigin-Name: 7c7fd34c8a05832a3973aaffe696250cb4d2a0b1646c9bfbe83970daf33cd817
2022-05-24 22:16:12 +00:00
stephan de1e02ee52 fiddle: initial work on loading a client-side db file. Works but requires some cleanup. Export is not yet implemented.
FossilOrigin-Name: 0fa8378c006fcf2311772d36cf2e3c2cd8e8648f671de89ee9832e2e1a06ef49
2022-05-24 19:01:21 +00:00
drh 6af03b469e When an ON clause on an INNER JOIN references a table to the right of
of the join, just convert the ON clause to an ordinary WHERE clause term,
in order to be compatible with older versions of SQLite.  See
[forum:/forumpost/687b0bf563a1d4f1|forum thread 687b0bf563a1d4f1] for details.

FossilOrigin-Name: 2b6ebba26d936ae7b9acf7d4bd15e82cbfabda22e1044b3dd838c7b07095100e
2022-05-24 16:05:41 +00:00
stephan 73c6ad1920 MoUse re descriptive var names in ext/fiddle/Makefile.
FossilOrigin-Name: 2f9a42fb141d386f6edd03a37da3b0cef63dcc9fbfd076076b5330a8aa7d45a8
2022-05-24 14:45:16 +00:00
stephan 400ee2ecef fiddle: lots of generic refactoring, restructuring, and cleanup in the higher-level code. Added push-fiddle ext/fiddle/Makefile target to push the fiddle app to a remote server via rsync.
FossilOrigin-Name: ed19fef3459499abb0a4a010f368b4576d6e068d930c8480446ea677ac87c1c1
2022-05-24 14:36:45 +00:00
stephan 2f6a729d55 wasm/JS: minor doc updates, corrected bind()ing of the undefined value to behave as documented, removed some superfluous code.
FossilOrigin-Name: 526c8c728019b317624a93f6f07840ca524bca84e7c03ce5e86e38953146236f
2022-05-24 01:15:21 +00:00
stephan 64f0e9376b wasm/JS: documented DB.selectValue() and corrected the fetching of NULL columns via Stmt.get().
FossilOrigin-Name: 70f91fab825d365f505750acdb8d3ae532880c4cdb64d1e61bb21b24a115958b
2022-05-24 00:35:18 +00:00
stephan a240a24ad0 wasm/JS: added support for scalar UDFs. Fixed a deallocation problem with bind()ed strings/blobs.
FossilOrigin-Name: 325a9ee31ad7abae563c4da5cd8228e151b00aa9afcac7e9bca5efaa9d48e107
2022-05-24 00:22:10 +00:00
stephan e145136a4e wasm: minor refactoring and doc updates.
FossilOrigin-Name: 6044605b2a712da73600cabb967797a03ed1915dc0ab0b10edbd52525e548196
2022-05-23 19:38:57 +00:00
stephan 325b56b504 fiddle: cleaned up and documented the status-loading progress mechanism in prep for reusing it in the sqlite3-api worker.
FossilOrigin-Name: 107e3497869d757265f2a4235082bf324ba1220075d1096c2a82021a5d348a6c
2022-05-23 16:54:18 +00:00
stephan dc5888e04a wasm: added missing 'use strict' and fixed an undeclared var use caught by that.
FossilOrigin-Name: c16a7f4950d47c2f5177db7dc5d83f0f11eb0cafdce1ec688d6f1bd740d92733
2022-05-23 13:55:39 +00:00
stephan 192201d2ed wasm: corrected isSupportedBindType() to behave as documented.
FossilOrigin-Name: 7e2d2e807272e98e9a3c9c9ba492b796a603f36b7cc12b16923cd8a9e6579851
2022-05-23 13:52:36 +00:00
drh ea488b1258 New test cases to further demonstrate the difference in name matching rules
for LEFT JOIN versus RIGHT JOIN that protect legacy behavior.
[forum:/forumpost/e90a8e6e6fa652ac|Forum thread e90a8e6e6fa652ac].

FossilOrigin-Name: 2c586060a016f5481bad29bfb8f4ac4e1204075afdd0fa1851fbbcf2abda4893
2022-05-23 12:37:54 +00:00
drh 51e5d447ba Add an assert() to clear a harmless static-analyzer warning.
FossilOrigin-Name: 919ba2f0472e12c2d1e82364c1481e778b24ea406615b08992964a4eb80abee7
2022-05-23 11:51:10 +00:00
drh 43baa1de76 Do not run the subroutine that materializes a view or subquery until just
before the materialization is actually needed, so that if the materialization
turns out to never been needed, unnecessary work can be avoided.

FossilOrigin-Name: d64ae49a1f2513171d678259928c67741d79b2ae595078299525641a676cc46b
2022-05-23 10:57:20 +00:00
stephan 71eacead76 WASM: removed the in64-related bindings, as MDN says that calling a wasm function which has an int64 type in its signature will currently throw because JS has no 64-bit integer support. Those bindings now use doubles and simply hope that the user doesn't exceed their integer precision (2^53-1, approx 9 quadrillion).
FossilOrigin-Name: 392e84828275ec203bc713d3a5d4790852add57539add6b29b5f6de1da2dc97a
2022-05-23 01:11:49 +00:00
stephan c21c0e2af9 WASM: an arg handling fix for DB.exec({multi:true...}).
FossilOrigin-Name: 0d6332f706ec5c34cc6a9ff79878f4e10a9ad81b24cc7d743b52168586285811
2022-05-22 22:10:38 +00:00
stephan 744a65cf7d WASM: added exec(), execMulti(), and several getters. Various touchups and fixes.
FossilOrigin-Name: b790c91b85e9cf8eecce86ac1717e8ccd2c3b6b98a1ad6a5d64eefc94ee86f9d
2022-05-22 22:00:39 +00:00
stephan f6868562ce Implemented Stmt.get() and friends for WASM OO #1 wrapper. Added basic tests for prepare/step/get. Restructured module init output to introduce only 1 global-scope symbol instead of 2.
FossilOrigin-Name: 601dc3fa29c2ce2ede5a8320c79050305f3774b6d7bc759247c5021f3b74aaec
2022-05-22 19:09:59 +00:00
stephan 40b5b19a0f WASM: added bindings for sqlite3_compileoption_get/used(), moved OO #1 into sqlite3-api.js since it can only be used from the same thread as that API and separating them complicates client-side use. Started adding test utilities and tests for the OO1 API.
FossilOrigin-Name: f3bc0328c87cac7d50513b0f13576d8fe7b411396f19c08fbe7e7c657b33cfbf
2022-05-22 16:25:43 +00:00
stephan d60b7275c3 WASM OO wrapper #1: prepare() and bind() APIs are in place but are untested, pending fetch/get APIs.
FossilOrigin-Name: 84c8f63a1c446331a3afe52b0c8bdfa6980f24aa4cf600f576877fef5e650c39
2022-05-22 14:07:44 +00:00
stephan 166542cc98 Build refactoring for the fiddle/wasm bits. Set up wasm binding of a chunk of the core C API and added some infastructure for creating test pages for it.
FossilOrigin-Name: dea098b64eb95c395b346ebcae687afe42b7d21df48833527808c02226300a66
2022-05-22 00:27:19 +00:00
stephan 4ec29fc115 Minor fiddle-related build restructuring to support upcoming development of the C-style wasm sqlite3 interface, plus some commentary about the plans and goals for that.
FossilOrigin-Name: c7cfdd4c3682659352642461d3307bf8180703b121ec1802ba5881f8e1ef9809
2022-05-21 21:13:44 +00:00
58 changed files with 39595 additions and 2300 deletions
+119 -19
View File
@@ -1515,31 +1515,131 @@ sqlite3.dll: $(REAL_LIBOBJ) sqlite3.def
#
# fiddle section
# fiddle/wasm section
#
fiddle_dir = ext/fiddle
fiddle_dir_abs = $(TOP)/$(fiddle_dir)
# ^^^ some emcc opts require absolute paths
fiddle_html = $(fiddle_dir)/fiddle.html
fiddle_module_js = $(fiddle_dir)/fiddle-module.js
fiddle_generated = $(fiddle_module_js) \
$(fiddle_dir)/fiddle-module.wasm
# fiddle_dummy_exports = ,comma,list of func bound solely for
# experimentation and testing purposes in the WASM build.
fiddle_dummy_exports = ,_fiddle_experiment,_fiddle_the_db,_fiddle_db_arg
clean-fiddle:
rm -f $(fiddle_generated)
clean: clean-fiddle
sqlite3_wasm_js = $(fiddle_dir)/sqlite3.js
sqlite3_wasm = $(fiddle_dir)/sqlite3.wasm
#emcc_opt = -O0
#emcc_opt = -O1
#emcc_opt = -O2
#emcc_opt = -O3
# --js-library $(fiddle_dir)/_dummylib.js
emcc_opt = -Oz
emcc_flags = $(emcc_opt) $(SHELL_OPT) \
-sEXPORTED_RUNTIME_METHODS=ccall,cwrap \
-sEXPORTED_FUNCTIONS=_fiddle_exec,_fiddle_interrupt$(fiddle_dummy_exports) \
$(fiddle_cflags)
# $(fiddle_cflags) is intended to be passed to make via the CLI in
# order to override, e.g., -Ox for one-off builds.
$(fiddle_module_js): Makefile sqlite3.c shell.c
emcc -o $@ $(emcc_flags) sqlite3.c shell.c
fiddle: $(fiddle_module_js)
emcc_flags = $(emcc_opt) -sALLOW_TABLE_GROWTH -sSTRICT_JS \
-sENVIRONMENT=web -sMODULARIZE \
-sEXPORTED_RUNTIME_METHODS=@$(fiddle_dir_abs)/EXPORTED_RUNTIME_METHODS \
-sDYNAMIC_EXECUTION=0 \
-I. $(SHELL_OPT)
$(fiddle_module_js): Makefile sqlite3.c shell.c \
$(fiddle_dir)/EXPORTED_RUNTIME_METHODS \
$(fiddle_dir)/EXPORTED_FUNCTIONS.fiddle
emcc -o $@ $(emcc_flags) \
-sEXPORT_NAME=initFiddleModule \
-sEXPORTED_FUNCTIONS=@$(fiddle_dir_abs)/EXPORTED_FUNCTIONS.fiddle \
sqlite3.c shell.c
gzip < $@ > $@.gz
gzip < $(fiddle_dir)/fiddle-module.wasm > $(fiddle_dir)/fiddle-module.wasm.gz
$(sqlite3_wasm_js): Makefile sqlite3.c \
$(fiddle_dir)/sqlite3-api.js \
$(fiddle_dir)/EXPORTED_RUNTIME_METHODS \
$(fiddle_dir)/EXPORTED_FUNCTIONS.sqlite3-api
emcc -o $@ $(emcc_flags) \
-sEXPORT_NAME=initSqlite3Module \
-sEXPORTED_FUNCTIONS=@$(fiddle_dir_abs)/EXPORTED_FUNCTIONS.sqlite3-api \
--post-js=$(fiddle_dir)/sqlite3-api.js \
--no-entry \
sqlite3.c
gzip < $@ > $@.gz
gzip < $(sqlite3_wasm) > $(sqlite3_wasm).gz
gzip < $(fiddle_dir)/sqlite3-api.js > $(fiddle_dir)/sqlite3-api.js.gz
$(fiddle_dir)/fiddle.js.gz: $(fiddle_dir)/fiddle.js
gzip < $< > $@
$(fiddle_dir)/sqlite3-api.js.gz: $(fiddle_dir)/sqlite3-api.js
gzip < $< > $@
fiddle_generated = $(fiddle_module_js) $(fiddle_module_js).gz \
$(fiddle_dir)/fiddle-module.wasm \
$(fiddle_dir)/fiddle-module.wasm.gz \
$(fiddle_dir)/fiddle.js.gz
sqlite3_wasm_generated = \
$(sqlite3_wasm) $(sqlite3_wasm).gz \
$(sqlite3_wasm_js) $(sqlite3_wasm_js).gz \
$(fiddle_dir)/sqlite3.js.gz \
$(fiddle_dir)/sqlite3-api.js.gz
clean-wasm:
rm -f $(fiddle_generated) $(sqlite3_wasm_generated)
clean: clean-wasm
fiddle: $(fiddle_module_js) $(fiddle_dir)/fiddle.js.gz
sqlite3-wasm: $(sqlite3_wasm_js)
wasm: fiddle sqlite3-wasm
########################################################################
# Explanation of the emcc build flags follows. Full docs for these can
# be found at:
#
# https://github.com/emscripten-core/emscripten/blob/main/src/settings.js
#
# -sENVIRONMENT=web: elides bootstrap code related to non-web JS
# environments like node.js. Removing this makes the output a tiny
# tick larger but hypothetically makes it more portable to
# non-browser JS environments.
#
# -sMODULARIZE: changes how the generated code is structured to avoid
# declaring a global Module object and instead installing a function
# which loads and initialized the module. The function is named...
#
# -sEXPORT_NAME=jsFunctionName (see -sMODULARIZE)
#
# -sEXPORTED_RUNTIME_METHODS=@/absolute/path/to/file: a file
# containing a list of emscripten-supplied APIs, one per line, which
# must be exported into the generated JS. Must be an absolute path!
#
# -sEXPORTED_FUNCTIONS=@/absolute/path/to/file: a file containing a
# list of C functions, one per line, which must be exported via wasm
# so they're visible to JS. C symbols names in that file must all
# start with an underscore for reasons known only to the emcc
# developers. e.g., _sqlite3_open_v2 and _sqlite3_finalize. Must be
# an absolute path!
#
# -sSTRICT_JS ensures that the emitted JS code includes the 'use
# strict' option. Note that -sSTRICT is more broadly-scoped and
# results in build errors.
#
# -sALLOW_TABLE_GROWTH is required for (at a minimum) the UDF-binding
# feature.
#
# -sDYNAMIC_EXECUTION=0 disables eval() and the Function constructor.
# If the build runs without these, it's preferable to use this flag
# because certain execution environments disallow those constructs.
# This flag is not strictly necessary, however.
#
# -sWASM_BIGINT is UNTESTED but "should" allow the int64-using C APIs
# to work with JS/wasm, insofar as the JS environment supports the
# BigInt type. That support requires an extremely recent browser:
# Safari didn't get that support until late 2020.
#
# --no-entry: for compiling library code with no main(). If this is
# not supplied and the code has a main(), it is called as part of the
# module init process. Note that main() is #if'd out of shell.c
# (renamed) when building in wasm mode.
#
# --pre-js/--post-js=FILE relative or absolute paths to JS files to
# prepend/append to the emcc-generated bootstrapping JS. It's
# easier/faster to develop with separate JS files (reduces rebuilding
# requirements) but certain configurations, namely -sMODULARIZE, may
# require using at least a --pre-js file. They can be used
# individually and need not be paired.
#
# -O0..-O3 and -Oz: optimization levels affect not only C-style
# optimization but whether or not the resulting generated JS code
# gets minified. -O0 compiles _much_ more quickly than -O3 or -Oz,
# and doesn't minimize any JS code, so is recommended for
# development. -O3 or -Oz are recommended for deployment, but
# primarily because -Oz will shrink the wasm file notably. JS-side
# minification makes little difference in terms of overall
# distributable size.
########################################################################
+7
View File
@@ -0,0 +1,7 @@
_fiddle_exec
_fiddle_interrupt
_fiddle_experiment
_fiddle_the_db
_fiddle_db_arg
_fiddle_db_filename
_fiddle_reset_db
+51
View File
@@ -0,0 +1,51 @@
_sqlite3_bind_blob
_sqlite3_bind_double
_sqlite3_bind_int
_sqlite3_bind_int64
_sqlite3_bind_null
_sqlite3_bind_parameter_count
_sqlite3_bind_parameter_index
_sqlite3_bind_text
_sqlite3_changes
_sqlite3_clear_bindings
_sqlite3_close_v2
_sqlite3_column_blob
_sqlite3_column_bytes
_sqlite3_column_count
_sqlite3_column_count
_sqlite3_column_double
_sqlite3_column_int
_sqlite3_column_int64
_sqlite3_column_name
_sqlite3_column_text
_sqlite3_column_type
_sqlite3_compileoption_get
_sqlite3_compileoption_used
_sqlite3_create_function_v2
_sqlite3_data_count
_sqlite3_db_filename
_sqlite3_errmsg
_sqlite3_exec
_sqlite3_finalize
_sqlite3_interrupt
_sqlite3_libversion
_sqlite3_open
_sqlite3_open_v2
_sqlite3_prepare_v2
_sqlite3_prepare_v2
_sqlite3_reset
_sqlite3_result_blob
_sqlite3_result_double
_sqlite3_result_error
_sqlite3_result_int
_sqlite3_result_null
_sqlite3_result_text
_sqlite3_sourceid
_sqlite3_sql
_sqlite3_step
_sqlite3_value_blob
_sqlite3_value_bytes
_sqlite3_value_double
_sqlite3_value_text
_sqlite3_value_type
_free
+14
View File
@@ -0,0 +1,14 @@
ALLOC_NORMAL
FS
UTF8ToString
addFunction
allocate
allocateUTF8OnStack
ccall
cwrap
getValue
removeFunction
setValue
stackAlloc
stackRestore
stackSave
+31 -4
View File
@@ -1,7 +1,34 @@
# This makefile exists primarily to simplify/speed up development from
# emacs. It is not part of the canonical build process.
# This GNU makefile exists primarily to simplify/speed up development
# from emacs. It is not part of the canonical build process.
default:
make -C ../.. fiddle -e emcc_opt=-O0
$(MAKE) -C ../.. wasm -e emcc_opt=-O0
clean:
make -C ../../ clean-fiddle
$(MAKE) -C ../../ clean-wasm
fiddle_files = emscripten.css fiddle.html \
fiddle.js fiddle-module.js \
fiddle-module.wasm fiddle-worker.js \
$(wildcard *.wasm.gz) $(wildcard *.js.gz)
# fiddle_remote is the remote destination for the fiddle app. It
# must be a [user@]HOST:/path for rsync.
# Note that the target "should probably" contain a symlink of
# index.html -> fiddle.html.
fiddle_remote ?=
ifeq (,$(fiddle_remote))
ifneq (,$(wildcard /home/stephan))
fiddle_remote = wh2:www/wh/sqlite3/.
else ifneq (,$(wildcard /home/drh))
#fiddle_remote = if appropriate, add that user@host:/path here
endif
endif
$(fiddle_files): default
push-fiddle: $(fiddle_files)
@if [ x = "x$(fiddle_remote)" ]; then \
echo "fiddle_remote must be a [user@]HOST:/path for rsync"; \
exit 1; \
fi
rsync -va $(fiddle_files) $(fiddle_remote)
+144
View File
@@ -0,0 +1,144 @@
/*
2022-05-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.
***********************************************************************
This file contains bootstrapping code used by various test scripts
which live in this file's directory.
*/
(function(){
/* querySelectorAll() proxy */
const EAll = function(/*[element=document,] cssSelector*/){
return (arguments.length>1 ? arguments[0] : document)
.querySelectorAll(arguments[arguments.length-1]);
};
/* querySelector() proxy */
const E = function(/*[element=document,] cssSelector*/){
return (arguments.length>1 ? arguments[0] : document)
.querySelector(arguments[arguments.length-1]);
};
/**
Helpers for writing sqlite3-specific tests.
*/
self/*window or worker*/.SqliteTestUtil = {
/** Running total of the number of tests run via
this API. */
counter: 0,
/**
If expr is a function, it is called and its result
is returned, coerced to a bool, else expr, coerced to
a bool, is returned.
*/
toBool: function(expr){
return (expr instanceof Function) ? !!expr() : !!expr;
},
/** abort() if expr is false. If expr is a function, it
is called and its result is evaluated.
*/
assert: function f(expr, msg){
if(!f._){
f._ = ('undefined'===typeof abort
? (msg)=>{throw new Error(msg)}
: abort);
}
++this.counter;
if(!this.toBool(expr)){
f._(msg || "Assertion failed.");
}
return this;
},
/** Identical to assert() but throws instead of calling
abort(). */
affirm: function(expr, msg){
++this.counter;
if(!this.toBool(expr)) throw new Error(msg || "Affirmation failed.");
return this;
},
/** Calls f() and squelches any exception it throws. If it
does not throw, this function throws. */
mustThrow: function(f, msg){
++this.counter;
let err;
try{ f(); } catch(e){err=e;}
if(!err) throw new Error(msg || "Expected exception.");
return this;
},
/** Throws if expr is truthy or expr is a function and expr()
returns truthy. */
throwIf: function(expr, msg){
++this.counter;
if(this.toBool(expr)) throw new Error(msg || "throwIf() failed");
return this;
},
/** Throws if expr is falsy or expr is a function and expr()
returns falsy. */
throwUnless: function(expr, msg){
++this.counter;
if(!this.toBool(expr)) throw new Error(msg || "throwUnless() failed");
return this;
}
};
/**
This is a module object for use with the emscripten-installed
initSqlite3Module() factory function.
*/
self.sqlite3TestModule = {
postRun: [
/* function(theModule){...} */
],
//onRuntimeInitialized: function(){},
/* Proxy for C-side stdout output. */
print: function(){
console.log.apply(console, Array.prototype.slice.call(arguments));
},
/* Proxy for C-side stderr output. */
printErr: function(){
console.error.apply(console, Array.prototype.slice.call(arguments));
},
/**
Called by the module init bits to report loading
progress. It gets passed an empty argument when loading is
done (after onRuntimeInitialized() and any this.postRun
callbacks have been run).
*/
setStatus: function f(text){
if(!f.last){
f.last = { text: '', step: 0 };
f.ui = {
status: E('#module-status'),
progress: E('#module-progress'),
spinner: E('#module-spinner')
};
}
if(text === f.last.text) return;
f.last.text = text;
if(f.ui.progress){
f.ui.progress.value = f.last.step;
f.ui.progress.max = f.last.step + 1;
}
++f.last.step;
if(text) {
f.ui.status.classList.remove('hidden');
f.ui.status.innerText = text;
}else{
if(f.ui.progress){
f.ui.progress.remove();
f.ui.spinner.remove();
delete f.ui.progress;
delete f.ui.spinner;
}
f.ui.status.classList.add('hidden');
}
}
};
})(self/*window or worker*/);
+24
View File
@@ -0,0 +1,24 @@
/* emcscript-related styling, used during the module load/intialization processes... */
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
div.emscripten { text-align: center; }
div.emscripten_border { border: 1px solid black; }
#module-spinner { overflow: visible; }
#module-spinner > * {
margin-top: 1em;
}
.spinner {
height: 50px;
width: 50px;
margin: 0px auto;
animation: rotation 0.8s linear infinite;
border-left: 10px solid rgb(0,150,240);
border-right: 10px solid rgb(0,150,240);
border-bottom: 10px solid rgb(0,150,240);
border-top: 10px solid rgb(100,0,200);
border-radius: 100%;
background-color: rgb(200,100,250);
}
@keyframes rotation {
from {transform: rotate(0deg);}
to {transform: rotate(360deg);}
}
+230 -70
View File
@@ -14,6 +14,10 @@
sqlite3 wasm module and offers access to the db via the Worker
message-passing interface.
Forewarning: this API is still very much Under Construction and
subject to any number of changes as experience reveals what those
need to be.
Because we can have only a single message handler, as opposed to an
arbitrary number of discrete event listeners like with DOM elements,
we have to define a lower-level message API. Messages abstractly
@@ -34,19 +38,30 @@
layer. The data property is the string of the output, noting
that the emscripten binding emits these one line at a time. Thus,
if a C-side puts() emits multiple lines in a single call, the JS
side will see that as multiple calls.
side will see that as multiple calls. Example:
{type:'stdout', data: 'Hi, world.'}
- module: Status text. This is intended to alert the main thread
about module loading status so that, e.g., the main thread can
update a progress widget and DTRT when the module is finished
loading and available for work. The status text is mostly in some
undocumented(?) format emited by the emscripten generated
module-loading code, encoding progress info within it.
loading and available for work. Status messages come in the form
{type:'module', data:{
type:'status',
data: {text:string|null, step:1-based-integer}
}
with an incrementing step value for each subsequent message. When
the module loading is complete, a message with a text value of
null is posted.
- working: data='start'|'end'. Indicates that work is about to be
sent to the module or has just completed. This can be used, e.g.,
to disable UI elements which should not be activated while work
is pending.
is pending. Example:
{type:'working', data:'start'}
Main-to-Worker types:
@@ -56,7 +71,9 @@
it starts and a 'working' event (data='end') when it finished. If
called while work is currently being executed it emits stderr
message instead of doing actual work, as the underlying db cannot
handle concurrent tasks.
handle concurrent tasks. Example:
{type:'shellExec', data: 'select * from sqlite_master'}
- More TBD as the higher-level db layer develops.
*/
@@ -70,72 +87,215 @@
Noting that it happens in Firefox as well as Chrome. Harmless but
annoying.
*/
const thisWorker = self;
const wMsg = (type,data)=>postMessage({type, data});
self.onerror = function(/*message, source, lineno, colno, error*/) {
const err = arguments[4];
if(err && 'ExitStatus'==err.name){
Module._isDead = true;
Module.printErr("FATAL ERROR:", err.message);
Module.printErr("Restarting the app requires reloading the page.");
//const taOutput = document.querySelector('#output');
//if(taOutput) taOutput.classList.add('error');
}
Module.setStatus('Exception thrown, see JavaScript console');
Module.setStatus = function(text) {
if(text) console.error('[post-exception status] ' + text);
"use strict";
(function(){
/**
Posts a message in the form {type,data} unless passed more than 2
args, in which case it posts {type, data:[arg1...argN]}.
*/
const wMsg = function(type,data){
postMessage({
type,
data: arguments.length<3
? data
: Array.prototype.slice.call(arguments,1)
});
};
};
self.Module = {
/* ^^^ cannot declare that const because fiddle-module.js
(auto-generated) includes a decl for it and runs in this scope. */
preRun: [],
postRun: [],
//onRuntimeInitialized: function(){},
print: function(text){wMsg('stdout', Array.prototype.slice.call(arguments));},
printErr: function(text){wMsg('stderr', Array.prototype.slice.call(arguments));},
setStatus: function f(text){wMsg('module',{type:'status',data:text});},
totalDependencies: 0,
monitorRunDependencies: function(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
this.setStatus(left
? ('Preparing... (' + (this.totalDependencies-left)
+ '/' + this.totalDependencies + ')')
: 'All downloads complete.');
}
};
const stdout = function(){wMsg('stdout', Array.prototype.slice.call(arguments));};
const stderr = function(){wMsg('stderr', Array.prototype.slice.call(arguments));};
const shellExec = function f(sql){
if(!f._) f._ = Module.cwrap('fiddle_exec', null, ['string']);
if(Module._isDead){
wMsg('stderr', "shell module has exit()ed. Cannot run SQL.");
return;
}
wMsg('working','start');
try {
if(f._running) wMsg('stderr','Cannot run multiple commands concurrently.');
else{
f._running = true;
f._(sql);
self.onerror = function(/*message, source, lineno, colno, error*/) {
const err = arguments[4];
if(err && 'ExitStatus'==err.name){
/* This is relevant for the sqlite3 shell binding but not the
lower-level binding. */
fiddleModule.isDead = true;
stderr("FATAL ERROR:", err.message);
stderr("Restarting the app requires reloading the page.");
wMsg('error', err);
}
} finally {
wMsg('working','end');
delete f._running;
}
};
self.onmessage = function(ev){
ev = ev.data;
//console.debug("worker: onmessage.data",ev);
switch(ev.type){
case 'shellExec': shellExec(ev.data); return;
console.error(err);
fiddleModule.setStatus('Exception thrown, see JavaScript console: '+err);
};
console.warn("Unknown fiddle-worker message type:",ev);
};
self.Module.setStatus('Downloading...');
importScripts('fiddle-module.js')
/* loads module and notifies, via Module.setStatus(), when it's done loading. */;
const Sqlite3Shell = {
/** Returns the name of the currently-opened db. */
dbFilename: function f(){
if(!f._) f._ = fiddleModule.cwrap('fiddle_db_filename', "string", ['string']);
return f._();
},
/**
Runs the given text through the shell as if it had been typed
in by a user. Fires a working/start event before it starts and
working/end event when it finishes.
*/
exec: function f(sql){
if(!f._) f._ = fiddleModule.cwrap('fiddle_exec', null, ['string']);
if(fiddleModule.isDead){
stderr("shell module has exit()ed. Cannot run SQL.");
return;
}
wMsg('working','start');
try {
if(f._running){
stderr('Cannot run multiple commands concurrently.');
}else{
f._running = true;
f._(sql);
}
} finally {
delete f._running;
wMsg('working','end');
}
},
resetDb: function f(){
if(!f._) f._ = fiddleModule.cwrap('fiddle_reset_db', null);
stdout("Resetting database.");
f._();
stdout("Reset",this.dbFilename());
},
/* Interrupt can't work: this Worker is tied up working, so won't get the
interrupt event which would be needed to perform the interrupt. */
interrupt: function f(){
if(!f._) f._ = fiddleModule.cwrap('fiddle_interrupt', null);
stdout("Requesting interrupt.");
f._();
}
};
self.onmessage = function f(ev){
ev = ev.data;
if(!f.cache){
f.cache = {
prevFilename: null
};
}
//console.debug("worker: onmessage.data",ev);
switch(ev.type){
case 'shellExec': Sqlite3Shell.exec(ev.data); return;
case 'db-reset': Sqlite3Shell.resetDb(); return;
case 'interrupt': Sqlite3Shell.interrupt(); return;
/** Triggers the export of the current db. Fires an
event in the form:
{type:'db-export',
data:{
filename: name of db,
buffer: contents of the db file (Uint8Array),
error: on error, a message string and no buffer property.
}
}
*/
case 'db-export': {
const fn = Sqlite3Shell.dbFilename();
stdout("Exporting",fn+".");
const fn2 = fn ? fn.split(/[/\\]/).pop() : null;
try{
if(!fn2) throw new Error("DB appears to be closed.");
wMsg('db-export',{
filename: fn2,
buffer: fiddleModule.FS.readFile(fn, {encoding:"binary"})
});
}catch(e){
/* Post a failure message so that UI elements disabled
during the export can be re-enabled. */
wMsg('db-export',{
filename: fn,
error: e.message
});
}
return;
}
case 'open': {
/* Expects: {
buffer: ArrayBuffer | Uint8Array,
filename: for logging/informational purposes only
} */
const opt = ev.data;
let buffer = opt.buffer;
if(buffer instanceof Uint8Array){
}else if(buffer instanceof ArrayBuffer){
buffer = new Uint8Array(buffer);
}else{
stderr("'open' expects {buffer:Uint8Array} containing an uploaded db.");
return;
}
const fn = (
opt.filename
? opt.filename.split(/[/\\]/).pop().replace('"','_')
: ("db-"+((Math.random() * 10000000) | 0)+
"-"+((Math.random() * 10000000) | 0)+".sqlite3")
);
/* We cannot delete the existing db file until the new one
is installed, which means that we risk overflowing our
quota (if any) by having both the previous and current
db briefly installed in the virtual filesystem. */
fiddleModule.FS.createDataFile("/", fn, buffer, true, true);
const oldName = Sqlite3Shell.dbFilename();
Sqlite3Shell.exec('.open "/'+fn+'"');
if(oldName && oldName !== fn){
try{fiddleModule.FS.unlink(oldName);}
catch(e){/*ignored*/}
}
stdout("Replaced DB with",fn+".");
return;
}
};
console.warn("Unknown fiddle-worker message type:",ev);
};
/**
emscripten module for use with build mode -sMODULARIZE.
*/
const fiddleModule = {
print: stdout,
printErr: stderr,
/**
Intercepts status updates from the emscripting module init
and fires worker events with a type of 'status' and a
payload of:
{
text: string | null, // null at end of load process
step: integer // starts at 1, increments 1 per call
}
We have no way of knowing in advance how many steps will
be processed/posted, so creating a "percentage done" view is
not really practical. One can be approximated by giving it a
current value of message.step and max value of message.step+1,
though.
When work is finished, a message with a text value of null is
submitted.
After a message with text==null is posted, the module may later
post messages about fatal problems, e.g. an exit() being
triggered, so it is recommended that UI elements for posting
status messages not be outright removed from the DOM when
text==null, and that they instead be hidden until/unless
text!=null.
*/
setStatus: function f(text){
if(!f.last) f.last = { step: 0, text: '' };
else if(text === f.last.text) return;
f.last.text = text;
wMsg('module',{
type:'status',
data:{step: ++f.last.step, text: text||null}
});
}
};
importScripts('fiddle-module.js');
/**
initFiddleModule() is installed via fiddle-module.js due to
building with:
emcc ... -sMODULARIZE=1 -sEXPORT_NAME=initFiddleModule
*/
initFiddleModule(fiddleModule).then(function(thisModule){
wMsg('fiddle-ready');
});
})();
+80 -42
View File
@@ -4,36 +4,14 @@
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>sqlite3 fiddle</title>
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
<!-- to add a togglable terminal-style view, uncomment the following
two lines and ensure that these files are on the web server. -->
<!--script src="jqterm/jqterm-bundle.min.js"></script>
<link rel="stylesheet" href="jqterm/jquery.terminal.min.css"/-->
<link rel="stylesheet" href="emscripten.css"/>
<style>
/* emcscript-related styling, used during the intialization phase... */
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
div.emscripten { text-align: center; }
div.emscripten_border { border: 1px solid black; }
#module-spinner { overflow: visible; }
#module-spinner > * {
margin-top: 1em;
}
.spinner {
height: 50px;
width: 50px;
margin: 0px auto;
animation: rotation 0.8s linear infinite;
border-left: 10px solid rgb(0,150,240);
border-right: 10px solid rgb(0,150,240);
border-bottom: 10px solid rgb(0,150,240);
border-top: 10px solid rgb(100,0,200);
border-radius: 100%;
background-color: rgb(200,100,250);
}
@keyframes rotation {
from {transform: rotate(0deg);}
to {transform: rotate(360deg);}
}
/* The following styles are for app-level use. */
textarea {
font-family: monospace;
flex: 1 1 auto;
@@ -70,9 +48,6 @@
filter: invert(100%);
flex: 10 1 auto;
}
/*#main-wrapper:not(.side-by-side) .ta-wrapper.input {
flex: 5 1 auto;
}*/
.button-bar {
display: flex;
justify-content: center;
@@ -85,9 +60,6 @@
label[for] {
cursor: pointer;
}
fieldset {
border-radius: 0.5em;
}
.error {
color: red;
background-color: yellow;
@@ -98,11 +70,55 @@
pointer-events: none !important;
display: none !important;
}
fieldset.options {
font-size: 75%;
/* Safari supports neither styling of nor event handling on a
fieldset legend, so we emulate a fieldset-like widget. */
.fieldset {
border-radius: 0.5em;
border: 1px inset;
display: flex;
flex-direction: column;
}
fieldset > legend {
.fieldset > .legend {
position: relative;
top: -1.5ex;
padding: 0 0.5em;
font-size: 85%;
margin-left: 0.5em;
flex: 0 1 auto;
align-self: self-start;
cursor: pointer;
}
.fieldset.options > div {
display: flex;
flex-wrap: wrap;
font-size: 70%;
margin: 0 0.5em 0.5em 0.5em;
}
.fieldset > .legend > span {
position: relative;
}
.fieldset > .legend::before {
/* Hide the parent element's top border where this
element intersects it. */
content: ' ';
width: 100%;
height: 100%;
background-color: white
/* REALLY want to 'inherit' the color from the fieldset's
parent, but inherit leads to a transparent bg, which is
exactly what we're trying to avoid here. */;
opacity: 1;
position: absolute;
top: 0;
left: 0;
}
.fieldset > .legend::after {
content: " [hide]";
position: relative;
}
.fieldset.collapsed > .legend::after {
content: " [show]";
position: relative;
}
span.labeled-input {
padding: 0.25em;
@@ -136,12 +152,16 @@
}
#view-split {
display: flex;
flex-direction: column;
flex-direction: column-reverse;
}
#view-split > .fieldset.options {
margin-top: 0.5em;
}
</style>
</head>
<body>
<header id='titlebar'><span>sqlite3 fiddle</span></header>
<!-- emscripten bits -->
<figure id="module-spinner">
<div class="spinner"></div>
<div class='center'><strong>Initializing app...</strong></div>
@@ -154,15 +174,15 @@
<div class="emscripten" id="module-status">Downloading...</div>
<div class="emscripten">
<progress value="0" max="100" id="module-progress" hidden='1'></progress>
</div>
</div><!-- /emscripten bits -->
<div id='view-terminal' class='app-view hidden initially-hidden'>
This is a placeholder for a terminal-like view.
</div>
<div id='view-split' class='app-view initially-hidden'>
<fieldset class='options'>
<legend>Options</legend>
<div class='fieldset options collapsible'>
<span class='legend'><span>Options</span></span>
<div class=''>
<span class='labeled-input'>
<input type='checkbox' id='opt-cb-sbs'
@@ -188,13 +208,27 @@
data-config='autoClearOutput'>
<label for='opt-cb-autoclear'>Auto-clear output</label>
</span>
<span class='labeled-input'>
<input type='file' id='load-db' class='hidden'/>
<button id='btn-load-db'>Load DB...</button>
</span>
<span class='labeled-input'>
<button id='btn-export'>Download DB</button>
</span>
<span class='labeled-input'>
<button id='btn-reset'>Reset DB</button>
</span>
<span class='labeled-input'>
<select id='select-examples'></select>
</span>
</div>
</fieldset>
</div><!-- .fieldset -->
<div id='main-wrapper' class=''>
<div class='ta-wrapper input'>
<textarea id="input"
placeholder="Shell input. Ctrl-enter/shift-enter runs it.">
-- Use ctrl-enter or shift-enter to execute SQL
-- Use ctrl-enter or shift-enter to execute SQL. If only a subset
-- is currently selected, only that part is executed.
.nullvalue NULL
.mode box
CREATE TABLE t(a,b);
@@ -204,7 +238,6 @@ SELECT * FROM t;</textarea>
<button id='btn-shell-exec'>Run</button>
<button id='btn-clear'>Clear Input</button>
<button data-cmd='.help'>Help</button>
<select id='select-examples'></select>
</div>
</div>
<div class='ta-wrapper output'>
@@ -212,6 +245,11 @@ SELECT * FROM t;</textarea>
placeholder="Shell output."></textarea>
<div class='button-bar'>
<button id='btn-clear-output'>Clear Output</button>
<button id='btn-interrupt' class='hidden' disabled>Interrupt</button>
<!-- interruption cannot work in the current configuration
because we cannot send an interrupt message when work
is currently underway. At that point the Worker is
tied up and will not receive the message. -->
</div>
</div>
</div>
+400 -56
View File
@@ -16,6 +16,183 @@
*/
(function(){
'use strict';
/* Recall that the 'self' symbol, except where locally
overwritten, refers to the global window or worker object. */
const storage = (function(NS/*namespace object in which to store this module*/){
/* Pedantic licensing note: this code originated in the Fossil SCM
source tree, where it has a different license, but the person who
ported it into sqlite is the same one who wrote it for fossil. */
'use strict';
NS = NS||{};
/**
This module provides a basic wrapper around localStorage
or sessionStorage or a dummy proxy object if neither
of those are available.
*/
const tryStorage = function f(obj){
if(!f.key) f.key = 'storage.access.check';
try{
obj.setItem(f.key, 'f');
const x = obj.getItem(f.key);
obj.removeItem(f.key);
if(x!=='f') throw new Error(f.key+" failed")
return obj;
}catch(e){
return undefined;
}
};
/** Internal storage impl for this module. */
const $storage =
tryStorage(window.localStorage)
|| tryStorage(window.sessionStorage)
|| tryStorage({
// A basic dummy xyzStorage stand-in
$$$:{},
setItem: function(k,v){this.$$$[k]=v},
getItem: function(k){
return this.$$$.hasOwnProperty(k) ? this.$$$[k] : undefined;
},
removeItem: function(k){delete this.$$$[k]},
clear: function(){this.$$$={}}
});
/**
For the dummy storage we need to differentiate between
$storage and its real property storage for hasOwnProperty()
to work properly...
*/
const $storageHolder = $storage.hasOwnProperty('$$$') ? $storage.$$$ : $storage;
/**
A prefix which gets internally applied to all storage module
property keys so that localStorage and sessionStorage across the
same browser profile instance do not "leak" across multiple apps
being hosted by the same origin server. Such cross-polination is
still there but, with this key prefix applied, it won't be
immediately visible via the storage API.
With this in place we can justify using localStorage instead of
sessionStorage.
One implication of using localStorage and sessionStorage is that
their scope (the same "origin" and client application/profile)
allows multiple apps on the same origin to use the same
storage. Thus /appA/foo could then see changes made via
/appB/foo. The data do not cross user- or browser boundaries,
though, so it "might" arguably be called a
feature. storageKeyPrefix was added so that we can sandbox that
state for each separate app which shares an origin.
See: https://fossil-scm.org/forum/forumpost/4afc4d34de
Sidebar: it might seem odd to provide a key prefix and stick all
properties in the topmost level of the storage object. We do that
because adding a layer of object to sandbox each app would mean
(de)serializing that whole tree on every storage property change.
e.g. instead of storageObject.projectName.foo we have
storageObject[storageKeyPrefix+'foo']. That's soley for
efficiency's sake (in terms of battery life and
environment-internal storage-level effort).
*/
const storageKeyPrefix = (
$storageHolder===$storage/*localStorage or sessionStorage*/
? (
(NS.config ?
(NS.config.projectCode || NS.config.projectName
|| NS.config.shortProjectName)
: false)
|| window.location.pathname
)+'::' : (
'' /* transient storage */
)
);
/**
A proxy for localStorage or sessionStorage or a
page-instance-local proxy, if neither one is availble.
Which exact storage implementation is uses is unspecified, and
apps must not rely on it.
*/
NS.storage = {
storageKeyPrefix: storageKeyPrefix,
/** Sets the storage key k to value v, implicitly converting
it to a string. */
set: (k,v)=>$storage.setItem(storageKeyPrefix+k,v),
/** Sets storage key k to JSON.stringify(v). */
setJSON: (k,v)=>$storage.setItem(storageKeyPrefix+k,JSON.stringify(v)),
/** Returns the value for the given storage key, or
dflt if the key is not found in the storage. */
get: (k,dflt)=>$storageHolder.hasOwnProperty(
storageKeyPrefix+k
) ? $storage.getItem(storageKeyPrefix+k) : dflt,
/** Returns true if the given key has a value of "true". If the
key is not found, it returns true if the boolean value of dflt
is "true". (Remember that JS persistent storage values are all
strings.) */
getBool: function(k,dflt){
return 'true'===this.get(k,''+(!!dflt));
},
/** Returns the JSON.parse()'d value of the given
storage key's value, or dflt is the key is not
found or JSON.parse() fails. */
getJSON: function f(k,dflt){
try {
const x = this.get(k,f);
return x===f ? dflt : JSON.parse(x);
}
catch(e){return dflt}
},
/** Returns true if the storage contains the given key,
else false. */
contains: (k)=>$storageHolder.hasOwnProperty(storageKeyPrefix+k),
/** Removes the given key from the storage. Returns this. */
remove: function(k){
$storage.removeItem(storageKeyPrefix+k);
return this;
},
/** Clears ALL keys from the storage. Returns this. */
clear: function(){
this.keys().forEach((k)=>$storage.removeItem(/*w/o prefix*/k));
return this;
},
/** Returns an array of all keys currently in the storage. */
keys: ()=>Object.keys($storageHolder).filter((v)=>(v||'').startsWith(storageKeyPrefix)),
/** Returns true if this storage is transient (only available
until the page is reloaded), indicating that fileStorage
and sessionStorage are unavailable. */
isTransient: ()=>$storageHolder!==$storage,
/** Returns a symbolic name for the current storage mechanism. */
storageImplName: function(){
if($storage===window.localStorage) return 'localStorage';
else if($storage===window.sessionStorage) return 'sessionStorage';
else return 'transient';
},
/**
Returns a brief help text string for the currently-selected
storage type.
*/
storageHelpDescription: function(){
return {
localStorage: "Browser-local persistent storage with an "+
"unspecified long-term lifetime (survives closing the browser, "+
"but maybe not a browser upgrade).",
sessionStorage: "Storage local to this browser tab, "+
"lost if this tab is closed.",
"transient": "Transient storage local to this invocation of this page."
}[this.storageImplName()];
}
};
return NS.storage;
})({})/*storage API setup*/;
/** Name of the stored copy of SqliteFiddle.config. */
const configStorageKey = 'sqlite3-fiddle-config';
/**
The SqliteFiddle object is intended to be the primary
@@ -76,13 +253,15 @@
f._.value = '';
}
if(this.config.echoToConsole) console.log(text);
if(this.jqTerm) window.Module.jqTerm.echo(text);
if(this.jqTerm) this.jqTerm.echo(text);
f._.value += text + "\n";
if(this.config.autoScrollOutput){
f._.scrollTop = f._.scrollHeight;
}
},
_msgMap: {},
/** Adds a worker message handler for messages of the given
type. */
addMsgHandler: function f(type,callback){
if(Array.isArray(type)){
type.forEach((t)=>this.addMsgHandler(t, callback));
@@ -93,6 +272,7 @@
: (this._msgMap[type] = [])).push(callback);
return this;
},
/** Given a worker message, runs all handlers for msg.type. */
runMsgHandlers: function(msg){
const list = (this._msgMap.hasOwnProperty(msg.type)
? this._msgMap[msg.type] : false);
@@ -104,6 +284,7 @@
list.forEach((f)=>f(msg));
return true;
},
/** Removes all message handlers for the given message type. */
clearMsgHandlers: function(type){
delete this._msgMap[type];
return this;
@@ -112,9 +293,39 @@
wMsg: function(type,data){
this.worker.postMessage({type, data});
return this;
},
/**
Prompts for confirmation and, if accepted, deletes
all content and tables in the (transient) database.
*/
resetDb: function(){
if(window.confirm("Really destroy all content and tables "
+"in the (transient) db?")){
this.wMsg('db-reset');
}
return this;
},
/** Stores this object's config in the browser's storage. */
storeConfig: function(){
storage.setJSON(configStorageKey,this.config);
}
};
if(1){ /* Restore SF.config */
const storedConfig = storage.getJSON(configStorageKey);
if(storedConfig){
/* Copy all properties to SF.config which are currently in
storedConfig. We don't bother copying any other
properties: those have been removed from the app in the
meantime. */
Object.keys(SF.config).forEach(function(k){
if(storedConfig.hasOwnProperty(k)){
SF.config[k] = storedConfig[k];
}
});
}
}
SF.worker = new Worker('fiddle-worker.js');
SF.worker.onmessage = (ev)=>SF.runMsgHandlers(ev.data);
SF.addMsgHandler(['stdout', 'stderr'], (ev)=>SF.echo(ev.data));
@@ -129,43 +340,55 @@
return (arguments.length>1 ? arguments[0] : document)
.querySelector(arguments[arguments.length-1]);
};
const statusElement = E('#module-status');
const progressElement = E('#module-progress');
const spinnerElement = E('#module-spinner');
/** Handles status updates from the Module object. */
SF.addMsgHandler('module', function f(ev){
ev = ev.data;
//console.log("Module status:",ev);
if('status'!==ev.type) return;
/* This weird handling of the ev.data is simply how
emscripten's auto-generated code notifies the client of
load progress. */
let text = ev.data;
if(!f.last) f.last = { time: Date.now(), text: '' };
if(text === f.last.text) return;
const m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
const now = Date.now();
if(m && now - f.last.time < 30) return; // if this is a progress update, skip it if too soon
f.last.time = now;
f.last.text = text;
if(m) {
text = m[1];
progressElement.value = parseInt(m[2])*100;
progressElement.max = parseInt(m[4])*100;
progressElement.hidden = false;
spinnerElement.hidden = false;
} else {
progressElement.remove();
if(!text) spinnerElement.remove();
if('status'!==ev.type){
console.warn("Unexpected module-type message:",ev);
return;
}
if(text) statusElement.innerText = text;
else {
console.log("Finalizing status.");
statusElement.remove();
SF.clearMsgHandlers('module');
self.onSFLoaded();
if(!f.ui){
f.ui = {
status: E('#module-status'),
progress: E('#module-progress'),
spinner: E('#module-spinner')
};
}
const msg = ev.data;
if(f.ui.progres){
progress.value = msg.step;
progress.max = msg.step + 1/*we don't know how many steps to expect*/;
}
if(1==msg.step){
f.ui.progress.classList.remove('hidden');
f.ui.spinner.classList.remove('hidden');
}
if(msg.text){
f.ui.status.classList.remove('hidden');
f.ui.status.innerText = msg.text;
}else{
if(f.ui.progress){
f.ui.progress.remove();
f.ui.spinner.remove();
delete f.ui.progress;
delete f.ui.spinner;
}
f.ui.status.classList.add('hidden');
/* The module can post messages about fatal problems,
e.g. an exit() being triggered or assertion failure,
after the last "load" message has arrived, so
leave f.ui.status and message listener intact. */
}
});
/**
The 'fiddle-ready' event is fired (with no payload) when the
wasm module has finished loading. Interestingly, that happens
_before_ the final module:status event */
SF.addMsgHandler('fiddle-ready', function(){
SF.clearMsgHandlers('fiddle-ready');
self.onSFLoaded();
});
/**
@@ -177,7 +400,7 @@
delete this.onSFLoaded;
// Unhide all elements which start out hidden
EAll('.initially-hidden').forEach((e)=>e.classList.remove('initially-hidden'));
E('#btn-reset').addEventListener('click',()=>SF.resetDb());
const taInput = E('#input');
const btnClearIn = E('#btn-clear');
btnClearIn.addEventListener('click',function(){
@@ -198,11 +421,27 @@
if(SF.jqTerm) SF.jqTerm.clear();
},false);
const btnShellExec = E('#btn-shell-exec');
btnShellExec.addEventListener('click',function(){
const sql = taInput.value.trim();
btnShellExec.addEventListener('click',function(ev){
let sql;
ev.preventDefault();
if(taInput.selectionStart<taInput.selectionEnd){
sql = taInput.value.substring(taInput.selectionStart,taInput.selectionEnd).trim();
}else{
sql = taInput.value.trim();
}
if(sql) SF.dbExec(sql);
},false);
const btnInterrupt = E("#btn-interrupt");
//btnInterrupt.classList.add('hidden');
/** To be called immediately before work is sent to the
worker. Updates some UI elements. The 'working'/'end'
event will apply the inverse, undoing the bits this
function does. This impl is not in the 'working'/'start'
event handler because that event is given to us
asynchronously _after_ we need to have performed this
work.
*/
const preStartWork = function f(){
if(!f._){
const title = E('title');
@@ -214,6 +453,7 @@
}
f._.pageTitle.innerText = "[working...] "+f._.pageTitleOrig;
btnShellExec.setAttribute('disabled','disabled');
btnInterrupt.removeAttribute('disabled','disabled');
};
/* Sends the given text to the db module to evaluate as if it
@@ -229,16 +469,19 @@
};
SF.addMsgHandler('working',function f(ev){
if('start' === ev.data){
//btnShellExec.innerText = "Working..."; // forces layout reflow (annoying)
}else if('end' === ev.data){
preStartWork._.pageTitle.innerText = preStartWork._.pageTitleOrig;
btnShellExec.innerText = preStartWork._.btnLabel;
btnShellExec.removeAttribute('disabled');
switch(ev.data){
case 'start': /* See notes in preStartWork(). */; return;
case 'end':
preStartWork._.pageTitle.innerText = preStartWork._.pageTitleOrig;
btnShellExec.innerText = preStartWork._.btnLabel;
btnShellExec.removeAttribute('disabled');
btnInterrupt.setAttribute('disabled','disabled');
return;
}
console.warn("Unhandled 'working' event:",ev.data);
});
/* For each checkboxes with data-csstgt, set up a handler which
/* For each checkbox with data-csstgt, set up a handler which
toggles the given CSS class on the element matching
E(data-csstgt). */
EAll('input[type=checkbox][data-csstgt]')
@@ -266,15 +509,113 @@
}
e.addEventListener('change', function(){
SF.config[this.dataset.config] = this.checked;
SF.storeConfig();
}, false);
});
/* For each button with data-cmd=X, map a click handler which
calls dbExec(X). */
calls SF.dbExec(X). */
const cmdClick = function(){SF.dbExec(this.dataset.cmd);};
EAll('button[data-cmd]').forEach(
e => e.addEventListener('click', cmdClick, false)
);
btnInterrupt.addEventListener('click',function(){
SF.wMsg('interrupt');
});
/** Initiate a download of the db. */
const btnExport = E('#btn-export');
const eLoadDb = E('#load-db');
const btnLoadDb = E('#btn-load-db');
btnLoadDb.addEventListener('click', ()=>eLoadDb.click());
/**
Enables (if passed true) or disables all UI elements which
"might," if timed "just right," interfere with an
in-progress db import/export/exec operation.
*/
const enableMutatingElements = function f(enable){
if(!f._elems){
f._elems = [
/* UI elements to disable while import/export are
running. Normally the export is fast enough
that this won't matter, but we really don't
want to be reading (from outside of sqlite) the
db when the user taps btnShellExec. */
btnShellExec, btnExport, eLoadDb
];
}
f._elems.forEach( enable
? (e)=>e.removeAttribute('disabled')
: (e)=>e.setAttribute('disabled','disabled') );
};
btnExport.addEventListener('click',function(){
enableMutatingElements(false);
SF.wMsg('db-export');
});
SF.addMsgHandler('db-export', function(ev){
enableMutatingElements(true);
ev = ev.data;
if(ev.error){
SF.echo("Export failed:",ev.error);
return;
}
const blob = new Blob([ev.buffer], {type:"application/x-sqlite3"});
const a = document.createElement('a');
document.body.appendChild(a);
a.href = window.URL.createObjectURL(blob);
a.download = ev.filename;
a.addEventListener('click',function(){
setTimeout(function(){
SF.echo("Exported (possibly auto-downloaded):",ev.filename);
window.URL.revokeObjectURL(a.href);
a.remove();
},500);
});
a.click();
});
/**
Handle load/import of an external db file.
*/
eLoadDb.addEventListener('change',function(){
const f = this.files[0];
const r = new FileReader();
const status = {loaded: 0, total: 0};
enableMutatingElements(false);
r.addEventListener('loadstart', function(){
SF.echo("Loading",f.name,"...");
});
r.addEventListener('progress', function(ev){
SF.echo("Loading progress:",ev.loaded,"of",ev.total,"bytes.");
});
const that = this;
r.addEventListener('load', function(){
enableMutatingElements(true);
SF.echo("Loaded",f.name+". Opening db...");
SF.wMsg('open',{
filename: f.name,
buffer: this.result
});
});
r.addEventListener('error',function(){
enableMutatingElements(true);
SF.echo("Loading",f.name,"failed for unknown reasons.");
});
r.addEventListener('abort',function(){
enableMutatingElements(true);
SF.echo("Cancelled loading of",f.name+".");
});
r.readAsArrayBuffer(f);
});
EAll('.fieldset.collapsible').forEach(function(fs){
const legend = E(fs,'span.legend'),
content = EAll(fs,':scope > div');
legend.addEventListener('click', function(){
fs.classList.toggle('collapsed');
content.forEach((d)=>d.classList.toggle('hidden'));
}, false);
});
/**
Given a DOM element, this routine measures its "effective
height", which is the bounding top/bottom range of this element
@@ -341,22 +682,24 @@
debounce.$defaultDelay = 500 /*arbitrary*/;
const ForceResizeKludge = (function(){
/* Workaround for Safari mayhem regarding use of vh CSS units....
We cannot use vh units to set the terminal area size because
Safari chokes on that, so we calculate that height here. Larger
than ~95% is too big for Firefox on Android, causing the input
area to move off-screen. */
const bcl = document.body.classList;
/* Workaround for Safari mayhem regarding use of vh CSS
units.... We cannot use vh units to set the main view
size because Safari chokes on that, so we calculate
that height here. Larger than ~95% is too big for
Firefox on Android, causing the input area to move
off-screen. */
const appViews = EAll('.app-view');
const elemsToCount = [
/* Elements which we need to always count in the
visible body size. */
E('body > header'),
E('body > footer')
];
const resized = function f(){
if(f.$disabled) return;
const wh = window.innerHeight;
var ht;
var extra = 0;
const elemsToCount = [
E('body > header'),
E('body > footer')
];
elemsToCount.forEach((e)=>e ? extra += effectiveHeight(e) : false);
ht = wh - extra;
appViews.forEach(function(e){
@@ -420,7 +763,7 @@ SELECT group_concat(rtrim(t),x'0a') as Mandelbrot FROM a;`}
taInput.value = '-- ' +
this.selectedOptions[0].innerText +
'\n' + this.value;
//dbExec(this.value);
SF.dbExec(this.value);
});
})()/* example queries */;
@@ -428,7 +771,7 @@ SELECT group_concat(rtrim(t),x'0a') as Mandelbrot FROM a;`}
if(window.jQuery && window.jQuery.terminal){
/* Set up the terminal-style view... */
const eTerm = window.jQuery('#view-terminal').empty();
SF.jqTerm = eTerm.terminal(dbExec,{
SF.jqTerm = eTerm.terminal(SF.dbExec.bind(SF),{
prompt: 'sqlite> ',
greetings: false /* note that the docs incorrectly call this 'greeting' */
});
@@ -436,6 +779,7 @@ SELECT group_concat(rtrim(t),x'0a') as Mandelbrot FROM a;`}
const head = E('header#titlebar');
const btnToggleView = document.createElement('button');
btnToggleView.appendChild(document.createTextNode("Toggle View"));
head.appendChild(btnToggleView);
btnToggleView.addEventListener('click',function f(){
EAll('.app-view').forEach(e=>e.classList.toggle('hidden'));
if(document.body.classList.toggle('terminal-mode')){
File diff suppressed because it is too large Load Diff
+44
View File
@@ -0,0 +1,44 @@
/*
2022-05-23
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 is a JS Worker file for the main sqlite3 api. It loads
sqlite3.js, initializes the module, and postMessage()'s a message
after the module is initialized:
{type: 'sqlite3-api', data: 'ready'}
This seemingly superfluous level of indirection is necessary when
loading sqlite3.js via a Worker. Loading sqlite3.js from the main
window thread elides the Worker-specific API. Instantiating a worker
with new Worker("sqlite.js") will not (cannot) call
initSqlite3Module() to initialize the module due to a
timing/order-of-operations conflict (and that symbol is not exported
in a way that a Worker loading it that way can see it). Thus JS
code wanting to load the sqlite3 Worker-specific API needs to pass
_this_ file (or equivalent) to the Worker constructor and then
listen for an event in the form shown above in order to know when
the module has completed initialization. sqlite3.js will fire a
similar event, with data:'loaded' as the final step in its loading
process. Whether or not we _really_ need both 'loaded' and 'ready'
events is unclear, but they are currently separate events primarily
for the sake of clarity in the timing of when it's okay to use the
loaded module. At the time the 'loaded' event is fired, it's
possible (but unknown and unknowable) that the emscripten-generated
module-setup infrastructure still has work to do. Thus it is
hypothesized that client code is better off waiting for the 'ready'
even before using the API.
*/
"use strict";
importScripts('sqlite3.js');
initSqlite3Module().then(function(){
setTimeout(()=>self.postMessage({type:'sqlite3-api',data:'ready'}), 0);
});
+31
View File
@@ -0,0 +1,31 @@
textarea {
font-family: monospace;
}
header {
font-size: 130%;
font-weight: bold;
}
.hidden, .initially-hidden {
position: absolute !important;
opacity: 0 !important;
pointer-events: none !important;
display: none !important;
}
fieldset.options {
font-size: 75%;
}
fieldset > legend {
padding: 0 0.5em;
}
span.labeled-input {
padding: 0.25em;
margin: 0.25em 0.5em;
border-radius: 0.25em;
white-space: nowrap;
background: #0002;
}
.center { text-align: center; }
.error {
color: red;
background-color: yellow;
}
+33
View File
@@ -0,0 +1,33 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
<link rel="stylesheet" href="emscripten.css"/>
<link rel="stylesheet" href="testing.css"/>
<title>sqlite3-api.js tests</title>
<style></style>
</head>
<body>
<header id='titlebar'><span>sqlite3-api.js tests</span></header>
<!-- emscripten bits -->
<figure id="module-spinner">
<div class="spinner"></div>
<div class='center'><strong>Initializing app...</strong></div>
<div class='center'>
On a slow internet connection this may take a moment. If this
message displays for "a long time", intialization may have
failed and the JavaScript console may contain clues as to why.
</div>
</figure>
<div class="emscripten" id="module-status">Downloading...</div>
<div class="emscripten">
<progress value="0" max="100" id="module-progress" hidden='1'></progress>
</div><!-- /emscripten bits -->
<div>Everything on this page happens in the dev console.</div>
<script src="sqlite3.js"></script>
<script src="SqliteTestUtil.js"></script>
<script src="testing1.js"></script>
</body>
</html>
+179
View File
@@ -0,0 +1,179 @@
/*
2022-05-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.
***********************************************************************
A basic test script for sqlite3-api.js. This file must be run in
main JS thread and sqlite3.js must have been loaded before it.
*/
(function(){
const T = self.SqliteTestUtil;
const log = console.log.bind(console);
const assert = function(condition, text) {
if (!condition) {
throw new Error('Assertion failed' + (text ? ': ' + text : ''));
}
};
const test1 = function(db,sqlite3){
const api = sqlite3.api;
log("Basic sanity tests...");
T.assert(db._pDb);
let st = db.prepare("select 3 as a");
//log("statement =",st);
T.assert(st._pStmt)
.assert(!st._mayGet)
.assert('a' === st.getColumnName(0))
.assert(st === db._statements[st._pStmt])
.assert(1===st.columnCount)
.assert(0===st.parameterCount)
.mustThrow(()=>st.bind(1,null))
.assert(true===st.step())
.assert(3 === st.get(0))
.mustThrow(()=>st.get(1))
.mustThrow(()=>st.get(0,~api.SQLITE_INTEGER))
.assert(3 === st.get(0,api.SQLITE_INTEGER))
.assert(3 === st.getInt(0))
.assert('3' === st.get(0,api.SQLITE_TEXT))
.assert('3' === st.getString(0))
.assert(3.0 === st.get(0,api.SQLITE_FLOAT))
.assert(3.0 === st.getFloat(0))
.assert(st.get(0,api.SQLITE_BLOB) instanceof Uint8Array)
.assert(st.getBlob(0) instanceof Uint8Array)
.assert(3 === st.get([])[0])
.assert(3 === st.get({}).a)
.assert(3 === st.getJSON(0))
.assert(st._mayGet)
.assert(false===st.step())
.assert(!st._mayGet)
;
let pId = st._pStmt;
st.finalize();
T.assert(!st._pStmt)
.assert(!db._statements[pId]);
let list = [];
db.exec({
sql:`CREATE TABLE t(a,b);
INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`,
multi: true,
saveSql: list,
bind: [5,6]
});
T.assert(2 === list.length);
//log("Exec'd SQL:", list);
let counter = 0, colNames = [];
list.length = 0;
db.exec("SELECT a a, b b FROM t",{
rowMode: 'object',
resultRows: list,
columnNames: colNames,
callback: function(row,stmt){
++counter;
T.assert(row.a%2 && row.a<6);
}
});
T.assert(2 === colNames.length)
.assert('a' === colNames[0])
.assert(3 === counter)
.assert(3 === list.length);
list.length = 0;
db.exec("SELECT a a, b b FROM t",{
rowMode: 'array',
callback: function(row,stmt){
++counter;
T.assert(Array.isArray(row))
.assert(0===row[1]%2 && row[1]<7);
}
});
T.assert(6 === counter);
};
const testUDF = function(db){
log("Testing UDF...");
db.createFunction("foo",function(a,b){return a+b});
T.assert(7===db.selectValue("select foo(3,4)")).
assert(5===db.selectValue("select foo(3,?)",2)).
assert(5===db.selectValue("select foo(?,?)",[1,4])).
assert(5===db.selectValue("select foo($a,$b)",{$a:0,$b:5}));
db.createFunction("bar", {
arity: -1,
callback: function(){
var rc = 0;
for(let i = 0; i < arguments.length; ++i) rc += arguments[i];
return rc;
}
});
log("Testing DB::selectValue() w/ UDF...");
T.assert(0===db.selectValue("select bar()")).
assert(1===db.selectValue("select bar(1)")).
assert(3===db.selectValue("select bar(1,2)")).
assert(-1===db.selectValue("select bar(1,2,-4)"));
T.assert('hi' === db.selectValue("select ?",'hi')).
assert(null===db.selectValue("select null")).
assert(null === db.selectValue("select ?",null)).
assert(null === db.selectValue("select ?",[null])).
assert(null === db.selectValue("select $a",{$a:null}));
};
const testAttach = function(db){
log("Testing ATTACH...");
db.exec({
sql:[
"attach 'foo.db' as foo",
"create table foo.bar(a)",
"insert into foo.bar(a) values(1),(2),(3)"
].join(';'),
multi: true
});
T.assert(2===db.selectValue('select a from foo.bar where a>1 order by a'));
db.exec("detach foo");
T.mustThrow(()=>db.exec("select * from foo.bar"));
};
const runTests = function(Module){
T.assert(Module._free instanceof Function).
assert(Module.allocate instanceof Function).
assert(Module.addFunction instanceof Function).
assert(Module.removeFunction instanceof Function);
const sqlite3 = Module.sqlite3;
const api = sqlite3.api;
const oo = sqlite3.SQLite3;
console.log("Loaded module:",api.sqlite3_libversion(),
api.sqlite3_sourceid());
log("Build options:",oo.compileOptionUsed());
const db = new oo.DB();
try {
log("DB:",db.filename);
[
test1, testUDF, testAttach
].forEach((f)=>{
const t = T.counter;
f(db, sqlite3);
log("Test count:",T.counter - t);
});
}finally{
db.close();
}
log("Total Test count:",T.counter);
};
initSqlite3Module(self.sqlite3TestModule).then(function(theModule){
/** Use a timeout so that we are (hopefully) out from
under the module init stack when our setup gets
run. Just on principle, not because we _need_ to
be. */
//console.debug("theModule =",theModule);
setTimeout(()=>runTests(theModule), 0);
});
})();
+32
View File
@@ -0,0 +1,32 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
<link rel="stylesheet" href="emscripten.css"/>
<link rel="stylesheet" href="testing.css"/>
<title>sqlite3-worker.js tests</title>
<style></style>
</head>
<body>
<header id='titlebar'><span>sqlite3-worker.js tests</span></header>
<!-- emscripten bits -->
<figure id="module-spinner">
<div class="spinner"></div>
<div class='center'><strong>Initializing app...</strong></div>
<div class='center'>
On a slow internet connection this may take a moment. If this
message displays for "a long time", intialization may have
failed and the JavaScript console may contain clues as to why.
</div>
</figure>
<div class="emscripten" id="module-status">Downloading...</div>
<div class="emscripten">
<progress value="0" max="100" id="module-progress" hidden='1'></progress>
</div><!-- /emscripten bits -->
<div>Everything on this page happens in the dev console.</div>
<script src="testing-common.js"></script>
<script src="testing2.js"></script>
</body>
</html>
+235
View File
@@ -0,0 +1,235 @@
/*
2022-05-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.
***********************************************************************
A basic test script for sqlite3-worker.js.
*/
(function(){
const T = self.SqliteTestUtil;
const SW = new Worker("sqlite3-worker.js");
/** Posts a worker message as {type:type, data:data}. */
const wMsg = function(type,data){
SW.postMessage({type, data});
return SW;
};
const log = console.log.bind(console);
const warn = console.warn.bind(console);
const error = console.error.bind(console);
SW.onerror = function(event){
error("onerror",event);
};
/**
A queue for callbacks which are to be run in response to async
DB commands. See the notes in runTests() for why we need
this. The event-handling plumbing of this file requires that
any DB command which includes a `messageId` property also have
a queued callback entry, as the existence of that property in
response payloads is how it knows whether or not to shift an
entry off of the queue.
*/
const MsgHandlerQueue = {
queue: [],
id: 0,
push: function(type,callback){
this.queue.push(callback);
return type + '-' + (++this.id);
},
shift: function(){
return this.queue.shift();
}
};
const testCount = ()=>log("Total test count:",T.counter);
const runOneTest = function(eventType, eventData, callback){
T.assert(eventData && 'object'===typeof eventData);
/* ^^^ that is for the testing and messageId-related code, not
a hard requirement of all of the Worker-exposed APIs. */
eventData.messageId = MsgHandlerQueue.push(eventType,function(ev){
log("runOneTest",eventType,"result",ev.data);
if(callback instanceof Function){
callback(ev);
testCount();
}
});
wMsg(eventType, eventData);
};
/** Methods which map directly to onmessage() event.type keys.
They get passed the inbound event.data. */
const dbMsgHandler = {
open: function(ev){
log("open result",ev.data);
},
exec: function(ev){
log("exec result",ev.data);
},
export: function(ev){
log("exec result",ev.data);
},
error: function(ev){
error("ERROR from the worker:",ev.data);
},
resultRowTest1: function f(ev){
if(undefined === f.counter) f.counter = 0;
if(ev.data) ++f.counter;
//log("exec() result row:",ev.data);
T.assert(null===ev.data || 'number' === typeof ev.data.b);
}
};
const runTests = function(){
const mustNotReach = ()=>{
throw new Error("This is not supposed to be reached.");
};
/**
"The problem" now is that the test results are async. We
know, however, that the messages posted to the worker will
be processed in the order they are passed to it, so we can
create a queue of callbacks to handle them. The problem
with that approach is that it's not error-handling
friendly, in that an error can cause us to bypass a result
handler queue entry. We have to perform some extra
acrobatics to account for that.
*/
runOneTest('open', {filename:'testing2.sqlite3'}, function(ev){
//log("open result",ev);
T.assert('testing2.sqlite3'===ev.data.filename)
.assert(ev.data.messageId);
});
runOneTest('exec',{
sql: ["create table t(a,b)",
"insert into t(a,b) values(1,2),(3,4),(5,6)"
].join(';'),
multi: true,
resultRows: [], columnNames: []
}, function(ev){
ev = ev.data;
T.assert(0===ev.resultRows.length)
.assert(0===ev.columnNames.length);
});
runOneTest('exec',{
sql: 'select a a, b b from t order by a',
resultRows: [], columnNames: [],
}, function(ev){
ev = ev.data;
T.assert(3===ev.resultRows.length)
.assert(1===ev.resultRows[0][0])
.assert(6===ev.resultRows[2][1])
.assert(2===ev.columnNames.length)
.assert('b'===ev.columnNames[1]);
});
runOneTest('exec',{
sql: 'select a a, b b from t order by a',
resultRows: [], columnNames: [],
rowMode: 'object'
}, function(ev){
ev = ev.data;
T.assert(3===ev.resultRows.length)
.assert(1===ev.resultRows[0].a)
.assert(6===ev.resultRows[2].b)
});
runOneTest('exec',{sql:'intentional_error'}, mustNotReach);
// Ensure that the message-handler queue survives ^^^ that error...
runOneTest('exec',{
sql:'select 1',
resultRows: [],
//rowMode: 'array', // array is the default in the Worker interface
}, function(ev){
ev = ev.data;
T.assert(1 === ev.resultRows.length)
.assert(1 === ev.resultRows[0][0]);
});
runOneTest('exec',{
sql: 'select a a, b b from t order by a',
callback: 'resultRowTest1',
rowMode: 'object'
}, function(ev){
T.assert(3===dbMsgHandler.resultRowTest1.counter);
dbMsgHandler.resultRowTest1.counter = 0;
});
runOneTest('exec',{sql: 'delete from t where a>3'});
runOneTest('exec',{
sql: 'select count(a) from t',
resultRows: []
},function(ev){
ev = ev.data;
T.assert(1===ev.resultRows.length)
.assert(2===ev.resultRows[0][0]);
});
runOneTest('export',{}, function(ev){
ev = ev.data;
T.assert('string' === typeof ev.filename)
.assert(ev.buffer instanceof Uint8Array)
.assert(ev.buffer.length > 1024)
.assert('application/x-sqlite3' === ev.mimetype);
});
/***** close() tests must come last. *****/
runOneTest('close',{unlink:true},function(ev){
ev = ev.data;
T.assert('string' === typeof ev.filename);
});
runOneTest('close',{unlink:true},function(ev){
ev = ev.data;
T.assert(undefined === ev.filename);
});
};
SW.onmessage = function(ev){
if(!ev.data || 'object'!==typeof ev.data){
warn("Unknown sqlite3-worker message type:",ev);
return;
}
ev = ev.data/*expecting a nested object*/;
//log("main window onmessage:",ev);
if(ev.data && ev.data.messageId){
/* We're expecting a queued-up callback handler. */
const f = MsgHandlerQueue.shift();
if('error'===ev.type){
dbMsgHandler.error(ev);
return;
}
T.assert(f instanceof Function);
f(ev);
return;
}
switch(ev.type){
case 'sqlite3-api':
switch(ev.data){
case 'loaded':
log("Message:",ev); return;
case 'ready':
log("Message:",ev);
self.sqlite3TestModule.setStatus(null);
setTimeout(runTests, 0);
return;
default:
warn("Unknown sqlite3-api message type:",ev);
return;
}
default:
if(dbMsgHandler.hasOwnProperty(ev.type)){
try{dbMsgHandler[ev.type](ev);}
catch(err){
error("Exception while handling db result message",
ev,":",err);
}
return;
}
warn("Unknown sqlite3-api message type:",ev);
}
};
log("Init complete, but async init bits may still be running.");
})();
+3
View File
@@ -3899,6 +3899,8 @@ static int fts3IncrmergePush(
pBlk->n += sqlite3Fts3PutVarint(&pBlk->a[pBlk->n], nPrefix);
}
pBlk->n += sqlite3Fts3PutVarint(&pBlk->a[pBlk->n], nSuffix);
assert( nPrefix+nSuffix<=nTerm );
assert( nPrefix>=0 );
memcpy(&pBlk->a[pBlk->n], &zTerm[nPrefix], nSuffix);
pBlk->n += nSuffix;
@@ -4021,6 +4023,7 @@ static int fts3IncrmergeAppend(
pLeaf = &pWriter->aNodeWriter[0];
nPrefix = fts3PrefixCompress(pLeaf->key.a, pLeaf->key.n, zTerm, nTerm);
nSuffix = nTerm - nPrefix;
if(nSuffix<=0 ) return FTS_CORRUPT_VTAB;
nSpace = sqlite3Fts3VarintLen(nPrefix);
nSpace += sqlite3Fts3VarintLen(nSuffix) + nSuffix;
+61 -44
View File
@@ -1,9 +1,9 @@
C Defer\smaterializing\sa\sview\sor\ssubquery\suntil\sthe\smaterialization\sis\sactually\nneeded,\sso\sthat\sif\sit\sis\snot\sneeded,\snot\suseless\swork\sis\sperformed.
D 2022-05-21T18:03:33.534
C Prevent\ssubtype\svalues\sfrom\sslipping\sacross\sa\ssubquery\sboundry\swhen\sthe\nsubquery\sis\simplemented\sas\sa\sco-routine.
D 2022-06-09T17:17:14.158
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F Makefile.in 91cf7d4ee6e97dd84cd5cdf541df498e2df038dd83f4b30d1ed8b19fbee3bbdd
F Makefile.in e45046a016985c8fedfc5e7aad88ee396033a1e9b4e929fd6e1033e4c39dba95
F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241
F Makefile.msc b28a8a7a977e7312f6859f560348e1eb110c21bd6cf9fab0d16537c0a514eef3
F README.md 8b8df9ca852aeac4864eb1e400002633ee6db84065bd01b78c33817f97d31f5e
@@ -55,11 +55,23 @@ F ext/expert/expert1.test 3c642a4e7bbb14f21ddab595436fb465a4733f47a0fe5b2855e1d5
F ext/expert/sqlite3expert.c 6ca30d73b9ed75bd56d6e0d7f2c962d2affaa72c505458619d0ff5d9cdfac204
F ext/expert/sqlite3expert.h ca81efc2679a92373a13a3e76a6138d0310e32be53d6c3bfaedabd158ea8969b
F ext/expert/test_expert.c d56c194b769bdc90cf829a14c9ecbc1edca9c850b837a4d0b13be14095c32a72
F ext/fiddle/Makefile b2904d52c10a7c984cfab95c54fb85f33aa8a6b2653faf1527d08ce57114be46
F ext/fiddle/fiddle-worker.js eae61419d5a7fcc9c4fcfda157ca0fe0a36b690b716899e5ec54a6d20c7037a9
F ext/fiddle/fiddle.html f536878dbaa35ba4d9ad8c87dda7fb2ea5502fdd824577d83b2265d65b8ca4d1
F ext/fiddle/fiddle.js 76a490d59bf0e09fb8a9610e6fa5c5032ee44680fbf9773b55974cb50d6dc3dc
F ext/fiddle/EXPORTED_FUNCTIONS.fiddle 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3
F ext/fiddle/EXPORTED_FUNCTIONS.sqlite3-api 540b9dec63a3a62a256e2f030827848a92e9b9d9b6fa5c0188295a4a1c5382cd
F ext/fiddle/EXPORTED_RUNTIME_METHODS b831017ba67ba993b34a27400cef2f6095bd6789c0fc4eba7e7a251c207be31c
F ext/fiddle/Makefile e25d34a0e1324f771d64c09c592601b97219282011587e6ce410fa8acdedb913
F ext/fiddle/SqliteTestUtil.js 559731c3e8e0de330ec7d292e6c1846566408caee6637acc8a119ac338a8781c
F ext/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
F ext/fiddle/fiddle-worker.js 88bc2193a6cb6a3f04d8911bed50a4401fe6f277de7a71ba833865ab64a1b4ae
F ext/fiddle/fiddle.html c44f86abf286231fccfed9fd080fd17b200f5e5c05939e8080c810b6920a9326
F ext/fiddle/fiddle.js 6c20bf264cf26a570f9bf22e533adcc7250a6f183d482eaa725ccd0fa83a6d95
F ext/fiddle/index.md d9c1c308d8074341bc3b11d1d39073cd77754cb3ca9aeb949f23fdd8323d81cf
F ext/fiddle/sqlite3-api.js ccf4bd0c1c5bbb3be3469573423d6c53991941bec497eac63e9f17ea13bf8952
F ext/fiddle/sqlite3-worker.js a9c2b614beca187dbdd8c053ec2770cc61ec1ac9c0ec6398ceb49a79f705a421
F ext/fiddle/testing.css 750572dded671d2cf142bbcb27af5542522ac08db128245d0b9fe410aa1d7f2a
F ext/fiddle/testing1.html ea1f3be727f78e420007f823912c1a03b337ecbb8e79449abc2244ad4fe15d9a
F ext/fiddle/testing1.js 68715f4716a30aa4dab0ce954d465f662486c8bc96a19a4fc8edbea4ac211fd2
F ext/fiddle/testing2.html 9063b2430ade2fe9da4e711addd1b51a2741cf0c7ebf6926472a5e5dd63c0bc4
F ext/fiddle/testing2.js 7b45b4e7fddbd51dbaf89b6722c02758051b34bac5a98c11b569a7e7572f88ee
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
F ext/fts1/ft_hash.h 06df7bba40dadd19597aa400a875dbc2fed705ea
@@ -108,7 +120,7 @@ F ext/fts3/fts3_tokenizer.h 64c6ef6c5272c51ebe60fc607a896e84288fcbc3
F ext/fts3/fts3_tokenizer1.c 5c98225a53705e5ee34824087478cf477bdb7004
F ext/fts3/fts3_unicode.c de426ff05c1c2e7bce161cf6b706638419c3a1d9c2667de9cb9dc0458c18e226
F ext/fts3/fts3_unicode2.c 416eb7e1e81142703520d284b768ca2751d40e31fa912cae24ba74860532bf0f
F ext/fts3/fts3_write.c 3109c1a232da86474e196cc7db754445a354409f141e08cb11c846cdb17bdf31
F ext/fts3/fts3_write.c 85279b980f99253c296006503a13f92957ec49b716123083f021acc74545ecfc
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
F ext/fts3/tool/fts3cov.sh c331d006359456cf6f8f953e37f2b9c7d568f3863f00bb5f7eb87fea4ac01b73
F ext/fts3/tool/fts3view.c 413c346399159df81f86c4928b7c4a455caab73bfbc8cd68f950f632e5751674
@@ -491,14 +503,14 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
F src/alter.c d8872f9d1863d8e31c37475e318de746e1b5ca57c0e477e35042a9ebbb6e0298
F src/alter.c 0390ca1d69ec3626cfa9f153114b7ab233e6b2bada6a9eb91361ed385fe90deb
F src/analyze.c aabdf3769c7fd9954a8ec508eb7041ae174b66f88d12c47199fabbea9a646467
F src/attach.c 4431f82f0247bf3aaf91589acafdff77d1882235c95407b36da1585c765fbbc8
F src/auth.c f4fa91b6a90bbc8e0d0f738aa284551739c9543a367071f55574681e0f24f8cf
F src/backup.c a2891172438e385fdbe97c11c9745676bec54f518d4447090af97189fd8e52d7
F src/bitvec.c 7c849aac407230278445cb069bebc5f89bf2ddd87c5ed9459b070a9175707b3d
F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
F src/btree.c df695e953c2ce78ce4878ee5016751df9bc9a921dc0500a0f53ab3bb3196e505
F src/btree.c 6f12c520c386fa56b16c2db67de6227786ca811d9c8b0e83b6a8829a10cb0d38
F src/btree.h 74d64b8f28cfa4a894d14d4ed64fa432cd697b98b61708d4351482ae15913e22
F src/btreeInt.h 8ce1332edd89dfd2461d561ac10a0ab5601c8e06200cb5230596c3caaf54482e
F src/build.c 23f874642825d7eaaeeb7a3281b2b1a75e1d4c4dd9ae4dceddcd908266634214
@@ -509,10 +521,10 @@ F src/date.c 15082566229d4b1e5f24fdb490bf9bcc68824b911d70e3573ef075a1b9e2d26f
F src/dbpage.c 90661a87e1db8bfbc8d2ebbdcd3749651ddb287c555c07a28fb17c7c591ffb68
F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d
F src/delete.c a8e844af211a48b13b5b358be77a12c860c6a557c21990ad51a548e2536500ce
F src/expr.c 19507ae3244402860cac2944be3b92bf9a8b50212fbfabaf7e9817127fec7c00
F src/expr.c 4907afcb86d72b5525d8767515ce425ec53c7a2d3664441b46cef5b376ee0cba
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c d965ede15d8360c09ed59348940649ee647b192e784466837d7aefa836d1d91e
F src/func.c a3407a6fbb0d4088d8d502e46f0ace63e0aeae7467ae23a9ca9815bbf9239761
F src/func.c 8f72e88cccdee22185133c10f96ccd61dc34c5ea4b1fa9a73c237ef59b2e64f1
F src/global.c e83ee571b79ee3adc32e380cf554cf1254bc43763d23786c71721fbcdfbbb965
F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
@@ -547,7 +559,7 @@ F src/os_win.c a8ea80037e81127ca01959daa87387cc135f325c88dc745376c4f760de852a10
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 42120492784fc9bcd9082b5c9b5e329b7318c357f9f3574a1bbfcf7418910356
F src/pager.h f82e9844166e1585f5786837ddc7709966138ced17f568c16af7ccf946c2baa3
F src/parse.y ad9a59d0078267e5aea4cf62f244c0ce6fcec9a91edd365e929c75d3a1716563
F src/parse.y 8e67d820030d2655b9942ffe61c1e7e6b96cea2f2f72183533299393907d0564
F src/pcache.c 084e638432c610f95aea72b8509f0845d2791293f39d1b82f0c0a7e089c3bb6b
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
F src/pcache1.c 54881292a9a5db202b2c0ac541c5e3ef9a5e8c4f1c1383adb2601d5499a60e65
@@ -558,16 +570,16 @@ F src/printf.c 6166a30417b05c5b2f82e1f183f75faa2926ad60531c0b688a57dbc951441a20
F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c
F src/resolve.c a4eb3c617027fd049b07432f3b942ea7151fa793a332a11a7d0f58c9539e104f
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
F src/select.c 4f13d01141caae4f982125fecf50703c746ab621c57ca0aa747fab2b28e88c1e
F src/shell.c.in 55d71bf8c7a8f2a66bc5f99cd76f226790b291599b83415533dad84a553ed806
F src/sqlite.h.in d15c307939039086adca159dd340a94b79b69827e74c6d661f343eeeaefba896
F src/select.c 5f2dfc578ee9cc313a32f5848b9d9ba7d8dab8095591ed746dcee4a271890508
F src/shell.c.in 08e59f1cb9d9b1180aba52861aaada0c95f6ddd210488719684e160a0724c806
F src/sqlite.h.in 172528c287399a34f188154017b7268bf82c6d5b780902e361958d2318c4e37c
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h a988810c9b21c0dc36dc7a62735012339dc76fc7ab448fb0792721d30eacb69d
F src/sqliteInt.h 3064533677f135771e71843b5221482df18d6589afe65e6a7ef828ccb8879a5f
F src/sqliteInt.h 8353e96646372efdb0795a13cd9949831b4992c928de8f5c43b2524e8a4c6e7b
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
F src/status.c 4a3da6d77eeb3531cb0dbdf7047772a2a1b99f98c69e90ce009c75fe6328b2c0
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
F src/tclsqlite.c 1f6673991147bc2cecc08a40d22f9803b84c805b24b499fe727f392256f73474
F src/tclsqlite.c 4e64ba300a5a26e0f1170e09032429faeb65e45e8f3d1a7833e8edb69fc2979e
F src/test1.c 1356984e97bff07e4a8cc3863e892f05b3348678a74783bb6f350b76316736f1
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644
@@ -623,20 +635,20 @@ F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c a38f52058b517929e264094abd0b5fd1e8e145a1aa43bc6f6a72ae5218f96c98
F src/treeview.c 73facf395c8841653b9a54e789d8c80e15bc3d0d1cb9d16104c2d889c15e33cd
F src/trigger.c 4fe4c1ac811755aff49d669d2e52e414eb5dfa6e172e849ab7b6825e70a571c0
F src/treeview.c c48bbb4b04a951dcecf95b464d0fe94930339af56688a77f18ee50a526bc1706
F src/trigger.c 61bea163b1fa3039bc572ed8312461b978e5c527e5301f302b078f4c1ccdec6a
F src/update.c 2cfaded82ca80ff56afb8c3ae5e88284e0824bfd86119827cc22481959f96f92
F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
F src/util.c 602fe229f32a96ceccae4f40824129669582096f7c355f53dbac156c9fecef23
F src/vacuum.c bb346170b0b54c6683bba4a5983aea40485597fdf605c87ec8bc2e199fe88cd8
F src/vdbe.c a6a20956fa128c0b8d3c5592a9a3db890a46cf626f46ffee0cab2ac5bf8d04b2
F src/vdbe.c 388ced8d8c8724f8c1e9395b0727c628d7b9b813dfcb4fc67a67c56e46896be2
F src/vdbe.h 07641758ca8b4f4c6d81ea667ea167c541e6ece21f5574da11e3d21ec37e2662
F src/vdbeInt.h ef43f7fdc5fde29fc3fd29c506c12830f366178fdb4edbbf0cbc3dfbd1278b5f
F src/vdbeapi.c 354c893f1500cf524cc45c32879b9c68893a28b77e3442c24668d6afe4236217
F src/vdbeaux.c 75c4f75ed7e1d12eb3d80093a160ec998c839f3008a1c3c967fc5acf522d0e3c
F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd
F src/vdbemem.c 7189090b72baa025f945a1ac8c61ee420c645254476e8a191d555db76dfea5d4
F src/vdbemem.c 3db315458f8dc158aff58719795441437dd6c0fd302e9d9379a8f2a61e185ad6
F src/vdbesort.c 43756031ca7430f7aec3ef904824a7883c4ede783e51f280d99b9b65c0796e35
F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf823
F src/vdbevtab.c f99b275366c5fc5e2d99f734729880994ab9500bdafde7fae3b02d562b9d323c
@@ -645,10 +657,10 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d
F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
F src/where.c 226b9b5485d101fe4f99db2a7a1ce3fc2ab5be270b539817fd7dcbefdbfd440a
F src/whereInt.h 8da918f392bf202ccc0ee61291455b33ad171d209445f1ff3eaf62e0b6f6b363
F src/wherecode.c 2a8a73bcf1886632f2b2247c79395f94852a4b74484d8aa70a005892ce73d339
F src/whereexpr.c efed370c684dce04eab949202c5452bbde993efb198de43c7a88f59411ad2a2c
F src/where.c 7a5c084800eab6ed9006bccd5d3116a7a6a998c56c525b22f62f131b3b133189
F src/whereInt.h b48ca529ffe293c18cbfa8326af18a09e39910de66fb3e96ef788c7cbf8ef3a7
F src/wherecode.c 0b09abfcb88c61c6a6984a3e065786631ff35495e9bdf865e6b74ab0a1299c5b
F src/whereexpr.c 20255cf03e0b765b742301197d165511ff99e95da0d7ee9c8a2ebc1e888dd049
F src/window.c fff1b51757438c664e471d5184634e48dcdf8ea34b640f3b1b0810b1e06de18c
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
@@ -671,11 +683,12 @@ F test/alterfault.test 289067108947bedca27534edd4ff251bcd298cf84402d7b24eaa37493
F test/alterlegacy.test f38c6d06cda39e1f7b955bbce57f2e3ef5b7cb566d3d1234502093e228c15811
F test/altermalloc.test 167a47de41b5c638f5f5c6efb59784002b196fff70f98d9b4ed3cd74a3fb80c9
F test/altermalloc2.test 17fb3724c4b004c469c27dc4ef181608aa644555fbd3f3236767584f73747c81
F test/altermalloc3.test 55e606edf4b0acfbbd851ddfe93cfdddfae43d103644dcfd6008ae4ab3c44adf
F test/altermalloc3.test 8531b3086f0a7889f43971a579a8c81832d5123f4703d8c86b89ba1136c63b9a
F test/alterqf.test ff6c6f881485c29ed699b8ef4774864ca1b0c01a6c08f5cdd624a008e4b40fca
F test/altertab.test 7273b8506eab46342be016af78028df49f3bd99037412f997a8f1011b37a6912
F test/altertab2.test 62597b6fd08feaba1b6bfe7d31dac6117c67e06dc9ce9c478a3abe75b5926de0
F test/altertab3.test 8af5c6eb4a7dd2fc73235b865b53561bf07428d1d6a9cd59a067abf51141891e
F test/altertab3.test 6c432fbb9963e0bd6549bf1422f6861d744ee5a80cb3298564e81e556481df16
F test/altertrig.test fb5951d21a2c954be3b8a8cf8e10b5c0fa20687c53fd67d63cea88d08dd058d5
F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f
F test/analyze.test 547bb700f903107b38611b014ca645d6b5bb819f5210d7bf39c40802aafeb7d7
F test/analyze3.test 4440c4932247adb2b4e0c838f657c19dc7af4f56859255436dc4e855f39b9324
@@ -776,6 +789,7 @@ F test/carray01.test d55d57bf66b1af1c7ac55fae66ff4910884a8f5d21a90a18797ce386212
F test/cast.test 336fa21989b5170ebcaf90c24266be22dd97b3e23d1fad5ecf6ad4efb04c4423
F test/cffault.test 9d6b20606afe712374952eec4f8fd74b1a8097ef
F test/changes.test 9dd8e597d84072122fc8a4fcdea837f4a54a461e6e536053ea984303e8ca937b
F test/changes2.test d222c0cbf5ab0ac4d7c180594e486c1bf20b2098d33e56ce33b8e12eba6823b9
F test/check.test 56e4ed457e9f8683b9fc56f5b964f461f6e8a8dd5a13f3d495408215d66419ed
F test/checkfault.test da6cb3d50247169efcb20bdf57863a3ccfa1d27d9e55cd324f0680096970f014
F test/chunksize.test 427d87791743486cbf0c3b8c625002f3255cb3a89c6eba655a98923b1387b760
@@ -990,12 +1004,12 @@ F test/fts3b.test c15c4a9d04e210d0be67e54ce6a87b927168fbf9c1e3faec8c1a732c366fd4
F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958
F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c
F test/fts3conf.test c84bbaec81281c1788aa545ac6e78a6bd6cde2bdbbce2da261690e3659f5a76b
F test/fts3corrupt.test 43c6c89b994e90997590ece4dfa9c9325c9b61cddd7c97e158498da8b1de79f8
F test/fts3corrupt.test 6732477c5ace050c5758a40a8b5706c8c0cccd416b9c558e0e15224805a40e57
F test/fts3corrupt2.test e318f0676e5e78d5a4b702637e2bb25265954c08a1b1e4aaf93c7880bb0c67d0
F test/fts3corrupt3.test 0d5b69a0998b4adf868cc301fc78f3d0707745f1d984ce044c205cdb764b491f
F test/fts3corrupt4.test 799ff994b964fed7201be6b6b62c7ff2ef7bb3da6c02b9eaf0d96a5a4d9b6ca3
F test/fts3corrupt5.test 0549f85ec4bd22e992f645f13c59b99d652f2f5e643dac75568bfd23a6db7ed5
F test/fts3corrupt6.test 657b4b8e5791d8d4adc93c90588fb25f1c7346544dd877c6c298a0746749146d
F test/fts3corrupt6.test f417c910254f32c0bc9ead7affa991a1d5aec35b3b32a183ffb05eea78289525
F test/fts3cov.test 7eacdbefd756cfa4dc2241974e3db2834e9b372ca215880e00032222f32194cf
F test/fts3d.test 2bd8c97bcb9975f2334147173b4872505b6a41359a4f9068960a36afe07a679f
F test/fts3defer.test f4c20e4c7153d20a98ee49ee5f3faef624fefc9a067f8d8d629db380c4d9f1de
@@ -1063,7 +1077,7 @@ F test/func3.test 600a632c305a88f3946d38f9a51efe145c989b2e13bd2b2a488db47fe76bab
F test/func4.test 2285fb5792d593fef442358763f0fd9de806eda47dbc7a5934df57ffdc484c31
F test/func5.test 863e6d1bd0013d09c17236f8a13ea34008dd857d87d85a13a673960e4c25d82a
F test/func6.test 9cc9b1f43b435af34fe1416eb1e318c8920448ea7a6962f2121972f5215cb9b0
F test/func7.test b9e2a1a30a8562b00841b4a21a5d2d81754fa3ab99275fd71fd5279287b44b1c
F test/func7.test adbfc910385a6ffd15dc47be3c619ef070c542fcb7488964badb17b2d9a4d080
F test/fuzz-oss1.test 514dcabb24687818ea949fa6760229eaacad74ca70157743ef36d35bbe01ffb0
F test/fuzz.test 4608c1310cff4c3014a84bcced6278139743e080046e5f6784b0de7b069371d8
F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1
@@ -1102,7 +1116,7 @@ F test/in5.test b32ce7f4a93f44c5dee94af16886d922cc16ebe33c8e1765c73d4049d0f4b40f
F test/in6.test f5f40d6816a8bb7c784424b58a10ac38efb76ab29127a2c17399e0cbeeda0e4b
F test/incrblob.test c9b96afc292aeff43d6687bcb09b0280aa599822
F test/incrblob2.test a494c9e848560039a23974b9119cfc2cf3ad3bd15cc2694ee6367ae537ef8f1f
F test/incrblob3.test d47be78a46da142dd60b93772db6e03936a8a36a3b6129dff8d11923dfdc6d63
F test/incrblob3.test 67621a04b3084113bf38ce03797d70eca012d9d8f948193b8f655df577b0da6f
F test/incrblob4.test 21a52a6843a56cdcce968c6a86b72a7066d0e6ba
F test/incrblob_err.test 89372a28f1d98254f03fed705f9efcd34ef61a674df16d2dbb4726944a2de5e9
F test/incrblobfault.test de274b1e329169c2c3438f9528994807ea8201ebf38ae9f157d34bf3ec0cc549
@@ -1116,10 +1130,10 @@ F test/index2.test f835d5e13ca163bd78c4459ca15fd2e4ed487407
F test/index3.test 51685f39345462b84fcf77eb8537af847fdf438cc96b05c45d6aaca4e473ade0
F test/index4.test ab92e736d5946840236cd61ac3191f91a7856bf6
F test/index5.test 8621491915800ec274609e42e02a97d67e9b13e7
F test/index6.test 6e5b6943f6a97a34898e48c4d0d4990caf55c12c00465a43a9c33d2fd9a3a820
F test/index6.test b376a648e85aa71c50074382784e6cb0c126ec46e43d1ad15af9a4d234c52e65
F test/index7.test b238344318e0b4e42126717f6554f0e7dfd0b39cecad4b736039b43e1e3b6eb3
F test/index8.test caa097735c91dbc23d8a402f5e63a2a03c83840ba3928733ed7f9a03f8a912a3
F test/index9.test 0aa3e509dddf81f93380396e40e9bb386904c1054924ba8fa9bcdfe85a8e7721
F test/index9.test 2ac891806a4136ef3e91280477e23114e67575207dc331e6797fa0ed9379f997
F test/indexedby.test f21eca4f7a6ffe14c8500a7ad6cd53166666c99e5ccd311842a28bc94a195fe0
F test/indexexpr1.test 3360c2a29a8844e7c4b13293567025281257f9e13a31854cfff6959cede11502
F test/indexexpr2.test 2c7abe3c48f8aaa5a448615ab4d13df3662185d28419c00999670834a3f0b484
@@ -1146,20 +1160,21 @@ F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c
F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4
F test/ioerr6.test a395a6ab144b26a9e3e21059a1ab6a7149cca65b
F test/istrue.test e7f285bb70282625c258e866ce6337d4c762922f5a300e1b50f958aef6e7d9c9
F test/join.test edeaff6edc1c1a2bcfebee343744e04d000f861c3d67cb653114f88565f8c955
F test/join.test 5c7f917aa219a125d1df517d3df384c79f74860fdfb2e48c0a599f1e2b3e9ed8
F test/join2.test 466b07233820f5deee66a6c3bf6e4500c8bbf7b83649e67606f5f649c07928c0
F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
F test/join5.test d22b6cba8fb59ab3f1c82701434c360705eb12d4ce200c449f37b018fc47681a
F test/join6.test f809c025fa253f9e150c0e9afd4cef8813257bceeb6f46e04041228c9403cc2c
F test/join7.test 8e72de4b45e5e930d18c305c7efe86015fb2552731e4e03ea226353036b0dab0
F test/join8.test adf30584c6d0cc61816b24fd6f3cc8386ce89f579da4b65a6c8e05c2f2dce08e
F test/join7.test 2268dcbb54b724391dda3748ea95c60d960607ffeed67885675998e7117697f6
F test/join8.test d799d60485852037f496fefb513c39d027d5649c39045fb5b31bda521436439c
F test/join9.test 9056ddd3b0c0f4f9d658f4521038d9a37dc23ead8ca9a505d0b0db2b6a471e05
F test/joinA.test 7eab225dc1c1ab258a5e62513a4ed7cabbd3db971d59d5d92f4fb6fa14c12f6a
F test/joinB.test 1b2ba3fc8568b49411787fccbf540570c148e9b6a53a30f80691cb6268098ded
F test/joinC.test 1f1a602c2127f55f136e2cbd3bf2d26546614bf8cffe5902ec1ac9c07f87f207
F test/joinD.test 7f0f4dd1f2767330bf1fda5c9cc8a437015a54bcd2355036b4d04ddfc1519d76
F test/joinD.test 1a430af8dac5b68663f13df534ffe98775e582bac2305b80f1e8eb4ab778672a
F test/joinE.test d5d182f3812771e2c0d97c9dcf5dbe4c41c8e21c82560e59358731c4a3981d6b
F test/joinF.test 53dd66158806823ea680dd7543b5406af151b5aafa5cd06a7f3231cd94938127
F test/journal1.test c7b768041b7f494471531e17abc2f4f5ebf9e5096984f43ed17c4eb80ba34497
F test/journal2.test 9dac6b4ba0ca79c3b21446bbae993a462c2397c4
F test/journal3.test 7c3cf23ffc77db06601c1fcfc9743de8441cb77db9d1aa931863d94f5ffa140e
@@ -1340,9 +1355,9 @@ F test/round1.test 768018b04522ca420b1aba8a24bd76091d269f3bce3902af3ec6ebcee41ab
F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
F test/rowid.test e29025be95baf6b32f0d5edef59a7633028325896a98f1caa8019559ca910350
F test/rowvalue.test 228b312f8526ed000ecda559a6a9adf30aa2d79dcb12afa5c04eebaafcf55eae
F test/rowvalue.test ff1ffa31cebe12feb6f989e09263f3b1e8c560db94b40fe006126a8435fd6832
F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b
F test/rowvalue3.test 3068f508753af69884b12125995f023da0dbb256
F test/rowvalue3.test 1347e25ca11c547c5a6ff0cc5626f95aa9740e9275bfaec096029f57cb2130ce
F test/rowvalue4.test 441e7e366ac6d939a3a95a574031c56ec2a854077a91d66eee5ff1d86cb5be58
F test/rowvalue5.test 00740304ea6a53a8704640c7405690f0045d5d2a6b4b04dde7bccc14c3068ea7
F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087
@@ -1464,7 +1479,7 @@ F test/subquery.test 3a1a5b600b8d4f504d2a2c61f33db820983dba94a0ef3e4aedca8f0165e
F test/subquery2.test 90cf944b9de8204569cf656028391e4af1ccc8c0cc02d4ef38ee3be8de1ffb12
F test/subselect.test 0966aa8e720224dbd6a5e769a3ec2a723e332303
F test/substr.test a673e3763e247e9b5e497a6cacbaf3da2bd8ec8921c0677145c109f2e633f36b
F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8
F test/subtype1.test 45c85632abd38f7ea9b33f17448d966d67550f552e0822bab74576814d0d1718
F test/superlock.test ec94f0556b6488d97f71c79f9061ae08d9ab8f12
F test/swarmvtab.test 250231404fcac88f61a6c147bb0e3a118ed879278cd3ccb0ae2d3a729e1e8e26
F test/swarmvtab2.test c948cb2fdfc5b01d85e8f6d6504854202dc1a0782ab2a0ed61538f27cbd0aa5c
@@ -1697,6 +1712,7 @@ F test/upfrom1.tcl 8859d9d437f03b44174c4524a7a734a391fd4526fcff65be08285dafc9dc9
F test/upfrom1.test 8cb06689e99cd707d884faa16da0e8eb26ff658bb01c47ddf72fadade666e6e1
F test/upfrom2.test 66f3ebf721b3cebd922faee5c386bf244f816d416b57c000753ff51af62328a1
F test/upfrom3.test 6130f24ebf97f5ea865e5d2a14a2d543fe5428a62e87cc60f62d875e45c1f5f0
F test/upfrom4.test 1cd82e9423e02b1f63d069e8665c6c3932ec424fd0043d033cc0ba99abf33236
F test/upfromfault.test 3a10075a0043f0c4fad6614b2c371f88a8ba5a4acab68b907438413865d6a8d6
F test/upsert1.test b0ae2f58680c5205b4bc1cdeed3c3d444057c506f6c44494fa3eac60731d68a2
F test/upsert2.test 720e94d09f7362a282bc69b3c6b83d51daeaaf0440eb4920a08b86518b8c7496
@@ -1830,6 +1846,7 @@ F test/window9.test 349c71eab4288a1ffc19e2f65872ec2c37e6cf8a1dda2ad300364b7450ae
F test/windowA.test 6d63dc1260daa17141a55007600581778523a8b420629f1282d2acfc36af23be
F test/windowB.test f2fb42b864b0cf431c956407583e9478a74c3642bdf8737fdcb6ff4a40298b07
F test/windowC.test 6fd75f5bb2f1343d34e470e36e68f0ff638d8a42f6aa7d99471261b31a0d42f2
F test/windowD.test 65cf5a765fb8072450e8a0de2979ce7f09a38d87724fe1280c6444073e3da49b
F test/windowerr.tcl f5acd6fbc210d7b5546c0e879d157888455cd4a17a1d3f28f07c1c8a387019e0
F test/windowerr.test a8b752402109c15aa1c5efe1b93ccb0ce1ef84fa964ae1cd6684dd0b3cc1819b
F test/windowfault.test 15094c1529424e62f798bc679e3fe9dfab6e8ba2f7dfe8c923b6248c31660a7c
@@ -1959,8 +1976,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 34d7045b2cc8ef16fd4115499888703a376d2c3a289e7fdd117f8341ffa586c3
R 058fc768a58c4fd785bb93fdbe45be57
P 08af1fe27ebd0edf6e0f1ac477deea033e7f7c813f1016b75196836daf02d2e4
R c35df4ec51cfa3b0337adabea0f5033f
U drh
Z dfce5b150de5672fb6eac9f2e6af6b0f
Z f8e714816d78c089a301e0aab2d70435
# Remove this line to create a well-formed Fossil manifest.
+1 -1
View File
@@ -1 +1 @@
16bf350683fd6ac906dbd02b21fb8bf1b1014ed05594cacf108645acd383ae65
9e51a6c0fbfb1899b2b01888430125fba6d4da9bad9eeaa3ad41e29fca54bbe5
+33 -18
View File
@@ -1317,27 +1317,33 @@ static int renameResolveTrigger(Parse *pParse){
if( rc==SQLITE_OK && pStep->zTarget ){
SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep);
if( pSrc ){
int i;
for(i=0; i<pSrc->nSrc && rc==SQLITE_OK; i++){
SrcItem *p = &pSrc->a[i];
p->iCursor = pParse->nTab++;
if( p->pSelect ){
sqlite3SelectPrep(pParse, p->pSelect, 0);
sqlite3ExpandSubquery(pParse, p);
assert( i>0 );
assert( pStep->pFrom->a[i-1].pSelect );
sqlite3SelectPrep(pParse, pStep->pFrom->a[i-1].pSelect, 0);
}else{
p->pTab = sqlite3LocateTableItem(pParse, 0, p);
if( p->pTab==0 ){
rc = SQLITE_ERROR;
}else{
p->pTab->nTabRef++;
rc = sqlite3ViewGetColumnNames(pParse, p->pTab);
Select *pSel = sqlite3SelectNew(
pParse, pStep->pExprList, pSrc, 0, 0, 0, 0, 0, 0
);
if( pSel==0 ){
pStep->pExprList = 0;
pSrc = 0;
rc = SQLITE_NOMEM;
}else{
sqlite3SelectPrep(pParse, pSel, 0);
rc = pParse->nErr ? SQLITE_ERROR : SQLITE_OK;
assert( pStep->pExprList==0 || pStep->pExprList==pSel->pEList );
assert( pSrc==pSel->pSrc );
if( pStep->pExprList ) pSel->pEList = 0;
pSel->pSrc = 0;
sqlite3SelectDelete(db, pSel);
}
if( pStep->pFrom ){
int i;
for(i=0; i<pStep->pFrom->nSrc && rc==SQLITE_OK; i++){
SrcItem *p = &pStep->pFrom->a[i];
if( p->pSelect ){
sqlite3SelectPrep(pParse, p->pSelect, 0);
}
}
}
if( rc==SQLITE_OK && db->mallocFailed ){
if( db->mallocFailed ){
rc = SQLITE_NOMEM;
}
sNC.pSrcList = pSrc;
@@ -1789,6 +1795,15 @@ static void renameTableFunc(
if( pStep->zTarget && 0==sqlite3_stricmp(pStep->zTarget, zOld) ){
renameTokenFind(&sParse, &sCtx, pStep->zTarget);
}
if( pStep->pFrom ){
int i;
for(i=0; i<pStep->pFrom->nSrc; i++){
SrcItem *pItem = &pStep->pFrom->a[i];
if( 0==sqlite3_stricmp(pItem->zName, zOld) ){
renameTokenFind(&sParse, &sCtx, pItem->zName);
}
}
}
}
}
}
+1 -1
View File
@@ -9165,7 +9165,7 @@ int sqlite3BtreeInsert(
TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n",
pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno,
loc==0 ? "overwrite" : "new entry"));
assert( pPage->isInit );
assert( pPage->isInit || CORRUPT_DB );
newCell = pBt->pTmpSpace;
assert( newCell!=0 );
if( flags & BTREE_PREFORMAT ){
+30 -16
View File
@@ -3955,7 +3955,17 @@ static int exprCodeInlineFunction(
caseExpr.x.pList = pFarg;
return sqlite3ExprCodeTarget(pParse, &caseExpr, target);
}
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
case INLINEFUNC_sqlite_offset: {
Expr *pArg = pFarg->a[0].pExpr;
if( pArg->op==TK_COLUMN && pArg->iTable>=0 ){
sqlite3VdbeAddOp3(v, OP_Offset, pArg->iTable, pArg->iColumn, target);
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
}
break;
}
#endif
default: {
/* The UNLIKELY() function is a no-op. The result is the value
** of the first argument.
@@ -4494,20 +4504,8 @@ expr_code_doover:
if( !pColl ) pColl = db->pDfltColl;
sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
}
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
if( (pDef->funcFlags & SQLITE_FUNC_OFFSET)!=0 && ALWAYS(pFarg!=0) ){
Expr *pArg = pFarg->a[0].pExpr;
if( pArg->op==TK_COLUMN ){
sqlite3VdbeAddOp3(v, OP_Offset, pArg->iTable, pArg->iColumn, target);
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
}
}else
#endif
{
sqlite3VdbeAddFunctionCall(pParse, constMask, r1, target, nFarg,
pDef, pExpr->op2);
}
sqlite3VdbeAddFunctionCall(pParse, constMask, r1, target, nFarg,
pDef, pExpr->op2);
if( nFarg ){
if( constMask==0 ){
sqlite3ReleaseTempRange(pParse, r1, nFarg);
@@ -4579,8 +4577,24 @@ expr_code_doover:
exprCodeBetween(pParse, pExpr, target, 0, 0);
return target;
}
case TK_COLLATE: {
if( !ExprHasProperty(pExpr, EP_Collate)
&& ALWAYS(pExpr->pLeft)
&& pExpr->pLeft->op==TK_FUNCTION
){
inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
if( inReg!=target ){
sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target);
inReg = target;
}
sqlite3VdbeAddOp1(v, OP_ClrSubtype, inReg);
return inReg;
}else{
pExpr = pExpr->pLeft;
goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. */
}
}
case TK_SPAN:
case TK_COLLATE:
case TK_UPLUS: {
pExpr = pExpr->pLeft;
goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. OSSFuzz. */
+3 -4
View File
@@ -2098,11 +2098,11 @@ static void logFunc(
switch( SQLITE_PTR_TO_INT(sqlite3_user_data(context)) ){
case 1:
/* Convert from natural logarithm to log base 10 */
ans *= 1.0/M_LN10;
ans /= M_LN10;
break;
case 2:
/* Convert from natural logarithm to log base 2 */
ans *= 1.0/M_LN2;
ans /= M_LN2;
break;
default:
break;
@@ -2241,8 +2241,7 @@ void sqlite3RegisterBuiltinFunctions(void){
INLINE_FUNC(likelihood, 2, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY),
INLINE_FUNC(likely, 1, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY),
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
{1, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_FUNC_OFFSET|SQLITE_FUNC_TYPEOF,
0, 0, noopFunc, 0, 0, 0, "sqlite_offset", {0} },
INLINE_FUNC(sqlite_offset, 1, INLINEFUNC_sqlite_offset, 0 ),
#endif
FUNCTION(ltrim, 1, 1, 0, trimFunc ),
FUNCTION(ltrim, 2, 1, 0, trimFunc ),
+24 -2
View File
@@ -924,7 +924,18 @@ where_opt_ret(A) ::= WHERE expr(X) RETURNING selcollist(Y).
cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
where_opt_ret(W) orderby_opt(O) limit_opt(L). {
sqlite3SrcListIndexedBy(pParse, X, &I);
X = sqlite3SrcListAppendList(pParse, X, F);
if( F ){
SrcList *pFromClause = F;
if( pFromClause->nSrc>1 ){
Select *pSubquery;
Token as;
pSubquery = sqlite3SelectNew(pParse,0,pFromClause,0,0,0,0,SF_NestedFrom,0);
as.n = 0;
as.z = 0;
pFromClause = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0);
}
X = sqlite3SrcListAppendList(pParse, X, pFromClause);
}
sqlite3ExprListCheckLength(pParse,Y,"set list");
#ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
if( O || L ){
@@ -940,7 +951,18 @@ cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
where_opt_ret(W). {
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3ExprListCheckLength(pParse,Y,"set list");
X = sqlite3SrcListAppendList(pParse, X, F);
if( F ){
SrcList *pFromClause = F;
if( pFromClause->nSrc>1 ){
Select *pSubquery;
Token as;
pSubquery = sqlite3SelectNew(pParse,0,pFromClause,0,0,0,0,SF_NestedFrom,0);
as.n = 0;
as.z = 0;
pFromClause = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0);
}
X = sqlite3SrcListAppendList(pParse, X, pFromClause);
}
sqlite3Update(pParse,X,Y,W,R,0,0,0);
}
%endif
+73 -11
View File
@@ -610,6 +610,7 @@ static int sqlite3ProcessJoin(Parse *pParse, Select *p){
sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor, joinType);
p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->u3.pOn);
pRight->u3.pOn = 0;
pRight->fg.isOn = 1;
}
}
return 0;
@@ -2193,10 +2194,11 @@ int sqlite3ColumnsFromExprList(
}
if( pColExpr->op==TK_COLUMN
&& ALWAYS( ExprUseYTab(pColExpr) )
&& (pTab = pColExpr->y.pTab)!=0
&& ALWAYS( pColExpr->y.pTab!=0 )
){
/* For columns use the column name name */
int iCol = pColExpr->iColumn;
pTab = pColExpr->y.pTab;
if( iCol<0 ) iCol = pTab->iPKey;
zName = iCol>=0 ? pTab->aCol[iCol].zCnName : "rowid";
}else if( pColExpr->op==TK_ID ){
@@ -3762,9 +3764,10 @@ static Expr *substExpr(
Expr *pExpr /* Expr in which substitution occurs */
){
if( pExpr==0 ) return 0;
if( ExprHasProperty(pExpr, EP_OuterON)
if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON)
&& pExpr->w.iJoin==pSubst->iTable
){
testcase( ExprHasProperty(pExpr, EP_InnerON) );
pExpr->w.iJoin = pSubst->iNewTable;
}
if( pExpr->op==TK_COLUMN
@@ -3809,6 +3812,11 @@ static Expr *substExpr(
}
sqlite3ExprDelete(db, pExpr);
pExpr = pNew;
if( pExpr->op==TK_TRUEFALSE ){
pExpr->u.iValue = sqlite3ExprTruthValue(pExpr);
pExpr->op = TK_INTEGER;
ExprSetProperty(pExpr, EP_IntValue);
}
/* Ensure that the expression now has an implicit collation sequence,
** just as it did when it was a column of a view or sub-query. */
@@ -4162,6 +4170,11 @@ static void renumberCursors(
**
** (28) The subquery is not a MATERIALIZED CTE.
**
** (29) Either the subquery is not the right-hand operand of a join with an
** ON or USING clause nor the right-hand operand of a NATURAL JOIN, or
** the right-most table within the FROM clause of the subquery
** is not part of an outer join.
**
**
** In this routine, the "p" parameter is a pointer to the outer query.
** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query
@@ -4289,6 +4302,35 @@ static int flattenSubquery(
return 0; /* (28) */
}
/* Restriction (29):
**
** We do not want two constraints on the same term of the flattened
** query where one constraint has EP_InnerON and the other is EP_OuterON.
** To prevent this, one or the other of the following conditions must be
** false:
**
** (29a) The right-most entry in the FROM clause of the subquery
** must not be part of an outer join.
**
** (29b) The subquery itself must not be the right operand of a
** NATURAL join or a join that as an ON or USING clause.
**
** These conditions are sufficient to keep an EP_OuterON from being
** flattened into an EP_InnerON. Restrictions (3a) and (27) prevent
** an EP_InnerON from being flattened into an EP_OuterON.
*/
if( pSubSrc->nSrc>=2
&& (pSubSrc->a[pSubSrc->nSrc-1].fg.jointype & JT_OUTER)!=0
){
if( (pSubitem->fg.jointype & JT_NATURAL)!=0
|| pSubitem->fg.isUsing
|| NEVER(pSubitem->u3.pOn!=0) /* ON clause already shifted into WHERE */
|| pSubitem->fg.isOn
){
return 0;
}
}
/* Restriction (17): If the sub-query is a compound SELECT, then it must
** use only the UNION ALL operator. And none of the simple select queries
** that make up the compound SELECT are allowed to be aggregate or distinct
@@ -6518,6 +6560,29 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
}
#endif /* SQLITE_COUNTOFVIEW_OPTIMIZATION */
/*
** If any term of pSrc, or any SF_NestedFrom sub-query, is not the same
** as pSrcItem but has the same alias as p0, then return true.
** Otherwise return false.
*/
static int sameSrcAlias(SrcItem *p0, SrcList *pSrc){
int i;
for(i=0; i<pSrc->nSrc; i++){
SrcItem *p1 = &pSrc->a[i];
if( p1==p0 ) continue;
if( p0->pTab==p1->pTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){
return 1;
}
if( p1->pSelect
&& (p1->pSelect->selFlags & SF_NestedFrom)!=0
&& sameSrcAlias(p0, p1->pSelect->pSrc)
){
return 1;
}
}
return 0;
}
/*
** Generate code for the SELECT statement given in the p argument.
**
@@ -6622,15 +6687,12 @@ int sqlite3Select(
** disallow it altogether. */
if( p->selFlags & SF_UFSrcCheck ){
SrcItem *p0 = &p->pSrc->a[0];
for(i=1; i<p->pSrc->nSrc; i++){
SrcItem *p1 = &p->pSrc->a[i];
if( p0->pTab==p1->pTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){
sqlite3ErrorMsg(pParse,
"target object/alias may not appear in FROM clause: %s",
p0->zAlias ? p0->zAlias : p0->pTab->zName
);
goto select_end;
}
if( sameSrcAlias(p0, p->pSrc) ){
sqlite3ErrorMsg(pParse,
"target object/alias may not appear in FROM clause: %s",
p0->zAlias ? p0->zAlias : p0->pTab->zName
);
goto select_end;
}
/* Clear the SF_UFSrcCheck flag. The check has already been performed,
+62 -5
View File
@@ -1838,7 +1838,11 @@ static int safeModeAuth(
UNUSED_PARAMETER(zA4);
switch( op ){
case SQLITE_ATTACH: {
#ifndef SQLITE_SHELL_WASM_MODE
/* In WASM builds the filesystem is a virtual sandbox, so
** there's no harm in using ATTACH. */
failIfSafeMode(p, "cannot run ATTACH in safe mode");
#endif
break;
}
case SQLITE_FUNCTION: {
@@ -4293,7 +4297,9 @@ static const char *(azHelp[]) = {
".connection [close] [#] Open or close an auxiliary database connection",
".databases List names and files of attached databases",
".dbconfig ?op? ?val? List or change sqlite3_db_config() options",
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
".dbinfo ?DB? Show status information about the database",
#endif
".dump ?OBJECTS? Render database content as SQL",
" Options:",
" --data-only Output only INSERT statements",
@@ -4394,6 +4400,9 @@ static const char *(azHelp[]) = {
" --bom Put a UTF8 byte-order mark at the beginning",
" -e Send output to the system text editor",
" -x Send output as CSV to a spreadsheet (same as \".excel\")",
/* Note that .open is (partially) available in WASM builds but is
** currently only intended to be used by the fiddle tool, not
** end users, so is "undocumented." */
".open ?OPTIONS? ?FILE? Close existing database and reopen FILE",
" Options:",
" --append Use appendvfs to append database to the end of FILE",
@@ -4407,12 +4416,14 @@ static const char *(azHelp[]) = {
" --nofollow Do not follow symbolic links",
" --readonly Open FILE readonly",
" --zip FILE is a ZIP archive",
#ifndef SQLITE_SHELL_WASM_MODE
".output ?FILE? Send output to FILE or stdout if FILE is omitted",
" If FILE begins with '|' then open it as a pipe.",
" Options:",
" --bom Prefix output with a UTF8 byte-order mark",
" -e Send output to the system text editor",
" -x Send output as CSV to a spreadsheet",
#endif
".parameter CMD ... Manage SQL parameter bindings",
" clear Erase all bindings",
" init Initialize the TEMP table that holds bindings",
@@ -5826,6 +5837,7 @@ static int db_int(sqlite3 *db, const char *zSql){
return res;
}
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
/*
** Convert a 2-byte or 4-byte big-endian integer into a native integer
*/
@@ -5932,6 +5944,8 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){
utf8_printf(p->out, "%-20s %u\n", "data version", iDataVersion);
return 0;
}
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE)
&& defined(SQLITE_ENABLE_DBPAGE_VTAB) */
/*
** Print the current sqlite3_errmsg() value to stderr and return 1.
@@ -8474,11 +8488,11 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
if( c=='d' && n>=3 && strncmp(azArg[0], "dbinfo", n)==0 ){
rc = shell_dbinfo_command(p, nArg, azArg);
}else
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
if( c=='r' && strncmp(azArg[0], "recover", n)==0 ){
open_db(p, 0);
rc = recoverDatabaseCmd(p, nArg, azArg);
@@ -9574,6 +9588,7 @@ static int do_meta_command(char *zLine, ShellState *p){
/* Check for command-line arguments */
for(iName=1; iName<nArg; iName++){
const char *z = azArg[iName];
#ifndef SQLITE_SHELL_WASM_MODE
if( optionMatch(z,"new") ){
newFlag = 1;
#ifdef SQLITE_HAVE_ZLIB
@@ -9594,7 +9609,9 @@ static int do_meta_command(char *zLine, ShellState *p){
}else if( optionMatch(z, "maxsize") && iName+1<nArg ){
p->szMax = integerValue(azArg[++iName]);
#endif /* SQLITE_OMIT_DESERIALIZE */
}else if( z[0]=='-' ){
}else
#endif /* !SQLITE_SHELL_WASM_MODE */
if( z[0]=='-' ){
utf8_printf(stderr, "unknown option: %s\n", z);
rc = 1;
goto meta_command_exit;
@@ -9621,6 +9638,7 @@ static int do_meta_command(char *zLine, ShellState *p){
/* If a filename is specified, try to open it first */
if( zFN || p->openMode==SHELL_OPEN_HEXDB ){
if( newFlag && zFN && !p->bSafeMode ) shellDeleteFile(zFN);
#ifndef SQLITE_SHELL_WASM_MODE
if( p->bSafeMode
&& p->openMode!=SHELL_OPEN_HEXDB
&& zFN
@@ -9628,6 +9646,9 @@ static int do_meta_command(char *zLine, ShellState *p){
){
failIfSafeMode(p, "cannot open disk-based database files in safe mode");
}
#else
/* WASM mode has its own sandboxed pseudo-filesystem. */
#endif
if( zFN ){
zNewFilename = sqlite3_mprintf("%s", zFN);
shell_check_oom(zNewFilename);
@@ -12598,12 +12619,46 @@ sqlite3 * fiddle_db_arg(sqlite3 *arg){
/*
** Intended to be called via a SharedWorker() while a separate
** SharedWorker() (which manages the wasm module) is performing work
** which should be interrupted.
** which should be interrupted. Unfortunately, SharedWorker is not
** portable enough to make real use of.
*/
void fiddle_interrupt(void){
if(globalDb) sqlite3_interrupt(globalDb);
}
/*
** Returns the filename of the given db name, assuming "main" if
** zDbName is NULL. Returns NULL if globalDb is not opened.
*/
const char * fiddle_db_filename(const char * zDbName){
return globalDb
? sqlite3_db_filename(globalDb, zDbName ? zDbName : "main")
: NULL;
}
/*
** Closes, unlinks, and reopens the db using its current filename (or
** the default if the db is currently closed). It is assumed, for
** purposes of the fiddle build, that the file is in a transient
** virtual filesystem within the browser.
*/
void fiddle_reset_db(void){
char *zFilename = 0;
if(0==globalDb){
shellState.pAuxDb->zDbFilename = "/fiddle.sqlite3";
}else{
zFilename =
sqlite3_mprintf("%s", sqlite3_db_filename(globalDb, "main"));
shell_check_oom(zFilename);
close_db(globalDb);
shellDeleteFile(zFilename);
shellState.db = 0;
shellState.pAuxDb->zDbFilename = zFilename;
}
open_db(&shellState, 0);
sqlite3_free(zFilename);
}
/*
** Trivial exportable function for emscripten. Needs to be exported using:
**
@@ -12630,9 +12685,11 @@ void fiddle_exec(const char * zSql){
);
puts("WASM shell");
puts("Enter \".help\" for usage hints.");
if(once>0) open_db(&shellState, 0);
if(once>0){
fiddle_reset_db();
}
if(shellState.db){
puts("Connected to a transient in-memory database.");
printf("Connected to %s.\n", fiddle_db_filename(NULL));
}else{
fprintf(stderr,"ERROR initializing db!\n");
return;
+3 -1
View File
@@ -6282,7 +6282,9 @@ sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
**
** ^The sqlite3_db_name(D,N) interface returns a pointer to the schema name
** for the N-th database on database connection D, or a NULL pointer of N is
** out of range.
** out of range. An N alue of 0 means the main database file. An N of 1 is
** the "temp" schema. Larger values of N correspond to various ATTACH-ed
** databases.
**
** Space to hold the string that is returned by sqlite3_db_name() is managed
** by SQLite itself. The string might be deallocated by any operation that
+3 -1
View File
@@ -1884,7 +1884,7 @@ struct FuncDestructor {
#define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a
** single query - might change over time */
#define SQLITE_FUNC_TEST 0x4000 /* Built-in testing functions */
#define SQLITE_FUNC_OFFSET 0x8000 /* Built-in sqlite_offset() function */
/* 0x8000 -- available for reuse */
#define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */
#define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */
#define SQLITE_FUNC_DIRECT 0x00080000 /* Not for use in TRIGGERs or VIEWs */
@@ -1901,6 +1901,7 @@ struct FuncDestructor {
#define INLINEFUNC_expr_compare 3
#define INLINEFUNC_affinity 4
#define INLINEFUNC_iif 5
#define INLINEFUNC_sqlite_offset 6
#define INLINEFUNC_unlikely 99 /* Default case */
/*
@@ -3100,6 +3101,7 @@ struct SrcItem {
unsigned isCte :1; /* This is a CTE */
unsigned notCte :1; /* This item may not match a CTE */
unsigned isUsing :1; /* u3.pUsing is valid */
unsigned isOn :1; /* u3.pOn was once valid and non-NULL */
unsigned isSynthUsing :1; /* u3.pUsing is synthensized from NATURAL */
unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */
} fg;
+1 -1
View File
@@ -2965,7 +2965,7 @@ deserialize_error:
}
if( objc==(6+isReadonly) ){
zDb = Tcl_GetString(objv[2]);
zDb = Tcl_GetString(objv[2+isReadonly]);
}
zTable = Tcl_GetString(objv[objc-3]);
zColumn = Tcl_GetString(objv[objc-2]);
+3
View File
@@ -215,6 +215,9 @@ void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){
if( pItem->fg.isCte ){
sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse);
}
if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){
sqlite3_str_appendf(&x, " ON");
}
sqlite3StrAccumFinish(&x);
sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1);
n = 0;
+9 -1
View File
@@ -514,7 +514,7 @@ TriggerStep *sqlite3TriggerInsertStep(
TriggerStep *sqlite3TriggerUpdateStep(
Parse *pParse, /* Parser */
Token *pTableName, /* Name of the table to be updated */
SrcList *pFrom,
SrcList *pFrom, /* FROM clause for an UPDATE-FROM, or NULL */
ExprList *pEList, /* The SET clause: list of column and new values */
Expr *pWhere, /* The WHERE clause */
u8 orconf, /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
@@ -850,6 +850,14 @@ SrcList *sqlite3TriggerStepSrc(
}
if( pStep->pFrom ){
SrcList *pDup = sqlite3SrcListDup(db, pStep->pFrom, 0);
if( pDup && pDup->nSrc>1 && !IN_RENAME_OBJECT ){
Select *pSubquery;
Token as;
pSubquery = sqlite3SelectNew(pParse,0,pDup,0,0,0,0,SF_NestedFrom,0);
as.n = 0;
as.z = 0;
pDup = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0);
}
pSrc = sqlite3SrcListAppendList(pParse, pSrc, pDup);
}
}else{
+25 -1
View File
@@ -1479,11 +1479,16 @@ case OP_Move: {
break;
}
/* Opcode: Copy P1 P2 P3 * *
/* Opcode: Copy P1 P2 P3 * P5
** Synopsis: r[P2@P3+1]=r[P1@P3+1]
**
** Make a copy of registers P1..P1+P3 into registers P2..P2+P3.
**
** If the 0x0002 bit of P5 is set then also clear the MEM_Subtype flag in the
** destination. The 0x0001 bit of P5 indicates that this Copy opcode cannot
** be merged. The 0x0001 bit is used by the query planner and does not
** come into play during query execution.
**
** This instruction makes a deep copy of the value. A duplicate
** is made of any string or blob constant. See also OP_SCopy.
*/
@@ -1498,6 +1503,9 @@ case OP_Copy: {
memAboutToChange(p, pOut);
sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem);
Deephemeralize(pOut);
if( (pOut->flags & MEM_Subtype)!=0 && (pOp->p5 & 0x0002)!=0 ){
pOut->flags &= ~MEM_Subtype;
}
#ifdef SQLITE_DEBUG
pOut->pScopyFrom = 0;
#endif
@@ -3880,6 +3888,11 @@ case OP_Transaction: {
}
p->expired = 1;
rc = SQLITE_SCHEMA;
/* Set changeCntOn to 0 to prevent the value returned by sqlite3_changes()
** from being modified in sqlite3VdbeHalt(). If this statement is
** reprepared, changeCntOn will be set again. */
p->changeCntOn = 0;
}
if( rc ) goto abort_due_to_error;
break;
@@ -8301,6 +8314,17 @@ case OP_Function: { /* group */
break;
}
/* Opcode: ClrSubtype P1 * * * *
** Synopsis: r[P1].subtype = 0
**
** Clear the subtype from register P1.
*/
case OP_ClrSubtype: { /* in1 */
pIn1 = &aMem[pOp->p1];
pIn1->flags &= ~MEM_Subtype;
break;
}
/* Opcode: FilterAdd P1 * P3 P4 *
** Synopsis: filter(P1) += key(P3@P4)
**
+2 -2
View File
@@ -1571,8 +1571,8 @@ static int valueFromExpr(
rc = valueFromExpr(db, pExpr->pLeft, enc, aff, ppVal, pCtx);
testcase( rc!=SQLITE_OK );
if( *ppVal ){
sqlite3VdbeMemCast(*ppVal, aff, SQLITE_UTF8);
sqlite3ValueApplyAffinity(*ppVal, affinity, SQLITE_UTF8);
sqlite3VdbeMemCast(*ppVal, aff, enc);
sqlite3ValueApplyAffinity(*ppVal, affinity, enc);
}
return rc;
}
+53 -23
View File
@@ -682,6 +682,7 @@ static void translateColumnToCopy(
pOp->p1 = pOp->p2 + iRegister;
pOp->p2 = pOp->p3;
pOp->p3 = 0;
pOp->p5 = 2; /* Cause the MEM_Subtype flag to be cleared */
}else if( pOp->opcode==OP_Rowid ){
pOp->opcode = OP_Sequence;
pOp->p1 = iAutoidxCur;
@@ -1177,13 +1178,20 @@ static sqlite3_index_info *allocateIndexInfo(
assert( pTerm->u.x.leftColumn<pTab->nCol );
/* tag-20191211-002: WHERE-clause constraints are not useful to the
** right-hand table of a LEFT JOIN nor to the left-hand table of a
** right-hand table of a LEFT JOIN nor to the either table of a
** RIGHT JOIN. See tag-20191211-001 for the
** equivalent restriction for ordinary tables. */
if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ))!=0
&& !ExprHasProperty(pTerm->pExpr, EP_OuterON)
){
continue;
if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ){
testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT );
testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_RIGHT );
testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ );
testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) );
testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) );
if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON)
|| pTerm->pExpr->w.iJoin != pSrc->iCursor
){
continue;
}
}
nTerm++;
pTerm->wtFlags |= TERM_OK;
@@ -2833,12 +2841,25 @@ static int whereLoopAddBtreeIndex(
/* tag-20191211-001: Do not allow constraints from the WHERE clause to
** be used by the right table of a LEFT JOIN nor by the left table of a
** RIGHT JOIN. Only constraints in the
** ON clause are allowed. See tag-20191211-002 for the vtab equivalent. */
if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ))!=0
&& !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON)
){
continue;
** RIGHT JOIN. Only constraints in the ON clause are allowed.
** See tag-20191211-002 for the vtab equivalent.
**
** 2022-06-06: See https://sqlite.org/forum/forumpost/206d99a16dd9212f
** for an example of a WHERE clause constraints that may not be used on
** the right table of a RIGHT JOIN because the constraint implies a
** not-NULL condition on the left table of the RIGHT JOIN.
*/
if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ){
testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT );
testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_RIGHT );
testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ );
testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) )
testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) );
if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON)
|| pTerm->pExpr->w.iJoin != pSrc->iCursor
){
continue;
}
}
if( IsUniqueIndex(pProbe) && saved_nEq==pProbe->nKeyCol-1 ){
@@ -3190,15 +3211,18 @@ static int indexMightHelpWithOrderBy(
*/
static int whereUsablePartialIndex(
int iTab, /* The table for which we want an index */
int isLeft, /* True if iTab is the right table of a LEFT JOIN */
u8 jointype, /* The JT_* flags on the join */
WhereClause *pWC, /* The WHERE clause of the query */
Expr *pWhere /* The WHERE clause from the partial index */
){
int i;
WhereTerm *pTerm;
Parse *pParse = pWC->pWInfo->pParse;
Parse *pParse;
if( jointype & JT_LTORJ ) return 0;
pParse = pWC->pWInfo->pParse;
while( pWhere->op==TK_AND ){
if( !whereUsablePartialIndex(iTab,isLeft,pWC,pWhere->pLeft) ) return 0;
if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0;
pWhere = pWhere->pRight;
}
if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0;
@@ -3206,7 +3230,7 @@ static int whereUsablePartialIndex(
Expr *pExpr;
pExpr = pTerm->pExpr;
if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab)
&& (isLeft==0 || ExprHasProperty(pExpr, EP_OuterON))
&& ((jointype & JT_OUTER)==0 || ExprHasProperty(pExpr, EP_OuterON))
&& sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab)
&& (pTerm->wtFlags & TERM_VNULL)==0
){
@@ -3372,9 +3396,8 @@ static int whereLoopAddBtree(
for(; rc==SQLITE_OK && pProbe;
pProbe=(pSrc->fg.isIndexedBy ? 0 : pProbe->pNext), iSortIdx++
){
int isLeft = (pSrc->fg.jointype & JT_OUTER)!=0;
if( pProbe->pPartIdxWhere!=0
&& !whereUsablePartialIndex(pSrc->iCursor, isLeft, pWC,
&& !whereUsablePartialIndex(pSrc->iCursor, pSrc->fg.jointype, pWC,
pProbe->pPartIdxWhere)
){
testcase( pNew->iTab!=pSrc->iCursor ); /* See ticket [98d973b8f5] */
@@ -3664,6 +3687,7 @@ static int whereLoopAddVirtualOne(
*pbIn = 1; assert( (mExclude & WO_IN)==0 );
}
assert( pbRetryLimit || !isLimitTerm(pTerm) );
if( isLimitTerm(pTerm) && *pbIn ){
/* If there is an IN(...) term handled as an == (separate call to
** xFilter for each value on the RHS of the IN) and a LIMIT or
@@ -4137,8 +4161,10 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
SrcItem *pEnd = &pTabList->a[pWInfo->nLevel];
sqlite3 *db = pWInfo->pParse->db;
int rc = SQLITE_OK;
int bFirstPastRJ = 0;
WhereLoop *pNew;
/* Loop over the tables in the join, from left to right */
pNew = pBuilder->pNew;
whereLoopInit(pNew);
@@ -4148,10 +4174,13 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
pNew->iTab = iTab;
pBuilder->iPlanLimit += SQLITE_QUERY_PLANNER_LIMIT_INCR;
pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor);
if( (pItem->fg.jointype & (JT_OUTER|JT_CROSS))!=0 ){
/* This condition is true when pItem is the FROM clause term on the
** right-hand-side of a OUTER or CROSS JOIN. */
if( bFirstPastRJ || (pItem->fg.jointype & (JT_OUTER|JT_CROSS))!=0 ){
/* Add prerequisites to prevent reordering of FROM clause terms
** across CROSS joins and outer joins. The bFirstPastRJ boolean
** prevents the right operand of a RIGHT JOIN from being swapped with
** other elements even further to the right. */
mPrereq |= mPrior;
bFirstPastRJ = (pItem->fg.jointype & JT_RIGHT)!=0;
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pItem->pTab) ){
@@ -5932,7 +5961,7 @@ WhereInfo *sqlite3WhereBegin(
if( pSrc->fg.isCorrelated ){
sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub);
}else{
int iOnce = sqlite3VdbeAddOp0(v, OP_Once);
int iOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub);
sqlite3VdbeJumpHere(v, iOnce);
}
@@ -6028,6 +6057,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
SrcList *pTabList = pWInfo->pTabList;
sqlite3 *db = pParse->db;
int iEnd = sqlite3VdbeCurrentAddr(v);
int nRJ = 0;
/* Generate loop termination code.
*/
@@ -6044,8 +6074,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
pRJ->endSubrtn = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp3(v, OP_Return, pRJ->regReturn, pRJ->addrSubrtn, 1);
VdbeCoverage(v);
assert( pParse->withinRJSubrtn>0 );
pParse->withinRJSubrtn--;
nRJ++;
}
pLoop = pLevel->pWLoop;
if( pLevel->op!=OP_Noop ){
@@ -6326,5 +6355,6 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
*/
pParse->nQueryLoop = pWInfo->savedNQueryLoop;
whereInfoFree(db, pWInfo);
pParse->withinRJSubrtn -= nRJ;
return;
}
+2 -1
View File
@@ -612,8 +612,9 @@ void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*);
#define WO_AND 0x0400 /* Two or more AND-connected terms */
#define WO_EQUIV 0x0800 /* Of the form A==B, both columns */
#define WO_NOOP 0x1000 /* This term does not restrict search space */
#define WO_ROWVAL 0x2000 /* A row-value term */
#define WO_ALL 0x1fff /* Mask of all possible WO_* values */
#define WO_ALL 0x3fff /* Mask of all possible WO_* values */
#define WO_SINGLE 0x01ff /* Mask of all non-compound WO_* values */
/*
+8 -1
View File
@@ -2624,6 +2624,9 @@ Bitmask sqlite3WhereCodeOneLoopStart(
/* Defer processing WHERE clause constraints until after outer
** join processing. tag-20220513a */
continue;
}else if( (pTabItem->fg.jointype & JT_LEFT)==JT_LEFT
&& !ExprHasProperty(pE,EP_OuterON) ){
continue;
}else{
Bitmask m = sqlite3WhereGetMask(&pWInfo->sMaskSet, pE->w.iJoin);
if( m & pLevel->notReady ){
@@ -2859,7 +2862,11 @@ SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
mAll |= pLoop->maskSelf;
for(k=0; k<pWC->nTerm; k++){
WhereTerm *pTerm = &pWC->a[k];
if( pTerm->wtFlags & TERM_VIRTUAL ) break;
if( (pTerm->wtFlags & (TERM_VIRTUAL|TERM_SLICE))!=0
&& pTerm->eOperator!=WO_ROWVAL
){
break;
}
if( pTerm->prereqAll & ~mAll ) continue;
if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue;
pSubWhere = sqlite3ExprAnd(pParse, pSubWhere,
+18 -10
View File
@@ -1105,18 +1105,26 @@ static void exprAnalyze(
if( prereqAll!=sqlite3WhereExprUsageNN(pMaskSet, pExpr) ){
printf("\n*** Incorrect prereqAll computed for:\n");
sqlite3TreeViewExpr(0,pExpr,0);
abort();
assert( 0 );
}
#endif
if( ExprHasProperty(pExpr, EP_OuterON) ){
if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) ){
Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->w.iJoin);
prereqAll |= x;
extraRight = x-1; /* ON clause terms may not be used with an index
** on left table of a LEFT JOIN. Ticket #3015 */
if( (prereqAll>>1)>=x ){
sqlite3ErrorMsg(pParse, "ON clause references tables to its right");
return;
if( ExprHasProperty(pExpr, EP_OuterON) ){
prereqAll |= x;
extraRight = x-1; /* ON clause terms may not be used with an index
** on left table of a LEFT JOIN. Ticket #3015 */
if( (prereqAll>>1)>=x ){
sqlite3ErrorMsg(pParse, "ON clause references tables to its right");
return;
}
}else if( (prereqAll>>1)>=x ){
/* The ON clause of an INNER JOIN references a table to its right.
** Most other SQL database engines raise an error. But all versions
** of SQLite going back to 3.0.0 have just put the ON clause constraint
** into the WHERE clause and carried on. */
ExprClearProperty(pExpr, EP_InnerON);
}
}
pTerm->prereqAll = prereqAll;
@@ -1403,7 +1411,7 @@ static void exprAnalyze(
}
pTerm = &pWC->a[idxTerm];
pTerm->wtFlags |= TERM_CODED|TERM_VIRTUAL; /* Disable the original */
pTerm->eOperator = 0;
pTerm->eOperator = WO_ROWVAL;
}
/* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create
@@ -1604,7 +1612,7 @@ void sqlite3WhereAddLimit(WhereClause *pWC, Select *p){
/* This term is a vector operation that has been decomposed into
** other, subsequent terms. It can be ignored. See tag-20220128a */
assert( pWC->a[ii].wtFlags & TERM_VIRTUAL );
assert( pWC->a[ii].eOperator==0 );
assert( pWC->a[ii].eOperator==WO_ROWVAL );
continue;
}
if( pWC->a[ii].leftCursor!=iCsr ) return;
+7
View File
@@ -21,6 +21,7 @@ ifcapable !altertable {
return
}
set ::TMPDBERROR [list 1 \
{unable to open a temporary database file for storing temporary tables}
]
@@ -47,6 +48,7 @@ do_faultsim_test 1 -prep {
faultsim_test_result {0 {}} $::TMPDBERROR
}
#-------------------------------------------------------------------------
# dbsqlfuzz e3dd84cda3848016a6a6024c7249d09bc2ef2615
#
@@ -59,6 +61,11 @@ do_execsql_test 2.0 {
SELECT a FROM cte1
), 1);
END;
CREATE TRIGGER r1 AFTER INSERT ON t2 BEGIN
UPDATE t2 SET k=1 FROM t2 AS one, t2 AS two NATURAL JOIN t2 AS three
WHERE one.k=two.v;
END;
}
faultsim_save_and_close
+1 -1
View File
@@ -645,7 +645,7 @@ do_execsql_test 26.5 {
} {}
do_catchsql_test 26.6 {
ALTER TABLE t1 RENAME TO t2;
} {1 {error in trigger xx: ambiguous column name: xx}}
} {1 {error in trigger xx: no such column: xx}}
#-------------------------------------------------------------------------
+163
View File
@@ -0,0 +1,163 @@
# 2022 May 27
#
# 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.
#
#*************************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix altertrig
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
proc collapse_whitespace {in} {
regsub -all {[ \t\n]+} [string trim $in] { }
}
proc do_whitespace_sql_test {tn sql res} {
set got [execsql $sql]
set wgot [list]
set wres [list]
foreach g $got { lappend wgot [collapse_whitespace $g] }
foreach r $res { lappend wres [collapse_whitespace $r] }
uplevel [list do_test $tn [list set {} $wgot] $wres]
}
do_execsql_test 1.0 {
CREATE TABLE t1(x);
CREATE TABLE t2(y);
CREATE TABLE t3(z);
CREATE TABLE t4(a);
CREATE TRIGGER r1 INSERT ON t1 BEGIN
UPDATE t1 SET d='xyz' FROM t2, t3;
END;
}
do_whitespace_sql_test 1.1 {
ALTER TABLE t3 RENAME TO t5;
SELECT sql FROM sqlite_schema WHERE type='trigger';
} {{
CREATE TRIGGER r1 INSERT ON t1 BEGIN
UPDATE t1 SET d='xyz' FROM t2, "t5";
END
}}
do_execsql_test 1.2 {
DROP TRIGGER r1;
CREATE TRIGGER r1 INSERT ON t1 BEGIN
UPDATE t1 SET d='xyz' FROM t2, (SELECT * FROM t5);
END;
}
do_whitespace_sql_test 1.3 {
ALTER TABLE t5 RENAME TO t3;
SELECT sql FROM sqlite_schema WHERE type='trigger';
} {{
CREATE TRIGGER r1 INSERT ON t1 BEGIN
UPDATE t1 SET d='xyz' FROM t2, (SELECT * FROM "t3");
END
}}
foreach {tn alter update final} {
1 {
ALTER TABLE t3 RENAME TO t10
} {
UPDATE t1 SET d='xyz' FROM t2, (SELECT * FROM t3)
} {
UPDATE t1 SET d='xyz' FROM t2, (SELECT * FROM "t10")
}
2 {
ALTER TABLE t3 RENAME TO t10
} {
UPDATE t1 SET a='xyz' FROM t3, (SELECT * FROM (SELECT e FROM t3))
} {
UPDATE t1 SET a='xyz' FROM "t10", (SELECT * FROM (SELECT e FROM "t10"))
}
3 {
ALTER TABLE t3 RENAME e TO abc
} {
UPDATE t1 SET a='xyz' FROM t3, (SELECT * FROM (SELECT e FROM t3))
} {
UPDATE t1 SET a='xyz' FROM t3, (SELECT * FROM (SELECT abc FROM t3))
}
4 {
ALTER TABLE t2 RENAME c TO abc
} {
UPDATE t1 SET a='xyz' FROM t3, (SELECT 1 FROM t2 WHERE c)
} {
UPDATE t1 SET a='xyz' FROM t3, (SELECT 1 FROM t2 WHERE abc)
}
5 {
ALTER TABLE t2 RENAME c TO abc
} {
UPDATE t1 SET a=t2.c FROM t2
} {
UPDATE t1 SET a=t2.abc FROM t2
}
6 {
ALTER TABLE t2 RENAME c TO abc
} {
UPDATE t1 SET a=t2.c FROM t2, t3
} {
UPDATE t1 SET a=t2.abc FROM t2, t3
}
7 {
ALTER TABLE t4 RENAME e TO abc
} {
UPDATE t1 SET a=1 FROM t3 NATURAL JOIN t4 WHERE t4.e=a
} {
UPDATE t1 SET a=1 FROM t3 NATURAL JOIN t4 WHERE t4.abc=a
}
8 {
ALTER TABLE t4 RENAME TO abc
} {
UPDATE t1 SET a=1 FROM t3 NATURAL JOIN t4 WHERE t4.e=a
} {
UPDATE t1 SET a=1 FROM t3 NATURAL JOIN "abc" WHERE "abc".e=a
}
} {
reset_db
do_execsql_test 2.$tn.1 {
CREATE TABLE t1(a,b);
CREATE TABLE t2(c,d);
CREATE TABLE t3(e,f);
CREATE TABLE t4(e,f);
}
do_execsql_test 2.$tn.2 "
CREATE TRIGGER r1 INSERT ON t1 BEGIN
$update;
END
"
do_execsql_test 2.$tn.3 $alter
do_whitespace_sql_test 2.$tn.4 {
SELECT sqL FROM sqlite_schema WHERE type='trigger'
} "{
CREATE TRIGGER r1 INSERT ON t1 BEGIN
$final;
END
}"
}
finish_test
+95
View File
@@ -0,0 +1,95 @@
# 2022 June 6
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix changes2
do_execsql_test 1.0 {
CREATE TABLE some_table (
id INTEGER NOT NULL, value VARCHAR(40) NOT NULL, PRIMARY KEY (id)
);
INSERT INTO some_table (id, value) VALUES (1, 'v1');
} {}
set ::stmt [sqlite3_prepare_v2 db {
UPDATE some_table SET value='v2' WHERE id=1 RETURNING id
} -1 dummy]
do_test 1.1 {
list [sqlite3_step $::stmt] [db changes]
} {SQLITE_ROW 1}
do_test 1.2 {
list [sqlite3_step $::stmt] [db changes]
} {SQLITE_DONE 1}
sqlite3_reset $::stmt
do_execsql_test 1.2 {
DROP TABLE some_table;
CREATE TABLE some_table (
id INTEGER NOT NULL, value VARCHAR(40) NOT NULL, PRIMARY KEY (id)
);
INSERT INTO some_table (id, value) VALUES (1, 'v1');
} {}
do_test 1.3 {
list [sqlite3_step $::stmt] [db changes]
} {SQLITE_ROW 1}
do_test 1.4 {
list [sqlite3_step $::stmt] [db changes]
} {SQLITE_DONE 1}
sqlite3_finalize $::stmt
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
CREATE TABLE t1(a, b);
CREATE TABLE log(t);
} {}
set ::stmt [sqlite3_prepare_v2 db {
INSERT INTO log VALUES(changes() || ' changes')
} -1 dummy]
do_execsql_test 2.1 {
INSERT INTO t1 VALUES (1, 'v1'), (2, 'v2');
} {}
do_test 2.2 {
list [sqlite3_step $::stmt] [sqlite3_reset $::stmt]
} {SQLITE_DONE SQLITE_OK}
do_execsql_test 2.3 {
CREATE TABLE t3(x);
}
do_execsql_test 2.2 {
INSERT INTO t1 VALUES (3, 'v1'), (4, 'v2');
} {}
do_test 2.3 {
list [sqlite3_step $::stmt] [sqlite3_reset $::stmt]
} {SQLITE_DONE SQLITE_OK}
sqlite3_finalize $::stmt
do_execsql_test 2.4 {
SELECT * FROM log;
} {{2 changes} {2 changes}}
finish_test
+38
View File
@@ -193,4 +193,42 @@ do_catchsql_test 7.10 {
SELECT matchinfo( f , 'pcx') FROM f WHERE b MATCH x'c533';
} {1 {database disk image is malformed}}
reset_db
sqlite3_fts3_may_be_corrupt 1
do_execsql_test 8.1 {
CREATE VIRTUAL TABLE f USING fts3(a);
INSERT INTO f(f) VALUES('nodesize=24');
BEGIN;
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz0123456789');
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz0123456789');
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz0123456789');
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz012345678X');
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz012345678X');
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz012345678X');
COMMIT;
BEGIN;
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz0123456789');
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz0123456789');
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz0123456789');
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz012345678X');
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz012345678X');
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz012345678X');
COMMIT;
SELECT count(*) FROM f_segments;
} {4}
do_execsql_test 8.2 {
UPDATE f_segments SET block = (
SELECT block FROM f_segments WHERE blockid=1
) WHERE blockid=2
}
do_catchsql_test 8.3 {
INSERT INTO f(f) VALUES('merge=2,2');
} {1 {database disk image is malformed}}
sqlite3_fts3_may_be_corrupt 0
finish_test
+3 -2
View File
@@ -64,7 +64,8 @@ do_execsql_test 2.1 {
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
breakpoint
do_catchsql_test 3.0 {
CREATE VIRTUAL TABLE main.Table0 USING fts3();
INSERT INTO Table0 VALUES (1), (printf('%8.1280000X') ), (1), (printf('%8.1280000X') ), (1) ;
INSERT INTO Table0 VALUES (0), (printf('%8.1280000X%8.1280000X') ), (1), (printf('%1280000.1280000X%#1280000.1280000E%8.1280000X') ), (1) ;
@@ -72,7 +73,7 @@ do_execsql_test 3.0 {
UPDATE Table0_segdir SET start_block = 1;
INSERT INTO Table0 VALUES (1) ;
INSERT INTO Table0(Table0) VALUES('merge=6,8');
}
} {1 {database disk image is malformed}}
set sqlite_fts3_enable_parentheses $saved_sqlite_fts3_enable_parentheses
finish_test
+6
View File
@@ -60,6 +60,12 @@ do_execsql_test func7-pg-170 {
do_execsql_test func7-pg-180 {
SELECT log10(1000.0)
} {3.0}
do_execsql_test func7-pg-181 {
SELECT format('%.30f', log10(100.0) );
} {2.000000000000000000000000000000}
do_execsql_test func7-pg-182 {
SELECT format('%.30f', ln(exp(2.0)) );
} {2.000000000000000000000000000000}
do_execsql_test func7-pg-190 {
SELECT log(2.0, 64.0)
} {6.0}
+38
View File
@@ -13,6 +13,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix incrblob3
ifcapable !incrblob {
finish_test
@@ -273,4 +274,41 @@ do_test incrblob3-7.2 {
db close
tvfs delete
#-------------------------------------------------------------------------
#
reset_db
forcedelete test.db2
do_execsql_test 8.1 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
ATTACH 'test.db2' AS aux;
CREATE TABLE aux.t1(a INTEGER PRIMARY KEY, b);
INSERT INTO t1 VALUES(4, 'hello');
INSERT INTO aux.t1 VALUES(4, 'world');
}
do_test 8.2 {
set ::blob [db incrblob -readonly main t1 b 4]
read $::blob
} {hello}
close $::blob
do_test 8.3 {
set ::blob [db incrblob -readonly aux t1 b 4]
read $::blob
} {world}
close $::blob
do_test 8.4 {
set ::blob [db incrblob -readonly t1 b 4]
read $::blob
} {hello}
close $::blob
do_test 8.5 {
list [catch { db incrblob -readonly nosuchdb t1 b 4 } msg] $msg
} {1 {no such table: nosuchdb.t1}}
db close
finish_test
+20
View File
@@ -508,4 +508,24 @@ do_execsql_test index6-18.1 {
SELECT * FROM t1 WHERE a IS NOT NULL;
} {10 10}
# 2022-06-09
# https://sqlite.org/forum/forumpost/c4676c4956
# Cannot do a scan of a partial index on the left table of a RIGHT JOIN
# since that will cause extra rows to appear in the output during the
# right-join no-match loop. The following testcase is verify using
# PostgreSQL 14.
#
reset_db
do_execsql_test index6-19.1 {
CREATE TABLE t1(a INT, b INT);
INSERT INTO t1(a) VALUES(2);
CREATE TABLE t2(c INT);
CREATE INDEX i0 ON t2(c) WHERE c=3;
CREATE TABLE t3(d INT);
INSERT INTO t3 VALUES(1);
}
do_execsql_test index6-19.2 {
SELECT * FROM t2 RIGHT JOIN t3 ON d<>0 LEFT JOIN t1 ON c=3 WHERE t1.a<>0;
} {}
finish_test
+1
View File
@@ -34,6 +34,7 @@ do_execsql_test 1.0 {
CREATE TABLE t1(x, y);
CREATE INDEX t1x ON t1(x) WHERE y=45;
}
unset -nocomplain a
set y [expr 45]
do_sqluses_test 1.1 { SELECT * FROM t1 WHERE x=? AND y=$y } {t1 t1x}
set y [expr 45.1]
+34
View File
@@ -1067,4 +1067,38 @@ do_catchsql_test join-26.1 {
SELECT * FROM t5 JOIN ((t4 JOIN (t5 JOIN t6)) t7);
} {/1 {.*}/}
# 2022-06-09 Invalid subquery flattening caused by
# check-in 3f45007d544e5f78 and detected by dbsqlfuzz
#
reset_db
do_execsql_test join-27.1 {
CREATE TABLE t1(a INT,b INT,c INT); INSERT INTO t1 VALUES(NULL,NULL,NULL);
CREATE TABLE t2(d INT,e INT); INSERT INTO t2 VALUES(NULL,NULL);
CREATE INDEX x2 ON t1(c,b);
CREATE TABLE t3(x INT); INSERT INTO t3 VALUES(NULL);
}
do_execsql_test join-27.2 {
WITH t99(b) AS MATERIALIZED (
SELECT b FROM t2 LEFT JOIN t1 ON c IN (SELECT x FROM t3)
)
SELECT 5 FROM t2 JOIN t99 ON b IN (1,2,3);
} {}
do_execsql_test join-27.3 {
WITH t99(b) AS NOT MATERIALIZED (
SELECT b FROM t2 LEFT JOIN t1 ON c IN (SELECT x FROM t3)
)
SELECT 5 FROM t2 JOIN t99 ON b IN (1,2,3);
} {}
do_execsql_test join-27.4 {
WITH t99(b) AS (SELECT b FROM t2 LEFT JOIN t1 ON c IN (SELECT x FROM t3))
SELECT 5 FROM t2 JOIN t99 ON b IN (1,2,3);
} {}
do_execsql_test join-27.5 {
SELECT 5
FROM t2 JOIN (
SELECT b FROM t2 LEFT JOIN t1 ON c IN (SELECT x FROM t3)
) AS t99 ON b IN (1,2,3);
} {}
finish_test
+66
View File
@@ -139,6 +139,72 @@ foreach {id schema} {
1 3 3 33
1 4 4 44
}
do_execsql_test join7-$id.32 {
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c
WHERE b=c
ORDER BY +b;
} {
1 3 3 33
1 4 4 44
}
do_execsql_test join7-$id.33 {
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c
WHERE b>0
ORDER BY +b;
} {
1 2 NULL NULL
1 3 3 33
1 4 4 44
}
do_execsql_test join7-$id.34 {
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c
WHERE b>0 OR b IS NULL
ORDER BY +b;
} {
NULL NULL 5 55
1 2 NULL NULL
1 3 3 33
1 4 4 44
}
do_execsql_test join7-$id.35 {
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c AND b>3 AND c>4
ORDER BY coalesce(b,c,0);
} {
1 2 NULL NULL
NULL NULL 3 33
1 3 NULL NULL
NULL NULL 4 44
1 4 NULL NULL
NULL NULL 5 55
}
do_execsql_test join7-$id.36 {
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c AND b>3 WHERE c>4
ORDER BY coalesce(b,c,0);
} {
NULL NULL 5 55
}
do_execsql_test join7-$id.37 {
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c WHERE b>3 AND c>4
ORDER BY coalesce(b,c,0);
} {
}
do_execsql_test join7-$id.38 {
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c WHERE b>3 OR c>4
ORDER BY coalesce(b,c,0);
} {
1 4 4 44
NULL NULL 5 55
}
do_execsql_test join7-$id.39 {
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c AND (b>3 OR c>4)
ORDER BY coalesce(b,c,0);
} {
1 2 NULL NULL
NULL NULL 3 33
1 3 NULL NULL
1 4 4 44
NULL NULL 5 55
}
do_execsql_test join7-$id.40 {
SELECT * FROM t1 RIGHT OUTER JOIN t2 ON b=c ORDER BY +b;
} {
+282
View File
@@ -375,4 +375,286 @@ do_execsql_test join8-11040 {
SELECT b, d FROM t1 RIGHT JOIN t2 WHERE (b+0)=1 AND d!=4;
} {1 not-4}
# 2022-05-23
# NATURAL JOIN name resolution is more forgiving with LEFT JOIN
# https://sqlite.org/forum/forumpost/e90a8e6e6f
#
reset_db
db null -
do_execsql_test join8-12000 {
CREATE TABLE t1(a INT); INSERT INTO t1 VALUES(0),(1);
CREATE TABLE t2(a INT); INSERT INTO t2 VALUES(0),(2);
CREATE TABLE t3(a INT); INSERT INTO t3 VALUES(0),(3);
} {}
do_catchsql_test join8-12010 {
SELECT * FROM t1 RIGHT JOIN t2 ON t2.a<>0 NATURAL RIGHT JOIN t3;
} {1 {ambiguous reference to a in USING()}}
do_catchsql_test join8-12020 {
SELECT * FROM t1 RIGHT JOIN t2 ON t2.a<>0 NATURAL LEFT JOIN t3;
} {1 {ambiguous reference to a in USING()}}
do_catchsql_test join8-12030 {
SELECT * FROM t1 LEFT JOIN t2 ON t2.a<>0 NATURAL RIGHT JOIN t3;
} {1 {ambiguous reference to a in USING()}}
# The following query should probably also return the same error as the
# previous three cases. However, historical versions of SQLite have always
# let it pass. We will not "fix" this, since to do so might break legacy
# applications.
#
do_catchsql_test join8-12040 {
SELECT * FROM t1 LEFT JOIN t2 ON t2.a<>0 NATURAL LEFT JOIN t3;
} {0 {0 2 1 2}}
# 2022-05-24
# https://sqlite.org/forum/forumpost/687b0bf563a1d4f1
#
reset_db
do_execsql_test join8-13000 {
CREATE TABLE t0(t TEXT, u TEXT); INSERT INTO t0 VALUES('t', 'u');
CREATE TABLE t1(v TEXT, w TEXT); INSERT INTO t1 VALUES('v', 'w');
CREATE TABLE t2(x TEXT, y TEXT); INSERT INTO t2 VALUES('x', 'y');
SELECT * FROM t0 JOIN t1 ON (t2.x NOTNULL) LEFT JOIN t2 ON false;
SELECT * FROM t0 JOIN t1 ON (t2.x NOTNULL) LEFT JOIN t2 ON false
WHERE t2.y ISNULL;
} {}
# 2022-05-25
# https://sqlite.org/forum/forumpost/5cfe08eed6
#
reset_db
do_execsql_test join8-14000 {
CREATE TABLE t0(a TEXT, b TEXT, c TEXT);
CREATE TABLE t1(a TEXT);
INSERT INTO t1 VALUES('1');
CREATE VIEW v0 AS SELECT 'xyz' AS d;
SELECT * FROM v0 RIGHT JOIN t1 ON t1.a<>'' INNER JOIN t0 ON t0.c<>'';
SELECT * FROM v0 RIGHT JOIN t1 ON t1.a<>'' INNER JOIN t0 ON t0.c<>'' WHERE b ISNULL;
} {}
do_execsql_test join8-14010 {
CREATE TABLE y0(a INT);
CREATE TABLE y1(b INT); INSERT INTO y1 VALUES(1), (2);
CREATE TABLE y2(c INT); INSERT INTO y2 VALUES(3), (4);
} {}
db null -
do_execsql_test join8-14020 {
SELECT * FROM y0 RIGHT JOIN y1 ON true INNER JOIN y2 ON true WHERE y2.c!=99 AND y2.c!=98;
} {
- 1 3
- 1 4
- 2 3
- 2 4
}
# 2022-05-30
# https://sqlite.org/forum/forumpost/3902c7b833
#
reset_db
do_execsql_test join8-15000 {
CREATE TABLE t1(x INT);
CREATE TABLE t2(y INT);
CREATE TABLE t3(z INT);
INSERT INTO t1 VALUES(10);
INSERT INTO t3 VALUES(20),(30);
}
do_execsql_test join8-15010 {
SELECT * FROM t1 LEFT JOIN t2 ON true JOIN t3 ON t2.y IS NOT NULL;
} {}
do_execsql_test join8-15020 {
SELECT * FROM t1 LEFT JOIN t2 ON true JOIN t3 ON t2.y IS NOT NULL
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600);
} {}
do_execsql_test join8-15100 {
PRAGMA automatic_index = 0;
CREATE TABLE t4(x TEXT);
CREATE TABLE t5(y TEXT);
CREATE TABLE t6(z TEXT);
INSERT INTO t4 VALUES('a'), ('b');
INSERT INTO t5 VALUES('b'), ('c');
INSERT INTO t6 VALUES('a'), ('d');
} {}
db null -
do_execsql_test join8-15110 {
SELECT * FROM t4 LEFT JOIN t5 ON x=y LEFT JOIN t6 ON (x=z) ORDER BY +x;
} {a - a b b -}
do_execsql_test join8-15120 {
SELECT * FROM t4 LEFT JOIN t5 ON x=y LEFT JOIN t6 ON (x=z)
WHERE t5.y!='x' AND t4.x!='x';
} {b b -}
# 2022-05-31
# https://sqlite.org/forum/forumpost/c2554d560b
reset_db
do_execsql_test join8-16000 {
CREATE TABLE t1(a TEXT);
CREATE TABLE t2(b TEXT);
CREATE TABLE t3(c TEXT);
INSERT INTO t2(b) VALUES ('x');
INSERT INTO t3(c) VALUES ('y'), ('z');
} {}
db null -
do_execsql_test join8-16010 {
SELECT * FROM t1 RIGHT JOIN t2 ON true LEFT JOIN t3 ON a<>'';
} {- x -}
do_execsql_test join8-16020 {
SELECT * FROM t1 RIGHT JOIN t2 ON true LEFT JOIN t3 ON a<>'' WHERE c IS NULL;
} {- x -}
do_execsql_test join8-16020 {
SELECT * FROM t1 RIGHT JOIN t2 ON true JOIN t3 ON a<>'' WHERE c IS NULL;
} {}
do_execsql_test join8-16030 {
SELECT * FROM t1 RIGHT JOIN t2 ON true JOIN t3 ON a<>'';
} {}
do_execsql_test join8-16040 {
SELECT * FROM t1 RIGHT JOIN t2 ON true LEFT JOIN t3 ON a<>'' WHERE c<>'';
} {}
do_execsql_test join8-16050 {
SELECT * FROM t1 RIGHT JOIN t2 ON true LEFT JOIN t3 ON a<>'' WHERE c IS NOT NULL;
} {}
do_execsql_test join8-16060 {
SELECT * FROM t1 RIGHT JOIN t2 ON true JOIN t3 ON a<>'' WHERE c<>'';
} {}
do_execsql_test join8-16070 {
SELECT * FROM t1 RIGHT JOIN t2 ON true JOIN t3 ON a<>'' WHERE c IS NOT NULL;
} {}
# 2022-06-01
# https://sqlite.org/forum/forumpost/087de2d9ec
#
reset_db
do_execsql_test join8-17000 {
CREATE TABLE t1(id INTEGER PRIMARY KEY, x INT, y INT);
CREATE TABLE t2(z INT);
INSERT INTO t1(id,x,y) VALUES(1, 0, 0);
} {}
db null NULL
do_execsql_test join8-17010 {
SELECT * FROM t2 RIGHT JOIN t1 ON true;
} {NULL 1 0 0}
do_execsql_test join8-17020 {
SELECT 99=id AND 0=y AS "truth" FROM t2 RIGHT JOIN t1 ON true;
} {0}
do_execsql_test join8-17030 {
SELECT (99, 0)==(id, y) AS "truth" FROM t2 RIGHT JOIN t1;
} {0}
do_execsql_test join8-17040 {
SELECT * FROM t2 RIGHT JOIN t1 WHERE 99=id AND 0=y;
} {}
do_execsql_test join8-17041 {
SELECT * FROM t2 RIGHT JOIN t1 WHERE 99=+id AND 0=y;
} {}
do_execsql_test join8-17050 {
SELECT * FROM t2 RIGHT JOIN t1 WHERE (99, 0)==(id,y);
} {}
do_execsql_test join8-17051 {
SELECT * FROM t2 RIGHT JOIN t1 WHERE (99, 0)==(+id,y);
} {}
do_execsql_test join8-17060 {
SELECT * FROM t2 RIGHT JOIN t1 WHERE 1=id AND 0=y;
} {NULL 1 0 0}
do_execsql_test join8-17061 {
SELECT * FROM t2 RIGHT JOIN t1 WHERE 1=+id AND 0=y;
} {NULL 1 0 0}
do_execsql_test join8-17070 {
SELECT * FROM t2 RIGHT JOIN t1 WHERE (1, 0)==(id,y);
} {NULL 1 0 0}
do_execsql_test join8-17071 {
SELECT * FROM t2 RIGHT JOIN t1 WHERE (1, 0)==(+id,y);
} {NULL 1 0 0}
do_execsql_test join8-17080 {
CREATE TABLE t3(a INTEGER PRIMARY KEY, b INT);
CREATE TABLE t4(x INT, y INT);
INSERT INTO t3(a,b) VALUES(1, 3);
} {}
do_execsql_test join8-17090 {
SELECT t3.a FROM t4 RIGHT JOIN t3 ON (x=a) WHERE (b, 4)=(SELECT 3, 4);
} {1}
do_execsql_test join8-17091 {
SELECT t3.a FROM t4 RIGHT JOIN t3 ON (x=a) WHERE (b, 4) IS (SELECT 3, 4);
} {1}
# 2022-06-06
# https://sqlite.org/forum/forumpost/206d99a16dd9212f
# tag-20191211-001
#
reset_db
do_execsql_test join8-18000 {
CREATE TABLE t1(a BOOLEAN); INSERT INTO t1 VALUES (false);
CREATE TABLE t2(x INT); INSERT INTO t2 VALUES (0);
SELECT *, x NOTNULL, (x NOTNULL)=a FROM t2 RIGHT JOIN t1 ON true WHERE (x NOTNULL)=a;
} {}
do_execsql_test join8-18010 {
CREATE INDEX t1a ON t1(a);
SELECT *, x NOTNULL, (x NOTNULL)=a FROM t2 RIGHT JOIN t1 ON true WHERE (x NOTNULL)=a;
} {}
do_execsql_test join8-18020 {
CREATE TABLE t3(z);
INSERT INTO t3 VALUES('t3value');
SELECT *, x NOTNULL, (x NOTNULL)=a FROM t2 RIGHT JOIN t1 ON true INNER JOIN t3 ON (x NOTNULL)=a;
} {}
ifcapable rtree {
do_execsql_test join8-18030 {
CREATE VIRTUAL TABLE rtree1 USING rtree(a, x1, x2);
INSERT INTO rtree1 VALUES(0, 0, 0);
}
do_execsql_test join8-18040 {
SELECT *, x NOTNULL, (x NOTNULL)=a FROM t2
RIGHT JOIN rtree1 ON true INNER JOIN t3 ON (x NOTNULL)=+a;
} {}
do_execsql_test join8-18050 {
SELECT *, x NOTNULL, (x NOTNULL)=a FROM t2
RIGHT JOIN rtree1 ON true INNER JOIN t3 ON (x NOTNULL)=a;
} {}
}
reset_db
do_execsql_test join8-19000 {
CREATE TABLE t1(a INT);
CREATE TABLE t2(b INT, c INT);
CREATE TABLE t3(d INT);
INSERT INTO t1 VALUES(10);
INSERT INTO t2 VALUES(50,51);
INSERT INTO t3 VALUES(299);
CREATE INDEX t2b ON t2( (b IS NOT NULL) );
}
do_execsql_test join8-19010 {
SELECT * FROM t1 LEFT JOIN t2 ON true INNER JOIN t3 ON (b IS NOT NULL)=0;
}
# 2022-06-07
# https://sqlite.org/forum/forumpost/323f86cc30
reset_db
do_execsql_test join8-20000 {
CREATE TABLE t1(x TEXT);
INSERT INTO t1(x) VALUES('aaa');
CREATE VIEW v0(y) AS SELECT x FROM t1;
CREATE TABLE t2(z TEXT);
} {}
db null -
do_execsql_test join8-20010 {
SELECT * FROM t2 JOIN v0 ON z<>'bbb' RIGHT JOIN t1 ON z<>'ccc';
} {- - aaa}
do_execsql_test join8-20020 {
SELECT * FROM t2 JOIN v0 ON z<>'bbb' RIGHT JOIN t1 ON z<>'ccc' ORDER BY z;
} {- - aaa}
do_execsql_test join8-20030 {
SELECT 99 as "m" FROM t2 JOIN v0 ON z<>'bbb' RIGHT JOIN t1 ON z<>'ccc';
} {99}
do_execsql_test join8-20040 {
SELECT 99 as "m" FROM t2 JOIN v0 ON z<>'bbb' RIGHT JOIN t1 ON z<>'ccc' ORDER BY z;
} {99}
do_execsql_test join8-20050 {
SELECT count(*)
FROM (SELECT 99 as "m" FROM t2 JOIN v0 ON z<>'' RIGHT JOIN t1 ON z<>'') AS "t3";
} {1}
do_execsql_test join8-20060 {
SELECT count(*)
FROM (SELECT 99 as "m" FROM t2 JOIN v0 ON z<>'' RIGHT JOIN t1 ON z<>'' ORDER BY z) AS "t3";
} {1}
finish_test
+34094 -1958
View File
File diff suppressed because it is too large Load Diff
+613
View File
@@ -0,0 +1,613 @@
# 2022-05-31
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file implements tests for JOINs
#
# The test case output is (mostly) all generated by PostgreSQL 14. This
# test module was created as follows:
#
# 1. Run a TCL script (included at the bottom of this file) that
# generates an input script for "psql" that will run man
# diverse tests on joins.
#
# 2. Run the script from step (1) through psql and collect the
# output.
#
# 3. Make a few minor global search-and-replace operations to convert
# the psql output into a form suitable for this test module.
#
# 4. Add this header, and the script content at the footer.
#
# A few extra tests that were not generated from postgresql output are
# added at the end.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
db nullvalue -
db eval {
CREATE TABLE t1(x INT);
CREATE TABLE t2(y INT);
CREATE TABLE t3(z INT);
CREATE TABLE t4(w INT);
INSERT INTO t1 VALUES(10);
INSERT INTO t3 VALUES(20),(30);
INSERT INTO t4 VALUES(50);
}
do_execsql_test joinF-1 {
SELECT *
FROM t1 INNER JOIN t2 ON true
INNER JOIN t3 ON t2.y IS NOT NULL
INNER JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-2 {
SELECT *
FROM t1 INNER JOIN t2 ON true
INNER JOIN t3 ON t2.y IS NOT NULL
INNER JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-3 {
SELECT *
FROM t1 INNER JOIN t2 ON true
INNER JOIN t3 ON t2.y IS NOT NULL
LEFT JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-4 {
SELECT *
FROM t1 INNER JOIN t2 ON true
INNER JOIN t3 ON t2.y IS NOT NULL
LEFT JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-5 {
SELECT *
FROM t1 INNER JOIN t2 ON true
INNER JOIN t3 ON t2.y IS NOT NULL
RIGHT JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - - 50
}
do_execsql_test joinF-6 {
SELECT *
FROM t1 INNER JOIN t2 ON true
INNER JOIN t3 ON t2.y IS NOT NULL
RIGHT JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-7 {
SELECT *
FROM t1 INNER JOIN t2 ON true
LEFT JOIN t3 ON t2.y IS NOT NULL
INNER JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-8 {
SELECT *
FROM t1 INNER JOIN t2 ON true
LEFT JOIN t3 ON t2.y IS NOT NULL
INNER JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-9 {
SELECT *
FROM t1 INNER JOIN t2 ON true
LEFT JOIN t3 ON t2.y IS NOT NULL
LEFT JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-10 {
SELECT *
FROM t1 INNER JOIN t2 ON true
LEFT JOIN t3 ON t2.y IS NOT NULL
LEFT JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-11 {
SELECT *
FROM t1 INNER JOIN t2 ON true
LEFT JOIN t3 ON t2.y IS NOT NULL
RIGHT JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - - 50
}
do_execsql_test joinF-12 {
SELECT *
FROM t1 INNER JOIN t2 ON true
LEFT JOIN t3 ON t2.y IS NOT NULL
RIGHT JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-13 {
SELECT *
FROM t1 INNER JOIN t2 ON true
RIGHT JOIN t3 ON t2.y IS NOT NULL
INNER JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - 20 50
- - 30 50
}
do_execsql_test joinF-14 {
SELECT *
FROM t1 INNER JOIN t2 ON true
RIGHT JOIN t3 ON t2.y IS NOT NULL
INNER JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - 20 50
- - 30 50
}
do_execsql_test joinF-15 {
SELECT *
FROM t1 INNER JOIN t2 ON true
RIGHT JOIN t3 ON t2.y IS NOT NULL
LEFT JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - 20 50
- - 30 50
}
do_execsql_test joinF-16 {
SELECT *
FROM t1 INNER JOIN t2 ON true
RIGHT JOIN t3 ON t2.y IS NOT NULL
LEFT JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - 20 50
- - 30 50
}
do_execsql_test joinF-17 {
SELECT *
FROM t1 INNER JOIN t2 ON true
RIGHT JOIN t3 ON t2.y IS NOT NULL
RIGHT JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - 20 50
- - 30 50
}
do_execsql_test joinF-18 {
SELECT *
FROM t1 INNER JOIN t2 ON true
RIGHT JOIN t3 ON t2.y IS NOT NULL
RIGHT JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - 20 50
- - 30 50
}
do_execsql_test joinF-19 {
SELECT *
FROM t1 LEFT JOIN t2 ON true
INNER JOIN t3 ON t2.y IS NOT NULL
INNER JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-20 {
SELECT *
FROM t1 LEFT JOIN t2 ON true
INNER JOIN t3 ON t2.y IS NOT NULL
INNER JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-21 {
SELECT *
FROM t1 LEFT JOIN t2 ON true
INNER JOIN t3 ON t2.y IS NOT NULL
LEFT JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-22 {
SELECT *
FROM t1 LEFT JOIN t2 ON true
INNER JOIN t3 ON t2.y IS NOT NULL
LEFT JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-23 {
SELECT *
FROM t1 LEFT JOIN t2 ON true
INNER JOIN t3 ON t2.y IS NOT NULL
RIGHT JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - - 50
}
do_execsql_test joinF-24 {
SELECT *
FROM t1 LEFT JOIN t2 ON true
INNER JOIN t3 ON t2.y IS NOT NULL
RIGHT JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-25 {
SELECT *
FROM t1 LEFT JOIN t2 ON true
LEFT JOIN t3 ON t2.y IS NOT NULL
INNER JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
10 - - 50
}
do_execsql_test joinF-26 {
SELECT *
FROM t1 LEFT JOIN t2 ON true
LEFT JOIN t3 ON t2.y IS NOT NULL
INNER JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-27 {
SELECT *
FROM t1 LEFT JOIN t2 ON true
LEFT JOIN t3 ON t2.y IS NOT NULL
LEFT JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
10 - - 50
}
do_execsql_test joinF-28 {
SELECT *
FROM t1 LEFT JOIN t2 ON true
LEFT JOIN t3 ON t2.y IS NOT NULL
LEFT JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-29 {
SELECT *
FROM t1 LEFT JOIN t2 ON true
LEFT JOIN t3 ON t2.y IS NOT NULL
RIGHT JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
10 - - 50
}
do_execsql_test joinF-30 {
SELECT *
FROM t1 LEFT JOIN t2 ON true
LEFT JOIN t3 ON t2.y IS NOT NULL
RIGHT JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-31 {
SELECT *
FROM t1 LEFT JOIN t2 ON true
RIGHT JOIN t3 ON t2.y IS NOT NULL
INNER JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - 20 50
- - 30 50
}
do_execsql_test joinF-32 {
SELECT *
FROM t1 LEFT JOIN t2 ON true
RIGHT JOIN t3 ON t2.y IS NOT NULL
INNER JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - 20 50
- - 30 50
}
do_execsql_test joinF-33 {
SELECT *
FROM t1 LEFT JOIN t2 ON true
RIGHT JOIN t3 ON t2.y IS NOT NULL
LEFT JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - 20 50
- - 30 50
}
do_execsql_test joinF-34 {
SELECT *
FROM t1 LEFT JOIN t2 ON true
RIGHT JOIN t3 ON t2.y IS NOT NULL
LEFT JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - 20 50
- - 30 50
}
do_execsql_test joinF-35 {
SELECT *
FROM t1 LEFT JOIN t2 ON true
RIGHT JOIN t3 ON t2.y IS NOT NULL
RIGHT JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - 20 50
- - 30 50
}
do_execsql_test joinF-36 {
SELECT *
FROM t1 LEFT JOIN t2 ON true
RIGHT JOIN t3 ON t2.y IS NOT NULL
RIGHT JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - 20 50
- - 30 50
}
do_execsql_test joinF-37 {
SELECT *
FROM t1 RIGHT JOIN t2 ON true
INNER JOIN t3 ON t2.y IS NOT NULL
INNER JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-38 {
SELECT *
FROM t1 RIGHT JOIN t2 ON true
INNER JOIN t3 ON t2.y IS NOT NULL
INNER JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-39 {
SELECT *
FROM t1 RIGHT JOIN t2 ON true
INNER JOIN t3 ON t2.y IS NOT NULL
LEFT JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-40 {
SELECT *
FROM t1 RIGHT JOIN t2 ON true
INNER JOIN t3 ON t2.y IS NOT NULL
LEFT JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-41 {
SELECT *
FROM t1 RIGHT JOIN t2 ON true
INNER JOIN t3 ON t2.y IS NOT NULL
RIGHT JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - - 50
}
do_execsql_test joinF-42 {
SELECT *
FROM t1 RIGHT JOIN t2 ON true
INNER JOIN t3 ON t2.y IS NOT NULL
RIGHT JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-43 {
SELECT *
FROM t1 RIGHT JOIN t2 ON true
LEFT JOIN t3 ON t2.y IS NOT NULL
INNER JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-44 {
SELECT *
FROM t1 RIGHT JOIN t2 ON true
LEFT JOIN t3 ON t2.y IS NOT NULL
INNER JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-45 {
SELECT *
FROM t1 RIGHT JOIN t2 ON true
LEFT JOIN t3 ON t2.y IS NOT NULL
LEFT JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-46 {
SELECT *
FROM t1 RIGHT JOIN t2 ON true
LEFT JOIN t3 ON t2.y IS NOT NULL
LEFT JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-47 {
SELECT *
FROM t1 RIGHT JOIN t2 ON true
LEFT JOIN t3 ON t2.y IS NOT NULL
RIGHT JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - - 50
}
do_execsql_test joinF-48 {
SELECT *
FROM t1 RIGHT JOIN t2 ON true
LEFT JOIN t3 ON t2.y IS NOT NULL
RIGHT JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
}
do_execsql_test joinF-49 {
SELECT *
FROM t1 RIGHT JOIN t2 ON true
RIGHT JOIN t3 ON t2.y IS NOT NULL
INNER JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - 20 50
- - 30 50
}
do_execsql_test joinF-50 {
SELECT *
FROM t1 RIGHT JOIN t2 ON true
RIGHT JOIN t3 ON t2.y IS NOT NULL
INNER JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - 20 50
- - 30 50
}
do_execsql_test joinF-51 {
SELECT *
FROM t1 RIGHT JOIN t2 ON true
RIGHT JOIN t3 ON t2.y IS NOT NULL
LEFT JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - 20 50
- - 30 50
}
do_execsql_test joinF-52 {
SELECT *
FROM t1 RIGHT JOIN t2 ON true
RIGHT JOIN t3 ON t2.y IS NOT NULL
LEFT JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - 20 50
- - 30 50
}
do_execsql_test joinF-53 {
SELECT *
FROM t1 RIGHT JOIN t2 ON true
RIGHT JOIN t3 ON t2.y IS NOT NULL
RIGHT JOIN t4 ON true
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - 20 50
- - 30 50
}
do_execsql_test joinF-54 {
SELECT *
FROM t1 RIGHT JOIN t2 ON true
RIGHT JOIN t3 ON t2.y IS NOT NULL
RIGHT JOIN t4 ON true
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
} {
- - 20 50
- - 30 50
}
finish_test
############################################################################
# This is the TCL script used to generate the psql script that generated
# the data above.
#
# puts "
# \\pset border off
# \\pset tuples_only on
# \\pset null -
#
# DROP TABLE IF EXISTS t1;
# DROP TABLE IF EXISTS t2;
# DROP TABLE IF EXISTS t3;
# DROP TABLE IF EXISTS t4;
# CREATE TABLE t1(x INT);
# CREATE TABLE t2(y INT);
# CREATE TABLE t3(z INT);
# CREATE TABLE t4(w INT);
# INSERT INTO t1 VALUES(10);
# INSERT INTO t3 VALUES(20),(30);
# INSERT INTO t4 VALUES(50);
# "
#
# proc echo {prefix txt} {
# regsub -all {\n} $txt \n$prefix txt
# puts "$prefix$txt"
# }
#
# set n 0
# foreach j1 {INNER LEFT RIGHT} {
# foreach j2 {INNER LEFT RIGHT} {
# foreach j3 {INNER LEFT RIGHT} {
#
# incr n
# set q1 ""
# append q1 "SELECT *\n"
# append q1 " FROM t1 $j1 JOIN t2 ON true\n"
# append q1 " $j2 JOIN t3 ON t2.y IS NOT NULL\n"
# append q1 " $j3 JOIN t4 ON true\n"
# append q1 " ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);"
#
# echo "\\qecho " "do_execsql_test joinF-$n \{"
# echo "\\qecho X " $q1
# echo "\\qecho " "\} \{"
# puts $q1
# echo "\\qecho " "\}"
#
# incr n
# set q1 ""
# append q1 "SELECT *\n"
# append q1 " FROM t1 $j1 JOIN t2 ON true\n"
# append q1 " $j2 JOIN t3 ON t2.y IS NOT NULL\n"
# append q1 " $j3 JOIN t4 ON true\n"
# append q1 " WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)\n"
# append q1 " ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);"
#
# echo "\\qecho " "do_execsql_test joinF-$n \{"
# echo "\\qecho X " $q1
# echo "\\qecho " "\} \{"
# puts $q1
# echo "\\qecho " "\}"
#
# }
# }
# }
#
+27 -3
View File
@@ -260,11 +260,23 @@ do_catchsql_test 11.8 {
#
do_execsql_test 12.1 {
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(1,2);
CREATE TABLE t1(a INT,b INT); INSERT INTO t1 VALUES(1,2);
DROP TABLE IF EXISTS t2;
CREATE TABLE t2(x,y); INSERT INTO t2 VALUES(3,4);
CREATE TABLE t2(x INT,y INT); INSERT INTO t2 VALUES(3,4);
SELECT *,'x' FROM t1 LEFT JOIN t2 ON (a,b)=(x,y);
} {1 2 {} {} x}
db null -
do_execsql_test 12.2 {
SELECT t1.*, t2.* FROM t2 RIGHT JOIN t1 ON (a,b)=(x,y);
} {1 2 - -}
do_execsql_test 12.3 {
SELECT t1.*, t2.* FROM t1 FULL JOIN t2 ON (a,b)=(x,y)
ORDER BY coalesce(a,x);
} {
1 2 - -
- - 3 4
}
db null {}
foreach {tn sql} {
@@ -540,7 +552,7 @@ do_execsql_test 19.36 {
SELECT * FROM t1 WHERE (3,32)>=(a,b) ORDER BY a DESC;
} {2 22 1 11}
# 2018-02-18: Memory leak nexted row-value. Detected by OSSFuzz.
# 2018-02-18: Memory leak nested row-value. Detected by OSSFuzz.
#
do_catchsql_test 20.1 {
SELECT 1 WHERE (2,(2,0)) IS (2,(2,0));
@@ -632,9 +644,15 @@ do_execsql_test 26.10 {
do_execsql_test 26.20 {
SELECT 2 FROM t1 LEFT JOIN t0 ON (c0, x'') != (NULL, 0);
} {2}
do_execsql_test 26.21 {
SELECT 21 FROM t0 RIGHT JOIN t1 ON (c0, x'') != (NULL, 0);
} {21}
do_execsql_test 26.30 {
SELECT 3 FROM t1 LEFT JOIN t0 WHERE (c0, x'') != (NULL, 0);
} {3}
do_execsql_test 26.31 {
SELECT 31 FROM t0 RIGHT JOIN t1 WHERE (c0, x'') != (NULL, 0);
} {31}
# 2019-12-30 ticket 892575cdba4e1e36
#
@@ -700,6 +718,9 @@ do_execsql_test 31.1 {
CREATE TABLE b(b1 UNIQUE,b2);
SELECT * FROM a LEFT JOIN b ON b2=NULL AND b2=5 WHERE (b1,substr(b.b1,1,1))==(SELECT 1024,'b');
} {}
do_execsql_test 31.1b {
SELECT * FROM b RIGHT JOIN a ON b2=NULL AND b2=5 WHERE (b1,substr(b.b1,1,1))==(SELECT 1024,'b');
} {}
do_execsql_test 31.2 {
CREATE TABLE t1(a);
INSERT INTO t1 VALUES(0);
@@ -707,6 +728,9 @@ do_execsql_test 31.2 {
INSERT INTO t2 VALUES(NULL,123,456);
SELECT * FROM t1 LEFT JOIN t2 ON b=NULL WHERE (c,d)==(SELECT 123, 456+a);
} {}
do_execsql_test 31.2b {
SELECT * FROM t2 RIGHT JOIN t1 ON b=NULL WHERE (c,d)==(SELECT 123, 456+a);
} {}
# 2022-02-03 dbsqlfuzz 80a9fade844b4fb43564efc972bcb2c68270f5d1
reset_db
+2 -1
View File
@@ -213,7 +213,8 @@ do_execsql_test 5.0 {
CREATE TABLE T2(a TEXT PRIMARY KEY,n INT);
INSERT INTO T2(a, n) VALUES('aaa',0);
SELECT * FROM T2
WHERE (a,n) IN (SELECT T1.a, V.n FROM T1, (SELECT * FROM (SELECT 0 n)) V);
WHERE (a,n) IN (SELECT T1.a, V.n
FROM T1, (SELECT * FROM (SELECT 0 n) T3) V);
} {aaa 0}
+28
View File
@@ -28,4 +28,32 @@ do_execsql_test subtype1-130 {
SELECT test_setsubtype('hello',123);
} {hello}
# 2022-06-09
# https://sqlite.org/forum/forumpost/3d9caa45cbe38c78
#
# Avoid carrying subtypes through into a subquery that has been flattened
# or to which the outer WHERE clause has been pushed down.
#
reset_db
do_execsql_test subtype1-200 {
CREATE TABLE t1(a); INSERT INTO t1 VALUES ('x');
CREATE VIEW t2(b) AS SELECT json(TRUE);
CREATE TABLE t3(b); INSERT INTO t3 VALUES(json(TRUE));
}
do_execsql_test subtype1-210 {
SELECT * FROM t3, t1 WHERE NOT json_quote(b);
} {1 x}
do_execsql_test subtype1-220 {
SELECT * FROM t2, t1 WHERE NOT json_quote(b);
} {1 x}
do_execsql_test subtype1-230 {
WITH t4(a) AS MATERIALIZED (SELECT json(1)) SELECT subtype(a) FROM t4;
} {0}
do_execsql_test subtype1-231 {
WITH t4(a) AS NOT MATERIALIZED (SELECT json(1)) SELECT subtype(a) FROM t4;
} {0}
finish_test
+130
View File
@@ -0,0 +1,130 @@
# 2022-05-24
#
# 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.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix upfrom4
do_execsql_test 100 {
DROP TABLE IF EXISTS t5;
DROP TABLE IF EXISTS m1;
DROP TABLE IF EXISTS m2;
CREATE TABLE t5(a INTEGER PRIMARY KEY, b TEXT, c TEXT);
CREATE TABLE m1(x INTEGER PRIMARY KEY, y TEXT);
CREATE TABLE m2(u INTEGER PRIMARY KEY, v TEXT);
INSERT INTO t5 VALUES(1, 'one', 'ONE');
INSERT INTO t5 VALUES(2, 'two', 'TWO');
INSERT INTO t5 VALUES(3, 'three', 'THREE');
INSERT INTO t5 VALUES(4, 'four', 'FOUR');
INSERT INTO m1 VALUES(1, 'i');
INSERT INTO m1 VALUES(2, 'ii');
INSERT INTO m1 VALUES(3, 'iii');
INSERT INTO m2 VALUES(1, 'I');
INSERT INTO m2 VALUES(3, 'II');
INSERT INTO m2 VALUES(4, 'III');
SELECT * FROM t5;
} {1 one ONE 2 two TWO 3 three THREE 4 four FOUR}
do_execsql_test 110 {
BEGIN;
UPDATE t5 SET b=y, c=v FROM m1 LEFT JOIN m2 ON (x=u) WHERE x=a;
SELECT * FROM t5 ORDER BY a;
ROLLBACK;
} {1 i I 2 ii {} 3 iii II 4 four FOUR}
do_execsql_test 120 {
BEGIN;
UPDATE t5 SET b=y, c=v FROM m2 RIGHT JOIN m1 ON (x=u) WHERE x=a;
SELECT * FROM t5 ORDER BY a;
ROLLBACK;
} {1 i I 2 ii {} 3 iii II 4 four FOUR}
reset_db
db null -
do_execsql_test 200 {
CREATE TABLE t1(a INT PRIMARY KEY, b INT, c INT);
INSERT INTO t1(a) VALUES(1),(2),(8),(19);
CREATE TABLE c1(x INTEGER PRIMARY KEY, b INT);
INSERT INTO c1(x,b) VALUES(1,1),(8,8),(17,17),(NULL,NULL);
CREATE TABLE c2(x INT,c INT);
INSERT INTO c2(x,c) VALUES(2,2),(8,8),(NULL,NULL);
CREATE TABLE dual(dummy TEXT);
INSERT INTO dual VALUES('X');
} {}
do_execsql_test 210 {
BEGIN;
SELECT * FROM t1 ORDER BY a;
UPDATE t1 SET b=c1.b, c=c2.c
FROM dual, c1 NATURAL RIGHT JOIN c2
WHERE x=a;
SELECT * FROM t1 ORDER BY a;
ROLLBACK;
} {
1 - -
2 - -
8 - -
19 - -
1 - -
2 - 2
8 8 8
19 - -
}
do_execsql_test 300 {
CREATE TABLE t2(x);
CREATE TRIGGER AFTER INSERT ON t2 BEGIN
UPDATE t1 SET b=c1.b, c=c2.c
FROM dual, c1 NATURAL RIGHT JOIN c2
WHERE x=a;
END;
} {}
do_execsql_test 310 {
BEGIN;
SELECT * FROM t1 ORDER BY a;
INSERT INTO t2(x) VALUES(1);
SELECT * FROM t1 ORDER BY a;
ROLLBACK;
} {
1 - -
2 - -
8 - -
19 - -
1 - -
2 - 2
8 8 8
19 - -
}
# 2022-05-26 dbsqlfuzz crash-9401d6ba699f1257d352a657de236286bf2b14da
#
reset_db
db null -
do_execsql_test 400 {
CREATE TABLE t2(x,y,z PRIMARY KEY) WITHOUT ROWID;
INSERT INTO t2 VALUES(89,-89,6);
CREATE TABLE t1(a INT,b TEXT,c TEXT,d REAL) STRICT;
INSERT INTO t1 VALUES(1,'xyz','def',4.5);
CREATE TRIGGER t1tr BEFORE UPDATE ON t1 BEGIN
INSERT INTO t1(a,b) VALUES(1000,'uvw');
UPDATE t1 SET b=NULL FROM (SELECT CAST(a AS varchar) FROM t1 ORDER BY b) NATURAL LEFT FULL JOIN t1 AS text;
END;
UPDATE t1 SET b=b|100;
SELECT * FROM t1 ORDER BY a;
} {
1 100 def 4.5
1000 - - -
}
finish_test
+94
View File
@@ -0,0 +1,94 @@
# 2022 June 2
#
# 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.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix windowD
do_execsql_test 1.0 {
CREATE TABLE t0(c0 TEXT);
CREATE VIEW v0(c0, c1)
AS SELECT CUME_DIST() OVER (PARTITION BY t0.c0), TRUE FROM t0;
INSERT INTO t0 VALUES ('x');
}
do_execsql_test 1.1 {
SELECT ('500') IS (v0.c1) FROM v0;
} {
0
}
do_execsql_test 1.2 {
SELECT (('500') IS (v0.c1)) FROM v0, t0;
} {
0
}
do_execsql_test 1.2 {
SELECT (('500') IS (v0.c1)) IS FALSE FROM v0;
} {
1
}
do_execsql_test 1.3 {
SELECT * FROM v0;
} {
1.0 1
}
do_execsql_test 1.4 {
SELECT * FROM v0 WHERE ('500' IS v0.c1) IS FALSE;
} {
1.0 1
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
CREATE TABLE t1(x);
INSERT INTO t1 VALUES('value');
CREATE VIEW v1(a, b, c, d) AS SELECT 1, 2, TRUE, FALSE FROM t1;
}
do_execsql_test 2.1 {
SELECT 500 IS a, 500 IS b, 500 IS c, 500 IS d FROM v1
} {0 0 0 0}
do_execsql_test 2.2 {
SELECT * FROM v1 WHERE 500 IS c;
} {}
do_execsql_test 2.3 {
SELECT * FROM v1 WHERE 500 IS d;
} {}
do_execsql_test 2.4 {
CREATE VIEW v2 AS SELECT max(x) OVER () AS a, TRUE AS c FROM t1;
}
do_execsql_test 2.5 {
SELECT 500 IS c FROM v2;
} 0
do_execsql_test 2.6 {
SELECT * FROM v2 WHERE 500 IS c;
} {}
finish_test