]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Test cases and bug fixes for the sqlite3_rtree_query_callback()
authordrh <drh@noemail.net>
Thu, 17 Apr 2014 14:52:20 +0000 (14:52 +0000)
committerdrh <drh@noemail.net>
Thu, 17 Apr 2014 14:52:20 +0000 (14:52 +0000)
mechanism.

FossilOrigin-Name: 1ccaaed6b516ec2ce953c1b31025a82ba76d00e7

ext/rtree/rtree.c
ext/rtree/rtreeE.test [new file with mode: 0644]
manifest
manifest.uuid
src/test_rtree.c

index 3c9a8daba938fd1ef2b096c28f8697c0ee347372..53414af7ee618c9d54d607189c8da6c2a168afae 100644 (file)
@@ -162,9 +162,11 @@ struct Rtree {
 #ifdef SQLITE_RTREE_INT_ONLY
   typedef sqlite3_int64 RtreeDValue;       /* High accuracy coordinate */
   typedef int RtreeValue;                  /* Low accuracy coordinate */
+# define RTREE_ZERO 0
 #else
   typedef double RtreeDValue;              /* High accuracy coordinate */
   typedef float RtreeValue;                /* Low accuracy coordinate */
+# define RTREE_ZERO 0.0
 #endif
 
 /*
@@ -270,7 +272,7 @@ struct RtreeConstraint {
     int (*xGeom)(sqlite3_rtree_geometry*,int,RtreeDValue*,int*);
     int (*xQueryFunc)(sqlite3_rtree_query_info*);
   } u;
-  sqlite3_rtree_query_info *pGeom;  /* xGeom and xQueryFunc argument */
+  sqlite3_rtree_query_info *pInfo;  /* xGeom and xQueryFunc argument */
 };
 
 /* Possible values for RtreeConstraint.op */
@@ -863,10 +865,10 @@ static void freeCursorConstraints(RtreeCursor *pCsr){
   if( pCsr->aConstraint ){
     int i;                        /* Used to iterate through constraint array */
     for(i=0; i<pCsr->nConstraint; i++){
-      sqlite3_rtree_query_info *pGeom = pCsr->aConstraint[i].pGeom;
-      if( pGeom ){
-        if( pGeom->xDelUser ) pGeom->xDelUser(pGeom->pUser);
-        sqlite3_free(pGeom);
+      sqlite3_rtree_query_info *pInfo = pCsr->aConstraint[i].pInfo;
+      if( pInfo ){
+        if( pInfo->xDelUser ) pInfo->xDelUser(pInfo->pUser);
+        sqlite3_free(pInfo);
       }
     }
     sqlite3_free(pCsr->aConstraint);
@@ -928,8 +930,8 @@ static int rtreeCallbackConstraint(
   int *peWithin                  /* OUT: visibility of the cell */
 ){
   int i;                                                /* Loop counter */
-  sqlite3_rtree_query_info *pGeom = pConstraint->pGeom; /* Callback info */
-  int nCoord = pGeom->nCoord;                           /* No. of coordinates */
+  sqlite3_rtree_query_info *pInfo = pConstraint->pInfo; /* Callback info */
+  int nCoord = pInfo->nCoord;                           /* No. of coordinates */
   int rc;                                             /* Callback return code */
   sqlite3_rtree_dbl aCoord[RTREE_MAX_DIMENSIONS*2];   /* Decoded coordinates */
 
@@ -941,17 +943,20 @@ static int rtreeCallbackConstraint(
     RTREE_DECODE_COORD(eInt, pCellData, aCoord[i]);
   }
   if( pConstraint->op==RTREE_MATCH ){
-    rc = pConstraint->u.xGeom((sqlite3_rtree_geometry*)pGeom,
+    rc = pConstraint->u.xGeom((sqlite3_rtree_geometry*)pInfo,
                               nCoord, aCoord, &i);
     if( i==0 ) *peWithin = NOT_WITHIN;
+    *prScore = RTREE_ZERO;
   }else{
-    pGeom->aCoord = aCoord;
-    pGeom->iLevel = pSearch->iLevel;
-    pGeom->rScore = pGeom->rParentScore = pSearch->rScore;
-    pGeom->eWithin = pGeom->eParentWithin = pSearch->eWithin;
-    rc = pConstraint->u.xQueryFunc(pGeom);
-    if( pGeom->eWithin<*peWithin ) *peWithin = pGeom->eWithin;
-    if( pGeom->rScore<*prScore ) *prScore = pGeom->rScore;
+    pInfo->aCoord = aCoord;
+    pInfo->iLevel = pSearch->iLevel;
+    pInfo->rScore = pInfo->rParentScore = pSearch->rScore;
+    pInfo->eWithin = pInfo->eParentWithin = pSearch->eWithin;
+    rc = pConstraint->u.xQueryFunc(pInfo);
+    if( pInfo->eWithin<*peWithin ) *peWithin = pInfo->eWithin;
+    if( pInfo->rScore<*prScore || *prScore<RTREE_ZERO ){
+      *prScore = pInfo->rScore;
+    }
   }
   return rc;
 }
@@ -1065,6 +1070,12 @@ static int nodeParentIndex(Rtree *pRtree, RtreeNode *pNode, int *piIndex){
 /*
 ** Compare two search points.  Return negative, zero, or positive if the first
 ** is less than, equal to, or greater than the second.
+**
+** The rScore is the primary key.  Smaller rScore values come first.
+** If the rScore is a tie, then use iLevel as the tie breaker with smaller
+** iLevel values coming first.  In this way, if rScore is the same for all
+** SearchPoints, then iLevel becomes the deciding factor and the result
+** is a depth-first search, which is the desired default behavior.
 */
 static int rtreeSearchPointCompare(
   const RtreeSearchPoint *pA,
@@ -1289,7 +1300,7 @@ static int rtreeStepToLeaf(RtreeCursor *pCur){
     nCell = NCELL(pNode);
     assert( nCell<200 );
     while( p->iCell<nCell ){
-      sqlite3_rtree_dbl rScore = (sqlite3_rtree_dbl)0;
+      sqlite3_rtree_dbl rScore = (sqlite3_rtree_dbl)-1;
       u8 *pCellData = pNode->zData + (4+pRtree->nBytesPerCell*p->iCell);
       eWithin = FULLY_WITHIN;
       for(ii=0; ii<nConstraint; ii++){
@@ -1319,6 +1330,7 @@ static int rtreeStepToLeaf(RtreeCursor *pCur){
         RTREE_QUEUE_TRACE(pCur, "POP-S:");
         rtreeSearchPointPop(pCur);
       }
+      if( rScore<RTREE_ZERO ) rScore = RTREE_ZERO;
       p = rtreeSearchPointNew(pCur, rScore, x.iLevel);
       if( p==0 ) return SQLITE_NOMEM;
       p->eWithin = eWithin;
@@ -1429,9 +1441,10 @@ static int findLeafNode(
 ** operator.
 */
 static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
-  RtreeMatchArg *p;
-  sqlite3_rtree_query_info *pGeom;
-  int nBlob;
+  RtreeMatchArg *pBlob;              /* BLOB returned by geometry function */
+  sqlite3_rtree_query_info *pInfo;   /* Callback information */
+  int nBlob;                         /* Size of the geometry function blob */
+  int nExpected;                     /* Expected size of the BLOB */
 
   /* Check that value is actually a blob. */
   if( sqlite3_value_type(pValue)!=SQLITE_BLOB ) return SQLITE_ERROR;
@@ -1444,24 +1457,29 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
     return SQLITE_ERROR;
   }
 
-  pGeom = (sqlite3_rtree_query_info*)sqlite3_malloc( sizeof(*pGeom)+nBlob );
-  if( !pGeom ) return SQLITE_NOMEM;
-  memset(pGeom, 0, sizeof(*pGeom));
-  p = (RtreeMatchArg*)&pGeom[1];
+  pInfo = (sqlite3_rtree_query_info*)sqlite3_malloc( sizeof(*pInfo)+nBlob );
+  if( !pInfo ) return SQLITE_NOMEM;
+  memset(pInfo, 0, sizeof(*pInfo));
+  pBlob = (RtreeMatchArg*)&pInfo[1];
 
-  memcpy(p, sqlite3_value_blob(pValue), nBlob);
-  if( p->magic!=RTREE_GEOMETRY_MAGIC 
-   || nBlob!=(int)(sizeof(RtreeMatchArg) + (p->nParam-1)*sizeof(RtreeDValue))
-  ){
-    sqlite3_free(pGeom);
+  memcpy(pBlob, sqlite3_value_blob(pValue), nBlob);
+  nExpected = (int)(sizeof(RtreeMatchArg) +
+                    (pBlob->nParam-1)*sizeof(RtreeDValue));
+  if( pBlob->magic!=RTREE_GEOMETRY_MAGIC || nBlob!=nExpected ){
+    sqlite3_free(pInfo);
     return SQLITE_ERROR;
   }
-  pGeom->pContext = p->cb.pContext;
-  pGeom->nParam = p->nParam;
-  pGeom->aParam = p->aParam;
+  pInfo->pContext = pBlob->cb.pContext;
+  pInfo->nParam = pBlob->nParam;
+  pInfo->aParam = pBlob->aParam;
 
-  pCons->u.xGeom = p->cb.xGeom;
-  pCons->pGeom = pGeom;
+  if( pBlob->cb.xGeom ){
+    pCons->u.xGeom = pBlob->cb.xGeom;
+  }else{
+    pCons->op = RTREE_QUERY;
+    pCons->u.xQueryFunc = pBlob->cb.xQueryFunc;
+  }
+  pCons->pInfo = pInfo;
   return SQLITE_OK;
 }
 
@@ -1493,7 +1511,7 @@ static int rtreeFilter(
     i64 iNode = 0;
     rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
     if( rc==SQLITE_OK && pLeaf!=0 ){
-      p = rtreeSearchPointNew(pCsr, 0.0, 0);
+      p = rtreeSearchPointNew(pCsr, RTREE_ZERO, 0);
       assert( p!=0 );  /* Always returns pCsr->sPoint */
       pCsr->aNode[0] = pLeaf;
       p->id = iNode;
@@ -1521,7 +1539,7 @@ static int rtreeFilter(
           RtreeConstraint *p = &pCsr->aConstraint[ii];
           p->op = idxStr[ii*2];
           p->iCoord = idxStr[ii*2+1]-'0';
-          if( p->op==RTREE_MATCH ){
+          if( p->op>=RTREE_MATCH ){
             /* A MATCH operator. The right-hand-side must be a blob that
             ** can be cast into an RtreeMatchArg object. One created using
             ** an sqlite3_rtree_geometry_callback() SQL user function.
@@ -1530,7 +1548,7 @@ static int rtreeFilter(
             if( rc!=SQLITE_OK ){
               break;
             }
-            p->pGeom->nCoord = pRtree->nDim*2;
+            p->pInfo->nCoord = pRtree->nDim*2;
           }else{
 #ifdef SQLITE_RTREE_INT_ONLY
             p->u.rValue = sqlite3_value_int64(argv[ii]);
@@ -1546,7 +1564,8 @@ static int rtreeFilter(
       rc = nodeAcquire(pRtree, 1, 0, &pRoot);
     }
     if( rc==SQLITE_OK ){
-      RtreeSearchPoint *pNew = rtreeSearchPointNew(pCsr, 0.0, pRtree->iDepth+1);
+      RtreeSearchPoint *pNew;
+      pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, pRtree->iDepth+1);
       if( pNew==0 ) return SQLITE_NOMEM;
       pNew->id = 1;
       pNew->iCell = 0;
@@ -1759,7 +1778,7 @@ static RtreeDValue cellOverlap(
   int nCell
 ){
   int ii;
-  RtreeDValue overlap = 0.0;
+  RtreeDValue overlap = RTREE_ZERO;
   for(ii=0; ii<nCell; ii++){
     int jj;
     RtreeDValue o = (RtreeDValue)1;
@@ -1799,8 +1818,8 @@ static int ChooseLeaf(
     int iCell;
     sqlite3_int64 iBest = 0;
 
-    RtreeDValue fMinGrowth = 0.0;
-    RtreeDValue fMinArea = 0.0;
+    RtreeDValue fMinGrowth = RTREE_ZERO;
+    RtreeDValue fMinArea = RTREE_ZERO;
 
     int nCell = NCELL(pNode);
     RtreeCell cell;
@@ -2050,7 +2069,7 @@ static int splitNodeStartree(
 
   int iBestDim = 0;
   int iBestSplit = 0;
-  RtreeDValue fBestMargin = 0.0;
+  RtreeDValue fBestMargin = RTREE_ZERO;
 
   int nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int));
 
@@ -2071,9 +2090,9 @@ static int splitNodeStartree(
   }
 
   for(ii=0; ii<pRtree->nDim; ii++){
-    RtreeDValue margin = 0.0;
-    RtreeDValue fBestOverlap = 0.0;
-    RtreeDValue fBestArea = 0.0;
+    RtreeDValue margin = RTREE_ZERO;
+    RtreeDValue fBestOverlap = RTREE_ZERO;
+    RtreeDValue fBestArea = RTREE_ZERO;
     int iBestLeft = 0;
     int nLeft;
 
@@ -2492,7 +2511,7 @@ static int Reinsert(
   }
 
   for(ii=0; ii<nCell; ii++){
-    aDistance[ii] = 0.0;
+    aDistance[ii] = RTREE_ZERO;
     for(iDim=0; iDim<pRtree->nDim; iDim++){
       RtreeDValue coord = (DCOORD(aCell[ii].aCoord[iDim*2+1]) - 
                                DCOORD(aCell[ii].aCoord[iDim*2]));
@@ -3299,8 +3318,8 @@ int sqlite3RtreeInit(sqlite3 *db){
 ** the corresponding SQL function is deleted.
 */
 static void rtreeFreeCallback(void *p){
-  RtreeGeomCallback *pGeom = (RtreeGeomCallback*)p;
-  if( pGeom->xDestructor ) pGeom->xDestructor(pGeom->pContext);
+  RtreeGeomCallback *pInfo = (RtreeGeomCallback*)p;
+  if( pInfo->xDestructor ) pInfo->xDestructor(pInfo->pContext);
   sqlite3_free(p);
 }
 
diff --git a/ext/rtree/rtreeE.test b/ext/rtree/rtreeE.test
new file mode 100644 (file)
index 0000000..14f5c33
--- /dev/null
@@ -0,0 +1,72 @@
+# 2010 August 28
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file contains tests for the r-tree module. Specifically, it tests
+# that new-style custom r-tree queries (geometry callbacks) work.
+# 
+
+if {![info exists testdir]} {
+  set testdir [file join [file dirname [info script]] .. .. test]
+} 
+source $testdir/tester.tcl
+ifcapable !rtree { finish_test ; return }
+ifcapable rtree_int_only { finish_test; return }
+
+
+#-------------------------------------------------------------------------
+# Test the example 2d "circle" geometry callback.
+#
+register_circle_geom db
+
+do_execsql_test rtreeE-1.1 {
+  PRAGMA page_size=512;
+  CREATE VIRTUAL TABLE rt2 USING rtree(id,x0,x1,y0,y1);
+  
+  /* A tight pattern of small boxes near 0,0 */
+  WITH RECURSIVE
+    x(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM x WHERE x<4),
+    y(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM y WHERE y<4)
+  INSERT INTO rt2 SELECT x+5*y, x, x+2, y, y+2 FROM x, y;
+
+  /* A looser pattern of small boxes near 100, 0 */
+  WITH RECURSIVE
+    x(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM x WHERE x<4),
+    y(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM y WHERE y<4)
+  INSERT INTO rt2 SELECT 100+x+5*y, x*3+100, x*3+102, y*3, y*3+2 FROM x, y;
+
+  /* A looser pattern of larger boxes near 0, 200 */
+  WITH RECURSIVE
+    x(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM x WHERE x<4),
+    y(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM y WHERE y<4)
+  INSERT INTO rt2 SELECT 200+x+5*y, x*7, x*7+15, y*7+200, y*7+215 FROM x, y;
+} {}
+
+if 0 {
+# Queries against each of the three clusters */
+do_execsql_test rtreeE-1.1 {
+  SELECT id FROM rt2 WHERE id MATCH Qcircle(0.0, 0.0, 50.0) ORDER BY id;
+} {0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24}
+do_execsql_test rtreeE-1.2 {
+  SELECT id FROM rt2 WHERE id MATCH Qcircle(100.0, 0.0, 50.0) ORDER BY id;
+} {100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124}
+do_execsql_test rtreeE-1.3 {
+  SELECT id FROM rt2 WHERE id MATCH Qcircle(0.0, 200.0, 50.0) ORDER BY id;
+} {200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224}
+}
+
+# The Qcircle geometry function gives a lower score to larger leaf-nodes.
+# This causes the 200s to sort before the 100s and the 0s to sort before
+# last.
+#
+do_execsql_test rtreeE-1.4 {
+  SELECT id FROM rt2 WHERE id MATCH Qcircle(0,0,1000) AND id%100==0
+} {200 100 0}
+
+finish_test
index d10804afcba12e49bbf9cd6d5e68c1540804deb7..63a2d6b9ce493e72568da37fa0cbab2961c216df 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Refactor\sthe\sconstraint\schecking\slogic\sin\sRTree.\s\sThe\snew-style\sconstraint\ncallbacks\screated\sby\ssqlite3_rtree_query_callback()\sare\snow\shooked\sup\sfrom\nend\sto\send,\sthough\sstill\suntested.
-D 2014-04-17T13:15:33.281
+C Test\scases\sand\sbug\sfixes\sfor\sthe\ssqlite3_rtree_query_callback()\nmechanism.
+D 2014-04-17T14:52:20.025
 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 117aaebed87a7c1e5d3e03afbad83c3700aa5ab8
+F ext/rtree/rtree.c 6a47918e44697dd32f5bba8a79d3490e56bd76c9
 F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
 F ext/rtree/rtree1.test cf679265ecafff494a768ac9c2f43a70915a6290
 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
@@ -135,6 +135,7 @@ F ext/rtree/rtreeA.test ace05e729a36e342d40cf94e9efc7b4723d9dcdf
 F ext/rtree/rtreeB.test c85f9ce78766c4e68b8b89fbf2979ee9cfa82b4e
 F ext/rtree/rtreeC.test df158dcc81f1a43ce7eef361af03c48ec91f1e06
 F ext/rtree/rtreeD.test 636630357638f5983701550b37f0f5867130d2ca
+F ext/rtree/rtreeE.test c8c951df54fd556d30eb621ecc2acd8771970a5e
 F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195
 F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea
 F ext/rtree/sqlite3rtree.h 488cf8834d2012b913d33683157d3cf5f1327a69
@@ -274,7 +275,7 @@ F src/test_osinst.c 90a845c8183013d80eccb1f29e8805608516edba
 F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00
 F src/test_quota.c 30c64f0ef84734f2231a686df41ed882b0c59bc0
 F src/test_quota.h 8761e463b25e75ebc078bd67d70e39b9c817a0cb
-F src/test_rtree.c cd35d54c0b847c0c373d66f91616c49697ab4edf
+F src/test_rtree.c 636d2a5bc9ded2fa1df4e7d4c575eb0d3f13b334
 F src/test_schema.c cd12a2223c3a394f4d07bb93bdf6d344c5c121b6
 F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe
 F src/test_sqllog.c c1c1bbedbcaf82b93d83e4f9dd990e62476a680e
@@ -1175,7 +1176,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
-P 5d20ff9ec837ad35bc44d6c25d13764b350e81dd
-R 9ddd572c495f4e9fca0e8e969ae19d88
+P 32a13870175a1dd1d33af3572dde09ff607a04b6
+R 1f38906450e19fda5c27a4543b70f18c
 U drh
-Z 43698a2882be63e69b530e3c48875378
+Z 26a5b86beaa061e155488cf6e80f8d30
index 5fbbb6cae9bb877239359a4e953dc5e813ba492d..07ccb102680fda0cd98a16b58f8217de7ccecb99 100644 (file)
@@ -1 +1 @@
-32a13870175a1dd1d33af3572dde09ff607a04b6
\ No newline at end of file
+1ccaaed6b516ec2ce953c1b31025a82ba76d00e7
\ No newline at end of file
index 3b4c205879d069512007b282f43b1a81e7028a59..c1951a896b24d5b0a5641fc5a41502061b6dd5ac 100644 (file)
@@ -35,6 +35,7 @@ struct Circle {
   double centerx;
   double centery;
   double radius;
+  double mxArea;
 };
 
 /*
@@ -58,7 +59,12 @@ static int circle_geom(
   double xmin, xmax;              /* X dimensions of box being tested */
   double ymin, ymax;              /* X dimensions of box being tested */
 
-  if( p->pUser==0 ){
+  xmin = aCoord[0];
+  xmax = aCoord[1];
+  ymin = aCoord[2];
+  ymax = aCoord[3];
+  pCircle = (Circle *)p->pUser;
+  if( pCircle==0 ){
     /* If pUser is still 0, then the parameter values have not been tested
     ** for correctness or stored into a Circle structure yet. Do this now. */
 
@@ -104,14 +110,9 @@ static int circle_geom(
     pCircle->aBox[1].xmax = pCircle->centerx - pCircle->radius;
     pCircle->aBox[1].ymin = pCircle->centery;
     pCircle->aBox[1].ymax = pCircle->centery;
+    pCircle->mxArea = (xmax - xmin)*(ymax - ymin) + 1.0;
   }
 
-  pCircle = (Circle *)p->pUser;
-  xmin = aCoord[0];
-  xmax = aCoord[1];
-  ymin = aCoord[2];
-  ymax = aCoord[3];
-
   /* Check if any of the 4 corners of the bounding-box being tested lie 
   ** inside the circular region. If they do, then the bounding-box does
   ** intersect the region of interest. Set the output variable to true and
@@ -161,7 +162,12 @@ static int circle_query_func(sqlite3_rtree_query_info *p){
   double ymin, ymax;              /* X dimensions of box being tested */
   int nWithin = 0;                /* Number of corners inside the circle */
 
-  if( p->pUser==0 ){
+  xmin = p->aCoord[0];
+  xmax = p->aCoord[1];
+  ymin = p->aCoord[2];
+  ymax = p->aCoord[3];
+  pCircle = (Circle *)p->pUser;
+  if( pCircle==0 ){
     /* If pUser is still 0, then the parameter values have not been tested
     ** for correctness or stored into a Circle structure yet. Do this now. */
 
@@ -207,14 +213,9 @@ static int circle_query_func(sqlite3_rtree_query_info *p){
     pCircle->aBox[1].xmax = pCircle->centerx - pCircle->radius;
     pCircle->aBox[1].ymin = pCircle->centery;
     pCircle->aBox[1].ymax = pCircle->centery;
+    pCircle->mxArea = 200.0*200.0;
   }
 
-  pCircle = (Circle *)p->pUser;
-  xmin = p->aCoord[0];
-  xmax = p->aCoord[1];
-  ymin = p->aCoord[2];
-  ymax = p->aCoord[3];
-
   /* Check if any of the 4 corners of the bounding-box being tested lie 
   ** inside the circular region. If they do, then the bounding-box does
   ** intersect the region of interest. Set the output variable to true and
@@ -246,7 +247,12 @@ static int circle_query_func(sqlite3_rtree_query_info *p){
     }
   }
 
-  p->rScore = p->iLevel;
+  if( p->iLevel==2 ){
+    p->rScore = 1.0 - (xmax-xmin)*(ymax-ymin)/pCircle->mxArea;
+    if( p->rScore<0.01 ) p->rScore = 0.01;
+  }else{
+    p->rScore = 0.0;
+  }
   if( nWithin==0 ){
     p->eWithin = NOT_WITHIN;
   }else if( nWithin>=4 ){