From 5c658b4eb7da882e811304d479ac8a5ee5c42799 Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Tue, 5 Sep 2023 19:42:39 +0300 Subject: [PATCH] PG16 - Add citus_truncate_trigger for Citus foreign tables (#7170) Since in PG16, truncate triggers are supported on foreign tables, we add the citus_truncate_trigger to Citus foreign tables as well, such that the TRUNCATE command is propagated to the table's single local shard as well. Note that TRUNCATE command was working for foreign tables even before this commit: see https://github.com/citusdata/citus/pull/7170#issuecomment-1706240593 for details This commit also adds tests with user-enabled truncate triggers on Citus foreign tables: both trigger on the shell table and on its single foreign local shard. Relevant PG commit: https://github.com/postgres/postgres/commit/3b00a94 --- .../citus_add_local_table_to_metadata.c | 9 +++ .../commands/create_distributed_table.c | 9 +++ src/test/regress/expected/pg16.out | 75 +++++++++++++++++++ src/test/regress/sql/pg16.sql | 58 ++++++++++++++ 4 files changed, 151 insertions(+) diff --git a/src/backend/distributed/commands/citus_add_local_table_to_metadata.c b/src/backend/distributed/commands/citus_add_local_table_to_metadata.c index ebc69d19b..c713ce099 100644 --- a/src/backend/distributed/commands/citus_add_local_table_to_metadata.c +++ b/src/backend/distributed/commands/citus_add_local_table_to_metadata.c @@ -1478,11 +1478,20 @@ InsertMetadataForCitusLocalTable(Oid citusLocalTableId, uint64 shardId, static void FinalizeCitusLocalTableCreation(Oid relationId) { +#if PG_VERSION_NUM >= PG_VERSION_16 + + /* + * PG16+ supports truncate triggers on foreign tables + */ + if (RegularTable(relationId) || IsForeignTable(relationId)) +#else + /* * If it is a foreign table, then skip creating citus truncate trigger * as foreign tables do not support truncate triggers. */ if (RegularTable(relationId)) +#endif { CreateTruncateTrigger(relationId); } diff --git a/src/backend/distributed/commands/create_distributed_table.c b/src/backend/distributed/commands/create_distributed_table.c index ff02593f5..dc06692b3 100644 --- a/src/backend/distributed/commands/create_distributed_table.c +++ b/src/backend/distributed/commands/create_distributed_table.c @@ -1256,8 +1256,17 @@ CreateCitusTable(Oid relationId, CitusTableType tableType, colocationId, citusTableParams.replicationModel, autoConverted); +#if PG_VERSION_NUM >= PG_VERSION_16 + + /* + * PG16+ supports truncate triggers on foreign tables + */ + if (RegularTable(relationId) || IsForeignTable(relationId)) +#else + /* foreign tables do not support TRUNCATE trigger */ if (RegularTable(relationId)) +#endif { CreateTruncateTrigger(relationId); } diff --git a/src/test/regress/expected/pg16.out b/src/test/regress/expected/pg16.out index 8c0fdc859..27abfdf7a 100644 --- a/src/test/regress/expected/pg16.out +++ b/src/test/regress/expected/pg16.out @@ -314,6 +314,80 @@ SELECT result FROM run_command_on_workers (2 rows) SET search_path TO pg16; +SET citus.next_shard_id TO 951000; +-- Foreign table TRUNCATE trigger +-- Relevant PG commit: +-- https://github.com/postgres/postgres/commit/3b00a94 +SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +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 'pg16', table_name 'foreign_table_test'); +-- verify it's a Citus foreign table +SELECT partmethod, repmodel FROM pg_dist_partition +WHERE logicalrelid = 'foreign_table'::regclass ORDER BY logicalrelid; + partmethod | repmodel +--------------------------------------------------------------------- + n | s +(1 row) + +INSERT INTO foreign_table VALUES (2, 'test_2'); +INSERT INTO foreign_table_test VALUES (3, 'test_3'); +CREATE FUNCTION trigger_func() RETURNS trigger LANGUAGE plpgsql AS $$ +BEGIN + RAISE NOTICE 'trigger_func(%) called: action = %, when = %, level = %', + TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL; + RETURN NULL; +END;$$; +CREATE FUNCTION trigger_func_on_shard() RETURNS trigger LANGUAGE plpgsql AS $$ +BEGIN + RAISE NOTICE 'trigger_func_on_shard(%) called: action = %, when = %, level = %', + TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL; + RETURN NULL; +END;$$; +CREATE TRIGGER trig_stmt_before BEFORE TRUNCATE ON foreign_table + FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func(); +SET citus.override_table_visibility TO off; +CREATE TRIGGER trig_stmt_shard_before BEFORE TRUNCATE ON foreign_table_951001 + FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func_on_shard(); +RESET citus.override_table_visibility; +SELECT * FROM foreign_table ORDER BY 1; + id | data | a +--------------------------------------------------------------------- + 1 | text_test | 1 + 2 | test_2 | 1 + 3 | test_3 | 2 +(3 rows) + +TRUNCATE foreign_table; +NOTICE: trigger_func() called: action = TRUNCATE, when = BEFORE, level = STATEMENT +CONTEXT: PL/pgSQL function trigger_func() line XX at RAISE +NOTICE: trigger_func_on_shard() called: action = TRUNCATE, when = BEFORE, level = STATEMENT +CONTEXT: PL/pgSQL function trigger_func_on_shard() line XX at RAISE +SELECT * FROM foreign_table ORDER BY 1; + id | data | a +--------------------------------------------------------------------- +(0 rows) + +RESET citus.use_citus_managed_tables; -- -- COPY FROM ... DEFAULT -- Already supported in Citus, adding all PG tests with a distributed table @@ -678,4 +752,5 @@ SELECT result FROM run_command_on_workers \set VERBOSITY terse SET client_min_messages TO ERROR; +DROP EXTENSION postgres_fdw CASCADE; DROP SCHEMA pg16 CASCADE; diff --git a/src/test/regress/sql/pg16.sql b/src/test/regress/sql/pg16.sql index 1df96e6a7..f5185deab 100644 --- a/src/test/regress/sql/pg16.sql +++ b/src/test/regress/sql/pg16.sql @@ -146,6 +146,63 @@ DROP DATABASE test_db; SELECT result FROM run_command_on_workers ($$DROP DATABASE test_db$$); SET search_path TO pg16; +SET citus.next_shard_id TO 951000; + +-- Foreign table TRUNCATE trigger +-- Relevant PG commit: +-- https://github.com/postgres/postgres/commit/3b00a94 +SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0); +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 'pg16', table_name 'foreign_table_test'); + +-- verify it's a Citus foreign table +SELECT partmethod, repmodel FROM pg_dist_partition +WHERE logicalrelid = 'foreign_table'::regclass ORDER BY logicalrelid; + +INSERT INTO foreign_table VALUES (2, 'test_2'); +INSERT INTO foreign_table_test VALUES (3, 'test_3'); + +CREATE FUNCTION trigger_func() RETURNS trigger LANGUAGE plpgsql AS $$ +BEGIN + RAISE NOTICE 'trigger_func(%) called: action = %, when = %, level = %', + TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL; + RETURN NULL; +END;$$; + +CREATE FUNCTION trigger_func_on_shard() RETURNS trigger LANGUAGE plpgsql AS $$ +BEGIN + RAISE NOTICE 'trigger_func_on_shard(%) called: action = %, when = %, level = %', + TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL; + RETURN NULL; +END;$$; + +CREATE TRIGGER trig_stmt_before BEFORE TRUNCATE ON foreign_table + FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func(); +SET citus.override_table_visibility TO off; +CREATE TRIGGER trig_stmt_shard_before BEFORE TRUNCATE ON foreign_table_951001 + FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func_on_shard(); +RESET citus.override_table_visibility; + +SELECT * FROM foreign_table ORDER BY 1; +TRUNCATE foreign_table; +SELECT * FROM foreign_table ORDER BY 1; + +RESET citus.use_citus_managed_tables; -- -- COPY FROM ... DEFAULT @@ -390,4 +447,5 @@ SELECT result FROM run_command_on_workers \set VERBOSITY terse SET client_min_messages TO ERROR; +DROP EXTENSION postgres_fdw CASCADE; DROP SCHEMA pg16 CASCADE;