From 16ea2ee23cb089f9f804e5f49b90b02c3c04e3fa Mon Sep 17 00:00:00 2001 From: Aditya <95466063+Kingpin98k@users.noreply.github.com> Date: Wed, 2 Apr 2025 04:41:08 +0530 Subject: [PATCH] feat:Added support for exporting SQL as OracleDB SQL (#192) * feat:Added support for exporting SQL as OracleDB SQL * Add parser * Import from sql * Add beta tag * Export from generic * Fix export * Add import affinity --------- Co-authored-by: 1ilit <1ilit@proton.me> --- package-lock.json | 7 + package.json | 1 + src/assets/oraclesql-icon.png | Bin 0 -> 15121 bytes src/assets/oraclesql.png | Bin 0 -> 28695 bytes src/components/EditorHeader/ControlPanel.jsx | 113 +++++-- src/components/EditorHeader/Modal/Modal.jsx | 18 +- src/components/Workspace.jsx | 17 +- src/data/constants.js | 1 + src/data/databases.js | 10 + src/data/datatypes.js | 297 ++++++++++++++++++- src/pages/LandingPage.jsx | 4 +- src/utils/exportSQL/generic.js | 131 +++++++- src/utils/exportSQL/index.js | 3 + src/utils/exportSQL/oraclesql.js | 56 ++++ src/utils/importSQL/index.js | 4 + src/utils/importSQL/oraclesql.js | 137 +++++++++ 16 files changed, 739 insertions(+), 60 deletions(-) create mode 100644 src/assets/oraclesql-icon.png create mode 100644 src/assets/oraclesql.png create mode 100644 src/utils/exportSQL/oraclesql.js create mode 100644 src/utils/importSQL/oraclesql.js diff --git a/package-lock.json b/package-lock.json index 9e4af97..2fa0011 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "lexical": "^0.12.5", "node-sql-parser": "^5.3.8", "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", @@ -5373,6 +5374,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 fdd3389..5cbb190 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "lexical": "^0.12.5", "node-sql-parser": "^5.3.8", "octokit": "^4.0.2", + "oracle-sql-parser": "^0.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hotkeys-hook": "^4.4.1", diff --git a/src/assets/oraclesql-icon.png b/src/assets/oraclesql-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ef0f66bb7fce222cf7d47addf584f2335371db7a GIT binary patch literal 15121 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 BgA)J% 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 { + function: () => { setModal(MODAL.IMPORT); setImportFrom(IMPORT_FROM.DBML); }, + name: "DBML", }, ], }, @@ -807,34 +811,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", + }, + { + function: () => { + setModal(MODAL.IMPORT_SRC); + setImportDb(DB.ORACLESQL); + }, + name: "Oracle", + label: "Beta", }, ], }), @@ -848,7 +865,8 @@ export default function ControlPanel({ ...(database === DB.GENERIC && { children: [ { - MySQL: () => { + name: "MySQL", + function: () => { setModal(MODAL.CODE); const src = jsonToMySQL({ tables: tables, @@ -864,7 +882,8 @@ export default function ControlPanel({ }, }, { - PostgreSQL: () => { + name: "PostgreSQL", + function: () => { setModal(MODAL.CODE); const src = jsonToPostgreSQL({ tables: tables, @@ -880,7 +899,8 @@ export default function ControlPanel({ }, }, { - SQLite: () => { + name: "SQLite", + function: () => { setModal(MODAL.CODE); const src = jsonToSQLite({ tables: tables, @@ -896,7 +916,8 @@ export default function ControlPanel({ }, }, { - MariaDB: () => { + name: "MariaDB", + function: () => { setModal(MODAL.CODE); const src = jsonToMariaDB({ tables: tables, @@ -912,7 +933,8 @@ export default function ControlPanel({ }, }, { - MSSQL: () => { + name: "MSSQL", + function: () => { setModal(MODAL.CODE); const src = jsonToSQLServer({ tables: tables, @@ -927,6 +949,24 @@ export default function ControlPanel({ })); }, }, + { + label: "Beta", + name: "Oracle", + function: () => { + setModal(MODAL.CODE); + const src = jsonToOracleSQL({ + tables: tables, + references: relationships, + types: types, + database: database, + }); + setExportData((prev) => ({ + ...prev, + data: src, + extension: "sql", + })); + }, + }, ], }), function: () => { @@ -949,7 +989,8 @@ export default function ControlPanel({ export_as: { children: [ { - PNG: () => { + name: "PNG", + function: () => { toPng(document.getElementById("canvas")).then(function (dataUrl) { setExportData((prev) => ({ ...prev, @@ -961,7 +1002,8 @@ export default function ControlPanel({ }, }, { - JPEG: () => { + name: "JPEG", + function: () => { toJpeg(document.getElementById("canvas"), { quality: 0.95 }).then( function (dataUrl) { setExportData((prev) => ({ @@ -975,7 +1017,8 @@ export default function ControlPanel({ }, }, { - SVG: () => { + name: "SVG", + function: () => { const filter = (node) => node.tagName !== "i"; toSvg(document.getElementById("canvas"), { filter: filter }).then( function (dataUrl) { @@ -990,7 +1033,8 @@ export default function ControlPanel({ }, }, { - JSON: () => { + name: "JSON", + function: () => { setModal(MODAL.CODE); const result = JSON.stringify( { @@ -1014,7 +1058,8 @@ export default function ControlPanel({ }, }, { - DBML: () => { + name: "DBML", + function: () => { setModal(MODAL.CODE); const result = toDBML({ tables, @@ -1029,7 +1074,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", [ @@ -1049,7 +1095,8 @@ export default function ControlPanel({ }, }, { - MERMAID: () => { + name: "Mermaid", + function: () => { setModal(MODAL.CODE); const result = jsonToMermaid({ tables: tables, @@ -1067,7 +1114,8 @@ export default function ControlPanel({ }, }, { - readme: () => { + name: "Markdown", + function: () => { setModal(MODAL.CODE); const result = jsonToDocumentation({ tables: tables, @@ -1271,7 +1319,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"); @@ -1281,7 +1330,8 @@ export default function ControlPanel({ }, }, { - dark: () => { + name: t("dark"), + function: () => { const body = document.body; if (body.hasAttribute("theme-mode")) { body.setAttribute("theme-mode", "dark"); @@ -1602,9 +1652,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(); } } }} @@ -1703,7 +1753,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/EditorHeader/Modal/Modal.jsx b/src/components/EditorHeader/Modal/Modal.jsx index 270172e..34cb712 100644 --- a/src/components/EditorHeader/Modal/Modal.jsx +++ b/src/components/EditorHeader/Modal/Modal.jsx @@ -20,6 +20,7 @@ import { } from "../../../hooks"; import { saveAs } from "file-saver"; import { Parser } from "node-sql-parser"; +import { Parser as OracleParser } from "oracle-sql-parser"; import { getModalTitle, getModalWidth, @@ -131,12 +132,21 @@ export default function Modal({ }; const parseSQLAndLoadDiagram = () => { - const parser = new Parser(); + const targetDatabase = database === DB.GENERIC ? importDb : database; + let ast = null; try { - ast = parser.astify(importSource.src, { - database: database === DB.GENERIC ? importDb : database, - }); + if (targetDatabase === DB.ORACLESQL) { + const oracleParser = new OracleParser(); + + ast = oracleParser.parse(importSource.src); + } else { + const parser = new Parser(); + + ast = parser.astify(importSource.src, { + database: targetDatabase, + }); + } } catch (error) { const message = error.location ? `${error.name} [Ln ${error.location.start.line}, Col ${error.location.start.column}]: ${error.message}` diff --git a/src/components/Workspace.jsx b/src/components/Workspace.jsx index 1797851..faab921 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"; @@ -161,7 +161,7 @@ export default function WorkSpace() { enums, gistId, loadedFromGistId, - saveState + saveState, ]); const load = useCallback(async () => { @@ -467,17 +467,24 @@ export default function WorkSpace() {
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" } ${selectedDb === x.label ? "border-zinc-400" : "border-transparent"}`} > -
{x.name}
+
+
{x.name}
+ {x.beta && ( + + Beta + + )} +
{x.image && ( { + return /^-?\d+(\.\d+)?$/.test(field.default); + }, + hasCheck: true, + isSized: false, + hasPrecision: true, + canIncrement: false, + }, FLOAT: { type: "FLOAT", checkDefault: (field) => { @@ -110,6 +120,20 @@ const defaultTypesBase = { defaultSize: 255, hasQuotes: true, }, + VARCHAR2: { + type: "VARCHAR2", + checkDefault: (field) => { + if (strHasQuotes(field.default)) { + return field.default.length - 2 <= field.size; + } + return field.default.length <= field.size; + }, + hasCheck: true, + isSized: true, + hasPrecision: false, + defaultSize: 225, + hasQuotes: true, + }, TEXT: { type: "TEXT", checkDefault: (field) => true, @@ -140,7 +164,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, @@ -223,6 +249,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, @@ -405,7 +447,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, @@ -913,8 +957,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, @@ -939,7 +984,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()) ); }, @@ -1118,7 +1164,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; } @@ -1128,7 +1178,7 @@ const postgresTypesBase = { hasPrecision: false, hasQuotes: true, }, - HALFVEC:{ + HALFVEC: { type: "HALFVEC", checkDefault: (field) => { let elements; @@ -1138,7 +1188,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; } @@ -1155,9 +1209,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, @@ -1320,7 +1374,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, @@ -1581,7 +1637,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, @@ -1747,6 +1805,218 @@ export const mssqlTypes = new Proxy(mssqlTypesBase, { get: (target, prop) => (prop in target ? target[prop] : false), }); +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) => { + return /^-?\d+(\.\d+)?$/.test(field.default); + }, + hasCheck: true, + isSized: false, + 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) => { + 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, + }, + 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) => { + 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, + }, + 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, + 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, + }, + 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) => { + 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, + }, + BOOLEAN: { + type: "BOOLEAN", + checkDefault: (field) => { + return ( + field.default === "0" || + field.default === "1" || + field.default.toUpperCase() === "TRUE" || + field.default.toUpperCase() === "FALSE" + ); + }, + hasCheck: false, + isSized: false, + hasPrecision: false, + }, + RAW: { + type: "RAW", + checkDefault: (field) => { + return /^[0-9A-Fa-f]+$/.test(field.default); + }, + hasCheck: false, + isSized: true, + hasPrecision: false, + defaultSize: 2000, + hasQuotes: false, + }, +}; + +export const oraclesqlTypes = new Proxy(oraclesqlTypesBase, { + get: (target, prop) => (prop in target ? target[prop] : false), +}); + const dbToTypesBase = { [DB.GENERIC]: defaultTypes, [DB.MYSQL]: mysqlTypes, @@ -1754,6 +2024,7 @@ const dbToTypesBase = { [DB.SQLITE]: sqliteTypes, [DB.MSSQL]: mssqlTypes, [DB.MARIADB]: mysqlTypes, + [DB.ORACLESQL]: oraclesqlTypes, }; export const dbToTypes = new Proxy(dbToTypesBase, { diff --git a/src/pages/LandingPage.jsx b/src/pages/LandingPage.jsx index 5bda06e..700ff36 100644 --- a/src/pages/LandingPage.jsx +++ b/src/pages/LandingPage.jsx @@ -8,6 +8,7 @@ import mysql_icon from "../assets/mysql.png"; import postgres_icon from "../assets/postgres.png"; import sqlite_icon from "../assets/sqlite.png"; import mariadb_icon from "../assets/mariadb.png"; +import oraclesql_icon from "../assets/oraclesql.png"; import sql_server_icon from "../assets/sql-server.png"; import discord from "../assets/discord.png"; import github from "../assets/github.png"; @@ -150,7 +151,7 @@ export default function LandingPage() {
Design for your database
-
+
{dbs.map((s, i) => ( `\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")}`) + .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` @@ -247,7 +291,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)}` : "" @@ -377,7 +421,7 @@ export function jsonToMariaDB(obj) { (field) => `\t\`${ field.name - }\` ${getTypeString(field, obj.database)}${field.notNull ? " NOT NULL" : ""}${ + }\` ${getTypeString(field, obj.database, DB.MYSQL)}${field.notNull ? " NOT NULL" : ""}${ field.increment ? " AUTO_INCREMENT" : "" }${field.unique ? " UNIQUE" : ""}${ field.default !== "" @@ -436,7 +480,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 @@ -449,7 +493,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" : "" @@ -493,3 +537,70 @@ export function jsonToSQLServer(obj) { ) .join("\n")}`; } + +export function jsonToOracleSQL(obj) { + return `${obj.tables + .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, DB.ORACLESQL)}${ + field.notNull ? " NOT NULL" : "" + }${field.increment ? " GENERATED ALWAYS AS IDENTITY" : ""}${ + field.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 "${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 + }");`, + ) + .join("\n")}`; +} diff --git a/src/utils/exportSQL/index.js b/src/utils/exportSQL/index.js index ad877da..987fb26 100644 --- a/src/utils/exportSQL/index.js +++ b/src/utils/exportSQL/index.js @@ -2,6 +2,7 @@ import { DB } from "../../data/constants"; import { toMariaDB } from "./mariadb"; import { toMSSQL } from "./mssql"; import { toMySQL } from "./mysql"; +import { toOracleSQL } from "./oraclesql"; import { toPostgres } from "./postgres"; import { toSqlite } from "./sqlite"; @@ -17,6 +18,8 @@ export function exportSQL(diagram) { return toMariaDB(diagram); case DB.MSSQL: return toMSSQL(diagram); + case DB.ORACLESQL: + return toOracleSQL(diagram); default: return ""; } diff --git a/src/utils/exportSQL/oraclesql.js b/src/utils/exportSQL/oraclesql.js new file mode 100644 index 0000000..81d8081 --- /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 && Boolean(field.size.trim()) ? "(" + field.size + ")" : ""}${ + field.notNull ? " NOT NULL" : "" + }${ + field.increment ? " GENERATED ALWAYS AS IDENTITY" : "" + }${field.unique ? " UNIQUE" : ""}${ + field.default !== "" + ? ` DEFAULT ${parseDefault(field, diagram.database)}` + : "" + }${ + field.check === "" || + !dbToTypes[diagram.database][field.type].hasCheck + ? "" + : ` CHECK(${field.check})` + }${field.comment ? ` -- ${field.comment}` : ""}`, + ) + .join(",\n")}${ + table.fields.filter((f) => f.primary).length > 0 + ? `,\n\tPRIMARY KEY(${table.fields + .filter((f) => f.primary) + .map((f) => `"${f.name}"`) + .join(", ")})` + : "" + }\n)${table.comment ? ` -- ${table.comment}` : ""};\n${`\n${table.indices + .map( + (i) => + `\nCREATE ${i.unique ? "UNIQUE " : ""}INDEX "${i.name}"\nON "${table.name}" (${i.fields + .map((f) => `"${f}"`) + .join(", ")});`, + ) + .join("")}`}`, + ) + .join("\n")}\n${diagram.references + .map( + (r) => + `ALTER TABLE "${diagram.tables[r.startTableId].name}"\nADD CONSTRAINT "${r.name}" FOREIGN KEY ("${ + diagram.tables[r.startTableId].fields[r.startFieldId].name + }") REFERENCES "${diagram.tables[r.endTableId].name}" ("${ + diagram.tables[r.endTableId].fields[r.endFieldId].name + }")\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};`, + ) + .join("\n")}`; +} diff --git a/src/utils/importSQL/index.js b/src/utils/importSQL/index.js index fb849e4..e92ea72 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 0000000..790f0e2 --- /dev/null +++ b/src/utils/importSQL/oraclesql.js @@ -0,0 +1,137 @@ +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 = []; + + 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") + 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); + } + }); + table.fields.forEach((f, j) => { + f.id = j; + }); + tables.push(table); + } + } + }; + + ast.forEach((e) => parseSingleStatement(e)); + + relationships.forEach((r, i) => (r.id = i)); + + return { tables, relationships, enums }; +}