Add code to allow user-defined searches of r-tree tables. Still largely untested.
FossilOrigin-Name: 782ca3b716ee1ecb0dfb5ab6f21dfd73d41758e4
This commit is contained in:
parent
7b19facf56
commit
9508daa929
@ -26,7 +26,7 @@ BCC = @BUILD_CC@ @BUILD_CFLAGS@
|
||||
# will run on the target platform. (BCC and TCC are usually the
|
||||
# same unless your are cross-compiling.)
|
||||
#
|
||||
TCC = @CC@ @CPPFLAGS@ @CFLAGS@ -I. -I${TOP}/src
|
||||
TCC = @CC@ @CPPFLAGS@ @CFLAGS@ -I. -I${TOP}/src -I${TOP}/ext/rtree
|
||||
|
||||
# Define this for the autoconf-based build, so that the code knows it can
|
||||
# include the generated config.h
|
||||
@ -367,6 +367,7 @@ TESTSRC = \
|
||||
$(TOP)/src/test_onefile.c \
|
||||
$(TOP)/src/test_osinst.c \
|
||||
$(TOP)/src/test_pcache.c \
|
||||
$(TOP)/src/test_rtree.c \
|
||||
$(TOP)/src/test_schema.c \
|
||||
$(TOP)/src/test_server.c \
|
||||
$(TOP)/src/test_stat.c \
|
||||
@ -460,6 +461,8 @@ EXTHDR += \
|
||||
$(TOP)/ext/rtree/rtree.h
|
||||
EXTHDR += \
|
||||
$(TOP)/ext/icu/sqliteicu.h
|
||||
EXTHDR += \
|
||||
$(TOP)/ext/rtree/sqlite3rtree.h
|
||||
|
||||
# This is the default Makefile target. The objects listed here
|
||||
# are what get build when you type just "make" with no arguments.
|
||||
|
@ -64,6 +64,8 @@
|
||||
#include "sqlite3.h"
|
||||
#endif
|
||||
|
||||
#include "sqlite3rtree.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
@ -79,6 +81,7 @@ typedef struct RtreeNode RtreeNode;
|
||||
typedef struct RtreeCell RtreeCell;
|
||||
typedef struct RtreeConstraint RtreeConstraint;
|
||||
typedef union RtreeCoord RtreeCoord;
|
||||
typedef struct RtreeGeomBlob RtreeGeomBlob;
|
||||
|
||||
/* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */
|
||||
#define RTREE_MAX_DIMENSIONS 5
|
||||
@ -179,17 +182,20 @@ union RtreeCoord {
|
||||
** A search constraint.
|
||||
*/
|
||||
struct RtreeConstraint {
|
||||
int iCoord; /* Index of constrained coordinate */
|
||||
int op; /* Constraining operation */
|
||||
double rValue; /* Constraint value. */
|
||||
int iCoord; /* Index of constrained coordinate */
|
||||
int op; /* Constraining operation */
|
||||
double rValue; /* Constraint value. */
|
||||
int (*xGeom)(RtreeGeometry *, int, double *, int *);
|
||||
RtreeGeometry *pGeom; /* Constraint callback argument for a MATCH */
|
||||
};
|
||||
|
||||
/* 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_EQ 0x41
|
||||
#define RTREE_LE 0x42
|
||||
#define RTREE_LT 0x43
|
||||
#define RTREE_GE 0x44
|
||||
#define RTREE_GT 0x45
|
||||
#define RTREE_MATCH 0x46
|
||||
|
||||
/*
|
||||
** An rtree structure node.
|
||||
@ -227,6 +233,22 @@ struct RtreeCell {
|
||||
RtreeCoord aCoord[RTREE_MAX_DIMENSIONS*2];
|
||||
};
|
||||
|
||||
|
||||
#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 RtreeGeomBlob {
|
||||
u32 magic; /* Always RTREE_GEOMETRY_MAGIC */
|
||||
int (*xGeom)(RtreeGeometry *, int, double *, int *);
|
||||
void *pContext;
|
||||
int nParam;
|
||||
double aParam[1];
|
||||
};
|
||||
|
||||
#ifndef MAX
|
||||
# define MAX(x,y) ((x) < (y) ? (y) : (x))
|
||||
#endif
|
||||
@ -715,6 +737,25 @@ static int rtreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Free the RtreeCursor.aConstraint[] array and its contents.
|
||||
*/
|
||||
static void freeCursorConstraints(RtreeCursor *pCsr){
|
||||
if( pCsr->aConstraint ){
|
||||
int i; /* Used to iterate through constraint array */
|
||||
for(i=0; i<pCsr->nConstraint; i++){
|
||||
RtreeGeometry *pGeom = pCsr->aConstraint[i].pGeom;
|
||||
if( pGeom ){
|
||||
if( pGeom->xDelUser ) pGeom->xDelUser(pGeom->pUser);
|
||||
sqlite3_free(pGeom);
|
||||
}
|
||||
}
|
||||
sqlite3_free(pCsr->aConstraint);
|
||||
pCsr->aConstraint = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Rtree virtual table module xClose method.
|
||||
*/
|
||||
@ -722,7 +763,7 @@ static int rtreeClose(sqlite3_vtab_cursor *cur){
|
||||
Rtree *pRtree = (Rtree *)(cur->pVtab);
|
||||
int rc;
|
||||
RtreeCursor *pCsr = (RtreeCursor *)cur;
|
||||
sqlite3_free(pCsr->aConstraint);
|
||||
freeCursorConstraints(pCsr);
|
||||
rc = nodeRelease(pRtree, pCsr->pNode);
|
||||
sqlite3_free(pCsr);
|
||||
return rc;
|
||||
@ -739,13 +780,39 @@ static int rtreeEof(sqlite3_vtab_cursor *cur){
|
||||
return (pCsr->pNode==0);
|
||||
}
|
||||
|
||||
/*
|
||||
** The r-tree constraint passed as the second argument to this function is
|
||||
** guaranteed to be a MATCH constraint.
|
||||
*/
|
||||
static int testRtreeGeom(
|
||||
Rtree *pRtree, /* R-Tree object */
|
||||
RtreeConstraint *pConstraint, /* MATCH constraint to test */
|
||||
RtreeCell *pCell, /* Cell to test */
|
||||
int *pbRes /* OUT: Test result */
|
||||
){
|
||||
int i;
|
||||
double aCoord[RTREE_MAX_DIMENSIONS*2];
|
||||
int nCoord = pRtree->nDim*2;
|
||||
|
||||
assert( pConstraint->op==RTREE_MATCH );
|
||||
assert( pConstraint->pGeom );
|
||||
|
||||
for(i=0; i<nCoord; i++){
|
||||
aCoord[i] = DCOORD(pCell->aCoord[i]);
|
||||
}
|
||||
return pConstraint->xGeom(pConstraint->pGeom, nCoord, aCoord, pbRes);
|
||||
}
|
||||
|
||||
/*
|
||||
** Cursor pCursor currently points to a cell in a non-leaf page.
|
||||
** Return true if the sub-tree headed by the cell is filtered
|
||||
** 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.
|
||||
**
|
||||
** Return SQLITE_OK if successful or an SQLite error code if an error
|
||||
** occurs within a geometry callback.
|
||||
*/
|
||||
static int testRtreeCell(Rtree *pRtree, RtreeCursor *pCursor){
|
||||
static int testRtreeCell(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){
|
||||
RtreeCell cell;
|
||||
int ii;
|
||||
int bRes = 0;
|
||||
@ -757,7 +824,7 @@ static int testRtreeCell(Rtree *pRtree, RtreeCursor *pCursor){
|
||||
double cell_max = DCOORD(cell.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_GT || p->op==RTREE_EQ || p->op==RTREE_MATCH
|
||||
);
|
||||
|
||||
switch( p->op ){
|
||||
@ -769,25 +836,43 @@ static int testRtreeCell(Rtree *pRtree, RtreeCursor *pCursor){
|
||||
bRes = p->rValue>cell_max;
|
||||
break;
|
||||
|
||||
default: assert( p->op==RTREE_EQ );
|
||||
case RTREE_EQ:
|
||||
bRes = (p->rValue>cell_max || p->rValue<cell_min);
|
||||
break;
|
||||
|
||||
default: {
|
||||
int rc;
|
||||
assert( p->op==RTREE_MATCH );
|
||||
rc = testRtreeGeom(pRtree, p, &cell, &bRes);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
bRes = !bRes;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bRes;
|
||||
*pbEof = bRes;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the cell that cursor pCursor currently points to
|
||||
** Test if the cell that cursor pCursor currently points to
|
||||
** would be filtered (excluded) by the constraints in the
|
||||
** pCursor->aConstraint[] array, or false otherwise.
|
||||
** 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.
|
||||
**
|
||||
** 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){
|
||||
static int testRtreeEntry(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){
|
||||
RtreeCell cell;
|
||||
int ii;
|
||||
*pbEof = 0;
|
||||
|
||||
nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell);
|
||||
for(ii=0; ii<pCursor->nConstraint; ii++){
|
||||
@ -795,20 +880,32 @@ static int testRtreeEntry(Rtree *pRtree, RtreeCursor *pCursor){
|
||||
double coord = DCOORD(cell.aCoord[p->iCoord]);
|
||||
int res;
|
||||
assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
|
||||
|| p->op==RTREE_GT || p->op==RTREE_EQ
|
||||
|| p->op==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 = (coord<p->rValue); break;
|
||||
case RTREE_GE: res = (coord>=p->rValue); break;
|
||||
case RTREE_GT: res = (coord>p->rValue); break;
|
||||
default: res = (coord==p->rValue); break;
|
||||
case RTREE_EQ: res = (coord==p->rValue); break;
|
||||
default: {
|
||||
int rc;
|
||||
assert( p->op==RTREE_MATCH );
|
||||
rc = testRtreeGeom(pRtree, p, &cell, &res);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !res ) return 1;
|
||||
if( !res ){
|
||||
*pbEof = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -835,13 +932,13 @@ static int descendToCell(
|
||||
assert( iHeight>=0 );
|
||||
|
||||
if( iHeight==0 ){
|
||||
isEof = testRtreeEntry(pRtree, pCursor);
|
||||
rc = testRtreeEntry(pRtree, pCursor, &isEof);
|
||||
}else{
|
||||
isEof = testRtreeCell(pRtree, pCursor);
|
||||
rc = testRtreeCell(pRtree, pCursor, &isEof);
|
||||
}
|
||||
if( isEof || iHeight==0 ){
|
||||
if( rc!=SQLITE_OK || isEof || iHeight==0 ){
|
||||
*pEof = isEof;
|
||||
return SQLITE_OK;
|
||||
return rc;
|
||||
}
|
||||
|
||||
iRowid = nodeGetRowid(pRtree, pCursor->pNode, pCursor->iCell);
|
||||
@ -997,6 +1094,49 @@ static int findLeafNode(Rtree *pRtree, i64 iRowid, RtreeNode **ppLeaf){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called to configure the RtreeConstraint object passed
|
||||
** as the second argument for a MATCH constraint. The value passed as the
|
||||
** first argument to this function is the right-hand operand to the MATCH
|
||||
** operator.
|
||||
*/
|
||||
static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
|
||||
RtreeGeomBlob *p;
|
||||
RtreeGeometry *pGeom;
|
||||
int nBlob;
|
||||
|
||||
/* Check that value is actually a blob. */
|
||||
if( !sqlite3_value_type(pValue)==SQLITE_BLOB ) return SQLITE_MISUSE;
|
||||
|
||||
/* Check that the blob is roughly the right size. */
|
||||
nBlob = sqlite3_value_bytes(pValue);
|
||||
if( nBlob<sizeof(RtreeGeomBlob)
|
||||
|| ((nBlob-sizeof(RtreeGeomBlob))%sizeof(double))!=0
|
||||
){
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
|
||||
pGeom = (RtreeGeometry *)sqlite3_malloc(sizeof(RtreeGeometry) + nBlob);
|
||||
if( !pGeom ) return SQLITE_NOMEM;
|
||||
memset(pGeom, 0, sizeof(RtreeGeometry));
|
||||
p = (RtreeGeomBlob *)&pGeom[1];
|
||||
|
||||
memcpy(p, sqlite3_value_blob(pValue), nBlob);
|
||||
if( p->magic!=RTREE_GEOMETRY_MAGIC
|
||||
|| nBlob!=(sizeof(RtreeGeomBlob) + (p->nParam-1)*sizeof(double))
|
||||
){
|
||||
sqlite3_free(p);
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
|
||||
pGeom->pContext = p->pContext;
|
||||
pGeom->nParam = p->nParam;
|
||||
pGeom->aParam = p->aParam;
|
||||
|
||||
pCons->xGeom = p->xGeom;
|
||||
pCons->pGeom = pGeom;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Rtree virtual table module xFilter method.
|
||||
@ -1015,8 +1155,7 @@ static int rtreeFilter(
|
||||
|
||||
rtreeReference(pRtree);
|
||||
|
||||
sqlite3_free(pCsr->aConstraint);
|
||||
pCsr->aConstraint = 0;
|
||||
freeCursorConstraints(pCsr);
|
||||
pCsr->iStrategy = idxNum;
|
||||
|
||||
if( idxNum==1 ){
|
||||
@ -1039,12 +1178,24 @@ static int rtreeFilter(
|
||||
if( !pCsr->aConstraint ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(pCsr->aConstraint, 0, sizeof(RtreeConstraint)*argc);
|
||||
assert( (idxStr==0 && argc==0) || strlen(idxStr)==argc*2 );
|
||||
for(ii=0; ii<argc; ii++){
|
||||
RtreeConstraint *p = &pCsr->aConstraint[ii];
|
||||
p->op = idxStr[ii*2];
|
||||
p->iCoord = idxStr[ii*2+1]-'a';
|
||||
p->rValue = sqlite3_value_double(argv[ii]);
|
||||
if( p->op==RTREE_MATCH ){
|
||||
/* A MATCH operator. The right-hand-side must be a blob that
|
||||
** can be cast into an RtreeGeomBlob object. One created using
|
||||
** an sqlite3_rtree_geometry_callback() SQL user function.
|
||||
*/
|
||||
rc = deserializeGeometry(argv[ii], p);
|
||||
if( rc!=SQLITE_OK ){
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
p->rValue = sqlite3_value_double(argv[ii]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1104,6 +1255,7 @@ static int rtreeFilter(
|
||||
** < 0x43 ('C')
|
||||
** >= 0x44 ('D')
|
||||
** > 0x45 ('E')
|
||||
** MATCH 0x46 ('F')
|
||||
** ----------------------
|
||||
**
|
||||
** The second of each pair of bytes identifies the coordinate column
|
||||
@ -1142,7 +1294,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
if( p->usable && p->iColumn>0 ){
|
||||
if( p->usable && (p->iColumn>0 || p->op==SQLITE_INDEX_CONSTRAINT_MATCH) ){
|
||||
u8 op = 0;
|
||||
switch( p->op ){
|
||||
case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break;
|
||||
@ -1150,6 +1302,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
case SQLITE_INDEX_CONSTRAINT_LE: op = RTREE_LE; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_GE: op = RTREE_GE; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_MATCH: op = RTREE_MATCH; break;
|
||||
}
|
||||
if( op ){
|
||||
/* Make sure this particular constraint has not been used before.
|
||||
@ -2754,7 +2907,7 @@ static int rtreeInit(
|
||||
Rtree *pRtree;
|
||||
int nDb; /* Length of string argv[1] */
|
||||
int nName; /* Length of string argv[2] */
|
||||
int eCoordType = (int)pAux;
|
||||
int eCoordType = (pAux ? RTREE_COORD_INT32 : RTREE_COORD_REAL32);
|
||||
|
||||
const char *aErrMsg[] = {
|
||||
0, /* 0 */
|
||||
@ -2920,6 +3073,59 @@ int sqlite3RtreeInit(sqlite3 *db){
|
||||
return rc;
|
||||
}
|
||||
|
||||
typedef struct GeomCallbackCtx GeomCallbackCtx;
|
||||
struct GeomCallbackCtx {
|
||||
int (*xGeom)(RtreeGeometry *, int, double *, int *);
|
||||
void *pContext;
|
||||
};
|
||||
|
||||
static void doSqlite3Free(void *p){
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
|
||||
GeomCallbackCtx *pGeomCtx = (GeomCallbackCtx *)sqlite3_user_data(ctx);
|
||||
RtreeGeomBlob *pBlob;
|
||||
int nBlob;
|
||||
|
||||
nBlob = sizeof(RtreeGeomBlob) + (nArg-1)*sizeof(double);
|
||||
pBlob = (RtreeGeomBlob *)sqlite3_malloc(nBlob);
|
||||
if( !pBlob ){
|
||||
sqlite3_result_error_nomem(ctx);
|
||||
}else{
|
||||
int i;
|
||||
pBlob->magic = RTREE_GEOMETRY_MAGIC;
|
||||
pBlob->xGeom = pGeomCtx->xGeom;
|
||||
pBlob->pContext = pGeomCtx->pContext;
|
||||
pBlob->nParam = nArg;
|
||||
for(i=0; i<nArg; i++){
|
||||
pBlob->aParam[i] = sqlite3_value_double(aArg[i]);
|
||||
}
|
||||
sqlite3_result_blob(ctx, pBlob, nBlob, doSqlite3Free);
|
||||
}
|
||||
}
|
||||
|
||||
int sqlite3_rtree_geometry_callback(
|
||||
sqlite3 *db,
|
||||
const char *zGeom,
|
||||
int (*xGeom)(RtreeGeometry *, int nCoord, double *aCoord, int *piResOut),
|
||||
void *pContext
|
||||
){
|
||||
GeomCallbackCtx *pGeomCtx; /* Context object for new user-function */
|
||||
|
||||
/* Allocate and populate the context object. */
|
||||
pGeomCtx = (GeomCallbackCtx *)sqlite3_malloc(sizeof(GeomCallbackCtx));
|
||||
if( !pGeomCtx ) return SQLITE_NOMEM;
|
||||
pGeomCtx->xGeom = xGeom;
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
#if !SQLITE_CORE
|
||||
int sqlite3_extension_init(
|
||||
sqlite3 *db,
|
||||
|
@ -124,12 +124,12 @@ do_execsql_test rtree8-2.2.3 {
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that trying to use the MATCH operator with the r-tree module does
|
||||
# not confuse it.
|
||||
# not confuse it.
|
||||
#
|
||||
populate_t1 10
|
||||
do_catchsql_test rtree8-3.1 {
|
||||
SELECT * FROM t1 WHERE x1 MATCH '1234'
|
||||
} {1 {unable to use function MATCH in the requested context}}
|
||||
} {1 {library routine called out of sequence}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test a couple of invalid arguments to rtreedepth().
|
||||
|
54
ext/rtree/rtree9.test
Normal file
54
ext/rtree/rtree9.test
Normal file
@ -0,0 +1,54 @@
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
|
||||
register_cube_geom db
|
||||
|
||||
do_execsql_test rtree9-1.1 {
|
||||
CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2, y1, y2, z1, z2);
|
||||
INSERT INTO rt VALUES(1, 1, 2, 1, 2, 1, 2);
|
||||
} {}
|
||||
do_execsql_test rtree9-1.2 {
|
||||
SELECT * FROM rt WHERE id MATCH cube(0, 0, 0, 2, 2, 2);
|
||||
} {1 1.0 2.0 1.0 2.0 1.0 2.0}
|
||||
do_execsql_test rtree9-1.3 {
|
||||
SELECT * FROM rt WHERE id MATCH cube(3, 3, 3, 2, 2, 2);
|
||||
} {}
|
||||
do_execsql_test rtree9-1.4 {
|
||||
DELETE FROM rt;
|
||||
} {}
|
||||
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
set x [expr $i%10]
|
||||
set y [expr ($i/10)%10]
|
||||
set z [expr ($i/100)%10]
|
||||
execsql { INSERT INTO rt VALUES($i, $x, $x+1, $y, $y+1, $z, $z+1) }
|
||||
}
|
||||
|
||||
do_execsql_test rtree9-2.1 {
|
||||
SELECT id FROM rt WHERE id MATCH cube(2.5, 2.5, 2.5, 1, 1, 1) ORDER BY id;
|
||||
} {222 223 232 233 322 323 332 333}
|
||||
do_execsql_test rtree9-2.2 {
|
||||
SELECT id FROM rt WHERE id MATCH cube(5.5, 5.5, 5.5, 1, 1, 1) ORDER BY id;
|
||||
} {555 556 565 566 655 656 665 666}
|
||||
|
||||
do_catchsql_test rtree9-3.1 {
|
||||
SELECT id FROM rt WHERE id MATCH cube(5.5, 5.5, 1, 1, 1) ORDER BY id;
|
||||
} {1 {SQL logic error or missing database}}
|
||||
|
||||
|
||||
finish_test
|
1
main.mk
1
main.mk
@ -245,6 +245,7 @@ TESTSRC = \
|
||||
$(TOP)/src/test_onefile.c \
|
||||
$(TOP)/src/test_osinst.c \
|
||||
$(TOP)/src/test_pcache.c \
|
||||
$(TOP)/src/test_rtree.c \
|
||||
$(TOP)/src/test_schema.c \
|
||||
$(TOP)/src/test_server.c \
|
||||
$(TOP)/src/test_stat.c \
|
||||
|
36
manifest
36
manifest
@ -1,10 +1,7 @@
|
||||
-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA1
|
||||
|
||||
C Make\ssqlite3_create_function()\sa\sspecial\scase\sof\ssqlite3_create_function_v2()\nin\sorder\sreduce\sthe\snumber\sof\scode\spaths\sand\ssimplify\stesting.
|
||||
D 2010-08-27T18:44:55
|
||||
C Add\scode\sto\sallow\suser-defined\ssearches\sof\sr-tree\stables.\sStill\slargely\suntested.
|
||||
D 2010-08-28T18:58:01
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 543f91f24cd7fee774ecc0a61c19704c0c3e78fd
|
||||
F Makefile.in c599a15d268b1db2aeadea19df2adc3bf2eb6bee
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
F Makefile.vxworks c85ec1d8597fe2f7bc225af12ac1666e21379151
|
||||
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
|
||||
@ -81,7 +78,7 @@ F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
|
||||
F ext/icu/icu.c 850e9a36567bbcce6bd85a4b68243cad8e3c2de2
|
||||
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
|
||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
||||
F ext/rtree/rtree.c 2e87d4f44329bfdfb1d074d874b7500e9db83a06
|
||||
F ext/rtree/rtree.c d1a00cf3105c9bfd5c83148aaa4d01373a926ea8
|
||||
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
|
||||
F ext/rtree/rtree1.test dbd4250ac0ad367a262eb9676f7e3080b0368206
|
||||
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
|
||||
@ -90,14 +87,15 @@ F ext/rtree/rtree4.test 0061e6f464fd3dc6a79f82454c5a1c3dadbe42af
|
||||
F ext/rtree/rtree5.test ce3d7ccae2cfd9d2e1052b462424964c9bdcda12
|
||||
F ext/rtree/rtree6.test 1ebe0d632a7501cc80ba5a225f028fd4f0fdda08
|
||||
F ext/rtree/rtree7.test bcb647b42920b3b5d025846689147778485cc318
|
||||
F ext/rtree/rtree8.test e4e291e4cdbc576ac0cfc34c6a75c00b2ee347c3
|
||||
F ext/rtree/rtree8.test 67c5a03476bb729853ce01ad3828a290bf65eade
|
||||
F ext/rtree/rtree9.test 16775c219f0e134471c08a9bb0c3902e75ccb4c6
|
||||
F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195
|
||||
F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea
|
||||
F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
|
||||
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F main.mk 26ad86cf0689940f19b3d608bbfdb3956b2fb9a7
|
||||
F main.mk e5a8d2441a4949abfc73bb3fe752c65fb2f8a62c
|
||||
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
|
||||
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
|
||||
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
|
||||
@ -139,7 +137,7 @@ F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
|
||||
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
|
||||
F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e
|
||||
F src/loadext.c 6d422ea91cf3d2d00408c5a8f2391cd458da85f8
|
||||
F src/main.c ce47368bf71e815ac7a484ca2e872ed3ed92f58c
|
||||
F src/main.c a05a66be80bcd7646f9df101ff29688309f0cfda
|
||||
F src/malloc.c f34c9253326fcd2dad0041801992ccf18ddd6ab5
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c 89d4ea8d5cdd55635cbaa48ad53132af6294cbb2
|
||||
@ -180,7 +178,7 @@ F src/sqliteInt.h e3055cc4bd65c6a097b8e9132df807ae683bef9f
|
||||
F src/sqliteLimit.h a17dcd3fb775d63b64a43a55c54cb282f9726f44
|
||||
F src/status.c 496913d4e8441195f6f2a75b1c95993a45b9b30b
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
F src/tclsqlite.c b1565eb727ec7121b59287fed77fc378118bfb69
|
||||
F src/tclsqlite.c 3a6687806193a2610eb7c4c2d282493ff5c8d96c
|
||||
F src/test1.c 2d3ab2cacced2adfee13a6d93b3570ada4072c39
|
||||
F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31
|
||||
F src/test3.c 4c21700c73a890a47fc685c1097bfb661346ac94
|
||||
@ -209,6 +207,7 @@ F src/test_mutex.c ce06b59aca168cd8c520b77159a24352a7469bd3
|
||||
F src/test_onefile.c 40cf9e212a377a6511469384a64b01e6e34b2eec
|
||||
F src/test_osinst.c f408c6a181f2fb04c56273afd5c3e1e82f60392c
|
||||
F src/test_pcache.c 7bf828972ac0d2403f5cfa4cd14da41f8ebe73d8
|
||||
F src/test_rtree.c b15a04974186c95ce7245c47d255745121a3286c
|
||||
F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
|
||||
F src/test_server.c bbba05c144b5fc4b52ff650a4328027b3fa5fcc6
|
||||
F src/test_stat.c f682704b5d1ba8e1d4e7e882a6d7922e2dcf066c
|
||||
@ -851,14 +850,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
|
||||
P 9a724dfbe822c77e76721abe3443c9cb018bb2e2
|
||||
R 9607aa96cd0cdc8bcb2114635902ff6c
|
||||
U drh
|
||||
Z f9a6877eb14f8e1dda2f51167504f9f2
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
Version: GnuPG v1.4.6 (GNU/Linux)
|
||||
|
||||
iD8DBQFMeAeqoxKgR168RlERAqqWAJ9hpff5bHcs4tXzdEHiUEvZACZNEgCfazU6
|
||||
NIzIv3Z2B+i1yILARX0ZbxM=
|
||||
=/ORU
|
||||
-----END PGP SIGNATURE-----
|
||||
P 4758d86d57aaafc058c98c8b485eae24e6547588
|
||||
R 3b6aead175fe973b8e2182acc4c535b6
|
||||
U dan
|
||||
Z 2b81312e66e5ff6d16bea0e9e2280249
|
||||
|
@ -1 +1 @@
|
||||
4758d86d57aaafc058c98c8b485eae24e6547588
|
||||
782ca3b716ee1ecb0dfb5ab6f21dfd73d41758e4
|
@ -1075,13 +1075,17 @@ int sqlite3_create_function_v2(
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( xDestroy ){
|
||||
pArg = (FuncDestructor *)sqlite3DbMallocZero(db, sizeof(FuncDestructor));
|
||||
if( !pArg ) goto out;
|
||||
if( !pArg ){
|
||||
xDestroy(p);
|
||||
goto out;
|
||||
}
|
||||
pArg->xDestroy = xDestroy;
|
||||
pArg->pUserData = p;
|
||||
}
|
||||
rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p, xFunc, xStep, xFinal, pArg);
|
||||
if( pArg && pArg->nRef==0 ){
|
||||
assert( rc!=SQLITE_OK );
|
||||
xDestroy(p);
|
||||
sqlite3DbFree(db, pArg);
|
||||
}
|
||||
|
||||
|
@ -3576,6 +3576,7 @@ static void init_all(Tcl_Interp *interp){
|
||||
extern int Sqlitetestintarray_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestvfs_Init(Tcl_Interp *);
|
||||
extern int SqlitetestStat_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestrtree_Init(Tcl_Interp*);
|
||||
|
||||
Sqliteconfig_Init(interp);
|
||||
Sqlitetest1_Init(interp);
|
||||
@ -3604,6 +3605,7 @@ static void init_all(Tcl_Interp *interp){
|
||||
Sqlitetestintarray_Init(interp);
|
||||
Sqlitetestvfs_Init(interp);
|
||||
SqlitetestStat_Init(interp);
|
||||
Sqlitetestrtree_Init(interp);
|
||||
|
||||
Tcl_CreateObjCommand(interp,"load_testfixture_extensions",init_all_cmd,0,0);
|
||||
|
||||
|
115
src/test_rtree.c
Normal file
115
src/test_rtree.c
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Code for testing all sorts of SQLite interfaces. This code
|
||||
** is not included in the SQLite library.
|
||||
*/
|
||||
#include "sqlite3rtree.h"
|
||||
#include <sqlite3.h>
|
||||
#include <assert.h>
|
||||
#include "tcl.h"
|
||||
|
||||
typedef struct Cube Cube;
|
||||
struct Cube {
|
||||
double x;
|
||||
double y;
|
||||
double z;
|
||||
double width;
|
||||
double height;
|
||||
double depth;
|
||||
};
|
||||
|
||||
static void cube_context_free(void *p){
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
static int gHere = 42;
|
||||
|
||||
/*
|
||||
** Implementation of a simple r-tree geom callback to test for intersection
|
||||
** of r-tree rows with a "cube" shape. Cubes are defined by six scalar
|
||||
** coordinates as follows:
|
||||
**
|
||||
** cube(x, y, z, width, height, depth)
|
||||
**
|
||||
** The width, height and depth parameters must all be greater than zero.
|
||||
*/
|
||||
static int cube_geom(
|
||||
RtreeGeometry *p,
|
||||
int nCoord,
|
||||
double *aCoord,
|
||||
int *piRes
|
||||
){
|
||||
Cube *pCube = (Cube *)p->pUser;
|
||||
|
||||
assert( p->pContext==(void *)&gHere );
|
||||
|
||||
if( pCube==0 ){
|
||||
if( p->nParam!=6 || nCoord!=6
|
||||
|| p->aParam[3]<=0.0 || p->aParam[4]<=0.0 || p->aParam[5]<=0.0
|
||||
){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
pCube = (Cube *)sqlite3_malloc(sizeof(Cube));
|
||||
if( !pCube ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pCube->x = p->aParam[0];
|
||||
pCube->y = p->aParam[1];
|
||||
pCube->z = p->aParam[2];
|
||||
pCube->width = p->aParam[3];
|
||||
pCube->height = p->aParam[4];
|
||||
pCube->depth = p->aParam[5];
|
||||
|
||||
p->pUser = (void *)pCube;
|
||||
p->xDelUser = cube_context_free;
|
||||
}
|
||||
|
||||
assert( nCoord==6 );
|
||||
*piRes = 0;
|
||||
if( aCoord[0]<=(pCube->x+pCube->width)
|
||||
&& aCoord[1]>=pCube->x
|
||||
&& aCoord[2]<=(pCube->y+pCube->height)
|
||||
&& aCoord[3]>=pCube->y
|
||||
&& aCoord[4]<=(pCube->z+pCube->depth)
|
||||
&& aCoord[5]>=pCube->z
|
||||
){
|
||||
*piRes = 1;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int register_cube_geom(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
#ifdef SQLITE_ENABLE_RTREE
|
||||
extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
|
||||
sqlite3 *db;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
sqlite3_rtree_geometry_callback(db, "cube", cube_geom, (void *)&gHere);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
int Sqlitetestrtree_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(interp, "register_cube_geom", register_cube_geom, 0, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user