?? btree.c
字號:
pP1 = pBt->page1; rc = sqlitepager_write(pBt->page1); if( rc ) return rc; rc = sqlitepager_get(pBt->pPager, 2, (void**)&pRoot); if( rc ) return rc; rc = sqlitepager_write(pRoot); if( rc ){ sqlitepager_unref(pRoot); return rc; } strcpy(pP1->zMagic, zMagicHeader); if( btree_native_byte_order ){ pP1->iMagic = MAGIC; pBt->needSwab = 0; }else{ pP1->iMagic = swab32(MAGIC); pBt->needSwab = 1; } zeroPage(pBt, pRoot); sqlitepager_unref(pRoot); return SQLITE_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:**** sqliteBtreeCreateTable()** sqliteBtreeCreateIndex()** sqliteBtreeClearTable()** sqliteBtreeDropTable()** sqliteBtreeInsert()** sqliteBtreeDelete()** sqliteBtreeUpdateMeta()*/static int fileBtreeBeginTrans(Btree *pBt){ int rc; if( pBt->inTrans ) return SQLITE_ERROR; if( pBt->readOnly ) return SQLITE_READONLY; if( pBt->page1==0 ){ rc = lockBtree(pBt); if( rc!=SQLITE_OK ){ return rc; } } rc = sqlitepager_begin(pBt->page1); if( rc==SQLITE_OK ){ rc = newDatabase(pBt); } if( rc==SQLITE_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 ? SQLITE_OK : sqlitepager_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 SQLITE_OK; pBt->inTrans = 0; pBt->inCkpt = 0; rc = pBt->readOnly ? SQLITE_OK : sqlitepager_rollback(pBt->pPager); for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ if( pCur->pPage && pCur->pPage->isInit==0 ){ sqlitepager_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 ? SQLITE_READONLY : SQLITE_ERROR; } rc = pBt->readOnly ? SQLITE_OK : sqlitepager_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 = sqlitepager_ckpt_commit(pBt->pPager); }else{ rc = SQLITE_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 SQLITE_OK; rc = sqlitepager_ckpt_rollback(pBt->pPager); for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ if( pCur->pPage && pCur->pPage->isInit==0 ){ sqlitepager_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 sqliteBtreeNext() 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->readOnly && wrFlag ){ *ppCur = 0; return SQLITE_READONLY; } if( pBt->page1==0 ){ rc = lockBtree(pBt); if( rc!=SQLITE_OK ){ *ppCur = 0; return rc; } } pCur = sqliteMalloc( sizeof(*pCur) ); if( pCur==0 ){ rc = SQLITE_NOMEM; goto create_cursor_exception; } pCur->pgnoRoot = (Pgno)iTable; rc = sqlitepager_get(pBt->pPager, pCur->pgnoRoot, (void**)&pCur->pPage); if( rc!=SQLITE_OK ){ goto create_cursor_exception; } rc = initPage(pBt, pCur->pPage, pCur->pgnoRoot, 0); if( rc!=SQLITE_OK ){ goto create_cursor_exception; } pCur->pOps = &sqliteBtreeCursorOps; 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 SQLITE_OK;create_cursor_exception: *ppCur = 0; if( pCur ){ if( pCur->pPage ) sqlitepager_unref(pCur->pPage); sqliteFree(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 ){ sqlitepager_unref(pCur->pPage); } if( pCur->pShared!=pCur ){ BtCursor *pRing = pCur->pShared; while( pRing->pShared!=pCur ){ pRing = pRing->pShared; } pRing->pShared = pCur->pShared; } unlockBtreeIfUnused(pBt); sqliteFree(pCur); return SQLITE_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 ){ sqlitepager_ref(pTempCur->pPage); }}/*** Delete a temporary cursor such as was made by the CreateTemporaryCursor()** function above.*/static void releaseTempCursor(BtCursor *pCur){ if( pCur->pPage ){ sqlitepager_unref(pCur->pPage); }}/*** Set *pSize to the number of bytes of key in the entry the** cursor currently points to. Always return SQLITE_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 SQLITE_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 SQLITE_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 = sqlitepager_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; } sqlitepager_unref(pOvfl); } if( amt>0 ){ return SQLITE_CORRUPT; } return SQLITE_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;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -