Skip to content

feat(bigquery): add native materialized view support and type domain contract - #1

Merged
vistart merged 10 commits into
release/v1.0.0.dev1from
feature/activerecord-derived-ddl
Sep 27, 2026
Merged

vistart merged 10 commits into
release/v1.0.0.dev1from
feature/activerecord-derived-ddl

Conversation

@vistart

@vistart vistart commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Description

Brings the BigQuery backend onto the derived-DDL model in
rhosocial/python-activerecord#121. This is the smallest of the backend changes — 9 commits
across 19 files — and it is mostly additive.

  • Native BigQuery materialized views are supported. The capability probe previously reported
    support for DDL it could not emit, so the existing case that asserted the old probe
    behaviour is realigned rather than deleted.
  • A type domain negative contract is added.
  • DDL rendering moves to direct DDL expressions, and DDLSource collection, table inheritance
    and tablespace are covered.

Type of change

  • feat: A new feature
  • docs: 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

  • Yes, for end-users (backward-incompatible API changes)
  • Yes, for backend developers (internal architectural changes that may affect custom implementations)
  • No breaking changes

No commit carries a ! marker. The public model and query APIs are unchanged.

Impact on End-Users:

No backward-incompatible API changes. Materialized views now render instead of being
reported as supported and then dropped.

Impact on Backend Developers (Custom Implementations):

  • The materialized-view capability probe is corrected, so a conformance suite asserting the
    old value will fail.
  • DDL rendering moves to direct DDL expressions.
  • TYPE_CHECKING-only imports broken by the expression namespace split are repaired.

Related Repositories/Packages

The core half is rhosocial/python-activerecord#121 (merged into release/v1.0.0.dev30) and
the shared contract half is rhosocial/python-activerecord-testsuite#21 (merged into
release/v1.0.0.dev17).

This PR also repoints this repository's workflow at those release branches, a prerequisite for
merging: the dependencies were pinned to feature/activerecord-derived-ddl, a branch that
only exists to carry this work.

Merged or in flight: mysql #56, postgres #58, mariadb #5, sqlserver #4, oracle. Still to
merge: clickhouse, firebird, snowflake.

Testing

  • I have added new tests to cover my changes.
  • I have updated existing tests to reflect my changes.
  • I have run the full test suite locally and confirmed all tests pass.

Local full-suite execution was skipped at the user's instruction. Verification relied on
remote CI on this exact tip 4d2e9f0, which is also the first execution against the release
branches rather than the feature branch, so the new dependency refs are verified to resolve.

Checklist

  • My code follows the project's coding conventions
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the documentation accordingly
  • I have added a changelog fragment to changelog.d/ (to be added after this PR is
    opened, named by the actual PR number)

Additional Notes

There is no pre-written 1.added.md in this repository's changelog.d/; existing fragments
are numbered 2 and 3 from an earlier release, so PR #1 does not collide with anything.

Unlike the other eight backends, this repository has no prior pull request, so this is its
first PR against the derived-DDL model and its first CI run against the release branches.

BigQuery has native materialized views, but supports_materialized_view() inherited
the core default of False, so there was no support at all — raw SQL was the only
option.

Implemented against the GoogleSQL DDL reference (each clause checked against the
official documentation):

    CREATE [OR REPLACE] MATERIALIZED VIEW [IF NOT EXISTS] mv_name
        [PARTITION BY expr] [CLUSTER BY cols] [OPTIONS(...)] AS query
    ALTER MATERIALIZED VIEW [IF EXISTS] mv_name SET OPTIONS(...)
    DROP MATERIALIZED VIEW [IF EXISTS] mv_name
    CREATE MATERIALIZED VIEW replica [OPTIONS(...)] AS REPLICA OF source

Design notes:

- BigQuery has no REFRESH MATERIALIZED VIEW statement; refresh is scheduled
  through OPTIONS(enable_refresh=…, refresh_interval_minutes=…), so
  supports_refresh_materialized_view() stays False and schedule changes go
  through BigQueryAlterMaterializedViewSetOptionsExpression.
- Clauses BigQuery lacks are refused rather than silently dropped: column
  aliases (a BQ MV takes its column names from the query), TABLESPACE, storage
  parameters, WITH [NO] DATA and DROP ... CASCADE. Passing them via the generic
  expressions raises UnsupportedFeatureError.
- Option names are enumerated as BigQueryMaterializedViewOption (10 documented
  options with value_type and documented defaults) and validated against that
  enum rather than by pattern matching — the same trade-off as PostgreSQL's
  PostgresStorageParameter.
- OR REPLACE and IF NOT EXISTS are mutually exclusive (explicit in BQ's grammar),
  so they are rejected up front instead of producing SQL the server refuses.
- Replica creation validates replication_interval_seconds within the documented
  [60, 3600] range.

Tests: 42 new cases covering clause order, clause rejection, the option
catalogue, boundary values, protocol conformance, and the backend-side
assertion of the MRO invariant "the formatter is owned by the BigQuery mixin".
…iour

test_materialized_view_not_supported in test_bigquery_ddl_improvements asserted
supports_materialized_view() is False (surfaced by CI run 36214625382). BigQuery
now supports native materialized views, so it asserts True and additionally pins
supports_refresh_materialized_view() to False — BigQuery has no REFRESH
MATERIALIZED VIEW statement, refresh is scheduled through OPTIONS.
… revisions

The installed hook exec'd scripts/preflight.sh unconditionally. That hook is a
local, untracked artifact, so it survives a checkout of any commit that predates
the script, and on such a commit exec failed with a bare "No such file or
directory" — blocking the commit with nothing to act on.

In this repository scripts/preflight.sh first appears at 71de328, so every earlier
commit is affected. The failure is also silent in a dangerous way: it swallowed
a `git commit --amend`, so a commit-message rewrite reported as applied when it
had not been, and had to be redone.

The hook now checks the script is executable and exits 0 otherwise, so a revision
without the pre-flight simply skips it. Normal operation is unchanged.

Also honours core.hooksPath. git ignores .git/hooks entirely once that is set, so
the hook was being written to a location that would never run while this script
still reported success. No repository sets it today, so that part is latent.
Both dependencies were pinned to feature/activerecord-derived-ddl, a branch
that exists only to carry the derived-DDL work. Now that the core change has
landed in release/v1.0.0.dev30 and the matching testsuite change in
release/v1.0.0.dev17, pinning to the feature branch means CI keeps resolving a
branch that will be deleted once the backend PRs merge.

This is a prerequisite for merging: leaving the old ref in place means the
first run after that branch disappears fails to resolve, not the code.
@vistart vistart self-assigned this Sep 27, 2026
@vistart vistart added the enhancement New feature or request label Sep 27, 2026
@vistart
vistart merged commit e96119a into release/v1.0.0.dev1 Sep 27, 2026
5 checks passed
vistart added a commit that referenced this pull request Oct 11, 2026
Two defects, both silent. The configured template path pointed at a file that
was never committed, so 	owncrier build failed outright instead of emitting
a changelog -- verified on a clean checkout, not just locally. Filled in with
the template the other backends use.

Separately, PR #1 left a 1.internal.md behind, but internal is not one of
the four types this repo configures. towncrier skips a fragment whose type it
has no directory for, so that entry -- installing core and testsuite from
release branches, and the pre-commit hook fix -- would have vanished from the
next release notes. Moved to 1.changed.md, a type this repo does configure,
and deleted the original.

Verified after both changes: 	owncrier build --draft exits 0 and renders
every fragment from all four pull requests under its section.
vistart added a commit that referenced this pull request Oct 11, 2026
… new string nodes (#4)

* feat: the typed-columns column-type suggestion table

* feat: align the column-type table with the rebuilt core protocol

* fix(bigquery): follow the core rename of DateTimeColumn to TimestampColumn

* test: follow the rebuilt DDL-source accessor semantics (column_type / column_data_type)

* test: build predicates explicitly, now that the bare Column offers no operators

* deps: pin pydantic 2.12.2 / pydantic-core 2.41.4 on Python 3.13 (incl. 3.13t)

* feat: render the new pad/repeat/trim nodes (BigQuery spells lpad/rpad/repeat natively, trim via TRIM/LTRIM/RTRIM)

* test: enumerate every core node class so a missing formatter cannot stay silent

* ci: add the Python 3.15 leg (GIL only — no cp315t wheel for the driver)

google-cloud-bigquery 3.46.1 is a universal wheel and declares a
Python :: 3.15 classifier, but publishes no cp315t wheel, so a
free-threaded 3.15t leg has nothing to run and is deliberately not
declared.

CI: append '3.15' to the plain python-version matrix list, on the same
required footing as the other legs (no continue-on-error, no if: gate).

pyproject: insert the Programming Language :: Python :: 3.15 classifier
in sorted position (the list starts at 3.10; no lower versions invented).

README: state the supported Python range explicitly (3.10 through 3.15
inclusive; requires-python is a floor, not a cap) and note the dynamic
pyversions badge reads the classifiers from PyPI, so it picks 3.15 up on
publish.

* ci: install the core from its release branch now that typed-columns has merged

While these branches were in development, CI installed the core from
`@feature/typed-columns`, because the release branch did not carry the rebuilt
column layer yet. That branch has now merged into `release/v1.0.0.dev30`
(PR #126), so the feature reference is both stale and about to disappear with
branch deletion.

Point the core dependency at `release/v1.0.0.dev30` instead — the same branch
these backends are merging into, which is what the release-side CI already
used. The testsuite reference is deliberately left alone: that branch
(`test/typed-column-contracts`) has not merged into its release yet, and
pointing at a release that lacks the new contracts would break every backend
at once. It moves when the testsuite merges.

ClickHouse expresses the dependency as a `ref:` on `actions/checkout` rather
than as a pip requirement; that one is switched the same way.

* chore: add changelog fragment for PR #4

* fix(changelog): add the missing template and stop dropping a fragment

Two defects, both silent. The configured template path pointed at a file that
was never committed, so 	owncrier build failed outright instead of emitting
a changelog -- verified on a clean checkout, not just locally. Filled in with
the template the other backends use.

Separately, PR #1 left a 1.internal.md behind, but internal is not one of
the four types this repo configures. towncrier skips a fragment whose type it
has no directory for, so that entry -- installing core and testsuite from
release branches, and the pre-commit hook fix -- would have vanished from the
next release notes. Moved to 1.changed.md, a type this repo does configure,
and deleted the original.

Verified after both changes: 	owncrier build --draft exits 0 and renders
every fragment from all four pull requests under its section.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant