Expose sqlite3_stmt_explain(), sqlite3_stmt_isexplain(), and sqlite3_stmt_readonly() to JNI.

FossilOrigin-Name: 208b786afe16eafaf0ce791f319a5e05f733a7b71ce1c542e1b83481b013ec38
This commit is contained in:
stephan 2023-09-01 06:50:17 +00:00
parent 0d3f0a9c11
commit a4b47b034c
7 changed files with 143 additions and 49 deletions

View File

@ -113,14 +113,6 @@
# define SQLITE_THREADSAFE 1
#endif
/*
** 2023-08-25: initial attempts at running with SQLITE_THREADSAFE=0
** lead to as-yet-uninvestigated bad reference errors from JNI.
*/
#if 0 && SQLITE_THREADSAFE==0
# error "This code currently requires SQLITE_THREADSAFE!=0."
#endif
/**********************************************************************/
/* SQLITE_USE_... */
#ifndef SQLITE_USE_URI
@ -132,9 +124,11 @@
** Which sqlite3.c we're using needs to be configurable to enable
** building against a custom copy, e.g. the SEE variant. We have to
** include sqlite3.c, as opposed to sqlite3.h, in order to get access
** to SQLITE_MAX_... and friends. This increases the rebuild time
** considerably but we need this in order to keep the exported values
** of SQLITE_MAX_... and SQLITE_LIMIT_... in sync with the C build.
** to some interal details like SQLITE_MAX_... and friends. This
** increases the rebuild time considerably but we need this in order
** to access some internal functionality and keep the to-Java-exported
** values of SQLITE_MAX_... and SQLITE_LIMIT_... in sync with the C
** build.
*/
#ifndef SQLITE_C
# define SQLITE_C sqlite3.c
@ -452,7 +446,8 @@ struct S3JniDb {
#endif
S3JniDb * pNext /* Next entry in SJG.perDb.aFree */;
};
#define S3JniDb_clientdata_key "S3JniDb"
static const char * const S3JniDb_clientdata_key = "S3JniDb";
#define S3JniDb_from_clientdata(pDb) \
(pDb ? sqlite3_get_clientdata(pDb, S3JniDb_clientdata_key) : 0)
@ -1463,36 +1458,23 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, jobject jDb){
** Returns the S3JniDb object for the given org.sqlite.jni.sqlite3
** object, or NULL if jDb is NULL, no pointer can be extracted
** from it, or no matching entry can be found.
**
** Requires locking the S3JniDb mutex.
*/
static S3JniDb * S3JniDb__from_java(JNIEnv * const env, jobject jDb){
S3JniDb * s = 0;
sqlite3 * pDb = 0;
S3JniDb_mutex_enter;
if( jDb ) pDb = PtrGet_sqlite3(jDb);
s = S3JniDb_from_clientdata(pDb);
S3JniDb_mutex_leave;
return s;
}
#define S3JniDb_from_java(jObject) S3JniDb__from_java(env,(jObject))
static S3JniDb * S3JniDb__from_java_unlocked(JNIEnv * const env, jobject jDb){
sqlite3 * pDb = 0;
S3JniDb_mutex_assertLocker;
if( jDb ) pDb = PtrGet_sqlite3(jDb);
return S3JniDb_from_clientdata(pDb);
}
#define S3JniDb_from_java_unlocked(JDB) S3JniDb__from_java_unlocked(env, (JDB))
/*
** S3JniDb finalizer for use with sqlite3_set_clientdata().
*/
static void S3JniDb_xDestroy(void *p){
S3JniDeclLocal_env;
S3JniDb * const ps = p;
assert( !ps->pNext );
assert( !ps->pNext && "Else ps is already in the free-list.");
S3JniDb_set_aside(ps);
}
@ -2012,6 +1994,11 @@ static void udf_xInverse(sqlite3_context* cx, int argc,
JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jobject pStmt, jint n){ \
return (jint)CName(PtrGet_sqlite3_stmt(pStmt), (int)n); \
}
/** Create a trivial JNI wrapper for (boolish-int CName(sqlite3_stmt*)). */
#define WRAP_BOOL_STMT(JniNameSuffix,CName) \
JniDecl(jboolean,JniNameSuffix)(JniArgsEnvClass, jobject pStmt){ \
return CName(PtrGet_sqlite3_stmt(pStmt)) ? JNI_TRUE : JNI_FALSE; \
}
/** Create a trivial JNI wrapper for (jstring CName(sqlite3_stmt*,int)). */
#define WRAP_STR_STMT_INT(JniNameSuffix,CName) \
JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass, jobject pStmt, jint ndx){ \
@ -2061,6 +2048,9 @@ WRAP_INT_DB(1preupdate_1depth, sqlite3_preupdate_depth)
WRAP_INT_INT(1release_1memory, sqlite3_release_memory)
WRAP_INT_INT(1sleep, sqlite3_sleep)
WRAP_MUTF8_VOID(1sourceid, sqlite3_sourceid)
WRAP_INT_STMT_INT(1stmt_1explain, sqlite3_stmt_explain)
WRAP_INT_STMT(1stmt_1isexplain, sqlite3_stmt_isexplain)
WRAP_BOOL_STMT(1stmt_1readonly, sqlite3_stmt_readonly)
WRAP_INT_VOID(1threadsafe, sqlite3_threadsafe)
WRAP_INT_DB(1total_1changes, sqlite3_total_changes)
WRAP_INT64_DB(1total_1changes64, sqlite3_total_changes64)
@ -2513,7 +2503,7 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)(
int rc = 0;
S3JniDb_mutex_enter;
ps = S3JniDb_from_java_unlocked(jDb);
ps = S3JniDb_from_java(jDb);
if( !ps ){
S3JniDb_mutex_leave;
return SQLITE_MISUSE;
@ -2655,7 +2645,7 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env,
S3JniHook * pHook;
S3JniDb_mutex_enter;
ps = S3JniDb_from_java_unlocked(jDb);
ps = S3JniDb_from_java(jDb);
if( !ps ){
s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0);
S3JniDb_mutex_leave;
@ -2878,7 +2868,7 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(),
S3JniDb * ps;
S3JniDb_mutex_enter;
ps = S3JniDb_from_java_unlocked(jDb);
ps = S3JniDb_from_java(jDb);
if( !ps ){
rc = SQLITE_MISUSE;
}else{

View File

@ -1643,6 +1643,30 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1status64
JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1step
(JNIEnv *, jclass, jobject);
/*
* Class: org_sqlite_jni_SQLite3Jni
* Method: sqlite3_stmt_explain
* Signature: (Lorg/sqlite/jni/sqlite3_stmt;I)I
*/
JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1stmt_1explain
(JNIEnv *, jclass, jobject, jint);
/*
* Class: org_sqlite_jni_SQLite3Jni
* Method: sqlite3_stmt_isexplain
* Signature: (Lorg/sqlite/jni/sqlite3_stmt;)I
*/
JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1stmt_1isexplain
(JNIEnv *, jclass, jobject);
/*
* Class: org_sqlite_jni_SQLite3Jni
* Method: sqlite3_stmt_readonly
* Signature: (Lorg/sqlite/jni/sqlite3_stmt;)Z
*/
JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1stmt_1readonly
(JNIEnv *, jclass, jobject);
/*
* Class: org_sqlite_jni_SQLite3Jni
* Method: sqlite3_strglob

View File

@ -1325,6 +1325,17 @@ public final class SQLite3Jni {
@Canonical
public static native int sqlite3_step(@NotNull sqlite3_stmt stmt);
@Canonical
public static native int sqlite3_stmt_explain(
@NotNull sqlite3_stmt stmt, int op
);
@Canonical
public static native int sqlite3_stmt_isexplain(@NotNull sqlite3_stmt stmt);
@Canonical
public static native boolean sqlite3_stmt_readonly(@NotNull sqlite3_stmt stmt);
/**
Internal impl of the public sqlite3_strglob() method. Neither
argument may be NULL and both MUST be NUL-terminated UTF-8.

View File

@ -192,7 +192,7 @@ public class Tester1 implements Runnable {
static sqlite3_stmt prepare(sqlite3 db, boolean throwOnError, String sql){
final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt();
int rc = sqlite3_prepare(db, sql, outStmt);
int rc = sqlite3_prepare_v2(db, sql, outStmt);
if( throwOnError ){
affirm( 0 == rc );
}
@ -203,9 +203,11 @@ public class Tester1 implements Runnable {
}
return rv;
}
static sqlite3_stmt prepare(sqlite3 db, String sql){
return prepare(db, true, sql);
}
private void showCompileOption(){
int i = 0;
String optName;
@ -260,6 +262,7 @@ public class Tester1 implements Runnable {
affirm(0 == rc);
sqlite3_stmt stmt = outStmt.take();
affirm(0 != stmt.getNativePointer());
affirm( !sqlite3_stmt_readonly(stmt) );
affirm( db == sqlite3_db_handle(stmt) );
rc = sqlite3_step(stmt);
if( SQLITE_DONE != rc ){
@ -360,6 +363,7 @@ public class Tester1 implements Runnable {
affirm(sqlite3_changes64(db) > changes64);
affirm(sqlite3_total_changes64(db) > changesT64);
stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
affirm( sqlite3_stmt_readonly(stmt) );
int total2 = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){
total2 += sqlite3_column_int(stmt, 0);
@ -1362,7 +1366,7 @@ public class Tester1 implements Runnable {
private void testColumnMetadata(){
sqlite3 db = createNewDb();
final sqlite3 db = createNewDb();
execSql(db, new String[] {
"CREATE TABLE t(a duck primary key not null collate noCase); ",
"INSERT INTO t(a) VALUES(1),(2),(3);"
@ -1397,7 +1401,7 @@ public class Tester1 implements Runnable {
}
private void testTxnState(){
sqlite3 db = createNewDb();
final sqlite3 db = createNewDb();
affirm( SQLITE_TXN_NONE == sqlite3_txn_state(db, null) );
execSql(db, "BEGIN;");
affirm( SQLITE_TXN_NONE == sqlite3_txn_state(db, null) );
@ -1410,6 +1414,32 @@ public class Tester1 implements Runnable {
sqlite3_close_v2(db);
}
private void testExplain(){
final sqlite3 db = createNewDb();
sqlite3_stmt stmt = prepare(db,"SELECT 1");
affirm( 0 == sqlite3_stmt_isexplain(stmt) );
int rc = sqlite3_stmt_explain(stmt, 1);
affirm( 1 == sqlite3_stmt_isexplain(stmt) );
rc = sqlite3_stmt_explain(stmt, 2);
affirm( 2 == sqlite3_stmt_isexplain(stmt) );
sqlite3_finalize(stmt);
sqlite3_close_v2(db);
}
/* 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);
}
@ManualTest /* we really only want to run this test manually. */
private void testSleep(){
out("Sleeping briefly... ");

View File

@ -1,19 +1,58 @@
/**
This package houses a JNI binding to the SQLite3 C API.
<p>The docs are in progress.
<p>The primary interfaces are in {@link
org.sqlite.jni.SQLite3Jni}.</p>
<p>The primary interfaces are in {@link org.sqlite.jni.SQLite3Jni}.
<h1>API Goals and Requirements</h1>
<ul>
<li>A 1-to-1(-ish) mapping of the C API to Java via JNI, insofar
as cross-language semantics allow for. A closely-related goal is
that <a href='https://sqlite.org/c3ref/intro.html'>the C
documentation</a> should be usable as-is, insofar as possible,
for most of the JNI binding. As a rule, undocumented symbols
behave as documented for their C API counterpart, and only
semantic differences are documented here.</li>
<li>Support Java as far back as version 8 (2014).</li>
<li>Environment-independent. Should work everywhere both Java and
SQLite3 do.</li>
<li>No 3rd-party dependencies beyond the JDK. That includes no
build-level dependencies for specific IDEs and toolchains. We
welcome the addition of build files for arbitrary environments
insofar as they neither interfere with each other nor become a
maintenance burden for the sqlite developers.</li>
</ul>
<h2>Non-Goals</h2>
<ul>
<li>Creation of high-level OO wrapper APIs. Clients are free to
create them off of the C-style API.</li>
<li>Support for mixed-mode operation, where client code accesses
SQLite both via the Java-side API and the C API via their own
native code. In such cases, proxy functionalities (primarily
callback handler wrappers of all sorts) may fail because the
C-side use of the SQLite APIs will bypass those proxies.</li>
</ul>
<h1>State of this API</h1>
<p>As of version 3.43, this software is in "tech preview" form. We
tentatively plan to stamp it as stable with the 3.44 release.
tentatively plan to stamp it as stable with the 3.44 release.</p>
<h1>Threading Considerations</h1>
<p>This API is, if built with SQLITE_THREADSAFE set to 1 or 2,
thread-safe, insofar as the C API guarantees, with some addenda:
thread-safe, insofar as the C API guarantees, with some addenda:</p>
<ul>
@ -39,11 +78,11 @@
<p>Any number of threads may, of course, create and use any number
of database handles they wish. Care only needs to be taken when
those handles or their associated resources cross threads, or...
those handles or their associated resources cross threads, or...</p>
<p>When built with SQLITE_THREADSAFE=0 then no threading guarantees
are provided and multi-threaded use of the library will provoke
undefined behavior.
undefined behavior.</p>
*/
package org.sqlite.jni;

View File

@ -1,5 +1,5 @@
C Export\ssqlite3_txn_state()\sto\sJNI.
D 2023-08-31T19:35:59.499
C Expose\ssqlite3_stmt_explain(),\ssqlite3_stmt_isexplain(),\sand\ssqlite3_stmt_readonly()\sto\sJNI.
D 2023-09-01T06:50:17.074
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -237,8 +237,8 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3
F ext/jni/GNUmakefile b6ae6d04cc33f2300ab7177bd5db1ecfbc8627f76fee6aec441f980e57594291
F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9
F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
F ext/jni/src/c/sqlite3-jni.c 6f66f0855f4adc908151d1d4072dd51423f70311b308e7040ca30ecd6d26224b
F ext/jni/src/c/sqlite3-jni.h d82c8b503d19a539bdc5ad6d495e8aaf0d04d3e330ab75a7a402734194908590
F ext/jni/src/c/sqlite3-jni.c 6342a09eb10c46835b4b3ea84f1cfaeb9e7e720c0c15c7405fb18bfc62f9cc0f
F ext/jni/src/c/sqlite3-jni.h 3546b35d31fe3c13697edf3a3c35871941283de1d4f189154456ca61fd1282f6
F ext/jni/src/org/sqlite/jni/AbstractCollationCallback.java 95e88ba04f4aac51ffec65693e878e234088b2f21b387f4e4285c8b72b33e436
F ext/jni/src/org/sqlite/jni/AggregateFunction.java 7312486bc65fecdb91753c0a4515799194e031f45edbe16a6373cea18f404dc4
F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java d00a2409ab76cae168927e2ca6a7ffbd0621a42547cce88768b4eeebc13827e0
@ -262,10 +262,10 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c
F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java 16042be9d072a26dbb2f1b1b63e7639989b747bb80d2bd667ba4f7555f56a825
F ext/jni/src/org/sqlite/jni/SQLFunction.java 544a875d33fd160467d82e2397ac33157b29971d715a821a4fad3c899113ee8c
F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java c2748ab52856075b053a55b317988d95dc7fb4d3d42520f8c33573effe1cd185
F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 04163ebc06213bef77ac9c1cdb66d10e6f1fdcc1fc7a8628629529e1ee2c5940
F ext/jni/src/org/sqlite/jni/SQLite3Jni.java af43be2c0795e22ca489d9237697cf743a9338860ba937ea689cd1810bf737ee
F ext/jni/src/org/sqlite/jni/ScalarFunction.java 6d387bb499fbe3bc13c53315335233dbf6a0c711e8fa7c521683219b041c614c
F ext/jni/src/org/sqlite/jni/TableColumnMetadata.java 54511b4297fa28dcb3f49b24035e34ced10e3fd44fd0e458e784f4d6b0096dab
F ext/jni/src/org/sqlite/jni/Tester1.java ca145b97e0cd5c39d5745dfa5ab7cf595a89b4b1f40dfb1928f4f6e79d3c0851
F ext/jni/src/org/sqlite/jni/Tester1.java 891255bbaac87893fafe3b0109f6dbc9329c2accfd5b5c7a0b2bd9d874810ede
F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629
F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 641926b05a772c2c05c842a81aa839053ba4a13b78ef04b402f5705d060c6246
F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java be2bc96ff4f56b3c1fd18ae7dba9b207b25b6c123b8a5fd2f7aaf3cc208d8b7d
@ -279,7 +279,7 @@ F ext/jni/src/org/sqlite/jni/annotation/package-info.java f66bfb621c6494e67c03ed
F ext/jni/src/org/sqlite/jni/fts5_api.java ee47f1837d32968f7bb62278c7504c0fb572a68ec107371b714578312e9f734b
F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c
F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java a92c2e55bda492e4c76d48ddc73369bcc0d5e8727940840f9339e3292ea58fa7
F ext/jni/src/org/sqlite/jni/package-info.java 73f7821c240e4d116f164e87b613c5836b8a33ce2666967a29d9acb1ced7ca92
F ext/jni/src/org/sqlite/jni/package-info.java 016cda6582ffb7af976455360f0730bf075a0ef4ef9d561f7646790c951e1504
F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c388ec3bc1de547f098a0217158fc
F ext/jni/src/org/sqlite/jni/sqlite3_context.java 66ca95ce904044263a4aff684abe262d56f73e6b06bca6cf650761d79d7779ad
F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 78e6d1b95ac600a9475e9db4623f69449322b0c93d1bd4e1616e76ed547ed9fc
@ -2116,8 +2116,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 2e2bb841ef8b53266ec48d8c3408d6396f29f17922e858eac1b4f12f4adeb05e
R 46c546f77881736213c45575c57e77e9
P d732f71d0a292dbb493f79f7c6ecd8a4effbfbc91453b1c54bdd9becf2d75bdb
R d6cbdc791e6b94495eeb2607a5aa6bb1
U stephan
Z f333ee5f1318a9468223872999f97afe
Z d7105d8e3faee08846ef7fd47243b17f
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
d732f71d0a292dbb493f79f7c6ecd8a4effbfbc91453b1c54bdd9becf2d75bdb
208b786afe16eafaf0ce791f319a5e05f733a7b71ce1c542e1b83481b013ec38