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

Commit 3497248

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

File tree

5 files changed

+249
-0
lines changed

5 files changed

+249
-0
lines changed

sql-blueprinting/.env.example

Lines changed: 8 additions & 0 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 26 additions & 0 deletions
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

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
import mysql from "mysql2";
2+
import chalk from "chalk";
3+
import { config } from "dotenv";
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 = 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+
21+
console.log("Connected to database!");
22+
23+
const getTables = (db) => {
24+
return new Promise((resolve, reject) => {
25+
connection.query(
26+
"SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = ?",
27+
[db],
28+
(err, tables) => {
29+
if (err) reject(err);
30+
return resolve(tables);
31+
}
32+
);
33+
});
34+
};
35+
36+
const dropTable = (table) => {
37+
return new Promise((resolve, reject) => {
38+
connection.query(
39+
`DROP TABLE IF EXISTS ${db2}.??`,
40+
[table],
41+
(err, res) => {
42+
if (err) reject(err);
43+
return resolve(res);
44+
}
45+
);
46+
});
47+
};
48+
49+
const getColumns = (db, table) => {
50+
return new Promise((resolve, reject) => {
51+
connection.query(
52+
"SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?",
53+
[db, table],
54+
(err, columns) => {
55+
if (err) reject(err);
56+
return resolve(columns);
57+
}
58+
);
59+
});
60+
};
61+
62+
const dropColumn = (table, column) => {
63+
console.log("droping column: " + column + " from table" + table);
64+
return new Promise((resolve, reject) => {
65+
connection.query(
66+
`ALTER TABLE ${db2}.?? DROP COLUMN ??`,
67+
[table, column],
68+
(err, res) => {
69+
if (err) reject(err);
70+
return resolve(res);
71+
}
72+
);
73+
});
74+
};
75+
76+
const getData = (targetTableName) => {
77+
return new Promise((resolve, reject) => {
78+
connection.query(
79+
`SELECT * FROM ${db2}.?`,
80+
[targetTableName],
81+
(err, columns) => {
82+
if (err) reject(err);
83+
return resolve(columns);
84+
}
85+
);
86+
});
87+
};
88+
89+
const setData = (columns, targetTableName) => {
90+
return new Promise((resolve, reject) => {
91+
columns.forEach((entry) => {
92+
connection.query(
93+
`INSERT INTO ${db1}.? VALUES (?) `,
94+
[targetTableName, entry],
95+
(err, results) => {
96+
if (err) reject(err);
97+
return resolve(results);
98+
}
99+
);
100+
});
101+
});
102+
};
103+
104+
const toggleForeignKeyCheck = (state) => {
105+
return new Promise((resolve, reject) => {
106+
connection.query(`SET FOREIGN_KEY_CHECKS = ${state}`, (err, results) => {
107+
if (err) reject(err);
108+
return resolve("Foreign Key Check is set to " + state + "!");
109+
});
110+
});
111+
};
112+
113+
(async () => {
114+
try {
115+
let foreignKeyCheckState = 0;
116+
toggleForeignKeyCheck(foreignKeyCheckState).then((res) =>
117+
console.log(chalk.bold.yellowBright(res))
118+
);
119+
const tableNames_db1 = await getTables(db1);
120+
const tableNames_db2 = await getTables(db2);
121+
122+
await new Promise((resolve) => {
123+
tableNames_db2.forEach(async (table_db2, tableIndex) => {
124+
let tableExistanceFlag = false;
125+
let targetTableName = table_db2.TABLE_NAME;
126+
127+
tableNames_db1.forEach((table_db1) => {
128+
if (targetTableName === table_db1.TABLE_NAME) {
129+
tableExistanceFlag = true;
130+
}
131+
});
132+
if (tableExistanceFlag) {
133+
const columns_db1 = await getColumns(db1, targetTableName);
134+
const columns_db2 = await getColumns(db2, targetTableName);
135+
let columnsCountEquality = false;
136+
if (columns_db1.length === columns_db2.length) {
137+
columnsCountEquality = true;
138+
} else {
139+
columns_db2.forEach(async (column_db2) => {
140+
let columnExistanceFlag = false;
141+
let columnNameDB2 = column_db2.COLUMN_NAME;
142+
143+
columns_db1.forEach((column_db1) => {
144+
if (columnNameDB2 === column_db1.COLUMN_NAME) {
145+
columnExistanceFlag = true;
146+
}
147+
});
148+
if (!columnExistanceFlag) {
149+
await dropColumn(targetTableName, columnNameDB2);
150+
}
151+
});
152+
}
153+
console.log(
154+
`The table ${chalk.bold.greenBright(
155+
targetTableName
156+
)} exists in both databases ${
157+
columnsCountEquality
158+
? "and columns count " +
159+
chalk.bold.greenBright("is equal") +
160+
"."
161+
: "but columns count " +
162+
chalk.bold.redBright("is not equal") +
163+
"."
164+
}`
165+
);
166+
} else {
167+
console.log(
168+
`The table ${chalk.bold.redBright(
169+
targetTableName
170+
)} does not exists in both databases`
171+
);
172+
await dropTable(targetTableName);
173+
}
174+
if (tableIndex === tableNames_db2.length - 1) {
175+
resolve();
176+
}
177+
});
178+
});
179+
foreignKeyCheckState = 1;
180+
toggleForeignKeyCheck(foreignKeyCheckState).then((res) =>
181+
console.log(chalk.bold.yellowBright(res))
182+
);
183+
184+
console.log("Database cleanup is done, closing connection...");
185+
connection.end();
186+
} catch (err) {
187+
console.log(err);
188+
}
189+
})();
190+
});

sql-blueprinting/package.json

Lines changed: 22 additions & 0 deletions
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)