exec-ddl
CREATE TABLE ints (n BIGINT, INDEX (n));
----

exec-ddl
CREATE TABLE xy (x INT PRIMARY KEY, y INT);
----

# --------------------------------------------------
# InlineWith
# --------------------------------------------------

# Do not inline a multiply-referenced CTE.
norm expect-not=InlineWith format=show-all
WITH foo AS (SELECT 1) (SELECT * FROM foo) UNION ALL (SELECT * FROM foo)
----
with &1 (foo)
 ├── columns: "?column?":4(int!null)
 ├── cardinality: [2 - 2]
 ├── stats: [rows=2]
 ├── cost: 0.08
 ├── prune: (4)
 ├── values
 │    ├── columns: "?column?":1(int!null)
 │    ├── cardinality: [1 - 1]
 │    ├── stats: [rows=1]
 │    ├── cost: 0.02
 │    ├── key: ()
 │    ├── fd: ()-->(1)
 │    ├── prune: (1)
 │    └── tuple [type=tuple{int}]
 │         └── const: 1 [type=int]
 └── union-all
      ├── columns: "?column?":4(int!null)
      ├── left columns: "?column?":2(int)
      ├── right columns: "?column?":3(int)
      ├── cardinality: [2 - 2]
      ├── stats: [rows=2]
      ├── cost: 0.05
      ├── prune: (4)
      ├── cte-uses
      │    └── &1: count=2 used-columns=(1)
      ├── with-scan &1 (foo)
      │    ├── columns: "?column?":2(int!null)
      │    ├── mapping:
      │    │    └──  "?column?":1(int) => "?column?":2(int)
      │    ├── cardinality: [1 - 1]
      │    ├── stats: [rows=1]
      │    ├── cost: 0.01
      │    ├── key: ()
      │    ├── fd: ()-->(2)
      │    ├── prune: (2)
      │    └── cte-uses
      │         └── &1: count=1 used-columns=(1)
      └── with-scan &1 (foo)
           ├── columns: "?column?":3(int!null)
           ├── mapping:
           │    └──  "?column?":1(int) => "?column?":3(int)
           ├── cardinality: [1 - 1]
           ├── stats: [rows=1]
           ├── cost: 0.01
           ├── key: ()
           ├── fd: ()-->(3)
           ├── prune: (3)
           └── cte-uses
                └── &1: count=1 used-columns=(1)

# Inline a multiply-referenced CTE if it is NOT MATERIALIZED.
norm expect=InlineWith format=show-all
WITH foo AS NOT MATERIALIZED (SELECT 1) (SELECT * FROM foo) UNION ALL (SELECT * FROM foo)
----
union-all
 ├── columns: "?column?":4(int!null)
 ├── left columns: "?column?":2(int)
 ├── right columns: "?column?":3(int)
 ├── cardinality: [2 - 2]
 ├── stats: [rows=2]
 ├── cost: 0.07
 ├── prune: (4)
 ├── values
 │    ├── columns: "?column?":2(int!null)
 │    ├── cardinality: [1 - 1]
 │    ├── stats: [rows=1]
 │    ├── cost: 0.02
 │    ├── key: ()
 │    ├── fd: ()-->(2)
 │    ├── prune: (2)
 │    └── tuple [type=tuple{int}]
 │         └── const: 1 [type=int]
 └── values
      ├── columns: "?column?":3(int!null)
      ├── cardinality: [1 - 1]
      ├── stats: [rows=1]
      ├── cost: 0.02
      ├── key: ()
      ├── fd: ()-->(3)
      ├── prune: (3)
      └── tuple [type=tuple{int}]
           └── const: 1 [type=int]

norm format=show-all expect=InlineWith
WITH foo AS (SELECT 1) SELECT * FROM foo
----
values
 ├── columns: "?column?":2(int!null)
 ├── cardinality: [1 - 1]
 ├── stats: [rows=1]
 ├── cost: 0.02
 ├── key: ()
 ├── fd: ()-->(2)
 ├── prune: (2)
 └── tuple [type=tuple{int}]
      └── const: 1 [type=int]

norm format=show-all expect=InlineWith
WITH foo AS (SELECT 1) SELECT * FROM foo CROSS JOIN (VALUES (2))
----
values
 ├── columns: "?column?":2(int!null) column1:3(int!null)
 ├── cardinality: [1 - 1]
 ├── stats: [rows=1]
 ├── cost: 0.02
 ├── key: ()
 ├── fd: ()-->(2,3)
 ├── prune: (2,3)
 └── tuple [type=tuple{int, int}]
      ├── const: 1 [type=int]
      └── const: 2 [type=int]

norm expect=InlineWith
WITH foo AS (SELECT 1), bar AS (SELECT 2) SELECT * FROM foo CROSS JOIN bar
----
values
 ├── columns: "?column?":3!null "?column?":4!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(3,4)
 └── (1, 2)

# Descend into scalar expressions.

norm expect=InlineWith
WITH foo AS (SELECT 1), bar AS (SELECT 2) SELECT (SELECT * FROM foo) + (SELECT * FROM bar)
----
values
 ├── columns: "?column?":5!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(5)
 └── (3,)

norm expect=InlineWith
WITH foo AS (SELECT 1), bar AS (SELECT 2) SELECT (SELECT * FROM foo) + (SELECT * FROM bar) + (SELECT * FROM bar)
----
with &2 (bar)
 ├── columns: "?column?":6
 ├── cardinality: [1 - 1]
 ├── immutable
 ├── key: ()
 ├── fd: ()-->(6)
 ├── values
 │    ├── columns: "?column?":2!null
 │    ├── cardinality: [1 - 1]
 │    ├── key: ()
 │    ├── fd: ()-->(2)
 │    └── (2,)
 └── values
      ├── columns: "?column?":6
      ├── cardinality: [1 - 1]
      ├── immutable
      ├── key: ()
      ├── fd: ()-->(6)
      └── tuple
           └── plus
                ├── plus
                │    ├── subquery
                │    │    └── with-scan &2 (bar)
                │    │         ├── columns: "?column?":4!null
                │    │         ├── mapping:
                │    │         │    └──  "?column?":2 => "?column?":4
                │    │         ├── cardinality: [1 - 1]
                │    │         ├── key: ()
                │    │         └── fd: ()-->(4)
                │    └── 1
                └── subquery
                     └── with-scan &2 (bar)
                          ├── columns: "?column?":5!null
                          ├── mapping:
                          │    └──  "?column?":2 => "?column?":5
                          ├── cardinality: [1 - 1]
                          ├── key: ()
                          └── fd: ()-->(5)

norm expect=InlineWith
WITH foo AS (SELECT 1), bar AS (SELECT 2) SELECT * FROM foo CROSS JOIN bar CROSS JOIN bar AS bar2
----
with &2 (bar)
 ├── columns: "?column?":3!null "?column?":4!null "?column?":5!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(3-5)
 ├── values
 │    ├── columns: "?column?":2!null
 │    ├── cardinality: [1 - 1]
 │    ├── key: ()
 │    ├── fd: ()-->(2)
 │    └── (2,)
 └── inner-join (cross)
      ├── columns: "?column?":3!null "?column?":4!null "?column?":5!null
      ├── cardinality: [1 - 1]
      ├── multiplicity: left-rows(exactly-one), right-rows(exactly-one)
      ├── key: ()
      ├── fd: ()-->(3-5)
      ├── inner-join (cross)
      │    ├── columns: "?column?":3!null "?column?":4!null
      │    ├── cardinality: [1 - 1]
      │    ├── multiplicity: left-rows(exactly-one), right-rows(exactly-one)
      │    ├── key: ()
      │    ├── fd: ()-->(3,4)
      │    ├── values
      │    │    ├── columns: "?column?":3!null
      │    │    ├── cardinality: [1 - 1]
      │    │    ├── key: ()
      │    │    ├── fd: ()-->(3)
      │    │    └── (1,)
      │    ├── with-scan &2 (bar)
      │    │    ├── columns: "?column?":4!null
      │    │    ├── mapping:
      │    │    │    └──  "?column?":2 => "?column?":4
      │    │    ├── cardinality: [1 - 1]
      │    │    ├── key: ()
      │    │    └── fd: ()-->(4)
      │    └── filters (true)
      ├── with-scan &2 (bar)
      │    ├── columns: "?column?":5!null
      │    ├── mapping:
      │    │    └──  "?column?":2 => "?column?":5
      │    ├── cardinality: [1 - 1]
      │    ├── key: ()
      │    └── fd: ()-->(5)
      └── filters (true)

norm format=show-all
WITH
    foo AS (SELECT 1), bar AS (SELECT 2)
SELECT
    *
FROM
    foo CROSS JOIN bar CROSS JOIN bar AS bar2 CROSS JOIN foo AS foo2
----
with &1 (foo)
 ├── columns: "?column?":3(int!null) "?column?":4(int!null) "?column?":5(int!null) "?column?":6(int!null)
 ├── cardinality: [1 - 1]
 ├── stats: [rows=1]
 ├── cost: 0.25
 ├── key: ()
 ├── fd: ()-->(3-6)
 ├── prune: (3-6)
 ├── values
 │    ├── columns: "?column?":1(int!null)
 │    ├── cardinality: [1 - 1]
 │    ├── stats: [rows=1]
 │    ├── cost: 0.02
 │    ├── key: ()
 │    ├── fd: ()-->(1)
 │    ├── prune: (1)
 │    └── tuple [type=tuple{int}]
 │         └── const: 1 [type=int]
 └── with &2 (bar)
      ├── columns: "?column?":3(int!null) "?column?":4(int!null) "?column?":5(int!null) "?column?":6(int!null)
      ├── cardinality: [1 - 1]
      ├── stats: [rows=1]
      ├── cost: 0.22
      ├── key: ()
      ├── fd: ()-->(3-6)
      ├── prune: (3-6)
      ├── cte-uses
      │    └── &1: count=2 used-columns=(1)
      ├── values
      │    ├── columns: "?column?":2(int!null)
      │    ├── cardinality: [1 - 1]
      │    ├── stats: [rows=1]
      │    ├── cost: 0.02
      │    ├── key: ()
      │    ├── fd: ()-->(2)
      │    ├── prune: (2)
      │    └── tuple [type=tuple{int}]
      │         └── const: 2 [type=int]
      └── inner-join (cross)
           ├── columns: "?column?":3(int!null) "?column?":4(int!null) "?column?":5(int!null) "?column?":6(int!null)
           ├── cardinality: [1 - 1]
           ├── multiplicity: left-rows(exactly-one), right-rows(exactly-one)
           ├── stats: [rows=1]
           ├── cost: 0.19
           ├── key: ()
           ├── fd: ()-->(3-6)
           ├── prune: (3-6)
           ├── cte-uses
           │    ├── &1: count=2 used-columns=(1)
           │    └── &2: count=2 used-columns=(2)
           ├── inner-join (cross)
           │    ├── columns: "?column?":3(int!null) "?column?":4(int!null) "?column?":5(int!null)
           │    ├── cardinality: [1 - 1]
           │    ├── multiplicity: left-rows(exactly-one), right-rows(exactly-one)
           │    ├── stats: [rows=1]
           │    ├── cost: 0.13
           │    ├── key: ()
           │    ├── fd: ()-->(3-5)
           │    ├── prune: (3-5)
           │    ├── cte-uses
           │    │    ├── &1: count=1 used-columns=(1)
           │    │    └── &2: count=2 used-columns=(2)
           │    ├── inner-join (cross)
           │    │    ├── columns: "?column?":3(int!null) "?column?":4(int!null)
           │    │    ├── cardinality: [1 - 1]
           │    │    ├── multiplicity: left-rows(exactly-one), right-rows(exactly-one)
           │    │    ├── stats: [rows=1]
           │    │    ├── cost: 0.07
           │    │    ├── key: ()
           │    │    ├── fd: ()-->(3,4)
           │    │    ├── prune: (3,4)
           │    │    ├── cte-uses
           │    │    │    ├── &1: count=1 used-columns=(1)
           │    │    │    └── &2: count=1 used-columns=(2)
           │    │    ├── with-scan &1 (foo)
           │    │    │    ├── columns: "?column?":3(int!null)
           │    │    │    ├── mapping:
           │    │    │    │    └──  "?column?":1(int) => "?column?":3(int)
           │    │    │    ├── cardinality: [1 - 1]
           │    │    │    ├── stats: [rows=1]
           │    │    │    ├── cost: 0.01
           │    │    │    ├── key: ()
           │    │    │    ├── fd: ()-->(3)
           │    │    │    ├── prune: (3)
           │    │    │    └── cte-uses
           │    │    │         └── &1: count=1 used-columns=(1)
           │    │    ├── with-scan &2 (bar)
           │    │    │    ├── columns: "?column?":4(int!null)
           │    │    │    ├── mapping:
           │    │    │    │    └──  "?column?":2(int) => "?column?":4(int)
           │    │    │    ├── cardinality: [1 - 1]
           │    │    │    ├── stats: [rows=1]
           │    │    │    ├── cost: 0.01
           │    │    │    ├── key: ()
           │    │    │    ├── fd: ()-->(4)
           │    │    │    ├── prune: (4)
           │    │    │    └── cte-uses
           │    │    │         └── &2: count=1 used-columns=(2)
           │    │    └── filters (true)
           │    ├── with-scan &2 (bar)
           │    │    ├── columns: "?column?":5(int!null)
           │    │    ├── mapping:
           │    │    │    └──  "?column?":2(int) => "?column?":5(int)
           │    │    ├── cardinality: [1 - 1]
           │    │    ├── stats: [rows=1]
           │    │    ├── cost: 0.01
           │    │    ├── key: ()
           │    │    ├── fd: ()-->(5)
           │    │    ├── prune: (5)
           │    │    └── cte-uses
           │    │         └── &2: count=1 used-columns=(2)
           │    └── filters (true)
           ├── with-scan &1 (foo)
           │    ├── columns: "?column?":6(int!null)
           │    ├── mapping:
           │    │    └──  "?column?":1(int) => "?column?":6(int)
           │    ├── cardinality: [1 - 1]
           │    ├── stats: [rows=1]
           │    ├── cost: 0.01
           │    ├── key: ()
           │    ├── fd: ()-->(6)
           │    ├── prune: (6)
           │    └── cte-uses
           │         └── &1: count=1 used-columns=(1)
           └── filters (true)

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

norm
WITH foo AS (VALUES (1))
SELECT * FROM a WHERE NOT EXISTS(SELECT * FROM (VALUES (k), ((SELECT * FROM foo))) WHERE column1=k)
----
anti-join-apply
 ├── columns: k:2!null i:3 f:4 s:5 j:6
 ├── key: (2)
 ├── fd: (2)-->(3-6)
 ├── scan a
 │    ├── columns: k:2!null i:3 f:4 s:5 j:6
 │    ├── key: (2)
 │    └── fd: (2)-->(3-6)
 ├── values
 │    ├── columns: column1:10
 │    ├── outer: (2)
 │    ├── cardinality: [2 - 2]
 │    ├── (k:2,)
 │    └── (1,)
 └── filters
      └── column1:10 = k:2 [outer=(2,10), constraints=(/2: (/NULL - ]; /10: (/NULL - ]), fd=(2)==(10), (10)==(2)]

# Don't inline side-effecting expressions.
norm expect-not=InlineWith
WITH foo AS (INSERT INTO a VALUES (1) RETURNING *) SELECT * FROM foo
----
with &1 (foo)
 ├── columns: k:13!null i:14 f:15 s:16 j:17
 ├── cardinality: [1 - 1]
 ├── volatile, mutations
 ├── key: ()
 ├── fd: ()-->(13-17)
 ├── insert a
 │    ├── columns: a.k:1!null a.i:2 a.f:3 a.s:4 a.j:5
 │    ├── insert-mapping:
 │    │    ├── column1:8 => a.k:1
 │    │    ├── i_default:9 => a.i:2
 │    │    ├── f_default:10 => a.f:3
 │    │    ├── s_default:11 => a.s:4
 │    │    └── j_default:12 => a.j:5
 │    ├── return-mapping:
 │    │    ├── column1:8 => a.k:1
 │    │    ├── i_default:9 => a.i:2
 │    │    ├── f_default:10 => a.f:3
 │    │    ├── s_default:11 => a.s:4
 │    │    └── j_default:12 => a.j:5
 │    ├── cardinality: [1 - 1]
 │    ├── volatile, mutations
 │    ├── key: ()
 │    ├── fd: ()-->(1-5)
 │    └── values
 │         ├── columns: column1:8!null i_default:9 f_default:10 s_default:11 j_default:12
 │         ├── cardinality: [1 - 1]
 │         ├── key: ()
 │         ├── fd: ()-->(8-12)
 │         └── (1, NULL, NULL, NULL, NULL)
 └── with-scan &1 (foo)
      ├── columns: k:13!null i:14 f:15 s:16 j:17
      ├── mapping:
      │    ├──  a.k:1 => k:13
      │    ├──  a.i:2 => i:14
      │    ├──  a.f:3 => f:15
      │    ├──  a.s:4 => s:16
      │    └──  a.j:5 => j:17
      ├── cardinality: [1 - 1]
      ├── key: ()
      └── fd: ()-->(13-17)

norm expect-not=InlineWith
WITH foo AS (SELECT crdb_internal.notice('foo')) SELECT * FROM foo
----
with &1 (foo)
 ├── columns: crdb_internal.notice:2
 ├── cardinality: [1 - 1]
 ├── volatile
 ├── key: ()
 ├── fd: ()-->(2)
 ├── values
 │    ├── columns: crdb_internal.notice:1
 │    ├── cardinality: [1 - 1]
 │    ├── volatile
 │    ├── key: ()
 │    ├── fd: ()-->(1)
 │    └── (crdb_internal.notice('foo'),)
 └── with-scan &1 (foo)
      ├── columns: crdb_internal.notice:2
      ├── mapping:
      │    └──  crdb_internal.notice:1 => crdb_internal.notice:2
      ├── cardinality: [1 - 1]
      ├── key: ()
      └── fd: ()-->(2)

# Don't inline side-effecting expressions, even if the CTE is NOT MATERIALIZED.
norm expect-not=InlineWith
WITH foo AS NOT MATERIALIZED (INSERT INTO a VALUES (1) RETURNING *) SELECT * FROM foo
----
with &1 (foo)
 ├── columns: k:13!null i:14 f:15 s:16 j:17
 ├── not-materialized
 ├── cardinality: [1 - 1]
 ├── volatile, mutations
 ├── key: ()
 ├── fd: ()-->(13-17)
 ├── insert a
 │    ├── columns: a.k:1!null a.i:2 a.f:3 a.s:4 a.j:5
 │    ├── insert-mapping:
 │    │    ├── column1:8 => a.k:1
 │    │    ├── i_default:9 => a.i:2
 │    │    ├── f_default:10 => a.f:3
 │    │    ├── s_default:11 => a.s:4
 │    │    └── j_default:12 => a.j:5
 │    ├── return-mapping:
 │    │    ├── column1:8 => a.k:1
 │    │    ├── i_default:9 => a.i:2
 │    │    ├── f_default:10 => a.f:3
 │    │    ├── s_default:11 => a.s:4
 │    │    └── j_default:12 => a.j:5
 │    ├── cardinality: [1 - 1]
 │    ├── volatile, mutations
 │    ├── key: ()
 │    ├── fd: ()-->(1-5)
 │    └── values
 │         ├── columns: column1:8!null i_default:9 f_default:10 s_default:11 j_default:12
 │         ├── cardinality: [1 - 1]
 │         ├── key: ()
 │         ├── fd: ()-->(8-12)
 │         └── (1, NULL, NULL, NULL, NULL)
 └── with-scan &1 (foo)
      ├── columns: k:13!null i:14 f:15 s:16 j:17
      ├── mapping:
      │    ├──  a.k:1 => k:13
      │    ├──  a.i:2 => i:14
      │    ├──  a.f:3 => f:15
      │    ├──  a.s:4 => s:16
      │    └──  a.j:5 => j:17
      ├── cardinality: [1 - 1]
      ├── key: ()
      └── fd: ()-->(13-17)

norm expect-not=InlineWith
WITH foo AS NOT MATERIALIZED (SELECT crdb_internal.notice('foo')) SELECT * FROM foo
----
with &1 (foo)
 ├── columns: crdb_internal.notice:2
 ├── not-materialized
 ├── cardinality: [1 - 1]
 ├── volatile
 ├── key: ()
 ├── fd: ()-->(2)
 ├── values
 │    ├── columns: crdb_internal.notice:1
 │    ├── cardinality: [1 - 1]
 │    ├── volatile
 │    ├── key: ()
 │    ├── fd: ()-->(1)
 │    └── (crdb_internal.notice('foo'),)
 └── with-scan &1 (foo)
      ├── columns: crdb_internal.notice:2
      ├── mapping:
      │    └──  crdb_internal.notice:1 => crdb_internal.notice:2
      ├── cardinality: [1 - 1]
      ├── key: ()
      └── fd: ()-->(2)

norm expect=InlineWith
WITH foo AS (SELECT 1), bar AS (SELECT * FROM foo) SELECT * FROM foo
----
values
 ├── columns: "?column?":3!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(3)
 └── (1,)

norm expect=InlineWith
WITH foo AS (SELECT 1), bar AS (SELECT * FROM foo) SELECT * FROM foo
----
values
 ├── columns: "?column?":3!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(3)
 └── (1,)

# Inline nested Withs.
norm expect=InlineWith
WITH
    t (x) AS (WITH t (x) AS (SELECT 1) SELECT x * 10 FROM t)
SELECT
    x + 2
FROM
    t
----
values
 ├── columns: "?column?":5!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(5)
 └── (12,)

# Regression test for #43148: WithScans with no columns should still be
# uniquely identifiable. Without this uniqueness, they can't be assigned
# different required physical properties.
norm
WITH cte AS (SELECT * FROM a) (SELECT 1 FROM cte LIMIT 9) UNION (SELECT 1 FROM cte LIMIT 10)
----
with &1 (cte)
 ├── columns: "?column?":20!null
 ├── cardinality: [0 - 19]
 ├── key: (20)
 ├── scan a
 │    ├── columns: a.k:1!null a.i:2 a.f:3 a.s:4 a.j:5
 │    ├── key: (1)
 │    └── fd: (1)-->(2-5)
 └── union
      ├── columns: "?column?":20!null
      ├── left columns: "?column?":13
      ├── right columns: "?column?":19
      ├── cardinality: [0 - 19]
      ├── key: (20)
      ├── project
      │    ├── columns: "?column?":13!null
      │    ├── cardinality: [0 - 9]
      │    ├── fd: ()-->(13)
      │    ├── limit
      │    │    ├── cardinality: [0 - 9]
      │    │    ├── with-scan &1 (cte)
      │    │    │    ├── mapping:
      │    │    │    └── limit hint: 9.00
      │    │    └── 9
      │    └── projections
      │         └── 1 [as="?column?":13]
      └── project
           ├── columns: "?column?":19!null
           ├── cardinality: [0 - 10]
           ├── fd: ()-->(19)
           ├── limit
           │    ├── cardinality: [0 - 10]
           │    ├── with-scan &1 (cte)
           │    │    ├── mapping:
           │    │    └── limit hint: 10.00
           │    └── 10
           └── projections
                └── 1 [as="?column?":19]

# Check cte-uses when used with mutations (for FK checks).
exec-ddl
CREATE TABLE parent (p INT PRIMARY KEY)
----

exec-ddl
CREATE TABLE child (c INT PRIMARY KEY, p INT REFERENCES parent(p))
----

norm format=show-all
WITH cte AS (INSERT INTO child VALUES (1, 1) RETURNING c) SELECT c FROM cte UNION SELECT c+1 FROM cte
----
with &2 (cte)
 ├── columns: c:14(int!null)
 ├── cardinality: [1 - 2]
 ├── volatile, mutations
 ├── stats: [rows=2, distinct(14)=2, null(14)=0]
 ├── cost: 1060.9075
 ├── cost-flags: full-scan-penalty
 ├── key: (14)
 ├── insert t.public.child
 │    ├── columns: t.public.child.c:1(int!null)
 │    ├── insert-mapping:
 │    │    ├── column1:5 => t.public.child.c:1
 │    │    └── column2:6 => t.public.child.p:2
 │    ├── return-mapping:
 │    │    └── column1:5 => t.public.child.c:1
 │    ├── input binding: &1
 │    ├── cardinality: [1 - 1]
 │    ├── volatile, mutations
 │    ├── stats: [rows=1, distinct(1)=1, null(1)=0]
 │    ├── cost: 1060.7975
 │    ├── cost-flags: full-scan-penalty
 │    ├── key: ()
 │    ├── fd: ()-->(1)
 │    ├── values
 │    │    ├── columns: column1:5(int!null) column2:6(int!null)
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── stats: [rows=1, distinct(5)=1, null(5)=0, distinct(6)=1, null(6)=0]
 │    │    ├── cost: 0.02
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(5,6)
 │    │    ├── prune: (5,6)
 │    │    └── tuple [type=tuple{int, int}]
 │    │         ├── const: 1 [type=int]
 │    │         └── const: 1 [type=int]
 │    └── f-k-checks
 │         └── f-k-checks-item: child(p) -> parent(p)
 │              └── anti-join (hash)
 │                   ├── columns: p:7(int!null)
 │                   ├── cardinality: [0 - 1]
 │                   ├── stats: [rows=1e-10]
 │                   ├── cost: 1060.7675
 │                   ├── cost-flags: full-scan-penalty
 │                   ├── key: ()
 │                   ├── fd: ()-->(7)
 │                   ├── cte-uses
 │                   │    └── &1: count=1 used-columns=(6)
 │                   ├── with-scan &1
 │                   │    ├── columns: p:7(int!null)
 │                   │    ├── mapping:
 │                   │    │    └──  column2:6(int) => p:7(int)
 │                   │    ├── cardinality: [1 - 1]
 │                   │    ├── stats: [rows=1, distinct(7)=1, null(7)=0]
 │                   │    ├── cost: 0.01
 │                   │    ├── key: ()
 │                   │    ├── fd: ()-->(7)
 │                   │    ├── prune: (7)
 │                   │    └── cte-uses
 │                   │         └── &1: count=1 used-columns=(6)
 │                   ├── scan t.public.parent
 │                   │    ├── columns: t.public.parent.p:8(int!null)
 │                   │    ├── flags: avoid-full-scan disabled not visible index feature
 │                   │    ├── stats: [rows=1000, distinct(8)=1000, null(8)=0]
 │                   │    ├── cost: 1048.22
 │                   │    ├── cost-flags: full-scan-penalty
 │                   │    ├── key: (8)
 │                   │    ├── prune: (8)
 │                   │    ├── interesting orderings: (+8)
 │                   │    └── unfiltered-cols: (8-10)
 │                   └── filters
 │                        └── eq [type=bool, outer=(7,8), constraints=(/7: (/NULL - ]; /8: (/NULL - ]), fd=(7)==(8), (8)==(7)]
 │                             ├── variable: p:7 [type=int]
 │                             └── variable: t.public.parent.p:8 [type=int]
 └── union
      ├── columns: c:14(int!null)
      ├── left columns: c:11(int)
      ├── right columns: "?column?":13(int)
      ├── cardinality: [1 - 2]
      ├── immutable
      ├── stats: [rows=2, distinct(14)=2, null(14)=0]
      ├── cost: 0.1
      ├── key: (14)
      ├── with-scan &2 (cte)
      │    ├── columns: c:11(int!null)
      │    ├── mapping:
      │    │    └──  t.public.child.c:1(int) => c:11(int)
      │    ├── cardinality: [1 - 1]
      │    ├── stats: [rows=1, distinct(11)=1, null(11)=0]
      │    ├── cost: 0.01
      │    ├── key: ()
      │    ├── fd: ()-->(11)
      │    └── prune: (11)
      └── project
           ├── columns: "?column?":13(int!null)
           ├── cardinality: [1 - 1]
           ├── immutable
           ├── stats: [rows=1, distinct(13)=1, null(13)=0]
           ├── cost: 0.04
           ├── key: ()
           ├── fd: ()-->(13)
           ├── prune: (13)
           ├── with-scan &2 (cte)
           │    ├── columns: c:12(int!null)
           │    ├── mapping:
           │    │    └──  t.public.child.c:1(int) => c:12(int)
           │    ├── cardinality: [1 - 1]
           │    ├── stats: [rows=1, distinct(12)=1, null(12)=0]
           │    ├── cost: 0.01
           │    ├── key: ()
           │    ├── fd: ()-->(12)
           │    └── prune: (12)
           └── projections
                └── plus [as="?column?":13, type=int, outer=(12), immutable]
                     ├── variable: c:12 [type=int]
                     └── const: 1 [type=int]

# Original CTE is inlined, adding "NOT MATERIALIZED" should not change the behavior.
norm format=show-all expect=InlineWith
WITH foo AS NOT MATERIALIZED (SELECT 1) SELECT * FROM foo
----
values
 ├── columns: "?column?":2(int!null)
 ├── cardinality: [1 - 1]
 ├── stats: [rows=1]
 ├── cost: 0.02
 ├── key: ()
 ├── fd: ()-->(2)
 ├── prune: (2)
 └── tuple [type=tuple{int}]
      └── const: 1 [type=int]

# Original CTE is inlined, adding "MATERIALIZED" should prevent inlining.
norm format=show-all expect-not=InlineWith
WITH foo AS MATERIALIZED (SELECT 1) SELECT * FROM foo
----
with &1 (foo)
 ├── columns: "?column?":2(int!null)
 ├── materialized
 ├── cardinality: [1 - 1]
 ├── stats: [rows=1]
 ├── cost: 0.04
 ├── key: ()
 ├── fd: ()-->(2)
 ├── prune: (2)
 ├── values
 │    ├── columns: "?column?":1(int!null)
 │    ├── cardinality: [1 - 1]
 │    ├── stats: [rows=1]
 │    ├── cost: 0.02
 │    ├── key: ()
 │    ├── fd: ()-->(1)
 │    ├── prune: (1)
 │    └── tuple [type=tuple{int}]
 │         └── const: 1 [type=int]
 └── with-scan &1 (foo)
      ├── columns: "?column?":2(int!null)
      ├── mapping:
      │    └──  "?column?":1(int) => "?column?":2(int)
      ├── cardinality: [1 - 1]
      ├── stats: [rows=1]
      ├── cost: 0.01
      ├── key: ()
      ├── fd: ()-->(2)
      └── prune: (2)

# Original CTE is not inlined, adding "MATERIALIZED" should not change the behavior.
norm expect-not=InlineWith
WITH foo AS MATERIALIZED (SELECT 1/0) SELECT * FROM foo
----
with &1 (foo)
 ├── columns: "?column?":2
 ├── materialized
 ├── cardinality: [1 - 1]
 ├── immutable
 ├── key: ()
 ├── fd: ()-->(2)
 ├── values
 │    ├── columns: "?column?":1
 │    ├── cardinality: [1 - 1]
 │    ├── immutable
 │    ├── key: ()
 │    ├── fd: ()-->(1)
 │    └── (1 / 0,)
 └── with-scan &1 (foo)
      ├── columns: "?column?":2
      ├── mapping:
      │    └──  "?column?":1 => "?column?":2
      ├── cardinality: [1 - 1]
      ├── key: ()
      └── fd: ()-->(2)

# Original CTE is not inlined, adding "NOT MATERIALIZED" should force the inline.
norm expect=InlineWith
WITH foo AS NOT MATERIALIZED (SELECT 1/0) SELECT * FROM foo
----
values
 ├── columns: "?column?":2
 ├── cardinality: [1 - 1]
 ├── immutable
 ├── key: ()
 ├── fd: ()-->(2)
 └── (1 / 0,)

# Original CTE is not inlined, adding "NOT MATERIALIZED" should force the inline.
norm expect=InlineWith
WITH foo AS NOT MATERIALIZED (SELECT 1) SELECT * FROM foo UNION ALL SELECT * FROM foo;
----
union-all
 ├── columns: "?column?":4!null
 ├── left columns: "?column?":2
 ├── right columns: "?column?":3
 ├── cardinality: [2 - 2]
 ├── values
 │    ├── columns: "?column?":2!null
 │    ├── cardinality: [1 - 1]
 │    ├── key: ()
 │    ├── fd: ()-->(2)
 │    └── (1,)
 └── values
      ├── columns: "?column?":3!null
      ├── cardinality: [1 - 1]
      ├── key: ()
      ├── fd: ()-->(3)
      └── (1,)

# Recursive CTEs should respect "MATERIALIZED".
norm expect-not=InlineWith
WITH RECURSIVE t(n) AS MATERIALIZED (VALUES (1) UNION ALL SELECT n+1 FROM t WHERE n < 100 ) SELECT sum(n) FROM t;
----
with &3 (t)
 ├── columns: sum:6
 ├── materialized
 ├── cardinality: [1 - 1]
 ├── immutable
 ├── key: ()
 ├── fd: ()-->(6)
 ├── recursive-c-t-e
 │    ├── columns: n:2
 │    ├── working table binding: &2
 │    ├── initial columns: column1:1
 │    ├── recursive columns: "?column?":4
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: n:2
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(2)
 │    ├── values
 │    │    ├── columns: column1:1!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1)
 │    │    └── (1,)
 │    └── project
 │         ├── columns: "?column?":4!null
 │         ├── cardinality: [0 - 1]
 │         ├── immutable
 │         ├── key: ()
 │         ├── fd: ()-->(4)
 │         ├── select
 │         │    ├── columns: n:3!null
 │         │    ├── cardinality: [0 - 1]
 │         │    ├── key: ()
 │         │    ├── fd: ()-->(3)
 │         │    ├── with-scan &2 (t)
 │         │    │    ├── columns: n:3
 │         │    │    ├── mapping:
 │         │    │    │    └──  n:2 => n:3
 │         │    │    ├── cardinality: [1 - 1]
 │         │    │    ├── key: ()
 │         │    │    └── fd: ()-->(3)
 │         │    └── filters
 │         │         └── n:3 < 100 [outer=(3), constraints=(/3: (/NULL - /99]; tight)]
 │         └── projections
 │              └── n:3 + 1 [as="?column?":4, outer=(3), immutable]
 └── scalar-group-by
      ├── columns: sum:6
      ├── cardinality: [1 - 1]
      ├── key: ()
      ├── fd: ()-->(6)
      ├── with-scan &3 (t)
      │    ├── columns: n:5
      │    ├── mapping:
      │    │    └──  n:2 => n:5
      │    └── cardinality: [1 - ]
      └── aggregations
           └── sum [as=sum:6, outer=(5)]
                └── n:5

# Recursive CTEs should respect "NOT MATERIALIZED".
norm expect=InlineWith
WITH RECURSIVE t(n) AS NOT MATERIALIZED (VALUES (1) UNION ALL SELECT n+1 FROM t WHERE n < 100 ) SELECT sum(n) FROM t;
----
scalar-group-by
 ├── columns: sum:6
 ├── cardinality: [1 - 1]
 ├── immutable
 ├── key: ()
 ├── fd: ()-->(6)
 ├── project
 │    ├── columns: n:5
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── recursive-c-t-e
 │    │    ├── columns: n:2
 │    │    ├── working table binding: &2
 │    │    ├── initial columns: column1:1
 │    │    ├── recursive columns: "?column?":4
 │    │    ├── cardinality: [1 - ]
 │    │    ├── immutable
 │    │    ├── fake-rel
 │    │    │    ├── columns: n:2
 │    │    │    ├── cardinality: [1 - 1]
 │    │    │    ├── key: ()
 │    │    │    └── fd: ()-->(2)
 │    │    ├── values
 │    │    │    ├── columns: column1:1!null
 │    │    │    ├── cardinality: [1 - 1]
 │    │    │    ├── key: ()
 │    │    │    ├── fd: ()-->(1)
 │    │    │    └── (1,)
 │    │    └── project
 │    │         ├── columns: "?column?":4!null
 │    │         ├── cardinality: [0 - 1]
 │    │         ├── immutable
 │    │         ├── key: ()
 │    │         ├── fd: ()-->(4)
 │    │         ├── select
 │    │         │    ├── columns: n:3!null
 │    │         │    ├── cardinality: [0 - 1]
 │    │         │    ├── key: ()
 │    │         │    ├── fd: ()-->(3)
 │    │         │    ├── with-scan &2 (t)
 │    │         │    │    ├── columns: n:3
 │    │         │    │    ├── mapping:
 │    │         │    │    │    └──  n:2 => n:3
 │    │         │    │    ├── cardinality: [1 - 1]
 │    │         │    │    ├── key: ()
 │    │         │    │    └── fd: ()-->(3)
 │    │         │    └── filters
 │    │         │         └── n:3 < 100 [outer=(3), constraints=(/3: (/NULL - /99]; tight)]
 │    │         └── projections
 │    │              └── n:3 + 1 [as="?column?":4, outer=(3), immutable]
 │    └── projections
 │         └── n:2 [as=n:5, outer=(2)]
 └── aggregations
      └── sum [as=sum:6, outer=(5)]
           └── n:5

# --------------------------------------------------
# InlineAnyWithScanOfValues
# --------------------------------------------------

exec-ddl
CREATE TABLE t87790(i INT, j INT, k float, CONSTRAINT "primary" PRIMARY KEY (i,j))
----

# An array unnested to VALUES should be inlined.
norm expect=InlineAnyWithScanOfValues
WITH ivals AS (SELECT * FROM unnest(ARRAY[1,2,3]))
SELECT * FROM t87790 WHERE i IN (SELECT * FROM ivals)
----
select
 ├── columns: i:2!null j:3!null k:4
 ├── key: (2,3)
 ├── fd: (2,3)-->(4)
 ├── scan t87790
 │    ├── columns: i:2!null j:3!null k:4
 │    ├── key: (2,3)
 │    └── fd: (2,3)-->(4)
 └── filters
      └── i:2 IN (1, 2, 3) [outer=(2), constraints=(/2: [/1 - /1] [/2 - /2] [/3 - /3]; tight)]

# An array unnested to VALUES in a MATERIALIZED WITH should not be inlined.
norm expect-not=InlineAnyWithScanOfValues
WITH ivals AS MATERIALIZED (SELECT * FROM unnest(ARRAY[1,2,3]))
SELECT * FROM t87790 WHERE i IN (SELECT * FROM ivals)
----
with &1 (ivals)
 ├── columns: i:2!null j:3!null k:4
 ├── materialized
 ├── key: (2,3)
 ├── fd: (2,3)-->(4)
 ├── values
 │    ├── columns: unnest:1!null
 │    ├── cardinality: [3 - 3]
 │    ├── (1,)
 │    ├── (2,)
 │    └── (3,)
 └── semi-join (hash)
      ├── columns: i:2!null j:3!null k:4
      ├── key: (2,3)
      ├── fd: (2,3)-->(4)
      ├── scan t87790
      │    ├── columns: i:2!null j:3!null k:4
      │    ├── key: (2,3)
      │    └── fd: (2,3)-->(4)
      ├── with-scan &1 (ivals)
      │    ├── columns: unnest:7!null
      │    ├── mapping:
      │    │    └──  unnest:1 => unnest:7
      │    └── cardinality: [3 - 3]
      └── filters
           └── i:2 = unnest:7 [outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ]), fd=(2)==(7), (7)==(2)]

# Random (volatile) VALUES should not be inlined.
norm expect-not=InlineAnyWithScanOfValues
WITH randvals AS (SELECT random())
SELECT * FROM t87790 WHERE k NOT IN (SELECT * FROM randvals)
----
with &1 (randvals)
 ├── columns: i:2!null j:3!null k:4
 ├── volatile
 ├── key: (2,3)
 ├── fd: (2,3)-->(4)
 ├── values
 │    ├── columns: random:1
 │    ├── cardinality: [1 - 1]
 │    ├── volatile
 │    ├── key: ()
 │    ├── fd: ()-->(1)
 │    └── (random(),)
 └── anti-join (cross)
      ├── columns: i:2!null j:3!null k:4
      ├── key: (2,3)
      ├── fd: (2,3)-->(4)
      ├── scan t87790
      │    ├── columns: i:2!null j:3!null k:4
      │    ├── key: (2,3)
      │    └── fd: (2,3)-->(4)
      ├── with-scan &1 (randvals)
      │    ├── columns: random:7
      │    ├── mapping:
      │    │    └──  random:1 => random:7
      │    ├── cardinality: [1 - 1]
      │    ├── key: ()
      │    └── fd: ()-->(7)
      └── filters
           └── (k:4 = random:7) IS NOT false [outer=(4,7)]

# A non-constant WITH expression cannot be inlined in the middle of
# normalization.
norm expect-not=InlineAnyWithScanOfValues
WITH ivals AS (SELECT * FROM unnest(ARRAY(SELECT i FROM t87790)))
SELECT * FROM t87790 WHERE i IN (SELECT * FROM ivals)
----
semi-join (hash)
 ├── columns: i:7!null j:8!null k:9
 ├── immutable
 ├── key: (7,8)
 ├── fd: (7,8)-->(9)
 ├── scan t87790
 │    ├── columns: i:7!null j:8!null k:9
 │    ├── key: (7,8)
 │    └── fd: (7,8)-->(9)
 ├── project
 │    ├── columns: unnest:12
 │    ├── immutable
 │    ├── project-set
 │    │    ├── columns: unnest:6
 │    │    ├── immutable
 │    │    ├── values
 │    │    │    ├── cardinality: [1 - 1]
 │    │    │    ├── key: ()
 │    │    │    └── ()
 │    │    └── zip
 │    │         └── function: unnest [immutable, subquery]
 │    │              └── array-flatten
 │    │                   └── scan t87790
 │    │                        └── columns: i:1!null
 │    └── projections
 │         └── unnest:6 [as=unnest:12, outer=(6)]
 └── filters
      └── i:7 = unnest:12 [outer=(7,12), constraints=(/7: (/NULL - ]; /12: (/NULL - ]), fd=(7)==(12), (12)==(7)]

# Placeholders are considered constant for the InlineAnyWithScanOfValues
# normalization.
assign-placeholders-norm query-args=(1, 2, 3)
WITH ivals AS (SELECT * FROM unnest(ARRAY[$1::INT, $2::INT, $3::INT]))
SELECT * FROM t87790 WHERE i IN (SELECT * FROM ivals)
----
select
 ├── columns: i:2!null j:3!null k:4
 ├── key: (2,3)
 ├── fd: (2,3)-->(4)
 ├── scan t87790
 │    ├── columns: i:2!null j:3!null k:4
 │    ├── key: (2,3)
 │    └── fd: (2,3)-->(4)
 └── filters
      └── i:2 IN (1, 2, 3) [outer=(2), constraints=(/2: [/1 - /1] [/2 - /2] [/3 - /3]; tight)]

norm expect=InlineAnyWithScanOfValues
WITH v(a,b) AS (VALUES (1,2))
SELECT 1 FROM t87790 WHERE i IN (SELECT a FROM v)
UNION ALL
SELECT 1 FROM t87790 WHERE j IN (SELECT b FROM v)
----
union-all
 ├── columns: "?column?":19!null
 ├── left columns: "?column?":10
 ├── right columns: "?column?":18
 ├── project
 │    ├── columns: "?column?":10!null
 │    ├── fd: ()-->(10)
 │    ├── select
 │    │    ├── columns: i:3!null
 │    │    ├── fd: ()-->(3)
 │    │    ├── scan t87790
 │    │    │    └── columns: i:3!null
 │    │    └── filters
 │    │         └── i:3 = 1 [outer=(3), constraints=(/3: [/1 - /1]; tight), fd=()-->(3)]
 │    └── projections
 │         └── 1 [as="?column?":10]
 └── project
      ├── columns: "?column?":18!null
      ├── fd: ()-->(18)
      ├── select
      │    ├── columns: j:12!null
      │    ├── fd: ()-->(12)
      │    ├── scan t87790
      │    │    └── columns: j:12!null
      │    └── filters
      │         └── j:12 = 2 [outer=(12), constraints=(/12: [/2 - /2]; tight), fd=()-->(12)]
      └── projections
           └── 1 [as="?column?":18]

# --------------------------------------------------
# ApplyLimitToRecursiveCTEScan
# --------------------------------------------------

# Simple iterative case. Note that the limit is not explicit on the anchor
# branch, but its cardinality is bounded anyway.
norm expect=ApplyLimitToRecursiveCTEScan expect-not=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i) AS (
  (SELECT 1)
  UNION ALL
  (SELECT i+1 FROM cte WHERE i <= 10 LIMIT 1)
)
SELECT i FROM cte;
----
project
 ├── columns: i:5
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:2
 │    ├── working table binding: &2
 │    ├── initial columns: "?column?":1
 │    ├── recursive columns: "?column?":4
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:2
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(2)
 │    ├── values
 │    │    ├── columns: "?column?":1!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1)
 │    │    └── (1,)
 │    └── project
 │         ├── columns: "?column?":4!null
 │         ├── cardinality: [0 - 1]
 │         ├── immutable
 │         ├── key: ()
 │         ├── fd: ()-->(4)
 │         ├── select
 │         │    ├── columns: i:3!null
 │         │    ├── cardinality: [0 - 1]
 │         │    ├── key: ()
 │         │    ├── fd: ()-->(3)
 │         │    ├── with-scan &2 (cte)
 │         │    │    ├── columns: i:3
 │         │    │    ├── mapping:
 │         │    │    │    └──  i:2 => i:3
 │         │    │    ├── cardinality: [1 - 1]
 │         │    │    ├── key: ()
 │         │    │    └── fd: ()-->(3)
 │         │    └── filters
 │         │         └── i:3 <= 10 [outer=(3), constraints=(/3: (/NULL - /10]; tight)]
 │         └── projections
 │              └── i:3 + 1 [as="?column?":4, outer=(3), immutable]
 └── projections
      └── i:2 [as=i:5, outer=(2)]

# Case with different (but bounded) cardinalities for the anchor and
# recursive branches. The max cardinality of the two is used for the
# recursive scan.
norm expect=ApplyLimitToRecursiveCTEScan expect-not=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i) AS (
  (SELECT 1)
  UNION ALL
  (SELECT i+1 FROM cte WHERE i <= 10 LIMIT 5)
)
SELECT i FROM cte;
----
project
 ├── columns: i:5
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:2
 │    ├── working table binding: &2
 │    ├── initial columns: "?column?":1
 │    ├── recursive columns: "?column?":4
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:2
 │    │    └── cardinality: [1 - 5]
 │    ├── values
 │    │    ├── columns: "?column?":1!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1)
 │    │    └── (1,)
 │    └── project
 │         ├── columns: "?column?":4!null
 │         ├── cardinality: [0 - 5]
 │         ├── immutable
 │         ├── select
 │         │    ├── columns: i:3!null
 │         │    ├── cardinality: [0 - 5]
 │         │    ├── with-scan &2 (cte)
 │         │    │    ├── columns: i:3
 │         │    │    ├── mapping:
 │         │    │    │    └──  i:2 => i:3
 │         │    │    └── cardinality: [1 - 5]
 │         │    └── filters
 │         │         └── i:3 <= 10 [outer=(3), constraints=(/3: (/NULL - /10]; tight)]
 │         └── projections
 │              └── i:3 + 1 [as="?column?":4, outer=(3), immutable]
 └── projections
      └── i:2 [as=i:5, outer=(2)]

# Example from postgres blog with limit added to the recursive branch.
# https://malisper.me/the-missing-postgres-scan-the-loose-index-scan
norm expect=ApplyLimitToRecursiveCTEScan expect-not=TryAddLimitToRecursiveBranch
WITH RECURSIVE temp (i) AS (
  (SELECT n FROM ints ORDER BY n ASC LIMIT 1)
UNION ALL (
  SELECT n FROM temp,
    LATERAL (
      SELECT n
      FROM ints
      WHERE n > i
      ORDER BY n ASC
      LIMIT 1
    ) sub LIMIT 1
  )
)
SELECT count(*) FROM temp;
----
scalar-group-by
 ├── columns: count:13!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(13)
 ├── recursive-c-t-e
 │    ├── columns: i:5
 │    ├── working table binding: &2
 │    ├── initial columns: n:1
 │    ├── recursive columns: n:7
 │    ├── fake-rel
 │    │    ├── columns: i:5
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(5)
 │    ├── limit
 │    │    ├── columns: n:1
 │    │    ├── internal-ordering: +1
 │    │    ├── cardinality: [0 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1)
 │    │    ├── sort
 │    │    │    ├── columns: n:1
 │    │    │    ├── ordering: +1
 │    │    │    ├── limit hint: 1.00
 │    │    │    └── scan ints
 │    │    │         └── columns: n:1
 │    │    └── 1
 │    └── project
 │         ├── columns: n:7!null
 │         ├── cardinality: [0 - 1]
 │         ├── key: ()
 │         ├── fd: ()-->(7)
 │         └── limit
 │              ├── columns: i:6!null n:7!null rownum:11!null
 │              ├── internal-ordering: +7 opt(6,11)
 │              ├── cardinality: [0 - 1]
 │              ├── key: ()
 │              ├── fd: ()-->(6,7,11)
 │              ├── sort
 │              │    ├── columns: i:6!null n:7!null rownum:11!null
 │              │    ├── fd: ()-->(6,11)
 │              │    ├── ordering: +7 opt(6,11) [actual: +7]
 │              │    ├── limit hint: 1.00
 │              │    └── inner-join (cross)
 │              │         ├── columns: i:6!null n:7!null rownum:11!null
 │              │         ├── multiplicity: left-rows(zero-or-more), right-rows(zero-or-one)
 │              │         ├── fd: ()-->(6,11)
 │              │         ├── ordinality
 │              │         │    ├── columns: i:6 rownum:11!null
 │              │         │    ├── cardinality: [1 - 1]
 │              │         │    ├── key: ()
 │              │         │    ├── fd: ()-->(6,11)
 │              │         │    └── with-scan &2 (temp)
 │              │         │         ├── columns: i:6
 │              │         │         ├── mapping:
 │              │         │         │    └──  i:5 => i:6
 │              │         │         ├── cardinality: [1 - 1]
 │              │         │         ├── key: ()
 │              │         │         └── fd: ()-->(6)
 │              │         ├── scan ints
 │              │         │    └── columns: n:7
 │              │         └── filters
 │              │              └── n:7 > i:6 [outer=(6,7), constraints=(/6: (/NULL - ]; /7: (/NULL - ])]
 │              └── 1
 └── aggregations
      └── count-rows [as=count_rows:13]

# No-op because the optimizer cannot prove that the recursive branch has a
# limit without performing induction from the fact that the anchor branch
# has one row. TryAddLimitToRecursiveBranch is disabled to exercise the case.
norm expect-not=ApplyLimitToRecursiveCTEScan disable=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i) AS (
  (SELECT 1)
  UNION ALL
  (SELECT i+1 FROM cte WHERE i <= 10)
)
SELECT i FROM cte;
----
project
 ├── columns: i:5
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:2
 │    ├── working table binding: &1
 │    ├── initial columns: "?column?":1
 │    ├── recursive columns: "?column?":4
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:2
 │    │    └── cardinality: [1 - ]
 │    ├── values
 │    │    ├── columns: "?column?":1!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1)
 │    │    └── (1,)
 │    └── project
 │         ├── columns: "?column?":4!null
 │         ├── immutable
 │         ├── select
 │         │    ├── columns: i:3!null
 │         │    ├── with-scan &1 (cte)
 │         │    │    ├── columns: i:3
 │         │    │    ├── mapping:
 │         │    │    │    └──  i:2 => i:3
 │         │    │    └── cardinality: [1 - ]
 │         │    └── filters
 │         │         └── i:3 <= 10 [outer=(3), constraints=(/3: (/NULL - /10]; tight)]
 │         └── projections
 │              └── i:3 + 1 [as="?column?":4, outer=(3), immutable]
 └── projections
      └── i:2 [as=i:5, outer=(2)]

# No-op for the same reason as above.
norm expect-not=ApplyLimitToRecursiveCTEScan disable=TryAddLimitToRecursiveBranch
WITH RECURSIVE temp (i) AS (
  (SELECT n FROM ints ORDER BY n ASC LIMIT 1)
UNION ALL (
  SELECT n FROM temp,
    LATERAL (
      SELECT n
      FROM ints
      WHERE n > i
      ORDER BY n ASC
      LIMIT 1
    ) sub
  )
)
SELECT count(*) FROM temp;
----
scalar-group-by
 ├── columns: count:13!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(13)
 ├── recursive-c-t-e
 │    ├── columns: i:5
 │    ├── working table binding: &1
 │    ├── initial columns: n:1
 │    ├── recursive columns: n:7
 │    ├── fake-rel
 │    │    ├── columns: i:5
 │    │    └── cardinality: [1 - ]
 │    ├── limit
 │    │    ├── columns: n:1
 │    │    ├── internal-ordering: +1
 │    │    ├── cardinality: [0 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1)
 │    │    ├── sort
 │    │    │    ├── columns: n:1
 │    │    │    ├── ordering: +1
 │    │    │    ├── limit hint: 1.00
 │    │    │    └── scan ints
 │    │    │         └── columns: n:1
 │    │    └── 1
 │    └── project
 │         ├── columns: n:7!null
 │         └── distinct-on
 │              ├── columns: n:7!null rownum:11!null
 │              ├── grouping columns: rownum:11!null
 │              ├── internal-ordering: +7
 │              ├── key: (11)
 │              ├── fd: (11)-->(7)
 │              ├── sort
 │              │    ├── columns: i:6!null n:7!null rownum:11!null
 │              │    ├── fd: (11)-->(6)
 │              │    ├── ordering: +7
 │              │    └── inner-join (cross)
 │              │         ├── columns: i:6!null n:7!null rownum:11!null
 │              │         ├── fd: (11)-->(6)
 │              │         ├── ordinality
 │              │         │    ├── columns: i:6 rownum:11!null
 │              │         │    ├── cardinality: [1 - ]
 │              │         │    ├── key: (11)
 │              │         │    ├── fd: (11)-->(6)
 │              │         │    └── with-scan &1 (temp)
 │              │         │         ├── columns: i:6
 │              │         │         ├── mapping:
 │              │         │         │    └──  i:5 => i:6
 │              │         │         └── cardinality: [1 - ]
 │              │         ├── scan ints
 │              │         │    └── columns: n:7
 │              │         └── filters
 │              │              └── n:7 > i:6 [outer=(6,7), constraints=(/6: (/NULL - ]; /7: (/NULL - ])]
 │              └── aggregations
 │                   └── first-agg [as=n:7, outer=(7)]
 │                        └── n:7
 └── aggregations
      └── count-rows [as=count_rows:13]

# No-op because the anchor branch is unbounded.
norm expect-not=ApplyLimitToRecursiveCTEScan expect-not=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i) AS (
  (SELECT n FROM ints)
  UNION ALL
  (SELECT i+1 FROM cte WHERE i <= 10)
)
SELECT i FROM cte;
----
project
 ├── columns: i:8
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:5
 │    ├── working table binding: &1
 │    ├── initial columns: n:1
 │    ├── recursive columns: "?column?":7
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:5
 │    │    └── cardinality: [1 - ]
 │    ├── scan ints
 │    │    └── columns: n:1
 │    └── project
 │         ├── columns: "?column?":7!null
 │         ├── immutable
 │         ├── select
 │         │    ├── columns: i:6!null
 │         │    ├── with-scan &1 (cte)
 │         │    │    ├── columns: i:6
 │         │    │    ├── mapping:
 │         │    │    │    └──  i:5 => i:6
 │         │    │    └── cardinality: [1 - ]
 │         │    └── filters
 │         │         └── i:6 <= 10 [outer=(6), constraints=(/6: (/NULL - /10]; tight)]
 │         └── projections
 │              └── i:6 + 1 [as="?column?":7, outer=(6), immutable]
 └── projections
      └── i:5 [as=i:8, outer=(5)]

# No-op because the recursive branch is unbounded.
norm expect-not=ApplyLimitToRecursiveCTEScan expect-not=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i) AS (
  (SELECT 1)
  UNION ALL
  (SELECT n FROM cte INNER JOIN ints ON n > i)
)
SELECT i FROM cte;
----
project
 ├── columns: i:8
 ├── cardinality: [1 - ]
 ├── recursive-c-t-e
 │    ├── columns: i:2
 │    ├── working table binding: &1
 │    ├── initial columns: "?column?":1
 │    ├── recursive columns: n:4
 │    ├── cardinality: [1 - ]
 │    ├── fake-rel
 │    │    ├── columns: i:2
 │    │    └── cardinality: [1 - ]
 │    ├── values
 │    │    ├── columns: "?column?":1!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1)
 │    │    └── (1,)
 │    └── project
 │         ├── columns: n:4!null
 │         └── inner-join (cross)
 │              ├── columns: i:3!null n:4!null
 │              ├── with-scan &1 (cte)
 │              │    ├── columns: i:3
 │              │    ├── mapping:
 │              │    │    └──  i:2 => i:3
 │              │    └── cardinality: [1 - ]
 │              ├── scan ints
 │              │    └── columns: n:4
 │              └── filters
 │                   └── n:4 > i:3 [outer=(3,4), constraints=(/3: (/NULL - ]; /4: (/NULL - ])]
 └── projections
      └── i:2 [as=i:8, outer=(2)]

# Case with Union instead of UnionAll.
norm
WITH RECURSIVE cte (i) AS (
  (VALUES (0), (1), (2))
  UNION
  (SELECT (i + 1) % 4 FROM cte)
)
SELECT * FROM cte
----
project
 ├── columns: i:5
 ├── cardinality: [3 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:2
 │    ├── deduplicate
 │    ├── working table binding: &2
 │    ├── initial columns: column1:1
 │    ├── recursive columns: "?column?":4
 │    ├── cardinality: [3 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:2
 │    │    └── cardinality: [1 - 3]
 │    ├── values
 │    │    ├── columns: column1:1!null
 │    │    ├── cardinality: [3 - 3]
 │    │    ├── (0,)
 │    │    ├── (1,)
 │    │    └── (2,)
 │    └── project
 │         ├── columns: "?column?":4
 │         ├── cardinality: [1 - 3]
 │         ├── immutable
 │         ├── with-scan &2 (cte)
 │         │    ├── columns: i:3
 │         │    ├── mapping:
 │         │    │    └──  i:2 => i:3
 │         │    └── cardinality: [1 - 3]
 │         └── projections
 │              └── (i:3 + 1) % 4 [as="?column?":4, outer=(3), immutable]
 └── projections
      └── i:2 [as=i:5, outer=(2)]

# --------------------------------------------------
# TryAddLimitToRecursiveBranch
# --------------------------------------------------

# Example from postgres blog (no changes made).
# https://malisper.me/the-missing-postgres-scan-the-loose-index-scan
norm expect=TryAddLimitToRecursiveBranch
WITH RECURSIVE temp (i) AS (
  (SELECT n FROM ints ORDER BY n ASC LIMIT 1)
UNION ALL (
  SELECT n FROM temp,
    LATERAL (
      SELECT n
      FROM ints
      WHERE n > i
      ORDER BY n ASC
      LIMIT 1
    ) sub
  )
)
SELECT count(*) FROM temp;
----
scalar-group-by
 ├── columns: count:13!null
 ├── cardinality: [1 - 1]
 ├── key: ()
 ├── fd: ()-->(13)
 ├── recursive-c-t-e
 │    ├── columns: i:5
 │    ├── working table binding: &2
 │    ├── initial columns: n:1
 │    ├── recursive columns: n:7
 │    ├── fake-rel
 │    │    ├── columns: i:5
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(5)
 │    ├── limit
 │    │    ├── columns: n:1
 │    │    ├── internal-ordering: +1
 │    │    ├── cardinality: [0 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1)
 │    │    ├── sort
 │    │    │    ├── columns: n:1
 │    │    │    ├── ordering: +1
 │    │    │    ├── limit hint: 1.00
 │    │    │    └── scan ints
 │    │    │         └── columns: n:1
 │    │    └── 1
 │    └── project
 │         ├── columns: n:7!null
 │         ├── cardinality: [0 - 1]
 │         ├── key: ()
 │         ├── fd: ()-->(7)
 │         └── limit
 │              ├── columns: i:6!null n:7!null rownum:11!null
 │              ├── internal-ordering: +7 opt(6,11)
 │              ├── cardinality: [0 - 1]
 │              ├── key: ()
 │              ├── fd: ()-->(6,7,11)
 │              ├── sort
 │              │    ├── columns: i:6!null n:7!null rownum:11!null
 │              │    ├── fd: ()-->(6,11)
 │              │    ├── ordering: +7 opt(6,11) [actual: +7]
 │              │    ├── limit hint: 1.00
 │              │    └── inner-join (cross)
 │              │         ├── columns: i:6!null n:7!null rownum:11!null
 │              │         ├── multiplicity: left-rows(zero-or-more), right-rows(zero-or-one)
 │              │         ├── fd: ()-->(6,11)
 │              │         ├── ordinality
 │              │         │    ├── columns: i:6 rownum:11!null
 │              │         │    ├── cardinality: [1 - 1]
 │              │         │    ├── key: ()
 │              │         │    ├── fd: ()-->(6,11)
 │              │         │    └── with-scan &2 (temp)
 │              │         │         ├── columns: i:6
 │              │         │         ├── mapping:
 │              │         │         │    └──  i:5 => i:6
 │              │         │         ├── cardinality: [1 - 1]
 │              │         │         ├── key: ()
 │              │         │         └── fd: ()-->(6)
 │              │         ├── scan ints
 │              │         │    └── columns: n:7
 │              │         └── filters
 │              │              └── n:7 > i:6 [outer=(6,7), constraints=(/6: (/NULL - ]; /7: (/NULL - ])]
 │              └── 1
 └── aggregations
      └── count-rows [as=count_rows:13]

# Simple case with a Project and Select.
norm expect=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i) AS (
  (SELECT 1)
  UNION ALL
  (SELECT i+1 FROM cte WHERE i <= 10)
)
SELECT i FROM cte;
----
project
 ├── columns: i:5
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:2
 │    ├── working table binding: &2
 │    ├── initial columns: "?column?":1
 │    ├── recursive columns: "?column?":4
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:2
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(2)
 │    ├── values
 │    │    ├── columns: "?column?":1!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1)
 │    │    └── (1,)
 │    └── project
 │         ├── columns: "?column?":4!null
 │         ├── cardinality: [0 - 1]
 │         ├── immutable
 │         ├── key: ()
 │         ├── fd: ()-->(4)
 │         ├── select
 │         │    ├── columns: i:3!null
 │         │    ├── cardinality: [0 - 1]
 │         │    ├── key: ()
 │         │    ├── fd: ()-->(3)
 │         │    ├── with-scan &2 (cte)
 │         │    │    ├── columns: i:3
 │         │    │    ├── mapping:
 │         │    │    │    └──  i:2 => i:3
 │         │    │    ├── cardinality: [1 - 1]
 │         │    │    ├── key: ()
 │         │    │    └── fd: ()-->(3)
 │         │    └── filters
 │         │         └── i:3 <= 10 [outer=(3), constraints=(/3: (/NULL - /10]; tight)]
 │         └── projections
 │              └── i:3 + 1 [as="?column?":4, outer=(3), immutable]
 └── projections
      └── i:2 [as=i:5, outer=(2)]

# Case with an ordinality operator.
norm expect=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i, j) AS (
  (SELECT 1, 2)
  UNION ALL
  (SELECT i+1, ordinality FROM (SELECT i FROM cte WHERE i <= 10) WITH ORDINALITY)
)
SELECT i, j FROM cte;
----
project
 ├── columns: i:9 j:10
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:3 j:4
 │    ├── working table binding: &2
 │    ├── initial columns: "?column?":1 "?column?":2
 │    ├── recursive columns: "?column?":8 ordinality:7
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:3 j:4
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(3,4)
 │    ├── values
 │    │    ├── columns: "?column?":1!null "?column?":2!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1,2)
 │    │    └── (1, 2)
 │    └── project
 │         ├── columns: "?column?":8!null ordinality:7!null
 │         ├── cardinality: [0 - 1]
 │         ├── immutable
 │         ├── key: ()
 │         ├── fd: ()-->(7,8)
 │         ├── ordinality
 │         │    ├── columns: i:5!null ordinality:7!null
 │         │    ├── cardinality: [0 - 1]
 │         │    ├── key: ()
 │         │    ├── fd: ()-->(5,7)
 │         │    └── select
 │         │         ├── columns: i:5!null
 │         │         ├── cardinality: [0 - 1]
 │         │         ├── key: ()
 │         │         ├── fd: ()-->(5)
 │         │         ├── with-scan &2 (cte)
 │         │         │    ├── columns: i:5
 │         │         │    ├── mapping:
 │         │         │    │    └──  i:3 => i:5
 │         │         │    ├── cardinality: [1 - 1]
 │         │         │    ├── key: ()
 │         │         │    └── fd: ()-->(5)
 │         │         └── filters
 │         │              └── i:5 <= 10 [outer=(5), constraints=(/5: (/NULL - /10]; tight)]
 │         └── projections
 │              └── i:5 + 1 [as="?column?":8, outer=(5), immutable]
 └── projections
      ├── i:3 [as=i:9, outer=(3)]
      └── j:4 [as=j:10, outer=(4)]

# Case with a window function.
norm expect=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i, j) AS (
  (SELECT 1, 2::numeric)
  UNION ALL
  (SELECT i+1, sum(i) OVER (ORDER BY j) FROM cte WHERE i <= 10)
)
SELECT i, j FROM cte;
----
project
 ├── columns: i:9 j:10
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:3 j:4
 │    ├── working table binding: &2
 │    ├── initial columns: "?column?":1 numeric:2
 │    ├── recursive columns: "?column?":8 sum:7
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:3 j:4
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(3,4)
 │    ├── values
 │    │    ├── columns: "?column?":1!null numeric:2!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1,2)
 │    │    └── (1, 2)
 │    └── project
 │         ├── columns: "?column?":8!null sum:7
 │         ├── cardinality: [0 - 1]
 │         ├── immutable
 │         ├── key: ()
 │         ├── fd: ()-->(7,8)
 │         ├── window partition=()
 │         │    ├── columns: i:5!null sum:7
 │         │    ├── cardinality: [0 - 1]
 │         │    ├── key: ()
 │         │    ├── fd: ()-->(5,7)
 │         │    ├── select
 │         │    │    ├── columns: i:5!null
 │         │    │    ├── cardinality: [0 - 1]
 │         │    │    ├── key: ()
 │         │    │    ├── fd: ()-->(5)
 │         │    │    ├── with-scan &2 (cte)
 │         │    │    │    ├── columns: i:5
 │         │    │    │    ├── mapping:
 │         │    │    │    │    └──  i:3 => i:5
 │         │    │    │    ├── cardinality: [1 - 1]
 │         │    │    │    ├── key: ()
 │         │    │    │    └── fd: ()-->(5)
 │         │    │    └── filters
 │         │    │         └── i:5 <= 10 [outer=(5), constraints=(/5: (/NULL - /10]; tight)]
 │         │    └── windows
 │         │         └── sum [as=sum:7, outer=(5)]
 │         │              └── i:5
 │         └── projections
 │              └── i:5 + 1 [as="?column?":8, outer=(5), immutable]
 └── projections
      ├── i:3 [as=i:9, outer=(3)]
      └── j:4 [as=j:10, outer=(4)]

# Case with DistinctOn.
norm expect=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i) AS (
  (SELECT 1)
  UNION ALL
  (SELECT DISTINCT i+1 FROM cte WHERE i <= 10)
)
SELECT i FROM cte;
----
project
 ├── columns: i:5
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:2
 │    ├── working table binding: &2
 │    ├── initial columns: "?column?":1
 │    ├── recursive columns: "?column?":4
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:2
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(2)
 │    ├── values
 │    │    ├── columns: "?column?":1!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1)
 │    │    └── (1,)
 │    └── project
 │         ├── columns: "?column?":4!null
 │         ├── cardinality: [0 - 1]
 │         ├── immutable
 │         ├── key: ()
 │         ├── fd: ()-->(4)
 │         ├── select
 │         │    ├── columns: i:3!null
 │         │    ├── cardinality: [0 - 1]
 │         │    ├── key: ()
 │         │    ├── fd: ()-->(3)
 │         │    ├── with-scan &2 (cte)
 │         │    │    ├── columns: i:3
 │         │    │    ├── mapping:
 │         │    │    │    └──  i:2 => i:3
 │         │    │    ├── cardinality: [1 - 1]
 │         │    │    ├── key: ()
 │         │    │    └── fd: ()-->(3)
 │         │    └── filters
 │         │         └── i:3 <= 10 [outer=(3), constraints=(/3: (/NULL - /10]; tight)]
 │         └── projections
 │              └── i:3 + 1 [as="?column?":4, outer=(3), immutable]
 └── projections
      └── i:2 [as=i:5, outer=(2)]

# Case with GroupBy.
norm expect=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i, j) AS (
  (SELECT 1, 2)
  UNION ALL
  (SELECT i+1, sum(j)::int FROM cte WHERE i <= 10 GROUP BY i)
)
SELECT i, j FROM cte;
----
project
 ├── columns: i:10 j:11
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:3 j:4
 │    ├── working table binding: &2
 │    ├── initial columns: "?column?":1 "?column?":2
 │    ├── recursive columns: "?column?":8 sum:9
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:3 j:4
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(3,4)
 │    ├── values
 │    │    ├── columns: "?column?":1!null "?column?":2!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1,2)
 │    │    └── (1, 2)
 │    └── project
 │         ├── columns: "?column?":8!null sum:9
 │         ├── cardinality: [0 - 1]
 │         ├── immutable
 │         ├── key: ()
 │         ├── fd: ()-->(8,9)
 │         ├── group-by (streaming)
 │         │    ├── columns: i:5!null sum:7
 │         │    ├── cardinality: [0 - 1]
 │         │    ├── key: ()
 │         │    ├── fd: ()-->(5,7)
 │         │    ├── select
 │         │    │    ├── columns: i:5!null j:6
 │         │    │    ├── cardinality: [0 - 1]
 │         │    │    ├── key: ()
 │         │    │    ├── fd: ()-->(5,6)
 │         │    │    ├── with-scan &2 (cte)
 │         │    │    │    ├── columns: i:5 j:6
 │         │    │    │    ├── mapping:
 │         │    │    │    │    ├──  i:3 => i:5
 │         │    │    │    │    └──  j:4 => j:6
 │         │    │    │    ├── cardinality: [1 - 1]
 │         │    │    │    ├── key: ()
 │         │    │    │    └── fd: ()-->(5,6)
 │         │    │    └── filters
 │         │    │         └── i:5 <= 10 [outer=(5), constraints=(/5: (/NULL - /10]; tight)]
 │         │    └── aggregations
 │         │         ├── sum [as=sum:7, outer=(6)]
 │         │         │    └── j:6
 │         │         └── const-agg [as=i:5, outer=(5)]
 │         │              └── i:5
 │         └── projections
 │              ├── i:5 + 1 [as="?column?":8, outer=(5), immutable]
 │              └── sum:7::INT8 [as=sum:9, outer=(7), immutable]
 └── projections
      ├── i:3 [as=i:10, outer=(3)]
      └── j:4 [as=j:11, outer=(4)]

# Case with InnerJoin.
norm expect=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i, j) AS (
  (SELECT 1, 2)
  UNION ALL
  (SELECT i+1, y FROM cte INNER JOIN xy ON j = x WHERE i <= 10)
)
SELECT i, j FROM cte;
----
project
 ├── columns: i:12 j:13
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:3 j:4
 │    ├── working table binding: &2
 │    ├── initial columns: "?column?":1 "?column?":2
 │    ├── recursive columns: "?column?":11 y:8
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:3 j:4
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(3,4)
 │    ├── values
 │    │    ├── columns: "?column?":1!null "?column?":2!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1,2)
 │    │    └── (1, 2)
 │    └── project
 │         ├── columns: "?column?":11!null y:8
 │         ├── cardinality: [0 - 1]
 │         ├── immutable
 │         ├── key: ()
 │         ├── fd: ()-->(8,11)
 │         ├── inner-join (hash)
 │         │    ├── columns: i:5!null j:6!null x:7!null y:8
 │         │    ├── cardinality: [0 - 1]
 │         │    ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-one)
 │         │    ├── key: ()
 │         │    ├── fd: ()-->(5-8), (6)==(7), (7)==(6)
 │         │    ├── select
 │         │    │    ├── columns: i:5!null j:6
 │         │    │    ├── cardinality: [0 - 1]
 │         │    │    ├── key: ()
 │         │    │    ├── fd: ()-->(5,6)
 │         │    │    ├── with-scan &2 (cte)
 │         │    │    │    ├── columns: i:5 j:6
 │         │    │    │    ├── mapping:
 │         │    │    │    │    ├──  i:3 => i:5
 │         │    │    │    │    └──  j:4 => j:6
 │         │    │    │    ├── cardinality: [1 - 1]
 │         │    │    │    ├── key: ()
 │         │    │    │    └── fd: ()-->(5,6)
 │         │    │    └── filters
 │         │    │         └── i:5 <= 10 [outer=(5), constraints=(/5: (/NULL - /10]; tight)]
 │         │    ├── scan xy
 │         │    │    ├── columns: x:7!null y:8
 │         │    │    ├── key: (7)
 │         │    │    └── fd: (7)-->(8)
 │         │    └── filters
 │         │         └── j:6 = x:7 [outer=(6,7), constraints=(/6: (/NULL - ]; /7: (/NULL - ]), fd=(6)==(7), (7)==(6)]
 │         └── projections
 │              └── i:5 + 1 [as="?column?":11, outer=(5), immutable]
 └── projections
      ├── i:3 [as=i:12, outer=(3)]
      └── j:4 [as=j:13, outer=(4)]

# Case with LeftJoin.
norm expect=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i, j) AS (
  (SELECT 1, 2)
  UNION ALL
  (SELECT i+1, y FROM cte LEFT JOIN xy ON j = x WHERE i <= 10)
)
SELECT i, j FROM cte;
----
project
 ├── columns: i:12 j:13
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:3 j:4
 │    ├── working table binding: &2
 │    ├── initial columns: "?column?":1 "?column?":2
 │    ├── recursive columns: "?column?":11 y:8
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:3 j:4
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(3,4)
 │    ├── values
 │    │    ├── columns: "?column?":1!null "?column?":2!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1,2)
 │    │    └── (1, 2)
 │    └── project
 │         ├── columns: "?column?":11!null y:8
 │         ├── cardinality: [0 - 1]
 │         ├── immutable
 │         ├── key: ()
 │         ├── fd: ()-->(8,11)
 │         ├── left-join (hash)
 │         │    ├── columns: i:5!null j:6 x:7 y:8
 │         │    ├── cardinality: [0 - 1]
 │         │    ├── multiplicity: left-rows(exactly-one), right-rows(zero-or-one)
 │         │    ├── key: ()
 │         │    ├── fd: ()-->(5-8)
 │         │    ├── select
 │         │    │    ├── columns: i:5!null j:6
 │         │    │    ├── cardinality: [0 - 1]
 │         │    │    ├── key: ()
 │         │    │    ├── fd: ()-->(5,6)
 │         │    │    ├── with-scan &2 (cte)
 │         │    │    │    ├── columns: i:5 j:6
 │         │    │    │    ├── mapping:
 │         │    │    │    │    ├──  i:3 => i:5
 │         │    │    │    │    └──  j:4 => j:6
 │         │    │    │    ├── cardinality: [1 - 1]
 │         │    │    │    ├── key: ()
 │         │    │    │    └── fd: ()-->(5,6)
 │         │    │    └── filters
 │         │    │         └── i:5 <= 10 [outer=(5), constraints=(/5: (/NULL - /10]; tight)]
 │         │    ├── scan xy
 │         │    │    ├── columns: x:7!null y:8
 │         │    │    ├── key: (7)
 │         │    │    └── fd: (7)-->(8)
 │         │    └── filters
 │         │         └── j:6 = x:7 [outer=(6,7), constraints=(/6: (/NULL - ]; /7: (/NULL - ]), fd=(6)==(7), (7)==(6)]
 │         └── projections
 │              └── i:5 + 1 [as="?column?":11, outer=(5), immutable]
 └── projections
      ├── i:3 [as=i:12, outer=(3)]
      └── j:4 [as=j:13, outer=(4)]

# Case with SemiJoin.
norm expect=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i) AS (
  (SELECT 1)
  UNION ALL
  (SELECT i+1 FROM cte WHERE i <= 10 AND EXISTS (SELECT * FROM xy WHERE i = x))
)
SELECT i FROM cte;
----
project
 ├── columns: i:10
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:2
 │    ├── working table binding: &2
 │    ├── initial columns: "?column?":1
 │    ├── recursive columns: "?column?":9
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:2
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(2)
 │    ├── values
 │    │    ├── columns: "?column?":1!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1)
 │    │    └── (1,)
 │    └── project
 │         ├── columns: "?column?":9!null
 │         ├── cardinality: [0 - 1]
 │         ├── immutable
 │         ├── key: ()
 │         ├── fd: ()-->(9)
 │         ├── semi-join (hash)
 │         │    ├── columns: i:3!null
 │         │    ├── cardinality: [0 - 1]
 │         │    ├── key: ()
 │         │    ├── fd: ()-->(3)
 │         │    ├── select
 │         │    │    ├── columns: i:3!null
 │         │    │    ├── cardinality: [0 - 1]
 │         │    │    ├── key: ()
 │         │    │    ├── fd: ()-->(3)
 │         │    │    ├── with-scan &2 (cte)
 │         │    │    │    ├── columns: i:3
 │         │    │    │    ├── mapping:
 │         │    │    │    │    └──  i:2 => i:3
 │         │    │    │    ├── cardinality: [1 - 1]
 │         │    │    │    ├── key: ()
 │         │    │    │    └── fd: ()-->(3)
 │         │    │    └── filters
 │         │    │         └── i:3 <= 10 [outer=(3), constraints=(/3: (/NULL - /10]; tight)]
 │         │    ├── select
 │         │    │    ├── columns: x:4!null
 │         │    │    ├── key: (4)
 │         │    │    ├── scan xy
 │         │    │    │    ├── columns: x:4!null
 │         │    │    │    └── key: (4)
 │         │    │    └── filters
 │         │    │         └── x:4 <= 10 [outer=(4), constraints=(/4: (/NULL - /10]; tight)]
 │         │    └── filters
 │         │         └── i:3 = x:4 [outer=(3,4), constraints=(/3: (/NULL - ]; /4: (/NULL - ]), fd=(3)==(4), (4)==(3)]
 │         └── projections
 │              └── i:3 + 1 [as="?column?":9, outer=(3), immutable]
 └── projections
      └── i:2 [as=i:10, outer=(2)]

# Case with AntiJoin.
norm expect=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i) AS (
  (SELECT 1)
  UNION ALL
  (SELECT i+1 FROM cte WHERE i <= 10 AND NOT EXISTS (SELECT * FROM xy WHERE i = x))
)
SELECT i FROM cte;
----
project
 ├── columns: i:10
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:2
 │    ├── working table binding: &2
 │    ├── initial columns: "?column?":1
 │    ├── recursive columns: "?column?":9
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:2
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(2)
 │    ├── values
 │    │    ├── columns: "?column?":1!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1)
 │    │    └── (1,)
 │    └── project
 │         ├── columns: "?column?":9!null
 │         ├── cardinality: [0 - 1]
 │         ├── immutable
 │         ├── key: ()
 │         ├── fd: ()-->(9)
 │         ├── anti-join (hash)
 │         │    ├── columns: i:3!null
 │         │    ├── cardinality: [0 - 1]
 │         │    ├── key: ()
 │         │    ├── fd: ()-->(3)
 │         │    ├── select
 │         │    │    ├── columns: i:3!null
 │         │    │    ├── cardinality: [0 - 1]
 │         │    │    ├── key: ()
 │         │    │    ├── fd: ()-->(3)
 │         │    │    ├── with-scan &2 (cte)
 │         │    │    │    ├── columns: i:3
 │         │    │    │    ├── mapping:
 │         │    │    │    │    └──  i:2 => i:3
 │         │    │    │    ├── cardinality: [1 - 1]
 │         │    │    │    ├── key: ()
 │         │    │    │    └── fd: ()-->(3)
 │         │    │    └── filters
 │         │    │         └── i:3 <= 10 [outer=(3), constraints=(/3: (/NULL - /10]; tight)]
 │         │    ├── select
 │         │    │    ├── columns: x:4!null
 │         │    │    ├── key: (4)
 │         │    │    ├── scan xy
 │         │    │    │    ├── columns: x:4!null
 │         │    │    │    └── key: (4)
 │         │    │    └── filters
 │         │    │         └── x:4 <= 10 [outer=(4), constraints=(/4: (/NULL - /10]; tight)]
 │         │    └── filters
 │         │         └── i:3 = x:4 [outer=(3,4), constraints=(/3: (/NULL - ]; /4: (/NULL - ]), fd=(3)==(4), (4)==(3)]
 │         └── projections
 │              └── i:3 + 1 [as="?column?":9, outer=(3), immutable]
 └── projections
      └── i:2 [as=i:10, outer=(2)]

# Case with child CTE.
norm expect=TryAddLimitToRecursiveBranch disable=InlineWith
WITH RECURSIVE cte (i) AS (
  (SELECT 1)
  UNION ALL
  (
    SELECT i+1 FROM (
      WITH nested (x) AS (SELECT x FROM xy)
      SELECT i FROM cte INNER JOIN nested ON i = x
    )
    WHERE i <= 10
  )
)
SELECT i FROM cte;
----
with &2 (nested)
 ├── columns: i:10
 ├── cardinality: [1 - ]
 ├── immutable
 ├── scan xy
 │    ├── columns: xy.x:3!null
 │    └── key: (3)
 └── with &4 (cte)
      ├── columns: i:10
      ├── cardinality: [1 - ]
      ├── immutable
      ├── recursive-c-t-e
      │    ├── columns: i:2
      │    ├── working table binding: &3
      │    ├── initial columns: "?column?":1
      │    ├── recursive columns: "?column?":9
      │    ├── cardinality: [1 - ]
      │    ├── immutable
      │    ├── fake-rel
      │    │    ├── columns: i:2
      │    │    ├── cardinality: [1 - 1]
      │    │    ├── key: ()
      │    │    └── fd: ()-->(2)
      │    ├── values
      │    │    ├── columns: "?column?":1!null
      │    │    ├── cardinality: [1 - 1]
      │    │    ├── key: ()
      │    │    ├── fd: ()-->(1)
      │    │    └── (1,)
      │    └── project
      │         ├── columns: "?column?":9!null
      │         ├── cardinality: [0 - 1]
      │         ├── immutable
      │         ├── key: ()
      │         ├── fd: ()-->(9)
      │         ├── inner-join (hash)
      │         │    ├── columns: i:7!null x:8!null
      │         │    ├── cardinality: [0 - 1]
      │         │    ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-one)
      │         │    ├── key: ()
      │         │    ├── fd: ()-->(7,8), (7)==(8), (8)==(7)
      │         │    ├── select
      │         │    │    ├── columns: i:7!null
      │         │    │    ├── cardinality: [0 - 1]
      │         │    │    ├── key: ()
      │         │    │    ├── fd: ()-->(7)
      │         │    │    ├── with-scan &3 (cte)
      │         │    │    │    ├── columns: i:7
      │         │    │    │    ├── mapping:
      │         │    │    │    │    └──  i:2 => i:7
      │         │    │    │    ├── cardinality: [1 - 1]
      │         │    │    │    ├── key: ()
      │         │    │    │    └── fd: ()-->(7)
      │         │    │    └── filters
      │         │    │         └── i:7 <= 10 [outer=(7), constraints=(/7: (/NULL - /10]; tight)]
      │         │    ├── select
      │         │    │    ├── columns: x:8!null
      │         │    │    ├── key: (8)
      │         │    │    ├── with-scan &2 (nested)
      │         │    │    │    ├── columns: x:8!null
      │         │    │    │    ├── mapping:
      │         │    │    │    │    └──  xy.x:3 => x:8
      │         │    │    │    └── key: (8)
      │         │    │    └── filters
      │         │    │         └── x:8 <= 10 [outer=(8), constraints=(/8: (/NULL - /10]; tight)]
      │         │    └── filters
      │         │         └── i:7 = x:8 [outer=(7,8), constraints=(/7: (/NULL - ]; /8: (/NULL - ]), fd=(7)==(8), (8)==(7)]
      │         └── projections
      │              └── i:7 + 1 [as="?column?":9, outer=(7), immutable]
      └── with-scan &4 (cte)
           ├── columns: i:10
           ├── mapping:
           │    └──  i:2 => i:10
           └── cardinality: [1 - ]

# Case with parent CTE.
norm expect=TryAddLimitToRecursiveBranch disable=InlineWith
WITH parent (x) AS (SELECT x FROM xy)
SELECT * FROM (
  WITH RECURSIVE cte (i) AS (
    (SELECT 1)
    UNION ALL
    (
      SELECT i+1 FROM cte
      INNER JOIN parent ON i = x
      WHERE i <= 10
    )
  )
  SELECT i FROM cte
)
----
with &1 (parent)
 ├── columns: i:10
 ├── cardinality: [1 - ]
 ├── immutable
 ├── scan xy
 │    ├── columns: xy.x:1!null
 │    └── key: (1)
 └── with &4 (cte)
      ├── columns: i:10
      ├── cardinality: [1 - ]
      ├── immutable
      ├── recursive-c-t-e
      │    ├── columns: i:6
      │    ├── working table binding: &3
      │    ├── initial columns: "?column?":5
      │    ├── recursive columns: "?column?":9
      │    ├── cardinality: [1 - ]
      │    ├── immutable
      │    ├── fake-rel
      │    │    ├── columns: i:6
      │    │    ├── cardinality: [1 - 1]
      │    │    ├── key: ()
      │    │    └── fd: ()-->(6)
      │    ├── values
      │    │    ├── columns: "?column?":5!null
      │    │    ├── cardinality: [1 - 1]
      │    │    ├── key: ()
      │    │    ├── fd: ()-->(5)
      │    │    └── (1,)
      │    └── project
      │         ├── columns: "?column?":9!null
      │         ├── cardinality: [0 - 1]
      │         ├── immutable
      │         ├── key: ()
      │         ├── fd: ()-->(9)
      │         ├── inner-join (hash)
      │         │    ├── columns: i:7!null x:8!null
      │         │    ├── cardinality: [0 - 1]
      │         │    ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-one)
      │         │    ├── key: ()
      │         │    ├── fd: ()-->(7,8), (7)==(8), (8)==(7)
      │         │    ├── select
      │         │    │    ├── columns: i:7!null
      │         │    │    ├── cardinality: [0 - 1]
      │         │    │    ├── key: ()
      │         │    │    ├── fd: ()-->(7)
      │         │    │    ├── with-scan &3 (cte)
      │         │    │    │    ├── columns: i:7
      │         │    │    │    ├── mapping:
      │         │    │    │    │    └──  i:6 => i:7
      │         │    │    │    ├── cardinality: [1 - 1]
      │         │    │    │    ├── key: ()
      │         │    │    │    └── fd: ()-->(7)
      │         │    │    └── filters
      │         │    │         └── i:7 <= 10 [outer=(7), constraints=(/7: (/NULL - /10]; tight)]
      │         │    ├── select
      │         │    │    ├── columns: x:8!null
      │         │    │    ├── key: (8)
      │         │    │    ├── with-scan &1 (parent)
      │         │    │    │    ├── columns: x:8!null
      │         │    │    │    ├── mapping:
      │         │    │    │    │    └──  xy.x:1 => x:8
      │         │    │    │    └── key: (8)
      │         │    │    └── filters
      │         │    │         └── x:8 <= 10 [outer=(8), constraints=(/8: (/NULL - /10]; tight)]
      │         │    └── filters
      │         │         └── i:7 = x:8 [outer=(7,8), constraints=(/7: (/NULL - ]; /8: (/NULL - ]), fd=(7)==(8), (8)==(7)]
      │         └── projections
      │              └── i:7 + 1 [as="?column?":9, outer=(7), immutable]
      └── with-scan &4 (cte)
           ├── columns: i:10
           ├── mapping:
           │    └──  i:6 => i:10
           └── cardinality: [1 - ]

# Case where the DistinctOn de-duplicates the output of the join.
norm expect=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i, j) AS (
  (SELECT 1, 2)
  UNION ALL
  (SELECT DISTINCT ON (i) i+1, x FROM cte INNER JOIN xy ON j = y WHERE i <= 10)
)
SELECT i, j FROM cte;
----
project
 ├── columns: i:12 j:13
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:3 j:4
 │    ├── working table binding: &2
 │    ├── initial columns: "?column?":1 "?column?":2
 │    ├── recursive columns: "?column?":11 x:7
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:3 j:4
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(3,4)
 │    ├── values
 │    │    ├── columns: "?column?":1!null "?column?":2!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1,2)
 │    │    └── (1, 2)
 │    └── project
 │         ├── columns: "?column?":11!null x:7!null
 │         ├── cardinality: [0 - 1]
 │         ├── immutable
 │         ├── key: ()
 │         ├── fd: ()-->(7,11)
 │         ├── limit
 │         │    ├── columns: i:5!null j:6!null x:7!null y:8!null
 │         │    ├── cardinality: [0 - 1]
 │         │    ├── key: ()
 │         │    ├── fd: ()-->(5-8), (6)==(8), (8)==(6)
 │         │    ├── inner-join (hash)
 │         │    │    ├── columns: i:5!null j:6!null x:7!null y:8!null
 │         │    │    ├── multiplicity: left-rows(zero-or-more), right-rows(zero-or-one)
 │         │    │    ├── key: (7)
 │         │    │    ├── fd: ()-->(5,6,8), (6)==(8), (8)==(6)
 │         │    │    ├── limit hint: 1.00
 │         │    │    ├── select
 │         │    │    │    ├── columns: i:5!null j:6
 │         │    │    │    ├── cardinality: [0 - 1]
 │         │    │    │    ├── key: ()
 │         │    │    │    ├── fd: ()-->(5,6)
 │         │    │    │    ├── with-scan &2 (cte)
 │         │    │    │    │    ├── columns: i:5 j:6
 │         │    │    │    │    ├── mapping:
 │         │    │    │    │    │    ├──  i:3 => i:5
 │         │    │    │    │    │    └──  j:4 => j:6
 │         │    │    │    │    ├── cardinality: [1 - 1]
 │         │    │    │    │    ├── key: ()
 │         │    │    │    │    └── fd: ()-->(5,6)
 │         │    │    │    └── filters
 │         │    │    │         └── i:5 <= 10 [outer=(5), constraints=(/5: (/NULL - /10]; tight)]
 │         │    │    ├── scan xy
 │         │    │    │    ├── columns: x:7!null y:8
 │         │    │    │    ├── key: (7)
 │         │    │    │    └── fd: (7)-->(8)
 │         │    │    └── filters
 │         │    │         └── j:6 = y:8 [outer=(6,8), constraints=(/6: (/NULL - ]; /8: (/NULL - ]), fd=(6)==(8), (8)==(6)]
 │         │    └── 1
 │         └── projections
 │              └── i:5 + 1 [as="?column?":11, outer=(5), immutable]
 └── projections
      ├── i:3 [as=i:12, outer=(3)]
      └── j:4 [as=j:13, outer=(4)]

# Case where the GroupBy de-duplicates the output of the join.
norm expect=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i, j) AS (
  (SELECT 1, 2)
  UNION ALL
  (SELECT i+1, sum(x)::int FROM cte INNER JOIN xy ON j = y WHERE i <= 10 GROUP BY i)
)
SELECT i, j FROM cte;
----
project
 ├── columns: i:14 j:15
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:3 j:4
 │    ├── working table binding: &2
 │    ├── initial columns: "?column?":1 "?column?":2
 │    ├── recursive columns: "?column?":12 sum:13
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:3 j:4
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(3,4)
 │    ├── values
 │    │    ├── columns: "?column?":1!null "?column?":2!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1,2)
 │    │    └── (1, 2)
 │    └── project
 │         ├── columns: "?column?":12!null sum:13!null
 │         ├── cardinality: [0 - 1]
 │         ├── immutable
 │         ├── key: ()
 │         ├── fd: ()-->(12,13)
 │         ├── group-by (streaming)
 │         │    ├── columns: i:5!null sum:11!null
 │         │    ├── cardinality: [0 - 1]
 │         │    ├── key: ()
 │         │    ├── fd: ()-->(5,11)
 │         │    ├── inner-join (hash)
 │         │    │    ├── columns: i:5!null j:6!null x:7!null y:8!null
 │         │    │    ├── multiplicity: left-rows(zero-or-more), right-rows(zero-or-one)
 │         │    │    ├── key: (7)
 │         │    │    ├── fd: ()-->(5,6,8), (6)==(8), (8)==(6)
 │         │    │    ├── select
 │         │    │    │    ├── columns: i:5!null j:6
 │         │    │    │    ├── cardinality: [0 - 1]
 │         │    │    │    ├── key: ()
 │         │    │    │    ├── fd: ()-->(5,6)
 │         │    │    │    ├── with-scan &2 (cte)
 │         │    │    │    │    ├── columns: i:5 j:6
 │         │    │    │    │    ├── mapping:
 │         │    │    │    │    │    ├──  i:3 => i:5
 │         │    │    │    │    │    └──  j:4 => j:6
 │         │    │    │    │    ├── cardinality: [1 - 1]
 │         │    │    │    │    ├── key: ()
 │         │    │    │    │    └── fd: ()-->(5,6)
 │         │    │    │    └── filters
 │         │    │    │         └── i:5 <= 10 [outer=(5), constraints=(/5: (/NULL - /10]; tight)]
 │         │    │    ├── scan xy
 │         │    │    │    ├── columns: x:7!null y:8
 │         │    │    │    ├── key: (7)
 │         │    │    │    └── fd: (7)-->(8)
 │         │    │    └── filters
 │         │    │         └── j:6 = y:8 [outer=(6,8), constraints=(/6: (/NULL - ]; /8: (/NULL - ]), fd=(6)==(8), (8)==(6)]
 │         │    └── aggregations
 │         │         ├── sum [as=sum:11, outer=(7)]
 │         │         │    └── x:7
 │         │         └── const-agg [as=i:5, outer=(5)]
 │         │              └── i:5
 │         └── projections
 │              ├── i:5 + 1 [as="?column?":12, outer=(5), immutable]
 │              └── sum:11::INT8 [as=sum:13, outer=(11), immutable]
 └── projections
      ├── i:3 [as=i:14, outer=(3)]
      └── j:4 [as=j:15, outer=(4)]

# No-op because the recursive branch already has a limit.
norm expect-not=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i) AS (
  (SELECT 1)
  UNION ALL
  (SELECT i+1 FROM cte WHERE i <= 10 LIMIT 1)
)
SELECT i FROM cte;
----
project
 ├── columns: i:5
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:2
 │    ├── working table binding: &2
 │    ├── initial columns: "?column?":1
 │    ├── recursive columns: "?column?":4
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:2
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── fd: ()-->(2)
 │    ├── values
 │    │    ├── columns: "?column?":1!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1)
 │    │    └── (1,)
 │    └── project
 │         ├── columns: "?column?":4!null
 │         ├── cardinality: [0 - 1]
 │         ├── immutable
 │         ├── key: ()
 │         ├── fd: ()-->(4)
 │         ├── select
 │         │    ├── columns: i:3!null
 │         │    ├── cardinality: [0 - 1]
 │         │    ├── key: ()
 │         │    ├── fd: ()-->(3)
 │         │    ├── with-scan &2 (cte)
 │         │    │    ├── columns: i:3
 │         │    │    ├── mapping:
 │         │    │    │    └──  i:2 => i:3
 │         │    │    ├── cardinality: [1 - 1]
 │         │    │    ├── key: ()
 │         │    │    └── fd: ()-->(3)
 │         │    └── filters
 │         │         └── i:3 <= 10 [outer=(3), constraints=(/3: (/NULL - /10]; tight)]
 │         └── projections
 │              └── i:3 + 1 [as="?column?":4, outer=(3), immutable]
 └── projections
      └── i:2 [as=i:5, outer=(2)]

# No-op case where the join can duplicate rows.
norm expect-not=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i, j) AS (
  (SELECT 1, 2)
  UNION ALL
  (SELECT i+1, y FROM cte INNER JOIN xy ON j = y WHERE i <= 10)
)
SELECT i, j FROM cte;
----
project
 ├── columns: i:12 j:13
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:3 j:4
 │    ├── working table binding: &1
 │    ├── initial columns: "?column?":1 "?column?":2
 │    ├── recursive columns: "?column?":11 y:8
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:3 j:4
 │    │    └── cardinality: [1 - ]
 │    ├── values
 │    │    ├── columns: "?column?":1!null "?column?":2!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1,2)
 │    │    └── (1, 2)
 │    └── project
 │         ├── columns: "?column?":11!null y:8!null
 │         ├── immutable
 │         ├── inner-join (hash)
 │         │    ├── columns: i:5!null j:6!null y:8!null
 │         │    ├── fd: (6)==(8), (8)==(6)
 │         │    ├── select
 │         │    │    ├── columns: i:5!null j:6
 │         │    │    ├── with-scan &1 (cte)
 │         │    │    │    ├── columns: i:5 j:6
 │         │    │    │    ├── mapping:
 │         │    │    │    │    ├──  i:3 => i:5
 │         │    │    │    │    └──  j:4 => j:6
 │         │    │    │    └── cardinality: [1 - ]
 │         │    │    └── filters
 │         │    │         └── i:5 <= 10 [outer=(5), constraints=(/5: (/NULL - /10]; tight)]
 │         │    ├── scan xy
 │         │    │    └── columns: y:8
 │         │    └── filters
 │         │         └── j:6 = y:8 [outer=(6,8), constraints=(/6: (/NULL - ]; /8: (/NULL - ]), fd=(6)==(8), (8)==(6)]
 │         └── projections
 │              └── i:5 + 1 [as="?column?":11, outer=(5), immutable]
 └── projections
      ├── i:3 [as=i:12, outer=(3)]
      └── j:4 [as=j:13, outer=(4)]

# No-op case where the join can duplicate rows and the DistinctOn does not
# de-duplicate the output of the join.
norm expect-not=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i, j) AS (
  (SELECT 1, 2)
  UNION ALL
  (SELECT DISTINCT ON (y) i+1, x FROM cte INNER JOIN xy ON j = y WHERE i <= 10)
)
SELECT i, j FROM cte;
----
project
 ├── columns: i:12 j:13
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:3 j:4
 │    ├── working table binding: &1
 │    ├── initial columns: "?column?":1 "?column?":2
 │    ├── recursive columns: "?column?":11 x:7
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:3 j:4
 │    │    └── cardinality: [1 - ]
 │    ├── values
 │    │    ├── columns: "?column?":1!null "?column?":2!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1,2)
 │    │    └── (1, 2)
 │    └── project
 │         ├── columns: x:7!null "?column?":11!null
 │         ├── immutable
 │         ├── key: (7)
 │         ├── fd: (7)-->(11)
 │         └── distinct-on
 │              ├── columns: x:7!null y:8!null "?column?":11!null
 │              ├── grouping columns: y:8!null
 │              ├── immutable
 │              ├── key: (8)
 │              ├── fd: (7)-->(8), (8)-->(7,11)
 │              ├── project
 │              │    ├── columns: "?column?":11!null x:7!null y:8!null
 │              │    ├── immutable
 │              │    ├── fd: (7)-->(8)
 │              │    ├── inner-join (hash)
 │              │    │    ├── columns: i:5!null j:6!null x:7!null y:8!null
 │              │    │    ├── fd: (7)-->(8), (6)==(8), (8)==(6)
 │              │    │    ├── select
 │              │    │    │    ├── columns: i:5!null j:6
 │              │    │    │    ├── with-scan &1 (cte)
 │              │    │    │    │    ├── columns: i:5 j:6
 │              │    │    │    │    ├── mapping:
 │              │    │    │    │    │    ├──  i:3 => i:5
 │              │    │    │    │    │    └──  j:4 => j:6
 │              │    │    │    │    └── cardinality: [1 - ]
 │              │    │    │    └── filters
 │              │    │    │         └── i:5 <= 10 [outer=(5), constraints=(/5: (/NULL - /10]; tight)]
 │              │    │    ├── scan xy
 │              │    │    │    ├── columns: x:7!null y:8
 │              │    │    │    ├── key: (7)
 │              │    │    │    └── fd: (7)-->(8)
 │              │    │    └── filters
 │              │    │         └── j:6 = y:8 [outer=(6,8), constraints=(/6: (/NULL - ]; /8: (/NULL - ]), fd=(6)==(8), (8)==(6)]
 │              │    └── projections
 │              │         └── i:5 + 1 [as="?column?":11, outer=(5), immutable]
 │              └── aggregations
 │                   ├── first-agg [as="?column?":11, outer=(11)]
 │                   │    └── "?column?":11
 │                   └── first-agg [as=x:7, outer=(7)]
 │                        └── x:7
 └── projections
      ├── i:3 [as=i:12, outer=(3)]
      └── j:4 [as=j:13, outer=(4)]

# No-op case with the recursive scan in the right input of a LeftJoin.
norm expect-not=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i, j) AS (
  (SELECT 1, 2)
  UNION ALL
  (SELECT i+1, y FROM xy LEFT JOIN cte ON j = x AND i <= 10)
)
SELECT i, j FROM cte;
----
project
 ├── columns: i:12 j:13
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:3 j:4
 │    ├── working table binding: &1
 │    ├── initial columns: "?column?":1 "?column?":2
 │    ├── recursive columns: "?column?":11 y:6
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:3 j:4
 │    │    └── cardinality: [1 - ]
 │    ├── values
 │    │    ├── columns: "?column?":1!null "?column?":2!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1,2)
 │    │    └── (1, 2)
 │    └── project
 │         ├── columns: "?column?":11 y:6
 │         ├── immutable
 │         ├── left-join (hash)
 │         │    ├── columns: x:5!null y:6 i:9 j:10
 │         │    ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-one)
 │         │    ├── fd: (5)-->(6)
 │         │    ├── scan xy
 │         │    │    ├── columns: x:5!null y:6
 │         │    │    ├── key: (5)
 │         │    │    └── fd: (5)-->(6)
 │         │    ├── select
 │         │    │    ├── columns: i:9!null j:10
 │         │    │    ├── with-scan &1 (cte)
 │         │    │    │    ├── columns: i:9 j:10
 │         │    │    │    ├── mapping:
 │         │    │    │    │    ├──  i:3 => i:9
 │         │    │    │    │    └──  j:4 => j:10
 │         │    │    │    └── cardinality: [1 - ]
 │         │    │    └── filters
 │         │    │         └── i:9 <= 10 [outer=(9), constraints=(/9: (/NULL - /10]; tight)]
 │         │    └── filters
 │         │         └── j:10 = x:5 [outer=(5,10), constraints=(/5: (/NULL - ]; /10: (/NULL - ]), fd=(5)==(10), (10)==(5)]
 │         └── projections
 │              └── i:9 + 1 [as="?column?":11, outer=(9), immutable]
 └── projections
      ├── i:3 [as=i:12, outer=(3)]
      └── j:4 [as=j:13, outer=(4)]

# No-op case with a FullJoin.
norm expect-not=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i, j) AS (
  (SELECT 1, 2)
  UNION ALL
  (SELECT i+1, y FROM cte FULL JOIN xy ON j = x AND i <= 10)
)
SELECT i, j FROM cte;
----
project
 ├── columns: i:12 j:13
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:3 j:4
 │    ├── working table binding: &1
 │    ├── initial columns: "?column?":1 "?column?":2
 │    ├── recursive columns: "?column?":11 y:8
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:3 j:4
 │    │    └── cardinality: [1 - ]
 │    ├── values
 │    │    ├── columns: "?column?":1!null "?column?":2!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1,2)
 │    │    └── (1, 2)
 │    └── project
 │         ├── columns: "?column?":11 y:8
 │         ├── cardinality: [1 - ]
 │         ├── immutable
 │         ├── full-join (hash)
 │         │    ├── columns: i:5 j:6 x:7 y:8
 │         │    ├── cardinality: [1 - ]
 │         │    ├── multiplicity: left-rows(exactly-one), right-rows(one-or-more)
 │         │    ├── fd: (7)-->(8)
 │         │    ├── with-scan &1 (cte)
 │         │    │    ├── columns: i:5 j:6
 │         │    │    ├── mapping:
 │         │    │    │    ├──  i:3 => i:5
 │         │    │    │    └──  j:4 => j:6
 │         │    │    └── cardinality: [1 - ]
 │         │    ├── scan xy
 │         │    │    ├── columns: x:7!null y:8
 │         │    │    ├── key: (7)
 │         │    │    └── fd: (7)-->(8)
 │         │    └── filters
 │         │         ├── j:6 = x:7 [outer=(6,7), constraints=(/6: (/NULL - ]; /7: (/NULL - ]), fd=(6)==(7), (7)==(6)]
 │         │         └── i:5 <= 10 [outer=(5), constraints=(/5: (/NULL - /10]; tight)]
 │         └── projections
 │              └── i:5 + 1 [as="?column?":11, outer=(5), immutable]
 └── projections
      ├── i:3 [as=i:12, outer=(3)]
      └── j:4 [as=j:13, outer=(4)]

# Case with a limit. No-op because the limit causes the recursive branch to
# have an upper bound. Note that we could get a better upper bound by
# matching TryAddLimitToRecursiveBranch if the anchor limit is smaller than
# the recursive branch limit, but it doesn't seem useful enough to justify
# the added complexity.
norm expect-not=TryAddLimitToRecursiveBranch
WITH RECURSIVE cte (i) AS (
  (SELECT 1)
  UNION ALL
  (SELECT i+1 FROM cte WHERE i <= 10 ORDER BY i LIMIT 5)
)
SELECT i FROM cte;
----
project
 ├── columns: i:5
 ├── cardinality: [1 - ]
 ├── immutable
 ├── recursive-c-t-e
 │    ├── columns: i:2
 │    ├── working table binding: &2
 │    ├── initial columns: "?column?":1
 │    ├── recursive columns: "?column?":4
 │    ├── cardinality: [1 - ]
 │    ├── immutable
 │    ├── fake-rel
 │    │    ├── columns: i:2
 │    │    └── cardinality: [1 - 5]
 │    ├── values
 │    │    ├── columns: "?column?":1!null
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    ├── fd: ()-->(1)
 │    │    └── (1,)
 │    └── project
 │         ├── columns: "?column?":4!null
 │         ├── cardinality: [0 - 5]
 │         ├── immutable
 │         ├── select
 │         │    ├── columns: i:3!null
 │         │    ├── cardinality: [0 - 5]
 │         │    ├── with-scan &2 (cte)
 │         │    │    ├── columns: i:3
 │         │    │    ├── mapping:
 │         │    │    │    └──  i:2 => i:3
 │         │    │    └── cardinality: [1 - 5]
 │         │    └── filters
 │         │         └── i:3 <= 10 [outer=(3), constraints=(/3: (/NULL - /10]; tight)]
 │         └── projections
 │              └── i:3 + 1 [as="?column?":4, outer=(3), immutable]
 └── projections
      └── i:2 [as=i:5, outer=(2)]

exec-ddl
CREATE TABLE yz (y INT, z INT, INDEX (y) STORING (z));
----

exec-ddl
ALTER TABLE yz INJECT STATISTICS '[
  {
    "columns": ["y"],
    "created_at": "2018-01-01 1:00:00.00000+00:00",
    "row_count": 100000,
    "distinct_count": 100000
  },
  {
    "columns": ["z"],
    "created_at": "2018-01-01 1:00:00.00000+00:00",
    "row_count": 100000,
    "distinct_count": 1
  }
]';
----

# Regression test for #99389.
assign-placeholders-norm query-args=(1) format=show-stats
WITH RECURSIVE cte(a,b) AS (
  (SELECT * FROM yz WHERE y = $1)
  UNION ALL
  (SELECT y+1, z FROM yz LEFT JOIN cte ON b = z)
)
SELECT * FROM cte;
----
project
 ├── columns: a:16 b:17
 ├── immutable
 ├── stats: [rows=10]
 ├── recursive-c-t-e
 │    ├── columns: a:6 b:7
 │    ├── working table binding: &1
 │    ├── initial columns: y:1 z:2
 │    ├── recursive columns: "?column?":15 z:9
 │    ├── immutable
 │    ├── stats: [rows=10]
 │    ├── fake-rel
 │    │    ├── columns: a:6 b:7
 │    │    ├── cardinality: [1 - ]
 │    │    └── stats: [rows=1.00001, distinct(7)=0.100001, null(7)=0.0100001]
 │    ├── select
 │    │    ├── columns: y:1!null z:2
 │    │    ├── stats: [rows=1.00001, distinct(1)=1, null(1)=0]
 │    │    ├── fd: ()-->(1)
 │    │    ├── scan yz
 │    │    │    ├── columns: y:1 z:2
 │    │    │    └── stats: [rows=100000, distinct(1)=100000, null(1)=0]
 │    │    └── filters
 │    │         └── y:1 = 1 [outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
 │    └── project
 │         ├── columns: "?column?":15 z:9
 │         ├── immutable
 │         ├── stats: [rows=100001]
 │         ├── left-join (hash)
 │         │    ├── columns: y:8 z:9 b:14
 │         │    ├── stats: [rows=100001, distinct(14)=1, null(14)=0]
 │         │    ├── scan yz
 │         │    │    ├── columns: y:8 z:9
 │         │    │    └── stats: [rows=100000, distinct(9)=1, null(9)=0]
 │         │    ├── with-scan &1 (cte)
 │         │    │    ├── columns: b:14
 │         │    │    ├── mapping:
 │         │    │    │    └──  b:7 => b:14
 │         │    │    ├── cardinality: [1 - ]
 │         │    │    └── stats: [rows=1.00001, distinct(14)=0.100001, null(14)=0.0100001]
 │         │    └── filters
 │         │         └── b:14 = z:9 [outer=(9,14), constraints=(/9: (/NULL - ]; /14: (/NULL - ]), fd=(9)==(14), (14)==(9)]
 │         └── projections
 │              └── y:8 + 1 [as="?column?":15, outer=(8), immutable]
 └── projections
      ├── a:6 [as=a:16, outer=(6)]
      └── b:7 [as=b:17, outer=(7)]
