This commit is contained in:
yassine belkaid 2025-02-06 12:24:39 +03:00 committed by GitHub
commit 037ef0cd28
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 308 additions and 1 deletions

View File

@ -73,6 +73,8 @@ import { jsonToMermaid } from "../../utils/exportAs/mermaid";
import { isRtl } from "../../i18n/utils/rtl";
import { jsonToDocumentation } from "../../utils/exportAs/documentation";
import { IdContext } from "../Workspace";
import DatabasesSwitcher from "./DatabasesSwitcher";
import { convertTableSchema } from "../../utils/typesMappings";
export default function ControlPanel({
diagramId,
@ -80,6 +82,7 @@ export default function ControlPanel({
title,
setTitle,
lastSaved,
setLastSaved,
}) {
const [modal, setModal] = useState(MODAL.NONE);
const [sidesheet, setSidesheet] = useState(SIDESHEET.NONE);
@ -108,6 +111,7 @@ export default function ControlPanel({
updateRelationship,
database,
} = useDiagram();
const [prevDatabase, setPrevDatabase] = useState(database);
const { enums, setEnums, deleteEnum, addEnum, updateEnum } = useEnums();
const { types, addType, deleteType, updateType, setTypes } = useTypes();
const { notes, setNotes, updateNote, addNote, deleteNote } = useNotes();
@ -920,8 +924,9 @@ export default function ControlPanel({
function: () => {
if (database === DB.GENERIC) return;
setModal(MODAL.CODE);
const newTables = tables.map(table => convertTableSchema(table, prevDatabase, database));
const src = exportSQL({
tables: tables,
tables: newTables,
references: relationships,
types: types,
database: database,
@ -1652,6 +1657,11 @@ export default function ControlPanel({
title={databases[database].name + " diagram"}
/>
)}
<DatabasesSwitcher
setLastSaved={setLastSaved}
diagramId={diagramId}
setPrevDatabase={setPrevDatabase}
/>
<div
className="text-xl me-1"
onPointerEnter={(e) => e.isPrimary && setShowEditName(true)}

View File

@ -0,0 +1,77 @@
import { Select } from "@douyinfe/semi-ui";
import { databases } from "../../data/databases";
import { DB, State } from "../../data/constants";
import { useDiagram, useSaveState } from "../../hooks";
import { db } from "../../data/db";
const databasesWithoutGeneric = Object.keys(databases).filter(db => ![DB.GENERIC, DB.MARIADB, DB.MSSQL].includes(databases[db].label));
export default function DatabasesSwitcher({ setLastSaved, diagramId, setPrevDatabase }) {
const { database, setDatabase } = useDiagram();
const { setSaveState } = useSaveState();
if (!databases[database] || database === DB.GENERIC) return null;
const renderOptionItem = renderProps => {
const {
disabled,
selected,
label,
value,
focused,
className,
style,
onMouseEnter,
onClick,
} = renderProps;
const optionCls = [
'flex justify-start items-center pl-2 pt-3 cursor-pointer custom-option-render',
focused && 'custom-option-render-focused',
disabled && 'custom-option-render-disabled',
selected && 'custom-option-render-selected',
className,
].filter(cls => cls).join(' ');
return (
<div style={style} className={optionCls} onClick={() => onClick()} onMouseEnter={() => onMouseEnter()}>
{databases[value].image && (
<img
src={databases[value].image}
className="h-5 pr-2"
style={{
filter:
"opacity(0.4) drop-shadow(0 0 0 white) drop-shadow(0 0 0 white)",
}}
alt={databases[value].name + " icon"}
title={databases[value].name + " diagram"}
/>
)}
<div className="option-right pr-2">{label}</div>
</div>
);
};
const onChangeHandler = async (selectedDb) => {
await db.diagrams
.update(diagramId, {
database: selectedDb,
}).then(() => {
setSaveState(State.SAVED);
setLastSaved(new Date().toLocaleString());
setPrevDatabase(database);
setDatabase(selectedDb);
});
};
return <Select
className="w-100"
optionList={databasesWithoutGeneric.map((db) => ({
label: databases[db].name,
value: databases[db].label,
}))}
filter
value={database}
placeholder="Select database"
onChange={onChangeHandler}
renderOptionItem={renderOptionItem}
/>
}

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
};
})
};
}