⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pager.c

📁 在VC6环境下开发
💻 C
📖 第 1 页 / 共 5 页
字号:
			*/
			off_t hdrSz, pgSz, jSz;
			hdrSz = JOURNAL_HDR_SZ(journal_format);
			pgSz = JOURNAL_PG_SZ(journal_format);
			rc = eDbOsFileSize(&pPager->jfd, &jSz);
			if( rc!=0 ) return rc;
				assert( pPager->nRec*pgSz+hdrSz==jSz );
			}
#endif
			if( journal_format>=3 ){
				/* Write the nRec value into the journal file header */
				off_t szJ;
				if( pPager->fullSync ){
					TRACE1("SYNC\n");
					rc = eDbOsSync(&pPager->jfd);
					if( rc!=0 ) return rc;
				}
				eDbOsSeek(&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);
				eDbOsSeek(&pPager->jfd, szJ);
			}
			TRACE1("SYNC\n");
			rc = eDbOsSync(&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 eDb_OK;
	pPager = pList->pPager;
	while( pList ){
		assert( pList->dirty );
		eDbOsSeek(&pPager->fd, (pList->pgno-1)*(off_t)eDb_PAGE_SIZE);
		CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6);
		TRACE2("STORE %d\n", pList->pgno);
		rc = eDbOsWrite(&pPager->fd, PGHDR_TO_DATA(pList), eDb_PAGE_SIZE);
		CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 0);
		if( rc ) return rc;
		pList->dirty = 0;
		pList = pList->pDirty;
	}
	return eDb_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 eDbpager_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 eDbpager_get(Pager *pPager, Pgno pgno, void **ppPage){
	PgHdr *pPg;
	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 = eDbOsReadLock(&pPager->fd);
		if( rc!=eDb_OK ){
			return rc;
		}
		pPager->state = eDb_READLOCK;

		/* If a journal file exists, try to play it back.
		*/
		if(pPager->useJournal && eDbOsFileExists(pPager->zJournal)){
			int rc;

			/* Get a write lock on the database
			*/
			rc = eDbOsWriteLock(&pPager->fd);
			if( rc!=eDb_OK ){
				if( eDbOsUnlock(&pPager->fd)!=eDb_OK ){
					/* This should never happen! */
					rc = eDb_INTERNAL;
				}
				return rc;
			}
			pPager->state = eDb_WRITELOCK;

			/* Open the journal for reading only.  Return eDb_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 = eDbOsOpenReadOnly(pPager->zJournal, &pPager->jfd);
			if( rc!=eDb_OK ){
				rc = eDbOsUnlock(&pPager->fd);
				assert( rc==eDb_OK );
				return eDb_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!=eDb_OK ){
				return rc;
			}
		}/*if(pPager->useJournal && eDbOsFileExists(pPager->zJournal))*/
		pPg = 0;
	}	/*if( pPager->nRef==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 = eDbMallocRaw( sizeof(*pPg) + eDb_PAGE_SIZE 
								  + sizeof(u32) + pPager->nExtra );
			if( pPg==0 ){
				pager_unwritelock(pPager);
				pPager->errMask |= PAGER_ERR_MEM;
				return eDb_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++;
		}/* end if(pPager->nPage<pPager->mxPage || pPager->pFirst==0)*/
		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 = syncJournal(pPager);
				if( rc!=0 ){
					eDbpager_rollback(pPager);
					return eDb_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!=eDb_OK ){
					eDbpager_rollback(pPager);
					return eDb_IOERR;
				}
			}
			assert( pPg->dirty==0 );

			/* If the page we are recycling is marked as alwaysRollback, then
			** set the global alwaysRollback flag, thus disabling the
			** eDb_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++;
		}/* end end if(pPager->nPage<pPager->mxPage || pPager->pFirst==0) else */
		pPg->pgno = pgno;
		if( pPager->aInJournal && (int)pgno<=pPager->origDbSize ){
			eDbCheckMemory(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 ) eDbpager_pagecount(pPager);
		if( pPager->errMask!=0 ){
			eDbpager_unref(PGHDR_TO_DATA(pPg));
			rc = pager_errcode(pPager);
			return rc;
		}
		if( pPager->dbSize<(int)pgno ){
			memset(PGHDR_TO_DATA(pPg), 0, eDb_PAGE_SIZE);
		}else{
			int rc;
			eDbOsSeek(&pPager->fd, (pgno-1)*(off_t)eDb_PAGE_SIZE);
			rc = eDbOsRead(&pPager->fd, PGHDR_TO_DATA(pPg), eDb_PAGE_SIZE);
			TRACE2("FETCH %d\n", pPg->pgno);
			CODEC(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3);
			if( rc!=eDb_OK ){
				off_t fileSize;
				if( eDbOsFileSize(&pPager->fd,&fileSize)!=eDb_OK
					   || fileSize>=pgno*eDb_PAGE_SIZE ){
					eDbpager_unref(PGHDR_TO_DATA(pPg));
					return rc;
				}else{
					memset(PGHDR_TO_DATA(pPg), 0, eDb_PAGE_SIZE);
				}
			}
		}
	}/*end if(pPg == 0)*/
	else{
		/* The requested page is in the page cache. */
		pPager->nHit++;
		page_ref(pPg);
	}
	*ppPage = PGHDR_TO_DATA(pPg);
	return eDb_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 eDbpager_get().  The difference between this routine
** and eDbpager_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 *eDbpager_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.
**

⌨️ 快捷键说明

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