# Test that we propagate statistics for virtual computed columns correctly.

exec-ddl
CREATE TABLE abc (a INT PRIMARY KEY, b INT AS (a % 10) VIRTUAL, c INT, INDEX (b))
----

# Use statistics from:
#   INSERT INTO abc (a) SELECT generate_series(0, 19)
# Assume we have collected statistics on b, including histograms.
exec-ddl
ALTER TABLE abc INJECT STATISTICS '[
    {
        "avg_size": 8,
        "columns": [
            "a"
        ],
        "created_at": "2024-03-06 19:42:11.250622",
        "distinct_count": 20,
        "histo_buckets": [
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "0"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "1"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "2"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "3"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "4"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "5"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "6"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "7"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "8"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "9"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "10"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "11"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "12"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "13"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "14"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "15"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "16"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "17"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "18"
            },
            {
                "distinct_range": 0,
                "num_eq": 1,
                "num_range": 0,
                "upper_bound": "19"
            }
        ],
        "histo_col_type": "INT8",
        "histo_version": 3,
        "null_count": 0,
        "row_count": 20
    },
    {
        "avg_size": 8,
        "columns": [
            "b"
        ],
        "created_at": "2024-03-06 19:42:11.250622",
        "distinct_count": 10,
        "histo_buckets": [
            {
                "distinct_range": 0,
                "num_eq": 2,
                "num_range": 0,
                "upper_bound": "0"
            },
            {
                "distinct_range": 0,
                "num_eq": 2,
                "num_range": 0,
                "upper_bound": "1"
            },
            {
                "distinct_range": 0,
                "num_eq": 2,
                "num_range": 0,
                "upper_bound": "2"
            },
            {
                "distinct_range": 0,
                "num_eq": 2,
                "num_range": 0,
                "upper_bound": "3"
            },
            {
                "distinct_range": 0,
                "num_eq": 2,
                "num_range": 0,
                "upper_bound": "4"
            },
            {
                "distinct_range": 0,
                "num_eq": 2,
                "num_range": 0,
                "upper_bound": "5"
            },
            {
                "distinct_range": 0,
                "num_eq": 2,
                "num_range": 0,
                "upper_bound": "6"
            },
            {
                "distinct_range": 0,
                "num_eq": 2,
                "num_range": 0,
                "upper_bound": "7"
            },
            {
                "distinct_range": 0,
                "num_eq": 2,
                "num_range": 0,
                "upper_bound": "8"
            },
            {
                "distinct_range": 0,
                "num_eq": 2,
                "num_range": 0,
                "upper_bound": "9"
            }
        ],
        "histo_col_type": "INT8",
        "histo_version": 3,
        "null_count": 0,
        "row_count": 20
    },
    {
        "avg_size": 0,
        "columns": [
            "c"
        ],
        "created_at": "2024-03-06 19:42:11.250622",
        "distinct_count": 1,
        "histo_col_type": "INT8",
        "histo_version": 3,
        "null_count": 20,
        "row_count": 20
    }
]'
----

# Check that we can call colStatScan with a virtual column, even without the
# usual project in place.
norm colstat=2
SELECT a FROM abc
----
scan abc
 ├── columns: a:1(int!null)
 ├── computed column expressions
 │    └── b:2
 │         └── a:1 % 10 [type=int]
 ├── stats: [rows=20, distinct(2)=10, null(2)=0]
 │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2
 │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │   virtcolstats: (2)
 └── key: (1)

# It should also work with the usual project on top.
norm colstat=2
SELECT b FROM abc
----
project
 ├── columns: b:2(int!null)
 ├── immutable
 ├── stats: [rows=20, distinct(2)=10, null(2)=0]
 │   virtcolstats: (2)
 ├── scan abc
 │    ├── columns: a:1(int!null)
 │    ├── computed column expressions
 │    │    └── b:2
 │    │         └── a:1 % 10 [type=int]
 │    ├── stats: [rows=20, distinct(2)=10, null(2)=0]
 │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2
 │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │    │   virtcolstats: (2)
 │    └── key: (1)
 └── projections
      └── a:1 % 10 [as=b:2, type=int, outer=(1), immutable]

# Push a select below the project.
norm colstat=1 colstat=2 expect=PushSelectIntoProject
SELECT b FROM abc WHERE a % 2 = 0
----
project
 ├── columns: b:2(int!null)
 ├── immutable
 ├── stats: [rows=6.666667, distinct(1)=6.66667, null(1)=0, distinct(2)=5.55556, null(2)=0]
 │   virtcolstats: (2)
 ├── select
 │    ├── columns: a:1(int!null)
 │    ├── immutable
 │    ├── stats: [rows=6.666667, distinct(1)=6.66667, null(1)=0, distinct(2)=5.55556, null(2)=0]
 │    │   virtcolstats: (2)
 │    ├── key: (1)
 │    ├── scan abc
 │    │    ├── columns: a:1(int!null)
 │    │    ├── computed column expressions
 │    │    │    └── b:2
 │    │    │         └── a:1 % 10 [type=int]
 │    │    ├── stats: [rows=20, distinct(1)=20, null(1)=0, distinct(2)=10, null(2)=0]
 │    │    │   histogram(1)=  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1
 │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 --- 11 --- 12 --- 13 --- 14 --- 15 --- 16 --- 17 --- 18 --- 19
 │    │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2
 │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │    │    │   virtcolstats: (2)
 │    │    └── key: (1)
 │    └── filters
 │         └── (a:1 % 2) = 0 [type=bool, outer=(1), immutable]
 └── projections
      └── a:1 % 10 [as=b:2, type=int, outer=(1), immutable]

# Push a select on the virtual column below the project.
norm colstat=1 colstat=2
SELECT * FROM abc WHERE b > 3
----
project
 ├── columns: a:1(int!null) b:2(int!null) c:3(int)
 ├── immutable
 ├── stats: [rows=12, distinct(1)=12, null(1)=0, distinct(2)=6, null(2)=0]
 │   virtcolstats: (2)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── select
 │    ├── columns: a:1(int!null) c:3(int)
 │    ├── immutable
 │    ├── stats: [rows=12, distinct(1)=12, null(1)=0, distinct(2)=6, null(2)=0]
 │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2
 │    │                <--- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │    │   virtcolstats: (2)
 │    ├── key: (1)
 │    ├── fd: (1)-->(3)
 │    ├── scan abc
 │    │    ├── columns: a:1(int!null) c:3(int)
 │    │    ├── computed column expressions
 │    │    │    └── b:2
 │    │    │         └── a:1 % 10 [type=int]
 │    │    ├── stats: [rows=20, distinct(1)=20, null(1)=0, distinct(2)=10, null(2)=0]
 │    │    │   histogram(1)=  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1
 │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 --- 11 --- 12 --- 13 --- 14 --- 15 --- 16 --- 17 --- 18 --- 19
 │    │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2
 │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │    │    │   virtcolstats: (2)
 │    │    ├── key: (1)
 │    │    └── fd: (1)-->(3)
 │    └── filters
 │         └── (a:1 % 10) > 3 [type=bool, outer=(1), immutable]
 └── projections
      └── a:1 % 10 [as=b:2, type=int, outer=(1), immutable]

# Again, with placeholders.
assign-placeholders-norm query-args=(3)
SELECT * FROM abc WHERE b > $1
----
project
 ├── columns: a:1(int!null) b:2(int!null) c:3(int)
 ├── immutable
 ├── stats: [rows=12]
 │   virtcolstats: (2)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── select
 │    ├── columns: a:1(int!null) c:3(int)
 │    ├── immutable
 │    ├── stats: [rows=12, distinct(2)=6, null(2)=0]
 │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2
 │    │                <--- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │    │   virtcolstats: (2)
 │    ├── key: (1)
 │    ├── fd: (1)-->(3)
 │    ├── scan abc
 │    │    ├── columns: a:1(int!null) c:3(int)
 │    │    ├── computed column expressions
 │    │    │    └── b:2
 │    │    │         └── a:1 % 10 [type=int]
 │    │    ├── stats: [rows=20, distinct(1)=20, null(1)=0, distinct(2)=10, null(2)=0]
 │    │    │   histogram(1)=  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1
 │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 --- 11 --- 12 --- 13 --- 14 --- 15 --- 16 --- 17 --- 18 --- 19
 │    │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2
 │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │    │    │   virtcolstats: (2)
 │    │    ├── key: (1)
 │    │    └── fd: (1)-->(3)
 │    └── filters
 │         └── (a:1 % 10) > 3 [type=bool, outer=(1), immutable]
 └── projections
      └── a:1 % 10 [as=b:2, type=int, outer=(1), immutable]

# Push a select on the virtual column expression below the project.
norm colstat=1 colstat=2
SELECT * FROM abc WHERE a % 10 > 3
----
project
 ├── columns: a:1(int!null) b:2(int!null) c:3(int)
 ├── immutable
 ├── stats: [rows=12, distinct(1)=12, null(1)=0, distinct(2)=6, null(2)=0]
 │   virtcolstats: (2)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── select
 │    ├── columns: a:1(int!null) c:3(int)
 │    ├── immutable
 │    ├── stats: [rows=12, distinct(1)=12, null(1)=0, distinct(2)=6, null(2)=0]
 │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2
 │    │                <--- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │    │   virtcolstats: (2)
 │    ├── key: (1)
 │    ├── fd: (1)-->(3)
 │    ├── scan abc
 │    │    ├── columns: a:1(int!null) c:3(int)
 │    │    ├── computed column expressions
 │    │    │    └── b:2
 │    │    │         └── a:1 % 10 [type=int]
 │    │    ├── stats: [rows=20, distinct(1)=20, null(1)=0, distinct(2)=10, null(2)=0]
 │    │    │   histogram(1)=  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1
 │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 --- 11 --- 12 --- 13 --- 14 --- 15 --- 16 --- 17 --- 18 --- 19
 │    │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2
 │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │    │    │   virtcolstats: (2)
 │    │    ├── key: (1)
 │    │    └── fd: (1)-->(3)
 │    └── filters
 │         └── (a:1 % 10) > 3 [type=bool, outer=(1), immutable]
 └── projections
      └── a:1 % 10 [as=b:2, type=int, outer=(1), immutable]

# Again, with placeholders.
assign-placeholders-norm query-args=(3)
SELECT * FROM abc WHERE a % 10 > $1
----
project
 ├── columns: a:1(int!null) b:2(int!null) c:3(int)
 ├── immutable
 ├── stats: [rows=12]
 │   virtcolstats: (2)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── select
 │    ├── columns: a:1(int!null) c:3(int)
 │    ├── immutable
 │    ├── stats: [rows=12, distinct(2)=6, null(2)=0]
 │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2
 │    │                <--- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │    │   virtcolstats: (2)
 │    ├── key: (1)
 │    ├── fd: (1)-->(3)
 │    ├── scan abc
 │    │    ├── columns: a:1(int!null) c:3(int)
 │    │    ├── computed column expressions
 │    │    │    └── b:2
 │    │    │         └── a:1 % 10 [type=int]
 │    │    ├── stats: [rows=20, distinct(1)=20, null(1)=0, distinct(2)=10, null(2)=0]
 │    │    │   histogram(1)=  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1
 │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 --- 11 --- 12 --- 13 --- 14 --- 15 --- 16 --- 17 --- 18 --- 19
 │    │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2
 │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │    │    │   virtcolstats: (2)
 │    │    ├── key: (1)
 │    │    └── fd: (1)-->(3)
 │    └── filters
 │         └── (a:1 % 10) > 3 [type=bool, outer=(1), immutable]
 └── projections
      └── a:1 % 10 [as=b:2, type=int, outer=(1), immutable]

# Push a select on the virtual column expression below the project.
norm colstat=1 colstat=2
SELECT * FROM (SELECT a FROM abc) WHERE a % 10 > 3
----
select
 ├── columns: a:1(int!null)
 ├── immutable
 ├── stats: [rows=12, distinct(1)=12, null(1)=0, distinct(2)=6, null(2)=0]
 │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2
 │                <--- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │   virtcolstats: (2)
 ├── key: (1)
 ├── scan abc
 │    ├── columns: a:1(int!null)
 │    ├── computed column expressions
 │    │    └── b:2
 │    │         └── a:1 % 10 [type=int]
 │    ├── stats: [rows=20, distinct(1)=20, null(1)=0, distinct(2)=10, null(2)=0]
 │    │   histogram(1)=  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1
 │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 --- 11 --- 12 --- 13 --- 14 --- 15 --- 16 --- 17 --- 18 --- 19
 │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2
 │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │    │   virtcolstats: (2)
 │    └── key: (1)
 └── filters
      └── (a:1 % 10) > 3 [type=bool, outer=(1), immutable]

# Again, with placeholders.
assign-placeholders-norm query-args=(3)
SELECT * FROM (SELECT a FROM abc) WHERE a % 10 > $1
----
select
 ├── columns: a:1(int!null)
 ├── immutable
 ├── stats: [rows=12, distinct(2)=6, null(2)=0]
 │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2
 │                <--- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │   virtcolstats: (2)
 ├── key: (1)
 ├── scan abc
 │    ├── columns: a:1(int!null)
 │    ├── computed column expressions
 │    │    └── b:2
 │    │         └── a:1 % 10 [type=int]
 │    ├── stats: [rows=20, distinct(1)=20, null(1)=0, distinct(2)=10, null(2)=0]
 │    │   histogram(1)=  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1
 │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 --- 11 --- 12 --- 13 --- 14 --- 15 --- 16 --- 17 --- 18 --- 19
 │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2
 │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │    │   virtcolstats: (2)
 │    └── key: (1)
 └── filters
      └── (a:1 % 10) > 3 [type=bool, outer=(1), immutable]

# Select using an IN set.
norm colstat=1 colstat=2
SELECT * FROM abc WHERE a % 10 IN (2, 4, 6)
----
project
 ├── columns: a:1(int!null) b:2(int!null) c:3(int)
 ├── immutable
 ├── stats: [rows=6, distinct(1)=6, null(1)=0, distinct(2)=3, null(2)=0]
 │   virtcolstats: (2)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── select
 │    ├── columns: a:1(int!null) c:3(int)
 │    ├── immutable
 │    ├── stats: [rows=6, distinct(1)=6, null(1)=0, distinct(2)=3, null(2)=0]
 │    │   histogram(2)=  0  2  0  2  0  2
 │    │                <--- 2 --- 4 --- 6
 │    │   virtcolstats: (2)
 │    ├── key: (1)
 │    ├── fd: (1)-->(3)
 │    ├── scan abc
 │    │    ├── columns: a:1(int!null) c:3(int)
 │    │    ├── computed column expressions
 │    │    │    └── b:2
 │    │    │         └── a:1 % 10 [type=int]
 │    │    ├── stats: [rows=20, distinct(1)=20, null(1)=0, distinct(2)=10, null(2)=0]
 │    │    │   histogram(1)=  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1
 │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 --- 11 --- 12 --- 13 --- 14 --- 15 --- 16 --- 17 --- 18 --- 19
 │    │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2
 │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │    │    │   virtcolstats: (2)
 │    │    ├── key: (1)
 │    │    └── fd: (1)-->(3)
 │    └── filters
 │         └── (a:1 % 10) IN (2, 4, 6) [type=bool, outer=(1), immutable]
 └── projections
      └── a:1 % 10 [as=b:2, type=int, outer=(1), immutable]

# Again, with placeholders.
assign-placeholders-norm query-args=(2, 4, 6)
SELECT * FROM abc WHERE a % 10 IN ($1, $2, $3)
----
project
 ├── columns: a:1(int!null) b:2(int!null) c:3(int)
 ├── immutable
 ├── stats: [rows=6]
 │   virtcolstats: (2)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── select
 │    ├── columns: a:1(int!null) c:3(int)
 │    ├── immutable
 │    ├── stats: [rows=6, distinct(2)=3, null(2)=0]
 │    │   histogram(2)=  0  2  0  2  0  2
 │    │                <--- 2 --- 4 --- 6
 │    │   virtcolstats: (2)
 │    ├── key: (1)
 │    ├── fd: (1)-->(3)
 │    ├── scan abc
 │    │    ├── columns: a:1(int!null) c:3(int)
 │    │    ├── computed column expressions
 │    │    │    └── b:2
 │    │    │         └── a:1 % 10 [type=int]
 │    │    ├── stats: [rows=20, distinct(1)=20, null(1)=0, distinct(2)=10, null(2)=0]
 │    │    │   histogram(1)=  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1
 │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 --- 11 --- 12 --- 13 --- 14 --- 15 --- 16 --- 17 --- 18 --- 19
 │    │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2
 │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │    │    │   virtcolstats: (2)
 │    │    ├── key: (1)
 │    │    └── fd: (1)-->(3)
 │    └── filters
 │         └── (a:1 % 10) IN (2, 4, 6) [type=bool, outer=(1), immutable]
 └── projections
      └── a:1 % 10 [as=b:2, type=int, outer=(1), immutable]

# Distinct on top of the project.
norm
SELECT DISTINCT b FROM abc
----
distinct-on
 ├── columns: b:2(int!null)
 ├── grouping columns: b:2(int!null)
 ├── immutable
 ├── stats: [rows=10, distinct(2)=10, null(2)=0]
 │   virtcolstats: (2)
 ├── key: (2)
 └── project
      ├── columns: b:2(int!null)
      ├── immutable
      ├── stats: [rows=20, distinct(2)=10, null(2)=0]
      │   virtcolstats: (2)
      ├── scan abc
      │    ├── columns: a:1(int!null)
      │    ├── computed column expressions
      │    │    └── b:2
      │    │         └── a:1 % 10 [type=int]
      │    ├── stats: [rows=20, distinct(2)=10, null(2)=0]
      │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2
      │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9
      │    │   virtcolstats: (2)
      │    └── key: (1)
      └── projections
           └── a:1 % 10 [as=b:2, type=int, outer=(1), immutable]

# Distinct on top of the project using just the virtual column expression.
norm
SELECT DISTINCT a % 10 FROM abc
----
distinct-on
 ├── columns: "?column?":6(int!null)
 ├── grouping columns: "?column?":6(int!null)
 ├── immutable
 ├── stats: [rows=10, distinct(6)=10, null(6)=0]
 │   virtcolstats: (2)
 ├── key: (6)
 └── project
      ├── columns: "?column?":6(int!null)
      ├── immutable
      ├── stats: [rows=20, distinct(6)=10, null(6)=0]
      │   virtcolstats: (2)
      ├── scan abc
      │    ├── columns: a:1(int!null)
      │    ├── computed column expressions
      │    │    └── b:2
      │    │         └── a:1 % 10 [type=int]
      │    ├── stats: [rows=20, distinct(2)=10, null(2)=0]
      │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2
      │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9
      │    │   virtcolstats: (2)
      │    └── key: (1)
      └── projections
           └── a:1 % 10 [as="?column?":6, type=int, outer=(1), immutable]

# Distinct on top of the project using just the virtual column expression.
norm
SELECT DISTINCT a % 10 FROM abc WHERE a % 10 > 3
----
distinct-on
 ├── columns: "?column?":6(int!null)
 ├── grouping columns: "?column?":6(int!null)
 ├── immutable
 ├── stats: [rows=6, distinct(6)=6, null(6)=0]
 │   virtcolstats: (2)
 ├── key: (6)
 └── project
      ├── columns: "?column?":6(int!null)
      ├── immutable
      ├── stats: [rows=12, distinct(6)=6, null(6)=0]
      │   virtcolstats: (2)
      ├── select
      │    ├── columns: a:1(int!null)
      │    ├── immutable
      │    ├── stats: [rows=12, distinct(2)=6, null(2)=0]
      │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2
      │    │                <--- 4 --- 5 --- 6 --- 7 --- 8 --- 9
      │    │   virtcolstats: (2)
      │    ├── key: (1)
      │    ├── scan abc
      │    │    ├── columns: a:1(int!null)
      │    │    ├── computed column expressions
      │    │    │    └── b:2
      │    │    │         └── a:1 % 10 [type=int]
      │    │    ├── stats: [rows=20, distinct(1)=20, null(1)=0, distinct(2)=10, null(2)=0]
      │    │    │   histogram(1)=  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1
      │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 --- 11 --- 12 --- 13 --- 14 --- 15 --- 16 --- 17 --- 18 --- 19
      │    │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2
      │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9
      │    │    │   virtcolstats: (2)
      │    │    └── key: (1)
      │    └── filters
      │         └── (a:1 % 10) > 3 [type=bool, outer=(1), immutable]
      └── projections
           └── a:1 % 10 [as="?column?":6, type=int, outer=(1), immutable]

# Push a join below the project.
opt colstat=1 colstat=2 colstat=6 colstat=7
SELECT x.b FROM abc x JOIN abc y ON x.a = y.a + 1 WHERE y.a < 5
----
project
 ├── columns: b:2(int!null)
 ├── immutable
 ├── stats: [rows=5, distinct(1)=5, null(1)=0, distinct(2)=4.01263, null(2)=0, distinct(6)=1, null(6)=5, distinct(7)=3.02042, null(7)=0]
 │   virtcolstats: (2,7)
 └── project
      ├── columns: b:2(int!null) a:1(int!null) column11:11(int!null)
      ├── immutable
      ├── stats: [rows=5, distinct(1)=5, null(1)=0, distinct(2)=4.01263, null(2)=0, distinct(7)=3.02042, null(7)=0, distinct(11)=5, null(11)=0]
      │   virtcolstats: (2,7)
      ├── fd: (1)-->(2), (1)==(11), (11)==(1)
      ├── inner-join (lookup abc)
      │    ├── columns: a:1(int!null) column11:11(int!null)
      │    ├── key columns: [11] = [1]
      │    ├── lookup columns are key
      │    ├── immutable
      │    ├── stats: [rows=5, distinct(1)=5, null(1)=0, distinct(11)=5, null(11)=0]
      │    │   virtcolstats: (2,7)
      │    ├── fd: (1)==(11), (11)==(1)
      │    ├── project
      │    │    ├── columns: column11:11(int!null)
      │    │    ├── immutable
      │    │    ├── stats: [rows=5, distinct(7)=4.375, null(7)=0, distinct(11)=5, null(11)=0]
      │    │    │   virtcolstats: (7)
      │    │    ├── scan abc
      │    │    │    ├── columns: a:6(int!null)
      │    │    │    ├── constraint: /6: [ - /4]
      │    │    │    ├── stats: [rows=5, distinct(6)=5, null(6)=0, distinct(7)=4.375, null(7)=0]
      │    │    │    │   histogram(6)=  0  1  0  1  0  1  0  1  0  1
      │    │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4
      │    │    │    │   virtcolstats: (7)
      │    │    │    └── key: (6)
      │    │    └── projections
      │    │         └── a:6 + 1 [as=column11:11, type=int, outer=(6), immutable]
      │    └── filters (true)
      └── projections
           └── a:1 % 10 [as=b:2, type=int, outer=(1), immutable]

# Push an index join below the project.
opt colstat=1 colstat=2 colstat=3
SELECT * FROM abc@abc_b_idx
----
project
 ├── columns: a:1(int!null) b:2(int!null) c:3(int)
 ├── immutable
 ├── stats: [rows=20, distinct(1)=20, null(1)=0, distinct(2)=10, null(2)=0, distinct(3)=1, null(3)=20]
 │   virtcolstats: (2)
 ├── key: (1)
 ├── fd: (1)-->(2,3)
 ├── index-join abc
 │    ├── columns: a:1(int!null) c:3(int)
 │    ├── stats: [rows=20, distinct(1)=20, null(1)=0, distinct(2)=10, null(2)=0, distinct(3)=1, null(3)=20]
 │    │   histogram(1)=  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1
 │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 --- 11 --- 12 --- 13 --- 14 --- 15 --- 16 --- 17 --- 18 --- 19
 │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2
 │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │    │   virtcolstats: (2)
 │    ├── key: (1)
 │    ├── fd: (1)-->(3)
 │    └── scan abc@abc_b_idx
 │         ├── columns: a:1(int!null)
 │         ├── flags: force-index=abc_b_idx
 │         ├── stats: [rows=20]
 │         │   virtcolstats: (2)
 │         └── key: (1)
 └── projections
      └── a:1 % 10 [as=b:2, type=int, outer=(1), immutable]

# Push a limit below the project.
norm colstat=1 colstat=2
SELECT a, b FROM abc ORDER BY a LIMIT 5
----
project
 ├── columns: a:1(int!null) b:2(int!null)
 ├── cardinality: [0 - 5]
 ├── immutable
 ├── stats: [rows=5, distinct(1)=5, null(1)=0, distinct(2)=4.375, null(2)=0]
 │   virtcolstats: (2)
 ├── key: (1)
 ├── fd: (1)-->(2)
 ├── ordering: +1
 ├── limit
 │    ├── columns: a:1(int!null)
 │    ├── internal-ordering: +1
 │    ├── cardinality: [0 - 5]
 │    ├── stats: [rows=5, distinct(1)=5, null(1)=0, distinct(2)=4.375, null(2)=0]
 │    │   virtcolstats: (2)
 │    ├── key: (1)
 │    ├── ordering: +1
 │    ├── scan abc
 │    │    ├── columns: a:1(int!null)
 │    │    ├── computed column expressions
 │    │    │    └── b:2
 │    │    │         └── a:1 % 10 [type=int]
 │    │    ├── stats: [rows=20, distinct(1)=20, null(1)=0, distinct(2)=10, null(2)=0]
 │    │    │   histogram(1)=  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1
 │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 --- 11 --- 12 --- 13 --- 14 --- 15 --- 16 --- 17 --- 18 --- 19
 │    │    │   histogram(2)=  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2  0  2
 │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9
 │    │    │   virtcolstats: (2)
 │    │    ├── key: (1)
 │    │    ├── ordering: +1
 │    │    └── limit hint: 5.00
 │    └── 5 [type=int]
 └── projections
      └── a:1 % 10 [as=b:2, type=int, outer=(1), immutable]

# Set operations currently act as barriers for virtual column stats. Make sure
# we don't panic, though stats will be less accurate. (Only columns 11 and 12
# matter for these stats, the others are to check that we don't panic.)
norm colstat=1 colstat=2 colstat=6 colstat=7 colstat=11 colstat=12
SELECT a % 10 FROM (SELECT a FROM abc UNION ALL SELECT a FROM abc)
----
project
 ├── columns: "?column?":12(int!null)
 ├── immutable
 ├── stats: [rows=40, distinct(1)=1, null(1)=40, distinct(2)=1, null(2)=40, distinct(6)=1, null(6)=40, distinct(7)=1, null(7)=40, distinct(11)=40, null(11)=0, distinct(12)=40, null(12)=0]
 ├── union-all
 │    ├── columns: a:11(int!null)
 │    ├── left columns: abc.a:1(int)
 │    ├── right columns: abc.a:6(int)
 │    ├── stats: [rows=40, distinct(11)=40, null(11)=0]
 │    ├── scan abc
 │    │    ├── columns: abc.a:1(int!null)
 │    │    ├── computed column expressions
 │    │    │    └── b:2
 │    │    │         └── abc.a:1 % 10 [type=int]
 │    │    ├── stats: [rows=20, distinct(1)=20, null(1)=0]
 │    │    │   histogram(1)=  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1
 │    │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 --- 11 --- 12 --- 13 --- 14 --- 15 --- 16 --- 17 --- 18 --- 19
 │    │    │   virtcolstats: (2)
 │    │    └── key: (1)
 │    └── scan abc
 │         ├── columns: abc.a:6(int!null)
 │         ├── computed column expressions
 │         │    └── b:7
 │         │         └── abc.a:6 % 10 [type=int]
 │         ├── stats: [rows=20, distinct(6)=20, null(6)=0]
 │         │   histogram(6)=  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1
 │         │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 --- 11 --- 12 --- 13 --- 14 --- 15 --- 16 --- 17 --- 18 --- 19
 │         │   virtcolstats: (7)
 │         └── key: (6)
 └── projections
      └── a:11 % 10 [as="?column?":12, type=int, outer=(11), immutable]

# With-scans currently act as barriers for virtual column stats. Make sure we
# don't panic, though stats will be less accurate. (Only columns 6 and 7 matter
# for these stats, the others are to check that we don't panic.)
norm colstat=1 colstat=2 colstat=6 colstat=7
WITH w AS MATERIALIZED (SELECT a FROM abc) SELECT a % 10 FROM w
----
with &1 (w)
 ├── columns: "?column?":7(int!null)
 ├── materialized
 ├── immutable
 ├── stats: [rows=20]
 ├── scan abc
 │    ├── columns: abc.a:1(int!null)
 │    ├── computed column expressions
 │    │    └── b:2
 │    │         └── abc.a:1 % 10 [type=int]
 │    ├── stats: [rows=20, distinct(1)=20, null(1)=0]
 │    │   histogram(1)=  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1   0  1
 │    │                <--- 0 --- 1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 --- 11 --- 12 --- 13 --- 14 --- 15 --- 16 --- 17 --- 18 --- 19
 │    │   virtcolstats: (2)
 │    └── key: (1)
 └── project
      ├── columns: "?column?":7(int!null)
      ├── immutable
      ├── stats: [rows=20, distinct(1)=1, null(1)=20, distinct(2)=1, null(2)=20, distinct(6)=20, null(6)=0, distinct(7)=20, null(7)=0]
      ├── with-scan &1 (w)
      │    ├── columns: a:6(int!null)
      │    ├── mapping:
      │    │    └──  abc.a:1(int) => a:6(int)
      │    ├── stats: [rows=20, distinct(6)=20, null(6)=0]
      │    └── key: (6)
      └── projections
           └── a:6 % 10 [as="?column?":7, type=int, outer=(6), immutable]
