/ Check-in [aaa53e11]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Checkpoint code added to the pager. Regression tests work but the new APIs have not been tested yet. (CVS 361)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: aaa53e113ef849e34883ead8ae584c722ad967db
User & Date: drh 2002-02-02 15:01:16
Context
2002-02-02
18:49
Get the ABORT conflict resolution algorithm working. (CVS 362) check-in: 9be4d4c6 user: drh tags: trunk
15:01
Checkpoint code added to the pager. Regression tests work but the new APIs have not been tested yet. (CVS 361) check-in: aaa53e11 user: drh tags: trunk
2002-01-31
15:54
Change to five conflict resolution algorithms: ROLLBACK, ABORT, FAIL, IGNORE, and REPLACE. This checkin is code only. Documentation and tests are still needed. Also, ABORT is not fully implemented. (CVS 360) check-in: d0e7cf4a user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to publish.sh.

    92     92   sqlite_free_table
    93     93   sqlite_mprintf
    94     94   sqlite_vmprintf
    95     95   sqlite_exec_printf
    96     96   sqlite_exec_vprintf
    97     97   sqlite_get_table_printf
    98     98   sqlite_get_table_vprintf
           99  +sqlite_freemem
          100  +sqlite_libversion
          101  +sqlite_libencoding
    99    102   sqliteMalloc
   100    103   sqliteFree
   101    104   sqliteRealloc
   102    105   END_OF_FILE
   103    106   i386-mingw32-dllwrap \
   104    107        --def sqlite.def -v --export-all \
   105    108        --driver-name i386-mingw32-gcc \

Changes to src/main.c.

    10     10   **
    11     11   *************************************************************************
    12     12   ** Main file for the SQLite library.  The routines in this file
    13     13   ** implement the programmer interface to the library.  Routines in
    14     14   ** other files are for internal use by SQLite and should not be
    15     15   ** accessed by users of the library.
    16     16   **
    17         -** $Id: main.c,v 1.57 2002/01/31 15:54:22 drh Exp $
           17  +** $Id: main.c,v 1.58 2002/02/02 15:01:16 drh Exp $
    18     18   */
    19     19   #include "sqliteInt.h"
    20     20   #include "os.h"
    21     21   
    22     22   /*
    23     23   ** This is the callback routine for the code that initializes the
    24     24   ** database.  See sqliteInit() below for additional information.
................................................................................
   545    545   
   546    546   /*
   547    547   ** Cause any pending operation to stop at its earliest opportunity.
   548    548   */
   549    549   void sqlite_interrupt(sqlite *db){
   550    550     db->flags |= SQLITE_Interrupt;
   551    551   }
          552  +
          553  +/*
          554  +** Windows systems should call this routine to free memory that
          555  +** is returned in the in the errmsg parameter of sqlite_open() when
          556  +** SQLite is a DLL.  For some reason, it does not work to call free()
          557  +** directly.
          558  +**
          559  +** Note that we need to call free() not sqliteFree() here, since every
          560  +** string that is exported from SQLite should have already passed through
          561  +** sqliteStrRealloc().
          562  +*/
          563  +void sqlite_freemem(void *p){ free(p); }
          564  +
          565  +/*
          566  +** Windows systems need functions to call to return the sqlite_version
          567  +** and sqlite_encoding strings.
          568  +*/
          569  +const char *sqlite_libversion(void){ return sqlite_version; }
          570  +const char *sqlite_libencoding(void){ return sqlite_encoding; }

Changes to src/os.c.

   301    301   
   302    302   /*
   303    303   ** Attempt to open a new file for exclusive access by this process.
   304    304   ** The file will be opened for both reading and writing.  To avoid
   305    305   ** a potential security problem, we do not allow the file to have
   306    306   ** previously existed.  Nor do we allow the file to be a symbolic
   307    307   ** link.
          308  +**
          309  +** If delFlag is true, then make arrangements to automatically delete
          310  +** the file when it is closed.
   308    311   **
   309    312   ** On success, write the file handle into *id and return SQLITE_OK.
   310    313   **
   311    314   ** On failure, return SQLITE_CANTOPEN.
   312    315   */
   313         -int sqliteOsOpenExclusive(const char *zFilename, OsFile *id){
          316  +int sqliteOsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){
   314    317   #if OS_UNIX
   315    318     if( access(zFilename, 0)==0 ){
   316    319       return SQLITE_CANTOPEN;
   317    320     }
   318    321   #ifndef O_NOFOLLOW
   319    322   # define O_NOFOLLOW 0
   320    323   #endif
................................................................................
   327    330     sqliteOsLeaveMutex();
   328    331     if( id->pLock==0 ){
   329    332       close(id->fd);
   330    333       unlink(zFilename);
   331    334       return SQLITE_NOMEM;
   332    335     }
   333    336     id->locked = 0;
          337  +  if( delFlag ){
          338  +    unlink(zFilename);
          339  +  }
   334    340     return SQLITE_OK;
   335    341   #endif
   336    342   #if OS_WIN
   337         -  HANDLE h = CreateFile(zFilename,
          343  +  HANDLE h;
          344  +  int fileflags;
          345  +  if( delFlag ){
          346  +    fileflags = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_RANDOM_ACCESS 
          347  +                     | FILE_FLAG_DELETE_ON_CLOSE;
          348  +  }else{
          349  +    fileflags = FILE_FLAG_RANDOM_ACCESS;
          350  +  }
          351  +  h = CreateFile(zFilename,
   338    352        GENERIC_READ | GENERIC_WRITE,
   339    353        0,
   340    354        NULL,
   341    355        CREATE_ALWAYS,
   342         -     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
          356  +     fileflags,
   343    357        NULL
   344    358     );
   345    359     if( h==INVALID_HANDLE_VALUE ){
   346    360       return SQLITE_CANTOPEN;
   347    361     }
   348    362     id->h = h;
   349    363     id->locked = 0;

Changes to src/os.h.

    54     54   # define SQLITE_TEMPNAME_SIZE (MAX_PATH+50)
    55     55   # define SQLITE_MIN_SLEEP_MS 1
    56     56   #endif
    57     57   
    58     58   int sqliteOsDelete(const char*);
    59     59   int sqliteOsFileExists(const char*);
    60     60   int sqliteOsOpenReadWrite(const char*, OsFile*, int*);
    61         -int sqliteOsOpenExclusive(const char*, OsFile*);
           61  +int sqliteOsOpenExclusive(const char*, OsFile*, int);
    62     62   int sqliteOsOpenReadOnly(const char*, OsFile*);
    63     63   int sqliteOsTempFileName(char*);
    64     64   int sqliteOsClose(OsFile*);
    65     65   int sqliteOsRead(OsFile*, void*, int amt);
    66     66   int sqliteOsWrite(OsFile*, const void*, int amt);
    67     67   int sqliteOsSeek(OsFile*, int offset);
    68     68   int sqliteOsSync(OsFile*);

Changes to src/pager.c.

    14     14   ** The pager is used to access a database disk file.  It implements
    15     15   ** atomic commit and rollback through the use of a journal file that
    16     16   ** is separate from the database file.  The pager also implements file
    17     17   ** locking to prevent two processes from writing the same database
    18     18   ** file simultaneously, or one process from reading the database while
    19     19   ** another is writing.
    20     20   **
    21         -** @(#) $Id: pager.c,v 1.36 2002/01/14 09:28:20 drh Exp $
           21  +** @(#) $Id: pager.c,v 1.37 2002/02/02 15:01:16 drh Exp $
    22     22   */
    23     23   #include "sqliteInt.h"
    24     24   #include "pager.h"
    25     25   #include "os.h"
    26     26   #include <assert.h>
    27     27   #include <string.h>
    28     28   
................................................................................
    40     40   **                       multiple readers accessing the same database
    41     41   **                       file at the same time.
    42     42   **
    43     43   **   SQLITE_WRITELOCK    The page cache is writing the database.
    44     44   **                       Access is exclusive.  No other processes or
    45     45   **                       threads can be reading or writing while one
    46     46   **                       process is writing.
           47  +**
           48  +**   SQLITE_CHECKPOINT   The page cache is writing to the database and
           49  +**                       preserving its changes so that it can back them
           50  +**                       out later if need be.
    47     51   **
    48     52   ** The page cache comes up in SQLITE_UNLOCK.  The first time a
    49     53   ** sqlite_page_get() occurs, the state transitions to SQLITE_READLOCK.
    50     54   ** After all pages have been released using sqlite_page_unref(),
    51     55   ** the state transitions back to SQLITE_UNLOCK.  The first time
    52     56   ** that sqlite_page_write() is called, the state transitions to
    53     57   ** SQLITE_WRITELOCK.  (Note that sqlite_page_write() can only be
    54     58   ** called on an outstanding page which means that the pager must
    55     59   ** be in SQLITE_READLOCK before it transitions to SQLITE_WRITELOCK.)
    56     60   ** The sqlite_page_rollback() and sqlite_page_commit() functions 
    57     61   ** transition the state from SQLITE_WRITELOCK back to SQLITE_READLOCK.
           62  +**
           63  +** The sqlite_ckpt_begin() function moves the state from SQLITE_WRITELOCK
           64  +** to SQLITE_CHECKPOINT.  The state transitions back to SQLITE_WRITELOCK
           65  +** on calls to sqlite_ckpt_commit() or sqlite_ckpt_rollback().  While
           66  +** in SQLITE_CHECKPOINT, calls to sqlite_commit() or sqlite_rollback()
           67  +** transition directly back to SQLITE_READLOCK.
           68  +**
           69  +** The code does unequality comparisons on these constants so the order
           70  +** must be preserved.
    58     71   */
    59     72   #define SQLITE_UNLOCK      0
    60     73   #define SQLITE_READLOCK    1
    61     74   #define SQLITE_WRITELOCK   2
           75  +#define SQLITE_CHECKPOINT  3
    62     76   
    63     77   
    64     78   /*
    65     79   ** Each in-memory image of a page begins with the following header.
    66     80   ** This header is only visible to this pager module.  The client
    67     81   ** code that calls pager sees only the data that follows the header.
    68     82   */
................................................................................
    71     85     Pager *pPager;                 /* The pager to which this page belongs */
    72     86     Pgno pgno;                     /* The page number for this page */
    73     87     PgHdr *pNextHash, *pPrevHash;  /* Hash collision chain for PgHdr.pgno */
    74     88     int nRef;                      /* Number of users of this page */
    75     89     PgHdr *pNextFree, *pPrevFree;  /* Freelist of pages where nRef==0 */
    76     90     PgHdr *pNextAll, *pPrevAll;    /* A list of all pages */
    77     91     char inJournal;                /* TRUE if has been written to journal */
           92  +  char inCkpt;                   /* TRUE if written to the checkpoint journal */
    78     93     char dirty;                    /* TRUE if we need to write back changes */
    79     94     /* SQLITE_PAGE_SIZE bytes of page data follow this header */
    80     95     /* Pager.nExtra bytes of local data follow the page data */
    81     96   };
    82     97   
    83     98   /*
    84     99   ** Convert a pointer to a PgHdr into a pointer to its data
................................................................................
    97    112   /*
    98    113   ** A open page cache is an instance of the following structure.
    99    114   */
   100    115   struct Pager {
   101    116     char *zFilename;            /* Name of the database file */
   102    117     char *zJournal;             /* Name of the journal file */
   103    118     OsFile fd, jfd;             /* File descriptors for database and journal */
          119  +  OsFile cpfd;                /* File descriptor for the checkpoint journal */
   104    120     int journalOpen;            /* True if journal file descriptors is valid */
          121  +  int ckptOpen;               /* True if the checkpoint journal is open */
   105    122     int dbSize;                 /* Number of pages in the file */
   106    123     int origDbSize;             /* dbSize before the current change */
          124  +  int ckptSize, ckptJSize;    /* Size of database and journal at ckpt_begin() */
   107    125     int nExtra;                 /* Add this many bytes to each in-memory page */
   108    126     void (*xDestructor)(void*); /* Call this routine when freeing pages */
   109    127     int nPage;                  /* Total number of in-memory pages */
   110    128     int nRef;                   /* Number of in-memory pages with PgHdr.nRef>0 */
   111    129     int mxPage;                 /* Maximum number of pages to hold in cache */
   112    130     int nHit, nMiss, nOvfl;     /* Cache hits, missing, and LRU overflows */
   113    131     unsigned char state;        /* SQLITE_UNLOCK, _READLOCK or _WRITELOCK */
   114    132     unsigned char errMask;      /* One of several kinds of errors */
   115    133     unsigned char tempFile;     /* zFilename is a temporary file */
   116    134     unsigned char readOnly;     /* True for a read-only database */
   117    135     unsigned char needSync;     /* True if an fsync() is needed on the journal */
   118    136     unsigned char *aInJournal;  /* One bit for each page in the database file */
          137  +  unsigned char *aInCkpt;     /* One bit for each page in the database */
   119    138     PgHdr *pFirst, *pLast;      /* List of free pages */
   120    139     PgHdr *pAll;                /* List of all pages */
   121    140     PgHdr *aHash[N_PG_HASH];    /* Hash table to map page number of PgHdr */
   122    141   };
   123    142   
   124    143   /*
   125    144   ** These are bits that can be set in Pager.errMask.
................................................................................
   211    230       sqliteFree(pPg);
   212    231     }
   213    232     pPager->pFirst = 0;
   214    233     pPager->pLast = 0;
   215    234     pPager->pAll = 0;
   216    235     memset(pPager->aHash, 0, sizeof(pPager->aHash));
   217    236     pPager->nPage = 0;
   218         -  if( pPager->state==SQLITE_WRITELOCK ){
          237  +  if( pPager->state>=SQLITE_WRITELOCK ){
   219    238       sqlitepager_rollback(pPager);
   220    239     }
   221    240     sqliteOsUnlock(&pPager->fd);
   222    241     pPager->state = SQLITE_UNLOCK;
   223    242     pPager->dbSize = -1;
   224    243     pPager->nRef = 0;
   225    244     assert( pPager->journalOpen==0 );
................................................................................
   230    249   ** a write lock on the database.  This routine releases the database
   231    250   ** write lock and acquires a read lock in its place.  The journal file
   232    251   ** is deleted and closed.
   233    252   */
   234    253   static int pager_unwritelock(Pager *pPager){
   235    254     int rc;
   236    255     PgHdr *pPg;
   237         -  if( pPager->state!=SQLITE_WRITELOCK ) return SQLITE_OK;
          256  +  if( pPager->state<SQLITE_WRITELOCK ) return SQLITE_OK;
   238    257     sqliteOsClose(&pPager->jfd);
   239    258     pPager->journalOpen = 0;
   240    259     sqliteOsDelete(pPager->zJournal);
   241    260     rc = sqliteOsReadLock(&pPager->fd);
   242    261     assert( rc==SQLITE_OK );
          262  +  sqliteFree( pPager->aInCkpt );
   243    263     sqliteFree( pPager->aInJournal );
   244    264     pPager->aInJournal = 0;
   245    265     for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
   246    266       pPg->inJournal = 0;
   247    267       pPg->dirty = 0;
   248    268     }
   249    269     pPager->state = SQLITE_READLOCK;
   250    270     return rc;
   251    271   }
          272  +
          273  +/*
          274  +** Read a single page from the journal file opened on file descriptor
          275  +** jfd.  Playback this one page.
          276  +*/
          277  +static int pager_playback_one_page(Pager *pPager, OsFile *jfd){
          278  +  int rc;
          279  +  PgHdr *pPg;              /* An existing page in the cache */
          280  +  PageRecord pgRec;
          281  +
          282  +  rc = sqliteOsRead(&pPager->jfd, &pgRec, sizeof(pgRec));
          283  +  if( rc!=SQLITE_OK ) return rc;
          284  +
          285  +  /* Sanity checking on the page */
          286  +  if( pgRec.pgno>pPager->dbSize || pgRec.pgno==0 ) return SQLITE_CORRUPT;
          287  +
          288  +  /* Playback the page.  Update the in-memory copy of the page
          289  +  ** at the same time, if there is one.
          290  +  */
          291  +  pPg = pager_lookup(pPager, pgRec.pgno);
          292  +  if( pPg ){
          293  +    memcpy(PGHDR_TO_DATA(pPg), pgRec.aData, SQLITE_PAGE_SIZE);
          294  +    memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra);
          295  +  }
          296  +  rc = sqliteOsSeek(&pPager->fd, (pgRec.pgno-1)*SQLITE_PAGE_SIZE);
          297  +  if( rc==SQLITE_OK ){
          298  +    rc = sqliteOsWrite(&pPager->fd, pgRec.aData, SQLITE_PAGE_SIZE);
          299  +  }
          300  +  return rc;
          301  +}
   252    302   
   253    303   /*
   254    304   ** Playback the journal and thus restore the database file to
   255    305   ** the state it was in before we started making changes.  
   256    306   **
   257    307   ** The journal file format is as follows:  There is an initial
   258    308   ** file-type string for sanity checking.  Then there is a single
   259    309   ** Pgno number which is the number of pages in the database before
   260    310   ** changes were made.  The database is truncated to this size.
   261    311   ** Next come zero or more page records where each page record
   262    312   ** consists of a Pgno and SQLITE_PAGE_SIZE bytes of data.  See
   263    313   ** the PageRecord structure for details.
   264    314   **
   265         -** For playback, the pages are read from the journal in
   266         -** reverse order and put back into the original database file.
   267         -** It used to be required to replay pages in reverse order because
   268         -** there was a possibility of a page appearing in the journal more
   269         -** than once.  In that case, the original value of the page was
   270         -** the first entry so it should be reset last.  But now, a bitmap
   271         -** is used to record every page that is in the journal.  No pages
   272         -** are ever repeated. So we could, in theory, playback the journal
   273         -** in the forward direction and it would still work.
   274         -**
   275    315   ** If the file opened as the journal file is not a well-formed
   276    316   ** journal file (as determined by looking at the magic number
   277    317   ** at the beginning) then this routine returns SQLITE_PROTOCOL.
   278    318   ** If any other errors occur during playback, the database will
   279    319   ** likely be corrupted, so the PAGER_ERR_CORRUPT bit is set in
   280    320   ** pPager->errMask and SQLITE_CORRUPT is returned.  If it all
   281    321   ** works, then this routine returns SQLITE_OK.
   282    322   */
   283    323   static int pager_playback(Pager *pPager){
   284    324     int nRec;                /* Number of Records */
   285    325     int i;                   /* Loop counter */
   286    326     Pgno mxPg = 0;           /* Size of the original file in pages */
   287         -  PgHdr *pPg;              /* An existing page in the cache */
   288         -  PageRecord pgRec;
   289    327     unsigned char aMagic[sizeof(aJournalMagic)];
   290    328     int rc;
   291    329   
   292    330     /* Figure out how many records are in the journal.  Abort early if
   293    331     ** the journal is empty.
   294    332     */
   295    333     assert( pPager->journalOpen );
................................................................................
   317    355     }
   318    356     rc = sqliteOsTruncate(&pPager->fd, mxPg*SQLITE_PAGE_SIZE);
   319    357     if( rc!=SQLITE_OK ){
   320    358       goto end_playback;
   321    359     }
   322    360     pPager->dbSize = mxPg;
   323    361     
   324         -  /* Process segments beginning with the last and working backwards
   325         -  ** to the first.
          362  +  /* Copy original pages out of the journal and back into the database file.
   326    363     */
   327    364     for(i=nRec-1; i>=0; i--){
   328         -    /* Seek to the beginning of the segment */
   329         -    int ofst;
   330         -    ofst = i*sizeof(PageRecord) + sizeof(aMagic) + sizeof(Pgno);
   331         -    rc = sqliteOsSeek(&pPager->jfd, ofst);
   332         -    if( rc!=SQLITE_OK ) break;
   333         -    rc = sqliteOsRead(&pPager->jfd, &pgRec, sizeof(pgRec));
   334         -    if( rc!=SQLITE_OK ) break;
   335         -
   336         -    /* Sanity checking on the page */
   337         -    if( pgRec.pgno>mxPg || pgRec.pgno==0 ){
   338         -      rc = SQLITE_CORRUPT;
   339         -      break;
   340         -    }
   341         -
   342         -    /* Playback the page.  Update the in-memory copy of the page
   343         -    ** at the same time, if there is one.
   344         -    */
   345         -    pPg = pager_lookup(pPager, pgRec.pgno);
   346         -    if( pPg ){
   347         -      memcpy(PGHDR_TO_DATA(pPg), pgRec.aData, SQLITE_PAGE_SIZE);
   348         -      memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra);
   349         -    }
   350         -    rc = sqliteOsSeek(&pPager->fd, (pgRec.pgno-1)*SQLITE_PAGE_SIZE);
   351         -    if( rc!=SQLITE_OK ) break;
   352         -    rc = sqliteOsWrite(&pPager->fd, pgRec.aData, SQLITE_PAGE_SIZE);
          365  +    rc = pager_playback_one_page(pPager, &pPager->jfd);
   353    366       if( rc!=SQLITE_OK ) break;
   354    367     }
   355    368   
   356    369   end_playback:
          370  +  if( rc!=SQLITE_OK ){
          371  +    pager_unwritelock(pPager);
          372  +    pPager->errMask |= PAGER_ERR_CORRUPT;
          373  +    rc = SQLITE_CORRUPT;
          374  +  }else{
          375  +    rc = pager_unwritelock(pPager);
          376  +  }
          377  +  return rc;
          378  +}
          379  +
          380  +/*
          381  +** Playback the checkpoint journal.
          382  +**
          383  +** This is similar to playing back the transaction journal but with
          384  +** a few extra twists.
          385  +**
          386  +**    (1)  The original size of the database file is stored in
          387  +**         pPager->ckptSize, not in the journal file itself.
          388  +**
          389  +**    (2)  In addition to playing back the checkpoint journal, also
          390  +**         playback all pages of the transaction journal beginning
          391  +**         at offset pPager->ckptJSize.
          392  +*/
          393  +static int pager_ckpt_playback(Pager *pPager){
          394  +  int nRec;                /* Number of Records */
          395  +  int i;                   /* Loop counter */
          396  +  int rc;
          397  +
          398  +  /* Truncate the database back to its original size.
          399  +  */
          400  +  rc = sqliteOsTruncate(&pPager->fd, pPager->ckptSize);
          401  +  pPager->dbSize = pPager->ckptSize;
          402  +
          403  +  /* Figure out how many records are in the checkpoint journal.
          404  +  */
          405  +  assert( pPager->ckptOpen && pPager->journalOpen );
          406  +  sqliteOsSeek(&pPager->cpfd, 0);
          407  +  rc = sqliteOsFileSize(&pPager->cpfd, &nRec);
          408  +  if( rc!=SQLITE_OK ){
          409  +    goto end_ckpt_playback;
          410  +  }
          411  +  nRec /= sizeof(PageRecord);
          412  +  
          413  +  /* Copy original pages out of the checkpoint journal and back into the
          414  +  ** database file.
          415  +  */
          416  +  for(i=nRec-1; i>=0; i--){
          417  +    rc = pager_playback_one_page(pPager, &pPager->cpfd);
          418  +    if( rc!=SQLITE_OK ) goto end_ckpt_playback;
          419  +  }
          420  +
          421  +  /* Figure out how many pages need to be copied out of the transaction
          422  +  ** journal.
          423  +  */
          424  +  rc = sqliteOsSeek(&pPager->jfd, pPager->ckptJSize);
          425  +  if( rc!=SQLITE_OK ){
          426  +    goto end_ckpt_playback;
          427  +  }
          428  +  rc = sqliteOsFileSize(&pPager->jfd, &nRec);
          429  +  if( rc!=SQLITE_OK ){
          430  +    goto end_ckpt_playback;
          431  +  }
          432  +  nRec = (nRec - pPager->ckptJSize)/sizeof(PageRecord);
          433  +  for(i=nRec-1; i>=0; i--){
          434  +    rc = pager_playback_one_page(pPager, &pPager->jfd);
          435  +    if( rc!=SQLITE_OK ) goto end_ckpt_playback;
          436  +  }
          437  +  
          438  +
          439  +end_ckpt_playback:
          440  +  sqliteOsClose(&pPager->cpfd);
          441  +  pPager->ckptOpen = 0;
   357    442     if( rc!=SQLITE_OK ){
   358    443       pager_unwritelock(pPager);
   359    444       pPager->errMask |= PAGER_ERR_CORRUPT;
   360    445       rc = SQLITE_CORRUPT;
   361    446     }else{
   362    447       rc = pager_unwritelock(pPager);
   363    448     }
................................................................................
   368    453   ** Change the maximum number of in-memory pages that are allowed.
   369    454   */
   370    455   void sqlitepager_set_cachesize(Pager *pPager, int mxPage){
   371    456     if( mxPage>10 ){
   372    457       pPager->mxPage = mxPage;
   373    458     }
   374    459   }
          460  +
          461  +/*
          462  +** Open a temporary file.  Write the name of the file into zName
          463  +** (zName must be at least SQLITE_TEMPNAME_SIZE bytes long.)  Write
          464  +** the file descriptor into *fd.  Return SQLITE_OK on success or some
          465  +** other error code if we fail.
          466  +**
          467  +** The OS will automatically delete the temporary file when it is
          468  +** closed.
          469  +*/
          470  +static int sqlitepager_opentemp(char *zFile, OsFile *fd){
          471  +  int cnt = 8;
          472  +  int rc;
          473  +  do{
          474  +    cnt--;
          475  +    sqliteOsTempFileName(zFile);
          476  +    rc = sqliteOsOpenExclusive(zFile, fd, 1);
          477  +  }while( cnt>0 && rc!=SQLITE_OK );
          478  +  return rc;
          479  +}
   375    480   
   376    481   /*
   377    482   ** Create a new page cache and put a pointer to the page cache in *ppPager.
   378    483   ** The file to be cached need not exist.  The file is not locked until
   379    484   ** the first call to sqlitepager_get() and is only held open until the
   380    485   ** last page is released using sqlitepager_unref().
   381    486   **
................................................................................
   401    506     if( sqlite_malloc_failed ){
   402    507       return SQLITE_NOMEM;
   403    508     }
   404    509     if( zFilename ){
   405    510       rc = sqliteOsOpenReadWrite(zFilename, &fd, &readOnly);
   406    511       tempFile = 0;
   407    512     }else{
   408         -    int cnt = 8;
   409         -    sqliteOsTempFileName(zTemp);
   410         -    do{
   411         -      cnt--;
   412         -      sqliteOsTempFileName(zTemp);
   413         -      rc = sqliteOsOpenExclusive(zTemp, &fd);
   414         -    }while( cnt>0 && rc!=SQLITE_OK );
          513  +    rc = sqlitepager_opentemp(zTemp, &fd);
   415    514       zFilename = zTemp;
   416    515       tempFile = 1;
   417    516     }
   418    517     if( rc!=SQLITE_OK ){
   419    518       return SQLITE_CANTOPEN;
   420    519     }
   421    520     nameLen = strlen(zFilename);
................................................................................
   427    526     pPager->zFilename = (char*)&pPager[1];
   428    527     pPager->zJournal = &pPager->zFilename[nameLen+1];
   429    528     strcpy(pPager->zFilename, zFilename);
   430    529     strcpy(pPager->zJournal, zFilename);
   431    530     strcpy(&pPager->zJournal[nameLen], "-journal");
   432    531     pPager->fd = fd;
   433    532     pPager->journalOpen = 0;
          533  +  pPager->ckptOpen = 0;
   434    534     pPager->nRef = 0;
   435    535     pPager->dbSize = -1;
          536  +  pPager->ckptSize = 0;
          537  +  pPager->ckptJSize = 0;
   436    538     pPager->nPage = 0;
   437    539     pPager->mxPage = mxPage>5 ? mxPage : 10;
   438    540     pPager->state = SQLITE_UNLOCK;
   439    541     pPager->errMask = 0;
   440    542     pPager->tempFile = tempFile;
   441    543     pPager->readOnly = readOnly;
   442    544     pPager->needSync = 0;
................................................................................
   489    591   ** and their memory is freed.  Any attempt to use a page associated
   490    592   ** with this page cache after this function returns will likely
   491    593   ** result in a coredump.
   492    594   */
   493    595   int sqlitepager_close(Pager *pPager){
   494    596     PgHdr *pPg, *pNext;
   495    597     switch( pPager->state ){
          598  +    case SQLITE_CHECKPOINT:
   496    599       case SQLITE_WRITELOCK: {
   497    600         sqlitepager_rollback(pPager);
   498    601         sqliteOsUnlock(&pPager->fd);
   499    602         assert( pPager->journalOpen==0 );
   500    603         break;
   501    604       }
   502    605       case SQLITE_READLOCK: {
................................................................................
   511    614     for(pPg=pPager->pAll; pPg; pPg=pNext){
   512    615       pNext = pPg->pNextAll;
   513    616       sqliteFree(pPg);
   514    617     }
   515    618     sqliteOsClose(&pPager->fd);
   516    619     assert( pPager->journalOpen==0 );
   517    620     if( pPager->tempFile ){
   518         -    sqliteOsDelete(pPager->zFilename);
          621  +    /* sqliteOsDelete(pPager->zFilename); */
   519    622     }
   520    623     sqliteFree(pPager);
   521    624     return SQLITE_OK;
   522    625   }
   523    626   
   524    627   /*
   525    628   ** Return the page number for the given page data.
................................................................................
   571    674   ** non-obvious optimization.  fsync() is an expensive operation so we
   572    675   ** want to minimize the number it is called.  After an fsync() call,
   573    676   ** we are free to write dirty pages back to the database.  It is best
   574    677   ** to go ahead and write as many dirty pages as possible to minimize 
   575    678   ** the risk of having to do another fsync() later on.  Writing dirty
   576    679   ** free pages in this way was observed to make database operations go
   577    680   ** up to 10 times faster.
          681  +**
          682  +** If we are writing to temporary database, there is no need to preserve
          683  +** the integrity of the journal file, so we can save time and skip the
          684  +** fsync().
   578    685   */
   579    686   static int syncAllPages(Pager *pPager){
   580    687     PgHdr *pPg;
   581    688     int rc = SQLITE_OK;
   582    689     if( pPager->needSync ){
   583         -    rc = sqliteOsSync(&pPager->jfd);
   584         -    if( rc!=0 ) return rc;
          690  +    if( !pPager->tempFile ){
          691  +      rc = sqliteOsSync(&pPager->jfd);
          692  +      if( rc!=0 ) return rc;
          693  +    }
   585    694       pPager->needSync = 0;
   586    695     }
   587    696     for(pPg=pPager->pFirst; pPg; pPg=pPg->pNextFree){
   588    697       if( pPg->dirty ){
   589    698         sqliteOsSeek(&pPager->fd, (pPg->pgno-1)*SQLITE_PAGE_SIZE);
   590    699         rc = sqliteOsWrite(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
   591    700         if( rc!=SQLITE_OK ) break;
................................................................................
   770    879       }
   771    880       pPg->pgno = pgno;
   772    881       if( pPager->aInJournal && (int)pgno<=pPager->origDbSize ){
   773    882         pPg->inJournal = (pPager->aInJournal[pgno/8] & (1<<(pgno&7)))!=0;
   774    883       }else{
   775    884         pPg->inJournal = 0;
   776    885       }
          886  +    if( pPager->aInCkpt && (int)pgno*SQLITE_PAGE_SIZE<=pPager->ckptSize ){
          887  +      pPg->inCkpt = (pPager->aInCkpt[pgno/8] & (1<<(pgno&7)))!=0;
          888  +    }else{
          889  +      pPg->inCkpt = 0;
          890  +    }
   777    891       pPg->dirty = 0;
   778    892       pPg->nRef = 1;
   779    893       REFINFO(pPg);
   780    894       pPager->nRef++;
   781    895       h = pager_hash(pgno);
   782    896       pPg->pNextHash = pPager->aHash[h];
   783    897       pPager->aHash[h] = pPg;
................................................................................
   918   1032       return SQLITE_PERM;
   919   1033     }
   920   1034   
   921   1035     /* Mark the page as dirty.  If the page has already been written
   922   1036     ** to the journal then we can return right away.
   923   1037     */
   924   1038     pPg->dirty = 1;
   925         -  if( pPg->inJournal ){ return SQLITE_OK; }
         1039  +  if( pPg->inJournal && (pPg->inCkpt || pPager->ckptOpen==0) ){
         1040  +    return SQLITE_OK;
         1041  +  }
   926   1042   
   927   1043     /* If we get this far, it means that the page needs to be
   928         -  ** written to the journal file. First check to see if the
   929         -  ** journal exists and create it if it does not.
         1044  +  ** written to the transaction journal or the ckeckpoint journal
         1045  +  ** or both.
         1046  +  **
         1047  +  ** First check to see that the transaction journal exists and
         1048  +  ** create it if it does not.
   930   1049     */
   931   1050     assert( pPager->state!=SQLITE_UNLOCK );
   932   1051     if( pPager->state==SQLITE_READLOCK ){
   933   1052       assert( pPager->aInJournal==0 );
   934   1053       rc = sqliteOsWriteLock(&pPager->fd);
   935   1054       if( rc!=SQLITE_OK ){
   936   1055         return rc;
   937   1056       }
   938   1057       pPager->aInJournal = sqliteMalloc( pPager->dbSize/8 + 1 );
   939   1058       if( pPager->aInJournal==0 ){
   940   1059         sqliteOsReadLock(&pPager->fd);
   941   1060         return SQLITE_NOMEM;
   942   1061       }
   943         -    rc = sqliteOsOpenExclusive(pPager->zJournal, &pPager->jfd);
         1062  +    rc = sqliteOsOpenExclusive(pPager->zJournal, &pPager->jfd, 0);
   944   1063       if( rc!=SQLITE_OK ){
   945   1064         sqliteFree(pPager->aInJournal);
   946   1065         pPager->aInJournal = 0;
   947   1066         sqliteOsReadLock(&pPager->fd);
   948   1067         return SQLITE_CANTOPEN;
   949   1068       }
   950   1069       pPager->journalOpen = 1;
................................................................................
   961   1080         if( rc==SQLITE_OK ) rc = SQLITE_FULL;
   962   1081         return rc;
   963   1082       }
   964   1083     }
   965   1084     assert( pPager->state==SQLITE_WRITELOCK );
   966   1085     assert( pPager->journalOpen );
   967   1086   
   968         -  /* The journal now exists and we have a write lock on the
   969         -  ** main database file.  Write the current page to the journal.
         1087  +  /* The transaction journal now exists and we have a write lock on the
         1088  +  ** main database file.  Write the current page to the transaction 
         1089  +  ** journal if it is not there already.
   970   1090     */
   971         -  if( (int)pPg->pgno <= pPager->origDbSize ){
         1091  +  if( !pPg->inJournal && (int)pPg->pgno <= pPager->origDbSize ){
   972   1092       rc = sqliteOsWrite(&pPager->jfd, &pPg->pgno, sizeof(Pgno));
   973   1093       if( rc==SQLITE_OK ){
   974   1094         rc = sqliteOsWrite(&pPager->jfd, pData, SQLITE_PAGE_SIZE);
   975   1095       }
   976   1096       if( rc!=SQLITE_OK ){
   977   1097         sqlitepager_rollback(pPager);
   978   1098         pPager->errMask |= PAGER_ERR_FULL;
   979   1099         return rc;
   980   1100       }
   981   1101       assert( pPager->aInJournal!=0 );
   982   1102       pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
   983   1103       pPager->needSync = 1;
         1104  +    pPg->inJournal = 1;
         1105  +    if( pPager->ckptOpen ){
         1106  +      pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
         1107  +      pPg->inCkpt = 1;
         1108  +    }
         1109  +  }
         1110  +
         1111  +  /* If the checkpoint journal is open and the page is not in it,
         1112  +  ** then write the current page to the checkpoint journal.
         1113  +  */
         1114  +  if( pPager->ckptOpen && !pPg->inCkpt 
         1115  +      && (int)pPg->pgno*SQLITE_PAGE_SIZE < pPager->ckptSize ){
         1116  +    assert( pPg->inJournal );
         1117  +    rc = sqliteOsWrite(&pPager->cpfd, &pPg->pgno, sizeof(Pgno));
         1118  +    if( rc==SQLITE_OK ){
         1119  +      rc = sqliteOsWrite(&pPager->cpfd, pData, SQLITE_PAGE_SIZE);
         1120  +    }
         1121  +    if( rc!=SQLITE_OK ){
         1122  +      sqlitepager_rollback(pPager);
         1123  +      pPager->errMask |= PAGER_ERR_FULL;
         1124  +      return rc;
         1125  +    }
         1126  +    assert( pPager->aInCkpt!=0 );
         1127  +    pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
         1128  +    pPg->inCkpt = 1;
   984   1129     }
   985   1130   
   986         -  /* Mark the current page as being in the journal and return.
         1131  +  /* Update the database size and return.
   987   1132     */
   988         -  pPg->inJournal = 1;
   989   1133     if( pPager->dbSize<(int)pPg->pgno ){
   990   1134       pPager->dbSize = pPg->pgno;
   991   1135     }
   992   1136     return rc;
   993   1137   }
   994   1138   
   995   1139   /*
................................................................................
  1100   1244     a[4] = pPager->state;
  1101   1245     a[5] = pPager->errMask;
  1102   1246     a[6] = pPager->nHit;
  1103   1247     a[7] = pPager->nMiss;
  1104   1248     a[8] = pPager->nOvfl;
  1105   1249     return a;
  1106   1250   }
         1251  +
         1252  +/*
         1253  +** Set the checkpoint.
         1254  +**
         1255  +** This routine should be called with the transaction journal already
         1256  +** open.  A new checkpoint journal is created that can be used to rollback
         1257  +** changes of a single command within a larger transaction.
         1258  +*/
         1259  +int sqlitepager_ckpt_begin(Pager *pPager){
         1260  +  int rc;
         1261  +  char zTemp[SQLITE_TEMPNAME_SIZE];
         1262  +  assert( pPager->journalOpen );
         1263  +  assert( !pPager->ckptOpen );
         1264  +  pPager->aInCkpt = sqliteMalloc( pPager->dbSize/8 + 1 );
         1265  +  if( pPager->aInCkpt==0 ){
         1266  +    sqliteOsReadLock(&pPager->fd);
         1267  +    return SQLITE_NOMEM;
         1268  +  }
         1269  +  rc = sqliteOsFileSize(&pPager->jfd, &pPager->ckptJSize);
         1270  +  if( rc ) goto ckpt_begin_failed;
         1271  +  pPager->ckptSize = pPager->dbSize * SQLITE_PAGE_SIZE;
         1272  +  rc = sqlitepager_opentemp(zTemp, &pPager->cpfd);
         1273  +  if( rc ) goto ckpt_begin_failed;
         1274  +  pPager->ckptOpen = 1;
         1275  +  return SQLITE_OK;
         1276  + 
         1277  +ckpt_begin_failed:
         1278  +  if( pPager->aInCkpt ){
         1279  +    sqliteFree(pPager->aInCkpt);
         1280  +    pPager->aInCkpt = 0;
         1281  +  }
         1282  +  return rc;
         1283  +}
         1284  +
         1285  +/*
         1286  +** Commit a checkpoint.
         1287  +*/
         1288  +int sqlitepager_ckpt_commit(Pager *pPager){
         1289  +  assert( pPager->ckptOpen );
         1290  +  sqliteOsClose(&pPager->cpfd);
         1291  +  sqliteFree(pPager->aInCkpt);
         1292  +  pPager->ckptOpen = 0;
         1293  +  return SQLITE_OK;
         1294  +}
         1295  +
         1296  +/*
         1297  +** Rollback a checkpoint.
         1298  +*/
         1299  +int sqlitepager_ckpt_rollback(Pager *pPager){
         1300  +  int rc;
         1301  +  assert( pPager->ckptOpen );
         1302  +  rc = pager_ckpt_playback(pPager);
         1303  +  sqliteOsClose(&pPager->cpfd);
         1304  +  sqliteFree(pPager->aInCkpt);
         1305  +  pPager->ckptOpen = 0;
         1306  +  return rc;
         1307  +}
  1107   1308   
  1108   1309   #if SQLITE_TEST
  1109   1310   /*
  1110   1311   ** Print a listing of all referenced pages and their ref count.
  1111   1312   */
  1112   1313   void sqlitepager_refdump(Pager *pPager){
  1113   1314     PgHdr *pPg;

Changes to src/pager.h.

     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** This header file defines the interface that the sqlite page cache
    13     13   ** subsystem.  The page cache subsystem reads and writes a file a page
    14     14   ** at a time and provides a journal for rollback.
    15     15   **
    16         -** @(#) $Id: pager.h,v 1.13 2001/12/15 14:22:19 drh Exp $
           16  +** @(#) $Id: pager.h,v 1.14 2002/02/02 15:01:16 drh Exp $
    17     17   */
    18     18   
    19     19   /*
    20     20   ** The size of one page
    21     21   **
    22     22   ** You can change this value to another (reasonable) power of two
    23     23   ** such as 512, 2048, 4096, or 8192 and things will still work.  But
................................................................................
    58     58   Pgno sqlitepager_pagenumber(void*);
    59     59   int sqlitepager_write(void*);
    60     60   int sqlitepager_iswriteable(void*);
    61     61   int sqlitepager_pagecount(Pager*);
    62     62   int sqlitepager_commit(Pager*);
    63     63   int sqlitepager_rollback(Pager*);
    64     64   int sqlitepager_isreadonly(Pager*);
           65  +int sqlitepager_ckpt_begin(Pager*);
           66  +int sqlitepager_ckpt_commit(Pager*);
           67  +int sqlitepager_ckpt_rollback(Pager*);
    65     68   int *sqlitepager_stats(Pager*);
    66     69   
    67     70   #ifdef SQLITE_TEST
    68     71   void sqlitepager_refdump(Pager*);
    69     72   int pager_refinfo_enable;
    70     73   #endif

Changes to src/parse.y.

    10     10   **
    11     11   *************************************************************************
    12     12   ** This file contains SQLite's grammar for SQL.  Process this file
    13     13   ** using the lemon parser generator to generate C code that runs
    14     14   ** the parser.  Lemon will also generate a header file containing
    15     15   ** numeric codes for all of the tokens.
    16     16   **
    17         -** @(#) $Id: parse.y,v 1.45 2002/01/31 15:54:22 drh Exp $
           17  +** @(#) $Id: parse.y,v 1.46 2002/02/02 15:01:16 drh Exp $
    18     18   */
    19     19   %token_prefix TK_
    20     20   %token_type {Token}
    21     21   %default_type {Token}
    22     22   %extra_argument {Parse *pParse}
    23     23   %syntax_error {
    24     24     sqliteSetString(&pParse->zErrMsg,"syntax error",0);
................................................................................
    52     52   ecmd ::= explain cmd.  {sqliteExec(pParse);}
    53     53   ecmd ::= cmd.          {sqliteExec(pParse);}
    54     54   ecmd ::= .
    55     55   explain ::= EXPLAIN.    {pParse->explain = 1;}
    56     56   
    57     57   ///////////////////// Begin and end transactions. ////////////////////////////
    58     58   //
    59         -cmd ::= BEGIN trans_opt onconf(R).  {sqliteBeginTransaction(pParse,R);}
           59  +
           60  +// For now, disable the ability to change the default conflict resolution
           61  +// algorithm in a transaction.  We made add it back later.
           62  +// cmd ::= BEGIN trans_opt onconf(R).  {sqliteBeginTransaction(pParse,R);}
           63  +cmd ::= BEGIN trans_opt.        {sqliteBeginTransaction(pParse, OE_Default);}
    60     64   trans_opt ::= .
    61     65   trans_opt ::= TRANSACTION.
    62     66   trans_opt ::= TRANSACTION ids.
    63     67   cmd ::= COMMIT trans_opt.      {sqliteCommitTransaction(pParse);}
    64     68   cmd ::= END trans_opt.         {sqliteCommitTransaction(pParse);}
    65     69   cmd ::= ROLLBACK trans_opt.    {sqliteRollbackTransaction(pParse);}
    66     70   
................................................................................
   321    325   
   322    326   setlist(A) ::= setlist(Z) COMMA ids(X) EQ expr(Y).
   323    327       {A = sqliteExprListAppend(Z,Y,&X);}
   324    328   setlist(A) ::= ids(X) EQ expr(Y).   {A = sqliteExprListAppend(0,Y,&X);}
   325    329   
   326    330   ////////////////////////// The INSERT command /////////////////////////////////
   327    331   //
   328         -cmd ::= INSERT orconf(R) INTO ids(X) inscollist_opt(F) VALUES LP itemlist(Y) RP.
          332  +cmd ::= insert_cmd(R) INTO ids(X) inscollist_opt(F) VALUES LP itemlist(Y) RP.
   329    333                  {sqliteInsert(pParse, &X, Y, 0, F, R);}
   330         -cmd ::= INSERT orconf(R) INTO ids(X) inscollist_opt(F) select(S).
          334  +cmd ::= insert_cmd(R) INTO ids(X) inscollist_opt(F) select(S).
   331    335                  {sqliteInsert(pParse, &X, 0, S, F, R);}
   332    336   
          337  +%type insert_cmd {int}
          338  +insert_cmd(A) ::= INSERT orconf(R).   {A = R;}
          339  +insert_cmd(A) ::= REPLACE.            {A = OE_Replace;}
          340  +
   333    341   
   334    342   %type itemlist {ExprList*}
   335    343   %destructor itemlist {sqliteExprListDelete($$);}
   336    344   %type item {Expr*}
   337    345   %destructor item {sqliteExprDelete($$);}
   338    346   
   339    347   itemlist(A) ::= itemlist(X) COMMA item(Y).  {A = sqliteExprListAppend(X,Y,0);}

Changes to src/test2.c.

     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** Code for testing the pager.c module in SQLite.  This code
    13     13   ** is not included in the SQLite library.  It is used for automated
    14     14   ** testing of the SQLite library.
    15     15   **
    16         -** $Id: test2.c,v 1.6 2001/10/12 17:30:05 drh Exp $
           16  +** $Id: test2.c,v 1.7 2002/02/02 15:01:16 drh Exp $
    17     17   */
    18     18   #include "sqliteInt.h"
    19     19   #include "pager.h"
    20     20   #include "tcl.h"
    21     21   #include <stdlib.h>
    22     22   #include <string.h>
    23     23   
................................................................................
   148    148     if( argc!=2 ){
   149    149       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   150    150          " ID\"", 0);
   151    151       return TCL_ERROR;
   152    152     }
   153    153     if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR;
   154    154     rc = sqlitepager_commit(pPager);
          155  +  if( rc!=SQLITE_OK ){
          156  +    Tcl_AppendResult(interp, errorName(rc), 0);
          157  +    return TCL_ERROR;
          158  +  }
          159  +  return TCL_OK;
          160  +}
          161  +
          162  +/*
          163  +** Usage:   pager_ckpt_begin ID
          164  +**
          165  +** Start a new checkpoint.
          166  +*/
          167  +static int pager_ckpt_begin(
          168  +  void *NotUsed,
          169  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
          170  +  int argc,              /* Number of arguments */
          171  +  char **argv            /* Text of each argument */
          172  +){
          173  +  Pager *pPager;
          174  +  int rc;
          175  +  if( argc!=2 ){
          176  +    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
          177  +       " ID\"", 0);
          178  +    return TCL_ERROR;
          179  +  }
          180  +  if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR;
          181  +  rc = sqlitepager_ckpt_begin(pPager);
          182  +  if( rc!=SQLITE_OK ){
          183  +    Tcl_AppendResult(interp, errorName(rc), 0);
          184  +    return TCL_ERROR;
          185  +  }
          186  +  return TCL_OK;
          187  +}
          188  +
          189  +/*
          190  +** Usage:   pager_ckpt_rollback ID
          191  +**
          192  +** Rollback changes to a checkpoint
          193  +*/
          194  +static int pager_ckpt_rollback(
          195  +  void *NotUsed,
          196  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
          197  +  int argc,              /* Number of arguments */
          198  +  char **argv            /* Text of each argument */
          199  +){
          200  +  Pager *pPager;
          201  +  int rc;
          202  +  if( argc!=2 ){
          203  +    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
          204  +       " ID\"", 0);
          205  +    return TCL_ERROR;
          206  +  }
          207  +  if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR;
          208  +  rc = sqlitepager_ckpt_rollback(pPager);
          209  +  if( rc!=SQLITE_OK ){
          210  +    Tcl_AppendResult(interp, errorName(rc), 0);
          211  +    return TCL_ERROR;
          212  +  }
          213  +  return TCL_OK;
          214  +}
          215  +
          216  +/*
          217  +** Usage:   pager_ckpt_commit ID
          218  +**
          219  +** Commit changes to a checkpoint
          220  +*/
          221  +static int pager_ckpt_commit(
          222  +  void *NotUsed,
          223  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
          224  +  int argc,              /* Number of arguments */
          225  +  char **argv            /* Text of each argument */
          226  +){
          227  +  Pager *pPager;
          228  +  int rc;
          229  +  if( argc!=2 ){
          230  +    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
          231  +       " ID\"", 0);
          232  +    return TCL_ERROR;
          233  +  }
          234  +  if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR;
          235  +  rc = sqlitepager_ckpt_commit(pPager);
   155    236     if( rc!=SQLITE_OK ){
   156    237       Tcl_AppendResult(interp, errorName(rc), 0);
   157    238       return TCL_ERROR;
   158    239     }
   159    240     return TCL_OK;
   160    241   }
   161    242   
................................................................................
   389    470   */
   390    471   int Sqlitetest2_Init(Tcl_Interp *interp){
   391    472     extern int sqlite_io_error_pending;
   392    473     Tcl_CreateCommand(interp, "pager_open", pager_open, 0, 0);
   393    474     Tcl_CreateCommand(interp, "pager_close", pager_close, 0, 0);
   394    475     Tcl_CreateCommand(interp, "pager_commit", pager_commit, 0, 0);
   395    476     Tcl_CreateCommand(interp, "pager_rollback", pager_rollback, 0, 0);
          477  +  Tcl_CreateCommand(interp, "pager_ckpt_begin", pager_ckpt_begin, 0, 0);
          478  +  Tcl_CreateCommand(interp, "pager_ckpt_commit", pager_ckpt_commit, 0, 0);
          479  +  Tcl_CreateCommand(interp, "pager_ckpt_rollback", pager_ckpt_rollback, 0, 0);
   396    480     Tcl_CreateCommand(interp, "pager_stats", pager_stats, 0, 0);
   397    481     Tcl_CreateCommand(interp, "pager_pagecount", pager_pagecount, 0, 0);
   398    482     Tcl_CreateCommand(interp, "page_get", page_get, 0, 0);
   399    483     Tcl_CreateCommand(interp, "page_lookup", page_lookup, 0, 0);
   400    484     Tcl_CreateCommand(interp, "page_unref", page_unref, 0, 0);
   401    485     Tcl_CreateCommand(interp, "page_read", page_read, 0, 0);
   402    486     Tcl_CreateCommand(interp, "page_write", page_write, 0, 0);
   403    487     Tcl_CreateCommand(interp, "page_number", page_number, 0, 0);
   404    488     Tcl_LinkVar(interp, "sqlite_io_error_pending",
   405    489        (char*)&sqlite_io_error_pending, TCL_LINK_INT);
   406    490     return TCL_OK;
   407    491   }

Changes to www/changes.tcl.

    17     17     puts "<DD><P><UL>$desc</UL></P></DD>"
    18     18   }
    19     19   
    20     20   chng {2002 Jan 30 (2.3.0 beta)} {
    21     21   <li>Added the ability to resolve constraint conflicts is ways other than
    22     22       an abort and rollback.  See the documentation on the "ON CONFLICT"
    23     23       clause for details.</li>
           24  +<li>Temporary files are now automatically deleted by the operating system
           25  +    when closed.  There are no more dangling temporary files on a program
           26  +    crash.  (If the OS crashes, fsck will delete the file after reboot 
           27  +    under Unix.  I do not know what happens under Windows.)</li>
    24     28   <li>NOT NULL constraints are honored.</li>
    25     29   <li>The COPY command puts NULLs in columns whose data is '\N'.</li>
    26     30   <li>In the COPY command, backslash can now be used to escape a newline.</li>
    27     31   }
    28     32   
    29     33   chng {2002 Jan 28 (2.2.5)} {
    30     34   <li>Important bug fix: the IN operator was not working if either the