SQLRules Type Support Matrix¶
Purpose¶
This document defines which Python and Pydantic field types SQLRules supports and how each type is translated into SQLAlchemy WHERE expressions.
SQLRules only supports types whose constraints can be translated deterministically into SQLAlchemy boolean expressions.
Guiding Principles¶
Prefer deterministic translations.
Never guess SQL semantics.
Unsupported types always fail fast (they are not softened by
on_unsupported).Type support is independent of database dialect whenever possible.
Supported types (1.0)¶
Python Type Supported Notes
bool ✅ Equality / literal constraints only
int ✅ Numeric comparisons
float ✅ Numeric comparisons
Decimal ✅ Numeric comparisons (max_digits /
decimal_places still unsupported)
str ✅ Length constraints, Literal, Enum;
pattern IR only
date ✅ Range comparisons
datetime ✅ Range comparisons (no TZ conversion)
time ✅ Range comparisons
Enum ✅ Translated to IN (...)
Literal ✅ Translated to IN (...)
UUID ✅ Literal / Enum only
timedelta ❌ No deterministic SQL mapping yet
Numeric Types¶
Supported constraints:
gt
ge
lt
le
multiple_of
Example:
age: Annotated[int, Field(ge=18, le=65)]
↓
[
users.c.age >= 18,
users.c.age <= 65,
]
Strings¶
Supported constraints:
min_length
max_length
IR only (no portable translator in core):
pattern
Planned (later milestones):
starts_with
ends_with
contains
Example:
name: Annotated[str, Field(min_length=2)]
↓
func.length(users.c.name) >= 2
Boolean¶
Boolean fields generally do not produce rules unless constrained through
Literal or similar constructs.
Examples:
Literal[True]
Literal[False]
↓
column.in_([True])
Date, DateTime, and Time¶
Supported operators:
gt
ge
lt
le
multiple_of is rejected for temporal types.
No timezone conversion is performed by SQLRules. Aware and naive
datetime values are passed through unchanged.
UUID¶
Supported:
Unconstrained fields (skipped; no column required)
Literal[...]/Enum
Rejected:
length constraints
numeric comparisons
Enum¶
Example:
class Status(Enum):
ACTIVE = "ACTIVE"
DISABLED = "DISABLED"
↓
column.in_(["ACTIVE", "DISABLED"])
Literal¶
Example:
Literal["A", "B", "C"]
↓
column.in_(["A", "B", "C"])
Optional Types¶
Optional[T] / T | None is supported.
By itself, optionality does not generate SQL rules unless
emit_type_checks=True, in which case TypeSpec.allow_none is set and
dialect translators emit (column IS NULL) OR <type predicate>.
Type checks (emit_type_checks)¶
Opt-in: Compiler(emit_type_checks=True) or
sqlrules.compile(..., emit_type_checks=True).
When enabled, supported scalar annotations emit a type_check
constraint with TypeSpec(python_type, strict, allow_none):
Strictness from
Field(strict=…),Strict(), thenmodel_config = ConfigDict(strict=…)Literal/Enum/list/dictdo not gettype_check(domain already covered or marker-only)
Core extracts IR only — there is no portable translator. Install a
dialect plugin (or register a custom type_check translator).
Approximations are dialect-specific and intentionally incomplete versus
full Pydantic coercion. Unsupported (type, strict) pairs raise
UnsupportedConstraintError rather than guessing. See dialect plugin
READMEs for the matrix.
Example:
from sqlrules import Compiler
from sqlrules_postgresql import PostgresPlugin
class Filter(BaseModel):
age: int
compiler = Compiler(plugins=[PostgresPlugin()], emit_type_checks=True)
rules = compiler.compile(Filter, users)
Containers¶
Type Status
list ✅ Marker-driven fields only (e.g. ArrayContains)
dict ✅ Marker-driven fields only (e.g. JsonContains)
tuple ❌
set ❌
Portable constraints (gt, min_length, pattern, …) on list / dict
still raise. Use sqlrules.markers with a dialect plugin.
Unsupported Pydantic Features¶
The following are intentionally unsupported:
validators
field_validator
model_validator
computed_field
serializer hooks
arbitrary custom metadata (except
ConstraintMarkerinstances)custom validation functions
max_digits/decimal_places(deferred; no deterministic WHERE map)
These features require runtime execution or lack portable SQL semantics.
Post-1.0 expansion¶
Historical 0.2–0.4 delivery notes live in MILESTONES, not here. This matrix is the 1.0 contract.
Planned after 1.0 (not available yet) only if SQL semantics are
deterministic: starts_with / ends_with / contains, decimal precision
constraints. See the project roadmap.
Support should expand only when the resulting SQL semantics are well-defined across supported SQLAlchemy backends.
Compatibility¶
Target versions:
Python 3.10+
Pydantic v2
SQLAlchemy 2.x
Design Principles¶
Explicit support matrix
Stable translations
Backend-neutral semantics
Fail fast for unsupported types
Predictable compiler output