/drivers/sqlite-wp7/sqlite/btree_c.cs
C# | 9518 lines | 6172 code | 602 blank | 2744 comment | 1863 complexity | 577f6e1cb79f3f8dfb87f580fc3d20fd MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- using System;
- using System.Diagnostics;
- using System.Text;
- using i64 = System.Int64;
- using u8 = System.Byte;
- using u16 = System.UInt16;
- using u32 = System.UInt32;
- using u64 = System.UInt64;
- using sqlite3_int64 = System.Int64;
- using Pgno = System.UInt32;
- namespace Community.CsharpSqlite
- {
- using DbPage = Sqlite3.PgHdr;
- public partial class Sqlite3
- {
- /*
- ** 2004 April 6
- **
- ** The author disclaims copyright to this source code. In place of
- ** a legal notice, here is a blessing:
- **
- ** May you do good and not evil.
- ** May you find forgiveness for yourself and forgive others.
- ** May you share freely, never taking more than you give.
- **
- ** This file implements a external (disk-based) database using BTrees.
- ** See the header comment on "btreeInt.h" for additional information.
- ** Including a description of file format and an overview of operation.
- *************************************************************************
- ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
- ** C#-SQLite is an independent reimplementation of the SQLite software library
- **
- ** SQLITE_SOURCE_ID: 2011-01-28 17:03:50 ed759d5a9edb3bba5f48f243df47be29e3fe8cd7
- **
- *************************************************************************
- */
- //#include "btreeInt.h"
- /*
- ** The header string that appears at the beginning of every
- ** SQLite database.
- */
- static byte[] zMagicHeader = Encoding.UTF8.GetBytes( SQLITE_FILE_HEADER );
- /*
- ** Set this global variable to 1 to enable tracing using the TRACE
- ** macro.
- */
- #if TRACE
- static bool sqlite3BtreeTrace=false; /* True to enable tracing */
- //# define TRACE(X) if(sqlite3BtreeTrace){printf X;fflush(stdout);}
- static void TRACE(string X, params object[] ap) { if (sqlite3BtreeTrace) printf(X, ap); }
- #else
- //# define TRACE(X)
- static void TRACE( string X, params object[] ap )
- {
- }
- #endif
- /*
- ** Extract a 2-byte big-endian integer from an array of unsigned bytes.
- ** But if the value is zero, make it 65536.
- **
- ** This routine is used to extract the "offset to cell content area" value
- ** from the header of a btree page. If the page size is 65536 and the page
- ** is empty, the offset should be 65536, but the 2-byte value stores zero.
- ** This routine makes the necessary adjustment to 65536.
- */
- //#define get2byteNotZero(X) (((((int)get2byte(X))-1)&0xffff)+1)
- static int get2byteNotZero( byte[] X, int offset )
- {
- return ( ( ( ( (int)get2byte( X, offset ) ) - 1 ) & 0xffff ) + 1 );
- }
- #if !SQLITE_OMIT_SHARED_CACHE
- /*
- ** A list of BtShared objects that are eligible for participation
- ** in shared cache. This variable has file scope during normal builds,
- ** but the test harness needs to access it so we make it global for
- ** test builds.
- **
- ** Access to this variable is protected by SQLITE_MUTEX_STATIC_MASTER.
- */
- #if SQLITE_TEST
- BtShared *SQLITE_WSD sqlite3SharedCacheList = 0;
- #else
- static BtShared *SQLITE_WSD sqlite3SharedCacheList = 0;
- #endif
- #endif //* SQLITE_OMIT_SHARED_CACHE */
- #if !SQLITE_OMIT_SHARED_CACHE
- /*
- ** Enable or disable the shared pager and schema features.
- **
- ** This routine has no effect on existing database connections.
- ** The shared cache setting effects only future calls to
- ** sqlite3_open(), sqlite3_open16(), or sqlite3_open_v2().
- */
- int sqlite3_enable_shared_cache(int enable){
- sqlite3GlobalConfig.sharedCacheEnabled = enable;
- return SQLITE_OK;
- }
- #endif
- #if SQLITE_OMIT_SHARED_CACHE
- /*
- ** The functions querySharedCacheTableLock(), setSharedCacheTableLock(),
- ** and clearAllSharedCacheTableLocks()
- ** manipulate entries in the BtShared.pLock linked list used to store
- ** shared-cache table level locks. If the library is compiled with the
- ** shared-cache feature disabled, then there is only ever one user
- ** of each BtShared structure and so this locking is not necessary.
- ** So define the lock related functions as no-ops.
- */
- //#define querySharedCacheTableLock(a,b,c) SQLITE_OK
- static int querySharedCacheTableLock( Btree p, Pgno iTab, u8 eLock )
- {
- return SQLITE_OK;
- }
- //#define setSharedCacheTableLock(a,b,c) SQLITE_OK
- //#define clearAllSharedCacheTableLocks(a)
- static void clearAllSharedCacheTableLocks( Btree a )
- {
- }
- //#define downgradeAllSharedCacheTableLocks(a)
- static void downgradeAllSharedCacheTableLocks( Btree a )
- {
- }
- //#define hasSharedCacheTableLock(a,b,c,d) 1
- static bool hasSharedCacheTableLock( Btree a, Pgno b, int c, int d )
- {
- return true;
- }
- //#define hasReadConflicts(a, b) 0
- static bool hasReadConflicts( Btree a, Pgno b )
- {
- return false;
- }
- #endif
- #if !SQLITE_OMIT_SHARED_CACHE
- #if SQLITE_DEBUG
- /*
- **** This function is only used as part of an assert() statement. ***
- **
- ** Check to see if pBtree holds the required locks to read or write to the
- ** table with root page iRoot. Return 1 if it does and 0 if not.
- **
- ** For example, when writing to a table with root-page iRoot via
- ** Btree connection pBtree:
- **
- ** assert( hasSharedCacheTableLock(pBtree, iRoot, 0, WRITE_LOCK) );
- **
- ** When writing to an index that resides in a sharable database, the
- ** caller should have first obtained a lock specifying the root page of
- ** the corresponding table. This makes things a bit more complicated,
- ** as this module treats each table as a separate structure. To determine
- ** the table corresponding to the index being written, this
- ** function has to search through the database schema.
- **
- ** Instead of a lock on the table/index rooted at page iRoot, the caller may
- ** hold a write-lock on the schema table (root page 1). This is also
- ** acceptable.
- */
- static int hasSharedCacheTableLock(
- Btree pBtree, /* Handle that must hold lock */
- Pgno iRoot, /* Root page of b-tree */
- int isIndex, /* True if iRoot is the root of an index b-tree */
- int eLockType /* Required lock type (READ_LOCK or WRITE_LOCK) */
- ){
- Schema pSchema = (Schema *)pBtree.pBt.pSchema;
- Pgno iTab = 0;
- BtLock pLock;
- /* If this database is not shareable, or if the client is reading
- ** and has the read-uncommitted flag set, then no lock is required.
- ** Return true immediately.
- */
- if( (pBtree.sharable==null)
- || (eLockType==READ_LOCK && (pBtree.db.flags & SQLITE_ReadUncommitted))
- ){
- return 1;
- }
- /* If the client is reading or writing an index and the schema is
- ** not loaded, then it is too difficult to actually check to see if
- ** the correct locks are held. So do not bother - just return true.
- ** This case does not come up very often anyhow.
- */
- if( isIndex && (!pSchema || (pSchema->flags&DB_SchemaLoaded)==0) ){
- return 1;
- }
- /* Figure out the root-page that the lock should be held on. For table
- ** b-trees, this is just the root page of the b-tree being read or
- ** written. For index b-trees, it is the root page of the associated
- ** table. */
- if( isIndex ){
- HashElem p;
- for(p=sqliteHashFirst(pSchema.idxHash); p!=null; p=sqliteHashNext(p)){
- Index pIdx = (Index *)sqliteHashData(p);
- if( pIdx.tnum==(int)iRoot ){
- iTab = pIdx.pTable.tnum;
- }
- }
- }else{
- iTab = iRoot;
- }
- /* Search for the required lock. Either a write-lock on root-page iTab, a
- ** write-lock on the schema table, or (if the client is reading) a
- ** read-lock on iTab will suffice. Return 1 if any of these are found. */
- for(pLock=pBtree.pBt.pLock; pLock; pLock=pLock.pNext){
- if( pLock.pBtree==pBtree
- && (pLock.iTable==iTab || (pLock.eLock==WRITE_LOCK && pLock.iTable==1))
- && pLock.eLock>=eLockType
- ){
- return 1;
- }
- }
- /* Failed to find the required lock. */
- return 0;
- }
- #endif //* SQLITE_DEBUG */
- #if SQLITE_DEBUG
- /*
- ** This function may be used as part of assert() statements only. ****
- **
- ** Return true if it would be illegal for pBtree to write into the
- ** table or index rooted at iRoot because other shared connections are
- ** simultaneously reading that same table or index.
- **
- ** It is illegal for pBtree to write if some other Btree object that
- ** shares the same BtShared object is currently reading or writing
- ** the iRoot table. Except, if the other Btree object has the
- ** read-uncommitted flag set, then it is OK for the other object to
- ** have a read cursor.
- **
- ** For example, before writing to any part of the table or index
- ** rooted at page iRoot, one should call:
- **
- ** assert( !hasReadConflicts(pBtree, iRoot) );
- */
- static int hasReadConflicts(Btree pBtree, Pgno iRoot){
- BtCursor p;
- for(p=pBtree.pBt.pCursor; p!=null; p=p.pNext){
- if( p.pgnoRoot==iRoot
- && p.pBtree!=pBtree
- && 0==(p.pBtree.db.flags & SQLITE_ReadUncommitted)
- ){
- return 1;
- }
- }
- return 0;
- }
- #endif //* #if SQLITE_DEBUG */
- /*
- ** Query to see if Btree handle p may obtain a lock of type eLock
- ** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return
- ** SQLITE_OK if the lock may be obtained (by calling
- ** setSharedCacheTableLock()), or SQLITE_LOCKED if not.
- */
- static int querySharedCacheTableLock(Btree p, Pgno iTab, u8 eLock){
- BtShared pBt = p.pBt;
- BtLock pIter;
- Debug.Assert( sqlite3BtreeHoldsMutex(p) );
- Debug.Assert( eLock==READ_LOCK || eLock==WRITE_LOCK );
- Debug.Assert( p.db!=null );
- Debug.Assert( !(p.db.flags&SQLITE_ReadUncommitted)||eLock==WRITE_LOCK||iTab==1 );
- /* If requesting a write-lock, then the Btree must have an open write
- ** transaction on this file. And, obviously, for this to be so there
- ** must be an open write transaction on the file itself.
- */
- Debug.Assert( eLock==READ_LOCK || (p==pBt.pWriter && p.inTrans==TRANS_WRITE) );
- Debug.Assert( eLock==READ_LOCK || pBt.inTransaction==TRANS_WRITE );
- /* This routine is a no-op if the shared-cache is not enabled */
- if( !p.sharable ){
- return SQLITE_OK;
- }
- /* If some other connection is holding an exclusive lock, the
- ** requested lock may not be obtained.
- */
- if( pBt.pWriter!=p && pBt.isExclusive ){
- sqlite3ConnectionBlocked(p.db, pBt.pWriter.db);
- return SQLITE_LOCKED_SHAREDCACHE;
- }
- for(pIter=pBt.pLock; pIter; pIter=pIter.pNext){
- /* The condition (pIter.eLock!=eLock) in the following if(...)
- ** statement is a simplification of:
- **
- ** (eLock==WRITE_LOCK || pIter.eLock==WRITE_LOCK)
- **
- ** since we know that if eLock==WRITE_LOCK, then no other connection
- ** may hold a WRITE_LOCK on any table in this file (since there can
- ** only be a single writer).
- */
- Debug.Assert( pIter.eLock==READ_LOCK || pIter.eLock==WRITE_LOCK );
- Debug.Assert( eLock==READ_LOCK || pIter.pBtree==p || pIter.eLock==READ_LOCK);
- if( pIter.pBtree!=p && pIter.iTable==iTab && pIter.eLock!=eLock ){
- sqlite3ConnectionBlocked(p.db, pIter.pBtree.db);
- if( eLock==WRITE_LOCK ){
- Debug.Assert( p==pBt.pWriter );
- pBt.isPending = 1;
- }
- return SQLITE_LOCKED_SHAREDCACHE;
- }
- }
- return SQLITE_OK;
- }
- #endif //* !SQLITE_OMIT_SHARED_CACHE */
- #if !SQLITE_OMIT_SHARED_CACHE
- /*
- ** Add a lock on the table with root-page iTable to the shared-btree used
- ** by Btree handle p. Parameter eLock must be either READ_LOCK or
- ** WRITE_LOCK.
- **
- ** This function assumes the following:
- **
- ** (a) The specified Btree object p is connected to a sharable
- ** database (one with the BtShared.sharable flag set), and
- **
- ** (b) No other Btree objects hold a lock that conflicts
- ** with the requested lock (i.e. querySharedCacheTableLock() has
- ** already been called and returned SQLITE_OK).
- **
- ** SQLITE_OK is returned if the lock is added successfully. SQLITE_NOMEM
- ** is returned if a malloc attempt fails.
- */
- static int setSharedCacheTableLock(Btree p, Pgno iTable, u8 eLock){
- BtShared pBt = p.pBt;
- BtLock pLock = 0;
- BtLock pIter;
- Debug.Assert( sqlite3BtreeHoldsMutex(p) );
- Debug.Assert( eLock==READ_LOCK || eLock==WRITE_LOCK );
- Debug.Assert( p.db!=null );
- /* A connection with the read-uncommitted flag set will never try to
- ** obtain a read-lock using this function. The only read-lock obtained
- ** by a connection in read-uncommitted mode is on the sqlite_master
- ** table, and that lock is obtained in BtreeBeginTrans(). */
- Debug.Assert( 0==(p.db.flags&SQLITE_ReadUncommitted) || eLock==WRITE_LOCK );
- /* This function should only be called on a sharable b-tree after it
- ** has been determined that no other b-tree holds a conflicting lock. */
- Debug.Assert( p.sharable );
- Debug.Assert( SQLITE_OK==querySharedCacheTableLock(p, iTable, eLock) );
- /* First search the list for an existing lock on this table. */
- for(pIter=pBt.pLock; pIter; pIter=pIter.pNext){
- if( pIter.iTable==iTable && pIter.pBtree==p ){
- pLock = pIter;
- break;
- }
- }
- /* If the above search did not find a BtLock struct associating Btree p
- ** with table iTable, allocate one and link it into the list.
- */
- if( !pLock ){
- pLock = (BtLock *)sqlite3MallocZero(sizeof(BtLock));
- if( !pLock ){
- return SQLITE_NOMEM;
- }
- pLock.iTable = iTable;
- pLock.pBtree = p;
- pLock.pNext = pBt.pLock;
- pBt.pLock = pLock;
- }
- /* Set the BtLock.eLock variable to the maximum of the current lock
- ** and the requested lock. This means if a write-lock was already held
- ** and a read-lock requested, we don't incorrectly downgrade the lock.
- */
- Debug.Assert( WRITE_LOCK>READ_LOCK );
- if( eLock>pLock.eLock ){
- pLock.eLock = eLock;
- }
- return SQLITE_OK;
- }
- #endif //* !SQLITE_OMIT_SHARED_CACHE */
- #if !SQLITE_OMIT_SHARED_CACHE
- /*
- ** Release all the table locks (locks obtained via calls to
- ** the setSharedCacheTableLock() procedure) held by Btree object p.
- **
- ** This function assumes that Btree p has an open read or write
- ** transaction. If it does not, then the BtShared.isPending variable
- ** may be incorrectly cleared.
- */
- static void clearAllSharedCacheTableLocks(Btree p){
- BtShared pBt = p.pBt;
- BtLock **ppIter = &pBt.pLock;
- Debug.Assert( sqlite3BtreeHoldsMutex(p) );
- Debug.Assert( p.sharable || 0==*ppIter );
- Debug.Assert( p.inTrans>0 );
- while( ppIter ){
- BtLock pLock = ppIter;
- Debug.Assert( pBt.isExclusive==null || pBt.pWriter==pLock.pBtree );
- Debug.Assert( pLock.pBtree.inTrans>=pLock.eLock );
- if( pLock.pBtree==p ){
- ppIter = pLock.pNext;
- Debug.Assert( pLock.iTable!=1 || pLock==&p.lock );
- if( pLock.iTable!=1 ){
- pLock=null;//sqlite3_free(ref pLock);
- }
- }else{
- ppIter = &pLock.pNext;
- }
- }
- Debug.Assert( pBt.isPending==null || pBt.pWriter );
- if( pBt.pWriter==p ){
- pBt.pWriter = 0;
- pBt.isExclusive = 0;
- pBt.isPending = 0;
- }else if( pBt.nTransaction==2 ){
- /* This function is called when Btree p is concluding its
- ** transaction. If there currently exists a writer, and p is not
- ** that writer, then the number of locks held by connections other
- ** than the writer must be about to drop to zero. In this case
- ** set the isPending flag to 0.
- **
- ** If there is not currently a writer, then BtShared.isPending must
- ** be zero already. So this next line is harmless in that case.
- */
- pBt.isPending = 0;
- }
- }
- /*
- ** This function changes all write-locks held by Btree p into read-locks.
- */
- static void downgradeAllSharedCacheTableLocks(Btree p){
- BtShared pBt = p.pBt;
- if( pBt.pWriter==p ){
- BtLock pLock;
- pBt.pWriter = 0;
- pBt.isExclusive = 0;
- pBt.isPending = 0;
- for(pLock=pBt.pLock; pLock; pLock=pLock.pNext){
- Debug.Assert( pLock.eLock==READ_LOCK || pLock.pBtree==p );
- pLock.eLock = READ_LOCK;
- }
- }
- }
- #endif //* SQLITE_OMIT_SHARED_CACHE */
- //static void releasePage(MemPage pPage); /* Forward reference */
- /*
- ***** This routine is used inside of assert() only ****
- **
- ** Verify that the cursor holds the mutex on its BtShared
- */
- #if SQLITE_DEBUG
- static bool cursorHoldsMutex( BtCursor p )
- {
- return sqlite3_mutex_held( p.pBt.mutex );
- }
- #else
- static bool cursorHoldsMutex(BtCursor p) { return true; }
- #endif
- #if !SQLITE_OMIT_INCRBLOB
- /*
- ** Invalidate the overflow page-list cache for cursor pCur, if any.
- */
- static void invalidateOverflowCache(BtCursor pCur){
- Debug.Assert( cursorHoldsMutex(pCur) );
- //sqlite3_free(ref pCur.aOverflow);
- pCur.aOverflow = null;
- }
- /*
- ** Invalidate the overflow page-list cache for all cursors opened
- ** on the shared btree structure pBt.
- */
- static void invalidateAllOverflowCache(BtShared pBt){
- BtCursor p;
- Debug.Assert( sqlite3_mutex_held(pBt.mutex) );
- for(p=pBt.pCursor; p!=null; p=p.pNext){
- invalidateOverflowCache(p);
- }
- }
- /*
- ** This function is called before modifying the contents of a table
- ** to invalidate any incrblob cursors that are open on the
- ** row or one of the rows being modified.
- **
- ** If argument isClearTable is true, then the entire contents of the
- ** table is about to be deleted. In this case invalidate all incrblob
- ** cursors open on any row within the table with root-page pgnoRoot.
- **
- ** Otherwise, if argument isClearTable is false, then the row with
- ** rowid iRow is being replaced or deleted. In this case invalidate
- ** only those incrblob cursors open on that specific row.
- */
- static void invalidateIncrblobCursors(
- Btree pBtree, /* The database file to check */
- i64 iRow, /* The rowid that might be changing */
- int isClearTable /* True if all rows are being deleted */
- ){
- BtCursor p;
- BtShared pBt = pBtree.pBt;
- Debug.Assert( sqlite3BtreeHoldsMutex(pBtree) );
- for(p=pBt.pCursor; p!=null; p=p.pNext){
- if( p.isIncrblobHandle && (isClearTable || p.info.nKey==iRow) ){
- p.eState = CURSOR_INVALID;
- }
- }
- }
- #else
- /* Stub functions when INCRBLOB is omitted */
- //#define invalidateOverflowCache(x)
- static void invalidateOverflowCache( BtCursor pCur )
- {
- }
- //#define invalidateAllOverflowCache(x)
- static void invalidateAllOverflowCache( BtShared pBt )
- {
- }
- //#define invalidateIncrblobCursors(x,y,z)
- static void invalidateIncrblobCursors( Btree x, i64 y, int z )
- {
- }
- #endif //* SQLITE_OMIT_INCRBLOB */
- /*
- ** Set bit pgno of the BtShared.pHasContent bitvec. This is called
- ** when a page that previously contained data becomes a free-list leaf
- ** page.
- **
- ** The BtShared.pHasContent bitvec exists to work around an obscure
- ** bug caused by the interaction of two useful IO optimizations surrounding
- ** free-list leaf pages:
- **
- ** 1) When all data is deleted from a page and the page becomes
- ** a free-list leaf page, the page is not written to the database
- ** (as free-list leaf pages contain no meaningful data). Sometimes
- ** such a page is not even journalled (as it will not be modified,
- ** why bother journalling it?).
- **
- ** 2) When a free-list leaf page is reused, its content is not read
- ** from the database or written to the journal file (why should it
- ** be, if it is not at all meaningful?).
- **
- ** By themselves, these optimizations work fine and provide a handy
- ** performance boost to bulk delete or insert operations. However, if
- ** a page is moved to the free-list and then reused within the same
- ** transaction, a problem comes up. If the page is not journalled when
- ** it is moved to the free-list and it is also not journalled when it
- ** is extracted from the free-list and reused, then the original data
- ** may be lost. In the event of a rollback, it may not be possible
- ** to restore the database to its original configuration.
- **
- ** The solution is the BtShared.pHasContent bitvec. Whenever a page is
- ** moved to become a free-list leaf page, the corresponding bit is
- ** set in the bitvec. Whenever a leaf page is extracted from the free-list,
- ** optimization 2 above is omitted if the corresponding bit is already
- ** set in BtShared.pHasContent. The contents of the bitvec are cleared
- ** at the end of every transaction.
- */
- static int btreeSetHasContent( BtShared pBt, Pgno pgno )
- {
- int rc = SQLITE_OK;
- if ( null == pBt.pHasContent )
- {
- Debug.Assert( pgno <= pBt.nPage );
- pBt.pHasContent = sqlite3BitvecCreate( pBt.nPage );
- if ( null == pBt.pHasContent )
- {
- rc = SQLITE_NOMEM;
- }
- }
- if ( rc == SQLITE_OK && pgno <= sqlite3BitvecSize( pBt.pHasContent ) )
- {
- rc = sqlite3BitvecSet( pBt.pHasContent, pgno );
- }
- return rc;
- }
- /*
- ** Query the BtShared.pHasContent vector.
- **
- ** This function is called when a free-list leaf page is removed from the
- ** free-list for reuse. It returns false if it is safe to retrieve the
- ** page from the pager layer with the 'no-content' flag set. True otherwise.
- */
- static bool btreeGetHasContent( BtShared pBt, Pgno pgno )
- {
- Bitvec p = pBt.pHasContent;
- return ( p != null && ( pgno > sqlite3BitvecSize( p ) || sqlite3BitvecTest( p, pgno ) != 0 ) );
- }
- /*
- ** Clear (destroy) the BtShared.pHasContent bitvec. This should be
- ** invoked at the conclusion of each write-transaction.
- */
- static void btreeClearHasContent( BtShared pBt )
- {
- sqlite3BitvecDestroy( ref pBt.pHasContent );
- pBt.pHasContent = null;
- }
- /*
- ** Save the current cursor position in the variables BtCursor.nKey
- ** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
- **
- ** The caller must ensure that the cursor is valid (has eState==CURSOR_VALID)
- ** prior to calling this routine.
- */
- static int saveCursorPosition( BtCursor pCur )
- {
- int rc;
- Debug.Assert( CURSOR_VALID == pCur.eState );
- Debug.Assert( null == pCur.pKey );
- Debug.Assert( cursorHoldsMutex( pCur ) );
- rc = sqlite3BtreeKeySize( pCur, ref pCur.nKey );
- Debug.Assert( rc == SQLITE_OK ); /* KeySize() cannot fail */
- /* If this is an intKey table, then the above call to BtreeKeySize()
- ** stores the integer key in pCur.nKey. In this case this value is
- ** all that is required. Otherwise, if pCur is not open on an intKey
- ** table, then malloc space for and store the pCur.nKey bytes of key
- ** data.
- */
- if ( 0 == pCur.apPage[0].intKey )
- {
- byte[] pKey = sqlite3Malloc( (int)pCur.nKey );
- //if( pKey !=null){
- rc = sqlite3BtreeKey( pCur, 0, (u32)pCur.nKey, pKey );
- if ( rc == SQLITE_OK )
- {
- pCur.pKey = pKey;
- }
- //else{
- // sqlite3_free(ref pKey);
- //}
- //}else{
- // rc = SQLITE_NOMEM;
- //}
- }
- Debug.Assert( 0 == pCur.apPage[0].intKey || null == pCur.pKey );
- if ( rc == SQLITE_OK )
- {
- int i;
- for ( i = 0; i <= pCur.iPage; i++ )
- {
- releasePage( pCur.apPage[i] );
- pCur.apPage[i] = null;
- }
- pCur.iPage = -1;
- pCur.eState = CURSOR_REQUIRESEEK;
- }
- invalidateOverflowCache( pCur );
- return rc;
- }
- /*
- ** Save the positions of all cursors (except pExcept) that are open on
- ** the table with root-page iRoot. Usually, this is called just before cursor
- ** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()).
- */
- static int saveAllCursors( BtShared pBt, Pgno iRoot, BtCursor pExcept )
- {
- BtCursor p;
- Debug.Assert( sqlite3_mutex_held( pBt.mutex ) );
- Debug.Assert( pExcept == null || pExcept.pBt == pBt );
- for ( p = pBt.pCursor; p != null; p = p.pNext )
- {
- if ( p != pExcept && ( 0 == iRoot || p.pgnoRoot == iRoot ) &&
- p.eState == CURSOR_VALID )
- {
- int rc = saveCursorPosition( p );
- if ( SQLITE_OK != rc )
- {
- return rc;
- }
- }
- }
- return SQLITE_OK;
- }
- /*
- ** Clear the current cursor position.
- */
- static void sqlite3BtreeClearCursor( BtCursor pCur )
- {
- Debug.Assert( cursorHoldsMutex( pCur ) );
- sqlite3_free( ref pCur.pKey );
- pCur.eState = CURSOR_INVALID;
- }
- /*
- ** In this version of BtreeMoveto, pKey is a packed index record
- ** such as is generated by the OP_MakeRecord opcode. Unpack the
- ** record and then call BtreeMovetoUnpacked() to do the work.
- */
- static int btreeMoveto(
- BtCursor pCur, /* Cursor open on the btree to be searched */
- byte[] pKey, /* Packed key if the btree is an index */
- i64 nKey, /* Integer key for tables. Size of pKey for indices */
- int bias, /* Bias search to the high end */
- ref int pRes /* Write search results here */
- )
- {
- int rc; /* Status code */
- UnpackedRecord pIdxKey; /* Unpacked index key */
- UnpackedRecord aSpace = new UnpackedRecord();//char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */
- if ( pKey != null )
- {
- Debug.Assert( nKey == (i64)(int)nKey );
- pIdxKey = sqlite3VdbeRecordUnpack( pCur.pKeyInfo, (int)nKey, pKey,
- aSpace, 16 );//sizeof( aSpace ) );
- if ( pIdxKey == null )
- return SQLITE_NOMEM;
- }
- else
- {
- pIdxKey = null;
- }
- rc = sqlite3BtreeMovetoUnpacked( pCur, pIdxKey, nKey, bias != 0 ? 1 : 0, ref pRes );
- if ( pKey != null )
- {
- sqlite3VdbeDeleteUnpackedRecord( pIdxKey );
- }
- return rc;
- }
- /*
- ** Restore the cursor to the position it was in (or as close to as possible)
- ** when saveCursorPosition() was called. Note that this call deletes the
- ** saved position info stored by saveCursorPosition(), so there can be
- ** at most one effective restoreCursorPosition() call after each
- ** saveCursorPosition().
- */
- static int btreeRestoreCursorPosition( BtCursor pCur )
- {
- int rc;
- Debug.Assert( cursorHoldsMutex( pCur ) );
- Debug.Assert( pCur.eState >= CURSOR_REQUIRESEEK );
- if ( pCur.eState == CURSOR_FAULT )
- {
- return pCur.skipNext;
- }
- pCur.eState = CURSOR_INVALID;
- rc = btreeMoveto( pCur, pCur.pKey, pCur.nKey, 0, ref pCur.skipNext );
- if ( rc == SQLITE_OK )
- {
- //sqlite3_free(ref pCur.pKey);
- pCur.pKey = null;
- Debug.Assert( pCur.eState == CURSOR_VALID || pCur.eState == CURSOR_INVALID );
- }
- return rc;
- }
- //#define restoreCursorPosition(p) \
- // (p.eState>=CURSOR_REQUIRESEEK ? \
- // btreeRestoreCursorPosition(p) : \
- // SQLITE_OK)
- static int restoreCursorPosition( BtCursor pCur )
- {
- if ( pCur.eState >= CURSOR_REQUIRESEEK )
- return btreeRestoreCursorPosition( pCur );
- else
- return SQLITE_OK;
- }
- /*
- ** Determine whether or not a cursor has moved from the position it
- ** was last placed at. Cursors can move when the row they are pointing
- ** at is deleted out from under them.
- **
- ** This routine returns an error code if something goes wrong. The
- ** integer pHasMoved is set to one if the cursor has moved and 0 if not.
- */
- static int sqlite3BtreeCursorHasMoved( BtCursor pCur, ref int pHasMoved )
- {
- int rc;
- rc = restoreCursorPosition( pCur );
- if ( rc != 0 )
- {
- pHasMoved = 1;
- return rc;
- }
- if ( pCur.eState != CURSOR_VALID || pCur.skipNext != 0 )
- {
- pHasMoved = 1;
- }
- else
- {
- pHasMoved = 0;
- }
- return SQLITE_OK;
- }
- #if !SQLITE_OMIT_AUTOVACUUM
- /*
- ** Given a page number of a regular database page, return the page
- ** number for the pointer-map page that contains the entry for the
- ** input page number.
- **
- ** Return 0 (not a valid page) for pgno==1 since there is
- ** no pointer map associated with page 1. The integrity_check logic
- ** requires that ptrmapPageno(*,1)!=1.
- */
- static Pgno ptrmapPageno( BtShared pBt, Pgno pgno )
- {
- int nPagesPerMapPage;
- Pgno iPtrMap, ret;
- Debug.Assert( sqlite3_mutex_held( pBt.mutex ) );
- if ( pgno < 2 )
- return 0;
- nPagesPerMapPage = (int)( pBt.usableSize / 5 + 1 );
- iPtrMap = (Pgno)( ( pgno - 2 ) / nPagesPerMapPage );
- ret = (Pgno)( iPtrMap * nPagesPerMapPage ) + 2;
- if ( ret == PENDING_BYTE_PAGE( pBt ) )
- {
- ret++;
- }
- return ret;
- }
- /*
- ** Write an entry into the pointer map.
- **
- ** This routine updates the pointer map entry for page number 'key'
- ** so that it maps to type 'eType' and parent page number 'pgno'.
- **
- ** If pRC is initially non-zero (non-SQLITE_OK) then this routine is
- ** a no-op. If an error occurs, the appropriate error code is written
- ** into pRC.
- */
- static void ptrmapPut( BtShared pBt, Pgno key, u8 eType, Pgno parent, ref int pRC )
- {
- PgHdr pDbPage = new PgHdr(); /* The pointer map page */
- u8[] pPtrmap; /* The pointer map data */
- Pgno iPtrmap; /* The pointer map page number */
- int offset; /* Offset in pointer map page */
- int rc; /* Return code from subfunctions */
- if ( pRC != 0 )
- return;
- Debug.Assert( sqlite3_mutex_held( pBt.mutex ) );
- /* The master-journal page number must never be used as a pointer map page */
- Debug.Assert( false == PTRMAP_ISPAGE( pBt, PENDING_BYTE_PAGE( pBt ) ) );
- Debug.Assert( pBt.autoVacuum );
- if ( key == 0 )
- {
- pRC = SQLITE_CORRUPT_BKPT();
- return;
- }
- iPtrmap = PTRMAP_PAGENO( pBt, key );
- rc = sqlite3PagerGet( pBt.pPager, iPtrmap, ref pDbPage );
- if ( rc != SQLITE_OK )
- {
- pRC = rc;
- return;
- }
- offset = (int)PTRMAP_PTROFFSET( iPtrmap, key );
- if ( offset < 0 )
- {
- pRC = SQLITE_CORRUPT_BKPT();
- goto ptrmap_exit;
- }
- pPtrmap = sqlite3PagerGetData( pDbPage );
- if ( eType != pPtrmap[offset] || sqlite3Get4byte( pPtrmap, offset + 1 ) != parent )
- {
- TRACE( "PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent );
- pRC = rc = sqlite3PagerWrite( pDbPage );
- if ( rc == SQLITE_OK )
- {
- pPtrmap[offset] = eType;
- sqlite3Put4byte( pPtrmap, offset + 1, parent );
- }
- }
- ptrmap_exit:
- sqlite3PagerUnref( pDbPage );
- }
- /*
- ** Read an entry from the pointer map.
- **
- ** This routine retrieves the pointer map entry for page 'key', writing
- ** the type and parent page number to pEType and pPgno respectively.
- ** An error code is returned if something goes wrong, otherwise SQLITE_OK.
- */
- static int ptrmapGet( BtShared pBt, Pgno key, ref u8 pEType, ref Pgno pPgno )
- {
- PgHdr pDbPage = new PgHdr();/* The pointer map page */
- int iPtrmap; /* Pointer map page index */
- u8[] pPtrmap; /* Pointer map page data */
- int offset; /* Offset of entry in pointer map */
- int rc;
- Debug.Assert( sqlite3_mutex_held( pBt.mutex ) );
- iPtrmap = (int)PTRMAP_PAGENO( pBt, key );
- rc = sqlite3PagerGet( pBt.pPager, (u32)iPtrmap, ref pDbPage );
- if ( rc != 0 )
- {
- return rc;
- }
- pPtrmap = sqlite3PagerGetData( pDbPage );
- offset = (int)PTRMAP_PTROFFSET( (u32)iPtrmap, key );
- // Under C# pEType will always exist. No need to test; //
- //Debug.Assert( pEType != 0 );
- pEType = pPtrmap[offset];
- // Under C# pPgno will always exist. No need to test; //
- //if ( pPgno != 0 )
- pPgno = sqlite3Get4byte( pPtrmap, offset + 1 );
- sqlite3PagerUnref( pDbPage );
- if ( pEType < 1 || pEType > 5 )
- return SQLITE_CORRUPT_BKPT();
- return SQLITE_OK;
- }
- #else //* if defined SQLITE_OMIT_AUTOVACUUM */
- //#define ptrmapPut(w,x,y,z,rc)
- //#define ptrmapGet(w,x,y,z) SQLITE_OK
- //#define ptrmapPutOvflPtr(x, y, rc)
- #endif
- /*
- ** Given a btree page and a cell index (0 means the first cell on
- ** the page, 1 means the second cell, and so forth) return a pointer
- ** to the cell content.
- **
- ** This routine works only for pages that do not contain overflow cells.
- */
- //#define findCell(P,I) \
- // ((P).aData + ((P).maskPage & get2byte((P).aData[(P).cellOffset+2*(I)])))
- static int findCell( MemPage pPage, int iCell )
- {
- return get2byte( pPage.aData, pPage.cellOffset + 2 * ( iCell ) );
- }
- /*
- ** This a more complex version of findCell() that works for
- ** pages that do contain overflow cells.
- */
- static int findOverflowCell( MemPage pPage, int iCell )
- {
- int i;
- Debug.Assert( sqlite3_mutex_held( pPage.pBt.mutex ) );
- for ( i = pPage.nOverflow - 1; i >= 0; i-- )
- {
- int k;
- _OvflCell pOvfl;
- pOvfl = pPage.aOvfl[i];
- k = pOvfl.idx;
- if ( k <= iCell )
- {
- if ( k == iCell )
- {
- //return pOvfl.pCell;
- return -i - 1; // Negative Offset means overflow cells
- }
- iCell--;
- }
- }
- return findCell( pPage, iCell );
- }
- /*
- ** Parse a cell content block and fill in the CellInfo structure. There
- ** are two versions of this function. btreeParseCell() takes a
- ** cell index as the second argument and btreeParseCellPtr()
- ** takes a pointer to the body of the cell as its second argument.
- **
- ** Within this file, the parseCell() macro can be called instead of
- ** btreeParseCellPtr(). Using some compilers, this will be faster.
- */
- //OVERLOADS
- static void btreeParseCellPtr(
- MemPage pPage, /* Page containing the cell */
- int iCell, /* Pointer to the cell text. */
- ref CellInfo pInfo /* Fill in this structure */
- )
- {
- btreeParseCellPtr( pPage, pPage.aData, iCell, ref pInfo );
- }
- static void btreeParseCellPtr(
- MemPage pPage, /* Page containing the cell */
- byte[] pCell, /* The actual data */
- ref CellInfo pInfo /* Fill in this structure */
- )
- {
- btreeParseCellPtr( pPage, pCell, 0, ref pInfo );
- }
- static void btreeParseCellPtr(
- MemPage pPage, /* Page containing the cell */
- u8[] pCell, /* Pointer to the cell text. */
- int iCell, /* Pointer to the cell text. */
- ref CellInfo pInfo /* Fill in this structure */
- )
- {
- u16 n; /* Number bytes in cell content header */
- u32 nPayload = 0; /* Number of bytes of cell payload */
- Debug.Assert( sqlite3_mutex_held( pPage.pBt.mutex ) );
- if ( pInfo.pCell != pCell )
- pInfo.pCell = pCell;
- pInfo.iCell = iCell;
- Debug.Assert( pPage.leaf == 0 || pPage.leaf == 1 );
- n = pPage.childPtrSize;
- Debug.Assert( n == 4 - 4 * pPage.leaf );
- if ( pPage.intKey != 0 )
- {
- if ( pPage.hasData != 0 )
- {
- n += (u16)getVarint32( pCell, iCell + n, ref nPayload );
- }
- else
- {
- nPayload = 0;
- }
- n += (u16)getVarint( pCell, iCell + n, ref pInfo.nKey );
- pInfo.nData = nPayload;
- }
- else
- {
- pInfo.nData = 0;
- n += (u16)getVarint32( pCell, iCell + n, ref nPayload );
- pInfo.nKey = nPayload;
- }
- pInfo.nPayload = nPayload;
- pInfo.nHeader = n;
- testcase( nPayload == pPage.maxLocal );
- testcase( nPayload == pPage.maxLocal + 1 );
- if ( likely( nPayload <= pPage.maxLocal ) )
- {
- /* This is the (easy) common case where the entire payload fits
- ** on the local page. No overflow is required.
- */
- if ( ( pInfo.nSize = (u16)( n + nPayload ) ) < 4 )
- pInfo.nSize = 4;
- pInfo.nLocal = (u16)nPayload;
- pInfo.iOverflow = 0;
- }
- else
- {
- /* If the payload will not fit completely on the local page, we have
- ** to decide how much to store locally and how much to spill onto
- ** overflow pages. The strategy is to minimize the amount of unused
- ** space on overflow pages while keeping the amount of local storage
- ** in between minLocal and maxLocal.
- **
- ** Warning: changing the way overflow payload is distributed in any
- ** way will result in an incompatible file format.
- */
- int minLocal; /* Minimum amount of payload held locally */
- int maxLocal; /* Maximum amount of payload held locally */
- int surplus; /* Overflow payload available for local storage */
- minLocal = pPage.minLocal;
- maxLocal = pPage.maxLocal;
- surplus = (int)( minLocal + ( nPayload - minLocal ) % ( pPage.pBt.usableSize - 4 ) );
- testcase( surplus == maxLocal );
- testcase( surplus == maxLocal + 1 );
- if ( surplus <= maxLocal )
- {
- pInfo.nLocal = (u16)surplus;
- }
- else
- {
- pInfo.nLocal = (u16)minLocal;
- }
- pInfo.iOverflow = (u16)( pInfo.nLocal + n );
- pInfo.nSize = (u16)( pInfo.iOverflow + 4 );
- }
- }
- //#define parseCell(pPage, iCell, pInfo) \
- // btreeParseCellPtr((pPage), findCell((pPage), (iCell)), (pInfo))
- static void parseCell( MemPage pPage, int iCell, ref CellInfo pInfo )
- {
- btreeParseCellPtr( pPage, findCell( pPage, iCell ), ref pInfo );
- }
- static void btreeParseCell(
- MemPage pPage, /* Page containing the cell */
- int iCell, /* The cell index. First cell is 0 */
- ref CellInfo pInfo /* Fill in this structure */
- )
- {
- parseCell( pPage, iCell, ref pInfo );
- }
- /*
- ** Compute the total number of bytes that a Cell needs in the cell
- ** data area of the btree-page. The return number includes the cell
- ** data header and the local payload, but not any overflow page or
- ** the space used by the cell pointer.
- */
- // Alternative form for C#
- static u16 cellSizePtr( MemPage pPage, int iCell )
- {
- CellInfo info = new CellInfo();
- byte[] pCell = new byte[13];
- // Minimum Size = (2 bytes of Header or (4) Child Pointer) + (maximum of) 9 bytes data
- if ( iCell < 0 )// Overflow Cell
- Buffer.BlockCopy( pPage.aOvfl[-( iCell + 1 )].pCell, 0, pCell, 0, pCell.Length < pPage.aOvfl[-( iCell + 1 )].pCell.Length ? pCell.Length : pPage.aOvfl[-( iCell + 1 )].pCell.Length );
- else if ( iCell >= pPage.aData.Length + 1 - pCell.Length )
- Buffer.BlockCopy( pPage.aData, iCell, pCell, 0, pPage.aData.Length - iCell );
- else
- Buffer.BlockCopy( pPage.aData, iCell, pCell, 0, pCell.Length );
- btreeParseCellPtr( pPage, pCell, ref info );
- return info.nSize;
- }
- // Alternative form for C#
- static u16 cellSizePtr( MemPage pPage, byte[] pCell, int offset )
- {
- CellInfo info = new CellInfo();
- info.pCell = sqlite3Malloc( pCell.Length );
- Buffer.BlockCopy( pCell, offset, info.pCell, 0, pCell.Length - offset );
- btreeParseCellPtr( pPage, info.pCell, ref info );
- return info.nSize;
- }
- static u16 cellSizePtr( MemPage pPage, u8[] pCell )
- {
- int _pIter = pPage.childPtrSize; //u8 pIter = &pCell[pPage.childPtrSize];
- u32 nSize = 0;
- #if SQLITE_DEBUG || DEBUG
- /* The value returned by this function should always be the same as
- ** the (CellInfo.nSize) value found by doing a full parse of the
- ** cell. If SQLITE_DEBUG is defined, an Debug.Assert() at the bottom of
- ** this function verifies that this invariant is not violated. */
- CellInfo debuginfo = new CellInfo();
- btreeParseCellPtr( pPage, pCell, ref debuginfo );
- #else
- CellInfo debuginfo = new CellInfo();
- #endif
- if ( pPage.intKey != 0 )
- {
- int pEnd;
- if ( pPage.hasData != 0 )
- {
- _pIter += getVarint32( pCell, ref nSize );// pIter += getVarint32( pIter, ref nSize );
- }
- else
- {
- nSize = 0;
- }
- /* pIter now points at the 64-bit integer key value, a variable length
- ** integer. The following block moves pIter to point at the first byte
- ** past the end of the key value. */
- pEnd = _pIter + 9;//pEnd = &pIter[9];
- while ( ( ( pCell[_pIter++] ) & 0x80 ) != 0 && _pIter < pEnd )
- ;//while( (pIter++)&0x80 && pIter<pEnd );
- }
- else
- {
- _pIter += getVarint32( pCell, _pIter, ref nSize ); //pIter += getVarint32( pIter, ref nSize );
- }
- testcase( nSize == pPage.maxLocal );
- testcase( nSize == pPage.maxLocal + 1 );
- if ( nSize > pPage.maxLocal )
- {
- int minLocal = pPage.minLocal;
- nSize = (u32)( minLocal + ( nSize - minLocal ) % ( pPage.pBt.usableSize - 4 ) );
- testcase( nSize == pPage.maxLocal );
- testcase( nSize == pPage.maxLocal + 1 );
- if ( nSize > pPage.maxLocal )
- {
- nSize = (u32)minLocal;
- }
- nSize += 4;
- }
- nSize += (uint)_pIter;//nSize += (u32)(pIter - pCell);
- /* The minimum size of any cell is 4 bytes. */
- if ( nSize < 4 )
- {
- nSize = 4;
- }
- Debug.Assert( nSize == debuginfo.nSize );
- return (u16)nSize;
- }
- #if SQLITE_DEBUG
- /* This variation on cellSizePtr() is used inside of assert() statements
- ** only. */
- static u16 cellSize( MemPage pPage, int iCell )
- {
- return cellSizePtr( pPage, findCell( pPage, iCell ) );
- }
- #else
- static int cellSize(MemPage pPage, int iCell) { return -1; }
- #endif
- #if !SQLITE_OMIT_AUTOVACUUM
- /*
- ** If the cell pCell, part of page pPage contains a pointer
- ** to an overflow page, insert an entry into the pointer-map
- ** for the overflow page.
- */
- static void ptrmapPutOvflPtr( MemPage pPage, int pCell, ref int pRC )
- {
- if ( pRC != 0 )
- return;
- CellInfo info = new CellInfo();
- Debug.Assert( pCell != 0 );
- btreeParseCellPtr( pPage, pCell, ref info );
- Debug.Assert( ( info.nData + ( pPage.intKey != 0 ? 0 : info.nKey ) ) == info.nPayload );
- if ( info.iOverflow != 0 )
- {
- Pgno ovfl = sqlite3Get4byte( pPage.aData, pCell, info.iOverflow );
- ptrmapPut( pPage.pBt, ovfl, PTRMAP_OVERFLOW1, pPage.pgno, ref pRC );
- }
- }
- static void ptrmapPutOvflPtr( MemPage pPage, u8[] pCell, ref int pRC )
- {
- if ( pRC != 0 )
- return;
- CellInfo info = new CellInfo();
- Debug.Assert( pCell != null );
- btreeParseCellPtr( pPage, pCell, ref info );
- Debug.Assert( ( info.nData + ( pPage.intKey != 0 ? 0 : info.nKey ) ) == info.nPayload );
- if ( info.iOverflow != 0 )
- {
- Pgno ovfl = sqlite3Get4byte( pCell, info.iOverflow );
- ptrmapPut( pPage.pBt, ovfl, PTRMAP_OVERFLOW1, pPage.pgno, ref pRC );
- }
- }
- #endif
- /*
- ** Defragment the page given. All Cells are moved to the
- ** end of the page and all free space is collected into one
- ** big FreeBlk that occurs in between the header and cell
- ** pointer array and the cell content area.
- */
- static int defragmentPage( MemPage pPage )
- {
- int i; /* Loop counter */
- int pc; /* Address of a i-th cell */
- int addr; /* Offset of first byte after cell pointer array */
- int hdr; /* Offset to the page header */
- int size; /* Size of a cell */
- int usableSize; /* Number of usable bytes on a page */
- int cellOffset; /* Offset to the cell pointer array */
- int cbrk; /* Offset to the cell content area */
- int nCell; /* Number of cells on the page */
- byte[] data; /* The page data */
- byte[] temp; /* Temp area for cell content */
- int iCellFirst; /* First allowable cell index */
- int iCellLast; /* Last possible cell index */
- Debug.Assert( sqlite3PagerIswriteable( pPage.pDbPage ) );
- Debug.Assert( pPage.pBt != null );
- Debug.Assert( pPage.pBt.usableSize <= SQLITE_MAX_PAGE_SIZE );
- Debug.Assert( pPage.nOverflow == 0 );
- Debug.Assert( sqlite3_mutex_held( pPage.pBt.mutex ) );
- temp = sqlite3PagerTempSpace( pPage.pBt.pPager );
- data = pPage.aData;
- hdr = pPage.hdrOffset;
- cellOffset = pPage.cellOffset;
- nCell = pPage.nCell;
- Debug.Assert( nCell == get2byte( data, hdr + 3 ) );
- usableSize = (int)pPage.pBt.usableSize;
- cbrk = get2byte( data, hdr + 5 );
- Buffer.BlockCopy( data, cbrk, temp, cbrk, usableSize - cbrk );//memcpy( temp[cbrk], ref data[cbrk], usableSize - cbrk );
- cbrk = usableSize;
- iCellFirst = cellOffset + 2 * nCell;
- iCellLast = usableSize - 4;
- for ( i = 0; i < nCell; i++ )
- {
- int pAddr; /* The i-th cell pointer */
- pAddr = cellOffset + i * 2; // &data[cellOffset + i * 2];
- pc = get2byte( data, pAddr );
- testcase( pc == iCellFirst );
- testcase( pc == iCellLast );
- #if !(SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
- /* These conditions have already been verified in btreeInitPage()
- ** if SQLITE_ENABLE_OVERSIZE_CELL_CHECK is defined
- */
- if ( pc < iCellFirst || pc > iCellLast )
- {
- return SQLITE_CORRUPT_BKPT();
- }
- #endif
- Debug.Assert( pc >= iCellFirst && pc <= iCellLast );
- size = cellSizePtr( pPage, temp, pc );
- cbrk -= size;
- #if (SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
- if ( cbrk < iCellFirst )
- {
- return SQLITE_CORRUPT_BKPT();
- }
- #else
- if ( cbrk < iCellFirst || pc + size > usableSize )
- {
- return SQLITE_CORRUPT_BKPT();
- }
- #endif
- Debug.Assert( cbrk + size <= usableSize && cbrk >= iCellFirst );
- testcase( cbrk + size == usableSize );
- testcase( pc + size == usableSize );
- Buffer.BlockCopy( temp, pc, data, cbrk, size );//memcpy(data[cbrk], ref temp[pc], size);
- put2byte( data, pAddr, cbrk );
- }
- Debug.Assert( cbrk >= iCellFirst );
- put2byte( data, hdr + 5, cbrk );
- data[hdr + 1] = 0;
- data[hdr + 2] = 0;
- data[hdr + 7] = 0;
- addr = cellOffset + 2 * nCell;
- Array.Clear( data, addr, cbrk - addr ); //memset(data[iCellFirst], 0, cbrk-iCellFirst);
- Debug.Assert( sqlite3PagerIswriteable( pPage.pDbPage ) );
- if ( cbrk - iCellFirst != pPage.nFree )
- {
- return SQLITE_CORRUPT_BKPT();
- }
- return SQLITE_OK;
- }
- /*
- ** Allocate nByte bytes of space from within the B-Tree page passed
- ** as the first argument. Write into pIdx the index into pPage.aData[]
- ** of the first byte of allocated space. Return either SQLITE_OK or
- ** an error code (usually SQLITE_CORRUPT).
- **
- ** The caller guarantees that there is sufficient space to make the
- ** allocation. This routine might need to defragment in order to bring
- ** all the space together, however. This routine will avoid using
- ** the first two bytes past the cell pointer area since presumably this
- ** allocation is being made in order to insert a new cell, so we will
- ** also end up needing a new cell pointer.
- */
- static int allocateSpace( MemPage pPage, int nByte, ref int pIdx )
- {
- int hdr = pPage.hdrOffset; /* Local cache of pPage.hdrOffset */
- u8[] data = pPage.aData; /* Local cache of pPage.aData */
- int nFrag; /* Number of fragmented bytes on pPage */
- int top; /* First byte of cell content area */
- int gap; /* First byte of gap between cell pointers and cell content */
- int rc; /* Integer return code */
- u32 usableSize; /* Usable size of the page */
- Debug.Assert( sqlite3PagerIswriteable( pPage.pDbPage ) );
- Debug.Assert( pPage.pBt != null );
- Debug.Assert( sqlite3_mutex_held( pPage.pBt.mutex ) );
- Debug.Assert( nByte >= 0 ); /* Minimum cell size is 4 */
- Debug.Assert( pPage.nFree >= nByte );
- Debug.Assert( pPage.nOverflow == 0 );
- usableSize = pPage.pBt.usableSize;
- Debug.Assert( nByte < usableSize - 8 );
- nFrag = data[hdr + 7];
- Debug.Assert( pPage.cellOffset == hdr + 12 - 4 * pPage.leaf );
- gap = pPage.cellOffset + 2 * pPage.nCell;
- top = get2byteNotZero( data, hdr + 5 );
- if ( gap > top )
- return SQLITE_CORRUPT_BKPT();
- testcase( gap + 2 == top );
- testcase( gap + 1 == top );
- testcase( gap == top );
- if ( nFrag >= 60 )
- {
- /* Always defragment highly fragmented pages */
- rc = defragmentPage( pPage );
- if ( rc != 0 )
- return rc;
- top = get2byteNotZero( data, hdr + 5 );
- }
- else if ( gap + 2 <= top )
- {
- /* Search the freelist looking for a free slot big enough to satisfy
- ** the request. The allocation is made from the first free slot in
- ** the list that is large enough to accomadate it.
- */
- int pc, addr;
- for ( addr = hdr + 1; ( pc = get2byte( data, addr ) ) > 0; addr = pc )
- {
- int size; /* Size of free slot */
- if ( pc > usableSize - 4 || pc < addr + 4 )
- {
- return SQLITE_CORRUPT_BKPT();
- }
- size = get2byte( data, pc + 2 );
- if ( size >= nByte )
- {
- int x = size - nByte;
- testcase( x == 4 );
- testcase( x == 3 );
- if ( x < 4 )
- {
- /* Remove the slot from the free-list. Update the number of
- ** fragmented bytes within the page. */
- data[addr + 0] = data[pc + 0];
- data[addr + 1] = data[pc + 1]; //memcpy( data[addr], ref data[pc], 2 );
- data[hdr + 7] = (u8)( nFrag + x );
- }
- else if ( size + pc > usableSize )
- {
- return SQLITE_CORRUPT_BKPT();
- }
- else
- {
- /* The slot remains on the free-list. Reduce its size to account
- ** for the portion used by the new allocation. */
- put2byte( data, pc + 2, x );
- }
- pIdx = pc + x;
- return SQLITE_OK;
- }
- }
- }
- /* Check to make sure there is enough space in the gap to satisfy
- ** the allocation. If not, defragment.
- */
- testcase( gap + 2 + nByte == top );
- if ( gap + 2 + nByte > top )
- {
- rc = defragmentPage( pPage );
- if ( rc != 0 )
- return rc;
- top = get2byteNotZero( data, hdr + 5 );
- Debug.Asser…
Large files files are truncated, but you can click here to view the full file