fix(dbml): import dbml notes (table + fields) (#827)

This commit is contained in:
Guy Ben-Aharon
2025-08-04 12:43:02 +03:00
committed by GitHub
parent 337f7cdab4
commit b9a1e78b53
3 changed files with 96 additions and 15 deletions

View File

@@ -199,6 +199,7 @@ const buildSourceMappings = (sourceDiagram: Diagram) => {
const updateTables = ({
targetTables,
sourceTables,
defaultDatabaseSchema,
}: {
targetTables: DBTable[] | undefined;
sourceTables: DBTable[] | undefined;
@@ -225,6 +226,24 @@ const updateTables = ({
const targetKey = createObjectKeyFromTable(targetTable);
let sourceTable = sourceTablesByKey.get(targetKey);
if (!sourceTable && defaultDatabaseSchema) {
if (!targetTable.schema) {
// If target table has no schema, try matching with default schema
const defaultKey = createObjectKeyFromTable({
...targetTable,
schema: defaultDatabaseSchema,
});
sourceTable = sourceTablesByKey.get(defaultKey);
} else if (targetTable.schema === defaultDatabaseSchema) {
// If target table's schema matches default, try matching without schema
const noSchemaKey = createObjectKeyFromTable({
...targetTable,
schema: undefined,
});
sourceTable = sourceTablesByKey.get(noSchemaKey);
}
}
// If no exact match, try matching by name only
if (!sourceTable) {
sourceTable = sourceTables.find(
@@ -285,6 +304,7 @@ const updateTables = ({
...sourceTable,
fields: updatedFields,
indexes: updatedIndexes,
comments: targetTable.comments,
};
// Update nullable, unique, primaryKey from target fields

View File

@@ -1244,4 +1244,47 @@ Table "public_3"."comments" {
expect(relationshipsHaveSchemas).toBe(true);
});
});
describe('Notes Support', () => {
it('should import table with note', async () => {
const dbmlWithTableNote = `
Table products {
id integer [pk]
name varchar(100)
Note: 'This table stores product information'
}`;
const diagram = await importDBMLToDiagram(dbmlWithTableNote);
expect(diagram.tables).toHaveLength(1);
const productsTable = diagram.tables?.[0];
expect(productsTable?.name).toBe('products');
expect(productsTable?.comments).toBe(
'This table stores product information'
);
});
it('should import field with note', async () => {
const dbmlWithFieldNote = `
Table orders {
id integer [pk]
total numeric(10,2) [note: 'Order total including tax']
}`;
const diagram = await importDBMLToDiagram(dbmlWithFieldNote);
expect(diagram.tables).toHaveLength(1);
const ordersTable = diagram.tables?.[0];
expect(ordersTable?.fields).toHaveLength(2);
const totalField = ordersTable?.fields.find(
(f) => f.name === 'total'
);
// Field notes should be imported
expect(totalField).toBeDefined();
expect(totalField?.name).toBe('total');
expect(totalField?.comments).toBe('Order total including tax');
});
});
});

View File

@@ -86,6 +86,7 @@ interface DBMLField {
characterMaximumLength?: string | null;
precision?: number | null;
scale?: number | null;
note?: string | { value: string } | null;
}
interface DBMLIndexColumn {
@@ -311,6 +312,7 @@ export const importDBMLToDiagram = async (
pk: field.pk,
not_null: field.not_null,
increment: field.increment,
note: field.note,
...getFieldExtraAttributes(field, allEnums),
} satisfies DBMLField;
}),
@@ -424,21 +426,37 @@ export const importDBMLToDiagram = async (
const tableSpacing = 300;
// Create fields first so we have their IDs
const fields: DBField[] = table.fields.map((field) => ({
id: generateId(),
name: field.name.replace(/['"]/g, ''),
type: mapDBMLTypeToDataType(field.type.type_name, {
...options,
enums: extractedData.enums,
}),
nullable: !field.not_null,
primaryKey: field.pk || false,
unique: field.unique || false,
createdAt: Date.now(),
characterMaximumLength: field.characterMaximumLength,
precision: field.precision,
scale: field.scale,
}));
const fields: DBField[] = table.fields.map((field) => {
// Extract field note/comment
let fieldComment: string | undefined;
if (field.note) {
if (typeof field.note === 'string') {
fieldComment = field.note;
} else if (
typeof field.note === 'object' &&
'value' in field.note
) {
fieldComment = field.note.value;
}
}
return {
id: generateId(),
name: field.name.replace(/['"]/g, ''),
type: mapDBMLTypeToDataType(field.type.type_name, {
...options,
enums: extractedData.enums,
}),
nullable: !field.not_null,
primaryKey: field.pk || false,
unique: field.unique || false,
createdAt: Date.now(),
characterMaximumLength: field.characterMaximumLength,
precision: field.precision,
scale: field.scale,
...(fieldComment ? { comments: fieldComment } : {}),
};
});
// Convert DBML indexes to ChartDB indexes
const indexes: DBIndex[] =