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 347 348 349 350 351 352 353 354 355 356 357 358
|
# 2015-08-19
#
# 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.
#
#***********************************************************************
#
# This file implements tests for table-valued-functions implemented using
# eponymous virtual tables.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix tabfunc01
ifcapable !vtab {
finish_test
return
}
load_static_extension db series
load_static_extension db carray
load_static_extension db remember
do_execsql_test tabfunc01-1.1 {
SELECT *, '|' FROM generate_series WHERE start=1 AND stop=9 AND step=2;
} {1 | 3 | 5 | 7 | 9 |}
do_execsql_test tabfunc01-1.1b {
PRAGMA table_xinfo(generate_series);
} {0 value {} 0 {} 0 0 1 start {} 0 {} 0 1 2 stop {} 0 {} 0 1 3 step {} 0 {} 0 1}
do_execsql_test tabfunc01-1.2 {
SELECT *, '|' FROM generate_series(0) LIMIT 5;
} {0 | 1 | 2 | 3 | 4 |}
do_catchsql_test tabfunc01-1.2b {
SELECT *, '|' FROM generate_series LIMIT 5;
} {1 {first argument to "generate_series()" missing or unusable}}
do_catchsql_test tabfunc01-1.2c {
SELECT *, '|' FROM generate_series(value) LIMIT 5;
} {1 {first argument to "generate_series()" missing or unusable}}
do_catchsql_test tabfunc01-1.3 {
CREATE VIRTUAL TABLE t1 USING generate_series;
} {1 {no such module: generate_series}}
do_execsql_test tabfunc01-1.4 {
SELECT * FROM generate_series(1,9,2);
} {1 3 5 7 9}
do_execsql_test tabfunc01-1.5 {
SELECT * FROM generate_series(1,9);
} {1 2 3 4 5 6 7 8 9}
do_execsql_test tabfunc01-1.6 {
SELECT * FROM generate_series(1,10) WHERE step=3;
} {1 4 7 10}
do_catchsql_test tabfunc01-1.7 {
SELECT * FROM generate_series(1,9,2,11);
} {1 {too many arguments on generate_series() - max 3}}
do_execsql_test tabfunc01-1.8 {
SELECT * FROM generate_series(0,32,5) ORDER BY rowid DESC;
} {30 25 20 15 10 5 0}
do_execsql_test tabfunc01-1.9 {
SELECT rowid, * FROM generate_series(0,32,5) ORDER BY value DESC;
} {7 30 6 25 5 20 4 15 3 10 2 5 1 0}
do_execsql_test tabfunc01-1.10 {
SELECT rowid, * FROM generate_series(0,32,5) ORDER BY +value DESC;
} {7 30 6 25 5 20 4 15 3 10 2 5 1 0}
do_execsql_test tabfunc01-1.20 {
CREATE VIEW v1(a,b) AS VALUES(1,2),(3,4);
SELECT * FROM v1;
} {1 2 3 4}
do_catchsql_test tabfunc01-1.21.1 {
SELECT * FROM v1(55);
} {1 {'v1' is not a function}}
do_catchsql_test tabfunc01-1.21.2 {
SELECT * FROM v1();
} {1 {'v1' is not a function}}
do_execsql_test tabfunc01-1.22 {
CREATE VIEW v2(x) AS SELECT value FROM generate_series(1,5);
SELECT * FROM v2;
} {1 2 3 4 5}
do_catchsql_test tabfunc01-1.23.1 {
SELECT * FROM v2(55);
} {1 {'v2' is not a function}}
do_catchsql_test tabfunc01-1.23.2 {
SELECT * FROM v2();
} {1 {'v2' is not a function}}
do_execsql_test tabfunc01-1.24 {
CREATE TABLE t0(x);
INSERT INTO t0(x) VALUES(123),(456),(789);
SELECT * FROM t0 ORDER BY x;
} {123 456 789}
do_catchsql_test tabfunc01-1.25 {
SELECT * FROM t0(55) ORDER BY x;
} {1 {'t0' is not a function}}
do_catchsql_test tabfunc01-1.26 {
WITH w0 AS (SELECT * FROM t0)
INSERT INTO t0(x) SELECT * FROM w0()
} {1 {'w0' is not a function}}
do_execsql_test tabfunc01-2.1 {
CREATE TABLE t1(x);
INSERT INTO t1(x) VALUES(2),(3);
SELECT *, '|' FROM t1, generate_series(1,x) ORDER BY 1, 2
} {2 1 | 2 2 | 3 1 | 3 2 | 3 3 |}
do_execsql_test tabfunc01-2.2 {
SELECT *, '|' FROM (SELECT x FROM t1) AS y, generate_series(1,y.x)
ORDER BY 1, 2;
} {2 1 | 2 2 | 3 1 | 3 2 | 3 3 |}
do_execsql_test tabfunc01-2.50 {
SELECT * FROM generate_series(0) LIMIT 5;
} {0 1 2 3 4}
do_execsql_test tabfunc01-3.1 {
SELECT DISTINCT value FROM generate_series(1,x), t1 ORDER BY 1;
} {1 2 3}
do_eqp_test tabfunc01-3.10 {
SELECT value FROM generate_series(1,10) ORDER BY value;
} {
QUERY PLAN
`--SCAN generate_series VIRTUAL TABLE INDEX 19:
}
do_eqp_test tabfunc01-3.11 {
SELECT value FROM generate_series(1,10) ORDER BY +value;
} {
QUERY PLAN
|--SCAN generate_series VIRTUAL TABLE INDEX 3:
`--USE TEMP B-TREE FOR ORDER BY
}
do_eqp_test tabfunc01-3.12 {
SELECT value FROM generate_series(1,10) ORDER BY value, stop;
} {
QUERY PLAN
`--SCAN generate_series VIRTUAL TABLE INDEX 19:
}
do_eqp_test tabfunc01-3.13 {
SELECT value FROM generate_series(1,10) ORDER BY stop, value;
} {
QUERY PLAN
|--SCAN generate_series VIRTUAL TABLE INDEX 3:
`--USE TEMP B-TREE FOR ORDER BY
}
do_eqp_test tabfunc01-3.20 {
WITH t1(a) AS (
SELECT value FROM generate_series(0,10,2)
UNION ALL
SELECT value FROM generate_series(9,18,3)
)
SELECT * FROM t1 ORDER BY a;
} {
QUERY PLAN
`--MERGE (UNION ALL)
|--LEFT
| `--SCAN generate_series VIRTUAL TABLE INDEX 23:
`--RIGHT
`--SCAN generate_series VIRTUAL TABLE INDEX 23:
}
# Eponymous virtual table exists in all schemas.
#
do_execsql_test tabfunc01-4.1 {
SELECT * FROM main.generate_series(1,4)
} {1 2 3 4}
do_execsql_test tabfunc01-4.2 {
SELECT * FROM temp.generate_series(1,4)
} {1 2 3 4}
do_execsql_test tabfunc01-4.3 {
ATTACH ':memory:' AS aux1;
CREATE TABLE aux1.t1(a,b,c);
SELECT * FROM aux1.generate_series(1,4)
} {1 2 3 4}
# 2018-12-03: Fix bug reported by by private email.
do_execsql_test tabfunc01-4.4 {
SELECT * FROM (generate_series(1,5,2)) AS x LIMIT 10;
} {1 3 5}
# The next series of tests is verifying that virtual table are able
# to optimize the IN operator, even on terms that are not marked "omit".
# When the generate_series virtual table is compiled for the testfixture,
# the special -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 option is used, which
# causes the xBestIndex method of generate_series to leave the
# sqlite3_index_constraint_usage.omit flag set to 0, which should cause
# the SQLite core to verify the start=, stop=, and step= constraints on
# each step of output. At one point, the IN operator could not be used
# by virtual tables unless omit was set.
#
do_execsql_test tabfunc01-500 {
SELECT * FROM generate_series WHERE start IN (1,7) AND stop=20 AND step=10
ORDER BY +1;
} {1 7 11 17}
# Table-valued functions on the RHS of an IN operator
#
do_execsql_test tabfunc01-600 {
CREATE TABLE t600(a INTEGER PRIMARY KEY, b TEXT);
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100)
INSERT INTO t600(a,b) SELECT x, printf('(%03d)',x) FROM c;
SELECT b FROM t600 WHERE a IN generate_series(2,52,10);
} {(002) (012) (022) (032) (042) (052)}
do_test tabfunc01-700 {
set PTR1 [intarray_addr 5 7 13 17 23]
db eval {
SELECT b FROM t600, carray(inttoptr($PTR1),5) WHERE a=value;
}
} {(005) (007) (013) (017) (023)}
do_test tabfunc01-701 {
db eval {
SELECT b FROM t600 WHERE a IN carray(inttoptr($PTR1),5,'int32');
}
} {(005) (007) (013) (017) (023)}
do_test tabfunc01-702 {
db eval {
SELECT b FROM t600 WHERE a IN carray(inttoptr($PTR1),4,'int32');
}
} {(005) (007) (013) (017)}
do_catchsql_test tabfunc01-710 {
SELECT b FROM t600 WHERE a IN carray(inttoptr($PTR1),5,'int33');
} {1 {unknown datatype: 'int33'}}
do_test tabfunc01-720 {
set PTR2 [int64array_addr 5 7 13 17 23]
db eval {
SELECT b FROM t600, carray(inttoptr($PTR2),5,'int64') WHERE a=value;
}
} {(005) (007) (013) (017) (023)}
do_test tabfunc01-721 {
db eval {
SELECT remember(123,inttoptr($PTR2));
SELECT value FROM carray(inttoptr($PTR2),5,'int64');
}
} {123 123 7 13 17 23}
do_test tabfunc01-722 {
set PTR3 [expr {$PTR2+16}]
db eval {
SELECT remember(987,inttoptr($PTR3));
SELECT value FROM carray(inttoptr($PTR2),5,'int64');
}
} {987 123 7 987 17 23}
do_test tabfunc01-730 {
set PTR4 [doublearray_addr 5.0 7.0 13.0 17.0 23.0]
db eval {
SELECT b FROM t600, carray(inttoptr($PTR4),5,'double') WHERE a=value;
}
} {(005) (007) (013) (017) (023)}
do_test tabfunc01-740 {
set PTR5 [textarray_addr x5 x7 x13 x17 x23]
db eval {
SELECT b FROM t600, carray(inttoptr($PTR5),5,'char*')
WHERE a=trim(value,'x');
}
} {(005) (007) (013) (017) (023)}
do_test tabfunc01-750 {
db eval {
SELECT aa.value, bb.value, '|'
FROM carray(inttoptr($PTR4),5,'double') AS aa
JOIN carray(inttoptr($PTR5),5,'char*') AS bb ON aa.rowid=bb.rowid;
}
} {5.0 x5 | 7.0 x7 | 13.0 x13 | 17.0 x17 | 23.0 x23 |}
# ticket https://www.sqlite.org/src/info/2ae0c599b735d59e
# Verification of testtag-20230227a
do_test tabfunc01-751 {
db eval {
SELECT aa.value, bb.value, '|'
FROM carray(inttoptr($PTR4),5,'double') AS aa
LEFT JOIN carray(inttoptr($PTR5),5,'char*') AS bb ON aa.rowid=bb.rowid;
}
} {5.0 x5 | 7.0 x7 | 13.0 x13 | 17.0 x17 | 23.0 x23 |}
ifcapable altertable {
do_test tabfunc01-800 {
catchsql {
ALTER TABLE generate_series ADD COLUMN col2;
}
} {1 {virtual tables may not be altered}}
do_test tabfunc01-810 {
catchsql {
ALTER TABLE generate_series RENAME TO flubber;
}
} {1 {table generate_series may not be altered}}
do_test tabfunc01-820 {
catchsql {
ALTER TABLE generate_series RENAME start TO flubber;
}
} {1 {table generate_series may not be altered}}
do_test tabfunc01-830 {
catchsql {
ALTER TABLE generate_series DROP COLUMN start;
}
} {1 {table generate_series may not be altered}}
do_test tabfunc01-900 {
catchsql {
ALTER TABLE pragma_compile_options ADD COLUMN col2;
}
} {1 {virtual tables may not be altered}}
do_test tabfunc01-910 {
catchsql {
ALTER TABLE pragma_compile_options RENAME TO flubber;
}
} {1 {table pragma_compile_options may not be altered}}
do_test tabfunc01-920 {
catchsql {
ALTER TABLE pragma_compile_options RENAME start TO flubber;
}
} {1 {table pragma_compile_options may not be altered}}
do_test tabfunc01-930 {
catchsql {
ALTER TABLE pragma_compile_options DROP COLUMN start;
}
} {1 {table pragma_compile_options may not be altered}}
}
#-----------------------------------------------------------------------------
# 2024-04-26 LIMIT and OFFSET passed into virtual tables
# https://sqlite.org/forum/forumpost/c243b8f856
#
do_execsql_test tabfunc01-900 {
SELECT * FROM (
SELECT * FROM generate_series(1,10)
UNION ALL
SELECT * FROM generate_series(101,104)
) LIMIT 10 OFFSET 5;
} {6 7 8 9 10 101 102 103 104}
do_execsql_test tabfunc01-910 {
SELECT * FROM (
SELECT * FROM generate_series(1,10)
UNION ALL
SELECT * FROM generate_series(101,104)
) LIMIT -1 OFFSET 5;
} {6 7 8 9 10 101 102 103 104}
do_execsql_test tabfunc01-920 {
SELECT * FROM (
SELECT * FROM generate_series(1,10)
UNION ALL
SELECT * FROM generate_series(101,104)
) LIMIT -1 OFFSET 0;
} {1 2 3 4 5 6 7 8 9 10 101 102 103 104}
# Free up memory allocations
intarray_addr
int64array_addr
doublearray_addr
textarray_addr
finish_test
|