aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordan <dan@noemail.net>2014-11-07 14:41:11 +0000
committerdan <dan@noemail.net>2014-11-07 14:41:11 +0000
commitb391b944319fad869ec86b4798a0e62daf04d48c (patch)
treead20af6386af9fcec7f0f4f8b4e2abc403eae496 /src
parent97d3898335b39b07e8753d0d705dd4b396bd85a2 (diff)
downloadsqlite-b391b944319fad869ec86b4798a0e62daf04d48c.tar.gz
sqlite-b391b944319fad869ec86b4798a0e62daf04d48c.zip
Add new test file e_blobopen.test, containing tests for sqlite3_blob_open().
FossilOrigin-Name: ecbccd0e594d22b3ae7fabc8037951dc49570bc3
Diffstat (limited to 'src')
-rw-r--r--src/sqlite.h.in62
-rw-r--r--src/tclsqlite.c2
-rw-r--r--src/test1.c152
-rw-r--r--src/test_blob.c319
-rw-r--r--src/test_config.c6
-rw-r--r--src/vdbe.c5
6 files changed, 367 insertions, 179 deletions
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index fc741b729..bed64bfae 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -5657,26 +5657,42 @@ typedef struct sqlite3_blob sqlite3_blob;
** SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;
** </pre>)^
**
+** ^(Parameter zDb is not the filename that contains the database, but
+** rather the symbolic name of the database. For attached databases, this is
+** the name that appears after the AS keyword in the [ATTACH] statement.
+** For the main database file, the database name is "main". For TEMP
+** tables, the database name is "temp".)^
+**
** ^If the flags parameter is non-zero, then the BLOB is opened for read
-** and write access. ^If it is zero, the BLOB is opened for read access.
-** ^It is not possible to open a column that is part of an index or primary
-** key for writing. ^If [foreign key constraints] are enabled, it is
-** not possible to open a column that is part of a [child key] for writing.
-**
-** ^Note that the database name is not the filename that contains
-** the database but rather the symbolic name of the database that
-** appears after the AS keyword when the database is connected using [ATTACH].
-** ^For the main database file, the database name is "main".
-** ^For TEMP tables, the database name is "temp".
-**
-** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is written
-** to *ppBlob. Otherwise an [error code] is returned and *ppBlob is set
-** to be a null pointer.)^
-** ^This function sets the [database connection] error code and message
-** accessible via [sqlite3_errcode()] and [sqlite3_errmsg()] and related
-** functions. ^Note that the *ppBlob variable is always initialized in a
-** way that makes it safe to invoke [sqlite3_blob_close()] on *ppBlob
-** regardless of the success or failure of this routine.
+** and write access. ^If the flags parameter is zero, the BLOB is opened for
+** read-only access.
+**
+** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is stored
+** in *ppBlob. Otherwise an [error code] is returned and, unless the error
+** code is SQLITE_MISUSE, *ppBlob is set to NULL.)^ ^This means that, provided
+** the API is not misused, it is always safe to call [sqlite3_blob_close()]
+** on *ppBlob after this function it returns.
+**
+** This function fails with SQLITE_ERROR if any of the following are true:
+** <ul>
+** <li> ^(Database zDb does not exist)^,
+** <li> ^(Table zTable does not exist within database zDb)^,
+** <li> ^(Table zTable is a WITHOUT ROWID table)^,
+** <li> ^(Column zColumn does not exist)^,
+** <li> ^(Row iRow is not present in the table)^,
+** <li> ^(The specified column of row iRow contains a value that is not
+** a TEXT or BLOB value)^,
+** <li> ^(Column zColumn is part of an index, PRIMARY KEY or UNIQUE
+** constraint and the blob is being opened for read/write access)^,
+** <li> ^([foreign key constraints | Foreign key constraints] are enabled,
+** column zColumn is part of a [child key] definition and the blob is
+** being opened for read/write access)^.
+** </ul>
+**
+** ^Unless it returns SQLITE_MISUSE, this function sets the
+** [database connection] error code and message accessible via
+** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions.
+**
**
** ^(If the row that a BLOB handle points to is modified by an
** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects
@@ -5694,13 +5710,9 @@ typedef struct sqlite3_blob sqlite3_blob;
** interface. Use the [UPDATE] SQL command to change the size of a
** blob.
**
-** ^The [sqlite3_blob_open()] interface will fail for a [WITHOUT ROWID]
-** table. Incremental BLOB I/O is not possible on [WITHOUT ROWID] tables.
-**
** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces
-** and the built-in [zeroblob] SQL function can be used, if desired,
-** to create an empty, zero-filled blob in which to read or write using
-** this interface.
+** and the built-in [zeroblob] SQL function may be used to create a
+** zero-filled blob to read or write using the incremental-blob interface.
**
** To avoid a resource leak, every open [BLOB handle] should eventually
** be released by a call to [sqlite3_blob_close()].
diff --git a/src/tclsqlite.c b/src/tclsqlite.c
index bff4a9242..4fb78a5d1 100644
--- a/src/tclsqlite.c
+++ b/src/tclsqlite.c
@@ -3725,6 +3725,7 @@ static void init_all(Tcl_Interp *interp){
extern int Sqlitetest9_Init(Tcl_Interp*);
extern int Sqlitetestasync_Init(Tcl_Interp*);
extern int Sqlitetest_autoext_Init(Tcl_Interp*);
+ extern int Sqlitetest_blob_Init(Tcl_Interp*);
extern int Sqlitetest_demovfs_Init(Tcl_Interp *);
extern int Sqlitetest_func_Init(Tcl_Interp*);
extern int Sqlitetest_hexio_Init(Tcl_Interp*);
@@ -3768,6 +3769,7 @@ static void init_all(Tcl_Interp *interp){
Sqlitetest9_Init(interp);
Sqlitetestasync_Init(interp);
Sqlitetest_autoext_Init(interp);
+ Sqlitetest_blob_Init(interp);
Sqlitetest_demovfs_Init(interp);
Sqlitetest_func_Init(interp);
Sqlitetest_hexio_Init(interp);
diff --git a/src/test1.c b/src/test1.c
index fcc1a23de..e7a592932 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -1651,154 +1651,6 @@ static int blobHandleFromObj(
return TCL_OK;
}
-/*
-** sqlite3_blob_bytes CHANNEL
-*/
-static int test_blob_bytes(
- ClientData clientData, /* Not used */
- Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
- int objc, /* Number of arguments */
- Tcl_Obj *CONST objv[] /* Command arguments */
-){
- sqlite3_blob *pBlob;
- int nByte;
-
- if( objc!=2 ){
- Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL");
- return TCL_ERROR;
- }
-
- if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
- nByte = sqlite3_blob_bytes(pBlob);
- Tcl_SetObjResult(interp, Tcl_NewIntObj(nByte));
-
- return TCL_OK;
-}
-
-/*
-** sqlite3_blob_close CHANNEL
-*/
-static int test_blob_close(
- ClientData clientData, /* Not used */
- Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
- int objc, /* Number of arguments */
- Tcl_Obj *CONST objv[] /* Command arguments */
-){
- sqlite3_blob *pBlob;
-
- if( objc!=2 ){
- Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL");
- return TCL_ERROR;
- }
-
- if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
- sqlite3_blob_close(pBlob);
-
- return TCL_OK;
-}
-
-/*
-** sqlite3_blob_read CHANNEL OFFSET N
-**
-** This command is used to test the sqlite3_blob_read() in ways that
-** the Tcl channel interface does not. The first argument should
-** be the name of a valid channel created by the [incrblob] method
-** of a database handle. This function calls sqlite3_blob_read()
-** to read N bytes from offset OFFSET from the underlying SQLite
-** blob handle.
-**
-** On success, a byte-array object containing the read data is
-** returned. On failure, the interpreter result is set to the
-** text representation of the returned error code (i.e. "SQLITE_NOMEM")
-** and a Tcl exception is thrown.
-*/
-static int test_blob_read(
- ClientData clientData, /* Not used */
- Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
- int objc, /* Number of arguments */
- Tcl_Obj *CONST objv[] /* Command arguments */
-){
- sqlite3_blob *pBlob;
- int nByte;
- int iOffset;
- unsigned char *zBuf = 0;
- int rc;
-
- if( objc!=4 ){
- Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET N");
- return TCL_ERROR;
- }
-
- if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
- if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset)
- || TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &nByte)
- ){
- return TCL_ERROR;
- }
-
- if( nByte>0 ){
- zBuf = (unsigned char *)Tcl_Alloc(nByte);
- }
- rc = sqlite3_blob_read(pBlob, zBuf, nByte, iOffset);
- if( rc==SQLITE_OK ){
- Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(zBuf, nByte));
- }else{
- Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
- }
- Tcl_Free((char *)zBuf);
-
- return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
-}
-
-/*
-** sqlite3_blob_write CHANNEL OFFSET DATA ?NDATA?
-**
-** This command is used to test the sqlite3_blob_write() in ways that
-** the Tcl channel interface does not. The first argument should
-** be the name of a valid channel created by the [incrblob] method
-** of a database handle. This function calls sqlite3_blob_write()
-** to write the DATA byte-array to the underlying SQLite blob handle.
-** at offset OFFSET.
-**
-** On success, an empty string is returned. On failure, the interpreter
-** result is set to the text representation of the returned error code
-** (i.e. "SQLITE_NOMEM") and a Tcl exception is thrown.
-*/
-static int test_blob_write(
- ClientData clientData, /* Not used */
- Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
- int objc, /* Number of arguments */
- Tcl_Obj *CONST objv[] /* Command arguments */
-){
- sqlite3_blob *pBlob;
- int iOffset;
- int rc;
-
- unsigned char *zBuf;
- int nBuf;
-
- if( objc!=4 && objc!=5 ){
- Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET DATA ?NDATA?");
- return TCL_ERROR;
- }
-
- if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
- if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) ){
- return TCL_ERROR;
- }
-
- zBuf = Tcl_GetByteArrayFromObj(objv[3], &nBuf);
- if( objc==5 && Tcl_GetIntFromObj(interp, objv[4], &nBuf) ){
- return TCL_ERROR;
- }
- rc = sqlite3_blob_write(pBlob, zBuf, nBuf, iOffset);
- if( rc!=SQLITE_OK ){
- Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
- }
-
- return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
-}
-
static int test_blob_reopen(
ClientData clientData, /* Not used */
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
@@ -6910,11 +6762,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "sqlite3_table_column_metadata", test_table_column_metadata, 0 },
#endif
#ifndef SQLITE_OMIT_INCRBLOB
- { "sqlite3_blob_read", test_blob_read, 0 },
- { "sqlite3_blob_write", test_blob_write, 0 },
{ "sqlite3_blob_reopen", test_blob_reopen, 0 },
- { "sqlite3_blob_bytes", test_blob_bytes, 0 },
- { "sqlite3_blob_close", test_blob_close, 0 },
#endif
{ "pcache_stats", test_pcache_stats, 0 },
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
diff --git a/src/test_blob.c b/src/test_blob.c
new file mode 100644
index 000000000..d88c91366
--- /dev/null
+++ b/src/test_blob.c
@@ -0,0 +1,319 @@
+/*
+** 2014 October 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.
+**
+*************************************************************************
+**
+*/
+#include "sqliteInt.h"
+#include "tcl.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+/* These functions are implemented in main.c. */
+extern const char *sqlite3ErrName(int);
+
+/* From test1.c: */
+extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
+extern void *sqlite3TestTextToPtr(const char *z);
+
+/*
+** Return a pointer to a buffer containing a text representation of the
+** pointer passed as the only argument. The original pointer may be extracted
+** from the text using sqlite3TestTextToPtr().
+*/
+static char *ptrToText(void *p){
+ static char buf[100];
+ sqlite3_snprintf(sizeof(buf)-1, buf, "%p", p);
+ return buf;
+}
+
+/*
+** Attempt to extract a blob handle (type sqlite3_blob*) from the Tcl
+** object passed as the second argument. If successful, set *ppBlob to
+** point to the blob handle and return TCL_OK. Otherwise, store an error
+** message in the tcl interpreter and return TCL_ERROR. The final value
+** of *ppBlob is undefined in this case.
+**
+** If the object contains a string that begins with "incrblob_", then it
+** is assumed to be the name of a Tcl channel opened using the [db incrblob]
+** command (see tclsqlite.c). Otherwise, it is assumed to be a pointer
+** encoded using the ptrToText() routine or similar.
+*/
+static int blobHandleFromObj(
+ Tcl_Interp *interp,
+ Tcl_Obj *pObj,
+ sqlite3_blob **ppBlob
+){
+ char *z;
+ int n;
+
+ z = Tcl_GetStringFromObj(pObj, &n);
+ if( n==0 ){
+ *ppBlob = 0;
+ }else if( n>9 && 0==memcmp("incrblob_", z, 9) ){
+ int notUsed;
+ Tcl_Channel channel;
+ ClientData instanceData;
+
+ channel = Tcl_GetChannel(interp, z, &notUsed);
+ if( !channel ) return TCL_ERROR;
+
+ Tcl_Flush(channel);
+ Tcl_Seek(channel, 0, SEEK_SET);
+
+ instanceData = Tcl_GetChannelInstanceData(channel);
+ *ppBlob = *((sqlite3_blob **)instanceData);
+ }else{
+ *ppBlob = (sqlite3_blob*)sqlite3TestTextToPtr(z);
+ }
+
+ return TCL_OK;
+}
+
+/*
+** Like Tcl_GetString(), except that if the string is 0 bytes in size, a
+** NULL Pointer is returned.
+*/
+static char *blobStringFromObj(Tcl_Obj *pObj){
+ int n;
+ char *z;
+ z = Tcl_GetStringFromObj(pObj, &n);
+ return (n ? z : 0);
+}
+
+/*
+** sqlite3_blob_open DB DATABASE TABLE COLUMN ROWID FLAGS VARNAME
+**
+** Tcl test harness for the sqlite3_blob_open() function.
+*/
+static int test_blob_open(
+ ClientData clientData, /* Not used */
+ Tcl_Interp *interp, /* Calling TCL interpreter */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3 *db;
+ const char *zDb;
+ const char *zTable;
+ const char *zColumn;
+ sqlite_int64 iRowid;
+ int flags;
+ const char *zVarname;
+ int nVarname;
+
+ sqlite3_blob *pBlob = (sqlite3_blob*)0xFFFFFFFF;
+ int rc;
+
+ if( objc!=8 ){
+ const char *zUsage = "DB DATABASE TABLE COLUMN ROWID FLAGS VARNAME";
+ Tcl_WrongNumArgs(interp, 1, objv, zUsage);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ zDb = Tcl_GetString(objv[2]);
+ zTable = blobStringFromObj(objv[3]);
+ zColumn = Tcl_GetString(objv[4]);
+ if( Tcl_GetWideIntFromObj(interp, objv[5], &iRowid) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[6], &flags) ) return TCL_ERROR;
+ zVarname = Tcl_GetStringFromObj(objv[7], &nVarname);
+
+ if( nVarname>0 ){
+ rc = sqlite3_blob_open(db, zDb, zTable, zColumn, iRowid, flags, &pBlob);
+ Tcl_SetVar(interp, zVarname, ptrToText(pBlob), 0);
+ }else{
+ rc = sqlite3_blob_open(db, zDb, zTable, zColumn, iRowid, flags, 0);
+ }
+
+ if( rc==SQLITE_OK ){
+ Tcl_ResetResult(interp);
+ }else{
+ Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+
+/*
+** sqlite3_blob_close HANDLE
+*/
+static int test_blob_close(
+ ClientData clientData, /* Not used */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3_blob *pBlob;
+ int rc;
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
+ return TCL_ERROR;
+ }
+
+ if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
+ rc = sqlite3_blob_close(pBlob);
+
+ if( rc ){
+ Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE);
+ }else{
+ Tcl_ResetResult(interp);
+ }
+ return TCL_OK;
+}
+
+/*
+** sqlite3_blob_bytes HANDLE
+*/
+static int test_blob_bytes(
+ ClientData clientData, /* Not used */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3_blob *pBlob;
+ int nByte;
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
+ return TCL_ERROR;
+ }
+
+ if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
+ nByte = sqlite3_blob_bytes(pBlob);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(nByte));
+
+ return TCL_OK;
+}
+
+/*
+** sqlite3_blob_read CHANNEL OFFSET N
+**
+** This command is used to test the sqlite3_blob_read() in ways that
+** the Tcl channel interface does not. The first argument should
+** be the name of a valid channel created by the [incrblob] method
+** of a database handle. This function calls sqlite3_blob_read()
+** to read N bytes from offset OFFSET from the underlying SQLite
+** blob handle.
+**
+** On success, a byte-array object containing the read data is
+** returned. On failure, the interpreter result is set to the
+** text representation of the returned error code (i.e. "SQLITE_NOMEM")
+** and a Tcl exception is thrown.
+*/
+static int test_blob_read(
+ ClientData clientData, /* Not used */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3_blob *pBlob;
+ int nByte;
+ int iOffset;
+ unsigned char *zBuf = 0;
+ int rc;
+
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET N");
+ return TCL_ERROR;
+ }
+
+ if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
+ if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset)
+ || TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &nByte)
+ ){
+ return TCL_ERROR;
+ }
+
+ if( nByte>0 ){
+ zBuf = (unsigned char *)Tcl_Alloc(nByte);
+ }
+ rc = sqlite3_blob_read(pBlob, zBuf, nByte, iOffset);
+ if( rc==SQLITE_OK ){
+ Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(zBuf, nByte));
+ }else{
+ Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
+ }
+ Tcl_Free((char *)zBuf);
+
+ return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
+}
+
+/*
+** sqlite3_blob_write HANDLE OFFSET DATA ?NDATA?
+**
+** This command is used to test the sqlite3_blob_write() in ways that
+** the Tcl channel interface does not. The first argument should
+** be the name of a valid channel created by the [incrblob] method
+** of a database handle. This function calls sqlite3_blob_write()
+** to write the DATA byte-array to the underlying SQLite blob handle.
+** at offset OFFSET.
+**
+** On success, an empty string is returned. On failure, the interpreter
+** result is set to the text representation of the returned error code
+** (i.e. "SQLITE_NOMEM") and a Tcl exception is thrown.
+*/
+static int test_blob_write(
+ ClientData clientData, /* Not used */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3_blob *pBlob;
+ int iOffset;
+ int rc;
+
+ unsigned char *zBuf;
+ int nBuf;
+
+ if( objc!=4 && objc!=5 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "HANDLE OFFSET DATA ?NDATA?");
+ return TCL_ERROR;
+ }
+
+ if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
+ if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) ){
+ return TCL_ERROR;
+ }
+
+ zBuf = Tcl_GetByteArrayFromObj(objv[3], &nBuf);
+ if( objc==5 && Tcl_GetIntFromObj(interp, objv[4], &nBuf) ){
+ return TCL_ERROR;
+ }
+ rc = sqlite3_blob_write(pBlob, zBuf, nBuf, iOffset);
+ if( rc!=SQLITE_OK ){
+ Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
+ }
+
+ return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
+}
+
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int Sqlitetest_blob_Init(Tcl_Interp *interp){
+ static struct {
+ char *zName;
+ Tcl_ObjCmdProc *xProc;
+ } aObjCmd[] = {
+ { "sqlite3_blob_open", test_blob_open },
+ { "sqlite3_blob_close", test_blob_close },
+ { "sqlite3_blob_bytes", test_blob_bytes },
+ { "sqlite3_blob_read", test_blob_read },
+ { "sqlite3_blob_write", test_blob_write },
+ };
+ int i;
+ for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
+ Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0);
+ }
+ return TCL_OK;
+}
diff --git a/src/test_config.c b/src/test_config.c
index 03f4f76f6..834113b33 100644
--- a/src/test_config.c
+++ b/src/test_config.c
@@ -155,6 +155,12 @@ static void set_options(Tcl_Interp *interp){
Tcl_SetVar2(interp, "sqlite_options", "analyze", "1", TCL_GLOBAL_ONLY);
#endif
+#ifdef SQLITE_ENABLE_API_ARMOR
+ Tcl_SetVar2(interp, "sqlite_options", "api_armor", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "api_armor", "0", TCL_GLOBAL_ONLY);
+#endif
+
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
Tcl_SetVar2(interp, "sqlite_options", "atomicwrite", "1", TCL_GLOBAL_ONLY);
#else
diff --git a/src/vdbe.c b/src/vdbe.c
index bf9b233a2..f80e7787a 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -3798,10 +3798,11 @@ case OP_Found: { /* jump, in3 */
}else{
pIdxKey = sqlite3VdbeAllocUnpackedRecord(
pC->pKeyInfo, aTempRec, sizeof(aTempRec), &pFree
- );
+ );
if( pIdxKey==0 ) goto no_mem;
assert( pIn3->flags & MEM_Blob );
- assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */
+ /* assert( (pIn3->flags & MEM_Zero)==0 ); // zeroblobs already expanded */
+ ExpandBlob(pIn3);
sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey);
}
pIdxKey->default_rc = 0;