OLD | NEW |
1 /* | 1 /* |
2 ** 2008 December 3 | 2 ** 2008 December 3 |
3 ** | 3 ** |
4 ** The author disclaims copyright to this source code. In place of | 4 ** The author disclaims copyright to this source code. In place of |
5 ** a legal notice, here is a blessing: | 5 ** a legal notice, here is a blessing: |
6 ** | 6 ** |
7 ** May you do good and not evil. | 7 ** May you do good and not evil. |
8 ** May you find forgiveness for yourself and forgive others. | 8 ** May you find forgiveness for yourself and forgive others. |
9 ** May you share freely, never taking more than you give. | 9 ** May you share freely, never taking more than you give. |
10 ** | 10 ** |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
43 ** will only see elements that were inserted before the last change | 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 | 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 | 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. | 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 | 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. | 48 ** a non-zero batch number, it will see all prior INSERTs. |
49 ** | 49 ** |
50 ** No INSERTs may occurs after a SMALLEST. An assertion will fail if | 50 ** No INSERTs may occurs after a SMALLEST. An assertion will fail if |
51 ** that is attempted. | 51 ** that is attempted. |
52 ** | 52 ** |
53 ** The cost of an INSERT is roughly constant. (Sometime new memory | 53 ** The cost of an INSERT is roughly constant. (Sometimes new memory |
54 ** has to be allocated on an INSERT.) The cost of a TEST with a new | 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. | 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 | 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 | 57 ** of the first SMALLEST is O(NlogN). Second and subsequent SMALLEST |
58 ** primitives are constant time. The cost of DESTROY is O(N). | 58 ** primitives are constant time. The cost of DESTROY is O(N). |
59 ** | 59 ** |
60 ** There is an added cost of O(N) when switching between TEST and | 60 ** There is an added cost of O(N) when switching between TEST and |
61 ** SMALLEST primitives. | 61 ** SMALLEST primitives. |
62 */ | 62 */ |
63 #include "sqliteInt.h" | 63 #include "sqliteInt.h" |
64 | 64 |
65 | 65 |
66 /* | 66 /* |
67 ** Target size for allocation chunks. | 67 ** Target size for allocation chunks. |
68 */ | 68 */ |
69 #define ROWSET_ALLOCATION_SIZE 1024 | 69 #define ROWSET_ALLOCATION_SIZE 1024 |
70 | 70 |
71 /* | 71 /* |
72 ** The number of rowset entries per allocation chunk. | 72 ** The number of rowset entries per allocation chunk. |
73 */ | 73 */ |
74 #define ROWSET_ENTRY_PER_CHUNK \ | 74 #define ROWSET_ENTRY_PER_CHUNK \ |
75 ((ROWSET_ALLOCATION_SIZE-8)/sizeof(struct RowSetEntry)) | 75 ((ROWSET_ALLOCATION_SIZE-8)/sizeof(struct RowSetEntry)) |
76 | 76 |
77 /* | 77 /* |
78 ** Each entry in a RowSet is an instance of the following object. | 78 ** Each entry in a RowSet is an instance of the following object. |
| 79 ** |
| 80 ** This same object is reused to store a linked list of trees of RowSetEntry |
| 81 ** objects. In that alternative use, pRight points to the next entry |
| 82 ** in the list, pLeft points to the tree, and v is unused. The |
| 83 ** RowSet.pForest value points to the head of this forest list. |
79 */ | 84 */ |
80 struct RowSetEntry { | 85 struct RowSetEntry { |
81 i64 v; /* ROWID value for this entry */ | 86 i64 v; /* ROWID value for this entry */ |
82 struct RowSetEntry *pRight; /* Right subtree (larger entries) or list */ | 87 struct RowSetEntry *pRight; /* Right subtree (larger entries) or list */ |
83 struct RowSetEntry *pLeft; /* Left subtree (smaller entries) */ | 88 struct RowSetEntry *pLeft; /* Left subtree (smaller entries) */ |
84 }; | 89 }; |
85 | 90 |
86 /* | 91 /* |
87 ** RowSetEntry objects are allocated in large chunks (instances of the | 92 ** RowSetEntry objects are allocated in large chunks (instances of the |
88 ** following structure) to reduce memory allocation overhead. The | 93 ** following structure) to reduce memory allocation overhead. The |
89 ** chunks are kept on a linked list so that they can be deallocated | 94 ** chunks are kept on a linked list so that they can be deallocated |
90 ** when the RowSet is destroyed. | 95 ** when the RowSet is destroyed. |
91 */ | 96 */ |
92 struct RowSetChunk { | 97 struct RowSetChunk { |
93 struct RowSetChunk *pNextChunk; /* Next chunk on list of them all */ | 98 struct RowSetChunk *pNextChunk; /* Next chunk on list of them all */ |
94 struct RowSetEntry aEntry[ROWSET_ENTRY_PER_CHUNK]; /* Allocated entries */ | 99 struct RowSetEntry aEntry[ROWSET_ENTRY_PER_CHUNK]; /* Allocated entries */ |
95 }; | 100 }; |
96 | 101 |
97 /* | 102 /* |
98 ** A RowSet in an instance of the following structure. | 103 ** A RowSet in an instance of the following structure. |
99 ** | 104 ** |
100 ** A typedef of this structure if found in sqliteInt.h. | 105 ** A typedef of this structure if found in sqliteInt.h. |
101 */ | 106 */ |
102 struct RowSet { | 107 struct RowSet { |
103 struct RowSetChunk *pChunk; /* List of all chunk allocations */ | 108 struct RowSetChunk *pChunk; /* List of all chunk allocations */ |
104 sqlite3 *db; /* The database connection */ | 109 sqlite3 *db; /* The database connection */ |
105 struct RowSetEntry *pEntry; /* List of entries using pRight */ | 110 struct RowSetEntry *pEntry; /* List of entries using pRight */ |
106 struct RowSetEntry *pLast; /* Last entry on the pEntry list */ | 111 struct RowSetEntry *pLast; /* Last entry on the pEntry list */ |
107 struct RowSetEntry *pFresh; /* Source of new entry objects */ | 112 struct RowSetEntry *pFresh; /* Source of new entry objects */ |
108 struct RowSetEntry *pTree; /* Binary tree of entries */ | 113 struct RowSetEntry *pForest; /* List of binary trees of entries */ |
109 u16 nFresh; /* Number of objects on pFresh */ | 114 u16 nFresh; /* Number of objects on pFresh */ |
110 u8 isSorted; /* True if pEntry is sorted */ | 115 u16 rsFlags; /* Various flags */ |
111 u8 iBatch; /* Current insert batch */ | 116 int iBatch; /* Current insert batch */ |
112 }; | 117 }; |
113 | 118 |
114 /* | 119 /* |
| 120 ** Allowed values for RowSet.rsFlags |
| 121 */ |
| 122 #define ROWSET_SORTED 0x01 /* True if RowSet.pEntry is sorted */ |
| 123 #define ROWSET_NEXT 0x02 /* True if sqlite3RowSetNext() has been called */ |
| 124 |
| 125 /* |
115 ** Turn bulk memory into a RowSet object. N bytes of memory | 126 ** 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 | 127 ** are available at pSpace. The db pointer is used as a memory context |
117 ** for any subsequent allocations that need to occur. | 128 ** for any subsequent allocations that need to occur. |
118 ** Return a pointer to the new RowSet object. | 129 ** Return a pointer to the new RowSet object. |
119 ** | 130 ** |
120 ** It must be the case that N is sufficient to make a Rowset. If not | 131 ** It must be the case that N is sufficient to make a Rowset. If not |
121 ** an assertion fault occurs. | 132 ** an assertion fault occurs. |
122 ** | 133 ** |
123 ** If N is larger than the minimum, use the surplus as an initial | 134 ** If N is larger than the minimum, use the surplus as an initial |
124 ** allocation of entries available to be filled. | 135 ** allocation of entries available to be filled. |
125 */ | 136 */ |
126 RowSet *sqlite3RowSetInit(sqlite3 *db, void *pSpace, unsigned int N){ | 137 RowSet *sqlite3RowSetInit(sqlite3 *db, void *pSpace, unsigned int N){ |
127 RowSet *p; | 138 RowSet *p; |
128 assert( N >= ROUND8(sizeof(*p)) ); | 139 assert( N >= ROUND8(sizeof(*p)) ); |
129 p = pSpace; | 140 p = pSpace; |
130 p->pChunk = 0; | 141 p->pChunk = 0; |
131 p->db = db; | 142 p->db = db; |
132 p->pEntry = 0; | 143 p->pEntry = 0; |
133 p->pLast = 0; | 144 p->pLast = 0; |
134 p->pTree = 0; | 145 p->pForest = 0; |
135 p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p); | 146 p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p); |
136 p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry)); | 147 p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry)); |
137 p->isSorted = 1; | 148 p->rsFlags = ROWSET_SORTED; |
138 p->iBatch = 0; | 149 p->iBatch = 0; |
139 return p; | 150 return p; |
140 } | 151 } |
141 | 152 |
142 /* | 153 /* |
143 ** Deallocate all chunks from a RowSet. This frees all memory that | 154 ** Deallocate all chunks from a RowSet. This frees all memory that |
144 ** the RowSet has allocated over its lifetime. This routine is | 155 ** the RowSet has allocated over its lifetime. This routine is |
145 ** the destructor for the RowSet. | 156 ** the destructor for the RowSet. |
146 */ | 157 */ |
147 void sqlite3RowSetClear(RowSet *p){ | 158 void sqlite3RowSetClear(RowSet *p){ |
148 struct RowSetChunk *pChunk, *pNextChunk; | 159 struct RowSetChunk *pChunk, *pNextChunk; |
149 for(pChunk=p->pChunk; pChunk; pChunk = pNextChunk){ | 160 for(pChunk=p->pChunk; pChunk; pChunk = pNextChunk){ |
150 pNextChunk = pChunk->pNextChunk; | 161 pNextChunk = pChunk->pNextChunk; |
151 sqlite3DbFree(p->db, pChunk); | 162 sqlite3DbFree(p->db, pChunk); |
152 } | 163 } |
153 p->pChunk = 0; | 164 p->pChunk = 0; |
154 p->nFresh = 0; | 165 p->nFresh = 0; |
155 p->pEntry = 0; | 166 p->pEntry = 0; |
156 p->pLast = 0; | 167 p->pLast = 0; |
157 p->pTree = 0; | 168 p->pForest = 0; |
158 p->isSorted = 1; | 169 p->rsFlags = ROWSET_SORTED; |
| 170 } |
| 171 |
| 172 /* |
| 173 ** Allocate a new RowSetEntry object that is associated with the |
| 174 ** given RowSet. Return a pointer to the new and completely uninitialized |
| 175 ** objected. |
| 176 ** |
| 177 ** In an OOM situation, the RowSet.db->mallocFailed flag is set and this |
| 178 ** routine returns NULL. |
| 179 */ |
| 180 static struct RowSetEntry *rowSetEntryAlloc(RowSet *p){ |
| 181 assert( p!=0 ); |
| 182 if( p->nFresh==0 ){ |
| 183 struct RowSetChunk *pNew; |
| 184 pNew = sqlite3DbMallocRaw(p->db, sizeof(*pNew)); |
| 185 if( pNew==0 ){ |
| 186 return 0; |
| 187 } |
| 188 pNew->pNextChunk = p->pChunk; |
| 189 p->pChunk = pNew; |
| 190 p->pFresh = pNew->aEntry; |
| 191 p->nFresh = ROWSET_ENTRY_PER_CHUNK; |
| 192 } |
| 193 p->nFresh--; |
| 194 return p->pFresh++; |
159 } | 195 } |
160 | 196 |
161 /* | 197 /* |
162 ** Insert a new value into a RowSet. | 198 ** Insert a new value into a RowSet. |
163 ** | 199 ** |
164 ** The mallocFailed flag of the database connection is set if a | 200 ** The mallocFailed flag of the database connection is set if a |
165 ** memory allocation fails. | 201 ** memory allocation fails. |
166 */ | 202 */ |
167 void sqlite3RowSetInsert(RowSet *p, i64 rowid){ | 203 void sqlite3RowSetInsert(RowSet *p, i64 rowid){ |
168 struct RowSetEntry *pEntry; /* The new entry */ | 204 struct RowSetEntry *pEntry; /* The new entry */ |
169 struct RowSetEntry *pLast; /* The last prior entry */ | 205 struct RowSetEntry *pLast; /* The last prior entry */ |
170 assert( p!=0 ); | 206 |
171 if( p->nFresh==0 ){ | 207 /* This routine is never called after sqlite3RowSetNext() */ |
172 struct RowSetChunk *pNew; | 208 assert( p!=0 && (p->rsFlags & ROWSET_NEXT)==0 ); |
173 pNew = sqlite3DbMallocRaw(p->db, sizeof(*pNew)); | 209 |
174 if( pNew==0 ){ | 210 pEntry = rowSetEntryAlloc(p); |
175 return; | 211 if( pEntry==0 ) 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; | 212 pEntry->v = rowid; |
185 pEntry->pRight = 0; | 213 pEntry->pRight = 0; |
186 pLast = p->pLast; | 214 pLast = p->pLast; |
187 if( pLast ){ | 215 if( pLast ){ |
188 if( p->isSorted && rowid<=pLast->v ){ | 216 if( (p->rsFlags & ROWSET_SORTED)!=0 && rowid<=pLast->v ){ |
189 p->isSorted = 0; | 217 p->rsFlags &= ~ROWSET_SORTED; |
190 } | 218 } |
191 pLast->pRight = pEntry; | 219 pLast->pRight = pEntry; |
192 }else{ | 220 }else{ |
193 assert( p->pEntry==0 ); /* Fires if INSERT after SMALLEST */ | |
194 p->pEntry = pEntry; | 221 p->pEntry = pEntry; |
195 } | 222 } |
196 p->pLast = pEntry; | 223 p->pLast = pEntry; |
197 } | 224 } |
198 | 225 |
199 /* | 226 /* |
200 ** Merge two lists of RowSetEntry objects. Remove duplicates. | 227 ** Merge two lists of RowSetEntry objects. Remove duplicates. |
201 ** | 228 ** |
202 ** The input lists are connected via pRight pointers and are | 229 ** The input lists are connected via pRight pointers and are |
203 ** assumed to each already be in sorted order. | 230 ** assumed to each already be in sorted order. |
204 */ | 231 */ |
205 static struct RowSetEntry *rowSetMerge( | 232 static struct RowSetEntry *rowSetEntryMerge( |
206 struct RowSetEntry *pA, /* First sorted list to be merged */ | 233 struct RowSetEntry *pA, /* First sorted list to be merged */ |
207 struct RowSetEntry *pB /* Second sorted list to be merged */ | 234 struct RowSetEntry *pB /* Second sorted list to be merged */ |
208 ){ | 235 ){ |
209 struct RowSetEntry head; | 236 struct RowSetEntry head; |
210 struct RowSetEntry *pTail; | 237 struct RowSetEntry *pTail; |
211 | 238 |
212 pTail = &head; | 239 pTail = &head; |
213 while( pA && pB ){ | 240 while( pA && pB ){ |
214 assert( pA->pRight==0 || pA->v<=pA->pRight->v ); | 241 assert( pA->pRight==0 || pA->v<=pA->pRight->v ); |
215 assert( pB->pRight==0 || pB->v<=pB->pRight->v ); | 242 assert( pB->pRight==0 || pB->v<=pB->pRight->v ); |
(...skipping 13 matching lines...) Expand all Loading... |
229 assert( pA->pRight==0 || pA->v<=pA->pRight->v ); | 256 assert( pA->pRight==0 || pA->v<=pA->pRight->v ); |
230 pTail->pRight = pA; | 257 pTail->pRight = pA; |
231 }else{ | 258 }else{ |
232 assert( pB==0 || pB->pRight==0 || pB->v<=pB->pRight->v ); | 259 assert( pB==0 || pB->pRight==0 || pB->v<=pB->pRight->v ); |
233 pTail->pRight = pB; | 260 pTail->pRight = pB; |
234 } | 261 } |
235 return head.pRight; | 262 return head.pRight; |
236 } | 263 } |
237 | 264 |
238 /* | 265 /* |
239 ** Sort all elements on the pEntry list of the RowSet into ascending order. | 266 ** Sort all elements on the list of RowSetEntry objects into order of |
| 267 ** increasing v. |
240 */ | 268 */ |
241 static void rowSetSort(RowSet *p){ | 269 static struct RowSetEntry *rowSetEntrySort(struct RowSetEntry *pIn){ |
242 unsigned int i; | 270 unsigned int i; |
243 struct RowSetEntry *pEntry; | 271 struct RowSetEntry *pNext, *aBucket[40]; |
244 struct RowSetEntry *aBucket[40]; | |
245 | 272 |
246 assert( p->isSorted==0 ); | |
247 memset(aBucket, 0, sizeof(aBucket)); | 273 memset(aBucket, 0, sizeof(aBucket)); |
248 while( p->pEntry ){ | 274 while( pIn ){ |
249 pEntry = p->pEntry; | 275 pNext = pIn->pRight; |
250 p->pEntry = pEntry->pRight; | 276 pIn->pRight = 0; |
251 pEntry->pRight = 0; | |
252 for(i=0; aBucket[i]; i++){ | 277 for(i=0; aBucket[i]; i++){ |
253 pEntry = rowSetMerge(aBucket[i], pEntry); | 278 pIn = rowSetEntryMerge(aBucket[i], pIn); |
254 aBucket[i] = 0; | 279 aBucket[i] = 0; |
255 } | 280 } |
256 aBucket[i] = pEntry; | 281 aBucket[i] = pIn; |
| 282 pIn = pNext; |
257 } | 283 } |
258 pEntry = 0; | 284 pIn = 0; |
259 for(i=0; i<sizeof(aBucket)/sizeof(aBucket[0]); i++){ | 285 for(i=0; i<sizeof(aBucket)/sizeof(aBucket[0]); i++){ |
260 pEntry = rowSetMerge(pEntry, aBucket[i]); | 286 pIn = rowSetEntryMerge(pIn, aBucket[i]); |
261 } | 287 } |
262 p->pEntry = pEntry; | 288 return pIn; |
263 p->pLast = 0; | |
264 p->isSorted = 1; | |
265 } | 289 } |
266 | 290 |
267 | 291 |
268 /* | 292 /* |
269 ** The input, pIn, is a binary tree (or subtree) of RowSetEntry objects. | 293 ** 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 | 294 ** 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. | 295 ** and return pointers to the first and last elements of the new list. |
272 */ | 296 */ |
273 static void rowSetTreeToList( | 297 static void rowSetTreeToList( |
274 struct RowSetEntry *pIn, /* Root of the input tree */ | 298 struct RowSetEntry *pIn, /* Root of the input tree */ |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
348 pLeft = p; | 372 pLeft = p; |
349 p = pList; | 373 p = pList; |
350 pList = p->pRight; | 374 pList = p->pRight; |
351 p->pLeft = pLeft; | 375 p->pLeft = pLeft; |
352 p->pRight = rowSetNDeepTree(&pList, iDepth); | 376 p->pRight = rowSetNDeepTree(&pList, iDepth); |
353 } | 377 } |
354 return p; | 378 return p; |
355 } | 379 } |
356 | 380 |
357 /* | 381 /* |
358 ** Convert the list in p->pEntry into a sorted list if it is not | 382 ** Take all the entries on p->pEntry and on the trees in p->pForest and |
359 ** sorted already. If there is a binary tree on p->pTree, then | 383 ** sort them all together into one big ordered list on p->pEntry. |
360 ** convert it into a list too and merge it into the p->pEntry list. | 384 ** |
| 385 ** This routine should only be called once in the life of a RowSet. |
361 */ | 386 */ |
362 static void rowSetToList(RowSet *p){ | 387 static void rowSetToList(RowSet *p){ |
363 if( !p->isSorted ){ | 388 |
364 rowSetSort(p); | 389 /* This routine is called only once */ |
| 390 assert( p!=0 && (p->rsFlags & ROWSET_NEXT)==0 ); |
| 391 |
| 392 if( (p->rsFlags & ROWSET_SORTED)==0 ){ |
| 393 p->pEntry = rowSetEntrySort(p->pEntry); |
365 } | 394 } |
366 if( p->pTree ){ | 395 |
367 struct RowSetEntry *pHead, *pTail; | 396 /* While this module could theoretically support it, sqlite3RowSetNext() |
368 rowSetTreeToList(p->pTree, &pHead, &pTail); | 397 ** is never called after sqlite3RowSetText() for the same RowSet. So |
369 p->pTree = 0; | 398 ** there is never a forest to deal with. Should this change, simply |
370 p->pEntry = rowSetMerge(p->pEntry, pHead); | 399 ** remove the assert() and the #if 0. */ |
| 400 assert( p->pForest==0 ); |
| 401 #if 0 |
| 402 while( p->pForest ){ |
| 403 struct RowSetEntry *pTree = p->pForest->pLeft; |
| 404 if( pTree ){ |
| 405 struct RowSetEntry *pHead, *pTail; |
| 406 rowSetTreeToList(pTree, &pHead, &pTail); |
| 407 p->pEntry = rowSetEntryMerge(p->pEntry, pHead); |
| 408 } |
| 409 p->pForest = p->pForest->pRight; |
371 } | 410 } |
| 411 #endif |
| 412 p->rsFlags |= ROWSET_NEXT; /* Verify this routine is never called again */ |
372 } | 413 } |
373 | 414 |
374 /* | 415 /* |
375 ** Extract the smallest element from the RowSet. | 416 ** Extract the smallest element from the RowSet. |
376 ** Write the element into *pRowid. Return 1 on success. Return | 417 ** Write the element into *pRowid. Return 1 on success. Return |
377 ** 0 if the RowSet is already empty. | 418 ** 0 if the RowSet is already empty. |
378 ** | 419 ** |
379 ** After this routine has been called, the sqlite3RowSetInsert() | 420 ** After this routine has been called, the sqlite3RowSetInsert() |
380 ** routine may not be called again. | 421 ** routine may not be called again. |
381 */ | 422 */ |
382 int sqlite3RowSetNext(RowSet *p, i64 *pRowid){ | 423 int sqlite3RowSetNext(RowSet *p, i64 *pRowid){ |
383 rowSetToList(p); | 424 assert( p!=0 ); |
| 425 |
| 426 /* Merge the forest into a single sorted list on first call */ |
| 427 if( (p->rsFlags & ROWSET_NEXT)==0 ) rowSetToList(p); |
| 428 |
| 429 /* Return the next entry on the list */ |
384 if( p->pEntry ){ | 430 if( p->pEntry ){ |
385 *pRowid = p->pEntry->v; | 431 *pRowid = p->pEntry->v; |
386 p->pEntry = p->pEntry->pRight; | 432 p->pEntry = p->pEntry->pRight; |
387 if( p->pEntry==0 ){ | 433 if( p->pEntry==0 ){ |
388 sqlite3RowSetClear(p); | 434 sqlite3RowSetClear(p); |
389 } | 435 } |
390 return 1; | 436 return 1; |
391 }else{ | 437 }else{ |
392 return 0; | 438 return 0; |
393 } | 439 } |
394 } | 440 } |
395 | 441 |
396 /* | 442 /* |
397 ** Check to see if element iRowid was inserted into the the rowset as | 443 ** Check to see if element iRowid was inserted into the rowset as |
398 ** part of any insert batch prior to iBatch. Return 1 or 0. | 444 ** part of any insert batch prior to iBatch. Return 1 or 0. |
| 445 ** |
| 446 ** If this is the first test of a new batch and if there exist entries |
| 447 ** on pRowSet->pEntry, then sort those entries into the forest at |
| 448 ** pRowSet->pForest so that they can be tested. |
399 */ | 449 */ |
400 int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 iRowid){ | 450 int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64 iRowid){ |
401 struct RowSetEntry *p; | 451 struct RowSetEntry *p, *pTree; |
| 452 |
| 453 /* This routine is never called after sqlite3RowSetNext() */ |
| 454 assert( pRowSet!=0 && (pRowSet->rsFlags & ROWSET_NEXT)==0 ); |
| 455 |
| 456 /* Sort entries into the forest on the first test of a new batch |
| 457 */ |
402 if( iBatch!=pRowSet->iBatch ){ | 458 if( iBatch!=pRowSet->iBatch ){ |
403 if( pRowSet->pEntry ){ | 459 p = pRowSet->pEntry; |
404 rowSetToList(pRowSet); | 460 if( p ){ |
405 pRowSet->pTree = rowSetListToTree(pRowSet->pEntry); | 461 struct RowSetEntry **ppPrevTree = &pRowSet->pForest; |
| 462 if( (pRowSet->rsFlags & ROWSET_SORTED)==0 ){ |
| 463 p = rowSetEntrySort(p); |
| 464 } |
| 465 for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){ |
| 466 ppPrevTree = &pTree->pRight; |
| 467 if( pTree->pLeft==0 ){ |
| 468 pTree->pLeft = rowSetListToTree(p); |
| 469 break; |
| 470 }else{ |
| 471 struct RowSetEntry *pAux, *pTail; |
| 472 rowSetTreeToList(pTree->pLeft, &pAux, &pTail); |
| 473 pTree->pLeft = 0; |
| 474 p = rowSetEntryMerge(pAux, p); |
| 475 } |
| 476 } |
| 477 if( pTree==0 ){ |
| 478 *ppPrevTree = pTree = rowSetEntryAlloc(pRowSet); |
| 479 if( pTree ){ |
| 480 pTree->v = 0; |
| 481 pTree->pRight = 0; |
| 482 pTree->pLeft = rowSetListToTree(p); |
| 483 } |
| 484 } |
406 pRowSet->pEntry = 0; | 485 pRowSet->pEntry = 0; |
407 pRowSet->pLast = 0; | 486 pRowSet->pLast = 0; |
| 487 pRowSet->rsFlags |= ROWSET_SORTED; |
408 } | 488 } |
409 pRowSet->iBatch = iBatch; | 489 pRowSet->iBatch = iBatch; |
410 } | 490 } |
411 p = pRowSet->pTree; | 491 |
412 while( p ){ | 492 /* Test to see if the iRowid value appears anywhere in the forest. |
413 if( p->v<iRowid ){ | 493 ** Return 1 if it does and 0 if not. |
414 p = p->pRight; | 494 */ |
415 }else if( p->v>iRowid ){ | 495 for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){ |
416 p = p->pLeft; | 496 p = pTree->pLeft; |
417 }else{ | 497 while( p ){ |
418 return 1; | 498 if( p->v<iRowid ){ |
| 499 p = p->pRight; |
| 500 }else if( p->v>iRowid ){ |
| 501 p = p->pLeft; |
| 502 }else{ |
| 503 return 1; |
| 504 } |
419 } | 505 } |
420 } | 506 } |
421 return 0; | 507 return 0; |
422 } | 508 } |
OLD | NEW |