Annotation of embedaddon/php/ext/sqlite/libsqlite/src/btree_rb.c, revision 1.1
1.1 ! misho 1: /*
! 2: ** 2003 Feb 4
! 3: **
! 4: ** The author disclaims copyright to this source code. In place of
! 5: ** a legal notice, here is a blessing:
! 6: **
! 7: ** May you do good and not evil.
! 8: ** May you find forgiveness for yourself and forgive others.
! 9: ** May you share freely, never taking more than you give.
! 10: **
! 11: *************************************************************************
! 12: ** $Id: btree_rb.c 195361 2005-09-07 15:11:33Z iliaa $
! 13: **
! 14: ** This file implements an in-core database using Red-Black balanced
! 15: ** binary trees.
! 16: **
! 17: ** It was contributed to SQLite by anonymous on 2003-Feb-04 23:24:49 UTC.
! 18: */
! 19: #include "btree.h"
! 20: #include "sqliteInt.h"
! 21: #include <assert.h>
! 22:
! 23: /*
! 24: ** Omit this whole file if the SQLITE_OMIT_INMEMORYDB macro is
! 25: ** defined. This allows a lot of code to be omitted for installations
! 26: ** that do not need it.
! 27: */
! 28: #ifndef SQLITE_OMIT_INMEMORYDB
! 29:
! 30:
! 31: typedef struct BtRbTree BtRbTree;
! 32: typedef struct BtRbNode BtRbNode;
! 33: typedef struct BtRollbackOp BtRollbackOp;
! 34: typedef struct Rbtree Rbtree;
! 35: typedef struct RbtCursor RbtCursor;
! 36:
! 37: /* Forward declarations */
! 38: static BtOps sqliteRbtreeOps;
! 39: static BtCursorOps sqliteRbtreeCursorOps;
! 40:
! 41: /*
! 42: * During each transaction (or checkpoint), a linked-list of
! 43: * "rollback-operations" is accumulated. If the transaction is rolled back,
! 44: * then the list of operations must be executed (to restore the database to
! 45: * it's state before the transaction started). If the transaction is to be
! 46: * committed, just delete the list.
! 47: *
! 48: * Each operation is represented as follows, depending on the value of eOp:
! 49: *
! 50: * ROLLBACK_INSERT -> Need to insert (pKey, pData) into table iTab.
! 51: * ROLLBACK_DELETE -> Need to delete the record (pKey) into table iTab.
! 52: * ROLLBACK_CREATE -> Need to create table iTab.
! 53: * ROLLBACK_DROP -> Need to drop table iTab.
! 54: */
! 55: struct BtRollbackOp {
! 56: u8 eOp;
! 57: int iTab;
! 58: int nKey;
! 59: void *pKey;
! 60: int nData;
! 61: void *pData;
! 62: BtRollbackOp *pNext;
! 63: };
! 64:
! 65: /*
! 66: ** Legal values for BtRollbackOp.eOp:
! 67: */
! 68: #define ROLLBACK_INSERT 1 /* Insert a record */
! 69: #define ROLLBACK_DELETE 2 /* Delete a record */
! 70: #define ROLLBACK_CREATE 3 /* Create a table */
! 71: #define ROLLBACK_DROP 4 /* Drop a table */
! 72:
! 73: struct Rbtree {
! 74: BtOps *pOps; /* Function table */
! 75: int aMetaData[SQLITE_N_BTREE_META];
! 76:
! 77: int next_idx; /* next available table index */
! 78: Hash tblHash; /* All created tables, by index */
! 79: u8 isAnonymous; /* True if this Rbtree is to be deleted when closed */
! 80: u8 eTransState; /* State of this Rbtree wrt transactions */
! 81:
! 82: BtRollbackOp *pTransRollback;
! 83: BtRollbackOp *pCheckRollback;
! 84: BtRollbackOp *pCheckRollbackTail;
! 85: };
! 86:
! 87: /*
! 88: ** Legal values for Rbtree.eTransState.
! 89: */
! 90: #define TRANS_NONE 0 /* No transaction is in progress */
! 91: #define TRANS_INTRANSACTION 1 /* A transaction is in progress */
! 92: #define TRANS_INCHECKPOINT 2 /* A checkpoint is in progress */
! 93: #define TRANS_ROLLBACK 3 /* We are currently rolling back a checkpoint or
! 94: * transaction. */
! 95:
! 96: struct RbtCursor {
! 97: BtCursorOps *pOps; /* Function table */
! 98: Rbtree *pRbtree;
! 99: BtRbTree *pTree;
! 100: int iTree; /* Index of pTree in pRbtree */
! 101: BtRbNode *pNode;
! 102: RbtCursor *pShared; /* List of all cursors on the same Rbtree */
! 103: u8 eSkip; /* Determines if next step operation is a no-op */
! 104: u8 wrFlag; /* True if this cursor is open for writing */
! 105: };
! 106:
! 107: /*
! 108: ** Legal values for RbtCursor.eSkip.
! 109: */
! 110: #define SKIP_NONE 0 /* Always step the cursor */
! 111: #define SKIP_NEXT 1 /* The next sqliteRbtreeNext() is a no-op */
! 112: #define SKIP_PREV 2 /* The next sqliteRbtreePrevious() is a no-op */
! 113: #define SKIP_INVALID 3 /* Calls to Next() and Previous() are invalid */
! 114:
! 115: struct BtRbTree {
! 116: RbtCursor *pCursors; /* All cursors pointing to this tree */
! 117: BtRbNode *pHead; /* Head of the tree, or NULL */
! 118: };
! 119:
! 120: struct BtRbNode {
! 121: int nKey;
! 122: void *pKey;
! 123: int nData;
! 124: void *pData;
! 125: u8 isBlack; /* true for a black node, 0 for a red node */
! 126: BtRbNode *pParent; /* Nodes parent node, NULL for the tree head */
! 127: BtRbNode *pLeft; /* Nodes left child, or NULL */
! 128: BtRbNode *pRight; /* Nodes right child, or NULL */
! 129:
! 130: int nBlackHeight; /* Only used during the red-black integrity check */
! 131: };
! 132:
! 133: /* Forward declarations */
! 134: static int memRbtreeMoveto(
! 135: RbtCursor* pCur,
! 136: const void *pKey,
! 137: int nKey,
! 138: int *pRes
! 139: );
! 140: static int memRbtreeClearTable(Rbtree* tree, int n);
! 141: static int memRbtreeNext(RbtCursor* pCur, int *pRes);
! 142: static int memRbtreeLast(RbtCursor* pCur, int *pRes);
! 143: static int memRbtreePrevious(RbtCursor* pCur, int *pRes);
! 144:
! 145:
! 146: /*
! 147: ** This routine checks all cursors that point to the same table
! 148: ** as pCur points to. If any of those cursors were opened with
! 149: ** wrFlag==0 then this routine returns SQLITE_LOCKED. If all
! 150: ** cursors point to the same table were opened with wrFlag==1
! 151: ** then this routine returns SQLITE_OK.
! 152: **
! 153: ** In addition to checking for read-locks (where a read-lock
! 154: ** means a cursor opened with wrFlag==0) this routine also NULLs
! 155: ** out the pNode field of all other cursors.
! 156: ** This is necessary because an insert
! 157: ** or delete might change erase the node out from under
! 158: ** another cursor.
! 159: */
! 160: static int checkReadLocks(RbtCursor *pCur){
! 161: RbtCursor *p;
! 162: assert( pCur->wrFlag );
! 163: for(p=pCur->pTree->pCursors; p; p=p->pShared){
! 164: if( p!=pCur ){
! 165: if( p->wrFlag==0 ) return SQLITE_LOCKED;
! 166: p->pNode = 0;
! 167: }
! 168: }
! 169: return SQLITE_OK;
! 170: }
! 171:
! 172: /*
! 173: * The key-compare function for the red-black trees. Returns as follows:
! 174: *
! 175: * (key1 < key2) -1
! 176: * (key1 == key2) 0
! 177: * (key1 > key2) 1
! 178: *
! 179: * Keys are compared using memcmp(). If one key is an exact prefix of the
! 180: * other, then the shorter key is less than the longer key.
! 181: */
! 182: static int key_compare(void const*pKey1, int nKey1, void const*pKey2, int nKey2)
! 183: {
! 184: int mcmp = memcmp(pKey1, pKey2, (nKey1 <= nKey2)?nKey1:nKey2);
! 185: if( mcmp == 0){
! 186: if( nKey1 == nKey2 ) return 0;
! 187: return ((nKey1 < nKey2)?-1:1);
! 188: }
! 189: return ((mcmp>0)?1:-1);
! 190: }
! 191:
! 192: /*
! 193: * Perform the LEFT-rotate transformation on node X of tree pTree. This
! 194: * transform is part of the red-black balancing code.
! 195: *
! 196: * | |
! 197: * X Y
! 198: * / \ / \
! 199: * a Y X c
! 200: * / \ / \
! 201: * b c a b
! 202: *
! 203: * BEFORE AFTER
! 204: */
! 205: static void leftRotate(BtRbTree *pTree, BtRbNode *pX)
! 206: {
! 207: BtRbNode *pY;
! 208: BtRbNode *pb;
! 209: pY = pX->pRight;
! 210: pb = pY->pLeft;
! 211:
! 212: pY->pParent = pX->pParent;
! 213: if( pX->pParent ){
! 214: if( pX->pParent->pLeft == pX ) pX->pParent->pLeft = pY;
! 215: else pX->pParent->pRight = pY;
! 216: }
! 217: pY->pLeft = pX;
! 218: pX->pParent = pY;
! 219: pX->pRight = pb;
! 220: if( pb ) pb->pParent = pX;
! 221: if( pTree->pHead == pX ) pTree->pHead = pY;
! 222: }
! 223:
! 224: /*
! 225: * Perform the RIGHT-rotate transformation on node X of tree pTree. This
! 226: * transform is part of the red-black balancing code.
! 227: *
! 228: * | |
! 229: * X Y
! 230: * / \ / \
! 231: * Y c a X
! 232: * / \ / \
! 233: * a b b c
! 234: *
! 235: * BEFORE AFTER
! 236: */
! 237: static void rightRotate(BtRbTree *pTree, BtRbNode *pX)
! 238: {
! 239: BtRbNode *pY;
! 240: BtRbNode *pb;
! 241: pY = pX->pLeft;
! 242: pb = pY->pRight;
! 243:
! 244: pY->pParent = pX->pParent;
! 245: if( pX->pParent ){
! 246: if( pX->pParent->pLeft == pX ) pX->pParent->pLeft = pY;
! 247: else pX->pParent->pRight = pY;
! 248: }
! 249: pY->pRight = pX;
! 250: pX->pParent = pY;
! 251: pX->pLeft = pb;
! 252: if( pb ) pb->pParent = pX;
! 253: if( pTree->pHead == pX ) pTree->pHead = pY;
! 254: }
! 255:
! 256: /*
! 257: * A string-manipulation helper function for check_redblack_tree(). If (orig ==
! 258: * NULL) a copy of val is returned. If (orig != NULL) then a copy of the *
! 259: * concatenation of orig and val is returned. The original orig is deleted
! 260: * (using sqliteFree()).
! 261: */
! 262: static char *append_val(char * orig, char const * val){
! 263: char *z;
! 264: if( !orig ){
! 265: z = sqliteStrDup( val );
! 266: } else{
! 267: z = 0;
! 268: sqliteSetString(&z, orig, val, (char*)0);
! 269: sqliteFree( orig );
! 270: }
! 271: return z;
! 272: }
! 273:
! 274: /*
! 275: * Append a string representation of the entire node to orig and return it.
! 276: * This is used to produce debugging information if check_redblack_tree() finds
! 277: * a problem with a red-black binary tree.
! 278: */
! 279: static char *append_node(char * orig, BtRbNode *pNode, int indent)
! 280: {
! 281: char buf[128];
! 282: int i;
! 283:
! 284: for( i=0; i<indent; i++ ){
! 285: orig = append_val(orig, " ");
! 286: }
! 287:
! 288: sprintf(buf, "%p", pNode);
! 289: orig = append_val(orig, buf);
! 290:
! 291: if( pNode ){
! 292: indent += 3;
! 293: if( pNode->isBlack ){
! 294: orig = append_val(orig, " B \n");
! 295: }else{
! 296: orig = append_val(orig, " R \n");
! 297: }
! 298: orig = append_node( orig, pNode->pLeft, indent );
! 299: orig = append_node( orig, pNode->pRight, indent );
! 300: }else{
! 301: orig = append_val(orig, "\n");
! 302: }
! 303: return orig;
! 304: }
! 305:
! 306: /*
! 307: * Print a representation of a node to stdout. This function is only included
! 308: * so you can call it from within a debugger if things get really bad. It
! 309: * is not called from anyplace in the code.
! 310: */
! 311: static void print_node(BtRbNode *pNode)
! 312: {
! 313: char * str = append_node(0, pNode, 0);
! 314: printf("%s", str);
! 315:
! 316: /* Suppress a warning message about print_node() being unused */
! 317: (void)print_node;
! 318: }
! 319:
! 320: /*
! 321: * Check the following properties of the red-black tree:
! 322: * (1) - If a node is red, both of it's children are black
! 323: * (2) - Each path from a given node to a leaf (NULL) node passes thru the
! 324: * same number of black nodes
! 325: *
! 326: * If there is a problem, append a description (using append_val() ) to *msg.
! 327: */
! 328: static void check_redblack_tree(BtRbTree * tree, char ** msg)
! 329: {
! 330: BtRbNode *pNode;
! 331:
! 332: /* 0 -> came from parent
! 333: * 1 -> came from left
! 334: * 2 -> came from right */
! 335: int prev_step = 0;
! 336:
! 337: pNode = tree->pHead;
! 338: while( pNode ){
! 339: switch( prev_step ){
! 340: case 0:
! 341: if( pNode->pLeft ){
! 342: pNode = pNode->pLeft;
! 343: }else{
! 344: prev_step = 1;
! 345: }
! 346: break;
! 347: case 1:
! 348: if( pNode->pRight ){
! 349: pNode = pNode->pRight;
! 350: prev_step = 0;
! 351: }else{
! 352: prev_step = 2;
! 353: }
! 354: break;
! 355: case 2:
! 356: /* Check red-black property (1) */
! 357: if( !pNode->isBlack &&
! 358: ( (pNode->pLeft && !pNode->pLeft->isBlack) ||
! 359: (pNode->pRight && !pNode->pRight->isBlack) )
! 360: ){
! 361: char buf[128];
! 362: sprintf(buf, "Red node with red child at %p\n", pNode);
! 363: *msg = append_val(*msg, buf);
! 364: *msg = append_node(*msg, tree->pHead, 0);
! 365: *msg = append_val(*msg, "\n");
! 366: }
! 367:
! 368: /* Check red-black property (2) */
! 369: {
! 370: int leftHeight = 0;
! 371: int rightHeight = 0;
! 372: if( pNode->pLeft ){
! 373: leftHeight += pNode->pLeft->nBlackHeight;
! 374: leftHeight += (pNode->pLeft->isBlack?1:0);
! 375: }
! 376: if( pNode->pRight ){
! 377: rightHeight += pNode->pRight->nBlackHeight;
! 378: rightHeight += (pNode->pRight->isBlack?1:0);
! 379: }
! 380: if( leftHeight != rightHeight ){
! 381: char buf[128];
! 382: sprintf(buf, "Different black-heights at %p\n", pNode);
! 383: *msg = append_val(*msg, buf);
! 384: *msg = append_node(*msg, tree->pHead, 0);
! 385: *msg = append_val(*msg, "\n");
! 386: }
! 387: pNode->nBlackHeight = leftHeight;
! 388: }
! 389:
! 390: if( pNode->pParent ){
! 391: if( pNode == pNode->pParent->pLeft ) prev_step = 1;
! 392: else prev_step = 2;
! 393: }
! 394: pNode = pNode->pParent;
! 395: break;
! 396: default: assert(0);
! 397: }
! 398: }
! 399: }
! 400:
! 401: /*
! 402: * Node pX has just been inserted into pTree (by code in sqliteRbtreeInsert()).
! 403: * It is possible that pX is a red node with a red parent, which is a violation
! 404: * of the red-black tree properties. This function performs rotations and
! 405: * color changes to rebalance the tree
! 406: */
! 407: static void do_insert_balancing(BtRbTree *pTree, BtRbNode *pX)
! 408: {
! 409: /* In the first iteration of this loop, pX points to the red node just
! 410: * inserted in the tree. If the parent of pX exists (pX is not the root
! 411: * node) and is red, then the properties of the red-black tree are
! 412: * violated.
! 413: *
! 414: * At the start of any subsequent iterations, pX points to a red node
! 415: * with a red parent. In all other respects the tree is a legal red-black
! 416: * binary tree. */
! 417: while( pX != pTree->pHead && !pX->pParent->isBlack ){
! 418: BtRbNode *pUncle;
! 419: BtRbNode *pGrandparent;
! 420:
! 421: /* Grandparent of pX must exist and must be black. */
! 422: pGrandparent = pX->pParent->pParent;
! 423: assert( pGrandparent );
! 424: assert( pGrandparent->isBlack );
! 425:
! 426: /* Uncle of pX may or may not exist. */
! 427: if( pX->pParent == pGrandparent->pLeft )
! 428: pUncle = pGrandparent->pRight;
! 429: else
! 430: pUncle = pGrandparent->pLeft;
! 431:
! 432: /* If the uncle of pX exists and is red, we do the following:
! 433: * | |
! 434: * G(b) G(r)
! 435: * / \ / \
! 436: * U(r) P(r) U(b) P(b)
! 437: * \ \
! 438: * X(r) X(r)
! 439: *
! 440: * BEFORE AFTER
! 441: * pX is then set to G. If the parent of G is red, then the while loop
! 442: * will run again. */
! 443: if( pUncle && !pUncle->isBlack ){
! 444: pGrandparent->isBlack = 0;
! 445: pUncle->isBlack = 1;
! 446: pX->pParent->isBlack = 1;
! 447: pX = pGrandparent;
! 448: }else{
! 449:
! 450: if( pX->pParent == pGrandparent->pLeft ){
! 451: if( pX == pX->pParent->pRight ){
! 452: /* If pX is a right-child, do the following transform, essentially
! 453: * to change pX into a left-child:
! 454: * | |
! 455: * G(b) G(b)
! 456: * / \ / \
! 457: * P(r) U(b) X(r) U(b)
! 458: * \ /
! 459: * X(r) P(r) <-- new X
! 460: *
! 461: * BEFORE AFTER
! 462: */
! 463: pX = pX->pParent;
! 464: leftRotate(pTree, pX);
! 465: }
! 466:
! 467: /* Do the following transform, which balances the tree :)
! 468: * | |
! 469: * G(b) P(b)
! 470: * / \ / \
! 471: * P(r) U(b) X(r) G(r)
! 472: * / \
! 473: * X(r) U(b)
! 474: *
! 475: * BEFORE AFTER
! 476: */
! 477: assert( pGrandparent == pX->pParent->pParent );
! 478: pGrandparent->isBlack = 0;
! 479: pX->pParent->isBlack = 1;
! 480: rightRotate( pTree, pGrandparent );
! 481:
! 482: }else{
! 483: /* This code is symetric to the illustrated case above. */
! 484: if( pX == pX->pParent->pLeft ){
! 485: pX = pX->pParent;
! 486: rightRotate(pTree, pX);
! 487: }
! 488: assert( pGrandparent == pX->pParent->pParent );
! 489: pGrandparent->isBlack = 0;
! 490: pX->pParent->isBlack = 1;
! 491: leftRotate( pTree, pGrandparent );
! 492: }
! 493: }
! 494: }
! 495: pTree->pHead->isBlack = 1;
! 496: }
! 497:
! 498: /*
! 499: * A child of pParent, which in turn had child pX, has just been removed from
! 500: * pTree (the figure below depicts the operation, Z is being removed). pParent
! 501: * or pX, or both may be NULL.
! 502: * | |
! 503: * P P
! 504: * / \ / \
! 505: * Z X
! 506: * / \
! 507: * X nil
! 508: *
! 509: * This function is only called if Z was black. In this case the red-black tree
! 510: * properties have been violated, and pX has an "extra black". This function
! 511: * performs rotations and color-changes to re-balance the tree.
! 512: */
! 513: static
! 514: void do_delete_balancing(BtRbTree *pTree, BtRbNode *pX, BtRbNode *pParent)
! 515: {
! 516: BtRbNode *pSib;
! 517:
! 518: /* TODO: Comment this code! */
! 519: while( pX != pTree->pHead && (!pX || pX->isBlack) ){
! 520: if( pX == pParent->pLeft ){
! 521: pSib = pParent->pRight;
! 522: if( pSib && !(pSib->isBlack) ){
! 523: pSib->isBlack = 1;
! 524: pParent->isBlack = 0;
! 525: leftRotate(pTree, pParent);
! 526: pSib = pParent->pRight;
! 527: }
! 528: if( !pSib ){
! 529: pX = pParent;
! 530: }else if(
! 531: (!pSib->pLeft || pSib->pLeft->isBlack) &&
! 532: (!pSib->pRight || pSib->pRight->isBlack) ) {
! 533: pSib->isBlack = 0;
! 534: pX = pParent;
! 535: }else{
! 536: if( (!pSib->pRight || pSib->pRight->isBlack) ){
! 537: if( pSib->pLeft ) pSib->pLeft->isBlack = 1;
! 538: pSib->isBlack = 0;
! 539: rightRotate( pTree, pSib );
! 540: pSib = pParent->pRight;
! 541: }
! 542: pSib->isBlack = pParent->isBlack;
! 543: pParent->isBlack = 1;
! 544: if( pSib->pRight ) pSib->pRight->isBlack = 1;
! 545: leftRotate(pTree, pParent);
! 546: pX = pTree->pHead;
! 547: }
! 548: }else{
! 549: pSib = pParent->pLeft;
! 550: if( pSib && !(pSib->isBlack) ){
! 551: pSib->isBlack = 1;
! 552: pParent->isBlack = 0;
! 553: rightRotate(pTree, pParent);
! 554: pSib = pParent->pLeft;
! 555: }
! 556: if( !pSib ){
! 557: pX = pParent;
! 558: }else if(
! 559: (!pSib->pLeft || pSib->pLeft->isBlack) &&
! 560: (!pSib->pRight || pSib->pRight->isBlack) ){
! 561: pSib->isBlack = 0;
! 562: pX = pParent;
! 563: }else{
! 564: if( (!pSib->pLeft || pSib->pLeft->isBlack) ){
! 565: if( pSib->pRight ) pSib->pRight->isBlack = 1;
! 566: pSib->isBlack = 0;
! 567: leftRotate( pTree, pSib );
! 568: pSib = pParent->pLeft;
! 569: }
! 570: pSib->isBlack = pParent->isBlack;
! 571: pParent->isBlack = 1;
! 572: if( pSib->pLeft ) pSib->pLeft->isBlack = 1;
! 573: rightRotate(pTree, pParent);
! 574: pX = pTree->pHead;
! 575: }
! 576: }
! 577: pParent = pX->pParent;
! 578: }
! 579: if( pX ) pX->isBlack = 1;
! 580: }
! 581:
! 582: /*
! 583: * Create table n in tree pRbtree. Table n must not exist.
! 584: */
! 585: static void btreeCreateTable(Rbtree* pRbtree, int n)
! 586: {
! 587: BtRbTree *pNewTbl = sqliteMalloc(sizeof(BtRbTree));
! 588: sqliteHashInsert(&pRbtree->tblHash, 0, n, pNewTbl);
! 589: }
! 590:
! 591: /*
! 592: * Log a single "rollback-op" for the given Rbtree. See comments for struct
! 593: * BtRollbackOp.
! 594: */
! 595: static void btreeLogRollbackOp(Rbtree* pRbtree, BtRollbackOp *pRollbackOp)
! 596: {
! 597: assert( pRbtree->eTransState == TRANS_INCHECKPOINT ||
! 598: pRbtree->eTransState == TRANS_INTRANSACTION );
! 599: if( pRbtree->eTransState == TRANS_INTRANSACTION ){
! 600: pRollbackOp->pNext = pRbtree->pTransRollback;
! 601: pRbtree->pTransRollback = pRollbackOp;
! 602: }
! 603: if( pRbtree->eTransState == TRANS_INCHECKPOINT ){
! 604: if( !pRbtree->pCheckRollback ){
! 605: pRbtree->pCheckRollbackTail = pRollbackOp;
! 606: }
! 607: pRollbackOp->pNext = pRbtree->pCheckRollback;
! 608: pRbtree->pCheckRollback = pRollbackOp;
! 609: }
! 610: }
! 611:
! 612: int sqliteRbtreeOpen(
! 613: const char *zFilename,
! 614: int mode,
! 615: int nPg,
! 616: Btree **ppBtree
! 617: ){
! 618: Rbtree **ppRbtree = (Rbtree**)ppBtree;
! 619: *ppRbtree = (Rbtree *)sqliteMalloc(sizeof(Rbtree));
! 620: if( sqlite_malloc_failed ) goto open_no_mem;
! 621: sqliteHashInit(&(*ppRbtree)->tblHash, SQLITE_HASH_INT, 0);
! 622:
! 623: /* Create a binary tree for the SQLITE_MASTER table at location 2 */
! 624: btreeCreateTable(*ppRbtree, 2);
! 625: if( sqlite_malloc_failed ) goto open_no_mem;
! 626: (*ppRbtree)->next_idx = 3;
! 627: (*ppRbtree)->pOps = &sqliteRbtreeOps;
! 628: /* Set file type to 4; this is so that "attach ':memory:' as ...." does not
! 629: ** think that the database in uninitialised and refuse to attach
! 630: */
! 631: (*ppRbtree)->aMetaData[2] = 4;
! 632:
! 633: return SQLITE_OK;
! 634:
! 635: open_no_mem:
! 636: *ppBtree = 0;
! 637: return SQLITE_NOMEM;
! 638: }
! 639:
! 640: /*
! 641: * Create a new table in the supplied Rbtree. Set *n to the new table number.
! 642: * Return SQLITE_OK if the operation is a success.
! 643: */
! 644: static int memRbtreeCreateTable(Rbtree* tree, int* n)
! 645: {
! 646: assert( tree->eTransState != TRANS_NONE );
! 647:
! 648: *n = tree->next_idx++;
! 649: btreeCreateTable(tree, *n);
! 650: if( sqlite_malloc_failed ) return SQLITE_NOMEM;
! 651:
! 652: /* Set up the rollback structure (if we are not doing this as part of a
! 653: * rollback) */
! 654: if( tree->eTransState != TRANS_ROLLBACK ){
! 655: BtRollbackOp *pRollbackOp = sqliteMalloc(sizeof(BtRollbackOp));
! 656: if( pRollbackOp==0 ) return SQLITE_NOMEM;
! 657: pRollbackOp->eOp = ROLLBACK_DROP;
! 658: pRollbackOp->iTab = *n;
! 659: btreeLogRollbackOp(tree, pRollbackOp);
! 660: }
! 661:
! 662: return SQLITE_OK;
! 663: }
! 664:
! 665: /*
! 666: * Delete table n from the supplied Rbtree.
! 667: */
! 668: static int memRbtreeDropTable(Rbtree* tree, int n)
! 669: {
! 670: BtRbTree *pTree;
! 671: assert( tree->eTransState != TRANS_NONE );
! 672:
! 673: memRbtreeClearTable(tree, n);
! 674: pTree = sqliteHashInsert(&tree->tblHash, 0, n, 0);
! 675: assert(pTree);
! 676: assert( pTree->pCursors==0 );
! 677: sqliteFree(pTree);
! 678:
! 679: if( tree->eTransState != TRANS_ROLLBACK ){
! 680: BtRollbackOp *pRollbackOp = sqliteMalloc(sizeof(BtRollbackOp));
! 681: if( pRollbackOp==0 ) return SQLITE_NOMEM;
! 682: pRollbackOp->eOp = ROLLBACK_CREATE;
! 683: pRollbackOp->iTab = n;
! 684: btreeLogRollbackOp(tree, pRollbackOp);
! 685: }
! 686:
! 687: return SQLITE_OK;
! 688: }
! 689:
! 690: static int memRbtreeKeyCompare(RbtCursor* pCur, const void *pKey, int nKey,
! 691: int nIgnore, int *pRes)
! 692: {
! 693: assert(pCur);
! 694:
! 695: if( !pCur->pNode ) {
! 696: *pRes = -1;
! 697: } else {
! 698: if( (pCur->pNode->nKey - nIgnore) < 0 ){
! 699: *pRes = -1;
! 700: }else{
! 701: *pRes = key_compare(pCur->pNode->pKey, pCur->pNode->nKey-nIgnore,
! 702: pKey, nKey);
! 703: }
! 704: }
! 705: return SQLITE_OK;
! 706: }
! 707:
! 708: /*
! 709: * Get a new cursor for table iTable of the supplied Rbtree. The wrFlag
! 710: * parameter indicates that the cursor is open for writing.
! 711: *
! 712: * Note that RbtCursor.eSkip and RbtCursor.pNode both initialize to 0.
! 713: */
! 714: static int memRbtreeCursor(
! 715: Rbtree* tree,
! 716: int iTable,
! 717: int wrFlag,
! 718: RbtCursor **ppCur
! 719: ){
! 720: RbtCursor *pCur;
! 721: assert(tree);
! 722: pCur = *ppCur = sqliteMalloc(sizeof(RbtCursor));
! 723: if( sqlite_malloc_failed ) return SQLITE_NOMEM;
! 724: pCur->pTree = sqliteHashFind(&tree->tblHash, 0, iTable);
! 725: assert( pCur->pTree );
! 726: pCur->pRbtree = tree;
! 727: pCur->iTree = iTable;
! 728: pCur->pOps = &sqliteRbtreeCursorOps;
! 729: pCur->wrFlag = wrFlag;
! 730: pCur->pShared = pCur->pTree->pCursors;
! 731: pCur->pTree->pCursors = pCur;
! 732:
! 733: assert( (*ppCur)->pTree );
! 734: return SQLITE_OK;
! 735: }
! 736:
! 737: /*
! 738: * Insert a new record into the Rbtree. The key is given by (pKey,nKey)
! 739: * and the data is given by (pData,nData). The cursor is used only to
! 740: * define what database the record should be inserted into. The cursor
! 741: * is left pointing at the new record.
! 742: *
! 743: * If the key exists already in the tree, just replace the data.
! 744: */
! 745: static int memRbtreeInsert(
! 746: RbtCursor* pCur,
! 747: const void *pKey,
! 748: int nKey,
! 749: const void *pDataInput,
! 750: int nData
! 751: ){
! 752: void * pData;
! 753: int match;
! 754:
! 755: /* It is illegal to call sqliteRbtreeInsert() if we are
! 756: ** not in a transaction */
! 757: assert( pCur->pRbtree->eTransState != TRANS_NONE );
! 758:
! 759: /* Make sure some other cursor isn't trying to read this same table */
! 760: if( checkReadLocks(pCur) ){
! 761: return SQLITE_LOCKED; /* The table pCur points to has a read lock */
! 762: }
! 763:
! 764: /* Take a copy of the input data now, in case we need it for the
! 765: * replace case */
! 766: pData = sqliteMallocRaw(nData);
! 767: if( sqlite_malloc_failed ) return SQLITE_NOMEM;
! 768: memcpy(pData, pDataInput, nData);
! 769:
! 770: /* Move the cursor to a node near the key to be inserted. If the key already
! 771: * exists in the table, then (match == 0). In this case we can just replace
! 772: * the data associated with the entry, we don't need to manipulate the tree.
! 773: *
! 774: * If there is no exact match, then the cursor points at what would be either
! 775: * the predecessor (match == -1) or successor (match == 1) of the
! 776: * searched-for key, were it to be inserted. The new node becomes a child of
! 777: * this node.
! 778: *
! 779: * The new node is initially red.
! 780: */
! 781: memRbtreeMoveto( pCur, pKey, nKey, &match);
! 782: if( match ){
! 783: BtRbNode *pNode = sqliteMalloc(sizeof(BtRbNode));
! 784: if( pNode==0 ) return SQLITE_NOMEM;
! 785: pNode->nKey = nKey;
! 786: pNode->pKey = sqliteMallocRaw(nKey);
! 787: if( sqlite_malloc_failed ) return SQLITE_NOMEM;
! 788: memcpy(pNode->pKey, pKey, nKey);
! 789: pNode->nData = nData;
! 790: pNode->pData = pData;
! 791: if( pCur->pNode ){
! 792: switch( match ){
! 793: case -1:
! 794: assert( !pCur->pNode->pRight );
! 795: pNode->pParent = pCur->pNode;
! 796: pCur->pNode->pRight = pNode;
! 797: break;
! 798: case 1:
! 799: assert( !pCur->pNode->pLeft );
! 800: pNode->pParent = pCur->pNode;
! 801: pCur->pNode->pLeft = pNode;
! 802: break;
! 803: default:
! 804: assert(0);
! 805: }
! 806: }else{
! 807: pCur->pTree->pHead = pNode;
! 808: }
! 809:
! 810: /* Point the cursor at the node just inserted, as per SQLite requirements */
! 811: pCur->pNode = pNode;
! 812:
! 813: /* A new node has just been inserted, so run the balancing code */
! 814: do_insert_balancing(pCur->pTree, pNode);
! 815:
! 816: /* Set up a rollback-op in case we have to roll this operation back */
! 817: if( pCur->pRbtree->eTransState != TRANS_ROLLBACK ){
! 818: BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) );
! 819: if( pOp==0 ) return SQLITE_NOMEM;
! 820: pOp->eOp = ROLLBACK_DELETE;
! 821: pOp->iTab = pCur->iTree;
! 822: pOp->nKey = pNode->nKey;
! 823: pOp->pKey = sqliteMallocRaw( pOp->nKey );
! 824: if( sqlite_malloc_failed ) return SQLITE_NOMEM;
! 825: memcpy( pOp->pKey, pNode->pKey, pOp->nKey );
! 826: btreeLogRollbackOp(pCur->pRbtree, pOp);
! 827: }
! 828:
! 829: }else{
! 830: /* No need to insert a new node in the tree, as the key already exists.
! 831: * Just clobber the current nodes data. */
! 832:
! 833: /* Set up a rollback-op in case we have to roll this operation back */
! 834: if( pCur->pRbtree->eTransState != TRANS_ROLLBACK ){
! 835: BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) );
! 836: if( pOp==0 ) return SQLITE_NOMEM;
! 837: pOp->iTab = pCur->iTree;
! 838: pOp->nKey = pCur->pNode->nKey;
! 839: pOp->pKey = sqliteMallocRaw( pOp->nKey );
! 840: if( sqlite_malloc_failed ) return SQLITE_NOMEM;
! 841: memcpy( pOp->pKey, pCur->pNode->pKey, pOp->nKey );
! 842: pOp->nData = pCur->pNode->nData;
! 843: pOp->pData = pCur->pNode->pData;
! 844: pOp->eOp = ROLLBACK_INSERT;
! 845: btreeLogRollbackOp(pCur->pRbtree, pOp);
! 846: }else{
! 847: sqliteFree( pCur->pNode->pData );
! 848: }
! 849:
! 850: /* Actually clobber the nodes data */
! 851: pCur->pNode->pData = pData;
! 852: pCur->pNode->nData = nData;
! 853: }
! 854:
! 855: return SQLITE_OK;
! 856: }
! 857:
! 858: /* Move the cursor so that it points to an entry near pKey.
! 859: ** Return a success code.
! 860: **
! 861: ** *pRes<0 The cursor is left pointing at an entry that
! 862: ** is smaller than pKey or if the table is empty
! 863: ** and the cursor is therefore left point to nothing.
! 864: **
! 865: ** *pRes==0 The cursor is left pointing at an entry that
! 866: ** exactly matches pKey.
! 867: **
! 868: ** *pRes>0 The cursor is left pointing at an entry that
! 869: ** is larger than pKey.
! 870: */
! 871: static int memRbtreeMoveto(
! 872: RbtCursor* pCur,
! 873: const void *pKey,
! 874: int nKey,
! 875: int *pRes
! 876: ){
! 877: BtRbNode *pTmp = 0;
! 878:
! 879: pCur->pNode = pCur->pTree->pHead;
! 880: *pRes = -1;
! 881: while( pCur->pNode && *pRes ) {
! 882: *pRes = key_compare(pCur->pNode->pKey, pCur->pNode->nKey, pKey, nKey);
! 883: pTmp = pCur->pNode;
! 884: switch( *pRes ){
! 885: case 1: /* cursor > key */
! 886: pCur->pNode = pCur->pNode->pLeft;
! 887: break;
! 888: case -1: /* cursor < key */
! 889: pCur->pNode = pCur->pNode->pRight;
! 890: break;
! 891: }
! 892: }
! 893:
! 894: /* If (pCur->pNode == NULL), then we have failed to find a match. Set
! 895: * pCur->pNode to pTmp, which is either NULL (if the tree is empty) or the
! 896: * last node traversed in the search. In either case the relation ship
! 897: * between pTmp and the searched for key is already stored in *pRes. pTmp is
! 898: * either the successor or predecessor of the key we tried to move to. */
! 899: if( !pCur->pNode ) pCur->pNode = pTmp;
! 900: pCur->eSkip = SKIP_NONE;
! 901:
! 902: return SQLITE_OK;
! 903: }
! 904:
! 905:
! 906: /*
! 907: ** Delete the entry that the cursor is pointing to.
! 908: **
! 909: ** The cursor is left pointing at either the next or the previous
! 910: ** entry. If the cursor is left pointing to the next entry, then
! 911: ** the pCur->eSkip flag is set to SKIP_NEXT which forces the next call to
! 912: ** sqliteRbtreeNext() to be a no-op. That way, you can always call
! 913: ** sqliteRbtreeNext() after a delete and the cursor will be left
! 914: ** pointing to the first entry after the deleted entry. Similarly,
! 915: ** pCur->eSkip is set to SKIP_PREV is the cursor is left pointing to
! 916: ** the entry prior to the deleted entry so that a subsequent call to
! 917: ** sqliteRbtreePrevious() will always leave the cursor pointing at the
! 918: ** entry immediately before the one that was deleted.
! 919: */
! 920: static int memRbtreeDelete(RbtCursor* pCur)
! 921: {
! 922: BtRbNode *pZ; /* The one being deleted */
! 923: BtRbNode *pChild; /* The child of the spliced out node */
! 924:
! 925: /* It is illegal to call sqliteRbtreeDelete() if we are
! 926: ** not in a transaction */
! 927: assert( pCur->pRbtree->eTransState != TRANS_NONE );
! 928:
! 929: /* Make sure some other cursor isn't trying to read this same table */
! 930: if( checkReadLocks(pCur) ){
! 931: return SQLITE_LOCKED; /* The table pCur points to has a read lock */
! 932: }
! 933:
! 934: pZ = pCur->pNode;
! 935: if( !pZ ){
! 936: return SQLITE_OK;
! 937: }
! 938:
! 939: /* If we are not currently doing a rollback, set up a rollback op for this
! 940: * deletion */
! 941: if( pCur->pRbtree->eTransState != TRANS_ROLLBACK ){
! 942: BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) );
! 943: if( pOp==0 ) return SQLITE_NOMEM;
! 944: pOp->iTab = pCur->iTree;
! 945: pOp->nKey = pZ->nKey;
! 946: pOp->pKey = pZ->pKey;
! 947: pOp->nData = pZ->nData;
! 948: pOp->pData = pZ->pData;
! 949: pOp->eOp = ROLLBACK_INSERT;
! 950: btreeLogRollbackOp(pCur->pRbtree, pOp);
! 951: }
! 952:
! 953: /* First do a standard binary-tree delete (node pZ is to be deleted). How
! 954: * to do this depends on how many children pZ has:
! 955: *
! 956: * If pZ has no children or one child, then splice out pZ. If pZ has two
! 957: * children, splice out the successor of pZ and replace the key and data of
! 958: * pZ with the key and data of the spliced out successor. */
! 959: if( pZ->pLeft && pZ->pRight ){
! 960: BtRbNode *pTmp;
! 961: int dummy;
! 962: pCur->eSkip = SKIP_NONE;
! 963: memRbtreeNext(pCur, &dummy);
! 964: assert( dummy == 0 );
! 965: if( pCur->pRbtree->eTransState == TRANS_ROLLBACK ){
! 966: sqliteFree(pZ->pKey);
! 967: sqliteFree(pZ->pData);
! 968: }
! 969: pZ->pData = pCur->pNode->pData;
! 970: pZ->nData = pCur->pNode->nData;
! 971: pZ->pKey = pCur->pNode->pKey;
! 972: pZ->nKey = pCur->pNode->nKey;
! 973: pTmp = pZ;
! 974: pZ = pCur->pNode;
! 975: pCur->pNode = pTmp;
! 976: pCur->eSkip = SKIP_NEXT;
! 977: }else{
! 978: int res;
! 979: pCur->eSkip = SKIP_NONE;
! 980: memRbtreeNext(pCur, &res);
! 981: pCur->eSkip = SKIP_NEXT;
! 982: if( res ){
! 983: memRbtreeLast(pCur, &res);
! 984: memRbtreePrevious(pCur, &res);
! 985: pCur->eSkip = SKIP_PREV;
! 986: }
! 987: if( pCur->pRbtree->eTransState == TRANS_ROLLBACK ){
! 988: sqliteFree(pZ->pKey);
! 989: sqliteFree(pZ->pData);
! 990: }
! 991: }
! 992:
! 993: /* pZ now points at the node to be spliced out. This block does the
! 994: * splicing. */
! 995: {
! 996: BtRbNode **ppParentSlot = 0;
! 997: assert( !pZ->pLeft || !pZ->pRight ); /* pZ has at most one child */
! 998: pChild = ((pZ->pLeft)?pZ->pLeft:pZ->pRight);
! 999: if( pZ->pParent ){
! 1000: assert( pZ == pZ->pParent->pLeft || pZ == pZ->pParent->pRight );
! 1001: ppParentSlot = ((pZ == pZ->pParent->pLeft)
! 1002: ?&pZ->pParent->pLeft:&pZ->pParent->pRight);
! 1003: *ppParentSlot = pChild;
! 1004: }else{
! 1005: pCur->pTree->pHead = pChild;
! 1006: }
! 1007: if( pChild ) pChild->pParent = pZ->pParent;
! 1008: }
! 1009:
! 1010: /* pZ now points at the spliced out node. pChild is the only child of pZ, or
! 1011: * NULL if pZ has no children. If pZ is black, and not the tree root, then we
! 1012: * will have violated the "same number of black nodes in every path to a
! 1013: * leaf" property of the red-black tree. The code in do_delete_balancing()
! 1014: * repairs this. */
! 1015: if( pZ->isBlack ){
! 1016: do_delete_balancing(pCur->pTree, pChild, pZ->pParent);
! 1017: }
! 1018:
! 1019: sqliteFree(pZ);
! 1020: return SQLITE_OK;
! 1021: }
! 1022:
! 1023: /*
! 1024: * Empty table n of the Rbtree.
! 1025: */
! 1026: static int memRbtreeClearTable(Rbtree* tree, int n)
! 1027: {
! 1028: BtRbTree *pTree;
! 1029: BtRbNode *pNode;
! 1030:
! 1031: pTree = sqliteHashFind(&tree->tblHash, 0, n);
! 1032: assert(pTree);
! 1033:
! 1034: pNode = pTree->pHead;
! 1035: while( pNode ){
! 1036: if( pNode->pLeft ){
! 1037: pNode = pNode->pLeft;
! 1038: }
! 1039: else if( pNode->pRight ){
! 1040: pNode = pNode->pRight;
! 1041: }
! 1042: else {
! 1043: BtRbNode *pTmp = pNode->pParent;
! 1044: if( tree->eTransState == TRANS_ROLLBACK ){
! 1045: sqliteFree( pNode->pKey );
! 1046: sqliteFree( pNode->pData );
! 1047: }else{
! 1048: BtRollbackOp *pRollbackOp = sqliteMallocRaw(sizeof(BtRollbackOp));
! 1049: if( pRollbackOp==0 ) return SQLITE_NOMEM;
! 1050: pRollbackOp->eOp = ROLLBACK_INSERT;
! 1051: pRollbackOp->iTab = n;
! 1052: pRollbackOp->nKey = pNode->nKey;
! 1053: pRollbackOp->pKey = pNode->pKey;
! 1054: pRollbackOp->nData = pNode->nData;
! 1055: pRollbackOp->pData = pNode->pData;
! 1056: btreeLogRollbackOp(tree, pRollbackOp);
! 1057: }
! 1058: sqliteFree( pNode );
! 1059: if( pTmp ){
! 1060: if( pTmp->pLeft == pNode ) pTmp->pLeft = 0;
! 1061: else if( pTmp->pRight == pNode ) pTmp->pRight = 0;
! 1062: }
! 1063: pNode = pTmp;
! 1064: }
! 1065: }
! 1066:
! 1067: pTree->pHead = 0;
! 1068: return SQLITE_OK;
! 1069: }
! 1070:
! 1071: static int memRbtreeFirst(RbtCursor* pCur, int *pRes)
! 1072: {
! 1073: if( pCur->pTree->pHead ){
! 1074: pCur->pNode = pCur->pTree->pHead;
! 1075: while( pCur->pNode->pLeft ){
! 1076: pCur->pNode = pCur->pNode->pLeft;
! 1077: }
! 1078: }
! 1079: if( pCur->pNode ){
! 1080: *pRes = 0;
! 1081: }else{
! 1082: *pRes = 1;
! 1083: }
! 1084: pCur->eSkip = SKIP_NONE;
! 1085: return SQLITE_OK;
! 1086: }
! 1087:
! 1088: static int memRbtreeLast(RbtCursor* pCur, int *pRes)
! 1089: {
! 1090: if( pCur->pTree->pHead ){
! 1091: pCur->pNode = pCur->pTree->pHead;
! 1092: while( pCur->pNode->pRight ){
! 1093: pCur->pNode = pCur->pNode->pRight;
! 1094: }
! 1095: }
! 1096: if( pCur->pNode ){
! 1097: *pRes = 0;
! 1098: }else{
! 1099: *pRes = 1;
! 1100: }
! 1101: pCur->eSkip = SKIP_NONE;
! 1102: return SQLITE_OK;
! 1103: }
! 1104:
! 1105: /*
! 1106: ** Advance the cursor to the next entry in the database. If
! 1107: ** successful then set *pRes=0. If the cursor
! 1108: ** was already pointing to the last entry in the database before
! 1109: ** this routine was called, then set *pRes=1.
! 1110: */
! 1111: static int memRbtreeNext(RbtCursor* pCur, int *pRes)
! 1112: {
! 1113: if( pCur->pNode && pCur->eSkip != SKIP_NEXT ){
! 1114: if( pCur->pNode->pRight ){
! 1115: pCur->pNode = pCur->pNode->pRight;
! 1116: while( pCur->pNode->pLeft )
! 1117: pCur->pNode = pCur->pNode->pLeft;
! 1118: }else{
! 1119: BtRbNode * pX = pCur->pNode;
! 1120: pCur->pNode = pX->pParent;
! 1121: while( pCur->pNode && (pCur->pNode->pRight == pX) ){
! 1122: pX = pCur->pNode;
! 1123: pCur->pNode = pX->pParent;
! 1124: }
! 1125: }
! 1126: }
! 1127: pCur->eSkip = SKIP_NONE;
! 1128:
! 1129: if( !pCur->pNode ){
! 1130: *pRes = 1;
! 1131: }else{
! 1132: *pRes = 0;
! 1133: }
! 1134:
! 1135: return SQLITE_OK;
! 1136: }
! 1137:
! 1138: static int memRbtreePrevious(RbtCursor* pCur, int *pRes)
! 1139: {
! 1140: if( pCur->pNode && pCur->eSkip != SKIP_PREV ){
! 1141: if( pCur->pNode->pLeft ){
! 1142: pCur->pNode = pCur->pNode->pLeft;
! 1143: while( pCur->pNode->pRight )
! 1144: pCur->pNode = pCur->pNode->pRight;
! 1145: }else{
! 1146: BtRbNode * pX = pCur->pNode;
! 1147: pCur->pNode = pX->pParent;
! 1148: while( pCur->pNode && (pCur->pNode->pLeft == pX) ){
! 1149: pX = pCur->pNode;
! 1150: pCur->pNode = pX->pParent;
! 1151: }
! 1152: }
! 1153: }
! 1154: pCur->eSkip = SKIP_NONE;
! 1155:
! 1156: if( !pCur->pNode ){
! 1157: *pRes = 1;
! 1158: }else{
! 1159: *pRes = 0;
! 1160: }
! 1161:
! 1162: return SQLITE_OK;
! 1163: }
! 1164:
! 1165: static int memRbtreeKeySize(RbtCursor* pCur, int *pSize)
! 1166: {
! 1167: if( pCur->pNode ){
! 1168: *pSize = pCur->pNode->nKey;
! 1169: }else{
! 1170: *pSize = 0;
! 1171: }
! 1172: return SQLITE_OK;
! 1173: }
! 1174:
! 1175: static int memRbtreeKey(RbtCursor* pCur, int offset, int amt, char *zBuf)
! 1176: {
! 1177: if( !pCur->pNode ) return 0;
! 1178: if( !pCur->pNode->pKey || ((amt + offset) <= pCur->pNode->nKey) ){
! 1179: memcpy(zBuf, ((char*)pCur->pNode->pKey)+offset, amt);
! 1180: }else{
! 1181: memcpy(zBuf, ((char*)pCur->pNode->pKey)+offset, pCur->pNode->nKey-offset);
! 1182: amt = pCur->pNode->nKey-offset;
! 1183: }
! 1184: return amt;
! 1185: }
! 1186:
! 1187: static int memRbtreeDataSize(RbtCursor* pCur, int *pSize)
! 1188: {
! 1189: if( pCur->pNode ){
! 1190: *pSize = pCur->pNode->nData;
! 1191: }else{
! 1192: *pSize = 0;
! 1193: }
! 1194: return SQLITE_OK;
! 1195: }
! 1196:
! 1197: static int memRbtreeData(RbtCursor *pCur, int offset, int amt, char *zBuf)
! 1198: {
! 1199: if( !pCur->pNode ) return 0;
! 1200: if( (amt + offset) <= pCur->pNode->nData ){
! 1201: memcpy(zBuf, ((char*)pCur->pNode->pData)+offset, amt);
! 1202: }else{
! 1203: memcpy(zBuf, ((char*)pCur->pNode->pData)+offset ,pCur->pNode->nData-offset);
! 1204: amt = pCur->pNode->nData-offset;
! 1205: }
! 1206: return amt;
! 1207: }
! 1208:
! 1209: static int memRbtreeCloseCursor(RbtCursor* pCur)
! 1210: {
! 1211: if( pCur->pTree->pCursors==pCur ){
! 1212: pCur->pTree->pCursors = pCur->pShared;
! 1213: }else{
! 1214: RbtCursor *p = pCur->pTree->pCursors;
! 1215: while( p && p->pShared!=pCur ){ p = p->pShared; }
! 1216: assert( p!=0 );
! 1217: if( p ){
! 1218: p->pShared = pCur->pShared;
! 1219: }
! 1220: }
! 1221: sqliteFree(pCur);
! 1222: return SQLITE_OK;
! 1223: }
! 1224:
! 1225: static int memRbtreeGetMeta(Rbtree* tree, int* aMeta)
! 1226: {
! 1227: memcpy( aMeta, tree->aMetaData, sizeof(int) * SQLITE_N_BTREE_META );
! 1228: return SQLITE_OK;
! 1229: }
! 1230:
! 1231: static int memRbtreeUpdateMeta(Rbtree* tree, int* aMeta)
! 1232: {
! 1233: memcpy( tree->aMetaData, aMeta, sizeof(int) * SQLITE_N_BTREE_META );
! 1234: return SQLITE_OK;
! 1235: }
! 1236:
! 1237: /*
! 1238: * Check that each table in the Rbtree meets the requirements for a red-black
! 1239: * binary tree. If an error is found, return an explanation of the problem in
! 1240: * memory obtained from sqliteMalloc(). Parameters aRoot and nRoot are ignored.
! 1241: */
! 1242: static char *memRbtreeIntegrityCheck(Rbtree* tree, int* aRoot, int nRoot)
! 1243: {
! 1244: char * msg = 0;
! 1245: HashElem *p;
! 1246:
! 1247: for(p=sqliteHashFirst(&tree->tblHash); p; p=sqliteHashNext(p)){
! 1248: BtRbTree *pTree = sqliteHashData(p);
! 1249: check_redblack_tree(pTree, &msg);
! 1250: }
! 1251:
! 1252: return msg;
! 1253: }
! 1254:
! 1255: static int memRbtreeSetCacheSize(Rbtree* tree, int sz)
! 1256: {
! 1257: return SQLITE_OK;
! 1258: }
! 1259:
! 1260: static int memRbtreeSetSafetyLevel(Rbtree *pBt, int level){
! 1261: return SQLITE_OK;
! 1262: }
! 1263:
! 1264: static int memRbtreeBeginTrans(Rbtree* tree)
! 1265: {
! 1266: if( tree->eTransState != TRANS_NONE )
! 1267: return SQLITE_ERROR;
! 1268:
! 1269: assert( tree->pTransRollback == 0 );
! 1270: tree->eTransState = TRANS_INTRANSACTION;
! 1271: return SQLITE_OK;
! 1272: }
! 1273:
! 1274: /*
! 1275: ** Delete a linked list of BtRollbackOp structures.
! 1276: */
! 1277: static void deleteRollbackList(BtRollbackOp *pOp){
! 1278: while( pOp ){
! 1279: BtRollbackOp *pTmp = pOp->pNext;
! 1280: sqliteFree(pOp->pData);
! 1281: sqliteFree(pOp->pKey);
! 1282: sqliteFree(pOp);
! 1283: pOp = pTmp;
! 1284: }
! 1285: }
! 1286:
! 1287: static int memRbtreeCommit(Rbtree* tree){
! 1288: /* Just delete pTransRollback and pCheckRollback */
! 1289: deleteRollbackList(tree->pCheckRollback);
! 1290: deleteRollbackList(tree->pTransRollback);
! 1291: tree->pTransRollback = 0;
! 1292: tree->pCheckRollback = 0;
! 1293: tree->pCheckRollbackTail = 0;
! 1294: tree->eTransState = TRANS_NONE;
! 1295: return SQLITE_OK;
! 1296: }
! 1297:
! 1298: /*
! 1299: * Close the supplied Rbtree. Delete everything associated with it.
! 1300: */
! 1301: static int memRbtreeClose(Rbtree* tree)
! 1302: {
! 1303: HashElem *p;
! 1304: memRbtreeCommit(tree);
! 1305: while( (p=sqliteHashFirst(&tree->tblHash))!=0 ){
! 1306: tree->eTransState = TRANS_ROLLBACK;
! 1307: memRbtreeDropTable(tree, sqliteHashKeysize(p));
! 1308: }
! 1309: sqliteHashClear(&tree->tblHash);
! 1310: sqliteFree(tree);
! 1311: return SQLITE_OK;
! 1312: }
! 1313:
! 1314: /*
! 1315: * Execute and delete the supplied rollback-list on pRbtree.
! 1316: */
! 1317: static void execute_rollback_list(Rbtree *pRbtree, BtRollbackOp *pList)
! 1318: {
! 1319: BtRollbackOp *pTmp;
! 1320: RbtCursor cur;
! 1321: int res;
! 1322:
! 1323: cur.pRbtree = pRbtree;
! 1324: cur.wrFlag = 1;
! 1325: while( pList ){
! 1326: switch( pList->eOp ){
! 1327: case ROLLBACK_INSERT:
! 1328: cur.pTree = sqliteHashFind( &pRbtree->tblHash, 0, pList->iTab );
! 1329: assert(cur.pTree);
! 1330: cur.iTree = pList->iTab;
! 1331: cur.eSkip = SKIP_NONE;
! 1332: memRbtreeInsert( &cur, pList->pKey,
! 1333: pList->nKey, pList->pData, pList->nData );
! 1334: break;
! 1335: case ROLLBACK_DELETE:
! 1336: cur.pTree = sqliteHashFind( &pRbtree->tblHash, 0, pList->iTab );
! 1337: assert(cur.pTree);
! 1338: cur.iTree = pList->iTab;
! 1339: cur.eSkip = SKIP_NONE;
! 1340: memRbtreeMoveto(&cur, pList->pKey, pList->nKey, &res);
! 1341: assert(res == 0);
! 1342: memRbtreeDelete( &cur );
! 1343: break;
! 1344: case ROLLBACK_CREATE:
! 1345: btreeCreateTable(pRbtree, pList->iTab);
! 1346: break;
! 1347: case ROLLBACK_DROP:
! 1348: memRbtreeDropTable(pRbtree, pList->iTab);
! 1349: break;
! 1350: default:
! 1351: assert(0);
! 1352: }
! 1353: sqliteFree(pList->pKey);
! 1354: sqliteFree(pList->pData);
! 1355: pTmp = pList->pNext;
! 1356: sqliteFree(pList);
! 1357: pList = pTmp;
! 1358: }
! 1359: }
! 1360:
! 1361: static int memRbtreeRollback(Rbtree* tree)
! 1362: {
! 1363: tree->eTransState = TRANS_ROLLBACK;
! 1364: execute_rollback_list(tree, tree->pCheckRollback);
! 1365: execute_rollback_list(tree, tree->pTransRollback);
! 1366: tree->pTransRollback = 0;
! 1367: tree->pCheckRollback = 0;
! 1368: tree->pCheckRollbackTail = 0;
! 1369: tree->eTransState = TRANS_NONE;
! 1370: return SQLITE_OK;
! 1371: }
! 1372:
! 1373: static int memRbtreeBeginCkpt(Rbtree* tree)
! 1374: {
! 1375: if( tree->eTransState != TRANS_INTRANSACTION )
! 1376: return SQLITE_ERROR;
! 1377:
! 1378: assert( tree->pCheckRollback == 0 );
! 1379: assert( tree->pCheckRollbackTail == 0 );
! 1380: tree->eTransState = TRANS_INCHECKPOINT;
! 1381: return SQLITE_OK;
! 1382: }
! 1383:
! 1384: static int memRbtreeCommitCkpt(Rbtree* tree)
! 1385: {
! 1386: if( tree->eTransState == TRANS_INCHECKPOINT ){
! 1387: if( tree->pCheckRollback ){
! 1388: tree->pCheckRollbackTail->pNext = tree->pTransRollback;
! 1389: tree->pTransRollback = tree->pCheckRollback;
! 1390: tree->pCheckRollback = 0;
! 1391: tree->pCheckRollbackTail = 0;
! 1392: }
! 1393: tree->eTransState = TRANS_INTRANSACTION;
! 1394: }
! 1395: return SQLITE_OK;
! 1396: }
! 1397:
! 1398: static int memRbtreeRollbackCkpt(Rbtree* tree)
! 1399: {
! 1400: if( tree->eTransState != TRANS_INCHECKPOINT ) return SQLITE_OK;
! 1401: tree->eTransState = TRANS_ROLLBACK;
! 1402: execute_rollback_list(tree, tree->pCheckRollback);
! 1403: tree->pCheckRollback = 0;
! 1404: tree->pCheckRollbackTail = 0;
! 1405: tree->eTransState = TRANS_INTRANSACTION;
! 1406: return SQLITE_OK;
! 1407: }
! 1408:
! 1409: #ifdef SQLITE_TEST
! 1410: static int memRbtreePageDump(Rbtree* tree, int pgno, int rec)
! 1411: {
! 1412: assert(!"Cannot call sqliteRbtreePageDump");
! 1413: return SQLITE_OK;
! 1414: }
! 1415:
! 1416: static int memRbtreeCursorDump(RbtCursor* pCur, int* aRes)
! 1417: {
! 1418: assert(!"Cannot call sqliteRbtreeCursorDump");
! 1419: return SQLITE_OK;
! 1420: }
! 1421: #endif
! 1422:
! 1423: static struct Pager *memRbtreePager(Rbtree* tree)
! 1424: {
! 1425: return 0;
! 1426: }
! 1427:
! 1428: /*
! 1429: ** Return the full pathname of the underlying database file.
! 1430: */
! 1431: static const char *memRbtreeGetFilename(Rbtree *pBt){
! 1432: return 0; /* A NULL return indicates there is no underlying file */
! 1433: }
! 1434:
! 1435: /*
! 1436: ** The copy file function is not implemented for the in-memory database
! 1437: */
! 1438: static int memRbtreeCopyFile(Rbtree *pBt, Rbtree *pBt2){
! 1439: return SQLITE_INTERNAL; /* Not implemented */
! 1440: }
! 1441:
! 1442: static BtOps sqliteRbtreeOps = {
! 1443: (int(*)(Btree*)) memRbtreeClose,
! 1444: (int(*)(Btree*,int)) memRbtreeSetCacheSize,
! 1445: (int(*)(Btree*,int)) memRbtreeSetSafetyLevel,
! 1446: (int(*)(Btree*)) memRbtreeBeginTrans,
! 1447: (int(*)(Btree*)) memRbtreeCommit,
! 1448: (int(*)(Btree*)) memRbtreeRollback,
! 1449: (int(*)(Btree*)) memRbtreeBeginCkpt,
! 1450: (int(*)(Btree*)) memRbtreeCommitCkpt,
! 1451: (int(*)(Btree*)) memRbtreeRollbackCkpt,
! 1452: (int(*)(Btree*,int*)) memRbtreeCreateTable,
! 1453: (int(*)(Btree*,int*)) memRbtreeCreateTable,
! 1454: (int(*)(Btree*,int)) memRbtreeDropTable,
! 1455: (int(*)(Btree*,int)) memRbtreeClearTable,
! 1456: (int(*)(Btree*,int,int,BtCursor**)) memRbtreeCursor,
! 1457: (int(*)(Btree*,int*)) memRbtreeGetMeta,
! 1458: (int(*)(Btree*,int*)) memRbtreeUpdateMeta,
! 1459: (char*(*)(Btree*,int*,int)) memRbtreeIntegrityCheck,
! 1460: (const char*(*)(Btree*)) memRbtreeGetFilename,
! 1461: (int(*)(Btree*,Btree*)) memRbtreeCopyFile,
! 1462: (struct Pager*(*)(Btree*)) memRbtreePager,
! 1463: #ifdef SQLITE_TEST
! 1464: (int(*)(Btree*,int,int)) memRbtreePageDump,
! 1465: #endif
! 1466: };
! 1467:
! 1468: static BtCursorOps sqliteRbtreeCursorOps = {
! 1469: (int(*)(BtCursor*,const void*,int,int*)) memRbtreeMoveto,
! 1470: (int(*)(BtCursor*)) memRbtreeDelete,
! 1471: (int(*)(BtCursor*,const void*,int,const void*,int)) memRbtreeInsert,
! 1472: (int(*)(BtCursor*,int*)) memRbtreeFirst,
! 1473: (int(*)(BtCursor*,int*)) memRbtreeLast,
! 1474: (int(*)(BtCursor*,int*)) memRbtreeNext,
! 1475: (int(*)(BtCursor*,int*)) memRbtreePrevious,
! 1476: (int(*)(BtCursor*,int*)) memRbtreeKeySize,
! 1477: (int(*)(BtCursor*,int,int,char*)) memRbtreeKey,
! 1478: (int(*)(BtCursor*,const void*,int,int,int*)) memRbtreeKeyCompare,
! 1479: (int(*)(BtCursor*,int*)) memRbtreeDataSize,
! 1480: (int(*)(BtCursor*,int,int,char*)) memRbtreeData,
! 1481: (int(*)(BtCursor*)) memRbtreeCloseCursor,
! 1482: #ifdef SQLITE_TEST
! 1483: (int(*)(BtCursor*,int*)) memRbtreeCursorDump,
! 1484: #endif
! 1485:
! 1486: };
! 1487:
! 1488: #endif /* SQLITE_OMIT_INMEMORYDB */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>