SQLRULES_INTEGRATION.md¶
SQLRules Integration¶
Purpose¶
This document defines how RowGuard integrates with SQLRules.
The relationship between the two libraries is intentionally layered:
SQLRules compiles supported Pydantic constraints into SQLAlchemy WHERE expressions.
RowGuard executes queries, validates returned rows with Pydantic, and manages rejected rows.
Neither project should absorb the responsibilities of the other.
Architecture¶
Pydantic Model
│
▼
SQLRules
Compile SQL-safe constraints
│
▼
WHERE Expressions
│
▼
RowGuard Query Builder
│
▼
SQLAlchemy Statement
│
▼
Database
│
▼
RowGuard
Row Adaptation
│
▼
Pydantic Validation
│
├── Valid Models
└── Rejected Rows
SQLRules performs compile-time work.
RowGuard performs runtime work.
Responsibilities¶
SQLRules¶
Responsible for:
Inspecting Pydantic metadata
Extracting supported constraints
Producing SQLAlchemy boolean expressions
Translator plugins
Dialect-aware SQL compilation
Not responsible for:
Query execution
Database sessions
Row validation
Rejection handling
RowGuard¶
Responsible for:
Building SQLAlchemy statements
Executing queries
Streaming
Row adaptation
Pydantic validation
Rejection policies
Diagnostics
Statistics
Not responsible for:
Compiling constraints
Translating Pydantic metadata into SQL
Integration Contract¶
RowGuard should treat SQLRules as a dependency with a narrow public interface.
Preferred interaction:
rules = sqlrules.compile(
model=UserRead,
table=users,
)
expressions = sqlrules.where(rules)
RowGuard should never depend on SQLRules internals such as compiler phases, intermediate representations, or translator implementations.
Query Construction¶
Typical flow:
stmt = select(users)
rules = sqlrules.compile(UserRead, users)
stmt = stmt.where(*sqlrules.where(rules))
Additional user-supplied filters are combined naturally:
stmt = stmt.where(users.c.active == True)
The ordering of WHERE expressions should not affect semantics.
SQL Pushdown¶
SQLRules should push only constraints that have deterministic SQL equivalents.
Examples:
gt
ge
lt
le
multiple_of
min_length
max_length
Literal
Enum
Constraints without safe SQL equivalents remain the responsibility of Pydantic validation.
Validation Remains Required¶
Even after SQL pushdown, RowGuard always validates returned rows.
Reasons include:
Database drift
Trigger behavior
Views
Joins
Database-specific semantics
Constraints not representable in SQL
SQLRules reduces candidate rows.
RowGuard establishes trust.
Unsupported Constraints¶
SQLRules may report unsupported constraints.
Depending on compiler configuration:
raise
warn
ignore
RowGuard should surface these diagnostics but continue to validate rows using Pydantic.
An unsupported SQL constraint does not imply that validation should be skipped.
Diagnostics¶
RowGuard should preserve SQLRules diagnostics.
Examples:
constraints translated
constraints skipped
translator warnings
dialect decisions
Diagnostics become part of the final QueryResult.
Caching¶
SQLRules may cache compilation work.
RowGuard should treat compiled rule dictionaries as immutable.
Possible optimization:
compiled = sqlrules.compile(UserRead, users)
rowguard.select(
session=session,
statement=stmt,
compiled_rules=compiled,
)
This avoids recompilation when repeatedly querying the same model.
Execution Plans¶
Execution plans should reference compiled SQLRules output rather than the original Pydantic model whenever practical.
Example:
ExecutionPlan
statement
compiled_rules
model
reject_policy
This separates planning from execution.
Version Compatibility¶
Initial compatibility target:
SQLRules 0.x
RowGuard 0.x
Before RowGuard 1.0, SQLRules should reach a stable public API.
RowGuard should depend only on documented SQLRules entry points.
Error Handling¶
Compilation failures remain SQLRules errors.
Execution failures remain RowGuard errors.
Example:
SQLRulesError
UnsupportedConstraintError
RowGuardError
QueryExecutionError
ValidationFailure
Errors should not be wrapped unnecessarily.
Optional Usage¶
RowGuard should allow SQLRules integration to be disabled.
Example:
rowguard.select(
session=session,
table=users,
model=UserRead,
use_sqlrules=False,
)
Behavior:
No SQL pushdown
Full Pydantic validation
Identical rejection handling
This supports debugging and performance comparisons.
Future Integration¶
Future SQLRules features may automatically benefit RowGuard:
new translators
new dialect plugins
performance improvements
compiler caching
RowGuard should gain these benefits without architectural changes.
Testing Requirements¶
Integration tests should verify:
SQLRules expressions are applied
Additional WHERE clauses compose correctly
Unsupported constraints still validate in Pydantic
Diagnostics propagate
Cached compilations behave identically
SQLRules disabled mode
Multiple dialects
Design Principles¶
Clear separation of responsibilities.
Depend only on SQLRules’ public API.
SQL pushdown is an optimization, not a replacement for validation.
Preserve diagnostics across library boundaries.
Keep the integration thin, explicit, and testable.