Pre-flight schema validator โ powered by sqlglot. Checks field names and types against BigQuery, Snowflake, Redshift, Spark, Databricks, Hive, Postgres, MySQL, DuckDB, Trino, ClickHouse, Kafka/Avro. Pure Python. No API. Works offline.
Project description
schema-preflight ๐ซ
Check your schema against 12 systems simultaneously โ before a single byte of data is written.
Pure Python. No API server. No network calls. Works completely offline. Powered by sqlglot's community-maintained dialect databases.
You name a field 'timestamp'. Kafka accepts it. 50 million
messages later โ BigQuery is broken. TIMESTAMP is a reserved
keyword. The dashboard team calls at 2am.
schema-preflight catches this in milliseconds. Before you deploy.
Install
pip install schema-preflight
One dependency: sqlglot (the SQL parser powering dbt, Airflow, and hundreds of data tools).
Quickstart โ 3 lines
from schema_preflight import SchemaPreflight
pf = SchemaPreflight() # no URL, no API, pure offline
report = pf.check(
name="OrderEvent",
source_format="avro",
target_systems=["bigquery", "snowflake", "redshift", "spark", "postgres"],
fields=[
{"name": "order_id", "type": "string"}, # โ
safe
{"name": "user_id", "type": "string"}, # โ
safe
{"name": "timestamp", "type": "string"}, # โ CRITICAL โ 5 systems
{"name": "select", "type": "integer"}, # โ CRITICAL โ 5 systems
{"name": "total", "type": "number"}, # โ
safe
{"name": "metadata", "type": "map"}, # โ CRITICAL โ BigQuery+Redshift
{"name": "2fa_enabled", "type": "boolean"}, # โ CRITICAL โ starts with digit
],
)
report.print_summary()
report.raise_if_critical() # raises PreflightFailed if any CRITICAL found
Output:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Schema Preflight v2 ยท OrderEvent
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Status : โ FAILED โ fix before writing data
Systems : 5 checked 0 passed 5 failed
Issues : 22 critical 0 warning 9 info
๐ Issues affecting ALL 5 target systems
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ด [RESERVED_KEYWORD] field: 'timestamp'
'timestamp' is a reserved keyword in all 5 systems
Fix : Rename to 'timestamp_value' | 'event_timestamp' | 'ts'
๐ด [RESERVED_KEYWORD] field: 'select'
'select' is a reserved SQL keyword in all 5 systems
Fix : Rename to 'select_value' | 'select_col' | 'event_select'
๐ด [STARTS_WITH_DIGIT] field: '2fa_enabled'
Starts with digit โ invalid in all SQL systems and Avro
Fix : Rename to '_2fa_enabled' or 'col_2fa_enabled'
๐ก Suggested renames:
'timestamp' โ timestamp_value | event_timestamp | ts
'select' โ select_value | select_col
'2fa_enabled' โ col_2fa_enabled | _2fa_enabled
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
All the ways to use it
Plain dicts
report = pf.check("User", fields=[
{"name": "user_id", "type": "string"},
{"name": "email", "type": "string"},
{"name": "timestamp", "type": "string"}, # flagged
{"name": "created_at", "type": "timestamp"},
], target_systems=["bigquery", "snowflake"])
SchemaField objects (IDE autocomplete + type safety)
from schema_preflight import SchemaField
fields = [
SchemaField("user_id", "string", required=True),
SchemaField("email", "string", required=True, max_length=255),
SchemaField("tags", "array", items=SchemaField("tag", "string")),
SchemaField("address", "object", fields=[
SchemaField("city", "string"),
SchemaField("country", "string"),
SchemaField("select", "string"), # nested reserved word โ still caught
]),
]
pf.check("User", fields).raise_if_critical()
PySpark (reads metadata โ no data scan)
df = spark.read.parquet("s3://raw/clickstream/")
pf.check_spark_schema(df, "ClickEvent",
target_systems=["bigquery", "snowflake", "redshift"]
).raise_if_critical()
df.write.parquet("s3://clean/clickstream/")
pandas
df = pd.read_csv("orders.csv")
pf.check_pandas_schema(df, "Orders").raise_if_critical()
SQLAlchemy
orders = Table("orders", meta,
Column("order_id", String),
Column("timestamp", String), # flagged
)
pf.check_sqlalchemy_table(orders).raise_if_critical()
Avro schema file
import json
with open("order.avsc") as f:
schema = json.load(f)
pf.check_avro_schema(schema).raise_if_critical()
JSON Schema (draft-07)
pf.check_json_schema({
"title": "Order",
"type": "object",
"properties": {
"order_id": {"type": "string"},
"timestamp": {"type": "string"}, # flagged
}
}).raise_if_critical()
Infer from a sample dict
sample = {"order_id": "123", "timestamp": "2025-01-01", "total": 99.99}
pf.check_dict_sample(sample, "Order").print_summary()
Quick single field check
result = pf.check_name("timestamp")
# {"safe": False, "affected_systems": ["bigquery","snowflake",...],
# "suggestions": ["timestamp_value","event_timestamp","ts"]}
Get safe renames
pf.suggest("timestamp")
# โ ["timestamp_value", "event_timestamp", "ts", "data_timestamp"]
CLI
# Full schema check
preflight check \
--name OrderEvent \
--format avro \
--systems bigquery snowflake redshift spark \
--fields order_id:string timestamp:string total:number select:integer
# Check from JSON file
preflight check --file schema.json --systems bigquery snowflake
# Single field name check
preflight check-name timestamp
# Get safe suggestions
preflight suggest select
# List all supported systems
preflight systems
# JSON output (for CI pipelines)
preflight check --json --file schema.json
Use in CI/CD (GitHub Actions)
Block PRs that introduce schema breaking changes:
- name: Schema preflight
run: |
pip install schema-preflight
preflight check \
--file schemas/order_event.json \
--systems bigquery snowflake redshift spark
# exits 1 and blocks merge if CRITICAL issues found
What it checks โ 24 edge case categories
All sourced from official system documentation.
| # | Check | Systems |
|---|---|---|
| 1 | Reserved SQL keywords (via sqlglot โ 31 dialects, always current) | All |
| 2 | System type name collisions (TIMESTAMP, STRING, BOOLEAN as column names) | All |
| 3 | BigQuery reserved prefixes (_TABLE_, _FILE_, _PARTITION_, _CHANGE_*, _SESSION_) |
BigQuery |
| 4 | Names starting with a digit | All |
| 5 | Special characters (spaces, hyphens, dots) | All |
| 6 | Leading / trailing whitespace | All |
| 7 | Empty field name | All |
| 8 | Column name length limits (BQ:300, SF:255, RS:127, PG:63, MySQL:64) | All |
| 9 | Duplicate names after case-folding | All |
| 10 | Unicode chars in systems that forbid them | BQ, Redshift, Hive |
| 11 | Double-underscore prefix (pseudo-column collision) | BQ, Redshift, Postgres |
| 12 | Avro name regex [A-Za-z_][A-Za-z0-9_]* |
Kafka/Avro |
| 13 | Avro complex union types (more than [null, T]) |
Kafka/Avro |
| 14 | Array of array โ BigQuery hard limit | BigQuery |
| 15 | Max nesting depth 15 โ BigQuery hard limit | BigQuery |
| 16 | Max 10,000 columns including nested โ BigQuery hard limit | BigQuery |
| 17 | Max 1,600 columns โ Redshift / Postgres hard limit | Redshift, Postgres |
| 18 | Type mapping safety per system | All |
| 19 | Decimal precision exceeds system maximum | All |
| 20 | MAP type with non-string key โ crashes Redshift connector | Redshift |
| 21 | MAP type โ unsupported in BigQuery (no native MAP type) | BigQuery |
| 22 | Array type โ unsupported in Redshift Spectrum with Avro format | Redshift |
| 23 | SQL function name collisions (COUNT, SUM, RANK โ work but cause confusion) | All |
| 24 | ClickHouse case-sensitivity (ClickHouse is case-sensitive, others are not) | ClickHouse |
Supported systems
| ID | System | sqlglot dialect |
|---|---|---|
bigquery |
Google BigQuery | โ |
snowflake |
Snowflake | โ |
redshift |
Amazon Redshift | โ |
spark |
Apache Spark SQL | โ |
databricks |
Databricks / Delta Lake | โ |
hive |
Apache Hive | โ |
postgres |
PostgreSQL | โ |
mysql |
MySQL | โ |
duckdb |
DuckDB | โ |
trino |
Trino | โ |
clickhouse |
ClickHouse | โ |
kafka_avro |
Kafka / Avro | custom |
Supported field types
string integer long float double number boolean bytes
date time datetime timestamp array object record map
enum decimal null uuid json
Supported source formats
json avro parquet protobuf csv orc
Why this is different from every other tool
| Tool | What it does | Gap |
|---|---|---|
| Confluent Schema Registry | Avro compatibility for Kafka | Single system, no cross-system check |
| AWS Glue Schema Registry | Schema for AWS ecosystem | AWS-only, no cross-system check |
| sqlglot | SQL parser, 31 dialects | Checks SQL queries โ not schema field names |
| buf.build | Protobuf registry | Protobuf only |
| DataHub | Data contracts | Requires full platform, no offline check |
| schema-preflight | Field name + type check across all systems | First offline cross-system pre-flight tool |
Publish to PyPI
pip install build twine
python3 -m build
python3 -m twine upload dist/*
License
MIT ยฉ Amrutham Akshithraj
Project details
Release history Release notifications | RSS feed
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 schema_preflight-2.0.0.tar.gz.
File metadata
- Download URL: schema_preflight-2.0.0.tar.gz
- Upload date:
- Size: 31.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ca8cc60db1febe345f613f456c16a0139e7208bbb6d3aecf809e952404d9d47
|
|
| MD5 |
7619c1652f1335c744587d79cbe3e455
|
|
| BLAKE2b-256 |
3015f258e53b184c061735667d752d3870900eda7ca46016472c34f1500da0a4
|
File details
Details for the file schema_preflight-2.0.0-py3-none-any.whl.
File metadata
- Download URL: schema_preflight-2.0.0-py3-none-any.whl
- Upload date:
- Size: 29.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3786aee79d39dce05acb006b12880f7f3a741bf78abe80c3a7e85337b8be4b0e
|
|
| MD5 |
8ac29621b2ee75e2fb5b20edc1279530
|
|
| BLAKE2b-256 |
0636f5fe0fef098d4424e4725905fa74a8cdeba2ccb6b0fb02da9551a2fff92a
|