pager.c

来自「sqlite最新源码」· C语言 代码 · 共 1,620 行 · 第 1/5 页

C
1,620
字号
      }    }else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){      rc = sqlite3OsTruncate(pPager->jfd, 0);      pPager->journalOff = 0;      pPager->journalStarted = 0;    }else if( pPager->exclusiveMode      || pPager->journalMode==PAGER_JOURNALMODE_PERSIST    ){      rc = zeroJournalHdr(pPager, hasMaster);      pager_error(pPager, rc);      pPager->journalOff = 0;      pPager->journalStarted = 0;    }else{      assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE || rc );      sqlite3OsClose(pPager->jfd);      if( rc==SQLITE_OK && !pPager->tempFile ){        rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);      }    }#ifdef SQLITE_CHECK_PAGES    sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash);#endif    sqlite3PcacheCleanAll(pPager->pPCache);    sqlite3BitvecDestroy(pPager->pInJournal);    pPager->pInJournal = 0;    pPager->nRec = 0;  }  if( !pPager->exclusiveMode ){    rc2 = osUnlock(pPager->fd, SHARED_LOCK);    pPager->state = PAGER_SHARED;    pPager->changeCountDone = 0;  }else if( pPager->state==PAGER_SYNCED ){    pPager->state = PAGER_EXCLUSIVE;  }  pPager->setMaster = 0;  pPager->needSync = 0;  pPager->dbModified = 0;  /* TODO: Is this optimal? Why is the db size invalidated here   ** when the database file is not unlocked? */  pPager->dbOrigSize = 0;  sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);  if( !MEMDB ){    pPager->dbSizeValid = 0;  }  return (rc==SQLITE_OK?rc2:rc);}/*** Parameter aData must point to a buffer of pPager->pageSize bytes** of data. Compute and return a checksum based ont the contents of the ** page of data and the current value of pPager->cksumInit.**** This is not a real checksum. It is really just the sum of the ** random initial value (pPager->cksumInit) and every 200th byte** of the page data, starting with byte offset (pPager->pageSize%200).** Each byte is interpreted as an 8-bit unsigned integer.**** Changing the formula used to compute this checksum results in an** incompatible journal file format.**** If journal corruption occurs due to a power failure, the most likely ** scenario is that one end or the other of the record will be changed. ** It is much less likely that the two ends of the journal record will be** correct and the middle be corrupt.  Thus, this "checksum" scheme,** though fast and simple, catches the mostly likely kind of corruption.*/static u32 pager_cksum(Pager *pPager, const u8 *aData){  u32 cksum = pPager->cksumInit;         /* Checksum value to return */  int i = pPager->pageSize-200;          /* Loop counter */  while( i>0 ){    cksum += aData[i];    i -= 200;  }  return cksum;}/*** Read a single page from either the journal file (if isMainJrnl==1) or** from the sub-journal (if isMainJrnl==0) and playback that page.** The page begins at offset *pOffset into the file. The *pOffset** value is increased to the start of the next page in the journal.**** The isMainJrnl flag is true if this is the main rollback journal and** false for the statement journal.  The main rollback journal uses** checksums - the statement journal does not.**** If the page number of the page record read from the (sub-)journal file** is greater than the current value of Pager.dbSize, then playback is** skipped and SQLITE_OK is returned.**** If pDone is not NULL, then it is a record of pages that have already** been played back.  If the page at *pOffset has already been played back** (if the corresponding pDone bit is set) then skip the playback.** Make sure the pDone bit corresponding to the *pOffset page is set** prior to returning.**** If the page record is successfully read from the (sub-)journal file** and played back, then SQLITE_OK is returned. If an IO error occurs** while reading the record from the (sub-)journal file or while writing** to the database file, then the IO error code is returned. If data** is successfully read from the (sub-)journal file but appears to be** corrupted, SQLITE_DONE is returned. Data is considered corrupted in** two circumstances:** **   * If the record page-number is illegal (0 or PAGER_MJ_PGNO), or**   * If the record is being rolled back from the main journal file**     and the checksum field does not match the record content.**** Neither of these two scenarios are possible during a savepoint rollback.**** If this is a savepoint rollback, then memory may have to be dynamically** allocated by this function. If this is the case and an allocation fails,** SQLITE_NOMEM is returned.*/static int pager_playback_one_page(  Pager *pPager,                /* The pager being played back */  int isMainJrnl,               /* 1 -> main journal. 0 -> sub-journal. */  int isUnsync,                 /* True if reading from unsynced main journal */  i64 *pOffset,                 /* Offset of record to playback */  int isSavepnt,                /* True for a savepoint rollback */  Bitvec *pDone                 /* Bitvec of pages already played back */){  int rc;  PgHdr *pPg;                   /* An existing page in the cache */  Pgno pgno;                    /* The page number of a page in journal */  u32 cksum;                    /* Checksum used for sanity checking */  u8 *aData;                    /* Temporary storage for the page */  sqlite3_file *jfd;            /* The file descriptor for the journal file */  assert( (isMainJrnl&~1)==0 );      /* isMainJrnl is 0 or 1 */  assert( (isSavepnt&~1)==0 );       /* isSavepnt is 0 or 1 */  assert( isMainJrnl || pDone );     /* pDone always used on sub-journals */  assert( isSavepnt || pDone==0 );   /* pDone never used on non-savepoint */  aData = (u8*)pPager->pTmpSpace;  assert( aData );         /* Temp storage must have already been allocated */  /* Read the page number and page data from the journal or sub-journal  ** file. Return an error code to the caller if an IO error occurs.  */  jfd = isMainJrnl ? pPager->jfd : pPager->sjfd;  rc = read32bits(jfd, *pOffset, &pgno);  if( rc!=SQLITE_OK ) return rc;  rc = sqlite3OsRead(jfd, aData, pPager->pageSize, (*pOffset)+4);  if( rc!=SQLITE_OK ) return rc;  *pOffset += pPager->pageSize + 4 + isMainJrnl*4;  /* Sanity checking on the page.  This is more important that I originally  ** thought.  If a power failure occurs while the journal is being written,  ** it could cause invalid data to be written into the journal.  We need to  ** detect this invalid data (with high probability) and ignore it.  */  if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){    assert( !isSavepnt );    return SQLITE_DONE;  }  if( pgno>(Pgno)pPager->dbSize || sqlite3BitvecTest(pDone, pgno) ){    return SQLITE_OK;  }  if( isMainJrnl ){    rc = read32bits(jfd, (*pOffset)-4, &cksum);    if( rc ) return rc;    if( !isSavepnt && pager_cksum(pPager, aData)!=cksum ){      return SQLITE_DONE;    }  }  if( pDone && (rc = sqlite3BitvecSet(pDone, pgno))!=SQLITE_OK ){    return rc;  }  assert( pPager->state==PAGER_RESERVED || pPager->state>=PAGER_EXCLUSIVE );  /* If the pager is in RESERVED state, then there must be a copy of this  ** page in the pager cache. In this case just update the pager cache,  ** not the database file. The page is left marked dirty in this case.  **  ** An exception to the above rule: If the database is in no-sync mode  ** and a page is moved during an incremental vacuum then the page may  ** not be in the pager cache. Later: if a malloc() or IO error occurs  ** during a Movepage() call, then the page may not be in the cache  ** either. So the condition described in the above paragraph is not  ** assert()able.  **  ** If in EXCLUSIVE state, then we update the pager cache if it exists  ** and the main file. The page is then marked not dirty.  **  ** Ticket #1171:  The statement journal might contain page content that is  ** different from the page content at the start of the transaction.  ** This occurs when a page is changed prior to the start of a statement  ** then changed again within the statement.  When rolling back such a  ** statement we must not write to the original database unless we know  ** for certain that original page contents are synced into the main rollback  ** journal.  Otherwise, a power loss might leave modified data in the  ** database file without an entry in the rollback journal that can  ** restore the database to its original form.  Two conditions must be  ** met before writing to the database files. (1) the database must be  ** locked.  (2) we know that the original page content is fully synced  ** in the main journal either because the page is not in cache or else  ** the page is marked as needSync==0.  **  ** 2008-04-14:  When attempting to vacuum a corrupt database file, it  ** is possible to fail a statement on a database that does not yet exist.  ** Do not attempt to write if database file has never been opened.  */  pPg = pager_lookup(pPager, pgno);  PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n",               PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, aData),               (isMainJrnl?"main-journal":"sub-journal")  ));  if( (pPager->state>=PAGER_EXCLUSIVE)   && (pPg==0 || 0==(pPg->flags&PGHDR_NEED_SYNC))   && isOpen(pPager->fd)   && !isUnsync  ){    i64 ofst = (pgno-1)*(i64)pPager->pageSize;    rc = sqlite3OsWrite(pPager->fd, aData, pPager->pageSize, ofst);    if( pgno>pPager->dbFileSize ){      pPager->dbFileSize = pgno;    }    sqlite3BackupUpdate(pPager->pBackup, pgno, aData);  }else if( !isMainJrnl && pPg==0 ){    /* If this is a rollback of a savepoint and data was not written to    ** the database and the page is not in-memory, there is a potential    ** problem. When the page is next fetched by the b-tree layer, it     ** will be read from the database file, which may or may not be     ** current.     **    ** There are a couple of different ways this can happen. All are quite    ** obscure. When running in synchronous mode, this can only happen     ** if the page is on the free-list at the start of the transaction, then    ** populated, then moved using sqlite3PagerMovepage().    **    ** The solution is to add an in-memory page to the cache containing    ** the data just read from the sub-journal. Mark the page as dirty     ** and if the pager requires a journal-sync, then mark the page as     ** requiring a journal-sync before it is written.    */    assert( isSavepnt );    if( (rc = sqlite3PagerAcquire(pPager, pgno, &pPg, 1))!=SQLITE_OK ){      return rc;    }    pPg->flags &= ~PGHDR_NEED_READ;    sqlite3PcacheMakeDirty(pPg);  }  if( pPg ){    /* No page should ever be explicitly rolled back that is in use, except    ** for page 1 which is held in use in order to keep the lock on the    ** database active. However such a page may be rolled back as a result    ** of an internal error resulting in an automatic call to    ** sqlite3PagerRollback().    */    void *pData;    pData = pPg->pData;    memcpy(pData, aData, pPager->pageSize);    if( pPager->xReiniter ){      pPager->xReiniter(pPg);    }    if( isMainJrnl && (!isSavepnt || *pOffset<=pPager->journalHdr) ){      /* If the contents of this page were just restored from the main       ** journal file, then its content must be as they were when the       ** transaction was first opened. In this case we can mark the page      ** as clean, since there will be no need to write it out to the.      **      ** There is one exception to this rule. If the page is being rolled      ** back as part of a savepoint (or statement) rollback from an       ** unsynced portion of the main journal file, then it is not safe      ** to mark the page as clean. This is because marking the page as      ** clean will clear the PGHDR_NEED_SYNC flag. Since the page is      ** already in the journal file (recorded in Pager.pInJournal) and      ** the PGHDR_NEED_SYNC flag is cleared, if the page is written to      ** again within this transaction, it will be marked as dirty but      ** the PGHDR_NEED_SYNC flag will not be set. It could then potentially      ** be written out into the database file before its journal file      ** segment is synced. If a crash occurs during or following this,      ** database corruption may ensue.      */      sqlite3PcacheMakeClean(pPg);    }#ifdef SQLITE_CHECK_PAGES    pPg->pageHash = pager_pagehash(pPg);#endif    /* If this was page 1, then restore the value of Pager.dbFileVers.    ** Do this before any decoding. */    if( pgno==1 ){      memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers));    }    /* Decode the page just read from disk */    CODEC1(pPager, pData, pPg->pgno, 3);    sqlite3PcacheRelease(pPg);  }  return rc;}#if !defined(NDEBUG) || defined(SQLITE_COVERAGE_TEST)/*** This routine looks ahead into the main journal file and determines** whether or not the next record (the record that begins at file** offset pPager->journalOff) is a well-formed page record consisting** of a valid page number, pPage->pageSize bytes of content, followed** by a valid checksum.**** The pager never needs to know this in order to do its job.   This** routine is only used from with assert() and testcase() macros.*/static int pagerNextJournalPageIsValid(Pager *pPager){  Pgno pgno;           /* The page number of the page */  u32 cksum;           /* The page checksum */  int rc;              /* Return code from read operations */  sqlite3_file *fd;    /* The file descriptor from which we are reading */  u8 *aData;           /* Content of the page */  /* Read the page number header */  fd = pPager->jfd;  rc = read32bits(fd, pPager->journalOff, &pgno);  if( rc!=SQLITE_OK ){ return 0; }                                  /*NO_TEST*/  if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){ return 0; }         /*NO_TEST*/  if( pgno>(Pgno)pPager->dbSize ){ return 0; }             

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?