# DBML Reader Reads Database Markup Language (DBML) files and extracts database schema information. ## Overview The DBML Reader parses `.dbml` files that define database schemas using the DBML syntax (used by dbdiagram.io) and converts them into RelSpec's internal database model representation. ## Features - Parses DBML syntax - Extracts tables, columns, and relationships - Supports DBML-specific features: - Table groups and notes - Enum definitions - Indexes - Foreign key relationships ## Usage ### Basic Example ```go package main import ( "fmt" "git.warky.dev/wdevs/relspecgo/pkg/readers" "git.warky.dev/wdevs/relspecgo/pkg/readers/dbml" ) func main() { options := &readers.ReaderOptions{ FilePath: "/path/to/schema.dbml", } reader := dbml.NewReader(options) db, err := reader.ReadDatabase() if err != nil { panic(err) } fmt.Printf("Found %d schemas\n", len(db.Schemas)) } ``` ### CLI Example ```bash # Read DBML file and convert to JSON relspec --input dbml --in-file schema.dbml --output json --out-file schema.json # Convert DBML to GORM models relspec --input dbml --in-file database.dbml --output gorm --out-file models.go ``` ## Example DBML File ```dbml Table users { id bigserial [pk, increment] username varchar(50) [not null, unique] email varchar(100) [not null] created_at timestamp [not null, default: `now()`] Note: 'Users table' } Table posts { id bigserial [pk] user_id bigint [not null, ref: > users.id] title varchar(200) [not null] content text indexes { user_id (user_id, created_at) [name: 'idx_user_posts'] } } Ref: posts.user_id > users.id [delete: cascade] ``` ## DBML Features Supported - Table definitions with columns - Primary keys (`pk`) - Not null constraints (`not null`) - Unique constraints (`unique`) - Default values (`default`) - Inline references (`ref`) - Standalone `Ref` blocks - Indexes and composite indexes - Table notes and column notes - Enums - Dialect directives (`@postgres:` / `@sqlite:` — see below) ## Dialect directives Lines of the form `@[()]: ` embed database-specific features that plain DBML cannot express (partitioning, `WITHOUT ROWID`, tablespaces, index storage parameters, …). They are stored losslessly on the relevant object's `Metadata` and round-trip unchanged through the DBML writer; the PostgreSQL and SQLite writers translate the ones they understand to SQL. ```dbml @postgres: search_path myapp Table myapp.events { id bigint [pk] created_at timestamp [not null] @postgres(id): identity always @postgres: partition by RANGE (created_at) @sqlite: without rowid indexes { (created_at) [name: 'idx_events_created'] @postgres: with (fillfactor=90) } } ``` | Position | Attaches to | |----------|-------------| | Before the first `Table {` | database | | Table body, no `(target)` | that table | | Table body, `(col)` target | column `col` (error if unknown) | | Inside `indexes { }` | the most recently listed index entry | `args` is preserved verbatim; the **key** (lowercased first token) drives duplicate detection. Repeated directives are kept in order; catalog "singleton" keys error on a second occurrence at the same location. All errors are line-numbered. `ReaderOptions.StrictDirectives` (CLI `--strict-directives`) turns an unknown namespace or key into an error instead of preserving it silently. See [`docs/DBML_DIRECTIVES.md`](../../../docs/DBML_DIRECTIVES.md) for the full grammar and the supported-directive matrix. ## Notes - DBML is designed for database documentation and diagramming - Schema name defaults to `public` - Relationship cardinality is preserved