Skip to content

refactor: replace Any backend with typed engine#27

Merged
0x054 merged 7 commits intomainfrom
refactor/multi-db-backend
Apr 25, 2026
Merged

refactor: replace Any backend with typed engine#27
0x054 merged 7 commits intomainfrom
refactor/multi-db-backend

Conversation

@0x054
Copy link
Copy Markdown
Contributor

@0x054 0x054 commented Apr 24, 2026

Description

Ferro now executes through explicit typed SQLite and Postgres pools instead of the sqlx::Any runtime path. This removes backend ambiguity from the Rust core and makes schema metadata the shared contract for runtime DDL, Alembic metadata, query casting, hydration, and backend-matrix testing.

This also fixes and hardens the class of bridge-boundary bugs exposed by UUID many-to-many relationships: query payloads now serialize non-JSON-native values safely, direct M2M operations keep typed IDs for Rust/Postgres binding, and new tests cover the places where the same logical value crosses Python JSON, PyO3, and backend-specific SQL boundaries.

Postgres coverage no longer depends on Supabase. The test harness can now run against an ephemeral local Postgres server via pytest-postgresql, while still accepting FERRO_POSTGRES_URL or the legacy FERRO_SUPABASE_URL for externally managed databases.

Changes

  • Replace legacy sqlx::Any execution with typed EngineHandle / EngineConnection paths for direct operations and transactions.
  • Normalize schema metadata for nullability, primary keys, UUIDs, Decimal, JSON, temporal types, and many-to-many join tables.
  • Tighten backend-specific SQL generation and hydration for Postgres JSON, Decimal, UUID, enum, and temporal values while preserving SQLite behavior.
  • Serialize non-JSON-native query payload values at the query boundary without mutating live relationship context.
  • Add Postgres UUID casts for M2M join-table add/remove/clear operations.
  • Expand M2M coverage across int/UUID PKs, source and reverse traversal, add/all/count/remove/clear, transactions, SQLite, and Postgres.
  • Add fast query serialization contract tests and a static guardrail against raw json.dumps(query_def) in query methods.
  • Add Postgres JSON/Decimal update and hydration regressions, which also fixed JSON/Decimal-only SELECT text-cast activation.
  • Add pytest-postgresql to the dev and CI test dependency groups for local ephemeral Postgres coverage.
  • Generalize Postgres test configuration to prefer FERRO_POSTGRES_URL, retain FERRO_SUPABASE_URL as a fallback, and support FERRO_POSTGRES_PROVIDER=local to force the local provider.
  • Document bridge-boundary user repro conventions and local Postgres matrix setup in the testing guide.

Bridge and Schema Impact

  • No Rust/Python bridge changes
  • Python model/schema changed
  • Rust core or SQL generation changed
  • src/ferro/_core.pyi updated (if needed)
  • Integration test added first for new behavior

Migration / Breaking Changes

  • No breaking changes
  • Breaking changes included (details below)

Documentation and Changelog

  • No docs update needed
  • Docs updated (README/docs/inline docs)
  • Changelog entry needed

Test Plan

  • cargo test (15 passed)
  • uv run pytest --db-backends=sqlite,postgres tests/test_auto_migrate.py::test_uuid_m2m_relationship_query_serializes_source_id -q (2 passed)
  • uv run pytest --db-backends=postgres -q (221 passed, 5 skipped)
  • uv run pytest --db-backends=sqlite,postgres -q (326 passed, 9 skipped)
  • uv run pytest tests/test_db_backends.py -q (8 passed)
  • FERRO_POSTGRES_PROVIDER=local uv run pytest tests/test_auto_migrate.py::test_uuid_m2m_relationship_query_serializes_source_id tests/test_structural_types.py::test_postgres_json_and_decimal_updates_keep_typed_hydration --db-backends=postgres -q (2 passed)
  • Lints clean on edited files

Related Issues

Made with Cursor

0x054 added 6 commits April 25, 2026 13:09
Move runtime execution onto typed SQLite/Postgres pools and normalize schema metadata so runtime DDL, Alembic metadata, and backend-specific value handling stay in sync.

Made-with: Cursor
Normalize non-JSON-native query payload values at the serialization boundary while preserving typed M2M IDs for direct Rust operations.

Made-with: Cursor
Add relationship, query serialization, Postgres typed-cast, and static contract coverage so Python/Rust boundary regressions surface before user repros.

Made-with: Cursor
Made-with: Cursor
@0x054 0x054 force-pushed the refactor/multi-db-backend branch from ac2fdec to d8d376d Compare April 25, 2026 17:09
@0x054 0x054 merged commit fa1c003 into main Apr 25, 2026
7 checks passed
@0x054 0x054 deleted the refactor/multi-db-backend branch April 25, 2026 18:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant