Repository navigation
feat(postgres): adopt the expression/dialect split and declare the probes core now gates on - #61
Merged
vistart merged 8 commits intoOct 8, 2026
Conversation
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 instead of calling whatever is callable. Qualified names are carried here as schema objects rather than parsed strings, which is what made the object and the statement that acts on it able to disagree about what "the same table" meant. Six types this backend does not have were being exported. COMMENT ON now renders from the object it annotates. The round-trip matrix this branch added was reporting nothing, which the refactor made visible rather than causing. Three classes it pinned as raising AttributeError name a format_method whose formatter PostgreSQL does not have, and core now reports a missing formatter as UnsupportedFeatureError -- the spelling every other capability probe in the tree already used. The pins move with it, and each now names the formatter it is missing so the entry says which gap it is. Walking PostgresDialect.__mro__ to resolve every format_* name to the class that defines it shows none of the 411 resolving to a Protocol: the stub format_* a protocol declares in the MRO are each overridden by a real mixin ahead of it. That includes CreateRoutineSupport and DropRoutineSupport, whose presence in the base list used to answer a dispatch with a protocol body returning None; the core CreateFunctionExpression and DropFunctionExpression now render.
Core commit 9dc18ab made all three sequence DDL formatters consult supports_sequence() first and unconditionally, and added four option probes (supports_sequence_start, _increment, _minvalue, _maxvalue). PostgreSQL's sequence mixin never defined supports_sequence -- core had never asked for it -- so the master switch fell back to core's default False and CREATE SEQUENCE, DROP SEQUENCE and ALTER SEQUENCE all raised UnsupportedFeatureError. A full-support dialect had stopped rendering the statement it supports. The mixin now answers every probe those formatters consult. supports_sequence, supports_create_sequence, supports_drop_sequence, supports_alter_sequence, supports_sequence_if_not_exists, supports_sequence_if_exists, and the option probes start, increment, minvalue, maxvalue, cycle, cache and owned_by all return True. supports_sequence_order returns False: the PostgreSQL CREATE SEQUENCE synopsis has no ORDER / NO ORDER clause -- that spelling is Oracle and SQL Server -- and the live server rejects it with a syntax error, so the formatter must refuse it rather than emit a clause the engine cannot parse. PostgresSequenceSupport restates the same switches, so the protocol and the mixin agree method for method. It derives from core's CreateSequenceSupport, DropSequenceSupport and AlterSequenceSupport, which is how the non-overlap guard in the protocol conformance test recognises the restatement as the deliberate overlap it is. The dialect's three redundant direct protocol bases are dropped for the same reason: a base may not precede the subclass that restates it. The three DDL formatters remain core's; this backend only answers the capability questions they ask.
Core's format_alter_sequence_statement gated AlterSequenceExpression.start on supports_sequence_start(), the probe for the CREATE SEQUENCE ... START WITH clause. Those are two different clauses, and True for one does not imply True for the other. PostgreSQL accepts START on ALTER -- it records the start value that future RESTART calls use, while RESTART WITH sets the current value -- but Oracle, SQL Server, Firebird and Snowflake all reject it. Reusing the CREATE probe for the ALTER formatter would therefore let those four dialects emit SQL their server rejects. Add supports_alter_sequence_start() to PostgresSequenceMixin, answering True because the live server accepts the clause, and declare it on PostgresSequenceSupport so the protocol keeps mirroring the mixin. The default for the new probe is False, which is the safe side: a dialect that accepts the clause must say so explicitly, as this one does.
…them Core made the identity and auto-increment nodes fail-closed: IdentityClause consults supports_identity_column() plus one probe per option, and AutoIncrementClause consults supports_auto_increment_column(). PostgreSQL declared none of the new probes -- the dialect did not mix in the identity mixin at all -- so core's formatters refused every identity clause. Declare the measured boundary: supports_identity_column() is True from PostgreSQL 10 on, because PostgreSQL 9 rejects the clause with `syntax error at or near "GENERATED"` (measured) while 10 through 19 all accept it. The six option probes -- generation ALWAYS, start, increment, minvalue, maxvalue, cycle -- answer True: they are part of the same synopsis. supports_auto_increment_column() answers False: PostgreSQL has no AUTO_INCREMENT spelling, and the probe is declared explicitly rather than inherited from the fail-closed default. The legacy is_auto_increment constraint now routes through the gated identity formatter, so PostgreSQL 9 refuses an identity request instead of rendering a GENERATED clause its server rejects. There is no fallback to SERIAL: the SERIAL type family stays available for users who ask for it explicitly, and it keeps executing on every version. PostgresIdentitySupport and PostgresAutoIncrementSupport mirror the two new mixins; the protocol conformance test pairs them with the existing forward/reverse coverage. The new execution-confirmation suite renders and executes each clause against a live server: accepted where the probe says True, NOT_RENDERED on PostgreSQL 9 where it says False, with a rejected sentinel and an accepted control before each verdict. The PG9 identity example now uses the supported IdentityAttribute path instead of a keyword ColumnDefinition never accepted.
…ect gaps Core split every two-spelling clause into one parameter per spelling (438b7aa): unspecified means neither parameter, both means ValueError. The PostgreSQL formatters still read the pre-split fields, so this follows. Formatters consuming a pair: - format_truncate_statement: restart_identity|continue_identity (CONTINUE IDENTITY was silently dropped) and cascade|restrict (RESTRICT was silently dropped). - format_create_materialized_view_statement and format_refresh_materialized_view_pg_statement: with_data|no_data; unspecified renders no tail (the old else-branch spelled WITH NO DATA). - format_drop_materialized_view_statement: cascade|restrict (RESTRICT was silently dropped). - format_begin_transaction / format_set_transaction: deferrable|not_deferrable; [NOT] DEFERRABLE renders whenever requested, not only alongside SERIALIZABLE, and unspecified no longer spells NOT DEFERRABLE. - _format_exclude_constraint: deferrable|not_deferrable and initially_deferred|initially_immediate; INITIALLY ... renders as the independent constraint attribute it is. - format_copy_to_statement: force_array|force_array_false. - format_table_constraint: the enforced gate no longer reads a tri-state, so table constraints render again instead of refusing every state. Dialect-specific nodes split to the pair shape: - PostgresRefreshMaterializedViewExpression: with_data|no_data. - PostgresCopyToExpression: force_array|force_array_false. - PostgresCreateMaterializedViewExpression: with_data|no_data (its constructor dropped no_data and defaulted to WITH DATA). - PostgresDropMaterializedViewExpression: restrict pass-through. - PostgresExcludeConstraint: deferrable|not_deferrable and initially_deferred|initially_immediate. The audit registry did not carry this node; it was found by the sweep and measured before/after. New probes declared True (measured against live PostgreSQL 16 and 19): supports_schema_restrict, supports_restrict_view, supports_drop_function_restrict, supports_materialized_view_restrict, supports_truncate_restrict, and supports_identity_cache. The CACHE option's negative spelling, NO CACHE, has no PostgreSQL form (syntax error, measured), so no_cache is refused by name for sequences and identity while cache=n renders; ORDER/NO ORDER stay refused. The transaction manager now drives the split setters, so set_deferrable(False) renders NOT DEFERRABLE instead of raising. Behaviour changes (intended, per the round): - CREATE MATERIALIZED VIEW default no longer emits WITH NO DATA. - SET TRANSACTION with nothing requested no longer emits NOT DEFERRABLE. - BEGIN ... DEFERRABLE renders for any isolation level. - RESTRICT and CONTINUE IDENTITY become reachable where they were dropped. Guard: test_clause_pair_guard.py asserts the four states of every pair the dialect consumes are pairwise distinguishable; run red against the unmodified tree first (24 failing test ids), green after. Two mutations (one removed exclusion check, one reverted restrict branch) were each caught by the expected guard test.
Core's c4d6adc gave call sites to three probes that had been decorative: format_cte_expression consults supports_materialized_cte(), format_truncate_statement consults supports_truncate(), and the CTAS / CREATE MATERIALIZED VIEW / REFRESH MATERIALIZED VIEW renderers consult the new supports_with_data_clause() before spelling WITH [NO] DATA. It also added supports_transaction_wait() for the wait / no_wait pair. A dialect that leaves any of them to a core default refuses statements it used to render: this package answered the with-data probe False through core's default, and its transaction formatters dropped the wait pair entirely. Declare each from measurement or the documented grammar: - supports_materialized_cte() keeps its >= 12 gate (already declared here). Live measurement: 9.6, 10 and 11 reject AS MATERIALIZED / AS NOT MATERIALIZED with a syntax error; 12 through 19beta4 accept both. - supports_truncate() answers True explicitly. TRUNCATE is in the synopsis of every supported version and the live matrix accepts it everywhere. - supports_with_data_clause() answers True, version-independent. The clause is in all three synopses -- the CTAS form long predates materialized views, and CREATE / REFRESH MATERIALIZED VIEW ... [ WITH [ NO ] DATA ] is documented from 9.3 (the version that introduced materialized views) -- and the live matrix accepts every form. The same probe covers the REFRESH form; it needs no probe of its own. - supports_transaction_wait() answers False. BEGIN NO WAIT and SET TRANSACTION NO WAIT are syntax errors on every measured server (9.6 through 19beta4); PostgreSQL has no lock-wait clause. The PostgreSQL overrides now consume the probes the core formatters consume: format_create_materialized_view_statement and format_refresh_materialized_view_pg_statement refuse WITH [NO] DATA by name when the clause probe declines (generic REFRESH and CTAS delegate to core's gate), format_truncate_statement consults supports_truncate(), and format_begin_transaction / format_set_transaction refuse wait / no_wait by name instead of silently dropping the pair. PostgresTruncateSupport, PostgresMaterializedViewSupport and PostgresTransactionSupport restate the declarations so the protocol and the mixin stay mirrored; the conformance forward / reverse coverage tests enforce the mirror. Behaviour changes (intended, per the round): - CTAS and generic REFRESH MATERIALIZED VIEW with WITH DATA / WITH NO DATA render instead of being refused by the fail-closed default. - BEGIN / SET TRANSACTION wait=True / no_wait=True raise UnsupportedFeatureError naming the option instead of rendering a statement that dropped it. Guard: test_clause_pair_guard.py adds the wait pair and a declaration / gate class; run red against the unmodified tree first (13 failing ids of 155), green after. test_master_probe_execution.py renders each clause through the live backend and asserts acceptance against the probe, with a sentinel and a control before every verdict; green on the 11-scenario matrix (9.6 through 19beta4).
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.dev17 #61 +/- ##
========================================================
+ Coverage 75.06% 75.42% +0.35%
========================================================
Files 338 346 +8
Lines 15807 16111 +304
Branches 2680 2741 +61
========================================================
+ Hits 11866 12151 +285
+ Misses 2986 2969 -17
- Partials 955 991 +36
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 is the PostgreSQL backend's part of the eleven-repository coordinated change that follows
core PR rhosocial/python-activerecord#125 ("separate what an expression collects from what a
dialect decides"), which landed on the core
release/v1.0.0.dev30line. Every repository in theseries carries a branch named
feature/expression-schema-object-refactor; this one is branchedfrom, and targets,
release/v1.0.0.dev17and is self-contained (see Dependency below).What core changed, in outline:
or pick spellings themselves. Every two-spelling clause got one parameter per spelling
(
cycle/no_cycle,cascade/restrict,enforced/not_enforced,with_data/no_data,materialized/not_materialized,restart_identity/continue_identity,include_nulls/exclude_nulls,all_/distinct,wait/no_wait,cache/no_cache,order/no_order,deferrable/not_deferrable,initially_deferred/initially_immediate). "Not specified" is"neither parameter set"; setting both raises
ValueError.dialect/protocols.pybecame thedialect/protocols/package, with the umbrella protocolssplit per statement (
SchemaSupport->CreateSchemaSupport/DropSchemaSupport/NamespaceSupport;IndexSupport->CreateIndexSupport/DropIndexSupport/FulltextIndexSupport/IndexObjectSupport).Falsewhile SQL was still rendered is adefect, and probed answers that had never been measured were measured and corrected.
This branch does seven commits (oldest first):
249a5cb refactor(postgres): carry catalogue objects instead of parsed names-- a relation issplit into the object it names and the source that reads it; qualified names are carried as
schema objects rather than parsed strings, and
COMMENT ONrenders from the object itannotates. The naming side is stated once, in
mixins/namespace.py+protocols/catalog.py(PostgresNamespaceSupport);PostgresCatalogMixin/PostgresCatalogSupportare kept as importable deprecated aliases. Six names (VECTOR,HALFVEC,BITVEC,SPARSEVEC,GEOMETRY,GEOGRAPHY) were removed fromtype_values.__all__; they were never module attributes, and the pgvector/PostGIS types thatdo exist are exported under their
Postgres*class names. Resolving everyformat_*namethrough
PostgresDialect.__mro__(411 names) showed none answered by a protocol stub body.The expression round-trip matrix was reworked (see Testing).
877cebe fix(postgres): declare the sequence probes core now gates on-- core madesupports_sequence()the master switch for all three sequence formatters and added fouroption probes. The mixin now answers every probe those formatters consult.
1887630 fix(postgres): declare the ALTER SEQUENCE START probe explicitly--START WITHonALTER SEQUENCEis a different clause fromCREATE SEQUENCE ... START WITH, and core'sdefault for the former is
False.0ea4d39 feat(postgres): declare the identity probes and gate identity DDL on them-- identityand auto-increment nodes became fail-closed in core; this backend declared none of the new
probes, so core's formatters refused every identity clause. The measured boundary is declared
and the legacy
is_auto_incrementconstraint now routes through the gated formatter.b575a82 feat(postgres): consume the clause-parameter pairs and close the dialect gaps-- theformatters that still read pre-split fields now consume the pair shape; dialect-specific nodes
were split to the same shape; new probes were declared and two unspellable options refused by
name.
592e4f9 feat(postgres): declare the master probes core now gates on-- the CTE, TRUNCATE,WITH [NO] DATAand transaction wait probes are declared from measurement, and the PostgreSQLoverrides consume the probes the core formatters consume.
7246214 ci: pin the core dependency to the merged release line--CORE_PKGnow points atrelease/v1.0.0.dev30instead of the core feature branch.Clause pairs this dialect now consumes
format_truncate_statementrestart_identity/continue_identity,cascade/restrictCONTINUE IDENTITYandRESTRICTwere silently droppedformat_create_materialized_view_statement,format_refresh_materialized_view_pg_statementwith_data/no_dataWITH NO DATA; the negative was unreachable inPostgresCreateMaterializedViewExpressionformat_drop_materialized_view_statementcascade/restrictRESTRICTwas silently droppedformat_begin_transaction,format_set_transactiondeferrable/not_deferrableDEFERRABLErendered only alongsideSERIALIZABLE; unspecified renderedNOT DEFERRABLE_format_exclude_constraintdeferrable/not_deferrable,initially_deferred/initially_immediateOptional[bool]readformat_copy_to_statementforce_array/force_array_falseOptional[bool]readformat_table_constraintenforced/not_enforcedformat_create_sequence_statement,format_alter_sequence_statement,format_identity_clausecache/no_cacheDialect-specific nodes reshaped to the same pair form:
PostgresRefreshMaterializedViewExpression(
with_data/no_data),PostgresCopyToExpression(force_array/force_array_false),PostgresCreateMaterializedViewExpression(with_data/no_data),PostgresDropMaterializedViewExpression(restrictpass-through), andPostgresExcludeConstraint(deferrable/not_deferrable,initially_deferred/initially_immediate). The exclude constraint was found by the sweep rather than the auditregistry and was measured before and after.
Probes declared or corrected
Sequence (commit
877cebe,1887630):supports_sequence(),supports_create_sequence(),supports_drop_sequence(),supports_alter_sequence(),supports_sequence_if_not_exists(),supports_sequence_if_exists(),supports_sequence_start(),_increment(),_minvalue(),_maxvalue(),_cycle(),_cache(),_owned_by()answerTrue.supports_sequence_order()answersFalse:ORDER/NO ORDERis Oracle / SQL Server syntax,absent from the PostgreSQL
CREATE SEQUENCEsynopsis and rejected by the live server.NO CACHEhas no PostgreSQL form (syntax error at or near "CACHE", measured on PostgreSQL16); both sequence formatters refuse
no_cache=Trueby name rather than render a token theserver rejects.
supports_alter_sequence_start()answersTrueexplicitly. The core default isFalsebecause Oracle, SQL Server, Firebird and Snowflake reject
STARTonALTER SEQUENCE;PostgreSQL accepts it (records the start value future
RESTARTcalls use).Identity / auto-increment (commit
0ea4d39):supports_identity_column()answersTruefrom PostgreSQL 10 on (version >= (10, 0, 0)):PostgreSQL 9 rejects the clause with
syntax error at or near "GENERATED"(measured), while 10through 19 accept it. The six option probes (
generation ALWAYS,start,increment,minvalue,maxvalue,cycle) answerTrue-- same synopsis.supports_auto_increment_column()answersFalse: PostgreSQL has noAUTO_INCREMENTspelling.It is declared explicitly rather than inherited from the fail-closed default.
Restrict / cache option probes (commit
b575a82, measured against live PostgreSQL 16 and 19):supports_schema_restrict(),supports_restrict_view(),supports_materialized_view_restrict(),supports_truncate_restrict()answerTrue(
RESTRICTis the default behavior but the token itself is legal).supports_drop_function_restrict()answersTrueand follows the routine DDL gate(
>= 9.6).supports_identity_cache()answersTrue(CACHE 1is the uncached form);NO CACHEisrefused by name for identity too.
Master probes (commit
592e4f9; measurements on the 9.6 - 19beta4 matrix):supports_materialized_cte()keeps its>= 12gate: 9.6, 10 and 11 rejectAS MATERIALIZEDand
AS NOT MATERIALIZED; 12 through 19beta4 accept both.supports_truncate()answersTrue-- accepted by every server in the matrix.supports_with_data_clause()answersTrue, version-independent; the one probe answers allthree consumers (CTAS, CREATE MATERIALIZED VIEW, REFRESH MATERIALIZED VIEW).
supports_transaction_wait()answersFalse:BEGIN NO WAITandSET TRANSACTION NO WAITaresyntax errors on every measured server (9.6 through 19beta4); the pair is refused by name
instead of being dropped from the rendered statement.
Naming side (commit
249a5cb):supports_catalog(),supports_catalog_qualification()andsupports_schema_qualification()answerTrue;validate_catalog_name()accepts any non-emptydatabase name;
format_qualified_name()spells[database.][schema.]namelevel by level.supports_schema()remains the DDL question ("can this engine have schemas") and stays inmixins/schema.py.Routine (commit
249a5cb):supports_function(),supports_create_function(),supports_drop_function(),supports_function_or_replace(),supports_function_parameters(),supports_drop_function_if_exists(),supports_drop_function_cascade()andsupports_drop_function_restrict()are declared; the CREATE/DROP forms are tied tosupports_function_ddl()(>= 9.6).CreateFunctionExpression/DropFunctionExpressionnowrender through the core formatters.
Intended behaviour changes
CREATE MATERIALIZED VIEWwith nothing requested no longer appendsWITH NO DATA.SET TRANSACTIONwith nothing requested no longer appendsNOT DEFERRABLE.BEGIN ... DEFERRABLErenders for any isolation level.RESTRICTandCONTINUE IDENTITYbecome reachable where they were previously dropped.REFRESH MATERIALIZED VIEWwithWITH [NO] DATArender instead of beingrefused by the fail-closed default;
wait/no_waitnow raiseUnsupportedFeatureErrornaming the option instead of being silently dropped.
(
UnsupportedFeatureError) instead of being handedGENERATED ... AS IDENTITY; theSERIALtype family keeps working and stays the explicit choice.
Dependency
The workflow installs core from
release/v1.0.0.dev30(the line PR #125 landed on) and thetestsuite from its own
feature/expression-schema-object-refactorbranch, which is not mergedyet. No unmerged core branch is needed, so this PR can be merged on its own.
Adds # (issue): n/a -- no tracking issue in this repository; core counterpart is
rhosocial/python-activerecord#125.
Type of change
feat: A new featuretest: Adding missing tests or correcting existing tests (for the new feature)ci: Changes to our CI configuration files and scripts (if related to the new feature)Breaking Change
(If Yes, please describe the impact and migration path below):
Yes, but only on the dialect/expression layer. No model- or query-level application API is
touched: every changed file under
src/is inrhosocial/activerecord/backend/impl/postgres/,and no model or query-builder signature changed. The breaks are for code that uses this
package's dialect/expression layer directly:
PostgresDialect.supports_auto_increment()is removed. It answeredTruefor "AUTO_INCREMENT/IDENTITY supported" and was defined on
PostgresGeneratedColumnMixin. It is replaced by twomechanism probes:
supports_identity_column()(true from PostgreSQL 10) andsupports_auto_increment_column()(False). Migration: ask the specific probe.per spelling, for example
PostgresCreateMaterializedViewExpression(dialect, view=MaterializedView(...), with_data=False, no_data=True)instead ofview_name="..."plus a tri-state; the same appliesto
PostgresDropMaterializedViewExpression(restrict),PostgresRefreshMaterializedViewExpression(
with_data/no_data),PostgresCopyToExpression(force_array/force_array_false) andPostgresExcludeConstraint(deferrable/not_deferrable,initially_deferred/initially_immediate). Setting both of a pair raisesValueErrorat construction. Migration:pass the object class instead of a name string and exactly one side of each pair.
format_materialized_view_alter_action()now returns(sql, params)instead of a bare string(
protocols/materialized_view.py).transaction
wait/no_waitis now refused by name instead of being silently dropped, andrequests for spellings PostgreSQL lacks (
no_cacheon sequences and identity, a bareAUTO_INCREMENT, identity on PostgreSQL 9 via the legacyis_auto_incrementroute) raiseUnsupportedFeatureErrorinstead of rendering SQL the server rejects.PostgresCatalogMixin->PostgresNamespaceMixin,PostgresCatalogSupport->PostgresNamespaceSupport; both old namesstay importable.
Applicable
rhosocial-activerecordVersion Range:pyproject.tomldeclaresrhosocial-activerecord>=1.0.0.dev0,<2.0.0; this branch requires theexpression/dialect split merged as core #125 on the
release/v1.0.0.dev30line, which is whatthe workflow pins.
Related Repositories/Packages
release/v1.0.0.dev30. That is theentire cross-repository dependency of this branch;
test.ymlinstalls core from that line.feature/expression-schema-object-refactor, notmerged yet. CI installs the testsuite from that branch; it does not block merging this PR, but
the testsuite branch must land for these tests to run against a released testsuite.
feature/expression-schema-object-refactor; they are independent of this PR (elevenrepositories total, core included).
Testing
Test Plan:
New guard / confirmation files added by this branch:
tests/rhosocial/activerecord_postgres_test/feature/backend/test_clause_pair_guard.py-- assertsthat the four states of every pair this dialect consumes are pairwise distinguishable (neither
set renders neither spelling; A renders A only; B renders B only; both raises
ValueError),plus a non-vacuousness class (every case has two distinct parameters and a spelling or a named
refusal). Run red against the unmodified tree first: 24 failing test ids; green after commit
b575a82. Extended in592e4f9with the master probes and the wait pair: red first with 13failing ids of 155; green after. Two mutations (removed exclusion check, reverted restrict
branch) were each caught by the expected guard test.
tests/rhosocial/activerecord_postgres_test/feature/backend/test_identity_execution.py-- rendersidentity / auto-increment / SERIAL DDL and executes it against a live server; each verdict is
preceded by a rejected sentinel and accepted control, identity is
NOT_RENDEREDon PostgreSQL 9where the probe says
False.tests/rhosocial/activerecord_postgres_test/feature/backend/test_master_probe_execution.py--execution confirmation for the master probes (materialized CTE, truncate,
WITH [NO] DATA,wait), green on the 11-scenario matrix (9.6 through 19beta4).
tests/rhosocial/activerecord_postgres_test/feature/backend/dialect/test_object_kind_guards.py--a statement handed the wrong kind of catalogue object must refuse, not render; the
(statement class, slot, required kind, impostor kind) table is derived from the constructor
annotations rather than hand-listed.
Reworked:
tests/rhosocial/activerecord_postgres_test/feature/backend/test_expression_roundtrip_all.py--the matrix used to call the testsuite's
sql_consistent, which swallowed every render failureand reported green; the module docstring records the before-measurement (168 classes discovered,
125 reached a SQL comparison, 20 raised and were swallowed, 23 skipped as unconstructible -- 43
of 168 never asserted). Outcomes are now classified:
renders(byte-identical SQL and bindparameters across dict/JSON/XML),
UnsupportedFeatureError, entries inLEGITIMATE_NON_RENDERSpinned by type and message fragment, and anything else fails namingthe class.
Updated:
test_postgres_protocol_conformance.py(protocol/mixin forward and reverse coverage plusthe non-overlap guard for the new protocols),
test_ddl_expressions.py,test_ddl_objects.py,test_ddl_alter_config.py,test_materialized_view.py,test_materialized_view_integration.py,test_drop_table_cascade.py,test_schema_support.py,test_schema_namespace.py,test_view_execution.py,test_index_pg.py,test_auto_increment_ddl.py,test_transaction_advanced_features.py,test_model_truncate.py, and the extension integrationsuites.
Commands:
export PYTHONPATH=src:tests python -m pytest tests/ --tb=short -n 8 --dist=loadgroupThe workflow matrix covers: Python 3.8 / PostgreSQL 9, 3.8 / 10, 3.9 / 11, 3.10 / 12, 3.11 / 13,
3.12 / 14, 3.13 / 15, 3.13 / 16, 3.14 / 17 and 3.14 / 18 (coverage), plus a Python 3.14 /
PostgreSQL 19beta4 job and a Python 3.14 / PostgreSQL 18 benchmark job. The recorded green run for
the last code commit (
592e4f9, workflow run 37657306058,https://github.com/rhosocial/python-activerecord-postgres/actions/runs/37657306058) shows all
twelve matrix jobs and the benchmark job passing;
7246214only changes the workflow's coredependency pin.
Checklist
README.md, etc.).changelog.d/<issue_number>.<type>.md).Additional Notes
docs/en_US/postgres_specific_features/protocol_support.md,docs/zh_CN/postgres_specific_features/protocol_support.md,docs/zh_CN/ddl/materialized_view.md,.claude/backend_development.md, and the docstringsacross the changed mixins/protocols. The examples under
src/.../impl/postgres/examples/were updated to the object- and pair-based constructor shapes.
git diff origin/release/v1.0.0.dev17..HEAD -- changelog.d/is empty); add one when opening the PR ifthe repository requires it (
changelog.d/<issue>.added.md/.changed.md).pre-existing
DeprecationWarnings from core (e.g. raw subtype strings), and nowarnings-as-errors run was performed on this branch.
gate on regression.