This guide provides step-by-step instructions for using the SQLFluff Schemachange Templater to lint SQL files that use schemachange-compatible Jinja templating features.
Important: This templater provides a standalone implementation of schemachange's templating features. It does not require or import the schemachange package - it reads
schemachange-config.ymlfiles directly and replicates the same Jinja environment.
# Install the templater
pip install sqlfluff-templater-schemachange
# Or install from source
git clone https://github.com/yourusername/sqlfluff-templater-schemachange
cd sqlfluff-templater-schemachange
pip install -e .Create a .sqlfluff file in your project root:
[sqlfluff]
templater = schemachange
dialect = snowflake
[sqlfluff:templater:schemachange]
config_folder = .
modules_folder = .\templatesCreate a schemachange-config.yml file:
config-version: 1
vars:
database_name: 'MY_DATABASE'
schema_name: 'ANALYTICS'
environment: 'dev'Create test.sql:
USE DATABASE {{ database_name }};
USE SCHEMA {{ schema_name }};
CREATE TABLE customers (
id INTEGER,
name VARCHAR(255)
);sqlfluff lint test.sqlThe schemachange templater integrates with SQLFluff's templating system by:
- Configuration Loading: Reads
schemachange-config.ymlwith environment variable substitution - Variable Extraction: Merges variables from config file and CLI arguments
- Jinja Environment Setup: Creates a Jinja environment with proper search paths and functions
- Template Rendering: Processes SQL files using Jinja2 templating
- Secret Filtering: Automatically filters sensitive variables from logs
[sqlfluff:templater:schemachange]
# Path to schemachange config (auto-discovered if not specified)
config_file = schemachange-config.yml
# Additional variables (merged with config file vars)
vars = {"debug_mode": true, "linting": true}
# Additional template search paths
modules_folder = templates
The templater supports all schemachange configuration features:
snowflake-account: '{{ env_var("SNOWFLAKE_ACCOUNT") }}'
database_name: '{{ env_var("DATABASE", "DEFAULT_DB") }}'vars:
sources:
raw: 'RAW_DATABASE'
staging: 'STAGING_DATABASE'
features:
enable_masking: '{{ env_var("ENABLE_MASKING", "false") | bool }}'
secrets:
api_key: '{{ env_var("API_KEY") }}' # Automatically filtered from logsmodules-folder: 'templates'Variables are automatically treated as secrets if:
- Name contains "secret" (case-insensitive)
- Nested under a "secrets" key
vars:
api_key_secret: "sensitive" # Filtered
password: "123" # Not filtered
secrets:
token: "abc123" # FilteredCreate reusable macros in your modules folder:
templates/common.sql:
{% macro audit_columns() %}
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
{% endmacro %}Usage in SQL files:
{% from "common.sql" import audit_columns %}
CREATE TABLE users (
id INTEGER,
name VARCHAR(255),
{{ audit_columns() }}
);CREATE TABLE events (
id INTEGER,
{% if environment == 'prod' %}
sensitive_data VARCHAR(255),
{% endif %}
created_at TIMESTAMP
);Base template (templates/base_table.sql):
{% block table_definition %}
CREATE TABLE {{ table_name }} (
{% block columns %}{% endblock %}
{% block audit_columns %}
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
{% endblock %}
);
{% endblock %}Child template:
{% extends "base_table.sql" %}
{% set table_name = "products" %}
{% block columns %}
id INTEGER PRIMARY KEY,
name VARCHAR(255)
{% endblock %}Development (.sqlfluff):
[sqlfluff:templater:schemachange]
config_folder = configs
config_file = dev.yml
vars = {"environment": "dev", "debug": true}Production (CI/CD):
[sqlfluff:templater:schemachange]
config_folder = configs
config_file = prod.yml
vars = {"environment": "prod", "debug": false}{% from "macros.sql" import create_scd_table, environment_setup %}
{{ environment_setup() }}
-- Create customer dimension with SCD Type 2
{{ create_scd_table('dim_customer', 'customer_key', [
{'name': 'first_name', 'type': 'VARCHAR(100)'},
{'name': 'last_name', 'type': 'VARCHAR(100)'},
{'name': 'email', 'type': 'VARCHAR(255)', 'not_null': true}
]) }}
-- Environment-specific grants
{% if environment == 'prod' %}
GRANT SELECT ON dim_customer TO ROLE ANALYST_PROD;
{% else %}
GRANT ALL ON dim_customer TO ROLE DEVELOPER;
{% endif %}GitHub Actions:
name: SQL Linting
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install dependencies
run: |
pip install sqlfluff sqlfluff-templater-schemachange
- name: Lint SQL files
env:
SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
DATABASE_NAME: ${{ secrets.DATABASE_NAME }}
run: sqlfluff lint migrations/Error: TemplateNotFound: common.sql
Solution: Check your modules-folder configuration and file paths.
Error: Undefined variable 'database_name'
Solution: Add the variable to your schemachange-config.yml or CLI vars.
Error: FileNotFoundError: schemachange-config.yml
Solution: Ensure the config file path is correct and accessible.
Enable verbose logging:
sqlfluff lint --verbose --debug migrations/Check what variables are being loaded:
# Add to your config for debugging
vars:
debug_variables: trueTest your templates without running schemachange:
# Use the render command (if available)
sqlfluff render V1.0.1__test.sql
# Or create a simple test script
python -c "
from jinja2 import Template
import yaml
with open('schemachange-config.yml') as f:
config = yaml.safe_load(f)
with open('V1.0.1__test.sql') as f:
template = Template(f.read())
print(template.render(**config['vars']))
"The templater caches Jinja environments and templates. For large projects:
[sqlfluff:templater:schemachange]
# Limit template search paths to improve performance
modules_folder = templatesFor large projects, use selective linting:
# Lint only changed files
sqlfluff lint $(git diff --name-only --diff-filter=AM | grep '\.sql$')
# Lint specific directories
sqlfluff lint migrations/versioned/Use SQLFluff's parallel processing:
sqlfluff lint --processes 4 migrations/.pre-commit-config.yaml:
repos:
- repo: https://github.com/sqlfluff/sqlfluff
rev: 2.0.0
hooks:
- id: sqlfluff-lint
additional_dependencies: ['sqlfluff-templater-schemachange']
- id: sqlfluff-fix
additional_dependencies: ['sqlfluff-templater-schemachange'].vscode/settings.json:
{
"sqlfluff.executablePath": "sqlfluff",
"sqlfluff.config": ".sqlfluff",
"sqlfluff.linter.run": "onSave"
}Makefile:
lint-sql:
sqlfluff lint migrations/
fix-sql:
sqlfluff fix migrations/
check-sql: lint-sql
@echo "SQL linting complete"- Keep environment-specific configs separate
- Use environment variables for sensitive data
- Version control your
.sqlfluffand config files
- Use consistent macro naming conventions
- Group related macros in single files
- Document complex template logic
- Test templates with different variable combinations
- Use linting in CI/CD pipelines
- Validate generated SQL in staging environments
- Never commit secrets to version control
- Use the secret filtering features
- Audit template access in production environments
- Move Jinja variables to
schemachange-config.yml - Update
.sqlfluffto useschemachangetemplater - Test with existing SQL files
- Create initial
schemachange-config.ymlwith current settings - Add versioning to existing SQL files
- Gradually adopt templating features
- GitHub Issues: Report bugs and request features
- Documentation: Comprehensive guides and API reference
- Examples: Real-world usage examples and templates
This completes the comprehensive setup for integrating schemachange's Jinja templating system with SQLFluff's linting capabilities.