diff --git a/src/lib/dbml/apply-dbml/apply-dbml.ts b/src/lib/dbml/apply-dbml/apply-dbml.ts index 7095e07b..30da6dda 100644 --- a/src/lib/dbml/apply-dbml/apply-dbml.ts +++ b/src/lib/dbml/apply-dbml/apply-dbml.ts @@ -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 diff --git a/src/lib/dbml/dbml-import/__tests__/dbml-import-fantasy-examples.test.ts b/src/lib/dbml/dbml-import/__tests__/dbml-import-fantasy-examples.test.ts index 482a8658..eae700ef 100644 --- a/src/lib/dbml/dbml-import/__tests__/dbml-import-fantasy-examples.test.ts +++ b/src/lib/dbml/dbml-import/__tests__/dbml-import-fantasy-examples.test.ts @@ -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'); + }); + }); }); diff --git a/src/lib/dbml/dbml-import/dbml-import.ts b/src/lib/dbml/dbml-import/dbml-import.ts index 6be0631b..22bcf8f8 100644 --- a/src/lib/dbml/dbml-import/dbml-import.ts +++ b/src/lib/dbml/dbml-import/dbml-import.ts @@ -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[] =