Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
55c071f
fix(firebird): drop index by name without ON <table>
vistart Sep 19, 2026
d8e43c1
feat(firebird): add FirebirdColumnDefinition and typed column options
vistart Sep 20, 2026
76f054a
feat(firebird): add FirebirdCreateTableExpression for on_commit_delet…
vistart Sep 20, 2026
38946dd
refactor(firebird): drop dialect_options from data-type value objects
vistart Sep 20, 2026
28dec9a
fix(firebird): correct unsupported cascade-view and functional-index …
vistart Sep 20, 2026
4b5ec19
test(firebird): pin view check-option / collation / unique-index bits
vistart Sep 20, 2026
00ddcdf
refactor(ddl): drop legacy identity fields from column classes
vistart Sep 21, 2026
3792e2d
feat(ddl): render column attributes; backend identity syntax
vistart Sep 21, 2026
e4bb75f
test(ddl): classify ColumnAttributeSupport; use deriver.creation_plan()
vistart Sep 21, 2026
8490502
fix(firebird): stop silently dropping comments; compose comment mixin…
vistart Sep 23, 2026
524e71e
test(ddl): classify CommentSupport for Firebird conformance
vistart Sep 23, 2026
294bb60
fix(ddl): stop silently dropping generated columns and constraint opt…
vistart Sep 23, 2026
a341aa2
feat(firebird): complete domain DDL support
vistart Sep 24, 2026
d89a9c9
fix(ddl): reject unsupported table declarations
vistart Sep 24, 2026
f776c49
test(ddl): cover table inheritance and tablespace
vistart Sep 24, 2026
9c05556
test(firebird): adapt auto increment dialect fixture
vistart Sep 24, 2026
09c5dde
fix(firebird): restore domain DDL rendering
vistart Sep 24, 2026
3ab49d8
test(firebird): render alter domain constraint
vistart Sep 24, 2026
a67d94b
test(firebird): handle DDL type protocol alias
vistart Sep 24, 2026
e06025c
refactor(firebird): use direct DDL expressions
vistart Sep 25, 2026
7007137
docs: document repository search scope
vistart Sep 25, 2026
646f3f1
test(ddl): cover DDLSource collection
vistart Sep 25, 2026
80376bf
fix(backend): repair unresolvable TYPE_CHECKING imports
vistart Sep 27, 2026
2733ea9
chore(test): register the pytest markers the suite already uses
vistart Sep 27, 2026
d1dd46f
fix(scripts): stop the pre-commit hook from blocking commits on older…
vistart Sep 27, 2026
0c57328
ci: install core and testsuite from release branches
vistart Sep 27, 2026
0b58a37
chore: add changelog fragment for PR #5
vistart Sep 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
- name: Install activerecord and testsuite
run: |
pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30"
pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/activerecord-derived-ddl"
pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17"

Check warning on line 71 in .github/workflows/test.yml

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Omitting "--only-binary :all:" can lead to the execution of setup scripts. Make sure it is safe here.

See more on https://sonarcloud.io/project/issues?id=rhosocial_python-activerecord-firebird&issues=AaDipTzYTFIbZ_TprMmD&open=AaDipTzYTFIbZ_TprMmD&pullRequest=5

Check warning on line 71 in .github/workflows/test.yml

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Using dependencies without locking resolved versions is security-sensitive.

See more on https://sonarcloud.io/project/issues?id=rhosocial_python-activerecord-firebird&issues=AaDipTzYTFIbZ_TprMmE&open=AaDipTzYTFIbZ_TprMmE&pullRequest=5

- name: Verify Firebird connection and create test user
run: |
Expand Down
8 changes: 7 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,10 @@ This project is under active development. Key features implemented:

## Version Control and Changelog

This project adheres to the same version control, branching, commit message, and changelog management standards as the main `python-activerecord` project.
This project adheres to the same version control, branching, commit message, and changelog management standards as the main `python-activerecord` project.

## 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/5.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Complete Firebird domain DDL support, including `ALTER DOMAIN` constraint rendering and the DDL type protocol alias, add `FirebirdCreateTableExpression` for `on_commit_delete` and `external_file`, add `FirebirdColumnDefinition` with typed column options, classify `CommentSupport` for conformance, and drop an index by bare name without `ON <table>`.
1 change: 1 addition & 0 deletions changelog.d/5.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Restore Firebird domain DDL rendering, which was broken rather than merely incomplete, and stop silently dropping generated column definitions, constraint options and declared comments — the last of these because the comment mixin was composed after `TableMixin` and so never reached the renderer. Unsupported table declarations are now rejected, and the cascade-view and functional-index capability bits are corrected.
1 change: 1 addition & 0 deletions changelog.d/5.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, repair TYPE_CHECKING imports broken by the expression namespace split, register the pytest markers the suite already uses, and stop the pre-commit hook from blocking commits on revisions that predate `scripts/preflight.sh`.
1 change: 1 addition & 0 deletions changelog.d/5.removed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove the reserved `dialect_options` bag from data-type value objects and the legacy identity fields from column classes in the Firebird backend.
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ markers = [
"query: marks tests for query functionality",
"transaction: marks tests for transaction handling",
"requires_capability: marks tests requiring specific backend capabilities",
"feature: category label for feature-area tests; changes collection grouping only, not ordering",
"benchmark_relation: marks relation benchmarks; changes collection grouping only, not ordering",

]

[tool.coverage.run]
Expand Down
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

Check failure on line 17 in scripts/install-git-hooks.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=rhosocial_python-activerecord-firebird&issues=AaDipT0MTFIbZ_TprMmF&open=AaDipT0MTFIbZ_TprMmF&pullRequest=5
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
26 changes: 17 additions & 9 deletions src/rhosocial/activerecord/backend/impl/firebird/dialect.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
- DECFLOAT (FB 4.0+)
"""

from typing import Any, Dict, Optional, Tuple, TYPE_CHECKING
from typing import Optional, Tuple, TYPE_CHECKING

from rhosocial.activerecord.backend.dialect.base import SQLDialectBase
from rhosocial.activerecord.backend.dialect.protocols import (
Expand Down Expand Up @@ -50,6 +50,7 @@
GeneratedColumnSupport,
ViewSupport,
FunctionSupport,
UserDefinedTypeSupport,
)
from rhosocial.activerecord.backend.dialect.mixins import (
CollationMixin,
Expand Down Expand Up @@ -87,11 +88,10 @@
DQLMixin,
DMLMixin,
DDLColumnMixin,
UserDefinedTypeMixin,
TransactionControlMixin,
)
from rhosocial.activerecord.backend.dialect.exceptions import UnsupportedFeatureError

from .collation import validate_firebird_collation_name
from .mixins.version_boundaries import _norm_version
from .reserved_words import FIREBIRD_RESERVED_WORDS
from .mixins import (
Expand Down Expand Up @@ -140,6 +140,7 @@
FirebirdLockingSupport,
FirebirdTransactionSupport,
FirebirdTableSupport,
FirebirdDomainSupport,
FirebirdTriggerSupport,
FirebirdReturningSupport,
FirebirdIntrospectionSupport,
Expand All @@ -165,10 +166,7 @@
)

if TYPE_CHECKING:
from rhosocial.activerecord.backend.expression import bases
from rhosocial.activerecord.backend.expression.collation import CollateExpression
from rhosocial.activerecord.backend.expression.statements import (
ReturningClause,
CreateTableExpression,
ColumnDefinition,
TableConstraint,
Expand Down Expand Up @@ -217,10 +215,12 @@ class FirebirdDialect(
DQLMixin,
FirebirdAlterTableModifierMixin, # Before DDLColumnMixin to override format_*_action
DDLColumnMixin,
UserDefinedTypeMixin,
TransactionControlMixin,
# Firebird-specific overrides (before generic mixins to take precedence)
FirebirdDMLOperationMixin, # Must be before DMLMixin
FirebirdTableMixin, # Must be before TableMixin
FirebirdCommentMixin, # Must be before TableMixin (supports_comment_on/format_comment_statement)
TableMixin,
ConstraintMixin,
FirebirdTriggerMixin, # Must be before TriggerMixin
Expand All @@ -236,7 +236,6 @@ class FirebirdDialect(
FirebirdExternalFunctionMixin,
FirebirdRoleMixin,
FirebirdUserMixin,
FirebirdCommentMixin,
FirebirdDatabaseMixin,
# Core feature mixins (no duplicates)
DMLMixin,
Expand Down Expand Up @@ -303,6 +302,7 @@ class FirebirdDialect(
FirebirdLockingSupport,
FirebirdTransactionSupport,
FirebirdTableSupport,
FirebirdDomainSupport,
FirebirdTriggerSupport,
FirebirdReturningSupport,
FirebirdIntrospectionSupport,
Expand All @@ -325,6 +325,7 @@ class FirebirdDialect(
FirebirdCollationSupport,
FirebirdExceptionSupport,
FirebirdContextVariableSupport,
UserDefinedTypeSupport,
FirebirdPartitionMixin,
FirebirdTypeSupportMixin,
):
Expand Down Expand Up @@ -580,14 +581,20 @@ def supports_create_index(self) -> bool:
def supports_drop_index(self) -> bool:
return True

def supports_drop_index_on_table(self) -> bool:
return False

def supports_unique_index(self) -> bool:
return True




def supports_functional_index(self) -> bool:
return True
# Expression (computed) indexes need Firebird's ``COMPUTED BY`` syntax,
# which is not implemented yet; declaring support would let the generic
# renderer emit invalid SQL.
return False



Expand Down Expand Up @@ -616,7 +623,8 @@ def supports_view_check_option(self) -> bool:
return True

def supports_cascade_view(self) -> bool:
return True
# Firebird's DROP VIEW has no CASCADE clause.
return False

def supports_trigger(self) -> bool:
return True
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# src/rhosocial/activerecord/backend/impl/firebird/expression/__init__.py
"""Firebird-specific expression types."""

from .column import FirebirdColumnDefinition, FirebirdColumnOptions
from .create_table import FirebirdCreateTableExpression
from .alter_table import (
SetGenerated,
RestartIdentity,
Expand Down Expand Up @@ -31,6 +33,7 @@
FirebirdCreateUserExpression,
FirebirdDomainAlterMode,
FirebirdDropDomainExpression,
FirebirdSetDomainDataTypeAction,
FirebirdDropExceptionExpression,
FirebirdDropExternalFunctionExpression,
FirebirdDropPackageExpression,
Expand All @@ -53,13 +56,18 @@
FirebirdFloatType,
FirebirdDoubleType,
FirebirdBlobSubType,
FirebirdCharType,
FirebirdVarCharType,
FirebirdTimeStampTzType,
FirebirdTimeTzType,
FirebirdTimeWithoutTimeZoneType,
FirebirdDecFloatType,
FirebirdInt128Type,
)

__all__ = [
'FirebirdColumnDefinition', 'FirebirdColumnOptions',
"FirebirdCreateTableExpression",
"SetGenerated",
"RestartIdentity",
"SetIncrement",
Expand All @@ -71,8 +79,11 @@
"FirebirdFloatType",
"FirebirdDoubleType",
"FirebirdBlobSubType",
"FirebirdCharType",
"FirebirdVarCharType",
"FirebirdTimeStampTzType",
"FirebirdTimeTzType",
"FirebirdTimeWithoutTimeZoneType",
"FirebirdDecFloatType",
"FirebirdInt128Type",
"BlobColumnExpression",
Expand All @@ -84,6 +95,7 @@
"FirebirdAlterDomainExpression",
"FirebirdDomainAlterMode",
"FirebirdDropDomainExpression",
"FirebirdSetDomainDataTypeAction",
"FirebirdCreateExceptionExpression",
"FirebirdAlterExceptionExpression",
"FirebirdDropExceptionExpression",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# src/rhosocial/activerecord/backend/impl/firebird/expression/column.py
"""Firebird-specific column definition expressions.

Firebird extends the standard column definition with per-column clauses that
have no generic equivalent:

* ``COMPUTED BY (<expr>)`` — a computed (stored-expression) column.
* ``CHARACTER SET <name>`` — column character set.
* ``COLLATE <name>`` — column collation (Firebird spelling at column level).

These live on ``FirebirdColumnDefinition`` (deriving the generic
``ColumnDefinition``) and are rendered by the Firebird
``format_column_definition`` override; they are declared through
``FirebirdColumnOptions`` (deriving the generic ``ColumnOptions``).
"""

from typing import Optional, TYPE_CHECKING

from rhosocial.activerecord.backend.expression.statements import ColumnDefinition
from rhosocial.activerecord.base import ColumnOptions

if TYPE_CHECKING: # pragma: no cover
from rhosocial.activerecord.backend.dialect import SQLDialectBase


__all__ = [
"FirebirdColumnDefinition",
"FirebirdColumnOptions",
]


class FirebirdColumnDefinition(ColumnDefinition):
"""A Firebird column definition extending the generic one.

Adds Firebird-only typed attributes: ``computed_by``, ``character_set``
and ``collation``.
"""

def __init__(
self,
dialect: "SQLDialectBase",
name: str,
data_type,
constraints=None,
comment: Optional[str] = None,
generated_expression=None,
attributes=None,
*,
computed_by: Optional[str] = None,
character_set: Optional[str] = None,
collation: Optional[str] = None,
):
super().__init__(
dialect,
name,
data_type,
constraints=constraints,
comment=comment,
generated_expression=generated_expression,
attributes=attributes,
)
self.computed_by = computed_by
self.character_set = character_set
self.collation = collation


class FirebirdColumnOptions(ColumnOptions):
"""Firebird per-column options declaration."""

def __init__(
self,
*,
computed_by: Optional[str] = None,
character_set: Optional[str] = None,
collation: Optional[str] = None,
):
super().__init__()
self.computed_by = computed_by
self.character_set = character_set
self.collation = collation

def column_definition_class(self):
"""Build a ``FirebirdColumnDefinition`` for these options."""
return FirebirdColumnDefinition

def apply_to(self, column) -> None:
"""Transfer the Firebird-only fields onto the column definition."""
if not isinstance(column, FirebirdColumnDefinition):
raise TypeError(
"FirebirdColumnOptions.apply_to requires a FirebirdColumnDefinition, "
f"got {type(column).__name__}"
)
column.computed_by = self.computed_by
column.character_set = self.character_set
column.collation = self.collation
Loading
Loading