Repository navigation
test(ddl): add cross-backend DDL contracts and narrow the materialized-view contract - #21
Merged
Merged
Conversation
…ize tz-aware datetimes - CastExpression is now an AST node: tests build it explicitly and assert the node wrapping (placeholder for prior _cast_types state). - ColumnDefinition/ColumnConstraint/TableConstraint/ForeignKeyConstraint now require the dialect as the first constructor argument. - Mock async/sync backends supply a SQLDialectBase-specced dialect so the strict dialect binding accepts them. - _normalize_for_compare converts tz-aware datetimes to UTC before stripping tzinfo, so the same instant read back in a different session time zone compares equal. - _placeholder_for/_column_definition support the DataType-family types. (cherry picked from commit 93d6e37)
Add test_bmp_multilingual_content_round_trip and test_bmp_json_field_round_trip (sync + async) to the logging test suite. These exercise the entire Basic Multilingual Plane (U+0000..U+FFFF) with no supplementary-plane characters. Every backend can run these tests. Backends whose charset cannot store supplementary characters (e.g. Oracle 18c AL32UTF8) should skip the full-Unicode tests in their own conftest.py and keep these BMP-only tests as their Unicode coverage. (cherry picked from commit ff625be)
New decorator for tests requiring ORDER BY inside CTE definitions. Used by test_cte_with_range_conditions (sync + async) which generate CTE SQL with ORDER BY — unsupported on SQL Server. (cherry picked from commit cf33153)
…name (cherry picked from commit d080b31)
Model base already defaults to "id"; fixtures should exercise the unspecified-PK default path. (cherry picked from commit 7f180c1)
(cherry picked from commit 09a1aed)
(cherry picked from commit a0b1fd5)
Cover declaration ordering, field metadata, table defaults, and sync/async parity for the shared DDLSource contract. (cherry picked from commit f2227e5)
The postgres materialized view defect (MRO order left the PG mixin dead) and ClickHouse's equivalent (probe returned True while inheriting the core formatter, producing SQL ClickHouse cannot execute) share one root cause: nothing constrained a capability probe to the formatter behind it. The core layer had no cross-backend MV contract at all, so each backend was on its own and a defect could only ever be found inside that one backend. Adds feature/basic/ddl/test_materialized_view_contract.py (expression/dialect layer, no live connection) in two layers: - Invariants (never skipped): declaring supports_materialized_view() implies owning the CREATE formatter; declaring supports_refresh_materialized_view() implies owning the refresh formatter; the generic expressions must render on any backend that claims support (catches formatters written against a private expression surface) - Capability-gated rendering: statement shape for CREATE/DROP/REFRESH, with unsupported backends skipped declaratively CREATE is constrained on its own because its syntax diverges on every backend (PG WITH (...)/TABLESPACE/WITH [NO] DATA, Oracle BUILD IMMEDIATE/REFRESH, Snowflake CLUSTER BY/COMMENT, ClickHouse ENGINE/TO/POPULATE/REFRESH). A backend that genuinely wants the generic form satisfies the rule by overriding the formatter and delegating to super(), turning a silent accident into an explicit statement. DROP is left unconstrained: DROP MATERIALIZED VIEW [IF EXISTS] [CASCADE] is broadly valid, so inheriting it is legitimate. Measured: postgres 120 passed; ClickHouse 3 failed before its fix (naming the missing override), Snowflake 3 failed before its fix (AttributeError on the generic expression). (cherry picked from commit 951172c)
…a version Produced 12 failures on Oracle: OracleMaterializedViewMixin gates its formatter on self.version (MVs need 9i, IF NOT EXISTS needs 23ai), while the ddl_dialect fixture hands over an unadapted dialect, so every rendering assertion hit DialectNotAdaptedException. That is correct Oracle behaviour, not a defect — version-gated DDL genuinely needs a resolved server version. The contract exists to catch lying probes, not to require every backend to render SQL without a connection. Grading: - MRO ownership invariants (need no environment, always run): probe True implies owning the CREATE / refresh formatter. This is the valuable one; it is what caught ClickHouse. - Rendering assertions: skip when the probe or formatter raises DialectNotAdaptedException (handled uniformly by the _probe / _render helpers) Verified on nine backends: postgres 96 passed, oracle 6 passed / 18 skipped (ownership invariants still run), snowflake 6 passed / 2 skipped; sqlite/mysql/mariadb/sqlserver/clickhouse/bigquery/firebird all skip on capability. (cherry picked from commit 971225c)
Landing Oracle's DBMS_MVIEW.REFRESH exposed the contract's second over-specification: it asserted the rendered REFRESH starts with "REFRESH MATERIALIZED VIEW", but Oracle renders BEGIN DBMS_MVIEW.REFRESH(...) END;. Same root cause as the earlier DROP case: asserting a concrete statement shape assumes every backend shares one syntax. What the contract must guarantee is that the request renders and refers to the target view; the statement shape is asserted per backend. Now: non-empty output plus a case-insensitive match on the view name (Oracle passes a string name to DBMS_MVIEW and it folds to upper case). Also fixes a self-contradictory assertion (upper-casing the SQL but comparing a lower-case needle), which the previous assertion had short-circuited before it could surface. Verified on ten backends: oracle 12 passed (the new refresh assertion actually executes), postgres 96 passed, the rest skip on capability. (cherry picked from commit 3e7d997)
Landing BigQuery exposed two more over-specifications: column aliases and DROP ... CASCADE. BigQuery's CREATE MATERIALIZED VIEW takes its column names from the query (no alias clause) and its DROP has no CASCADE. Asserting those while also asserting "it renders" again assumes one shared syntax. This is the third relaxation for the same reason (DROP ownership, REFRESH statement shape, aliases/CASCADE). The lesson is now written into the contract docstring as an explicit scope rule: the contract asserts only what every MV-capable database has; everything else (aliases, CASCADE, TABLESPACE, storage parameters, refresh scheduling) is a per-backend feature asserted in that backend's own suite. Verified on ten backends: bigquery 5 passed, postgres 84 passed, oracle 12 passed, snowflake 5 passed, the rest skip on capability. (cherry picked from commit 1de74c4)
…are not universal
|
This was referenced Sep 27, 2026
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
Brings the shared test suite in line with the derived-DDL work in
python-activerecord, and tightens the materialized-view contract so it only asserts
behaviour every MV backend can actually provide.
The MV contract previously assumed more than SQL guarantees. Three commits narrow it:
through a vendor-specific route are handled as such.
skipping or failing wholesale.
Alongside that, two new contract suites cover areas that had none:
DDLSourcecollection contract, with the retained-source cases pinned by testsThe expression tests are adapted to node-based casts, and timezone-aware datetimes are
normalised so round-trips compare equal. BMP-only Unicode round-trip tests are added, and
a
requires_cte_order_bydecorator is introduced with the matching protocol rename.Deprecated
features.pyand the unusedsqlite_features.pyare removed.Type of change
feat: A new featuredocs: Documentation only changes (if related to the new feature)test: Adding missing tests or correcting existing tests (for the new feature)chore: Changes to the build process or auxiliary tools and libraries (if related to the new feature)Breaking Change
No commit carries a
!marker, and nothing in the public helper surface used by backendrepositories changes shape. The impact is on what the contracts now require of a backend.
Impact on End-Users:
None. This package is a shared test suite, not a runtime dependency.
Impact on Backend Developers (Custom Implementations):
The materialized-view contract is stricter in one direction and looser in another. A backend
that advertised
supports_materialized_view()on the strength of the old, broader contractmay now fail cases it previously skipped. The grading change also alters when a case is
skipped rather than failed, so a backend may see a different pass/skip distribution without
any code change of its own.
Backend repositories should re-run their suites against this testsuite before their own
PRs are merged, since several are adapting to the same core change.
Related Repositories/Packages
This is the test-suite half of the derived-DDL work whose core half is
rhosocial/python-activerecord#121, already merged into
release/v1.0.0.dev30.The nine backend repositories consume this package and must be merged after it, each
repointing its workflow's install source away from the now-merged
feature/activerecord-derived-ddlbranch:bde82d7cc825f654ffce028351e76e6b15f7e23db0d1dd46f9307aef41bb30bTesting
Not applicable, and deliberately not claimed. This package has no test suite of its
own — it is the shared suite that other projects install into their own environments —
and it has no CI pipeline. Its
.github/workflows/contains onlychangelog-check.ymland
publish.yml, so there is no test job to run or to wait on.The change is exercised indirectly: the nine backend repositories run it as part of their
own suites, and all nine were green against the matching core branch at the tips listed
above.
Checklist
changelog.d/(to be added after this PR isopened, named by the actual PR number)
Additional Notes
chore: remove dead code - deprecated features.py and unused sqlite_features.pyis the onlycommit here that is a pure deletion with no replacement. It is included because it is the
same dead-code sweep that core PR #121 carried, and splitting it out would have left the two
repositories inconsistent.