# See Logical.CanHaveSideEffects comment for the optimizer's side-effect policy.

exec-ddl
CREATE TABLE a (k INT PRIMARY KEY, i INT, f FLOAT, s STRING, j JSON)
----
TABLE a
 ├── k int not null
 ├── i int
 ├── f float
 ├── s string
 ├── j jsonb
 └── INDEX primary
      └── k int not null

exec-ddl
CREATE TABLE xy (x INT PRIMARY KEY, y INT)
----
TABLE xy
 ├── x int not null
 ├── y int
 └── INDEX primary
      └── x int not null

exec-ddl
CREATE TABLE uv (u INT PRIMARY KEY, v INT)
----
TABLE uv
 ├── u int not null
 ├── v int
 └── INDEX primary
      └── u int not null

# Don't allow ORDER BY column to be eliminated if it has a side effect.
opt
SELECT * FROM a ORDER BY length('foo'), random()+1.0
----
sort
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)  [hidden: column7:7(float)]
 ├── side-effects
 ├── key: (1)
 ├── fd: (1)-->(2-5,7)
 ├── ordering: +7
 └── project
      ├── columns: column7:7(float) k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      ├── side-effects
      ├── key: (1)
      ├── fd: (1)-->(2-5,7)
      ├── scan a
      │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      │    ├── key: (1)
      │    └── fd: (1)-->(2-5)
      └── projections
           └── random() + 1.0 [type=float, side-effects]

# Don't allow GROUP BY column to be eliminated if it has a side effect.
opt
SELECT avg(f) FROM a WHERE i=5 GROUP BY i+(random()*10)::int, i+1
----
project
 ├── columns: avg:6(float)
 ├── side-effects
 └── group-by
      ├── columns: avg:6(float) column7:7(int)
      ├── grouping columns: column7:7(int)
      ├── side-effects
      ├── key: (7)
      ├── fd: (7)-->(6)
      ├── project
      │    ├── columns: column7:7(int) f:3(float)
      │    ├── side-effects
      │    ├── select
      │    │    ├── columns: i:2(int!null) f:3(float)
      │    │    ├── fd: ()-->(2)
      │    │    ├── scan a
      │    │    │    └── columns: i:2(int) f:3(float)
      │    │    └── filters
      │    │         └── i = 5 [type=bool, outer=(2), constraints=(/2: [/5 - /5]; tight), fd=()-->(2)]
      │    └── projections
      │         └── i + (random() * 10.0)::INT8 [type=int, outer=(2), side-effects]
      └── aggregations
           └── avg [type=float, outer=(3)]
                └── variable: f [type=float]

# Allow elimination of side effecting expressions during column pruning.
opt
SELECT i FROM (SELECT i, nextval('foo') FROM a)
----
scan a
 └── columns: i:2(int)

# Allow duplication of side effecting expressions during predicate pushdown.
opt
SELECT * FROM a INNER JOIN xy ON k=x WHERE k=random()
----
inner-join (merge)
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) y:7(int)
 ├── left ordering: +1
 ├── right ordering: +6
 ├── side-effects
 ├── key: (6)
 ├── fd: (1)-->(2-5), (6)-->(7), (1)==(6), (6)==(1)
 ├── select
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── side-effects
 │    ├── key: (1)
 │    ├── fd: (1)-->(2-5)
 │    ├── ordering: +1
 │    ├── scan a
 │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    │    ├── key: (1)
 │    │    ├── fd: (1)-->(2-5)
 │    │    └── ordering: +1
 │    └── filters
 │         └── k = random() [type=bool, outer=(1), side-effects, constraints=(/1: (/NULL - ])]
 ├── select
 │    ├── columns: x:6(int!null) y:7(int)
 │    ├── side-effects
 │    ├── key: (6)
 │    ├── fd: (6)-->(7)
 │    ├── ordering: +6
 │    ├── scan xy
 │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    ├── key: (6)
 │    │    ├── fd: (6)-->(7)
 │    │    └── ordering: +6
 │    └── filters
 │         └── x = random() [type=bool, outer=(6), side-effects, constraints=(/6: (/NULL - ])]
 └── filters (true)

# Decorrelate CASE WHEN branch if there are no side effects.
opt
SELECT CASE WHEN i<0 THEN (SELECT y FROM xy WHERE x=i LIMIT 1) ELSE 5 END FROM a
----
project
 ├── columns: case:8(int)
 ├── left-join
 │    ├── columns: i:2(int) x:6(int) y:7(int)
 │    ├── fd: (6)-->(7)
 │    ├── scan a
 │    │    └── columns: i:2(int)
 │    ├── scan xy
 │    │    ├── columns: x:6(int!null) y:7(int)
 │    │    ├── key: (6)
 │    │    └── fd: (6)-->(7)
 │    └── filters
 │         └── x = i [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
 └── projections
      └── CASE WHEN i < 0 THEN y ELSE 5 END [type=int, outer=(2,7)]

# Decorrelate CASE ELSE branch if there are no side effects.
opt
SELECT * FROM a WHERE (CASE WHEN i<0 THEN 5 ELSE (SELECT y FROM xy WHERE x=i LIMIT 1) END)=k
----
project
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── fd: (1)-->(2-5)
 └── select
      ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int) y:7(int)
      ├── key: (1,6)
      ├── fd: (1)-->(2-5), (6)-->(7)
      ├── left-join
      │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int) y:7(int)
      │    ├── key: (1,6)
      │    ├── fd: (1)-->(2-5), (6)-->(7)
      │    ├── scan a
      │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      │    │    ├── key: (1)
      │    │    └── fd: (1)-->(2-5)
      │    ├── scan xy
      │    │    ├── columns: x:6(int!null) y:7(int)
      │    │    ├── key: (6)
      │    │    └── fd: (6)-->(7)
      │    └── filters
      │         └── x = i [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
      └── filters
           └── k = CASE WHEN i < 0 THEN 5 ELSE y END [type=bool, outer=(1,2,7), constraints=(/1: (/NULL - ])]

# Don't decorrelate CASE WHEN branch if there are side effects.
opt
SELECT CASE WHEN i<0 THEN (SELECT y FROM xy WHERE x=i LIMIT (random()*10)::int) ELSE 5 END FROM a
----
project
 ├── columns: case:8(int)
 ├── side-effects
 ├── scan a
 │    └── columns: i:2(int)
 └── projections
      └── case [type=int, outer=(2), side-effects, correlated-subquery]
           ├── true [type=bool]
           ├── when [type=int]
           │    ├── i < 0 [type=bool]
           │    └── subquery [type=int]
           │         └── project
           │              ├── columns: y:7(int)
           │              ├── outer: (2)
           │              ├── cardinality: [0 - 1]
           │              ├── side-effects
           │              ├── key: ()
           │              ├── fd: ()-->(7)
           │              └── limit
           │                   ├── columns: x:6(int!null) y:7(int)
           │                   ├── outer: (2)
           │                   ├── cardinality: [0 - 1]
           │                   ├── side-effects
           │                   ├── key: ()
           │                   ├── fd: ()-->(6,7)
           │                   ├── select
           │                   │    ├── columns: x:6(int!null) y:7(int)
           │                   │    ├── outer: (2)
           │                   │    ├── cardinality: [0 - 1]
           │                   │    ├── key: ()
           │                   │    ├── fd: ()-->(6,7)
           │                   │    ├── scan xy
           │                   │    │    ├── columns: x:6(int!null) y:7(int)
           │                   │    │    ├── key: (6)
           │                   │    │    └── fd: (6)-->(7)
           │                   │    └── filters
           │                   │         └── x = i [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
           │                   └── (random() * 10.0)::INT8 [type=int]
           └── const: 5 [type=int]

# Don't decorrelate CASE ELSE branch if there are side effects.
opt
SELECT * FROM a WHERE (CASE WHEN i<0 THEN 5 ELSE (SELECT y FROM xy WHERE x=i AND 5/y>1) END)=k
----
select
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── side-effects
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 └── filters
      └── eq [type=bool, outer=(1,2), side-effects, correlated-subquery, constraints=(/1: (/NULL - ])]
           ├── variable: k [type=int]
           └── case [type=int]
                ├── true [type=bool]
                ├── when [type=int]
                │    ├── i < 0 [type=bool]
                │    └── const: 5 [type=int]
                └── subquery [type=int]
                     └── project
                          ├── columns: y:7(int)
                          ├── outer: (2)
                          ├── cardinality: [0 - 1]
                          ├── side-effects
                          ├── key: ()
                          ├── fd: ()-->(7)
                          └── select
                               ├── columns: x:6(int!null) y:7(int)
                               ├── outer: (2)
                               ├── cardinality: [0 - 1]
                               ├── side-effects
                               ├── key: ()
                               ├── fd: ()-->(6,7)
                               ├── scan xy
                               │    ├── columns: x:6(int!null) y:7(int)
                               │    ├── key: (6)
                               │    └── fd: (6)-->(7)
                               └── filters
                                    ├── x = i [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
                                    └── (5 / y) > 1 [type=bool, outer=(7), side-effects]


# Don't decorrelate IFERROR branch if there are side effects
norm
SELECT * FROM a WHERE IFERROR(1/0, (SELECT y::DECIMAL FROM xy WHERE x = i AND 5/y>1))=k
----
select
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── side-effects
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 ├── scan a
 │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 └── filters
      └── eq [type=bool, outer=(1,2), side-effects, correlated-subquery, constraints=(/1: (/NULL - ])]
           ├── variable: k [type=int]
           └── if-err [type=decimal]
                ├── 1 / 0 [type=decimal]
                └── else
                     └── subquery [type=decimal]
                          └── project
                               ├── columns: y:8(decimal)
                               ├── outer: (2)
                               ├── cardinality: [0 - 1]
                               ├── side-effects
                               ├── key: ()
                               ├── fd: ()-->(8)
                               ├── select
                               │    ├── columns: x:6(int!null) xy.y:7(int)
                               │    ├── outer: (2)
                               │    ├── cardinality: [0 - 1]
                               │    ├── side-effects
                               │    ├── key: ()
                               │    ├── fd: ()-->(6,7)
                               │    ├── scan xy
                               │    │    ├── columns: x:6(int!null) xy.y:7(int)
                               │    │    ├── key: (6)
                               │    │    └── fd: (6)-->(7)
                               │    └── filters
                               │         ├── x = i [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
                               │         └── (5 / xy.y) > 1 [type=bool, outer=(7), side-effects]
                               └── projections
                                    └── xy.y::DECIMAL [type=decimal, outer=(7)]

# Decorrelate IFERROR branch if there are no side effects
norm
SELECT * FROM a WHERE IFERROR(1/0, (SELECT y::DECIMAL FROM xy WHERE x = i))=k
----
project
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── side-effects
 ├── fd: (1)-->(2-5)
 └── select
      ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int) y:8(decimal)
      ├── side-effects
      ├── key: (1,6)
      ├── fd: (1)-->(2-5), (6)-->(8)
      ├── left-join
      │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int) y:8(decimal)
      │    ├── key: (1,6)
      │    ├── fd: (1)-->(2-5), (6)-->(8)
      │    ├── scan a
      │    │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
      │    │    ├── key: (1)
      │    │    └── fd: (1)-->(2-5)
      │    ├── project
      │    │    ├── columns: y:8(decimal) x:6(int!null)
      │    │    ├── key: (6)
      │    │    ├── fd: (6)-->(8)
      │    │    ├── scan xy
      │    │    │    ├── columns: x:6(int!null) xy.y:7(int)
      │    │    │    ├── key: (6)
      │    │    │    └── fd: (6)-->(7)
      │    │    └── projections
      │    │         └── xy.y::DECIMAL [type=decimal, outer=(7)]
      │    └── filters
      │         └── x = i [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
      └── filters
           └── k = IFERROR(1 / 0, y) [type=bool, outer=(1,8), side-effects, constraints=(/1: (/NULL - ])]
