Schedule icon
Script icon
UploadFiles icon
Switch icon
Log icon
Fail icon
If icon
SlackIncomingWebhook icon

Find the SQL that will not run on your next warehouse before you migrate

Check which SQL files will break on a new warehouse with Kestra and SQLFluff. Parse per dialect, publish a portability matrix, gate on blockers.

Categories
Data

Moving from one warehouse to another starts with a question nobody can answer quickly: which of our queries will not even parse on the new engine? TOP 10, QUALIFY and payload:field::string are everyday syntax in some dialects and parse errors in others. This flow parses every SQL file with SQLFluff once per dialect, keeps only parse errors (code PRS), and publishes a matrix of files against dialects. It fails while any file still cannot be parsed in the migration target, so the blocker count can be tracked down to zero week by week.

It runs with no setup on a small demo corpus written for Snowflake. The default target is BigQuery.

This blueprint was created by zkasuran.

How it works

  1. port_check (io.kestra.plugin.scripts.python.Script, sqlfluff==4.2.1) runs sqlfluff lint --dialect <d> --format json --nofail for the source dialect, the target and every dialect in compare_dialects, keeps the PRS violations and builds:
    • a matrix of ok / FAIL per file and dialect,
    • the dialects ranked by how many files fail,
    • the blockers for the target, with the line and the unparsable section. It writes report.md and outputs verdict, blocker_count and blockers.
  2. publish_report (io.kestra.plugin.core.namespace.UploadFiles) writes the report to migration/sql-portability-<source>-to-<target>.md.
  3. gate (io.kestra.plugin.core.flow.Switch):
    • READY: every file parses in the target.
    • SOURCE_INVALID: a file does not parse in its own dialect, so the comparison would be meaningless. The run fails.
    • BLOCKED: optional Slack, then the run fails with the blockers.
  4. The weekly Schedule tracks progress every Monday.

On the demo corpus the matrix reads:

file snowflake bigquery postgres tsql duckdb databricks
01_top_customers.sql (TOP 10) ok FAIL ok ok ok FAIL
02_latest_order.sql (QUALIFY) ok ok FAIL FAIL ok ok
04_json_field.sql (payload:customer.email) ok FAIL FAIL FAIL FAIL ok

Inputs

  • source_dialect (SELECT, default snowflake): the dialect the SQL is written in.
  • target_dialect (SELECT, default bigquery): the migration target. This is the only gated dialect.
  • compare_dialects (ARRAY, default [postgres, tsql, duckdb, databricks]): reported, never gated.
  • notify_slack (BOOL, default false).

Prerequisites

  • A Kestra worker that can run Docker containers.

Secrets

  • SLACK_WEBHOOK_URL: only when notify_slack is true.

Quick start

  1. Run it. The verdict is BLOCKED with 2 of 5 files, and the matrix is in the namespace Files tab.
  2. Run it with source_dialect set to bigquery. The verdict is SOURCE_INVALID, because two files are not BigQuery SQL.
  3. Check your own SQL: wrap port_check in a io.kestra.plugin.core.flow.WorkingDirectory with io.kestra.plugin.git.Clone before it, and remove the demo inputFiles.

Things to know

  • A parse error means the target will certainly reject the statement. A clean parse does not prove it runs: SQLFluff accepted TOP in Postgres as a column alias. Treat the result as the minimum list of rewrites.
  • Templated SQL (dbt, Jinja) needs the SQLFluff dbt or Jinja templater configured in a .sqlfluff file.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.