tobilg/polyglot

Rust/Wasm-powered SQL transpiler for more than 30 SQL dialects

Rust

970

208 commits

updated Oct 9, 2026

See the code

README

Polyglot

Rust/Wasm-powered SQL transpiler for more than 30 SQL dialects, inspired by sqlglot.

Polyglot parses, generates, transpiles, and formats SQL across more than 30 SQL dialects. It ships as:

There's also a playground where you can try it out in the browser, as well as Rust API Docs, TypeScript API Docs, and Python API Docs.

Release notes are tracked in CHANGELOG.md.

Features

  • Transpile SQL between any pair of more than 30 SQL dialects
  • Parse SQL into a fully-typed AST
  • Generate SQL back from AST nodes
  • Format / pretty-print SQL
  • Fluent builder API for constructing queries programmatically
  • Validation with syntax, semantic, and schema-aware checks
  • Column lineage and OpenLineage-compatible payload generation
  • Compact query analysis facts for projections, relations, CTEs, and set operations
  • AST visitor utilities for walking, transforming, and analyzing queries
  • Stack-safety hardening on native targets via default-on stacker
  • C FFI shared/static library for multi-language bindings (polyglot-sql-ffi)
  • Python bindings powered by PyO3 (polyglot-sql on PyPI)

Supported Dialects

AthenaBigQueryClickHouseCockroachDBDatabricks
DorisDremioDrillDruidDuckDB
DuneExasolFabricHiveMaterialize
MySQLOraclePostgreSQLPrestoRedshift
RisingWaveSingleStoreSnowflakeSolrSpark
SQLiteStarRocksTableauTeradataTiDB
TrinoTSQLVerticaDataFusionSAP HANA
Generic SQL

Quick Start

Rust

use polyglot_sql::{transpile, DialectType};

// Transpile MySQL to PostgreSQL
let result = transpile(
    "SELECT IFNULL(a, b) FROM t",
    DialectType::MySQL,
    DialectType::Postgres,
).unwrap();
assert_eq!(result[0], "SELECT COALESCE(a, b) FROM t");
use polyglot_sql::builder::*;

// Fluent query builder
let query = select(["id", "name"])
    .from("users")
    .where_(col("age").gt(lit(18)))
    .order_by(["name"])
    .limit(10)
    .build();

See the full Rust crate README for more examples.

TypeScript

npm install @polyglot-sql/sdk
import { transpile, Dialect } from '@polyglot-sql/sdk';

// Transpile MySQL to PostgreSQL
const result = transpile(
  'SELECT IFNULL(a, b) FROM t',
  Dialect.MySQL,
  Dialect.PostgreSQL,
);
console.log(result.sql[0]); // SELECT COALESCE(a, b) FROM t
import { select, col, lit } from '@polyglot-sql/sdk';

// Fluent query builder
const sql = select('id', 'name')
  .from('users')
  .where(col('age').gt(lit(18)))
  .orderBy(col('name').asc())
  .limit(10)
  .toSql('postgresql');

See the full TypeScript SDK README for more examples.

Python

pip install polyglot-sql
import polyglot_sql

result = polyglot_sql.transpile(
    "SELECT IFNULL(a, b) FROM t",
    read="mysql",
    write="postgres",
)
print(result[0])  # SELECT COALESCE(a, b) FROM t

See the full Python bindings README.

Go

go get github.com/tobilg/polyglot/packages/go

The Go module contains the PureGo wrapper only. Runtime API calls require a separate matching polyglot-sql-ffi shared library (.so, .dylib, or .dll) from the same Polyglot release or a local FFI build:

cargo build -p polyglot-sql-ffi --profile ffi_release
export POLYGLOT_SQL_FFI_PATH="$PWD/target/ffi_release/libpolyglot_sql_ffi.so"
import (
    "fmt"

    polyglot "github.com/tobilg/polyglot/packages/go"
)

client, err := polyglot.OpenDefault()
if err != nil {
    panic(err)
}
defer client.Close()

result, err := client.Transpile(
    "SELECT IFNULL(a, b) FROM t",
    "mysql",
    "postgres",
)
if err != nil {
    panic(err)
}
fmt.Println(result[0]) // SELECT COALESCE(a, b) FROM t

The Go SDK uses PureGo over polyglot-sql-ffi; it does not download native libraries or bundle release artifacts in the Go module. Build/download the FFI shared library and set POLYGLOT_SQL_FFI_PATH or pass its path to polyglot.Open. See the full Go SDK README.

Lineage and OpenLineage Output

Polyglot can trace column lineage through SQL queries and can generate OpenLineage-compatible JSON payloads from that analysis. The OpenLineage support currently produces columnLineage dataset facets, optional schema facets, and JobEvent / RunEvent payloads for supported query shapes such as SELECT, set-operation queries, INSERT ... SELECT, and CREATE TABLE AS SELECT.

Lineage can be selected by output name or by zero-based output ordinal. Ordered output metadata preserves unnamed projections and unresolved wildcards, so callers can decide whether a positional lookup is complete before tracing it. Immediate set-operation branch roots include the operator, original zero-based branch ordinal, and ALL flag. For OpenLineage, both UNION branches are direct value dependencies; the right branch of EXCEPT and INTERSECT is an indirect FILTER dependency.

OpenLineage transport and client emission are intentionally out of scope: Polyglot builds payloads for callers to inspect, persist, or send through their own infrastructure.

Compact Query Analysis

For applications that need summary facts instead of a full AST or full lineage graph, analyze_query / analyzeQuery returns a compact payload with output projections, direct visible relations, transitive physical baseTables, CTE names and top-level cteFacts, original starProjections, set-operation branches, transform kinds, conservative projection nullability, optional type hints, and upstream column references. The API is additive and uses the same optional ValidationSchema shape as schema-aware validation and lineage. Each set-operation branch is classified as a value or filter contribution. columnUses separately groups references used in joins, filters, grouping, HAVING/QUALIFY, window partition/order/frame expressions, ordering, aggregate filters/ordering, and set-operation filter branches. Each fact includes a context, scopePath, expressionPath, dialect-rendered expressionSql, and references using the existing source identity and confidence vocabulary. For example, SELECT o.id FROM orders o WHERE o.amount > 0 reports an amount use with context filter; the id projection's upstream facts are unchanged.

Paths distinguish CTEs, derived tables, subqueries and nested set-operation branches (for example root.branches[1]). They are deterministic within an analysis, not persistent identifiers across edits. References preserve repeated occurrences; multiple upstream dependencies may share a use's location. Optional span objects contain half-open start/end Unicode-character offsets into the original SQL, not UTF-8 bytes or JavaScript UTF-16 offsets. Reference spans locate the original use, including uses of CTE columns or output aliases. Complete expression spans are returned only when available; generated SQL is not used to invent source locations. Implicit NATURAL JOIN keys need complete source columns; otherwise an unknown wildcard reference records that gap. These are direct query-analysis facts, not cross-query impact analysis.

Validation uses broad type families, while query analysis preserves detailed schema type strings such as DECIMAL(10,2) for typeHint values when they can be parsed. For physical relations, name remains the qualified display name while catalog, schema, and table expose parsed identifier parts for consumers that need to distinguish qualifiers from table names.

Validation schema JSON uses:

{
  "strict": true,
  "tables": [
    {
      "name": "orders",
      "schema": "analytics",
      "aliases": ["o"],
      "primaryKey": ["id"],
      "uniqueKeys": [["external_id"]],
      "foreignKeys": [
        {
          "columns": ["customer_id"],
          "references": { "table": "customers", "columns": ["id"] }
        }
      ],
      "columns": [
        { "name": "id", "type": "INT", "nullable": false, "primaryKey": true },
        { "name": "amount", "type": "DECIMAL(10,2)", "nullable": true }
      ]
    }
  ]
}

Use the type key for column types in JSON. dataType / data_type are not accepted aliases for this schema payload.

Format Guard Rails

SQL formatting runs through guard limits in Rust core to prevent pathological inputs from exhausting memory:

  • maxInputBytes: 16 MiB (default)
  • maxTokens: 1_000_000 (default)
  • maxAstNodes: 1_000_000 (default)
  • maxSetOpChain: 256 (default)

Guard failures return error codes in the message (E_GUARD_INPUT_TOO_LARGE, E_GUARD_TOKEN_BUDGET_EXCEEDED, E_GUARD_AST_BUDGET_EXCEEDED, E_GUARD_SET_OP_CHAIN_EXCEEDED).

Configuration surface by runtime:

  • Rust: configurable via format_with_options.
  • WASM: configurable via format_sql_with_options / format_sql_with_options_value.
  • TypeScript SDK: configurable via formatWithOptions.
  • C FFI: configurable via polyglot_format_with_options.
  • Python: configurable via keyword-only format_sql(..., max_*) overrides.

WASM low-level example (from polyglot-sql-wasm exports):

import init, { format_sql_with_options } from "./polyglot_sql_wasm.js";

await init();
const raw = format_sql_with_options(
  "SELECT a,b FROM t",
  "generic",
  JSON.stringify({
    maxInputBytes: 2 * 1024 * 1024,
    maxTokens: 250000,
    maxAstNodes: 250000,
    maxSetOpChain: 128
  }),
);
const result = JSON.parse(raw);

Stack Safety

On native Rust builds, polyglot-sql enables the optional stacker feature by default. This adds stack-growth protection around the deepest parser / generator / transpile entry points so pathological or heavily nested SQL is less likely to abort the process with a stack overflow.

Important scope notes:

  • Rust, C FFI, and Python native builds inherit this by default.
  • WASM does not use stacker; polyglot-sql-wasm depends on the core crate with default-features = false.
  • A few paths still use explicitly larger thread stacks as defense-in-depth for very deep workloads, especially some test harnesses, the Python worker thread, and the bench_json example.

If you want to disable stacker for a native Rust build, turn off default features and opt back into the ones you need:

[dependencies]
polyglot-sql = { version = "0.13.3", default-features = false, features = ["all-dialects", "transpile"] }

That can reduce overhead slightly on trusted inputs, but you lose the default stack-growth protection for deeply nested SQL.

Project Structure

polyglot/
├── crates/
│   ├── polyglot-sql/           # Core Rust library (parser, generator, builder)
│   ├── polyglot-sql-function-catalogs/ # Optional dialect function catalogs (feature-gated data)
│   ├── polyglot-sql-wasm/      # WASM bindings
│   ├── polyglot-sql-ffi/       # C ABI bindings (.so/.dylib/.dll + .a/.lib + header)
│   └── polyglot-sql-python/    # Python bindings (PyO3 + maturin, published on PyPI)
├── packages/
│   ├── sdk/                    # TypeScript SDK (@polyglot-sql/sdk on npm)
│   ├── go/                     # Go SDK backed by polyglot-sql-ffi
│   ├── documentation/          # TypeScript API documentation site
│   ├── playground/             # Playground for testing the SDK (React 19, Tailwind v4, Vite)
│   └── python-docs/            # Python API documentation site (Cloudflare Pages)
├── examples/
│   ├── rust/                   # Rust example
│   ├── typescript/             # TypeScript SDK example
│   └── c/                      # C FFI example
└── tools/
    ├── sqlglot-compare/        # Test extraction & comparison tool
    └── bench-compare/          # Performance benchmarks

Examples

Standalone example projects are available in the examples/ directory. Each one pulls the latest published package and can be run independently.

Rust

cargo run --manifest-path examples/rust/Cargo.toml

TypeScript

cd examples/typescript
pnpm install && pnpm start

Building from Source

Use pnpm 12 for the JavaScript workspace (CI uses pnpm 12.4.2 and Node.js 24). Run pnpm install --frozen-lockfile from the repository root before building. Required dependency build scripts are approved in pnpm-workspace.yaml.

# Build Rust core
cargo build -p polyglot-sql

# Build C FFI crate (shared/static libs + generated header)
cargo build -p polyglot-sql-ffi --profile ffi_release

# Build Python extension / wheel
make develop-python
make build-python

# Build WASM + TypeScript SDK
make build-all

# Or step by step:
cd crates/polyglot-sql-wasm && wasm-pack build --target bundler --release
cd packages/sdk && npm run build

C FFI

Polyglot provides a stable C ABI in crates/polyglot-sql-ffi.

  • Crate README: crates/polyglot-sql-ffi/README.md
  • Generated header: crates/polyglot-sql-ffi/polyglot_sql.h
  • Example program: examples/c/main.c
  • Make targets:
    • make build-ffi
    • make generate-ffi-header
    • make build-ffi-example
    • make test-ffi

For tagged releases (v*), CI also attaches prebuilt FFI artifacts and checksums to GitHub Releases.

Python Bindings

Polyglot provides first-party Python bindings in crates/polyglot-sql-python.

Function Catalogs

Optional dialect function catalogs are provided via crates/polyglot-sql-function-catalogs.

  • Crate README: crates/polyglot-sql-function-catalogs/README.md
  • Core feature flags:
    • stacker (enabled by default on native polyglot-sql builds)
    • function-catalog-clickhouse
    • function-catalog-duckdb
    • function-catalog-all-dialects
  • Intended behavior: compile-time inclusion, one-time load in core, auto-use during schema validation type checks.

Testing

CI runs four Rust suites in parallel for pull requests, pushes to main, and v* release tags: core/debug, release fixtures, bindings, and feature gates. The required rust-test check succeeds only when every suite and the quality job pass. Go integration tests reuse the bindings suite's FFI release library; published multi-platform FFI archives remain separate.

To reproduce one suite locally, run make test-rust-ci-core, make test-rust-ci-release-fixtures, make test-rust-ci-bindings, or make test-rust-ci-feature-gates. Extract fixtures first with make extract-fixtures for core or make extract-all-fixtures for release fixtures. make test-rust-verify remains the sequential local verification command and shares its test recipes with CI.

Polyglot currently runs 11,333 SQLGlot fixture cases plus additional project-specific suites. All strict pass/fail suites are at 100% in the latest verification run.

CategoryCountPass Rate
SQLGlot generic identity977100%
SQLGlot dialect identity4,086100%
SQLGlot transpilation6,061100%
SQLGlot transpile (generic)154100%
SQLGlot parser32100%
SQLGlot pretty-print23100%
Lib unit tests (non-ignored)1,150100%
Custom dialect identity276100%
Custom dialect transpilation347100%
ClickHouse parser corpus (non-skipped)9,417100%
ClickHouse normalized round trips (in-scope)121,020100%
FFI tests (unit + integration)67100%
Python bindings tests (non-skipped)178100%
Total (strict Rust/FFI pass/fail case count)143,610100%

One Rust unit test is ignored, one Python capability-contract test is skipped unless POLYGLOT_API_CONTRACT is configured (179/179 pass when it is), and 172 out-of-scope KQL/non-SQL ClickHouse fixtures are excluded from the strict counts.

# Setup fixtures (required once)
make setup-fixtures

# Run all tests
make test-rust-all          # All 11,333 SQLGlot fixture cases
make test-rust-lib          # 1,150 active lib unit tests (1 ignored)
make test-rust-verify       # All 143,610 strict Rust/FFI cases
make test-ffi               # 67 FFI unit/integration tests
make test-python            # 178 active Python tests (1 contract test skipped by default)

# Individual test suites
make test-rust-identity     # 977 generic identity cases
make test-rust-dialect      # 4,086 dialect identity cases
make test-rust-transpile    # 6,061 transpilation cases
make test-rust-transpile-generic # 154 generic transpile cases
make test-rust-parser       # 32 parser cases
make test-rust-pretty       # 23 pretty-print cases
make test-rust-clickhouse-parser   # 9,417 ClickHouse files
make test-rust-clickhouse-coverage # 121,020 normalized round trips

# Additional tests
make test-rust-roundtrip    # Organized roundtrip unit tests
make test-rust-matrix       # Dialect matrix transpilation tests
make test-rust-compat       # SQLGlot compatibility tests
make test-rust-errors       # Error handling tests
make test-rust-functions    # Function normalization tests

# TypeScript SDK tests
cd packages/sdk && npm test

# Full comparison against Python SQLGlot
make test-compare

Benchmarks

make bench-compare          # Compare polyglot-sql vs sqlglot performance
make bench-rust             # Rust benchmarks (JSON output)
make bench-python           # Python sqlglot benchmarks (JSON output)
cargo bench -p polyglot-sql  # Criterion benchmarks

SQLGlot comparison targets build Polyglot with the same python_release profile used for published Python wheels. This keeps benchmark optimizer settings aligned with the shipped Python package; the first build can take several minutes because it uses thin LTO.

Fuzzing

cargo +nightly fuzz run fuzz_parser
cargo +nightly fuzz run fuzz_roundtrip
cargo +nightly fuzz run fuzz_transpile

Makefile Targets

TargetDescription
make helpShow all available commands
make build-allBuild core release + FFI + Python + bindings + WASM/SDK
make build-wasmBuild WASM package + TypeScript SDK
make build-ffiBuild C FFI crate (ffi_release profile)
make generate-ffi-headerGenerate C header via cbindgen/build.rs
make build-ffi-exampleBuild + run C example against FFI lib
make develop-pythonBuild/install Python extension in uv-managed env
make build-pythonBuild Python wheels with maturin (python_release profile)
make test-ffiRun FFI integration tests
make test-rustRun SQLGlot-named Rust tests in polyglot-sql
make test-rust-allRun all 11,333 SQLGlot fixture cases
make test-rust-libRun 1,150 active lib unit tests (1 ignored)
make test-rust-verifyFull verification suite
make test-rust-clickhouse-parserRun strict ClickHouse parser suite
make test-rust-clickhouse-coverageRun the strict ClickHouse normalized round-trip suite
make test-compareCompare against Python sqlglot
make bench-comparePerformance comparison
make bench-rust-parsing-reportRun rust_parsing bench and generate Markdown report
make bench-parseCore parse benchmark (polyglot vs sqlglot)
make bench-parse-quickFaster core parse benchmark mode
make bench-parse-fullParse benchmark including optional parsers
make extract-fixturesRegenerate JSON fixtures from Python
make setup-fixturesCreate fixture symlink for Rust tests
make generate-bindingsGenerate TypeScript type bindings
make test-pythonRun Python bindings tests
make typecheck-pythonRun Python bindings type-check
make documentation-buildBuild documentation site
make documentation-deployDeploy documentation to Cloudflare Pages
make python-docs-buildBuild Python API docs site
make python-docs-deployDeploy Python API docs to Cloudflare Pages
make playground-buildBuild playground
make playground-deployDeploy playground to Cloudflare Pages
make cleanRemove all build artifacts

Licenses

MIT

rust
sql
sqlglot
transpiler
wasm

tobilg/polyglot

Rust/Wasm-powered SQL transpiler for more than 30 SQL dialects

Rust

970

208 commits

updated Oct 9, 2026

See the code

README

Polyglot

Rust/Wasm-powered SQL transpiler for more than 30 SQL dialects, inspired by sqlglot.

Polyglot parses, generates, transpiles, and formats SQL across more than 30 SQL dialects. It ships as:

There's also a playground where you can try it out in the browser, as well as Rust API Docs, TypeScript API Docs, and Python API Docs.

Release notes are tracked in CHANGELOG.md.

Features

  • Transpile SQL between any pair of more than 30 SQL dialects
  • Parse SQL into a fully-typed AST
  • Generate SQL back from AST nodes
  • Format / pretty-print SQL
  • Fluent builder API for constructing queries programmatically
  • Validation with syntax, semantic, and schema-aware checks
  • Column lineage and OpenLineage-compatible payload generation
  • Compact query analysis facts for projections, relations, CTEs, and set operations
  • AST visitor utilities for walking, transforming, and analyzing queries
  • Stack-safety hardening on native targets via default-on stacker
  • C FFI shared/static library for multi-language bindings (polyglot-sql-ffi)
  • Python bindings powered by PyO3 (polyglot-sql on PyPI)

Supported Dialects

AthenaBigQueryClickHouseCockroachDBDatabricks
DorisDremioDrillDruidDuckDB
DuneExasolFabricHiveMaterialize
MySQLOraclePostgreSQLPrestoRedshift
RisingWaveSingleStoreSnowflakeSolrSpark
SQLiteStarRocksTableauTeradataTiDB
TrinoTSQLVerticaDataFusionSAP HANA
Generic SQL

Quick Start

Rust

use polyglot_sql::{transpile, DialectType};

// Transpile MySQL to PostgreSQL
let result = transpile(
    "SELECT IFNULL(a, b) FROM t",
    DialectType::MySQL,
    DialectType::Postgres,
).unwrap();
assert_eq!(result[0], "SELECT COALESCE(a, b) FROM t");
use polyglot_sql::builder::*;

// Fluent query builder
let query = select(["id", "name"])
    .from("users")
    .where_(col("age").gt(lit(18)))
    .order_by(["name"])
    .limit(10)
    .build();

See the full Rust crate README for more examples.

TypeScript

npm install @polyglot-sql/sdk
import { transpile, Dialect } from '@polyglot-sql/sdk';

// Transpile MySQL to PostgreSQL
const result = transpile(
  'SELECT IFNULL(a, b) FROM t',
  Dialect.MySQL,
  Dialect.PostgreSQL,
);
console.log(result.sql[0]); // SELECT COALESCE(a, b) FROM t
import { select, col, lit } from '@polyglot-sql/sdk';

// Fluent query builder
const sql = select('id', 'name')
  .from('users')
  .where(col('age').gt(lit(18)))
  .orderBy(col('name').asc())
  .limit(10)
  .toSql('postgresql');

See the full TypeScript SDK README for more examples.

Python

pip install polyglot-sql
import polyglot_sql

result = polyglot_sql.transpile(
    "SELECT IFNULL(a, b) FROM t",
    read="mysql",
    write="postgres",
)
print(result[0])  # SELECT COALESCE(a, b) FROM t

See the full Python bindings README.

Go

go get github.com/tobilg/polyglot/packages/go

The Go module contains the PureGo wrapper only. Runtime API calls require a separate matching polyglot-sql-ffi shared library (.so, .dylib, or .dll) from the same Polyglot release or a local FFI build:

cargo build -p polyglot-sql-ffi --profile ffi_release
export POLYGLOT_SQL_FFI_PATH="$PWD/target/ffi_release/libpolyglot_sql_ffi.so"
import (
    "fmt"

    polyglot "github.com/tobilg/polyglot/packages/go"
)

client, err := polyglot.OpenDefault()
if err != nil {
    panic(err)
}
defer client.Close()

result, err := client.Transpile(
    "SELECT IFNULL(a, b) FROM t",
    "mysql",
    "postgres",
)
if err != nil {
    panic(err)
}
fmt.Println(result[0]) // SELECT COALESCE(a, b) FROM t

The Go SDK uses PureGo over polyglot-sql-ffi; it does not download native libraries or bundle release artifacts in the Go module. Build/download the FFI shared library and set POLYGLOT_SQL_FFI_PATH or pass its path to polyglot.Open. See the full Go SDK README.

Lineage and OpenLineage Output

Polyglot can trace column lineage through SQL queries and can generate OpenLineage-compatible JSON payloads from that analysis. The OpenLineage support currently produces columnLineage dataset facets, optional schema facets, and JobEvent / RunEvent payloads for supported query shapes such as SELECT, set-operation queries, INSERT ... SELECT, and CREATE TABLE AS SELECT.

Lineage can be selected by output name or by zero-based output ordinal. Ordered output metadata preserves unnamed projections and unresolved wildcards, so callers can decide whether a positional lookup is complete before tracing it. Immediate set-operation branch roots include the operator, original zero-based branch ordinal, and ALL flag. For OpenLineage, both UNION branches are direct value dependencies; the right branch of EXCEPT and INTERSECT is an indirect FILTER dependency.

OpenLineage transport and client emission are intentionally out of scope: Polyglot builds payloads for callers to inspect, persist, or send through their own infrastructure.

Compact Query Analysis

For applications that need summary facts instead of a full AST or full lineage graph, analyze_query / analyzeQuery returns a compact payload with output projections, direct visible relations, transitive physical baseTables, CTE names and top-level cteFacts, original starProjections, set-operation branches, transform kinds, conservative projection nullability, optional type hints, and upstream column references. The API is additive and uses the same optional ValidationSchema shape as schema-aware validation and lineage. Each set-operation branch is classified as a value or filter contribution. columnUses separately groups references used in joins, filters, grouping, HAVING/QUALIFY, window partition/order/frame expressions, ordering, aggregate filters/ordering, and set-operation filter branches. Each fact includes a context, scopePath, expressionPath, dialect-rendered expressionSql, and references using the existing source identity and confidence vocabulary. For example, SELECT o.id FROM orders o WHERE o.amount > 0 reports an amount use with context filter; the id projection's upstream facts are unchanged.

Paths distinguish CTEs, derived tables, subqueries and nested set-operation branches (for example root.branches[1]). They are deterministic within an analysis, not persistent identifiers across edits. References preserve repeated occurrences; multiple upstream dependencies may share a use's location. Optional span objects contain half-open start/end Unicode-character offsets into the original SQL, not UTF-8 bytes or JavaScript UTF-16 offsets. Reference spans locate the original use, including uses of CTE columns or output aliases. Complete expression spans are returned only when available; generated SQL is not used to invent source locations. Implicit NATURAL JOIN keys need complete source columns; otherwise an unknown wildcard reference records that gap. These are direct query-analysis facts, not cross-query impact analysis.

Validation uses broad type families, while query analysis preserves detailed schema type strings such as DECIMAL(10,2) for typeHint values when they can be parsed. For physical relations, name remains the qualified display name while catalog, schema, and table expose parsed identifier parts for consumers that need to distinguish qualifiers from table names.

Validation schema JSON uses:

{
  "strict": true,
  "tables": [
    {
      "name": "orders",
      "schema": "analytics",
      "aliases": ["o"],
      "primaryKey": ["id"],
      "uniqueKeys": [["external_id"]],
      "foreignKeys": [
        {
          "columns": ["customer_id"],
          "references": { "table": "customers", "columns": ["id"] }
        }
      ],
      "columns": [
        { "name": "id", "type": "INT", "nullable": false, "primaryKey": true },
        { "name": "amount", "type": "DECIMAL(10,2)", "nullable": true }
      ]
    }
  ]
}

Use the type key for column types in JSON. dataType / data_type are not accepted aliases for this schema payload.

Format Guard Rails

SQL formatting runs through guard limits in Rust core to prevent pathological inputs from exhausting memory:

  • maxInputBytes: 16 MiB (default)
  • maxTokens: 1_000_000 (default)
  • maxAstNodes: 1_000_000 (default)
  • maxSetOpChain: 256 (default)

Guard failures return error codes in the message (E_GUARD_INPUT_TOO_LARGE, E_GUARD_TOKEN_BUDGET_EXCEEDED, E_GUARD_AST_BUDGET_EXCEEDED, E_GUARD_SET_OP_CHAIN_EXCEEDED).

Configuration surface by runtime:

  • Rust: configurable via format_with_options.
  • WASM: configurable via format_sql_with_options / format_sql_with_options_value.
  • TypeScript SDK: configurable via formatWithOptions.
  • C FFI: configurable via polyglot_format_with_options.
  • Python: configurable via keyword-only format_sql(..., max_*) overrides.

WASM low-level example (from polyglot-sql-wasm exports):

import init, { format_sql_with_options } from "./polyglot_sql_wasm.js";

await init();
const raw = format_sql_with_options(
  "SELECT a,b FROM t",
  "generic",
  JSON.stringify({
    maxInputBytes: 2 * 1024 * 1024,
    maxTokens: 250000,
    maxAstNodes: 250000,
    maxSetOpChain: 128
  }),
);
const result = JSON.parse(raw);

Stack Safety

On native Rust builds, polyglot-sql enables the optional stacker feature by default. This adds stack-growth protection around the deepest parser / generator / transpile entry points so pathological or heavily nested SQL is less likely to abort the process with a stack overflow.

Important scope notes:

  • Rust, C FFI, and Python native builds inherit this by default.
  • WASM does not use stacker; polyglot-sql-wasm depends on the core crate with default-features = false.
  • A few paths still use explicitly larger thread stacks as defense-in-depth for very deep workloads, especially some test harnesses, the Python worker thread, and the bench_json example.

If you want to disable stacker for a native Rust build, turn off default features and opt back into the ones you need:

[dependencies]
polyglot-sql = { version = "0.13.3", default-features = false, features = ["all-dialects", "transpile"] }

That can reduce overhead slightly on trusted inputs, but you lose the default stack-growth protection for deeply nested SQL.

Project Structure

polyglot/
├── crates/
│   ├── polyglot-sql/           # Core Rust library (parser, generator, builder)
│   ├── polyglot-sql-function-catalogs/ # Optional dialect function catalogs (feature-gated data)
│   ├── polyglot-sql-wasm/      # WASM bindings
│   ├── polyglot-sql-ffi/       # C ABI bindings (.so/.dylib/.dll + .a/.lib + header)
│   └── polyglot-sql-python/    # Python bindings (PyO3 + maturin, published on PyPI)
├── packages/
│   ├── sdk/                    # TypeScript SDK (@polyglot-sql/sdk on npm)
│   ├── go/                     # Go SDK backed by polyglot-sql-ffi
│   ├── documentation/          # TypeScript API documentation site
│   ├── playground/             # Playground for testing the SDK (React 19, Tailwind v4, Vite)
│   └── python-docs/            # Python API documentation site (Cloudflare Pages)
├── examples/
│   ├── rust/                   # Rust example
│   ├── typescript/             # TypeScript SDK example
│   └── c/                      # C FFI example
└── tools/
    ├── sqlglot-compare/        # Test extraction & comparison tool
    └── bench-compare/          # Performance benchmarks

Examples

Standalone example projects are available in the examples/ directory. Each one pulls the latest published package and can be run independently.

Rust

cargo run --manifest-path examples/rust/Cargo.toml

TypeScript

cd examples/typescript
pnpm install && pnpm start

Building from Source

Use pnpm 12 for the JavaScript workspace (CI uses pnpm 12.4.2 and Node.js 24). Run pnpm install --frozen-lockfile from the repository root before building. Required dependency build scripts are approved in pnpm-workspace.yaml.

# Build Rust core
cargo build -p polyglot-sql

# Build C FFI crate (shared/static libs + generated header)
cargo build -p polyglot-sql-ffi --profile ffi_release

# Build Python extension / wheel
make develop-python
make build-python

# Build WASM + TypeScript SDK
make build-all

# Or step by step:
cd crates/polyglot-sql-wasm && wasm-pack build --target bundler --release
cd packages/sdk && npm run build

C FFI

Polyglot provides a stable C ABI in crates/polyglot-sql-ffi.

  • Crate README: crates/polyglot-sql-ffi/README.md
  • Generated header: crates/polyglot-sql-ffi/polyglot_sql.h
  • Example program: examples/c/main.c
  • Make targets:
    • make build-ffi
    • make generate-ffi-header
    • make build-ffi-example
    • make test-ffi

For tagged releases (v*), CI also attaches prebuilt FFI artifacts and checksums to GitHub Releases.

Python Bindings

Polyglot provides first-party Python bindings in crates/polyglot-sql-python.

Function Catalogs

Optional dialect function catalogs are provided via crates/polyglot-sql-function-catalogs.

  • Crate README: crates/polyglot-sql-function-catalogs/README.md
  • Core feature flags:
    • stacker (enabled by default on native polyglot-sql builds)
    • function-catalog-clickhouse
    • function-catalog-duckdb
    • function-catalog-all-dialects
  • Intended behavior: compile-time inclusion, one-time load in core, auto-use during schema validation type checks.

Testing

CI runs four Rust suites in parallel for pull requests, pushes to main, and v* release tags: core/debug, release fixtures, bindings, and feature gates. The required rust-test check succeeds only when every suite and the quality job pass. Go integration tests reuse the bindings suite's FFI release library; published multi-platform FFI archives remain separate.

To reproduce one suite locally, run make test-rust-ci-core, make test-rust-ci-release-fixtures, make test-rust-ci-bindings, or make test-rust-ci-feature-gates. Extract fixtures first with make extract-fixtures for core or make extract-all-fixtures for release fixtures. make test-rust-verify remains the sequential local verification command and shares its test recipes with CI.

Polyglot currently runs 11,333 SQLGlot fixture cases plus additional project-specific suites. All strict pass/fail suites are at 100% in the latest verification run.

CategoryCountPass Rate
SQLGlot generic identity977100%
SQLGlot dialect identity4,086100%
SQLGlot transpilation6,061100%
SQLGlot transpile (generic)154100%
SQLGlot parser32100%
SQLGlot pretty-print23100%
Lib unit tests (non-ignored)1,150100%
Custom dialect identity276100%
Custom dialect transpilation347100%
ClickHouse parser corpus (non-skipped)9,417100%
ClickHouse normalized round trips (in-scope)121,020100%
FFI tests (unit + integration)67100%
Python bindings tests (non-skipped)178100%
Total (strict Rust/FFI pass/fail case count)143,610100%

One Rust unit test is ignored, one Python capability-contract test is skipped unless POLYGLOT_API_CONTRACT is configured (179/179 pass when it is), and 172 out-of-scope KQL/non-SQL ClickHouse fixtures are excluded from the strict counts.

# Setup fixtures (required once)
make setup-fixtures

# Run all tests
make test-rust-all          # All 11,333 SQLGlot fixture cases
make test-rust-lib          # 1,150 active lib unit tests (1 ignored)
make test-rust-verify       # All 143,610 strict Rust/FFI cases
make test-ffi               # 67 FFI unit/integration tests
make test-python            # 178 active Python tests (1 contract test skipped by default)

# Individual test suites
make test-rust-identity     # 977 generic identity cases
make test-rust-dialect      # 4,086 dialect identity cases
make test-rust-transpile    # 6,061 transpilation cases
make test-rust-transpile-generic # 154 generic transpile cases
make test-rust-parser       # 32 parser cases
make test-rust-pretty       # 23 pretty-print cases
make test-rust-clickhouse-parser   # 9,417 ClickHouse files
make test-rust-clickhouse-coverage # 121,020 normalized round trips

# Additional tests
make test-rust-roundtrip    # Organized roundtrip unit tests
make test-rust-matrix       # Dialect matrix transpilation tests
make test-rust-compat       # SQLGlot compatibility tests
make test-rust-errors       # Error handling tests
make test-rust-functions    # Function normalization tests

# TypeScript SDK tests
cd packages/sdk && npm test

# Full comparison against Python SQLGlot
make test-compare

Benchmarks

make bench-compare          # Compare polyglot-sql vs sqlglot performance
make bench-rust             # Rust benchmarks (JSON output)
make bench-python           # Python sqlglot benchmarks (JSON output)
cargo bench -p polyglot-sql  # Criterion benchmarks

SQLGlot comparison targets build Polyglot with the same python_release profile used for published Python wheels. This keeps benchmark optimizer settings aligned with the shipped Python package; the first build can take several minutes because it uses thin LTO.

Fuzzing

cargo +nightly fuzz run fuzz_parser
cargo +nightly fuzz run fuzz_roundtrip
cargo +nightly fuzz run fuzz_transpile

Makefile Targets

TargetDescription
make helpShow all available commands
make build-allBuild core release + FFI + Python + bindings + WASM/SDK
make build-wasmBuild WASM package + TypeScript SDK
make build-ffiBuild C FFI crate (ffi_release profile)
make generate-ffi-headerGenerate C header via cbindgen/build.rs
make build-ffi-exampleBuild + run C example against FFI lib
make develop-pythonBuild/install Python extension in uv-managed env
make build-pythonBuild Python wheels with maturin (python_release profile)
make test-ffiRun FFI integration tests
make test-rustRun SQLGlot-named Rust tests in polyglot-sql
make test-rust-allRun all 11,333 SQLGlot fixture cases
make test-rust-libRun 1,150 active lib unit tests (1 ignored)
make test-rust-verifyFull verification suite
make test-rust-clickhouse-parserRun strict ClickHouse parser suite
make test-rust-clickhouse-coverageRun the strict ClickHouse normalized round-trip suite
make test-compareCompare against Python sqlglot
make bench-comparePerformance comparison
make bench-rust-parsing-reportRun rust_parsing bench and generate Markdown report
make bench-parseCore parse benchmark (polyglot vs sqlglot)
make bench-parse-quickFaster core parse benchmark mode
make bench-parse-fullParse benchmark including optional parsers
make extract-fixturesRegenerate JSON fixtures from Python
make setup-fixturesCreate fixture symlink for Rust tests
make generate-bindingsGenerate TypeScript type bindings
make test-pythonRun Python bindings tests
make typecheck-pythonRun Python bindings type-check
make documentation-buildBuild documentation site
make documentation-deployDeploy documentation to Cloudflare Pages
make python-docs-buildBuild Python API docs site
make python-docs-deployDeploy Python API docs to Cloudflare Pages
make playground-buildBuild playground
make playground-deployDeploy playground to Cloudflare Pages
make cleanRemove all build artifacts

Licenses

MIT

rust
sql
sqlglot
transpiler
wasm

Significant stargazers

(top 24 of 53)

Pooya Parsa

5,150 followers · starred Mar 2026

Luke Edwards

5,090 followers · starred Feb 2026

Matt Silverlock

1,125 followers · starred Feb 2026

Michael K.

71 followers · starred Feb 2026