101ed72f2Sdan# 2021 September 13 201ed72f2Sdan# 301ed72f2Sdan# The author disclaims copyright to this source code. In place of 401ed72f2Sdan# a legal notice, here is a blessing: 501ed72f2Sdan# 601ed72f2Sdan# May you do good and not evil. 701ed72f2Sdan# May you find forgiveness for yourself and forgive others. 801ed72f2Sdan# May you share freely, never taking more than you give. 901ed72f2Sdan# 1001ed72f2Sdan#*********************************************************************** 1101ed72f2Sdan# 1201ed72f2Sdan# The focus of this file is testing the r-tree extension. 1301ed72f2Sdan# 1401ed72f2Sdan 1501ed72f2Sdanif {![info exists testdir]} { 1601ed72f2Sdan set testdir [file join [file dirname [info script]] .. .. test] 1701ed72f2Sdan} 1801ed72f2Sdansource [file join [file dirname [info script]] rtree_util.tcl] 1901ed72f2Sdansource $testdir/tester.tcl 2001ed72f2Sdanset testprefix rtreedoc2 2101ed72f2Sdan 2201ed72f2Sdanifcapable !rtree { 2301ed72f2Sdan finish_test 2401ed72f2Sdan return 2501ed72f2Sdan} 2601ed72f2Sdan 2701ed72f2Sdan#------------------------------------------------------------------------- 2801ed72f2Sdan#------------------------------------------------------------------------- 2901ed72f2Sdan# Section 6 of documentation. 3001ed72f2Sdan#------------------------------------------------------------------------- 3101ed72f2Sdan#------------------------------------------------------------------------- 3201ed72f2Sdanset testprefix rtreedoc2-1 3301ed72f2Sdan 3401ed72f2Sdan# EVIDENCE-OF: R-35254-48865 A call to one of the above APIs creates a 3501ed72f2Sdan# new SQL function named by the second parameter (zQueryFunc or zGeom). 3601ed72f2Sdan# 3701ed72f2Sdan# [register_circle_geom db] registers new geometry callback "Qcircle" 3801ed72f2Sdan# and legacy implementation "circle". Test that these do actually appear. 3901ed72f2Sdan# 4001ed72f2Sdando_execsql_test 1.1.0 { 4101ed72f2Sdan SELECT * FROM pragma_function_list WHERE name IN('circle', 'qcircle'); 4201ed72f2Sdan} { 4301ed72f2Sdan} 4401ed72f2Sdando_test 1.1 { 4501ed72f2Sdan register_circle_geom db 4601ed72f2Sdan} {SQLITE_OK} 4701ed72f2Sdando_execsql_test 1.1.2 { 4801ed72f2Sdan SELECT * FROM pragma_function_list WHERE name = 'circle' AND enc='utf8'; 4901ed72f2Sdan} { 5001ed72f2Sdan circle 0 s utf8 -1 0 5101ed72f2Sdan} 5201ed72f2Sdando_execsql_test 1.1.3 { 5301ed72f2Sdan SELECT * FROM pragma_function_list WHERE name = 'qcircle' AND enc='utf8'; 5401ed72f2Sdan} { 5501ed72f2Sdan qcircle 0 s utf8 -1 0 5601ed72f2Sdan} 5701ed72f2Sdan 5801ed72f2Sdando_execsql_test 1.2.0 { SELECT circle(1, 2, 3); } {{}} 5901ed72f2Sdando_execsql_test 1.2.1 { SELECT qcircle(1, 2, 3); } {{}} 6001ed72f2Sdan 6101ed72f2Sdan# EVIDENCE-OF: R-61427-46983 6201ed72f2Sdando_execsql_test 1.3.0 { 6301ed72f2Sdan CREATE VIRTUAL TABLE demo_index USING rtree(id, x1,x2, y1,y2); 6401ed72f2Sdan INSERT INTO demo_index VALUES(10, 45,45, 24,24); 6501ed72f2Sdan INSERT INTO demo_index VALUES(20, 50,50, 28,28); 6601ed72f2Sdan INSERT INTO demo_index VALUES(30, 43,43, 22,22); 6701ed72f2Sdan} 6801ed72f2Sdando_execsql_test 1.3.1 { 6901ed72f2Sdan SELECT id FROM demo_index WHERE id MATCH circle(45.3, 22.9, 5.0) 7001ed72f2Sdan} {10 30} 7101ed72f2Sdan 7201ed72f2Sdan# EVIDENCE-OF: R-16907-50223 The SQL syntax for custom queries is the 7301ed72f2Sdan# same regardless of which interface, sqlite3_rtree_geometry_callback() 7401ed72f2Sdan# or sqlite3_rtree_query_callback(), is used to register the SQL 7501ed72f2Sdan# function. 7601ed72f2Sdando_execsql_test 1.3.2 { 7701ed72f2Sdan SELECT id FROM demo_index WHERE id MATCH qcircle(45.3, 22.9, 5.0, 1) 7801ed72f2Sdan} {10 30} 7901ed72f2Sdan 8001ed72f2Sdan 8101ed72f2Sdan# EVIDENCE-OF: R-59634-51678 When that SQL function appears on the 8201ed72f2Sdan# right-hand side of the MATCH operator and the left-hand side of the 8301ed72f2Sdan# MATCH operator is any column in the R*Tree virtual table, then the 8401ed72f2Sdan# callback defined by the third argument (xQueryFunc or xGeom) is 8501ed72f2Sdan# invoked to determine if a particular object or subtree overlaps the 8601ed72f2Sdan# desired region. 8701ed72f2Sdanproc box_geom {args} { 8801ed72f2Sdan lappend ::box_geom [concat [lindex $args 0] [lrange $args 2 end-1]] 8901ed72f2Sdan return "" 9001ed72f2Sdan} 9101ed72f2Sdanregister_box_geom db box_geom 9201ed72f2Sdanset box_geom [list] 9301ed72f2Sdando_execsql_test 1.3.2 { 9401ed72f2Sdan SELECT id FROM demo_index WHERE id MATCH box(43,46, 21,25); 9501ed72f2Sdan} {10 30} 9601ed72f2Sdando_test 1.3.3 { 9701ed72f2Sdan set ::box_geom 9801ed72f2Sdan} [list {*}{ 9901ed72f2Sdan {box {43.0 46.0 21.0 25.0} {45.0 45.0 24.0 24.0}} 10001ed72f2Sdan {box {43.0 46.0 21.0 25.0} {50.0 50.0 28.0 28.0}} 10101ed72f2Sdan {box {43.0 46.0 21.0 25.0} {43.0 43.0 22.0 22.0}} 10201ed72f2Sdan}] 10301ed72f2Sdan 10401ed72f2Sdan#------------------------------------------------------------------------- 10501ed72f2Sdan#------------------------------------------------------------------------- 10601ed72f2Sdan# Section 6 of documentation. 10701ed72f2Sdan#------------------------------------------------------------------------- 10801ed72f2Sdan#------------------------------------------------------------------------- 10901ed72f2Sdanset testprefix rtreedoc2-2 11001ed72f2Sdan 11101ed72f2Sdan# EVIDENCE-OF: R-02424-24769 The second argument is the number of 11201ed72f2Sdan# coordinates in each r-tree entry, and is always the same for any given 11301ed72f2Sdan# R*Tree. 11401ed72f2Sdan# 11501ed72f2Sdan# EVIDENCE-OF: R-40260-16838 The number of coordinates is 2 for a 11601ed72f2Sdan# 1-dimensional R*Tree, 4 for a 2-dimensional R*Tree, 6 for a 11701ed72f2Sdan# 3-dimensional R*Tree, and so forth. 11801ed72f2Sdan# 11901ed72f2Sdan# The second argument refered to above is the length of the list passed 12001ed72f2Sdan# as the 3rd parameter to the Tcl script. 12101ed72f2Sdan# 12201ed72f2Sdando_execsql_test 1.0 { 12301ed72f2Sdan CREATE VIRTUAL TABLE rt1 USING rtree(id, x1,x2); 12401ed72f2Sdan CREATE VIRTUAL TABLE rt2 USING rtree(id, x1,x2, y1,y2); 12501ed72f2Sdan CREATE VIRTUAL TABLE rt3 USING rtree(id, x1,x2, y1,y2, z1,z2); 12601ed72f2Sdan 12701ed72f2Sdan INSERT INTO rt1 DEFAULT VALUES; 12801ed72f2Sdan INSERT INTO rt2 DEFAULT VALUES; 12901ed72f2Sdan INSERT INTO rt3 DEFAULT VALUES; 13001ed72f2Sdan} 13101ed72f2Sdanforeach {tn tbl nCoord} { 13201ed72f2Sdan 1 rt1 2 13301ed72f2Sdan 2 rt2 4 13401ed72f2Sdan 3 rt3 6 13501ed72f2Sdan} { 13601ed72f2Sdan set ::box_geom [list] 13701ed72f2Sdan do_catchsql_test 1.$tn.1 " 13801ed72f2Sdan SELECT id FROM $tbl WHERE id MATCH box(); 13901ed72f2Sdan " {1 {SQL logic error}} 14001ed72f2Sdan 14101ed72f2Sdan do_test 1.$tn.2 { 14201ed72f2Sdan llength [lindex $::box_geom 0 2] 14301ed72f2Sdan } $nCoord 14401ed72f2Sdan} 14501ed72f2Sdan 14601ed72f2Sdan# EVIDENCE-OF: R-28051-48608 If xGeom returns anything other than 14701ed72f2Sdan# SQLITE_OK, then the r-tree query will abort with an error. 14801ed72f2Sdanproc box_geom {args} { 14901ed72f2Sdan error "an error!" 15001ed72f2Sdan} 15101ed72f2Sdando_catchsql_test 2.0 { 15201ed72f2Sdan SELECT * FROM rt2 WHERE id MATCH box(22,23, 24,25); 15301ed72f2Sdan} {1 {SQL logic error}} 15401ed72f2Sdan 15501ed72f2Sdando_execsql_test 3.0 { 15601ed72f2Sdan INSERT INTO rt1 VALUES(10, 10, 10); 15701ed72f2Sdan INSERT INTO rt1 VALUES(11, 11, 11); 15801ed72f2Sdan INSERT INTO rt1 VALUES(12, 12, 12); 15901ed72f2Sdan INSERT INTO rt1 VALUES(13, 13, 13); 16001ed72f2Sdan INSERT INTO rt1 VALUES(14, 14, 14); 16101ed72f2Sdan} 16201ed72f2Sdan 16301ed72f2Sdan# EVIDENCE-OF: R-53759-57366 The exact same sqlite3_rtree_geometry 16401ed72f2Sdan# structure is used for every callback for same MATCH operator in the 16501ed72f2Sdan# same query. 16601ed72f2Sdanproc box_geom {args} { 16701ed72f2Sdan lappend ::ptr_list [lindex $args 4] 16801ed72f2Sdan return 0 16901ed72f2Sdan} 17001ed72f2Sdanset ::ptr_list [list] 17101ed72f2Sdando_execsql_test 3.1 { 17201ed72f2Sdan SELECT * FROM rt1 WHERE id MATCH box(1,1); 17301ed72f2Sdan} 17401ed72f2Sdando_test 3.2 { 17501ed72f2Sdan set val [lindex $::ptr_list 0] 17601ed72f2Sdan foreach p $::ptr_list { 17701ed72f2Sdan if {$p!=$val} {error "pointer mismatch"} 17801ed72f2Sdan } 17901ed72f2Sdan} {} 18001ed72f2Sdan 18101ed72f2Sdan# EVIDENCE-OF: R-60247-35692 The contents of the sqlite3_rtree_geometry 18201ed72f2Sdan# structure are initialized by SQLite but are not subsequently modified. 18301ed72f2Sdanproc box_geom {args} { 18401ed72f2Sdan lappend ::box_geom [concat [lindex $args 0] [lrange $args 2 end-1]] 18501ed72f2Sdan if {[llength $::box_geom]==3} { 18601ed72f2Sdan return "zero" 18701ed72f2Sdan } 18801ed72f2Sdan return "" 18901ed72f2Sdan} 19001ed72f2Sdanset ::box_geom [list] 19101ed72f2Sdando_catchsql_test 3.2 { 19201ed72f2Sdan SELECT * FROM rt1 WHERE id MATCH box(1,1); 19301ed72f2Sdan} {1 {SQL logic error}} 19401ed72f2Sdando_test 3.3 { 19501ed72f2Sdan set ::box_geom 19601ed72f2Sdan} [list {*}{ 19701ed72f2Sdan {box {1.0 1.0} {0.0 0.0}} 19801ed72f2Sdan {box {1.0 1.0} {10.0 10.0}} 19901ed72f2Sdan {box {1.0 1.0} {11.0 11.0}} 20001ed72f2Sdan {box 0.0 {12.0 12.0}} 20101ed72f2Sdan}] 20201ed72f2Sdan 20301ed72f2Sdan# EVIDENCE-OF: R-31246-29731 The pContext member of the 20401ed72f2Sdan# sqlite3_rtree_geometry structure is always set to a copy of the 20501ed72f2Sdan# pContext argument passed to sqlite3_rtree_geometry_callback() when the 20601ed72f2Sdan# callback is registered. 20701ed72f2Sdanreset_db 20801ed72f2Sdando_execsql_test 4.0 { 20901ed72f2Sdan CREATE VIRTUAL TABLE r1 USING rtree(id, minX,maxX, minY,maxY); 21001ed72f2Sdan WITH s(i) AS ( 21101ed72f2Sdan VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<120 21201ed72f2Sdan ) 21301ed72f2Sdan INSERT INTO r1 SELECT i,i,i+1, 200,201 FROM s; 21401ed72f2Sdan} 21501ed72f2Sdanset ctx [register_box_geom db box_geom] 21601ed72f2Sdanset ::box_geom [list] 21701ed72f2Sdanproc box_geom {args} { 21801ed72f2Sdan lappend ::box_geom [lindex $args 1] 21901ed72f2Sdan return "" 22001ed72f2Sdan} 22101ed72f2Sdando_execsql_test 4.1 { 22201ed72f2Sdan SELECT count(*) FROM r1 WHERE id MATCH box(0,150,199,201) 22301ed72f2Sdan} 120 22401ed72f2Sdando_test 4.2 { 22501ed72f2Sdan foreach g $::box_geom { 22601ed72f2Sdan if {$g!=$ctx} {error "pointer mismatch"} 22701ed72f2Sdan } 22801ed72f2Sdan} {} 22901ed72f2Sdan 23001ed72f2Sdan# EVIDENCE-OF: R-09904-19077 The aParam[] array (size nParam) contains 23101ed72f2Sdan# the parameter values passed to the SQL function on the right-hand side 23201ed72f2Sdan# of the MATCH operator. 23301ed72f2Sdanproc box_geom {args} { 23401ed72f2Sdan set ::box_geom [lindex $args 2] 23501ed72f2Sdan} 23601ed72f2Sdanforeach {tn q vals} { 23701ed72f2Sdan 1 "SELECT count(*) FROM r1 WHERE id MATCH box(1,2,3)" {1.0 2.0 3.0} 23801ed72f2Sdan 2 "SELECT count(*) FROM r1 WHERE id MATCH box(10001)" {10001.0} 23901ed72f2Sdan 3 "SELECT count(*) FROM r1 WHERE id MATCH box(-10001)" {-10001.0} 24001ed72f2Sdan} { 24101ed72f2Sdan do_catchsql_test 5.$tn.1 $q {1 {SQL logic error}} 24201ed72f2Sdan do_test 5.$tn.2 { set ::box_geom } $vals 24301ed72f2Sdan} 24401ed72f2Sdan 245*5488e082Sdando_execsql_test 5.0 { 246*5488e082Sdan CREATE VIRTUAL TABLE myrtree USING rtree(id, x1,x2); 247*5488e082Sdan INSERT INTO myrtree VALUES(1, 1, 1); 248*5488e082Sdan INSERT INTO myrtree VALUES(2, 2, 2); 249*5488e082Sdan INSERT INTO myrtree VALUES(3, 3, 3); 250*5488e082Sdan} 25101ed72f2Sdan 252*5488e082Sdan# EVIDENCE-OF: R-44448-00687 The pUser and xDelUser members of the 253*5488e082Sdan# sqlite3_rtree_geometry structure are initially set to NULL. 254*5488e082Sdanset ::box_geom_calls 0 255*5488e082Sdanproc box_geom {args} { 256*5488e082Sdan incr ::box_geom_calls 257*5488e082Sdan return user_is_zero 258*5488e082Sdan} 259*5488e082Sdando_execsql_test 5.1.1 { 260*5488e082Sdan SELECT * FROM myrtree WHERE id MATCH box(4, 5); 261*5488e082Sdan} 262*5488e082Sdando_test 5.1.2 { set ::box_geom_calls } 3 263*5488e082Sdan 264*5488e082Sdan 265*5488e082Sdan# EVIDENCE-OF: R-55837-00155 The pUser variable may be set by the 266*5488e082Sdan# callback implementation to any arbitrary value that may be useful to 267*5488e082Sdan# subsequent invocations of the callback within the same query (for 268*5488e082Sdan# example, a pointer to a complicated data structure used to test for 269*5488e082Sdan# region intersection). 270*5488e082Sdan# 271*5488e082Sdan# EVIDENCE-OF: R-34745-08839 If the xDelUser variable is set to a 272*5488e082Sdan# non-NULL value, then after the query has finished running SQLite 273*5488e082Sdan# automatically invokes it with the value of the pUser variable as the 274*5488e082Sdan# only argument. 275*5488e082Sdan# 276*5488e082Sdanset ::box_geom_calls 0 277*5488e082Sdanproc box_geom {args} { 278*5488e082Sdan incr ::box_geom_calls 279*5488e082Sdan switch -- $::box_geom_calls { 280*5488e082Sdan 1 { 281*5488e082Sdan return user_is_zero 282*5488e082Sdan } 283*5488e082Sdan 2 { 284*5488e082Sdan return [list user box_geom_finalizer] 285*5488e082Sdan } 286*5488e082Sdan } 287*5488e082Sdan return "" 288*5488e082Sdan} 289*5488e082Sdanproc box_geom_finalizer {} { 290*5488e082Sdan set ::box_geom_finalizer "::box_geom_calls is $::box_geom_calls" 291*5488e082Sdan} 292*5488e082Sdando_execsql_test 5.1.1 { 293*5488e082Sdan SELECT * FROM myrtree WHERE id MATCH box(4, 5); 294*5488e082Sdan} 295*5488e082Sdando_test 5.1.2 { set ::box_geom_calls } 3 296*5488e082Sdando_test 5.1.3 { 297*5488e082Sdan set ::box_geom_finalizer 298*5488e082Sdan} {::box_geom_calls is 3} 299*5488e082Sdan 300*5488e082Sdan 301*5488e082Sdan# EVIDENCE-OF: R-28176-28813 The xGeom callback always does a 302*5488e082Sdan# depth-first search of the r-tree. 303*5488e082Sdan# 304*5488e082Sdan# For a breadth first search, final test case would return "B L" only. 305*5488e082Sdan# 306*5488e082Sdando_execsql_test 6.0 { 307*5488e082Sdan CREATE VIRTUAL TABLE xyz USING rtree(x, x1,x2, y1,y2); 308*5488e082Sdan WITH s(i) AS ( 309*5488e082Sdan VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<15 310*5488e082Sdan ) 311*5488e082Sdan INSERT INTO xyz SELECT NULL, one.i,one.i+1, two.i,two.i+1 FROM s one, s two; 312*5488e082Sdan} 313*5488e082Sdando_execsql_test 6.1 { 314*5488e082Sdan SELECT count(*) FROM xyz_node 315*5488e082Sdan} {10} 316*5488e082Sdanproc box_geom {args} { 317*5488e082Sdan set coords [lindex $args 3] 318*5488e082Sdan set area [expr { 319*5488e082Sdan ([lindex $coords 1]-[lindex $coords 0]) * 320*5488e082Sdan ([lindex $coords 3]-[lindex $coords 2]) 321*5488e082Sdan }] 322*5488e082Sdan if {$area==1} { 323*5488e082Sdan lappend ::box_geom_calls L 324*5488e082Sdan } else { 325*5488e082Sdan lappend ::box_geom_calls B 326*5488e082Sdan } 327*5488e082Sdan} 328*5488e082Sdanset ::box_geom_calls [list] 329*5488e082Sdando_execsql_test 6.2 { 330*5488e082Sdan SELECT count(*) FROM xyz WHERE x MATCH box(0,20,0,20) 331*5488e082Sdan} 225 332*5488e082Sdando_test 6.3 { 333*5488e082Sdan set prev "" 334*5488e082Sdan set box_calls [list] 335*5488e082Sdan foreach c $::box_geom_calls { 336*5488e082Sdan if {$c!=$prev} { 337*5488e082Sdan lappend ::box_calls $c 338*5488e082Sdan set prev $c 339*5488e082Sdan } 340*5488e082Sdan } 341*5488e082Sdan set ::box_calls 342*5488e082Sdan} {B L B L B L B L B L B L B L B L B L} 34301ed72f2Sdan 34401ed72f2Sdan 34501ed72f2Sdanfinish_test 34601ed72f2Sdan 347