pager.c

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

C
2,008 行
字号
  int rc;  /* Make sure we have not hit any critical errors.  */   assert( pPager!=0 );  assert( pgno!=0 );  *ppPage = 0;  if( pPager->errMask & ~(PAGER_ERR_FULL) ){    return pager_errcode(pPager);  }  /* If this is the first page accessed, then get a read lock  ** on the database file.  */  if( pPager->nRef==0 ){    rc = sqliteOsReadLock(&pPager->fd);    if( rc!=SQLITE_OK ){      return rc;    }    pPager->state = SQLITE_READLOCK;    /* If a journal file exists, try to play it back.    */    if( pPager->useJournal && sqliteOsFileExists(pPager->zJournal) ){       int rc;       /* Get a write lock on the database       */       rc = sqliteOsWriteLock(&pPager->fd);       if( rc!=SQLITE_OK ){         if( sqliteOsUnlock(&pPager->fd)!=SQLITE_OK ){           /* This should never happen! */           rc = SQLITE_INTERNAL;         }         return rc;       }       pPager->state = SQLITE_WRITELOCK;       /* Open the journal for reading only.  Return SQLITE_BUSY if       ** we are unable to open the journal file.        **       ** The journal file does not need to be locked itself.  The       ** journal file is never open unless the main database file holds       ** a write lock, so there is never any chance of two or more       ** processes opening the journal at the same time.       */       rc = sqliteOsOpenReadOnly(pPager->zJournal, &pPager->jfd);       if( rc!=SQLITE_OK ){         rc = sqliteOsUnlock(&pPager->fd);         assert( rc==SQLITE_OK );         return SQLITE_BUSY;       }       pPager->journalOpen = 1;       pPager->journalStarted = 0;       /* Playback and delete the journal.  Drop the database write       ** lock and reacquire the read lock.       */       rc = pager_playback(pPager, 0);       if( rc!=SQLITE_OK ){         return rc;       }    }    pPg = 0;  }else{    /* Search for page in cache */    pPg = pager_lookup(pPager, pgno);  }  if( pPg==0 ){    /* The requested page is not in the page cache. */    int h;    pPager->nMiss++;    if( pPager->nPage<pPager->mxPage || pPager->pFirst==0 ){      /* Create a new page */      pPg = sqliteMallocRaw( sizeof(*pPg) + SQLITE_PAGE_SIZE                               + sizeof(u32) + pPager->nExtra );      if( pPg==0 ){        pager_unwritelock(pPager);        pPager->errMask |= PAGER_ERR_MEM;        return SQLITE_NOMEM;      }      memset(pPg, 0, sizeof(*pPg));      pPg->pPager = pPager;      pPg->pNextAll = pPager->pAll;      if( pPager->pAll ){        pPager->pAll->pPrevAll = pPg;      }      pPg->pPrevAll = 0;      pPager->pAll = pPg;      pPager->nPage++;    }else{      /* Find a page to recycle.  Try to locate a page that does not      ** require us to do an fsync() on the journal.      */      pPg = pPager->pFirstSynced;      /* If we could not find a page that does not require an fsync()      ** on the journal file then fsync the journal file.  This is a      ** very slow operation, so we work hard to avoid it.  But sometimes      ** it can't be helped.      */      if( pPg==0 ){        int rc = syncAllPages(pPager);        if( rc!=0 ){          sqlitepager_rollback(pPager);          return SQLITE_IOERR;        }        pPg = pPager->pFirst;      }      assert( pPg->nRef==0 );      /* Write the page to the database file if it is dirty.      */      if( pPg->dirty ){        assert( pPg->needSync==0 );        pPg->pDirty = 0;        rc = pager_write_pagelist( pPg );        if( rc!=SQLITE_OK ){          sqlitepager_rollback(pPager);          return SQLITE_IOERR;        }      }      assert( pPg->dirty==0 );      /* If the page we are recycling is marked as alwaysRollback, then      ** set the global alwaysRollback flag, thus disabling the      ** sqlite_dont_rollback() optimization for the rest of this transaction.      ** It is necessary to do this because the page marked alwaysRollback      ** might be reloaded at a later time but at that point we won't remember      ** that is was marked alwaysRollback.  This means that all pages must      ** be marked as alwaysRollback from here on out.      */      if( pPg->alwaysRollback ){        pPager->alwaysRollback = 1;      }      /* Unlink the old page from the free list and the hash table      */      if( pPg==pPager->pFirstSynced ){        PgHdr *p = pPg->pNextFree;        while( p && p->needSync ){ p = p->pNextFree; }        pPager->pFirstSynced = p;      }      if( pPg->pPrevFree ){        pPg->pPrevFree->pNextFree = pPg->pNextFree;      }else{        assert( pPager->pFirst==pPg );        pPager->pFirst = pPg->pNextFree;      }      if( pPg->pNextFree ){        pPg->pNextFree->pPrevFree = pPg->pPrevFree;      }else{        assert( pPager->pLast==pPg );        pPager->pLast = pPg->pPrevFree;      }      pPg->pNextFree = pPg->pPrevFree = 0;      if( pPg->pNextHash ){        pPg->pNextHash->pPrevHash = pPg->pPrevHash;      }      if( pPg->pPrevHash ){        pPg->pPrevHash->pNextHash = pPg->pNextHash;      }else{        h = pager_hash(pPg->pgno);        assert( pPager->aHash[h]==pPg );        pPager->aHash[h] = pPg->pNextHash;      }      pPg->pNextHash = pPg->pPrevHash = 0;      pPager->nOvfl++;    }    pPg->pgno = pgno;    if( pPager->aInJournal && (int)pgno<=pPager->origDbSize ){      sqliteCheckMemory(pPager->aInJournal, pgno/8);      assert( pPager->journalOpen );      pPg->inJournal = (pPager->aInJournal[pgno/8] & (1<<(pgno&7)))!=0;      pPg->needSync = 0;    }else{      pPg->inJournal = 0;      pPg->needSync = 0;    }    if( pPager->aInCkpt && (int)pgno<=pPager->ckptSize             && (pPager->aInCkpt[pgno/8] & (1<<(pgno&7)))!=0 ){      page_add_to_ckpt_list(pPg);    }else{      page_remove_from_ckpt_list(pPg);    }    pPg->dirty = 0;    pPg->nRef = 1;    REFINFO(pPg);    pPager->nRef++;    h = pager_hash(pgno);    pPg->pNextHash = pPager->aHash[h];    pPager->aHash[h] = pPg;    if( pPg->pNextHash ){      assert( pPg->pNextHash->pPrevHash==0 );      pPg->pNextHash->pPrevHash = pPg;    }    if( pPager->nExtra>0 ){      memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra);    }    if( pPager->dbSize<0 ) sqlitepager_pagecount(pPager);    if( pPager->errMask!=0 ){      sqlitepager_unref(PGHDR_TO_DATA(pPg));      rc = pager_errcode(pPager);      return rc;    }    if( pPager->dbSize<(int)pgno ){      memset(PGHDR_TO_DATA(pPg), 0, SQLITE_PAGE_SIZE);    }else{      int rc;      sqliteOsSeek(&pPager->fd, (pgno-1)*(off_t)SQLITE_PAGE_SIZE);      rc = sqliteOsRead(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);      if( rc!=SQLITE_OK ){        off_t fileSize;        if( sqliteOsFileSize(&pPager->fd,&fileSize)!=SQLITE_OK               || fileSize>=pgno*SQLITE_PAGE_SIZE ){          sqlitepager_unref(PGHDR_TO_DATA(pPg));          return rc;        }else{          memset(PGHDR_TO_DATA(pPg), 0, SQLITE_PAGE_SIZE);        }      }    }  }else{    /* The requested page is in the page cache. */    pPager->nHit++;    page_ref(pPg);  }  *ppPage = PGHDR_TO_DATA(pPg);  return SQLITE_OK;}/*** Acquire a page if it is already in the in-memory cache.  Do** not read the page from disk.  Return a pointer to the page,** or 0 if the page is not in cache.**** See also sqlitepager_get().  The difference between this routine** and sqlitepager_get() is that _get() will go to the disk and read** in the page if the page is not already in cache.  This routine** returns NULL if the page is not in cache or if a disk I/O error ** has ever happened.*/void *sqlitepager_lookup(Pager *pPager, Pgno pgno){  PgHdr *pPg;  assert( pPager!=0 );  assert( pgno!=0 );  if( pPager->errMask & ~(PAGER_ERR_FULL) ){    return 0;  }  /* if( pPager->nRef==0 ){  **  return 0;  ** }  */  pPg = pager_lookup(pPager, pgno);  if( pPg==0 ) return 0;  page_ref(pPg);  return PGHDR_TO_DATA(pPg);}/*** Release a page.**** If the number of references to the page drop to zero, then the** page is added to the LRU list.  When all references to all pages** are released, a rollback occurs and the lock on the database is** removed.*/int sqlitepager_unref(void *pData){  PgHdr *pPg;  /* Decrement the reference count for this page  */  pPg = DATA_TO_PGHDR(pData);  assert( pPg->nRef>0 );  pPg->nRef--;  REFINFO(pPg);  /* When the number of references to a page reach 0, call the  ** destructor and add the page to the freelist.  */  if( pPg->nRef==0 ){    Pager *pPager;    pPager = pPg->pPager;    pPg->pNextFree = 0;    pPg->pPrevFree = pPager->pLast;    pPager->pLast = pPg;    if( pPg->pPrevFree ){      pPg->pPrevFree->pNextFree = pPg;    }else{      pPager->pFirst = pPg;    }    if( pPg->needSync==0 && pPager->pFirstSynced==0 ){      pPager->pFirstSynced = pPg;    }    if( pPager->xDestructor ){      pPager->xDestructor(pData);    }      /* When all pages reach the freelist, drop the read lock from    ** the database file.    */    pPager->nRef--;    assert( pPager->nRef>=0 );    if( pPager->nRef==0 ){      pager_reset(pPager);    }  }  return SQLITE_OK;}/*** Create a journal file for pPager.  There should already be a write** lock on the database file when this routine is called.**** Return SQLITE_OK if everything.  Return an error code and release the** write lock if anything goes wrong.*/static int pager_open_journal(Pager *pPager){  int rc;  assert( pPager->state==SQLITE_WRITELOCK );  assert( pPager->journalOpen==0 );  assert( pPager->useJournal );  sqlitepager_pagecount(pPager);  pPager->aInJournal = sqliteMalloc( pPager->dbSize/8 + 1 );  if( pPager->aInJournal==0 ){    sqliteOsReadLock(&pPager->fd);    pPager->state = SQLITE_READLOCK;    return SQLITE_NOMEM;  }  rc = sqliteOsOpenExclusive(pPager->zJournal, &pPager->jfd,pPager->tempFile);  if( rc!=SQLITE_OK ){    sqliteFree(pPager->aInJournal);    pPager->aInJournal = 0;    sqliteOsReadLock(&pPager->fd);    pPager->state = SQLITE_READLOCK;    return SQLITE_CANTOPEN;  }  sqliteOsOpenDirectory(pPager->zDirectory, &pPager->jfd);  pPager->journalOpen = 1;  pPager->journalStarted = 0;  pPager->needSync = 0;  pPager->alwaysRollback = 0;  pPager->nRec = 0;  if( pPager->errMask!=0 ){    rc = pager_errcode(pPager);    return rc;  }  pPager->origDbSize = pPager->dbSize;  if( journal_format==JOURNAL_FORMAT_3 ){    rc = sqliteOsWrite(&pPager->jfd, aJournalMagic3, sizeof(aJournalMagic3));    if( rc==SQLITE_OK ){      rc = write32bits(&pPager->jfd, pPager->noSync ? 0xffffffff : 0);    }    if( rc==SQLITE_OK ){      pPager->cksumInit = (u32)sqliteRandomInteger();      rc = write32bits(&pPager->jfd, pPager->cksumInit);    }  }else if( journal_format==JOURNAL_FORMAT_2 ){    rc = sqliteOsWrite(&pPager->jfd, aJournalMagic2, sizeof(aJournalMagic2));  }else{    assert( journal_format==JOURNAL_FORMAT_1 );    rc = sqliteOsWrite(&pPager->jfd, aJournalMagic1, sizeof(aJournalMagic1));  }  if( rc==SQLITE_OK ){    rc = write32bits(&pPager->jfd, pPager->dbSize);  }  if( pPager->ckptAutoopen && rc==SQLITE_OK ){    rc = sqlitepager_ckpt_begin(pPager);  }  if( rc!=SQLITE_OK ){    rc = pager_unwritelock(pPager);    if( rc==SQLITE_OK ){      rc = SQLITE_FULL;    }  }  return rc;  }/*** Acquire a write-lock on the database.  The lock is removed when** the any of the following happen:****   *  sqlitepager_commit() is called.**   *  sqlitepager_rollback() is called.**   *  sqlitepager_close() is called.**   *  sqlitepager_unref() is called to on every outstanding page.**** The parameter to this routine is a pointer to any open page of the** database file.  Nothing changes about the page - it is used merely** to acquire a pointer to the Pager structure and as proof that there** is already a read-lock on the database.**** A journal file is opened if this is not a temporary file.  For** temporary files, the opening of the journal file is deferred until** there is an actual need to write to the journal.**** If the database is already write-locked, this routine is a no-op.*/int sqlitepager_begin(void *pData){  PgHdr *pPg = DATA_TO_PGHDR(pData);  Pager *pPager = pPg->pPager;

⌨️ 快捷键说明

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