#include "btree.h"
#include "sqliteInt.h"
#include <assert.h>
#ifndef SQLITE_OMIT_INMEMORYDB
typedef struct BtRbTree BtRbTree;
typedef struct BtRbNode BtRbNode;
typedef struct BtRollbackOp BtRollbackOp;
typedef struct Rbtree Rbtree;
typedef struct RbtCursor RbtCursor;
static BtOps sqliteRbtreeOps;
static BtCursorOps sqliteRbtreeCursorOps;
struct BtRollbackOp {
u8 eOp;
int iTab;
int nKey;
void *pKey;
int nData;
void *pData;
BtRollbackOp *pNext;
};
#define ROLLBACK_INSERT 1
#define ROLLBACK_DELETE 2
#define ROLLBACK_CREATE 3
#define ROLLBACK_DROP 4
struct Rbtree {
BtOps *pOps;
int aMetaData[SQLITE_N_BTREE_META];
int next_idx;
Hash tblHash;
u8 isAnonymous;
u8 eTransState;
BtRollbackOp *pTransRollback;
BtRollbackOp *pCheckRollback;
BtRollbackOp *pCheckRollbackTail;
};
#define TRANS_NONE 0
#define TRANS_INTRANSACTION 1
#define TRANS_INCHECKPOINT 2
#define TRANS_ROLLBACK 3
struct RbtCursor {
BtCursorOps *pOps;
Rbtree *pRbtree;
BtRbTree *pTree;
int iTree;
BtRbNode *pNode;
RbtCursor *pShared;
u8 eSkip;
u8 wrFlag;
};
#define SKIP_NONE 0
#define SKIP_NEXT 1
#define SKIP_PREV 2
#define SKIP_INVALID 3
struct BtRbTree {
RbtCursor *pCursors;
BtRbNode *pHead;
};
struct BtRbNode {
int nKey;
void *pKey;
int nData;
void *pData;
u8 isBlack;
BtRbNode *pParent;
BtRbNode *pLeft;
BtRbNode *pRight;
int nBlackHeight;
};
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);
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;
}
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);
}
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;
}
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;
}
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;
}
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;
}
static void print_node(BtRbNode *pNode)
{
char * str = append_node(0, pNode, 0);
printf("%s", str);
(void)print_node;
}
static void check_redblack_tree(BtRbTree * tree, char ** msg)
{
BtRbNode *pNode;
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:
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");
}
{
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);
}
}
}
static void do_insert_balancing(BtRbTree *pTree, BtRbNode *pX)
{
while( pX != pTree->pHead && !pX->pParent->isBlack ){
BtRbNode *pUncle;
BtRbNode *pGrandparent;
pGrandparent = pX->pParent->pParent;
assert( pGrandparent );
assert( pGrandparent->isBlack );
if( pX->pParent == pGrandparent->pLeft )
pUncle = pGrandparent->pRight;
else
pUncle = pGrandparent->pLeft;
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 ){
pX = pX->pParent;
leftRotate(pTree, pX);
}
assert( pGrandparent == pX->pParent->pParent );
pGrandparent->isBlack = 0;
pX->pParent->isBlack = 1;
rightRotate( pTree, pGrandparent );
}else{
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;
}
static
void do_delete_balancing(BtRbTree *pTree, BtRbNode *pX, BtRbNode *pParent)
{
BtRbNode *pSib;
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;
}
static void btreeCreateTable(Rbtree* pRbtree, int n)
{
BtRbTree *pNewTbl = sqliteMalloc(sizeof(BtRbTree));
sqliteHashInsert(&pRbtree->tblHash, 0, n, pNewTbl);
}
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);
btreeCreateTable(*ppRbtree, 2);
if( sqlite_malloc_failed ) goto open_no_mem;
(*ppRbtree)->next_idx = 3;
(*ppRbtree)->pOps = &sqliteRbtreeOps;
(*ppRbtree)->aMetaData[2] = 4;
return SQLITE_OK;
open_no_mem:
*ppBtree = 0;
return SQLITE_NOMEM;
}
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;
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;
}
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;
}
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;
}
static int memRbtreeInsert(
RbtCursor* pCur,
const void *pKey,
int nKey,
const void *pDataInput,
int nData
){
void * pData;
int match;
assert( pCur->pRbtree->eTransState != TRANS_NONE );
if( checkReadLocks(pCur) ){
return SQLITE_LOCKED;
}
pData = sqliteMallocRaw(nData);
if( sqlite_malloc_failed ) return SQLITE_NOMEM;
memcpy(pData, pDataInput, nData);
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;
}
pCur->pNode = pNode;
do_insert_balancing(pCur->pTree, pNode);
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{
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 );
}
pCur->pNode->pData = pData;
pCur->pNode->nData = nData;
}
return SQLITE_OK;
}
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:
pCur->pNode = pCur->pNode->pLeft;
break;
case -1:
pCur->pNode = pCur->pNode->pRight;
break;
}
}
if( !pCur->pNode ) pCur->pNode = pTmp;
pCur->eSkip = SKIP_NONE;
return SQLITE_OK;
}
static int memRbtreeDelete(RbtCursor* pCur)
{
BtRbNode *pZ;
BtRbNode *pChild;
assert( pCur->pRbtree->eTransState != TRANS_NONE );
if( checkReadLocks(pCur) ){
return SQLITE_LOCKED;
}
pZ = pCur->pNode;
if( !pZ ){
return SQLITE_OK;
}
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);
}
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);
}
}
{
BtRbNode **ppParentSlot = 0;
assert( !pZ->pLeft || !pZ->pRight );
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;
}
if( pZ->isBlack ){
do_delete_balancing(pCur->pTree, pChild, pZ->pParent);
}
sqliteFree(pZ);
return SQLITE_OK;
}
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;
}
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;
}
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;
}
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){
deleteRollbackList(tree->pCheckRollback);
deleteRollbackList(tree->pTransRollback);
tree->pTransRollback = 0;
tree->pCheckRollback = 0;
tree->pCheckRollbackTail = 0;
tree->eTransState = TRANS_NONE;
return SQLITE_OK;
}
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;
}
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;
}
static const char *memRbtreeGetFilename(Rbtree *pBt){
return 0;
}
static int memRbtreeCopyFile(Rbtree *pBt, Rbtree *pBt2){
return SQLITE_INTERNAL;
}
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