Skip to content

Each case is labeled VALID (no error), VALID with a warning (a warning and no error), or INVALID (a parse error or at least one error diagnostic). The cases that import from ./catalog, ./auth, ./billing, ./crm, ./internal-helpers or ./other read the small files of grammar/fixtures/. Since v0.6.4, the reference parser's test suite runs every case and fails when a case and the parser disagree. A corpus with expected ASTs in JSON form is planned for a future release.


§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.6

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

Expected error: invalid-tuple-positions. Positions run from [0] without a gap, each once (§8.6).

VALID with a warning -- tuple positions not contiguous, in a document declaring v0.5 (invalid-tuple-positions) ​

xdbml: 0.5

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

No parser checked tuple positions before v0.6.4, so a document declaring a version earlier than 0.6 draws a warning (§8.6).

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 -- quoted names at the head of a path, in a key and in a relationship (v0.6.7) ​

xdbml: 0.6

Container sales {
  Entity "Work Order" {
    "Work Order ID" int [pk]
  }
}

Entity "Work Order Part" {
  "Work Order ID" int [ref: > "sales"."Work Order"."Work Order ID"]
  "Part ID"       int
  indexes {
    "Part ID"
  }
  constraints {
    ("Work Order ID", "Part ID") [pk]
  }
}

TableGroup operations {
  "sales"."Work Order"
  "Work Order Part"
}

VALID -- a dot inside quotes is part of the name (v0.6.7) ​

xdbml: 0.6

Container my {
  Table table {
    id int [pk]
  }
}

Table "my.table" {
  id int [pk]
}

Table other {
  a int
  b int
}

Ref: other.a > my.table.id
Ref: other.b > "my.table".id

INVALID -- a quoted name holding a dot does not name an entity of a Container (unresolved-entity, v0.6.7) ​

xdbml: 0.6

Container my {
  Table table {
    id int [pk]
  }
}

Table other {
  a int
}

Ref: other.a > "my.table".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 ​

xdbml: 0.6

Entity products {
  sku varchar [pk]
}

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

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.6

Type varchar {           // shadows builtin
  custom_field int
}

Expected error: named-type-shadows-builtin (§15.2).

VALID with a warning -- named type shadows builtin, in a document declaring v0.5 (named-type-shadows-builtin) ​

xdbml: 0.5

Type varchar {
  custom_field int
}

No parser reported this before v0.6.4, so a document declaring a version earlier than 0.6 draws a warning (§15.2).

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 ​

xdbml: 0.6

Entity customers {
  id int [pk]
}

Entity orders {
  id          int [pk]
  customer_id int
}

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

VALID -- four-key alternative form ​

xdbml: 0.6

Entity people {
  id int [pk]
}

Entity pets {
  id       int [pk]
  owner_id int
}

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

VALID -- optional marks on the operator, short, long and inline (DBML, v0.7) ​

Table users {
  id int [pk]
}

Table posts {
  id          int [pk]
  author_id   int
  owner_id    int
  reviewer_id int
  editor_id   int [ref: ?>? users.id]
}

Ref: posts.author_id >? users.id
Ref: users.id ?< posts.owner_id
Ref pair {
  posts.reviewer_id -? users.id [delete: cascade]
}

VALID with a warning -- a ? and a setting that disagree on an end (v0.7) ​

xdbml: 0.7

Entity customers {
  id int [pk]
}

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

Ref: orders.customer_id >? customers.id [target: '1..1']

INVALID -- a ? apart from the operator (DBML, v0.7) ​

Table users {
  id int [pk]
}

Table posts {
  author_id int
}

Ref: posts.author_id > ? users.id

VALID -- a ? on a relationship between entities (v0.7) ​

xdbml: 0.7

Entity Customer { }
Entity Order { }

Ref: Customer >? Order

§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 catalog [type: schema, target: Oracle] {
  Entity products {
    sku   varchar [pk]
    title varchar [not null]
  }
}

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: one item per line, no separator
reuse {
  entity products
  entity categories
  type Email
} from './catalog'

The import list is a list body (§3.9, §28.2): the single-line form of the next case imports the same three declarations. The two directives go in separate documents, since importing one declaration twice into one document declares it twice.

VALID -- comma-separated single-line import list ​

xdbml: 0.2

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: "an absolute path; a module source is read relative to the importing file, as in DBML (§28.13)."

INVALID -- a URL with a scheme other than https (§28.14) ​

xdbml: 0.3

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

Expected error: "Module source scheme 'http:' is not allowed; remote sources must use 'https://'."

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'''
  }
}

§19 Sticky notes (v0.7) ​

VALID -- a sticky note with a color and custom properties (DBML) ​

Table orders {
  id int [pk]
}

Note design_decision [color: #F4D03F, author: "docs"] {
  '''
  ## Line items stay in orders

  Reads outnumber writes **100 to 1**.
  '''
}

VALID -- color none, a quoted name, and a qualified name in the implicit Container (DBML) ​

Table core.users {
  id int [pk]
}

Note "Release plan" [color: none] {
  'Deprecated fields leave in release 4.2.'
}

Note core.legend {
  'Every table of core belongs to the web shop.'
}

VALID -- a sticky note in a Container, beside the Container's own note (v0.7) ​

xdbml: 0.7

Container sales [type: schema] {
  Note: 'Order management'

  Entity orders {
    id     int [pk]
    status varchar
  }

  Note order_states [color: #FBCFE8] {
    'An order moves from placed to shipped to delivered.'
  }
}

DiagramView order_flow {
  Tables {
    sales.orders
  }
  Notes {
    sales.order_states
  }
}

Metadata Note sales.order_states {
  owner: 'ops'
}

VALID -- a top-level Note without a name is the note of the project ​

Table orders {
  id int [pk]
}

Note: 'Reference model of the web shop'

VALID with a warning -- a second note of the project (duplicate-project-note, §19.5) ​

Project shop {
  Note: 'Reference model'
}

Note: 'Maintained by the data team'

INVALID -- a sticky note in a Container, without xdbml: 0.7 (§19.4) ​

Container sales [type: schema] {
  Note order_states {
    'placed, shipped, delivered'
  }
}

INVALID -- a color of another form (invalid-note-color, §19.3) ​

Note reminder [color: red] {
  'Review the owners'
}

INVALID -- settings on a Note without a name (§19.5) ​

Note [color: #F4D03F] {
  'Reference model'
}

INVALID -- a sticky note with an empty body (§19.2) ​

Note reminder {
}

INVALID -- two sticky notes of one name (duplicate-declaration, §19.2) ​

Note reminder {
  'one'
}

Note reminder {
  'two'
}

§24.7 Metadata blocks (v0.7) ​

VALID -- Table, Column, TableGroup and Note blocks (DBML) ​

Table users [owner: 'jane'] {
  id    int [pk]
  email varchar
}

TableGroup accounts {
  users
}

Note reminder {
  'Review the owners'
}

Metadata Table users {
  owner: 'data-team'
  note: 'Accounts of the web shop'
  headercolor: #3498DB
}

Metadata Column users.email {
  classification: 'confidential'
}

Metadata TableGroup accounts {
  color: #2ECC71
}

Metadata Note reminder {
  color: none
}

VALID -- Container, View, DiagramView and the entity and field synonyms (v0.7) ​

xdbml: 0.7

Container sales [type: schema] {
  Entity orders {
    id   int [pk]
    addr object {
      city varchar
    }
  }
  View open_orders {
    id int
  }
}

DiagramView order_flow {
  Tables {
    sales.orders
  }
}

Metadata Container sales {
  note: 'Sales schema'
  color: #E67E22
}

Metadata Entity sales.orders {
  x_owner: 'sales-ops'
}

Metadata Field orders.addr.city {
  note: 'City of delivery'
}

Metadata View open_orders {
  headercolor: #95A5A6
}

Metadata DiagramView order_flow {
  note: 'Order lifecycle'
}

INVALID -- an xDBML kind without a version declaration ​

Table users {
  id int [pk]
}

Metadata Entity users {
  owner: 'x'
}

INVALID -- a target that names nothing (DBML) ​

Table users {
  id int [pk]
}

Metadata Column users.nickname {
  owner: 'x'
}

INVALID -- a flag in the body (DBML) ​

Table users {
  id int
}

Metadata Column users.id {
  pk
}

INVALID -- a Metadata block inside a Container ​

xdbml: 0.7

Container core [type: schema] {
  Table users {
    id int
  }
  Metadata Table users {
    owner: 'x'
  }
}

VALID with a warning -- a block that changes the entity's own value (DBML) ​

Table users [owner: 'jane'] {
  id int [pk]
}

Metadata Table users {
  owner: 'scott'
}

VALID with a warning -- synonyms in a Metadata block read as a custom property ​

xdbml: 0.7

Entity customers {
  id int [pk]
}

Metadata Entity customers {
  synonyms: 'clients'
}

VALID with a warning -- a setting written twice in one list ​

Table users [owner: 'a', owner: 'b'] {
  id int [pk]
}

§26 Records -- DBML 10.3.1 forms (v0.7) ​

VALID -- example flag on a top-level declaration, after the column list (DBML) ​

Table users {
  id   int [pk]
  name varchar
}

records users (id, name) [example] {
  1, 'Alice'
  2, 'Bob'
}

VALID -- example flag inside a table, with and without a column list (DBML) ​

Table users {
  id   int [pk]
  name varchar
  records [example] {
    1, 'Alice'
  }
}

Table orders {
  id      int [pk]
  user_id int
  total   decimal
  records (id, user_id) [example] {
    10, 1
  }
}

VALID -- column list on records inside an entity ​

xdbml: 0.7

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

VALID -- empty fields read as null (DBML) ​

Table events {
  id      int [pk]
  kind    varchar
  payload varchar
}

records events (id, kind, payload) {
  1, , 'no kind'
  , 'orphan', 'no id'
  3, 'last payload empty',
}

INVALID -- a second records block inside an entity ​

Table users {
  id int [pk]
  records {
    1
  }
  records {
    2
  }
}

INVALID -- a records block inside an entity and a top-level declaration for it ​

xdbml: 0.7

Entity users {
  id   int [pk]
  name varchar
  records {
    1, 'Alice'
  }
}

records users (id, name) [example] {
  2, 'Bob'
}

VALID with a warning -- a second records block in a document declaring 0.6 ​

xdbml: 0.6

Entity users {
  id   int [pk]
  name varchar
}

records users (id) {
  1
}

records users (name) {
  'Alice'
}

INVALID -- the brackets before the column list ​

Table users {
  id int [pk]
  records [example] (id) {
    1
  }
}

INVALID -- a column of the list that is not a field of the entity ​

xdbml: 0.7

Entity users {
  id int [pk]
  records (id, nickname) {
    1, 'Al'
  }
}

§27 Data lineage (v0.7) ​

VALID -- the three forms of DBML, in a document without a version ​

Table raw_orders {
  id         int [pk]
  amount_usd decimal(12, 2)
}

Table stg_orders [dep: <- raw_orders] {
  order_id int [pk, dep: <- raw_orders.id]
  revenue  decimal(12, 2)
}

Table fct_orders {
  order_id int [pk]
  revenue  decimal(12, 2)
}

Dep: raw_orders.amount_usd -> stg_orders.revenue [note: 'Converted to the reporting currency']
Dep order_revenue: fct_orders.revenue <- stg_orders.revenue

Dep build_fct_orders [owner: 'analytics', color: #d97706] {
  stg_orders -> fct_orders
  materialized: 'table'
  Note: '''
    One row per order.
  '''
}

VALID -- entity to field and field to entity, a table built from its own previous state, and a cycle ​

Table orders {
  id         int [pk]
  line_items json
}

Table order_lines {
  order_id int
  sku      varchar
}

Table daily_kpis {
  day         date [pk]
  order_count int
}

Dep: orders -> daily_kpis.order_count
Dep: orders.line_items -> order_lines
Dep: daily_kpis -> daily_kpis
Dep: order_lines -> orders

VALID -- a block gathers the source fields of one target field, each with settings of its own ​

Table customers {
  id         int [pk]
  first_name varchar
  last_name  varchar
}

Table dim_customer {
  id        int [pk]
  full_name varchar
}

Dep full_name [note: 'Concatenates the first and the last name'] {
  customers.first_name -> dim_customer.full_name [color: #0891b2]
  customers.last_name  -> dim_customer.full_name
}

VALID -- Views, nested fields and a Container (§27.1) ​

xdbml: 0.7

Container stripe [type: database, target: 'Stripe API'] {
  Entity charges {
    id     varchar [pk]
    amount int
  }
}

Collection events {
  _id     objectId [pk]
  payload object {
    customer object {
      id int
    }
  }
  items array [object {
    sku varchar
  }]
}

Table payments {
  id           varchar [pk]
  amount_cents int
}

View payment_summary {
  customer_id int
  total       int
}

Dep: stripe.charges.amount -> payments.amount_cents
Dep: events.payload.customer.id -> payment_summary.customer_id
Dep: events.items.[*].sku -> payments.id
Dep: payments -> payment_summary

VALID -- an alias names the entity, two Deps share a name, and dep names a field ​

Table very_long_orders_table as O {
  id  int [pk]
  dep varchar
}

Table summary {
  id int [pk]
}

Dep load: O.id -> summary.id
Dep load: O -> summary

VALID with a warning -- a block with no dependency declares nothing (empty-dep, §27.2) ​

Table a {
  id int
}

Dep nothing_yet {
  note: 'To be mapped'
}

VALID with a warning -- dep without an arrow stays a custom property in a v0.6 document (invalid-dep-value, §27.9) ​

xdbml: 0.6

Table orders [dep: 'nightly batch'] {
  id int [pk]
}

INVALID -- dep without an arrow in a document without a version (invalid-dep-value, §27.2) ​

Table orders [dep: 'nightly batch'] {
  id int [pk]
}

INVALID -- a Dep in a Container (§27.4) ​

xdbml: 0.7

Container sales {
  Entity orders {
    id int
  }
  Dep: orders -> orders
}

INVALID -- a Dep in the body of a table (§27.4) ​

Table orders {
  id int
  Dep: orders -> orders
}

INVALID -- an endpoint that names no element (unresolved-dep-endpoint, §27.1) ​

Table orders {
  id int
}

Dep: payments.amount -> orders.id

INVALID -- an endpoint that names a field the entity does not declare (unresolved-dep-endpoint, §27.1) ​

Table orders {
  id int
}

Table payments {
  id int
}

Dep: payments.amount -> orders.id

INVALID -- an endpoint that names an Edge (invalid-dep-endpoint, §27.1) ​

xdbml: 0.7

Table person {
  id int [pk]
}

Edge knows [source: person, target: person] {
  since date
}

Dep: person -> knows

INVALID -- a composite endpoint (invalid-dep-endpoint, §27.1) ​

Table orders {
  a int
  b int
}

Table totals {
  t int
}

Dep: orders.(a, b) -> totals.t

INVALID -- a relationship operator in a dependency (§27.2) ​

Table a {
  id int
}

Table b {
  id int
}

Dep: a > b

INVALID -- a space inside the arrow (§27.2) ​

Table a {
  id int
}

Table b {
  id int
}

Dep: a - > b

INVALID -- three endpoints in one dependency (§27.2) ​

Table a {
  id int
}

Table b {
  id int
}

Table c {
  id int
}

Dep: a -> b -> c

INVALID -- two upstream elements in one dependency, without a version declaration (§27.2) ​

Table a {
  id int
}

Table b {
  id int
}

Table c {
  id int
}

Dep: a, b -> c

INVALID -- a block whose dependencies feed two elements (dep-mixed-downstream, §27.2) ​

Table a {
  id int
}

Table b {
  id int
}

Table c {
  id int
}

Dep load {
  a -> b
  a -> c
}

INVALID -- a block mixing a dependency between entities with one naming a field, without a version declaration (dep-mixed-level, §27.2) ​

Table a {
  id int
}

Table b {
  id int
}

Dep load {
  a -> b
  a.id -> b.id
}

INVALID -- an inline dep on a field naming an entity (dep-wrong-kind, §27.2) ​

Table a {
  id int
}

Table b {
  id int [dep: <- a]
}

INVALID -- an inline dep on an entity naming a field (dep-wrong-kind, §27.2) ​

Table a {
  id int
}

Table b [dep: <- a.id] {
  id int
}

INVALID -- a dep on a TablePartial (dep-in-partial, §27.2) ​

Table a {
  id int
}

TablePartial audited [dep: <- a] {
  loaded_at timestamp
}

INVALID -- a color that is no hex code (invalid-dep-color, §27.3) ​

Table a {
  id int
}

Table b {
  id int
}

Dep: a -> b [color: none]

INVALID -- a field depending on itself (dep-self-loop, §27.4) ​

Table a {
  id int
}

Dep: a.id -> a.id

INVALID -- a dependency stated twice, inline and as a Dep (duplicate-dep, §27.4) ​

Table a {
  id int
}

Table b [dep: <- a] {
  id int
}

Dep: a -> b

INVALID -- a Dep name qualified with a Container (§27.4) ​

Table a {
  id int
}

Dep sales.load: a -> a

§28.13 Module specifiers -- DBML 10.3.1 resolution (v0.7) ​

The cases import from grammar/fixtures/, where legacy.dbml is a DBML file with no .xdbml twin.

VALID -- an import without an extension reads a .dbml file ​

xdbml: 0.7

use { table legacy_orders } from './legacy'

VALID -- an explicit .dbml extension ​

xdbml: 0.7

use { table legacy_orders } from './legacy.dbml'

VALID -- a specifier without ./, read relative to the importing file as in DBML ​

use { table products } from 'catalog'

VALID -- a first folder that looks like a host is a folder (a clone keeps the case offline) ​

xdbml: 0.7

use { entity products } from 'acme.com/catalog' {
  Entity products {
    id int [pk]
  }
}

INVALID -- a Windows drive path, absolute, that no host resolves ​

xdbml: 0.7

use * from 'C:\\models\\catalog'

Expected error: "an absolute path; a module source is read relative to the importing file, as in DBML (§28.13)."

INVALID -- a specifier naming no file under either extension ​

xdbml: 0.7

use * from './missing'

Expected error: "Failed to read referenced module file '.../missing.xdbml' or '.../missing.dbml'."

§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.8

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.7.99

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 { * } }

VALID -- a note in the brackets, a note in the body, and a custom property (§18.1) ​

xdbml: 0.6

Table orders { id int [pk] }

DiagramView ordering [note: 'Orders only', x_owner: 'sales-ops'] {
  Tables { orders }
}

DiagramView ordering_too {
  Note: 'Orders only'
  Tables { orders }
}

VALID -- any key other than note is a custom property, prefixed or not, as on a TableGroup (§18.1, v0.7.1) ​

xdbml: 0.6

Table orders { id int [pk] }

DiagramView ordering [owner: 'sales-ops', "x-hackolade-id": 'ordering-4'] {
  Tables { orders }
}

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

Table orders { id int [pk] }

DiagramView ordering {
  Note: 'Orders only'
  Tables { orders }
}

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

Table core.users { id int [pk] }

DiagramView v {
  Containers { core }
}

§15.8 Internal definitions (v0.6.5) ​

VALID -- internal definitions in an entity, object and scalar forms (§15.8.1) ​

xdbml: 0.6

Project crm { targets: 'JSON Schema' }

Entity customers {
  id       string  [not null]
  billing  Address [not null]
  contacts array [Contact]

  definitions {
    Address {
      street  string [not null]
      country CountryCode
    }
    CountryCode string [pattern: '^[A-Z]{2}$']
    Contact [note: 'One way to reach the customer'] {
      kind    string [not null]
      address Address
    }
  }
}

VALID -- a field named definitions beside the block, and commas between entries (§3.9, §3.10) ​

xdbml: 0.6

Collection settings {
  _id         objectId [pk]
  definitions varchar
  theme       Theme

  definitions { Theme { color string, font string }, Size int }
}

VALID -- MongoDB accepts the block; its generator inlines the shapes (§15.8.4, §15.8.5) ​

xdbml: 0.6

Project shop { targets: MongoDB }

Collection orders {
  _id      objectId [pk]
  shipping Address

  definitions {
    Address { street string, city string }
  }
}

VALID with a warning -- an entry named like a Type of the Project (definition-shadows-type, §15.8.2) ​

xdbml: 0.6

Type Address { line string }

Entity customers {
  id      string [not null]
  billing Address

  definitions {
    Address { street string, city string }
  }
}

INVALID -- a definitions block for a relational target (definitions-unsupported-target, §15.8.4) ​

xdbml: 0.6

Project erp { targets: Oracle }

Table customers {
  id      number(11) [pk]
  billing Address

  definitions {
    Address { street varchar2(100) }
  }
}

INVALID -- a definitions block for a property graph target (definitions-unsupported-target, §15.8.4) ​

xdbml: 0.6

Project graph { targets: Neo4j }

Entity Person {
  id   string [pk]
  home Address

  definitions {
    Address { city string }
  }
}

INVALID -- a definitions block in a TablePartial (definitions-outside-entity, §15.8.1) ​

xdbml: 0.6

TablePartial audited {
  created Stamp

  definitions {
    Stamp { at timestamp, by varchar }
  }
}

INVALID -- a definitions block in a View (§15.8.1) ​

xdbml: 0.6

View active_customers {
  id varchar

  definitions {
    Address { street varchar }
  }
}

INVALID -- a second definitions block (duplicate-definitions-block, §15.8.1) ​

xdbml: 0.6

Entity customers {
  billing Address
  definitions { Address { street string } }
  definitions { Phone string }
}

INVALID -- two entries under one name (duplicate-definition, §15.8.2) ​

xdbml: 0.6

Entity customers {
  billing Address

  definitions {
    Address { street string }
    Address string
  }
}

INVALID -- an entry under the name of a built-in type (named-type-shadows-builtin, §15.8.2) ​

xdbml: 0.6

Entity customers {
  amount money

  definitions {
    money decimal(10,2)
  }
}

VALID -- a field imported on its own takes the shape of the definitions it names (§15.8.3) ​

xdbml: 0.6

reuse { field customers.billing } from './crm'

Entity invoices {
  id      string [not null]
  bill_to billing
}

INVALID -- a field imported on its own that reaches a recursive definition (§15.8.3) ​

xdbml: 0.6

reuse { field customers.root } from './crm'

Entity trees {
  id   string [not null]
  root root
}

v0.7.1 -- EntityPartial and EntityGroup, color, x- custom properties ​

VALID -- EntityPartial and EntityGroup, with TablePartial and TableGroup as aliases (§17) ​

xdbml: 0.7.1

EntityPartial audit [color: '#F1C40F'] {
  created_at timestamp
}

TablePartial versioned {
  version int
}

Entity orders {
  id int [pk]
  ~audit
  ~versioned
}

Entity customers {
  id int [pk]
}

EntityGroup commerce [color: '#3498DB'] {
  orders
}

TableGroup crm {
  customers
}

VALID -- Entities and EntityGroups in a diagram view (§18.1) ​

xdbml: 0.7.1

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

EntityGroup crm { customers }

DiagramView ordering {
  Entities { orders }
  EntityGroups { crm }
}

INVALID -- Entities and Tables in one diagram view (duplicate-diagram-view-category, §18.1) ​

xdbml: 0.7.1

Entity orders { id int [pk] }

DiagramView ordering {
  Entities { orders }
  Tables { orders }
}

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

Table orders { id int [pk] }

DiagramView ordering {
  Entities { orders }
}

VALID -- a quoted x- custom property on a diagram view, a TableGroup and an entity (§24.5, §18.1) ​

xdbml: 0.7.1

Table orders ["x-hackolade-id": 'orders-1'] { id int [pk] }

TableGroup billing [color: '#E74C3C', "x-hackolade-id": 'billing-2'] {
  orders
}

DiagramView "Customers and Vehicles" ["x-hackolade-id": 'view-3', x_owner: 'ops'] {
  Tables { orders }
}

VALID -- color sets the header color of an entity, a View and an EntityPartial (§7.5) ​

xdbml: 0.7.1

Entity orders [color: '#3498DB'] { id int [pk] }

View open_orders [color: '#2ECC71'] { id int }

Metadata Entity orders {
  note: 'Orders of the web shop'
}

VALID with a warning -- color and headercolor in one settings list (duplicate-setting, §7.5, §24.5) ​

xdbml: 0.7.1

Entity orders [color: '#3498DB', headercolor: '#E74C3C'] { id int [pk] }

INVALID -- color and headercolor in one Metadata block (duplicate-metadata-setting, §24.7.3) ​

xdbml: 0.7.1

Entity orders { id int [pk] }

Metadata Entity orders {
  color: '#3498DB'
  headercolor: '#E74C3C'
}

VALID -- color on a Table without a version declaration is a custom property, as in DBML (§7.5) ​

Table orders [color: #3498DB, headercolor: #E74C3C] { id int [pk] }

v0.7.2 -- Source-to-target mapping, metrics and grain ​

VALID -- a source-to-target mapping with its statement (§27.10) ​

xdbml: 0.7

Container legacy [type: database, target: Oracle] {
  Table MBR {
    MBR_ID   number [pk]
    FIRST_NM varchar2(50)
    LAST_NM  varchar2(50)
    STATUS   char(1)
    CNTRY_CD char(2)
  }
  Table COUNTRY {
    CODE char(2) [pk]
    NAME varchar2(80)
  }
}

Container dw [type: schema, target: Snowflake] {
  Table dim_member [grain: 'One row per member'] {
    member_key   int       [pk, increment]
    member_id    int       [unique]
    full_name    varchar
    country_name varchar
    email        varchar   [note: 'Not available in the source system.']
    loaded_at    timestamp [default: `CURRENT_TIMESTAMP`]
  }
}

Dep load_dim_member [source_database: snowflake] {
  legacy.MBR.MBR_ID   -> dw.dim_member.member_id    [transformation: identity]
  legacy.MBR.FIRST_NM, legacy.MBR.LAST_NM -> dw.dim_member.full_name [transformation: calculation]
  legacy.COUNTRY.NAME -> dw.dim_member.country_name [transformation: identity]

  legacy.MBR.CNTRY_CD, legacy.COUNTRY.CODE -> dw.dim_member [transformation: lookup]
  legacy.MBR.STATUS -> dw.dim_member [transformation: filter]

  source_query: '''
    SELECT m.MBR_ID AS member_id,
           TRIM(m.FIRST_NM) || ' ' || TRIM(m.LAST_NM) AS full_name,
           c.NAME AS country_name
    FROM legacy.MBR m
    LEFT JOIN legacy.COUNTRY c ON c.CODE = m.CNTRY_CD
    WHERE m.STATUS <> 'X'
  '''
  Note: 'Initial load, then a daily delta of the members of the legacy system.'
}

VALID -- a CASE: calculation of the value, conditional of the field it tests (§27.10.1) ​

xdbml: 0.7

Container src {
  Table payment {
    id     int [pk]
    amount decimal(12, 2)
    state  char(1)
  }
}

Container dw {
  Table fct_payment {
    id             int [pk]
    settled_amount decimal(12, 2)
  }
}

Dep load_fct_payment [source_database: ansi_sql] {
  src.payment.amount -> dw.fct_payment.settled_amount [transformation: calculation]
  src.payment.state  -> dw.fct_payment.settled_amount [transformation: conditional]
  source_query: '''
    SELECT CASE WHEN state = 'S' THEN amount END AS settled_amount
    FROM src.payment
  '''
}

VALID -- a Data Vault record source, from the source entity to the field (§27.10.4) ​

xdbml: 0.7

Container legacy {
  Table MBR {
    MBR_ID number [pk]
  }
}

Container crm {
  Table contacts {
    member_number int [pk]
  }
}

Container dv {
  Table hub_member {
    member_id int [pk]
    REC_SRC   varchar
  }
}

Dep load_hub_member_legacy {
  legacy.MBR.MBR_ID -> dv.hub_member.member_id
  legacy.MBR        -> dv.hub_member.REC_SRC
  source_query: '''
    SELECT MBR_ID AS member_id, 'LEGACY' AS REC_SRC FROM legacy.MBR
  '''
}

Dep load_hub_member_crm {
  crm.contacts.member_number -> dv.hub_member.member_id
  crm.contacts               -> dv.hub_member.REC_SRC
  source_query: '''
    SELECT member_number AS member_id, 'CRM' AS REC_SRC FROM crm.contacts
  '''
}

VALID -- metrics: fields of a View that is not materialized (§27.11) ​

xdbml: 0.7

Table fct_orders [grain: 'One row per order'] {
  order_id    int [pk]
  customer_id int
  revenue     decimal(12, 2)
  ordered_at  date
}

View sales_metrics [source_database: ansi_sql, note: 'Measures of the order process, computed at query time.'] {
  source_query: '''
    SELECT SUM(revenue) AS total_revenue,
           SUM(revenue) / COUNT(DISTINCT order_id) AS average_order_value
    FROM fct_orders
  '''
  total_revenue       decimal(18, 2) [synonyms: ['revenue', 'total sales'], note: 'Revenue of the orders, net of refunds.']
  average_order_value decimal(12, 2)
}

Dep sales_metrics_lineage {
  fct_orders.revenue -> sales_metrics.total_revenue [transformation: aggregation]
  fct_orders.revenue, fct_orders.order_id -> sales_metrics.average_order_value [transformation: aggregation]
}

VALID -- several upstream ends, with either arrow (§27.2) ​

xdbml: 0.7

Table stg_customers {
  first_name varchar
  last_name  varchar
  nick_name  varchar
}

Table dim_customer {
  full_name    varchar
  display_name varchar
}

Dep: stg_customers.first_name, stg_customers.last_name -> dim_customer.full_name
Dep: dim_customer.display_name <- stg_customers.nick_name, stg_customers.first_name

VALID -- a block mixing the entity level and the field level (§27.2) ​

xdbml: 0.7

Table raw_orders {
  amount_usd decimal(12, 2)
}

Table stg_orders {
  revenue decimal(12, 2)
}

Dep stg_orders {
  raw_orders -> stg_orders
  raw_orders.amount_usd -> stg_orders.revenue
}

VALID -- values in any casing, and dialect as an alias of source_database (§27.3) ​

xdbml: 0.7

Table customers {
  id         int [pk]
  country_id int
}

Table countries {
  id   int [pk]
  name varchar
}

Table dim_customer {
  id      int [pk]
  country varchar
}

Dep load_dim_customer [Dialect: ANSI_SQL] {
  customers.id -> dim_customer.id [transformation: Identity]
  countries.name -> dim_customer.country [transformation: 'IDENTITY']
  customers.country_id, countries.id -> dim_customer [transformation: Lookup]
}

VALID -- without a version declaration, transformation is a custom property, as in DBML (§27.3) ​

Table a {
  id int
}

Table b {
  id int
}

Dep: a -> b [transformation: copy, source_query: 'SELECT id FROM a']

VALID -- a copy between two entities (identity, §27.10.1) ​

xdbml: 0.7

Container crm {
  Table contacts {
    member_number int [pk]
    email         varchar
  }
}

Container stage {
  Table contacts {
    member_number int [pk]
    email         varchar
  }
}

Dep replicate_contacts: crm.contacts -> stage.contacts [transformation: identity]

VALID -- a role-playing date dimension: one field feeds three fields of a fact (§27.4) ​

xdbml: 0.7

Table dim_date {
  date_key int [pk]
}

Table fct_orders {
  order_date_key    int
  ship_date_key     int
  delivery_date_key int
}

Dep load_fct_orders {
  dim_date.date_key -> fct_orders.order_date_key [transformation: lookup]
  dim_date.date_key -> fct_orders.ship_date_key [transformation: lookup]
  dim_date.date_key -> fct_orders.delivery_date_key [transformation: lookup]
}

VALID -- one field in several roles of one load: the same pair once for each kind (§27.4) ​

xdbml: 0.7

Table orders {
  member_id int
  order_dt  date
  amount    decimal(12, 2)
}

Table members {
  member_id int [pk]
}

Table fct_daily_sales {
  member_id int
  sale_date date
  total     decimal(14, 2)
}

Dep load_fct_daily_sales {
  orders.member_id -> fct_daily_sales.member_id [transformation: identity]
  orders.order_dt  -> fct_daily_sales.sale_date [transformation: identity]
  orders.amount    -> fct_daily_sales.total [transformation: aggregation]
  orders.member_id, members.member_id -> fct_daily_sales [transformation: join]
  orders.member_id, orders.order_dt -> fct_daily_sales [transformation: group_by]
}

INVALID -- the same pair with the same kind twice (duplicate-dep, §27.4) ​

xdbml: 0.7

Table a {
  x int
}

Table b {
  x int
}

Dep load {
  a.x -> b [transformation: filter]
  a.x -> b [transformation: filter]
}

INVALID -- two downstream ends in one dependency (§27.2) ​

xdbml: 0.7

Table a {
  x int
}

Table b {
  x int
  y int
}

Dep: a.x -> b.x, b.y

INVALID -- an unknown value of transformation (unknown-transformation, §27.10.1) ​

xdbml: 0.7

Table a {
  x int
}

Table b {
  x int
}

Dep: a.x -> b.x [transformation: copy]

INVALID -- a transformation between two entities (transformation-between-entities, §27.10.1) ​

xdbml: 0.7

Table raw_orders {
  id int
}

Table stg_orders {
  id int
}

Dep: raw_orders -> stg_orders [transformation: filter]

INVALID -- a kind that gives the value of a field, ending at an entity (transformation-target-mismatch, §27.10.1) ​

xdbml: 0.7

Table a {
  first_name varchar
}

Table b {
  full_name varchar
}

Dep: a.first_name -> b [transformation: calculation]

VALID with a warning -- identity with several upstream ends (transformation-mismatch, §27.10.5) ​

xdbml: 0.7

Table a {
  first_name varchar
  last_name  varchar
}

Table b {
  full_name varchar
}

Dep: a.first_name, a.last_name -> b.full_name [transformation: identity]

VALID with a warning -- an indirect kind from a whole entity (indirect-from-entity, §27.10.5) ​

xdbml: 0.7

Table members {
  id     int
  status char(1)
}

Table dim_member {
  id int
}

Dep: members -> dim_member.id [transformation: filter]

VALID with a warning -- source_query in the brackets of a Dep (source-query-in-settings, §27.3) ​

xdbml: 0.7

Table a {
  id int
}

Table b {
  id int
}

Dep load [source_query: 'SELECT id FROM a'] {
  a.id -> b.id
}

VALID with a warning -- dialect and source_database in one settings list (duplicate-setting, §24.5) ​

xdbml: 0.7

Table a {
  id int
}

View v [dialect: snowflake, source_database: oracle] {
  source_query: 'SELECT id FROM a'
  id int
}

VALID with a warning -- grain in a Metadata block (metadata-custom-property, §24.7.3) ​

xdbml: 0.7

Table fct_orders {
  order_id int [pk]
}

Metadata Table fct_orders {
  grain: 'One row per order'
}

v0.7.3 -- calculation ​

INVALID -- derivation is no kind of transformation (unknown-transformation, §27.10.1) ​

xdbml: 0.7

Table a {
  first_name varchar
  last_name  varchar
}

Table b {
  full_name varchar
}

Dep: a.first_name, a.last_name -> b.full_name [transformation: derivation]

v0.7.4 -- types of several words without quotes ​

VALID with a warning -- a type of several words without quotes reads as the quoted name (unquoted-type-name, §8.1) ​

xdbml: 0.7

TablePartial Audit {
  "Created At" TIMESTAMP WITH TIME ZONE [not null, default: `CURRENT_TIMESTAMP`]
  "Updated At" TIMESTAMP WITH TIME ZONE [not null, default: `CURRENT_TIMESTAMP`]
}

VALID with a warning -- an even number of words after the first, and parameters on a later word (unquoted-type-name, §8.1) ​

xdbml: 0.7

Table events {
  id         int [pk]
  created_at TIMESTAMP WITH LOCAL TIME ZONE [not null]
  duration   INTERVAL DAY(2) TO SECOND(6)
}

VALID with a warning -- in a document without a version declaration (unquoted-type-name, §8.1) ​

Table measures {
  id    int [pk]
  value double precision
}

VALID -- the quoted form, which tools write (§8.1) ​

xdbml: 0.7

Table events {
  id         int [pk]
  created_at "timestamp with time zone" [not null]
  value      "double precision"
}

INVALID -- a type in the brackets of a map takes the quotes (§8.1) ​

xdbml: 0.7

Table readings {
  id     int [pk]
  values map [string, double precision]
}

v0.7.5 -- relationship flags ​

VALID -- a flag written alone, with true, or with false, on a Ref and on an Edge (§11.9, §13.3) ​

xdbml: 0.7

Entity Person { id int [pk] }
Entity Phone {
  phone_id int [pk]
  owner_id int
}
Entity Headset { headset_id int [pk] }

Edge KNOWS [source: Person, target: Person, undirected] { since date }
Edge FOLLOWS [source: Person, target: Person, undirected: false] { since date }

Ref paired_with: Phone - Headset [undirected]
Ref sold_with: Phone - Headset [undirected: true]
Ref owned_by: Phone.owner_id > Person.id [inactive: false, foreign_master: false]

VALID -- foreign_master: false on a field needs no inline ref (§11.9, §11.10.2) ​

xdbml: 0.7

Table orders {
  id            int [pk]
  customer_name varchar [foreign_master: false]
}

INVALID -- undirected on an Edge with a value other than true or false (invalid-undirected, §11.9) ​

xdbml: 0.7

Entity Person { id int [pk] }

Edge KNOWS [source: Person, target: Person, undirected: yes] { since date }

INVALID -- inactive with a value other than true or false (invalid-inactive, §11.9) ​

xdbml: 0.7

Table users { id int [pk] }
Table posts {
  id      int [pk]
  user_id int
}

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

Test runner ​

The reference parser runs this corpus in its test suite (parser/test/run-tests.ts, the "grammar test corpus" group, part of npm run check). For each case it parses the code block with grammar/fixtures/ as the directory of the importing file, resolves names, and checks the label: VALID means no parse error and no error diagnostic, VALID with a warning means at least one warning and no error, and INVALID means a parse error or at least one error diagnostic. Implementations in other languages can run the same corpus with the same three rules and the same fixtures.

Spec under Apache License 2.0 · Examples under CC0 1.0