diff --git a/package-lock.json b/package-lock.json index f3536c74..6aa2215a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "lexical": "^0.12.5", "node-sql-parser": "^5.3.6", "octokit": "^4.0.2", + "oracle-sql-parser": "^0.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hotkeys-hook": "^4.4.1", @@ -5097,6 +5098,12 @@ "node": ">= 0.8.0" } }, + "node_modules/oracle-sql-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/oracle-sql-parser/-/oracle-sql-parser-0.1.0.tgz", + "integrity": "sha512-8MLYOJIKaOY1cWvnMFuYPxWcDH5GfmJMh/f1Tyow0bydC31heO+eSoexZW+NJBSdK87lNJl8nsQ/SY//ZGOwcQ==", + "license": "MIT" + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", diff --git a/package.json b/package.json index 9303e029..00a0c286 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "lexical": "^0.12.5", "node-sql-parser": "^5.3.6", "octokit": "^4.0.2", + "oracle-sql-parser": "^0.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hotkeys-hook": "^4.4.1", diff --git a/src/assets/oraclesql-icon.png b/src/assets/oraclesql-icon.png new file mode 100644 index 00000000..cdeb3a31 Binary files /dev/null and b/src/assets/oraclesql-icon.png differ diff --git a/src/assets/oraclesql.png b/src/assets/oraclesql.png new file mode 100644 index 00000000..cb7686e4 Binary files /dev/null and b/src/assets/oraclesql.png differ diff --git a/src/components/EditorHeader/ControlPanel.jsx b/src/components/EditorHeader/ControlPanel.jsx index 40afb068..b62dd970 100644 --- a/src/components/EditorHeader/ControlPanel.jsx +++ b/src/components/EditorHeader/ControlPanel.jsx @@ -30,6 +30,7 @@ import { jsonToSQLite, jsonToMariaDB, jsonToSQLServer, + jsonToOracleSQL, } from "../../utils/exportSQL/generic"; import { ObjectType, @@ -836,6 +837,12 @@ export default function ControlPanel({ setImportDb(DB.MSSQL); }, }, + { + Oracle: () => { + setModal(MODAL.IMPORT_SRC); + setImportDb(DB.ORACLESQL); + }, + }, ], }), function: () => { @@ -927,6 +934,22 @@ export default function ControlPanel({ })); }, }, + { + OracleSQL: () => { + setModal(MODAL.CODE); + const src = jsonToOracleSQL({ + tables: tables, + references: relationships, + types: types, + database: database, + }); + setExportData((prev) => ({ + ...prev, + data: src, + extension: "sql", + })); + }, + }, ], }), function: () => { diff --git a/src/components/EditorHeader/Modal/Modal.jsx b/src/components/EditorHeader/Modal/Modal.jsx index 270172e6..34cb7127 100644 --- a/src/components/EditorHeader/Modal/Modal.jsx +++ b/src/components/EditorHeader/Modal/Modal.jsx @@ -20,6 +20,7 @@ import { } from "../../../hooks"; import { saveAs } from "file-saver"; import { Parser } from "node-sql-parser"; +import { Parser as OracleParser } from "oracle-sql-parser"; import { getModalTitle, getModalWidth, @@ -131,12 +132,21 @@ export default function Modal({ }; const parseSQLAndLoadDiagram = () => { - const parser = new Parser(); + const targetDatabase = database === DB.GENERIC ? importDb : database; + let ast = null; try { - ast = parser.astify(importSource.src, { - database: database === DB.GENERIC ? importDb : database, - }); + if (targetDatabase === DB.ORACLESQL) { + const oracleParser = new OracleParser(); + + ast = oracleParser.parse(importSource.src); + } else { + const parser = new Parser(); + + ast = parser.astify(importSource.src, { + database: targetDatabase, + }); + } } catch (error) { const message = error.location ? `${error.name} [Ln ${error.location.start.line}, Col ${error.location.start.column}]: ${error.message}` diff --git a/src/data/constants.js b/src/data/constants.js index 0dbe0f84..f734eb2f 100644 --- a/src/data/constants.js +++ b/src/data/constants.js @@ -113,6 +113,7 @@ export const DB = { MSSQL: "transactsql", SQLITE: "sqlite", MARIADB: "mariadb", + ORACLESQL: "oraclesql", GENERIC: "generic", }; diff --git a/src/data/databases.js b/src/data/databases.js index 58ffe764..7169b311 100644 --- a/src/data/databases.js +++ b/src/data/databases.js @@ -3,6 +3,7 @@ import postgresImage from "../assets/postgres-icon.png"; import sqliteImage from "../assets/sqlite-icon.png"; import mariadbImage from "../assets/mariadb-icon.png"; import mssqlImage from "../assets/mssql-icon.png"; +import oraclesqlImage from "../assets/oraclesql-icon.png"; import i18n from "../i18n/i18n"; import { DB } from "./constants"; @@ -42,6 +43,14 @@ export const databases = new Proxy( image: mssqlImage, hasTypes: false, }, + [DB.ORACLESQL]: { + name: "Oracle SQL", + label: DB.ORACLESQL, + image: oraclesqlImage, + hasTypes: false, + hasEnums: false, + hasArrays: false, + }, [DB.GENERIC]: { name: i18n.t("generic"), label: DB.GENERIC, diff --git a/src/data/datatypes.js b/src/data/datatypes.js index 9fda287a..1a6d6b10 100644 --- a/src/data/datatypes.js +++ b/src/data/datatypes.js @@ -55,6 +55,16 @@ const defaultTypesBase = { isSized: false, hasPrecision: true, }, + NUMBER: { + type: "NUMBER", + checkDefault: (field) => { + return /^-?\d+(\.\d+)?$/.test(field.default); + }, + hasCheck: true, + isSized: false, + hasPrecision: true, + canIncrement: false, + }, FLOAT: { type: "FLOAT", checkDefault: (field) => { @@ -110,6 +120,20 @@ const defaultTypesBase = { defaultSize: 255, hasQuotes: true, }, + VARCHAR2: { + type: "VARCHAR2", + checkDefault: (field) => { + if (strHasQuotes(field.default)) { + return field.default.length - 2 <= field.size; + } + return field.default.length <= field.size; + }, + hasCheck: true, + isSized: true, + hasPrecision: false, + defaultSize: 225, + hasQuotes: true, + }, TEXT: { type: "TEXT", checkDefault: (field) => true, @@ -223,6 +247,22 @@ const defaultTypesBase = { hasPrecision: false, noDefault: true, }, + CLOB: { + type: "CLOB", + checkDefault: (field) => true, + isSized: false, + hasCheck: false, + hasPrecision: false, + noDefault: true, + }, + NCLOB: { + type: "NCLOB", + checkDefault: (field) => true, + isSized: false, + hasCheck: false, + hasPrecision: false, + noDefault: true, + }, JSON: { type: "JSON", checkDefault: (field) => true, @@ -1747,6 +1787,155 @@ export const mssqlTypes = new Proxy(mssqlTypesBase, { get: (target, prop) => (prop in target ? target[prop] : false), }); +const oraclesqlTypesBase = { + NUMBER: { + type: "NUMBER", + checkDefault: (field) => { + return /^-?\d+(\.\d+)?$/.test(field.default); + }, + hasCheck: true, + isSized: false, + hasPrecision: true, + canIncrement: false, + }, + VARCHAR2: { + type: "VARCHAR2", + checkDefault: (field) => { + if (strHasQuotes(field.default)) { + return field.default.length - 2 <= field.size; + } + return field.default.length <= field.size; + }, + hasCheck: true, + isSized: true, + hasPrecision: false, + defaultSize: 4000, + hasQuotes: true, + }, + CHAR: { + type: "CHAR", + checkDefault: (field) => { + if (strHasQuotes(field.default)) { + return field.default.length - 2 <= field.size; + } + return field.default.length <= field.size; + }, + hasCheck: true, + isSized: true, + hasPrecision: false, + defaultSize: 1, + hasQuotes: true, + }, + CLOB: { + type: "CLOB", + checkDefault: (field) => true, + isSized: false, + hasCheck: false, + hasPrecision: false, + noDefault: true, + }, + NCLOB: { + type: "NCLOB", + checkDefault: (field) => true, + isSized: false, + hasCheck: false, + hasPrecision: false, + noDefault: true, + }, + BLOB: { + type: "BLOB", + checkDefault: (field) => true, + isSized: false, + hasCheck: false, + hasPrecision: false, + noDefault: true, + }, + DATE: { + type: "DATE", + checkDefault: (field) => { + return /^\d{4}-\d{2}-\d{2}$/.test(field.default); + }, + hasCheck: false, + isSized: false, + hasPrecision: false, + hasQuotes: true, + }, + TIMESTAMP: { + type: "TIMESTAMP", + checkDefault: (field) => { + if (field.default.toUpperCase() === "CURRENT_TIMESTAMP") { + return true; + } + return /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)?$/.test( + field.default, + ); + }, + hasCheck: false, + isSized: false, + hasPrecision: true, + hasQuotes: true, + }, + INTERVAL: { + type: "INTERVAL", + checkDefault: (field) => { + return /^INTERVAL\s'\d+'(\s+DAY|HOUR|MINUTE|SECOND)?$/.test( + field.default, + ); + }, + hasCheck: false, + isSized: false, + hasPrecision: false, + hasQuotes: true, + }, + FLOAT: { + type: "FLOAT", + checkDefault: (field) => { + return /^-?\d+(\.\d+)?$/.test(field.default); + }, + hasCheck: true, + isSized: false, + hasPrecision: true, + }, + DOUBLE: { + type: "DOUBLE", + checkDefault: (field) => { + return /^-?\d+(\.\d+)?$/.test(field.default); + }, + hasCheck: true, + isSized: false, + hasPrecision: true, + }, + BOOLEAN: { + type: "BOOLEAN", + checkDefault: (field) => { + return ( + field.default === "0" || + field.default === "1" || + field.default.toUpperCase() === "TRUE" || + field.default.toUpperCase() === "FALSE" + ); + }, + hasCheck: false, + isSized: false, + hasPrecision: false, + }, + RAW: { + type: "RAW", + checkDefault: (field) => { + return /^[0-9A-Fa-f]+$/.test(field.default); + }, + hasCheck: false, + isSized: true, + hasPrecision: false, + defaultSize: 2000, + hasQuotes: false, + }, +}; + +export const oraclesqlTypes = new Proxy(oraclesqlTypesBase, { + get: (target, prop) => (prop in target ? target[prop] : false), +}); + const dbToTypesBase = { [DB.GENERIC]: defaultTypes, [DB.MYSQL]: mysqlTypes, @@ -1754,6 +1943,7 @@ const dbToTypesBase = { [DB.SQLITE]: sqliteTypes, [DB.MSSQL]: mssqlTypes, [DB.MARIADB]: mysqlTypes, + [DB.ORACLESQL]: oraclesqlTypes, }; export const dbToTypes = new Proxy(dbToTypesBase, { diff --git a/src/pages/LandingPage.jsx b/src/pages/LandingPage.jsx index 0398cb88..f3d1a82a 100644 --- a/src/pages/LandingPage.jsx +++ b/src/pages/LandingPage.jsx @@ -8,6 +8,7 @@ import mysql_icon from "../assets/mysql.png"; import postgres_icon from "../assets/postgres.png"; import sqlite_icon from "../assets/sqlite.png"; import mariadb_icon from "../assets/mariadb.png"; +import oraclesql_icon from "../assets/oraclesql.png"; import sql_server_icon from "../assets/sql-server.png"; import discord from "../assets/discord.png"; import github from "../assets/github.png"; @@ -150,7 +151,7 @@ export default function LandingPage() {
Design for your database
-
+
{dbs.map((s, i) => ( `'${v}'`) + .join(", ")}))`; + } + + return oracleType; } } @@ -375,7 +445,7 @@ export function jsonToMariaDB(obj) { (field) => `\t\`${ field.name - }\` ${getTypeString(field, obj.database)}${field.notNull ? " NOT NULL" : ""}${ + }\` ${getTypeString(field, obj.database, "oraclesql")}${field.notNull ? " NOT NULL" : ""}${ field.increment ? " AUTO_INCREMENT" : "" }${field.unique ? " UNIQUE" : ""}${ field.default !== "" @@ -491,3 +561,55 @@ export function jsonToSQLServer(obj) { ) .join("\n")}`; } + +export function jsonToOracleSQL(obj) { + return `${obj.tables + .map( + (table) => + `${ + table.comment === "" ? "" : `/* ${table.comment} */\n` + }CREATE TABLE "${table.name}" (\n${table.fields + .map( + (field) => + `${field.comment === "" ? "" : ` -- ${field.comment}\n`} "${ + field.name + }" ${getTypeString(field, obj.database, "oraclesql")}${field.primary ? "" : field.notNull ? (table.indices.some((index) => index.fields.some((f) => f === field.name)) ? " NOT NULL" : field.unique ? " NOT NULL UNIQUE" : " UNIQUE") : ""}${ + field.default !== "" + ? ` DEFAULT ${parseDefault(field, obj.database)}` + : "" + }${ + field.check === "" || + !dbToTypes[obj.database][field.type].hasCheck + ? "" + : ` CHECK (${field.check})` + }`, + ) + .join(",\n")}${ + table.fields.filter((f) => f.primary).length > 0 + ? `,\n PRIMARY KEY (${table.fields + .filter((f) => f.primary) + .map((f) => `"${f.name}"`) + .join(", ")})` + : "" + }\n);\n${table.indices + .map( + (i) => + `\nCREATE ${i.unique ? "UNIQUE " : ""}INDEX "${i.name}"\n ON "${ + table.name + }" (${i.fields.map((f) => `"${f}"`).join(", ")});`, + ) + .join("\n")}`, + ) + .join("\n\n")}\n${obj.references + .map( + (r) => + `ALTER TABLE "${obj.tables[r.startTableId].name}"\nADD CONSTRAINT fk_${ + r.startTableId + }_${r.endTableId} FOREIGN KEY ("${ + obj.tables[r.startTableId].fields[r.startFieldId].name + }") REFERENCES "${obj.tables[r.endTableId].name}"("${ + obj.tables[r.endTableId].fields[r.endFieldId].name + }");`, + ) + .join("\n")}`; +} diff --git a/src/utils/exportSQL/index.js b/src/utils/exportSQL/index.js index ad877daf..002e7745 100644 --- a/src/utils/exportSQL/index.js +++ b/src/utils/exportSQL/index.js @@ -2,6 +2,7 @@ import { DB } from "../../data/constants"; import { toMariaDB } from "./mariadb"; import { toMSSQL } from "./mssql"; import { toMySQL } from "./mysql"; +import { toOracleSQL } from "./oraclesql"; import { toPostgres } from "./postgres"; import { toSqlite } from "./sqlite"; @@ -17,6 +18,8 @@ export function exportSQL(diagram) { return toMariaDB(diagram); case DB.MSSQL: return toMSSQL(diagram); + case DB.ORACLE: + return toOracleSQL(diagram); default: return ""; } diff --git a/src/utils/exportSQL/oraclesql.js b/src/utils/exportSQL/oraclesql.js new file mode 100644 index 00000000..308caaf3 --- /dev/null +++ b/src/utils/exportSQL/oraclesql.js @@ -0,0 +1,56 @@ +import { dbToTypes } from "../../data/datatypes"; +import { parseDefault } from "./shared"; + +export function toOracleSQL(diagram) { + return `${diagram.tables + .map( + (table) => + `${ + table.comment === "" ? "" : `/* ${table.comment} */\n` + }CREATE TABLE "${table.name}" (\n${table.fields + .map( + (field) => + `${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t"${ + field.name + }" ${field.type}${field.size !== undefined && field.size !== "" ? "(" + field.size + ")" : ""}${ + field.notNull ? " NOT NULL" : "" + }${ + field.increment ? " GENERATED ALWAYS AS IDENTITY" : "" + }${field.unique ? " UNIQUE" : ""}${ + field.default !== "" + ? ` DEFAULT ${parseDefault(field, diagram.database)}` + : "" + }${ + field.check === "" || + !dbToTypes[diagram.database][field.type].hasCheck + ? "" + : ` CHECK(${field.check})` + }${field.comment ? ` -- ${field.comment}` : ""}`, + ) + .join(",\n")}${ + table.fields.filter((f) => f.primary).length > 0 + ? `,\n\tPRIMARY KEY(${table.fields + .filter((f) => f.primary) + .map((f) => `"${f.name}"`) + .join(", ")})` + : "" + }\n)${table.comment ? ` -- ${table.comment}` : ""};\n${`\n${table.indices + .map( + (i) => + `\nCREATE ${i.unique ? "UNIQUE " : ""}INDEX "${i.name}"\nON "${table.name}" (${i.fields + .map((f) => `"${f}"`) + .join(", ")});`, + ) + .join("")}`}`, + ) + .join("\n")}\n${diagram.references + .map( + (r) => + `ALTER TABLE "${diagram.tables[r.startTableId].name}"\nADD CONSTRAINT "${r.name}" FOREIGN KEY ("${ + diagram.tables[r.startTableId].fields[r.startFieldId].name + }") REFERENCES "${diagram.tables[r.endTableId].name}" ("${ + diagram.tables[r.endTableId].fields[r.endFieldId].name + }")\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};`, + ) + .join("\n")}`; +} diff --git a/src/utils/importSQL/index.js b/src/utils/importSQL/index.js index fb849e43..e92ea724 100644 --- a/src/utils/importSQL/index.js +++ b/src/utils/importSQL/index.js @@ -3,6 +3,7 @@ import { arrangeTables } from "../arrangeTables"; import { fromMariaDB } from "./mariadb"; import { fromMSSQL } from "./mssql"; import { fromMySQL } from "./mysql"; +import { fromOracleSQL } from "./oraclesql"; import { fromPostgres } from "./postgres"; import { fromSQLite } from "./sqlite"; @@ -24,6 +25,9 @@ export function importSQL(ast, toDb = DB.MYSQL, diagramDb = DB.GENERIC) { case DB.MSSQL: diagram = fromMSSQL(ast, diagramDb); break; + case DB.ORACLESQL: + diagram = fromOracleSQL(ast, diagramDb); + break; default: diagram = { tables: [], relationships: [] }; break; diff --git a/src/utils/importSQL/oraclesql.js b/src/utils/importSQL/oraclesql.js new file mode 100644 index 00000000..dd8fa9a9 --- /dev/null +++ b/src/utils/importSQL/oraclesql.js @@ -0,0 +1,218 @@ +export function fromOracleSQL(ast) { + const tables = []; + const relationships = []; + const enums = []; + + const parseSingleStatement = (e) => { + console.log(e); + if (e.operation === "create") { + if (e.object === "table") { + const table = {}; + table.name = e.name.name; + table.comment = ""; + table.color = "#175e7a"; + table.fields = []; + table.indices = []; + table.id = tables.length; + e.table.relational_properties.forEach((d) => { + if (d.resource === "column") { + const field = {}; + field.name = d.name; + + let type = d.type.type.toUpperCase(); + field.type = type; + field.comment = ""; + field.unique = false; + field.increment = false; + field.notNull = false; + field.primary = false; + + field.default = ""; + // if (d.default_val) { + // let defaultValue = ""; + // if (d.default_val.value.type === "function") { + // defaultValue = d.default_val.value.name.name[0].value; + // 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 = ""; + // if (d.check) { + // field.check = buildSQLFromAST(d.check.definition[0], DB.MYSQL); + // } + + table.fields.push(field); + } + // else if (d.resource === "constraint") { + // if (d.constraint_type === "primary key") { + // d.definition.forEach((c) => { + // table.fields.forEach((f) => { + // if (f.name === c.column && !f.primary) { + // f.primary = true; + // } + // }); + // }); + // } else if (d.constraint_type.toLowerCase() === "foreign key") { + // 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 (endFieldId === -1) return; + + // const startFieldId = table.fields.findIndex( + // (f) => f.name === startField, + // ); + // if (startFieldId === -1) return; + + // relationship.name = + // "fk_" + startTable + "_" + startField + "_" + endTable; + // 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; + + // if (table.fields[startFieldId].unique) { + // relationship.cardinality = Cardinality.ONE_TO_ONE; + // } else { + // relationship.cardinality = Cardinality.MANY_TO_ONE; + // } + + // relationships.push(relationship); + // } + // } + }); + table.fields.forEach((f, j) => { + f.id = j; + }); + tables.push(table); + } + } + // else if (e.type === "alter") { + // e.expr.forEach((expr) => { + // if ( + // expr.action === "add" && + // expr.create_definitions.constraint_type.toLowerCase() === + // "foreign key" + // ) { + // 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); + // } + // }, + // ); + + // const startTableId = tables.findIndex((t) => t.name === startTable); + // if (startTable === -1) return; + + // const endTableId = tables.findIndex((t) => t.name === endTable); + // if (endTableId === -1) return; + + // const endFieldId = tables[endTableId].fields.findIndex( + // (f) => f.name === endField, + // ); + // if (endFieldId === -1) return; + + // const startFieldId = tables[startTableId].fields.findIndex( + // (f) => f.name === startField, + // ); + // if (startFieldId === -1) return; + + // relationship.name = + // "fk_" + startTable + "_" + startField + "_" + endTable; + // relationship.startTableId = startTableId; + // relationship.startFieldId = startFieldId; + // relationship.endTableId = endTableId; + // relationship.endFieldId = endFieldId; + // relationship.updateConstraint = updateConstraint; + // relationship.deleteConstraint = deleteConstraint; + + // if (tables[startTableId].fields[startFieldId].unique) { + // relationship.cardinality = Cardinality.ONE_TO_ONE; + // } else { + // relationship.cardinality = Cardinality.MANY_TO_ONE; + // } + + // relationships.push(relationship); + + // relationships.forEach((r, i) => (r.id = i)); + // } + // }); + // } + }; + + ast.forEach((e) => parseSingleStatement(e)); + + relationships.forEach((r, i) => (r.id = i)); + + return { tables, relationships, enums }; +}