/* ** 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 #include 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); sqlite3_result_error(context, "BLOB read failed", -1); }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; }