SQLite

Check-in [4516416b4d]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Test interaction of incremental io and other database writes. (CVS 3922)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 4516416b4d38679ea7d259155f241e54c4c58d7d
User & Date: danielk1977 2007-05-04 18:36:45.000
Context
2007-05-04
19:03
Fix compilation and testing with OMIT_INCRBLOB defined. (CVS 3923) (check-in: a0f8adc692 user: danielk1977 tags: trunk)
18:36
Test interaction of incremental io and other database writes. (CVS 3922) (check-in: 4516416b4d user: danielk1977 tags: trunk)
18:30
Change incremental vacuum to be triggered by a pragma rather than a command. We have a lot to learn about this yet and we do not want to paint ourselves into a corner by commiting to specific syntax too early. (CVS 3921) (check-in: b13e497a32 user: drh tags: trunk)
Changes
Side-by-Side Diff Ignore Whitespace Patch
Changes to src/btree.c.
1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
19











-
+







/*
** 2004 April 6
**
** 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.
**
*************************************************************************
** $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
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.
391
392
393
394
395
396
397
398

399
400
401
402
403
404
405
391
392
393
394
395
396
397

398
399
400
401
402
403
404
405







-
+







  CellInfo info;            /* A parse of the cell we are pointing at */
  u8 wrFlag;                /* True if writable */
  u8 eState;                /* One of the CURSOR_XXX constants (see below) */
  void *pKey;      /* Saved key that was cursor's last known position */
  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
};

/*
** Potential values for BtCursor.eState.
**
761
762
763
764
765
766
767



768
769
770
771
772
773
774
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777







+
+
+







** If the second argument argument - doSeek - is false, then instead of 
** returning the cursor to it's saved position, any saved position is deleted
** and the cursor state set to CURSOR_INVALID.
*/
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 ){
    sqliteFree(pCur->pKey);
    pCur->pKey = 0;
    assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID );
  }
3181
3182
3183
3184
3185
3186
3187
3188

3189
3190
3191
3192
3193
3194
3195
3184
3185
3186
3187
3188
3189
3190

3191
3192
3193
3194
3195
3196
3197
3198







-
+







** Data is read to or from the buffer pBuf.
**
** This routine does not make a distinction between key and data.
** It just reads or writes bytes from the payload area.  Data might 
** 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
** cache to make seeking to the supplied offset more efficient.
**
** Once an overflow page-list cache has been allocated, it may be
** invalidated if some other cursor writes to the same table, or if
3250
3251
3252
3253
3254
3255
3256
3257

3258
3259
3260
3261
3262
3263
3264

3265
3266
3267
3268
3269
3270
3271
3253
3254
3255
3256
3257
3258
3259

3260
3261
3262
3263
3264
3265
3266

3267
3268
3269
3270
3271
3272
3273
3274







-
+






-
+







  if( rc==SQLITE_OK && amt>0 ){
    const int ovflSize = pBt->usableSize - 4;  /* Bytes content per ovfl page */
    Pgno nextPage;

    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 ){
        rc = SQLITE_NOMEM;
      }
    }

6985
6986
6987
6988
6989
6990
6991
6992

6993
6994





6995
6996
6997
6998
6999



7000
7001
7002
7003
7004
7005
7006
7007

7008

7009
7010
7011
7012
7013
7014
7015
6988
6989
6990
6991
6992
6993
6994

6995
6996
6997
6998
6999
7000
7001
7002
7003




7004
7005
7006
7007





7008

7009
7010
7011
7012
7013
7014
7015
7016
7017
7018







-
+


+
+
+
+
+

-
-
-
-
+
+
+

-
-
-
-
-

-
+

+







/*
** Argument pCsr must be a cursor opened for writing on an 
** INTKEY table currently pointing at a valid table entry. 
** This function modifies the data stored as part of that entry.
** 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 */
  }
  if( pCsr->eState==CURSOR_INVALID || !pCsr->pPage->intKey ){
    return SQLITE_ERROR;
  }

7023
7024
7025
7026
7027
7028
7029
7030

7031
7032

7033
7034
7035
7036
7037
7038
7039
7026
7027
7028
7029
7030
7031
7032

7033
7034

7035
7036
7037
7038
7039
7040
7041
7042







-
+

-
+







**
** This function sets a flag only. The actual page location cache
** (stored in BtCursor.aOverflow[]) is allocated and used by function
** accessPayload() (the worker function for sqlite3BtreeData() and
** sqlite3BtreePutData()).
*/
void sqlite3BtreeCacheOverflow(BtCursor *pCur){
  assert(!pCur->cacheOverflow);
  assert(!pCur->isIncrblobHandle);
  assert(!pCur->aOverflow);
  pCur->cacheOverflow = 1;
  pCur->isIncrblobHandle = 1;
}
#endif

/*
** The following debugging interface has to be in this file (rather
** than in, for example, test1.c) so that it can get access to
** the definition of BtShared.
Changes to src/btree.h.
9
10
11
12
13
14
15
16

17
18
19
20
21
22
23
9
10
11
12
13
14
15

16
17
18
19
20
21
22
23







-
+







**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the sqlite B-Tree file
** 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_

/* TODO: This definition is just included so other modules compile. It
** needs to be revisited.
*/
138
139
140
141
142
143
144
145

146
147
148
149
150
151
152
138
139
140
141
142
143
144

145
146
147
148
149
150
151
152







-
+







const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt);
int sqlite3BtreeDataSize(BtCursor*, u32 *pSize);
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
int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
void sqlite3BtreeCursorList(Btree*);
#endif

Changes to src/tclsqlite.c.
8
9
10
11
12
13
14
15

16
17
18
19
20
21
22
8
9
10
11
12
13
14

15
16
17
18
19
20
21
22







-
+







**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** 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>

/*
** Some additional include files are needed if this file is not
** appended to the amalgamation.
116
117
118
119
120
121
122
123
124

125
126
127
128
129
130
131
132
133
116
117
118
119
120
121
122

123
124
125

126
127
128
129
130
131
132







-

+

-







  SqlPreparedStmt *stmtLast; /* Last statement in the list */
  int maxStmt;               /* The next maximum number of stmtList */
  int nStmt;                 /* Number of statements in stmtList */
  IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */
};

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 */
};

/*
** Close all incrblob channels opened using database connection pDb.
Changes to src/test1.c.
9
10
11
12
13
14
15
16

17
18
19
20
21
22
23
9
10
11
12
13
14
15

16
17
18
19
20
21
22
23







-
+







**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing all sorts of SQLite interfaces.  This code
** 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"
#include "os.h"
#include <stdlib.h>
#include <string.h>

1461
1462
1463
1464
1465
1466
1467





























































































1468
1469
1470
1471
1472
1473
1474
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(autoincrement));
  Tcl_SetObjResult(interp, pRet);

  return TCL_OK;
}
#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?
*/
static int test_load_extension(
  ClientData clientData, /* Not used */
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
4497
4498
4499
4500
4501
4502
4503




4504
4505
4506
4507
4508
4509
4510
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607







+
+
+
+







#ifndef SQLITE_OMIT_SHARED_CACHE
     { "sqlite3_enable_shared_cache", test_enable_shared, 0  },
#endif
     { "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;
  int i;
  extern int sqlite3_os_trace;
  extern int sqlite3_where_trace;
  extern int sqlite3_sync_count, sqlite3_fullsync_count;
  extern int sqlite3_opentemp_count;
Changes to src/vdbeblob.c.
1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
20












-
+







/*
** 2007 May 1
**
** 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.
**
*************************************************************************
**
** $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"
#include "vdbeInt.h"

#ifndef SQLITE_OMIT_INCRBLOB

240
241
242
243
244
245
246
247
248
249
250
251









252
253








254

255

256










257
258











259
260
261
262
263
264
265
266
267
268
269

270
271
272
273
274
275
276
277
278
279
280
281
282
283
240
241
242
243
244
245
246





247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265

266

267
268
269
270
271
272
273
274
275
276
277
278


279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295





296



297
298
299
300
301
302
303
304
305
306
307







-
-
-
-
-
+
+
+
+
+
+
+
+
+


+
+
+
+
+
+
+
+
-
+
-
+

+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+






-
-
-
-
-
+
-
-
-











int sqlite3_blob_close(sqlite3_blob *pBlob){
  Incrblob *p = (Incrblob *)pBlob;
  sqlite3_stmt *pStmt = p->pStmt;
  sqliteFree(p);
  return sqlite3_finalize(pStmt);
}

/*
** Read data from a blob handle.
*/
int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){
  int rc = SQLITE_ERROR;

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 ){
  if( (iOffset+n)>p->nByte ){
    rc = sqlite3BtreeData(p->pCsr, iOffset+p->iOffset, n, z);
    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(v->db, v->rc);
    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){
  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);
  return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite3BtreePutData);
  }
  v->rc = rc;
  return sqlite3ApiExit(v->db, v->rc);
}

/*
** Query a blob handle for the size of the data.
*/
int sqlite3_blob_bytes(sqlite3_blob *pBlob){
  Incrblob *p = (Incrblob *)pBlob;
  return p->nByte;
}

#endif /* #ifndef SQLITE_OMIT_INCRBLOB */
Changes to test/incrblob.test.
1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
19











-
+







# 2007 May 1
#
# 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.
#
#***********************************************************************
#
# $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]
source $testdir/tester.tcl

do_test incrblob-1.1 {
  execsql {
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221













222
223
224
225
226
227
228
198
199
200
201
202
203
204




205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237







-
-
-
-













+
+
+
+
+
+
+
+
+
+
+
+
+







}

#------------------------------------------------------------------------
# incrblob-3.*: 
#
# 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
  read $::blob 20
} "1234567890abcdefghij"
do_test incrblob-3.2 {
  seek $::blob 0
  set rc [catch {
    puts -nonewline $::blob "helloworld"
  } msg]
  close $::blob
  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.*: 
#
# Try a couple of error conditions:
#
#     4.1 - Attempt to open a row that does not exist.
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
392
393
394
395
396
397
398

399
400
401
402
403
404
405







-







do_test incrblob-6.7 {
  set ::blob [db2 incrblob blobs i 4]
  gets $::blob
} {connection}
do_test incrblob-6.8 {
  tell $::blob
} {10}
breakpoint
do_test incrblob-6.9 {
  seek $::blob 0
  puts -nonewline $::blob "invocation"
  flush $::blob
} {}

# At this point rollback or commit should be illegal (because 
421
422
423
424
425
426
427























































































































428

429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
do_test incrblob-6.14 {
  execsql {
    SELECT * FROM blobs WHERE rowid = 4;
  }
} {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