diff options
Diffstat (limited to 'ext/sqlite/libsqlite/src/btree_rb.c')
-rw-r--r-- | ext/sqlite/libsqlite/src/btree_rb.c | 1488 |
1 files changed, 0 insertions, 1488 deletions
diff --git a/ext/sqlite/libsqlite/src/btree_rb.c b/ext/sqlite/libsqlite/src/btree_rb.c deleted file mode 100644 index 4d08e00cb..000000000 --- a/ext/sqlite/libsqlite/src/btree_rb.c +++ /dev/null @@ -1,1488 +0,0 @@ -/* -** 2003 Feb 4 -** -** 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. -** -************************************************************************* -** $Id: btree_rb.c 195361 2005-09-07 15:11:33Z iliaa $ -** -** This file implements an in-core database using Red-Black balanced -** binary trees. -** -** It was contributed to SQLite by anonymous on 2003-Feb-04 23:24:49 UTC. -*/ -#include "btree.h" -#include "sqliteInt.h" -#include <assert.h> - -/* -** Omit this whole file if the SQLITE_OMIT_INMEMORYDB macro is -** defined. This allows a lot of code to be omitted for installations -** that do not need it. -*/ -#ifndef SQLITE_OMIT_INMEMORYDB - - -typedef struct BtRbTree BtRbTree; -typedef struct BtRbNode BtRbNode; -typedef struct BtRollbackOp BtRollbackOp; -typedef struct Rbtree Rbtree; -typedef struct RbtCursor RbtCursor; - -/* Forward declarations */ -static BtOps sqliteRbtreeOps; -static BtCursorOps sqliteRbtreeCursorOps; - -/* - * During each transaction (or checkpoint), a linked-list of - * "rollback-operations" is accumulated. If the transaction is rolled back, - * then the list of operations must be executed (to restore the database to - * it's state before the transaction started). If the transaction is to be - * committed, just delete the list. - * - * Each operation is represented as follows, depending on the value of eOp: - * - * ROLLBACK_INSERT -> Need to insert (pKey, pData) into table iTab. - * ROLLBACK_DELETE -> Need to delete the record (pKey) into table iTab. - * ROLLBACK_CREATE -> Need to create table iTab. - * ROLLBACK_DROP -> Need to drop table iTab. - */ -struct BtRollbackOp { - u8 eOp; - int iTab; - int nKey; - void *pKey; - int nData; - void *pData; - BtRollbackOp *pNext; -}; - -/* -** Legal values for BtRollbackOp.eOp: -*/ -#define ROLLBACK_INSERT 1 /* Insert a record */ -#define ROLLBACK_DELETE 2 /* Delete a record */ -#define ROLLBACK_CREATE 3 /* Create a table */ -#define ROLLBACK_DROP 4 /* Drop a table */ - -struct Rbtree { - BtOps *pOps; /* Function table */ - int aMetaData[SQLITE_N_BTREE_META]; - - int next_idx; /* next available table index */ - Hash tblHash; /* All created tables, by index */ - u8 isAnonymous; /* True if this Rbtree is to be deleted when closed */ - u8 eTransState; /* State of this Rbtree wrt transactions */ - - BtRollbackOp *pTransRollback; - BtRollbackOp *pCheckRollback; - BtRollbackOp *pCheckRollbackTail; -}; - -/* -** Legal values for Rbtree.eTransState. -*/ -#define TRANS_NONE 0 /* No transaction is in progress */ -#define TRANS_INTRANSACTION 1 /* A transaction is in progress */ -#define TRANS_INCHECKPOINT 2 /* A checkpoint is in progress */ -#define TRANS_ROLLBACK 3 /* We are currently rolling back a checkpoint or - * transaction. */ - -struct RbtCursor { - BtCursorOps *pOps; /* Function table */ - Rbtree *pRbtree; - BtRbTree *pTree; - int iTree; /* Index of pTree in pRbtree */ - BtRbNode *pNode; - RbtCursor *pShared; /* List of all cursors on the same Rbtree */ - u8 eSkip; /* Determines if next step operation is a no-op */ - u8 wrFlag; /* True if this cursor is open for writing */ -}; - -/* -** Legal values for RbtCursor.eSkip. -*/ -#define SKIP_NONE 0 /* Always step the cursor */ -#define SKIP_NEXT 1 /* The next sqliteRbtreeNext() is a no-op */ -#define SKIP_PREV 2 /* The next sqliteRbtreePrevious() is a no-op */ -#define SKIP_INVALID 3 /* Calls to Next() and Previous() are invalid */ - -struct BtRbTree { - RbtCursor *pCursors; /* All cursors pointing to this tree */ - BtRbNode *pHead; /* Head of the tree, or NULL */ -}; - -struct BtRbNode { - int nKey; - void *pKey; - int nData; - void *pData; - u8 isBlack; /* true for a black node, 0 for a red node */ - BtRbNode *pParent; /* Nodes parent node, NULL for the tree head */ - BtRbNode *pLeft; /* Nodes left child, or NULL */ - BtRbNode *pRight; /* Nodes right child, or NULL */ - - int nBlackHeight; /* Only used during the red-black integrity check */ -}; - -/* Forward declarations */ -static int memRbtreeMoveto( - RbtCursor* pCur, - const void *pKey, - int nKey, - int *pRes -); -static int memRbtreeClearTable(Rbtree* tree, int n); -static int memRbtreeNext(RbtCursor* pCur, int *pRes); -static int memRbtreeLast(RbtCursor* pCur, int *pRes); -static int memRbtreePrevious(RbtCursor* pCur, int *pRes); - - -/* -** This routine checks all cursors that point to the same table -** as pCur points to. If any of those cursors were opened with -** wrFlag==0 then this routine returns SQLITE_LOCKED. If all -** cursors point to the same table were opened with wrFlag==1 -** then this routine returns SQLITE_OK. -** -** In addition to checking for read-locks (where a read-lock -** means a cursor opened with wrFlag==0) this routine also NULLs -** out the pNode field of all other cursors. -** This is necessary because an insert -** or delete might change erase the node out from under -** another cursor. -*/ -static int checkReadLocks(RbtCursor *pCur){ - RbtCursor *p; - assert( pCur->wrFlag ); - for(p=pCur->pTree->pCursors; p; p=p->pShared){ - if( p!=pCur ){ - if( p->wrFlag==0 ) return SQLITE_LOCKED; - p->pNode = 0; - } - } - return SQLITE_OK; -} - -/* - * The key-compare function for the red-black trees. Returns as follows: - * - * (key1 < key2) -1 - * (key1 == key2) 0 - * (key1 > key2) 1 - * - * Keys are compared using memcmp(). If one key is an exact prefix of the - * other, then the shorter key is less than the longer key. - */ -static int key_compare(void const*pKey1, int nKey1, void const*pKey2, int nKey2) -{ - int mcmp = memcmp(pKey1, pKey2, (nKey1 <= nKey2)?nKey1:nKey2); - if( mcmp == 0){ - if( nKey1 == nKey2 ) return 0; - return ((nKey1 < nKey2)?-1:1); - } - return ((mcmp>0)?1:-1); -} - -/* - * Perform the LEFT-rotate transformation on node X of tree pTree. This - * transform is part of the red-black balancing code. - * - * | | - * X Y - * / \ / \ - * a Y X c - * / \ / \ - * b c a b - * - * BEFORE AFTER - */ -static void leftRotate(BtRbTree *pTree, BtRbNode *pX) -{ - BtRbNode *pY; - BtRbNode *pb; - pY = pX->pRight; - pb = pY->pLeft; - - pY->pParent = pX->pParent; - if( pX->pParent ){ - if( pX->pParent->pLeft == pX ) pX->pParent->pLeft = pY; - else pX->pParent->pRight = pY; - } - pY->pLeft = pX; - pX->pParent = pY; - pX->pRight = pb; - if( pb ) pb->pParent = pX; - if( pTree->pHead == pX ) pTree->pHead = pY; -} - -/* - * Perform the RIGHT-rotate transformation on node X of tree pTree. This - * transform is part of the red-black balancing code. - * - * | | - * X Y - * / \ / \ - * Y c a X - * / \ / \ - * a b b c - * - * BEFORE AFTER - */ -static void rightRotate(BtRbTree *pTree, BtRbNode *pX) -{ - BtRbNode *pY; - BtRbNode *pb; - pY = pX->pLeft; - pb = pY->pRight; - - pY->pParent = pX->pParent; - if( pX->pParent ){ - if( pX->pParent->pLeft == pX ) pX->pParent->pLeft = pY; - else pX->pParent->pRight = pY; - } - pY->pRight = pX; - pX->pParent = pY; - pX->pLeft = pb; - if( pb ) pb->pParent = pX; - if( pTree->pHead == pX ) pTree->pHead = pY; -} - -/* - * A string-manipulation helper function for check_redblack_tree(). If (orig == - * NULL) a copy of val is returned. If (orig != NULL) then a copy of the * - * concatenation of orig and val is returned. The original orig is deleted - * (using sqliteFree()). - */ -static char *append_val(char * orig, char const * val){ - char *z; - if( !orig ){ - z = sqliteStrDup( val ); - } else{ - z = 0; - sqliteSetString(&z, orig, val, (char*)0); - sqliteFree( orig ); - } - return z; -} - -/* - * Append a string representation of the entire node to orig and return it. - * This is used to produce debugging information if check_redblack_tree() finds - * a problem with a red-black binary tree. - */ -static char *append_node(char * orig, BtRbNode *pNode, int indent) -{ - char buf[128]; - int i; - - for( i=0; i<indent; i++ ){ - orig = append_val(orig, " "); - } - - sprintf(buf, "%p", pNode); - orig = append_val(orig, buf); - - if( pNode ){ - indent += 3; - if( pNode->isBlack ){ - orig = append_val(orig, " B \n"); - }else{ - orig = append_val(orig, " R \n"); - } - orig = append_node( orig, pNode->pLeft, indent ); - orig = append_node( orig, pNode->pRight, indent ); - }else{ - orig = append_val(orig, "\n"); - } - return orig; -} - -/* - * Print a representation of a node to stdout. This function is only included - * so you can call it from within a debugger if things get really bad. It - * is not called from anyplace in the code. - */ -static void print_node(BtRbNode *pNode) -{ - char * str = append_node(0, pNode, 0); - printf("%s", str); - - /* Suppress a warning message about print_node() being unused */ - (void)print_node; -} - -/* - * Check the following properties of the red-black tree: - * (1) - If a node is red, both of it's children are black - * (2) - Each path from a given node to a leaf (NULL) node passes thru the - * same number of black nodes - * - * If there is a problem, append a description (using append_val() ) to *msg. - */ -static void check_redblack_tree(BtRbTree * tree, char ** msg) -{ - BtRbNode *pNode; - - /* 0 -> came from parent - * 1 -> came from left - * 2 -> came from right */ - int prev_step = 0; - - pNode = tree->pHead; - while( pNode ){ - switch( prev_step ){ - case 0: - if( pNode->pLeft ){ - pNode = pNode->pLeft; - }else{ - prev_step = 1; - } - break; - case 1: - if( pNode->pRight ){ - pNode = pNode->pRight; - prev_step = 0; - }else{ - prev_step = 2; - } - break; - case 2: - /* Check red-black property (1) */ - if( !pNode->isBlack && - ( (pNode->pLeft && !pNode->pLeft->isBlack) || - (pNode->pRight && !pNode->pRight->isBlack) ) - ){ - char buf[128]; - sprintf(buf, "Red node with red child at %p\n", pNode); - *msg = append_val(*msg, buf); - *msg = append_node(*msg, tree->pHead, 0); - *msg = append_val(*msg, "\n"); - } - - /* Check red-black property (2) */ - { - int leftHeight = 0; - int rightHeight = 0; - if( pNode->pLeft ){ - leftHeight += pNode->pLeft->nBlackHeight; - leftHeight += (pNode->pLeft->isBlack?1:0); - } - if( pNode->pRight ){ - rightHeight += pNode->pRight->nBlackHeight; - rightHeight += (pNode->pRight->isBlack?1:0); - } - if( leftHeight != rightHeight ){ - char buf[128]; - sprintf(buf, "Different black-heights at %p\n", pNode); - *msg = append_val(*msg, buf); - *msg = append_node(*msg, tree->pHead, 0); - *msg = append_val(*msg, "\n"); - } - pNode->nBlackHeight = leftHeight; - } - - if( pNode->pParent ){ - if( pNode == pNode->pParent->pLeft ) prev_step = 1; - else prev_step = 2; - } - pNode = pNode->pParent; - break; - default: assert(0); - } - } -} - -/* - * Node pX has just been inserted into pTree (by code in sqliteRbtreeInsert()). - * It is possible that pX is a red node with a red parent, which is a violation - * of the red-black tree properties. This function performs rotations and - * color changes to rebalance the tree - */ -static void do_insert_balancing(BtRbTree *pTree, BtRbNode *pX) -{ - /* In the first iteration of this loop, pX points to the red node just - * inserted in the tree. If the parent of pX exists (pX is not the root - * node) and is red, then the properties of the red-black tree are - * violated. - * - * At the start of any subsequent iterations, pX points to a red node - * with a red parent. In all other respects the tree is a legal red-black - * binary tree. */ - while( pX != pTree->pHead && !pX->pParent->isBlack ){ - BtRbNode *pUncle; - BtRbNode *pGrandparent; - - /* Grandparent of pX must exist and must be black. */ - pGrandparent = pX->pParent->pParent; - assert( pGrandparent ); - assert( pGrandparent->isBlack ); - - /* Uncle of pX may or may not exist. */ - if( pX->pParent == pGrandparent->pLeft ) - pUncle = pGrandparent->pRight; - else - pUncle = pGrandparent->pLeft; - - /* If the uncle of pX exists and is red, we do the following: - * | | - * G(b) G(r) - * / \ / \ - * U(r) P(r) U(b) P(b) - * \ \ - * X(r) X(r) - * - * BEFORE AFTER - * pX is then set to G. If the parent of G is red, then the while loop - * will run again. */ - if( pUncle && !pUncle->isBlack ){ - pGrandparent->isBlack = 0; - pUncle->isBlack = 1; - pX->pParent->isBlack = 1; - pX = pGrandparent; - }else{ - - if( pX->pParent == pGrandparent->pLeft ){ - if( pX == pX->pParent->pRight ){ - /* If pX is a right-child, do the following transform, essentially - * to change pX into a left-child: - * | | - * G(b) G(b) - * / \ / \ - * P(r) U(b) X(r) U(b) - * \ / - * X(r) P(r) <-- new X - * - * BEFORE AFTER - */ - pX = pX->pParent; - leftRotate(pTree, pX); - } - - /* Do the following transform, which balances the tree :) - * | | - * G(b) P(b) - * / \ / \ - * P(r) U(b) X(r) G(r) - * / \ - * X(r) U(b) - * - * BEFORE AFTER - */ - assert( pGrandparent == pX->pParent->pParent ); - pGrandparent->isBlack = 0; - pX->pParent->isBlack = 1; - rightRotate( pTree, pGrandparent ); - - }else{ - /* This code is symetric to the illustrated case above. */ - if( pX == pX->pParent->pLeft ){ - pX = pX->pParent; - rightRotate(pTree, pX); - } - assert( pGrandparent == pX->pParent->pParent ); - pGrandparent->isBlack = 0; - pX->pParent->isBlack = 1; - leftRotate( pTree, pGrandparent ); - } - } - } - pTree->pHead->isBlack = 1; -} - -/* - * A child of pParent, which in turn had child pX, has just been removed from - * pTree (the figure below depicts the operation, Z is being removed). pParent - * or pX, or both may be NULL. - * | | - * P P - * / \ / \ - * Z X - * / \ - * X nil - * - * This function is only called if Z was black. In this case the red-black tree - * properties have been violated, and pX has an "extra black". This function - * performs rotations and color-changes to re-balance the tree. - */ -static -void do_delete_balancing(BtRbTree *pTree, BtRbNode *pX, BtRbNode *pParent) -{ - BtRbNode *pSib; - - /* TODO: Comment this code! */ - while( pX != pTree->pHead && (!pX || pX->isBlack) ){ - if( pX == pParent->pLeft ){ - pSib = pParent->pRight; - if( pSib && !(pSib->isBlack) ){ - pSib->isBlack = 1; - pParent->isBlack = 0; - leftRotate(pTree, pParent); - pSib = pParent->pRight; - } - if( !pSib ){ - pX = pParent; - }else if( - (!pSib->pLeft || pSib->pLeft->isBlack) && - (!pSib->pRight || pSib->pRight->isBlack) ) { - pSib->isBlack = 0; - pX = pParent; - }else{ - if( (!pSib->pRight || pSib->pRight->isBlack) ){ - if( pSib->pLeft ) pSib->pLeft->isBlack = 1; - pSib->isBlack = 0; - rightRotate( pTree, pSib ); - pSib = pParent->pRight; - } - pSib->isBlack = pParent->isBlack; - pParent->isBlack = 1; - if( pSib->pRight ) pSib->pRight->isBlack = 1; - leftRotate(pTree, pParent); - pX = pTree->pHead; - } - }else{ - pSib = pParent->pLeft; - if( pSib && !(pSib->isBlack) ){ - pSib->isBlack = 1; - pParent->isBlack = 0; - rightRotate(pTree, pParent); - pSib = pParent->pLeft; - } - if( !pSib ){ - pX = pParent; - }else if( - (!pSib->pLeft || pSib->pLeft->isBlack) && - (!pSib->pRight || pSib->pRight->isBlack) ){ - pSib->isBlack = 0; - pX = pParent; - }else{ - if( (!pSib->pLeft || pSib->pLeft->isBlack) ){ - if( pSib->pRight ) pSib->pRight->isBlack = 1; - pSib->isBlack = 0; - leftRotate( pTree, pSib ); - pSib = pParent->pLeft; - } - pSib->isBlack = pParent->isBlack; - pParent->isBlack = 1; - if( pSib->pLeft ) pSib->pLeft->isBlack = 1; - rightRotate(pTree, pParent); - pX = pTree->pHead; - } - } - pParent = pX->pParent; - } - if( pX ) pX->isBlack = 1; -} - -/* - * Create table n in tree pRbtree. Table n must not exist. - */ -static void btreeCreateTable(Rbtree* pRbtree, int n) -{ - BtRbTree *pNewTbl = sqliteMalloc(sizeof(BtRbTree)); - sqliteHashInsert(&pRbtree->tblHash, 0, n, pNewTbl); -} - -/* - * Log a single "rollback-op" for the given Rbtree. See comments for struct - * BtRollbackOp. - */ -static void btreeLogRollbackOp(Rbtree* pRbtree, BtRollbackOp *pRollbackOp) -{ - assert( pRbtree->eTransState == TRANS_INCHECKPOINT || - pRbtree->eTransState == TRANS_INTRANSACTION ); - if( pRbtree->eTransState == TRANS_INTRANSACTION ){ - pRollbackOp->pNext = pRbtree->pTransRollback; - pRbtree->pTransRollback = pRollbackOp; - } - if( pRbtree->eTransState == TRANS_INCHECKPOINT ){ - if( !pRbtree->pCheckRollback ){ - pRbtree->pCheckRollbackTail = pRollbackOp; - } - pRollbackOp->pNext = pRbtree->pCheckRollback; - pRbtree->pCheckRollback = pRollbackOp; - } -} - -int sqliteRbtreeOpen( - const char *zFilename, - int mode, - int nPg, - Btree **ppBtree -){ - Rbtree **ppRbtree = (Rbtree**)ppBtree; - *ppRbtree = (Rbtree *)sqliteMalloc(sizeof(Rbtree)); - if( sqlite_malloc_failed ) goto open_no_mem; - sqliteHashInit(&(*ppRbtree)->tblHash, SQLITE_HASH_INT, 0); - - /* Create a binary tree for the SQLITE_MASTER table at location 2 */ - btreeCreateTable(*ppRbtree, 2); - if( sqlite_malloc_failed ) goto open_no_mem; - (*ppRbtree)->next_idx = 3; - (*ppRbtree)->pOps = &sqliteRbtreeOps; - /* Set file type to 4; this is so that "attach ':memory:' as ...." does not - ** think that the database in uninitialised and refuse to attach - */ - (*ppRbtree)->aMetaData[2] = 4; - - return SQLITE_OK; - -open_no_mem: - *ppBtree = 0; - return SQLITE_NOMEM; -} - -/* - * Create a new table in the supplied Rbtree. Set *n to the new table number. - * Return SQLITE_OK if the operation is a success. - */ -static int memRbtreeCreateTable(Rbtree* tree, int* n) -{ - assert( tree->eTransState != TRANS_NONE ); - - *n = tree->next_idx++; - btreeCreateTable(tree, *n); - if( sqlite_malloc_failed ) return SQLITE_NOMEM; - - /* Set up the rollback structure (if we are not doing this as part of a - * rollback) */ - if( tree->eTransState != TRANS_ROLLBACK ){ - BtRollbackOp *pRollbackOp = sqliteMalloc(sizeof(BtRollbackOp)); - if( pRollbackOp==0 ) return SQLITE_NOMEM; - pRollbackOp->eOp = ROLLBACK_DROP; - pRollbackOp->iTab = *n; - btreeLogRollbackOp(tree, pRollbackOp); - } - - return SQLITE_OK; -} - -/* - * Delete table n from the supplied Rbtree. - */ -static int memRbtreeDropTable(Rbtree* tree, int n) -{ - BtRbTree *pTree; - assert( tree->eTransState != TRANS_NONE ); - - memRbtreeClearTable(tree, n); - pTree = sqliteHashInsert(&tree->tblHash, 0, n, 0); - assert(pTree); - assert( pTree->pCursors==0 ); - sqliteFree(pTree); - - if( tree->eTransState != TRANS_ROLLBACK ){ - BtRollbackOp *pRollbackOp = sqliteMalloc(sizeof(BtRollbackOp)); - if( pRollbackOp==0 ) return SQLITE_NOMEM; - pRollbackOp->eOp = ROLLBACK_CREATE; - pRollbackOp->iTab = n; - btreeLogRollbackOp(tree, pRollbackOp); - } - - return SQLITE_OK; -} - -static int memRbtreeKeyCompare(RbtCursor* pCur, const void *pKey, int nKey, - int nIgnore, int *pRes) -{ - assert(pCur); - - if( !pCur->pNode ) { - *pRes = -1; - } else { - if( (pCur->pNode->nKey - nIgnore) < 0 ){ - *pRes = -1; - }else{ - *pRes = key_compare(pCur->pNode->pKey, pCur->pNode->nKey-nIgnore, - pKey, nKey); - } - } - return SQLITE_OK; -} - -/* - * Get a new cursor for table iTable of the supplied Rbtree. The wrFlag - * parameter indicates that the cursor is open for writing. - * - * Note that RbtCursor.eSkip and RbtCursor.pNode both initialize to 0. - */ -static int memRbtreeCursor( - Rbtree* tree, - int iTable, - int wrFlag, - RbtCursor **ppCur -){ - RbtCursor *pCur; - assert(tree); - pCur = *ppCur = sqliteMalloc(sizeof(RbtCursor)); - if( sqlite_malloc_failed ) return SQLITE_NOMEM; - pCur->pTree = sqliteHashFind(&tree->tblHash, 0, iTable); - assert( pCur->pTree ); - pCur->pRbtree = tree; - pCur->iTree = iTable; - pCur->pOps = &sqliteRbtreeCursorOps; - pCur->wrFlag = wrFlag; - pCur->pShared = pCur->pTree->pCursors; - pCur->pTree->pCursors = pCur; - - assert( (*ppCur)->pTree ); - return SQLITE_OK; -} - -/* - * Insert a new record into the Rbtree. The key is given by (pKey,nKey) - * and the data is given by (pData,nData). The cursor is used only to - * define what database the record should be inserted into. The cursor - * is left pointing at the new record. - * - * If the key exists already in the tree, just replace the data. - */ -static int memRbtreeInsert( - RbtCursor* pCur, - const void *pKey, - int nKey, - const void *pDataInput, - int nData -){ - void * pData; - int match; - - /* It is illegal to call sqliteRbtreeInsert() if we are - ** not in a transaction */ - assert( pCur->pRbtree->eTransState != TRANS_NONE ); - - /* Make sure some other cursor isn't trying to read this same table */ - if( checkReadLocks(pCur) ){ - return SQLITE_LOCKED; /* The table pCur points to has a read lock */ - } - - /* Take a copy of the input data now, in case we need it for the - * replace case */ - pData = sqliteMallocRaw(nData); - if( sqlite_malloc_failed ) return SQLITE_NOMEM; - memcpy(pData, pDataInput, nData); - - /* Move the cursor to a node near the key to be inserted. If the key already - * exists in the table, then (match == 0). In this case we can just replace - * the data associated with the entry, we don't need to manipulate the tree. - * - * If there is no exact match, then the cursor points at what would be either - * the predecessor (match == -1) or successor (match == 1) of the - * searched-for key, were it to be inserted. The new node becomes a child of - * this node. - * - * The new node is initially red. - */ - memRbtreeMoveto( pCur, pKey, nKey, &match); - if( match ){ - BtRbNode *pNode = sqliteMalloc(sizeof(BtRbNode)); - if( pNode==0 ) return SQLITE_NOMEM; - pNode->nKey = nKey; - pNode->pKey = sqliteMallocRaw(nKey); - if( sqlite_malloc_failed ) return SQLITE_NOMEM; - memcpy(pNode->pKey, pKey, nKey); - pNode->nData = nData; - pNode->pData = pData; - if( pCur->pNode ){ - switch( match ){ - case -1: - assert( !pCur->pNode->pRight ); - pNode->pParent = pCur->pNode; - pCur->pNode->pRight = pNode; - break; - case 1: - assert( !pCur->pNode->pLeft ); - pNode->pParent = pCur->pNode; - pCur->pNode->pLeft = pNode; - break; - default: - assert(0); - } - }else{ - pCur->pTree->pHead = pNode; - } - - /* Point the cursor at the node just inserted, as per SQLite requirements */ - pCur->pNode = pNode; - - /* A new node has just been inserted, so run the balancing code */ - do_insert_balancing(pCur->pTree, pNode); - - /* Set up a rollback-op in case we have to roll this operation back */ - if( pCur->pRbtree->eTransState != TRANS_ROLLBACK ){ - BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) ); - if( pOp==0 ) return SQLITE_NOMEM; - pOp->eOp = ROLLBACK_DELETE; - pOp->iTab = pCur->iTree; - pOp->nKey = pNode->nKey; - pOp->pKey = sqliteMallocRaw( pOp->nKey ); - if( sqlite_malloc_failed ) return SQLITE_NOMEM; - memcpy( pOp->pKey, pNode->pKey, pOp->nKey ); - btreeLogRollbackOp(pCur->pRbtree, pOp); - } - - }else{ - /* No need to insert a new node in the tree, as the key already exists. - * Just clobber the current nodes data. */ - - /* Set up a rollback-op in case we have to roll this operation back */ - if( pCur->pRbtree->eTransState != TRANS_ROLLBACK ){ - BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) ); - if( pOp==0 ) return SQLITE_NOMEM; - pOp->iTab = pCur->iTree; - pOp->nKey = pCur->pNode->nKey; - pOp->pKey = sqliteMallocRaw( pOp->nKey ); - if( sqlite_malloc_failed ) return SQLITE_NOMEM; - memcpy( pOp->pKey, pCur->pNode->pKey, pOp->nKey ); - pOp->nData = pCur->pNode->nData; - pOp->pData = pCur->pNode->pData; - pOp->eOp = ROLLBACK_INSERT; - btreeLogRollbackOp(pCur->pRbtree, pOp); - }else{ - sqliteFree( pCur->pNode->pData ); - } - - /* Actually clobber the nodes data */ - pCur->pNode->pData = pData; - pCur->pNode->nData = nData; - } - - return SQLITE_OK; -} - -/* Move the cursor so that it points to an entry near pKey. -** Return a success code. -** -** *pRes<0 The cursor is left pointing at an entry that -** is smaller than pKey or if the table is empty -** and the cursor is therefore left point to nothing. -** -** *pRes==0 The cursor is left pointing at an entry that -** exactly matches pKey. -** -** *pRes>0 The cursor is left pointing at an entry that -** is larger than pKey. -*/ -static int memRbtreeMoveto( - RbtCursor* pCur, - const void *pKey, - int nKey, - int *pRes -){ - BtRbNode *pTmp = 0; - - pCur->pNode = pCur->pTree->pHead; - *pRes = -1; - while( pCur->pNode && *pRes ) { - *pRes = key_compare(pCur->pNode->pKey, pCur->pNode->nKey, pKey, nKey); - pTmp = pCur->pNode; - switch( *pRes ){ - case 1: /* cursor > key */ - pCur->pNode = pCur->pNode->pLeft; - break; - case -1: /* cursor < key */ - pCur->pNode = pCur->pNode->pRight; - break; - } - } - - /* If (pCur->pNode == NULL), then we have failed to find a match. Set - * pCur->pNode to pTmp, which is either NULL (if the tree is empty) or the - * last node traversed in the search. In either case the relation ship - * between pTmp and the searched for key is already stored in *pRes. pTmp is - * either the successor or predecessor of the key we tried to move to. */ - if( !pCur->pNode ) pCur->pNode = pTmp; - pCur->eSkip = SKIP_NONE; - - return SQLITE_OK; -} - - -/* -** Delete the entry that the cursor is pointing to. -** -** The cursor is left pointing at either the next or the previous -** entry. If the cursor is left pointing to the next entry, then -** the pCur->eSkip flag is set to SKIP_NEXT which forces the next call to -** sqliteRbtreeNext() to be a no-op. That way, you can always call -** sqliteRbtreeNext() after a delete and the cursor will be left -** pointing to the first entry after the deleted entry. Similarly, -** pCur->eSkip is set to SKIP_PREV is the cursor is left pointing to -** the entry prior to the deleted entry so that a subsequent call to -** sqliteRbtreePrevious() will always leave the cursor pointing at the -** entry immediately before the one that was deleted. -*/ -static int memRbtreeDelete(RbtCursor* pCur) -{ - BtRbNode *pZ; /* The one being deleted */ - BtRbNode *pChild; /* The child of the spliced out node */ - - /* It is illegal to call sqliteRbtreeDelete() if we are - ** not in a transaction */ - assert( pCur->pRbtree->eTransState != TRANS_NONE ); - - /* Make sure some other cursor isn't trying to read this same table */ - if( checkReadLocks(pCur) ){ - return SQLITE_LOCKED; /* The table pCur points to has a read lock */ - } - - pZ = pCur->pNode; - if( !pZ ){ - return SQLITE_OK; - } - - /* If we are not currently doing a rollback, set up a rollback op for this - * deletion */ - if( pCur->pRbtree->eTransState != TRANS_ROLLBACK ){ - BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) ); - if( pOp==0 ) return SQLITE_NOMEM; - pOp->iTab = pCur->iTree; - pOp->nKey = pZ->nKey; - pOp->pKey = pZ->pKey; - pOp->nData = pZ->nData; - pOp->pData = pZ->pData; - pOp->eOp = ROLLBACK_INSERT; - btreeLogRollbackOp(pCur->pRbtree, pOp); - } - - /* First do a standard binary-tree delete (node pZ is to be deleted). How - * to do this depends on how many children pZ has: - * - * If pZ has no children or one child, then splice out pZ. If pZ has two - * children, splice out the successor of pZ and replace the key and data of - * pZ with the key and data of the spliced out successor. */ - if( pZ->pLeft && pZ->pRight ){ - BtRbNode *pTmp; - int dummy; - pCur->eSkip = SKIP_NONE; - memRbtreeNext(pCur, &dummy); - assert( dummy == 0 ); - if( pCur->pRbtree->eTransState == TRANS_ROLLBACK ){ - sqliteFree(pZ->pKey); - sqliteFree(pZ->pData); - } - pZ->pData = pCur->pNode->pData; - pZ->nData = pCur->pNode->nData; - pZ->pKey = pCur->pNode->pKey; - pZ->nKey = pCur->pNode->nKey; - pTmp = pZ; - pZ = pCur->pNode; - pCur->pNode = pTmp; - pCur->eSkip = SKIP_NEXT; - }else{ - int res; - pCur->eSkip = SKIP_NONE; - memRbtreeNext(pCur, &res); - pCur->eSkip = SKIP_NEXT; - if( res ){ - memRbtreeLast(pCur, &res); - memRbtreePrevious(pCur, &res); - pCur->eSkip = SKIP_PREV; - } - if( pCur->pRbtree->eTransState == TRANS_ROLLBACK ){ - sqliteFree(pZ->pKey); - sqliteFree(pZ->pData); - } - } - - /* pZ now points at the node to be spliced out. This block does the - * splicing. */ - { - BtRbNode **ppParentSlot = 0; - assert( !pZ->pLeft || !pZ->pRight ); /* pZ has at most one child */ - pChild = ((pZ->pLeft)?pZ->pLeft:pZ->pRight); - if( pZ->pParent ){ - assert( pZ == pZ->pParent->pLeft || pZ == pZ->pParent->pRight ); - ppParentSlot = ((pZ == pZ->pParent->pLeft) - ?&pZ->pParent->pLeft:&pZ->pParent->pRight); - *ppParentSlot = pChild; - }else{ - pCur->pTree->pHead = pChild; - } - if( pChild ) pChild->pParent = pZ->pParent; - } - - /* pZ now points at the spliced out node. pChild is the only child of pZ, or - * NULL if pZ has no children. If pZ is black, and not the tree root, then we - * will have violated the "same number of black nodes in every path to a - * leaf" property of the red-black tree. The code in do_delete_balancing() - * repairs this. */ - if( pZ->isBlack ){ - do_delete_balancing(pCur->pTree, pChild, pZ->pParent); - } - - sqliteFree(pZ); - return SQLITE_OK; -} - -/* - * Empty table n of the Rbtree. - */ -static int memRbtreeClearTable(Rbtree* tree, int n) -{ - BtRbTree *pTree; - BtRbNode *pNode; - - pTree = sqliteHashFind(&tree->tblHash, 0, n); - assert(pTree); - - pNode = pTree->pHead; - while( pNode ){ - if( pNode->pLeft ){ - pNode = pNode->pLeft; - } - else if( pNode->pRight ){ - pNode = pNode->pRight; - } - else { - BtRbNode *pTmp = pNode->pParent; - if( tree->eTransState == TRANS_ROLLBACK ){ - sqliteFree( pNode->pKey ); - sqliteFree( pNode->pData ); - }else{ - BtRollbackOp *pRollbackOp = sqliteMallocRaw(sizeof(BtRollbackOp)); - if( pRollbackOp==0 ) return SQLITE_NOMEM; - pRollbackOp->eOp = ROLLBACK_INSERT; - pRollbackOp->iTab = n; - pRollbackOp->nKey = pNode->nKey; - pRollbackOp->pKey = pNode->pKey; - pRollbackOp->nData = pNode->nData; - pRollbackOp->pData = pNode->pData; - btreeLogRollbackOp(tree, pRollbackOp); - } - sqliteFree( pNode ); - if( pTmp ){ - if( pTmp->pLeft == pNode ) pTmp->pLeft = 0; - else if( pTmp->pRight == pNode ) pTmp->pRight = 0; - } - pNode = pTmp; - } - } - - pTree->pHead = 0; - return SQLITE_OK; -} - -static int memRbtreeFirst(RbtCursor* pCur, int *pRes) -{ - if( pCur->pTree->pHead ){ - pCur->pNode = pCur->pTree->pHead; - while( pCur->pNode->pLeft ){ - pCur->pNode = pCur->pNode->pLeft; - } - } - if( pCur->pNode ){ - *pRes = 0; - }else{ - *pRes = 1; - } - pCur->eSkip = SKIP_NONE; - return SQLITE_OK; -} - -static int memRbtreeLast(RbtCursor* pCur, int *pRes) -{ - if( pCur->pTree->pHead ){ - pCur->pNode = pCur->pTree->pHead; - while( pCur->pNode->pRight ){ - pCur->pNode = pCur->pNode->pRight; - } - } - if( pCur->pNode ){ - *pRes = 0; - }else{ - *pRes = 1; - } - pCur->eSkip = SKIP_NONE; - return SQLITE_OK; -} - -/* -** Advance the cursor to the next entry in the database. If -** successful then set *pRes=0. If the cursor -** was already pointing to the last entry in the database before -** this routine was called, then set *pRes=1. -*/ -static int memRbtreeNext(RbtCursor* pCur, int *pRes) -{ - if( pCur->pNode && pCur->eSkip != SKIP_NEXT ){ - if( pCur->pNode->pRight ){ - pCur->pNode = pCur->pNode->pRight; - while( pCur->pNode->pLeft ) - pCur->pNode = pCur->pNode->pLeft; - }else{ - BtRbNode * pX = pCur->pNode; - pCur->pNode = pX->pParent; - while( pCur->pNode && (pCur->pNode->pRight == pX) ){ - pX = pCur->pNode; - pCur->pNode = pX->pParent; - } - } - } - pCur->eSkip = SKIP_NONE; - - if( !pCur->pNode ){ - *pRes = 1; - }else{ - *pRes = 0; - } - - return SQLITE_OK; -} - -static int memRbtreePrevious(RbtCursor* pCur, int *pRes) -{ - if( pCur->pNode && pCur->eSkip != SKIP_PREV ){ - if( pCur->pNode->pLeft ){ - pCur->pNode = pCur->pNode->pLeft; - while( pCur->pNode->pRight ) - pCur->pNode = pCur->pNode->pRight; - }else{ - BtRbNode * pX = pCur->pNode; - pCur->pNode = pX->pParent; - while( pCur->pNode && (pCur->pNode->pLeft == pX) ){ - pX = pCur->pNode; - pCur->pNode = pX->pParent; - } - } - } - pCur->eSkip = SKIP_NONE; - - if( !pCur->pNode ){ - *pRes = 1; - }else{ - *pRes = 0; - } - - return SQLITE_OK; -} - -static int memRbtreeKeySize(RbtCursor* pCur, int *pSize) -{ - if( pCur->pNode ){ - *pSize = pCur->pNode->nKey; - }else{ - *pSize = 0; - } - return SQLITE_OK; -} - -static int memRbtreeKey(RbtCursor* pCur, int offset, int amt, char *zBuf) -{ - if( !pCur->pNode ) return 0; - if( !pCur->pNode->pKey || ((amt + offset) <= pCur->pNode->nKey) ){ - memcpy(zBuf, ((char*)pCur->pNode->pKey)+offset, amt); - }else{ - memcpy(zBuf, ((char*)pCur->pNode->pKey)+offset, pCur->pNode->nKey-offset); - amt = pCur->pNode->nKey-offset; - } - return amt; -} - -static int memRbtreeDataSize(RbtCursor* pCur, int *pSize) -{ - if( pCur->pNode ){ - *pSize = pCur->pNode->nData; - }else{ - *pSize = 0; - } - return SQLITE_OK; -} - -static int memRbtreeData(RbtCursor *pCur, int offset, int amt, char *zBuf) -{ - if( !pCur->pNode ) return 0; - if( (amt + offset) <= pCur->pNode->nData ){ - memcpy(zBuf, ((char*)pCur->pNode->pData)+offset, amt); - }else{ - memcpy(zBuf, ((char*)pCur->pNode->pData)+offset ,pCur->pNode->nData-offset); - amt = pCur->pNode->nData-offset; - } - return amt; -} - -static int memRbtreeCloseCursor(RbtCursor* pCur) -{ - if( pCur->pTree->pCursors==pCur ){ - pCur->pTree->pCursors = pCur->pShared; - }else{ - RbtCursor *p = pCur->pTree->pCursors; - while( p && p->pShared!=pCur ){ p = p->pShared; } - assert( p!=0 ); - if( p ){ - p->pShared = pCur->pShared; - } - } - sqliteFree(pCur); - return SQLITE_OK; -} - -static int memRbtreeGetMeta(Rbtree* tree, int* aMeta) -{ - memcpy( aMeta, tree->aMetaData, sizeof(int) * SQLITE_N_BTREE_META ); - return SQLITE_OK; -} - -static int memRbtreeUpdateMeta(Rbtree* tree, int* aMeta) -{ - memcpy( tree->aMetaData, aMeta, sizeof(int) * SQLITE_N_BTREE_META ); - return SQLITE_OK; -} - -/* - * Check that each table in the Rbtree meets the requirements for a red-black - * binary tree. If an error is found, return an explanation of the problem in - * memory obtained from sqliteMalloc(). Parameters aRoot and nRoot are ignored. - */ -static char *memRbtreeIntegrityCheck(Rbtree* tree, int* aRoot, int nRoot) -{ - char * msg = 0; - HashElem *p; - - for(p=sqliteHashFirst(&tree->tblHash); p; p=sqliteHashNext(p)){ - BtRbTree *pTree = sqliteHashData(p); - check_redblack_tree(pTree, &msg); - } - - return msg; -} - -static int memRbtreeSetCacheSize(Rbtree* tree, int sz) -{ - return SQLITE_OK; -} - -static int memRbtreeSetSafetyLevel(Rbtree *pBt, int level){ - return SQLITE_OK; -} - -static int memRbtreeBeginTrans(Rbtree* tree) -{ - if( tree->eTransState != TRANS_NONE ) - return SQLITE_ERROR; - - assert( tree->pTransRollback == 0 ); - tree->eTransState = TRANS_INTRANSACTION; - return SQLITE_OK; -} - -/* -** Delete a linked list of BtRollbackOp structures. -*/ -static void deleteRollbackList(BtRollbackOp *pOp){ - while( pOp ){ - BtRollbackOp *pTmp = pOp->pNext; - sqliteFree(pOp->pData); - sqliteFree(pOp->pKey); - sqliteFree(pOp); - pOp = pTmp; - } -} - -static int memRbtreeCommit(Rbtree* tree){ - /* Just delete pTransRollback and pCheckRollback */ - deleteRollbackList(tree->pCheckRollback); - deleteRollbackList(tree->pTransRollback); - tree->pTransRollback = 0; - tree->pCheckRollback = 0; - tree->pCheckRollbackTail = 0; - tree->eTransState = TRANS_NONE; - return SQLITE_OK; -} - -/* - * Close the supplied Rbtree. Delete everything associated with it. - */ -static int memRbtreeClose(Rbtree* tree) -{ - HashElem *p; - memRbtreeCommit(tree); - while( (p=sqliteHashFirst(&tree->tblHash))!=0 ){ - tree->eTransState = TRANS_ROLLBACK; - memRbtreeDropTable(tree, sqliteHashKeysize(p)); - } - sqliteHashClear(&tree->tblHash); - sqliteFree(tree); - return SQLITE_OK; -} - -/* - * Execute and delete the supplied rollback-list on pRbtree. - */ -static void execute_rollback_list(Rbtree *pRbtree, BtRollbackOp *pList) -{ - BtRollbackOp *pTmp; - RbtCursor cur; - int res; - - cur.pRbtree = pRbtree; - cur.wrFlag = 1; - while( pList ){ - switch( pList->eOp ){ - case ROLLBACK_INSERT: - cur.pTree = sqliteHashFind( &pRbtree->tblHash, 0, pList->iTab ); - assert(cur.pTree); - cur.iTree = pList->iTab; - cur.eSkip = SKIP_NONE; - memRbtreeInsert( &cur, pList->pKey, - pList->nKey, pList->pData, pList->nData ); - break; - case ROLLBACK_DELETE: - cur.pTree = sqliteHashFind( &pRbtree->tblHash, 0, pList->iTab ); - assert(cur.pTree); - cur.iTree = pList->iTab; - cur.eSkip = SKIP_NONE; - memRbtreeMoveto(&cur, pList->pKey, pList->nKey, &res); - assert(res == 0); - memRbtreeDelete( &cur ); - break; - case ROLLBACK_CREATE: - btreeCreateTable(pRbtree, pList->iTab); - break; - case ROLLBACK_DROP: - memRbtreeDropTable(pRbtree, pList->iTab); - break; - default: - assert(0); - } - sqliteFree(pList->pKey); - sqliteFree(pList->pData); - pTmp = pList->pNext; - sqliteFree(pList); - pList = pTmp; - } -} - -static int memRbtreeRollback(Rbtree* tree) -{ - tree->eTransState = TRANS_ROLLBACK; - execute_rollback_list(tree, tree->pCheckRollback); - execute_rollback_list(tree, tree->pTransRollback); - tree->pTransRollback = 0; - tree->pCheckRollback = 0; - tree->pCheckRollbackTail = 0; - tree->eTransState = TRANS_NONE; - return SQLITE_OK; -} - -static int memRbtreeBeginCkpt(Rbtree* tree) -{ - if( tree->eTransState != TRANS_INTRANSACTION ) - return SQLITE_ERROR; - - assert( tree->pCheckRollback == 0 ); - assert( tree->pCheckRollbackTail == 0 ); - tree->eTransState = TRANS_INCHECKPOINT; - return SQLITE_OK; -} - -static int memRbtreeCommitCkpt(Rbtree* tree) -{ - if( tree->eTransState == TRANS_INCHECKPOINT ){ - if( tree->pCheckRollback ){ - tree->pCheckRollbackTail->pNext = tree->pTransRollback; - tree->pTransRollback = tree->pCheckRollback; - tree->pCheckRollback = 0; - tree->pCheckRollbackTail = 0; - } - tree->eTransState = TRANS_INTRANSACTION; - } - return SQLITE_OK; -} - -static int memRbtreeRollbackCkpt(Rbtree* tree) -{ - if( tree->eTransState != TRANS_INCHECKPOINT ) return SQLITE_OK; - tree->eTransState = TRANS_ROLLBACK; - execute_rollback_list(tree, tree->pCheckRollback); - tree->pCheckRollback = 0; - tree->pCheckRollbackTail = 0; - tree->eTransState = TRANS_INTRANSACTION; - return SQLITE_OK; -} - -#ifdef SQLITE_TEST -static int memRbtreePageDump(Rbtree* tree, int pgno, int rec) -{ - assert(!"Cannot call sqliteRbtreePageDump"); - return SQLITE_OK; -} - -static int memRbtreeCursorDump(RbtCursor* pCur, int* aRes) -{ - assert(!"Cannot call sqliteRbtreeCursorDump"); - return SQLITE_OK; -} -#endif - -static struct Pager *memRbtreePager(Rbtree* tree) -{ - return 0; -} - -/* -** Return the full pathname of the underlying database file. -*/ -static const char *memRbtreeGetFilename(Rbtree *pBt){ - return 0; /* A NULL return indicates there is no underlying file */ -} - -/* -** The copy file function is not implemented for the in-memory database -*/ -static int memRbtreeCopyFile(Rbtree *pBt, Rbtree *pBt2){ - return SQLITE_INTERNAL; /* Not implemented */ -} - -static BtOps sqliteRbtreeOps = { - (int(*)(Btree*)) memRbtreeClose, - (int(*)(Btree*,int)) memRbtreeSetCacheSize, - (int(*)(Btree*,int)) memRbtreeSetSafetyLevel, - (int(*)(Btree*)) memRbtreeBeginTrans, - (int(*)(Btree*)) memRbtreeCommit, - (int(*)(Btree*)) memRbtreeRollback, - (int(*)(Btree*)) memRbtreeBeginCkpt, - (int(*)(Btree*)) memRbtreeCommitCkpt, - (int(*)(Btree*)) memRbtreeRollbackCkpt, - (int(*)(Btree*,int*)) memRbtreeCreateTable, - (int(*)(Btree*,int*)) memRbtreeCreateTable, - (int(*)(Btree*,int)) memRbtreeDropTable, - (int(*)(Btree*,int)) memRbtreeClearTable, - (int(*)(Btree*,int,int,BtCursor**)) memRbtreeCursor, - (int(*)(Btree*,int*)) memRbtreeGetMeta, - (int(*)(Btree*,int*)) memRbtreeUpdateMeta, - (char*(*)(Btree*,int*,int)) memRbtreeIntegrityCheck, - (const char*(*)(Btree*)) memRbtreeGetFilename, - (int(*)(Btree*,Btree*)) memRbtreeCopyFile, - (struct Pager*(*)(Btree*)) memRbtreePager, -#ifdef SQLITE_TEST - (int(*)(Btree*,int,int)) memRbtreePageDump, -#endif -}; - -static BtCursorOps sqliteRbtreeCursorOps = { - (int(*)(BtCursor*,const void*,int,int*)) memRbtreeMoveto, - (int(*)(BtCursor*)) memRbtreeDelete, - (int(*)(BtCursor*,const void*,int,const void*,int)) memRbtreeInsert, - (int(*)(BtCursor*,int*)) memRbtreeFirst, - (int(*)(BtCursor*,int*)) memRbtreeLast, - (int(*)(BtCursor*,int*)) memRbtreeNext, - (int(*)(BtCursor*,int*)) memRbtreePrevious, - (int(*)(BtCursor*,int*)) memRbtreeKeySize, - (int(*)(BtCursor*,int,int,char*)) memRbtreeKey, - (int(*)(BtCursor*,const void*,int,int,int*)) memRbtreeKeyCompare, - (int(*)(BtCursor*,int*)) memRbtreeDataSize, - (int(*)(BtCursor*,int,int,char*)) memRbtreeData, - (int(*)(BtCursor*)) memRbtreeCloseCursor, -#ifdef SQLITE_TEST - (int(*)(BtCursor*,int*)) memRbtreeCursorDump, -#endif - -}; - -#endif /* SQLITE_OMIT_INMEMORYDB */ |