sqlrules 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sqlrules-0.3.0 → sqlrules-0.4.0}/CHANGELOG.md +25 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/CONTRIBUTING.md +16 -10
- {sqlrules-0.3.0 → sqlrules-0.4.0}/PKG-INFO +23 -11
- {sqlrules-0.3.0 → sqlrules-0.4.0}/README.md +22 -10
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/API.md +8 -4
- sqlrules-0.4.0/docs/CONSTRAINTS.md +55 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/DIALECT_SUPPORT.md +41 -57
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/MILESTONES.md +10 -4
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/PLUGIN_SYSTEM.md +21 -12
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/ROADMAP.md +2 -1
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/SPEC.md +9 -6
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/TRANSLATORS.md +9 -4
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/TYPE_SUPPORT.md +9 -7
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/index.md +1 -1
- sqlrules-0.4.0/packages/sqlrules-mssql/README.md +37 -0
- sqlrules-0.4.0/packages/sqlrules-mssql/pyproject.toml +28 -0
- sqlrules-0.4.0/packages/sqlrules-mssql/src/sqlrules_mssql/__init__.py +52 -0
- sqlrules-0.4.0/packages/sqlrules-mssql/src/sqlrules_mssql/json.py +70 -0
- sqlrules-0.4.0/packages/sqlrules-mssql/src/sqlrules_mssql/length.py +26 -0
- sqlrules-0.4.0/packages/sqlrules-mssql/tests/test_mssql_plugin.py +52 -0
- sqlrules-0.4.0/packages/sqlrules-mysql/README.md +36 -0
- sqlrules-0.4.0/packages/sqlrules-mysql/pyproject.toml +28 -0
- sqlrules-0.4.0/packages/sqlrules-mysql/src/sqlrules_mysql/__init__.py +39 -0
- sqlrules-0.4.0/packages/sqlrules-mysql/src/sqlrules_mysql/fulltext.py +20 -0
- sqlrules-0.4.0/packages/sqlrules-mysql/src/sqlrules_mysql/json.py +35 -0
- sqlrules-0.4.0/packages/sqlrules-mysql/src/sqlrules_mysql/pattern.py +22 -0
- sqlrules-0.4.0/packages/sqlrules-mysql/tests/test_mysql_plugin.py +45 -0
- sqlrules-0.4.0/packages/sqlrules-postgresql/README.md +53 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/packages/sqlrules-postgresql/pyproject.toml +3 -3
- sqlrules-0.4.0/packages/sqlrules-postgresql/src/sqlrules_postgresql/__init__.py +46 -0
- sqlrules-0.4.0/packages/sqlrules-postgresql/src/sqlrules_postgresql/array.py +25 -0
- sqlrules-0.4.0/packages/sqlrules-postgresql/src/sqlrules_postgresql/jsonb.py +25 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/packages/sqlrules-postgresql/src/sqlrules_postgresql/pattern.py +5 -2
- sqlrules-0.4.0/packages/sqlrules-postgresql/src/sqlrules_postgresql/range.py +25 -0
- sqlrules-0.4.0/packages/sqlrules-postgresql/tests/test_postgres_plugin.py +88 -0
- sqlrules-0.4.0/packages/sqlrules-sqlite/README.md +53 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/packages/sqlrules-sqlite/pyproject.toml +3 -3
- {sqlrules-0.3.0 → sqlrules-0.4.0}/packages/sqlrules-sqlite/src/sqlrules_sqlite/__init__.py +19 -4
- sqlrules-0.4.0/packages/sqlrules-sqlite/src/sqlrules_sqlite/json.py +58 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/packages/sqlrules-sqlite/src/sqlrules_sqlite/pattern.py +7 -1
- sqlrules-0.4.0/packages/sqlrules-sqlite/src/sqlrules_sqlite/regexp.py +30 -0
- sqlrules-0.4.0/packages/sqlrules-sqlite/tests/test_sqlite_plugin.py +72 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/pyproject.toml +1 -1
- {sqlrules-0.3.0 → sqlrules-0.4.0}/src/sqlrules/__init__.py +21 -1
- {sqlrules-0.3.0 → sqlrules-0.4.0}/src/sqlrules/conformance.py +19 -5
- {sqlrules-0.3.0 → sqlrules-0.4.0}/src/sqlrules/constraints.py +61 -11
- {sqlrules-0.3.0 → sqlrules-0.4.0}/src/sqlrules/ir.py +8 -0
- sqlrules-0.4.0/src/sqlrules/markers.py +86 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/tests/test_correctness.py +12 -3
- {sqlrules-0.3.0 → sqlrules-0.4.0}/tests/test_errors.py +1 -1
- {sqlrules-0.3.0 → sqlrules-0.4.0}/tests/test_plugins.py +7 -6
- {sqlrules-0.3.0 → sqlrules-0.4.0}/tests/test_v01_features.py +2 -2
- {sqlrules-0.3.0 → sqlrules-0.4.0}/tests/test_v02_features.py +24 -6
- sqlrules-0.4.0/tests/test_v04_markers.py +101 -0
- sqlrules-0.3.0/docs/CONSTRAINTS.md +0 -42
- sqlrules-0.3.0/packages/sqlrules-postgresql/README.md +0 -16
- sqlrules-0.3.0/packages/sqlrules-postgresql/src/sqlrules_postgresql/__init__.py +0 -28
- sqlrules-0.3.0/packages/sqlrules-postgresql/tests/test_postgres_plugin.py +0 -33
- sqlrules-0.3.0/packages/sqlrules-sqlite/README.md +0 -19
- sqlrules-0.3.0/packages/sqlrules-sqlite/tests/test_sqlite_plugin.py +0 -33
- {sqlrules-0.3.0 → sqlrules-0.4.0}/.gitignore +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/LICENSE +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/benchmarks/__init__.py +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/benchmarks/bench_compile.py +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/ARCHITECTURE.md +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/COMPILER.md +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/CONTRIBUTING.md +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/DESIGN_DECISIONS.md +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/ERRORS.md +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/INTERNAL_API.md +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/NON_GOALS.md +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/PERFORMANCE.md +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/PHILOSOPHY.md +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/TESTING.md +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/docs/VISION.md +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/src/sqlrules/cache.py +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/src/sqlrules/columns.py +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/src/sqlrules/compiler.py +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/src/sqlrules/errors.py +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/src/sqlrules/inspectors.py +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/src/sqlrules/plugins.py +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/src/sqlrules/py.typed +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/src/sqlrules/translators.py +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/tests/conftest.py +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/tests/test_coverage_edges.py +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/tests/test_helpers.py +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/tests/test_literal_enum.py +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/tests/test_numeric_constraints.py +0 -0
- {sqlrules-0.3.0 → sqlrules-0.4.0}/tests/test_string_constraints.py +0 -0
|
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0/).
|
|
7
7
|
|
|
8
|
+
## [0.4.0] - 2026-07-10
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `ConstraintMarker` protocol and shared markers in `sqlrules.markers`:
|
|
13
|
+
`JsonContains`, `JsonHasKey`, `ArrayContains`, `ArrayOverlap`,
|
|
14
|
+
`RangeContains`, `RangeOverlap`, `FullTextMatch`
|
|
15
|
+
- `PatternSpec` IR value for `pattern` (preserves `ignore_case` from
|
|
16
|
+
`re.Pattern` flags); `pattern_text()` helper for plugins
|
|
17
|
+
- `list` / `dict` annotations for marker-driven fields (portable
|
|
18
|
+
constraints on containers still raise)
|
|
19
|
+
- Expanded dialect plugins:
|
|
20
|
+
- `sqlrules-postgresql` — `~` / `~*`, JSONB, ARRAY, range operators
|
|
21
|
+
- `sqlrules-sqlite` — `register_regexp()`, flag-aware REGEXP, JSON helpers
|
|
22
|
+
- `sqlrules-mysql` — REGEXP, JSON, `fulltext_match`
|
|
23
|
+
- `sqlrules-mssql` — JSON helpers, `LEN` overrides for length constraints
|
|
24
|
+
- Conformance helpers accept optional `model` / `table` / `field` for
|
|
25
|
+
non-`pattern` operators
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- Package version bumped to 0.4.0
|
|
30
|
+
- Official plugin packages depend on `sqlrules>=0.4,<0.5`
|
|
31
|
+
|
|
8
32
|
## [0.3.0] - 2026-07-10
|
|
9
33
|
|
|
10
34
|
### Added
|
|
@@ -108,6 +132,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
108
132
|
- Docs clarify that `on_unsupported` does not soften unsupported types;
|
|
109
133
|
plugin / two-phase compile designs are marked as future
|
|
110
134
|
|
|
135
|
+
[0.4.0]: https://github.com/eddiethedean/sqlrules/releases/tag/v0.4.0
|
|
111
136
|
[0.3.0]: https://github.com/eddiethedean/sqlrules/releases/tag/v0.3.0
|
|
112
137
|
[0.2.0]: https://github.com/eddiethedean/sqlrules/releases/tag/v0.2.0
|
|
113
138
|
[0.1.0]: https://github.com/eddiethedean/sqlrules/releases/tag/v0.1.0
|
|
@@ -16,7 +16,8 @@ Thanks for helping improve SQLRules.
|
|
|
16
16
|
python -m venv .venv
|
|
17
17
|
source .venv/bin/activate
|
|
18
18
|
pip install -e ".[dev]"
|
|
19
|
-
pip install -e packages/sqlrules-postgresql -e packages/sqlrules-sqlite
|
|
19
|
+
pip install -e packages/sqlrules-postgresql -e packages/sqlrules-sqlite \
|
|
20
|
+
-e packages/sqlrules-mysql -e packages/sqlrules-mssql
|
|
20
21
|
pre-commit install
|
|
21
22
|
```
|
|
22
23
|
|
|
@@ -26,10 +27,13 @@ pre-commit install
|
|
|
26
27
|
ruff check .
|
|
27
28
|
ruff format --check .
|
|
28
29
|
mypy src/sqlrules
|
|
29
|
-
pytest tests packages/sqlrules-postgresql/tests packages/sqlrules-sqlite/tests
|
|
30
|
+
pytest tests packages/sqlrules-postgresql/tests packages/sqlrules-sqlite/tests \
|
|
31
|
+
packages/sqlrules-mysql/tests packages/sqlrules-mssql/tests
|
|
30
32
|
python -m build && twine check dist/*
|
|
31
33
|
python -m build packages/sqlrules-postgresql --outdir dist-plugins
|
|
32
34
|
python -m build packages/sqlrules-sqlite --outdir dist-plugins
|
|
35
|
+
python -m build packages/sqlrules-mysql --outdir dist-plugins
|
|
36
|
+
python -m build packages/sqlrules-mssql --outdir dist-plugins
|
|
33
37
|
twine check dist-plugins/*
|
|
34
38
|
```
|
|
35
39
|
|
|
@@ -42,7 +46,7 @@ wheel imports cleanly.
|
|
|
42
46
|
- Prefer `register_constraint(..., on_conflict=...)`.
|
|
43
47
|
- Run `sqlrules.conformance.run_basic_conformance(plugin)`.
|
|
44
48
|
- Official dialect packages live under `packages/` and should track the
|
|
45
|
-
|
|
49
|
+
core minor (`sqlrules>=0.4,<0.5` while `PLUGIN_API_VERSION == "1"`).
|
|
46
50
|
|
|
47
51
|
## Pull requests
|
|
48
52
|
|
|
@@ -58,11 +62,11 @@ Releases are published to PyPI by pushing a version tag. Before tagging:
|
|
|
58
62
|
2. Run the full check suite (`ruff`, `mypy`, `pytest`).
|
|
59
63
|
3. Ensure CI is green on `main`.
|
|
60
64
|
|
|
61
|
-
Then create and push the tag (example for 0.
|
|
65
|
+
Then create and push the tag (example for 0.4.0):
|
|
62
66
|
|
|
63
67
|
```bash
|
|
64
|
-
git tag -a v0.
|
|
65
|
-
git push origin v0.
|
|
68
|
+
git tag -a v0.4.0 -m "sqlrules 0.4.0"
|
|
69
|
+
git push origin v0.4.0
|
|
66
70
|
```
|
|
67
71
|
|
|
68
72
|
The [release workflow](.github/workflows/release.yml) runs CI, verifies the
|
|
@@ -71,10 +75,12 @@ publishes **core** `sqlrules` with `PYPI_API_TOKEN`.
|
|
|
71
75
|
|
|
72
76
|
Dialect plugin packages under `packages/` are versioned independently and
|
|
73
77
|
are **not** published by the core release workflow. Publish them separately
|
|
74
|
-
after core `0.
|
|
78
|
+
after core `0.4.0` is on PyPI (they depend on `sqlrules>=0.4,<0.5`):
|
|
75
79
|
|
|
76
80
|
```bash
|
|
77
|
-
python -m build packages/sqlrules-postgresql
|
|
78
|
-
python -m build packages/sqlrules-sqlite
|
|
79
|
-
|
|
81
|
+
python -m build packages/sqlrules-postgresql --outdir dist-plugins
|
|
82
|
+
python -m build packages/sqlrules-sqlite --outdir dist-plugins
|
|
83
|
+
python -m build packages/sqlrules-mysql --outdir dist-plugins
|
|
84
|
+
python -m build packages/sqlrules-mssql --outdir dist-plugins
|
|
85
|
+
twine upload dist-plugins/*
|
|
80
86
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlrules
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Compile constrained Pydantic models into SQLAlchemy WHERE-rule dictionaries.
|
|
5
5
|
Project-URL: Homepage, https://github.com/eddiethedean/sqlrules
|
|
6
6
|
Project-URL: Documentation, https://github.com/eddiethedean/sqlrules#readme
|
|
@@ -84,19 +84,27 @@ Requires Python 3.10+, Pydantic v2, and SQLAlchemy 2.x.
|
|
|
84
84
|
Optional dialect plugins:
|
|
85
85
|
|
|
86
86
|
```bash
|
|
87
|
-
pip install sqlrules-postgresql #
|
|
88
|
-
pip install sqlrules-sqlite #
|
|
87
|
+
pip install sqlrules-postgresql # ~ / ~*, JSONB, ARRAY, range
|
|
88
|
+
pip install sqlrules-sqlite # REGEXP helper + JSON
|
|
89
|
+
pip install sqlrules-mysql # REGEXP, JSON, full-text
|
|
90
|
+
pip install sqlrules-mssql # JSON + LEN string ops
|
|
89
91
|
```
|
|
90
92
|
|
|
91
93
|
```python
|
|
92
|
-
from
|
|
94
|
+
from typing import Annotated, Any
|
|
95
|
+
from pydantic import BaseModel, Field
|
|
96
|
+
from sqlrules import Compiler, JsonContains
|
|
93
97
|
from sqlrules_postgresql import PostgresPlugin
|
|
94
98
|
|
|
99
|
+
class RowFilter(BaseModel):
|
|
100
|
+
name: Annotated[str, Field(pattern=r"^A")]
|
|
101
|
+
meta: Annotated[dict[str, Any], JsonContains({"active": True})]
|
|
102
|
+
|
|
95
103
|
compiler = Compiler(plugins=[PostgresPlugin()], dialect="postgresql")
|
|
96
|
-
rules = compiler.compile(
|
|
104
|
+
rules = compiler.compile(RowFilter, table)
|
|
97
105
|
```
|
|
98
106
|
|
|
99
|
-
## Supported constraints (0.
|
|
107
|
+
## Supported constraints (0.4)
|
|
100
108
|
|
|
101
109
|
| Constraint | SQLAlchemy expression |
|
|
102
110
|
|---|---|
|
|
@@ -106,9 +114,11 @@ rules = compiler.compile(UserFilter, users)
|
|
|
106
114
|
| `Literal[...]` | `column.in_(...)` |
|
|
107
115
|
| `Enum` | `column.in_(...)` |
|
|
108
116
|
|
|
109
|
-
`pattern` is extracted into IR but has no portable core
|
|
110
|
-
Use a
|
|
111
|
-
|
|
117
|
+
`pattern` is extracted into IR (`PatternSpec`) but has no portable core
|
|
118
|
+
translator. Use a dialect plugin or a custom registry translator.
|
|
119
|
+
|
|
120
|
+
Dialect markers (`JsonContains`, `ArrayContains`, `RangeContains`,
|
|
121
|
+
`FullTextMatch`, …) live in `sqlrules.markers` and require a dialect plugin.
|
|
112
122
|
|
|
113
123
|
Unsupported constraints raise `UnsupportedConstraintError` by default.
|
|
114
124
|
Use `on_unsupported="warn"` or `"ignore"` to change that policy for unknown
|
|
@@ -147,8 +157,10 @@ and [roadmap](docs/ROADMAP.md).
|
|
|
147
157
|
|
|
148
158
|
```bash
|
|
149
159
|
pip install -e ".[dev]"
|
|
150
|
-
pip install -e packages/sqlrules-postgresql -e packages/sqlrules-sqlite
|
|
151
|
-
|
|
160
|
+
pip install -e packages/sqlrules-postgresql -e packages/sqlrules-sqlite \
|
|
161
|
+
-e packages/sqlrules-mysql -e packages/sqlrules-mssql
|
|
162
|
+
pytest tests packages/sqlrules-postgresql/tests packages/sqlrules-sqlite/tests \
|
|
163
|
+
packages/sqlrules-mysql/tests packages/sqlrules-mssql/tests
|
|
152
164
|
ruff check .
|
|
153
165
|
mypy src/sqlrules
|
|
154
166
|
python -m benchmarks.bench_compile
|
|
@@ -44,19 +44,27 @@ Requires Python 3.10+, Pydantic v2, and SQLAlchemy 2.x.
|
|
|
44
44
|
Optional dialect plugins:
|
|
45
45
|
|
|
46
46
|
```bash
|
|
47
|
-
pip install sqlrules-postgresql #
|
|
48
|
-
pip install sqlrules-sqlite #
|
|
47
|
+
pip install sqlrules-postgresql # ~ / ~*, JSONB, ARRAY, range
|
|
48
|
+
pip install sqlrules-sqlite # REGEXP helper + JSON
|
|
49
|
+
pip install sqlrules-mysql # REGEXP, JSON, full-text
|
|
50
|
+
pip install sqlrules-mssql # JSON + LEN string ops
|
|
49
51
|
```
|
|
50
52
|
|
|
51
53
|
```python
|
|
52
|
-
from
|
|
54
|
+
from typing import Annotated, Any
|
|
55
|
+
from pydantic import BaseModel, Field
|
|
56
|
+
from sqlrules import Compiler, JsonContains
|
|
53
57
|
from sqlrules_postgresql import PostgresPlugin
|
|
54
58
|
|
|
59
|
+
class RowFilter(BaseModel):
|
|
60
|
+
name: Annotated[str, Field(pattern=r"^A")]
|
|
61
|
+
meta: Annotated[dict[str, Any], JsonContains({"active": True})]
|
|
62
|
+
|
|
55
63
|
compiler = Compiler(plugins=[PostgresPlugin()], dialect="postgresql")
|
|
56
|
-
rules = compiler.compile(
|
|
64
|
+
rules = compiler.compile(RowFilter, table)
|
|
57
65
|
```
|
|
58
66
|
|
|
59
|
-
## Supported constraints (0.
|
|
67
|
+
## Supported constraints (0.4)
|
|
60
68
|
|
|
61
69
|
| Constraint | SQLAlchemy expression |
|
|
62
70
|
|---|---|
|
|
@@ -66,9 +74,11 @@ rules = compiler.compile(UserFilter, users)
|
|
|
66
74
|
| `Literal[...]` | `column.in_(...)` |
|
|
67
75
|
| `Enum` | `column.in_(...)` |
|
|
68
76
|
|
|
69
|
-
`pattern` is extracted into IR but has no portable core
|
|
70
|
-
Use a
|
|
71
|
-
|
|
77
|
+
`pattern` is extracted into IR (`PatternSpec`) but has no portable core
|
|
78
|
+
translator. Use a dialect plugin or a custom registry translator.
|
|
79
|
+
|
|
80
|
+
Dialect markers (`JsonContains`, `ArrayContains`, `RangeContains`,
|
|
81
|
+
`FullTextMatch`, …) live in `sqlrules.markers` and require a dialect plugin.
|
|
72
82
|
|
|
73
83
|
Unsupported constraints raise `UnsupportedConstraintError` by default.
|
|
74
84
|
Use `on_unsupported="warn"` or `"ignore"` to change that policy for unknown
|
|
@@ -107,8 +117,10 @@ and [roadmap](docs/ROADMAP.md).
|
|
|
107
117
|
|
|
108
118
|
```bash
|
|
109
119
|
pip install -e ".[dev]"
|
|
110
|
-
pip install -e packages/sqlrules-postgresql -e packages/sqlrules-sqlite
|
|
111
|
-
|
|
120
|
+
pip install -e packages/sqlrules-postgresql -e packages/sqlrules-sqlite \
|
|
121
|
+
-e packages/sqlrules-mysql -e packages/sqlrules-mssql
|
|
122
|
+
pytest tests packages/sqlrules-postgresql/tests packages/sqlrules-sqlite/tests \
|
|
123
|
+
packages/sqlrules-mysql/tests packages/sqlrules-mssql/tests
|
|
112
124
|
ruff check .
|
|
113
125
|
mypy src/sqlrules
|
|
114
126
|
python -m benchmarks.bench_compile
|
|
@@ -90,6 +90,7 @@ instances.
|
|
|
90
90
|
|
|
91
91
|
```python
|
|
92
92
|
from sqlrules import PLUGIN_API_VERSION, Compiler
|
|
93
|
+
from sqlrules.constraints import pattern_text
|
|
93
94
|
|
|
94
95
|
class MyPlugin:
|
|
95
96
|
name = "my-plugin"
|
|
@@ -98,15 +99,16 @@ class MyPlugin:
|
|
|
98
99
|
def register(self, registry):
|
|
99
100
|
registry.register_constraint(
|
|
100
101
|
"pattern",
|
|
101
|
-
lambda c, col, ctx: col.op("~")(c.value),
|
|
102
|
+
lambda c, col, ctx: col.op("~")(pattern_text(c.value)[0]),
|
|
102
103
|
on_conflict="replace",
|
|
103
104
|
)
|
|
104
105
|
|
|
105
106
|
compiler = Compiler(plugins=[MyPlugin()])
|
|
106
107
|
```
|
|
107
108
|
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
Dialect markers (`JsonContains`, `ArrayContains`, …) are extracted into IR
|
|
110
|
+
and translated by official dialect plugins. See [PLUGIN_SYSTEM.md](PLUGIN_SYSTEM.md).
|
|
111
|
+
Conformance helpers live in `sqlrules.conformance`.
|
|
110
112
|
|
|
111
113
|
## Other exports
|
|
112
114
|
|
|
@@ -116,7 +118,9 @@ Also exported for advanced use and typing:
|
|
|
116
118
|
- `PLUGIN_API_VERSION`, `SQLRulesPlugin`
|
|
117
119
|
- `SQLRulesWarning`
|
|
118
120
|
- `CompilationContext`, `Constraint`, `FieldDescriptor`, `FieldIR`, `ModelIR`,
|
|
119
|
-
`Diagnostic`
|
|
121
|
+
`Diagnostic`, `PatternSpec`
|
|
122
|
+
- Markers: `ConstraintMarker`, `JsonContains`, `JsonHasKey`, `ArrayContains`,
|
|
123
|
+
`ArrayOverlap`, `RangeContains`, `RangeOverlap`, `FullTextMatch`
|
|
120
124
|
|
|
121
125
|
`DiagnosticsCollector` and `ModelIRCache` are internal (import from
|
|
122
126
|
`sqlrules.ir` / `sqlrules.cache` only if you accept instability).
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Supported Constraint Mapping
|
|
2
|
+
|
|
3
|
+
| Constraint | SQLAlchemy expression |
|
|
4
|
+
|---|---|
|
|
5
|
+
| `gt` | `column > value` |
|
|
6
|
+
| `ge` | `column >= value` |
|
|
7
|
+
| `lt` | `column < value` |
|
|
8
|
+
| `le` | `column <= value` |
|
|
9
|
+
| `min_length` | `func.length(column) >= value` |
|
|
10
|
+
| `max_length` | `func.length(column) <= value` |
|
|
11
|
+
| `multiple_of` | `column % value == 0` |
|
|
12
|
+
| `Literal[...]` | `column.in_(...)` |
|
|
13
|
+
| `Enum` | `column.in_(member values)` |
|
|
14
|
+
| `pattern` | IR (`PatternSpec`); translators via plugins / custom registry |
|
|
15
|
+
|
|
16
|
+
## Dialect markers (`sqlrules.markers`)
|
|
17
|
+
|
|
18
|
+
| Marker | IR operator | Typical plugin translation |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| `JsonContains` | `json_contains` | JSONB / JSON containment |
|
|
21
|
+
| `JsonHasKey` | `json_has_key` | JSON key existence |
|
|
22
|
+
| `ArrayContains` | `array_contains` | PostgreSQL array containment |
|
|
23
|
+
| `ArrayOverlap` | `array_overlap` | PostgreSQL array overlap |
|
|
24
|
+
| `RangeContains` | `range_contains` | PostgreSQL range `@>` |
|
|
25
|
+
| `RangeOverlap` | `range_overlap` | PostgreSQL range `&&` |
|
|
26
|
+
| `FullTextMatch` | `fulltext_match` | MySQL `MATCH ... AGAINST` |
|
|
27
|
+
|
|
28
|
+
Accepted input forms for portable constraints include `Field(...)`,
|
|
29
|
+
`annotated_types` primitives, `Interval`, `Len`, `conint`/`constr`, and
|
|
30
|
+
`StringConstraints` (length attributes; `pattern` becomes `PatternSpec` IR).
|
|
31
|
+
|
|
32
|
+
Constraints without a deterministic SQL equivalent are rejected by default.
|
|
33
|
+
See [TYPE_SUPPORT.md](TYPE_SUPPORT.md) and [ERRORS.md](ERRORS.md).
|
|
34
|
+
|
|
35
|
+
To translate `pattern` or markers, use a dialect plugin:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from sqlrules import Compiler, JsonContains
|
|
39
|
+
from sqlrules_postgresql import PostgresPlugin
|
|
40
|
+
|
|
41
|
+
compiler = Compiler(plugins=[PostgresPlugin()], dialect="postgresql")
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from sqlrules.constraints import pattern_text
|
|
46
|
+
from sqlrules.translators import default_registry
|
|
47
|
+
|
|
48
|
+
registry = default_registry()
|
|
49
|
+
registry.register_constraint(
|
|
50
|
+
"pattern",
|
|
51
|
+
lambda c, col, ctx: col.op("~")(pattern_text(c.value)[0]),
|
|
52
|
+
on_conflict="replace",
|
|
53
|
+
)
|
|
54
|
+
compiler = sqlrules.Compiler(registry=registry)
|
|
55
|
+
```
|
|
@@ -29,18 +29,18 @@ compiler deterministic.
|
|
|
29
29
|
|
|
30
30
|
------------------------------------------------------------------------
|
|
31
31
|
|
|
32
|
-
#
|
|
32
|
+
# Dialects
|
|
33
33
|
|
|
34
34
|
Core emits portable SQLAlchemy Core expressions. The table below describes
|
|
35
35
|
**intended** dialect posture, not a certified multi-backend test matrix.
|
|
36
36
|
|
|
37
37
|
Dialect Status Notes
|
|
38
38
|
------------ -------------------- --------------------------------
|
|
39
|
-
SQLite Core + plugin `sqlrules-sqlite`
|
|
40
|
-
PostgreSQL Core + plugin `sqlrules-postgresql`
|
|
41
|
-
MySQL Core
|
|
42
|
-
MariaDB Core
|
|
43
|
-
SQL Server Core
|
|
39
|
+
SQLite Core + plugin `sqlrules-sqlite` — REGEXP + JSON
|
|
40
|
+
PostgreSQL Core + plugin `sqlrules-postgresql` — regex, JSONB, ARRAY, range
|
|
41
|
+
MySQL Core + plugin `sqlrules-mysql` — REGEXP, JSON, full-text
|
|
42
|
+
MariaDB Core + plugin Follows MySQL plugin where practical
|
|
43
|
+
SQL Server Core + plugin `sqlrules-mssql` — JSON + `LEN`
|
|
44
44
|
Oracle Planned Core support after 1.0
|
|
45
45
|
|
|
46
46
|
------------------------------------------------------------------------
|
|
@@ -61,20 +61,33 @@ These should compile identically across supported databases:
|
|
|
61
61
|
|
|
62
62
|
------------------------------------------------------------------------
|
|
63
63
|
|
|
64
|
-
#
|
|
64
|
+
# Shared dialect markers
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
Dialect-oriented operators are expressed with `sqlrules.markers` and
|
|
67
|
+
extracted into IR. Core does not translate them; plugins register
|
|
68
|
+
translators for the stable operator names:
|
|
69
|
+
|
|
70
|
+
- `JsonContains` → `json_contains`
|
|
71
|
+
- `JsonHasKey` → `json_has_key`
|
|
72
|
+
- `ArrayContains` → `array_contains`
|
|
73
|
+
- `ArrayOverlap` → `array_overlap`
|
|
74
|
+
- `RangeContains` → `range_contains`
|
|
75
|
+
- `RangeOverlap` → `range_overlap`
|
|
76
|
+
- `FullTextMatch` → `fulltext_match`
|
|
67
77
|
|
|
68
|
-
|
|
78
|
+
`list` / `dict` field annotations are allowed for marker-driven fields.
|
|
79
|
+
Portable constraints on containers still raise.
|
|
80
|
+
|
|
81
|
+
------------------------------------------------------------------------
|
|
69
82
|
|
|
70
|
-
|
|
83
|
+
# PostgreSQL
|
|
71
84
|
|
|
72
|
-
|
|
85
|
+
Package: `sqlrules-postgresql`
|
|
73
86
|
|
|
74
|
-
-
|
|
75
|
-
- JSONB
|
|
76
|
-
-
|
|
77
|
-
-
|
|
87
|
+
- `pattern` → `~` or `~*` (`PatternSpec.ignore_case`)
|
|
88
|
+
- JSONB containment / key checks
|
|
89
|
+
- ARRAY contains / overlap
|
|
90
|
+
- range `@>` / `&&`
|
|
78
91
|
|
|
79
92
|
------------------------------------------------------------------------
|
|
80
93
|
|
|
@@ -82,37 +95,29 @@ Potential later enhancements (0.4+):
|
|
|
82
95
|
|
|
83
96
|
Package: `sqlrules-sqlite`
|
|
84
97
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
-
|
|
88
|
-
- Portable numeric comparisons
|
|
89
|
-
- Portable string-length operations
|
|
90
|
-
- `pattern` via `REGEXP` (application must enable REGEXP on the connection)
|
|
91
|
-
|
|
92
|
-
Limitations:
|
|
93
|
-
|
|
94
|
-
- REGEXP requires an extension / `create_function`
|
|
95
|
-
- Limited advanced JSON functionality
|
|
98
|
+
- `pattern` via `REGEXP` — call `register_regexp(connection)`
|
|
99
|
+
- Case-insensitive patterns use a `(?i)` prefix understood by the helper
|
|
100
|
+
- JSON helpers via JSON1 `json_extract` / `json_type`
|
|
96
101
|
|
|
97
102
|
------------------------------------------------------------------------
|
|
98
103
|
|
|
99
104
|
# MySQL / MariaDB
|
|
100
105
|
|
|
101
|
-
|
|
106
|
+
Package: `sqlrules-mysql`
|
|
102
107
|
|
|
103
|
-
-
|
|
104
|
-
- JSON operators
|
|
105
|
-
-
|
|
108
|
+
- `pattern` → `REGEXP`
|
|
109
|
+
- JSON operators (`JSON_CONTAINS`, `JSON_CONTAINS_PATH`)
|
|
110
|
+
- `fulltext_match` → `MATCH ... AGAINST` (requires FULLTEXT index)
|
|
106
111
|
|
|
107
112
|
------------------------------------------------------------------------
|
|
108
113
|
|
|
109
114
|
# SQL Server
|
|
110
115
|
|
|
111
|
-
|
|
116
|
+
Package: `sqlrules-mssql`
|
|
112
117
|
|
|
113
|
-
-
|
|
114
|
-
- JSON
|
|
115
|
-
-
|
|
118
|
+
- `min_length` / `max_length` → `LEN`
|
|
119
|
+
- JSON helpers via `JSON_VALUE` / `JSON_QUERY`
|
|
120
|
+
- `pattern` is not registered (no deterministic portable regex)
|
|
116
121
|
|
|
117
122
|
------------------------------------------------------------------------
|
|
118
123
|
|
|
@@ -145,36 +150,16 @@ If no override exists, the portable translator is used.
|
|
|
145
150
|
|
|
146
151
|
------------------------------------------------------------------------
|
|
147
152
|
|
|
148
|
-
# Example
|
|
149
|
-
|
|
150
|
-
Portable:
|
|
151
|
-
|
|
152
|
-
``` python
|
|
153
|
-
func.length(users.c.name) >= 3
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
PostgreSQL plugin:
|
|
157
|
-
|
|
158
|
-
``` python
|
|
159
|
-
users.c.name.op("~")(pattern)
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
Both originate from the same IR.
|
|
163
|
-
|
|
164
|
-
------------------------------------------------------------------------
|
|
165
|
-
|
|
166
153
|
# Plugin Packages
|
|
167
154
|
|
|
168
|
-
|
|
155
|
+
Official plugins:
|
|
169
156
|
|
|
170
157
|
- sqlrules-postgresql
|
|
171
158
|
- sqlrules-sqlite
|
|
172
159
|
- sqlrules-mysql
|
|
173
160
|
- sqlrules-mssql
|
|
174
|
-
- sqlrules-oracle
|
|
175
161
|
|
|
176
|
-
Each package
|
|
177
|
-
backend.
|
|
162
|
+
Each package registers only the translators relevant to its backend.
|
|
178
163
|
|
|
179
164
|
------------------------------------------------------------------------
|
|
180
165
|
|
|
@@ -184,7 +169,6 @@ Every supported dialect should include:
|
|
|
184
169
|
|
|
185
170
|
- Unit tests
|
|
186
171
|
- SQL compilation tests
|
|
187
|
-
- Integration tests
|
|
188
172
|
- Regression tests
|
|
189
173
|
|
|
190
174
|
The same Pydantic model should produce equivalent logical behavior
|
|
@@ -114,7 +114,7 @@ Official plugin packages begin:
|
|
|
114
114
|
|
|
115
115
|
------------------------------------------------------------------------
|
|
116
116
|
|
|
117
|
-
# 0.4.0 --- Dialect Enhancements
|
|
117
|
+
# 0.4.0 --- Dialect Enhancements ✅
|
|
118
118
|
|
|
119
119
|
## Goals
|
|
120
120
|
|
|
@@ -122,14 +122,14 @@ Provide optimized translations for major databases.
|
|
|
122
122
|
|
|
123
123
|
### PostgreSQL
|
|
124
124
|
|
|
125
|
-
- regex
|
|
125
|
+
- regex (`~` / `~*`)
|
|
126
126
|
- JSONB
|
|
127
127
|
- ARRAY
|
|
128
128
|
- range operators
|
|
129
129
|
|
|
130
130
|
### SQLite
|
|
131
131
|
|
|
132
|
-
- REGEXP integration
|
|
132
|
+
- REGEXP integration (`register_regexp`)
|
|
133
133
|
- JSON helpers
|
|
134
134
|
|
|
135
135
|
### MySQL / MariaDB
|
|
@@ -140,7 +140,13 @@ Provide optimized translations for major databases.
|
|
|
140
140
|
### SQL Server
|
|
141
141
|
|
|
142
142
|
- JSON support
|
|
143
|
-
- string optimizations
|
|
143
|
+
- string optimizations (`LEN`)
|
|
144
|
+
|
|
145
|
+
### Core
|
|
146
|
+
|
|
147
|
+
- `ConstraintMarker` + `sqlrules.markers`
|
|
148
|
+
- `PatternSpec` for pattern IR
|
|
149
|
+
- `list` / `dict` type support for marker fields
|
|
144
150
|
|
|
145
151
|
------------------------------------------------------------------------
|
|
146
152
|
|
|
@@ -51,6 +51,7 @@ A plugin is a Python object that exposes `name`, `api_version`, and
|
|
|
51
51
|
|
|
52
52
|
``` python
|
|
53
53
|
from sqlrules import PLUGIN_API_VERSION
|
|
54
|
+
from sqlrules.constraints import pattern_text
|
|
54
55
|
from sqlrules.translators import TranslatorRegistry
|
|
55
56
|
|
|
56
57
|
class CompanyPlugin:
|
|
@@ -60,12 +61,21 @@ class CompanyPlugin:
|
|
|
60
61
|
def register(self, registry: TranslatorRegistry) -> None:
|
|
61
62
|
registry.register_constraint(
|
|
62
63
|
"pattern",
|
|
63
|
-
lambda c, col, ctx: col.op("~")(c.value),
|
|
64
|
+
lambda c, col, ctx: col.op("~")(pattern_text(c.value)[0]),
|
|
64
65
|
on_conflict="replace",
|
|
65
66
|
)
|
|
66
67
|
```
|
|
67
68
|
|
|
68
|
-
`api_version` must equal `sqlrules.PLUGIN_API_VERSION` (`"1"` in 0.
|
|
69
|
+
`api_version` must equal `sqlrules.PLUGIN_API_VERSION` (`"1"` in 0.4).
|
|
70
|
+
|
|
71
|
+
------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
# Markers
|
|
74
|
+
|
|
75
|
+
Dialect-oriented constraints use `sqlrules.markers` (for example
|
|
76
|
+
`JsonContains`, `ArrayContains`). Markers implement the
|
|
77
|
+
`ConstraintMarker` protocol (`operator`, `value`) and are extracted into
|
|
78
|
+
IR. Plugins register translators for those operator names.
|
|
69
79
|
|
|
70
80
|
------------------------------------------------------------------------
|
|
71
81
|
|
|
@@ -91,7 +101,7 @@ Invalid translators raise `InvalidTranslatorError`. Duplicate operators
|
|
|
91
101
|
raise `RegistryError` unless `on_conflict` / `replace` allows otherwise.
|
|
92
102
|
|
|
93
103
|
`register_type`, `register_dialect`, and `register_compiler_pass` are
|
|
94
|
-
not implemented
|
|
104
|
+
not implemented (reserved).
|
|
95
105
|
|
|
96
106
|
------------------------------------------------------------------------
|
|
97
107
|
|
|
@@ -121,14 +131,12 @@ caller-owned registries are never mutated.
|
|
|
121
131
|
|
|
122
132
|
# Dialect Plugins
|
|
123
133
|
|
|
124
|
-
Official
|
|
125
|
-
|
|
126
|
-
- `sqlrules-postgresql` — `pattern` → `column ~ value`
|
|
127
|
-
- `sqlrules-sqlite` — `pattern` → `column REGEXP value` (app must enable REGEXP)
|
|
134
|
+
Official packages (monorepo under `packages/`):
|
|
128
135
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
136
|
+
- `sqlrules-postgresql` — regex, JSONB, ARRAY, range
|
|
137
|
+
- `sqlrules-sqlite` — REGEXP helper + JSON
|
|
138
|
+
- `sqlrules-mysql` — REGEXP, JSON, full-text
|
|
139
|
+
- `sqlrules-mssql` — JSON + `LEN` length overrides
|
|
132
140
|
|
|
133
141
|
------------------------------------------------------------------------
|
|
134
142
|
|
|
@@ -168,7 +176,8 @@ run_basic_conformance(PostgresPlugin(), operator="pattern")
|
|
|
168
176
|
```
|
|
169
177
|
|
|
170
178
|
Helpers also cover API version checks, builtin preservation, and
|
|
171
|
-
deterministic translation.
|
|
179
|
+
deterministic translation. Pass `model=` / `table=` / `field=` when
|
|
180
|
+
conformance-checking non-`pattern` operators.
|
|
172
181
|
|
|
173
182
|
------------------------------------------------------------------------
|
|
174
183
|
|
|
@@ -185,7 +194,7 @@ The plugin system does not support:
|
|
|
185
194
|
|
|
186
195
|
- Runtime SQL execution
|
|
187
196
|
- Database connections
|
|
188
|
-
- Automatic package downloads / entry-point discovery
|
|
197
|
+
- Automatic package downloads / entry-point discovery
|
|
189
198
|
- Dynamic code generation
|
|
190
199
|
- Compiler pass plugins (future)
|
|
191
200
|
|
|
@@ -28,11 +28,12 @@
|
|
|
28
28
|
- Versioned plugin API (`PLUGIN_API_VERSION`)
|
|
29
29
|
- Diagnostic codes
|
|
30
30
|
|
|
31
|
-
## 0.4 — Dialect Enhancements
|
|
31
|
+
## 0.4 — Dialect Enhancements ✅
|
|
32
32
|
|
|
33
33
|
- PostgreSQL regex polish, JSONB, ARRAY, range operators
|
|
34
34
|
- SQLite REGEXP integration helpers
|
|
35
35
|
- MySQL / MariaDB / SQL Server enhancements
|
|
36
|
+
- Shared `ConstraintMarker` / `sqlrules.markers` API
|
|
36
37
|
|
|
37
38
|
## 1.0 — Stable
|
|
38
39
|
|
|
@@ -16,7 +16,7 @@ Ordering is deterministic and follows model field declaration order.
|
|
|
16
16
|
Within a field, expressions follow constraint extraction order.
|
|
17
17
|
Fields that produce no expressions are omitted from the dictionary.
|
|
18
18
|
|
|
19
|
-
## Supported constraints (v0.
|
|
19
|
+
## Supported constraints (v0.4)
|
|
20
20
|
|
|
21
21
|
- `gt`
|
|
22
22
|
- `ge`
|
|
@@ -28,17 +28,20 @@ Fields that produce no expressions are omitted from the dictionary.
|
|
|
28
28
|
- `Literal`
|
|
29
29
|
- `Enum`
|
|
30
30
|
|
|
31
|
-
`pattern` is extracted into IR but has no portable core
|
|
32
|
-
by default; use `on_unsupported="warn"` / `"ignore"`,
|
|
33
|
-
translator, or install a dialect plugin
|
|
34
|
-
`sqlrules-sqlite`).
|
|
31
|
+
`pattern` is extracted into IR as `PatternSpec` but has no portable core
|
|
32
|
+
translator. It raises by default; use `on_unsupported="warn"` / `"ignore"`,
|
|
33
|
+
register a custom translator, or install a dialect plugin.
|
|
35
34
|
|
|
36
|
-
|
|
35
|
+
Dialect markers (`JsonContains`, `ArrayContains`, `RangeContains`,
|
|
36
|
+
`FullTextMatch`, …) are extracted into IR and require a dialect plugin.
|
|
37
|
+
|
|
38
|
+
## Supported types (v0.4)
|
|
37
39
|
|
|
38
40
|
- `bool`, `int`, `float`, `Decimal`, `str`
|
|
39
41
|
- `date`, `datetime`, `time`
|
|
40
42
|
- `UUID` (Literal / Enum only)
|
|
41
43
|
- `Literal[...]`, `Enum`
|
|
44
|
+
- `list` / `dict` (marker-driven fields only)
|
|
42
45
|
|
|
43
46
|
## Unsupported constraints
|
|
44
47
|
|