diff --git a/src/components/EditorHeader/Modal/Modal.jsx b/src/components/EditorHeader/Modal/Modal.jsx index fb7ff8f01..85d414f3c 100644 --- a/src/components/EditorHeader/Modal/Modal.jsx +++ b/src/components/EditorHeader/Modal/Modal.jsx @@ -4,7 +4,7 @@ import { Parser } from "node-sql-parser"; import { Parser as OracleParser } from "oracle-sql-parser"; import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { DB, MODAL, STATUS } from "../../../data/constants"; +import { DB, EMPTY_ENUM_PLACEHOLDER, MODAL, STATUS } from "../../../data/constants"; import { databases } from "../../../data/databases"; import { useAreas, @@ -102,16 +102,41 @@ export default function Modal({ const parseSQLAndLoadDiagram = () => { const targetDatabase = database === DB.GENERIC ? importDb : database; + let normalizedSql = importSource.src; + if (targetDatabase === DB.POSTGRES) { + // Strip block comments /* ... */ + normalizedSql = normalizedSql.replace(/\/\*[\s\S]*?\*\//g, ''); + // Strip line comments -- ... + normalizedSql = normalizedSql.replace(/--.*$/gm, ''); + + // Fix empty ENUM () which crashes node-sql-parser + normalizedSql = normalizedSql.replace( + /CREATE\s+TYPE\s+"([^"]+)"\s+AS\s+ENUM\s*\(\s*\)\s*;/gi, + `CREATE TYPE "$1" AS ENUM ('${EMPTY_ENUM_PLACEHOLDER}');`, + ); + + // Hoist CREATE TYPE statements to the top so enum types are + // defined before any table that references them + const typeStatements = + normalizedSql.match(/CREATE\s+TYPE\s+[^;]+;/gi) || []; + if (typeStatements.length > 0) { + const rest = normalizedSql + .replace(/CREATE\s+TYPE\s+[^;]+;/gi, '') + .trim(); + normalizedSql = [...typeStatements, rest].filter(Boolean).join('\n'); + } + } + let ast = null; try { if (targetDatabase === DB.ORACLESQL) { const oracleParser = new OracleParser(); - ast = oracleParser.parse(importSource.src); + ast = oracleParser.parse(normalizedSql); } else { const parser = new Parser(); - ast = parser.astify(importSource.src, { + ast = parser.astify(normalizedSql, { database: targetDatabase, }); } diff --git a/src/data/constants.js b/src/data/constants.js index c56a68f68..02e363d36 100644 --- a/src/data/constants.js +++ b/src/data/constants.js @@ -103,6 +103,8 @@ export const SIDESHEET = { VERSIONS: 2, }; +export const EMPTY_ENUM_PLACEHOLDER = "_placeholder"; + export const DB = { MYSQL: "mysql", POSTGRES: "postgresql", diff --git a/src/utils/exportSQL/postgres.js b/src/utils/exportSQL/postgres.js index 3dc6c0e13..20dd36ba5 100644 --- a/src/utils/exportSQL/postgres.js +++ b/src/utils/exportSQL/postgres.js @@ -1,14 +1,20 @@ -import { escapeQuotes, exportFieldComment, parseDefault } from "./shared"; +import { EMPTY_ENUM_PLACEHOLDER } from "../../data/constants"; +import { + escapeQuotes, + exportFieldComment, + parseDefault, +} from "./shared"; import { dbToTypes } from "../../data/datatypes"; export function toPostgres(diagram) { const enumStatements = diagram.enums - .map( - (e) => - `CREATE TYPE "${e.name}" AS ENUM (\n${e.values - .map((v) => `\t'${v}'`) - .join(",\n")}\n);\n`, - ) + .map((e) => { + const values = + e.values?.length > 0 ? e.values : [EMPTY_ENUM_PLACEHOLDER]; + return `CREATE TYPE "${e.name}" AS ENUM (\n${values + .map((v) => `\t'${escapeQuotes(v)}'`) + .join(",\n")}\n);\n`; + }) .join("\n"); const typeStatements = diagram.types diff --git a/src/utils/importSQL/postgres.js b/src/utils/importSQL/postgres.js index 66103769c..d7fdb0bf7 100644 --- a/src/utils/importSQL/postgres.js +++ b/src/utils/importSQL/postgres.js @@ -65,7 +65,7 @@ export function fromPostgres(ast, diagramDb = DB.GENERIC) { field.unique = false; if (d.unique) field.unique = true; field.increment = false; - if (d.auto_increment) field.increment = true; + if (d.auto_increment || d.generated_by_default) field.increment = true; field.notNull = false; if (d.nullable) field.notNull = true; field.primary = false; @@ -257,7 +257,7 @@ export function fromPostgres(ast, diagramDb = DB.GENERIC) { if (e.resource === "enum") { const newEnum = { name: e.name.name, - values: e.create_definitions.value.map((x) => x.value), + values: (e.create_definitions?.value ?? []).map((x) => x.value), }; enums.push(newEnum); } else if (Array.isArray(e.create_definitions)) {