3 # The author disclaims copyright to this source code. In place of
4 # a legal notice, here is a blessing:
6 # May you do good and not evil.
7 # May you find forgiveness for yourself and forgive others.
8 # May you share freely, never taking more than you give.
10 #***********************************************************************
12 # The focus of this file is testing the r-tree extension.
15 if {![info exists testdir]} {
16 set testdir [file join [file dirname [info script]] .. .. test]
18 source [file join [file dirname [info script]] rtree_util.tcl]
19 source $testdir/tester.tcl
24 # rtree-1.*: Creating/destroying r-tree tables.
25 # rtree-2.*: Test the implicit constraints - unique rowid and
26 # (coord[N]<=coord[N+1]) for even values of N. Also
27 # automatic assigning of rowid values.
28 # rtree-3.*: Linear scans of r-tree data.
29 # rtree-4.*: Test INSERT
30 # rtree-5.*: Test DELETE
31 # rtree-6.*: Test UPDATE
32 # rtree-7.*: Test renaming an r-tree table.
33 # rtree-8.*: Test constrained scans of r-tree data.
35 # rtree-12.*: Test that on-conflict clauses are supported.
43 #----------------------------------------------------------------------------
44 # Test cases rtree-1.* test CREATE and DROP table statements.
47 # Test creating and dropping an rtree table.
50 execsql { CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2, y1, y2) }
53 execsql { SELECT name FROM sqlite_master ORDER BY name }
54 } {t1 t1_node t1_parent t1_rowid}
58 SELECT name FROM sqlite_master ORDER BY name;
62 # Test creating and dropping an rtree table with an odd name in
63 # an attached database.
66 file delete -force test2.db
68 ATTACH 'test2.db' AS aux;
69 CREATE VIRTUAL TABLE aux.'a" "b' USING rtree(ii, x1, x2, y1, y2);
73 execsql { SELECT name FROM sqlite_master ORDER BY name }
76 execsql { SELECT name FROM aux.sqlite_master ORDER BY name }
77 } {{a" "b} {a" "b_node} {a" "b_parent} {a" "b_rowid}}
80 DROP TABLE aux.'a" "b';
81 SELECT name FROM aux.sqlite_master ORDER BY name;
85 # Test that the logic for checking the number of columns specified
86 # for an rtree table. Acceptable values are odd numbers between 3 and
89 set cols [list i1 i2 i3 i4 i5 i6 i7 i8 i9 iA iB iC iD iE iF iG iH iI iJ iK]
90 for {set nCol 1} {$nCol<[llength $cols]} {incr nCol} {
92 set columns [join [lrange $cols 0 [expr {$nCol-1}]] ,]
95 if {$nCol%2 == 0} { set X {1 {Wrong number of columns for an rtree table}} }
96 if {$nCol < 3} { set X {1 {Too few columns for an rtree table}} }
97 if {$nCol > 11} { set X {1 {Too many columns for an rtree table}} }
99 do_test rtree-1.3.$nCol {
101 CREATE VIRTUAL TABLE t1 USING rtree($columns);
105 catchsql { DROP TABLE t1 }
108 # Like execsql except display output as integer where that can be
109 # done without loss of information.
111 proc execsql_intout {sql} {
113 foreach term [execsql $sql] {
114 regsub {\.0$} $term {} term
120 # Test that it is possible to open an existing database that contains
123 do_test rtree-1.4.1 {
125 CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2);
126 INSERT INTO t1 VALUES(1, 5.0, 10.0);
127 INSERT INTO t1 VALUES(2, 15.0, 20.0);
130 do_test rtree-1.4.2 {
133 execsql_intout { SELECT * FROM t1 ORDER BY ii }
135 do_test rtree-1.4.3 {
136 execsql { DROP TABLE t1 }
139 # Test that it is possible to create an r-tree table with ridiculous
142 do_test rtree-1.5.1 {
144 CREATE VIRTUAL TABLE t1 USING rtree("the key", "x dim.", "x2'dim");
145 INSERT INTO t1 VALUES(1, 2, 3);
146 SELECT "the key", "x dim.", "x2'dim" FROM t1;
149 do_test rtree-1.5.1 {
150 execsql { DROP TABLE t1 }
153 # Force the r-tree constructor to fail.
155 do_test rtree-1.6.1 {
156 execsql { CREATE TABLE t1_rowid(a); }
158 CREATE VIRTUAL TABLE t1 USING rtree("the key", "x dim.", "x2'dim");
160 } {1 {table "t1_rowid" already exists}}
161 do_test rtree-1.6.1 {
162 execsql { DROP TABLE t1_rowid }
165 #----------------------------------------------------------------------------
166 # Test cases rtree-2.*
168 do_test rtree-2.1.1 {
170 CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2, y1, y2);
175 do_test rtree-2.1.2 {
176 execsql { INSERT INTO t1 VALUES(NULL, 1, 3, 2, 4) }
177 execsql_intout { SELECT * FROM t1 }
179 do_test rtree-2.1.3 {
180 execsql { INSERT INTO t1 VALUES(NULL, 1, 3, 2, 4) }
181 execsql { SELECT rowid FROM t1 ORDER BY rowid }
183 do_test rtree-2.1.3 {
184 execsql { INSERT INTO t1 VALUES(NULL, 1, 3, 2, 4) }
185 execsql { SELECT ii FROM t1 ORDER BY ii }
188 do_test rtree-2.2.1 {
189 catchsql { INSERT INTO t1 VALUES(2, 1, 3, 2, 4) }
190 } {1 {constraint failed}}
191 do_test rtree-2.2.2 {
192 catchsql { INSERT INTO t1 VALUES(4, 1, 3, 4, 2) }
193 } {1 {constraint failed}}
194 do_test rtree-2.2.3 {
195 catchsql { INSERT INTO t1 VALUES(4, 3, 1, 2, 4) }
196 } {1 {constraint failed}}
197 do_test rtree-2.2.4 {
198 execsql { SELECT ii FROM t1 ORDER BY ii }
202 execsql { DROP TABLE t1 }
205 #----------------------------------------------------------------------------
206 # Test cases rtree-3.* test linear scans of r-tree table data. To test
207 # this we have to insert some data into an r-tree, but that is not the
208 # focus of these tests.
210 do_test rtree-3.1.1 {
212 CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2, y1, y2);
216 do_test rtree-3.1.2 {
218 INSERT INTO t1 VALUES(5, 1, 3, 2, 4);
222 do_test rtree-3.1.3 {
224 INSERT INTO t1 VALUES(6, 2, 6, 4, 8);
227 } {5 1 3 2 4 6 2 6 4 8}
229 # Test the constraint on the coordinates (c[i]<=c[i+1] where (i%2==0)):
230 do_test rtree-3.2.1 {
231 catchsql { INSERT INTO t1 VALUES(7, 2, 6, 4, 3) }
232 } {1 {constraint failed}}
233 do_test rtree-3.2.2 {
234 catchsql { INSERT INTO t1 VALUES(8, 2, 6, 3, 3) }
237 #----------------------------------------------------------------------------
238 # Test cases rtree-5.* test DELETE operations.
240 do_test rtree-5.1.1 {
241 execsql { CREATE VIRTUAL TABLE t2 USING rtree(ii, x1, x2) }
243 do_test rtree-5.1.2 {
245 INSERT INTO t2 VALUES(1, 10, 20);
246 INSERT INTO t2 VALUES(2, 30, 40);
247 INSERT INTO t2 VALUES(3, 50, 60);
248 SELECT * FROM t2 ORDER BY ii;
250 } {1 10 20 2 30 40 3 50 60}
251 do_test rtree-5.1.3 {
253 DELETE FROM t2 WHERE ii=2;
254 SELECT * FROM t2 ORDER BY ii;
257 do_test rtree-5.1.4 {
259 DELETE FROM t2 WHERE ii=1;
260 SELECT * FROM t2 ORDER BY ii;
263 do_test rtree-5.1.5 {
265 DELETE FROM t2 WHERE ii=3;
266 SELECT * FROM t2 ORDER BY ii;
269 do_test rtree-5.1.6 {
270 execsql { SELECT * FROM t2_rowid }
273 #----------------------------------------------------------------------------
274 # Test cases rtree-5.* test UPDATE operations.
276 do_test rtree-6.1.1 {
277 execsql { CREATE VIRTUAL TABLE t3 USING rtree(ii, x1, x2, y1, y2) }
279 do_test rtree-6.1.2 {
281 INSERT INTO t3 VALUES(1, 2, 3, 4, 5);
286 do_test rtree-6.1.3 {
287 execsql { UPDATE t3 SET ii = 2 }
288 execsql_intout { SELECT * FROM t3 }
291 #----------------------------------------------------------------------------
292 # Test cases rtree-7.* test rename operations.
294 do_test rtree-7.1.1 {
296 CREATE VIRTUAL TABLE t4 USING rtree(ii, x1, x2, y1, y2, z1, z2);
297 INSERT INTO t4 VALUES(1, 2, 3, 4, 5, 6, 7);
300 do_test rtree-7.1.2 {
301 execsql { ALTER TABLE t4 RENAME TO t5 }
302 execsql_intout { SELECT * FROM t5 }
304 do_test rtree-7.1.3 {
307 execsql_intout { SELECT * FROM t5 }
309 do_test rtree-7.1.4 {
310 execsql { ALTER TABLE t5 RENAME TO 'raisara "one"'''}
311 execsql_intout { SELECT * FROM "raisara ""one""'" }
313 do_test rtree-7.1.5 {
314 execsql_intout { SELECT * FROM 'raisara "one"''' }
316 do_test rtree-7.1.6 {
317 execsql { ALTER TABLE "raisara ""one""'" RENAME TO "abc 123" }
318 execsql_intout { SELECT * FROM "abc 123" }
320 do_test rtree-7.1.7 {
323 execsql_intout { SELECT * FROM "abc 123" }
326 # An error midway through a rename operation.
327 do_test rtree-7.2.1 {
329 CREATE TABLE t4_node(a);
331 catchsql { ALTER TABLE "abc 123" RENAME TO t4 }
332 } {1 {SQL logic error or missing database}}
333 do_test rtree-7.2.2 {
334 execsql_intout { SELECT * FROM "abc 123" }
336 do_test rtree-7.2.3 {
339 CREATE TABLE t4_rowid(a);
341 catchsql { ALTER TABLE "abc 123" RENAME TO t4 }
342 } {1 {SQL logic error or missing database}}
343 do_test rtree-7.2.4 {
346 execsql_intout { SELECT * FROM "abc 123" }
348 do_test rtree-7.2.5 {
349 execsql { DROP TABLE t4_rowid }
350 execsql { ALTER TABLE "abc 123" RENAME TO t4 }
351 execsql_intout { SELECT * FROM t4 }
355 #----------------------------------------------------------------------------
356 # Test cases rtree-8.*
359 # Test that the function to determine if a leaf cell is part of the
361 do_test rtree-8.1.1 {
363 CREATE VIRTUAL TABLE t6 USING rtree(ii, x1, x2);
364 INSERT INTO t6 VALUES(1, 3, 7);
365 INSERT INTO t6 VALUES(2, 4, 6);
368 do_test rtree-8.1.2 { execsql { SELECT ii FROM t6 WHERE x1>2 } } {1 2}
369 do_test rtree-8.1.3 { execsql { SELECT ii FROM t6 WHERE x1>3 } } {2}
370 do_test rtree-8.1.4 { execsql { SELECT ii FROM t6 WHERE x1>4 } } {}
371 do_test rtree-8.1.5 { execsql { SELECT ii FROM t6 WHERE x1>5 } } {}
372 do_test rtree-8.1.6 { execsql { SELECT ii FROM t6 WHERE x1<3 } } {}
373 do_test rtree-8.1.7 { execsql { SELECT ii FROM t6 WHERE x1<4 } } {1}
374 do_test rtree-8.1.8 { execsql { SELECT ii FROM t6 WHERE x1<5 } } {1 2}
376 #----------------------------------------------------------------------------
377 # Test cases rtree-9.*
379 # Test that ticket #3549 is fixed.
382 CREATE TABLE foo (id INTEGER PRIMARY KEY);
383 CREATE VIRTUAL TABLE bar USING rtree (id, minX, maxX, minY, maxY);
384 INSERT INTO foo VALUES (null);
385 INSERT INTO foo SELECT null FROM foo;
386 INSERT INTO foo SELECT null FROM foo;
387 INSERT INTO foo SELECT null FROM foo;
388 INSERT INTO foo SELECT null FROM foo;
389 INSERT INTO foo SELECT null FROM foo;
390 INSERT INTO foo SELECT null FROM foo;
391 DELETE FROM foo WHERE id > 40;
392 INSERT INTO bar SELECT NULL, 0, 0, 0, 0 FROM foo;
396 # This used to crash.
399 SELECT count(*) FROM bar b1, bar b2, foo s1 WHERE s1.id = b1.id;
404 SELECT count(*) FROM bar b1, bar b2, foo s1
405 WHERE b1.minX <= b2.maxX AND s1.id = b1.id;
409 #-------------------------------------------------------------------------
410 # Ticket #3970: Check that the error message is meaningful when a
411 # keyword is used as a column name.
414 catchsql { CREATE VIRTUAL TABLE t7 USING rtree(index, x1, y1, x2, y2) }
415 } {1 {near "index": syntax error}}
417 #-------------------------------------------------------------------------
418 # Test last_insert_rowid().
422 CREATE VIRTUAL TABLE t8 USING rtree(idx, x1, x2, y1, y2);
423 INSERT INTO t8 VALUES(1, 1.0, 1.0, 2.0, 2.0);
424 SELECT last_insert_rowid();
429 INSERT INTO t8 VALUES(NULL, 1.0, 1.0, 2.0, 2.0);
430 SELECT last_insert_rowid();
434 #-------------------------------------------------------------------------
435 # Test on-conflict clause handling.
438 do_execsql_test 12.0 {
439 CREATE VIRTUAL TABLE t1 USING rtree_i32(idx, x1, x2, y1, y2);
440 INSERT INTO t1 VALUES(1, 1, 2, 3, 4);
441 INSERT INTO t1 VALUES(2, 2, 3, 4, 5);
442 INSERT INTO t1 VALUES(3, 3, 4, 5, 6);
444 CREATE TABLE source(idx, x1, x2, y1, y2);
445 INSERT INTO source VALUES(5, 8, 8, 8, 8);
446 INSERT INTO source VALUES(2, 7, 7, 7, 7);
450 foreach {tn sql_template testdata} {
451 1 "INSERT %CONF% INTO t1 VALUES(2, 7, 7, 7, 7)" {
452 ROLLBACK 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
453 ABORT 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
454 IGNORE 0 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
455 FAIL 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
456 REPLACE 0 0 {1 1 2 3 4 2 7 7 7 7 3 3 4 5 6 4 4 5 6 7}
459 2 "INSERT %CONF% INTO t1 SELECT * FROM source" {
460 ROLLBACK 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
461 ABORT 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
462 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}
463 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}
464 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}
467 3 "UPDATE %CONF% t1 SET idx = 2 WHERE idx = 4" {
468 ROLLBACK 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
469 ABORT 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
470 IGNORE 1 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
471 FAIL 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
472 REPLACE 1 0 {1 1 2 3 4 2 4 5 6 7 3 3 4 5 6}
475 3 "UPDATE %CONF% t1 SET idx = ((idx+1)%5)+1 WHERE idx > 2" {
476 ROLLBACK 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
477 ABORT 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
478 IGNORE 1 0 {1 1 2 3 4 2 2 3 4 5 4 4 5 6 7 5 3 4 5 6}
479 FAIL 1 1 {1 1 2 3 4 2 2 3 4 5 4 4 5 6 7 5 3 4 5 6}
480 REPLACE 1 0 {1 4 5 6 7 2 2 3 4 5 5 3 4 5 6}
483 4 "INSERT %CONF% INTO t1 VALUES(2, 7, 6, 7, 7)" {
484 ROLLBACK 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
485 ABORT 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
486 IGNORE 0 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
487 FAIL 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
488 REPLACE 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
492 foreach {mode uses error data} $testdata {
493 db_restore_and_reopen
495 set sql [string map [list %CONF% "OR $mode"] $sql_template]
496 set testname "12.$tn.[string tolower $mode]"
500 INSERT INTO t1 VALUES(4, 4, 5, 6, 7);
504 set res(1) {1 {constraint failed}}
505 do_catchsql_test $testname.1 $sql $res($error)
506 do_test $testname.2 [list sql_uses_stmt db $sql] $uses
507 do_execsql_test $testname.3 { SELECT * FROM t1 ORDER BY idx } $data
509 do_test $testname.4 { rtree_check db t1 } 0