# 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
$(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 \
$(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.
#include "sqlite3.h"
#endif
+#include "sqlite3rtree.h"
+
#include <string.h>
#include <assert.h>
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
** 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.
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
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.
*/
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;
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;
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 ){
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++){
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;
}
/*
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);
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.
rtreeReference(pRtree);
- sqlite3_free(pCsr->aConstraint);
- pCsr->aConstraint = 0;
+ freeCursorConstraints(pCsr);
pCsr->iStrategy = idxNum;
if( idxNum==1 ){
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]);
+ }
}
}
}
** < 0x43 ('C')
** >= 0x44 ('D')
** > 0x45 ('E')
+** MATCH 0x46 ('F')
** ----------------------
**
** The second of each pair of bytes identifies the coordinate column
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;
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.
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 */
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,
#-------------------------------------------------------------------------
# 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().
--- /dev/null
+# 2010 August 28
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+
+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
$(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 \
------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
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
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
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
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
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
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
-4758d86d57aaafc058c98c8b485eae24e6547588
\ No newline at end of file
+782ca3b716ee1ecb0dfb5ab6f21dfd73d41758e4
\ No newline at end of file
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);
}
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);
Sqlitetestintarray_Init(interp);
Sqlitetestvfs_Init(interp);
SqlitetestStat_Init(interp);
+ Sqlitetestrtree_Init(interp);
Tcl_CreateObjCommand(interp,"load_testfixture_extensions",init_all_cmd,0,0);
--- /dev/null
+/*
+** 2010 August 28
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** 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;
+}
+