commit
d05ba8de65
14
package-lock.json
generated
14
package-lock.json
generated
@ -25,7 +25,7 @@
|
|||||||
"jsonschema": "^1.4.1",
|
"jsonschema": "^1.4.1",
|
||||||
"jspdf": "^2.5.1",
|
"jspdf": "^2.5.1",
|
||||||
"lexical": "^0.12.5",
|
"lexical": "^0.12.5",
|
||||||
"node-sql-parser": "^4.17.0",
|
"node-sql-parser": "^5.0.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hotkeys-hook": "^4.4.1",
|
"react-hotkeys-hook": "^4.4.1",
|
||||||
@ -1715,6 +1715,11 @@
|
|||||||
"@babel/types": "^7.20.7"
|
"@babel/types": "^7.20.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/pegjs": {
|
||||||
|
"version": "0.10.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/pegjs/-/pegjs-0.10.6.tgz",
|
||||||
|
"integrity": "sha512-eLYXDbZWXh2uxf+w8sXS8d6KSoXTswfps6fvCUuVAGN8eRpfe7h9eSRydxiSJvo9Bf+GzifsDOr9TMQlmJdmkw=="
|
||||||
|
},
|
||||||
"node_modules/@types/prop-types": {
|
"node_modules/@types/prop-types": {
|
||||||
"version": "15.7.11",
|
"version": "15.7.11",
|
||||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
|
||||||
@ -4368,10 +4373,11 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/node-sql-parser": {
|
"node_modules/node-sql-parser": {
|
||||||
"version": "4.17.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-sql-parser/-/node-sql-parser-4.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-sql-parser/-/node-sql-parser-5.0.0.tgz",
|
||||||
"integrity": "sha512-3IhovpmUBpcETnoKK/KBdkz2mz53kVG5E1dnqz1QuYvtzdxYZW5xaGGEvW9u6Yyy2ivwR3eUZrn9inmEVef02w==",
|
"integrity": "sha512-hkNU1gIT8BNe8vmcsU7uYie0gzow/6AIj5KnGRBJQSZlgEu1NNuLVS11it5gAEdpmvJHelc34BwR439Iela+zQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/pegjs": "^0.10.0",
|
||||||
"big-integer": "^1.6.48"
|
"big-integer": "^1.6.48"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
"jsonschema": "^1.4.1",
|
"jsonschema": "^1.4.1",
|
||||||
"jspdf": "^2.5.1",
|
"jspdf": "^2.5.1",
|
||||||
"lexical": "^0.12.5",
|
"lexical": "^0.12.5",
|
||||||
"node-sql-parser": "^4.17.0",
|
"node-sql-parser": "^5.0.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hotkeys-hook": "^4.4.1",
|
"react-hotkeys-hook": "^4.4.1",
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import { Upload, Checkbox } from "@douyinfe/semi-ui";
|
import { Upload, Checkbox, Banner } from "@douyinfe/semi-ui";
|
||||||
import { STATUS } from "../../../data/constants";
|
import { STATUS } from "../../../data/constants";
|
||||||
|
|
||||||
export default function ImportSource({ importData, setImportData, setError }) {
|
export default function ImportSource({
|
||||||
|
importData,
|
||||||
|
setImportData,
|
||||||
|
error,
|
||||||
|
setError,
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Upload
|
<Upload
|
||||||
@ -60,6 +65,29 @@ export default function ImportSource({ importData, setImportData, setError }) {
|
|||||||
>
|
>
|
||||||
Overwrite existing diagram
|
Overwrite existing diagram
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
|
<div className="mt-2">
|
||||||
|
{error.type === STATUS.ERROR ? (
|
||||||
|
<Banner
|
||||||
|
type="danger"
|
||||||
|
fullMode={false}
|
||||||
|
description={<div>{error.message}</div>}
|
||||||
|
/>
|
||||||
|
) : error.type === STATUS.OK ? (
|
||||||
|
<Banner
|
||||||
|
type="info"
|
||||||
|
fullMode={false}
|
||||||
|
description={<div>{error.message}</div>}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
error.type === STATUS.WARNING && (
|
||||||
|
<Banner
|
||||||
|
type="warning"
|
||||||
|
fullMode={false}
|
||||||
|
description={<div>{error.message}</div>}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -114,9 +114,17 @@ export default function Modal({
|
|||||||
try {
|
try {
|
||||||
ast = parser.astify(importSource.src, { database: "MySQL" });
|
ast = parser.astify(importSource.src, { database: "MySQL" });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Toast.error(
|
setError({
|
||||||
"Could not parse the sql file. Make sure there are no syntax errors.",
|
type: STATUS.ERROR,
|
||||||
);
|
message:
|
||||||
|
err.name +
|
||||||
|
" [Ln " +
|
||||||
|
err.location.start.line +
|
||||||
|
", Col " +
|
||||||
|
err.location.start.column +
|
||||||
|
"]: " +
|
||||||
|
err.message,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,6 +132,7 @@ export default function Modal({
|
|||||||
if (importSource.overwrite) {
|
if (importSource.overwrite) {
|
||||||
setTables(d.tables);
|
setTables(d.tables);
|
||||||
setRelationships(d.relationships);
|
setRelationships(d.relationships);
|
||||||
|
setTransform((prev) => ({ ...prev, pan: { x: 0, y: 0 } }));
|
||||||
setNotes([]);
|
setNotes([]);
|
||||||
setAreas([]);
|
setAreas([]);
|
||||||
setTypes([]);
|
setTypes([]);
|
||||||
@ -133,6 +142,7 @@ export default function Modal({
|
|||||||
setTables((prev) => [...prev, ...d.tables]);
|
setTables((prev) => [...prev, ...d.tables]);
|
||||||
setRelationships((prev) => [...prev, ...d.relationships]);
|
setRelationships((prev) => [...prev, ...d.relationships]);
|
||||||
}
|
}
|
||||||
|
setModal(MODAL.NONE);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createNewDiagram = (id) => {
|
const createNewDiagram = (id) => {
|
||||||
@ -167,7 +177,6 @@ export default function Modal({
|
|||||||
return;
|
return;
|
||||||
case MODAL.IMPORT_SRC:
|
case MODAL.IMPORT_SRC:
|
||||||
parseSQLAndLoadDiagram();
|
parseSQLAndLoadDiagram();
|
||||||
setModal(MODAL.NONE);
|
|
||||||
return;
|
return;
|
||||||
case MODAL.OPEN:
|
case MODAL.OPEN:
|
||||||
if (selectedDiagramId === 0) return;
|
if (selectedDiagramId === 0) return;
|
||||||
@ -207,6 +216,7 @@ export default function Modal({
|
|||||||
<ImportSource
|
<ImportSource
|
||||||
importData={importSource}
|
importData={importSource}
|
||||||
setImportData={setImportSource}
|
setImportData={setImportSource}
|
||||||
|
error={error}
|
||||||
setError={setError}
|
setError={setError}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -80,7 +80,7 @@ export default function FieldDetails({ data, tid, index }) {
|
|||||||
undo: editField,
|
undo: editField,
|
||||||
redo: { values: data.values },
|
redo: { values: data.values },
|
||||||
message: `Edit table field values to "${JSON.stringify(
|
message: `Edit table field values to "${JSON.stringify(
|
||||||
data.values
|
data.values,
|
||||||
)}"`,
|
)}"`,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
@ -123,9 +123,11 @@ export default function FieldDetails({ data, tid, index }) {
|
|||||||
<div className="font-semibold">Precision</div>
|
<div className="font-semibold">Precision</div>
|
||||||
<Input
|
<Input
|
||||||
className="my-2 w-full"
|
className="my-2 w-full"
|
||||||
placeholder="Set precision: (size, d)"
|
placeholder="Set precision: size, d"
|
||||||
validateStatus={
|
validateStatus={
|
||||||
/^\(\d+,\s*\d+\)$|^$/.test(data.size) ? "default" : "error"
|
!data.size || /^\d+,\s*\d+$|^$/.test(data.size)
|
||||||
|
? "default"
|
||||||
|
: "error"
|
||||||
}
|
}
|
||||||
value={data.size}
|
value={data.size}
|
||||||
onChange={(value) => updateField(tid, index, { size: value })}
|
onChange={(value) => updateField(tid, index, { size: value })}
|
||||||
|
@ -123,7 +123,7 @@ export default function TableField({ data, tid, index }) {
|
|||||||
<Col span={3}>
|
<Col span={3}>
|
||||||
<Button
|
<Button
|
||||||
type={data.notNull ? "primary" : "tertiary"}
|
type={data.notNull ? "primary" : "tertiary"}
|
||||||
title="Nullable"
|
title="Not Null"
|
||||||
theme={data.notNull ? "solid" : "light"}
|
theme={data.notNull ? "solid" : "light"}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setUndoStack((prev) => [
|
setUndoStack((prev) => [
|
||||||
|
@ -1,9 +1,49 @@
|
|||||||
import { Cardinality } from "../data/constants";
|
import {
|
||||||
|
Cardinality,
|
||||||
|
tableColorStripHeight,
|
||||||
|
tableFieldHeight,
|
||||||
|
tableHeaderHeight,
|
||||||
|
} from "../data/constants";
|
||||||
|
|
||||||
|
function buildSQLFromAST(ast) {
|
||||||
|
if (ast.type === "binary_expr") {
|
||||||
|
const leftSQL = buildSQLFromAST(ast.left);
|
||||||
|
const rightSQL = buildSQLFromAST(ast.right);
|
||||||
|
return `${leftSQL} ${ast.operator} ${rightSQL}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast.type === "function") {
|
||||||
|
let expr = "";
|
||||||
|
expr = ast.name;
|
||||||
|
if (ast.args) {
|
||||||
|
expr +=
|
||||||
|
"(" +
|
||||||
|
ast.args.value
|
||||||
|
.map((v) => {
|
||||||
|
if (v.type === "column_ref") return "`" + v.column + "`";
|
||||||
|
if (
|
||||||
|
v.type === "single_quote_string" ||
|
||||||
|
v.type === "double_quote_string"
|
||||||
|
)
|
||||||
|
return "'" + v.value + "'";
|
||||||
|
return v.value;
|
||||||
|
})
|
||||||
|
.join(", ") +
|
||||||
|
")";
|
||||||
|
}
|
||||||
|
return expr;
|
||||||
|
} else if (ast.type === "column_ref") {
|
||||||
|
return "`" + ast.column + "`";
|
||||||
|
} else if (ast.type === "expr_list") {
|
||||||
|
return ast.value.map((v) => v.value).join(" AND ");
|
||||||
|
} else {
|
||||||
|
return typeof ast.value === "string" ? "'" + ast.value + "'" : ast.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function astToDiagram(ast) {
|
export function astToDiagram(ast) {
|
||||||
const tables = [];
|
const tables = [];
|
||||||
const relationships = [];
|
const relationships = [];
|
||||||
const inlineForeignKeys = [];
|
|
||||||
|
|
||||||
ast.forEach((e) => {
|
ast.forEach((e) => {
|
||||||
if (e.type === "create") {
|
if (e.type === "create") {
|
||||||
@ -14,13 +54,15 @@ export function astToDiagram(ast) {
|
|||||||
table.color = "#175e7a";
|
table.color = "#175e7a";
|
||||||
table.fields = [];
|
table.fields = [];
|
||||||
table.indices = [];
|
table.indices = [];
|
||||||
table.x = 0;
|
table.id = tables.length;
|
||||||
table.y = 0;
|
|
||||||
e.create_definitions.forEach((d) => {
|
e.create_definitions.forEach((d) => {
|
||||||
if (d.resource === "column") {
|
if (d.resource === "column") {
|
||||||
const field = {};
|
const field = {};
|
||||||
field.name = d.column.column;
|
field.name = d.column.column;
|
||||||
field.type = d.definition.dataType;
|
field.type = d.definition.dataType;
|
||||||
|
if (d.definition.expr && d.definition.expr.type === "expr_list") {
|
||||||
|
field.values = d.definition.expr.value.map((v) => v.value);
|
||||||
|
}
|
||||||
field.comment = "";
|
field.comment = "";
|
||||||
field.unique = false;
|
field.unique = false;
|
||||||
if (d.unique) field.unique = true;
|
if (d.unique) field.unique = true;
|
||||||
@ -31,39 +73,42 @@ export function astToDiagram(ast) {
|
|||||||
field.primary = false;
|
field.primary = false;
|
||||||
if (d.primary_key) field.primary = true;
|
if (d.primary_key) field.primary = true;
|
||||||
field.default = "";
|
field.default = "";
|
||||||
if (d.default_val) field.default = d.default_val.value.value.toString();
|
if (d.default_val) {
|
||||||
if (d.definition["length"]) field.size = d.definition["length"];
|
let defaultValue = "";
|
||||||
|
if (d.default_val.value.type === "function") {
|
||||||
|
defaultValue = d.default_val.value.name;
|
||||||
|
if (d.default_val.value.args) {
|
||||||
|
defaultValue +=
|
||||||
|
"(" +
|
||||||
|
d.default_val.value.args.value
|
||||||
|
.map((v) => {
|
||||||
|
if (
|
||||||
|
v.type === "single_quote_string" ||
|
||||||
|
v.type === "double_quote_string"
|
||||||
|
)
|
||||||
|
return "'" + v.value + "'";
|
||||||
|
return v.value;
|
||||||
|
})
|
||||||
|
.join(", ") +
|
||||||
|
")";
|
||||||
|
}
|
||||||
|
} else if (d.default_val.value.type === "null") {
|
||||||
|
defaultValue = "NULL";
|
||||||
|
} else {
|
||||||
|
defaultValue = d.default_val.value.value.toString();
|
||||||
|
}
|
||||||
|
field.default = defaultValue;
|
||||||
|
}
|
||||||
|
if (d.definition["length"]) {
|
||||||
|
if (d.definition.scale) {
|
||||||
|
field.size = d.definition["length"] + "," + d.definition.scale;
|
||||||
|
} else {
|
||||||
|
field.size = d.definition["length"];
|
||||||
|
}
|
||||||
|
}
|
||||||
field.check = "";
|
field.check = "";
|
||||||
if (d.check) {
|
if (d.check) {
|
||||||
let check = "";
|
field.check = buildSQLFromAST(d.check.definition[0]);
|
||||||
if (d.check.definition[0].left.column) {
|
|
||||||
let value = d.check.definition[0].right.value;
|
|
||||||
if (
|
|
||||||
d.check.definition[0].right.type === "double_quote_string" ||
|
|
||||||
d.check.definition[0].right.type === "single_quote_string"
|
|
||||||
)
|
|
||||||
value = "'" + value + "'";
|
|
||||||
check =
|
|
||||||
d.check.definition[0].left.column +
|
|
||||||
" " +
|
|
||||||
d.check.definition[0].operator +
|
|
||||||
" " +
|
|
||||||
value;
|
|
||||||
} else {
|
|
||||||
let value = d.check.definition[0].right.value;
|
|
||||||
if (
|
|
||||||
d.check.definition[0].left.type === "double_quote_string" ||
|
|
||||||
d.check.definition[0].left.type === "single_quote_string"
|
|
||||||
)
|
|
||||||
value = "'" + value + "'";
|
|
||||||
check =
|
|
||||||
value +
|
|
||||||
" " +
|
|
||||||
d.check.definition[0].operator +
|
|
||||||
" " +
|
|
||||||
d.check.definition[0].right.column;
|
|
||||||
}
|
|
||||||
field.check = check;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
table.fields.push(field);
|
table.fields.push(field);
|
||||||
@ -77,17 +122,58 @@ export function astToDiagram(ast) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else if (d.constraint_type === "FOREIGN KEY") {
|
} else if (d.constraint_type === "FOREIGN KEY") {
|
||||||
inlineForeignKeys.push({ ...d, startTable: e.table[0].table });
|
const relationship = {};
|
||||||
|
const startTableId = table.id;
|
||||||
|
const startTable = e.table[0].table;
|
||||||
|
const startField = d.definition[0].column;
|
||||||
|
const endTable = d.reference_definition.table[0].table;
|
||||||
|
const endField = d.reference_definition.definition[0].column;
|
||||||
|
|
||||||
|
const endTableId = tables.findIndex((t) => t.name === endTable);
|
||||||
|
if (endTableId === -1) return;
|
||||||
|
|
||||||
|
const endFieldId = tables[endTableId].fields.findIndex(
|
||||||
|
(f) => f.name === endField,
|
||||||
|
);
|
||||||
|
if (endField === -1) return;
|
||||||
|
|
||||||
|
const startFieldId = table.fields.findIndex(
|
||||||
|
(f) => f.name === startField,
|
||||||
|
);
|
||||||
|
if (startFieldId === -1) return;
|
||||||
|
|
||||||
|
relationship.name = startTable + "_" + startField + "_fk";
|
||||||
|
relationship.startTableId = startTableId;
|
||||||
|
relationship.endTableId = endTableId;
|
||||||
|
relationship.endFieldId = endFieldId;
|
||||||
|
relationship.startFieldId = startFieldId;
|
||||||
|
let updateConstraint = "No action";
|
||||||
|
let deleteConstraint = "No action";
|
||||||
|
d.reference_definition.on_action.forEach((c) => {
|
||||||
|
if (c.type === "on update") {
|
||||||
|
updateConstraint = c.value.value;
|
||||||
|
updateConstraint =
|
||||||
|
updateConstraint[0].toUpperCase() +
|
||||||
|
updateConstraint.substring(1);
|
||||||
|
} else if (c.type === "on delete") {
|
||||||
|
deleteConstraint = c.value.value;
|
||||||
|
deleteConstraint =
|
||||||
|
deleteConstraint[0].toUpperCase() +
|
||||||
|
deleteConstraint.substring(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
relationship.updateConstraint = updateConstraint;
|
||||||
|
relationship.deleteConstraint = deleteConstraint;
|
||||||
|
relationship.cardinality = Cardinality.ONE_TO_ONE;
|
||||||
|
relationships.push(relationship);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
tables.push(table);
|
table.fields.forEach((f, j) => {
|
||||||
tables.forEach((e, i) => {
|
f.id = j;
|
||||||
e.id = i;
|
|
||||||
e.fields.forEach((f, j) => {
|
|
||||||
f.id = j;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
tables.push(table);
|
||||||
} else if (e.keyword === "index") {
|
} else if (e.keyword === "index") {
|
||||||
const index = {};
|
const index = {};
|
||||||
index.name = e.index;
|
index.name = e.index;
|
||||||
@ -108,145 +194,89 @@ export function astToDiagram(ast) {
|
|||||||
if (found !== -1) tables[found].indices.forEach((i, j) => (i.id = j));
|
if (found !== -1) tables[found].indices.forEach((i, j) => (i.id = j));
|
||||||
}
|
}
|
||||||
} else if (e.type === "alter") {
|
} else if (e.type === "alter") {
|
||||||
if (
|
e.expr.forEach((expr) => {
|
||||||
e.expr[0].action === "add" &&
|
if (
|
||||||
e.expr[0].create_definitions.constraint_type === "FOREIGN KEY"
|
expr.action === "add" &&
|
||||||
) {
|
expr.create_definitions.constraint_type === "FOREIGN KEY"
|
||||||
const relationship = {};
|
) {
|
||||||
const startTable = e.table[0].table;
|
const relationship = {};
|
||||||
const startField = e.expr[0].create_definitions.definition[0].column;
|
const startTable = e.table[0].table;
|
||||||
const endTable =
|
const startField = expr.create_definitions.definition[0].column;
|
||||||
e.expr[0].create_definitions.reference_definition.table[0].table;
|
const endTable =
|
||||||
const endField =
|
expr.create_definitions.reference_definition.table[0].table;
|
||||||
e.expr[0].create_definitions.reference_definition.definition[0]
|
const endField =
|
||||||
.column;
|
expr.create_definitions.reference_definition.definition[0].column;
|
||||||
let updateConstraint = "No action";
|
let updateConstraint = "No action";
|
||||||
let deleteConstraint = "No action";
|
let deleteConstraint = "No action";
|
||||||
e.expr[0].create_definitions.reference_definition.on_action.forEach(
|
expr.create_definitions.reference_definition.on_action.forEach(
|
||||||
(c) => {
|
(c) => {
|
||||||
if (c.type === "on update") {
|
if (c.type === "on update") {
|
||||||
updateConstraint = c.value.value;
|
updateConstraint = c.value.value;
|
||||||
updateConstraint =
|
updateConstraint =
|
||||||
updateConstraint[0].toUpperCase() +
|
updateConstraint[0].toUpperCase() +
|
||||||
updateConstraint.substring(1);
|
updateConstraint.substring(1);
|
||||||
} else if (c.type === "on delete") {
|
} else if (c.type === "on delete") {
|
||||||
deleteConstraint = c.value.value;
|
deleteConstraint = c.value.value;
|
||||||
deleteConstraint =
|
deleteConstraint =
|
||||||
deleteConstraint[0].toUpperCase() +
|
deleteConstraint[0].toUpperCase() +
|
||||||
deleteConstraint.substring(1);
|
deleteConstraint.substring(1);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let startTableId = -1;
|
const startTableId = tables.findIndex((t) => t.name === startTable);
|
||||||
let startFieldId = -1;
|
if (startTable === -1) return;
|
||||||
let endTableId = -1;
|
|
||||||
let endFieldId = -1;
|
|
||||||
|
|
||||||
tables.forEach((t) => {
|
const endTableId = tables.findIndex((t) => t.name === endTable);
|
||||||
if (t.name === startTable) {
|
if (endTableId === -1) return;
|
||||||
startTableId = t.id;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.name === endTable) {
|
const endFieldId = tables[endTableId].fields.findIndex(
|
||||||
endTableId = t.id;
|
(f) => f.name === endField,
|
||||||
}
|
);
|
||||||
});
|
if (endField === -1) return;
|
||||||
|
|
||||||
if (startTableId === -1 || endTableId === -1) return;
|
const startFieldId = tables[startTableId].fields.findIndex(
|
||||||
|
(f) => f.name === startField,
|
||||||
|
);
|
||||||
|
if (startFieldId === -1) return;
|
||||||
|
|
||||||
tables[startTableId].fields.forEach((f) => {
|
relationship.name = startTable + "_" + startField + "_fk";
|
||||||
if (f.name === startField) {
|
relationship.startTableId = startTableId;
|
||||||
startFieldId = f.id;
|
relationship.startFieldId = startFieldId;
|
||||||
return;
|
relationship.endTableId = endTableId;
|
||||||
}
|
relationship.endFieldId = endFieldId;
|
||||||
|
relationship.updateConstraint = updateConstraint;
|
||||||
|
relationship.deleteConstraint = deleteConstraint;
|
||||||
|
relationship.cardinality = Cardinality.ONE_TO_ONE;
|
||||||
|
relationships.push(relationship);
|
||||||
|
|
||||||
if (f.name === endField) {
|
relationships.forEach((r, i) => (r.id = i));
|
||||||
endFieldId = f.id;
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
if (startFieldId === -1 || endFieldId === -1) return;
|
|
||||||
|
|
||||||
relationship.name = startTable + "_" + startField + "_fk";
|
|
||||||
relationship.startTableId = startTableId;
|
|
||||||
relationship.startFieldId = startFieldId;
|
|
||||||
relationship.endTableId = endTableId;
|
|
||||||
relationship.endFieldId = endFieldId;
|
|
||||||
relationship.updateConstraint = updateConstraint;
|
|
||||||
relationship.deleteConstraint = deleteConstraint;
|
|
||||||
relationship.cardinality = Cardinality.ONE_TO_ONE;
|
|
||||||
relationships.push(relationship);
|
|
||||||
|
|
||||||
relationships.forEach((r, i) => (r.id = i));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
inlineForeignKeys.forEach((fk) => {
|
|
||||||
const relationship = {};
|
|
||||||
const startTable = fk.startTable;
|
|
||||||
const startField = fk.definition[0].column;
|
|
||||||
const endTable = fk.reference_definition.table[0].table;
|
|
||||||
const endField = fk.reference_definition.definition[0].column;
|
|
||||||
let updateConstraint = "No action";
|
|
||||||
let deleteConstraint = "No action";
|
|
||||||
fk.reference_definition.on_action.forEach((c) => {
|
|
||||||
if (c.type === "on update") {
|
|
||||||
updateConstraint = c.value.value;
|
|
||||||
updateConstraint =
|
|
||||||
updateConstraint[0].toUpperCase() + updateConstraint.substring(1);
|
|
||||||
} else if (c.type === "on delete") {
|
|
||||||
deleteConstraint = c.value.value;
|
|
||||||
deleteConstraint =
|
|
||||||
deleteConstraint[0].toUpperCase() + deleteConstraint.substring(1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let startTableId = -1;
|
|
||||||
let startFieldId = -1;
|
|
||||||
let endTableId = -1;
|
|
||||||
let endFieldId = -1;
|
|
||||||
|
|
||||||
tables.forEach((t) => {
|
|
||||||
if (t.name === startTable) {
|
|
||||||
startTableId = t.id;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.name === endTable) {
|
|
||||||
endTableId = t.id;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (startTableId === -1 || endTableId === -1) return;
|
|
||||||
|
|
||||||
tables[startTableId].fields.forEach((f) => {
|
|
||||||
if (f.name === startField) {
|
|
||||||
startFieldId = f.id;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (f.name === endField) {
|
|
||||||
endFieldId = f.id;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (startFieldId === -1 || endFieldId === -1) return;
|
|
||||||
|
|
||||||
relationship.name = startTable + "_" + startField + "_fk";
|
|
||||||
relationship.startTableId = startTableId;
|
|
||||||
relationship.startFieldId = startFieldId;
|
|
||||||
relationship.endTableId = endTableId;
|
|
||||||
relationship.endFieldId = endFieldId;
|
|
||||||
relationship.updateConstraint = updateConstraint;
|
|
||||||
relationship.deleteConstraint = deleteConstraint;
|
|
||||||
relationship.cardinality = Cardinality.ONE_TO_ONE;
|
|
||||||
relationships.push(relationship);
|
|
||||||
});
|
|
||||||
|
|
||||||
relationships.forEach((r, i) => (r.id = i));
|
relationships.forEach((r, i) => (r.id = i));
|
||||||
|
|
||||||
|
let maxHeight = -1;
|
||||||
|
const tableWidth = 200;
|
||||||
|
const gapX = 54;
|
||||||
|
const gapY = 40;
|
||||||
|
tables.forEach((table, i) => {
|
||||||
|
if (i < tables.length / 2) {
|
||||||
|
table.x = i * tableWidth + (i + 1) * gapX;
|
||||||
|
table.y = gapY;
|
||||||
|
const height =
|
||||||
|
table.fields.length * tableFieldHeight +
|
||||||
|
tableHeaderHeight +
|
||||||
|
tableColorStripHeight;
|
||||||
|
maxHeight = Math.max(height, maxHeight);
|
||||||
|
} else {
|
||||||
|
const index = tables.length - i - 1;
|
||||||
|
table.x = index * tableWidth + (index + 1) * gapX;
|
||||||
|
table.y = maxHeight + 2 * gapY;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return { tables, relationships };
|
return { tables, relationships };
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { strHasQuotes } from "./utils";
|
import { isFunction, strHasQuotes } from "./utils";
|
||||||
|
|
||||||
function validateDateStr(str) {
|
function validateDateStr(str) {
|
||||||
return /^(?!0000)(?!00)(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9]|3[01])|(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31))$/.test(
|
return /^(?!0000)(?!00)(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9]|3[01])|(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31))$/.test(
|
||||||
@ -9,13 +9,23 @@ function validateDateStr(str) {
|
|||||||
function checkDefault(field) {
|
function checkDefault(field) {
|
||||||
if (field.default === "") return true;
|
if (field.default === "") return true;
|
||||||
|
|
||||||
|
if (isFunction(field.default)) return true;
|
||||||
|
|
||||||
|
if (!field.notNull && field.default.toLowerCase() === "null") return true;
|
||||||
|
|
||||||
switch (field.type) {
|
switch (field.type) {
|
||||||
case "INT":
|
case "INT":
|
||||||
case "BIGINT":
|
case "BIGINT":
|
||||||
case "SMALLINT":
|
case "SMALLINT":
|
||||||
return /^-?\d*$/.test(field.default);
|
return /^-?\d*$/.test(field.default);
|
||||||
|
case "SET": {
|
||||||
|
const defaultValues = field.default.split(",");
|
||||||
|
for (let i = 0; i < defaultValues.length; i++) {
|
||||||
|
if (!field.values.includes(defaultValues[i].trim())) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
case "ENUM":
|
case "ENUM":
|
||||||
case "SET":
|
|
||||||
return field.values.includes(field.default);
|
return field.values.includes(field.default);
|
||||||
case "CHAR":
|
case "CHAR":
|
||||||
case "VARCHAR":
|
case "VARCHAR":
|
||||||
@ -30,7 +40,8 @@ function checkDefault(field) {
|
|||||||
);
|
);
|
||||||
case "BOOLEAN":
|
case "BOOLEAN":
|
||||||
return (
|
return (
|
||||||
field.default.trim() === "false" || field.default.trim() === "true"
|
field.default.trim().toLowerCase() === "false" ||
|
||||||
|
field.default.trim().toLowerCase() === "true"
|
||||||
);
|
);
|
||||||
case "FLOAT":
|
case "FLOAT":
|
||||||
case "DECIMAL":
|
case "DECIMAL":
|
||||||
@ -55,6 +66,9 @@ function checkDefault(field) {
|
|||||||
return parseInt(date[0]) >= 1970 && parseInt(date[0]) <= 2038;
|
return parseInt(date[0]) >= 1970 && parseInt(date[0]) <= 2038;
|
||||||
}
|
}
|
||||||
case "DATETIME": {
|
case "DATETIME": {
|
||||||
|
if (field.default.toUpperCase() === "CURRENT_TIMESTAMP") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (!/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(field.default)) {
|
if (!/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(field.default)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -116,12 +130,6 @@ export function getIssues(diagram) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (field.type === "DOUBLE" && field.size !== "") {
|
|
||||||
issues.push(
|
|
||||||
`Specifying number of digits for floating point data types is deprecated.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (duplicateFieldNames[field.name]) {
|
if (duplicateFieldNames[field.name]) {
|
||||||
issues.push(`Duplicate table fields in table "${table.name}"`);
|
issues.push(`Duplicate table fields in table "${table.name}"`);
|
||||||
} else {
|
} else {
|
||||||
@ -182,12 +190,6 @@ export function getIssues(diagram) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (field.type === "DOUBLE" && field.size !== "") {
|
|
||||||
issues.push(
|
|
||||||
`Specifying number of digits for floating point data types is deprecated.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (duplicateFieldNames[field.name]) {
|
if (duplicateFieldNames[field.name]) {
|
||||||
issues.push(`Duplicate type fields in "${type.name}"`);
|
issues.push(`Duplicate type fields in "${type.name}"`);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { sqlDataTypes } from "../data/constants";
|
import { sqlDataTypes } from "../data/constants";
|
||||||
import { strHasQuotes } from "./utils";
|
import { isFunction, isKeyword, strHasQuotes } from "./utils";
|
||||||
|
|
||||||
export function getJsonType(f) {
|
export function getJsonType(f) {
|
||||||
if (!sqlDataTypes.includes(f.type)) {
|
if (!sqlDataTypes.includes(f.type)) {
|
||||||
@ -44,10 +44,7 @@ export function getTypeString(field, dbms = "mysql", baseType = false) {
|
|||||||
if (field.type === "UUID") {
|
if (field.type === "UUID") {
|
||||||
return `VARCHAR(36)`;
|
return `VARCHAR(36)`;
|
||||||
}
|
}
|
||||||
if (isSized(field.type)) {
|
if (hasPrecision(field.type) || isSized(field.type)) {
|
||||||
return `${field.type}(${field.size})`;
|
|
||||||
}
|
|
||||||
if (hasPrecision(field.type)) {
|
|
||||||
return `${field.type}${field.size ? `(${field.size})` : ""}`;
|
return `${field.type}${field.size ? `(${field.size})` : ""}`;
|
||||||
}
|
}
|
||||||
if (field.type === "SET" || field.type === "ENUM") {
|
if (field.type === "SET" || field.type === "ENUM") {
|
||||||
@ -147,13 +144,16 @@ export function hasQuotes(type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function parseDefault(field) {
|
export function parseDefault(field) {
|
||||||
if (strHasQuotes(field.default)) {
|
if (
|
||||||
|
strHasQuotes(field.default) ||
|
||||||
|
isFunction(field.default) ||
|
||||||
|
isKeyword(field.default) ||
|
||||||
|
!hasQuotes(field.type)
|
||||||
|
) {
|
||||||
return field.default;
|
return field.default;
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasQuotes(field.type) && field.default.toLowerCase() !== "null"
|
return `'${field.default}'`;
|
||||||
? `'${field.default}'`
|
|
||||||
: `${field.default}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function jsonToMySQL(obj) {
|
export function jsonToMySQL(obj) {
|
||||||
|
@ -24,3 +24,13 @@ export function strHasQuotes(str) {
|
|||||||
(str[0] === str[str.length - 1] && str[0] === "`")
|
(str[0] === str[str.length - 1] && str[0] === "`")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const keywords = ["CURRENT_TIMESTAMP", "NULL"];
|
||||||
|
|
||||||
|
export function isKeyword(str) {
|
||||||
|
return keywords.includes(str.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isFunction(str) {
|
||||||
|
return /\w+\([^)]*\)$/.test(str);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user