Repository navigation
feat(sqlserver): consume clause-parameter pairs and declare measured probes - #7
Merged
vistart merged 10 commits intoOct 8, 2026
Merged
Conversation
…names them Core's expression and dialect layers were reworked: a relation is split into the object it names and the source that reads it, the namespace shape became a protocol the dialect states once, and to_sql() now inspects what answers a format lookup. Schema objects are rendered as objects rather than as parsed names, and the object kind is checked where the name is spelled. Thirty-two dispatch-failure pins move from AttributeError to UnsupportedFeatureError. Their shared fragment moved with them, and the reason matters: a dialect that knows a statement and refuses it also raises UnsupportedFeatureError in the same message shape, and only the dispatch's suggestion offers a mixin. With the type no longer separating the two causes, the fragment does. Four shadowed formatters did not move. CreateFulltextIndexExpression, DropFunctionExpression, CreateFunctionExpression and DropTriggerExpression still raise exactly what they raised before, because the dispatch reaches them the same way: the cause is a signature colliding with core's, not a missing formatter, and the pins still record it. Of 303 distinct format_* names, none resolves to a Protocol and no non-protocol class aliases a protocol stub. assert_sql_roundtrip_classified consulted its pinned tables after catching UnsupportedFeatureError, so the retyped entries were unreachable on the classification path; the tables come first now. Four XML classes were pinned as unconstructible. Each wants a Sequence of its own item type, and testsuite's constructor read that alias' name as the Sequence catalogue object rather than as a list. They build now, and refuse at dispatch naming the formatter this dialect does not compose.
… has SQL Server 2012 introduced SEQUENCE, but SQLServerSequenceMixin declared only supports_create_sequence and supports_drop_sequence and left supports_sequence() and every option probe to core's defaults, which are False. Once core's 9dc18ab gated the three sequence DDL formatters on those probes, the declaration and the renderer disagreed: the dialect answered "no sequence object" and "no option" for capabilities the engine has spelled since 2012. Add supports_sequence(), supports_alter_sequence(), supports_sequence_if_not_exists, supports_sequence_if_exists, the four new option probes (start, increment, minvalue, maxvalue) and the remaining ones (cycle, cache, order, owned_by), each version-gated to what the server accepts. Verified against the CREATE/ALTER/DROP SEQUENCE (Transact-SQL) reference and against live 2019, 2022 and 2025 containers: START WITH, INCREMENT BY, MINVALUE, MAXVALUE, CYCLE/NO CYCLE and CACHE are accepted; IF NOT EXISTS, ORDER/NOORDER and OWNED BY are syntax errors; DROP SEQUENCE IF EXISTS is a 2016 (13.x) addition. The local format_create_sequence_statement and format_drop_sequence_statement are deleted: SQL Server's spelling is exactly what core's SequenceMixin emits once the probes answer, so one renderer serves every dialect and the probes are the only place that decides. The local create silently dropped an ORDER request, and the local drop gated IF EXISTS on 2012 rather than 2016, so a 2012-2014 server would have received a clause it rejects. The local format_alter_sequence_statement is kept because SQL Server's ALTER grammar genuinely differs: it restarts with RESTART WITH and rejects START WITH (error 11710, confirmed live), and has no ORDER or OWNED BY. It now consults the probes and raises for the words the engine cannot spell instead of dropping them. format_next_value_for now takes the concrete SQLServerNextValueForExpression and reads its sequence_name field, replacing the Any-typed argument, getattr guess and str() fallback that could not name what it accepted. The protocol declaration moves with it; the single call site passes the expression itself.
SQL Server's CREATE SEQUENCE accepts START WITH, but ALTER SEQUENCE does not: it changes the start point with RESTART WITH, and the engine rejects START WITH there outright with argument error 11710. The probe supports_sequence_start describes the CREATE clause, so it cannot stand in for the ALTER one. Declare supports_alter_sequence_start on SQLServerSequenceMixin and answer it False, then gate the start field of format_alter_sequence_statement on that probe instead of refusing the clause as a bare statement-grammar fact. The refusal is unchanged in effect, but it now names the option and fails closed from a declared capability rather than an inline raise. A probe answering True by default would let a dialect emit START WITH on ALTER SEQUENCE and hand the server SQL it rejects, so the explicit False is what keeps the two clauses honest. Live check against SQL Server 2025: ALTER SEQUENCE ... START WITH 5 is rejected with 11710, while RESTART WITH 5 and INCREMENT BY 5 are accepted.
…ctually has The dialect's identity spelling is IDENTITY(seed, increment): it overrides the standard GENERATED ... AS IDENTITY formatter, and SQL Server 2019 / 2022 / 2025 all refuse the standard grammar (measured). Declare the probes core now consults, and gate the formatter on them: - supports_identity_column() = True -- IDENTITY(seed, increment) is this dialect's identity expression, measured to execute; - supports_identity_generation_always() = False -- the identity property has no generation mode. ALWAYS was previously rendered byte-identical to BY DEFAULT ( IDENTITY(1, 1)); it is now refused by name; - supports_identity_start() / _increment() = True -- the property's seed and increment, both measured to take effect; - supports_identity_minvalue() / _maxvalue() / _cycle() = False -- every candidate spelling was measured refused on all three servers; - supports_auto_increment_column() = False -- IDENTITY(seed, inc) is not AUTO_INCREMENT. The formatter refuses a requested option by name rather than dropping it. Execution confirmation covers the rendered DDL on all three servers, with sentinels that must come back REJECTED.
…dropping them The dialect overrides format_identity_clause to render its own IDENTITY(seed, increment) property. That override gated minvalue / maxvalue / cycle and then jumped straight to seed / increment -- so a request for order or cache, the two fields core added this round, rendered the bare property ( IDENTITY(1, 1)) with the request silently dropped, while the same method refused cycle. Two behaviours in one method. Add the two gates. supports_identity_order() and supports_identity_cache() answer False -- measured on 2019 / 2022 / 2025: ORDER, NO ORDER, CACHE <n> and NO CACHE are syntax errors in the property -- so the refusal is by name, IDENTITY ORDER / IDENTITY CACHE, never a downgrade. Add a general walk test that reads every option field from IdentityClause.__init__ itself and asserts the formatter renders each one or refuses it by name, never renders it byte-identical to the bare clause. Withdrawing a gate fails the walk with "the option was silently dropped", and a self-test proves the walk rejects a formatter that drops options, so the next field core adds cannot reopen this hole. Execution confirmation runs the refusal verdict on all three servers, and the raw candidate spellings join the measured-rejection list -- ORDER, NO ORDER, CACHE <n> and NO CACHE are refused by 2019 / 2022 / 2025, with the existing sentinels still proving the classifier can see a rejection.
…WNED BY format_alter_sequence_statement refused ORDER and OWNED BY with a hard-coded raise, while every neighbouring option in the same method consulted its own probe. supports_sequence_order() and supports_sequence_owned_by() answer False for SQL Server, so the output was right by accident: flipping either probe in a subclass changed nothing, because the formatter never asked. Gate both options the way the neighbours are gated -- consult the probe, refuse by name when it answers False, render the clause when it accepts. The output is byte-identical: both probes still answer False, so a request for either is still refused. Snapshots across 2012/2016/2022 and live 2019/2022/2025 before and after differ by zero bytes; all live verdicts and sentinels met on the three servers. Add test_sequence_probes_sqlserver.py. Its test_every_sequence_option_probe_is_load_bearing walks every sequence option probe on CREATE/ALTER/DROP, flips each one in a subclass, and asserts the behaviour flips with it; it was red on ALTER order, NO ORDER and owned_by before the gates. A self-test feeds the walk the pre-fix hard-coded shape and asserts it reports a decorative probe, and a completeness check refuses to let a declared probe fall out of the walk.
… dialect gaps Every two-spelling clause node now carries one parameter per spelling; "unspecified" is neither set and setting both raises ValueError at construction. The SQL Server formatters that read the changed fields consume the pairs, and the overrides that replace core's gated formatters carry the gates for the pairs their nodes grew. Pairs consumed, per formatter: - ALTER SEQUENCE override: cycle/no_cycle and cache/no_cache are spellable (measured accepted on 2019/2022/2025); order/no_order is refused by name because the engine has no ORDER option. - Identity formatter: cycle/no_cycle, cache/no_cache, order/no_order have no spelling in IDENTITY(seed, increment); each explicit spelling is refused by name, and an unset pair renders the bare property. - Set operation: all_ is rendered; distinct is refused by name (T-SQL has no DISTINCT keyword in set operations; measured refused). - TRUNCATE / DROP VIEW / DROP SCHEMA overrides: cascade/restrict and restart_identity/continue_identity are refused by name instead of silently dropped. - Constraint formatters (table, column, table-TYPE): deferrable/not_deferrable, initially_deferred/initially_immediate, enforced/not_enforced are gated on the existing probes and refused by name. - Transaction formatters: deferrable/not_deferrable refused by name (BEGIN TRANSACTION DEFERRABLE parses as a transaction *name*, measured). - The table-TYPE formatter's is-not-None field scan, which the bool defaults turned into an always-refuse, is replaced with per-spelling consumption. Dialect-specific gaps: - SQLServerAlterColumn: Optional[bool] not_null becomes not_null/nullable (NOT NULL / NULL); both set raises ValueError. Measured: unspecified ALTER COLUMN c INT is accepted and leaves the column NULLable; both explicit spellings are accepted and take effect. - SQLServerColumnstoreIndexExpression: Optional[bool] clustered becomes clustered/nonclustered (CLUSTERED / NONCLUSTERED); both set raises ValueError. Measured: the bare keyword requires a key list and the server records that form as NONCLUSTERED, so validate() now allows columns when neither keyword is set; the clustered form still forbids them. Probes: none of the new core probes (supports_schema_restrict, supports_restrict_view, supports_drop_function_restrict, supports_materialized_view_restrict, supports_truncate_restrict) is supported by SQL Server; every candidate spelling was measured refused on 2019/2022/2025 and the probes stay False. Behaviour change (intended): an explicit spelling whose probe is False now raises instead of being dropped; an unset pair renders nothing where the old encoding spelled a negative form. Guard: test_clause_pair_guard_sqlserver.py asserts the four states of every pair the dialect consumes are pairwise distinguishable (neither rendered / A rendered or refused by name / B rendered or refused by name / both refused). It was run red against the unmodified tree first (46 failing test ids) and is green after the change (86 passed); two mutations (a disabled no_cache branch, a disabled nullable branch) were each caught by the expected guard test. Tests: the pointed files (roundtrip matrix, sequence/DDL, identity, protocol conformance, sequence-probe guard, plus the formatter-touching files) pass: 1348 passed, 9 skipped; the one async test errors locally because aioodbc is not installed in the local venv (CI installs it).
…pair
Core at c4d6adc now consults supports_materialized_cte (the CTE
MATERIALIZED hint), supports_truncate (TRUNCATE itself) and the new
supports_with_data_clause (WITH [NO] DATA on CTAS / materialized view
create / refresh), and added supports_transaction_wait for the
wait/no_wait pair on the two transaction expressions. A dialect that
leaves these probes undeclared inherits a core default; this declares
each one in this backend with its measured answer.
Measured (SQL Server 2019 / 2022 / 2025, ports 11433/11434/11435,
{SQL Server} driver; raw output in the round's plan directory):
- supports_truncate -> True: TRUNCATE TABLE accepted and empties the
table; CASCADE rejected.
- supports_with_data_clause -> False: SELECT ... INTO accepted, but
WITH DATA / WITH NO DATA are syntax errors near 'with'; CREATE TABLE
... AS is not SQL Server syntax either.
- supports_materialized_cte -> False: plain CTE accepted; AS MATERIALIZED
and AS NOT MATERIALIZED are syntax errors.
- supports_transaction_wait -> False: BEGIN TRANSACTION WAIT parses the
word as the transaction name (sys.dm_tran_active_transactions.name
reads 'WAIT'); NO WAIT, SET TRANSACTION WAIT and SET TRANSACTION
NO WAIT are syntax errors.
Formatter changes so each declaration is load-bearing:
- format_truncate_statement consults supports_truncate() and refuses by
name; the stock output is unchanged (the probe is True).
- format_begin_transaction / format_set_transaction consume the
wait/no_wait pair through _transaction_wait_clause: they refuse by name
when the probe declines (the previous shape silently dropped the pair,
e.g. no_wait rendered as a bare BEGIN TRANSACTION), and render the
spelling when a subclass's probe accepts it.
Behaviour change (intended): an explicitly requested wait/no_wait on
SQL Server now raises instead of being dropped; every other captured
state renders byte-identically (before/after diff in the plan directory).
Guard: test_master_probes_guard_sqlserver.py walks all four probes for
load-bearing and declaration completeness, checks the four states of the
wait pair, and is able to fail (two dropping-formatter sentinels). It
was run red against the unmodified tree first (20 failing ids) and is
green after the change (41 passed); two mutations (truncate gate
removed, wait pair dropped) were each caught by the expected guard
tests.
The schema-object refactor these branches depend on landed on the core's release/v1.0.0.dev30 line as PR #125, so the workflow no longer needs to install the core from the feature branch. CORE_PKG now points at the release branch; the testsuite dependency stays on its own feature branch, which is not merged yet.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## release/v1.0.0.dev3 #7 +/- ##
=======================================================
+ Coverage 73.36% 73.97% +0.61%
=======================================================
Files 132 135 +3
Lines 7313 7605 +292
Branches 1171 1261 +90
=======================================================
+ Hits 5365 5626 +261
Misses 1540 1540
- Partials 408 439 +31
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
7 of 22 tasks
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.




Description
This branch moves the SQL Server backend onto the expression/dialect separation that landed in core
(
rhosocial/python-activerecord#125, merged as5895bd3onrelease/v1.0.0.dev30): an expressioncollects parameters; the dialect decides spelling and support. It is the SQL Server repository of
the coordinated eleven-repository
feature/expression-schema-object-refactorround.Catalogue objects. Schema objects travel as objects, not parsed names (
e6d44bd):src/rhosocial/activerecord/backend/impl/sqlserver/expression/objects.pydefinesSQLServerTable(the#temporary prefix is a field settled at construction),PartitionFunctionandPartitionScheme(each declaring aformat_partition_*_objectrenderer).src/rhosocial/activerecord/backend/impl/sqlserver/mixins/namespace.pyimplements core'sNamespaceSupportfor SQL Server: a catalog above the schema, both levels rendered, the128-character identifier cap at every level, and the documented bare-name
format_object_id_nameexception for
OBJECT_ID(which does not strip brackets).NamespaceSupport, one*ObjectSupportpercatalogue kind, and
Create*Support/Drop*Support/FulltextIndexSupport— and.claude/backend_development.mddocuments the mapping.Clause pairs. Every two-spelling clause this dialect renders now carries one parameter per
spelling; "unspecified" is neither set, and setting both raises
ValueErrorat construction. Theformatters consume each pair and refuse an explicitly requested spelling by name instead of
silently dropping it (
802bed0,16af634):format_alter_sequence_statement(dialect.py):cycle/no_cycle,cache/no_cache(measuredaccepted on 2019/2022/2025);
order/no_orderrefused by name (no ORDER option).format_identity_clause(mixins/identity_column.py):cycle/no_cycle,cache/no_cache,order/no_orderrefused by name —IDENTITY(seed, increment)has no spelling for them; anunset pair renders the bare property.
format_set_operation_expression(mixins/set_operation.py):all_rendered;distinctrefusedby name (T-SQL has no DISTINCT keyword in set operations; measured refused).
format_truncate_statement(dialect.py):cascade/restrictandrestart_identity/continue_identityrefused by name.format_drop_view_statement(mixins/ddl_view.py) andformat_drop_schema_statement(mixins/schema.py):
cascade/restrictrefused by name.format_table_constraint,format_column_definitionand_format_table_type_constraint(mixins/ddl_type.py):
deferrable/not_deferrable,initially_deferred/initially_immediate,enforced/not_enforcedgated on the existing probes and refused by name.format_begin_transaction/format_set_transaction(mixins/transaction.py):deferrable/not_deferrableandwait/no_waitrefused by name.Two dialect-specific gaps closed (both were shape-C tri-states — one nullable bool meaning two
spellings):
SQLServerAlterColumn.not_null: Optional[bool]becomesnot_null/nullable(NOT NULL/NULL); both set raisesValueError. Measured: unspecifiedALTER COLUMN c INTis accepted andleaves the column nullable; both explicit spellings are accepted and take effect.
SQLServerColumnstoreIndexExpression.clustered: Optional[bool]becomesclustered/nonclustered;both set raises
ValueError. Measured: the bare keyword requires a key list and the server recordsthat form as NONCLUSTERED, so
validate()allows columns when no keyword is set; the clusteredform still forbids them.
Probes declared and corrected, each with a measured answer:
4469d80,7602a38):supports_sequence,supports_alter_sequence, the optionprobes (start/increment/minvalue/maxvalue/cycle/cache, 2012+) and the negatives (
if_not_exists,order,owned_by) are now stated instead of inheriting core defaults;supports_sequence_if_existsis 2016+.supports_alter_sequence_startanswersFalse: ALTERSEQUENCE rejects
START WITHwith error 11710 (RESTART WITH is the spelling). The localCREATE/DROP renderers were deleted — core's exact spelling once the probes answer — and the local
DROP had gated
IF EXISTSon 2012 rather than 2016.format_alter_sequence_statementstays(genuinely different grammar) and now consults its probes (
8feb28a).9cd15d0,c16c733):supports_identity_columntrue,generation_alwaysfalse(ALWAYS is refused, never downgraded), start/increment true, minvalue/maxvalue/cycle/order/cache
false,
supports_auto_increment_columnfalse.16af634):supports_truncatetrue,supports_with_data_clausefalse,supports_materialized_ctefalse,supports_transaction_waitfalse.BEGIN TRANSACTION WAITtrap: the server acceptsBEGIN TRANSACTION WAIT— readingWAITas the transaction name (
sys.dm_tran_active_transactions.namereturnsWAIT) — so a naiveprobe would report support;
NO WAIT,SET TRANSACTION WAITandSET TRANSACTION NO WAITaresyntax errors. The probe answers False and both transaction formatters refuse a requested spelling
by name.
supports_schema_restrict,supports_restrict_view,supports_drop_function_restrict,supports_materialized_view_restrict,supports_truncate_restrict): every candidate spelling was measured refused on 2019/2022/2025;the SQL Server overrides for DROP VIEW, DROP SCHEMA and TRUNCATE refuse an explicit spelling by
name rather than silently dropping it.
Intended behaviour change: an explicit spelling whose probe is False now raises
UnsupportedFeatureErrorinstead of rendering SQL that omitted the request; an unset pair rendersnothing where the old encoding spelled a negative form.
Dependencies. Core is already merged (PR #125 →
5895bd3), and2bf5a42repoints the workflowfrom the core feature branch to
release/v1.0.0.dev30; this PR does not wait on any unmerged corebranch and can be merged on its own. The testsuite install remains on its own
feature/expression-schema-object-refactorbranch as part of the same round.Adds # (no tracking issue; core's half of the round is rhosocial/python-activerecord#125)
Type of change
feat: A new featuredocs: Documentation only changes (if related to the new feature)perf: A code change that improves performance (if part of the feature)test: Adding missing tests or correcting existing tests (for the new feature)chore: Changes to the build process or auxiliary tools and libraries suchs as documentation generation (if related to the new feature)ci: Changes to our CI configuration files and scripts (if related to the new feature)build: Changes that affect the build system or external dependencies (if related to the new feature)revert: Reverts a previous commitBreaking Change
(If Yes, please describe the impact and migration path below):
Impact on End-Users:
Typical model/field/CRUD usage is unchanged — the round's before/after captures show every
unspecified state rendering byte-identically. Three narrow, end-user-visible breaks apply to code
that builds the SQL Server-specific expressions directly:
SQLServerAlterColumn(..., not_null=False)used to spellNULL; it now means "unspecified".Migration: use
nullable=True(thenot_null=Trueform is unchanged).SQLServerColumnstoreIndexExpression(..., clustered=False)used to spellNONCLUSTERED; it nowmeans "no keyword". Migration: use
nonclustered=True.TRUNCATE ... CASCADE, identityORDER/CACHE, transactionWAIT,UNION DISTINCT, constraintENFORCED) now raisesUnsupportedFeatureErrorinstead of rendering SQL that silently omitted the request. Callersshould remove the option, or switch to the spelling the dialect documents.
Both classes are exported from
rhosocial.activerecord.backend.impl.sqlserver.expression(see its__init__.py).Impact on Backend Developers (Custom Implementations):
This backend follows core's protocol package split: the dialect is a
NamespaceSupportand one*ObjectSupportper catalogue kind; DDL protocols are one per statement(
CreateSchemaSupport/DropSchemaSupport,CreateIndexSupport/DropIndexSupport/FulltextIndexSupport,CreateTypeSupport/DropTypeSupport, ...); schema objects render throughper-kind
format_*_objectmethods. Its own protocol declarations follow:SQLServerTableSupportderives from
TableObjectSupport,SQLServerUserDefinedTypeSupportfromCreateTypeSupport/DropTypeSupport, andSQLServerSequenceSupport.format_next_value_fornamesSQLServerNextValueForExpression(the oldAnyargument with thegetattr/str()fallback isgone). Custom subclasses that referenced the previous
SchemaSupport/IndexSupport/TableSupport/UserDefinedTypeSupportnames, or relied on formatters silently dropping clause options, must beupdated to the new names and to the pair-consuming contracts.
Related Repositories/Packages
5895bd3onrelease/v1.0.0.dev30.Required dependency; CI installs core from that line as of
2bf5a42.feature/expression-schema-object-refactorbranch in CI (same round; not yet on a release line).feature/expression-schema-object-refactorround(eleven repositories total), all consuming the same core separation.
Testing
Test Plan:
New guard files (run red first, green after):
tests/rhosocial/activerecord_sqlserver_test/feature/backend/test_clause_pair_guard_sqlserver.py— asserts the four states of every clause pair the dialect consumes are pairwise distinguishable.
Red against the unmodified tree: 46 failed / 40 passed; after the change: 86 passed. Two mutations
(a disabled
no_cachebranch, a disablednullablebranch) were each caught (1 failed / 85passed).
tests/rhosocial/activerecord_sqlserver_test/feature/backend/test_master_probes_guard_sqlserver.py— declaration completeness and a load-bearing walk for the four master probes, plus the four
wait/no_waitstates and dropping-formatter sentinels. Red: 20 failed / 21 passed; green:41 passed. Mutations (truncate gate removed; wait pair dropped) were each caught (2 failed / 1
passed; 5 failed / 3 passed).
tests/rhosocial/activerecord_sqlserver_test/feature/backend/test_sequence_probes_sqlserver.py— walks every sequence option probe on CREATE/ALTER/DROP, flips each one in a subclass and asserts
the behaviour flips with it; a self-test feeds the walk the pre-fix hard-coded shape and reports a
decorative probe. It was red on ALTER
order,NO ORDERandowned_bybefore the gates.New / extended suites:
tests/rhosocial/activerecord_sqlserver_test/feature/backend/test_identity_clause_sqlserver.pyand
.../test_identity_execution_confirmation.py— rendering plus live execution verdicts, withsentinels that must come back REJECTED.
.../test_expression_roundtrip_all.py(+1238 lines),.../test_dialect_comprehensive.py,.../test_dialect_version_branches.py,.../test_sqlserver_protocol_conformance.py,.../test_schema_support.py,.../test_sqlserver_type_ddl.py— updated for catalogue objects,the renamed/split protocols, pair consumption and the probe answers.
Local pointed runs: the full pointed run recorded with the pair work (
802bed0) reports1348 passed, 9 skipped; the pointed re-run after the master-probe work (guard files plus the
roundtrip / dialect / protocol suites, 8 files) reports 1147 passed, 8 skipped. One async test
errors locally because
aioodbcis not installed in the local venv (CI installs it).CI:
.github/workflows/test.ymlruns nine jobs — Python 3.9/3.10/3.11/3.12/3.13/3.13t/3.14/3.14tagainst SQL Server 2017, 2019, 2022 and 2025 containers. The workflow-dispatch run on this branch at
16af634is green across all nine jobs; the final commit2bf5a42only repoints the core installand has no fresh run recorded yet.
Live measurement: every probe answer and both dialect gaps were measured on SQL Server
2019/2022/2025 containers; the raw logs are kept in the round's (git-ignored) plan directory.
Checklist
.claude/code_style.md.README.md, etc.)..claude/version_control.md(changelog.d/<issue_number>.<type>.md).Additional Notes
emits
UserWarning: SQLServerTable.get_params(): cannot find attribute '_slots' or 'slots' for parameter 'slots'while round-tripping the newSQLServerTable. It is a helper introspectionlimitation, not a test failure; it is why the "no new warnings" box is unticked.
changelog.d/<issue>.<type>.mdfile, sothat checkbox is unticked.
red/green runs and the mutation results live in the round's plan directory (git-ignored local
evidence).
git log --format='%h %s' origin/release/v1.0.0.dev3..HEAD; the threeguard files above; and
mixins/transaction.py::supports_transaction_waitfor theWAIT-as-a-transaction-name trap.