Adding the sqlite sourcecode
This commit is contained in:
1 parent
bdb54a1f8f
commit
406b584760
2208 files changed
+1172785
-1
No files matched your search
@@ -0,0 +1,120 @@
|
||||
|
||||
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 primary 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 equal to 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);
|
||||
INSERT INTO boxes VALUES(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
|
||||
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 RTREE 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, libSqliteRtree.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.
|
||||
+1839
File diff suppressed because it is too large.
Load diff
+4479
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
** 2008 May 26
|
||||
**
|
||||
** 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 header file is used by programs that want to link against the
|
||||
** RTREE library. All it does is declare the sqlite3RtreeInit() interface.
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
|
||||
#ifdef SQLITE_OMIT_VIRTUALTABLE
|
||||
# undef SQLITE_ENABLE_RTREE
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int sqlite3RtreeInit(sqlite3 *db);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
@@ -0,0 +1,818 @@
|
||||
# 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.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix rtree1
|
||||
|
||||
# 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.
|
||||
#
|
||||
# rtree-12.*: Test that on-conflict clauses are supported.
|
||||
# rtree-13.*: Test that bug [d2889096e7bdeac6d] has been fixed.
|
||||
# rtree-14.*: Test if a non-integer is inserted into the PK column of an
|
||||
# r-tree table, it is converted to an integer before being
|
||||
# inserted. Also that if a non-numeric is inserted into one
|
||||
# of the min/max dimension columns, it is converted to the
|
||||
# required type before being inserted.
|
||||
# rtree-15.*: Check that DROP TABLE works within a transaction that
|
||||
# writes to an r-tree table.
|
||||
#
|
||||
|
||||
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.2a {
|
||||
execsql { SELECT name FROM sqlite_master ORDER BY name }
|
||||
} {t1 t1_node t1_parent t1_rowid}
|
||||
do_execsql_test rtree-1.1.2b {
|
||||
SELECT name FROM pragma_table_list WHERE type='shadow' ORDER BY name;
|
||||
} {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 {
|
||||
file delete -force test2.db
|
||||
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 }
|
||||
}
|
||||
do_catchsql_test rtree-1.3.1000 {
|
||||
CREATE VIRTUAL TABLE t1000 USING rtree;
|
||||
} {1 {Too few columns for an rtree table}}
|
||||
|
||||
# Like execsql except display output as integer where that can be
|
||||
# done without loss of information.
|
||||
#
|
||||
proc execsql_intout {sql} {
|
||||
set out {}
|
||||
foreach term [execsql $sql] {
|
||||
regsub {\.0$} $term {} term
|
||||
lappend out $term
|
||||
}
|
||||
return $out
|
||||
}
|
||||
|
||||
# Test that it is possible to open an existing database that contains
|
||||
# r-tree tables.
|
||||
#
|
||||
do_execsql_test rtree-1.4.1a {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2);
|
||||
INSERT INTO t1 VALUES(1, 5.0, 10.0);
|
||||
SELECT substr(hex(data),1,40) FROM t1_node;
|
||||
} {00000001000000000000000140A0000041200000}
|
||||
do_execsql_test rtree-1.4.1b {
|
||||
INSERT INTO t1 VALUES(2, 15.0, 20.0);
|
||||
} {}
|
||||
do_test rtree-1.4.2 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql_intout { SELECT * FROM t1 ORDER BY ii }
|
||||
} {1 5 10 2 15 20}
|
||||
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_intout {
|
||||
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 3}
|
||||
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_intout { SELECT * FROM t1 }
|
||||
} {1 1 3 2 4}
|
||||
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 {UNIQUE constraint failed: t1.ii}}
|
||||
do_test rtree-2.2.2 {
|
||||
catchsql { INSERT INTO t1 VALUES(4, 1, 3, 4, 2) }
|
||||
} {1 {rtree constraint failed: t1.(y1<=y2)}}
|
||||
do_test rtree-2.2.3 {
|
||||
catchsql { INSERT INTO t1 VALUES(4, 3, 1, 2, 4) }
|
||||
} {1 {rtree constraint failed: t1.(x1<=x2)}}
|
||||
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_intout {
|
||||
INSERT INTO t1 VALUES(5, 1, 3, 2, 4);
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {5 1 3 2 4}
|
||||
do_test rtree-3.1.3 {
|
||||
execsql_intout {
|
||||
INSERT INTO t1 VALUES(6, 2, 6, 4, 8);
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {5 1 3 2 4 6 2 6 4 8}
|
||||
|
||||
# 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 {rtree constraint failed: t1.(y1<=y2)}}
|
||||
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_intout {
|
||||
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 20 2 30 40 3 50 60}
|
||||
do_test rtree-5.1.3 {
|
||||
execsql_intout {
|
||||
DELETE FROM t2 WHERE ii=2;
|
||||
SELECT * FROM t2 ORDER BY ii;
|
||||
}
|
||||
} {1 10 20 3 50 60}
|
||||
do_test rtree-5.1.4 {
|
||||
execsql_intout {
|
||||
DELETE FROM t2 WHERE ii=1;
|
||||
SELECT * FROM t2 ORDER BY ii;
|
||||
}
|
||||
} {3 50 60}
|
||||
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_intout {
|
||||
INSERT INTO t3 VALUES(1, 2, 3, 4, 5);
|
||||
UPDATE t3 SET x2=5;
|
||||
SELECT * FROM t3;
|
||||
}
|
||||
} {1 2 5 4 5}
|
||||
do_test rtree-6.1.3 {
|
||||
execsql { UPDATE t3 SET ii = 2 }
|
||||
execsql_intout { SELECT * FROM t3 }
|
||||
} {2 2 5 4 5}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# 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_intout { SELECT * FROM t5 }
|
||||
} {1 2 3 4 5 6 7}
|
||||
do_test rtree-7.1.3 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql_intout { SELECT * FROM t5 }
|
||||
} {1 2 3 4 5 6 7}
|
||||
do_test rtree-7.1.4 {
|
||||
execsql { ALTER TABLE t5 RENAME TO 'raisara "one"'''}
|
||||
execsql_intout { SELECT * FROM "raisara ""one""'" }
|
||||
} {1 2 3 4 5 6 7}
|
||||
do_test rtree-7.1.5 {
|
||||
execsql_intout { SELECT * FROM 'raisara "one"''' }
|
||||
} {1 2 3 4 5 6 7}
|
||||
do_test rtree-7.1.6 {
|
||||
execsql { ALTER TABLE "raisara ""one""'" RENAME TO "abc 123" }
|
||||
execsql_intout { SELECT * FROM "abc 123" }
|
||||
} {1 2 3 4 5 6 7}
|
||||
do_test rtree-7.1.7 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql_intout { SELECT * FROM "abc 123" }
|
||||
} {1 2 3 4 5 6 7}
|
||||
|
||||
# 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}}
|
||||
do_test rtree-7.2.2 {
|
||||
execsql_intout { SELECT * FROM "abc 123" }
|
||||
} {1 2 3 4 5 6 7}
|
||||
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}}
|
||||
do_test rtree-7.2.4 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql_intout { SELECT * FROM "abc 123" }
|
||||
} {1 2 3 4 5 6 7}
|
||||
do_test rtree-7.2.5 {
|
||||
execsql { DROP TABLE t4_rowid }
|
||||
execsql { ALTER TABLE "abc 123" RENAME TO t4 }
|
||||
execsql_intout { SELECT * FROM t4 }
|
||||
} {1 2 3 4 5 6 7}
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# 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>''} } {}
|
||||
do_test rtree-8.1.7 { execsql { SELECT ii FROM t6 WHERE x1>null}} {}
|
||||
do_test rtree-8.1.8 { execsql { SELECT ii FROM t6 WHERE x1>'2'} } {1 2}
|
||||
do_test rtree-8.1.9 { execsql { SELECT ii FROM t6 WHERE x1>'3'} } {2}
|
||||
do_test rtree-8.2.2 { execsql { SELECT ii FROM t6 WHERE x1>=2 } } {1 2}
|
||||
do_test rtree-8.2.3 { execsql { SELECT ii FROM t6 WHERE x1>=3 } } {1 2}
|
||||
do_test rtree-8.2.4 { execsql { SELECT ii FROM t6 WHERE x1>=4 } } {2}
|
||||
do_test rtree-8.2.5 { execsql { SELECT ii FROM t6 WHERE x1>=5 } } {}
|
||||
do_test rtree-8.2.6 { execsql { SELECT ii FROM t6 WHERE x1>=''} } {}
|
||||
do_test rtree-8.2.7 { execsql { SELECT ii FROM t6 WHERE x1>=null}} {}
|
||||
do_test rtree-8.2.8 { execsql { SELECT ii FROM t6 WHERE x1>='4'} } {2}
|
||||
do_test rtree-8.2.9 { execsql { SELECT ii FROM t6 WHERE x1>='5'} } {}
|
||||
do_test rtree-8.3.2 { execsql { SELECT ii FROM t6 WHERE x1<2 } } {}
|
||||
do_test rtree-8.3.3 { execsql { SELECT ii FROM t6 WHERE x1<3 } } {}
|
||||
do_test rtree-8.3.4 { execsql { SELECT ii FROM t6 WHERE x1<4 } } {1}
|
||||
do_test rtree-8.3.5 { execsql { SELECT ii FROM t6 WHERE x1<5 } } {1 2}
|
||||
do_test rtree-8.3.6 { execsql { SELECT ii FROM t6 WHERE x1<''} } {1 2}
|
||||
do_test rtree-8.3.7 { execsql { SELECT ii FROM t6 WHERE x1<null}} {}
|
||||
do_test rtree-8.3.8 { execsql { SELECT ii FROM t6 WHERE x1<'3'} } {}
|
||||
do_test rtree-8.3.9 { execsql { SELECT ii FROM t6 WHERE x1<'4'} } {1}
|
||||
do_test rtree-8.4.2 { execsql { SELECT ii FROM t6 WHERE x1<=2 } } {}
|
||||
do_test rtree-8.4.3 { execsql { SELECT ii FROM t6 WHERE x1<=3 } } {1}
|
||||
do_test rtree-8.4.4 { execsql { SELECT ii FROM t6 WHERE x1<=4 } } {1 2}
|
||||
do_test rtree-8.4.5 { execsql { SELECT ii FROM t6 WHERE x1<=5 } } {1 2}
|
||||
do_test rtree-8.4.6 { execsql { SELECT ii FROM t6 WHERE x1<=''} } {1 2}
|
||||
do_test rtree-8.4.7 { execsql { SELECT ii FROM t6 WHERE x1<=null}} {}
|
||||
do_test rtree-8.5.2 { execsql { SELECT ii FROM t6 WHERE x1=2 } } {}
|
||||
do_test rtree-8.5.3 { execsql { SELECT ii FROM t6 WHERE x1=3 } } {1}
|
||||
do_test rtree-8.5.4 { execsql { SELECT ii FROM t6 WHERE x1=4 } } {2}
|
||||
do_test rtree-8.5.5 { execsql { SELECT ii FROM t6 WHERE x1=5 } } {}
|
||||
do_test rtree-8.5.6 { execsql { SELECT ii FROM t6 WHERE x1=''} } {}
|
||||
do_test rtree-8.5.7 { execsql { SELECT ii FROM t6 WHERE x1=null}} {}
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Test cases rtree-9.*
|
||||
#
|
||||
# Test that ticket #3549 is fixed.
|
||||
do_test rtree-9.1 {
|
||||
execsql {
|
||||
CREATE TABLE foo (id INTEGER PRIMARY KEY);
|
||||
CREATE VIRTUAL TABLE bar USING rtree (id, minX, maxX, minY, maxY);
|
||||
INSERT INTO foo VALUES (null);
|
||||
INSERT INTO foo SELECT null FROM foo;
|
||||
INSERT INTO foo SELECT null FROM foo;
|
||||
INSERT INTO foo SELECT null FROM foo;
|
||||
INSERT INTO foo SELECT null FROM foo;
|
||||
INSERT INTO foo SELECT null FROM foo;
|
||||
INSERT INTO foo SELECT null FROM foo;
|
||||
DELETE FROM foo WHERE id > 40;
|
||||
INSERT INTO bar SELECT NULL, 0, 0, 0, 0 FROM foo;
|
||||
}
|
||||
} {}
|
||||
|
||||
# This used to crash.
|
||||
do_test rtree-9.2 {
|
||||
execsql {
|
||||
SELECT count(*) FROM bar b1, bar b2, foo s1 WHERE s1.id = b1.id;
|
||||
}
|
||||
} {1600}
|
||||
do_test rtree-9.3 {
|
||||
execsql {
|
||||
SELECT count(*) FROM bar b1, bar b2, foo s1
|
||||
WHERE b1.minX <= b2.maxX AND s1.id = b1.id;
|
||||
}
|
||||
} {1600}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Ticket #3970: Check that the error message is meaningful when a
|
||||
# keyword is used as a column name.
|
||||
#
|
||||
do_test rtree-10.1 {
|
||||
catchsql { CREATE VIRTUAL TABLE t7 USING rtree(index, x1, y1, x2, y2) }
|
||||
} {1 {near "index": syntax error}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test last_insert_rowid().
|
||||
#
|
||||
do_test rtree-11.1 {
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE t8 USING rtree(idx, x1, x2, y1, y2);
|
||||
INSERT INTO t8 VALUES(1, 1.0, 1.0, 2.0, 2.0);
|
||||
SELECT last_insert_rowid();
|
||||
}
|
||||
} {1}
|
||||
do_test rtree-11.2 {
|
||||
execsql {
|
||||
INSERT INTO t8 VALUES(NULL, 1.0, 1.0, 2.0, 2.0);
|
||||
SELECT last_insert_rowid();
|
||||
}
|
||||
} {2}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test on-conflict clause handling.
|
||||
#
|
||||
db_delete_and_reopen
|
||||
do_execsql_test 12.0.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree_i32(idx, x1, x2, y1, y2);
|
||||
INSERT INTO t1 VALUES(1, 1, 2, 3, 4);
|
||||
SELECT substr(hex(data),1,56) FROM t1_node;
|
||||
} {00000001000000000000000100000001000000020000000300000004}
|
||||
do_execsql_test 12.0.2 {
|
||||
INSERT INTO t1 VALUES(2, 2, 3, 4, 5);
|
||||
INSERT INTO t1 VALUES(3, 3, 4, 5, 6);
|
||||
|
||||
CREATE TABLE source(idx, x1, x2, y1, y2);
|
||||
INSERT INTO source VALUES(5, 8, 8, 8, 8);
|
||||
INSERT INTO source VALUES(2, 7, 7, 7, 7);
|
||||
}
|
||||
db_save_and_close
|
||||
foreach {tn sql_template testdata} {
|
||||
1 "INSERT %CONF% INTO t1 VALUES(2, 7, 7, 7, 7)" {
|
||||
ROLLBACK 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
|
||||
ABORT 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
IGNORE 0 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
FAIL 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
REPLACE 0 0 {1 1 2 3 4 2 7 7 7 7 3 3 4 5 6 4 4 5 6 7}
|
||||
}
|
||||
|
||||
2 "INSERT %CONF% INTO t1 SELECT * FROM source" {
|
||||
ROLLBACK 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
|
||||
ABORT 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
IGNORE 1 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7 5 8 8 8 8}
|
||||
FAIL 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7 5 8 8 8 8}
|
||||
REPLACE 1 0 {1 1 2 3 4 2 7 7 7 7 3 3 4 5 6 4 4 5 6 7 5 8 8 8 8}
|
||||
}
|
||||
|
||||
3 "UPDATE %CONF% t1 SET idx = 2 WHERE idx = 4" {
|
||||
ROLLBACK 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
|
||||
ABORT 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
IGNORE 0 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
FAIL 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
REPLACE 0 0 {1 1 2 3 4 2 4 5 6 7 3 3 4 5 6}
|
||||
}
|
||||
|
||||
3 "UPDATE %CONF% t1 SET idx = ((idx+1)%5)+1 WHERE idx > 2" {
|
||||
ROLLBACK 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
|
||||
ABORT 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
IGNORE 1 0 {1 1 2 3 4 2 2 3 4 5 4 4 5 6 7 5 3 4 5 6}
|
||||
FAIL 1 1 {1 1 2 3 4 2 2 3 4 5 4 4 5 6 7 5 3 4 5 6}
|
||||
REPLACE 1 0 {1 4 5 6 7 2 2 3 4 5 5 3 4 5 6}
|
||||
}
|
||||
|
||||
4 "INSERT %CONF% INTO t1 VALUES(2, 7, 6, 7, 7)" {
|
||||
ROLLBACK 0 2 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
|
||||
ABORT 0 2 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
IGNORE 0 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
FAIL 0 2 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
REPLACE 0 2 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
}
|
||||
|
||||
} {
|
||||
foreach {mode uses error data} $testdata {
|
||||
db_restore_and_reopen
|
||||
|
||||
set sql [string map [list %CONF% "OR $mode"] $sql_template]
|
||||
set testname "12.$tn.[string tolower $mode]"
|
||||
|
||||
execsql {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(4, 4, 5, 6, 7);
|
||||
}
|
||||
|
||||
set res(0) {0 {}}
|
||||
set res(1) {1 {UNIQUE constraint failed: t1.idx}}
|
||||
set res(2) {1 {rtree constraint failed: t1.(x1<=x2)}}
|
||||
|
||||
do_catchsql_test $testname.1 $sql $res($error)
|
||||
do_test $testname.2 [list sql_uses_stmt db $sql] $uses
|
||||
do_execsql_test $testname.3 { SELECT * FROM t1 ORDER BY idx } $data
|
||||
|
||||
do_rtree_integrity_test $testname.4 t1
|
||||
db close
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that bug [d2889096e7bdeac6d] has been fixed.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 13.1 {
|
||||
CREATE VIRTUAL TABLE t9 USING rtree(id, xmin, xmax);
|
||||
INSERT INTO t9 VALUES(1,0,0);
|
||||
INSERT INTO t9 VALUES(2,0,0);
|
||||
SELECT * FROM t9 WHERE id IN (1, 2);
|
||||
} {1 0.0 0.0 2 0.0 0.0}
|
||||
|
||||
do_execsql_test 13.2 {
|
||||
WITH r(x) AS (
|
||||
SELECT 1 UNION ALL
|
||||
SELECT 2 UNION ALL
|
||||
SELECT 3
|
||||
)
|
||||
SELECT * FROM r CROSS JOIN t9 WHERE id=x;
|
||||
} {1 1 0.0 0.0 2 2 0.0 0.0}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test if a non-integer is inserted into the PK column of an r-tree
|
||||
# table, it is converted to an integer before being inserted. Also
|
||||
# that if a non-numeric is inserted into one of the min/max dimension
|
||||
# columns, it is converted to the required type before being inserted.
|
||||
#
|
||||
do_execsql_test 14.1 {
|
||||
CREATE VIRTUAL TABLE t10 USING rtree(ii, x1, x2);
|
||||
}
|
||||
|
||||
do_execsql_test 14.2 {
|
||||
INSERT INTO t10 VALUES(NULL, 1, 2);
|
||||
INSERT INTO t10 VALUES(NULL, 2, 3);
|
||||
INSERT INTO t10 VALUES('4xxx', 3, 4);
|
||||
INSERT INTO t10 VALUES(5.0, 4, 5);
|
||||
INSERT INTO t10 VALUES(6.4, 5, 6);
|
||||
}
|
||||
do_execsql_test 14.3 {
|
||||
SELECT * FROM t10;
|
||||
} {
|
||||
1 1.0 2.0 2 2.0 3.0 4 3.0 4.0 5 4.0 5.0 6 5.0 6.0
|
||||
}
|
||||
|
||||
do_execsql_test 14.4 {
|
||||
DELETE FROM t10;
|
||||
INSERT INTO t10 VALUES(1, 'one', 'two');
|
||||
INSERT INTO t10 VALUES(2, '52xyz', '81...');
|
||||
}
|
||||
do_execsql_test 14.5 {
|
||||
SELECT * FROM t10;
|
||||
} {
|
||||
1 0.0 0.0
|
||||
2 52.0 81.0
|
||||
}
|
||||
do_execsql_test 14.6 {
|
||||
INSERT INTO t10 VALUES(0,10,20);
|
||||
SELECT * FROM t10 WHERE ii=NULL;
|
||||
} {}
|
||||
do_execsql_test 14.7 {
|
||||
SELECT * FROM t10 WHERE ii='xyz';
|
||||
} {}
|
||||
do_execsql_test 14.8 {
|
||||
SELECT * FROM t10 WHERE ii='0.0';
|
||||
} {0 10.0 20.0}
|
||||
do_execsql_test 14.9 {
|
||||
SELECT * FROM t10 WHERE ii=0.0;
|
||||
} {0 10.0 20.0}
|
||||
|
||||
|
||||
do_execsql_test 14.104 {
|
||||
DROP TABLE t10;
|
||||
CREATE VIRTUAL TABLE t10 USING rtree_i32(ii, x1, x2);
|
||||
INSERT INTO t10 VALUES(1, 'one', 'two');
|
||||
INSERT INTO t10 VALUES(2, '52xyz', '81...');
|
||||
INSERT INTO t10 VALUES(3, 42.3, 49.9);
|
||||
}
|
||||
do_execsql_test 14.105 {
|
||||
SELECT * FROM t10;
|
||||
} {
|
||||
1 0 0
|
||||
2 52 81
|
||||
3 42 49
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 15.0 {
|
||||
CREATE VIRTUAL TABLE rt USING rtree(id, x1,x2, y1,y2);
|
||||
CREATE TEMP TABLE t13(a, b, c);
|
||||
}
|
||||
do_execsql_test 15.1 {
|
||||
BEGIN;
|
||||
INSERT INTO rt VALUES(1,2,3,4,5);
|
||||
}
|
||||
do_execsql_test 15.2 {
|
||||
DROP TABLE t13;
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
# Test cases for the new auxiliary columns feature
|
||||
#
|
||||
do_catchsql_test 16.100 {
|
||||
CREATE VIRTUAL TABLE t16 USING rtree(id,x0,x1,y0,+aux1,x1);
|
||||
} {1 {Auxiliary rtree columns must be last}}
|
||||
do_test 16.110 {
|
||||
set sql {
|
||||
CREATE VIRTUAL TABLE t16 USING rtree(
|
||||
id, x00, x01, x10, x11, x20, x21, x30, x31, x40, x41
|
||||
}
|
||||
for {set i 12} {$i<=100} {incr i} {
|
||||
append sql ", +a$i"
|
||||
}
|
||||
append sql ");"
|
||||
execsql $sql
|
||||
} {}
|
||||
do_test 16.120 {
|
||||
set sql {
|
||||
CREATE VIRTUAL TABLE t16b USING rtree(
|
||||
id, x00, x01, x10, x11, x20, x21, x30, x31, x40, x41
|
||||
}
|
||||
for {set i 12} {$i<=101} {incr i} {
|
||||
append sql ", +a$i"
|
||||
}
|
||||
append sql ");"
|
||||
catchsql $sql
|
||||
} {1 {Too many columns for an rtree table}}
|
||||
|
||||
do_execsql_test 16.130 {
|
||||
DROP TABLE IF EXISTS rt1;
|
||||
CREATE VIRTUAL TABLE rt1 USING rtree(id, x1, x2, +aux);
|
||||
INSERT INTO rt1 VALUES(1, 1, 2, 'aux1');
|
||||
INSERT INTO rt1 VALUES(2, 2, 3, 'aux2');
|
||||
INSERT INTO rt1 VALUES(3, 3, 4, 'aux3');
|
||||
INSERT INTO rt1 VALUES(4, 4, 5, 'aux4');
|
||||
SELECT * FROM rt1 WHERE id IN (1, 2, 3, 4);
|
||||
} {1 1.0 2.0 aux1 2 2.0 3.0 aux2 3 3.0 4.0 aux3 4 4.0 5.0 aux4}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 17.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(id, x1 PRIMARY KEY, x2, y1, y2);
|
||||
CREATE VIRTUAL TABLE t2 USING rtree(id, x1, x2, y1, y2 UNIQUE);
|
||||
}
|
||||
do_execsql_test 17.1 {
|
||||
REINDEX t1;
|
||||
REINDEX t2;
|
||||
} {}
|
||||
|
||||
do_execsql_test 17.2 {
|
||||
REINDEX;
|
||||
} {}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 18.0 {
|
||||
CREATE VIRTUAL TABLE rt0 USING rtree(c0, c1, c2);
|
||||
INSERT INTO rt0(c0,c1,c2) VALUES(9,2,3);
|
||||
SELECT c0 FROM rt0 WHERE rt0.c1 > '-1';
|
||||
SELECT rt0.c1 > '-1' FROM rt0;
|
||||
} {9 1}
|
||||
|
||||
expand_all_sql db
|
||||
|
||||
# 2020-02-28 ticket e63b4d1a65546532
|
||||
reset_db
|
||||
do_execsql_test 19.0 {
|
||||
CREATE VIRTUAL TABLE rt0 USING rtree(a,b,c);
|
||||
INSERT INTO rt0(a,b,c) VALUES(0,0.0,0.0);
|
||||
CREATE VIEW v0(x) AS SELECT DISTINCT rt0.b FROM rt0;
|
||||
SELECT v0.x FROM v0, rt0;
|
||||
} {0.0}
|
||||
do_execsql_test 19.1 {
|
||||
SELECT v0.x FROM v0, rt0 WHERE v0.x = rt0.b;
|
||||
} {0.0}
|
||||
|
||||
# 2022-06-20 https://sqlite.org/forum/forumpost/57bdf2217d
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 20.0 {
|
||||
CREATE VIRTUAL TABLE rt0 USING rtree(id, x0, x1);
|
||||
CREATE TABLE t0(a INT);
|
||||
CREATE TABLE t1(b INT);
|
||||
INSERT INTO rt0 VALUES(0, 0, 0);
|
||||
}
|
||||
do_catchsql_test 20.1 {
|
||||
SELECT * FROM t1 JOIN t0 ON x0>a RIGHT JOIN rt0 ON true WHERE +x0 = 0;
|
||||
} {1 {ON clause references tables to its right}}
|
||||
do_catchsql_test 20.2 {
|
||||
SELECT * FROM t1 JOIN t0 ON x0>a RIGHT JOIN rt0 ON true WHERE x0 = 0;
|
||||
} {1 {ON clause references tables to its right}}
|
||||
db null -
|
||||
do_execsql_test 20.3 {
|
||||
SELECT * FROM t1 JOIN t0 ON true RIGHT JOIN rt0 ON x0>a WHERE +x0 = 0;
|
||||
} {- - 0 0.0 0.0}
|
||||
do_execsql_test 20.4 {
|
||||
SELECT * FROM t1 JOIN t0 ON true RIGHT JOIN rt0 ON x0>a WHERE x0 = 0;
|
||||
} {- - 0 0.0 0.0}
|
||||
|
||||
# 2023-05-19 https://sqlite.org/forum/forumpost/da61c4a1b5b4af19
|
||||
# Do not omit constraints that involve equality comparisons of
|
||||
# floating-point values.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 21.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(id, x0, x1);
|
||||
INSERT INTO t1 VALUES(0, 1, 9223372036854775807);
|
||||
SELECT count(*) FROM t1 WHERE x1=9223372036854775807;
|
||||
} {0}
|
||||
do_execsql_test 21.1 {
|
||||
SELECT x1=9223372036854775807 FROM t1;
|
||||
} {0}
|
||||
|
||||
# 2023-05-22 https://sqlite.org/forum/forumpost/da70ee0d0d
|
||||
# Round-off error associated with using large integer constraints on
|
||||
# a rtree search.
|
||||
#
|
||||
if {$tcl_platform(machine)!="i686" || $tcl_platform(os)!="Linux"} {
|
||||
reset_db
|
||||
do_execsql_test 22.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree ( id, x0, x1 );
|
||||
INSERT INTO t1 VALUES (123, 9223372036854775799, 9223372036854775800);
|
||||
SELECT id FROM t1 WHERE x0 > 9223372036854775807;
|
||||
} {123}
|
||||
do_execsql_test 22.1 {
|
||||
SELECT id, x0 > 9223372036854775807 AS 'a0' FROM t1;
|
||||
} {123 1}
|
||||
}
|
||||
|
||||
# 2023-10-14 dbsqlfuzz --sql-fuzz find. rtreecheck() should not call
|
||||
# BEGIN/COMMIT because that causes problems with statement transactions,
|
||||
# and it is unnecessary.
|
||||
#
|
||||
reset_db
|
||||
do_test 23.0 {
|
||||
db eval {CREATE TABLE t1(a,b,c);}
|
||||
catch {db eval {CREATE TABLE t2 AS SELECT rtreecheck('t1') AS y;}}
|
||||
db eval {PRAGMA integrity_check;}
|
||||
} {ok}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 24.0 {
|
||||
CREATE VIRTUAL TABLE rt1 USING rtree_i32(rid, c1, c2);
|
||||
INSERT INTO rt1(rid, c1, c2) VALUES (9223372036854775807, 10, 18);
|
||||
}
|
||||
|
||||
do_execsql_test 24.1 {
|
||||
SELECT (rid = (CAST (9223372036854775807 AS REAL)))
|
||||
FROM rt1 WHERE
|
||||
(rid = (CAST (9223372036854775807 AS REAL)));
|
||||
}
|
||||
|
||||
do_execsql_test 24.2 {
|
||||
DELETE FROM rt1;
|
||||
INSERT INTO rt1(rid, c1, c2) VALUES(1,2,3);
|
||||
SELECT * FROM rt1 WHERE rid=1.005;
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,147 @@
|
||||
# 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.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !rtree {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set ::NROW 1000
|
||||
set ::NDEL 10
|
||||
set ::NSELECT 100
|
||||
|
||||
if {[info exists G(isquick)] && $G(isquick)} {
|
||||
set ::NROW 100
|
||||
set ::NSELECT 10
|
||||
}
|
||||
|
||||
foreach module {rtree_i32 rtree} {
|
||||
if {$module=="rtree_i32"} {set etype INT} {set etype REAL}
|
||||
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 $etype"
|
||||
}
|
||||
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 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_rtree_integrity_test rtree2-$module.$nDim.3 t1
|
||||
|
||||
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_rtree_integrity_test rtree2-$module.$nDim.5.$ii.2 t1
|
||||
}
|
||||
|
||||
do_test rtree2-$module.$nDim.6 {
|
||||
execsql {
|
||||
DROP TABLE t1;
|
||||
DROP TABLE t2;
|
||||
}
|
||||
} {}
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,270 @@
|
||||
# 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.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
ifcapable !rtree {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set ::TMPDBERROR [list 1 \
|
||||
{unable to open a temporary database file for storing temporary tables}
|
||||
]
|
||||
|
||||
# Test summary:
|
||||
#
|
||||
# rtree3-1: Test OOM in simple CREATE TABLE, INSERT, DELETE and SELECT
|
||||
# commands on an almost empty table.
|
||||
#
|
||||
# rtree3-2: Test OOM in a DROP TABLE command.
|
||||
#
|
||||
# rtree3-3a: Test OOM during a transaction to insert 100 pseudo-random rows.
|
||||
#
|
||||
# rtree3-3b: Test OOM during a transaction deleting all entries in the
|
||||
# database constructed in [rtree3-3a] in pseudo-random order.
|
||||
#
|
||||
# rtree3-4a: OOM during "SELECT count(*) FROM ..." on a big table.
|
||||
#
|
||||
# rtree3-4b: OOM while deleting rows from a big table.
|
||||
#
|
||||
# rtree3-5: Test OOM while inserting rows into a big table.
|
||||
#
|
||||
# rtree3-6: Test OOM while deleting all rows of a table, one at a time.
|
||||
#
|
||||
# rtree3-7: OOM during an ALTER TABLE RENAME TABLE command.
|
||||
#
|
||||
# rtree3-8: Test OOM while registering the r-tree module with sqlite.
|
||||
#
|
||||
# rtree3-11: OOM following a constraint failure
|
||||
#
|
||||
do_faultsim_test rtree3-1 -faults oom* -prep {
|
||||
faultsim_delete_and_reopen
|
||||
} -body {
|
||||
execsql {
|
||||
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_test rtree3-2.prep {
|
||||
faultsim_delete_and_reopen
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
|
||||
INSERT INTO rt VALUES(NULL, 3, 5, 7, 9);
|
||||
}
|
||||
faultsim_save_and_close
|
||||
} {}
|
||||
do_faultsim_test rtree3-2 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql { DROP TABLE rt }
|
||||
}
|
||||
|
||||
do_malloc_test rtree3-3.prep {
|
||||
faultsim_delete_and_reopen
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2, +a1, +a2);
|
||||
INSERT INTO rt VALUES(NULL, 3, 5, 7, 9);
|
||||
}
|
||||
faultsim_save_and_close
|
||||
} {}
|
||||
|
||||
do_faultsim_test rtree3-3a -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
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
|
||||
}
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test rtree3-3b -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
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
|
||||
}
|
||||
|
||||
do_test rtree3-4.prep {
|
||||
faultsim_delete_and_reopen
|
||||
execsql {
|
||||
BEGIN;
|
||||
PRAGMA page_size = 512;
|
||||
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
|
||||
}
|
||||
for {set i 0} {$i < 1500} {incr i} {
|
||||
execsql { INSERT INTO rt VALUES($i, $i, $i+1, $i, $i+1) }
|
||||
}
|
||||
execsql { COMMIT }
|
||||
faultsim_save_and_close
|
||||
} {}
|
||||
|
||||
do_faultsim_test rtree3-4a -faults oom-* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
db eval { SELECT count(*) FROM rt }
|
||||
} -test {
|
||||
faultsim_test_result {0 1500}
|
||||
}
|
||||
|
||||
do_faultsim_test rtree3-4b -faults oom-transient -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
db eval { DELETE FROM rt WHERE ii BETWEEN 1 AND 100 }
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
do_test rtree3-5.prep {
|
||||
faultsim_delete_and_reopen
|
||||
execsql {
|
||||
BEGIN;
|
||||
PRAGMA page_size = 512;
|
||||
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
|
||||
}
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
execsql { INSERT INTO rt VALUES($i, $i, $i+1, $i, $i+1) }
|
||||
}
|
||||
execsql { COMMIT }
|
||||
faultsim_save_and_close
|
||||
} {}
|
||||
do_faultsim_test rtree3-5 -faults oom-* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
for {set i 100} {$i < 110} {incr i} {
|
||||
execsql { INSERT INTO rt VALUES($i, $i, $i+1, $i, $i+1) }
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
do_test rtree3-6.prep {
|
||||
faultsim_delete_and_reopen
|
||||
execsql {
|
||||
BEGIN;
|
||||
PRAGMA page_size = 512;
|
||||
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
|
||||
}
|
||||
for {set i 0} {$i < 50} {incr i} {
|
||||
execsql { INSERT INTO rt VALUES($i, $i, $i+1, $i, $i+1) }
|
||||
}
|
||||
execsql { COMMIT }
|
||||
faultsim_save_and_close
|
||||
} {}
|
||||
do_faultsim_test rtree3-6 -faults oom-* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql BEGIN
|
||||
for {set i 0} {$i < 50} {incr i} {
|
||||
execsql { DELETE FROM rt WHERE ii=$i }
|
||||
}
|
||||
execsql COMMIT
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
do_test rtree3-7.prep {
|
||||
faultsim_delete_and_reopen
|
||||
execsql { CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2) }
|
||||
faultsim_save_and_close
|
||||
} {}
|
||||
do_faultsim_test rtree3-7 -faults oom-* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql { ALTER TABLE rt RENAME TO rt2 }
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} $::TMPDBERROR
|
||||
}
|
||||
|
||||
do_faultsim_test rtree3-8 -faults oom-* -prep {
|
||||
catch { db close }
|
||||
} -body {
|
||||
sqlite3 db test.db
|
||||
}
|
||||
|
||||
do_faultsim_test rtree3-9 -faults oom-* -prep {
|
||||
sqlite3 db :memory:
|
||||
} -body {
|
||||
set rc [register_cube_geom db]
|
||||
if {$rc != "SQLITE_OK"} { error $rc }
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
|
||||
}
|
||||
|
||||
do_test rtree3-10.prep {
|
||||
faultsim_delete_and_reopen
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2, z1, z2);
|
||||
INSERT INTO rt VALUES(1, 10, 10, 10, 11, 11, 11);
|
||||
INSERT INTO rt VALUES(2, 5, 6, 6, 7, 7, 8);
|
||||
}
|
||||
faultsim_save_and_close
|
||||
} {}
|
||||
do_faultsim_test rtree3-10 -faults oom-* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
register_cube_geom db
|
||||
execsql { SELECT * FROM rt }
|
||||
} -body {
|
||||
execsql { SELECT ii FROM rt WHERE ii MATCH cube(4.5, 5.5, 6.5, 1, 1, 1) }
|
||||
} -test {
|
||||
faultsim_test_result {0 2}
|
||||
}
|
||||
|
||||
|
||||
do_test rtree3-11.prep {
|
||||
faultsim_delete_and_reopen
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
|
||||
INSERT INTO rt VALUES(1, 2, 3, 4, 5);
|
||||
}
|
||||
faultsim_save_and_close
|
||||
} {}
|
||||
do_faultsim_test rtree3-10.1 -faults oom-* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql { SELECT * FROM rt }
|
||||
} -body {
|
||||
execsql { INSERT INTO rt VALUES(1, 2, 3, 4, 5) }
|
||||
} -test {
|
||||
faultsim_test_result {1 {UNIQUE constraint failed: rt.ii}} \
|
||||
{1 {constraint failed}}
|
||||
}
|
||||
do_faultsim_test rtree3-10.2 -faults oom-* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql { SELECT * FROM rt }
|
||||
} -body {
|
||||
execsql { INSERT INTO rt VALUES(2, 2, 3, 5, 4) }
|
||||
} -test {
|
||||
faultsim_test_result {1 {rtree constraint failed: rt.(y1<=y2)}} \
|
||||
{1 {constraint failed}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,254 @@
|
||||
# 2008 May 23
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Randomized test cases for the rtree extension.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !rtree {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set ::NROW 2500
|
||||
if {[info exists G(isquick)] && $G(isquick)} {
|
||||
set ::NROW 250
|
||||
}
|
||||
|
||||
ifcapable !rtree_int_only {
|
||||
# Return a floating point number between -X and X.
|
||||
#
|
||||
proc rand {X} {
|
||||
return [expr {int((rand()-0.5)*1024.0*$X)/512.0}]
|
||||
}
|
||||
|
||||
# Return a positive floating point number less than or equal to X
|
||||
#
|
||||
proc randincr {X} {
|
||||
while 1 {
|
||||
set r [expr {int(rand()*$X*32.0)/32.0}]
|
||||
if {$r>0.0} {return $r}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
# For rtree_int_only, return an number between -X and X.
|
||||
#
|
||||
proc rand {X} {
|
||||
return [expr {int((rand()-0.5)*2*$X)}]
|
||||
}
|
||||
|
||||
# Return a positive integer less than or equal to X
|
||||
#
|
||||
proc randincr {X} {
|
||||
while 1 {
|
||||
set r [expr {int(rand()*$X)+1}]
|
||||
if {$r>0} {return $r}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Scramble the $inlist into a random order.
|
||||
#
|
||||
proc scramble {inlist} {
|
||||
set y {}
|
||||
foreach x $inlist {
|
||||
lappend y [list [expr {rand()}] $x]
|
||||
}
|
||||
set y [lsort $y]
|
||||
set outlist {}
|
||||
foreach x $y {
|
||||
lappend outlist [lindex $x 1]
|
||||
}
|
||||
return $outlist
|
||||
}
|
||||
|
||||
# Always use the same random seed so that the sequence of tests
|
||||
# is repeatable.
|
||||
#
|
||||
expr {srand(1234)}
|
||||
|
||||
# Run these tests for all number of dimensions between 1 and 5.
|
||||
#
|
||||
for {set nDim 1} {$nDim<=5} {incr nDim} {
|
||||
|
||||
# Construct an rtree virtual table and an ordinary btree table
|
||||
# to mirror it. The ordinary table should be much slower (since
|
||||
# it has to do a full table scan) but should give the exact same
|
||||
# answers.
|
||||
#
|
||||
do_test rtree4-$nDim.1 {
|
||||
set clist {}
|
||||
set cklist {}
|
||||
for {set i 0} {$i<$nDim} {incr i} {
|
||||
lappend clist mn$i mx$i
|
||||
lappend cklist "mn$i<mx$i"
|
||||
}
|
||||
db eval "DROP TABLE IF EXISTS rx"
|
||||
db eval "DROP TABLE IF EXISTS bx"
|
||||
db eval "CREATE VIRTUAL TABLE rx USING rtree(id, [join $clist ,])"
|
||||
db eval "CREATE TABLE bx(id INTEGER PRIMARY KEY,\
|
||||
[join $clist ,], CHECK( [join $cklist { AND }] ))"
|
||||
} {}
|
||||
|
||||
# Do many insertions of small objects. Do both overlapping and
|
||||
# contained-within queries after each insert to verify that all
|
||||
# is well.
|
||||
#
|
||||
unset -nocomplain where
|
||||
for {set i 1} {$i<$::NROW} {incr i} {
|
||||
# Do a random insert
|
||||
#
|
||||
do_test rtree4-$nDim.2.$i.1 {
|
||||
set vlist {}
|
||||
for {set j 0} {$j<$nDim} {incr j} {
|
||||
set mn [rand 10000]
|
||||
set mx [expr {$mn+[randincr 50]}]
|
||||
lappend vlist $mn $mx
|
||||
}
|
||||
db eval "INSERT INTO rx VALUES(NULL, [join $vlist ,])"
|
||||
db eval "INSERT INTO bx VALUES(NULL, [join $vlist ,])"
|
||||
} {}
|
||||
|
||||
# Do a contained-in query on all dimensions
|
||||
#
|
||||
set where {}
|
||||
for {set j 0} {$j<$nDim} {incr j} {
|
||||
set mn [rand 10000]
|
||||
set mx [expr {$mn+[randincr 500]}]
|
||||
lappend where mn$j>=$mn mx$j<=$mx
|
||||
}
|
||||
set where "WHERE [join $where { AND }]"
|
||||
do_test rtree4-$nDim.2.$i.2 {
|
||||
list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
|
||||
} [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
|
||||
|
||||
# Do an overlaps query on all dimensions
|
||||
#
|
||||
set where {}
|
||||
for {set j 0} {$j<$nDim} {incr j} {
|
||||
set mn [rand 10000]
|
||||
set mx [expr {$mn+[randincr 500]}]
|
||||
lappend where mx$j>=$mn mn$j<=$mx
|
||||
}
|
||||
set where "WHERE [join $where { AND }]"
|
||||
do_test rtree4-$nDim.2.$i.3 {
|
||||
list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
|
||||
} [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
|
||||
|
||||
# Do a contained-in query with surplus contraints at the beginning.
|
||||
# This should force a full-table scan on the rtree.
|
||||
#
|
||||
set where {}
|
||||
for {set j 0} {$j<$nDim} {incr j} {
|
||||
lappend where mn$j>-10000 mx$j<10000
|
||||
}
|
||||
for {set j 0} {$j<$nDim} {incr j} {
|
||||
set mn [rand 10000]
|
||||
set mx [expr {$mn+[randincr 500]}]
|
||||
lappend where mn$j>=$mn mx$j<=$mx
|
||||
}
|
||||
set where "WHERE [join $where { AND }]"
|
||||
do_test rtree4-$nDim.2.$i.3 {
|
||||
list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
|
||||
} [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
|
||||
|
||||
# Do an overlaps query with surplus contraints at the beginning.
|
||||
# This should force a full-table scan on the rtree.
|
||||
#
|
||||
set where {}
|
||||
for {set j 0} {$j<$nDim} {incr j} {
|
||||
lappend where mn$j>=-10000 mx$j<=10000
|
||||
}
|
||||
for {set j 0} {$j<$nDim} {incr j} {
|
||||
set mn [rand 10000]
|
||||
set mx [expr {$mn+[randincr 500]}]
|
||||
lappend where mx$j>$mn mn$j<$mx
|
||||
}
|
||||
set where "WHERE [join $where { AND }]"
|
||||
do_test rtree4-$nDim.2.$i.4 {
|
||||
list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
|
||||
} [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
|
||||
|
||||
# Do a contained-in query with surplus contraints at the end
|
||||
#
|
||||
set where {}
|
||||
for {set j 0} {$j<$nDim} {incr j} {
|
||||
set mn [rand 10000]
|
||||
set mx [expr {$mn+[randincr 500]}]
|
||||
lappend where mn$j>=$mn mx$j<$mx
|
||||
}
|
||||
for {set j [expr {$nDim-1}]} {$j>=0} {incr j -1} {
|
||||
lappend where mn$j>=-10000 mx$j<10000
|
||||
}
|
||||
set where "WHERE [join $where { AND }]"
|
||||
do_test rtree4-$nDim.2.$i.5 {
|
||||
list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
|
||||
} [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
|
||||
|
||||
# Do an overlaps query with surplus contraints at the end
|
||||
#
|
||||
set where {}
|
||||
for {set j [expr {$nDim-1}]} {$j>=0} {incr j -1} {
|
||||
set mn [rand 10000]
|
||||
set mx [expr {$mn+[randincr 500]}]
|
||||
lappend where mx$j>$mn mn$j<=$mx
|
||||
}
|
||||
for {set j 0} {$j<$nDim} {incr j} {
|
||||
lappend where mx$j>-10000 mn$j<=10000
|
||||
}
|
||||
set where "WHERE [join $where { AND }]"
|
||||
do_test rtree4-$nDim.2.$i.6 {
|
||||
list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
|
||||
} [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
|
||||
|
||||
# Do a contained-in query with surplus contraints where the
|
||||
# constraints appear in a random order.
|
||||
#
|
||||
set where {}
|
||||
for {set j 0} {$j<$nDim} {incr j} {
|
||||
set mn1 [rand 10000]
|
||||
set mn2 [expr {$mn1+[randincr 100]}]
|
||||
set mx1 [expr {$mn2+[randincr 400]}]
|
||||
set mx2 [expr {$mx1+[randincr 100]}]
|
||||
lappend where mn$j>=$mn1 mn$j>$mn2 mx$j<$mx1 mx$j<=$mx2
|
||||
}
|
||||
set where "WHERE [join [scramble $where] { AND }]"
|
||||
do_test rtree4-$nDim.2.$i.7 {
|
||||
list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
|
||||
} [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
|
||||
|
||||
# Do an overlaps query with surplus contraints where the
|
||||
# constraints appear in a random order.
|
||||
#
|
||||
set where {}
|
||||
for {set j 0} {$j<$nDim} {incr j} {
|
||||
set mn1 [rand 10000]
|
||||
set mn2 [expr {$mn1+[randincr 100]}]
|
||||
set mx1 [expr {$mn2+[randincr 400]}]
|
||||
set mx2 [expr {$mx1+[randincr 100]}]
|
||||
lappend where mx$j>=$mn1 mx$j>$mn2 mn$j<$mx1 mn$j<=$mx2
|
||||
}
|
||||
set where "WHERE [join [scramble $where] { AND }]"
|
||||
do_test rtree4-$nDim.2.$i.8 {
|
||||
list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
|
||||
} [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
|
||||
}
|
||||
|
||||
do_rtree_integrity_test rtree4-$nDim.3 rx
|
||||
}
|
||||
|
||||
expand_all_sql db
|
||||
finish_test
|
||||
@@ -0,0 +1,83 @@
|
||||
# 2008 Jul 14
|
||||
#
|
||||
# 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 when it is
|
||||
# configured to store values as 32 bit integers.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !rtree {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_test rtree5-1.0 {
|
||||
execsql { CREATE VIRTUAL TABLE t1 USING rtree_i32(id, x1, x2, y1, y2) }
|
||||
} {}
|
||||
do_test rtree5-1.1 {
|
||||
execsql { INSERT INTO t1 VALUES(1, 5, 10, 4, 11.2) }
|
||||
} {}
|
||||
do_test rtree5-1.2 {
|
||||
execsql { SELECT * FROM t1 }
|
||||
} {1 5 10 4 11}
|
||||
do_test rtree5-1.3 {
|
||||
execsql { SELECT typeof(x1) FROM t1 }
|
||||
} {integer}
|
||||
|
||||
do_test rtree5-1.4 {
|
||||
execsql { SELECT x1==5 FROM t1 }
|
||||
} {1}
|
||||
do_test rtree5-1.5 {
|
||||
execsql { SELECT x1==5.2 FROM t1 }
|
||||
} {0}
|
||||
do_test rtree5-1.6 {
|
||||
execsql { SELECT x1==5.0 FROM t1 }
|
||||
} {1}
|
||||
|
||||
do_test rtree5-1.7 {
|
||||
execsql { SELECT count(*) FROM t1 WHERE x1==5 }
|
||||
} {1}
|
||||
ifcapable !rtree_int_only {
|
||||
do_test rtree5-1.8 {
|
||||
execsql { SELECT count(*) FROM t1 WHERE x1==5.2 }
|
||||
} {0}
|
||||
}
|
||||
do_test rtree5-1.9 {
|
||||
execsql { SELECT count(*) FROM t1 WHERE x1==5.0 }
|
||||
} {1}
|
||||
|
||||
do_test rtree5-1.10 {
|
||||
execsql { SELECT (1<<31)-5, (1<<31)-1, -1*(1<<31), -1*(1<<31)+5 }
|
||||
} {2147483643 2147483647 -2147483648 -2147483643}
|
||||
do_test rtree5-1.11 {
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(2, (1<<31)-5, (1<<31)-1, -1*(1<<31), -1*(1<<31)+5)
|
||||
}
|
||||
} {}
|
||||
do_test rtree5-1.12 {
|
||||
execsql { SELECT * FROM t1 WHERE id=2 }
|
||||
} {2 2147483643 2147483647 -2147483648 -2147483643}
|
||||
do_test rtree5-1.13 {
|
||||
execsql {
|
||||
SELECT * FROM t1 WHERE
|
||||
x1=2147483643 AND x2=2147483647 AND
|
||||
y1=-2147483648 AND y2=-2147483643
|
||||
}
|
||||
} {2 2147483643 2147483647 -2147483648 -2147483643}
|
||||
do_rtree_integrity_test rtree5-1.14 t1
|
||||
|
||||
expand_all_sql db
|
||||
finish_test
|
||||
@@ -0,0 +1,183 @@
|
||||
# 2008 Sep 1
|
||||
#
|
||||
# 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
|
||||
set testprefix rtree6
|
||||
|
||||
ifcapable {!rtree || rtree_int_only} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Operator Byte Value
|
||||
# ----------------------
|
||||
# = 0x41 ('A')
|
||||
# <= 0x42 ('B')
|
||||
# < 0x43 ('C')
|
||||
# >= 0x44 ('D')
|
||||
# > 0x45 ('E')
|
||||
# ----------------------
|
||||
|
||||
proc rtree_strategy {sql} {
|
||||
set ret [list]
|
||||
db eval "explain $sql" a {
|
||||
if {$a(opcode) eq "VFilter"} {
|
||||
lappend ret $a(p4)
|
||||
}
|
||||
}
|
||||
set ret
|
||||
}
|
||||
|
||||
proc query_plan {sql} {
|
||||
set ret [list]
|
||||
db eval "explain query plan $sql" a {
|
||||
lappend ret $a(detail)
|
||||
}
|
||||
set ret
|
||||
}
|
||||
|
||||
do_test rtree6-1.1 {
|
||||
execsql {
|
||||
CREATE TABLE t2(k INTEGER PRIMARY KEY, v);
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2, y1, y2);
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test rtree6-1.2 {
|
||||
rtree_strategy {SELECT * FROM t1 WHERE x1>10}
|
||||
} {E0}
|
||||
do_test rtree6-1.2.1 {
|
||||
rtree_strategy {SELECT * FROM t1 WHERE x1>10 AND x2 LIKE '%x%'}
|
||||
} {E0}
|
||||
|
||||
do_test rtree6-1.3 {
|
||||
rtree_strategy {SELECT * FROM t1 WHERE x1<10}
|
||||
} {C0}
|
||||
|
||||
do_test rtree6-1.4 {
|
||||
rtree_strategy {SELECT * FROM t1,t2 WHERE k=ii AND x1<10}
|
||||
} {C0}
|
||||
|
||||
do_test rtree6-1.5 {
|
||||
rtree_strategy {SELECT * FROM t1,t2 WHERE k=+ii AND x1<10}
|
||||
} {C0}
|
||||
|
||||
do_eqp_test rtree6.2.1 {
|
||||
SELECT * FROM t1,t2 WHERE k=+ii AND x1<10
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN t1 VIRTUAL TABLE INDEX 2:C0
|
||||
`--SEARCH t2 USING INTEGER PRIMARY KEY (rowid=?)
|
||||
}
|
||||
|
||||
do_eqp_test rtree6.2.2 {
|
||||
SELECT * FROM t1,t2 WHERE k=ii AND x1<10
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN t1 VIRTUAL TABLE INDEX 2:C0
|
||||
`--SEARCH t2 USING INTEGER PRIMARY KEY (rowid=?)
|
||||
}
|
||||
|
||||
do_eqp_test rtree6.2.3 {
|
||||
SELECT * FROM t1,t2 WHERE k=ii
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN t1 VIRTUAL TABLE INDEX 2:
|
||||
`--SEARCH t2 USING INTEGER PRIMARY KEY (rowid=?)
|
||||
}
|
||||
|
||||
do_eqp_test rtree6.2.4.1 {
|
||||
SELECT * FROM t1,t2 WHERE v=+ii and x1<10 and x2>10
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN t1 VIRTUAL TABLE INDEX 2:C0E1
|
||||
|--BLOOM FILTER ON t2 (v=?)
|
||||
`--SEARCH t2 USING AUTOMATIC COVERING INDEX (v=?)
|
||||
}
|
||||
do_eqp_test rtree6.2.4.2 {
|
||||
SELECT * FROM t1,t2 WHERE v=10 and x1<10 and x2>10
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN t1 VIRTUAL TABLE INDEX 2:C0E1
|
||||
|--BLOOM FILTER ON t2 (v=?)
|
||||
`--SEARCH t2 USING AUTOMATIC PARTIAL COVERING INDEX (v=?)
|
||||
}
|
||||
|
||||
do_eqp_test rtree6.2.5 {
|
||||
SELECT * FROM t1,t2 WHERE k=ii AND x1<v
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN t1 VIRTUAL TABLE INDEX 2:
|
||||
`--SEARCH t2 USING INTEGER PRIMARY KEY (rowid=?)
|
||||
}
|
||||
|
||||
do_execsql_test rtree6-3.1 {
|
||||
CREATE VIRTUAL TABLE t3 USING rtree(id, x1, x2, y1, y2);
|
||||
INSERT INTO t3 VALUES(NULL, 1, 1, 2, 2);
|
||||
SELECT * FROM t3 WHERE
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5;
|
||||
} {1 1.0 1.0 2.0 2.0}
|
||||
|
||||
do_test rtree6.3.2 {
|
||||
rtree_strategy {
|
||||
SELECT * FROM t3 WHERE
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5
|
||||
}
|
||||
} {E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0}
|
||||
do_test rtree6.3.3 {
|
||||
rtree_strategy {
|
||||
SELECT * FROM t3 WHERE
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5
|
||||
}
|
||||
} {E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0}
|
||||
|
||||
do_execsql_test rtree6-3.4 {
|
||||
SELECT * FROM t3 WHERE x1>0.5 AND x1>0.8 AND x1>1.1
|
||||
} {}
|
||||
do_execsql_test rtree6-3.5 {
|
||||
SELECT * FROM t3 WHERE
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>1.1
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(id,x0,x1,y0,y1);
|
||||
}
|
||||
do_execsql_test 4.1 {
|
||||
DELETE FROM t1 WHERE x0>1 AND x1<2 OR y0<92;
|
||||
}
|
||||
|
||||
expand_all_sql db
|
||||
finish_test
|
||||
@@ -0,0 +1,73 @@
|
||||
# 2010 February 16
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Test that nothing goes wrong if an rtree table is created, then the
|
||||
# database page-size is modified. At one point (3.6.22), this was causing
|
||||
# malfunctions.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !rtree||!vacuum {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Like execsql except display output as integer where that can be
|
||||
# done without loss of information.
|
||||
#
|
||||
proc execsql_intout {sql} {
|
||||
set out {}
|
||||
foreach term [execsql $sql] {
|
||||
regsub {\.0$} $term {} term
|
||||
lappend out $term
|
||||
}
|
||||
return $out
|
||||
}
|
||||
|
||||
do_test rtree7-1.1 {
|
||||
execsql {
|
||||
PRAGMA page_size = 1024;
|
||||
CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2, y1, y2);
|
||||
INSERT INTO rt VALUES(1, 1, 2, 3, 4);
|
||||
}
|
||||
} {}
|
||||
do_test rtree7-1.2 {
|
||||
execsql_intout { SELECT * FROM rt }
|
||||
} {1 1 2 3 4}
|
||||
do_test rtree7-1.3 {
|
||||
execsql_intout {
|
||||
PRAGMA page_size = 2048;
|
||||
VACUUM;
|
||||
SELECT * FROM rt;
|
||||
}
|
||||
} {1 1 2 3 4}
|
||||
do_test rtree7-1.4 {
|
||||
for {set i 2} {$i <= 51} {incr i} {
|
||||
execsql { INSERT INTO rt VALUES($i, 1, 2, 3, 4) }
|
||||
}
|
||||
execsql_intout { SELECT sum(x1), sum(x2), sum(y1), sum(y2) FROM rt }
|
||||
} {51 102 153 204}
|
||||
do_test rtree7-1.5 {
|
||||
execsql_intout {
|
||||
PRAGMA page_size = 512;
|
||||
VACUUM;
|
||||
SELECT sum(x1), sum(x2), sum(y1), sum(y2) FROM rt
|
||||
}
|
||||
} {51 102 153 204}
|
||||
|
||||
do_rtree_integrity_test rtree7-1.6 rt
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,209 @@
|
||||
# 2010 February 16
|
||||
#
|
||||
# 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 [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The following block of tests - rtree8-1.* - feature reading and writing
|
||||
# an r-tree table while there exist open cursors on it.
|
||||
#
|
||||
proc populate_t1 {n} {
|
||||
execsql { DELETE FROM t1 }
|
||||
for {set i 1} {$i <= $n} {incr i} {
|
||||
execsql { INSERT INTO t1 VALUES($i, $i, $i+2) }
|
||||
}
|
||||
}
|
||||
|
||||
# A DELETE while a cursor is reading the table.
|
||||
#
|
||||
do_test rtree8-1.1.1 {
|
||||
execsql { PRAGMA page_size = 512 }
|
||||
execsql { CREATE VIRTUAL TABLE t1 USING rtree_i32(id, x1, x2) }
|
||||
populate_t1 5
|
||||
} {}
|
||||
do_test rtree8-1.1.2 {
|
||||
set res [list]
|
||||
set rc [catch {
|
||||
db eval { SELECT * FROM t1 } {
|
||||
lappend res $x1 $x2
|
||||
if {$id==3} { db eval { DELETE FROM t1 WHERE id>3 } }
|
||||
}
|
||||
} msg];
|
||||
lappend rc $msg
|
||||
set rc
|
||||
} {1 {database table is locked}}
|
||||
do_test rtree8-1.1.2b {
|
||||
db eval { SELECT * FROM t1 ORDER BY +id } {
|
||||
if {$id==3} { db eval { DELETE FROM t1 WHERE id>3 } }
|
||||
}
|
||||
db eval {SELECT x1, x2 FROM t1}
|
||||
} {1 3 2 4 3 5}
|
||||
do_test rtree8-1.1.3 {
|
||||
execsql { SELECT * FROM t1 }
|
||||
} {1 1 3 2 2 4 3 3 5}
|
||||
|
||||
# Many SELECTs on the same small table.
|
||||
#
|
||||
proc nested_select {n} {
|
||||
set ::max $n
|
||||
db eval { SELECT * FROM t1 } {
|
||||
if {$id == $n} { nested_select [expr $n+1] }
|
||||
}
|
||||
return $::max
|
||||
}
|
||||
do_test rtree8-1.2.1 { populate_t1 50 } {}
|
||||
do_test rtree8-1.2.2 { nested_select 1 } {51}
|
||||
|
||||
# This test runs many SELECT queries simultaneously against a large
|
||||
# table, causing a collision in the hash-table used to store r-tree
|
||||
# nodes internally.
|
||||
#
|
||||
populate_t1 1500
|
||||
do_rtree_integrity_test rtree8-1.3.0 t1
|
||||
do_execsql_test rtree8-1.3.1 { SELECT max(nodeno) FROM t1_node } {183}
|
||||
do_test rtree8-1.3.2 {
|
||||
set rowids [execsql {SELECT min(rowid) FROM t1_rowid GROUP BY nodeno}]
|
||||
set stmt_list [list]
|
||||
foreach row $rowids {
|
||||
set stmt [sqlite3_prepare db "SELECT * FROM t1 WHERE id = $row" -1 tail]
|
||||
sqlite3_step $stmt
|
||||
lappend res_list [sqlite3_column_int $stmt 0]
|
||||
lappend stmt_list $stmt
|
||||
}
|
||||
} {}
|
||||
do_test rtree8-1.3.3 { set res_list } $rowids
|
||||
do_execsql_test rtree8-1.3.4 { SELECT count(*) FROM t1 } {1500}
|
||||
do_test rtree8-1.3.5 {
|
||||
foreach stmt $stmt_list { sqlite3_finalize $stmt }
|
||||
} {}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The following block of tests - rtree8-2.* - test a couple of database
|
||||
# corruption cases. In this case things are not corrupted at the b-tree
|
||||
# level, but the contents of the various tables used internally by an
|
||||
# r-tree table are inconsistent.
|
||||
#
|
||||
populate_t1 50
|
||||
do_execsql_test rtree8-2.1.1 { SELECT max(nodeno) FROM t1_node } {5}
|
||||
sqlite3_db_config db DEFENSIVE 0
|
||||
do_execsql_test rtree8-2.1.2 { DELETE FROM t1_node } {}
|
||||
for {set i 1} {$i <= 50} {incr i} {
|
||||
do_catchsql_test rtree8-2.1.3.$i {
|
||||
SELECT * FROM t1 WHERE id = $i
|
||||
} {1 {database disk image is malformed}}
|
||||
}
|
||||
do_catchsql_test rtree8-2.1.4 {
|
||||
SELECT * FROM t1
|
||||
} {1 {database disk image is malformed}}
|
||||
do_catchsql_test rtree8-2.1.5 {
|
||||
DELETE FROM t1
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
do_execsql_test rtree8-2.1.6 {
|
||||
DROP TABLE t1;
|
||||
CREATE VIRTUAL TABLE t1 USING rtree_i32(id, x1, x2);
|
||||
} {}
|
||||
|
||||
|
||||
populate_t1 50
|
||||
sqlite3_db_config db DEFENSIVE 0
|
||||
do_execsql_test rtree8-2.2.1 {
|
||||
DELETE FROM t1_parent
|
||||
} {}
|
||||
do_catchsql_test rtree8-2.2.2 {
|
||||
DELETE FROM t1 WHERE id=25
|
||||
} {1 {database disk image is malformed}}
|
||||
do_execsql_test rtree8-2.2.3 {
|
||||
DROP TABLE t1;
|
||||
CREATE VIRTUAL TABLE t1 USING rtree_i32(id, x1, x2);
|
||||
} {}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that trying to use the MATCH operator with the r-tree module does
|
||||
# not confuse it.
|
||||
#
|
||||
populate_t1 10
|
||||
do_catchsql_test rtree8-3.1 {
|
||||
SELECT * FROM t1 WHERE x1 MATCH '1234'
|
||||
} {1 {SQL logic error}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test a couple of invalid arguments to rtreedepth().
|
||||
#
|
||||
do_catchsql_test rtree8-4.1 {
|
||||
SELECT rtreedepth('hello world')
|
||||
} {1 {Invalid argument to rtreedepth()}}
|
||||
do_catchsql_test rtree8-4.2 {
|
||||
SELECT rtreedepth(X'00')
|
||||
} {1 {Invalid argument to rtreedepth()}}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Delete half of a lopsided tree.
|
||||
#
|
||||
do_execsql_test rtree8-5.1 {
|
||||
CREATE VIRTUAL TABLE t2 USING rtree_i32(id, x1, x2)
|
||||
} {}
|
||||
do_test rtree8-5.2 {
|
||||
execsql BEGIN
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
execsql { INSERT INTO t2 VALUES($i, 100, 101) }
|
||||
}
|
||||
for {set i 100} {$i < 200} {incr i} {
|
||||
execsql { INSERT INTO t2 VALUES($i, 1000, 1001) }
|
||||
}
|
||||
execsql COMMIT
|
||||
} {}
|
||||
do_rtree_integrity_test rtree8-5.3 t2
|
||||
do_test rtree8-5.4 {
|
||||
execsql BEGIN
|
||||
for {set i 0} {$i < 200} {incr i} {
|
||||
execsql { DELETE FROM t2 WHERE id = $i }
|
||||
}
|
||||
execsql COMMIT
|
||||
} {}
|
||||
do_rtree_integrity_test rtree8-5.5 t2
|
||||
|
||||
# 2018-05-24
|
||||
# The following script caused an assertion fault and/or segfault
|
||||
# prior to the fix that prevents simultaneous reads and writes on
|
||||
# the same rtree virtual table.
|
||||
#
|
||||
do_test rtree8-6.1 {
|
||||
db close
|
||||
sqlite3 db :memory:
|
||||
db eval {
|
||||
PRAGMA page_size=512;
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(id,x1,x2,y1,y2);
|
||||
WITH RECURSIVE c(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM c WHERE x<49)
|
||||
INSERT INTO t1 SELECT x, x, x+1, x, x+1 FROM c;
|
||||
}
|
||||
set rc [catch {
|
||||
db eval {SELECT id FROM t1} x {
|
||||
db eval {DELETE FROM t1 WHERE id=$x(id)}
|
||||
}
|
||||
} msg]
|
||||
lappend rc $msg
|
||||
} {1 {database table is locked}}
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,129 @@
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file contains tests for the r-tree module. Specifically, it tests
|
||||
# that custom r-tree queries (geometry callbacks) work.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
ifcapable rtree_int_only { 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;
|
||||
} {}
|
||||
|
||||
unset -nocomplain x
|
||||
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_rtree_integrity_test rtree9-2.0 rt
|
||||
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_execsql_test rtree9-3.0 {
|
||||
CREATE VIRTUAL TABLE rt32 USING rtree_i32(id, x1, x2, y1, y2, z1, z2);
|
||||
} {}
|
||||
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 rt32 VALUES($i, $x, $x+1, $y, $y+1, $z, $z+1) }
|
||||
}
|
||||
do_rtree_integrity_test rtree9-3.1 rt32
|
||||
do_execsql_test rtree9-3.2 {
|
||||
SELECT id FROM rt32 WHERE id MATCH cube(3, 3, 3, 1, 1, 1) ORDER BY id;
|
||||
} {222 223 224 232 233 234 242 243 244 322 323 324 332 333 334 342 343 344 422 423 424 432 433 434 442 443 444}
|
||||
do_execsql_test rtree9-3.3 {
|
||||
SELECT id FROM rt32 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-4.1 {
|
||||
SELECT id FROM rt32 WHERE id MATCH cube(5.5, 5.5, 1, 1, 1) ORDER BY id;
|
||||
} {1 {SQL logic error}}
|
||||
for {set x 2} {$x<200} {incr x 2} {
|
||||
do_catchsql_test rtree9-4.2.[expr $x/2] {
|
||||
SELECT id FROM rt WHERE id MATCH randomblob($x)
|
||||
} {1 {SQL logic error}}
|
||||
}
|
||||
do_catchsql_test rtree9-4.3 {
|
||||
SELECT id FROM rt WHERE id MATCH CAST(
|
||||
(cube(5.5, 5.5, 5.5, 1, 1, 1) || X'1234567812345678') AS blob
|
||||
)
|
||||
} {1 {SQL logic error}}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the example 2d "circle" geometry callback.
|
||||
#
|
||||
register_circle_geom db
|
||||
|
||||
do_execsql_test rtree9-5.1 {
|
||||
CREATE VIRTUAL TABLE rt2 USING rtree(id, xmin, xmax, ymin, ymax);
|
||||
|
||||
INSERT INTO rt2 VALUES(1, 1, 2, 1, 2);
|
||||
INSERT INTO rt2 VALUES(2, 1, 2, -2, -1);
|
||||
INSERT INTO rt2 VALUES(3, -2, -1, -2, -1);
|
||||
INSERT INTO rt2 VALUES(4, -2, -1, 1, 2);
|
||||
|
||||
INSERT INTO rt2 VALUES(5, 2, 3, 2, 3);
|
||||
INSERT INTO rt2 VALUES(6, 2, 3, -3, -2);
|
||||
INSERT INTO rt2 VALUES(7, -3, -2, -3, -2);
|
||||
INSERT INTO rt2 VALUES(8, -3, -2, 2, 3);
|
||||
|
||||
INSERT INTO rt2 VALUES(9, 1.8, 3, 1.8, 3);
|
||||
INSERT INTO rt2 VALUES(10, 1.8, 3, -3, -1.8);
|
||||
INSERT INTO rt2 VALUES(11, -3, -1.8, -3, -1.8);
|
||||
INSERT INTO rt2 VALUES(12, -3, -1.8, 1.8, 3);
|
||||
|
||||
INSERT INTO rt2 VALUES(13, -15, 15, 1.8, 2.2);
|
||||
INSERT INTO rt2 VALUES(14, -15, 15, -2.2, -1.8);
|
||||
INSERT INTO rt2 VALUES(15, 1.8, 2.2, -15, 15);
|
||||
INSERT INTO rt2 VALUES(16, -2.2, -1.8, -15, 15);
|
||||
|
||||
INSERT INTO rt2 VALUES(17, -100, 100, -100, 100);
|
||||
} {}
|
||||
|
||||
do_execsql_test rtree9-5.2 {
|
||||
SELECT id FROM rt2 WHERE id MATCH circle(0.0, 0.0, 2.0);
|
||||
} {1 2 3 4 13 14 15 16 17}
|
||||
|
||||
do_execsql_test rtree9-5.3 {
|
||||
UPDATE rt2 SET xmin=xmin+5, ymin=ymin+5, xmax=xmax+5, ymax=ymax+5;
|
||||
SELECT id FROM rt2 WHERE id MATCH circle(5.0, 5.0, 2.0);
|
||||
} {1 2 3 4 13 14 15 16 17}
|
||||
do_rtree_integrity_test rtree9-5.4 rt2
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,262 @@
|
||||
# 2010 September 22
|
||||
#
|
||||
# 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 tests for the r-tree module. Specifically, it tests
|
||||
# that corrupt or inconsistent databases do not cause crashes in the r-tree
|
||||
# module.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
|
||||
proc create_t1 {} {
|
||||
db close
|
||||
forcedelete test.db
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
PRAGMA page_size = 1024;
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(id, x1, x2, y1, y2);
|
||||
}
|
||||
}
|
||||
proc populate_t1 {} {
|
||||
execsql BEGIN
|
||||
for {set i 0} {$i < 500} {incr i} {
|
||||
set x2 [expr $i+5]
|
||||
set y2 [expr $i+5]
|
||||
execsql { INSERT INTO t1 VALUES($i, $i, $x2, $i, $y2) }
|
||||
}
|
||||
execsql COMMIT
|
||||
sqlite3_db_config db DEFENSIVE 0
|
||||
}
|
||||
|
||||
proc truncate_node {nodeno nTrunc} {
|
||||
set blob [db one {SELECT data FROM t1_node WHERE nodeno=$nodeno}]
|
||||
if {$nTrunc<0} {set nTrunc "end-$nTrunc"}
|
||||
set blob [string range $blob 0 $nTrunc]
|
||||
db eval { UPDATE t1_node SET data = $blob WHERE nodeno=$nodeno }
|
||||
}
|
||||
|
||||
proc set_tree_depth {tbl {newvalue ""}} {
|
||||
set blob [db one "SELECT data FROM ${tbl}_node WHERE nodeno=1"]
|
||||
|
||||
if {$newvalue == ""} {
|
||||
binary scan $blob Su oldvalue
|
||||
return $oldvalue
|
||||
}
|
||||
|
||||
set blob [binary format Sua* $newvalue [string range $blob 2 end]]
|
||||
db eval "UPDATE ${tbl}_node SET data = \$blob WHERE nodeno=1"
|
||||
return [set_tree_depth $tbl]
|
||||
}
|
||||
|
||||
proc set_entry_count {tbl nodeno {newvalue ""}} {
|
||||
set blob [db one "SELECT data FROM ${tbl}_node WHERE nodeno=$nodeno"]
|
||||
|
||||
if {$newvalue == ""} {
|
||||
binary scan [string range $blob 2 end] Su oldvalue
|
||||
return $oldvalue
|
||||
}
|
||||
|
||||
set blob [binary format a*Sua* \
|
||||
[string range $blob 0 1] $newvalue [string range $blob 4 end]
|
||||
]
|
||||
db eval "UPDATE ${tbl}_node SET data = \$blob WHERE nodeno=$nodeno"
|
||||
return [set_entry_count $tbl $nodeno]
|
||||
}
|
||||
|
||||
|
||||
proc do_corruption_tests {prefix args} {
|
||||
set testarray [lindex $args end]
|
||||
set errormsg {database disk image is malformed}
|
||||
|
||||
foreach {z value} [lrange $args 0 end-1] {
|
||||
set n [string length $z]
|
||||
if {$n>=2 && [string equal -length $n $z "-error"]} {
|
||||
set errormsg $value
|
||||
}
|
||||
}
|
||||
|
||||
foreach {tn sql} $testarray {
|
||||
do_catchsql_test $prefix.$tn $sql [list 1 $errormsg]
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the libraries response if the %_node table is completely empty
|
||||
# (i.e. the root node is missing), or has been removed from the database
|
||||
# entirely.
|
||||
#
|
||||
create_t1
|
||||
populate_t1
|
||||
do_execsql_test rtreeA-1.0 {
|
||||
DELETE FROM t1_node;
|
||||
} {}
|
||||
|
||||
do_corruption_tests rtreeA-1.1 {
|
||||
1 "SELECT * FROM t1"
|
||||
2 "SELECT * FROM t1 WHERE rowid=5"
|
||||
3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)"
|
||||
4 "SELECT * FROM t1 WHERE x1<10 AND x2>12"
|
||||
}
|
||||
|
||||
do_execsql_test rtreeA-1.1.1 {
|
||||
SELECT rtreecheck('main', 't1')
|
||||
} {{Node 1 missing from database
|
||||
Wrong number of entries in %_rowid table - expected 0, actual 500
|
||||
Wrong number of entries in %_parent table - expected 0, actual 25}}
|
||||
|
||||
do_execsql_test rtreeA-1.2.0 { DROP TABLE t1_node } {}
|
||||
do_corruption_tests rtreeA-1.2 -error "database disk image is malformed" {
|
||||
1 "SELECT * FROM t1"
|
||||
2 "SELECT * FROM t1 WHERE rowid=5"
|
||||
3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)"
|
||||
4 "SELECT * FROM t1 WHERE x1<10 AND x2>12"
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the libraries response if some of the entries in the %_node table
|
||||
# are the wrong size.
|
||||
#
|
||||
create_t1
|
||||
populate_t1
|
||||
do_test rtreeA-2.1.0 {
|
||||
set nodes [db eval {select nodeno FROM t1_node}]
|
||||
foreach {a b c} $nodes { truncate_node $c 200 }
|
||||
} {}
|
||||
do_corruption_tests rtreeA-2.1 {
|
||||
1 "SELECT * FROM t1"
|
||||
2 "SELECT * FROM t1 WHERE rowid=5"
|
||||
3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)"
|
||||
4 "SELECT * FROM t1 WHERE x1<10 AND x2>12"
|
||||
}
|
||||
|
||||
create_t1
|
||||
populate_t1
|
||||
do_test rtreeA-2.2.0 { truncate_node 1 200 } {}
|
||||
do_corruption_tests rtreeA-2.2 {
|
||||
1 "SELECT * FROM t1"
|
||||
2 "SELECT * FROM t1 WHERE +rowid=5"
|
||||
3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)"
|
||||
4 "SELECT * FROM t1 WHERE x1<10 AND x2>12"
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Set the "depth" of the tree stored on the root node incorrectly. Test
|
||||
# that this does not cause any problems.
|
||||
#
|
||||
create_t1
|
||||
populate_t1
|
||||
do_test rtreeA-3.1.0.1 { set_tree_depth t1 } {1}
|
||||
do_test rtreeA-3.1.0.2 { set_tree_depth t1 3 } {3}
|
||||
do_corruption_tests rtreeA-3.1 {
|
||||
1 "SELECT * FROM t1"
|
||||
2 "SELECT * FROM t1 WHERE +rowid=5"
|
||||
3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)"
|
||||
}
|
||||
|
||||
do_execsql_test rtreeA-3.1.0.3 {
|
||||
SELECT rtreecheck('main', 't1')!='ok'
|
||||
} {1}
|
||||
|
||||
do_test rtreeA-3.2.0 { set_tree_depth t1 1000 } {1000}
|
||||
do_corruption_tests rtreeA-3.2 {
|
||||
1 "SELECT * FROM t1"
|
||||
2 "SELECT * FROM t1 WHERE +rowid=5"
|
||||
3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)"
|
||||
}
|
||||
|
||||
create_t1
|
||||
populate_t1
|
||||
do_test rtreeA-3.3.0 {
|
||||
execsql { DELETE FROM t1 WHERE rowid = 0 }
|
||||
set_tree_depth t1 65535
|
||||
} {65535}
|
||||
do_corruption_tests rtreeA-3.3 {
|
||||
1 "SELECT * FROM t1"
|
||||
2 "SELECT * FROM t1 WHERE +rowid=5"
|
||||
3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)"
|
||||
}
|
||||
|
||||
do_execsql_test rtreeA-3.3.3.4 {
|
||||
SELECT rtreecheck('main', 't1')
|
||||
} {{Rtree depth out of range (65535)
|
||||
Wrong number of entries in %_rowid table - expected 0, actual 499
|
||||
Wrong number of entries in %_parent table - expected 0, actual 25}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Set the "number of entries" field on some nodes incorrectly.
|
||||
#
|
||||
create_t1
|
||||
populate_t1
|
||||
do_test rtreeA-4.1.0 {
|
||||
set_entry_count t1 1 4000
|
||||
} {4000}
|
||||
do_corruption_tests rtreeA-4.1 {
|
||||
1 "SELECT * FROM t1"
|
||||
2 "SELECT * FROM t1 WHERE +rowid=5"
|
||||
3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)"
|
||||
4 "SELECT * FROM t1 WHERE x1<10 AND x2>12"
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Remove entries from the %_parent table and check that this does not
|
||||
# cause a crash.
|
||||
#
|
||||
create_t1
|
||||
populate_t1
|
||||
do_execsql_test rtreeA-5.1.0 { DELETE FROM t1_parent } {}
|
||||
do_corruption_tests rtreeA-5.1 {
|
||||
1 "DELETE FROM t1 WHERE +rowid = 5"
|
||||
2 "DELETE FROM t1"
|
||||
}
|
||||
|
||||
do_execsql_test rtreeA-5.2 {
|
||||
SELECT rtreecheck('main', 't1')!='ok'
|
||||
} {1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Add some bad entries to the %_parent table.
|
||||
#
|
||||
create_t1
|
||||
populate_t1
|
||||
do_execsql_test rtreeA-6.1.0 {
|
||||
UPDATE t1_parent set parentnode = parentnode+1
|
||||
} {}
|
||||
do_corruption_tests rtreeA-6.1 {
|
||||
1 "DELETE FROM t1 WHERE rowid = 5"
|
||||
2 "UPDATE t1 SET x1=x1+1, x2=x2+1"
|
||||
}
|
||||
|
||||
do_execsql_test rtreeA-6.2 {
|
||||
SELECT rtreecheck('main', 't1')!='ok'
|
||||
} {1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Truncated blobs in the _node table.
|
||||
#
|
||||
create_t1
|
||||
populate_t1
|
||||
sqlite3 db test.db
|
||||
sqlite3_db_config db DEFENSIVE 0
|
||||
do_execsql_test rtreeA-7.100 {
|
||||
UPDATE t1_node SET data=x'' WHERE rowid=1;
|
||||
} {}
|
||||
do_catchsql_test rtreeA-7.110 {
|
||||
SELECT * FROM t1 WHERE x1>0 AND x1<100 AND x2>0 AND x2<100;
|
||||
} {1 {undersize RTree blobs in "t1_node"}}
|
||||
do_test rtreeA-7.120 {
|
||||
sqlite3_extended_errcode db
|
||||
} {SQLITE_CORRUPT_VTAB}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,50 @@
|
||||
# 2011 March 2
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
# Make sure the rtreenode() testing function can handle entries with
|
||||
# 64-bit rowids.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
|
||||
ifcapable rtree_int_only {
|
||||
do_test rtreeB-1.1-intonly {
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(ii, x0, y0, x1, y1);
|
||||
INSERT INTO t1 VALUES(1073741824, 0.0, 0.0, 100.0, 100.0);
|
||||
INSERT INTO t1 VALUES(2147483646, 0.0, 0.0, 200.0, 200.0);
|
||||
INSERT INTO t1 VALUES(4294967296, 0.0, 0.0, 300.0, 300.0);
|
||||
INSERT INTO t1 VALUES(8589934592, 20.0, 20.0, 150.0, 150.0);
|
||||
INSERT INTO t1 VALUES(9223372036854775807, 150, 150, 400, 400);
|
||||
SELECT rtreenode(2, data) FROM t1_node;
|
||||
}
|
||||
} {{{1073741824 0 0 100 100} {2147483646 0 0 200 200} {4294967296 0 0 300 300} {8589934592 20 20 150 150} {9223372036854775807 150 150 400 400}}}
|
||||
} else {
|
||||
do_test rtreeB-1.1 {
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(ii, x0, y0, x1, y1);
|
||||
INSERT INTO t1 VALUES(1073741824, 0.0, 0.0, 100.0, 100.0);
|
||||
INSERT INTO t1 VALUES(2147483646, 0.0, 0.0, 200.0, 200.0);
|
||||
INSERT INTO t1 VALUES(4294967296, 0.0, 0.0, 300.0, 300.0);
|
||||
INSERT INTO t1 VALUES(8589934592, 20.0, 20.0, 150.0, 150.0);
|
||||
INSERT INTO t1 VALUES(9223372036854775807, 150, 150, 400, 400);
|
||||
SELECT rtreenode(2, data) FROM t1_node;
|
||||
}
|
||||
} {{{1073741824 0 0 100 100} {2147483646 0 0 200 200} {4294967296 0 0 300 300} {8589934592 20 20 150 150} {9223372036854775807 150 150 400 400}}}
|
||||
}
|
||||
|
||||
do_rtree_integrity_test rtreeB-1.2 t1
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,378 @@
|
||||
# 2011 March 2
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
# Make sure the rtreenode() testing function can handle entries with
|
||||
# 64-bit rowids.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
set testprefix rtreeC
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE r_tree USING rtree(id, min_x, max_x, min_y, max_y);
|
||||
CREATE TABLE t(x, y);
|
||||
}
|
||||
|
||||
do_eqp_test 1.1 {
|
||||
SELECT * FROM r_tree, t
|
||||
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN t
|
||||
`--SCAN r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0
|
||||
}
|
||||
|
||||
do_eqp_test 1.2 {
|
||||
SELECT * FROM t, r_tree
|
||||
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN t
|
||||
`--SCAN r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0
|
||||
}
|
||||
|
||||
do_eqp_test 1.3 {
|
||||
SELECT * FROM t, r_tree
|
||||
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND ?<=max_y
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN t
|
||||
`--SCAN r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0
|
||||
}
|
||||
|
||||
do_eqp_test 1.5 {
|
||||
SELECT * FROM t, r_tree
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN r_tree VIRTUAL TABLE INDEX 2:
|
||||
`--SCAN t
|
||||
}
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
INSERT INTO t VALUES(0, 0);
|
||||
INSERT INTO t VALUES(0, 1);
|
||||
INSERT INTO t VALUES(0, 2);
|
||||
INSERT INTO t VALUES(0, 3);
|
||||
INSERT INTO t VALUES(0, 4);
|
||||
INSERT INTO t VALUES(0, 5);
|
||||
INSERT INTO t VALUES(0, 6);
|
||||
INSERT INTO t VALUES(0, 7);
|
||||
INSERT INTO t VALUES(0, 8);
|
||||
INSERT INTO t VALUES(0, 9);
|
||||
|
||||
INSERT INTO t SELECT x+1, y FROM t;
|
||||
INSERT INTO t SELECT x+2, y FROM t;
|
||||
INSERT INTO t SELECT x+4, y FROM t;
|
||||
INSERT INTO r_tree SELECT NULL, x-1, x+1, y-1, y+1 FROM t;
|
||||
ANALYZE;
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_eqp_test 2.1 {
|
||||
SELECT * FROM r_tree, t
|
||||
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN t
|
||||
`--SCAN r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0
|
||||
}
|
||||
|
||||
do_eqp_test 2.2 {
|
||||
SELECT * FROM t, r_tree
|
||||
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN t
|
||||
`--SCAN r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0
|
||||
}
|
||||
|
||||
do_eqp_test 2.3 {
|
||||
SELECT * FROM t, r_tree
|
||||
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND ?<=max_y
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN t
|
||||
`--SCAN r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0
|
||||
}
|
||||
|
||||
do_eqp_test 2.5 {
|
||||
SELECT * FROM t, r_tree
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN r_tree VIRTUAL TABLE INDEX 2:
|
||||
`--SCAN t
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that the special CROSS JOIN handling works with rtree tables.
|
||||
#
|
||||
do_execsql_test 3.1 {
|
||||
CREATE TABLE t1(x);
|
||||
CREATE TABLE t2(y);
|
||||
CREATE VIRTUAL TABLE t3 USING rtree(z, x1,x2, y1,y2);
|
||||
}
|
||||
|
||||
do_eqp_test 3.2.1 { SELECT * FROM t1 CROSS JOIN t2 } {
|
||||
QUERY PLAN
|
||||
|--SCAN t1
|
||||
`--SCAN t2
|
||||
}
|
||||
do_eqp_test 3.2.2 { SELECT * FROM t2 CROSS JOIN t1 } {
|
||||
QUERY PLAN
|
||||
|--SCAN t2
|
||||
`--SCAN t1
|
||||
}
|
||||
|
||||
do_eqp_test 3.3.1 { SELECT * FROM t1 CROSS JOIN t3 } {
|
||||
QUERY PLAN
|
||||
|--SCAN t1
|
||||
`--SCAN t3 VIRTUAL TABLE INDEX 2:
|
||||
}
|
||||
do_eqp_test 3.3.2 { SELECT * FROM t3 CROSS JOIN t1 } {
|
||||
QUERY PLAN
|
||||
|--SCAN t3 VIRTUAL TABLE INDEX 2:
|
||||
`--SCAN t1
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that LEFT JOINs are not reordered if the right-hand-side is
|
||||
# a virtual table.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.1 {
|
||||
CREATE TABLE t1(a);
|
||||
CREATE VIRTUAL TABLE t2 USING rtree(b, x1,x2);
|
||||
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO t1 VALUES(2);
|
||||
|
||||
INSERT INTO t2 VALUES(1, 0.0, 0.1);
|
||||
INSERT INTO t2 VALUES(3, 0.0, 0.1);
|
||||
}
|
||||
|
||||
do_execsql_test 4.2 {
|
||||
SELECT a, b FROM t1 LEFT JOIN t2 ON (+a = +b);
|
||||
} {1 1 2 {}}
|
||||
|
||||
do_execsql_test 4.3 {
|
||||
SELECT b, a FROM t2 LEFT JOIN t1 ON (+a = +b);
|
||||
} {1 1 3 {}}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that the sqlite_stat1 data is used correctly.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 5.1 {
|
||||
CREATE TABLE t1(x INT PRIMARY KEY, y);
|
||||
CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2, +d1);
|
||||
|
||||
INSERT INTO t1(x) VALUES(1);
|
||||
INSERT INTO t1(x) SELECT x+1 FROM t1; -- 2
|
||||
INSERT INTO t1(x) SELECT x+2 FROM t1; -- 4
|
||||
INSERT INTO t1(x) SELECT x+4 FROM t1; -- 8
|
||||
INSERT INTO t1(x) SELECT x+8 FROM t1; -- 16
|
||||
INSERT INTO t1(x) SELECT x+16 FROM t1; -- 32
|
||||
INSERT INTO t1(x) SELECT x+32 FROM t1; -- 64
|
||||
INSERT INTO t1(x) SELECT x+64 FROM t1; -- 128
|
||||
INSERT INTO t1(x) SELECT x+128 FROM t1; -- 256
|
||||
INSERT INTO t1(x) SELECT x+256 FROM t1; -- 512
|
||||
INSERT INTO t1(x) SELECT x+512 FROM t1; --1024
|
||||
|
||||
INSERT INTO rt SELECT x, x, x+1, printf('x%04xy',x) FROM t1 WHERE x<=5;
|
||||
}
|
||||
do_rtree_integrity_test 5.1.1 rt
|
||||
|
||||
# First test a query with no ANALYZE data at all. The outer loop is
|
||||
# real table "t1".
|
||||
#
|
||||
do_eqp_test 5.2 {
|
||||
SELECT * FROM t1, rt WHERE x==id;
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN t1
|
||||
`--SCAN rt VIRTUAL TABLE INDEX 1:
|
||||
}
|
||||
|
||||
# Now create enough ANALYZE data to tell SQLite that virtual table "rt"
|
||||
# contains very few rows. This causes it to move "rt" to the outer loop.
|
||||
#
|
||||
do_execsql_test 5.3 {
|
||||
ANALYZE;
|
||||
DELETE FROM sqlite_stat1 WHERE tbl='t1';
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
do_eqp_test 5.4 {
|
||||
SELECT * FROM t1, rt WHERE x==id;
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN rt VIRTUAL TABLE INDEX 2:
|
||||
`--SEARCH t1 USING INDEX sqlite_autoindex_t1_1 (x=?)
|
||||
}
|
||||
|
||||
# Delete the ANALYZE data. "t1" should be the outer loop again.
|
||||
#
|
||||
do_execsql_test 5.5 { DROP TABLE sqlite_stat1; }
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
do_eqp_test 5.6 {
|
||||
SELECT * FROM t1, rt WHERE x==id;
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN t1
|
||||
`--SCAN rt VIRTUAL TABLE INDEX 1:
|
||||
}
|
||||
|
||||
# This time create and attach a database that contains ANALYZE data for
|
||||
# tables of the same names as those used internally by virtual table
|
||||
# "rt". Check that the rtree module is not fooled into using this data.
|
||||
# Table "t1" should remain the outer loop.
|
||||
#
|
||||
do_test 5.7 {
|
||||
db backup test.db2
|
||||
sqlite3 db2 test.db2
|
||||
db2 eval {
|
||||
ANALYZE;
|
||||
DELETE FROM sqlite_stat1 WHERE tbl='t1';
|
||||
}
|
||||
db2 close
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql { ATTACH 'test.db2' AS aux; }
|
||||
} {}
|
||||
do_eqp_test 5.8 {
|
||||
SELECT * FROM t1, rt WHERE x==id;
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN t1
|
||||
`--SCAN rt VIRTUAL TABLE INDEX 1:
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that having a second connection drop the sqlite_stat1 table
|
||||
# before it is required by rtreeConnect() does not cause problems.
|
||||
#
|
||||
ifcapable rtree {
|
||||
reset_db
|
||||
do_execsql_test 6.1 {
|
||||
CREATE TABLE t1(x);
|
||||
CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO rt VALUES(1,2,3);
|
||||
ANALYZE;
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
do_execsql_test 6.2 { SELECT * FROM t1 } {1}
|
||||
|
||||
do_test 6.3 {
|
||||
sqlite3 db2 test.db
|
||||
db2 eval { DROP TABLE sqlite_stat1 }
|
||||
db2 close
|
||||
execsql { SELECT * FROM rt }
|
||||
} {1 2.0 3.0}
|
||||
db close
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that queries featuring LEFT or CROSS JOINS are handled correctly.
|
||||
# Handled correctly in this case means:
|
||||
#
|
||||
# * Terms with prereqs that appear to the left of a LEFT JOIN against
|
||||
# the virtual table are always available to xBestIndex.
|
||||
#
|
||||
# * Terms with prereqs that appear to the right of a LEFT JOIN against
|
||||
# the virtual table are never available to xBestIndex.
|
||||
#
|
||||
# And the same behaviour for CROSS joins.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
CREATE TABLE xdir(x1);
|
||||
CREATE TABLE ydir(y1);
|
||||
CREATE VIRTUAL TABLE rt USING rtree_i32(id, xmin, xmax, ymin, ymax);
|
||||
|
||||
INSERT INTO xdir VALUES(5);
|
||||
INSERT INTO ydir VALUES(10);
|
||||
|
||||
INSERT INTO rt VALUES(1, 2, 7, 12, 14); -- Not a hit
|
||||
INSERT INTO rt VALUES(2, 2, 7, 8, 12); -- A hit!
|
||||
INSERT INTO rt VALUES(3, 7, 11, 8, 12); -- Not a hit!
|
||||
INSERT INTO rt VALUES(4, 5, 5, 10, 10); -- A hit!
|
||||
|
||||
}
|
||||
|
||||
proc do_eqp_execsql_test {tn sql res1 res2} {
|
||||
do_eqp_test $tn.1 $sql $res1
|
||||
do_execsql_test $tn.2 $sql $res2
|
||||
}
|
||||
|
||||
do_eqp_execsql_test 7.1 {
|
||||
SELECT id FROM xdir, rt, ydir
|
||||
ON (y1 BETWEEN ymin AND ymax)
|
||||
WHERE (x1 BETWEEN xmin AND xmax);
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN xdir
|
||||
|--SCAN ydir
|
||||
`--SCAN rt VIRTUAL TABLE INDEX 2:B2D3B0D1
|
||||
} {
|
||||
2 4
|
||||
}
|
||||
|
||||
do_eqp_execsql_test 7.2 {
|
||||
SELECT * FROM xdir, rt LEFT JOIN ydir
|
||||
ON (y1 BETWEEN ymin AND ymax)
|
||||
WHERE (x1 BETWEEN xmin AND xmax);
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN xdir
|
||||
|--SCAN rt VIRTUAL TABLE INDEX 2:B0D1
|
||||
`--SCAN ydir LEFT-JOIN
|
||||
} {
|
||||
5 1 2 7 12 14 {}
|
||||
5 2 2 7 8 12 10
|
||||
5 4 5 5 10 10 10
|
||||
}
|
||||
|
||||
do_eqp_execsql_test 7.3 {
|
||||
SELECT id FROM xdir, rt CROSS JOIN ydir
|
||||
ON (y1 BETWEEN ymin AND ymax)
|
||||
WHERE (x1 BETWEEN xmin AND xmax);
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN xdir
|
||||
|--SCAN rt VIRTUAL TABLE INDEX 2:B0D1
|
||||
`--SCAN ydir
|
||||
} {
|
||||
2 4
|
||||
}
|
||||
|
||||
do_eqp_execsql_test 7.4 {
|
||||
SELECT id FROM rt, xdir CROSS JOIN ydir
|
||||
ON (y1 BETWEEN ymin AND ymax)
|
||||
WHERE (x1 BETWEEN xmin AND xmax);
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN xdir
|
||||
|--SCAN rt VIRTUAL TABLE INDEX 2:B0D1
|
||||
`--SCAN ydir
|
||||
} {
|
||||
2 4
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,55 @@
|
||||
# 2014 March 11
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Miscellaneous tests for errors in the rtree constructor.
|
||||
#
|
||||
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
ifcapable !rtree||!builtin_test {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
set testprefix rtreeD
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that if an SQLITE_BUSY is encountered within the vtable
|
||||
# constructor, a relevant error message is returned.
|
||||
#
|
||||
do_multiclient_test tn {
|
||||
do_test 1.$tn.1 {
|
||||
sql1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1,2);
|
||||
CREATE VIRTUAL TABLE rt USING rtree(id, minx, maxx, miny, maxy);
|
||||
INSERT INTO rt VALUES(1,2,3,4,5);
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test 1.$tn.2 {
|
||||
sql2 { SELECT * FROM t1; }
|
||||
} {1 2}
|
||||
|
||||
do_test 1.$tn.3 {
|
||||
sql1 { BEGIN EXCLUSIVE; INSERT INTO t1 VALUES(3, 4); }
|
||||
} {}
|
||||
|
||||
do_test 1.$tn.4 {
|
||||
list [catch { sql2 { SELECT * FROM rt } } msg] $msg
|
||||
} {1 {database is locked}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,143 @@
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file contains tests for the r-tree module. Specifically, it tests
|
||||
# that new-style custom r-tree queries (geometry callbacks) work.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
ifcapable rtree_int_only { finish_test; return }
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the example 2d "circle" geometry callback.
|
||||
#
|
||||
register_circle_geom db
|
||||
|
||||
do_execsql_test rtreeE-1.0.0 {
|
||||
PRAGMA page_size=512;
|
||||
CREATE VIRTUAL TABLE rt1 USING rtree(id,x0,x1,y0,y1);
|
||||
|
||||
/* A tight pattern of small boxes near 0,0 */
|
||||
WITH RECURSIVE
|
||||
x(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM x WHERE x<4),
|
||||
y(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM y WHERE y<4)
|
||||
INSERT INTO rt1 SELECT x+5*y, x, x+2, y, y+2 FROM x, y;
|
||||
|
||||
/* A looser pattern of small boxes near 100, 0 */
|
||||
WITH RECURSIVE
|
||||
x(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM x WHERE x<4),
|
||||
y(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM y WHERE y<4)
|
||||
INSERT INTO rt1 SELECT 100+x+5*y, x*3+100, x*3+102, y*3, y*3+2 FROM x, y;
|
||||
|
||||
/* A looser pattern of larger boxes near 0, 200 */
|
||||
WITH RECURSIVE
|
||||
x(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM x WHERE x<4),
|
||||
y(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM y WHERE y<4)
|
||||
INSERT INTO rt1 SELECT 200+x+5*y, x*7, x*7+15, y*7+200, y*7+215 FROM x, y;
|
||||
} {}
|
||||
do_rtree_integrity_test rtreeE-1.0.1 rt1
|
||||
|
||||
# Queries against each of the three clusters */
|
||||
do_execsql_test rtreeE-1.1 {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle(0.0, 0.0, 50.0, 3) ORDER BY id;
|
||||
} {0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24}
|
||||
do_execsql_test rtreeE-1.1x {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle('x:0 y:0 r:50.0 e:3') ORDER BY id;
|
||||
} {0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24}
|
||||
do_execsql_test rtreeE-1.2 {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle(100.0, 0.0, 50.0, 3) ORDER BY id;
|
||||
} {100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124}
|
||||
do_execsql_test rtreeE-1.3 {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle(0.0, 200.0, 50.0, 3) ORDER BY id;
|
||||
} {200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224}
|
||||
|
||||
# The Qcircle geometry function gives a lower score to larger leaf-nodes.
|
||||
# This causes the 200s to sort before the 100s and the 0s to sort before
|
||||
# last.
|
||||
#
|
||||
do_execsql_test rtreeE-1.4 {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle('r:1000 e:3') AND id%100==0
|
||||
} {200 100 0}
|
||||
|
||||
# Exclude odd rowids on a depth-first search
|
||||
do_execsql_test rtreeE-1.5 {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle('r:1000 e:4') ORDER BY +id
|
||||
} {0 2 4 6 8 10 12 14 16 18 20 22 24 100 102 104 106 108 110 112 114 116 118 120 122 124 200 202 204 206 208 210 212 214 216 218 220 222 224}
|
||||
|
||||
# Exclude odd rowids on a breadth-first search.
|
||||
do_execsql_test rtreeE-1.6 {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle(0,0,1000,5) ORDER BY +id
|
||||
} {0 2 4 6 8 10 12 14 16 18 20 22 24 100 102 104 106 108 110 112 114 116 118 120 122 124 200 202 204 206 208 210 212 214 216 218 220 222 224}
|
||||
|
||||
# Test that rtree prefers MATCH to lookup-by-rowid.
|
||||
#
|
||||
do_execsql_test rtreeE-1.7 {
|
||||
SELECT id FROM rt1 WHERE id=18 AND id MATCH Qcircle(0,0,1000,5)
|
||||
} {18}
|
||||
|
||||
|
||||
# Construct a large 2-D RTree with thousands of random entries.
|
||||
#
|
||||
do_test rtreeE-2.1 {
|
||||
db eval {
|
||||
CREATE TABLE t2(id,x0,x1,y0,y1);
|
||||
CREATE VIRTUAL TABLE rt2 USING rtree(id,x0,x1,y0,y1);
|
||||
BEGIN;
|
||||
}
|
||||
expr srand(0)
|
||||
for {set i 1} {$i<=10000} {incr i} {
|
||||
set dx [expr {int(rand()*40)+1}]
|
||||
set dy [expr {int(rand()*40)+1}]
|
||||
set x0 [expr {int(rand()*(10000 - $dx))}]
|
||||
set x1 [expr {$x0+$dx}]
|
||||
set y0 [expr {int(rand()*(10000 - $dy))}]
|
||||
set y1 [expr {$y0+$dy}]
|
||||
set id [expr {$i+10000}]
|
||||
db eval {INSERT INTO t2 VALUES($id,$x0,$x1,$y0,$y1)}
|
||||
}
|
||||
db eval {
|
||||
INSERT INTO rt2 SELECT * FROM t2;
|
||||
COMMIT;
|
||||
}
|
||||
} {}
|
||||
do_rtree_integrity_test rtreeE-2.1.1 rt2
|
||||
|
||||
for {set i 1} {$i<=200} {incr i} {
|
||||
set dx [expr {int(rand()*100)}]
|
||||
set dy [expr {int(rand()*100)}]
|
||||
set x0 [expr {int(rand()*(10000 - $dx))}]
|
||||
set x1 [expr {$x0+$dx}]
|
||||
set y0 [expr {int(rand()*(10000 - $dy))}]
|
||||
set y1 [expr {$y0+$dy}]
|
||||
set ans [db eval {SELECT id FROM t2 WHERE x1>=$x0 AND x0<=$x1 AND y1>=$y0 AND y0<=$y1 ORDER BY id}]
|
||||
do_execsql_test rtreeE-2.2.$i {
|
||||
SELECT id FROM rt2 WHERE id MATCH breadthfirstsearch($x0,$x1,$y0,$y1) ORDER BY id
|
||||
} $ans
|
||||
}
|
||||
|
||||
# Run query that have very deep priority queues
|
||||
#
|
||||
set ans [db eval {SELECT id FROM t2 WHERE x1>=0 AND x0<=5000 AND y1>=0 AND y0<=5000 ORDER BY id}]
|
||||
do_execsql_test rtreeE-2.3 {
|
||||
SELECT id FROM rt2 WHERE id MATCH breadthfirstsearch(0,5000,0,5000) ORDER BY id
|
||||
} $ans
|
||||
set ans [db eval {SELECT id FROM t2 WHERE x1>=0 AND x0<=10000 AND y1>=0 AND y0<=10000 ORDER BY id}]
|
||||
do_execsql_test rtreeE-2.4 {
|
||||
SELECT id FROM rt2 WHERE id MATCH breadthfirstsearch(0,10000,0,10000) ORDER BY id
|
||||
} $ans
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,84 @@
|
||||
# 2014-08-21
|
||||
#
|
||||
# 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 tests for the r-tree module.
|
||||
#
|
||||
# This file contains test cases for the ticket
|
||||
# [369d57fb8e5ccdff06f197a37147a88f9de95cda] (2014-08-21)
|
||||
#
|
||||
# The following SQL causes an assertion fault while running
|
||||
# sqlite3_prepare() on the DELETE statement:
|
||||
#
|
||||
# CREATE TABLE t1(x);
|
||||
# CREATE TABLE t2(y);
|
||||
# CREATE VIRTUAL TABLE t3 USING rtree(a,b,c);
|
||||
# CREATE TRIGGER t2del AFTER DELETE ON t2 WHEN (SELECT 1 from t1) BEGIN
|
||||
# DELETE FROM t3 WHERE a=old.y;
|
||||
# END;
|
||||
# DELETE FROM t2 WHERE y=1;
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
|
||||
do_execsql_test rtreeF-1.1 {
|
||||
CREATE TABLE t1(x);
|
||||
CREATE TABLE t2(y);
|
||||
CREATE VIRTUAL TABLE t3 USING rtree(a,b,c);
|
||||
CREATE TRIGGER t2dwl AFTER DELETE ON t2 WHEN (SELECT 1 from t1) BEGIN
|
||||
DELETE FROM t3 WHERE a=old.y;
|
||||
END;
|
||||
|
||||
INSERT INTO t1(x) VALUES(999);
|
||||
INSERT INTO t2(y) VALUES(1),(2),(3),(4),(5);
|
||||
INSERT INTO t3(a,b,c) VALUES(1,2,3),(2,3,4),(3,4,5),(4,5,6),(5,6,7);
|
||||
|
||||
SELECT a FROM t3 ORDER BY a;
|
||||
SELECT '|';
|
||||
SELECT y FROM t2 ORDER BY y;
|
||||
} {1 2 3 4 5 | 1 2 3 4 5}
|
||||
do_execsql_test rtreeF-1.2 {
|
||||
DELETE FROM t2 WHERE y=3;
|
||||
|
||||
SELECT a FROM t3 ORDER BY a;
|
||||
SELECT '|';
|
||||
SELECT y FROM t2 ORDER BY y;
|
||||
} {1 2 4 5 | 1 2 4 5}
|
||||
do_execsql_test rtreeF-1.3 {
|
||||
DELETE FROM t1;
|
||||
DELETE FROM t2 WHERE y=5;
|
||||
|
||||
SELECT a FROM t3 ORDER BY a;
|
||||
SELECT '|';
|
||||
SELECT y FROM t2 ORDER BY y;
|
||||
} {1 2 4 5 | 1 2 4}
|
||||
do_execsql_test rtreeF-1.4 {
|
||||
INSERT INTO t1 DEFAULT VALUES;
|
||||
DELETE FROM t2 WHERE y=5;
|
||||
|
||||
SELECT a FROM t3 ORDER BY a;
|
||||
SELECT '|';
|
||||
SELECT y FROM t2 ORDER BY y;
|
||||
} {1 2 4 5 | 1 2 4}
|
||||
do_execsql_test rtreeF-1.5 {
|
||||
DELETE FROM t2 WHERE y=2;
|
||||
|
||||
SELECT a FROM t3 ORDER BY a;
|
||||
SELECT '|';
|
||||
SELECT y FROM t2 ORDER BY y;
|
||||
} {1 4 5 | 1 4}
|
||||
|
||||
do_rtree_integrity_test rtreeF-1.6 t3
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,69 @@
|
||||
# 2016-05-32
|
||||
#
|
||||
# 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 tests for the r-tree module.
|
||||
#
|
||||
# Verify that no invalid SQL is run during initialization
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
test_sqlite3_log [list lappend ::log]
|
||||
set ::log [list]
|
||||
sqlite3 db test.db
|
||||
|
||||
|
||||
set ::log {}
|
||||
do_execsql_test rtreeG-1.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(id,x0,x1,y0,y1);
|
||||
} {}
|
||||
do_test rtreeG-1.1log {
|
||||
set ::log
|
||||
} {}
|
||||
|
||||
do_execsql_test rtreeG-1.2 {
|
||||
INSERT INTO t1 VALUES(1,10,15,5,23),(2,20,21,5,23),(3,10,15,20,30);
|
||||
SELECT id from t1 WHERE x0>8 AND x1<16 AND y0>2 AND y1<25;
|
||||
} {1}
|
||||
do_rtree_integrity_test rtreeG-1.2.integrity t1
|
||||
do_test rtreeG-1.2log {
|
||||
set ::log
|
||||
} {}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
do_execsql_test rtreeG-1.3 {
|
||||
SELECT id from t1 WHERE x0>8 AND x1<16 AND y0>2 AND y1<25;
|
||||
} {1}
|
||||
do_test rtreeG-1.3log {
|
||||
set ::log
|
||||
} {}
|
||||
|
||||
do_execsql_test rtreeG-1.4 {
|
||||
DROP TABLE t1;
|
||||
} {}
|
||||
do_test rtreeG-1.4log {
|
||||
set ::log
|
||||
} {}
|
||||
|
||||
expand_all_sql db
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
test_sqlite3_log
|
||||
sqlite3_initialize
|
||||
sqlite3 db test.db
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,103 @@
|
||||
# 2018-05-16
|
||||
#
|
||||
# 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 tests for the r-tree module, specifically the
|
||||
# auxiliary column mechanism.
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
|
||||
do_execsql_test rtreeH-100 {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(id,x0,x1,y0,y1,+label,+other);
|
||||
INSERT INTO t1(x0,x1,y0,y1,label) VALUES
|
||||
(0,10,0,10,'lower-left corner'),
|
||||
(0,10,90,100,'upper-left corner'),
|
||||
(90,100,0,10,'lower-right corner'),
|
||||
(90,100,90,100,'upper-right corner'),
|
||||
(40,60,40,60,'center'),
|
||||
(0,5,0,100,'left edge'),
|
||||
(95,100,0,100,'right edge'),
|
||||
(0,100,0,5,'bottom edge'),
|
||||
(0,100,95,100,'top edge'),
|
||||
(0,100,0,100,'the whole thing'),
|
||||
(0,50,0,100,'left half'),
|
||||
(51,100,0,100,'right half'),
|
||||
(0,100,0,50,'bottom half'),
|
||||
(0,100,51,100,'top half');
|
||||
} {}
|
||||
do_execsql_test rtreeH-101 {
|
||||
SELECT * FROM t1_rowid ORDER BY rowid
|
||||
} {1 1 {lower-left corner} {} 2 1 {upper-left corner} {} 3 1 {lower-right corner} {} 4 1 {upper-right corner} {} 5 1 center {} 6 1 {left edge} {} 7 1 {right edge} {} 8 1 {bottom edge} {} 9 1 {top edge} {} 10 1 {the whole thing} {} 11 1 {left half} {} 12 1 {right half} {} 13 1 {bottom half} {} 14 1 {top half} {}}
|
||||
|
||||
do_execsql_test rtreeH-102 {
|
||||
SELECT * FROM t1 WHERE rowid=5;
|
||||
} {5 40.0 60.0 40.0 60.0 center {}}
|
||||
do_execsql_test rtreeH-102b {
|
||||
SELECT * FROM t1 WHERE rowid=5.0;
|
||||
} {5 40.0 60.0 40.0 60.0 center {}}
|
||||
do_execsql_test rtreeH-102c {
|
||||
SELECT * FROM t1 WHERE rowid='5';
|
||||
} {5 40.0 60.0 40.0 60.0 center {}}
|
||||
do_execsql_test rtreeH-102d {
|
||||
SELECT * FROM t1 WHERE rowid='0005';
|
||||
} {5 40.0 60.0 40.0 60.0 center {}}
|
||||
do_execsql_test rtreeH-102e {
|
||||
SELECT * FROM t1 WHERE rowid='+5.0e+0';
|
||||
} {5 40.0 60.0 40.0 60.0 center {}}
|
||||
do_execsql_test rtreeH-103 {
|
||||
SELECT * FROM t1 WHERE label='center';
|
||||
} {5 40.0 60.0 40.0 60.0 center {}}
|
||||
|
||||
do_execsql_test rtreeH-104 {
|
||||
SELECT * FROM t1 WHERE rowid='+5.0e+0x';
|
||||
} {}
|
||||
do_execsql_test rtreeH-105 {
|
||||
SELECT * FROM t1 WHERE rowid=x'35';
|
||||
} {}
|
||||
do_execsql_test rtreeH-106 {
|
||||
SELECT * FROM t1 WHERE rowid=null;
|
||||
} {}
|
||||
|
||||
|
||||
do_rtree_integrity_test rtreeH-110 t1
|
||||
|
||||
do_execsql_test rtreeH-120 {
|
||||
SELECT label FROM t1 WHERE x1<=50 ORDER BY id
|
||||
} {{lower-left corner} {upper-left corner} {left edge} {left half}}
|
||||
do_execsql_test rtreeH-121 {
|
||||
SELECT label FROM t1 WHERE x1<=50 AND label NOT LIKE '%corner%' ORDER BY id
|
||||
} {{left edge} {left half}}
|
||||
|
||||
do_execsql_test rtreeH-200 {
|
||||
WITH RECURSIVE
|
||||
c1(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM c1 WHERE x<99),
|
||||
c2(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM c2 WHERE y<99)
|
||||
INSERT INTO t1(id, x0,x1,y0,y1,label)
|
||||
SELECT 1000+x+y*100, x, x+1, y, y+1, printf('box-%d,%d',x,y) FROM c1, c2;
|
||||
} {}
|
||||
|
||||
do_execsql_test rtreeH-210 {
|
||||
SELECT label FROM t1 WHERE x0>=48 AND x1<=50 AND y0>=48 AND y1<=50
|
||||
ORDER BY id;
|
||||
} {box-48,48 box-49,48 box-48,49 box-49,49}
|
||||
|
||||
do_execsql_test rtreeH-300 {
|
||||
UPDATE t1 SET label='x'||label
|
||||
WHERE x0>=49 AND x1<=50 AND y0>=49 AND y1<=50;
|
||||
SELECT label FROM t1 WHERE x0>=48 AND x1<=50 AND y0>=48 AND y1<=50
|
||||
ORDER BY id;
|
||||
} {box-48,48 box-49,48 box-48,49 xbox-49,49}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,74 @@
|
||||
# 2019-12-05
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
# Additional test cases
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
|
||||
# The following is a test of rowvalue handling on virtual tables that
|
||||
# deal with inequalities and that set the OMIT flag on terms of the
|
||||
# WHERE clause. This is not specific to rtree. We just use rtree because
|
||||
# it is a convenient test platform since it has all the right
|
||||
# characteristics.
|
||||
#
|
||||
do_execsql_test rtreeI-1.10 {
|
||||
CREATE TABLE t1(a);
|
||||
INSERT INTO t1 VALUES(2);
|
||||
CREATE VIRTUAL TABLE t2 USING rtree(id,x0,x1);
|
||||
INSERT INTO t2(id,x0,x1) VALUES(1,2,3);
|
||||
} {}
|
||||
do_execsql_test rtreeI-1.20 {
|
||||
SELECT 123 FROM t1, t2 WHERE (a,0)>(x0,0);
|
||||
} {}
|
||||
do_execsql_test rtreeI-1.21 {
|
||||
SELECT 123 FROM t1, t2 WHERE (a,0.1)>(x0,0);
|
||||
} {123}
|
||||
do_execsql_test rtreeI-1.22 {
|
||||
SELECT 123 FROM t1, t2 WHERE (a,0)>=(x0,0);
|
||||
} {123}
|
||||
do_execsql_test rtreeI-1.23 {
|
||||
SELECT 123 FROM t1, t2 WHERE (a,0)<=(x0,0);
|
||||
} {123}
|
||||
do_execsql_test rtreeI-1.24 {
|
||||
SELECT 123 FROM t1, t2 WHERE (a,0)<(x0,0);
|
||||
} {}
|
||||
do_execsql_test rtreeI-1.30 {
|
||||
SELECT 123 FROM t1, t2 WHERE (x0,0)<(a,0);
|
||||
} {}
|
||||
do_execsql_test rtreeI-1.31 {
|
||||
SELECT 123 FROM t1, t2 WHERE (x0,0)<(a,0.1);
|
||||
} {123}
|
||||
do_execsql_test rtreeI-1.40 {
|
||||
SELECT 123 FROM t1, t2 WHERE x1<5 AND id<99 AND (a,0)>(x0,0);
|
||||
} {}
|
||||
do_execsql_test rtreeI-1.41 {
|
||||
SELECT 123 FROM t1, t2 WHERE x1<5 AND id<99 AND (a,0.5)>(x0,0);
|
||||
} {123}
|
||||
do_execsql_test rtreeI-1.42 {
|
||||
SELECT 123 FROM t1, t2 WHERE x1<5 AND id<99 AND (a,0)>=(x0,0);
|
||||
} {123}
|
||||
do_execsql_test rtreeI-1.43 {
|
||||
SELECT 123 FROM t1, t2 WHERE x1<5 AND id<99 AND (a,0)<(x0,0);
|
||||
} {}
|
||||
do_execsql_test rtreeI-1.50 {
|
||||
SELECT 123 FROM t1, t2 WHERE 5>x1 AND 99>id AND (x0,0)<(a,0);
|
||||
} {}
|
||||
do_execsql_test rtreeI-1.51 {
|
||||
SELECT 123 FROM t1, t2 WHERE 5>x1 AND 99>id AND (x0,0)<(a,0.5);
|
||||
} {123}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,273 @@
|
||||
# 2024-02-03
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# ROLLBACK in the middle of an RTREE query
|
||||
#
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
set testprefix rtreeJ
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(id, x1, x2);
|
||||
INSERT INTO t1 VALUES(1, 1, 1), (2, 2, 2);
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT * FROM t1
|
||||
} {1 1.0 1.0 2 2.0 2.0}
|
||||
|
||||
# If a ROLLBACK occurs that backs out changes to the RTREE, then
|
||||
# all pending queries to the RTREE are aborted.
|
||||
#
|
||||
do_test 1.2 {
|
||||
db eval {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(3, 3, 3);
|
||||
INSERT INTO t1 VALUES(4, 4, 4);
|
||||
}
|
||||
set rc [catch {
|
||||
db eval { SELECT * FROM t1 } {
|
||||
if {$id==1} {
|
||||
db eval { ROLLBACK }
|
||||
}
|
||||
lappend res $id $x1 $x2
|
||||
}
|
||||
} msg]
|
||||
list $rc $msg
|
||||
} {1 {query aborted}}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
SELECT * FROM t1;
|
||||
} {1 1.0 1.0 2 2.0 2.0}
|
||||
|
||||
# A COMMIT of changes to the RTREE does not affect pending queries
|
||||
#
|
||||
do_test 1.4 {
|
||||
set res {}
|
||||
db eval {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(5, 5, 5);
|
||||
INSERT INTO t1 VALUES(6, 6, 6);
|
||||
}
|
||||
db eval { SELECT * FROM t1 } {
|
||||
if {$id==1} {
|
||||
db eval { COMMIT }
|
||||
}
|
||||
lappend res $id $x1 $x2
|
||||
}
|
||||
set res
|
||||
} {1 1.0 1.0 2 2.0 2.0 5 5.0 5.0 6 6.0 6.0}
|
||||
|
||||
do_execsql_test 1.5 {
|
||||
SELECT * FROM t1;
|
||||
} {1 1.0 1.0 2 2.0 2.0 5 5.0 5.0 6 6.0 6.0}
|
||||
|
||||
do_execsql_test 1.6 {
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES(1,1,1),(2,2,2),(3,3,3),(4,4,4);
|
||||
CREATE TABLE t2(x);
|
||||
SELECT * FROM t1;
|
||||
} {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0 4 4.0 4.0}
|
||||
|
||||
# A rollback that does not affect the rtree table because
|
||||
# the rtree table has not been written to does not cause
|
||||
# a query abort.
|
||||
#
|
||||
do_test 1.7 {
|
||||
set res {}
|
||||
db eval {
|
||||
BEGIN;
|
||||
INSERT INTO t2(x) VALUES(12345);
|
||||
}
|
||||
db eval { SELECT * FROM t1 } {
|
||||
if {$id==1} {
|
||||
db eval { ROLLBACK }
|
||||
}
|
||||
lappend res $id $x1 $x2
|
||||
}
|
||||
set res
|
||||
} {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0 4 4.0 4.0}
|
||||
|
||||
# ROLLBACK TO that affects the RTREE does cause a query abort.
|
||||
#
|
||||
do_test 1.8 {
|
||||
db eval {
|
||||
DELETE FROM t1 WHERE rowid>1;
|
||||
BEGIN;
|
||||
DELETE FROM t2;
|
||||
INSERT INTO t2(x) VALUES(23456);
|
||||
SAVEPOINT 'one';
|
||||
INSERT INTO t1 VALUES(2,2,2),(3,3,3);
|
||||
}
|
||||
set rc [catch {
|
||||
db eval { SELECT * FROM t1 } {
|
||||
if {$id==1} {
|
||||
db eval { ROLLBACK TO 'one'; }
|
||||
}
|
||||
lappend res $id $x1 $x2
|
||||
}
|
||||
} msg]
|
||||
list $rc $msg
|
||||
} {1 {query aborted}}
|
||||
|
||||
do_execsql_test 1.9 {
|
||||
COMMIT;
|
||||
SELECT * FROM t1;
|
||||
} {1 1.0 1.0}
|
||||
|
||||
# ROLLBACK TO that does not affect the RTREE does not cause a query abort.
|
||||
#
|
||||
do_execsql_test 1.10 {
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES(1,1,1),(2,2,2),(3,3,3);
|
||||
BEGIN;
|
||||
DELETE FROM t2;
|
||||
INSERT INTO t2(x) VALUES(34567);
|
||||
SAVEPOINT 'one';
|
||||
INSERT INTO t2(x) VALUES('a string');
|
||||
SELECT * FROM t1;
|
||||
} {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0}
|
||||
do_test 1.11 {
|
||||
set rc [catch {
|
||||
set res {}
|
||||
db eval { SELECT * FROM t1 } {
|
||||
if {$id==2} {
|
||||
# db eval { ROLLBACK TO 'one'; }
|
||||
}
|
||||
lappend res $id $x1 $x2
|
||||
}
|
||||
set res
|
||||
} msg]
|
||||
list $rc $msg
|
||||
} {0 {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0}}
|
||||
|
||||
do_execsql_test 1.12 {
|
||||
COMMIT;
|
||||
SELECT * FROM t1;
|
||||
} {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0}
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(id, x1, x2);
|
||||
INSERT INTO t1 VALUES(1, 1, 1), (2, 2, 2);
|
||||
CREATE TABLE t2(x);
|
||||
} {}
|
||||
|
||||
do_test 2.1 {
|
||||
db eval {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(3, 3, 3);
|
||||
PRAGMA writable_schema = RESET;
|
||||
}
|
||||
|
||||
set rc [catch {
|
||||
db eval { SELECT x1, x2 FROM t1 } {
|
||||
if {$x1==1} {
|
||||
db eval { ROLLBACK }
|
||||
}
|
||||
lappend res $x1 $x2
|
||||
}
|
||||
} msg]
|
||||
list $rc $msg
|
||||
} {1 {query aborted}}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
CREATE TABLE bak_node(nodeno, data);
|
||||
CREATE TABLE bak_parent(nodeno, parentnode);
|
||||
CREATE TABLE bak_rowid(rowid, nodeno);
|
||||
}
|
||||
proc save_t1 {} {
|
||||
db eval {
|
||||
DELETE FROM bak_node;
|
||||
DELETE FROM bak_parent;
|
||||
DELETE FROM bak_rowid;
|
||||
INSERT INTO bak_node SELECT * FROM t1_node;
|
||||
INSERT INTO bak_parent SELECT * FROM t1_parent;
|
||||
INSERT INTO bak_rowid SELECT * FROM t1_rowid;
|
||||
}
|
||||
}
|
||||
proc restore_t1 {} {
|
||||
db eval {
|
||||
DELETE FROM t1_node;
|
||||
DELETE FROM t1_parent;
|
||||
DELETE FROM t1_rowid;
|
||||
INSERT INTO t1_node SELECT * FROM bak_node;
|
||||
INSERT INTO t1_parent SELECT * FROM bak_parent;
|
||||
INSERT INTO t1_rowid SELECT * FROM bak_rowid;
|
||||
}
|
||||
}
|
||||
|
||||
do_test 2.3 {
|
||||
save_t1
|
||||
db eval {
|
||||
INSERT INTO t1 VALUES(3, 3, 3);
|
||||
}
|
||||
set rc [catch {
|
||||
db eval { SELECT rowid, x1, x2 FROM t1 } {
|
||||
if {$x1==1} {
|
||||
restore_t1
|
||||
}
|
||||
lappend res $x1 $x2
|
||||
}
|
||||
} msg]
|
||||
list $rc $msg
|
||||
} {1 {query aborted}}
|
||||
do_execsql_test 2.4 {
|
||||
SELECT * FROM t1
|
||||
} {1 1.0 1.0 2 2.0 2.0}
|
||||
|
||||
do_test 2.5 {
|
||||
save_t1
|
||||
db eval {
|
||||
INSERT INTO t1 VALUES(3, 3, 3);
|
||||
}
|
||||
set rc [catch {
|
||||
db eval { SELECT x1 FROM t1 } {
|
||||
if {$x1==1} {
|
||||
restore_t1
|
||||
}
|
||||
lappend res $x1 $x2
|
||||
}
|
||||
} msg]
|
||||
list $rc $msg
|
||||
} {1 {query aborted}}
|
||||
do_execsql_test 2.6 {
|
||||
SELECT * FROM t1
|
||||
} {1 1.0 1.0 2 2.0 2.0}
|
||||
|
||||
do_test 2.7 {
|
||||
save_t1
|
||||
db eval {
|
||||
INSERT INTO t1 VALUES(3, 3, 3);
|
||||
}
|
||||
set ::res [list]
|
||||
set rc [catch {
|
||||
db eval { SELECT 'abc' FROM t1 } {
|
||||
if {$::res==[list]} {
|
||||
restore_t1
|
||||
set ::bDone 1
|
||||
}
|
||||
lappend res abc
|
||||
}
|
||||
} msg]
|
||||
set res
|
||||
} {abc abc abc}
|
||||
do_execsql_test 2.6 {
|
||||
SELECT * FROM t1
|
||||
} {1 1.0 1.0 2 2.0 2.0}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,74 @@
|
||||
|
||||
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"
|
||||
@@ -0,0 +1,197 @@
|
||||
# 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.
|
||||
#
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# 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
|
||||
}
|
||||
|
||||
proc do_rtree_integrity_test {tn tbl} {
|
||||
uplevel [list do_execsql_test $tn.1 "SELECT rtreecheck('$tbl')" ok]
|
||||
uplevel [list do_execsql_test $tn.2 "PRAGMA integrity_check" ok]
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
# 2017 August 17
|
||||
#
|
||||
# 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
|
||||
set testprefix rtreecheck
|
||||
|
||||
ifcapable !rtree {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
proc swap_int32 {blob i0 i1} {
|
||||
binary scan $blob I* L
|
||||
|
||||
set a [lindex $L $i0]
|
||||
set b [lindex $L $i1]
|
||||
|
||||
lset L $i0 $b
|
||||
lset L $i1 $a
|
||||
|
||||
binary format I* $L
|
||||
}
|
||||
|
||||
proc set_int32 {blob idx val} {
|
||||
binary scan $blob I* L
|
||||
lset L $idx $val
|
||||
binary format I* $L
|
||||
}
|
||||
|
||||
do_catchsql_test 1.0 {
|
||||
SELECT rtreecheck();
|
||||
} {1 {wrong number of arguments to function rtreecheck()}}
|
||||
|
||||
do_catchsql_test 1.1 {
|
||||
SELECT rtreecheck(0,0,0);
|
||||
} {1 {wrong number of arguments to function rtreecheck()}}
|
||||
|
||||
|
||||
proc setup_simple_db {{module rtree}} {
|
||||
reset_db
|
||||
db func swap_int32 swap_int32
|
||||
execsql "
|
||||
CREATE VIRTUAL TABLE r1 USING $module (id, x1, x2, y1, y2);
|
||||
INSERT INTO r1 VALUES(1, 5, 5, 5, 5); -- 3
|
||||
INSERT INTO r1 VALUES(2, 6, 6, 6, 6); -- 9
|
||||
INSERT INTO r1 VALUES(3, 7, 7, 7, 7); -- 15
|
||||
INSERT INTO r1 VALUES(4, 8, 8, 8, 8); -- 21
|
||||
INSERT INTO r1 VALUES(5, 9, 9, 9, 9); -- 27
|
||||
"
|
||||
sqlite3_db_config db DEFENSIVE 0
|
||||
}
|
||||
|
||||
setup_simple_db
|
||||
do_execsql_test 2.1 {
|
||||
SELECT rtreecheck('r1')
|
||||
} {ok}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
UPDATE r1_node SET data = swap_int32(data, 3, 9);
|
||||
UPDATE r1_node SET data = swap_int32(data, 23, 29);
|
||||
}
|
||||
|
||||
do_execsql_test 2.3 {
|
||||
SELECT rtreecheck('r1')
|
||||
} {{Dimension 0 of cell 0 on node 1 is corrupt
|
||||
Dimension 1 of cell 3 on node 1 is corrupt}}
|
||||
do_execsql_test 2.3b {
|
||||
PRAGMA integrity_check;
|
||||
} {{In RTree main.r1:
|
||||
Dimension 0 of cell 0 on node 1 is corrupt
|
||||
Dimension 1 of cell 3 on node 1 is corrupt}}
|
||||
|
||||
setup_simple_db
|
||||
do_execsql_test 2.4 {
|
||||
DELETE FROM r1_rowid WHERE rowid = 3;
|
||||
SELECT rtreecheck('r1')
|
||||
} {{Mapping (3 -> 1) missing from %_rowid table
|
||||
Wrong number of entries in %_rowid table - expected 5, actual 4}}
|
||||
do_execsql_test 2.4b {
|
||||
PRAGMA integrity_check
|
||||
} {{In RTree main.r1:
|
||||
Mapping (3 -> 1) missing from %_rowid table
|
||||
Wrong number of entries in %_rowid table - expected 5, actual 4}}
|
||||
|
||||
setup_simple_db
|
||||
do_execsql_test 2.5 {
|
||||
UPDATE r1_rowid SET nodeno=2 WHERE rowid=3;
|
||||
SELECT rtreecheck('r1')
|
||||
} {{Found (3 -> 2) in %_rowid table, expected (3 -> 1)}}
|
||||
do_execsql_test 2.5b {
|
||||
PRAGMA integrity_check
|
||||
} {{In RTree main.r1:
|
||||
Found (3 -> 2) in %_rowid table, expected (3 -> 1)}}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE r1 USING rtree_i32(id, x1, x2);
|
||||
INSERT INTO r1 VALUES(1, 0x7FFFFFFF*-1, 0x7FFFFFFF);
|
||||
INSERT INTO r1 VALUES(2, 0x7FFFFFFF*-1, 5);
|
||||
INSERT INTO r1 VALUES(3, -5, 5);
|
||||
INSERT INTO r1 VALUES(4, 5, 0x11111111);
|
||||
INSERT INTO r1 VALUES(5, 5, 0x00800000);
|
||||
INSERT INTO r1 VALUES(6, 5, 0x00008000);
|
||||
INSERT INTO r1 VALUES(7, 5, 0x00000080);
|
||||
INSERT INTO r1 VALUES(8, 5, 0x40490fdb);
|
||||
INSERT INTO r1 VALUES(9, 0x7f800000, 0x7f900000);
|
||||
SELECT rtreecheck('r1');
|
||||
PRAGMA integrity_check;
|
||||
} {ok ok}
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
CREATE VIRTUAL TABLE r2 USING rtree_i32(id, x1, x2);
|
||||
INSERT INTO r2 VALUES(2, -1*(1<<31), -1*(1<<31)+5);
|
||||
SELECT rtreecheck('r2');
|
||||
PRAGMA integrity_check;
|
||||
} {ok ok}
|
||||
|
||||
sqlite3_db_config db DEFENSIVE 0
|
||||
do_execsql_test 3.2 {
|
||||
BEGIN;
|
||||
UPDATE r2_node SET data = X'123456';
|
||||
SELECT rtreecheck('r2')!='ok';
|
||||
} {1}
|
||||
|
||||
do_execsql_test 3.3 {
|
||||
ROLLBACK;
|
||||
UPDATE r2_node SET data = X'00001234';
|
||||
SELECT rtreecheck('r2')!='ok';
|
||||
} {1}
|
||||
do_execsql_test 3.4 {
|
||||
PRAGMA integrity_check;
|
||||
} {{In RTree main.r2:
|
||||
Node 1 is too small for cell count of 4660 (4 bytes)
|
||||
Wrong number of entries in %_rowid table - expected 0, actual 1}}
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE notanrtree(i);
|
||||
SELECT rtreecheck('notanrtree');
|
||||
} {{Schema corrupt or not an rtree}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
db func set_int32 set_int32
|
||||
do_execsql_test 5.0 {
|
||||
CREATE VIRTUAL TABLE r3 USING rtree_i32(id, x1, x2, y1, y2);
|
||||
WITH x(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<1000
|
||||
)
|
||||
INSERT INTO r3 SELECT i, i, i, i, i FROM x;
|
||||
}
|
||||
sqlite3_db_config db DEFENSIVE 0
|
||||
do_execsql_test 5.1 {
|
||||
BEGIN;
|
||||
UPDATE r3_node SET data = set_int32(data, 3, 5000);
|
||||
UPDATE r3_node SET data = set_int32(data, 4, 5000);
|
||||
SELECT rtreecheck('r3')=='ok'
|
||||
} 0
|
||||
do_execsql_test 5.2 {
|
||||
ROLLBACK;
|
||||
BEGIN;
|
||||
UPDATE r3_node SET data = set_int32(data, 3, 0);
|
||||
UPDATE r3_node SET data = set_int32(data, 4, 0);
|
||||
SELECT rtreecheck('r3')=='ok'
|
||||
} 0
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# dbsqlfuzz 4a1399d39bf9feccbf6b290da51d3b30103a4bf6
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
PRAGMA encoding = 'utf16';
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(id, x, y);
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
if {[permutation]=="inmemory_journal"} {
|
||||
# This doesn't hit an SQLITE_LOCKED in this permutation as the schema
|
||||
# has already been loaded.
|
||||
do_catchsql_test 6.1.inmemory_journal {
|
||||
SELECT ( 'elvis' IN(SELECT rtreecheck('t1')) ) FROM (SELECT 1) GROUP BY 1;
|
||||
} {0 0}
|
||||
} else {
|
||||
do_catchsql_test 6.1 {
|
||||
SELECT ( 'elvis' IN(SELECT rtreecheck('t1')) ) FROM (SELECT 1) GROUP BY 1;
|
||||
} {0 0}
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,66 @@
|
||||
# 2018 Dec 22
|
||||
#
|
||||
# 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 implements regression tests for SQLite library. The
|
||||
# focus of this script is testing the FTS5 module.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix rtreecirc
|
||||
|
||||
ifcapable !rtree {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2, y1, y2);
|
||||
SELECT name FROM sqlite_master ORDER BY 1;
|
||||
} {
|
||||
rt rt_node rt_parent rt_rowid
|
||||
}
|
||||
db_save_and_close
|
||||
|
||||
foreach {tn schema sql} {
|
||||
1 {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON rt_node BEGIN
|
||||
SELECT * FROM rt;
|
||||
END;
|
||||
} {
|
||||
INSERT INTO rt VALUES(1, 2, 3, 4, 5);
|
||||
}
|
||||
2 {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON rt_parent BEGIN
|
||||
SELECT * FROM rt;
|
||||
END;
|
||||
} {
|
||||
INSERT INTO rt VALUES(1, 2, 3, 4, 5);
|
||||
}
|
||||
3 {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON rt_rowid BEGIN
|
||||
SELECT * FROM rt;
|
||||
END;
|
||||
} {
|
||||
INSERT INTO rt VALUES(1, 2, 3, 4, 5);
|
||||
}
|
||||
} {
|
||||
db_restore_and_reopen
|
||||
do_execsql_test 1.1.$tn.1 $schema
|
||||
do_catchsql_test 1.1.$tn.2 $sql {1 {no such table: main.rt}}
|
||||
db close
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# 2017 August 17
|
||||
#
|
||||
# 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. Specifically,
|
||||
# the impact of an SQLITE_SCHEMA error within the rtree module xConnect
|
||||
# callback.
|
||||
#
|
||||
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
set testprefix rtreeconnect
|
||||
|
||||
ifcapable !rtree {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE r1 USING rtree(id, x1, x2, y1, y2);
|
||||
CREATE TABLE t1(id, x1, x2, y1, y2);
|
||||
CREATE TABLE log(l);
|
||||
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO r1 VALUES(new.id, new.x1, new.x2, new.y1, new.y2);
|
||||
INSERT INTO log VALUES('r1: ' || new.id);
|
||||
END;
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
sqlite3 db2 test.db
|
||||
|
||||
do_test 1.1 {
|
||||
db eval { INSERT INTO log VALUES('startup'); }
|
||||
db2 eval { CREATE TABLE newtable(x,y); }
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
INSERT INTO t1 VALUES(1, 2, 3, 4, 5);
|
||||
}
|
||||
|
||||
db2 close
|
||||
db close
|
||||
|
||||
finish_test
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,346 @@
|
||||
# 2021 September 13
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix rtreedoc2
|
||||
|
||||
ifcapable !rtree {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#-------------------------------------------------------------------------
|
||||
# Section 6 of documentation.
|
||||
#-------------------------------------------------------------------------
|
||||
#-------------------------------------------------------------------------
|
||||
set testprefix rtreedoc2-1
|
||||
|
||||
# EVIDENCE-OF: R-35254-48865 A call to one of the above APIs creates a
|
||||
# new SQL function named by the second parameter (zQueryFunc or zGeom).
|
||||
#
|
||||
# [register_circle_geom db] registers new geometry callback "Qcircle"
|
||||
# and legacy implementation "circle". Test that these do actually appear.
|
||||
#
|
||||
do_execsql_test 1.1.0 {
|
||||
SELECT * FROM pragma_function_list WHERE name IN('circle', 'qcircle');
|
||||
} {
|
||||
}
|
||||
do_test 1.1 {
|
||||
register_circle_geom db
|
||||
} {SQLITE_OK}
|
||||
do_execsql_test 1.1.2 {
|
||||
SELECT * FROM pragma_function_list WHERE name = 'circle' AND enc='utf8';
|
||||
} {
|
||||
circle 0 s utf8 -1 0
|
||||
}
|
||||
do_execsql_test 1.1.3 {
|
||||
SELECT * FROM pragma_function_list WHERE name = 'qcircle' AND enc='utf8';
|
||||
} {
|
||||
qcircle 0 s utf8 -1 0
|
||||
}
|
||||
|
||||
do_execsql_test 1.2.0 { SELECT circle(1, 2, 3); } {{}}
|
||||
do_execsql_test 1.2.1 { SELECT qcircle(1, 2, 3); } {{}}
|
||||
|
||||
# EVIDENCE-OF: R-61427-46983
|
||||
do_execsql_test 1.3.0 {
|
||||
CREATE VIRTUAL TABLE demo_index USING rtree(id, x1,x2, y1,y2);
|
||||
INSERT INTO demo_index VALUES(10, 45,45, 24,24);
|
||||
INSERT INTO demo_index VALUES(20, 50,50, 28,28);
|
||||
INSERT INTO demo_index VALUES(30, 43,43, 22,22);
|
||||
}
|
||||
do_execsql_test 1.3.1 {
|
||||
SELECT id FROM demo_index WHERE id MATCH circle(45.3, 22.9, 5.0)
|
||||
} {10 30}
|
||||
|
||||
# EVIDENCE-OF: R-16907-50223 The SQL syntax for custom queries is the
|
||||
# same regardless of which interface, sqlite3_rtree_geometry_callback()
|
||||
# or sqlite3_rtree_query_callback(), is used to register the SQL
|
||||
# function.
|
||||
do_execsql_test 1.3.2 {
|
||||
SELECT id FROM demo_index WHERE id MATCH qcircle(45.3, 22.9, 5.0, 1)
|
||||
} {10 30}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-59634-51678 When that SQL function appears on the
|
||||
# right-hand side of the MATCH operator and the left-hand side of the
|
||||
# MATCH operator is any column in the R*Tree virtual table, then the
|
||||
# callback defined by the third argument (xQueryFunc or xGeom) is
|
||||
# invoked to determine if a particular object or subtree overlaps the
|
||||
# desired region.
|
||||
proc box_geom {args} {
|
||||
lappend ::box_geom [concat [lindex $args 0] [lrange $args 2 end-1]]
|
||||
return ""
|
||||
}
|
||||
register_box_geom db box_geom
|
||||
set box_geom [list]
|
||||
do_execsql_test 1.3.2 {
|
||||
SELECT id FROM demo_index WHERE id MATCH box(43,46, 21,25);
|
||||
} {10 30}
|
||||
do_test 1.3.3 {
|
||||
set ::box_geom
|
||||
} [list {*}{
|
||||
{box {43.0 46.0 21.0 25.0} {45.0 45.0 24.0 24.0}}
|
||||
{box {43.0 46.0 21.0 25.0} {50.0 50.0 28.0 28.0}}
|
||||
{box {43.0 46.0 21.0 25.0} {43.0 43.0 22.0 22.0}}
|
||||
}]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#-------------------------------------------------------------------------
|
||||
# Section 6 of documentation.
|
||||
#-------------------------------------------------------------------------
|
||||
#-------------------------------------------------------------------------
|
||||
set testprefix rtreedoc2-2
|
||||
|
||||
# EVIDENCE-OF: R-02424-24769 The second argument is the number of
|
||||
# coordinates in each r-tree entry, and is always the same for any given
|
||||
# R*Tree.
|
||||
#
|
||||
# EVIDENCE-OF: R-40260-16838 The number of coordinates is 2 for a
|
||||
# 1-dimensional R*Tree, 4 for a 2-dimensional R*Tree, 6 for a
|
||||
# 3-dimensional R*Tree, and so forth.
|
||||
#
|
||||
# The second argument referred to above is the length of the list passed
|
||||
# as the 3rd parameter to the Tcl script.
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE rt1 USING rtree(id, x1,x2);
|
||||
CREATE VIRTUAL TABLE rt2 USING rtree(id, x1,x2, y1,y2);
|
||||
CREATE VIRTUAL TABLE rt3 USING rtree(id, x1,x2, y1,y2, z1,z2);
|
||||
|
||||
INSERT INTO rt1 DEFAULT VALUES;
|
||||
INSERT INTO rt2 DEFAULT VALUES;
|
||||
INSERT INTO rt3 DEFAULT VALUES;
|
||||
}
|
||||
foreach {tn tbl nCoord} {
|
||||
1 rt1 2
|
||||
2 rt2 4
|
||||
3 rt3 6
|
||||
} {
|
||||
set ::box_geom [list]
|
||||
do_catchsql_test 1.$tn.1 "
|
||||
SELECT id FROM $tbl WHERE id MATCH box();
|
||||
" {1 {SQL logic error}}
|
||||
|
||||
do_test 1.$tn.2 {
|
||||
llength [lindex $::box_geom 0 2]
|
||||
} $nCoord
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-28051-48608 If xGeom returns anything other than
|
||||
# SQLITE_OK, then the r-tree query will abort with an error.
|
||||
proc box_geom {args} {
|
||||
error "an error!"
|
||||
}
|
||||
do_catchsql_test 2.0 {
|
||||
SELECT * FROM rt2 WHERE id MATCH box(22,23, 24,25);
|
||||
} {1 {SQL logic error}}
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
INSERT INTO rt1 VALUES(10, 10, 10);
|
||||
INSERT INTO rt1 VALUES(11, 11, 11);
|
||||
INSERT INTO rt1 VALUES(12, 12, 12);
|
||||
INSERT INTO rt1 VALUES(13, 13, 13);
|
||||
INSERT INTO rt1 VALUES(14, 14, 14);
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-53759-57366 The exact same sqlite3_rtree_geometry
|
||||
# structure is used for every callback for same MATCH operator in the
|
||||
# same query.
|
||||
proc box_geom {args} {
|
||||
lappend ::ptr_list [lindex $args 4]
|
||||
return 0
|
||||
}
|
||||
set ::ptr_list [list]
|
||||
do_execsql_test 3.1 {
|
||||
SELECT * FROM rt1 WHERE id MATCH box(1,1);
|
||||
}
|
||||
do_test 3.2 {
|
||||
set val [lindex $::ptr_list 0]
|
||||
foreach p $::ptr_list {
|
||||
if {$p!=$val} {error "pointer mismatch"}
|
||||
}
|
||||
} {}
|
||||
|
||||
# EVIDENCE-OF: R-60247-35692 The contents of the sqlite3_rtree_geometry
|
||||
# structure are initialized by SQLite but are not subsequently modified.
|
||||
proc box_geom {args} {
|
||||
lappend ::box_geom [concat [lindex $args 0] [lrange $args 2 end-1]]
|
||||
if {[llength $::box_geom]==3} {
|
||||
return "zero"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
set ::box_geom [list]
|
||||
do_catchsql_test 3.2 {
|
||||
SELECT * FROM rt1 WHERE id MATCH box(1,1);
|
||||
} {1 {SQL logic error}}
|
||||
do_test 3.3 {
|
||||
set ::box_geom
|
||||
} [list {*}{
|
||||
{box {1.0 1.0} {0.0 0.0}}
|
||||
{box {1.0 1.0} {10.0 10.0}}
|
||||
{box {1.0 1.0} {11.0 11.0}}
|
||||
{box 0.0 {12.0 12.0}}
|
||||
}]
|
||||
|
||||
# EVIDENCE-OF: R-31246-29731 The pContext member of the
|
||||
# sqlite3_rtree_geometry structure is always set to a copy of the
|
||||
# pContext argument passed to sqlite3_rtree_geometry_callback() when the
|
||||
# callback is registered.
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE r1 USING rtree(id, minX,maxX, minY,maxY);
|
||||
WITH s(i) AS (
|
||||
VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<120
|
||||
)
|
||||
INSERT INTO r1 SELECT i,i,i+1, 200,201 FROM s;
|
||||
}
|
||||
set ctx [register_box_geom db box_geom]
|
||||
set ::box_geom [list]
|
||||
proc box_geom {args} {
|
||||
lappend ::box_geom [lindex $args 1]
|
||||
return ""
|
||||
}
|
||||
do_execsql_test 4.1 {
|
||||
SELECT count(*) FROM r1 WHERE id MATCH box(0,150,199,201)
|
||||
} 120
|
||||
do_test 4.2 {
|
||||
foreach g $::box_geom {
|
||||
if {$g!=$ctx} {error "pointer mismatch"}
|
||||
}
|
||||
} {}
|
||||
|
||||
# EVIDENCE-OF: R-09904-19077 The aParam[] array (size nParam) contains
|
||||
# the parameter values passed to the SQL function on the right-hand side
|
||||
# of the MATCH operator.
|
||||
proc box_geom {args} {
|
||||
set ::box_geom [lindex $args 2]
|
||||
}
|
||||
foreach {tn q vals} {
|
||||
1 "SELECT count(*) FROM r1 WHERE id MATCH box(1,2,3)" {1.0 2.0 3.0}
|
||||
2 "SELECT count(*) FROM r1 WHERE id MATCH box(10001)" {10001.0}
|
||||
3 "SELECT count(*) FROM r1 WHERE id MATCH box(-10001)" {-10001.0}
|
||||
} {
|
||||
do_catchsql_test 5.$tn.1 $q {1 {SQL logic error}}
|
||||
do_test 5.$tn.2 { set ::box_geom } $vals
|
||||
}
|
||||
|
||||
do_execsql_test 5.0 {
|
||||
CREATE VIRTUAL TABLE myrtree USING rtree(id, x1,x2);
|
||||
INSERT INTO myrtree VALUES(1, 1, 1);
|
||||
INSERT INTO myrtree VALUES(2, 2, 2);
|
||||
INSERT INTO myrtree VALUES(3, 3, 3);
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-44448-00687 The pUser and xDelUser members of the
|
||||
# sqlite3_rtree_geometry structure are initially set to NULL.
|
||||
set ::box_geom_calls 0
|
||||
proc box_geom {args} {
|
||||
incr ::box_geom_calls
|
||||
return user_is_zero
|
||||
}
|
||||
do_execsql_test 5.1.1 {
|
||||
SELECT * FROM myrtree WHERE id MATCH box(4, 5);
|
||||
}
|
||||
do_test 5.1.2 { set ::box_geom_calls } 3
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-55837-00155 The pUser variable may be set by the
|
||||
# callback implementation to any arbitrary value that may be useful to
|
||||
# subsequent invocations of the callback within the same query (for
|
||||
# example, a pointer to a complicated data structure used to test for
|
||||
# region intersection).
|
||||
#
|
||||
# EVIDENCE-OF: R-34745-08839 If the xDelUser variable is set to a
|
||||
# non-NULL value, then after the query has finished running SQLite
|
||||
# automatically invokes it with the value of the pUser variable as the
|
||||
# only argument.
|
||||
#
|
||||
set ::box_geom_calls 0
|
||||
proc box_geom {args} {
|
||||
incr ::box_geom_calls
|
||||
switch -- $::box_geom_calls {
|
||||
1 {
|
||||
return user_is_zero
|
||||
}
|
||||
2 {
|
||||
return [list user box_geom_finalizer]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
proc box_geom_finalizer {} {
|
||||
set ::box_geom_finalizer "::box_geom_calls is $::box_geom_calls"
|
||||
}
|
||||
do_execsql_test 5.1.1 {
|
||||
SELECT * FROM myrtree WHERE id MATCH box(4, 5);
|
||||
}
|
||||
do_test 5.1.2 { set ::box_geom_calls } 3
|
||||
do_test 5.1.3 {
|
||||
set ::box_geom_finalizer
|
||||
} {::box_geom_calls is 3}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-28176-28813 The xGeom callback always does a
|
||||
# depth-first search of the r-tree.
|
||||
#
|
||||
# For a breadth first search, final test case would return "B L" only.
|
||||
#
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE xyz USING rtree(x, x1,x2, y1,y2);
|
||||
WITH s(i) AS (
|
||||
VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<15
|
||||
)
|
||||
INSERT INTO xyz SELECT NULL, one.i,one.i+1, two.i,two.i+1 FROM s one, s two;
|
||||
}
|
||||
do_execsql_test 6.1 {
|
||||
SELECT count(*) FROM xyz_node
|
||||
} {10}
|
||||
proc box_geom {args} {
|
||||
set coords [lindex $args 3]
|
||||
set area [expr {
|
||||
([lindex $coords 1]-[lindex $coords 0]) *
|
||||
([lindex $coords 3]-[lindex $coords 2])
|
||||
}]
|
||||
if {$area==1} {
|
||||
lappend ::box_geom_calls L
|
||||
} else {
|
||||
lappend ::box_geom_calls B
|
||||
}
|
||||
}
|
||||
set ::box_geom_calls [list]
|
||||
do_execsql_test 6.2 {
|
||||
SELECT count(*) FROM xyz WHERE x MATCH box(0,20,0,20)
|
||||
} 225
|
||||
do_test 6.3 {
|
||||
set prev ""
|
||||
set box_calls [list]
|
||||
foreach c $::box_geom_calls {
|
||||
if {$c!=$prev} {
|
||||
lappend ::box_calls $c
|
||||
set prev $c
|
||||
}
|
||||
}
|
||||
set ::box_calls
|
||||
} {B L B L B L B L B L B L B L B L B L}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
# 2021 September 13
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix rtreedoc3
|
||||
|
||||
ifcapable !rtree {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
# This command assumes that the argument is a node blob for a 2 dimensional
|
||||
# i32 r-tree table. It decodes and returns a list of cells from the node
|
||||
# as a list. Each cell is itself a list of the following form:
|
||||
#
|
||||
# {$rowid $minX $maxX $minY $maxY}
|
||||
#
|
||||
# For internal (non-leaf) nodes, the rowid is replaced by the child node
|
||||
# number.
|
||||
#
|
||||
proc rnode_cells {aData} {
|
||||
set nDim 2
|
||||
|
||||
set nData [string length $aData]
|
||||
set nBytePerCell [expr (8 + 2*$nDim*4)]
|
||||
binary scan [string range $aData 2 3] S nCell
|
||||
|
||||
set res [list]
|
||||
for {set i 0} {$i < $nCell} {incr i} {
|
||||
set iOff [expr $i*$nBytePerCell+4]
|
||||
set cell [string range $aData $iOff [expr $iOff+$nBytePerCell-1]]
|
||||
binary scan $cell WIIII rowid x1 x2 y1 y2
|
||||
lappend res [list $rowid $x1 $x2 $y1 $y2]
|
||||
}
|
||||
|
||||
return $res
|
||||
}
|
||||
|
||||
# Interpret the first two bytes of the blob passed as the only parameter
|
||||
# as a 16-bit big-endian integer and return the value. If this blob is
|
||||
# the root node of an r-tree, this value is the height of the tree.
|
||||
#
|
||||
proc rnode_height {aData} {
|
||||
binary scan [string range $aData 0 1] S nHeight
|
||||
return $nHeight
|
||||
}
|
||||
|
||||
# Return a blob containing node iNode of r-tree "rt".
|
||||
#
|
||||
proc rt_node_get {iNode} {
|
||||
db one { SELECT data FROM rt_node WHERE nodeno=$iNode }
|
||||
}
|
||||
|
||||
|
||||
#--------------------------------------------------------------
|
||||
# API:
|
||||
#
|
||||
# pq_init
|
||||
# Initialize a new test.
|
||||
#
|
||||
# pq_test_callback
|
||||
# Invoked each time the xQueryCallback function is called. This Tcl
|
||||
# command checks that the arguments that SQLite passed to xQueryCallback
|
||||
# are as expected.
|
||||
#
|
||||
# pq_test_row
|
||||
# Invoked each time a row is returned. Checks that the row returned
|
||||
# was predicted by the documentation.
|
||||
#
|
||||
# DATA STRUCTURE:
|
||||
# The priority queue is stored as a Tcl list. The order of elements in
|
||||
# the list is unimportant - it is just used as a set here. Each element
|
||||
# in the priority queue is itself a list. The first element is the
|
||||
# priority value for the entry (a real). Following this is a list of
|
||||
# key-value pairs that make up the entries fields.
|
||||
#
|
||||
proc pq_init {} {
|
||||
global Q
|
||||
set Q(pri_queue) [list]
|
||||
|
||||
set nHeight [rnode_height [rt_node_get 1]]
|
||||
set nCell [llength [rnode_cells [rt_node_get 1]]]
|
||||
|
||||
# EVIDENCE-OF: R-54708-13595 An R*Tree query is initialized by making
|
||||
# the root node the only entry in a priority queue sorted by rScore.
|
||||
lappend Q(pri_queue) [list 0.0 [list \
|
||||
iLevel [expr $nHeight+1] \
|
||||
iChild 1 \
|
||||
iCurrent 0 \
|
||||
]]
|
||||
}
|
||||
|
||||
proc pq_extract {} {
|
||||
global Q
|
||||
if {[llength $Q(pri_queue)]==0} {
|
||||
error "priority queue is empty!"
|
||||
}
|
||||
|
||||
# Find the priority queue entry with the lowest score.
|
||||
#
|
||||
# EVIDENCE-OF: R-47257-47871 Smaller scores are processed first.
|
||||
set iBest 0
|
||||
set rBestScore [lindex $Q(pri_queue) 0 0]
|
||||
for {set ii 1} {$ii < [llength $Q(pri_queue)]} {incr ii} {
|
||||
set rScore [expr [lindex $Q(pri_queue) $ii 0]]
|
||||
if {$rScore<$rBestScore} {
|
||||
set rBestScore $rScore
|
||||
set iBest $ii
|
||||
}
|
||||
}
|
||||
|
||||
# Extract the entry with the lowest score from the queue and return it.
|
||||
#
|
||||
# EVIDENCE-OF: R-60002-49798 The query proceeds by extracting the entry
|
||||
# from the priority queue that has the lowest score.
|
||||
set ret [lindex $Q(pri_queue) $iBest]
|
||||
set Q(pri_queue) [lreplace $Q(pri_queue) $iBest $iBest]
|
||||
|
||||
return $ret
|
||||
}
|
||||
|
||||
proc pq_new_entry {rScore iLevel cell} {
|
||||
global Q
|
||||
|
||||
set rowid_name "iChild"
|
||||
if {$iLevel==0} { set rowid_name "iRowid" }
|
||||
|
||||
set kv [list]
|
||||
lappend kv aCoord [lrange $cell 1 end]
|
||||
lappend kv iLevel $iLevel
|
||||
|
||||
if {$iLevel==0} {
|
||||
lappend kv iRowid [lindex $cell 0]
|
||||
} else {
|
||||
lappend kv iChild [lindex $cell 0]
|
||||
lappend kv iCurrent 0
|
||||
}
|
||||
|
||||
lappend Q(pri_queue) [list $rScore $kv]
|
||||
}
|
||||
|
||||
proc pq_test_callback {L res} {
|
||||
#pq_debug "pq_test_callback $L -> $res"
|
||||
global Q
|
||||
|
||||
array set G $L ;# "Got" - as in stuff passed to xQuery
|
||||
|
||||
# EVIDENCE-OF: R-65127-42665 If the extracted priority queue entry is a
|
||||
# node (a subtree), then the next child of that node is passed to the
|
||||
# xQueryFunc callback.
|
||||
#
|
||||
# If it had been a leaf, the row should have been returned, instead of
|
||||
# xQueryCallback being called on a child - as is happening here.
|
||||
foreach {rParentScore parent} [pq_extract] {}
|
||||
array set P $parent ;# "Parent" - as in parent of expected cell
|
||||
if {$P(iLevel)==0} { error "query callback mismatch (1)" }
|
||||
set child_node [rnode_cells [rt_node_get $P(iChild)]]
|
||||
set expected_cell [lindex $child_node $P(iCurrent)]
|
||||
set expected_coords [lrange $expected_cell 1 end]
|
||||
if {[llength $expected_coords] != [llength $G(aCoord)]} {
|
||||
puts [array get P]
|
||||
puts "E: $expected_coords G: $G(aCoord)"
|
||||
error "coordinate mismatch in query callback (1)"
|
||||
}
|
||||
foreach a [lrange $expected_cell 1 end] b $G(aCoord) {
|
||||
if {$a!=$b} { error "coordinate mismatch in query callback (2)" }
|
||||
}
|
||||
|
||||
# Check level is as expected
|
||||
#
|
||||
if {$G(iLevel) != $P(iLevel)-1} {
|
||||
error "iLevel mismatch in query callback (1)"
|
||||
}
|
||||
|
||||
# Unless the callback returned NOT_WITHIN, add the entry to the priority
|
||||
# queue.
|
||||
#
|
||||
# EVIDENCE-OF: R-28754-35153 Those subelements for which the xQueryFunc
|
||||
# callback sets eWithin to PARTLY_WITHIN or FULLY_WITHIN are added to
|
||||
# the priority queue using the score supplied by the callback.
|
||||
#
|
||||
# EVIDENCE-OF: R-08681-45277 Subelements that return NOT_WITHIN are
|
||||
# discarded.
|
||||
set r [lindex $res 0]
|
||||
set rScore [lindex $res 1]
|
||||
if {$r!="fully" && $r!="partly" && $r!="not"} {
|
||||
error "unknown result: $r - expected \"fully\", \"partly\" or \"not\""
|
||||
}
|
||||
if {$r!="not"} {
|
||||
pq_new_entry $rScore [expr $P(iLevel)-1] $expected_cell
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-07194-63805 If the node has more children then it is
|
||||
# returned to the priority queue. Otherwise it is discarded.
|
||||
incr P(iCurrent)
|
||||
if {$P(iCurrent)<[llength $child_node]} {
|
||||
lappend Q(pri_queue) [list $rParentScore [array get P]]
|
||||
}
|
||||
}
|
||||
|
||||
proc pq_test_result {id x1 x2 y1 y2} {
|
||||
#pq_debug "pq_test_result $id $x1 $x2 $y1 $y2"
|
||||
foreach {rScore next} [pq_extract] {}
|
||||
|
||||
# The extracted entry must be a leaf (otherwise, xQueryCallback would
|
||||
# have been called on the extracted entries children instead of just
|
||||
# returning the data).
|
||||
#
|
||||
# EVIDENCE-OF: R-13214-54017 If that entry is a leaf (meaning that it is
|
||||
# an actual R*Tree entry and not a subtree) then that entry is returned
|
||||
# as one row of the query result.
|
||||
array set N $next
|
||||
if {$N(iLevel)!=0} { error "result row mismatch (1)" }
|
||||
|
||||
if {$x1!=[lindex $N(aCoord) 0] || $x2!=[lindex $N(aCoord) 1]
|
||||
|| $y1!=[lindex $N(aCoord) 2] || $y2!=[lindex $N(aCoord) 3]
|
||||
} {
|
||||
if {$N(iLevel)!=0} { error "result row mismatch (2)" }
|
||||
}
|
||||
|
||||
if {$id!=$N(iRowid)} { error "result row mismatch (3)" }
|
||||
}
|
||||
|
||||
proc pq_done {} {
|
||||
global Q
|
||||
# EVIDENCE-OF: R-57438-45968 The query runs until the priority queue is
|
||||
# empty.
|
||||
if {[llength $Q(pri_queue)]>0} {
|
||||
error "priority queue is not empty!"
|
||||
}
|
||||
}
|
||||
|
||||
proc pq_debug {caption} {
|
||||
global Q
|
||||
|
||||
puts "**** $caption ****"
|
||||
set i 0
|
||||
foreach q [lsort -real -index 0 $Q(pri_queue)] {
|
||||
puts "PQ $i: $q"
|
||||
incr i
|
||||
}
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------
|
||||
|
||||
proc box_query {a} {
|
||||
set res [list fully [expr rand()]]
|
||||
pq_test_callback $a $res
|
||||
return $res
|
||||
}
|
||||
|
||||
register_box_query db box_query
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE rt USING rtree_i32(id, x1,x2, y1,y2);
|
||||
WITH s(i) AS (
|
||||
SELECT 0 UNION ALL SELECT i+1 FROM s WHERE i<64
|
||||
)
|
||||
INSERT INTO rt SELECT NULL, a.i, a.i+1, b.i, b.i+1 FROM s a, s b;
|
||||
}
|
||||
|
||||
proc box_query {a} {
|
||||
set res [list fully [expr rand()]]
|
||||
pq_test_callback $a $res
|
||||
return $res
|
||||
}
|
||||
|
||||
pq_init
|
||||
db eval { SELECT id, x1,x2, y1,y2 FROM rt WHERE id MATCH qbox() } {
|
||||
pq_test_result $id $x1 $x2 $y1 $y2
|
||||
}
|
||||
pq_done
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
** 2010 August 30
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef _SQLITE3RTREE_H_
|
||||
#define _SQLITE3RTREE_H_
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
|
||||
typedef struct sqlite3_rtree_query_info sqlite3_rtree_query_info;
|
||||
|
||||
/* The double-precision datatype used by RTree depends on the
|
||||
** SQLITE_RTREE_INT_ONLY compile-time option.
|
||||
*/
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
typedef sqlite3_int64 sqlite3_rtree_dbl;
|
||||
#else
|
||||
typedef double sqlite3_rtree_dbl;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Register a geometry callback named zGeom that can be used as part of an
|
||||
** R-Tree geometry query as follows:
|
||||
**
|
||||
** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zGeom(... params ...)
|
||||
*/
|
||||
int sqlite3_rtree_geometry_callback(
|
||||
sqlite3 *db,
|
||||
const char *zGeom,
|
||||
int (*xGeom)(sqlite3_rtree_geometry*, int, sqlite3_rtree_dbl*,int*),
|
||||
void *pContext
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
** A pointer to a structure of the following type is passed as the first
|
||||
** argument to callbacks registered using rtree_geometry_callback().
|
||||
*/
|
||||
struct sqlite3_rtree_geometry {
|
||||
void *pContext; /* Copy of pContext passed to s_r_g_c() */
|
||||
int nParam; /* Size of array aParam[] */
|
||||
sqlite3_rtree_dbl *aParam; /* Parameters passed to SQL geom function */
|
||||
void *pUser; /* Callback implementation user data */
|
||||
void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */
|
||||
};
|
||||
|
||||
/*
|
||||
** Register a 2nd-generation geometry callback named zScore that can be
|
||||
** used as part of an R-Tree geometry query as follows:
|
||||
**
|
||||
** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zQueryFunc(... params ...)
|
||||
*/
|
||||
int sqlite3_rtree_query_callback(
|
||||
sqlite3 *db,
|
||||
const char *zQueryFunc,
|
||||
int (*xQueryFunc)(sqlite3_rtree_query_info*),
|
||||
void *pContext,
|
||||
void (*xDestructor)(void*)
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
** A pointer to a structure of the following type is passed as the
|
||||
** argument to scored geometry callback registered using
|
||||
** sqlite3_rtree_query_callback().
|
||||
**
|
||||
** Note that the first 5 fields of this structure are identical to
|
||||
** sqlite3_rtree_geometry. This structure is a subclass of
|
||||
** sqlite3_rtree_geometry.
|
||||
*/
|
||||
struct sqlite3_rtree_query_info {
|
||||
void *pContext; /* pContext from when function registered */
|
||||
int nParam; /* Number of function parameters */
|
||||
sqlite3_rtree_dbl *aParam; /* value of function parameters */
|
||||
void *pUser; /* callback can use this, if desired */
|
||||
void (*xDelUser)(void*); /* function to free pUser */
|
||||
sqlite3_rtree_dbl *aCoord; /* Coordinates of node or entry to check */
|
||||
unsigned int *anQueue; /* Number of pending entries in the queue */
|
||||
int nCoord; /* Number of coordinates */
|
||||
int iLevel; /* Level of current node or entry */
|
||||
int mxLevel; /* The largest iLevel value in the tree */
|
||||
sqlite3_int64 iRowid; /* Rowid for current entry */
|
||||
sqlite3_rtree_dbl rParentScore; /* Score of parent node */
|
||||
int eParentWithin; /* Visibility of parent node */
|
||||
int eWithin; /* OUT: Visibility */
|
||||
sqlite3_rtree_dbl rScore; /* OUT: Write the score here */
|
||||
/* The following fields are only available in 3.8.11 and later */
|
||||
sqlite3_value **apSqlParam; /* Original SQL values of parameters */
|
||||
};
|
||||
|
||||
/*
|
||||
** Allowed values for sqlite3_rtree_query.eWithin and .eParentWithin.
|
||||
*/
|
||||
#define NOT_WITHIN 0 /* Object completely outside of query region */
|
||||
#define PARTLY_WITHIN 1 /* Object partially overlaps query region */
|
||||
#define FULLY_WITHIN 2 /* Object fully contained within query region */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end of the 'extern "C"' block */
|
||||
#endif
|
||||
|
||||
#endif /* ifndef _SQLITE3RTREE_H_ */
|
||||
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
** 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 "sqlite3.h"
|
||||
#include "tclsqlite.h"
|
||||
|
||||
/* Solely for the UNUSED_PARAMETER() macro. */
|
||||
#include "sqliteInt.h"
|
||||
|
||||
#ifdef SQLITE_ENABLE_RTREE
|
||||
|
||||
typedef struct BoxGeomCtx BoxGeomCtx;
|
||||
struct BoxGeomCtx {
|
||||
Tcl_Interp *interp;
|
||||
Tcl_Obj *pScript;
|
||||
};
|
||||
|
||||
typedef struct BoxQueryCtx BoxQueryCtx;
|
||||
struct BoxQueryCtx {
|
||||
Tcl_Interp *interp;
|
||||
Tcl_Obj *pScript;
|
||||
};
|
||||
|
||||
static void testDelUser(void *pCtx){
|
||||
BoxGeomCtx *p = (BoxGeomCtx*)pCtx;
|
||||
Tcl_EvalObjEx(p->interp, p->pScript, 0);
|
||||
Tcl_DecrRefCount(p->pScript);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
static int invokeTclGeomCb(
|
||||
const char *zName,
|
||||
sqlite3_rtree_geometry *p,
|
||||
int nCoord,
|
||||
sqlite3_rtree_dbl *aCoord
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
if( p->pContext ){
|
||||
char aPtr[64];
|
||||
BoxGeomCtx *pCtx = (BoxGeomCtx*)p->pContext;
|
||||
Tcl_Interp *interp = pCtx->interp;
|
||||
Tcl_Obj *pScript = 0;
|
||||
Tcl_Obj *pParam = 0;
|
||||
Tcl_Obj *pCoord = 0;
|
||||
int ii;
|
||||
Tcl_Obj *pRes;
|
||||
|
||||
|
||||
pScript = Tcl_DuplicateObj(pCtx->pScript);
|
||||
Tcl_IncrRefCount(pScript);
|
||||
Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(zName,-1));
|
||||
|
||||
sqlite3_snprintf(sizeof(aPtr)-1, aPtr, "%p", (void*)p->pContext);
|
||||
Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(aPtr,-1));
|
||||
|
||||
pParam = Tcl_NewObj();
|
||||
for(ii=0; ii<p->nParam; ii++){
|
||||
Tcl_ListObjAppendElement(
|
||||
interp, pParam, Tcl_NewDoubleObj(p->aParam[ii])
|
||||
);
|
||||
}
|
||||
Tcl_ListObjAppendElement(interp, pScript, pParam);
|
||||
|
||||
pCoord = Tcl_NewObj();
|
||||
for(ii=0; ii<nCoord; ii++){
|
||||
Tcl_ListObjAppendElement(interp, pCoord, Tcl_NewDoubleObj(aCoord[ii]));
|
||||
}
|
||||
Tcl_ListObjAppendElement(interp, pScript, pCoord);
|
||||
|
||||
sqlite3_snprintf(sizeof(aPtr)-1, aPtr, "%p", (void*)p);
|
||||
Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(aPtr,-1));
|
||||
|
||||
rc = Tcl_EvalObjEx(interp, pScript, 0);
|
||||
if( rc!=TCL_OK ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
Tcl_Size nObj = 0;
|
||||
Tcl_Obj **aObj = 0;
|
||||
|
||||
pRes = Tcl_GetObjResult(interp);
|
||||
if( Tcl_ListObjGetElements(interp, pRes, &nObj, &aObj) ) return TCL_ERROR;
|
||||
if( nObj>0 ){
|
||||
const char *zCmd = Tcl_GetString(aObj[0]);
|
||||
if( 0==sqlite3_stricmp(zCmd, "zero") ){
|
||||
p->aParam[0] = 0.0;
|
||||
p->nParam = 1;
|
||||
}
|
||||
else if( 0==sqlite3_stricmp(zCmd, "user") ){
|
||||
if( p->pUser || p->xDelUser ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
BoxGeomCtx *pBGCtx = sqlite3_malloc(sizeof(BoxGeomCtx));
|
||||
if( pBGCtx==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
pBGCtx->interp = interp;
|
||||
pBGCtx->pScript = Tcl_DuplicateObj(pRes);
|
||||
Tcl_IncrRefCount(pBGCtx->pScript);
|
||||
Tcl_ListObjReplace(interp, pBGCtx->pScript, 0, 1, 0, 0);
|
||||
p->pUser = (void*)pBGCtx;
|
||||
p->xDelUser = testDelUser;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( 0==sqlite3_stricmp(zCmd, "user_is_zero") ){
|
||||
if( p->pUser || p->xDelUser ) rc = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
# EVIDENCE-OF: R-00693-36727 The legacy xGeom callback is invoked with
|
||||
# four arguments.
|
||||
|
||||
# EVIDENCE-OF: R-50437-53270 The first argument is a pointer to an
|
||||
# sqlite3_rtree_geometry structure which provides information about how
|
||||
# the SQL function was invoked.
|
||||
|
||||
# EVIDENCE-OF: R-00090-24248 The third argument, aCoord[], is an array
|
||||
# of nCoord coordinates that defines a bounding box to be tested.
|
||||
|
||||
# EVIDENCE-OF: R-28207-40885 The last argument is a pointer into which
|
||||
# the callback result should be written.
|
||||
|
||||
*/
|
||||
static int box_geom(
|
||||
sqlite3_rtree_geometry *p, /* R-50437-53270 */
|
||||
int nCoord, /* R-02424-24769 */
|
||||
sqlite3_rtree_dbl *aCoord, /* R-00090-24248 */
|
||||
int *pRes /* R-28207-40885 */
|
||||
){
|
||||
int ii;
|
||||
|
||||
if( p->nParam!=nCoord ){
|
||||
invokeTclGeomCb("box", p, nCoord, aCoord);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
if( invokeTclGeomCb("box", p, nCoord, aCoord) ) return SQLITE_ERROR;
|
||||
|
||||
for(ii=0; ii<nCoord; ii+=2){
|
||||
if( aCoord[ii]>p->aParam[ii+1] || aCoord[ii+1]<p->aParam[ii] ){
|
||||
/* R-28207-40885 */
|
||||
*pRes = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* R-28207-40885 */
|
||||
*pRes = 1;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int SQLITE_TCLAPI register_box_geom(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
|
||||
extern const char *sqlite3ErrName(int);
|
||||
sqlite3 *db;
|
||||
BoxGeomCtx *pCtx;
|
||||
char aPtr[64];
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB SCRIPT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
|
||||
pCtx = (BoxGeomCtx*)ckalloc(sizeof(BoxGeomCtx*));
|
||||
pCtx->interp = interp;
|
||||
pCtx->pScript = Tcl_DuplicateObj(objv[2]);
|
||||
Tcl_IncrRefCount(pCtx->pScript);
|
||||
|
||||
sqlite3_rtree_geometry_callback(db, "box", box_geom, (void*)pCtx);
|
||||
|
||||
sqlite3_snprintf(64, aPtr, "%p", (void*)pCtx);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(aPtr, -1));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int box_query(sqlite3_rtree_query_info *pInfo){
|
||||
const char *azParentWithin[] = {"not", "partly", "fully", 0};
|
||||
BoxQueryCtx *pCtx = (BoxQueryCtx*)pInfo->pContext;
|
||||
Tcl_Interp *interp = pCtx->interp;
|
||||
Tcl_Obj *pEval;
|
||||
Tcl_Obj *pArg;
|
||||
Tcl_Obj *pTmp = 0;
|
||||
int rc;
|
||||
int ii;
|
||||
|
||||
pEval = Tcl_DuplicateObj(pCtx->pScript);
|
||||
Tcl_IncrRefCount(pEval);
|
||||
pArg = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pArg);
|
||||
|
||||
/* aParam[] */
|
||||
pTmp = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pTmp);
|
||||
for(ii=0; ii<pInfo->nParam; ii++){
|
||||
Tcl_Obj *p = Tcl_NewDoubleObj(pInfo->aParam[ii]);
|
||||
Tcl_ListObjAppendElement(interp, pTmp, p);
|
||||
}
|
||||
Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("aParam", -1));
|
||||
Tcl_ListObjAppendElement(interp, pArg, pTmp);
|
||||
Tcl_DecrRefCount(pTmp);
|
||||
|
||||
/* aCoord[] */
|
||||
pTmp = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pTmp);
|
||||
for(ii=0; ii<pInfo->nCoord; ii++){
|
||||
Tcl_Obj *p = Tcl_NewDoubleObj(pInfo->aCoord[ii]);
|
||||
Tcl_ListObjAppendElement(interp, pTmp, p);
|
||||
}
|
||||
Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("aCoord", -1));
|
||||
Tcl_ListObjAppendElement(interp, pArg, pTmp);
|
||||
Tcl_DecrRefCount(pTmp);
|
||||
|
||||
/* anQueue[] */
|
||||
pTmp = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pTmp);
|
||||
for(ii=0; ii<=pInfo->mxLevel; ii++){
|
||||
Tcl_Obj *p = Tcl_NewIntObj((int)pInfo->anQueue[ii]);
|
||||
Tcl_ListObjAppendElement(interp, pTmp, p);
|
||||
}
|
||||
Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("anQueue", -1));
|
||||
Tcl_ListObjAppendElement(interp, pArg, pTmp);
|
||||
Tcl_DecrRefCount(pTmp);
|
||||
|
||||
/* iLevel */
|
||||
Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("iLevel", -1));
|
||||
Tcl_ListObjAppendElement(interp, pArg, Tcl_NewIntObj(pInfo->iLevel));
|
||||
|
||||
/* mxLevel */
|
||||
Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("mxLevel", -1));
|
||||
Tcl_ListObjAppendElement(interp, pArg, Tcl_NewIntObj(pInfo->mxLevel));
|
||||
|
||||
/* iRowid */
|
||||
Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("iRowid", -1));
|
||||
Tcl_ListObjAppendElement(interp, pArg, Tcl_NewWideIntObj(pInfo->iRowid));
|
||||
|
||||
/* rParentScore */
|
||||
Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("rParentScore", -1));
|
||||
Tcl_ListObjAppendElement(interp, pArg, Tcl_NewDoubleObj(pInfo->rParentScore));
|
||||
|
||||
/* eParentWithin */
|
||||
assert( pInfo->eParentWithin==0
|
||||
|| pInfo->eParentWithin==1
|
||||
|| pInfo->eParentWithin==2
|
||||
);
|
||||
Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("eParentWithin", -1));
|
||||
Tcl_ListObjAppendElement(interp, pArg,
|
||||
Tcl_NewStringObj(azParentWithin[pInfo->eParentWithin], -1)
|
||||
);
|
||||
|
||||
Tcl_ListObjAppendElement(interp, pEval, pArg);
|
||||
rc = Tcl_EvalObjEx(interp, pEval, 0) ? SQLITE_ERROR : SQLITE_OK;
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
double rScore = 0.0;
|
||||
Tcl_Size nObj = 0;
|
||||
int eP = 0;
|
||||
Tcl_Obj **aObj = 0;
|
||||
Tcl_Obj *pRes = Tcl_GetObjResult(interp);
|
||||
|
||||
if( Tcl_ListObjGetElements(interp, pRes, &nObj, &aObj)
|
||||
|| nObj!=2
|
||||
|| Tcl_GetDoubleFromObj(interp, aObj[1], &rScore)
|
||||
|| Tcl_GetIndexFromObj(interp, aObj[0], azParentWithin, "value", 0, &eP)
|
||||
){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
pInfo->rScore = rScore;
|
||||
pInfo->eParentWithin = eP;
|
||||
}
|
||||
}
|
||||
|
||||
Tcl_DecrRefCount(pArg);
|
||||
Tcl_DecrRefCount(pEval);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void box_query_destroy(void *p){
|
||||
BoxQueryCtx *pCtx = (BoxQueryCtx*)p;
|
||||
Tcl_DecrRefCount(pCtx->pScript);
|
||||
ckfree((char*)pCtx);
|
||||
}
|
||||
|
||||
static int SQLITE_TCLAPI register_box_query(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
|
||||
extern const char *sqlite3ErrName(int);
|
||||
sqlite3 *db;
|
||||
BoxQueryCtx *pCtx;
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB SCRIPT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
|
||||
pCtx = (BoxQueryCtx*)ckalloc(sizeof(BoxQueryCtx));
|
||||
pCtx->interp = interp;
|
||||
pCtx->pScript = Tcl_DuplicateObj(objv[2]);
|
||||
Tcl_IncrRefCount(pCtx->pScript);
|
||||
|
||||
sqlite3_rtree_query_callback(
|
||||
db, "qbox", box_query, (void*)pCtx, box_query_destroy
|
||||
);
|
||||
|
||||
Tcl_ResetResult(interp);
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_RTREE */
|
||||
|
||||
|
||||
int Sqlitetestrtreedoc_Init(Tcl_Interp *interp){
|
||||
#ifdef SQLITE_ENABLE_RTREE
|
||||
Tcl_CreateObjCommand(interp, "register_box_geom", register_box_geom, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "register_box_query", register_box_query, 0, 0);
|
||||
#endif /* SQLITE_ENABLE_RTREE */
|
||||
return TCL_OK;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
# 2008 Sep 08
|
||||
#
|
||||
# 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 ticket #3363 is fixed.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !rtree {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_test tkt3363.1.1 {
|
||||
execsql { CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2, y1, y2) }
|
||||
} {}
|
||||
|
||||
do_test tkt3363.1.2 {
|
||||
for {set ii 1} {$ii < 50} {incr ii} {
|
||||
set x 1000000
|
||||
set y [expr 4000000 + $ii*10]
|
||||
execsql { INSERT INTO t1 VALUES($ii, $x, $x, $y, $y) }
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test tkt3363.1.3 {
|
||||
execsql {
|
||||
SELECT count(*) FROM t1 WHERE +y2>4000425.0;
|
||||
}
|
||||
} {7}
|
||||
|
||||
do_test tkt3363.1.4 {
|
||||
execsql {
|
||||
SELECT count(*) FROM t1 WHERE y2>4000425.0;
|
||||
}
|
||||
} {7}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/tclsh
|
||||
#
|
||||
# This script generates a cluster of random polygons that are useful
|
||||
# for testing the geopoly extension.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# tclsh randomshape.tcl | tee x.sql | sqlite3 >x.html
|
||||
#
|
||||
# The output files are x.sql and x.html. Run the above multiple times
|
||||
# until an interesting "x.html" file is found, then use the "x.sql" inputs
|
||||
# to construct test cases.
|
||||
#
|
||||
proc randomenclosure {cx cy p1 p2 p3 p4} {
|
||||
set r 0
|
||||
set pi 3.145926
|
||||
set pi2 [expr {$pi*2}]
|
||||
set x0 [expr {$cx + rand()*$p3 + $p4}]
|
||||
set ans "\[\[$x0,$cy\]"
|
||||
while {1} {
|
||||
set r [expr {$r+$p1+$p2*rand()}]
|
||||
if {$r>=$pi2} break
|
||||
set m [expr {rand()*$p3 + $p4}]
|
||||
set x [expr {$cx+$m*cos($r)}]
|
||||
set y [expr {$cy+$m*sin($r)}]
|
||||
append ans ",\[$x,$y\]"
|
||||
}
|
||||
append ans ",\[$x0,$cy\]\]"
|
||||
return $ans
|
||||
}
|
||||
proc randomshape1 {} {
|
||||
set cx [expr {100+int(rand()*800)}]
|
||||
set cy [expr {100+int(rand()*600)}]
|
||||
set p1 [expr {rand()*0.1}]
|
||||
set p2 [expr {rand()*0.5+0.5}]
|
||||
set p3 [expr {rand()*100+25}]
|
||||
set p4 [expr {rand()*25}]
|
||||
return [randomenclosure $cx $cy $p1 $p2 $p3 $p4]
|
||||
}
|
||||
proc randomshape1_sm {} {
|
||||
set cx [expr {100+int(rand()*800)}]
|
||||
set cy [expr {100+int(rand()*600)}]
|
||||
set p1 [expr {rand()*0.1}]
|
||||
set p2 [expr {rand()*0.5+0.5}]
|
||||
set p3 [expr {rand()*10+25}]
|
||||
set p4 [expr {rand()*5}]
|
||||
return [randomenclosure $cx $cy $p1 $p2 $p3 $p4]
|
||||
}
|
||||
proc randomshape2 {} {
|
||||
set cx [expr {400+int(rand()*200)}]
|
||||
set cy [expr {300+int(rand()*200)}]
|
||||
set p1 [expr {rand()*0.05}]
|
||||
set p2 [expr {rand()*0.5+0.5}]
|
||||
set p3 [expr {rand()*50+200}]
|
||||
set p4 [expr {rand()*50+100}]
|
||||
return [randomenclosure $cx $cy $p1 $p2 $p3 $p4]
|
||||
}
|
||||
proc randomcolor {} {
|
||||
set n [expr {int(rand()*5)}]
|
||||
return [lindex {red orange green blue purple} $n]
|
||||
}
|
||||
|
||||
puts {.print '<html>'}
|
||||
puts {.print '<svg width="1000" height="800" style="border:1px solid black">'}
|
||||
puts {CREATE TABLE t1(poly,clr);}
|
||||
puts {CREATE TABLE t2(poly,clr);}
|
||||
for {set i 0} {$i<30} {incr i} {
|
||||
puts "INSERT INTO t1(rowid,poly,clr)"
|
||||
puts " VALUES($i,'[randomshape1]','[randomcolor]');"
|
||||
}
|
||||
for {set i 30} {$i<80} {incr i} {
|
||||
puts "INSERT INTO t1(rowid,poly,clr)"
|
||||
puts " VALUES($i,'[randomshape1_sm]','[randomcolor]');"
|
||||
}
|
||||
for {set i 100} {$i<105} {incr i} {
|
||||
puts "INSERT INTO t2(rowid,poly,clr)"
|
||||
puts " VALUES($i,'[randomshape2]','[randomcolor]');"
|
||||
}
|
||||
|
||||
puts {DELETE FROM t1 WHERE geopoly_json(poly) IS NULL;}
|
||||
puts {SELECT geopoly_svg(poly,
|
||||
printf('style="fill:none;stroke:%s;stroke-width:1;"',clr))
|
||||
FROM t1;}
|
||||
puts {SELECT geopoly_svg(poly,
|
||||
printf('style="fill:none;stroke:%s;stroke-width:2;"',clr))
|
||||
FROM t2;}
|
||||
puts {.print '<svg>'}
|
||||
@@ -0,0 +1,188 @@
|
||||
|
||||
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
|
||||
@@ -0,0 +1,602 @@
|
||||
#!sqlite3
|
||||
#
|
||||
# This is a visual test case for the geopoly virtual table.
|
||||
#
|
||||
# Run this script in the sqlite3 CLI, and redirect output into an
|
||||
# HTML file. This display the HTML in a webbrowser.
|
||||
#
|
||||
|
||||
/* Test data.
|
||||
** Lots of shapes to be displayed over a 1000x800 canvas.
|
||||
*/
|
||||
CREATE TEMP TABLE basis(name TEXT, jshape TEXT);
|
||||
INSERT INTO basis(name,jshape) VALUES
|
||||
('box-20','[[0,0],[20,0],[20,20],[0,20],[0,0]]'),
|
||||
('house-70','[[0,0],[50,0],[50,50],[25,70],[0,50],[0,0]]'),
|
||||
('line-40','[[0,0],[40,0],[40,5],[0,5],[0,0]]'),
|
||||
('line-80','[[0,0],[80,0],[80,7],[0,7],[0,0]]'),
|
||||
('arrow-50','[[0,0],[25,25],[0,50],[15,25],[0,0]]'),
|
||||
('triangle-30','[[0,0],[30,0],[15,30],[0,0]]'),
|
||||
('angle-30','[[0,0],[30,0],[30,30],[26,30],[26,4],[0,4],[0,0]]'),
|
||||
('star-10','[[1,0],[5,2],[9,0],[7,4],[10,8],[7,7],[5,10],[3,7],[0,8],[3,4],[1,0]]');
|
||||
CREATE TEMP TABLE xform(A,B,C,D,clr);
|
||||
INSERT INTO xform(A,B,clr) VALUES
|
||||
(1,0,'black'),
|
||||
(0.707,0.707,'blue'),
|
||||
(0.5,0.866,'red'),
|
||||
(-0.866,0.5,'green');
|
||||
CREATE TEMP TABLE xyoff(id1,id2,xoff,yoff,PRIMARY KEY(id1,id2,xoff,yoff))
|
||||
WITHOUT ROWID;
|
||||
INSERT INTO xyoff VALUES(1,1,811,659);
|
||||
INSERT INTO xyoff VALUES(1,1,235,550);
|
||||
INSERT INTO xyoff VALUES(1,1,481,620);
|
||||
INSERT INTO xyoff VALUES(1,1,106,494);
|
||||
INSERT INTO xyoff VALUES(1,1,487,106);
|
||||
INSERT INTO xyoff VALUES(1,1,817,595);
|
||||
INSERT INTO xyoff VALUES(1,1,240,504);
|
||||
INSERT INTO xyoff VALUES(1,1,806,457);
|
||||
INSERT INTO xyoff VALUES(1,1,608,107);
|
||||
INSERT INTO xyoff VALUES(1,1,768,662);
|
||||
INSERT INTO xyoff VALUES(1,2,808,528);
|
||||
INSERT INTO xyoff VALUES(1,2,768,528);
|
||||
INSERT INTO xyoff VALUES(1,2,771,171);
|
||||
INSERT INTO xyoff VALUES(1,2,275,671);
|
||||
INSERT INTO xyoff VALUES(1,2,326,336);
|
||||
INSERT INTO xyoff VALUES(1,2,690,688);
|
||||
INSERT INTO xyoff VALUES(1,2,597,239);
|
||||
INSERT INTO xyoff VALUES(1,2,317,528);
|
||||
INSERT INTO xyoff VALUES(1,2,366,223);
|
||||
INSERT INTO xyoff VALUES(1,2,621,154);
|
||||
INSERT INTO xyoff VALUES(1,3,829,469);
|
||||
INSERT INTO xyoff VALUES(1,3,794,322);
|
||||
INSERT INTO xyoff VALUES(1,3,358,387);
|
||||
INSERT INTO xyoff VALUES(1,3,184,444);
|
||||
INSERT INTO xyoff VALUES(1,3,729,500);
|
||||
INSERT INTO xyoff VALUES(1,3,333,523);
|
||||
INSERT INTO xyoff VALUES(1,3,117,595);
|
||||
INSERT INTO xyoff VALUES(1,3,496,201);
|
||||
INSERT INTO xyoff VALUES(1,3,818,601);
|
||||
INSERT INTO xyoff VALUES(1,3,541,343);
|
||||
INSERT INTO xyoff VALUES(1,4,603,248);
|
||||
INSERT INTO xyoff VALUES(1,4,761,649);
|
||||
INSERT INTO xyoff VALUES(1,4,611,181);
|
||||
INSERT INTO xyoff VALUES(1,4,607,233);
|
||||
INSERT INTO xyoff VALUES(1,4,860,206);
|
||||
INSERT INTO xyoff VALUES(1,4,310,231);
|
||||
INSERT INTO xyoff VALUES(1,4,727,539);
|
||||
INSERT INTO xyoff VALUES(1,4,660,661);
|
||||
INSERT INTO xyoff VALUES(1,4,403,133);
|
||||
INSERT INTO xyoff VALUES(1,4,619,331);
|
||||
INSERT INTO xyoff VALUES(2,1,712,578);
|
||||
INSERT INTO xyoff VALUES(2,1,567,313);
|
||||
INSERT INTO xyoff VALUES(2,1,231,423);
|
||||
INSERT INTO xyoff VALUES(2,1,490,175);
|
||||
INSERT INTO xyoff VALUES(2,1,898,353);
|
||||
INSERT INTO xyoff VALUES(2,1,589,483);
|
||||
INSERT INTO xyoff VALUES(2,1,188,462);
|
||||
INSERT INTO xyoff VALUES(2,1,720,106);
|
||||
INSERT INTO xyoff VALUES(2,1,793,380);
|
||||
INSERT INTO xyoff VALUES(2,1,154,396);
|
||||
INSERT INTO xyoff VALUES(2,2,324,218);
|
||||
INSERT INTO xyoff VALUES(2,2,120,327);
|
||||
INSERT INTO xyoff VALUES(2,2,655,133);
|
||||
INSERT INTO xyoff VALUES(2,2,516,603);
|
||||
INSERT INTO xyoff VALUES(2,2,529,572);
|
||||
INSERT INTO xyoff VALUES(2,2,481,212);
|
||||
INSERT INTO xyoff VALUES(2,2,802,107);
|
||||
INSERT INTO xyoff VALUES(2,2,234,509);
|
||||
INSERT INTO xyoff VALUES(2,2,501,269);
|
||||
INSERT INTO xyoff VALUES(2,2,349,553);
|
||||
INSERT INTO xyoff VALUES(2,3,495,685);
|
||||
INSERT INTO xyoff VALUES(2,3,897,372);
|
||||
INSERT INTO xyoff VALUES(2,3,350,681);
|
||||
INSERT INTO xyoff VALUES(2,3,832,257);
|
||||
INSERT INTO xyoff VALUES(2,3,778,149);
|
||||
INSERT INTO xyoff VALUES(2,3,683,426);
|
||||
INSERT INTO xyoff VALUES(2,3,693,217);
|
||||
INSERT INTO xyoff VALUES(2,3,746,317);
|
||||
INSERT INTO xyoff VALUES(2,3,805,369);
|
||||
INSERT INTO xyoff VALUES(2,3,336,585);
|
||||
INSERT INTO xyoff VALUES(2,4,890,255);
|
||||
INSERT INTO xyoff VALUES(2,4,556,565);
|
||||
INSERT INTO xyoff VALUES(2,4,865,555);
|
||||
INSERT INTO xyoff VALUES(2,4,230,293);
|
||||
INSERT INTO xyoff VALUES(2,4,247,251);
|
||||
INSERT INTO xyoff VALUES(2,4,730,563);
|
||||
INSERT INTO xyoff VALUES(2,4,318,282);
|
||||
INSERT INTO xyoff VALUES(2,4,220,431);
|
||||
INSERT INTO xyoff VALUES(2,4,828,336);
|
||||
INSERT INTO xyoff VALUES(2,4,278,525);
|
||||
INSERT INTO xyoff VALUES(3,1,324,656);
|
||||
INSERT INTO xyoff VALUES(3,1,625,362);
|
||||
INSERT INTO xyoff VALUES(3,1,155,570);
|
||||
INSERT INTO xyoff VALUES(3,1,267,433);
|
||||
INSERT INTO xyoff VALUES(3,1,599,121);
|
||||
INSERT INTO xyoff VALUES(3,1,873,498);
|
||||
INSERT INTO xyoff VALUES(3,1,789,520);
|
||||
INSERT INTO xyoff VALUES(3,1,656,378);
|
||||
INSERT INTO xyoff VALUES(3,1,831,601);
|
||||
INSERT INTO xyoff VALUES(3,1,256,471);
|
||||
INSERT INTO xyoff VALUES(3,2,332,258);
|
||||
INSERT INTO xyoff VALUES(3,2,305,463);
|
||||
INSERT INTO xyoff VALUES(3,2,796,341);
|
||||
INSERT INTO xyoff VALUES(3,2,830,229);
|
||||
INSERT INTO xyoff VALUES(3,2,413,271);
|
||||
INSERT INTO xyoff VALUES(3,2,269,140);
|
||||
INSERT INTO xyoff VALUES(3,2,628,441);
|
||||
INSERT INTO xyoff VALUES(3,2,747,643);
|
||||
INSERT INTO xyoff VALUES(3,2,584,435);
|
||||
INSERT INTO xyoff VALUES(3,2,784,314);
|
||||
INSERT INTO xyoff VALUES(3,3,722,233);
|
||||
INSERT INTO xyoff VALUES(3,3,815,421);
|
||||
INSERT INTO xyoff VALUES(3,3,401,267);
|
||||
INSERT INTO xyoff VALUES(3,3,451,650);
|
||||
INSERT INTO xyoff VALUES(3,3,329,485);
|
||||
INSERT INTO xyoff VALUES(3,3,878,370);
|
||||
INSERT INTO xyoff VALUES(3,3,162,616);
|
||||
INSERT INTO xyoff VALUES(3,3,844,183);
|
||||
INSERT INTO xyoff VALUES(3,3,161,216);
|
||||
INSERT INTO xyoff VALUES(3,3,176,676);
|
||||
INSERT INTO xyoff VALUES(3,4,780,128);
|
||||
INSERT INTO xyoff VALUES(3,4,566,121);
|
||||
INSERT INTO xyoff VALUES(3,4,646,120);
|
||||
INSERT INTO xyoff VALUES(3,4,223,557);
|
||||
INSERT INTO xyoff VALUES(3,4,251,117);
|
||||
INSERT INTO xyoff VALUES(3,4,139,209);
|
||||
INSERT INTO xyoff VALUES(3,4,813,597);
|
||||
INSERT INTO xyoff VALUES(3,4,454,538);
|
||||
INSERT INTO xyoff VALUES(3,4,616,198);
|
||||
INSERT INTO xyoff VALUES(3,4,210,159);
|
||||
INSERT INTO xyoff VALUES(4,1,208,415);
|
||||
INSERT INTO xyoff VALUES(4,1,326,665);
|
||||
INSERT INTO xyoff VALUES(4,1,612,133);
|
||||
INSERT INTO xyoff VALUES(4,1,537,513);
|
||||
INSERT INTO xyoff VALUES(4,1,638,438);
|
||||
INSERT INTO xyoff VALUES(4,1,808,269);
|
||||
INSERT INTO xyoff VALUES(4,1,552,121);
|
||||
INSERT INTO xyoff VALUES(4,1,100,189);
|
||||
INSERT INTO xyoff VALUES(4,1,643,664);
|
||||
INSERT INTO xyoff VALUES(4,1,726,378);
|
||||
INSERT INTO xyoff VALUES(4,2,478,409);
|
||||
INSERT INTO xyoff VALUES(4,2,497,507);
|
||||
INSERT INTO xyoff VALUES(4,2,233,148);
|
||||
INSERT INTO xyoff VALUES(4,2,587,237);
|
||||
INSERT INTO xyoff VALUES(4,2,604,166);
|
||||
INSERT INTO xyoff VALUES(4,2,165,455);
|
||||
INSERT INTO xyoff VALUES(4,2,320,258);
|
||||
INSERT INTO xyoff VALUES(4,2,353,496);
|
||||
INSERT INTO xyoff VALUES(4,2,347,495);
|
||||
INSERT INTO xyoff VALUES(4,2,166,622);
|
||||
INSERT INTO xyoff VALUES(4,3,461,332);
|
||||
INSERT INTO xyoff VALUES(4,3,685,278);
|
||||
INSERT INTO xyoff VALUES(4,3,427,594);
|
||||
INSERT INTO xyoff VALUES(4,3,467,346);
|
||||
INSERT INTO xyoff VALUES(4,3,125,548);
|
||||
INSERT INTO xyoff VALUES(4,3,597,680);
|
||||
INSERT INTO xyoff VALUES(4,3,820,445);
|
||||
INSERT INTO xyoff VALUES(4,3,144,330);
|
||||
INSERT INTO xyoff VALUES(4,3,557,434);
|
||||
INSERT INTO xyoff VALUES(4,3,254,315);
|
||||
INSERT INTO xyoff VALUES(4,4,157,339);
|
||||
INSERT INTO xyoff VALUES(4,4,249,220);
|
||||
INSERT INTO xyoff VALUES(4,4,391,323);
|
||||
INSERT INTO xyoff VALUES(4,4,589,429);
|
||||
INSERT INTO xyoff VALUES(4,4,859,592);
|
||||
INSERT INTO xyoff VALUES(4,4,337,680);
|
||||
INSERT INTO xyoff VALUES(4,4,410,288);
|
||||
INSERT INTO xyoff VALUES(4,4,636,596);
|
||||
INSERT INTO xyoff VALUES(4,4,734,433);
|
||||
INSERT INTO xyoff VALUES(4,4,559,549);
|
||||
INSERT INTO xyoff VALUES(5,1,549,607);
|
||||
INSERT INTO xyoff VALUES(5,1,584,498);
|
||||
INSERT INTO xyoff VALUES(5,1,699,116);
|
||||
INSERT INTO xyoff VALUES(5,1,525,524);
|
||||
INSERT INTO xyoff VALUES(5,1,304,667);
|
||||
INSERT INTO xyoff VALUES(5,1,302,232);
|
||||
INSERT INTO xyoff VALUES(5,1,403,149);
|
||||
INSERT INTO xyoff VALUES(5,1,824,403);
|
||||
INSERT INTO xyoff VALUES(5,1,697,203);
|
||||
INSERT INTO xyoff VALUES(5,1,293,689);
|
||||
INSERT INTO xyoff VALUES(5,2,199,275);
|
||||
INSERT INTO xyoff VALUES(5,2,395,393);
|
||||
INSERT INTO xyoff VALUES(5,2,657,642);
|
||||
INSERT INTO xyoff VALUES(5,2,200,655);
|
||||
INSERT INTO xyoff VALUES(5,2,882,234);
|
||||
INSERT INTO xyoff VALUES(5,2,483,565);
|
||||
INSERT INTO xyoff VALUES(5,2,755,640);
|
||||
INSERT INTO xyoff VALUES(5,2,810,305);
|
||||
INSERT INTO xyoff VALUES(5,2,731,655);
|
||||
INSERT INTO xyoff VALUES(5,2,466,690);
|
||||
INSERT INTO xyoff VALUES(5,3,563,584);
|
||||
INSERT INTO xyoff VALUES(5,3,491,117);
|
||||
INSERT INTO xyoff VALUES(5,3,779,292);
|
||||
INSERT INTO xyoff VALUES(5,3,375,637);
|
||||
INSERT INTO xyoff VALUES(5,3,253,553);
|
||||
INSERT INTO xyoff VALUES(5,3,797,514);
|
||||
INSERT INTO xyoff VALUES(5,3,229,480);
|
||||
INSERT INTO xyoff VALUES(5,3,257,194);
|
||||
INSERT INTO xyoff VALUES(5,3,449,555);
|
||||
INSERT INTO xyoff VALUES(5,3,849,630);
|
||||
INSERT INTO xyoff VALUES(5,4,329,286);
|
||||
INSERT INTO xyoff VALUES(5,4,640,197);
|
||||
INSERT INTO xyoff VALUES(5,4,104,150);
|
||||
INSERT INTO xyoff VALUES(5,4,438,272);
|
||||
INSERT INTO xyoff VALUES(5,4,773,226);
|
||||
INSERT INTO xyoff VALUES(5,4,441,650);
|
||||
INSERT INTO xyoff VALUES(5,4,242,340);
|
||||
INSERT INTO xyoff VALUES(5,4,301,435);
|
||||
INSERT INTO xyoff VALUES(5,4,171,397);
|
||||
INSERT INTO xyoff VALUES(5,4,541,619);
|
||||
INSERT INTO xyoff VALUES(6,1,651,301);
|
||||
INSERT INTO xyoff VALUES(6,1,637,137);
|
||||
INSERT INTO xyoff VALUES(6,1,765,643);
|
||||
INSERT INTO xyoff VALUES(6,1,173,296);
|
||||
INSERT INTO xyoff VALUES(6,1,263,192);
|
||||
INSERT INTO xyoff VALUES(6,1,791,302);
|
||||
INSERT INTO xyoff VALUES(6,1,860,601);
|
||||
INSERT INTO xyoff VALUES(6,1,780,445);
|
||||
INSERT INTO xyoff VALUES(6,1,462,214);
|
||||
INSERT INTO xyoff VALUES(6,1,802,207);
|
||||
INSERT INTO xyoff VALUES(6,2,811,685);
|
||||
INSERT INTO xyoff VALUES(6,2,533,531);
|
||||
INSERT INTO xyoff VALUES(6,2,390,614);
|
||||
INSERT INTO xyoff VALUES(6,2,260,580);
|
||||
INSERT INTO xyoff VALUES(6,2,116,377);
|
||||
INSERT INTO xyoff VALUES(6,2,860,458);
|
||||
INSERT INTO xyoff VALUES(6,2,438,590);
|
||||
INSERT INTO xyoff VALUES(6,2,604,562);
|
||||
INSERT INTO xyoff VALUES(6,2,241,242);
|
||||
INSERT INTO xyoff VALUES(6,2,667,298);
|
||||
INSERT INTO xyoff VALUES(6,3,787,698);
|
||||
INSERT INTO xyoff VALUES(6,3,868,521);
|
||||
INSERT INTO xyoff VALUES(6,3,412,587);
|
||||
INSERT INTO xyoff VALUES(6,3,640,131);
|
||||
INSERT INTO xyoff VALUES(6,3,748,410);
|
||||
INSERT INTO xyoff VALUES(6,3,257,244);
|
||||
INSERT INTO xyoff VALUES(6,3,411,195);
|
||||
INSERT INTO xyoff VALUES(6,3,464,356);
|
||||
INSERT INTO xyoff VALUES(6,3,157,339);
|
||||
INSERT INTO xyoff VALUES(6,3,434,505);
|
||||
INSERT INTO xyoff VALUES(6,4,480,671);
|
||||
INSERT INTO xyoff VALUES(6,4,519,228);
|
||||
INSERT INTO xyoff VALUES(6,4,404,513);
|
||||
INSERT INTO xyoff VALUES(6,4,120,538);
|
||||
INSERT INTO xyoff VALUES(6,4,403,663);
|
||||
INSERT INTO xyoff VALUES(6,4,477,677);
|
||||
INSERT INTO xyoff VALUES(6,4,690,154);
|
||||
INSERT INTO xyoff VALUES(6,4,606,498);
|
||||
INSERT INTO xyoff VALUES(6,4,430,665);
|
||||
INSERT INTO xyoff VALUES(6,4,499,273);
|
||||
INSERT INTO xyoff VALUES(7,1,118,526);
|
||||
INSERT INTO xyoff VALUES(7,1,817,522);
|
||||
INSERT INTO xyoff VALUES(7,1,388,638);
|
||||
INSERT INTO xyoff VALUES(7,1,181,265);
|
||||
INSERT INTO xyoff VALUES(7,1,442,332);
|
||||
INSERT INTO xyoff VALUES(7,1,475,282);
|
||||
INSERT INTO xyoff VALUES(7,1,722,633);
|
||||
INSERT INTO xyoff VALUES(7,1,104,394);
|
||||
INSERT INTO xyoff VALUES(7,1,631,262);
|
||||
INSERT INTO xyoff VALUES(7,1,372,392);
|
||||
INSERT INTO xyoff VALUES(7,2,600,413);
|
||||
INSERT INTO xyoff VALUES(7,2,386,223);
|
||||
INSERT INTO xyoff VALUES(7,2,839,174);
|
||||
INSERT INTO xyoff VALUES(7,2,293,410);
|
||||
INSERT INTO xyoff VALUES(7,2,281,391);
|
||||
INSERT INTO xyoff VALUES(7,2,859,387);
|
||||
INSERT INTO xyoff VALUES(7,2,478,347);
|
||||
INSERT INTO xyoff VALUES(7,2,646,690);
|
||||
INSERT INTO xyoff VALUES(7,2,713,234);
|
||||
INSERT INTO xyoff VALUES(7,2,199,588);
|
||||
INSERT INTO xyoff VALUES(7,3,389,256);
|
||||
INSERT INTO xyoff VALUES(7,3,349,542);
|
||||
INSERT INTO xyoff VALUES(7,3,363,345);
|
||||
INSERT INTO xyoff VALUES(7,3,751,302);
|
||||
INSERT INTO xyoff VALUES(7,3,423,386);
|
||||
INSERT INTO xyoff VALUES(7,3,267,444);
|
||||
INSERT INTO xyoff VALUES(7,3,243,182);
|
||||
INSERT INTO xyoff VALUES(7,3,453,658);
|
||||
INSERT INTO xyoff VALUES(7,3,126,345);
|
||||
INSERT INTO xyoff VALUES(7,3,120,472);
|
||||
INSERT INTO xyoff VALUES(7,4,359,654);
|
||||
INSERT INTO xyoff VALUES(7,4,339,516);
|
||||
INSERT INTO xyoff VALUES(7,4,710,452);
|
||||
INSERT INTO xyoff VALUES(7,4,810,560);
|
||||
INSERT INTO xyoff VALUES(7,4,644,692);
|
||||
INSERT INTO xyoff VALUES(7,4,826,327);
|
||||
INSERT INTO xyoff VALUES(7,4,465,462);
|
||||
INSERT INTO xyoff VALUES(7,4,310,456);
|
||||
INSERT INTO xyoff VALUES(7,4,577,613);
|
||||
INSERT INTO xyoff VALUES(7,4,502,555);
|
||||
INSERT INTO xyoff VALUES(8,1,601,620);
|
||||
INSERT INTO xyoff VALUES(8,1,372,683);
|
||||
INSERT INTO xyoff VALUES(8,1,758,399);
|
||||
INSERT INTO xyoff VALUES(8,1,485,552);
|
||||
INSERT INTO xyoff VALUES(8,1,159,563);
|
||||
INSERT INTO xyoff VALUES(8,1,536,303);
|
||||
INSERT INTO xyoff VALUES(8,1,122,263);
|
||||
INSERT INTO xyoff VALUES(8,1,836,435);
|
||||
INSERT INTO xyoff VALUES(8,1,544,146);
|
||||
INSERT INTO xyoff VALUES(8,1,270,277);
|
||||
INSERT INTO xyoff VALUES(8,2,849,281);
|
||||
INSERT INTO xyoff VALUES(8,2,563,242);
|
||||
INSERT INTO xyoff VALUES(8,2,704,463);
|
||||
INSERT INTO xyoff VALUES(8,2,102,165);
|
||||
INSERT INTO xyoff VALUES(8,2,797,524);
|
||||
INSERT INTO xyoff VALUES(8,2,612,426);
|
||||
INSERT INTO xyoff VALUES(8,2,345,372);
|
||||
INSERT INTO xyoff VALUES(8,2,820,376);
|
||||
INSERT INTO xyoff VALUES(8,2,789,156);
|
||||
INSERT INTO xyoff VALUES(8,2,321,466);
|
||||
INSERT INTO xyoff VALUES(8,3,150,332);
|
||||
INSERT INTO xyoff VALUES(8,3,136,152);
|
||||
INSERT INTO xyoff VALUES(8,3,468,528);
|
||||
INSERT INTO xyoff VALUES(8,3,409,192);
|
||||
INSERT INTO xyoff VALUES(8,3,820,216);
|
||||
INSERT INTO xyoff VALUES(8,3,847,249);
|
||||
INSERT INTO xyoff VALUES(8,3,801,267);
|
||||
INSERT INTO xyoff VALUES(8,3,181,670);
|
||||
INSERT INTO xyoff VALUES(8,3,398,563);
|
||||
INSERT INTO xyoff VALUES(8,3,439,576);
|
||||
INSERT INTO xyoff VALUES(8,4,123,309);
|
||||
INSERT INTO xyoff VALUES(8,4,190,496);
|
||||
INSERT INTO xyoff VALUES(8,4,571,531);
|
||||
INSERT INTO xyoff VALUES(8,4,290,255);
|
||||
INSERT INTO xyoff VALUES(8,4,244,412);
|
||||
INSERT INTO xyoff VALUES(8,4,264,596);
|
||||
INSERT INTO xyoff VALUES(8,4,253,420);
|
||||
INSERT INTO xyoff VALUES(8,4,847,536);
|
||||
INSERT INTO xyoff VALUES(8,4,120,288);
|
||||
INSERT INTO xyoff VALUES(8,4,331,639);
|
||||
|
||||
/* Create the geopoly object from test data above */
|
||||
CREATE VIRTUAL TABLE geo1 USING geopoly(type,clr);
|
||||
INSERT INTO geo1(_shape,type,clr)
|
||||
SELECT geopoly_xform(jshape,A,B,-B,A,xoff,yoff), basis.name, xform.clr
|
||||
FROM basis, xform, xyoff
|
||||
WHERE xyoff.id1=basis.rowid AND xyoff.id2=xform.rowid;
|
||||
|
||||
|
||||
/* Query polygon */
|
||||
CREATE TEMP TABLE querypoly(poly JSON, clr TEXT);
|
||||
INSERT INTO querypoly(clr, poly) VALUES
|
||||
('orange', '[[300,300],[400,350],[500,250],[480,500],[400,480],[300,550],[280,450],[320,400],[280,350],[300,300]]');
|
||||
|
||||
/* Generate the HTML */
|
||||
.print '<html>'
|
||||
.print '<h1>Everything</h1>'
|
||||
.print '<svg width="1000" height="800" style="border:1px solid black">'
|
||||
SELECT geopoly_svg(_shape,
|
||||
printf('style="fill:none;stroke:%s;stroke-width:1"',clr)
|
||||
)
|
||||
FROM geo1;
|
||||
SELECT geopoly_svg(poly,
|
||||
printf('style="fill:%s;fill-opacity:0.5;"',clr)
|
||||
)
|
||||
FROM querypoly;
|
||||
.print '</svg>'
|
||||
|
||||
.print '<h1>Overlap Query</h1>'
|
||||
.print '<pre>'
|
||||
.print 'SELECT *'
|
||||
.print ' FROM geo1, querypoly'
|
||||
.print ' WHERE geopoly_overlap(_shape, poly);'
|
||||
.print
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT geopoly_svg(_shape,
|
||||
printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
|
||||
)
|
||||
FROM geo1, querypoly
|
||||
WHERE geopoly_overlap(_shape, poly);
|
||||
.print '</pre>'
|
||||
.print '<svg width="1000" height="800" style="border:1px solid black">'
|
||||
SELECT geopoly_svg(_shape,
|
||||
printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
|
||||
)
|
||||
FROM geo1, querypoly
|
||||
WHERE geopoly_overlap(_shape, poly);
|
||||
SELECT geopoly_svg(poly,
|
||||
printf('style="fill:%s;fill-opacity:0.5;"',clr)
|
||||
)
|
||||
FROM querypoly;
|
||||
.print '</svg>'
|
||||
|
||||
.print '<h1>Overlap Query And Result Bounding Box</h1>'
|
||||
.print '<svg width="1000" height="800" style="border:1px solid black">'
|
||||
SELECT geopoly_svg(_shape,
|
||||
printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
|
||||
)
|
||||
FROM geo1, querypoly
|
||||
WHERE geopoly_overlap(_shape, poly);
|
||||
SELECT geopoly_svg(geopoly_bbox(poly),
|
||||
'style="fill:none;stroke:black;stroke-width:3"'
|
||||
)
|
||||
FROM querypoly;
|
||||
SELECT geopoly_svg(poly,
|
||||
printf('style="fill:%s;fill-opacity:0.5;"',clr)
|
||||
)
|
||||
FROM querypoly;
|
||||
SELECT geopoly_svg(geopoly_group_bbox(_shape),
|
||||
'style="fill:none;stroke:red;stroke-width:3"'
|
||||
)
|
||||
FROM geo1, querypoly
|
||||
WHERE geopoly_overlap(_shape, poly);
|
||||
.print '</svg>'
|
||||
|
||||
.print '<h1>Bounding-Box Overlap Query</h1>'
|
||||
.print '<svg width="1000" height="800" style="border:1px solid black">'
|
||||
SELECT geopoly_svg(_shape,
|
||||
printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
|
||||
),
|
||||
geopoly_svg(geopoly_bbox(_shape),
|
||||
'style="fill:none;stroke:black;stroke-width:1"'
|
||||
)
|
||||
FROM geo1, querypoly
|
||||
WHERE geopoly_overlap(geopoly_bbox(_shape), geopoly_bbox(poly));
|
||||
SELECT geopoly_svg(poly,
|
||||
printf('style="fill:%s;fill-opacity:0.5;"',clr)
|
||||
)
|
||||
FROM querypoly;
|
||||
SELECT geopoly_svg(geopoly_bbox(poly),
|
||||
'style="fill:none;stroke:black;stroke-width:3"'
|
||||
)
|
||||
FROM querypoly;
|
||||
.print '</svg>'
|
||||
|
||||
.print '<h1>Within Query</h1>'
|
||||
.print '<pre>'
|
||||
.print 'SELECT *'
|
||||
.print ' FROM geo1, querypoly'
|
||||
.print ' WHERE geopoly_within(_shape, poly);'
|
||||
.print
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT geopoly_svg(_shape,
|
||||
printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
|
||||
)
|
||||
FROM geo1, querypoly
|
||||
WHERE geopoly_within(_shape, poly);
|
||||
.print '</pre>'
|
||||
.print '<svg width="1000" height="800" style="border:1px solid black">'
|
||||
SELECT geopoly_svg(_shape,
|
||||
printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
|
||||
)
|
||||
FROM geo1, querypoly
|
||||
WHERE geopoly_within(_shape, poly);
|
||||
SELECT geopoly_svg(poly,
|
||||
printf('style="fill:%s;fill-opacity:0.5;"',clr)
|
||||
)
|
||||
FROM querypoly;
|
||||
.print '</svg>'
|
||||
|
||||
.print '<h1>Bounding-Box WITHIN Query</h1>'
|
||||
.print '<svg width="1000" height="800" style="border:1px solid black">'
|
||||
SELECT geopoly_svg(_shape,
|
||||
printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
|
||||
),
|
||||
geopoly_svg(geopoly_bbox(_shape),
|
||||
'style="fill:none;stroke:black;stroke-width:1"'
|
||||
)
|
||||
FROM geo1, querypoly
|
||||
WHERE geopoly_within(geopoly_bbox(_shape), geopoly_bbox(poly));
|
||||
SELECT geopoly_svg(poly,
|
||||
printf('style="fill:%s;fill-opacity:0.5;"',clr)
|
||||
)
|
||||
FROM querypoly;
|
||||
SELECT geopoly_svg(geopoly_bbox(poly),
|
||||
'style="fill:none;stroke:black;stroke-width:3"'
|
||||
)
|
||||
FROM querypoly;
|
||||
.print '</svg>'
|
||||
|
||||
.print '<h1>Not Overlap Query</h1>'
|
||||
.print '<pre>'
|
||||
.print 'SELECT *'
|
||||
.print ' FROM geo1, querypoly'
|
||||
.print ' WHERE NOT geopoly_overlap(_shape, poly);'
|
||||
.print
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT geopoly_svg(_shape,
|
||||
printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
|
||||
)
|
||||
FROM geo1, querypoly
|
||||
WHERE NOT geopoly_overlap(_shape, poly);
|
||||
.print '</pre>'
|
||||
.print '<svg width="1000" height="800" style="border:1px solid black">'
|
||||
SELECT geopoly_svg(_shape,
|
||||
printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
|
||||
)
|
||||
FROM geo1, querypoly
|
||||
WHERE NOT geopoly_overlap(_shape, poly);
|
||||
SELECT geopoly_svg(poly,
|
||||
printf('style="fill:%s;fill-opacity:0.5;"',clr)
|
||||
)
|
||||
FROM querypoly;
|
||||
.print '</svg>'
|
||||
|
||||
.print '<h1>Not Within Query</h1>'
|
||||
.print '<pre>'
|
||||
.print 'SELECT *'
|
||||
.print ' FROM geo1, querypoly'
|
||||
.print ' WHERE NOT geopoly_within(_shape, poly);'
|
||||
.print
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT geopoly_svg(_shape,
|
||||
printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
|
||||
)
|
||||
FROM geo1, querypoly
|
||||
WHERE NOT geopoly_within(_shape, poly);
|
||||
.print '</pre>'
|
||||
.print '<svg width="1000" height="800" style="border:1px solid black">'
|
||||
SELECT geopoly_svg(_shape,
|
||||
printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
|
||||
)
|
||||
FROM geo1, querypoly
|
||||
WHERE NOT geopoly_within(_shape, poly);
|
||||
SELECT geopoly_svg(poly,
|
||||
printf('style="fill:%s;fill-opacity:0.5;"',clr)
|
||||
)
|
||||
FROM querypoly;
|
||||
.print '</svg>'
|
||||
|
||||
.print '<h1>Color-Change For Overlapping Elements</h1>'
|
||||
BEGIN;
|
||||
UPDATE geo1
|
||||
SET clr=CASE WHEN rowid IN (SELECT geo1.rowid FROM geo1, querypoly
|
||||
WHERE geopoly_overlap(_shape,poly))
|
||||
THEN 'red' ELSE 'blue' END;
|
||||
.print '<svg width="1000" height="800" style="border:1px solid black">'
|
||||
SELECT geopoly_svg(_shape,
|
||||
printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
|
||||
)
|
||||
FROM geo1;
|
||||
SELECT geopoly_svg(poly,'style="fill:none;stroke:black;stroke-width:2"')
|
||||
FROM querypoly;
|
||||
ROLLBACK;
|
||||
.print '</svg>'
|
||||
|
||||
.print '<h1>Color-Change And Move Overlapping Elements</h1>'
|
||||
BEGIN;
|
||||
UPDATE geo1
|
||||
SET clr=CASE WHEN rowid IN (SELECT geo1.rowid FROM geo1, querypoly
|
||||
WHERE geopoly_overlap(_shape,poly))
|
||||
THEN 'red' ELSE '#76ccff' END;
|
||||
UPDATE geo1
|
||||
SET _shape=geopoly_xform(_shape,1,0,0,1,300,0)
|
||||
WHERE geopoly_overlap(_shape,(SELECT poly FROM querypoly));
|
||||
.print '<svg width="1000" height="800" style="border:1px solid black">'
|
||||
SELECT geopoly_svg(_shape,
|
||||
printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
|
||||
)
|
||||
FROM geo1;
|
||||
SELECT geopoly_svg(poly,'style="fill:none;stroke:black;stroke-width:2"')
|
||||
FROM querypoly;
|
||||
--ROLLBACK;
|
||||
.print '</svg>'
|
||||
|
||||
|
||||
.print '<h1>Overlap With Translated Query Polygon</h1>'
|
||||
UPDATE querypoly SET poly=geopoly_xform(poly,1,0,0,1,300,0);
|
||||
.print '<svg width="1000" height="800" style="border:1px solid black">'
|
||||
SELECT geopoly_svg(_shape,
|
||||
printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
|
||||
)
|
||||
FROM geo1
|
||||
WHERE geopoly_overlap(_shape,(SELECT poly FROM querypoly));
|
||||
SELECT geopoly_svg(poly,'style="fill:none;stroke:black;stroke-width:2"')
|
||||
FROM querypoly;
|
||||
ROLLBACK;
|
||||
.print '</svg>'
|
||||
|
||||
.print '<h1>Regular Polygons</h1>'
|
||||
.print '<svg width="1000" height="200" style="border:1px solid black">'
|
||||
SELECT geopoly_svg(geopoly_regular(100,100,40,3),'style="fill:none;stroke:red;stroke-width:1"');
|
||||
SELECT geopoly_svg(geopoly_regular(200,100,40,4),'style="fill:none;stroke:orange;stroke-width:1"');
|
||||
SELECT geopoly_svg(geopoly_regular(300,100,40,5),'style="fill:none;stroke:green;stroke-width:1"');
|
||||
SELECT geopoly_svg(geopoly_regular(400,100,40,6),'style="fill:none;stroke:blue;stroke-width:1"');
|
||||
SELECT geopoly_svg(geopoly_regular(500,100,40,7),'style="fill:none;stroke:purple;stroke-width:1"');
|
||||
SELECT geopoly_svg(geopoly_regular(600,100,40,8),'style="fill:none;stroke:red;stroke-width:1"');
|
||||
SELECT geopoly_svg(geopoly_regular(700,100,40,10),'style="fill:none;stroke:orange;stroke-width:1"');
|
||||
SELECT geopoly_svg(geopoly_regular(800,100,40,20),'style="fill:none;stroke:green;stroke-width:1"');
|
||||
SELECT geopoly_svg(geopoly_regular(900,100,40,30),'style="fill:none;stroke:blue;stroke-width:1"');
|
||||
.print '</svg>'
|
||||
|
||||
.print '</html>'
|
||||
Reference in new issue
Block a user