\set VERBOSITY terse
SET citus.next_shard_id TO 1508000;
SET citus.shard_replication_factor TO 1;
SET citus.enable_local_execution TO ON;
CREATE SCHEMA foreign_tables_schema_mx;
SET search_path TO foreign_tables_schema_mx;
SET client_min_messages to ERROR;
-- ensure that coordinator is added to pg_dist_node
SELECT 1 FROM master_add_node('localhost', :master_port, groupId => 0);
 ?column?
---------------------------------------------------------------------
        1
(1 row)

RESET client_min_messages;
-- test adding foreign table to metadata with the guc
SET citus.use_citus_managed_tables TO ON;
CREATE TABLE foreign_table_test (id integer NOT NULL, data text, a bigserial);
INSERT INTO foreign_table_test VALUES (1, 'text_test');
CREATE EXTENSION postgres_fdw;
CREATE SERVER foreign_server
        FOREIGN DATA WRAPPER postgres_fdw
        OPTIONS (host 'localhost', port :'master_port', dbname 'regression');
CREATE USER MAPPING FOR CURRENT_USER
        SERVER foreign_server
        OPTIONS (user 'postgres');
CREATE FOREIGN TABLE foreign_table (
        id integer NOT NULL,
        data text,
        a bigserial
)
        SERVER foreign_server
        OPTIONS (schema_name 'foreign_tables_schema_mx', table_name 'foreign_table_test');
--verify
SELECT partmethod, repmodel FROM pg_dist_partition WHERE logicalrelid = 'foreign_table'::regclass ORDER BY logicalrelid;
 partmethod | repmodel
---------------------------------------------------------------------
 n          | s
(1 row)

-- COPY FROM doesn't work for Citus foreign tables
COPY foreign_table FROM stdin;
ERROR:  cannot PREPARE a transaction that has operated on postgres_fdw foreign tables
CREATE TABLE parent_for_foreign_tables (
    project_id integer
) PARTITION BY HASH (project_id);
CREATE SERVER IF NOT EXISTS srv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (dbname 'regression', host 'localhost', port :'master_port');
CREATE SERVER IF NOT EXISTS srv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (dbname 'regression', host 'localhost', port :'master_port');
CREATE SERVER IF NOT EXISTS srv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (dbname 'regression', host 'localhost', port :'master_port');
CREATE FOREIGN TABLE foreign_partition_1 PARTITION OF parent_for_foreign_tables FOR VALUES WITH (modulus 3, remainder 0) SERVER srv1 OPTIONS (table_name 'dummy');
CREATE FOREIGN TABLE foreign_partition_2 PARTITION OF parent_for_foreign_tables FOR VALUES WITH (modulus 3, remainder 1) SERVER srv2 OPTIONS (table_name 'dummy');
CREATE FOREIGN TABLE foreign_partition_3 PARTITION OF parent_for_foreign_tables FOR VALUES WITH (modulus 3, remainder 2) SERVER srv3 OPTIONS (table_name 'dummy');
SELECT partmethod, repmodel FROM pg_dist_partition
    WHERE logicalrelid IN ('parent_for_foreign_tables'::regclass, 'foreign_partition_1'::regclass, 'foreign_partition_2'::regclass, 'foreign_partition_3'::regclass)
    ORDER BY logicalrelid;
 partmethod | repmodel
---------------------------------------------------------------------
 n          | s
 n          | s
 n          | s
 n          | s
(4 rows)

ALTER FOREIGN TABLE foreign_table SET SCHEMA public;
ALTER FOREIGN TABLE public.foreign_table RENAME TO foreign_table_newname;
ALTER FOREIGN TABLE public.foreign_table_newname RENAME COLUMN id TO id_test;
ALTER FOREIGN TABLE public.foreign_table_newname ADD dummy_col bigint NOT NULL DEFAULT 1;
ALTER FOREIGN TABLE public.foreign_table_newname ALTER dummy_col DROP DEFAULT;
ALTER FOREIGN TABLE public.foreign_table_newname ALTER dummy_col SET DEFAULT 2;
ALTER FOREIGN TABLE public.foreign_table_newname ALTER dummy_col TYPE int;
ALTER TABLE foreign_table_test RENAME COLUMN id TO id_test;
ALTER TABLE foreign_table_test ADD dummy_col int NOT NULL DEFAULT 1;
INSERT INTO public.foreign_table_newname VALUES (2, 'test_2');
INSERT INTO foreign_table_test VALUES (3, 'test_3');
ALTER FOREIGN TABLE public.foreign_table_newname ADD CONSTRAINT check_c check(id_test < 1000);
ALTER FOREIGN TABLE public.foreign_table_newname DROP constraint check_c;
ALTER FOREIGN TABLE public.foreign_table_newname ADD CONSTRAINT check_c_2 check(id_test < 1000) NOT VALID;
ALTER FOREIGN TABLE public.foreign_table_newname VALIDATE CONSTRAINT check_c_2;
ALTER FOREIGN TABLE public.foreign_table_newname DROP constraint IF EXISTS check_c_2;
-- trigger test
CREATE TABLE table42(value int);
CREATE FUNCTION insert_42() RETURNS trigger AS $insert_42$
BEGIN
    INSERT INTO table42 VALUES (42);
    RETURN NEW;
END;
$insert_42$ LANGUAGE plpgsql;
CREATE TRIGGER insert_42_trigger
AFTER DELETE ON public.foreign_table_newname
FOR EACH ROW EXECUTE FUNCTION insert_42();
-- do the same pattern from the workers as well
INSERT INTO public.foreign_table_newname VALUES (99, 'test_2');
delete from public.foreign_table_newname where id_test = 99;
select * from table42 ORDER BY value;
 value
---------------------------------------------------------------------
    42
(1 row)

-- disable trigger
alter foreign table public.foreign_table_newname disable trigger insert_42_trigger;
INSERT INTO public.foreign_table_newname VALUES (99, 'test_2');
delete from public.foreign_table_newname where id_test = 99;
-- should not insert again as trigger disabled
select * from table42 ORDER BY value;
 value
---------------------------------------------------------------------
    42
(1 row)

DROP TRIGGER insert_42_trigger ON public.foreign_table_newname;
-- should throw errors
select alter_table_set_access_method('public.foreign_table_newname', 'columnar');
ERROR:  cannot complete operation because it is a foreign table
select alter_distributed_table('public.foreign_table_newname', shard_count:=4);
ERROR:  cannot alter table because the table is not distributed
ALTER FOREIGN TABLE public.foreign_table_newname OWNER TO pg_monitor;
SELECT run_command_on_workers($$select r.rolname from pg_roles r join pg_class c on r.oid=c.relowner where relname = 'foreign_table_newname';$$);
     run_command_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,pg_monitor)
 (localhost,57638,t,pg_monitor)
(2 rows)

ALTER FOREIGN TABLE public.foreign_table_newname OWNER TO postgres;
SELECT run_command_on_workers($$select r.rolname from pg_roles r join pg_class c on r.oid=c.relowner where relname = 'foreign_table_newname';$$);
    run_command_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,postgres)
 (localhost,57638,t,postgres)
(2 rows)

\c - - - :worker_1_port
SET search_path TO foreign_tables_schema_mx;
SELECT * FROM public.foreign_table_newname ORDER BY id_test;
 id_test |   data    | a | dummy_col
---------------------------------------------------------------------
       1 | text_test | 1 |         1
       2 | test_2    | 1 |         2
       3 | test_3    | 2 |         1
(3 rows)

SELECT * FROM foreign_table_test ORDER BY id_test;
 id_test |   data    | a | dummy_col
---------------------------------------------------------------------
       1 | text_test | 1 |         1
       2 | test_2    | 1 |         2
       3 | test_3    | 2 |         1
(3 rows)

-- should error out
ALTER FOREIGN TABLE public.foreign_table_newname DROP COLUMN id;
ERROR:  operation is not allowed on this node
SELECT partmethod, repmodel FROM pg_dist_partition
    WHERE logicalrelid IN ('parent_for_foreign_tables'::regclass, 'foreign_partition_1'::regclass, 'foreign_partition_2'::regclass, 'foreign_partition_3'::regclass)
    ORDER BY logicalrelid;
 partmethod | repmodel
---------------------------------------------------------------------
 n          | s
 n          | s
 n          | s
 n          | s
(4 rows)

\c - - - :master_port
ALTER FOREIGN TABLE foreign_table_newname RENAME TO foreign_table;
SET search_path TO foreign_tables_schema_mx;
ALTER FOREIGN TABLE public.foreign_table SET SCHEMA foreign_tables_schema_mx;
ALTER FOREIGN TABLE IF EXISTS foreign_table RENAME COLUMN id_test TO id;
ALTER TABLE foreign_table_test RENAME COLUMN id_test TO id;
ALTER FOREIGN TABLE foreign_table DROP COLUMN id;
ALTER FOREIGN TABLE foreign_table DROP COLUMN dummy_col;
ALTER TABLE foreign_table_test DROP COLUMN dummy_col;
ALTER FOREIGN TABLE foreign_table OPTIONS (DROP schema_name, SET table_name 'notable');
SELECT run_command_on_workers($$SELECT f.ftoptions FROM pg_foreign_table f JOIN pg_class c ON f.ftrelid=c.oid WHERE c.relname = 'foreign_table';$$);
          run_command_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,{table_name=notable})
 (localhost,57638,t,{table_name=notable})
(2 rows)

ALTER FOREIGN TABLE foreign_table OPTIONS (ADD schema_name 'foreign_tables_schema_mx', SET table_name 'foreign_table_test');
SELECT * FROM foreign_table ORDER BY a;
   data    | a
---------------------------------------------------------------------
 text_test | 1
 test_2    | 1
 test_3    | 2
(3 rows)

-- test alter user mapping
ALTER USER MAPPING FOR postgres SERVER foreign_server OPTIONS (SET user 'nonexistiniguser');
-- should fail
SELECT * FROM foreign_table ORDER BY a;
ERROR:  could not connect to server "foreign_server"
ALTER USER MAPPING FOR postgres SERVER foreign_server OPTIONS (SET user 'postgres');
-- test undistributing
DELETE FROM foreign_table;
SELECT undistribute_table('foreign_table');
NOTICE:  creating a new table for foreign_tables_schema_mx.foreign_table
NOTICE:  dropping the old foreign_tables_schema_mx.foreign_table
NOTICE:  renaming the new table to foreign_tables_schema_mx.foreign_table
 undistribute_table
---------------------------------------------------------------------

(1 row)

-- both should error out
SELECT create_distributed_table('foreign_table','data');
ERROR:  foreign tables cannot be distributed
SELECT create_reference_table('foreign_table');
ERROR:  foreign tables cannot be distributed
INSERT INTO foreign_table_test VALUES (1, 'testt');
SELECT * FROM foreign_table ORDER BY a;
 data  | a
---------------------------------------------------------------------
 testt | 3
(1 row)

SELECT * FROM foreign_table_test ORDER BY a;
 id | data  | a
---------------------------------------------------------------------
  1 | testt | 3
(1 row)

DROP TABLE parent_for_foreign_tables;
CREATE TABLE parent_for_foreign_tables (id integer NOT NULL, data text, a bigserial)
    PARTITION BY HASH (id);
CREATE FOREIGN TABLE foreign_partition_1 PARTITION OF parent_for_foreign_tables FOR VALUES WITH (modulus 3, remainder 0) SERVER srv1 OPTIONS (schema_name 'foreign_tables_schema_mx', table_name 'foreign_table_test');
CREATE FOREIGN TABLE foreign_partition_2 PARTITION OF parent_for_foreign_tables FOR VALUES WITH (modulus 3, remainder 1) SERVER srv2 OPTIONS (schema_name 'foreign_tables_schema_mx', table_name 'foreign_table_test');
SELECT citus_add_local_table_to_metadata('parent_for_foreign_tables');
 citus_add_local_table_to_metadata
---------------------------------------------------------------------

(1 row)

CREATE FOREIGN TABLE foreign_partition_3 PARTITION OF parent_for_foreign_tables FOR VALUES WITH (modulus 3, remainder 2) SERVER srv2 OPTIONS (schema_name 'foreign_tables_schema_mx', table_name 'foreign_table_test');
SELECT partmethod, repmodel FROM pg_dist_partition
    WHERE logicalrelid IN ('parent_for_foreign_tables'::regclass, 'foreign_partition_1'::regclass, 'foreign_partition_2'::regclass, 'foreign_partition_3'::regclass)
    ORDER BY logicalrelid;
 partmethod | repmodel
---------------------------------------------------------------------
 n          | s
 n          | s
 n          | s
 n          | s
(4 rows)

CREATE USER MAPPING FOR CURRENT_USER
        SERVER srv1
        OPTIONS (user 'postgres');
CREATE USER MAPPING FOR CURRENT_USER
        SERVER srv2
        OPTIONS (user 'postgres');
SELECT * FROM parent_for_foreign_tables ORDER BY id;
 id | data  | a
---------------------------------------------------------------------
  1 | testt | 3
  1 | testt | 3
  1 | testt | 3
(3 rows)

SELECT * FROM foreign_partition_1 ORDER BY id;
 id | data  | a
---------------------------------------------------------------------
  1 | testt | 3
(1 row)

SELECT * FROM foreign_partition_2 ORDER BY id;
 id | data  | a
---------------------------------------------------------------------
  1 | testt | 3
(1 row)

SELECT * FROM foreign_partition_3 ORDER BY id;
 id | data  | a
---------------------------------------------------------------------
  1 | testt | 3
(1 row)

\c - - - :worker_1_port
SET search_path TO foreign_tables_schema_mx;
SELECT partmethod, repmodel FROM pg_dist_partition
    WHERE logicalrelid IN ('parent_for_foreign_tables'::regclass, 'foreign_partition_1'::regclass, 'foreign_partition_2'::regclass, 'foreign_partition_3'::regclass)
    ORDER BY logicalrelid;
 partmethod | repmodel
---------------------------------------------------------------------
 n          | s
 n          | s
 n          | s
 n          | s
(4 rows)

SELECT * FROM parent_for_foreign_tables ORDER BY id;
 id | data  | a
---------------------------------------------------------------------
  1 | testt | 3
  1 | testt | 3
  1 | testt | 3
(3 rows)

SELECT * FROM foreign_partition_1 ORDER BY id;
 id | data  | a
---------------------------------------------------------------------
  1 | testt | 3
(1 row)

SELECT * FROM foreign_partition_2 ORDER BY id;
 id | data  | a
---------------------------------------------------------------------
  1 | testt | 3
(1 row)

SELECT * FROM foreign_partition_3 ORDER BY id;
 id | data  | a
---------------------------------------------------------------------
  1 | testt | 3
(1 row)

\c - - - :master_port
SET search_path TO foreign_tables_schema_mx;
--verify
SELECT partmethod, repmodel FROM pg_dist_partition WHERE logicalrelid = 'foreign_table'::regclass ORDER BY logicalrelid;
 partmethod | repmodel
---------------------------------------------------------------------
(0 rows)

CREATE SERVER foreign_server_local
        FOREIGN DATA WRAPPER postgres_fdw
        OPTIONS (host 'localhost', port :'master_port', dbname 'regression');
CREATE USER MAPPING FOR CURRENT_USER
        SERVER foreign_server_local
        OPTIONS (user 'postgres');
CREATE FOREIGN TABLE foreign_table_local (
        id integer NOT NULL,
        data text
)
        SERVER foreign_server_local
        OPTIONS (schema_name 'foreign_tables_schema_mx', table_name 'foreign_table_test');
CREATE TABLE dist_tbl(a int);
INSERT INTO dist_tbl VALUES (1);
SELECT create_distributed_table('dist_tbl','a');
NOTICE:  Copying data from local table...
NOTICE:  copying the data has completed
 create_distributed_table
---------------------------------------------------------------------

(1 row)

SELECT * FROM dist_tbl d JOIN foreign_table_local f ON d.a=f.id ORDER BY f.id;
 a | id | data
---------------------------------------------------------------------
 1 |  1 | testt
(1 row)

CREATE TABLE ref_tbl(a int);
INSERT INTO ref_tbl VALUES (1);
SELECT create_reference_table('ref_tbl');
NOTICE:  Copying data from local table...
NOTICE:  copying the data has completed
 create_reference_table
---------------------------------------------------------------------

(1 row)

SELECT * FROM ref_tbl d JOIN foreign_table_local f ON d.a=f.id ORDER BY f.id;
 a | id | data
---------------------------------------------------------------------
 1 |  1 | testt
(1 row)

SELECT citus_add_local_table_to_metadata('foreign_table_local');
 citus_add_local_table_to_metadata
---------------------------------------------------------------------

(1 row)

\c - - - :worker_1_port
SET search_path TO foreign_tables_schema_mx;
SELECT * FROM dist_tbl d JOIN foreign_table_local f ON d.a=f.id ORDER BY f.id;
 a | id | data
---------------------------------------------------------------------
 1 |  1 | testt
(1 row)

SELECT * FROM ref_tbl d JOIN foreign_table_local f ON d.a=f.id ORDER BY f.id;
 a | id | data
---------------------------------------------------------------------
 1 |  1 | testt
(1 row)

\c - - - :master_port
SET search_path TO foreign_tables_schema_mx;
CREATE FOREIGN TABLE foreign_table_local_fails (
        id integer NOT NULL,
        data text
)
        SERVER foreign_server_local
        OPTIONS (schema_name 'foreign_tables_schema_mx');
-- should error out because doesn't have a table_name field
SELECT citus_add_local_table_to_metadata('foreign_table_local_fails');
ERROR:  table_name option must be provided when using postgres_fdw with Citus
-- should work since it has a table_name
ALTER FOREIGN TABLE foreign_table_local_fails OPTIONS (table_name 'foreign_table_test');
SELECT citus_add_local_table_to_metadata('foreign_table_local_fails');
 citus_add_local_table_to_metadata
---------------------------------------------------------------------

(1 row)

INSERT INTO foreign_table_test VALUES (1, 'test');
SELECT undistribute_table('foreign_table_local_fails');
NOTICE:  creating a new table for foreign_tables_schema_mx.foreign_table_local_fails
NOTICE:  dropping the old foreign_tables_schema_mx.foreign_table_local_fails
NOTICE:  renaming the new table to foreign_tables_schema_mx.foreign_table_local_fails
 undistribute_table
---------------------------------------------------------------------

(1 row)

DROP FOREIGN TABLE foreign_table_local;
-- disallow dropping table_name when foreign table is in metadata
CREATE TABLE table_name_drop(id int);
CREATE FOREIGN TABLE foreign_table_name_drop_fails (
        id INT
)
        SERVER foreign_server_local
        OPTIONS (schema_name 'foreign_tables_schema_mx', table_name 'table_name_drop');
SELECT citus_add_local_table_to_metadata('foreign_table_name_drop_fails');
 citus_add_local_table_to_metadata
---------------------------------------------------------------------

(1 row)

-- table_name option is already added
ALTER FOREIGN TABLE foreign_table_name_drop_fails OPTIONS (ADD table_name 'table_name_drop');
ERROR:  option "table_name" provided more than once
-- throw error if user tries to drop table_name option from a foreign table inside metadata
ALTER FOREIGN TABLE foreign_table_name_drop_fails OPTIONS (DROP table_name);
ERROR:  alter foreign table alter options (drop table_name) command is not allowed for Citus tables
-- case sensitive option name
ALTER FOREIGN TABLE foreign_table_name_drop_fails OPTIONS (DROP Table_Name);
ERROR:  alter foreign table alter options (drop table_name) command is not allowed for Citus tables
-- other options are allowed to drop
ALTER FOREIGN TABLE foreign_table_name_drop_fails OPTIONS (DROP schema_name);
CREATE FOREIGN TABLE foreign_table_name_drop (
        id INT
)
        SERVER foreign_server_local
        OPTIONS (schema_name 'foreign_tables_schema_mx', table_name 'table_name_drop');
-- user can drop table_option if foreign table is not in metadata
ALTER FOREIGN TABLE foreign_table_name_drop OPTIONS (DROP table_name);
-- we should not intercept data wrappers other than postgres_fdw
CREATE EXTENSION file_fdw;
-- remove validator method to add table_name option; otherwise, table_name option is not allowed
SELECT result FROM run_command_on_all_nodes('ALTER FOREIGN DATA WRAPPER file_fdw NO VALIDATOR');
           result
---------------------------------------------------------------------
 ALTER FOREIGN DATA WRAPPER
 ALTER FOREIGN DATA WRAPPER
 ALTER FOREIGN DATA WRAPPER
(3 rows)

CREATE SERVER citustest FOREIGN DATA WRAPPER file_fdw;
\copy (select i from generate_series(0,100)i) to '/tmp/test_file_fdw.data';
CREATE FOREIGN TABLE citustest_filefdw (
        data text
)
        SERVER citustest
        OPTIONS ( filename '/tmp/test_file_fdw.data');
-- add non-postgres_fdw table into metadata even if it does not have table_name option
SELECT citus_add_local_table_to_metadata('citustest_filefdw');
 citus_add_local_table_to_metadata
---------------------------------------------------------------------

(1 row)

ALTER FOREIGN TABLE citustest_filefdw OPTIONS (ADD table_name 'unused_table_name_option');
-- drop table_name option of non-postgres_fdw table even if it is inside metadata
ALTER FOREIGN TABLE citustest_filefdw OPTIONS (DROP table_name);
-- cleanup at exit
set client_min_messages to error;
DROP SCHEMA foreign_tables_schema_mx CASCADE;
