Annotation of embedaddon/sqlite3/src/rowset.c, revision 1.1
1.1 ! misho 1: /*
! 2: ** 2008 December 3
! 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: **
! 13: ** This module implements an object we call a "RowSet".
! 14: **
! 15: ** The RowSet object is a collection of rowids. Rowids
! 16: ** are inserted into the RowSet in an arbitrary order. Inserts
! 17: ** can be intermixed with tests to see if a given rowid has been
! 18: ** previously inserted into the RowSet.
! 19: **
! 20: ** After all inserts are finished, it is possible to extract the
! 21: ** elements of the RowSet in sorted order. Once this extraction
! 22: ** process has started, no new elements may be inserted.
! 23: **
! 24: ** Hence, the primitive operations for a RowSet are:
! 25: **
! 26: ** CREATE
! 27: ** INSERT
! 28: ** TEST
! 29: ** SMALLEST
! 30: ** DESTROY
! 31: **
! 32: ** The CREATE and DESTROY primitives are the constructor and destructor,
! 33: ** obviously. The INSERT primitive adds a new element to the RowSet.
! 34: ** TEST checks to see if an element is already in the RowSet. SMALLEST
! 35: ** extracts the least value from the RowSet.
! 36: **
! 37: ** The INSERT primitive might allocate additional memory. Memory is
! 38: ** allocated in chunks so most INSERTs do no allocation. There is an
! 39: ** upper bound on the size of allocated memory. No memory is freed
! 40: ** until DESTROY.
! 41: **
! 42: ** The TEST primitive includes a "batch" number. The TEST primitive
! 43: ** will only see elements that were inserted before the last change
! 44: ** in the batch number. In other words, if an INSERT occurs between
! 45: ** two TESTs where the TESTs have the same batch nubmer, then the
! 46: ** value added by the INSERT will not be visible to the second TEST.
! 47: ** The initial batch number is zero, so if the very first TEST contains
! 48: ** a non-zero batch number, it will see all prior INSERTs.
! 49: **
! 50: ** No INSERTs may occurs after a SMALLEST. An assertion will fail if
! 51: ** that is attempted.
! 52: **
! 53: ** The cost of an INSERT is roughly constant. (Sometime new memory
! 54: ** has to be allocated on an INSERT.) The cost of a TEST with a new
! 55: ** batch number is O(NlogN) where N is the number of elements in the RowSet.
! 56: ** The cost of a TEST using the same batch number is O(logN). The cost
! 57: ** of the first SMALLEST is O(NlogN). Second and subsequent SMALLEST
! 58: ** primitives are constant time. The cost of DESTROY is O(N).
! 59: **
! 60: ** There is an added cost of O(N) when switching between TEST and
! 61: ** SMALLEST primitives.
! 62: */
! 63: #include "sqliteInt.h"
! 64:
! 65:
! 66: /*
! 67: ** Target size for allocation chunks.
! 68: */
! 69: #define ROWSET_ALLOCATION_SIZE 1024
! 70:
! 71: /*
! 72: ** The number of rowset entries per allocation chunk.
! 73: */
! 74: #define ROWSET_ENTRY_PER_CHUNK \
! 75: ((ROWSET_ALLOCATION_SIZE-8)/sizeof(struct RowSetEntry))
! 76:
! 77: /*
! 78: ** Each entry in a RowSet is an instance of the following object.
! 79: */
! 80: struct RowSetEntry {
! 81: i64 v; /* ROWID value for this entry */
! 82: struct RowSetEntry *pRight; /* Right subtree (larger entries) or list */
! 83: struct RowSetEntry *pLeft; /* Left subtree (smaller entries) */
! 84: };
! 85:
! 86: /*
! 87: ** RowSetEntry objects are allocated in large chunks (instances of the
! 88: ** following structure) to reduce memory allocation overhead. The
! 89: ** chunks are kept on a linked list so that they can be deallocated
! 90: ** when the RowSet is destroyed.
! 91: */
! 92: struct RowSetChunk {
! 93: struct RowSetChunk *pNextChunk; /* Next chunk on list of them all */
! 94: struct RowSetEntry aEntry[ROWSET_ENTRY_PER_CHUNK]; /* Allocated entries */
! 95: };
! 96:
! 97: /*
! 98: ** A RowSet in an instance of the following structure.
! 99: **
! 100: ** A typedef of this structure if found in sqliteInt.h.
! 101: */
! 102: struct RowSet {
! 103: struct RowSetChunk *pChunk; /* List of all chunk allocations */
! 104: sqlite3 *db; /* The database connection */
! 105: struct RowSetEntry *pEntry; /* List of entries using pRight */
! 106: struct RowSetEntry *pLast; /* Last entry on the pEntry list */
! 107: struct RowSetEntry *pFresh; /* Source of new entry objects */
! 108: struct RowSetEntry *pTree; /* Binary tree of entries */
! 109: u16 nFresh; /* Number of objects on pFresh */
! 110: u8 isSorted; /* True if pEntry is sorted */
! 111: u8 iBatch; /* Current insert batch */
! 112: };
! 113:
! 114: /*
! 115: ** Turn bulk memory into a RowSet object. N bytes of memory
! 116: ** are available at pSpace. The db pointer is used as a memory context
! 117: ** for any subsequent allocations that need to occur.
! 118: ** Return a pointer to the new RowSet object.
! 119: **
! 120: ** It must be the case that N is sufficient to make a Rowset. If not
! 121: ** an assertion fault occurs.
! 122: **
! 123: ** If N is larger than the minimum, use the surplus as an initial
! 124: ** allocation of entries available to be filled.
! 125: */
! 126: RowSet *sqlite3RowSetInit(sqlite3 *db, void *pSpace, unsigned int N){
! 127: RowSet *p;
! 128: assert( N >= ROUND8(sizeof(*p)) );
! 129: p = pSpace;
! 130: p->pChunk = 0;
! 131: p->db = db;
! 132: p->pEntry = 0;
! 133: p->pLast = 0;
! 134: p->pTree = 0;
! 135: p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p);
! 136: p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry));
! 137: p->isSorted = 1;
! 138: p->iBatch = 0;
! 139: return p;
! 140: }
! 141:
! 142: /*
! 143: ** Deallocate all chunks from a RowSet. This frees all memory that
! 144: ** the RowSet has allocated over its lifetime. This routine is
! 145: ** the destructor for the RowSet.
! 146: */
! 147: void sqlite3RowSetClear(RowSet *p){
! 148: struct RowSetChunk *pChunk, *pNextChunk;
! 149: for(pChunk=p->pChunk; pChunk; pChunk = pNextChunk){
! 150: pNextChunk = pChunk->pNextChunk;
! 151: sqlite3DbFree(p->db, pChunk);
! 152: }
! 153: p->pChunk = 0;
! 154: p->nFresh = 0;
! 155: p->pEntry = 0;
! 156: p->pLast = 0;
! 157: p->pTree = 0;
! 158: p->isSorted = 1;
! 159: }
! 160:
! 161: /*
! 162: ** Insert a new value into a RowSet.
! 163: **
! 164: ** The mallocFailed flag of the database connection is set if a
! 165: ** memory allocation fails.
! 166: */
! 167: void sqlite3RowSetInsert(RowSet *p, i64 rowid){
! 168: struct RowSetEntry *pEntry; /* The new entry */
! 169: struct RowSetEntry *pLast; /* The last prior entry */
! 170: assert( p!=0 );
! 171: if( p->nFresh==0 ){
! 172: struct RowSetChunk *pNew;
! 173: pNew = sqlite3DbMallocRaw(p->db, sizeof(*pNew));
! 174: if( pNew==0 ){
! 175: return;
! 176: }
! 177: pNew->pNextChunk = p->pChunk;
! 178: p->pChunk = pNew;
! 179: p->pFresh = pNew->aEntry;
! 180: p->nFresh = ROWSET_ENTRY_PER_CHUNK;
! 181: }
! 182: pEntry = p->pFresh++;
! 183: p->nFresh--;
! 184: pEntry->v = rowid;
! 185: pEntry->pRight = 0;
! 186: pLast = p->pLast;
! 187: if( pLast ){
! 188: if( p->isSorted && rowid<=pLast->v ){
! 189: p->isSorted = 0;
! 190: }
! 191: pLast->pRight = pEntry;
! 192: }else{
! 193: assert( p->pEntry==0 ); /* Fires if INSERT after SMALLEST */
! 194: p->pEntry = pEntry;
! 195: }
! 196: p->pLast = pEntry;
! 197: }
! 198:
! 199: /*
! 200: ** Merge two lists of RowSetEntry objects. Remove duplicates.
! 201: **
! 202: ** The input lists are connected via pRight pointers and are
! 203: ** assumed to each already be in sorted order.
! 204: */
! 205: static struct RowSetEntry *rowSetMerge(
! 206: struct RowSetEntry *pA, /* First sorted list to be merged */
! 207: struct RowSetEntry *pB /* Second sorted list to be merged */
! 208: ){
! 209: struct RowSetEntry head;
! 210: struct RowSetEntry *pTail;
! 211:
! 212: pTail = &head;
! 213: while( pA && pB ){
! 214: assert( pA->pRight==0 || pA->v<=pA->pRight->v );
! 215: assert( pB->pRight==0 || pB->v<=pB->pRight->v );
! 216: if( pA->v<pB->v ){
! 217: pTail->pRight = pA;
! 218: pA = pA->pRight;
! 219: pTail = pTail->pRight;
! 220: }else if( pB->v<pA->v ){
! 221: pTail->pRight = pB;
! 222: pB = pB->pRight;
! 223: pTail = pTail->pRight;
! 224: }else{
! 225: pA = pA->pRight;
! 226: }
! 227: }
! 228: if( pA ){
! 229: assert( pA->pRight==0 || pA->v<=pA->pRight->v );
! 230: pTail->pRight = pA;
! 231: }else{
! 232: assert( pB==0 || pB->pRight==0 || pB->v<=pB->pRight->v );
! 233: pTail->pRight = pB;
! 234: }
! 235: return head.pRight;
! 236: }
! 237:
! 238: /*
! 239: ** Sort all elements on the pEntry list of the RowSet into ascending order.
! 240: */
! 241: static void rowSetSort(RowSet *p){
! 242: unsigned int i;
! 243: struct RowSetEntry *pEntry;
! 244: struct RowSetEntry *aBucket[40];
! 245:
! 246: assert( p->isSorted==0 );
! 247: memset(aBucket, 0, sizeof(aBucket));
! 248: while( p->pEntry ){
! 249: pEntry = p->pEntry;
! 250: p->pEntry = pEntry->pRight;
! 251: pEntry->pRight = 0;
! 252: for(i=0; aBucket[i]; i++){
! 253: pEntry = rowSetMerge(aBucket[i], pEntry);
! 254: aBucket[i] = 0;
! 255: }
! 256: aBucket[i] = pEntry;
! 257: }
! 258: pEntry = 0;
! 259: for(i=0; i<sizeof(aBucket)/sizeof(aBucket[0]); i++){
! 260: pEntry = rowSetMerge(pEntry, aBucket[i]);
! 261: }
! 262: p->pEntry = pEntry;
! 263: p->pLast = 0;
! 264: p->isSorted = 1;
! 265: }
! 266:
! 267:
! 268: /*
! 269: ** The input, pIn, is a binary tree (or subtree) of RowSetEntry objects.
! 270: ** Convert this tree into a linked list connected by the pRight pointers
! 271: ** and return pointers to the first and last elements of the new list.
! 272: */
! 273: static void rowSetTreeToList(
! 274: struct RowSetEntry *pIn, /* Root of the input tree */
! 275: struct RowSetEntry **ppFirst, /* Write head of the output list here */
! 276: struct RowSetEntry **ppLast /* Write tail of the output list here */
! 277: ){
! 278: assert( pIn!=0 );
! 279: if( pIn->pLeft ){
! 280: struct RowSetEntry *p;
! 281: rowSetTreeToList(pIn->pLeft, ppFirst, &p);
! 282: p->pRight = pIn;
! 283: }else{
! 284: *ppFirst = pIn;
! 285: }
! 286: if( pIn->pRight ){
! 287: rowSetTreeToList(pIn->pRight, &pIn->pRight, ppLast);
! 288: }else{
! 289: *ppLast = pIn;
! 290: }
! 291: assert( (*ppLast)->pRight==0 );
! 292: }
! 293:
! 294:
! 295: /*
! 296: ** Convert a sorted list of elements (connected by pRight) into a binary
! 297: ** tree with depth of iDepth. A depth of 1 means the tree contains a single
! 298: ** node taken from the head of *ppList. A depth of 2 means a tree with
! 299: ** three nodes. And so forth.
! 300: **
! 301: ** Use as many entries from the input list as required and update the
! 302: ** *ppList to point to the unused elements of the list. If the input
! 303: ** list contains too few elements, then construct an incomplete tree
! 304: ** and leave *ppList set to NULL.
! 305: **
! 306: ** Return a pointer to the root of the constructed binary tree.
! 307: */
! 308: static struct RowSetEntry *rowSetNDeepTree(
! 309: struct RowSetEntry **ppList,
! 310: int iDepth
! 311: ){
! 312: struct RowSetEntry *p; /* Root of the new tree */
! 313: struct RowSetEntry *pLeft; /* Left subtree */
! 314: if( *ppList==0 ){
! 315: return 0;
! 316: }
! 317: if( iDepth==1 ){
! 318: p = *ppList;
! 319: *ppList = p->pRight;
! 320: p->pLeft = p->pRight = 0;
! 321: return p;
! 322: }
! 323: pLeft = rowSetNDeepTree(ppList, iDepth-1);
! 324: p = *ppList;
! 325: if( p==0 ){
! 326: return pLeft;
! 327: }
! 328: p->pLeft = pLeft;
! 329: *ppList = p->pRight;
! 330: p->pRight = rowSetNDeepTree(ppList, iDepth-1);
! 331: return p;
! 332: }
! 333:
! 334: /*
! 335: ** Convert a sorted list of elements into a binary tree. Make the tree
! 336: ** as deep as it needs to be in order to contain the entire list.
! 337: */
! 338: static struct RowSetEntry *rowSetListToTree(struct RowSetEntry *pList){
! 339: int iDepth; /* Depth of the tree so far */
! 340: struct RowSetEntry *p; /* Current tree root */
! 341: struct RowSetEntry *pLeft; /* Left subtree */
! 342:
! 343: assert( pList!=0 );
! 344: p = pList;
! 345: pList = p->pRight;
! 346: p->pLeft = p->pRight = 0;
! 347: for(iDepth=1; pList; iDepth++){
! 348: pLeft = p;
! 349: p = pList;
! 350: pList = p->pRight;
! 351: p->pLeft = pLeft;
! 352: p->pRight = rowSetNDeepTree(&pList, iDepth);
! 353: }
! 354: return p;
! 355: }
! 356:
! 357: /*
! 358: ** Convert the list in p->pEntry into a sorted list if it is not
! 359: ** sorted already. If there is a binary tree on p->pTree, then
! 360: ** convert it into a list too and merge it into the p->pEntry list.
! 361: */
! 362: static void rowSetToList(RowSet *p){
! 363: if( !p->isSorted ){
! 364: rowSetSort(p);
! 365: }
! 366: if( p->pTree ){
! 367: struct RowSetEntry *pHead, *pTail;
! 368: rowSetTreeToList(p->pTree, &pHead, &pTail);
! 369: p->pTree = 0;
! 370: p->pEntry = rowSetMerge(p->pEntry, pHead);
! 371: }
! 372: }
! 373:
! 374: /*
! 375: ** Extract the smallest element from the RowSet.
! 376: ** Write the element into *pRowid. Return 1 on success. Return
! 377: ** 0 if the RowSet is already empty.
! 378: **
! 379: ** After this routine has been called, the sqlite3RowSetInsert()
! 380: ** routine may not be called again.
! 381: */
! 382: int sqlite3RowSetNext(RowSet *p, i64 *pRowid){
! 383: rowSetToList(p);
! 384: if( p->pEntry ){
! 385: *pRowid = p->pEntry->v;
! 386: p->pEntry = p->pEntry->pRight;
! 387: if( p->pEntry==0 ){
! 388: sqlite3RowSetClear(p);
! 389: }
! 390: return 1;
! 391: }else{
! 392: return 0;
! 393: }
! 394: }
! 395:
! 396: /*
! 397: ** Check to see if element iRowid was inserted into the the rowset as
! 398: ** part of any insert batch prior to iBatch. Return 1 or 0.
! 399: */
! 400: int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 iRowid){
! 401: struct RowSetEntry *p;
! 402: if( iBatch!=pRowSet->iBatch ){
! 403: if( pRowSet->pEntry ){
! 404: rowSetToList(pRowSet);
! 405: pRowSet->pTree = rowSetListToTree(pRowSet->pEntry);
! 406: pRowSet->pEntry = 0;
! 407: pRowSet->pLast = 0;
! 408: }
! 409: pRowSet->iBatch = iBatch;
! 410: }
! 411: p = pRowSet->pTree;
! 412: while( p ){
! 413: if( p->v<iRowid ){
! 414: p = p->pRight;
! 415: }else if( p->v>iRowid ){
! 416: p = p->pLeft;
! 417: }else{
! 418: return 1;
! 419: }
! 420: }
! 421: return 0;
! 422: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>