Compare commits

...

185 Commits

Author SHA1 Message Date
drh 51bb5dec6c Fix a test case broken by the previous check-in.
FossilOrigin-Name: 1e61864c3bffec133632f4575d59d11936e8bafc089c4d84a129dcaf7e9b6d7d
2023-10-21 11:00:57 +00:00
drh d5e040b6f0 Add string_agg(X,Y) as an alias for group_concat(X,Y), for compatibility
with SQLServer and PG.

FossilOrigin-Name: b91c19bf2680f60d7826ab5d9e7902e2dc2a55d847bbea565a6489d47f2cc8f1
2023-10-20 20:19:30 +00:00
dan 71bae9f19f Fix a problem allowing a COMMIT following an OOM to cause fts5 corruption.
FossilOrigin-Name: fba3129d1362dd3ee04d61cb3d3c48518cd98dde3febc85298f411776d60073e
2023-10-20 19:59:12 +00:00
drh 81656b15c9 Fix PRAGMA integrity_check so that it does not raise an error if the
schema contains a CREATE VIRTUAL TABLE that does not have its module
loaded.

FossilOrigin-Name: 5cb61c6788d7c0170b587e4667ee57d588c991a9901951762e33b97c6a135a82
2023-10-20 19:06:04 +00:00
drh e98f62fb3f Omit some redundant calls to strlen() used to find the size of
Expr.u.zToken in sqliteExprDup().  This inefficiency was seen while working
on the previous check-in, and I thought it best to fix it while it was
fresh in mind.

FossilOrigin-Name: b7a84eff5dcdf5b2ff81331097cdc64d0c5d16a4ae224320f39b64541ec72dc7
2023-10-20 18:09:08 +00:00
drh ab3eb5b770 Improvements to the sqlite3ExprDup() logic for faster performance and better
run-time error detection.  This check-in fixes the 5x oversize memory allocation
bug from [f371e4c0f8ea73ae] as well as all other known issues that result from
handing the ORDER BY clause of an aggregate function off of the pLeft pointer
of the Expr object.

FossilOrigin-Name: f5c01676fd281e938181b846dd2024d050f597dc6a7a91928beab9d8553dfdb5
2023-10-20 17:15:15 +00:00
dan 17c3408e0e Add the SQLITE_CHANGESETAPPLY_FKNOACTION flag to sqlite3session.h, for passing to sqlite3changeset_apply_v2() to cause all foreign key constraints to behave as if they were declared NO ACTION.
FossilOrigin-Name: fc9f82ea084159eaf3dd1757b96d17d1201b00c4e06455a7dcd8067172b25f28
2023-10-20 17:06:39 +00:00
drh 90e4a3b7fc Simplifications and optimizations to the Expr object duplication logic.
The 5x multiplier crutch from [f371e4c0f8ea73ae] is still present.  More
fixes are still needed.

FossilOrigin-Name: 56142a78163b755f16afc05201f623a7a19d9a4b0620a67f7fa20d2a965a288d
2023-10-20 15:47:30 +00:00
drh 0a261eb4bb Omit an unused constant from sessionfuzz.c to prevent a compiler warning.
FossilOrigin-Name: a0cf7e24f928183866ac54f0d6cd83c859d487a19dc87572ab0188d90d2ff87d
2023-10-20 14:05:26 +00:00
stephan 11223e0cff Correct the opfs-sahpool VFS's xGetLastError() method to return the previous error code, not 0, on success.
FossilOrigin-Name: 95a1dde63117d696323c775580b9c04f044a5b8d609e9174b739ac03ecc1336c
2023-10-20 12:23:49 +00:00
drh d707e012f0 Use sqlite3ParserAddCleanup() instead of calling sqlite3ExprListDelete()
directly when disposing of an unused ORDER BY in an aggregate function,
to avoid disrupting ALTER TABLE data structures.

FossilOrigin-Name: d083e42086733ecd79aba8c268e020b01782bfe1cfa9684ce1c277af9c8bf92a
2023-10-20 10:18:03 +00:00
drh dcf76a8bbd Remove a faulty assert() that was added just a few check-ins ago by
[8b6fffb552c30f9c].

FossilOrigin-Name: 18e7c826f08bce51719ef045daa60200b33790f2fe312c6853f9ef6c7e9d5030
2023-10-19 20:06:20 +00:00
drh 5f53f85e22 For TK_ORDER expression nodes to always be full-size.
FossilOrigin-Name: a5c73b46f4772f214ffbfa31cb87dce82ebd690addabef539bd09def26b7744d
2023-10-19 19:57:57 +00:00
drh 5080397e67 Fix an adverse interaction between the new aggregate ORDER BY logic and the
expression compressor.

FossilOrigin-Name: f371e4c0f8ea73aee9ea0645f396e3da20f1eb97be34c83de9d94c9cbb959934
2023-10-19 19:19:59 +00:00
drh d8b840a4f4 In the TreeView logic (used for debugging only) do not show unnecessary fields
of the Window object when a function has a FILTER.

FossilOrigin-Name: d2c6b82c346dbf39e07f09d187e4a8216a56caa7df3dcfafc82e32f676a1db8a
2023-10-19 19:08:33 +00:00
dan ee3c55471c Fix an fts3 problem caused by reducing the page size using the undocumented "nodesize" option, then running an incremental-merge.
FossilOrigin-Name: 2875dcb1ac86db2704ad377d719f308c8837e196b49c98b20ceda1324f2d27ea
2023-10-19 18:23:17 +00:00
drh 4e254641ff Simplify the Expr compression logic slightly by adding the new EP_FullSize
property to expressions that are exceptions to the rule and should not be
compressed.

FossilOrigin-Name: d5ae82ec52eafed5e3dc8c9d99685f6523fce7d973ef7e8d9d75ed9b8912426a
2023-10-19 18:07:58 +00:00
drh 0588385799 Simplification to sqlite3GetVarint32() to avoid confusing gcov.
FossilOrigin-Name: 89862c51ad9715bedf5b029db484602e740f0db7404970d482ce503c1b9a0ed2
2023-10-19 13:35:22 +00:00
drh 11877c6ba4 Fix an error in a debugging routine used inside of testcase(). Does not
appear in production.  Problem discovered by scan-build.

FossilOrigin-Name: c6c9d7ed6dbc71b998aeaaa1bdeb36b1bb9b902f9d6088ad36db29f8ea8b7ef6
2023-10-19 13:00:41 +00:00
drh 266178f369 New assert() statements to help verify correctness of the ORDER BY aggregate
code.

FossilOrigin-Name: 8b6fffb552c30f9cbf7102c2e26a7b486c4c9edd9f494d803de7004db3018277
2023-10-19 12:36:59 +00:00
drh 519017f25a Fix the sqlite3ReferencesSrcList() routine so that it recognizes columns
in the ORDER BY clause of an aggregate.  Fixes a problem with
[634286828dad873d] discoverd by dbsqlfuzz.

FossilOrigin-Name: 3d26f1aaa4876f21f2c3abf13bbc37933c8f32471153e29019880cc9530cb011
2023-10-19 12:12:49 +00:00
drh d9451051f1 Add support for ORDER BY on the argument list of aggregate functions.
FossilOrigin-Name: 634286828dad873dba244751441aa729cd37b6ed5899fa5875643d4af03c006c
2023-10-19 01:26:34 +00:00
drh 0b633640fa Merge the latest changes from trunk.
FossilOrigin-Name: d18bc400146812c8c5048af7eaf4a5371ddd80fc9ce895d253b6114b9eae7fc3
2023-10-19 01:09:49 +00:00
drh 69ff726d4d Fix a false-positive in run-time error checking.
FossilOrigin-Name: cd63eec0758960d9ee63d7b964ec62e2bd622f8c94a58bd9556046381ffa18d0
2023-10-19 00:25:35 +00:00
drh d05e54efff Changes for test coverage.
FossilOrigin-Name: ddfa09c6031afd4391a9888381bf09214cd542a826b431eeb1537a070f65c5f9
2023-10-18 23:48:24 +00:00
drh 42f5ea354e Test case for alter column that is used in the ORDER BY of an aggregate.
FossilOrigin-Name: 648ddb3a8eb358fc6c95aaa561ae68476c77996abda23890054ac07166278cef
2023-10-18 22:53:22 +00:00
drh ce51b31367 Deal with an OOM condition while processing aggregate ORDER BYs.
FossilOrigin-Name: b6d44427ec0184880ad14919667e2f526777c5085ffa636f6734cb5951a31225
2023-10-18 22:27:59 +00:00
drh 20b95f8d3f Make sure all terms of the ORDER BY within an aggregate go through aggregate
analysis.  Do not attach an aggregate ORDER BY to a window function.

FossilOrigin-Name: 16f3805514a741405f70e0ee3b5a6b67720bc75719372e82daa4136fe411ea2b
2023-10-18 22:03:48 +00:00
drh 49dc1be96f Basic test cases.
FossilOrigin-Name: 6cccf86c362631ada0c6cbaf661520648f8bbf7ae2b137b3d7d959647387e5c2
2023-10-18 19:44:59 +00:00
drh 0021079ceb Fix one minor error in order to get all legacy tests to pass.
FossilOrigin-Name: 5a885139c433a143302ab43dc5aaac1fed9db61548b4ff7d4b19b3a6a46c8fcc
2023-10-18 19:07:45 +00:00
drh 59a0d0bbf9 ORDER BY on aggregates seem to work, at least for simple smoke tests. Lots
more testing is needed though.  Surely there are many bugs.

FossilOrigin-Name: 64c12a835b6f1df8f2f5f4a41de083f6b3fc7f8030042c6aac0082382cd9cc4d
2023-10-18 18:11:11 +00:00
drh db19f48b69 Basic error checking. Resolve symbols in the aggregate ORDER BY expressions.
FossilOrigin-Name: c83a53a574d312130d1238c05ffa449d8bed2535d5ef5b5d9cf02f894494cca4
2023-10-18 13:58:31 +00:00
drh f8202f1ff3 Enhance the parser so that it can accept an ORDER BY clause on a function
invocation.  For this incremental check-in, the ORDER BY clause is currently
ignored.

FossilOrigin-Name: 3a98ff24bf468ed42d410a9a12d9f9b2ca154c7babe99fd6bc6f7b0565e0d132
2023-10-18 13:18:52 +00:00
stephan 0b4de1acac JS: replace one errant reference to 'self' with 'globalThis' and remove a separate dead-code 'self' reference.
FossilOrigin-Name: da1a47932ea96755b1e11fc2547ce11780b87846af6885857318ff18f1e62c31
2023-10-17 23:35:47 +00:00
drh 43dc31cf1f Fix a harmless compiler warning about variant types for a pointer function.
FossilOrigin-Name: 37ff0d8e7f91c32e8c53bb015280be47c66bf599281b640a8d3fd41335b55289
2023-10-17 19:33:52 +00:00
drh 6db4e5ed4c Make a new branch in the SqlExec opcode reachable by tests.
FossilOrigin-Name: 023ad470b68b7c09e159244493a6d0285d706efdecd8719a71e6885509d4750c
2023-10-17 18:59:13 +00:00
drh 42b49a3138 Omit an unreachable branch when SQLITE_ENABLE_API_ARMOR is not defined.
FossilOrigin-Name: 5f8b807b568f4f9ec65f1c924e0e1ac2f996f405e045204145ad7116be7580ee
2023-10-17 18:28:27 +00:00
drh 4c827feba1 Changes to sqlite3IntFloatCompare() in an attempt to better measure
branch coverage in the face of aggressive compiler optimization.

FossilOrigin-Name: 5781d043ffeccda03357ee530564987443dc1deb6111a4028701b4a98fdfe16d
2023-10-17 17:53:46 +00:00
drh 52d934e316 Fix a JSON bug introduced by the optimization of [df099ad713011b67] and
first appearing in 3.43.0.  The problem occurs when doing a JSON_EXTRACT()
on an array element that was added by JSON_SET() without first reparsing.
Reported by [forum:/forumpost/fc0e3f1e2a|forum post fc0e3f1e2a].

FossilOrigin-Name: e5099c549a1d8959d4015516f090b8e6438e517a64b20651175bf5413d94fb58
2023-10-17 13:41:41 +00:00
drh 5e0b11bda7 Enhance the documentation of sqlite3_deserialize() to make it clear that
the input database may not be in WAL mode.
[forum:/forumpost/a7e272cee9ac469f|Forum post a7e272cee9ac469f]

FossilOrigin-Name: e754789971e019647e7068f76dc6f9778675e4849fe51d8b25b39d3edf2379e5
2023-10-17 11:57:36 +00:00
drh 11095bcb89 When an I/O or similar error occurs during a transaction with
journal_mode=MEMORY, attempt to rollback before closing the journal, as
all rollback information is forgotten when a memory rollback journal is
closed.

FossilOrigin-Name: 1d67f75de259e5a26b751a50432822a268ebe367cda6510891ab81a15e5daa1c
2023-10-17 10:47:42 +00:00
stephan b866f98346 Harden API_ARMOR checks for sqlite3_stmt_scanstatus_v2().
FossilOrigin-Name: d3f38e813ba3b887e973af034713cd58fdfbe07da375b154accc7b5790cfaeda
2023-10-17 02:15:49 +00:00
stephan 08747d44a2 JNI: add aggregate function support to the wrapper1 API.
FossilOrigin-Name: 15b28b340a5c5efdbfe3fbed16ee0b699561edaeebb77446addf2374bdf9357e
2023-10-16 16:04:23 +00:00
stephan 626d0a9fda JNI: add scalar UDF support to the wrapper1 API.
FossilOrigin-Name: a850535766d2243d9475e1523c753615875a2da9c9d82a41a9fb61b141c6334a
2023-10-16 14:31:13 +00:00
stephan f2d7e961d9 JNI: initial draft (untested - requires more infrastructure first) of a UDF argument/result-handling interface which completely hides the C-style API from the client.
FossilOrigin-Name: 43b10a5cf9cb8be53d62914f340d533e60a70bf4caa8b9b91c0f867fa0f70493
2023-10-16 13:04:42 +00:00
stephan 7e540e5a2c JNI: cleanups in Tester2 and update the jar makefile target to account for [9fcdf96adca2].
FossilOrigin-Name: abc82bf4b800dde1b6e6172c7be816edb391fdbed5dbd2749f54623fdf3bf8e6
2023-10-16 10:38:34 +00:00
stephan 22fd153e43 JNI: move the C-style API parts into the capi subpackage and the higher-level wrapper into (tentatively) wrapper1, so that CApi.java can support multiple independent higher-level wrappers without name collisions, and CApi can be made public but have the option to be elided from wrapper-level javadocs for wrappers which do not wish to expose it.
FossilOrigin-Name: 9fcdf96adca25cc2d2f4b75ec4eea94254fb9671c5ba63b88213d7f62dedff1b
2023-10-16 10:27:19 +00:00
stephan 19179722d6 JNI: after calling a Java-side UDF, zero-out the pointer of the Java-side sqlite3_context and sqlite3_value array entries to avoid misbehavior if a client makes the mistake of holding a reference to one of those objects.
FossilOrigin-Name: 9fc3104f76a83d600beb11d91feb97bcea8bc7f7cda8cd73e7a6b81fbba879df
2023-10-16 08:10:11 +00:00
stephan f83a4d850c JNI: do not expose SQLITE_OPEN_... flags which are specific to VFSes.
FossilOrigin-Name: 2b4e53d8be42a3bc098317abd8bb58b8ddc25094d80787f784bbc896f4f7b976
2023-10-16 08:05:51 +00:00
stephan 24f6baca16 Change 3 instance of #if SQLITE_ENABLE_API_ARMOR to #ifdef for consistency with how it is normally used.
FossilOrigin-Name: dd766eeb59fec71627dd8ad8f120875b96fda455c6401e5671e086b785e2b2bc
2023-10-15 13:36:21 +00:00
stephan 5698e62f9e JNI: enable all optional components in the default build and fix a test broken by an option toggle.
FossilOrigin-Name: 5586a9df58a400c20a6815e8b0fbc7a7c49e5ef89daf92c452f1990586ca78ae
2023-10-15 13:32:38 +00:00
stephan 29aeb620e7 Correct non-void return from sqlite3_preupdate_hook() when API_ARMOR is enabled. Broken by [6cb77503484e].
FossilOrigin-Name: 40f5906f48d3f1f44d54e5b271fe04e7082166e50e3ed250bb57a4760f112e8a
2023-10-15 13:25:39 +00:00
stephan 6089e5bfcb Add coverage of more functions to SQLITE_ENABLE_API_ARMOR builds.
FossilOrigin-Name: 6cb77503484e104a51f08690974d6e19c336ab5467d327f84be1d042615f008c
2023-10-15 12:11:10 +00:00
stephan 8a273bb6e8 JNI: extend [baf220e78a46246c47] to include macro-generated sqlite3_value_...() bindings.
FossilOrigin-Name: ad34b2542e330c22fcf331810ab43e66346b1bad8de1a3989e6208cfa0fc2f92
2023-10-14 20:44:13 +00:00
stephan 4b1e42ef94 JNI: make the sqlite3_value_...() family of bindings resistent to NULL arguments.
FossilOrigin-Name: baf220e78a46246c476cd429ac131698db5fa66f62fb5725d4f736fe5246283c
2023-10-14 20:34:40 +00:00
drh d718bde6da Do not allow an ALTER TABLE ADD COLUMN on a STRICT table if the added column
contains a DEFAULT clause that would violate the type of the added column.

FossilOrigin-Name: 75b075863eaa56e36635a1d27740d37de8600ba92099b3fad9378a1e6ce12c0e
2023-10-14 20:24:52 +00:00
stephan eb62ccdab5 Add API_ARMOR support to the scanstatus family of functions.
FossilOrigin-Name: 2f3101f016d5f8d954a262befdf5e0f938a223e444bc534e1d3655b733f4663d
2023-10-14 20:01:55 +00:00
drh 153790d9ae The rtreecheck() SQL function should not invoke BEGIN or COMMIT as this
causes issues for statement transactions.

FossilOrigin-Name: b7b2e30b570efda338c62e88446dedb6667640f33855b2422ad20c7b569b025a
2023-10-14 17:14:53 +00:00
stephan 4213cc78cd JNI: add a missing result code check to sqlite3_bind_value(stmt, null).
FossilOrigin-Name: f1a357f42de58927c33e7047dc4417e3b9549d7529625f30c583e66b7d603b00
2023-10-14 16:57:48 +00:00
stephan a9000d424e JNI: add missing sqlite3_bind_value() and minor memory-safety-related cleanups.
FossilOrigin-Name: 3900031763abf4b2aab7b57e886cc407571d0d5928b74f24dcea8fb7371655af
2023-10-14 16:56:12 +00:00
stephan c76e450908 Add API_ARMOR support to the sqlite3_result_...() family of functions and sqlite3_bind_zeroblob64().
FossilOrigin-Name: afabe3e35a66625527e2881749cdb6e13300888ab57bc0f05889d0e3ee203d73
2023-10-14 16:29:36 +00:00
stephan 7a54b12695 More API_ARMOR additions.
FossilOrigin-Name: 78ebf838f645742f87733665cd72af736df345683b27377a2c8310c893b1769d
2023-10-14 14:53:18 +00:00
stephan a17f63290c Add column name to API_ARMOR check in sqlite3_blob_open() to avoid a null-pointer deref.
FossilOrigin-Name: 0114a6622afc4588c47e98d804340449417b603dc4831513eab4d8e4ccb15d42
2023-10-14 13:24:30 +00:00
stephan 067a13c395 Revert [f6cd88e6b234] - the NULL callback case is perfectly legal.
FossilOrigin-Name: 718ab67607895176e529eb7469832d262a347d030e83e7ee66d3b4704bf933de
2023-10-14 12:45:11 +00:00
stephan 6d035d481f Bring JNI-side sqlite3_last_insert_rowid() and sqlite3_table_column_metadata() in line with the core's NULL handling.
FossilOrigin-Name: c2afc1c2a2fc9bdf3d7b7701004e0fa40965cf8c6b7cf44b46f2ca37cfa1d2aa
2023-10-14 12:40:57 +00:00
stephan eaebd7fb6a Extend API_ARMOR checks on sqlite3_commit/rollback_hook() to include a check for the callback pointer.
FossilOrigin-Name: f6cd88e6b234560f729ff00da86144a0121ad96d5a07d227c9ffa3f43c22f72d
2023-10-14 12:20:55 +00:00
stephan 2ba1acce81 Add missing JNI bindings for sqlite3_db_readonly() and sqlite3_db_name(). Code-adjacent tweaks for the API_ARMOR audit.
FossilOrigin-Name: e5eace747ae656f1e05a346e53a2827242e57107567d2c3470e292cf0b40858d
2023-10-14 12:19:59 +00:00
drh c84a0b2195 Bug fix in sqlite3_analyzer: for databases larger than 1GiB, take into
account the lock-byte page when calculating the number of freelist pages.

FossilOrigin-Name: 26a909cdd32afee0f15968ca6f611eb259373b2a2195d0b3d47cfba103f1e6d6
2023-10-14 10:54:37 +00:00
drh 9132b8816a Earlier detection of a host of errors in CREATE TABLE, such the CREATE TABLE
statement itself fails, rather than generating an error on the first attempted
use of the created table.

FossilOrigin-Name: 348fa7aaf7958b3fb689ed023d946064ae8d92718a497a346e95114a2410cbf5
2023-10-13 22:19:23 +00:00
drh cc67a62fa0 Apply the correct affinity to DEFAULT values that are TRUE or FALSE.
FossilOrigin-Name: 4958db70c8103e249748fdeffc35f1c385e2e550e8a48697b82fcd797f3016be
2023-10-13 19:41:20 +00:00
drh 6f12e51313 Do not allow triggers on shadow tables under defensive mode.
FossilOrigin-Name: 44b932ff16e35814febd842d11215cf243393f173aade33ceded473598221e88
2023-10-13 18:29:18 +00:00
dan d7cdfe74f6 Fix an fts5 problem caused by a 'rebuild' followed by a DELETE in secure-delete mode.
FossilOrigin-Name: 18be505c628d9b13431ca6cfe822d4aeae119c53ae08aef26a67f310a8bd7bd3
2023-10-13 15:59:11 +00:00
drh af527231c1 Immediately fail a CREATE TABLE statement that attempts to create a
table that has a generated column loop.  Legacy allows the table to be
created but the table would not be usable for anything.

FossilOrigin-Name: 3237bf964117c1ef71143042837ef21872bb3d04bfd682075672e768953ec802
2023-10-13 13:49:46 +00:00
drh 9817065340 Actually prevent PRAGMA writable_schema=ON from being set in defensive mode,
rather than just preventing it from functioning.

FossilOrigin-Name: 2b574d9e7e922d0961ea27bfc0148d3098157ff8d4feefba469489c81d43f3e0
2023-10-13 12:57:23 +00:00
stephan 7dc0cc45f3 Round one of an audit for SQLITE_ENABLE_API_ARMOR for functions exposed by JNI and those functions missing armor, as [forum:5e3fc453a69b49ca|reported in several forum posts].
FossilOrigin-Name: 8c25c4b18ad07861bf0e47f99f3db04b569b9b859ad0690602f748ddf3576939
2023-10-13 12:48:35 +00:00
larrybr 99d43979d3 Avoid potential overflow in hex(). [forum:/forumpost/7ac0c9c5ea|See forum post 7ac0c9c5ea.]
FossilOrigin-Name: 2e968114fd0e60eebcc088dec02684e10b06ac1ec42d6bedb5287362f5cbf032
2023-10-13 01:39:47 +00:00
drh 5dc4a405c5 Make sure virtual tables have been connected before trying to invoke
the xIntegrity method during PRAGMA integrity_check.

FossilOrigin-Name: 4a4eccb681741917c6771101d4e329f91eae9b035f4d345218e522bb55fff6da
2023-10-12 20:51:17 +00:00
dan 3046039309 Fix a problem with an fts5 secure-delete on a rowid/term pair that follows a legacy delete of the same pair.
FossilOrigin-Name: 579aea0c28e01a79620ac758edc02db3a01baaa073e7773b8f0b6f610479520b
2023-10-12 19:46:58 +00:00
drh 4b136ed70a Enable SQLITE_VTAB_INNOCUOUS for FTS3, FTS5, and RTREE.
FossilOrigin-Name: f34c533b6c1f8ef3f69c75c1039406c12751cdde1fbdcb4d9776b24455facf8a
2023-10-12 18:46:27 +00:00
dan a11499a156 Add test case for [29937081].
FossilOrigin-Name: d3b983edf7164d30ddb8b4b745a3feff9737149d85b1c428d6ad488ac6a1960a
2023-10-12 18:09:27 +00:00
drh 5d9a6c6734 Make sure the journal file is closed when transitioning into MEMORY journal
mode, to avoid an assertion fault in the new sqlite3_randomness() avoidance
code added by [c84e4483cb44f827].

FossilOrigin-Name: 29937081a986d88f495ad48748c35ff5829f0ac31dd4ad3e48d180ae2fcb9a0c
2023-10-12 17:41:18 +00:00
larrybr 35d6c24fd5 CLI to check for rare prepare failures. [forum:/forumpost/5be6395182|See forum post 5be6395182.]
FossilOrigin-Name: 286d1e8012a5f5d9e1fe6dce1a732f725b8a1837f66daf6aefb792e19e96f2d4
2023-10-12 15:12:38 +00:00
stephan 6a0f297da9 Unconditionally force use of SQLITE_ENABLE_API_ARMOR in the WASM and JNI builds. Their corresponding test suites still work.
FossilOrigin-Name: c11394b50d0687b6d6abad9c80d5cd3cdce77e9b9c278f40601c460f463744a1
2023-10-11 20:37:42 +00:00
drh ac4aea5102 Fix the use of an uninitialized value that occurs when doing a json_insert()
of a string value that contains embedded U+0000 characters.

FossilOrigin-Name: d3c0cbb90966316be9cd25e3edb501da42731e8a83c13227b90ce17d3975a2c3
2023-10-11 17:24:31 +00:00
stephan 582d65cce3 Add (prepare, step, reset, finalize) parts of the JNI level-2 stmt wrapper and associated tests.
FossilOrigin-Name: a7082f186f2b9b6666bbc65f2eadeb74d91fa0a681e3b2468b261ffd322bd249
2023-10-11 13:52:05 +00:00
drh 58c7b770de Simplification to sqlite3ApiExit(). Generates identical machine code, but
easier for humans to read.

FossilOrigin-Name: 793bbfa5af9721bc3a61e8e5eda46dfce2f5ff3f223a7564c9e1b09f11e53cb3
2023-10-11 13:34:18 +00:00
drh 853fb5e723 Fix a bug in the mkautoconfamal.sh script, introduced by check-in [3308fdda4b81c110]
so that the "snapshot-tarball" and "amalgamation-tarball" makefile targets
work again when run from outside of the source tree.

FossilOrigin-Name: 65ccf5fef812d43aed9e00af36c90e1a499d197e30148753790445e25ee1324c
2023-10-10 15:33:05 +00:00
stephan 0b29d37ef9 Start adding tests for the second JNI layer.
FossilOrigin-Name: 0177f4dfe8a5c03c1d5dd279c6babe5a87b596c2351373f845b89fc6abf7947b
2023-10-10 12:16:04 +00:00
drh aaf1341dcd Use snprintf() in place of sprintf() in a debugging function in the CLI.
FossilOrigin-Name: 5e5e4d2b4958c329578d1e5af4ed25c1b774f0ed2d15fa6747d7d7fbbba4fa46
2023-10-09 22:45:10 +00:00
dan 6c96bf2c3f Avoid calling sqlite3_randomness and taking the PRNG mutex when writing a journal header in "journal_mode=memory" mode.
FossilOrigin-Name: c84e4483cb44f827416d8caafa22f076b2f31b2024fe8c5b5bcb0c9955149d11
2023-10-09 17:54:34 +00:00
drh 01a5a19919 Add a TCL script that does various verification checks on the source tree
to make sure that generated code has been updated correctly.

FossilOrigin-Name: 1f1a358af77f4386f98010eeae8487e6d39548a6dfe58c2664552490e7661122
2023-10-09 14:56:15 +00:00
drh 7e60f6dbf1 Fix a compiler warning caused by the previous check-in.
FossilOrigin-Name: 7e8768bf8b4002b1c287f2bc95262548e2ae81b437936154f2bb1ea1f739a904
2023-10-09 14:47:25 +00:00
drh 50da20d5b1 Turn on SEH by default when building using MSVC.
FossilOrigin-Name: f57e8275cf1ab3ad8027044cda0893282d1c403a5aa9fabbb1c7de280597b5cd
2023-10-09 14:05:21 +00:00
drh faef28e6bd Fix a potential UAF caused by JSON parser cache spill.
[forum:/forumpost/b25edc1d46|Forum post b25edc1d46].

FossilOrigin-Name: a163fecca90cab9d1b7bf8ebac78d498775eed7b6d81e7920e3401633c3a4b60
2023-10-09 12:46:53 +00:00
stephan 66dacae4c3 Flesh out the error state captured by SqliteException.java. Doc additions.
FossilOrigin-Name: 5c5397ff15543f4b3620244d9e57e15708eafcab1d42c9f87b4a60f0c01e8858
2023-10-09 12:45:28 +00:00
drh 4a11142072 Fix harmless compiler warning in the test module for sessions.
FossilOrigin-Name: 3d5ec99397c183cfb8c5d31e1967e0df8a4d6edbea92bbdf728571e3a6a36d68
2023-10-09 12:39:59 +00:00
stephan c8f829cdac Add JNI Sqlite and SqliteException classes. Add Tester2.java as the main test app for the high-level API.
FossilOrigin-Name: 6acf52be7abce8dcf434c5ebf0d5e88859b033e6418077846247ecf00ccf9381
2023-10-09 11:46:32 +00:00
stephan fd013682d6 Minor JNI doc and public/private cleanups.
FossilOrigin-Name: c49d36ece283274963ce2e5a4db1e8f586dffa22e47f4adb93c625f918c3fd5d
2023-10-09 10:44:10 +00:00
stephan 68585ea342 Rename SQLite3Jni to CApi to (A) reduce name prefix collisions with incoming classes and (B) align with its counterpart in the JS build. Remove the Canonical annotation because (A) the new code separation will inherently make that distinction and (B) the line between truly canonical and semi-canonical (e.g. differing in signature overloads) is blurry enough that consistent use of that annocation is becoming an unnecessary burden.
FossilOrigin-Name: ca216b4486aff7d206ebfc3a5e84d48919c282425d4313396bc19313ffca8a0e
2023-10-09 10:30:54 +00:00
dan 3b381b4afc Fix a problem with sqlite3changegroup_schema() and patchsets.
FossilOrigin-Name: c4ab4200dc2538e1132d87d70fd309b26b0da8a918fede1cb09b567ea40ec889
2023-10-07 17:29:10 +00:00
dan 4bdcfc6666 Add the sqlite3changegroup_schema() API. To allow changegroup objects to upgrade the schema of a changeset/patchset under some circumstances.
FossilOrigin-Name: 03f9e50b15c9c2fb4636dee24c1491c45322578b9fed08c6f4360be07c5c3830
2023-10-07 16:32:02 +00:00
dan 18efe17553 Fix a problem with the changes on this branch and tables that use an implicit primary key.
FossilOrigin-Name: 1f9d10235ff32e72074be695049567135b02e681068ce117b66adc14a6214449
2023-10-07 16:07:02 +00:00
drh 8f0634549c Add a test case to prevent a regression of the leak fixed in the previous
trunk check-in.

FossilOrigin-Name: f99ff655d09763c4a22d065041644ece793d84c82c644931e89ccf50c4f4564a
2023-10-07 08:15:47 +00:00
drh 43e232dc47 Fix a resource leak: sqlite3_blob_reopen() was failing to invoke the
xDel() distructor on a register when it sets that register to the new
rowid.  This was never a problem prior to the large-column cache of
[771fe35074b50b8d] as that destructor was always a no-op.
dbsqlfuzz cd96368deaece480fb94d42427dde053737a650e.

FossilOrigin-Name: 545edd5ecf20a7feb99dc5ce6f338fa1f172c2e17b240cbede497f0729dbd3d7
2023-10-07 08:00:54 +00:00
dan 83e43dc757 Add missing source code comments and fix other issues with the new code on this branch.
FossilOrigin-Name: df39fbe9ab87937beb77af353cd55602290b185e222e0a92a3ebf9a9a0b9e2e7
2023-10-06 20:39:42 +00:00
dan 9fc208aada Update this branch with latest changes from the trunk.
FossilOrigin-Name: 897e926a5eaa0eab7e09e5003e4c15c974897be298a18ae85345962dc3f410a1
2023-10-06 19:46:19 +00:00
dan 53e91a5e22 Add tests for the sqlite3changegroup_schema() API.
FossilOrigin-Name: 5dab481c101b1523b1cfde92678cbc654ea26d946bf29da372d71c1f89cbaf46
2023-10-06 19:01:43 +00:00
drh 00eee7a786 Remove an unused parameter from the recomputeColumnsNotIndexed() routine in
order to squash a harmless compiler warning.

FossilOrigin-Name: 9bf4bfd68080367b58594e0d44b110b3ee9766420f648537fd7bc638dacefb72
2023-10-06 12:55:53 +00:00
drh 1935887a68 Ensure that all fields of static sqlite3_module objects are explicitly
initialized, in order to hush-up nuisance compiler warnings.

FossilOrigin-Name: f3b3d712d6e58b1cb8fdebd2b6b3125080b6b3ac8c7c849a8cc1e5e778d62fe7
2023-10-06 12:51:05 +00:00
drh 03f7e42538 Remove and unused static variable from shell.c.in in order to silence a
harmless compiler warning.

FossilOrigin-Name: 477577120b897bf15edc2a85e220d5c64a1d5a245354939269f6753d8140ac95
2023-10-06 12:22:14 +00:00
drh 568643fd15 Increase the size of some variables associated with the PMA sorter in order
to avoid any possibility of a signed integer overflow.
[https://bugs.chromium.org/p/chromium/issues/detail?id=1489025|Chromium fuzzer issue 1489025].

FossilOrigin-Name: dc3be3af471d90dd810c1e2cc59e83cf6a57f01971b1258ea0da402e4d577ef2
2023-10-06 12:15:01 +00:00
dan 16381d062a Add the sqlite3changegroup_schema() API. To allow changegroups to handle differences in schema created by ALTER TABLE ADD COLUMN.
FossilOrigin-Name: 309deee2dd8dd07623fce79f6bb62d5279d140dd0be3b34bc42af20b0507726b
2023-10-05 19:09:23 +00:00
drh 5d9446d100 Improved detection of corrupt databases in balance_nonroot().
dbsqlfuzz d1cf013c50a620b68c2a5bd240d29afd65e5f58e.

FossilOrigin-Name: a2464bbb825b5976ef974a2e6c17ea150f5e6fcd0dd0f144b9f9c1c22a9c9c82
2023-10-05 11:06:10 +00:00
stephan a357bdbfc4 Do not squelch exceptions from SAHPool VFS importDb() - rethrow them. Problem reported in [forum:c80fc578809b80a3|forum post c80fc578809b80a3].
FossilOrigin-Name: 325bcdea4a436e514d83095d3893caf07a714b718e1b643507179b101c108e3b
2023-10-05 11:04:49 +00:00
drh 3a3912f21c Remove a stray blank line from the beginning of btree.c. No functional
code changes.

FossilOrigin-Name: c89983dcfc208faa86b31717144c069572598e9770228264d69a044a84bde277
2023-10-05 10:35:51 +00:00
dan 6d8e91be9d Allow a session object to generate a changeset, even if columns were added to one of the tables using ALTER TABLE ADD COLUMN while the changeset was being collected.
FossilOrigin-Name: a3f435eccf3a2aa11cb7420e94af5efcdfa04e9c169c5aaf61fa5cdcb165ceef
2023-10-04 21:15:24 +00:00
drh 10e751937c The Makefile distinguishes between tcl8.4 and tcl8.5. Some makefile targets
require tcl8.5, but others (ex: "sqlite3.c", "shell.c", and "sqlite3") require
only tcl8.4.

FossilOrigin-Name: 770308db9776b8c0a70b8807463e89a9eddfe5552e25e67324cd303dc974f50e
2023-10-04 12:49:08 +00:00
drh 57996f53e4 Modify the main makefile so that it fails with an error if the tclsh found
by ./configure is not tclsh 8.5 or later.
[forum:/forumpost/4380363682708ece|Forum post 4380363682708ece].

FossilOrigin-Name: 1765f3b5a00a8ca20a7b9e18ac7f9d7de0679470b234b83aea83aa5b4d4d34e6
2023-10-04 11:52:44 +00:00
stephan 4f65a3cec9 JNI: when fetching text-or-blob/length pairs, fetch the text-or-blob first, then the length, as the API docs suggest.
FossilOrigin-Name: e4f9d3946fa570bccea2de17c5536901bdabd46cfe25678fdd37ba0e2bfd99b1
2023-10-02 17:12:16 +00:00
drh 51e3f7a1c3 Fix the length of the string in the new concat() and concat_ws() functions.
FossilOrigin-Name: 325243226520376f4a142864ae3fb98866f25bff4dc50a6b648ef98428a7169f
2023-10-02 17:06:28 +00:00
drh 43d71eb892 Remove an ALWAYS() from a branch that can in fact be false following an OOM
fault.

FossilOrigin-Name: 3a1feb1c469d3dd50e2558581199b16bfd59b9d5bcb0de5f1a8f0ec30a17b4d1
2023-10-02 15:56:37 +00:00
stephan 041fb16631 Pointer-casting-related fixes for JNI on 32-bit ARM (pi4 armv7l).
FossilOrigin-Name: 54441039ae3ebe39e42f008f77ce1be117cbda6daeed8b01739ad42a3c1f0754
2023-10-02 14:44:28 +00:00
stephan dc13d74140 Add JNI binding for sqlite3_normalized_sql().
FossilOrigin-Name: 2532e5662a2bbb3e6b4c4be3fad035734b684bdf59b178cb64b444ca6d8b956b
2023-10-01 12:15:38 +00:00
stephan ec15e551f9 Make JNI binding of sqlite3_value_frombind() return boolean instead of int and add tests for it.
FossilOrigin-Name: 906e2ed3e9647f5c2355e9fea2beee141767cae7016da9fa1fe88283726b9369
2023-10-01 11:53:40 +00:00
stephan f046b82324 Fix JNI binding to compile without SQLITE_ENABLE_PREUPDATE_HOOK. Add build option to disable all optional ENABLE flags.
FossilOrigin-Name: c04022b7407f77eaf0175e831ebcd6bbdc0af1cef0d42c5c11102aa8484f24ca
2023-09-30 17:08:29 +00:00
stephan d1878bed86 Make all SQLite3Jni methods which take a void-pointer-as-long-int argument explicitly package-private, as they're always for internal use only even if SQLite3Jni is eventually made public.
FossilOrigin-Name: 5e387275f69ab2d3159b4b67b8cbfc6270410b61e5ac1f988616e8d051f6572e
2023-09-30 16:19:57 +00:00
stephan 41c6232e54 JNI: rename sqlite3_errmsg16() to sqlite3_errmsg(), as the 16 suffix is superfluous there.
FossilOrigin-Name: df25b21689de84b7b8c940df39461d3d88f592f0ba2dddcfcbb0bebcea6ee7da
2023-09-30 14:48:21 +00:00
stephan 0018ef15a3 Expose sqlite3_stmt_busy() to JNI.
FossilOrigin-Name: 5733aa5fe341d50b84baebd8803a86e381bb95959a8bc733b8fff6fd4e732867
2023-09-30 14:34:55 +00:00
stephan 8f43696c8a Correct an undefined-value deref in the JS bindings, triggered by passing invalid args and then tripping over an undefined value while trying to report that. Reported in [forum:dd42df144651d779|forum post dd42df144651d779].
FossilOrigin-Name: fd4632540717c4771ac9ae1fe4400091a2cd0553639bf809abaf4ecbc54de4f0
2023-09-30 13:13:23 +00:00
stephan 9ac7e2d0fc Make (almost) all SQLite3Jni.sqlite3_... members public but make the SQLite3Jni class package-private, which has the same foot-protection effect but makes it much simpler to patch SQLite3Jni to publicize the interface later. Remove some of the extraneous foot-shooting protection from indidivual functions, in lieu moving it to the still-pending higher-level interfaces.
FossilOrigin-Name: 082b86683e77b2d4a30e1515552f52511324ef97c43b622b4dab7975ae9fbd7d
2023-09-30 10:59:18 +00:00
stephan 008b62f1ae Merge in and close jni-ptr-passing branch. The remaining pointer-passing adaptation can be done incrementally without affecting the interface.
FossilOrigin-Name: 7927c42410ba0d5fc8e6e08caaa5cbaaeb359869f17d1afaa082f1aa47fdb2fc
2023-09-30 10:37:36 +00:00
stephan d1ce2755f0 Make all native JNI sqlite3_...() bindings package-private as a foot-shooting protective measure (higher-level pre-native-call argument validation is largely pending). Move SQLTester.java and TesterFts5.java into the org.sqlite.jni package so that they can access the being-tested methods.
FossilOrigin-Name: ec82f7251acab7df40755ef5f456f36fe49b59e63a20be59bd610fc4280ba8cd
2023-09-30 10:31:56 +00:00
stephan ebce46f02f More work towards the new pointer-passing mechanism in JNI, and code-adjacent cleanups.
FossilOrigin-Name: 6c63987e893357dc8b10decaa96c30fb37b75481640a303e77a0d8224354491e
2023-09-30 09:41:58 +00:00
dan bee5657042 Add test case to ensure the assert() removed by [f090af77] no longer fails.
FossilOrigin-Name: 615ab71761754b072439f92e73fdb98c916b820937bcca8904f4be34936d5f9c
2023-09-29 15:56:40 +00:00
drh 8be48440b6 Remove an incorrect assert().
FossilOrigin-Name: f090af775f6d2ce8b50850f5c9e33546bcb9cff5467d99ca210530cb7ca83d5b
2023-09-29 15:53:55 +00:00
stephan 83f62816f8 JNI: correct a NullPointerException triggered via SQLTester.
FossilOrigin-Name: 0a873de76c0cbcd8e2eda3f0508e427f1dcb32b01798687c0545acfe10102179
2023-09-28 20:34:28 +00:00
larrybr d93cfa2831 Clarify serialization API doc w.r.t. buffer validity and usage, instigated by [forum:/forumpost/8922e2ad2ad41205|forum post 8922e2ad2ad41205]. No code changes.
FossilOrigin-Name: b488b9fb71652eca90d2bf73d32f3d748badf517859dc833c58e021b0e017194
2023-09-28 15:00:25 +00:00
stephan 0dabcd139d Adapted the JNI bindings for the sqlite3_value_...() and (pre)update hook APIs to the new pointer-passing mechanism.
FossilOrigin-Name: 4182f0275d5d65e04a130eeef4d44642a5ffeeb4b84430d240ea2605345f1404
2023-09-28 11:19:37 +00:00
stephan 5d1448d08a Convert the remaining macro-generated JNI bindings to the new pointer-passing mechanism.
FossilOrigin-Name: 250fd6ae806cf705c0f29ad30ad8fb885b12590848e7adae63bc21d874c6d3bd
2023-09-28 10:50:26 +00:00
stephan 50b2a41330 Convert the macro-generated JNI bindings which take a db pointer to the new pointer-passing mechanism.
FossilOrigin-Name: 5f47fb77db4ee77afc541e680559ad88e66ba7fd04b830e70f8be92cf8d0a60c
2023-09-28 10:27:01 +00:00
stephan 1d74432fef Adapted JNI sqlite3_blob_...() and sqlite3_close...() to the new pointer-passing mechanism.
FossilOrigin-Name: 0b22c8ef93e5ccd45316099fb8575e27620158b1992c0c70fe0348cfc10147f8
2023-09-27 14:41:49 +00:00
stephan a917ee7056 Adapt JNI sqlite3_bind_...() bindings to the new pointer-passing method and correct the mapping of sqlite3_bind_zeroblob64() to use zeroblob64() instead of zeroblob(). Related internal API renaming.
FossilOrigin-Name: 980d559fff6e55d1e2ef39f242a8a09313a936cfb141807db475bcceff924869
2023-09-27 11:01:32 +00:00
stephan 9afd67cce7 Add missing license headers to JNI annotation classes and extend the definition of @NotNull to include 0L as the equivalent of null to account for the current rework of how pointers are passed from Java to C.
FossilOrigin-Name: bccdfeb9efde20410bde545893fb98ce5c0c4d04a8e7797b868ba8994120e1db
2023-09-27 10:29:07 +00:00
stephan 4632f91403 Rework the JNI sqlite3_backup APIs to the new pointer-passing approach.
FossilOrigin-Name: 48aeb1e2cdeb4aec85c4f63a1f809215cd4b473791169e72b5ddf6d6bdc2f7b7
2023-09-27 09:58:36 +00:00
stephan 36f60e6c1a Start reworking JNI methods such that they pass void pointers from Java to C instead of passing their strongly-typed wrappers, as that is reportedly significantly faster than passing the wrapper objects to C and extracting the pointers there. There are still many, many functions left to rework for this.
FossilOrigin-Name: 66c814dd473731703ee00e1ff610acfdccf09d1b87dd2355795ed697d4ed5d3e
2023-09-26 21:37:52 +00:00
drh 61df55d0be Fix harmless compiler warnings in the testfixture testing utility.
FossilOrigin-Name: 5a39a02d2dfd9ed6171cd0bd434b2bc268d0ed8ead6e1a396d1603266d9493ef
2023-09-26 21:22:46 +00:00
stephan e81d229fb7 JNI: implement AutoCloseable for the sqlite3 and sqlite3_stmt classes, and adjust a few tests to use it. Override Object.finalize() for sqlite3 class but not sqlite3_stmt (where it triggers a JVM crash for as-yet-unknown reasons).
FossilOrigin-Name: b633d8bbdbd43464ee03058c273e5188117b9538ed5d5148a1a3851e4adf6410
2023-09-26 19:49:35 +00:00
drh 0e224d93ea Fix yet another assert() in the partial-index constant value optimization.
FossilOrigin-Name: f459d0806cf044fd07743e4c91d0a5a6ddf45b3b41004bde4278f190d99a4cf5
2023-09-26 19:38:24 +00:00
dan 1c67749166 Remove a NEVER() from a condition in the partial-index constant value optimization that can sometimes be true.
FossilOrigin-Name: 63aea8c3e0605edcf49ad1e5ce1a56d2690dcb8c4857043997545daab46ca035
2023-09-26 19:26:47 +00:00
drh a6e7202677 Remove unreachable code from the partial-index constant value optimization
([8d4160910d651246]).

FossilOrigin-Name: 7ad38254c37153efa72291d09800693ca60894359548eda877d59defa8c70d49
2023-09-26 14:57:30 +00:00
stephan 6f737371c0 Remove some outdated JNI docs.
FossilOrigin-Name: 2ca5cbbb2a8ee94fe9f4888dea695a4429aefa17611fd9ebe59bf4866290ab0c
2023-09-26 09:46:04 +00:00
stephan efa8369214 Minor JNI doc updates. No code changes.
FossilOrigin-Name: 63687cc659f06a1f6aa9d4496ef01d9f0adbee6184b8e265fb8feec1ad30449b
2023-09-26 07:43:51 +00:00
dan 47b427423d In partial index scans, if the WHERE clause implies a constant value for a table column, replace occurences of that table column with the constant. This increases the likelihood of the partial index being a covering index.
FossilOrigin-Name: 8d4160910d6512469cb0a060b89c2509dde1c8e33d9358034d7fb39cd494eabe
2023-09-25 17:46:02 +00:00
dan 1b96f9167f Another very minor tweak to code on this branch to save cycles.
FossilOrigin-Name: 2c805dc68112db7f06aea56a731a33e046e876851bdda3e725ba96f6da1bf2ee
2023-09-25 17:21:53 +00:00
dan cf0906575d Change the order of an if..else... in new code to make it slightly faster.
FossilOrigin-Name: f80dc64483a54a488e507da140836261ae1b864577cc95d8e869919d5a25e73c
2023-09-25 15:25:49 +00:00
drh cc11526e22 Enable -Wall for the compiler on --enable-debug builds.
FossilOrigin-Name: 63a7b521390001939909d43d908af78f7df7cb92272d2b74f038da918c2bef05
2023-09-25 14:20:07 +00:00
dan 873849b81e Fix a bad interaction between LEFT JOIN, bloom filters and partial indexes.
FossilOrigin-Name: 5775f92b31f0539c66d37e50689bec17a82c0e857a781331c03e34bc18157087
2023-09-25 11:12:39 +00:00
stephan a2ead23868 Resolve the JNI FTS5 test5() failure and remove some nearby dead code.
FossilOrigin-Name: 5655c51f103c6dfade799ffb5ca2eb21b4360ada598741f373c952ae732535aa
2023-09-24 19:13:31 +00:00
dan af7cce98c5 Add extra tests for changes on this branch.
FossilOrigin-Name: cb45f5b070cfdbb4358e8e909b0b34d658b5925db06e282b38046298a159c545
2023-09-23 21:31:40 +00:00
dan 7a9bbfe1fa Avoid an error when parsing a schema that contains indexes with WHERE clauses containing unknown collation sequences.
FossilOrigin-Name: d5dd39e37ab994ce5d75688a40c6494c17807431529e85eb2b11d58f367fe232
2023-09-23 19:25:03 +00:00
dan c18c7523a4 Fix a failing assert() caused by changes on this branch.
FossilOrigin-Name: d0e21f20bd8643d21c4b88fb75b83939d68e9b7118f7289a164656f11d2ee555
2023-09-23 18:52:22 +00:00
drh 9f811a35b8 Fix a harmless compiler warning.
FossilOrigin-Name: 7c4210253b660b385d14de3ae7ab30f038036308e1164ec17b40e2805b9b2235
2023-09-23 18:49:30 +00:00
dan 2d3c5385bf Add extra tests for the fts5 xPhraseQuery API.
FossilOrigin-Name: b5edea16317ccc896394011f45442af44d5ea1041b6abfe152c90412c78bfc5b
2023-09-23 15:34:02 +00:00
stephan 6c248201b1 Resolve the JNI FTS5 test5() crash, caused by two unrelated typos in JNI callback signature strings. That test now fails with an expected-vs-got mismatch but no longer dies in the native code.
FossilOrigin-Name: 0233f310bf6bbb84f76b25205af160951e0bf6d8b2f1bca4537384af4fe47e58
2023-09-23 06:50:19 +00:00
stephan 8a67a46340 Remove an invalid optimization from JNI code and add a missing check check for Java-side exception.
FossilOrigin-Name: b8151f879718f7e30520f7e5ae04bd6724ae98af067be097b1a00df2175a038a
2023-09-23 06:37:18 +00:00
dan bd42642431 In partial index scans, if the WHERE clause implies a constant value for a table column, replace occurences of that table column with the constant. This increases the likelihood of the partial index being a covering index.
FossilOrigin-Name: 66ed7abdfa228abde2052e3988589371f0e49b11582b1b4a83255d2df3a0aefa
2023-09-22 20:21:27 +00:00
dan 8aaf63c6ac Add extra tests for java Fts5ExtensionApi API.
FossilOrigin-Name: f9d62b853ce8bfbfdc9f137e984e7a1b51d70e88c38b136b4fad1e8ae6ee8913
2023-09-18 20:42:06 +00:00
dan 876f09b560 Extra tests for java Fts5ExtensionApi interface.
FossilOrigin-Name: c923893f3604b278277de1bb919ef713bf7a4296b7ff71451cfe19bc2ff03190
2023-09-18 18:12:03 +00:00
drh 38119058aa Remove a NEVER() in btree.c that could actually be reached, with the approriate
database corruption.

FossilOrigin-Name: d1fd00e0875f881cacdc581a75215ec1872a81969ac07b8ebe3aaa707a9a3640
2023-09-17 16:36:22 +00:00
dan f99a27fab9 Fix resolution of "rowid" and similar identifiers in queries that use nested joins.
FossilOrigin-Name: 37ec43d92bde13efc71fa57ff5e59c4a95b9054c298f844aefeb06d4a91ad0d4
2023-09-16 18:18:57 +00:00
dan 2599705105 Enable "OR IGNORE" support via SQLITE_VTAB_CONSTRAINT_SUPPORT for internal-content (not contentless or external-content) fts5 tables.
FossilOrigin-Name: c362bde4f4b8489947f080154d7fddcfd6e8e21d742a483c496fb7fbe59969d2
2023-09-16 17:11:44 +00:00
drh a91fe45339 Add a NEVER() to an unreachable branch.
FossilOrigin-Name: 6b6eb38979d68c06e382620c8813d6b67a3de02c4a7a029c84f924b9a2e380c6
2023-09-16 16:39:27 +00:00
drh 5bd96b2446 Fix minor code indentation inconsistencies. No function changes.
FossilOrigin-Name: 05107a0ae1456b746d3119af68d39532fad23a7eef88c09a9ef46ab7f8da4b9d
2023-09-16 16:23:20 +00:00
drh 5ddf0cbe25 Fix a harmless compiler warning.
FossilOrigin-Name: 9e9ff4ca3a205d0b599634782a5ad389fbe4823663550f48c54ca35dbc660dc7
2023-09-16 16:12:10 +00:00
dan 2e8edc1d45 Further tests and assert() statements for the change on this branch.
FossilOrigin-Name: 1c202d540ac362bfc747a9f8472e83c9d7614e38467f8b48787a669fb34664ba
2023-09-16 14:42:18 +00:00
dan 63702bccaa Fix resolution of unqualified "rowid" identifiers in queries with nested joins.
FossilOrigin-Name: bbcbd3d537d6790373d97f59386b8ce7fa2177db572af0f1babe058a76e25cc3
2023-09-15 20:57:05 +00:00
drh 124a6aa7d5 Simplifications and performance optimizations for the RTree extension.
FossilOrigin-Name: 04a333f5faf6b90592f1f69889ac6c28949955e186f39037cd639480b06feae8
2023-09-15 20:28:27 +00:00
drh ef2056e533 Drop support for the view-scan optimization (check-in [609fbb94b8f01d67]) as
it was causing multiple performance regressions.  In its place, reduce the
estimated row count for DISTINCT subsqueries by a factor of 8.

FossilOrigin-Name: f911f1c4977fbcae041243955cf2b98d8cc8baa337885a69be0f2b9bd2efa6f3
2023-09-15 19:51:18 +00:00
dan 81b70d97eb Allow expressions like "<tbl>.rowid" to refer to implicit rowid columns of tables in nested FROM clauses.
FossilOrigin-Name: 59a1bbc69f5dbb33418fa4b383393fb13a46bc1e531577da8ad54ae2fad5a10e
2023-09-15 18:36:51 +00:00
drh 647e3ed2ba Reduce the number of calls to sqlite3_mprintf() made by RTree.
FossilOrigin-Name: f158b7d4917e0951fbb86a6f438abcb618d8602566fa54bf04c05a37f3a73513
2023-09-14 01:46:57 +00:00
drh 7de8ae22f7 Omit the Reinsert algorithm from RTree. This causes most benchmarks to run
faster, at the expense of having a slightly less dense and hence larger index
(example: 33 entries/node versus 34 entries/node).

FossilOrigin-Name: b3049a1d3dbdd63c471499c2f6b417655defe9ad90228e7cc722f5be877aae01
2023-09-13 17:30:12 +00:00
drh f9967cb302 Performance optimization to cellContains() in RTREE.
FossilOrigin-Name: 43cde22bf3f36687df231eddf642581d1d4f4102ad8568d31f5b2ff0302ca800
2023-09-13 14:07:07 +00:00
drh cdae00b3e8 In the ChooseLeaf algorithm of RTREE, do an initial pass through the cells
of a node looking for solutions that involve no cell enlargement.  Only look
for the minimum cell enlargement if the enlargement is non-zero for all cells.
This results in a performance improvement by reducing the number of calls
to cellUnion().

FossilOrigin-Name: 59f0e239d19393190911ea3bd89b5a24be0d85d64ccf51906efc9537ad5d7298
2023-09-13 13:12:08 +00:00
drh 386e359044 Reduce the number of calls to cellArea() in the ChooseLeaf() algorithm of
rtree.

FossilOrigin-Name: c633fe6be0edc07a0aa1ac819d58b6c629b7c95b47b63516ae1b0a937f6a2b89
2023-09-13 12:06:02 +00:00
drh f4ae37ca44 Remove unused variable from the ChooseLeaf algorithm in r-tree.
FossilOrigin-Name: fb2e4a8067edc29ba64973820f265b21dfee0d32f4d675d32100d5eef93c2830
2023-09-13 11:36:44 +00:00
224 changed files with 8848 additions and 3947 deletions
+37 -21
View File
@@ -766,13 +766,22 @@ mptest: mptester$(TEXE)
$(MPTEST2) --journalmode DELETE
has_tclsh84:
sh $(TOP)/tool/cktclsh.sh 8.4 $(TCLSH_CMD)
touch has_tclsh84
has_tclsh85:
sh $(TOP)/tool/cktclsh.sh 8.5 $(TCLSH_CMD)
touch has_tclsh85
# This target creates a directory named "tsrc" and fills it with
# copies of all of the C source code and header files needed to
# build on the target system. Some of the C source code and header
# files are automatically generated. This target takes care of
# all that automatic generation.
#
.target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl fts5.c
.target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl has_tclsh84 fts5.c
rm -rf tsrc
mkdir tsrc
cp -f $(SRC) tsrc
@@ -782,15 +791,15 @@ mptest: mptester$(TEXE)
cp fts5.c fts5.h tsrc
touch .target_source
sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl src-verify
sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl src-verify has_tclsh84
$(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl $(AMALGAMATION_LINE_MACROS)
cp tsrc/sqlite3ext.h .
cp $(TOP)/ext/session/sqlite3session.h .
sqlite3r.h: sqlite3.h
sqlite3r.h: sqlite3.h has_tclsh84
$(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) --enable-recover >sqlite3r.h
sqlite3r.c: sqlite3.c sqlite3r.h
sqlite3r.c: sqlite3.c sqlite3r.h has_tclsh84
cp $(TOP)/ext/recover/sqlite3recover.c tsrc/
cp $(TOP)/ext/recover/sqlite3recover.h tsrc/
cp $(TOP)/ext/recover/dbdata.c tsrc/
@@ -805,7 +814,7 @@ tclsqlite3.c: sqlite3.c
echo '#endif /* USE_SYSTEM_SQLITE */' >>tclsqlite3.c
cat $(TOP)/src/tclsqlite.c >>tclsqlite3.c
sqlite3-all.c: sqlite3.c $(TOP)/tool/split-sqlite3c.tcl
sqlite3-all.c: sqlite3.c $(TOP)/tool/split-sqlite3c.tcl has_tclsh84
$(TCLSH_CMD) $(TOP)/tool/split-sqlite3c.tcl
# Rule to build the amalgamation
@@ -1093,10 +1102,10 @@ tclsqlite3$(TEXE): tclsqlite-shell.lo libsqlite3.la
# Rules to build opcodes.c and opcodes.h
#
opcodes.c: opcodes.h $(TOP)/tool/mkopcodec.tcl
opcodes.c: opcodes.h $(TOP)/tool/mkopcodec.tcl has_tclsh84
$(TCLSH_CMD) $(TOP)/tool/mkopcodec.tcl opcodes.h >opcodes.c
opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/tool/mkopcodeh.tcl
opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/tool/mkopcodeh.tcl has_tclsh84
cat parse.h $(TOP)/src/vdbe.c | $(TCLSH_CMD) $(TOP)/tool/mkopcodeh.tcl >opcodes.h
# Rules to build parse.c and parse.h - the outputs of lemon.
@@ -1107,10 +1116,10 @@ parse.c: $(TOP)/src/parse.y lemon$(BEXE)
cp $(TOP)/src/parse.y .
./lemon$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) -S parse.y
sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest mksourceid$(BEXE) $(TOP)/VERSION
sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest mksourceid$(BEXE) $(TOP)/VERSION has_tclsh84
$(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h
sqlite3rc.h: $(TOP)/src/sqlite3.rc $(TOP)/VERSION
sqlite3rc.h: $(TOP)/src/sqlite3.rc $(TOP)/VERSION has_tclsh84
echo '#ifndef SQLITE_RESOURCE_VERSION' >$@
echo -n '#define SQLITE_RESOURCE_VERSION ' >>$@
cat $(TOP)/VERSION | $(TCLSH_CMD) $(TOP)/tool/replace.tcl exact . , >>$@
@@ -1146,7 +1155,7 @@ SHELL_SRC = \
$(TOP)/ext/recover/sqlite3recover.h \
$(TOP)/src/test_windirent.c
shell.c: $(SHELL_SRC) $(TOP)/tool/mkshellc.tcl
shell.c: $(SHELL_SRC) $(TOP)/tool/mkshellc.tcl has_tclsh84
$(TCLSH_CMD) $(TOP)/tool/mkshellc.tcl >shell.c
@@ -1234,7 +1243,7 @@ fts5parse.c: $(TOP)/ext/fts5/fts5parse.y lemon$(BEXE)
fts5parse.h: fts5parse.c
fts5.c: $(FTS5_SRC)
fts5.c: $(FTS5_SRC) has_tclsh84
$(TCLSH_CMD) $(TOP)/ext/fts5/tool/mkfts5c.tcl
cp $(TOP)/ext/fts5/fts5.h .
@@ -1268,7 +1277,7 @@ TESTFIXTURE_SRC1 = sqlite3.c
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)/src/tclsqlite.c
TESTFIXTURE_SRC += $(TESTFIXTURE_SRC$(USE_AMALGAMATION))
testfixture$(TEXE): $(TESTFIXTURE_SRC)
testfixture$(TEXE): has_tclsh85 $(TESTFIXTURE_SRC)
$(LTLINK) -DSQLITE_NO_SYNC=1 $(TEMP_STORE) $(TESTFIXTURE_FLAGS) \
-o $@ $(TESTFIXTURE_SRC) $(LIBTCL) $(TLIBS)
@@ -1319,17 +1328,23 @@ testrunner: testfixture$(TEXE)
# Runs both fuzztest and testrunner, consecutively.
#
devtest: testfixture$(TEXE) fuzztest testrunner
devtest: srctree-check testfixture$(TEXE) fuzztest testrunner
mdevtest:
mdevtest: srctree-check has_tclsh85
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl mdevtest
sdevtest:
sdevtest: has_tclsh85
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl sdevtest
# Validate that various generated files in the source tree
# are up-to-date.
#
srctree-check: $(TOP)/tool/srctree-check.tcl
$(TCLSH_CMD) $(TOP)/tool/srctree-check.tcl
# Testing for a release
#
releasetest: testfixture$(TEXE)
releasetest: srctree-check testfixture$(TEXE)
./testfixture$(TEXE) $(TOP)/test/testrunner.tcl release
# Minimal testing that runs in less than 3 minutes
@@ -1340,7 +1355,7 @@ quicktest: ./testfixture$(TEXE)
# This is the common case. Run many tests that do not take too long,
# including fuzzcheck, sqlite3_analyzer, and sqldiff tests.
#
test: fuzztest sourcetest $(TESTPROGS) tcltest
test: srctree-check fuzztest sourcetest $(TESTPROGS) tcltest
# Run a test using valgrind. This can take a really long time
# because valgrind is so much slower than a native machine.
@@ -1358,13 +1373,13 @@ smoketest: $(TESTPROGS) fuzzcheck$(TEXE)
shelltest: $(TESTPROGS)
./testfixture$(TEXT) $(TOP)/test/permutations.test shell
sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in
sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in has_tclsh85
$(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in >sqlite3_analyzer.c
sqlite3_analyzer$(TEXE): sqlite3_analyzer.c
$(LTLINK) sqlite3_analyzer.c -o $@ $(LIBTCL) $(TLIBS)
sqltclsh.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/sqltclsh.tcl $(TOP)/ext/misc/appendvfs.c $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in
sqltclsh.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/sqltclsh.tcl $(TOP)/ext/misc/appendvfs.c $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in has_tclsh85
$(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in >sqltclsh.c
sqltclsh$(TEXE): sqltclsh.c
@@ -1383,7 +1398,7 @@ CHECKER_DEPS =\
$(TOP)/ext/misc/btreeinfo.c \
$(TOP)/ext/repair/sqlite3_checker.c.in
sqlite3_checker.c: $(CHECKER_DEPS)
sqlite3_checker.c: $(CHECKER_DEPS) has_tclsh85
$(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/ext/repair/sqlite3_checker.c.in >$@
sqlite3_checker$(TEXE): sqlite3_checker.c
@@ -1549,6 +1564,7 @@ clean:
rm -f threadtest5
rm -f src-verify
rm -f custom.rws
rm -f has_tclsh84 has_tclsh85
distclean: clean
rm -f sqlite_cfg.h config.log config.status libtool Makefile sqlite3.pc \
@@ -1593,7 +1609,7 @@ fiddle: sqlite3.c shell.c
@echo 'Updating custom dictionary from tool/custom.txt'
aspell --lang=en create master ./custom.rws < $<
misspell: ./custom.rws
misspell: ./custom.rws has_tclsh84
$(TCLSH_CMD) ./tool/spellsift.tcl ./src/*.c ./src/*.h ./src/*.in
#
+6 -5
View File
@@ -52,8 +52,8 @@ MINIMAL_AMALGAMATION = 0
USE_STDCALL = 0
!ENDIF
# Set this non-0 to use structured exception handling (SEH) for WAL mode
# in the core library.
# Use the USE_SEH=0 option on the nmake command line to omit structured
# exception handling (SEH) support. SEH is on by default.
#
!IFNDEF USE_SEH
USE_SEH = 1
@@ -403,10 +403,11 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RBU=1
!ENDIF
# Should structured exception handling (SEH) be enabled for WAL mode in
# the core library?
# the core library? It is on by default. Only omit it if the
# USE_SEH=0 option is provided on the nmake command-line.
#
!IF $(USE_SEH)!=0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_USE_SEH=1
!IF $(USE_SEH)==0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_OMIT_SEH=1
!ENDIF
# These are the "extended" SQLite compilation options used when compiling for
+6 -5
View File
@@ -52,8 +52,8 @@ MINIMAL_AMALGAMATION = 0
USE_STDCALL = 0
!ENDIF
# Set this non-0 to use structured exception handling (SEH) for WAL mode
# in the core library.
# Use the USE_SEH=0 option on the nmake command line to omit structured
# exception handling (SEH) support. SEH is on by default.
#
!IFNDEF USE_SEH
USE_SEH = 1
@@ -325,10 +325,11 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RBU=1
!ENDIF
# Should structured exception handling (SEH) be enabled for WAL mode in
# the core library?
# the core library? It is on by default. Only omit it if the
# USE_SEH=0 option is provided on the nmake command-line.
#
!IF $(USE_SEH)!=0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_USE_SEH=1
!IF $(USE_SEH)==0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_OMIT_SEH=1
!ENDIF
# These are the "extended" SQLite compilation options used when compiling for
Vendored
+1 -1
View File
@@ -11345,7 +11345,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build type" >&5
$as_echo_n "checking build type... " >&6; }
if test "${enable_debug}" = "yes" ; then
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0"
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0 -Wall"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: debug" >&5
$as_echo "debug" >&6; }
else
+1 -1
View File
@@ -632,7 +632,7 @@ AC_SEARCH_LIBS(fdatasync, [rt])
AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug],[enable debugging & verbose explain]))
AC_MSG_CHECKING([build type])
if test "${enable_debug}" = "yes" ; then
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0"
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0 -Wall"
AC_MSG_RESULT([debug])
else
TARGET_DEBUG="-DNDEBUG"
+1
View File
@@ -662,6 +662,7 @@ static int idxRegisterVtab(sqlite3expert *p){
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0, /* xIntegrity */
};
return sqlite3_create_module(p->dbv, "expert", &expertModule, (void*)p);
+1
View File
@@ -640,6 +640,7 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
zLanguageid = (p->zLanguageid ? p->zLanguageid : "__langid");
sqlite3_vtab_config(p->db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
sqlite3_vtab_config(p->db, SQLITE_VTAB_INNOCUOUS);
/* Create a list of user columns for the virtual table */
zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]);
+2 -1
View File
@@ -545,7 +545,8 @@ int sqlite3Fts3InitAux(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
int rc; /* Return code */
+2 -1
View File
@@ -362,7 +362,8 @@ int sqlite3Fts3InitTerm(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
int rc; /* Return code */
+2 -1
View File
@@ -445,7 +445,8 @@ int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash, void(*xDestroy)(void*)){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
int rc; /* Return code */
+8 -4
View File
@@ -4034,9 +4034,13 @@ static int fts3IncrmergeAppend(
nSpace += sqlite3Fts3VarintLen(nDoclist) + nDoclist;
/* If the current block is not empty, and if adding this term/doclist
** to the current block would make it larger than Fts3Table.nNodeSize
** bytes, write this block out to the database. */
if( pLeaf->block.n>0 && (pLeaf->block.n + nSpace)>p->nNodeSize ){
** to the current block would make it larger than Fts3Table.nNodeSize bytes,
** and if there is still room for another leaf page, write this block out to
** the database. */
if( pLeaf->block.n>0
&& (pLeaf->block.n + nSpace)>p->nNodeSize
&& pLeaf->iBlock < (pWriter->iStart + pWriter->nLeafEst)
){
rc = fts3WriteSegment(p, pLeaf->iBlock, pLeaf->block.a, pLeaf->block.n);
pWriter->nWork++;
@@ -4368,7 +4372,7 @@ static int fts3IncrmergeLoad(
rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock,0);
blobGrowBuffer(&pNode->block,
MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc
);
);
if( rc==SQLITE_OK ){
memcpy(pNode->block.a, aBlock, nBlock);
pNode->block.n = nBlock;
+56 -34
View File
@@ -2904,7 +2904,6 @@ static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){
assert_nc( i2!=0 );
pRes->bTermEq = 1;
if( p1->iRowid==p2->iRowid ){
p1->bDel = p2->bDel;
return i2;
}
res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : +1;
@@ -5132,34 +5131,39 @@ static void fts5DoSecureDelete(
/* Set variable bLastInDoclist to true if this entry happens to be
** the last rowid in the doclist for its term. */
if( iNextOff>=iPgIdx ){
int pgno = pSeg->iLeafPgno+1;
fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist);
iNextOff = iPgIdx;
}else{
/* Loop through the page-footer. If iNextOff (offset of the
** entry following the one we are removing) is equal to the
** offset of a key on this page, then the entry is the last
** in its doclist. */
int iKeyOff = 0;
for(iIdx=0; iIdx<nIdx; /* no-op */){
u32 iVal = 0;
iIdx += fts5GetVarint32(&aIdx[iIdx], iVal);
iKeyOff += iVal;
if( iKeyOff==iNextOff ){
bLastInDoclist = 1;
if( pSeg->bDel==0 ){
if( iNextOff>=iPgIdx ){
int pgno = pSeg->iLeafPgno+1;
fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist);
iNextOff = iPgIdx;
}else{
/* Loop through the page-footer. If iNextOff (offset of the
** entry following the one we are removing) is equal to the
** offset of a key on this page, then the entry is the last
** in its doclist. */
int iKeyOff = 0;
for(iIdx=0; iIdx<nIdx; /* no-op */){
u32 iVal = 0;
iIdx += fts5GetVarint32(&aIdx[iIdx], iVal);
iKeyOff += iVal;
if( iKeyOff==iNextOff ){
bLastInDoclist = 1;
}
}
}
/* If this is (a) the first rowid on a page and (b) is not followed by
** another position list on the same page, set the "first-rowid" field
** of the header to 0. */
if( fts5GetU16(&aPg[0])==iStart && (bLastInDoclist || iNextOff==iPgIdx) ){
fts5PutU16(&aPg[0], 0);
}
}
/* If this is (a) the first rowid on a page and (b) is not followed by
** another position list on the same page, set the "first-rowid" field
** of the header to 0. */
if( fts5GetU16(&aPg[0])==iStart && (bLastInDoclist || iNextOff==iPgIdx) ){
fts5PutU16(&aPg[0], 0);
}
if( bLastInDoclist==0 ){
if( pSeg->bDel ){
iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta);
aPg[iOff++] = 0x01;
}else if( bLastInDoclist==0 ){
if( iNextOff!=iPgIdx ){
u64 iNextDelta = 0;
iNextOff += fts5GetVarint(&aPg[iNextOff], &iNextDelta);
@@ -5271,6 +5275,15 @@ static void fts5DoSecureDelete(
}
}
/* Assuming no error has occurred, this block does final edits to the
** leaf page before writing it back to disk. Input variables are:
**
** nPg: Total initial size of leaf page.
** iPgIdx: Initial offset of page footer.
**
** iOff: Offset to move data to
** iNextOff: Offset to move data from
*/
if( p->rc==SQLITE_OK ){
const int nMove = nPg - iNextOff; /* Number of bytes to move */
int nShift = iNextOff - iOff; /* Distance to move them */
@@ -5471,10 +5484,16 @@ static void fts5FlushOneHash(Fts5Index *p){
fts5WriteFlushLeaf(p, &writer);
}
}else{
int bDummy;
int nPos;
int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy);
nCopy += nPos;
int bDel = 0;
int nPos = 0;
int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDel);
if( bDel && bSecureDelete ){
fts5BufferAppendVarint(&p->rc, pBuf, nPos*2);
iOff += nCopy;
nCopy = nPos;
}else{
nCopy += nPos;
}
if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){
/* The entire poslist will fit on the current leaf. So copy
** it in one go. */
@@ -5512,7 +5531,6 @@ static void fts5FlushOneHash(Fts5Index *p){
assert( pBuf->n<=pBuf->nSpace );
if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash);
}
sqlite3Fts5HashClear(pHash);
fts5WriteFinish(p, &writer, &pgnoLast);
assert( p->rc!=SQLITE_OK || bSecureDelete || pgnoLast>0 );
@@ -5545,7 +5563,6 @@ static void fts5FlushOneHash(Fts5Index *p){
fts5IndexCrisismerge(p, &pStruct);
fts5StructureWrite(p, pStruct);
fts5StructureRelease(pStruct);
p->nContentlessDelete = 0;
}
/*
@@ -5556,8 +5573,12 @@ static void fts5IndexFlush(Fts5Index *p){
if( p->nPendingData || p->nContentlessDelete ){
assert( p->pHash );
fts5FlushOneHash(p);
p->nPendingData = 0;
p->nPendingRow = 0;
if( p->rc==SQLITE_OK ){
sqlite3Fts5HashClear(p->pHash);
p->nPendingData = 0;
p->nPendingRow = 0;
p->nContentlessDelete = 0;
}
}
}
@@ -8299,7 +8320,8 @@ int sqlite3Fts5IndexInit(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
rc = sqlite3_create_module(db, "fts5_structure", &fts5structure_module, 0);
}
+66 -8
View File
@@ -117,6 +117,8 @@ struct Fts5FullTable {
Fts5Storage *pStorage; /* Document store */
Fts5Global *pGlobal; /* Global (connection wide) data */
Fts5Cursor *pSortCsr; /* Sort data from this cursor */
int iSavepoint; /* Successful xSavepoint()+1 */
int bInSavepoint;
#ifdef SQLITE_DEBUG
struct Fts5TransactionState ts;
#endif
@@ -405,6 +407,13 @@ static int fts5InitVtab(
pConfig->pzErrmsg = 0;
}
if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
rc = sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, (int)1);
}
if( rc==SQLITE_OK ){
rc = sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
}
if( rc!=SQLITE_OK ){
fts5FreeVtab(pTab);
pTab = 0;
@@ -1526,6 +1535,7 @@ static int fts5SpecialInsert(
Fts5Config *pConfig = pTab->p.pConfig;
int rc = SQLITE_OK;
int bError = 0;
int bLoadConfig = 0;
if( 0==sqlite3_stricmp("delete-all", zCmd) ){
if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
@@ -1537,6 +1547,7 @@ static int fts5SpecialInsert(
}else{
rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage);
}
bLoadConfig = 1;
}else if( 0==sqlite3_stricmp("rebuild", zCmd) ){
if( pConfig->eContent==FTS5_CONTENT_NONE ){
fts5SetVtabError(pTab,
@@ -1546,6 +1557,7 @@ static int fts5SpecialInsert(
}else{
rc = sqlite3Fts5StorageRebuild(pTab->pStorage);
}
bLoadConfig = 1;
}else if( 0==sqlite3_stricmp("optimize", zCmd) ){
rc = sqlite3Fts5StorageOptimize(pTab->pStorage);
}else if( 0==sqlite3_stricmp("merge", zCmd) ){
@@ -1558,6 +1570,8 @@ static int fts5SpecialInsert(
}else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){
pConfig->bPrefixIndex = sqlite3_value_int(pVal);
#endif
}else if( 0==sqlite3_stricmp("flush", zCmd) ){
rc = sqlite3Fts5FlushToDisk(&pTab->p);
}else{
rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
if( rc==SQLITE_OK ){
@@ -1571,6 +1585,12 @@ static int fts5SpecialInsert(
}
}
}
if( rc==SQLITE_OK && bLoadConfig ){
pTab->p.pConfig->iCookie--;
rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
}
return rc;
}
@@ -1689,7 +1709,7 @@ static int fts5UpdateMethod(
assert( nArg!=1 || eType0==SQLITE_INTEGER );
/* Filter out attempts to run UPDATE or DELETE on contentless tables.
** This is not suported. Except - DELETE is supported if the CREATE
** This is not suported. Except - they are both supported if the CREATE
** VIRTUAL TABLE statement contained "contentless_delete=1". */
if( eType0==SQLITE_INTEGER
&& pConfig->eContent==FTS5_CONTENT_NONE
@@ -1718,7 +1738,8 @@ static int fts5UpdateMethod(
}
else if( eType0!=SQLITE_INTEGER ){
/* If this is a REPLACE, first remove the current entry (if any) */
/* An INSERT statement. If the conflict-mode is REPLACE, first remove
** the current entry (if any). */
if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){
i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0);
@@ -2592,8 +2613,12 @@ static int fts5RenameMethod(
sqlite3_vtab *pVtab, /* Virtual table handle */
const char *zName /* New name of table */
){
int rc;
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
return sqlite3Fts5StorageRename(pTab->pStorage, zName);
pTab->bInSavepoint = 1;
rc = sqlite3Fts5StorageRename(pTab->pStorage, zName);
pTab->bInSavepoint = 0;
return rc;
}
int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
@@ -2607,9 +2632,30 @@ int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
** Flush the contents of the pending-terms table to disk.
*/
static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
int rc = SQLITE_OK;
char *zSql = 0;
UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_SAVEPOINT, iSavepoint);
return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint);
if( pTab->bInSavepoint==0 ){
zSql = sqlite3_mprintf("INSERT INTO %Q.%Q(%Q) VALUES('flush')",
pTab->p.pConfig->zDb, pTab->p.pConfig->zName, pTab->p.pConfig->zName
);
if( zSql ){
pTab->bInSavepoint = 1;
rc = sqlite3_exec(pTab->p.pConfig->db, zSql, 0, 0, 0);
pTab->bInSavepoint = 0;
sqlite3_free(zSql);
}else{
rc = SQLITE_NOMEM;
}
if( rc==SQLITE_OK ){
pTab->iSavepoint = iSavepoint+1;
}
}
return rc;
}
/*
@@ -2618,9 +2664,17 @@ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
** This is a no-op.
*/
static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
int rc = SQLITE_OK;
UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_RELEASE, iSavepoint);
return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint);
if( (iSavepoint+1)<pTab->iSavepoint ){
rc = sqlite3Fts5FlushToDisk(&pTab->p);
if( rc==SQLITE_OK ){
pTab->iSavepoint = iSavepoint;
}
}
return rc;
}
/*
@@ -2630,11 +2684,15 @@ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
*/
static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
int rc = SQLITE_OK;
UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
fts5TripCursors(pTab);
pTab->p.pConfig->pgsz = 0;
return sqlite3Fts5StorageRollback(pTab->pStorage);
if( (iSavepoint+1)<=pTab->iSavepoint ){
rc = sqlite3Fts5StorageRollback(pTab->pStorage);
}
return rc;
}
/*
+3 -1
View File
@@ -1184,7 +1184,9 @@ int sqlite3Fts5StorageSync(Fts5Storage *p){
i64 iLastRowid = sqlite3_last_insert_rowid(p->pConfig->db);
if( p->bTotalsValid ){
rc = fts5StorageSaveTotals(p);
p->bTotalsValid = 0;
if( rc==SQLITE_OK ){
p->bTotalsValid = 0;
}
}
if( rc==SQLITE_OK ){
rc = sqlite3Fts5IndexSync(p->pIndex);
+2 -1
View File
@@ -472,7 +472,8 @@ int sqlite3Fts5TestRegisterTok(sqlite3 *db, fts5_api *pApi){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
int rc; /* Return code */
+2 -1
View File
@@ -783,7 +783,8 @@ int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0
/* xShadowName */ 0,
/* xIntegrity */ 0
};
void *p = (void*)pGlobal;
+27
View File
@@ -307,4 +307,31 @@ do_catchsql_test 10.1.4 {
SELECT group_concat(firstcol(t1), '.') FROM t1 GROUP BY rowid
} {1 {unable to use function firstcol in the requested context}}
#-------------------------------------------------------------------------
# Test that xInstCount() works from within an xPhraseQuery() callback.
#
reset_db
proc xCallback {cmd} {
incr ::hitcount [$cmd xInstCount]
return SQLITE_OK
}
proc fts5_hitcount {cmd} {
set ::hitcount 0
$cmd xQueryPhrase 0 xCallback
return $::hitcount
}
sqlite3_fts5_create_function db fts5_hitcount fts5_hitcount
do_execsql_test 11.1 {
CREATE VIRTUAL TABLE x1 USING fts5(z);
INSERT INTO x1 VALUES('one two three');
INSERT INTO x1 VALUES('one two one three one');
INSERT INTO x1 VALUES('one two three');
}
do_execsql_test 11.2 {
SELECT fts5_hitcount(x1) FROM x1('one') LIMIT 1;
} {5}
finish_test
+40
View File
@@ -65,4 +65,44 @@ do_execsql_test 2.1 {
INSERT INTO fts_idx(fts_idx) VALUES('integrity-check');
}
#-------------------------------------------------------------------------
# Tests for OR IGNORE conflict handling.
#
reset_db
foreach_detail_mode $::testprefix {
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE t1 USING fts5(xyz, detail=%DETAIL%);
BEGIN;
INSERT INTO t1(rowid, xyz) VALUES(13, 'thirteen documents');
INSERT INTO t1(rowid, xyz) VALUES(14, 'fourteen documents');
INSERT INTO t1(rowid, xyz) VALUES(15, 'fifteen documents');
COMMIT;
}
set db_cksum [cksum]
foreach {tn sql} {
1 {
INSERT OR IGNORE INTO t1(rowid, xyz) VALUES(14, 'new text');
}
2 {
UPDATE OR IGNORE t1 SET rowid=13 WHERE rowid=15;
}
3 {
INSERT OR IGNORE INTO t1(rowid, xyz)
SELECT 13, 'some text'
UNION ALL
SELECT 14, 'some text'
UNION ALL
SELECT 15, 'some text'
}
} {
do_execsql_test 3.1.$tn.1 $sql
do_test 3.1.$tn.2 { cksum } $db_cksum
}
}
finish_test
+50
View File
@@ -0,0 +1,50 @@
# 2010 June 15
#
# 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.
#
#***********************************************************************
#
source [file join [file dirname [info script]] fts5_common.tcl]
source $testdir/malloc_common.tcl
set testprefix fts5faultG
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
set ::testprefix fts5faultG
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t1 USING fts5(a);
INSERT INTO t1 VALUES('test renaming the table');
INSERT INTO t1 VALUES(' after it has been written');
INSERT INTO t1 VALUES(' actually other stuff instead');
}
faultsim_save_and_close
do_faultsim_test 1 -faults oom-t* -prep {
faultsim_restore_and_reopen
execsql {
BEGIN;
DELETE FROM t1 WHERE rowid=2;
}
} -body {
execsql {
DELETE FROM t1;
}
} -test {
catchsql { COMMIT }
faultsim_integrity_check
faultsim_test_result {0 {}}
}
finish_test
+49 -3
View File
@@ -18,7 +18,7 @@ db progress 1 progress_handler
set ::PHC 0
proc progress_handler {args} {
incr ::PHC
if {($::PHC % 100000)==0} breakpoint
# if {($::PHC % 100000)==0} breakpoint
return 0
}
@@ -73,12 +73,12 @@ do_execsql_test 2.2 {
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE t1 USING fts5(x);
INSERT INTO t1(t1, rank) VALUES('secure-delete', $sd)
}
do_execsql_test 2.1 {
do_execsql_test 3.1 {
BEGIN;
INSERT INTO t1(rowid, x)
VALUES(51869, 'when whenever where weress what turn'),
@@ -91,5 +91,51 @@ do_execsql_test 3.2 {
INSERT INTO t1(t1) VALUES('integrity-check');
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE t1 USING fts5(x);
INSERT INTO t1(rowid, x) VALUES(10, 'one two');
}
do_execsql_test 4.1 {
UPDATE t1 SET x = 'one three' WHERE rowid=10;
INSERT INTO t1(t1, rank) VALUES('secure-delete', 1);
}
do_execsql_test 4.2 {
DELETE FROM t1 WHERE rowid=10;
}
do_execsql_test 4.3 {
INSERT INTO t1(t1) VALUES('integrity-check');
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 5.0 {
CREATE VIRTUAL TABLE t1 USING fts5(content);
INSERT INTO t1(t1,rank) VALUES('secure-delete',1);
INSERT INTO t1 VALUES('active'),('boomer'),('atom'),('atomic'),
('alpha channel backup abandon test aback boomer atom alpha active');
DELETE FROM t1 WHERE t1 MATCH 'abandon';
}
do_execsql_test 5.1 {
INSERT INTO t1(t1) VALUES('rebuild');
}
do_execsql_test 5.2 {
DELETE FROM t1 WHERE rowid NOTNULL<5;
}
db close
sqlite3 db test.db
do_execsql_test 5.3 {
PRAGMA integrity_check;
} {ok}
finish_test
+77 -51
View File
@@ -26,8 +26,9 @@ dir.src.c := $(dir.src)/c
dir.bld := $(dir.jni)/bld
dir.bld.c := $(dir.bld)
dir.src.jni := $(dir.src)/org/sqlite/jni
dir.src.jni.tester := $(dir.src.jni)/tester
dir.src.capi := $(dir.src.jni)/capi
dir.src.fts5 := $(dir.src.jni)/fts5
dir.tests := $(dir.src)/tests
mkdir ?= mkdir -p
$(dir.bld.c):
$(mkdir) $@
@@ -45,9 +46,9 @@ DISTCLEAN_FILES := $(dir.jni)/*~ $(dir.src.c)/*~ $(dir.src.jni)/*~
sqlite3-jni.h := $(dir.src.c)/sqlite3-jni.h
.NOTPARALLEL: $(sqlite3-jni.h)
SQLite3Jni.java := src/org/sqlite/jni/SQLite3Jni.java
SQLTester.java := src/org/sqlite/jni/tester/SQLTester.java
SQLite3Jni.class := $(SQLite3Jni.java:.java=.class)
CApi.java := $(dir.src.capi)/CApi.java
SQLTester.java := $(dir.src.capi)/SQLTester.java
CApi.class := $(CApi.java:.java=.class)
SQLTester.class := $(SQLTester.java:.java=.class)
########################################################################
@@ -61,8 +62,12 @@ SQLTester.class := $(SQLTester.java:.java=.class)
# which the fts5 APIs have been stripped unless that feature is
# intended to be stripped for good.
enable.fts5 ?= 1
# If enable.tester is 0, the org/sqlite/jni/tester/* bits are elided.
enable.tester ?= $(if $(wildcard $(dir.src.jni.tester)/SQLTester.java),1,0)
ifeq (,$(wildcard $(dir.tests)/*))
enable.tester := 0
else
enable.tester := 1
endif
# bin.version-info = binary to output various sqlite3 version info
# building the distribution zip file.
@@ -73,10 +78,10 @@ $(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile
# Be explicit about which Java files to compile so that we can work on
# in-progress files without requiring them to be in a compilable statae.
JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\
annotation/Canonical.java \
annotation/NotNull.java \
annotation/Nullable.java \
JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/annotation/%,\
NotNull.java \
Nullable.java \
) $(patsubst %,$(dir.src.capi)/%,\
AbstractCollationCallback.java \
AggregateFunction.java \
AuthorizerCallback.java \
@@ -97,21 +102,34 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\
ScalarFunction.java \
SQLFunction.java \
CallbackProxy.java \
SQLite3Jni.java \
CApi.java \
TableColumnMetadata.java \
TraceV2Callback.java \
UpdateHookCallback.java \
ValueHolder.java \
WindowFunction.java \
XDestroyCallback.java \
sqlite3.java \
sqlite3_context.java \
sqlite3_stmt.java \
sqlite3_value.java \
) $(patsubst %,$(dir.src.jni)/wrapper1/%,\
AggregateFunction.java \
ScalarFunction.java \
SqlFunction.java \
Sqlite.java \
SqliteException.java \
ValueHolder.java \
)
JAVA_FILES.unittest := $(patsubst %,$(dir.src.jni)/%,\
Tester1.java \
capi/Tester1.java \
wrapper1/Tester2.java \
)
ifeq (1,$(enable.fts5))
JAVA_FILES.unittest += $(patsubst %,$(dir.src.fts5)/%,\
TesterFts5.java \
)
JAVA_FILES.main += $(patsubst %,$(dir.src.fts5)/%,\
fts5_api.java \
fts5_extension_function.java \
@@ -121,11 +139,10 @@ ifeq (1,$(enable.fts5))
Fts5ExtensionApi.java \
Fts5PhraseIter.java \
Fts5Tokenizer.java \
TesterFts5.java \
XTokenizeCallback.java \
)
endif
JAVA_FILES.tester := $(dir.src.jni.tester)/SQLTester.java
JAVA_FILES.tester := $(SQLTester.java)
JAVA_FILES.package.info := \
$(dir.src.jni)/package-info.java \
$(dir.src.jni)/annotation/package-info.java
@@ -188,7 +205,19 @@ opt.fatal-oom ?= 1
opt.debug ?= 1
opt.metrics ?= 1
SQLITE_OPT = \
-DSQLITE_ENABLE_RTREE \
-DSQLITE_THREADSAFE=$(opt.threadsafe) \
-DSQLITE_TEMP_STORE=2 \
-DSQLITE_USE_URI=1 \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_OMIT_DEPRECATED \
-DSQLITE_OMIT_SHARED_CACHE \
-DSQLITE_C=$(sqlite3.c) \
-DSQLITE_JNI_FATAL_OOM=$(opt.fatal-oom) \
-DSQLITE_JNI_ENABLE_METRICS=$(opt.metrics)
opt.extras ?= 1
ifeq (1,$(opt.extras))
SQLITE_OPT += -DSQLITE_ENABLE_RTREE \
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
-DSQLITE_ENABLE_STMTVTAB \
-DSQLITE_ENABLE_DBPAGE_VTAB \
@@ -196,21 +225,14 @@ SQLITE_OPT = \
-DSQLITE_ENABLE_BYTECODE_VTAB \
-DSQLITE_ENABLE_OFFSET_SQL_FUNC \
-DSQLITE_ENABLE_PREUPDATE_HOOK \
-DSQLITE_ENABLE_SQLLOG \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_OMIT_DEPRECATED \
-DSQLITE_OMIT_SHARED_CACHE \
-DSQLITE_THREADSAFE=$(opt.threadsafe) \
-DSQLITE_TEMP_STORE=2 \
-DSQLITE_USE_URI=1 \
-DSQLITE_C=$(sqlite3.c) \
-DSQLITE_JNI_FATAL_OOM=$(opt.fatal-oom) \
-DSQLITE_JNI_ENABLE_METRICS=$(opt.metrics)
-DSQLITE_ENABLE_NORMALIZE \
-DSQLITE_ENABLE_SQLLOG
endif
ifeq (1,$(opt.debug))
SQLITE_OPT += -DSQLITE_DEBUG -g -DDEBUG -UNDEBUG
else
SQLITE_OPT += -O2
SQLITE_OPT += -Os
endif
ifeq (1,$(enable.fts5))
@@ -232,19 +254,18 @@ $$(dir.bld.c)/org_sqlite_jni$(3)_$(2).h: $(1)/$(2).java
endef
# Invoke ADD_JNI_H once for each Java file which includes JNI
# declarations:
$(eval $(call ADD_JNI_H,$(dir.src.jni),SQLite3Jni,))
$(eval $(call ADD_JNI_H,$(dir.src.capi),CApi,_capi))
$(eval $(call ADD_JNI_H,$(dir.src.capi),SQLTester,_capi))
ifeq (1,$(enable.fts5))
$(eval $(call ADD_JNI_H,$(dir.src.fts5),Fts5ExtensionApi,_fts5))
$(eval $(call ADD_JNI_H,$(dir.src.fts5),fts5_api,_fts5))
$(eval $(call ADD_JNI_H,$(dir.src.fts5),fts5_tokenizer,_fts5))
endif
ifeq (1,$(enable.tester))
$(eval $(call ADD_JNI_H,$(dir.src.jni.tester),SQLTester,_tester))
endif
$(sqlite3-jni.h.in): $(dir.bld.c)
#package.dll.cfiles :=
package.dll.cflags = \
-std=c99 \
-fPIC \
-I. \
-I$(dir $(sqlite3.h)) \
@@ -252,17 +273,18 @@ package.dll.cflags = \
-I$(JDK_HOME)/include \
$(patsubst %,-I%,$(patsubst %.h,,$(wildcard $(JDK_HOME)/include/*))) \
-Wall
# Using (-Wall -Wextra) triggers an untennable number of
# gcc warnings from sqlite3.c for mundane things like
# unused parameters.
#
# The gross $(patsubst...) above is to include the platform-specific
# subdir which lives under $(JDK_HOME)/include and is a required
# include path for client-level code.
#
# Using (-Wall -Wextra) triggers an untennable number of
# gcc warnings from sqlite3.c for mundane things like
# unused parameters.
########################################################################
ifeq (1,$(enable.tester))
package.dll.cflags += -DSQLITE_JNI_ENABLE_SQLTester
endif
$(sqlite3-jni.h): $(sqlite3-jni.h.in) $(MAKEFILE)
@cat $(sqlite3-jni.h.in) > $@.tmp
@if cmp $@ $@.tmp >/dev/null; then \
@@ -284,19 +306,23 @@ $(package.dll): $(sqlite3-jni.c) $(MAKEFILE)
all: $(package.dll)
.PHONY: test test-one
test.flags ?=
test.main.flags = -ea -Djava.library.path=$(dir.bld.c) \
$(java.flags) -cp $(classpath) \
org.sqlite.jni.Tester1
Tester1.flags ?=
Tester2.flags ?=
test.flags.jvm = -ea -Djava.library.path=$(dir.bld.c) \
$(java.flags) -cp $(classpath)
test.deps := $(CLASS_FILES) $(package.dll)
test-one: $(test.deps)
$(bin.java) $(test.main.flags) $(test.flags)
$(bin.java) $(test.flags.jvm) org.sqlite.jni.capi.Tester1 $(Tester1.flags)
$(bin.java) $(test.flags.jvm) org.sqlite.jni.wrapper1.Tester2 $(Tester2.flags)
test-sqllog: $(test.deps)
@echo "Testing with -sqllog..."
$(bin.java) $(test.main.flags) -sqllog
$(bin.java) $(test.flags.jvm) -sqllog
test-mt: $(test.deps)
@echo "Testing in multi-threaded mode:";
$(bin.java) $(test.main.flags) -t 7 -r 50 -shuffle $(test.flags)
$(bin.java) $(test.flags.jvm) org.sqlite.jni.capi.Tester1 \
-t 7 -r 50 -shuffle $(Tester1.flags)
$(bin.java) $(test.flags.jvm) org.sqlite.jni.wrapper1.Tester2 \
-t 7 -r 50 -shuffle $(Tester2.flags)
test: test-one test-mt
tests: test test-sqllog
@@ -308,21 +334,21 @@ ifeq (1,$(enable.tester))
tester-local: $(CLASS_FILES.tester) $(package.dll)
$(bin.java) -ea -Djava.library.path=$(dir.bld.c) \
$(java.flags) -cp $(classpath) \
org.sqlite.jni.tester.SQLTester $(tester.flags) $(tester.scripts)
org.sqlite.jni.capi.SQLTester $(tester.flags) $(tester.scripts)
tester: tester-local
else
tester:
@echo "SQLTester support is disabled. Build with enable.tester=1 to enable it."
@echo "SQLTester support is disabled."
endif
tester.extdir.default := src/tests/ext
tester.extdir.default := $(dir.tests)/ext
tester.extdir ?= $(tester.extdir.default)
tester.extern-scripts := $(wildcard $(tester.extdir)/*.test)
ifneq (,$(tester.extern-scripts))
tester-ext:
$(bin.java) -ea -Djava.library.path=$(dir.bld.c) \
$(java.flags) -cp $(classpath) \
org.sqlite.jni.tester.SQLTester $(tester.flags) $(tester.extern-scripts)
org.sqlite.jni.capi.SQLTester $(tester.flags) $(tester.extern-scripts)
else
tester-ext:
@echo "******************************************************"; \
@@ -362,13 +388,12 @@ JAVA_FILES.jar := $(JAVA_FILES.main) $(JAVA_FILES.unittest) $(JAVA_FILES.package
CLASS_FILES.jar := $(filter-out %/package-info.class,$(JAVA_FILES.jar:.java=.class))
$(package.jar.in): $(package.dll) $(MAKEFILE)
ls -1 \
$(dir.src.jni)/*.java $(dir.src.jni)/*.class \
$(dir.src.jni)/annotation/*.java $(dir.src.jni)/annotation/*.class \
$(dir.src.jni)/*/*.java $(dir.src.jni)/*/*.class \
| sed -e 's,^$(dir.src)/,,' | sort > $@
$(package.jar): $(CLASS_FILES.jar) $(MAKEFILE) $(package.jar.in)
@rm -f $(dir.src)/c/*~ $(dir.src.jni)/*~
cd $(dir.src); $(bin.jar) -cfe ../$@ org.sqlite.jni.Tester1 @$(package.jar.in)
cd $(dir.src); $(bin.jar) -cfe ../$@ org.sqlite.jni.capi.Tester1 @$(package.jar.in)
@ls -la $@
@echo "To use this jar you will need the -Djava.library.path=DIR/CONTAINING/libsqlite3-jni.so flag."
@echo "e.g. java -Djava.library.path=bld -jar $@"
@@ -381,11 +406,12 @@ run-jar: $(package.jar) $(package.dll)
# javadoc...
dir.doc := $(dir.jni)/javadoc
doc.index := $(dir.doc)/index.html
javadoc.exclude := -exclude org.sqlite.jni.tester \
-exclude org.sqlite.jni.fts5
javadoc.exclude := -exclude org.sqlite.jni.fts5
# ^^^^ 2023-09-13: elide the fts5 parts from the public docs for
# the time being, as it's not clear where the Java bindings for
# those bits are going.
# javadoc.exclude += -exclude org.sqlite.jni.capi
# ^^^^ exclude the capi API only for certain builds (TBD)
$(doc.index): $(JAVA_FILES.main) $(MAKEFILE)
@if [ -d $(dir.doc) ]; then rm -fr $(dir.doc)/*; fi
$(bin.javadoc) -cp $(classpath) -d $(dir.doc) -quiet \
@@ -406,7 +432,7 @@ docserve: $(doc.index)
# Clean up...
CLEAN_FILES += $(dir.bld.c)/* \
$(dir.src.jni)/*.class \
$(dir.src.jni.tester)/*.class \
$(dir.src.jni)/*/*.class \
$(package.dll) \
hs_err_pid*.log
+1 -1
View File
@@ -55,7 +55,7 @@ Hello World
```java
import org.sqlite.jni.*;
import static org.sqlite.jni.SQLite3Jni.*;
import static org.sqlite.jni.CApi.*;
...
File diff suppressed because it is too large Load Diff
+1430 -1402
View File
File diff suppressed because it is too large Load Diff
-155
View File
@@ -1,155 +0,0 @@
/*
** 2023-07-21
**
** 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 is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
/**
This enum contains all of the core and "extended" result codes used
by the sqlite3 library. It is provided not for use with the C-style
API (with which it won't work) but for higher-level code which may
find it useful to map SQLite result codes to human-readable names.
*/
public enum ResultCode {
SQLITE_OK(SQLite3Jni.SQLITE_OK),
SQLITE_ERROR(SQLite3Jni.SQLITE_ERROR),
SQLITE_INTERNAL(SQLite3Jni.SQLITE_INTERNAL),
SQLITE_PERM(SQLite3Jni.SQLITE_PERM),
SQLITE_ABORT(SQLite3Jni.SQLITE_ABORT),
SQLITE_BUSY(SQLite3Jni.SQLITE_BUSY),
SQLITE_LOCKED(SQLite3Jni.SQLITE_LOCKED),
SQLITE_NOMEM(SQLite3Jni.SQLITE_NOMEM),
SQLITE_READONLY(SQLite3Jni.SQLITE_READONLY),
SQLITE_INTERRUPT(SQLite3Jni.SQLITE_INTERRUPT),
SQLITE_IOERR(SQLite3Jni.SQLITE_IOERR),
SQLITE_CORRUPT(SQLite3Jni.SQLITE_CORRUPT),
SQLITE_NOTFOUND(SQLite3Jni.SQLITE_NOTFOUND),
SQLITE_FULL(SQLite3Jni.SQLITE_FULL),
SQLITE_CANTOPEN(SQLite3Jni.SQLITE_CANTOPEN),
SQLITE_PROTOCOL(SQLite3Jni.SQLITE_PROTOCOL),
SQLITE_EMPTY(SQLite3Jni.SQLITE_EMPTY),
SQLITE_SCHEMA(SQLite3Jni.SQLITE_SCHEMA),
SQLITE_TOOBIG(SQLite3Jni.SQLITE_TOOBIG),
SQLITE_CONSTRAINT(SQLite3Jni.SQLITE_CONSTRAINT),
SQLITE_MISMATCH(SQLite3Jni.SQLITE_MISMATCH),
SQLITE_MISUSE(SQLite3Jni.SQLITE_MISUSE),
SQLITE_NOLFS(SQLite3Jni.SQLITE_NOLFS),
SQLITE_AUTH(SQLite3Jni.SQLITE_AUTH),
SQLITE_FORMAT(SQLite3Jni.SQLITE_FORMAT),
SQLITE_RANGE(SQLite3Jni.SQLITE_RANGE),
SQLITE_NOTADB(SQLite3Jni.SQLITE_NOTADB),
SQLITE_NOTICE(SQLite3Jni.SQLITE_NOTICE),
SQLITE_WARNING(SQLite3Jni.SQLITE_WARNING),
SQLITE_ROW(SQLite3Jni.SQLITE_ROW),
SQLITE_DONE(SQLite3Jni.SQLITE_DONE),
SQLITE_ERROR_MISSING_COLLSEQ(SQLite3Jni.SQLITE_ERROR_MISSING_COLLSEQ),
SQLITE_ERROR_RETRY(SQLite3Jni.SQLITE_ERROR_RETRY),
SQLITE_ERROR_SNAPSHOT(SQLite3Jni.SQLITE_ERROR_SNAPSHOT),
SQLITE_IOERR_READ(SQLite3Jni.SQLITE_IOERR_READ),
SQLITE_IOERR_SHORT_READ(SQLite3Jni.SQLITE_IOERR_SHORT_READ),
SQLITE_IOERR_WRITE(SQLite3Jni.SQLITE_IOERR_WRITE),
SQLITE_IOERR_FSYNC(SQLite3Jni.SQLITE_IOERR_FSYNC),
SQLITE_IOERR_DIR_FSYNC(SQLite3Jni.SQLITE_IOERR_DIR_FSYNC),
SQLITE_IOERR_TRUNCATE(SQLite3Jni.SQLITE_IOERR_TRUNCATE),
SQLITE_IOERR_FSTAT(SQLite3Jni.SQLITE_IOERR_FSTAT),
SQLITE_IOERR_UNLOCK(SQLite3Jni.SQLITE_IOERR_UNLOCK),
SQLITE_IOERR_RDLOCK(SQLite3Jni.SQLITE_IOERR_RDLOCK),
SQLITE_IOERR_DELETE(SQLite3Jni.SQLITE_IOERR_DELETE),
SQLITE_IOERR_BLOCKED(SQLite3Jni.SQLITE_IOERR_BLOCKED),
SQLITE_IOERR_NOMEM(SQLite3Jni.SQLITE_IOERR_NOMEM),
SQLITE_IOERR_ACCESS(SQLite3Jni.SQLITE_IOERR_ACCESS),
SQLITE_IOERR_CHECKRESERVEDLOCK(SQLite3Jni.SQLITE_IOERR_CHECKRESERVEDLOCK),
SQLITE_IOERR_LOCK(SQLite3Jni.SQLITE_IOERR_LOCK),
SQLITE_IOERR_CLOSE(SQLite3Jni.SQLITE_IOERR_CLOSE),
SQLITE_IOERR_DIR_CLOSE(SQLite3Jni.SQLITE_IOERR_DIR_CLOSE),
SQLITE_IOERR_SHMOPEN(SQLite3Jni.SQLITE_IOERR_SHMOPEN),
SQLITE_IOERR_SHMSIZE(SQLite3Jni.SQLITE_IOERR_SHMSIZE),
SQLITE_IOERR_SHMLOCK(SQLite3Jni.SQLITE_IOERR_SHMLOCK),
SQLITE_IOERR_SHMMAP(SQLite3Jni.SQLITE_IOERR_SHMMAP),
SQLITE_IOERR_SEEK(SQLite3Jni.SQLITE_IOERR_SEEK),
SQLITE_IOERR_DELETE_NOENT(SQLite3Jni.SQLITE_IOERR_DELETE_NOENT),
SQLITE_IOERR_MMAP(SQLite3Jni.SQLITE_IOERR_MMAP),
SQLITE_IOERR_GETTEMPPATH(SQLite3Jni.SQLITE_IOERR_GETTEMPPATH),
SQLITE_IOERR_CONVPATH(SQLite3Jni.SQLITE_IOERR_CONVPATH),
SQLITE_IOERR_VNODE(SQLite3Jni.SQLITE_IOERR_VNODE),
SQLITE_IOERR_AUTH(SQLite3Jni.SQLITE_IOERR_AUTH),
SQLITE_IOERR_BEGIN_ATOMIC(SQLite3Jni.SQLITE_IOERR_BEGIN_ATOMIC),
SQLITE_IOERR_COMMIT_ATOMIC(SQLite3Jni.SQLITE_IOERR_COMMIT_ATOMIC),
SQLITE_IOERR_ROLLBACK_ATOMIC(SQLite3Jni.SQLITE_IOERR_ROLLBACK_ATOMIC),
SQLITE_IOERR_DATA(SQLite3Jni.SQLITE_IOERR_DATA),
SQLITE_IOERR_CORRUPTFS(SQLite3Jni.SQLITE_IOERR_CORRUPTFS),
SQLITE_LOCKED_SHAREDCACHE(SQLite3Jni.SQLITE_LOCKED_SHAREDCACHE),
SQLITE_LOCKED_VTAB(SQLite3Jni.SQLITE_LOCKED_VTAB),
SQLITE_BUSY_RECOVERY(SQLite3Jni.SQLITE_BUSY_RECOVERY),
SQLITE_BUSY_SNAPSHOT(SQLite3Jni.SQLITE_BUSY_SNAPSHOT),
SQLITE_BUSY_TIMEOUT(SQLite3Jni.SQLITE_BUSY_TIMEOUT),
SQLITE_CANTOPEN_NOTEMPDIR(SQLite3Jni.SQLITE_CANTOPEN_NOTEMPDIR),
SQLITE_CANTOPEN_ISDIR(SQLite3Jni.SQLITE_CANTOPEN_ISDIR),
SQLITE_CANTOPEN_FULLPATH(SQLite3Jni.SQLITE_CANTOPEN_FULLPATH),
SQLITE_CANTOPEN_CONVPATH(SQLite3Jni.SQLITE_CANTOPEN_CONVPATH),
SQLITE_CANTOPEN_SYMLINK(SQLite3Jni.SQLITE_CANTOPEN_SYMLINK),
SQLITE_CORRUPT_VTAB(SQLite3Jni.SQLITE_CORRUPT_VTAB),
SQLITE_CORRUPT_SEQUENCE(SQLite3Jni.SQLITE_CORRUPT_SEQUENCE),
SQLITE_CORRUPT_INDEX(SQLite3Jni.SQLITE_CORRUPT_INDEX),
SQLITE_READONLY_RECOVERY(SQLite3Jni.SQLITE_READONLY_RECOVERY),
SQLITE_READONLY_CANTLOCK(SQLite3Jni.SQLITE_READONLY_CANTLOCK),
SQLITE_READONLY_ROLLBACK(SQLite3Jni.SQLITE_READONLY_ROLLBACK),
SQLITE_READONLY_DBMOVED(SQLite3Jni.SQLITE_READONLY_DBMOVED),
SQLITE_READONLY_CANTINIT(SQLite3Jni.SQLITE_READONLY_CANTINIT),
SQLITE_READONLY_DIRECTORY(SQLite3Jni.SQLITE_READONLY_DIRECTORY),
SQLITE_ABORT_ROLLBACK(SQLite3Jni.SQLITE_ABORT_ROLLBACK),
SQLITE_CONSTRAINT_CHECK(SQLite3Jni.SQLITE_CONSTRAINT_CHECK),
SQLITE_CONSTRAINT_COMMITHOOK(SQLite3Jni.SQLITE_CONSTRAINT_COMMITHOOK),
SQLITE_CONSTRAINT_FOREIGNKEY(SQLite3Jni.SQLITE_CONSTRAINT_FOREIGNKEY),
SQLITE_CONSTRAINT_FUNCTION(SQLite3Jni.SQLITE_CONSTRAINT_FUNCTION),
SQLITE_CONSTRAINT_NOTNULL(SQLite3Jni.SQLITE_CONSTRAINT_NOTNULL),
SQLITE_CONSTRAINT_PRIMARYKEY(SQLite3Jni.SQLITE_CONSTRAINT_PRIMARYKEY),
SQLITE_CONSTRAINT_TRIGGER(SQLite3Jni.SQLITE_CONSTRAINT_TRIGGER),
SQLITE_CONSTRAINT_UNIQUE(SQLite3Jni.SQLITE_CONSTRAINT_UNIQUE),
SQLITE_CONSTRAINT_VTAB(SQLite3Jni.SQLITE_CONSTRAINT_VTAB),
SQLITE_CONSTRAINT_ROWID(SQLite3Jni.SQLITE_CONSTRAINT_ROWID),
SQLITE_CONSTRAINT_PINNED(SQLite3Jni.SQLITE_CONSTRAINT_PINNED),
SQLITE_CONSTRAINT_DATATYPE(SQLite3Jni.SQLITE_CONSTRAINT_DATATYPE),
SQLITE_NOTICE_RECOVER_WAL(SQLite3Jni.SQLITE_NOTICE_RECOVER_WAL),
SQLITE_NOTICE_RECOVER_ROLLBACK(SQLite3Jni.SQLITE_NOTICE_RECOVER_ROLLBACK),
SQLITE_WARNING_AUTOINDEX(SQLite3Jni.SQLITE_WARNING_AUTOINDEX),
SQLITE_AUTH_USER(SQLite3Jni.SQLITE_AUTH_USER),
SQLITE_OK_LOAD_PERMANENTLY(SQLite3Jni.SQLITE_OK_LOAD_PERMANENTLY);
public final int value;
ResultCode(int rc){
value = rc;
ResultCodeMap.set(rc, this);
}
/**
Returns the entry from this enum for the given result code, or
null if no match is found.
*/
public static ResultCode getEntryForInt(int rc){
return ResultCodeMap.get(rc);
}
/**
Internal level of indirection required because we cannot initialize
static enum members in an enum before the enum constructor is
invoked.
*/
private static final class ResultCodeMap {
private static final java.util.Map<Integer,ResultCode> i2e
= new java.util.HashMap<>();
private static void set(int rc, ResultCode e){ i2e.put(rc, e); }
private static ResultCode get(int rc){ return i2e.get(rc); }
}
}
@@ -1,35 +0,0 @@
package org.sqlite.jni.annotation;
/**
This annotation is for marking functions as "canonical", meaning
that they map directly to a function in the core sqlite3 C API. The
intent is to distinguish them from functions added specifically to
the Java API.
<p>Canonical functions, unless specifically documented, have the
same semantics as their counterparts in <a
href="https://sqlite.org/c3ref/intro.html">the C API
documentation</a>, despite their signatures perhaps differing
slightly. Canonical forms may be native or implemented in Java.
Sometimes multiple overloads are labeled as Canonical because one
or more of them are just type- or encoding-related conversion
wrappers but provide identical semantics (e.g. from a String to a
byte[]). The Java API adds a number of convenience overloads to
simplify use, as well as a few Java-specific functions, and those
are never flagged as @Canonical.
<p>In some cases, the canonical version of a function is private
and exposed to Java via public overloads.
<p>The comment property can be used to add a comment.
*/
@java.lang.annotation.Documented
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD)
public @interface Canonical{
/**
Brief comments about the binding, e.g. noting any major
semantic differences.
*/
String comment() default "";
}
@@ -1,3 +1,16 @@
/*
** 2023-09-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.
**
*************************************************************************
** This file houses the NotNull annotaion for the sqlite3 C API.
*/
package org.sqlite.jni.annotation;
/**
@@ -20,6 +33,11 @@ package org.sqlite.jni.annotation;
any parameter marked with this annoation specifically invokes
undefined behavior.</p>
<p>Passing 0 (i.e. C NULL) or a negative value for any long-type
parameter marked with this annoation specifically invokes undefined
behavior. Such values are treated as C pointers in the JNI
layer.</p>
<p>Note that the C-style API does not throw any exceptions on its
own because it has a no-throw policy in order to retain its C-style
semantics, but it may trigger NullPointerExceptions (or similar) if
@@ -29,10 +47,11 @@ package org.sqlite.jni.annotation;
programmatically ensure that NotNull is conformed to in client
code.</p>
<p>This annotation is solely for the use by the classes in this
package but is made public so that javadoc will link to it from the
annotated functions. It is not part of the public API and
client-level code must not rely on it.</p>
<p>This annotation is solely for the use by the classes in the
org.sqlite package and subpackages, but is made public so that
javadoc will link to it from the annotated functions. It is not
part of the public API and client-level code must not rely on
it.</p>
*/
@java.lang.annotation.Documented
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
@@ -1,3 +1,16 @@
/*
** 2023-09-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.
**
*************************************************************************
** This file houses the Nullable annotaion for the sqlite3 C API.
*/
package org.sqlite.jni.annotation;
/**
@@ -1,4 +1,17 @@
/*
** 2023-09-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.
**
*************************************************************************
*/
/**
This package houses annotations specific a JNI binding to the SQLite3 C API.
This package houses annotations specific to the JNI bindings of the
SQLite3 C API.
*/
package org.sqlite.jni.annotation;
@@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
import org.sqlite.jni.annotation.NotNull;
/**
@@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
@@ -11,11 +11,11 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
import org.sqlite.jni.annotation.*;
/**
Callback for use with {@link SQLite3Jni#sqlite3_set_authorizer}.
Callback for use with {@link CApi#sqlite3_set_authorizer}.
*/
public interface AuthorizerCallback extends CallbackProxy {
/**
@@ -11,10 +11,10 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for use with the {@link SQLite3Jni#sqlite3_auto_extension}
Callback for use with the {@link CApi#sqlite3_auto_extension}
family of APIs.
*/
public interface AutoExtensionCallback extends CallbackProxy {
@@ -11,10 +11,10 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for use with {@link SQLite3Jni#sqlite3_busy_handler}.
Callback for use with {@link CApi#sqlite3_busy_handler}.
*/
public interface BusyHandlerCallback extends CallbackProxy {
/**
@@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
This marker interface exists soley for use as a documentation and
class-grouping tool. It should be applied to interfaces or
@@ -11,11 +11,11 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
import org.sqlite.jni.annotation.NotNull;
/**
Callback for use with {@link SQLite3Jni#sqlite3_create_collation}.
Callback for use with {@link CApi#sqlite3_create_collation}.
@see AbstractCollationCallback
*/
@@ -11,10 +11,10 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for use with {@link SQLite3Jni#sqlite3_collation_needed}.
Callback for use with {@link CApi#sqlite3_collation_needed}.
*/
public interface CollationNeededCallback extends CallbackProxy {
/**
@@ -11,10 +11,10 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for use with {@link SQLite3Jni#sqlite3_commit_hook}.
Callback for use with {@link CApi#sqlite3_commit_hook}.
*/
public interface CommitHookCallback extends CallbackProxy {
/**
@@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
A callback for use with sqlite3_config().
@@ -19,7 +19,7 @@ package org.sqlite.jni;
public interface ConfigLogCallback {
/**
Must function as described for a C-level callback for
{@link SQLite3Jni#sqlite3_config(ConfigLogCallback)}, with the slight signature change.
{@link CApi#sqlite3_config(ConfigLogCallback)}, with the slight signature change.
*/
void call(int errCode, String msg);
}
@@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
A callback for use with sqlite3_config().
@@ -19,7 +19,7 @@ package org.sqlite.jni;
public interface ConfigSqllogCallback {
/**
Must function as described for a C-level callback for
{@link SQLite3Jni#sqlite3_config(ConfigSqllogCallback)}, with the slight signature change.
{@link CApi#sqlite3_config(ConfigSqllogCallback)}, with the slight signature change.
*/
void call(sqlite3 db, String msg, int msgType );
}
@@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
A helper for passing pointers between JNI C code and Java, in
@@ -29,5 +29,18 @@ package org.sqlite.jni;
public class NativePointerHolder<ContextType> {
//! Only set from JNI, where access permissions don't matter.
private volatile long nativePointer = 0;
/**
For use ONLY by package-level APIs which act as proxies for
close/finalize operations. Such ops must call this to zero out
the pointer so that this object is not carrying a stale
pointer. This function returns the prior value of the pointer and
sets it to 0.
*/
final long clearNativePointer() {
final long rv = nativePointer;
nativePointer= 0;
return rv;
}
public final long getNativePointer(){ return nativePointer; }
}
@@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Helper classes for handling JNI output pointers.
@@ -49,16 +49,16 @@ public final class OutputPointer {
code.
*/
public static final class sqlite3 {
private org.sqlite.jni.sqlite3 value;
private org.sqlite.jni.capi.sqlite3 value;
/** Initializes with a null value. */
public sqlite3(){value = null;}
/** Sets the current value to null. */
public void clear(){value = null;}
/** Returns the current value. */
public final org.sqlite.jni.sqlite3 get(){return value;}
public final org.sqlite.jni.capi.sqlite3 get(){return value;}
/** Equivalent to calling get() then clear(). */
public final org.sqlite.jni.sqlite3 take(){
final org.sqlite.jni.sqlite3 v = value;
public final org.sqlite.jni.capi.sqlite3 take(){
final org.sqlite.jni.capi.sqlite3 v = value;
value = null;
return v;
}
@@ -70,16 +70,16 @@ public final class OutputPointer {
code.
*/
public static final class sqlite3_blob {
private org.sqlite.jni.sqlite3_blob value;
private org.sqlite.jni.capi.sqlite3_blob value;
/** Initializes with a null value. */
public sqlite3_blob(){value = null;}
/** Sets the current value to null. */
public void clear(){value = null;}
/** Returns the current value. */
public final org.sqlite.jni.sqlite3_blob get(){return value;}
public final org.sqlite.jni.capi.sqlite3_blob get(){return value;}
/** Equivalent to calling get() then clear(). */
public final org.sqlite.jni.sqlite3_blob take(){
final org.sqlite.jni.sqlite3_blob v = value;
public final org.sqlite.jni.capi.sqlite3_blob take(){
final org.sqlite.jni.capi.sqlite3_blob v = value;
value = null;
return v;
}
@@ -92,16 +92,16 @@ public final class OutputPointer {
code.
*/
public static final class sqlite3_stmt {
private org.sqlite.jni.sqlite3_stmt value;
private org.sqlite.jni.capi.sqlite3_stmt value;
/** Initializes with a null value. */
public sqlite3_stmt(){value = null;}
/** Sets the current value to null. */
public void clear(){value = null;}
/** Returns the current value. */
public final org.sqlite.jni.sqlite3_stmt get(){return value;}
public final org.sqlite.jni.capi.sqlite3_stmt get(){return value;}
/** Equivalent to calling get() then clear(). */
public final org.sqlite.jni.sqlite3_stmt take(){
final org.sqlite.jni.sqlite3_stmt v = value;
public final org.sqlite.jni.capi.sqlite3_stmt take(){
final org.sqlite.jni.capi.sqlite3_stmt v = value;
value = null;
return v;
}
@@ -114,16 +114,16 @@ public final class OutputPointer {
code.
*/
public static final class sqlite3_value {
private org.sqlite.jni.sqlite3_value value;
private org.sqlite.jni.capi.sqlite3_value value;
/** Initializes with a null value. */
public sqlite3_value(){value = null;}
/** Sets the current value to null. */
public void clear(){value = null;}
/** Returns the current value. */
public final org.sqlite.jni.sqlite3_value get(){return value;}
public final org.sqlite.jni.capi.sqlite3_value get(){return value;}
/** Equivalent to calling get() then clear(). */
public final org.sqlite.jni.sqlite3_value take(){
final org.sqlite.jni.sqlite3_value v = value;
public final org.sqlite.jni.capi.sqlite3_value take(){
final org.sqlite.jni.capi.sqlite3_value v = value;
value = null;
return v;
}
@@ -11,10 +11,10 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for use with {@link SQLite3Jni#sqlite3_prepare_multi}.
Callback for use with {@link CApi#sqlite3_prepare_multi}.
*/
public interface PrepareMultiCallback extends CallbackProxy {
@@ -53,7 +53,7 @@ public interface PrepareMultiCallback extends CallbackProxy {
try {
return this.p.call(st);
}finally{
SQLite3Jni.sqlite3_finalize(st);
CApi.sqlite3_finalize(st);
}
}
}
@@ -70,9 +70,9 @@ public interface PrepareMultiCallback extends CallbackProxy {
else the result of the final step is returned.
*/
@Override public int call(sqlite3_stmt st){
int rc = SQLite3Jni.SQLITE_DONE;
while( SQLite3Jni.SQLITE_ROW == (rc = SQLite3Jni.sqlite3_step(st)) ){}
return SQLite3Jni.SQLITE_DONE==rc ? 0 : rc;
int rc = CApi.SQLITE_DONE;
while( CApi.SQLITE_ROW == (rc = CApi.sqlite3_step(st)) ){}
return CApi.SQLITE_DONE==rc ? 0 : rc;
}
}
}
@@ -11,10 +11,10 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for use with {@link SQLite3Jni#sqlite3_preupdate_hook}.
Callback for use with {@link CApi#sqlite3_preupdate_hook}.
*/
public interface PreupdateHookCallback extends CallbackProxy {
/**
@@ -11,10 +11,10 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for use with {@link SQLite3Jni#sqlite3_progress_handler}.
Callback for use with {@link CApi#sqlite3_progress_handler}.
*/
public interface ProgressHandlerCallback extends CallbackProxy {
/**
@@ -0,0 +1,155 @@
/*
** 2023-07-21
**
** 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 is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
This enum contains all of the core and "extended" result codes used
by the sqlite3 library. It is provided not for use with the C-style
API (with which it won't work) but for higher-level code which may
find it useful to map SQLite result codes to human-readable names.
*/
public enum ResultCode {
SQLITE_OK(CApi.SQLITE_OK),
SQLITE_ERROR(CApi.SQLITE_ERROR),
SQLITE_INTERNAL(CApi.SQLITE_INTERNAL),
SQLITE_PERM(CApi.SQLITE_PERM),
SQLITE_ABORT(CApi.SQLITE_ABORT),
SQLITE_BUSY(CApi.SQLITE_BUSY),
SQLITE_LOCKED(CApi.SQLITE_LOCKED),
SQLITE_NOMEM(CApi.SQLITE_NOMEM),
SQLITE_READONLY(CApi.SQLITE_READONLY),
SQLITE_INTERRUPT(CApi.SQLITE_INTERRUPT),
SQLITE_IOERR(CApi.SQLITE_IOERR),
SQLITE_CORRUPT(CApi.SQLITE_CORRUPT),
SQLITE_NOTFOUND(CApi.SQLITE_NOTFOUND),
SQLITE_FULL(CApi.SQLITE_FULL),
SQLITE_CANTOPEN(CApi.SQLITE_CANTOPEN),
SQLITE_PROTOCOL(CApi.SQLITE_PROTOCOL),
SQLITE_EMPTY(CApi.SQLITE_EMPTY),
SQLITE_SCHEMA(CApi.SQLITE_SCHEMA),
SQLITE_TOOBIG(CApi.SQLITE_TOOBIG),
SQLITE_CONSTRAINT(CApi.SQLITE_CONSTRAINT),
SQLITE_MISMATCH(CApi.SQLITE_MISMATCH),
SQLITE_MISUSE(CApi.SQLITE_MISUSE),
SQLITE_NOLFS(CApi.SQLITE_NOLFS),
SQLITE_AUTH(CApi.SQLITE_AUTH),
SQLITE_FORMAT(CApi.SQLITE_FORMAT),
SQLITE_RANGE(CApi.SQLITE_RANGE),
SQLITE_NOTADB(CApi.SQLITE_NOTADB),
SQLITE_NOTICE(CApi.SQLITE_NOTICE),
SQLITE_WARNING(CApi.SQLITE_WARNING),
SQLITE_ROW(CApi.SQLITE_ROW),
SQLITE_DONE(CApi.SQLITE_DONE),
SQLITE_ERROR_MISSING_COLLSEQ(CApi.SQLITE_ERROR_MISSING_COLLSEQ),
SQLITE_ERROR_RETRY(CApi.SQLITE_ERROR_RETRY),
SQLITE_ERROR_SNAPSHOT(CApi.SQLITE_ERROR_SNAPSHOT),
SQLITE_IOERR_READ(CApi.SQLITE_IOERR_READ),
SQLITE_IOERR_SHORT_READ(CApi.SQLITE_IOERR_SHORT_READ),
SQLITE_IOERR_WRITE(CApi.SQLITE_IOERR_WRITE),
SQLITE_IOERR_FSYNC(CApi.SQLITE_IOERR_FSYNC),
SQLITE_IOERR_DIR_FSYNC(CApi.SQLITE_IOERR_DIR_FSYNC),
SQLITE_IOERR_TRUNCATE(CApi.SQLITE_IOERR_TRUNCATE),
SQLITE_IOERR_FSTAT(CApi.SQLITE_IOERR_FSTAT),
SQLITE_IOERR_UNLOCK(CApi.SQLITE_IOERR_UNLOCK),
SQLITE_IOERR_RDLOCK(CApi.SQLITE_IOERR_RDLOCK),
SQLITE_IOERR_DELETE(CApi.SQLITE_IOERR_DELETE),
SQLITE_IOERR_BLOCKED(CApi.SQLITE_IOERR_BLOCKED),
SQLITE_IOERR_NOMEM(CApi.SQLITE_IOERR_NOMEM),
SQLITE_IOERR_ACCESS(CApi.SQLITE_IOERR_ACCESS),
SQLITE_IOERR_CHECKRESERVEDLOCK(CApi.SQLITE_IOERR_CHECKRESERVEDLOCK),
SQLITE_IOERR_LOCK(CApi.SQLITE_IOERR_LOCK),
SQLITE_IOERR_CLOSE(CApi.SQLITE_IOERR_CLOSE),
SQLITE_IOERR_DIR_CLOSE(CApi.SQLITE_IOERR_DIR_CLOSE),
SQLITE_IOERR_SHMOPEN(CApi.SQLITE_IOERR_SHMOPEN),
SQLITE_IOERR_SHMSIZE(CApi.SQLITE_IOERR_SHMSIZE),
SQLITE_IOERR_SHMLOCK(CApi.SQLITE_IOERR_SHMLOCK),
SQLITE_IOERR_SHMMAP(CApi.SQLITE_IOERR_SHMMAP),
SQLITE_IOERR_SEEK(CApi.SQLITE_IOERR_SEEK),
SQLITE_IOERR_DELETE_NOENT(CApi.SQLITE_IOERR_DELETE_NOENT),
SQLITE_IOERR_MMAP(CApi.SQLITE_IOERR_MMAP),
SQLITE_IOERR_GETTEMPPATH(CApi.SQLITE_IOERR_GETTEMPPATH),
SQLITE_IOERR_CONVPATH(CApi.SQLITE_IOERR_CONVPATH),
SQLITE_IOERR_VNODE(CApi.SQLITE_IOERR_VNODE),
SQLITE_IOERR_AUTH(CApi.SQLITE_IOERR_AUTH),
SQLITE_IOERR_BEGIN_ATOMIC(CApi.SQLITE_IOERR_BEGIN_ATOMIC),
SQLITE_IOERR_COMMIT_ATOMIC(CApi.SQLITE_IOERR_COMMIT_ATOMIC),
SQLITE_IOERR_ROLLBACK_ATOMIC(CApi.SQLITE_IOERR_ROLLBACK_ATOMIC),
SQLITE_IOERR_DATA(CApi.SQLITE_IOERR_DATA),
SQLITE_IOERR_CORRUPTFS(CApi.SQLITE_IOERR_CORRUPTFS),
SQLITE_LOCKED_SHAREDCACHE(CApi.SQLITE_LOCKED_SHAREDCACHE),
SQLITE_LOCKED_VTAB(CApi.SQLITE_LOCKED_VTAB),
SQLITE_BUSY_RECOVERY(CApi.SQLITE_BUSY_RECOVERY),
SQLITE_BUSY_SNAPSHOT(CApi.SQLITE_BUSY_SNAPSHOT),
SQLITE_BUSY_TIMEOUT(CApi.SQLITE_BUSY_TIMEOUT),
SQLITE_CANTOPEN_NOTEMPDIR(CApi.SQLITE_CANTOPEN_NOTEMPDIR),
SQLITE_CANTOPEN_ISDIR(CApi.SQLITE_CANTOPEN_ISDIR),
SQLITE_CANTOPEN_FULLPATH(CApi.SQLITE_CANTOPEN_FULLPATH),
SQLITE_CANTOPEN_CONVPATH(CApi.SQLITE_CANTOPEN_CONVPATH),
SQLITE_CANTOPEN_SYMLINK(CApi.SQLITE_CANTOPEN_SYMLINK),
SQLITE_CORRUPT_VTAB(CApi.SQLITE_CORRUPT_VTAB),
SQLITE_CORRUPT_SEQUENCE(CApi.SQLITE_CORRUPT_SEQUENCE),
SQLITE_CORRUPT_INDEX(CApi.SQLITE_CORRUPT_INDEX),
SQLITE_READONLY_RECOVERY(CApi.SQLITE_READONLY_RECOVERY),
SQLITE_READONLY_CANTLOCK(CApi.SQLITE_READONLY_CANTLOCK),
SQLITE_READONLY_ROLLBACK(CApi.SQLITE_READONLY_ROLLBACK),
SQLITE_READONLY_DBMOVED(CApi.SQLITE_READONLY_DBMOVED),
SQLITE_READONLY_CANTINIT(CApi.SQLITE_READONLY_CANTINIT),
SQLITE_READONLY_DIRECTORY(CApi.SQLITE_READONLY_DIRECTORY),
SQLITE_ABORT_ROLLBACK(CApi.SQLITE_ABORT_ROLLBACK),
SQLITE_CONSTRAINT_CHECK(CApi.SQLITE_CONSTRAINT_CHECK),
SQLITE_CONSTRAINT_COMMITHOOK(CApi.SQLITE_CONSTRAINT_COMMITHOOK),
SQLITE_CONSTRAINT_FOREIGNKEY(CApi.SQLITE_CONSTRAINT_FOREIGNKEY),
SQLITE_CONSTRAINT_FUNCTION(CApi.SQLITE_CONSTRAINT_FUNCTION),
SQLITE_CONSTRAINT_NOTNULL(CApi.SQLITE_CONSTRAINT_NOTNULL),
SQLITE_CONSTRAINT_PRIMARYKEY(CApi.SQLITE_CONSTRAINT_PRIMARYKEY),
SQLITE_CONSTRAINT_TRIGGER(CApi.SQLITE_CONSTRAINT_TRIGGER),
SQLITE_CONSTRAINT_UNIQUE(CApi.SQLITE_CONSTRAINT_UNIQUE),
SQLITE_CONSTRAINT_VTAB(CApi.SQLITE_CONSTRAINT_VTAB),
SQLITE_CONSTRAINT_ROWID(CApi.SQLITE_CONSTRAINT_ROWID),
SQLITE_CONSTRAINT_PINNED(CApi.SQLITE_CONSTRAINT_PINNED),
SQLITE_CONSTRAINT_DATATYPE(CApi.SQLITE_CONSTRAINT_DATATYPE),
SQLITE_NOTICE_RECOVER_WAL(CApi.SQLITE_NOTICE_RECOVER_WAL),
SQLITE_NOTICE_RECOVER_ROLLBACK(CApi.SQLITE_NOTICE_RECOVER_ROLLBACK),
SQLITE_WARNING_AUTOINDEX(CApi.SQLITE_WARNING_AUTOINDEX),
SQLITE_AUTH_USER(CApi.SQLITE_AUTH_USER),
SQLITE_OK_LOAD_PERMANENTLY(CApi.SQLITE_OK_LOAD_PERMANENTLY);
public final int value;
ResultCode(int rc){
value = rc;
ResultCodeMap.set(rc, this);
}
/**
Returns the entry from this enum for the given result code, or
null if no match is found.
*/
public static ResultCode getEntryForInt(int rc){
return ResultCodeMap.get(rc);
}
/**
Internal level of indirection required because we cannot initialize
static enum members in an enum before the enum constructor is
invoked.
*/
private static final class ResultCodeMap {
private static final java.util.Map<Integer,ResultCode> i2e
= new java.util.HashMap<>();
private static void set(int rc, ResultCode e){ i2e.put(rc, e); }
private static ResultCode get(int rc){ return i2e.get(rc); }
}
}
@@ -11,10 +11,10 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for use with {@link SQLite3Jni#sqlite3_rollback_hook}.
Callback for use with {@link CApi#sqlite3_rollback_hook}.
*/
public interface RollbackHookCallback extends CallbackProxy {
/**
@@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
SQLFunction is used in conjunction with the
@@ -12,16 +12,13 @@
** This file contains the main application entry pointer for the
** SQLTester framework.
*/
package org.sqlite.jni.tester;
package org.sqlite.jni.capi;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.nio.charset.StandardCharsets;
import java.util.regex.*;
import org.sqlite.jni.*;
import static org.sqlite.jni.SQLite3Jni.*;
import org.sqlite.jni.sqlite3;
import static org.sqlite.jni.capi.CApi.*;
/**
Modes for how to escape (or not) column values and names from
@@ -71,7 +68,7 @@ class SQLTesterException extends RuntimeException {
class DbException extends SQLTesterException {
DbException(sqlite3 db, int rc, boolean closeDb){
super("DB error #"+rc+": "+sqlite3_errmsg16(db),true);
super("DB error #"+rc+": "+sqlite3_errmsg(db),true);
if( closeDb ) sqlite3_close_v2(db);
}
DbException(sqlite3 db, int rc){
@@ -150,12 +147,15 @@ class Outer {
}
/**
This class provides an application which aims to implement the
<p>This class provides an application which aims to implement the
rudimentary SQL-driven test tool described in the accompanying
{@code test-script-interpreter.md}.
<p>This is a work in progress.
<p>This class is an internal testing tool, not part of the public
interface but is (A) in the same package as the library because
access permissions require it to be so and (B) the JDK8 javadoc
offers no way to filter individual classes out of the doc
generation process (it can only exclude packages, but see (A)).
<p>An instance of this application provides a core set of services
which TestScript instances use for processing testing logic.
@@ -457,8 +457,8 @@ public class SQLTester {
}
private void appendDbErr(sqlite3 db, StringBuilder sb, int rc){
sb.append(org.sqlite.jni.ResultCode.getEntryForInt(rc)).append(' ');
final String msg = escapeSqlValue(sqlite3_errmsg16(db));
sb.append(org.sqlite.jni.capi.ResultCode.getEntryForInt(rc)).append(' ');
final String msg = escapeSqlValue(sqlite3_errmsg(db));
if( '{' == msg.charAt(0) ){
sb.append(msg);
}else{
@@ -668,7 +668,7 @@ public class SQLTester {
static {
System.loadLibrary("sqlite3-jni")
/* Interestingly, when SQLTester is the main app, we have to
load that lib from here. The same load from SQLite3Jni does
load that lib from here. The same load from CApi does
not happen early enough. Without this,
installCustomExtensions() is an unresolved symbol. */;
}
@@ -932,7 +932,7 @@ class RunCommand extends Command {
final int rc = t.execSql(db, false, ResultBufferMode.NONE,
ResultRowMode.ONELINE, sql);
if( 0!=rc && t.isVerbose() ){
String msg = sqlite3_errmsg16(db);
String msg = sqlite3_errmsg(db);
ts.verbose1(argv[0]," non-fatal command error #",rc,": ",
msg,"\nfor SQL:\n",sql);
}
@@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
@@ -27,7 +27,7 @@ public abstract class ScalarFunction implements SQLFunction {
/**
Optionally override to be notified when the UDF is finalized by
SQLite. This implementation does nothing.
SQLite. This default implementation does nothing.
*/
public void xDestroy() {}
}
@@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
A wrapper object for use with sqlite3_table_column_metadata().
@@ -11,8 +11,8 @@
*************************************************************************
** This file contains a set of tests for the sqlite3 JNI bindings.
*/
package org.sqlite.jni;
import static org.sqlite.jni.SQLite3Jni.*;
package org.sqlite.jni.capi;
import static org.sqlite.jni.capi.CApi.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.ArrayList;
@@ -38,17 +38,6 @@ import java.util.concurrent.Future;
@java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD})
@interface SingleThreadOnly{}
/**
A helper class which simply holds a single value. Its current use
is for communicating values out of anonymous classes, as doing so
requires a "final" reference.
*/
class ValueHolder<T> {
public T value;
public ValueHolder(){}
public ValueHolder(T v){value = v;}
}
public class Tester1 implements Runnable {
//! True when running in multi-threaded mode.
private static boolean mtMode = false;
@@ -146,7 +135,7 @@ public class Tester1 implements Runnable {
sqlite3 db = out.take();
if( 0!=rc ){
final String msg =
null==db ? sqlite3_errstr(rc) : sqlite3_errmsg16(db);
null==db ? sqlite3_errstr(rc) : sqlite3_errmsg(db);
sqlite3_close(db);
throw new RuntimeException("Opening db failed: "+msg);
}
@@ -197,7 +186,7 @@ public class Tester1 implements Runnable {
if(SQLITE_ROW==rc || SQLITE_DONE==rc) rc = 0;
if( 0!=rc && throwOnError){
throw new RuntimeException("db op failed with rc="
+rc+": "+sqlite3_errmsg16(db));
+rc+": "+sqlite3_errmsg(db));
}
return rc;
}
@@ -249,12 +238,18 @@ public class Tester1 implements Runnable {
++metrics.dbOpen;
sqlite3 db = out.get();
affirm(0 == rc);
affirm(0 < db.getNativePointer());
affirm(db.getNativePointer()!=0);
sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, null)
/* This function has different mangled names in jdk8 vs jdk19,
and this call is here to ensure that the build fails
if it cannot find both names. */;
affirm( 0==sqlite3_db_readonly(db,"main") );
affirm( 0==sqlite3_db_readonly(db,null) );
affirm( 0>sqlite3_db_readonly(db,"nope") );
affirm( 0>sqlite3_db_readonly(null,null) );
affirm( 0==sqlite3_last_insert_rowid(null) );
// These interrupt checks are only to make sure that the JNI binding
// has the proper exported symbol names. They don't actually test
// anything useful.
@@ -273,7 +268,7 @@ public class Tester1 implements Runnable {
++metrics.dbOpen;
affirm(0 == rc);
sqlite3 db = out.get();
affirm(0 < db.getNativePointer());
affirm(0 != db.getNativePointer());
sqlite3_close_v2(db);
affirm(0 == db.getNativePointer());
}
@@ -289,9 +284,6 @@ public class Tester1 implements Runnable {
affirm( !sqlite3_stmt_readonly(stmt) );
affirm( db == sqlite3_db_handle(stmt) );
rc = sqlite3_step(stmt);
if( SQLITE_DONE != rc ){
outln("step failed ??? ",rc, " ",sqlite3_errmsg16(db));
}
affirm(SQLITE_DONE == rc);
sqlite3_finalize(stmt);
affirm( null == sqlite3_db_handle(stmt) );
@@ -346,7 +338,7 @@ public class Tester1 implements Runnable {
stmt = sqlite3_prepare(db, "intentional error");
affirm( null==stmt );
affirm( 0!=sqlite3_errcode(db) );
affirm( 0==sqlite3_errmsg16(db).indexOf("near \"intentional\"") );
affirm( 0==sqlite3_errmsg(db).indexOf("near \"intentional\"") );
sqlite3_finalize(stmt);
stmt = sqlite3_prepare(db, "/* empty input*/\n-- comments only");
affirm( null==stmt );
@@ -389,65 +381,80 @@ public class Tester1 implements Runnable {
affirm(sqlite3_total_changes64(db) > changesT64);
stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
affirm( sqlite3_stmt_readonly(stmt) );
affirm( !sqlite3_stmt_busy(stmt) );
int total2 = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){
affirm( sqlite3_stmt_busy(stmt) );
total2 += sqlite3_column_int(stmt, 0);
sqlite3_value sv = sqlite3_column_value(stmt, 0);
affirm( null != sv );
affirm( 0 != sv.getNativePointer() );
affirm( SQLITE_INTEGER == sqlite3_value_type(sv) );
}
affirm( !sqlite3_stmt_busy(stmt) );
sqlite3_finalize(stmt);
affirm(total1 == total2);
// sqlite3_value_frombind() checks...
stmt = prepare(db, "SELECT 1, ?");
sqlite3_bind_int(stmt, 1, 2);
rc = sqlite3_step(stmt);
affirm( SQLITE_ROW==rc );
affirm( !sqlite3_value_frombind(sqlite3_column_value(stmt, 0)) );
affirm( sqlite3_value_frombind(sqlite3_column_value(stmt, 1)) );
sqlite3_finalize(stmt);
sqlite3_close_v2(db);
affirm(0 == db.getNativePointer());
}
private void testBindFetchInt64(){
sqlite3 db = createNewDb();
execSql(db, "CREATE TABLE t(a)");
sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);");
long total1 = 0;
for(long i = 0xffffffff; i < 0xffffffff + 3; ++i ){
total1 += i;
sqlite3_bind_int64(stmt, 1, i);
sqlite3_step(stmt);
sqlite3_reset(stmt);
try (sqlite3 db = createNewDb()){
execSql(db, "CREATE TABLE t(a)");
sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);");
long total1 = 0;
for(long i = 0xffffffff; i < 0xffffffff + 3; ++i ){
total1 += i;
sqlite3_bind_int64(stmt, 1, i);
sqlite3_step(stmt);
sqlite3_reset(stmt);
}
sqlite3_finalize(stmt);
stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
long total2 = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){
total2 += sqlite3_column_int64(stmt, 0);
}
sqlite3_finalize(stmt);
affirm(total1 == total2);
//sqlite3_close_v2(db);
}
sqlite3_finalize(stmt);
stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
long total2 = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){
total2 += sqlite3_column_int64(stmt, 0);
}
sqlite3_finalize(stmt);
affirm(total1 == total2);
sqlite3_close_v2(db);
}
private void testBindFetchDouble(){
sqlite3 db = createNewDb();
execSql(db, "CREATE TABLE t(a)");
sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);");
double total1 = 0;
for(double i = 1.5; i < 5.0; i = i + 1.0 ){
total1 += i;
sqlite3_bind_double(stmt, 1, i);
sqlite3_step(stmt);
sqlite3_reset(stmt);
try (sqlite3 db = createNewDb()){
execSql(db, "CREATE TABLE t(a)");
sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);");
double total1 = 0;
for(double i = 1.5; i < 5.0; i = i + 1.0 ){
total1 += i;
sqlite3_bind_double(stmt, 1, i);
sqlite3_step(stmt);
sqlite3_reset(stmt);
}
sqlite3_finalize(stmt);
stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
double total2 = 0;
int counter = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){
++counter;
total2 += sqlite3_column_double(stmt, 0);
}
affirm(4 == counter);
sqlite3_finalize(stmt);
affirm(total2<=total1+0.01 && total2>=total1-0.01);
//sqlite3_close_v2(db);
}
sqlite3_finalize(stmt);
stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
double total2 = 0;
int counter = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){
++counter;
total2 += sqlite3_column_double(stmt, 0);
}
affirm(4 == counter);
sqlite3_finalize(stmt);
affirm(total2<=total1+0.01 && total2>=total1-0.01);
sqlite3_close_v2(db);
}
private void testBindFetchText(){
@@ -491,20 +498,25 @@ public class Tester1 implements Runnable {
affirm(3 == n);
affirm("w😃rldhell🤩!🤩".equals(sbuf.toString()));
stmt = prepare(db, "SELECT ?, ?");
rc = sqlite3_bind_text(stmt, 1, "");
affirm( 0==rc );
rc = sqlite3_bind_text(stmt, 2, (String)null);
affirm( 0==rc );
rc = sqlite3_step(stmt);
affirm( SQLITE_ROW==rc );
byte[] colBa = sqlite3_column_text(stmt, 0);
affirm( 0==colBa.length );
colBa = sqlite3_column_text(stmt, 1);
affirm( null==colBa );
sqlite3_finalize(stmt);
try( sqlite3_stmt stmt2 = prepare(db, "SELECT ?, ?") ){
rc = sqlite3_bind_text(stmt2, 1, "");
affirm( 0==rc );
rc = sqlite3_bind_text(stmt2, 2, (String)null);
affirm( 0==rc );
rc = sqlite3_step(stmt2);
affirm( SQLITE_ROW==rc );
byte[] colBa = sqlite3_column_text(stmt2, 0);
affirm( 0==colBa.length );
colBa = sqlite3_column_text(stmt2, 1);
affirm( null==colBa );
//sqlite3_finalize(stmt);
}
sqlite3_close_v2(db);
if(true){
sqlite3_close_v2(db);
}else{
// Let the Object.finalize() override deal with it.
}
}
private void testBindFetchBlob(){
@@ -543,7 +555,10 @@ public class Tester1 implements Runnable {
sqlite3_finalize(stmt);
stmt = prepare(db, "SELECT ?");
sqlite3_bind_text(stmt, 1, "hell😃");
affirm( "SELECT 'hell😃'".equals(sqlite3_expanded_sql(stmt)) );
final String expect = "SELECT 'hell😃'";
affirm( expect.equals(sqlite3_expanded_sql(stmt)) );
String n = sqlite3_normalized_sql(stmt);
affirm( null==n || "SELECT?;".equals(n) );
sqlite3_finalize(stmt);
sqlite3_close(db);
}
@@ -668,6 +683,8 @@ public class Tester1 implements Runnable {
// These ValueHolders are just to confirm that the func did what we want...
final ValueHolder<Boolean> xDestroyCalled = new ValueHolder<>(false);
final ValueHolder<Integer> xFuncAccum = new ValueHolder<>(0);
final ValueHolder<sqlite3_value[]> neverEverDoThisInClientCode = new ValueHolder<>(null);
final ValueHolder<sqlite3_context> neverEverDoThisInClientCode2 = new ValueHolder<>(null);
// Create an SQLFunction instance using one of its 3 subclasses:
// Scalar, Aggregate, or Window:
@@ -678,6 +695,15 @@ public class Tester1 implements Runnable {
new ScalarFunction(){
public void xFunc(sqlite3_context cx, sqlite3_value[] args){
affirm(db == sqlite3_context_db_handle(cx));
if( null==neverEverDoThisInClientCode.value ){
/* !!!NEVER!!! hold a reference to an sqlite3_value or
sqlite3_context object like this in client code! They
are ONLY legal for the duration of their single
call. We do it here ONLY to test that the defenses
against clients doing this are working. */
neverEverDoThisInClientCode2.value = cx;
neverEverDoThisInClientCode.value = args;
}
int result = 0;
for( sqlite3_value v : args ) result += sqlite3_value_int(v);
xFuncAccum.value += result;// just for post-run testing
@@ -703,6 +729,13 @@ public class Tester1 implements Runnable {
affirm(1 == n);
affirm(6 == xFuncAccum.value);
affirm( !xDestroyCalled.value );
affirm( null!=neverEverDoThisInClientCode.value );
affirm( null!=neverEverDoThisInClientCode2.value );
affirm( 0<neverEverDoThisInClientCode.value.length );
affirm( 0==neverEverDoThisInClientCode2.value.getNativePointer() );
for( sqlite3_value sv : neverEverDoThisInClientCode.value ){
affirm( 0==sv.getNativePointer() );
}
sqlite3_close_v2(db);
affirm( xDestroyCalled.value );
}
@@ -729,7 +762,7 @@ public class Tester1 implements Runnable {
rc = sqlite3_step(stmt);
sqlite3_finalize(stmt);
affirm( 0 != rc );
affirm( sqlite3_errmsg16(db).indexOf("an xFinal") > 0 );
affirm( sqlite3_errmsg(db).indexOf("an xFinal") > 0 );
SQLFunction funcSc = new ScalarFunction(){
@Override public void xFunc(sqlite3_context cx, sqlite3_value[] args){
@@ -743,7 +776,7 @@ public class Tester1 implements Runnable {
rc = sqlite3_step(stmt);
sqlite3_finalize(stmt);
affirm( 0 != rc );
affirm( sqlite3_errmsg16(db).indexOf("an xFunc") > 0 );
affirm( sqlite3_errmsg(db).indexOf("an xFunc") > 0 );
rc = sqlite3_create_function(db, "mysca", 1, -1, funcSc);
affirm( SQLITE_FORMAT==rc, "invalid encoding value." );
sqlite3_close_v2(db);
@@ -914,7 +947,7 @@ public class Tester1 implements Runnable {
private void listBoundMethods(){
if(false){
final java.lang.reflect.Field[] declaredFields =
SQLite3Jni.class.getDeclaredFields();
CApi.class.getDeclaredFields();
outln("Bound constants:\n");
for(java.lang.reflect.Field field : declaredFields) {
if(java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
@@ -923,7 +956,7 @@ public class Tester1 implements Runnable {
}
}
final java.lang.reflect.Method[] declaredMethods =
SQLite3Jni.class.getDeclaredMethods();
CApi.class.getDeclaredMethods();
final java.util.List<String> funcList = new java.util.ArrayList<>();
for(java.lang.reflect.Method m : declaredMethods){
if((m.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0){
@@ -1008,8 +1041,11 @@ public class Tester1 implements Runnable {
affirm( 0 == rc );
affirm( outDb.get() != db1 );
final sqlite3 db2 = outDb.get();
affirm( "main".equals( sqlite3_db_name(db1, 0) ) );
rc = sqlite3_db_config(db1, SQLITE_DBCONFIG_MAINDBNAME, "foo");
affirm( sqlite3_db_filename(db1, "foo").endsWith(dbName) );
affirm( "foo".equals( sqlite3_db_name(db1, 0) ) );
final ValueHolder<Integer> xBusyCalled = new ValueHolder<>(0);
BusyHandlerCallback handler = new BusyHandlerCallback(){
@@ -1025,7 +1061,7 @@ public class Tester1 implements Runnable {
execSql(db1, "BEGIN EXCLUSIVE");
rc = sqlite3_prepare_v2(db2, "SELECT * from t", outStmt);
affirm( SQLITE_BUSY == rc);
assert( null == outStmt.get() );
affirm( null == outStmt.get() );
affirm( 3 == xBusyCalled.value );
sqlite3_close_v2(db1);
sqlite3_close_v2(db2);
@@ -1436,7 +1472,7 @@ public class Tester1 implements Runnable {
affirm( "noCase".equals(zCollSeq.value) );
affirm( "duck".equals(zDataType.value) );
final TableColumnMetadata m =
TableColumnMetadata m =
sqlite3_table_column_metadata(db, "main", "t", "a");
affirm( null != m );
affirm( bPrimaryKey.value == m.isPrimaryKey() );
@@ -1446,6 +1482,16 @@ public class Tester1 implements Runnable {
affirm( zDataType.value.equals(m.getDataType()) );
affirm( null == sqlite3_table_column_metadata(db, "nope", "t", "a") );
affirm( null == sqlite3_table_column_metadata(db, "main", "nope", "a") );
m = sqlite3_table_column_metadata(db, "main", "t", null)
/* Check only for existence of table */;
affirm( null != m );
affirm( m.isPrimaryKey() );
affirm( !m.isAutoincrement() );
affirm( !m.isNotNull() );
affirm( "BINARY".equalsIgnoreCase(m.getCollation()) );
affirm( "INTEGER".equalsIgnoreCase(m.getDataType()) );
sqlite3_close_v2(db);
}
@@ -1508,35 +1554,34 @@ public class Tester1 implements Runnable {
}
private void testBackup(){
final sqlite3 db1 = createNewDb();
final sqlite3 db2 = createNewDb();
final sqlite3 dbDest = createNewDb();
execSql(db1, new String[]{
"pragma page_size=512; VACUUM;",
"create table t(a);",
"insert into t(a) values(1),(2),(3);"
});
affirm( null==sqlite3_backup_init(db1,"main",db1,"main") );
final sqlite3_backup b = sqlite3_backup_init(db2,"main",db1,"main");
affirm( null!=b );
affirm( b.getNativePointer()!=0 );
int rc;
while( SQLITE_DONE!=(rc = sqlite3_backup_step(b, 1)) ){
affirm( 0==rc );
try (sqlite3 dbSrc = createNewDb()) {
execSql(dbSrc, new String[]{
"pragma page_size=512; VACUUM;",
"create table t(a);",
"insert into t(a) values(1),(2),(3);"
});
affirm( null==sqlite3_backup_init(dbSrc,"main",dbSrc,"main") );
try (sqlite3_backup b = sqlite3_backup_init(dbDest,"main",dbSrc,"main")) {
affirm( null!=b );
affirm( b.getNativePointer()!=0 );
int rc;
while( SQLITE_DONE!=(rc = sqlite3_backup_step(b, 1)) ){
affirm( 0==rc );
}
affirm( sqlite3_backup_pagecount(b) > 0 );
rc = sqlite3_backup_finish(b);
affirm( 0==rc );
affirm( b.getNativePointer()==0 );
}
}
affirm( sqlite3_backup_pagecount(b) > 0 );
rc = sqlite3_backup_finish(b);
affirm( 0==rc );
affirm( b.getNativePointer()==0 );
sqlite3_close_v2(db1);
final sqlite3_stmt stmt = prepare(db2,"SELECT sum(a) from t");
sqlite3_step(stmt);
affirm( sqlite3_column_int(stmt,0) == 6 );
sqlite3_finalize(stmt);
sqlite3_close_v2(db2);
try (sqlite3_stmt stmt = prepare(dbDest,"SELECT sum(a) from t")) {
sqlite3_step(stmt);
affirm( sqlite3_column_int(stmt,0) == 6 );
}
sqlite3_close_v2(dbDest);
}
private void testRandomness(){
@@ -1906,29 +1951,24 @@ public class Tester1 implements Runnable {
sqlite3_shutdown();
int nMethods = 0;
int nNatives = 0;
int nCanonical = 0;
final java.lang.reflect.Method[] declaredMethods =
SQLite3Jni.class.getDeclaredMethods();
CApi.class.getDeclaredMethods();
for(java.lang.reflect.Method m : declaredMethods){
final int mod = m.getModifiers();
if( 0!=(mod & java.lang.reflect.Modifier.STATIC) ){
final String name = m.getName();
if(name.startsWith("sqlite3_")){
++nMethods;
if( m.isAnnotationPresent( org.sqlite.jni.annotation.Canonical.class ) ){
++nCanonical;
}
if( 0!=(mod & java.lang.reflect.Modifier.NATIVE) ){
++nNatives;
}
}
}
}
outln("\tSQLite3Jni.sqlite3_*() methods: "+
outln("\tCApi.sqlite3_*() methods: "+
nMethods+" total, with "+
nNatives+" native, "+
(nMethods - nNatives)+" Java, ",
nCanonical," @Canonical"
(nMethods - nNatives)+" Java"
);
outln("\tTotal test time = "
+(timeEnd - timeStart)+"ms");
@@ -11,11 +11,11 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
import org.sqlite.jni.annotation.Nullable;
/**
Callback for use with {@link SQLite3Jni#sqlite3_trace_v2}.
Callback for use with {@link CApi#sqlite3_trace_v2}.
*/
public interface TraceV2Callback extends CallbackProxy {
/**
@@ -11,10 +11,10 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for use with {@link SQLite3Jni#sqlite3_update_hook}.
Callback for use with {@link CApi#sqlite3_update_hook}.
*/
public interface UpdateHookCallback extends CallbackProxy {
/**
@@ -0,0 +1,25 @@
/*
** 2023-10-16
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains a set of tests for the sqlite3 JNI bindings.
*/
package org.sqlite.jni.capi;
/**
A helper class which simply holds a single value. Its primary use
is for communicating values out of anonymous classes, as doing so
requires a "final" reference.
*/
public class ValueHolder<T> {
public T value;
public ValueHolder(){}
public ValueHolder(T v){value = v;}
}
@@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
@@ -11,7 +11,7 @@
*************************************************************************
** This file declares JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for a hook called by SQLite when certain client-provided
@@ -2,7 +2,7 @@
This package houses a JNI binding to the SQLite3 C API.
<p>The primary interfaces are in {@link
org.sqlite.jni.SQLite3Jni}.</p>
org.sqlite.jni.capi.CApi}.</p>
<h1>API Goals and Requirements</h1>
@@ -86,4 +86,4 @@
undefined behavior.</p>
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
@@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
A wrapper for communicating C-level (sqlite3*) instances with
@@ -19,19 +19,25 @@ package org.sqlite.jni;
simply provide a type-safe way to communicate it between Java
and C via JNI.
*/
public final class sqlite3 extends NativePointerHolder<sqlite3> {
public final class sqlite3 extends NativePointerHolder<sqlite3>
implements AutoCloseable {
// Only invoked from JNI
private sqlite3(){}
public String toString(){
long ptr = getNativePointer();
final long ptr = getNativePointer();
if( 0==ptr ){
return sqlite3.class.getSimpleName()+"@null";
}
String fn = SQLite3Jni.sqlite3_db_filename(this, "main");
final String fn = CApi.sqlite3_db_filename(this, "main");
return sqlite3.class.getSimpleName()
+"@"+String.format("0x%08x",ptr)
+"["+((null == fn) ? "<unnamed>" : fn)+"]"
;
}
@Override public void close(){
CApi.sqlite3_close_v2(this.clearNativePointer());
}
}
@@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
A wrapper for passing C-level (sqlite3_backup*) instances around in
@@ -19,7 +19,13 @@ package org.sqlite.jni;
simply provide a type-safe way to communicate it between Java and C
via JNI.
*/
public final class sqlite3_backup extends NativePointerHolder<sqlite3_backup> {
public final class sqlite3_backup extends NativePointerHolder<sqlite3_backup>
implements AutoCloseable {
// Only invoked from JNI.
private sqlite3_backup(){}
@Override public void close(){
CApi.sqlite3_backup_finish(this);
}
}
@@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
A wrapper for passing C-level (sqlite3_blob*) instances around in
@@ -19,7 +19,13 @@ package org.sqlite.jni;
simply provide a type-safe way to communicate it between Java and C
via JNI.
*/
public final class sqlite3_blob extends NativePointerHolder<sqlite3_blob> {
public final class sqlite3_blob extends NativePointerHolder<sqlite3_blob>
implements AutoCloseable {
// Only invoked from JNI.
private sqlite3_blob(){}
@Override public void close(){
CApi.sqlite3_blob_close(this.clearNativePointer());
}
}
@@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
sqlite3_context instances are used in conjunction with user-defined
@@ -71,7 +71,7 @@ public final class sqlite3_context extends NativePointerHolder<sqlite3_context>
*/
public synchronized Long getAggregateContext(boolean initIfNeeded){
if( aggregateContext==null ){
aggregateContext = SQLite3Jni.sqlite3_aggregate_context(this, initIfNeeded);
aggregateContext = CApi.sqlite3_aggregate_context(this, initIfNeeded);
if( !initIfNeeded && null==aggregateContext ) aggregateContext = 0L;
}
return (null==aggregateContext || 0!=aggregateContext) ? aggregateContext : null;
@@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
A wrapper for communicating C-level (sqlite3_stmt*) instances with
@@ -19,7 +19,12 @@ package org.sqlite.jni;
simply provide a type-safe way to communicate it between Java and C
via JNI.
*/
public final class sqlite3_stmt extends NativePointerHolder<sqlite3_stmt> {
public final class sqlite3_stmt extends NativePointerHolder<sqlite3_stmt>
implements AutoCloseable {
// Only invoked from JNI.
private sqlite3_stmt(){}
@Override public void close(){
CApi.sqlite3_finalize(this.clearNativePointer());
}
}
@@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
public final class sqlite3_value extends NativePointerHolder<sqlite3_value> {
//! Invoked only from JNI.
@@ -12,7 +12,7 @@
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.fts5;
import org.sqlite.jni.*;
import org.sqlite.jni.capi.*;
/**
A wrapper for communicating C-level (Fts5Context*) instances with
@@ -13,14 +13,10 @@
*/
package org.sqlite.jni.fts5;
import java.nio.charset.StandardCharsets;
import org.sqlite.jni.*;
import org.sqlite.jni.capi.*;
import org.sqlite.jni.annotation.*;
/**
ALMOST COMPLETELY UNTESTED.
FAR FROM COMPLETE and the feasibility of binding this to Java
is still undetermined. This might be removed.
*/
public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi> {
//! Only called from JNI
@@ -37,67 +33,51 @@ public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi
*/
public static native Fts5ExtensionApi getInstance();
@Canonical
public native int xColumnCount(@NotNull Fts5Context fcx);
@Canonical
public native int xColumnSize(@NotNull Fts5Context cx, int iCol,
@NotNull OutputPointer.Int32 pnToken);
@Canonical
public native int xColumnText(@NotNull Fts5Context cx, int iCol,
@NotNull OutputPointer.String txt);
@Canonical
public native int xColumnTotalSize(@NotNull Fts5Context fcx, int iCol,
@NotNull OutputPointer.Int64 pnToken);
@Canonical
public native Object xGetAuxdata(@NotNull Fts5Context cx, boolean clearIt);
@Canonical
public native int xInst(@NotNull Fts5Context cx, int iIdx,
@NotNull OutputPointer.Int32 piPhrase,
@NotNull OutputPointer.Int32 piCol,
@NotNull OutputPointer.Int32 piOff);
@Canonical
public native int xInstCount(@NotNull Fts5Context fcx,
@NotNull OutputPointer.Int32 pnInst);
@Canonical
public native int xPhraseCount(@NotNull Fts5Context fcx);
@Canonical
public native int xPhraseFirst(@NotNull Fts5Context cx, int iPhrase,
@NotNull Fts5PhraseIter iter,
@NotNull OutputPointer.Int32 iCol,
@NotNull OutputPointer.Int32 iOff);
@Canonical
public native int xPhraseFirstColumn(@NotNull Fts5Context cx, int iPhrase,
@NotNull Fts5PhraseIter iter,
@NotNull OutputPointer.Int32 iCol);
@Canonical
public native void xPhraseNext(@NotNull Fts5Context cx,
@NotNull Fts5PhraseIter iter,
@NotNull OutputPointer.Int32 iCol,
@NotNull OutputPointer.Int32 iOff);
@Canonical
public native void xPhraseNextColumn(@NotNull Fts5Context cx,
@NotNull Fts5PhraseIter iter,
@NotNull OutputPointer.Int32 iCol);
@Canonical
public native int xPhraseSize(@NotNull Fts5Context fcx, int iPhrase);
@Canonical
public native int xQueryPhrase(@NotNull Fts5Context cx, int iPhrase,
@NotNull XQueryPhraseCallback callback);
@Canonical
public native int xRowCount(@NotNull Fts5Context fcx,
@NotNull OutputPointer.Int64 nRow);
@Canonical
public native long xRowid(@NotNull Fts5Context cx);
/* Note that the JNI binding lacks the C version's xDelete()
callback argument. Instead, if pAux has an xDestroy() method, it
@@ -106,14 +86,11 @@ public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi
pAux held by the JNI layer will be relinquished regardless of
whether pAux has an xDestroy() method. */
@Canonical
public native int xSetAuxdata(@NotNull Fts5Context cx, @Nullable Object pAux);
@Canonical
public native int xTokenize(@NotNull Fts5Context cx, @NotNull byte[] pText,
@NotNull XTokenizeCallback callback);
@Canonical
public native Object xUserData(Fts5Context cx);
//^^^ returns the pointer passed as the 3rd arg to the C-level
// fts5_api::xCreateFunction().
@@ -12,7 +12,7 @@
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.fts5;
import org.sqlite.jni.NativePointerHolder;
import org.sqlite.jni.capi.NativePointerHolder;
/**
A wrapper for C-level Fts5PhraseIter. They are only modified and
@@ -12,7 +12,7 @@
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.fts5;
import org.sqlite.jni.NativePointerHolder;
import org.sqlite.jni.capi.NativePointerHolder;
/**
INCOMPLETE AND COMPLETELY UNTESTED.
+406 -6
View File
@@ -12,9 +12,10 @@
** This file contains a set of tests for the sqlite3 JNI bindings.
*/
package org.sqlite.jni.fts5;
import static org.sqlite.jni.SQLite3Jni.*;
import static org.sqlite.jni.Tester1.*;
import org.sqlite.jni.*;
import static org.sqlite.jni.capi.CApi.*;
import static org.sqlite.jni.capi.Tester1.*;
import org.sqlite.jni.capi.*;
import org.sqlite.jni.fts5.*;
import java.util.*;
@@ -103,7 +104,7 @@ public class TesterFts5 {
}
});
if( rc!=SQLITE_OK ){
throw new RuntimeException(sqlite3_errmsg16(db));
throw new RuntimeException(sqlite3_errmsg(db));
}
/* Convert to array and return */
@@ -275,7 +276,272 @@ public class TesterFts5 {
public void xDestroy(){ }
};
fts5_aux fts5_aux1 = new fts5_aux();
/*
** fts5_inst(<fts>);
**
** This is used to test the xInstCount() and xInst() APIs. It returns a
** text value containing a Tcl list with xInstCount() elements. Each
** element is itself a list of 3 integers - the phrase number, column
** number and token offset returned by each call to xInst().
*/
fts5_extension_function fts5_inst = new fts5_extension_function(){
@Override public void call(
Fts5ExtensionApi ext,
Fts5Context fCx,
sqlite3_context pCx,
sqlite3_value argv[]
){
if( argv.length!=0 ){
throw new RuntimeException("fts5_inst: wrong number of args");
}
OutputPointer.Int32 pnInst = new OutputPointer.Int32();
OutputPointer.Int32 piPhrase = new OutputPointer.Int32();
OutputPointer.Int32 piCol = new OutputPointer.Int32();
OutputPointer.Int32 piOff = new OutputPointer.Int32();
String ret = new String();
int rc = ext.xInstCount(fCx, pnInst);
int nInst = pnInst.get();
int ii;
for(ii=0; rc==SQLITE_OK && ii<nInst; ii++){
ext.xInst(fCx, ii, piPhrase, piCol, piOff);
if( ii>0 ) ret += " ";
ret += "{"+piPhrase.get()+" "+piCol.get()+" "+piOff.get()+"}";
}
sqlite3_result_text(pCx, ret);
}
public void xDestroy(){ }
};
/*
** fts5_pinst(<fts>);
**
** Like SQL function fts5_inst(), except using the following
**
** xPhraseCount
** xPhraseFirst
** xPhraseNext
*/
fts5_extension_function fts5_pinst = new fts5_extension_function(){
@Override public void call(
Fts5ExtensionApi ext,
Fts5Context fCx,
sqlite3_context pCx,
sqlite3_value argv[]
){
if( argv.length!=0 ){
throw new RuntimeException("fts5_pinst: wrong number of args");
}
OutputPointer.Int32 piCol = new OutputPointer.Int32();
OutputPointer.Int32 piOff = new OutputPointer.Int32();
String ret = new String();
int rc = SQLITE_OK;
int nPhrase = ext.xPhraseCount(fCx);
int ii;
for(ii=0; rc==SQLITE_OK && ii<nPhrase; ii++){
Fts5PhraseIter pIter = new Fts5PhraseIter();
for(rc = ext.xPhraseFirst(fCx, ii, pIter, piCol, piOff);
rc==SQLITE_OK && piCol.get()>=0;
ext.xPhraseNext(fCx, pIter, piCol, piOff)
){
if( !ret.equals("") ) ret += " ";
ret += "{"+ii+" "+piCol.get()+" "+piOff.get()+"}";
}
}
if( rc!=SQLITE_OK ){
throw new RuntimeException("fts5_pinst: rc=" + rc);
}else{
sqlite3_result_text(pCx, ret);
}
}
public void xDestroy(){ }
};
/*
** fts5_pcolinst(<fts>);
**
** Like SQL function fts5_pinst(), except using the following
**
** xPhraseFirstColumn
** xPhraseNextColumn
*/
fts5_extension_function fts5_pcolinst = new fts5_extension_function(){
@Override public void call(
Fts5ExtensionApi ext,
Fts5Context fCx,
sqlite3_context pCx,
sqlite3_value argv[]
){
if( argv.length!=0 ){
throw new RuntimeException("fts5_pcolinst: wrong number of args");
}
OutputPointer.Int32 piCol = new OutputPointer.Int32();
String ret = new String();
int rc = SQLITE_OK;
int nPhrase = ext.xPhraseCount(fCx);
int ii;
for(ii=0; rc==SQLITE_OK && ii<nPhrase; ii++){
Fts5PhraseIter pIter = new Fts5PhraseIter();
for(rc = ext.xPhraseFirstColumn(fCx, ii, pIter, piCol);
rc==SQLITE_OK && piCol.get()>=0;
ext.xPhraseNextColumn(fCx, pIter, piCol)
){
if( !ret.equals("") ) ret += " ";
ret += "{"+ii+" "+piCol.get()+"}";
}
}
if( rc!=SQLITE_OK ){
throw new RuntimeException("fts5_pcolinst: rc=" + rc);
}else{
sqlite3_result_text(pCx, ret);
}
}
public void xDestroy(){ }
};
/*
** fts5_rowcount(<fts>);
*/
fts5_extension_function fts5_rowcount = new fts5_extension_function(){
@Override public void call(
Fts5ExtensionApi ext,
Fts5Context fCx,
sqlite3_context pCx,
sqlite3_value argv[]
){
if( argv.length!=0 ){
throw new RuntimeException("fts5_rowcount: wrong number of args");
}
OutputPointer.Int64 pnRow = new OutputPointer.Int64();
int rc = ext.xRowCount(fCx, pnRow);
if( rc==SQLITE_OK ){
sqlite3_result_int64(pCx, pnRow.get());
}else{
throw new RuntimeException("fts5_rowcount: rc=" + rc);
}
}
public void xDestroy(){ }
};
/*
** fts5_phrasesize(<fts>);
*/
fts5_extension_function fts5_phrasesize = new fts5_extension_function(){
@Override public void call(
Fts5ExtensionApi ext,
Fts5Context fCx,
sqlite3_context pCx,
sqlite3_value argv[]
){
if( argv.length!=1 ){
throw new RuntimeException("fts5_phrasesize: wrong number of args");
}
int iPhrase = sqlite3_value_int(argv[0]);
int sz = ext.xPhraseSize(fCx, iPhrase);
sqlite3_result_int(pCx, sz);
}
public void xDestroy(){ }
};
/*
** fts5_phrasehits(<fts>, <phrase-number>);
**
** Use the xQueryPhrase() API to determine how many hits, in total,
** there are for phrase <phrase-number> in the database.
*/
fts5_extension_function fts5_phrasehits = new fts5_extension_function(){
@Override public void call(
Fts5ExtensionApi ext,
Fts5Context fCx,
sqlite3_context pCx,
sqlite3_value argv[]
){
if( argv.length!=1 ){
throw new RuntimeException("fts5_phrasesize: wrong number of args");
}
int iPhrase = sqlite3_value_int(argv[0]);
int rc = SQLITE_OK;
class MyCallback implements Fts5ExtensionApi.XQueryPhraseCallback {
public int nRet = 0;
public int getRet() { return nRet; }
@Override
public int call(Fts5ExtensionApi fapi, Fts5Context cx){
OutputPointer.Int32 pnInst = new OutputPointer.Int32();
int rc = fapi.xInstCount(cx, pnInst);
nRet += pnInst.get();
return rc;
}
};
MyCallback xCall = new MyCallback();
rc = ext.xQueryPhrase(fCx, iPhrase, xCall);
if( rc!=SQLITE_OK ){
throw new RuntimeException("fts5_phrasehits: rc=" + rc);
}
sqlite3_result_int(pCx, xCall.getRet());
}
public void xDestroy(){ }
};
/*
** fts5_tokenize(<fts>, <text>)
*/
fts5_extension_function fts5_tokenize = new fts5_extension_function(){
@Override public void call(
Fts5ExtensionApi ext,
Fts5Context fCx,
sqlite3_context pCx,
sqlite3_value argv[]
){
if( argv.length!=1 ){
throw new RuntimeException("fts5_tokenize: wrong number of args");
}
byte[] utf8 = sqlite3_value_text(argv[0]);
int rc = SQLITE_OK;
class MyCallback implements XTokenizeCallback {
private List<String> myList = new ArrayList<String>();
public String getval() {
return String.join("+", myList);
}
@Override
public int call(int tFlags, byte[] txt, int iStart, int iEnd){
try {
String str = new String(txt, "UTF-8");
myList.add(str);
} catch (Exception e) {
}
return SQLITE_OK;
}
};
MyCallback xCall = new MyCallback();
ext.xTokenize(fCx, utf8, xCall);
sqlite3_result_text16(pCx, xCall.getval());
if( rc!=SQLITE_OK ){
throw new RuntimeException("fts5_tokenize: rc=" + rc);
}
}
public void xDestroy(){ }
};
fts5_api api = fts5_api.getInstanceForDb(db);
api.xCreateFunction("fts5_rowid", fts5_rowid);
@@ -286,8 +552,15 @@ public class TesterFts5 {
api.xCreateFunction("fts5_aux1", new fts5_aux());
api.xCreateFunction("fts5_aux2", new fts5_aux());
}
api.xCreateFunction("fts5_inst", fts5_inst);
api.xCreateFunction("fts5_pinst", fts5_pinst);
api.xCreateFunction("fts5_pcolinst", fts5_pcolinst);
api.xCreateFunction("fts5_rowcount", fts5_rowcount);
api.xCreateFunction("fts5_phrasesize", fts5_phrasesize);
api.xCreateFunction("fts5_phrasehits", fts5_phrasehits);
api.xCreateFunction("fts5_tokenize", fts5_tokenize);
}
/*
** Test of various Fts5ExtensionApi methods
*/
@@ -381,6 +654,11 @@ public class TesterFts5 {
affirm( e.getMessage().matches(".*column index out of range") );
}
do_execsql_test(db,
"SELECT rowid, fts5_rowcount(ft) FROM ft('z')",
"[1, 6, 2, 6, 3, 6]"
);
sqlite3_close_v2(db);
}
@@ -417,13 +695,135 @@ public class TesterFts5 {
"SELECT fts5_aux1(ft, b), fts5_aux1(ft) FROM ft('the')",
"[null, 1, 1, 2, 2, 3, 3, null]"
);
}
/*
** Test of various Fts5ExtensionApi methods
*/
private static void test4(){
/* Open db and populate an fts5 table */
sqlite3 db = createNewDb();
create_test_functions(db);
do_execsql_test(db,
"CREATE VIRTUAL TABLE ft USING fts5(a, b);" +
"INSERT INTO ft(a, b) VALUES('one two three', 'two three four');" +
"INSERT INTO ft(a, b) VALUES('two three four', 'three four five');" +
"INSERT INTO ft(a, b) VALUES('three four five', 'four five six');"
);
do_execsql_test(db,
"SELECT fts5_inst(ft) FROM ft('two')",
"[{0 0 1} {0 1 0}, {0 0 0}]"
);
do_execsql_test(db,
"SELECT fts5_inst(ft) FROM ft('four')",
"[{0 1 2}, {0 0 2} {0 1 1}, {0 0 1} {0 1 0}]"
);
do_execsql_test(db,
"SELECT fts5_inst(ft) FROM ft('a OR b OR four')",
"[{2 1 2}, {2 0 2} {2 1 1}, {2 0 1} {2 1 0}]"
);
do_execsql_test(db,
"SELECT fts5_inst(ft) FROM ft('two four')",
"[{0 0 1} {0 1 0} {1 1 2}, {0 0 0} {1 0 2} {1 1 1}]"
);
do_execsql_test(db,
"SELECT fts5_pinst(ft) FROM ft('two')",
"[{0 0 1} {0 1 0}, {0 0 0}]"
);
do_execsql_test(db,
"SELECT fts5_pinst(ft) FROM ft('four')",
"[{0 1 2}, {0 0 2} {0 1 1}, {0 0 1} {0 1 0}]"
);
do_execsql_test(db,
"SELECT fts5_pinst(ft) FROM ft('a OR b OR four')",
"[{2 1 2}, {2 0 2} {2 1 1}, {2 0 1} {2 1 0}]"
);
do_execsql_test(db,
"SELECT fts5_pinst(ft) FROM ft('two four')",
"[{0 0 1} {0 1 0} {1 1 2}, {0 0 0} {1 0 2} {1 1 1}]"
);
do_execsql_test(db,
"SELECT fts5_pcolinst(ft) FROM ft('two')",
"[{0 0} {0 1}, {0 0}]"
);
do_execsql_test(db,
"SELECT fts5_pcolinst(ft) FROM ft('four')",
"[{0 1}, {0 0} {0 1}, {0 0} {0 1}]"
);
do_execsql_test(db,
"SELECT fts5_pcolinst(ft) FROM ft('a OR b OR four')",
"[{2 1}, {2 0} {2 1}, {2 0} {2 1}]"
);
do_execsql_test(db,
"SELECT fts5_pcolinst(ft) FROM ft('two four')",
"[{0 0} {0 1} {1 1}, {0 0} {1 0} {1 1}]"
);
do_execsql_test(db,
"SELECT fts5_phrasesize(ft, 0) FROM ft('four five six') LIMIT 1;",
"[1]"
);
do_execsql_test(db,
"SELECT fts5_phrasesize(ft, 0) FROM ft('four + five + six') LIMIT 1;",
"[3]"
);
sqlite3_close_v2(db);
}
private static void test5(){
/* Open db and populate an fts5 table */
sqlite3 db = createNewDb();
create_test_functions(db);
do_execsql_test(db,
"CREATE VIRTUAL TABLE ft USING fts5(x, b);" +
"INSERT INTO ft(x) VALUES('one two three four five six seven eight');" +
"INSERT INTO ft(x) VALUES('one two one four one six one eight');" +
"INSERT INTO ft(x) VALUES('one two three four five six seven eight');"
);
do_execsql_test(db,
"SELECT fts5_phrasehits(ft, 0) FROM ft('one') LIMIT 1",
"[6]"
);
sqlite3_close_v2(db);
}
private static void test6(){
sqlite3 db = createNewDb();
create_test_functions(db);
do_execsql_test(db,
"CREATE VIRTUAL TABLE ft USING fts5(x, b);" +
"INSERT INTO ft(x) VALUES('one two three four five six seven eight');"
);
do_execsql_test(db,
"SELECT fts5_tokenize(ft, 'abc def ghi') FROM ft('one')",
"[abc+def+ghi]"
);
do_execsql_test(db,
"SELECT fts5_tokenize(ft, 'it''s BEEN a...') FROM ft('one')",
"[it+s+been+a]"
);
sqlite3_close_v2(db);
}
private static synchronized void runTests(){
test1();
test2();
test3();
test4();
test5();
test6();
}
public TesterFts5(){
@@ -13,11 +13,9 @@
*/
package org.sqlite.jni.fts5;
import org.sqlite.jni.annotation.*;
import org.sqlite.jni.*;
import org.sqlite.jni.capi.*;
/**
INCOMPLETE AND COMPLETELY UNTESTED.
A wrapper for communicating C-level (fts5_api*) instances with
Java. These wrappers do not own their associated pointer, they
simply provide a type-safe way to communicate it between Java and C
@@ -35,7 +33,6 @@ public final class fts5_api extends NativePointerHolder<fts5_api> {
*/
public static synchronized native fts5_api getInstanceForDb(@NotNull sqlite3 db);
@Canonical
public synchronized native int xCreateFunction(@NotNull String name,
@Nullable Object userData,
@NotNull fts5_extension_function xFunction);
@@ -12,8 +12,8 @@
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.fts5;
import org.sqlite.jni.sqlite3_context;
import org.sqlite.jni.sqlite3_value;
import org.sqlite.jni.capi.sqlite3_context;
import org.sqlite.jni.capi.sqlite3_value;
/**
JNI-level wrapper for C's fts5_extension_function type.
@@ -12,12 +12,10 @@
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.fts5;
import org.sqlite.jni.NativePointerHolder;
import org.sqlite.jni.capi.NativePointerHolder;
import org.sqlite.jni.annotation.NotNull;
/**
INCOMPLETE AND COMPLETELY UNTESTED.
A wrapper for communicating C-level (fts5_tokenizer*) instances with
Java. These wrappers do not own their associated pointer, they
simply provide a type-safe way to communicate it between Java and C
@@ -0,0 +1,82 @@
/*
** 2023-10-16
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the wrapper1 interface for sqlite3.
*/
package org.sqlite.jni.wrapper1;
import org.sqlite.jni.capi.CApi;
import org.sqlite.jni.annotation.*;
import org.sqlite.jni.capi.sqlite3_context;
import org.sqlite.jni.capi.sqlite3_value;
/**
EXPERIMENTAL/INCOMPLETE/UNTESTED
A SqlFunction implementation for aggregate functions. The T type
represents the type of data accumulated by this aggregate while it
works. e.g. a SUM()-like UDF might use Integer or Long and a
CONCAT()-like UDF might use a StringBuilder or a List<String>.
*/
public abstract class AggregateFunction<T> implements SqlFunction {
/**
As for the xStep() argument of the C API's
sqlite3_create_function(). If this function throws, the
exception is reported via sqlite3_result_error().
*/
public abstract void xStep(SqlFunction.Arguments args);
/**
As for the xFinal() argument of the C API's
sqlite3_create_function(). If this function throws, it is
translated into sqlite3_result_error().
Note that the passed-in object will not actually contain any
arguments for xFinal() but will contain the context object needed
for setting the call's result or error state.
*/
public abstract void xFinal(SqlFunction.Arguments args);
/**
Optionally override to be notified when the UDF is finalized by
SQLite.
*/
public void xDestroy() {}
/** Per-invocation state for the UDF. */
private final SqlFunction.PerContextState<T> map =
new SqlFunction.PerContextState<>();
/**
To be called from the implementation's xStep() method, as well
as the xValue() and xInverse() methods of the {@link WindowFunction}
subclass, to fetch the current per-call UDF state. On the
first call to this method for any given sqlite3_context
argument, the context is set to the given initial value. On all other
calls, the 2nd argument is ignored.
@see SQLFunction.PerContextState#getAggregateState
*/
protected final ValueHolder<T> getAggregateState(SqlFunction.Arguments args, T initialValue){
return map.getAggregateState(args, initialValue);
}
/**
To be called from the implementation's xFinal() method to fetch
the final state of the UDF and remove its mapping.
see SQLFunction.PerContextState#takeAggregateState
*/
protected final T takeAggregateState(SqlFunction.Arguments args){
return map.takeAggregateState(args);
}
}
@@ -0,0 +1,301 @@
/*
** 2023-10-16
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the wrapper1 interface for sqlite3.
*/
package org.sqlite.jni.wrapper1;
import org.sqlite.jni.capi.CApi;
import org.sqlite.jni.capi.sqlite3_context;
import org.sqlite.jni.capi.sqlite3_value;
/**
Base marker interface for SQLite's three types of User-Defined SQL
Functions (UDFs): Scalar, Aggregate, and Window functions.
*/
public interface SqlFunction {
/**
The Arguments type is an abstraction on top of the lower-level
UDF function argument types. It provides _most_ of the functionality
of the lower-level interface, insofar as possible without "leaking"
those types into this API.
*/
public final static class Arguments implements Iterable<SqlFunction.Arguments.Arg>{
private final sqlite3_context cx;
private final sqlite3_value args[];
public final int length;
/**
Must be passed the context and arguments for the UDF call this
object is wrapping. Intended to be used by internal proxy
classes which "convert" the lower-level interface into this
package's higher-level interface, e.g. ScalarAdapter and
AggregateAdapter.
Passing null for the args is equivalent to passing a length-0
array.
*/
Arguments(sqlite3_context cx, sqlite3_value args[]){
this.cx = cx;
this.args = args==null ? new sqlite3_value[0] : args;;
this.length = this.args.length;
}
/**
Wrapper for a single SqlFunction argument. Primarily intended
for use with the Arguments class's Iterable interface.
*/
public final static class Arg {
private final Arguments a;
private final int ndx;
/* Only for use by the Arguments class. */
private Arg(Arguments a, int ndx){
this.a = a;
this.ndx = ndx;
}
/** Returns this argument's index in its parent argument list. */
public int getIndex(){return ndx;}
public int getInt(){return a.getInt(ndx);}
public long getInt64(){return a.getInt64(ndx);}
public double getDouble(){return a.getDouble(ndx);}
public byte[] getBlob(){return a.getBlob(ndx);}
public byte[] getText(){return a.getText(ndx);}
public String getText16(){return a.getText16(ndx);}
public int getBytes(){return a.getBytes(ndx);}
public int getBytes16(){return a.getBytes16(ndx);}
public Object getObject(){return a.getObject(ndx);}
public <T> T getObjectCasted(Class<T> type){ return a.getObjectCasted(ndx, type); }
public int getType(){return a.getType(ndx);}
public Object getAuxData(){return a.getAuxData(ndx);}
public void setAuxData(Object o){a.setAuxData(ndx, o);}
}
@Override
public java.util.Iterator<SqlFunction.Arguments.Arg> iterator(){
final Arg[] proxies = new Arg[args.length];
for( int i = 0; i < args.length; ++i ){
proxies[i] = new Arg(this, i);
}
return java.util.Arrays.stream(proxies).iterator();
}
/**
Returns the sqlite3_value at the given argument index or throws
an IllegalArgumentException exception if ndx is out of range.
*/
private sqlite3_value valueAt(int ndx){
if(ndx<0 || ndx>=args.length){
throw new IllegalArgumentException(
"SQL function argument index "+ndx+" is out of range."
);
}
return args[ndx];
}
sqlite3_context getContext(){return cx;}
public int getArgCount(){ return args.length; }
public int getInt(int arg){return CApi.sqlite3_value_int(valueAt(arg));}
public long getInt64(int arg){return CApi.sqlite3_value_int64(valueAt(arg));}
public double getDouble(int arg){return CApi.sqlite3_value_double(valueAt(arg));}
public byte[] getBlob(int arg){return CApi.sqlite3_value_blob(valueAt(arg));}
public byte[] getText(int arg){return CApi.sqlite3_value_text(valueAt(arg));}
public String getText16(int arg){return CApi.sqlite3_value_text16(valueAt(arg));}
public int getBytes(int arg){return CApi.sqlite3_value_bytes(valueAt(arg));}
public int getBytes16(int arg){return CApi.sqlite3_value_bytes16(valueAt(arg));}
public Object getObject(int arg){return CApi.sqlite3_value_java_object(valueAt(arg));}
public <T> T getObjectCasted(int arg, Class<T> type){
return CApi.sqlite3_value_java_casted(valueAt(arg), type);
}
public int getType(int arg){return CApi.sqlite3_value_type(valueAt(arg));}
public int getSubtype(int arg){return CApi.sqlite3_value_subtype(valueAt(arg));}
public int getNumericType(int arg){return CApi.sqlite3_value_numeric_type(valueAt(arg));}
public int getNoChange(int arg){return CApi.sqlite3_value_nochange(valueAt(arg));}
public boolean getFromBind(int arg){return CApi.sqlite3_value_frombind(valueAt(arg));}
public int getEncoding(int arg){return CApi.sqlite3_value_encoding(valueAt(arg));}
public void resultInt(int v){ CApi.sqlite3_result_int(cx, v); }
public void resultInt64(long v){ CApi.sqlite3_result_int64(cx, v); }
public void resultDouble(double v){ CApi.sqlite3_result_double(cx, v); }
public void resultError(String msg){CApi.sqlite3_result_error(cx, msg);}
public void resultError(Exception e){CApi.sqlite3_result_error(cx, e);}
public void resultErrorTooBig(){CApi.sqlite3_result_error_toobig(cx);}
public void resultErrorCode(int rc){CApi.sqlite3_result_error_code(cx, rc);}
public void resultObject(Object o){CApi.sqlite3_result_java_object(cx, o);}
public void resultNull(){CApi.sqlite3_result_null(cx);}
public void resultArg(int argNdx){CApi.sqlite3_result_value(cx, valueAt(argNdx));}
public void resultZeroBlob(long n){
// Throw on error? If n is too big,
// sqlite3_result_error_toobig() is automatically called.
CApi.sqlite3_result_zeroblob64(cx, n);
}
public void resultBlob(byte[] blob){CApi.sqlite3_result_blob(cx, blob);}
public void resultText(byte[] utf8){CApi.sqlite3_result_text(cx, utf8);}
public void resultText(String txt){CApi.sqlite3_result_text(cx, txt);}
public void resultText16(byte[] utf16){CApi.sqlite3_result_text16(cx, utf16);}
public void resultText16(String txt){CApi.sqlite3_result_text16(cx, txt);}
public void setAuxData(int arg, Object o){
/* From the API docs: https://www.sqlite.org/c3ref/get_auxdata.html
The value of the N parameter to these interfaces should be
non-negative. Future enhancements may make use of negative N
values to define new kinds of function caching behavior.
*/
valueAt(arg);
CApi.sqlite3_set_auxdata(cx, arg, o);
}
public Object getAuxData(int arg){
valueAt(arg);
return CApi.sqlite3_get_auxdata(cx, arg);
}
}
/**
PerContextState assists aggregate and window functions in
managing their accumulator state across calls to the UDF's
callbacks.
<p>T must be of a type which can be legally stored as a value in
java.util.HashMap<KeyType,T>.
<p>If a given aggregate or window function is called multiple times
in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)...,
then the clients need some way of knowing which call is which so
that they can map their state between their various UDF callbacks
and reset it via xFinal(). This class takes care of such
mappings.
<p>This class works by mapping
sqlite3_context.getAggregateContext() to a single piece of
state, of a client-defined type (the T part of this class), which
persists across a "matching set" of the UDF's callbacks.
<p>This class is a helper providing commonly-needed functionality
- it is not required for use with aggregate or window functions.
Client UDFs are free to perform such mappings using custom
approaches. The provided {@link AggregateFunction} and {@link
WindowFunction} classes use this.
*/
public static final class PerContextState<T> {
private final java.util.Map<Long,ValueHolder<T>> map
= new java.util.HashMap<>();
/**
Should be called from a UDF's xStep(), xValue(), and xInverse()
methods, passing it that method's first argument and an initial
value for the persistent state. If there is currently no
mapping for the given context within the map, one is created
using the given initial value, else the existing one is used
and the 2nd argument is ignored. It returns a ValueHolder<T>
which can be used to modify that state directly without
requiring that the client update the underlying map's entry.
<p>The caller is obligated to eventually call
takeAggregateState() to clear the mapping.
*/
public ValueHolder<T> getAggregateState(SqlFunction.Arguments args, T initialValue){
final Long key = args.getContext().getAggregateContext(true);
ValueHolder<T> rc = null==key ? null : map.get(key);
if( null==rc ){
map.put(key, rc = new ValueHolder<>(initialValue));
}
return rc;
}
/**
Should be called from a UDF's xFinal() method and passed that
method's first argument. This function removes the value
associated with with the arguments' aggregate context from the
map and returns it, returning null if no other UDF method has
been called to set up such a mapping. The latter condition will
be the case if a UDF is used in a statement which has no result
rows.
*/
public T takeAggregateState(SqlFunction.Arguments args){
final ValueHolder<T> h = map.remove(args.getContext().getAggregateContext(false));
return null==h ? null : h.value;
}
}
/**
Internal-use adapter for wrapping this package's ScalarFunction
for use with the org.sqlite.jni.capi.ScalarFunction interface.
*/
static final class ScalarAdapter extends org.sqlite.jni.capi.ScalarFunction {
final ScalarFunction impl;
ScalarAdapter(ScalarFunction impl){
this.impl = impl;
}
/**
Proxies this.impl.xFunc(), adapting the call arguments to that
function's signature. If the proxy throws, it's translated to
sqlite_result_error() with the exception's message.
*/
public void xFunc(sqlite3_context cx, sqlite3_value[] args){
try{
impl.xFunc( new SqlFunction.Arguments(cx, args) );
}catch(Exception e){
CApi.sqlite3_result_error(cx, e);
}
}
public void xDestroy(){
impl.xDestroy();
}
}
/**
Internal-use adapter for wrapping this package's AggregateFunction
for use with the org.sqlite.jni.capi.AggregateFunction interface.
*/
static final class AggregateAdapter extends org.sqlite.jni.capi.AggregateFunction {
final AggregateFunction impl;
AggregateAdapter(AggregateFunction impl){
this.impl = impl;
}
/**
Proxies this.impl.xStep(), adapting the call arguments to that
function's signature. If the proxied function throws, it is
translated to sqlite_result_error() with the exception's
message.
*/
public void xStep(sqlite3_context cx, sqlite3_value[] args){
try{
impl.xStep( new SqlFunction.Arguments(cx, args) );
}catch(Exception e){
CApi.sqlite3_result_error(cx, e);
}
}
/**
As for the xFinal() argument of the C API's sqlite3_create_function().
If the proxied function throws, it is translated into a sqlite3_result_error().
*/
public void xFinal(sqlite3_context cx){
try{
impl.xFinal( new SqlFunction.Arguments(cx, null) );
}catch(Exception e){
CApi.sqlite3_result_error(cx, e);
}
}
public void xDestroy(){
impl.xDestroy();
}
}
}
@@ -0,0 +1,218 @@
/*
** 2023-10-09
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the wrapper1 interface for sqlite3.
*/
package org.sqlite.jni.wrapper1;
import java.nio.charset.StandardCharsets;
import static org.sqlite.jni.capi.CApi.*;
import org.sqlite.jni.capi.CApi;
import org.sqlite.jni.capi.sqlite3;
import org.sqlite.jni.capi.sqlite3_stmt;
import org.sqlite.jni.capi.OutputPointer;
/**
This class represents a database connection, analog to the C-side
sqlite3 class but with added argument validation, exceptions, and
similar "smoothing of sharp edges" to make the API safe to use from
Java. It also acts as a namespace for other types for which
individual instances are tied to a specific database connection.
*/
public final class Sqlite implements AutoCloseable {
private sqlite3 db;
//! Used only by the open() factory functions.
private Sqlite(sqlite3 db){
this.db = db;
}
/**
Returns a newly-opened db connection or throws SqliteException if
opening fails. All arguments are as documented for
sqlite3_open_v2().
Design question: do we want static factory functions or should
this be reformulated as a constructor?
*/
public static Sqlite open(String filename, int flags, String vfsName){
final OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
final int rc = sqlite3_open_v2(filename, out, flags, vfsName);
final sqlite3 n = out.take();
if( 0!=rc ){
if( null==n ) throw new SqliteException(rc);
final SqliteException ex = new SqliteException(n);
n.close();
throw ex;
}
return new Sqlite(n);
}
public static Sqlite open(String filename, int flags){
return open(filename, flags, null);
}
public static Sqlite open(String filename){
return open(filename, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, null);
}
@Override public void close(){
if(null!=this.db){
this.db.close();
this.db = null;
}
}
/**
Returns this object's underlying native db handle, or null if
this instance has been closed. This is very specifically not
public.
*/
sqlite3 nativeHandle(){ return this.db; }
private sqlite3 affirmOpen(){
if( null==db || 0==db.getNativePointer() ){
throw new IllegalArgumentException("This database instance is closed.");
}
return this.db;
}
// private byte[] stringToUtf8(String s){
// return s==null ? null : s.getBytes(StandardCharsets.UTF_8);
// }
private void affirmRcOk(int rc){
if( 0!=rc ){
throw new SqliteException(db);
}
}
/**
Corresponds to the sqlite3_stmt class. Use Sqlite.prepare() to
create new instances.
*/
public final class Stmt implements AutoCloseable {
private Sqlite _db = null;
private sqlite3_stmt stmt = null;
/** Only called by the prepare() factory functions. */
Stmt(Sqlite db, sqlite3_stmt stmt){
this._db = db;
this.stmt = stmt;
}
sqlite3_stmt nativeHandle(){
return stmt;
}
private sqlite3_stmt affirmOpen(){
if( null==stmt || 0==stmt.getNativePointer() ){
throw new IllegalArgumentException("This Stmt has been finalized.");
}
return stmt;
}
/**
Corresponds to sqlite3_finalize(), but we cannot override the
name finalize() here because this one requires a different
signature. It does not throw on error here because "destructors
do not throw." If it returns non-0, the object is still
finalized.
*/
public int finalizeStmt(){
int rc = 0;
if( null!=stmt ){
sqlite3_finalize(stmt);
stmt = null;
}
return rc;
}
@Override public void close(){
finalizeStmt();
}
/**
Throws if rc is any value other than 0, SQLITE_ROW, or
SQLITE_DONE, else returns rc.
*/
private int checkRc(int rc){
switch(rc){
case 0:
case SQLITE_ROW:
case SQLITE_DONE: return rc;
default:
throw new SqliteException(this);
}
}
/**
Works like sqlite3_step() but throws SqliteException for any
result other than 0, SQLITE_ROW, or SQLITE_DONE.
*/
public int step(){
return checkRc(sqlite3_step(affirmOpen()));
}
public Sqlite db(){ return this._db; }
/**
Works like sqlite3_reset() but throws on error.
*/
public void reset(){
checkRc(sqlite3_reset(affirmOpen()));
}
public void clearBindings(){
sqlite3_clear_bindings( affirmOpen() );
}
}
/**
prepare() TODOs include:
- overloads taking byte[] and ByteBuffer.
- multi-statement processing, like CApi.sqlite3_prepare_multi()
but using a callback specific to the higher-level Stmt class
rather than the sqlite3_stmt class.
*/
public Stmt prepare(String sql, int prepFlags){
final OutputPointer.sqlite3_stmt out = new OutputPointer.sqlite3_stmt();
final int rc = sqlite3_prepare_v3(affirmOpen(), sql, prepFlags, out);
affirmRcOk(rc);
return new Stmt(this, out.take());
}
public Stmt prepare(String sql){
return prepare(sql, 0);
}
public void createFunction(String name, int nArg, int eTextRep, ScalarFunction f ){
int rc = CApi.sqlite3_create_function(affirmOpen(), name, nArg, eTextRep,
new SqlFunction.ScalarAdapter(f));
if( 0!=rc ) throw new SqliteException(db);
}
public void createFunction(String name, int nArg, ScalarFunction f){
this.createFunction(name, nArg, CApi.SQLITE_UTF8, f);
}
public void createFunction(String name, int nArg, int eTextRep, AggregateFunction f ){
int rc = CApi.sqlite3_create_function(affirmOpen(), name, nArg, eTextRep,
new SqlFunction.AggregateAdapter(f));
if( 0!=rc ) throw new SqliteException(db);
}
public void createFunction(String name, int nArg, AggregateFunction f){
this.createFunction(name, nArg, CApi.SQLITE_UTF8, f);
}
}
@@ -0,0 +1,82 @@
/*
** 2023-10-09
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the wrapper1 interface for sqlite3.
*/
package org.sqlite.jni.wrapper1;
import static org.sqlite.jni.capi.CApi.*;
import org.sqlite.jni.capi.sqlite3;
/**
A wrapper for communicating C-level (sqlite3*) instances with
Java. These wrappers do not own their associated pointer, they
simply provide a type-safe way to communicate it between Java
and C via JNI.
*/
public final class SqliteException extends java.lang.RuntimeException {
int errCode = SQLITE_ERROR;
int xerrCode = SQLITE_ERROR;
int errOffset = -1;
int sysErrno = 0;
/**
Records the given error string and uses SQLITE_ERROR for both the
error code and extended error code.
*/
public SqliteException(String msg){
super(msg);
}
/**
Uses sqlite3_errstr(sqlite3ResultCode) for the error string and
sets both the error code and extended error code to the given
value.
*/
public SqliteException(int sqlite3ResultCode){
super(sqlite3_errstr(sqlite3ResultCode));
errCode = xerrCode = sqlite3ResultCode;
}
/**
Records the current error state of db (which must not be null and
must refer to an opened db object). Note that this does NOT close
the db.
Design note: closing the db on error is likely only useful during
a failed db-open operation, and the place(s) where that can
happen are inside this library, not client-level code.
*/
SqliteException(sqlite3 db){
super(sqlite3_errmsg(db));
errCode = sqlite3_errcode(db);
xerrCode = sqlite3_extended_errcode(db);
errOffset = sqlite3_error_offset(db);
sysErrno = sqlite3_system_errno(db);
}
/**
Records the current error state of db (which must not be null and
must refer to an open database).
*/
public SqliteException(Sqlite db){
this(db.nativeHandle());
}
public SqliteException(Sqlite.Stmt stmt){
this( stmt.db() );
}
public int errcode(){ return errCode; }
public int extendedErrcode(){ return xerrCode; }
public int errorOffset(){ return errOffset; }
public int systemErrno(){ return sysErrno; }
}
@@ -0,0 +1,584 @@
/*
** 2023-10-09
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains a set of tests for the sqlite3 JNI bindings.
*/
package org.sqlite.jni.wrapper1;
//import static org.sqlite.jni.capi.CApi.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.sqlite.jni.capi.*;
/**
An annotation for Tester2 tests which we do not want to run in
reflection-driven test mode because either they are not suitable
for multi-threaded threaded mode or we have to control their execution
order.
*/
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD})
@interface ManualTest{}
/**
Annotation for Tester2 tests which mark those which must be skipped
in multi-threaded mode.
*/
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD})
@interface SingleThreadOnly{}
public class Tester2 implements Runnable {
//! True when running in multi-threaded mode.
private static boolean mtMode = false;
//! True to sleep briefly between tests.
private static boolean takeNaps = false;
//! True to shuffle the order of the tests.
private static boolean shuffle = false;
//! True to dump the list of to-run tests to stdout.
private static boolean listRunTests = false;
//! True to squelch all out() and outln() output.
private static boolean quietMode = false;
//! Total number of runTests() calls.
private static int nTestRuns = 0;
//! List of test*() methods to run.
private static List<java.lang.reflect.Method> testMethods = null;
//! List of exceptions collected by run()
private static List<Exception> listErrors = new ArrayList<>();
private static final class Metrics {
//! Number of times createNewDb() (or equivalent) is invoked.
volatile int dbOpen = 0;
}
//! Instance ID.
private Integer tId;
Tester2(Integer id){
tId = id;
}
static final Metrics metrics = new Metrics();
public static synchronized void outln(){
if( !quietMode ){
System.out.println("");
}
}
public static synchronized void outPrefix(){
if( !quietMode ){
System.out.print(Thread.currentThread().getName()+": ");
}
}
public static synchronized void outln(Object val){
if( !quietMode ){
outPrefix();
System.out.println(val);
}
}
public static synchronized void out(Object val){
if( !quietMode ){
System.out.print(val);
}
}
@SuppressWarnings("unchecked")
public static synchronized void out(Object... vals){
if( !quietMode ){
outPrefix();
for(Object v : vals) out(v);
}
}
@SuppressWarnings("unchecked")
public static synchronized void outln(Object... vals){
if( !quietMode ){
out(vals); out("\n");
}
}
static volatile int affirmCount = 0;
public static synchronized int affirm(Boolean v, String comment){
++affirmCount;
if( false ) assert( v /* prefer assert over exception if it's enabled because
the JNI layer sometimes has to suppress exceptions,
so they might be squelched on their way back to the
top. */);
if( !v ) throw new RuntimeException(comment);
return affirmCount;
}
public static void affirm(Boolean v){
affirm(v, "Affirmation failed.");
}
public static void execSql(Sqlite db, String[] sql){
execSql(db, String.join("", sql));
}
public static int execSql(Sqlite dbw, boolean throwOnError, String sql){
final sqlite3 db = dbw.nativeHandle();
OutputPointer.Int32 oTail = new OutputPointer.Int32();
final byte[] sqlUtf8 = sql.getBytes(StandardCharsets.UTF_8);
int pos = 0, n = 1;
byte[] sqlChunk = sqlUtf8;
int rc = 0;
sqlite3_stmt stmt = null;
final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt();
while(pos < sqlChunk.length){
if(pos > 0){
sqlChunk = Arrays.copyOfRange(sqlChunk, pos,
sqlChunk.length);
}
if( 0==sqlChunk.length ) break;
rc = CApi.sqlite3_prepare_v2(db, sqlChunk, outStmt, oTail);
if(throwOnError) affirm(0 == rc);
else if( 0!=rc ) break;
pos = oTail.value;
stmt = outStmt.take();
if( null == stmt ){
// empty statement was parsed.
continue;
}
affirm(0 != stmt.getNativePointer());
while( CApi.SQLITE_ROW == (rc = CApi.sqlite3_step(stmt)) ){
}
CApi.sqlite3_finalize(stmt);
affirm(0 == stmt.getNativePointer());
if(0!=rc && CApi.SQLITE_ROW!=rc && CApi.SQLITE_DONE!=rc){
break;
}
}
CApi.sqlite3_finalize(stmt);
if(CApi.SQLITE_ROW==rc || CApi.SQLITE_DONE==rc) rc = 0;
if( 0!=rc && throwOnError){
throw new SqliteException(db);
}
return rc;
}
static void execSql(Sqlite db, String sql){
execSql(db, true, sql);
}
@SingleThreadOnly /* because it's thread-agnostic */
private void test1(){
affirm(CApi.sqlite3_libversion_number() == CApi.SQLITE_VERSION_NUMBER);
}
/* Copy/paste/rename this to add new tests. */
private void _testTemplate(){
//final sqlite3 db = createNewDb();
//sqlite3_stmt stmt = prepare(db,"SELECT 1");
//sqlite3_finalize(stmt);
//sqlite3_close_v2(db);
}
private void nap() throws InterruptedException {
if( takeNaps ){
Thread.sleep(java.util.concurrent.ThreadLocalRandom.current().nextInt(3, 17), 0);
}
}
Sqlite openDb(String name){
final Sqlite db = Sqlite.open(name, CApi.SQLITE_OPEN_READWRITE|
CApi.SQLITE_OPEN_CREATE|
CApi.SQLITE_OPEN_EXRESCODE);
++metrics.dbOpen;
return db;
}
Sqlite openDb(){ return openDb(":memory:"); }
void testOpenDb1(){
Sqlite db = openDb();
affirm( 0!=db.nativeHandle().getNativePointer() );
db.close();
affirm( null==db.nativeHandle() );
SqliteException ex = null;
try {
db = openDb("/no/such/dir/.../probably");
}catch(SqliteException e){
ex = e;
}
affirm( ex!=null );
affirm( ex.errcode() != 0 );
affirm( ex.extendedErrcode() != 0 );
affirm( ex.errorOffset() < 0 );
// there's no reliable way to predict what ex.systemErrno() might be
}
void testPrepare1(){
try (Sqlite db = openDb()) {
Sqlite.Stmt stmt = db.prepare("SELECT 1");
affirm( null!=stmt.nativeHandle() );
affirm( CApi.SQLITE_ROW == stmt.step() );
affirm( CApi.SQLITE_DONE == stmt.step() );
stmt.reset();
affirm( CApi.SQLITE_ROW == stmt.step() );
affirm( CApi.SQLITE_DONE == stmt.step() );
affirm( 0 == stmt.finalizeStmt() );
affirm( null==stmt.nativeHandle() );
stmt = db.prepare("SELECT 1");
affirm( CApi.SQLITE_ROW == stmt.step() );
affirm( 0 == stmt.finalizeStmt() )
/* getting a non-0 out of sqlite3_finalize() is tricky */;
affirm( null==stmt.nativeHandle() );
}
}
void testUdfScalar(){
final ValueHolder<Integer> xDestroyCalled = new ValueHolder<>(0);
try (Sqlite db = openDb()) {
execSql(db, "create table t(a); insert into t(a) values(1),(2),(3)");
final ValueHolder<Integer> vh = new ValueHolder<>(0);
final ScalarFunction f = new ScalarFunction(){
public void xFunc(SqlFunction.Arguments args){
for( SqlFunction.Arguments.Arg arg : args ){
vh.value += arg.getInt();
}
}
public void xDestroy(){
++xDestroyCalled.value;
}
};
db.createFunction("myfunc", -1, f);
execSql(db, "select myfunc(1,2,3)");
affirm( 6 == vh.value );
vh.value = 0;
execSql(db, "select myfunc(-1,-2,-3)");
affirm( -6 == vh.value );
affirm( 0 == xDestroyCalled.value );
}
affirm( 1 == xDestroyCalled.value );
}
void testUdfAggregate(){
final ValueHolder<Integer> xDestroyCalled = new ValueHolder<>(0);
final ValueHolder<Integer> vh = new ValueHolder<>(0);
try (Sqlite db = openDb()) {
execSql(db, "create table t(a); insert into t(a) values(1),(2),(3)");
final AggregateFunction f = new AggregateFunction<Integer>(){
public void xStep(SqlFunction.Arguments args){
final ValueHolder<Integer> agg = this.getAggregateState(args, 0);
for( SqlFunction.Arguments.Arg arg : args ){
agg.value += arg.getInt();
}
}
public void xFinal(SqlFunction.Arguments args){
final Integer v = this.takeAggregateState(args);
if( null==v ) args.resultNull();
else args.resultInt(v);
vh.value = v;
}
public void xDestroy(){
++xDestroyCalled.value;
}
};
db.createFunction("myagg", -1, f);
execSql(db, "select myagg(a) from t");
affirm( 6 == vh.value );
affirm( 0 == xDestroyCalled.value );
}
affirm( 1 == xDestroyCalled.value );
}
private void runTests(boolean fromThread) throws Exception {
List<java.lang.reflect.Method> mlist = testMethods;
affirm( null!=mlist );
if( shuffle ){
mlist = new ArrayList<>( testMethods.subList(0, testMethods.size()) );
java.util.Collections.shuffle(mlist);
}
if( listRunTests ){
synchronized(this.getClass()){
if( !fromThread ){
out("Initial test"," list: ");
for(java.lang.reflect.Method m : testMethods){
out(m.getName()+" ");
}
outln();
outln("(That list excludes some which are hard-coded to run.)");
}
out("Running"," tests: ");
for(java.lang.reflect.Method m : mlist){
out(m.getName()+" ");
}
outln();
}
}
for(java.lang.reflect.Method m : mlist){
nap();
try{
m.invoke(this);
}catch(java.lang.reflect.InvocationTargetException e){
outln("FAILURE: ",m.getName(),"(): ", e.getCause());
throw e;
}
}
synchronized( this.getClass() ){
++nTestRuns;
}
}
public void run() {
try {
runTests(0!=this.tId);
}catch(Exception e){
synchronized( listErrors ){
listErrors.add(e);
}
}finally{
affirm( CApi.sqlite3_java_uncache_thread() );
affirm( !CApi.sqlite3_java_uncache_thread() );
}
}
/**
Runs the basic sqlite3 JNI binding sanity-check suite.
CLI flags:
-q|-quiet: disables most test output.
-t|-thread N: runs the tests in N threads
concurrently. Default=1.
-r|-repeat N: repeats the tests in a loop N times, each one
consisting of the -thread value's threads.
-shuffle: randomizes the order of most of the test functions.
-naps: sleep small random intervals between tests in order to add
some chaos for cross-thread contention.
-list-tests: outputs the list of tests being run, minus some
which are hard-coded. This is noisy in multi-threaded mode.
-fail: forces an exception to be thrown during the test run. Use
with -shuffle to make its appearance unpredictable.
-v: emit some developer-mode info at the end.
*/
public static void main(String[] args) throws Exception {
Integer nThread = 1;
boolean doSomethingForDev = false;
Integer nRepeat = 1;
boolean forceFail = false;
boolean sqlLog = false;
boolean configLog = false;
boolean squelchTestOutput = false;
for( int i = 0; i < args.length; ){
String arg = args[i++];
if(arg.startsWith("-")){
arg = arg.replaceFirst("-+","");
if(arg.equals("v")){
doSomethingForDev = true;
//listBoundMethods();
}else if(arg.equals("t") || arg.equals("thread")){
nThread = Integer.parseInt(args[i++]);
}else if(arg.equals("r") || arg.equals("repeat")){
nRepeat = Integer.parseInt(args[i++]);
}else if(arg.equals("shuffle")){
shuffle = true;
}else if(arg.equals("list-tests")){
listRunTests = true;
}else if(arg.equals("fail")){
forceFail = true;
}else if(arg.equals("sqllog")){
sqlLog = true;
}else if(arg.equals("configlog")){
configLog = true;
}else if(arg.equals("naps")){
takeNaps = true;
}else if(arg.equals("q") || arg.equals("quiet")){
squelchTestOutput = true;
}else{
throw new IllegalArgumentException("Unhandled flag:"+arg);
}
}
}
if( sqlLog ){
if( CApi.sqlite3_compileoption_used("ENABLE_SQLLOG") ){
final ConfigSqllogCallback log = new ConfigSqllogCallback() {
@Override public void call(sqlite3 db, String msg, int op){
switch(op){
case 0: outln("Opening db: ",db); break;
case 1: outln("SQL ",db,": ",msg); break;
case 2: outln("Closing db: ",db); break;
}
}
};
int rc = CApi.sqlite3_config( log );
affirm( 0==rc );
rc = CApi.sqlite3_config( (ConfigSqllogCallback)null );
affirm( 0==rc );
rc = CApi.sqlite3_config( log );
affirm( 0==rc );
}else{
outln("WARNING: -sqllog is not active because library was built ",
"without SQLITE_ENABLE_SQLLOG.");
}
}
if( configLog ){
final ConfigLogCallback log = new ConfigLogCallback() {
@Override public void call(int code, String msg){
outln("ConfigLogCallback: ",ResultCode.getEntryForInt(code),": ", msg);
};
};
int rc = CApi.sqlite3_config( log );
affirm( 0==rc );
rc = CApi.sqlite3_config( (ConfigLogCallback)null );
affirm( 0==rc );
rc = CApi.sqlite3_config( log );
affirm( 0==rc );
}
quietMode = squelchTestOutput;
outln("If you just saw warning messages regarding CallStaticObjectMethod, ",
"you are very likely seeing the side effects of a known openjdk8 ",
"bug. It is unsightly but does not affect the library.");
{
// Build list of tests to run from the methods named test*().
testMethods = new ArrayList<>();
int nSkipped = 0;
for(final java.lang.reflect.Method m : Tester2.class.getDeclaredMethods()){
final String name = m.getName();
if( name.equals("testFail") ){
if( forceFail ){
testMethods.add(m);
}
}else if( !m.isAnnotationPresent( ManualTest.class ) ){
if( nThread>1 && m.isAnnotationPresent( SingleThreadOnly.class ) ){
if( 0==nSkipped++ ){
out("Skipping tests in multi-thread mode:");
}
out(" "+name+"()");
}else if( name.startsWith("test") ){
testMethods.add(m);
}
}
}
if( nSkipped>0 ) out("\n");
}
final long timeStart = System.currentTimeMillis();
int nLoop = 0;
switch( CApi.sqlite3_threadsafe() ){ /* Sanity checking */
case 0:
affirm( CApi.SQLITE_ERROR==CApi.sqlite3_config( CApi.SQLITE_CONFIG_SINGLETHREAD ),
"Could not switch to single-thread mode." );
affirm( CApi.SQLITE_ERROR==CApi.sqlite3_config( CApi.SQLITE_CONFIG_MULTITHREAD ),
"Could switch to multithread mode." );
affirm( CApi.SQLITE_ERROR==CApi.sqlite3_config( CApi.SQLITE_CONFIG_SERIALIZED ),
"Could not switch to serialized threading mode." );
outln("This is a single-threaded build. Not using threads.");
nThread = 1;
break;
case 1:
case 2:
affirm( 0==CApi.sqlite3_config( CApi.SQLITE_CONFIG_SINGLETHREAD ),
"Could not switch to single-thread mode." );
affirm( 0==CApi.sqlite3_config( CApi.SQLITE_CONFIG_MULTITHREAD ),
"Could not switch to multithread mode." );
affirm( 0==CApi.sqlite3_config( CApi.SQLITE_CONFIG_SERIALIZED ),
"Could not switch to serialized threading mode." );
break;
default:
affirm( false, "Unhandled SQLITE_THREADSAFE value." );
}
outln("libversion_number: ",
CApi.sqlite3_libversion_number(),"\n",
CApi.sqlite3_libversion(),"\n",CApi.SQLITE_SOURCE_ID,"\n",
"SQLITE_THREADSAFE=",CApi.sqlite3_threadsafe());
final boolean showLoopCount = (nRepeat>1 && nThread>1);
if( showLoopCount ){
outln("Running ",nRepeat," loop(s) with ",nThread," thread(s) each.");
}
if( takeNaps ) outln("Napping between tests is enabled.");
for( int n = 0; n < nRepeat; ++n ){
++nLoop;
if( showLoopCount ) out((1==nLoop ? "" : " ")+nLoop);
if( nThread<=1 ){
new Tester2(0).runTests(false);
continue;
}
Tester2.mtMode = true;
final ExecutorService ex = Executors.newFixedThreadPool( nThread );
for( int i = 0; i < nThread; ++i ){
ex.submit( new Tester2(i), i );
}
ex.shutdown();
try{
ex.awaitTermination(nThread*200, java.util.concurrent.TimeUnit.MILLISECONDS);
ex.shutdownNow();
}catch (InterruptedException ie){
ex.shutdownNow();
Thread.currentThread().interrupt();
}
if( !listErrors.isEmpty() ){
quietMode = false;
outln("TEST ERRORS:");
Exception err = null;
for( Exception e : listErrors ){
e.printStackTrace();
if( null==err ) err = e;
}
if( null!=err ) throw err;
}
}
if( showLoopCount ) outln();
quietMode = false;
final long timeEnd = System.currentTimeMillis();
outln("Tests done. Metrics across ",nTestRuns," total iteration(s):");
outln("\tAssertions checked: ",affirmCount);
outln("\tDatabases opened: ",metrics.dbOpen);
if( doSomethingForDev ){
CApi.sqlite3_jni_internal_details();
}
affirm( 0==CApi.sqlite3_release_memory(1) );
CApi.sqlite3_shutdown();
int nMethods = 0;
int nNatives = 0;
int nCanonical = 0;
final java.lang.reflect.Method[] declaredMethods =
CApi.class.getDeclaredMethods();
for(java.lang.reflect.Method m : declaredMethods){
final int mod = m.getModifiers();
if( 0!=(mod & java.lang.reflect.Modifier.STATIC) ){
final String name = m.getName();
if(name.startsWith("sqlite3_")){
++nMethods;
if( 0!=(mod & java.lang.reflect.Modifier.NATIVE) ){
++nNatives;
}
}
}
}
outln("\tCApi.sqlite3_*() methods: "+
nMethods+" total, with "+
nNatives+" native, "+
(nMethods - nNatives)+" Java"
);
outln("\tTotal test time = "
+(timeEnd - timeStart)+"ms");
}
}
@@ -0,0 +1,25 @@
/*
** 2023-10-16
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains a set of tests for the sqlite3 JNI bindings.
*/
package org.sqlite.jni.wrapper1;
/**
A helper class which simply holds a single value. Its primary use
is for communicating values out of anonymous classes, as doing so
requires a "final" reference.
*/
public class ValueHolder<T> {
public T value;
public ValueHolder(){}
public ValueHolder(T v){value = v;}
}
+5
View File
@@ -1061,6 +1061,11 @@ static sqlite3_module lsm1Module = {
lsm1Rollback, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
+2 -1
View File
@@ -1475,7 +1475,8 @@ static sqlite3_module amatchModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
+2 -1
View File
@@ -411,7 +411,8 @@ int sqlite3BinfoRegister(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
return sqlite3_create_module(db, "sqlite_btreeinfo", &binfo_module, 0);
}
+5
View File
@@ -409,6 +409,11 @@ static sqlite3_module carrayModule = {
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadow */
0 /* xIntegrity */
};
/*
+2 -1
View File
@@ -939,7 +939,8 @@ static sqlite3_module closureModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
+2 -1
View File
@@ -470,7 +470,8 @@ static sqlite3_module completionModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
+10
View File
@@ -897,6 +897,11 @@ static sqlite3_module CsvModule = {
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
#ifdef SQLITE_TEST
@@ -929,6 +934,11 @@ static sqlite3_module CsvModuleFauxWrite = {
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_TEST */
+1
View File
@@ -293,6 +293,7 @@ static sqlite3_module explainModule = {
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
+1
View File
@@ -983,6 +983,7 @@ static int fsdirRegister(sqlite3 *db){
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
int rc = sqlite3_create_module(db, "fsdir", &fsdirModule, 0);
+2 -1
View File
@@ -1058,7 +1058,8 @@ static sqlite3_module deltaparsevtabModule = {
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0
/* xShadowName */ 0,
/* xIntegrity */ 0
};
+5
View File
@@ -1165,6 +1165,11 @@ static sqlite3_module fuzzerModule = {
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
+1
View File
@@ -396,6 +396,7 @@ static sqlite3_module memstatModule = {
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
+2 -1
View File
@@ -248,7 +248,8 @@ static sqlite3_module prefixesModule = {
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0
/* xShadowName */ 0,
/* xIntegrity */ 0
};
/*
+2 -1
View File
@@ -439,7 +439,8 @@ static sqlite3_module qpvtabModule = {
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0
/* xShadowName */ 0,
/* xIntegrity */ 0
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
+2 -1
View File
@@ -557,7 +557,8 @@ static sqlite3_module seriesModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
+5
View File
@@ -3009,6 +3009,11 @@ static sqlite3_module spellfix1Module = {
0, /* xRollback */
0, /* xFindMethod */
spellfix1Rename, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
/*
+1
View File
@@ -314,6 +314,7 @@ static sqlite3_module stmtModule = {
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
+2 -1
View File
@@ -249,7 +249,8 @@ static sqlite3_module templatevtabModule = {
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0
/* xShadowName */ 0,
/* xIntegrity */ 0
};
+2 -1
View File
@@ -1351,7 +1351,8 @@ static int createUnionVtab(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
int rc;
+5
View File
@@ -775,6 +775,11 @@ static sqlite3_module VfsStatModule = {
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
/*

Some files were not shown because too many files have changed in this diff Show More