rtreedoc2.test 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. # 2021 September 13
  2. #
  3. # The author disclaims copyright to this source code. In place of
  4. # a legal notice, here is a blessing:
  5. #
  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.
  9. #
  10. #***********************************************************************
  11. #
  12. # The focus of this file is testing the r-tree extension.
  13. #
  14. if {![info exists testdir]} {
  15. set testdir [file join [file dirname [info script]] .. .. test]
  16. }
  17. source [file join [file dirname [info script]] rtree_util.tcl]
  18. source $testdir/tester.tcl
  19. set testprefix rtreedoc2
  20. ifcapable !rtree {
  21. finish_test
  22. return
  23. }
  24. #-------------------------------------------------------------------------
  25. #-------------------------------------------------------------------------
  26. # Section 6 of documentation.
  27. #-------------------------------------------------------------------------
  28. #-------------------------------------------------------------------------
  29. set testprefix rtreedoc2-1
  30. # EVIDENCE-OF: R-35254-48865 A call to one of the above APIs creates a
  31. # new SQL function named by the second parameter (zQueryFunc or zGeom).
  32. #
  33. # [register_circle_geom db] registers new geometry callback "Qcircle"
  34. # and legacy implementation "circle". Test that these do actually appear.
  35. #
  36. do_execsql_test 1.1.0 {
  37. SELECT * FROM pragma_function_list WHERE name IN('circle', 'qcircle');
  38. } {
  39. }
  40. do_test 1.1 {
  41. register_circle_geom db
  42. } {SQLITE_OK}
  43. do_execsql_test 1.1.2 {
  44. SELECT * FROM pragma_function_list WHERE name = 'circle' AND enc='utf8';
  45. } {
  46. circle 0 s utf8 -1 0
  47. }
  48. do_execsql_test 1.1.3 {
  49. SELECT * FROM pragma_function_list WHERE name = 'qcircle' AND enc='utf8';
  50. } {
  51. qcircle 0 s utf8 -1 0
  52. }
  53. do_execsql_test 1.2.0 { SELECT circle(1, 2, 3); } {{}}
  54. do_execsql_test 1.2.1 { SELECT qcircle(1, 2, 3); } {{}}
  55. # EVIDENCE-OF: R-61427-46983
  56. do_execsql_test 1.3.0 {
  57. CREATE VIRTUAL TABLE demo_index USING rtree(id, x1,x2, y1,y2);
  58. INSERT INTO demo_index VALUES(10, 45,45, 24,24);
  59. INSERT INTO demo_index VALUES(20, 50,50, 28,28);
  60. INSERT INTO demo_index VALUES(30, 43,43, 22,22);
  61. }
  62. do_execsql_test 1.3.1 {
  63. SELECT id FROM demo_index WHERE id MATCH circle(45.3, 22.9, 5.0)
  64. } {10 30}
  65. # EVIDENCE-OF: R-16907-50223 The SQL syntax for custom queries is the
  66. # same regardless of which interface, sqlite3_rtree_geometry_callback()
  67. # or sqlite3_rtree_query_callback(), is used to register the SQL
  68. # function.
  69. do_execsql_test 1.3.2 {
  70. SELECT id FROM demo_index WHERE id MATCH qcircle(45.3, 22.9, 5.0, 1)
  71. } {10 30}
  72. # EVIDENCE-OF: R-59634-51678 When that SQL function appears on the
  73. # right-hand side of the MATCH operator and the left-hand side of the
  74. # MATCH operator is any column in the R*Tree virtual table, then the
  75. # callback defined by the third argument (xQueryFunc or xGeom) is
  76. # invoked to determine if a particular object or subtree overlaps the
  77. # desired region.
  78. proc box_geom {args} {
  79. lappend ::box_geom [concat [lindex $args 0] [lrange $args 2 end-1]]
  80. return ""
  81. }
  82. register_box_geom db box_geom
  83. set box_geom [list]
  84. do_execsql_test 1.3.2 {
  85. SELECT id FROM demo_index WHERE id MATCH box(43,46, 21,25);
  86. } {10 30}
  87. do_test 1.3.3 {
  88. set ::box_geom
  89. } [list {*}{
  90. {box {43.0 46.0 21.0 25.0} {45.0 45.0 24.0 24.0}}
  91. {box {43.0 46.0 21.0 25.0} {50.0 50.0 28.0 28.0}}
  92. {box {43.0 46.0 21.0 25.0} {43.0 43.0 22.0 22.0}}
  93. }]
  94. #-------------------------------------------------------------------------
  95. #-------------------------------------------------------------------------
  96. # Section 6 of documentation.
  97. #-------------------------------------------------------------------------
  98. #-------------------------------------------------------------------------
  99. set testprefix rtreedoc2-2
  100. # EVIDENCE-OF: R-02424-24769 The second argument is the number of
  101. # coordinates in each r-tree entry, and is always the same for any given
  102. # R*Tree.
  103. #
  104. # EVIDENCE-OF: R-40260-16838 The number of coordinates is 2 for a
  105. # 1-dimensional R*Tree, 4 for a 2-dimensional R*Tree, 6 for a
  106. # 3-dimensional R*Tree, and so forth.
  107. #
  108. # The second argument referred to above is the length of the list passed
  109. # as the 3rd parameter to the Tcl script.
  110. #
  111. do_execsql_test 1.0 {
  112. CREATE VIRTUAL TABLE rt1 USING rtree(id, x1,x2);
  113. CREATE VIRTUAL TABLE rt2 USING rtree(id, x1,x2, y1,y2);
  114. CREATE VIRTUAL TABLE rt3 USING rtree(id, x1,x2, y1,y2, z1,z2);
  115. INSERT INTO rt1 DEFAULT VALUES;
  116. INSERT INTO rt2 DEFAULT VALUES;
  117. INSERT INTO rt3 DEFAULT VALUES;
  118. }
  119. foreach {tn tbl nCoord} {
  120. 1 rt1 2
  121. 2 rt2 4
  122. 3 rt3 6
  123. } {
  124. set ::box_geom [list]
  125. do_catchsql_test 1.$tn.1 "
  126. SELECT id FROM $tbl WHERE id MATCH box();
  127. " {1 {SQL logic error}}
  128. do_test 1.$tn.2 {
  129. llength [lindex $::box_geom 0 2]
  130. } $nCoord
  131. }
  132. # EVIDENCE-OF: R-28051-48608 If xGeom returns anything other than
  133. # SQLITE_OK, then the r-tree query will abort with an error.
  134. proc box_geom {args} {
  135. error "an error!"
  136. }
  137. do_catchsql_test 2.0 {
  138. SELECT * FROM rt2 WHERE id MATCH box(22,23, 24,25);
  139. } {1 {SQL logic error}}
  140. do_execsql_test 3.0 {
  141. INSERT INTO rt1 VALUES(10, 10, 10);
  142. INSERT INTO rt1 VALUES(11, 11, 11);
  143. INSERT INTO rt1 VALUES(12, 12, 12);
  144. INSERT INTO rt1 VALUES(13, 13, 13);
  145. INSERT INTO rt1 VALUES(14, 14, 14);
  146. }
  147. # EVIDENCE-OF: R-53759-57366 The exact same sqlite3_rtree_geometry
  148. # structure is used for every callback for same MATCH operator in the
  149. # same query.
  150. proc box_geom {args} {
  151. lappend ::ptr_list [lindex $args 4]
  152. return 0
  153. }
  154. set ::ptr_list [list]
  155. do_execsql_test 3.1 {
  156. SELECT * FROM rt1 WHERE id MATCH box(1,1);
  157. }
  158. do_test 3.2 {
  159. set val [lindex $::ptr_list 0]
  160. foreach p $::ptr_list {
  161. if {$p!=$val} {error "pointer mismatch"}
  162. }
  163. } {}
  164. # EVIDENCE-OF: R-60247-35692 The contents of the sqlite3_rtree_geometry
  165. # structure are initialized by SQLite but are not subsequently modified.
  166. proc box_geom {args} {
  167. lappend ::box_geom [concat [lindex $args 0] [lrange $args 2 end-1]]
  168. if {[llength $::box_geom]==3} {
  169. return "zero"
  170. }
  171. return ""
  172. }
  173. set ::box_geom [list]
  174. do_catchsql_test 3.2 {
  175. SELECT * FROM rt1 WHERE id MATCH box(1,1);
  176. } {1 {SQL logic error}}
  177. do_test 3.3 {
  178. set ::box_geom
  179. } [list {*}{
  180. {box {1.0 1.0} {0.0 0.0}}
  181. {box {1.0 1.0} {10.0 10.0}}
  182. {box {1.0 1.0} {11.0 11.0}}
  183. {box 0.0 {12.0 12.0}}
  184. }]
  185. # EVIDENCE-OF: R-31246-29731 The pContext member of the
  186. # sqlite3_rtree_geometry structure is always set to a copy of the
  187. # pContext argument passed to sqlite3_rtree_geometry_callback() when the
  188. # callback is registered.
  189. reset_db
  190. do_execsql_test 4.0 {
  191. CREATE VIRTUAL TABLE r1 USING rtree(id, minX,maxX, minY,maxY);
  192. WITH s(i) AS (
  193. VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<120
  194. )
  195. INSERT INTO r1 SELECT i,i,i+1, 200,201 FROM s;
  196. }
  197. set ctx [register_box_geom db box_geom]
  198. set ::box_geom [list]
  199. proc box_geom {args} {
  200. lappend ::box_geom [lindex $args 1]
  201. return ""
  202. }
  203. do_execsql_test 4.1 {
  204. SELECT count(*) FROM r1 WHERE id MATCH box(0,150,199,201)
  205. } 120
  206. do_test 4.2 {
  207. foreach g $::box_geom {
  208. if {$g!=$ctx} {error "pointer mismatch"}
  209. }
  210. } {}
  211. # EVIDENCE-OF: R-09904-19077 The aParam[] array (size nParam) contains
  212. # the parameter values passed to the SQL function on the right-hand side
  213. # of the MATCH operator.
  214. proc box_geom {args} {
  215. set ::box_geom [lindex $args 2]
  216. }
  217. foreach {tn q vals} {
  218. 1 "SELECT count(*) FROM r1 WHERE id MATCH box(1,2,3)" {1.0 2.0 3.0}
  219. 2 "SELECT count(*) FROM r1 WHERE id MATCH box(10001)" {10001.0}
  220. 3 "SELECT count(*) FROM r1 WHERE id MATCH box(-10001)" {-10001.0}
  221. } {
  222. do_catchsql_test 5.$tn.1 $q {1 {SQL logic error}}
  223. do_test 5.$tn.2 { set ::box_geom } $vals
  224. }
  225. do_execsql_test 5.0 {
  226. CREATE VIRTUAL TABLE myrtree USING rtree(id, x1,x2);
  227. INSERT INTO myrtree VALUES(1, 1, 1);
  228. INSERT INTO myrtree VALUES(2, 2, 2);
  229. INSERT INTO myrtree VALUES(3, 3, 3);
  230. }
  231. # EVIDENCE-OF: R-44448-00687 The pUser and xDelUser members of the
  232. # sqlite3_rtree_geometry structure are initially set to NULL.
  233. set ::box_geom_calls 0
  234. proc box_geom {args} {
  235. incr ::box_geom_calls
  236. return user_is_zero
  237. }
  238. do_execsql_test 5.1.1 {
  239. SELECT * FROM myrtree WHERE id MATCH box(4, 5);
  240. }
  241. do_test 5.1.2 { set ::box_geom_calls } 3
  242. # EVIDENCE-OF: R-55837-00155 The pUser variable may be set by the
  243. # callback implementation to any arbitrary value that may be useful to
  244. # subsequent invocations of the callback within the same query (for
  245. # example, a pointer to a complicated data structure used to test for
  246. # region intersection).
  247. #
  248. # EVIDENCE-OF: R-34745-08839 If the xDelUser variable is set to a
  249. # non-NULL value, then after the query has finished running SQLite
  250. # automatically invokes it with the value of the pUser variable as the
  251. # only argument.
  252. #
  253. set ::box_geom_calls 0
  254. proc box_geom {args} {
  255. incr ::box_geom_calls
  256. switch -- $::box_geom_calls {
  257. 1 {
  258. return user_is_zero
  259. }
  260. 2 {
  261. return [list user box_geom_finalizer]
  262. }
  263. }
  264. return ""
  265. }
  266. proc box_geom_finalizer {} {
  267. set ::box_geom_finalizer "::box_geom_calls is $::box_geom_calls"
  268. }
  269. do_execsql_test 5.1.1 {
  270. SELECT * FROM myrtree WHERE id MATCH box(4, 5);
  271. }
  272. do_test 5.1.2 { set ::box_geom_calls } 3
  273. do_test 5.1.3 {
  274. set ::box_geom_finalizer
  275. } {::box_geom_calls is 3}
  276. # EVIDENCE-OF: R-28176-28813 The xGeom callback always does a
  277. # depth-first search of the r-tree.
  278. #
  279. # For a breadth first search, final test case would return "B L" only.
  280. #
  281. do_execsql_test 6.0 {
  282. CREATE VIRTUAL TABLE xyz USING rtree(x, x1,x2, y1,y2);
  283. WITH s(i) AS (
  284. VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<15
  285. )
  286. INSERT INTO xyz SELECT NULL, one.i,one.i+1, two.i,two.i+1 FROM s one, s two;
  287. }
  288. do_execsql_test 6.1 {
  289. SELECT count(*) FROM xyz_node
  290. } {10}
  291. proc box_geom {args} {
  292. set coords [lindex $args 3]
  293. set area [expr {
  294. ([lindex $coords 1]-[lindex $coords 0]) *
  295. ([lindex $coords 3]-[lindex $coords 2])
  296. }]
  297. if {$area==1} {
  298. lappend ::box_geom_calls L
  299. } else {
  300. lappend ::box_geom_calls B
  301. }
  302. }
  303. set ::box_geom_calls [list]
  304. do_execsql_test 6.2 {
  305. SELECT count(*) FROM xyz WHERE x MATCH box(0,20,0,20)
  306. } 225
  307. do_test 6.3 {
  308. set prev ""
  309. set box_calls [list]
  310. foreach c $::box_geom_calls {
  311. if {$c!=$prev} {
  312. lappend ::box_calls $c
  313. set prev $c
  314. }
  315. }
  316. set ::box_calls
  317. } {B L B L B L B L B L B L B L B L B L}
  318. finish_test