SQLRules Compiler Architecture¶
Purpose¶
The SQLRules compiler transforms a constrained Pydantic model into a dictionary of SQLAlchemy WHERE expressions.
The compiler never connects to a database, executes SQL, or generates SQL strings. It only produces SQLAlchemy expression objects.
Pipeline¶
Pydantic Model
│
▼
Model Introspection
│
▼
Field Extraction
│
▼
Constraint Extraction
│
▼
Intermediate Representation (IR)
│
▼
Constraint Translators
│
▼
SQLAlchemy Expressions
│
▼
Rule Dictionary
Stage 1 – Model Introspection¶
Inputs: - Pydantic BaseModel subclass
Responsibilities: - Validate the input model. - Enumerate model fields. - Preserve declaration order.
Output: - Iterable of field definitions (later cached as ModelIR).
Table / column binding happens in Phase 2 (bind), not Stage 1.
Stage 2 – Field Extraction¶
For each field:
Python type
Optionality
Metadata
Field name
Alias (optional)
Unsupported field definitions fail fast.
Stage 3 – Constraint Extraction¶
Read only constraints that have deterministic SQL equivalents.
Examples: - gt - ge - lt - le - multiple_of - min_length - max_length - Literal - Enum
pattern is normalized into IR (operator="pattern") but has no
portable core translator.
Constraints without SQL equivalents are delegated to the unsupported-constraint policy.
Stage 4 – Intermediate Representation¶
Each constraint becomes a normalized object.
Example:
Constraint(
field="age",
operator="ge",
value=18,
)
The IR isolates compiler logic from both Pydantic and SQLAlchemy APIs.
Stage 5 – Translators¶
Each constraint is translated independently.
Examples:
ge → column >= value
gt → column > value
min_length → func.length(column) >= value
Literal → column.in_(…)
Every translator returns one SQLAlchemy expression.
Stage 6 – Rule Assembly¶
Expressions are grouped by field.
Example:
{
"age": [
users.c.age >= 18,
users.c.age <= 65,
]
}
Ordering is deterministic and follows the source model.
Error Handling¶
Modes:
raise (default)
warn
ignore
Errors include:
UnsupportedConstraintError
MissingColumnError
InvalidModelError
TranslatorError
Caching¶
Phase-1 model IR is cached in-process by model class (default on).
Cached values are immutable field descriptors and constraints — never
SQLAlchemy column objects. Disable with cache=False.
Translation remains deterministic.
Extension Points¶
Available today:
Inject a custom
TranslatorRegistryviaCompiler(registry=...)Register versioned plugins via
Compiler(plugins=[...])Override operators with
register_constraint(..., on_conflict=...)Optional
dialect=hint onCompilationContext
Reserved for later:
Compiler pass plugins
Entry-point / auto-discovery
Design Principles¶
Pure function compiler
No database dependency
Deterministic output
SQLAlchemy-first
Easy to test
Easy to extend
Fail fast on unsupported semantics
Public Contract¶
rules = sqlrules.compile(UserFilter, users)
Returns:
dict[str, list]
where each value is a list of SQLAlchemy boolean expressions suitable
for passing directly to .where(*expressions).