Skip to content
Open
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
33 changes: 33 additions & 0 deletions db/migrate/20260627120000_add_translation_cleanup_triggers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

require Rails.root.join('lib/sagittarius/database/polymorphic_cleanup_trigger')

class AddTranslationCleanupTriggers < Code0::ZeroTrack::Database::Migration[1.0]
include Sagittarius::Database::PolymorphicCleanupTrigger

TRANSLATION_OWNERS = {
data_types: 'DataType',
flow_types: 'FlowType',
flow_type_settings: 'FlowTypeSetting',
function_definitions: 'FunctionDefinition',
module_configuration_definitions: 'ModuleConfigurationDefinition',
parameter_definitions: 'ParameterDefinition',
runtime_flow_types: 'RuntimeFlowType',
runtime_flow_type_settings: 'RuntimeFlowTypeSetting',
runtime_function_definitions: 'RuntimeFunctionDefinition',
runtime_modules: 'RuntimeModule',
runtime_parameter_definitions: 'RuntimeParameterDefinition',
}.freeze

def up
TRANSLATION_OWNERS.each do |parent_table, parent_class|
create_polymorphic_cleanup_trigger(:translations, parent_table, :owner, parent_class)
end
end

def down
TRANSLATION_OWNERS.each_key do |parent_table|
drop_polymorphic_cleanup_trigger(:translations, parent_table)
end
end
end
1 change: 1 addition & 0 deletions db/schema_migrations/20260627120000
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
19dfafb7e212541b59fb7a25cb0662fad87c8b2fd9e9e60f92c260fffdd1edbd
66 changes: 66 additions & 0 deletions db/structure.sql
Original file line number Diff line number Diff line change
@@ -1,3 +1,47 @@
CREATE FUNCTION delete_data_types_translations() RETURNS trigger
LANGUAGE plpgsql
AS $$ BEGIN DELETE FROM "translations" WHERE "owner_type" = 'DataType' AND "owner_id" IN (SELECT id FROM old_rows); RETURN NULL; END; $$;

CREATE FUNCTION delete_flow_type_settings_translations() RETURNS trigger
LANGUAGE plpgsql
AS $$ BEGIN DELETE FROM "translations" WHERE "owner_type" = 'FlowTypeSetting' AND "owner_id" IN (SELECT id FROM old_rows); RETURN NULL; END; $$;

CREATE FUNCTION delete_flow_types_translations() RETURNS trigger
LANGUAGE plpgsql
AS $$ BEGIN DELETE FROM "translations" WHERE "owner_type" = 'FlowType' AND "owner_id" IN (SELECT id FROM old_rows); RETURN NULL; END; $$;

CREATE FUNCTION delete_function_definitions_translations() RETURNS trigger
LANGUAGE plpgsql
AS $$ BEGIN DELETE FROM "translations" WHERE "owner_type" = 'FunctionDefinition' AND "owner_id" IN (SELECT id FROM old_rows); RETURN NULL; END; $$;

CREATE FUNCTION delete_module_configuration_definitions_translations() RETURNS trigger
LANGUAGE plpgsql
AS $$ BEGIN DELETE FROM "translations" WHERE "owner_type" = 'ModuleConfigurationDefinition' AND "owner_id" IN (SELECT id FROM old_rows); RETURN NULL; END; $$;

CREATE FUNCTION delete_parameter_definitions_translations() RETURNS trigger
LANGUAGE plpgsql
AS $$ BEGIN DELETE FROM "translations" WHERE "owner_type" = 'ParameterDefinition' AND "owner_id" IN (SELECT id FROM old_rows); RETURN NULL; END; $$;

CREATE FUNCTION delete_runtime_flow_type_settings_translations() RETURNS trigger
LANGUAGE plpgsql
AS $$ BEGIN DELETE FROM "translations" WHERE "owner_type" = 'RuntimeFlowTypeSetting' AND "owner_id" IN (SELECT id FROM old_rows); RETURN NULL; END; $$;

CREATE FUNCTION delete_runtime_flow_types_translations() RETURNS trigger
LANGUAGE plpgsql
AS $$ BEGIN DELETE FROM "translations" WHERE "owner_type" = 'RuntimeFlowType' AND "owner_id" IN (SELECT id FROM old_rows); RETURN NULL; END; $$;

CREATE FUNCTION delete_runtime_function_definitions_translations() RETURNS trigger
LANGUAGE plpgsql
AS $$ BEGIN DELETE FROM "translations" WHERE "owner_type" = 'RuntimeFunctionDefinition' AND "owner_id" IN (SELECT id FROM old_rows); RETURN NULL; END; $$;

CREATE FUNCTION delete_runtime_modules_translations() RETURNS trigger
LANGUAGE plpgsql
AS $$ BEGIN DELETE FROM "translations" WHERE "owner_type" = 'RuntimeModule' AND "owner_id" IN (SELECT id FROM old_rows); RETURN NULL; END; $$;

CREATE FUNCTION delete_runtime_parameter_definitions_translations() RETURNS trigger
LANGUAGE plpgsql
AS $$ BEGIN DELETE FROM "translations" WHERE "owner_type" = 'RuntimeParameterDefinition' AND "owner_id" IN (SELECT id FROM old_rows); RETURN NULL; END; $$;

CREATE TABLE active_storage_attachments (
id bigint NOT NULL,
name character varying NOT NULL,
Expand Down Expand Up @@ -1698,6 +1742,28 @@ CREATE UNIQUE INDEX "index_users_on_LOWER_username" ON users USING btree (lower(

CREATE UNIQUE INDEX index_users_on_totp_secret ON users USING btree (totp_secret) WHERE (totp_secret IS NOT NULL);

CREATE TRIGGER trigger_delete_data_types_translations AFTER DELETE ON data_types REFERENCING OLD TABLE AS old_rows FOR EACH STATEMENT EXECUTE FUNCTION delete_data_types_translations();

CREATE TRIGGER trigger_delete_flow_type_settings_translations AFTER DELETE ON flow_type_settings REFERENCING OLD TABLE AS old_rows FOR EACH STATEMENT EXECUTE FUNCTION delete_flow_type_settings_translations();

CREATE TRIGGER trigger_delete_flow_types_translations AFTER DELETE ON flow_types REFERENCING OLD TABLE AS old_rows FOR EACH STATEMENT EXECUTE FUNCTION delete_flow_types_translations();

CREATE TRIGGER trigger_delete_function_definitions_translations AFTER DELETE ON function_definitions REFERENCING OLD TABLE AS old_rows FOR EACH STATEMENT EXECUTE FUNCTION delete_function_definitions_translations();

CREATE TRIGGER trigger_delete_module_configuration_definitions_translations AFTER DELETE ON module_configuration_definitions REFERENCING OLD TABLE AS old_rows FOR EACH STATEMENT EXECUTE FUNCTION delete_module_configuration_definitions_translations();

CREATE TRIGGER trigger_delete_parameter_definitions_translations AFTER DELETE ON parameter_definitions REFERENCING OLD TABLE AS old_rows FOR EACH STATEMENT EXECUTE FUNCTION delete_parameter_definitions_translations();

CREATE TRIGGER trigger_delete_runtime_flow_type_settings_translations AFTER DELETE ON runtime_flow_type_settings REFERENCING OLD TABLE AS old_rows FOR EACH STATEMENT EXECUTE FUNCTION delete_runtime_flow_type_settings_translations();

CREATE TRIGGER trigger_delete_runtime_flow_types_translations AFTER DELETE ON runtime_flow_types REFERENCING OLD TABLE AS old_rows FOR EACH STATEMENT EXECUTE FUNCTION delete_runtime_flow_types_translations();

CREATE TRIGGER trigger_delete_runtime_function_definitions_translations AFTER DELETE ON runtime_function_definitions REFERENCING OLD TABLE AS old_rows FOR EACH STATEMENT EXECUTE FUNCTION delete_runtime_function_definitions_translations();

CREATE TRIGGER trigger_delete_runtime_modules_translations AFTER DELETE ON runtime_modules REFERENCING OLD TABLE AS old_rows FOR EACH STATEMENT EXECUTE FUNCTION delete_runtime_modules_translations();

CREATE TRIGGER trigger_delete_runtime_parameter_definitions_translations AFTER DELETE ON runtime_parameter_definitions REFERENCING OLD TABLE AS old_rows FOR EACH STATEMENT EXECUTE FUNCTION delete_runtime_parameter_definitions_translations();

ALTER TABLE ONLY node_parameters
ADD CONSTRAINT fk_rails_0d79310cfa FOREIGN KEY (node_function_id) REFERENCES node_functions(id) ON DELETE CASCADE;

Expand Down
54 changes: 54 additions & 0 deletions lib/sagittarius/database/polymorphic_cleanup_trigger.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# frozen_string_literal: true

module Sagittarius
module Database
module PolymorphicCleanupTrigger
def create_polymorphic_cleanup_trigger(table, parent_table, parent_column, parent_class)
function_name = "delete_#{parent_table}_#{table}"
trigger_name = "trigger_#{function_name}"

execute <<~SQL.squish
CREATE OR REPLACE FUNCTION #{quote_column_name(function_name)}()
RETURNS TRIGGER AS $$
BEGIN
DELETE FROM #{quote_table_name(table)}
WHERE #{quote_column_name("#{parent_column}_type")} = #{quote(parent_class)}
AND #{quote_column_name("#{parent_column}_id")} IN (SELECT id FROM old_rows);
RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER #{quote_column_name(trigger_name)}
AFTER DELETE ON #{quote_table_name(parent_table)}
REFERENCING OLD TABLE AS old_rows
FOR EACH STATEMENT
EXECUTE FUNCTION #{quote_column_name(function_name)}();
SQL
end

def drop_polymorphic_cleanup_trigger(table, parent_table)
function_name = "delete_#{parent_table}_#{table}"
trigger_name = "trigger_#{function_name}"

execute <<~SQL.squish
DROP TRIGGER #{quote_column_name(trigger_name)} ON #{quote_table_name(parent_table)};
DROP FUNCTION #{quote_column_name(function_name)}();
SQL
end

private

def quote(value)
connection.quote(value)
end

def quote_column_name(name)
connection.quote_column_name(name)
end

def quote_table_name(name)
connection.quote_table_name(name)
end
end
end
end
11 changes: 11 additions & 0 deletions spec/models/translation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@
it { is_expected.to validate_presence_of(:content) }
end

describe 'polymorphic owner cleanup trigger' do
it 'deletes translations when an owner is deleted directly in PostgreSQL' do
owner = create(:data_type)
translation = create(:translation, owner: owner)

DataType.where(id: owner.id).delete_all

expect(described_class).not_to exist(translation.id)
end
end

describe '#to_grpc' do
it 'matches the model' do
grpc_object = translation.to_grpc
Expand Down