Beispiel #1
0
static int interiorCursorNextPage(RecoverInteriorCursor **ppCursor,
                                  DbPage **ppPage){
  RecoverInteriorCursor *pCursor = *ppCursor;
  while( 1 ){
    int rc;
    const unsigned char *pPageHeader;  

    
    while( pCursor->iChild<pCursor->nChildren ){
      const unsigned iPage = interiorCursorChildPage(pCursor);
      pCursor->iChild++;
      if( interiorCursorPageInUse(pCursor, iPage) ){
        fprintf(stderr, "Loop detected at %d\n", iPage);
      }else{
        int rc = sqlite3PagerAcquire(pCursor->pPage->pPager, iPage, ppPage, 0);
        if( rc==SQLITE_OK ){
          return SQLITE_ROW;
        }
      }
    }

    
    if( !pCursor->pParent ){
      return SQLITE_DONE;
    }
    rc = interiorCursorNextPage(&pCursor->pParent, ppPage);
    if( rc!=SQLITE_ROW ){
      return rc;
    }

    pPageHeader = PageHeader(*ppPage);
    if( pPageHeader[kiPageTypeOffset]!=kTableInteriorPage ){
      *ppCursor = pCursor->pParent;
      pCursor->pParent = NULL;
      interiorCursorDestroy(pCursor);
      return SQLITE_ROW;
    }

    
    interiorCursorSetPage(pCursor, *ppPage);
    *ppPage = NULL;
  }

  assert(NULL);  
  return SQLITE_CORRUPT;
}
Beispiel #2
0
static int leafCursorCreate(Pager *pPager, unsigned nPageSize,
                            u32 iRootPage, RecoverLeafCursor **ppCursor){
  DbPage *pPage;               
  RecoverLeafCursor *pCursor;  
  int rc;

  
  rc = sqlite3PagerAcquire(pPager, iRootPage, &pPage, 0);
  if( rc!=SQLITE_OK ){
    return rc;
  }

  pCursor = sqlite3_malloc(sizeof(RecoverLeafCursor));
  if( !pCursor ){
    sqlite3PagerUnref(pPage);
    return SQLITE_NOMEM;
  }
  memset(pCursor, 0, sizeof(*pCursor));

  pCursor->nPageSize = nPageSize;

  rc = leafCursorLoadPage(pCursor, pPage);
  if( rc!=SQLITE_OK ){
    sqlite3PagerUnref(pPage);
    leafCursorDestroy(pCursor);
    return rc;
  }

  
  if( !pCursor->pPage ){
    rc = leafCursorNextPage(pCursor);
    if( rc!=SQLITE_DONE && rc!=SQLITE_ROW ){
      leafCursorDestroy(pCursor);
      return rc;
    }
  }

  *ppCursor = pCursor;
  return SQLITE_OK;
}
Beispiel #3
0
static int overflowMaybeCreate(DbPage *pPage, unsigned nPageSize,
                               unsigned iRecordOffset, unsigned nRecordBytes,
                               unsigned *pnLocalRecordBytes,
                               RecoverOverflow **ppOverflow){
  unsigned nLocalRecordBytes;  
  unsigned iNextPage;          
  unsigned nBytes;             
  int rc;
  RecoverOverflow *pFirstOverflow;  
  RecoverOverflow *pLastOverflow;   

  const unsigned maxLocal = nPageSize - 35;
  const unsigned minLocal = ((nPageSize-12)*32/255)-23;  

  
  if( nRecordBytes<=maxLocal ){
    *pnLocalRecordBytes = nRecordBytes;
    *ppOverflow = NULL;
    return SQLITE_OK;
  }

  nLocalRecordBytes = minLocal+((nRecordBytes-minLocal)%(nPageSize-4));
  if( maxLocal<nLocalRecordBytes ){
    nLocalRecordBytes = minLocal;
  }

  
  if( iRecordOffset+nLocalRecordBytes+4>nPageSize ){
    return SQLITE_CORRUPT;
  }

  
  iNextPage =
      decodeUnsigned32(PageData(pPage, iRecordOffset + nLocalRecordBytes));
  nBytes = nLocalRecordBytes;

  pFirstOverflow = pLastOverflow = NULL;
  rc = SQLITE_OK;
  while( iNextPage && nBytes<nRecordBytes ){
    RecoverOverflow *pOverflow;  

    rc = sqlite3PagerAcquire(pPage->pPager, iNextPage, &pPage, 0);
    if( rc!=SQLITE_OK ){
      break;
    }

    pOverflow = sqlite3_malloc(sizeof(RecoverOverflow));
    if( !pOverflow ){
      sqlite3PagerUnref(pPage);
      rc = SQLITE_NOMEM;
      break;
    }
    memset(pOverflow, 0, sizeof(*pOverflow));
    pOverflow->pPage = pPage;
    pOverflow->nPageSize = nPageSize;

    if( !pFirstOverflow ){
      pFirstOverflow = pOverflow;
    }else{
      pLastOverflow->pNextOverflow = pOverflow;
    }
    pLastOverflow = pOverflow;

    iNextPage = decodeUnsigned32(pPage->pData);
    nBytes += nPageSize-4;

    
    if( overflowPageInUse(pFirstOverflow, iNextPage) ){
      fprintf(stderr, "Overflow loop detected at %d\n", iNextPage);
      rc = SQLITE_CORRUPT;
      break;
    }
  }

  if( rc==SQLITE_OK && (nBytes<nRecordBytes || iNextPage) ){
    rc = SQLITE_CORRUPT;
  }

  if( rc==SQLITE_OK ){
    *ppOverflow = pFirstOverflow;
    *pnLocalRecordBytes = nLocalRecordBytes;
  }else if( pFirstOverflow ){
    overflowDestroy(pFirstOverflow);
  }
  return rc;
}
Beispiel #4
0
/*
** Copy nPage pages from the source b-tree to the destination.
*/
int sqlite3_backup_step(sqlite3_backup *p, int nPage) {
    int rc;
    int destMode;       /* Destination journal mode */
    int pgszSrc = 0;    /* Source page size */
    int pgszDest = 0;   /* Destination page size */

    sqlite3_mutex_enter(p->pSrcDb->mutex);
    sqlite3BtreeEnter(p->pSrc);
    if( p->pDestDb ) {
        sqlite3_mutex_enter(p->pDestDb->mutex);
    }

    rc = p->rc;
    if( !isFatalError(rc) ) {
        Pager * const pSrcPager = sqlite3BtreePager(p->pSrc);     /* Source pager */
        Pager * const pDestPager = sqlite3BtreePager(p->pDest);   /* Dest pager */
        int ii;                            /* Iterator variable */
        int nSrcPage = -1;                 /* Size of source db in pages */
        int bCloseTrans = 0;               /* True if src db requires unlocking */

        /* If the source pager is currently in a write-transaction, return
        ** SQLITE_BUSY immediately.
        */
        if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ) {
            rc = SQLITE_BUSY;
        } else {
            rc = SQLITE_OK;
        }

        /* Lock the destination database, if it is not locked already. */
        if( SQLITE_OK==rc && p->bDestLocked==0
                && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2))
          ) {
            p->bDestLocked = 1;
            sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema);
        }

        /* If there is no open read-transaction on the source database, open
        ** one now. If a transaction is opened here, then it will be closed
        ** before this function exits.
        */
        if( rc==SQLITE_OK && 0==sqlite3BtreeIsInReadTrans(p->pSrc) ) {
            rc = sqlite3BtreeBeginTrans(p->pSrc, 0);
            bCloseTrans = 1;
        }

        /* Do not allow backup if the destination database is in WAL mode
        ** and the page sizes are different between source and destination */
        pgszSrc = sqlite3BtreeGetPageSize(p->pSrc);
        pgszDest = sqlite3BtreeGetPageSize(p->pDest);
        destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest));
        if( SQLITE_OK==rc && destMode==PAGER_JOURNALMODE_WAL && pgszSrc!=pgszDest ) {
            rc = SQLITE_READONLY;
        }

        /* Now that there is a read-lock on the source database, query the
        ** source pager for the number of pages in the database.
        */
        nSrcPage = (int)sqlite3BtreeLastPage(p->pSrc);
        assert( nSrcPage>=0 );
        for(ii=0; (nPage<0 || ii<nPage) && p->iNext<=(Pgno)nSrcPage && !rc; ii++) {
            const Pgno iSrcPg = p->iNext;                 /* Source page number */
            if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ) {
                DbPage *pSrcPg;                             /* Source page object */
                rc = sqlite3PagerAcquire(pSrcPager, iSrcPg, &pSrcPg,
                                         PAGER_ACQUIRE_READONLY);
                if( rc==SQLITE_OK ) {
                    rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg), 0);
                    sqlite3PagerUnref(pSrcPg);
                }
            }
            p->iNext++;
        }
        if( rc==SQLITE_OK ) {
            p->nPagecount = nSrcPage;
            p->nRemaining = nSrcPage+1-p->iNext;
            if( p->iNext>(Pgno)nSrcPage ) {
                rc = SQLITE_DONE;
            } else if( !p->isAttached ) {
                attachBackupObject(p);
            }
        }

        /* Update the schema version field in the destination database. This
        ** is to make sure that the schema-version really does change in
        ** the case where the source and destination databases have the
        ** same schema version.
        */
        if( rc==SQLITE_DONE ) {
            if( nSrcPage==0 ) {
                rc = sqlite3BtreeNewDb(p->pDest);
                nSrcPage = 1;
            }
            if( rc==SQLITE_OK || rc==SQLITE_DONE ) {
                rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1);
            }
            if( rc==SQLITE_OK ) {
                if( p->pDestDb ) {
                    sqlite3ResetAllSchemasOfConnection(p->pDestDb);
                }
                if( destMode==PAGER_JOURNALMODE_WAL ) {
                    rc = sqlite3BtreeSetVersion(p->pDest, 2);
                }
            }
            if( rc==SQLITE_OK ) {
                int nDestTruncate;
                /* Set nDestTruncate to the final number of pages in the destination
                ** database. The complication here is that the destination page
                ** size may be different to the source page size.
                **
                ** If the source page size is smaller than the destination page size,
                ** round up. In this case the call to sqlite3OsTruncate() below will
                ** fix the size of the file. However it is important to call
                ** sqlite3PagerTruncateImage() here so that any pages in the
                ** destination file that lie beyond the nDestTruncate page mark are
                ** journalled by PagerCommitPhaseOne() before they are destroyed
                ** by the file truncation.
                */
                assert( pgszSrc==sqlite3BtreeGetPageSize(p->pSrc) );
                assert( pgszDest==sqlite3BtreeGetPageSize(p->pDest) );
                if( pgszSrc<pgszDest ) {
                    int ratio = pgszDest/pgszSrc;
                    nDestTruncate = (nSrcPage+ratio-1)/ratio;
                    if( nDestTruncate==(int)PENDING_BYTE_PAGE(p->pDest->pBt) ) {
                        nDestTruncate--;
                    }
                } else {
                    nDestTruncate = nSrcPage * (pgszSrc/pgszDest);
                }
                assert( nDestTruncate>0 );

                if( pgszSrc<pgszDest ) {
                    /* If the source page-size is smaller than the destination page-size,
                    ** two extra things may need to happen:
                    **
                    **   * The destination may need to be truncated, and
                    **
                    **   * Data stored on the pages immediately following the
                    **     pending-byte page in the source database may need to be
                    **     copied into the destination database.
                    */
                    const i64 iSize = (i64)pgszSrc * (i64)nSrcPage;
                    sqlite3_file * const pFile = sqlite3PagerFile(pDestPager);
                    Pgno iPg;
                    int nDstPage;
                    i64 iOff;
                    i64 iEnd;

                    assert( pFile );
                    assert( nDestTruncate==0
                            || (i64)nDestTruncate*(i64)pgszDest >= iSize || (
                                nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1)
                                && iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest
                            ));

                    /* This block ensures that all data required to recreate the original
                    ** database has been stored in the journal for pDestPager and the
                    ** journal synced to disk. So at this point we may safely modify
                    ** the database file in any way, knowing that if a power failure
                    ** occurs, the original database will be reconstructed from the
                    ** journal file.  */
                    sqlite3PagerPagecount(pDestPager, &nDstPage);
                    for(iPg=nDestTruncate; rc==SQLITE_OK && iPg<=(Pgno)nDstPage; iPg++) {
                        if( iPg!=PENDING_BYTE_PAGE(p->pDest->pBt) ) {
                            DbPage *pPg;
                            rc = sqlite3PagerGet(pDestPager, iPg, &pPg);
                            if( rc==SQLITE_OK ) {
                                rc = sqlite3PagerWrite(pPg);
                                sqlite3PagerUnref(pPg);
                            }
                        }
                    }
                    if( rc==SQLITE_OK ) {
                        rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1);
                    }

                    /* Write the extra pages and truncate the database file as required */
                    iEnd = MIN(PENDING_BYTE + pgszDest, iSize);
                    for(
                        iOff=PENDING_BYTE+pgszSrc;
                        rc==SQLITE_OK && iOff<iEnd;
                        iOff+=pgszSrc
                    ) {
                        PgHdr *pSrcPg = 0;
                        const Pgno iSrcPg = (Pgno)((iOff/pgszSrc)+1);
                        rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
                        if( rc==SQLITE_OK ) {
                            u8 *zData = sqlite3PagerGetData(pSrcPg);
                            rc = sqlite3OsWrite(pFile, zData, pgszSrc, iOff);
                        }
                        sqlite3PagerUnref(pSrcPg);
                    }
                    if( rc==SQLITE_OK ) {
                        rc = backupTruncateFile(pFile, iSize);
                    }

                    /* Sync the database file to disk. */
                    if( rc==SQLITE_OK ) {
                        rc = sqlite3PagerSync(pDestPager);
                    }
                } else {
                    sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
                    rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0);
                }

                /* Finish committing the transaction to the destination database. */
                if( SQLITE_OK==rc
                        && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0))
                  ) {
                    rc = SQLITE_DONE;
                }
            }
        }

        /* If bCloseTrans is true, then this function opened a read transaction
        ** on the source database. Close the read transaction here. There is
        ** no need to check the return values of the btree methods here, as
        ** "committing" a read-only transaction cannot fail.
        */
        if( bCloseTrans ) {
            TESTONLY( int rc2 );
            TESTONLY( rc2  = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0);
            TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc, 0);
            assert( rc2==SQLITE_OK );
        }

        if( rc==SQLITE_IOERR_NOMEM ) {
            rc = SQLITE_NOMEM;
        }
        p->rc = rc;
    }