/ Check-in [42c2f3fe]
Login

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

Overview
Comment:Getting ready to redo the journal file format. (CVS 210)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:42c2f3fe68c8a6bab96e035371ecd64296c5491f
User & Date: drh 2001-04-14 16:38:23
Context
2001-04-15
00:37
Pager is working, mostly. (CVS 211) check-in: f82fa707 user: drh tags: trunk
2001-04-14
16:38
Getting ready to redo the journal file format. (CVS 210) check-in: 42c2f3fe user: drh tags: trunk
2001-04-12
23:21
More testing (CVS 209) check-in: 3bde1284 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/pager.c.

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
..
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
..
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
...
103
104
105
106
107
108
109

110

111
112
113
114

115
116

117
118
119
120
121
122
123
...
606
607
608
609
610
611
612






613
614
615
616
617
618
619
...
625
626
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
...
665
666
667
668
669
670
671




672
673
674
675
676
677
*************************************************************************
** This is the implementation of the page cache subsystem.
** 
** The page cache is used to access a database file.  The pager journals
** all writes in order to support rollback.  Locking is used to limit
** access to one or more reader or on writer.
**
** @(#) $Id: pager.c,v 1.1 2001/04/11 14:29:22 drh Exp $
*/
#include "pager.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>

................................................................................
**                       writing the database file.  There is no
**                       data held in memory.  This is the initial
**                       state.
**
**   SQLITE_READLOCK     The page cache is reading the database.
**                       Writing is not permitted.  There can be
**                       multiple readers accessing the same database
**                       at the same time.
**
**   SQLITE_WRITELOCK    The page cache is writing the database.
**                       Access is exclusive.  No other processes or
**                       threads can be reading or writing while one
**                       process is writing.
**
** The page cache comes up in PCS_UNLOCK.  The first time a
................................................................................

/*
** Each in-memory image of a page begins with the following header.
*/
struct PgHdr {
  Pager *pPager;                 /* The pager to which this page belongs */
  Pgno pgno;                     /* The page number for this page */
  PgHdr *pNextHash, *pPrevHash;  /* Hash collision change for PgHdr.pgno */
  int nRef;                      /* Number of users of this page */
  PgHdr *pNext, *pPrev;          /* Freelist of pages where nRef==0 */
  char inJournal;                /* TRUE if has been written to journal */
  char dirty;                    /* TRUE if we need to write back changes */
  /* The page data follows this header */
};

/*
** Convert a pointer to a PgHdr into a pointer to its data,
** and vice verse.
*/
#define PGHDR_TO_DATA(P)  ((void*)(&(P)[1]))
#define DATA_TO_PGHDR(D)  (&((PgHdr*)(D))[-1])

/*
** The number of page numbers that will fit on one page.
*/
................................................................................
*/
struct Pager {
  char *zFilename;            /* Name of the database file */
  char *zJournal;             /* Name of the journal file */
  int fd, jfd;                /* File descriptors for database and journal */
  int nRef;                   /* Sum of PgHdr.nRef */
  int dbSize;                 /* Number of pages in the file */

  int jSize;                  /* Number of pages in the journal */

  int nPage;                  /* Total number of in-memory pages */
  int mxPage;                 /* Maximum number of pages to hold in cache */
  Pgno *aIdx;                 /* Current journal index page */
  char state;                 /* SQLITE_UNLOCK, _READLOCK or _WRITELOCK */

  PgHdr *pFirst, *pLast;      /* List of free pages */
  PgHdr *aHash[N_PG_HASH];    /* Hash table to map page number of PgHdr */

};

/*
** Hash a page number
*/
#define sqlite_pager_hash(PN)  ((PN)%N_PG_HASH)

................................................................................
** The first time this routine is called, the pager creates a new
** journal and acquires a write lock on the database.  If the write
** lock could not be acquired, this routine returns SQLITE_BUSY.  The
** calling routine must check for that routine and be careful not to
** change any page data until this routine returns SQLITE_OK.
*/
int sqlite_pager_write(void *pData){






  if( pPager->state==SQLITE_READLOCK ){
    pPager->jfd = open(pPager->zJournal, O_RDWR|O_CREAT, 0644);
    if( pPager->jfd<0 ){
      return SQLITE_CANTOPEN;
    }
    if( sqlite_pager_lock(pPager->jfd, 1) ){
      close(pPager->jfd);
................................................................................
      close(pPager->jfd);
      pPager->jfd = -1;
      pPager->state = SQLITE_UNLOCK;
      sqlite_pager_reset(pPager);
      return SQLITE_PROTOCOL;
    }
    pPager->state = SQLITE_WRITELOCK;
    pPager->jSize = 0;



  }
  /* Write this page to the journal */


















}

/*
** Commit all changes to the database and release the write lock.
*/
int sqlite_pager_commit(Pager*){
  int i, rc;
  PgHdr *pPg;
  assert( pPager->state==SQLITE_WRITELOCK );
  assert( pPager->jfd>=0 );




  if( fsync(pPager->jfd) ){
    return SQLITE_IOERR;
  }
  for(i=0; i<N_PG_HASH; i++){
    for(pPg=pPager->aHash[i]; pPg; pPg=pPg->pNextHash){
      if( pPg->dirty==0 ) continue;
      rc = sqlite_pager_seekpage(pPager->fd, pPg->pgno, SEEK_SET);
................................................................................
** Rollback all changes.  The database falls back to read-only mode.
** All in-memory cache pages revert to their original data contents.
** The journal is deleted.
*/
int sqlite_pager_rollback(Pager *pPager){
  int rc;
  if( pPager->state!=SQLITE_WRITELOCK ) return SQLITE_OK;




  rc = sqlite_pager_playback(pPager);
  if( rc!=SQLITE_OK ){
    rc = sqlite_pager_unwritelock(pPager);
  }
  return rc;
};







|







 







|







 







|




|



|
|







 







>

>


<

>


>







 







>
>
>
>
>
>







 







|
>
>
>


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










>
>
>
>







 







>
>
>
>






23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
..
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
..
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
...
103
104
105
106
107
108
109
110
111
112
113
114

115
116
117
118
119
120
121
122
123
124
125
126
...
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
...
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
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
...
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
*************************************************************************
** This is the implementation of the page cache subsystem.
** 
** The page cache is used to access a database file.  The pager journals
** all writes in order to support rollback.  Locking is used to limit
** access to one or more reader or on writer.
**
** @(#) $Id: pager.c,v 1.2 2001/04/14 16:38:23 drh Exp $
*/
#include "pager.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>

................................................................................
**                       writing the database file.  There is no
**                       data held in memory.  This is the initial
**                       state.
**
**   SQLITE_READLOCK     The page cache is reading the database.
**                       Writing is not permitted.  There can be
**                       multiple readers accessing the same database
**                       file at the same time.
**
**   SQLITE_WRITELOCK    The page cache is writing the database.
**                       Access is exclusive.  No other processes or
**                       threads can be reading or writing while one
**                       process is writing.
**
** The page cache comes up in PCS_UNLOCK.  The first time a
................................................................................

/*
** Each in-memory image of a page begins with the following header.
*/
struct PgHdr {
  Pager *pPager;                 /* The pager to which this page belongs */
  Pgno pgno;                     /* The page number for this page */
  PgHdr *pNextHash, *pPrevHash;  /* Hash collision chain for PgHdr.pgno */
  int nRef;                      /* Number of users of this page */
  PgHdr *pNext, *pPrev;          /* Freelist of pages where nRef==0 */
  char inJournal;                /* TRUE if has been written to journal */
  char dirty;                    /* TRUE if we need to write back changes */
  /* SQLITE_PAGE_SIZE bytes of page data follow this header */
};

/*
** Convert a pointer to a PgHdr into a pointer to its data
** and back again.
*/
#define PGHDR_TO_DATA(P)  ((void*)(&(P)[1]))
#define DATA_TO_PGHDR(D)  (&((PgHdr*)(D))[-1])

/*
** The number of page numbers that will fit on one page.
*/
................................................................................
*/
struct Pager {
  char *zFilename;            /* Name of the database file */
  char *zJournal;             /* Name of the journal file */
  int fd, jfd;                /* File descriptors for database and journal */
  int nRef;                   /* Sum of PgHdr.nRef */
  int dbSize;                 /* Number of pages in the file */
  int origDbSize;             /* dbSize before the current change */
  int jSize;                  /* Number of pages in the journal */
  int nIdx;                   /* Number of entries in aIdx[] */
  int nPage;                  /* Total number of in-memory pages */
  int mxPage;                 /* Maximum number of pages to hold in cache */

  char state;                 /* SQLITE_UNLOCK, _READLOCK or _WRITELOCK */
  char ioErr;                 /* True if an I/O error has occurred */
  PgHdr *pFirst, *pLast;      /* List of free pages */
  PgHdr *aHash[N_PG_HASH];    /* Hash table to map page number of PgHdr */
  Pgno aIdx[SQLITE_INDEX_SIZE];  /* Current journal index page */
};

/*
** Hash a page number
*/
#define sqlite_pager_hash(PN)  ((PN)%N_PG_HASH)

................................................................................
** The first time this routine is called, the pager creates a new
** journal and acquires a write lock on the database.  If the write
** lock could not be acquired, this routine returns SQLITE_BUSY.  The
** calling routine must check for that routine and be careful not to
** change any page data until this routine returns SQLITE_OK.
*/
int sqlite_pager_write(void *pData){
  PgHdr *pPg = DATA_TO_PGHDR(pData);
  Pager *pPager = pPg->pPager;
  int rc;

  if( pPg->inJournal ){ return SQLITE_OK; }
  if( pPager->state==SQLITE_UNLOCK ){ return SQLITE_PROTOCOL; }
  if( pPager->state==SQLITE_READLOCK ){
    pPager->jfd = open(pPager->zJournal, O_RDWR|O_CREAT, 0644);
    if( pPager->jfd<0 ){
      return SQLITE_CANTOPEN;
    }
    if( sqlite_pager_lock(pPager->jfd, 1) ){
      close(pPager->jfd);
................................................................................
      close(pPager->jfd);
      pPager->jfd = -1;
      pPager->state = SQLITE_UNLOCK;
      sqlite_pager_reset(pPager);
      return SQLITE_PROTOCOL;
    }
    pPager->state = SQLITE_WRITELOCK;
    pPager->jSize = 1;
    pPager->aIdx[0] = pPager->dbSize;
    pPager->origDbSize = pPager->dbSize;
    pPager->nIdx = 1;
  }
  /* Write this page to the journal */
  assert( pPager->jfd>=0 );
  if( pPg->pgno >= pPager->origDbSize ){
    sqlite_pager_seekpage(pPager->fd, pPg->pgno, SEEK_SET);
    rc = sqlite_pager_writepage(pPager->fd, pData);
    pPg->inJournal = 1;
    return rc;
  }
  pPager->aIdx[pPager->nIdx++] = pPg->pgno;
  sqlite_pager_seekpage(pPager->jfd, pPager->jSize++, SEEK_SET);
  rc = sqlite_pager_write(pPager->jfd, pData);
  pPg->inJournal = 1;
  if( pPager->nIdx==SQLITE_INDEX_SIZE ){
    sqlite_pager_seekpage(pPager->jfd, pPager->idxPgno, SEEK_SET);
    rc = sqlite_pager_writepage(pPager->jfd, &pPager->aIdx);
    pPager->nIdx = 0;
    pPager->jSize++;
  }
  return rc;
}

/*
** Commit all changes to the database and release the write lock.
*/
int sqlite_pager_commit(Pager*){
  int i, rc;
  PgHdr *pPg;
  assert( pPager->state==SQLITE_WRITELOCK );
  assert( pPager->jfd>=0 );
  memset(&pPager->aIdx[&pPager->nIdx], 0, 
          (SQLITE_INDEX_SIZE - pPager->nIdx)*sizeof(Pgno));
  sqlite_pager_seekpage(pPager->jfd, pPager->idxPgno, SEEK_SET);
  rc = sqlite_pager_writepage(pPager->jfd, &pPager->aIdx);
  if( fsync(pPager->jfd) ){
    return SQLITE_IOERR;
  }
  for(i=0; i<N_PG_HASH; i++){
    for(pPg=pPager->aHash[i]; pPg; pPg=pPg->pNextHash){
      if( pPg->dirty==0 ) continue;
      rc = sqlite_pager_seekpage(pPager->fd, pPg->pgno, SEEK_SET);
................................................................................
** Rollback all changes.  The database falls back to read-only mode.
** All in-memory cache pages revert to their original data contents.
** The journal is deleted.
*/
int sqlite_pager_rollback(Pager *pPager){
  int rc;
  if( pPager->state!=SQLITE_WRITELOCK ) return SQLITE_OK;
  memset(&pPager->aIdx[&pPager->nIdx], 0, 
          (SQLITE_INDEX_SIZE - pPager->nIdx)*sizeof(Pgno));
  sqlite_pager_seekpage(pPager->jfd, pPager->idxPgno, SEEK_SET);
  rc = sqlite_pager_writepage(pPager->jfd, &pPager->aIdx);
  rc = sqlite_pager_playback(pPager);
  if( rc!=SQLITE_OK ){
    rc = sqlite_pager_unwritelock(pPager);
  }
  return rc;
};