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

📄 btree.c

📁 在VC6环境下开发
💻 C
📖 第 1 页 / 共 5 页
字号:
		pP1->iMagic = swab32(MAGIC);
		pBt->needSwab = 1;
	}
	zeroPage(pBt, pRoot);
	eDbpager_unref(pRoot);
	return eDb_OK;
}

/*
** Attempt to start a new transaction.
**
** A transaction must be started before attempting any changes
** to the database.  None of the following routines will work
** unless a transaction is started first:
**
**      eDbBtreeCreateTable()
**      eDbBtreeCreateIndex()
**      eDbBtreeClearTable()
**      eDbBtreeDropTable()
**      eDbBtreeInsert()
**      eDbBtreeDelete()
**      eDbBtreeUpdateMeta()
*/
static int fileBtreeBeginTrans(Btree *pBt){
	int rc;
	if( pBt->inTrans ) return eDb_ERROR;
	if( pBt->readOnly ) return eDb_READONLY;
	if( pBt->page1==0 ){
		rc = lockBtree(pBt);
		if( rc!=eDb_OK ){
			return rc;
		}
	}
	rc = eDbpager_begin(pBt->page1);
	if( rc==eDb_OK ){
		rc = newDatabase(pBt);
	}
	if( rc==eDb_OK ){
		pBt->inTrans = 1;
		pBt->inCkpt = 0;
	}else{
		unlockBtreeIfUnused(pBt);
	}
	return rc;
}

/*
** Commit the transaction currently in progress.
**
** This will release the write lock on the database file.  If there
** are no active cursors, it also releases the read lock.
*/
static int fileBtreeCommit(Btree *pBt){
	int rc;
	rc = pBt->readOnly ? eDb_OK : eDbpager_commit(pBt->pPager);
	pBt->inTrans = 0;
	pBt->inCkpt = 0;
	unlockBtreeIfUnused(pBt);
	return rc;
}

/*
** Rollback the transaction in progress.  All cursors will be
** invalided by this operation.  Any attempt to use a cursor
** that was open at the beginning of this operation will result
** in an error.
**
** This will release the write lock on the database file.  If there
** are no active cursors, it also releases the read lock.
*/
static int fileBtreeRollback(Btree *pBt){
	int rc;
	BtCursor *pCur;
	if( pBt->inTrans==0 ) return eDb_OK;
	pBt->inTrans = 0;
	pBt->inCkpt = 0;
	rc = pBt->readOnly ? eDb_OK : eDbpager_rollback(pBt->pPager);
	for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
		if( pCur->pPage && pCur->pPage->isInit==0 ){
			eDbpager_unref(pCur->pPage);
			pCur->pPage = 0;
		}
	}
	unlockBtreeIfUnused(pBt);
	return rc;
}

/*
** Set the checkpoint for the current transaction.  The checkpoint serves
** as a sub-transaction that can be rolled back independently of the
** main transaction.  You must start a transaction before starting a
** checkpoint.  The checkpoint is ended automatically if the transaction
** commits or rolls back.
**
** Only one checkpoint may be active at a time.  It is an error to try
** to start a new checkpoint if another checkpoint is already active.
*/
static int fileBtreeBeginCkpt(Btree *pBt){
	int rc;
	if( !pBt->inTrans || pBt->inCkpt ){
		return pBt->readOnly ? eDb_READONLY : eDb_ERROR;
	}
	rc = pBt->readOnly ? eDb_OK : eDbpager_ckpt_begin(pBt->pPager);
	pBt->inCkpt = 1;
	return rc;
}


/*
** Commit a checkpoint to transaction currently in progress.  If no
** checkpoint is active, this is a no-op.
*/
static int fileBtreeCommitCkpt(Btree *pBt){
	int rc;
	if( pBt->inCkpt && !pBt->readOnly ){
		rc = eDbpager_ckpt_commit(pBt->pPager);
	}else{
		rc = eDb_OK;
	}
	pBt->inCkpt = 0;
	return rc;
}

/*
** Rollback the checkpoint to the current transaction.  If there
** is no active checkpoint or transaction, this routine is a no-op.
**
** All cursors will be invalided by this operation.  Any attempt
** to use a cursor that was open at the beginning of this operation
** will result in an error.
*/
static int fileBtreeRollbackCkpt(Btree *pBt){
	int rc;
	BtCursor *pCur;
	if( pBt->inCkpt==0 || pBt->readOnly ) return eDb_OK;
	rc = eDbpager_ckpt_rollback(pBt->pPager);
	for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
		if( pCur->pPage && pCur->pPage->isInit==0 ){
			eDbpager_unref(pCur->pPage);
			pCur->pPage = 0;
		}
	}
	pBt->inCkpt = 0;
	return rc;
}

/*
** Create a new cursor for the BTree whose root is on the page
** iTable.  The act of acquiring a cursor gets a read lock on 
** the database file.
**
** If wrFlag==0, then the cursor can only be used for reading.
** If wrFlag==1, then the cursor can be used for reading or for
** writing if other conditions for writing are also met.  These
** are the conditions that must be met in order for writing to
** be allowed:
**
** 1:  The cursor must have been opened with wrFlag==1
**
** 2:  No other cursors may be open with wrFlag==0 on the same table
**
** 3:  The database must be writable (not on read-only media)
**
** 4:  There must be an active transaction.
**
** Condition 2 warrants further discussion.  If any cursor is opened
** on a table with wrFlag==0, that prevents all other cursors from
** writing to that table.  This is a kind of "read-lock".  When a cursor
** is opened with wrFlag==0 it is guaranteed that the table will not
** change as long as the cursor is open.  This allows the cursor to
** do a sequential scan of the table without having to worry about
** entries being inserted or deleted during the scan.  Cursors should
** be opened with wrFlag==0 only if this read-lock property is needed.
** That is to say, cursors should be opened with wrFlag==0 only if they
** intend to use the eDbBtreeNext() system call.  All other cursors
** should be opened with wrFlag==1 even if they never really intend
** to write.
** 
** No checking is done to make sure that page iTable really is the
** root page of a b-tree.  If it is not, then the cursor acquired
** will not work correctly.
*/
static int fileBtreeCursor(Btree *pBt, int iTable, int wrFlag, BtCursor **ppCur){
	int rc;
	BtCursor *pCur, *pRing;

	if( pBt->page1==0 ){
		rc = lockBtree(pBt);
		if( rc!=eDb_OK ){
			*ppCur = 0;
			return rc;
		}
	}
	pCur = eDbMalloc( sizeof(*pCur) );
	if( pCur==0 ){
		rc = eDb_NOMEM;
		goto create_cursor_exception;
	}
	pCur->pgnoRoot = (Pgno)iTable;
	rc = eDbpager_get(pBt->pPager, pCur->pgnoRoot, (void**)&pCur->pPage);
	if( rc!=eDb_OK ){
		goto create_cursor_exception;
	}
	rc = initPage(pBt, pCur->pPage, pCur->pgnoRoot, 0);
	if( rc!=eDb_OK ){
		goto create_cursor_exception;
	}
	pCur->pOps = &eDbBtreeCursorOps;
	pCur->pBt = pBt;
	pCur->wrFlag = wrFlag;
	pCur->idx = 0;
	pCur->eSkip = SKIP_INVALID;
	pCur->pNext = pBt->pCursor;
	if( pCur->pNext ){
		pCur->pNext->pPrev = pCur;
	}
	pCur->pPrev = 0;
	pRing = pBt->pCursor;
	while( pRing && pRing->pgnoRoot!=pCur->pgnoRoot ){ pRing = pRing->pNext; }
	if( pRing ){
		pCur->pShared = pRing->pShared;
		pRing->pShared = pCur;
	}else{
		pCur->pShared = pCur;
	}
	pBt->pCursor = pCur;
	*ppCur = pCur;
	return eDb_OK;

create_cursor_exception:
	*ppCur = 0;
	if( pCur ){
		if( pCur->pPage ) eDbpager_unref(pCur->pPage);
		eDbFree(pCur);
	}
	unlockBtreeIfUnused(pBt);
	return rc;
}

/*
** Close a cursor.  The read lock on the database file is released
** when the last cursor is closed.
*/
static int fileBtreeCloseCursor(BtCursor *pCur){
	Btree *pBt = pCur->pBt;
	if( pCur->pPrev ){
		pCur->pPrev->pNext = pCur->pNext;
	}else{
		pBt->pCursor = pCur->pNext;
	}
	if( pCur->pNext ){
		pCur->pNext->pPrev = pCur->pPrev;
	}
	if( pCur->pPage ){
		eDbpager_unref(pCur->pPage);
	}
	if( pCur->pShared!=pCur ){
		BtCursor *pRing = pCur->pShared;
		while( pRing->pShared!=pCur ){ pRing = pRing->pShared; }
		pRing->pShared = pCur->pShared;
	}
	unlockBtreeIfUnused(pBt);
	eDbFree(pCur);
	return eDb_OK;
}

/*
** Make a temporary cursor by filling in the fields of pTempCur.
** The temporary cursor is not on the cursor list for the Btree.
*/
static void getTempCursor(BtCursor *pCur, BtCursor *pTempCur){
	memcpy(pTempCur, pCur, sizeof(*pCur));
	pTempCur->pNext = 0;
	pTempCur->pPrev = 0;
	if( pTempCur->pPage ){
		eDbpager_ref(pTempCur->pPage);
	}
}

/*
** Delete a temporary cursor such as was made by the CreateTemporaryCursor()
** function above.
*/
static void releaseTempCursor(BtCursor *pCur){
	if( pCur->pPage ){
		eDbpager_unref(pCur->pPage);
	}
}

/*
** Set *pSize to the number of bytes of key in the entry the
** cursor currently points to.  Always return eDb_OK.
** Failure is not possible.  If the cursor is not currently
** pointing to an entry (which can happen, for example, if
** the database is empty) then *pSize is set to 0.
*/
static int fileBtreeKeySize(BtCursor *pCur, int *pSize){
	Cell *pCell;
	MemPage *pPage;

	pPage = pCur->pPage;
	assert( pPage!=0 );
	if( pCur->idx >= pPage->nCell ){
		*pSize = 0;
	}else{
		pCell = pPage->apCell[pCur->idx];
		*pSize = NKEY(pCur->pBt, pCell->h);
	}
	return eDb_OK;
}

/*
** Read payload information from the entry that the pCur cursor is
** pointing to.  Begin reading the payload at "offset" and read
** a total of "amt" bytes.  Put the result in zBuf.
**
** This routine does not make a distinction between key and data.
** It just reads bytes from the payload area.
*/
static int getPayload(BtCursor *pCur, int offset, int amt, char *zBuf){
	char *aPayload;
	Pgno nextPage;
	int rc;
	Btree *pBt = pCur->pBt;
	assert( pCur!=0 && pCur->pPage!=0 );
	assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
	aPayload = pCur->pPage->apCell[pCur->idx]->aPayload;
	if( offset<MX_LOCAL_PAYLOAD ){
		int a = amt;
		if( a+offset>MX_LOCAL_PAYLOAD ){
			a = MX_LOCAL_PAYLOAD - offset;
		}
		memcpy(zBuf, &aPayload[offset], a);
		if( a==amt ){
			return eDb_OK;
		}
		offset = 0;
		zBuf += a;
		amt -= a;
	}else{
		offset -= MX_LOCAL_PAYLOAD;
	}
	if( amt>0 ){
		nextPage = SWAB32(pBt, pCur->pPage->apCell[pCur->idx]->ovfl);
	}
	while( amt>0 && nextPage ){
		OverflowPage *pOvfl;
		rc = eDbpager_get(pBt->pPager, nextPage, (void**)&pOvfl);
		if( rc!=0 ){
			return rc;
		}
		nextPage = SWAB32(pBt, pOvfl->iNext);
		if( offset<OVERFLOW_SIZE ){
		int a = amt;
		if( a + offset > OVERFLOW_SIZE ){
			a = OVERFLOW_SIZE - offset;
		}
		memcpy(zBuf, &pOvfl->aPayload[offset], a);
		offset = 0;
		amt -= a;
		zBuf += a;
		}else{
			offset -= OVERFLOW_SIZE;
		}
		eDbpager_unref(pOvfl);
	}
	if( amt>0 ){
		return eDb_CORRUPT;
	}
	return eDb_OK;
}

/*
** Read part of the key associated with cursor pCur.  A maximum
** of "amt" bytes will be transfered into zBuf[].  The transfer
** begins at "offset".  The number of bytes actually read is
** returned. 
**
** Change:  It used to be that the amount returned will be smaller
** than the amount requested if there are not enough bytes in the key
** to satisfy the request.  But now, it must be the case that there
** is enough data available to satisfy the request.  If not, an exception
** is raised.  The change was made in an effort to boost performance
** by eliminating unneeded tests.
*/
static int fileBtreeKey(BtCursor *pCur, int offset, int amt, char *zBuf){
	MemPage *pPage;

	assert( amt>=0 );
	assert( offset>=0 );
	assert( pCur->pPage!=0 );
	pPage = pCur->pPage;
	if( pCur->idx >= pPage->nCell ){
		return 0;
	}
	assert( amt+offset <= NKEY(pCur->pBt, pPage->apCell[pCur->idx]->h) );
	getPayload(pCur, offset, amt, zBuf);
	return amt;
}

/*
** Set *pSize to the number of bytes of data in the entry the
** cursor currently points to.  Always return eDb_OK.
** Failure is not possible.  If the cursor is not currently
** pointing to an entry (which can happen, for example, if
** the database is empty) then *pSize is set to 0.
*/
static int fileBtreeDataSize(BtCursor *pCur, int *pSize){
	Cell *pCell;
	MemPage *pPage;

	pPage = pCur->pPage;
	assert( pPage!=0 );
	if( pCur->idx >= pPage->nCell ){
		*pSize = 0;
	}else{
		pCell = pPage->apCell[pCur->idx];
		*pSize = NDATA(pCur->pBt, pCell->h);

⌨️ 快捷键说明

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