BDL Reference¶
BDL (Bani Definition Language) is the declarative format for defining database migrations. BDL files use XML (.bdl) or JSON (.bdl.json) syntax. This reference documents every element and attribute.
Document Structure¶
Every BDL document is wrapped in a <bani> root element:
<?xml version="1.0" encoding="UTF-8"?>
<bani schemaVersion="1.0">
<project ... />
<source ... />
<target ... />
<options> ... </options>
<tables> ... </tables>
<typeMappings> ... </typeMappings>
<hooks> ... </hooks>
<schedule> ... </schedule>
<sync> ... </sync>
</bani>
| Attribute | Required | Description |
|---|---|---|
schemaVersion |
Yes | BDL schema version. Currently "1.0". |
<project>¶
Project metadata. The name attribute is required.
<project name="erp-to-analytics"
description="Full migration from MySQL to PostgreSQL"
author="data-engineering"
created="2026-03-27T08:00:00Z">
<tags>
<tag>erp</tag>
<tag>analytics</tag>
</tags>
</project>
| Attribute | Required | Description |
|---|---|---|
name |
Yes | Short slug-style project identifier. |
description |
No | Human-readable description. |
author |
No | Author name or team. |
created |
No | ISO 8601 creation timestamp. |
The <tags> child element contains zero or more <tag> elements with text content.
<source> and <target>¶
Connection definitions for the source and target databases. Both share the same structure.
<source connector="mysql">
<connection host="localhost"
port="3306"
database="erp"
username="${env:MYSQL_USER}"
password="${env:MYSQL_PASS}" />
<connectorConfig>
<option name="charset" value="utf8mb4" />
</connectorConfig>
</source>
<source> / <target> attributes¶
| Attribute | Required | Description |
|---|---|---|
connector |
Yes | Connector name: mysql, postgresql, mssql, oracle, sqlite. |
<connection> attributes¶
| Attribute | Required | Description |
|---|---|---|
host |
Yes | Database hostname or IP. |
port |
Yes | Database port. |
database |
Yes | Database name (or file path for SQLite). |
username |
No | Credential, typically ${env:VAR_NAME}. |
password |
No | Credential, typically ${env:VAR_NAME}. |
usernameEnv |
No | Alternative: environment variable name for username. |
passwordEnv |
No | Alternative: environment variable name for password. |
Credential security
Always use ${env:VAR_NAME} references for credentials. Bani resolves these at runtime from the process environment. Plaintext passwords in BDL files are a security risk.
<connectorConfig>¶
Optional connector-specific settings. Each <option> has name and value attributes.
<connectorConfig>
<option name="charset" value="utf8mb4" />
<option name="connectTimeout" value="30" />
<option name="service_name" value="ORCLPDB1" /> <!-- Oracle -->
<option name="oracle_client_lib" value="/opt/oracle/instantclient" />
</connectorConfig>
<tables>¶
Defines which tables to migrate and how columns are mapped.
<tables mode="include">
<table sourceName="customers"
targetName="customers"
sourceSchema="dbo"
targetSchema="public"
writeStrategy="insert">
<columnMappings>
<column source="id" target="customer_id" targetType="BIGINT" />
<column source="full_name" target="name" />
</columnMappings>
<filter>WHERE is_active = 1</filter>
</table>
</tables>
<tables> attributes¶
| Attribute | Default | Description |
|---|---|---|
mode |
include |
"include": only listed tables. "exclude": all tables except listed ones. |
<table> attributes¶
| Attribute | Required | Description |
|---|---|---|
sourceName |
Yes | Table name in the source database. |
targetName |
No | Table name in the target. Defaults to sourceName. |
sourceSchema |
No | Schema in the source database. |
targetSchema |
No | Schema in the target database. |
filter |
No | SQL WHERE clause to restrict rows. |
writeStrategy |
No | "insert" (default), "upsert", or "truncate-insert". |
<columnMappings>¶
Optional. Maps source columns to different target names or types.
| Attribute | Required | Description |
|---|---|---|
source |
Yes | Source column name. |
target |
Yes | Target column name. |
targetType |
No | Override the automatically mapped data type. |
If <columnMappings> is omitted, all columns are transferred with automatic name preservation and type mapping.
<options>¶
Project-level configuration for the migration engine.
<options>
<batchSize>100000</batchSize>
<parallelWorkers>4</parallelWorkers>
<memoryLimitMB>2048</memoryLimitMB>
<onError>log-and-continue</onError>
<createTargetSchema>true</createTargetSchema>
<dropTargetTablesFirst>false</dropTargetTablesFirst>
<transferIndexes>true</transferIndexes>
<transferForeignKeys>true</transferForeignKeys>
</options>
| Element | Default | Description |
|---|---|---|
batchSize |
100000 |
Rows per Arrow RecordBatch. Larger batches improve throughput but use more memory. |
parallelWorkers |
4 |
Tables transferred concurrently. |
memoryLimitMB |
2048 |
Soft memory cap in MB. |
onError |
log-and-continue |
"log-and-continue" skips failed rows/tables. "fail-fast" (or "abort") halts on the first error. |
createTargetSchema |
true |
Create the target schema if it does not exist. |
dropTargetTablesFirst |
false |
Drop existing target tables before creating. Use true for repeatable full refreshes. |
transferIndexes |
true |
Copy indexes from source to target. |
transferForeignKeys |
true |
Copy foreign key constraints. |
transferDefaults |
true |
Copy column default values. |
transferCheckConstraints |
true |
Copy CHECK constraints. |
<typeMappings>¶
Override the automatic type mapping for specific source types.
<typeMappings>
<mapping sourceType="MEDIUMTEXT" targetType="TEXT" />
<mapping sourceType="TINYINT(1)" targetType="BOOLEAN" />
</typeMappings>
Each <mapping> has:
| Attribute | Description |
|---|---|
sourceType |
Source database type to match. |
targetType |
Target database type to use instead of the automatic mapping. |
See Type Mappings for the default mapping tables.
<hooks>¶
Pre- and post-migration hooks for running custom SQL or shell commands.
<hooks>
<hook event="before-migration" type="sql" target="target">
CREATE SCHEMA IF NOT EXISTS analytics;
</hook>
<hook event="after-table" tableName="orders" type="sql" target="target">
CREATE INDEX idx_orders_date ON orders (order_date);
</hook>
<hook event="after-migration" type="shell" onFailure="continue">
curl -s -X POST https://hooks.slack.com/... -d '{"text":"Migration complete"}'
</hook>
</hooks>
<hook> attributes¶
| Attribute | Required | Default | Description |
|---|---|---|---|
event |
Yes | -- | "before-migration", "after-migration", "before-table", "after-table". |
type |
Yes | -- | "sql" or "shell". |
target |
For SQL | -- | "source" or "target" -- which database to run SQL against. |
tableName |
No | -- | For per-table events, the table this hook applies to. |
onFailure |
No | "abort" |
"abort" halts the migration. "continue" logs and proceeds. |
timeout |
No | 300 |
Maximum execution time in seconds. |
The text content of the <hook> element is the SQL statement or shell command.
Shell hook security
Shell hooks execute with the privileges of the Bani process. Only use them in trusted environments.
<schedule>¶
Configure recurring scheduled migrations via the OS scheduler (crontab).
<schedule enabled="true">
<cron>0 2 * * *</cron>
<timezone>UTC</timezone>
<retryOnFailure maxRetries="2" delaySeconds="120" />
</schedule>
| Element / Attribute | Default | Description |
|---|---|---|
enabled |
false |
Whether the schedule is active. |
<cron> |
-- | Standard 5-field cron expression (e.g. "0 2 * * *" for daily at 2 AM). |
<timezone> |
UTC |
IANA timezone for cron evaluation. |
maxRetries |
0 |
Number of retries on failure. |
delaySeconds |
0 |
Delay between retries in seconds. |
<sync>¶
Enable incremental (delta) synchronisation.
<sync enabled="true">
<strategy>timestamp</strategy>
<trackingColumn table="Orders" column="ModifiedAt" />
<trackingColumn table="Customers" column="UpdatedAt" />
</sync>
| Element / Attribute | Default | Description |
|---|---|---|
enabled |
false |
Whether sync is active. |
<strategy> |
full |
"timestamp", "rowversion", or "checksum". |
<trackingColumn> |
-- | Maps a table to its change-tracking column. |
See Incremental Sync for details on each strategy.
JSON Format¶
BDL files can also be written as JSON (.bdl.json). The structure mirrors the XML:
{
"schemaVersion": "1.0",
"project": {
"name": "my-migration",
"description": "MySQL to PostgreSQL"
},
"source": {
"connector": "mysql",
"connection": {
"host": "localhost",
"port": 3306,
"database": "erp",
"username": "${env:MYSQL_USER}",
"password": "${env:MYSQL_PASS}"
}
},
"target": {
"connector": "postgresql",
"connection": {
"host": "localhost",
"port": 5432,
"database": "analytics",
"username": "${env:PG_USER}",
"password": "${env:PG_PASS}"
}
},
"options": {
"batchSize": 100000,
"parallelWorkers": 4,
"onError": "log-and-continue"
},
"tables": [
{"sourceName": "customers", "targetName": "customers"},
{"sourceName": "orders", "targetName": "orders"}
]
}
Example Files¶
The examples/bdl/ directory contains annotated example BDL files:
mysql-to-postgresql.bdl-- Basic full migration with column mappingsincremental-sync.bdl-- Delta sync with timestamp trackingcustom-hooks.bdl-- Pre/post migration hooks (SQL and shell)mssql-to-postgresql.bdl-- Cross-dialect migrationoracle-to-postgresql.bdl-- Oracle source with service_namesqlite-to-mysql.bdl-- SQLite file to MySQLscheduled-nightly.bdl-- Nightly scheduled migrationfiltered-migration.bdl-- Row filtering with WHERE clausesmulti-schema.bdl-- Multi-schema migrationchained-migration.bdl-- Multi-step migration pipeline