# 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 `@<namespace>[(<column>)]: <args>` 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
