1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

Add busy-handler support to JNI wrapper1.

FossilOrigin-Name: dcf579ab2de4a3d3a437cde59b2fd60f1729c0bde31df1865117e6a5ea4bab20
This commit is contained in:
stephan
2023-11-05 00:02:47 +00:00
parent 15d38c0dde
commit b02ca781ad
5 changed files with 140 additions and 69 deletions

View File

@ -1031,49 +1031,48 @@ public class Tester1 implements Runnable {
@SingleThreadOnly /* because threads inherently break this test */ @SingleThreadOnly /* because threads inherently break this test */
private static void testBusy(){ private static void testBusy(){
final String dbName = "_busy-handler.db"; final String dbName = "_busy-handler.db";
final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3();
final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt();
int rc = sqlite3_open(dbName, outDb);
++metrics.dbOpen;
affirm( 0 == rc );
final sqlite3 db1 = outDb.get();
execSql(db1, "CREATE TABLE IF NOT EXISTS t(a)");
rc = sqlite3_open(dbName, outDb);
++metrics.dbOpen;
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) ) );
affirm( SQLITE_MISUSE == sqlite3_db_config(db1, 0, 0, null) );
final ValueHolder<Integer> xBusyCalled = new ValueHolder<>(0);
BusyHandlerCallback handler = new BusyHandlerCallback(){
@Override public int call(int n){
//outln("busy handler #"+n);
return n > 2 ? 0 : ++xBusyCalled.value;
}
};
rc = sqlite3_busy_handler(db2, handler);
affirm(0 == rc);
// Force a locked condition...
execSql(db1, "BEGIN EXCLUSIVE");
rc = sqlite3_prepare_v2(db2, "SELECT * from t", outStmt);
affirm( SQLITE_BUSY == rc);
affirm( null == outStmt.get() );
affirm( 3 == xBusyCalled.value );
sqlite3_close_v2(db1);
sqlite3_close_v2(db2);
try{ try{
final java.io.File f = new java.io.File(dbName); final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3();
f.delete(); final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt();
}catch(Exception e){
/* ignore */ int rc = sqlite3_open(dbName, outDb);
++metrics.dbOpen;
affirm( 0 == rc );
final sqlite3 db1 = outDb.get();
execSql(db1, "CREATE TABLE IF NOT EXISTS t(a)");
rc = sqlite3_open(dbName, outDb);
++metrics.dbOpen;
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) ) );
affirm( SQLITE_MISUSE == sqlite3_db_config(db1, 0, 0, null) );
final ValueHolder<Integer> xBusyCalled = new ValueHolder<>(0);
BusyHandlerCallback handler = new BusyHandlerCallback(){
@Override public int call(int n){
//outln("busy handler #"+n);
return n > 2 ? 0 : ++xBusyCalled.value;
}
};
rc = sqlite3_busy_handler(db2, handler);
affirm(0 == rc);
// Force a locked condition...
execSql(db1, "BEGIN EXCLUSIVE");
rc = sqlite3_prepare_v2(db2, "SELECT * from t", outStmt);
affirm( SQLITE_BUSY == rc);
affirm( null == outStmt.get() );
affirm( 3 == xBusyCalled.value );
sqlite3_close_v2(db1);
sqlite3_close_v2(db2);
}finally{
try{(new java.io.File(dbName)).delete();}
catch(Exception e){/* ignore */}
} }
} }

View File

@ -32,6 +32,7 @@ public final class Sqlite implements AutoCloseable {
public static final int OPEN_READWRITE = CApi.SQLITE_OPEN_READWRITE; 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_CREATE = CApi.SQLITE_OPEN_CREATE;
public static final int OPEN_EXRESCODE = CApi.SQLITE_OPEN_EXRESCODE; 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_NONE = CApi.SQLITE_TXN_NONE;
public static final int TXN_READ = CApi.SQLITE_TXN_READ; public static final int TXN_READ = CApi.SQLITE_TXN_READ;
public static final int TXN_WRITE = CApi.SQLITE_TXN_WRITE; public static final int TXN_WRITE = CApi.SQLITE_TXN_WRITE;
@ -107,6 +108,10 @@ public final class Sqlite implements AutoCloseable {
/* We elide the UTF16_ALIGNED from this interface because it /* We elide the UTF16_ALIGNED from this interface because it
is irrelevant for the Java interface. */ is irrelevant for the Java interface. */
public static final int DONE = CApi.SQLITE_DONE;
public static final int BUSY = CApi.SQLITE_BUSY;
public static final int LOCKED = CApi.SQLITE_LOCKED;
//! Used only by the open() factory functions. //! Used only by the open() factory functions.
private Sqlite(sqlite3 db){ private Sqlite(sqlite3 db){
this.db = db; this.db = db;
@ -430,10 +435,6 @@ public final class Sqlite implements AutoCloseable {
return CApi.sqlite3_get_autocommit(thisDb()); 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, Analog to sqlite3_txn_state(). Returns one of TXN_NONE, TXN_READ,
or TXN_WRITE to denote this database's current transaction state or TXN_WRITE to denote this database's current transaction state
@ -1028,11 +1029,6 @@ public final class Sqlite implements AutoCloseable {
private Sqlite dbTo = null; private Sqlite dbTo = null;
private Sqlite dbFrom = null; private Sqlite dbFrom = null;
public static final int DONE = CApi.SQLITE_DONE;
public static final int BUSY = CApi.SQLITE_BUSY;
public static final int LOCKED = CApi.SQLITE_LOCKED;
Backup(Sqlite dbDest, String schemaDest,Sqlite dbSrc, String schemaSrc){ Backup(Sqlite dbDest, String schemaDest,Sqlite dbSrc, String schemaSrc){
this.dbTo = dbDest; this.dbTo = dbDest;
this.dbFrom = dbSrc; this.dbFrom = dbSrc;
@ -1073,19 +1069,19 @@ public final class Sqlite implements AutoCloseable {
/** /**
Analog to sqlite3_backup_step(). Returns 0 if stepping succeeds Analog to sqlite3_backup_step(). Returns 0 if stepping succeeds
or, DONE if the end is reached, BUSY if one of the databases is or, Sqlite.DONE if the end is reached, Sqlite.BUSY if one of
busy, LOCKED if one of the databases is locked, and throws for the databases is busy, Sqlite.LOCKED if one of the databases is
any other result code or if this object has been closed. Note locked, and throws for any other result code or if this object
that BUSY and LOCKED are not necessarily permanent errors, so has been closed. Note that BUSY and LOCKED are not necessarily
do not trigger an exception. permanent errors, so do not trigger an exception.
*/ */
public int step(int pageCount){ public int step(int pageCount){
final int rc = CApi.sqlite3_backup_step(getNative(), pageCount); final int rc = CApi.sqlite3_backup_step(getNative(), pageCount);
switch(rc){ switch(rc){
case 0: case 0:
case DONE: case Sqlite.DONE:
case BUSY: case Sqlite.BUSY:
case LOCKED: case Sqlite.LOCKED:
return rc; return rc;
default: default:
toss(); toss();
@ -1219,4 +1215,42 @@ public final class Sqlite implements AutoCloseable {
} }
checkRc( CApi.sqlite3_collation_needed(thisDb(), cnc) ); checkRc( CApi.sqlite3_collation_needed(thisDb(), cnc) );
} }
/**
Callback for use with busyHandler().
*/
public interface BusyHandler {
/**
Must function as documented for the C-level
sqlite3_busy_handler() callback argument, minus the (void*)
argument the C-level function requires.
If this function throws, it is translated to a database-level
error.
*/
int call(int n);
}
/**
Analog to sqlite3_busy_timeout().
*/
public void setBusyTimeout(int ms){
checkRc(CApi.sqlite3_busy_timeout(thisDb(), ms));
}
/**
Analog to sqlite3_busy_handler(). If b is null then any
current handler is cleared.
*/
void setBusyHandler( BusyHandler b ){
org.sqlite.jni.capi.BusyHandlerCallback bhc = null;
if( null!=b ){
bhc = new org.sqlite.jni.capi.BusyHandlerCallback(){
@Override public int call(int n){
return b.call(n);
}
};
}
checkRc( CApi.sqlite3_busy_handler(thisDb(), bhc) );
}
} }

View File

@ -623,7 +623,7 @@ public class Tester2 implements Runnable {
try (Sqlite.Backup b = dbDest.initBackup("main",dbSrc,"main")) { try (Sqlite.Backup b = dbDest.initBackup("main",dbSrc,"main")) {
affirm( null!=b ); affirm( null!=b );
int rc; int rc;
while( Sqlite.Backup.DONE!=(rc = b.step(1)) ){ while( Sqlite.DONE!=(rc = b.step(1)) ){
affirm( 0==rc ); affirm( 0==rc );
} }
affirm( b.pageCount() > 0 ); affirm( b.pageCount() > 0 );
@ -699,6 +699,44 @@ public class Tester2 implements Runnable {
db.close(); db.close();
} }
@SingleThreadOnly /* because threads inherently break this test */
private void testBusy(){
final String dbName = "_busy-handler.db";
try{
Sqlite db1 = openDb(dbName);
++metrics.dbOpen;
execSql(db1, "CREATE TABLE IF NOT EXISTS t(a)");
Sqlite db2 = openDb(dbName);
++metrics.dbOpen;
final ValueHolder<Integer> xBusyCalled = new ValueHolder<>(0);
Sqlite.BusyHandler handler = new Sqlite.BusyHandler(){
@Override public int call(int n){
return n > 2 ? 0 : ++xBusyCalled.value;
}
};
db2.setBusyHandler(handler);
// Force a locked condition...
execSql(db1, "BEGIN EXCLUSIVE");
int rc = 0;
SqliteException ex = null;
try{
db2.prepare("SELECT * from t");
}catch(SqliteException x){
ex = x;
}
affirm( null!=ex );
affirm( Sqlite.BUSY == ex.errcode() );
affirm( 3 == xBusyCalled.value );
db1.close();
db2.close();
}finally{
try{(new java.io.File(dbName)).delete();}
catch(Exception e){/* ignore */}
}
}
private void runTests(boolean fromThread) throws Exception { private void runTests(boolean fromThread) throws Exception {
List<java.lang.reflect.Method> mlist = testMethods; List<java.lang.reflect.Method> mlist = testMethods;
affirm( null!=mlist ); affirm( null!=mlist );

View File

@ -1,5 +1,5 @@
C Bind\scollation\sand\scollation-needed\sto\sJNI\swrapper1\sand\scorrect\sthe\scallback\sreturn\stype\sfor\scollation-needed\scallbacks\sin\sthe\slower-level\sJNI\sbinding. C Add\sbusy-handler\ssupport\sto\sJNI\swrapper1.
D 2023-11-04T23:37:11.738 D 2023-11-05T00:02:47.384
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385
F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1
F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615
F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f
F ext/jni/src/org/sqlite/jni/capi/Tester1.java 8823d962f283aa7af5878d1a87b759ca03e1c9519ae692077f785eab81c86f3f F ext/jni/src/org/sqlite/jni/capi/Tester1.java 4bb5e62907a422a80a0fccbcb83085e9163c2c245451312a62c7550a45d16683
F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 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/UpdateHookCallback.java 2766b8526bbffc4f1045f70e79f1bc1b1efe1c3e95ca06cdb8a7391032dda3b4
F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950
@ -296,9 +296,9 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe
F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483 F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483
F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03
F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 2833afdb9af5c1949bb35f4c926a5351fba9d1cdf0996864caa7b47827a346c7 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 2833afdb9af5c1949bb35f4c926a5351fba9d1cdf0996864caa7b47827a346c7
F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 6a861cfc8b3284c07cf2fa88916deab27f98e9e4234fae1bed1917c933c64083 F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java c930ca964f605ba8f175d3b0c85099d7f93069b59bf825929c9eef9e68ac96c5
F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 929a1e2ab4e135fbbae7f0d2d609f77cfbbc60bbec7ba789ce23d9c73bc6156e F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 929a1e2ab4e135fbbae7f0d2d609f77cfbbc60bbec7ba789ce23d9c73bc6156e
F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java a4e4b0b8ee0d56a383fd57b24244c6f93f8a0fe2e2ba5faacc0a3331f8d3fc84 F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 6f5fae3c3827ca42ef124c319b24907483aadda69b7453173f7807e0a94f33dd
F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af
F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java c7d1452f9ff26175b3c19bbf273116cc2846610af68e01756d755f037fe7319f F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java c7d1452f9ff26175b3c19bbf273116cc2846610af68e01756d755f037fe7319f
F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745
@ -2142,8 +2142,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 3ee6cc29d2111e7ad90860827c0ea808fdf07bc71defdade7e6794ec4a2a3ce2 P 0f673140681685ab390ecd7326a8b80d060b7ab23c31a2cfc28ba76fd5096afe
R ef3119e103f71f255ed5e129f7bdb70b R 2dabe33af17a981e8c8323ecd84b4487
U stephan U stephan
Z 99a7727c5c06597d0eb0c0df96988bd8 Z c2d53a448e9f555de7ff26068d4eba40
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
0f673140681685ab390ecd7326a8b80d060b7ab23c31a2cfc28ba76fd5096afe dcf579ab2de4a3d3a437cde59b2fd60f1729c0bde31df1865117e6a5ea4bab20