SQLRules Translator Architecture¶
Purpose¶
Translators convert a single normalized constraint from the compiler’s Intermediate Representation (IR) into one or more SQLAlchemy boolean expressions.
A translator has one responsibility:
Constraint IR → SQLAlchemy Expression
It does not inspect Pydantic models, execute SQL, or perform validation.
Translator Pipeline¶
Constraint IR
│
▼
Translator Registry
│
▼
Constraint Translator
│
▼
SQLAlchemy Expression
Translator Interface¶
Every translator implements a common interface.
translate(
constraint,
column,
context,
) -> ColumnElement[bool]
Parameters:
constraint— normalized IR objectcolumn— SQLAlchemy columncontext— compiler settings, dialect hints, registry
Returns:
SQLAlchemy boolean expression
Built-in Translators¶
Numeric¶
Constraint Translation
gt column > value
ge column >= value
lt column < value
le column <= value
multiple_of column % value == 0
String¶
Constraint Translation
min_length func.length(column) >= value
max_length func.length(column) <= value
pattern (1.0): extracted as PatternSpec in core IR; no portable
core translator. Install sqlrules-postgresql / sqlrules-sqlite /
sqlrules-mysql, or register a custom translator. SQL Server
(sqlrules-mssql) does not register pattern.
Not in 1.0: starts_with, ends_with, contains.
Literal¶
Literal["A", "B"]
↓
column.in_(["A", "B"])
Enum¶
class Status(Enum):
ACTIVE = "ACTIVE"
DISABLED = "DISABLED"
↓
column.in_(["ACTIVE", "DISABLED"])
Date / DateTime / Time¶
Supported operators:
gt
ge
lt
le
Translation is identical to numeric comparison. multiple_of is rejected
for temporal types.
Translator Registry¶
The compiler owns a registry mapping IR operators to translators.
"gt" -> GreaterThanTranslator
"ge" -> GreaterEqualTranslator
"lt" -> LessThanTranslator
"le" -> LessEqualTranslator
"min_length" -> MinLengthTranslator
"max_length" -> MaxLengthTranslator
"literal" -> LiteralTranslator
"enum" -> EnumTranslator
The registry is responsible for dispatch only.
Design Rules¶
Each translator should:
perform exactly one translation
be deterministic
never mutate compiler state
never inspect unrelated fields
never execute SQL
Unsupported Constraints¶
If no translator exists, behavior depends on the compiler policy.
raise
warn
ignore
Custom Translators¶
Applications may register additional translators on a
TranslatorRegistry and pass it to Compiler(registry=...), or use the
plugin API:
from sqlrules import Compiler, PLUGIN_API_VERSION
from sqlrules.constraints import pattern_text
from sqlrules.translators import default_registry
registry = default_registry()
registry.register_constraint(
"pattern",
lambda constraint, column, context: column.op("~")(
pattern_text(constraint.value)[0]
),
on_conflict="replace",
)
compiler = Compiler(registry=registry)
class PatternPlugin:
name = "pattern"
api_version = PLUGIN_API_VERSION
def register(self, registry):
registry.register_constraint(
"pattern",
lambda c, col, ctx: col.op("~")(pattern_text(c.value)[0]),
on_conflict="replace",
)
compiler = Compiler(plugins=[PatternPlugin()])
Dialect Overrides¶
Some SQL constructs differ by backend. Official packages:
sqlrules-postgresql—~/~*, JSONB, ARRAY, rangesqlrules-sqlite— REGEXP helper + JSONsqlrules-mysql— REGEXP, JSON, full-textsqlrules-mssql— JSON +LENlength overrides
A dialect plugin overrides the default translator while preserving the same IR. See PLUGIN_SYSTEM.md and DIALECT_SUPPORT.md.
Testing Requirements¶
Every translator should have tests for:
successful translation
invalid input
edge values
deterministic output
SQLAlchemy expression type
One translator should never rely on another translator’s implementation.
Design Principles¶
Small, composable classes or functions
One constraint per translator
Pure transformations
Easy to extend
Easy to unit test
Backend-independent IR