feat: add db engines conversions for postresql, mysql, sqlite

This commit is contained in:
Yassine Belkaid 2024-10-13 14:16:14 +03:00
parent 3d6a4fcbb5
commit a1d6c829a2
3 changed files with 228 additions and 3 deletions

View File

@ -74,6 +74,7 @@ import { isRtl } from "../../i18n/utils/rtl";
import { jsonToDocumentation } from "../../utils/exportAs/documentation"; import { jsonToDocumentation } from "../../utils/exportAs/documentation";
import { IdContext } from "../Workspace"; import { IdContext } from "../Workspace";
import DatabasesSwitcher from "./DatabasesSwitcher"; import DatabasesSwitcher from "./DatabasesSwitcher";
import { convertTableSchema } from "../../utils/typesMappings";
export default function ControlPanel({ export default function ControlPanel({
diagramId, diagramId,
@ -109,6 +110,7 @@ export default function ControlPanel({
deleteRelationship, deleteRelationship,
database, database,
} = useDiagram(); } = useDiagram();
const [prevDatabase, setPrevDatabase] = useState(database);
const { enums, setEnums, deleteEnum, addEnum, updateEnum } = useEnums(); const { enums, setEnums, deleteEnum, addEnum, updateEnum } = useEnums();
const { types, addType, deleteType, updateType, setTypes } = useTypes(); const { types, addType, deleteType, updateType, setTypes } = useTypes();
const { notes, setNotes, updateNote, addNote, deleteNote } = useNotes(); const { notes, setNotes, updateNote, addNote, deleteNote } = useNotes();
@ -925,8 +927,9 @@ export default function ControlPanel({
function: () => { function: () => {
if (database === DB.GENERIC) return; if (database === DB.GENERIC) return;
setModal(MODAL.CODE); setModal(MODAL.CODE);
const newTables = tables.map(table => convertTableSchema(table, prevDatabase, database));
const src = exportSQL({ const src = exportSQL({
tables: tables, tables: newTables,
references: relationships, references: relationships,
types: types, types: types,
database: database, database: database,
@ -1645,6 +1648,7 @@ export default function ControlPanel({
<DatabasesSwitcher <DatabasesSwitcher
setLastSaved={setLastSaved} setLastSaved={setLastSaved}
diagramId={diagramId} diagramId={diagramId}
setPrevDatabase={setPrevDatabase}
/> />
<div <div
className="text-xl me-1" className="text-xl me-1"

View File

@ -4,9 +4,9 @@ import { DB, State } from "../../data/constants";
import { useDiagram, useSaveState } from "../../hooks"; import { useDiagram, useSaveState } from "../../hooks";
import { db } from "../../data/db"; import { db } from "../../data/db";
const databasesWithoutGeneric = Object.keys(databases).filter(db => databases[db].label !== DB.GENERIC); const databasesWithoutGeneric = Object.keys(databases).filter(db => ![DB.GENERIC, DB.MARIADB, DB.MSSQL].includes(databases[db].label));
export default function DatabasesSwitcher({ setLastSaved, diagramId }) { export default function DatabasesSwitcher({ setLastSaved, diagramId, setPrevDatabase }) {
const { database, setDatabase } = useDiagram(); const { database, setDatabase } = useDiagram();
const { setSaveState } = useSaveState(); const { setSaveState } = useSaveState();
@ -57,6 +57,7 @@ export default function DatabasesSwitcher({ setLastSaved, diagramId }) {
}).then(() => { }).then(() => {
setSaveState(State.SAVED); setSaveState(State.SAVED);
setLastSaved(new Date().toLocaleString()); setLastSaved(new Date().toLocaleString());
setPrevDatabase(database);
setDatabase(selectedDb); setDatabase(selectedDb);
}); });
}; };

220
src/utils/typesMappings.js Normal file
View File

@ -0,0 +1,220 @@
import { DB } from "../data/constants";
function mapDataTypes(fromDb, toDb) {
if (!fromDb || !toDb) {
throw new Error("Please provide both from/to database names.");
}
fromDb = fromDb.toLowerCase();
toDb = toDb.toLowerCase();
if (fromDb === toDb || [fromDb, toDb].includes(DB.GENERIC)) {
return "";
}
const typeMapping = {
postgresql: {
mysql: {
"SMALLINT": "TINYINT",
"INTEGER": "INT",
"BIGINT": "BIGINT",
"SERIAL": "INT AUTO_INCREMENT",
"BIGSERIAL": "BIGINT AUTO_INCREMENT",
"DECIMAL": "DECIMAL",
"NUMERIC": "DECIMAL",
"REAL": "FLOAT",
"DOUBLE PRECISION": "DOUBLE",
"MONEY": "DECIMAL(19,4)",
"VARCHAR": "VARCHAR",
"CHAR": "CHAR",
"TEXT": "TEXT",
"BYTEA": "BLOB",
"BOOLEAN": "TINYINT(1)",
"DATE": "DATE",
"TIME": "TIME",
"TIMETZ": "TIME",
"TIMESTAMP": "DATETIME",
"TIMESTAMPTZ": "DATETIME",
"INTERVAL": "TIME",
"UUID": "CHAR(36)",
"JSON": "JSON",
"JSONB": "JSON",
"XML": "TEXT",
},
sqlite: {
"SMALLINT": "INTEGER",
"INTEGER": "INTEGER",
"BIGINT": "INTEGER",
"SERIAL": "INTEGER PRIMARY KEY AUTOINCREMENT",
"BIGSERIAL": "INTEGER",
"DECIMAL": "REAL",
"NUMERIC": "REAL",
"REAL": "REAL",
"DOUBLE PRECISION": "REAL",
"MONEY": "REAL",
"VARCHAR": "TEXT",
"CHAR": "TEXT",
"TEXT": "TEXT",
"BYTEA": "BLOB",
"BOOLEAN": "INTEGER",
"DATE": "TEXT",
"TIME": "TEXT",
"TIMETZ": "TEXT",
"TIMESTAMP": "TEXT",
"TIMESTAMPTZ": "TEXT",
"INTERVAL": "TEXT",
"UUID": "TEXT",
"JSON": "TEXT",
"JSONB": "TEXT",
"XML": "TEXT",
},
},
mysql: {
postgresql: {
"TINYINT": "SMALLINT",
"SMALLINT": "SMALLINT",
"MEDIUMINT": "INTEGER",
"INT": "INTEGER",
"BIGINT": "BIGINT",
"FLOAT": "REAL",
"DOUBLE": "DOUBLE PRECISION",
"DECIMAL": "DECIMAL",
"NUMERIC": "NUMERIC",
"VARCHAR": "VARCHAR",
"CHAR": "CHAR",
"TEXT": "TEXT",
"BLOB": "BYTEA",
"TINYBLOB": "BYTEA",
"MEDIUMBLOB": "BYTEA",
"LONGBLOB": "BYTEA",
"BIT": "BOOLEAN",
"BOOLEAN": "BOOLEAN",
"DATE": "DATE",
"TIME": "TIME",
"DATETIME": "TIMESTAMP",
"TIMESTAMP": "TIMESTAMP",
"YEAR": "DATE",
"JSON": "JSON",
"ENUM": "VARCHAR",
"SET": "TEXT",
"POINT": "POINT",
"LINESTRING": "LINE",
"POLYGON": "POLYGON"
},
sqlite: {
"TINYINT": "INTEGER",
"SMALLINT": "INTEGER",
"MEDIUMINT": "INTEGER",
"INT": "INTEGER",
"BIGINT": "INTEGER",
"FLOAT": "REAL",
"DOUBLE": "REAL",
"DECIMAL": "REAL",
"NUMERIC": "REAL",
"VARCHAR(n)": "TEXT",
"CHAR(n)": "TEXT",
"TEXT": "TEXT",
"BLOB": "BLOB",
"TINYBLOB": "BLOB",
"MEDIUMBLOB": "BLOB",
"LONGBLOB": "BLOB",
"BIT": "INTEGER",
"BOOLEAN": "INTEGER",
"DATE": "TEXT",
"TIME": "TEXT",
"DATETIME": "TEXT",
"TIMESTAMP": "TEXT",
"YEAR": "TEXT",
"JSON": "TEXT",
"ENUM": "TEXT",
"SET": "TEXT",
"POINT": "TEXT",
"LINESTRING": "TEXT",
"POLYGON": "TEXT"
},
},
sqlite: {
postgresql: {
"INTEGER": "INTEGER",
"REAL": "DOUBLE PRECISION",
"TEXT": "TEXT",
"BLOB": "BYTEA",
"BOOLEAN": "BOOLEAN",
"DATE": "DATE",
"TIME": "TIME",
"DATETIME": "TIMESTAMP",
"NUMERIC": "NUMERIC",
"VARCHAR": "VARCHAR",
"CHAR": "CHAR",
"JSON": "JSON",
"POINT": "POINT",
"LINESTRING": "LINE",
"POLYGON": "POLYGON",
},
mysql: {
"INTEGER": "INTEGER",
"REAL": "DOUBLE",
"TEXT": "TEXT",
"BLOB": "BLOB",
"BOOLEAN": "TINYINT(1)",
"DATE": "DATE",
"TIME": "TIME",
"DATETIME": "DATETIME",
"NUMERIC": "DECIMAL",
"VARCHAR": "VARCHAR",
"CHAR": "CHAR",
"JSON": "JSON",
"POINT": "POINT",
"LINESTRING": "LINESTRING",
"POLYGON": "POLYGON",
},
},
};
if (typeMapping[fromDb] && typeMapping[fromDb][toDb]) {
return typeMapping[fromDb][toDb];
} else {
return ''; // Unsupported data type mapping
}
}
export function convertTableSchema(table, fromDb, toDb) {
return {
...table,
fields: table.fields.map(field => {
let mappedType = mapDataTypes(fromDb, toDb)?.[field.type];
// Handle ENUM values if they exist
if (field.type === "ENUM" && [DB.POSTGRES, DB.SQLITE].includes(toDb)) {
mappedType = toDb === DB.POSTGRES ? "VARCHAR" : "TEXT";
if (field.values?.length) {
field.check = `${field.name} IN (${field.values.join(", ")})`;
}
}
if (field.type === "VARCHAR" && field.check && DB.MYSQL === toDb) {
mappedType = `ENUM`;
const regex = /\(([^)]+)\)/;
const match = field.check.match(regex);
if (match) {
field.values = match[1].split(',').map(value => value.trim());
mappedType += `(${field.values.join(", ")})`;
}
}
if (DB.POSTGRES === toDb && field.increment) {
mappedType = `SERIAL`;
}
return {
...field,
type: mappedType || field.type,
// Remove MySQL-specific properties if necessary
values: field.values || undefined
};
})
};
}