1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 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 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 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 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 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 refered 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
|