exec-ddl
CREATE TABLE kv (k INT PRIMARY KEY, v INT)
----

exec-ddl
CREATE TABLE abcd (a INT, b INT, c INT, d INT, PRIMARY KEY (a,b,c))
----

assign-placeholders-norm query-args=(1)
SELECT v FROM kv WHERE k = $1
----
project
 ├── columns: v:2
 ├── cardinality: [0 - 1]
 ├── key: ()
 ├── fd: ()-->(2)
 └── select
      ├── columns: k:1!null v:2
      ├── cardinality: [0 - 1]
      ├── key: ()
      ├── fd: ()-->(1,2)
      ├── scan kv
      │    ├── columns: k:1!null v:2
      │    ├── key: (1)
      │    └── fd: (1)-->(2)
      └── filters
           └── k:1 = 1 [outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]

assign-placeholders-opt query-args=(1)
SELECT v FROM kv WHERE k = $1
----
project
 ├── columns: v:2
 ├── cardinality: [0 - 1]
 ├── key: ()
 ├── fd: ()-->(2)
 └── scan kv
      ├── columns: k:1!null v:2
      ├── constraint: /1: [/1 - /1]
      ├── cardinality: [0 - 1]
      ├── key: ()
      └── fd: ()-->(1,2)

# This is what we ideally want to obtain after assigning placeholders in the
# test below.
norm
SELECT * FROM abcd WHERE (a, b) IN (
  SELECT unnest('{1}'::INT[]),
         unnest('{2}'::INT[])
)
----
select
 ├── columns: a:1!null b:2!null c:3!null d:4
 ├── key: (3)
 ├── fd: ()-->(1,2), (3)-->(4)
 ├── scan abcd
 │    ├── columns: a:1!null b:2!null c:3!null d:4
 │    ├── key: (1-3)
 │    └── fd: (1-3)-->(4)
 └── filters
      ├── a:1 = 1 [outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
      └── b:2 = 2 [outer=(2), constraints=(/2: [/2 - /2]; tight), fd=()-->(2)]

# The normalized expression above can be explored into a constrained scan.
opt
SELECT * FROM abcd WHERE (a, b) IN (
  SELECT unnest('{1}'::INT[]),
         unnest('{2}'::INT[])
)
----
scan abcd
 ├── columns: a:1!null b:2!null c:3!null d:4
 ├── constraint: /1/2/3: [/1/2 - /1/2]
 ├── key: (3)
 └── fd: ()-->(1,2), (3)-->(4)

assign-placeholders-norm query-args=('{1}','{2}')
SELECT * FROM abcd WHERE (a, b) IN (
  SELECT unnest($1:::STRING::INT[]),
         unnest($2:::STRING::INT[])
)
----
select
 ├── columns: a:1!null b:2!null c:3!null d:4
 ├── key: (3)
 ├── fd: ()-->(1,2), (3)-->(4)
 ├── scan abcd
 │    ├── columns: a:1!null b:2!null c:3!null d:4
 │    ├── key: (1-3)
 │    └── fd: (1-3)-->(4)
 └── filters
      ├── a:1 = 1 [outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]
      └── b:2 = 2 [outer=(2), constraints=(/2: [/2 - /2]; tight), fd=()-->(2)]

# We want this query to be optimized into a constrained scan, just like the
# no-placeholders variant above.
assign-placeholders-opt query-args=('{1}','{2}')
SELECT * FROM abcd WHERE (a, b) IN (
  SELECT unnest($1:::STRING::INT[]),
         unnest($2:::STRING::INT[])
)
----
scan abcd
 ├── columns: a:1!null b:2!null c:3!null d:4
 ├── constraint: /1/2/3: [/1/2 - /1/2]
 ├── key: (3)
 └── fd: ()-->(1,2), (3)-->(4)

# Note: \x2c is a comma; we can't use a comma directly because of the
# datadriven parser.
assign-placeholders-norm query-args=('{1\x2c 2}','{3\x2c 4}')
SELECT * FROM abcd WHERE (a, b) IN (
  SELECT unnest($1:::STRING::INT[]),
         unnest($2:::STRING::INT[])
)
----
semi-join (hash)
 ├── columns: a:1!null b:2!null c:3!null d:4
 ├── stable
 ├── key: (1-3)
 ├── fd: (1-3)-->(4)
 ├── scan abcd
 │    ├── columns: a:1!null b:2!null c:3!null d:4
 │    ├── key: (1-3)
 │    └── fd: (1-3)-->(4)
 ├── project-set
 │    ├── columns: unnest:7 unnest:8
 │    ├── stable
 │    ├── values
 │    │    ├── cardinality: [1 - 1]
 │    │    ├── key: ()
 │    │    └── ()
 │    └── zip
 │         ├── unnest(e'{1\\x2c 2}'::INT8[]) [stable]
 │         └── unnest(e'{3\\x2c 4}'::INT8[]) [stable]
 └── filters
      ├── unnest:7 = a:1 [outer=(1,7), constraints=(/1: (/NULL - ]; /7: (/NULL - ]), fd=(1)==(7), (7)==(1)]
      └── unnest:8 = b:2 [outer=(2,8), constraints=(/2: (/NULL - ]; /8: (/NULL - ]), fd=(2)==(8), (8)==(2)]

assign-placeholders-opt query-args=('{1\x2c 2}','{3\x2c 4}')
SELECT * FROM abcd WHERE (a, b) IN (
  SELECT unnest($1:::STRING::INT[]),
         unnest($2:::STRING::INT[])
)
----
project
 ├── columns: a:1!null b:2!null c:3!null d:4
 ├── stable
 ├── key: (1-3)
 ├── fd: (1-3)-->(4)
 └── inner-join (lookup abcd)
      ├── columns: a:1!null b:2!null c:3!null d:4 unnest:7!null unnest:8!null
      ├── key columns: [7 8] = [1 2]
      ├── stable
      ├── key: (3,7,8)
      ├── fd: (1-3)-->(4), (1)==(7), (7)==(1), (2)==(8), (8)==(2)
      ├── distinct-on
      │    ├── columns: unnest:7 unnest:8
      │    ├── grouping columns: unnest:7 unnest:8
      │    ├── stable
      │    ├── key: (7,8)
      │    └── project-set
      │         ├── columns: unnest:7 unnest:8
      │         ├── stable
      │         ├── values
      │         │    ├── cardinality: [1 - 1]
      │         │    ├── key: ()
      │         │    └── ()
      │         └── zip
      │              ├── unnest(e'{1\\x2c 2}'::INT8[]) [stable]
      │              └── unnest(e'{3\\x2c 4}'::INT8[]) [stable]
      └── filters (true)
