Import 'rtree' extension. (CVS 5159)

FossilOrigin-Name: b104dcd6adadbd3fe15a348fe9d4d290119e139e
This commit is contained in:
danielk1977 2008-05-26 18:41:54 +00:00
parent 02a50b709c
commit ebaecc148f
14 changed files with 3995 additions and 13 deletions

123
ext/rtree/README Normal file
View File

@ -0,0 +1,123 @@
This directory contains an SQLite extension that implements a virtual
table type that allows users to create, query and manipulate r-tree[1]
data structures inside of SQLite databases. Users create, populate
and query r-tree structures using ordinary SQL statements.
1. SQL Interface
1.1 Table Creation
1.2 Data Manipulation
1.3 Data Querying
1.4 Introspection and Analysis
2. Compilation and Deployment
3. References
1. SQL INTERFACE
1.1 Table Creation.
All r-tree virtual tables have an odd number of columns between
3 and 11. Unlike regular SQLite tables, r-tree tables are strongly
typed.
The leftmost column is always the pimary key and contains 64-bit
integer values. Each subsequent column contains a 32-bit real
value. For each pair of real values, the first (leftmost) must be
less than or greater than the second. R-tree tables may be
constructed using the following syntax:
CREATE VIRTUAL TABLE <name> USING rtree(<column-names>)
For example:
CREATE VIRTUAL TABLE boxes USING rtree(boxno, xmin, xmax, ymin, ymax);
CREATE VIRTUAL TABLE boxes USING rtree(1, 1.0, 3.0, 2.0, 4.0);
Constructing a virtual r-tree table <name> creates the following three
real tables in the database to store the data structure:
<name>_node
<name>_rowid
<name>_parent
Dropping or modifying the contents of these tables directly will
corrupt the r-tree structure. To delete an r-tree from a database,
use a regular DROP TABLE statement:
DROP TABLE <name>;
Dropping the main r-tree table automatically drops the automatically
created tables.
1.2 Data Manipulation (INSERT, UPDATE, DELETE).
The usual INSERT, UPDATE or DELETE syntax is used to manipulate data
stored in an r-tree table. Please note the following:
* Inserting a NULL value into the primary key column has the
same effect as inserting a NULL into an INTEGER PRIMARY KEY
column of a regular table. The system automatically assigns
an unused integer key value to the new record. Usually, this
is one greater than the largest primary key value currently
present in the table.
* Attempting to insert a duplicate primary key value fails with
an SQLITE_CONSTRAINT error.
* Attempting to insert or modify a record such that the value
stored in the (N*2)th column is greater than that stored in
the (N*2+1)th column fails with an SQLITE_CONSTRAINT error.
* When a record is inserted, values are always converted to
the required type (64-bit integer or 32-bit real) as if they
were part of an SQL CAST expression. Non-numeric strings are
converted to zero.
1.3 Queries.
R-tree tables may be queried using all of the same SQL syntax supported
by regular tables. However, some query patterns are more efficient faster
than others.
R-trees support fast lookup by primary key value (O(logN), like
regular tables).
Any combination of equality and range (<, <=, >, >=) constraints
on spatial data columns may be used to optimize other queries. This
is the key advantage to using r-tree tables instead of creating
indices on regular tables.
1.4 Introspection and Analysis.
TODO: Describe rtreenode() and rtreedepth() functions.
2. COMPILATION AND USAGE
The easiest way to compile and use the ICU extension is to build
and use it as a dynamically loadable SQLite extension. To do this
using gcc on *nix:
gcc -shared rtree.c -o libSqliteRtree.so
You may need to add "-I" flags so that gcc can find sqlite3ext.h
and sqlite3.h. The resulting shared lib, libSqliteIcu.so, may be
loaded into sqlite in the same way as any other dynamicly loadable
extension.
3. REFERENCES
[1] Atonin Guttman, "R-trees - A Dynamic Index Structure For Spatial
Searching", University of California Berkeley, 1984.
[2] Norbert Beckmann, Hans-Peter Kriegel, Ralf Schneider, Bernhard Seeger,
"The R*-tree: An Efficient and Robust Access Method for Points and
Rectangles", Universitaet Bremen, 1990.

2753
ext/rtree/rtree.c Normal file

File diff suppressed because it is too large Load Diff

43
ext/rtree/rtree.test Normal file
View File

@ -0,0 +1,43 @@
#
# 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.
#
#***********************************************************************
# This file runs all rtree related tests.
#
# $Id: rtree.test,v 1.1 2008/05/26 18:41:54 danielk1977 Exp $
set testdir [file join [file dirname $argv0] .. .. test]
source $testdir/tester.tcl
rename finish_test really_finish_test
proc finish_test {} {}
set ISQUICK 1
set EXCLUDE {
rtree.test
}
# Files to include in the test. If this list is empty then everything
# that is not in the EXCLUDE list is run.
#
set INCLUDE {
}
foreach testfile [lsort -dictionary [glob [file dirname $argv0]/*.test]] {
set tail [file tail $testfile]
if {[lsearch -exact $EXCLUDE $tail]>=0} continue
if {[llength $INCLUDE]>0 && [lsearch -exact $INCLUDE $tail]<0} continue
source $testfile
catch {db close}
if {$sqlite_open_file_count>0} {
puts "$tail did not close all files: $sqlite_open_file_count"
incr nErr
lappend ::failList $tail
set sqlite_open_file_count 0
}
}
set sqlite_open_file_count 0
really_finish_test

361
ext/rtree/rtree1.test Normal file
View File

@ -0,0 +1,361 @@
# 2008 Feb 19
#
# 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.
#
#***********************************************************************
#
# The focus of this file is testing the r-tree extension.
#
# $Id: rtree1.test,v 1.1 2008/05/26 18:41:54 danielk1977 Exp $
#
set testdir [file join [file dirname $argv0] .. .. test]
source $testdir/tester.tcl
# Test plan:
#
# rtree-1.*: Creating/destroying r-tree tables.
# rtree-2.*: Test the implicit constraints - unique rowid and
# (coord[N]<=coord[N+1]) for even values of N. Also
# automatic assigning of rowid values.
# rtree-3.*: Linear scans of r-tree data.
# rtree-4.*: Test INSERT
# rtree-5.*: Test DELETE
# rtree-6.*: Test UPDATE
# rtree-7.*: Test renaming an r-tree table.
# rtree-8.*: Test constrained scans of r-tree data.
#
ifcapable !rtree {
finish_test
return
}
#----------------------------------------------------------------------------
# Test cases rtree-1.* test CREATE and DROP table statements.
#
# Test creating and dropping an rtree table.
#
do_test rtree-1.1.1 {
execsql { CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2, y1, y2) }
} {}
do_test rtree-1.1.2 {
execsql { SELECT name FROM sqlite_master ORDER BY name }
} {t1 t1_node t1_parent t1_rowid}
do_test rtree-1.1.3 {
execsql {
DROP TABLE t1;
SELECT name FROM sqlite_master ORDER BY name;
}
} {}
# Test creating and dropping an rtree table with an odd name in
# an attached database.
#
do_test rtree-1.2.1 {
execsql {
ATTACH 'test2.db' AS aux;
CREATE VIRTUAL TABLE aux.'a" "b' USING rtree(ii, x1, x2, y1, y2);
}
} {}
do_test rtree-1.2.2 {
execsql { SELECT name FROM sqlite_master ORDER BY name }
} {}
do_test rtree-1.2.3 {
execsql { SELECT name FROM aux.sqlite_master ORDER BY name }
} {{a" "b} {a" "b_node} {a" "b_parent} {a" "b_rowid}}
do_test rtree-1.2.4 {
execsql {
DROP TABLE aux.'a" "b';
SELECT name FROM aux.sqlite_master ORDER BY name;
}
} {}
# Test that the logic for checking the number of columns specified
# for an rtree table. Acceptable values are odd numbers between 3 and
# 11, inclusive.
#
set cols [list i1 i2 i3 i4 i5 i6 i7 i8 i9 iA iB iC iD iE iF iG iH iI iJ iK]
for {set nCol 1} {$nCol<[llength $cols]} {incr nCol} {
set columns [join [lrange $cols 0 [expr {$nCol-1}]] ,]
set X {0 {}}
if {$nCol%2 == 0} { set X {1 {Wrong number of columns for an rtree table}} }
if {$nCol < 3} { set X {1 {Too few columns for an rtree table}} }
if {$nCol > 11} { set X {1 {Too many columns for an rtree table}} }
do_test rtree-1.3.$nCol {
catchsql "
CREATE VIRTUAL TABLE t1 USING rtree($columns);
"
} $X
catchsql { DROP TABLE t1 }
}
# Test that it is possible to open an existing database that contains
# r-tree tables.
#
do_test rtree-1.4.1 {
execsql {
CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2);
INSERT INTO t1 VALUES(1, 5.0, 10.0);
INSERT INTO t1 VALUES(2, 15.0, 20.0);
}
} {}
do_test rtree-1.4.2 {
db close
sqlite3 db test.db
execsql { SELECT * FROM t1 ORDER BY ii }
} {1 5.0 10.0 2 15.0 20.0}
do_test rtree-1.4.3 {
execsql { DROP TABLE t1 }
} {}
# Test that it is possible to create an r-tree table with ridiculous
# column names.
#
do_test rtree-1.5.1 {
execsql {
CREATE VIRTUAL TABLE t1 USING rtree("the key", "x dim.", "x2'dim");
INSERT INTO t1 VALUES(1, 2, 3);
SELECT "the key", "x dim.", "x2'dim" FROM t1;
}
} {1 2.0 3.0}
do_test rtree-1.5.1 {
execsql { DROP TABLE t1 }
} {}
# Force the r-tree constructor to fail.
#
do_test rtree-1.6.1 {
execsql { CREATE TABLE t1_rowid(a); }
catchsql {
CREATE VIRTUAL TABLE t1 USING rtree("the key", "x dim.", "x2'dim");
}
} {1 {table 't1_rowid' already exists}}
do_test rtree-1.6.1 {
execsql { DROP TABLE t1_rowid }
} {}
#----------------------------------------------------------------------------
# Test cases rtree-2.*
#
do_test rtree-2.1.1 {
execsql {
CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2, y1, y2);
SELECT * FROM t1;
}
} {}
do_test rtree-2.1.2 {
execsql { INSERT INTO t1 VALUES(NULL, 1, 3, 2, 4) }
execsql { SELECT * FROM t1 }
} {1 1.0 3.0 2.0 4.0}
do_test rtree-2.1.3 {
execsql { INSERT INTO t1 VALUES(NULL, 1, 3, 2, 4) }
execsql { SELECT rowid FROM t1 ORDER BY rowid }
} {1 2}
do_test rtree-2.1.3 {
execsql { INSERT INTO t1 VALUES(NULL, 1, 3, 2, 4) }
execsql { SELECT ii FROM t1 ORDER BY ii }
} {1 2 3}
do_test rtree-2.2.1 {
catchsql { INSERT INTO t1 VALUES(2, 1, 3, 2, 4) }
} {1 {constraint failed}}
do_test rtree-2.2.2 {
catchsql { INSERT INTO t1 VALUES(4, 1, 3, 4, 2) }
} {1 {constraint failed}}
do_test rtree-2.2.3 {
catchsql { INSERT INTO t1 VALUES(4, 3, 1, 2, 4) }
} {1 {constraint failed}}
do_test rtree-2.2.4 {
execsql { SELECT ii FROM t1 ORDER BY ii }
} {1 2 3}
do_test rtree-2.X {
execsql { DROP TABLE t1 }
} {}
#----------------------------------------------------------------------------
# Test cases rtree-3.* test linear scans of r-tree table data. To test
# this we have to insert some data into an r-tree, but that is not the
# focus of these tests.
#
do_test rtree-3.1.1 {
execsql {
CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2, y1, y2);
SELECT * FROM t1;
}
} {}
do_test rtree-3.1.2 {
execsql {
INSERT INTO t1 VALUES(5, 1, 3, 2, 4);
SELECT * FROM t1;
}
} {5 1.0 3.0 2.0 4.0}
do_test rtree-3.1.3 {
execsql {
INSERT INTO t1 VALUES(6, 2, 6, 4, 8);
SELECT * FROM t1;
}
} {5 1.0 3.0 2.0 4.0 6 2.0 6.0 4.0 8.0}
# Test the constraint on the coordinates (c[i]<=c[i+1] where (i%2==0)):
do_test rtree-3.2.1 {
catchsql { INSERT INTO t1 VALUES(7, 2, 6, 4, 3) }
} {1 {constraint failed}}
do_test rtree-3.2.2 {
catchsql { INSERT INTO t1 VALUES(8, 2, 6, 3, 3) }
} {0 {}}
#----------------------------------------------------------------------------
# Test cases rtree-5.* test DELETE operations.
#
do_test rtree-5.1.1 {
execsql { CREATE VIRTUAL TABLE t2 USING rtree(ii, x1, x2) }
} {}
do_test rtree-5.1.2 {
execsql {
INSERT INTO t2 VALUES(1, 10, 20);
INSERT INTO t2 VALUES(2, 30, 40);
INSERT INTO t2 VALUES(3, 50, 60);
SELECT * FROM t2 ORDER BY ii;
}
} {1 10.0 20.0 2 30.0 40.0 3 50.0 60.0}
do_test rtree-5.1.3 {
execsql {
DELETE FROM t2 WHERE ii=2;
SELECT * FROM t2 ORDER BY ii;
}
} {1 10.0 20.0 3 50.0 60.0}
do_test rtree-5.1.4 {
execsql {
DELETE FROM t2 WHERE ii=1;
SELECT * FROM t2 ORDER BY ii;
}
} {3 50.0 60.0}
do_test rtree-5.1.5 {
execsql {
DELETE FROM t2 WHERE ii=3;
SELECT * FROM t2 ORDER BY ii;
}
} {}
do_test rtree-5.1.6 {
execsql { SELECT * FROM t2_rowid }
} {}
#----------------------------------------------------------------------------
# Test cases rtree-5.* test UPDATE operations.
#
do_test rtree-6.1.1 {
execsql { CREATE VIRTUAL TABLE t3 USING rtree(ii, x1, x2, y1, y2) }
} {}
do_test rtree-6.1.2 {
execsql {
INSERT INTO t3 VALUES(1, 2, 3, 4, 5);
UPDATE t3 SET x2=5;
SELECT * FROM t3;
}
} {1 2.0 5.0 4.0 5.0}
do_test rtree-6.1.3 {
execsql { UPDATE t3 SET ii = 2 }
execsql { SELECT * FROM t3 }
} {2 2.0 5.0 4.0 5.0}
#----------------------------------------------------------------------------
# Test cases rtree-7.* test rename operations.
#
do_test rtree-7.1.1 {
execsql {
CREATE VIRTUAL TABLE t4 USING rtree(ii, x1, x2, y1, y2, z1, z2);
INSERT INTO t4 VALUES(1, 2, 3, 4, 5, 6, 7);
}
} {}
do_test rtree-7.1.2 {
execsql { ALTER TABLE t4 RENAME TO t5 }
execsql { SELECT * FROM t5 }
} {1 2.0 3.0 4.0 5.0 6.0 7.0}
do_test rtree-7.1.3 {
db close
sqlite3 db test.db
execsql { SELECT * FROM t5 }
} {1 2.0 3.0 4.0 5.0 6.0 7.0}
do_test rtree-7.1.4 {
execsql { ALTER TABLE t5 RENAME TO 'raisara "one"'''}
execsql { SELECT * FROM "raisara ""one""'" }
} {1 2.0 3.0 4.0 5.0 6.0 7.0}
do_test rtree-7.1.5 {
execsql { SELECT * FROM 'raisara "one"''' }
} {1 2.0 3.0 4.0 5.0 6.0 7.0}
do_test rtree-7.1.6 {
execsql { ALTER TABLE "raisara ""one""'" RENAME TO "abc 123" }
execsql { SELECT * FROM "abc 123" }
} {1 2.0 3.0 4.0 5.0 6.0 7.0}
do_test rtree-7.1.7 {
db close
sqlite3 db test.db
execsql { SELECT * FROM "abc 123" }
} {1 2.0 3.0 4.0 5.0 6.0 7.0}
# An error midway through a rename operation.
do_test rtree-7.2.1 {
execsql {
CREATE TABLE t4_node(a);
}
catchsql { ALTER TABLE "abc 123" RENAME TO t4 }
} {1 {SQL logic error or missing database}}
do_test rtree-7.2.2 {
execsql { SELECT * FROM "abc 123" }
} {1 2.0 3.0 4.0 5.0 6.0 7.0}
do_test rtree-7.2.3 {
execsql {
DROP TABLE t4_node;
CREATE TABLE t4_rowid(a);
}
catchsql { ALTER TABLE "abc 123" RENAME TO t4 }
} {1 {SQL logic error or missing database}}
do_test rtree-7.2.4 {
db close
sqlite3 db test.db
execsql { SELECT * FROM "abc 123" }
} {1 2.0 3.0 4.0 5.0 6.0 7.0}
do_test rtree-7.2.5 {
execsql { DROP TABLE t4_rowid }
execsql { ALTER TABLE "abc 123" RENAME TO t4 }
execsql { SELECT * FROM t4 }
} {1 2.0 3.0 4.0 5.0 6.0 7.0}
#----------------------------------------------------------------------------
# Test cases rtree-8.*
#
# Test that the function to determine if a leaf cell is part of the
# result set works.
do_test rtree-8.1.1 {
execsql {
CREATE VIRTUAL TABLE t6 USING rtree(ii, x1, x2);
INSERT INTO t6 VALUES(1, 3, 7);
INSERT INTO t6 VALUES(2, 4, 6);
}
} {}
do_test rtree-8.1.2 { execsql { SELECT ii FROM t6 WHERE x1>2 } } {1 2}
do_test rtree-8.1.3 { execsql { SELECT ii FROM t6 WHERE x1>3 } } {2}
do_test rtree-8.1.4 { execsql { SELECT ii FROM t6 WHERE x1>4 } } {}
do_test rtree-8.1.5 { execsql { SELECT ii FROM t6 WHERE x1>5 } } {}
do_test rtree-8.1.6 { execsql { SELECT ii FROM t6 WHERE x1<3 } } {}
do_test rtree-8.1.7 { execsql { SELECT ii FROM t6 WHERE x1<4 } } {1}
do_test rtree-8.1.8 { execsql { SELECT ii FROM t6 WHERE x1<5 } } {1 2}
finish_test

144
ext/rtree/rtree2.test Normal file
View File

@ -0,0 +1,144 @@
# 2008 Feb 19
#
# 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.
#
#***********************************************************************
#
# The focus of this file is testing the r-tree extension.
#
# $Id: rtree2.test,v 1.1 2008/05/26 18:41:54 danielk1977 Exp $
#
set testdir [file join [file dirname $argv0] .. .. test]
source $testdir/tester.tcl
source [file join [file dirname $argv0] rtree_util.tcl]
ifcapable !rtree {
finish_test
return
}
set ::NROW 1000
set ::NDEL 10
set ::NSELECT 100
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
#}
}
}
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 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}]
if {$rc != 1} {
puts $t1
puts $t2
}
set rc
} {1}
do_test rtree2-$nDim.5.$ii.2 {
rtree_check db t1
} {0}
}
do_test rtree2-$nDim.6 {
execsql {
DROP TABLE t1;
DROP TABLE t2;
}
} {}
}
finish_test

72
ext/rtree/rtree3.test Normal file
View File

@ -0,0 +1,72 @@
# 2008 Feb 19
#
# 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.
#
#***********************************************************************
#
# The focus of this file is testing that the r-tree correctly handles
# out-of-memory conditions.
#
# $Id: rtree3.test,v 1.1 2008/05/26 18:41:54 danielk1977 Exp $
#
set testdir [file join [file dirname $argv0] .. .. test]
source $testdir/tester.tcl
ifcapable !rtree {
finish_test
return
}
# Only run these tests if memory debugging is turned on.
#
source $testdir/malloc_common.tcl
if {!$MEMDEBUG} {
puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
finish_test
return
}
do_malloc_test rtree3-1 -sqlbody {
BEGIN TRANSACTION;
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
INSERT INTO rt VALUES(NULL, 3, 5, 7, 9);
INSERT INTO rt VALUES(NULL, 13, 15, 17, 19);
DELETE FROM rt WHERE ii = 1;
SELECT * FROM rt;
SELECT ii FROM rt WHERE ii = 2;
COMMIT;
}
do_malloc_test rtree3-2 -sqlprep {
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
INSERT INTO rt VALUES(NULL, 3, 5, 7, 9);
} -sqlbody {
DROP TABLE rt;
}
do_malloc_test rtree3-3 -sqlprep {
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
INSERT INTO rt VALUES(NULL, 3, 5, 7, 9);
} -tclbody {
db eval BEGIN
for {set ii 0} {$ii < 100} {incr ii} {
set f [expr rand()]
db eval {INSERT INTO rt VALUES(NULL, $f*10.0, $f*10.0, $f*15.0, $f*15.0)}
}
db eval COMMIT
db eval BEGIN
for {set ii 0} {$ii < 100} {incr ii} {
set f [expr rand()]
db eval { DELETE FROM rt WHERE x1<($f*10.0) AND x1>($f*10.5) }
}
db eval COMMIT
}
finish_test

76
ext/rtree/rtree_perf.tcl Normal file
View File

@ -0,0 +1,76 @@
set testdir [file join [file dirname $argv0] .. .. test]
source $testdir/tester.tcl
ifcapable !rtree {
finish_test
return
}
set NROW 10000
set NQUERY 500
puts "Generating $NROW rows of data..."
set data [list]
for {set ii 0} {$ii < $NROW} {incr ii} {
set x1 [expr {rand()*1000}]
set x2 [expr {$x1+rand()*50}]
set y1 [expr {rand()*1000}]
set y2 [expr {$y1+rand()*50}]
lappend data $x1 $x2 $y1 $y2
}
puts "Finished generating data"
set sql1 {CREATE TABLE btree(ii INTEGER PRIMARY KEY, x1, x2, y1, y2)}
set sql2 {CREATE VIRTUAL TABLE rtree USING rtree(ii, x1, x2, y1, y2)}
puts "Creating tables:"
puts " $sql1"
puts " $sql2"
db eval $sql1
db eval $sql2
db eval "pragma cache_size=100"
puts -nonewline "Inserting into btree... "
flush stdout
set btree_time [time {db transaction {
set ii 1
foreach {x1 x2 y1 y2} $data {
db eval {INSERT INTO btree VALUES($ii, $x1, $x2, $y1, $y2)}
incr ii
}
}}]
puts "$btree_time"
puts -nonewline "Inserting into rtree... "
flush stdout
set rtree_time [time {db transaction {
set ii 1
foreach {x1 x2 y1 y2} $data {
incr ii
db eval {INSERT INTO rtree VALUES($ii, $x1, $x2, $y1, $y2)}
}
}}]
puts "$rtree_time"
puts -nonewline "Selecting from btree... "
flush stdout
set btree_select_time [time {
foreach {x1 x2 y1 y2} [lrange $data 0 [expr $NQUERY*4-1]] {
db eval {SELECT * FROM btree WHERE x1<$x1 AND x2>$x2 AND y1<$y1 AND y2>$y2}
}
}]
puts "$btree_select_time"
puts -nonewline "Selecting from rtree... "
flush stdout
set rtree_select_time [time {
foreach {x1 x2 y1 y2} [lrange $data 0 [expr $NQUERY*4-1]] {
db eval {SELECT * FROM rtree WHERE x1<$x1 AND x2>$x2 AND y1<$y1 AND y2>$y2}
}
}]
puts "$rtree_select_time"

195
ext/rtree/rtree_util.tcl Normal file
View File

@ -0,0 +1,195 @@
# 2008 Feb 19
#
# 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.
#
#***********************************************************************
#
# This file contains Tcl code that may be useful for testing or
# analyzing r-tree structures created with this module. It is
# used by both test procedures and the r-tree viewer application.
#
# $Id: rtree_util.tcl,v 1.1 2008/05/26 18:41:54 danielk1977 Exp $
#
#--------------------------------------------------------------------------
# PUBLIC API:
#
# rtree_depth
# rtree_ndim
# rtree_node
# rtree_mincells
# rtree_check
# rtree_dump
# rtree_treedump
#
proc rtree_depth {db zTab} {
$db one "SELECT rtreedepth(data) FROM ${zTab}_node WHERE nodeno=1"
}
proc rtree_nodedepth {db zTab iNode} {
set iDepth [rtree_depth $db $zTab]
set ii $iNode
while {$ii != 1} {
set sql "SELECT parentnode FROM ${zTab}_parent WHERE nodeno = $ii"
set ii [db one $sql]
incr iDepth -1
}
return $iDepth
}
# Return the number of dimensions of the rtree.
#
proc rtree_ndim {db zTab} {
set nDim [expr {(([llength [$db eval "pragma table_info($zTab)"]]/6)-1)/2}]
}
# Return the contents of rtree node $iNode.
#
proc rtree_node {db zTab iNode {iPrec 6}} {
set nDim [rtree_ndim $db $zTab]
set sql "
SELECT rtreenode($nDim, data) FROM ${zTab}_node WHERE nodeno = $iNode
"
set node [db one $sql]
set nCell [llength $node]
set nCoord [expr $nDim*2]
for {set ii 0} {$ii < $nCell} {incr ii} {
for {set jj 1} {$jj <= $nCoord} {incr jj} {
set newval [format "%.${iPrec}f" [lindex $node $ii $jj]]
lset node $ii $jj $newval
}
}
set node
}
proc rtree_mincells {db zTab} {
set n [$db one "select length(data) FROM ${zTab}_node LIMIT 1"]
set nMax [expr {int(($n-4)/(8+[rtree_ndim $db $zTab]*2*4))}]
return [expr {int($nMax/3)}]
}
# An integrity check for the rtree $zTab accessible via database
# connection $db.
#
proc rtree_check {db zTab} {
array unset ::checked
# Check each r-tree node.
set rc [catch {
rtree_node_check $db $zTab 1 [rtree_depth $db $zTab]
} msg]
if {$rc && $msg ne ""} { error $msg }
# Check that the _rowid and _parent tables have the right
# number of entries.
set nNode [$db one "SELECT count(*) FROM ${zTab}_node"]
set nRow [$db one "SELECT count(*) FROM ${zTab}"]
set nRowid [$db one "SELECT count(*) FROM ${zTab}_rowid"]
set nParent [$db one "SELECT count(*) FROM ${zTab}_parent"]
if {$nNode != ($nParent+1)} {
error "Wrong number of entries in ${zTab}_parent"
}
if {$nRow != $nRowid} {
error "Wrong number of entries in ${zTab}_rowid"
}
return $rc
}
proc rtree_node_check {db zTab iNode iDepth} {
if {[info exists ::checked($iNode)]} { error "Second ref to $iNode" }
set ::checked($iNode) 1
set node [rtree_node $db $zTab $iNode]
if {$iNode!=1 && [llength $node]==0} { error "No such node: $iNode" }
if {$iNode != 1 && [llength $node]<[rtree_mincells $db $zTab]} {
puts "Node $iNode: Has only [llength $node] cells"
error ""
}
if {$iNode == 1 && [llength $node]==1 && [rtree_depth $db $zTab]>0} {
set depth [rtree_depth $db $zTab]
puts "Node $iNode: Has only 1 child (tree depth is $depth)"
error ""
}
set nDim [expr {([llength [lindex $node 0]]-1)/2}]
if {$iDepth > 0} {
set d [expr $iDepth-1]
foreach cell $node {
set shouldbe [rtree_node_check $db $zTab [lindex $cell 0] $d]
if {$cell ne $shouldbe} {
puts "Node $iNode: Cell is: {$cell}, should be {$shouldbe}"
error ""
}
}
}
set mapping_table "${zTab}_parent"
set mapping_sql "SELECT parentnode FROM $mapping_table WHERE rowid = \$rowid"
if {$iDepth==0} {
set mapping_table "${zTab}_rowid"
set mapping_sql "SELECT nodeno FROM $mapping_table WHERE rowid = \$rowid"
}
foreach cell $node {
set rowid [lindex $cell 0]
set mapping [db one $mapping_sql]
if {$mapping != $iNode} {
puts "Node $iNode: $mapping_table entry for cell $rowid is $mapping"
error ""
}
}
set ret [list $iNode]
for {set ii 1} {$ii <= $nDim*2} {incr ii} {
set f [lindex $node 0 $ii]
foreach cell $node {
set f2 [lindex $cell $ii]
if {($ii%2)==1 && $f2<$f} {set f $f2}
if {($ii%2)==0 && $f2>$f} {set f $f2}
}
lappend ret $f
}
return $ret
}
proc rtree_dump {db zTab} {
set zRet ""
set nDim [expr {(([llength [$db eval "pragma table_info($zTab)"]]/6)-1)/2}]
set sql "SELECT nodeno, rtreenode($nDim, data) AS node FROM ${zTab}_node"
$db eval $sql {
append zRet [format "% -10s %s\n" $nodeno $node]
}
set zRet
}
proc rtree_nodetreedump {db zTab zIndent iDepth iNode} {
set ret ""
set node [rtree_node $db $zTab $iNode 1]
append ret [format "%-3d %s%s\n" $iNode $zIndent $node]
if {$iDepth>0} {
foreach cell $node {
set i [lindex $cell 0]
append ret [rtree_nodetreedump $db $zTab "$zIndent " [expr $iDepth-1] $i]
}
}
set ret
}
proc rtree_treedump {db zTab} {
set d [rtree_depth $db $zTab]
rtree_nodetreedump $db $zTab "" $d 1
}

189
ext/rtree/viewrtree.tcl Normal file
View File

@ -0,0 +1,189 @@
load ./libsqlite3.dylib
#package require sqlite3
source [file join [file dirname $argv0] rtree_util.tcl]
wm title . "SQLite r-tree viewer"
if {[llength $argv]!=1} {
puts stderr "Usage: $argv0 <database-file>"
puts stderr ""
exit
}
sqlite3 db [lindex $argv 0]
canvas .c -background white -width 400 -height 300 -highlightthickness 0
button .b -text "Parent Node" -command {
set sql "SELECT parentnode FROM $::O(zTab)_parent WHERE nodeno = $::O(iNode)"
set ::O(iNode) [db one $sql]
if {$::O(iNode) eq ""} {set ::O(iNode) 1}
view_node
}
set O(iNode) 1
set O(zTab) ""
set O(listbox_captions) [list]
set O(listbox_itemmap) [list]
set O(listbox_highlight) -1
listbox .l -listvariable ::O(listbox_captions) -yscrollcommand {.ls set}
scrollbar .ls -command {.l yview}
label .status -font courier -anchor w
label .title -anchor w -text "Node 1:" -background white -borderwidth 0
set rtree_tables [list]
db eval {
SELECT name
FROM sqlite_master
WHERE type='table' AND sql LIKE '%virtual%table%using%rtree%'
} {
set nCol [expr [llength [db eval "pragma table_info($name)"]]/6]
if {$nCol != 5} {
puts stderr "Not viewing $name - is not 2-dimensional"
} else {
lappend rtree_tables [list Table $name]
}
}
if {$rtree_tables eq ""} {
puts stderr "Cannot find an r-tree table in database [lindex $argv 0]"
puts stderr ""
exit
}
eval tk_optionMenu .select option_var $rtree_tables
trace add variable option_var write set_option_var
proc set_option_var {args} {
set ::O(zTab) [lindex $::option_var 1]
set ::O(iNode) 1
view_node
}
set ::O(zTab) [lindex $::rtree_tables 0 1]
bind .l <1> {listbox_click [.l nearest %y]}
bind .l <Motion> {listbox_mouseover [.l nearest %y]}
bind .l <Leave> {listbox_mouseover -1}
proc listbox_click {sel} {
if {$sel ne ""} {
set ::O(iNode) [lindex $::O(listbox_captions) $sel 1]
view_node
}
}
proc listbox_mouseover {i} {
set oldid [lindex $::O(listbox_itemmap) $::O(listbox_highlight)]
.c itemconfigure $oldid -fill ""
.l selection clear 0 end
.status configure -text ""
if {$i>=0} {
set id [lindex $::O(listbox_itemmap) $i]
.c itemconfigure $id -fill grey
.c lower $id
set ::O(listbox_highlight) $i
.l selection set $i
.status configure -text [cell_report db $::O(zTab) $::O(iNode) $i]
}
}
grid configure .select -row 0 -column 0 -columnspan 2 -sticky nsew
grid configure .b -row 1 -column 0 -columnspan 2 -sticky nsew
grid configure .l -row 2 -column 0 -sticky nsew
grid configure .status -row 3 -column 0 -columnspan 3 -sticky nsew
grid configure .title -row 0 -column 2 -sticky nsew
grid configure .c -row 1 -column 2 -rowspan 2 -sticky nsew
grid configure .ls -row 2 -column 1 -sticky nsew
grid columnconfigure . 2 -weight 1
grid rowconfigure . 2 -weight 1
proc node_bbox {data} {
set xmin 0
set xmax 0
set ymin 0
set ymax 0
foreach {rowid xmin xmax ymin ymax} [lindex $data 0] break
foreach cell [lrange $data 1 end] {
foreach {rowid x1 x2 y1 y2} $cell break
if {$x1 < $xmin} {set xmin $x1}
if {$x2 > $xmax} {set xmax $x2}
if {$y1 < $ymin} {set ymin $y1}
if {$y2 > $ymax} {set ymax $y2}
}
list $xmin $xmax $ymin $ymax
}
proc view_node {} {
set iNode $::O(iNode)
set zTab $::O(zTab)
set data [rtree_node db $zTab $iNode 12]
set depth [rtree_nodedepth db $zTab $iNode]
.c delete all
set ::O(listbox_captions) [list]
set ::O(listbox_itemmap) [list]
set $::O(listbox_highlight) -1
.b configure -state normal
if {$iNode == 1} {.b configure -state disabled}
.title configure -text "Node $iNode: [cell_report db $zTab $iNode -1]"
foreach {xmin xmax ymin ymax} [node_bbox $data] break
set total_area 0.0
set xscale [expr {double([winfo width .c]-20)/($xmax-$xmin)}]
set yscale [expr {double([winfo height .c]-20)/($ymax-$ymin)}]
set xoff [expr {10.0 - $xmin*$xscale}]
set yoff [expr {10.0 - $ymin*$yscale}]
foreach cell $data {
foreach {rowid x1 x2 y1 y2} $cell break
set total_area [expr {$total_area + ($x2-$x1)*($y2-$y1)}]
set x1 [expr {$x1*$xscale + $xoff}]
set x2 [expr {$x2*$xscale + $xoff}]
set y1 [expr {$y1*$yscale + $yoff}]
set y2 [expr {$y2*$yscale + $yoff}]
set id [.c create rectangle $x1 $y1 $x2 $y2]
if {$depth>0} {
lappend ::O(listbox_captions) "Node $rowid"
lappend ::O(listbox_itemmap) $id
}
}
}
proc cell_report {db zTab iParent iCell} {
set data [rtree_node db $zTab $iParent 12]
set cell [lindex $data $iCell]
foreach {xmin xmax ymin ymax} [node_bbox $data] break
set total_area [expr ($xmax-$xmin)*($ymax-$ymin)]
if {$cell eq ""} {
set cell_area 0.0
foreach cell $data {
foreach {rowid x1 x2 y1 y2} $cell break
set cell_area [expr $cell_area+($x2-$x1)*($y2-$y1)]
}
set cell_area [expr $cell_area/[llength $data]]
set zReport [format "Size = %.1f x %.1f Average child area = %.1f%%" \
[expr $xmax-$xmin] [expr $ymax-$ymin] [expr 100.0*$cell_area/$total_area]\
]
append zReport " Sub-tree height: [rtree_nodedepth db $zTab $iParent]"
} else {
foreach {rowid x1 x2 y1 y2} $cell break
set cell_area [expr ($x2-$x1)*($y2-$y1)]
set zReport [format "Size = %.1f x %.1f Area = %.1f%%" \
[expr $x2-$x1] [expr $y2-$y1] [expr 100.0*$cell_area/$total_area]
]
}
return $zReport
}
view_node
bind .c <Configure> view_node

View File

@ -58,7 +58,7 @@ LIBOBJ+= alter.o analyze.o attach.o auth.o bitvec.o btmutex.o btree.o build.o \
select.o table.o $(TCLOBJ) tokenize.o trigger.o \
update.o util.o vacuum.o \
vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbefifo.o vdbemem.o \
where.o utf.o legacy.o vtab.o
where.o utf.o legacy.o vtab.o rtree.o
EXTOBJ = icu.o
EXTOBJ += fts1.o \
@ -412,6 +412,9 @@ fts3_tokenizer.o: $(TOP)/ext/fts3/fts3_tokenizer.c $(HDR) $(EXTHDR)
fts3_tokenizer1.o: $(TOP)/ext/fts3/fts3_tokenizer1.c $(HDR) $(EXTHDR)
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_tokenizer1.c
rtree.o: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR)
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/rtree/rtree.c
# Rules for building test programs and for running tests
#

View File

@ -1,5 +1,5 @@
C Fix\sthe\sLIKE\squery\soptimizer\sso\sthat\sit\sworks\swith\sLIKE\spatterns\nending\sin\s'@%'\son\sNOCASE\scolumns.\s\sTicket\s#3139.\s(CVS\s5158)
D 2008-05-26T18:33:41
C Import\s'rtree'\sextension.\s(CVS\s5159)
D 2008-05-26T18:41:54
F Makefile.arm-wince-mingw32ce-gcc ac5f7b2cef0cd850d6f755ba6ee4ab961b1fadf7
F Makefile.in 79aeba12300a54903f1b1257c1e7c190234045dd
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@ -63,9 +63,18 @@ F ext/fts3/fts3_tokenizer1.c 0a5bcc579f35de5d24a9345d7908dc25ae403ee7
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
F ext/icu/README.txt 3b130aa66e7a681136f6add198b076a2f90d1e33
F ext/icu/icu.c 12e763d288d23b5a49de37caa30737b971a2f1e2
F ext/rtree/README decb7976cfacf834074d15028af99344439e30c3
F ext/rtree/rtree.c f56f8a5888d3584f5b19112ade2855db5a985690
F ext/rtree/rtree.test ec173a9420ff012e4d29b3063add143583a597a7
F ext/rtree/rtree1.test 96563843773129eaec544f52768853f06be61d9c
F ext/rtree/rtree2.test 98f3c39b03577330566abf3c7e1e0baf8f9aa521
F ext/rtree/rtree3.test 46d1959aa651d3df8b64d93762d3061c62b38105
F ext/rtree/rtree_perf.tcl 0fabb6d5c48cb8024e042ce5d4bb88998b6ec1cb
F ext/rtree/rtree_util.tcl ee0a0311eb12175319d78bfb37302320496cee6e
F ext/rtree/viewrtree.tcl 09526398dae87a5a87c5aac2b3854dbaf8376869
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
F ltmain.sh 09fe5815427dc7d0abb188bbcdf0e34896577210
F main.mk 6a916bb5c17cf2a753346b32cc0869ffdc1ed4b3
F main.mk 6c01687f355dc8c7dff14a952a7c720b3a4c11a6
F mkdll.sh 712e74f3efe08a6ba12b2945d018a29a89d7fe3b
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
F mkextw.sh 1a866b53637dab137191341cc875575a5ca110fb
@ -102,7 +111,7 @@ F src/insert.c 77f0829b3e2edd19e9238195c56b0d56ab000f17
F src/journal.c cffd2cd214e58c0e99c3ff632b3bee6c7cbb260e
F src/legacy.c 8f5a2b25d9673b4004287cf2bf51dbf7d0738406
F src/loadext.c eac6c61810a3b531808774bec7f3d238cfe261f3
F src/main.c cf415e0920dc9f66806dd766ad72ba5cda533363
F src/main.c 51f02209493572630dfcf4d4c8855f08aae21b9b
F src/malloc.c 12c1ae98ef1eff34b13c9eb526e0b7b479e1e820
F src/md5.c 008216bbb5d34c6fbab5357aa68575ad8a31516a
F src/mem1.c fc716ff521b6dd3e43eaa211967383308800e70a
@ -148,7 +157,7 @@ F src/test9.c 4615ef08750245a2d96aaa7cbe2fb4aff2b57acc
F src/test_async.c 0d26a72361022f6f732dd1174c6615bad6e587ff
F src/test_autoext.c 5e892ab84aece3f0428920bf46923f16ac83962a
F src/test_btree.c c1308ba0b88ab577fa56c9e493a09829dfcded9c
F src/test_config.c b910754c5ba311abf149457cdbfd66144e715b35
F src/test_config.c 982bba6221b854a86427ae64e9c65b313b0f6e03
F src/test_devsym.c 76cf28b79c6f01658083ae2a972647b97a362a01
F src/test_func.c f4aafa10f17d52c43a64b47717265802e6e552b3
F src/test_hexio.c 2f1122aa3f012fa0142ee3c36ce5c902a70cd12f
@ -637,7 +646,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
P 77d5a7aa1c7ea715298228ed2dbd0497cacbd0e4
R d32b39a4864ba1e92f958c456cb9d453
U drh
Z b0b4c859c49b7350ba450c2f85000be9
P 33548744369643cc8843b74ad1fc1b7d5988d7a4
R 675fcc0cf171d37f901dfc59fa943c1b
U danielk1977
Z a3cfe87334f3bee1a0f0c75b4054c9e1

View File

@ -1 +1 @@
33548744369643cc8843b74ad1fc1b7d5988d7a4
b104dcd6adadbd3fe15a348fe9d4d290119e139e

View File

@ -14,7 +14,7 @@
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.440 2008/05/22 13:56:17 danielk1977 Exp $
** $Id: main.c,v 1.441 2008/05/26 18:41:54 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@ -1184,6 +1184,14 @@ static int openDatabase(
rc = sqlite3IcuInit(db);
}
#endif
#ifdef SQLITE_ENABLE_RTREE
if( !db->mallocFailed && rc==SQLITE_OK){
extern int sqlite3RtreeInit(sqlite3*);
rc = sqlite3RtreeInit(db);
}
#endif
sqlite3Error(db, rc, 0);
/* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking

View File

@ -16,7 +16,7 @@
** The focus of this file is providing the TCL testing layer
** access to compile-time constants.
**
** $Id: test_config.c,v 1.25 2008/04/14 01:00:58 drh Exp $
** $Id: test_config.c,v 1.26 2008/05/26 18:41:54 danielk1977 Exp $
*/
#include "sqliteLimit.h"
@ -338,6 +338,12 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double",
Tcl_SetVar2(interp, "sqlite_options", "reindex", "1", TCL_GLOBAL_ONLY);
#endif
#ifdef SQLITE_ENABLE_RTREE
Tcl_SetVar2(interp, "sqlite_options", "rtree", "1", TCL_GLOBAL_ONLY);
#else
Tcl_SetVar2(interp, "sqlite_options", "rtree", "0", TCL_GLOBAL_ONLY);
#endif
#ifdef SQLITE_OMIT_SCHEMA_PRAGMAS
Tcl_SetVar2(interp, "sqlite_options", "schema_pragmas", "0", TCL_GLOBAL_ONLY);
#else