mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-30 19:03:16 +03:00
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
This commit is contained in:
@ -63,6 +63,7 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#ifndef SQLITE_AMALGAMATION
|
#ifndef SQLITE_AMALGAMATION
|
||||||
#include "sqlite3rtree.h"
|
#include "sqlite3rtree.h"
|
||||||
@ -86,6 +87,7 @@ typedef struct RtreeConstraint RtreeConstraint;
|
|||||||
typedef struct RtreeMatchArg RtreeMatchArg;
|
typedef struct RtreeMatchArg RtreeMatchArg;
|
||||||
typedef struct RtreeGeomCallback RtreeGeomCallback;
|
typedef struct RtreeGeomCallback RtreeGeomCallback;
|
||||||
typedef union RtreeCoord RtreeCoord;
|
typedef union RtreeCoord RtreeCoord;
|
||||||
|
typedef struct RtreeSearchPoint RtreeSearchPoint;
|
||||||
|
|
||||||
/* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */
|
/* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */
|
||||||
#define RTREE_MAX_DIMENSIONS 5
|
#define RTREE_MAX_DIMENSIONS 5
|
||||||
@ -165,6 +167,23 @@ struct Rtree {
|
|||||||
typedef float RtreeValue; /* Low accuracy coordinate */
|
typedef float RtreeValue; /* Low accuracy coordinate */
|
||||||
#endif
|
#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
|
** The minimum number of cells allowed for a node is a third of the
|
||||||
** maximum. In Gutman's notation:
|
** maximum. In Gutman's notation:
|
||||||
@ -187,18 +206,34 @@ struct Rtree {
|
|||||||
*/
|
*/
|
||||||
#define RTREE_MAX_DEPTH 40
|
#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.
|
** An rtree cursor object.
|
||||||
*/
|
*/
|
||||||
struct RtreeCursor {
|
struct RtreeCursor {
|
||||||
sqlite3_vtab_cursor base; /* Base class. Must be first */
|
sqlite3_vtab_cursor base; /* Base class. Must be first */
|
||||||
RtreeNode *pNode; /* Node cursor is currently pointing at */
|
u8 atEOF; /* True if at end of search */
|
||||||
int iCell; /* Index of current cell in pNode */
|
u8 bPoint; /* True if sPoint is valid */
|
||||||
int iStrategy; /* Copy of idxNum search parameter */
|
int iStrategy; /* Copy of idxNum search parameter */
|
||||||
int nConstraint; /* Number of entries in aConstraint */
|
int nConstraint; /* Number of entries in aConstraint */
|
||||||
RtreeConstraint *aConstraint; /* Search constraints. */
|
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
|
** 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.
|
** 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_MATCH 0x46 /* Old-style sqlite3_rtree_geometry_callback() */
|
||||||
#define RTREE_QUERY 0x47 /* New-style sqlite3_rtree_query_callback() */
|
#define RTREE_QUERY 0x47 /* New-style sqlite3_rtree_query_callback() */
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** An rtree structure node.
|
** An rtree structure node.
|
||||||
*/
|
*/
|
||||||
@ -838,12 +874,13 @@ static void freeCursorConstraints(RtreeCursor *pCsr){
|
|||||||
*/
|
*/
|
||||||
static int rtreeClose(sqlite3_vtab_cursor *cur){
|
static int rtreeClose(sqlite3_vtab_cursor *cur){
|
||||||
Rtree *pRtree = (Rtree *)(cur->pVtab);
|
Rtree *pRtree = (Rtree *)(cur->pVtab);
|
||||||
int rc;
|
int ii;
|
||||||
RtreeCursor *pCsr = (RtreeCursor *)cur;
|
RtreeCursor *pCsr = (RtreeCursor *)cur;
|
||||||
freeCursorConstraints(pCsr);
|
freeCursorConstraints(pCsr);
|
||||||
rc = nodeRelease(pRtree, pCsr->pNode);
|
sqlite3_free(pCsr->aPoint);
|
||||||
|
for(ii=0; ii<RTREE_CACHE_SZ; ii++) nodeRelease(pRtree, pCsr->aNode[ii]);
|
||||||
sqlite3_free(pCsr);
|
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){
|
static int rtreeEof(sqlite3_vtab_cursor *cur){
|
||||||
RtreeCursor *pCsr = (RtreeCursor *)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
|
** The r-tree constraint passed as the second argument to this function is
|
||||||
** guaranteed to be a MATCH constraint.
|
** guaranteed to be a MATCH constraint.
|
||||||
*/
|
*/
|
||||||
static int testRtreeGeom(
|
static int rtreeTestGeom(
|
||||||
Rtree *pRtree, /* R-Tree object */
|
Rtree *pRtree, /* R-Tree object */
|
||||||
RtreeConstraint *pConstraint, /* MATCH constraint to test */
|
RtreeConstraint *pConstraint, /* MATCH constraint to test */
|
||||||
RtreeCell *pCell, /* Cell 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.
|
** 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
|
** Set *peWithin to NOT_WITHIN if the constraints in pCursor->aConstraint[]
|
||||||
** (excluded) by the constraints in the pCursor->aConstraint[]
|
** are guaranteed to never be satisfied by any subelement under the
|
||||||
** array, or false otherwise.
|
** 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
|
** 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){
|
static int rtreeTestCell(
|
||||||
RtreeCell cell;
|
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 ii;
|
||||||
int bRes = 0;
|
int bOutOfBounds = 0;
|
||||||
int rc = SQLITE_OK;
|
int rc = SQLITE_OK;
|
||||||
|
Rtree *pRtree = RTREE_OF_CURSOR(pCursor);
|
||||||
|
|
||||||
nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell);
|
for(ii=0; bOutOfBounds==0 && ii<pCursor->nConstraint; ii++){
|
||||||
for(ii=0; bRes==0 && ii<pCursor->nConstraint; ii++){
|
|
||||||
RtreeConstraint *p = &pCursor->aConstraint[ii];
|
RtreeConstraint *p = &pCursor->aConstraint[ii];
|
||||||
RtreeDValue cell_min = DCOORD(cell.aCoord[(p->iCoord>>1)*2]);
|
RtreeDValue cell_min = DCOORD(pCell->aCoord[(p->iCoord>>1)*2]);
|
||||||
RtreeDValue cell_max = DCOORD(cell.aCoord[(p->iCoord>>1)*2+1]);
|
RtreeDValue cell_max = DCOORD(pCell->aCoord[(p->iCoord>>1)*2+1]);
|
||||||
|
|
||||||
assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
|
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
|
|| 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 ){
|
switch( p->op ){
|
||||||
case RTREE_LE: case RTREE_LT:
|
case RTREE_LE: case RTREE_LT:
|
||||||
bRes = p->u.rValue<cell_min;
|
bOutOfBounds = p->u.rValue<cell_min;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RTREE_GE: case RTREE_GT:
|
case RTREE_GE: case RTREE_GT:
|
||||||
bRes = p->u.rValue>cell_max;
|
bOutOfBounds = p->u.rValue>cell_max;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RTREE_EQ:
|
case RTREE_EQ:
|
||||||
bRes = (p->u.rValue>cell_max || p->u.rValue<cell_min);
|
bOutOfBounds = (p->u.rValue>cell_max || p->u.rValue<cell_min);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
assert( p->op==RTREE_MATCH );
|
assert( p->op==RTREE_MATCH );
|
||||||
rc = testRtreeGeom(pRtree, p, &cell, &bRes);
|
rc = rtreeTestGeom(pRtree, p, pCell, &bOutOfBounds);
|
||||||
bRes = !bRes;
|
bOutOfBounds = !bOutOfBounds;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*pbEof = bRes;
|
*peWithin = bOutOfBounds ? NOT_WITHIN : PARTLY_WITHIN;
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Test if the cell that cursor pCursor currently points to
|
** pCursor points to a leaf r-tree entry which is a candidate for output.
|
||||||
** would be filtered (excluded) by the constraints in the
|
** This routine sets *peWithin to one of NOT_WITHIN, PARTLY_WITHIN, or
|
||||||
** pCursor->aConstraint[] array. If so, set *pbEof to true before
|
** FULLY_WITHIN depending on whether or not the leaf entry is completely
|
||||||
** returning. If the cell is not filtered (excluded) by the constraints,
|
** outside the region defined by pCursor->aConstraints[], or overlaps the
|
||||||
** set pbEof to zero.
|
** 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
|
** Return SQLITE_OK if successful or an SQLite error code if an error
|
||||||
** occurs within a geometry callback.
|
** occurs within a geometry callback.
|
||||||
**
|
**
|
||||||
** This function assumes that the cell is part of a leaf node.
|
** This function assumes that the cell is part of a leaf node.
|
||||||
*/
|
*/
|
||||||
static int testRtreeEntry(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){
|
static int rtreeTestEntry(
|
||||||
RtreeCell cell;
|
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;
|
int ii;
|
||||||
*pbEof = 0;
|
int res = 1; /* Innocent until proven guilty */
|
||||||
|
|
||||||
nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell);
|
for(ii=0; res && ii<pCursor->nConstraint; ii++){
|
||||||
for(ii=0; ii<pCursor->nConstraint; ii++){
|
|
||||||
RtreeConstraint *p = &pCursor->aConstraint[ii];
|
RtreeConstraint *p = &pCursor->aConstraint[ii];
|
||||||
RtreeDValue coord = DCOORD(cell.aCoord[p->iCoord]);
|
RtreeDValue coord = DCOORD(pCell->aCoord[p->iCoord]);
|
||||||
int res;
|
|
||||||
assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
|
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
|
|| 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: {
|
default: {
|
||||||
int rc;
|
int rc;
|
||||||
assert( p->op==RTREE_MATCH );
|
assert( p->op==RTREE_MATCH );
|
||||||
rc = testRtreeGeom(pRtree, p, &cell, &res);
|
rc = rtreeTestGeom(pRtree, p, pCell, &res);
|
||||||
if( rc!=SQLITE_OK ){
|
if( rc!=SQLITE_OK ){
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !res ){
|
|
||||||
*pbEof = 1;
|
|
||||||
return SQLITE_OK;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*peWithin = res ? FULLY_WITHIN : NOT_WITHIN;
|
||||||
return SQLITE_OK;
|
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 && ii<NCELL(pChild); ii++){
|
|
||||||
pCursor->iCell = 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
|
** 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.
|
** integer value equal to iRowid. Return the index of this cell.
|
||||||
@ -1057,6 +1052,7 @@ static int nodeRowidIndex(
|
|||||||
){
|
){
|
||||||
int ii;
|
int ii;
|
||||||
int nCell = NCELL(pNode);
|
int nCell = NCELL(pNode);
|
||||||
|
assert( nCell<200 );
|
||||||
for(ii=0; ii<nCell; ii++){
|
for(ii=0; ii<nCell; ii++){
|
||||||
if( nodeGetRowid(pRtree, pNode, ii)==iRowid ){
|
if( nodeGetRowid(pRtree, pNode, ii)==iRowid ){
|
||||||
*piIndex = ii;
|
*piIndex = ii;
|
||||||
@ -1079,48 +1075,241 @@ static int nodeParentIndex(Rtree *pRtree, RtreeNode *pNode, int *piIndex){
|
|||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Compare two search points. Return negative, zero, or positive if the first
|
||||||
|
** is less than, equal to, or greater than the second.
|
||||||
|
*/
|
||||||
|
static int rtreeSearchPointCompare(
|
||||||
|
const RtreeSearchPoint *pA,
|
||||||
|
const RtreeSearchPoint *pB
|
||||||
|
){
|
||||||
|
if( pA->rScore<pB->rScore ) return -1;
|
||||||
|
if( pA->rScore>pB->rScore ) return +1;
|
||||||
|
if( pA->iLevel<pB->iLevel ) 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( i<j );
|
||||||
|
p->aPoint[i] = p->aPoint[j];
|
||||||
|
p->aPoint[j] = t;
|
||||||
|
if( i<RTREE_CACHE_SZ-1 ){
|
||||||
|
if( j>=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)<n ){
|
||||||
|
k = j+1;
|
||||||
|
if( k<n && rtreeSearchPointCompare(&p->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->iCell<nCell ){
|
||||||
|
nodeGetCell(pRtree, pNode, p->iCell, &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.
|
** Rtree virtual table module xNext method.
|
||||||
*/
|
*/
|
||||||
static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){
|
static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){
|
||||||
Rtree *pRtree = (Rtree *)(pVtabCursor->pVtab);
|
|
||||||
RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
|
RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
|
||||||
int rc = SQLITE_OK;
|
int rc = SQLITE_OK;
|
||||||
|
|
||||||
/* RtreeCursor.pNode must not be NULL. If is is NULL, then this cursor is
|
/* Move to the next entry that matches the configured constraints. */
|
||||||
** already at EOF. It is against the rules to call the xNext() method of
|
traceTop(pCsr, "POP:");
|
||||||
** a cursor that has already reached EOF.
|
rtreeSearchPointPop(pCsr);
|
||||||
*/
|
rtreeStepToLeaf(pCsr);
|
||||||
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->iCell<nCell; pCsr->iCell++){
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1128,13 +1317,14 @@ static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){
|
|||||||
** Rtree virtual table module xRowid method.
|
** Rtree virtual table module xRowid method.
|
||||||
*/
|
*/
|
||||||
static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
|
static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
|
||||||
Rtree *pRtree = (Rtree *)pVtabCursor->pVtab;
|
|
||||||
RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
|
RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
|
||||||
|
RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr);
|
||||||
assert(pCsr->pNode);
|
int rc = SQLITE_OK;
|
||||||
*pRowid = nodeGetRowid(pRtree, pCsr->pNode, pCsr->iCell);
|
RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
|
||||||
|
if( rc==SQLITE_OK && p ){
|
||||||
return SQLITE_OK;
|
*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){
|
static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||||||
Rtree *pRtree = (Rtree *)cur->pVtab;
|
Rtree *pRtree = (Rtree *)cur->pVtab;
|
||||||
RtreeCursor *pCsr = (RtreeCursor *)cur;
|
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 ){
|
if( i==0 ){
|
||||||
i64 iRowid = nodeGetRowid(pRtree, pCsr->pNode, pCsr->iCell);
|
sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell));
|
||||||
sqlite3_result_int64(ctx, iRowid);
|
|
||||||
}else{
|
}else{
|
||||||
RtreeCoord c;
|
if( rc ) return rc;
|
||||||
nodeGetCoord(pRtree, pCsr->pNode, pCsr->iCell, i-1, &c);
|
nodeGetCoord(pRtree, pNode, p->iCell, i-1, &c);
|
||||||
#ifndef SQLITE_RTREE_INT_ONLY
|
#ifndef SQLITE_RTREE_INT_ONLY
|
||||||
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
|
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
|
||||||
sqlite3_result_double(ctx, c.f);
|
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);
|
sqlite3_result_int(ctx, c.i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return SQLITE_OK;
|
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
|
** *ppLeaf to 0 and return SQLITE_OK. If an error occurs, set *ppLeaf
|
||||||
** to zero and return an SQLite error code.
|
** 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;
|
int rc;
|
||||||
*ppLeaf = 0;
|
*ppLeaf = 0;
|
||||||
sqlite3_bind_int64(pRtree->pReadRowid, 1, iRowid);
|
sqlite3_bind_int64(pRtree->pReadRowid, 1, iRowid);
|
||||||
if( sqlite3_step(pRtree->pReadRowid)==SQLITE_ROW ){
|
if( sqlite3_step(pRtree->pReadRowid)==SQLITE_ROW ){
|
||||||
i64 iNode = sqlite3_column_int64(pRtree->pReadRowid, 0);
|
i64 iNode = sqlite3_column_int64(pRtree->pReadRowid, 0);
|
||||||
|
if( piNode ) *piNode = iNode;
|
||||||
rc = nodeAcquire(pRtree, iNode, 0, ppLeaf);
|
rc = nodeAcquire(pRtree, iNode, 0, ppLeaf);
|
||||||
sqlite3_reset(pRtree->pReadRowid);
|
sqlite3_reset(pRtree->pReadRowid);
|
||||||
}else{
|
}else{
|
||||||
@ -1239,10 +1439,10 @@ static int rtreeFilter(
|
|||||||
){
|
){
|
||||||
Rtree *pRtree = (Rtree *)pVtabCursor->pVtab;
|
Rtree *pRtree = (Rtree *)pVtabCursor->pVtab;
|
||||||
RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
|
RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
|
||||||
|
|
||||||
RtreeNode *pRoot = 0;
|
RtreeNode *pRoot = 0;
|
||||||
int ii;
|
int ii;
|
||||||
int rc = SQLITE_OK;
|
int rc = SQLITE_OK;
|
||||||
|
int iCell = 0;
|
||||||
|
|
||||||
rtreeReference(pRtree);
|
rtreeReference(pRtree);
|
||||||
|
|
||||||
@ -1252,13 +1452,16 @@ static int rtreeFilter(
|
|||||||
if( idxNum==1 ){
|
if( idxNum==1 ){
|
||||||
/* Special case - lookup by rowid. */
|
/* Special case - lookup by rowid. */
|
||||||
RtreeNode *pLeaf; /* Leaf on which the required cell resides */
|
RtreeNode *pLeaf; /* Leaf on which the required cell resides */
|
||||||
|
RtreeSearchPoint *p; /* Search point for the the leaf */
|
||||||
i64 iRowid = sqlite3_value_int64(argv[0]);
|
i64 iRowid = sqlite3_value_int64(argv[0]);
|
||||||
rc = findLeafNode(pRtree, iRowid, &pLeaf);
|
p = rtreeSearchPointNew(pCsr, 0.0, 0);
|
||||||
pCsr->pNode = pLeaf;
|
if( p==0 ) return SQLITE_NOMEM;
|
||||||
if( pLeaf ){
|
rc = findLeafNode(pRtree, iRowid, &pLeaf, &p->id);
|
||||||
assert( rc==SQLITE_OK );
|
pCsr->aNode[0] = pLeaf;
|
||||||
rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &pCsr->iCell);
|
p->eWithin = PARTLY_WITHIN;
|
||||||
}
|
if( rc ) rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &iCell);
|
||||||
|
p->iCell = iCell;
|
||||||
|
traceTop(pCsr, "PUSH:");
|
||||||
}else{
|
}else{
|
||||||
/* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array
|
/* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array
|
||||||
** with the configured constraints.
|
** with the configured constraints.
|
||||||
@ -1297,26 +1500,18 @@ static int rtreeFilter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
pCsr->pNode = 0;
|
|
||||||
rc = nodeAcquire(pRtree, 1, 0, &pRoot);
|
rc = nodeAcquire(pRtree, 1, 0, &pRoot);
|
||||||
}
|
}
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
int isEof = 1;
|
RtreeSearchPoint *pNew = rtreeSearchPointNew(pCsr, 0.0, pRtree->iDepth+1);
|
||||||
int nCell = NCELL(pRoot);
|
if( pNew==0 ) return SQLITE_NOMEM;
|
||||||
pCsr->pNode = pRoot;
|
pNew->id = 1;
|
||||||
for(pCsr->iCell=0; rc==SQLITE_OK && pCsr->iCell<nCell; pCsr->iCell++){
|
pNew->iCell = 0;
|
||||||
assert( pCsr->pNode==pRoot );
|
pNew->eWithin = PARTLY_WITHIN;
|
||||||
rc = descendToCell(pRtree, pCsr, pRtree->iDepth, &isEof);
|
assert( pCsr->bPoint==1 );
|
||||||
if( !isEof ){
|
pCsr->aNode[0] = pRoot;
|
||||||
break;
|
traceTop(pCsr, "PUSH:");
|
||||||
}
|
rc = rtreeStepToLeaf(pCsr);
|
||||||
}
|
|
||||||
if( rc==SQLITE_OK && isEof ){
|
|
||||||
assert( pCsr->pNode==pRoot );
|
|
||||||
nodeRelease(pRtree, pRoot);
|
|
||||||
pCsr->pNode = 0;
|
|
||||||
}
|
|
||||||
assert( rc!=SQLITE_OK || !pCsr->pNode || pCsr->iCell<NCELL(pCsr->pNode) );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2395,7 +2590,7 @@ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
|
|||||||
** about to be deleted.
|
** about to be deleted.
|
||||||
*/
|
*/
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
rc = findLeafNode(pRtree, iDelete, &pLeaf);
|
rc = findLeafNode(pRtree, iDelete, &pLeaf, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Delete the cell in question from the leaf node. */
|
/* Delete the cell in question from the leaf node. */
|
||||||
|
15
manifest
15
manifest
@ -1,5 +1,5 @@
|
|||||||
C Fix\scomments\son\sthe\srtreenode()\sand\srtreedepth()\stest\sfunction\sin\sthe\sR-Tree\nmodule.
|
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-14T14:43:09.474
|
D 2014-04-15T21:06:14.359
|
||||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||||
F Makefile.in e4ee6d36cdf6136aee0158675a3b24dd3bf31a5a
|
F Makefile.in e4ee6d36cdf6136aee0158675a3b24dd3bf31a5a
|
||||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||||
@ -120,7 +120,7 @@ F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
|
|||||||
F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e
|
F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e
|
||||||
F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
|
F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
|
||||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
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/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
|
||||||
F ext/rtree/rtree1.test cf679265ecafff494a768ac9c2f43a70915a6290
|
F ext/rtree/rtree1.test cf679265ecafff494a768ac9c2f43a70915a6290
|
||||||
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
|
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-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||||
F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
|
F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
|
||||||
F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
|
F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
|
||||||
P 3ba5f295c709faebf5505e61f6dc5266b811b086
|
P ade5b986e8baab9df7bdaf7ccfaee2d6ba55fa3c
|
||||||
R e7b36f9b46b38d0d4d4bc75aff5d0712
|
R 52e87385ec75daf64fa5f31a0250db29
|
||||||
|
T *branch * rtree-queue
|
||||||
|
T *sym-rtree-queue *
|
||||||
|
T -sym-rtree-enhancements *
|
||||||
U drh
|
U drh
|
||||||
Z 1c3694849e99525baeaeac92e41be917
|
Z d0176a391caa75c92333b568fa6cbbbf
|
||||||
|
@ -1 +1 @@
|
|||||||
ade5b986e8baab9df7bdaf7ccfaee2d6ba55fa3c
|
53688a25c23c394278a357829793889970aa4157
|
Reference in New Issue
Block a user