From 1beed53cb640c831ec6d772cf0cbef0f5dfa9fdd Mon Sep 17 00:00:00 2001 From: 1ilit Date: Sat, 20 Apr 2024 11:39:34 +0300 Subject: [PATCH 01/19] Handle default function parsing --- package-lock.json | 14 ++++++++++---- package.json | 2 +- src/utils/astToDiagram.js | 27 +++++++++++++++++++++++++-- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 685fee2..9f66e86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "jsonschema": "^1.4.1", "jspdf": "^2.5.1", "lexical": "^0.12.5", - "node-sql-parser": "^4.17.0", + "node-sql-parser": "^5.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hotkeys-hook": "^4.4.1", @@ -1715,6 +1715,11 @@ "@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": { "version": "15.7.11", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", @@ -4368,10 +4373,11 @@ "dev": true }, "node_modules/node-sql-parser": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/node-sql-parser/-/node-sql-parser-4.17.0.tgz", - "integrity": "sha512-3IhovpmUBpcETnoKK/KBdkz2mz53kVG5E1dnqz1QuYvtzdxYZW5xaGGEvW9u6Yyy2ivwR3eUZrn9inmEVef02w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/node-sql-parser/-/node-sql-parser-5.0.0.tgz", + "integrity": "sha512-hkNU1gIT8BNe8vmcsU7uYie0gzow/6AIj5KnGRBJQSZlgEu1NNuLVS11it5gAEdpmvJHelc34BwR439Iela+zQ==", "dependencies": { + "@types/pegjs": "^0.10.0", "big-integer": "^1.6.48" }, "engines": { diff --git a/package.json b/package.json index 875cdbc..fa19917 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "jsonschema": "^1.4.1", "jspdf": "^2.5.1", "lexical": "^0.12.5", - "node-sql-parser": "^4.17.0", + "node-sql-parser": "^5.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hotkeys-hook": "^4.4.1", diff --git a/src/utils/astToDiagram.js b/src/utils/astToDiagram.js index 2742219..660e3bf 100644 --- a/src/utils/astToDiagram.js +++ b/src/utils/astToDiagram.js @@ -31,7 +31,30 @@ export function astToDiagram(ast) { field.primary = false; if (d.primary_key) field.primary = true; field.default = ""; - if (d.default_val) field.default = d.default_val.value.value.toString(); + if (d.default_val) { + 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 { + defaultValue = d.default_val.value.value.toString(); + } + field.default = defaultValue; + } if (d.definition["length"]) field.size = d.definition["length"]; field.check = ""; if (d.check) { @@ -135,7 +158,7 @@ export function astToDiagram(ast) { deleteConstraint[0].toUpperCase() + deleteConstraint.substring(1); } - } + }, ); let startTableId = -1; From bce0aeac4837ad9e3a2755c734a9f961674e5dce Mon Sep 17 00:00:00 2001 From: 1ilit Date: Sat, 20 Apr 2024 13:59:05 +0300 Subject: [PATCH 02/19] Handle enum and set value parsing --- src/utils/astToDiagram.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/utils/astToDiagram.js b/src/utils/astToDiagram.js index 660e3bf..136ffa5 100644 --- a/src/utils/astToDiagram.js +++ b/src/utils/astToDiagram.js @@ -21,6 +21,9 @@ export function astToDiagram(ast) { const field = {}; field.name = d.column.column; 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.unique = false; if (d.unique) field.unique = true; From 124af2735fab305ef4a0f15d985089b49d506f94 Mon Sep 17 00:00:00 2001 From: 1ilit Date: Sat, 20 Apr 2024 14:14:46 +0300 Subject: [PATCH 03/19] Fix `field.size` parsing --- src/utils/astToDiagram.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/utils/astToDiagram.js b/src/utils/astToDiagram.js index 136ffa5..40931fc 100644 --- a/src/utils/astToDiagram.js +++ b/src/utils/astToDiagram.js @@ -58,7 +58,14 @@ export function astToDiagram(ast) { } field.default = defaultValue; } - if (d.definition["length"]) field.size = d.definition["length"]; + if (d.definition["length"]) { + if (d.definition.scale) { + field.size = + "(" + d.definition["length"] + "," + d.definition.scale + ")"; + } else { + field.size = d.definition["length"]; + } + } field.check = ""; if (d.check) { let check = ""; From 2f837d625bdd446273c71bbb0ea1d8fcf1ff64e6 Mon Sep 17 00:00:00 2001 From: 1ilit Date: Sat, 20 Apr 2024 14:29:20 +0300 Subject: [PATCH 04/19] Make boolean defaults case insensitive --- src/utils/issues.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/issues.js b/src/utils/issues.js index b95fa74..05ba93f 100644 --- a/src/utils/issues.js +++ b/src/utils/issues.js @@ -30,7 +30,8 @@ function checkDefault(field) { ); case "BOOLEAN": return ( - field.default.trim() === "false" || field.default.trim() === "true" + field.default.trim().toLowerCase() === "false" || + field.default.trim().toLowerCase() === "true" ); case "FLOAT": case "DECIMAL": From c51bf5815fc547f0667652ae3c2c64a037dfdc1b Mon Sep 17 00:00:00 2001 From: 1ilit Date: Sat, 20 Apr 2024 14:40:49 +0300 Subject: [PATCH 05/19] Accept empty precision input fields --- src/components/EditorSidePanel/TablesTab/FieldDetails.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/EditorSidePanel/TablesTab/FieldDetails.jsx b/src/components/EditorSidePanel/TablesTab/FieldDetails.jsx index 30b7e33..46d41df 100644 --- a/src/components/EditorSidePanel/TablesTab/FieldDetails.jsx +++ b/src/components/EditorSidePanel/TablesTab/FieldDetails.jsx @@ -80,7 +80,7 @@ export default function FieldDetails({ data, tid, index }) { undo: editField, redo: { values: data.values }, message: `Edit table field values to "${JSON.stringify( - data.values + data.values, )}"`, }, ]); @@ -125,7 +125,9 @@ export default function FieldDetails({ data, tid, index }) { className="my-2 w-full" placeholder="Set precision: (size, d)" validateStatus={ - /^\(\d+,\s*\d+\)$|^$/.test(data.size) ? "default" : "error" + !data.size || /^\(\d+,\s*\d+\)$|^$/.test(data.size) + ? "default" + : "error" } value={data.size} onChange={(value) => updateField(tid, index, { size: value })} From 22f7012c472f043f8ed255a353067f80daaa3f95 Mon Sep 17 00:00:00 2001 From: 1ilit Date: Sat, 20 Apr 2024 15:04:55 +0300 Subject: [PATCH 06/19] Validate default values for set data type --- src/utils/issues.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/utils/issues.js b/src/utils/issues.js index 05ba93f..997849d 100644 --- a/src/utils/issues.js +++ b/src/utils/issues.js @@ -14,8 +14,14 @@ function checkDefault(field) { case "BIGINT": case "SMALLINT": 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 "SET": return field.values.includes(field.default); case "CHAR": case "VARCHAR": From e8ea47fd3f61f9d7aaf0071f622a45780d42316f Mon Sep 17 00:00:00 2001 From: 1ilit Date: Sat, 20 Apr 2024 15:23:29 +0300 Subject: [PATCH 07/19] Accept `CURRENT_TIMESTAMP` for datetime --- src/utils/issues.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/utils/issues.js b/src/utils/issues.js index 997849d..56f3c19 100644 --- a/src/utils/issues.js +++ b/src/utils/issues.js @@ -62,6 +62,9 @@ function checkDefault(field) { return parseInt(date[0]) >= 1970 && parseInt(date[0]) <= 2038; } 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)) { return false; } From 898d81d6e90c7ac8579e01bffb731cc5042e3a76 Mon Sep 17 00:00:00 2001 From: 1ilit Date: Sat, 20 Apr 2024 18:16:41 +0300 Subject: [PATCH 08/19] Display sql error location and message on import --- .../EditorHeader/Modal/ImportSource.jsx | 32 +++++++++++++++++-- src/components/EditorHeader/Modal/Modal.jsx | 17 +++++++--- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/components/EditorHeader/Modal/ImportSource.jsx b/src/components/EditorHeader/Modal/ImportSource.jsx index 1b86454..f5283ab 100644 --- a/src/components/EditorHeader/Modal/ImportSource.jsx +++ b/src/components/EditorHeader/Modal/ImportSource.jsx @@ -1,7 +1,12 @@ -import { Upload, Checkbox } from "@douyinfe/semi-ui"; +import { Upload, Checkbox, Banner } from "@douyinfe/semi-ui"; import { STATUS } from "../../../data/constants"; -export default function ImportSource({ importData, setImportData, setError }) { +export default function ImportSource({ + importData, + setImportData, + error, + setError, +}) { return (
Overwrite existing diagram +
+ {error.type === STATUS.ERROR ? ( + {error.message}
} + /> + ) : error.type === STATUS.OK ? ( + {error.message}
} + /> + ) : ( + error.type === STATUS.WARNING && ( + {error.message}} + /> + ) + )} + ); diff --git a/src/components/EditorHeader/Modal/Modal.jsx b/src/components/EditorHeader/Modal/Modal.jsx index 462503b..841881c 100644 --- a/src/components/EditorHeader/Modal/Modal.jsx +++ b/src/components/EditorHeader/Modal/Modal.jsx @@ -114,9 +114,17 @@ export default function Modal({ try { ast = parser.astify(importSource.src, { database: "MySQL" }); } catch (err) { - Toast.error( - "Could not parse the sql file. Make sure there are no syntax errors.", - ); + setError({ + type: STATUS.ERROR, + message: + err.name + + " [Ln " + + err.location.start.line + + ", Col " + + err.location.start.column + + "]: " + + err.message, + }); return; } @@ -133,6 +141,7 @@ export default function Modal({ setTables((prev) => [...prev, ...d.tables]); setRelationships((prev) => [...prev, ...d.relationships]); } + setModal(MODAL.NONE); }; const createNewDiagram = (id) => { @@ -167,7 +176,6 @@ export default function Modal({ return; case MODAL.IMPORT_SRC: parseSQLAndLoadDiagram(); - setModal(MODAL.NONE); return; case MODAL.OPEN: if (selectedDiagramId === 0) return; @@ -207,6 +215,7 @@ export default function Modal({ ); From 4a879354122d4540c7121a54c9f1c30516fb99e0 Mon Sep 17 00:00:00 2001 From: 1ilit Date: Mon, 22 Apr 2024 13:05:46 +0300 Subject: [PATCH 09/19] Fix field precision and size input and parsing --- .../EditorSidePanel/TablesTab/FieldDetails.jsx | 4 ++-- src/utils/astToDiagram.js | 13 +++++-------- src/utils/toSQL.js | 5 +---- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/components/EditorSidePanel/TablesTab/FieldDetails.jsx b/src/components/EditorSidePanel/TablesTab/FieldDetails.jsx index 46d41df..ae574ba 100644 --- a/src/components/EditorSidePanel/TablesTab/FieldDetails.jsx +++ b/src/components/EditorSidePanel/TablesTab/FieldDetails.jsx @@ -123,9 +123,9 @@ export default function FieldDetails({ data, tid, index }) {
Precision
{ - e.id = i; - e.fields.forEach((f, j) => { - f.id = j; - }); + table.fields.forEach((f, j) => { + f.id = j; }); + table.id = tables.length; + tables.push(table); } else if (e.keyword === "index") { const index = {}; index.name = e.index; diff --git a/src/utils/toSQL.js b/src/utils/toSQL.js index 4720a6e..28bb4db 100644 --- a/src/utils/toSQL.js +++ b/src/utils/toSQL.js @@ -44,10 +44,7 @@ export function getTypeString(field, dbms = "mysql", baseType = false) { if (field.type === "UUID") { return `VARCHAR(36)`; } - if (isSized(field.type)) { - return `${field.type}(${field.size})`; - } - if (hasPrecision(field.type)) { + if (hasPrecision(field.type) || isSized(field.type)) { return `${field.type}${field.size ? `(${field.size})` : ""}`; } if (field.type === "SET" || field.type === "ENUM") { From 11e83bb12bdcb6d5cb3c7f9e93a7235084be5048 Mon Sep 17 00:00:00 2001 From: 1ilit Date: Mon, 22 Apr 2024 21:42:47 +0300 Subject: [PATCH 10/19] Arrange tables in import from source (#28) --- src/components/EditorHeader/Modal/Modal.jsx | 1 + src/utils/astToDiagram.js | 29 ++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/components/EditorHeader/Modal/Modal.jsx b/src/components/EditorHeader/Modal/Modal.jsx index 841881c..f2f870b 100644 --- a/src/components/EditorHeader/Modal/Modal.jsx +++ b/src/components/EditorHeader/Modal/Modal.jsx @@ -132,6 +132,7 @@ export default function Modal({ if (importSource.overwrite) { setTables(d.tables); setRelationships(d.relationships); + setTransform((prev) => ({ ...prev, pan: { x: 0, y: 0 } })); setNotes([]); setAreas([]); setTypes([]); diff --git a/src/utils/astToDiagram.js b/src/utils/astToDiagram.js index 3bb650f..1696d3e 100644 --- a/src/utils/astToDiagram.js +++ b/src/utils/astToDiagram.js @@ -1,4 +1,9 @@ -import { Cardinality } from "../data/constants"; +import { + Cardinality, + tableColorStripHeight, + tableFieldHeight, + tableHeaderHeight, +} from "../data/constants"; export function astToDiagram(ast) { const tables = []; @@ -14,8 +19,6 @@ export function astToDiagram(ast) { table.color = "#175e7a"; table.fields = []; table.indices = []; - table.x = 0; - table.y = 0; e.create_definitions.forEach((d) => { if (d.resource === "column") { const field = {}; @@ -278,5 +281,25 @@ export function astToDiagram(ast) { 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 }; } From ca8ee61ee96fbe5aa2bac465ab5c41fcbb3b6560 Mon Sep 17 00:00:00 2001 From: 1ilit Date: Tue, 23 Apr 2024 13:48:31 +0300 Subject: [PATCH 11/19] Fix parsing alter statements --- src/utils/astToDiagram.js | 233 +++++++++++++++++--------------------- 1 file changed, 101 insertions(+), 132 deletions(-) diff --git a/src/utils/astToDiagram.js b/src/utils/astToDiagram.js index 1696d3e..82d2f2f 100644 --- a/src/utils/astToDiagram.js +++ b/src/utils/astToDiagram.js @@ -8,7 +8,6 @@ import { export function astToDiagram(ast) { const tables = []; const relationships = []; - const inlineForeignKeys = []; ast.forEach((e) => { if (e.type === "create") { @@ -19,6 +18,7 @@ export function astToDiagram(ast) { table.color = "#175e7a"; table.fields = []; table.indices = []; + table.id = tables.length; e.create_definitions.forEach((d) => { if (d.resource === "column") { const field = {}; @@ -112,14 +112,57 @@ export function astToDiagram(ast) { }); }); } 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); } } }); table.fields.forEach((f, j) => { f.id = j; }); - table.id = tables.length; tables.push(table); } else if (e.keyword === "index") { const index = {}; @@ -141,144 +184,70 @@ export function astToDiagram(ast) { if (found !== -1) tables[found].indices.forEach((i, j) => (i.id = j)); } } else if (e.type === "alter") { - if ( - e.expr[0].action === "add" && - e.expr[0].create_definitions.constraint_type === "FOREIGN KEY" - ) { - const relationship = {}; - const startTable = e.table[0].table; - const startField = e.expr[0].create_definitions.definition[0].column; - const endTable = - e.expr[0].create_definitions.reference_definition.table[0].table; - const endField = - e.expr[0].create_definitions.reference_definition.definition[0] - .column; - let updateConstraint = "No action"; - let deleteConstraint = "No action"; - e.expr[0].create_definitions.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); - } - }, - ); + e.expr.forEach((expr) => { + if ( + expr.action === "add" && + expr.create_definitions.constraint_type === "FOREIGN KEY" + ) { + console.log(e); + const relationship = {}; + const startTable = e.table[0].table; + const startField = expr.create_definitions.definition[0].column; + const endTable = + expr.create_definitions.reference_definition.table[0].table; + const endField = + expr.create_definitions.reference_definition.definition[0].column; + let updateConstraint = "No action"; + let deleteConstraint = "No action"; + expr.create_definitions.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; + const startTableId = tables.findIndex((t) => t.name === startTable); + if (startTable === -1) return; - tables.forEach((t) => { - if (t.name === startTable) { - startTableId = t.id; - return; - } + const endTableId = tables.findIndex((t) => t.name === endTable); + if (endTableId === -1) return; - if (t.name === endTable) { - endTableId = t.id; - } - }); + const endFieldId = tables[endTableId].fields.findIndex( + (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) => { - if (f.name === startField) { - startFieldId = f.id; - 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); + console.log(relationship); - 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)); + } + }); } }); - 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)); let maxHeight = -1; From 2751a6d6df124a9c3e973c2196d0eeefadef1bcf Mon Sep 17 00:00:00 2001 From: 1ilit Date: Wed, 24 Apr 2024 11:49:00 +0300 Subject: [PATCH 12/19] Take in functions as defaults --- src/utils/issues.js | 4 +++- src/utils/toSQL.js | 4 ++-- src/utils/utils.js | 4 ++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/utils/issues.js b/src/utils/issues.js index 56f3c19..6a7dbcf 100644 --- a/src/utils/issues.js +++ b/src/utils/issues.js @@ -1,4 +1,4 @@ -import { strHasQuotes } from "./utils"; +import { isFunction, strHasQuotes } from "./utils"; 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( @@ -9,6 +9,8 @@ function validateDateStr(str) { function checkDefault(field) { if (field.default === "") return true; + if (isFunction(field.default)) return true; + switch (field.type) { case "INT": case "BIGINT": diff --git a/src/utils/toSQL.js b/src/utils/toSQL.js index 28bb4db..bd2904f 100644 --- a/src/utils/toSQL.js +++ b/src/utils/toSQL.js @@ -1,5 +1,5 @@ import { sqlDataTypes } from "../data/constants"; -import { strHasQuotes } from "./utils"; +import { isFunction, strHasQuotes } from "./utils"; export function getJsonType(f) { if (!sqlDataTypes.includes(f.type)) { @@ -144,7 +144,7 @@ export function hasQuotes(type) { } export function parseDefault(field) { - if (strHasQuotes(field.default)) { + if (strHasQuotes(field.default) || isFunction(field.default)) { return field.default; } diff --git a/src/utils/utils.js b/src/utils/utils.js index 53226a6..cafca7c 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -24,3 +24,7 @@ export function strHasQuotes(str) { (str[0] === str[str.length - 1] && str[0] === "`") ); } + +export function isFunction(str) { + return /\w+\([^)]*\)$/.test(str); +} From 583138949bc567a70a5a51804f5b893035baf428 Mon Sep 17 00:00:00 2001 From: 1ilit Date: Wed, 24 Apr 2024 12:19:36 +0300 Subject: [PATCH 13/19] Remove quotes for defaults that are keywords --- src/utils/toSQL.js | 13 ++++++++----- src/utils/utils.js | 6 ++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/utils/toSQL.js b/src/utils/toSQL.js index bd2904f..40577ce 100644 --- a/src/utils/toSQL.js +++ b/src/utils/toSQL.js @@ -1,5 +1,5 @@ import { sqlDataTypes } from "../data/constants"; -import { isFunction, strHasQuotes } from "./utils"; +import { isFunction, isKeyword, strHasQuotes } from "./utils"; export function getJsonType(f) { if (!sqlDataTypes.includes(f.type)) { @@ -144,13 +144,16 @@ export function hasQuotes(type) { } export function parseDefault(field) { - if (strHasQuotes(field.default) || isFunction(field.default)) { + if ( + strHasQuotes(field.default) || + isFunction(field.default) || + isKeyword(field.default) || + !hasQuotes(field.type) + ) { return field.default; } - return hasQuotes(field.type) && field.default.toLowerCase() !== "null" - ? `'${field.default}'` - : `${field.default}`; + return `'${field.default}'`; } export function jsonToMySQL(obj) { diff --git a/src/utils/utils.js b/src/utils/utils.js index cafca7c..a51919f 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -25,6 +25,12 @@ export function strHasQuotes(str) { ); } +const keywords = ["CURRENT_TIMESTAMP", "NULL"]; + +export function isKeyword(str) { + return keywords.includes(str.toUpperCase()); +} + export function isFunction(str) { return /\w+\([^)]*\)$/.test(str); } From aad146949600068dd16a31f0e8689e79078a5536 Mon Sep 17 00:00:00 2001 From: 1ilit Date: Wed, 24 Apr 2024 15:09:38 +0300 Subject: [PATCH 14/19] Handle null as default --- src/utils/astToDiagram.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/astToDiagram.js b/src/utils/astToDiagram.js index 82d2f2f..98120e8 100644 --- a/src/utils/astToDiagram.js +++ b/src/utils/astToDiagram.js @@ -56,6 +56,8 @@ export function astToDiagram(ast) { .join(", ") + ")"; } + } else if (d.default_val.value.type === "null") { + defaultValue = "NULL"; } else { defaultValue = d.default_val.value.value.toString(); } @@ -189,7 +191,6 @@ export function astToDiagram(ast) { expr.action === "add" && expr.create_definitions.constraint_type === "FOREIGN KEY" ) { - console.log(e); const relationship = {}; const startTable = e.table[0].table; const startField = expr.create_definitions.definition[0].column; @@ -240,7 +241,6 @@ export function astToDiagram(ast) { relationship.deleteConstraint = deleteConstraint; relationship.cardinality = Cardinality.ONE_TO_ONE; relationships.push(relationship); - console.log(relationship); relationships.forEach((r, i) => (r.id = i)); } From aaaca8675fa1621c8f6ad9402b060d7e90e495c0 Mon Sep 17 00:00:00 2001 From: 1ilit Date: Wed, 24 Apr 2024 16:47:26 +0300 Subject: [PATCH 15/19] Remove double precision warning --- src/utils/issues.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/utils/issues.js b/src/utils/issues.js index 6a7dbcf..c73d4a7 100644 --- a/src/utils/issues.js +++ b/src/utils/issues.js @@ -128,12 +128,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]) { issues.push(`Duplicate table fields in table "${table.name}"`); } else { @@ -194,12 +188,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]) { issues.push(`Duplicate type fields in "${type.name}"`); } else { From a88bc9799f9bb51847eacf5de1ab550d811a5ecf Mon Sep 17 00:00:00 2001 From: 1ilit Date: Wed, 24 Apr 2024 18:17:41 +0300 Subject: [PATCH 16/19] Parse nested check statements --- src/utils/astToDiagram.js | 66 ++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/src/utils/astToDiagram.js b/src/utils/astToDiagram.js index 98120e8..eb20da1 100644 --- a/src/utils/astToDiagram.js +++ b/src/utils/astToDiagram.js @@ -5,6 +5,42 @@ import { 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 ast.value; + } +} + export function astToDiagram(ast) { const tables = []; const relationships = []; @@ -72,35 +108,7 @@ export function astToDiagram(ast) { } field.check = ""; if (d.check) { - let check = ""; - 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; + field.check = buildSQLFromAST(d.check.definition[0]); } table.fields.push(field); From a2c5b8156f7b5db195fad9e1722baed618fd1f17 Mon Sep 17 00:00:00 2001 From: 1ilit Date: Wed, 24 Apr 2024 18:28:28 +0300 Subject: [PATCH 17/19] Remove nested parentheses for checks --- src/utils/astToDiagram.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/astToDiagram.js b/src/utils/astToDiagram.js index eb20da1..92991ad 100644 --- a/src/utils/astToDiagram.js +++ b/src/utils/astToDiagram.js @@ -9,7 +9,7 @@ function buildSQLFromAST(ast) { if (ast.type === "binary_expr") { const leftSQL = buildSQLFromAST(ast.left); const rightSQL = buildSQLFromAST(ast.right); - return `(${leftSQL}) ${ast.operator} (${rightSQL})`; + return `${leftSQL} ${ast.operator} ${rightSQL}`; } if (ast.type === "function") { @@ -37,7 +37,7 @@ function buildSQLFromAST(ast) { } else if (ast.type === "expr_list") { return ast.value.map((v) => v.value).join(" AND "); } else { - return ast.value; + return typeof ast.value === "string" ? "'" + ast.value + "'" : ast.value; } } From 22331080d888a86d4c2288939ecf5ed2082568d8 Mon Sep 17 00:00:00 2001 From: 1ilit Date: Wed, 24 Apr 2024 18:37:50 +0300 Subject: [PATCH 18/19] Accept null values for nullable fields --- src/utils/issues.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/issues.js b/src/utils/issues.js index c73d4a7..90e1259 100644 --- a/src/utils/issues.js +++ b/src/utils/issues.js @@ -11,6 +11,8 @@ function checkDefault(field) { if (isFunction(field.default)) return true; + if (!field.notNull && field.default.toLowerCase() === "null") return true; + switch (field.type) { case "INT": case "BIGINT": From 3c7c0eafde70176e61817f853f92f6ef7ce9f03c Mon Sep 17 00:00:00 2001 From: 1ilit Date: Wed, 24 Apr 2024 18:45:30 +0300 Subject: [PATCH 19/19] Change not null button title from nullable to not null (#66) --- src/components/EditorSidePanel/TablesTab/TableField.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/EditorSidePanel/TablesTab/TableField.jsx b/src/components/EditorSidePanel/TablesTab/TableField.jsx index 71db783..7ffe2da 100644 --- a/src/components/EditorSidePanel/TablesTab/TableField.jsx +++ b/src/components/EditorSidePanel/TablesTab/TableField.jsx @@ -123,7 +123,7 @@ export default function TableField({ data, tid, index }) {