From: stephan Date: Fri, 3 Nov 2023 13:00:51 +0000 (+0000) Subject: Wrap more of the stmt API behind the JNI wrapper1 API. X-Git-Tag: version-3.45.0~224 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b94518319572c478982c0ebc5a39dbe3e7cadca7;p=thirdparty%2Fsqlite.git Wrap more of the stmt API behind the JNI wrapper1 API. FossilOrigin-Name: 8fea23dc3af023ccf2909f1b4c6f91e7df0ffaac875b15f1fb3e264fba169b6a --- diff --git a/ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java b/ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java index 0495702561..7df748e8d8 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java +++ b/ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java @@ -24,8 +24,9 @@ package org.sqlite.jni.capi; propagated. For callback interfaces which support returning error info to the core, the JNI binding will convert any exceptions to C-level error information. For callback interfaces which do not - support, all exceptions will necessarily be suppressed in order to - retain the C-style no-throw semantics. + support returning error information, all exceptions will + necessarily be suppressed in order to retain the C-style no-throw + semantics and avoid invoking undefined behavior in the C layer.

Callbacks of this style follow a common naming convention: diff --git a/ext/jni/src/org/sqlite/jni/capi/ValueHolder.java b/ext/jni/src/org/sqlite/jni/capi/ValueHolder.java index b3f03ac867..6d5db3f669 100644 --- a/ext/jni/src/org/sqlite/jni/capi/ValueHolder.java +++ b/ext/jni/src/org/sqlite/jni/capi/ValueHolder.java @@ -9,14 +9,15 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** This file contains a set of tests for the sqlite3 JNI bindings. +** This file holds utility code 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. + requires a "final" reference, as well as communicating aggregate + SQL function state across calls to such functions. */ public class ValueHolder { public T value; diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java b/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java index 2d0ebfaf32..5bcb3bd5fa 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java @@ -121,6 +121,17 @@ public interface SqlFunction { public void resultText16(byte[] utf16){CApi.sqlite3_result_text16(cx, utf16);} public void resultText16(String txt){CApi.sqlite3_result_text16(cx, txt);} + /** + Callbacks should invoke this on OOM errors, instead of throwing + OutOfMemoryError, because the latter cannot be propagated + through the C API. + */ + public void resultNoMem(){CApi.sqlite3_result_error_nomem(cx);} + + /** + Analog to sqlite3_set_auxdata() but throws if argNdx is out of + range. + */ public void setAuxData(int argNdx, Object o){ /* From the API docs: https://www.sqlite.org/c3ref/get_auxdata.html @@ -132,6 +143,10 @@ public interface SqlFunction { CApi.sqlite3_set_auxdata(cx, argNdx, o); } + /** + Analog to sqlite3_get_auxdata() but throws if argNdx is out of + range. + */ public Object getAuxData(int argNdx){ valueAt(argNdx); return CApi.sqlite3_get_auxdata(cx, argNdx); diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index 6bb87cc392..907755d99a 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -210,11 +210,19 @@ public final class Sqlite implements AutoCloseable { 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. + + As a special case, if rc is SQLITE_NOMEM, an OutOfMemoryError is + thrown. */ private void checkRc(int rc){ if( 0!=rc ){ - if( null==db || 0==sqlite3_errcode(db)) throw new SqliteException(rc); - else throw new SqliteException(db); + if( CApi.SQLITE_NOMEM==rc ){ + throw new OutOfMemoryError(); + }else if( null==db || 0==sqlite3_errcode(db)){ + throw new SqliteException(rc); + }else{ + throw new SqliteException(db); + } } } @@ -470,8 +478,11 @@ public final class Sqlite implements AutoCloseable { We save the result column count in order to prevent having to call into C to fetch that value every time we need to check that value for the columnXyz() methods. + + Design note: if this is final then we cannot zero it in + finalizeStmt(). */ - private final int resultColCount; + private int resultColCount; /** Only called by the prepare() factory functions. */ Stmt(Sqlite db, sqlite3_stmt stmt){ @@ -509,13 +520,17 @@ public final class Sqlite implements AutoCloseable { 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. + finalized, but the result code is an indication that something + went wrong in a prior call into the statement's API, as + documented for sqlite3_finalize(). */ public int finalizeStmt(){ int rc = 0; if( null!=stmt ){ sqlite3_finalize(stmt); stmt = null; + _db = null; + resultColCount = 0; } return rc; } @@ -565,6 +580,10 @@ public final class Sqlite implements AutoCloseable { */ } + /** + Returns the Sqlite which prepared this statement, or null if + this statement has been finalized. + */ public Sqlite db(){ return this._db; } /** @@ -574,6 +593,44 @@ public final class Sqlite implements AutoCloseable { checkRc(CApi.sqlite3_reset(thisStmt())); } + public boolean isBusy(){ + return CApi.sqlite3_stmt_busy(thisStmt()); + } + + public boolean isReadOnly(){ + return CApi.sqlite3_stmt_readonly(thisStmt()); + } + + public String sql(){ + return CApi.sqlite3_sql(thisStmt()); + } + + public String expandedSql(){ + return CApi.sqlite3_expanded_sql(thisStmt()); + } + + /** + Analog to sqlite3_stmt_explain() but throws if op is invalid. + */ + public void explain(int op){ + checkRc(CApi.sqlite3_stmt_explain(thisStmt(), op)); + } + + /** + Analog to sqlite3_stmt_isexplain(). + */ + public int isExplain(){ + return CApi.sqlite3_stmt_isexplain(thisStmt()); + } + + /** + Analog to sqlite3_normalized_sql(), returning null if the + library is built without the SQLITE_ENABLE_NORMALIZE flag. + */ + public String normalizedSql(){ + return CApi.sqlite3_normalized_sql(thisStmt()); + } + public void clearBindings(){ CApi.sqlite3_clear_bindings( thisStmt() ); } @@ -610,8 +667,8 @@ public final class Sqlite implements AutoCloseable { public void bindText16(int ndx, byte[] utf16){ checkRc(CApi.sqlite3_bind_text16(thisStmt(), ndx, utf16)); } - public void bindText16(int ndx, String txt){ - checkRc(CApi.sqlite3_bind_text16(thisStmt(), ndx, txt)); + public void bindText16(int ndx, String asUtf16){ + checkRc(CApi.sqlite3_bind_text16(thisStmt(), ndx, asUtf16)); } public void bindZeroBlob(int ndx, int n){ checkRc(CApi.sqlite3_bind_zeroblob(thisStmt(), ndx, n)); diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index e6de9ab018..d24551ebf1 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -422,6 +422,28 @@ public class Tester2 implements Runnable { affirm( null==Sqlite.keywordName(n) ); } + + private void testExplain(){ + final Sqlite db = openDb(); + Sqlite.Stmt q = db.prepare("SELECT 1"); + affirm( 0 == q.isExplain() ); + q.explain(0); + affirm( 0 == q.isExplain() ); + q.explain(1); + affirm( 1 == q.isExplain() ); + q.explain(2); + affirm( 2 == q.isExplain() ); + Exception ex = null; + try{ + q.explain(-1); + }catch(Exception e){ + ex = e; + } + affirm( ex instanceof SqliteException ); + q.finalizeStmt(); + db.close(); + } + private void runTests(boolean fromThread) throws Exception { List mlist = testMethods; affirm( null!=mlist ); diff --git a/manifest b/manifest index a27a5977bc..0a014ab96c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Bind\sthe\ssqlite3_keyword_...()\sand\ssqlite3_compileoption_...()\sfamily\sof\sfunctions\sto\sthe\sJNI\swrapper1\sAPI. -D 2023-11-03T11:53:13.823 +C Wrap\smore\sof\sthe\sstmt\sAPI\sbehind\sthe\sJNI\swrapper1\sAPI. +D 2023-11-03T13:00:51.184 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -251,7 +251,7 @@ F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java 7ed409d5449684616cc924 F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca F ext/jni/src/org/sqlite/jni/capi/CApi.java 24aba7b14b11da52cd47083608d37c186122c2e2072e75b2ff923d1f15bfb9e5 -F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 0bfd6e56e8265c2f05c9207665707285534d78f8466ef0e0430c65677f00943d +F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java f81cf10b79c52f9b2e9247d523d29ae48863935f60420eae35f257c38c80ce95 F ext/jni/src/org/sqlite/jni/capi/CommitHookCallback.java 29c002f3c638cc80f7db1594564a262d1beb32637824c3dca2d60a224d1f71d7 @@ -271,7 +271,7 @@ F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff22 F ext/jni/src/org/sqlite/jni/capi/Tester1.java b6b2f3354ba68956a6bcd1c586b8eb25a0bd66eed2b58b340405e1129da15de9 F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java 2766b8526bbffc4f1045f70e79f1bc1b1efe1c3e95ca06cdb8a7391032dda3b4 -F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 9f9e151f1da017b706c0ee5f40f4c86b54e773d6ae4339723e0cc85a456251ab +F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 F ext/jni/src/org/sqlite/jni/capi/WindowFunction.java caf4396f91b2567904cf94bc538a069fd62260d975bd037d15a02a890ed1ef9e F ext/jni/src/org/sqlite/jni/capi/XDestroyCallback.java f3abb8dd7381f53ebba909437090caf68200f06717b8a7d6aa96fa3e8133117d F ext/jni/src/org/sqlite/jni/capi/package-info.java 08ff986a65d2be9162442c82d28a65ce431d826f188520717c2ecb1484d0a50e @@ -294,10 +294,10 @@ F ext/jni/src/org/sqlite/jni/fts5/fts5_tokenizer.java 92bdaa3893bd684533004d64ad F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e 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 9be20f3ba6edbe63285224d5d640dbfb0d84fb8cf5134084e3ae40004ec9d6d9 -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 698fd699dd113fa2bceb78d3565f3d4578dee86150fb86e1838ec915994aa57b +F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 0b01b9058ef6737c85b505c6aa2490fb1dc1d974fb39d88a93269fed09553f9f +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 1c95f5e0f872aeb9cdd174cbb2e254d158df1f8b2fee9f0e6ec82c348602a7bd F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java aa85b4b05fae240b14f3d332f9524a2f80c619fb03856be72b4adda866b63b72 -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java f74b9000cc24d415cfdd9fd77234aa6ab0549871f495529993cee6176cad0329 +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 9ab7e38e6741842f8e3b74cd3ecb4953e2f1957f5229bd32663df7331245ce95 F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java 1a1afbafbd7406ff67e7d6405541c6347517c731de535a97d7a3df1d4db835b4 F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 @@ -2141,8 +2141,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 52ab3deba4d26ca0e9a84a6dff254195b4a0e2cc6cf948cf7a66bb11117e7002 -R 713111c1d3b2b5aa68a4fe22b929906b +P b27242414d6023eac7e62cf6120e1f02b0ddc7b8f0a1e4c48111cfe88d197cbd +R 05c1b264db19fc6b766555a4fdb5a28d U stephan -Z 5f0fb960919ed3f05e59594d37935544 +Z 7009b77b29804732443dad34d2696167 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0a15fa2c89..88247987ba 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b27242414d6023eac7e62cf6120e1f02b0ddc7b8f0a1e4c48111cfe88d197cbd \ No newline at end of file +8fea23dc3af023ccf2909f1b4c6f91e7df0ffaac875b15f1fb3e264fba169b6a \ No newline at end of file