From aa89ee84080cb9239f1e011bc20a89fd8e0fabaa Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 30 Jul 2024 17:13:02 +0530 Subject: [PATCH 01/10] feat:Added support for exporting SQL as OracleDB SQL --- src/assets/oraclesql-icon.png | Bin 0 -> 16256 bytes src/assets/oraclesql.png | Bin 0 -> 28695 bytes src/components/EditorHeader/ControlPanel.jsx | 8 +- src/data/constants.js | 2 +- src/data/databases.js | 14 +- src/data/datatypes.js | 191 ++++++++++++++++++- src/pages/LandingPage.jsx | 38 +++- src/utils/exportSQL/generic.js | 124 +++++++++++- src/utils/exportSQL/index.js | 3 +- src/utils/exportSQL/oraclesql.js | 56 ++++++ 10 files changed, 412 insertions(+), 24 deletions(-) create mode 100644 src/assets/oraclesql-icon.png create mode 100644 src/assets/oraclesql.png create mode 100644 src/utils/exportSQL/oraclesql.js diff --git a/src/assets/oraclesql-icon.png b/src/assets/oraclesql-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..cdeb3a31a5a11b0dbe3b11629df1a06c45d691f5 GIT binary patch literal 16256 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 literal 0 HcmV?d00001 diff --git a/src/assets/oraclesql.png b/src/assets/oraclesql.png new file mode 100644 index 0000000000000000000000000000000000000000..cb7686e4358d294ae524d04fc7f12e8ad834404a GIT binary patch literal 28695 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 Zyu|y9?FLsEJ { + OracleSQL: () => { setModal(MODAL.CODE); - const src = jsonToOracle({ + const src = jsonToOracleSQL({ tables: tables, references: relationships, types: types, @@ -1164,7 +1164,7 @@ export default function ControlPanel({ data: src, extension: "sql", })); - } + }, }, ], }), diff --git a/src/data/constants.js b/src/data/constants.js index 88d4aea3b..97cf0d76c 100644 --- a/src/data/constants.js +++ b/src/data/constants.js @@ -142,7 +142,7 @@ export const DB = { MSSQL: "transactsql", SQLITE: "sqlite", MARIADB: "mariadb", - ORACLE: "oracledb", + ORACLESQL: "oraclesql", GENERIC: "generic", }; diff --git a/src/data/databases.js b/src/data/databases.js index 354183fb5..7169b3110 100644 --- a/src/data/databases.js +++ b/src/data/databases.js @@ -3,7 +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 oracleImage from "../assets/oracle-icon.png"; +import oraclesqlImage from "../assets/oraclesql-icon.png"; import i18n from "../i18n/i18n"; import { DB } from "./constants"; @@ -43,11 +43,13 @@ export const databases = new Proxy( image: mssqlImage, hasTypes: false, }, - [DB.ORACLE]: { - name: "Oracle", - label: DB.ORACLE, - image: oracleImage, - hasTypes: true, + [DB.ORACLESQL]: { + name: "Oracle SQL", + label: DB.ORACLESQL, + image: oraclesqlImage, + hasTypes: false, + hasEnums: false, + hasArrays: false, }, [DB.GENERIC]: { name: i18n.t("generic"), diff --git a/src/data/datatypes.js b/src/data/datatypes.js index 37eb83c5f..3186d4db1 100644 --- a/src/data/datatypes.js +++ b/src/data/datatypes.js @@ -65,6 +65,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) => { @@ -120,6 +130,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, @@ -233,6 +257,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, @@ -1894,6 +1934,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, @@ -1901,7 +2090,7 @@ const dbToTypesBase = { [DB.SQLITE]: sqliteTypes, [DB.MSSQL]: mssqlTypes, [DB.MARIADB]: mysqlTypes, - [DB.ORACLE]: oracleTypes, + [DB.ORACLESQL]: oraclesqlTypes, }; export const dbToTypes = new Proxy(dbToTypesBase, { diff --git a/src/pages/LandingPage.jsx b/src/pages/LandingPage.jsx index 0398cb889..50015bfee 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"; @@ -202,19 +203,36 @@ export default function LandingPage() { - {/* Tweets */} + {/* Supported Databases */}
- What the internet says about us + Supported Databases
-
- - - - +
+ + + + + +
diff --git a/src/utils/exportSQL/generic.js b/src/utils/exportSQL/generic.js index 84e0f9f47..751102dca 100644 --- a/src/utils/exportSQL/generic.js +++ b/src/utils/exportSQL/generic.js @@ -134,6 +134,76 @@ export function getTypeString( } return type; + } else if (dbms === "oraclesql") { + let oracleType; + switch (field.type) { + case "INT": + case "INTEGER": + case "SMALLINT": + case "BIGINT": + case "DECIMAL": + case "NUMERIC": + case "REAL": + case "FLOAT": + case "DOUBLE": + oracleType = "NUMBER"; + break; + case "CHAR": + oracleType = "CHAR"; + break; + case "VARCHAR": + oracleType = "VARCHAR2"; + break; + case "TEXT": + oracleType = "CLOB"; + break; + case "TIME": + oracleType = "TIMESTAMP"; + break; + case "TIMESTAMP": + case "DATE": + case "DATETIME": + oracleType = field.type; + break; + case "BOOLEAN": + oracleType = "NUMBER(1)"; + break; + case "BINARY": + case "VARBINARY": + oracleType = "RAW"; + break; + case "BLOB": + oracleType = "BLOB"; + break; + case "JSON": + oracleType = "JSON"; + break; + case "UUID": + oracleType = "RAW(16)"; + break; + case "ENUM": + case "SET": + oracleType = "VARCHAR2"; + break; + default: + throw new Error(`Unsupported type for Oracle: ${field.type}`); + } + const typeInfo = dbToTypes[currentDb][oracleType]; + if (typeInfo.isSized || typeInfo.hasPrecision) { + if (oracleType === "NUMBER") { + return `${oracleType}${field.size ? `(${field.size})` : "(38,0)"}`; + } else { + return `${oracleType}${field.size ? `(${field.size})` : ""}`; + } + } + + if (field.type === "ENUM" || field.type === "SET") { + oracleType += ` CHECK (${field.name} IN (${field.values + .map((v) => `'${v}'`) + .join(", ")}))`; + } + + return oracleType; } } @@ -412,7 +482,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 !== "" @@ -528,3 +598,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 2eabbdb9a..f3de18e0c 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"; import { toOracle } from "./oracle"; @@ -19,7 +20,7 @@ export function exportSQL(diagram) { case DB.MSSQL: return toMSSQL(diagram); case DB.ORACLE: - return toOracle(diagram); + return toOracleSQL(diagram); default: return ""; } diff --git a/src/utils/exportSQL/oraclesql.js b/src/utils/exportSQL/oraclesql.js new file mode 100644 index 000000000..308caaf3d --- /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")}`; +} From a316032fe48f1a940fd3f1335869537322389ab8 Mon Sep 17 00:00:00 2001 From: 1ilit <1ilit@proton.me> Date: Sat, 15 Mar 2025 19:11:07 +0400 Subject: [PATCH 02/10] Add parser --- package-lock.json | 7 + package.json | 1 + src/components/EditorHeader/ControlPanel.jsx | 2 +- src/components/EditorHeader/Modal/Modal.jsx | 18 +- src/utils/importSQL/index.js | 4 + src/utils/importSQL/oraclesql.js | 218 +++++++++++++++++++ 6 files changed, 245 insertions(+), 5 deletions(-) create mode 100644 src/utils/importSQL/oraclesql.js diff --git a/package-lock.json b/package-lock.json index 37e0f4f8d..9314a27ab 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", @@ -8424,6 +8425,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 a874a5343..cf3f73022 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,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/components/EditorHeader/ControlPanel.jsx b/src/components/EditorHeader/ControlPanel.jsx index ed36a4320..e24ad5f13 100644 --- a/src/components/EditorHeader/ControlPanel.jsx +++ b/src/components/EditorHeader/ControlPanel.jsx @@ -1056,7 +1056,7 @@ export default function ControlPanel({ { Oracle: () => { setModal(MODAL.IMPORT_SRC); - setImportDb(DB.ORACLE); + setImportDb(DB.ORACLESQL); }, }, ], diff --git a/src/components/EditorHeader/Modal/Modal.jsx b/src/components/EditorHeader/Modal/Modal.jsx index 0eb7b5dcf..8ac007751 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, @@ -135,12 +136,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/utils/importSQL/index.js b/src/utils/importSQL/index.js index fb849e430..e92ea7240 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 000000000..dd8fa9a98 --- /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 }; +} From af1a07eb91ba0cd99458414434e68095820974f6 Mon Sep 17 00:00:00 2001 From: 1ilit <1ilit@proton.me> Date: Tue, 1 Apr 2025 22:01:23 +0400 Subject: [PATCH 03/10] Import from sql --- src/components/Workspace.jsx | 6 +- src/utils/importSQL/oraclesql.js | 244 +++++++++---------------------- 2 files changed, 74 insertions(+), 176 deletions(-) diff --git a/src/components/Workspace.jsx b/src/components/Workspace.jsx index 9d59bb2a9..c96d9e023 100644 --- a/src/components/Workspace.jsx +++ b/src/components/Workspace.jsx @@ -466,12 +466,12 @@ export default function WorkSpace() { }} okButtonProps={{ disabled: selectedDb === "" }} > -
+
{Object.values(databases).map((x) => (
setSelectedDb(x.label)} - className={`space-y-3 py-3 px-4 rounded-md border-2 select-none ${ + className={`space-y-3 p-3 rounded-md border-2 select-none ${ settings.mode === "dark" ? "bg-zinc-700 hover:bg-zinc-600" : "bg-zinc-100 hover:bg-zinc-200" @@ -481,7 +481,7 @@ export default function WorkSpace() { {x.image && ( { - // 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); - // } + for (const c of d.constraints) { + if (c.constraint.primary_key === "primary key") + field.primary = true; + if (c.constraint.not_null === "not null") field.notNull = true; + if (c.constraint.unique === "unique") field.unique = true; + } + + if (d.identity) { + field.increment = true; + } + + // TODO: reconstruct default when implemented in parser + if (d.default) { + field.default = JSON.stringify(d.default.expr); + } 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); - // } - // } + } else if (d.resource === "constraint") { + const relationship = {}; + const startTableId = table.id; + const startField = d.constraint.columns[0]; + const endField = d.constraint.reference.columns[0]; + const endTable = d.constraint.reference.object.name; + + 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.startTableId = startTableId; + relationship.startFieldId = startFieldId; + relationship.endTableId = endTableId; + relationship.endFieldId = endFieldId; + relationship.updateConstraint = Constraint.NONE; + relationship.name = + d.name && Boolean(d.name.trim()) + ? d.name + : "fk_" + table.name + "_" + startField + "_" + endTable; + relationship.deleteConstraint = + d.constraint.reference.on_delete && + Boolean(d.constraint.reference.on_delete.trim()) + ? d.constraint.reference.on_delete[0].toUpperCase() + + d.constraint.reference.on_delete.substring(1) + : Constraint.NONE; + + 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)); From a3d35c9721a83ec78677c92c5f2a7eb8af2543cc Mon Sep 17 00:00:00 2001 From: 1ilit <1ilit@proton.me> Date: Tue, 1 Apr 2025 23:16:03 +0400 Subject: [PATCH 04/10] Add beta tag --- src/assets/oraclesql-icon.png | Bin 16256 -> 15121 bytes src/components/EditorHeader/ControlPanel.jsx | 94 +++++++++++++------ src/components/Workspace.jsx | 15 ++- src/data/databases.js | 1 + 4 files changed, 77 insertions(+), 33 deletions(-) diff --git a/src/assets/oraclesql-icon.png b/src/assets/oraclesql-icon.png index cdeb3a31a5a11b0dbe3b11629df1a06c45d691f5..ef0f66bb7fce222cf7d47addf584f2335371db7a 100644 GIT binary patch literal 15121 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 BgA)J% literal 16256 zcmdVBcQl;Q|L^-CqL(CE2qK9PEeIo`n@AxN!3;+4y+m(=s0k5Ll<0LZqceJ+2oasp zOC)+538Ty~&gA>McisEP{o}0PU1zOxtc7LU_On0xv7gUtzd!HitY#A>O`Gvw^^I9~3_}uF<4|P}8*)e(H)21>pcvO@X(yG#((F}nMG{GVBqNOZa8$G7frnsb1 zl@WOZDcJnjI{A07%KlC!k!b(7DYB#c-~c2d`WZiIF~iGTI@2Tl+R2r@lxdek zne>73v$qMD4W-kc2|IBjEp*prMbE!Dc}k}bte&-vhpelA&Bn2ig3rg7PTaD;FCOgU zA80BlbJY)mC$e3d@=ntn{MT89I_d65sjh!C?O&fNe{@aKK%BHI{d-IJJmxc=ZpyYNW-f&m~3LGOMws1uba{h(DB^&&*rZX?E8&mV2<zZsmdq8>}K9j=d3is<&(28~TREp)=kacU!d8kaZ=7f9KAKQdb!az1| zpL~@*gMy;A&;ZvzLovr+9_D_GDpgsU*M_xoI9=8Q|XzU#R2PyY@!usuio@F-*G zI*fVq;o|i#5n!moZS?&mvopT(6v49c1EIx$xK9Z=@jn&nL{${|f9rj(*F8RJ-l%C~ zHaI_V;|B51xmpEX*j_^-2epiHD~>cOgz}IObIEay0O4LWfvt+K*6t%!EUq84mLon@ zIHnFU+P$f9Cn+T`&3tsvbx3;*FNe2TBd1g032{Gh?rvUcXWz^_IC|R4v+LejT&jFx zhfBn3t+qcR;T6tv#YrK0?S?l%tP2E9zJVV6ZO`Z1u5XPc2_kiOY|edO|BQ)GfAsib z70}l%R27%OcLs~g8}d76jSCQWpfkLMHs~cV3mqDjEuQY}St!?vi&n6Jk&g(=sF>8G z9>I$*##&PW(91EAt_X8s3@|v57c) znyyF`NbTI8z4I-yt6M*tvneLETT+mzQ;x|z)Y(ux^vbI^!&eNErzd!}Sv(hEm(53NOnxnz%f7jIY3{5%iuMDpS^JA%4 z!u0dNAWxsd{iE*q%@WzRPwz@fFE)3ovT$>bvPnx1>su~74p~T-=!l(h8euW-+5UN0 zW3Itu5dlitmi~o2Y#b;4VqQ@gO`VVPXLijA{S;CdU^KSCirr)ERJ z3$N$%)F%w8q}XSsPz|~*DK>Pg{7cwxOJd5D+B!r!=up>b7f3JO^iOjcl!9hb8R=F#M(rMmixYy3=Msa z1O$w)G{&N9a=%~u7lSQIW6d?f z>`hHPYfjw@-rnmxH~3Po^A4*&kcDr(k-pFCfE(82cmpD5>YLELruXUtGLWiz(`O71 z-O#$mZ8Mi zth*IasZJbRi4p3_s7*A2=U7M~p-@AkW_lZ;(LtKSdTEmLudM+&+`pJnZIyjf^EKWz zh2e$VBrzdgxjpw~@V0=bVAVcR)z7F@|*UD`mv9|1#!%B*{Oc5QC6jV_2l)Uk)GnN{^0deFZ8oguVIq`9fyrkR${yfWd2(8aH){ipCzxhm9_wJ++$SxjP9OP zduR%Ut`ENE^6<57TNa*cGSm*c7hlN_#Py( zJh185VMcu<>2kGMe*~3oR_MRpchu`p_NK|VSC3XlEY9;|x8V@Tn$kRnb(-7k33Rj< z%<^A5;NPChIGWnKNjJSiFv*35ySI5N?F40w;T~Z1yW2!}q>L}st2v^&Z+R7x#|@N9 ze<=gud(5l2RV^G6wW)z|4$L~%(^7^)(Cdw!Oz{B<=$#O7DchDetTX+e_9n@AsOYgwSUi~5A zr{@*g6x)aFv(w-n;%W?s%A=Ndv&tZ;Hzqlt+fSpr&rbO6jfW1bvURw*^7+)iYiLs1 z2=bTYG>?bBIX?Wv{RoBB1jijQh3*s-Y4cev#PMI;^DpxmYyHO5{^My^QoB^oZltBP zMg4jr-k@HJ*LD#T@%;paPZcG8*RPU|d&5xaXUcq+a8$aX1}t6#`GxSj%J>wSr6Q4i zle9_0p++%_hC2{_mUnns8CmSSc>@@FZ@Ihf@p|^-LdKTXth~L}K5#Y((cQHxzo|Zd zvY#n*0Pxh(8-;%C@ir5(p4RmMrJHK#KlC&h{>G5GKV;tOeB78hH*uv=btfbTXV4#| zhMWzs8am3qWM)Y9^j|Ji&|7m~rPFP4y`W{i`JLh-4Nook*^st#;{(P_U`t_aO4OEmCnx$N?Bezi6|VBrUvQ-q zgI%>ru!dXi{dM*#(nJYjQ+gU2M^1Q|YrH1I1a(zE=3qgG4>N5^rJ3v6^=zpOp}*MM zH>a{aswhwattBqLtYt{#)O?O7<20z1zvAY!LDQJ&m(y;&ex;E?njO83wPuM6b~V$; zy4CO;4c(xaQlfJk!jZ+;+Mq!rB+Lh^l0Lw9gHv9f|Hig|jcNa9_0d_C(<7(AL=v7W z`tWT1>DcL!ZC~u@;+1(Beis=bQtLQz^^Cd$lPoSr2fO8^)7x~O>2DzZqNO6_xFJ>W zyzQp{oAsP{O5Z`nZQqcBn}`T)`6Sl;{cqmm>V3)Mr7JP>8_xeFz$qEY%t_JT`6?|jaC-3!_64$gtB9mAk( zhs!nc8hwjdD`35{=6lX`tE9P=u~#_l<%SC1UoW2 z?gYK!^@wij&iojOTg|dA;sdgXUx|s~?q5ZS73t5P$ zelnO7ojZRJdsj%`x(?-3-evs#U>Z8=kz|fIU*-g%;iuDbly$8f_4sXg?#j98mqRS0N!fq{9_<4&ij!_`g|&~TpJ1`eDl9CQG8gPsZd?vB zIe=$#ua2WnzB;T;oaW`>m_oIag6rZ>&xjtM_C~_B*N7$V5=-C{oTrP}6|Y_9ezWYt zOMEfy(=+EHHeMie?B=bKi{suO#l85`K?CNM+ltYGtqa-%=Cs|x%&X#UYml!;#-Br6 zE?=94^!*O(*lIZ7R01n3-o3~7>6@3Ig=_b#WBb{|AR&H<1=e4I;K4H!*ZwQP1VnEA zJ_yT}r^3Ke)vSi*lFsN`y`pUujj3_Z_!VO`bVh6$V)dN#&OX0`!So6S^>Pnj2?EW! zn8WnYu%mGHsFR1g7uoTNN4?@~kE>1|e(D{#Bhh4tMg87cTpJ6=kc9@(3E1_bLG_98 zF3{=*pS%)L)Tj$>ELD&Hs<;eyN`E2qR-P|2et5j=(3*mCa!C2$9CPx%+?%iZTTI!i zbLgJXPi54Hqd%J!R_KT4vXPqf^x#^SkKWoH~ee9;2;x_RQ)Iuqp_|{bDRYG+Oke=CR$j$`PLxn?ws{YP{ z-bKQt{lp{Jr1j;0`=U`kYRk&amuKJykzrOZ^!3?-`2nJ-H{@Uu*UFz;&9EW0a5J8w z*O%diNZUvGGV$rfmF{E`Q78uBq779TedVELwtYfUPt1GR9YfZLz*wS(6aZLj{I^x2 z|DPh$|NNhyrc*JwKW-xgJ?7;l5qsO~olee`PA*zRkV)$r=Q|@M z#w`j%mSKqq_tGrqP9j;nq9rx+^|O$0Kt8zF%AsqRk*bDdAMTmj6&5n{*IxZ+xck8Ef^rI#bj0zG8K9GE)&uPIz43#ruMyI^U@yNj8|1!dO~q- z!GP}SOA&B5eVdqB*T^z9cHOW<^EIaR;%nkVvdki&z-0MG_Y372ov3+qrchWSZ}vVc zNC8jfSxh8`x+NbR9Mrm{mqMFC+Jx+s6waf)VbjIHz`!Oq?ZKg;`9FVF2auq%P(qfF zkV%E02fuzZ%LSZ2YcA|B z)n4YFSCVrI*?&(kgC=Kxjxjc9FGm+t@P+U@e)6;RsVZ^nc>>AfDZ7;5PEp!>%hCjfh|Q^tePo@^Q#Y04u)SS zkNFwQAZ?bkEH1Q!5t?0b@Q(o2YR4$-<_atHd;;^C`ca2vDS-*&cO5)bq4raE<+ge+ z->wYpM4Ya9fw-b>;wFk|pK?8#Nj8SUd-f?>dYuqI3yS8lw@fESm#^X zLh9+~nJJI3G&{vzzRv_^pEfuN9}@^^7Sxh=I2sHL3>srTKW4A9rkl1M^gzo)qr)kF zRtVUl-^C%!;O)xm^0T=y+c(z25$=m^WtgOHEuiuX(irWBTUdFZKBVjtGkr>K$U|cP{HZEec97wY=NK8cN8)0*VK871i_F^0iZHb%R2o0=X%OZLWfqaa~4?IMnjZ zl5a@U-RJAw_S6Ei)#7vp-0_ci?DeuU?z^N!x|k?DM|-Av6q0*Z8~>^_O#%g*z&f&u zxcDX-RF*XJM;tJY+>f5i*b{X}%ajGFw|@Pv4g@9Z6@$Q%2t}4pHzoSOSunJRMx)ckuUN=R-0IoprTWurm!AnDv^?* z|F8RBL|aI)nnG__Vc@@+ILhKrXrSoU3$ErAL5;YVqamsNt)t}CI!GgssE!`CWvKV= z`lK_9#*o90Up?iB4=PvPJw41S=V$fU&M=l=8=N`KpPV$1%N`1_cmP;vv%EJigAx_} ze~!q@`3?H~)lUue`2(i>QAa^MK5hv4NJYT_NcLT@jgZbOyqB$!xnBJdn}n%wCB0_O z5;5ch(*RS;$H)GrBUzbf0bqDG^ls;I~@QVWJno7e;)Ex2mWFP044kW z`(S9ot-Lg9pa3ECx7L4q{(RuiMK&p+X2V=b7+|*_7$UeMO@(F5@z&BAN!c{p@Ovn6NARA5cV~195tKn())i)RcYC z2>?uW7b_TbI2bi51c;00q|B@JY4uFe5)z$8kx8L3Tx3dw2d;?67mM(*toSO zd2&b3hmjNHPQwc6UXYcv8zxCieYCb3bo&+QZ0)~4X@=qVDm#`(mNdKTAy-qx0&xyTPBMpNp* zQ3OLgx}*z>G{8^yMUOH$;jCIMx&2fQpxFjraSu&;T*Qh4wMA`u@M6L!Zn0e` zRP#L{#8!0!>+GN-uytxuw(zj*hL&ukOYKJOz^uq{z%Ld+rUr?Pj?{DiSe2Z^R5+G9;t=7uSu^gp!bi_s;{IG#>{+iHA^riak@NmZ%NR?7 zqN1Wc8Tq!dwe7+ABbv^e>En-mjNa(CaW%HIY%i0cls9zU8|~}mRg-0i`xuhNB&xSU zzPNH4fsg0nZW9X|zIAPFEt9khkJ)bTk*c{ibSN=l({|WA?rUD+dt+KdYxb^$#Kgdf z(4q6wCUovRkvb=MbIqF=mquuoAT&J?e~4YZ1nZUQ4sr7oa6Yix(ykY0XKx{}BHD(W zdFD&Y5Hte{{7tY{`&Vp;{vx6DT)>9P(g#JX)t^V=rM~77hr5DCT>Pii{b+ft zsh()faw6~W97w7F0RQcOUH~0D8mHqEa({}TO(+b#VN*XGqp^>6B1q19c=EsA3fG)m zN9;&?2N(A#_Ty~U(8=wK@-~Zfp(&Lf*Ic>m zYvK4qf)S*&pecmvnh-Ikh&b{^|YvULJUy9|F8kNQL{CWaIvSbaM+9XKzzbA9t|ku>A@ zEJ6E%^TMXin#NIKl%_AgVo+vELZWzv_mNLS{FKLR$7UG(-Q+|%v`Xw~P=vMn@wT#|W?*ESQ=mtu8|1!A`+Z$fevk?t)eZSzABJiw=Ngyt>4$?Mg z(2nQzMEELDf9qKt-BL)EvX6zcR!^n-JV>YXpDlk7DM_#sXmXX4zAmWFVPt5J-q}={ zd)wo7lDfG{`9#QM#5G%qL1#DN5kIZb!Hw-b5TxriBFKv?Kh0&Wfub(ySP<6E54!r;Xy^)F3 z!1XKY96;DJS|b3E7OsR!u$7go{ii4f&lPHGivVp_oo%@Cgq9nH)nDD%E2D{^7U9s zV01)y1CRGf$8lyFyDrmDr+`9FR#&4ftpQbOZb7VrBSV>|DD)C44l*c8#;^OIS1n~V zVc}M8ON|W;*U2q3@=8kLB3iLcFAnFM{kGnc5o%_%_V>#XIk(%9K|zqTeTE{K!a z4pM1vZ?D1IaKkTHclbZJrAgMTa>B5+){e3B&_6#??)HyR0^D~*W#$d@@{S}X)X2Ct zw?G9P)1*g;%UaB{^0Dsy1FgNipJZ`_!Cw{#yDV%0iA=ix@EZ45J%4ZhGUnU+wE)0w z`r^flZgI~U8_9&Gv5zhTV@?iXOV&W@ZFb|u_RzbVpiI+?M~X&FOzZRroRBC0G+FOg zh}1=s7xfGmdfq(QKE~dExBax5PkmyS4+#6C`?ByC^5A4rJCMmpyqOF_v&2Q`m*ej3 zXLSp-Y5y3E6J-X`-%(IJJwBe=lG>29IY0iLfM@TkcVA>CC}%6fVHK)&MZy3V%|I(3 ztX0IAZ4@qI^&Gl%Oop6@wixJj=OW=%X9|mkgWy-}+G#;bHm}Bd68PhUTVjol3vz$U zxi2)3^KV33Lf6Eqa{!kjz!)(xS-eQXPnxqd$v(cCmshqzHCuDX$)~E^%sYpSlPn~& zvYC%G6Cz9~$vAKvT*C6Ue&~;W5}XJg)Fg;5~Xs z_8k;#4OfMAvT5xM%>PnP9dRy{(zs-|bx0#PqA7aYjM z)sHsG2y&&k&WkTeL+J6N+75^1Cx^)guefV`qq z3@}guzBNclQXy#1z&y?KN+|wFWia#w;nRt?eoET5Y);o6tW8k=j+LZq{+r`xV{{0(&6c`Zj_N0m?z~A5S zymWtm|5iojqx_NRiSct&b8~x~m%E!Aa%^lY;(Y>P>&cI60rz7Q(RP-^gM-g_a@|#) zkaMX)hTC;}ARHY>lGSLqTzyp?3`ufag?O(1f!8ChGLH;$pI}Uj! z+HuLX{!c$0+%afz%_nwlXGv<$ZOJ;=*3KkCzfo|>l}$kO7E=R9MkO{meQ07}Zxv(< zr3s0$b$(UJU$!s>142y%)jQ+pjCo)A5nXQv6Ln726n&3kmRgUUymeZ~Y$|H!r|HLQ zGGEBpY&DvWLI^I=o1rCC9GaBg3{wwNMPIQi1z3%q4=XH$?OhQNO4Yy66 ztenzJQ%-iJ{G|Ri5IzoDJkxzLves$f;VAi#5n(S%30Zg?li|CTiSyw51>2|WMNw|%qDQ_Xbk0I$e1`5aL(Z?Yjwu7CPfMsv&Kf_*9k{x@ z$8ygI*j%YJcCR(;D}SPe@|2@PEd(BKs&^sH87zSTwhVgx%yVcVv%oVj_gS@$ZQq7u zq>B}OL1iEIyX|XZ{%_(*t%HQQnt_uEswtA3g-F+4ttxi%V@!Y_onN6f*L`tyPz~W% z{vKj4H?q7+-n?EQEVG?}1{Mz3x#)Rj%0eNP$@c}vlhtMCfEXuPe)T%*!(5Q%I(feoL0|B`E1H8ZM+8iAtT;G*)rB7*f~t&S>Txtr3Eul0N> z(cAFSBxZ^_scPSA>2NKORZo!R6Z|lqx|kt+Y==w!%Vlk>VQTeF6C*O)-MO}jb!#{c zrSELkHoRKD;eZaDaY4S5i;~7T??kCNbme&SkeFXj_wNR=)EjA#agd@KDc)jV76NXb z(U1N%gY;V_?*3$lj`~Ppy`CNk#$ZsyQ{FBq7D}IE+UXBdzAC7B6sOw|s2eF4Q~9y{ zruT4knFj5VfRIkl>qOp0ZJ|v>rahuo_`n5Z<)MpPOCxhRw*^%HK2ihEh|7!~4*k zJT-q~EP@x4%Be9)Ys6MK;L5P{?c%I275CpyFZU+FLUZp;TT^K?zS9~pLb-aYmP{6P za!HH$veRp7t@YaJKI5X_168H<(}Tj&xL}X10XVU zDecg^sMCaY3CqH7ftfTSh8;mVJZk(d2y z4Ss_Idi-SzoXX@hadbv@o8R>I@nPqw{QLO*%|uZfeyj6e-Q7{mEiL^A(g2`WAOaPb z?K;o?ExGc4GaKUQl^4+AFKhtQuPS$S>lXQ?Y{dHp>jdc#r2rV`t*Q(YJ|^d+4hRnp z4o(>Gkwwj|)6w6ey|@xkuA!aqHYQ(IwI>RATl)vU|Apy znJvGH$Xb|wXQ8dkvBgRO7U>_UCXz^OeAH#kd}<6pv8d8NMDr<%C~~b@-Cu4tr2sB! zqTXn+7|MNK`|eaYRnbn`O#Wd@QE?F%7TURdKQhb*9OOVOdXYVc!C(mV7XeA7CYnRC z`gG?%gvMSy$ilxxAC2Sy5a5JIkPrAJOpMyuU~BX?IoX<>KR1wsVx$f^B|0M%v5DN>=xW90e=urjoB%Pk@t1W`- z2@cfN(TQ}%02c`Vdvhq1xFdITxHH=tnsS6&tcYt=W|tB|MVySiP-!IdS8IC=lYpoT z`y3Fq^zytc*Al-LqtlUG>183sknN4mJ;8#tf}(aNE6o3m$cjIwH^46n2?+(Yd$2pZ zFSOjTb6y}TbhGLR@Tz0{LV={q)khb@{uU2SwxA{^uQgRZ)kt&uqfeGf8;t$`@ma3_ z`d$?sCRyGX5C(IV$MiR+Wrvaicq=!zv~;)O)!fPxebwYT&#lFwRhQ87vm?ADB@YZx zJ<^Rq#Y(Z;HVqU#)Np^4i-gYFZve_FDni6I*gsFe5uRtDMI2ZzYJ)=TkRI&pmLlH> zW@;@zuKNa$Qs}P)td}YK1m??rE|r{wng-SYfMf9stV||h3d|`JRT>Dngut0rceLwV)#Qmj*gC=w%130L_<<`zmG=BGZX?Aq4X;3k2m#XwNpH^Eydr7w+1pXx#6sW zw&%&hXsWFcOPSjkd& zdt`XFfeIRp;f4p%mm7dRKkh$z>m@36NLg%R<;ZX)As{GZ`=>r6DPJ5#`(vegRO`tI zxnhMsIAGhiX8HT~ukQTZoPJ75%6Bt`f3=iCUVi=x;w<@N8(t=@u7XRn&c|JH{Bo1! z@s;X{Ci+H>%!OEaCt<$a>RQ^6bkj$*CQKMmp zTD5wK2y`E7d8Er~BHA}3q^%s(jBp|l2!Tqa&yuvrFgQGJj@0q@_g^pV>1QYRdo19r z?Vb~2W&r-~gGA!@@d5}8S&b8v!N-YQ(0}7SFSofy&Pk7Y5BJo@Vvs0-L~ZXCJMfQ4 ziTFob)s7(CtQ!bbW`E@vv6&OnFrAzj!}P(=gQGLuzbr;XW zW~&UDRtO7LnOKk*4uK=0Beeo80_kX{FA|1}8`VYT63b!O+OzzZdz+gh>IUX9wL?N- z)l^uAEG+%UT$hPO8uhxUJ&X;u43-jyNHg4vX0Lc2UHJ<_tB0qw)iJ*u?ezAwQ>hi? zT&?n(fX$K3ejeyFDamn&X~7B89)i zNR5k0)K*<8cpDW;&`-2HCEJ|$JbX4Z(I?4HRPMVVImCT^cwa{7%GN>iW`1l z7D)dRE^MM`%W&023Nu{pQ4eWfC6PkTgcM%xh+>Ah=ov<|&9xddZd03z&3;Y^Px89= zkIw3jpMZZ1R@}G-D{PL7!XZSaPDJf8hoN)|;at390jsCf72#tdqf${0rd&uy3ct9- z9i!G!big%~e~jV<^jjaRB^-psD*cKt>3AHIdSW?lgxxreLZvZK`n=iCi>X{PZ;nsU z*Qoei+BU($qCMS@EPtcy*m5bHRBO5c65?7%8c6#>RiM}b^N3)2irPV}kY$yoTDuPVc@uGNZn#aa9{ceJmj#(PBmis#==Rrv27JJO1jr&Ev1n zzv`T=`pHI5jn2w`?D+HO%52Qsu9p2*8$~j~G01(Uz0{_9)YaN8u5TnN=@z;D(3N44 z5PwL&B(*pL6nuN{NPDUJINh{a&S-DKugP+kQx7v}|3;(t25gKn%e8r#wTp`z^{9-p zER@>nQuUK>53&)O_f3`_U=}kC|Jrm*5LA2f?pq4Z9ayY;e2Q>wz91hS;IXL?5Jhp-tn_6-o_D0!Mc3KnSnbVp zv2nj!Gj)zs_42Z_wNj7!-^0YXySOe@rxuL!o|b2RnAxq?U-XnLtOFyO-!njN4ZlmI zzU)B1fv_Z-ln(AL7y!WE>;K1TU;itFyHDgw-0^Yh5j!9WBlCk*)j#oSTG*a2?%d1A z%iD-Tq0CqSz#@aKlANqtNp3ad(|zzF`8BwssrJ*=0UomIl=#~R0B%3YFX&6+5Fmb& zPf>j138@auJ*xVDL^E>E(e6-Qr8P_mXz;&EjcJ z9IB1caRYtwN1(&Iy)JDy25M(saB~!q4~cGqGgWoq1pP8h@!!v?gbmAQErLBx%Vh!AQA=>z~6^6FZ}r<#V<{^ zE99T$?_8~X5%O6!=y1i%)TF2i=jVDp^80r_8AH9v0^79tbs#4grctW#Tzt(!J2_qm zv=M0BEvhmy7rUE}{pdn14p_|mvoSfLXXGpv{n#s=d6_wl_1=;yU%>B@S&ET=jO;IX zNH$B@;{X1w_KWn+41h;&cdC&E5&eNRujl{fk^V&Q6?*Ly)wppJN1&gMh^VuA$wr=@ zc3w%b?km~aFmlfObfGMosl2@W{xmsUR~v1O&j?zUapR6;)@qJgns^F*MuC4J)Je2mRrAI1(gZ&_u&p*n!~3rqpl!mU(mk3iM$S)BGkuGM{KBO` zjQqd*Chk5Kj)?RsUoEL6yE&pu_c6fJK|{r>cCIblS_#ZES|-tLWnb23Sl zf8}^JuqCStnxOmZQV=lw?1iRYrJ0Gz@_krE+hRN4sIk;?Vv;p~?R3|~Nk?m2n;FgS zj~`m){8T_he$Sv0HqiTX84qcdc+%<90cJ7s+N|(^sj#+{k&)DAip*#TO-v^eMwX;J z8EIm6|MSYHMa-3+%hjvn?E*%k#+XFuu#!p(NOz#GZ!)pI?I>#O`l_klRpZ{Y=g}X) zLqkKM(1t)O6@W1_I-I@KiT47EN3S8RD%!bu7di|hyS*s6=&3h@mCZv3 z6xpsd?K7l3GRS=xr>smbxi(K;c(+t{B9kd$BEW}-9&C_qp|e5x zq+1;ed+dcupCfv-ei=Sgrx6Yl)pryD$(EOBqT#%1qG65 zRXFj`^=bQleubfXk>n(ITw=WaYlXX(PXew`S41b5_`hC4(IDIG=IT7m6V$r(Zi~;7o5r7wI!F4@wrg(!+zQ*$lWCdZ}8I9 z=n~T0epi8^pR&He1!sjwm%uf(B!xFMLIpblm^~&im+CdtGjSanHpJ>Q4U$@P|9Q|2 zoBaoe?7J=^u=QrZIClk-bkp_L;G;161;SvZXWV;JyjhyK<0z^G3T6)OGhanxmLy4yq@i?yI=kc?}n^ zk#56iq*SUsHuqP+YlsM4wq&1rq{L<2?ru4I8c*^1fk)(aL*_-%w4t-?lEBx}N(2B- z3xZ$Qp?RN}iP(o>$tvJ{z~NFOy?PQm{3aPY*r07aYQR0Ui?x0@gM(;HOXS8^vOMI& zw{78b?Np9k5+TMoDyd^69wV#HaVNvgF;X&m_Z9d}8*2fXTJjj6ClAc&nsYASelwCX zX>sfeA!@$4L8s0kSOwJeO;KBJH;B`H$clh8awOkX%ekDhPJWq6fL_#^Gt%p9NNk2X zMp6b-Id^EE!xbrpC}@jNKWoPzr*)M0$ro8MTr|z!Xn(DC!{+rND5?s@0T diff --git a/src/components/EditorHeader/ControlPanel.jsx b/src/components/EditorHeader/ControlPanel.jsx index e24ad5f13..51fed65f0 100644 --- a/src/components/EditorHeader/ControlPanel.jsx +++ b/src/components/EditorHeader/ControlPanel.jsx @@ -20,6 +20,7 @@ import { InputNumber, Tooltip, Spin, + Tag, Toast, Popconfirm, } from "@douyinfe/semi-ui"; @@ -1010,13 +1011,15 @@ export default function ControlPanel({ import_from: { children: [ { - JSON: fileImport, + function: fileImport, + name: "JSON", }, { - DBML: () => { + function: () => { setModal(MODAL.IMPORT); setImportFrom(IMPORT_FROM.DBML); }, + name: "DBML", }, ], }, @@ -1024,40 +1027,47 @@ export default function ControlPanel({ ...(database === DB.GENERIC && { children: [ { - MySQL: () => { + function: () => { setModal(MODAL.IMPORT_SRC); setImportDb(DB.MYSQL); }, + name: "MySQL", }, { - PostgreSQL: () => { + function: () => { setModal(MODAL.IMPORT_SRC); setImportDb(DB.POSTGRES); }, + name: "PostgreSQL", }, { - SQLite: () => { + function: () => { setModal(MODAL.IMPORT_SRC); setImportDb(DB.SQLITE); }, + name: "SQLite", }, { - MariaDB: () => { + function: () => { setModal(MODAL.IMPORT_SRC); setImportDb(DB.MARIADB); }, + name: "MariaDB", }, { - MSSQL: () => { + function: () => { setModal(MODAL.IMPORT_SRC); setImportDb(DB.MSSQL); }, + name: "MSSQL", }, { - Oracle: () => { + function: () => { setModal(MODAL.IMPORT_SRC); setImportDb(DB.ORACLESQL); }, + name: "Oracle", + label: "Beta", }, ], }), @@ -1071,7 +1081,8 @@ export default function ControlPanel({ ...(database === DB.GENERIC && { children: [ { - MySQL: () => { + name: "MySQL", + function: () => { setModal(MODAL.CODE); const src = jsonToMySQL({ tables: tables, @@ -1087,7 +1098,8 @@ export default function ControlPanel({ }, }, { - PostgreSQL: () => { + name: "PostgreSQL", + function: () => { setModal(MODAL.CODE); const src = jsonToPostgreSQL({ tables: tables, @@ -1103,7 +1115,8 @@ export default function ControlPanel({ }, }, { - SQLite: () => { + name: "SQLite", + function: () => { setModal(MODAL.CODE); const src = jsonToSQLite({ tables: tables, @@ -1119,7 +1132,8 @@ export default function ControlPanel({ }, }, { - MariaDB: () => { + name: "MariaDB", + function: () => { setModal(MODAL.CODE); const src = jsonToMariaDB({ tables: tables, @@ -1135,7 +1149,8 @@ export default function ControlPanel({ }, }, { - MSSQL: () => { + name: "MSSQL", + function: () => { setModal(MODAL.CODE); const src = jsonToSQLServer({ tables: tables, @@ -1151,7 +1166,9 @@ export default function ControlPanel({ }, }, { - OracleSQL: () => { + label: "Beta", + name: "Oracle", + function: () => { setModal(MODAL.CODE); const src = jsonToOracleSQL({ tables: tables, @@ -1188,7 +1205,8 @@ export default function ControlPanel({ export_as: { children: [ { - PNG: () => { + name: "PNG", + function: () => { toPng(document.getElementById("canvas")).then(function (dataUrl) { setExportData((prev) => ({ ...prev, @@ -1200,7 +1218,8 @@ export default function ControlPanel({ }, }, { - JPEG: () => { + name: "JPEG", + function: () => { toJpeg(document.getElementById("canvas"), { quality: 0.95 }).then( function (dataUrl) { setExportData((prev) => ({ @@ -1214,7 +1233,8 @@ export default function ControlPanel({ }, }, { - SVG: () => { + name: "SVG", + function: () => { const filter = (node) => node.tagName !== "i"; toSvg(document.getElementById("canvas"), { filter: filter }).then( function (dataUrl) { @@ -1229,7 +1249,8 @@ export default function ControlPanel({ }, }, { - JSON: () => { + name: "JSON", + function: () => { setModal(MODAL.CODE); const result = JSON.stringify( { @@ -1253,7 +1274,8 @@ export default function ControlPanel({ }, }, { - DBML: () => { + name: "DBML", + function: () => { setModal(MODAL.CODE); const result = toDBML({ tables, @@ -1268,7 +1290,8 @@ export default function ControlPanel({ }, }, { - PDF: () => { + name: "PDF", + function: () => { const canvas = document.getElementById("canvas"); toJpeg(canvas).then(function (dataUrl) { const doc = new jsPDF("l", "px", [ @@ -1288,7 +1311,8 @@ export default function ControlPanel({ }, }, { - MERMAID: () => { + name: "Mermaid", + function: () => { setModal(MODAL.CODE); const result = jsonToMermaid({ tables: tables, @@ -1306,7 +1330,8 @@ export default function ControlPanel({ }, }, { - readme: () => { + name: "Markdown", + function: () => { setModal(MODAL.CODE); const result = jsonToDocumentation({ tables: tables, @@ -1518,7 +1543,8 @@ export default function ControlPanel({ theme: { children: [ { - light: () => { + name: t("light"), + function: () => { const body = document.body; if (body.hasAttribute("theme-mode")) { body.setAttribute("theme-mode", "light"); @@ -1528,7 +1554,8 @@ export default function ControlPanel({ }, }, { - dark: () => { + name: t("dark"), + function: () => { const body = document.body; if (body.hasAttribute("theme-mode")) { body.setAttribute("theme-mode", "dark"); @@ -1852,9 +1879,9 @@ export default function ControlPanel({ const body = document.body; if (body.hasAttribute("theme-mode")) { if (body.getAttribute("theme-mode") === "light") { - menu["view"]["theme"].children[1]["dark"](); + menu["view"]["theme"].children[1].function(); } else { - menu["view"]["theme"].children[0]["light"](); + menu["view"]["theme"].children[0].function(); } } }} @@ -1953,7 +1980,7 @@ export default function ControlPanel({ if (menu[category][item].children) { return ( ( - {t(Object.keys(e)[0])} + {e.name} + {e.label && ( + + {e.label} + + )} ), )} diff --git a/src/components/Workspace.jsx b/src/components/Workspace.jsx index c96d9e023..7bf55b84e 100644 --- a/src/components/Workspace.jsx +++ b/src/components/Workspace.jsx @@ -19,7 +19,7 @@ import { useEnums, } from "../hooks"; import FloatingControls from "./FloatingControls"; -import { Modal } from "@douyinfe/semi-ui"; +import { Modal, Tag } from "@douyinfe/semi-ui"; import { useTranslation } from "react-i18next"; import { databases } from "../data/databases"; import { isRtl } from "../i18n/utils/rtl"; @@ -165,7 +165,7 @@ export default function WorkSpace() { enums, gistId, loadedFromGistId, - saveState + saveState, ]); const load = useCallback(async () => { @@ -466,7 +466,7 @@ export default function WorkSpace() { }} okButtonProps={{ disabled: selectedDb === "" }} > -
+
{Object.values(databases).map((x) => (
-
{x.name}
+
+
{x.name}
+ {x.beta && ( + + Beta + + )} +
{x.image && ( Date: Wed, 2 Apr 2025 01:40:31 +0400 Subject: [PATCH 05/10] Export from generic --- src/utils/exportSQL/generic.js | 95 +++++++++++++++------------------- 1 file changed, 43 insertions(+), 52 deletions(-) diff --git a/src/utils/exportSQL/generic.js b/src/utils/exportSQL/generic.js index 751102dca..972fcb52e 100644 --- a/src/utils/exportSQL/generic.js +++ b/src/utils/exportSQL/generic.js @@ -1,3 +1,4 @@ +import { DB } from "../../data/constants"; import { dbToTypes, defaultTypes } from "../../data/datatypes"; import { getInlineFK, parseDefault } from "./shared"; @@ -42,10 +43,10 @@ export function generateSchema(type) { export function getTypeString( field, currentDb, - dbms = "mysql", + dbms = DB.MYSQL, baseType = false, ) { - if (dbms === "mysql") { + if (dbms === DB.MYSQL) { if (field.type === "UUID") { return `VARCHAR(36)`; } @@ -62,7 +63,7 @@ export function getTypeString( return "JSON"; } return field.type; - } else if (dbms === "postgres") { + } else if (dbms === DB.POSTGRES) { if (field.type === "SMALLINT" && field.increment) { return "smallserial"; } @@ -97,7 +98,7 @@ export function getTypeString( return `${field.type.toLowerCase()}${field.size ? `(${field.size})` : ""}`; } return field.type.toLowerCase(); - } else if (dbms === "mssql") { + } else if (dbms === DB.MSSQL) { let type = field.type; switch (field.type) { case "ENUM": @@ -134,23 +135,12 @@ export function getTypeString( } return type; - } else if (dbms === "oraclesql") { + } else if (dbms === DB.ORACLESQL) { let oracleType; switch (field.type) { - case "INT": - case "INTEGER": - case "SMALLINT": case "BIGINT": - case "DECIMAL": - case "NUMERIC": - case "REAL": - case "FLOAT": - case "DOUBLE": oracleType = "NUMBER"; break; - case "CHAR": - oracleType = "CHAR"; - break; case "VARCHAR": oracleType = "VARCHAR2"; break; @@ -158,35 +148,23 @@ export function getTypeString( oracleType = "CLOB"; break; case "TIME": - oracleType = "TIMESTAMP"; - break; - case "TIMESTAMP": - case "DATE": case "DATETIME": - oracleType = field.type; - break; - case "BOOLEAN": - oracleType = "NUMBER(1)"; + oracleType = "TIMESTAMP"; break; case "BINARY": case "VARBINARY": oracleType = "RAW"; break; - case "BLOB": - oracleType = "BLOB"; - break; - case "JSON": - oracleType = "JSON"; - break; case "UUID": oracleType = "RAW(16)"; break; - case "ENUM": case "SET": - oracleType = "VARCHAR2"; + case "ENUM": + oracleType = field.name + "_t"; break; default: - throw new Error(`Unsupported type for Oracle: ${field.type}`); + oracleType = field.type; + break; } const typeInfo = dbToTypes[currentDb][oracleType]; if (typeInfo.isSized || typeInfo.hasPrecision) { @@ -197,12 +175,6 @@ export function getTypeString( } } - if (field.type === "ENUM" || field.type === "SET") { - oracleType += ` CHECK (${field.name} IN (${field.values - .map((v) => `'${v}'`) - .join(", ")}))`; - } - return oracleType; } } @@ -320,16 +292,20 @@ export function jsonToPostgreSQL(obj) { type.comment === "" ? "" : `/**\n${type.comment}\n*/\n` }CREATE TYPE ${type.name} AS (\n${type.fields .map( - (f) => `\t${f.name} ${getTypeString(f, obj.database, "postgres")}`, + (f) => `\t${f.name} ${getTypeString(f, obj.database, DB.POSTGRES)}`, ) .join("\n")}\n);` ); } else { return `CREATE TYPE ${type.name} AS (\n${type.fields - .map((f) => `\t${f.name} ${getTypeString(f, obj.database, "postgres")}`) - .join( - "\n", - )}\n);\n${type.comment != "" ? `\nCOMMENT ON TYPE ${type.name} IS '${type.comment}';\n` : ""}`; + .map( + (f) => `\t${f.name} ${getTypeString(f, obj.database, DB.POSTGRES)}`, + ) + .join(",\n")}\n);\n${ + type.comment && type.comment.trim() != "" + ? `\nCOMMENT ON TYPE ${type.name} IS '${type.comment}';\n` + : "" + }`; } })}\n${obj.tables .map( @@ -352,7 +328,7 @@ export function jsonToPostgreSQL(obj) { (field) => `${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t"${ field.name - }" ${getTypeString(field, obj.database, "postgres")}${ + }" ${getTypeString(field, obj.database, DB.POSTGRES)}${ field.notNull ? " NOT NULL" : "" }${field.unique ? " UNIQUE" : ""}${ field.default !== "" ? ` DEFAULT ${parseDefault(field)}` : "" @@ -482,7 +458,7 @@ export function jsonToMariaDB(obj) { (field) => `\t\`${ field.name - }\` ${getTypeString(field, obj.database, "oraclesql")}${field.notNull ? " NOT NULL" : ""}${ + }\` ${getTypeString(field, obj.database, DB.MYSQL)}${field.notNull ? " NOT NULL" : ""}${ field.increment ? " AUTO_INCREMENT" : "" }${field.unique ? " UNIQUE" : ""}${ field.default !== "" @@ -541,7 +517,7 @@ export function jsonToSQLServer(obj) { }CREATE TYPE [${type.name}] FROM ${ type.fields.length < 0 ? "" - : `${getTypeString(type.fields[0], obj.database, "mssql", true)}` + : `${getTypeString(type.fields[0], obj.database, DB.MSSQL, true)}` };\nGO\n`; }) .join("\n")}\n${obj.tables @@ -554,7 +530,7 @@ export function jsonToSQLServer(obj) { (field) => `${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t[${ field.name - }] ${getTypeString(field, obj.database, "mssql")}${ + }] ${getTypeString(field, obj.database, DB.MSSQL)}${ field.notNull ? " NOT NULL" : "" }${field.increment ? " IDENTITY" : ""}${ field.unique ? " UNIQUE" : "" @@ -604,13 +580,30 @@ export function jsonToOracleSQL(obj) { .map( (table) => `${ + table.fields.filter((f) => f.type === "ENUM" || f.type === "SET") + .length > 0 + ? `${table.fields + .filter((f) => f.type === "ENUM" || f.type === "SET") + .map( + (f) => + `CREATE DOMAIN "${f.name}_t" AS ENUM (${f.values + .map((v) => `'${v}'`) + .join(", ")});\n`, + ) + .join("\n")}\n` + : "" + }${ 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") : ""}${ + }" ${getTypeString(field, obj.database, DB.ORACLESQL)}${ + field.notNull ? " NOT NULL" : "" + }${field.increment ? " GENERATED ALWAYS AS IDENTITY" : ""}${ + field.unique ? " UNIQUE" : "" + }${ field.default !== "" ? ` DEFAULT ${parseDefault(field, obj.database)}` : "" @@ -640,9 +633,7 @@ export function jsonToOracleSQL(obj) { .join("\n\n")}\n${obj.references .map( (r) => - `ALTER TABLE "${obj.tables[r.startTableId].name}"\nADD CONSTRAINT fk_${ - r.startTableId - }_${r.endTableId} FOREIGN KEY ("${ + `ALTER TABLE "${obj.tables[r.startTableId].name}"\nADD CONSTRAINT "${r.name}" FOREIGN KEY ("${ obj.tables[r.startTableId].fields[r.startFieldId].name }") REFERENCES "${obj.tables[r.endTableId].name}"("${ obj.tables[r.endTableId].fields[r.endFieldId].name From 12cf4e74cc566843acf80c82a5cdc7116da6b11f Mon Sep 17 00:00:00 2001 From: 1ilit <1ilit@proton.me> Date: Wed, 2 Apr 2025 02:53:16 +0400 Subject: [PATCH 06/10] Fix export --- src/data/datatypes.js | 143 ++++++++++++++++++++++++------- src/utils/exportSQL/index.js | 2 +- src/utils/exportSQL/oraclesql.js | 2 +- 3 files changed, 114 insertions(+), 33 deletions(-) diff --git a/src/data/datatypes.js b/src/data/datatypes.js index 3186d4db1..58d3865b7 100644 --- a/src/data/datatypes.js +++ b/src/data/datatypes.js @@ -174,7 +174,9 @@ const defaultTypesBase = { } const content = field.default.split(" "); const date = content[0].split("-"); - return Number.parseInt(date[0]) >= 1970 && Number.parseInt(date[0]) <= 2038; + return ( + Number.parseInt(date[0]) >= 1970 && Number.parseInt(date[0]) <= 2038 + ); }, hasCheck: false, isSized: false, @@ -592,7 +594,9 @@ const mysqlTypesBase = { } const content = field.default.split(" "); const date = content[0].split("-"); - return Number.parseInt(date[0]) >= 1970 && Number.parseInt(date[0]) <= 2038; + return ( + Number.parseInt(date[0]) >= 1970 && Number.parseInt(date[0]) <= 2038 + ); }, hasCheck: false, isSized: false, @@ -1100,8 +1104,9 @@ const postgresTypesBase = { checkDefault: (field) => { const specialValues = ["now", "allballs"]; return ( - /^(?:[01]?\d|2[0-3]):[0-5]?\d:[0-5]?\d([+-]\d{2}:\d{2})?$/.test(field.default) || - specialValues.includes(field.default.toLowerCase()) + /^(?:[01]?\d|2[0-3]):[0-5]?\d:[0-5]?\d([+-]\d{2}:\d{2})?$/.test( + field.default, + ) || specialValues.includes(field.default.toLowerCase()) ); }, hasCheck: false, @@ -1126,7 +1131,8 @@ const postgresTypesBase = { ]; return ( /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(field.default) || - (Number.parseInt(date[0]) >= 1970 && Number.parseInt(date[0]) <= 2038) || + (Number.parseInt(date[0]) >= 1970 && + Number.parseInt(date[0]) <= 2038) || specialValues.includes(field.default.toLowerCase()) ); }, @@ -1305,7 +1311,11 @@ const postgresTypesBase = { elementsStr = field.default.slice(1, -1); } elements = JSON.parse(elementsStr); - return Array.isArray(elements) && elements.length === field.size && elements.every(Number.isFinite); + return ( + Array.isArray(elements) && + elements.length === field.size && + elements.every(Number.isFinite) + ); } catch (e) { return false; } @@ -1315,7 +1325,7 @@ const postgresTypesBase = { hasPrecision: false, hasQuotes: true, }, - HALFVEC:{ + HALFVEC: { type: "HALFVEC", checkDefault: (field) => { let elements; @@ -1325,7 +1335,11 @@ const postgresTypesBase = { elementsStr = field.default.slice(1, -1); } elements = JSON.parse(elementsStr); - return Array.isArray(elements) && elements.length === field.size && elements.every(Number.isFinite); + return ( + Array.isArray(elements) && + elements.length === field.size && + elements.every(Number.isFinite) + ); } catch (e) { return false; } @@ -1342,9 +1356,9 @@ const postgresTypesBase = { if (strHasQuotes(field.default)) { elementsStr = field.default.slice(1, -1); } - const lengthStr = elementsStr.split('/')[1] - const length = Number.parseInt(lengthStr) - return length === field.size + const lengthStr = elementsStr.split("/")[1]; + const length = Number.parseInt(lengthStr); + return length === field.size; }, hasCheck: true, isSized: true, @@ -1507,7 +1521,9 @@ const sqliteTypesBase = { } const content = field.default.split(" "); const date = content[0].split("-"); - return Number.parseInt(date[0]) >= 1970 && Number.parseInt(date[0]) <= 2038; + return ( + Number.parseInt(date[0]) >= 1970 && Number.parseInt(date[0]) <= 2038 + ); }, hasCheck: false, isSized: false, @@ -1768,7 +1784,9 @@ const mssqlTypesBase = { } const content = field.default.split(" "); const date = content[0].split("-"); - return Number.parseInt(date[0]) >= 1970 && Number.parseInt(date[0]) <= 2038; + return ( + Number.parseInt(date[0]) >= 1970 && Number.parseInt(date[0]) <= 2038 + ); }, hasCheck: false, isSized: false, @@ -1935,6 +1953,16 @@ export const mssqlTypes = new Proxy(mssqlTypesBase, { }); const oraclesqlTypesBase = { + INTEGER: { + type: "INTEGER", + checkDefault: (field) => { + return intRegex.test(field.default); + }, + hasCheck: true, + isSized: false, + hasPrecision: false, + canIncrement: true, + }, NUMBER: { type: "NUMBER", checkDefault: (field) => { @@ -1945,6 +1973,25 @@ const oraclesqlTypesBase = { hasPrecision: true, canIncrement: false, }, + FLOAT: { + type: "FLOAT", + checkDefault: (field) => { + return /^-?\d+(\.\d+)?$/.test(field.default); + }, + hasCheck: true, + isSized: false, + hasPrecision: true, + }, + LONG: { + type: "LONG", + checkDefault: (field) => { + return intRegex.test(field.default); + }, + hasCheck: true, + isSized: false, + hasPrecision: false, + canIncrement: true, + }, VARCHAR2: { type: "VARCHAR2", checkDefault: (field) => { @@ -1959,6 +2006,20 @@ const oraclesqlTypesBase = { defaultSize: 4000, hasQuotes: true, }, + NVARCHAR2: { + 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) => { @@ -1973,6 +2034,20 @@ const oraclesqlTypesBase = { defaultSize: 1, hasQuotes: true, }, + NCHAR: { + type: "NCHAR", + 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, @@ -1997,6 +2072,30 @@ const oraclesqlTypesBase = { hasPrecision: false, noDefault: true, }, + BFILE: { + type: "BFILE", + checkDefault: (field) => true, + isSized: false, + hasCheck: false, + hasPrecision: false, + noDefault: true, + }, + JSON: { + type: "JSON", + checkDefault: (field) => true, + isSized: false, + hasCheck: false, + hasPrecision: false, + noDefault: true, + }, + VECTOR: { + type: "VECTOR", + checkDefault: (field) => true, + isSized: false, + hasCheck: false, + hasPrecision: false, + noDefault: true, + }, DATE: { type: "DATE", checkDefault: (field) => { @@ -2034,24 +2133,6 @@ const oraclesqlTypesBase = { 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) => { diff --git a/src/utils/exportSQL/index.js b/src/utils/exportSQL/index.js index f3de18e0c..014f381e0 100644 --- a/src/utils/exportSQL/index.js +++ b/src/utils/exportSQL/index.js @@ -19,7 +19,7 @@ export function exportSQL(diagram) { return toMariaDB(diagram); case DB.MSSQL: return toMSSQL(diagram); - case DB.ORACLE: + case DB.ORACLESQL: return toOracleSQL(diagram); default: return ""; diff --git a/src/utils/exportSQL/oraclesql.js b/src/utils/exportSQL/oraclesql.js index 308caaf3d..81d808126 100644 --- a/src/utils/exportSQL/oraclesql.js +++ b/src/utils/exportSQL/oraclesql.js @@ -12,7 +12,7 @@ export function toOracleSQL(diagram) { (field) => `${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t"${ field.name - }" ${field.type}${field.size !== undefined && field.size !== "" ? "(" + field.size + ")" : ""}${ + }" ${field.type}${field.size && Boolean(field.size.trim()) ? "(" + field.size + ")" : ""}${ field.notNull ? " NOT NULL" : "" }${ field.increment ? " GENERATED ALWAYS AS IDENTITY" : "" From 893dfe0efe6009250acd8c2c1ca457d6e9a0f9fc Mon Sep 17 00:00:00 2001 From: 1ilit <1ilit@proton.me> Date: Wed, 2 Apr 2025 03:03:17 +0400 Subject: [PATCH 07/10] Add import affinity --- src/utils/importSQL/oraclesql.js | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/utils/importSQL/oraclesql.js b/src/utils/importSQL/oraclesql.js index 761551ad8..790f0e2bc 100644 --- a/src/utils/importSQL/oraclesql.js +++ b/src/utils/importSQL/oraclesql.js @@ -1,6 +1,24 @@ -import { Cardinality, Constraint } from "../../data/constants"; - -export function fromOracleSQL(ast) { +import { Cardinality, Constraint, DB } from "../../data/constants"; +import { dbToTypes } from "../../data/datatypes"; + +const affinity = { + [DB.ORACLESQL]: new Proxy( + { INT: "INTEGER" }, + { NUMERIC: "NUMBER" }, + { DECIMAL: "NUMBER" }, + { CHARACTER: "CHAR" }, + { get: (target, prop) => (prop in target ? target[prop] : "BLOB") }, + ), + [DB.GENERIC]: new Proxy( + { + INTEGER: "INT", + MEDIUMINT: "INTEGER", + }, + { get: (target, prop) => (prop in target ? target[prop] : "BLOB") }, + ), +}; + +export function fromOracleSQL(ast, diagramDb = DB.GENERIC) { const tables = []; const relationships = []; const enums = []; @@ -22,6 +40,9 @@ export function fromOracleSQL(ast) { field.name = d.name; let type = d.type.type.toUpperCase(); + if (!dbToTypes[diagramDb][type]) { + type = affinity[diagramDb][type]; + } field.type = type; if (d.type.scale && d.type.precision) { From 9d5c5309e0311287f4ae791c473f15d96422b5d0 Mon Sep 17 00:00:00 2001 From: HansMarcus01 Date: Mon, 29 Sep 2025 14:22:16 -0600 Subject: [PATCH 08/10] Feat: add oracle parser to support import and export oracle sql code --- src/components/EditorHeader/Modal/Modal.jsx | 63 +++- src/data/datatypes.js | 23 -- src/pages/LandingPage.jsx | 1 - src/utils/exportSQL/index.js | 1 - src/utils/exportSQL/oracle.js | 59 --- src/utils/exportSQL/oraclesql.js | 75 ++-- src/utils/importSQL/oracle.js | 270 -------------- src/utils/importSQL/oraclesql.js | 389 +++++++++++++++----- test/oracle__u_idx.test.js | 16 +- test/oracle_c_idx.test.js | 18 +- test/oracle_chk.test.js | 16 +- test/oracle_export.test.js | 20 +- test/oracle_pk_fk.test.js | 20 +- test/oracle_unique.test.js | 14 +- 14 files changed, 440 insertions(+), 545 deletions(-) delete mode 100644 src/utils/exportSQL/oracle.js delete mode 100644 src/utils/importSQL/oracle.js diff --git a/src/components/EditorHeader/Modal/Modal.jsx b/src/components/EditorHeader/Modal/Modal.jsx index 8ac007751..2004c4c08 100644 --- a/src/components/EditorHeader/Modal/Modal.jsx +++ b/src/components/EditorHeader/Modal/Modal.jsx @@ -138,18 +138,63 @@ export default function Modal({ const parseSQLAndLoadDiagram = () => { const targetDatabase = database === DB.GENERIC ? importDb : database; + // Preprocess SQL for Oracle parser + const preprocessOracleSQL = (sql) => { + return sql + // Remove comments that start with --@ which can cause parsing issues + .replace(/--@[^\r\n]*/g, '') + // Remove standalone comment lines that just contain -- + .replace(/^\s*--\s*$/gm, '') + // Remove table comment blocks like -- TABLE: TABLENAME -- + .replace(/--\s*TABLE:\s*[^-]*--/g, '') + // Clean up multiple empty lines + .replace(/\n\s*\n\s*\n/g, '\n\n') + // Ensure statements end with semicolons + .split('\n') + .map(line => line.trim()) + .filter(line => line.length > 0 && !line.match(/^--\s*$/)) + .join('\n'); + }; + let ast = null; try { if (targetDatabase === DB.ORACLESQL) { - const oracleParser = new OracleParser(); - - ast = oracleParser.parse(importSource.src); + // Try Oracle parser first + try { + const oracleParser = new OracleParser(); + const preprocessedSQL = preprocessOracleSQL(importSource.src); + console.log("Preprocessed SQL:", preprocessedSQL); + ast = oracleParser.parse(preprocessedSQL); + console.log("Oracle parser AST:", ast); + } catch (oracleError) { + console.warn("Oracle parser failed, trying standard parser:", oracleError); + // Fallback to standard parser with modified SQL + try { + const parser = new Parser(); + // Try to convert Oracle-specific syntax to MySQL-compatible + const mysqlCompatibleSQL = importSource.src + .replace(/NUMBER\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)/gi, 'DECIMAL($1,$2)') + .replace(/NUMBER\s*\(\s*(\d+)\s*\)/gi, 'INT($1)') + .replace(/NUMBER(?!\s*\()/gi, 'INT') + .replace(/VARCHAR2/gi, 'VARCHAR') + .replace(/--@[^\r\n]*/g, ''); + + ast = parser.astify(mysqlCompatibleSQL, { + database: "mysql", + }); + console.log("Standard parser AST with MySQL compatibility:", ast); + } catch (standardError) { + console.error("Both parsers failed:", standardError); + throw new Error(`Failed to parse SQL. Oracle parser error: ${oracleError.message}. Standard parser error: ${standardError.message}`); + } + } } else { const parser = new Parser(); ast = parser.astify(importSource.src, { database: targetDatabase, }); + console.log("Standard parser AST:", ast); } } catch (error) { const message = error.location @@ -161,6 +206,13 @@ export default function Modal({ } try { + console.log("About to import SQL with AST:", ast); + console.log("Target database:", database === DB.GENERIC ? importDb : database); + console.log("Current database:", database); + if (!ast) { + throw new Error("AST is null or undefined"); + } + const diagramData = importSQL( ast, database === DB.GENERIC ? importDb : database, @@ -190,10 +242,11 @@ export default function Modal({ } setModal(MODAL.NONE); - } catch { + } catch (importError) { + console.error("Import error:", importError); setError({ type: STATUS.ERROR, - message: `Please check for syntax errors or let us know about the error.`, + message: `Import failed: ${importError.message || "Please check for syntax errors or let us know about the error."}`, }); } }; diff --git a/src/data/datatypes.js b/src/data/datatypes.js index 58d3865b7..cfcd996ce 100644 --- a/src/data/datatypes.js +++ b/src/data/datatypes.js @@ -7,16 +7,6 @@ const binaryRegex = /^[01]+$/; /* eslint-disable no-unused-vars */ const defaultTypesBase = { - NUMBER: { - type: "NUMBER", - checkDefault: (field) => { - return intRegex.test(field.default); - }, - hasCheck: true, - isSized: false, - hasPrecision: true, - canIncrement: true, - }, INT: { type: "INT", checkDefault: (field) => { @@ -315,19 +305,6 @@ const defaultTypesBase = { hasPrecision: false, noDefault: 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: 10, - }, }; export const defaultTypes = new Proxy(defaultTypesBase, { diff --git a/src/pages/LandingPage.jsx b/src/pages/LandingPage.jsx index 50015bfee..8c846ef00 100644 --- a/src/pages/LandingPage.jsx +++ b/src/pages/LandingPage.jsx @@ -16,7 +16,6 @@ import screenshot from "../assets/screenshot.png"; import FadeIn from "../animations/FadeIn"; import axios from "axios"; import { languages } from "../i18n/i18n"; -import { Tweet } from "react-tweet"; import { socials } from "../data/socials"; function shortenNumber(number) { diff --git a/src/utils/exportSQL/index.js b/src/utils/exportSQL/index.js index 014f381e0..987fb26a5 100644 --- a/src/utils/exportSQL/index.js +++ b/src/utils/exportSQL/index.js @@ -5,7 +5,6 @@ import { toMySQL } from "./mysql"; import { toOracleSQL } from "./oraclesql"; import { toPostgres } from "./postgres"; import { toSqlite } from "./sqlite"; -import { toOracle } from "./oracle"; export function exportSQL(diagram) { switch (diagram.database) { diff --git a/src/utils/exportSQL/oracle.js b/src/utils/exportSQL/oracle.js deleted file mode 100644 index 4cda4de17..000000000 --- a/src/utils/exportSQL/oracle.js +++ /dev/null @@ -1,59 +0,0 @@ -import { parseDefault } from "./shared"; - -export function toOracle(diagram) { - return `${diagram.tables - .map( - (table) => - `CREATE TABLE ${table.name} (\n${table.fields - .map( - (field) => - `\t"${field.name}" ${field.type}${field.size ? `(${field.size})` : ""}${ - field.default !== "" ? ` DEFAULT ${parseDefault(field, diagram.database)}` : "" - }${field.notNull ? " NOT NULL" : ""}` - ) - .join(",\n")}${ - table.fields.filter((f) => f.unique && !f.primary).length > 0 - ? `,\n\t${table.fields - .filter((f) => f.unique && !f.primary) - .map((f) => `CONSTRAINT ${table.name}_${f.name}_uk UNIQUE("${f.name}")`) - .join(",\n\t")}` - : "" - }${ - table.fields.filter((f) => f.check).length > 0 - ? `,\n\t${table.fields - .filter((f) => f.check) - .map((f) => `CONSTRAINT ${table.name}_${f.name}_chk CHECK("${f.name}" ${f.check})`) - .join(",\n\t")}` - : "" - }${ - table.fields.filter((f) => f.primary).length > 0 - ? `,\n\tCONSTRAINT ${table.name}_pk PRIMARY KEY(${table.fields - .filter((f) => f.primary) - .map((f) => `"${f.name}"`) - .join(", ")})` - : "" - }\n);\n${table.comment ? `COMMENT ON TABLE ${table.name} IS '${table.comment}';` : ""}${table.fields - .map( - (field) => - field.comment ? `\nCOMMENT ON COLUMN "${table.name}"."${field.name}" IS '${field.comment}';` : "" - ) - .join("")}\n${table.indices - .map( - (i) => - `\nCREATE ${i.unique ? "UNIQUE " : ""}INDEX "${i.name}" ON ${table.name} (${i.fields - .map((f) => `"${f}"`) - .join(", ")});` - ) - .join("")}` - ) - .join("\n")}\n${diagram.references - .map( - (r) => { - const deleteConstraint = r.deleteConstraint && ['CASCADE', 'SET NULL'].includes(r.deleteConstraint.toUpperCase()) - ? ` ON DELETE ${r.deleteConstraint.toUpperCase()}` - : ''; - return `ALTER TABLE ${diagram.tables[r.startTableId].name} ADD CONSTRAINT ${diagram.tables[r.startTableId].name}_${diagram.tables[r.startTableId].fields[r.startFieldId].name}_fk\nFOREIGN KEY("${diagram.tables[r.startTableId].fields[r.startFieldId].name}") REFERENCES ${diagram.tables[r.endTableId].name}("${diagram.tables[r.endTableId].fields[r.endFieldId].name}")${deleteConstraint};` - } - ) - .join("\n")}`; -} diff --git a/src/utils/exportSQL/oraclesql.js b/src/utils/exportSQL/oraclesql.js index 81d808126..6234273d1 100644 --- a/src/utils/exportSQL/oraclesql.js +++ b/src/utils/exportSQL/oraclesql.js @@ -1,56 +1,61 @@ -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 + `CREATE TABLE ${table.name} (\n${table.fields .map( (field) => - `${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t"${ - field.name - }" ${field.type}${field.size && Boolean(field.size.trim()) ? "(" + field.size + ")" : ""}${ - field.notNull ? " NOT NULL" : "" - }${ + `\t${field.name} ${field.type}${field.size ? `(${field.size})` : ""}${ + field.default !== "" ? ` DEFAULT ${parseDefault(field, diagram.database)}` : "" + }${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.unique && !f.primary).length > 0 + ? `,\n\t${table.fields + .filter((f) => f.unique && !f.primary) + .map((f) => `CONSTRAINT ${table.name}_${f.name}_uk UNIQUE(${f.name})`) + .join(",\n\t")}` + : "" + }${ + table.fields.filter((f) => f.check).length > 0 + ? `,\n\t${table.fields + .filter((f) => f.check) + .map((f) => `CONSTRAINT ${table.name}_${f.name}_chk CHECK(${f.name} ${f.check})`) + .join(",\n\t")}` + : "" + }${ table.fields.filter((f) => f.primary).length > 0 - ? `,\n\tPRIMARY KEY(${table.fields + ? `,\n\tCONSTRAINT ${table.name}_pk PRIMARY KEY(${table.fields .filter((f) => f.primary) - .map((f) => `"${f.name}"`) + .map((f) => `${f.name}`) .join(", ")})` : "" - }\n)${table.comment ? ` -- ${table.comment}` : ""};\n${`\n${table.indices + }\n);\n${table.comment ? `COMMENT ON TABLE ${table.name} IS '${table.comment}';` : ""}${table.fields + .map( + (field) => + field.comment ? `\nCOMMENT ON COLUMN ${table.name}.${field.name} IS '${field.comment}';` : "" + ) + .join("")}\n${table.indices .map( (i) => - `\nCREATE ${i.unique ? "UNIQUE " : ""}INDEX "${i.name}"\nON "${table.name}" (${i.fields - .map((f) => `"${f}"`) - .join(", ")});`, + `\nCREATE ${i.unique ? "UNIQUE " : ""}INDEX ${i.name} ON ${table.name} (${i.fields + .map((f) => `${f}`) + .join(", ")});` ) - .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")}`; + .map( + (r) => { + const deleteConstraint = r.deleteConstraint && ['CASCADE', 'SET NULL'].includes(r.deleteConstraint.toUpperCase()) + ? ` ON DELETE ${r.deleteConstraint.toUpperCase()}` + : ''; + return `ALTER TABLE ${diagram.tables[r.startTableId].name} ADD CONSTRAINT ${diagram.tables[r.startTableId].name}_${diagram.tables[r.startTableId].fields[r.startFieldId].name}_fk\nFOREIGN KEY(${diagram.tables[r.startTableId].fields[r.startFieldId].name}) REFERENCES ${diagram.tables[r.endTableId].name}(${diagram.tables[r.endTableId].fields[r.endFieldId].name})${deleteConstraint};` + } + ) + .join("\n")}`; } diff --git a/src/utils/importSQL/oracle.js b/src/utils/importSQL/oracle.js deleted file mode 100644 index 29df9e70a..000000000 --- a/src/utils/importSQL/oracle.js +++ /dev/null @@ -1,270 +0,0 @@ -import { RelationshipType, RelationshipCardinalities, DB } from "../../data/constants"; -import { dbToTypes } from "../../data/datatypes"; -import { buildSQLFromAST } from "./shared"; - -const affinity = { - [DB.MARIADB]: new Proxy( - { INT: "INTEGER" }, - { get: (target, prop) => (prop in target ? target[prop] : "BLOB") }, - ), - [DB.GENERIC]: new Proxy( - { - INT: "INTEGER", - TINYINT: "SMALLINT", - MEDIUMINT: "INTEGER", - BIT: "BOOLEAN", - YEAR: "INTEGER", - }, - { get: (target, prop) => (prop in target ? target[prop] : "BLOB") }, - ), -}; - -export function fromOracle(ast, diagramDb = DB.GENERIC) { - const tables = []; - const relationships = []; - - const parseSingleStatement = (e) => { - if (e.type === "create") { - if (e.keyword === "table") { - const table = {}; - table.name = e.table[0].table; - table.comment = ""; - table.color = "#175e7a"; - table.fields = []; - table.indices = []; - table.id = tables.length; - e.create_definitions.forEach((d) => { - if (d.resource === "column") { - const field = {}; - field.name = d.column.column; - - let type = d.definition.dataType; - if (!dbToTypes[diagramDb][type]) { - type = affinity[diagramDb][type]; - } - field.type = type; - - if (d.definition.expr && d.definition.expr.type === "expr_list") { - field.values = d.definition.expr.value.map((v) => v.value); - } - field.comment = d.comment ? d.comment.value.value : ""; - field.unique = false; - if (d.unique) field.unique = true; - field.increment = false; - if (d.auto_increment) field.increment = true; - field.notNull = false; - if (d.nullable) field.notNull = true; - field.primary = false; - if (d.primary_key) field.primary = true; - 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.MARIADB); - } - - 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 = 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; - - if (table.fields[startFieldId].unique) { - relationship.relationshipType = RelationshipType.ONE_TO_ONE; - relationship.cardinality = RelationshipCardinalities[RelationshipType.ONE_TO_ONE][0].label; - } else { - relationship.relationshipType = RelationshipType.ONE_TO_MANY; - relationship.cardinality = RelationshipCardinalities[RelationshipType.ONE_TO_MANY][0].label; - } - - relationships.push(relationship); - } - } - }); - table.fields.forEach((f, j) => { - f.id = j; - }); - tables.push(table); - } else if (e.keyword === "index") { - const index = {}; - index.name = e.index; - index.unique = false; - if (e.index_type === "unique") index.unique = true; - index.fields = []; - e.index_columns.forEach((f) => index.fields.push(f.column)); - - let found = -1; - tables.forEach((t, i) => { - if (found !== -1) return; - if (t.name === e.table.table) { - t.indices.push(index); - found = i; - } - }); - - if (found !== -1) tables[found].indices.forEach((i, j) => (i.id = j)); - } - } 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 = startTable + "_" + startField + "_fk"; - 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.relationshipType = RelationshipType.ONE_TO_ONE; - relationship.cardinality = RelationshipCardinalities[RelationshipType.ONE_TO_ONE][0].label; - } else { - relationship.relationshipType = RelationshipType.ONE_TO_MANY; - relationship.cardinality = RelationshipCardinalities[RelationshipType.ONE_TO_MANY][0].label; - } - - relationships.push(relationship); - - relationships.forEach((r, i) => (r.id = i)); - } - }); - } - }; - - if (Array.isArray(ast)) { - ast.forEach((e) => parseSingleStatement(e)); - } else { - parseSingleStatement(ast); - } - - relationships.forEach((r, i) => (r.id = i)); - - return { tables, relationships }; -} - - diff --git a/src/utils/importSQL/oraclesql.js b/src/utils/importSQL/oraclesql.js index 790f0e2bc..5d3c86d29 100644 --- a/src/utils/importSQL/oraclesql.js +++ b/src/utils/importSQL/oraclesql.js @@ -1,4 +1,4 @@ -import { Cardinality, Constraint, DB } from "../../data/constants"; +import { Constraint, DB } from "../../data/constants"; import { dbToTypes } from "../../data/datatypes"; const affinity = { @@ -24,113 +24,304 @@ export function fromOracleSQL(ast, diagramDb = DB.GENERIC) { 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(); - if (!dbToTypes[diagramDb][type]) { - type = affinity[diagramDb][type]; - } - field.type = type; - - if (d.type.scale && d.type.precision) { - field.size = d.type.precision + "," + d.type.scale; - } else if (d.type.size || d.type.precision) { - field.size = d.type.size || d.type.precision; - } - - field.comment = ""; - field.check = ""; - field.default = ""; - field.unique = false; - field.increment = false; - field.notNull = false; - field.primary = false; - - for (const c of d.constraints) { - if (c.constraint.primary_key === "primary key") + console.log("Parsing statement:", e); + // Handle Oracle parser format + if (e.operation === "create" && e.object === "table") { + const table = {}; + table.name = e.name?.name || e.name; + table.comment = ""; + table.color = "#175e7a"; + table.fields = []; + table.indices = []; + table.id = tables.length; + // Check if table structure exists + if (!e.table) { + console.error("Table structure missing in AST:", e); + return; + } + // Check for different possible structures + const columns = e.table.relational_properties || e.table.columns || e.columns; + if (!columns) { + console.error("No columns found in table structure:", e.table); + return; + } + parseTableColumns(table, columns); + tables.push(table); + } + // Handle standard parser format (fallback) + else if (e.type === "create" && e.keyword === "table") { + const table = {}; + table.name = e.table[0].table; + table.comment = ""; + table.color = "#175e7a"; + table.fields = []; + table.indices = []; + table.id = tables.length; + if (e.create_definitions) { + parseStandardTableDefinitions(table, e.create_definitions); + } + tables.push(table); + } + }; + + const parseTableColumns = (table, columns) => { + columns.forEach((d) => { + if (d.resource === "column" || d.type) { + const field = {}; + field.name = d.name; + + // Handle different type structures + let typeInfo = d.type || d; + let type = typeInfo.type?.toUpperCase() || typeInfo.dataType?.toUpperCase() || "VARCHAR2"; + if (!dbToTypes[diagramDb][type]) { + type = affinity[diagramDb][type] || type; + } + field.type = type; + + // Handle precision and scale + if (typeInfo.scale && typeInfo.precision) { + field.size = typeInfo.precision + "," + typeInfo.scale; + } else if (typeInfo.size || typeInfo.precision) { + field.size = typeInfo.size || typeInfo.precision; + } + + field.comment = ""; + field.check = ""; + field.default = ""; + field.unique = false; + field.increment = false; + field.notNull = false; + field.primary = false; + + // Handle constraints - check if they exist + const constraints = d.constraints || []; + for (const c of constraints) { + if (c.constraint.primary_key === "primary key") + field.primary = true; + if (c.constraint.not_null === "not null") field.notNull = true; + if (c.constraint.unique === "unique") field.unique = true; + } + + if (d.identity) { + field.increment = true; + } + + // TODO: reconstruct default when implemented in parser + if (d.default) { + field.default = JSON.stringify(d.default.expr); + } + + table.fields.push(field); + } else if (d.resource === "constraint") { + parseConstraint(table, d); + } + }); + }; + + const parseStandardTableDefinitions = (table, definitions) => { + definitions.forEach((d) => { + if (d.resource === "column") { + const field = {}; + field.name = d.column.column; + + let type = d.definition.dataType; + if (!dbToTypes[diagramDb][type]) { + type = affinity[diagramDb][type] || type; + } + field.type = type; + + field.comment = d.comment ? d.comment.value.value : ""; + field.check = ""; + field.default = d.default_val ? d.default_val.value.value : ""; + field.unique = false; + field.increment = false; + field.notNull = false; + field.primary = false; + + // Handle size/precision + if (d.definition.length) { + if (Array.isArray(d.definition.length)) { + // Handle precision and scale: [precision, scale] + field.size = d.definition.length.join(","); + } else { + field.size = d.definition.length; + } + } + + // Handle NOT NULL constraint + if (d.nullable && d.nullable.value === "not null") { + field.notNull = true; + } + + table.fields.push(field); + } else if (d.resource === "constraint") { + // Handle table-level constraints + console.log("Processing table constraint:", d); + console.log("Constraint type:", d.constraint_type); + console.log("Full constraint object:", JSON.stringify(d, null, 2)); + if (d.constraint_type === "primary key") { + console.log("Found PRIMARY KEY constraint:", d.definition); + if (d.definition && Array.isArray(d.definition)) { + d.definition.forEach(colRef => { + const columnName = colRef.column; + console.log("Setting primary key for column:", columnName); + const field = table.fields.find(f => f.name === columnName); + if (field) { field.primary = true; - if (c.constraint.not_null === "not null") field.notNull = true; - if (c.constraint.unique === "unique") field.unique = true; - } - - if (d.identity) { - field.increment = true; - } - - // TODO: reconstruct default when implemented in parser - if (d.default) { - field.default = JSON.stringify(d.default.expr); - } - - table.fields.push(field); - } else if (d.resource === "constraint") { - const relationship = {}; - const startTableId = table.id; - const startField = d.constraint.columns[0]; - const endField = d.constraint.reference.columns[0]; - const endTable = d.constraint.reference.object.name; - - 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.startTableId = startTableId; - relationship.startFieldId = startFieldId; - relationship.endTableId = endTableId; - relationship.endFieldId = endFieldId; - relationship.updateConstraint = Constraint.NONE; - relationship.name = - d.name && Boolean(d.name.trim()) - ? d.name - : "fk_" + table.name + "_" + startField + "_" + endTable; - relationship.deleteConstraint = - d.constraint.reference.on_delete && - Boolean(d.constraint.reference.on_delete.trim()) - ? d.constraint.reference.on_delete[0].toUpperCase() + - d.constraint.reference.on_delete.substring(1) - : Constraint.NONE; - - if (table.fields[startFieldId].unique) { - relationship.cardinality = Cardinality.ONE_TO_ONE; - } else { - relationship.cardinality = Cardinality.MANY_TO_ONE; - } - - relationships.push(relationship); + console.log("Successfully set primary key for field:", field.name); + } else { + console.warn("Could not find field for primary key:", columnName); + } + }); + } + } + if (d.constraint_type === "FOREIGN KEY" || d.constraint_type === "foreign key") { + // Handle foreign key constraints + console.log("Found FOREIGN KEY constraint:", d); + console.log("Definition:", d.definition); + console.log("Reference definition:", d.reference_definition); + // Extract foreign key information + const sourceColumn = d.definition[0]?.column; + const targetTable = d.reference_definition?.table[0]?.table; + const targetColumn = d.reference_definition?.definition[0]?.column; + console.log(`Creating foreign key: ${sourceColumn} -> ${targetTable}.${targetColumn}`); + // Store foreign key for later processing + if (!table.foreignKeys) table.foreignKeys = []; + table.foreignKeys.push({ + sourceColumn, + targetTable, + targetColumn, + constraintName: d.constraint + }); + } + } + }); + // Set field IDs + table.fields.forEach((f, j) => { + f.id = j; + }); + }; + + const parseConstraint = (table, d) => { + console.log("Processing constraint:", d); + console.log("Constraint details:", d.constraint); + // Handle PRIMARY KEY constraints + if (d.constraint.primary_key) { + console.log("Found PRIMARY KEY constraint:", d.constraint); + if (d.constraint.columns && Array.isArray(d.constraint.columns)) { + d.constraint.columns.forEach(columnName => { + console.log("Setting primary key for column:", columnName); + const field = table.fields.find(f => f.name === columnName); + if (field) { + field.primary = true; + console.log("Successfully set primary key for field:", field.name); + } else { + console.warn("Could not find field for primary key:", columnName); } }); - table.fields.forEach((f, j) => { - f.id = j; - }); - tables.push(table); } } + // Check if this is a foreign key constraint + if (d.constraint.reference) { + const relationship = {}; + const startTableId = table.id; + // Validate structure before accessing + if (!d.constraint.columns || !d.constraint.reference.columns) { + console.warn("Invalid constraint structure - missing columns:", d.constraint); + return; + } + const startField = d.constraint.columns[0]; + const endField = d.constraint.reference.columns[0]; + const endTable = d.constraint.reference.object.name; + + 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.startTableId = startTableId; + relationship.startFieldId = startFieldId; + relationship.endTableId = endTableId; + relationship.endFieldId = endFieldId; + relationship.updateConstraint = Constraint.NONE; + relationship.name = + d.name && Boolean(d.name.trim()) + ? d.name + : "fk_" + table.name + "_" + startField + "_" + endTable; + relationship.deleteConstraint = + d.constraint.reference.on_delete && + Boolean(d.constraint.reference.on_delete.trim()) + ? d.constraint.reference.on_delete[0].toUpperCase() + + d.constraint.reference.on_delete.substring(1) + : Constraint.NONE; + + relationships.push(relationship); + } }; ast.forEach((e) => parseSingleStatement(e)); + // Process stored foreign keys after all tables are created + console.log("Processing stored foreign keys..."); + tables.forEach((table, tableIndex) => { + console.log(`Checking table ${table.name} (index ${tableIndex}) for foreign keys...`); + if (table.foreignKeys && table.foreignKeys.length > 0) { + console.log(`Found ${table.foreignKeys.length} foreign keys in table ${table.name}`); + table.foreignKeys.forEach((fk, fkIndex) => { + console.log(`Processing foreign key ${fkIndex + 1}:`, JSON.stringify(fk, null, 2)); + const relationship = {}; + const startField = fk.sourceColumn; + const endTable = fk.targetTable; + const endField = fk.targetColumn; + console.log(`Creating relationship: ${table.name}.${startField} -> ${endTable}.${endField}`); + const endTableId = tables.findIndex((t) => t.name === endTable); + if (endTableId === -1) { + console.warn("Could not find referenced table:", endTable); + return; + } + + const endFieldId = tables[endTableId].fields.findIndex( + (f) => f.name === endField, + ); + if (endFieldId === -1) { + console.warn("Could not find referenced field:", endField); + return; + } + + const startFieldId = table.fields.findIndex( + (f) => f.name === startField, + ); + if (startFieldId === -1) { + console.warn("Could not find source field:", startField); + return; + } + + relationship.id = relationships.length; + relationship.startTableId = table.id; + relationship.startFieldId = startFieldId; + relationship.endTableId = endTableId; + relationship.endFieldId = endFieldId; + relationship.updateConstraint = Constraint.NONE; + relationship.name = fk.constraintName || + "fk_" + table.name + "_" + startField + "_" + endTable; + relationship.deleteConstraint = Constraint.NONE; + + console.log("Created relationship:", relationship); + relationships.push(relationship); + }); + } else { + console.log(`No foreign keys found in table ${table.name}`); + } + }); + relationships.forEach((r, i) => (r.id = i)); return { tables, relationships, enums }; diff --git a/test/oracle__u_idx.test.js b/test/oracle__u_idx.test.js index e7c3895dd..6ded4dd1f 100644 --- a/test/oracle__u_idx.test.js +++ b/test/oracle__u_idx.test.js @@ -1,8 +1,8 @@ /* eslint-env jest */ -import { toOracle } from "../src/utils/exportSQL/oracle.js"; +import { toOracleSQL } from "../src/utils/exportSQL/oraclesql.js"; import { DB } from "../src/data/constants.js"; -describe("toOracle", () => { +describe("toOracleSQL", () => { test("test for unique index creation", () => { const diagram = { database: DB.ORACLE, @@ -39,16 +39,16 @@ describe("toOracle", () => { }; const expectedSQL = `CREATE TABLE mochila ( -\t"mochila_id" NUMBER(10,0) NOT NULL, -\t"capacidad_kg" NUMBER(2,0) NOT NULL, -\tCONSTRAINT mochila_pk PRIMARY KEY("mochila_id") +\tmochila_id NUMBER(10,0) NOT NULL, +\tcapacidad_kg NUMBER(2,0) NOT NULL, +\tCONSTRAINT mochila_pk PRIMARY KEY(mochila_id) ); -CREATE UNIQUE INDEX "mochila_index_0" ON mochila ("mochila_id"); -`; +CREATE UNIQUE INDEX mochila_index_0 ON mochila (mochila_id); +`; - const result = toOracle(diagram); + const result = toOracleSQL(diagram); expect(result.trim()).toBe(expectedSQL.trim()); }); }); diff --git a/test/oracle_c_idx.test.js b/test/oracle_c_idx.test.js index 5815fe1c8..3d6118780 100644 --- a/test/oracle_c_idx.test.js +++ b/test/oracle_c_idx.test.js @@ -1,8 +1,8 @@ /* eslint-env jest */ -import { toOracle } from "../src/utils/exportSQL/oracle.js"; +import { toOracleSQL } from "../src/utils/exportSQL/oraclesql.js"; import { DB } from "../src/data/constants.js"; -describe("toOracle", () => { +describe("toOracleSQL", () => { test("test for unique index creation", () => { const diagram = { database: DB.ORACLE, @@ -46,17 +46,17 @@ describe("toOracle", () => { }; const expectedSQL = `CREATE TABLE mochila ( -\t"mochila_id" NUMBER(10,0) NOT NULL, -\t"capacidad_kg" NUMBER(2,0) NOT NULL, -\t"compartimentos" NUMBER(2,0) NOT NULL, -\tCONSTRAINT mochila_pk PRIMARY KEY("mochila_id") +\tmochila_id NUMBER(10,0) NOT NULL, +\tcapacidad_kg NUMBER(2,0) NOT NULL, +\tcompartimentos NUMBER(2,0) NOT NULL, +\tCONSTRAINT mochila_pk PRIMARY KEY(mochila_id) ); -CREATE UNIQUE INDEX "mochila_index_0" ON mochila ("capacidad_kg", "compartimentos"); -`; +CREATE UNIQUE INDEX mochila_index_0 ON mochila (capacidad_kg, compartimentos); +`; - const result = toOracle(diagram); + const result = toOracleSQL(diagram); expect(result.trim()).toBe(expectedSQL.trim()); }); }); diff --git a/test/oracle_chk.test.js b/test/oracle_chk.test.js index c9e05a99c..6cfcbc2ff 100644 --- a/test/oracle_chk.test.js +++ b/test/oracle_chk.test.js @@ -1,8 +1,8 @@ /* eslint-env jest */ -import { toOracle } from "../src/utils/exportSQL/oracle.js"; +import { toOracleSQL } from "../src/utils/exportSQL/oraclesql.js"; import { DB } from "../src/data/constants.js"; -describe("toOracle", () => { +describe("toOracleSQL", () => { test("test for check constraint in any field of a table", () => { const diagram = { database: DB.ORACLE, @@ -34,13 +34,13 @@ describe("toOracle", () => { }; const expectedSQL = `CREATE TABLE salon ( -\t"salon_id" NUMBER(10,0) NOT NULL, -\t"capacidad" NUMBER(10,0) NOT NULL, -\tCONSTRAINT salon_capacidad_chk CHECK("capacidad" > 0), -\tCONSTRAINT salon_pk PRIMARY KEY("salon_id") -);`; +\tsalon_id NUMBER(10,0) NOT NULL, +\tcapacidad NUMBER(10,0) NOT NULL, +\tCONSTRAINT salon_capacidad_chk CHECK(capacidad > 0), +\tCONSTRAINT salon_pk PRIMARY KEY(salon_id) +);`; - const result = toOracle(diagram); + const result = toOracleSQL(diagram); expect(result.trim()).toBe(expectedSQL.trim()); }); }); diff --git a/test/oracle_export.test.js b/test/oracle_export.test.js index e631a8d20..5ec65e3cd 100644 --- a/test/oracle_export.test.js +++ b/test/oracle_export.test.js @@ -1,8 +1,8 @@ /* eslint-env jest */ -import { toOracle } from "../src/utils/exportSQL/oracle.js"; +import { toOracleSQL } from "../src/utils/exportSQL/oraclesql.js"; import { DB } from "../src/data/constants.js"; -describe("toOracle", () => { +describe("toOracleSQL", () => { test("should generate correct Oracle SQL for tables with relationships", () => { const diagram = { database: DB.ORACLE, @@ -54,22 +54,22 @@ describe("toOracle", () => { }; const expectedSQL = `CREATE TABLE casa ( -\t"id" NUMBER(10,0) NOT NULL, -\t"xd" VARCHAR(255) NOT NULL, -\tCONSTRAINT casa_pk PRIMARY KEY("id") +\tid NUMBER(10,0) NOT NULL, +\txd VARCHAR(255) NOT NULL, +\tCONSTRAINT casa_pk PRIMARY KEY(id) ); CREATE TABLE cuarto ( -\t"id" NUMBER, -\tCONSTRAINT cuarto_pk PRIMARY KEY("id") +\tid NUMBER, +\tCONSTRAINT cuarto_pk PRIMARY KEY(id) ); ALTER TABLE casa ADD CONSTRAINT casa_id_fk -FOREIGN KEY("id") REFERENCES cuarto("id");`; +FOREIGN KEY(id) REFERENCES cuarto(id);`; - const result = toOracle(diagram); + const result = toOracleSQL(diagram); expect(result.trim()).toBe(expectedSQL.trim()); }); -}); \ No newline at end of file +}); \ No newline at end of file diff --git a/test/oracle_pk_fk.test.js b/test/oracle_pk_fk.test.js index e631a8d20..5ec65e3cd 100644 --- a/test/oracle_pk_fk.test.js +++ b/test/oracle_pk_fk.test.js @@ -1,8 +1,8 @@ /* eslint-env jest */ -import { toOracle } from "../src/utils/exportSQL/oracle.js"; +import { toOracleSQL } from "../src/utils/exportSQL/oraclesql.js"; import { DB } from "../src/data/constants.js"; -describe("toOracle", () => { +describe("toOracleSQL", () => { test("should generate correct Oracle SQL for tables with relationships", () => { const diagram = { database: DB.ORACLE, @@ -54,22 +54,22 @@ describe("toOracle", () => { }; const expectedSQL = `CREATE TABLE casa ( -\t"id" NUMBER(10,0) NOT NULL, -\t"xd" VARCHAR(255) NOT NULL, -\tCONSTRAINT casa_pk PRIMARY KEY("id") +\tid NUMBER(10,0) NOT NULL, +\txd VARCHAR(255) NOT NULL, +\tCONSTRAINT casa_pk PRIMARY KEY(id) ); CREATE TABLE cuarto ( -\t"id" NUMBER, -\tCONSTRAINT cuarto_pk PRIMARY KEY("id") +\tid NUMBER, +\tCONSTRAINT cuarto_pk PRIMARY KEY(id) ); ALTER TABLE casa ADD CONSTRAINT casa_id_fk -FOREIGN KEY("id") REFERENCES cuarto("id");`; +FOREIGN KEY(id) REFERENCES cuarto(id);`; - const result = toOracle(diagram); + const result = toOracleSQL(diagram); expect(result.trim()).toBe(expectedSQL.trim()); }); -}); \ No newline at end of file +}); \ No newline at end of file diff --git a/test/oracle_unique.test.js b/test/oracle_unique.test.js index 177c9c043..9d375f32d 100644 --- a/test/oracle_unique.test.js +++ b/test/oracle_unique.test.js @@ -1,8 +1,8 @@ /* eslint-env jest */ -import { toOracle } from "../src/utils/exportSQL/oracle.js"; +import { toOracleSQL } from "../src/utils/exportSQL/oraclesql.js"; import { DB } from "../src/data/constants.js"; -describe("toOracle", () => { +describe("toOracleSQL", () => { test("test for unique constraints", () => { const diagram = { database: DB.ORACLE, @@ -34,13 +34,13 @@ describe("toOracle", () => { }; const expectedSQL = `CREATE TABLE computador ( -\t"computador_id" NUMBER(10,0) NOT NULL, -\t"num_serie" VARCHAR2(40) NOT NULL, -\tCONSTRAINT computador_num_serie_uk UNIQUE("num_serie"), -\tCONSTRAINT computador_pk PRIMARY KEY("computador_id") +\tcomputador_id NUMBER(10,0) NOT NULL, +\tnum_serie VARCHAR2(40) NOT NULL, +\tCONSTRAINT computador_num_serie_uk UNIQUE(num_serie), +\tCONSTRAINT computador_pk PRIMARY KEY(computador_id) );`; - const result = toOracle(diagram); + const result = toOracleSQL(diagram); expect(result.trim()).toBe(expectedSQL.trim()); }); }); \ No newline at end of file From c26bcb217f0231c3695e35e31a5b80a90f105677 Mon Sep 17 00:00:00 2001 From: HansMarcus01 Date: Mon, 29 Sep 2025 15:02:52 -0600 Subject: [PATCH 09/10] relationships begin with the right table --- src/utils/importSQL/oraclesql.js | 46 ++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/utils/importSQL/oraclesql.js b/src/utils/importSQL/oraclesql.js index 5d3c86d29..096daaa30 100644 --- a/src/utils/importSQL/oraclesql.js +++ b/src/utils/importSQL/oraclesql.js @@ -278,40 +278,46 @@ export function fromOracleSQL(ast, diagramDb = DB.GENERIC) { table.foreignKeys.forEach((fk, fkIndex) => { console.log(`Processing foreign key ${fkIndex + 1}:`, JSON.stringify(fk, null, 2)); const relationship = {}; - const startField = fk.sourceColumn; - const endTable = fk.targetTable; - const endField = fk.targetColumn; - console.log(`Creating relationship: ${table.name}.${startField} -> ${endTable}.${endField}`); - const endTableId = tables.findIndex((t) => t.name === endTable); - if (endTableId === -1) { - console.warn("Could not find referenced table:", endTable); + // En una relación FK: + // - La tabla que contiene la PK es el START (tabla padre) + // - La tabla que contiene la FK es el END (tabla hija) + const foreignKeyField = fk.sourceColumn; // Campo FK en tabla actual + const primaryKeyTable = fk.targetTable; // Tabla que contiene la PK + const primaryKeyField = fk.targetColumn; // Campo PK en tabla referenciada + console.log(`Creating relationship: ${primaryKeyTable}.${primaryKeyField} -> ${table.name}.${foreignKeyField}`); + + // La tabla que contiene la Primary Key es el START + const startTableId = tables.findIndex((t) => t.name === primaryKeyTable); + if (startTableId === -1) { + console.warn("Could not find referenced table:", primaryKeyTable); return; } - const endFieldId = tables[endTableId].fields.findIndex( - (f) => f.name === endField, + const startFieldId = tables[startTableId].fields.findIndex( + (f) => f.name === primaryKeyField, ); - if (endFieldId === -1) { - console.warn("Could not find referenced field:", endField); + if (startFieldId === -1) { + console.warn("Could not find referenced field:", primaryKeyField); return; } - const startFieldId = table.fields.findIndex( - (f) => f.name === startField, + // La tabla que contiene la Foreign Key es el END + const endFieldId = table.fields.findIndex( + (f) => f.name === foreignKeyField, ); - if (startFieldId === -1) { - console.warn("Could not find source field:", startField); + if (endFieldId === -1) { + console.warn("Could not find source field:", foreignKeyField); return; } relationship.id = relationships.length; - relationship.startTableId = table.id; - relationship.startFieldId = startFieldId; - relationship.endTableId = endTableId; - relationship.endFieldId = endFieldId; + relationship.startTableId = startTableId; // Tabla con PK + relationship.startFieldId = startFieldId; // Campo PK + relationship.endTableId = table.id; // Tabla con FK + relationship.endFieldId = endFieldId; // Campo FK relationship.updateConstraint = Constraint.NONE; relationship.name = fk.constraintName || - "fk_" + table.name + "_" + startField + "_" + endTable; + "fk_" + primaryKeyTable + "_" + primaryKeyField + "_" + table.name; relationship.deleteConstraint = Constraint.NONE; console.log("Created relationship:", relationship); From 9a807eae73da6dc44bde1f21cc08bcd04effd31f Mon Sep 17 00:00:00 2001 From: HansMarcus01 Date: Mon, 29 Sep 2025 15:16:01 -0600 Subject: [PATCH 10/10] Remove unnecessary console.log --- src/components/EditorHeader/Modal/Modal.jsx | 9 +--- src/utils/importSQL/oraclesql.js | 48 +++++---------------- 2 files changed, 11 insertions(+), 46 deletions(-) diff --git a/src/components/EditorHeader/Modal/Modal.jsx b/src/components/EditorHeader/Modal/Modal.jsx index 2004c4c08..d8bb3d4f7 100644 --- a/src/components/EditorHeader/Modal/Modal.jsx +++ b/src/components/EditorHeader/Modal/Modal.jsx @@ -130,7 +130,7 @@ export default function Modal({ } }) .catch((error) => { - console.log(error); + console.error(error); Toast.error(t("didnt_find_diagram")); }); }; @@ -163,9 +163,7 @@ export default function Modal({ try { const oracleParser = new OracleParser(); const preprocessedSQL = preprocessOracleSQL(importSource.src); - console.log("Preprocessed SQL:", preprocessedSQL); ast = oracleParser.parse(preprocessedSQL); - console.log("Oracle parser AST:", ast); } catch (oracleError) { console.warn("Oracle parser failed, trying standard parser:", oracleError); // Fallback to standard parser with modified SQL @@ -182,7 +180,6 @@ export default function Modal({ ast = parser.astify(mysqlCompatibleSQL, { database: "mysql", }); - console.log("Standard parser AST with MySQL compatibility:", ast); } catch (standardError) { console.error("Both parsers failed:", standardError); throw new Error(`Failed to parse SQL. Oracle parser error: ${oracleError.message}. Standard parser error: ${standardError.message}`); @@ -194,7 +191,6 @@ export default function Modal({ ast = parser.astify(importSource.src, { database: targetDatabase, }); - console.log("Standard parser AST:", ast); } } catch (error) { const message = error.location @@ -206,9 +202,6 @@ export default function Modal({ } try { - console.log("About to import SQL with AST:", ast); - console.log("Target database:", database === DB.GENERIC ? importDb : database); - console.log("Current database:", database); if (!ast) { throw new Error("AST is null or undefined"); } diff --git a/src/utils/importSQL/oraclesql.js b/src/utils/importSQL/oraclesql.js index 096daaa30..deada3de8 100644 --- a/src/utils/importSQL/oraclesql.js +++ b/src/utils/importSQL/oraclesql.js @@ -24,7 +24,6 @@ export function fromOracleSQL(ast, diagramDb = DB.GENERIC) { const enums = []; const parseSingleStatement = (e) => { - console.log("Parsing statement:", e); // Handle Oracle parser format if (e.operation === "create" && e.object === "table") { const table = {}; @@ -156,19 +155,13 @@ export function fromOracleSQL(ast, diagramDb = DB.GENERIC) { table.fields.push(field); } else if (d.resource === "constraint") { // Handle table-level constraints - console.log("Processing table constraint:", d); - console.log("Constraint type:", d.constraint_type); - console.log("Full constraint object:", JSON.stringify(d, null, 2)); if (d.constraint_type === "primary key") { - console.log("Found PRIMARY KEY constraint:", d.definition); if (d.definition && Array.isArray(d.definition)) { d.definition.forEach(colRef => { const columnName = colRef.column; - console.log("Setting primary key for column:", columnName); const field = table.fields.find(f => f.name === columnName); if (field) { field.primary = true; - console.log("Successfully set primary key for field:", field.name); } else { console.warn("Could not find field for primary key:", columnName); } @@ -177,14 +170,10 @@ export function fromOracleSQL(ast, diagramDb = DB.GENERIC) { } if (d.constraint_type === "FOREIGN KEY" || d.constraint_type === "foreign key") { // Handle foreign key constraints - console.log("Found FOREIGN KEY constraint:", d); - console.log("Definition:", d.definition); - console.log("Reference definition:", d.reference_definition); // Extract foreign key information const sourceColumn = d.definition[0]?.column; const targetTable = d.reference_definition?.table[0]?.table; const targetColumn = d.reference_definition?.definition[0]?.column; - console.log(`Creating foreign key: ${sourceColumn} -> ${targetTable}.${targetColumn}`); // Store foreign key for later processing if (!table.foreignKeys) table.foreignKeys = []; table.foreignKeys.push({ @@ -203,18 +192,13 @@ export function fromOracleSQL(ast, diagramDb = DB.GENERIC) { }; const parseConstraint = (table, d) => { - console.log("Processing constraint:", d); - console.log("Constraint details:", d.constraint); // Handle PRIMARY KEY constraints if (d.constraint.primary_key) { - console.log("Found PRIMARY KEY constraint:", d.constraint); if (d.constraint.columns && Array.isArray(d.constraint.columns)) { d.constraint.columns.forEach(columnName => { - console.log("Setting primary key for column:", columnName); const field = table.fields.find(f => f.name === columnName); if (field) { field.primary = true; - console.log("Successfully set primary key for field:", field.name); } else { console.warn("Could not find field for primary key:", columnName); } @@ -270,23 +254,14 @@ export function fromOracleSQL(ast, diagramDb = DB.GENERIC) { ast.forEach((e) => parseSingleStatement(e)); // Process stored foreign keys after all tables are created - console.log("Processing stored foreign keys..."); - tables.forEach((table, tableIndex) => { - console.log(`Checking table ${table.name} (index ${tableIndex}) for foreign keys...`); + tables.forEach((table) => { if (table.foreignKeys && table.foreignKeys.length > 0) { - console.log(`Found ${table.foreignKeys.length} foreign keys in table ${table.name}`); - table.foreignKeys.forEach((fk, fkIndex) => { - console.log(`Processing foreign key ${fkIndex + 1}:`, JSON.stringify(fk, null, 2)); + table.foreignKeys.forEach((fk) => { const relationship = {}; - // En una relación FK: - // - La tabla que contiene la PK es el START (tabla padre) - // - La tabla que contiene la FK es el END (tabla hija) - const foreignKeyField = fk.sourceColumn; // Campo FK en tabla actual - const primaryKeyTable = fk.targetTable; // Tabla que contiene la PK - const primaryKeyField = fk.targetColumn; // Campo PK en tabla referenciada - console.log(`Creating relationship: ${primaryKeyTable}.${primaryKeyField} -> ${table.name}.${foreignKeyField}`); - - // La tabla que contiene la Primary Key es el START + const foreignKeyField = fk.sourceColumn; + const primaryKeyTable = fk.targetTable; + const primaryKeyField = fk.targetColumn; + // The table that contains the Primary Key is the START const startTableId = tables.findIndex((t) => t.name === primaryKeyTable); if (startTableId === -1) { console.warn("Could not find referenced table:", primaryKeyTable); @@ -311,20 +286,17 @@ export function fromOracleSQL(ast, diagramDb = DB.GENERIC) { } relationship.id = relationships.length; - relationship.startTableId = startTableId; // Tabla con PK - relationship.startFieldId = startFieldId; // Campo PK - relationship.endTableId = table.id; // Tabla con FK - relationship.endFieldId = endFieldId; // Campo FK + relationship.startTableId = startTableId; // Table with PK + relationship.startFieldId = startFieldId; // PK field + relationship.endTableId = table.id; // Table with FK + relationship.endFieldId = endFieldId; // FK field relationship.updateConstraint = Constraint.NONE; relationship.name = fk.constraintName || "fk_" + primaryKeyTable + "_" + primaryKeyField + "_" + table.name; relationship.deleteConstraint = Constraint.NONE; - console.log("Created relationship:", relationship); relationships.push(relationship); }); - } else { - console.log(`No foreign keys found in table ${table.name}`); } });