]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Continuing clean-up of the R-Tree module in preparation for cutting in the
authordrh <drh@noemail.net>
Sat, 12 Apr 2014 17:44:00 +0000 (17:44 +0000)
committerdrh <drh@noemail.net>
Sat, 12 Apr 2014 17:44:00 +0000 (17:44 +0000)
new generalized query mechanism.

FossilOrigin-Name: 66c858f20586424e15d0bfa3d7b56643bde66226

ext/rtree/rtree.c
manifest
manifest.uuid
src/test_rtree.c

index ddfc3d79a1cc38a9e7ed88e583bdd3f27d2ec107..e85fe4449d238b21632507a8bc3512ef61ccf944 100644 (file)
@@ -289,12 +289,12 @@ struct RtreeConstraint {
 ** An rtree structure node.
 */
 struct RtreeNode {
-  RtreeNode *pParent;          /* Parent node */
-  i64 iNode;                   /* The node number */
-  int nRef;                    /* Number of references to this node */
-  int isDirty;                 /* True if the node needs to be written to disk */
-  u8 *zData;                   /* Content of the node, as should be on disk */
-  RtreeNode *pNext;            /* Next node in this hash collision chain */
+  RtreeNode *pParent;         /* Parent node */
+  i64 iNode;                  /* The node number */
+  int nRef;                   /* Number of references to this node */
+  int isDirty;                /* True if the node needs to be written to disk */
+  u8 *zData;                  /* Content of the node, as should be on disk */
+  RtreeNode *pNext;           /* Next node in this hash collision chain */
 };
 
 /* Return the number of cells in a node  */
@@ -309,6 +309,28 @@ struct RtreeCell {
 };
 
 
+/*
+** This object becomes the sqlite3_user_data() for the SQL functions
+** that are created by sqlite3_rtree_geometry_callback() and
+** sqlite3_rtree_query_callback() and which appear on the right of MATCH
+** operators in order to constrain a search.
+**
+** xGeom and xQueryFunc are the callback functions.  Exactly one of 
+** xGeom and xQueryFunc fields is non-NULL, depending on whether the
+** SQL function was created using sqlite3_rtree_geometry_callback() or
+** sqlite3_rtree_query_callback().
+** 
+** This object is deleted automatically by the destructor mechanism in
+** sqlite3_create_function_v2().
+*/
+struct RtreeGeomCallback {
+  int (*xGeom)(sqlite3_rtree_geometry*, int, RtreeDValue*, int*);
+  int (*xQueryFunc)(sqlite3_rtree_query_info*);
+  void (*xDestructor)(void*);
+  void *pContext;
+};
+
+
 /*
 ** Value for the first field of every RtreeMatchArg object. The MATCH
 ** operator tests that the first field of a blob operand matches this
@@ -317,32 +339,16 @@ struct RtreeCell {
 #define RTREE_GEOMETRY_MAGIC 0x891245AB
 
 /*
-** An instance of this structure must be supplied as a blob argument to
-** the right-hand-side of an SQL MATCH operator used to constrain an
-** r-tree query.
+** An instance of this structure (in the form of a BLOB) is returned by
+** the SQL functions that sqlite3_rtree_geometry_callback() and
+** sqlite3_rtree_query_callback() create, and is read as the right-hand
+** operand to the MATCH operator of an R-Tree.
 */
 struct RtreeMatchArg {
-  u32 magic;                      /* Always RTREE_GEOMETRY_MAGIC */
-  int (*xGeom)(sqlite3_rtree_geometry *, int, RtreeDValue*, int *);
-  int (*xQueryFunc)(sqlite3_rtree_query_info*);
-  void *pContext;
-  int nParam;
-  RtreeDValue aParam[1];
-};
-
-/*
-** When a geometry callback is created using either
-** sqlite3_rtree_geometry_callback() or sqlite3_rtree_query_callback(),
-** a single instance of the following structure is allocated. It is used
-** as the context for the user-function created by sqlite3_rtree_*_callback().
-** The object is eventually deleted by the destructor mechanism provided by
-** sqlite3_create_function_v2().
-*/
-struct RtreeGeomCallback {
-  int (*xGeom)(sqlite3_rtree_geometry*, int, RtreeDValue*, int*);
-  int (*xQueryFunc)(sqlite3_rtree_query_info*);
-  void (*xDestructor)(void*);
-  void *pContext;
+  u32 magic;                  /* Always RTREE_GEOMETRY_MAGIC */
+  RtreeGeomCallback cb;       /* Info about the callback functions */
+  int nParam;                 /* Number of parameters to the SQL function */
+  RtreeDValue aParam[1];      /* Values for parameters to the SQL function */
 };
 
 #ifndef MAX
@@ -1253,11 +1259,11 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
     return SQLITE_ERROR;
   }
 
-  pGeom->pContext = p->pContext;
+  pGeom->pContext = p->cb.pContext;
   pGeom->nParam = p->nParam;
   pGeom->aParam = p->aParam;
 
-  pCons->xGeom = p->xGeom;
+  pCons->xGeom = p->cb.xGeom;
   pCons->pGeom = pGeom;
   return SQLITE_OK;
 }
@@ -3355,9 +3361,11 @@ int sqlite3RtreeInit(sqlite3 *db){
 }
 
 /*
-** This routine is called when a custom geometry function or custom query
-** function is cancelled.  The pointer is to a RtreeGeomCallback object
-** that needs to be freed.
+** This routine deletes the RtreeGeomCallback object that was attached
+** one of the SQL functions create by sqlite3_rtree_geometry_callback()
+** or sqlite3_rtree_query_callback().  In other words, this routine is the
+** destructor for an RtreeGeomCallback objecct.  This routine is called when
+** the corresponding SQL function is deleted.
 */
 static void rtreeFreeCallback(void *p){
   RtreeGeomCallback *pGeom = (RtreeGeomCallback*)p;
@@ -3368,10 +3376,16 @@ static void rtreeFreeCallback(void *p){
 /*
 ** Each call to sqlite3_rtree_geometry_callback() or
 ** sqlite3_rtree_query_callback() creates an ordinary SQLite
-** scalar user function that is implemented by this routine.
+** scalar function that is implemented by this routine.
 **
-** This function returns a blob that is interpreted by r-tree
-** table MATCH operator.
+** All this function does is construct an RtreeMatchArg object that
+** contains the geometry-checking callback routines and a list of
+** parameters to this function, then return that RtreeMatchArg object
+** as a BLOB.
+**
+** The R-Tree MATCH operator will read the returned BLOB, deserialize
+** the RtreeMatchArg object, and use the RtreeMatchArg object to figure
+** out which elements of the R-Tree should be returned by the query.
 */
 static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
   RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx);
@@ -3385,8 +3399,7 @@ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
   }else{
     int i;
     pBlob->magic = RTREE_GEOMETRY_MAGIC;
-    pBlob->xGeom = pGeomCtx->xGeom;
-    pBlob->pContext = pGeomCtx->pContext;
+    pBlob->cb = pGeomCtx[0];
     pBlob->nParam = nArg;
     for(i=0; i<nArg; i++){
 #ifdef SQLITE_RTREE_INT_ONLY
@@ -3403,10 +3416,10 @@ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
 ** Register a new geometry function for use with the r-tree MATCH operator.
 */
 int sqlite3_rtree_geometry_callback(
-  sqlite3 *db,
-  const char *zGeom,
-  int (*xGeom)(sqlite3_rtree_geometry *, int, RtreeDValue *, int *),
-  void *pContext
+  sqlite3 *db,                  /* Register SQL function on this connection */
+  const char *zGeom,            /* Name of the new SQL function */
+  int (*xGeom)(sqlite3_rtree_geometry*,int,RtreeDValue*,int*), /* Callback */
+  void *pContext                /* Extra data associated with the callback */
 ){
   RtreeGeomCallback *pGeomCtx;      /* Context object for new user-function */
 
@@ -3417,9 +3430,6 @@ int sqlite3_rtree_geometry_callback(
   pGeomCtx->xQueryFunc = 0;
   pGeomCtx->xDestructor = 0;
   pGeomCtx->pContext = pContext;
-
-  /* Create the new user-function. Register a destructor function to delete
-  ** the context object when it is no longer required.  */
   return sqlite3_create_function_v2(db, zGeom, -1, SQLITE_ANY, 
       (void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback
   );
@@ -3430,11 +3440,11 @@ int sqlite3_rtree_geometry_callback(
 ** r-tree MATCH operator.
 */
 int sqlite3_rtree_query_callback(
-  sqlite3 *db,
-  const char *zQueryFunc,
-  int (*xQueryFunc)(sqlite3_rtree_query_info*),
-  void *pContext,
-  void (*xDestructor)(void*)
+  sqlite3 *db,                 /* Register SQL function on this connection */
+  const char *zQueryFunc,      /* Name of new SQL function */
+  int (*xQueryFunc)(sqlite3_rtree_query_info*), /* Callback */
+  void *pContext,              /* Extra data passed into the callback */
+  void (*xDestructor)(void*)   /* Destructor for the extra data */
 ){
   RtreeGeomCallback *pGeomCtx;      /* Context object for new user-function */
 
@@ -3445,9 +3455,6 @@ int sqlite3_rtree_query_callback(
   pGeomCtx->xQueryFunc = xQueryFunc;
   pGeomCtx->xDestructor = xDestructor;
   pGeomCtx->pContext = pContext;
-
-  /* Create the new user-function. Register a destructor function to delete
-  ** the context object when it is no longer required.  */
   return sqlite3_create_function_v2(db, zQueryFunc, -1, SQLITE_ANY, 
       (void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback
   );
index af46986305cc75be89b3ea7f0fbc5e01f4ec157d..0f929e71554ffbac99208485f76a61bf639a5cbd 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sthe\snew\sinterfaces\sto\srtree,\sthough\sthey\sdo\snot\syet\swork.\s\sAdd\sthe\n"show_speedtest1_rtree.tcl"\sscript\sfor\sshowing\sthe\stest\sdata\sused\sfor\sthe\nR-Tree\stests\sof\sspeedtest1.\s\sChange\sspeedtest1\sto\sgenerate\sbetter\sR-Tree\ntest\sdata.
-D 2014-04-11T23:14:48.914
+C Continuing\sclean-up\sof\sthe\sR-Tree\smodule\sin\spreparation\sfor\scutting\sin\sthe\nnew\sgeneralized\squery\smechanism.
+D 2014-04-12T17:44:00.241
 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 541016f8a53bbedda4dc2deb0067f6955b0accb9
+F ext/rtree/rtree.c 8778f55ece9016ab3b17969f19f9656a06f6e100
 F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
 F ext/rtree/rtree1.test cf679265ecafff494a768ac9c2f43a70915a6290
 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
@@ -274,7 +274,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 f3d1d12538dccb75fd916e3fa58f250edbdd3b47
+F src/test_rtree.c cd35d54c0b847c0c373d66f91616c49697ab4edf
 F src/test_schema.c cd12a2223c3a394f4d07bb93bdf6d344c5c121b6
 F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe
 F src/test_sqllog.c c1c1bbedbcaf82b93d83e4f9dd990e62476a680e
@@ -1175,7 +1175,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 9d485c4207a81f32334857d4a608c5c511dd2b83
-R 0017d933bc77106350e8e33449b1c08c
+P 0b70275972c7a6a533566c1e50bffbf3ac531e95
+R 958d8acaf66c9f2ba2d270349d1218f7
 U drh
-Z 0d8113e9fad970081c8175cf29f2569d
+Z 02617e452696d44ca40af16d5a687407
index 1c530a4dc894ea5acfc9d59c23fef2b588bab66a..866d285af2bbd4d1e5d28c52f61c473a5b75db1d 100644 (file)
@@ -1 +1 @@
-0b70275972c7a6a533566c1e50bffbf3ac531e95
\ No newline at end of file
+66c858f20586424e15d0bfa3d7b56643bde66226
\ No newline at end of file
index e1966c243736b1c773c6fa515bdcee85063fbe57..3b4c205879d069512007b282f43b1a81e7028a59 100644 (file)
@@ -50,11 +50,7 @@ static void circle_del(void *p){
 static int circle_geom(
   sqlite3_rtree_geometry *p,
   int nCoord, 
-#ifdef SQLITE_RTREE_INT_ONLY
-  sqlite3_int64 *aCoord,
-#else
-  double *aCoord, 
-#endif
+  sqlite3_rtree_dbl *aCoord,
   int *pRes
 ){
   int i;                          /* Iterator variable */
@@ -154,6 +150,113 @@ static int circle_geom(
   return SQLITE_OK;
 }
 
+/*
+** Implementation of "circle" r-tree geometry callback using the 
+** 2nd-generation interface that allows scoring.
+*/
+static int circle_query_func(sqlite3_rtree_query_info *p){
+  int i;                          /* Iterator variable */
+  Circle *pCircle;                /* Structure defining circular region */
+  double xmin, xmax;              /* X dimensions of box being tested */
+  double ymin, ymax;              /* X dimensions of box being tested */
+  int nWithin = 0;                /* Number of corners inside the circle */
+
+  if( p->pUser==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. */
+
+    /* This geometry callback is for use with a 2-dimensional r-tree table.
+    ** Return an error if the table does not have exactly 2 dimensions. */
+    if( p->nCoord!=4 ) return SQLITE_ERROR;
+
+    /* Test that the correct number of parameters (3) have been supplied,
+    ** and that the parameters are in range (that the radius of the circle 
+    ** radius is greater than zero). */
+    if( p->nParam!=3 || p->aParam[2]<0.0 ) return SQLITE_ERROR;
+
+    /* Allocate a structure to cache parameter data in. Return SQLITE_NOMEM
+    ** if the allocation fails. */
+    pCircle = (Circle *)(p->pUser = sqlite3_malloc(sizeof(Circle)));
+    if( !pCircle ) return SQLITE_NOMEM;
+    p->xDelUser = circle_del;
+
+    /* Record the center and radius of the circular region. One way that
+    ** tested bounding boxes that intersect the circular region are detected
+    ** is by testing if each corner of the bounding box lies within radius
+    ** units of the center of the circle. */
+    pCircle->centerx = p->aParam[0];
+    pCircle->centery = p->aParam[1];
+    pCircle->radius = p->aParam[2];
+
+    /* Define two bounding box regions. The first, aBox[0], extends to
+    ** infinity in the X dimension. It covers the same range of the Y dimension
+    ** as the circular region. The second, aBox[1], extends to infinity in
+    ** the Y dimension and is constrained to the range of the circle in the
+    ** X dimension.
+    **
+    ** Then imagine each box is split in half along its short axis by a line
+    ** that intersects the center of the circular region. A bounding box
+    ** being tested can be said to intersect the circular region if it contains
+    ** points from each half of either of the two infinite bounding boxes.
+    */
+    pCircle->aBox[0].xmin = pCircle->centerx;
+    pCircle->aBox[0].xmax = pCircle->centerx;
+    pCircle->aBox[0].ymin = pCircle->centery + pCircle->radius;
+    pCircle->aBox[0].ymax = pCircle->centery - pCircle->radius;
+    pCircle->aBox[1].xmin = pCircle->centerx + pCircle->radius;
+    pCircle->aBox[1].xmax = pCircle->centerx - pCircle->radius;
+    pCircle->aBox[1].ymin = pCircle->centery;
+    pCircle->aBox[1].ymax = pCircle->centery;
+  }
+
+  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
+  ** return SQLITE_OK in this case. */
+  for(i=0; i<4; i++){
+    double x = (i&0x01) ? xmax : xmin;
+    double y = (i&0x02) ? ymax : ymin;
+    double d2;
+    
+    d2  = (x-pCircle->centerx)*(x-pCircle->centerx);
+    d2 += (y-pCircle->centery)*(y-pCircle->centery);
+    if( d2<(pCircle->radius*pCircle->radius) ) nWithin++;
+  }
+
+  /* Check if the bounding box covers any other part of the circular region.
+  ** See comments above for a description of how this test works. If it does
+  ** cover part of the circular region, set the output variable to true
+  ** and return SQLITE_OK. */
+  if( nWithin==0 ){
+    for(i=0; i<2; i++){
+      if( xmin<=pCircle->aBox[i].xmin 
+       && xmax>=pCircle->aBox[i].xmax 
+       && ymin<=pCircle->aBox[i].ymin 
+       && ymax>=pCircle->aBox[i].ymax 
+      ){
+        nWithin = 1;
+        break;
+      }
+    }
+  }
+
+  p->rScore = p->iLevel;
+  if( nWithin==0 ){
+    p->eWithin = NOT_WITHIN;
+  }else if( nWithin>=4 ){
+    p->eWithin = FULLY_WITHIN;
+  }else{
+    p->eWithin = PARTLY_WITHIN;
+  }
+  return SQLITE_OK;
+}
+
 /* END of implementation of "circle" geometry callback.
 **************************************************************************
 *************************************************************************/
@@ -194,11 +297,7 @@ static int gHere = 42;
 static int cube_geom(
   sqlite3_rtree_geometry *p,
   int nCoord,
-#ifdef SQLITE_RTREE_INT_ONLY
-  sqlite3_int64 *aCoord, 
-#else
-  double *aCoord, 
-#endif
+  sqlite3_rtree_dbl *aCoord,
   int *piRes
 ){
   Cube *pCube = (Cube *)p->pUser;
@@ -293,6 +392,10 @@ static int register_circle_geom(
   }
   if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
   rc = sqlite3_rtree_geometry_callback(db, "circle", circle_geom, 0);
+  if( rc==SQLITE_OK ){
+    rc = sqlite3_rtree_query_callback(db, "Qcircle",
+                                      circle_query_func, 0, 0);
+  }
   Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC);
 #endif
   return TCL_OK;