Skip to content

Commit

Permalink
Add migration helper functions
Browse files Browse the repository at this point in the history
  • Loading branch information
exAspArk committed Sep 17, 2024
1 parent f977b10 commit 93ad811
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 73 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

#### v0.3.2 - 2024-09-17

- Add migration helper functions

#### v0.3.1 - 2024-09-17

- Allow calling `setContext` multiple times
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bemi-db/mikro-orm",
"version": "0.3.1",
"version": "0.3.2",
"description": "Automatic data change tracking for MikroORM",
"main": "dist/index.js",
"module": "./dist/index.mjs",
Expand Down
75 changes: 3 additions & 72 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,84 +5,15 @@ import fs from "fs";
import path from "path";

const MIGRATION_CONTENT = `import { Migration } from '@mikro-orm/migrations';
import { bemiUpSql, bemiDownSql } from '@bemi-db/mikro-orm';
export class {{CLASS_NAME}} extends Migration {
async up(): Promise<void> {
this.addSql(\`
CREATE OR REPLACE FUNCTION _bemi_row_trigger_func()
RETURNS TRIGGER
AS $$
DECLARE
_bemi_metadata TEXT;
BEGIN
SELECT split_part(split_part(current_query(), '/*Bemi ', 2), ' Bemi*/', 1) INTO _bemi_metadata;
IF _bemi_metadata <> '' THEN
PERFORM pg_logical_emit_message(true, '_bemi', _bemi_metadata);
END IF;
IF (TG_OP = 'DELETE') THEN
RETURN OLD;
ELSE
RETURN NEW;
END IF;
END;
$$ LANGUAGE plpgsql;
\`);
this.addSql(\`
CREATE OR REPLACE PROCEDURE _bemi_create_triggers()
AS $$
DECLARE
current_tablename TEXT;
BEGIN
FOR current_tablename IN
SELECT tablename FROM pg_tables
LEFT JOIN information_schema.triggers ON tablename = event_object_table AND schemaname = trigger_schema AND trigger_name LIKE '_bemi_row_trigger_%'
WHERE schemaname = 'public' AND trigger_name IS NULL
GROUP BY tablename
LOOP
EXECUTE format(
'CREATE OR REPLACE TRIGGER _bemi_row_trigger_%s
BEFORE INSERT OR UPDATE OR DELETE ON %I FOR EACH ROW
EXECUTE FUNCTION _bemi_row_trigger_func()',
current_tablename, current_tablename
);
END LOOP;
END;
$$ LANGUAGE plpgsql;
\`);
this.addSql(\`
CALL _bemi_create_triggers();
\`);
this.addSql(\`
CREATE OR REPLACE FUNCTION _bemi_create_table_trigger_func()
RETURNS event_trigger
AS $$
BEGIN
CALL _bemi_create_triggers();
END
$$ LANGUAGE plpgsql;
\`);
this.addSql(\`
DO $$
BEGIN
DROP EVENT TRIGGER IF EXISTS _bemi_create_table_trigger;
CREATE EVENT TRIGGER _bemi_create_table_trigger ON ddl_command_end WHEN TAG IN ('CREATE TABLE') EXECUTE FUNCTION _bemi_create_table_trigger_func();
EXCEPTION WHEN insufficient_privilege THEN
RAISE NOTICE 'Please execute "CALL _bemi_create_triggers();" manually after adding new tables you want to track. (%) %.', SQLSTATE, SQLERRM;
END
$$ LANGUAGE plpgsql;
\`);
this.addSql(bemiUpSql());
}
async down(): Promise<void> {
this.addSql('DROP EVENT TRIGGER _bemi_create_table_trigger;');
this.addSql('DROP FUNCTION _bemi_create_table_trigger_func;');
this.addSql('DROP PROCEDURE _bemi_create_triggers;');
this.addSql('DROP FUNCTION _bemi_row_trigger_func CASCADE;');
this.addSql(bemiDownSql());
}
}`

Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,5 @@ export const setContext = (
});
};
};

export { bemiUpSql, bemiDownSql } from "./migration-helpers";
71 changes: 71 additions & 0 deletions src/migration-helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
export const bemiUpSql = () => {
return `
CREATE OR REPLACE FUNCTION _bemi_row_trigger_func()
RETURNS TRIGGER
AS $$
DECLARE
_bemi_metadata TEXT;
BEGIN
SELECT split_part(split_part(current_query(), '/*Bemi ', 2), ' Bemi*/', 1) INTO _bemi_metadata;
IF _bemi_metadata <> '' THEN
PERFORM pg_logical_emit_message(true, '_bemi', _bemi_metadata);
END IF;
IF (TG_OP = 'DELETE') THEN
RETURN OLD;
ELSE
RETURN NEW;
END IF;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE PROCEDURE _bemi_create_triggers()
AS $$
DECLARE
current_tablename TEXT;
BEGIN
FOR current_tablename IN
SELECT tablename FROM pg_tables
LEFT JOIN information_schema.triggers ON tablename = event_object_table AND schemaname = trigger_schema AND trigger_name LIKE '_bemi_row_trigger_%'
WHERE schemaname = 'public' AND trigger_name IS NULL
GROUP BY tablename
LOOP
EXECUTE format(
'CREATE OR REPLACE TRIGGER _bemi_row_trigger_%s
BEFORE INSERT OR UPDATE OR DELETE ON %I FOR EACH ROW
EXECUTE FUNCTION _bemi_row_trigger_func()',
current_tablename, current_tablename
);
END LOOP;
END;
$$ LANGUAGE plpgsql;
CALL _bemi_create_triggers();
CREATE OR REPLACE FUNCTION _bemi_create_table_trigger_func()
RETURNS event_trigger
AS $$
BEGIN
CALL _bemi_create_triggers();
END
$$ LANGUAGE plpgsql;
DO $$
BEGIN
DROP EVENT TRIGGER IF EXISTS _bemi_create_table_trigger;
CREATE EVENT TRIGGER _bemi_create_table_trigger ON ddl_command_end WHEN TAG IN ('CREATE TABLE') EXECUTE FUNCTION _bemi_create_table_trigger_func();
EXCEPTION WHEN insufficient_privilege THEN
RAISE NOTICE 'Please execute "CALL _bemi_create_triggers();" manually after adding new tables you want to track. (%) %.', SQLSTATE, SQLERRM;
END
$$ LANGUAGE plpgsql;
`;
};

export const bemiDownSql = () => {
return `
DROP EVENT TRIGGER _bemi_create_table_trigger;
DROP FUNCTION _bemi_create_table_trigger_func;
DROP PROCEDURE _bemi_create_triggers;
DROP FUNCTION _bemi_row_trigger_func CASCADE;
`;
};

0 comments on commit 93ad811

Please sign in to comment.