]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add new test file e_blobopen.test, containing tests for sqlite3_blob_open().
authordan <dan@noemail.net>
Fri, 7 Nov 2014 14:41:11 +0000 (14:41 +0000)
committerdan <dan@noemail.net>
Fri, 7 Nov 2014 14:41:11 +0000 (14:41 +0000)
FossilOrigin-Name: ecbccd0e594d22b3ae7fabc8037951dc49570bc3

12 files changed:
main.mk
manifest
manifest.uuid
src/sqlite.h.in
src/tclsqlite.c
src/test1.c
src/test_blob.c [new file with mode: 0644]
src/test_config.c
src/vdbe.c
test/e_blobopen.test [new file with mode: 0644]
test/fkey7.test
test/without_rowid5.test

diff --git a/main.mk b/main.mk
index 4a7ac02710bf841c6ca4a4976e13cdfaf42f935f..c8bf72b48bcdc5493a52fa3526f73838d31dd0dc 100644 (file)
--- a/main.mk
+++ b/main.mk
@@ -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 \
index 579589c94b6e9f03dbce100832c87b6ebbf2790a..e08707974e4c4e284f81308ef2325889c85ef177 100644 (file)
--- a/manifest
+++ b/manifest
@@ -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
index 13afcc03f73a203a5606745c214ed0ba44e5870a..6227627e369706e175040da4ab9d281a6f35b5cd 100644 (file)
@@ -1 +1 @@
-b45bc80bb16f07192d84fd14433bb724a84d4146
\ No newline at end of file
+ecbccd0e594d22b3ae7fabc8037951dc49570bc3
\ No newline at end of file
index fc741b72917e390cce7077c6ef15362fa16c20fc..bed64bfae22a5d45bb17dd3717df10650cc30316 100644 (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.
-**
-** ^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()].
index bff4a92421460269a968301fcdb68f03f93fa733..4fb78a5d12c2564d43a41f2ac8edf63b2d09ab8f 100644 (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);
index fcc1a23de1f68635380b5f0ee96ca0e465315390..e7a5929323fa151235c5a31199a3048b9f107708 100644 (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
diff --git a/src/test_blob.c b/src/test_blob.c
new file mode 100644 (file)
index 0000000..d88c913
--- /dev/null
@@ -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;
+}
index 03f4f76f6dde50834f319bbc986eb67e0ce65919..834113b33b14beb4dcc4f15ae6c8d3f9acc2abce 100644 (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
index bf9b233a26dfc759fa263a64e0d7a41610bdcdad..f80e7787af9ac2cd40334644d817499767812395 100644 (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;
diff --git a/test/e_blobopen.test b/test/e_blobopen.test
new file mode 100644 (file)
index 0000000..01f62cd
--- /dev/null
@@ -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
+
index c2682edbe5fbf3a286693b380046f6592f77e472..6c646a9a7f86931b08ab809670c35d4aa4fe2d64 100644 (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
index 45e047befe6d9d8dc68f21ac70f0b954fa31366d..d163d9c1bc83d597819585c9160d69d0580b97f8 100644 (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;