]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add code to allow user-defined searches of r-tree tables. Still largely untested.
authordan <dan@noemail.net>
Sat, 28 Aug 2010 18:58:00 +0000 (18:58 +0000)
committerdan <dan@noemail.net>
Sat, 28 Aug 2010 18:58:00 +0000 (18:58 +0000)
FossilOrigin-Name: 782ca3b716ee1ecb0dfb5ab6f21dfd73d41758e4

Makefile.in
ext/rtree/rtree.c
ext/rtree/rtree8.test
ext/rtree/rtree9.test [new file with mode: 0644]
main.mk
manifest
manifest.uuid
src/main.c
src/tclsqlite.c
src/test_rtree.c [new file with mode: 0644]

index 5d2c1d95b8b0ff67563184fe9d32d0d87be61e1d..4f8f09ee6f9c3448855fa36dcb40d010e23160c9 100644 (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.
index 1e402b8f930f05c58ba2b2f2f76effb76782f2fe..a026e67085be4eb5b75086e3142e6834e7a765b7 100644 (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,
index 9181b9d5f712720199596ec5b114a1af7b7371ab..db7bda434b8a49baf0c00a52f6cca37a71fd2b9c 100644 (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().
diff --git a/ext/rtree/rtree9.test b/ext/rtree/rtree9.test
new file mode 100644 (file)
index 0000000..2b96958
--- /dev/null
@@ -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 83228c30e8f7aab6b80a0ecdaf1559b159e52683..06a00a2d8a66117cc935dc04b582181fcc586c51 100644 (file)
--- 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 \
index ff6fdebf92929fe1d41fd757ffb6ef6a0d6ccd48..c30050d13a911c3e84dcaa227b22d257153752ab 100644 (file)
--- 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
index a9189be575c68afe61934adef9a099cb89ca899c..90dab2ae4a84b6f9fc023759165902b6edbd5cd8 100644 (file)
@@ -1 +1 @@
-4758d86d57aaafc058c98c8b485eae24e6547588
\ No newline at end of file
+782ca3b716ee1ecb0dfb5ab6f21dfd73d41758e4
\ No newline at end of file
index 9d749ea84eea484a7fa0a75c8fa4fe56ca7d4517..5dcc4dd6e8570deae5423310c4e10d4a447d496c 100644 (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);
   }
 
index 141e68e55f19f982a29146f346bc002b11715c2a..164bc03b1f5b627bd20a0897fe907b149a130eac 100644 (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);
 
diff --git a/src/test_rtree.c b/src/test_rtree.c
new file mode 100644 (file)
index 0000000..6cca357
--- /dev/null
@@ -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;
+}
+