** This file contains code for implementations of the r-tree and r*-tree
** algorithms packaged as an SQLite virtual table module.
**
-** $Id: rtree.c,v 1.5 2008/06/23 15:55:52 danielk1977 Exp $
+** $Id: rtree.c,v 1.6 2008/07/14 15:37:01 danielk1977 Exp $
*/
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RTREE)
typedef struct RtreeNode RtreeNode;
typedef struct RtreeCell RtreeCell;
typedef struct RtreeConstraint RtreeConstraint;
+typedef union RtreeCoord RtreeCoord;
/* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */
#define RTREE_MAX_DIMENSIONS 5
sqlite3_stmt *pReadParent;
sqlite3_stmt *pWriteParent;
sqlite3_stmt *pDeleteParent;
+
+ int eCoordType;
};
+/* Possible values for eCoordType: */
+#define RTREE_COORD_REAL32 0
+#define RTREE_COORD_INT32 1
+
/*
** The minimum number of cells allowed for a node is a third of the
** maximum. In Gutman's notation:
RtreeConstraint *aConstraint; /* Search constraints. */
};
+union RtreeCoord {
+ float f;
+ int i;
+};
+
+/*
+** The argument is an RtreeCoord. Return the value stored within the RtreeCoord
+** formatted as a double. This macro assumes that local variable pRtree points
+** to the Rtree structure associated with the RtreeCoord.
+*/
+#define DCOORD(coord) ( \
+ (pRtree->eCoordType==RTREE_COORD_REAL32) ? \
+ ((double)coord.f) : \
+ ((double)coord.i) \
+)
+
/*
** A search constraint.
*/
struct RtreeConstraint {
int iCoord; /* Index of constrained coordinate */
int op; /* Constraining operation */
- float rValue; /* Constraint value. */
+ double rValue; /* Constraint value. */
};
/* Possible values for RtreeConstraint.op */
*/
struct RtreeCell {
i64 iRowid;
- float aCoord[RTREE_MAX_DIMENSIONS*2];
+ RtreeCoord aCoord[RTREE_MAX_DIMENSIONS*2];
};
#define MAX(x,y) ((x) < (y) ? (y) : (x))
static int readInt16(u8 *p){
return (p[0]<<8) + p[1];
}
-static float readReal32(u8 *p){
+static void readCoord(u8 *p, RtreeCoord *pCoord){
u32 i = (
(((u32)p[0]) << 24) +
(((u32)p[1]) << 16) +
(((u32)p[2]) << 8) +
(((u32)p[3]) << 0)
);
- return *(float *)&i;
+ *(u32 *)pCoord = i;
}
static i64 readInt64(u8 *p){
return (
p[1] = (i>> 0)&0xFF;
return 2;
}
-static int writeReal32(u8 *p, float f){
+static int writeCoord(u8 *p, RtreeCoord *pCoord){
u32 i;
- assert( sizeof(float)==4 );
+ assert( sizeof(RtreeCoord)==4 );
assert( sizeof(u32)==4 );
- i = *(u32 *)&f;
+ i = *(u32 *)pCoord;
p[0] = (i>>24)&0xFF;
p[1] = (i>>16)&0xFF;
p[2] = (i>> 8)&0xFF;
u8 *p = &pNode->zData[4 + pRtree->nBytesPerCell*iCell];
p += writeInt64(p, pCell->iRowid);
for(ii=0; ii<(pRtree->nDim*2); ii++){
- p += writeReal32(p, pCell->aCoord[ii]);
+ p += writeCoord(p, &pCell->aCoord[ii]);
}
pNode->isDirty = 1;
}
/*
** Return coordinate iCoord from cell iCell in node pNode.
*/
-static float nodeGetCoord(
+static void nodeGetCoord(
Rtree *pRtree,
RtreeNode *pNode,
int iCell,
- int iCoord
+ int iCoord,
+ RtreeCoord *pCoord /* Space to write result to */
){
- return readReal32(&pNode->zData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord]);
+ readCoord(&pNode->zData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord);
}
/*
int ii;
pCell->iRowid = nodeGetRowid(pRtree, pNode, iCell);
for(ii=0; ii<pRtree->nDim*2; ii++){
- pCell->aCoord[ii] = nodeGetCoord(pRtree, pNode, iCell, ii);
+ nodeGetCoord(pRtree, pNode, iCell, ii, &pCell->aCoord[ii]);
}
}
-/* Forward declaration for the function that does the work of
+/* Forward declaration for the function that does the work of
** the virtual table module xCreate() and xConnect() methods.
*/
static int rtreeInit(
- sqlite3 *, void *, int, const char *const*, sqlite3_vtab **, char **, int
+ sqlite3 *, void *, int, const char *const*, sqlite3_vtab **, char **, int, int
);
/*
sqlite3_vtab **ppVtab,
char **pzErr
){
- return rtreeInit(db, pAux, argc, argv, ppVtab, pzErr, 1);
+ return rtreeInit(db, pAux, argc, argv, ppVtab, pzErr, 1, (int)pAux);
}
/*
sqlite3_vtab **ppVtab,
char **pzErr
){
- return rtreeInit(db, pAux, argc, argv, ppVtab, pzErr, 0);
+ return rtreeInit(db, pAux, argc, argv, ppVtab, pzErr, 0, (int)pAux);
}
/*
static int testRtreeCell(Rtree *pRtree, RtreeCursor *pCursor){
RtreeCell cell;
int ii;
+ int bRes = 0;
nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell);
- for(ii=0; ii<pCursor->nConstraint; ii++){
+ for(ii=0; bRes==0 && ii<pCursor->nConstraint; ii++){
RtreeConstraint *p = &pCursor->aConstraint[ii];
+ double cell_min = DCOORD(cell.aCoord[(p->iCoord>>1)*2]);
+ double cell_max = DCOORD(cell.aCoord[(p->iCoord>>1)*2+1]);
- float cell_min = cell.aCoord[(p->iCoord>>1)*2];
- float cell_max = cell.aCoord[(p->iCoord>>1)*2+1];
- assert( cell_min<=cell_max );
+ assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
+ || p->op==RTREE_GT || p->op==RTREE_EQ
+ );
switch( p->op ){
- case RTREE_LE: case RTREE_LT: {
- if( p->rValue<cell_min ){
- return 1;
- }
+ case RTREE_LE: case RTREE_LT: bRes = p->rValue<cell_min; break;
+ case RTREE_GE: case RTREE_GT: bRes = p->rValue>cell_max; break;
+ case RTREE_EQ:
+ bRes = (p->rValue>cell_max || p->rValue<cell_min);
break;
- }
-
- case RTREE_GE: case RTREE_GT: {
- if( p->rValue>cell_max ){
- return 1;
- }
- break;
- }
-
- case RTREE_EQ: {
- if( p->rValue>cell_max || p->rValue<cell_min ){
- return 1;
- }
- break;
- }
-#ifndef NDEBUG
- default: assert(!"Internal error");
-#endif
}
}
- return 0;
+ return bRes;
}
/*
nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell);
for(ii=0; ii<pCursor->nConstraint; ii++){
RtreeConstraint *p = &pCursor->aConstraint[ii];
- float cell_val = cell.aCoord[p->iCoord];
+ 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
+ );
switch( p->op ){
- case RTREE_LE: res = (cell_val<=p->rValue); break;
- case RTREE_LT: res = (cell_val<p->rValue); break;
- case RTREE_GE: res = (cell_val>=p->rValue); break;
- case RTREE_GT: res = (cell_val>p->rValue); break;
- case RTREE_EQ: res = (cell_val==p->rValue); break;
-#ifndef NDEBUG
- default: assert(!"Internal error");
-#endif
+ 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;
+ case RTREE_EQ: res = (coord==p->rValue); break;
}
+
if( !res ) return 1;
}
i64 iRowid = nodeGetRowid(pRtree, pCsr->pNode, pCsr->iCell);
sqlite3_result_int64(ctx, iRowid);
}else{
- float fCoord = nodeGetCoord(pRtree, pCsr->pNode, pCsr->iCell, i-1);
- sqlite3_result_double(ctx, fCoord);
+ RtreeCoord c;
+ nodeGetCoord(pRtree, pCsr->pNode, pCsr->iCell, i-1, &c);
+ if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
+ sqlite3_result_double(ctx, c.f);
+ }else{
+ assert( pRtree->eCoordType==RTREE_COORD_INT32 );
+ sqlite3_result_int(ctx, c.i);
+ }
}
return SQLITE_OK;
float area = 1.0;
int ii;
for(ii=0; ii<(pRtree->nDim*2); ii+=2){
- area = area * (p->aCoord[ii+1] - p->aCoord[ii]);
+ area = area * (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii]));
}
return area;
}
float margin = 0.0;
int ii;
for(ii=0; ii<(pRtree->nDim*2); ii+=2){
- margin += (p->aCoord[ii+1] - p->aCoord[ii]);
+ margin += (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii]));
}
return margin;
}
*/
static void cellUnion(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){
int ii;
- for(ii=0; ii<(pRtree->nDim*2); ii+=2){
- p1->aCoord[ii] = MIN(p1->aCoord[ii], p2->aCoord[ii]);
- p1->aCoord[ii+1] = MAX(p1->aCoord[ii+1], p2->aCoord[ii+1]);
+ if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
+ for(ii=0; ii<(pRtree->nDim*2); ii+=2){
+ p1->aCoord[ii].f = MIN(p1->aCoord[ii].f, p2->aCoord[ii].f);
+ p1->aCoord[ii+1].f = MAX(p1->aCoord[ii+1].f, p2->aCoord[ii+1].f);
+ }
+ }else{
+ for(ii=0; ii<(pRtree->nDim*2); ii+=2){
+ p1->aCoord[ii].i = MIN(p1->aCoord[ii].i, p2->aCoord[ii].i);
+ p1->aCoord[ii+1].i = MAX(p1->aCoord[ii+1].i, p2->aCoord[ii+1].i);
+ }
}
}
int jj;
float o = 1.0;
for(jj=0; jj<(pRtree->nDim*2); jj+=2){
+ double x1;
+ double x2;
- float x1 = MAX(p->aCoord[jj], aCell[ii].aCoord[jj]);
- float x2 = MIN(p->aCoord[jj+1], aCell[ii].aCoord[jj+1]);
+ x1 = MAX(DCOORD(p->aCoord[jj]), DCOORD(aCell[ii].aCoord[jj]));
+ x2 = MIN(DCOORD(p->aCoord[jj+1]), DCOORD(aCell[ii].aCoord[jj+1]));
if( x2<x1 ){
o = 0.0;
** sorting algorithm.
*/
static void SortByDimension(
+ Rtree *pRtree,
int *aIdx,
int nIdx,
int iDim,
int *aLeft = aIdx;
int *aRight = &aIdx[nLeft];
- SortByDimension(aLeft, nLeft, iDim, aCell, aSpare);
- SortByDimension(aRight, nRight, iDim, aCell, aSpare);
+ SortByDimension(pRtree, aLeft, nLeft, iDim, aCell, aSpare);
+ SortByDimension(pRtree, aRight, nRight, iDim, aCell, aSpare);
memcpy(aSpare, aLeft, sizeof(int)*nLeft);
aLeft = aSpare;
while( iLeft<nLeft || iRight<nRight ){
- float xleft1 = aCell[aLeft[iLeft]].aCoord[iDim*2];
- float xleft2 = aCell[aLeft[iLeft]].aCoord[iDim*2+1];
- float xright1 = aCell[aRight[iRight]].aCoord[iDim*2];
- float xright2 = aCell[aRight[iRight]].aCoord[iDim*2+1];
-
+ double xleft1 = DCOORD(aCell[aLeft[iLeft]].aCoord[iDim*2]);
+ double xleft2 = DCOORD(aCell[aLeft[iLeft]].aCoord[iDim*2+1]);
+ double xright1 = DCOORD(aCell[aRight[iRight]].aCoord[iDim*2]);
+ double xright2 = DCOORD(aCell[aRight[iRight]].aCoord[iDim*2+1]);
if( (iLeft!=nLeft) && ((iRight==nRight)
|| (xleft1<xright1)
|| (xleft1==xright1 && xleft2<xright2)
for(jj=0; jj<nCell; jj++){
aaSorted[ii][jj] = jj;
}
- SortByDimension(aaSorted[ii], nCell, ii, aCell, aSpare);
+ SortByDimension(pRtree, aaSorted[ii], nCell, ii, aCell, aSpare);
}
for(ii=0; ii<pRtree->nDim; ii++){
}
aOrder[ii] = ii;
for(iDim=0; iDim<pRtree->nDim; iDim++){
- aCenterCoord[iDim] += aCell[ii].aCoord[iDim*2];
- aCenterCoord[iDim] += aCell[ii].aCoord[iDim*2+1];
+ aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2]);
+ aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2+1]);
}
}
for(iDim=0; iDim<pRtree->nDim; iDim++){
for(ii=0; ii<nCell; ii++){
aDistance[ii] = 0.0;
for(iDim=0; iDim<pRtree->nDim; iDim++){
- float coord = aCell[ii].aCoord[iDim*2+1] - aCell[ii].aCoord[iDim*2];
+ float coord = DCOORD(aCell[ii].aCoord[iDim*2+1]) -
+ DCOORD(aCell[ii].aCoord[iDim*2]);
aDistance[ii] += (coord-aCenterCoord[iDim])*(coord-aCenterCoord[iDim]);
}
}
/* Populate the cell.aCoord[] array. The first coordinate is azData[3]. */
assert( nData==(pRtree->nDim*2 + 3) );
- for(ii=0; ii<(pRtree->nDim*2); ii+=2){
- cell.aCoord[ii] = (float)sqlite3_value_double(azData[ii+3]);
- cell.aCoord[ii+1] = (float)sqlite3_value_double(azData[ii+4]);
- if( cell.aCoord[ii]>cell.aCoord[ii+1] ){
- rc = SQLITE_CONSTRAINT;
- goto constraint;
+ if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
+ for(ii=0; ii<(pRtree->nDim*2); ii+=2){
+ cell.aCoord[ii].f = (float)sqlite3_value_double(azData[ii+3]);
+ cell.aCoord[ii+1].f = (float)sqlite3_value_double(azData[ii+4]);
+ if( cell.aCoord[ii].f>cell.aCoord[ii+1].f ){
+ rc = SQLITE_CONSTRAINT;
+ goto constraint;
+ }
+ }
+ }else{
+ for(ii=0; ii<(pRtree->nDim*2); ii+=2){
+ cell.aCoord[ii].i = sqlite3_value_int(azData[ii+3]);
+ cell.aCoord[ii+1].i = sqlite3_value_int(azData[ii+4]);
+ if( cell.aCoord[ii].i>cell.aCoord[ii+1].i ){
+ rc = SQLITE_CONSTRAINT;
+ goto constraint;
+ }
}
}
int argc, const char *const*argv, /* Parameters to CREATE TABLE statement */
sqlite3_vtab **ppVtab, /* OUT: New virtual table */
char **pzErr, /* OUT: Error message, if any */
- int isCreate /* True for xCreate, false for xConnect */
+ int isCreate, /* True for xCreate, false for xConnect */
+ int eCoordType /* One of the RTREE_COORD_* constants */
){
int rc = SQLITE_OK;
int iPageSize = 0;
pRtree->zName = &pRtree->zDb[nDb+1];
pRtree->nDim = (argc-4)/2;
pRtree->nBytesPerCell = 8 + pRtree->nDim*4*2;
+ pRtree->eCoordType = eCoordType;
memcpy(pRtree->zDb, argv[1], nDb);
memcpy(pRtree->zName, argv[2], nName);
sqlite3_snprintf(512-nCell,&zCell[nCell],"%d", cell.iRowid);
nCell = strlen(zCell);
for(jj=0; jj<tree.nDim*2; jj++){
- sqlite3_snprintf(512-nCell,&zCell[nCell]," %f",(double)cell.aCoord[jj]);
+ sqlite3_snprintf(512-nCell,&zCell[nCell]," %f",(double)cell.aCoord[jj].f);
nCell = strlen(zCell);
}
rc = sqlite3_create_function(db, "rtreedepth", 1, utf8, 0,rtreedepth, 0, 0);
}
if( rc==SQLITE_OK ){
- rc = sqlite3_create_module_v2(db, "rtree", &rtreeModule, 0, 0);
+ void *c = (void *)RTREE_COORD_REAL32;
+ rc = sqlite3_create_module_v2(db, "rtree", &rtreeModule, c, 0);
+ }
+ if( rc==SQLITE_OK ){
+ void *c = (void *)RTREE_COORD_INT32;
+ rc = sqlite3_create_module_v2(db, "rtree_i32", &rtreeModule, c, 0);
}
return rc;
#
# The focus of this file is testing the r-tree extension.
#
-# $Id: rtree2.test,v 1.3 2008/06/23 15:55:52 danielk1977 Exp $
+# $Id: rtree2.test,v 1.4 2008/07/14 15:37:01 danielk1977 Exp $
#
if {![info exists testdir]} {
set ::NSELECT 10
}
-for {set nDim 1} {$nDim <= 5} {incr nDim} {
-
- do_test rtree2-$nDim.1 {
- set cols [list]
- foreach c [list c0 c1 c2 c3 c4 c5 c6 c7 c8 c9] {
- lappend cols "$c REAL"
- }
- set cols [join [lrange $cols 0 [expr {$nDim*2-1}]] ", "]
- execsql "
- CREATE VIRTUAL TABLE t1 USING rtree(ii, $cols);
- CREATE TABLE t2 (ii, $cols);
- "
- } {}
-
- do_test rtree2-$nDim.2 {
- db transaction {
- for {set ii 0} {$ii < $::NROW} {incr ii} {
- #puts "Row $ii"
- set values [list]
- for {set jj 0} {$jj<$nDim*2} {incr jj} {
- lappend values [expr int(rand()*1000)]
- }
- set values [join $values ,]
- #puts [rtree_treedump db t1]
- #puts "INSERT INTO t2 VALUES($ii, $values)"
- set rc [catch {db eval "INSERT INTO t1 VALUES($ii, $values)"}]
- if {$rc} {
- incr ii -1
- } else {
- db eval "INSERT INTO t2 VALUES($ii, $values)"
- }
- #if {[rtree_check db t1]} {
- #puts [rtree_treedump db t1]
- #exit
- #}
+foreach module {rtree_i32 rtree} {
+ for {set nDim 1} {$nDim <= 5} {incr nDim} {
+
+ do_test rtree2-$module.$nDim.1 {
+ set cols [list]
+ foreach c [list c0 c1 c2 c3 c4 c5 c6 c7 c8 c9] {
+ lappend cols "$c REAL"
}
- }
-
- set t1 [execsql {SELECT * FROM t1 ORDER BY ii}]
- set t2 [execsql {SELECT * FROM t2 ORDER BY ii}]
- set rc [expr {$t1 eq $t2}]
- if {$rc != 1} {
- puts $t1
- puts $t2
- }
- set rc
- } {1}
-
- do_test rtree2-$nDim.3 {
- rtree_check db t1
- } 0
-
- set OPS [list < > <= >= =]
- for {set ii 0} {$ii < $::NSELECT} {incr ii} {
- do_test rtree2-$nDim.4.$ii.1 {
- set where [list]
- foreach look_three_dots! {. . .} {
- set colidx [expr int(rand()*($nDim*2+1))-1]
- if {$colidx<0} {
- set col ii
- } else {
- set col "c$colidx"
+ set cols [join [lrange $cols 0 [expr {$nDim*2-1}]] ", "]
+ execsql "
+ CREATE VIRTUAL TABLE t1 USING ${module}(ii, $cols);
+ CREATE TABLE t2 (ii, $cols);
+ "
+ } {}
+
+ do_test rtree2-$module.$nDim.2 {
+ db transaction {
+ for {set ii 0} {$ii < $::NROW} {incr ii} {
+ #puts "Row $ii"
+ set values [list]
+ for {set jj 0} {$jj<$nDim*2} {incr jj} {
+ lappend values [expr int(rand()*1000)]
+ }
+ set values [join $values ,]
+ #puts [rtree_treedump db t1]
+ #puts "INSERT INTO t2 VALUES($ii, $values)"
+ set rc [catch {db eval "INSERT INTO t1 VALUES($ii, $values)"}]
+ if {$rc} {
+ incr ii -1
+ } else {
+ db eval "INSERT INTO t2 VALUES($ii, $values)"
+ }
+ #if {[rtree_check db t1]} {
+ #puts [rtree_treedump db t1]
+ #exit
+ #}
}
- set op [lindex $OPS [expr int(rand()*[llength $OPS])]]
- set val [expr int(rand()*1000)]
- lappend where "$col $op $val"
- }
- set where [join $where " AND "]
-
- set t1 [execsql "SELECT * FROM t1 WHERE $where ORDER BY ii"]
- set t2 [execsql "SELECT * FROM t2 WHERE $where ORDER BY ii"]
- set rc [expr {$t1 eq $t2}]
- if {$rc != 1} {
- #puts $where
- puts $t1
- puts $t2
- #puts [rtree_treedump db t1]
- #breakpoint
- #set t1 [execsql "SELECT * FROM t1 WHERE $where ORDER BY ii"]
- #exit
}
- set rc
- } {1}
- }
-
- for {set ii 0} {$ii < $::NROW} {incr ii $::NDEL} {
- #puts [rtree_treedump db t1]
- do_test rtree2-$nDim.5.$ii.1 {
- execsql "DELETE FROM t2 WHERE ii <= $::ii"
- execsql "DELETE FROM t1 WHERE ii <= $::ii"
-
+
set t1 [execsql {SELECT * FROM t1 ORDER BY ii}]
set t2 [execsql {SELECT * FROM t2 ORDER BY ii}]
set rc [expr {$t1 eq $t2}]
}
set rc
} {1}
- do_test rtree2-$nDim.5.$ii.2 {
+
+ do_test rtree2-$module.$nDim.3 {
rtree_check db t1
- } {0}
- }
-
- do_test rtree2-$nDim.6 {
- execsql {
- DROP TABLE t1;
- DROP TABLE t2;
+ } 0
+
+ set OPS [list < > <= >= =]
+ for {set ii 0} {$ii < $::NSELECT} {incr ii} {
+ do_test rtree2-$module.$nDim.4.$ii.1 {
+ set where [list]
+ foreach look_three_dots! {. . .} {
+ set colidx [expr int(rand()*($nDim*2+1))-1]
+ if {$colidx<0} {
+ set col ii
+ } else {
+ set col "c$colidx"
+ }
+ set op [lindex $OPS [expr int(rand()*[llength $OPS])]]
+ set val [expr int(rand()*1000)]
+ lappend where "$col $op $val"
+ }
+ set where [join $where " AND "]
+
+ set t1 [execsql "SELECT * FROM t1 WHERE $where ORDER BY ii"]
+ set t2 [execsql "SELECT * FROM t2 WHERE $where ORDER BY ii"]
+ set rc [expr {$t1 eq $t2}]
+ if {$rc != 1} {
+ #puts $where
+ puts $t1
+ puts $t2
+ #puts [rtree_treedump db t1]
+ #breakpoint
+ #set t1 [execsql "SELECT * FROM t1 WHERE $where ORDER BY ii"]
+ #exit
+ }
+ set rc
+ } {1}
}
- } {}
+
+ for {set ii 0} {$ii < $::NROW} {incr ii $::NDEL} {
+ #puts [rtree_treedump db t1]
+ do_test rtree2-$module.$nDim.5.$ii.1 {
+ execsql "DELETE FROM t2 WHERE ii <= $::ii"
+ execsql "DELETE FROM t1 WHERE ii <= $::ii"
+
+ set t1 [execsql {SELECT * FROM t1 ORDER BY ii}]
+ set t2 [execsql {SELECT * FROM t2 ORDER BY ii}]
+ set rc [expr {$t1 eq $t2}]
+ if {$rc != 1} {
+ puts $t1
+ puts $t2
+ }
+ set rc
+ } {1}
+ do_test rtree2-$module.$nDim.5.$ii.2 {
+ rtree_check db t1
+ } {0}
+ }
+
+ do_test rtree2-$module.$nDim.6 {
+ execsql {
+ DROP TABLE t1;
+ DROP TABLE t2;
+ }
+ } {}
+ }
}
finish_test