]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Initial attempt at getting R-Tree queries to work using a priority queue.
authordrh <drh@noemail.net>
Tue, 15 Apr 2014 21:06:14 +0000 (21:06 +0000)
committerdrh <drh@noemail.net>
Tue, 15 Apr 2014 21:06:14 +0000 (21:06 +0000)
This check-in compiles, but R-Trees do not work well.  And there are
debugging printf()s left in the code.  This is an incremental check-in.

FossilOrigin-Name: 53688a25c23c394278a357829793889970aa4157

ext/rtree/rtree.c
manifest
manifest.uuid

index 17375718e91e9313bf326a4111a75ef0fd0a35c6..9f41da3534c6f9201a0e98e08bf088ad261d0bf2 100644 (file)
@@ -63,6 +63,7 @@
 
 #include <string.h>
 #include <assert.h>
+#include <stdio.h>
 
 #ifndef SQLITE_AMALGAMATION
 #include "sqlite3rtree.h"
@@ -86,6 +87,7 @@ typedef struct RtreeConstraint RtreeConstraint;
 typedef struct RtreeMatchArg RtreeMatchArg;
 typedef struct RtreeGeomCallback RtreeGeomCallback;
 typedef union RtreeCoord RtreeCoord;
+typedef struct RtreeSearchPoint RtreeSearchPoint;
 
 /* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */
 #define RTREE_MAX_DIMENSIONS 5
@@ -165,6 +167,23 @@ struct Rtree {
   typedef float RtreeValue;                /* Low accuracy coordinate */
 #endif
 
+/*
+** When doing a search of an r-tree, instances of the following structure
+** record intermediate results from the tree walk.
+**
+** The id is always a node-id.  For iLevel>=1 the id is the node-id of
+** the node that the RtreeSearchPoint represents.  When iLevel==0, however,
+** the id is of the parent node and the cell that RtreeSearchPoint
+** represents is the iCell-th entry in the parent node.
+*/
+struct RtreeSearchPoint {
+  RtreeDValue rScore;    /* The score for this node.  Smallest goes first. */
+  sqlite3_int64 id;      /* Node ID */
+  u8 iLevel;             /* 0=entries.  1=leaf node.  2+ for higher */
+  u8 eWithin;            /* PARTLY_WITHIN or FULLY_WITHIN */
+  u8 iCell;              /* Cell index within the node */
+};
+
 /*
 ** The minimum number of cells allowed for a node is a third of the 
 ** maximum. In Gutman's notation:
@@ -187,18 +206,34 @@ struct Rtree {
 */
 #define RTREE_MAX_DEPTH 40
 
+
+/*
+** Number of entries in the cursor RtreeNode cache.  The first entry is
+** used to cache the RtreeNode for RtreeCursor.sPoint.  The remaining
+** entries cache the RtreeNode for the first elements of the priority queue.
+*/
+#define RTREE_CACHE_SZ  5
+
 /* 
 ** An rtree cursor object.
 */
 struct RtreeCursor {
   sqlite3_vtab_cursor base;         /* Base class.  Must be first */
-  RtreeNode *pNode;                 /* Node cursor is currently pointing at */
-  int iCell;                        /* Index of current cell in pNode */
+  u8 atEOF;                         /* True if at end of search */
+  u8 bPoint;                        /* True if sPoint is valid */
   int iStrategy;                    /* Copy of idxNum search parameter */
   int nConstraint;                  /* Number of entries in aConstraint */
   RtreeConstraint *aConstraint;     /* Search constraints. */
+  int nPointAlloc;                  /* Number of slots allocated for aPoint[] */
+  int nPoint;                       /* Number of slots used in aPoint[] */
+  RtreeSearchPoint *aPoint;         /* Priority queue for search points */
+  RtreeSearchPoint sPoint;          /* Cached next search point */
+  RtreeNode *aNode[RTREE_CACHE_SZ]; /* Rtree node cache */
 };
 
+/* Return the Rtree of a RtreeCursor */
+#define RTREE_OF_CURSOR(X)   ((Rtree*)((X)->base.pVtab))
+
 /*
 ** A coordinate can be either a floating point number or a integer.  All
 ** coordinates within a single R-Tree are always of the same time.
@@ -247,6 +282,7 @@ struct RtreeConstraint {
 #define RTREE_MATCH 0x46  /* Old-style sqlite3_rtree_geometry_callback() */
 #define RTREE_QUERY 0x47  /* New-style sqlite3_rtree_query_callback() */
 
+
 /* 
 ** An rtree structure node.
 */
@@ -838,12 +874,13 @@ static void freeCursorConstraints(RtreeCursor *pCsr){
 */
 static int rtreeClose(sqlite3_vtab_cursor *cur){
   Rtree *pRtree = (Rtree *)(cur->pVtab);
-  int rc;
+  int ii;
   RtreeCursor *pCsr = (RtreeCursor *)cur;
   freeCursorConstraints(pCsr);
-  rc = nodeRelease(pRtree, pCsr->pNode);
+  sqlite3_free(pCsr->aPoint);
+  for(ii=0; ii<RTREE_CACHE_SZ; ii++) nodeRelease(pRtree, pCsr->aNode[ii]);
   sqlite3_free(pCsr);
-  return rc;
+  return SQLITE_OK;
 }
 
 /*
@@ -854,14 +891,14 @@ static int rtreeClose(sqlite3_vtab_cursor *cur){
 */
 static int rtreeEof(sqlite3_vtab_cursor *cur){
   RtreeCursor *pCsr = (RtreeCursor *)cur;
-  return (pCsr->pNode==0);
+  return pCsr->atEOF;
 }
 
 /*
 ** The r-tree constraint passed as the second argument to this function is
 ** guaranteed to be a MATCH constraint.
 */
-static int testRtreeGeom(
+static int rtreeTestGeom(
   Rtree *pRtree,                  /* R-Tree object */
   RtreeConstraint *pConstraint,   /* MATCH constraint to test */
   RtreeCell *pCell,               /* Cell to test */
@@ -883,24 +920,39 @@ static int testRtreeGeom(
 
 /* 
 ** Cursor pCursor currently points to a cell in a non-leaf page.
-** Set *pbEof to true if the sub-tree headed by the cell is filtered
-** (excluded) by the constraints in the pCursor->aConstraint[] 
-** array, or false otherwise.
+** Set *peWithin to NOT_WITHIN if the constraints in pCursor->aConstraint[]
+** are guaranteed to never be satisfied by any subelement under the
+** current cell.  If some subelement of the cell might satisfy all
+** constraints, then set *peWithin to PARTLY_WITHIN.  If all subelements
+** of the cell are guaranteed to fully satisfy all constraints, then
+** set *peWithin to FULLY_WITHIN.
+**
+** In other words, set *peWithin to NOT_WITHIN, PARTLY_WITHIN, or
+** FULLY_WITHIN if the cell is completely outside of the field-of-view,
+** overlaps the field of view, or is completely contained within the
+** field of view, respectively.
+**
+** It is not an error to set *peWithin to PARTLY_WITHIN when FULLY_WITHIN
+** would be correct.  Doing so is suboptimal, but will still give the
+** correct answer.  
 **
 ** Return SQLITE_OK if successful or an SQLite error code if an error
-** occurs within a geometry callback.
+** occurs.  Errors can only possible if there is a geometry callback.
 */
-static int testRtreeCell(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){
-  RtreeCell cell;
+static int rtreeTestCell(
+  RtreeCursor *pCursor,      /* The cursor to check */
+  RtreeCell *pCell,          /* The cell to check */
+  int *peWithin              /* Set true if element is out-of-bounds */
+){
   int ii;
-  int bRes = 0;
+  int bOutOfBounds = 0;
   int rc = SQLITE_OK;
+  Rtree *pRtree = RTREE_OF_CURSOR(pCursor);
 
-  nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell);
-  for(ii=0; bRes==0 && ii<pCursor->nConstraint; ii++){
+  for(ii=0; bOutOfBounds==0 && ii<pCursor->nConstraint; ii++){
     RtreeConstraint *p = &pCursor->aConstraint[ii];
-    RtreeDValue cell_min = DCOORD(cell.aCoord[(p->iCoord>>1)*2]);
-    RtreeDValue cell_max = DCOORD(cell.aCoord[(p->iCoord>>1)*2+1]);
+    RtreeDValue cell_min = DCOORD(pCell->aCoord[(p->iCoord>>1)*2]);
+    RtreeDValue cell_max = DCOORD(pCell->aCoord[(p->iCoord>>1)*2+1]);
 
     assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE 
         || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_MATCH
@@ -908,52 +960,61 @@ static int testRtreeCell(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){
 
     switch( p->op ){
       case RTREE_LE: case RTREE_LT: 
-        bRes = p->u.rValue<cell_min; 
+        bOutOfBounds = p->u.rValue<cell_min; 
         break;
 
       case RTREE_GE: case RTREE_GT: 
-        bRes = p->u.rValue>cell_max; 
+        bOutOfBounds = p->u.rValue>cell_max; 
         break;
 
       case RTREE_EQ:
-        bRes = (p->u.rValue>cell_max || p->u.rValue<cell_min);
+        bOutOfBounds = (p->u.rValue>cell_max || p->u.rValue<cell_min);
         break;
 
       default: {
         assert( p->op==RTREE_MATCH );
-        rc = testRtreeGeom(pRtree, p, &cell, &bRes);
-        bRes = !bRes;
+        rc = rtreeTestGeom(pRtree, p, pCell, &bOutOfBounds);
+        bOutOfBounds = !bOutOfBounds;
         break;
       }
     }
   }
 
-  *pbEof = bRes;
+  *peWithin = bOutOfBounds ? NOT_WITHIN : PARTLY_WITHIN;
   return rc;
 }
 
 /* 
-** Test if the cell that cursor pCursor currently points to
-** would be filtered (excluded) by the constraints in the 
-** pCursor->aConstraint[] array. If so, set *pbEof to true before
-** returning. If the cell is not filtered (excluded) by the constraints,
-** set pbEof to zero.
+** pCursor points to a leaf r-tree entry which is a candidate for output.
+** This routine sets *peWithin to one of NOT_WITHIN, PARTLY_WITHIN, or
+** FULLY_WITHIN depending on whether or not the leaf entry is completely
+** outside the region defined by pCursor->aConstraints[], or overlaps the
+** region, or is completely within the region, respectively.
+**
+** This routine is more selective than rtreeTestCell().  rtreeTestCell()
+** will return PARTLY_WITHIN or FULLY_WITHIN if the constraints are such
+** that a subelement of the cell to be included in the result set.  This
+** routine is is only called for leaf r-tree entries and does not need
+** to concern itself with subelements.  Hence it only sets *peWithin to
+** PARTLY_WITHIN or FULLY_WITHIN if the cell itself meets the requirements.
 **
 ** Return SQLITE_OK if successful or an SQLite error code if an error
 ** occurs within a geometry callback.
 **
 ** This function assumes that the cell is part of a leaf node.
 */
-static int testRtreeEntry(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){
-  RtreeCell cell;
+static int rtreeTestEntry(
+  RtreeCursor *pCursor,   /* Cursor pointing to the leaf element */
+  RtreeCell *pCell,       /* The cell to check */
+  int *peWithin           /* OUT: NOT_WITHIN, PARTLY_WITHIN, or FULLY_WITHIN */
+){
+  Rtree *pRtree = RTREE_OF_CURSOR(pCursor);
   int ii;
-  *pbEof = 0;
+  int res = 1;     /* Innocent until proven guilty */
 
-  nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell);
-  for(ii=0; ii<pCursor->nConstraint; ii++){
+  for(ii=0; res && ii<pCursor->nConstraint; ii++){
     RtreeConstraint *p = &pCursor->aConstraint[ii];
-    RtreeDValue coord = DCOORD(cell.aCoord[p->iCoord]);
-    int res;
+    RtreeDValue coord = DCOORD(pCell->aCoord[p->iCoord]);
     assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE 
         || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_MATCH
     );
@@ -966,85 +1027,19 @@ static int testRtreeEntry(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){
       default: {
         int rc;
         assert( p->op==RTREE_MATCH );
-        rc = testRtreeGeom(pRtree, p, &cell, &res);
+        rc = rtreeTestGeom(pRtree, p, pCell, &res);
         if( rc!=SQLITE_OK ){
           return rc;
         }
         break;
       }
     }
-
-    if( !res ){
-      *pbEof = 1;
-      return SQLITE_OK;
-    }
   }
 
+  *peWithin = res ? FULLY_WITHIN : NOT_WITHIN;
   return SQLITE_OK;
 }
 
-/*
-** Cursor pCursor currently points at a node that heads a sub-tree of
-** height iHeight (if iHeight==0, then the node is a leaf). Descend
-** to point to the left-most cell of the sub-tree that matches the 
-** configured constraints.
-*/
-static int descendToCell(
-  Rtree *pRtree, 
-  RtreeCursor *pCursor, 
-  int iHeight,
-  int *pEof                 /* OUT: Set to true if cannot descend */
-){
-  int isEof;
-  int rc;
-  int ii;
-  RtreeNode *pChild;
-  sqlite3_int64 iRowid;
-
-  RtreeNode *pSavedNode = pCursor->pNode;
-  int iSavedCell = pCursor->iCell;
-
-  assert( iHeight>=0 );
-
-  if( iHeight==0 ){
-    rc = testRtreeEntry(pRtree, pCursor, &isEof);
-  }else{
-    rc = testRtreeCell(pRtree, pCursor, &isEof);
-  }
-  if( rc!=SQLITE_OK || isEof || iHeight==0 ){
-    goto descend_to_cell_out;
-  }
-
-  iRowid = nodeGetRowid(pRtree, pCursor->pNode, pCursor->iCell);
-  rc = nodeAcquire(pRtree, iRowid, pCursor->pNode, &pChild);
-  if( rc!=SQLITE_OK ){
-    goto descend_to_cell_out;
-  }
-
-  nodeRelease(pRtree, pCursor->pNode);
-  pCursor->pNode = pChild;
-  isEof = 1;
-  for(ii=0; isEof && ii<NCELL(pChild); ii++){
-    pCursor->iCell = ii;
-    rc = descendToCell(pRtree, pCursor, iHeight-1, &isEof);
-    if( rc!=SQLITE_OK ){
-      goto descend_to_cell_out;
-    }
-  }
-
-  if( isEof ){
-    assert( pCursor->pNode==pChild );
-    nodeReference(pSavedNode);
-    nodeRelease(pRtree, pChild);
-    pCursor->pNode = pSavedNode;
-    pCursor->iCell = iSavedCell;
-  }
-
-descend_to_cell_out:
-  *pEof = isEof;
-  return rc;
-}
-
 /*
 ** One of the cells in node pNode is guaranteed to have a 64-bit 
 ** integer value equal to iRowid. Return the index of this cell.
@@ -1057,6 +1052,7 @@ static int nodeRowidIndex(
 ){
   int ii;
   int nCell = NCELL(pNode);
+  assert( nCell<200 );
   for(ii=0; ii<nCell; ii++){
     if( nodeGetRowid(pRtree, pNode, ii)==iRowid ){
       *piIndex = ii;
@@ -1079,48 +1075,241 @@ static int nodeParentIndex(Rtree *pRtree, RtreeNode *pNode, int *piIndex){
   return SQLITE_OK;
 }
 
-/* 
-** Rtree virtual table module xNext method.
+/*
+** Compare two search points.  Return negative, zero, or positive if the first
+** is less than, equal to, or greater than the second.
 */
-static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){
-  Rtree *pRtree = (Rtree *)(pVtabCursor->pVtab);
-  RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
-  int rc = SQLITE_OK;
+static int rtreeSearchPointCompare(
+  const RtreeSearchPoint *pA,
+  const RtreeSearchPoint *pB
+){
+  if( pA->rScore<pB->rScore ) return -1;
+  if( pA->rScore>pB->rScore ) return +1;
+  if( pA->iLevel<pB->iLevel ) return -1;
+  if( pA->iLevel>pB->iLevel ) return +1;
+  return 0;
+}
 
-  /* RtreeCursor.pNode must not be NULL. If is is NULL, then this cursor is
-  ** already at EOF. It is against the rules to call the xNext() method of
-  ** a cursor that has already reached EOF.
-  */
-  assert( pCsr->pNode );
+/*
+** Interchange to search points in a cursor.
+*/
+static void rtreeSearchPointSwap(RtreeCursor *p, int i, int j){
+  RtreeSearchPoint t = p->aPoint[i];
+  assert( i<j );
+  p->aPoint[i] = p->aPoint[j];
+  p->aPoint[j] = t;
+  if( i<RTREE_CACHE_SZ-1 ){
+    if( j>=RTREE_CACHE_SZ-1 ){
+      nodeRelease(RTREE_OF_CURSOR(p), p->aNode[i+1]);
+      p->aNode[i+1] = 0;
+    }else{
+      RtreeNode *pTemp = p->aNode[i+i];
+      p->aNode[i+1] = p->aNode[j+1];
+      p->aNode[j+1] = pTemp;
+    }
+  }
+}
 
-  if( pCsr->iStrategy==1 ){
-    /* This "scan" is a direct lookup by rowid. There is no next entry. */
-    nodeRelease(pRtree, pCsr->pNode);
-    pCsr->pNode = 0;
+/*
+** Return the search point with the lowest current score.
+*/
+static RtreeSearchPoint *rtreeSearchPointFirst(RtreeCursor *pCur){
+  return pCur->bPoint ? &pCur->sPoint : pCur->nPoint ? pCur->aPoint : 0;
+}
+
+/*
+** Get the RtreeNode for the search point with the lowest score.
+*/
+static RtreeNode *rtreeNodeOfFirstSearchPoint(RtreeCursor *pCur, int *pRC){
+  sqlite3_int64 id;
+  int ii = 1 - pCur->bPoint;
+  assert( ii==0 || ii==1 );
+  assert( pCur->bPoint || pCur->nPoint );
+  if( pCur->aNode[ii]==0 ){
+    assert( pRC!=0 );
+    id = ii ? pCur->aPoint[0].id : pCur->sPoint.id;
+    *pRC = nodeAcquire(RTREE_OF_CURSOR(pCur), id, 0, &pCur->aNode[ii]);
+  }
+  return pCur->aNode[ii];
+}
+
+/*
+** Push a new element onto the priority queue
+*/
+static RtreeSearchPoint *rtreeEnqueue(
+  RtreeCursor *pCur,    /* The cursor */
+  RtreeDValue rScore,   /* Score for the new search point */
+  u8 iLevel             /* Level for the new search point */
+){
+  int i, j;
+  RtreeSearchPoint *pNew;
+  if( pCur->nPoint>=pCur->nPointAlloc ){
+    int nNew = pCur->nPointAlloc*2 + 8;
+    pNew = sqlite3_realloc(pCur->aPoint, nNew*sizeof(pCur->aPoint[0]));
+    if( pNew==0 ) return 0;
+    pCur->aPoint = pNew;
+    pCur->nPointAlloc = nNew;
+  }
+  i = pCur->nPoint++;
+  pNew = pCur->aPoint + i;
+  pNew->rScore = rScore;
+  pNew->iLevel = iLevel;  
+  while( i>0 ){
+    RtreeSearchPoint *pParent;
+    j = (i-1)/2;
+    pParent = pCur->aPoint + j;
+    if( rtreeSearchPointCompare(pNew, pParent)>=0 ) break;
+    rtreeSearchPointSwap(pCur, j, i);
+    i = j;
+    pNew = pParent;
+  }
+  return pNew;
+}
+
+/*
+** Allocate a new RtreeSearchPoint and return a pointer to it.  Return
+** NULL if malloc fails.
+*/
+static RtreeSearchPoint *rtreeSearchPointNew(
+  RtreeCursor *pCur,    /* The cursor */
+  RtreeDValue rScore,   /* Score for the new search point */
+  u8 iLevel             /* Level for the new search point */
+){
+  RtreeSearchPoint *pNew, *pFirst;
+  pFirst = rtreeSearchPointFirst(pCur);
+  if( pFirst==0
+   || pFirst->rScore>rScore 
+   || (pFirst->rScore==rScore && pFirst->iLevel>iLevel)
+  ){
+    if( pCur->bPoint ){
+      pNew = rtreeEnqueue(pCur, rScore, iLevel);
+      if( pNew==0 ) return 0;
+      assert( pCur->aNode[1]==0 );
+      pCur->aNode[1] = pCur->aNode[0];
+      pCur->aNode[0] = 0;
+      *pNew = pCur->sPoint;
+    }
+    pCur->sPoint.rScore = rScore;
+    pCur->sPoint.iLevel = iLevel;
+    pCur->bPoint = 1;
+    return &pCur->sPoint;
   }else{
-    /* Move to the next entry that matches the configured constraints. */
-    int iHeight = 0;
-    while( pCsr->pNode ){
-      RtreeNode *pNode = pCsr->pNode;
-      int nCell = NCELL(pNode);
-      for(pCsr->iCell++; pCsr->iCell<nCell; pCsr->iCell++){
-        int isEof;
-        rc = descendToCell(pRtree, pCsr, iHeight, &isEof);
-        if( rc!=SQLITE_OK || !isEof ){
-          return rc;
+    return rtreeEnqueue(pCur, rScore, iLevel);
+  }
+}
+
+static void traceTop(RtreeCursor *pCur, const char *zPrefix){
+  RtreeSearchPoint *p = rtreeSearchPointFirst(pCur);
+  if( p ){
+    printf("=== %6s id=%lld lvl=%d iCell=%d rScore=%g eWithin=%d\n",
+       zPrefix, p->id, p->iLevel, p->iCell, p->rScore, p->eWithin);
+  }
+}
+
+/* Remove the search point with the lowest current score.
+*/
+static void rtreeSearchPointPop(RtreeCursor *p){
+  int i, j, k, n;
+  i = p->bPoint;
+  assert( i==0 || i==1 );
+  if( p->aNode[i] ){
+    nodeRelease(RTREE_OF_CURSOR(p), p->aNode[i]);
+    p->aNode[i] = 0;
+  }
+  if( p->bPoint ){
+    p->bPoint = 0;
+  }else if( p->nPoint ){
+    n = --p->nPoint;
+    p->aPoint[0] = p->aPoint[n];
+    i = 0;
+    while( (j = i*2+1)<n ){
+      k = j+1;
+      if( k<n && rtreeSearchPointCompare(&p->aPoint[k], &p->aPoint[j])<0 ){
+        if( rtreeSearchPointCompare(&p->aPoint[k], &p->aPoint[i])<0 ){
+          rtreeSearchPointSwap(p, i, k);
+          i = k;
+        }else{
+          break;
+        }
+      }else{
+        if( rtreeSearchPointCompare(&p->aPoint[j], &p->aPoint[i])<0 ){
+          rtreeSearchPointSwap(p, i, j);
+          i = j;
+        }else{
+          break;
         }
       }
-      pCsr->pNode = pNode->pParent;
-      rc = nodeParentIndex(pRtree, pNode, &pCsr->iCell);
-      if( rc!=SQLITE_OK ){
-        return rc;
+    }
+  }
+}
+
+
+/*
+** Continue the search on cursor pCur until the front of the queue
+** contains an entry suitable for returning as a result-set row,
+** or until the RtreeSearchPoint queue is empty, indicating that the
+** query has completed.
+*/
+static int rtreeStepToLeaf(RtreeCursor *pCur){
+  RtreeSearchPoint *p;
+  RtreeSearchPoint *pNew;
+  Rtree *pRtree = RTREE_OF_CURSOR(pCur);
+  RtreeNode *pNode;
+  int eWithin;
+  int rc = SQLITE_OK;
+  int nCell;
+  RtreeCell cell;
+  RtreeSearchPoint x;
+
+  while( (p = rtreeSearchPointFirst(pCur))!=0 && p->iLevel>0 ){
+    pNode = rtreeNodeOfFirstSearchPoint(pCur, &rc);
+    if( rc ) return rc;
+    nCell = NCELL(pNode);
+    assert( nCell<200 );
+    while( p->iCell<nCell ){
+      nodeGetCell(pRtree, pNode, p->iCell, &cell);
+      if( p->iLevel==1 ){
+        rc = rtreeTestEntry(pCur, &cell, &eWithin);
+      }else{
+        rc = rtreeTestCell(pCur, &cell, &eWithin);
       }
-      nodeReference(pCsr->pNode);
-      nodeRelease(pRtree, pNode);
-      iHeight++;
+      if( rc ) return rc;
+      x = *p;
+      p->iCell++;
+      if( p->iCell>=nCell ){
+traceTop(pCur, "POP:");
+        rtreeSearchPointPop(pCur);
+      }
+      if( eWithin==NOT_WITHIN ) continue;
+      pNew = rtreeSearchPointNew(pCur, /*rScore*/0.0, x.iLevel-1);
+      if( pNew==0 ) return SQLITE_NOMEM;
+      pNew->eWithin = eWithin;
+      if( pNew->iLevel ){
+        pNew->id = cell.iRowid;
+        pNew->iCell = 0;
+      }else{
+        pNew->id = x.id;
+        pNew->iCell = x.iCell;
+      }
+traceTop(pCur, "PUSH:");
+      break;
     }
   }
+  pCur->atEOF = p==0;
+  return SQLITE_OK;
+}
+
+/* 
+** Rtree virtual table module xNext method.
+*/
+static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){
+  RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
+  int rc = SQLITE_OK;
 
+  /* Move to the next entry that matches the configured constraints. */
+traceTop(pCsr, "POP:");
+  rtreeSearchPointPop(pCsr);
+  rtreeStepToLeaf(pCsr);
   return rc;
 }
 
@@ -1128,13 +1317,14 @@ static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){
 ** Rtree virtual table module xRowid method.
 */
 static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
-  Rtree *pRtree = (Rtree *)pVtabCursor->pVtab;
   RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
-
-  assert(pCsr->pNode);
-  *pRowid = nodeGetRowid(pRtree, pCsr->pNode, pCsr->iCell);
-
-  return SQLITE_OK;
+  RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr);
+  int rc = SQLITE_OK;
+  RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
+  if( rc==SQLITE_OK && p ){
+    *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell);
+  }
+  return rc;
 }
 
 /* 
@@ -1143,13 +1333,18 @@ static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
 static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
   Rtree *pRtree = (Rtree *)cur->pVtab;
   RtreeCursor *pCsr = (RtreeCursor *)cur;
+  RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr);
+  RtreeCoord c;
+  int rc = SQLITE_OK;
+  RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
 
+  if( rc ) return rc;
+  if( p==0 ) return SQLITE_OK;
   if( i==0 ){
-    i64 iRowid = nodeGetRowid(pRtree, pCsr->pNode, pCsr->iCell);
-    sqlite3_result_int64(ctx, iRowid);
+    sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell));
   }else{
-    RtreeCoord c;
-    nodeGetCoord(pRtree, pCsr->pNode, pCsr->iCell, i-1, &c);
+    if( rc ) return rc;
+    nodeGetCoord(pRtree, pNode, p->iCell, i-1, &c);
 #ifndef SQLITE_RTREE_INT_ONLY
     if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
       sqlite3_result_double(ctx, c.f);
@@ -1160,7 +1355,6 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
       sqlite3_result_int(ctx, c.i);
     }
   }
-
   return SQLITE_OK;
 }
 
@@ -1171,12 +1365,18 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
 ** *ppLeaf to 0 and return SQLITE_OK. If an error occurs, set *ppLeaf
 ** to zero and return an SQLite error code.
 */
-static int findLeafNode(Rtree *pRtree, i64 iRowid, RtreeNode **ppLeaf){
+static int findLeafNode(
+  Rtree *pRtree,              /* RTree to search */
+  i64 iRowid,                 /* The rowid searching for */
+  RtreeNode **ppLeaf,         /* Write the node here */
+  sqlite3_int64 *piNode       /* Write the node-id here */
+){
   int rc;
   *ppLeaf = 0;
   sqlite3_bind_int64(pRtree->pReadRowid, 1, iRowid);
   if( sqlite3_step(pRtree->pReadRowid)==SQLITE_ROW ){
     i64 iNode = sqlite3_column_int64(pRtree->pReadRowid, 0);
+    if( piNode ) *piNode = iNode;
     rc = nodeAcquire(pRtree, iNode, 0, ppLeaf);
     sqlite3_reset(pRtree->pReadRowid);
   }else{
@@ -1239,10 +1439,10 @@ static int rtreeFilter(
 ){
   Rtree *pRtree = (Rtree *)pVtabCursor->pVtab;
   RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
-
   RtreeNode *pRoot = 0;
   int ii;
   int rc = SQLITE_OK;
+  int iCell = 0;
 
   rtreeReference(pRtree);
 
@@ -1252,13 +1452,16 @@ static int rtreeFilter(
   if( idxNum==1 ){
     /* Special case - lookup by rowid. */
     RtreeNode *pLeaf;        /* Leaf on which the required cell resides */
+    RtreeSearchPoint *p;     /* Search point for the the leaf */
     i64 iRowid = sqlite3_value_int64(argv[0]);
-    rc = findLeafNode(pRtree, iRowid, &pLeaf);
-    pCsr->pNode = pLeaf; 
-    if( pLeaf ){
-      assert( rc==SQLITE_OK );
-      rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &pCsr->iCell);
-    }
+    p = rtreeSearchPointNew(pCsr, 0.0, 0);
+    if( p==0 ) return SQLITE_NOMEM;
+    rc = findLeafNode(pRtree, iRowid, &pLeaf, &p->id);
+    pCsr->aNode[0] = pLeaf;
+    p->eWithin = PARTLY_WITHIN;
+    if( rc ) rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &iCell);
+    p->iCell = iCell;
+traceTop(pCsr, "PUSH:");
   }else{
     /* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array 
     ** with the configured constraints. 
@@ -1297,26 +1500,18 @@ static int rtreeFilter(
     }
   
     if( rc==SQLITE_OK ){
-      pCsr->pNode = 0;
       rc = nodeAcquire(pRtree, 1, 0, &pRoot);
     }
     if( rc==SQLITE_OK ){
-      int isEof = 1;
-      int nCell = NCELL(pRoot);
-      pCsr->pNode = pRoot;
-      for(pCsr->iCell=0; rc==SQLITE_OK && pCsr->iCell<nCell; pCsr->iCell++){
-        assert( pCsr->pNode==pRoot );
-        rc = descendToCell(pRtree, pCsr, pRtree->iDepth, &isEof);
-        if( !isEof ){
-          break;
-        }
-      }
-      if( rc==SQLITE_OK && isEof ){
-        assert( pCsr->pNode==pRoot );
-        nodeRelease(pRtree, pRoot);
-        pCsr->pNode = 0;
-      }
-      assert( rc!=SQLITE_OK || !pCsr->pNode || pCsr->iCell<NCELL(pCsr->pNode) );
+      RtreeSearchPoint *pNew = rtreeSearchPointNew(pCsr, 0.0, pRtree->iDepth+1);
+      if( pNew==0 ) return SQLITE_NOMEM;
+      pNew->id = 1;
+      pNew->iCell = 0;
+      pNew->eWithin = PARTLY_WITHIN;
+      assert( pCsr->bPoint==1 );
+      pCsr->aNode[0] = pRoot;
+traceTop(pCsr, "PUSH:");
+      rc = rtreeStepToLeaf(pCsr);
     }
   }
 
@@ -2395,7 +2590,7 @@ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
   ** about to be deleted. 
   */
   if( rc==SQLITE_OK ){
-    rc = findLeafNode(pRtree, iDelete, &pLeaf);
+    rc = findLeafNode(pRtree, iDelete, &pLeaf, 0);
   }
 
   /* Delete the cell in question from the leaf node. */
index 89c7e30ee793d376a87893432780c7415ddcf933..7acb80fc79742e2718dcd57b02021ea69768f16c 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\scomments\son\sthe\srtreenode()\sand\srtreedepth()\stest\sfunction\sin\sthe\sR-Tree\nmodule.
-D 2014-04-14T14:43:09.474
+C Initial\sattempt\sat\sgetting\sR-Tree\squeries\sto\swork\susing\sa\spriority\squeue.\nThis\scheck-in\scompiles,\sbut\sR-Trees\sdo\snot\swork\swell.\s\sAnd\sthere\sare\ndebugging\sprintf()s\sleft\sin\sthe\scode.\s\sThis\sis\san\sincremental\scheck-in.
+D 2014-04-15T21:06:14.359
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in e4ee6d36cdf6136aee0158675a3b24dd3bf31a5a
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -120,7 +120,7 @@ F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
 F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e
 F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
-F ext/rtree/rtree.c 07630c252ce0ba63d2d0e6922847eff6eec64fbc
+F ext/rtree/rtree.c 2f4d35a0689d588698cd0dd7d513761dff20c8fa
 F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
 F ext/rtree/rtree1.test cf679265ecafff494a768ac9c2f43a70915a6290
 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
@@ -1175,7 +1175,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
-P 3ba5f295c709faebf5505e61f6dc5266b811b086
-R e7b36f9b46b38d0d4d4bc75aff5d0712
+P ade5b986e8baab9df7bdaf7ccfaee2d6ba55fa3c
+R 52e87385ec75daf64fa5f31a0250db29
+T *branch * rtree-queue
+T *sym-rtree-queue *
+T -sym-rtree-enhancements *
 U drh
-Z 1c3694849e99525baeaeac92e41be917
+Z d0176a391caa75c92333b568fa6cbbbf
index 0a6c215cd0e8fbe5a3f953a47251b8200f33d6b1..ddde4e05251c7a8a29fcb82181468632cc85cf44 100644 (file)
@@ -1 +1 @@
-ade5b986e8baab9df7bdaf7ccfaee2d6ba55fa3c
\ No newline at end of file
+53688a25c23c394278a357829793889970aa4157
\ No newline at end of file