1 //-< RTREE.CPP >-----------------------------------------------------*--------*
2 // GigaBASE                  Version 1.0         (c) 1999  GARRET    *     ?  *
3 // (Post Relational Database Management System)                      *   /\|  *
4 //                                                                   *  /  \  *
5 //                          Created:     22-Nov-2001  K.A. Knizhnik  * / [] \ *
6 //                          Last update: 22-Nov-2001  K.A. Knizhnik  * GARRET *
7 //-------------------------------------------------------------------*--------*
8 // R-tree class implementation
9 //-------------------------------------------------------------------*--------*
10 
11 #define INSIDE_GIGABASE
12 
13 #include "gigabase.h"
14 #include "rtree.h"
15 
16 BEGIN_GIGABASE_NAMESPACE
17 
insert(dbDatabase * db,oid_t treeId,oid_t recordId,int offs)18 void dbRtree::insert(dbDatabase* db, oid_t treeId, oid_t recordId, int offs)
19 {
20     dbGetTie treeTie;
21     dbRtree* tree = (dbRtree*)db->getRow(treeTie, treeId);
22     dbGetTie tie;
23     byte* record = (byte*)db->getRow(tie, recordId);
24     rectangle& r = *(rectangle*)(record + offs);
25     if (tree->root == 0) {
26         dbPutTie tie;
27         dbRtree* t = (dbRtree*)db->putRow(tie, treeId);
28         t->root = dbRtreePage::allocate(db, recordId, r);
29         t->height = 1;
30     } else {
31         oid_t p = dbRtreePage::insert(db, r, tree->root, recordId, tree->height);
32         if (p != 0) {
33             dbPutTie tie;
34             dbRtree* t = (dbRtree*)db->putRow(tie, treeId);
35             // root splitted
36             t->root = dbRtreePage::allocate(db, tree->root, p);
37             t->height += 1;
38         }
39     }
40 }
41 
42 
insert(dbDatabase * db,oid_t treeId,oid_t recordId,rectangle const & r)43 void dbRtree::insert(dbDatabase* db, oid_t treeId, oid_t recordId, rectangle const& r)
44 {
45     dbGetTie treeTie;
46     dbRtree* tree = (dbRtree*)db->getRow(treeTie, treeId);
47     if (tree->root == 0) {
48         dbPutTie tie;
49         dbRtree* t = (dbRtree*)db->putRow(tie, treeId);
50         t->root = dbRtreePage::allocate(db, recordId, r);
51         t->height = 1;
52     } else {
53         oid_t p = dbRtreePage::insert(db, r, tree->root, recordId, tree->height);
54         if (p != 0) {
55             dbPutTie tie;
56             dbRtree* t = (dbRtree*)db->putRow(tie, treeId);
57             // root splitted
58             t->root = dbRtreePage::allocate(db, tree->root, p);
59             t->height += 1;
60         }
61     }
62 }
63 
64 
remove(dbDatabase * db,oid_t treeId,oid_t recordId,int offs)65 void dbRtree::remove(dbDatabase* db, oid_t treeId, oid_t recordId, int offs)
66 {
67     dbGetTie treeTie;
68     dbRtree* tree = (dbRtree*)db->getRow(treeTie, treeId);
69     assert(tree->height != 0);
70 
71     dbGetTie getTie;
72     byte* record = (byte*)db->getRow(getTie, recordId);
73     rectangle& r = *(rectangle*)(record + offs);
74 
75     dbRtreePage::reinsert_list rlist;
76     bool found = dbRtreePage::remove(db, r, tree->root, recordId, tree->height, rlist);
77     assert(found);
78 
79     dbPutTie putTie;
80     oid_t p = rlist.chain;
81     int level = rlist.level;
82     bool rootSplitted = false;
83 
84     while (p != 0) {
85         dbRtreePage* pg = (dbRtreePage*)db->get(p);
86         for (int i = 0, n = pg->n; i < n; i++) {
87             oid_t q = dbRtreePage::insert(db, pg->b[i].rect, tree->root,
88                                         pg->b[i].p, tree->height-level);
89             if (q != 0) {
90                 // root splitted
91                 oid_t oldRoot = tree->root;
92                 if (!rootSplitted) {
93                     tree = (dbRtree*)db->putRow(putTie, treeId);
94                     rootSplitted = true;
95                 }
96                 tree->root = dbRtreePage::allocate(db, oldRoot, q);
97                 tree->height += 1;
98             }
99         }
100         level -= 1;
101         oid_t next = pg->next_reinsert_page();
102         db->pool.unfix(pg);
103         db->freePage(p);
104         p = next;
105     }
106     dbRtreePage* pg = (dbRtreePage*)db->get(tree->root);
107     if (pg->n == 1 && tree->height > 1) {
108         oid_t newRoot = (tree->height > 1) ? pg->b[0].p : 0;
109         db->freePage(tree->root);
110         if (!rootSplitted) {
111             tree = (dbRtree*)db->putRow(putTie, treeId);
112         }
113         tree->root = newRoot;
114         tree->height -= 1;
115     }
116     db->pool.unfix(pg);
117 }
118 
find(dbDatabase * db,oid_t treeId,dbSearchContext & sc)119 bool dbRtree::find(dbDatabase* db, oid_t treeId, dbSearchContext& sc)
120 {
121     dbGetTie treeTie;
122     dbRtree* tree = (dbRtree*)db->getRow(treeTie, treeId);
123     if (tree->height > 0) {
124         return dbRtreePage::find(db, tree->root, sc, tree->height);
125     }
126     return true;
127 }
128 
purge(dbDatabase * db,oid_t treeId)129 void dbRtree::purge(dbDatabase* db, oid_t treeId)
130 {
131     dbPutTie treeTie;
132     dbRtree* tree = (dbRtree*)db->putRow(treeTie, treeId);
133     if (tree->height > 0) {
134         dbRtreePage::purge(db, tree->root, tree->height);
135     }
136     tree->root = 0;
137     tree->height = 0;
138 }
139 
drop(dbDatabase * db,oid_t treeId)140 void dbRtree::drop(dbDatabase* db, oid_t treeId)
141 {
142     purge(db, treeId);
143     db->free(db->getPos(treeId) & ~dbFlagsMask, sizeof(dbRtree));
144     db->freeId(treeId);
145 }
146 
allocate(dbDatabase * db)147 oid_t dbRtree::allocate(dbDatabase* db)
148 {
149     oid_t oid = db->allocateId();
150     offs_t pos = db->allocate(sizeof(dbRtree));
151     db->setPos(oid, pos | dbModifiedFlag);
152     dbPutTie tie;
153     tie.set(db->pool, oid, pos, sizeof(dbRtree));
154     dbRtree* tree = (dbRtree*)tie.get();
155     tree->size = sizeof(dbRtree);
156     tree->root = 0;
157     tree->height = 0;
158     return oid;
159 }
160 
161 //-------------------------------------------------------------------------
162 // R-tree page methods
163 //-------------------------------------------------------------------------
164 
165 //
166 // Search for objects overlapped with specified rectangle and call
167 // callback method for all such objects.
168 //
169 
find(dbDatabase * db,oid_t pageId,dbSearchContext & sc,int level)170 bool dbRtreePage::find(dbDatabase* db, oid_t pageId, dbSearchContext& sc, int level)
171 {
172     dbRtreePage* pg = (dbRtreePage*)db->get(pageId);
173     bool rc = pg->find(db, sc, level);
174     db->pool.unfix(pg);
175     return rc;
176 }
177 
178 static rectangle::comparator comparators[] =
179 {
180     &rectangle::operator ==,
181     &rectangle::operator &,
182     &rectangle::operator >,
183     &rectangle::operator >=,
184     &rectangle::operator <,
185     &rectangle::operator <=
186 };
187 
find(dbDatabase * db,dbSearchContext & sc,int level) const188 bool dbRtreePage::find(dbDatabase* db, dbSearchContext& sc, int level) const
189 {
190     assert(level >= 0);
191     rectangle& r = *(rectangle*)sc.firstKey;
192     sc.probes += 1;
193     if (--level != 0) { /* this is an internal node in the tree */
194         for (int i = 0; i < n; i++) {
195             if (b[i].rect & r) {
196                 if (!find(db, b[i].p, sc, level)) {
197                     return false;
198                 }
199             }
200         }
201     } else { /* this is a leaf node */
202         rectangle::comparator cmp = comparators[sc.firstKeyInclusion];
203         for (int i = 0; i < n; i++) {
204             if ((b[i].rect.*cmp)(r)) {
205                 if (sc.condition == NULL
206                     || db->evaluateBoolean(sc.condition, b[i].p, sc.cursor->table, sc.cursor))
207                 {
208                     if (!sc.cursor->add(b[i].p)) {
209                         return false;
210                     }
211                 }
212             }
213         }
214     }
215 //    printf("Level %d, nodes %d, intersects %d\n", level, n, nIntersects);
216     return true;
217 }
218 
219 //
220 // Create root page
221 //
allocate(dbDatabase * db,oid_t recordId,rectangle const & r)222 oid_t dbRtreePage::allocate(dbDatabase* db, oid_t recordId, rectangle const& r)
223 {
224     oid_t pageId = db->allocatePage();
225     dbRtreePage* pg = (dbRtreePage*)db->put(pageId);
226     pg->n = 1;
227     pg->b[0].rect = r;
228     pg->b[0].p = recordId;
229     db->pool.unfix(pg);
230     return pageId;
231 }
232 
233 //
234 // Create new root page (root splitting)
235 //
allocate(dbDatabase * db,oid_t oldRootId,oid_t newPageId)236 oid_t dbRtreePage::allocate(dbDatabase* db, oid_t oldRootId, oid_t newPageId)
237 {
238     oid_t pageId = db->allocatePage();
239     dbRtreePage* pg = (dbRtreePage*)db->put(pageId);
240     pg->n = 2;
241     cover(db, oldRootId, pg->b[0].rect);
242     pg->b[0].p = oldRootId;
243     cover(db, newPageId, pg->b[1].rect);
244     pg->b[1].p = newPageId;
245     db->pool.unfix(pg);
246     return pageId;
247 }
248 
249 //
250 // Calculate cover of all rectangles at page
251 //
cover(dbDatabase * db,oid_t pageId,rectangle & r)252 void dbRtreePage::cover(dbDatabase* db, oid_t pageId, rectangle& r)
253 {
254     dbRtreePage* pg = (dbRtreePage*)db->get(pageId);
255     pg->cover(r);
256     db->pool.unfix(pg);
257 }
258 
cover(rectangle & r) const259 void dbRtreePage::cover(rectangle& r) const
260 {
261     r = b[0].rect;
262     for (int i = 1; i < n; i++) {
263         r += b[i].rect;
264     }
265 }
266 
267 #define INFINITY (area_t)1000000000*1000000000
268 
split_page(dbDatabase * db,branch const & br)269 oid_t dbRtreePage::split_page(dbDatabase* db, branch const& br)
270 {
271     int i, j, seed[2];
272     area_t rect_area[card+1], waste, worst_waste = -INFINITY;
273     //
274     // As the seeds for the two groups, find two rectangles which waste
275     // the most area if covered by a single rectangle.
276     //
277     rect_area[0] = area(br.rect);
278     for (i = 0; i < card; i++) {
279         rect_area[i+1] = area(b[i].rect);
280     }
281     branch const* bp = &br;
282     for (i = 0; i < card; i++) {
283         for (j = i+1; j <= card; j++) {
284             waste = area(bp->rect + b[j-1].rect) - rect_area[i] - rect_area[j];
285             if (waste > worst_waste) {
286                 worst_waste = waste;
287                 seed[0] = i;
288                 seed[1] = j;
289             }
290         }
291         bp = &b[i];
292     }
293     char taken[card];
294     rectangle group[2];
295     area_t group_area[2];
296     int    group_card[2];
297     oid_t  pid;
298 
299     memset(taken, 0, sizeof taken);
300     taken[seed[1]-1] = 2;
301     group[1] = b[seed[1]-1].rect;
302 
303     if (seed[0] == 0) {
304         group[0] = br.rect;
305         pid = allocate(db, br.p, br.rect);
306     } else {
307         group[0] = b[seed[0]-1].rect;
308         pid = allocate(db, b[seed[0]-1].p, group[0]);
309         b[seed[0]-1] = br;
310     }
311     dbPutTie tie;
312     dbRtreePage* p = (dbRtreePage*)db->put(tie, pid);
313 
314     group_card[0] = group_card[1] = 1;
315     group_area[0] = rect_area[seed[0]];
316     group_area[1] = rect_area[seed[1]];
317     //
318     // Split remaining rectangles between two groups.
319     // The one chosen is the one with the greatest difference in area
320     // expansion depending on which group - the rect most strongly
321     // attracted to one group and repelled from the other.
322     //
323     while (group_card[0] + group_card[1] < card + 1
324            && group_card[0] < card + 1 - min_fill
325            && group_card[1] < card + 1 - min_fill)
326     {
327         int better_group = -1, chosen = -1;
328         area_t biggest_diff = -1;
329         for (i = 0; i < card; i++) {
330             if (!taken[i]) {
331                 area_t diff = (area(group[0] + b[i].rect) - group_area[0])
332                              - (area(group[1] + b[i].rect) - group_area[1]);
333                 if (diff > biggest_diff || -diff > biggest_diff) {
334                     chosen = i;
335                     if (diff < 0) {
336                         better_group = 0;
337                         biggest_diff = -diff;
338                     } else {
339                         better_group = 1;
340                         biggest_diff = diff;
341                     }
342                 }
343             }
344         }
345         assert(chosen >= 0);
346         group_card[better_group] += 1;
347         group[better_group] += b[chosen].rect;
348         group_area[better_group] = area(group[better_group]);
349         taken[chosen] = better_group+1;
350         if (better_group == 0) {
351             p->b[group_card[0]-1] = b[chosen];
352         }
353     }
354     //
355     // If one group gets too full, then remaining rectangle are
356     // split between two groups in such way to balance cards of two groups.
357     //
358     if (group_card[0] + group_card[1] < card + 1) {
359         for (i = 0; i < card; i++) {
360             if (!taken[i]) {
361                 if (group_card[0] >= group_card[1]) {
362                     taken[i] = 2;
363                     group_card[1] += 1;
364                 } else {
365                     taken[i] = 1;
366                     p->b[group_card[0]++] = b[i];
367                 }
368             }
369         }
370     }
371     p->n = group_card[0];
372     n = group_card[1];
373     for (i = 0, j = 0; i < n; j++) {
374         if (taken[j] == 2) {
375             b[i++] = b[j];
376         }
377     }
378     return pid;
379 }
380 
remove_branch(int i)381 void dbRtreePage::remove_branch(int i)
382 {
383     n -= 1;
384     memmove(&b[i], &b[i+1], (n-i)*sizeof(branch));
385 }
386 
insert(dbDatabase * db,rectangle const & r,oid_t pageId,oid_t recordId,int level)387 oid_t dbRtreePage::insert(dbDatabase* db, rectangle const& r, oid_t pageId, oid_t recordId, int level)
388 {
389     dbPutTie tie;
390     dbRtreePage* pg = (dbRtreePage*)db->put(tie, pageId);
391     oid_t oid = pg->insert(db, r, recordId, level);
392     return oid;
393 }
394 
insert(dbDatabase * db,rectangle const & r,oid_t recordId,int level)395 oid_t dbRtreePage::insert(dbDatabase* db, rectangle const& r, oid_t recordId, int level)
396 {
397     branch br;
398     if (--level != 0) {
399         // not leaf page
400         int i, mini = 0;
401         area_t min_incr = INFINITY;
402         area_t best_area = INFINITY;
403         for (i = 0; i < n; i++) {
404             area_t r_area = area(b[i].rect);
405             area_t incr = area(b[i].rect + r) - r_area;
406             if (incr < min_incr) {
407                 best_area = r_area;
408                 min_incr = incr;
409                 mini = i;
410             } else if (incr == min_incr && r_area < best_area) {
411                 best_area = r_area;
412                 mini = i;
413             }
414         }
415         oid_t q = insert(db, r, b[mini].p, recordId, level);
416         if (q == 0) {
417             // child was not split
418             b[mini].rect += r;
419             return 0;
420         } else {
421             // child was split
422             cover(db, b[mini].p, b[mini].rect);
423             br.p = q;
424             cover(db, q, br.rect);
425             return add_branch(db, br);
426         }
427     } else {
428         br.p = recordId;
429         br.rect = r;
430         return add_branch(db, br);
431     }
432 }
433 
remove(dbDatabase * db,rectangle const & r,oid_t pageId,oid_t recordId,int level,reinsert_list & rlist)434 bool dbRtreePage::remove(dbDatabase* db, rectangle const& r,  oid_t pageId, oid_t recordId,
435                          int level, reinsert_list& rlist)
436 {
437     dbPutTie tie;
438     dbRtreePage* pg = (dbRtreePage*)db->put(tie, pageId);
439     bool rc = pg->remove(db, r, recordId, level, rlist);
440     return rc;
441 }
442 
remove(dbDatabase * db,rectangle const & r,oid_t recordId,int level,reinsert_list & rlist)443 bool dbRtreePage::remove(dbDatabase* db, rectangle const& r, oid_t recordId, int level,
444                          reinsert_list& rlist)
445 {
446     if (--level != 0) {
447         for (int i = 0; i < n; i++) {
448             if (b[i].rect & r) {
449                 if (remove(db, r, b[i].p, recordId, level, rlist)) {
450                     dbRtreePage* p = (dbRtreePage*)db->get(b[i].p);
451                     if (p->n >= min_fill) {
452                         p->cover(b[i].rect);
453                         db->pool.unfix(p);
454                     } else {
455                         dbPutTie tie;
456                         // not enough entries in child
457                         db->pool.unfix(p);
458                         p = (dbRtreePage*)db->put(tie, b[i].p);
459                         p->b[card-1].p = rlist.chain;
460                         rlist.chain = b[i].p;
461                         rlist.level = level - 1;
462                         remove_branch(i);
463                     }
464                     return true;
465                 }
466             }
467         }
468     } else {
469         for (int i = 0; i < n; i++) {
470             if (b[i].p == recordId) {
471                 remove_branch(i);
472                 return true;
473             }
474         }
475     }
476     return false;
477 }
478 
purge(dbDatabase * db,oid_t pageId,int level)479 void dbRtreePage::purge(dbDatabase* db, oid_t pageId, int level)
480 {
481     if (--level != 0) { /* this is an internal node in the tree */
482         dbRtreePage* pg = (dbRtreePage*)db->get(pageId);
483         for (int i = 0; i < pg->n; i++) {
484             purge(db, pg->b[i].p, level);
485         }
486         db->pool.unfix(pg);
487     }
488     db->freePage(pageId);
489 }
490 
init(dbDatabase * db,oid_t treeId,dbSearchContext & sc)491 void dbRtreeIterator::init(dbDatabase* db, oid_t treeId, dbSearchContext& sc)
492 {
493     this->db = db;
494     this->sc = sc;
495     this->treeId = treeId;
496 }
497 
first()498 oid_t dbRtreeIterator::first()
499 {
500     dbGetTie tie;
501     dbRtree* tree = (dbRtree*)db->getRow(tie, treeId);
502     height = tree->height;
503 
504     if (height == 0) {
505         return 0;
506     }
507     return gotoFirstItem(0, tree->root);
508 }
509 
last()510 oid_t dbRtreeIterator::last()
511 {
512     dbGetTie tie;
513     dbRtree* tree = (dbRtree*)db->getRow(tie, treeId);
514     height = tree->height;
515 
516     if (height == 0) {
517         return 0;
518     }
519     return gotoLastItem(0, tree->root);
520 }
521 
522 
next()523 oid_t dbRtreeIterator::next()
524 {
525     rectangle& r = *(rectangle*)sc.firstKey;
526     int sp = height;
527     while (--sp >= 0) {
528         dbRtreePage* pg = (dbRtreePage*)db->get(pageStack[sp]);
529         for (int i = posStack[sp], n = pg->n; ++i < n;) {
530             if (r & pg->b[i].rect) {
531                 oid_t curr = pg->b[i].p;
532                 if (sp+1 == height) {
533                     if (sc.condition != NULL && !db->evaluateBoolean(sc.condition, curr, sc.cursor->table, sc.cursor)) {
534                         continue;
535                     }
536                 } else if ((curr = gotoFirstItem(sp+1, curr)) == 0) {
537                     continue;
538                 }
539                 pageStack[sp] = pageStack[sp];
540                 posStack[sp] = i;
541                 db->pool.unfix(pg);
542                 return curr;
543             }
544         }
545         db->pool.unfix(pg);
546     }
547     return 0;
548 }
549 
prev()550 oid_t dbRtreeIterator::prev()
551 {
552     rectangle& r = *(rectangle*)sc.firstKey;
553     int sp = height;
554     while (--sp >= 0) {
555         dbRtreePage* pg = (dbRtreePage*)db->get(pageStack[sp]);
556         for (int i = posStack[sp]; --i >= 0;) {
557             if (r & pg->b[i].rect) {
558                 oid_t curr = pg->b[i].p;
559                 if (sp+1 == height) {
560                     if (sc.condition != NULL && !db->evaluateBoolean(sc.condition, curr, sc.cursor->table, sc.cursor)) {
561                         continue;
562                     }
563                 } else if ((curr = gotoLastItem(sp+1, curr)) == 0) {
564                     continue;
565                 }
566                 pageStack[sp] = pageStack[sp];
567                 posStack[sp] = i;
568                 db->pool.unfix(pg);
569                 return curr;
570             }
571         }
572         db->pool.unfix(pg);
573     }
574     return 0;
575 }
576 
gotoFirstItem(int sp,oid_t pageId)577 oid_t dbRtreeIterator::gotoFirstItem(int sp, oid_t pageId)
578 {
579     dbRtreePage* pg = (dbRtreePage*)db->get(pageId);
580     rectangle& r = *(rectangle*)sc.firstKey;
581 
582     for (int i = 0, n = pg->n; i < n; i++) {
583         if (r & pg->b[i].rect) {
584             oid_t curr = pg->b[i].p;
585             if (sp+1 == height) {
586                 if (sc.condition != NULL && !db->evaluateBoolean(sc.condition, curr, sc.cursor->table, sc.cursor)) {
587                     continue;
588                 }
589             } else if ((curr = gotoFirstItem(sp+1, curr)) == 0) {
590                 continue;
591             }
592             pageStack[sp] = pageId;
593             posStack[sp] = i;
594             db->pool.unfix(pg);
595             return curr;
596         }
597     }
598     db->pool.unfix(pg);
599     return 0;
600 }
601 
gotoLastItem(int sp,oid_t pageId)602 oid_t dbRtreeIterator::gotoLastItem(int sp, oid_t pageId)
603 {
604     dbRtreePage* pg = (dbRtreePage*)db->get(pageId);
605     rectangle& r = *(rectangle*)sc.firstKey;
606 
607     for (int i = pg->n; --i >= 0;) {
608         if (r & pg->b[i].rect) {
609             oid_t curr = pg->b[i].p;
610             if (sp+1 == height) {
611                 if (sc.condition != NULL && !db->evaluateBoolean(sc.condition, curr, sc.cursor->table, sc.cursor)) {
612                     continue;
613                 }
614             } else if ((curr = gotoLastItem(sp+1, curr)) == 0) {
615                 continue;
616             }
617             pageStack[sp] = pageId;
618             posStack[sp] = i;
619             db->pool.unfix(pg);
620             return curr;
621         }
622     }
623     db->pool.unfix(pg);
624     return 0;
625 }
626 
627 
628 
629 END_GIGABASE_NAMESPACE
630