\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) 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;