rtree_util.tcl 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. # 2008 Feb 19
  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. # This file contains Tcl code that may be useful for testing or
  13. # analyzing r-tree structures created with this module. It is
  14. # used by both test procedures and the r-tree viewer application.
  15. #
  16. #--------------------------------------------------------------------------
  17. # PUBLIC API:
  18. #
  19. # rtree_depth
  20. # rtree_ndim
  21. # rtree_node
  22. # rtree_mincells
  23. # rtree_check
  24. # rtree_dump
  25. # rtree_treedump
  26. #
  27. proc rtree_depth {db zTab} {
  28. $db one "SELECT rtreedepth(data) FROM ${zTab}_node WHERE nodeno=1"
  29. }
  30. proc rtree_nodedepth {db zTab iNode} {
  31. set iDepth [rtree_depth $db $zTab]
  32. set ii $iNode
  33. while {$ii != 1} {
  34. set sql "SELECT parentnode FROM ${zTab}_parent WHERE nodeno = $ii"
  35. set ii [db one $sql]
  36. incr iDepth -1
  37. }
  38. return $iDepth
  39. }
  40. # Return the number of dimensions of the rtree.
  41. #
  42. proc rtree_ndim {db zTab} {
  43. set nDim [expr {(([llength [$db eval "pragma table_info($zTab)"]]/6)-1)/2}]
  44. }
  45. # Return the contents of rtree node $iNode.
  46. #
  47. proc rtree_node {db zTab iNode {iPrec 6}} {
  48. set nDim [rtree_ndim $db $zTab]
  49. set sql "
  50. SELECT rtreenode($nDim, data) FROM ${zTab}_node WHERE nodeno = $iNode
  51. "
  52. set node [db one $sql]
  53. set nCell [llength $node]
  54. set nCoord [expr $nDim*2]
  55. for {set ii 0} {$ii < $nCell} {incr ii} {
  56. for {set jj 1} {$jj <= $nCoord} {incr jj} {
  57. set newval [format "%.${iPrec}f" [lindex $node $ii $jj]]
  58. lset node $ii $jj $newval
  59. }
  60. }
  61. set node
  62. }
  63. proc rtree_mincells {db zTab} {
  64. set n [$db one "select length(data) FROM ${zTab}_node LIMIT 1"]
  65. set nMax [expr {int(($n-4)/(8+[rtree_ndim $db $zTab]*2*4))}]
  66. return [expr {int($nMax/3)}]
  67. }
  68. # An integrity check for the rtree $zTab accessible via database
  69. # connection $db.
  70. #
  71. proc rtree_check {db zTab} {
  72. array unset ::checked
  73. # Check each r-tree node.
  74. set rc [catch {
  75. rtree_node_check $db $zTab 1 [rtree_depth $db $zTab]
  76. } msg]
  77. if {$rc && $msg ne ""} { error $msg }
  78. # Check that the _rowid and _parent tables have the right
  79. # number of entries.
  80. set nNode [$db one "SELECT count(*) FROM ${zTab}_node"]
  81. set nRow [$db one "SELECT count(*) FROM ${zTab}"]
  82. set nRowid [$db one "SELECT count(*) FROM ${zTab}_rowid"]
  83. set nParent [$db one "SELECT count(*) FROM ${zTab}_parent"]
  84. if {$nNode != ($nParent+1)} {
  85. error "Wrong number of entries in ${zTab}_parent"
  86. }
  87. if {$nRow != $nRowid} {
  88. error "Wrong number of entries in ${zTab}_rowid"
  89. }
  90. return $rc
  91. }
  92. proc rtree_node_check {db zTab iNode iDepth} {
  93. if {[info exists ::checked($iNode)]} { error "Second ref to $iNode" }
  94. set ::checked($iNode) 1
  95. set node [rtree_node $db $zTab $iNode]
  96. if {$iNode!=1 && [llength $node]==0} { error "No such node: $iNode" }
  97. if {$iNode != 1 && [llength $node]<[rtree_mincells $db $zTab]} {
  98. puts "Node $iNode: Has only [llength $node] cells"
  99. error ""
  100. }
  101. if {$iNode == 1 && [llength $node]==1 && [rtree_depth $db $zTab]>0} {
  102. set depth [rtree_depth $db $zTab]
  103. puts "Node $iNode: Has only 1 child (tree depth is $depth)"
  104. error ""
  105. }
  106. set nDim [expr {([llength [lindex $node 0]]-1)/2}]
  107. if {$iDepth > 0} {
  108. set d [expr $iDepth-1]
  109. foreach cell $node {
  110. set shouldbe [rtree_node_check $db $zTab [lindex $cell 0] $d]
  111. if {$cell ne $shouldbe} {
  112. puts "Node $iNode: Cell is: {$cell}, should be {$shouldbe}"
  113. error ""
  114. }
  115. }
  116. }
  117. set mapping_table "${zTab}_parent"
  118. set mapping_sql "SELECT parentnode FROM $mapping_table WHERE rowid = \$rowid"
  119. if {$iDepth==0} {
  120. set mapping_table "${zTab}_rowid"
  121. set mapping_sql "SELECT nodeno FROM $mapping_table WHERE rowid = \$rowid"
  122. }
  123. foreach cell $node {
  124. set rowid [lindex $cell 0]
  125. set mapping [db one $mapping_sql]
  126. if {$mapping != $iNode} {
  127. puts "Node $iNode: $mapping_table entry for cell $rowid is $mapping"
  128. error ""
  129. }
  130. }
  131. set ret [list $iNode]
  132. for {set ii 1} {$ii <= $nDim*2} {incr ii} {
  133. set f [lindex $node 0 $ii]
  134. foreach cell $node {
  135. set f2 [lindex $cell $ii]
  136. if {($ii%2)==1 && $f2<$f} {set f $f2}
  137. if {($ii%2)==0 && $f2>$f} {set f $f2}
  138. }
  139. lappend ret $f
  140. }
  141. return $ret
  142. }
  143. proc rtree_dump {db zTab} {
  144. set zRet ""
  145. set nDim [expr {(([llength [$db eval "pragma table_info($zTab)"]]/6)-1)/2}]
  146. set sql "SELECT nodeno, rtreenode($nDim, data) AS node FROM ${zTab}_node"
  147. $db eval $sql {
  148. append zRet [format "% -10s %s\n" $nodeno $node]
  149. }
  150. set zRet
  151. }
  152. proc rtree_nodetreedump {db zTab zIndent iDepth iNode} {
  153. set ret ""
  154. set node [rtree_node $db $zTab $iNode 1]
  155. append ret [format "%-3d %s%s\n" $iNode $zIndent $node]
  156. if {$iDepth>0} {
  157. foreach cell $node {
  158. set i [lindex $cell 0]
  159. append ret [rtree_nodetreedump $db $zTab "$zIndent " [expr $iDepth-1] $i]
  160. }
  161. }
  162. set ret
  163. }
  164. proc rtree_treedump {db zTab} {
  165. set d [rtree_depth $db $zTab]
  166. rtree_nodetreedump $db $zTab "" $d 1
  167. }
  168. proc do_rtree_integrity_test {tn tbl} {
  169. uplevel [list do_execsql_test $tn.1 "SELECT rtreecheck('$tbl')" ok]
  170. uplevel [list do_execsql_test $tn.2 "PRAGMA integrity_check" ok]
  171. }