2019-03-30 18:30:50 +01:00
|
|
|
/*
|
|
|
|
** 2019-03-30
|
|
|
|
**
|
|
|
|
** The author disclaims copyright to this source code. In place of
|
|
|
|
** a legal notice, here is a blessing:
|
|
|
|
**
|
|
|
|
** May you do good and not evil.
|
|
|
|
** May you find forgiveness for yourself and forgive others.
|
|
|
|
** May you share freely, never taking more than you give.
|
|
|
|
**
|
|
|
|
******************************************************************************
|
|
|
|
**
|
|
|
|
** An SQL function that uses the incremental BLOB I/O mechanism of SQLite
|
|
|
|
** to read or write part of a blob. This is intended for debugging use
|
|
|
|
** in the CLI.
|
|
|
|
**
|
|
|
|
** readblob(SCHEMA,TABLE,COLUMN,ROWID,OFFSET,N)
|
|
|
|
**
|
|
|
|
** Returns N bytes of the blob starting at OFFSET.
|
|
|
|
**
|
|
|
|
** writeblob(SCHEMA,TABLE,COLUMN,ROWID,OFFSET,NEWDATA)
|
|
|
|
**
|
|
|
|
** NEWDATA must be a blob. The content of NEWDATA overwrites the
|
|
|
|
** existing BLOB data at SCHEMA.TABLE.COLUMN for row ROWID beginning
|
|
|
|
** at OFFSET bytes into the blob.
|
|
|
|
*/
|
|
|
|
#include "sqlite3ext.h"
|
|
|
|
SQLITE_EXTENSION_INIT1
|
|
|
|
#include <assert.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
static void readblobFunc(
|
|
|
|
sqlite3_context *context,
|
|
|
|
int argc,
|
|
|
|
sqlite3_value **argv
|
|
|
|
){
|
|
|
|
sqlite3_blob *pBlob = 0;
|
|
|
|
const char *zSchema;
|
|
|
|
const char *zTable;
|
|
|
|
const char *zColumn;
|
|
|
|
sqlite3_int64 iRowid;
|
|
|
|
int iOfst;
|
|
|
|
unsigned char *aData;
|
|
|
|
int nData;
|
|
|
|
sqlite3 *db;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
zSchema = (const char*)sqlite3_value_text(argv[0]);
|
|
|
|
zTable = (const char*)sqlite3_value_text(argv[1]);
|
|
|
|
if( zTable==0 ){
|
|
|
|
sqlite3_result_error(context, "bad table name", -1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
zColumn = (const char*)sqlite3_value_text(argv[2]);
|
|
|
|
if( zTable==0 ){
|
|
|
|
sqlite3_result_error(context, "bad column name", -1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
iRowid = sqlite3_value_int64(argv[3]);
|
|
|
|
iOfst = sqlite3_value_int(argv[4]);
|
|
|
|
nData = sqlite3_value_int(argv[5]);
|
|
|
|
if( nData<=0 ) return;
|
|
|
|
aData = sqlite3_malloc64( nData+1 );
|
|
|
|
if( aData==0 ){
|
|
|
|
sqlite3_result_error_nomem(context);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
db = sqlite3_context_db_handle(context);
|
|
|
|
rc = sqlite3_blob_open(db, zSchema, zTable, zColumn, iRowid, 0, &pBlob);
|
|
|
|
if( rc ){
|
|
|
|
sqlite3_free(aData);
|
|
|
|
sqlite3_result_error(context, "cannot open BLOB pointer", -1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
rc = sqlite3_blob_read(pBlob, aData, nData, iOfst);
|
|
|
|
sqlite3_blob_close(pBlob);
|
|
|
|
if( rc ){
|
|
|
|
sqlite3_free(aData);
|
2019-05-27 13:21:43 +02:00
|
|
|
sqlite3_result_error(context, "BLOB read failed", -1);
|
2019-03-30 18:30:50 +01:00
|
|
|
}else{
|
|
|
|
sqlite3_result_blob(context, aData, nData, sqlite3_free);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void writeblobFunc(
|
|
|
|
sqlite3_context *context,
|
|
|
|
int argc,
|
|
|
|
sqlite3_value **argv
|
|
|
|
){
|
|
|
|
sqlite3_blob *pBlob = 0;
|
|
|
|
const char *zSchema;
|
|
|
|
const char *zTable;
|
|
|
|
const char *zColumn;
|
|
|
|
sqlite3_int64 iRowid;
|
|
|
|
int iOfst;
|
|
|
|
unsigned char *aData;
|
|
|
|
int nData;
|
|
|
|
sqlite3 *db;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
zSchema = (const char*)sqlite3_value_text(argv[0]);
|
|
|
|
zTable = (const char*)sqlite3_value_text(argv[1]);
|
|
|
|
if( zTable==0 ){
|
|
|
|
sqlite3_result_error(context, "bad table name", -1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
zColumn = (const char*)sqlite3_value_text(argv[2]);
|
|
|
|
if( zTable==0 ){
|
|
|
|
sqlite3_result_error(context, "bad column name", -1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
iRowid = sqlite3_value_int64(argv[3]);
|
|
|
|
iOfst = sqlite3_value_int(argv[4]);
|
|
|
|
if( sqlite3_value_type(argv[5])!=SQLITE_BLOB ){
|
|
|
|
sqlite3_result_error(context, "6th argument must be a BLOB", -1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
nData = sqlite3_value_bytes(argv[5]);
|
|
|
|
aData = (unsigned char *)sqlite3_value_blob(argv[5]);
|
|
|
|
db = sqlite3_context_db_handle(context);
|
|
|
|
rc = sqlite3_blob_open(db, zSchema, zTable, zColumn, iRowid, 1, &pBlob);
|
|
|
|
if( rc ){
|
|
|
|
sqlite3_result_error(context, "cannot open BLOB pointer", -1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
rc = sqlite3_blob_write(pBlob, aData, nData, iOfst);
|
|
|
|
sqlite3_blob_close(pBlob);
|
|
|
|
if( rc ){
|
|
|
|
sqlite3_result_error(context, "BLOB write failed", -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
__declspec(dllexport)
|
|
|
|
#endif
|
|
|
|
int sqlite3_blobio_init(
|
|
|
|
sqlite3 *db,
|
|
|
|
char **pzErrMsg,
|
|
|
|
const sqlite3_api_routines *pApi
|
|
|
|
){
|
|
|
|
int rc = SQLITE_OK;
|
|
|
|
SQLITE_EXTENSION_INIT2(pApi);
|
|
|
|
(void)pzErrMsg; /* Unused parameter */
|
|
|
|
rc = sqlite3_create_function(db, "readblob", 6, SQLITE_UTF8, 0,
|
|
|
|
readblobFunc, 0, 0);
|
|
|
|
if( rc==SQLITE_OK ){
|
|
|
|
rc = sqlite3_create_function(db, "writeblob", 6, SQLITE_UTF8, 0,
|
|
|
|
writeblobFunc, 0, 0);
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|