$(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 \
-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
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
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
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
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
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
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
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
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
-b45bc80bb16f07192d84fd14433bb724a84d4146
\ No newline at end of file
+ecbccd0e594d22b3ae7fabc8037951dc49570bc3
\ No newline at end of file
** 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
** 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()].
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*);
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);
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 */
{ "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
--- /dev/null
+/*
+** 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, ¬Used);
+ 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;
+}
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
}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;
--- /dev/null
+# 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
+
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
# 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;