/ Check-in [83d1eafb]
Login

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

Overview
Comment:Fix for 'truncate file' operations on in-memory databases. (CVS 6131)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 83d1eafbde556f56969c6f285b6767d2c658dbfc
User & Date: danielk1977 2009-01-07 15:18:21
Context
2009-01-07
15:33
Conjecture: a journal header with nRec==0 must be the last header in the journal. Add asserts to make this conjecture explicit. (CVS 6132) check-in: 15b5b5f9 user: drh tags: trunk
15:18
Fix for 'truncate file' operations on in-memory databases. (CVS 6131) check-in: 83d1eafb user: danielk1977 tags: trunk
10:52
Add a comment to the openSubjournal() function in pager.c. (CVS 6130) check-in: 04387ae1 user: danielk1977 tags: trunk
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
....
1082
1083
1084
1085
1086
1087
1088

1089
1090
1091
1092
1093
1094
1095
....
1458
1459
1460
1461
1462
1463
1464

1465
1466

1467
1468
1469
1470
1471
1472
1473
1474
1475
....
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
....
1664
1665
1666
1667
1668
1669
1670

1671
1672
1673
1674
1675
1676
1677
....
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
....
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
....
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
....
3981
3982
3983
3984
3985
3986
3987






3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
** 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.537 2009/01/07 10:52:56 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"

/*
** Macros for troubleshooting.  Normally turned off
*/
................................................................................
  }else if( pPager->state==PAGER_SYNCED ){
    pPager->state = PAGER_EXCLUSIVE;
  }
  pPager->dbOrigSize = 0;
  pPager->setMaster = 0;
  pPager->needSync = 0;
  /* lruListSetFirstSynced(pPager); */

  if( !MEMDB ){
    pPager->dbSizeValid = 0;
  }
  pPager->dbModified = 0;

  return (rc==SQLITE_OK?rc2:rc);
}
................................................................................
  }
  sqlite3_free(pMaster);
  return rc;
}


/*

** Truncate the main file of the given pager to the number of pages
** indicated. Also truncate the cached representation of the file.

**
** Might might be the case that the file on disk is smaller than nPage.
** This can happen, for example, if we are in the middle of a transaction
** which has extended the file size and the new pages are still all held
** in cache, then an INSERT or UPDATE does a statement rollback.  Some
** operating system implementations can get confused if you try to
** truncate a file to some size that is larger than it currently is,
** so detect this case and write a single zero byte to the end of the new
** file instead.
................................................................................
        rc = sqlite3OsWrite(pPager->fd, "", 1, newSize-1);
      }
      if( rc==SQLITE_OK ){
        pPager->dbFileSize = nPage;
      }
    }
  }
  if( rc==SQLITE_OK ){
    pPager->dbSize = nPage;
    sqlite3PcacheTruncate(pPager->pPCache, nPage);
  }
  return rc;
}

/*
** Set the sectorSize for the given pager.
**
** The sector size is at least as big as the sector size reported
................................................................................
    ** database file back to its original size.
    */
    if( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ){
      rc = pager_truncate(pPager, mxPg);
      if( rc!=SQLITE_OK ){
        goto end_playback;
      }

    }

    /* Copy original pages out of the journal and back into the database file.
    */
    for(u=0; u<nRec; u++){
      rc = pager_playback_one_page(pPager, 1, &pPager->journalOff, 0, 0);
      if( rc!=SQLITE_OK ){
................................................................................
      pPager->state = (u8)locktype;
      IOTRACE(("LOCK %p %d\n", pPager, locktype))
    }
  }
  return rc;
}

/*
** Truncate the file to the number of pages specified. 
**
** Unless an IO error occurs, this function is guaranteed to modify the 
** database file itself. If an exclusive lock is not held when this function
** is called, one is obtained before truncating the file.
*/
int sqlite3PagerTruncate(Pager *pPager, Pgno nPage){
  int rc = SQLITE_OK;
  assert( pPager->state>=PAGER_SHARED );

  sqlite3PagerPagecount(pPager, 0);
  if( pPager->errCode ){
    rc = pPager->errCode;
  }else if( nPage<pPager->dbFileSize ){
    rc = syncJournal(pPager);
    if( rc==SQLITE_OK ){
      /* Get an exclusive lock on the database before truncating. */
      rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
    }
    if( rc==SQLITE_OK ){
      rc = pager_truncate(pPager, nPage);
    }
  }

  return rc;
}

#ifndef SQLITE_OMIT_AUTOVACUUM
/*
** Truncate the in-memory database file image to nPage pages. Unlike
** sqlite3PagerTruncate(), this function does not actually modify the
** database file on disk. It just sets the internal state of the pager
** object so that the truncation will be done when the current 
** transaction is committed.
*/
void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){
  assert( pPager->dbSizeValid );
  assert( pPager->dbSize>=nPage );
  pPager->dbSize = nPage;
}

................................................................................
    if( !pPager->fd->pMethods ){
      assert(pPager->tempFile);
      rc = sqlite3PagerOpentemp(pPager, pPager->fd, pPager->vfsFlags);
      if( rc ) return rc;
    }

    /* If there are dirty pages in the page cache with page numbers greater
    ** than Pager.dbSize, this means sqlite3PagerTruncate() was called to
    ** make the file smaller (presumably by auto-vacuum code). Do not write
    ** any such pages to the file.
    */
    if( pList->pgno<=pPager->dbSize && 0==(pList->flags&PGHDR_DONT_WRITE) ){
      i64 offset = (pList->pgno-1)*(i64)pPager->pageSize;
      char *pData = CODEC2(pPager, pList->pData, pList->pgno, 6);

................................................................................
        rc = writeMasterJournal(pPager, zMaster);
        if( rc!=SQLITE_OK ) goto sync_exit;
        rc = syncJournal(pPager);
      }
    }
    if( rc!=SQLITE_OK ) goto sync_exit;

#ifndef SQLITE_OMIT_AUTOVACUUM
    if( pPager->dbSize<pPager->dbFileSize ){
      rc = sqlite3PagerTruncate(pPager, pPager->dbSize);
      if( rc!=SQLITE_OK ) goto sync_exit;
    }
#endif

    /* Write all dirty pages to the database file */
    pPg = sqlite3PcacheDirtyList(pPager->pPCache);
    rc = pager_write_pagelist(pPg);
    if( rc!=SQLITE_OK ){
      assert( rc!=SQLITE_IOERR_BLOCKED );
      /* The error might have left the dirty list all fouled up here,
      ** but that does not matter because if the if the dirty list did
................................................................................
      ** discard the dirty list.  There is an assert in
      ** pager_get_all_dirty_pages() that verifies that no attempt
      ** is made to use an invalid dirty list.
      */
      goto sync_exit;
    }
    sqlite3PcacheCleanAll(pPager->pPCache);







    /* Sync the database file. */
    if( !pPager->noSync && !noSync ){
      rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
    }
    IOTRACE(("DBSYNC %p\n", pPager))

    pPager->state = PAGER_SYNCED;
  }else if( MEMDB && pPager->dbSize<pPager->dbFileSize ){
    rc = sqlite3PagerTruncate(pPager, pPager->dbSize);
  }

sync_exit:
  if( rc==SQLITE_IOERR_BLOCKED ){
    /* pager_incr_changecounter() may attempt to obtain an exclusive
     * lock to spill the cache and return IOERR_BLOCKED. But since 
     * there is no chance the cache is inconsistent, it is







|







 







>







 







>
|
<
>

|







 







<
<
<
<







 







>







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


|
|
|
|
<







 







|







 







<
<
<
<
<
<
<







 







>
>
>
>
>
>








<
<







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
....
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
....
1459
1460
1461
1462
1463
1464
1465
1466
1467

1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
....
1489
1490
1491
1492
1493
1494
1495




1496
1497
1498
1499
1500
1501
1502
....
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
....
2344
2345
2346
2347
2348
2349
2350




























2351
2352
2353
2354
2355
2356

2357
2358
2359
2360
2361
2362
2363
....
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
....
3929
3930
3931
3932
3933
3934
3935







3936
3937
3938
3939
3940
3941
3942
....
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964


3965
3966
3967
3968
3969
3970
3971
** 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.538 2009/01/07 15:18:21 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"

/*
** Macros for troubleshooting.  Normally turned off
*/
................................................................................
  }else if( pPager->state==PAGER_SYNCED ){
    pPager->state = PAGER_EXCLUSIVE;
  }
  pPager->dbOrigSize = 0;
  pPager->setMaster = 0;
  pPager->needSync = 0;
  /* lruListSetFirstSynced(pPager); */
  sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
  if( !MEMDB ){
    pPager->dbSizeValid = 0;
  }
  pPager->dbModified = 0;

  return (rc==SQLITE_OK?rc2:rc);
}
................................................................................
  }
  sqlite3_free(pMaster);
  return rc;
}


/*
** If the main database file is open and an exclusive lock is held, 
** truncate the main file of the given pager to the specified number 

** of pages.
**
** It might might be the case that the file on disk is smaller than nPage.
** This can happen, for example, if we are in the middle of a transaction
** which has extended the file size and the new pages are still all held
** in cache, then an INSERT or UPDATE does a statement rollback.  Some
** operating system implementations can get confused if you try to
** truncate a file to some size that is larger than it currently is,
** so detect this case and write a single zero byte to the end of the new
** file instead.
................................................................................
        rc = sqlite3OsWrite(pPager->fd, "", 1, newSize-1);
      }
      if( rc==SQLITE_OK ){
        pPager->dbFileSize = nPage;
      }
    }
  }




  return rc;
}

/*
** Set the sectorSize for the given pager.
**
** The sector size is at least as big as the sector size reported
................................................................................
    ** database file back to its original size.
    */
    if( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ){
      rc = pager_truncate(pPager, mxPg);
      if( rc!=SQLITE_OK ){
        goto end_playback;
      }
      pPager->dbSize = mxPg;
    }

    /* Copy original pages out of the journal and back into the database file.
    */
    for(u=0; u<nRec; u++){
      rc = pager_playback_one_page(pPager, 1, &pPager->journalOff, 0, 0);
      if( rc!=SQLITE_OK ){
................................................................................
      pPager->state = (u8)locktype;
      IOTRACE(("LOCK %p %d\n", pPager, locktype))
    }
  }
  return rc;
}





























#ifndef SQLITE_OMIT_AUTOVACUUM
/*
** Truncate the in-memory database file image to nPage pages. This 
** function does not actually modify the database file on disk. It 
** just sets the internal state of the pager object so that the 
** truncation will be done when the current transaction is committed.

*/
void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){
  assert( pPager->dbSizeValid );
  assert( pPager->dbSize>=nPage );
  pPager->dbSize = nPage;
}

................................................................................
    if( !pPager->fd->pMethods ){
      assert(pPager->tempFile);
      rc = sqlite3PagerOpentemp(pPager, pPager->fd, pPager->vfsFlags);
      if( rc ) return rc;
    }

    /* If there are dirty pages in the page cache with page numbers greater
    ** than Pager.dbSize, this means sqlite3PagerTruncateImage() was called to
    ** make the file smaller (presumably by auto-vacuum code). Do not write
    ** any such pages to the file.
    */
    if( pList->pgno<=pPager->dbSize && 0==(pList->flags&PGHDR_DONT_WRITE) ){
      i64 offset = (pList->pgno-1)*(i64)pPager->pageSize;
      char *pData = CODEC2(pPager, pList->pData, pList->pgno, 6);

................................................................................
        rc = writeMasterJournal(pPager, zMaster);
        if( rc!=SQLITE_OK ) goto sync_exit;
        rc = syncJournal(pPager);
      }
    }
    if( rc!=SQLITE_OK ) goto sync_exit;








    /* Write all dirty pages to the database file */
    pPg = sqlite3PcacheDirtyList(pPager->pPCache);
    rc = pager_write_pagelist(pPg);
    if( rc!=SQLITE_OK ){
      assert( rc!=SQLITE_IOERR_BLOCKED );
      /* The error might have left the dirty list all fouled up here,
      ** but that does not matter because if the if the dirty list did
................................................................................
      ** discard the dirty list.  There is an assert in
      ** pager_get_all_dirty_pages() that verifies that no attempt
      ** is made to use an invalid dirty list.
      */
      goto sync_exit;
    }
    sqlite3PcacheCleanAll(pPager->pPCache);

    if( pPager->dbSize<pPager->dbFileSize ){
      assert( pPager->state>=PAGER_EXCLUSIVE );
      rc = pager_truncate(pPager, pPager->dbSize);
      if( rc!=SQLITE_OK ) goto sync_exit;
    }

    /* Sync the database file. */
    if( !pPager->noSync && !noSync ){
      rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
    }
    IOTRACE(("DBSYNC %p\n", pPager))

    pPager->state = PAGER_SYNCED;


  }

sync_exit:
  if( rc==SQLITE_IOERR_BLOCKED ){
    /* pager_incr_changecounter() may attempt to obtain an exclusive
     * lock to spill the cache and return IOERR_BLOCKED. But since 
     * there is no chance the cache is inconsistent, it is

Changes to src/pager.h.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the sqlite page cache
** subsystem.  The page cache subsystem reads and writes a file a page
** at a time and provides a journal for rollback.
**
** @(#) $Id: pager.h,v 1.92 2009/01/06 14:19:37 drh Exp $
*/

#ifndef _PAGER_H_
#define _PAGER_H_

/*
** If defined as non-zero, auto-vacuum is enabled by default. Otherwise
................................................................................
#define sqlite3PagerGet(A,B,C) sqlite3PagerAcquire(A,B,C,0)
DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno);
int sqlite3PagerPageRefcount(DbPage*);
int sqlite3PagerRef(DbPage*);
int sqlite3PagerUnref(DbPage*);
int sqlite3PagerWrite(DbPage*);
int sqlite3PagerPagecount(Pager*, int*);
int sqlite3PagerTruncate(Pager*,Pgno);
int sqlite3PagerBegin(DbPage*, int exFlag);
int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int);
int sqlite3PagerCommitPhaseTwo(Pager*);
int sqlite3PagerRollback(Pager*);
u8 sqlite3PagerIsreadonly(Pager*);
void sqlite3PagerDontRollback(DbPage*);
int sqlite3PagerDontWrite(DbPage*);







|







 







<







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
84
85
86
87
88
89
90

91
92
93
94
95
96
97
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the sqlite page cache
** subsystem.  The page cache subsystem reads and writes a file a page
** at a time and provides a journal for rollback.
**
** @(#) $Id: pager.h,v 1.93 2009/01/07 15:18:21 danielk1977 Exp $
*/

#ifndef _PAGER_H_
#define _PAGER_H_

/*
** If defined as non-zero, auto-vacuum is enabled by default. Otherwise
................................................................................
#define sqlite3PagerGet(A,B,C) sqlite3PagerAcquire(A,B,C,0)
DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno);
int sqlite3PagerPageRefcount(DbPage*);
int sqlite3PagerRef(DbPage*);
int sqlite3PagerUnref(DbPage*);
int sqlite3PagerWrite(DbPage*);
int sqlite3PagerPagecount(Pager*, int*);

int sqlite3PagerBegin(DbPage*, int exFlag);
int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int);
int sqlite3PagerCommitPhaseTwo(Pager*);
int sqlite3PagerRollback(Pager*);
u8 sqlite3PagerIsreadonly(Pager*);
void sqlite3PagerDontRollback(DbPage*);
int sqlite3PagerDontWrite(DbPage*);

Changes to src/pcache1.c.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
..
43
44
45
46
47
48
49


50
51
52
53
54
55
56
...
553
554
555
556
557
558
559



560
561
562
563
564
565
566
...
627
628
629
630
631
632
633




634
635
636
637
638
639
640
...
641
642
643
644
645
646
647

648


649
650
651
652
653
654
655
**
** This file implements the default page cache implementation (the
** sqlite3_pcache interface). It also contains part of the implementation
** of the SQLITE_CONFIG_PAGECACHE and sqlite3_release_memory() features.
** If the default page cache implementation is overriden, then neither of
** these two features are available.
**
** @(#) $Id: pcache1.c,v 1.6 2008/12/10 18:03:46 drh Exp $
*/

#include "sqliteInt.h"

typedef struct PCache1 PCache1;
typedef struct PgHdr1 PgHdr1;
typedef struct PgFreeslot PgFreeslot;
................................................................................
  ** when the accessor is holding the global mutex (see pcache1EnterMutex() 
  ** and pcache1LeaveMutex()).
  */
  unsigned int nRecyclable;           /* Number of pages in the LRU list */
  unsigned int nPage;                 /* Total number of pages in apHash */
  unsigned int nHash;                 /* Number of slots in apHash[] */
  PgHdr1 **apHash;                    /* Hash table for fast lookup by key */


};

/*
** Each cache entry is represented by an instance of the following 
** structure. A buffer of PgHdr1.pCache->szPage bytes is allocated 
** directly after the structure in memory (see the PGHDR1_TO_PAGE() 
** macro below).
................................................................................
    pPage->iKey = iKey;
    pPage->pNext = pCache->apHash[h];
    pPage->pCache = pCache;
    pCache->apHash[h] = pPage;
  }

fetch_out:



  if( createFlag==1 ) sqlite3EndBenignMalloc();
  pcache1LeaveMutex();
  return (pPage ? PGHDR1_TO_PAGE(pPage) : 0);
}


/*
................................................................................
  }
  *pp = pPage->pNext;

  h = iNew%pCache->nHash;
  pPage->iKey = iNew;
  pPage->pNext = pCache->apHash[h];
  pCache->apHash[h] = pPage;





  pcache1LeaveMutex();
}

/*
** Implementation of the sqlite3_pcache.xTruncate method. 
**
................................................................................
** Discard all unpinned pages in the cache with a page number equal to
** or greater than parameter iLimit. Any pinned pages with a page number
** equal to or greater than iLimit are implicitly unpinned.
*/
static void pcache1Truncate(sqlite3_pcache *p, unsigned int iLimit){
  PCache1 *pCache = (PCache1 *)p;
  pcache1EnterMutex();

  pcache1TruncateUnsafe(pCache, iLimit);


  pcache1LeaveMutex();
}

/*
** Implementation of the sqlite3_pcache.xDestroy method. 
**
** Destroy a cache allocated using pcache1Create().







|







 







>
>







 







>
>
>







 







>
>
>
>







 







>
|
>
>







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
..
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
...
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
...
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
...
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
**
** This file implements the default page cache implementation (the
** sqlite3_pcache interface). It also contains part of the implementation
** of the SQLITE_CONFIG_PAGECACHE and sqlite3_release_memory() features.
** If the default page cache implementation is overriden, then neither of
** these two features are available.
**
** @(#) $Id: pcache1.c,v 1.7 2009/01/07 15:18:21 danielk1977 Exp $
*/

#include "sqliteInt.h"

typedef struct PCache1 PCache1;
typedef struct PgHdr1 PgHdr1;
typedef struct PgFreeslot PgFreeslot;
................................................................................
  ** when the accessor is holding the global mutex (see pcache1EnterMutex() 
  ** and pcache1LeaveMutex()).
  */
  unsigned int nRecyclable;           /* Number of pages in the LRU list */
  unsigned int nPage;                 /* Total number of pages in apHash */
  unsigned int nHash;                 /* Number of slots in apHash[] */
  PgHdr1 **apHash;                    /* Hash table for fast lookup by key */

  unsigned int iMaxKey;               /* Largest key seen since xTruncate() */
};

/*
** Each cache entry is represented by an instance of the following 
** structure. A buffer of PgHdr1.pCache->szPage bytes is allocated 
** directly after the structure in memory (see the PGHDR1_TO_PAGE() 
** macro below).
................................................................................
    pPage->iKey = iKey;
    pPage->pNext = pCache->apHash[h];
    pPage->pCache = pCache;
    pCache->apHash[h] = pPage;
  }

fetch_out:
  if( pPage && iKey>pCache->iMaxKey ){
    pCache->iMaxKey = iKey;
  }
  if( createFlag==1 ) sqlite3EndBenignMalloc();
  pcache1LeaveMutex();
  return (pPage ? PGHDR1_TO_PAGE(pPage) : 0);
}


/*
................................................................................
  }
  *pp = pPage->pNext;

  h = iNew%pCache->nHash;
  pPage->iKey = iNew;
  pPage->pNext = pCache->apHash[h];
  pCache->apHash[h] = pPage;

  if( iNew>pCache->iMaxKey ){
    pCache->iMaxKey = iNew;
  }

  pcache1LeaveMutex();
}

/*
** Implementation of the sqlite3_pcache.xTruncate method. 
**
................................................................................
** Discard all unpinned pages in the cache with a page number equal to
** or greater than parameter iLimit. Any pinned pages with a page number
** equal to or greater than iLimit are implicitly unpinned.
*/
static void pcache1Truncate(sqlite3_pcache *p, unsigned int iLimit){
  PCache1 *pCache = (PCache1 *)p;
  pcache1EnterMutex();
  if( iLimit<=pCache->iMaxKey ){
    pcache1TruncateUnsafe(pCache, iLimit);
    pCache->iMaxKey = iLimit-1;
  }
  pcache1LeaveMutex();
}

/*
** Implementation of the sqlite3_pcache.xDestroy method. 
**
** Destroy a cache allocated using pcache1Create().

Changes to src/test2.c.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the pager.c module in SQLite.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test2.c,v 1.64 2009/01/02 18:10:42 drh Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

................................................................................
static int pager_truncate(
  void *NotUsed,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  const char **argv      /* Text of each argument */
){
  Pager *pPager;
  int rc;
  int pgno;
  if( argc!=3 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " ID PGNO\"", 0);
    return TCL_ERROR;
  }
  pPager = sqlite3TestTextToPtr(argv[1]);
  if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
  rc = sqlite3PagerTruncate(pPager, pgno);
  if( rc!=SQLITE_OK ){
    Tcl_AppendResult(interp, errorName(rc), 0);
    return TCL_ERROR;
  }
  return TCL_OK;
}


/*
** Usage:   page_unref PAGE
**







|







 







<








|
<
<
<
<







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
389
390
391
392
393
394
395

396
397
398
399
400
401
402
403
404




405
406
407
408
409
410
411
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the pager.c module in SQLite.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test2.c,v 1.65 2009/01/07 15:18:21 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

................................................................................
static int pager_truncate(
  void *NotUsed,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  const char **argv      /* Text of each argument */
){
  Pager *pPager;

  int pgno;
  if( argc!=3 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " ID PGNO\"", 0);
    return TCL_ERROR;
  }
  pPager = sqlite3TestTextToPtr(argv[1]);
  if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
  sqlite3PagerTruncateImage(pPager, pgno);




  return TCL_OK;
}


/*
** Usage:   page_unref PAGE
**

Changes to test/memdb.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
406
407
408
409
410
411
412



















413
414
415
416
417
#    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 in-memory database backend.
#
# $Id: memdb.test,v 1.15 2006/01/30 22:48:44 drh Exp $


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

ifcapable memorydb {

................................................................................
} 5
do_test memdb-8.2 {
  execsql {
    DELETE FROM t1;
    SELECT count(*) FROM t1;
  }
} 0





















} ;# ifcapable memorydb

finish_test







|







 







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





7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
#    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 in-memory database backend.
#
# $Id: memdb.test,v 1.16 2009/01/07 15:18:21 danielk1977 Exp $


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

ifcapable memorydb {

................................................................................
} 5
do_test memdb-8.2 {
  execsql {
    DELETE FROM t1;
    SELECT count(*) FROM t1;
  }
} 0

# Test that auto-vacuum works with in-memory databases.
# 
do_test memdb-9.1 {
  db close
  sqlite3 db test.db
  db cache size 0
  execsql {
    PRAGMA auto_vacuum = full;
    CREATE TABLE t1(a);
    INSERT INTO t1 VALUES(randstr(1000,1000));
    INSERT INTO t1 VALUES(randstr(1000,1000));
    INSERT INTO t1 VALUES(randstr(1000,1000));
  }
  set memused [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1]
  execsql { DELETE FROM t1 }
  set memused2 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1]
  expr {$memused2 + 2048 < $memused}
} {1}


} ;# ifcapable memorydb

finish_test

Changes to test/pager.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
#    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 page cache subsystem.
#
# $Id: pager.test,v 1.33 2009/01/05 17:15:00 danielk1977 Exp $


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

if {[info commands pager_open]!=""} {
db close
................................................................................
  pager_close [pager_open ptf2.db -15]
} {}

# Test truncate on an in-memory database is Ok.
ifcapable memorydb {
  do_test pager-4.6.2 {
    set ::p2 [pager_open :memory: 10]
    pager_truncate $::p2 5
  } {}
  do_test pager-4.6.3 {
    set page1 [page_get $::p2 1]
    for {set i 1} {$i<5} {incr i} {
      set p [page_get $::p2 $i]
      page_write $p "Page $i"
      pager_commit $::p2
      page_unref $p
    }
    page_unref $page1
    # pager_truncate $::p2 3
  } {}
  do_test pager-4.6.4 {
    pager_close $::p2
  } {}
}

do_test pager-4.99 {







|







 







|










|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
#    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 page cache subsystem.
#
# $Id: pager.test,v 1.34 2009/01/07 15:18:21 danielk1977 Exp $


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

if {[info commands pager_open]!=""} {
db close
................................................................................
  pager_close [pager_open ptf2.db -15]
} {}

# Test truncate on an in-memory database is Ok.
ifcapable memorydb {
  do_test pager-4.6.2 {
    set ::p2 [pager_open :memory: 10]
    pager_truncate $::p2 0
  } {}
  do_test pager-4.6.3 {
    set page1 [page_get $::p2 1]
    for {set i 1} {$i<5} {incr i} {
      set p [page_get $::p2 $i]
      page_write $p "Page $i"
      pager_commit $::p2
      page_unref $p
    }
    page_unref $page1
    pager_truncate $::p2 3
  } {}
  do_test pager-4.6.4 {
    pager_close $::p2
  } {}
}

do_test pager-4.99 {