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

📄 pager.c

📁 在VC6环境下开发
💻 C
📖 第 1 页 / 共 5 页
字号:
** 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 eDbpager_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 eDb_OK;
}

/*
** Create a journal file for pPager.  There should already be a write
** lock on the database file when this routine is called.
**
** Return eDb_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==eDb_WRITELOCK );
	assert( pPager->journalOpen==0 );
	assert( pPager->useJournal );
	eDbpager_pagecount(pPager);
	pPager->aInJournal = eDbMalloc( pPager->dbSize/8 + 1 );
	if( pPager->aInJournal==0 ){
		eDbOsReadLock(&pPager->fd);
		pPager->state = eDb_READLOCK;
		return eDb_NOMEM;
	}
	rc = eDbOsOpenExclusive(pPager->zJournal, &pPager->jfd,pPager->tempFile);
	if( rc!=eDb_OK ){
		eDbFree(pPager->aInJournal);
		pPager->aInJournal = 0;
		eDbOsReadLock(&pPager->fd);
		pPager->state = eDb_READLOCK;
		return eDb_CANTOPEN;
	}
	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 = eDbOsWrite(&pPager->jfd, aJournalMagic3, sizeof(aJournalMagic3));
		if( rc==eDb_OK ){
			rc = write32bits(&pPager->jfd, pPager->noSync ? 0xffffffff : 0);
		}
		if( rc==eDb_OK ){
			eDbRandomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
			rc = write32bits(&pPager->jfd, pPager->cksumInit);
		}
	}else if( journal_format==JOURNAL_FORMAT_2 ){
		rc = eDbOsWrite(&pPager->jfd, aJournalMagic2, sizeof(aJournalMagic2));
	}else{
		assert( journal_format==JOURNAL_FORMAT_1 );
		rc = eDbOsWrite(&pPager->jfd, aJournalMagic1, sizeof(aJournalMagic1));
	}
	if( rc==eDb_OK ){
		rc = write32bits(&pPager->jfd, pPager->dbSize);
	}
	if( pPager->ckptAutoopen && rc==eDb_OK ){
		rc = eDbpager_ckpt_begin(pPager);
	}
	if( rc!=eDb_OK ){
		rc = pager_unwritelock(pPager);
		if( rc==eDb_OK ){
			rc = eDb_FULL;
		}
	}
	return rc;  
}

/*
** Acquire a write-lock on the database.  The lock is removed when
** the any of the following happen:
**
**   *  eDbpager_commit() is called.
**   *  eDbpager_rollback() is called.
**   *  eDbpager_close() is called.
**   *  eDbpager_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 eDbpager_begin(void *pData){
	PgHdr *pPg = DATA_TO_PGHDR(pData);
	Pager *pPager = pPg->pPager;
	int rc = eDb_OK;
	assert( pPg->nRef>0 );
	assert( pPager->state!=eDb_UNLOCK );
	if( pPager->state==eDb_READLOCK ){
		assert( pPager->aInJournal==0 );
		rc = eDbOsWriteLock(&pPager->fd);
		if( rc!=eDb_OK ){
			return rc;
		}
		pPager->state = eDb_WRITELOCK;
		pPager->dirtyFile = 0;
		TRACE1("TRANSACTION\n");
		if( pPager->useJournal && !pPager->tempFile ){
			rc = pager_open_journal(pPager);
		}
	}
	return rc;
}

/*
** Mark a data page as writeable.  The page is written into the journal 
** if it is not there already.  This routine must be called before making
** changes to a page.
**
** The first time this routine is called, the pager creates a new
** journal and acquires a write lock on the database.  If the write
** lock could not be acquired, this routine returns eDb_BUSY.  The
** calling routine must check for that return value and be careful not to
** change any page data until this routine returns eDb_OK.
**
** If the journal file could not be written because the disk is full,
** then this routine returns eDb_FULL and does an immediate rollback.
** All subsequent write attempts also return eDb_FULL until there
** is a call to eDbpager_commit() or eDbpager_rollback() to
** reset.
*/
int eDbpager_write(void *pData){
	PgHdr *pPg = DATA_TO_PGHDR(pData);
	Pager *pPager = pPg->pPager;
	int rc = eDb_OK;

	/* Check for errors
	*/
	if( pPager->errMask ){ 
		return pager_errcode(pPager);
	}
	if( pPager->readOnly ){
		return eDb_PERM;
	}

	/* Mark the page as dirty.  If the page has already been written
	** to the journal then we can return right away.
	*/
	pPg->dirty = 1;
	if( pPg->inJournal && (pPg->inCkpt || pPager->ckptInUse==0)){
		pPager->dirtyFile = 1;
		return eDb_OK;
	}

	/* If we get this far, it means that the page needs to be
	** written to the transaction journal or the ckeckpoint journal
	** or both.
	**
	** First check to see that the transaction journal exists and
	** create it if it does not.
	*/
	assert( pPager->state!=eDb_UNLOCK );
	rc = eDbpager_begin(pData);
	if( rc!=eDb_OK ){
		return rc;
	}
	assert( pPager->state==eDb_WRITELOCK );
	if( !pPager->journalOpen && pPager->useJournal ){
		rc = pager_open_journal(pPager);
		if( rc!=eDb_OK ) return rc;
	}
	assert( pPager->journalOpen || !pPager->useJournal );
	pPager->dirtyFile = 1;

	/* The transaction journal now exists and we have a write lock on the
	** main database file.  Write the current page to the transaction 
	** journal if it is not there already.
	*/
	if(!pPg->inJournal && pPager->useJournal){
		if((int)pPg->pgno <= pPager->origDbSize){
			int szPg;
			u32 saved;
			if( journal_format>=JOURNAL_FORMAT_3 ){
				u32 cksum = pager_cksum(pPager, pPg->pgno, pData);
				saved = *(u32*)PGHDR_TO_EXTRA(pPg);
				store32bits(cksum, pPg, eDb_PAGE_SIZE);
				szPg = eDb_PAGE_SIZE+8;
			}else{
				szPg = eDb_PAGE_SIZE+4;
			}
			store32bits(pPg->pgno, pPg, -4);
			CODEC(pPager, pData, pPg->pgno, 7);
			rc = eDbOsWrite(&pPager->jfd, &((char*)pData)[-4], szPg);
			TRACE3("JOURNAL %d %d\n", pPg->pgno, pPg->needSync);
			CODEC(pPager, pData, pPg->pgno, 0);
			if( journal_format>=JOURNAL_FORMAT_3 ){
				*(u32*)PGHDR_TO_EXTRA(pPg) = saved;
			}
			if( rc!=eDb_OK ){
				eDbpager_rollback(pPager);
				pPager->errMask |= PAGER_ERR_FULL;
				return rc;
			}
			pPager->nRec++;
			assert( pPager->aInJournal!=0 );
			pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
			pPg->needSync = !pPager->noSync;
			pPg->inJournal = 1;
			if( pPager->ckptInUse ){
				pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
				page_add_to_ckpt_list(pPg);
			}
		}/* end if((int)pPg->pgno <= pPager->origDbSize)*/
		else{
			pPg->needSync = !pPager->journalStarted && !pPager->noSync;
			TRACE3("APPEND %d %d\n", pPg->pgno, pPg->needSync);
		}
		if( pPg->needSync ){
			pPager->needSync = 1;
		}
	}/*end if(!pPg->inJournal && pPager->useJournal)*/

	/* If the checkpoint journal is open and the page is not in it,
	** then write the current page to the checkpoint journal.  Note that
	** the checkpoint journal always uses the simplier format 2 that lacks
	** checksums.  The header is also omitted from the checkpoint journal.
	*/
	if( pPager->ckptInUse && !pPg->inCkpt && (int)pPg->pgno<=pPager->ckptSize ){
		assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize );
		store32bits(pPg->pgno, pPg, -4);
		CODEC(pPager, pData, pPg->pgno, 7);
		rc = eDbOsWrite(&pPager->cpfd, &((char*)pData)[-4], eDb_PAGE_SIZE+4);
		TRACE2("CKPT-JOURNAL %d\n", pPg->pgno);
		CODEC(pPager, pData, pPg->pgno, 0);
		if( rc!=eDb_OK ){
			eDbpager_rollback(pPager);
			pPager->errMask |= PAGER_ERR_FULL;
			return rc;
		}
		pPager->ckptNRec++;
		assert( pPager->aInCkpt!=0 );
		pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
		page_add_to_ckpt_list(pPg);
  }

	/* Update the database size and return.
	*/
	if( pPager->dbSize<(int)pPg->pgno ){
		pPager->dbSize = pPg->pgno;
	}
	return rc;
}

/*
** Return TRUE if the page given in the argument was previously passed
** to eDbpager_write().  In other words, return TRUE if it is ok
** to change the content of the page.
*/
int eDbpager_iswriteable(void *pData){
	PgHdr *pPg = DATA_TO_PGHDR(pData);
	return pPg->dirty;
}

/*
** Replace the content of a single page with the information in the third
** argument.
*/
int eDbpager_overwrite(Pager *pPager, Pgno pgno, void *pData){
	void *pPage;
	int rc;

	rc = eDbpager_get(pPager, pgno, &pPage);
	if( rc==eDb_OK ){
		rc = eDbpager_write(pPage);
		if( rc==eDb_OK ){
			memcpy(pPage, pData, eDb_PAGE_SIZE);
		}
		eDbpager_unref(pPage);
	}
	return rc;
}

/*
** A call to this routine tells the pager that it is not necessary to
** write the information on page "pgno" back to the disk, even though
** that page might be marked as dirty.
**
** The overlying software layer calls this routine when all of the data
** on the given page is unused.  The pager marks the page as clean so
** that it does not get written to disk.
**
** Tests show that this optimization, together with the
** eDbpager_dont_rollback() below, more than double the speed
** of large INSERT operations and quadruple the speed of large DELETEs.
**
** When this routine is called, set the alwaysRollback flag to true.
** Subsequent calls to eDbpager_dont_rollback() for the same page
** will thereafter be ignored.  This is necessary to avoid a problem
** where a page with data is added to the freelist during one part of
** a transaction then removed from the freelist during a later part
** of the same transaction and reused for some other purpose.  When it
** is first added to the freelist, this routine is called.  When reused,
** the dont_rollback() routine is called.  But because the page contains
** critical data, we still need to be sure it gets rolled back in spite
** of the dont_rollback() call.
*/
void eDbpager_dont_write(Pager *pPager, Pgno pgno){
	PgHdr *pPg;

	pPg = pager_lookup(pPager, pgno);
	pPg->alwaysRollback = 1;
	if( pPg && pPg->dirty ){
		if( pPager->dbSize==(int)pPg->pgno && pPager->origDbSize<pPager->dbSize ){
		  /* If this pages is the last page in the file and the file has grown
		  ** during the current transaction, then do NOT mark the page as clean.
		  ** When the database file grows, we must make sure that the last page
		  ** gets written at least once so that the disk file will be the correct
		  ** size. If you do not write this page and the size of the file
		  ** on the disk ends up being too small, that can lead to database
		  ** corruption during the next transaction.
		  */
		}else{
			TRACE2("DONT_WRITE %d\n", pgno);
			pPg->dirty = 0;
		}
	}
}

/*
** A call to this routine tells the pager that if a rollback occurs,
** it is not necessary to restore the data on the given page.  This
** means that the pager does not have to record the given page in the
** rollback journal.
*/
void eDbpager_dont_rollback(void *pData){
	PgHdr *pPg = DATA_TO_PGHDR(pData);
	Pager *pPager = pPg->pPager;

	if( pPager->state!=eDb_WRITELOCK || pPager->journalOpen==0 ) return;
	if( pPg->alwaysRollback || pPager->alwaysRollback ) return;
	if( !pPg->inJournal && (int)pPg->pgno <= pPager->origDbSize ){
		assert( pPager->aInJournal!=0 );
		pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
		pPg->inJournal = 1;
		if( pPager->ckptInUse ){
			pPag

⌨️ 快捷键说明

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