fix(sql-export): escape newlines and quotes in multi-line comments (#765)

This commit is contained in:
Jonathan Fishner
2025-07-16 15:18:52 +03:00
committed by GitHub
parent b35e17526b
commit f7f92903de
6 changed files with 60 additions and 10 deletions

View File

@@ -48,6 +48,50 @@ export function exportFieldComment(comment: string): string {
.join('');
}
export function escapeSQLComment(comment: string): string {
if (!comment) {
return '';
}
// Escape single quotes by doubling them
let escaped = comment.replace(/'/g, "''");
// Replace newlines with spaces to prevent breaking SQL syntax
// Some databases support multi-line comments with specific syntax,
// but for maximum compatibility, we'll replace newlines with spaces
escaped = escaped.replace(/[\r\n]+/g, ' ');
// Trim any excessive whitespace
escaped = escaped.replace(/\s+/g, ' ').trim();
return escaped;
}
export function formatTableComment(comment: string): string {
if (!comment) {
return '';
}
// Split by newlines and add -- to each line
return (
comment
.split('\n')
.map((line) => `-- ${line}`)
.join('\n') + '\n'
);
}
export function formatMSSQLTableComment(comment: string): string {
if (!comment) {
return '';
}
// For MSSQL, we use multi-line comment syntax
// Escape */ to prevent breaking the comment block
const escaped = comment.replace(/\*\//g, '* /');
return `/**\n${escaped}\n*/\n`;
}
export function getInlineFK(table: DBTable, diagram: Diagram): string {
if (!diagram.relationships) {
return '';

View File

@@ -1,5 +1,6 @@
import {
exportFieldComment,
formatMSSQLTableComment,
isFunction,
isKeyword,
strHasQuotes,
@@ -108,7 +109,7 @@ export function exportMSSQL(diagram: Diagram): string {
: `[${table.name}]`;
return `${
table.comments ? `/**\n${table.comments}\n*/\n` : ''
table.comments ? formatMSSQLTableComment(table.comments) : ''
}CREATE TABLE ${tableName} (\n${table.fields
.map((field: DBField) => {
const fieldName = `[${field.name}]`;

View File

@@ -1,5 +1,7 @@
import {
exportFieldComment,
escapeSQLComment,
formatTableComment,
isFunction,
isKeyword,
strHasQuotes,
@@ -215,7 +217,7 @@ export function exportMySQL(diagram: Diagram): string {
const primaryKeyFields = table.fields.filter((f) => f.primaryKey);
return `${
table.comments ? `-- ${table.comments}\n` : ''
table.comments ? formatTableComment(table.comments) : ''
}CREATE TABLE IF NOT EXISTS ${tableName} (\n${table.fields
.map((field: DBField) => {
const fieldName = `\`${field.name}\``;
@@ -289,7 +291,7 @@ export function exportMySQL(diagram: Diagram): string {
// MySQL supports inline comments
const comment = field.comments
? ` COMMENT '${field.comments.replace(/'/g, "''")}'`
? ` COMMENT '${escapeSQLComment(field.comments)}'`
: '';
return `${exportFieldComment(field.comments ?? '')} ${fieldName} ${typeWithSize}${notNull}${autoIncrement}${unique}${defaultValue}${comment}`;
@@ -304,7 +306,7 @@ export function exportMySQL(diagram: Diagram): string {
}\n)${
// MySQL supports table comments
table.comments
? ` COMMENT='${table.comments.replace(/'/g, "''")}'`
? ` COMMENT='${escapeSQLComment(table.comments)}'`
: ''
};\n\n${
// Add indexes - MySQL creates them separately from the table definition

View File

@@ -1,5 +1,7 @@
import {
exportFieldComment,
escapeSQLComment,
formatTableComment,
isFunction,
isKeyword,
strHasQuotes,
@@ -216,7 +218,7 @@ export function exportPostgreSQL(diagram: Diagram): string {
const primaryKeyFields = table.fields.filter((f) => f.primaryKey);
return `${
table.comments ? `-- ${table.comments}\n` : ''
table.comments ? formatTableComment(table.comments) : ''
}CREATE TABLE ${tableName} (\n${table.fields
.map((field: DBField) => {
const fieldName = `"${field.name}"`;
@@ -311,7 +313,7 @@ export function exportPostgreSQL(diagram: Diagram): string {
}\n);\n\n${
// Add table comments
table.comments
? `COMMENT ON TABLE ${tableName} IS '${table.comments.replace(/'/g, "''")}';\n\n`
? `COMMENT ON TABLE ${tableName} IS '${escapeSQLComment(table.comments)}';\n\n`
: ''
}${
// Add column comments
@@ -319,7 +321,7 @@ export function exportPostgreSQL(diagram: Diagram): string {
.filter((f) => f.comments)
.map(
(f) =>
`COMMENT ON COLUMN ${tableName}."${f.name}" IS '${f.comments?.replace(/'/g, "''")}';\n`
`COMMENT ON COLUMN ${tableName}."${f.name}" IS '${escapeSQLComment(f.comments || '')}';\n`
)
.join('')
}\n${

View File

@@ -1,5 +1,6 @@
import {
exportFieldComment,
formatTableComment,
isFunction,
isKeyword,
strHasQuotes,
@@ -195,7 +196,7 @@ export function exportSQLite(diagram: Diagram): string {
primaryKeyFields[0].type.name.toLowerCase() === 'int');
return `${schemaComment}${
table.comments ? `-- ${table.comments}\n` : ''
table.comments ? formatTableComment(table.comments) : ''
}CREATE TABLE IF NOT EXISTS ${tableName} (\n${table.fields
.map((field: DBField) => {
const fieldName = `"${field.name}"`;

View File

@@ -285,13 +285,13 @@ export const exportBaseSQL = ({
// Add table comment
if (table.comments) {
sqlScript += `COMMENT ON TABLE ${tableName} IS '${table.comments}';\n`;
sqlScript += `COMMENT ON TABLE ${tableName} IS '${table.comments.replace(/'/g, "''")}';\n`;
}
table.fields.forEach((field) => {
// Add column comment
if (field.comments) {
sqlScript += `COMMENT ON COLUMN ${tableName}.${field.name} IS '${field.comments}';\n`;
sqlScript += `COMMENT ON COLUMN ${tableName}.${field.name} IS '${field.comments.replace(/'/g, "''")}';\n`;
}
});