mirror of
https://github.com/drawdb-io/drawdb.git
synced 2025-05-24 02:09:17 +00:00
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>
This commit is contained in:
parent
fcc16bbe85
commit
16ea2ee23c
7
package-lock.json
generated
7
package-lock.json
generated
@ -32,6 +32,7 @@
|
|||||||
"lexical": "^0.12.5",
|
"lexical": "^0.12.5",
|
||||||
"node-sql-parser": "^5.3.8",
|
"node-sql-parser": "^5.3.8",
|
||||||
"octokit": "^4.0.2",
|
"octokit": "^4.0.2",
|
||||||
|
"oracle-sql-parser": "^0.1.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hotkeys-hook": "^4.4.1",
|
"react-hotkeys-hook": "^4.4.1",
|
||||||
@ -5373,6 +5374,12 @@
|
|||||||
"node": ">= 0.8.0"
|
"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": {
|
"node_modules/p-limit": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
"lexical": "^0.12.5",
|
"lexical": "^0.12.5",
|
||||||
"node-sql-parser": "^5.3.8",
|
"node-sql-parser": "^5.3.8",
|
||||||
"octokit": "^4.0.2",
|
"octokit": "^4.0.2",
|
||||||
|
"oracle-sql-parser": "^0.1.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hotkeys-hook": "^4.4.1",
|
"react-hotkeys-hook": "^4.4.1",
|
||||||
|
BIN
src/assets/oraclesql-icon.png
Normal file
BIN
src/assets/oraclesql-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
src/assets/oraclesql.png
Normal file
BIN
src/assets/oraclesql.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
@ -20,6 +20,7 @@ import {
|
|||||||
InputNumber,
|
InputNumber,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Spin,
|
Spin,
|
||||||
|
Tag,
|
||||||
Toast,
|
Toast,
|
||||||
Popconfirm,
|
Popconfirm,
|
||||||
} from "@douyinfe/semi-ui";
|
} from "@douyinfe/semi-ui";
|
||||||
@ -30,6 +31,7 @@ import {
|
|||||||
jsonToSQLite,
|
jsonToSQLite,
|
||||||
jsonToMariaDB,
|
jsonToMariaDB,
|
||||||
jsonToSQLServer,
|
jsonToSQLServer,
|
||||||
|
jsonToOracleSQL,
|
||||||
} from "../../utils/exportSQL/generic";
|
} from "../../utils/exportSQL/generic";
|
||||||
import {
|
import {
|
||||||
ObjectType,
|
ObjectType,
|
||||||
@ -793,13 +795,15 @@ export default function ControlPanel({
|
|||||||
import_from: {
|
import_from: {
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
JSON: fileImport,
|
function: fileImport,
|
||||||
|
name: "JSON",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
DBML: () => {
|
function: () => {
|
||||||
setModal(MODAL.IMPORT);
|
setModal(MODAL.IMPORT);
|
||||||
setImportFrom(IMPORT_FROM.DBML);
|
setImportFrom(IMPORT_FROM.DBML);
|
||||||
},
|
},
|
||||||
|
name: "DBML",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -807,34 +811,47 @@ export default function ControlPanel({
|
|||||||
...(database === DB.GENERIC && {
|
...(database === DB.GENERIC && {
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
MySQL: () => {
|
function: () => {
|
||||||
setModal(MODAL.IMPORT_SRC);
|
setModal(MODAL.IMPORT_SRC);
|
||||||
setImportDb(DB.MYSQL);
|
setImportDb(DB.MYSQL);
|
||||||
},
|
},
|
||||||
|
name: "MySQL",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
PostgreSQL: () => {
|
function: () => {
|
||||||
setModal(MODAL.IMPORT_SRC);
|
setModal(MODAL.IMPORT_SRC);
|
||||||
setImportDb(DB.POSTGRES);
|
setImportDb(DB.POSTGRES);
|
||||||
},
|
},
|
||||||
|
name: "PostgreSQL",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
SQLite: () => {
|
function: () => {
|
||||||
setModal(MODAL.IMPORT_SRC);
|
setModal(MODAL.IMPORT_SRC);
|
||||||
setImportDb(DB.SQLITE);
|
setImportDb(DB.SQLITE);
|
||||||
},
|
},
|
||||||
|
name: "SQLite",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
MariaDB: () => {
|
function: () => {
|
||||||
setModal(MODAL.IMPORT_SRC);
|
setModal(MODAL.IMPORT_SRC);
|
||||||
setImportDb(DB.MARIADB);
|
setImportDb(DB.MARIADB);
|
||||||
},
|
},
|
||||||
|
name: "MariaDB",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
MSSQL: () => {
|
function: () => {
|
||||||
setModal(MODAL.IMPORT_SRC);
|
setModal(MODAL.IMPORT_SRC);
|
||||||
setImportDb(DB.MSSQL);
|
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 && {
|
...(database === DB.GENERIC && {
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
MySQL: () => {
|
name: "MySQL",
|
||||||
|
function: () => {
|
||||||
setModal(MODAL.CODE);
|
setModal(MODAL.CODE);
|
||||||
const src = jsonToMySQL({
|
const src = jsonToMySQL({
|
||||||
tables: tables,
|
tables: tables,
|
||||||
@ -864,7 +882,8 @@ export default function ControlPanel({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
PostgreSQL: () => {
|
name: "PostgreSQL",
|
||||||
|
function: () => {
|
||||||
setModal(MODAL.CODE);
|
setModal(MODAL.CODE);
|
||||||
const src = jsonToPostgreSQL({
|
const src = jsonToPostgreSQL({
|
||||||
tables: tables,
|
tables: tables,
|
||||||
@ -880,7 +899,8 @@ export default function ControlPanel({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
SQLite: () => {
|
name: "SQLite",
|
||||||
|
function: () => {
|
||||||
setModal(MODAL.CODE);
|
setModal(MODAL.CODE);
|
||||||
const src = jsonToSQLite({
|
const src = jsonToSQLite({
|
||||||
tables: tables,
|
tables: tables,
|
||||||
@ -896,7 +916,8 @@ export default function ControlPanel({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
MariaDB: () => {
|
name: "MariaDB",
|
||||||
|
function: () => {
|
||||||
setModal(MODAL.CODE);
|
setModal(MODAL.CODE);
|
||||||
const src = jsonToMariaDB({
|
const src = jsonToMariaDB({
|
||||||
tables: tables,
|
tables: tables,
|
||||||
@ -912,7 +933,8 @@ export default function ControlPanel({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
MSSQL: () => {
|
name: "MSSQL",
|
||||||
|
function: () => {
|
||||||
setModal(MODAL.CODE);
|
setModal(MODAL.CODE);
|
||||||
const src = jsonToSQLServer({
|
const src = jsonToSQLServer({
|
||||||
tables: tables,
|
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: () => {
|
function: () => {
|
||||||
@ -949,7 +989,8 @@ export default function ControlPanel({
|
|||||||
export_as: {
|
export_as: {
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
PNG: () => {
|
name: "PNG",
|
||||||
|
function: () => {
|
||||||
toPng(document.getElementById("canvas")).then(function (dataUrl) {
|
toPng(document.getElementById("canvas")).then(function (dataUrl) {
|
||||||
setExportData((prev) => ({
|
setExportData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
@ -961,7 +1002,8 @@ export default function ControlPanel({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
JPEG: () => {
|
name: "JPEG",
|
||||||
|
function: () => {
|
||||||
toJpeg(document.getElementById("canvas"), { quality: 0.95 }).then(
|
toJpeg(document.getElementById("canvas"), { quality: 0.95 }).then(
|
||||||
function (dataUrl) {
|
function (dataUrl) {
|
||||||
setExportData((prev) => ({
|
setExportData((prev) => ({
|
||||||
@ -975,7 +1017,8 @@ export default function ControlPanel({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
SVG: () => {
|
name: "SVG",
|
||||||
|
function: () => {
|
||||||
const filter = (node) => node.tagName !== "i";
|
const filter = (node) => node.tagName !== "i";
|
||||||
toSvg(document.getElementById("canvas"), { filter: filter }).then(
|
toSvg(document.getElementById("canvas"), { filter: filter }).then(
|
||||||
function (dataUrl) {
|
function (dataUrl) {
|
||||||
@ -990,7 +1033,8 @@ export default function ControlPanel({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
JSON: () => {
|
name: "JSON",
|
||||||
|
function: () => {
|
||||||
setModal(MODAL.CODE);
|
setModal(MODAL.CODE);
|
||||||
const result = JSON.stringify(
|
const result = JSON.stringify(
|
||||||
{
|
{
|
||||||
@ -1014,7 +1058,8 @@ export default function ControlPanel({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
DBML: () => {
|
name: "DBML",
|
||||||
|
function: () => {
|
||||||
setModal(MODAL.CODE);
|
setModal(MODAL.CODE);
|
||||||
const result = toDBML({
|
const result = toDBML({
|
||||||
tables,
|
tables,
|
||||||
@ -1029,7 +1074,8 @@ export default function ControlPanel({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
PDF: () => {
|
name: "PDF",
|
||||||
|
function: () => {
|
||||||
const canvas = document.getElementById("canvas");
|
const canvas = document.getElementById("canvas");
|
||||||
toJpeg(canvas).then(function (dataUrl) {
|
toJpeg(canvas).then(function (dataUrl) {
|
||||||
const doc = new jsPDF("l", "px", [
|
const doc = new jsPDF("l", "px", [
|
||||||
@ -1049,7 +1095,8 @@ export default function ControlPanel({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
MERMAID: () => {
|
name: "Mermaid",
|
||||||
|
function: () => {
|
||||||
setModal(MODAL.CODE);
|
setModal(MODAL.CODE);
|
||||||
const result = jsonToMermaid({
|
const result = jsonToMermaid({
|
||||||
tables: tables,
|
tables: tables,
|
||||||
@ -1067,7 +1114,8 @@ export default function ControlPanel({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
readme: () => {
|
name: "Markdown",
|
||||||
|
function: () => {
|
||||||
setModal(MODAL.CODE);
|
setModal(MODAL.CODE);
|
||||||
const result = jsonToDocumentation({
|
const result = jsonToDocumentation({
|
||||||
tables: tables,
|
tables: tables,
|
||||||
@ -1271,7 +1319,8 @@ export default function ControlPanel({
|
|||||||
theme: {
|
theme: {
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
light: () => {
|
name: t("light"),
|
||||||
|
function: () => {
|
||||||
const body = document.body;
|
const body = document.body;
|
||||||
if (body.hasAttribute("theme-mode")) {
|
if (body.hasAttribute("theme-mode")) {
|
||||||
body.setAttribute("theme-mode", "light");
|
body.setAttribute("theme-mode", "light");
|
||||||
@ -1281,7 +1330,8 @@ export default function ControlPanel({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
dark: () => {
|
name: t("dark"),
|
||||||
|
function: () => {
|
||||||
const body = document.body;
|
const body = document.body;
|
||||||
if (body.hasAttribute("theme-mode")) {
|
if (body.hasAttribute("theme-mode")) {
|
||||||
body.setAttribute("theme-mode", "dark");
|
body.setAttribute("theme-mode", "dark");
|
||||||
@ -1602,9 +1652,9 @@ export default function ControlPanel({
|
|||||||
const body = document.body;
|
const body = document.body;
|
||||||
if (body.hasAttribute("theme-mode")) {
|
if (body.hasAttribute("theme-mode")) {
|
||||||
if (body.getAttribute("theme-mode") === "light") {
|
if (body.getAttribute("theme-mode") === "light") {
|
||||||
menu["view"]["theme"].children[1]["dark"]();
|
menu["view"]["theme"].children[1].function();
|
||||||
} else {
|
} 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) {
|
if (menu[category][item].children) {
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
style={{ width: "120px" }}
|
style={{ width: "150px" }}
|
||||||
key={item}
|
key={item}
|
||||||
position="rightTop"
|
position="rightTop"
|
||||||
render={
|
render={
|
||||||
@ -1712,9 +1762,18 @@ export default function ControlPanel({
|
|||||||
(e, i) => (
|
(e, i) => (
|
||||||
<Dropdown.Item
|
<Dropdown.Item
|
||||||
key={i}
|
key={i}
|
||||||
onClick={Object.values(e)[0]}
|
onClick={e.function}
|
||||||
|
className="flex justify-between"
|
||||||
>
|
>
|
||||||
{t(Object.keys(e)[0])}
|
<span>{e.name}</span>
|
||||||
|
{e.label && (
|
||||||
|
<Tag
|
||||||
|
size="small"
|
||||||
|
color="light-blue"
|
||||||
|
>
|
||||||
|
{e.label}
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
} from "../../../hooks";
|
} from "../../../hooks";
|
||||||
import { saveAs } from "file-saver";
|
import { saveAs } from "file-saver";
|
||||||
import { Parser } from "node-sql-parser";
|
import { Parser } from "node-sql-parser";
|
||||||
|
import { Parser as OracleParser } from "oracle-sql-parser";
|
||||||
import {
|
import {
|
||||||
getModalTitle,
|
getModalTitle,
|
||||||
getModalWidth,
|
getModalWidth,
|
||||||
@ -131,12 +132,21 @@ export default function Modal({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const parseSQLAndLoadDiagram = () => {
|
const parseSQLAndLoadDiagram = () => {
|
||||||
const parser = new Parser();
|
const targetDatabase = database === DB.GENERIC ? importDb : database;
|
||||||
|
|
||||||
let ast = null;
|
let ast = null;
|
||||||
try {
|
try {
|
||||||
ast = parser.astify(importSource.src, {
|
if (targetDatabase === DB.ORACLESQL) {
|
||||||
database: database === DB.GENERIC ? importDb : database,
|
const oracleParser = new OracleParser();
|
||||||
});
|
|
||||||
|
ast = oracleParser.parse(importSource.src);
|
||||||
|
} else {
|
||||||
|
const parser = new Parser();
|
||||||
|
|
||||||
|
ast = parser.astify(importSource.src, {
|
||||||
|
database: targetDatabase,
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error.location
|
const message = error.location
|
||||||
? `${error.name} [Ln ${error.location.start.line}, Col ${error.location.start.column}]: ${error.message}`
|
? `${error.name} [Ln ${error.location.start.line}, Col ${error.location.start.column}]: ${error.message}`
|
||||||
|
@ -19,7 +19,7 @@ import {
|
|||||||
useEnums,
|
useEnums,
|
||||||
} from "../hooks";
|
} from "../hooks";
|
||||||
import FloatingControls from "./FloatingControls";
|
import FloatingControls from "./FloatingControls";
|
||||||
import { Modal } from "@douyinfe/semi-ui";
|
import { Modal, Tag } from "@douyinfe/semi-ui";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { databases } from "../data/databases";
|
import { databases } from "../data/databases";
|
||||||
import { isRtl } from "../i18n/utils/rtl";
|
import { isRtl } from "../i18n/utils/rtl";
|
||||||
@ -161,7 +161,7 @@ export default function WorkSpace() {
|
|||||||
enums,
|
enums,
|
||||||
gistId,
|
gistId,
|
||||||
loadedFromGistId,
|
loadedFromGistId,
|
||||||
saveState
|
saveState,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
@ -467,17 +467,24 @@ export default function WorkSpace() {
|
|||||||
<div
|
<div
|
||||||
key={x.name}
|
key={x.name}
|
||||||
onClick={() => setSelectedDb(x.label)}
|
onClick={() => 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"
|
settings.mode === "dark"
|
||||||
? "bg-zinc-700 hover:bg-zinc-600"
|
? "bg-zinc-700 hover:bg-zinc-600"
|
||||||
: "bg-zinc-100 hover:bg-zinc-200"
|
: "bg-zinc-100 hover:bg-zinc-200"
|
||||||
} ${selectedDb === x.label ? "border-zinc-400" : "border-transparent"}`}
|
} ${selectedDb === x.label ? "border-zinc-400" : "border-transparent"}`}
|
||||||
>
|
>
|
||||||
<div className="font-semibold">{x.name}</div>
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="font-semibold">{x.name}</div>
|
||||||
|
{x.beta && (
|
||||||
|
<Tag size="small" color="light-blue">
|
||||||
|
Beta
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{x.image && (
|
{x.image && (
|
||||||
<img
|
<img
|
||||||
src={x.image}
|
src={x.image}
|
||||||
className="h-10"
|
className="h-8"
|
||||||
style={{
|
style={{
|
||||||
filter:
|
filter:
|
||||||
"opacity(0.4) drop-shadow(0 0 0 white) drop-shadow(0 0 0 white)",
|
"opacity(0.4) drop-shadow(0 0 0 white) drop-shadow(0 0 0 white)",
|
||||||
|
@ -113,6 +113,7 @@ export const DB = {
|
|||||||
MSSQL: "transactsql",
|
MSSQL: "transactsql",
|
||||||
SQLITE: "sqlite",
|
SQLITE: "sqlite",
|
||||||
MARIADB: "mariadb",
|
MARIADB: "mariadb",
|
||||||
|
ORACLESQL: "oraclesql",
|
||||||
GENERIC: "generic",
|
GENERIC: "generic",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import postgresImage from "../assets/postgres-icon.png";
|
|||||||
import sqliteImage from "../assets/sqlite-icon.png";
|
import sqliteImage from "../assets/sqlite-icon.png";
|
||||||
import mariadbImage from "../assets/mariadb-icon.png";
|
import mariadbImage from "../assets/mariadb-icon.png";
|
||||||
import mssqlImage from "../assets/mssql-icon.png";
|
import mssqlImage from "../assets/mssql-icon.png";
|
||||||
|
import oraclesqlImage from "../assets/oraclesql-icon.png";
|
||||||
import i18n from "../i18n/i18n";
|
import i18n from "../i18n/i18n";
|
||||||
import { DB } from "./constants";
|
import { DB } from "./constants";
|
||||||
|
|
||||||
@ -42,6 +43,15 @@ export const databases = new Proxy(
|
|||||||
image: mssqlImage,
|
image: mssqlImage,
|
||||||
hasTypes: false,
|
hasTypes: false,
|
||||||
},
|
},
|
||||||
|
[DB.ORACLESQL]: {
|
||||||
|
name: "Oracle SQL",
|
||||||
|
label: DB.ORACLESQL,
|
||||||
|
image: oraclesqlImage,
|
||||||
|
hasTypes: false,
|
||||||
|
hasEnums: false,
|
||||||
|
hasArrays: false,
|
||||||
|
beta: true,
|
||||||
|
},
|
||||||
[DB.GENERIC]: {
|
[DB.GENERIC]: {
|
||||||
name: i18n.t("generic"),
|
name: i18n.t("generic"),
|
||||||
label: DB.GENERIC,
|
label: DB.GENERIC,
|
||||||
|
@ -55,6 +55,16 @@ const defaultTypesBase = {
|
|||||||
isSized: false,
|
isSized: false,
|
||||||
hasPrecision: true,
|
hasPrecision: true,
|
||||||
},
|
},
|
||||||
|
NUMBER: {
|
||||||
|
type: "NUMBER",
|
||||||
|
checkDefault: (field) => {
|
||||||
|
return /^-?\d+(\.\d+)?$/.test(field.default);
|
||||||
|
},
|
||||||
|
hasCheck: true,
|
||||||
|
isSized: false,
|
||||||
|
hasPrecision: true,
|
||||||
|
canIncrement: false,
|
||||||
|
},
|
||||||
FLOAT: {
|
FLOAT: {
|
||||||
type: "FLOAT",
|
type: "FLOAT",
|
||||||
checkDefault: (field) => {
|
checkDefault: (field) => {
|
||||||
@ -110,6 +120,20 @@ const defaultTypesBase = {
|
|||||||
defaultSize: 255,
|
defaultSize: 255,
|
||||||
hasQuotes: true,
|
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: {
|
TEXT: {
|
||||||
type: "TEXT",
|
type: "TEXT",
|
||||||
checkDefault: (field) => true,
|
checkDefault: (field) => true,
|
||||||
@ -140,7 +164,9 @@ const defaultTypesBase = {
|
|||||||
}
|
}
|
||||||
const content = field.default.split(" ");
|
const content = field.default.split(" ");
|
||||||
const date = content[0].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,
|
hasCheck: false,
|
||||||
isSized: false,
|
isSized: false,
|
||||||
@ -223,6 +249,22 @@ const defaultTypesBase = {
|
|||||||
hasPrecision: false,
|
hasPrecision: false,
|
||||||
noDefault: true,
|
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: {
|
JSON: {
|
||||||
type: "JSON",
|
type: "JSON",
|
||||||
checkDefault: (field) => true,
|
checkDefault: (field) => true,
|
||||||
@ -405,7 +447,9 @@ const mysqlTypesBase = {
|
|||||||
}
|
}
|
||||||
const content = field.default.split(" ");
|
const content = field.default.split(" ");
|
||||||
const date = content[0].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,
|
hasCheck: false,
|
||||||
isSized: false,
|
isSized: false,
|
||||||
@ -913,8 +957,9 @@ const postgresTypesBase = {
|
|||||||
checkDefault: (field) => {
|
checkDefault: (field) => {
|
||||||
const specialValues = ["now", "allballs"];
|
const specialValues = ["now", "allballs"];
|
||||||
return (
|
return (
|
||||||
/^(?:[01]?\d|2[0-3]):[0-5]?\d:[0-5]?\d([+-]\d{2}:\d{2})?$/.test(field.default) ||
|
/^(?:[01]?\d|2[0-3]):[0-5]?\d:[0-5]?\d([+-]\d{2}:\d{2})?$/.test(
|
||||||
specialValues.includes(field.default.toLowerCase())
|
field.default,
|
||||||
|
) || specialValues.includes(field.default.toLowerCase())
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
hasCheck: false,
|
hasCheck: false,
|
||||||
@ -939,7 +984,8 @@ const postgresTypesBase = {
|
|||||||
];
|
];
|
||||||
return (
|
return (
|
||||||
/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(field.default) ||
|
/^\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())
|
specialValues.includes(field.default.toLowerCase())
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -1118,7 +1164,11 @@ const postgresTypesBase = {
|
|||||||
elementsStr = field.default.slice(1, -1);
|
elementsStr = field.default.slice(1, -1);
|
||||||
}
|
}
|
||||||
elements = JSON.parse(elementsStr);
|
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) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1128,7 +1178,7 @@ const postgresTypesBase = {
|
|||||||
hasPrecision: false,
|
hasPrecision: false,
|
||||||
hasQuotes: true,
|
hasQuotes: true,
|
||||||
},
|
},
|
||||||
HALFVEC:{
|
HALFVEC: {
|
||||||
type: "HALFVEC",
|
type: "HALFVEC",
|
||||||
checkDefault: (field) => {
|
checkDefault: (field) => {
|
||||||
let elements;
|
let elements;
|
||||||
@ -1138,7 +1188,11 @@ const postgresTypesBase = {
|
|||||||
elementsStr = field.default.slice(1, -1);
|
elementsStr = field.default.slice(1, -1);
|
||||||
}
|
}
|
||||||
elements = JSON.parse(elementsStr);
|
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) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1155,9 +1209,9 @@ const postgresTypesBase = {
|
|||||||
if (strHasQuotes(field.default)) {
|
if (strHasQuotes(field.default)) {
|
||||||
elementsStr = field.default.slice(1, -1);
|
elementsStr = field.default.slice(1, -1);
|
||||||
}
|
}
|
||||||
const lengthStr = elementsStr.split('/')[1]
|
const lengthStr = elementsStr.split("/")[1];
|
||||||
const length = Number.parseInt(lengthStr)
|
const length = Number.parseInt(lengthStr);
|
||||||
return length === field.size
|
return length === field.size;
|
||||||
},
|
},
|
||||||
hasCheck: true,
|
hasCheck: true,
|
||||||
isSized: true,
|
isSized: true,
|
||||||
@ -1320,7 +1374,9 @@ const sqliteTypesBase = {
|
|||||||
}
|
}
|
||||||
const content = field.default.split(" ");
|
const content = field.default.split(" ");
|
||||||
const date = content[0].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,
|
hasCheck: false,
|
||||||
isSized: false,
|
isSized: false,
|
||||||
@ -1581,7 +1637,9 @@ const mssqlTypesBase = {
|
|||||||
}
|
}
|
||||||
const content = field.default.split(" ");
|
const content = field.default.split(" ");
|
||||||
const date = content[0].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,
|
hasCheck: false,
|
||||||
isSized: false,
|
isSized: false,
|
||||||
@ -1747,6 +1805,218 @@ export const mssqlTypes = new Proxy(mssqlTypesBase, {
|
|||||||
get: (target, prop) => (prop in target ? target[prop] : false),
|
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 = {
|
const dbToTypesBase = {
|
||||||
[DB.GENERIC]: defaultTypes,
|
[DB.GENERIC]: defaultTypes,
|
||||||
[DB.MYSQL]: mysqlTypes,
|
[DB.MYSQL]: mysqlTypes,
|
||||||
@ -1754,6 +2024,7 @@ const dbToTypesBase = {
|
|||||||
[DB.SQLITE]: sqliteTypes,
|
[DB.SQLITE]: sqliteTypes,
|
||||||
[DB.MSSQL]: mssqlTypes,
|
[DB.MSSQL]: mssqlTypes,
|
||||||
[DB.MARIADB]: mysqlTypes,
|
[DB.MARIADB]: mysqlTypes,
|
||||||
|
[DB.ORACLESQL]: oraclesqlTypes,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const dbToTypes = new Proxy(dbToTypesBase, {
|
export const dbToTypes = new Proxy(dbToTypesBase, {
|
||||||
|
@ -8,6 +8,7 @@ import mysql_icon from "../assets/mysql.png";
|
|||||||
import postgres_icon from "../assets/postgres.png";
|
import postgres_icon from "../assets/postgres.png";
|
||||||
import sqlite_icon from "../assets/sqlite.png";
|
import sqlite_icon from "../assets/sqlite.png";
|
||||||
import mariadb_icon from "../assets/mariadb.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 sql_server_icon from "../assets/sql-server.png";
|
||||||
import discord from "../assets/discord.png";
|
import discord from "../assets/discord.png";
|
||||||
import github from "../assets/github.png";
|
import github from "../assets/github.png";
|
||||||
@ -150,7 +151,7 @@ export default function LandingPage() {
|
|||||||
<div className="text-lg font-medium text-center mt-12 mb-6">
|
<div className="text-lg font-medium text-center mt-12 mb-6">
|
||||||
Design for your database
|
Design for your database
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-center items-center gap-8 md:block">
|
<div className="grid grid-cols-3 place-items-center sm:grid-cols-1 sm:gap-10">
|
||||||
{dbs.map((s, i) => (
|
{dbs.map((s, i) => (
|
||||||
<img
|
<img
|
||||||
key={"icon-" + i}
|
key={"icon-" + i}
|
||||||
@ -300,6 +301,7 @@ const dbs = [
|
|||||||
{ icon: sqlite_icon, height: 64 },
|
{ icon: sqlite_icon, height: 64 },
|
||||||
{ icon: mariadb_icon, height: 64 },
|
{ icon: mariadb_icon, height: 64 },
|
||||||
{ icon: sql_server_icon, height: 64 },
|
{ icon: sql_server_icon, height: 64 },
|
||||||
|
{ icon: oraclesql_icon, height: 172 },
|
||||||
];
|
];
|
||||||
|
|
||||||
const features = [
|
const features = [
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { DB } from "../../data/constants";
|
||||||
import { dbToTypes, defaultTypes } from "../../data/datatypes";
|
import { dbToTypes, defaultTypes } from "../../data/datatypes";
|
||||||
import { getInlineFK, parseDefault } from "./shared";
|
import { getInlineFK, parseDefault } from "./shared";
|
||||||
|
|
||||||
@ -42,10 +43,10 @@ export function generateSchema(type) {
|
|||||||
export function getTypeString(
|
export function getTypeString(
|
||||||
field,
|
field,
|
||||||
currentDb,
|
currentDb,
|
||||||
dbms = "mysql",
|
dbms = DB.MYSQL,
|
||||||
baseType = false,
|
baseType = false,
|
||||||
) {
|
) {
|
||||||
if (dbms === "mysql") {
|
if (dbms === DB.MYSQL) {
|
||||||
if (field.type === "UUID") {
|
if (field.type === "UUID") {
|
||||||
return `VARCHAR(36)`;
|
return `VARCHAR(36)`;
|
||||||
}
|
}
|
||||||
@ -62,7 +63,7 @@ export function getTypeString(
|
|||||||
return "JSON";
|
return "JSON";
|
||||||
}
|
}
|
||||||
return field.type;
|
return field.type;
|
||||||
} else if (dbms === "postgres") {
|
} else if (dbms === DB.POSTGRES) {
|
||||||
if (field.type === "SMALLINT" && field.increment) {
|
if (field.type === "SMALLINT" && field.increment) {
|
||||||
return "smallserial";
|
return "smallserial";
|
||||||
}
|
}
|
||||||
@ -97,7 +98,7 @@ export function getTypeString(
|
|||||||
return `${field.type.toLowerCase()}${field.size ? `(${field.size})` : ""}`;
|
return `${field.type.toLowerCase()}${field.size ? `(${field.size})` : ""}`;
|
||||||
}
|
}
|
||||||
return field.type.toLowerCase();
|
return field.type.toLowerCase();
|
||||||
} else if (dbms === "mssql") {
|
} else if (dbms === DB.MSSQL) {
|
||||||
let type = field.type;
|
let type = field.type;
|
||||||
switch (field.type) {
|
switch (field.type) {
|
||||||
case "ENUM":
|
case "ENUM":
|
||||||
@ -134,6 +135,47 @@ export function getTypeString(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return type;
|
return type;
|
||||||
|
} else if (dbms === DB.ORACLESQL) {
|
||||||
|
let oracleType;
|
||||||
|
switch (field.type) {
|
||||||
|
case "BIGINT":
|
||||||
|
oracleType = "NUMBER";
|
||||||
|
break;
|
||||||
|
case "VARCHAR":
|
||||||
|
oracleType = "VARCHAR2";
|
||||||
|
break;
|
||||||
|
case "TEXT":
|
||||||
|
oracleType = "CLOB";
|
||||||
|
break;
|
||||||
|
case "TIME":
|
||||||
|
case "DATETIME":
|
||||||
|
oracleType = "TIMESTAMP";
|
||||||
|
break;
|
||||||
|
case "BINARY":
|
||||||
|
case "VARBINARY":
|
||||||
|
oracleType = "RAW";
|
||||||
|
break;
|
||||||
|
case "UUID":
|
||||||
|
oracleType = "RAW(16)";
|
||||||
|
break;
|
||||||
|
case "SET":
|
||||||
|
case "ENUM":
|
||||||
|
oracleType = field.name + "_t";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
oracleType = field.type;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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})` : ""}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return oracleType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,13 +255,15 @@ export function jsonToPostgreSQL(obj) {
|
|||||||
type.comment === "" ? "" : `/**\n${type.comment}\n*/\n`
|
type.comment === "" ? "" : `/**\n${type.comment}\n*/\n`
|
||||||
}CREATE TYPE ${type.name} AS (\n${type.fields
|
}CREATE TYPE ${type.name} AS (\n${type.fields
|
||||||
.map(
|
.map(
|
||||||
(f) => `\t${f.name} ${getTypeString(f, obj.database, "postgres")}`,
|
(f) => `\t${f.name} ${getTypeString(f, obj.database, DB.POSTGRES)}`,
|
||||||
)
|
)
|
||||||
.join("\n")}\n);`
|
.join("\n")}\n);`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return `CREATE TYPE ${type.name} AS (\n${type.fields
|
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${
|
.join(",\n")}\n);\n${
|
||||||
type.comment && type.comment.trim() != ""
|
type.comment && type.comment.trim() != ""
|
||||||
? `\nCOMMENT ON TYPE ${type.name} IS '${type.comment}';\n`
|
? `\nCOMMENT ON TYPE ${type.name} IS '${type.comment}';\n`
|
||||||
@ -247,7 +291,7 @@ export function jsonToPostgreSQL(obj) {
|
|||||||
(field) =>
|
(field) =>
|
||||||
`${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t"${
|
`${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t"${
|
||||||
field.name
|
field.name
|
||||||
}" ${getTypeString(field, obj.database, "postgres")}${
|
}" ${getTypeString(field, obj.database, DB.POSTGRES)}${
|
||||||
field.notNull ? " NOT NULL" : ""
|
field.notNull ? " NOT NULL" : ""
|
||||||
}${field.unique ? " UNIQUE" : ""}${
|
}${field.unique ? " UNIQUE" : ""}${
|
||||||
field.default !== "" ? ` DEFAULT ${parseDefault(field)}` : ""
|
field.default !== "" ? ` DEFAULT ${parseDefault(field)}` : ""
|
||||||
@ -377,7 +421,7 @@ export function jsonToMariaDB(obj) {
|
|||||||
(field) =>
|
(field) =>
|
||||||
`\t\`${
|
`\t\`${
|
||||||
field.name
|
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.increment ? " AUTO_INCREMENT" : ""
|
||||||
}${field.unique ? " UNIQUE" : ""}${
|
}${field.unique ? " UNIQUE" : ""}${
|
||||||
field.default !== ""
|
field.default !== ""
|
||||||
@ -436,7 +480,7 @@ export function jsonToSQLServer(obj) {
|
|||||||
}CREATE TYPE [${type.name}] FROM ${
|
}CREATE TYPE [${type.name}] FROM ${
|
||||||
type.fields.length < 0
|
type.fields.length < 0
|
||||||
? ""
|
? ""
|
||||||
: `${getTypeString(type.fields[0], obj.database, "mssql", true)}`
|
: `${getTypeString(type.fields[0], obj.database, DB.MSSQL, true)}`
|
||||||
};\nGO\n`;
|
};\nGO\n`;
|
||||||
})
|
})
|
||||||
.join("\n")}\n${obj.tables
|
.join("\n")}\n${obj.tables
|
||||||
@ -449,7 +493,7 @@ export function jsonToSQLServer(obj) {
|
|||||||
(field) =>
|
(field) =>
|
||||||
`${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t[${
|
`${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t[${
|
||||||
field.name
|
field.name
|
||||||
}] ${getTypeString(field, obj.database, "mssql")}${
|
}] ${getTypeString(field, obj.database, DB.MSSQL)}${
|
||||||
field.notNull ? " NOT NULL" : ""
|
field.notNull ? " NOT NULL" : ""
|
||||||
}${field.increment ? " IDENTITY" : ""}${
|
}${field.increment ? " IDENTITY" : ""}${
|
||||||
field.unique ? " UNIQUE" : ""
|
field.unique ? " UNIQUE" : ""
|
||||||
@ -493,3 +537,70 @@ export function jsonToSQLServer(obj) {
|
|||||||
)
|
)
|
||||||
.join("\n")}`;
|
.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")}`;
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ import { DB } from "../../data/constants";
|
|||||||
import { toMariaDB } from "./mariadb";
|
import { toMariaDB } from "./mariadb";
|
||||||
import { toMSSQL } from "./mssql";
|
import { toMSSQL } from "./mssql";
|
||||||
import { toMySQL } from "./mysql";
|
import { toMySQL } from "./mysql";
|
||||||
|
import { toOracleSQL } from "./oraclesql";
|
||||||
import { toPostgres } from "./postgres";
|
import { toPostgres } from "./postgres";
|
||||||
import { toSqlite } from "./sqlite";
|
import { toSqlite } from "./sqlite";
|
||||||
|
|
||||||
@ -17,6 +18,8 @@ export function exportSQL(diagram) {
|
|||||||
return toMariaDB(diagram);
|
return toMariaDB(diagram);
|
||||||
case DB.MSSQL:
|
case DB.MSSQL:
|
||||||
return toMSSQL(diagram);
|
return toMSSQL(diagram);
|
||||||
|
case DB.ORACLESQL:
|
||||||
|
return toOracleSQL(diagram);
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
56
src/utils/exportSQL/oraclesql.js
Normal file
56
src/utils/exportSQL/oraclesql.js
Normal file
@ -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")}`;
|
||||||
|
}
|
@ -3,6 +3,7 @@ import { arrangeTables } from "../arrangeTables";
|
|||||||
import { fromMariaDB } from "./mariadb";
|
import { fromMariaDB } from "./mariadb";
|
||||||
import { fromMSSQL } from "./mssql";
|
import { fromMSSQL } from "./mssql";
|
||||||
import { fromMySQL } from "./mysql";
|
import { fromMySQL } from "./mysql";
|
||||||
|
import { fromOracleSQL } from "./oraclesql";
|
||||||
import { fromPostgres } from "./postgres";
|
import { fromPostgres } from "./postgres";
|
||||||
import { fromSQLite } from "./sqlite";
|
import { fromSQLite } from "./sqlite";
|
||||||
|
|
||||||
@ -24,6 +25,9 @@ export function importSQL(ast, toDb = DB.MYSQL, diagramDb = DB.GENERIC) {
|
|||||||
case DB.MSSQL:
|
case DB.MSSQL:
|
||||||
diagram = fromMSSQL(ast, diagramDb);
|
diagram = fromMSSQL(ast, diagramDb);
|
||||||
break;
|
break;
|
||||||
|
case DB.ORACLESQL:
|
||||||
|
diagram = fromOracleSQL(ast, diagramDb);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
diagram = { tables: [], relationships: [] };
|
diagram = { tables: [], relationships: [] };
|
||||||
break;
|
break;
|
||||||
|
137
src/utils/importSQL/oraclesql.js
Normal file
137
src/utils/importSQL/oraclesql.js
Normal file
@ -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 };
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user