/ Check-in [641e5528]
Login

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

Overview
Comment:Test cases and corrections to IO and malloc() error handling in incremental blob IO functions. (CVS 3915)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:641e55284e1ba6070073c83ac6ed78ffb29f7e60
User & Date: danielk1977 2007-05-04 12:05:56
Context
2007-05-04
13:15
Eliminate all uses of sprintf() and strcpy(). These were not being misused. But getting rid of them removes a library dependency. And it avoids warnings from the OpenBSD compiler. Ticket #2336. (CVS 3916) check-in: ba4845b3 user: drh tags: trunk
12:05
Test cases and corrections to IO and malloc() error handling in incremental blob IO functions. (CVS 3915) check-in: 641e5528 user: danielk1977 tags: trunk
12:01
Fix a formatting errror in I/O logging. (CVS 3914) check-in: 2d37687a user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/btree.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
652
653
654
655
656
657
658
























659
660
661
662
663
664
665
...
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
....
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
....
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
....
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
....
5797
5798
5799
5800
5801
5802
5803
5804
5805
5806
5807
5808
5809
5810
5811
5812
5813
5814
5815
5816
5817
5818
5819
5820
5821
5822
** 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.371 2007/05/04 08:32:14 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.
................................................................................
    }
  }
}
#endif /* SQLITE_OMIT_SHARED_CACHE */

static void releasePage(MemPage *pPage);  /* Forward reference */

























/*
** Save the current cursor position in the variables BtCursor.nKey 
** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
*/
static int saveCursorPosition(BtCursor *pCur){
  int rc;

................................................................................

  if( rc==SQLITE_OK ){
    releasePage(pCur->pPage);
    pCur->pPage = 0;
    pCur->eState = CURSOR_REQUIRESEEK;
  }

#ifndef SQLITE_OMIT_INCRBLOB
  /* Delete the cache of overflow page numbers. */
  sqliteFree(pCur->aOverflow);
  pCur->aOverflow = 0;
#endif

  return rc;
}

/*
** Save the positions of all cursors except pExcept open on the table 
** with root-page iRoot. Usually, this is called just before cursor
** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()).
................................................................................
**
** If the incremental vacuum is finished after this function has run,
** SQLITE_DONE is returned. If it is not finished, but no error occured,
** SQLITE_OK is returned. Otherwise an SQLite error code. 
*/
int sqlite3BtreeIncrVacuum(Btree *p){
  BtShared *pBt = p->pBt;
  BtCursor *pCur;

  assert( pBt->inTransaction==TRANS_WRITE && p->inTrans==TRANS_WRITE );
  if( !pBt->autoVacuum ){
    return SQLITE_DONE;
  }
#ifndef SQLITE_OMIT_INCRBLOB
  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
    /* Delete the cache of overflow page numbers. */
    sqliteFree(pCur->aOverflow);
    pCur->aOverflow = 0;
  }
#endif
  return incrVacuumStep(pBt, 0);
}

/*
** This routine is called prior to sqlite3PagerCommit when a transaction
** is commited for an auto-vacuum database.
**
................................................................................
static int autoVacuumCommit(BtShared *pBt, Pgno *pnTrunc){
  int rc = SQLITE_OK;
  Pager *pPager = pBt->pPager;
#ifndef NDEBUG
  int nRef = sqlite3PagerRefcount(pPager);
#endif

#ifndef SQLITE_OMIT_INCRBLOB
  BtCursor *pCur;
  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
    /* Delete the cache of overflow page numbers. */
    sqliteFree(pCur->aOverflow);
    pCur->aOverflow = 0;
  }
#endif

  assert(pBt->autoVacuum);
  if( !pBt->incrVacuum ){
    Pgno nFin = 0;

    if( pBt->nTrunc==0 ){
      Pgno nFree;
      Pgno nPtrmap;
................................................................................
    pBt->pCursor = pCur->pNext;
  }
  if( pCur->pNext ){
    pCur->pNext->pPrev = pCur->pPrev;
  }
  releasePage(pCur->pPage);
  unlockBtreeIfUnused(pBt);
#ifndef SQLITE_OMIT_INCRBLOB
  sqliteFree(pCur->aOverflow);
#endif
  sqliteFree(pCur);
  return SQLITE_OK;
}

/*
** Make a temporary cursor by filling in the fields of pTempCur.
** The temporary cursor is not on the cursor list for the Btree.
................................................................................
  rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0);
  if( rc ) return rc;
#else
  if( pBt->autoVacuum ){
    Pgno pgnoMove;      /* Move a page here to make room for the root-page */
    MemPage *pPageMove; /* The page to move to. */

#ifndef SQLITE_OMIT_INCRBLOB
    /* Creating a new table may probably require moving an existing database
    ** to make room for the new tables root page. In case this page turns
    ** out to be an overflow page, delete all overflow page-map caches
    ** held by open cursors.
    */
    BtCursor *pCur;
    for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
      sqliteFree(pCur->aOverflow);
      pCur->aOverflow = 0;
    }
#endif

    /* Read the value of meta[3] from the database to determine where the
    ** root page of the new table should go. meta[3] is the largest root-page
    ** created so far, so the new root-page is (meta[3]+1).
    */
    rc = sqlite3BtreeGetMeta(p, 4, &pgnoRoot);
    if( rc!=SQLITE_OK ) return rc;







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
<
<
<
<
<







 







<
<




|
<
<
<
<
<
<







 







|
<
<
<
<
<
<
<
<







 







|
<
<







 







<





|
<
<
<
<
<







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
...
715
716
717
718
719
720
721
722





723
724
725
726
727
728
729
....
2438
2439
2440
2441
2442
2443
2444


2445
2446
2447
2448
2449






2450
2451
2452
2453
2454
2455
2456
....
2462
2463
2464
2465
2466
2467
2468
2469








2470
2471
2472
2473
2474
2475
2476
....
2958
2959
2960
2961
2962
2963
2964
2965


2966
2967
2968
2969
2970
2971
2972
....
5798
5799
5800
5801
5802
5803
5804

5805
5806
5807
5808
5809
5810





5811
5812
5813
5814
5815
5816
5817
** 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.372 2007/05/04 12:05:56 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.
................................................................................
    }
  }
}
#endif /* SQLITE_OMIT_SHARED_CACHE */

static void releasePage(MemPage *pPage);  /* Forward reference */

#ifndef SQLITE_OMIT_INCRBLOB
/*
** Invalidate the overflow page-list cache for cursor pCur, if any.
*/
static void invalidateOverflowCache(BtCursor *pCur){
  sqliteFree(pCur->aOverflow);
  pCur->aOverflow = 0;
}

/*
** Invalidate the overflow page-list cache for all cursors opened
** on the shared btree structure pBt.
*/
static void invalidateAllOverflowCache(BtShared *pBt){
  BtCursor *p;
  for(p=pBt->pCursor; p; p=p->pNext){
    invalidateOverflowCache(p);
  }
}
#else
  #define invalidateOverflowCache(x)
  #define invalidateAllOverflowCache(x)
#endif

/*
** Save the current cursor position in the variables BtCursor.nKey 
** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
*/
static int saveCursorPosition(BtCursor *pCur){
  int rc;

................................................................................

  if( rc==SQLITE_OK ){
    releasePage(pCur->pPage);
    pCur->pPage = 0;
    pCur->eState = CURSOR_REQUIRESEEK;
  }

  invalidateOverflowCache(pCur);





  return rc;
}

/*
** Save the positions of all cursors except pExcept open on the table 
** with root-page iRoot. Usually, this is called just before cursor
** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()).
................................................................................
**
** If the incremental vacuum is finished after this function has run,
** SQLITE_DONE is returned. If it is not finished, but no error occured,
** SQLITE_OK is returned. Otherwise an SQLite error code. 
*/
int sqlite3BtreeIncrVacuum(Btree *p){
  BtShared *pBt = p->pBt;


  assert( pBt->inTransaction==TRANS_WRITE && p->inTrans==TRANS_WRITE );
  if( !pBt->autoVacuum ){
    return SQLITE_DONE;
  }
  invalidateAllOverflowCache(pBt);






  return incrVacuumStep(pBt, 0);
}

/*
** This routine is called prior to sqlite3PagerCommit when a transaction
** is commited for an auto-vacuum database.
**
................................................................................
static int autoVacuumCommit(BtShared *pBt, Pgno *pnTrunc){
  int rc = SQLITE_OK;
  Pager *pPager = pBt->pPager;
#ifndef NDEBUG
  int nRef = sqlite3PagerRefcount(pPager);
#endif

  invalidateAllOverflowCache(pBt);








  assert(pBt->autoVacuum);
  if( !pBt->incrVacuum ){
    Pgno nFin = 0;

    if( pBt->nTrunc==0 ){
      Pgno nFree;
      Pgno nPtrmap;
................................................................................
    pBt->pCursor = pCur->pNext;
  }
  if( pCur->pNext ){
    pCur->pNext->pPrev = pCur->pPrev;
  }
  releasePage(pCur->pPage);
  unlockBtreeIfUnused(pBt);
  invalidateOverflowCache(pCur);


  sqliteFree(pCur);
  return SQLITE_OK;
}

/*
** Make a temporary cursor by filling in the fields of pTempCur.
** The temporary cursor is not on the cursor list for the Btree.
................................................................................
  rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0);
  if( rc ) return rc;
#else
  if( pBt->autoVacuum ){
    Pgno pgnoMove;      /* Move a page here to make room for the root-page */
    MemPage *pPageMove; /* The page to move to. */


    /* Creating a new table may probably require moving an existing database
    ** to make room for the new tables root page. In case this page turns
    ** out to be an overflow page, delete all overflow page-map caches
    ** held by open cursors.
    */
    invalidateAllOverflowCache(pBt);






    /* Read the value of meta[3] from the database to determine where the
    ** root page of the new table should go. meta[3] is the largest root-page
    ** created so far, so the new root-page is (meta[3]+1).
    */
    rc = sqlite3BtreeGetMeta(p, 4, &pgnoRoot);
    if( rc!=SQLITE_OK ) return rc;

Changes to src/tclsqlite.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
149
150
151
152
153
154
155
156

157
158
159
160
161
162
163
164
165
166
167
168

169





170
171
172
173
174
175
176
**    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.182 2007/05/03 16:31:26 danielk1977 Exp $
*/
#include "tcl.h"
#include <errno.h>

/*
** Some additional include files are needed if this file is not
** appended to the amalgamation.
................................................................................
}

/*
** Close an incremental blob channel.
*/
static int incrblobClose(ClientData instanceData, Tcl_Interp *interp){
  IncrblobChannel *p = (IncrblobChannel *)instanceData;
  sqlite3_blob_close(p->pBlob);


  /* Remove the channel from the SqliteDb.pIncrblob list. */
  if( p->pNext ){
    p->pNext->pPrev = p->pPrev;
  }
  if( p->pPrev ){
    p->pPrev->pNext = p->pNext;
  }
  if( p->pDb->pIncrblob==p ){
    p->pDb->pIncrblob = p->pNext;
  }


  Tcl_Free((char *)p);





  return TCL_OK;
}

/*
** Read data from an incremental blob channel.
*/
static int incrblobInput(







|







 







|
>












>

>
>
>
>
>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
**    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.183 2007/05/04 12:05:56 danielk1977 Exp $
*/
#include "tcl.h"
#include <errno.h>

/*
** Some additional include files are needed if this file is not
** appended to the amalgamation.
................................................................................
}

/*
** Close an incremental blob channel.
*/
static int incrblobClose(ClientData instanceData, Tcl_Interp *interp){
  IncrblobChannel *p = (IncrblobChannel *)instanceData;
  int rc = sqlite3_blob_close(p->pBlob);
  sqlite3 *db = p->pDb->db;

  /* Remove the channel from the SqliteDb.pIncrblob list. */
  if( p->pNext ){
    p->pNext->pPrev = p->pPrev;
  }
  if( p->pPrev ){
    p->pPrev->pNext = p->pNext;
  }
  if( p->pDb->pIncrblob==p ){
    p->pDb->pIncrblob = p->pNext;
  }

  /* Free the IncrblobChannel structure */
  Tcl_Free((char *)p);

  if( rc!=SQLITE_OK ){
    Tcl_SetResult(interp, (char *)sqlite3_errmsg(db), TCL_VOLATILE);
    return TCL_ERROR;
  }
  return TCL_OK;
}

/*
** Read data from an incremental blob channel.
*/
static int incrblobInput(

Changes to src/vdbeaux.c.

460
461
462
463
464
465
466

467
468
469
470
471
472

473
474
475
476
477
478
479
}


/*
** Change N opcodes starting at addr to No-ops.
*/
void sqlite3VdbeChangeToNoop(Vdbe *p, int addr, int N){

  VdbeOp *pOp = &p->aOp[addr];
  while( N-- ){
    freeP3(pOp->p3type, pOp->p3);
    memset(pOp, 0, sizeof(pOp[0]));
    pOp->opcode = OP_Noop;
    pOp++;

  }
}

/*
** Change the value of the P3 operand for a specific instruction.
** This routine is useful when a large program is loaded from a
** static array using sqlite3VdbeAddOpList but we want to make a







>
|
|
|
|
|
|
>







460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
}


/*
** Change N opcodes starting at addr to No-ops.
*/
void sqlite3VdbeChangeToNoop(Vdbe *p, int addr, int N){
  if( p && p->aOp ){
    VdbeOp *pOp = &p->aOp[addr];
    while( N-- ){
      freeP3(pOp->p3type, pOp->p3);
      memset(pOp, 0, sizeof(pOp[0]));
      pOp->opcode = OP_Noop;
      pOp++;
    }
  }
}

/*
** Change the value of the P3 operand for a specific instruction.
** This routine is useful when a large program is loaded from a
** static array using sqlite3VdbeAddOpList but we want to make a

Changes to src/vdbeblob.c.

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
...
166
167
168
169
170
171
172

173

174
175
176
177
178
179
180
181
182
183
184
185
...
218
219
220
221
222
223
224

225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
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
**
**    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.5 2007/05/03 18:14:10 danielk1977 Exp $
*/

#include "sqliteInt.h"
#include "vdbeInt.h"

#ifndef SQLITE_OMIT_INCRBLOB

................................................................................
      ** think that the table has one more column than it really
      ** does. An OP_Column to retrieve this imaginary column will
      ** always return an SQL NULL. This is useful because it means
      ** we can invoke OP_Column to fill in the vdbe cursors type 
      ** and offset cache without causing any IO.
      */
      sqlite3VdbeChangeP2(v, 5, pTab->nCol+1);

      sqlite3VdbeMakeReady(v, 1, 0, 1, 0);

    }

    rc = sqlite3SafetyOff(db);
    if( rc!=SQLITE_OK ){
      return rc;
    }

    sqlite3_bind_int64((sqlite3_stmt *)v, 1, iRow);
    rc = sqlite3_step((sqlite3_stmt *)v);
    if( rc!=SQLITE_ROW ){
      nAttempt++;
      rc = sqlite3_finalize((sqlite3_stmt *)v);
................................................................................
    rc = SQLITE_OK;
  }else if( rc==SQLITE_OK ){
    sqlite3_snprintf(sizeof(zErr), zErr, "no such rowid: %lld", iRow);
    rc = SQLITE_ERROR;
  }

blob_open_out:

  if( rc!=SQLITE_OK || sqlite3MallocFailed() ){
    sqlite3_finalize((sqlite3_stmt *)v);
  }
  zErr[sizeof(zErr)-1] = '\0';
  sqlite3Error(db, rc, zErr);
  return sqlite3ApiExit(db, rc);
}

/*
** Close a blob handle.
*/
int sqlite3_blob_close(sqlite3_blob *pBlob){
  Incrblob *p = (Incrblob *)pBlob;
  sqlite3_finalize(p->pStmt);
  sqliteFree(p);
  return SQLITE_OK;
}

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

  Incrblob *p = (Incrblob *)pBlob;

  if( (iOffset+n)>p->nByte ){
    return SQLITE_ERROR;

  }
  return sqlite3BtreeData(p->pCsr, iOffset+p->iOffset, n, z);


}

/*
** Write data to a blob handle.
*/
int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){

  Incrblob *p = (Incrblob *)pBlob;

  if( (iOffset+n)>p->nByte ){
    return SQLITE_ERROR;

  }
  return sqlite3BtreePutData(p->pCsr, iOffset+p->iOffset, n, z);


}

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







|







 







>
|
>



|
|







 







>



<
|








|

|






>

>
|
<
>

<
>
>






>

>
|
<
>

<
>
>











6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
...
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
...
220
221
222
223
224
225
226
227
228
229
230

231
232
233
234
235
236
237
238
239
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
**
**    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.6 2007/05/04 12:05:56 danielk1977 Exp $
*/

#include "sqliteInt.h"
#include "vdbeInt.h"

#ifndef SQLITE_OMIT_INCRBLOB

................................................................................
      ** think that the table has one more column than it really
      ** does. An OP_Column to retrieve this imaginary column will
      ** always return an SQL NULL. This is useful because it means
      ** we can invoke OP_Column to fill in the vdbe cursors type 
      ** and offset cache without causing any IO.
      */
      sqlite3VdbeChangeP2(v, 5, pTab->nCol+1);
      if( !sqlite3MallocFailed() ){
        sqlite3VdbeMakeReady(v, 1, 0, 1, 0);
      }
    }

    rc = sqlite3SafetyOff(db);
    if( rc!=SQLITE_OK || sqlite3MallocFailed() ){
      goto blob_open_out;
    }

    sqlite3_bind_int64((sqlite3_stmt *)v, 1, iRow);
    rc = sqlite3_step((sqlite3_stmt *)v);
    if( rc!=SQLITE_ROW ){
      nAttempt++;
      rc = sqlite3_finalize((sqlite3_stmt *)v);
................................................................................
    rc = SQLITE_OK;
  }else if( rc==SQLITE_OK ){
    sqlite3_snprintf(sizeof(zErr), zErr, "no such rowid: %lld", iRow);
    rc = SQLITE_ERROR;
  }

blob_open_out:
  zErr[sizeof(zErr)-1] = '\0';
  if( rc!=SQLITE_OK || sqlite3MallocFailed() ){
    sqlite3_finalize((sqlite3_stmt *)v);
  }

  sqlite3Error(db, rc, (rc==SQLITE_OK?0:zErr));
  return sqlite3ApiExit(db, rc);
}

/*
** Close a blob handle.
*/
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;
  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);
}

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

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

Added test/incrblob_err.test.

































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# 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_err.test,v 1.1 2007/05/04 12:05:56 danielk1977 Exp $
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Usage: do_malloc_test <test number> <options...>
#
# The first argument, <test number>, is an integer used to name the
# tests executed by this proc. Options are as follows:
#
#     -tclprep          TCL script to run to prepare test.
#     -sqlprep          SQL script to run to prepare test.
#     -tclbody          TCL script to run with malloc failure simulation.
#     -sqlbody          TCL script to run with malloc failure simulation.
#     -cleanup          TCL script to run after the test.
#
# This command runs a series of tests to verify SQLite's ability
# to handle an out-of-memory condition gracefully. It is assumed
# that if this condition occurs a malloc() call will return a
# NULL pointer. Linux, for example, doesn't do that by default. See
# the "BUGS" section of malloc(3).
#
# Each iteration of a loop, the TCL commands in any argument passed
# to the -tclbody switch, followed by the SQL commands in any argument
# passed to the -sqlbody switch are executed. Each iteration the
# Nth call to sqliteMalloc() is made to fail, where N is increased
# each time the loop runs starting from 1. When all commands execute
# successfully, the loop ends.
#
proc do_malloc_test {tn args} {
  array unset ::mallocopts 
  array set ::mallocopts $args

  set ::go 1
  for {set ::n 1} {$::go && $::n < 50000} {incr ::n} {
    do_test incrblob_err-$tn.$::n {

      # Remove all traces of database files test.db and test2.db from the files
      # system. Then open (empty database) "test.db" with the handle [db].
      # 
      sqlite_malloc_fail 0
      catch {db close} 
      catch {file delete -force test.db}
      catch {file delete -force test.db-journal}
      catch {file delete -force test2.db}
      catch {file delete -force test2.db-journal}
      catch {sqlite3 db test.db} 
      set ::DB [sqlite3_connection_pointer db]

      # Execute any -tclprep and -sqlprep scripts.
      #
      if {[info exists ::mallocopts(-tclprep)]} {
        eval $::mallocopts(-tclprep)
      }
      if {[info exists ::mallocopts(-sqlprep)]} {
        execsql $::mallocopts(-sqlprep)
      }

      # Now set the ${::n}th malloc() to fail and execute the -tclbody and
      # -sqlbody scripts.
      #
      sqlite_malloc_fail $::n
      set ::mallocbody {}
      if {[info exists ::mallocopts(-tclbody)]} {
        append ::mallocbody "$::mallocopts(-tclbody)\n"
      }
      if {[info exists ::mallocopts(-sqlbody)]} {
        append ::mallocbody "db eval {$::mallocopts(-sqlbody)}"
      }
      set v [catch $::mallocbody msg]

      # If the test fails (if $v!=0) and the database connection actually
      # exists, make sure the failure code is SQLITE_NOMEM.
      if {$v && [info command db]=="db" && [info exists ::mallocopts(-sqlbody)]
              && [db errorcode]!=7} {
        set v 999
      }

      set leftover [lindex [sqlite_malloc_stat] 2]
      if {$leftover>0} {
        if {$leftover>1} {puts "\nLeftover: $leftover\nReturn=$v  Message=$msg"}
        set ::go 0
        if {$v} {
          puts "\nError message returned: $msg"
        } else {
          set v {1 1}
        }
      } else {
        set v2 [expr {$msg=="" || $msg=="out of memory"}]
        if {!$v2} {puts "\nError message returned: $msg"}
        lappend v $v2
      }
    } {1 1}

    if {[info exists ::mallocopts(-cleanup)]} {
      catch [list uplevel #0 $::mallocopts(-cleanup)] msg
    }
  }
  unset ::mallocopts
}

set ::fd [open [info script]]
set ::data [read $::fd]
close $::fd

do_malloc_test 1 -tclprep {
  set bytes [file size [info script]]
  execsql {
    CREATE TABLE blobs(k, v BLOB);
    INSERT INTO blobs VALUES(1, zeroblob($::bytes));
  }
} -tclbody {
  set ::blob [db incrblob blobs v 1]
  set rc [catch {puts -nonewline $::blob $::data}]
  if {$rc} { error "out of memory" }
} 

do_malloc_test 2 -tclprep {
  execsql {
    CREATE TABLE blobs(k, v BLOB);
    INSERT INTO blobs VALUES(1, $::data);
  }
} -tclbody {
  set ::blob [db incrblob blobs v 1]
  set rc [catch {set ::r [read $::blob]}]
  if {$rc} { 
    error "out of memory" 
  } elseif {$::r ne $::data} {
    error "Bad data read..."
  }
}

do_malloc_test 3 -tclprep {
  execsql {
    CREATE TABLE blobs(k, v BLOB);
    INSERT INTO blobs VALUES(1, $::data);
  }
} -tclbody {
  set ::blob [db incrblob blobs v 1]
  set rc [catch {set ::r [read $::blob]}]
  if {$rc} { 
    error "out of memory" 
  } elseif {$::r ne $::data} {
    error "Bad data read..."
  }
  set rc [catch {close $::blob}]
  if {$rc} { 
    error "out of memory" 
  }
} 
sqlite_malloc_fail 0

do_ioerr_test incrblob_err-4 -cksum 1 -sqlprep {
  CREATE TABLE blobs(k, v BLOB);
  INSERT INTO blobs VALUES(1, $::data);
} -tclbody {
  set ::blob [db incrblob blobs v 1]
  read $::blob
}

do_ioerr_test incrblob_err-5 -cksum 1 -sqlprep {
  CREATE TABLE blobs(k, v BLOB);
  INSERT INTO blobs VALUES(1, zeroblob(length(CAST($::data AS BLOB))));
} -tclbody {
  set ::blob [db incrblob blobs v 1]
  puts -nonewline $::blob $::data
  close $::blob
}

do_ioerr_test incrblob_err-6 -cksum 1 -sqlprep {
  CREATE TABLE blobs(k, v BLOB);
  INSERT INTO blobs VALUES(1, $::data || $::data || $::data);
} -tclbody {
  set ::blob [db incrblob blobs v 1]
  seek $::blob -20 end
  puts -nonewline $::blob "12345678900987654321"
  close $::blob
}

finish_test

Changes to test/tester.tcl.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
...
476
477
478
479
480
481
482

483
484
485
486
487
488
489
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements some common TCL routines used for regression
# testing the SQLite library
#
# $Id: tester.tcl,v 1.80 2007/04/28 15:47:45 danielk1977 Exp $

# Make sure tclsqlite3 was compiled correctly.  Abort now with an
# error message if not.
#
if {[sqlite3 -tcl-uses-utf]} {
  if {"\u1234"=="u1234"} {
    puts stderr "***** BUILD PROBLEM *****"
................................................................................
  set ::ioerropts(-erc) 0
  set ::ioerropts(-count) 100000000
  set ::ioerropts(-persist) 1
  array set ::ioerropts $args

  set ::go 1
  for {set n $::ioerropts(-start)} {$::go} {incr n} {
set ::TN $n
    incr ::ioerropts(-count) -1
    if {$::ioerropts(-count)<0} break
 
    # Skip this IO error if it was specified with the "-exclude" option.
    if {[info exists ::ioerropts(-exclude)]} {
      if {[lsearch $::ioerropts(-exclude) $n]!=-1} continue
    }
................................................................................
      set s [expr $::sqlite_io_error_hit==0]
      set ::sqlite_io_error_hit 0

      # One of two things must have happened. either
      #   1.  We never hit the IO error and the SQL returned OK
      #   2.  An IO error was hit and the SQL failed
      #

      expr { ($s && !$r && !$::go) || (!$s && $r && $::go) }
    } {1}

    # If an IO error occured, then the checksum of the database should
    # be the same as before the script that caused the IO error was run.
    if {$::go && $::ioerropts(-cksum)} {
      do_test $testname.$n.4 {







|







 







|







 







>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
...
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements some common TCL routines used for regression
# testing the SQLite library
#
# $Id: tester.tcl,v 1.81 2007/05/04 12:05:56 danielk1977 Exp $

# Make sure tclsqlite3 was compiled correctly.  Abort now with an
# error message if not.
#
if {[sqlite3 -tcl-uses-utf]} {
  if {"\u1234"=="u1234"} {
    puts stderr "***** BUILD PROBLEM *****"
................................................................................
  set ::ioerropts(-erc) 0
  set ::ioerropts(-count) 100000000
  set ::ioerropts(-persist) 1
  array set ::ioerropts $args

  set ::go 1
  for {set n $::ioerropts(-start)} {$::go} {incr n} {
    set ::TN $n
    incr ::ioerropts(-count) -1
    if {$::ioerropts(-count)<0} break
 
    # Skip this IO error if it was specified with the "-exclude" option.
    if {[info exists ::ioerropts(-exclude)]} {
      if {[lsearch $::ioerropts(-exclude) $n]!=-1} continue
    }
................................................................................
      set s [expr $::sqlite_io_error_hit==0]
      set ::sqlite_io_error_hit 0

      # One of two things must have happened. either
      #   1.  We never hit the IO error and the SQL returned OK
      #   2.  An IO error was hit and the SQL failed
      #
#puts "$s $r $::go - $msg"
      expr { ($s && !$r && !$::go) || (!$s && $r && $::go) }
    } {1}

    # If an IO error occured, then the checksum of the database should
    # be the same as before the script that caused the IO error was run.
    if {$::go && $::ioerropts(-cksum)} {
      do_test $testname.$n.4 {