fix(dbml-import): handle unsupported DBML features and add comprehensive tests (#766)

* fix(dbml-import): handle unsupported DBML features and add comprehensive tests

* fix build

* fix(dbml-export): handle composite primary keys, invalid defaults, and char type formatting

* fix build

* fix

---------

Co-authored-by: Guy Ben-Aharon <baguy3@gmail.com>
This commit is contained in:
Jonathan Fishner
2025-07-21 11:33:34 +03:00
committed by GitHub
parent 6af94afc56
commit 22d46e1e90
9 changed files with 2827 additions and 619 deletions

View File

@@ -163,6 +163,12 @@ export const exportBaseSQL = ({
: table.name;
sqlScript += `CREATE TABLE ${tableName} (\n`;
// Check for composite primary keys
const primaryKeyFields = table.fields.filter(
(field) => field.primaryKey
);
const hasCompositePrimaryKey = primaryKeyFields.length > 1;
table.fields.forEach((field, index) => {
let typeName = simplifyDataType(field.type.name);
@@ -214,6 +220,15 @@ export const exportBaseSQL = ({
typeName = 'text[]';
}
// Handle special types
if (
typeName.toLowerCase() === 'char' &&
!field.characterMaximumLength
) {
// Default char without length to char(1)
typeName = 'char';
}
sqlScript += ` ${field.name} ${typeName}`;
// Add size for character types
@@ -225,6 +240,12 @@ export const exportBaseSQL = ({
} else if (field.type.name.toLowerCase().includes('varchar')) {
// Keep varchar sizing, but don't apply to TEXT (previously enum)
sqlScript += `(500)`;
} else if (
typeName.toLowerCase() === 'char' &&
!field.characterMaximumLength
) {
// Default char without explicit length to char(1) for compatibility
sqlScript += `(1)`;
}
// Add precision and scale for numeric types
@@ -249,38 +270,52 @@ export const exportBaseSQL = ({
// Temp remove default user-define value when it have it
let fieldDefault = field.default;
// Remove the type cast part after :: if it exists
if (fieldDefault.includes('::')) {
const endedWithParentheses = fieldDefault.endsWith(')');
fieldDefault = fieldDefault.split('::')[0];
// Skip invalid default values for DBML export
if (
fieldDefault === 'has default' ||
fieldDefault === 'DEFAULT has default'
) {
// Skip this default value as it's invalid SQL
} else {
// Remove the type cast part after :: if it exists
if (fieldDefault.includes('::')) {
const endedWithParentheses = fieldDefault.endsWith(')');
fieldDefault = fieldDefault.split('::')[0];
if (
(fieldDefault.startsWith('(') &&
!fieldDefault.endsWith(')')) ||
endedWithParentheses
) {
fieldDefault += ')';
if (
(fieldDefault.startsWith('(') &&
!fieldDefault.endsWith(')')) ||
endedWithParentheses
) {
fieldDefault += ')';
}
}
}
if (fieldDefault === `('now')`) {
fieldDefault = `now()`;
}
if (fieldDefault === `('now')`) {
fieldDefault = `now()`;
}
sqlScript += ` DEFAULT ${fieldDefault}`;
sqlScript += ` DEFAULT ${fieldDefault}`;
}
}
// Handle PRIMARY KEY constraint
if (field.primaryKey) {
// Handle PRIMARY KEY constraint - only add inline if not composite
if (field.primaryKey && !hasCompositePrimaryKey) {
sqlScript += ' PRIMARY KEY';
}
// Add a comma after each field except the last one
if (index < table.fields.length - 1) {
// Add a comma after each field except the last one (or before composite primary key)
if (index < table.fields.length - 1 || hasCompositePrimaryKey) {
sqlScript += ',\n';
}
});
// Add composite primary key constraint if needed
if (hasCompositePrimaryKey) {
const pkFieldNames = primaryKeyFields.map((f) => f.name).join(', ');
sqlScript += `\n PRIMARY KEY (${pkFieldNames})`;
}
sqlScript += '\n);\n\n';
// Add table comment