An alternative to Terraform for SQL databases without using a state.
Project description
SQLIaC — SQL Infrastructure as Code
An alternative to Terraform for SQL databases — without a state file.
SQLIaC lets you declare SQL database resources (databases, schemas, tables, roles, views, warehouses, grants…) in TOML definition files. Under the hood it queries the live database to detect drift, then renders and executes Jinja-templated DDL to bring the database into the declared state. No state file. No lock file. Just your definitions and the database.
How It Works
- You declare resources in
./definitions/*.toml— one file per resource type. - SQLIaC queries the live database using
state.sqltemplates to discover what currently exists. - Drift is detected by comparing definition → state; actions are determined: CREATE, ALTER, DROP, or no-op.
- Jinja-templated DDL renders the appropriate SQL statements.
- Execution runs in parallel respecting a dependency DAG built from your definitions.
Installation
pip install sqliac
You'll also need at least one database adapter:
pip install sqliac-snowflake # Snowflake
# pip install sqliac-postgres # PostgreSQL (future)
# pip install sqliac-oracle # Oracle (future)
# pip install sqliac-databricks# Databricks (future)
Quick Start
1. Scaffold a project
sqliac init
This creates:
.sqliac/
├── provider/
│ ├── config.toml # resource registry with DDL commands & context schemas
│ ├── credentials.toml # database connection credentials
│ ├── database/
│ │ ├── ddl_template.sql # Jinja DDL template
│ │ └── state.sql # state introspection query
│ ├── schema/
│ │ ├── ddl_template.sql
│ │ └── state.sql
│ └── table/
│ ├── ddl_template.sql
│ └── state.sql
definitions/
├── database.toml # declare your databases here
├── schema.toml # declare your schemas here
└── table.toml # declare your tables here
.agents/skills/sqliac/ # AI agent skill for generating template pairs
The scaffolding includes working examples (Snowflake DDL + state queries for database, schema, and table). Adjust the provider config to match your engine and resources.
2. Configure credentials
export SNOWFLAKE_ACCOUNT="my_account"
export SNOWFLAKE_USER="my_user"
export SNOWFLAKE_PASSWORD="my_password"
export SNOWFLAKE_WAREHOUSE="my_warehouse"
Or write them in .sqliac/provider/credentials.toml (env vars take precedence):
[snowflake]
account = "my_account"
user = "my_user"
password = "my_password"
warehouse = "my_warehouse"
3. Declare your resources
Edit the definition files under definitions/. For example, definitions/schema.toml:
[[schema]]
object_name = "MY_DB.STAGING"
transient = false
managed_access = false
data_retention_time_in_days = 1
comment = "Staging schema"
[schema.depends_on]
database = ["MY_DB"]
[[schema]]
object_name = "MY_DB.PRODUCTION"
transient = false
[Sschema.depends_on]
database = ["MY_DB"]
object_name— fully qualified name (uppercase by convention)depends_on— declare ordering constraints between resourceswait_time— optional delay (seconds) between DDL execution and the next resource
4. Preview and apply
sqliac apply --dry-run # see what would change
sqliac apply # execute for real
5. Tear down
sqliac destroy --dry-run # preview cleanup
sqliac destroy # drop all declared resources (in reverse DAG order)
CLI Reference
| Command | Description |
|---|---|
sqliac init |
Create project scaffolding with example templates and the agent skill |
sqliac list |
Show installed adapters and their available resource types |
sqliac graph |
Generate a dependencies.dot DAG file from your definitions |
sqliac compile --ddl create|alter|drop <resource> |
Render a DDL template with example context from config.toml |
sqliac compile --state <resource> |
Render a state query with example context from config.toml |
sqliac apply |
Compare definitions against live state and reconcile |
sqliac destroy |
Drop all declared resources in reverse dependency order |
Apply / Destroy options
sqliac apply [--threads N] [--dry-run]
sqliac destroy [--threads N] [--dry-run]
--threads— number of parallel workers (default: 3). Tasks execute concurrently within each topo-sorted batch of the DAG.--dry-run— preview what SQL would be executed without touching the database.
Logging
sqliac --log debug apply # console logging
sqliac --log-out debug apply # file logging (sqliac.log)
Provider Templates
Each resource type registered in .sqliac/provider/config.toml must have:
.sqliac/provider/<resource>/
├── ddl_template.sql # Jinja DDL for CREATE/ALTER/DROP
└── state.sql # query returning current state as a single JSON row
Core invariant
The keys returned by
state.sqlmust be a 1:1 match with the Jinja variables consumed byddl_template.sql.
If ddl_template.sql references add.get('warehouse'), then state.sql must return JSON containing warehouse. The tool detects mismatches and reports them at runtime, but you can validate pairs ahead of time using the agent skill's check script.
The compile command
Use sqliac compile to iterate on templates without applying:
sqliac compile --ddl create database # renders the CREATE/ALTER DDL
sqliac compile --state database # renders the state introspection query
The compile command reads the example ddl_context from config.toml, so keep it populated with realistic values — it serves as documentation, test fixture, and developer playground all at once.
Dependency Graph
Resources can declare inter-resource dependencies via depends_on. SQLIaC builds a DAG and executes in topo-sorted waves:
[[schema]]
object_name = "MY_DB.STAGING"
[schema.depends_on]
database = ["MY_DB"]
apply— resources with no dependencies run first; downstream resources wait for their deps to succeed.destroy— the DAG is reversed so dependents are dropped before their dependencies.- Circular dependencies are detected and reported with visual inline diagnostics.
sqliac graph # writes dependencies.dot + prints to terminal
The sqliac Agent Skill
SQLIaC ships with an agent skill (for AI coding assistants like OpenCode, Cursor, etc.) that automates writing ddl_template.sql and state.sql pairs. To use it:
- Run
sqliac init— the skill is copied into.agents/skills/sqliac/. - Ask your AI assistant: "create Snowflake templates for warehouse" and reference the
.agentsskill directory.
The skill:
- Reads engine-specific reference files (Snowflake, PostgreSQL, Oracle, Databricks patterns).
- Produces a validated
ddl_template.sql+state.sqlpair with correct Jinja + SQL. - Registers the resource in
config.tomlwith example values. - Runs
check_key_parity.pyto verify key parity between the template and state query.
See .agents/skills/sqliac/SKILL.md for the full skill documentation.
Drift Detection
On each apply, SQLIaC:
- Executes
state.sqlto fetch the live state as JSON. - Recursively normalizes both the definition and the state (filtering, sanitizing, aligning nested keys).
- Runs a structured diff via
dictdifferto produceadd,change, andremovesections. - Feeds those sections into the Jinja DDL template, which renders the appropriate
CREATE OR ALTER,ALTER, orDROPstatements.
If the resource does not exist (state query returns zero rows), a CREATE is generated. If no differences are found, no DDL is executed.
Adapters
Adapters are self-contained packages discovered via Python entry points (sqliac.adapters group). Each adapter provides:
- A credentials dataclass with validation
- A connection manager for thread-safe connection pooling
- An adapter class wrapping query execution
All adapters follow the base classes in sqliac.adapters.base, making it straightforward to add support for new database engines.
Project Layout
.sqliac/ # config directory
├── provider/
│ ├── config.toml # resource registry
│ ├── credentials.toml # connection credentials
│ └── <resource>/ # one dir per resource type
│ ├── ddl_template.sql
│ └── state.sql
definitions/ # your resource declarations
├── database.toml
├── schema.toml
└── table.toml
.agents/skills/sqliac/ # AI agent skill for template generation
License
MIT © Ruslan Gonzalez Konstantinov
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sqliac-0.1.6.tar.gz.
File metadata
- Download URL: sqliac-0.1.6.tar.gz
- Upload date:
- Size: 44.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Pop!_OS","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a914a196db6dd0dedd9266e3657255368d294ec557f23459af193549eb68564c
|
|
| MD5 |
1df213b39ca88356c09b3aff692db60d
|
|
| BLAKE2b-256 |
3fac8616ed8a3852faba2c83b8fb6f7adabfec43571fb56251f429498b3d5cb5
|
File details
Details for the file sqliac-0.1.6-py3-none-any.whl.
File metadata
- Download URL: sqliac-0.1.6-py3-none-any.whl
- Upload date:
- Size: 44.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Pop!_OS","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d476d03b56ff20a3027ec5555e0945be6fb416f404da5eeea2b3b607a64b4b0f
|
|
| MD5 |
656cd3028cf8502ace3f707d970f5796
|
|
| BLAKE2b-256 |
9655e3a8f517c708412751dfe9b26bbe266cb8f3d86ba8d9203eec5d29d74209
|