Skip to content
Merged
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ permissions:
env:
# rhosocial-activerecord and rhosocial-activerecord-testsuite dev releases are
# not published to PyPI; install them from the feature/parameter-inline branch.
CORE_PKG: "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/activerecord-derived-ddl"
TESTSUITE_PKG: "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/activerecord-derived-ddl"
CORE_PKG: "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30"
TESTSUITE_PKG: "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17"

jobs:
test:
Expand Down
6 changes: 6 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,9 @@ The backend config supports `api_endpoint` and `use_anonymous_credentials` for e
- **Service account authentication**: Key file or application default credentials
- **Query jobs**: All queries run as jobs; results fetched asynchronously
- **Local Testing**: Uses `goccy/bigquery-emulator` (Docker) for CI/integration tests; no real GCP account needed

## Search Scope

- Use `rg` for repository searches; never use `grep`.
- Limit searches to this `python-activerecord` repository and its related testsuite/backend directories.
- Exclude `.git/`, `.venv*/`, `__pycache__/`, `build/`, `dist/`, and other cache directories.
1 change: 1 addition & 0 deletions changelog.d/1.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Support native BigQuery materialized views, which now render instead of being reported as supported and then dropped. Add a type domain negative contract, render DDL through direct DDL expressions, and cover `DDLSource` collection, table inheritance and tablespace.
1 change: 1 addition & 0 deletions changelog.d/1.internal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Install core and testsuite from release branches in CI instead of the derived-DDL feature branch, and stop the pre-commit hook from blocking commits on revisions that predate `scripts/preflight.sh`.
1 change: 1 addition & 0 deletions changelog.d/3.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add BigQuery materialized view support: `BigQueryCreateMaterializedViewExpression` (`OR REPLACE` / `IF NOT EXISTS` / `PARTITION BY` / `CLUSTER BY` / `OPTIONS(...)`), `BigQueryDropMaterializedViewExpression`, `BigQueryAlterMaterializedViewSetOptionsExpression`, `BigQueryCreateMaterializedViewReplicaExpression` (`AS REPLICA OF`), and the enumerated `BigQueryMaterializedViewOption` catalogue for `OPTIONS(...)` names. Generic-expression clauses BigQuery does not have (column aliases, `TABLESPACE`, storage parameters, `WITH [NO] DATA`, `CASCADE`) are rejected with `UnsupportedFeatureError` rather than silently dropped. `supports_materialized_view()` now reports `True`; `supports_refresh_materialized_view()` stays `False` because BigQuery has no `REFRESH MATERIALIZED VIEW` statement — refresh is scheduled through `OPTIONS(enable_refresh=…, refresh_interval_minutes=…)`.
31 changes: 26 additions & 5 deletions scripts/install-git-hooks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,42 @@
# Installs an optional pre-commit hook that runs scripts/preflight.sh so a
# broken import/lint is caught before the commit (and before a CI round-trip).
# Bypass a single commit with: git commit --no-verify
#
# Re-run this after pulling, and re-run it from the repository root. To remove
# the hook, delete .git/hooks/pre-commit (or the core.hooksPath directory).
set -euo pipefail

cd "$(dirname "$0")/.."

HOOK=".git/hooks/pre-commit"
if [ ! -d .git/hooks ]; then
echo "install-git-hooks: .git/hooks not found (not a git repo?)" >&2
exit 2
# git ignores .git/hooks entirely once core.hooksPath is set, so a hook written
# there would never run while this script still reported success.
HOOKS_PATH="$(git config core.hooksPath || true)"
if [ -n "$HOOKS_PATH" ]; then
echo "install-git-hooks: core.hooksPath is set to '$HOOKS_PATH'." >&2
echo "install-git-hooks: .git/hooks is ignored by git; writing the hook to" >&2
echo " '$HOOKS_PATH/pre-commit' instead." >&2
HOOK="$HOOKS_PATH/pre-commit"
mkdir -p "$HOOKS_PATH"
else
HOOK=".git/hooks/pre-commit"
if [ ! -d .git/hooks ]; then
echo "install-git-hooks: .git/hooks not found (not a git repo?)" >&2
exit 2
fi
fi

cat > "$HOOK" <<'HOOK_BODY'
#!/usr/bin/env bash
# Installed by scripts/install-git-hooks.sh — runs the local pre-flight.
# Skip with: git commit --no-verify
exec "$(git rev-parse --show-toplevel)/scripts/preflight.sh"
#
# The guard below matters on older revisions: this hook is a local, untracked
# artifact, so it survives a checkout of a commit that predates
# scripts/preflight.sh. Without the guard, exec fails with a bare
# "No such file or directory" and blocks the commit with nothing to act on.
PREFLIGHT="$(git rev-parse --show-toplevel)/scripts/preflight.sh"
[ -x "$PREFLIGHT" ] || exit 0
exec "$PREFLIGHT"
HOOK_BODY
chmod +x "$HOOK"

Expand Down
19 changes: 19 additions & 0 deletions src/rhosocial/activerecord/backend/impl/bigquery/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@
from .dialect import BigQueryDialect
from .transaction import BigQueryTransactionManager
from .async_transaction import AsyncBigQueryTransactionManager
from .materialized_view_options import ( # noqa: F401
BigQueryMaterializedViewOption,
BigQueryMaterializedViewOptionValueType,
)
from .expression.materialized_view import (
BigQueryAlterMaterializedViewSetOptionsExpression,
BigQueryCreateMaterializedViewExpression,
BigQueryCreateMaterializedViewReplicaExpression,
BigQueryDropMaterializedViewExpression,
)
from .types import BigQueryStruct, BigQueryArray, BigQueryJSON
from .adapters import (
BigQueryStructAdapter,
Expand All @@ -35,6 +45,7 @@
BigQueryArraySupport,
BigQueryJSONSupport,
BigQueryGeographySupport,
BigQueryMaterializedViewSupport,
)
from .mixins import (
BigQueryStructMixin,
Expand All @@ -50,6 +61,13 @@
"BigQueryDialect",
"BigQueryTransactionManager",
"AsyncBigQueryTransactionManager",
# Materialized views
"BigQueryMaterializedViewOption",
"BigQueryMaterializedViewOptionValueType",
"BigQueryCreateMaterializedViewExpression",
"BigQueryDropMaterializedViewExpression",
"BigQueryAlterMaterializedViewSetOptionsExpression",
"BigQueryCreateMaterializedViewReplicaExpression",
"BigQueryStruct",
"BigQueryArray",
"BigQueryJSON",
Expand All @@ -62,6 +80,7 @@
"BigQueryArraySupport",
"BigQueryJSONSupport",
"BigQueryGeographySupport",
"BigQueryMaterializedViewSupport",
"BigQueryStructMixin",
"BigQueryArrayMixin",
"BigQueryJSONMixin",
Expand Down
12 changes: 10 additions & 2 deletions src/rhosocial/activerecord/backend/impl/bigquery/dialect.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
JoinSupport, ViewSupport, SchemaSupport, IndexSupport,
ConstraintSupport, IntrospectionSupport, TransactionControlSupport,
SQLFunctionSupport, JSONSupport, TruncateSupport,
UserDefinedTypeSupport, DomainSupport,
)
from rhosocial.activerecord.backend.dialect.mixins import (
CTEMixin, WindowFunctionMixin, JSONMixin,
Expand All @@ -19,12 +20,13 @@
# Core generic mixins (backend-agnostic implementations)
PredicateMixin, ExpressionMixin, DQLMixin, DMLMixin,
SetOperationMixin, DateTimeMixin,
DDLColumnMixin, DDLTypeMixin, TransactionControlMixin,
CollationMixin,
DDLColumnMixin, DDLTypeMixin, UserDefinedTypeMixin, DomainMixin,
TransactionControlMixin, CollationMixin,
)
from .protocols import (
BigQueryStructSupport, BigQueryArraySupport,
BigQueryJSONSupport, BigQueryGeographySupport,
BigQueryMaterializedViewSupport,
)
from .mixins import (
BigQueryStructMixin, BigQueryArrayMixin,
Expand All @@ -36,17 +38,21 @@
BigQueryDDLColumnMixin,
BigQueryCapabilityMixin,
BigQueryIdentifierMixin,
BigQueryMaterializedViewMixin,
)


class BigQueryDialect(
# New BigQuery-specific mixins (BEFORE SQLDialectBase and generic mixins they override)
BigQueryCapabilityMixin,
BigQueryTypeSupportMixin,
UserDefinedTypeMixin,
DomainMixin,
BigQueryDQLMixin,
BigQuerySchemaMixin,
BigQueryDDLColumnMixin,
BigQueryIdentifierMixin,
BigQueryMaterializedViewMixin, # Before ViewMixin to override materialized view DDL
SQLDialectBase,
# Generic mixins
CTEMixin, WindowFunctionMixin, JSONMixin,
Expand All @@ -68,6 +74,8 @@ class BigQueryDialect(
SQLFunctionSupport, JSONSupport, TruncateSupport,
BigQueryStructSupport, BigQueryArraySupport,
BigQueryJSONSupport, BigQueryGeographySupport,
BigQueryMaterializedViewSupport,
UserDefinedTypeSupport, DomainSupport,
):
def __init__(self, version: Tuple[int, ...] = (3, 0, 0), **kwargs):
super().__init__(**kwargs)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# src/rhosocial/activerecord/backend/impl/bigquery/expression/__init__.py
"""BigQuery-specific DDL expressions."""

from .materialized_view import (
BigQueryAlterMaterializedViewSetOptionsExpression,
BigQueryCreateMaterializedViewExpression,
BigQueryCreateMaterializedViewReplicaExpression,
BigQueryDropMaterializedViewExpression,
)

__all__ = [
"BigQueryAlterMaterializedViewSetOptionsExpression",
"BigQueryCreateMaterializedViewExpression",
"BigQueryCreateMaterializedViewReplicaExpression",
"BigQueryDropMaterializedViewExpression",
]
Loading
Loading