# 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)
 ├── side-effects
 ├── key: (1)
 ├── fd: (1)-->(2-5,7)
 ├── ordering: +7 opt(6)
 └── 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 [outer=(1-5), side-effects]
           └── 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 [type=bool, outer=(2), constraints=(/2: [/5 - /5]; tight), fd=()-->(2)]
      │    │         └── i = 5 [type=bool, outer=(2), constraints=(/2: [/5 - /5]; tight)]
      │    └── projections [outer=(2,3), side-effects]
      │         └── i + (random() * 10.0)::INT [type=int, outer=(2), side-effects]
      └── aggregations [outer=(3)]
           └── avg [type=float, outer=(3)]
                └── variable: f [type=float, outer=(3)]

# 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)
 ├── 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 [type=bool, outer=(1), side-effects, constraints=(/1: (/NULL - ])]
 │         └── 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 [type=bool, outer=(6), side-effects, constraints=(/6: (/NULL - ])]
 │         └── x = random() [type=bool, outer=(6), side-effects, constraints=(/6: (/NULL - ])]
 └── merge-on
      ├── left ordering: +1
      ├── right ordering: +6
      └── true [type=bool]

# 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 [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
 │         └── x = i [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ])]
 └── projections [outer=(2,7)]
      └── 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 [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
      │         └── x = i [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ])]
      └── filters [type=bool, outer=(1,2,7), constraints=(/1: (/NULL - ])]
           └── 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 [outer=(2), side-effects]
      └── case [type=int, outer=(2), side-effects]
           ├── true [type=bool]
           ├── when [type=int, outer=(2), side-effects]
           │    ├── i < 0 [type=bool, outer=(2)]
           │    └── subquery [type=int, outer=(2), side-effects]
           │         └── 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 [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
           │                   │         └── x = i [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ])]
           │                   └── (random() * 10.0)::INT [type=int, side-effects]
           └── 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 [type=bool, outer=(1,2), side-effects, constraints=(/1: (/NULL - ])]
      └── eq [type=bool, outer=(1,2), side-effects, constraints=(/1: (/NULL - ])]
           ├── variable: k [type=int, outer=(1)]
           └── case [type=int, outer=(2), side-effects]
                ├── true [type=bool]
                ├── when [type=int, outer=(2)]
                │    ├── i < 0 [type=bool, outer=(2)]
                │    └── const: 5 [type=int]
                └── subquery [type=int, outer=(2), side-effects]
                     └── 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 [type=bool, outer=(2,6,7), side-effects, constraints=(/2: (/NULL - ]; /6: (/NULL - ]), fd=(2)==(6), (6)==(2)]
                                    ├── x = i [type=bool, outer=(2,6), constraints=(/2: (/NULL - ]; /6: (/NULL - ])]
                                    └── (5 / y) > 1 [type=bool, outer=(7), side-effects]
