Skip to main content

Compile constrained Pydantic models into SQLAlchemy WHERE-rule dictionaries.

Project description

SQLRules

CI PyPI Documentation License

Compile Pydantic Field constraint metadata into SQLAlchemy WHERE expressions.

SQLRules reads constraints declared on a model (Field(ge=18), min_length, Literal, …) and turns them into SQLAlchemy boolean expressions. It depends on Pydantic v2 and SQLAlchemy 2.x, performs no database I/O, and needs a dialect plugin only for non-portable operators (regex, JSON, arrays, …).

SQLRules does THIS (constraint metadata → expressions):
  Field(ge=18)           →  column >= 18

It does NOT do this (instance values → predicates):
  UserFilter(age=25)     →  column == 25   # not what sqlrules.compile does

Not an ORM, validator, or query builder: constraint metadata → expressions, nothing else.

In 30 seconds:

pip install "sqlrules>=1,<2"
from typing import Annotated

from pydantic import BaseModel, Field
from sqlalchemy import Column, Integer, MetaData, String, Table

import sqlrules

users = Table(
    "users",
    MetaData(),
    Column("age", Integer),
    Column("name", String),
)

class UserFilter(BaseModel):
    age: Annotated[int, Field(ge=18, le=65)]
    name: Annotated[str, Field(min_length=2)]

rules = sqlrules.compile(UserFilter, users)
# {
#     "age": [users.c.age >= 18, users.c.age <= 65],
#     "name": [length(users.c.name) >= 2],
# }

stmt = users.select().where(*sqlrules.where(rules))

pattern needs a plugin. Core compiles comparisons, lengths, Literal, and Enum. Regex / JSON / arrays require a dialect package (see below). Prefer static patterns; untrusted regex can be expensive (see SECURITY).

Full walkthrough: Getting started · Runnable scripts: examples/.

Latest sqlrules on PyPI · Changelog
Docs sqlrules.readthedocs.io
Python 3.10+ (Pydantic v2, SQLAlchemy 2.x)

Choose your path

Path Start here
Not sure? Start here
Compile constraints Getting started
Dialect plugins Plugin system
Evaluate fit Design philosophy
Contribute Contributing

Install

Requires Python 3.10+, Pydantic v2, and SQLAlchemy 2.x (pulled in transitively).

From PyPI (1.0+)

pip install "sqlrules>=1,<2"
# or: uv add "sqlrules>=1,<2"

Confirm the installed version is 1.0.0 or newer (pip show sqlrules). If PyPI still serves an older release, or dialect packages are missing, use from source below.

Optional dialect plugins (same major line as core):

pip install "sqlrules-postgresql>=1,<2"   # ~ / ~*, JSONB, ARRAY, range
pip install "sqlrules-sqlite>=1,<2"       # REGEXP helper + JSON
pip install "sqlrules-mysql>=1,<2"        # REGEXP, JSON, full-text
pip install "sqlrules-mssql>=1,<2"        # JSON + LEN string ops

Extras are equivalent shortcuts (pull the matching plugin package):

pip install "sqlrules[postgresql]"
pip install "sqlrules[dialects]"   # all four

SQLite pattern emits REGEXP — call sqlrules_sqlite.register_regexp(connection) on each connection before executing queries.

From source (repo / pre-PyPI)

git clone https://github.com/eddiethedean/sqlrules.git
cd sqlrules
python -m venv .venv && source .venv/bin/activate
make install   # editable core + all four dialect plugins

Or install built wheels from a checkout that already ran make dist:

pip install dist/sqlrules-*.whl dist-plugins/sqlrules_*.whl

End users should prefer PyPI once 1.0+ is published. You do not need to clone this repo or install from packages/ for normal application use.

Plugin example

from typing import Annotated, Any

from pydantic import BaseModel, Field
from sqlalchemy import Column, MetaData, String, Table
from sqlalchemy.dialects.postgresql import JSONB

import sqlrules
from sqlrules import Compiler, JsonContains
from sqlrules_postgresql import PostgresPlugin

table = Table(
    "rows",
    MetaData(),
    Column("name", String),
    Column("meta", JSONB),
)

class RowFilter(BaseModel):
    name: Annotated[str, Field(pattern=r"^A")]
    meta: Annotated[dict[str, Any], JsonContains({"active": True})]

# dialect= is a hint for custom translators only — it does not load plugins.
compiler = Compiler(plugins=[PostgresPlugin()], dialect="postgresql")
rules = compiler.compile(RowFilter, table)
stmt = table.select().where(*sqlrules.where(rules))

See examples/postgresql_pattern.py.

Common failures

Symptom Fix
UnsupportedConstraintError on pattern Install a dialect plugin and pass it to Compiler(plugins=[...])
NameError: sqlrules after from sqlrules import Compiler Also import sqlrules (or import where)
pip installs < 1.0 / dialect package 404 Install from source until PyPI has 1.0+
SQLite REGEXP errors at execute time Call register_regexp(connection)

More: Troubleshooting.

Supported constraints (1.0)

Constraint SQLAlchemy expression
gt / ge / lt / le column > / >= / < / <= value
multiple_of column % value == 0
min_length / max_length func.length(column) >= / <= value
Literal[...] column.in_(...)
Enum column.in_(...)

pattern is extracted into IR (PatternSpec) but has no portable core translator. Install a dialect plugin or register a custom translator.

With emit_type_checks=True, supported scalar annotations also emit type_check (TypeSpec) IR — likewise plugin-translated only.

Dialect markers (JsonContains, ArrayContains, RangeContains, FullTextMatch, …) are re-exported from sqlrules (and live in sqlrules.markers); they require a dialect plugin.

Unsupported constraints raise UnsupportedConstraintError by default. Use on_unsupported="warn" or "ignore" for unknown operators. Unconstrained fields with supported types are omitted from the rules dict. Fields with unsupported type annotations always raise — even if unconstrained.

When not to use SQLRules

Skip SQLRules when:

  • You need request/instance values as WHERE predicates (UserFilter(age=25)age = 25). That is a query/filter library, not this compiler.
  • You have a few static expressions and will never share constraint metadata with a Pydantic model — write SQLAlchemy clauses directly.
  • You need portable regex with core alone — pattern requires a dialect plugin or custom translator.
  • You expect dialect="postgresql" to load plugins — it is a hint only; pass plugins=[...] explicitly.
  • You want a general-purpose query builder, ORM, or SQL string generator.

SQLRules pays off when the Pydantic model is the shared source of truth for constraint metadata across many fields or dialects.

Public API

sqlrules.compile(model, table, *, column_map=None, on_unsupported="raise", cache=True, emit_type_checks=False)
sqlrules.where(rules)           # prefer this to flatten expressions
sqlrules.flatten(rules)         # alias of where()
sqlrules.clear_model_cache()    # drop process-wide Phase-1 IR cache

compiler = sqlrules.Compiler(
    plugins=[...],           # optional SQLRulesPlugin instances
    on_conflict="raise",     # raise | replace | ignore
    dialect=None,            # hint only — does not load plugins
    on_unsupported="raise",
    cache=True,
    emit_type_checks=False,  # opt-in type_check IR (needs plugin translator)
)

Non-goals

SQLRules is not an ORM, validator, query builder, SQL string generator, migration tool, or database client. It only compiles supported Pydantic constraints into SQLAlchemy expressions. See design philosophy and non-goals.

Documentation

Full site: sqlrules.readthedocs.io

Topic Link
Getting started Guide
How-to guides ORM binding · Markers
Examples examples/
Spec & constraints SPEC · CONSTRAINTS
Plugins Plugin system
API reference Reference hub
FAQ FAQ
Support Support & compatibility
Roadmap Roadmap
Changelog Changelog
pip install -e ".[docs]"
make docs

Repo map

Path Who cares
PyPI sqlrules / sqlrules-* Application users — install these
examples/ Copy-paste runnable scripts
docs/ Spec, guides, architecture (also on Read the Docs)
packages/ Contributors — official dialect plugin sources
src/sqlrules/ Core library source
benchmarks/ Optional local compile benchmarks

Development (contributors)

make install
make check    # lint, types, tests, docs (matches most of CI)
make dist     # packaging smoke — required before release / to match full CI

See CONTRIBUTING.md. Maintainers: RELEASING.md.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sqlrules-1.0.0.tar.gz (80.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sqlrules-1.0.0-py3-none-any.whl (26.4 kB view details)

Uploaded Python 3

File details

Details for the file sqlrules-1.0.0.tar.gz.

File metadata

  • Download URL: sqlrules-1.0.0.tar.gz
  • Upload date:
  • Size: 80.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for sqlrules-1.0.0.tar.gz
Algorithm Hash digest
SHA256 3478c80e08234037a76ff2af75b55dba7ddc14ff2dcedd30a4a7c72d14a3ebd3
MD5 c541f1935a0e2a4bbd21509ca02f4dc9
BLAKE2b-256 991d9d170453151f1514df4f1862d5fdfe676d03ca0ad1c409a696354569f912

See more details on using hashes here.

File details

Details for the file sqlrules-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: sqlrules-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 26.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for sqlrules-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f24b84050c7517ebd7e6d60f7bfe56cfb97eca2473210475d18229cbabc2f2ba
MD5 596aa984afacd345b015e83261eb11fa
BLAKE2b-256 26eff551dced007653aac77c74024bf00102e6f9dea313985e1591e6bbba3c9e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page