statement ok
CREATE PROCEDURE p_nested() AS $$
  BEGIN
    RAISE NOTICE 'p_nested';
  END
$$ LANGUAGE PLpgSQL;

statement ok
CREATE PROCEDURE p() AS $$
  BEGIN
    CALL p_nested();
    RAISE NOTICE 'p';
  END
$$ LANGUAGE PLpgSQL;

query T noticetrace
CALL p();
----
NOTICE: p_nested
NOTICE: p

statement ok
DROP PROCEDURE p;
DROP PROCEDURE p_nested;

statement ok
CREATE PROCEDURE p_nested(x INT, y INT) AS $$
  DECLARE
    z INT;
  BEGIN
    z := x + y;
    RAISE NOTICE 'p_nested %', z;
  END
$$ LANGUAGE PLpgSQL;

# Pass constants to the nested procedure.
statement ok
CREATE PROCEDURE p() AS $$
  BEGIN
    CALL p_nested(100, 200);
    RAISE NOTICE 'p';
  END
$$ LANGUAGE PLpgSQL;

query T noticetrace
CALL p();
----
NOTICE: p_nested 300
NOTICE: p

# Pass variables to the nested procedure.
statement ok
DROP PROCEDURE p;
CREATE PROCEDURE p(a INT, b INT) AS $$
  BEGIN
    CALL p_nested(a, b);
    RAISE NOTICE 'p';
  END
$$ LANGUAGE PLpgSQL;

query T noticetrace
CALL p(100, 200);
----
NOTICE: p_nested 300
NOTICE: p

statement ok
DROP PROCEDURE p;
DROP PROCEDURE p_nested;

# The outer procedure passes variables in OUT-parameter position. Then, the
# result of the nested procedure is assigned to those variables.
statement ok
CREATE PROCEDURE p_nested(x INT, y INT, OUT a INT, OUT b INT) AS $$
  BEGIN
    a := x + y;
    RAISE NOTICE 'p_nested x: % y: %', x, y;
    b := x * y;
  END
$$ LANGUAGE PLpgSQL;

statement ok
CREATE PROCEDURE p() AS $$
  DECLARE
    foo INT := 100;
    a INT := 200;
    b INT;
  BEGIN
    RAISE NOTICE 'p foo: % a: % b: %', foo, a, b;
    CALL p_nested(foo, 300, a, b);
    RAISE NOTICE 'p foo: % a: % b: %', foo, a, b;
  END
$$ LANGUAGE PLpgSQL;

query T noticetrace
CALL p();
----
NOTICE: p foo: 100 a: 200 b: <NULL>
NOTICE: p_nested x: 100 y: 300
NOTICE: p foo: 100 a: 400 b: 30000

statement ok
DROP PROCEDURE p;
DROP PROCEDURE p_nested;

statement ok
CREATE PROCEDURE p_nested(INOUT x INT) AS $$
  BEGIN
    x := x * 2;
  END
$$ LANGUAGE PLpgSQL;

statement ok
CREATE PROCEDURE p(a INT) AS $$
  BEGIN
    RAISE NOTICE '%', a;
    CALL p_nested(a);
    RAISE NOTICE '%', a;
    CALL p_nested(a);
    RAISE NOTICE '%', a;
    CALL p_nested(a);
    RAISE NOTICE '%', a;
  END
$$ LANGUAGE PLpgSQL;

query T noticetrace
CALL p(2);
----
NOTICE: 2
NOTICE: 4
NOTICE: 8
NOTICE: 16

# Test function with nested CALL.
statement ok
CREATE FUNCTION f(a INT) RETURNS INT AS $$
  BEGIN
    CALL p_nested(a);
    RETURN a;
  END
$$ LANGUAGE PLpgSQL;

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

query I
SELECT * FROM f(2);
----
4

statement ok
DROP FUNCTION f;
CREATE FUNCTION f(a INT) RETURNS RECORD AS $$
  DECLARE
    b INT := -2;
  BEGIN
    CALL p_nested(a);
    CALL p_nested(b);
    RETURN (a, b);
  END
$$ LANGUAGE PLpgSQL;

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

query II
SELECT * FROM f(2) AS g(x INT, y INT);
----
4  -4

statement ok
DROP FUNCTION f;
DROP PROCEDURE p;

statement ok
CREATE PROCEDURE p_inner(OUT p_param INT) AS $$ SELECT 1; $$ LANGUAGE SQL;

statement ok
CREATE OR REPLACE FUNCTION f(OUT f_param INT) AS $$ BEGIN CALL p_inner(f_param); END; $$ LANGUAGE PLpgSQL;

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

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

statement ok
DROP FUNCTION f;

statement ok
CREATE PROCEDURE p_outer(OUT p_outer_param INT) AS $$ BEGIN CALL p_inner(p_outer_param); END; $$ LANGUAGE PLpgSQL;

query I colnames
CALL p_outer(NULL);
----
p_outer_param
1

statement ok
CREATE OR REPLACE PROCEDURE p_outer(OUT p_outer_param INT) AS $$
DECLARE
  v INT;
BEGIN
  CALL p_inner(v);
END; $$ LANGUAGE PLpgSQL;

query I colnames
CALL p_outer(NULL);
----
p_outer_param
NULL

statement ok
CREATE OR REPLACE PROCEDURE p_outer(OUT p_outer_param INT) AS $$
DECLARE
  v INT;
BEGIN
  CALL p_inner(v);
  SELECT v INTO p_outer_param;
END; $$ LANGUAGE PLpgSQL;

query I colnames
CALL p_outer(NULL);
----
p_outer_param
1

statement ok
DROP PROCEDURE p_outer;
DROP PROCEDURE p_inner;

# Test multiple levels of nested procedures and functions.
statement ok
DROP PROCEDURE p_nested;
CREATE PROCEDURE p_nested(INOUT x INT) AS $$
  BEGIN
    x := x * 2;
    RAISE NOTICE 'p_nested %', x;
  END
$$ LANGUAGE PLpgSQL;

statement ok
CREATE FUNCTION f_nested(a INT) RETURNS INT AS $$
  BEGIN
    a := a * 2;
    RAISE NOTICE 'f_nested %', a;
    CALL p_nested(a);
    RETURN a;
  END
$$ LANGUAGE PLpgSQL;

statement ok
CREATE PROCEDURE p(a INOUT INT) AS $$
  BEGIN
    RAISE NOTICE 'p %', a;
    CALL p_nested(a);
    a := f_nested(a);
  END
$$ LANGUAGE PLpgSQL;

statement ok
CREATE FUNCTION f() RETURNS INT AS $$
  DECLARE
    a INT := 1;
  BEGIN
    RAISE NOTICE 'f %', a;
    CALL p_nested(a);
    a := f_nested(a);
    CALL p(a);
    RETURN a;
  END
$$ LANGUAGE PLpgSQL;

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

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

query T noticetrace
SELECT f();
----
NOTICE: f 1
NOTICE: p_nested 2
NOTICE: f_nested 4
NOTICE: p_nested 8
NOTICE: p 8
NOTICE: p_nested 16
NOTICE: f_nested 32
NOTICE: p_nested 64

statement ok
DROP FUNCTION f;
DROP PROCEDURE p;
DROP FUNCTION f_nested;
DROP PROCEDURE p_nested;

# Single composite-type OUT parameter.
statement ok
CREATE TYPE typ AS (a INT, b INT);
CREATE PROCEDURE p_nested(OUT x typ) AS $$
  BEGIN
    x := ROW(1, 2);
  END
$$ LANGUAGE PLpgSQL;

statement ok
CREATE PROCEDURE p(OUT x typ) AS $$
  BEGIN
    CALL p_nested(x);
  END
$$ LANGUAGE PLpgSQL;

query T
CALL p(NULL);
----
(1,2)

subtest errors

# Cannot DROP a procedure that is being used.
statement error pgcode 2BP01 pq: cannot drop function \"p_nested\" because other objects \(\[test.public.p\]\) still depend on it
DROP PROCEDURE p_nested;

statement ok
DROP PROCEDURE p;
DROP PROCEDURE p_nested;

# Recursive CALL is not possible.
statement error pgcode 42883 pq: procedure p does not exist
CREATE PROCEDURE p() AS $$
  BEGIN
    CALL p();
  END
$$ LANGUAGE PLpgSQL;

# Try making a recursive CALL using REPLACE syntax.
statement ok
CREATE PROCEDURE p() AS $$
  BEGIN
    RAISE NOTICE 'p';
  END
$$ LANGUAGE PLpgSQL;

statement error pgcode 42P13 pq: cannot add dependency from descriptor \d+ to function p \(\d+\) because there will be a dependency cycle
CREATE OR REPLACE PROCEDURE p() AS $$
  BEGIN
    CALL p();
  END
$$ LANGUAGE PLpgSQL;

statement ok
DROP PROCEDURE p;

# Cannot CALL a UDF.
statement ok
CREATE FUNCTION f() RETURNS INT LANGUAGE SQL AS $$ SELECT 1; $$;

statement error pgcode 42809 pq: f\(\) is not a procedure
CREATE PROCEDURE p() AS $$
  BEGIN
    CALL f();
  END
$$ LANGUAGE PLpgSQL;

# Cannot CALL a builtin function.
statement error pgcode 42809 pq: current_database\(\) is not a procedure
CREATE PROCEDURE p() AS $$
  BEGIN
    CALL current_database();
  END
$$ LANGUAGE PLpgSQL;

statement error pgcode 42809 pq: max is not a procedure
CREATE PROCEDURE p() AS $$
  DECLARE
    x INT;
  BEGIN
    CALL max(x);
  END
$$ LANGUAGE PLpgSQL;

# Cannot assign to the same variable twice in a CALL statement.
statement ok
CREATE PROCEDURE p_nested(OUT x INT, OUT y INT) AS $$
  BEGIN
    x := 1;
    y := 2;
  END
$$ LANGUAGE PLpgSQL;

statement error pgcode 0A000 pq: unimplemented: duplicate CALL target
CREATE PROCEDURE p() AS $$
  DECLARE
    foo INT := 100;
  BEGIN
    CALL p_nested(foo, foo);
  END
$$ LANGUAGE PLpgSQL;

# CALL with mismatched argument types.
statement error pgcode 42883 pq: procedure p_nested\(float, decimal\) does not exist
CREATE PROCEDURE p() AS $$
  DECLARE
    a FLOAT;
    b DECIMAL;
  BEGIN
    CALL p_nested(a, b);
    RAISE NOTICE 'a: % b: %', a, b;
  END
$$ LANGUAGE PLpgSQL;

# CALL with non-variable argument in OUT parameter position.
statement error pgcode 42601 pq: procedure parameter "y" is an output parameter but corresponding argument is not writable
CREATE PROCEDURE p() AS $$
  DECLARE
    a INT;
    b INT;
  BEGIN
    CALL p_nested(a, b - 1);
  END
$$ LANGUAGE PLpgSQL;

statement error pgcode 42601 pq: procedure parameter "y" is an output parameter but corresponding argument is not writable
CREATE PROCEDURE p() AS $$
  DECLARE
    a INT;
    b FLOAT;
  BEGIN
    CALL p_nested(a, b::INT);
  END
$$ LANGUAGE PLpgSQL;

statement ok
DROP PROCEDURE p_nested;

subtest end
