diff --git a/manifest b/manifest index be38bbb4cd..9d532a3583 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index 2dc8858521..a3c94b01a3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b13e497a326697ab42b429993a1eee7df3c0c3eb \ No newline at end of file +4516416b4d38679ea7d259155f241e54c4c58d7d \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index cfdb91813d..01e5b6484e 100644 --- a/src/btree.c +++ b/src/btree.c @@ -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 diff --git a/src/btree.h b/src/btree.h index 69e72e3e71..2f123845a7 100644 --- a/src/btree.h +++ b/src/btree.h @@ -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 diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 7e6f32700e..3d7ff94440 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -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 @@ -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 */ diff --git a/src/test1.c b/src/test1.c index 43e85bb731..d07823116e 100644 --- a/src/test1.c +++ b/src/test1.c @@ -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]), ¬Used); + 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]), ¬Used); + 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; diff --git a/src/vdbeblob.c b/src/vdbeblob.c index 22e6d2da15..8624180602 100644 --- a/src/vdbeblob.c +++ b/src/vdbeblob.c @@ -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); } /* diff --git a/test/incrblob.test b/test/incrblob.test index 562c93da36..40c4e21508 100644 --- a/test/incrblob.test +++ b/test/incrblob.test @@ -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 +