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

Wrap the sqlite3_backup API in the JNI wrapper1 API.

FossilOrigin-Name: 3ee6cc29d2111e7ad90860827c0ea808fdf07bc71defdade7e6794ec4a2a3ce2
This commit is contained in:
stephan
2023-11-04 22:47:40 +00:00
parent ffdb479e7c
commit dc8a684c11
4 changed files with 154 additions and 16 deletions

View File

@ -16,6 +16,7 @@ import java.nio.charset.StandardCharsets;
import org.sqlite.jni.capi.CApi;
import org.sqlite.jni.capi.sqlite3;
import org.sqlite.jni.capi.sqlite3_stmt;
import org.sqlite.jni.capi.sqlite3_backup;
import org.sqlite.jni.capi.OutputPointer;
/**
@ -767,14 +768,6 @@ public final class Sqlite implements AutoCloseable {
"This \"cannot happen\": all possible result codes were checked already."
);
}
/*
Potential signature change TODO:
boolean step()
Returning true for SQLITE_ROW and false for anything else.
Those semantics have proven useful in the WASM/JS bindings.
*/
}
/**
@ -1019,4 +1012,114 @@ public final class Sqlite implements AutoCloseable {
}
}
/**
Encapsulates state related to the sqlite3 backup API. Use
Sqlite.initBackup() to create new instances.
*/
public static final class Backup implements AutoCloseable {
private sqlite3_backup b = null;
private Sqlite dbTo = 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){
this.dbTo = dbDest;
this.dbFrom = dbSrc;
b = CApi.sqlite3_backup_init(dbDest.nativeHandle(), schemaDest,
dbSrc.nativeHandle(), schemaSrc);
if(null==b) toss();
}
private void toss(){
int rc = CApi.sqlite3_errcode(dbTo.nativeHandle());
if(0!=rc) throw new SqliteException(dbTo);
rc = CApi.sqlite3_errcode(dbFrom.nativeHandle());
if(0!=rc) throw new SqliteException(dbFrom);
throw new SqliteException(CApi.SQLITE_ERROR);
}
private sqlite3_backup getNative(){
if( null==b ) throw new IllegalStateException("This Backup is already closed.");
return b;
}
/**
If this backup is still active, this completes the backup and
frees its native resources, otherwise it this is a no-op.
*/
public void finish(){
if( null!=b ){
CApi.sqlite3_backup_finish(b);
b = null;
dbTo = null;
dbFrom = null;
}
}
/** Equivalent to finish(). */
@Override public void close(){
this.finish();
}
/**
Analog to sqlite3_backup_step(). Returns 0 if stepping succeeds
or, DONE if the end is reached, BUSY if one of the databases is
busy, LOCKED if one of the databases is locked, and throws for
any other result code or if this object has been closed. Note
that BUSY and LOCKED are not necessarily permanent errors, so
do not trigger an exception.
*/
public int step(int pageCount){
final int rc = CApi.sqlite3_backup_step(getNative(), pageCount);
switch(rc){
case 0:
case DONE:
case BUSY:
case LOCKED:
return rc;
default:
toss();
return CApi.SQLITE_ERROR/*not reached*/;
}
}
/**
Analog to sqlite3_backup_pagecount().
*/
public int pageCount(){
return CApi.sqlite3_backup_pagecount(getNative());
}
/**
Analog to sqlite3_backup_remaining().
*/
public int remaining(){
return CApi.sqlite3_backup_remaining(getNative());
}
}
/**
Analog to sqlite3_backup_init(). If schemaSrc is null, "main" is
assumed. Throws if either this db or dbSrc (the source db) are
not opened, if either of schemaDest or schemaSrc are null, or if
the underlying call to sqlite3_backup_init() fails.
The returned object must eventually be cleaned up by either
arranging for it to be auto-closed (e.g. using
try-with-resources) or by calling its finish() method.
*/
public Backup initBackup(String schemaDest, Sqlite dbSrc, String schemaSrc){
thisDb();
dbSrc.thisDb();
if( null==schemaSrc || null==schemaDest ){
throw new IllegalArgumentException(
"Neither the source nor destination schema name may be null."
);
}
return new Backup(this, schemaDest, dbSrc, schemaSrc);
}
}

View File

@ -603,6 +603,41 @@ public class Tester2 implements Runnable {
affirm( 8 == val.value );
}
private void testBackup(){
final Sqlite dbDest = openDb();
try (Sqlite dbSrc = openDb()) {
execSql(dbSrc, new String[]{
"pragma page_size=512; VACUUM;",
"create table t(a);",
"insert into t(a) values(1),(2),(3);"
});
Exception e = null;
try {
dbSrc.initBackup("main",dbSrc,"main");
}catch(Exception x){
e = x;
}
affirm( e instanceof SqliteException );
e = null;
try (Sqlite.Backup b = dbDest.initBackup("main",dbSrc,"main")) {
affirm( null!=b );
int rc;
while( Sqlite.Backup.DONE!=(rc = b.step(1)) ){
affirm( 0==rc );
}
affirm( b.pageCount() > 0 );
b.finish();
}
}
try (Sqlite.Stmt q = dbDest.prepare("SELECT sum(a) from t")) {
q.step();
affirm( q.columnInt(0) == 6 );
}
dbDest.close();
}
private void runTests(boolean fromThread) throws Exception {
List<java.lang.reflect.Method> mlist = testMethods;
affirm( null!=mlist );