Add new test file e_blobopen.test, containing tests for sqlite3_blob_open().

FossilOrigin-Name: ecbccd0e594d22b3ae7fabc8037951dc49570bc3
This commit is contained in:
dan 2014-11-07 14:41:11 +00:00
parent 97d3898335
commit b391b94431
12 changed files with 950 additions and 194 deletions

View File

@ -246,6 +246,7 @@ TESTSRC = \
$(TOP)/src/test_autoext.c \
$(TOP)/src/test_async.c \
$(TOP)/src/test_backup.c \
$(TOP)/src/test_blob.c \
$(TOP)/src/test_btree.c \
$(TOP)/src/test_config.c \
$(TOP)/src/test_demovfs.c \

View File

@ -1,5 +1,5 @@
C Fix\sanother\sharmless\scomment\stypo.
D 2014-11-07T14:37:32.349
C Add\snew\stest\sfile\se_blobopen.test,\scontaining\stests\sfor\ssqlite3_blob_open().
D 2014-11-07T14:41:11.404
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -151,7 +151,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
F main.mk bbc8b6000ed143a1a8d31d3b4995c359a3188fa1
F main.mk 3fececc835c5c23637c3e0af970e27a81a9ba476
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
F mkopcodeh.awk c6b3fa301db6ef7ac916b14c60868aeaec1337b5
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
@ -229,15 +229,15 @@ F src/resolve.c 4965007d6497b6a4d7a6d98751cc39712885f952
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
F src/select.c 428165951748151e87a15295b7357221433e311b
F src/shell.c 0500307f63e3675dc4ab21112c6086b4d146fc08
F src/sqlite.h.in f433227d7f619887a1064913fa66cefa3da4349a
F src/sqlite.h.in 5abfd7d6c9bd2054105820de5ce12b1e491b1dc9
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d
F src/sqliteInt.h 71b0bf1a7fc55b5cb374f7579fd140e730a6e0f4
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 81712116e826b0089bb221b018929536b2b5406f
F src/table.c f142bba7903e93ca8d113a5b8877a108ad1a27dc
F src/tclsqlite.c 7cdd4dd3c2a4183483feca260070d73d6e22cd47
F src/test1.c ac7f3bad83ef4508d5efc85b32e86da48db8ed7e
F src/tclsqlite.c 0a874655dd39a9875e39c5d3c464db662171d228
F src/test1.c cce6ad0effe8c66a62d2634d9714ffdc7372ef11
F src/test2.c 98049e51a17dc62606a99a9eb95ee477f9996712
F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c
F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df
@ -249,8 +249,9 @@ F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60
F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8
F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12
F src/test_backup.c 3875e899222b651e18b662f86e0e50daa946344e
F src/test_blob.c 1f2e3e25255b731c4fcf15ee7990d06347cb6c09
F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f
F src/test_config.c c8b8b50bf2fe5102de10e4c7100b746d7f6bf62f
F src/test_config.c 035c17a173937d019b8dfc1d524f9d3fc8123504
F src/test_demovfs.c 69b2085076654ebc18014cbc6386f04409c959a9
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f
@ -289,7 +290,7 @@ F src/update.c 3c4ecc282accf12d39edb8d524cf089645e55a13
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
F src/util.c 3b627daa45c7308c1e36e3dbaa3f9ce7e5c7fa73
F src/vacuum.c 9b30ec729337dd012ed88d4c292922c8ef9cf00c
F src/vdbe.c 3fd4ebd3e87b63175bfd2be747608bae1670b4df
F src/vdbe.c d5dab22208e36e5689e9fb553aea3613921054ec
F src/vdbe.h 6fc69d9c5e146302c56e163cb4b31d1ee64a18c3
F src/vdbeInt.h 9bb69ff2447c34b6ccc58b34ec35b615f86ead78
F src/vdbeapi.c 07acb615d1e4170e71fc1b0d087f3c53a1ad8e83
@ -451,6 +452,7 @@ F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2
F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e
F test/distinct.test 086e70c765f172e8974e9f83b9ac5ca03c154e77
F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
F test/e_blobopen.test 234f960d90235a9b51ec3ca1e062e8541dd558d8 w test/e_blob.test
F test/e_changes.test fd66105385153dbf21fdb35eb8ef6c3e1eade579
F test/e_createtable.test c7e67b49e6cf92473c8fb30ab26143e9e2128cf7
F test/e_delete.test d5186e2f5478b659f16a2c8b66c09892823e542a
@ -491,7 +493,7 @@ F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49
F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d
F test/fkey5.test 8a1fde4e7721ae00b05b3178888833726ca2df8d
F test/fkey6.test abb59f866c1b44926fd02d1fdd217d831fe04f48
F test/fkey7.test e31d0e71a41c1d29349a16448d6c420e2c53a8fc
F test/fkey7.test 72e915890ee4a005daaf3002cb208e8fe973ac13
F test/fkey_malloc.test 594a7ea1fbab553c036c70813cd8bd9407d63749
F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb
F test/fts-9fd058691.test 78b887e30ae6816df0e1fed6259de4b5a64ad33c
@ -1155,7 +1157,7 @@ F test/without_rowid1.test 7862e605753c8d25329f665fa09072e842183151
F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
F test/without_rowid3.test 1081aabf60a1e1123b7f9a8f6ae19954351843b0
F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a
F test/without_rowid5.test b4a639a367f04d382d20e8f44fc1be4f2d57d107
F test/without_rowid5.test 61256715b686359df48ca1742db50cc7e3e7b862
F test/wordcount.c 9915e06cb33d8ca8109b8700791afe80d305afda
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
F test/zerodamage.test cf6748bad89553cc1632be51a6f54e487e4039ac
@ -1211,7 +1213,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P 5700508535c35ac6b158b527e1d47e529e8e28ab
R 95ce072bedb368f2e0f6da4bf10906cf
U drh
Z 8c052ffec320f3367f2daa07942512b6
P b45bc80bb16f07192d84fd14433bb724a84d4146
R 0ead7b89f3ed1286937ae097bba351c9
U dan
Z d785e78de9d268050b7beac3067db32e

View File

@ -1 +1 @@
b45bc80bb16f07192d84fd14433bb724a84d4146
ecbccd0e594d22b3ae7fabc8037951dc49570bc3

View File

@ -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.
** and write access. ^If the flags parameter is zero, the BLOB is opened for
** read-only access.
**
** ^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 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.
**
** ^(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.
**
** ^(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()].

View File

@ -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);

View File

@ -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

319
src/test_blob.c Normal file
View File

@ -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;
}

View File

@ -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

View File

@ -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;

549
test/e_blobopen.test Normal file
View File

@ -0,0 +1,549 @@
# 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.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix e_blobopen
forcedelete test.db2
do_execsql_test 1.0 {
ATTACH 'test.db2' AS aux;
CREATE TABLE main.t1(a INTEGER PRIMARY KEY, b TEXT, c BLOB);
CREATE TEMP TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c BLOB);
CREATE TABLE aux.t1(a INTEGER PRIMARY KEY, b TEXT, c BLOB);
CREATE TABLE main.x1(a INTEGER PRIMARY KEY, b TEXT, c BLOB);
CREATE TEMP TABLE x2(a INTEGER PRIMARY KEY, b TEXT, c BLOB);
CREATE TABLE aux.x3(a INTEGER PRIMARY KEY, b TEXT, c BLOB);
INSERT INTO main.t1 VALUES(1, 'main one', X'0101');
INSERT INTO main.t1 VALUES(2, 'main two', X'0102');
INSERT INTO main.t1 VALUES(3, 'main three', X'0103');
INSERT INTO main.t1 VALUES(4, 'main four', X'0104');
INSERT INTO main.t1 VALUES(5, 'main five', X'0105');
INSERT INTO main.x1 VALUES(1, 'x main one', X'000101');
INSERT INTO main.x1 VALUES(2, 'x main two', X'000102');
INSERT INTO main.x1 VALUES(3, 'x main three', X'000103');
INSERT INTO main.x1 VALUES(4, 'x main four', X'000104');
INSERT INTO main.x1 VALUES(5, 'x main five', X'000105');
INSERT INTO temp.t1 VALUES(1, 'temp one', X'0201');
INSERT INTO temp.t1 VALUES(2, 'temp two', X'0202');
INSERT INTO temp.t1 VALUES(3, 'temp three', X'0203');
INSERT INTO temp.t1 VALUES(4, 'temp four', X'0204');
INSERT INTO temp.t1 VALUES(5, 'temp five', X'0205');
INSERT INTO temp.x2 VALUES(1, 'x temp one', X'000201');
INSERT INTO temp.x2 VALUES(2, 'x temp two', X'000202');
INSERT INTO temp.x2 VALUES(3, 'x temp three', X'000203');
INSERT INTO temp.x2 VALUES(4, 'x temp four', X'000204');
INSERT INTO temp.x2 VALUES(5, 'x temp five', X'000205');
INSERT INTO aux.t1 VALUES(1, 'aux one', X'0301');
INSERT INTO aux.t1 VALUES(2, 'aux two', X'0302');
INSERT INTO aux.t1 VALUES(3, 'aux three', X'0303');
INSERT INTO aux.t1 VALUES(4, 'aux four', X'0304');
INSERT INTO aux.t1 VALUES(5, 'aux five', X'0305');
INSERT INTO aux.x3 VALUES(1, 'x aux one', X'000301');
INSERT INTO aux.x3 VALUES(2, 'x aux two', X'000302');
INSERT INTO aux.x3 VALUES(3, 'x aux three', X'000303');
INSERT INTO aux.x3 VALUES(4, 'x aux four', X'000304');
INSERT INTO aux.x3 VALUES(5, 'x aux five', X'000305');
}
#-------------------------------------------------------------------------
# EVIDENCE-OF: R-37639-55938 This interfaces opens a handle to the BLOB
# located in row iRow, column zColumn, table zTable in database zDb; in
# other words, the same BLOB that would be selected by: SELECT zColumn
# FROM zDb.zTable WHERE rowid = iRow;
#
proc read_blob {zDb zTab zCol iRow} {
sqlite3_blob_open db $zDb $zTab $zCol $iRow 0 B
set nByte [sqlite3_blob_bytes $B]
set data [sqlite3_blob_read $B 0 $nByte]
sqlite3_blob_close $B
return $data
}
do_test 1.1.1 { read_blob main t1 b 1 } "main one"
do_test 1.1.2 { read_blob main t1 c 1 } "\01\01"
do_test 1.1.3 { read_blob temp t1 b 1 } "temp one"
do_test 1.1.4 { read_blob temp t1 c 1 } "\02\01"
do_test 1.1.6 { read_blob aux t1 b 1 } "aux one"
do_test 1.1.7 { read_blob aux t1 c 1 } "\03\01"
do_test 1.2.1 { read_blob main t1 b 4 } "main four"
do_test 1.2.2 { read_blob main t1 c 4 } "\01\04"
do_test 1.2.3 { read_blob temp t1 b 4 } "temp four"
do_test 1.2.4 { read_blob temp t1 c 4 } "\02\04"
do_test 1.2.6 { read_blob aux t1 b 4 } "aux four"
do_test 1.2.7 { read_blob aux t1 c 4 } "\03\04"
do_test 1.3.1 { read_blob main x1 b 2 } "x main two"
do_test 1.3.2 { read_blob main x1 c 2 } "\00\01\02"
do_test 1.3.3 { read_blob temp x2 b 2 } "x temp two"
do_test 1.3.4 { read_blob temp x2 c 2 } "\00\02\02"
do_test 1.3.6 { read_blob aux x3 b 2 } "x aux two"
do_test 1.3.7 { read_blob aux x3 c 2 } "\00\03\02"
#-------------------------------------------------------------------------
# EVIDENCE-OF: R-27234-05761 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".
#
# The test cases immediately above demonstrate that the database name
# for the main db, for TEMP tables and for those in attached databases
# is correct. The following tests check that filenames cannot be
# used as well.
#
do_test 2.1 {
list [catch { sqlite3_blob_open db "test.db" t1 b 1 0 B } msg] $msg
} {1 SQLITE_ERROR}
do_test 2.2 {
list [catch { sqlite3_blob_open db "test.db2" t1 b 1 0 B } msg] $msg
} {1 SQLITE_ERROR}
#-------------------------------------------------------------------------
# EVIDENCE-OF: R-50854-53979 If the flags parameter is non-zero, then
# the BLOB is opened for read and write access.
#
# EVIDENCE-OF: R-03922-41160 If the flags parameter is zero, the BLOB is
# opened for read-only access.
#
foreach {tn iRow flags} {
1 1 0
2 2 1
3 3 -1
4 4 2147483647
5 5 -2147483648
} {
do_test 3.$tn.1 {
sqlite3_blob_open db main x1 c $iRow $flags B
set n [sqlite3_blob_bytes $B]
sqlite3_blob_read $B 0 $n
} [binary format ccc 0 1 $iRow]
if {$flags==0} {
# Blob was opened for read-only access - writing returns an error.
do_test 3.$tn.2 {
list [catch { sqlite3_blob_write $B 0 xxx 3 } msg] $msg
} {1 SQLITE_READONLY}
do_execsql_test 3.$tn.3 {
SELECT c FROM x1 WHERE a=$iRow;
} [binary format ccc 0 1 $iRow]
} else {
# Blob was opened for read/write access - writing succeeds
do_test 3.$tn.4 {
list [catch { sqlite3_blob_write $B 0 xxx 3 } msg] $msg
} {0 {}}
do_execsql_test 3.$tn.5 {
SELECT c FROM x1 WHERE a=$iRow;
} {xxx}
}
sqlite3_blob_close $B
}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 4.0 {
CREATE TABLE t1(x, y);
INSERT INTO t1 VALUES('abcd', 152);
INSERT INTO t1 VALUES(NULL, X'00010203');
INSERT INTO t1 VALUES('', 154.2);
CREATE TABLE t2(x PRIMARY KEY, y) WITHOUT ROWID;
INSERT INTO t2 VALUES(1, 'blob');
CREATE TABLE t3(a PRIMARY KEY, b, c, d, e, f, UNIQUE(e, f));
INSERT INTO t3 VALUES('aaaa', 'bbbb', 'cccc', 'dddd', 'eeee', 'ffff');
CREATE INDEX t3b ON t3(b);
CREATE TABLE p1(x PRIMARY KEY);
INSERT INTO p1 VALUES('abc');
CREATE TABLE c1(a INTEGER PRIMARY KEY, b REFERENCES p1);
INSERT INTO c1 VALUES(45, 'abc');
}
proc test_blob_open {tn zDb zTab zCol iRow flags errcode errmsg} {
global B
set B "0x1234"
if {$errcode=="SQLITE_OK"} {
set expected "0 {}"
} else {
set expected "1 $errcode"
}
set ::res [list [
catch { sqlite3_blob_open db $zDb $zTab $zCol $iRow $flags B } msg
] $msg]
do_test 4.$tn.1 { set ::res } $expected
# EVIDENCE-OF: R-08940-21305 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.
#
# This proc (test_blob_open) is used below to test various error and
# non-error conditions. But never SQLITE_MISUSE conditions. So these
# test cases are considered as partly verifying the requirement above.
# See below for a test of the SQLITE_MISUSE case.
#
do_test 4.$tn.2 {
sqlite3_errcode db
} $errcode
do_test 4.$tn.3 {
sqlite3_errmsg db
} $errmsg
# EVIDENCE-OF: R-31086-35521 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.
#
do_test 4.$tn.4 {
expr {$B == "0"}
} [expr {$errcode != "SQLITE_OK"}]
# EVIDENCE-OF: R-63421-15521 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.
do_test 4.$tn.5 {
sqlite3_blob_close $B
} {}
}
# EVIDENCE-OF: R-31204-44780 Database zDb does not exist
test_blob_open 1 nosuchdb t1 x 1 0 SQLITE_ERROR "no such table: nosuchdb.t1"
# EVIDENCE-OF: R-28676-08005 Table zTable does not exist within database zDb
test_blob_open 2 main tt1 x 1 0 SQLITE_ERROR "no such table: main.tt1"
# EVIDENCE-OF: R-40134-30296 Table zTable is a WITHOUT ROWID table
test_blob_open 3 main t2 y 1 0 SQLITE_ERROR \
"cannot open table without rowid: t2"
# EVIDENCE-OF: R-56376-21261 Column zColumn does not exist
test_blob_open 4 main t1 z 2 0 SQLITE_ERROR "no such column: \"z\""
# EVIDENCE-OF: R-28258-23166 Row iRow is not present in the table
test_blob_open 5 main t1 y 6 0 SQLITE_ERROR "no such rowid: 6"
# EVIDENCE-OF: R-11683-62380 The specified column of row iRow contains a
# value that is not a TEXT or BLOB value
test_blob_open 6 main t1 x 2 0 SQLITE_ERROR "cannot open value of type null"
test_blob_open 7 main t1 y 1 0 SQLITE_ERROR "cannot open value of type integer"
test_blob_open 8 main t1 y 3 0 SQLITE_ERROR "cannot open value of type real"
# EVIDENCE-OF: R-34146-30782 Column zColumn is part of an index, PRIMARY
# KEY or UNIQUE constraint and the blob is being opened for read/write
# access
#
# Test cases 8.1.* show that such columns can be opened for read-access.
# Tests 8.2.* show that read-write access is different. Columns "c" and "c"
# are not part of an index, PK or UNIQUE constraint, so they work in both
# cases.
#
test_blob_open 8.1.1 main t3 a 1 0 SQLITE_OK "not an error"
test_blob_open 8.1.2 main t3 b 1 0 SQLITE_OK "not an error"
test_blob_open 8.1.3 main t3 c 1 0 SQLITE_OK "not an error"
test_blob_open 8.1.4 main t3 d 1 0 SQLITE_OK "not an error"
test_blob_open 8.1.5 main t3 e 1 0 SQLITE_OK "not an error"
test_blob_open 8.1.6 main t3 f 1 0 SQLITE_OK "not an error"
set cannot "cannot open indexed column for writing"
test_blob_open 8.2.1 main t3 a 1 8 SQLITE_ERROR $cannot
test_blob_open 8.2.2 main t3 b 1 8 SQLITE_ERROR $cannot
test_blob_open 8.2.3 main t3 c 1 8 SQLITE_OK "not an error"
test_blob_open 8.2.4 main t3 d 1 8 SQLITE_OK "not an error"
test_blob_open 8.2.5 main t3 e 1 8 SQLITE_ERROR $cannot
test_blob_open 8.2.6 main t3 f 1 8 SQLITE_ERROR $cannot
# EVIDENCE-OF: R-50117-55204 Foreign key constraints are enabled, column
# zColumn is part of a child key definition and the blob is being opened
# for read/write access
#
# 9.1: FK disabled, read-only access.
# 9.2: FK disabled, read-only access.
# 9.3: FK enabled, read/write access.
# 9.4: FK enabled, read/write access.
#
test_blob_open 9.1 main c1 b 45 0 SQLITE_OK "not an error"
test_blob_open 9.2 main c1 b 45 1 SQLITE_OK "not an error"
execsql { PRAGMA foreign_keys = ON }
test_blob_open 9.3 main c1 b 45 0 SQLITE_OK "not an error"
test_blob_open 9.4 main c1 b 45 1 SQLITE_ERROR \
"cannot open foreign key column for writing"
#-------------------------------------------------------------------------
# EVIDENCE-OF: R-08940-21305 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.
#
# This requirement is partially verified by the many uses of test
# command [test_blob_open] above. All that is left is to verify the
# SQLITE_MISUSE case.
#
# SQLITE_MISUSE is only returned if SQLITE_ENABLE_API_ARMOR is defined
# during compilation.
#
ifcapable api_armor {
sqlite3_blob_open db main t1 x 1 0 B
do_test 10.1.1 {
list [catch {sqlite3_blob_open $B main t1 x 1 0 B2} msg] $msg
} {1 SQLITE_MISUSE}
do_test 10.1.2 {
list [sqlite3_errcode db] [sqlite3_errmsg db]
} {SQLITE_OK {not an error}}
sqlite3_blob_close $B
do_test 10.2.1 {
list [catch {sqlite3_blob_open db main {} x 1 0 B} msg] $msg
} {1 SQLITE_MISUSE}
do_test 10.2.2 {
list [sqlite3_errcode db] [sqlite3_errmsg db]
} {SQLITE_OK {not an error}}
}
#-------------------------------------------------------------------------
# EVIDENCE-OF: R-50542-62589 If the row that a BLOB handle points to is
# modified by an UPDATE, DELETE, or by ON CONFLICT side-effects then the
# BLOB handle is marked as "expired". This is true if any column of the
# row is changed, even a column other than the one the BLOB handle is
# open on.
#
# EVIDENCE-OF: R-48367-20048 Calls to sqlite3_blob_read() and
# sqlite3_blob_write() for an expired BLOB handle fail with a return
# code of SQLITE_ABORT.
#
# 11.2: read-only handle, DELETE.
# 11.3: read-only handle, UPDATE.
# 11.4: read-only handle, REPLACE.
# 11.5: read/write handle, DELETE.
# 11.6: read/write handle, UPDATE.
# 11.7: read/write handle, REPLACE.
#
do_execsql_test 11.1 {
CREATE TABLE b1(a INTEGER PRIMARY KEY, b, c UNIQUE);
INSERT INTO b1 VALUES(1, '1234567890', 1);
INSERT INTO b1 VALUES(2, '1234567890', 2);
INSERT INTO b1 VALUES(3, '1234567890', 3);
INSERT INTO b1 VALUES(4, '1234567890', 4);
INSERT INTO b1 VALUES(5, '1234567890', 5);
INSERT INTO b1 VALUES(6, '1234567890', 6);
CREATE TABLE b2(a INTEGER PRIMARY KEY, b, c UNIQUE);
INSERT INTO b2 VALUES(1, '1234567890', 1);
INSERT INTO b2 VALUES(2, '1234567890', 2);
INSERT INTO b2 VALUES(3, '1234567890', 3);
INSERT INTO b2 VALUES(4, '1234567890', 4);
INSERT INTO b2 VALUES(5, '1234567890', 5);
INSERT INTO b2 VALUES(6, '1234567890', 6);
}
do_test 11.2.1 {
sqlite3_blob_open db main b1 b 2 0 B
sqlite3_blob_read $B 0 10
} {1234567890}
do_test 11.2.2 {
# Deleting a different row does not invalidate the blob handle.
execsql { DELETE FROM b1 WHERE a = 1 }
sqlite3_blob_read $B 0 10
} {1234567890}
do_test 11.2.3 {
execsql { DELETE FROM b1 WHERE a = 2 }
list [catch { sqlite3_blob_read $B 0 10 } msg] $msg
} {1 SQLITE_ABORT}
do_test 11.2.4 {
sqlite3_blob_close $B
} {}
do_test 11.3.1 {
sqlite3_blob_open db main b1 b 3 0 B
sqlite3_blob_read $B 0 10
} {1234567890}
do_test 11.3.2 {
# Updating a different row
execsql { UPDATE b1 SET c = 42 WHERE a=4 }
sqlite3_blob_read $B 0 10
} {1234567890}
do_test 11.3.3 {
execsql { UPDATE b1 SET c = 43 WHERE a=3 }
list [catch { sqlite3_blob_read $B 0 10 } msg] $msg
} {1 SQLITE_ABORT}
do_test 11.3.4 {
sqlite3_blob_close $B
} {}
do_test 11.4.1 {
sqlite3_blob_open db main b1 b 6 0 B
sqlite3_blob_read $B 0 10
} {1234567890}
do_test 11.4.2 {
# Replace a different row
execsql { INSERT OR REPLACE INTO b1 VALUES(10, 'abcdefghij', 5) }
sqlite3_blob_read $B 0 10
} {1234567890}
do_test 11.4.3 {
execsql { INSERT OR REPLACE INTO b1 VALUES(11, 'abcdefghij', 6) }
list [catch { sqlite3_blob_read $B 0 10 } msg] $msg
} {1 SQLITE_ABORT}
do_test 11.4.4 {
sqlite3_blob_close $B
} {}
do_test 11.4.1 {
sqlite3_blob_open db main b2 b 2 1 B
sqlite3_blob_write $B 0 "abcdefghij"
} {}
do_test 11.4.2 {
# Deleting a different row does not invalidate the blob handle.
execsql { DELETE FROM b2 WHERE a = 1 }
sqlite3_blob_write $B 0 "ABCDEFGHIJ"
} {}
do_test 11.4.3 {
execsql { DELETE FROM b2 WHERE a = 2 }
list [catch { sqlite3_blob_write $B 0 "0987654321" } msg] $msg
} {1 SQLITE_ABORT}
do_test 11.4.4 {
sqlite3_blob_close $B
} {}
do_test 11.5.1 {
sqlite3_blob_open db main b2 b 3 1 B
sqlite3_blob_write $B 0 "abcdefghij"
} {}
do_test 11.5.2 {
# Updating a different row
execsql { UPDATE b2 SET c = 42 WHERE a=4 }
sqlite3_blob_write $B 0 "ABCDEFGHIJ"
} {}
do_test 11.5.3 {
execsql { UPDATE b2 SET c = 43 WHERE a=3 }
list [catch { sqlite3_blob_write $B 0 "0987654321" } msg] $msg
} {1 SQLITE_ABORT}
do_test 11.5.4 {
sqlite3_blob_close $B
} {}
do_test 11.6.1 {
sqlite3_blob_open db main b2 b 6 1 B
sqlite3_blob_write $B 0 "abcdefghij"
} {}
do_test 11.6.2 {
# Replace a different row
execsql { INSERT OR REPLACE INTO b2 VALUES(10, 'abcdefghij', 5) }
sqlite3_blob_write $B 0 "ABCDEFGHIJ"
} {}
do_test 11.6.3 {
execsql { INSERT OR REPLACE INTO b2 VALUES(11, 'abcdefghij', 6) }
list [catch { sqlite3_blob_write $B 0 "0987654321" } msg] $msg
} {1 SQLITE_ABORT}
do_test 11.6.4 {
sqlite3_blob_close $B
} {}
#-------------------------------------------------------------------------
# EVIDENCE-OF: R-45408-40694 Changes written into a BLOB prior to the
# BLOB expiring are not rolled back by the expiration of the BLOB. Such
# changes will eventually commit if the transaction continues to
# completion.
#
do_execsql_test 12.1 {
CREATE TABLE b3(x INTEGER PRIMARY KEY, y TEXT, z INTEGER);
INSERT INTO b3 VALUES(22, '..........', NULL);
}
do_test 12.2 {
sqlite3_blob_open db main b3 y 22 1 B
sqlite3_blob_write $B 0 "xxxxx" 5
} {}
do_execsql_test 12.3 {
UPDATE b3 SET z = 'not null';
}
do_test 12.4 {
list [catch {sqlite3_blob_write $B 5 "xxxxx" 5} msg] $msg
} {1 SQLITE_ABORT}
do_execsql_test 12.5 {
SELECT * FROM b3;
} {22 xxxxx..... {not null}}
do_test 12.5 {
sqlite3_blob_close $B
} {}
do_execsql_test 12.6 {
SELECT * FROM b3;
} {22 xxxxx..... {not null}}
#-------------------------------------------------------------------------
# EVIDENCE-OF: R-58813-55036 The sqlite3_bind_zeroblob() and
# sqlite3_result_zeroblob() interfaces 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.
#
do_execsql_test 13.1 {
CREATE TABLE c2(i INTEGER PRIMARY KEY, j);
INSERT INTO c2 VALUES(10, zeroblob(24));
}
do_test 13.2 {
set stmt [sqlite3_prepare_v2 db "INSERT INTO c2 VALUES(11, ?)" -1]
sqlite3_bind_zeroblob $stmt 1 45
sqlite3_step $stmt
sqlite3_finalize $stmt
} {SQLITE_OK}
# The blobs can be read:
#
do_test 13.3.1 {
sqlite3_blob_open db main c2 j 10 1 B
sqlite3_blob_open db main c2 j 11 1 B2
list [sqlite3_blob_bytes $B] [sqlite3_blob_bytes $B2]
} {24 45}
do_test 13.3.2 {
sqlite3_blob_read $B 0 24
} [string repeat [binary format c 0] 24]
do_test 13.3.3 {
sqlite3_blob_read $B2 0 45
} [string repeat [binary format c 0] 45]
# And also written:
#
do_test 13.4.1 {
sqlite3_blob_write $B 0 [string repeat [binary format c 1] 24]
} {}
do_test 13.4.2 {
sqlite3_blob_write $B2 0 [string repeat [binary format c 1] 45]
} {}
do_test 13.5 {
sqlite3_blob_close $B
sqlite3_blob_close $B2
execsql { SELECT j FROM c2 }
} [list \
[string repeat [binary format c 1] 24] \
[string repeat [binary format c 1] 45] \
]
finish_test

View File

@ -50,5 +50,22 @@ do_tblsread_test 1.3 { UPDATE par SET a=? WHERE b=? } {c1 c2 par}
do_tblsread_test 1.4 { UPDATE par SET c=? WHERE b=? } {c3 par}
do_tblsread_test 1.5 { UPDATE par SET a=?,b=?,c=? WHERE b=? } {c1 c2 c3 par s1}
ifcapable incrblob {
do_execsql_test 2.0 {
CREATE TABLE pX(x PRIMARY KEY);
CREATE TABLE cX(a INTEGER PRIMARY KEY, b REFERENCES pX);
}
do_catchsql_test 2.1 {
INSERT INTO cX VALUES(11, zeroblob(40));
} {1 {FOREIGN KEY constraint failed}}
do_test 2.2 {
set stmt [sqlite3_prepare_v2 db "INSERT INTO cX VALUES(11, ?)" -1]
sqlite3_bind_zeroblob $stmt 1 45
sqlite3_step $stmt
sqlite3_finalize $stmt
} {SQLITE_CONSTRAINT}
}
finish_test

View File

@ -185,8 +185,7 @@ do_execsql_test without_rowid5-5.9 {
# EVIDENCE-OF: R-12643-30541 The incremental blob I/O mechanism does not
# work for WITHOUT ROWID tables.
#
# EVIDENCE-OF: R-25760-33257 The sqlite3_blob_open() interface will fail
# for a WITHOUT ROWID table.
# EVIDENCE-OF: R-40134-30296 Table zTable is a WITHOUT ROWID table
#
do_execsql_test without_rowid5-6.1 {
CREATE TABLE b1(a INTEGER PRIMARY KEY, b BLOB) WITHOUT ROWID;