Add code to allow user-defined searches of r-tree tables. Still largely untested.

FossilOrigin-Name: 782ca3b716ee1ecb0dfb5ab6f21dfd73d41758e4
This commit is contained in:
dan 2010-08-28 18:58:00 +00:00
parent 7b19facf56
commit 9508daa929
10 changed files with 434 additions and 57 deletions

View File

@ -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.

View File

@ -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,

View File

@ -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
View 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

View File

@ -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 \

View File

@ -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

View File

@ -1 +1 @@
4758d86d57aaafc058c98c8b485eae24e6547588
782ca3b716ee1ecb0dfb5ab6f21dfd73d41758e4

View File

@ -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);
}

View File

@ -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
View 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;
}