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

Add incremental blob I/O support to JNI wrapper1.

FossilOrigin-Name: 7f1c76fe930d69a0274f70fa7b7e68e0db6226b731a065fa57d0936c8400ffb0
This commit is contained in:
stephan
2023-11-05 04:20:04 +00:00
parent 546db3f14a
commit ed99e7493a
5 changed files with 110 additions and 11 deletions

View File

@ -1633,7 +1633,7 @@ public class Tester1 implements Runnable {
sqlite3_finalize(stmt);
b = sqlite3_blob_open(db, "main", "t", "a",
sqlite3_last_insert_rowid(db), 1);
sqlite3_last_insert_rowid(db), 0);
affirm( null!=b );
rc = sqlite3_blob_reopen(b, 2);
affirm( 0==rc );

View File

@ -17,6 +17,7 @@ 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.sqlite3_blob;
import org.sqlite.jni.capi.OutputPointer;
/**
@ -473,7 +474,7 @@ public final class Sqlite implements AutoCloseable {
if( 0!=rc ){
if( CApi.SQLITE_NOMEM==rc ){
throw new OutOfMemoryError();
}else if( null==db || 0==CApi.sqlite3_errcode(db)){
}else if( null==db || 0==CApi.sqlite3_errcode(db) ){
throw new SqliteException(rc);
}else{
throw new SqliteException(db);
@ -1630,4 +1631,76 @@ public final class Sqlite implements AutoCloseable {
checkRc( CApi.sqlite3_set_authorizer( thisDb(), ac ) );
}
/**
Object type for use with blobOpen()
*/
public final class Blob implements AutoCloseable {
private Sqlite db;
private sqlite3_blob b;
Blob(Sqlite db, sqlite3_blob b){
this.db = db;
this.b = b;
}
/**
Analog to sqlite3_blob_close().
*/
@Override public void close(){
if( null!=b ){
CApi.sqlite3_blob_close(b);
b = null;
db = null;
}
}
/**
Analog to sqlite3_blob_reopen() but throws on error.
*/
public void reopen(long newRowId){
db.checkRc( CApi.sqlite3_blob_reopen(b, newRowId) );
}
/**
Analog to sqlite3_blob_write() but throws on error.
*/
public void write( byte[] bytes, int atOffset ){
db.checkRc( CApi.sqlite3_blob_write(b, bytes, atOffset) );
}
/**
Analog to sqlite3_blob_read() but throws on error.
*/
public void read( byte[] dest, int atOffset ){
db.checkRc( CApi.sqlite3_blob_read(b, dest, atOffset) );
}
/**
Analog to sqlite3_blob_bytes().
*/
public int bytes(){
return CApi.sqlite3_blob_bytes(b);
}
}
/**
Analog to sqlite3_blob_open(). Returns a Blob object for the
given database, table, column, and rowid. The blob is opened for
read-write mode if writeable is true, else it is read-only.
The returned object must eventually be freed, before this
database is closed, by either arranging for it to be auto-closed
or calling its close() method.
Throws on error.
*/
public Blob blobOpen(String dbName, String tableName, String columnName,
long iRow, boolean writeable){
final OutputPointer.sqlite3_blob out = new OutputPointer.sqlite3_blob();
checkRc(
CApi.sqlite3_blob_open(thisDb(), dbName, tableName, columnName,
iRow, writeable ? 1 : 0, out)
);
return new Blob(this, out.take());
}
}

View File

@ -912,6 +912,32 @@ public class Tester2 implements Runnable {
db.close();
}
private void testBlobOpen(){
final Sqlite db = openDb();
execSql(db, "CREATE TABLE T(a BLOB);"
+"INSERT INTO t(rowid,a) VALUES(1, 'def'),(2, 'XYZ');"
);
Sqlite.Blob b = db.blobOpen("main", "t", "a",
db.lastInsertRowId(), true);
affirm( 3==b.bytes() );
b.write(new byte[] {100, 101, 102 /*"DEF"*/}, 0);
b.close();
Sqlite.Stmt stmt = db.prepare("SELECT length(a), a FROM t ORDER BY a");
affirm( stmt.step() );
affirm( 3 == stmt.columnInt(0) );
affirm( "def".equals(stmt.columnText16(1)) );
stmt.finalizeStmt();
b = db.blobOpen("main", "t", "a", db.lastInsertRowId(), false);
b.reopen(2);
final byte[] tgt = new byte[3];
b.read( tgt, 0 );
affirm( 100==tgt[0] && 101==tgt[1] && 102==tgt[2], "DEF" );
b.close();
db.close();
}
private void runTests(boolean fromThread) throws Exception {
List<java.lang.reflect.Method> mlist = testMethods;
affirm( null!=mlist );