M4: row materialization (incremental source generator)#4
Merged
Conversation
Phase 2 (Codegen) gets its own minor version line per the semver convention: each phase owns a minor, carrying a prerelease suffix while the phase is in active development. The suffix drops to a clean 0.2.0 in the commit that closes the phase and gets tagged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The generator emits materialization code purely from the [SqlQuery] attribute and the declared partial method signature, never from .sqlbound/ snapshots. The roadmap forces this (codegen precedes verification, so no snapshot infrastructure exists when M4 is built), and it preserves ADR 0001''s DX stance that missing or stale snapshots degrade verification but never break the build. Snapshot-informed optimization remains a possible future amendment, not a commitment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
[SqlQuery] marks the static partial methods whose implementations the source generator (M4) emits. It lives in the runtime package, not the generator package, so user code compiles against SqlBound alone and the generator remains a pure build-time dependency. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The generator targets netstandard2.0 (Roslyn requirement) and packs into analyzers/dotnet/cs with no lib/ folder and no symbol package, so it can never become a runtime dependency of a consumer. Tests drive CSharpGeneratorDriver directly rather than the Microsoft.CodeAnalysis .Testing verifiers, which lag on xunit.v3 support; the harness exposes generated sources, generator diagnostics, and post-generation compilation diagnostics for snapshot assertions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ForAttributeWithMetadataName drives a transform that parses each [SqlQuery] method into a fully value-equatable model (EquatableArray, DiagnosticInfo instead of Diagnostic) so incremental caching works. Invalid usage reports SQLB001-008 as errors: non-partial or already implemented methods, non-static methods, missing DbConnection first parameter, unsupported return/row/parameter types, empty command text, and generic declarations. A valid method yields the emission model; code emission follows in the next commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The generated implementation resolves column ordinals once by constructor-parameter name before the read loop, then materializes each row with typed getter calls: nullable members map DBNull to null, non-nullable members throw a descriptive InvalidOperationException naming the column. Scalars bind as @name parameters (coalescing to DBNull only when the argument can be null), an optional DbTransaction is honored, and a missing CancellationToken parameter falls back to CancellationToken.None. Hint names carry an FNV-1a signature hash so output stays deterministic across processes, unlike string.GetHashCode. Snapshot tests lock the canonical output byte for byte and prove every variant compiles warning-free in a nullable-enabled compilation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The integration test project consumes SqlBound.Generators as an analyzer-only project reference (the same shape a NuGet consumer gets from analyzers/dotnet/cs) and executes a generated [SqlQuery] method against a real in-memory SQLite database: rows materialize including the DBNull-to-nullable path, and the generated method reads writes made by Dapper on the same shared connection, extending the M3 coexistence proof to the static query surface. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Opens Phase 2 (Codegen) at
0.2.0-preview.1and delivers M4: an incremental source generator that implements[SqlQuery]-annotatedstatic partialmethods with straight-line, reflection-freeDbDataReadermaterialization..sqlbound/snapshots. Forced by the roadmap (codegen precedes verification) and consistent with ADR 0001''s stance that missing snapshots never break the build; snapshot-informed optimization stays a possible future amendment.SqlQueryAttributelives in the runtime package so user code never references the generator assembly.SqlBound.Generators(netstandard2.0, Roslyn 4.8) packs intoanalyzers/dotnet/cswith nolib/folder and no runtime dependency. Tests driveCSharpGeneratorDriverdirectly (theMicrosoft.CodeAnalysis.Testingverifiers lag on xunit.v3).ForAttributeWithMetadataNamewith a fully value-equatable model for incremental caching; usage errorsSQLB001–SQLB008(non-partial/non-static methods, missingDbConnection, unsupported return/row/parameter types, empty SQL, generics) plus analyzer release tracking.GetOrdinal, typed getters per row,DBNull→nullfor nullable members and a descriptive exception naming the column otherwise,@namescalar binding (coalescing toDBNull.Valueonly when the argument can be null), optionalDbTransaction,CancellationToken.Nonefallback, deterministic FNV-1a hint names. Canonical output locked byte-for-byte in a golden test; all variants proven to compile warning-free.decimal?path — while reading Dapper''s writes on a shared connection, extending the M3 coexistence proof to the static surface.M4 scope is one canonical shape (
Task<IReadOnlyList<T>>over positional records); the remaining shapes land in M5, AOT + benchmarks in M6.Test plan
dotnet format --verify-no-changes, zero-warning build, anddotnet packverified locally (package layout inspected: analyzer DLL only).🤖 Generated with Claude Code