This commit is contained in:
Benjamin Toby
2025-07-05 14:59:30 +01:00
parent 6e334c2525
commit 7e8bb37c09
526 changed files with 17560 additions and 11386 deletions
@@ -0,0 +1,3 @@
type Params = {};
function createDuplicateTablesTriggers({}: Params) {}
@@ -0,0 +1,106 @@
```sql
DELIMITER //
CREATE PROCEDURE dsql_replicate_databases(IN source_db VARCHAR(64), IN target_db VARCHAR(64))
BEGIN
-- Declare variables
DECLARE done INT DEFAULT FALSE;
DECLARE table_name VARCHAR(64);
DECLARE column_list TEXT;
DECLARE trigger_sql TEXT;
-- Cursor to iterate over tables in source_db
DECLARE cur CURSOR FOR
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = source_db;
-- Handler for end of cursor
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
-- Start transaction to ensure consistency
START TRANSACTION;
-- Open cursor
OPEN cur;
read_loop: LOOP
FETCH cur INTO table_name;
IF done THEN
LEAVE read_loop;
END IF;
-- Dynamically get column names for the table
SELECT GROUP_CONCAT(CONCAT('NEW.', COLUMN_NAME))
INTO column_list
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = source_db
AND TABLE_NAME = table_name;
-- Drop existing triggers if they exist
SET @drop_trigger_insert = CONCAT('DROP TRIGGER IF EXISTS after_insert_', table_name);
SET @drop_trigger_update = CONCAT('DROP TRIGGER IF EXISTS after_update_', table_name);
SET @drop_trigger_delete = CONCAT('DROP TRIGGER IF EXISTS after_delete_', table_name);
PREPARE stmt_drop_insert FROM @drop_trigger_insert;
EXECUTE stmt_drop_insert;
DEALLOCATE PREPARE stmt_drop_insert;
PREPARE stmt_drop_update FROM @drop_trigger_update;
EXECUTE stmt_drop_update;
DEALLOCATE PREPARE stmt_drop_update;
PREPARE stmt_drop_delete FROM @drop_trigger_delete;
EXECUTE stmt_drop_delete;
DEALLOCATE PREPARE stmt_drop_delete;
-- Create INSERT trigger
SET @trigger_sql = CONCAT(
'CREATE TRIGGER after_insert_', table_name,
' AFTER INSERT ON ', source_db, '.', table_name, ' FOR EACH ROW ',
'BEGIN ',
'INSERT INTO ', target_db, '.', table_name, ' (',
(SELECT GROUP_CONCAT(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = source_db AND TABLE_NAME = table_name), ') ',
'VALUES (', column_list, '); ',
'END;'
);
PREPARE stmt FROM @trigger_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Create UPDATE trigger
SET @trigger_sql = CONCAT(
'CREATE TRIGGER after_update_', table_name,
' AFTER UPDATE ON ', source_db, '.', table_name, ' FOR EACH ROW ',
'BEGIN ',
'UPDATE ', target_db, '.', table_name, ' SET ',
(SELECT GROUP_CONCAT(CONCAT(COLUMN_NAME, '=NEW.', COLUMN_NAME))
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = source_db AND TABLE_NAME = table_name),
' WHERE ',
(SELECT CONCAT('id=NEW.id')
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = source_db AND TABLE_NAME = table_name AND COLUMN_NAME = 'id' LIMIT 1), '; ',
'END;'
);
PREPARE stmt FROM @trigger_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Create DELETE trigger
SET @trigger_sql = CONCAT(
'CREATE TRIGGER after_delete_', table_name,
' AFTER DELETE ON ', source_db, '.', table_name, ' FOR EACH ROW ',
'BEGIN ',
'DELETE FROM ', target_db, '.', table_name, ' WHERE id=OLD.id; ',
'END;'
);
PREPARE stmt FROM @trigger_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur;
COMMIT;
END //
DELIMITER ;
```
@@ -0,0 +1,212 @@
DELIMITER / / CREATE PROCEDURE replicate_databases(
IN source_db VARCHAR(64),
IN target_db VARCHAR(64)
) BEGIN -- Declare variables
DECLARE done INT DEFAULT FALSE;
DECLARE table_name VARCHAR(64);
DECLARE column_list TEXT;
DECLARE trigger_sql TEXT;
-- Cursor to iterate over tables in source_db
DECLARE cur CURSOR FOR
SELECT
TABLE_NAME
FROM
INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = source_db;
-- Handler for end of cursor
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET
done = TRUE;
-- Start transaction to ensure consistency
START TRANSACTION;
-- Open cursor
OPEN cur;
read_loop: LOOP FETCH cur INTO table_name;
IF done THEN LEAVE read_loop;
END IF;
-- Dynamically get column names for the table
SELECT
GROUP_CONCAT(CONCAT('NEW.', COLUMN_NAME)) INTO column_list
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_SCHEMA = source_db
AND TABLE_NAME = table_name;
-- Drop existing triggers if they exist
SET
@drop_trigger_insert = CONCAT(
'DROP TRIGGER IF EXISTS after_insert_',
table_name
);
SET
@drop_trigger_update = CONCAT(
'DROP TRIGGER IF EXISTS after_update_',
table_name
);
SET
@drop_trigger_delete = CONCAT(
'DROP TRIGGER IF EXISTS after_delete_',
table_name
);
PREPARE stmt_drop_insert
FROM
@drop_trigger_insert;
EXECUTE stmt_drop_insert;
DEALLOCATE PREPARE stmt_drop_insert;
PREPARE stmt_drop_update
FROM
@drop_trigger_update;
EXECUTE stmt_drop_update;
DEALLOCATE PREPARE stmt_drop_update;
PREPARE stmt_drop_delete
FROM
@drop_trigger_delete;
EXECUTE stmt_drop_delete;
DEALLOCATE PREPARE stmt_drop_delete;
-- Create INSERT trigger
SET
@trigger_sql = CONCAT(
'CREATE TRIGGER after_insert_',
table_name,
' AFTER INSERT ON ',
source_db,
'.',
table_name,
' FOR EACH ROW ',
'BEGIN ',
'INSERT INTO ',
target_db,
'.',
table_name,
' (',
(
SELECT
GROUP_CONCAT(COLUMN_NAME)
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_SCHEMA = source_db
AND TABLE_NAME = table_name
),
') ',
'VALUES (',
column_list,
'); ',
'END;'
);
PREPARE stmt
FROM
@trigger_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Create UPDATE trigger
SET
@trigger_sql = CONCAT(
'CREATE TRIGGER after_update_',
table_name,
' AFTER UPDATE ON ',
source_db,
'.',
table_name,
' FOR EACH ROW ',
'BEGIN ',
'UPDATE ',
target_db,
'.',
table_name,
' SET ',
(
SELECT
GROUP_CONCAT(CONCAT(COLUMN_NAME, '=NEW.', COLUMN_NAME))
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_SCHEMA = source_db
AND TABLE_NAME = table_name
),
' WHERE ',
(
SELECT
CONCAT('id=NEW.id')
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_SCHEMA = source_db
AND TABLE_NAME = table_name
AND COLUMN_NAME = 'id'
LIMIT
1
), '; ', 'END;'
);
PREPARE stmt
FROM
@trigger_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Create DELETE trigger
SET
@trigger_sql = CONCAT(
'CREATE TRIGGER after_delete_',
table_name,
' AFTER DELETE ON ',
source_db,
'.',
table_name,
' FOR EACH ROW ',
'BEGIN ',
'DELETE FROM ',
target_db,
'.',
table_name,
' WHERE id=OLD.id; ',
'END;'
);
PREPARE stmt
FROM
@trigger_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur;
COMMIT;
END / / DELIMITER;
@@ -0,0 +1,23 @@
export const TriggerParadigms = ["sync_tables", "sync_dbs"] as const;
type Params = {
userId?: string | number;
paradigm: (typeof TriggerParadigms)[number];
dbId?: string | number;
tableName?: string;
};
export default function grabTriggerName({
userId,
paradigm,
dbId,
tableName,
}: Params) {
let triggerName = `dsql_trig_${paradigm}`;
if (userId) triggerName += `_${userId}`;
if (dbId) triggerName += `_${dbId}`;
if (tableName) triggerName += `_${tableName}`;
return triggerName;
}
@@ -0,0 +1,44 @@
import { DSQL_DatabaseSchemaType, DSQL_TableSchemaType } from "../../../types";
const TriggerTypes = [
{
name: "after_insert",
value: "INSERT",
},
{
name: "after_update",
value: "UPDATE",
},
{
name: "after_delete",
value: "DELETE",
},
] as const;
export type TriggerSQLGenParams = {
type: (typeof TriggerTypes)[number];
srcDbSchema: DSQL_DatabaseSchemaType;
srcTableSchema: DSQL_TableSchemaType;
content: string;
proceedureName: string;
};
export default function triggerSQLGen({
type,
srcDbSchema,
srcTableSchema,
content,
proceedureName,
}: TriggerSQLGenParams) {
let sql = `DELIMITER //\n`;
sql += `CREATE PROCEDURE ${proceedureName}`;
sql += `\nBEGIN`;
sql += ` ${content}`;
sql += `\nEND //`;
sql += `\nDELIMITER\n`;
return sql;
}
@@ -0,0 +1,48 @@
import { DSQL_DatabaseSchemaType, DSQL_TableSchemaType } from "../../../types";
import triggerSQLGen, { TriggerSQLGenParams } from "./trigger-sql-gen";
type Params = TriggerSQLGenParams & {
dstDbSchema: DSQL_DatabaseSchemaType;
dstTableSchema: DSQL_TableSchemaType;
};
export default function tableReplicationTriggerSQLGen({
type,
dstDbSchema,
dstTableSchema,
srcDbSchema,
srcTableSchema,
userId,
paradigm,
}: Params) {
let sql = `CREATE TRIGGER`;
const srcColumns = srcTableSchema.fields
.map((fld) => fld.fieldName)
.filter((fld) => typeof fld == "string");
const dstColumns = dstTableSchema.fields
.map((fld) => fld.fieldName)
.filter((fld) => typeof fld == "string");
if (type.name == "after_insert") {
sql += ` INSERT INTO ${dstDbSchema.dbFullName}.${dstTableSchema.tableName}`;
sql += ` (${dstColumns.join(",")})`;
sql += ` VALUES (${dstColumns.map((c) => `NEW.${c}`).join(",")})`;
} else if (type.name == "after_update") {
sql += ` UPDATE ${dstDbSchema.dbFullName}.${dstTableSchema.tableName}`;
sql += ` SET ${dstColumns.map((c) => `${c}=NEW.${c}`).join(",")}`;
sql += ` WHERE id = NEW.id`;
} else if (type.name == "after_delete") {
sql += ` DELETE FROM ${dstDbSchema.dbFullName}.${dstTableSchema.tableName}`;
sql += ` WHERE id = OLD.id`;
}
return triggerSQLGen({
content: sql,
srcDbSchema,
srcTableSchema,
type,
paradigm,
userId,
});
}
@@ -0,0 +1,92 @@
```sql
DELIMITER //
CREATE PROCEDURE dsql_replicate_two_tables(
IN source_db VARCHAR(64),
IN target_db VARCHAR(64),
IN source_table VARCHAR(64),
IN target_table VARCHAR(64)
)
BEGIN
-- Declare variables
DECLARE column_list TEXT;
DECLARE set_clause TEXT;
DECLARE trigger_sql TEXT;
-- Start transaction to ensure consistency
START TRANSACTION;
-- Dynamically get column names for the source table
SELECT GROUP_CONCAT(CONCAT('NEW.', COLUMN_NAME))
INTO column_list
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = source_db
AND TABLE_NAME = source_table;
SELECT GROUP_CONCAT(CONCAT(COLUMN_NAME, '=NEW.', COLUMN_NAME))
INTO set_clause
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = source_db
AND TABLE_NAME = source_table;
-- Drop existing triggers if they exist
SET @drop_trigger_insert = CONCAT('DROP TRIGGER IF EXISTS after_insert_', source_table);
SET @drop_trigger_update = CONCAT('DROP TRIGGER IF EXISTS after_update_', source_table);
SET @drop_trigger_delete = CONCAT('DROP TRIGGER IF EXISTS after_delete_', source_table);
PREPARE stmt_drop_insert FROM @drop_trigger_insert;
EXECUTE stmt_drop_insert;
DEALLOCATE PREPARE stmt_drop_insert;
PREPARE stmt_drop_update FROM @drop_trigger_update;
EXECUTE stmt_drop_update;
DEALLOCATE PREPARE stmt_drop_update;
PREPARE stmt_drop_delete FROM @drop_trigger_delete;
EXECUTE stmt_drop_delete;
DEALLOCATE PREPARE stmt_drop_delete;
-- Create INSERT trigger
SET @trigger_sql = CONCAT(
'CREATE TRIGGER after_insert_', source_table,
' AFTER INSERT ON ', source_db, '.', source_table, ' FOR EACH ROW ',
'BEGIN ',
'INSERT INTO ', target_db, '.', target_table, ' (',
(SELECT GROUP_CONCAT(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = source_db AND TABLE_NAME = source_table), ') ',
'VALUES (', column_list, '); ',
'END;'
);
PREPARE stmt FROM @trigger_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Create UPDATE trigger
-- Assume 'id' as the primary key; adjust if different
SET @trigger_sql = CONCAT(
'CREATE TRIGGER after_update_', source_table,
' AFTER UPDATE ON ', source_db, '.', source_table, ' FOR EACH ROW ',
'BEGIN ',
'UPDATE ', target_db, '.', target_table, ' SET ',
set_clause,
' WHERE id = NEW.id; ',
'END;'
);
PREPARE stmt FROM @trigger_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Create DELETE trigger
SET @trigger_sql = CONCAT(
'CREATE TRIGGER after_delete_', source_table,
' AFTER DELETE ON ', source_db, '.', source_table, ' FOR EACH ROW ',
'BEGIN ',
'DELETE FROM ', target_db, '.', target_table, ' WHERE id = OLD.id; ',
'END;'
);
PREPARE stmt FROM @trigger_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
COMMIT;
END //
DELIMITER ;
```
@@ -0,0 +1,53 @@
import { DSQL_DatabaseSchemaType, DSQL_TableSchemaType } from "../../../types";
import grabTriggerName, { TriggerParadigms } from "./grab-trigger-name";
const TriggerTypes = [
{
name: "after_insert",
value: "INSERT",
},
{
name: "after_update",
value: "UPDATE",
},
{
name: "after_delete",
value: "DELETE",
},
] as const;
export type TriggerSQLGenParams = {
type: (typeof TriggerTypes)[number];
srcDbSchema: DSQL_DatabaseSchemaType;
srcTableSchema: DSQL_TableSchemaType;
content: string;
userId?: string | number;
paradigm: (typeof TriggerParadigms)[number];
};
export default function triggerSQLGen({
type,
srcDbSchema,
srcTableSchema,
content,
userId,
paradigm,
}: TriggerSQLGenParams) {
let sql = `CREATE TRIGGER`;
let triggerName = grabTriggerName({
paradigm,
dbId: srcDbSchema.id,
tableName: srcTableSchema.tableName,
userId,
});
sql += ` ${triggerName}`;
sql += ` AFTER ${type.value} ON ${srcTableSchema.tableName}`;
sql += ` FOR EACH ROW BEGIN`;
sql += ` ${content}`;
sql += ` END`;
return sql;
}