Пример #1
0
    /*
    ** Playback savepoint pSavepoint. Or, if pSavepoint==NULL, then playback
    ** the entire master journal file. The case pSavepoint==NULL occurs when
    ** a ROLLBACK TO command is invoked on a SAVEPOINT that is a transaction
    ** savepoint.
    **
    ** When pSavepoint is not NULL (meaning a non-transaction savepoint is
    ** being rolled back), then the rollback consists of up to three stages,
    ** performed in the order specified:
    **
    **   * Pages are played back from the main journal starting at byte
    **     offset PagerSavepoint.iOffset and continuing to
    **     PagerSavepoint.iHdrOffset, or to the end of the main journal
    **     file if PagerSavepoint.iHdrOffset is zero.
    **
    **   * If PagerSavepoint.iHdrOffset is not zero, then pages are played
    **     back starting from the journal header immediately following
    **     PagerSavepoint.iHdrOffset to the end of the main journal file.
    **
    **   * Pages are then played back from the sub-journal file, starting
    **     with the PagerSavepoint.iSubRec and continuing to the end of
    **     the journal file.
    **
    ** Throughout the rollback process, each time a page is rolled back, the
    ** corresponding bit is set in a bitvec structure (variable pDone in the
    ** implementation below). This is used to ensure that a page is only
    ** rolled back the first time it is encountered in either journal.
    **
    ** If pSavepoint is NULL, then pages are only played back from the main
    ** journal file. There is no need for a bitvec in this case.
    **
    ** In either case, before playback commences the Pager.dbSize variable
    ** is reset to the value that it held at the start of the savepoint
    ** (or transaction). No page with a page-number greater than this value
    ** is played back. If one is encountered it is simply skipped.
    */
    static int pagerPlaybackSavepoint( Pager pPager, PagerSavepoint pSavepoint )
    {
      i64 szJ;                 /* Effective size of the main journal */
      i64 iHdrOff;             /* End of first segment of main-journal records */
      int rc = SQLITE_OK;      /* Return code */
      Bitvec pDone = null;     /* Bitvec to ensure pages played back only once */

      Debug.Assert( pPager.state >= PAGER_SHARED );
      /* Allocate a bitvec to use to store the set of pages rolled back */
      if ( pSavepoint != null )
      {
        pDone = sqlite3BitvecCreate( pSavepoint.nOrig );
        if ( null == pDone )
        {
          return SQLITE_NOMEM;
        }
      }

      /* Set the database size back to the value it was before the savepoint
      ** being reverted was opened.
      */
      pPager.dbSize = pSavepoint != null ? pSavepoint.nOrig : pPager.dbOrigSize;

      /* Use pPager.journalOff as the effective size of the main rollback
      ** journal.  The actual file might be larger than this in
      ** PAGER_JOURNALMODE_TRUNCATE or PAGER_JOURNALMODE_PERSIST.  But anything
      ** past pPager.journalOff is off-limits to us.
      */
      szJ = pPager.journalOff;

      /* Begin by rolling back records from the main journal starting at
      ** PagerSavepoint.iOffset and continuing to the next journal header.
      ** There might be records in the main journal that have a page number
      ** greater than the current database size (pPager.dbSize) but those
      ** will be skipped automatically.  Pages are added to pDone as they
      ** are played back.
      */
      if ( pSavepoint != null )
      {
        iHdrOff = pSavepoint.iHdrOffset != 0 ? pSavepoint.iHdrOffset : szJ;
        pPager.journalOff = pSavepoint.iOffset;
        while ( rc == SQLITE_OK && pPager.journalOff < iHdrOff )
        {
          rc = pager_playback_one_page( pPager, 1, 0, ref pPager.journalOff, 1, pDone );
        }
        Debug.Assert( rc != SQLITE_DONE );
      }
      else
      {
        pPager.journalOff = 0;
      }

      /* Continue rolling back records out of the main journal starting at
      ** the first journal header seen and continuing until the effective end
      ** of the main journal file.  Continue to skip out-of-range pages and
      ** continue adding pages rolled back to pDone.
      */
      while ( rc == SQLITE_OK && pPager.journalOff < szJ )
      {
        u32 ii;            /* Loop counter */
        u32 nJRec = 0;     /* Number of Journal Records */
        u32 dummy = 0;
        rc = readJournalHdr( pPager, 0, (int)szJ, ref nJRec, ref dummy );
        Debug.Assert( rc != SQLITE_DONE );

        /*
        ** The "pPager.journalHdr+JOURNAL_HDR_SZ(pPager)==pPager.journalOff"
        ** test is related to ticket #2565.  See the discussion in the
        ** pager_playback() function for additional information.
        */
        if ( nJRec == 0
        && pPager.journalHdr + JOURNAL_HDR_SZ( pPager ) == pPager.journalOff
        )
        {
          nJRec = (u32)( ( szJ - pPager.journalOff ) / JOURNAL_PG_SZ( pPager ) );
        }
        for ( ii = 0; rc == SQLITE_OK && ii < nJRec && pPager.journalOff < szJ; ii++ )
        {
          rc = pager_playback_one_page( pPager, 1, 0, ref pPager.journalOff, 1, pDone );
        }
        Debug.Assert( rc != SQLITE_DONE );
      }
      Debug.Assert( rc != SQLITE_OK || pPager.journalOff == szJ );

      /* Finally,  rollback pages from the sub-journal.  Page that were
      ** previously rolled back out of the main journal (and are hence in pDone)
      ** will be skipped.  Out-of-range pages are also skipped.
      */
      if ( pSavepoint != null )
      {
        u32 ii;            /* Loop counter */
        i64 offset = pSavepoint.iSubRec * ( 4 + pPager.pageSize );
        for ( ii = pSavepoint.iSubRec; rc == SQLITE_OK && ii < pPager.nSubRec; ii++ )
        {
          Debug.Assert( offset == ii * ( 4 + pPager.pageSize ) );
          rc = pager_playback_one_page( pPager, 0, 0, ref offset, 1, pDone );
        }
        Debug.Assert( rc != SQLITE_DONE );
      }

      sqlite3BitvecDestroy( ref pDone );
      if ( rc == SQLITE_OK )
      {
        pPager.journalOff = (int)szJ;
      }
      return rc;
    }
Пример #2
0
    /*
    ** Check that there are at least nSavepoint savepoints open. If there are
    ** currently less than nSavepoints open, then open one or more savepoints
    ** to make up the difference. If the number of savepoints is already
    ** equal to nSavepoint, then this function is a no-op.
    **
    ** If a memory allocation fails, SQLITE_NOMEM is returned. If an error
    ** occurs while opening the sub-journal file, then an IO error code is
    ** returned. Otherwise, SQLITE_OK.
    */
    static int sqlite3PagerOpenSavepoint( Pager pPager, int nSavepoint )
    {
      int rc = SQLITE_OK;                      /* Return code */
      int nCurrent = pPager.nSavepoint;        /* Current number of savepoints */

      if ( nSavepoint > nCurrent && pPager.useJournal != 0 )
      {
        int ii;                 /* Iterator variable */
        PagerSavepoint[] aNew;  /* New Pager.aSavepoint array */

        /* Either there is no active journal or the sub-journal is open or
        ** the journal is always stored in memory */
        Debug.Assert( pPager.nSavepoint == 0 || isOpen( pPager.sjfd ) ||
        pPager.journalMode == PAGER_JOURNALMODE_MEMORY );

        /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM
        ** if the allocation fails. Otherwise, zero the new portion in case a
        ** malloc failure occurs while populating it in the for(...) loop below.
        */
        //aNew = (PagerSavepoint *)sqlite3Realloc(
        //    pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint
        //);
        Array.Resize( ref pPager.aSavepoint, nSavepoint );
        aNew = pPager.aSavepoint;
        //if( null==aNew ){
        //  return SQLITE_NOMEM;
        //}
        // memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint));
        // pPager.aSavepoint = aNew;
        pPager.nSavepoint = nSavepoint;

        /* Populate the PagerSavepoint structures just allocated. */
        for ( ii = nCurrent; ii < nSavepoint; ii++ )
        {
          Debug.Assert( pPager.dbSizeValid );
          aNew[ii] = new PagerSavepoint();
          aNew[ii].nOrig = pPager.dbSize;
          if ( isOpen( pPager.jfd ) && ALWAYS( pPager.journalOff > 0 ) )
          {
            aNew[ii].iOffset = pPager.journalOff;
          }
          else
          {
            aNew[ii].iOffset = (int)JOURNAL_HDR_SZ( pPager );
          }
          aNew[ii].iSubRec = pPager.nSubRec;
          aNew[ii].pInSavepoint = sqlite3BitvecCreate( pPager.dbSize );
          if ( null == aNew[ii].pInSavepoint )
          {
            return SQLITE_NOMEM;
          }
        }

        /* Open the sub-journal, if it is not already opened. */
        rc = openSubJournal( pPager );
        assertTruncateConstraint( pPager );
      }

      return rc;
    }
Пример #3
0
        /*
        ** Check that there are at least nSavepoint savepoints open. If there are
        ** currently less than nSavepoints open, then open one or more savepoints
        ** to make up the difference. If the number of savepoints is already
        ** equal to nSavepoint, then this function is a no-op.
        **
        ** If a memory allocation fails, SQLITE_NOMEM is returned. If an error
        ** occurs while opening the sub-journal file, then an IO error code is
        ** returned. Otherwise, SQLITE_OK.
        */
        static int sqlite3PagerOpenSavepoint( Pager pPager, int nSavepoint )
        {
            int rc = SQLITE_OK;                      /* Return code */
              int nCurrent = pPager.nSavepoint;        /* Current number of savepoints */

              Debug.Assert( pPager.eState >= PAGER_WRITER_LOCKED );
              Debug.Assert( assert_pager_state( pPager ) );

              if ( nSavepoint > nCurrent && pPager.useJournal != 0 )
              {
            int ii;                 /* Iterator variable */
            PagerSavepoint[] aNew;  /* New Pager.aSavepoint array */

            /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM
            ** if the allocation fails. Otherwise, zero the new portion in case a
            ** malloc failure occurs while populating it in the for(...) loop below.
            */
            //aNew = (PagerSavepoint *)sqlite3Realloc(
            //    pPager.aSavepoint, sizeof(PagerSavepoint)*nSavepoint
            //);
            Array.Resize( ref pPager.aSavepoint, nSavepoint );
            aNew = pPager.aSavepoint;
            //if( null==aNew ){
            //  return SQLITE_NOMEM;
            //}
            // memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint));
            // pPager.aSavepoint = aNew;

            /* Populate the PagerSavepoint structures just allocated. */
            for ( ii = nCurrent; ii < nSavepoint; ii++ )
            {
              aNew[ii] = new PagerSavepoint();
              aNew[ii].nOrig = pPager.dbSize;
              if ( isOpen( pPager.jfd ) && pPager.journalOff > 0 )
              {
            aNew[ii].iOffset = pPager.journalOff;
              }
              else
              {
            aNew[ii].iOffset = (int)JOURNAL_HDR_SZ( pPager );
              }
              aNew[ii].iSubRec = pPager.nSubRec;
              aNew[ii].pInSavepoint = sqlite3BitvecCreate( pPager.dbSize );
              if ( null == aNew[ii].pInSavepoint )
              {
            return SQLITE_NOMEM;
              }
              if ( pagerUseWal( pPager ) )
              {
            sqlite3WalSavepoint( pPager.pWal, aNew[ii].aWalData );
              }
              pPager.nSavepoint = ii + 1;
            }
            Debug.Assert( pPager.nSavepoint == nSavepoint );
            assertTruncateConstraint( pPager );
              }

              return rc;
        }