--
-- Regression tests for deparsing ALTER/DROP FUNCTION Queries
--
-- This test implements all the possible queries as of Postgres 11
-- in the order they are listed in the docs
--
-- ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
--     action [ ... ] [ RESTRICT ]
-- ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
--     RENAME TO new_name
-- ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
--     OWNER TO { new_owner | CURRENT_USER | SESSION_USER }
-- ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
--     SET SCHEMA new_schema
-- ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
--     DEPENDS ON EXTENSION extension_name
--
-- where action is one of:
--
--     CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
--     IMMUTABLE | STABLE | VOLATILE | [ NOT ] LEAKPROOF
--     [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
--     PARALLEL { UNSAFE | RESTRICTED | SAFE }
--     COST execution_cost
--     ROWS result_rows
--     SET configuration_parameter { TO | = } { value | DEFAULT }
--     SET configuration_parameter FROM CURRENT
--     RESET configuration_parameter
--     RESET ALL
--
-- DROP FUNCTION [ IF EXISTS ] name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] [, ...]
--     [ CASCADE | RESTRICT ]
SET citus.next_shard_id TO 20020000;
SET citus.enable_ddl_propagation TO off;
CREATE SCHEMA function_tests;
SET search_path TO function_tests;
SET citus.shard_count TO 4;
SET client_min_messages TO INFO;
CREATE FUNCTION deparse_test(text)
	RETURNS text
	AS 'citus'
 	LANGUAGE C STRICT;
CREATE OR REPLACE FUNCTION deparse_and_run_on_workers(IN query text,
                                                     OUT nodename text,
                                                     OUT nodeport int,
                                                     OUT success bool,
                                                     OUT result text)
    RETURNS SETOF record
    LANGUAGE PLPGSQL AS $fnc$
    DECLARE
        deparsed_query character varying(255);
    BEGIN
        deparsed_query := ( SELECT deparse_test($1) );
        RAISE INFO 'Propagating deparsed query: %', deparsed_query;
        RETURN QUERY SELECT * FROM run_command_on_workers(deparsed_query);
    END;
    $fnc$;
-- Create a simple function and distribute it
CREATE FUNCTION add(integer, integer) RETURNS integer
    AS 'select $1 + $2;'
    LANGUAGE SQL
    IMMUTABLE
    RETURNS NULL ON NULL INPUT;
-- Since deparse logic on workers can not work for if function
-- is distributed on workers, we are disabling object propagation
-- first. Same trick has been applied multiple times in this test.
SET citus.enable_metadata_sync TO OFF;
SELECT create_distributed_function('add(int,int)');
 create_distributed_function
---------------------------------------------------------------------

(1 row)

RESET citus.enable_metadata_sync;
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION  add CALLED ON NULL INPUT
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) CALLED ON NULL INPUT;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

-- RETURNS NULL ON NULL INPUT and STRICT are synonyms and can be used interchangeably
-- RETURNS NULL ON NULL INPUT is actually stored as STRICT in the query parse tree
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add RETURNS NULL ON NULL INPUT
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) STRICT;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add STRICT
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) STRICT;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add IMMUTABLE
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) IMMUTABLE;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add STABLE
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) STABLE;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add VOLATILE
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) VOLATILE;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add LEAKPROOF
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) LEAKPROOF;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add NOT LEAKPROOF
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) NOT LEAKPROOF;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

-- EXTERNAL keyword is ignored by Postgres Parser. It is allowed only for SQL conformance
-- The following queries will not have the EXTERNAL keyword after deparsing
SELECT deparse_and_run_on_workers($cmd$
ALTER  FUNCTION add EXTERNAL SECURITY INVOKER
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) SECURITY INVOKER;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add SECURITY INVOKER
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) SECURITY INVOKER;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER  FUNCTION add EXTERNAL SECURITY DEFINER
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) SECURITY DEFINER;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add SECURITY DEFINER
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) SECURITY DEFINER;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add PARALLEL UNSAFE
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) PARALLEL UNSAFE;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add PARALLEL RESTRICTED
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) PARALLEL RESTRICTED;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add PARALLEL SAFE
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) PARALLEL SAFE;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

-- The COST arguments should always be numeric
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add COST 1234
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) COST 1234.000000;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER  FUNCTION add COST 1234.5
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) COST 1234.500000;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION  add SET log_min_messages = ERROR
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) SET log_min_messages = 'error';
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION  add SET log_min_messages TO DEFAULT
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) SET log_min_messages TO DEFAULT;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION  add SET log_min_messages FROM CURRENT
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) SET log_min_messages FROM CURRENT;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add(int, int) SET TIME ZONE INTERVAL '-08:00' HOUR TO MINUTE;
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) SET TIME ZONE INTERVAL '@ 8 hours ago';
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add(int, int) SET TIME ZONE '-7';
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) SET timezone = '-7';
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add RESET log_min_messages
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) RESET log_min_messages;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add RESET ALL
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) RESET ALL;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

-- Rename the function in the workers
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add RENAME TO summation
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) RENAME TO summation;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

-- Rename the function inb the coordinator as well.
-- This is needed so the next query is parsed on the coordinator
ALTER FUNCTION add RENAME TO summation;
-- Rename it back to the original so that the next tests can pass
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION summation RENAME TO add
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.summation(integer,integer) RENAME TO add;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

-- Rename the function back to the original name in the coordinator
ALTER FUNCTION summation RENAME TO add;
SET citus.enable_ddl_propagation TO on;
CREATE ROLE function_role;
SET citus.enable_ddl_propagation TO off;
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add OWNER TO function_role
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) OWNER TO function_role;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add OWNER TO missing_role
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) OWNER TO missing_role;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
                     deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,f,"ERROR:  role ""missing_role"" does not exist")
 (localhost,57638,f,"ERROR:  role ""missing_role"" does not exist")
(2 rows)

-- SET the schema in workers as well as the coordinator so that it remains in the same schema
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add SET SCHEMA public
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) SET SCHEMA public;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

ALTER FUNCTION add SET SCHEMA public;
-- Revert the schema back
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION public.add SET SCHEMA function_tests
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION public.add(integer,integer) SET SCHEMA function_tests;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

ALTER FUNCTION public.add SET SCHEMA function_tests;
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add DEPENDS ON EXTENSION citus
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) DEPENDS ON EXTENSION citus;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

-- make sure "any" type is correctly deparsed
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION pg_catalog.get_shard_id_for_distribution_column(table_name regclass, distribution_value "any") PARALLEL SAFE;
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION pg_catalog.get_shard_id_for_distribution_column(pg_catalog.regclass,pg_catalog."any") PARALLEL SAFE;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

-- Do not run valid drop queries in the workers
SELECT deparse_test($cmd$
DROP FUNCTION add(int,int);
$cmd$);
                    deparse_test
---------------------------------------------------------------------
 DROP FUNCTION function_tests.add(integer,integer);
(1 row)

-- have multiple actions in a single query
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add volatile leakproof SECURITY DEFINER PARALLEL unsafe;
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.add(integer,integer) VOLATILE LEAKPROOF SECURITY DEFINER PARALLEL UNSAFE;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

-- Check that an invalid function name is still parsed correctly
-- Test that it fails when run without IF EXISTS clause
SELECT deparse_and_run_on_workers($cmd$
DROP FUNCTION missing_function(int, text);
$cmd$);
INFO:  Propagating deparsed query: DROP FUNCTION missing_function(pg_catalog.int4,text);
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
                              deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,f,"ERROR:  function missing_function(integer, text) does not exist")
 (localhost,57638,f,"ERROR:  function missing_function(integer, text) does not exist")
(2 rows)

-- Check that an invalid function name is still parsed correctly
-- Test that it is successful when run with IF EXISTS clause
SELECT deparse_and_run_on_workers($cmd$
DROP FUNCTION IF EXISTS missing_function(int, text);
$cmd$);
INFO:  Propagating deparsed query: DROP FUNCTION IF EXISTS missing_function(pg_catalog.int4,text);
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
     deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"DROP FUNCTION")
 (localhost,57638,t,"DROP FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
DROP FUNCTION IF EXISTS missing_schema.missing_function(int,float);
$cmd$);
INFO:  Propagating deparsed query: DROP FUNCTION IF EXISTS missing_schema.missing_function(pg_catalog.int4,pg_catalog.float8);
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
     deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"DROP FUNCTION")
 (localhost,57638,t,"DROP FUNCTION")
(2 rows)

SELECT deparse_and_run_on_workers($cmd$
DROP FUNCTION IF EXISTS missing_func_without_args;
$cmd$);
INFO:  Propagating deparsed query: DROP FUNCTION IF EXISTS missing_func_without_args;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
     deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"DROP FUNCTION")
 (localhost,57638,t,"DROP FUNCTION")
(2 rows)

set citus.enable_ddl_propagation to on;
-- create schema with weird names
CREATE SCHEMA "CiTuS.TeeN";
CREATE SCHEMA "CiTUS.TEEN2";
set citus.enable_ddl_propagation to off;
-- create table with weird names
CREATE FUNCTION "CiTuS.TeeN"."TeeNFunCT10N.1!?!"() RETURNS TEXT
    AS $$ SELECT 'test function without params' $$
    LANGUAGE SQL;
CREATE FUNCTION "CiTuS.TeeN"."TeeNFunCT10N.1!?!"(text) RETURNS TEXT
    AS $$ SELECT 'Overloaded function called with param: ' || $1 $$
    LANGUAGE SQL;
SET citus.enable_metadata_sync TO OFF;
SELECT create_distributed_function('"CiTuS.TeeN"."TeeNFunCT10N.1!?!"()');
 create_distributed_function
---------------------------------------------------------------------

(1 row)

SELECT create_distributed_function('"CiTuS.TeeN"."TeeNFunCT10N.1!?!"(text)');
 create_distributed_function
---------------------------------------------------------------------

(1 row)

RESET citus.enable_metadata_sync;
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION "CiTuS.TeeN"."TeeNFunCT10N.1!?!"() SET SCHEMA "CiTUS.TEEN2"
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION "CiTuS.TeeN"."TeeNFunCT10N.1!?!"() SET SCHEMA "CiTUS.TEEN2";
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

-- drop 2 functions at the same time
SELECT deparse_and_run_on_workers($cmd$
DROP FUNCTION "CiTUS.TEEN2"."TeeNFunCT10N.1!?!"(),"CiTuS.TeeN"."TeeNFunCT10N.1!?!"(text);
$cmd$);
INFO:  Propagating deparsed query: DROP FUNCTION "CiTUS.TEEN2"."TeeNFunCT10N.1!?!"(), "CiTuS.TeeN"."TeeNFunCT10N.1!?!"(pg_catalog.text);
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
     deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"DROP FUNCTION")
 (localhost,57638,t,"DROP FUNCTION")
(2 rows)

-- a function with a default parameter
CREATE FUNCTION func_default_param(param INT DEFAULT 0) RETURNS TEXT
    AS $$ SELECT 'supplied param is : ' || param; $$
    LANGUAGE SQL;
SET citus.enable_metadata_sync TO OFF;
SELECT create_distributed_function('func_default_param(INT)');
 create_distributed_function
---------------------------------------------------------------------

(1 row)

RESET citus.enable_metadata_sync;
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION func_default_param RENAME TO func_with_default_param;
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.func_default_param(integer) RENAME TO func_with_default_param;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

-- a function with IN and OUT parameters
CREATE FUNCTION func_out_param(IN param INT, OUT result TEXT)
    AS $$ SELECT 'supplied param is : ' || param; $$
    LANGUAGE SQL;
SET citus.enable_metadata_sync TO OFF;
SELECT create_distributed_function('func_out_param(INT)');
 create_distributed_function
---------------------------------------------------------------------

(1 row)

RESET citus.enable_metadata_sync;
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION func_out_param RENAME TO func_in_and_out_param;
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.func_out_param(integer) RENAME TO func_in_and_out_param;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

-- a function with INOUT parameter
CREATE FUNCTION square(INOUT a NUMERIC)
AS $$
BEGIN
   a := a * a;
END; $$
LANGUAGE plpgsql;
SET citus.enable_metadata_sync TO OFF;
SELECT create_distributed_function('square(NUMERIC)');
 create_distributed_function
---------------------------------------------------------------------

(1 row)

RESET citus.enable_metadata_sync;
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION square SET search_path TO DEFAULT;
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.square(numeric) SET search_path TO DEFAULT;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

-- a function with variadic input.
CREATE FUNCTION sum_avg(
   VARIADIC list NUMERIC[],
   OUT total NUMERIC,
   OUT average NUMERIC)
AS $$
BEGIN
   SELECT INTO total SUM(list[i])
   FROM generate_subscripts(list, 1) g(i);

   SELECT INTO average AVG(list[i])
   FROM generate_subscripts(list, 1) g(i);
END; $$
LANGUAGE plpgsql;
SET citus.enable_metadata_sync TO OFF;
SELECT create_distributed_function('sum_avg(NUMERIC[])');
 create_distributed_function
---------------------------------------------------------------------

(1 row)

RESET citus.enable_metadata_sync;
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION sum_avg COST 10000;
$cmd$);
INFO:  Propagating deparsed query: ALTER FUNCTION function_tests.sum_avg(numeric[]) COST 10000.000000;
CONTEXT:  PL/pgSQL function deparse_and_run_on_workers(text) line XX at RAISE
      deparse_and_run_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"ALTER FUNCTION")
 (localhost,57638,t,"ALTER FUNCTION")
(2 rows)

-- a function with a custom type IN parameter
SET citus.enable_ddl_propagation TO on;
CREATE TYPE intpair AS (x int, y int);
RESET citus.enable_ddl_propagation;
CREATE FUNCTION func_custom_param(IN param intpair, OUT total INT)
    AS $$ SELECT param.x + param.y $$
    LANGUAGE SQL;
SET citus.enable_metadata_sync TO OFF;
SELECT create_distributed_function('func_custom_param(intpair)');
NOTICE:  procedure function_tests.func_custom_param is already distributed
DETAIL:  Citus distributes procedures with CREATE [PROCEDURE|FUNCTION|AGGREGATE] commands
 create_distributed_function
---------------------------------------------------------------------

(1 row)

RESET citus.enable_metadata_sync;
ALTER FUNCTION func_custom_param RENAME TO func_with_custom_param;
-- a function that returns TABLE
CREATE FUNCTION func_returns_table(IN count INT)
    RETURNS TABLE (x INT, y INT)
    AS $$ SELECT i,i FROM generate_series(1,count) i $$
    LANGUAGE SQL;
SET citus.enable_metadata_sync TO OFF;
SELECT create_distributed_function('func_returns_table(INT)');
NOTICE:  procedure function_tests.func_returns_table is already distributed
DETAIL:  Citus distributes procedures with CREATE [PROCEDURE|FUNCTION|AGGREGATE] commands
 create_distributed_function
---------------------------------------------------------------------

(1 row)

RESET citus.enable_metadata_sync;
ALTER FUNCTION func_returns_table ROWS 100;
-- clear objects
SET client_min_messages TO WARNING; -- suppress cascading objects dropping
DROP SCHEMA "CiTuS.TeeN" CASCADE;
DROP SCHEMA "CiTUS.TEEN2" CASCADE;
DROP SCHEMA function_tests CASCADE;
DROP ROLE function_role;
