/ Check-in [8d3e8793]
Login

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

Overview
Comment:Add tests to insure VACUUM works in the presence of I/O errors. Fix some problems that came to light by these tests. (CVS 935)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 8d3e879349fc9523c72cb46111e0058b57ce9341
User & Date: drh 2003-04-25 15:37:58
Context
2003-04-25
17:52
Report the correct authorization context in the authorization callback when coding an INSTEAD OF trigger on an update or delete. (CVS 936) check-in: 67746833 user: drh tags: trunk
15:37
Add tests to insure VACUUM works in the presence of I/O errors. Fix some problems that came to light by these tests. (CVS 935) check-in: 8d3e8793 user: drh tags: trunk
13:28
Work around a name collision problem on windows. (CVS 934) check-in: c3b1f84d 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
....
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507

3508
3509
3510
3511
3512
3513

3514
3515









3516

3517
3518
3519
3520
3521
3522
3523
** 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.91 2003/04/25 13:22:52 drh 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.
................................................................................
** must be active for both files.
**
** The size of file pBtFrom may be reduced by this operation.
** If anything goes wrong, the transaction on pBtFrom is rolled back.
*/
static int fileBtreeCopyFile(Btree *pBtTo, Btree *pBtFrom){
  int rc = SQLITE_OK;
  Pgno i, nPage;

  if( !pBtTo->inTrans || !pBtFrom->inTrans ) return SQLITE_ERROR;
  if( pBtTo->needSwab!=pBtFrom->needSwab ) return SQLITE_ERROR;
  if( pBtTo->pCursor ) return SQLITE_BUSY;
  memcpy(pBtTo->page1, pBtFrom->page1, SQLITE_PAGE_SIZE);
  sqlitepager_overwrite(pBtTo->pPager, 1, pBtFrom->page1);

  nPage = sqlitepager_pagecount(pBtFrom->pPager);
  for(i=2; i<=nPage; i++){
    void *pPage;
    rc = sqlitepager_get(pBtFrom->pPager, i, &pPage);
    if( rc ) break;
    sqlitepager_overwrite(pBtTo->pPager, i, pPage);

    sqlitepager_unref(pPage);
  }









  if( !rc ) rc = sqlitepager_truncate(pBtTo->pPager, nPage);

  if( rc ){
    fileBtreeRollback(pBtTo);
  }
  return rc;  
}

/*







|







 







|





|
>

|



|
>


>
>
>
>
>
>
>
>
>
|
>







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
** 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.92 2003/04/25 15:37:58 drh 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.
................................................................................
** must be active for both files.
**
** The size of file pBtFrom may be reduced by this operation.
** If anything goes wrong, the transaction on pBtFrom is rolled back.
*/
static int fileBtreeCopyFile(Btree *pBtTo, Btree *pBtFrom){
  int rc = SQLITE_OK;
  Pgno i, nPage, nToPage;

  if( !pBtTo->inTrans || !pBtFrom->inTrans ) return SQLITE_ERROR;
  if( pBtTo->needSwab!=pBtFrom->needSwab ) return SQLITE_ERROR;
  if( pBtTo->pCursor ) return SQLITE_BUSY;
  memcpy(pBtTo->page1, pBtFrom->page1, SQLITE_PAGE_SIZE);
  rc = sqlitepager_overwrite(pBtTo->pPager, 1, pBtFrom->page1);
  nToPage = sqlitepager_pagecount(pBtTo->pPager);
  nPage = sqlitepager_pagecount(pBtFrom->pPager);
  for(i=2; rc==SQLITE_OK && i<=nPage; i++){
    void *pPage;
    rc = sqlitepager_get(pBtFrom->pPager, i, &pPage);
    if( rc ) break;
    rc = sqlitepager_overwrite(pBtTo->pPager, i, pPage);
    if( rc ) break;
    sqlitepager_unref(pPage);
  }
  for(i=nPage+1; rc==SQLITE_OK && i<=nToPage; i++){
    void *pPage;
    rc = sqlitepager_get(pBtTo->pPager, i, &pPage);
    if( rc ) break;
    rc = sqlitepager_write(pPage);
    sqlitepager_unref(pPage);
    sqlitepager_dont_write(pBtTo->pPager, i);
  }
  if( !rc && nPage<nToPage ){
    rc = sqlitepager_truncate(pBtTo->pPager, nPage);
  }
  if( rc ){
    fileBtreeRollback(pBtTo);
  }
  return rc;  
}

/*

Changes to src/pager.c.

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

940





941
942
943
944
945
946
947
....
1193
1194
1195
1196
1197
1198
1199

1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
....
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
....
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
....
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
....
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
....
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
....
1387
1388
1389
1390
1391
1392
1393



1394





1395
1396
1397
1398
1399
1400
1401
1402
1403
1404

1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
....
1528
1529
1530
1531
1532
1533
1534




1535
1536
1537
1538
1539
1540
1541
** 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.82 2003/04/25 13:22:53 drh Exp $
*/
#include "os.h"         /* Must be first to enable large file support */
#include "sqliteInt.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

................................................................................
static int syncAllPages(Pager*);

/*
** Truncate the file to the number of pages specified.
*/
int sqlitepager_truncate(Pager *pPager, Pgno nPage){
  int rc;

  if( pPager->dbSize<0 ) sqlitepager_pagecount(pPager);





  if( nPage>=pPager->dbSize ){
    return SQLITE_OK;
  }
  syncAllPages(pPager);
  rc = sqliteOsTruncate(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)nPage);
  if( rc==SQLITE_OK ){
    pPager->dbSize = nPage;
................................................................................
  PgHdr *pPg;
  int rc;

  /* Make sure we have not hit any critical errors.
  */ 
  assert( pPager!=0 );
  assert( pgno!=0 );

  if( pPager->errMask & ~(PAGER_ERR_FULL) ){
    return pager_errcode(pPager);
  }

  /* If this is the first page accessed, then get a read lock
  ** on the database file.
  */
  if( pPager->nRef==0 ){
    rc = sqliteOsReadLock(&pPager->fd);
    if( rc!=SQLITE_OK ){
      *ppPage = 0;
      return rc;
    }
    pPager->state = SQLITE_READLOCK;

    /* If a journal file exists, try to play it back.
    */
    if( pPager->useJournal && sqliteOsFileExists(pPager->zJournal) ){
................................................................................
       */
       rc = sqliteOsWriteLock(&pPager->fd);
       if( rc!=SQLITE_OK ){
         if( sqliteOsUnlock(&pPager->fd)!=SQLITE_OK ){
           /* This should never happen! */
           rc = SQLITE_INTERNAL;
         }
         *ppPage = 0;
         return rc;
       }
       pPager->state = SQLITE_WRITELOCK;

       /* Open the journal for exclusive access.  Return SQLITE_BUSY if
       ** we cannot get exclusive access to the journal file. 
       **
................................................................................
       ** we have to open the journal for writing in order to obtain an
       ** exclusive access lock.
       */
       rc = sqliteOsOpenReadWrite(pPager->zJournal, &pPager->jfd, &dummy);
       if( rc!=SQLITE_OK ){
         rc = sqliteOsUnlock(&pPager->fd);
         assert( rc==SQLITE_OK );
         *ppPage = 0;
         return SQLITE_BUSY;
       }
       pPager->journalOpen = 1;
       pPager->journalStarted = 0;

       /* Playback and delete the journal.  Drop the database write
       ** lock and reacquire the read lock.
................................................................................
    int h;
    pPager->nMiss++;
    if( pPager->nPage<pPager->mxPage || pPager->pFirst==0 ){
      /* Create a new page */
      pPg = sqliteMallocRaw( sizeof(*pPg) + SQLITE_PAGE_SIZE 
                              + sizeof(u32) + pPager->nExtra );
      if( pPg==0 ){
        *ppPage = 0;
        pager_unwritelock(pPager);
        pPager->errMask |= PAGER_ERR_MEM;
        return SQLITE_NOMEM;
      }
      memset(pPg, 0, sizeof(*pPg));
      pPg->pPager = pPager;
      pPg->pNextAll = pPager->pAll;
................................................................................
      ** very slow operation, so we work hard to avoid it.  But sometimes
      ** it can't be helped.
      */
      if( pPg==0 ){
        int rc = syncAllPages(pPager);
        if( rc!=0 ){
          sqlitepager_rollback(pPager);
          *ppPage = 0;
          return SQLITE_IOERR;
        }
        pPg = pPager->pFirst;
      }
      assert( pPg->nRef==0 );

      /* Write the page to the database file if it is dirty.
................................................................................
      */
      if( pPg->dirty ){
        assert( pPg->needSync==0 );
        pPg->pDirty = 0;
        rc = pager_write_pagelist( pPg );
        if( rc!=SQLITE_OK ){
          sqlitepager_rollback(pPager);
          *ppPage = 0;
          return SQLITE_IOERR;
        }
      }
      assert( pPg->dirty==0 );

      /* If the page we are recycling is marked as alwaysRollback, then
      ** set the global alwaysRollback flag, thus disabling the
................................................................................
    h = pager_hash(pgno);
    pPg->pNextHash = pPager->aHash[h];
    pPager->aHash[h] = pPg;
    if( pPg->pNextHash ){
      assert( pPg->pNextHash->pPrevHash==0 );
      pPg->pNextHash->pPrevHash = pPg;
    }



    if( pPager->dbSize<0 ) sqlitepager_pagecount(pPager);





    if( pPager->dbSize<(int)pgno ){
      memset(PGHDR_TO_DATA(pPg), 0, SQLITE_PAGE_SIZE);
    }else{
      int rc;
      sqliteOsSeek(&pPager->fd, (pgno-1)*(off_t)SQLITE_PAGE_SIZE);
      rc = sqliteOsRead(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
      if( rc!=SQLITE_OK ){
        off_t fileSize;
        if( sqliteOsFileSize(&pPager->fd,&fileSize)!=SQLITE_OK
               || fileSize>=pgno*SQLITE_PAGE_SIZE ){

          return rc;
        }else{
          memset(PGHDR_TO_DATA(pPg), 0, SQLITE_PAGE_SIZE);
        }
      }
    }
    if( pPager->nExtra>0 ){
      memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra);
    }
  }else{
    /* The requested page is in the page cache. */
    pPager->nHit++;
    page_ref(pPg);
  }
  *ppPage = PGHDR_TO_DATA(pPg);
  return SQLITE_OK;
................................................................................
  }
  pPager->journalOpen = 1;
  pPager->journalStarted = 0;
  pPager->needSync = 0;
  pPager->alwaysRollback = 0;
  pPager->nRec = 0;
  sqlitepager_pagecount(pPager);




  pPager->origDbSize = pPager->dbSize;
  if( journal_format==JOURNAL_FORMAT_3 ){
    rc = sqliteOsWrite(&pPager->jfd, aJournalMagic3, sizeof(aJournalMagic3));
    if( rc==SQLITE_OK ){
      rc = write32bits(&pPager->jfd, pPager->noSync ? 0xffffffff : 0);
    }
    if( rc==SQLITE_OK ){







|







 







>
|
>
>
>
>
>







 







>










<







 







<







 







<







 







<







 







<







 







<







 







>
>
>

>
>
>
>
>










>






<
<
<







 







>
>
>
>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
....
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216

1217
1218
1219
1220
1221
1222
1223
....
1227
1228
1229
1230
1231
1232
1233

1234
1235
1236
1237
1238
1239
1240
....
1242
1243
1244
1245
1246
1247
1248

1249
1250
1251
1252
1253
1254
1255
....
1269
1270
1271
1272
1273
1274
1275

1276
1277
1278
1279
1280
1281
1282
....
1297
1298
1299
1300
1301
1302
1303

1304
1305
1306
1307
1308
1309
1310
....
1311
1312
1313
1314
1315
1316
1317

1318
1319
1320
1321
1322
1323
1324
....
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420



1421
1422
1423
1424
1425
1426
1427
....
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
** 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.83 2003/04/25 15:37:58 drh Exp $
*/
#include "os.h"         /* Must be first to enable large file support */
#include "sqliteInt.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

................................................................................
static int syncAllPages(Pager*);

/*
** Truncate the file to the number of pages specified.
*/
int sqlitepager_truncate(Pager *pPager, Pgno nPage){
  int rc;
  if( pPager->dbSize<0 ){
    sqlitepager_pagecount(pPager);
  }
  if( pPager->errMask!=0 ){
    rc = pager_errcode(pPager);
    return rc;
  }
  if( nPage>=pPager->dbSize ){
    return SQLITE_OK;
  }
  syncAllPages(pPager);
  rc = sqliteOsTruncate(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)nPage);
  if( rc==SQLITE_OK ){
    pPager->dbSize = nPage;
................................................................................
  PgHdr *pPg;
  int rc;

  /* Make sure we have not hit any critical errors.
  */ 
  assert( pPager!=0 );
  assert( pgno!=0 );
  *ppPage = 0;
  if( pPager->errMask & ~(PAGER_ERR_FULL) ){
    return pager_errcode(pPager);
  }

  /* If this is the first page accessed, then get a read lock
  ** on the database file.
  */
  if( pPager->nRef==0 ){
    rc = sqliteOsReadLock(&pPager->fd);
    if( rc!=SQLITE_OK ){

      return rc;
    }
    pPager->state = SQLITE_READLOCK;

    /* If a journal file exists, try to play it back.
    */
    if( pPager->useJournal && sqliteOsFileExists(pPager->zJournal) ){
................................................................................
       */
       rc = sqliteOsWriteLock(&pPager->fd);
       if( rc!=SQLITE_OK ){
         if( sqliteOsUnlock(&pPager->fd)!=SQLITE_OK ){
           /* This should never happen! */
           rc = SQLITE_INTERNAL;
         }

         return rc;
       }
       pPager->state = SQLITE_WRITELOCK;

       /* Open the journal for exclusive access.  Return SQLITE_BUSY if
       ** we cannot get exclusive access to the journal file. 
       **
................................................................................
       ** we have to open the journal for writing in order to obtain an
       ** exclusive access lock.
       */
       rc = sqliteOsOpenReadWrite(pPager->zJournal, &pPager->jfd, &dummy);
       if( rc!=SQLITE_OK ){
         rc = sqliteOsUnlock(&pPager->fd);
         assert( rc==SQLITE_OK );

         return SQLITE_BUSY;
       }
       pPager->journalOpen = 1;
       pPager->journalStarted = 0;

       /* Playback and delete the journal.  Drop the database write
       ** lock and reacquire the read lock.
................................................................................
    int h;
    pPager->nMiss++;
    if( pPager->nPage<pPager->mxPage || pPager->pFirst==0 ){
      /* Create a new page */
      pPg = sqliteMallocRaw( sizeof(*pPg) + SQLITE_PAGE_SIZE 
                              + sizeof(u32) + pPager->nExtra );
      if( pPg==0 ){

        pager_unwritelock(pPager);
        pPager->errMask |= PAGER_ERR_MEM;
        return SQLITE_NOMEM;
      }
      memset(pPg, 0, sizeof(*pPg));
      pPg->pPager = pPager;
      pPg->pNextAll = pPager->pAll;
................................................................................
      ** very slow operation, so we work hard to avoid it.  But sometimes
      ** it can't be helped.
      */
      if( pPg==0 ){
        int rc = syncAllPages(pPager);
        if( rc!=0 ){
          sqlitepager_rollback(pPager);

          return SQLITE_IOERR;
        }
        pPg = pPager->pFirst;
      }
      assert( pPg->nRef==0 );

      /* Write the page to the database file if it is dirty.
................................................................................
      */
      if( pPg->dirty ){
        assert( pPg->needSync==0 );
        pPg->pDirty = 0;
        rc = pager_write_pagelist( pPg );
        if( rc!=SQLITE_OK ){
          sqlitepager_rollback(pPager);

          return SQLITE_IOERR;
        }
      }
      assert( pPg->dirty==0 );

      /* If the page we are recycling is marked as alwaysRollback, then
      ** set the global alwaysRollback flag, thus disabling the
................................................................................
    h = pager_hash(pgno);
    pPg->pNextHash = pPager->aHash[h];
    pPager->aHash[h] = pPg;
    if( pPg->pNextHash ){
      assert( pPg->pNextHash->pPrevHash==0 );
      pPg->pNextHash->pPrevHash = pPg;
    }
    if( pPager->nExtra>0 ){
      memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra);
    }
    if( pPager->dbSize<0 ) sqlitepager_pagecount(pPager);
    if( pPager->errMask!=0 ){
      sqlitepager_unref(PGHDR_TO_DATA(pPg));
      rc = pager_errcode(pPager);
      return rc;
    }
    if( pPager->dbSize<(int)pgno ){
      memset(PGHDR_TO_DATA(pPg), 0, SQLITE_PAGE_SIZE);
    }else{
      int rc;
      sqliteOsSeek(&pPager->fd, (pgno-1)*(off_t)SQLITE_PAGE_SIZE);
      rc = sqliteOsRead(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
      if( rc!=SQLITE_OK ){
        off_t fileSize;
        if( sqliteOsFileSize(&pPager->fd,&fileSize)!=SQLITE_OK
               || fileSize>=pgno*SQLITE_PAGE_SIZE ){
          sqlitepager_unref(PGHDR_TO_DATA(pPg));
          return rc;
        }else{
          memset(PGHDR_TO_DATA(pPg), 0, SQLITE_PAGE_SIZE);
        }
      }
    }



  }else{
    /* The requested page is in the page cache. */
    pPager->nHit++;
    page_ref(pPg);
  }
  *ppPage = PGHDR_TO_DATA(pPg);
  return SQLITE_OK;
................................................................................
  }
  pPager->journalOpen = 1;
  pPager->journalStarted = 0;
  pPager->needSync = 0;
  pPager->alwaysRollback = 0;
  pPager->nRec = 0;
  sqlitepager_pagecount(pPager);
  if( pPager->errMask!=0 ){
    rc = pager_errcode(pPager);
    return rc;
  }
  pPager->origDbSize = pPager->dbSize;
  if( journal_format==JOURNAL_FORMAT_3 ){
    rc = sqliteOsWrite(&pPager->jfd, aJournalMagic3, sizeof(aJournalMagic3));
    if( rc==SQLITE_OK ){
      rc = write32bits(&pPager->jfd, pPager->noSync ? 0xffffffff : 0);
    }
    if( rc==SQLITE_OK ){

Changes to src/vacuum.c.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291

292
293
294
295
296
297
298
299
300
301
302

303
304

305
306



307


308
309
310
311
312
313
314
315
316
317
318
319
320
**
*************************************************************************
** This file contains code used to implement the VACUUM command.
**
** Most of the code in this file may be omitted by defining the
** SQLITE_OMIT_VACUUM macro.
**
** $Id: vacuum.c,v 1.5 2003/04/25 13:22:53 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"

#define SQLITE_OMIT_VACUUM 1

/*
................................................................................
  if( execsql(pParse, db, "BEGIN") ) goto end_of_vacuum;
  if( execsql(pParse, dbNew, "PRAGMA synchronous=off; BEGIN") ){
    goto end_of_vacuum;
  }
  sVac.dbOld = db;
  sVac.dbNew = dbNew;
  sVac.pParse = pParse;
  for(i=0; i<sizeof(zPragma)/sizeof(zPragma[0]); i++){
    char zBuf[200];
    assert( strlen(zPragma[i])<100 );
    sprintf(zBuf, "PRAGMA %s;", zPragma[i]);
    sVac.zPragma = zPragma[i];
    rc = sqlite_exec(db, zBuf, vacuumCallback3, &sVac, &zErrMsg);
    if( rc ) goto vacuum_error;
  }
  if( rc==SQLITE_OK ){

    rc = sqlite_exec(db, "SELECT type, name, sql FROM sqlite_master "
             "WHERE sql NOT NULL", vacuumCallback1, &sVac, &zErrMsg);
  }
  if( rc ){
    if( pParse->zErrMsg==0 ){
      sqliteErrorMsg(pParse, "unable to vacuum database - %s", zErrMsg);
    }
    goto end_of_vacuum;
  }
  rc = sqliteBtreeCopyFile(db->aDb[0].pBt, dbNew->aDb[0].pBt);
  sqlite_exec(db, "COMMIT", 0, 0, 0);

  sqliteResetInternalSchema(db, 0);


end_of_vacuum:
  sqlite_exec(db, "COMMIT", 0, 0, 0);



  if( safety ) {


    sqliteSafetyOn(db);
  }
  if( dbNew ) sqlite_close(dbNew);
  sqliteOsDelete(zTemp);
  sqliteFree(zTemp);
  sqliteFree(sVac.s1.z);
  sqliteFree(sVac.s2.z);
  if( zErrMsg ) sqlite_freemem(zErrMsg);
  return;

vacuum_error:
#endif
}







|







 







|





<

<
>



|
<
<
<
<
<
|
|
>
|
|
>

<
>
>
>

>
>









<
<


10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
276
277
278
279
280
281
282
283
284
285
286
287
288

289

290
291
292
293
294





295
296
297
298
299
300
301

302
303
304
305
306
307
308
309
310
311
312
313
314
315
316


317
318
**
*************************************************************************
** This file contains code used to implement the VACUUM command.
**
** Most of the code in this file may be omitted by defining the
** SQLITE_OMIT_VACUUM macro.
**
** $Id: vacuum.c,v 1.6 2003/04/25 15:37:58 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"

#define SQLITE_OMIT_VACUUM 1

/*
................................................................................
  if( execsql(pParse, db, "BEGIN") ) goto end_of_vacuum;
  if( execsql(pParse, dbNew, "PRAGMA synchronous=off; BEGIN") ){
    goto end_of_vacuum;
  }
  sVac.dbOld = db;
  sVac.dbNew = dbNew;
  sVac.pParse = pParse;
  for(i=0; rc==SQLITE_OK && i<sizeof(zPragma)/sizeof(zPragma[0]); i++){
    char zBuf[200];
    assert( strlen(zPragma[i])<100 );
    sprintf(zBuf, "PRAGMA %s;", zPragma[i]);
    sVac.zPragma = zPragma[i];
    rc = sqlite_exec(db, zBuf, vacuumCallback3, &sVac, &zErrMsg);

  }

  if( !rc ){
    rc = sqlite_exec(db, "SELECT type, name, sql FROM sqlite_master "
             "WHERE sql NOT NULL", vacuumCallback1, &sVac, &zErrMsg);
  }
  if( !rc ){





    rc = sqliteBtreeCopyFile(db->aDb[0].pBt, dbNew->aDb[0].pBt);
    sqlite_exec(db, "COMMIT", 0, 0, 0);
    sqlite_exec(db, "ROLLBACK", 0, 0, 0);  /* In case the COMMIT failed */
    sqliteResetInternalSchema(db, 0);
  }

end_of_vacuum:

  if( rc && pParse->zErrMsg==0 && zErrMsg!=0 ){
    sqliteErrorMsg(pParse, "unable to vacuum database - %s", zErrMsg);
  }
  if( safety ) {
    sqlite_exec(db, "COMMIT", 0, 0, 0);
    sqlite_exec(db, "ROLLBACK", 0, 0, 0);  /* In case the COMMIT failed */
    sqliteSafetyOn(db);
  }
  if( dbNew ) sqlite_close(dbNew);
  sqliteOsDelete(zTemp);
  sqliteFree(zTemp);
  sqliteFree(sVac.s1.z);
  sqliteFree(sVac.s2.z);
  if( zErrMsg ) sqlite_freemem(zErrMsg);
  return;


#endif
}

Changes to src/vdbe.c.

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
....
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
....
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
**
** 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.219 2003/04/23 12:25:25 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** The makefile scans this source file and creates the following
** array of string constants which are the names of all VDBE opcodes.
................................................................................
  Cursor *pC;
  BtCursor *pCrsr;

  assert( i>=0 && i<p->nCursor );
  pC = &p->aCsr[i];
  if( (pCrsr = pC->pCursor)!=0 ){
    int res;
    sqliteBtreeLast(pCrsr, &res);
    p->aCsr[i].nullRow = res;
    if( res && pOp->p2>0 ){
      pc = pOp->p2 - 1;
    }
  }else{
    pC->nullRow = 0;
  }
................................................................................
  Cursor *pC;
  BtCursor *pCrsr;

  assert( i>=0 && i<p->nCursor );
  pC = &p->aCsr[i];
  if( (pCrsr = pC->pCursor)!=0 ){
    int res;
    sqliteBtreeFirst(pCrsr, &res);
    pC->atFirst = res==0;
    pC->nullRow = res;
    if( res && pOp->p2>0 ){
      pc = pOp->p2 - 1;
    }
  }else{
    pC->nullRow = 0;







|







 







|







 







|







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
....
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
....
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
**
** 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.220 2003/04/25 15:37:58 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** The makefile scans this source file and creates the following
** array of string constants which are the names of all VDBE opcodes.
................................................................................
  Cursor *pC;
  BtCursor *pCrsr;

  assert( i>=0 && i<p->nCursor );
  pC = &p->aCsr[i];
  if( (pCrsr = pC->pCursor)!=0 ){
    int res;
    rc = sqliteBtreeLast(pCrsr, &res);
    p->aCsr[i].nullRow = res;
    if( res && pOp->p2>0 ){
      pc = pOp->p2 - 1;
    }
  }else{
    pC->nullRow = 0;
  }
................................................................................
  Cursor *pC;
  BtCursor *pCrsr;

  assert( i>=0 && i<p->nCursor );
  pC = &p->aCsr[i];
  if( (pCrsr = pC->pCursor)!=0 ){
    int res;
    rc = sqliteBtreeFirst(pCrsr, &res);
    pC->atFirst = res==0;
    pC->nullRow = res;
    if( res && pOp->p2>0 ){
      pc = pOp->p2 - 1;
    }
  }else{
    pC->nullRow = 0;

Changes to test/ioerr.test.

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
..
52
53
54
55
56
57
58




59
























































60
# 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.2 2003/02/16 22:21:33 drh Exp $

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

set ::go 1
for {set n 1} {$go} {incr n} {
  do_test ioerr-1.$n.1 {
................................................................................
    # if {$r} {puts $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







|







 







>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
..
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
# 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.3 2003/04/25 15:37:59 drh Exp $

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

set ::go 1
for {set n 1} {$go} {incr n} {
  do_test ioerr-1.$n.1 {
................................................................................
    # if {$r} {puts $msg}
    set ::go [expr {$::sqlite_io_error_pending<=0}]
    expr {$::sqlite_io_error_pending>0 || $r!=0}
  } {1}
}
set ::sqlite_io_error_pending 0

proc cksum {{db db}} {
  set txt [$db eval {SELECT name, type, sql FROM sqlite_master}]\n
  foreach tbl [$db eval {SELECT name FROM sqlite_master WHERE type='table'}] {
    append txt [$db eval "SELECT * FROM $tbl"]\n
  }
  foreach prag {default_synchronous default_cache_size} {
    append txt $prag-[$db eval "PRAGMA $prag"]\n
  }
  set cksum [string length $txt]-[md5 $txt]
  # puts $cksum-[file size test.db]
  return $cksum
}

set ::go 1
for {set n 1} {$go} {incr n} {
  do_test ioerr-2.$n.1 {
    set ::sqlite_io_error_pending 0
    db close
    catch {file delete -force test.db}
    catch {file delete -force test.db-journal}
    sqlite db test.db
    execsql {
      BEGIN;
      CREATE TABLE t1(a, b, c);
      INSERT INTO t1 VALUES(1, randstr(5,50), randstr(5,50));
      INSERT INTO t1 SELECT a+2, b||'-'||rowid, c||'-'||rowid FROM t1;
      INSERT INTO t1 SELECT a+4, b||'-'||rowid, c||'-'||rowid FROM t1;
      INSERT INTO t1 SELECT a+8, b||'-'||rowid, c||'-'||rowid FROM t1;
      INSERT INTO t1 SELECT a+16, b||'-'||rowid, c||'-'||rowid FROM t1;
      INSERT INTO t1 SELECT a+32, b||'-'||rowid, c||'-'||rowid FROM t1;
      INSERT INTO t1 SELECT a+64, b||'-'||rowid, c||'-'||rowid FROM t1;
      INSERT INTO t1 SELECT a+128, b||'-'||rowid, c||'-'||rowid FROM t1;
      CREATE TABLE t2 AS SELECT * FROM t1;
      CREATE TABLE t3 AS SELECT * FROM t1;
      COMMIT;
      DROP TABLE t2;
    }
    set ::cksum [cksum]
    execsql {
      SELECT name FROM sqlite_master WHERE type='table'
    }
  } {t1 t3}
  do_test ioerr-2.$n.2 [subst {
    set ::sqlite_io_error_pending $n
  }] $n
  do_test ioerr-2.$n.3 {
    set r [catch {db eval {
      VACUUM;
    }} msg]
    # puts "error_pending=$::sqlite_io_error_pending"
    # if {$r} {puts $msg}
    set ::go [expr {$::sqlite_io_error_pending<=0}]
    expr {$::sqlite_io_error_pending>0 || $r!=0}
    set ::sqlite_io_error_pending 0
    db close
    sqlite db test.db
    cksum
  } $cksum
}
set ::sqlite_io_error_pending 0

finish_test