/ Check-in [4516416b]
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 | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 4516416b4d38679ea7d259155f241e54c4c58d7d
User & Date: danielk1977 2007-05-04 18:36:45
Context
2007-05-04
19:03
Fix compilation and testing with OMIT_INCRBLOB defined. (CVS 3923) check-in: a0f8adc6 user: danielk1977 tags: trunk
18:36
Test interaction of incremental io and other database writes. (CVS 3922) check-in: 4516416b 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: b13e497a user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/btree.c.

     5      5   ** a legal notice, here is a blessing:
     6      6   **
     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12         -** $Id: btree.c,v 1.373 2007/05/04 13:15:56 drh Exp $
           12  +** $Id: btree.c,v 1.374 2007/05/04 18:36:45 danielk1977 Exp $
    13     13   **
    14     14   ** This file implements a external (disk-based) database using BTrees.
    15     15   ** For a detailed discussion of BTrees, refer to
    16     16   **
    17     17   **     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
    18     18   **     "Sorting And Searching", pages 473-480. Addison-Wesley
    19     19   **     Publishing Company, Reading, Massachusetts.
................................................................................
   391    391     CellInfo info;            /* A parse of the cell we are pointing at */
   392    392     u8 wrFlag;                /* True if writable */
   393    393     u8 eState;                /* One of the CURSOR_XXX constants (see below) */
   394    394     void *pKey;      /* Saved key that was cursor's last known position */
   395    395     i64 nKey;        /* Size of pKey, or last integer key */
   396    396     int skip;        /* (skip<0) -> Prev() is a no-op. (skip>0) -> Next() is */
   397    397   #ifndef SQLITE_OMIT_INCRBLOB
   398         -  u8 cacheOverflow;         /* True to use aOverflow */
          398  +  u8 isIncrblobHandle;      /* True if this cursor is an incr. io handle */
   399    399     Pgno *aOverflow;          /* Cache of overflow page locations */
   400    400   #endif
   401    401   };
   402    402   
   403    403   /*
   404    404   ** Potential values for BtCursor.eState.
   405    405   **
................................................................................
   761    761   ** If the second argument argument - doSeek - is false, then instead of 
   762    762   ** returning the cursor to it's saved position, any saved position is deleted
   763    763   ** and the cursor state set to CURSOR_INVALID.
   764    764   */
   765    765   static int restoreOrClearCursorPositionX(BtCursor *pCur){
   766    766     int rc;
   767    767     assert( pCur->eState==CURSOR_REQUIRESEEK );
          768  +  if( pCur->isIncrblobHandle ){
          769  +    return SQLITE_ABORT;
          770  +  }
   768    771     pCur->eState = CURSOR_INVALID;
   769    772     rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &pCur->skip);
   770    773     if( rc==SQLITE_OK ){
   771    774       sqliteFree(pCur->pKey);
   772    775       pCur->pKey = 0;
   773    776       assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID );
   774    777     }
................................................................................
  3181   3184   ** Data is read to or from the buffer pBuf.
  3182   3185   **
  3183   3186   ** This routine does not make a distinction between key and data.
  3184   3187   ** It just reads or writes bytes from the payload area.  Data might 
  3185   3188   ** appear on the main page or be scattered out on multiple overflow 
  3186   3189   ** pages.
  3187   3190   **
  3188         -** If the BtCursor.cacheOverflow flag is set, and the current
         3191  +** If the BtCursor.isIncrblobHandle flag is set, and the current
  3189   3192   ** cursor entry uses one or more overflow pages, this function
  3190   3193   ** allocates space for and lazily popluates the overflow page-list 
  3191   3194   ** cache array (BtCursor.aOverflow). Subsequent calls use this
  3192   3195   ** cache to make seeking to the supplied offset more efficient.
  3193   3196   **
  3194   3197   ** Once an overflow page-list cache has been allocated, it may be
  3195   3198   ** invalidated if some other cursor writes to the same table, or if
................................................................................
  3250   3253     if( rc==SQLITE_OK && amt>0 ){
  3251   3254       const int ovflSize = pBt->usableSize - 4;  /* Bytes content per ovfl page */
  3252   3255       Pgno nextPage;
  3253   3256   
  3254   3257       nextPage = get4byte(&aPayload[pCur->info.nLocal]);
  3255   3258   
  3256   3259   #ifndef SQLITE_OMIT_INCRBLOB
  3257         -    /* If the cacheOverflow flag is set and the BtCursor.aOverflow[]
         3260  +    /* If the isIncrblobHandle flag is set and the BtCursor.aOverflow[]
  3258   3261       ** has not been allocated, allocate it now. The array is sized at
  3259   3262       ** one entry for each overflow page in the overflow chain. The
  3260   3263       ** page number of the first overflow page is stored in aOverflow[0],
  3261   3264       ** etc. A value of 0 in the aOverflow[] array means "not yet known"
  3262   3265       ** (the cache is lazily populated).
  3263   3266       */
  3264         -    if( pCur->cacheOverflow && !pCur->aOverflow ){
         3267  +    if( pCur->isIncrblobHandle && !pCur->aOverflow ){
  3265   3268         int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize;
  3266   3269         pCur->aOverflow = (Pgno *)sqliteMalloc(sizeof(Pgno)*nOvfl);
  3267   3270         if( nOvfl && !pCur->aOverflow ){
  3268   3271           rc = SQLITE_NOMEM;
  3269   3272         }
  3270   3273       }
  3271   3274   
................................................................................
  6985   6988   /*
  6986   6989   ** Argument pCsr must be a cursor opened for writing on an 
  6987   6990   ** INTKEY table currently pointing at a valid table entry. 
  6988   6991   ** This function modifies the data stored as part of that entry.
  6989   6992   ** Only the data content may only be modified, it is not possible
  6990   6993   ** to change the length of the data stored.
  6991   6994   */
  6992         -int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, const void *z){
         6995  +int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
  6993   6996     BtShared *pBt = pCsr->pBtree->pBt;
         6997  +
         6998  +  assert(pCsr->isIncrblobHandle);
         6999  +  if( pCsr->eState==CURSOR_REQUIRESEEK ){
         7000  +    return SQLITE_ABORT;
         7001  +  }
  6994   7002   
  6995   7003     /* Check some preconditions: 
  6996         -  **   (a) a write-transaction is open, 
  6997         -  **   (b) the cursor is open for writing,
  6998         -  **   (c) there is no read-lock on the table being modified and
  6999         -  **   (d) the cursor points at a valid row of an intKey table.
         7004  +  **   (a) the cursor is open for writing,
         7005  +  **   (b) there is no read-lock on the table being modified and
         7006  +  **   (c) the cursor points at a valid row of an intKey table.
  7000   7007     */
  7001         -  if( pBt->inTransaction!=TRANS_WRITE ){
  7002         -    /* Must start a transaction before writing to a blob */
  7003         -    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
         7008  +  if( !pCsr->wrFlag ){
         7009  +    return SQLITE_READONLY;
  7004   7010     }
  7005         -  assert( !pBt->readOnly );
  7006         -  if( !pCsr->wrFlag ){
  7007         -    return SQLITE_PERM;   /* Cursor not open for writing */
  7008         -  }
         7011  +  assert( !pBt->readOnly && pBt->inTransaction==TRANS_WRITE );
  7009   7012     if( checkReadLocks(pCsr->pBtree, pCsr->pgnoRoot, pCsr) ){
  7010   7013       return SQLITE_LOCKED; /* The table pCur points to has a read lock */
  7011   7014     }
  7012   7015     if( pCsr->eState==CURSOR_INVALID || !pCsr->pPage->intKey ){
  7013   7016       return SQLITE_ERROR;
  7014   7017     }
  7015   7018   
................................................................................
  7023   7026   **
  7024   7027   ** This function sets a flag only. The actual page location cache
  7025   7028   ** (stored in BtCursor.aOverflow[]) is allocated and used by function
  7026   7029   ** accessPayload() (the worker function for sqlite3BtreeData() and
  7027   7030   ** sqlite3BtreePutData()).
  7028   7031   */
  7029   7032   void sqlite3BtreeCacheOverflow(BtCursor *pCur){
  7030         -  assert(!pCur->cacheOverflow);
         7033  +  assert(!pCur->isIncrblobHandle);
  7031   7034     assert(!pCur->aOverflow);
  7032         -  pCur->cacheOverflow = 1;
         7035  +  pCur->isIncrblobHandle = 1;
  7033   7036   }
  7034   7037   #endif
  7035   7038   
  7036   7039   /*
  7037   7040   ** The following debugging interface has to be in this file (rather
  7038   7041   ** than in, for example, test1.c) so that it can get access to
  7039   7042   ** the definition of BtShared.

Changes to src/btree.h.

     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** This header file defines the interface that the sqlite B-Tree file
    13     13   ** subsystem.  See comments in the source code for a detailed description
    14     14   ** of what each interface routine does.
    15     15   **
    16         -** @(#) $Id: btree.h,v 1.78 2007/05/02 16:48:37 danielk1977 Exp $
           16  +** @(#) $Id: btree.h,v 1.79 2007/05/04 18:36:45 danielk1977 Exp $
    17     17   */
    18     18   #ifndef _BTREE_H_
    19     19   #define _BTREE_H_
    20     20   
    21     21   /* TODO: This definition is just included so other modules compile. It
    22     22   ** needs to be revisited.
    23     23   */
................................................................................
   138    138   const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt);
   139    139   int sqlite3BtreeDataSize(BtCursor*, u32 *pSize);
   140    140   int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*);
   141    141   
   142    142   char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*);
   143    143   struct Pager *sqlite3BtreePager(Btree*);
   144    144   
   145         -int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, const void*);
          145  +int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
   146    146   void sqlite3BtreeCacheOverflow(BtCursor *);
   147    147   
   148    148   #ifdef SQLITE_TEST
   149    149   int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
   150    150   void sqlite3BtreeCursorList(Btree*);
   151    151   #endif
   152    152   

Changes to src/tclsqlite.c.

     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** A TCL Interface to SQLite.  Append this file to sqlite3.c and
    13     13   ** compile the whole thing to build a TCL-enabled version of SQLite.
    14     14   **
    15         -** $Id: tclsqlite.c,v 1.184 2007/05/04 13:15:56 drh Exp $
           15  +** $Id: tclsqlite.c,v 1.185 2007/05/04 18:36:45 danielk1977 Exp $
    16     16   */
    17     17   #include "tcl.h"
    18     18   #include <errno.h>
    19     19   
    20     20   /*
    21     21   ** Some additional include files are needed if this file is not
    22     22   ** appended to the amalgamation.
................................................................................
   116    116     SqlPreparedStmt *stmtLast; /* Last statement in the list */
   117    117     int maxStmt;               /* The next maximum number of stmtList */
   118    118     int nStmt;                 /* Number of statements in stmtList */
   119    119     IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */
   120    120   };
   121    121   
   122    122   struct IncrblobChannel {
   123         -  SqliteDb *pDb;            /* Associated database connection */
   124    123     sqlite3_blob *pBlob;      /* sqlite3 blob handle */
          124  +  SqliteDb *pDb;            /* Associated database connection */
   125    125     int iSeek;                /* Current seek offset */
   126         -
   127    126     Tcl_Channel channel;      /* Channel identifier */
   128    127     IncrblobChannel *pNext;   /* Linked list of all open incrblob channels */
   129    128     IncrblobChannel *pPrev;   /* Linked list of all open incrblob channels */
   130    129   };
   131    130   
   132    131   /*
   133    132   ** Close all incrblob channels opened using database connection pDb.

Changes to src/test1.c.

     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** Code for testing all sorts of SQLite interfaces.  This code
    13     13   ** is not included in the SQLite library.  It is used for automated
    14     14   ** testing of the SQLite library.
    15     15   **
    16         -** $Id: test1.c,v 1.242 2007/05/02 16:51:59 drh Exp $
           16  +** $Id: test1.c,v 1.243 2007/05/04 18:36:45 danielk1977 Exp $
    17     17   */
    18     18   #include "sqliteInt.h"
    19     19   #include "tcl.h"
    20     20   #include "os.h"
    21     21   #include <stdlib.h>
    22     22   #include <string.h>
    23     23   
................................................................................
  1461   1461     Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(autoincrement));
  1462   1462     Tcl_SetObjResult(interp, pRet);
  1463   1463   
  1464   1464     return TCL_OK;
  1465   1465   }
  1466   1466   #endif
  1467   1467   
         1468  +#ifndef SQLITE_OMIT_INCRBLOB
         1469  +
         1470  +/*
         1471  +** sqlite3_blob_read  CHANNEL OFFSET N
         1472  +*/
         1473  +static int test_blob_read(
         1474  +  ClientData clientData, /* Not used */
         1475  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
         1476  +  int objc,              /* Number of arguments */
         1477  +  Tcl_Obj *CONST objv[]  /* Command arguments */
         1478  +){
         1479  +  Tcl_Channel channel;
         1480  +  ClientData instanceData;
         1481  +  sqlite3_blob *pBlob;
         1482  +  int notUsed;
         1483  +  int nByte;
         1484  +  int iOffset;
         1485  +  unsigned char *zBuf;
         1486  +  int rc;
         1487  +  
         1488  +  if( objc!=4 ){
         1489  +    Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET N");
         1490  +    return TCL_ERROR;
         1491  +  }
         1492  +
         1493  +  channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), &notUsed);
         1494  +  if( !channel
         1495  +   || TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset)
         1496  +   || TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &nByte)
         1497  +   || nByte<0 || iOffset<0
         1498  +  ){ 
         1499  +    return TCL_ERROR;
         1500  +  }
         1501  +
         1502  +  instanceData = Tcl_GetChannelInstanceData(channel);
         1503  +  pBlob = *((sqlite3_blob **)instanceData);
         1504  +
         1505  +  zBuf = (unsigned char *)Tcl_Alloc(nByte);
         1506  +  rc = sqlite3_blob_read(pBlob, zBuf, nByte, iOffset);
         1507  +  if( rc==SQLITE_OK ){
         1508  +    Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(zBuf, nByte));
         1509  +  }else{
         1510  +    Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
         1511  +  }
         1512  +  Tcl_Free((char *)zBuf);
         1513  +
         1514  +  return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
         1515  +}
         1516  +
         1517  +/*
         1518  +** sqlite3_blob_write CHANNEL OFFSET DATA
         1519  +*/
         1520  +static int test_blob_write(
         1521  +  ClientData clientData, /* Not used */
         1522  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
         1523  +  int objc,              /* Number of arguments */
         1524  +  Tcl_Obj *CONST objv[]  /* Command arguments */
         1525  +){
         1526  +  Tcl_Channel channel;
         1527  +  ClientData instanceData;
         1528  +  sqlite3_blob *pBlob;
         1529  +  int notUsed;
         1530  +  int iOffset;
         1531  +  int rc;
         1532  +
         1533  +  unsigned char *zBuf;
         1534  +  int nBuf;
         1535  +  
         1536  +  if( objc!=4 ){
         1537  +    Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET DATA");
         1538  +    return TCL_ERROR;
         1539  +  }
         1540  +
         1541  +  channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), &notUsed);
         1542  +  if( !channel
         1543  +   || TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset)
         1544  +   || iOffset<0
         1545  +  ){ 
         1546  +    return TCL_ERROR;
         1547  +  }
         1548  +
         1549  +  instanceData = Tcl_GetChannelInstanceData(channel);
         1550  +  pBlob = *((sqlite3_blob **)instanceData);
         1551  +
         1552  +  zBuf = Tcl_GetByteArrayFromObj(objv[3], &nBuf);
         1553  +  rc = sqlite3_blob_write(pBlob, zBuf, nBuf, iOffset);
         1554  +  if( rc!=SQLITE_OK ){
         1555  +    Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
         1556  +  }
         1557  +
         1558  +  return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
         1559  +}
         1560  +#endif
  1468   1561   
  1469   1562   /*
  1470   1563   ** Usage: sqlite3_load_extension DB-HANDLE FILE ?PROC?
  1471   1564   */
  1472   1565   static int test_load_extension(
  1473   1566     ClientData clientData, /* Not used */
  1474   1567     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
................................................................................
  4497   4590   #ifndef SQLITE_OMIT_SHARED_CACHE
  4498   4591        { "sqlite3_enable_shared_cache", test_enable_shared, 0  },
  4499   4592   #endif
  4500   4593        { "sqlite3_libversion_number", test_libversion_number, 0  },
  4501   4594   #ifdef SQLITE_ENABLE_COLUMN_METADATA
  4502   4595        { "sqlite3_table_column_metadata", test_table_column_metadata, 0  },
  4503   4596   #endif
         4597  +#ifndef SQLITE_OMIT_INCRBLOB
         4598  +     { "sqlite3_blob_read",  test_blob_read, 0  },
         4599  +     { "sqlite3_blob_write", test_blob_write, 0  },
         4600  +#endif
  4504   4601     };
  4505   4602     static int bitmask_size = sizeof(Bitmask)*8;
  4506   4603     int i;
  4507   4604     extern int sqlite3_os_trace;
  4508   4605     extern int sqlite3_where_trace;
  4509   4606     extern int sqlite3_sync_count, sqlite3_fullsync_count;
  4510   4607     extern int sqlite3_opentemp_count;

Changes to src/vdbeblob.c.

     6      6   **
     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   **
    13         -** $Id: vdbeblob.c,v 1.7 2007/05/04 13:15:57 drh Exp $
           13  +** $Id: vdbeblob.c,v 1.8 2007/05/04 18:36:45 danielk1977 Exp $
    14     14   */
    15     15   
    16     16   #include "sqliteInt.h"
    17     17   #include "vdbeInt.h"
    18     18   
    19     19   #ifndef SQLITE_OMIT_INCRBLOB
    20     20   
................................................................................
   240    240   int sqlite3_blob_close(sqlite3_blob *pBlob){
   241    241     Incrblob *p = (Incrblob *)pBlob;
   242    242     sqlite3_stmt *pStmt = p->pStmt;
   243    243     sqliteFree(p);
   244    244     return sqlite3_finalize(pStmt);
   245    245   }
   246    246   
          247  +
          248  +int blobReadWrite(
          249  +  sqlite3_blob *pBlob, 
          250  +  void *z, 
          251  +  int n, 
          252  +  int iOffset, 
          253  +  int (*xCall)(BtCursor*, u32, u32, void*)
          254  +){
          255  +  int rc;
          256  +  Incrblob *p = (Incrblob *)pBlob;
          257  +  Vdbe *v = (Vdbe *)(p->pStmt);
          258  +  sqlite3 *db;  
          259  +
          260  +  /* If there is no statement handle, then the blob-handle has
          261  +  ** already been invalidated. Return SQLITE_ABORT in this case.
          262  +  */
          263  +  if( !v ) return SQLITE_ABORT;
          264  +
          265  +  /* Request is out of range. Return a transient error. */
          266  +  if( (iOffset+n)>p->nByte ){
          267  +    return SQLITE_ERROR;
          268  +  }
          269  +
          270  +  /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is
          271  +  ** returned, clean-up the statement handle.
          272  +  */
          273  +  db = v->db;
          274  +  rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);
          275  +  if( rc==SQLITE_ABORT ){
          276  +    sqlite3VdbeFinalize(v);
          277  +    p->pStmt = 0;
          278  +  }else{
          279  +    v->rc = rc;
          280  +  }
          281  +
          282  +  return sqlite3ApiExit(db, rc);
          283  +}
          284  +
   247    285   /*
   248    286   ** Read data from a blob handle.
   249    287   */
   250    288   int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){
   251         -  int rc = SQLITE_ERROR;
   252         -  Incrblob *p = (Incrblob *)pBlob;
   253         -  Vdbe *v = (Vdbe *)(p->pStmt);
   254         -  if( (iOffset+n)<=p->nByte ){
   255         -    rc = sqlite3BtreeData(p->pCsr, iOffset+p->iOffset, n, z);
   256         -  }
   257         -  v->rc = rc;
   258         -  return sqlite3ApiExit(v->db, v->rc);
          289  +  return blobReadWrite(pBlob, z, n, iOffset, sqlite3BtreeData);
   259    290   }
   260    291   
   261    292   /*
   262    293   ** Write data to a blob handle.
   263    294   */
   264    295   int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){
   265         -  int rc = SQLITE_ERROR;
   266         -  Incrblob *p = (Incrblob *)pBlob;
   267         -  Vdbe *v = (Vdbe *)(p->pStmt);
   268         -  if( (iOffset+n)<=p->nByte ){
   269         -    rc = sqlite3BtreePutData(p->pCsr, iOffset+p->iOffset, n, z);
   270         -  }
   271         -  v->rc = rc;
   272         -  return sqlite3ApiExit(v->db, v->rc);
          296  +  return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite3BtreePutData);
   273    297   }
   274    298   
   275    299   /*
   276    300   ** Query a blob handle for the size of the data.
   277    301   */
   278    302   int sqlite3_blob_bytes(sqlite3_blob *pBlob){
   279    303     Incrblob *p = (Incrblob *)pBlob;
   280    304     return p->nByte;
   281    305   }
   282    306   
   283    307   #endif /* #ifndef SQLITE_OMIT_INCRBLOB */

Changes to test/incrblob.test.

     5      5   #
     6      6   #    May you do good and not evil.
     7      7   #    May you find forgiveness for yourself and forgive others.
     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11     11   #
    12         -# $Id: incrblob.test,v 1.7 2007/05/04 14:36:22 drh Exp $
           12  +# $Id: incrblob.test,v 1.8 2007/05/04 18:36:46 danielk1977 Exp $
    13     13   #
    14     14   
    15     15   set testdir [file dirname $argv0]
    16     16   source $testdir/tester.tcl
    17     17   
    18     18   do_test incrblob-1.1 {
    19     19     execsql {
................................................................................
   198    198   }
   199    199   
   200    200   #------------------------------------------------------------------------
   201    201   # incrblob-3.*: 
   202    202   #
   203    203   # Test the outcome of trying to write to a read-only blob handle.
   204    204   #
   205         -# TODO: The following test only tests the tcl interface, not the
   206         -# underlying sqlite3 interface. Need to find some other method
   207         -# to call sqlite3_blob_write() on a readonly handle...
   208         -#
   209    205   do_test incrblob-3.1 {
   210    206     set ::blob [db incrblob -readonly blobs v 1]
   211    207     seek $::blob -40 end
   212    208     read $::blob 20
   213    209   } "1234567890abcdefghij"
   214    210   do_test incrblob-3.2 {
   215    211     seek $::blob 0
   216    212     set rc [catch {
   217    213       puts -nonewline $::blob "helloworld"
   218    214     } msg]
   219    215     close $::blob
   220    216     list $rc $msg
   221    217   } "1 {channel \"$::blob\" wasn't opened for writing}"
          218  +
          219  +do_test incrblob-3.3 {
          220  +  set ::blob [db incrblob -readonly blobs v 1]
          221  +  seek $::blob -40 end
          222  +  read $::blob 20
          223  +} "1234567890abcdefghij"
          224  +do_test incrblob-3.4 {
          225  +  set rc [catch {
          226  +    sqlite3_blob_write $::blob 20 "qwertyuioplkjhgfds" 
          227  +  } msg]
          228  +  list $rc $msg
          229  +} {1 SQLITE_READONLY}
          230  +catch {close $::blob}
   222    231   
   223    232   #------------------------------------------------------------------------
   224    233   # incrblob-4.*: 
   225    234   #
   226    235   # Try a couple of error conditions:
   227    236   #
   228    237   #     4.1 - Attempt to open a row that does not exist.
................................................................................
   383    392   do_test incrblob-6.7 {
   384    393     set ::blob [db2 incrblob blobs i 4]
   385    394     gets $::blob
   386    395   } {connection}
   387    396   do_test incrblob-6.8 {
   388    397     tell $::blob
   389    398   } {10}
   390         -breakpoint
   391    399   do_test incrblob-6.9 {
   392    400     seek $::blob 0
   393    401     puts -nonewline $::blob "invocation"
   394    402     flush $::blob
   395    403   } {}
   396    404   
   397    405   # At this point rollback or commit should be illegal (because 
................................................................................
   421    429   do_test incrblob-6.14 {
   422    430     execsql {
   423    431       SELECT * FROM blobs WHERE rowid = 4;
   424    432     }
   425    433   } {a different invocation}
   426    434   db2 close
   427    435   
          436  +#-----------------------------------------------------------------------
          437  +# The following tests verify the behaviour of the incremental IO
          438  +# APIs in the following cases:
          439  +#
          440  +#     7.1 A row that containing an open blob is modified.
          441  +#
          442  +#     7.2 A CREATE TABLE requires that an overflow page that is part
          443  +#         of an open blob is moved.
          444  +#
          445  +#     7.3 An INCREMENTAL VACUUM moves an overflow page that is part
          446  +#         of an open blob.
          447  +#
          448  +# In the first case above, correct behaviour is for all subsequent
          449  +# read/write operations on the blob-handle to return SQLITE_ABORT.
          450  +# More accurately, blob-handles are invalidated whenever the table
          451  +# they belong to is written to.
          452  +#
          453  +# The second two cases have no external effect. They are testing
          454  +# that the internal cache of overflow page numbers is correctly
          455  +# invalidated.
          456  +#
          457  +do_test incrblob-7.1.0 {
          458  +  execsql {
          459  +    BEGIN;
          460  +    DROP TABLE blobs;
          461  +    CREATE TABLE t1 (a, b, c, d BLOB);
          462  +    INSERT INTO t1(a, b, c, d) VALUES(1, 2, 3, 4);
          463  +    COMMIT;
          464  +  }
          465  +} {}
          466  +
          467  +foreach {tn arg} {1 "" 2 -readonly} {
          468  +
          469  +  execsql {
          470  +    UPDATE t1 SET d = zeroblob(10000);
          471  +  }
          472  +
          473  +  do_test incrblob-7.1.$tn.1 {
          474  +    set ::b [eval db incrblob $arg t1 d 1]
          475  +    binary scan [sqlite3_blob_read $::b 5000 5] c* c
          476  +    set c
          477  +  } {0 0 0 0 0}
          478  +  do_test incrblob-7.1.$tn.2 {
          479  +    execsql {
          480  +      UPDATE t1 SET d = 15;
          481  +    }
          482  +  } {}
          483  +  do_test incrblob-7.1.$tn.3 {
          484  +    set rc [catch { sqlite3_blob_read $::b 5000 5 } msg]
          485  +    list $rc $msg
          486  +  } {1 SQLITE_ABORT}
          487  +  do_test incrblob-7.1.$tn.4 {
          488  +    execsql {
          489  +      SELECT d FROM t1;
          490  +    }
          491  +  } {15}
          492  +  do_test incrblob-7.1.$tn.5 {
          493  +    set rc [catch { close $::b } msg]
          494  +    list $rc $msg
          495  +  } {0 {}}
          496  +  do_test incrblob-7.1.$tn.6 {
          497  +    execsql {
          498  +      SELECT d FROM t1;
          499  +    }
          500  +  } {15}
          501  +
          502  +}
          503  +
          504  +set fd [open [info script]]
          505  +set ::data [read $fd]
          506  +close $fd
          507  +
          508  +db close
          509  +file delete -force test.db test.db-journal
          510  +sqlite3 db test.db
          511  +
          512  +do_test incrblob-7.2.1 {
          513  +  execsql {
          514  +    PRAGMA auto_vacuum = "incremental";
          515  +    CREATE TABLE t1(a INTEGER PRIMARY KEY, b);        -- root@page3
          516  +    INSERT INTO t1 VALUES(123, $::data);
          517  +  }
          518  +  set ::b [db incrblob -readonly t1 b 123]
          519  +  read $::b
          520  +} $::data
          521  +do_test incrblob-7.2.2 {
          522  +  execsql {
          523  +    CREATE TABLE t2(a INTEGER PRIMARY KEY, b);        -- root@page4
          524  +  }
          525  +  seek $::b 0
          526  +  read $::b
          527  +} $::data
          528  +do_test incrblob-7.2.3 {
          529  +  close $::b
          530  +  execsql {
          531  +    SELECT rootpage FROM sqlite_master;
          532  +  }
          533  +} {3 4}
          534  +
          535  +set ::otherdata "[string range $::data 0 1000][string range $::data 1001 end]"
          536  +do_test incrblob-7.3.1 {
          537  +  execsql {
          538  +    INSERT INTO t2 VALUES(456, $::otherdata);
          539  +  }
          540  +  set ::b [db incrblob -readonly t2 b 456]
          541  +  read $::b
          542  +} $::otherdata
          543  +do_test incrblob-7.3.2 {
          544  +  expr [file size test.db]/1024
          545  +} 30
          546  +do_test incrblob-7.3.3 {
          547  +  execsql {
          548  +    DELETE FROM t1 WHERE a = 123;
          549  +    INCREMENTAL VACUUM;
          550  +  }
          551  +  seek $::b 0
          552  +  read $::b
          553  +} $::otherdata
          554  +
   428    555   finish_test
          556  +