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 + -
显示快捷键?