Skip to content

Fire event triggers on chunk creation #8012

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .unreleased/pr_8012
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implements: #8012 Add event triggers support on chunk creation
27 changes: 21 additions & 6 deletions src/chunk.c
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,7 @@ Oid
ts_chunk_create_table(const Chunk *chunk, const Hypertable *ht, const char *tablespacename)
{
Relation rel;
ObjectAddress objaddr;
ObjectAddress address;
int sec_ctx;

/*
Expand Down Expand Up @@ -758,28 +758,43 @@ ts_chunk_create_table(const Chunk *chunk, const Hypertable *ht, const char *tabl
if (uid != saved_uid)
SetUserIdAndSecContext(uid, sec_ctx | SECURITY_LOCAL_USERID_CHANGE);

objaddr = DefineRelation(&stmt.base, chunk->relkind, rel->rd_rel->relowner, NULL, NULL);
/* Prepare event trigger state and invoke ddl_command_start triggers */
if (ts_guc_enable_event_triggers)
{
EventTriggerBeginCompleteQuery();
EventTriggerDDLCommandStart((Node *) &stmt.base);
}

address = DefineRelation(&stmt.base, chunk->relkind, rel->rd_rel->relowner, NULL, NULL);

/* Invoke ddl_command_end triggers and clean up the event trigger state */
if (ts_guc_enable_event_triggers)
{
EventTriggerCollectSimpleCommand(address, InvalidObjectAddress, (Node *) &stmt);
EventTriggerDDLCommandEnd((Node *) &stmt.base);
EventTriggerEndCompleteQuery();
}

/* Make the newly defined relation visible so that we can update the
* ACL. */
CommandCounterIncrement();

/* Copy acl from hypertable to chunk relation record */
copy_hypertable_acl_to_relid(ht, rel->rd_rel->relowner, objaddr.objectId);
copy_hypertable_acl_to_relid(ht, rel->rd_rel->relowner, address.objectId);

if (chunk->relkind == RELKIND_RELATION)
{
/*
* need to create a toast table explicitly for some of the option
* setting to work
*/
create_toast_table(&stmt.base, objaddr.objectId);
create_toast_table(&stmt.base, address.objectId);

/*
* Some options require being table owner to set for example statistics
* so we have to set them before restoring security context
*/
set_attoptions(rel, objaddr.objectId);
set_attoptions(rel, address.objectId);

if (uid != saved_uid)
SetUserIdAndSecContext(saved_uid, sec_ctx);
Expand All @@ -789,7 +804,7 @@ ts_chunk_create_table(const Chunk *chunk, const Hypertable *ht, const char *tabl

table_close(rel, AccessShareLock);

return objaddr.objectId;
return address.objectId;
}

static int32
Expand Down
11 changes: 11 additions & 0 deletions src/guc.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ TSDLLEXPORT bool ts_guc_enable_exclusive_locking_recompression = false;
TSDLLEXPORT bool ts_guc_enable_bool_compression = true;
TSDLLEXPORT int ts_guc_compression_batch_size_limit = 1000;
TSDLLEXPORT CompressTruncateBehaviour ts_guc_compress_truncate_behaviour = COMPRESS_TRUNCATE_ONLY;
bool ts_guc_enable_event_triggers = false;

/* Only settable in debug mode for testing */
TSDLLEXPORT bool ts_guc_enable_null_compression = true;
Expand Down Expand Up @@ -829,6 +830,16 @@ _guc_init(void)
NULL,
NULL,
NULL);
DefineCustomBoolVariable(MAKE_EXTOPTION("enable_event_triggers"),
"Enable event triggers for chunks creation",
"Enable event triggers for chunks creation",
&ts_guc_enable_event_triggers,
false,
PGC_SUSET,
0,
NULL,
NULL,
NULL);

#ifdef TS_DEBUG
DefineCustomBoolVariable(MAKE_EXTOPTION("enable_null_compression"),
Expand Down
1 change: 1 addition & 0 deletions src/guc.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ extern TSDLLEXPORT int ts_guc_compression_batch_size_limit;
#if PG16_GE
extern TSDLLEXPORT bool ts_guc_enable_skip_scan_for_distinct_aggregates;
#endif
extern bool ts_guc_enable_event_triggers;

/* Only settable in debug mode for testing */
extern TSDLLEXPORT bool ts_guc_enable_null_compression;
Expand Down
46 changes: 45 additions & 1 deletion tsl/test/expected/chunk_utils_internal.out
Original file line number Diff line number Diff line change
Expand Up @@ -1814,6 +1814,50 @@ SELECT * FROM event ORDER BY ts;
----+------
(0 rows)

-- clean up databases created
-- event triggers on chunk creation
CREATE TABLE ht_try(timec timestamptz NOT NULL, acq_id bigint, value bigint);
SELECT create_hypertable('ht_try', 'timec', chunk_time_interval => interval '1 day');
create_hypertable
----------------------
(25,public,ht_try,t)
(1 row)

-- creating event triggers requires superuser permissions
\c :TEST_DBNAME :ROLE_SUPERUSER
-- event trigger on ddl_start
CREATE OR REPLACE FUNCTION ddl_start_trigger_func() RETURNS EVENT_TRIGGER AS
$$
BEGIN
RAISE NOTICE 'ddl_start_trigger_func() is invoked';
END;
$$ LANGUAGE plpgsql;
CREATE EVENT TRIGGER ddl_start_trigger
ON ddl_command_start WHEN TAG IN ('CREATE TABLE') EXECUTE FUNCTION ddl_start_trigger_func();
-- event trigger on ddl_end
CREATE OR REPLACE FUNCTION ddl_end_trigger_func() RETURNS EVENT_TRIGGER AS
$$
DECLARE
cmd RECORD;
BEGIN
RAISE NOTICE 'ddl_end_trigger_func() is invoked';
FOR cmd IN SELECT * FROM pg_event_trigger_ddl_commands()
LOOP
RAISE NOTICE 'tag: %, object: %', cmd.command_tag, cmd.object_identity::regclass;
END LOOP;
END;
$$ LANGUAGE plpgsql;
CREATE EVENT TRIGGER ddl_end_trigger
ON ddl_command_end WHEN TAG IN ('CREATE TABLE') EXECUTE FUNCTION ddl_end_trigger_func();
-- by default event triggers on chunk creation are disabled
INSERT INTO ht_try VALUES ('2025-05-01 00:00', 1, 10);
SET timescaledb.enable_event_triggers = on;
INSERT INTO ht_try VALUES ('2025-05-02 00:00', 1, 10);
NOTICE: ddl_start_trigger_func() is invoked
NOTICE: ddl_end_trigger_func() is invoked
NOTICE: tag: CREATE TABLE, object: _timescaledb_internal._hyper_25_41_chunk
RESET timescaledb.enable_event_triggers;
DROP EVENT TRIGGER ddl_start_trigger;
DROP EVENT TRIGGER ddl_end_trigger;
DROP TABLE ht_try;
-- clean up databases created
DROP DATABASE postgres_fdw_db WITH (FORCE);
44 changes: 43 additions & 1 deletion tsl/test/sql/chunk_utils_internal.sql
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,48 @@ DELETE FROM event WHERE info = 'osm_chunk_ts';
DELETE FROM event WHERE info = 'chunk_ts';
SELECT * FROM event ORDER BY ts;

-- clean up databases created
-- event triggers on chunk creation
CREATE TABLE ht_try(timec timestamptz NOT NULL, acq_id bigint, value bigint);
SELECT create_hypertable('ht_try', 'timec', chunk_time_interval => interval '1 day');

-- creating event triggers requires superuser permissions
\c :TEST_DBNAME :ROLE_SUPERUSER

-- event trigger on ddl_start
CREATE OR REPLACE FUNCTION ddl_start_trigger_func() RETURNS EVENT_TRIGGER AS
$$
BEGIN
RAISE NOTICE 'ddl_start_trigger_func() is invoked';
END;
$$ LANGUAGE plpgsql;

CREATE EVENT TRIGGER ddl_start_trigger
ON ddl_command_start WHEN TAG IN ('CREATE TABLE') EXECUTE FUNCTION ddl_start_trigger_func();
-- event trigger on ddl_end
CREATE OR REPLACE FUNCTION ddl_end_trigger_func() RETURNS EVENT_TRIGGER AS
$$
DECLARE
cmd RECORD;
BEGIN
RAISE NOTICE 'ddl_end_trigger_func() is invoked';
FOR cmd IN SELECT * FROM pg_event_trigger_ddl_commands()
LOOP
RAISE NOTICE 'tag: %, object: %', cmd.command_tag, cmd.object_identity::regclass;
END LOOP;
END;
$$ LANGUAGE plpgsql;

CREATE EVENT TRIGGER ddl_end_trigger
ON ddl_command_end WHEN TAG IN ('CREATE TABLE') EXECUTE FUNCTION ddl_end_trigger_func();

-- by default event triggers on chunk creation are disabled
INSERT INTO ht_try VALUES ('2025-05-01 00:00', 1, 10);
SET timescaledb.enable_event_triggers = on;
INSERT INTO ht_try VALUES ('2025-05-02 00:00', 1, 10);
RESET timescaledb.enable_event_triggers;
DROP EVENT TRIGGER ddl_start_trigger;
DROP EVENT TRIGGER ddl_end_trigger;
DROP TABLE ht_try;

-- clean up databases created
DROP DATABASE postgres_fdw_db WITH (FORCE);
Loading