exec-ddl
CREATE FUNCTION fn_volatile() RETURNS INT LANGUAGE SQL AS 'SELECT 1'
----

exec-ddl
CREATE FUNCTION fn_stable() RETURNS INT STABLE LANGUAGE SQL AS 'SELECT 1'
----

exec-ddl
CREATE FUNCTION fn_immutable() RETURNS INT IMMUTABLE LANGUAGE SQL AS 'SELECT 1'
----

exec-ddl
CREATE FUNCTION fn_leakproof() RETURNS INT IMMUTABLE LEAKPROOF LANGUAGE SQL AS 'SELECT 1'
----

exec-ddl
CREATE TABLE ab (
  a INT PRIMARY KEY,
  b INT
)
----

build
SELECT a + fn_volatile() FROM ab
----
project
 ├── columns: "?column?":6(int)
 ├── volatile
 ├── prune: (6)
 ├── scan ab
 │    ├── columns: a:1(int!null) b:2(int) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-4)
 │    ├── prune: (1-4)
 │    └── interesting orderings: (+1)
 └── projections
      └── plus [as="?column?":6, type=int, outer=(1), volatile, udf]
           ├── variable: a:1 [type=int]
           └── udf: fn_volatile [type=int]
                └── body
                     └── limit
                          ├── columns: "?column?":5(int!null)
                          ├── cardinality: [1 - 1]
                          ├── key: ()
                          ├── fd: ()-->(5)
                          ├── project
                          │    ├── columns: "?column?":5(int!null)
                          │    ├── cardinality: [1 - 1]
                          │    ├── key: ()
                          │    ├── fd: ()-->(5)
                          │    ├── values
                          │    │    ├── cardinality: [1 - 1]
                          │    │    ├── key: ()
                          │    │    └── tuple [type=tuple]
                          │    └── projections
                          │         └── const: 1 [as="?column?":5, type=int]
                          └── const: 1 [type=int]

build
SELECT a FROM ab WHERE b = fn_immutable()
----
project
 ├── columns: a:1(int!null)
 ├── immutable
 ├── key: (1)
 ├── prune: (1)
 ├── interesting orderings: (+1)
 └── select
      ├── columns: a:1(int!null) b:2(int!null) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
      ├── immutable
      ├── key: (1)
      ├── fd: ()-->(2), (1)-->(3,4)
      ├── prune: (1,3,4)
      ├── interesting orderings: (+1 opt(2))
      ├── scan ab
      │    ├── columns: a:1(int!null) b:2(int) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
      │    ├── key: (1)
      │    ├── fd: (1)-->(2-4)
      │    ├── prune: (1-4)
      │    └── interesting orderings: (+1)
      └── filters
           └── eq [type=bool, outer=(2), immutable, udf, constraints=(/2: (/NULL - ]), fd=()-->(2)]
                ├── variable: b:2 [type=int]
                └── udf: fn_immutable [type=int]
                     └── body
                          └── limit
                               ├── columns: "?column?":5(int!null)
                               ├── cardinality: [1 - 1]
                               ├── key: ()
                               ├── fd: ()-->(5)
                               ├── project
                               │    ├── columns: "?column?":5(int!null)
                               │    ├── cardinality: [1 - 1]
                               │    ├── key: ()
                               │    ├── fd: ()-->(5)
                               │    ├── values
                               │    │    ├── cardinality: [1 - 1]
                               │    │    ├── key: ()
                               │    │    └── tuple [type=tuple]
                               │    └── projections
                               │         └── const: 1 [as="?column?":5, type=int]
                               └── const: 1 [type=int]

build
SELECT a FROM ab WHERE b = fn_immutable() + fn_stable()
----
project
 ├── columns: a:1(int!null)
 ├── stable
 ├── key: (1)
 ├── prune: (1)
 ├── interesting orderings: (+1)
 └── select
      ├── columns: a:1(int!null) b:2(int!null) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
      ├── stable
      ├── key: (1)
      ├── fd: ()-->(2), (1)-->(3,4)
      ├── prune: (1,3,4)
      ├── interesting orderings: (+1 opt(2))
      ├── scan ab
      │    ├── columns: a:1(int!null) b:2(int) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
      │    ├── key: (1)
      │    ├── fd: (1)-->(2-4)
      │    ├── prune: (1-4)
      │    └── interesting orderings: (+1)
      └── filters
           └── eq [type=bool, outer=(2), stable, udf, constraints=(/2: (/NULL - ]), fd=()-->(2)]
                ├── variable: b:2 [type=int]
                └── plus [type=int]
                     ├── udf: fn_immutable [type=int]
                     │    └── body
                     │         └── limit
                     │              ├── columns: "?column?":5(int!null)
                     │              ├── cardinality: [1 - 1]
                     │              ├── key: ()
                     │              ├── fd: ()-->(5)
                     │              ├── project
                     │              │    ├── columns: "?column?":5(int!null)
                     │              │    ├── cardinality: [1 - 1]
                     │              │    ├── key: ()
                     │              │    ├── fd: ()-->(5)
                     │              │    ├── values
                     │              │    │    ├── cardinality: [1 - 1]
                     │              │    │    ├── key: ()
                     │              │    │    └── tuple [type=tuple]
                     │              │    └── projections
                     │              │         └── const: 1 [as="?column?":5, type=int]
                     │              └── const: 1 [type=int]
                     └── udf: fn_stable [type=int]
                          └── body
                               └── limit
                                    ├── columns: "?column?":6(int!null)
                                    ├── cardinality: [1 - 1]
                                    ├── key: ()
                                    ├── fd: ()-->(6)
                                    ├── project
                                    │    ├── columns: "?column?":6(int!null)
                                    │    ├── cardinality: [1 - 1]
                                    │    ├── key: ()
                                    │    ├── fd: ()-->(6)
                                    │    ├── values
                                    │    │    ├── cardinality: [1 - 1]
                                    │    │    ├── key: ()
                                    │    │    └── tuple [type=tuple]
                                    │    └── projections
                                    │         └── const: 1 [as="?column?":6, type=int]
                                    └── const: 1 [type=int]

build
SELECT a FROM ab WHERE b = fn_leakproof()
----
project
 ├── columns: a:1(int!null)
 ├── key: (1)
 ├── prune: (1)
 ├── interesting orderings: (+1)
 └── select
      ├── columns: a:1(int!null) b:2(int!null) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
      ├── key: (1)
      ├── fd: ()-->(2), (1)-->(3,4)
      ├── prune: (1,3,4)
      ├── interesting orderings: (+1 opt(2))
      ├── scan ab
      │    ├── columns: a:1(int!null) b:2(int) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid)
      │    ├── key: (1)
      │    ├── fd: (1)-->(2-4)
      │    ├── prune: (1-4)
      │    └── interesting orderings: (+1)
      └── filters
           └── eq [type=bool, outer=(2), udf, constraints=(/2: (/NULL - ]), fd=()-->(2)]
                ├── variable: b:2 [type=int]
                └── udf: fn_leakproof [type=int]
                     └── body
                          └── limit
                               ├── columns: "?column?":5(int!null)
                               ├── cardinality: [1 - 1]
                               ├── key: ()
                               ├── fd: ()-->(5)
                               ├── project
                               │    ├── columns: "?column?":5(int!null)
                               │    ├── cardinality: [1 - 1]
                               │    ├── key: ()
                               │    ├── fd: ()-->(5)
                               │    ├── values
                               │    │    ├── cardinality: [1 - 1]
                               │    │    ├── key: ()
                               │    │    └── tuple [type=tuple]
                               │    └── projections
                               │         └── const: 1 [as="?column?":5, type=int]
                               └── const: 1 [type=int]

# The "udf" property should propagate to the top-most filter.
build
SELECT i FROM (VALUES (1), (2)) v(i) WHERE i = (SELECT a FROM ab WHERE b = fn_leakproof())
----
select
 ├── columns: i:1(int!null)
 ├── cardinality: [0 - 2]
 ├── values
 │    ├── columns: column1:1(int!null)
 │    ├── cardinality: [2 - 2]
 │    ├── prune: (1)
 │    ├── tuple [type=tuple{int}]
 │    │    └── const: 1 [type=int]
 │    └── tuple [type=tuple{int}]
 │         └── const: 2 [type=int]
 └── filters
      └── eq [type=bool, outer=(1), subquery, udf, constraints=(/1: (/NULL - ])]
           ├── variable: column1:1 [type=int]
           └── subquery [type=int]
                └── max1-row
                     ├── columns: a:2(int!null)
                     ├── error: "more than one row returned by a subquery used as an expression"
                     ├── cardinality: [0 - 1]
                     ├── key: ()
                     ├── fd: ()-->(2)
                     └── project
                          ├── columns: a:2(int!null)
                          ├── key: (2)
                          ├── prune: (2)
                          ├── interesting orderings: (+2)
                          └── select
                               ├── columns: a:2(int!null) b:3(int!null) crdb_internal_mvcc_timestamp:4(decimal) tableoid:5(oid)
                               ├── key: (2)
                               ├── fd: ()-->(3), (2)-->(4,5)
                               ├── prune: (2,4,5)
                               ├── interesting orderings: (+2 opt(3))
                               ├── scan ab
                               │    ├── columns: a:2(int!null) b:3(int) crdb_internal_mvcc_timestamp:4(decimal) tableoid:5(oid)
                               │    ├── key: (2)
                               │    ├── fd: (2)-->(3-5)
                               │    ├── prune: (2-5)
                               │    └── interesting orderings: (+2)
                               └── filters
                                    └── eq [type=bool, outer=(3), udf, constraints=(/3: (/NULL - ]), fd=()-->(3)]
                                         ├── variable: b:3 [type=int]
                                         └── udf: fn_leakproof [type=int]
                                              └── body
                                                   └── limit
                                                        ├── columns: "?column?":6(int!null)
                                                        ├── cardinality: [1 - 1]
                                                        ├── key: ()
                                                        ├── fd: ()-->(6)
                                                        ├── project
                                                        │    ├── columns: "?column?":6(int!null)
                                                        │    ├── cardinality: [1 - 1]
                                                        │    ├── key: ()
                                                        │    ├── fd: ()-->(6)
                                                        │    ├── values
                                                        │    │    ├── cardinality: [1 - 1]
                                                        │    │    ├── key: ()
                                                        │    │    └── tuple [type=tuple]
                                                        │    └── projections
                                                        │         └── const: 1 [as="?column?":6, type=int]
                                                        └── const: 1 [type=int]
