mirror of
https://github.com/drawdb-io/drawdb.git
synced 2025-05-24 02:09:17 +00:00
Merge a1d6c829a2
into 4ac039f2f1
This commit is contained in:
commit
037ef0cd28
@ -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)}
|
||||
|
77
src/components/EditorHeader/DatabasesSwitcher.jsx
Normal file
77
src/components/EditorHeader/DatabasesSwitcher.jsx
Normal 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
220
src/utils/typesMappings.js
Normal 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
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user