statement ok
CREATE TABLE ab (
  a INT PRIMARY KEY,
  b INT
)

statement ok
CREATE SEQUENCE sq1;

statement ok
CREATE TYPE notmyworkday AS ENUM ('Monday', 'Tuesday');

subtest usage

statement error pgcode 42P13 pq: no language specified
CREATE FUNCTION f() RETURNS INT IMMUTABLE AS $$ SELECT 1 $$;

statement error pgcode 42P13 pq: no function body specified
CREATE FUNCTION f() RETURNS INT IMMUTABLE LANGUAGE SQL;


statement ok
CREATE FUNCTION a(i INT) RETURNS INT LANGUAGE SQL AS 'SELECT i'

statement ok
CREATE FUNCTION b(i INT) RETURNS INT LANGUAGE SQL AS 'SELECT a FROM ab WHERE a = i'

statement ok
CREATE FUNCTION c(i INT, j INT) RETURNS INT LANGUAGE SQL AS 'SELECT i - j'

statement error pgcode 42703 column \"j\" does not exist
CREATE FUNCTION err(i INT) RETURNS INT LANGUAGE SQL AS 'SELECT j'

statement error pgcode 42703 column \"j\" does not exist
CREATE FUNCTION err(i INT) RETURNS INT LANGUAGE SQL AS 'SELECT a FROM ab WHERE a = j'

statement error pgcode 42P01 pq: relation "dne" does not exist
CREATE FUNCTION err() RETURNS INT LANGUAGE SQL AS 'SELECT a FROM dne'

statement ok
CREATE FUNCTION d(i INT2) RETURNS INT4 LANGUAGE SQL AS 'SELECT i'

statement error pgcode 42P13 return type mismatch in function declared to return bool\nDETAIL: Actual return type is int
CREATE FUNCTION err(i INT, j INT) RETURNS BOOL LANGUAGE SQL AS 'SELECT i - j'

statement error pgcode 42P13 return type mismatch in function declared to return int\nDETAIL: Actual return type is bool
CREATE FUNCTION err(b BOOL) RETURNS INT LANGUAGE SQL AS 'SELECT b'

statement error pgcode 42P13 return type mismatch in function declared to return bool\nDETAIL: Actual return type is int
CREATE FUNCTION err(i INT, j INT) RETURNS BOOL LANGUAGE SQL AS 'SELECT i - j'

# Make sure using table name as tuple type name works properly.
# It should pass the return type validation and stored as a tuple type.
statement ok
CREATE TABLE t_implicit_type(a INT PRIMARY KEY, b STRING);

statement error pgcode 42P13 pq: return type mismatch in function declared to return int\nDETAIL: Actual return type is record
CREATE FUNCTION f() RETURNS INT IMMUTABLE LANGUAGE SQL AS $$ SELECT a, b from t_implicit_type $$

# Create function with no references.
statement ok
CREATE FUNCTION f_no_ref(a int) RETURNS INT IMMUTABLE AS 'SELECT 1' LANGUAGE SQL

query T
SELECT create_statement FROM [SHOW CREATE FUNCTION f_no_ref];
----
CREATE FUNCTION public.f_no_ref(a INT8)
  RETURNS INT8
  IMMUTABLE
  NOT LEAKPROOF
  CALLED ON NULL INPUT
  LANGUAGE SQL
  SECURITY INVOKER
  AS $$
  SELECT 1;
$$

# Make sure that names are qualified, references are tracked and sequence
# expression is rewritten.
statement ok
CREATE TABLE t(
a INT PRIMARY KEY,
b INT,
C INT,
INDEX t_idx_b(b),
INDEX t_idx_c(c)
);


statement ok
CREATE FUNCTION f(a notmyworkday) RETURNS INT VOLATILE LANGUAGE SQL AS $$
SELECT a FROM t;
SELECT b FROM t@t_idx_b;
SELECT c FROM t@t_idx_c;
SELECT nextval('sq1');
$$

query T
SELECT create_statement FROM [SHOW CREATE FUNCTION f];
----
CREATE FUNCTION public.f(a public.notmyworkday)
  RETURNS INT8
  VOLATILE
  NOT LEAKPROOF
  CALLED ON NULL INPUT
  LANGUAGE SQL
  SECURITY INVOKER
  AS $$
  SELECT a FROM test.public.t;
  SELECT b FROM test.public.t@t_idx_b;
  SELECT c FROM test.public.t@t_idx_c;
  SELECT nextval('public.sq1'::REGCLASS);
$$

statement error pgcode 0A000 pq: unimplemented: alter function depends on extension not supported.*
ALTER FUNCTION f() DEPENDS ON EXTENSION postgis

subtest end

subtest udf_pg_proc

statement ok
CREATE FUNCTION proc_f(INT) RETURNS INT LANGUAGE SQL AS $$ SELECT 1 $$;

statement ok
CREATE FUNCTION proc_f(STRING, b INT) RETURNS STRING STRICT IMMUTABLE LEAKPROOF LANGUAGE SQL AS $$ SELECT 'hello' $$;

statement ok
CREATE SCHEMA sc;

statement
CREATE FUNCTION sc.proc_f_2(STRING) RETURNS STRING LANGUAGE SQL AS $$ SELECT 'hello' $$;

query TTTTTBBBTITTTTTT
SELECT oid, proname, pronamespace, proowner, prolang, proleakproof, proisstrict, proretset, provolatile, pronargs, prorettype, proargtypes, proargmodes, proargnames, prokind, prosrc
FROM pg_catalog.pg_proc WHERE proname IN ('proc_f', 'proc_f_2')
ORDER BY 1
----
100118  proc_f    105  1546506610  14  false  false  false  v  1  20  20     NULL  NULL    f  SELECT 1;
100119  proc_f    105  1546506610  14  true   true   false  i  2  25  25 20  NULL  {"",b}  f  SELECT 'hello';
100121  proc_f_2  120  1546506610  14  false  false  false  v  1  25  25     NULL  NULL    f  SELECT 'hello';

# Ensure that the pg_proc virtual index works properly.

query TT
SELECT oid, proname FROM pg_proc WHERE oid = 'sc.proc_f_2'::regproc
----
100121  proc_f_2

statement ok
USE defaultdb;

query TTTTTBBBTITTTTT
SELECT oid, proname, pronamespace, proowner, prolang, proleakproof, proisstrict, proretset, provolatile, pronargs, prorettype, proargtypes, proargmodes, proargnames, prosrc
FROM pg_catalog.pg_proc WHERE proname IN ('proc_f', 'proc_f_2');
----

statement ok
USE test;
subtest end


subtest udf_regproc

query T
SELECT '100126'::REGPROC;
----
100126

query T
SELECT 'sc.proc_f_2'::REGPROC;
----
proc_f_2

query I
SELECT 'sc.proc_f_2'::REGPROC::INT;
----
100121

statement error pgcode 42883 pq: unknown function: no_such_func()
SELECT 'no_such_func'::REGPROC;

statement error pgcode 42P09 pq: more than one function named 'proc_f'
SELECT 'proc_f'::REGPROC;

query T
SELECT 100126::regproc;
----
100126

query I
SELECT 100117::regproc::INT;
----
100117

query T
SELECT 999999::regproc;
----
999999

subtest end


subtest execute_dropped_function

statement ok
CREATE FUNCTION f_test_exec_dropped(a int) RETURNS INT LANGUAGE SQL AS $$ SELECT a $$;

query I
SELECT f_test_exec_dropped(123);
----
123

statement ok
DROP FUNCTION f_test_exec_dropped;

statement error pgcode 42883 pq: unknown function: f_test_exec_dropped\(\)
SELECT f_test_exec_dropped(321);

subtest end


subtest create_or_replace_function

statement ok
CREATE FUNCTION f_test_cor(a INT, b INT) RETURNS INT IMMUTABLE LEAKPROOF STRICT LANGUAGE SQL AS $$ SELECT 1 $$;

statement error pgcode 42723 pq: function "f_test_cor" already exists with same argument types
CREATE FUNCTION f_test_cor(a INT, b INT) RETURNS INT IMMUTABLE LANGUAGE SQL AS $$ SELECT 1 $$;

statement error pgcode 42809 cannot change routine kind\nDETAIL: "f_test_cor" is a function
CREATE OR REPLACE PROCEDURE f_test_cor(a INT, b INT) LANGUAGE SQL AS $$ SELECT 2 $$;

statement ok
CREATE OR REPLACE FUNCTION f_test_cor_not_exist(a INT, b INT) RETURNS INT IMMUTABLE LANGUAGE SQL AS $$ SELECT 1 $$;

statement error pgcode 42P13 pq: cannot change name of input parameter "b"
CREATE OR REPLACE FUNCTION f_test_cor(a INT, c INT) RETURNS INT IMMUTABLE LANGUAGE SQL AS $$ SELECT 1 $$;

statement error pgcode 42P13 pq: cannot change return type of existing function
CREATE OR REPLACE FUNCTION f_test_cor(a INT, b INT) RETURNS STRING IMMUTABLE LANGUAGE SQL AS $$ SELECT 'hello' $$;

statement error pgcode 42P13 leak proof function must be immutable, but got volatility: VOLATILE
CREATE OR REPLACE FUNCTION f_test_cor(a INT, b INT) RETURNS INT LEAKPROOF LANGUAGE SQL AS $$ SELECT 1 $$;

query T
SELECT create_statement FROM [SHOW CREATE FUNCTION f_test_cor];
----
CREATE FUNCTION public.f_test_cor(a INT8, b INT8)
  RETURNS INT8
  IMMUTABLE
  LEAKPROOF
  STRICT
  LANGUAGE SQL
  SECURITY INVOKER
  AS $$
  SELECT 1;
$$

# Make sure volatility, leakproof and null input behavior are default values
# after replacing with a definition not specifying them.
statement ok
CREATE OR REPLACE FUNCTION f_test_cor(a INT, b INT) RETURNS INT LANGUAGE SQL AS $$ SELECT 2 $$;

query T
SELECT create_statement FROM [SHOW CREATE FUNCTION f_test_cor];
----
CREATE FUNCTION public.f_test_cor(a INT8, b INT8)
  RETURNS INT8
  VOLATILE
  NOT LEAKPROOF
  CALLED ON NULL INPUT
  LANGUAGE SQL
  SECURITY INVOKER
  AS $$
  SELECT 2;
$$

statement ok
CREATE OR REPLACE FUNCTION f_test_cor(a INT, b INT) RETURNS INT IMMUTABLE LEAKPROOF STRICT LANGUAGE SQL AS $$ SELECT 3 $$;

query T
SELECT create_statement FROM [SHOW CREATE FUNCTION f_test_cor];
----
CREATE FUNCTION public.f_test_cor(a INT8, b INT8)
  RETURNS INT8
  IMMUTABLE
  LEAKPROOF
  STRICT
  LANGUAGE SQL
  SECURITY INVOKER
  AS $$
  SELECT 3;
$$

subtest end


subtest seq_qualified_name

statement ok
CREATE SCHEMA sc_seq_qualified_name;
CREATE SEQUENCE sc_seq_qualified_name.sq;

statement error pgcode 42P01 pq: relation "sc_seq_qualified_name.sq" does not exist
CREATE FUNCTION f_seq_qualified_name() RETURNS INT LANGUAGE SQL AS $$ SELECT * FROM nextval('"sc_seq_qualified_name.sq"') $$;

statement ok
CREATE FUNCTION f_seq_qualified_name() RETURNS INT LANGUAGE SQL AS $$ SELECT nextval('sc_seq_qualified_name.sq') $$;

query I
SELECT f_seq_qualified_name()
----
1

statement ok
CREATE FUNCTION f_seq_qualified_name_quoted() RETURNS INT LANGUAGE SQL AS $$ SELECT nextval('"sc_seq_qualified_name"."sq"') $$;

query I
SELECT f_seq_qualified_name_quoted()
----
2

subtest end


subtest execution

statement ok
INSERT INTO ab VALUES (1, 1), (2, 2), (3, 3), (4, 1), (5, 1)

statement ok
CREATE FUNCTION one() RETURNS INT LANGUAGE SQL AS 'SELECT 2-1';

query I
SELECT one()
----
1

query I colnames
SELECT * FROM one()
----
one
1

query III colnames
SELECT *, one() FROM ab WHERE a = one()
----
a  b  one
1  1  1

query III colnames,rowsort
SELECT *, one() FROM ab WHERE b = one()
----
a  b  one
1  1  1
4  1  1
5  1  1

query II colnames
SELECT * FROM ab WHERE b = one() + 1
----
a  b
2  2

statement ok
CREATE FUNCTION max_in_values() RETURNS INT LANGUAGE SQL AS $$
  SELECT i FROM (VALUES (1, 0), (2, 0), (3, 0)) AS v(i, j) ORDER BY i DESC
$$

query I
SELECT max_in_values()
----
3

statement ok
CREATE FUNCTION fetch_one_then_two() RETURNS INT LANGUAGE SQL AS $$
SELECT b FROM ab WHERE a = 1;
SELECT b FROM ab WHERE a = 2;
$$

query II
SELECT i, fetch_one_then_two()
FROM (VALUES (1), (2), (3)) AS v(i)
WHERE i = fetch_one_then_two()
----
2  2

query I colnames
SELECT * FROM fetch_one_then_two()
----
fetch_one_then_two
2

statement ok
CREATE TABLE empty (e INT);
CREATE FUNCTION empty_result() RETURNS INT LANGUAGE SQL AS $$
SELECT e FROM empty
$$

query I
SELECT empty_result()
----
NULL

statement ok
CREATE FUNCTION int_identity(i INT) RETURNS INT LANGUAGE SQL AS 'SELECT i';

query I
SELECT int_identity(1)
----
1

query I
SELECT int_identity(10 + int_identity(1))
----
11

query II rowsort
SELECT a+b, int_identity(a+b) FROM ab WHERE a = int_identity(a) AND b = int_identity(b)
----
2  2
4  4
6  6
5  5
6  6

# Define some custom arithmetic functions that we can write interesting tests
# with that use builtin operators as oracles.
statement ok
CREATE FUNCTION add(x INT, y INT) RETURNS INT LANGUAGE SQL AS 'SELECT x+y';

statement ok
CREATE FUNCTION sub(x INT, y INT) RETURNS INT LANGUAGE SQL AS 'SELECT x-y';

statement ok
CREATE FUNCTION mult(x INT, y INT) RETURNS INT LANGUAGE SQL AS 'SELECT x*y';

query II rowsort
SELECT a + a + a + b + b + b, add(a, add(a, add(a, add(b, add(b, b))))) FROM ab
----
6   6
12  12
18  18
15  15
18  18

query II rowsort
SELECT (a * (a + b)) - b, sub(mult(a, add(a, b)), b) FROM ab
----
1   1
6   6
15  15
19  19
29  29

query II rowsort
SELECT a * (3 + b - a) + a * b * a, add(mult(a, add(3, sub(b, a))), mult(a, mult(b, a))) FROM ab
----
4   4
14  14
36  36
16  16
20  20

statement ok
CREATE FUNCTION fetch_b(arg_a INT) RETURNS INT LANGUAGE SQL AS $$
SELECT b FROM ab WHERE a = arg_a
$$

query II rowsort
SELECT b, fetch_b(a) FROM ab
----
1  1
2  2
3  3
1  1
1  1

query II rowsort
SELECT b + (a * 7) - (a * b), add(fetch_b(a), sub(mult(a, 7), mult(a, fetch_b(a)))) FROM ab
----
7   7
12  12
15  15
25  25
31  31

query I
SELECT fetch_b(99999999)
----
NULL

statement ok
CREATE FUNCTION one_nth(n INT) RETURNS INT LANGUAGE SQL AS 'SELECT 1/n'

statement error pgcode 22012 division by zero
SELECT one_nth(0)

statement error pgcode 22012 division by zero
SELECT int_identity((1/0)::INT)

subtest end


subtest args

statement error pgcode 42P13 pq: SQL functions cannot have arguments of type record
CREATE FUNCTION f(r RECORD) RETURNS INT LANGUAGE SQL AS 'SELECT i'

# TODO(mgartner): Technically $3 is a parameter, and the error message should be
# more similar to Postgres's "there is no parameter $3".
statement error pgcode 42P02 no value provided for placeholder: \$3
CREATE FUNCTION err(x INT, y INT) RETURNS INT LANGUAGE SQL AS 'SELECT x + y + $1 + $2 + $3'

statement error pgcode 42P02 no value provided for placeholder: \$3
CREATE FUNCTION err(INT, INT) RETURNS INT LANGUAGE SQL AS 'SELECT $1 + $2 + $3'

statement error pgcode 42601 placeholder index must be between 1 and 65536
CREATE FUNCTION err(INT) RETURNS INT LANGUAGE SQL AS 'SELECT 1 + $0'

statement ok
CREATE FUNCTION add(x INT, y INT, z INT) RETURNS INT LANGUAGE SQL AS $$
  SELECT (x - $1 + x) + ($2 - y + $2) + (z - $3 + z);
$$

statement ok
CREATE FUNCTION mult(x INT, y INT, z INT) RETURNS INT LANGUAGE SQL AS $$
  SELECT $1 * y * (z - $3 + z);
$$

query II rowsort
SELECT a + b + a, add(a, b, a) FROM ab
----
3   3
6   6
9   9
9   9
11  11

query I
SELECT a FROM ab WHERE (a + b + b) != add(a, b, b)
----

query II rowsort
SELECT
  (a + b + a) * (a + 3 + 7) * (b + 11 + 17),
  mult(add(a, b, a), add(a, 3, 7), add(b, 11, 17))
FROM ab
----
957   957
2160  2160
3627  3627
3654  3654
4785  4785

statement ok
PREPARE do_math(INT, INT, INT, INT) AS
SELECT
  (a + b + a) * (a + $1 + $2) * (b + $3 + $4),
  mult(add(a, b, a), add(a, $1, $2), add(b, $3, $4))
FROM ab

query II rowsort
EXECUTE do_math(3, 7, 11, 17)
----
957   957
2160  2160
3627  3627
3654  3654
4785  4785

statement error pgcode 54023 functions cannot have more than 100 arguments
CREATE FUNCTION err(
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT
) RETURNS INT LANGUAGE SQL AS 'SELECT $1';

# Up to 100 arguments are allowed.
statement ok
CREATE FUNCTION add(
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT,
  INT, INT, INT, INT, INT, INT, INT, INT, INT, INT
) RETURNS INT LANGUAGE SQL AS $$
  SELECT $1 + $2 + $3 + $4 + $5 + $6 + $7 + $8 + $9 + $10 +
    $11 + $12 + $13 + $14 + $15 + $16 + $17 + $18 + $19 + $20 +
    $21 + $22 + $23 + $24 + $25 + $26 + $27 + $28 + $29 + $30 +
    $31 + $32 + $33 + $34 + $35 + $36 + $37 + $38 + $39 + $40 +
    $41 + $42 + $43 + $44 + $45 + $46 + $47 + $48 + $49 + $50 +
    $51 + $52 + $53 + $54 + $55 + $56 + $57 + $58 + $59 + $60 +
    $61 + $62 + $63 + $64 + $65 + $66 + $67 + $68 + $69 + $70 +
    $71 + $72 + $73 + $74 + $75 + $76 + $77 + $78 + $79 + $80 +
    $81 + $82 + $83 + $84 + $85 + $86 + $87 + $88 + $89 + $90 +
    $91 + $92 + $93 + $94 + $95 + $96 + $97 + $98 + $99 + $100;
$$;

query TI
SELECT sum(i),
  add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
  11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
  21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
  31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
  41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
  51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
  61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
  71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
  81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
  91, 92, 93, 94, 95, 96, 97, 98, 99, 100)
FROM generate_series(1, 100) AS g(i)
----
5050  5050

subtest end


subtest return_type_assignment_casts

# Do not allow functions with return type mismatches that cannot be cast in an
# implicit or assignment context.
statement error pgcode 42P13 return type mismatch in function declared to return bool
CREATE FUNCTION err(i INT) RETURNS BOOL LANGUAGE SQL AS 'SELECT i'

statement ok
CREATE FUNCTION itof(i INT) RETURNS FLOAT8 LANGUAGE SQL AS 'SELECT i'

query FT
SELECT itof(123), pg_typeof(itof(123))
----
123  double precision

statement ok
CREATE FUNCTION stoc(s STRING) RETURNS CHAR LANGUAGE SQL AS 'SELECT s'

query FT
SELECT stoc('a'), pg_typeof(stoc('a'))
----
a  text

statement error pgcode 22001 value too long for type CHAR
SELECT stoc('abc')

subtest end


subtest tagged_dollar_quotes

statement ok
CREATE FUNCTION single_quote(s STRING) RETURNS STRING LANGUAGE SQL AS $func$
  SELECT $prefix$'$prefix$ || s || $suffix$'$suffix$
$func$

query T
SELECT create_statement FROM [SHOW CREATE FUNCTION single_quote]
----
CREATE FUNCTION public.single_quote(s STRING)
  RETURNS STRING
  VOLATILE
  NOT LEAKPROOF
  CALLED ON NULL INPUT
  LANGUAGE SQL
  SECURITY INVOKER
  AS $$
  SELECT (e'\'' || s) || e'\'';
$$

query T
SELECT single_quote('hello')
----
'hello'

statement error pgcode 42601 unterminated string
CREATE FUNCTION err() RETURNS STRING LANGUAGE SQL AS $outer$
  SELECT $inner$hello$outer$
$inner$

subtest end


subtest array_flatten

statement ok
CREATE FUNCTION arr(x INT) RETURNS INT[] LANGUAGE SQL AS $$
  SELECT ARRAY(VALUES (1), (2), (x));
$$

query T
SELECT arr(10)
----
{1,2,10}

query T nosort
SELECT arr(i) FROM generate_series(1, 3) g(i)
----
{1,2,1}
{1,2,2}
{1,2,3}

subtest end


subtest lowercase_hint_error_implicit_schema

statement ok
CREATE FUNCTION lowercase_hint_error_implicit_schema_fn() RETURNS INT AS 'SELECT 1' LANGUAGE SQL

statement error unknown function: LOWERCASE_HINT_ERROR_IMPLICIT_SCHEMA_FN\(\)\nHINT: lower-case alternative lowercase_hint_error_implicit_schema_fn exists
SELECT "LOWERCASE_HINT_ERROR_IMPLICIT_SCHEMA_FN"();

subtest end

subtest lowercase_hint_error_explicit_schema

statement ok
CREATE FUNCTION public.lowercase_hint_error_explicit_schema_fn() RETURNS INT AS 'SELECT 1' LANGUAGE SQL

statement error unknown function: public.LOWERCASE_HINT_ERROR_EXPLICIT_SCHEMA_FN\(\)\nHINT: lower-case alternative public.lowercase_hint_error_explicit_schema_fn exists
SELECT public."LOWERCASE_HINT_ERROR_EXPLICIT_SCHEMA_FN"();

subtest end

# Regression test for #102227 - it should be possible to use a UDF with a
# composite type parameter after the type is updated.
subtest udt_parameter

statement ok
CREATE TYPE amount AS ("value" INT, "currency" STRING, "minor_units" INT)

statement ok
CREATE TABLE "purchase" (
    "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    "amount" amount NOT NULL,
    "timestamp" TIMESTAMP NOT NULL DEFAULT now()
)

statement ok
INSERT INTO "purchase" (amount) VALUES
    ((1000,  'GBP', 100)),
    ((10,    'YEN', 1)),
    ((10000, 'BHD', 1000))

statement ok
CREATE FUNCTION decimal_amount(a amount) RETURNS DECIMAL(10, 2) IMMUTABLE LANGUAGE SQL AS $$
    SELECT (a)."value" / (a)."minor_units";
$$

query RT rowsort
SELECT
    decimal_amount(amount) AS amount,
    (amount).currency
FROM purchase
----
10.00  YEN
10.00  BHD
10.00  GBP

statement ok
UPDATE purchase
SET amount = ((amount).value, (amount).currency, 10000)
WHERE (amount).currency = 'BHD'

query RT rowsort
SELECT
    decimal_amount(amount) AS amount,
    (amount).currency
FROM purchase
----
10.00  YEN
1.00   BHD
10.00  GBP

subtest regression_113186

statement ok
CREATE FUNCTION f113186() RETURNS RECORD AS $$ SELECT 1.99; $$ LANGUAGE SQL;

query I
SELECT * FROM f113186() AS foo(x INT);
----
2

# Test type-coercion rules for composite return types.
subtest return_tuple

statement ok
CREATE TYPE one_typ AS (x INT);
CREATE TYPE two_typ AS (x INT, y INT);

# Test a function returning a composite type with one element.
statement ok
DROP FUNCTION f;
CREATE FUNCTION f() RETURNS one_typ LANGUAGE SQL AS $$ SELECT 1; $$;

query T
SELECT f();
----
(1)

query I
SELECT * FROM f();
----
1

statement ok
DROP FUNCTION f;
CREATE FUNCTION f() RETURNS one_typ LANGUAGE SQL AS $$ SELECT ROW(1); $$;

query T
SELECT f();
----
(1)

query I
SELECT * FROM f();
----
1

statement ok
DROP FUNCTION f;

statement error pgcode 42P13 pq: return type mismatch in function declared to return one_typ
CREATE FUNCTION f() RETURNS one_typ LANGUAGE SQL AS $$ SELECT ROW(ROW(1)); $$;

# Test a function returning a composite type with two elements.
statement ok
CREATE FUNCTION f() RETURNS two_typ LANGUAGE SQL AS $$ SELECT 1, 2; $$;

query T
SELECT f();
----
(1,2)

query II
SELECT * FROM f();
----
1  2

statement ok
DROP FUNCTION f;
CREATE FUNCTION f() RETURNS two_typ LANGUAGE SQL AS $$ SELECT ROW(1, 2); $$;

query T
SELECT f();
----
(1,2)

query II
SELECT * FROM f();
----
1  2

statement ok
DROP FUNCTION f;

statement error pgcode 42P13 pq: return type mismatch in function declared to return two_typ
CREATE FUNCTION f() RETURNS two_typ LANGUAGE SQL AS $$ SELECT ROW(ROW(1, 2)); $$;

# Test a function with two OUT-parameters.
statement ok
CREATE FUNCTION f(OUT x INT, OUT y INT) LANGUAGE SQL AS $$ SELECT 1, 2; $$;

query T
SELECT f();
----
(1,2)

query II
SELECT * FROM f();
----
1  2

statement ok
DROP FUNCTION f;
CREATE FUNCTION f(OUT x INT, OUT y INT) LANGUAGE SQL AS $$ SELECT ROW(1, 2); $$;

query T
SELECT f();
----
(1,2)

query II
SELECT * FROM f();
----
1  2

statement ok
DROP FUNCTION f;

statement error pgcode 42P13 pq: return type mismatch in function declared to return record
CREATE FUNCTION f(OUT x INT, OUT y INT) LANGUAGE SQL AS $$ SELECT ROW(ROW(1, 2)); $$;

statement ok
CREATE FUNCTION f(x ANYELEMENT) RETURNS INT LANGUAGE SQL AS $$ SELECT 1; $$;

statement ok
SELECT f(0);

statement ok
SELECT f(0);

statement ok
DROP FUNCTION f;
CREATE FUNCTION f(x INT) RETURNS INT LANGUAGE SQL AS $$ SELECT 1; $$;

statement ok
SELECT f('0');

statement ok
DROP FUNCTION f;
CREATE FUNCTION f(x TEXT) RETURNS INT LANGUAGE SQL AS $$ SELECT 1; $$;

statement ok
SELECT f('0');

statement ok
DROP FUNCTION f;

# Regression test for #117101 - update the resolved type of a VALUES row with a
# UDF after the UDF is built.
subtest regression_117101

statement ok
CREATE FUNCTION f117101() RETURNS RECORD CALLED ON NULL INPUT AS $funcbody$
  SELECT ((42)::INT8, (43)::INT8)
$funcbody$ LANGUAGE SQL;

statement error pgcode 42804 pq: VALUES types tuple{int, int} and tuple{tuple{string, int}, unknown} cannot be matched
SELECT
  *
FROM
(
  VALUES
    (
     (('aloha'::TEXT,
     (44)::INT8), NULL)
    ),
    (COALESCE(f117101(), NULL))
);

subtest end
