Skip to content

no-database-programmability

Keep stored SQL functions and triggers out of embedded SQL.

Why

SQL hidden in Python can bypass migration-only architecture rules. Procedural fault fixtures require extra database objects and cleanup when a declarative constraint suffices.

Fix

Prefer declarative constraints or explicit transactional application behavior where equivalent, including in test fixtures. Preserve transaction atomicity and database-enforced integrity; use an exact SARJ470 suppression for an approved database-owned invariant or compatibility exception.

Examples

Before — flagged Procedural fault fixture
tests/fakes/batch_fault.py
async def install_fault(conn):
await conn.execute(
"CREATE TRIGGER fail_counter BEFORE UPDATE ON batch EXECUTE FUNCTION fail_counter()"
)
After — preferred Declarative failure in a disposable database
tests/fakes/batch_fault.py
async def install_fault(conn):
await conn.execute(
"ALTER TABLE batch ADD CONSTRAINT fail_counter CHECK (false) NOT VALID"
)