Skip to content

refactor(native): split the native crate into a layered workspace - #983

Merged
kvlonge merged 10 commits into
mainfrom
refactor/native-workspace-split
Oct 6, 2026
Merged

kvlonge merged 10 commits into
mainfrom
refactor/native-workspace-split

Conversation

@kvlonge

@kvlonge kvlonge commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Why

The native code lived in one 46k-line crate that mixed text scanning, polyglot analysis, rules, lint and the PyO3 bindings. Clear crate boundaries come before moving more of the compiler to Rust: plain-Rust crates can be tested without Python, and dependencies stay one-way.

Changes

  • Split crates/sqlbuild-rules-native into a workspace: sqlbuild-core (shared constants, panic boundary), sqlbuild-sqltext (lexical scanning, model headers, static substitution and references), sqlbuild-analysis (polyglot analysis, binding catalog, semantic validation, SQL tests), sqlbuild-rules (rules, lint, quality, formatting, config) and sqlbuild-python, the only PyO3 crate, still built as sqlbuild._native.
  • Modules moved with their tests. PyO3 classes became thin wrappers with the same names, signatures, GIL release and exceptions.
  • The release version moved to [workspace.package]; maturin, uv cache keys, release-please and fensu follow the new paths.
  • make check-ci runs a layering check: no upward crate dependency, PyO3 only in sqlbuild-python, no polyglot below sqlbuild-analysis, and matching workspace, pyproject and release-please versions.
  • The build identity now also hashes the workspace Cargo.toml, so dependency feature and profile changes invalidate native caches.

Verification

  • cargo test --workspace --all-features (113 tests), clippy, fmt.
  • make check-ci, including fensu and the new layering tests.
  • About 2,100 focused Python tests across rules, lint, SQL analysis, SQL-test assembly and the compiler pipeline.
  • The _native module surface matches main; sqb compile --json --manifest --dag on the waffle-shop example matches main apart from timings, with identical compiled files and DAG.
  • Compile JSON, manifest, DAG and compiled files on a larger real-world project are byte-identical to main, with and without custom rules.
  • Same-machine dense compile comparison showed no slowdown; cold release build time is unchanged.

…versioned workspace

The extension crate becomes the Python boundary crate of a Cargo workspace. The release
version moves to the workspace package so every native crate shares it, and maturin, uv
cache keys, release automation and fensu paths follow the new location.
…odule

ProjectCatalog, CompactAnalysisJob, BindingPositions and ParsedRulesRequest become thin
Python classes in bindings that wrap plain native models, and catalog mapping requests are
converted from Python at the boundary. Method names, signatures, GIL release, panic
handling and exception types are unchanged, so later crates can stay free of PyO3.
… panic boundary

The constants used by more than one future crate and the PyO3-free panic catcher move to a
dependency-free sqlbuild-core crate, with the panic tests. The Python crate keeps the named
exception and guards, and the build identity now hashes every workspace crate.
…odel headers

SQL scanning, the rules quote policy, model header tokenization and matching, static
variable substitution and static reference extraction move with their tests to a
polyglot-free sqlbuild-sqltext crate. Items other crates use become public; behaviour is
unchanged.
Query analysis, the binding catalog, semantic validation and usage, column references,
SQL tokens and SQL-test extraction, planning and rendering move with their tests and the
function-name data to sqlbuild-analysis, the lowest crate that depends on polyglot. A
test-support feature exposes one test-only helper to dependent crates' tests, and the
workspace states the insertion-ordered JSON feature every crate already relied on.
… formatting

Built-in rules, the rules engine and custom-rule host, SQL lint, quality checks, the
formatter, rules configuration and the request models move with their tests to
sqlbuild-rules. It owns the build identity script, which still hashes every workspace
crate, so sqlbuild-python is left with only the PyO3 bindings.
…are the crates to fensu

Items other crates use are reached through main entry points, models and types modules
instead of helpers: the static reference type moves to types.rs, the rules dialect and the
test-only built-in function list gain main entries, and the panic boundary splits into one
entry per function with its message in constants. fensu now declares every workspace
crate and its exceptions follow the moved files.
…s in check-ci

The workspace declares its crates from the bottom up, and a check run by make check-ci
fails when a crate depends on a later layer (resolving aliased workspace dependencies to
their packages), when any crate other than sqlbuild-python reaches PyO3, or when a crate
below sqlbuild-analysis reaches polyglot. It also fails when the workspace, pyproject and
release-please versions differ, since the version is part of the build identity.
crates/README.md documents the crates, the graph and the check.
Dependency features and release profiles live in the workspace Cargo.toml, so changing them
left BUILD_IDENTITY and every cache keyed on it unchanged. The build script now hashes and
watches the workspace manifest next to the lockfile and exports the hashed workspace files,
which a test asserts.
@kvlonge
kvlonge enabled auto-merge (squash) October 6, 2026 15:57
…pace-split

# Conflicts:
#	Cargo.lock
#	crates/sqlbuild-rules/Cargo.toml
@kvlonge
kvlonge merged commit 06fa96d into main Oct 6, 2026
34 checks passed
@kvlonge
kvlonge deleted the refactor/native-workspace-split branch October 6, 2026 16:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant