Skip to content

A more comprehensive test corpus, with expected ASTs in JSON form, is planned for a future release. The grammar repository (grammar/) currently contains the grammar file (xDBML.g4) and this reference test corpus only.


§17.1 Version declaration ​

VALID -- bare version ​

xdbml: 0.1
Project p { targets: Oracle }

VALID -- with experimental opt-in ​

xdbml: 0.1
experimental: [graph_path_expressions]

Project p { targets: Oracle }

VALID -- DBML compatibility (no version declaration) ​

Table users {
  id int [pk]
  email varchar
}

INVALID -- version declared after other constructs ​

Project p { }
xdbml: 0.1

Expected error: "version declaration must precede all other constructs."


§17.2 Nested / hierarchical structures ​

VALID -- nested object ​

xdbml: 0.1

Entity customers {
  id int [pk]
  address object {
    street varchar [not null]
    city   varchar [not null]
  }
}

VALID -- array of named objects ​

xdbml: 0.1

Entity orders {
  id int [pk]
  line_items array [
    line_item object {
      sku      varchar
      quantity int
    }
  ]
}

VALID -- heterogeneous tuple ​

xdbml: 0.1

Entity customers {
  id int [pk]
  addresses array [
    [0] billing  object {
      street varchar
      city varchar
    }
    [1] shipping object {
      street varchar
      city varchar
    }
  ]
}

VALID -- recursive named type ​

xdbml: 0.1

Type TreeNode {
  value int
  children array [child TreeNode]
}

Entity org_chart {
  root_id int [pk]
  tree    TreeNode
}

INVALID -- tuple positions not contiguous ​

xdbml: 0.1

Entity bad {
  arr array [
    [0] first  object { x int }
    [2] third  object { x int }
  ]
}

Expected error (semantic-analysis pass, not grammar): "tuple positions must be contiguous starting at 0."


§17.3 Polymorphism ​

VALID -- oneOf with discriminator ​

xdbml: 0.1

Entity payments {
  method oneOf {
    card object {
      last4 varchar(4)
      brand varchar
    }
    bank object { iban varchar }
  } [discriminator: method_kind]
}

VALID -- union (scalar type alternatives) ​

xdbml: 0.1

Entity records {
  score      union [int, decimal, null]
  legacy_id  union [string, int] [not null]
}

VALID -- array element-type shorthand (folds to a union) ​

xdbml: 0.1

Entity records {
  ids     array [int, varchar]          // shorthand for array [union [int, varchar]]
  mixed   array [int, varchar, null]
  tagged  array [union [int, varchar]]   // explicit form, still valid
  labels  set [int, varchar]             // set takes the same shorthand
}

VALID -- polymorphism inside an array ​

xdbml: 0.1

Entity event_log {
  events array [
    event oneOf {
      user_event object {
        type varchar
        user_id objectId
        action varchar
      }
      item_event object {
        type varchar
        item_id objectId
        qty int
      }
    } [discriminator: type]
  ]
}

§17.5 JSON-with-schema ​

VALID -- opaque JSON ​

xdbml: 0.1

Entity api_logs {
  payload json
  response variant
}

VALID -- JSON with inline schema ​

xdbml: 0.1

Entity orders {
  id int [pk]
  payload json {
    shipping_address object {
      street varchar
      city   varchar
    }
    items array [
      item object {
        sku varchar
        quantity int
      }
    ]
  }
}

VALID -- BSON types ​

xdbml: 0.1

Container app [type: database] {
  Collection users {
    _id          objectId    [pk]
    email        varchar     [unique]
    balance      Decimal128
    last_login   Date
    avatar       BinData
  }
}

§17.6 Path syntax ​

VALID -- path on index ​

xdbml: 0.1

Entity orders {
  id int [pk]
  line_items array [
    line_item object {
      sku varchar
      quantity int
    }
  ]

  indexes {
    line_items.sku                  // implicit array iteration (allowed in indexes)
    line_items.[*].sku              // explicit array iteration
    line_items.[0].sku              // positional
  }
}

VALID -- quoted segment for non-identifier name ​

xdbml: 0.1

Entity legacy {
  data object {
    "user.id" varchar
  }

  indexes {
    data."user.id"
  }
}

VALID -- JSONPath alias (parses, normalizes to dot-prefixed) ​

xdbml: 0.1

Entity orders {
  id int [pk]
  items array [item object { sku varchar }]

  indexes {
    items[*].sku    // JSONPath alias; normalizes to items.[*].sku
  }
}

INVALID -- implicit array iteration in Ref source path ​

xdbml: 0.1

Entity orders {
  id int [pk]
  line_items array [item object { sku varchar }]
}

Entity products {
  sku varchar [pk]
}

Ref: orders.line_items.sku > products.sku

Expected error (semantic-analysis pass): "Ref source path crosses array; explicit .[*] required."

VALID -- explicit .[*] in Ref ​

Ref: orders.line_items.[*].sku > products.sku

§17.7 Container ​

VALID -- explicit container ​

xdbml: 0.1

Container core [type: schema] {
  Entity users {
    id int [pk]
    name varchar
  }
}

VALID -- container synonyms ​

xdbml: 0.1

Database orders_store {
  Collection orders {
    _id objectId [pk]
  }
}

Keyspace metrics {
  Table page_views {
    event_id varchar [pk]
  }
}

Namespace events {
  Record OrderPlaced {
    event_id varchar [pk]
  }
}

VALID -- cross-container reference ​

xdbml: 0.1

Container core [type: schema] {
  Entity customers {
    id int [pk]
  }
}

Container sales [type: schema] {
  Entity orders {
    id          int [pk]
    customer_id int [ref: > core.customers.id]
  }
}

Ref: sales.orders.customer_id > core.customers.id

VALID -- container-qualified Enum, declared and referenced by qualified name (DBML form) ​

enum core.job_status {
  created
  running
  done
}

Table core.jobs {
  id     int [pk]
  status core.job_status [not null]
}

INVALID -- the same Enum declared in a Container block and with a qualified name (duplicate-declaration) ​

xdbml: 0.5

Container core [type: schema] {
  Enum job_status {
    created
  }
}

enum core.job_status {
  queued
}

§17.8 Named types ​

VALID -- named type referenced from multiple entities ​

xdbml: 0.1

Type Address {
  street varchar
  city varchar
}

Entity customers {
  primary_address   Address
  alternate_address Address
}

Entity orders {
  shipping_address Address
}

INVALID -- named type shadows builtin ​

xdbml: 0.1

Type varchar {           // shadows builtin
  custom_field int
}

Expected error (semantic-analysis pass): "named type 'varchar' shadows built-in type keyword; rename or remove."


§17.9 AI-readiness settings ​

VALID -- all four AI-readiness settings ​

xdbml: 0.1

Entity customers {
  mrr_amount decimal(10,2) [
    synonyms: ['monthly revenue', 'recurring revenue'],
    business_term: 'https://glossary.acme.com/MRR',
    tags: ['finance', 'kpi'],
    granularity: month
  ]
}

VALID -- custom property with x_ prefix ​

xdbml: 0.1

Entity customers [
  x_governance_owner: 'finance-team@acme.com',
  x_collibra_asset_id: 'urn:collibra:asset:abc-123'
] {
  id int [pk]
  name varchar
}

§17.10 Cardinality ​

VALID -- compact form ​

xdbml: 0.1

Entity customers { id int [pk] }
Entity orders { id int [pk]; customer_id int }

Ref: orders.customer_id > customers.id

VALID -- explicit source/target cardinality ​

Ref: orders.customer_id > customers.id [source: '1..*', target: '1..1']

VALID -- four-key alternative form ​

Ref: pets.owner_id > people.id [
  min_source: 0, max_source: '*',
  min_target: 0, max_target: 1
]

§17.11 Edge ​

VALID -- basic edge ​

xdbml: 0.1

Entity Person { id int [pk]; name varchar }

Edge KNOWS [source: Person, target: Person] {
  since    date [not null]
  intimacy int  [minimum: 0, maximum: 10]
}

VALID -- edge with cardinality ​

Edge OWNS [source: Person, target: Pet,
           source_cardinality: '0..*', target_cardinality: '0..1'] {
  acquired_date date
}

VALID -- undirected edge ​

Edge FRIENDS_WITH [source: Person, target: Person, undirected: true] {
  since date
}

VALID -- multiple edges between same entities ​

Edge LIKES   [source: User, target: Post] { liked_at timestamp }
Edge SHARED  [source: User, target: Post] { shared_at timestamp }
Edge BLOCKED [source: User, target: Post] { blocked_at timestamp }

§17.12 View ​

VALID -- virtual view ​

xdbml: 0.1

View active_customers [materialized: false] {
  source_query: '''
    SELECT id, email, name
    FROM customers
    WHERE deleted_at IS NULL
  '''
  id    int [pk]
  email varchar
  name  varchar
}

VALID -- materialized view with refresh settings ​

View monthly_revenue [materialized: true,
                      refresh_schedule: 'daily',
                      source_database: 'Oracle'] {
  source_query: '''
    SELECT TRUNC(placed_at, 'MM') AS month, SUM(total) AS revenue
    FROM orders
    GROUP BY TRUNC(placed_at, 'MM')
  '''
  month   date          [pk]
  revenue decimal(15,2)
}

VALID -- view inside container ​

xdbml: 0.1

Container analytics [type: schema] {
  Entity orders {
    id int [pk]
    total decimal(10,2)
    placed_at timestamp
  }

  View daily_totals [materialized: true] {
    source_query: 'SELECT DATE(placed_at) day, SUM(total) total FROM orders GROUP BY DATE(placed_at)'
    day   date [pk]
    total decimal(10,2)
  }
}

End-to-end: polyglot model ​

VALID -- full polyglot model with Oracle, MongoDB, Avro, Neo4j ​

xdbml: 0.1

Project polyglot { targets: [Oracle, MongoDB, Avro, Neo4j] }

Type Address {
  street  varchar [not null]
  city    varchar [not null]
  country varchar
}

Type MonetaryAmount {
  amount   Decimal128 [not null]
  currency string     [not null, pattern: '^[A-Z]{3}$', minLength: 3, maxLength: 3]
}

Container core [type: schema, target: Oracle] {
  Entity customers {
    id              int     [pk]
    email           varchar [unique, not null, pattern: '^[^@]+@[^@]+$']
    display_name    varchar [not null]
    primary_address Address
  }
}

Container orders_store [type: database, target: MongoDB] {
  Collection orders {
    _id          objectId  [pk]
    customer_id  int32     [not null]
    placed_at    Date      [granularity: second]
    total        MonetaryAmount
    line_items   array [
      line_item object {
        sku        string [not null]
        quantity   int32  [not null, minimum: 1]
        unit_price MonetaryAmount
      }
    ]
    payment_method oneOf {
      card   object {
        last4 string [maxLength: 4]
        brand string
      }
      bank   object { iban string }
      wallet object {
        provider string
        account string
      }
    } [discriminator: method_kind]
  }
}

Container events [type: namespace, target: Avro] {
  Record OrderPlaced {
    event_id    string     [pk]
    occurred    long       [granularity: millisecond, not null]
    order_id    string     [not null]
    customer_id int        [not null]
    total       MonetaryAmount
  }
}

Container social [type: database, target: Neo4j] {
  Edge FOLLOWS [source: core.customers, target: core.customers,
                source_cardinality: '0..*', target_cardinality: '0..*'] {
    since      date    [not null]
    is_close   boolean [default: false]
  }
}

Ref: orders_store.orders.customer_id > core.customers.id [source: '1..*', target: '1..1']
Ref: orders_store.orders.line_items.[*].sku > catalog.products.sku
Ref: events.OrderPlaced.order_id > orders_store.orders._id

§14.7 Scalar Named Types (v0.2) ​

VALID -- scalar Named Type with validation settings ​

xdbml: 0.2

Type Email varchar [pattern: '^[^@]+@[^@]+$', tags: ['pii']]
Type CountryCode varchar [pattern: '^[A-Z]{2}$', minLength: 2, maxLength: 2]
Type Percentage decimal(5,2) [minimum: 0, maximum: 100]

Entity users {
  id      int [pk]
  email   Email
  country CountryCode
}

VALID -- scalar Named Type alongside object-shaped Type ​

xdbml: 0.2

Type Email varchar [pattern: '^[^@]+@[^@]+$']
Type Address {
  street  varchar [not null]
  city    varchar [not null]
}

Entity customers {
  id              int [pk]
  email           Email
  primary_address Address
}

VALID -- scalar Named Type referencing another Type ​

xdbml: 0.2

Type Email varchar [pattern: '^[^@]+@[^@]+$']
Type PII_Email Email [tags: ['pii', 'gdpr-subject']]

Entity customers {
  id    int [pk]
  email PII_Email
}

§26 Module system (v0.2) ​

VALID -- reuse * (import all) at file scope ​

xdbml: 0.2

Project app { targets: [PostgreSQL] }

reuse * from './catalog'

Container ordering [type: schema] {
  Entity orders {
    id          int [pk]
    customer_id int
  }
}

VALID -- selective import inside Container body ​

xdbml: 0.2

Project app { targets: [PostgreSQL] }

Container ordering [type: schema] {
  reuse { entity core.products, entity core.categories } from './catalog'

  Entity orders {
    id          int [pk]
    customer_id int
  }

  Entity order_lines {
    order_id   int [ref: > ordering.orders.id]
    product_id int [ref: > ordering.products.id]
    quantity   int
  }
}

VALID -- import with clone block ​

xdbml: 0.2

Project sales_data_product { targets: [Snowflake] }

Container facts [type: schema] {
  reuse { entity core.dim_customer } from './conformed-dimensions' [cloned_at: '2026-06-06T14:30:00Z'] {
    Entity dim_customer {
      id    int     [pk]
      email varchar [pattern: '^[^@]+@[^@]+$', tags: ['pii']]
    }
  }

  Entity fact_sales {
    customer_id int [ref: > facts.dim_customer.id]
    amount      decimal(10,2)
  }
}

VALID -- aliased import ​

xdbml: 0.2

Project app { targets: [PostgreSQL] }

Container app [type: schema] {
  reuse { entity users as auth_users } from './auth'
  reuse { entity users as billing_users } from './billing'

  Entity sessions {
    id              int [pk]
    auth_user_id    int [ref: > app.auth_users.id]
    billing_user_id int [ref: > app.billing_users.id]
  }
}

VALID -- field-level import at file scope, used as a field type ​

xdbml: 0.2

Project new_system { targets: [PostgreSQL] }

reuse { field ops.customer_legacy.legacy_email as contact_email } from './legacy-customer' [cloned_at: '2026-06-06T14:30:00Z'] {
  contact_email varchar [pattern: '^[^@]+@[^@]+$', tags: ['pii']]
}

Container app [type: schema] {
  Entity new_customer {
    id            int           [pk]
    contact_email contact_email
  }
}

VALID -- use (non-transitive) for private internal imports ​

xdbml: 0.2

use { type InternalAuditFields } from './internal-helpers'

Container ordering [type: schema] {
  Entity orders {
    id          int [pk]
    audit       InternalAuditFields
    customer_id int
  }
}

VALID -- multi-line and comma-separated equivalence ​

xdbml: 0.2

// Multi-line form
reuse {
  entity products
  entity categories
  type Email
} from './catalog'

// Equivalent comma-separated single-line form
reuse { entity products, entity categories, type Email } from './catalog'

VALID -- reference-only directive (no clone block, DBML-compatible) ​

xdbml: 0.2

Project order_system { targets: [PostgreSQL] }

Container ordering [type: schema] {
  reuse { entity core.products } from './catalog'

  Entity orders {
    id          int [pk]
    customer_id int
  }
}

INVALID -- field import inside Container body ​

xdbml: 0.2

Container app [type: schema] {
  reuse { field core.dim_customer.email } from './conformed-dimensions'

  Entity new_customer {
    id    int [pk]
    email email
  }
}

Expected semantic error: "field imports may only appear at file scope (§25.5)."

INVALID -- absolute path ​

xdbml: 0.2

reuse * from '/absolute/path/to/file.xdbml'

Expected error: "import path must begin with './' or '../' (relative paths only in v0.2 phase 1)."

INVALID -- URL path (deferred to a later phase) ​

xdbml: 0.2

reuse * from 'https://example.com/schemas/users.xdbml'

Expected error: "URL imports are reserved for a later phase; v0.2 phase 1 supports relative paths only."

INVALID -- v0.1 file using v0.2-only constructs ​

xdbml: 0.1

reuse { entity core.products } from './catalog'

Expected error: "the module system (use/reuse directives) requires xdbml: 0.2 or later; this document declares xdbml: 0.1."

INVALID -- unrecognized element-type slot value ​

xdbml: 0.2

reuse { widget core.products } from './catalog'

Expected error: "unrecognized element type 'widget'; expected one of: table, entity, collection, record, enum, tablepartial, note, schema, container, tablegroup, type, edge, view, diagramview, field."

INVALID -- importing Project ​

xdbml: 0.2

reuse { project some_project } from './other'

Expected error: "Project declarations are not importable (§26.4)."


§10 Checks -- entity-level constraints (v0.2) ​

VALID -- multi-column check expression ​

xdbml: 0.2

Entity users {
  id     int [pk]
  wealth decimal(15,2)
  debt   decimal(15,2)
  checks {
    `debt + wealth >= 0` [name: 'chk_positive_net_worth']
  }
}

VALID -- multiple checks with and without names ​

xdbml: 0.2

Entity reservations {
  id         int  [pk]
  start_date date
  end_date   date
  checks {
    `start_date <= end_date`        [name: 'chk_valid_date_range']
    `end_date - start_date <= 30`   [name: 'chk_max_30_days']
    `start_date >= CURRENT_DATE`
  }
}

VALID -- check with note ​

xdbml: 0.2

Entity orders {
  id     int [pk]
  status varchar
  shipped_at timestamp
  checks {
    `(status != 'shipped') OR (shipped_at IS NOT NULL)` [
      name: 'chk_shipped_has_timestamp',
      note: 'A shipped order must record the shipment timestamp.'
    ]
  }
}

VALID -- entity with both checks and indexes ​

xdbml: 0.2

Entity inventory {
  id        int [pk]
  sku       varchar
  warehouse varchar
  qty       int
  indexes {
    (sku, warehouse) [unique]
  }
  checks {
    `qty >= 0` [name: 'chk_non_negative_qty']
  }
}

INVALID -- check expression not backtick-wrapped ​

xdbml: 0.2

Entity users {
  id     int [pk]
  wealth decimal(15,2)
  checks {
    wealth >= 0
  }
}

Expected error: "check expressions must be backtick-wrapped EXPRESSION_LITERAL values."


§11.9 Relationship settings -- inactive and color (v0.2) ​

VALID -- inactive flag on Ref ​

xdbml: 0.2

Entity posts {
  id      int [pk]
  user_id int
}

Entity users {
  id int [pk]
}

Ref: posts.user_id > users.id [inactive]

VALID -- inactive + color + note ​

xdbml: 0.2

Entity audit_log {
  id      int [pk]
  user_id int
}

Entity users {
  id int [pk]
}

Ref: audit_log.user_id > users.id [
  inactive,
  color: '#999999',
  note: 'Historical FK; superseded by audit_ref table'
]

VALID -- color alone (active relationship with custom color) ​

xdbml: 0.2

Entity orders {
  id          int [pk]
  customer_id int
}

Entity customers {
  id int [pk]
}

Ref: orders.customer_id > customers.id [color: '#3f51b5']

§16.2 TableGroup color (v0.2) ​

VALID -- TableGroup with color and note ​

xdbml: 0.2

Entity orders { id int [pk] }
Entity order_lines { id int [pk] }
Entity invoices { id int [pk] }

TableGroup ecommerce [color: '#3498DB', note: 'Commerce-side entities'] {
  orders
  order_lines
  invoices
}

VALID -- TableGroup with only color ​

xdbml: 0.2

Entity users { id int [pk] }
Entity sessions { id int [pk] }

TableGroup auth [color: '#FF5722'] {
  users
  sessions
}

§25 Records -- expanded forms (v0.2) ​

VALID -- records inside entity (implicit column list, v0.1 syntax) ​

xdbml: 0.2

Entity users {
  id    int [pk]
  name  varchar
  email varchar
  records {
    1, 'Alice', 'alice@example.com'
    2, 'Bob',   'bob@example.com'
  }
}

VALID -- top-level records with explicit column list ​

xdbml: 0.2

Entity users {
  id    int [pk]
  name  varchar
  email varchar
}

records users (id, name, email) {
  1, 'Alice', 'alice@example.com'
  2, 'Bob',   'bob@example.com'
}

VALID -- top-level records with cross-container reference ​

xdbml: 0.2

Container core [type: schema] {
  Entity users {
    id    int [pk]
    email varchar
  }
}

records core.users (id, email) {
  1, 'alice@example.com'
  2, 'bob@example.com'
}

VALID -- value forms (string, number, boolean, null, date, enum, backtick) ​

xdbml: 0.2

Enum Status { active inactive pending }

Entity events {
  id          int [pk]
  occurred_at timestamp
  status      Status
  is_archived boolean
  payload     varchar
  records {
    1, '2026-06-10T14:30:00Z', Status.active,   true,  'string value'
    2, '2026-06-11T09:00:00Z', Status.pending,  false, null
    3, `gen_random_uuid()`,    Status.inactive, null,  '''multi
line string'''
  }
}

§12 SupertypeGroup (v0.5) ​

VALID -- one group with completeness and exclusivity ​

xdbml: 0.5

Entity Party {
  party_id int [pk]
  name varchar [not null]
}
Entity Person {
  birth_date date
}
Entity Organization {
  legal_form varchar
}

SupertypeGroup legal_nature [supertype: Party, completeness: total, exclusivity: disjoint] {
  Person
  Organization
}

VALID -- materialization settings, a per-subtype strategy, and TableGroup-style separators ​

xdbml: 0.5

Entity Vehicle { vehicle_id int [pk] }
Entity Car { seats int }
Entity Motorcycle { sidecar boolean }
Entity Truck { payload decimal }

SupertypeGroup vehicle_kind [supertype: Vehicle, completeness: total, exclusivity: disjoint, strategy: roll_up, merge: flat, discriminator: kind] {
  Car, Motorcycle; Truck [strategy: roll_down]
}

VALID -- aliases normalize to canonical values ​

xdbml: 0.5

Entity A { id int [pk] }
Entity B { b int }
Entity C { c int }

SupertypeGroup g [supertype: A, completeness: complete, exclusivity: non_exclusive, strategy: joined] {
  B
  C [strategy: table_per_class]
}

VALID -- several axes on one supertype, and a second level ​

xdbml: 0.5

Entity Party { party_id int [pk] }
Entity Person { birth_date date }
Entity Organization { legal_form varchar }
Entity Customer { since date }
Entity Supplier { terms varchar }
Entity Employee { hire_date date }

SupertypeGroup legal_nature [supertype: Party, completeness: total, exclusivity: disjoint] {
  Person
  Organization
}
SupertypeGroup business_role [supertype: Party, completeness: partial, exclusivity: overlapping] {
  Customer
  Supplier
}
SupertypeGroup person_role [supertype: Person] {
  Employee
}

VALID -- a relationship names a subtype as an entity-level endpoint ​

xdbml: 0.5

Entity Party { party_id int [pk] }
Entity Employee { hire_date date }
Entity Assignment {
  assignment_id int [pk]
  employee_id int [not null]
}

SupertypeGroup g [supertype: Party] {
  Employee
}

Ref assigned: Assignment.employee_id > Employee [source: '0..*', target: '1..1']

INVALID -- group without a name (parse error) ​

xdbml: 0.5

Entity A { id int [pk] }
Entity B { b int }

SupertypeGroup [supertype: A] {
  B
}

INVALID -- an entity listed as a subtype in two groups (subtype-in-multiple-groups) ​

xdbml: 0.5

Entity Party { party_id int [pk] }
Entity Worker { worker_id int [pk] }
Entity Person { birth_date date }

SupertypeGroup legal_nature [supertype: Party] {
  Person
}
SupertypeGroup workforce [supertype: Worker] {
  Person
}

INVALID -- a subtype redeclares a supertype attribute (supertype-attribute-redeclared) ​

xdbml: 0.5

Entity Party {
  party_id int [pk]
  name varchar
}
Entity Person {
  name varchar
}

SupertypeGroup legal_nature [supertype: Party] {
  Person
}

INVALID -- discriminator on an overlapping group (discriminator-on-overlapping-group) ​

xdbml: 0.5

Entity Party { party_id int [pk] }
Entity Customer { since date }
Entity Supplier { terms varchar }

SupertypeGroup business_role [supertype: Party, exclusivity: overlapping, discriminator: role] {
  Customer
  Supplier
}

INVALID -- a newer version than the parser supports (unsupported-version) ​

xdbml: 0.7

Entity A { id int [pk] }

§10 Constraints -- keys and checks (v0.6) ​

VALID -- named composite primary key, composite unique key, quoted checks ​

xdbml: 0.6

Table pitstops {
  raceid   int
  driverid int
  stop     int
  lap      int

  constraints {
    (raceid, driverid, stop) [pk, name: 'pk_pitstops']
    (raceid, driverid, lap)  [unique, name: 'uk_pitstops_lap']
    'stop >= 1'              [name: 'chk_pitstops_stop']
    `lap IN (1, 2)`
  }
}

VALID -- a unique key on a nested field ​

xdbml: 0.6

Collection profiles {
  _id      objectId [pk]
  identity object {
    driverref string
  }

  constraints {
    identity.driverref [unique, name: 'uk_profiles_driverref']
  }
}

VALID -- a field named constraints ​

xdbml: 0.6

Table rules {
  id          int [pk]
  constraints varchar
}

VALID -- composite foreign key referencing a unique key ​

xdbml: 0.6

Table results {
  resultid int [pk]
  raceid   int
  driverid int
  constraints {
    (raceid, driverid) [unique]
  }
}

Table pitstops {
  raceid   int
  driverid int
}

Ref: pitstops.(raceid, driverid) > results.(driverid, raceid)

INVALID -- key line with neither pk nor unique (invalid-key-flags) ​

xdbml: 0.6

Table t {
  id int
  constraints {
    id [name: 'k']
  }
}

INVALID -- key path crossing an array (key-path-crosses-collection) ​

xdbml: 0.6

Table t {
  id   int [pk]
  tags array [object {
    code varchar
  }]
  constraints {
    tags.code [unique]
  }
}

INVALID -- second primary key (duplicate-primary-key) ​

xdbml: 0.6

Table t {
  id   int [pk]
  code int
  constraints {
    code [pk]
  }
}

INVALID -- null on a primary key field (null-in-primary-key) ​

xdbml: 0.6

Table t {
  id int [pk, null]
}

INVALID -- relationship to a field that is not a key (ref-target-not-key) ​

xdbml: 0.6

Table customers {
  id    int [pk]
  email varchar
}

Table orders {
  customer_email varchar
}

Ref: orders.customer_email > customers.email

INVALID -- constraints block in a v0.5 document (construct-requires-version) ​

xdbml: 0.5

Table t {
  id int
  constraints {
    id [pk]
  }
}

§3.9 List bodies (v0.6.1) ​

VALID -- Enum values separated by commas, as AI assistants often write them ​

Enum service_status {
  'scheduled' [note: 'Appointment booked'],
  'in_progress' [note: 'Currently being serviced'],
  'on_hold' [note: 'Paused, awaiting parts or decision'],
  'cancelled' [note: 'Cancelled by customer or shop']
}

VALID -- fields on one line, commas and semicolons, a trailing separator ​

xdbml: 0.6

Entity service_visits { id int [pk], status service_status; visited_at timestamp, }

Enum service_status { scheduled, in_progress, completed }

VALID -- separators in constraints, object, oneOf and View bodies ​

xdbml: 0.6

Entity payments {
  id int,
  amount decimal(12,2),
  method oneOf {
    card object { last4 varchar, brand varchar },
    cash object { change_given decimal(12,2) },
  } [discriminator: kind],
  constraints { (id) [pk], 'amount > 0' },
}

View large_payments {
  source_query: 'SELECT id, amount FROM payments WHERE amount > 1000',
  id int,
  amount decimal(12,2),
}

INVALID -- a comma between index entries ​

xdbml: 0.6

Entity people {
  id    int [pk]
  email varchar
  name  varchar
  indexes {
    email, name
  }
}

Expected error: a comma between index entries is not accepted; a composite index is written (email, name).

INVALID -- a separator inside a field declaration ​

xdbml: 0.6

Entity people { id, int }

§3.10 Keywords and field names (v0.6.1) ​

VALID -- fields named note, records, indexes and checks beside a table note and an index block ​

Table appointments {
  id      int [pk]
  note    varchar
  records int
  indexes varchar
  checks  varchar
  Note: 'One row per booked appointment'
  indexes {
    note
  }
}

VALID -- a View field named source_query ​

xdbml: 0.6

View query_log {
  source_query: 'SELECT id, source_query FROM query_history'
  id           int
  source_query varchar
}

§4 Version declaration with a patch number (v0.6.1) ​

VALID -- xdbml: 0.6.1 ​

xdbml: 0.6.1

Entity A { id int [pk] }

INVALID -- a patch release newer than the parser supports (unsupported-version) ​

xdbml: 0.6.2

Entity A { id int [pk] }

§14 View settings and source query (v0.6.1) ​

VALID -- settings in the brackets, the source query in the body ​

xdbml: 0.6

View monthly_revenue [materialized: true, refresh_schedule: 'daily'] {
  source_query: 'SELECT month, SUM(total) AS revenue FROM orders GROUP BY month'
  month   date [pk]
  revenue decimal(15,2)
}

INVALID -- a setting written in the body ​

xdbml: 0.6

View monthly_revenue {
  materialized: true
  month date [pk]
}

Expected error: materialized is a setting and goes in the brackets after the View name.

VALID with a warning -- source_query in the brackets (source-query-in-settings) ​

xdbml: 0.6

View active_customers [source_query: 'SELECT id FROM customers'] {
  id int [pk]
}

The bracketed query is not the view's source query.

VALID with a warning -- a second source query (duplicate-source-query) ​

xdbml: 0.6

View active_customers {
  source_query: 'SELECT id FROM customers'
  source_query: 'SELECT id FROM clients'
  id int [pk]
}

The first source_query: is the view's source query.


§10.3 A key declared twice (v0.6.1) ​

INVALID -- a primary key written inline and restated in constraints (duplicate-primary-key) ​

xdbml: 0.6

Table customers {
  customer_id int [pk]
  constraints {
    (customer_id) [pk]
  }
}

VALID with a warning -- a unique key written inline and restated in constraints (duplicate-unique-key) ​

xdbml: 0.6

Table customers {
  customer_id int [pk]
  tax_id      varchar(50) [unique]
  constraints {
    tax_id [unique, name: 'uk_customers_tax_id']
  }
}

v0.6.2 DBML forms and spec rules ​

VALID -- an unquoted color, as DBML writes it (§3.3) ​

xdbml: 0.6

Table customers [headercolor: #3498DB] {
  id int [pk]
}

VALID -- a relationship names an entity by its alias (§7.4) ​

xdbml: 0.6

Table very_long_customer_table as C {
  id int [pk]
}

Table orders {
  id          int [pk]
  customer_id int [ref: > C.id]
}

VALID -- target array types keep their brackets (§8.1) ​

xdbml: 0.6

Table posts {
  id   int [pk]
  tags text[]
  refs varchar(20)[]
}

INVALID -- a Container without a target in a Project with several (container-target-missing, §5.2) ​

xdbml: 0.6

Project fleet {
  targets: [PostgreSQL, MongoDB]
}

Schema core {
  Table vehicles { id int [pk] }
}

INVALID -- a Container target the Project does not list (container-target-not-in-project, §5.2) ​

xdbml: 0.6

Project fleet {
  targets: [PostgreSQL]
}

Schema core [target: Oracle] {
  Table vehicles { id int [pk] }
}

INVALID -- a Type and an Entity with one name (name-collision, §15.5) ​

xdbml: 0.6

Type customers {
  a int
}

Table customers {
  id int [pk]
}

§18 Diagram views (v0.6.3) ​

VALID -- Containers narrowed by the entities named beside them (§18.1, §18.2) ​

xdbml: 0.6

Schema sales {
  Table orders      { id int [pk] }
  Table order_lines { id int [pk] }
  Table returns     { id int [pk] }
}

Schema billing {
  Table invoices { id int [pk] }
}

Schema crm {
  Table customers { id int [pk] }
}

DiagramView order_flow {
  Containers {
    sales
    billing
  }
  Tables {
    sales.orders
    sales.order_lines
    crm.customers
  }
}

VALID -- the DBML form in a document without a version declaration (§18.5) ​

Table core.users      { id int [pk] }
Table analytics.events { id int [pk] }

DiagramView "Sales team" {
  Tables { core.users }
  Schemas { analytics }
}

DiagramView everything { * }

VALID -- commas and semicolons in category lists (§3.9) ​

xdbml: 0.6

Table a { id int [pk] }
Table b { id int [pk] }

DiagramView v {
  Tables { a, b; }
}

INVALID -- an Edges category (§18.1) ​

xdbml: 0.6

Table a { id int [pk] }

DiagramView v {
  Edges { * }
}

INVALID -- a DiagramView inside a Container (§18.1) ​

xdbml: 0.6

Schema sales {
  Table orders { id int [pk] }
  DiagramView v { Tables { orders } }
}

INVALID -- a name that matches nothing (unresolved-diagram-view-name, §18.3) ​

xdbml: 0.6

Table orders { id int [pk] }

DiagramView v {
  Tables { order }
}

INVALID -- an unqualified name held by two Containers (ambiguous-diagram-view-name, §18.3) ​

xdbml: 0.6

Schema a { Table orders { id int [pk] } }
Schema b { Table orders { id int [pk] } }

DiagramView v {
  Tables { orders }
}

INVALID -- a database view under Tables (diagram-view-wrong-category, §18.3) ​

xdbml: 0.6

Table orders { id int [pk] }

View revenue [materialized: true] {
  source_query: 'SELECT 1 AS total'
  total int
}

DiagramView v {
  Tables { revenue }
}

INVALID -- Schemas and Containers in one diagram view (duplicate-diagram-view-category, §18.1) ​

xdbml: 0.6

Schema sales { Table orders { id int [pk] } }

DiagramView v {
  Schemas { sales }
  Containers { sales }
}

VALID with a warning -- a category written twice without a version declaration (duplicate-diagram-view-category, §18.6) ​

Table a { id int [pk] }
Table b { id int [pk] }

DiagramView v {
  Tables { a }
  Tables { b }
}

INVALID -- two diagram views with one name (duplicate-diagram-view, §18.1) ​

xdbml: 0.6

Table a { id int [pk] }

DiagramView v { Tables { a } }
DiagramView v { Tables { * } }

INVALID -- Containers without a version declaration (construct-requires-version, §18.5) ​

Table core.users { id int [pk] }

DiagramView v {
  Containers { core }
}

Test runner ​

A reference TypeScript test harness (planned at grammar/test-runner.ts) parses each example, captures the resulting AST, and compares it against expected ASTs in grammar/expected/*.json. Implementations in other languages can run the same corpus with language-appropriate harnesses.

$ npx @xdbml/test-runner grammar/test-cases.md
PASS  §4.1.001   version declaration with experimental opt-in
PASS  §4.1.002   DBML document parses without xdbml: directive
FAIL  §10.6.005  expected error not raised: Ref source path crosses array
PASS  §6.1.001   explicit container with type
...
PASS rate: 247/250 (98.8%)

A conforming v0.2 implementation should achieve 100% pass rate on the published corpus, including v0.2 module system and scalar Named Type cases. A v0.1-only implementation should achieve 100% pass rate on the v0.1 subset and produce appropriate errors for v0.2 constructs.

Spec under Apache License 2.0 · Examples under CC0 1.0