/ Check-in [8ced4915]
Login

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

Overview
Comment:Test cases to improve coverage of btree.c (and minor bugfixes). (CVS 2190)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 8ced491588764b1e1066787d0abf3cde8b60970b
User & Date: danielk1977 2005-01-11 10:25:06
Original User & Date: danielk1977 2005-01-11 10:25:07
Context
2005-01-11
10:25
Test cases to improve coverage of btree.c (and minor bugfixes). (CVS 2191) check-in: a37e0108 user: danielk1977 tags: trunk
10:25
Test cases to improve coverage of btree.c (and minor bugfixes). (CVS 2190) check-in: 8ced4915 user: danielk1977 tags: trunk
2005-01-10
12:59
Extra test cases to improve coverage of btree.c (CVS 2189) check-in: a4619886 user: danielk1977 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
...
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
...
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
...
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
....
1291
1292
1293
1294
1295
1296
1297



1298
1299
1300
1301
1302
1303
1304
....
1900
1901
1902
1903
1904
1905
1906

1907
1908
1909
1910
1911
1912
1913
1914
1915
1916

1917
1918
1919
1920
1921
1922
1923
....
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
....
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
....
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
....
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
....
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
....
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
....
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
....
3869
3870
3871
3872
3873
3874
3875
3876

3877
3878
3879
3880
3881
3882
3883
....
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
....
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
....
4334
4335
4336
4337
4338
4339
4340
4341

4342
4343
4344
4345
4346
4347
4348
....
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
....
4767
4768
4769
4770
4771
4772
4773

4774
4775
4776
4777
4778
4779
4780
....
4863
4864
4865
4866
4867
4868
4869

4870

4871

4872
4873
4874
4875
4876
4877
4878
** 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.229 2005/01/10 12:59:52 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.
................................................................................
  void *pArg;               /* First arg to xCompare() */
  Pgno pgnoRoot;            /* The root page of this tree */
  MemPage *pPage;           /* Page that contains the entry */
  int idx;                  /* Index of the entry in pPage->aCell[] */
  CellInfo info;            /* A parse of the cell we are pointing at */
  u8 wrFlag;                /* True if writable */
  u8 isValid;               /* TRUE if points to a valid entry */
  u8 status;                /* Set to SQLITE_ABORT if cursors is invalidated */
};

/*
** Forward declaration
*/
static int checkReadLocks(Btree*,Pgno,BtCursor*);

................................................................................
** we failed to detect any corruption.
*/
static int initPage(
  MemPage *pPage,        /* The page to be initialized */
  MemPage *pParent       /* The parent.  Might be NULL */
){
  int pc;            /* Address of a freeblock within pPage->aData[] */
  int i;             /* Loop counter */
  int hdr;           /* Offset to beginning of page header */
  u8 *data;          /* Equal to pPage->aData */
  Btree *pBt;        /* The main btree structure */
  int usableSize;    /* Amount of usable space on each page */
  int cellOffset;    /* Offset from start of page to first cell pointer */
  int nFree;         /* Number of unused bytes on the page */
  int top;           /* First byte of the cell content area */
................................................................................
    /* All pages must have at least one cell, except for root pages */
    return SQLITE_CORRUPT; /* bkpt-CORRUPT */
  }

  /* Compute the total free space on the page */
  pc = get2byte(&data[hdr+1]);
  nFree = data[hdr+7] + top - (cellOffset + 2*pPage->nCell);
  i = 0;
  while( pc>0 ){
    int next, size;
    if( pc>usableSize-4 ){
      /* Free block is off the page */
      return SQLITE_CORRUPT;  /* bkpt-CORRUPT */
    }
    if( i++>SQLITE_MAX_PAGE_SIZE/4 ){
      /* The free block list forms an infinite loop */
      return SQLITE_CORRUPT;  /* bkpt-CORRUPT */
    }
    next = get2byte(&data[pc]);
    size = get2byte(&data[pc+2]);
    if( next>0 && next<=pc+size+3 ){
      /* Free blocks must be in accending order */
      return SQLITE_CORRUPT;  /* bkpt-CORRUPT */
    }
    nFree += size;
................................................................................
** size supplied does not meet this constraint then the page size is not
** changed.
**
** Page sizes are constrained to be a power of two so that the region
** of the database file used for locking (beginning at PENDING_BYTE,
** the first byte past the 1GB boundary, 0x40000000) needs to occur
** at the beginning of a page.



*/
int sqlite3BtreeSetPageSize(Btree *pBt, int pageSize, int nReserve){
  if( pBt->pageSizeFixed ){
    return SQLITE_READONLY;
  }
  if( nReserve<0 ){
    nReserve = pBt->pageSize - pBt->usableSize;
................................................................................
#endif

#ifdef SQLITE_TEST
/*
** Print debugging information about all cursors to standard output.
*/
void sqlite3BtreeCursorList(Btree *pBt){

  BtCursor *pCur;
  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
    MemPage *pPage = pCur->pPage;
    char *zMode = pCur->wrFlag ? "rw" : "ro";
    sqlite3DebugPrintf("CURSOR %p rooted at %4d(%s) currently at %d.%d%s\n",
       pCur, pCur->pgnoRoot, zMode,
       pPage ? pPage->pgno : 0, pCur->idx,
       pCur->isValid ? "" : " eof"
    );
  }

}
#endif

/*
** Rollback the transaction in progress.  All cursors will be
** invalided by this operation.  Any attempt to use a cursor
** that was open at the beginning of this operation will result
................................................................................
  pCur->pNext = pBt->pCursor;
  if( pCur->pNext ){
    pCur->pNext->pPrev = pCur;
  }
  pCur->pPrev = 0;
  pBt->pCursor = pCur;
  pCur->isValid = 0;
  pCur->status = SQLITE_OK;
  *ppCur = pCur;
  return SQLITE_OK;

create_cursor_exception:
  if( pCur ){
    releasePage(pCur->pPage);
    sqliteFree(pCur);
................................................................................
** begins at "offset".
**
** Return SQLITE_OK on success or an error code if anything goes
** wrong.  An error is returned if "offset+amt" is larger than
** the available payload.
*/
int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
  if( !pCur->isValid ){
    return pCur->status;
  }
  assert( pCur->pPage!=0 );
  assert( pCur->pPage->intKey==0 );
  assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
  return getPayload(pCur, offset, amt, (unsigned char*)pBuf, 0);
}

/*
................................................................................
** begins at "offset".
**
** Return SQLITE_OK on success or an error code if anything goes
** wrong.  An error is returned if "offset+amt" is larger than
** the available payload.
*/
int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
  if( !pCur->isValid ){
    return pCur->status ? pCur->status : SQLITE_INTERNAL;
  }
  assert( pCur->pPage!=0 );
  assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
  return getPayload(pCur, offset, amt, pBuf, 1);
}

/*
** Return a pointer to payload information from the entry that the 
................................................................................

/* Move the cursor to the first entry in the table.  Return SQLITE_OK
** on success.  Set *pRes to 0 if the cursor actually points to something
** or set *pRes to 1 if the table is empty.
*/
int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
  int rc;
  if( pCur->status ){
    return pCur->status;
  }
  rc = moveToRoot(pCur);
  if( rc ) return rc;
  if( pCur->isValid==0 ){
    assert( pCur->pPage->nCell==0 );
    *pRes = 1;
    return SQLITE_OK;
  }
................................................................................

/* Move the cursor to the last entry in the table.  Return SQLITE_OK
** on success.  Set *pRes to 0 if the cursor actually points to something
** or set *pRes to 1 if the table is empty.
*/
int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
  int rc;
  if( pCur->status ){
    return pCur->status;
  }
  rc = moveToRoot(pCur);
  if( rc ) return rc;
  if( pCur->isValid==0 ){
    assert( pCur->pPage->nCell==0 );
    *pRes = 1;
    return SQLITE_OK;
  }
................................................................................
**                  exactly matches pKey.
**
**     *pRes>0      The cursor is left pointing at an entry that
**                  is larger than pKey.
*/
int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
  int rc;

  if( pCur->status ){
    return pCur->status;
  }
  rc = moveToRoot(pCur);
  if( rc ) return rc;
  assert( pCur->pPage );
  assert( pCur->pPage->isInit );
  if( pCur->isValid==0 ){
    *pRes = -1;
    assert( pCur->pPage->nCell==0 );
................................................................................
      */
      if( pBt->autoVacuum && pgnoPtrmap!=0 && rc==SQLITE_OK ){
        rc = ptrmapPut(pBt, pgnoOvfl, PTRMAP_OVERFLOW2, pgnoPtrmap);
      }
#endif
      if( rc ){
        releasePage(pToRelease);
        clearCell(pPage, pCell);
        return rc;
      }
      put4byte(pPrior, pgnoOvfl);
      releasePage(pToRelease);
      pToRelease = pOvfl;
      pPrior = pOvfl->aData;
      put4byte(pPrior, 0);
................................................................................
  pageFlags = pPage->aData[0];
  for(i=0; i<k; i++){
    MemPage *pNew;
    if( i<nOld ){
      pNew = apNew[i] = apOld[i];
      pgnoNew[i] = pgnoOld[i];
      apOld[i] = 0;
      sqlite3pager_write(pNew->aData);

    }else{
      rc = allocatePage(pBt, &pNew, &pgnoNew[i], pgnoNew[i-1], 0);
      if( rc ) goto balance_cleanup;
      apNew[i] = pNew;
    }
    nNew++;
    zeroPage(pNew, pageFlags);
................................................................................
  int loc;
  int szNew;
  MemPage *pPage;
  Btree *pBt = pCur->pBt;
  unsigned char *oldCell;
  unsigned char *newCell = 0;

  if( pCur->status ){
    return pCur->status;  /* A rollback destroyed this cursor */
  }
  if( pBt->inTrans!=TRANS_WRITE ){
    /* Must start a transaction before doing an insert */
    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
  }
  assert( !pBt->readOnly );
  if( !pCur->wrFlag ){
    return SQLITE_PERM;   /* Cursor not open for writing */
................................................................................
  MemPage *pPage = pCur->pPage;
  unsigned char *pCell;
  int rc;
  Pgno pgnoChild = 0;
  Btree *pBt = pCur->pBt;

  assert( pPage->isInit );
  if( pCur->status ){
    return pCur->status;  /* A rollback destroyed this cursor */
  }
  if( pBt->inTrans!=TRANS_WRITE ){
    /* Must start a transaction before doing a delete */
    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
  }
  assert( !pBt->readOnly );
  if( pCur->idx >= pPage->nCell ){
    return SQLITE_ERROR;  /* The cursor is not pointing to anything */
................................................................................
  ** data. The clearCell() call frees any overflow pages associated with the
  ** cell. The cell itself is still intact.
  */
  pCell = findCell(pPage, pCur->idx);
  if( !pPage->leaf ){
    pgnoChild = get4byte(pCell);
  }
  clearCell(pPage, pCell);


  if( !pPage->leaf ){
    /*
    ** The entry we are about to delete is not a leaf so if we do not
    ** do something we will leave a hole on an internal page.
    ** We have to fill the hole by moving in a cell from a leaf.  The
    ** next Cell after the one to be deleted is guaranteed to exist and
................................................................................
  MemPage *pRoot;
  Pgno pgnoRoot;
  int rc;
  if( pBt->inTrans!=TRANS_WRITE ){
    /* Must start a transaction first */
    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
  }
  if( pBt->readOnly ){
    return SQLITE_READONLY;
  }

  /* It is illegal to create a table if any cursors are open on the
  ** database. This is because in auto-vacuum mode the backend may
  ** need to move a database page to make room for the new root-page.
  ** If an open cursor was using the page a problem would occur.
  */
  if( pBt->pCursor ){
................................................................................
}

/*
** Print a disassembly of the given page on standard output.  This routine
** is used for debugging and testing only.
*/
#ifdef SQLITE_TEST

static int btreePageDump(Btree *pBt, int pgno, int recursive, MemPage *pParent){
  int rc;
  MemPage *pPage;
  int i, j, c;
  int nFree;
  u16 idx;
  int hdr;
................................................................................
    btreePageDump(pBt, get4byte(&data[hdr+8]), 1, pPage);
  }
  pPage->isInit = isInit;
  sqlite3pager_unref(data);
  fflush(stdout);
  return SQLITE_OK;
}

int sqlite3BtreePageDump(Btree *pBt, int pgno, int recursive){

  return btreePageDump(pBt, pgno, recursive, 0);

}
#endif

#ifdef SQLITE_TEST
/*
** Fill aResult[] with information about the entry and page that the
** cursor is pointing to.







|







 







<







 







<







 







<






<
<
<
<







 







>
>
>







 







>










>







 







<







 







|
<
<







 







|
<
<







 







<
<
<







 







<
<
<







 







<
<
<
<







 







|







 







|
>







 







<
<
<







 







<
<
<







 







|
>







 







|
<
<







 







>







 







>

>

>







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
352
353
354
355
356
357
358

359
360
361
362
363
364
365
...
948
949
950
951
952
953
954

955
956
957
958
959
960
961
...
991
992
993
994
995
996
997

998
999
1000
1001
1002
1003




1004
1005
1006
1007
1008
1009
1010
....
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
....
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
....
2111
2112
2113
2114
2115
2116
2117

2118
2119
2120
2121
2122
2123
2124
....
2337
2338
2339
2340
2341
2342
2343
2344


2345
2346
2347
2348
2349
2350
2351
....
2354
2355
2356
2357
2358
2359
2360
2361


2362
2363
2364
2365
2366
2367
2368
....
2592
2593
2594
2595
2596
2597
2598



2599
2600
2601
2602
2603
2604
2605
....
2611
2612
2613
2614
2615
2616
2617



2618
2619
2620
2621
2622
2623
2624
....
2653
2654
2655
2656
2657
2658
2659




2660
2661
2662
2663
2664
2665
2666
....
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
....
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
....
4216
4217
4218
4219
4220
4221
4222



4223
4224
4225
4226
4227
4228
4229
....
4287
4288
4289
4290
4291
4292
4293



4294
4295
4296
4297
4298
4299
4300
....
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
....
4385
4386
4387
4388
4389
4390
4391
4392


4393
4394
4395
4396
4397
4398
4399
....
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
....
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
** 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.230 2005/01/11 10:25:07 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.
................................................................................
  void *pArg;               /* First arg to xCompare() */
  Pgno pgnoRoot;            /* The root page of this tree */
  MemPage *pPage;           /* Page that contains the entry */
  int idx;                  /* Index of the entry in pPage->aCell[] */
  CellInfo info;            /* A parse of the cell we are pointing at */
  u8 wrFlag;                /* True if writable */
  u8 isValid;               /* TRUE if points to a valid entry */

};

/*
** Forward declaration
*/
static int checkReadLocks(Btree*,Pgno,BtCursor*);

................................................................................
** we failed to detect any corruption.
*/
static int initPage(
  MemPage *pPage,        /* The page to be initialized */
  MemPage *pParent       /* The parent.  Might be NULL */
){
  int pc;            /* Address of a freeblock within pPage->aData[] */

  int hdr;           /* Offset to beginning of page header */
  u8 *data;          /* Equal to pPage->aData */
  Btree *pBt;        /* The main btree structure */
  int usableSize;    /* Amount of usable space on each page */
  int cellOffset;    /* Offset from start of page to first cell pointer */
  int nFree;         /* Number of unused bytes on the page */
  int top;           /* First byte of the cell content area */
................................................................................
    /* All pages must have at least one cell, except for root pages */
    return SQLITE_CORRUPT; /* bkpt-CORRUPT */
  }

  /* Compute the total free space on the page */
  pc = get2byte(&data[hdr+1]);
  nFree = data[hdr+7] + top - (cellOffset + 2*pPage->nCell);

  while( pc>0 ){
    int next, size;
    if( pc>usableSize-4 ){
      /* Free block is off the page */
      return SQLITE_CORRUPT;  /* bkpt-CORRUPT */
    }




    next = get2byte(&data[pc]);
    size = get2byte(&data[pc+2]);
    if( next>0 && next<=pc+size+3 ){
      /* Free blocks must be in accending order */
      return SQLITE_CORRUPT;  /* bkpt-CORRUPT */
    }
    nFree += size;
................................................................................
** size supplied does not meet this constraint then the page size is not
** changed.
**
** Page sizes are constrained to be a power of two so that the region
** of the database file used for locking (beginning at PENDING_BYTE,
** the first byte past the 1GB boundary, 0x40000000) needs to occur
** at the beginning of a page.
**
** If parameter nReserve is less than zero, then the number of reserved
** bytes per page is left unchanged.
*/
int sqlite3BtreeSetPageSize(Btree *pBt, int pageSize, int nReserve){
  if( pBt->pageSizeFixed ){
    return SQLITE_READONLY;
  }
  if( nReserve<0 ){
    nReserve = pBt->pageSize - pBt->usableSize;
................................................................................
#endif

#ifdef SQLITE_TEST
/*
** Print debugging information about all cursors to standard output.
*/
void sqlite3BtreeCursorList(Btree *pBt){
#ifndef SQLITE_OMIT_CURSOR
  BtCursor *pCur;
  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
    MemPage *pPage = pCur->pPage;
    char *zMode = pCur->wrFlag ? "rw" : "ro";
    sqlite3DebugPrintf("CURSOR %p rooted at %4d(%s) currently at %d.%d%s\n",
       pCur, pCur->pgnoRoot, zMode,
       pPage ? pPage->pgno : 0, pCur->idx,
       pCur->isValid ? "" : " eof"
    );
  }
#endif
}
#endif

/*
** Rollback the transaction in progress.  All cursors will be
** invalided by this operation.  Any attempt to use a cursor
** that was open at the beginning of this operation will result
................................................................................
  pCur->pNext = pBt->pCursor;
  if( pCur->pNext ){
    pCur->pNext->pPrev = pCur;
  }
  pCur->pPrev = 0;
  pBt->pCursor = pCur;
  pCur->isValid = 0;

  *ppCur = pCur;
  return SQLITE_OK;

create_cursor_exception:
  if( pCur ){
    releasePage(pCur->pPage);
    sqliteFree(pCur);
................................................................................
** begins at "offset".
**
** Return SQLITE_OK on success or an error code if anything goes
** wrong.  An error is returned if "offset+amt" is larger than
** the available payload.
*/
int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
  assert( pCur->isValid );


  assert( pCur->pPage!=0 );
  assert( pCur->pPage->intKey==0 );
  assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
  return getPayload(pCur, offset, amt, (unsigned char*)pBuf, 0);
}

/*
................................................................................
** begins at "offset".
**
** Return SQLITE_OK on success or an error code if anything goes
** wrong.  An error is returned if "offset+amt" is larger than
** the available payload.
*/
int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
  assert( pCur->isValid );


  assert( pCur->pPage!=0 );
  assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
  return getPayload(pCur, offset, amt, pBuf, 1);
}

/*
** Return a pointer to payload information from the entry that the 
................................................................................

/* Move the cursor to the first entry in the table.  Return SQLITE_OK
** on success.  Set *pRes to 0 if the cursor actually points to something
** or set *pRes to 1 if the table is empty.
*/
int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
  int rc;



  rc = moveToRoot(pCur);
  if( rc ) return rc;
  if( pCur->isValid==0 ){
    assert( pCur->pPage->nCell==0 );
    *pRes = 1;
    return SQLITE_OK;
  }
................................................................................

/* Move the cursor to the last entry in the table.  Return SQLITE_OK
** on success.  Set *pRes to 0 if the cursor actually points to something
** or set *pRes to 1 if the table is empty.
*/
int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
  int rc;



  rc = moveToRoot(pCur);
  if( rc ) return rc;
  if( pCur->isValid==0 ){
    assert( pCur->pPage->nCell==0 );
    *pRes = 1;
    return SQLITE_OK;
  }
................................................................................
**                  exactly matches pKey.
**
**     *pRes>0      The cursor is left pointing at an entry that
**                  is larger than pKey.
*/
int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
  int rc;




  rc = moveToRoot(pCur);
  if( rc ) return rc;
  assert( pCur->pPage );
  assert( pCur->pPage->isInit );
  if( pCur->isValid==0 ){
    *pRes = -1;
    assert( pCur->pPage->nCell==0 );
................................................................................
      */
      if( pBt->autoVacuum && pgnoPtrmap!=0 && rc==SQLITE_OK ){
        rc = ptrmapPut(pBt, pgnoOvfl, PTRMAP_OVERFLOW2, pgnoPtrmap);
      }
#endif
      if( rc ){
        releasePage(pToRelease);
        /* clearCell(pPage, pCell); */
        return rc;
      }
      put4byte(pPrior, pgnoOvfl);
      releasePage(pToRelease);
      pToRelease = pOvfl;
      pPrior = pOvfl->aData;
      put4byte(pPrior, 0);
................................................................................
  pageFlags = pPage->aData[0];
  for(i=0; i<k; i++){
    MemPage *pNew;
    if( i<nOld ){
      pNew = apNew[i] = apOld[i];
      pgnoNew[i] = pgnoOld[i];
      apOld[i] = 0;
      rc = sqlite3pager_write(pNew->aData);
      if( rc ) goto balance_cleanup;
    }else{
      rc = allocatePage(pBt, &pNew, &pgnoNew[i], pgnoNew[i-1], 0);
      if( rc ) goto balance_cleanup;
      apNew[i] = pNew;
    }
    nNew++;
    zeroPage(pNew, pageFlags);
................................................................................
  int loc;
  int szNew;
  MemPage *pPage;
  Btree *pBt = pCur->pBt;
  unsigned char *oldCell;
  unsigned char *newCell = 0;




  if( pBt->inTrans!=TRANS_WRITE ){
    /* Must start a transaction before doing an insert */
    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
  }
  assert( !pBt->readOnly );
  if( !pCur->wrFlag ){
    return SQLITE_PERM;   /* Cursor not open for writing */
................................................................................
  MemPage *pPage = pCur->pPage;
  unsigned char *pCell;
  int rc;
  Pgno pgnoChild = 0;
  Btree *pBt = pCur->pBt;

  assert( pPage->isInit );



  if( pBt->inTrans!=TRANS_WRITE ){
    /* Must start a transaction before doing a delete */
    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
  }
  assert( !pBt->readOnly );
  if( pCur->idx >= pPage->nCell ){
    return SQLITE_ERROR;  /* The cursor is not pointing to anything */
................................................................................
  ** data. The clearCell() call frees any overflow pages associated with the
  ** cell. The cell itself is still intact.
  */
  pCell = findCell(pPage, pCur->idx);
  if( !pPage->leaf ){
    pgnoChild = get4byte(pCell);
  }
  rc = clearCell(pPage, pCell);
  if( rc ) return rc;

  if( !pPage->leaf ){
    /*
    ** The entry we are about to delete is not a leaf so if we do not
    ** do something we will leave a hole on an internal page.
    ** We have to fill the hole by moving in a cell from a leaf.  The
    ** next Cell after the one to be deleted is guaranteed to exist and
................................................................................
  MemPage *pRoot;
  Pgno pgnoRoot;
  int rc;
  if( pBt->inTrans!=TRANS_WRITE ){
    /* Must start a transaction first */
    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
  }
  assert( !pBt->readOnly );



  /* It is illegal to create a table if any cursors are open on the
  ** database. This is because in auto-vacuum mode the backend may
  ** need to move a database page to make room for the new root-page.
  ** If an open cursor was using the page a problem would occur.
  */
  if( pBt->pCursor ){
................................................................................
}

/*
** Print a disassembly of the given page on standard output.  This routine
** is used for debugging and testing only.
*/
#ifdef SQLITE_TEST
#ifndef SQLITE_OMIT_BTREEPAGEDUMP
static int btreePageDump(Btree *pBt, int pgno, int recursive, MemPage *pParent){
  int rc;
  MemPage *pPage;
  int i, j, c;
  int nFree;
  u16 idx;
  int hdr;
................................................................................
    btreePageDump(pBt, get4byte(&data[hdr+8]), 1, pPage);
  }
  pPage->isInit = isInit;
  sqlite3pager_unref(data);
  fflush(stdout);
  return SQLITE_OK;
}
#endif
int sqlite3BtreePageDump(Btree *pBt, int pgno, int recursive){
#ifndef SQLITE_OMIT_BTREEPAGEDUMP
  return btreePageDump(pBt, pgno, recursive, 0);
#endif
}
#endif

#ifdef SQLITE_TEST
/*
** Fill aResult[] with information about the entry and page that the
** cursor is pointing to.

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
931
932
933
934
935
936
937
938
939
940


941
942
943
944
945
946
947
948
949
950
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.180 2005/01/08 12:42:39 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

................................................................................
  assert( pPager->state>=PAGER_EXCLUSIVE || pPg );
  TRACE3("PLAYBACK %d page %d\n", PAGERID(pPager), pgno);
  if( pPager->state>=PAGER_EXCLUSIVE ){
    sqlite3OsSeek(&pPager->fd, (pgno-1)*(i64)pPager->pageSize);
    rc = sqlite3OsWrite(&pPager->fd, aData, pPager->pageSize);
  }
  if( pPg ){
    /* No page should ever be rolled back that is in use, except for page
    ** 1 which is held in use in order to keep the lock on the database
    ** active.


    */
    void *pData;
    assert( pPg->nRef==0 || pPg->pgno==1 );
    pData = PGHDR_TO_DATA(pPg);
    memcpy(pData, aData, pPager->pageSize);
    if( pPager->xDestructor ){  /*** FIX ME:  Should this be xReinit? ***/
      pPager->xDestructor(pData, pPager->pageSize);
    }
    if( pPager->state>=PAGER_EXCLUSIVE ){
      pPg->dirty = 0;







|







 







|
|
|
>
>


|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.181 2005/01/11 10:25:08 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

................................................................................
  assert( pPager->state>=PAGER_EXCLUSIVE || pPg );
  TRACE3("PLAYBACK %d page %d\n", PAGERID(pPager), pgno);
  if( pPager->state>=PAGER_EXCLUSIVE ){
    sqlite3OsSeek(&pPager->fd, (pgno-1)*(i64)pPager->pageSize);
    rc = sqlite3OsWrite(&pPager->fd, aData, pPager->pageSize);
  }
  if( pPg ){
    /* No page should ever be explicitly rolled back that is in use, except
    ** for page 1 which is held in use in order to keep the lock on the
    ** database active. However such a page may be rolled back as a result
    ** of an internal error resulting in an automatic call to
    ** sqlite3pager_rollback().
    */
    void *pData;
    /* assert( pPg->nRef==0 || pPg->pgno==1 ); */
    pData = PGHDR_TO_DATA(pPg);
    memcpy(pData, aData, pPager->pageSize);
    if( pPager->xDestructor ){  /*** FIX ME:  Should this be xReinit? ***/
      pPager->xDestructor(pData, pPager->pageSize);
    }
    if( pPager->state>=PAGER_EXCLUSIVE ){
      pPg->dirty = 0;

Changes to src/pragma.c.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
**    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.
**
*************************************************************************
** This file contains code used to implement the PRAGMA command.
**
** $Id: pragma.c,v 1.82 2005/01/08 15:44:26 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>

/* Ignore this whole file if pragmas are disabled
*/
................................................................................
  */
  if( sqlite3StrICmp(zLeft,"page_size")==0 ){
    Btree *pBt = pDb->pBt;
    if( !zRight ){
      int size = pBt ? sqlite3BtreeGetPageSize(pBt) : 0;
      returnSingleInt(pParse, "page_size", size);
    }else{
      sqlite3BtreeSetPageSize(pBt, atoi(zRight), sqlite3BtreeGetReserve(pBt));
    }
  }else
#endif /* SQLITE_OMIT_PAGER_PRAGMAS */

  /*
  **  PRAGMA [database.]auto_vacuum
  **  PRAGMA [database.]auto_vacuum=N







|







 







|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
**    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.
**
*************************************************************************
** This file contains code used to implement the PRAGMA command.
**
** $Id: pragma.c,v 1.83 2005/01/11 10:25:08 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>

/* Ignore this whole file if pragmas are disabled
*/
................................................................................
  */
  if( sqlite3StrICmp(zLeft,"page_size")==0 ){
    Btree *pBt = pDb->pBt;
    if( !zRight ){
      int size = pBt ? sqlite3BtreeGetPageSize(pBt) : 0;
      returnSingleInt(pParse, "page_size", size);
    }else{
      sqlite3BtreeSetPageSize(pBt, atoi(zRight), -1);
    }
  }else
#endif /* SQLITE_OMIT_PAGER_PRAGMAS */

  /*
  **  PRAGMA [database.]auto_vacuum
  **  PRAGMA [database.]auto_vacuum=N

Changes to src/vdbe.c.

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
....
2561
2562
2563
2564
2565
2566
2567
2568



2569
2570
2571
2572
2573



2574
2575
2576
2577
2578
2579
2580
2581
2582



2583
2584
2585
2586
2587
2588
2589
....
2796
2797
2798
2799
2800
2801
2802
2803

2804
2805
2806
2807
2808

2809
2810
2811
2812
2813
2814
2815
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files.  The formatting
** of the code in this file is, therefore, important.  See other comments
** in this file for details.  If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
** $Id: vdbe.c,v 1.437 2005/01/10 12:59:53 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>
#include "vdbeInt.h"

/*
................................................................................
      if( pOp->p2==0 && pOp->opcode==OP_MoveGe ){
        pC->movetoTarget = iKey;
        pC->deferredMoveto = 1;
        assert( (pTos->flags & MEM_Dyn)==0 );
        pTos--;
        break;
      }
      sqlite3BtreeMoveto(pC->pCursor, 0, (u64)iKey, &res);



      pC->lastRecno = pTos->i;
      pC->recnoIsValid = res==0;
    }else{
      Stringify(pTos, db->enc);
      sqlite3BtreeMoveto(pC->pCursor, pTos->z, pTos->n, &res);



      pC->recnoIsValid = 0;
    }
    pC->deferredMoveto = 0;
    pC->cacheValid = 0;
    *pC->pIncrKey = 0;
    sqlite3_search_count++;
    if( oc==OP_MoveGe || oc==OP_MoveGt ){
      if( res<0 ){
        sqlite3BtreeNext(pC->pCursor, &res);



        pC->recnoIsValid = 0;
      }else{
        res = 0;
      }
    }else{
      assert( oc==OP_MoveLt || oc==OP_MoveLe );
      if( res>=0 ){
................................................................................
  assert( p->apCsr[i]!=0 );
  if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){
    int res, rx;
    u64 iKey;
    assert( pTos->flags & MEM_Int );
    assert( p->apCsr[i]->intKey );
    iKey = intToKey(pTos->i);
    rx = sqlite3BtreeMoveto(pCrsr, 0, iKey, &res);

    pC->lastRecno = pTos->i;
    pC->recnoIsValid = res==0;
    pC->nullRow = 0;
    pC->cacheValid = 0;
    if( rx!=SQLITE_OK || res!=0 ){

      pc = pOp->p2 - 1;
      pC->recnoIsValid = 0;
    }
  }
  Release(pTos);
  pTos--;
  break;







|







 







|
>
>
>




|
>
>
>








|
>
>
>







 







|
>




|
>







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
....
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
....
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files.  The formatting
** of the code in this file is, therefore, important.  See other comments
** in this file for details.  If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
** $Id: vdbe.c,v 1.438 2005/01/11 10:25:08 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>
#include "vdbeInt.h"

/*
................................................................................
      if( pOp->p2==0 && pOp->opcode==OP_MoveGe ){
        pC->movetoTarget = iKey;
        pC->deferredMoveto = 1;
        assert( (pTos->flags & MEM_Dyn)==0 );
        pTos--;
        break;
      }
      rc = sqlite3BtreeMoveto(pC->pCursor, 0, (u64)iKey, &res);
      if( rc!=SQLITE_OK ){
        goto abort_due_to_error;
      }
      pC->lastRecno = pTos->i;
      pC->recnoIsValid = res==0;
    }else{
      Stringify(pTos, db->enc);
      rc = sqlite3BtreeMoveto(pC->pCursor, pTos->z, pTos->n, &res);
      if( rc!=SQLITE_OK ){
        goto abort_due_to_error;
      }
      pC->recnoIsValid = 0;
    }
    pC->deferredMoveto = 0;
    pC->cacheValid = 0;
    *pC->pIncrKey = 0;
    sqlite3_search_count++;
    if( oc==OP_MoveGe || oc==OP_MoveGt ){
      if( res<0 ){
        rc = sqlite3BtreeNext(pC->pCursor, &res);
        if( rc!=SQLITE_OK ){
          goto abort_due_to_error;
        }
        pC->recnoIsValid = 0;
      }else{
        res = 0;
      }
    }else{
      assert( oc==OP_MoveLt || oc==OP_MoveLe );
      if( res>=0 ){
................................................................................
  assert( p->apCsr[i]!=0 );
  if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){
    int res, rx;
    u64 iKey;
    assert( pTos->flags & MEM_Int );
    assert( p->apCsr[i]->intKey );
    iKey = intToKey(pTos->i);
    // rx = sqlite3BtreeMoveto(pCrsr, 0, iKey, &res);
    rc = sqlite3BtreeMoveto(pCrsr, 0, iKey, &res);
    pC->lastRecno = pTos->i;
    pC->recnoIsValid = res==0;
    pC->nullRow = 0;
    pC->cacheValid = 0;
    // if( rx!=SQLITE_OK || res!=0 ){
    if( res!=0 ){
      pc = pOp->p2 - 1;
      pC->recnoIsValid = 0;
    }
  }
  Release(pTos);
  pTos--;
  break;

Added test/autovacuum_ioerr.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
# 2001 September 15
#
# 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.
#
#***********************************************************************
#
# This file runs the tests in the file crash.test with auto-vacuum enabled
# databases.
#
# $Id: autovacuum_ioerr.test,v 1.1 2005/01/11 10:25:07 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl
rename finish_test really_finish_test2
proc finish_test {} {}
set ISQUICK 1

rename sqlite3 real_sqlite3
proc sqlite3 {args} {
  set r [eval "real_sqlite3 $args"]
  if { [llength $args] == 2 } {
    [lindex $args 0] eval {pragma auto_vacuum = 1}
  }
  set r
}

rename do_test really_do_test
proc do_test {args} {
  set sc [concat really_do_test "autovacuum-[lindex $args 0]" \
      [lrange $args 1 end]]
  eval $sc
}

source $testdir/ioerr.test

rename sqlite3 ""
rename real_sqlite3 sqlite3
rename finish_test ""
rename really_finish_test2 finish_test
rename do_test ""
rename really_do_test do_test
finish_test



Added test/autovacuum_ioerr2.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
# 2001 October 12
#
# 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.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this file is testing for correct handling of I/O errors
# such as writes failing because the disk is full.
# 
# The tests in this file use special facilities that are only
# available in the SQLite test fixture.
#
# $Id: autovacuum_ioerr2.test,v 1.1 2005/01/11 10:25:07 danielk1977 Exp $

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

proc opendb {} {
  catch {file delete -force test.db}
  catch {file delete -force test.db-journal}
  sqlite3 db test.db
  execsql {pragma auto_vacuum = 1}
  execsql {SELECT * FROM sqlite_master}
}

set ::go 1
for {set n 1} {$go} {incr n} {
  do_test autovacuum-ioerr2-1.$n.1 {
    set ::sqlite_io_error_pending 0
    db close
    opendb
    execsql {
      CREATE TABLE abc(a);
      INSERT INTO abc VALUES(randstr(1500,1500));
    }
    expr [file size test.db]/1024
  } {4}
  do_test autovacuum-ioerr2-1.$n.2 [subst {
    set ::sqlite_io_error_pending $n
  }] $n
  do_test autovacuum-ioerr2-1.$n.3 {
    set r [catch {db eval {
      CREATE TABLE abc2(a);
      BEGIN;
      DELETE FROM abc;
      INSERT INTO abc VALUES(randstr(1500,1500));
      CREATE TABLE abc3(a);
      COMMIT;
    }} msg]
    set ::go [expr {$::sqlite_io_error_pending<=0}]
    expr {$::sqlite_io_error_pending>0 || $r!=0}
  } {1}
}
set ::sqlite_io_error_pending 0

set ::go 1
for {set n 1} {$go} {incr n} {
  do_test autovacuum-ioerr2-2.$n.1 {
    set ::sqlite_io_error_pending 0
    db close
    opendb
    execsql {
      PRAGMA cache_size = 10;
      BEGIN;
      CREATE TABLE abc(a);
      INSERT INTO abc VALUES(randstr(1100,1100)); -- Page 4 is overflow
      INSERT INTO abc VALUES(randstr(1100,1100)); -- Page 5 is overflow
    }
    for {set i 0} {$i<150} {incr i} {
      execsql {
        INSERT INTO abc VALUES(randstr(100,100)); 
      }
    }
    execsql COMMIT
    expr [file size test.db]/1024
  } {24}
  do_test autovacuum-ioerr2-2.$n.2 [subst {
    set ::sqlite_io_error_pending $n
  }] $n
  do_test autovacuum-ioerr2-2.$n.3 {
    set r [catch {db eval {
      BEGIN;
      DELETE FROM abc WHERE length(a)>100;
      UPDATE abc SET a = randstr(90,90);
      CREATE TABLE abc3(a);
      COMMIT;
    }} msg]
    set ::go [expr {$::sqlite_io_error_pending<=0}]
    expr {$::sqlite_io_error_pending>0 || $r!=0}
  } {1}
}
set ::sqlite_io_error_pending 0

set ::go 1
for {set n 1} {$go} {incr n} {
  do_test autovacuum-ioerr2-3.$n.1 {
    set ::sqlite_io_error_pending 0
    db close
    opendb
    execsql {
      CREATE TABLE abc(a);
      CREATE TABLE abc2(b);
    }
  } {}
  do_test autovacuum-ioerr2-3.$n.2 [subst {
    set ::sqlite_io_error_pending $n
  }] $n
  do_test autovacuum-ioerr2-3.$n.3 {
    set r [catch {db eval {
      BEGIN;
      INSERT INTO abc2 VALUES(10);
      DROP TABLE abc;
      COMMIT;
      DROP TABLE abc2;
    }} msg]
    set ::go [expr {$::sqlite_io_error_pending<=0}]
    expr {$::sqlite_io_error_pending>0 || $r!=0}
  } {1}
}
set ::sqlite_io_error_pending 0

do_test autovacuum-ioerr2.4.0 {
  db close
  opendb
  execsql {
    PRAGMA cache_size = 10;
    BEGIN;
    CREATE TABLE abc(a);
    INSERT INTO abc VALUES(randstr(1100,1100)); -- Page 4 is overflow
    INSERT INTO abc VALUES(randstr(1100,1100)); -- Page 5 is overflow
  }
  for {set i 0} {$i<2500} {incr i} {
    execsql {
      INSERT INTO abc VALUES(randstr(100,100)); 
    }
  }
  execsql COMMIT
  file copy -force test.db backup.db
} {}

proc opendb2 {} {
  catch {file delete -force test.db}
  catch {file delete -force test.db-journal}
  file copy backup.db test.db
  sqlite3 db test.db
  execsql {select * from sqlite_master}
  execsql {PRAGMA cache_size = 10}
  return ""
}

set ::go 1
for {set n 1} {$go} {incr n} {
  do_test autovacuum-ioerr2-4.$n.1 {
    set ::sqlite_io_error_pending 0
    db close
    opendb2
  } {}
  do_test autovacuum-ioerr2-4.$n.2 [subst {
    set ::sqlite_io_error_pending $n
  }] $n
  do_test autovacuum-ioerr2-4.$n.3 {
    set r [catch {db eval {
      BEGIN;
      DELETE FROM abc WHERE oid < 3;
      UPDATE abc SET a = randstr(100,100) WHERE oid > 2300;
      UPDATE abc SET a = randstr(1100,1100) WHERE oid = 
          (select max(oid) from abc);
      COMMIT;
    }} msg]
    set ::go [expr {$::sqlite_io_error_pending<=0}]
    expr {$::sqlite_io_error_pending>0 || $r!=0}
  } {1}
}
set ::sqlite_io_error_pending 0


rename opendb ""
db close
catch {file delete -force test.db}
catch {file delete -force test.db-journal}

finish_test


Changes to test/btree.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
193
194
195
196
197
198
199



200
201
202
203
204
205
206
207
208
209
210
...
274
275
276
277
278
279
280



281
282
283
284
285
286
287
288
289
290
291
...
485
486
487
488
489
490
491



492
493
494
495
496
497
498
499
500
501
502
....
1031
1032
1033
1034
1035
1036
1037
1038
1039


1040
1041
1042
1043
1044
1045
1046
....
1053
1054
1055
1056
1057
1058
1059
1060



















































































1061
1062
1063
1064
1065
1066
1067
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is btree database backend
#
# $Id: btree.test,v 1.34 2005/01/10 12:59:53 danielk1977 Exp $


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

ifcapable default_autovacuum {
  finish_test
................................................................................
do_test btree-3.20.1 {
  btree_next $::c1
  btree_key $::c1
} {0}
do_test btree-3.20.2 {
  btree_eof $::c1
} {1}



do_test btree-3.21 {
  set rc [catch {btree_data $::c1} res]
  lappend rc $res
} {1 SQLITE_INTERNAL}

# Commit the changes, reopen and reread the data
#
do_test btree-3.22 {
  set rc [catch {btree_close_cursor $::c1} msg]
  lappend rc $msg
} {0 {}}
................................................................................
do_test btree-3.38 {
  btree_data $::c1
} {6.00}
do_test btree-3.39 {
  btree_next $::c1
  btree_key $::c1
} {0}



do_test btree-3.40 {
  set rc [catch {btree_data $::c1} res]
  lappend rc $res
} {1 SQLITE_INTERNAL}
do_test btree-3.41 {
  lindex [btree_pager_stats $::b1] 1
} {1}


# Now try a delete
#
................................................................................
  lindex [btree_get_meta $::b1] 0
} {0}
do_test btree-6.9 {
  set ::c2 [btree_cursor $::b1 $::t2 1]
  lindex [btree_pager_stats $::b1] 1
} {2}




do_test btree-6.9.1 {
  btree_move_to $::c2 {}
  btree_key $::c2
} {}

# If we drop table 1 it just clears the table.  Table 1 always exists.
#
do_test btree-6.10 {
  btree_close_cursor $::c2
  btree_drop_table $::b1 1
  set ::c2 [btree_cursor $::b1 $::t2 1]
................................................................................
  btree_rollback $::b1
  lindex [btree_pager_stats $::b1] 1
} {0}
btree_pager_ref_dump $::b1

# Miscellaneous tests.
#
# btree-16.1 - Check that a statement cannot be started if a transaction is not
#              active.


do_test btree-16.1 {
  catch {btree_begin_statement $::b1} msg
  set msg
} SQLITE_ERROR

do_test btree-16.2 {
  btree_begin_transaction $::b1
................................................................................
  set ::c1 [btree_cursor $::b1 2 1]
  btree_first $::c1
} 0
do_test btree-16.4 {
  catch {btree_data $::c1 [expr [btree_payload_size $::c1] + 10]} msg
  set msg
} SQLITE_ERROR




















































































do_test btree-99.1 {
  btree_close $::b1
} {}
catch {unset data}
catch {unset key}

finish_test







|







 







>
>
>
|
|
|
|







 







>
>
>
|
|
|
|







 







>
>
>
|
|
|
|







 







|
|
>
>







 








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







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
...
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
...
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
....
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
....
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is btree database backend
#
# $Id: btree.test,v 1.35 2005/01/11 10:25:07 danielk1977 Exp $


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

ifcapable default_autovacuum {
  finish_test
................................................................................
do_test btree-3.20.1 {
  btree_next $::c1
  btree_key $::c1
} {0}
do_test btree-3.20.2 {
  btree_eof $::c1
} {1}
# This test case used to test that one couldn't request data from an 
# invalid cursor. That is now an assert()ed condition.
#
# do_test btree-3.21 {
#   set rc [catch {btree_data $::c1} res]
#   lappend rc $res
# } {1 SQLITE_INTERNAL}

# Commit the changes, reopen and reread the data
#
do_test btree-3.22 {
  set rc [catch {btree_close_cursor $::c1} msg]
  lappend rc $msg
} {0 {}}
................................................................................
do_test btree-3.38 {
  btree_data $::c1
} {6.00}
do_test btree-3.39 {
  btree_next $::c1
  btree_key $::c1
} {0}
# This test case used to test that requesting data from an invalid cursor
# returned SQLITE_INTERNAL. That is now an assert()ed condition.
#
# do_test btree-3.40 {
#   set rc [catch {btree_data $::c1} res]
#   lappend rc $res
# } {1 SQLITE_INTERNAL}
do_test btree-3.41 {
  lindex [btree_pager_stats $::b1] 1
} {1}


# Now try a delete
#
................................................................................
  lindex [btree_get_meta $::b1] 0
} {0}
do_test btree-6.9 {
  set ::c2 [btree_cursor $::b1 $::t2 1]
  lindex [btree_pager_stats $::b1] 1
} {2}

# This test case used to test that requesting the key from an invalid cursor
# returned an empty string.  But that is now an assert()ed condition.
#
# do_test btree-6.9.1 {
#   btree_move_to $::c2 {}
#   btree_key $::c2
# } {}

# If we drop table 1 it just clears the table.  Table 1 always exists.
#
do_test btree-6.10 {
  btree_close_cursor $::c2
  btree_drop_table $::b1 1
  set ::c2 [btree_cursor $::b1 $::t2 1]
................................................................................
  btree_rollback $::b1
  lindex [btree_pager_stats $::b1] 1
} {0}
btree_pager_ref_dump $::b1

# Miscellaneous tests.
#
# btree-16.1 - Check that a statement cannot be started if a transaction 
#              is not active.
# btree-16.2 - Check that it is an error to request more payload from a 
#              btree entry than the entry contains.
do_test btree-16.1 {
  catch {btree_begin_statement $::b1} msg
  set msg
} SQLITE_ERROR

do_test btree-16.2 {
  btree_begin_transaction $::b1
................................................................................
  set ::c1 [btree_cursor $::b1 2 1]
  btree_first $::c1
} 0
do_test btree-16.4 {
  catch {btree_data $::c1 [expr [btree_payload_size $::c1] + 10]} msg
  set msg
} SQLITE_ERROR

if {$tcl_platform(platform)=="unix"} {
  do_test btree-16.5 {
    btree_close $::b1
    set ::origperm [file attributes test1.bt -permissions]
    file attributes test1.bt -permissions o-w,g-w,a-w
    set ::b1 [btree_open test1.bt 2000 0]
    catch {btree_cursor $::b1 2 1} msg
    file attributes test1.bt -permissions $::origperm
    btree_close $::b1
    set ::b1 [btree_open test1.bt 2000 0]
    set msg
  } {SQLITE_READONLY}
}

do_test btree-16.6 {
  set ::c1 [btree_cursor $::b1 2 1]
  set ::c2 [btree_cursor $::b1 2 1]
  btree_begin_transaction $::b1
  for {set i 0} {$i<100} {incr i} {
    btree_insert $::c1 $i [string repeat helloworld 10]
  }
  btree_last $::c2
  btree_insert $::c1 100 [string repeat helloworld 10]
} {}

do_test btree-16.7 {
  btree_close_cursor $::c1
  btree_close_cursor $::c2
  btree_commit $::b1
  set ::c1 [btree_cursor $::b1 2 1]
  catch {btree_insert $::c1 101 helloworld} msg
  set msg
} {SQLITE_ERROR}
do_test btree-16.8 {
  btree_first $::c1
  catch {btree_delete $::c1} msg
  set msg
} {SQLITE_ERROR}
do_test btree-16.9 {
  btree_close_cursor $::c1
  btree_begin_transaction $::b1
  set ::c1 [btree_cursor $::b1 2 0]
  catch {btree_insert $::c1 101 helloworld} msg
  set msg
} {SQLITE_PERM}
do_test btree-16.10 {
  catch {btree_delete $::c1} msg
  set msg
} {SQLITE_PERM}
do_test btree-16.11 {
  btree_close_cursor $::c1
  set ::c2 [btree_cursor $::b1 2 1]
  set ::c1 [btree_cursor $::b1 2 0]
  catch {btree_insert $::c2 101 helloworld} msg
  set msg
} {SQLITE_LOCKED}
do_test btree-16.12 {
  btree_first $::c2
  catch {btree_delete $::c2} msg
  set msg
} {SQLITE_LOCKED}
do_test btree-16.13 {
  catch {btree_clear_table $::b1 2} msg
  set msg
} {SQLITE_LOCKED}
do_test btree-16.14 {
  btree_close_cursor $::c1
  btree_close_cursor $::c2
  btree_commit $::b1
  catch {btree_clear_table $::b1 2} msg
  set msg
} {SQLITE_ERROR}
do_test btree-16.15 {
  catch {btree_drop_table $::b1 2} msg
  set msg
} {SQLITE_ERROR}
do_test btree-16.16 {
  btree_begin_transaction $::b1
  set ::c1 [btree_cursor $::b1 2 0]
  catch {btree_drop_table $::b1 2} msg
  set msg
} {SQLITE_LOCKED}

do_test btree-99.1 {
  btree_close $::b1
} {}
catch {unset data}
catch {unset key}

finish_test

Changes to test/btree2.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
139
140
141
142
143
144
145


146

147
148


149

150
151
152
153
154
155
156
...
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
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is btree database backend
#
# $Id: btree2.test,v 1.13 2004/05/10 16:18:48 drh Exp $


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

if {[info commands btree_open]!=""} {

................................................................................
  btree_move_to $::c4 {}
  btree_move_to $::c2 N
  set N [btree_data $::c2]
  btree_move_to $::c2 L
  set L [btree_data $::c2]
  set LM1 [expr {$L-1}]
  for {set i 1} {$i<=$N} {incr i} {


    set key [btree_key $::c3]

    if {[scan $key %d k]<1} {set k 0}
    if {$k!=$i} {


      set key [btree_key $::c4]

      if {[scan $key %d k]<1} {set k 0}
      if {$k!=$i} {
        return "Key $i is missing from both foreground and background"
      }
      set data [btree_data $::c4]
      btree_next $::c4
    } else {
................................................................................
    }
    set data [make_payload $k $L $datalen]
    set basekey [format $format $k]
    if {[set c [btree_move_to $::c3 $basekey]]==0} {
      btree_delete $::c3
    } else {
      if {$c<0} {btree_next $::c3}

      if {[string match $basekey* [btree_key $::c3]]} {
        btree_delete $::c3

      }
    }
    if {[set c [btree_move_to $::c4 $basekey]]==0} {
      btree_delete $::c4
    } else {
      if {$c<0} {btree_next $::c4}

      if {[string match $basekey* [btree_key $::c4]]} {
        btree_delete $::c4
      }
    }



    if {[scan [btree_key $::c4] %d kx]<1} {set kx -1}

    if {$kx==$k} {
      btree_delete $::c4
    }
    # For debugging - change the "0" to "1" to integrity check after
    # every change.
    if 0 {
      incr chngcnt







|







 







>
>
|
>


>
>
|
>







 







>
|
|
>






>
|
|
|
|
>
>
>
|
>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
...
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
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is btree database backend
#
# $Id: btree2.test,v 1.14 2005/01/11 10:25:07 danielk1977 Exp $


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

if {[info commands btree_open]!=""} {

................................................................................
  btree_move_to $::c4 {}
  btree_move_to $::c2 N
  set N [btree_data $::c2]
  btree_move_to $::c2 L
  set L [btree_data $::c2]
  set LM1 [expr {$L-1}]
  for {set i 1} {$i<=$N} {incr i} {
    set key {}
    if {![btree_eof $::c3]} {
      set key [btree_key $::c3]
    }
    if {[scan $key %d k]<1} {set k 0}
    if {$k!=$i} {
      set key {}
      if {![btree_eof $::c4]} {
        set key [btree_key $::c4]
      }
      if {[scan $key %d k]<1} {set k 0}
      if {$k!=$i} {
        return "Key $i is missing from both foreground and background"
      }
      set data [btree_data $::c4]
      btree_next $::c4
    } else {
................................................................................
    }
    set data [make_payload $k $L $datalen]
    set basekey [format $format $k]
    if {[set c [btree_move_to $::c3 $basekey]]==0} {
      btree_delete $::c3
    } else {
      if {$c<0} {btree_next $::c3}
      if {![btree_eof $::c3]} {
        if {[string match $basekey* [btree_key $::c3]]} {
          btree_delete $::c3
        }
      }
    }
    if {[set c [btree_move_to $::c4 $basekey]]==0} {
      btree_delete $::c4
    } else {
      if {$c<0} {btree_next $::c4}
      if {![btree_eof $::c4]} {
        if {[string match $basekey* [btree_key $::c4]]} {
          btree_delete $::c4
        }
      }
    }
    set kx -1
    if {![btree_eof $::c4]} {
      if {[scan [btree_key $::c4] %d kx]<1} {set kx -1}
    }
    if {$kx==$k} {
      btree_delete $::c4
    }
    # For debugging - change the "0" to "1" to integrity check after
    # every change.
    if 0 {
      incr chngcnt

Added test/corrupt2.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
# 2004 August 30
#
# 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.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# This file implements tests to make sure SQLite does not crash or
# segfault if it sees a corrupt database file.
#
# $Id: corrupt2.test,v 1.1 2005/01/11 10:25:07 danielk1977 Exp $

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

# The following tests - corrupt2-1.* - create some databases corrupted in
# specific ways and ensure that SQLite detects them as corrupt.
#
do_test corrupt2-1.1 {
  execsql {
    CREATE TABLE abc(a, b, c);
  }
} {}

do_test corrupt2-1.2 {

  # Corrupt the 16 byte magic string at the start of the file
  file delete -force corrupt.db
  file delete -force corrupt.db-journal
  file copy test.db corrupt.db
  set f [open corrupt.db a]
  seek $f 8 start
  puts $f blah
  close $f

  sqlite3 db2 corrupt.db
  catchsql {
    SELECT * FROM sqlite_master;
  } db2
} {1 {file is encrypted or is not a database}}

do_test corrupt2-1.3 {
  db2 close

  # Corrupt the page-size (bytes 16 and 17 of page 1).
  file delete -force corrupt.db
  file delete -force corrupt.db-journal
  file copy test.db corrupt.db
  set f [open corrupt.db a]
  fconfigure $f -encoding binary
  seek $f 16 start
  puts -nonewline $f "\x00\xFF"
  close $f

  sqlite3 db2 corrupt.db
  catchsql {
    SELECT * FROM sqlite_master;
  } db2
} {1 {file is encrypted or is not a database}}

do_test corrupt2-1.4 {
  db2 close

  # Corrupt the free-block list on page 1.
  file delete -force corrupt.db
  file delete -force corrupt.db-journal
  file copy test.db corrupt.db
  set f [open corrupt.db a]
  fconfigure $f -encoding binary
  seek $f 101 start
  puts -nonewline $f "\xFF\xFF"
  close $f

  sqlite3 db2 corrupt.db
  catchsql {
    SELECT * FROM sqlite_master;
  } db2
} {1 {database disk image is malformed}}

do_test corrupt2-1.5 {
  db2 close

  # Corrupt the free-block list on page 1.
  file delete -force corrupt.db
  file delete -force corrupt.db-journal
  file copy test.db corrupt.db
  set f [open corrupt.db a]
  fconfigure $f -encoding binary
  seek $f 101 start
  puts -nonewline $f "\x00\xC8"
  seek $f 200 start
  puts -nonewline $f "\x00\x00"
  puts -nonewline $f "\x10\x00"
  close $f

  sqlite3 db2 corrupt.db
  catchsql {
    SELECT * FROM sqlite_master;
  } db2
} {1 {database disk image is malformed}}
db2 close

finish_test

Changes to test/ioerr.test.

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
...
128
129
130
131
132
133
134
135
136
137







































138
# This file implements regression tests for SQLite library.  The
# focus of this file is testing for correct handling of I/O errors
# such as writes failing because the disk is full.
# 
# The tests in this file use special facilities that are only
# available in the SQLite test fixture.
#
# $Id: ioerr.test,v 1.8 2005/01/10 12:59:53 danielk1977 Exp $

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

set ::AV [execsql {pragma auto_vacuum}]

set ::go 1
................................................................................
    set ::go [expr {$::sqlite_io_error_pending<=0}]
    expr {$::sqlite_io_error_pending>0 || $r!=0}
    set ::sqlite_io_error_pending 0
    db close
    sqlite3 db test.db
    cksum
  } $cksum
}
set ::sqlite_io_error_pending 0








































finish_test







|







 










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

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
...
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
# This file implements regression tests for SQLite library.  The
# focus of this file is testing for correct handling of I/O errors
# such as writes failing because the disk is full.
# 
# The tests in this file use special facilities that are only
# available in the SQLite test fixture.
#
# $Id: ioerr.test,v 1.9 2005/01/11 10:25:07 danielk1977 Exp $

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

set ::AV [execsql {pragma auto_vacuum}]

set ::go 1
................................................................................
    set ::go [expr {$::sqlite_io_error_pending<=0}]
    expr {$::sqlite_io_error_pending>0 || $r!=0}
    set ::sqlite_io_error_pending 0
    db close
    sqlite3 db test.db
    cksum
  } $cksum
}
set ::sqlite_io_error_pending 0

set ::go 1
for {set n 1} {$go} {incr n} {
  do_test ioerr-3.$n.1 {
    set ::sqlite_io_error_pending 0
    db close
    catch {file delete -force test.db}
    catch {file delete -force test.db-journal}
    sqlite3 db test.db
    execsql {
      PRAGMA cache_size = 10;
      BEGIN;
      CREATE TABLE abc(a);
      INSERT INTO abc VALUES(randstr(1500,1500)); -- Page 4 is overflow
    }
    for {set i 0} {$i<150} {incr i} {
      execsql {
        INSERT INTO abc VALUES(randstr(100,100)); 
      }
    }
    execsql COMMIT
  } {}
  do_test ioerr-3.$n.2 [subst {
    set ::sqlite_io_error_pending $n
  }] $n
  do_test ioerr-3.$n.3 {
    set r [catch {db eval {
      CREATE TABLE abc2(a);
      BEGIN;
      DELETE FROM abc WHERE length(a)>100;
      UPDATE abc SET a = randstr(90,90);
      COMMIT;
      CREATE TABLE abc3(a);
    }} msg]
    set ::go [expr {$::sqlite_io_error_pending<=0}]
    expr {$::sqlite_io_error_pending>0 || $r!=0}
  } {1}
}
set ::sqlite_io_error_pending 0

finish_test