Skip to content
This repository was archived by the owner on Oct 7, 2024. It is now read-only.

Commit df6c154

Browse files
feat(migration-scripts) add sql-blueprinting script
1 parent 30aedca commit df6c154

File tree

5 files changed

+195
-0
lines changed

5 files changed

+195
-0
lines changed

sql-blueprinting/.env.example

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
DATABASE_BLUEPRINT_NAME=clean_db
2+
DATABASE_TARGET_NAME=existing_db
3+
4+
DATABASE_HOST=
5+
DATABASE_PORT=
6+
DATABASE_USER=
7+
DATABASE_PASSWORD=
8+
DATABASE_NAME=

sql-blueprinting/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
*.env
3+
package-lock.json

sql-blueprinting/README.md

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# SQL Blueprinting
2+
3+
Compare two databases and drop the tables that are not common in both.
4+
We run our strapi project in a new sql db so it creates its clean structure.
5+
This db can be used as a blueprint since it is created by our strapi current state and doesnt have old entries etc.
6+
7+
## Usage example
8+
9+
- DB1 is a blueprint db that contains only a schema, we will use this db as a structure referance.
10+
- DB2 is a production db that contains the data and a schema.
11+
- We want to drop from the DB2 (prod) the tables that does not appear in the structure of DB1
12+
- After cleaning our prod db according to blueprint we can migrate it to v4
13+
14+
## Description
15+
16+
Since we have to cleanup by order keys, columns and finally the tables, the db sets foreign key checks to 0 and after running back to 1.
17+
18+
## Run
19+
20+
- npm i
21+
- npm run start
22+
23+
## Important Notes
24+
25+
- Please use this script on clone of your production db.
26+
- This script drops all columns, collections and tables that does not exist in blueprint database, so use it carefully.

sql-blueprinting/index.js

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import chalk from 'chalk';
2+
import { config } from 'dotenv';
3+
import mysql from 'mysql2/promise';
4+
5+
config();
6+
7+
const db1 = process.env.DATABASE_BLUEPRINT_NAME; //reference database
8+
const db2 = process.env.DATABASE_TARGET_NAME; // target database
9+
10+
const connection = await mysql.createConnection({
11+
host: process.env.DATABASE_HOST,
12+
port: process.env.DATABASE_PORT,
13+
user: process.env.DATABASE_USER,
14+
password: process.env.DATABASE_PASSWORD,
15+
database: process.env.DATABASE_NAME,
16+
});
17+
18+
connection.connect((err) => {
19+
if (err) throw err;
20+
console.log(chalk.bold.greenBright('Connected to the database!'));
21+
});
22+
23+
const getTables = async (db) => {
24+
const [tables] = await connection.query(
25+
'SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = ?',
26+
[db]
27+
);
28+
return tables;
29+
};
30+
31+
const dropTable = async (db, table) => {
32+
await connection.query(`DROP TABLE IF EXISTS ??.??`, [db, table]);
33+
return `The table ${chalk.bold.redBright(table)} does not exists in both databases. Dropping...`;
34+
};
35+
36+
const getColumns = async (db, table) => {
37+
const [columns] = await connection.query(
38+
'SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?',
39+
[db, table]
40+
);
41+
return columns;
42+
};
43+
44+
const dropColumn = async (db, table, column) => {
45+
await connection.query(`ALTER TABLE ??.?? DROP COLUMN ??`, [db, table, column]);
46+
return `The column ${chalk.bold.redBright(column)} does not exists in both ${chalk.bold.redBright(
47+
table
48+
)} tables. Dropping...`;
49+
};
50+
51+
const dropRow = async (db, table, column, value) => {
52+
await connection.query(`DELETE FROM ??.?? WHERE ?? = ?`, [db, table, column, value]);
53+
return `The row ${chalk.bold.redBright(value)} does not exists in both ${chalk.bold.redBright(
54+
table
55+
)} tables. Dropping...`;
56+
};
57+
58+
const toggleForeignKeyCheck = async (state) => {
59+
await connection.query(`SET FOREIGN_KEY_CHECKS = ${state}`);
60+
return 'Foreign Key Check is set to ' + state + '!';
61+
};
62+
63+
const getCoreStore = async (db) => {
64+
const [coreStore] = await connection.query('SELECT * FROM ??.core_store', [db]);
65+
return coreStore;
66+
};
67+
68+
(async () => {
69+
try {
70+
let foreignKeyCheckState = 0;
71+
toggleForeignKeyCheck(foreignKeyCheckState).then((res) =>
72+
console.log(chalk.bold.yellowBright(res))
73+
);
74+
const tableNames_db1 = await getTables(db1);
75+
const tableNames_db2 = await getTables(db2);
76+
77+
for (const tableName_db2 of tableNames_db2) {
78+
let tableExistanceFlag = false;
79+
let targetTableName = tableName_db2.TABLE_NAME;
80+
tableNames_db1.forEach((table_db1) => {
81+
if (targetTableName === table_db1.TABLE_NAME) {
82+
tableExistanceFlag = true;
83+
}
84+
});
85+
if (tableExistanceFlag && targetTableName !== 'core_store') {
86+
console.log(
87+
`The table ${chalk.bold.greenBright(targetTableName)} exists in both databases.`
88+
);
89+
const columns_db1 = await getColumns(db1, targetTableName);
90+
const columns_db2 = await getColumns(db2, targetTableName);
91+
92+
for (const column_db2 of columns_db2) {
93+
let columnExistanceFlag = false;
94+
let columnNameDB2 = column_db2.COLUMN_NAME;
95+
columns_db1.forEach((column_db1) => {
96+
if (columnNameDB2 === column_db1.COLUMN_NAME) {
97+
columnExistanceFlag = true;
98+
}
99+
});
100+
if (!columnExistanceFlag) {
101+
const dropColumnMsg = await dropColumn(db2, targetTableName, columnNameDB2);
102+
console.log(dropColumnMsg);
103+
}
104+
}
105+
} else if (targetTableName === 'core_store') {
106+
const coreStore1 = await getCoreStore(db1);
107+
const coreStore2 = await getCoreStore(db2);
108+
for (const coreStore2Item of coreStore2) {
109+
let coreStoreExistanceFlag = false;
110+
let coreStore2ItemKey = coreStore2Item.key;
111+
coreStore1.forEach((coreStore1Item) => {
112+
if (coreStore2ItemKey === coreStore1Item.key) {
113+
coreStoreExistanceFlag = true;
114+
}
115+
});
116+
if (!coreStoreExistanceFlag) {
117+
const dropRowMsg = await dropRow(db2, targetTableName, 'key', coreStore2ItemKey);
118+
console.log(dropRowMsg);
119+
}
120+
}
121+
} else {
122+
const dropTableMsg = await dropTable(db2, targetTableName);
123+
console.log(dropTableMsg);
124+
}
125+
}
126+
foreignKeyCheckState = 1;
127+
toggleForeignKeyCheck(foreignKeyCheckState)
128+
.then((res) => console.log(chalk.bold.yellowBright(res)))
129+
.then(() => {
130+
console.log('Database cleanup is done, closing connection...');
131+
connection.end();
132+
});
133+
} catch (err) {
134+
console.log(err);
135+
}
136+
})();

sql-blueprinting/package.json

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "sql-blueprinting",
3+
"version": "0.1.0",
4+
"description": "",
5+
"main": "index.js",
6+
"author": "FotisVasilopoulos",
7+
"scripts": {
8+
"start": "node index.js"
9+
},
10+
"dependencies": {
11+
"chalk": "5.2.0",
12+
"mysql": "2.18.1",
13+
"mysql2": "3.2.4",
14+
"ora": "6.2.0",
15+
"dotenv": "16.0.0"
16+
},
17+
"type": "module",
18+
"engines": {
19+
"npm": ">=6.0.0"
20+
},
21+
"license": "UNLICENSED"
22+
}

0 commit comments

Comments
 (0)