** 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 */
};
+/*
+** 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
#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
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;
}
}
/*
-** 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;
/*
** 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);
}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
** 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 */
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
);
** 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 */
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
);
-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
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
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
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
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 */
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.
**************************************************************************
*************************************************************************/
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;
}
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;