#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
/*
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 */
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);
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 */
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;
}
/*
** 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,
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++){
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;
** 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;
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;
}
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;
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.
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]);
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;
int nCell
){
int ii;
- RtreeDValue overlap = 0.0;
+ RtreeDValue overlap = RTREE_ZERO;
for(ii=0; ii<nCell; ii++){
int jj;
RtreeDValue o = (RtreeDValue)1;
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;
int iBestDim = 0;
int iBestSplit = 0;
- RtreeDValue fBestMargin = 0.0;
+ RtreeDValue fBestMargin = RTREE_ZERO;
int nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int));
}
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;
}
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]));
** 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);
}
--- /dev/null
+# 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
-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
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
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
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
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
double centerx;
double centery;
double radius;
+ double mxArea;
};
/*
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. */
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
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. */
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
}
}
- 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 ){