From: dan Date: Sat, 28 Aug 2010 18:58:00 +0000 (+0000) Subject: Add code to allow user-defined searches of r-tree tables. Still largely untested. X-Git-Tag: experimental~105 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9508daa9299d264cd905c19e735c0ff8e4dbd1f4;p=thirdparty%2Fsqlite.git Add code to allow user-defined searches of r-tree tables. Still largely untested. FossilOrigin-Name: 782ca3b716ee1ecb0dfb5ab6f21dfd73d41758e4 --- diff --git a/Makefile.in b/Makefile.in index 5d2c1d95b8..4f8f09ee6f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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. diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 1e402b8f93..a026e67085 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -64,6 +64,8 @@ #include "sqlite3.h" #endif +#include "sqlite3rtree.h" + #include #include @@ -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; inConstraint; 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; iaCoord[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->rValueop==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; iinConstraint; 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 = (coordrValue); 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( nBlobmagic!=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; iiaConstraint[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; iaParam[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, diff --git a/ext/rtree/rtree8.test b/ext/rtree/rtree8.test index 9181b9d5f7..db7bda434b 100644 --- a/ext/rtree/rtree8.test +++ b/ext/rtree/rtree8.test @@ -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(). diff --git a/ext/rtree/rtree9.test b/ext/rtree/rtree9.test new file mode 100644 index 0000000000..2b96958b6f --- /dev/null +++ b/ext/rtree/rtree9.test @@ -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 diff --git a/main.mk b/main.mk index 83228c30e8..06a00a2d8a 100644 --- a/main.mk +++ b/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 \ diff --git a/manifest b/manifest index ff6fdebf92..c30050d13a 100644 --- a/manifest +++ b/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 diff --git a/manifest.uuid b/manifest.uuid index a9189be575..90dab2ae4a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4758d86d57aaafc058c98c8b485eae24e6547588 \ No newline at end of file +782ca3b716ee1ecb0dfb5ab6f21dfd73d41758e4 \ No newline at end of file diff --git a/src/main.c b/src/main.c index 9d749ea84e..5dcc4dd6e8 100644 --- a/src/main.c +++ b/src/main.c @@ -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); } diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 141e68e55f..164bc03b1f 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -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); diff --git a/src/test_rtree.c b/src/test_rtree.c new file mode 100644 index 0000000000..6cca357e19 --- /dev/null +++ b/src/test_rtree.c @@ -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 +#include +#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; +} +