LCOV - code coverage report
Current view: top level - gdk - gdk_rtree.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 6 6 100.0 %
Date: 2024-04-25 20:03:45 Functions: 2 2 100.0 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: MPL-2.0
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       7             :  *
       8             :  * Copyright 2024 MonetDB Foundation;
       9             :  * Copyright August 2008 - 2023 MonetDB B.V.;
      10             :  * Copyright 1997 - July 2008 CWI.
      11             :  */
      12             : 
      13             : #include "monetdb_config.h"
      14             : #include "gdk.h"
      15             : #include "gdk_private.h"
      16             : 
      17             : //TODO Check if we need to input RTREEdestroy into drop_index func in sql_cat.c
      18             : //TODO Should you check if the parent BAT is null?
      19             : 
      20             : //TODO Re-check the conditions
      21             : /* Conditions to persist the RTree:
      22             :  * - BAT has to be persistent
      23             :  * - No deleted rows (when does batInserted update?)
      24             :  * - The heap is not dirty -> no new values
      25             :  * - DB Farm is persistent i.e. not in memory
      26             :  */
      27             : #ifdef HAVE_RTREE
      28             : static bool
      29             : RTREEpersistcheck (BAT *b) {
      30             :         return ((BBP_status(b->batCacheid) & BBPEXISTING)
      31             :                 && b->batInserted == b->batCount
      32             :                 && !b->theap->dirty
      33             :                 && !GDKinmemory(b->theap->farmid));
      34             : }
      35             : 
      36             : void
      37             : RTREEdecref(BAT *b)
      38             : {
      39             :         ATOMIC_BASE_TYPE refs = ATOMIC_DEC(&b->trtree->refs);
      40             :         //If RTree is marked for destruction and there are no refs, destroy the RTree
      41             :         if (b->trtree->destroy && refs == 0) {
      42             :                 ATOMIC_DESTROY(&b->trtree->refs);
      43             :                 rtree_destroy(b->trtree->rtree);
      44             :                 b->trtree->rtree = NULL;
      45             :                 GDKfree(b->trtree);
      46             :                 b->trtree = NULL;
      47             :         }
      48             : 
      49             : }
      50             : 
      51             : void
      52             : RTREEincref(BAT *b)
      53             : {
      54             :         (void) ATOMIC_INC(&b->trtree->refs);
      55             : }
      56             : 
      57             : // Persist rtree to disk if the conditions are right
      58             : static gdk_return
      59             : persistRtree (BAT *b)
      60             : {
      61             :         if (RTREEpersistcheck(b)) {
      62             :                 //TODO Necessary?
      63             :                 BBPfix(b->batCacheid);
      64             :                 rtree_t *rtree = b->trtree->rtree;
      65             : 
      66             :                 if (rtree) {
      67             :                         const char * filename = BBP_physical(b->batCacheid);
      68             :                         int farmid = b->theap->farmid;
      69             : 
      70             :                         int fd = GDKfdlocate(farmid, filename, "w", "bsrt");
      71             :                         FILE *file_stream = fdopen(fd,"w");
      72             : 
      73             :                         if (file_stream != NULL) {
      74             :                                 int err;
      75             :                                 if ((err = rtree_bsrt_write(rtree,file_stream)) != 0) {
      76             :                                         GDKerror("%s", rtree_strerror(err));
      77             :                                         fclose(file_stream);
      78             :                                         BBPunfix(b->batCacheid);
      79             :                                         return GDK_FAIL;
      80             :                                 }
      81             : 
      82             :                                 if (!(GDKdebug & NOSYNCMASK)) {
      83             :         #if defined(NATIVE_WIN32)
      84             :                                         _commit(fd);
      85             :         #elif defined(HAVE_FDATASYNC)
      86             :                                         fdatasync(fd);
      87             :         #elif defined(HAVE_FSYNC)
      88             :                                         fsync(fd);
      89             :         #endif
      90             :                                 }
      91             :                                 fclose(file_stream);
      92             :                         }
      93             :                         else {
      94             :                                 GDKerror("%s",strerror(errno));
      95             :                                 close(fd);
      96             :                                 return GDK_FAIL;
      97             :                         }
      98             :                 }
      99             :                 BBPunfix(b->batCacheid);
     100             :         }
     101             :         //TODO Should we just return sucess if the rtree is not persisted?
     102             :         return GDK_SUCCEED;
     103             : }
     104             : 
     105             : static gdk_return
     106             : BATcheckrtree(BAT *b) {
     107             :         const char * filename = BBP_physical(b->batCacheid);
     108             :         int farmid = b->theap->farmid;
     109             :         int fd = GDKfdlocate(farmid, filename, "r", "bsrt");
     110             : 
     111             :         //Do we have the rtree on file?
     112             :         if (fd == -1)
     113             :                 return GDK_SUCCEED;
     114             : 
     115             :         FILE *file_stream = fdopen(fd,"r");
     116             :         if (file_stream != NULL) {
     117             :                 rtree_t* rtree = rtree_bsrt_read(file_stream);
     118             :                 if (!rtree) {
     119             :                         GDKerror("%s", errno != 0 ? strerror(errno) : "Failed rtree_bsrt_read");
     120             :                         fclose(file_stream);
     121             :                         return GDK_FAIL;
     122             :                 }
     123             :                 b->trtree = GDKmalloc(sizeof(struct RTree));
     124             :                 b->trtree->rtree = rtree;
     125             :                 b->trtree->destroy = false;
     126             :                 ATOMIC_INIT(&b->trtree->refs, 1);
     127             :                 fclose(file_stream);
     128             :                 return GDK_SUCCEED;
     129             :         }
     130             :         else {
     131             :                 GDKerror("%s",strerror(errno));
     132             :                 close(fd);
     133             :                 return GDK_FAIL;
     134             :         }
     135             : }
     136             : 
     137             : //Check if RTree exists on file (previously created index)
     138             : static bool
     139             : RTREEexistsonfile(BAT *b) {
     140             :         const char * filename = BBP_physical(b->batCacheid);
     141             : 
     142             :         if (!b->theap) return false;
     143             : 
     144             :         int farmid = b->theap->farmid;
     145             :         int fd = GDKfdlocate(farmid, filename, "r", "bsrt");
     146             : 
     147             :         //Do we have the rtree on file?
     148             :         if (fd == -1) {
     149             :                 return false;
     150             :         } else {
     151             :                 close(fd);
     152             :                 return true;
     153             :         }
     154             : }
     155             : 
     156             : //Check if RTree exists
     157             : //We also check if it exists on file. If the index is not loaded, it will be
     158             : //TODO Check for destroy -> it does not exist if destroy
     159             : bool
     160             : RTREEexists(BAT *b)
     161             : {
     162             :         BAT *pb;
     163             :         bool ret;
     164             :         if (VIEWtparent(b)) {
     165             :                 pb = BBP_cache(VIEWtparent(b));
     166             :                 assert(pb);
     167             :         } else {
     168             :                 pb = b;
     169             :         }
     170             : 
     171             :         MT_lock_set(&pb->batIdxLock);
     172             :         ret = (pb->trtree != NULL || RTREEexistsonfile(pb));
     173             :         MT_lock_unset(&pb->batIdxLock);
     174             : 
     175             :         return ret;
     176             : 
     177             : }
     178             : 
     179             : bool
     180             : RTREEexists_bid(bat *bid)
     181             : {
     182             :         BAT *b;
     183             :         bool ret;
     184             :         if ((b = BATdescriptor(*bid)) == NULL)
     185             :                 return false;
     186             :         ret = RTREEexists(b);
     187             :         BBPunfix(b->batCacheid);
     188             :         return ret;
     189             : }
     190             : 
     191             : gdk_return
     192             : BATrtree(BAT *wkb, BAT *mbr)
     193             : {
     194             :         BAT *pb;
     195             :         BATiter bi;
     196             :         rtree_t *rtree = NULL;
     197             :         struct canditer ci;
     198             : 
     199             :         //Check for a parent BAT of wkb, load if exists
     200             :         if (VIEWtparent(wkb)) {
     201             :                 pb = BBP_cache(VIEWtparent(wkb));
     202             :                 assert(pb);
     203             :         } else {
     204             :                 pb = wkb;
     205             :         }
     206             : 
     207             :         //Check if rtree already exists
     208             :         if (pb->trtree == NULL) {
     209             :                 //If it doesn't exist, take the lock to create/get the rtree
     210             :                 MT_lock_set(&pb->batIdxLock);
     211             : 
     212             :                 //Try to load it from disk
     213             :                 if (BATcheckrtree(pb) == GDK_SUCCEED && pb->trtree != NULL) {
     214             :                         MT_lock_unset(&pb->batIdxLock);
     215             :                         return GDK_SUCCEED;
     216             :                 }
     217             : 
     218             :                 //First arg are dimensions: we only allow x, y
     219             :                 //Second arg are flags: split strategy and nodes-per-page
     220             :                 if ((rtree = rtree_new(2, RTREE_DEFAULT)) == NULL) {
     221             :                         GDKerror("rtree_new failed\n");
     222             :                         return GDK_FAIL;
     223             :                 }
     224             :                 bi = bat_iterator(mbr);
     225             :                 canditer_init(&ci, mbr,NULL);
     226             : 
     227             :                 for (BUN i = 0; i < ci.ncand; i++) {
     228             :                         oid p = canditer_next(&ci) - mbr->hseqbase;
     229             :                         mbr_t *inMBR = (mbr_t *)BUNtail(bi, p);
     230             : 
     231             :                         rtree_id_t rtree_id = i;
     232             :                         rtree_coord_t rect[4];
     233             :                         rect[0] = inMBR->xmin;
     234             :                         rect[1] = inMBR->ymin;
     235             :                         rect[2] = inMBR->xmax;
     236             :                         rect[3] = inMBR->ymax;
     237             :                         rtree_add_rect(rtree,rtree_id,rect);
     238             :                 }
     239             :                 bat_iterator_end(&bi);
     240             :                 pb->trtree = GDKmalloc(sizeof(struct RTree));
     241             :                 pb->trtree->rtree = rtree;
     242             :                 pb->trtree->destroy = false;
     243             :                 ATOMIC_INIT(&pb->trtree->refs, 1);
     244             :                 persistRtree(pb);
     245             :                 MT_lock_unset(&pb->batIdxLock);
     246             :         }
     247             :         //TODO What do we do when the conditions are not right for creating the index?
     248             :         return GDK_SUCCEED;
     249             : }
     250             : 
     251             : //Free the RTree from memory,
     252             : void
     253             : RTREEfree(BAT *b)
     254             : {
     255             :         BAT *pb;
     256             :         if (VIEWtparent(b)) {
     257             :                 pb = BBP_cache(VIEWtparent(b));
     258             :                 assert(pb);
     259             :         } else {
     260             :                 pb = b;
     261             :         }
     262             : 
     263             :         if (pb && pb->trtree) {
     264             :                 MT_lock_set(&pb->batIdxLock);
     265             :                 //Mark the RTree for destruction
     266             :                 pb->trtree->destroy = true;
     267             :                 RTREEdecref(pb);
     268             :                 MT_lock_unset(&b->batIdxLock);
     269             :         }
     270             : }
     271             : 
     272             : //Free the RTree from memory, unlink the file associated with it
     273             : void
     274             : RTREEdestroy(BAT *b)
     275             : {
     276             :         BAT *pb;
     277             :         if (VIEWtparent(b)) {
     278             :                 pb = BBP_cache(VIEWtparent(b));
     279             :                 assert(pb);
     280             :         } else {
     281             :                 pb = b;
     282             :         }
     283             : 
     284             :         MT_lock_set(&pb->batIdxLock);
     285             :         if (pb->trtree) {
     286             :                 //Mark the RTree for destruction
     287             :                 pb->trtree->destroy = true;
     288             :                 RTREEdecref(pb);
     289             :                 //If the farm is in-memory, don't unlink the file (there is no file in that case)
     290             :                 if (pb->theap && !GDKinmemory(pb->theap->farmid)) {
     291             :                         GDKunlink(pb->theap->farmid,
     292             :                                 BATDIR,
     293             :                                 BBP_physical(b->batCacheid),
     294             :                                 "bsrt");
     295             :                 }
     296             :         }
     297             :         //If the rtree is not loaded (pb->trtree is null), but there is a file with the index (from previous execution),
     298             :         //we should remove the file
     299             :         else if (RTREEexistsonfile(pb)) {
     300             :                 GDKunlink(pb->theap->farmid,
     301             :                         BATDIR,
     302             :                         BBP_physical(b->batCacheid),
     303             :                         "bsrt");
     304             :         }
     305             :         MT_lock_unset(&b->batIdxLock);
     306             : }
     307             : 
     308             : struct results_rtree {
     309             :         int results_next;
     310             :         int results_left;
     311             :         BUN* candidates;
     312             : };
     313             : 
     314             : static int
     315             : f (rtree_id_t id, void *context) {
     316             :         struct results_rtree *results_rtree = (struct results_rtree *) context;
     317             :         results_rtree->candidates[results_rtree->results_next++] = (BUN) id;
     318             :         --results_rtree->results_left;
     319             :         return results_rtree->results_left <= 0;
     320             : }
     321             : 
     322             : BUN*
     323             : RTREEsearch(BAT *b, mbr_t *inMBR, int result_limit) {
     324             :         BAT *pb;
     325             :         if (VIEWtparent(b)) {
     326             :                 pb = BBP_cache(VIEWtparent(b));
     327             :                 assert(pb);
     328             :         } else {
     329             :                 pb = b;
     330             :         }
     331             : 
     332             :         //Try to load if there is an RTree index on file
     333             :         MT_lock_set(&pb->batIdxLock);
     334             :         if (pb->trtree == NULL) {
     335             :                 if (BATcheckrtree(pb) != GDK_SUCCEED) {
     336             :                         MT_lock_unset(&pb->batIdxLock);
     337             :                         return NULL;
     338             :                 }
     339             :         }
     340             :         MT_lock_unset(&pb->batIdxLock);
     341             : 
     342             :         rtree_t *rtree = pb->trtree->rtree;
     343             :         if (rtree != NULL) {
     344             :                 //Increase ref, we're gonna use the index
     345             :                 RTREEincref(pb);
     346             :                 BUN *candidates = GDKmalloc((result_limit + 1) * SIZEOF_BUN);
     347             :                 memset(candidates, 0, (result_limit + 1) * SIZEOF_BUN);
     348             : 
     349             :                 rtree_coord_t rect[4];
     350             :                 rect[0] = inMBR->xmin;
     351             :                 rect[1] = inMBR->ymin;
     352             :                 rect[2] = inMBR->xmax;
     353             :                 rect[3] = inMBR->ymax;
     354             : 
     355             :                 struct results_rtree results;
     356             :                 results.results_next = 0;
     357             :                 results.results_left = result_limit;
     358             :                 results.candidates = candidates;
     359             : 
     360             :                 rtree_search(rtree, (const rtree_coord_t*) rect, f, &results);
     361             :                 candidates[result_limit - results.results_left] = BUN_NONE;
     362             : 
     363             :                 //Finished using the index, decrease ref
     364             :                 RTREEdecref(pb);
     365             : 
     366             :                 return candidates;
     367             :         } else
     368             :                 return NULL;
     369             : }
     370             : #else
     371             : void
     372    80728221 : RTREEdestroy(BAT *b) {
     373    80728221 :         (void) b;
     374    80728221 : }
     375             : 
     376             : void
     377      450631 : RTREEfree(BAT *b) {
     378      450631 :         (void) b;
     379      450631 : }
     380             : #endif

Generated by: LCOV version 1.14