SQLite

Check-in [8d3e879349]
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
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 8d3e879349fc9523c72cb46111e0058b57ce9341
User & Date: drh 2003-04-25 15:37:58.000
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: 67746833fc 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: 8d3e879349 user: drh tags: trunk)
13:28
Work around a name collision problem on windows. (CVS 934) (check-in: c3b1f84dfc user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/btree.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
** 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.
**
*************************************************************************
** $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.











|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
** 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.
**
*************************************************************************
** $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.
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
** 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;  
}

/*







|





|
>

|



|
>


>
>
>
>
>
>
>
>
>
|
>







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








|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
** 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>

933
934
935
936
937
938
939
940






941
942
943
944
945
946
947
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;







|
>
>
>
>
>
>







933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
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;
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
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
  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) ){
       int rc, dummy;

       /* Get a write lock on the database
       */
       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. 
       **
       ** Even though we will only be reading from the journal, not writing,
       ** 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.







>










<

















<















<







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
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233

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

1249
1250
1251
1252
1253
1254
1255
  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) ){
       int rc, dummy;

       /* Get a write lock on the database
       */
       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. 
       **
       ** Even though we will only be reading from the journal, not writing,
       ** 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.
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
    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;







<







1269
1270
1271
1272
1273
1274
1275

1276
1277
1278
1279
1280
1281
1282
    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;
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
      ** 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







<














<







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







>
>
>

>
>
>
>
>










>






<
<
<







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
    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;
1528
1529
1530
1531
1532
1533
1534




1535
1536
1537
1538
1539
1540
1541
  }
  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 ){







>
>
>
>







1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
  }
  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
**
*************************************************************************
** 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

/*







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
**
*************************************************************************
** 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

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







|





<

|



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

>
|
>

>
>









<
<


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







|







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
**
** 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.
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
  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;
  }







|







4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
  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;
  }
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
  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;







|







4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
  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
# 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 {







|







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 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 {
52
53
54
55
56
57
58




59
























































60
    # 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







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

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
    # 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