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

📄 pager.c

📁 在VC6环境下开发
💻 C
📖 第 1 页 / 共 5 页
字号:
		if( rc!=eDb_OK ) goto end_ckpt_playback;
	}

	/* Figure out how many pages need to be copied out of the transaction
	** journal.
	*/
	rc = eDbOsSeek(&pPager->jfd, pPager->ckptJSize);
	if( rc!=eDb_OK ){
		goto end_ckpt_playback;
	}
	rc = eDbOsFileSize(&pPager->jfd, &szJ);
	if( rc!=eDb_OK ){
		goto end_ckpt_playback;
	}
	nRec = (int)((szJ - pPager->ckptJSize)/JOURNAL_PG_SZ(journal_format));
	for(i=nRec-1; i>=0; i--){
		rc = pager_playback_one_page(pPager, &pPager->jfd, journal_format);
		if( rc!=eDb_OK ){
			assert( rc!=eDb_DONE );
			goto end_ckpt_playback;
		}
	}
  
end_ckpt_playback:
	if( rc!=eDb_OK ){
		pPager->errMask |= PAGER_ERR_CORRUPT;
		rc = eDb_CORRUPT;
	}
	return rc;
}

/*
** Change the maximum number of in-memory pages that are allowed.
**
** The maximum number is the absolute value of the mxPage parameter.
** If mxPage is negative, the noSync flag is also set.  noSync bypasses
** calls to eDbOsSync().  The pager runs much faster with noSync on,
** but if the operating system crashes or there is an abrupt power 
** failure, the database file might be left in an inconsistent and
** unrepairable state.  
*/
void eDbpager_set_cachesize(Pager *pPager, int mxPage){
	if( mxPage>=0 ){
		pPager->noSync = pPager->tempFile;
		if( pPager->noSync==0 ) pPager->needSync = 0;
	}else{
		pPager->noSync = 1;
		mxPage = -mxPage;
	}
	if( mxPage>10 ){
		pPager->mxPage = mxPage;
	}
}

/*
** Adjust the robustness of the database to damage due to OS crashes
** or power failures by changing the number of syncs()s when writing
** the rollback journal.  There are three levels:
**
**    OFF       eDbOsSync() is never called.  This is the default
**              for temporary and transient files.
**
**    NORMAL    The journal is synced once before writes begin on the
**              database.  This is normally adequate protection, but
**              it is theoretically possible, though very unlikely,
**              that an inopertune power failure could leave the journal
**              in a state which would cause damage to the database
**              when it is rolled back.
**
**    FULL      The journal is synced twice before writes begin on the
**              database (with some additional information - the nRec field
**              of the journal header - being written in between the two
**              syncs).  If we assume that writing a
**              single disk sector is atomic, then this mode provides
**              assurance that the journal will not be corrupted to the
**              point of causing damage to the database during rollback.
**
** Numeric values associated with these states are OFF==1, NORMAL=2,
** and FULL=3.
*/
void eDbpager_set_safety_level(Pager *pPager, int level){
	pPager->noSync =  level==1 || pPager->tempFile;
	pPager->fullSync = level==3 && !pPager->tempFile;
	if( pPager->noSync==0 ) pPager->needSync = 0;
}

/*
** Open a temporary file.  Write the name of the file into zName
** (zName must be at least eDb_TEMPNAME_SIZE bytes long.)  Write
** the file descriptor into *fd.  Return eDb_OK on success or some
** other error code if we fail.
**
** The OS will automatically delete the temporary file when it is
** closed.
*/
static int eDbpager_opentemp(char *zFile, OsFile *fd){
	int cnt = 8;
	int rc;
	do{
		cnt--;
		eDbOsTempFileName(zFile);
		rc = eDbOsOpenExclusive(zFile, fd, 1);
	}while( cnt>0 && rc!=eDb_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 eDbpager_get() and is only held open until the
** last page is released using eDbpager_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 eDbpager_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[eDb_TEMPNAME_SIZE];

	*ppPager = 0;
	if( eDb_malloc_failed ){
		return eDb_NOMEM;
	}
	if( zFilename && zFilename[0] ){
		zFullPathname = eDbOsFullPathname(zFilename);
		rc = eDbOsOpenReadWrite(zFullPathname, &fd, &readOnly);
		tempFile = 0;
	}else{
		rc = eDbpager_opentemp(zTemp, &fd);
		zFilename = zTemp;
		zFullPathname = eDbOsFullPathname(zFilename);
		tempFile = 1;
	}
	if( eDb_malloc_failed ){
		return eDb_NOMEM;
	}
	if( rc!=eDb_OK ){
		eDbFree(zFullPathname);
		return eDb_CANTOPEN;
	}
	nameLen = strlen(zFullPathname);
	pPager = eDbMalloc( sizeof(*pPager) + nameLen*3 + 30 );
	if( pPager==0 ){
		eDbOsClose(&fd);
		eDbFree(zFullPathname);
		return eDb_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);
	eDbFree(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 = eDb_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 eDb_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 eDbpager_close().  
** Destructors are only called by eDbpager_unref().
*/
void eDbpager_set_destructor(Pager *pPager, void (*xDesc)(void*)){
	pPager->xDestructor = xDesc;
}

/*
** Return the total number of pages in the disk file associated with
** pPager.
*/
int eDbpager_pagecount(Pager *pPager){
	off_t n;
	assert( pPager!=0 );
	if( pPager->dbSize>=0 ){
		return pPager->dbSize;
	}
	if( eDbOsFileSize(&pPager->fd, &n)!=eDb_OK ){
		pPager->errMask |= PAGER_ERR_DISK;
		return 0;
	}
	n /= eDb_PAGE_SIZE;
	if( pPager->state!=eDb_UNLOCK ){
		pPager->dbSize = (int)n;
	}
	return (int)n;
}

/*
** Forward declaration
*/
static int syncJournal(Pager*);

/*
** Truncate the file to the number of pages specified.
*/
int eDbpager_truncate(Pager *pPager, Pgno nPage){
	int rc;
	if( pPager->dbSize<0 ){
		eDbpager_pagecount(pPager);
	}
	if( pPager->errMask!=0 ){
		rc = pager_errcode(pPager);
		return rc;
	}
	if( nPage>=(unsigned)pPager->dbSize ){
		return eDb_OK;
	}
	syncJournal(pPager);
	rc = eDbOsTruncate(&pPager->fd, eDb_PAGE_SIZE*(off_t)nPage);
	if( rc==eDb_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 eDbpager_close(Pager *pPager){
	PgHdr *pPg, *pNext;
	switch( pPager->state ){
		case eDb_WRITELOCK: {
			eDbpager_rollback(pPager);
			eDbOsUnlock(&pPager->fd);
			assert( pPager->journalOpen==0 );
			break;
		}
		case eDb_READLOCK: {
			eDbOsUnlock(&pPager->fd);
			break;
		}
		default: {
			/* Do nothing */
			break;
		}
	}
	for(pPg=pPager->pAll; pPg; pPg=pNext){
		pNext = pPg->pNextAll;
		eDbFree(pPg);
	}
	eDbOsClose(&pPager->fd);
	assert( pPager->journalOpen==0 );
	/* Temp files are automatically deleted by the OS
	** if( pPager->tempFile ){
	**   eDbOsDelete(pPager->zFilename);
	** }
	*/
	CLR_PAGER(pPager);
	if( pPager->zFilename!=(char*)&pPager[1] ){
		assert( 0 );  /* Cannot happen */
		eDbFree(pPager->zFilename);
		eDbFree(pPager->zJournal);
		eDbFree(pPager->zDirectory);
	}
	eDbFree(pPager);
	return eDb_OK;
}

/*
** Return the page number for the given page data.
*/
Pgno eDbpager_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 eDbpager_ref(void *pData){
	PgHdr *pPg = DATA_TO_PGHDR(pData);
	page_ref(pPg);
	return eDb_OK;
}

/*
** Sync the journal.  In other words, make sure all the pages that have
** been written to the journal have actually reached the surface of the
** disk.  It is not safe to modify the original database file until after
** the journal has been synced.  If the original database is modified before
** the journal is synced and a power failure occurs, the unsynced journal
** data would be lost and we would be unable to completely rollback the
** database changes.  Database corruption would occur.
** 
** This routine also updates the nRec field in the header of the journal.
** (See comments on the pager_playback() routine for additional information.)
** If the sync mode is FULL, two syncs will occur.  First the whole journal
** is synced, then the nRec field is updated, then a second sync occurs.
**
** For temporary databases, we do not care if we are able to rollback
** after a power failure, so sync occurs.
**
** This routine clears the needSync field of every page current held in
** memory.
*/
static int syncJournal(Pager *pPager){
	PgHdr *pPg;
	int rc = eDb_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 ); // noSync might be set if synchronous
			** was turned off after the transaction was started.  Ticket #615 */
#ifndef NDEBUG
			{
			/* Make sure the pPager->nRec counter we are keeping agrees
			** with the nRec computed from the size of the journal file.

⌨️ 快捷键说明

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