Export sqlite

This commit is contained in:
1ilit 2024-06-10 15:23:57 +03:00
parent 9c31e2be52
commit 73dff50f14
6 changed files with 341 additions and 133 deletions

View File

@ -29,7 +29,7 @@ import {
jsonToSQLite, jsonToSQLite,
jsonToMariaDB, jsonToMariaDB,
jsonToSQLServer, jsonToSQLServer,
} from "../../utils/toSQL"; } from "../../utils/exportSQL/generic";
import { import {
ObjectType, ObjectType,
Action, Action,
@ -37,6 +37,7 @@ import {
State, State,
MODAL, MODAL,
SIDESHEET, SIDESHEET,
DB,
} from "../../data/constants"; } from "../../data/constants";
import jsPDF from "jspdf"; import jsPDF from "jspdf";
import { useHotkeys } from "react-hotkeys-hook"; import { useHotkeys } from "react-hotkeys-hook";
@ -62,6 +63,7 @@ import LayoutDropdown from "./LayoutDropdown";
import Sidesheet from "./SideSheet/Sidesheet"; import Sidesheet from "./SideSheet/Sidesheet";
import Modal from "./Modal/Modal"; import Modal from "./Modal/Modal";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { exportSQL } from "../../utils/exportSQL";
export default function ControlPanel({ export default function ControlPanel({
diagramId, diagramId,
@ -851,6 +853,7 @@ export default function ControlPanel({
function: () => {}, function: () => {},
}, },
export_source: { export_source: {
...(database === DB.GENERIC && {
children: [ children: [
{ {
MySQL: () => { MySQL: () => {
@ -933,7 +936,22 @@ export default function ControlPanel({
}, },
}, },
], ],
function: () => {}, }),
function: () => {
if (database === DB.GENERIC) return;
setModal(MODAL.CODE);
const src = exportSQL({
tables: tables,
references: relationships,
types: types,
database: database,
});
setExportData((prev) => ({
...prev,
data: src,
extension: "sql",
}));
},
}, },
exit: { exit: {
function: () => { function: () => {

View File

@ -98,6 +98,7 @@ export const defaultTypes = {
isSized: true, isSized: true,
hasPrecision: false, hasPrecision: false,
defaultSize: 1, defaultSize: 1,
hasQuotes: true,
}, },
VARCHAR: { VARCHAR: {
type: "VARCHAR", type: "VARCHAR",
@ -111,6 +112,7 @@ export const defaultTypes = {
isSized: true, isSized: true,
hasPrecision: false, hasPrecision: false,
defaultSize: 255, defaultSize: 255,
hasQuotes: true,
}, },
TEXT: { TEXT: {
type: "TEXT", type: "TEXT",
@ -119,16 +121,7 @@ export const defaultTypes = {
isSized: true, isSized: true,
hasPrecision: false, hasPrecision: false,
defaultSize: 65535, defaultSize: 65535,
}, hasQuotes: true,
DATE: {
type: "DATE",
checkDefault: (field) => {
return /^\d{4}-\d{2}-\d{2}$/.test(field.default);
},
hasCheck: false,
isSized: false,
hasPrecision: false,
defaultSize: null,
}, },
TIME: { TIME: {
type: "TIME", type: "TIME",
@ -139,6 +132,7 @@ export const defaultTypes = {
isSized: false, isSized: false,
hasPrecision: false, hasPrecision: false,
defaultSize: null, defaultSize: null,
hasQuotes: true,
}, },
TIMESTAMP: { TIMESTAMP: {
type: "TIMESTAMP", type: "TIMESTAMP",
@ -157,6 +151,18 @@ export const defaultTypes = {
isSized: false, isSized: false,
hasPrecision: false, hasPrecision: false,
defaultSize: null, defaultSize: null,
hasQuotes: true,
},
DATE: {
type: "DATE",
checkDefault: (field) => {
return /^\d{4}-\d{2}-\d{2}$/.test(field.default);
},
hasCheck: false,
isSized: false,
hasPrecision: false,
defaultSize: null,
hasQuotes: true,
}, },
DATETIME: { DATETIME: {
type: "DATETIME", type: "DATETIME",
@ -175,6 +181,7 @@ export const defaultTypes = {
isSized: false, isSized: false,
hasPrecision: false, hasPrecision: false,
defaultSize: null, defaultSize: null,
hasQuotes: true,
}, },
BOOLEAN: { BOOLEAN: {
type: "BOOLEAN", type: "BOOLEAN",
@ -200,6 +207,7 @@ export const defaultTypes = {
isSized: true, isSized: true,
hasPrecision: false, hasPrecision: false,
defaultSize: 1, defaultSize: 1,
hasQuotes: true,
}, },
VARBINARY: { VARBINARY: {
type: "VARBINARY", type: "VARBINARY",
@ -212,6 +220,7 @@ export const defaultTypes = {
isSized: true, isSized: true,
hasPrecision: false, hasPrecision: false,
defaultSize: 255, defaultSize: 255,
hasQuotes: true,
}, },
BLOB: { BLOB: {
type: "BLOB", type: "BLOB",
@ -246,6 +255,7 @@ export const defaultTypes = {
isSized: false, isSized: false,
hasPrecision: false, hasPrecision: false,
defaultSize: null, defaultSize: null,
hasQuotes: true,
}, },
SET: { SET: {
type: "SET", type: "SET",
@ -356,16 +366,126 @@ export const postgresTypes = {
}; };
export const sqliteTypes = { export const sqliteTypes = {
INTEGER: { type: "INTEGER", checkDefault: (field) => {} }, INT: {
REAL: { type: "REAL", checkDefault: (field) => {} }, type: "INT",
TEXT: { type: "TEXT", checkDefault: (field) => {} }, checkDefault: (field) => {
BLOB: { type: "BLOB", checkDefault: (field) => {} }, return intRegex.test(field.default);
NUMERIC: { type: "NUMERIC", checkDefault: (field) => {} }, },
BOOLEAN: { type: "BOOLEAN", checkDefault: (field) => {} }, hasCheck: true,
DATE: { type: "DATE", checkDefault: (field) => {} }, isSized: false,
DATETIME: { type: "DATETIME", checkDefault: (field) => {} }, hasPrecision: false,
TIME: { type: "TIME", checkDefault: (field) => {} }, defaultSize: null,
TIMESTAMP: { type: "TIMESTAMP", checkDefault: (field) => {} }, },
REAL: {
type: "REAL",
checkDefault: (field) => {
return doubleRegex.test(field.default);
},
hasCheck: true,
isSized: false,
hasPrecision: true,
defaultSize: null,
},
TEXT: {
type: "TEXT",
checkDefault: (field) => false,
hasCheck: false,
isSized: true,
hasPrecision: false,
defaultSize: 65535,
hasQuotes: true,
},
BLOB: {
type: "BLOB",
checkDefault: (field) => false,
isSized: false,
hasCheck: false,
hasPrecision: false,
defaultSize: null,
},
NUMERIC: {
type: "NUMERIC",
checkDefault: (field) => {
return doubleRegex.test(field.default);
},
hasCheck: true,
isSized: false,
hasPrecision: true,
defaultSize: null,
},
BOOLEAN: {
type: "BOOLEAN",
checkDefault: (field) => {
return (
field.default.trim().toLowerCase() === "false" ||
field.default.trim().toLowerCase() === "true"
);
},
hasCheck: false,
isSized: false,
hasPrecision: false,
defaultSize: null,
},
TIME: {
type: "TIME",
checkDefault: (field) => {
return /^(?:[01]?\d|2[0-3]):[0-5]?\d:[0-5]?\d$/.test(field.default);
},
hasCheck: false,
isSized: false,
hasPrecision: false,
defaultSize: null,
hasQuotes: true,
},
TIMESTAMP: {
type: "TIMESTAMP",
checkDefault: (field) => {
if (field.default.toUpperCase() === "CURRENT_TIMESTAMP") {
return true;
}
if (!/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(field.default)) {
return false;
}
const content = field.default.split(" ");
const date = content[0].split("-");
return parseInt(date[0]) >= 1970 && parseInt(date[0]) <= 2038;
},
hasCheck: false,
isSized: false,
hasPrecision: false,
defaultSize: null,
hasQuotes: true,
},
DATE: {
type: "DATE",
checkDefault: (field) => {
return /^\d{4}-\d{2}-\d{2}$/.test(field.default);
},
hasCheck: false,
isSized: false,
hasPrecision: false,
defaultSize: null,
hasQuotes: true,
},
DATETIME: {
type: "DATETIME",
checkDefault: (field) => {
if (field.default.toUpperCase() === "CURRENT_TIMESTAMP") {
return true;
}
if (!/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(field.default)) {
return false;
}
const c = field.default.split(" ");
const d = c[0].split("-");
return parseInt(d[0]) >= 1000 && parseInt(d[0]) <= 9999;
},
hasCheck: false,
isSized: false,
hasPrecision: false,
defaultSize: null,
hasQuotes: true,
},
}; };
export const mariadbTypes = { export const mariadbTypes = {

View File

@ -1,5 +1,5 @@
import { dbToTypes, defaultTypes } from "../data/datatypes"; import { dbToTypes, defaultTypes } from "../../data/datatypes";
import { isFunction, isKeyword, strHasQuotes } from "./utils"; import { parseDefault } from "./shared";
export function getJsonType(f) { export function getJsonType(f) {
if (!Object.keys(defaultTypes).includes(f.type)) { if (!Object.keys(defaultTypes).includes(f.type)) {
@ -137,33 +137,6 @@ export function getTypeString(
} }
} }
export function hasQuotes(type) {
return [
"CHAR",
"VARCHAR",
"BINARY",
"VARBINARY",
"ENUM",
"DATE",
"TIME",
"TIMESTAMP",
"DATETIME",
].includes(type);
}
export function parseDefault(field) {
if (
strHasQuotes(field.default) ||
isFunction(field.default) ||
isKeyword(field.default) ||
!hasQuotes(field.type)
) {
return field.default;
}
return `'${field.default}'`;
}
export function jsonToMySQL(obj) { export function jsonToMySQL(obj) {
return `${obj.tables return `${obj.tables
.map( .map(
@ -178,7 +151,7 @@ export function jsonToMySQL(obj) {
}\` ${getTypeString(field, obj.database)}${field.notNull ? " NOT NULL" : ""}${ }\` ${getTypeString(field, obj.database)}${field.notNull ? " NOT NULL" : ""}${
field.increment ? " AUTO_INCREMENT" : "" field.increment ? " AUTO_INCREMENT" : ""
}${field.unique ? " UNIQUE" : ""}${ }${field.unique ? " UNIQUE" : ""}${
field.default !== "" ? ` DEFAULT ${parseDefault(field)}` : "" field.default !== "" ? ` DEFAULT ${parseDefault(field, obj.database)}` : ""
}${ }${
field.check === "" || field.check === "" ||
!dbToTypes[obj.database][field.type].hasCheck !dbToTypes[obj.database][field.type].hasCheck
@ -277,7 +250,7 @@ export function jsonToPostgreSQL(obj) {
}" ${getTypeString(field, obj.database, "postgres")}${ }" ${getTypeString(field, obj.database, "postgres")}${
field.notNull ? " NOT NULL" : "" field.notNull ? " NOT NULL" : ""
}${ }${
field.default !== "" ? ` DEFAULT ${parseDefault(field)}` : "" field.default !== "" ? ` DEFAULT ${parseDefault(field, obj.database)}` : ""
}${ }${
field.check === "" || field.check === "" ||
!dbToTypes[obj.database][field.type].hasCheck !dbToTypes[obj.database][field.type].hasCheck
@ -378,7 +351,7 @@ export function jsonToSQLite(obj) {
field.name field.name
}" ${getSQLiteType(field)}${field.notNull ? " NOT NULL" : ""}${ }" ${getSQLiteType(field)}${field.notNull ? " NOT NULL" : ""}${
field.unique ? " UNIQUE" : "" field.unique ? " UNIQUE" : ""
}${field.default !== "" ? ` DEFAULT ${parseDefault(field)}` : ""}${ }${field.default !== "" ? ` DEFAULT ${parseDefault(field, obj.database)}` : ""}${
field.check === "" || field.check === "" ||
!dbToTypes[obj.database][field.type].hasCheck !dbToTypes[obj.database][field.type].hasCheck
? "" ? ""
@ -424,7 +397,7 @@ export function jsonToMariaDB(obj) {
}\` ${getTypeString(field, obj.database)}${field.notNull ? " NOT NULL" : ""}${ }\` ${getTypeString(field, obj.database)}${field.notNull ? " NOT NULL" : ""}${
field.increment ? " AUTO_INCREMENT" : "" field.increment ? " AUTO_INCREMENT" : ""
}${field.unique ? " UNIQUE" : ""}${ }${field.unique ? " UNIQUE" : ""}${
field.default !== "" ? ` DEFAULT ${parseDefault(field)}` : "" field.default !== "" ? ` DEFAULT ${parseDefault(field, obj.database)}` : ""
}${ }${
field.check === "" || field.check === "" ||
!dbToTypes[obj.database][field.type].hasCheck !dbToTypes[obj.database][field.type].hasCheck
@ -498,7 +471,7 @@ export function jsonToSQLServer(obj) {
}${field.increment ? " IDENTITY" : ""}${ }${field.increment ? " IDENTITY" : ""}${
field.unique ? " UNIQUE" : "" field.unique ? " UNIQUE" : ""
}${ }${
field.default !== "" ? ` DEFAULT ${parseDefault(field)}` : "" field.default !== "" ? ` DEFAULT ${parseDefault(field, obj.database)}` : ""
}${ }${
field.check === "" || field.check === "" ||
!dbToTypes[obj.database][field.type].hasCheck !dbToTypes[obj.database][field.type].hasCheck

View File

@ -0,0 +1,19 @@
import { DB } from "../../data/constants";
import { toSqlite } from "./sqlite";
export function exportSQL(diagram) {
switch (diagram.database) {
case DB.SQLITE:
return toSqlite(diagram);
case DB.MYSQL:
return "hi from mysql";
case DB.POSTGRES:
return "hi from postgres";
case DB.MARIADB:
return "hi from mariadb";
case DB.MSSQL:
return "hi from mssql";
default:
return "";
}
}

View File

@ -0,0 +1,16 @@
import { DB } from "../../data/constants";
import { dbToTypes } from "../../data/datatypes";
import { isFunction, isKeyword, strHasQuotes } from "../utils";
export function parseDefault(field, database = DB.GENERIC) {
if (
strHasQuotes(field.default) ||
isFunction(field.default) ||
isKeyword(field.default) ||
!dbToTypes[database][field.type].hasQuotes
) {
return field.default;
}
return `'${field.default}'`;
}

View File

@ -0,0 +1,62 @@
import { dbToTypes } from "../../data/datatypes";
import { parseDefault } from "./shared";
export function toSqlite(diagram) {
return diagram.tables
.map((table) => {
const inlineFK = getInlineFK(table, diagram);
return `${
table.comment === "" ? "" : `/* ${table.comment} */\n`
}CREATE TABLE IF NOT EXISTS "${table.name}" (\n${table.fields
.map(
(field) =>
`${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t"${
field.name
}" ${field.type}${field.notNull ? " NOT NULL" : ""}${
field.unique ? " UNIQUE" : ""
}${field.default !== "" ? ` DEFAULT ${parseDefault(field, diagram.database)}` : ""}${
field.check === "" ||
!dbToTypes[diagram.database][field.type].hasCheck
? ""
: ` CHECK(${field.check})`
}`,
)
.join(",\n")}${
table.fields.filter((f) => f.primary).length > 0
? `,\n\tPRIMARY KEY(${table.fields
.filter((f) => f.primary)
.map((f) => `"${f.name}"`)
.join(", ")})${inlineFK !== "" ? ",\n" : ""}`
: ""
}\t${inlineFK}\n);\n${
table.indices.length > 0
? `${table.indices
.map(
(i) =>
`\nCREATE ${i.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS "${
i.name
}"\nON "${table.name}" (${i.fields
.map((f) => `"${f}"`)
.join(", ")});`,
)
.join("\n")}`
: ""
}`;
})
.join("\n");
}
export function getInlineFK(table, obj) {
let fk = "";
obj.references.forEach((r) => {
if (fk !== "") return;
if (r.startTableId === table.id) {
fk = `FOREIGN KEY ("${table.fields[r.startFieldId].name}") REFERENCES "${
obj.tables[r.endTableId].name
}"("${
obj.tables[r.endTableId].fields[r.endFieldId].name
}")\n\tON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()}`;
}
});
return fk;
}