exec-ddl
CREATE TABLE a (k INT PRIMARY KEY, i INT, f FLOAT, s STRING, j JSON, arr int[])
----

exec-ddl
CREATE TABLE t (
  x INT PRIMARY KEY
)
----

exec-ddl
CREATE TABLE assn_cast (
    c CHAR,
    qc "char",
    i INT,
    s STRING
)
----

# --------------------------------------------------
# FoldNullCast
# --------------------------------------------------
norm expect=FoldNullCast
SELECT
    null::int,
    null::timestamptz,
    null::decimal(19,2)::int::bit::char(2),
    null::oidvector,
    null::int2vector
----
values
 ├── columns: int8:1 timestamptz:2 bpchar:3 oid:4 int2:5
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1-5)
 └── (NULL, NULL, NULL, NULL, NULL)

# --------------------------------------------------
# FoldNullUnary
# --------------------------------------------------
norm expect=FoldNullUnary
SELECT +null::int AS r, -null::int AS s, ~null::int AS t FROM a
----
project
 ├── columns: r:9 s:10 t:11
 ├── fd: ()-->(9-11)
 ├── scan a
 └── projections
      ├── CAST(NULL AS INT8) [as=r:9]
      ├── CAST(NULL AS INT8) [as=s:10]
      └── CAST(NULL AS INT8) [as=t:11]

# --------------------------------------------------
# FoldNullBinaryLeft, FoldNullBinaryRight
# --------------------------------------------------
norm expect=(FoldNullBinaryLeft,FoldNullBinaryRight)
SELECT
    null::int & 1 AS ra, 1 & null::int AS rb,
    null::decimal + 1 AS sa, 1 + null::decimal AS sb,
    null::float % 1 AS ta, 1 % null::float AS tb,
    null::int << 4 AS ua, 4 << null::int AS ub,

    -- These shouldn't be folded because AllowNullArgs is true for concat with arrays.
    arr::decimal[] || null AS va, null || arr::string[] AS vb,

    -- Concatenation with null arrays should not be folded.
    i::decimal || null::decimal[] AS wa, null::float[] || i::float AS wb,
    -- Scalars concatenated with nulls match string concatenation, and are folded.
    i::decimal || null AS xa,
    null || i::float AS xb
FROM a
----
project
 ├── columns: ra:9 rb:10 sa:11 sb:12 ta:13 tb:14 ua:15 ub:16 va:17 vb:18 wa:19 wb:20 xa:21 xb:21
 ├── immutable
 ├── fd: ()-->(9-16,21)
 ├── scan a
 │    └── columns: i:2 arr:6
 └── projections
      ├── CAST(NULL AS INT8) [as=ra:9]
      ├── CAST(NULL AS INT8) [as=rb:10]
      ├── CAST(NULL AS DECIMAL) [as=sa:11]
      ├── CAST(NULL AS DECIMAL) [as=sb:12]
      ├── CAST(NULL AS FLOAT8) [as=ta:13]
      ├── CAST(NULL AS FLOAT8) [as=tb:14]
      ├── CAST(NULL AS INT8) [as=ua:15]
      ├── CAST(NULL AS INT8) [as=ub:16]
      ├── arr:6::DECIMAL[] || CAST(NULL AS DECIMAL[]) [as=va:17, outer=(6), immutable]
      ├── CAST(NULL AS STRING[]) || arr:6::STRING[] [as=vb:18, outer=(6), immutable]
      ├── i:2::DECIMAL || CAST(NULL AS DECIMAL[]) [as=wa:19, outer=(2), immutable]
      ├── CAST(NULL AS FLOAT8[]) || i:2::FLOAT8 [as=wb:20, outer=(2), immutable]
      └── NULL [as=xa:21]

norm
SELECT
    null::json || '[1, 2]' AS ra, '[1, 2]' || null::json AS rb,
    null::json->'foo' AS sa, '{}'::jsonb->null::string AS sb,
    null::json->>'foo' AS ta, '{}'::jsonb->>null::string AS tb,
    null::json->>'foo' AS ua, '{}'::jsonb->>null::string AS ub,
    null::json#>ARRAY['foo'] AS va, '{}'::jsonb#>NULL AS vb,
    null::json#>>ARRAY['foo'] AS wa, '{}'::jsonb#>>NULL AS wb
FROM a
----
project
 ├── columns: ra:9 rb:10 sa:11 sb:12 ta:13 tb:14 ua:13 ub:14 va:15 vb:16 wa:17 wb:16
 ├── fd: ()-->(9-17)
 ├── scan a
 └── projections
      ├── CAST(NULL AS JSONB) [as=ra:9]
      ├── CAST(NULL AS JSONB) [as=rb:10]
      ├── CAST(NULL AS JSONB) [as=sa:11]
      ├── CAST(NULL AS JSONB) [as=sb:12]
      ├── CAST(NULL AS STRING) [as=ta:13]
      ├── CAST(NULL AS STRING) [as=tb:14]
      ├── CAST(NULL AS JSONB) [as=va:15]
      ├── NULL [as=vb:16]
      └── CAST(NULL AS STRING) [as=wa:17]

# --------------------------------------------------
# FoldNullInNonEmpty
# --------------------------------------------------
norm expect=FoldNullInNonEmpty
SELECT null IN (i) AS r, null NOT IN (s) AS s FROM a
----
project
 ├── columns: r:9 s:10
 ├── fd: ()-->(9,10)
 ├── scan a
 └── projections
      ├── CAST(NULL AS BOOL) [as=r:9]
      └── CAST(NULL AS BOOL) [as=s:10]

# --------------------------------------------------
# FoldInNull
# --------------------------------------------------
norm expect=FoldInNull
SELECT i IN (null, null) AS r, k NOT IN (1 * null, null::int, 1 < null) AS s FROM a
----
project
 ├── columns: r:9 s:10
 ├── fd: ()-->(9,10)
 ├── scan a
 └── projections
      ├── CAST(NULL AS BOOL) [as=r:9]
      └── CAST(NULL AS BOOL) [as=s:10]

# --------------------------------------------------
# FoldInEmpty
# --------------------------------------------------
norm expect=FoldInEmpty
SELECT 1 IN ()
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (false,)

norm expect=FoldInEmpty
SELECT NULL IN ()
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (false,)

norm expect=FoldInEmpty
SELECT i FROM a WHERE i = ANY ARRAY[]
----
values
 ├── columns: i:2!null
 ├── cardinality: [0 - 0]
 ├── key: ()
 └── fd: ()-->(2)

# --------------------------------------------------
# FoldNotInEmpty
# --------------------------------------------------
norm expect=FoldNotInEmpty
SELECT 1 NOT IN ()
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (true,)

norm expect=FoldNotInEmpty
SELECT NULL NOT IN ()
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (true,)

# --------------------------------------------------
# FoldArray
# --------------------------------------------------
norm expect=FoldArray
SELECT ARRAY[1, 2, 3] FROM t
----
project
 ├── columns: array:4!null
 ├── fd: ()-->(4)
 ├── scan t
 └── projections
      └── ARRAY[1,2,3] [as=array:4]

# Do not fold if there is a non-constant element.
norm expect-not=FoldArray
SELECT ARRAY[1, 2, 3, x] FROM t
----
project
 ├── columns: array:4!null
 ├── scan t
 │    ├── columns: x:1!null
 │    └── key: (1)
 └── projections
      └── ARRAY[1, 2, 3, x:1] [as=array:4, outer=(1)]

norm expect=FoldArray
SELECT ARRAY['foo', 'bar']
----
values
 ├── columns: array:1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (ARRAY['foo','bar'],)

# --------------------------------------------------
# FoldBinary
# --------------------------------------------------
# Fold constant.
norm expect=FoldBinary
SELECT 1::INT + 2::DECIMAL
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (3,)

# Don't fold: out of range error.
norm expect-not=FoldBinary
SELECT 9223372036854775800::INT + 9223372036854775800::INT
----
values
 ├── columns: "?column?":1
 ├── cardinality: [1 - 1]
 ├── immutable
 ├── key: ()
 ├── fd: ()-->(1)
 └── (9223372036854775800 + 9223372036854775800,)

# Fold constant.
norm expect=FoldBinary
SELECT 1::INT - 2::INT
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (-1,)

# Don't fold: out of range error.
norm expect-not=FoldBinary
SELECT (-9223372036854775800)::INT - 9223372036854775800::INT
----
values
 ├── columns: "?column?":1
 ├── cardinality: [1 - 1]
 ├── immutable
 ├── key: ()
 ├── fd: ()-->(1)
 └── (-9223372036854775800 - 9223372036854775800,)

# Fold constant.
norm expect=FoldBinary
SELECT 4::INT * 2::INT
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (8,)

# Don't fold: out of range error.
norm expect-not=FoldBinary
SELECT 9223372036854775800::INT * 9223372036854775800::INT
----
values
 ├── columns: "?column?":1
 ├── cardinality: [1 - 1]
 ├── immutable
 ├── key: ()
 ├── fd: ()-->(1)
 └── (9223372036854775800 * 9223372036854775800,)

# Fold constant.
norm expect=FoldBinary
SELECT 1::FLOAT / 2::FLOAT
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (0.5,)

# Don't fold: divide by zero error.
norm expect-not=FoldBinary
SELECT 1::INT / 0::INT
----
values
 ├── columns: "?column?":1
 ├── cardinality: [1 - 1]
 ├── immutable
 ├── key: ()
 ├── fd: ()-->(1)
 └── (1 / 0,)

# Fold constant.
norm expect=FoldBinary
SELECT B'01' # B'11'
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (B'10',)

# Don't fold: cannot mix bit array sizes error.
norm expect-not=FoldBinary
SELECT B'01' # B'11001001010101'
----
values
 ├── columns: "?column?":1
 ├── cardinality: [1 - 1]
 ├── immutable
 ├── key: ()
 ├── fd: ()-->(1)
 └── (B'01' # B'11001001010101',)

# Fold constant.
norm expect=FoldBinary
SELECT B'01' | B'11'
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (B'11',)

# Don't fold: cannot mix bit array sizes error.
norm expect-not=FoldBinary
SELECT B'01' | B'11001001010101'
----
values
 ├── columns: "?column?":1
 ├── cardinality: [1 - 1]
 ├── immutable
 ├── key: ()
 ├── fd: ()-->(1)
 └── (B'01' | B'11001001010101',)

# Fold constant.
norm expect=FoldBinary
SELECT '2000-05-05 10:00:00+03':::TIMESTAMP - '2000-05-06 10:00:00+03':::TIMESTAMP
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── ('-1 days',)

# Fold stable operation.
norm expect=FoldBinary
SELECT '2000-05-05 10:00:00+03':::TIMESTAMP - '2000-05-06 10:00:00+03':::TIMESTAMPTZ
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── ('-21:00:00',)

# Fold constant.
norm expect=FoldBinary
SELECT ARRAY['a','b','c'] || 'd'::string
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (ARRAY['a','b','c','d'],)

# Fold constant.
norm expect=FoldBinary
SELECT ARRAY['a','b','c'] || ARRAY['d','e','f']
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (ARRAY['a','b','c','d','e','f'],)

# NULL should not be added to the array.
norm expect=FoldBinary
SELECT ARRAY[1,2,3] || NULL
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (ARRAY[1,2,3],)

# Regression test for #34270.
norm expect=FoldBinary
VALUES ((e'{}' ->> 0) || (e'{}' ->> 0))
----
values
 ├── columns: column1:1
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (NULL,)

# Regression test for issue #87605.
norm expect=FoldBinary
SELECT 1.333 // 1.0
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (1,)

norm expect=FoldBinary
SELECT 2::INT // 1.0
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (2,)

# --------------------------------------------------
# FoldUnary
# --------------------------------------------------
norm expect=FoldUnary
SELECT -(1:::int)
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (-1,)

norm expect=FoldUnary
SELECT -(1:::float)
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (-1.0,)

# TODO(justin): it would be better if this produced an error in the optimizer
# rather than falling back to execution to error.
norm expect-not=FoldUnary format=show-all
SELECT -((-9223372036854775808)::int)
----
values
 ├── columns: "?column?":1(int)
 ├── cardinality: [1 - 1]
 ├── immutable
 ├── stats: [rows=1]
 ├── cost: 0.02
 ├── key: ()
 ├── fd: ()-->(1)
 ├── prune: (1)
 └── tuple [type=tuple{int}]
      └── unary-minus [type=int]
           └── const: -9223372036854775808 [type=int]

norm expect=FoldUnary format=show-all
SELECT -(1:::decimal)
----
values
 ├── columns: "?column?":1(decimal!null)
 ├── cardinality: [1 - 1]
 ├── stats: [rows=1]
 ├── cost: 0.02
 ├── key: ()
 ├── fd: ()-->(1)
 ├── prune: (1)
 └── tuple [type=tuple{decimal}]
      └── const: -1 [type=decimal]

norm expect=FoldUnary format=show-all
SELECT -('-1d'::interval);
----
values
 ├── columns: "?column?":1(interval!null)
 ├── cardinality: [1 - 1]
 ├── stats: [rows=1]
 ├── cost: 0.02
 ├── key: ()
 ├── fd: ()-->(1)
 ├── prune: (1)
 └── tuple [type=tuple{interval}]
      └── const: '1 day' [type=interval]

# TODO(justin): this seems incorrect but it's consistent with the existing
# planner. Revisit this: #26932.
norm expect=FoldUnary
SELECT -('-9223372036854775808d'::interval);
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── ('-9223372036854775808 days',)

# Fold constant.
norm expect=FoldUnary
SELECT ~(500::INT)
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (-501,)

# Fold constant.
norm expect=FoldUnary
SELECT ~('35.231.178.195'::INET)
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── ('220.24.77.60',)

# --------------------------------------------------
# FoldComparison
# --------------------------------------------------
norm expect=FoldComparison
SELECT 1::INT < 2::INT
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (true,)

norm expect=FoldComparison
SELECT 1::INT > 2::INT
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (false,)

norm expect=FoldComparison
SELECT 10.0::FLOAT <= 20.0::FLOAT
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (true,)

norm expect=FoldComparison
SELECT 10.0::FLOAT >= 20.0::FLOAT
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (false,)

norm expect=FoldComparison
SELECT 2.0::DECIMAL = 2::INT
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (true,)

norm expect=FoldComparison
SELECT 2.0::DECIMAL != 2::INT
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (false,)

norm expect=FoldComparison
SELECT 100 IS NOT DISTINCT FROM 200
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (false,)

norm expect=FoldComparison
SELECT 100 IS DISTINCT FROM 200
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (true,)

norm expect=FoldComparison
SELECT 'foo' IN ('a', 'b', 'c')
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (false,)

norm expect=FoldComparison
SELECT 'foo' NOT IN ('a', 'b', 'c')
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (true,)

norm expect=FoldComparison
SELECT 'foo' LIKE 'foobar'
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (false,)

norm expect=FoldComparison
SELECT 'foo' NOT LIKE 'foobar'
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (true,)

norm expect=FoldComparison
SELECT 'foo' ILIKE 'FOO%'
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (true,)

norm expect=FoldComparison
SELECT 'foo' NOT ILIKE 'FOO%'
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (false,)

norm expect=FoldComparison
SELECT 'monday' SIMILAR TO '_onday'
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (true,)

norm expect=FoldComparison
SELECT 'monday' NOT SIMILAR TO '_onday'
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (false,)

norm expect=FoldComparison
SELECT 'tuEsday' ~ 't[uU][eE]sday'
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (true,)

norm expect=FoldComparison
SELECT 'tuEsday' !~ 't[uU][eE]sday'
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (false,)

norm expect=FoldComparison
SELECT 'wednesday' ~* 'W.*y'
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (true,)

norm expect=FoldComparison
SELECT 'wednesday' !~* 'W.*y'
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (false,)

norm expect=FoldComparison
SELECT '[1, 2]'::JSONB <@ '[1, 2, 3]'::JSONB
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (true,)

norm expect=FoldComparison
SELECT ('a', 'b', 'c') = ('d', 'e', 'f')
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (false,)

# --------------------------------------------------
# FoldCast
# --------------------------------------------------
norm expect=FoldCast
SELECT 1.1::int/1
----
values
 ├── columns: "?column?":1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (1.0000000000000000000,)

norm no-stable-folds expect-not=FoldCast
SELECT '2020-01-01'::TIMESTAMPTZ
----
values
 ├── columns: timestamptz:1
 ├── cardinality: [1 - 1]
 ├── stable
 ├── key: ()
 ├── fd: ()-->(1)
 └── ('2020-01-01'::TIMESTAMPTZ,)

norm no-stable-folds expect-not=FoldCast
SELECT 'today'::TIMESTAMPTZ
----
values
 ├── columns: timestamptz:1
 ├── cardinality: [1 - 1]
 ├── stable
 ├── key: ()
 ├── fd: ()-->(1)
 └── ('today'::TIMESTAMPTZ,)

norm expect=FoldCast
SELECT '2020-01-01'::TIMESTAMPTZ
----
values
 ├── columns: timestamptz:1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── ('2020-01-01 00:00:00+00',)

norm expect=FoldCast
SELECT 'today'::TIMESTAMPTZ
----
values
 ├── columns: timestamptz:1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── ('2017-05-10 00:00:00+00',)

# Regression test for #96447. FoldCast should not panic when attempting to fold
# a typed NULL when FoldNullCast is disabled.
norm disable=FoldNullCast
SELECT NULL::INT::OID,
  NULL::INT::REGTYPE,
  NULL::INT::REGPROC,
  NULL::INT::REGPROCEDURE,
  NULL::INT::REGNAMESPACE,
  NULL::STRING::REGCLASS
----
values
 ├── columns: oid:1 regtype:2 regproc:3 regprocedure:4 regnamespace:5 regclass:6
 ├── cardinality: [1 - 1]
 ├── stable
 ├── key: ()
 ├── fd: ()-->(1-6)
 └── (CAST(NULL AS INT8)::OID, CAST(NULL AS INT8)::REGTYPE, CAST(NULL AS INT8)::REGPROC, CAST(NULL AS INT8)::REGPROCEDURE, CAST(NULL AS INT8)::REGNAMESPACE, CAST(NULL AS STRING)::REGCLASS)

# --------------------------------------------------
# FoldAssignmentCast
# --------------------------------------------------

norm expect=FoldAssignmentCast
INSERT INTO assn_cast (c, qc, i, s) VALUES (' ', 'foo', '1', 2)
----
insert assn_cast
 ├── columns: <none>
 ├── insert-mapping:
 │    ├── c_cast:12 => c:1
 │    ├── qc_cast:13 => qc:2
 │    ├── column3:10 => i:3
 │    ├── s_cast:14 => s:4
 │    └── rowid_default:15 => rowid:5
 ├── cardinality: [0 - 0]
 ├── volatile, mutations
 └── values
      ├── columns: column3:10!null c_cast:12!null qc_cast:13!null s_cast:14!null rowid_default:15
      ├── cardinality: [1 - 1]
      ├── volatile
      ├── key: ()
      ├── fd: ()-->(10,12-15)
      └── (1, '', 'f', '2', unique_rowid())

# Do not fold an assignment cast that results in an error.
norm expect-not=FoldAssignmentCast
INSERT INTO assn_cast (c) VALUES ('foo')
----
insert assn_cast
 ├── columns: <none>
 ├── insert-mapping:
 │    ├── c_cast:9 => c:1
 │    ├── qc_default:10 => qc:2
 │    ├── i_default:11 => i:3
 │    ├── s_default:12 => s:4
 │    └── rowid_default:13 => rowid:5
 ├── cardinality: [0 - 0]
 ├── volatile, mutations
 └── values
      ├── columns: c_cast:9 qc_default:10 i_default:11 s_default:12 rowid_default:13
      ├── cardinality: [1 - 1]
      ├── volatile
      ├── key: ()
      ├── fd: ()-->(9-13)
      └── tuple
           ├── assignment-cast: CHAR
           │    └── 'foo'
           ├── CAST(NULL AS "char")
           ├── CAST(NULL AS INT8)
           ├── CAST(NULL AS STRING)
           └── unique_rowid()

# Cannot fold non-constants.
norm expect-not=FoldAssignmentCast
INSERT INTO assn_cast(c) VALUES ($1)
----
insert assn_cast
 ├── columns: <none>
 ├── insert-mapping:
 │    ├── c_cast:9 => c:1
 │    ├── qc_default:10 => qc:2
 │    ├── i_default:11 => i:3
 │    ├── s_default:12 => s:4
 │    └── rowid_default:13 => rowid:5
 ├── cardinality: [0 - 0]
 ├── volatile, mutations, has-placeholder
 └── values
      ├── columns: c_cast:9 qc_default:10 i_default:11 s_default:12 rowid_default:13
      ├── cardinality: [1 - 1]
      ├── volatile, has-placeholder
      ├── key: ()
      ├── fd: ()-->(9-13)
      └── tuple
           ├── assignment-cast: CHAR
           │    └── $1
           ├── CAST(NULL AS "char")
           ├── CAST(NULL AS INT8)
           ├── CAST(NULL AS STRING)
           └── unique_rowid()

# Fold a stable assignment cast by default.
norm expect=FoldAssignmentCast
INSERT INTO assn_cast(s) VALUES (1.2::FLOAT)
----
insert assn_cast
 ├── columns: <none>
 ├── insert-mapping:
 │    ├── c_default:10 => c:1
 │    ├── qc_default:11 => qc:2
 │    ├── i_default:12 => i:3
 │    ├── s_cast:9 => s:4
 │    └── rowid_default:13 => rowid:5
 ├── cardinality: [0 - 0]
 ├── volatile, mutations
 └── values
      ├── columns: s_cast:9!null c_default:10 qc_default:11 i_default:12 rowid_default:13
      ├── cardinality: [1 - 1]
      ├── volatile
      ├── key: ()
      ├── fd: ()-->(9-13)
      └── ('1.2', CAST(NULL AS CHAR), CAST(NULL AS "char"), CAST(NULL AS INT8), unique_rowid())

# Do not fold a stable assignment cast if stable folds are disabled.
norm no-stable-folds expect-not=FoldAssignmentCast
INSERT INTO assn_cast(s) VALUES (1.2::FLOAT)
----
insert assn_cast
 ├── columns: <none>
 ├── insert-mapping:
 │    ├── c_default:10 => c:1
 │    ├── qc_default:11 => qc:2
 │    ├── i_default:12 => i:3
 │    ├── s_cast:9 => s:4
 │    └── rowid_default:13 => rowid:5
 ├── cardinality: [0 - 0]
 ├── stable+volatile, mutations
 └── values
      ├── columns: s_cast:9 c_default:10 qc_default:11 i_default:12 rowid_default:13
      ├── cardinality: [1 - 1]
      ├── stable+volatile
      ├── key: ()
      ├── fd: ()-->(9-13)
      └── tuple
           ├── assignment-cast: STRING
           │    └── 1.2
           ├── CAST(NULL AS CHAR)
           ├── CAST(NULL AS "char")
           ├── CAST(NULL AS INT8)
           └── unique_rowid()

# --------------------------------------------------
# FoldFunctionWithNullArgs
# --------------------------------------------------

norm expect=FoldFunctionWithNullArg
SELECT lpad(NULL::STRING, i) FROM a
----
project
 ├── columns: lpad:9
 ├── fd: ()-->(9)
 ├── scan a
 └── projections
      └── CAST(NULL AS STRING) [as=lpad:9]

norm expect=FoldFunctionWithNullArg
SELECT lpad(s, NULL::INT) FROM a
----
project
 ├── columns: lpad:9
 ├── fd: ()-->(9)
 ├── scan a
 └── projections
      └── CAST(NULL AS STRING) [as=lpad:9]

# Fold a stable function.
norm expect=FoldFunctionWithNullArg
SELECT date_trunc(s, NULL::TIMESTAMPTZ) FROM a
----
project
 ├── columns: date_trunc:9
 ├── fd: ()-->(9)
 ├── scan a
 └── projections
      └── CAST(NULL AS TIMESTAMPTZ) [as=date_trunc:9]

# Fold a volatile function.
norm expect=FoldFunctionWithNullArg
SELECT nextval(NULL::STRING) FROM a
----
project
 ├── columns: nextval:9
 ├── fd: ()-->(9)
 ├── scan a
 └── projections
      └── CAST(NULL AS INT8) [as=nextval:9]

# Do not fold an aggregate function.
norm expect-not=FoldFunctionWithNullArg
SELECT string_agg(s, NULL::STRING) FROM a
----
scalar-group-by
 ├── columns: string_agg:10
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(10)
 ├── project
 │    ├── columns: column9:9 s:4
 │    ├── fd: ()-->(9)
 │    ├── scan a
 │    │    └── columns: s:4
 │    └── projections
 │         └── CAST(NULL AS STRING) [as=column9:9]
 └── aggregations
      └── string-agg [as=string_agg:10, outer=(4,9)]
           ├── s:4
           └── column9:9

# Do not fold a function that allows null arguments.
norm expect-not=FoldFunctionWithNullArg
SELECT concat(s, NULL::STRING) FROM a
----
project
 ├── columns: concat:9
 ├── immutable
 ├── scan a
 │    └── columns: s:4
 └── projections
      └── concat(s:4, CAST(NULL AS STRING)) [as=concat:9, outer=(4), immutable]

# Do not fold a function without a Null argument.
norm expect-not=FoldFunctionWithNullArg
SELECT lpad(s, 5) FROM a
----
project
 ├── columns: lpad:9
 ├── immutable
 ├── scan a
 │    └── columns: s:4
 └── projections
      └── lpad(s:4, 5) [as=lpad:9, outer=(4), immutable]

# --------------------------------------------------
# FoldFunction
# --------------------------------------------------
norm expect=FoldFunction
SELECT length('abc'), upper('xyz'), lower('DEF')
----
values
 ├── columns: length:1!null upper:2!null lower:3!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1-3)
 └── (3, 'XYZ', 'def')

norm expect=FoldFunction
SELECT encode('abc', 'hex'), decode('616263', 'hex')
----
values
 ├── columns: encode:1!null decode:2!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1,2)
 └── ('616263', '\x616263')

norm expect=FoldFunction locality=(region=east,dc=east1-b)
SELECT crdb_internal.locality_value('dc')
----
values
 ├── columns: crdb_internal.locality_value:1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 ├── distribution: east
 └── ('east1-b',)

norm expect=FoldFunction
SELECT crdb_internal.locality_value('unk')
----
values
 ├── columns: crdb_internal.locality_value:1
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (NULL,)

norm expect=FoldFunction
SELECT ST_Length(ST_GeomFromText('LINESTRING(743238 2967416,743238 2967450)', 4326));
----
values
 ├── columns: st_length:1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── (34.0,)

norm no-stable-folds expect-not=FoldFunction
SELECT now(), current_user(), current_database()
----
values
 ├── columns: now:1 current_user:2 current_database:3
 ├── cardinality: [1 - 1]
 ├── stable
 ├── key: ()
 ├── fd: ()-->(1-3)
 └── (now(), current_user(), current_database())

norm expect=FoldFunction
SELECT now(), current_user(), current_database()
----
values
 ├── columns: now:1!null current_user:2!null current_database:3!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1-3)
 └── ('2017-05-10 13:00:00+00', 'opttester', 'defaultdb')

# --------------------------------------------------
# FoldIndirection
# --------------------------------------------------
# Fold when input is a static array constructor (but elements are not constant).
norm expect=FoldIndirection
SELECT ARRAY[i, i + 1][1] FROM a
----
project
 ├── columns: array:9
 ├── scan a
 │    └── columns: i:2
 └── projections
      └── i:2 [as=array:9, outer=(2)]

norm expect=FoldIndirection
SELECT ARRAY[i, i + 1][2] FROM a
----
project
 ├── columns: array:9
 ├── immutable
 ├── scan a
 │    └── columns: i:2
 └── projections
      └── i:2 + 1 [as=array:9, outer=(2), immutable]

# Fold when input is a DArray constant.
norm expect=FoldIndirection
SELECT ARRAY[4, 5, 6][2] FROM a
----
project
 ├── columns: array:9!null
 ├── fd: ()-->(9)
 ├── scan a
 └── projections
      └── 5 [as=array:9]

# Array bounds are out-of-range.
norm expect=FoldIndirection
SELECT ARRAY[s, 'foo'][0] FROM a
----
project
 ├── columns: array:9
 ├── fd: ()-->(9)
 ├── scan a
 └── projections
      └── CAST(NULL AS STRING) [as=array:9]

norm expect=FoldIndirection
SELECT ARRAY[i, i + 1][3] FROM a
----
project
 ├── columns: array:9
 ├── fd: ()-->(9)
 ├── scan a
 └── projections
      └── CAST(NULL AS INT8) [as=array:9]

norm expect=FoldIndirection
SELECT ARRAY[4, 5, 6][0] FROM a
----
project
 ├── columns: array:9
 ├── fd: ()-->(9)
 ├── scan a
 └── projections
      └── CAST(NULL AS INT8) [as=array:9]

# Array is dynamically constructed.
norm expect-not=FoldIndirection
SELECT arr[0] FROM a
----
project
 ├── columns: arr:9
 ├── scan a
 │    └── columns: a.arr:6
 └── projections
      └── a.arr:6[0] [as=arr:9, outer=(6)]

# Fold JSONB constants.
norm expect=FoldIndirection
SELECT ('{"a": 1}'::jsonb)['a'] AS other_col FROM a
----
project
 ├── columns: other_col:9!null
 ├── fd: ()-->(9)
 ├── scan a
 └── projections
      └── '1' [as=other_col:9]

# JSONB is dynamically constructured.
norm expect-not=FoldIndirection
SELECT j['field'] FROM a
----
project
 ├── columns: j:9
 ├── immutable
 ├── scan a
 │    └── columns: a.j:5
 └── projections
      └── a.j:5->'field' [as=j:9, outer=(5), immutable]

# Regression test for #40404.
norm expect=FoldIndirection
SELECT (SELECT x[1]) FROM (VALUES(null::oid[])) v(x)
----
values
 ├── columns: x:3
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(3)
 └── tuple
      └── subquery
           └── values
                ├── columns: x:2
                ├── cardinality: [1 - 1]
                ├── key: ()
                ├── fd: ()-->(2)
                └── (NULL,)

# --------------------------------------------------
# FoldColumnAccess
# --------------------------------------------------
# Fold when input is a static tuple constructor (but elements are not constant).
# NOTE: Use constant array access to avoid triggering ColumnAccess::TypeCheck
#       constant tuple folding.
norm expect=FoldColumnAccess
SELECT (ARRAY[(('foo', i) AS foo, bar)][1]).foo FROM a
----
project
 ├── columns: foo:9!null
 ├── fd: ()-->(9)
 ├── scan a
 └── projections
      └── 'foo' [as=foo:9]

norm expect=FoldColumnAccess
SELECT (ARRAY[(('foo', i) AS foo, bar)][1]).bar FROM a
----
project
 ├── columns: bar:9
 ├── scan a
 │    └── columns: i:2
 └── projections
      └── i:2 [as=bar:9, outer=(2)]

# Fold when input is a constant DTuple.
norm expect=FoldColumnAccess
SELECT (ARRAY[(('foo', 'bar') AS foo, bar)][1]).foo
----
values
 ├── columns: foo:1!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(1)
 └── ('foo',)

# Fold when input is Null. This is possible when FoldIndirection has already
# folded an Indirection with an out-of-bounds index to Null.
norm expect=FoldColumnAccess format=show-types
SELECT (ARRAY[(('foo', i) AS foo, bar)][0]).foo FROM a
----
project
 ├── columns: foo:9(string)
 ├── fd: ()-->(9)
 ├── scan a
 └── projections
      └── CAST(NULL AS STRING) [as=foo:9, type=string]

norm expect=FoldColumnAccess format=show-types
SELECT (ARRAY[(('foo', i) AS foo, bar)][0]).bar FROM a
----
project
 ├── columns: bar:9(int)
 ├── fd: ()-->(9)
 ├── scan a
 └── projections
      └── CAST(NULL AS INT8) [as=bar:9, type=int]

# --------------------------------------------------
# FoldEqualsAnyNull
# --------------------------------------------------
norm expect=FoldEqualsAnyNull
SELECT * FROM a WHERE i = ANY (NULL::int[])
----
values
 ├── columns: k:1!null i:2!null f:3!null s:4!null j:5!null arr:6!null
 ├── cardinality: [0 - 0]
 ├── key: ()
 └── fd: ()-->(1-6)
