should be it

This commit is contained in:
2025-10-24 19:21:19 -05:00
parent a4b23fc57c
commit f09560c7b1
14047 changed files with 3161551 additions and 1 deletions

View File

@@ -0,0 +1,152 @@
# name: test/optimizer/statistics/statistics_aggregate.test
# description: Statistics propagation test with aggregates expression
# group: [statistics]
statement ok
SET default_null_order='nulls_first';
statement ok
CREATE TABLE integers AS SELECT * FROM (VALUES (1), (2), (3)) tbl(i);
statement ok
PRAGMA explain_output = OPTIMIZED_ONLY;
# statistics propagation in groups
# no null values
query II
EXPLAIN SELECT i IS NULL FROM (SELECT i FROM integers GROUP BY i) integers(i);
----
logical_opt <!REGEX>:.*IS_NULL.*
# i=4 is out of range [1,3]
query II
EXPLAIN SELECT i=4 FROM (SELECT i FROM integers GROUP BY i) integers(i);
----
logical_opt <!REGEX>:.*\(i = 4\).*
# i=3 is in range [1,3]
query II
EXPLAIN SELECT i=3 FROM (SELECT i FROM integers GROUP BY i) integers(i);
----
logical_opt <REGEX>:.*\(i = 3\).*
# count without null values
query I
SELECT COUNT(i) FROM integers;
----
3
query II
SELECT i, COUNT(i) FROM integers GROUP BY i ORDER BY i;
----
1 1
2 1
3 1
statement ok
INSERT INTO integers VALUES (NULL);
# count with null values
query I
SELECT COUNT(i) FROM integers;
----
3
query II
SELECT i, COUNT(i) FROM integers GROUP BY i ORDER BY i;
----
NULL 0
1 1
2 1
3 1
# sum
# sum that fits in int64
statement ok
CREATE TABLE bigints AS SELECT i::BIGINT as i FROM (VALUES (1), (2), (3)) tbl(i);
query I
SELECT SUM(i) FROM bigints;
----
6
# avg
query I
SELECT AVG(i) FROM bigints;
----
2
# sum no longer fits in int64
statement ok
INSERT INTO bigints VALUES (9223372036854775806);
query I
SELECT SUM(i) FROM bigints;
----
9223372036854775812
query I
SELECT AVG(i) FROM bigints;
----
2305843009213693952
statement ok
DROP TABLE bigints;
statement ok
CREATE TABLE bigints AS SELECT i::BIGINT as i FROM (VALUES (-1), (-2), (-3)) tbl(i);
query I
SELECT SUM(i) FROM bigints;
----
-6
# avg
query I
SELECT AVG(i) FROM bigints;
----
-2
# sum no longer fits in int64 [negative]
statement ok
INSERT INTO bigints VALUES (-9223372036854775806);
query I
SELECT SUM(i) FROM bigints;
----
-9223372036854775812
query I
SELECT AVG(i) FROM bigints;
----
-2305843009213693952
# now with decimals
# sum that fits in int64
statement ok
CREATE TABLE decimals AS SELECT i::DECIMAL(18,1) as i FROM (VALUES (1), (2), (3)) tbl(i);
query I
SELECT SUM(i) FROM decimals;
----
6.0
# avg
query I
SELECT AVG(i) FROM decimals;
----
2.0
# sum no longer fits in int64
statement ok
INSERT INTO decimals SELECT 99999999999999999.9 FROM repeat(1, 10)
query I
SELECT SUM(i) FROM decimals;
----
1000000000000000005.0
query I
SELECT AVG(i) FROM decimals;
----
76923076923076923

View File

@@ -0,0 +1,191 @@
# name: test/optimizer/statistics/statistics_between.test
# description: Statistics propagation test with between expression
# group: [statistics]
statement ok
CREATE TABLE integers AS SELECT * FROM (VALUES (1), (2), (3)) tbl(i);
statement ok
PRAGMA explain_output = OPTIMIZED_ONLY;
# filter is out of range: no need to execute it
query II
EXPLAIN SELECT i=3 FROM integers WHERE i BETWEEN 0 AND 2
----
logical_opt <!REGEX>:.*\(i = 3\).*
# filter is in range: need to execute it
query II
EXPLAIN SELECT i=1 FROM integers WHERE i BETWEEN 0 AND 2
----
logical_opt <REGEX>:.*\(i = 1\).*
# between where lhs is bigger than rhs: we can prune this entirely
query II
EXPLAIN SELECT * FROM integers WHERE i BETWEEN 3 AND 2
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# now verify all of the results
query I
SELECT i=3 FROM integers WHERE i BETWEEN 0 AND 2;
----
0
0
query I
SELECT i=1 FROM integers WHERE i BETWEEN 0 AND 2;
----
1
0
query I
SELECT * FROM integers WHERE i BETWEEN 3 AND 2;
----
# now test the same with a subquery, where we don't have filter pushdown into the scan
query II
EXPLAIN SELECT i=3 FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN 0 AND 2
----
logical_opt <!REGEX>:.*\(i = 3\).*
query II
EXPLAIN SELECT i=1 FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN 0 AND 2
----
logical_opt <REGEX>:.*\(i = 1\).*
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN 3 AND 2
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# lower clause is always true: between should be converted into i <= 2
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN 0 AND 2;
----
logical_opt <REGEX>:.*\(i <= 2\).*
# upper clause is always true: between should be converted into i >= 2
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN 2 AND 10;
----
logical_opt <REGEX>:.*\(i >= 2\).*
# between is always false
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN -1 AND 0;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
query II
EXPLAIN SELECT i BETWEEN -1 AND 0 FROM (SELECT * FROM integers LIMIT 10) integers(i);
----
logical_opt <REGEX>:.*false.*
# verify the results
query I
SELECT i=3 FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN 0 AND 2;
----
0
0
query I
SELECT i=1 FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN 0 AND 2;
----
1
0
query I
SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN 3 AND 2;
----
query I
SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN 0 AND 10;
----
1
2
3
query I
SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN 0 AND 2;
----
1
2
query I
SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN 2 AND 10;
----
2
3
query I
SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN -1 AND 0;
----
query I
SELECT i BETWEEN -1 AND 0 FROM (SELECT * FROM integers LIMIT 10) integers(i);
----
0
0
0
statement ok
PRAGMA explain_output = PHYSICAL_ONLY;
# wide between: both are always true, entire filter can be pruned. (happens during physical planning).
# see https://github.com/duckdb/duckdb-fuzzer/issues/1357
# https://github.com/duckdb/duckdb-fuzzer/issues/1358
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN 0 AND 10;
----
physical_plan <!REGEX>:.*FILTER.*
statement ok
PRAGMA explain_output = OPTIMIZED_ONLY;
# now insert a null value
statement ok
INSERT INTO integers VALUES (NULL)
# between is always false or null: we can still prune the entire filter
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN -1 AND 0;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# between is always false or null: we can still prune the entire filter
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN -1 AND 0;
----
logical_opt <!REGEX>:.*FILTER.*
# however, if used in a select clause, we can only replace it with a constant_or_null clause
query II
EXPLAIN SELECT i BETWEEN -1 AND 0 FROM (SELECT * FROM integers LIMIT 10) integers(i);
----
logical_opt <REGEX>:.*constant_or_null.*
# in the case of null values we cannot prune the filter here
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN 0 AND 10;
----
logical_opt <REGEX>:.*FILTER.*
query I
SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN -1 AND 0;
----
query I
SELECT i BETWEEN -1 AND 0 FROM (SELECT * FROM integers LIMIT 10) integers(i);
----
0
0
0
NULL
query I
SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i BETWEEN 0 AND 10;
----
1
2
3

View File

@@ -0,0 +1,46 @@
# name: test/optimizer/statistics/statistics_case.test
# description: Test filter propagation in CASE expression
# group: [statistics]
statement ok
PRAGMA explain_output = OPTIMIZED_ONLY;
statement ok
CREATE TABLE integers AS SELECT * FROM (VALUES (1), (2), (3)) tbl(i);
# "i" does not contain null values, so we can statically determine this will not be null
query II
EXPLAIN SELECT * FROM integers WHERE (CASE WHEN i=2 THEN i+1 ELSE i+2 END) IS NULL;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# we can't here, because one of the children of the case has null
query II
EXPLAIN SELECT * FROM integers WHERE (CASE WHEN i=2 THEN i+1 ELSE NULL END) IS NULL;
----
logical_opt <!REGEX>:.*EMPTY_RESULT.*
# now check overflow testing
# this gives an overflow on the RHS
statement error
SELECT 123::TINYINT + (CASE WHEN i=2 THEN (i+1)::TINYINT ELSE (i+2)::TINYINT END) FROM integers;
----
<REGEX>:Out of Range Error:.*Overflow in addition.*
# this does not
query I
SELECT 122::TINYINT + (CASE WHEN i=2 THEN (i+1)::TINYINT ELSE (i+2)::TINYINT END) FROM integers;
----
125
125
127
query I
SELECT * FROM integers WHERE (CASE WHEN i=2 THEN i+1 ELSE i+2 END) IS NULL;
----
query I
SELECT * FROM integers WHERE (CASE WHEN i=2 THEN i+1 ELSE NULL END) IS NULL;
----
1
3

View File

@@ -0,0 +1,109 @@
# name: test/optimizer/statistics/statistics_coalesce.test
# description: Test statistics propagation in COALESCE expression
# group: [statistics]
statement ok
PRAGMA explain_output = OPTIMIZED_ONLY;
statement ok
CREATE TABLE integers AS SELECT * FROM (VALUES (1), (2), (3)) tbl(i);
# "i" does not contain null values, so the coalesce expression is short-circuited
# "17" is never output
query II
EXPLAIN SELECT * FROM integers WHERE (COALESCE(i, 17)=17);
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# adding NULLs randomly into the coalesce does not change anything
query II
EXPLAIN SELECT * FROM integers WHERE (COALESCE(NULL, NULL, NULL, i, NULL, 17)=17);
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# same here, i is never output, the expression is a constant false
query II
EXPLAIN SELECT * FROM integers WHERE (COALESCE(4, i, 17)=3);
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
query II
EXPLAIN SELECT * FROM integers WHERE (COALESCE(i, 4, 17)=3);
----
logical_opt <!REGEX>:.*EMPTY_RESULT.*
# execute the queries
query I
SELECT * FROM integers WHERE (COALESCE(i, 17)=17);
----
query I
SELECT * FROM integers WHERE (COALESCE(NULL, NULL, NULL, i, NULL, 17)=17);
----
query I
SELECT * FROM integers WHERE (COALESCE(4, i, 17)=3);
----
query I
SELECT * FROM integers WHERE (COALESCE(i, 4, 17)=3);
----
3
statement ok
INSERT INTO integers VALUES (NULL);
# after inserting a NULL, the coalesce result changes
query II
EXPLAIN SELECT * FROM integers WHERE (COALESCE(i, 17)=17);
----
logical_opt <!REGEX>:.*EMPTY_RESULT.*
query II
EXPLAIN SELECT * FROM integers WHERE (COALESCE(NULL, NULL, NULL, i, NULL, 17)=17);
----
logical_opt <!REGEX>:.*EMPTY_RESULT.*
query II
EXPLAIN SELECT * FROM integers WHERE (COALESCE(4, i, 17)=3);
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
query II
EXPLAIN SELECT * FROM integers WHERE (COALESCE(i, 4, 17)=3);
----
logical_opt <!REGEX>:.*EMPTY_RESULT.*
# execute the queries
query I
SELECT * FROM integers WHERE (COALESCE(i, 17)=17);
----
NULL
query I
SELECT * FROM integers WHERE (COALESCE(NULL, NULL, NULL, i, NULL, 17)=17);
----
NULL
query I
SELECT * FROM integers WHERE (COALESCE(4, i, 17)=3);
----
query I
SELECT * FROM integers WHERE (COALESCE(i, 4, 17)=3);
----
3
# Verification compares results, which are different for random().
statement ok
PRAGMA disable_verification
statement ok
PRAGMA disable_verify_fetch_row;
# COALESCE without statistics.
statement ok
SELECT COALESCE (CASE WHEN RANDOM() < 100 THEN RANDOM() ELSE NULL END, NULL, 42)
FROM range(10)

View File

@@ -0,0 +1,259 @@
# name: test/optimizer/statistics/statistics_filter.test_slow
# description: Statistics propagation test with filters
# group: [statistics]
foreach type utinyint usmallint uinteger ubigint tinyint smallint integer bigint hugeint uhugeint float double
statement ok
CREATE TABLE integers AS SELECT i::${type} i FROM (VALUES (1), (2), (3)) tbl(i);
statement ok
PRAGMA explain_output = OPTIMIZED_ONLY;
statement ok
PRAGMA enable_verification
# = filter is out of range
query II
EXPLAIN SELECT * FROM integers WHERE i=0;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
query II
EXPLAIN SELECT * FROM integers WHERE i=4;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# = filter is in range
query II
EXPLAIN SELECT * FROM integers WHERE i=1;
----
logical_opt <!REGEX>:.*EMPTY_RESULT.*
# > filter is out of range
query II
EXPLAIN SELECT * FROM integers WHERE i>3;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# > filter is in of range
query II
EXPLAIN SELECT * FROM integers WHERE i>2;
----
logical_opt <!REGEX>:.*EMPTY_RESULT.*
# >= filter is out of range
query II
EXPLAIN SELECT * FROM integers WHERE i>=4;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# >= filter is in of range
query II
EXPLAIN SELECT * FROM integers WHERE i>=3;
----
logical_opt <!REGEX>:.*EMPTY_RESULT.*
# < filter is out of range
query II
EXPLAIN SELECT * FROM integers WHERE i<1;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# < filter is in of range
query II
EXPLAIN SELECT * FROM integers WHERE i<2;
----
logical_opt <!REGEX>:.*EMPTY_RESULT.*
# <= filter is out of range
query II
EXPLAIN SELECT * FROM integers WHERE i<=0;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# <= filter is in of range
query II
EXPLAIN SELECT * FROM integers WHERE i<=1;
----
logical_opt <!REGEX>:.*EMPTY_RESULT.*
# verify that all these queries return correct results
query I
SELECT * FROM integers WHERE i=0;
----
query I
SELECT * FROM integers WHERE i=4;
----
query I
SELECT * FROM integers WHERE i=1;
----
1
query I
SELECT * FROM integers WHERE i>3;
----
query I
SELECT * FROM integers WHERE i>2;
----
3
query I
SELECT * FROM integers WHERE i>=4;
----
query I
SELECT * FROM integers WHERE i>=3;
----
3
query I
SELECT * FROM integers WHERE i<1;
----
query I
SELECT * FROM integers WHERE i<2;
----
1
query I
SELECT * FROM integers WHERE i<=0;
----
query I
SELECT * FROM integers WHERE i<=1;
----
1
# we repeat everything we did above, but with a subquery with a limit
# the limit prevents the filter from being pushed down into the scan
# which causes these tests to test different behavior
# = filter is out of range
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i=0;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i=4;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# = filter is in range
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i=1;
----
logical_opt <!REGEX>:.*EMPTY_RESULT.*
# > filter is out of range
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i>3;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# > filter is in of range
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i>2;
----
logical_opt <!REGEX>:.*EMPTY_RESULT.*
# >= filter is out of range
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i>=4;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# >= filter is in of range
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i>=3;
----
logical_opt <!REGEX>:.*EMPTY_RESULT.*
# < filter is out of range
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i<1;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# < filter is in of range
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i<2;
----
logical_opt <!REGEX>:.*EMPTY_RESULT.*
# <= filter is out of range
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i<=0;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# <= filter is in of range
query II
EXPLAIN SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i<=1;
----
logical_opt <!REGEX>:.*EMPTY_RESULT.*
# verify that all these queries return correct results
query I
SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i=0;
----
query I
SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i=4;
----
query I
SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i=1;
----
1
query I
SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i>3;
----
query I
SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i>2;
----
3
query I
SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i>=4;
----
query I
SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i>=3;
----
3
query I
SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i<1;
----
query I
SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i<2;
----
1
query I
SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i<=0;
----
query I
SELECT * FROM (SELECT * FROM integers LIMIT 10) integers(i) WHERE i<=1;
----
1
statement ok
DROP TABLE integers;
endloop

View File

@@ -0,0 +1,133 @@
# name: test/optimizer/statistics/statistics_filter_multicolumn.test
# description: Statistics propagation through filters with multiple columns
# group: [statistics]
statement ok
CREATE TABLE integers AS SELECT * FROM (VALUES (1), (2), (3)) tbl(i);
statement ok
CREATE TABLE integers2 AS SELECT * FROM (VALUES (2), (3), (4)) tbl(i);
statement ok
PRAGMA explain_output = OPTIMIZED_ONLY;
# we can statically prove that i=1 is false
query II
EXPLAIN SELECT i=1 FROM integers JOIN integers2 USING (i);
----
logical_opt <!REGEX>:.*\(i = 1\).*
# same with i=4
query II
EXPLAIN SELECT i=4 FROM integers JOIN integers2 USING (i);
----
logical_opt <!REGEX>:.*\(i = 4\).*
# and with i>3
query II
EXPLAIN SELECT i>3 FROM integers JOIN integers2 USING (i);
----
logical_opt <!REGEX>:.*\(i > 3\).*
# and with i<2
query II
EXPLAIN SELECT i<2 FROM integers JOIN integers2 USING (i);
----
logical_opt <!REGEX>:.*\(i < 2\).*
# NOT the case for i>2
query II
EXPLAIN SELECT i>2 FROM integers JOIN integers2 USING (i);
----
logical_opt <REGEX>:.*\(i > 2\).*
# OR i<3
query II
EXPLAIN SELECT i<3 FROM integers JOIN integers2 USING (i);
----
logical_opt <REGEX>:.*\(i < 3\).*
# range joins
# we again join two tables with i:[1, 3] on the left, and i:[2, 4] on the right
# but now on i2.i<i1.i
# the statistics are now: i1.i: [2,3], i2.i: [2, 3]
# so we can prune i=4
query II
EXPLAIN SELECT i2.i=4 FROM integers i1 JOIN integers2 i2 ON (i2.i<i1.i);
----
logical_opt <!REGEX>:.*\(i = 4\).*
# or i=1
query II
EXPLAIN SELECT i1.i=1 FROM integers i1 JOIN integers2 i2 ON (i2.i<i1.i);
----
logical_opt <!REGEX>:.*\(i = 1\).*
# but not i=3
query II
EXPLAIN SELECT i2.i=3 FROM integers i1 JOIN integers2 i2 ON (i2.i<i1.i);
----
logical_opt <REGEX>:.*\(i = 3\).*
# or i=2
query II
EXPLAIN SELECT i1.i=2 FROM integers i1 JOIN integers2 i2 ON (i2.i<i1.i);
----
logical_opt <REGEX>:.*\(i = 2\).*
query I
SELECT i=1 FROM integers JOIN integers2 USING (i);
----
0
0
query I
SELECT i=4 FROM integers JOIN integers2 USING (i);
----
0
0
query I
SELECT i>3 FROM integers JOIN integers2 USING (i);
----
0
0
query I
SELECT i<2 FROM integers JOIN integers2 USING (i);
----
0
0
query I rowsort
SELECT i>2 FROM integers JOIN integers2 USING (i);
----
0
1
query I rowsort
SELECT i<3 FROM integers JOIN integers2 USING (i);
----
0
1
query I
SELECT i2.i=4 FROM integers i1 JOIN integers2 i2 ON (i2.i<i1.i);
----
0
query I
SELECT i1.i=1 FROM integers i1 JOIN integers2 i2 ON (i2.i<i1.i);
----
0
query I
SELECT i2.i=3 FROM integers i1 JOIN integers2 i2 ON (i2.i<i1.i);
----
0
query I
SELECT i1.i=2 FROM integers i1 JOIN integers2 i2 ON (i2.i<i1.i);
----
0

View File

@@ -0,0 +1,139 @@
# name: test/optimizer/statistics/statistics_is_null.test
# description: Test filter propagation in IS NULL/IS NOT NULL operands
# group: [statistics]
statement ok
SET default_null_order='nulls_first';
statement ok
CREATE TABLE integers AS SELECT * FROM (VALUES (1), (2), (3)) tbl(i);
statement ok
CREATE TABLE integers2 AS SELECT * FROM (VALUES (4), (5), (NULL)) tbl(i);
statement ok
PRAGMA explain_output = OPTIMIZED_ONLY;
# we can statically determine IS NULL/IS NOT NULL are false, if there are no null values for this column
query II
EXPLAIN SELECT i IS NULL FROM integers;
----
logical_opt <!REGEX>:.*IS NULL.*
query II
EXPLAIN SELECT i IS NOT NULL FROM integers;
----
logical_opt <!REGEX>:.*IS NOT NULL.*
# if there are null values, however, we have to execute the operator
query II
EXPLAIN SELECT i IS NULL FROM integers2;
----
logical_opt <REGEX>:.*IS NULL.*
query II
EXPLAIN SELECT i IS NOT NULL FROM integers2;
----
logical_opt <REGEX>:.*IS NOT NULL.*
# filters remove null values, so even if the base column contains null values, we don't need to check IS NULL here
query II
EXPLAIN SELECT i IS NULL FROM integers2 WHERE i>0;
----
logical_opt <!REGEX>:.*IS NULL.*
# left/right outer joins with false condition can convert to cross product with constant NULL value,
# so we don't need to check IS NULL here, since it's always NULL
query II
EXPLAIN SELECT i2.i IS NULL FROM integers i1 LEFT JOIN integers i2 ON (false);
----
logical_opt <!REGEX>:.*IS NULL.*
# full outer joins can introduce nulls, even if the base tables do not contain them
query II
EXPLAIN SELECT i1.i IS NULL FROM integers i1 FULL OUTER JOIN integers i2 ON (false);
----
logical_opt <REGEX>:.*IS NULL.*
# verify that all these queries produce the correct results
query I
SELECT i IS NULL FROM integers;
----
0
0
0
query I
SELECT i IS NOT NULL FROM integers;
----
1
1
1
query I
SELECT i IS NULL FROM integers2;
----
0
0
1
query I
SELECT i IS NOT NULL FROM integers2;
----
1
1
0
query I
SELECT i IS NULL FROM integers2 WHERE i>0;
----
0
0
query I
SELECT i2.i IS NULL FROM integers i1 LEFT JOIN integers i2 ON (false);
----
1
1
1
query I
SELECT i1.i IS NULL FROM integers i1 FULL OUTER JOIN integers i2 ON (false) ORDER BY i1.i;
----
1
1
1
0
0
0
# FIXME: we don't yet correctly track when columns don't contain valid values
mode skip
statement ok
CREATE TABLE integers3 AS SELECT * FROM (VALUES (NULL), (NULL), (NULL)) tbl(i);
# we can statically determine IS NULL/IS NOT NULL are true, if there are no valid values for this column
query II
EXPLAIN SELECT i IS NULL FROM integers3;
----
logical_opt <!REGEX>:.*IS NULL.*
query II
EXPLAIN SELECT i IS NOT NULL FROM integers3;
----
logical_opt <!REGEX>:.*IS NOT NULL.*
query I
SELECT i IS NULL FROM integers3;
----
true
true
true
query I
SELECT i IS NOT NULL FROM integers3;
----
false
false
false

View File

@@ -0,0 +1,184 @@
# name: test/optimizer/statistics/statistics_join.test
# description: Statistics propagation test with joins
# group: [statistics]
statement ok
CREATE TABLE integers AS SELECT * FROM (VALUES (1), (2), (3)) tbl(i);
statement ok
CREATE TABLE integers2 AS SELECT * FROM (VALUES (4), (5), (6)) tbl(i);
statement ok
PRAGMA explain_output = OPTIMIZED_ONLY;
# inner join
# join cannot match
query II
EXPLAIN SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i1.i=i2.i ORDER BY 1;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
query II
EXPLAIN SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i1.i>i2.i ORDER BY 1;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
query II
EXPLAIN SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i1.i>=i2.i ORDER BY 1;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
query II
EXPLAIN SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i2.i<i1.i ORDER BY 1;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
query II
EXPLAIN SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i2.i<=i1.i ORDER BY 1;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# join is guaranteed to match
query II
EXPLAIN SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i1.i<i2.i ORDER BY 1;
----
logical_opt <REGEX>:.*CROSS_PRODUCT.*
query II
EXPLAIN SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i1.i<=i2.i ORDER BY 1;
----
logical_opt <REGEX>:.*CROSS_PRODUCT.*
query II
EXPLAIN SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i2.i>i1.i ORDER BY 1;
----
logical_opt <REGEX>:.*CROSS_PRODUCT.*
query II
EXPLAIN SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i2.i>=i1.i ORDER BY 1;
----
logical_opt <REGEX>:.*CROSS_PRODUCT.*
# left join
# join cannot match
# note that the result is not actually empty here; only the RHS is set to the empty result
query II
EXPLAIN SELECT i1.i FROM integers i1 LEFT JOIN integers2 i2 ON i1.i=i2.i ORDER BY 1;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# semi join
# join cannot match: replaced with empty result
query II
explain select * from integers where i > any(select * from integers2) order by 1;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# join is guaranteed to match: entire join is stripped away
query II
explain select * from integers where i < any(select * from integers2) order by 1;
----
logical_opt <!REGEX>:.*JOIN.*
query I
SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i1.i=i2.i ORDER BY 1;
----
query I
SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i1.i>i2.i ORDER BY 1;
----
query I
SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i1.i>=i2.i ORDER BY 1;
----
query I
SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i2.i<i1.i ORDER BY 1;
----
query I
SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i2.i<=i1.i ORDER BY 1;
----
query I
SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i1.i<i2.i ORDER BY 1;
----
1
1
1
2
2
2
3
3
3
query I
SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i1.i<=i2.i ORDER BY 1;
----
1
1
1
2
2
2
3
3
3
query I
SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i2.i>i1.i ORDER BY 1;
----
1
1
1
2
2
2
3
3
3
query I
SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i2.i>=i1.i ORDER BY 1;
----
1
1
1
2
2
2
3
3
3
query I
SELECT i1.i FROM integers i1 LEFT JOIN integers2 i2 ON i1.i=i2.i ORDER BY 1;
----
1
2
3
query I
SELECT i1.i FROM integers i1 LEFT JOIN integers2 i2 ON i1.i<i2.i ORDER BY 1;
----
1
1
1
2
2
2
3
3
3
query I
select * from integers where i > any(select * from integers2) order by 1;
----
query I
select * from integers where i < any(select * from integers2) order by 1;
----
1
2
3

View File

@@ -0,0 +1,42 @@
# name: test/optimizer/statistics/statistics_join_multicolumn.test
# description: Statistics propagation through joins on multiple columns
# group: [statistics]
statement ok
CREATE TABLE integers AS SELECT * FROM (VALUES (1), (2), (10)) tbl(i);
statement ok
CREATE TABLE integers2 AS SELECT * FROM (VALUES (2, 5), (3, 6), (4, 7)) tbl(i, j);
statement ok
PRAGMA explain_output = OPTIMIZED_ONLY;
# inner join
# integers.i has [1, 10] as min max
# integers2.i is [2, 4], integers2.j [5, 7]
# we compare integers.i = integers2.i AND integers.i = integers2.j
# these sets are disjoint, so we get an empty result
query II
EXPLAIN SELECT i1.i FROM integers i1 JOIN integers2 i2 ON (i1.i=i2.i AND i1.i=i2.j);
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
query II
EXPLAIN SELECT i1.i FROM integers i1 JOIN integers2 i2 ON (i1.i=i2.i AND i1.i>i2.j);
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# when we do i1.i < i2.j, the sets are no longer disjoint, because the set [2, 4] is smaller than the set [5, 7]
query II
EXPLAIN SELECT i1.i FROM integers i1 JOIN integers2 i2 ON (i1.i=i2.i AND i1.i<i2.j);
----
logical_opt <!REGEX>:.*EMPTY_RESULT.*
query I
SELECT i1.i FROM integers i1 JOIN integers2 i2 ON (i1.i=i2.i AND i1.i=i2.j);
----
query I
SELECT i1.i FROM integers i1 JOIN integers2 i2 ON (i1.i=i2.i AND i1.i<i2.j);
----
2

View File

@@ -0,0 +1,79 @@
# name: test/optimizer/statistics/statistics_join_null.test
# description: Statistics propagation test with joins and null values
# group: [statistics]
statement ok
CREATE TABLE integers AS SELECT * FROM (VALUES (1), (2), (NULL)) tbl(i);
statement ok
CREATE TABLE integers2 AS SELECT * FROM (VALUES (4), (5), (NULL)) tbl(i);
statement ok
PRAGMA explain_output = OPTIMIZED_ONLY;
# inner join
# join cannot match: prune result
query II
EXPLAIN SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i1.i=i2.i ORDER BY 1;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*
# join is guaranteed to match... except there are null values!
# we CANNOT convert to a cross product
# (well... we could, if we would push a x IS NOT NULL filter on both sides)
# (but that is for another day)
query II
EXPLAIN SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i1.i<i2.i ORDER BY 1;
----
logical_opt <!REGEX>:.*CROSS_PRODUCT.*
query II
EXPLAIN SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i1.i<=i2.i ORDER BY 1;
----
logical_opt <!REGEX>:.*CROSS_PRODUCT.*
query II
EXPLAIN SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i2.i>i1.i ORDER BY 1;
----
logical_opt <!REGEX>:.*CROSS_PRODUCT.*
query II
EXPLAIN SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i2.i>=i1.i ORDER BY 1;
----
logical_opt <!REGEX>:.*CROSS_PRODUCT.*
query I
SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i1.i=i2.i ORDER BY 1;
----
query I
SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i1.i<i2.i ORDER BY 1;
----
1
1
2
2
query I
SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i1.i<=i2.i ORDER BY 1;
----
1
1
2
2
query I
SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i2.i>i1.i ORDER BY 1;
----
1
1
2
2
query I
SELECT i1.i FROM integers i1 JOIN integers2 i2 ON i2.i>=i1.i ORDER BY 1;
----
1
1
2
2

View File

@@ -0,0 +1,94 @@
# name: test/optimizer/statistics/statistics_null_comparison.test
# description: Statistics propagation with comparisons and null values
# group: [statistics]
statement ok
CREATE TABLE integers AS SELECT * FROM (VALUES (10, 1), (20, 2), (30, NULL)) tbl(i, j);
statement ok
CREATE TABLE integers2 AS SELECT * FROM (VALUES (1), (2), (NULL)) tbl(i);
statement ok
CREATE TABLE t1 AS SELECT * FROM (VALUES (1)) tbl(c0);
statement ok
CREATE TABLE t2 AS SELECT * FROM (VALUES (NULL)) tbl(c0);
statement ok
PRAGMA explain_output = OPTIMIZED_ONLY;
# this is statically false, but there are null values NOT in a filter, so we can't optimize it away
# instead the comparison is replaced by a CONSTANT OR NULL
query II
EXPLAIN SELECT i=j FROM integers ORDER BY i;
----
logical_opt <REGEX>:.*constant_or_null.*
# if we put the same expression in a where clause, however, we CAN prune it
query II
EXPLAIN SELECT * FROM integers WHERE i=j ORDER BY i;
----
logical_opt <!REGEX>:.*constant_or_null.*
# this is statically true, but there are null values, so we can't optimize it away
query II
EXPLAIN SELECT i>j FROM integers ORDER BY i;
----
logical_opt <REGEX>:.*constant_or_null.*
# if we put it in the where clause, we STILL can't prune it
query II
EXPLAIN SELECT * FROM integers WHERE i>j ORDER BY i;
----
logical_opt <REGEX>:.*constant_or_null.*
# now verify that the results are correct
query I
SELECT i=j FROM integers ORDER BY i;
----
0
0
NULL
query II
SELECT * FROM integers WHERE i=j ORDER BY i;
----
query I
SELECT j=i FROM integers ORDER BY i;
----
0
0
NULL
query I
SELECT i>j FROM integers ORDER BY i;
----
1
1
NULL
query II
SELECT * FROM integers WHERE i>j ORDER BY i;
----
10 1
20 2
# relate issue 17338
query II
SELECT * FROM t1 INNER JOIN t2 ON ((t2.c0)>=(t2.c0));
----
statement ok
INSERT INTO t2 VALUES(1);
# t2.c0>=t2.c0 means always true or null which is equals to is not null
query II
EXPLAIN SELECT * FROM t1 INNER JOIN t2 ON ((t2.c0)>=(t2.c0));
----
logical_opt <REGEX>:.*IS NOT NULL.*
query II
SELECT * FROM t1 INNER JOIN t2 ON ((t2.c0)>=(t2.c0));
----
1 1

View File

@@ -0,0 +1,102 @@
# name: test/optimizer/statistics/statistics_numeric.test
# description: Statistics propagation of numeric functions
# group: [statistics]
statement ok
PRAGMA explain_output = OPTIMIZED_ONLY;
statement ok
CREATE TABLE integers(i INTEGER);
statement ok
INSERT INTO integers VALUES (1), (10);
# abs with only positive values
query I
SELECT ABS(i) FROM integers ORDER BY i
----
1
10
query I
SELECT STATS(ABS(i)) FROM integers LIMIT 1
----
<REGEX>:.*1.*10.*
# verify that the call to abs gets removed by the optimizer out
query II
EXPLAIN SELECT ABS(i) FROM integers ORDER BY i;
----
logical_opt <!REGEX>:.*abs.*
# mix of positive and negative values
statement ok
INSERT INTO integers VALUES (-5)
query I
SELECT STATS(ABS(i)) FROM integers LIMIT 1
----
<REGEX>:.*0.*10.*
query I
SELECT ABS(i) FROM integers ORDER BY i
----
5
1
10
# the call to abs can no longer be removed
query II
EXPLAIN SELECT ABS(i) FROM integers ORDER BY i;
----
logical_opt <REGEX>:.*abs.*
statement ok
INSERT INTO integers VALUES (-15)
query I
SELECT STATS(ABS(i)) FROM integers LIMIT 1
----
<REGEX>:.*0.*15.*
query I
SELECT ABS(i) FROM integers ORDER BY i
----
15
5
1
10
statement ok
INSERT INTO integers VALUES (0)
query I
SELECT STATS(ABS(i)) FROM integers LIMIT 1
----
<REGEX>:.*0.*15.*
query I
SELECT ABS(i) FROM integers ORDER BY i
----
15
5
0
1
10
# only negative values
statement ok
DROP TABLE integers
statement ok
CREATE TABLE integers(i INTEGER);
statement ok
INSERT INTO integers VALUES (-1), (-10);
query I
SELECT STATS(ABS(i)) FROM integers LIMIT 1
----
<REGEX>:.*1.*10.*

View File

@@ -0,0 +1,143 @@
# name: test/optimizer/statistics/statistics_setop.test
# description: Statistics propagation test with set operations
# group: [statistics]
statement ok
CREATE TABLE integers AS SELECT * FROM (VALUES (1), (2), (3)) tbl(i);
statement ok
CREATE TABLE integers2 AS SELECT * FROM (VALUES (4), (5), (6)) tbl(i);
statement ok
CREATE TABLE integers3 AS SELECT * FROM (VALUES (4), (5), (NULL)) tbl(i);
statement ok
PRAGMA explain_output = OPTIMIZED_ONLY;
# union all
# total min/max after union all is [1, 6]: 7 is out of bounds, so i=7 can be optimized away
query II
EXPLAIN SELECT i=7 FROM (SELECT * FROM integers UNION ALL SELECT * FROM integers2) tbl(i);
----
logical_opt <!REGEX>:.*\(i = 7\).*
# 5 is in bounds, so this cannot be optimized away
query II
EXPLAIN SELECT i=5 FROM (SELECT * FROM integers UNION ALL SELECT * FROM integers2) tbl(i);
----
logical_opt <REGEX>:.*\(i = 5\).*
# neither integers nor integers2 has null values, so this is false
query II
EXPLAIN SELECT i IS NULL FROM (SELECT * FROM integers UNION ALL SELECT * FROM integers2) tbl(i);
----
logical_opt <!REGEX>:.*IS NULL.*
# integers3 has null values, so once we include integers3 we need to execute the IS_NULL predicate
query II
EXPLAIN SELECT i IS NULL FROM (SELECT * FROM integers UNION ALL SELECT * FROM integers2 UNION ALL SELECT * FROM integers3) tbl(i);
----
logical_opt <REGEX>:.*IS NULL.*
# except
# except has the same stats as the LHS (as in the worst case, nothing is filtered out)
# in this case the LHS stats are [4, 6] without null values
query II
EXPLAIN SELECT i=7 FROM (SELECT * FROM integers2 EXCEPT SELECT * FROM integers3) tbl(i);
----
logical_opt <!REGEX>:.*\(i = 7\).*
query II
EXPLAIN SELECT i=5 FROM (SELECT * FROM integers2 EXCEPT SELECT * FROM integers3) tbl(i);
----
logical_opt <REGEX>:.*\(i = 5\).*
query II
EXPLAIN SELECT i IS NULL FROM (SELECT * FROM integers2 EXCEPT SELECT * FROM integers3) tbl(i);
----
logical_opt <!REGEX>:.*IS NULL.*
# intersect
# intersect has as stats the intersection of the LHS with the RHS
# in this case that would be [4, 5] with no null values
# however, we don't actually intersect the stats yet, so limit our testing to the stats of the LHS
query II
EXPLAIN SELECT i=7 FROM (SELECT * FROM integers2 INTERSECT SELECT * FROM integers3) tbl(i);
----
logical_opt <!REGEX>:.*\(i = 7\).*
query II
EXPLAIN SELECT i=5 FROM (SELECT * FROM integers2 INTERSECT SELECT * FROM integers3) tbl(i);
----
logical_opt <REGEX>:.*\(i = 5\).*
# now check the results of all these queries
query I
SELECT i=7 FROM (SELECT * FROM integers UNION ALL SELECT * FROM integers2) tbl(i);
----
0
0
0
0
0
0
query I
SELECT i=5 FROM (SELECT * FROM integers UNION ALL SELECT * FROM integers2) tbl(i);
----
0
0
0
0
1
0
query I
SELECT i IS NULL FROM (SELECT * FROM integers UNION ALL SELECT * FROM integers2) tbl(i);
----
0
0
0
0
0
0
query I
SELECT i IS NULL FROM (SELECT * FROM integers UNION ALL SELECT * FROM integers2 UNION ALL SELECT * FROM integers3) tbl(i);
----
0
0
0
0
0
0
0
0
1
query I
SELECT i=7 FROM (SELECT * FROM integers2 EXCEPT SELECT * FROM integers3) tbl(i);
----
0
query I
SELECT i=5 FROM (SELECT * FROM integers2 EXCEPT SELECT * FROM integers3) tbl(i);
----
0
query I
SELECT i IS NULL FROM (SELECT * FROM integers2 EXCEPT SELECT * FROM integers3) tbl(i);
----
0
query I
SELECT i=7 FROM (SELECT * FROM integers2 INTERSECT SELECT * FROM integers3) tbl(i);
----
0
0
query I
SELECT i=5 FROM (SELECT * FROM integers2 INTERSECT SELECT * FROM integers3) tbl(i) ORDER BY i;
----
0
1

View File

@@ -0,0 +1,27 @@
# name: test/optimizer/statistics/statistics_struct.test
# description: Statistics propagation test with structs
# group: [statistics]
statement ok
CREATE TABLE structs AS SELECT {'i': i} c FROM range(4) tbl(i);
statement ok
PRAGMA explain_output = OPTIMIZED_ONLY;
# we can statically determine IS NULL/IS NOT NULL are false, if there are no null values for this column
query II
EXPLAIN SELECT c IS NULL FROM structs;
----
logical_opt <!REGEX>:.*IS_NULL.*
# the same applies to c['i']
query II
EXPLAIN SELECT c['i'] IS NULL FROM structs;
----
logical_opt <!REGEX>:.*IS_NULL.*
# filter is out of range
query II
EXPLAIN SELECT * FROM structs WHERE c['i']=4;
----
logical_opt <REGEX>:.*EMPTY_RESULT.*

View File

@@ -0,0 +1,60 @@
# name: test/optimizer/statistics/statistics_try_cast.test
# description: Statistics with try cast
# group: [statistics]
statement ok
PRAGMA explain_output = OPTIMIZED_ONLY;
statement ok
CREATE TABLE integers AS SELECT range i FROM range(5)
query I
SELECT i::TINYINT IS NULL FROM integers
----
False
False
False
False
False
query I
SELECT TRY_CAST(i AS TINYINT) IS NULL FROM integers
----
False
False
False
False
False
query II
EXPLAIN SELECT i::TINYINT IS NULL FROM integers
----
logical_opt <!REGEX>:.*IS.*NULL.*
query II
EXPLAIN SELECT (TRY_CAST(i AS TINYINT)) IS NULL FROM integers
----
logical_opt <REGEX>:.*IS.*NULL.*
statement ok
INSERT INTO integers VALUES (255)
query I
SELECT TRY_CAST(i AS TINYINT) FROM integers
----
0
1
2
3
4
NULL
query I
SELECT TRY_CAST(i AS TINYINT) IS NULL FROM integers
----
False
False
False
False
False
True

View File

@@ -0,0 +1,74 @@
# name: test/optimizer/statistics/statistics_varchar.test
# description: Statistics propagation test with varchars
# group: [statistics]
statement ok
CREATE TABLE varchars AS SELECT * FROM (VALUES ('Mark'), ('Hannes'), ('World')) tbl(v);
statement ok
PRAGMA explain_output = OPTIMIZED_ONLY;
# no unicode
query II
SELECT LENGTH(v), STRLEN(v) FROM varchars
----
4 4
6 6
5 5
query I
SELECT STRPOS(v, 'e') FROM varchars
----
0
5
0
query II
SELECT UPPER(v), LOWER(v) FROM varchars
----
MARK mark
HANNES hannes
WORLD world
query I
SELECT SUBSTR(v, 2, 2) FROM varchars
----
ar
an
or
# unicode
statement ok
INSERT INTO varchars VALUES ('Mühleisen');
query II
SELECT LENGTH(v), STRLEN(v) FROM varchars
----
4 4
6 6
5 5
9 10
query I
SELECT STRPOS(v, 'e') FROM varchars
----
0
5
0
5
query II
SELECT UPPER(v), LOWER(v) FROM varchars
----
MARK mark
HANNES hannes
WORLD world
MÜHLEISEN mühleisen
query I
SELECT SUBSTR(v, 2, 2) FROM varchars
----
ar
an
or
üh