/ Check-in [ab7aeeea]
Login

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

Overview
Comment:Simplifications and performance improvement in pager_write().
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: ab7aeeead395a05b91a921ef9ebe9252fffad667
User & Date: drh 2015-06-29 14:11:50
Context
2015-06-29
15:41
Fix minor problems in the ota demo application. check-in: 6aaaec6e user: dan tags: trunk
14:11
Simplifications and performance improvement in pager_write(). check-in: ab7aeeea user: drh tags: trunk
04:21
Add the new PGHDR_CLEAN bit to PgHdr.flags in pcache.c. This bit is always the opposite of PGHDR_DIRTY. Use the extra bit to avoid a comparison for a small performance boost. check-in: 8619fc34 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/bitvec.c.

   122    122   }
   123    123   
   124    124   /*
   125    125   ** Check to see if the i-th bit is set.  Return true or false.
   126    126   ** If p is NULL (if the bitmap has not been created) or if
   127    127   ** i is out of range, then return false.
   128    128   */
   129         -int sqlite3BitvecTest(Bitvec *p, u32 i){
   130         -  if( p==0 ) return 0;
          129  +int sqlite3BitvecTestNotNull(Bitvec *p, u32 i){
          130  +  assert( p!=0 );
   131    131     i--;
   132    132     if( i>=p->iSize ) return 0;
   133    133     while( p->iDivisor ){
   134    134       u32 bin = i/p->iDivisor;
   135    135       i = i%p->iDivisor;
   136    136       p = p->u.apSub[bin];
   137    137       if (!p) {
................................................................................
   145    145       while( p->u.aHash[h] ){
   146    146         if( p->u.aHash[h]==i ) return 1;
   147    147         h = (h+1) % BITVEC_NINT;
   148    148       }
   149    149       return 0;
   150    150     }
   151    151   }
          152  +int sqlite3BitvecTest(Bitvec *p, u32 i){
          153  +  return p!=0 && sqlite3BitvecTestNotNull(p,i);
          154  +}
   152    155   
   153    156   /*
   154    157   ** Set the i-th bit.  Return 0 on success and an error code if
   155    158   ** anything goes wrong.
   156    159   **
   157    160   ** This routine might cause sub-bitmaps to be allocated.  Failing
   158    161   ** to get the memory needed to hold the sub-bitmap is the only

Changes to src/pager.c.

   804    804   **
   805    805   **   if( isOpen(pPager->jfd) ){ ...
   806    806   **
   807    807   ** instead of
   808    808   **
   809    809   **   if( pPager->jfd->pMethods ){ ...
   810    810   */
   811         -#define isOpen(pFd) ((pFd)->pMethods)
          811  +#define isOpen(pFd) ((pFd)->pMethods!=0)
   812    812   
   813    813   /*
   814    814   ** Return true if this pager uses a write-ahead log instead of the usual
   815    815   ** rollback journal. Otherwise false.
   816    816   */
   817    817   #ifndef SQLITE_OMIT_WAL
   818    818   static int pagerUseWal(Pager *pPager){
................................................................................
  1027   1027   static int subjRequiresPage(PgHdr *pPg){
  1028   1028     Pager *pPager = pPg->pPager;
  1029   1029     PagerSavepoint *p;
  1030   1030     Pgno pgno = pPg->pgno;
  1031   1031     int i;
  1032   1032     for(i=0; i<pPager->nSavepoint; i++){
  1033   1033       p = &pPager->aSavepoint[i];
  1034         -    if( p->nOrig>=pgno && 0==sqlite3BitvecTest(p->pInSavepoint, pgno) ){
         1034  +    if( p->nOrig>=pgno && 0==sqlite3BitvecTestNotNull(p->pInSavepoint, pgno) ){
  1035   1035         return 1;
  1036   1036       }
  1037   1037     }
  1038   1038     return 0;
  1039   1039   }
  1040   1040   
         1041  +#ifdef SQLITE_DEBUG
  1041   1042   /*
  1042   1043   ** Return true if the page is already in the journal file.
  1043   1044   */
  1044   1045   static int pageInJournal(Pager *pPager, PgHdr *pPg){
  1045   1046     return sqlite3BitvecTest(pPager->pInJournal, pPg->pgno);
  1046   1047   }
         1048  +#endif
  1047   1049   
  1048   1050   /*
  1049   1051   ** Read a 32-bit integer from the given file descriptor.  Store the integer
  1050   1052   ** that is read in *pRes.  Return SQLITE_OK if everything worked, or an
  1051   1053   ** error code is something goes wrong.
  1052   1054   **
  1053   1055   ** All values are stored on disk as big-endian.
................................................................................
  5643   5645       assert( rc!=SQLITE_OK || pPager->eState==PAGER_WRITER_LOCKED );
  5644   5646       assert( assert_pager_state(pPager) );
  5645   5647     }
  5646   5648   
  5647   5649     PAGERTRACE(("TRANSACTION %d\n", PAGERID(pPager)));
  5648   5650     return rc;
  5649   5651   }
         5652  +
         5653  +/*
         5654  +** Write page pPg onto the end of the rollback journal.
         5655  +*/
         5656  +static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){
         5657  +  Pager *pPager = pPg->pPager;
         5658  +  int rc;
         5659  +  u32 cksum;
         5660  +  char *pData2;
         5661  +  i64 iOff = pPager->journalOff;
         5662  +
         5663  +  /* We should never write to the journal file the page that
         5664  +  ** contains the database locks.  The following assert verifies
         5665  +  ** that we do not. */
         5666  +  assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
         5667  +
         5668  +  assert( pPager->journalHdr<=pPager->journalOff );
         5669  +  CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
         5670  +  cksum = pager_cksum(pPager, (u8*)pData2);
         5671  +
         5672  +  /* Even if an IO or diskfull error occurs while journalling the
         5673  +  ** page in the block above, set the need-sync flag for the page.
         5674  +  ** Otherwise, when the transaction is rolled back, the logic in
         5675  +  ** playback_one_page() will think that the page needs to be restored
         5676  +  ** in the database file. And if an IO error occurs while doing so,
         5677  +  ** then corruption may follow.
         5678  +  */
         5679  +  pPg->flags |= PGHDR_NEED_SYNC;
         5680  +
         5681  +  rc = write32bits(pPager->jfd, iOff, pPg->pgno);
         5682  +  if( rc!=SQLITE_OK ) return rc;
         5683  +  rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize, iOff+4);
         5684  +  if( rc!=SQLITE_OK ) return rc;
         5685  +  rc = write32bits(pPager->jfd, iOff+pPager->pageSize+4, cksum);
         5686  +  if( rc!=SQLITE_OK ) return rc;
         5687  +
         5688  +  IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno, 
         5689  +           pPager->journalOff, pPager->pageSize));
         5690  +  PAGER_INCR(sqlite3_pager_writej_count);
         5691  +  PAGERTRACE(("JOURNAL %d page %d needSync=%d hash(%08x)\n",
         5692  +       PAGERID(pPager), pPg->pgno, 
         5693  +       ((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg)));
         5694  +
         5695  +  pPager->journalOff += 8 + pPager->pageSize;
         5696  +  pPager->nRec++;
         5697  +  assert( pPager->pInJournal!=0 );
         5698  +  rc = sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
         5699  +  testcase( rc==SQLITE_NOMEM );
         5700  +  assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
         5701  +  rc |= addToSavepointBitvecs(pPager, pPg->pgno);
         5702  +  assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
         5703  +  return rc;
         5704  +}
  5650   5705   
  5651   5706   /*
  5652   5707   ** Mark a single data page as writeable. The page is written into the 
  5653   5708   ** main journal or sub-journal as required. If the page is written into
  5654   5709   ** one of the journals, the corresponding bit is set in the 
  5655   5710   ** Pager.pInJournal bitvec and the PagerSavepoint.pInSavepoint bitvecs
  5656   5711   ** of any open savepoints as appropriate.
  5657   5712   */
  5658   5713   static int pager_write(PgHdr *pPg){
  5659   5714     Pager *pPager = pPg->pPager;
  5660   5715     int rc = SQLITE_OK;
  5661         -  int inJournal;
  5662   5716   
  5663   5717     /* This routine is not called unless a write-transaction has already 
  5664   5718     ** been started. The journal file may or may not be open at this point.
  5665   5719     ** It is never called in the ERROR state.
  5666   5720     */
  5667   5721     assert( pPager->eState==PAGER_WRITER_LOCKED
  5668   5722          || pPager->eState==PAGER_WRITER_CACHEMOD
  5669   5723          || pPager->eState==PAGER_WRITER_DBMOD
  5670   5724     );
  5671   5725     assert( assert_pager_state(pPager) );
  5672   5726     assert( pPager->errCode==0 );
  5673   5727     assert( pPager->readOnly==0 );
  5674         -
  5675   5728     CHECK_PAGE(pPg);
  5676   5729   
  5677   5730     /* The journal file needs to be opened. Higher level routines have already
  5678   5731     ** obtained the necessary locks to begin the write-transaction, but the
  5679   5732     ** rollback journal might not yet be open. Open it now if this is the case.
  5680   5733     **
  5681   5734     ** This is done before calling sqlite3PcacheMakeDirty() on the page. 
................................................................................
  5686   5739     if( pPager->eState==PAGER_WRITER_LOCKED ){
  5687   5740       rc = pager_open_journal(pPager);
  5688   5741       if( rc!=SQLITE_OK ) return rc;
  5689   5742     }
  5690   5743     assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
  5691   5744     assert( assert_pager_state(pPager) );
  5692   5745   
  5693         -  /* Mark the page as dirty.  If the page has already been written
  5694         -  ** to the journal then we can return right away.
  5695         -  */
         5746  +  /* Mark the page that is about to be modified as dirty. */
  5696   5747     sqlite3PcacheMakeDirty(pPg);
  5697         -  inJournal = pageInJournal(pPager, pPg);
  5698         -  if( inJournal && (pPager->nSavepoint==0 || !subjRequiresPage(pPg)) ){
  5699         -    assert( !pagerUseWal(pPager) );
  5700         -  }else{
  5701         -  
  5702         -    /* The transaction journal now exists and we have a RESERVED or an
  5703         -    ** EXCLUSIVE lock on the main database file.  Write the current page to
  5704         -    ** the transaction journal if it is not there already.
  5705         -    */
  5706         -    if( !inJournal && !pagerUseWal(pPager) ){
  5707         -      assert( pagerUseWal(pPager)==0 );
  5708         -      if( pPg->pgno<=pPager->dbOrigSize && isOpen(pPager->jfd) ){
  5709         -        u32 cksum;
  5710         -        char *pData2;
  5711         -        i64 iOff = pPager->journalOff;
  5712         -
  5713         -        /* We should never write to the journal file the page that
  5714         -        ** contains the database locks.  The following assert verifies
  5715         -        ** that we do not. */
  5716         -        assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
  5717         -
  5718         -        assert( pPager->journalHdr<=pPager->journalOff );
  5719         -        CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
  5720         -        cksum = pager_cksum(pPager, (u8*)pData2);
  5721         -
  5722         -        /* Even if an IO or diskfull error occurs while journalling the
  5723         -        ** page in the block above, set the need-sync flag for the page.
  5724         -        ** Otherwise, when the transaction is rolled back, the logic in
  5725         -        ** playback_one_page() will think that the page needs to be restored
  5726         -        ** in the database file. And if an IO error occurs while doing so,
  5727         -        ** then corruption may follow.
  5728         -        */
         5748  +
         5749  +  /* If a rollback journal is in use, them make sure the page that is about
         5750  +  ** to change is in the rollback journal, or if the page is a new page off
         5751  +  ** then end of the file, make sure it is marked as PGHDR_NEED_SYNC.
         5752  +  */
         5753  +  assert( (pPager->pInJournal!=0) == isOpen(pPager->jfd) );
         5754  +  if( pPager->pInJournal!=0                                      /* Journal open */
         5755  +   && sqlite3BitvecTestNotNull(pPager->pInJournal, pPg->pgno)==0 /* pPg not in jrnl */
         5756  +  ){
         5757  +    assert( pagerUseWal(pPager)==0 );
         5758  +    if( pPg->pgno<=pPager->dbOrigSize ){
         5759  +      rc = pagerAddPageToRollbackJournal(pPg);
         5760  +      if( rc!=SQLITE_OK ){
         5761  +        return rc;
         5762  +      }
         5763  +    }else{
         5764  +      if( pPager->eState!=PAGER_WRITER_DBMOD ){
  5729   5765           pPg->flags |= PGHDR_NEED_SYNC;
  5730         -
  5731         -        rc = write32bits(pPager->jfd, iOff, pPg->pgno);
  5732         -        if( rc!=SQLITE_OK ) return rc;
  5733         -        rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize, iOff+4);
  5734         -        if( rc!=SQLITE_OK ) return rc;
  5735         -        rc = write32bits(pPager->jfd, iOff+pPager->pageSize+4, cksum);
  5736         -        if( rc!=SQLITE_OK ) return rc;
  5737         -
  5738         -        IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno, 
  5739         -                 pPager->journalOff, pPager->pageSize));
  5740         -        PAGER_INCR(sqlite3_pager_writej_count);
  5741         -        PAGERTRACE(("JOURNAL %d page %d needSync=%d hash(%08x)\n",
  5742         -             PAGERID(pPager), pPg->pgno, 
  5743         -             ((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg)));
  5744         -
  5745         -        pPager->journalOff += 8 + pPager->pageSize;
  5746         -        pPager->nRec++;
  5747         -        assert( pPager->pInJournal!=0 );
  5748         -        rc = sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
  5749         -        testcase( rc==SQLITE_NOMEM );
  5750         -        assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
  5751         -        rc |= addToSavepointBitvecs(pPager, pPg->pgno);
  5752         -        if( rc!=SQLITE_OK ){
  5753         -          assert( rc==SQLITE_NOMEM );
  5754         -          return rc;
  5755         -        }
  5756         -      }else{
  5757         -        if( pPager->eState!=PAGER_WRITER_DBMOD ){
  5758         -          pPg->flags |= PGHDR_NEED_SYNC;
  5759         -        }
  5760         -        PAGERTRACE(("APPEND %d page %d needSync=%d\n",
  5761         -                PAGERID(pPager), pPg->pgno,
  5762         -               ((pPg->flags&PGHDR_NEED_SYNC)?1:0)));
  5763   5766         }
         5767  +      PAGERTRACE(("APPEND %d page %d needSync=%d\n",
         5768  +              PAGERID(pPager), pPg->pgno,
         5769  +             ((pPg->flags&PGHDR_NEED_SYNC)?1:0)));
  5764   5770       }
         5771  +  }
  5765   5772     
  5766         -    /* If the statement journal is open and the page is not in it,
  5767         -    ** then write the current page to the statement journal.  Note that
  5768         -    ** the statement journal format differs from the standard journal format
  5769         -    ** in that it omits the checksums and the header.
  5770         -    */
  5771         -    if( pPager->nSavepoint>0 && subjRequiresPage(pPg) ){
  5772         -      rc = subjournalPage(pPg);
  5773         -    }
         5773  +  /* If the statement journal is open and the page is not in it,
         5774  +  ** then write the page into the statement journal.
         5775  +  */
         5776  +  if( pPager->nSavepoint>0 && subjRequiresPage(pPg) ){
         5777  +    rc = subjournalPage(pPg);
  5774   5778     }
  5775   5779   
  5776         -  /* Update the database size and return.
  5777         -  */
         5780  +  /* Update the database size and return. */
  5778   5781     if( pPager->dbSize<pPg->pgno ){
  5779   5782       pPager->dbSize = pPg->pgno;
  5780   5783     }
  5781   5784     return rc;
  5782   5785   }
  5783   5786   
  5784   5787   /*

Changes to src/sqliteInt.h.

  3234   3234   # define sqlite3FaultSim(X) SQLITE_OK
  3235   3235   #else
  3236   3236     int sqlite3FaultSim(int);
  3237   3237   #endif
  3238   3238   
  3239   3239   Bitvec *sqlite3BitvecCreate(u32);
  3240   3240   int sqlite3BitvecTest(Bitvec*, u32);
         3241  +int sqlite3BitvecTestNotNull(Bitvec*, u32);
  3241   3242   int sqlite3BitvecSet(Bitvec*, u32);
  3242   3243   void sqlite3BitvecClear(Bitvec*, u32, void*);
  3243   3244   void sqlite3BitvecDestroy(Bitvec*);
  3244   3245   u32 sqlite3BitvecSize(Bitvec*);
  3245   3246   #ifndef SQLITE_OMIT_BUILTIN_TEST
  3246   3247   int sqlite3BitvecBuiltinTest(int,int*);
  3247   3248   #endif