feat:Added support for exporting SQL as OracleDB SQL

This commit is contained in:
Aditya
2024-07-30 17:13:02 +05:30
parent 06f7284ea8
commit f6920e7165
10 changed files with 405 additions and 2 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
src/assets/oraclesql.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -29,6 +29,7 @@ import {
jsonToSQLite, jsonToSQLite,
jsonToMariaDB, jsonToMariaDB,
jsonToSQLServer, jsonToSQLServer,
jsonToOracleSQL,
} from "../../utils/exportSQL/generic"; } from "../../utils/exportSQL/generic";
import { import {
ObjectType, ObjectType,
@@ -882,6 +883,22 @@ export default function ControlPanel({
})); }));
}, },
}, },
{
OracleSQL: () => {
setModal(MODAL.CODE);
const src = jsonToOracleSQL({
tables: tables,
references: relationships,
types: types,
database: database,
});
setExportData((prev) => ({
...prev,
data: src,
extension: "sql",
}));
},
},
], ],
}), }),
function: () => { function: () => {

View File

@@ -112,5 +112,6 @@ export const DB = {
MSSQL: "transactsql", MSSQL: "transactsql",
SQLITE: "sqlite", SQLITE: "sqlite",
MARIADB: "mariadb", MARIADB: "mariadb",
ORACLESQL: "oraclesql",
GENERIC: "generic", GENERIC: "generic",
}; };

View File

@@ -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";
@@ -40,6 +41,14 @@ 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,
},
[DB.GENERIC]: { [DB.GENERIC]: {
name: i18n.t("generic"), name: i18n.t("generic"),
label: DB.GENERIC, label: DB.GENERIC,

View File

@@ -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,
@@ -223,6 +247,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,
@@ -1616,6 +1656,155 @@ 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 = {
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 = { const dbToTypesBase = {
[DB.GENERIC]: defaultTypes, [DB.GENERIC]: defaultTypes,
[DB.MYSQL]: mysqlTypes, [DB.MYSQL]: mysqlTypes,
@@ -1623,6 +1812,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, {

View File

@@ -9,6 +9,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";
@@ -178,7 +179,7 @@ export default function LandingPage() {
<div className="text-center text-2xl font-bold mb-4"> <div className="text-center text-2xl font-bold mb-4">
We support these DBMS We support these DBMS
</div> </div>
<div className="grid grid-cols-5 place-items-center items-baseline sm:grid-cols-1 sm:gap-4"> <div className="grid grid-cols-3 grid-rows-2 gap-y-[4rem] place-items-center items-baseline sm:grid-cols-1 sm:grid-rows-1 sm:gap-10">
<img <img
src={mysql_icon} src={mysql_icon}
className="opacity-70 hover:opacity-100 transition-all duration-300 h-20" className="opacity-70 hover:opacity-100 transition-all duration-300 h-20"
@@ -199,6 +200,10 @@ export default function LandingPage() {
src={sql_server_icon} src={sql_server_icon}
className="opacity-70 hover:opacity-100 transition-all duration-300 h-16" className="opacity-70 hover:opacity-100 transition-all duration-300 h-16"
/> />
<img
src={oraclesql_icon}
className="opacity-70 hover:opacity-100 transition-all duration-300 h-16 scale-[300%]"
/>
</div> </div>
<div className="mt-16 mb-2 text-2xl font-bold text-center"> <div className="mt-16 mb-2 text-2xl font-bold text-center">
Reach out to us Reach out to us

View File

@@ -134,6 +134,76 @@ export function getTypeString(
} }
return type; 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;
} }
} }
@@ -386,7 +456,7 @@ export function jsonToMariaDB(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)}${field.notNull ? " NOT NULL" : ""}${ }\` ${getTypeString(field, obj.database, "oraclesql")}${field.notNull ? " NOT NULL" : ""}${
field.increment ? " AUTO_INCREMENT" : "" field.increment ? " AUTO_INCREMENT" : ""
}${field.unique ? " UNIQUE" : ""}${ }${field.unique ? " UNIQUE" : ""}${
field.default !== "" field.default !== ""
@@ -502,3 +572,55 @@ export function jsonToSQLServer(obj) {
) )
.join("\n")}`; .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")}`;
}

View File

@@ -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.ORACLE:
return toOracleSQL(diagram);
default: default:
return ""; return "";
} }

View 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 !== 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")}`;
}