# How DrawDB Parses and Reconciles DBML Files: A Deep Dive into the Import Pipeline

> Learn how DrawDB parses DBML files using a two-stage pipeline, converting text to an AST and reconciling schema changes for seamless diagram updates.

- Repository: [drawDB/drawdb](https://github.com/drawdb-io/drawdb)
- Tags: deep-dive
- Published: 2026-08-14

---

**DrawDB parses DBML files using a two-stage pipeline that first converts DBML text to an AST via `@dbml/core`, then reconciles the resulting schema with the existing diagram by merging matching entities and surfacing conflicts for manual resolution.**

The `drawdb-io/drawdb` project includes robust support for DBML (Database Markup Language), a readable DSL for describing database schemas. When users import DBML, the application must not only parse the syntax but also intelligently merge the incoming schema with any existing diagram state. This article examines the complete import pipeline from source file to rendered diagram.

## The Entry Point: `fromDBML` in the Import Module

All DBML imports flow through **[`src/utils/importFrom/dbml.js`](https://github.com/drawdb-io/drawdb/blob/main/src/utils/importFrom/dbml.js)**, which exports the core `fromDBML` function. This function accepts two parameters: the raw DBML string and the current `database` object representing the diagram's state.

```javascript
// Conceptual signature based on drawDB architecture
export function fromDBML(dbmlString, database) {
  // 1. Parse DBML to AST
  // 2. Map AST to internal model
  // 3. Reconcile with existing database
  // 4. Return updated database
}

```

The function serves as the orchestrator, delegating parsing to the `@dbml/core` library and handling the complex merge logic internally.

## Stage 1: Parsing DBML to an Abstract Syntax Tree

The **DBML parsing** step leverages the official `@dbml/core` package (or the standalone `dbml-parser`) to transform text into a structured AST. This AST contains four primary node types:

- **Tables** – with column definitions, indexes, and table-level notes
- **Enums** – named sets of allowed values
- **Relationships** – foreign key constraints between tables
- **Notes** – free-form documentation attached to tables or columns

The parser handles DBML's full syntax including:

- Data types with precision/scale (e.g., `varchar(255)`, `decimal(10,2)`)
- Column attributes: `pk`, `unique`, `not null`, `default`, `ref`
- Inline and block-style relationship definitions
- Multi-line notes and schema-level settings

If the DBML contains syntax errors, the parser throws descriptive exceptions that propagate up to the UI layer, surfacing line numbers and error messages in the code editor component.

## Stage 2: Mapping the AST to DrawDB's Internal Model

Once parsing succeeds, drawDB walks the AST and constructs its own internal representation. This **AST-to-model mapping** preserves semantic meaning while translating to drawDB's native structure:

- **Table nodes** become objects in `database.tables` with generated `id` values, position coordinates, and field arrays
- **Columns** map to field objects capturing `name`, `type`, `default`, `nullable`, `unique`, `primary` flags
- **Enums** populate `database.enums` with their value lists
- **Relationships** transform into edge objects linking source table/field to target table/field, with cardinality (`-`, `<`, `>`, `<>`)

The mapping process also extracts metadata like `note` properties and attaches them to the corresponding entities for display in the UI.

## Stage 3: Reconciliation and Merge Logic

The most sophisticated aspect of drawDB's DBML import is its **reconciliation engine**. Rather than replacing the entire diagram, the system performs an intelligent merge:

**Matching by Identity**
- Tables match by `name` (case-sensitive)
- Columns match by `name` within matched tables
- Enums match by `name` globally

**Update Strategies**
- **Preservation of UI state**: Existing table positions, colors, and manual layout adjustments are retained
- **Field-level merges**: When a column exists in both source and target, properties are updated individually; new columns are appended, removed columns are flagged
- **Relationship rewiring**: Foreign keys referencing matched tables are recreated; dangling references generate warnings

**Conflict Detection**
The reconciler identifies problematic scenarios:

| Conflict Type | Resolution Behavior |
|-------------|-------------------|
| Name collision on new table | Append numeric suffix, flag for review |
| Column type change | Update type, preserve notes/position |
| Primary key change | Update index structure, warn if data loss |
| Circular reference creation | Block and surface error dialog |

Conflicts are collected in a `warnings` array returned alongside the updated database, enabling the UI to present a reconciliation report before finalizing changes.

## Stage 4: State Synchronization and Rendering

After reconciliation completes, the modified `database` object flows into drawDB's **global state manager** (implemented via React Context or similar). This triggers:

1. Re-render of the diagram canvas with new/updated tables
2. Synchronization of the side panel's schema browser
3. Invalidation of derived views (SQL export, share links)
4. Optional activation of **auto-arrange** ([`src/utils/autoArrange.js`](https://github.com/drawdb-io/drawdb/blob/main/src/utils/autoArrange.js)) to position newly added tables without overlapping existing ones

The entire pipeline operates synchronously for typical schemas, keeping the UI responsive even for diagrams with dozens of tables.

## Round-Trip Integrity: The Export Counterpart

DrawDB ensures **bidirectional fidelity** through `toDBML` in **[`src/utils/exportAs/dbml.js`](https://github.com/drawdb-io/drawdb/blob/main/src/utils/exportAs/dbml.js)**. This function walks the internal model and emits DBML that will re-import to an equivalent diagram—preserving names, types, relationships, and notes. The export process:

- Orders tables topologically to respect foreign key dependencies
- Includes `ref` statements for all relationships
- Preserves custom attributes as DBML settings
- Generates human-readable formatting with consistent indentation

This symmetry allows workflows like: export → edit in external DBML editor → re-import → minimal diff in reconciliation.

## Key Source Files

| Responsibility | Path |
|--------------|------|
| DBML import parser & reconciler | [`src/utils/importFrom/dbml.js`](https://github.com/drawdb-io/drawdb/blob/main/src/utils/importFrom/dbml.js) |
| DBML export generator | [`src/utils/exportAs/dbml.js`](https://github.com/drawdb-io/drawdb/blob/main/src/utils/exportAs/dbml.js) |
| Auto-arrangement utilities | [`src/utils/autoArrange.js`](https://github.com/drawdb-io/drawdb/blob/main/src/utils/autoArrange.js) |
| Supported format registry | [`src/data/constants.js`](https://github.com/drawdb-io/drawdb/blob/main/src/data/constants.js) |
| Code editor integration | [`src/components/CodeEditor/setUpDBML.js`](https://github.com/drawdb-io/drawdb/blob/main/src/components/CodeEditor/setUpDBML.js) |

## Summary

- **Parse**: `fromDBML` uses `@dbml/core` to convert DBML text into a structured AST
- **Map**: The AST walker translates DBML constructs into drawDB's native table, field, enum, and relationship objects
- **Reconcile**: Name-based matching preserves existing UI state while merging schema changes, with explicit conflict detection
- **Sync**: The updated database flows to React state, triggering canvas re-render and optional auto-arrangement
- **Round-trip**: `toDBML` ensures exported DBML re-imports with minimal reconciliation churn

## Frequently Asked Questions

### What DBML syntax features does drawDB support?

DrawDB supports the complete DBML specification including table definitions, enums, relationships (`ref`), indexes, notes, and column attributes (`pk`, `unique`, `not null`, `default`). The parser handles both inline and block-style relationship declarations.

### How does drawDB handle DBML import errors?

Syntax errors from `@dbml/core` propagate to the code editor component with line numbers and descriptive messages. The user must correct the DBML before import proceeds; partial or failed imports never modify the existing diagram.

### Can I import DBML into a non-empty diagram without losing my layout?

Yes. The reconciliation engine matches existing tables by name and preserves their position coordinates, colors, and manual adjustments. Only schema-level changes (added/removed columns, type changes) are applied; visual properties remain intact.

### What happens if my DBML defines a table that already exists with different columns?

The reconciler performs a field-level merge: matching columns by name update their properties, new columns are appended, and removed columns are flagged in the warnings report. The table's identity and position are preserved throughout.