subtest types

statement error pgcode 42P13 pq: function result type must be int because of OUT parameters
CREATE FUNCTION f(OUT param INT) RETURNS FLOAT AS $$ BEGIN SELECT 1; END $$ LANGUAGE PLpgSQL;

statement error pgcode 42P13 pq: function result type must be record because of OUT parameters
CREATE FUNCTION f(OUT param1 INT, OUT param2 INT) RETURNS INT AS $$ BEGIN SELECT 1, 2; END $$ LANGUAGE PLpgSQL;

statement error pgcode 42P13 pq: function result type must be int because of OUT parameters
CREATE FUNCTION f(OUT param INT) RETURNS VOID AS $$ BEGIN SELECT 1; END $$ LANGUAGE PLpgSQL;

statement error pgcode 42P13 pq: function result type must be int because of OUT parameters
CREATE FUNCTION f(OUT param INT) RETURNS RECORD AS $$ BEGIN SELECT 1; END $$ LANGUAGE PLpgSQL;

statement ok
CREATE FUNCTION f(OUT param INT) RETURNS INT AS $$ BEGIN SELECT 1 INTO param; END $$ LANGUAGE PLpgSQL;

query I colnames
SELECT f();
----
f
1

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

statement error pgcode 42809 f\(unknown\) is not a procedure
CALL f(NULL);

statement error pgcode 42809 f\(int\) is not a procedure
CALL f(NULL::INT);

statement error pgcode 42809 f\(int\) is not a procedure
CALL f(1);

statement ok
DROP FUNCTION f;

statement error RETURN cannot have a parameter in function with OUT parameters
CREATE FUNCTION f(OUT INT) AS $$ BEGIN RETURN NULL; END $$ LANGUAGE PLpgSQL;

statement error RETURN cannot have a parameter in function with OUT parameters
CREATE FUNCTION f(OUT INT, OUT INT) AS $$ BEGIN RETURN NULL; END $$ LANGUAGE PLpgSQL;

statement error RETURN cannot have a parameter in function with OUT parameters
CREATE FUNCTION f(INOUT a INT) AS $$ BEGIN RETURN NULL; END $$ LANGUAGE PLpgSQL;

statement ok
CREATE FUNCTION f(INOUT param1 INT, OUT param2 INT) RETURNS RECORD AS $$
BEGIN
  param2 := 2;
  RAISE NOTICE '%', param2;
END
$$ LANGUAGE PLpgSQL;

query T colnames
SELECT f(3);
----
f
(3,2)

query T noticetrace
SELECT f(3);
----
NOTICE: 2

query II colnames
SELECT * FROM f(3);
----
param1 param2
3 2

query T noticetrace
SELECT * FROM f(3);
----
NOTICE: 2

query I colnames
SELECT param1 FROM f(3);
----
param1
3

query I colnames
SELECT param2 FROM f(3);
----
param2
2

statement ok
DROP FUNCTION f;

statement ok
CREATE FUNCTION f(INOUT param1 INT, OUT param2 INT) AS $$ BEGIN SELECT 3 INTO param1; END $$ LANGUAGE PLpgSQL;

query II colnames
SELECT * FROM f(1);
----
param1 param2
3 NULL

statement ok
CREATE OR REPLACE FUNCTION f(INOUT param1 INT, OUT param2 INT) AS $$
BEGIN
  RAISE NOTICE '% %', param1, param2;
  param1 = 3;
  RAISE NOTICE '% %', param1, param2;
  SELECT 4 INTO param2;
  RAISE NOTICE '% %', param1, param2;
END
$$ LANGUAGE PLpgSQL;

query T colnames
SELECT f(1);
----
f
(3,4)

query T noticetrace
SELECT f(1);
----
NOTICE: 1 <NULL>
NOTICE: 3 <NULL>
NOTICE: 3 4

query II colnames
SELECT * FROM f(1);
----
param1 param2
3 4

query T noticetrace
SELECT * FROM f(1);
----
NOTICE: 1 <NULL>
NOTICE: 3 <NULL>
NOTICE: 3 4

statement ok
DROP FUNCTION f;

statement ok
CREATE FUNCTION f(INOUT param INT) AS $$ BEGIN SELECT 'hello'; END $$ LANGUAGE PLpgSQL;

statement ok
DROP FUNCTION f;

statement ok
CREATE FUNCTION f(INOUT param1 INT, OUT param2 INT) AS $$
BEGIN
  param1 := 1;
  SELECT 2 INTO param2;
END
$$ LANGUAGE PLpgSQL;

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

statement ok
DROP FUNCTION f;

# Verify that function resolution works correctly when dropping functions (OUT
# arguments are ignored).
statement ok
CREATE FUNCTION f(OUT param INT) AS $$ BEGIN SELECT 1 INTO param; END $$ LANGUAGE PLpgSQL;

statement ok
DROP FUNCTION f;

statement ok
CREATE FUNCTION f(OUT param INT) AS $$ BEGIN SELECT 1 INTO param; END $$ LANGUAGE PLpgSQL;

statement ok
DROP FUNCTION f(OUT INT);

statement ok
CREATE FUNCTION f(OUT param1 INT, OUT param2 INT) AS $$ BEGIN SELECT 1 INTO param1; END $$ LANGUAGE PLpgSQL;

statement ok
DROP FUNCTION f(OUT INT);

# TODO(119502): uncomment this and invoke the function when $i notation is
# supported.
# statement ok
# CREATE FUNCTION f(OUT param1 INT, OUT param2 INT) AS $$ BEGIN SELECT 1 INTO $2; END $$ LANGUAGE PLpgSQL;

statement ok
CREATE FUNCTION f(OUT param1 INT, OUT param2 INT) AS $$ BEGIN SELECT 1 INTO param2; END $$ LANGUAGE PLpgSQL;

statement error pq: function f\(int\) does not exist
DROP FUNCTION f(INT);

statement ok
DROP FUNCTION f;

statement ok
CREATE FUNCTION f(OUT param INT) RETURNS INT AS $$ BEGIN SELECT 1 INTO param; END $$ LANGUAGE PLpgSQL;

statement ok
DROP FUNCTION f(OUT INT, OUT text, OUT INT);

subtest end

subtest show_create

statement ok
CREATE FUNCTION f_param_types(IN p1 INT, INOUT p2 INT, IN OUT p3 INT, OUT p4 INT) AS $$
BEGIN
  SELECT p2, p3, p1;
END
$$ LANGUAGE PLpgSQL;

query T
SELECT create_statement FROM [SHOW CREATE FUNCTION f_param_types];
----
CREATE FUNCTION public.f_param_types(IN p1 INT8, INOUT p2 INT8, INOUT p3 INT8, OUT p4 INT8)
  RETURNS RECORD
  VOLATILE
  NOT LEAKPROOF
  CALLED ON NULL INPUT
  LANGUAGE plpgsql
  SECURITY INVOKER
  AS $$
  BEGIN
  SELECT p2, p3, p1;
  END;
$$

statement ok
DROP FUNCTION f_param_types;

statement ok
CREATE FUNCTION f_param_types(OUT param INT) AS $$
BEGIN
  SELECT 1;
END
$$ LANGUAGE PLpgSQL;

query T
SELECT create_statement FROM [SHOW CREATE FUNCTION f_param_types];
----
CREATE FUNCTION public.f_param_types(OUT param INT8)
  RETURNS INT8
  VOLATILE
  NOT LEAKPROOF
  CALLED ON NULL INPUT
  LANGUAGE plpgsql
  SECURITY INVOKER
  AS $$
  BEGIN
  SELECT 1;
  END;
$$

statement ok
DROP FUNCTION f_param_types;

subtest end

subtest parameter_names

# Unlike for SQL UDFs, sharing of parameter names is not allowed across
# different "parameter namespaces" (IN vs OUT).

statement error pgcode 42P13 pq: parameter name "a" used more than once
CREATE FUNCTION f_same_name(IN a INT, IN a INT) RETURNS INT AS $$ BEGIN RETURN a + a; END $$ LANGUAGE PLpgSQL;

statement error pgcode 42P13 pq: parameter name "a" used more than once
CREATE FUNCTION f_same_name(IN a INT, INOUT a INT) RETURNS INT AS $$ BEGIN RETURN a + a; END $$ LANGUAGE PLpgSQL;

statement error pgcode 42P13 pq: parameter name "a" used more than once
CREATE FUNCTION f_same_name(OUT a INT, INOUT a INT) RETURNS INT AS $$ BEGIN RETURN a + a; END $$ LANGUAGE PLpgSQL;

statement error pgcode 42P13 pq: parameter name "a" used more than once
CREATE FUNCTION f_same_name(IN a INT, OUT a INT) RETURNS INT AS $$ BEGIN RETURN a; END $$ LANGUAGE PLpgSQL;

statement ok
CREATE FUNCTION f_names(IN param_in INT, OUT param_out INT) AS $$ BEGIN SELECT param_in INTO param_out; END $$ LANGUAGE PLpgSQL;

query I colnames
SELECT f_names(2);
----
f_names
2

query I colnames
SELECT * FROM f_names(2);
----
param_out
2

statement ok
CREATE FUNCTION f_in_int(IN param INT) RETURNS INT AS $$ BEGIN RETURN param; END; $$ LANGUAGE PLpgSQL;

query I colnames
SELECT f_in_int(2);
----
f_in_int
2

query I colnames
SELECT * FROM f_in_int(2);
----
f_in_int
2

# Changing OUT parameter name is ok.
statement ok
CREATE FUNCTION f_out_int(OUT param INT) AS $$ BEGIN param = 2; END; $$ LANGUAGE PLpgSQL;

statement ok
CREATE OR REPLACE FUNCTION f_out_int(OUT param_new INT) AS $$ BEGIN param_new = 2; END; $$ LANGUAGE PLpgSQL;

query T
SELECT create_statement FROM [SHOW CREATE FUNCTION f_out_int];
----
CREATE FUNCTION public.f_out_int(OUT param_new INT8)
  RETURNS INT8
  VOLATILE
  NOT LEAKPROOF
  CALLED ON NULL INPUT
  LANGUAGE plpgsql
  SECURITY INVOKER
  AS $$
  BEGIN
  param_new := 2;
  END;
$$

query I colnames
SELECT f_out_int();
----
f_out_int
2

query I colnames
SELECT * FROM f_out_int();
----
param_new
2

# But changing IN or INOUT parameter name is not allowed.
statement error pgcode 42P13 pq: cannot change name of input parameter "param"
CREATE OR REPLACE FUNCTION f_in_int(IN param_new INT) RETURNS INT AS $$ BEGIN RETURN param_new; END; $$ LANGUAGE PLpgSQL;

statement ok
CREATE FUNCTION f_inout_int(INOUT param INT) AS $$ BEGIN param = 2; END; $$ LANGUAGE PLpgSQL;

statement error pgcode 42P13 pq: cannot change name of input parameter "param"
CREATE OR REPLACE FUNCTION f_inout_int(INOUT param_new INT) AS $$ BEGIN param_new = 2; END; $$ LANGUAGE PLpgSQL;

subtest end

subtest changing_parameters

statement ok
CREATE FUNCTION f_int(IN param INT) RETURNS INT AS $$ BEGIN RETURN param; END; $$ LANGUAGE PLpgSQL;

query I colnames
SELECT * FROM f_int(2);
----
f_int
2

# We can change the parameter class from IN to INOUT without introducing new
# overload.
statement ok
CREATE OR REPLACE FUNCTION f_int(INOUT param INT) AS $$ BEGIN param = 2; END; $$ LANGUAGE PLpgSQL;

query T
SELECT create_statement FROM [SHOW CREATE FUNCTION f_int];
----
CREATE FUNCTION public.f_int(INOUT param INT8)
  RETURNS INT8
  VOLATILE
  NOT LEAKPROOF
  CALLED ON NULL INPUT
  LANGUAGE plpgsql
  SECURITY INVOKER
  AS $$
  BEGIN
  param := 2;
  END;
$$

query I colnames
SELECT * FROM f_int(2);
----
param
2

# We can add and remove an OUT parameter too without introducing another
# overload (but must preserve the original parameter name for IN / INOUT
# parameter).
statement error pgcode 42P13 pq: cannot change name of input parameter "param"
CREATE OR REPLACE FUNCTION f_int(IN param_in INT, OUT param_out INT) AS $$ BEGIN param_out = param_in; END; $$ LANGUAGE PLpgSQL;

statement ok
CREATE OR REPLACE FUNCTION f_int(IN param INT, OUT param_out INT) AS $$ BEGIN SELECT param INTO param_out; END; $$ LANGUAGE PLpgSQL;

query T
SELECT create_statement FROM [SHOW CREATE FUNCTION f_int];
----
CREATE FUNCTION public.f_int(IN param INT8, OUT param_out INT8)
  RETURNS INT8
  VOLATILE
  NOT LEAKPROOF
  CALLED ON NULL INPUT
  LANGUAGE plpgsql
  SECURITY INVOKER
  AS $$
  BEGIN
  SELECT param INTO param_out;
  END;
$$

query I colnames
SELECT * FROM f_int(2);
----
param_out
2

statement ok
CREATE OR REPLACE FUNCTION f_int(OUT param_out INT, IN param INT) AS $$ BEGIN SELECT param INTO param_out; END; $$ LANGUAGE PLpgSQL;

query T
SELECT create_statement FROM [SHOW CREATE FUNCTION f_int];
----
CREATE FUNCTION public.f_int(OUT param_out INT8, IN param INT8)
  RETURNS INT8
  VOLATILE
  NOT LEAKPROOF
  CALLED ON NULL INPUT
  LANGUAGE plpgsql
  SECURITY INVOKER
  AS $$
  BEGIN
  SELECT param INTO param_out;
  END;
$$

query I colnames
SELECT * FROM f_int(2);
----
param_out
2

statement ok
CREATE OR REPLACE FUNCTION f_int(INOUT param INT) AS $$ BEGIN param = param; END; $$ LANGUAGE PLpgSQL;

query T
SELECT create_statement FROM [SHOW CREATE FUNCTION f_int];
----
CREATE FUNCTION public.f_int(INOUT param INT8)
  RETURNS INT8
  VOLATILE
  NOT LEAKPROOF
  CALLED ON NULL INPUT
  LANGUAGE plpgsql
  SECURITY INVOKER
  AS $$
  BEGIN
  param := param;
  END;
$$

query I colnames
SELECT * FROM f_int(2);
----
param
2

subtest end

subtest default_parameter_names

# Parameter names are optional. Whenever a UDF returns RECORD type, each unnamed
# OUT parameter with ordinal 'i' (among all OUT parameters) gets the default
# name that is "column" || i.

statement ok
CREATE FUNCTION f_default_names(OUT INT, OUT param2 INT, IN INT, OUT INT) AS $$ BEGIN param2 = 2; END; $$ LANGUAGE PLpgSQL;

query T
SELECT create_statement FROM [SHOW CREATE FUNCTION f_default_names];
----
CREATE FUNCTION public.f_default_names(OUT INT8, OUT param2 INT8, IN INT8, OUT INT8)
  RETURNS RECORD
  VOLATILE
  NOT LEAKPROOF
  CALLED ON NULL INPUT
  LANGUAGE plpgsql
  SECURITY INVOKER
  AS $$
  BEGIN
  param2 := 2;
  END;
$$

query T colnames
SELECT f_default_names(0);
----
f_default_names
(,2,)

query III colnames
SELECT * FROM f_default_names(0);
----
column1  param2  column3
NULL     2       NULL

query I colnames
SELECT column1 FROM f_default_names(0);
----
column1
NULL

query I colnames
SELECT param2 FROM f_default_names(0);
----
param2
2

query I colnames
SELECT column3 FROM f_default_names(0);
----
column3
NULL

# However, attempting to access the parameter by the default names is invalid.
statement error pgcode 42601 pq: \"column1\" is not a known variable
CREATE OR REPLACE FUNCTION f_default_names(OUT INT, OUT param2 INT, IN INT, OUT INT) AS $$ BEGIN SELECT 1 INTO column1; END; $$ LANGUAGE PLpgSQL;

# Introducing the OUT parameter name is disallowed because it'd change the
# return type.
statement error cannot change return type of existing function
CREATE OR REPLACE FUNCTION f_default_names(OUT INT, OUT param2 INT, IN INT, OUT param3 INT) AS $$ BEGIN param2 = 2; END; $$ LANGUAGE PLpgSQL;

# Introducing the name that matches the default OUT parameter name is allowed.
statement ok
CREATE OR REPLACE FUNCTION f_default_names(OUT INT, OUT param2 INT, IN INT, OUT column3 INT) AS $$ BEGIN SELECT 3 INTO column3; END; $$ LANGUAGE PLpgSQL;

query T
SELECT create_statement FROM [SHOW CREATE FUNCTION f_default_names];
----
CREATE FUNCTION public.f_default_names(OUT INT8, OUT param2 INT8, IN INT8, OUT column3 INT8)
  RETURNS RECORD
  VOLATILE
  NOT LEAKPROOF
  CALLED ON NULL INPUT
  LANGUAGE plpgsql
  SECURITY INVOKER
  AS $$
  BEGIN
  SELECT 3 INTO column3;
  END;
$$

query I colnames
SELECT column3 FROM f_default_names(0);
----
column3
3

# Then we can omit the default OUT parameter name again (but still cannot use it
# in the body).
statement error pgcode 42601 pq: \"column3\" is not a known variable
CREATE OR REPLACE FUNCTION f_default_names(OUT INT, OUT param2 INT, IN INT, OUT INT) AS $$ BEGIN SELECT 3 INTO column3; END; $$ LANGUAGE PLpgSQL;

statement ok
CREATE OR REPLACE FUNCTION f_default_names(OUT INT, OUT param2 INT, IN INT, OUT INT) AS $$ BEGIN param2 = 2; END; $$ LANGUAGE PLpgSQL;

query T
SELECT create_statement FROM [SHOW CREATE FUNCTION f_default_names];
----
CREATE FUNCTION public.f_default_names(OUT INT8, OUT param2 INT8, IN INT8, OUT INT8)
  RETURNS RECORD
  VOLATILE
  NOT LEAKPROOF
  CALLED ON NULL INPUT
  LANGUAGE plpgsql
  SECURITY INVOKER
  AS $$
  BEGIN
  param2 := 2;
  END;
$$

# Introducing the IN parameter name is ok.
statement ok
CREATE OR REPLACE FUNCTION f_default_names(OUT INT, OUT param2 INT, IN in_param INT, OUT INT) AS $$ BEGIN SELECT in_param INTO param2; END; $$ LANGUAGE PLpgSQL;

query T
SELECT create_statement FROM [SHOW CREATE FUNCTION f_default_names];
----
CREATE FUNCTION public.f_default_names(OUT INT8, OUT param2 INT8, IN in_param INT8, OUT INT8)
  RETURNS RECORD
  VOLATILE
  NOT LEAKPROOF
  CALLED ON NULL INPUT
  LANGUAGE plpgsql
  SECURITY INVOKER
  AS $$
  BEGIN
  SELECT in_param INTO param2;
  END;
$$

# But then the IN parameter name cannot be changed anymore.
statement error cannot change name of input parameter "in_param"
CREATE OR REPLACE FUNCTION f_default_names(OUT INT, OUT param2 INT, IN in_param_new INT, OUT INT) AS $$ BEGIN SELECT in_param_new INTO param2; END; $$ LANGUAGE PLpgSQL;

query T colnames
SELECT f_default_names(0);
----
f_default_names
(,0,)

query III colnames
SELECT * FROM f_default_names(0);
----
column1  param2  column3
NULL     0       NULL

subtest end

subtest return_col_names

statement ok
CREATE TYPE typ AS (a INT, b INT);
CREATE FUNCTION f_udt() RETURNS typ AS $$ BEGIN RETURN (1, 2); END; $$ LANGUAGE PLpgSQL;

query T colnames
SELECT f_udt()
----
f_udt
(1,2)

query II colnames
SELECT * FROM f_udt()
----
a  b
1  2

query I colnames
SELECT a FROM f_udt()
----
a
1

query I colnames
SELECT b FROM f_udt()
----
b
2

statement error pgcode 2BP01 cannot drop type "typ" because other objects \(\[test.public.f_udt\]\) still depend on it
DROP TYPE typ;

statement ok
DROP FUNCTION f_udt;

statement ok
CREATE TYPE greeting AS ENUM('hello', 'hi', 'yo');
CREATE FUNCTION f_enum(OUT greeting greeting) AS $$ BEGIN SELECT 'hi'::greeting INTO greeting; END; $$ LANGUAGE PLpgSQL;

query T colnames
SELECT * FROM f_enum()
----
greeting
hi

statement error pgcode 2BP01 cannot drop type "greeting" because other objects \(\[test.public.f_enum\]\) still depend on it
DROP TYPE greeting;

statement ok
DROP FUNCTION f_enum;

subtest implicit_return

# A routine with OUT-parameters should implicitly return the OUT-parameters when
# control flow exits. In addition, RETURN; with no expression immediately
# returns.
statement ok
CREATE FUNCTION f(OUT x INT, OUT y INT, n INT) AS $$
  BEGIN
    x := 1;
    IF n = 0 THEN
      y := 2;
      RETURN;
    ELSIF n = 1 THEN
      y := 3;
    END IF;
  END
$$ LANGUAGE PLpgSQL;

query TTT
SELECT f(0), f(1), f(2);
----
(1,2)  (1,3)  (1,)

# Implicit RETURN statements apply to exception handlers.
statement ok
DROP FUNCTION f;
CREATE FUNCTION f(OUT x INT, OUT y INT, n INT) AS $$
  BEGIN
    x := 1;
    SELECT 1 // 0;
  EXCEPTION WHEN division_by_zero THEN
    y := 2;
    IF n = 0 THEN
      x := 3;
      RETURN;
    END IF;
  END
$$ LANGUAGE PLpgSQL;

query TT
SELECT f(0), f(1);
----
(3,2)  (1,2)

statement ok
DROP FUNCTION f;

statement error pgcode 42804 pq: RETURN cannot have a parameter in function with OUT parameters
CREATE FUNCTION f(OUT x INT, OUT y INT, n INT) AS $$ BEGIN RETURN (5, 6); END $$ LANGUAGE PLpgSQL;

subtest end

subtest default_exprs

statement error pgcode 22P02 could not parse "a" as type int
CREATE FUNCTION my_sum(a INT, b INT DEFAULT 2, c INT DEFAULT 'a') RETURNS INT AS $$ BEGIN RETURN a + b + c; END; $$ LANGUAGE PLpgSQL;

statement error pgcode 42P13 input parameters after one with a default value must also have defaults
CREATE FUNCTION my_sum(a INT, b INT DEFAULT 2, c INT) RETURNS INT AS $$ BEGIN RETURN a + b + c; END; $$ LANGUAGE PLpgSQL;

statement error pgcode 42P13 input parameters after one with a default value must also have defaults
CREATE FUNCTION my_sum(a INT, b INT DEFAULT 2, INOUT c INT) RETURNS INT AS $$ BEGIN RETURN a + b + c; END; $$ LANGUAGE PLpgSQL;

statement error pgcode 42P13 only input parameters can have default values
CREATE FUNCTION my_sum(a INT, b INT DEFAULT 2, OUT c INT = 3) AS $$ BEGIN SELECT a + b INTO c; END; $$ LANGUAGE PLpgSQL;

statement error pgcode 42703 column "b" does not exist
CREATE FUNCTION my_sum(a INT, b INT, c INT = b + 1) RETURNS INT AS $$ BEGIN RETURN a + b + c; END; $$ LANGUAGE PLpgSQL;

statement error pgcode 42703 column "b" does not exist
CREATE FUNCTION my_sum(a INT, b INT = 2, c INT = b + 1) RETURNS INT AS $$ BEGIN RETURN a + b + c; END; $$ LANGUAGE PLpgSQL;

statement error pgcode 42703 column "d" does not exist
CREATE FUNCTION my_sum(a INT, b INT = 2, c INT = d + 1) RETURNS INT AS $$ BEGIN RETURN a + b + c; END; $$ LANGUAGE PLpgSQL;

statement error pgcode 42804 argument of DEFAULT must be type int, not type bool
CREATE FUNCTION my_sum(a INT, b INT DEFAULT true) RETURNS INT AS $$ BEGIN RETURN a + b; END; $$ LANGUAGE PLpgSQL;

statement ok
CREATE FUNCTION my_sum(a INT, b INT, c INT) RETURNS INT AS $$ BEGIN RETURN a + b + c; END; $$ LANGUAGE PLpgSQL;

statement error pgcode 42P13 input parameters after one with a default value must also have defaults
CREATE OR REPLACE FUNCTION my_sum(a INT = 1, b INT, c INT = 3) RETURNS INT AS $$ BEGIN RETURN a + b + c; END; $$ LANGUAGE PLpgSQL;

# Adding a default expression to a parameter is ok.
statement ok
CREATE OR REPLACE FUNCTION my_sum(a INT, b INT DEFAULT 2, c INT = 3) RETURNS INT AS $$ BEGIN RETURN a + b + c; END; $$ LANGUAGE PLpgSQL;

# But removing an existing default expression is not ok.
statement error pgcode 42P13 cannot remove parameter defaults from existing function
CREATE OR REPLACE FUNCTION my_sum(a INT, b INT, c INT = 3) RETURNS INT AS $$ BEGIN RETURN a + b + c; END; $$ LANGUAGE PLpgSQL;

statement error pgcode 22P02 could not parse "a" as type int
CREATE OR REPLACE FUNCTION my_sum(a INT = 'a', b INT DEFAULT 2, c INT = 3) RETURNS INT AS $$ BEGIN RETURN a + b + c; END; $$ LANGUAGE PLpgSQL;

statement error pgcode 0A000 subqueries are not allowed in DEFAULT expressions
CREATE OR REPLACE FUNCTION my_sum(a INT, b INT DEFAULT (SELECT 1)) RETURNS INT AS $$ BEGIN SELECT a + b; END; $$ LANGUAGE PLpgSQL;

statement error pgcode 0A000 subqueries are not allowed in DEFAULT expressions
CREATE OR REPLACE FUNCTION my_sum(a INT, b INT DEFAULT 1 + (SELECT 2 FROM (VALUES (NULL)))) RETURNS INT AS $$ BEGIN SELECT a + b; END; $$ LANGUAGE PLpgSQL;

query I
SELECT my_sum(1);
----
6

query I
SELECT my_sum(1, 1);
----
5

query I
SELECT my_sum(1, 1, 1);
----
3

# Note that postgres returns "function my_sum() does not exist".
statement error pgcode 42883 unknown signature: public.my_sum\(\)
SELECT my_sum();

# Note that postgres returns "function my_sum(int, int, int, int) does not exist".
statement error pgcode 42883 unknown signature: public.my_sum\(int, int, int, int\)
SELECT my_sum(1, 1, 1, 1);

# Same as above, but the default value needs to be coerced from numeric to int
# (becoming 4).
statement ok
CREATE OR REPLACE FUNCTION my_sum(a INT, b INT DEFAULT 2, c INT = 3.5) RETURNS INT AS $$ BEGIN RETURN a + b + c; END; $$ LANGUAGE PLpgSQL;

query I
SELECT my_sum(1);
----
7

query I
SELECT my_sum(1, 1);
----
6

# Add another overload that creates ambiguity for some number of input
# arguments.
statement ok
CREATE FUNCTION my_sum(a INT) RETURNS INT AS $$ BEGIN RETURN a; END; $$ LANGUAGE PLpgSQL;

statement error pgcode 42725 function name "my_sum" is not unique
SELECT my_sum(1);

statement ok
DROP FUNCTION my_sum(INT);

statement ok
DROP FUNCTION my_sum;

# Create a new function with the same name as the one we just dropped and then
# invoke in the same way as above - the optimizer should realize that the cached
# memo is stale.
statement ok
CREATE FUNCTION my_sum(OUT sum INT, INOUT a INT, INOUT b INT = 3) AS $$ BEGIN SELECT a + b INTO sum; END; $$ LANGUAGE PLpgSQL;

query T
SELECT my_sum(1);
----
(4,1,3)

query III
SELECT * FROM my_sum(1);
----
4  1  3

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

query III
SELECT * FROM my_sum(1, 1);
----
2  1  1

statement ok
DROP FUNCTION my_sum;

statement error pgcode 0A000 unnamed INOUT parameters are not yet supported
CREATE FUNCTION my_sum(OUT a_plus_one INT, INOUT a INT, INOUT INT = 3) AS $$ BEGIN SELECT a + 1 INTO a_plus_one; END; $$ LANGUAGE PLpgSQL;

statement ok
CREATE FUNCTION my_sum(OUT a_plus_one INT, INOUT a INT, INOUT b INT = 3) AS $$ BEGIN SELECT a + 1 INTO a_plus_one; END; $$ LANGUAGE PLpgSQL;

query T
SELECT my_sum(1);
----
(2,1,3)

query III colnames
SELECT * FROM my_sum(1);
----
a_plus_one  a  b
2           1  3

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

query III
SELECT * FROM my_sum(1, 1);
----
2  1  1

statement ok
DROP FUNCTION my_sum;

# Test for a narrowing type coercion.
statement ok
CREATE FUNCTION f(x CHAR DEFAULT 'foo') RETURNS CHAR AS $$ BEGIN RETURN x; END; $$ LANGUAGE PLpgSQL;

# Note that postgres doesn't actually truncate the value and returns 'foo' here
# (this difference is tracked by #115385).
query T
SELECT f();
----
f

statement ok
DROP FUNCTION f;

# Test case when DEFAULT expression uses a UDF.

statement ok
CREATE FUNCTION f1(a INT, b INT = 2) RETURNS INT AS $$ BEGIN RETURN a + b; END; $$ LANGUAGE PLpgSQL;

statement ok
CREATE FUNCTION f2(a INT, b INT = f1(1)) RETURNS INT AS $$ BEGIN RETURN a + b; END; $$ LANGUAGE PLpgSQL;

query III
SELECT f1(1), f2(1), f2(1, 1);
----
3  4  2

statement ok
CREATE OR REPLACE FUNCTION f1 (a INT, b INT = 2) RETURNS INT AS $$ BEGIN RETURN a * b; END; $$ LANGUAGE PLpgSQL;

query III
SELECT f1(1), f2(1), f2(1, 1);
----
2  3  2

statement error pgcode 2BP01 cannot drop function "f1" because other objects \(\[test.public.f2\]\) still depend on it
DROP FUNCTION f1;

statement ok
DROP FUNCTION f2;

statement ok
DROP FUNCTION f1;

# Test that dropping UDTs or enum members used in the DEFAULT expression is not
# allowed.

statement ok
CREATE FUNCTION f(p1 typ DEFAULT (1, 2), p2 greeting = 'yo') RETURNS INT AS $$ BEGIN SELECT 1; END; $$ LANGUAGE PLpgSQL;

statement error pgcode 2BP01 cannot drop type "typ" because other objects \(\[test.public.f\]\) still depend on it
DROP TYPE typ;

statement error pgcode 2BP01 could not remove enum value "yo" as it is being used in a routine "f"
ALTER TYPE greeting DROP VALUE 'yo';

# Dropping enum value not used in the DEFAULT expression should be ok.
statement ok
ALTER TYPE greeting DROP VALUE 'hello';

# Using a different enum value in the DEFAULT expression should allow us to drop
# the original enum value.
statement ok
CREATE OR REPLACE FUNCTION f(p1 typ DEFAULT (1, 2), p2 greeting = 'hi') RETURNS INT AS $$ BEGIN SELECT 1; END; $$ LANGUAGE PLpgSQL;

statement ok
ALTER TYPE greeting DROP VALUE 'yo';

statement error pgcode 2BP01 could not remove enum value "hi" as it is being used in a routine "f"
ALTER TYPE greeting DROP VALUE 'hi';

statement ok
DROP FUNCTION f;

# Test having sequences in the DEFAULT expression.

statement ok
CREATE SEQUENCE seq;

statement ok
CREATE FUNCTION f(a INT = nextval('seq')) RETURNS INT AS $$ BEGIN RETURN a; END; $$ LANGUAGE PLpgSQL;

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

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

query I
SELECT f();
----
2

statement error pgcode 2BP01 cannot drop sequence seq because other objects depend on it
DROP SEQUENCE seq;

statement ok
CREATE OR REPLACE FUNCTION f(a INT = 3) RETURNS INT AS $$ BEGIN RETURN a; END; $$ LANGUAGE PLpgSQL;

# DEFAULT expression no longer uses the sequence.
statement ok
DROP SEQUENCE seq;

statement ok
DROP FUNCTION f;

# Try doing the same when the sequence is added in the replacement.

statement ok
CREATE SEQUENCE seq;

statement ok
CREATE FUNCTION f(a INT = 3) RETURNS INT AS $$ BEGIN RETURN a; END; $$ LANGUAGE PLpgSQL;

statement ok
CREATE OR REPLACE FUNCTION f(a INT = nextval('seq')) RETURNS INT AS $$ BEGIN RETURN a; END; $$ LANGUAGE PLpgSQL;

statement error pgcode 2BP01 cannot drop sequence seq because other objects depend on it
DROP SEQUENCE seq;

statement ok
DROP FUNCTION f;

statement ok
DROP SEQUENCE seq;

# Regression test for invalid assertion when using the default argument for
# INOUT parameter when invoking the procedure (#122263).
statement ok
CREATE PROCEDURE p(INOUT a int DEFAULT 11) AS $$
BEGIN
  RAISE NOTICE 'a: %', a;
  a := a * 10;
END;
$$ LANGUAGE PLpgSQL;

statement error pgcode 42601 procedure parameter "a" is an output parameter but corresponding argument is not writable
CREATE FUNCTION foo() RETURNS VOID AS $$
DECLARE _a int;
BEGIN
  _a := 10;
  CALL p();  -- fail, no output argument for a
  RAISE NOTICE '_a: %', _a;
END
$$ LANGUAGE PLpgSQL;

statement ok
DROP PROCEDURE p;

subtest end
