Add many more high-level wrappers to the JNI wrapper1 API. Correct the JNI bindings of sqlite3_strglob/strlike() to compare as the core lib does if their glob argument is NULL and the other is not.

FossilOrigin-Name: 55c4b1dc402b358d53d65fa1f6ec063e9e38e95c81a05d98dae3cb58c52ef55c
This commit is contained in:
stephan 2023-10-23 01:34:17 +00:00
parent 96aa4d344d
commit 90a5617b55
5 changed files with 310 additions and 34 deletions

View File

@ -2182,7 +2182,9 @@ S3JniApi(sqlite3_aggregate_context(),jlong,1aggregate_1context)(
return S3JniCast_P2L(p);
}
/* Central auto-extension handler. */
/*
** Central auto-extension runner for auto-extensions created in Java.
*/
static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr,
const struct sqlite3_api_routines *ignored){
int rc = 0;
@ -3537,9 +3539,16 @@ S3JniApi(sqlite3_errmsg(),jstring,1errmsg)(
S3JniApi(sqlite3_errstr(),jstring,1errstr)(
JniArgsEnvClass, jint rcCode
){
jstring const rv = (*env)->NewStringUTF(env, sqlite3_errstr((int)rcCode))
/* We know these values to be plain ASCII, so pose no MUTF-8
** incompatibility */;
jstring rv;
const char * z = sqlite3_errstr((int)rcCode);
if( !z ){
/* This hypothetically cannot happen, but we'll behave like the
low-level library would in such a case... */
z = "unknown error";
}
rv = (*env)->NewStringUTF(env, z)
/* We know these values to be plain ASCII, so pose no MUTF-8
** incompatibility */;
s3jni_oom_check( rv );
return rv;
}
@ -4615,7 +4624,7 @@ static int s3jni_strlike_glob(int isLike, JNIEnv *const env,
jbyteArray baG, jbyteArray baT, jint escLike){
int rc = 0;
jbyte * const pG = s3jni_jbyteArray_bytes(baG);
jbyte * const pT = pG ? s3jni_jbyteArray_bytes(baT) : 0;
jbyte * const pT = s3jni_jbyteArray_bytes(baT);
/* Note that we're relying on the byte arrays having been
NUL-terminated on the Java side. */

View File

@ -32,6 +32,34 @@ public final class Sqlite implements AutoCloseable {
public static final int OPEN_READWRITE = CApi.SQLITE_OPEN_READWRITE;
public static final int OPEN_CREATE = CApi.SQLITE_OPEN_CREATE;
public static final int OPEN_EXRESCODE = CApi.SQLITE_OPEN_EXRESCODE;
public static final int TXN_NONE = CApi.SQLITE_TXN_NONE;
public static final int TXN_READ = CApi.SQLITE_TXN_READ;
public static final int TXN_WRITE = CApi.SQLITE_TXN_WRITE;
public static final int STATUS_MEMORY_USED = CApi.SQLITE_STATUS_MEMORY_USED;
public static final int STATUS_PAGECACHE_USED = CApi.SQLITE_STATUS_PAGECACHE_USED;
public static final int STATUS_PAGECACHE_OVERFLOW = CApi.SQLITE_STATUS_PAGECACHE_OVERFLOW;
public static final int STATUS_MALLOC_SIZE = CApi.SQLITE_STATUS_MALLOC_SIZE;
public static final int STATUS_PARSER_STACK = CApi.SQLITE_STATUS_PARSER_STACK;
public static final int STATUS_PAGECACHE_SIZE = CApi.SQLITE_STATUS_PAGECACHE_SIZE;
public static final int STATUS_MALLOC_COUNT = CApi.SQLITE_STATUS_MALLOC_COUNT;
public static final int LIMIT_LENGTH = CApi.SQLITE_LIMIT_LENGTH;
public static final int LIMIT_SQL_LENGTH = CApi.SQLITE_LIMIT_SQL_LENGTH;
public static final int LIMIT_COLUMN = CApi.SQLITE_LIMIT_COLUMN;
public static final int LIMIT_EXPR_DEPTH = CApi.SQLITE_LIMIT_EXPR_DEPTH;
public static final int LIMIT_COMPOUND_SELECT = CApi.SQLITE_LIMIT_COMPOUND_SELECT;
public static final int LIMIT_VDBE_OP = CApi.SQLITE_LIMIT_VDBE_OP;
public static final int LIMIT_FUNCTION_ARG = CApi.SQLITE_LIMIT_FUNCTION_ARG;
public static final int LIMIT_ATTACHED = CApi.SQLITE_LIMIT_ATTACHED;
public static final int LIMIT_LIKE_PATTERN_LENGTH = CApi.SQLITE_LIMIT_LIKE_PATTERN_LENGTH;
public static final int LIMIT_VARIABLE_NUMBER = CApi.SQLITE_LIMIT_VARIABLE_NUMBER;
public static final int LIMIT_TRIGGER_DEPTH = CApi.SQLITE_LIMIT_TRIGGER_DEPTH;
public static final int LIMIT_WORKER_THREADS = CApi.SQLITE_LIMIT_WORKER_THREADS;
public static final int PREPARE_PERSISTENT = CApi.SQLITE_PREPARE_PERSISTENT;
public static final int PREPARE_NORMALIZE = CApi.SQLITE_PREPARE_NORMALIZE;
public static final int PREPARE_NO_VTAB = CApi.SQLITE_PREPARE_NO_VTAB;
//! Used only by the open() factory functions.
private Sqlite(sqlite3 db){
@ -67,6 +95,33 @@ public final class Sqlite implements AutoCloseable {
return open(filename, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, null);
}
public static String libVersion(){
return CApi.sqlite3_libversion();
}
public static int libVersionNumber(){
return CApi.sqlite3_libversion_number();
}
public static String libSourceId(){
return CApi.sqlite3_sourceid();
}
/**
As per sqlite3_status64(), but returns its current and high-water
results as a two-element array. Throws if the first argument is
not one of the STATUS_... constants.
*/
public long[] libStatus(int op, boolean resetStats){
org.sqlite.jni.capi.OutputPointer.Int64 pCurrent =
new org.sqlite.jni.capi.OutputPointer.Int64();
org.sqlite.jni.capi.OutputPointer.Int64 pHighwater =
new org.sqlite.jni.capi.OutputPointer.Int64();
final int rc = CApi.sqlite3_status64(op, pCurrent, pHighwater, resetStats);
checkRc(rc);
return new long[] {pCurrent.value, pHighwater.value};
}
@Override public void close(){
if(null!=this.db){
this.db.close();
@ -74,6 +129,22 @@ public final class Sqlite implements AutoCloseable {
}
}
/**
Returns the value of the native library's build-time value of the
SQLITE_THREADSAFE build option.
*/
public static int libThreadsafe(){
return CApi.sqlite3_threadsafe();
}
public static boolean strglob(String glob, String txt){
return 0==CApi.sqlite3_strglob(glob, txt);
}
public static boolean strlike(String glob, String txt, char escChar){
return 0==CApi.sqlite3_strlike(glob, txt, escChar);
}
/**
Returns this object's underlying native db handle, or null if
this instance has been closed. This is very specifically not
@ -94,17 +165,21 @@ public final class Sqlite implements AutoCloseable {
/**
If rc!=0, throws an SqliteException. If this db is currently
opened, the error state is extracted from it, else only the
string form of rc is used.
opened and has non-0 sqlite3_errcode(), the error state is
extracted from it, else only the string form of rc is used. It is
the caller's responsibility to filter out non-error codes such as
SQLITE_ROW and SQLITE_DONE before calling this.
*/
private void affirmRcOk(int rc){
private void checkRc(int rc){
if( 0!=rc ){
if( null==db ) throw new SqliteException(rc);
if( null==db || 0==sqlite3_errcode(db)) throw new SqliteException(rc);
else throw new SqliteException(db);
}
}
/**
prepFlags must be 0 or a bitmask of the PREPARE_... constants.
prepare() TODOs include:
- overloads taking byte[] and ByteBuffer.
@ -116,8 +191,20 @@ public final class Sqlite implements AutoCloseable {
public Stmt prepare(String sql, int prepFlags){
final OutputPointer.sqlite3_stmt out = new OutputPointer.sqlite3_stmt();
final int rc = sqlite3_prepare_v3(thisDb(), sql, prepFlags, out);
affirmRcOk(rc);
return new Stmt(this, out.take());
checkRc(rc);
final sqlite3_stmt q = out.take();
if( null==q ){
/* The C-level API treats input which is devoid of SQL
statements (e.g. all comments or an empty string) as success
but returns a NULL sqlite3_stmt object. In higher-level APIs,
wrapping a "successful NULL" object that way is tedious to
use because it forces clients and/or wrapper-level code to
check for that unusual case. In practice, higher-level
bindings are generally better-served by treating empty SQL
input as an error. */
throw new IllegalArgumentException("Input contains no SQL statements.");
}
return new Stmt(this, q);
}
public Stmt prepare(String sql){
@ -154,6 +241,183 @@ public final class Sqlite implements AutoCloseable {
this.createFunction(name, nArg, CApi.SQLITE_UTF8, f);
}
public long changes(){
return CApi.sqlite3_changes64(thisDb());
}
public long totalChanges(){
return CApi.sqlite3_total_changes64(thisDb());
}
public long lastInsertRowId(){
return CApi.sqlite3_last_insert_rowid(thisDb());
}
public void setLastInsertRowId(long rowId){
CApi.sqlite3_set_last_insert_rowid(thisDb(), rowId);
}
public void interrupt(){
CApi.sqlite3_interrupt(thisDb());
}
public boolean isInterrupted(){
return CApi.sqlite3_is_interrupted(thisDb());
}
public boolean isAutoCommit(){
return CApi.sqlite3_get_autocommit(thisDb());
}
public void setBusyTimeout(int ms){
checkRc(CApi.sqlite3_busy_timeout(thisDb(), ms));
}
/**
Analog to sqlite3_txn_state(). Returns one of TXN_NONE, TXN_READ,
or TXN_WRITE to denote this database's current transaction state
for the given schema name (or the most restrictive state of any
schema if zSchema is null).
*/
public int transactionState(String zSchema){
return CApi.sqlite3_txn_state(thisDb(), zSchema);
}
/**
Analog to sqlite3_db_name(). Returns null if passed an unknown
index.
*/
public String dbName(int dbNdx){
return CApi.sqlite3_db_name(thisDb(), dbNdx);
}
/**
Analog to sqlite3_db_filename(). Returns null if passed an
unknown db name.
*/
public String dbFileName(String dbName){
return CApi.sqlite3_db_filename(thisDb(), dbName);
}
/**
Analog to the variant of sqlite3_db_config() for configuring the
SQLITE_DBCONFIG_MAINDBNAME option. Throws on error.
*/
public void setMainDbName(String name){
checkRc(
CApi.sqlite3_db_config(thisDb(), CApi.SQLITE_DBCONFIG_MAINDBNAME,
name)
);
}
/**
Analog to sqlite3_db_readonly() but throws an SqliteException
with result code SQLITE_NOTFOUND if given an unknown database
name.
*/
public boolean readOnly(String dbName){
final int rc = CApi.sqlite3_db_readonly(thisDb(), dbName);
if( 0==rc ) return false;
else if( rc>0 ) return true;
throw new SqliteException(CApi.SQLITE_NOTFOUND);
}
/**
Analog to sqlite3_db_release_memory().
*/
public void releaseMemory(){
CApi.sqlite3_db_release_memory(thisDb());
}
/**
Analog to sqlite3_release_memory().
*/
public static int releaseMemory(int n){
return CApi.sqlite3_release_memory(n);
}
/**
Analog to sqlite3_limit(). limitId must be one of the
LIMIT_... constants.
Returns the old limit for the given option. If newLimit is
negative, it returns the old limit without modifying the limit.
If sqlite3_limit() returns a negative value, this function throws
an SqliteException with the SQLITE_RANGE result code but no
further error info (because that case does not qualify as a
db-level error). Such errors may indicate an invalid argument
value or an invalid range for newLimit (the underlying function
does not differentiate between those).
*/
public int limit(int limitId, int newLimit){
final int rc = CApi.sqlite3_limit(thisDb(), limitId, newLimit);
if( rc<0 ){
throw new SqliteException(CApi.SQLITE_RANGE);
}
return rc;
}
/**
Analog to sqlite3_errstr().
*/
static String errstr(int resultCode){
return CApi.sqlite3_errstr(resultCode);
}
/**
A wrapper object for use with tableColumnMetadata(). They are
created and populated only via that interface.
*/
public final class TableColumnMetadata {
Boolean pNotNull = null;
Boolean pPrimaryKey = null;
Boolean pAutoinc = null;
String pzCollSeq = null;
String pzDataType = null;
private TableColumnMetadata(){}
public String getDataType(){ return pzDataType; }
public String getCollation(){ return pzCollSeq; }
public boolean isNotNull(){ return pNotNull; }
public boolean isPrimaryKey(){ return pPrimaryKey; }
public boolean isAutoincrement(){ return pAutoinc; }
}
/**
Returns data about a database, table, and (optionally) column
(which may be null), as per sqlite3_table_column_metadata().
Throws if passed invalid arguments, else returns the result as a
new TableColumnMetadata object.
*/
TableColumnMetadata tableColumnMetadata(
String zDbName, String zTableName, String zColumnName
){
org.sqlite.jni.capi.OutputPointer.String pzDataType
= new org.sqlite.jni.capi.OutputPointer.String();
org.sqlite.jni.capi.OutputPointer.String pzCollSeq
= new org.sqlite.jni.capi.OutputPointer.String();
org.sqlite.jni.capi.OutputPointer.Bool pNotNull
= new org.sqlite.jni.capi.OutputPointer.Bool();
org.sqlite.jni.capi.OutputPointer.Bool pPrimaryKey
= new org.sqlite.jni.capi.OutputPointer.Bool();
org.sqlite.jni.capi.OutputPointer.Bool pAutoinc
= new org.sqlite.jni.capi.OutputPointer.Bool();
final int rc = CApi.sqlite3_table_column_metadata(
thisDb(), zDbName, zTableName, zColumnName,
pzDataType, pzCollSeq, pNotNull, pPrimaryKey, pAutoinc
);
checkRc(rc);
TableColumnMetadata rv = new TableColumnMetadata();
rv.pzDataType = pzDataType.value;
rv.pzCollSeq = pzCollSeq.value;
rv.pNotNull = pNotNull.value;
rv.pPrimaryKey = pPrimaryKey.value;
rv.pAutoinc = pAutoinc.value;
return rv;
}
/**
Corresponds to the sqlite3_stmt class. Use Sqlite.prepare() to
create new instances.

View File

@ -12,7 +12,7 @@
** 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.CApi;
import org.sqlite.jni.capi.sqlite3;
/**
@ -22,10 +22,10 @@ import org.sqlite.jni.capi.sqlite3;
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;
private int errCode = CApi.SQLITE_ERROR;
private int xerrCode = CApi.SQLITE_ERROR;
private int errOffset = -1;
private int sysErrno = 0;
/**
Records the given error string and uses SQLITE_ERROR for both the
@ -38,10 +38,13 @@ public final class SqliteException extends java.lang.RuntimeException {
/**
Uses sqlite3_errstr(sqlite3ResultCode) for the error string and
sets both the error code and extended error code to the given
value.
value. This approach includes no database-level information and
systemErrno() will be 0, so is intended only for use with sqlite3
APIs for which a result code is not an error but which the
higher-level wrapper should treat as one.
*/
public SqliteException(int sqlite3ResultCode){
super(sqlite3_errstr(sqlite3ResultCode));
super(CApi.sqlite3_errstr(sqlite3ResultCode));
errCode = xerrCode = sqlite3ResultCode;
}
@ -50,16 +53,16 @@ public final class SqliteException extends java.lang.RuntimeException {
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
Design note: closing the db on error is really 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);
super(CApi.sqlite3_errmsg(db));
errCode = CApi.sqlite3_errcode(db);
xerrCode = CApi.sqlite3_extended_errcode(db);
errOffset = CApi.sqlite3_error_offset(db);
sysErrno = CApi.sqlite3_system_errno(db);
}
/**
@ -71,7 +74,7 @@ public final class SqliteException extends java.lang.RuntimeException {
}
public SqliteException(Sqlite.Stmt stmt){
this( stmt.db() );
this(stmt.db());
}
public int errcode(){ return errCode; }

View File

@ -1,5 +1,5 @@
C Add\shigh-level\swindow\sfunction\swrapper\sto\sthe\sJNI\swrapper1\sinterface.
D 2023-10-22T23:36:16.690
C Add\smany\smore\shigh-level\swrappers\sto\sthe\sJNI\swrapper1\sAPI.\sCorrect\sthe\sJNI\sbindings\sof\ssqlite3_strglob/strlike()\sto\scompare\sas\sthe\score\slib\sdoes\sif\stheir\sglob\sargument\sis\sNULL\sand\sthe\sother\sis\snot.
D 2023-10-23T01:34:17.318
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -239,7 +239,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3
F ext/jni/GNUmakefile 36919b7c4fb8447da4330df9996c7b064b766957f8b7be214a30eab55a8b8072
F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4
F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
F ext/jni/src/c/sqlite3-jni.c dcd6534b65b732ad927a49185c76c76abbd5ccadfa972d02f699abc45678e329
F ext/jni/src/c/sqlite3-jni.c 21a218f50cfae116c5dbe780fd14181338e5b608dd6d52751e4a982c1b41c877
F ext/jni/src/c/sqlite3-jni.h e839090f5ec35aa96983a5621659e55ef897dc0522242fd00f107028ef5e7dd5
F ext/jni/src/org/sqlite/jni/annotation/NotNull.java a99341e88154e70447596b1af6a27c586317df41a7e0f246fd41370cd7b723b2
F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba
@ -294,8 +294,8 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe
F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java bbe60ac7fd8718edb215a23dc901771bcedb1df3b46d9cf6caff6f419828587f
F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03
F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 585309311ffce6f39626024bf2ea3add91339f6a146b674720165c1955efbe68
F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 3e6cdb5fe1b01a592ba5ca6ae7d11681a85d081786ce8d046ef631a08ae82dde
F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 1386f7b753134fc12253ce2fbbc448ba8c970567fac01a3356cb672e14408d73
F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java fae71b7454fa3d9243ad26c80e55b590c044a0f0a23d18cae21f0cfa3a92a969
F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java aa85b4b05fae240b14f3d332f9524a2f80c619fb03856be72b4adda866b63b72
F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 13008f8d3c34c1dd55c3afe6dd18dcf94316874cde893ab0661a973fc51a87a4
F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af
F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java 1a1afbafbd7406ff67e7d6405541c6347517c731de535a97d7a3df1d4db835b4
@ -2137,8 +2137,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 89fecf1dd8b97941f9b45130a3c8a67af36ec65cc6f70f5026c569c058a4963f
R 160b97c24140d26b449a4e16b3331ec4
P a27e7471231a24864cbd04b77cbc4b336ce180d738a36ce4318543e2666ed708
R ba0772b5db9c34b23be02455e60dfd03
U stephan
Z 05c9adecf2f51f54547f1d6b2c6aaa3c
Z eac6f822004436c39a1705e9823cdae5
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
a27e7471231a24864cbd04b77cbc4b336ce180d738a36ce4318543e2666ed708
55c4b1dc402b358d53d65fa1f6ec063e9e38e95c81a05d98dae3cb58c52ef55c