]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Test interaction of incremental io and other database writes. (CVS 3922)
authordanielk1977 <danielk1977@noemail.net>
Fri, 4 May 2007 18:36:44 +0000 (18:36 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Fri, 4 May 2007 18:36:44 +0000 (18:36 +0000)
FossilOrigin-Name: 4516416b4d38679ea7d259155f241e54c4c58d7d

manifest
manifest.uuid
src/btree.c
src/btree.h
src/tclsqlite.c
src/test1.c
src/vdbeblob.c
test/incrblob.test

index be38bbb4cd2639fcebab55609d6dbcc6800ce432..9d532a3583f8fe4f66bc6f07be194dd182261e69 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Change\sincremental\svacuum\sto\sbe\striggered\sby\sa\spragma\srather\sthan\sa\scommand.\nWe\shave\sa\slot\sto\slearn\sabout\sthis\syet\sand\swe\sdo\snot\swant\sto\spaint\sourselves\ninto\sa\scorner\sby\scommiting\sto\sspecific\ssyntax\stoo\searly.\s(CVS\s3921)
-D 2007-05-04T18:30:41
+C Test\sinteraction\sof\sincremental\sio\sand\sother\sdatabase\swrites.\s(CVS\s3922)
+D 2007-05-04T18:36:45
 F Makefile.in 8cab54f7c9f5af8f22fd97ddf1ecfd1e1860de62
 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -59,8 +59,8 @@ F src/alter.c 2c79ec40f65e33deaf90ca493422c74586e481a3
 F src/analyze.c 4bbf5ddf9680587c6d4917e02e378b6037be3651
 F src/attach.c f088f8155541ff75542239ec40cf05f3d81390ba
 F src/auth.c 902f4722661c796b97f007d9606bd7529c02597f
-F src/btree.c ebf16f19fc9e732c93b98083c4dfdbadad247e5c
-F src/btree.h 2c187d60cf76d74c2b4767294d6b5fa267037ff0
+F src/btree.c d01ab360ec71e6207f121a117c77275d225645cd
+F src/btree.h a9cd72b05a14f6be22e057daf954ae548d2bcbe4
 F src/build.c 0dd6f0d0a5d304be91374f4c7228a3e9b00ff7f1
 F src/callback.c 6414ed32d55859d0f65067aa5b88d2da27b3af9e
 F src/complete.c 7d1a44be8f37de125fcafd3d3a018690b3799675
@@ -101,8 +101,8 @@ F src/sqlite.h.in a666300976897eced975b448f722a722b362c6b1
 F src/sqlite3ext.h 7d0d363ea7327e817ef0dfe1b7eee1f171b72890
 F src/sqliteInt.h 93ac1a9f1c8facfd861cf548845d2abc36039670
 F src/table.c a8de75bcedf84d4060d804264b067ab3b1a3561d
-F src/tclsqlite.c cd87bbaffd115ded71b4b9c03be7b5b213c4ba7a
-F src/test1.c 29a39fdde51f4612082ecf3f5af54dac93766f87
+F src/tclsqlite.c c0d9a818a379ae92ffb958a678ec71e714c5e8f6
+F src/test1.c 38858dc8a799c67073d7ed1605bb484e403731b6
 F src/test2.c 24458b17ab2f3c90cbc1c8446bd7ffe69be62f88
 F src/test3.c 946ea9d1a8c928656e3c70f0a2fcb8e733a15e86
 F src/test4.c 8b784cd82de158a2317cb4ac4bc86f91ad315e25
@@ -130,7 +130,7 @@ F src/vdbe.h 0025259af1939fb264a545816c69e4b5b8d52691
 F src/vdbeInt.h cb02cbbceddf3b40d49012e9f41576f17bcbec97
 F src/vdbeapi.c 37d793559390bec8a00c556f651f21b5f9e589af
 F src/vdbeaux.c c432e17fef6efaf102d507e979cee4e47f6ceac4
-F src/vdbeblob.c 2944bca84a40c093d442a06ad7835cb00a6b1c0a
+F src/vdbeblob.c 6a25e9bbb874a7dcee4cf8f37d8dd635c129002e
 F src/vdbefifo.c 3ca8049c561d5d67cbcb94dc909ae9bb68c0bf8f
 F src/vdbemem.c ba98f8572ec4609846b368fa7580db178022f1bb
 F src/vtab.c c5ebebf615b2f29499fbe97a584c4bb342632aa0
@@ -242,7 +242,7 @@ F test/fts2n.test a70357e72742681eaebfdbe9007b87ff3b771638
 F test/func.test 6727c7729472ae52b5acd86e802f89aa350ba50f
 F test/hook.test 7e7645fd9a033f79cce8fdff151e32715e7ec50a
 F test/in.test 369cb2aa1eab02296b4ec470732fe8c131260b1d
-F test/incrblob.test ebfee15334f33dae682d3161acff9c4d169d4672
+F test/incrblob.test 9738ec487d7a9145d78622f5dc2cb628e2b3d62f
 F test/incrblob_err.test 9f78c159279c992fa5ce49c06f50b680fc470520
 F test/incrvacuum.test f490c8ae86f2ecca622425d02e27d3119058cb21
 F test/incrvacuum2.test a1457ad2441e49e99fbc4d74f9575dd9f7ddbde3
@@ -475,7 +475,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
 F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
-P 681216767d7fabfccb0b12f6a81b18b6d1c252bf
-R 95c89ba898130ee0ba9ae5e0f60736ff
-U drh
-Z f926a6c2de5eba8d75508bc0c0193258
+P b13e497a326697ab42b429993a1eee7df3c0c3eb
+R 5900469fc0ef1860ae4674fa42817b8c
+U danielk1977
+Z 2fca248797e4ad3afe6f7db2e895bdb5
index 2dc8858521e30f09e15031ab7ec5b9b57a8e8531..a3c94b01a34977681caa2cb5b563e6806ee229f1 100644 (file)
@@ -1 +1 @@
-b13e497a326697ab42b429993a1eee7df3c0c3eb
\ No newline at end of file
+4516416b4d38679ea7d259155f241e54c4c58d7d
\ No newline at end of file
index cfdb91813d8ddac6ce025cbfabc011cfcedbd246..01e5b6484e9df2ffc458197352bb42694da90481 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.373 2007/05/04 13:15:56 drh Exp $
+** $Id: btree.c,v 1.374 2007/05/04 18:36:45 danielk1977 Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** For a detailed discussion of BTrees, refer to
@@ -395,7 +395,7 @@ struct BtCursor {
   i64 nKey;        /* Size of pKey, or last integer key */
   int skip;        /* (skip<0) -> Prev() is a no-op. (skip>0) -> Next() is */
 #ifndef SQLITE_OMIT_INCRBLOB
-  u8 cacheOverflow;         /* True to use aOverflow */
+  u8 isIncrblobHandle;      /* True if this cursor is an incr. io handle */
   Pgno *aOverflow;          /* Cache of overflow page locations */
 #endif
 };
@@ -765,6 +765,9 @@ static void clearCursorPosition(BtCursor *pCur){
 static int restoreOrClearCursorPositionX(BtCursor *pCur){
   int rc;
   assert( pCur->eState==CURSOR_REQUIRESEEK );
+  if( pCur->isIncrblobHandle ){
+    return SQLITE_ABORT;
+  }
   pCur->eState = CURSOR_INVALID;
   rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &pCur->skip);
   if( rc==SQLITE_OK ){
@@ -3185,7 +3188,7 @@ static int copyPayload(
 ** appear on the main page or be scattered out on multiple overflow 
 ** pages.
 **
-** If the BtCursor.cacheOverflow flag is set, and the current
+** If the BtCursor.isIncrblobHandle flag is set, and the current
 ** cursor entry uses one or more overflow pages, this function
 ** allocates space for and lazily popluates the overflow page-list 
 ** cache array (BtCursor.aOverflow). Subsequent calls use this
@@ -3254,14 +3257,14 @@ static int accessPayload(
     nextPage = get4byte(&aPayload[pCur->info.nLocal]);
 
 #ifndef SQLITE_OMIT_INCRBLOB
-    /* If the cacheOverflow flag is set and the BtCursor.aOverflow[]
+    /* If the isIncrblobHandle flag is set and the BtCursor.aOverflow[]
     ** has not been allocated, allocate it now. The array is sized at
     ** one entry for each overflow page in the overflow chain. The
     ** page number of the first overflow page is stored in aOverflow[0],
     ** etc. A value of 0 in the aOverflow[] array means "not yet known"
     ** (the cache is lazily populated).
     */
-    if( pCur->cacheOverflow && !pCur->aOverflow ){
+    if( pCur->isIncrblobHandle && !pCur->aOverflow ){
       int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize;
       pCur->aOverflow = (Pgno *)sqliteMalloc(sizeof(Pgno)*nOvfl);
       if( nOvfl && !pCur->aOverflow ){
@@ -6989,23 +6992,23 @@ int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
 ** Only the data content may only be modified, it is not possible
 ** to change the length of the data stored.
 */
-int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, const void *z){
+int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
   BtShared *pBt = pCsr->pBtree->pBt;
 
+  assert(pCsr->isIncrblobHandle);
+  if( pCsr->eState==CURSOR_REQUIRESEEK ){
+    return SQLITE_ABORT;
+  }
+
   /* Check some preconditions: 
-  **   (a) a write-transaction is open, 
-  **   (b) the cursor is open for writing,
-  **   (c) there is no read-lock on the table being modified and
-  **   (d) the cursor points at a valid row of an intKey table.
+  **   (a) the cursor is open for writing,
+  **   (b) there is no read-lock on the table being modified and
+  **   (c) the cursor points at a valid row of an intKey table.
   */
-  if( pBt->inTransaction!=TRANS_WRITE ){
-    /* Must start a transaction before writing to a blob */
-    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
-  }
-  assert( !pBt->readOnly );
   if( !pCsr->wrFlag ){
-    return SQLITE_PERM;   /* Cursor not open for writing */
+    return SQLITE_READONLY;
   }
+  assert( !pBt->readOnly && pBt->inTransaction==TRANS_WRITE );
   if( checkReadLocks(pCsr->pBtree, pCsr->pgnoRoot, pCsr) ){
     return SQLITE_LOCKED; /* The table pCur points to has a read lock */
   }
@@ -7027,9 +7030,9 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, const void *z){
 ** sqlite3BtreePutData()).
 */
 void sqlite3BtreeCacheOverflow(BtCursor *pCur){
-  assert(!pCur->cacheOverflow);
+  assert(!pCur->isIncrblobHandle);
   assert(!pCur->aOverflow);
-  pCur->cacheOverflow = 1;
+  pCur->isIncrblobHandle = 1;
 }
 #endif
 
index 69e72e3e71d0832b58d01a4628502b4c2317e84e..2f123845a767b6f847ed8a384a05df8a9d386880 100644 (file)
@@ -13,7 +13,7 @@
 ** subsystem.  See comments in the source code for a detailed description
 ** of what each interface routine does.
 **
-** @(#) $Id: btree.h,v 1.78 2007/05/02 16:48:37 danielk1977 Exp $
+** @(#) $Id: btree.h,v 1.79 2007/05/04 18:36:45 danielk1977 Exp $
 */
 #ifndef _BTREE_H_
 #define _BTREE_H_
@@ -142,7 +142,7 @@ int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*);
 char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*);
 struct Pager *sqlite3BtreePager(Btree*);
 
-int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, const void*);
+int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
 void sqlite3BtreeCacheOverflow(BtCursor *);
 
 #ifdef SQLITE_TEST
index 7e6f32700eabef18edc9f02f216d0021e843d833..3d7ff9444075119cab30f8e2692c07efdce70b86 100644 (file)
@@ -12,7 +12,7 @@
 ** A TCL Interface to SQLite.  Append this file to sqlite3.c and
 ** compile the whole thing to build a TCL-enabled version of SQLite.
 **
-** $Id: tclsqlite.c,v 1.184 2007/05/04 13:15:56 drh Exp $
+** $Id: tclsqlite.c,v 1.185 2007/05/04 18:36:45 danielk1977 Exp $
 */
 #include "tcl.h"
 #include <errno.h>
@@ -120,10 +120,9 @@ struct SqliteDb {
 };
 
 struct IncrblobChannel {
-  SqliteDb *pDb;            /* Associated database connection */
   sqlite3_blob *pBlob;      /* sqlite3 blob handle */
+  SqliteDb *pDb;            /* Associated database connection */
   int iSeek;                /* Current seek offset */
-
   Tcl_Channel channel;      /* Channel identifier */
   IncrblobChannel *pNext;   /* Linked list of all open incrblob channels */
   IncrblobChannel *pPrev;   /* Linked list of all open incrblob channels */
index 43e85bb73193de10d38b3eacfd482c4c0fe98640..d07823116e34bab9b57ac847e6735f6cc1967af8 100644 (file)
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test1.c,v 1.242 2007/05/02 16:51:59 drh Exp $
+** $Id: test1.c,v 1.243 2007/05/04 18:36:45 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -1465,6 +1465,99 @@ static int test_table_column_metadata(
 }
 #endif
 
+#ifndef SQLITE_OMIT_INCRBLOB
+
+/*
+** sqlite3_blob_read  CHANNEL OFFSET N
+*/
+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 */
+){
+  Tcl_Channel channel;
+  ClientData instanceData;
+  sqlite3_blob *pBlob;
+  int notUsed;
+  int nByte;
+  int iOffset;
+  unsigned char *zBuf;
+  int rc;
+  
+  if( objc!=4 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET N");
+    return TCL_ERROR;
+  }
+
+  channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), &notUsed);
+  if( !channel
+   || TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset)
+   || TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &nByte)
+   || nByte<0 || iOffset<0
+  ){ 
+    return TCL_ERROR;
+  }
+
+  instanceData = Tcl_GetChannelInstanceData(channel);
+  pBlob = *((sqlite3_blob **)instanceData);
+
+  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 *)sqlite3TestErrorName(rc), TCL_VOLATILE);
+  }
+  Tcl_Free((char *)zBuf);
+
+  return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
+}
+
+/*
+** sqlite3_blob_write CHANNEL OFFSET DATA
+*/
+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 */
+){
+  Tcl_Channel channel;
+  ClientData instanceData;
+  sqlite3_blob *pBlob;
+  int notUsed;
+  int iOffset;
+  int rc;
+
+  unsigned char *zBuf;
+  int nBuf;
+  
+  if( objc!=4 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET DATA");
+    return TCL_ERROR;
+  }
+
+  channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), &notUsed);
+  if( !channel
+   || TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset)
+   || iOffset<0
+  ){ 
+    return TCL_ERROR;
+  }
+
+  instanceData = Tcl_GetChannelInstanceData(channel);
+  pBlob = *((sqlite3_blob **)instanceData);
+
+  zBuf = Tcl_GetByteArrayFromObj(objv[3], &nBuf);
+  rc = sqlite3_blob_write(pBlob, zBuf, nBuf, iOffset);
+  if( rc!=SQLITE_OK ){
+    Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
+  }
+
+  return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
+}
+#endif
 
 /*
 ** Usage: sqlite3_load_extension DB-HANDLE FILE ?PROC?
@@ -4500,6 +4593,10 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
      { "sqlite3_libversion_number", test_libversion_number, 0  },
 #ifdef SQLITE_ENABLE_COLUMN_METADATA
      { "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  },
 #endif
   };
   static int bitmask_size = sizeof(Bitmask)*8;
index 22e6d2da15235f6bea7b3365ea6e82fc1a6da97a..8624180602b4cab5420e0f87eca87cb9d22e2489 100644 (file)
@@ -10,7 +10,7 @@
 **
 *************************************************************************
 **
-** $Id: vdbeblob.c,v 1.7 2007/05/04 13:15:57 drh Exp $
+** $Id: vdbeblob.c,v 1.8 2007/05/04 18:36:45 danielk1977 Exp $
 */
 
 #include "sqliteInt.h"
@@ -244,32 +244,56 @@ int sqlite3_blob_close(sqlite3_blob *pBlob){
   return sqlite3_finalize(pStmt);
 }
 
+
+int blobReadWrite(
+  sqlite3_blob *pBlob, 
+  void *z, 
+  int n, 
+  int iOffset, 
+  int (*xCall)(BtCursor*, u32, u32, void*)
+){
+  int rc;
+  Incrblob *p = (Incrblob *)pBlob;
+  Vdbe *v = (Vdbe *)(p->pStmt);
+  sqlite3 *db;  
+
+  /* If there is no statement handle, then the blob-handle has
+  ** already been invalidated. Return SQLITE_ABORT in this case.
+  */
+  if( !v ) return SQLITE_ABORT;
+
+  /* Request is out of range. Return a transient error. */
+  if( (iOffset+n)>p->nByte ){
+    return SQLITE_ERROR;
+  }
+
+  /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is
+  ** returned, clean-up the statement handle.
+  */
+  db = v->db;
+  rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);
+  if( rc==SQLITE_ABORT ){
+    sqlite3VdbeFinalize(v);
+    p->pStmt = 0;
+  }else{
+    v->rc = rc;
+  }
+
+  return sqlite3ApiExit(db, rc);
+}
+
 /*
 ** Read data from a blob handle.
 */
 int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){
-  int rc = SQLITE_ERROR;
-  Incrblob *p = (Incrblob *)pBlob;
-  Vdbe *v = (Vdbe *)(p->pStmt);
-  if( (iOffset+n)<=p->nByte ){
-    rc = sqlite3BtreeData(p->pCsr, iOffset+p->iOffset, n, z);
-  }
-  v->rc = rc;
-  return sqlite3ApiExit(v->db, v->rc);
+  return blobReadWrite(pBlob, z, n, iOffset, sqlite3BtreeData);
 }
 
 /*
 ** Write data to a blob handle.
 */
 int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){
-  int rc = SQLITE_ERROR;
-  Incrblob *p = (Incrblob *)pBlob;
-  Vdbe *v = (Vdbe *)(p->pStmt);
-  if( (iOffset+n)<=p->nByte ){
-    rc = sqlite3BtreePutData(p->pCsr, iOffset+p->iOffset, n, z);
-  }
-  v->rc = rc;
-  return sqlite3ApiExit(v->db, v->rc);
+  return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite3BtreePutData);
 }
 
 /*
index 562c93da3627e74c3f639c177440f3b621e1bc28..40c4e215088c0b34ecee8053d6c3db760b3c266b 100644 (file)
@@ -9,7 +9,7 @@
 #
 #***********************************************************************
 #
-# $Id: incrblob.test,v 1.7 2007/05/04 14:36:22 drh Exp $
+# $Id: incrblob.test,v 1.8 2007/05/04 18:36:46 danielk1977 Exp $
 #
 
 set testdir [file dirname $argv0]
@@ -202,10 +202,6 @@ foreach AutoVacuumMode [list 0 1] {
 #
 # Test the outcome of trying to write to a read-only blob handle.
 #
-# TODO: The following test only tests the tcl interface, not the
-# underlying sqlite3 interface. Need to find some other method
-# to call sqlite3_blob_write() on a readonly handle...
-#
 do_test incrblob-3.1 {
   set ::blob [db incrblob -readonly blobs v 1]
   seek $::blob -40 end
@@ -220,6 +216,19 @@ do_test incrblob-3.2 {
   list $rc $msg
 } "1 {channel \"$::blob\" wasn't opened for writing}"
 
+do_test incrblob-3.3 {
+  set ::blob [db incrblob -readonly blobs v 1]
+  seek $::blob -40 end
+  read $::blob 20
+} "1234567890abcdefghij"
+do_test incrblob-3.4 {
+  set rc [catch {
+    sqlite3_blob_write $::blob 20 "qwertyuioplkjhgfds" 
+  } msg]
+  list $rc $msg
+} {1 SQLITE_READONLY}
+catch {close $::blob}
+
 #------------------------------------------------------------------------
 # incrblob-4.*: 
 #
@@ -387,7 +396,6 @@ do_test incrblob-6.7 {
 do_test incrblob-6.8 {
   tell $::blob
 } {10}
-breakpoint
 do_test incrblob-6.9 {
   seek $::blob 0
   puts -nonewline $::blob "invocation"
@@ -425,4 +433,124 @@ do_test incrblob-6.14 {
 } {a different invocation}
 db2 close
 
+#-----------------------------------------------------------------------
+# The following tests verify the behaviour of the incremental IO
+# APIs in the following cases:
+#
+#     7.1 A row that containing an open blob is modified.
+#
+#     7.2 A CREATE TABLE requires that an overflow page that is part
+#         of an open blob is moved.
+#
+#     7.3 An INCREMENTAL VACUUM moves an overflow page that is part
+#         of an open blob.
+#
+# In the first case above, correct behaviour is for all subsequent
+# read/write operations on the blob-handle to return SQLITE_ABORT.
+# More accurately, blob-handles are invalidated whenever the table
+# they belong to is written to.
+#
+# The second two cases have no external effect. They are testing
+# that the internal cache of overflow page numbers is correctly
+# invalidated.
+#
+do_test incrblob-7.1.0 {
+  execsql {
+    BEGIN;
+    DROP TABLE blobs;
+    CREATE TABLE t1 (a, b, c, d BLOB);
+    INSERT INTO t1(a, b, c, d) VALUES(1, 2, 3, 4);
+    COMMIT;
+  }
+} {}
+
+foreach {tn arg} {1 "" 2 -readonly} {
+
+  execsql {
+    UPDATE t1 SET d = zeroblob(10000);
+  }
+
+  do_test incrblob-7.1.$tn.1 {
+    set ::b [eval db incrblob $arg t1 d 1]
+    binary scan [sqlite3_blob_read $::b 5000 5] c* c
+    set c
+  } {0 0 0 0 0}
+  do_test incrblob-7.1.$tn.2 {
+    execsql {
+      UPDATE t1 SET d = 15;
+    }
+  } {}
+  do_test incrblob-7.1.$tn.3 {
+    set rc [catch { sqlite3_blob_read $::b 5000 5 } msg]
+    list $rc $msg
+  } {1 SQLITE_ABORT}
+  do_test incrblob-7.1.$tn.4 {
+    execsql {
+      SELECT d FROM t1;
+    }
+  } {15}
+  do_test incrblob-7.1.$tn.5 {
+    set rc [catch { close $::b } msg]
+    list $rc $msg
+  } {0 {}}
+  do_test incrblob-7.1.$tn.6 {
+    execsql {
+      SELECT d FROM t1;
+    }
+  } {15}
+
+}
+
+set fd [open [info script]]
+set ::data [read $fd]
+close $fd
+
+db close
+file delete -force test.db test.db-journal
+sqlite3 db test.db
+
+do_test incrblob-7.2.1 {
+  execsql {
+    PRAGMA auto_vacuum = "incremental";
+    CREATE TABLE t1(a INTEGER PRIMARY KEY, b);        -- root@page3
+    INSERT INTO t1 VALUES(123, $::data);
+  }
+  set ::b [db incrblob -readonly t1 b 123]
+  read $::b
+} $::data
+do_test incrblob-7.2.2 {
+  execsql {
+    CREATE TABLE t2(a INTEGER PRIMARY KEY, b);        -- root@page4
+  }
+  seek $::b 0
+  read $::b
+} $::data
+do_test incrblob-7.2.3 {
+  close $::b
+  execsql {
+    SELECT rootpage FROM sqlite_master;
+  }
+} {3 4}
+
+set ::otherdata "[string range $::data 0 1000][string range $::data 1001 end]"
+do_test incrblob-7.3.1 {
+  execsql {
+    INSERT INTO t2 VALUES(456, $::otherdata);
+  }
+  set ::b [db incrblob -readonly t2 b 456]
+  read $::b
+} $::otherdata
+do_test incrblob-7.3.2 {
+  expr [file size test.db]/1024
+} 30
+do_test incrblob-7.3.3 {
+  execsql {
+    DELETE FROM t1 WHERE a = 123;
+    INCREMENTAL VACUUM;
+  }
+  seek $::b 0
+  read $::b
+} $::otherdata
+
 finish_test
+