pager.c

来自「SQLite 2.8.6 源代码,用来在Linux/Unix/Windows上编」· C语言 代码 · 共 2,008 行 · 第 1/5 页

C
2,008 行
字号
  int rc = SQLITE_OK;  assert( pPg->nRef>0 );  assert( pPager->state!=SQLITE_UNLOCK );  if( pPager->state==SQLITE_READLOCK ){    assert( pPager->aInJournal==0 );    rc = sqliteOsWriteLock(&pPager->fd);    if( rc!=SQLITE_OK ){      return rc;    }    pPager->state = SQLITE_WRITELOCK;    pPager->dirtyFile = 0;    TRACE1("TRANSACTION\n");    if( pPager->useJournal && !pPager->tempFile ){      rc = pager_open_journal(pPager);    }  }  return rc;}/*** Mark a data page as writeable.  The page is written into the journal ** if it is not there already.  This routine must be called before making** changes to a page.**** The first time this routine is called, the pager creates a new** journal and acquires a write lock on the database.  If the write** lock could not be acquired, this routine returns SQLITE_BUSY.  The** calling routine must check for that return value and be careful not to** change any page data until this routine returns SQLITE_OK.**** If the journal file could not be written because the disk is full,** then this routine returns SQLITE_FULL and does an immediate rollback.** All subsequent write attempts also return SQLITE_FULL until there** is a call to sqlitepager_commit() or sqlitepager_rollback() to** reset.*/int sqlitepager_write(void *pData){  PgHdr *pPg = DATA_TO_PGHDR(pData);  Pager *pPager = pPg->pPager;  int rc = SQLITE_OK;  /* Check for errors  */  if( pPager->errMask ){     return pager_errcode(pPager);  }  if( pPager->readOnly ){    return SQLITE_PERM;  }  /* Mark the page as dirty.  If the page has already been written  ** to the journal then we can return right away.  */  pPg->dirty = 1;  if( pPg->inJournal && (pPg->inCkpt || pPager->ckptInUse==0) ){    pPager->dirtyFile = 1;    return SQLITE_OK;  }  /* If we get this far, it means that the page needs to be  ** written to the transaction journal or the ckeckpoint journal  ** or both.  **  ** First check to see that the transaction journal exists and  ** create it if it does not.  */  assert( pPager->state!=SQLITE_UNLOCK );  rc = sqlitepager_begin(pData);  if( rc!=SQLITE_OK ){    return rc;  }  assert( pPager->state==SQLITE_WRITELOCK );  if( !pPager->journalOpen && pPager->useJournal ){    rc = pager_open_journal(pPager);    if( rc!=SQLITE_OK ) return rc;  }  assert( pPager->journalOpen || !pPager->useJournal );  pPager->dirtyFile = 1;  /* The transaction journal now exists and we have a write lock on the  ** main database file.  Write the current page to the transaction   ** journal if it is not there already.  */  if( !pPg->inJournal && pPager->useJournal ){    if( (int)pPg->pgno <= pPager->origDbSize ){      int szPg;      u32 saved;      if( journal_format>=JOURNAL_FORMAT_3 ){        u32 cksum = pager_cksum(pPager, pPg->pgno, pData);        saved = *(u32*)PGHDR_TO_EXTRA(pPg);        store32bits(cksum, pPg, SQLITE_PAGE_SIZE);        szPg = SQLITE_PAGE_SIZE+8;      }else{        szPg = SQLITE_PAGE_SIZE+4;      }      store32bits(pPg->pgno, pPg, -4);      rc = sqliteOsWrite(&pPager->jfd, &((char*)pData)[-4], szPg);      if( journal_format>=JOURNAL_FORMAT_3 ){        *(u32*)PGHDR_TO_EXTRA(pPg) = saved;      }      if( rc!=SQLITE_OK ){        sqlitepager_rollback(pPager);        pPager->errMask |= PAGER_ERR_FULL;        return rc;      }      pPager->nRec++;      assert( pPager->aInJournal!=0 );      pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);      pPg->needSync = !pPager->noSync;      pPg->inJournal = 1;      if( pPager->ckptInUse ){        pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);        page_add_to_ckpt_list(pPg);      }      TRACE3("JOURNAL %d %d\n", pPg->pgno, pPg->needSync);    }else{      pPg->needSync = !pPager->journalStarted && !pPager->noSync;      TRACE3("APPEND %d %d\n", pPg->pgno, pPg->needSync);    }    if( pPg->needSync ){      pPager->needSync = 1;    }  }  /* If the checkpoint journal is open and the page is not in it,  ** then write the current page to the checkpoint journal.  Note that  ** the checkpoint journal always uses the simplier format 2 that lacks  ** checksums.  The header is also omitted from the checkpoint journal.  */  if( pPager->ckptInUse && !pPg->inCkpt && (int)pPg->pgno<=pPager->ckptSize ){    assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize );    store32bits(pPg->pgno, pPg, -4);    rc = sqliteOsWrite(&pPager->cpfd, &((char*)pData)[-4], SQLITE_PAGE_SIZE+4);    if( rc!=SQLITE_OK ){      sqlitepager_rollback(pPager);      pPager->errMask |= PAGER_ERR_FULL;      return rc;    }    pPager->ckptNRec++;    assert( pPager->aInCkpt!=0 );    pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);    page_add_to_ckpt_list(pPg);  }  /* Update the database size and return.  */  if( pPager->dbSize<(int)pPg->pgno ){    pPager->dbSize = pPg->pgno;  }  return rc;}/*** Return TRUE if the page given in the argument was previously passed** to sqlitepager_write().  In other words, return TRUE if it is ok** to change the content of the page.*/int sqlitepager_iswriteable(void *pData){  PgHdr *pPg = DATA_TO_PGHDR(pData);  return pPg->dirty;}/*** Replace the content of a single page with the information in the third** argument.*/int sqlitepager_overwrite(Pager *pPager, Pgno pgno, void *pData){  void *pPage;  int rc;  rc = sqlitepager_get(pPager, pgno, &pPage);  if( rc==SQLITE_OK ){    rc = sqlitepager_write(pPage);    if( rc==SQLITE_OK ){      memcpy(pPage, pData, SQLITE_PAGE_SIZE);    }    sqlitepager_unref(pPage);  }  return rc;}/*** A call to this routine tells the pager that it is not necessary to** write the information on page "pgno" back to the disk, even though** that page might be marked as dirty.**** The overlying software layer calls this routine when all of the data** on the given page is unused.  The pager marks the page as clean so** that it does not get written to disk.**** Tests show that this optimization, together with the** sqlitepager_dont_rollback() below, more than double the speed** of large INSERT operations and quadruple the speed of large DELETEs.**** When this routine is called, set the alwaysRollback flag to true.** Subsequent calls to sqlitepager_dont_rollback() for the same page** will thereafter be ignored.  This is necessary to avoid a problem** where a page with data is added to the freelist during one part of** a transaction then removed from the freelist during a later part** of the same transaction and reused for some other purpose.  When it** is first added to the freelist, this routine is called.  When reused,** the dont_rollback() routine is called.  But because the page contains** critical data, we still need to be sure it gets rolled back in spite** of the dont_rollback() call.*/void sqlitepager_dont_write(Pager *pPager, Pgno pgno){  PgHdr *pPg;  pPg = pager_lookup(pPager, pgno);  pPg->alwaysRollback = 1;  if( pPg && pPg->dirty ){    if( pPager->dbSize==(int)pPg->pgno && pPager->origDbSize<pPager->dbSize ){      /* If this pages is the last page in the file and the file has grown      ** during the current transaction, then do NOT mark the page as clean.      ** When the database file grows, we must make sure that the last page      ** gets written at least once so that the disk file will be the correct      ** size. If you do not write this page and the size of the file      ** on the disk ends up being too small, that can lead to database      ** corruption during the next transaction.      */    }else{      TRACE2("DONT_WRITE %d\n", pgno);      pPg->dirty = 0;    }  }}/*** A call to this routine tells the pager that if a rollback occurs,** it is not necessary to restore the data on the given page.  This** means that the pager does not have to record the given page in the** rollback journal.*/void sqlitepager_dont_rollback(void *pData){  PgHdr *pPg = DATA_TO_PGHDR(pData);  Pager *pPager = pPg->pPager;  if( pPager->state!=SQLITE_WRITELOCK || pPager->journalOpen==0 ) return;  if( pPg->alwaysRollback || pPager->alwaysRollback ) return;  if( !pPg->inJournal && (int)pPg->pgno <= pPager->origDbSize ){    assert( pPager->aInJournal!=0 );    pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);    pPg->inJournal = 1;    if( pPager->ckptInUse ){      pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);      page_add_to_ckpt_list(pPg);    }    TRACE2("DONT_ROLLBACK %d\n", pPg->pgno);  }  if( pPager->ckptInUse && !pPg->inCkpt && (int)pPg->pgno<=pPager->ckptSize ){    assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize );    assert( pPager->aInCkpt!=0 );    pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);    page_add_to_ckpt_list(pPg);  }}/*** Commit all changes to the database and release the write lock.**** If the commit fails for any reason, a rollback attempt is made** and an error code is returned.  If the commit worked, SQLITE_OK** is returned.*/int sqlitepager_commit(Pager *pPager){  int rc;  PgHdr *pPg;  if( pPager->errMask==PAGER_ERR_FULL ){    rc = sqlitepager_rollback(pPager);    if( rc==SQLITE_OK ){      rc = SQLITE_FULL;    }    return rc;  }  if( pPager->errMask!=0 ){    rc = pager_errcode(pPager);    return rc;  }  if( pPager->state!=SQLITE_WRITELOCK ){    return SQLITE_ERROR;  }  TRACE1("COMMIT\n");  if( pPager->dirtyFile==0 ){    /* Exit early (without doing the time-consuming sqliteOsSync() calls)    ** if there have been no changes to the database file. */    assert( pPager->needSync==0 );    rc = pager_unwritelock(pPager);    pPager->dbSize = -1;    return rc;  }  assert( pPager->journalOpen );  if( pPager->needSync && sqliteOsSync(&pPager->jfd)!=SQLITE_OK ){    goto commit_abort;  }  pPg = pager_get_all_dirty_pages(pPager);  if( pPg ){    rc = pager_write_pagelist(pPg);    if( rc || (!pPager->noSync && sqliteOsSync(&pPager->fd)!=SQLITE_OK) ){      goto commit_abort;    }  }  rc = pager_unwritelock(pPager);  pPager->dbSize = -1;  return rc;  /* Jump here if anything goes wrong during the commit process.  */commit_abort:  rc = sqlitepager_rollback(pPager);  if( rc==SQLITE_OK ){    rc = SQLITE_FULL;  }  return rc;}/*** Rollback all changes.  The database falls back to read-only mode.** All in-memory cache pages revert to their original data contents.** The journal is deleted.**** This routine cannot fail unless some other process is not following** the correct locking protocol (SQLITE_PROTOCOL) or unless some other** process is writing trash into the journal file (SQLITE_CORRUPT) or** unless a prior malloc() failed (SQLITE_NOMEM).  Appropriate error** codes are returned for all these occasions.  Otherwise,** SQLITE_OK is returned.*/int sqlitepager_rollback(Pager *pPager){  int rc;  TRACE1("ROLLBACK\n");  if( !pPager->dirtyFile || !pPager->journalOpen ){    rc = pager_unwritelock(pPager);    pPager->dbSize = -1;    return rc;  }  if( pPager->errMask!=0 && pPager->errMask!=PAGER_ERR_FULL ){    if( pPager->state>=SQLITE_WRITELOCK ){      pager_playback(pPager, 1);    }    return pager_errcode(pPager);  }  if( pPager->state!=SQLITE_WRITELOCK ){    return SQLITE_OK;  }  rc = pager_playback(pPager, 1);  if( rc!=SQLITE_OK ){    rc = SQLITE_CORRUPT;    pPager->errMask |= PAGER_ERR_CORRUPT;  }  pPager->dbSize = -1;  return rc;}/*** Return TRUE if the database file is opened read-only.  Return FALSE** if the database is (in theory) writable.*/int sqlitepager_isreadonly(Pager *pPager){  return pPager->readOnly;}/*** This routine is used for testing and analysis only.*/int *sqlitepager_stats(Pager *pPager){  static int a[9];  a[0] = pPager->nRef;  a[1] = pPager->nPage;  a[2] = pPager->mxPage;  a[3] = pPager->dbSize;  a[4] = pPager->state;  a[5] = pPager->errMask;  a[6] = pPager->nHit;  a[7] = pPager->nMiss;  a[8] = pPager->nOvfl;  return a;}/*** Set the checkpoint.**** This routine should be called with the transaction journal already** open.  A new checkpoint journal is created that can be used to rollback** changes of a single SQL command within a larger transaction.*/int sqlitepager_ckpt_begin(Pager *pPager){  int rc;  char zTemp[SQLITE_TEMPNAME_SIZE];  if( !pPager->journalOpen ){    pPager->ckptAutoopen = 1;    return SQLITE_OK;  }  assert( pPager->journalOpen );  assert( !pPager->ckptInUse );  pPager->aInCkpt = sqliteMalloc( pPager->dbSize/8 + 1 );  if( pPager->aInCkpt==0 ){    sqliteOsReadLock(&pPager->fd);    return

⌨️ 快捷键说明

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