From dcc5c7b2fcdfbc89bea36650cdbaf454217783bb Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 11 Apr 2014 16:14:54 +0000 Subject: [PATCH 01/21] Update comments in the R-Tree module in preparation for some big changes. Add an "rtree" performance test to speedtest1. FossilOrigin-Name: 20a73ec0b2f56609a4504052e198e32803d30207 --- ext/rtree/rtree.c | 102 +++++++++++++++++++++++----------------------- manifest | 17 ++++---- manifest.uuid | 2 +- test/speedtest1.c | 97 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 158 insertions(+), 60 deletions(-) diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index cefb9a8b2a..2034ff693f 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -110,6 +110,7 @@ #include "sqlite3rtree.h" typedef sqlite3_int64 i64; typedef unsigned char u8; +typedef unsigned short u16; typedef unsigned int u32; #endif @@ -135,7 +136,7 @@ typedef union RtreeCoord RtreeCoord; ** ever contain very many entries, so a fixed number of buckets is ** used. */ -#define HASHSIZE 128 +#define HASHSIZE 97 /* The xBestIndex method of this virtual table requires an estimate of ** the number of rows in the virtual table to calculate the costs of @@ -151,15 +152,15 @@ typedef union RtreeCoord RtreeCoord; ** An rtree virtual-table object. */ struct Rtree { - sqlite3_vtab base; + sqlite3_vtab base; /* Base class. Must be first */ sqlite3 *db; /* Host database connection */ int iNodeSize; /* Size in bytes of each node in the node table */ - int nDim; /* Number of dimensions */ - int nBytesPerCell; /* Bytes consumed per cell */ + u8 nDim; /* Number of dimensions */ + u8 eCoordType; /* RTREE_COORD_REAL32 or RTREE_COORD_INT32 */ + u8 nBytesPerCell; /* Bytes consumed per cell */ int iDepth; /* Current depth of the r-tree structure */ char *zDb; /* Name of database containing r-tree table */ char *zName; /* Name of r-tree table */ - RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */ int nBusy; /* Current number of users of this structure */ i64 nRowEst; /* Estimated number of rows in this table */ @@ -186,10 +187,10 @@ struct Rtree { sqlite3_stmt *pWriteParent; sqlite3_stmt *pDeleteParent; - int eCoordType; + RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */ }; -/* Possible values for eCoordType: */ +/* Possible values for Rtree.eCoordType: */ #define RTREE_COORD_REAL32 0 #define RTREE_COORD_INT32 1 @@ -232,7 +233,7 @@ struct Rtree { ** An rtree cursor object. */ struct RtreeCursor { - sqlite3_vtab_cursor base; + 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 */ int iStrategy; /* Copy of idxNum search parameter */ @@ -240,9 +241,13 @@ struct RtreeCursor { RtreeConstraint *aConstraint; /* Search constraints. */ }; +/* +** 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. +*/ union RtreeCoord { - RtreeValue f; - int i; + RtreeValue f; /* Floating point value */ + int i; /* Integer value */ }; /* @@ -284,21 +289,23 @@ struct RtreeConstraint { ** An rtree structure node. */ struct RtreeNode { - RtreeNode *pParent; /* Parent node */ - i64 iNode; - int nRef; - int isDirty; - u8 *zData; - RtreeNode *pNext; /* Next node in this hash 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 */ #define NCELL(pNode) readInt16(&(pNode)->zData[2]) /* -** Structure to store a deserialized rtree record. +** A single cell from a node, deserialized */ struct RtreeCell { - i64 iRowid; - RtreeCoord aCoord[RTREE_MAX_DIMENSIONS*2]; + i64 iRowid; /* Node or entry ID */ + RtreeCoord aCoord[RTREE_MAX_DIMENSIONS*2]; /* Bounding box coordinates */ }; @@ -426,10 +433,7 @@ static void nodeZero(Rtree *pRtree, RtreeNode *p){ ** in the Rtree.aHash table. */ static int nodeHash(i64 iNode){ - return ( - (iNode>>56) ^ (iNode>>48) ^ (iNode>>40) ^ (iNode>>32) ^ - (iNode>>24) ^ (iNode>>16) ^ (iNode>> 8) ^ (iNode>> 0) - ) % HASHSIZE; + return iNode % HASHSIZE; } /* @@ -489,8 +493,7 @@ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){ /* ** Obtain a reference to an r-tree node. */ -static int -nodeAcquire( +static int nodeAcquire( Rtree *pRtree, /* R-tree structure */ i64 iNode, /* Node number to load */ RtreeNode *pParent, /* Either the parent node or NULL */ @@ -579,10 +582,10 @@ nodeAcquire( ** Overwrite cell iCell of node pNode with the contents of pCell. */ static void nodeOverwriteCell( - Rtree *pRtree, - RtreeNode *pNode, - RtreeCell *pCell, - int iCell + Rtree *pRtree, /* The overall R-Tree */ + RtreeNode *pNode, /* The node into which the cell is to be written */ + RtreeCell *pCell, /* The cell to write */ + int iCell /* Index into pNode into which pCell is written */ ){ int ii; u8 *p = &pNode->zData[4 + pRtree->nBytesPerCell*iCell]; @@ -594,7 +597,7 @@ static void nodeOverwriteCell( } /* -** Remove cell the cell with index iCell from node pNode. +** Remove the cell with index iCell from node pNode. */ static void nodeDeleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell){ u8 *pDst = &pNode->zData[4 + pRtree->nBytesPerCell*iCell]; @@ -611,11 +614,10 @@ static void nodeDeleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell){ ** ** If there is not enough free space in pNode, return SQLITE_FULL. */ -static int -nodeInsertCell( - Rtree *pRtree, - RtreeNode *pNode, - RtreeCell *pCell +static int nodeInsertCell( + Rtree *pRtree, /* The overall R-Tree */ + RtreeNode *pNode, /* Write new cell into this node */ + RtreeCell *pCell /* The cell to be inserted */ ){ int nCell; /* Current number of cells in pNode */ int nMaxCell; /* Maximum number of cells for pNode */ @@ -636,8 +638,7 @@ nodeInsertCell( /* ** If the node is dirty, write it out to the database. */ -static int -nodeWrite(Rtree *pRtree, RtreeNode *pNode){ +static int nodeWrite(Rtree *pRtree, RtreeNode *pNode){ int rc = SQLITE_OK; if( pNode->isDirty ){ sqlite3_stmt *p = pRtree->pWriteNode; @@ -662,8 +663,7 @@ nodeWrite(Rtree *pRtree, RtreeNode *pNode){ ** Release a reference to a node. If the node is dirty and the reference ** count drops to zero, the node data is written to the database. */ -static int -nodeRelease(Rtree *pRtree, RtreeNode *pNode){ +static int nodeRelease(Rtree *pRtree, RtreeNode *pNode){ int rc = SQLITE_OK; if( pNode ){ assert( pNode->nRef>0 ); @@ -691,9 +691,9 @@ nodeRelease(Rtree *pRtree, RtreeNode *pNode){ ** an internal node, then the 64-bit integer is a child page number. */ static i64 nodeGetRowid( - Rtree *pRtree, - RtreeNode *pNode, - int iCell + Rtree *pRtree, /* The overall R-Tree */ + RtreeNode *pNode, /* The node from which to extract the ID */ + int iCell /* The cell index from which to extract the ID */ ){ assert( iCellzData[4 + pRtree->nBytesPerCell*iCell]); @@ -703,11 +703,11 @@ static i64 nodeGetRowid( ** Return coordinate iCoord from cell iCell in node pNode. */ static void nodeGetCoord( - Rtree *pRtree, - RtreeNode *pNode, - int iCell, - int iCoord, - RtreeCoord *pCoord /* Space to write result to */ + Rtree *pRtree, /* The overall R-Tree */ + RtreeNode *pNode, /* The node from which to extract a coordinate */ + int iCell, /* The index of the cell within the node */ + int iCoord, /* Which coordinate to extract */ + RtreeCoord *pCoord /* OUT: Space to write result to */ ){ readCoord(&pNode->zData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord); } @@ -717,10 +717,10 @@ static void nodeGetCoord( ** to by pCell with the results. */ static void nodeGetCell( - Rtree *pRtree, - RtreeNode *pNode, - int iCell, - RtreeCell *pCell + Rtree *pRtree, /* The overall R-Tree */ + RtreeNode *pNode, /* The node containing the cell to be read */ + int iCell, /* Index of the cell within the node */ + RtreeCell *pCell /* OUT: Write the cell contents here */ ){ int ii; pCell->iRowid = nodeGetRowid(pRtree, pNode, iCell); diff --git a/manifest b/manifest index 0602716bed..1a7e491bc3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sall\srecent\schanges\sfrom\strunk,\nincluding\sthe\sfix\sfor\sthe\sOP_SCopy-vs-OP_Copy\sproblem. -D 2014-04-03T16:35:33.203 +C Update\scomments\sin\sthe\sR-Tree\smodule\sin\spreparation\sfor\ssome\sbig\schanges.\nAdd\san\s"rtree"\sperformance\stest\sto\sspeedtest1. +D 2014-04-11T16:14:54.092 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 2d9f95da404d850474e628c720c5ce15d29b47de +F ext/rtree/rtree.c 043f16a6c3f823c760657b975f2ff1de7fe5c197 F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree1.test cf679265ecafff494a768ac9c2f43a70915a6290 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba @@ -840,7 +840,7 @@ F test/speed3.test d32043614c08c53eafdc80f33191d5bd9b920523 F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715 F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b -F test/speedtest1.c 90446861e566a9965a8d005381a3c964ff333646 +F test/speedtest1.c 3e06b48e8c578eaa61a01e8c7c4d8734c9dd3776 F test/spellfix.test 61309f5efbec53603b3f86457d34a504f80abafe F test/sqllimits1.test b1aae27cc98eceb845e7f7adf918561256e31298 F test/stat.test 76fd746b85459e812a0193410fb599f0531f22de @@ -1174,7 +1174,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 fc8ca1a87e7127bd28f32fd809aec3e24355fbfa d5513dfa23baa0b0a095aaf17d19aacd30dcef61 -R 507e4fb477cf51261d2563e52f7cfa77 +P 9515c8344a6743bbb0c6a6e49fb79fb3139090df +R 57f2249bba89c44cd5839664eafe6658 +T *branch * rtree-enhancements +T *sym-rtree-enhancements * +T -sym-sessions * U drh -Z a91c37323943563a6d3bfb60a104e003 +Z bdd5cbefa0a4dcc9a9cc6ed610b85f71 diff --git a/manifest.uuid b/manifest.uuid index 7a0e457ea7..d8135897dd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9515c8344a6743bbb0c6a6e49fb79fb3139090df \ No newline at end of file +20a73ec0b2f56609a4504052e198e32803d30207 \ No newline at end of file diff --git a/test/speedtest1.c b/test/speedtest1.c index 28c24e0885..a32f12f923 100644 --- a/test/speedtest1.c +++ b/test/speedtest1.c @@ -931,6 +931,98 @@ void testset_cte(void){ } +/* Generate two numbers between 1 and mx. The first number is less than +** the second. Usually the numbers are near each other but can sometimes +** be far apart. +*/ +static void twoCoords( + unsigned mx, /* Range of 1..mx */ + unsigned *pX0, unsigned *pX1 /* OUT: write results here */ +){ + unsigned d, x0, x1, span; + + span = mx/100 + 1; + if( speedtest1_random()%3==0 ) span *= 3; + if( speedtest1_random()%17==0 ) span = mx - 5; + d = speedtest1_random()%span + 1; + x0 = speedtest1_random()%(mx-d) + 1; + x1 = x0 + d; + *pX0 = x0; + *pX1 = x1; +} + +/* +** A testset for the R-Tree virtual table +*/ +void testset_rtree(void){ + unsigned i, n; + unsigned mxCoord; + unsigned x0, x1, y0, y1, z0, z1; + + mxCoord = g.szTest*600; + n = g.szTest*100; + speedtest1_begin_test(100, "%d INSERTs into an r-tree", n); + speedtest1_exec("BEGIN"); + speedtest1_exec("CREATE VIRTUAL TABLE rt1 USING rtree(id,x0,x1,y0,y1,z0,z1)"); + speedtest1_prepare("INSERT INTO rt1(id,x0,x1,y0,y1,z0,z1)" + "VALUES(?1,?2,?3,?4,?5,?6,?7)"); + for(i=1; i<=n; i++){ + twoCoords(mxCoord, &x0, &x1); + twoCoords(mxCoord, &y0, &y1); + twoCoords(mxCoord, &z0, &z1); + sqlite3_bind_int(g.pStmt, 1, i); + sqlite3_bind_int(g.pStmt, 2, x0); + sqlite3_bind_int(g.pStmt, 3, x1); + sqlite3_bind_int(g.pStmt, 4, y0); + sqlite3_bind_int(g.pStmt, 5, y1); + sqlite3_bind_int(g.pStmt, 6, z0); + sqlite3_bind_int(g.pStmt, 7, z1); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + n = g.szTest*20; + speedtest1_begin_test(110, "%d one-dimensional intersect slice queries", n); + speedtest1_prepare("SELECT count(*) FROM rt1 WHERE x0>=?1 AND x1<=?2"); + for(i=0; i=?1 AND y0<=?2"); + for(i=0; i=?1 AND x0<=?2" + " AND y1>=?1 AND y0<=?2 AND z1>=?1 AND z0<=?2"); + for(i=0; i Date: Fri, 11 Apr 2014 17:41:08 +0000 Subject: [PATCH 02/21] Add the --verify option to speedtest1. Add verification test cases to the "rtree" testset and a case that uses a custom geometry callback. FossilOrigin-Name: 9d485c4207a81f32334857d4a608c5c511dd2b83 --- manifest | 15 +++--- manifest.uuid | 2 +- test/speedtest1.c | 116 ++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 109 insertions(+), 24 deletions(-) diff --git a/manifest b/manifest index 1a7e491bc3..12cad8c970 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\scomments\sin\sthe\sR-Tree\smodule\sin\spreparation\sfor\ssome\sbig\schanges.\nAdd\san\s"rtree"\sperformance\stest\sto\sspeedtest1. -D 2014-04-11T16:14:54.092 +C Add\sthe\s--verify\soption\sto\sspeedtest1.\s\sAdd\sverification\stest\scases\sto\nthe\s"rtree"\stestset\sand\sa\scase\sthat\suses\sa\scustom\sgeometry\scallback. +D 2014-04-11T17:41:08.614 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in e4ee6d36cdf6136aee0158675a3b24dd3bf31a5a F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -840,7 +840,7 @@ F test/speed3.test d32043614c08c53eafdc80f33191d5bd9b920523 F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715 F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b -F test/speedtest1.c 3e06b48e8c578eaa61a01e8c7c4d8734c9dd3776 +F test/speedtest1.c 017473605f9df5f4770dd60ddb4038e6d6829916 F test/spellfix.test 61309f5efbec53603b3f86457d34a504f80abafe F test/sqllimits1.test b1aae27cc98eceb845e7f7adf918561256e31298 F test/stat.test 76fd746b85459e812a0193410fb599f0531f22de @@ -1174,10 +1174,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 9515c8344a6743bbb0c6a6e49fb79fb3139090df -R 57f2249bba89c44cd5839664eafe6658 -T *branch * rtree-enhancements -T *sym-rtree-enhancements * -T -sym-sessions * +P 20a73ec0b2f56609a4504052e198e32803d30207 +R 47999e8bbddb67bbaef79a4f5741e58b U drh -Z bdd5cbefa0a4dcc9a9cc6ed610b85f71 +Z 73dffdddfd99e7f86f5cf9f646a8ff0c diff --git a/manifest.uuid b/manifest.uuid index d8135897dd..0d054cdd30 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -20a73ec0b2f56609a4504052e198e32803d30207 \ No newline at end of file +9d485c4207a81f32334857d4a608c5c511dd2b83 \ No newline at end of file diff --git a/test/speedtest1.c b/test/speedtest1.c index a32f12f923..0680e9599f 100644 --- a/test/speedtest1.c +++ b/test/speedtest1.c @@ -29,6 +29,7 @@ static const char zHelp[] = " --trace Turn on SQL tracing\n" " --utf16be Set text encoding to UTF-16BE\n" " --utf16le Set text encoding to UTF-16LE\n" + " --verify Run additional verification steps.\n" " --without-rowid Use WITHOUT ROWID where appropriate\n" ; @@ -51,6 +52,7 @@ static struct Global { int bReprepare; /* True to reprepare the SQL on each rerun */ int bSqlOnly; /* True to print the SQL once only */ int bExplain; /* Print SQL with EXPLAIN prefix */ + int bVerify; /* Try to verify that results are correct */ int szTest; /* Scale factor for test iterations */ const char *zWR; /* Might be WITHOUT ROWID */ const char *zNN; /* Might be NOT NULL */ @@ -951,6 +953,26 @@ static void twoCoords( *pX1 = x1; } +/* The following routine is an R-Tree geometry callback. It returns +** true if the object overlaps a slice on the Y coordinate between the +** two values given as arguments. In other words +** +** SELECT count(*) FROM rt1 WHERE id MATCH xslice(10,20); +** +** Is the same as saying: +** +** SELECT count(*) FROM rt1 WHERE y1>=10 AND y0<=20; +*/ +static int xsliceGeometryCallback( + sqlite3_rtree_geometry *p, + int nCoord, + double *aCoord, + int *pRes +){ + *pRes = aCoord[3]>=p->aParam[0] && aCoord[2]<=p->aParam[1]; + return SQLITE_OK; +} + /* ** A testset for the R-Tree virtual table */ @@ -958,6 +980,8 @@ void testset_rtree(void){ unsigned i, n; unsigned mxCoord; unsigned x0, x1, y0, y1, z0, z1; + unsigned iStep; + int *aCheck = sqlite3_malloc( sizeof(int)*g.szTest*100 ); mxCoord = g.szTest*600; n = g.szTest*100; @@ -982,23 +1006,83 @@ void testset_rtree(void){ speedtest1_exec("COMMIT"); speedtest1_end_test(); - n = g.szTest*20; - speedtest1_begin_test(110, "%d one-dimensional intersect slice queries", n); - speedtest1_prepare("SELECT count(*) FROM rt1 WHERE x0>=?1 AND x1<=?2"); - for(i=0; i=?1 AND x1<=?2"); + iStep = mxCoord/n; + for(i=0; i=?1 AND x1<=?2"); + iStep = mxCoord/n; + for(i=0; i=?1 AND y0<=?2"); - for(i=0; i=?1 AND y0<=?2"); + iStep = mxCoord/n; + for(i=0; i=?1 AND x0<=?2" " AND y1>=?1 AND y0<=?2 AND z1>=?1 AND z0<=?2"); - for(i=0; i Date: Fri, 11 Apr 2014 23:14:48 +0000 Subject: [PATCH 03/21] Add the new interfaces to rtree, though they do not yet work. Add the "show_speedtest1_rtree.tcl" script for showing the test data used for the R-Tree tests of speedtest1. Change speedtest1 to generate better R-Tree test data. FossilOrigin-Name: 0b70275972c7a6a533566c1e50bffbf3ac531e95 --- ext/rtree/rtree.c | 68 +++++++++++++++++++++++++--------- ext/rtree/sqlite3rtree.h | 61 +++++++++++++++++++++++++++--- main.mk | 2 +- manifest | 19 +++++----- manifest.uuid | 2 +- test/show_speedtest1_rtree.tcl | 57 ++++++++++++++++++++++++++++ test/speedtest1.c | 17 +++++---- 7 files changed, 184 insertions(+), 42 deletions(-) create mode 100644 test/show_speedtest1_rtree.tcl diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 2034ff693f..ddfc3d79a1 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -324,21 +324,24 @@ struct RtreeCell { 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 (see sqlite3_rtree_geometry_callback), +** 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 by s_r_g_c(). The object -** is eventually deleted by the destructor mechanism provided by -** sqlite3_create_function_v2() (which is called by s_r_g_c() to create -** the geometry callback function). +** 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; }; @@ -3352,22 +3355,23 @@ int sqlite3RtreeInit(sqlite3 *db){ } /* -** A version of sqlite3_free() that can be used as a callback. This is used -** in two places - as the destructor for the blob value returned by the -** invocation of a geometry function, and as the destructor for the geometry -** functions themselves. +** 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. */ -static void doSqlite3Free(void *p){ +static void rtreeFreeCallback(void *p){ + RtreeGeomCallback *pGeom = (RtreeGeomCallback*)p; + if( pGeom->xDestructor ) pGeom->xDestructor(pGeom->pContext); sqlite3_free(p); } /* -** Each call to sqlite3_rtree_geometry_callback() creates an ordinary SQLite -** scalar user function. This C function is the callback used for all such -** registered SQL functions. +** 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. ** -** The scalar user functions return a blob that is interpreted by r-tree -** table MATCH operators. +** This function returns a blob that is interpreted by r-tree +** table MATCH operator. */ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){ RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx); @@ -3391,7 +3395,7 @@ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){ pBlob->aParam[i] = sqlite3_value_double(aArg[i]); #endif } - sqlite3_result_blob(ctx, pBlob, nBlob, doSqlite3Free); + sqlite3_result_blob(ctx, pBlob, nBlob, sqlite3_free); } } @@ -3410,12 +3414,42 @@ int sqlite3_rtree_geometry_callback( pGeomCtx = (RtreeGeomCallback *)sqlite3_malloc(sizeof(RtreeGeomCallback)); if( !pGeomCtx ) return SQLITE_NOMEM; pGeomCtx->xGeom = xGeom; + 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, doSqlite3Free + (void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback + ); +} + +/* +** Register a new 2nd-generation geometry function for use with the +** 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*) +){ + RtreeGeomCallback *pGeomCtx; /* Context object for new user-function */ + + /* Allocate and populate the context object. */ + pGeomCtx = (RtreeGeomCallback *)sqlite3_malloc(sizeof(RtreeGeomCallback)); + if( !pGeomCtx ) return SQLITE_NOMEM; + pGeomCtx->xGeom = 0; + 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 ); } diff --git a/ext/rtree/sqlite3rtree.h b/ext/rtree/sqlite3rtree.h index c849091f29..79d1ffeede 100644 --- a/ext/rtree/sqlite3rtree.h +++ b/ext/rtree/sqlite3rtree.h @@ -21,6 +21,16 @@ extern "C" { #endif typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry; +typedef struct sqlite3_rtree_query_info sqlite3_rtree_query_info; + +/* The double-precision datatype used by RTree depends on the +** SQLITE_RTREE_INT_ONLY compile-time option. +*/ +#ifdef SQLITE_RTREE_INT_ONLY + typedef sqlite3_int64 sqlite3_rtree_dbl; +#else + typedef double sqlite3_rtree_dbl; +#endif /* ** Register a geometry callback named zGeom that can be used as part of an @@ -31,11 +41,7 @@ typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry; int sqlite3_rtree_geometry_callback( sqlite3 *db, const char *zGeom, -#ifdef SQLITE_RTREE_INT_ONLY - int (*xGeom)(sqlite3_rtree_geometry*, int n, sqlite3_int64 *a, int *pRes), -#else - int (*xGeom)(sqlite3_rtree_geometry*, int n, double *a, int *pRes), -#endif + int (*xGeom)(sqlite3_rtree_geometry*, int, sqlite3_rtree_dbl*,int*), void *pContext ); @@ -47,11 +53,54 @@ int sqlite3_rtree_geometry_callback( struct sqlite3_rtree_geometry { void *pContext; /* Copy of pContext passed to s_r_g_c() */ int nParam; /* Size of array aParam[] */ - double *aParam; /* Parameters passed to SQL geom function */ + sqlite3_rtree_dbl *aParam; /* Parameters passed to SQL geom function */ void *pUser; /* Callback implementation user data */ void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */ }; +/* +** Register a 2nd-generation geometry callback named zScore that can be +** used as part of an R-Tree geometry query as follows: +** +** SELECT ... FROM WHERE MATCH $zQueryFunc(... params ...) +*/ +int sqlite3_rtree_query_callback( + sqlite3 *db, + const char *zQueryFunc, + int (*xQueryFunc)(sqlite3_rtree_query_info*), + void *pContext, + void (*xDestructor)(void*) +); + + +/* +** A pointer to a structure of the following type is passed as the +** argument to scored geometry callback registered using +** sqlite3_rtree_query_callback(). +*/ +struct sqlite3_rtree_query_info { + void *pContext; /* pContext from when function registered */ + int nParam; /* Number of function parameters */ + sqlite3_rtree_dbl *aParam; /* value of function parameters */ + void *pUser; /* callback can use this, if desired */ + void (*xDelUser)(void*); /* function to free pUser */ + sqlite3_rtree_dbl *aCoord; /* Coordinates of node or entry to check */ + int nCoord; /* Number of coordinates */ + int iLevel; /* Level of current node or entry */ + sqlite3_int64 iRowid; /* Rowid for current entry */ + sqlite3_rtree_dbl rParentScore; /* Score of parent node */ + int eParentWithin; /* Visibility of parent node */ + int eWithin; /* OUT: Visiblity */ + sqlite3_rtree_dbl rScore; /* OUT: Write the score here */ +}; + +/* +** Allowed values for sqlite3_rtree_query.eWithin and .eParentWithin. +*/ +#define NOT_WITHIN 0 /* Object completely outside of query region */ +#define PARTLY_WITHIN 1 /* Object partially overlaps query region */ +#define FULLY_WITHIN 2 /* Object fully contained within query region */ + #ifdef __cplusplus } /* end of the 'extern "C"' block */ diff --git a/main.mk b/main.mk index d7f8aaa3dd..57757ccc6a 100644 --- a/main.mk +++ b/main.mk @@ -483,7 +483,7 @@ parse.c: $(TOP)/src/parse.y lemon $(TOP)/addopcodes.awk mv parse.h parse.h.temp $(NAWK) -f $(TOP)/addopcodes.awk parse.h.temp >parse.h -sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest.uuid $(TOP)/VERSION +sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest.uuid $(TOP)/VERSION $(TOP)/ext/rtree/sqlite3rtree.h tclsh $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h keywordhash.h: $(TOP)/tool/mkkeywordhash.c diff --git a/manifest b/manifest index 12cad8c970..af46986305 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\s--verify\soption\sto\sspeedtest1.\s\sAdd\sverification\stest\scases\sto\nthe\s"rtree"\stestset\sand\sa\scase\sthat\suses\sa\scustom\sgeometry\scallback. -D 2014-04-11T17:41:08.614 +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 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 043f16a6c3f823c760657b975f2ff1de7fe5c197 +F ext/rtree/rtree.c 541016f8a53bbedda4dc2deb0067f6955b0accb9 F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree1.test cf679265ecafff494a768ac9c2f43a70915a6290 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba @@ -137,7 +137,7 @@ F ext/rtree/rtreeC.test 16d7aa86ecb6a876d2a38cf590a1471a41b3a46d F ext/rtree/rtreeD.test 636630357638f5983701550b37f0f5867130d2ca F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195 F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea -F ext/rtree/sqlite3rtree.h c34c1e41d1ab80bb8ad09aae402c9c956871a765 +F ext/rtree/sqlite3rtree.h f93a466456ed25a9dadf90ad050e2945a2c49cff F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 F ext/session/session1.test 894e3bc9f497c4fa07a2aa3271e3911f3670c3d8 @@ -157,7 +157,7 @@ F ext/session/test_session.c 7878ac0e2fe9675e8ec24d54f6a538ccc105977f F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt f439556c5ce01ced70987e5ee86549a45165d9ff -F main.mk a3028a846754a96c8841d1a7227890c471c65ec2 +F main.mk 82b154dd823b75c2f1247f1bb98893bf94a5af57 F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea F mkopcodeh.awk c6b3fa301db6ef7ac916b14c60868aeaec1337b5 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 @@ -825,6 +825,7 @@ F test/shell3.test 5e8545ec72c4413a0e8d4c6be56496e3c257ca29 F test/shell4.test aa4eef8118b412d1a01477a53426ece169ea86a9 F test/shell5.test bb755ea9144b8078a752fc56223582627070b5f1 F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3 +F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5 F test/shrink.test 8c70f62b6e8eb4d54533de6d65bd06b1b9a17868 F test/sidedelete.test f0ad71abe6233e3b153100f3b8d679b19a488329 F test/skipscan1.test bed8cbe9d554c8c27afb6c88500f704c86a9196f @@ -840,7 +841,7 @@ F test/speed3.test d32043614c08c53eafdc80f33191d5bd9b920523 F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715 F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b -F test/speedtest1.c 017473605f9df5f4770dd60ddb4038e6d6829916 +F test/speedtest1.c d29c8048beb7ea9254191f3fde9414709166a920 F test/spellfix.test 61309f5efbec53603b3f86457d34a504f80abafe F test/sqllimits1.test b1aae27cc98eceb845e7f7adf918561256e31298 F test/stat.test 76fd746b85459e812a0193410fb599f0531f22de @@ -1174,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 20a73ec0b2f56609a4504052e198e32803d30207 -R 47999e8bbddb67bbaef79a4f5741e58b +P 9d485c4207a81f32334857d4a608c5c511dd2b83 +R 0017d933bc77106350e8e33449b1c08c U drh -Z 73dffdddfd99e7f86f5cf9f646a8ff0c +Z 0d8113e9fad970081c8175cf29f2569d diff --git a/manifest.uuid b/manifest.uuid index 0d054cdd30..1c530a4dc8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9d485c4207a81f32334857d4a608c5c511dd2b83 \ No newline at end of file +0b70275972c7a6a533566c1e50bffbf3ac531e95 \ No newline at end of file diff --git a/test/show_speedtest1_rtree.tcl b/test/show_speedtest1_rtree.tcl new file mode 100644 index 0000000000..3faa168146 --- /dev/null +++ b/test/show_speedtest1_rtree.tcl @@ -0,0 +1,57 @@ +#!/usr/bin/tclsh +# +# This script displays the field of rectangles used by --testset rtree +# of speedtest1. Run this script as follows: +# +# rm test.db +# ./speedtest1 --testset rtree --size 25 test.db +# sqlite3 --separator ' ' test.db 'SELECT * FROM rt1' >data.txt +# wish show_speedtest1_rtree.tcl +# +# The filename "data.txt" is hard coded into this script and so that name +# must be used on lines 3 and 4 above. Elsewhere, different filenames can +# be used. The --size N parameter can be adjusted as desired. +# +package require Tk +set f [open data.txt rb] +set data [read $f] +close $f +canvas .c +frame .b +button .b.b1 -text X-Y -command refill-xy +button .b.b2 -text X-Z -command refill-xz +button .b.b3 -text Y-Z -command refill-yz +pack .b.b1 .b.b2 .b.b3 -side left +pack .c -side top -fill both -expand 1 +pack .b -side top +proc resize_canvas_to_fit {} { + foreach {x0 y0 x1 y1} [.c bbox all] break + set w [expr {$x1-$x0}] + set h [expr {$y1-$y0}] + .c config -width $w -height $h +} +proc refill-xy {} { + .c delete all + foreach {id x0 x1 y0 y1 z0 z1} $::data { + .c create rectangle $x0 $y0 $x1 $y1 + } + .c scale all 0 0 0.05 0.05 + resize_canvas_to_fit +} +proc refill-xz {} { + .c delete all + foreach {id x0 x1 y0 y1 z0 z1} $::data { + .c create rectangle $x0 $z0 $x1 $z1 + } + .c scale all 0 0 0.05 0.05 + resize_canvas_to_fit +} +proc refill-yz {} { + .c delete all + foreach {id x0 x1 y0 y1 z0 z1} $::data { + .c create rectangle $y0 $z0 $y1 $z1 + } + .c scale all 0 0 0.05 0.05 + resize_canvas_to_fit +} +refill-xy diff --git a/test/speedtest1.c b/test/speedtest1.c index 0680e9599f..383f5809a9 100644 --- a/test/speedtest1.c +++ b/test/speedtest1.c @@ -938,14 +938,15 @@ void testset_cte(void){ ** be far apart. */ static void twoCoords( + int p1, int p2, /* Parameters adjusting sizes */ unsigned mx, /* Range of 1..mx */ unsigned *pX0, unsigned *pX1 /* OUT: write results here */ ){ unsigned d, x0, x1, span; span = mx/100 + 1; - if( speedtest1_random()%3==0 ) span *= 3; - if( speedtest1_random()%17==0 ) span = mx - 5; + if( speedtest1_random()%3==0 ) span *= p1; + if( speedtest1_random()%p2==0 ) span = mx/2; d = speedtest1_random()%span + 1; x0 = speedtest1_random()%(mx-d) + 1; x1 = x0 + d; @@ -976,14 +977,14 @@ static int xsliceGeometryCallback( /* ** A testset for the R-Tree virtual table */ -void testset_rtree(void){ +void testset_rtree(int p1, int p2){ unsigned i, n; unsigned mxCoord; unsigned x0, x1, y0, y1, z0, z1; unsigned iStep; int *aCheck = sqlite3_malloc( sizeof(int)*g.szTest*100 ); - mxCoord = g.szTest*600; + mxCoord = 15000; n = g.szTest*100; speedtest1_begin_test(100, "%d INSERTs into an r-tree", n); speedtest1_exec("BEGIN"); @@ -991,9 +992,9 @@ void testset_rtree(void){ speedtest1_prepare("INSERT INTO rt1(id,x0,x1,y0,y1,z0,z1)" "VALUES(?1,?2,?3,?4,?5,?6,?7)"); for(i=1; i<=n; i++){ - twoCoords(mxCoord, &x0, &x1); - twoCoords(mxCoord, &y0, &y1); - twoCoords(mxCoord, &z0, &z1); + twoCoords(p1, p2, mxCoord, &x0, &x1); + twoCoords(p1, p2, mxCoord, &y0, &y1); + twoCoords(p1, p2, mxCoord, &z0, &z1); sqlite3_bind_int(g.pStmt, 1, i); sqlite3_bind_int(g.pStmt, 2, x0); sqlite3_bind_int(g.pStmt, 3, x1); @@ -1322,7 +1323,7 @@ int main(int argc, char **argv){ }else if( strcmp(zTSet,"cte")==0 ){ testset_cte(); }else if( strcmp(zTSet,"rtree")==0 ){ - testset_rtree(); + testset_rtree(6, 147); }else{ fatal_error("unknown testset: \"%s\"\nChoices: main debug1 cte rtree\n", zTSet); From 0ccb6cdc874181048268ecafbbc90725a0056118 Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 12 Apr 2014 17:44:00 +0000 Subject: [PATCH 04/21] Continuing clean-up of the R-Tree module in preparation for cutting in the new generalized query mechanism. FossilOrigin-Name: 66c858f20586424e15d0bfa3d7b56643bde66226 --- ext/rtree/rtree.c | 121 ++++++++++++++++++++++++--------------------- manifest | 14 +++--- manifest.uuid | 2 +- src/test_rtree.c | 123 ++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 185 insertions(+), 75 deletions(-) diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index ddfc3d79a1..e85fe4449d 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -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 */ @@ -310,32 +310,17 @@ struct RtreeCell { /* -** Value for the first field of every RtreeMatchArg object. The MATCH -** operator tests that the first field of a blob operand matches this -** value to avoid operating on invalid blobs (which could cause a segfault). -*/ -#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. -*/ -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 +** 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 { @@ -345,6 +330,27 @@ struct RtreeGeomCallback { 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 +** value to avoid operating on invalid blobs (which could cause a segfault). +*/ +#define RTREE_GEOMETRY_MAGIC 0x891245AB + +/* +** 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 */ + 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 # define MAX(x,y) ((x) < (y) ? (y) : (x)) #endif @@ -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; ixQueryFunc = 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 ); diff --git a/manifest b/manifest index af46986305..0f929e7155 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index 1c530a4dc8..866d285af2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0b70275972c7a6a533566c1e50bffbf3ac531e95 \ No newline at end of file +66c858f20586424e15d0bfa3d7b56643bde66226 \ No newline at end of file diff --git a/src/test_rtree.c b/src/test_rtree.c index e1966c2437..3b4c205879 100644 --- a/src/test_rtree.c +++ b/src/test_rtree.c @@ -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; From 8ae04b53c9a6d8e221ae4ecbe7eabd0c032fdee7 Mon Sep 17 00:00:00 2001 From: drh Date: Sun, 13 Apr 2014 16:10:09 +0000 Subject: [PATCH 05/21] Continuing work on the new custom query mechanism for r-tree. FossilOrigin-Name: ca7357e66ca60f59477b1db000c2cdaeb8082ae1 --- ext/rtree/rtree.c | 49 +++++++++++++++++++++------------------- ext/rtree/sqlite3rtree.h | 4 ++++ manifest | 14 ++++++------ manifest.uuid | 2 +- 4 files changed, 38 insertions(+), 31 deletions(-) diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index e85fe4449d..aa57791cc5 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -272,9 +272,12 @@ union RtreeCoord { struct RtreeConstraint { int iCoord; /* Index of constrained coordinate */ int op; /* Constraining operation */ - RtreeDValue rValue; /* Constraint value. */ - int (*xGeom)(sqlite3_rtree_geometry*, int, RtreeDValue*, int*); - sqlite3_rtree_geometry *pGeom; /* Constraint callback argument for a MATCH */ + union { + RtreeDValue rValue; /* Constraint value. */ + int (*xGeom)(sqlite3_rtree_geometry*,int,RtreeDValue*,int*); + int (*xQueryFunc)(sqlite3_rtree_query_info*); + } u; + sqlite3_rtree_query_info *pGeom; /* xGeom and xQueryFunc argument */ }; /* Possible values for RtreeConstraint.op */ @@ -283,7 +286,8 @@ struct RtreeConstraint { #define RTREE_LT 0x43 #define RTREE_GE 0x44 #define RTREE_GT 0x45 -#define RTREE_MATCH 0x46 +#define RTREE_MATCH 0x46 /* Old-style sqlite3_rtree_geometry_callback() */ +#define RTREE_QUERY 0x47 /* New-style sqlite3_rtree_query_callback() */ /* ** An rtree structure node. @@ -860,7 +864,7 @@ static void freeCursorConstraints(RtreeCursor *pCsr){ if( pCsr->aConstraint ){ int i; /* Used to iterate through constraint array */ for(i=0; inConstraint; i++){ - sqlite3_rtree_geometry *pGeom = pCsr->aConstraint[i].pGeom; + sqlite3_rtree_query_info *pGeom = pCsr->aConstraint[i].pGeom; if( pGeom ){ if( pGeom->xDelUser ) pGeom->xDelUser(pGeom->pUser); sqlite3_free(pGeom); @@ -915,7 +919,8 @@ static int testRtreeGeom( for(i=0; iaCoord[i]); } - return pConstraint->xGeom(pConstraint->pGeom, nCoord, aCoord, pbRes); + return pConstraint->u.xGeom((sqlite3_rtree_geometry*)pConstraint->pGeom, + nCoord, aCoord, pbRes); } /* @@ -945,15 +950,15 @@ static int testRtreeCell(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){ switch( p->op ){ case RTREE_LE: case RTREE_LT: - bRes = p->rValueu.rValuerValue>cell_max; + bRes = p->u.rValue>cell_max; break; case RTREE_EQ: - bRes = (p->rValue>cell_max || p->rValueu.rValue>cell_max || p->u.rValueop==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_MATCH ); switch( p->op ){ - case RTREE_LE: res = (coord<=p->rValue); break; - case RTREE_LT: res = (coordrValue); break; - case RTREE_GE: res = (coord>=p->rValue); break; - case RTREE_GT: res = (coord>p->rValue); break; - case RTREE_EQ: res = (coord==p->rValue); break; + case RTREE_LE: res = (coord<=p->u.rValue); break; + case RTREE_LT: res = (coordu.rValue); break; + case RTREE_GE: res = (coord>=p->u.rValue); break; + case RTREE_GT: res = (coord>p->u.rValue); break; + case RTREE_EQ: res = (coord==p->u.rValue); break; default: { int rc; assert( p->op==RTREE_MATCH ); @@ -1230,7 +1235,7 @@ static int findLeafNode(Rtree *pRtree, i64 iRowid, RtreeNode **ppLeaf){ */ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){ RtreeMatchArg *p; - sqlite3_rtree_geometry *pGeom; + sqlite3_rtree_query_info *pGeom; int nBlob; /* Check that value is actually a blob. */ @@ -1244,12 +1249,10 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){ return SQLITE_ERROR; } - pGeom = (sqlite3_rtree_geometry *)sqlite3_malloc( - sizeof(sqlite3_rtree_geometry) + nBlob - ); + pGeom = (sqlite3_rtree_query_info*)sqlite3_malloc( sizeof(*pGeom)+nBlob ); if( !pGeom ) return SQLITE_NOMEM; - memset(pGeom, 0, sizeof(sqlite3_rtree_geometry)); - p = (RtreeMatchArg *)&pGeom[1]; + memset(pGeom, 0, sizeof(*pGeom)); + p = (RtreeMatchArg*)&pGeom[1]; memcpy(p, sqlite3_value_blob(pValue), nBlob); if( p->magic!=RTREE_GEOMETRY_MAGIC @@ -1263,7 +1266,7 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){ pGeom->nParam = p->nParam; pGeom->aParam = p->aParam; - pCons->xGeom = p->cb.xGeom; + pCons->u.xGeom = p->cb.xGeom; pCons->pGeom = pGeom; return SQLITE_OK; } @@ -1326,9 +1329,9 @@ static int rtreeFilter( } }else{ #ifdef SQLITE_RTREE_INT_ONLY - p->rValue = sqlite3_value_int64(argv[ii]); + p->u.rValue = sqlite3_value_int64(argv[ii]); #else - p->rValue = sqlite3_value_double(argv[ii]); + p->u.rValue = sqlite3_value_double(argv[ii]); #endif } } diff --git a/ext/rtree/sqlite3rtree.h b/ext/rtree/sqlite3rtree.h index 79d1ffeede..3f4df4ed89 100644 --- a/ext/rtree/sqlite3rtree.h +++ b/ext/rtree/sqlite3rtree.h @@ -77,6 +77,10 @@ int sqlite3_rtree_query_callback( ** A pointer to a structure of the following type is passed as the ** argument to scored geometry callback registered using ** sqlite3_rtree_query_callback(). +** +** Note that the first 5 fields of this structure are identical to +** sqlite3_rtree_geometry. This structure is a subclass of +** sqlite3_rtree_geometry. */ struct sqlite3_rtree_query_info { void *pContext; /* pContext from when function registered */ diff --git a/manifest b/manifest index 0f929e7155..29fa91b96a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -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 +C Continuing\swork\son\sthe\snew\scustom\squery\smechanism\sfor\sr-tree. +D 2014-04-13T16:10:09.443 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 8778f55ece9016ab3b17969f19f9656a06f6e100 +F ext/rtree/rtree.c 527490ab54b8f1bb88bece47037f0d947534cece F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree1.test cf679265ecafff494a768ac9c2f43a70915a6290 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba @@ -137,7 +137,7 @@ F ext/rtree/rtreeC.test 16d7aa86ecb6a876d2a38cf590a1471a41b3a46d F ext/rtree/rtreeD.test 636630357638f5983701550b37f0f5867130d2ca F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195 F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea -F ext/rtree/sqlite3rtree.h f93a466456ed25a9dadf90ad050e2945a2c49cff +F ext/rtree/sqlite3rtree.h 488cf8834d2012b913d33683157d3cf5f1327a69 F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 F ext/session/session1.test 894e3bc9f497c4fa07a2aa3271e3911f3670c3d8 @@ -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 0b70275972c7a6a533566c1e50bffbf3ac531e95 -R 958d8acaf66c9f2ba2d270349d1218f7 +P 66c858f20586424e15d0bfa3d7b56643bde66226 +R c2e5d1b590c0114514efc7f22a499336 U drh -Z 02617e452696d44ca40af16d5a687407 +Z 7b1f11ecd50995d28149a37777f2c7ae diff --git a/manifest.uuid b/manifest.uuid index 866d285af2..77fbfcc83e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -66c858f20586424e15d0bfa3d7b56643bde66226 \ No newline at end of file +ca7357e66ca60f59477b1db000c2cdaeb8082ae1 \ No newline at end of file From b6c3aeac904db2de7be1d4c366afa74c0e00c0c4 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 14 Apr 2014 12:18:17 +0000 Subject: [PATCH 06/21] Remove over 300 lines of unused code, code that implemented the older Guttman insertion algorithms that are no longer used. FossilOrigin-Name: 3ba5f295c709faebf5505e61f6dc5266b811b086 --- ext/rtree/rtree.c | 349 ++-------------------------------------------- manifest | 12 +- manifest.uuid | 2 +- 3 files changed, 22 insertions(+), 341 deletions(-) diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index aa57791cc5..5fff43529e 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -54,48 +54,6 @@ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RTREE) -/* -** This file contains an implementation of a couple of different variants -** of the r-tree algorithm. See the README file for further details. The -** same data-structure is used for all, but the algorithms for insert and -** delete operations vary. The variants used are selected at compile time -** by defining the following symbols: -*/ - -/* Either, both or none of the following may be set to activate -** r*tree variant algorithms. -*/ -#define VARIANT_RSTARTREE_CHOOSESUBTREE 0 -#define VARIANT_RSTARTREE_REINSERT 1 - -/* -** Exactly one of the following must be set to 1. -*/ -#define VARIANT_GUTTMAN_QUADRATIC_SPLIT 0 -#define VARIANT_GUTTMAN_LINEAR_SPLIT 0 -#define VARIANT_RSTARTREE_SPLIT 1 - -#define VARIANT_GUTTMAN_SPLIT \ - (VARIANT_GUTTMAN_LINEAR_SPLIT||VARIANT_GUTTMAN_QUADRATIC_SPLIT) - -#if VARIANT_GUTTMAN_QUADRATIC_SPLIT - #define PickNext QuadraticPickNext - #define PickSeeds QuadraticPickSeeds - #define AssignCells splitNodeGuttman -#endif -#if VARIANT_GUTTMAN_LINEAR_SPLIT - #define PickNext LinearPickNext - #define PickSeeds LinearPickSeeds - #define AssignCells splitNodeGuttman -#endif -#if VARIANT_RSTARTREE_SPLIT - #define AssignCells splitNodeStartree -#endif - -#if !defined(NDEBUG) && !defined(SQLITE_DEBUG) -# define NDEBUG 1 -#endif - #ifndef SQLITE_CORE #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 @@ -1556,62 +1514,32 @@ static RtreeDValue cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){ return (cellArea(pRtree, &cell)-area); } -#if VARIANT_RSTARTREE_CHOOSESUBTREE || VARIANT_RSTARTREE_SPLIT static RtreeDValue cellOverlap( Rtree *pRtree, RtreeCell *p, RtreeCell *aCell, - int nCell, - int iExclude + int nCell ){ int ii; RtreeDValue overlap = 0.0; for(ii=0; iinDim*2); jj+=2){ - RtreeDValue x1, x2; - - x1 = MAX(DCOORD(p->aCoord[jj]), DCOORD(aCell[ii].aCoord[jj])); - x2 = MIN(DCOORD(p->aCoord[jj+1]), DCOORD(aCell[ii].aCoord[jj+1])); - - if( x2nDim*2); jj+=2){ + RtreeDValue x1, x2; + x1 = MAX(DCOORD(p->aCoord[jj]), DCOORD(aCell[ii].aCoord[jj])); + x2 = MIN(DCOORD(p->aCoord[jj+1]), DCOORD(aCell[ii].aCoord[jj+1])); + if( x2iDepth-1) ){ - int jj; - aCell = sqlite3_malloc(sizeof(RtreeCell)*nCell); - if( !aCell ){ - rc = SQLITE_NOMEM; - nodeRelease(pRtree, pNode); - pNode = 0; - continue; - } - for(jj=0; jjiDepth-1) ){ - overlap = cellOverlapEnlargement(pRtree,&cell,pCell,aCell,nCell,iCell); - }else{ - overlap = 0.0; - } - if( (iCell==0) - || (overlapnDim; i++){ - RtreeDValue x1 = DCOORD(aCell[0].aCoord[i*2]); - RtreeDValue x2 = DCOORD(aCell[0].aCoord[i*2+1]); - RtreeDValue x3 = x1; - RtreeDValue x4 = x2; - int jj; - - int iCellLeft = 0; - int iCellRight = 0; - - for(jj=1; jjx4 ) x4 = right; - if( left>x3 ){ - x3 = left; - iCellRight = jj; - } - if( rightmaxNormalInnerWidth ){ - iLeftSeed = iCellLeft; - iRightSeed = iCellRight; - } - } - } - - *piLeftSeed = iLeftSeed; - *piRightSeed = iRightSeed; -} -#endif /* VARIANT_GUTTMAN_LINEAR_SPLIT */ - -#if VARIANT_GUTTMAN_QUADRATIC_SPLIT -/* -** Implementation of the quadratic variant of the PickNext() function from -** Guttman[84]. -*/ -static RtreeCell *QuadraticPickNext( - Rtree *pRtree, - RtreeCell *aCell, - int nCell, - RtreeCell *pLeftBox, - RtreeCell *pRightBox, - int *aiUsed -){ - #define FABS(a) ((a)<0.0?-1.0*(a):(a)) - - int iSelect = -1; - RtreeDValue fDiff; - int ii; - for(ii=0; iifDiff ){ - fDiff = diff; - iSelect = ii; - } - } - } - aiUsed[iSelect] = 1; - return &aCell[iSelect]; -} - -/* -** Implementation of the quadratic variant of the PickSeeds() function from -** Guttman[84]. -*/ -static void QuadraticPickSeeds( - Rtree *pRtree, - RtreeCell *aCell, - int nCell, - int *piLeftSeed, - int *piRightSeed -){ - int ii; - int jj; - - int iLeftSeed = 0; - int iRightSeed = 1; - RtreeDValue fWaste = 0.0; - - for(ii=0; iifWaste ){ - iLeftSeed = ii; - iRightSeed = jj; - fWaste = waste; - } - } - } - - *piLeftSeed = iLeftSeed; - *piRightSeed = iRightSeed; -} -#endif /* VARIANT_GUTTMAN_QUADRATIC_SPLIT */ /* ** Arguments aIdx, aDistance and aSpare all point to arrays of size @@ -2052,7 +1794,6 @@ static void SortByDimension( } } -#if VARIANT_RSTARTREE_SPLIT /* ** Implementation of the R*-tree variant of SplitNode from Beckman[1990]. */ @@ -2120,7 +1861,7 @@ static int splitNodeStartree( } margin += cellMargin(pRtree, &left); margin += cellMargin(pRtree, &right); - overlap = cellOverlap(pRtree, &left, &right, 1, -1); + overlap = cellOverlap(pRtree, &left, &right, 1); area = cellArea(pRtree, &left) + cellArea(pRtree, &right); if( (nLeft==RTREE_MINCELLS(pRtree)) || (overlap0; i--){ - RtreeCell *pNext; - pNext = PickNext(pRtree, aCell, nCell, pBboxLeft, pBboxRight, aiUsed); - RtreeDValue diff = - cellGrowth(pRtree, pBboxLeft, pNext) - - cellGrowth(pRtree, pBboxRight, pNext) - ; - if( (RTREE_MINCELLS(pRtree)-NCELL(pRight)==i) - || (diff>0.0 && (RTREE_MINCELLS(pRtree)-NCELL(pLeft)!=i)) - ){ - nodeInsertCell(pRtree, pRight, pNext); - cellUnion(pRtree, pBboxRight, pNext); - }else{ - nodeInsertCell(pRtree, pLeft, pNext); - cellUnion(pRtree, pBboxLeft, pNext); - } - } - - sqlite3_free(aiUsed); - return SQLITE_OK; -} -#endif static int updateMapping( Rtree *pRtree, @@ -2286,7 +1971,7 @@ static int SplitNode( memset(pLeft->zData, 0, pRtree->iNodeSize); memset(pRight->zData, 0, pRtree->iNodeSize); - rc = AssignCells(pRtree, aCell, nCell, pLeft, pRight, &leftbbox, &rightbbox); + rc = splitNodeStartree(pRtree, aCell, nCell, pLeft, pRight,&leftbbox,&rightbbox); if( rc!=SQLITE_OK ){ goto splitnode_out; } @@ -2635,16 +2320,12 @@ static int rtreeInsertCell( } } if( nodeInsertCell(pRtree, pNode, pCell) ){ -#if VARIANT_RSTARTREE_REINSERT if( iHeight<=pRtree->iReinsertHeight || pNode->iNode==1){ rc = SplitNode(pRtree, pNode, pCell, iHeight); }else{ pRtree->iReinsertHeight = iHeight; rc = Reinsert(pRtree, pNode, pCell, iHeight); } -#else - rc = SplitNode(pRtree, pNode, pCell, iHeight); -#endif }else{ rc = AdjustTree(pRtree, pNode, pCell); if( rc==SQLITE_OK ){ diff --git a/manifest b/manifest index 29fa91b96a..9126cb413e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Continuing\swork\son\sthe\snew\scustom\squery\smechanism\sfor\sr-tree. -D 2014-04-13T16:10:09.443 +C Remove\sover\s300\slines\sof\sunused\scode,\scode\sthat\simplemented\sthe\solder\s\nGuttman\sinsertion\salgorithms\sthat\sare\sno\slonger\sused. +D 2014-04-14T12:18:17.185 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 527490ab54b8f1bb88bece47037f0d947534cece +F ext/rtree/rtree.c d7fbe473260cc53463c372c9cf7303495a40fbc8 F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree1.test cf679265ecafff494a768ac9c2f43a70915a6290 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba @@ -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 66c858f20586424e15d0bfa3d7b56643bde66226 -R c2e5d1b590c0114514efc7f22a499336 +P ca7357e66ca60f59477b1db000c2cdaeb8082ae1 +R cf842e59d29c59dde99b1f71fa55bf0f U drh -Z 7b1f11ecd50995d28149a37777f2c7ae +Z e4f991d8b5489d1278b0a7eb150a81ec diff --git a/manifest.uuid b/manifest.uuid index 77fbfcc83e..512a941529 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ca7357e66ca60f59477b1db000c2cdaeb8082ae1 \ No newline at end of file +3ba5f295c709faebf5505e61f6dc5266b811b086 \ No newline at end of file From de8c279b28defed9ca99483bdc22eb3959f63fd9 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 14 Apr 2014 14:43:09 +0000 Subject: [PATCH 07/21] Fix comments on the rtreenode() and rtreedepth() test function in the R-Tree module. FossilOrigin-Name: ade5b986e8baab9df7bdaf7ccfaee2d6ba55fa3c --- ext/rtree/rtree.c | 17 +++++++++++++---- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 5fff43529e..17375718e9 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -2946,10 +2946,10 @@ static int rtreeInit( ** Implementation of a scalar function that decodes r-tree nodes to ** human readable strings. This can be used for debugging and analysis. ** -** The scalar function takes two arguments, a blob of data containing -** an r-tree node, and the number of dimensions the r-tree indexes. -** For a two-dimensional r-tree structure called "rt", to deserialize -** all nodes, a statement like: +** The scalar function takes two arguments: (1) the number of dimensions +** to the rtree (between 1 and 5, inclusive) and (2) a blob of data containing +** an r-tree node. For a two-dimensional r-tree structure called "rt", to +** deserialize all nodes, a statement like: ** ** SELECT rtreenode(2, data) FROM rt_node; ** @@ -3003,6 +3003,15 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){ sqlite3_result_text(ctx, zText, -1, sqlite3_free); } +/* This routine implements an SQL function that returns the "depth" parameter +** from the front of a blob that is an r-tree node. For example: +** +** SELECT rtreedepth(data) FROM rt_node WHERE nodeno=1; +** +** The depth value is 0 for all nodes other than the root node, and the root +** node always has nodeno=1, so the example above is the primary use for this +** routine. This routine is intended for testing and analysis only. +*/ static void rtreedepth(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){ UNUSED_PARAMETER(nArg); if( sqlite3_value_type(apArg[0])!=SQLITE_BLOB diff --git a/manifest b/manifest index 9126cb413e..89c7e30ee7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sover\s300\slines\sof\sunused\scode,\scode\sthat\simplemented\sthe\solder\s\nGuttman\sinsertion\salgorithms\sthat\sare\sno\slonger\sused. -D 2014-04-14T12:18:17.185 +C Fix\scomments\son\sthe\srtreenode()\sand\srtreedepth()\stest\sfunction\sin\sthe\sR-Tree\nmodule. +D 2014-04-14T14:43:09.474 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 d7fbe473260cc53463c372c9cf7303495a40fbc8 +F ext/rtree/rtree.c 07630c252ce0ba63d2d0e6922847eff6eec64fbc F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree1.test cf679265ecafff494a768ac9c2f43a70915a6290 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba @@ -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 ca7357e66ca60f59477b1db000c2cdaeb8082ae1 -R cf842e59d29c59dde99b1f71fa55bf0f +P 3ba5f295c709faebf5505e61f6dc5266b811b086 +R e7b36f9b46b38d0d4d4bc75aff5d0712 U drh -Z e4f991d8b5489d1278b0a7eb150a81ec +Z 1c3694849e99525baeaeac92e41be917 diff --git a/manifest.uuid b/manifest.uuid index 512a941529..0a6c215cd0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3ba5f295c709faebf5505e61f6dc5266b811b086 \ No newline at end of file +ade5b986e8baab9df7bdaf7ccfaee2d6ba55fa3c \ No newline at end of file From 96ce1b36aa7709d0aeffbf1c464bd0aad096afb9 Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 15 Apr 2014 21:06:14 +0000 Subject: [PATCH 08/21] Initial attempt at getting R-Tree queries to work using a priority queue. 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 | 547 +++++++++++++++++++++++++++++++--------------- manifest | 15 +- manifest.uuid | 2 +- 3 files changed, 381 insertions(+), 183 deletions(-) diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 17375718e9..9f41da3534 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -63,6 +63,7 @@ #include #include +#include #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; iiaNode[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 && iinConstraint; ii++){ + for(ii=0; bOutOfBounds==0 && iinConstraint; 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.rValueu.rValueu.rValue>cell_max; + bOutOfBounds = p->u.rValue>cell_max; break; case RTREE_EQ: - bRes = (p->u.rValue>cell_max || p->u.rValueu.rValue>cell_max || p->u.rValueop==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; iinConstraint; ii++){ + for(ii=0; res && iinConstraint; 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 && iiiCell = 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; iirScorerScore ) return -1; + if( pA->rScore>pB->rScore ) return +1; + if( pA->iLeveliLevel ) return -1; + if( pA->iLevel>pB->iLevel ) return +1; + return 0; +} + +/* +** Interchange to search points in a cursor. +*/ +static void rtreeSearchPointSwap(RtreeCursor *p, int i, int j){ + RtreeSearchPoint t = p->aPoint[i]; + assert( iaPoint[i] = p->aPoint[j]; + p->aPoint[j] = t; + if( i=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; + } + } +} + +/* +** 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{ + 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)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; + } + } + } + } +} + + +/* +** 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->iCelliCell, &cell); + if( p->iLevel==1 ){ + rc = rtreeTestEntry(pCur, &cell, &eWithin); + }else{ + rc = rtreeTestCell(pCur, &cell, &eWithin); + } + 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){ - Rtree *pRtree = (Rtree *)(pVtabCursor->pVtab); RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; int rc = SQLITE_OK; - /* 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 ); - - if( pCsr->iStrategy==1 ){ - /* This "scan" is a direct lookup by rowid. There is no next entry. */ - nodeRelease(pRtree, pCsr->pNode); - pCsr->pNode = 0; - }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->iCelliCell++){ - int isEof; - rc = descendToCell(pRtree, pCsr, iHeight, &isEof); - if( rc!=SQLITE_OK || !isEof ){ - return rc; - } - } - pCsr->pNode = pNode->pParent; - rc = nodeParentIndex(pRtree, pNode, &pCsr->iCell); - if( rc!=SQLITE_OK ){ - return rc; - } - nodeReference(pCsr->pNode); - nodeRelease(pRtree, pNode); - iHeight++; - } - } - + /* 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->iCelliCell++){ - 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->iCellpNode) ); + 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. */ diff --git a/manifest b/manifest index 89c7e30ee7..7acb80fc79 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index 0a6c215cd0..ddde4e0525 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ade5b986e8baab9df7bdaf7ccfaee2d6ba55fa3c \ No newline at end of file +53688a25c23c394278a357829793889970aa4157 \ No newline at end of file From f247982e37aa5b5f1d8b50ab96ba8f7238d2dab9 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 16 Apr 2014 13:00:08 +0000 Subject: [PATCH 09/21] Bug fixes to the priority-queue implementation for R-Trees. Improved tracing capability. Some queries work now, but still many problems. FossilOrigin-Name: a439ddd629c6bb5ea2e7e274673fee4f5c207acf --- ext/rtree/rtree.c | 79 ++++++++++++++++++++++++++++++++++------------- manifest | 15 ++++----- manifest.uuid | 2 +- 3 files changed, 65 insertions(+), 31 deletions(-) diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 9f41da3534..3754655bb0 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -1098,14 +1098,15 @@ static void rtreeSearchPointSwap(RtreeCursor *p, int i, int j){ assert( iaPoint[i] = p->aPoint[j]; p->aPoint[j] = t; - if( i=RTREE_CACHE_SZ-1 ){ - nodeRelease(RTREE_OF_CURSOR(p), p->aNode[i+1]); - p->aNode[i+1] = 0; + i++; j++; + if( i=RTREE_CACHE_SZ ){ + nodeRelease(RTREE_OF_CURSOR(p), p->aNode[i]); + p->aNode[i] = 0; }else{ - RtreeNode *pTemp = p->aNode[i+i]; - p->aNode[i+1] = p->aNode[j+1]; - p->aNode[j+1] = pTemp; + RtreeNode *pTemp = p->aNode[i]; + p->aNode[i] = p->aNode[j]; + p->aNode[j] = pTemp; } } } @@ -1182,10 +1183,16 @@ static RtreeSearchPoint *rtreeSearchPointNew( || (pFirst->rScore==rScore && pFirst->iLevel>iLevel) ){ if( pCur->bPoint ){ + int ii; pNew = rtreeEnqueue(pCur, rScore, iLevel); if( pNew==0 ) return 0; - assert( pCur->aNode[1]==0 ); - pCur->aNode[1] = pCur->aNode[0]; + ii = (int)(pNew - pCur->aPoint) + 1; + if( iiaNode[ii]==0 ); + pCur->aNode[ii] = pCur->aNode[0]; + }else{ + nodeRelease(RTREE_OF_CURSOR(pCur), pCur->aNode[0]); + } pCur->aNode[0] = 0; *pNew = pCur->sPoint; } @@ -1198,13 +1205,34 @@ static RtreeSearchPoint *rtreeSearchPointNew( } } -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); +#if 0 +/* Tracing routines for the RtreeSearchPoint queue */ +static void tracePoint(RtreeSearchPoint *p, int idx, RtreeCursor *pCur){ + if( idx<0 ){ printf(" s"); }else{ printf("%2d", idx); } + printf(" %d.%05lld.%02d %g %d", + p->iLevel, p->id, p->iCell, p->rScore, p->eWithin + ); + idx++; + if( idxaNode[idx]); + }else{ + printf("\n"); } } +static void traceQueue(RtreeCursor *pCur, const char *zPrefix){ + int ii; + printf("=== %9s ", zPrefix); + if( pCur->bPoint ){ + tracePoint(&pCur->sPoint, -1, pCur); + } + for(ii=0; iinPoint; ii++){ + if( ii>0 || pCur->bPoint ) printf(" "); + tracePoint(&pCur->aPoint[ii], ii, pCur); + } +} +#else +# define RTREE_QUEUE_TRACE(A,B) /* no-op */ +#endif /* Remove the search point with the lowest current score. */ @@ -1221,6 +1249,10 @@ static void rtreeSearchPointPop(RtreeCursor *p){ }else if( p->nPoint ){ n = --p->nPoint; p->aPoint[0] = p->aPoint[n]; + if( naNode[1] = p->aNode[n+1]; + p->aNode[n+1] = 0; + } i = 0; while( (j = i*2+1)iCell++; + if( eWithin==NOT_WITHIN ) continue; if( p->iCell>=nCell ){ -traceTop(pCur, "POP:"); + RTREE_QUEUE_TRACE(pCur, "POP-S:"); 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; @@ -1291,9 +1323,14 @@ traceTop(pCur, "POP:"); pNew->id = x.id; pNew->iCell = x.iCell; } -traceTop(pCur, "PUSH:"); + p = pNew; + RTREE_QUEUE_TRACE(pCur, "PUSH-S:"); break; } + if( p->iCell>=nCell ){ + RTREE_QUEUE_TRACE(pCur, "POP-Se:"); + rtreeSearchPointPop(pCur); + } } pCur->atEOF = p==0; return SQLITE_OK; @@ -1307,7 +1344,7 @@ static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){ int rc = SQLITE_OK; /* Move to the next entry that matches the configured constraints. */ -traceTop(pCsr, "POP:"); + RTREE_QUEUE_TRACE(pCsr, "POP-Nx:"); rtreeSearchPointPop(pCsr); rtreeStepToLeaf(pCsr); return rc; @@ -1461,7 +1498,7 @@ static int rtreeFilter( p->eWithin = PARTLY_WITHIN; if( rc ) rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &iCell); p->iCell = iCell; -traceTop(pCsr, "PUSH:"); + RTREE_QUEUE_TRACE(pCsr, "PUSH-F1:"); }else{ /* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array ** with the configured constraints. @@ -1510,7 +1547,7 @@ traceTop(pCsr, "PUSH:"); pNew->eWithin = PARTLY_WITHIN; assert( pCsr->bPoint==1 ); pCsr->aNode[0] = pRoot; -traceTop(pCsr, "PUSH:"); + RTREE_QUEUE_TRACE(pCsr, "PUSH-Fm:"); rc = rtreeStepToLeaf(pCsr); } } @@ -3177,7 +3214,7 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){ nCell = (int)strlen(zCell); for(jj=0; jj Date: Wed, 16 Apr 2014 14:45:11 +0000 Subject: [PATCH 10/21] =?UTF-8?q?Fix=20a=20bug=20in=20rowid=3D=3F=20query?= =?UTF-8?q?=20handling.=20=20More=20problems=20remain.?= FossilOrigin-Name: 5b0e6ba4a5050cf81cf41b977b28d714163569e0 --- ext/rtree/rtree.c | 23 ++++++++++++----------- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 3754655bb0..26082ff511 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -1230,6 +1230,7 @@ static void traceQueue(RtreeCursor *pCur, const char *zPrefix){ tracePoint(&pCur->aPoint[ii], ii, pCur); } } +# define RTREE_QUEUE_TRACE(A,B) traceQueue(A,B) #else # define RTREE_QUEUE_TRACE(A,B) /* no-op */ #endif @@ -1284,7 +1285,6 @@ static void rtreeSearchPointPop(RtreeCursor *p){ */ static int rtreeStepToLeaf(RtreeCursor *pCur){ RtreeSearchPoint *p; - RtreeSearchPoint *pNew; Rtree *pRtree = RTREE_OF_CURSOR(pCur); RtreeNode *pNode; int eWithin; @@ -1313,17 +1313,16 @@ static int rtreeStepToLeaf(RtreeCursor *pCur){ RTREE_QUEUE_TRACE(pCur, "POP-S:"); rtreeSearchPointPop(pCur); } - 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; + p = rtreeSearchPointNew(pCur, /*rScore*/0.0, x.iLevel-1); + if( p==0 ) return SQLITE_NOMEM; + p->eWithin = eWithin; + if( p->iLevel ){ + p->id = cell.iRowid; + p->iCell = 0; }else{ - pNew->id = x.id; - pNew->iCell = x.iCell; + p->id = x.id; + p->iCell = x.iCell; } - p = pNew; RTREE_QUEUE_TRACE(pCur, "PUSH-S:"); break; } @@ -1496,7 +1495,9 @@ static int rtreeFilter( rc = findLeafNode(pRtree, iRowid, &pLeaf, &p->id); pCsr->aNode[0] = pLeaf; p->eWithin = PARTLY_WITHIN; - if( rc ) rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &iCell); + if( rc==SQLITE_OK ){ + rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &iCell); + } p->iCell = iCell; RTREE_QUEUE_TRACE(pCsr, "PUSH-F1:"); }else{ diff --git a/manifest b/manifest index 83f2beda77..df0b85687e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Bug\sfixes\sto\sthe\spriority-queue\simplementation\sfor\sR-Trees.\s\sImproved\stracing\ncapability.\s\sSome\squeries\swork\snow,\sbut\sstill\smany\sproblems. -D 2014-04-16T13:00:08.915 +C Fix\sa\sbug\sin\srowid=?\squery\shandling.\s\sMore\sproblems\sremain. +D 2014-04-16T14:45:11.945 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 3105c5514d6dbf99c39aceac84c4c0f9e00f90ea +F ext/rtree/rtree.c 4702eae330e9803ac51208ec0e26c5aceb0cd20b F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree1.test cf679265ecafff494a768ac9c2f43a70915a6290 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba @@ -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 53688a25c23c394278a357829793889970aa4157 -R 67a30187386908beb1eb82f50c3b2dfd +P a439ddd629c6bb5ea2e7e274673fee4f5c207acf +R 759346bdeaafb6dce8a9cda8463bc2de U drh -Z ba82d149b9f56c655f1e6f2a959cfc71 +Z 100295249b6d7d7223775c8a6bd7cd19 diff --git a/manifest.uuid b/manifest.uuid index b2db9c77b5..8106164958 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a439ddd629c6bb5ea2e7e274673fee4f5c207acf \ No newline at end of file +5b0e6ba4a5050cf81cf41b977b28d714163569e0 \ No newline at end of file From 933a5ff2d4389b5a9007f7b43b1084d16950aad5 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 16 Apr 2014 17:15:14 +0000 Subject: [PATCH 11/21] TCL tests now all pass. FossilOrigin-Name: f864baccd3fe0ee939ac1ec20069792f649cddc0 --- ext/rtree/rtree.c | 24 ++++++++++++++---------- ext/rtree/rtreeB.test | 2 +- manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 26082ff511..9c62576612 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -1239,7 +1239,7 @@ static void traceQueue(RtreeCursor *pCur, const char *zPrefix){ */ static void rtreeSearchPointPop(RtreeCursor *p){ int i, j, k, n; - i = p->bPoint; + i = 1 - p->bPoint; assert( i==0 || i==1 ); if( p->aNode[i] ){ nodeRelease(RTREE_OF_CURSOR(p), p->aNode[i]); @@ -1345,7 +1345,7 @@ static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){ /* Move to the next entry that matches the configured constraints. */ RTREE_QUEUE_TRACE(pCsr, "POP-Nx:"); rtreeSearchPointPop(pCsr); - rtreeStepToLeaf(pCsr); + rc = rtreeStepToLeaf(pCsr); return rc; } @@ -1490,16 +1490,20 @@ static int rtreeFilter( RtreeNode *pLeaf; /* Leaf on which the required cell resides */ RtreeSearchPoint *p; /* Search point for the the leaf */ i64 iRowid = sqlite3_value_int64(argv[0]); - 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==SQLITE_OK ){ + i64 iNode = 0; + rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode); + if( rc==SQLITE_OK && pLeaf!=0 ){ + p = rtreeSearchPointNew(pCsr, 0.0, 0); + assert( p!=0 ); /* Always returns pCsr->sPoint */ + pCsr->aNode[0] = pLeaf; + p->id = iNode; + p->eWithin = PARTLY_WITHIN; rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &iCell); + p->iCell = iCell; + RTREE_QUEUE_TRACE(pCsr, "PUSH-F1:"); + }else{ + pCsr->atEOF = 1; } - p->iCell = iCell; - RTREE_QUEUE_TRACE(pCsr, "PUSH-F1:"); }else{ /* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array ** with the configured constraints. diff --git a/ext/rtree/rtreeB.test b/ext/rtree/rtreeB.test index 7cb445cc4f..aeb308eca7 100644 --- a/ext/rtree/rtreeB.test +++ b/ext/rtree/rtreeB.test @@ -41,7 +41,7 @@ ifcapable rtree_int_only { INSERT INTO t1 VALUES(9223372036854775807, 150, 150, 400, 400); SELECT rtreenode(2, data) FROM t1_node; } - } {{{1073741824 0.000000 0.000000 100.000000 100.000000} {2147483646 0.000000 0.000000 200.000000 200.000000} {4294967296 0.000000 0.000000 300.000000 300.000000} {8589934592 20.000000 20.000000 150.000000 150.000000} {9223372036854775807 150.000000 150.000000 400.000000 400.000000}}} + } {{{1073741824 0 0 100 100} {2147483646 0 0 200 200} {4294967296 0 0 300 300} {8589934592 20 20 150 150} {9223372036854775807 150 150 400 400}}} } finish_test diff --git a/manifest b/manifest index df0b85687e..1061ef3307 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sbug\sin\srowid=?\squery\shandling.\s\sMore\sproblems\sremain. -D 2014-04-16T14:45:11.945 +C TCL\stests\snow\sall\spass. +D 2014-04-16T17:15:14.114 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 4702eae330e9803ac51208ec0e26c5aceb0cd20b +F ext/rtree/rtree.c 142d70dc3bb8db1caf6c0e94b88ec0e8047d36ee F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree1.test cf679265ecafff494a768ac9c2f43a70915a6290 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba @@ -132,7 +132,7 @@ F ext/rtree/rtree7.test 1fa710b9e6bf997a0c1a537b81be7bb6fded1971 F ext/rtree/rtree8.test db79c812f9e4a11f9b1f3f9934007884610a713a F ext/rtree/rtree9.test d86ebf08ff6328895613ed577dd8a2a37c472c34 F ext/rtree/rtreeA.test ace05e729a36e342d40cf94e9efc7b4723d9dcdf -F ext/rtree/rtreeB.test 983e567b49b5dca165940f66b87e161aa30e82b2 +F ext/rtree/rtreeB.test c85f9ce78766c4e68b8b89fbf2979ee9cfa82b4e F ext/rtree/rtreeC.test 16d7aa86ecb6a876d2a38cf590a1471a41b3a46d F ext/rtree/rtreeD.test 636630357638f5983701550b37f0f5867130d2ca F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195 @@ -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 a439ddd629c6bb5ea2e7e274673fee4f5c207acf -R 759346bdeaafb6dce8a9cda8463bc2de +P 5b0e6ba4a5050cf81cf41b977b28d714163569e0 +R f6b681e4a8814b2c282cb2ca3c357703 U drh -Z 100295249b6d7d7223775c8a6bd7cd19 +Z 3d7f5774712bc082940f713cae866e31 diff --git a/manifest.uuid b/manifest.uuid index 8106164958..cd8f7f474f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5b0e6ba4a5050cf81cf41b977b28d714163569e0 \ No newline at end of file +f864baccd3fe0ee939ac1ec20069792f649cddc0 \ No newline at end of file From 18b5142b29d7fe828f17dd816c55b9fb5925ea85 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 16 Apr 2014 21:02:28 +0000 Subject: [PATCH 12/21] Performance optimization on nodeGetCell() in R-Tree. FossilOrigin-Name: 5d20ff9ec837ad35bc44d6c25d13764b350e81dd --- ext/rtree/rtree.c | 11 ++++++++--- manifest | 13 ++++++------- manifest.uuid | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 9c62576612..6bf1db11c4 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -729,10 +729,15 @@ static void nodeGetCell( int iCell, /* Index of the cell within the node */ RtreeCell *pCell /* OUT: Write the cell contents here */ ){ - int ii; + u8 *pData; + u8 *pEnd; + RtreeCoord *pCoord; pCell->iRowid = nodeGetRowid(pRtree, pNode, iCell); - for(ii=0; iinDim*2; ii++){ - nodeGetCoord(pRtree, pNode, iCell, ii, &pCell->aCoord[ii]); + pData = pNode->zData + (12 + pRtree->nBytesPerCell*iCell); + pEnd = pData + pRtree->nDim*8; + pCoord = pCell->aCoord; + for(; pData Date: Thu, 17 Apr 2014 13:15:33 +0000 Subject: [PATCH 13/21] Refactor the constraint checking logic in RTree. The new-style constraint callbacks created by sqlite3_rtree_query_callback() are now hooked up from end to end, though still untested. FossilOrigin-Name: 32a13870175a1dd1d33af3572dde09ff607a04b6 --- ext/rtree/rtree.c | 300 +++++++++++++++++++++--------------------- ext/rtree/rtree6.test | 18 +-- ext/rtree/rtreeC.test | 13 +- manifest | 16 +-- manifest.uuid | 2 +- 5 files changed, 172 insertions(+), 177 deletions(-) diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 6bf1db11c4..3c9a8daba9 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -274,13 +274,13 @@ struct RtreeConstraint { }; /* Possible values for RtreeConstraint.op */ -#define RTREE_EQ 0x41 -#define RTREE_LE 0x42 -#define RTREE_LT 0x43 -#define RTREE_GE 0x44 -#define RTREE_GT 0x45 -#define RTREE_MATCH 0x46 /* Old-style sqlite3_rtree_geometry_callback() */ -#define RTREE_QUERY 0x47 /* New-style sqlite3_rtree_query_callback() */ +#define RTREE_EQ 0x41 /* A */ +#define RTREE_LE 0x42 /* B */ +#define RTREE_LT 0x43 /* C */ +#define RTREE_GE 0x44 /* D */ +#define RTREE_GT 0x45 /* E */ +#define RTREE_MATCH 0x46 /* F: Old-style sqlite3_rtree_geometry_callback() */ +#define RTREE_QUERY 0x47 /* G: New-style sqlite3_rtree_query_callback() */ /* @@ -900,149 +900,131 @@ static int rtreeEof(sqlite3_vtab_cursor *cur){ } /* -** The r-tree constraint passed as the second argument to this function is -** guaranteed to be a MATCH constraint. +** Convert raw bits from the on-disk RTree record into a coordinate value +** The on-disk record stores integer coordinates if eInt is true and it +** stores 32-bit floating point records if eInt is false. a[] is the four +** bytes of the on-disk record to be decoded. Store the results in "r". */ -static int rtreeTestGeom( - Rtree *pRtree, /* R-Tree object */ - RtreeConstraint *pConstraint, /* MATCH constraint to test */ - RtreeCell *pCell, /* Cell to test */ - int *pbRes /* OUT: Test result */ -){ - int i; - RtreeDValue aCoord[RTREE_MAX_DIMENSIONS*2]; - int nCoord = pRtree->nDim*2; - - assert( pConstraint->op==RTREE_MATCH ); - assert( pConstraint->pGeom ); - - for(i=0; iaCoord[i]); - } - return pConstraint->u.xGeom((sqlite3_rtree_geometry*)pConstraint->pGeom, - nCoord, aCoord, pbRes); +#define RTREE_DECODE_COORD(eInt, a, r) { \ + u32 x; /* Raw bits of the coordinate value */ \ + RtreeCoord c; /* Coordinate decoded */ \ + x = ((u32)a[0]<<24) + ((u32)a[1]<<16) \ + +((u32)a[2]<<8) + a[3]; \ + c.i = *(int*)&x; \ + r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \ } + -/* -** Cursor pCursor currently points to a cell in a non-leaf page. -** 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. Errors can only possible if there is a geometry callback. +/* +** Check the RTree node or entry given by pCellData and p against the MATCH +** constraint pConstraint. */ -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 */ +static int rtreeCallbackConstraint( + RtreeConstraint *pConstraint, /* The constraint to test */ + int eInt, /* True if RTree holding integer coordinates */ + u8 *pCellData, /* Raw cell content */ + RtreeSearchPoint *pSearch, /* Container of this cell */ + sqlite3_rtree_dbl *prScore, /* OUT: score for the cell */ + int *peWithin /* OUT: visibility of the cell */ ){ - int ii; - int bOutOfBounds = 0; - int rc = SQLITE_OK; - Rtree *pRtree = RTREE_OF_CURSOR(pCursor); + int i; /* Loop counter */ + sqlite3_rtree_query_info *pGeom = pConstraint->pGeom; /* Callback info */ + int nCoord = pGeom->nCoord; /* No. of coordinates */ + int rc; /* Callback return code */ + sqlite3_rtree_dbl aCoord[RTREE_MAX_DIMENSIONS*2]; /* Decoded coordinates */ - for(ii=0; bOutOfBounds==0 && iinConstraint; ii++){ - RtreeConstraint *p = &pCursor->aConstraint[ii]; - RtreeDValue cell_min = DCOORD(pCell->aCoord[(p->iCoord>>1)*2]); - RtreeDValue cell_max = DCOORD(pCell->aCoord[(p->iCoord>>1)*2+1]); + assert( pConstraint->op==RTREE_MATCH || pConstraint->op==RTREE_QUERY ); + assert( nCoord==2 || nCoord==4 || nCoord==6 || nCoord==8 || nCoord==10 ); - 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 - ); - - switch( p->op ){ - case RTREE_LE: case RTREE_LT: - bOutOfBounds = p->u.rValueu.rValue>cell_max; - break; - - case RTREE_EQ: - bOutOfBounds = (p->u.rValue>cell_max || p->u.rValueop==RTREE_MATCH ); - rc = rtreeTestGeom(pRtree, p, pCell, &bOutOfBounds); - bOutOfBounds = !bOutOfBounds; - break; - } - } + pCellData += 8; + for(i=0; iop==RTREE_MATCH ){ + rc = pConstraint->u.xGeom((sqlite3_rtree_geometry*)pGeom, + nCoord, aCoord, &i); + if( i==0 ) *peWithin = NOT_WITHIN; + }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; } - - *peWithin = bOutOfBounds ? NOT_WITHIN : PARTLY_WITHIN; return rc; } /* -** 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. +** Check the internal RTree node given by pCellData against constraint p. +** If this constraint cannot be satisfied by any child within the node, +** set *peWithin to NOT_WITHIN. */ -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 */ +static void rtreeNonleafConstraint( + RtreeConstraint *p, /* The constraint to test */ + int eInt, /* True if RTree holds integer coordinates */ + u8 *pCellData, /* Raw cell content as appears on disk */ + int *peWithin /* Adjust downward, as appropriate */ ){ - Rtree *pRtree = RTREE_OF_CURSOR(pCursor); - int ii; - int res = 1; /* Innocent until proven guilty */ + sqlite3_rtree_dbl val; /* Coordinate value convert to a double */ - for(ii=0; res && iinConstraint; ii++){ - RtreeConstraint *p = &pCursor->aConstraint[ii]; - 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 - ); - switch( p->op ){ - case RTREE_LE: res = (coord<=p->u.rValue); break; - case RTREE_LT: res = (coordu.rValue); break; - case RTREE_GE: res = (coord>=p->u.rValue); break; - case RTREE_GT: res = (coord>p->u.rValue); break; - case RTREE_EQ: res = (coord==p->u.rValue); break; - default: { - int rc; - assert( p->op==RTREE_MATCH ); - rc = rtreeTestGeom(pRtree, p, pCell, &res); - if( rc!=SQLITE_OK ){ - return rc; - } - break; - } - } + /* p->iCoord might point to either a lower or upper bound coordinate + ** in a coordinate pair. But make pCellData point to the lower bound. + */ + pCellData += 8 + 4*(p->iCoord&0xfe); + + assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE + || p->op==RTREE_GT || p->op==RTREE_EQ ); + switch( p->op ){ + case RTREE_LE: + case RTREE_LT: + case RTREE_EQ: + RTREE_DECODE_COORD(eInt, pCellData, val); + /* val now holds the lower bound of the coordinate pair */ + if( p->u.rValue>=val ) return; + if( p->op!=RTREE_EQ ) break; /* RTREE_LE and RTREE_LT end here */ + /* Fall through for the RTREE_EQ case */ + + default: /* RTREE_GT or RTREE_GE, or fallthrough of RTREE_EQ */ + pCellData += 4; + RTREE_DECODE_COORD(eInt, pCellData, val); + /* val now holds the upper bound of the coordinate pair */ + if( p->u.rValue<=val ) return; } + *peWithin = NOT_WITHIN; +} - *peWithin = res ? FULLY_WITHIN : NOT_WITHIN; - return SQLITE_OK; +/* +** Check the leaf RTree cell given by pCellData against constraint p. +** If this constraint is not satisfied, set *peWithin to NOT_WITHIN. +** If the constraint is satisfied, leave *peWithin unchanged. +** +** The constraint is of the form: xN op $val +** +** The op is given by p->op. The xN is p->iCoord-th coordinate in +** pCellData. $val is given by p->u.rValue. +*/ +static void rtreeLeafConstraint( + RtreeConstraint *p, /* The constraint to test */ + int eInt, /* True if RTree holds integer coordinates */ + u8 *pCellData, /* Raw cell content as appears on disk */ + int *peWithin /* Adjust downward, as appropriate */ +){ + RtreeDValue xN; /* Coordinate value converted to a double */ + + assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE + || p->op==RTREE_GT || p->op==RTREE_EQ ); + pCellData += 8 + p->iCoord*4; + RTREE_DECODE_COORD(eInt, pCellData, xN); + switch( p->op ){ + case RTREE_LE: if( xN <= p->u.rValue ) return; break; + case RTREE_LT: if( xN < p->u.rValue ) return; break; + case RTREE_GE: if( xN >= p->u.rValue ) return; break; + case RTREE_GT: if( xN > p->u.rValue ) return; break; + default: if( xN == p->u.rValue ) return; break; + } + *peWithin = NOT_WITHIN; } /* @@ -1295,39 +1277,53 @@ static int rtreeStepToLeaf(RtreeCursor *pCur){ int eWithin; int rc = SQLITE_OK; int nCell; - RtreeCell cell; + int nConstraint = pCur->nConstraint; + int ii; + int eInt; RtreeSearchPoint x; + eInt = pRtree->eCoordType==RTREE_COORD_INT32; while( (p = rtreeSearchPointFirst(pCur))!=0 && p->iLevel>0 ){ pNode = rtreeNodeOfFirstSearchPoint(pCur, &rc); if( rc ) return rc; nCell = NCELL(pNode); assert( nCell<200 ); while( p->iCelliCell, &cell); - if( p->iLevel==1 ){ - rc = rtreeTestEntry(pCur, &cell, &eWithin); - }else{ - rc = rtreeTestCell(pCur, &cell, &eWithin); + sqlite3_rtree_dbl rScore = (sqlite3_rtree_dbl)0; + u8 *pCellData = pNode->zData + (4+pRtree->nBytesPerCell*p->iCell); + eWithin = FULLY_WITHIN; + for(ii=0; iiaConstraint + ii; + if( pConstraint->op>=RTREE_MATCH ){ + rc = rtreeCallbackConstraint(pConstraint, eInt, pCellData, p, + &rScore, &eWithin); + if( rc ) return rc; + }else if( p->iLevel==1 ){ + rtreeLeafConstraint(pConstraint, eInt, pCellData, &eWithin); + }else{ + rtreeNonleafConstraint(pConstraint, eInt, pCellData, &eWithin); + } + if( eWithin==NOT_WITHIN ) break; } - if( rc ) return rc; - x = *p; p->iCell++; if( eWithin==NOT_WITHIN ) continue; + x.iLevel = p->iLevel - 1; + if( x.iLevel ){ + x.id = readInt64(pCellData); + x.iCell = 0; + }else{ + x.id = p->id; + x.iCell = p->iCell - 1; + } if( p->iCell>=nCell ){ RTREE_QUEUE_TRACE(pCur, "POP-S:"); rtreeSearchPointPop(pCur); } - p = rtreeSearchPointNew(pCur, /*rScore*/0.0, x.iLevel-1); + p = rtreeSearchPointNew(pCur, rScore, x.iLevel); if( p==0 ) return SQLITE_NOMEM; p->eWithin = eWithin; - if( p->iLevel ){ - p->id = cell.iRowid; - p->iCell = 0; - }else{ - p->id = x.id; - p->iCell = x.iCell; - } + p->id = x.id; + p->iCell = x.iCell; RTREE_QUEUE_TRACE(pCur, "PUSH-S:"); break; } @@ -1460,7 +1456,6 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){ sqlite3_free(pGeom); return SQLITE_ERROR; } - pGeom->pContext = p->cb.pContext; pGeom->nParam = p->nParam; pGeom->aParam = p->aParam; @@ -1525,7 +1520,7 @@ static int rtreeFilter( for(ii=0; iiaConstraint[ii]; p->op = idxStr[ii*2]; - p->iCoord = idxStr[ii*2+1]-'a'; + p->iCoord = idxStr[ii*2+1]-'0'; 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 @@ -1535,6 +1530,7 @@ static int rtreeFilter( if( rc!=SQLITE_OK ){ break; } + p->pGeom->nCoord = pRtree->nDim*2; }else{ #ifdef SQLITE_RTREE_INT_ONLY p->u.rValue = sqlite3_value_int64(argv[ii]); @@ -1663,7 +1659,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ break; } zIdxStr[iIdx++] = op; - zIdxStr[iIdx++] = p->iColumn - 1 + 'a'; + zIdxStr[iIdx++] = p->iColumn - 1 + '0'; pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2); pIdxInfo->aConstraintUsage[ii].omit = 1; } diff --git a/ext/rtree/rtree6.test b/ext/rtree/rtree6.test index bdc9bb4146..c5a78bb3e3 100644 --- a/ext/rtree/rtree6.test +++ b/ext/rtree/rtree6.test @@ -57,31 +57,31 @@ do_test rtree6-1.1 { do_test rtree6-1.2 { rtree_strategy {SELECT * FROM t1 WHERE x1>10} -} {Ea} +} {E0} do_test rtree6-1.3 { rtree_strategy {SELECT * FROM t1 WHERE x1<10} -} {Ca} +} {C0} do_test rtree6-1.4 { rtree_strategy {SELECT * FROM t1,t2 WHERE k=ii AND x1<10} -} {Ca} +} {C0} do_test rtree6-1.5 { rtree_strategy {SELECT * FROM t1,t2 WHERE k=+ii AND x1<10} -} {Ca} +} {C0} do_eqp_test rtree6.2.1 { SELECT * FROM t1,t2 WHERE k=+ii AND x1<10 } { - 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca} + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0} 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)} } do_eqp_test rtree6.2.2 { SELECT * FROM t1,t2 WHERE k=ii AND x1<10 } { - 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca} + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0} 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)} } @@ -95,7 +95,7 @@ do_eqp_test rtree6.2.3 { do_eqp_test rtree6.2.4 { SELECT * FROM t1,t2 WHERE v=10 and x1<10 and x2>10 } { - 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:CaEb} + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0E1} 0 1 1 {SEARCH TABLE t2 USING AUTOMATIC COVERING INDEX (v=?)} } @@ -126,7 +126,7 @@ do_test rtree6.3.2 { x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 } -} {EaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEa} +} {E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0} do_test rtree6.3.3 { rtree_strategy { SELECT * FROM t3 WHERE @@ -137,7 +137,7 @@ do_test rtree6.3.3 { x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 } -} {EaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEa} +} {E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0} do_execsql_test rtree6-3.4 { SELECT * FROM t3 WHERE x1>0.5 AND x1>0.8 AND x1>1.1 diff --git a/ext/rtree/rtreeC.test b/ext/rtree/rtreeC.test index 23dc607841..94db05a4d1 100644 --- a/ext/rtree/rtreeC.test +++ b/ext/rtree/rtreeC.test @@ -29,7 +29,7 @@ do_eqp_test 1.1 { WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y } { 0 0 1 {SCAN TABLE t} - 0 1 0 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa} + 0 1 0 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0} } do_eqp_test 1.2 { @@ -37,7 +37,7 @@ do_eqp_test 1.2 { WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y } { 0 0 0 {SCAN TABLE t} - 0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa} + 0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0} } do_eqp_test 1.3 { @@ -45,7 +45,7 @@ do_eqp_test 1.3 { WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND ?<=max_y } { 0 0 0 {SCAN TABLE t} - 0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa} + 0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0} } do_eqp_test 1.5 { @@ -82,7 +82,7 @@ do_eqp_test 2.1 { WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y } { 0 0 1 {SCAN TABLE t} - 0 1 0 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa} + 0 1 0 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0} } do_eqp_test 2.2 { @@ -90,7 +90,7 @@ do_eqp_test 2.2 { WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y } { 0 0 0 {SCAN TABLE t} - 0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa} + 0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0} } do_eqp_test 2.3 { @@ -98,7 +98,7 @@ do_eqp_test 2.3 { WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND ?<=max_y } { 0 0 0 {SCAN TABLE t} - 0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa} + 0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0} } do_eqp_test 2.5 { @@ -271,4 +271,3 @@ ifcapable rtree { finish_test - diff --git a/manifest b/manifest index 336104aca0..d10804afcb 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Performance\soptimization\son\snodeGetCell()\sin\sR-Tree. -D 2014-04-16T21:02:28.844 +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 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in e4ee6d36cdf6136aee0158675a3b24dd3bf31a5a F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -120,20 +120,20 @@ 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 1a59db351a8dfaa46c59b3105865a1aaa5192a61 +F ext/rtree/rtree.c 117aaebed87a7c1e5d3e03afbad83c3700aa5ab8 F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree1.test cf679265ecafff494a768ac9c2f43a70915a6290 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc F ext/rtree/rtree4.test c8fe384f60ebd49540a5fecc990041bf452eb6e0 F ext/rtree/rtree5.test 6a510494f12454bf57ef28f45bc7764ea279431e -F ext/rtree/rtree6.test fe0bd377a21c68ce2826129d14354c884cb1f354 +F ext/rtree/rtree6.test 756585abc51727fec97c77852476445c10c0ee95 F ext/rtree/rtree7.test 1fa710b9e6bf997a0c1a537b81be7bb6fded1971 F ext/rtree/rtree8.test db79c812f9e4a11f9b1f3f9934007884610a713a F ext/rtree/rtree9.test d86ebf08ff6328895613ed577dd8a2a37c472c34 F ext/rtree/rtreeA.test ace05e729a36e342d40cf94e9efc7b4723d9dcdf F ext/rtree/rtreeB.test c85f9ce78766c4e68b8b89fbf2979ee9cfa82b4e -F ext/rtree/rtreeC.test 16d7aa86ecb6a876d2a38cf590a1471a41b3a46d +F ext/rtree/rtreeC.test df158dcc81f1a43ce7eef361af03c48ec91f1e06 F ext/rtree/rtreeD.test 636630357638f5983701550b37f0f5867130d2ca F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195 F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea @@ -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 f26936f71a16ab25590540f7feb273514dfb69ff -R b2b62cbe51f5e73202ce646c31659a05 +P 5d20ff9ec837ad35bc44d6c25d13764b350e81dd +R 9ddd572c495f4e9fca0e8e969ae19d88 U drh -Z 74eaec9038e9087250153055260f329f +Z 43698a2882be63e69b530e3c48875378 diff --git a/manifest.uuid b/manifest.uuid index a27b70bb21..5fbbb6cae9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5d20ff9ec837ad35bc44d6c25d13764b350e81dd \ No newline at end of file +32a13870175a1dd1d33af3572dde09ff607a04b6 \ No newline at end of file From b640595f97b7f55e619fd895becb7bf7ad5d6cf3 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 17 Apr 2014 14:52:20 +0000 Subject: [PATCH 14/21] Test cases and bug fixes for the sqlite3_rtree_query_callback() mechanism. FossilOrigin-Name: 1ccaaed6b516ec2ce953c1b31025a82ba76d00e7 --- ext/rtree/rtree.c | 113 ++++++++++++++++++++++++------------------ ext/rtree/rtreeE.test | 72 +++++++++++++++++++++++++++ manifest | 15 +++--- manifest.uuid | 2 +- src/test_rtree.c | 36 ++++++++------ 5 files changed, 168 insertions(+), 70 deletions(-) create mode 100644 ext/rtree/rtreeE.test diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 3c9a8daba9..53414af7ee 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -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; inConstraint; 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 || *prScorerScore; + } } 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->iCellzData + (4+pRtree->nBytesPerCell*p->iCell); eWithin = FULLY_WITHIN; for(ii=0; iieWithin = 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; iinDim+1)*(sizeof(int*)+nCell*sizeof(int)); @@ -2071,9 +2090,9 @@ static int splitNodeStartree( } for(ii=0; iinDim; 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; iinDim; 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 index 0000000000..14f5c332ca --- /dev/null +++ b/ext/rtree/rtreeE.test @@ -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 diff --git a/manifest b/manifest index d10804afcb..63a2d6b9ce 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index 5fbbb6cae9..07ccb10268 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -32a13870175a1dd1d33af3572dde09ff607a04b6 \ No newline at end of file +1ccaaed6b516ec2ce953c1b31025a82ba76d00e7 \ No newline at end of file diff --git a/src/test_rtree.c b/src/test_rtree.c index 3b4c205879..c1951a896b 100644 --- a/src/test_rtree.c +++ b/src/test_rtree.c @@ -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 ){ From 1821cbacf15e813af181b594a6b28af2c6fb0da6 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 17 Apr 2014 15:34:58 +0000 Subject: [PATCH 15/21] More test cases with very long priority queues. FossilOrigin-Name: 71692aa97c78676f0ba80eaeec0ad9ac225f4427 --- ext/rtree/rtreeE.test | 67 ++++++++++++++++++++++++++++++++++++------- manifest | 14 ++++----- manifest.uuid | 2 +- src/test_rtree.c | 61 +++++++++++++++++++++++++++++++++++---- 4 files changed, 120 insertions(+), 24 deletions(-) diff --git a/ext/rtree/rtreeE.test b/ext/rtree/rtreeE.test index 14f5c332ca..bc97943dcb 100644 --- a/ext/rtree/rtreeE.test +++ b/ext/rtree/rtreeE.test @@ -27,46 +27,93 @@ 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); + CREATE VIRTUAL TABLE rt1 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; + INSERT INTO rt1 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; + INSERT INTO rt1 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; + INSERT INTO rt1 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; + SELECT id FROM rt1 WHERE id MATCH Qcircle(0.0, 0.0, 50.0, 3) 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; + SELECT id FROM rt1 WHERE id MATCH Qcircle(100.0, 0.0, 50.0, 3) 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; + SELECT id FROM rt1 WHERE id MATCH Qcircle(0.0, 200.0, 50.0, 3) 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 + SELECT id FROM rt1 WHERE id MATCH Qcircle(0,0,1000,3) AND id%100==0 } {200 100 0} +# Construct a large 2-D RTree with thousands of random entries. +# +do_test rtreeE-2.1 { + db eval { + CREATE TABLE t2(id,x0,x1,y0,y1); + CREATE VIRTUAL TABLE rt2 USING rtree(id,x0,x1,y0,y1); + BEGIN; + } + expr srand(0) + for {set i 1} {$i<=10000} {incr i} { + set dx [expr {int(rand()*40)+1}] + set dy [expr {int(rand()*40)+1}] + set x0 [expr {int(rand()*(10000 - $dx))}] + set x1 [expr {$x0+$dx}] + set y0 [expr {int(rand()*(10000 - $dy))}] + set y1 [expr {$y0+$dy}] + set id [expr {$i+10000}] + db eval {INSERT INTO t2 VALUES($id,$x0,$x1,$y0,$y1)} + } + db eval { + INSERT INTO rt2 SELECT * FROM t2; + COMMIT; + } +} {} + +for {set i 1} {$i<=200} {incr i} { + set dx [expr {int(rand()*100)}] + set dy [expr {int(rand()*100)}] + set x0 [expr {int(rand()*(10000 - $dx))}] + set x1 [expr {$x0+$dx}] + set y0 [expr {int(rand()*(10000 - $dy))}] + set y1 [expr {$y0+$dy}] + set ans [db eval {SELECT id FROM t2 WHERE x1>=$x0 AND x0<=$x1 AND y1>=$y0 AND y0<=$y1 ORDER BY id}] + do_execsql_test rtreeE-2.2.$i { + SELECT id FROM rt2 WHERE id MATCH breadthfirstsearch($x0,$x1,$y0,$y1) ORDER BY id + } $ans +} + +# Run query that have very deep priority queues +# +set ans [db eval {SELECT id FROM t2 WHERE x1>=0 AND x0<=5000 AND y1>=0 AND y0<=5000 ORDER BY id}] +do_execsql_test rtreeE-2.3 { + SELECT id FROM rt2 WHERE id MATCH breadthfirstsearch(0,5000,0,5000) ORDER BY id +} $ans +set ans [db eval {SELECT id FROM t2 WHERE x1>=0 AND x0<=10000 AND y1>=0 AND y0<=10000 ORDER BY id}] +do_execsql_test rtreeE-2.4 { + SELECT id FROM rt2 WHERE id MATCH breadthfirstsearch(0,10000,0,10000) ORDER BY id +} $ans + finish_test diff --git a/manifest b/manifest index 63a2d6b9ce..6eb3fbb265 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Test\scases\sand\sbug\sfixes\sfor\sthe\ssqlite3_rtree_query_callback()\nmechanism. -D 2014-04-17T14:52:20.025 +C More\stest\scases\swith\svery\slong\spriority\squeues. +D 2014-04-17T15:34:58.372 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in e4ee6d36cdf6136aee0158675a3b24dd3bf31a5a F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -135,7 +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/rtreeE.test 0878fd6bce3a62ac980e6f67ba14cc86c8f4f2b3 F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195 F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea F ext/rtree/sqlite3rtree.h 488cf8834d2012b913d33683157d3cf5f1327a69 @@ -275,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 636d2a5bc9ded2fa1df4e7d4c575eb0d3f13b334 +F src/test_rtree.c 38cdb28581d07503c9135ef73692ec8192b876b0 F src/test_schema.c cd12a2223c3a394f4d07bb93bdf6d344c5c121b6 F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe F src/test_sqllog.c c1c1bbedbcaf82b93d83e4f9dd990e62476a680e @@ -1176,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 32a13870175a1dd1d33af3572dde09ff607a04b6 -R 1f38906450e19fda5c27a4543b70f18c +P 1ccaaed6b516ec2ce953c1b31025a82ba76d00e7 +R 449eb9a5ce303ca34184a4306bd9d328 U drh -Z 26a5b86beaa061e155488cf6e80f8d30 +Z 39039e877d4cb95ab625ec95bd5cc650 diff --git a/manifest.uuid b/manifest.uuid index 07ccb10268..f923c4ba43 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1ccaaed6b516ec2ce953c1b31025a82ba76d00e7 \ No newline at end of file +71692aa97c78676f0ba80eaeec0ad9ac225f4427 \ No newline at end of file diff --git a/src/test_rtree.c b/src/test_rtree.c index c1951a896b..51e991949c 100644 --- a/src/test_rtree.c +++ b/src/test_rtree.c @@ -36,6 +36,7 @@ struct Circle { double centery; double radius; double mxArea; + int eScoreType; }; /* @@ -175,10 +176,10 @@ static int circle_query_func(sqlite3_rtree_query_info *p){ ** 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, + /* Test that the correct number of parameters (4) 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; + if( p->nParam!=4 || p->aParam[2]<0.0 ) return SQLITE_ERROR; /* Allocate a structure to cache parameter data in. Return SQLITE_NOMEM ** if the allocation fails. */ @@ -193,6 +194,7 @@ static int circle_query_func(sqlite3_rtree_query_info *p){ pCircle->centerx = p->aParam[0]; pCircle->centery = p->aParam[1]; pCircle->radius = p->aParam[2]; + pCircle->eScoreType = (int)p->aParam[3]; /* 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 @@ -247,11 +249,21 @@ static int circle_query_func(sqlite3_rtree_query_info *p){ } } - if( p->iLevel==2 ){ - p->rScore = 1.0 - (xmax-xmin)*(ymax-ymin)/pCircle->mxArea; - if( p->rScore<0.01 ) p->rScore = 0.01; + if( pCircle->eScoreType==1 ){ + /* Depth first search */ + p->rScore = p->iLevel; + }else if( pCircle->eScoreType==2 ){ + /* Breadth first search */ + p->rScore = 100 - p->iLevel; }else{ - p->rScore = 0.0; + /* Depth-first search, except sort the leaf nodes by area with + ** the largest area first */ + 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; @@ -262,6 +274,39 @@ static int circle_query_func(sqlite3_rtree_query_info *p){ } return SQLITE_OK; } +/* +** Implementation of "breadthfirstsearch" r-tree geometry callback using the +** 2nd-generation interface that allows scoring. +** +** ... WHERE id MATCH breadthfirstsearch($x0,$x1,$y0,$y1) ... +** +** It returns all entries whose bounding boxes overlap with $x0,$x1,$y0,$y1. +*/ +static int bfs_query_func(sqlite3_rtree_query_info *p){ + double x0,x1,y0,y1; /* Dimensions of box being tested */ + double bx0,bx1,by0,by1; /* Boundary of the query function */ + + if( p->nParam!=4 ) return SQLITE_ERROR; + x0 = p->aCoord[0]; + x1 = p->aCoord[1]; + y0 = p->aCoord[2]; + y1 = p->aCoord[3]; + bx0 = p->aParam[0]; + bx1 = p->aParam[1]; + by0 = p->aParam[2]; + by1 = p->aParam[3]; + p->rScore = 100 - p->iLevel; + if( p->eParentWithin==FULLY_WITHIN ){ + p->eWithin = FULLY_WITHIN; + }else if( x0>=bx0 && x1<=bx1 && y0>=by0 && y1<=by1 ){ + p->eWithin = FULLY_WITHIN; + }else if( x1>=bx0 && x0<=bx1 && y1>=by0 && y0<=by1 ){ + p->eWithin = PARTLY_WITHIN; + }else{ + p->eWithin = NOT_WITHIN; + } + return SQLITE_OK; +} /* END of implementation of "circle" geometry callback. ************************************************************************** @@ -402,6 +447,10 @@ static int register_circle_geom( rc = sqlite3_rtree_query_callback(db, "Qcircle", circle_query_func, 0, 0); } + if( rc==SQLITE_OK ){ + rc = sqlite3_rtree_query_callback(db, "breadthfirstsearch", + bfs_query_func, 0, 0); + } Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); #endif return TCL_OK; From a5db90fc8ee2e7dfdd1093f9a6ed4dc27ad7f1c4 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 17 Apr 2014 23:23:29 +0000 Subject: [PATCH 16/21] Performance optimization on byte-swapping in R-Tree. FossilOrigin-Name: 444084fd620fc3f45cfb87b83f532d76bd2744e7 --- ext/rtree/rtree.c | 28 +++++++++++++++++++++------- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 53414af7ee..620c158288 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -243,6 +243,7 @@ struct RtreeCursor { union RtreeCoord { RtreeValue f; /* Floating point value */ int i; /* Integer value */ + u32 u; /* Unsigned for byte-order conversions */ }; /* @@ -902,19 +903,32 @@ static int rtreeEof(sqlite3_vtab_cursor *cur){ } /* -** Convert raw bits from the on-disk RTree record into a coordinate value -** The on-disk record stores integer coordinates if eInt is true and it -** stores 32-bit floating point records if eInt is false. a[] is the four +** Convert raw bits from the on-disk RTree record into a coordinate value. +** The on-disk format is big-endian and needs to be converted for little-endian +** platforms. The on-disk record stores integer coordinates if eInt is true +** and it stores 32-bit floating point records if eInt is false. a[] is the four ** bytes of the on-disk record to be decoded. Store the results in "r". +** +** The first version of this macro is fast on x86, x86_64 and ARM, all of which +** are little-endian. The second version of this macro is cross-platform but +** takes twice as long, according to valgrind on linux x64. */ +#if defined(__x86) || defined(__x86_64) || defined(__arm__) || defined(_MSC_VER) #define RTREE_DECODE_COORD(eInt, a, r) { \ - u32 x; /* Raw bits of the coordinate value */ \ RtreeCoord c; /* Coordinate decoded */ \ - x = ((u32)a[0]<<24) + ((u32)a[1]<<16) \ - +((u32)a[2]<<8) + a[3]; \ - c.i = *(int*)&x; \ + memcpy(&c.u,a,4); \ + c.u = ((c.u>>24)&0xff)|((c.u>>8)&0xff00)| \ + ((c.u&0xff)<<24)|((c.u&0xff00)<<8); \ r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \ } +#else +#define RTREE_DECODE_COORD(eInt, a, r) { \ + RtreeCoord c; /* Coordinate decoded */ \ + c.u = ((u32)a[0]<<24) + ((u32)a[1]<<16) \ + +((u32)a[2]<<8) + a[3]; \ + r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \ +} +#endif /* diff --git a/manifest b/manifest index 6eb3fbb265..f00e6668a6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C More\stest\scases\swith\svery\slong\spriority\squeues. -D 2014-04-17T15:34:58.372 +C Performance\soptimization\son\sbyte-swapping\sin\sR-Tree. +D 2014-04-17T23:23:29.676 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 6a47918e44697dd32f5bba8a79d3490e56bd76c9 +F ext/rtree/rtree.c 5cf5ae21ca1742be863f025ce64e4262fc6def4c F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree1.test cf679265ecafff494a768ac9c2f43a70915a6290 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba @@ -1176,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 1ccaaed6b516ec2ce953c1b31025a82ba76d00e7 -R 449eb9a5ce303ca34184a4306bd9d328 +P 71692aa97c78676f0ba80eaeec0ad9ac225f4427 +R f1ebd50a7930430ccdb2dd222ebc5021 U drh -Z 39039e877d4cb95ab625ec95bd5cc650 +Z 8e204d3bb697cd291df3586d6cea66ab diff --git a/manifest.uuid b/manifest.uuid index f923c4ba43..41369a2d98 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -71692aa97c78676f0ba80eaeec0ad9ac225f4427 \ No newline at end of file +444084fd620fc3f45cfb87b83f532d76bd2744e7 \ No newline at end of file From ec7c03a08ba8fa274c38dca3d00c83ea0b518286 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 18 Apr 2014 01:37:08 +0000 Subject: [PATCH 17/21] Further improvements to the RTREE_DECODE_COORD() method, to take advantage of known processor byte orders when available. This makes the code 3% faster, according to valgrind. Also add test cases to make sure the on-disk representation is correct. FossilOrigin-Name: 6f3e94f4b1b403cd7bfc5e8e0ffbd61b5174d3a4 --- ext/rtree/rtree.c | 34 +++++++++++++++++++++++----------- ext/rtree/rtree1.test | 19 +++++++++++-------- manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 42 insertions(+), 27 deletions(-) diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 620c158288..bcaa4d635a 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -904,16 +904,21 @@ static int rtreeEof(sqlite3_vtab_cursor *cur){ /* ** Convert raw bits from the on-disk RTree record into a coordinate value. -** The on-disk format is big-endian and needs to be converted for little-endian -** platforms. The on-disk record stores integer coordinates if eInt is true -** and it stores 32-bit floating point records if eInt is false. a[] is the four -** bytes of the on-disk record to be decoded. Store the results in "r". +** The on-disk format is big-endian and needs to be converted for little- +** endian platforms. The on-disk record stores integer coordinates if +** eInt is true and it stores 32-bit floating point records if eInt is +** false. a[] is the four bytes of the on-disk record to be decoded. +** Store the results in "r". ** -** The first version of this macro is fast on x86, x86_64 and ARM, all of which -** are little-endian. The second version of this macro is cross-platform but -** takes twice as long, according to valgrind on linux x64. +** There are three versions of this macro, one each for little-endian and +** big-endian processors and a third generic implementation. The endian- +** specific implementations are much faster and are preferred if the +** processor endianness is known at compile-time. The SQLITE_BYTEORDER +** macro is part of sqliteInt.h and hence the endian-specific +** implementation will only be used if this module is compiled as part +** of the amalgamation. */ -#if defined(__x86) || defined(__x86_64) || defined(__arm__) || defined(_MSC_VER) +#if defined(SQLITE_BYTEORDER) && SQLITE_BYTEORDER==1234 #define RTREE_DECODE_COORD(eInt, a, r) { \ RtreeCoord c; /* Coordinate decoded */ \ memcpy(&c.u,a,4); \ @@ -921,6 +926,12 @@ static int rtreeEof(sqlite3_vtab_cursor *cur){ ((c.u&0xff)<<24)|((c.u&0xff00)<<8); \ r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \ } +#elif defined(SQLITE_BYTEORDER) && SQLITE_BYTEORDER==4321 +#define RTREE_DECODE_COORD(eInt, a, r) { \ + RtreeCoord c; /* Coordinate decoded */ \ + memcpy(&c.u,a,4); \ + r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \ +} #else #define RTREE_DECODE_COORD(eInt, a, r) { \ RtreeCoord c; /* Coordinate decoded */ \ @@ -929,7 +940,6 @@ static int rtreeEof(sqlite3_vtab_cursor *cur){ r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \ } #endif - /* ** Check the RTree node or entry given by pCellData and p against the MATCH @@ -2242,7 +2252,8 @@ static int SplitNode( memset(pLeft->zData, 0, pRtree->iNodeSize); memset(pRight->zData, 0, pRtree->iNodeSize); - rc = splitNodeStartree(pRtree, aCell, nCell, pLeft, pRight,&leftbbox,&rightbbox); + rc = splitNodeStartree(pRtree, aCell, nCell, pLeft, pRight, + &leftbbox, &rightbbox); if( rc!=SQLITE_OK ){ goto splitnode_out; } @@ -3003,7 +3014,8 @@ static int rtreeSqlInit( char *zCreate = sqlite3_mprintf( "CREATE TABLE \"%w\".\"%w_node\"(nodeno INTEGER PRIMARY KEY, data BLOB);" "CREATE TABLE \"%w\".\"%w_rowid\"(rowid INTEGER PRIMARY KEY, nodeno INTEGER);" -"CREATE TABLE \"%w\".\"%w_parent\"(nodeno INTEGER PRIMARY KEY, parentnode INTEGER);" +"CREATE TABLE \"%w\".\"%w_parent\"(nodeno INTEGER PRIMARY KEY," + " parentnode INTEGER);" "INSERT INTO '%q'.'%q_node' VALUES(1, zeroblob(%d))", zDb, zPrefix, zDb, zPrefix, zDb, zPrefix, zDb, zPrefix, pRtree->iNodeSize ); diff --git a/ext/rtree/rtree1.test b/ext/rtree/rtree1.test index 275b13264f..9de5362781 100644 --- a/ext/rtree/rtree1.test +++ b/ext/rtree/rtree1.test @@ -120,12 +120,13 @@ proc execsql_intout {sql} { # Test that it is possible to open an existing database that contains # r-tree tables. # -do_test rtree-1.4.1 { - execsql { - CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2); - INSERT INTO t1 VALUES(1, 5.0, 10.0); - INSERT INTO t1 VALUES(2, 15.0, 20.0); - } +do_execsql_test rtree-1.4.1a { + CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2); + INSERT INTO t1 VALUES(1, 5.0, 10.0); + SELECT substr(hex(data),1,40) FROM t1_node; +} {00000001000000000000000140A0000041200000} +do_execsql_test rtree-1.4.1b { + INSERT INTO t1 VALUES(2, 15.0, 20.0); } {} do_test rtree-1.4.2 { db close @@ -435,16 +436,18 @@ do_test rtree-11.2 { # Test on-conflict clause handling. # db_delete_and_reopen -do_execsql_test 12.0 { +do_execsql_test 12.0.1 { CREATE VIRTUAL TABLE t1 USING rtree_i32(idx, x1, x2, y1, y2); INSERT INTO t1 VALUES(1, 1, 2, 3, 4); + SELECT substr(hex(data),1,56) FROM t1_node; +} {00000001000000000000000100000001000000020000000300000004} +do_execsql_test 12.0.2 { INSERT INTO t1 VALUES(2, 2, 3, 4, 5); INSERT INTO t1 VALUES(3, 3, 4, 5, 6); CREATE TABLE source(idx, x1, x2, y1, y2); INSERT INTO source VALUES(5, 8, 8, 8, 8); INSERT INTO source VALUES(2, 7, 7, 7, 7); - } db_save_and_close foreach {tn sql_template testdata} { diff --git a/manifest b/manifest index 967e3767a3..760724c749 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sthe\slatest\schanges\sfrom\ssessions. -D 2014-04-18T01:14:46.009 +C Further\simprovements\sto\sthe\sRTREE_DECODE_COORD()\smethod,\sto\stake\sadvantage\nof\sknown\sprocessor\sbyte\sorders\swhen\savailable.\s\sThis\smakes\sthe\scode\s3%\sfaster,\naccording\sto\svalgrind.\s\sAlso\sadd\stest\scases\sto\smake\ssure\sthe\son-disk\nrepresentation\sis\scorrect. +D 2014-04-18T01:37:08.946 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in e4ee6d36cdf6136aee0158675a3b24dd3bf31a5a F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -120,9 +120,9 @@ 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 5cf5ae21ca1742be863f025ce64e4262fc6def4c +F ext/rtree/rtree.c b8357523ca0aa01ec8efffdec0868909e3744257 F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e -F ext/rtree/rtree1.test cf679265ecafff494a768ac9c2f43a70915a6290 +F ext/rtree/rtree1.test e2da4aaa426918d27122d1a1066c6ecf8409a514 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc F ext/rtree/rtree4.test c8fe384f60ebd49540a5fecc990041bf452eb6e0 @@ -1177,7 +1177,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 444084fd620fc3f45cfb87b83f532d76bd2744e7 95e77efe076ab421bd246119c47dba5dacf9d087 -R e9845f4b2c71a18e1d378888de7ae7a2 +P d9eef5b03c7c4bb69c11eda41152ee81aed1cac7 +R fd5f15a432e8499b9e125ee6ec5ed14e U drh -Z 0c4fc380d884463ecab3c74420a85e5e +Z 4e8eac956a9a7b7e58efbf89636dab07 diff --git a/manifest.uuid b/manifest.uuid index 2112ab381a..ddcb25049d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d9eef5b03c7c4bb69c11eda41152ee81aed1cac7 \ No newline at end of file +6f3e94f4b1b403cd7bfc5e8e0ffbd61b5174d3a4 \ No newline at end of file From a6ca7f2c1bb27886702423d9217c6ab9d63edd0a Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 21 Apr 2014 15:21:19 +0000 Subject: [PATCH 18/21] Fix an off-by-one error in setting the "iLevel" field of the sqlite3_rtree_query_info structure passed into the RTree query callback. FossilOrigin-Name: d708f159abfb3b87e2844463088d4fb7f8da9c97 --- ext/rtree/rtree.c | 2 +- manifest | 14 +++++++------- manifest.uuid | 2 +- src/test_rtree.c | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index bcaa4d635a..4f843a3d46 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -973,7 +973,7 @@ static int rtreeCallbackConstraint( *prScore = RTREE_ZERO; }else{ pInfo->aCoord = aCoord; - pInfo->iLevel = pSearch->iLevel; + pInfo->iLevel = pSearch->iLevel - 1; pInfo->rScore = pInfo->rParentScore = pSearch->rScore; pInfo->eWithin = pInfo->eParentWithin = pSearch->eWithin; rc = pConstraint->u.xQueryFunc(pInfo); diff --git a/manifest b/manifest index 760724c749..dd1b8e9de2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Further\simprovements\sto\sthe\sRTREE_DECODE_COORD()\smethod,\sto\stake\sadvantage\nof\sknown\sprocessor\sbyte\sorders\swhen\savailable.\s\sThis\smakes\sthe\scode\s3%\sfaster,\naccording\sto\svalgrind.\s\sAlso\sadd\stest\scases\sto\smake\ssure\sthe\son-disk\nrepresentation\sis\scorrect. -D 2014-04-18T01:37:08.946 +C Fix\san\soff-by-one\serror\sin\ssetting\sthe\s"iLevel"\sfield\sof\sthe\nsqlite3_rtree_query_info\sstructure\spassed\sinto\sthe\sRTree\squery\scallback. +D 2014-04-21T15:21:19.367 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 b8357523ca0aa01ec8efffdec0868909e3744257 +F ext/rtree/rtree.c 09000d72086c21979960426bc9b592a6007bfb8d F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree1.test e2da4aaa426918d27122d1a1066c6ecf8409a514 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba @@ -275,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 38cdb28581d07503c9135ef73692ec8192b876b0 +F src/test_rtree.c 4c1ff96a2d3a84e6a12fe8643e6d46dae0ef11de F src/test_schema.c cd12a2223c3a394f4d07bb93bdf6d344c5c121b6 F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe F src/test_sqllog.c c1c1bbedbcaf82b93d83e4f9dd990e62476a680e @@ -1177,7 +1177,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 d9eef5b03c7c4bb69c11eda41152ee81aed1cac7 -R fd5f15a432e8499b9e125ee6ec5ed14e +P 6f3e94f4b1b403cd7bfc5e8e0ffbd61b5174d3a4 +R 9cd9b8456e5c8aff94fe9550b50b870a U drh -Z 4e8eac956a9a7b7e58efbf89636dab07 +Z 1f57c8fdc97d1a8a65988c09a5e9a0f3 diff --git a/manifest.uuid b/manifest.uuid index ddcb25049d..89bf0fdeca 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6f3e94f4b1b403cd7bfc5e8e0ffbd61b5174d3a4 \ No newline at end of file +d708f159abfb3b87e2844463088d4fb7f8da9c97 \ No newline at end of file diff --git a/src/test_rtree.c b/src/test_rtree.c index 51e991949c..eb918ac5fd 100644 --- a/src/test_rtree.c +++ b/src/test_rtree.c @@ -258,7 +258,7 @@ static int circle_query_func(sqlite3_rtree_query_info *p){ }else{ /* Depth-first search, except sort the leaf nodes by area with ** the largest area first */ - if( p->iLevel==2 ){ + if( p->iLevel==1 ){ p->rScore = 1.0 - (xmax-xmin)*(ymax-ymin)/pCircle->mxArea; if( p->rScore<0.01 ) p->rScore = 0.01; }else{ From 74188329ce6084e428e3fa9ddc9d1c5419819718 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 21 Apr 2014 15:53:45 +0000 Subject: [PATCH 19/21] Be sure to initialize the sqlite3_rtree_query_info.iRowid field for the leaves of the R-Tree when doing a query callback search. FossilOrigin-Name: 4394693882c04c19ebe87ef7547c57e679554397 --- ext/rtree/rtree.c | 3 +++ manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 4f843a3d46..a471514c53 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -962,6 +962,9 @@ static int rtreeCallbackConstraint( assert( pConstraint->op==RTREE_MATCH || pConstraint->op==RTREE_QUERY ); assert( nCoord==2 || nCoord==4 || nCoord==6 || nCoord==8 || nCoord==10 ); + if( pConstraint->op==RTREE_MATCH && pSearch->iLevel==1 ){ + pInfo->iRowid = readInt64(pCellData); + } pCellData += 8; for(i=0; i Date: Mon, 21 Apr 2014 18:13:37 +0000 Subject: [PATCH 20/21] Fix the generation of sqlite3_rtree_query_info.iRowid and add test cases to verify that it is fixed. FossilOrigin-Name: eba95ead49f8f8ce45d400186562ff0066537c5c --- ext/rtree/rtree.c | 2 +- ext/rtree/rtreeE.test | 10 ++++++++++ manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/test_rtree.c | 10 +++++++++- 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index a471514c53..a6c99cb9ee 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -962,7 +962,7 @@ static int rtreeCallbackConstraint( assert( pConstraint->op==RTREE_MATCH || pConstraint->op==RTREE_QUERY ); assert( nCoord==2 || nCoord==4 || nCoord==6 || nCoord==8 || nCoord==10 ); - if( pConstraint->op==RTREE_MATCH && pSearch->iLevel==1 ){ + if( pConstraint->op==RTREE_QUERY && pSearch->iLevel==1 ){ pInfo->iRowid = readInt64(pCellData); } pCellData += 8; diff --git a/ext/rtree/rtreeE.test b/ext/rtree/rtreeE.test index bc97943dcb..c450623790 100644 --- a/ext/rtree/rtreeE.test +++ b/ext/rtree/rtreeE.test @@ -67,6 +67,16 @@ do_execsql_test rtreeE-1.4 { SELECT id FROM rt1 WHERE id MATCH Qcircle(0,0,1000,3) AND id%100==0 } {200 100 0} +# Exclude odd rowids on a depth-first search +do_execsql_test rtreeE-1.5 { + SELECT id FROM rt1 WHERE id MATCH Qcircle(0,0,1000,4) ORDER BY +id +} {0 2 4 6 8 10 12 14 16 18 20 22 24 100 102 104 106 108 110 112 114 116 118 120 122 124 200 202 204 206 208 210 212 214 216 218 220 222 224} + +# Exclude odd rowids on a breadth-first search. +do_execsql_test rtreeE-1.6 { + SELECT id FROM rt1 WHERE id MATCH Qcircle(0,0,1000,5) ORDER BY +id +} {0 2 4 6 8 10 12 14 16 18 20 22 24 100 102 104 106 108 110 112 114 116 118 120 122 124 200 202 204 206 208 210 212 214 216 218 220 222 224} + # Construct a large 2-D RTree with thousands of random entries. # do_test rtreeE-2.1 { diff --git a/manifest b/manifest index 992786f329..a115e0886e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Be\ssure\sto\sinitialize\sthe\ssqlite3_rtree_query_info.iRowid\sfield\sfor\sthe\nleaves\sof\sthe\sR-Tree\swhen\sdoing\sa\squery\scallback\ssearch. -D 2014-04-21T15:53:45.432 +C Fix\sthe\sgeneration\sof\ssqlite3_rtree_query_info.iRowid\sand\sadd\stest\scases\nto\sverify\sthat\sit\sis\sfixed. +D 2014-04-21T18:13:37.363 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 2474c438614edfc171bd9a85e3374f732511d962 +F ext/rtree/rtree.c 77fdd459e7edf78a385a83930e2b072b2f0131b5 F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree1.test e2da4aaa426918d27122d1a1066c6ecf8409a514 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba @@ -135,7 +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 0878fd6bce3a62ac980e6f67ba14cc86c8f4f2b3 +F ext/rtree/rtreeE.test 388c1c8602c3ce55c15f03b509e9cf545fb7c41f F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195 F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea F ext/rtree/sqlite3rtree.h 488cf8834d2012b913d33683157d3cf5f1327a69 @@ -275,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 4c1ff96a2d3a84e6a12fe8643e6d46dae0ef11de +F src/test_rtree.c fdd8d29ca5165c7857987a2ba263fac5c69e231f F src/test_schema.c cd12a2223c3a394f4d07bb93bdf6d344c5c121b6 F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe F src/test_sqllog.c c1c1bbedbcaf82b93d83e4f9dd990e62476a680e @@ -1177,7 +1177,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 d708f159abfb3b87e2844463088d4fb7f8da9c97 -R b6276022832016ce4c25e5317a578a57 +P 4394693882c04c19ebe87ef7547c57e679554397 +R 9a6527f0d42b13ef1dcd87b1f989aba6 U drh -Z 8b38b422713df2439dae120af50d35b5 +Z 8819b772d7d2f245af8d8a9675d9c9d8 diff --git a/manifest.uuid b/manifest.uuid index ceffd746e2..d92fc37198 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4394693882c04c19ebe87ef7547c57e679554397 \ No newline at end of file +eba95ead49f8f8ce45d400186562ff0066537c5c \ No newline at end of file diff --git a/src/test_rtree.c b/src/test_rtree.c index eb918ac5fd..9d19fa0e2c 100644 --- a/src/test_rtree.c +++ b/src/test_rtree.c @@ -255,7 +255,7 @@ static int circle_query_func(sqlite3_rtree_query_info *p){ }else if( pCircle->eScoreType==2 ){ /* Breadth first search */ p->rScore = 100 - p->iLevel; - }else{ + }else if( pCircle->eScoreType==3 ){ /* Depth-first search, except sort the leaf nodes by area with ** the largest area first */ if( p->iLevel==1 ){ @@ -264,6 +264,14 @@ static int circle_query_func(sqlite3_rtree_query_info *p){ }else{ p->rScore = 0.0; } + }else if( pCircle->eScoreType==4 ){ + /* Depth-first search, except exclude odd rowids */ + p->rScore = p->iLevel; + if( p->iRowid&1 ) nWithin = 0; + }else{ + /* Breadth-first search, except exclude odd rowids */ + p->rScore = 100 - p->iLevel; + if( p->iRowid&1 ) nWithin = 0; } if( nWithin==0 ){ p->eWithin = NOT_WITHIN; From c686929914188d138527dcf460c025ef4b3638e9 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 25 Apr 2014 16:29:03 +0000 Subject: [PATCH 21/21] Enhance the sqlite3_rtree_query_info object to report on the number of elements in the priority queue at each level. FossilOrigin-Name: f7dad408dd46a1e3612b6142a3afb1d0d4fcda00 --- ext/rtree/rtree.c | 20 ++++++++++++++------ ext/rtree/sqlite3rtree.h | 2 ++ manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index a6c99cb9ee..7b540b4be1 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -228,9 +228,11 @@ struct RtreeCursor { RtreeConstraint *aConstraint; /* Search constraints. */ int nPointAlloc; /* Number of slots allocated for aPoint[] */ int nPoint; /* Number of slots used in aPoint[] */ + int mxLevel; /* iLevel value for root of the tree */ RtreeSearchPoint *aPoint; /* Priority queue for search points */ RtreeSearchPoint sPoint; /* Cached next search point */ RtreeNode *aNode[RTREE_CACHE_SZ]; /* Rtree node cache */ + u32 anQueue[RTREE_MAX_DEPTH+1]; /* Number of queued entries by iLevel */ }; /* Return the Rtree of a RtreeCursor */ @@ -1179,7 +1181,8 @@ static RtreeSearchPoint *rtreeEnqueue( i = pCur->nPoint++; pNew = pCur->aPoint + i; pNew->rScore = rScore; - pNew->iLevel = iLevel; + pNew->iLevel = iLevel; + assert( iLevel>=0 && iLevel<=RTREE_MAX_DEPTH ); while( i>0 ){ RtreeSearchPoint *pParent; j = (i-1)/2; @@ -1203,6 +1206,7 @@ static RtreeSearchPoint *rtreeSearchPointNew( ){ RtreeSearchPoint *pNew, *pFirst; pFirst = rtreeSearchPointFirst(pCur); + pCur->anQueue[iLevel]++; if( pFirst==0 || pFirst->rScore>rScore || (pFirst->rScore==rScore && pFirst->iLevel>iLevel) @@ -1271,8 +1275,10 @@ static void rtreeSearchPointPop(RtreeCursor *p){ p->aNode[i] = 0; } if( p->bPoint ){ + p->anQueue[p->sPoint.iLevel]--; p->bPoint = 0; }else if( p->nPoint ){ + p->anQueue[p->aPoint[0].iLevel]--; n = --p->nPoint; p->aPoint[0] = p->aPoint[n]; if( n0 ){ + rc = nodeAcquire(pRtree, 1, 0, &pRoot); + if( rc==SQLITE_OK && argc>0 ){ pCsr->aConstraint = sqlite3_malloc(sizeof(RtreeConstraint)*argc); pCsr->nConstraint = argc; if( !pCsr->aConstraint ){ rc = SQLITE_NOMEM; }else{ memset(pCsr->aConstraint, 0, sizeof(RtreeConstraint)*argc); + memset(pCsr->anQueue, 0, sizeof(u32)*(pRtree->iDepth + 1)); assert( (idxStr==0 && argc==0) || (idxStr && (int)strlen(idxStr)==argc*2) ); for(ii=0; iipInfo->nCoord = pRtree->nDim*2; + p->pInfo->anQueue = pCsr->anQueue; + p->pInfo->mxLevel = pRtree->iDepth + 1; }else{ #ifdef SQLITE_RTREE_INT_ONLY p->u.rValue = sqlite3_value_int64(argv[ii]); @@ -1586,10 +1596,6 @@ static int rtreeFilter( } } } - - if( rc==SQLITE_OK ){ - rc = nodeAcquire(pRtree, 1, 0, &pRoot); - } if( rc==SQLITE_OK ){ RtreeSearchPoint *pNew; pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, pRtree->iDepth+1); @@ -1599,11 +1605,13 @@ static int rtreeFilter( pNew->eWithin = PARTLY_WITHIN; assert( pCsr->bPoint==1 ); pCsr->aNode[0] = pRoot; + pRoot = 0; RTREE_QUEUE_TRACE(pCsr, "PUSH-Fm:"); rc = rtreeStepToLeaf(pCsr); } } + nodeRelease(pRtree, pRoot); rtreeRelease(pRtree); return rc; } diff --git a/ext/rtree/sqlite3rtree.h b/ext/rtree/sqlite3rtree.h index 3f4df4ed89..5de0508d00 100644 --- a/ext/rtree/sqlite3rtree.h +++ b/ext/rtree/sqlite3rtree.h @@ -89,8 +89,10 @@ struct sqlite3_rtree_query_info { void *pUser; /* callback can use this, if desired */ void (*xDelUser)(void*); /* function to free pUser */ sqlite3_rtree_dbl *aCoord; /* Coordinates of node or entry to check */ + unsigned int *anQueue; /* Number of pending entries in the queue */ int nCoord; /* Number of coordinates */ int iLevel; /* Level of current node or entry */ + int mxLevel; /* The largest iLevel value in the tree */ sqlite3_int64 iRowid; /* Rowid for current entry */ sqlite3_rtree_dbl rParentScore; /* Score of parent node */ int eParentWithin; /* Visibility of parent node */ diff --git a/manifest b/manifest index a115e0886e..707f14a0a3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\sgeneration\sof\ssqlite3_rtree_query_info.iRowid\sand\sadd\stest\scases\nto\sverify\sthat\sit\sis\sfixed. -D 2014-04-21T18:13:37.363 +C Enhance\sthe\ssqlite3_rtree_query_info\sobject\sto\sreport\son\sthe\snumber\sof\s\nelements\sin\sthe\spriority\squeue\sat\seach\slevel. +D 2014-04-25T16:29:03.796 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 77fdd459e7edf78a385a83930e2b072b2f0131b5 +F ext/rtree/rtree.c 6f70db93e0e42c369325c5cddcf2024c5a87ca43 F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree1.test e2da4aaa426918d27122d1a1066c6ecf8409a514 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba @@ -138,7 +138,7 @@ F ext/rtree/rtreeD.test 636630357638f5983701550b37f0f5867130d2ca F ext/rtree/rtreeE.test 388c1c8602c3ce55c15f03b509e9cf545fb7c41f F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195 F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea -F ext/rtree/sqlite3rtree.h 488cf8834d2012b913d33683157d3cf5f1327a69 +F ext/rtree/sqlite3rtree.h 83349d519fe5f518b3ea025d18dd1fe51b1684bd F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 F ext/session/session1.test 894e3bc9f497c4fa07a2aa3271e3911f3670c3d8 @@ -1177,7 +1177,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 4394693882c04c19ebe87ef7547c57e679554397 -R 9a6527f0d42b13ef1dcd87b1f989aba6 +P eba95ead49f8f8ce45d400186562ff0066537c5c +R efef19137bd31aa6c6fdc7dea3b8daf2 U drh -Z 8819b772d7d2f245af8d8a9675d9c9d8 +Z ded30c2c8b9ed7281fad6af13254832e diff --git a/manifest.uuid b/manifest.uuid index d92fc37198..8909227722 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -eba95ead49f8f8ce45d400186562ff0066537c5c \ No newline at end of file +f7dad408dd46a1e3612b6142a3afb1d0d4fcda00 \ No newline at end of file