drh | 8416fc7 | 2013-04-25 16:42:55 +0000 | [diff] [blame] | 1 | # 2013-04-25 |
| 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 | # Test cases for transitive_closure virtual table. |
| 13 | |
| 14 | set testdir [file dirname $argv0] |
| 15 | source $testdir/tester.tcl |
| 16 | set testprefix closure01 |
| 17 | |
drh | 3c2aeae | 2014-01-24 14:37:44 +0000 | [diff] [blame] | 18 | ifcapable !vtab||!cte { finish_test ; return } |
dan | 11f71d6 | 2013-05-15 18:34:17 +0000 | [diff] [blame] | 19 | |
drh | 8416fc7 | 2013-04-25 16:42:55 +0000 | [diff] [blame] | 20 | load_static_extension db closure |
| 21 | |
| 22 | do_execsql_test 1.0 { |
| 23 | BEGIN; |
| 24 | CREATE TABLE t1(x INTEGER PRIMARY KEY, y INTEGER); |
drh | 3c2aeae | 2014-01-24 14:37:44 +0000 | [diff] [blame] | 25 | WITH RECURSIVE |
| 26 | cnt(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM cnt LIMIT 131072) |
| 27 | INSERT INTO t1(x, y) SELECT i, nullif(i,1)/2 FROM cnt; |
drh | 8416fc7 | 2013-04-25 16:42:55 +0000 | [diff] [blame] | 28 | CREATE INDEX t1y ON t1(y); |
drh | 8416fc7 | 2013-04-25 16:42:55 +0000 | [diff] [blame] | 29 | COMMIT; |
| 30 | CREATE VIRTUAL TABLE cx |
| 31 | USING transitive_closure(tablename=t1, idcolumn=x, parentcolumn=y); |
| 32 | } {} |
| 33 | |
| 34 | # The entire table |
drh | 3c2aeae | 2014-01-24 14:37:44 +0000 | [diff] [blame] | 35 | do_timed_execsql_test 1.1 { |
drh | 8416fc7 | 2013-04-25 16:42:55 +0000 | [diff] [blame] | 36 | SELECT count(*), depth FROM cx WHERE root=1 GROUP BY depth ORDER BY 1; |
| 37 | } {/1 0 1 17 2 1 4 2 8 3 16 4 .* 65536 16/} |
drh | 3c2aeae | 2014-01-24 14:37:44 +0000 | [diff] [blame] | 38 | do_timed_execsql_test 1.1-cte { |
| 39 | WITH RECURSIVE |
| 40 | below(id,depth) AS ( |
| 41 | VALUES(1,0) |
| 42 | UNION ALL |
| 43 | SELECT t1.x, below.depth+1 |
| 44 | FROM t1 JOIN below on t1.y=below.id |
| 45 | ) |
| 46 | SELECT count(*), depth FROM below GROUP BY depth ORDER BY 1; |
| 47 | } {/1 0 1 17 2 1 4 2 8 3 16 4 .* 65536 16/} |
drh | 8416fc7 | 2013-04-25 16:42:55 +0000 | [diff] [blame] | 48 | |
| 49 | # descendents of 32768 |
drh | 9e2c7ae | 2014-01-24 15:42:51 +0000 | [diff] [blame] | 50 | do_timed_execsql_test 1.2 { |
drh | 8416fc7 | 2013-04-25 16:42:55 +0000 | [diff] [blame] | 51 | SELECT * FROM cx WHERE root=32768 ORDER BY id; |
| 52 | } {32768 0 65536 1 65537 1 131072 2} |
drh | 9e2c7ae | 2014-01-24 15:42:51 +0000 | [diff] [blame] | 53 | do_timed_execsql_test 1.2-cte { |
| 54 | WITH RECURSIVE |
| 55 | below(id,depth) AS ( |
| 56 | VALUES(32768,0) |
| 57 | UNION ALL |
| 58 | SELECT t1.x, below.depth+1 |
| 59 | FROM t1 JOIN below on t1.y=below.id |
| 60 | WHERE below.depth<2 |
| 61 | ) |
| 62 | SELECT id, depth FROM below ORDER BY id; |
| 63 | } {32768 0 65536 1 65537 1 131072 2} |
drh | 8416fc7 | 2013-04-25 16:42:55 +0000 | [diff] [blame] | 64 | |
| 65 | # descendents of 16384 |
drh | 3c2aeae | 2014-01-24 14:37:44 +0000 | [diff] [blame] | 66 | do_timed_execsql_test 1.3 { |
drh | 8416fc7 | 2013-04-25 16:42:55 +0000 | [diff] [blame] | 67 | SELECT * FROM cx WHERE root=16384 AND depth<=2 ORDER BY id; |
| 68 | } {16384 0 32768 1 32769 1 65536 2 65537 2 65538 2 65539 2} |
drh | 3c2aeae | 2014-01-24 14:37:44 +0000 | [diff] [blame] | 69 | do_timed_execsql_test 1.3-cte { |
| 70 | WITH RECURSIVE |
| 71 | below(id,depth) AS ( |
| 72 | VALUES(16384,0) |
| 73 | UNION ALL |
| 74 | SELECT t1.x, below.depth+1 |
| 75 | FROM t1 JOIN below on t1.y=below.id |
| 76 | WHERE below.depth<2 |
| 77 | ) |
| 78 | SELECT id, depth FROM below ORDER BY id; |
| 79 | } {16384 0 32768 1 32769 1 65536 2 65537 2 65538 2 65539 2} |
drh | 8416fc7 | 2013-04-25 16:42:55 +0000 | [diff] [blame] | 80 | |
| 81 | # children of 16384 |
| 82 | do_execsql_test 1.4 { |
| 83 | SELECT id, depth, root, tablename, idcolumn, parentcolumn FROM cx |
| 84 | WHERE root=16384 |
| 85 | AND depth=1 |
| 86 | ORDER BY id; |
| 87 | } {32768 1 {} t1 x y 32769 1 {} t1 x y} |
| 88 | |
| 89 | # great-grandparent of 16384 |
drh | 9e2c7ae | 2014-01-24 15:42:51 +0000 | [diff] [blame] | 90 | do_timed_execsql_test 1.5 { |
drh | 8416fc7 | 2013-04-25 16:42:55 +0000 | [diff] [blame] | 91 | SELECT id, depth, root, tablename, idcolumn, parentcolumn FROM cx |
| 92 | WHERE root=16384 |
| 93 | AND depth=3 |
| 94 | AND idcolumn='Y' |
| 95 | AND parentcolumn='X'; |
| 96 | } {2048 3 {} t1 Y X} |
drh | 9e2c7ae | 2014-01-24 15:42:51 +0000 | [diff] [blame] | 97 | do_timed_execsql_test 1.5-cte { |
| 98 | WITH RECURSIVE |
| 99 | above(id,depth) AS ( |
| 100 | VALUES(16384,0) |
| 101 | UNION ALL |
| 102 | SELECT t1.y, above.depth+1 |
| 103 | FROM t1 JOIN above ON t1.x=above.id |
| 104 | WHERE above.depth<3 |
| 105 | ) |
| 106 | SELECT id FROM above WHERE depth=3; |
| 107 | } {2048} |
drh | 8416fc7 | 2013-04-25 16:42:55 +0000 | [diff] [blame] | 108 | |
| 109 | # depth<5 |
drh | 9e2c7ae | 2014-01-24 15:42:51 +0000 | [diff] [blame] | 110 | do_timed_execsql_test 1.6 { |
drh | 8416fc7 | 2013-04-25 16:42:55 +0000 | [diff] [blame] | 111 | SELECT count(*), depth FROM cx WHERE root=1 AND depth<5 |
| 112 | GROUP BY depth ORDER BY 1; |
| 113 | } {1 0 2 1 4 2 8 3 16 4} |
drh | 9e2c7ae | 2014-01-24 15:42:51 +0000 | [diff] [blame] | 114 | do_timed_execsql_test 1.6-cte { |
| 115 | WITH RECURSIVE |
| 116 | below(id,depth) AS ( |
| 117 | VALUES(1,0) |
| 118 | UNION ALL |
| 119 | SELECT t1.x, below.depth+1 |
| 120 | FROM t1 JOIN below ON t1.y=below.id |
| 121 | WHERE below.depth<4 |
| 122 | ) |
| 123 | SELECT count(*), depth FROM below GROUP BY depth ORDER BY 1; |
| 124 | } {1 0 2 1 4 2 8 3 16 4} |
drh | 8416fc7 | 2013-04-25 16:42:55 +0000 | [diff] [blame] | 125 | |
| 126 | # depth<=5 |
| 127 | do_execsql_test 1.7 { |
| 128 | SELECT count(*), depth FROM cx WHERE root=1 AND depth<=5 |
| 129 | GROUP BY depth ORDER BY 1; |
| 130 | } {1 0 2 1 4 2 8 3 16 4 32 5} |
| 131 | |
| 132 | # depth==5 |
| 133 | do_execsql_test 1.8 { |
| 134 | SELECT count(*), depth FROM cx WHERE root=1 AND depth=5 |
| 135 | GROUP BY depth ORDER BY 1; |
| 136 | } {32 5} |
| 137 | |
| 138 | # depth BETWEEN 3 AND 5 |
| 139 | do_execsql_test 1.9 { |
| 140 | SELECT count(*), depth FROM cx WHERE root=1 AND depth BETWEEN 3 AND 5 |
| 141 | GROUP BY depth ORDER BY 1; |
| 142 | } {8 3 16 4 32 5} |
| 143 | |
| 144 | # depth==5 with min() and max() |
drh | 9e2c7ae | 2014-01-24 15:42:51 +0000 | [diff] [blame] | 145 | do_timed_execsql_test 1.10 { |
drh | 8416fc7 | 2013-04-25 16:42:55 +0000 | [diff] [blame] | 146 | SELECT count(*), min(id), max(id) FROM cx WHERE root=1 AND depth=5; |
| 147 | } {32 32 63} |
drh | 9e2c7ae | 2014-01-24 15:42:51 +0000 | [diff] [blame] | 148 | do_timed_execsql_test 1.10-cte { |
| 149 | WITH RECURSIVE |
| 150 | below(id,depth) AS ( |
| 151 | VALUES(1,0) |
| 152 | UNION ALL |
| 153 | SELECT t1.x, below.depth+1 |
| 154 | FROM t1 JOIN below ON t1.y=below.id |
| 155 | WHERE below.depth<5 |
| 156 | ) |
| 157 | SELECT count(*), min(id), max(id) FROM below WHERE depth=5; |
| 158 | } {32 32 63} |
drh | 8416fc7 | 2013-04-25 16:42:55 +0000 | [diff] [blame] | 159 | |
| 160 | # Create a much smaller table t2 with only 32 elements |
| 161 | db eval { |
| 162 | CREATE TABLE t2(x INTEGER PRIMARY KEY, y INTEGER); |
| 163 | INSERT INTO t2 SELECT x, y FROM t1 WHERE x<32; |
| 164 | CREATE INDEX t2y ON t2(y); |
| 165 | CREATE VIRTUAL TABLE c2 |
| 166 | USING transitive_closure(tablename=t2, idcolumn=x, parentcolumn=y); |
| 167 | } |
| 168 | |
| 169 | # t2 full-table |
| 170 | do_execsql_test 2.1 { |
| 171 | SELECT count(*), min(id), max(id) FROM c2 WHERE root=1; |
| 172 | } {31 1 31} |
| 173 | # t2 root=10 |
| 174 | do_execsql_test 2.2 { |
| 175 | SELECT id FROM c2 WHERE root=10; |
| 176 | } {10 20 21} |
| 177 | # t2 root=11 |
| 178 | do_execsql_test 2.3 { |
| 179 | SELECT id FROM c2 WHERE root=12; |
| 180 | } {12 24 25} |
| 181 | # t2 root IN [10,12] |
| 182 | do_execsql_test 2.4 { |
| 183 | SELECT id FROM c2 WHERE root IN (10,12) ORDER BY id; |
| 184 | } {10 12 20 21 24 25} |
| 185 | # t2 root IN [10,12] (sorted) |
| 186 | do_execsql_test 2.5 { |
| 187 | SELECT id FROM c2 WHERE root IN (10,12) ORDER BY +id; |
| 188 | } {10 12 20 21 24 25} |
| 189 | |
| 190 | # t2 c2up from 20 |
| 191 | do_execsql_test 3.0 { |
| 192 | CREATE VIRTUAL TABLE c2up USING transitive_closure( |
| 193 | tablename = t2, |
| 194 | idcolumn = y, |
| 195 | parentcolumn = x |
| 196 | ); |
| 197 | SELECT id FROM c2up WHERE root=20; |
| 198 | } {1 2 5 10 20} |
| 199 | |
| 200 | # cx as c2up |
| 201 | do_execsql_test 3.1 { |
| 202 | SELECT id FROM cx |
| 203 | WHERE root=20 |
| 204 | AND tablename='t2' |
| 205 | AND idcolumn='y' |
| 206 | AND parentcolumn='x'; |
| 207 | } {1 2 5 10 20} |
| 208 | |
| 209 | # t2 first cousins of 20 |
| 210 | do_execsql_test 3.2 { |
| 211 | SELECT DISTINCT id FROM c2 |
| 212 | WHERE root IN (SELECT id FROM c2up |
| 213 | WHERE root=20 AND depth<=2) |
| 214 | ORDER BY id; |
| 215 | } {5 10 11 20 21 22 23} |
| 216 | |
| 217 | # t2 first cousins of 20 |
| 218 | do_execsql_test 3.3 { |
| 219 | SELECT id FROM c2 |
| 220 | WHERE root=(SELECT id FROM c2up |
| 221 | WHERE root=20 AND depth=2) |
| 222 | AND depth=2 |
| 223 | EXCEPT |
| 224 | SELECT id FROM c2 |
| 225 | WHERE root=(SELECT id FROM c2up |
| 226 | WHERE root=20 AND depth=1) |
| 227 | AND depth<=1 |
| 228 | ORDER BY id; |
| 229 | } {22 23} |
| 230 | |
| 231 | # missing tablename. |
| 232 | do_test 4.1 { |
| 233 | catchsql { |
| 234 | SELECT id FROM cx |
| 235 | WHERE root=20 |
| 236 | AND tablename='t3' |
| 237 | AND idcolumn='y' |
| 238 | AND parentcolumn='x'; |
| 239 | } |
| 240 | } {1 {no such table: t3}} |
| 241 | |
| 242 | # missing idcolumn |
| 243 | do_test 4.2 { |
| 244 | catchsql { |
| 245 | SELECT id FROM cx |
| 246 | WHERE root=20 |
| 247 | AND tablename='t2' |
| 248 | AND idcolumn='xyz' |
| 249 | AND parentcolumn='x'; |
| 250 | } |
| 251 | } {1 {no such column: t2.xyz}} |
| 252 | |
| 253 | # missing parentcolumn |
| 254 | do_test 4.3 { |
| 255 | catchsql { |
| 256 | SELECT id FROM cx |
| 257 | WHERE root=20 |
| 258 | AND tablename='t2' |
| 259 | AND idcolumn='x' |
| 260 | AND parentcolumn='pqr'; |
| 261 | } |
| 262 | } {1 {no such column: t2.pqr}} |
| 263 | |
| 264 | # generic closure |
| 265 | do_execsql_test 5.1 { |
| 266 | CREATE VIRTUAL TABLE temp.closure USING transitive_closure; |
| 267 | SELECT id FROM closure |
| 268 | WHERE root=1 |
| 269 | AND depth=3 |
| 270 | AND tablename='t1' |
| 271 | AND idcolumn='x' |
| 272 | AND parentcolumn='y' |
| 273 | ORDER BY id; |
| 274 | } {8 9 10 11 12 13 14 15} |
| 275 | |
| 276 | finish_test |