pager.c

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

C
2,008 行
字号
    sqliteOsTempFileName(zFile);    rc = sqliteOsOpenExclusive(zFile, fd, 1);  }while( cnt>0 && rc!=SQLITE_OK );  return rc;}/*** Create a new page cache and put a pointer to the page cache in *ppPager.** The file to be cached need not exist.  The file is not locked until** the first call to sqlitepager_get() and is only held open until the** last page is released using sqlitepager_unref().**** If zFilename is NULL then a randomly-named temporary file is created** and used as the file to be cached.  The file will be deleted** automatically when it is closed.*/int sqlitepager_open(  Pager **ppPager,         /* Return the Pager structure here */  const char *zFilename,   /* Name of the database file to open */  int mxPage,              /* Max number of in-memory cache pages */  int nExtra,              /* Extra bytes append to each in-memory page */  int useJournal           /* TRUE to use a rollback journal on this file */){  Pager *pPager;  char *zFullPathname;  int nameLen;  OsFile fd;  int rc, i;  int tempFile;  int readOnly = 0;  char zTemp[SQLITE_TEMPNAME_SIZE];  *ppPager = 0;  if( sqlite_malloc_failed ){    return SQLITE_NOMEM;  }  if( zFilename ){    zFullPathname = sqliteOsFullPathname(zFilename);    rc = sqliteOsOpenReadWrite(zFullPathname, &fd, &readOnly);    tempFile = 0;  }else{    rc = sqlitepager_opentemp(zTemp, &fd);    zFilename = zTemp;    zFullPathname = sqliteOsFullPathname(zFilename);    tempFile = 1;  }  if( sqlite_malloc_failed ){    return SQLITE_NOMEM;  }  if( rc!=SQLITE_OK ){    sqliteFree(zFullPathname);    return SQLITE_CANTOPEN;  }  nameLen = strlen(zFullPathname);  pPager = sqliteMalloc( sizeof(*pPager) + nameLen*3 + 30 );  if( pPager==0 ){    sqliteOsClose(&fd);    sqliteFree(zFullPathname);    return SQLITE_NOMEM;  }  SET_PAGER(pPager);  pPager->zFilename = (char*)&pPager[1];  pPager->zDirectory = &pPager->zFilename[nameLen+1];  pPager->zJournal = &pPager->zDirectory[nameLen+1];  strcpy(pPager->zFilename, zFullPathname);  strcpy(pPager->zDirectory, zFullPathname);  for(i=nameLen; i>0 && pPager->zDirectory[i-1]!='/'; i--){}  if( i>0 ) pPager->zDirectory[i-1] = 0;  strcpy(pPager->zJournal, zFullPathname);  sqliteFree(zFullPathname);  strcpy(&pPager->zJournal[nameLen], "-journal");  pPager->fd = fd;  pPager->journalOpen = 0;  pPager->useJournal = useJournal;  pPager->ckptOpen = 0;  pPager->ckptInUse = 0;  pPager->nRef = 0;  pPager->dbSize = -1;  pPager->ckptSize = 0;  pPager->ckptJSize = 0;  pPager->nPage = 0;  pPager->mxPage = mxPage>5 ? mxPage : 10;  pPager->state = SQLITE_UNLOCK;  pPager->errMask = 0;  pPager->tempFile = tempFile;  pPager->readOnly = readOnly;  pPager->needSync = 0;  pPager->noSync = pPager->tempFile || !useJournal;  pPager->pFirst = 0;  pPager->pFirstSynced = 0;  pPager->pLast = 0;  pPager->nExtra = nExtra;  memset(pPager->aHash, 0, sizeof(pPager->aHash));  *ppPager = pPager;  return SQLITE_OK;}/*** Set the destructor for this pager.  If not NULL, the destructor is called** when the reference count on each page reaches zero.  The destructor can** be used to clean up information in the extra segment appended to each page.**** The destructor is not called as a result sqlitepager_close().  ** Destructors are only called by sqlitepager_unref().*/void sqlitepager_set_destructor(Pager *pPager, void (*xDesc)(void*)){  pPager->xDestructor = xDesc;}/*** Return the total number of pages in the disk file associated with** pPager.*/int sqlitepager_pagecount(Pager *pPager){  off_t n;  assert( pPager!=0 );  if( pPager->dbSize>=0 ){    return pPager->dbSize;  }  if( sqliteOsFileSize(&pPager->fd, &n)!=SQLITE_OK ){    pPager->errMask |= PAGER_ERR_DISK;    return 0;  }  n /= SQLITE_PAGE_SIZE;  if( pPager->state!=SQLITE_UNLOCK ){    pPager->dbSize = n;  }  return n;}/*** Forward declaration*/static int syncAllPages(Pager*);/*** Truncate the file to the number of pages specified.*/int sqlitepager_truncate(Pager *pPager, Pgno nPage){  int rc;  if( pPager->dbSize<0 ){    sqlitepager_pagecount(pPager);  }  if( pPager->errMask!=0 ){    rc = pager_errcode(pPager);    return rc;  }  if( nPage>=(unsigned)pPager->dbSize ){    return SQLITE_OK;  }  syncAllPages(pPager);  rc = sqliteOsTruncate(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)nPage);  if( rc==SQLITE_OK ){    pPager->dbSize = nPage;  }  return rc;}/*** Shutdown the page cache.  Free all memory and close all files.**** If a transaction was in progress when this routine is called, that** transaction is rolled back.  All outstanding pages are invalidated** and their memory is freed.  Any attempt to use a page associated** with this page cache after this function returns will likely** result in a coredump.*/int sqlitepager_close(Pager *pPager){  PgHdr *pPg, *pNext;  switch( pPager->state ){    case SQLITE_WRITELOCK: {      sqlitepager_rollback(pPager);      sqliteOsUnlock(&pPager->fd);      assert( pPager->journalOpen==0 );      break;    }    case SQLITE_READLOCK: {      sqliteOsUnlock(&pPager->fd);      break;    }    default: {      /* Do nothing */      break;    }  }  for(pPg=pPager->pAll; pPg; pPg=pNext){    pNext = pPg->pNextAll;    sqliteFree(pPg);  }  sqliteOsClose(&pPager->fd);  assert( pPager->journalOpen==0 );  /* Temp files are automatically deleted by the OS  ** if( pPager->tempFile ){  **   sqliteOsDelete(pPager->zFilename);  ** }  */  CLR_PAGER(pPager);  if( pPager->zFilename!=(char*)&pPager[1] ){    assert( 0 );  /* Cannot happen */    sqliteFree(pPager->zFilename);    sqliteFree(pPager->zJournal);    sqliteFree(pPager->zDirectory);  }  sqliteFree(pPager);  return SQLITE_OK;}/*** Return the page number for the given page data.*/Pgno sqlitepager_pagenumber(void *pData){  PgHdr *p = DATA_TO_PGHDR(pData);  return p->pgno;}/*** Increment the reference count for a page.  If the page is** currently on the freelist (the reference count is zero) then** remove it from the freelist.*/#define page_ref(P)   ((P)->nRef==0?_page_ref(P):(void)(P)->nRef++)static void _page_ref(PgHdr *pPg){  if( pPg->nRef==0 ){    /* The page is currently on the freelist.  Remove it. */    if( pPg==pPg->pPager->pFirstSynced ){      PgHdr *p = pPg->pNextFree;      while( p && p->needSync ){ p = p->pNextFree; }      pPg->pPager->pFirstSynced = p;    }    if( pPg->pPrevFree ){      pPg->pPrevFree->pNextFree = pPg->pNextFree;    }else{      pPg->pPager->pFirst = pPg->pNextFree;    }    if( pPg->pNextFree ){      pPg->pNextFree->pPrevFree = pPg->pPrevFree;    }else{      pPg->pPager->pLast = pPg->pPrevFree;    }    pPg->pPager->nRef++;  }  pPg->nRef++;  REFINFO(pPg);}/*** Increment the reference count for a page.  The input pointer is** a reference to the page data.*/int sqlitepager_ref(void *pData){  PgHdr *pPg = DATA_TO_PGHDR(pData);  page_ref(pPg);  return SQLITE_OK;}/*** Sync the journal and then write all free dirty pages to the database** file.**** Writing all free dirty pages to the database after the sync is a** non-obvious optimization.  fsync() is an expensive operation so we** want to minimize the number ot times it is called. After an fsync() call,** we are free to write dirty pages back to the database.  It is best** to go ahead and write as many dirty pages as possible to minimize ** the risk of having to do another fsync() later on.  Writing dirty** free pages in this way was observed to make database operations go** up to 10 times faster.**** If we are writing to temporary database, there is no need to preserve** the integrity of the journal file, so we can save time and skip the** fsync().*/static int syncAllPages(Pager *pPager){  PgHdr *pPg;  int rc = SQLITE_OK;  /* Sync the journal before modifying the main database  ** (assuming there is a journal and it needs to be synced.)  */  if( pPager->needSync ){    if( !pPager->tempFile ){      assert( pPager->journalOpen );      assert( !pPager->noSync );#ifndef NDEBUG      {        off_t hdrSz, pgSz, jSz;        hdrSz = JOURNAL_HDR_SZ(journal_format);        pgSz = JOURNAL_PG_SZ(journal_format);        rc = sqliteOsFileSize(&pPager->jfd, &jSz);        if( rc!=0 ) return rc;        assert( pPager->nRec*pgSz+hdrSz==jSz );      }#endif      if( journal_format>=3 ){        off_t szJ;        if( pPager->fullSync ){          TRACE1("SYNC\n");          rc = sqliteOsSync(&pPager->jfd);          if( rc!=0 ) return rc;        }        sqliteOsSeek(&pPager->jfd, sizeof(aJournalMagic1));        rc = write32bits(&pPager->jfd, pPager->nRec);        if( rc ) return rc;        szJ = JOURNAL_HDR_SZ(journal_format) +                 pPager->nRec*JOURNAL_PG_SZ(journal_format);        sqliteOsSeek(&pPager->jfd, szJ);      }      TRACE1("SYNC\n");      rc = sqliteOsSync(&pPager->jfd);      if( rc!=0 ) return rc;      pPager->journalStarted = 1;    }    pPager->needSync = 0;    /* Erase the needSync flag from every page.    */    for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){      pPg->needSync = 0;    }    pPager->pFirstSynced = pPager->pFirst;  }#ifndef NDEBUG  /* If the Pager.needSync flag is clear then the PgHdr.needSync  ** flag must also be clear for all pages.  Verify that this  ** invariant is true.  */  else{    for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){      assert( pPg->needSync==0 );    }    assert( pPager->pFirstSynced==pPager->pFirst );  }#endif  return rc;}/*** Given a list of pages (connected by the PgHdr.pDirty pointer) write** every one of those pages out to the database file and mark them all** as clean.*/static int pager_write_pagelist(PgHdr *pList){  Pager *pPager;  int rc;  if( pList==0 ) return SQLITE_OK;  pPager = pList->pPager;  while( pList ){    assert( pList->dirty );    sqliteOsSeek(&pPager->fd, (pList->pgno-1)*(off_t)SQLITE_PAGE_SIZE);    rc = sqliteOsWrite(&pPager->fd, PGHDR_TO_DATA(pList), SQLITE_PAGE_SIZE);    if( rc ) return rc;    pList->dirty = 0;    pList = pList->pDirty;  }  return SQLITE_OK;}/*** Collect every dirty page into a dirty list and** return a pointer to the head of that list.  All pages are** collected even if they are still in use.*/static PgHdr *pager_get_all_dirty_pages(Pager *pPager){  PgHdr *p, *pList;  pList = 0;  for(p=pPager->pAll; p; p=p->pNextAll){    if( p->dirty ){      p->pDirty = pList;      pList = p;    }  }  return pList;}/*** Acquire a page.**** A read lock on the disk file is obtained when the first page is acquired. ** This read lock is dropped when the last page is released.**** A _get works for any page number greater than 0.  If the database** file is smaller than the requested page, then no actual disk** read occurs and the memory image of the page is initialized to** all zeros.  The extra data appended to a page is always initialized** to zeros the first time a page is loaded into memory.**** The acquisition might fail for several reasons.  In all cases,** an appropriate error code is returned and *ppPage is set to NULL.**** See also sqlitepager_lookup().  Both this routine and _lookup() attempt** to find a page in the in-memory cache first.  If the page is not already** in memory, this routine goes to disk to read it in whereas _lookup()** just returns 0.  This routine acquires a read-lock the first time it** has to go to disk, and could also playback an old journal if necessary.** Since _lookup() never goes to disk, it never has to deal with locks** or journal files.*/int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){  PgHdr *pPg;

⌨️ 快捷键说明

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