sqlrules 0.3.0__py3-none-any.whl → 0.4.0__py3-none-any.whl
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/__init__.py +21 -1
- sqlrules/conformance.py +19 -5
- sqlrules/constraints.py +61 -11
- sqlrules/ir.py +8 -0
- sqlrules/markers.py +86 -0
- {sqlrules-0.3.0.dist-info → sqlrules-0.4.0.dist-info}/METADATA +23 -11
- {sqlrules-0.3.0.dist-info → sqlrules-0.4.0.dist-info}/RECORD +9 -8
- {sqlrules-0.3.0.dist-info → sqlrules-0.4.0.dist-info}/WHEEL +0 -0
- {sqlrules-0.3.0.dist-info → sqlrules-0.4.0.dist-info}/licenses/LICENSE +0 -0
sqlrules/__init__.py
CHANGED
|
@@ -18,27 +18,47 @@ from sqlrules.ir import (
|
|
|
18
18
|
FieldDescriptor,
|
|
19
19
|
FieldIR,
|
|
20
20
|
ModelIR,
|
|
21
|
+
PatternSpec,
|
|
22
|
+
)
|
|
23
|
+
from sqlrules.markers import (
|
|
24
|
+
ArrayContains,
|
|
25
|
+
ArrayOverlap,
|
|
26
|
+
ConstraintMarker,
|
|
27
|
+
FullTextMatch,
|
|
28
|
+
JsonContains,
|
|
29
|
+
JsonHasKey,
|
|
30
|
+
RangeContains,
|
|
31
|
+
RangeOverlap,
|
|
21
32
|
)
|
|
22
33
|
from sqlrules.plugins import PLUGIN_API_VERSION, SQLRulesPlugin
|
|
23
34
|
from sqlrules.translators import SQLRulesWarning
|
|
24
35
|
|
|
25
|
-
__version__ = "0.
|
|
36
|
+
__version__ = "0.4.0"
|
|
26
37
|
|
|
27
38
|
__all__ = [
|
|
28
39
|
"PLUGIN_API_VERSION",
|
|
40
|
+
"ArrayContains",
|
|
41
|
+
"ArrayOverlap",
|
|
29
42
|
"CompilationContext",
|
|
30
43
|
"Compiler",
|
|
31
44
|
"ConfigurationError",
|
|
32
45
|
"Constraint",
|
|
46
|
+
"ConstraintMarker",
|
|
33
47
|
"Diagnostic",
|
|
34
48
|
"FieldDescriptor",
|
|
35
49
|
"FieldIR",
|
|
50
|
+
"FullTextMatch",
|
|
36
51
|
"InternalCompilerError",
|
|
37
52
|
"InvalidModelError",
|
|
38
53
|
"InvalidTranslatorError",
|
|
54
|
+
"JsonContains",
|
|
55
|
+
"JsonHasKey",
|
|
39
56
|
"MissingColumnError",
|
|
40
57
|
"ModelIR",
|
|
58
|
+
"PatternSpec",
|
|
41
59
|
"PluginError",
|
|
60
|
+
"RangeContains",
|
|
61
|
+
"RangeOverlap",
|
|
42
62
|
"RegistryError",
|
|
43
63
|
"SQLRulesError",
|
|
44
64
|
"SQLRulesPlugin",
|
sqlrules/conformance.py
CHANGED
|
@@ -108,15 +108,23 @@ def assert_translates_operator(
|
|
|
108
108
|
f"returned {type(expression)!r}, expected ColumnElement"
|
|
109
109
|
)
|
|
110
110
|
|
|
111
|
-
# Deterministic compile smoke check (
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
# Deterministic compile smoke check (avoid literal_binds; values may be
|
|
112
|
+
# structured IR objects such as PatternSpec).
|
|
113
|
+
compiled_a = str(expression.compile())
|
|
114
|
+
compiled_b = str(expression.compile())
|
|
114
115
|
if compiled_a != compiled_b:
|
|
115
116
|
raise AssertionError(f"Plugin {plugin.name!r}: non-deterministic compile for {operator!r}")
|
|
116
117
|
return expression
|
|
117
118
|
|
|
118
119
|
|
|
119
|
-
def run_basic_conformance(
|
|
120
|
+
def run_basic_conformance(
|
|
121
|
+
plugin: SQLRulesPlugin,
|
|
122
|
+
*,
|
|
123
|
+
operator: str = "pattern",
|
|
124
|
+
model: type[BaseModel] | None = None,
|
|
125
|
+
table: Any | None = None,
|
|
126
|
+
field: str = "name",
|
|
127
|
+
) -> None:
|
|
120
128
|
"""Run the standard conformance checks for a dialect/translator plugin."""
|
|
121
129
|
if getattr(plugin, "api_version", None) != PLUGIN_API_VERSION:
|
|
122
130
|
raise PluginError(
|
|
@@ -129,7 +137,13 @@ def run_basic_conformance(plugin: SQLRulesPlugin, *, operator: str = "pattern")
|
|
|
129
137
|
assert_plugin_api_compatible(plugin)
|
|
130
138
|
assert_builtins_preserved(plugin, on_conflict="replace")
|
|
131
139
|
assert_register_idempotent_ignore(plugin)
|
|
132
|
-
assert_translates_operator(
|
|
140
|
+
assert_translates_operator(
|
|
141
|
+
plugin,
|
|
142
|
+
operator=operator,
|
|
143
|
+
model=model,
|
|
144
|
+
table=table,
|
|
145
|
+
field=field,
|
|
146
|
+
)
|
|
133
147
|
|
|
134
148
|
# Conflict under raise: only meaningful if the plugin re-registers an op
|
|
135
149
|
# already present after the first registration.
|
sqlrules/constraints.py
CHANGED
|
@@ -22,15 +22,18 @@ from annotated_types import (
|
|
|
22
22
|
from pydantic.fields import FieldInfo
|
|
23
23
|
|
|
24
24
|
from sqlrules.errors import UnsupportedConstraintError
|
|
25
|
-
from sqlrules.ir import Constraint, FieldDescriptor
|
|
25
|
+
from sqlrules.ir import Constraint, FieldDescriptor, PatternSpec
|
|
26
|
+
from sqlrules.markers import ConstraintMarker
|
|
26
27
|
|
|
27
28
|
_SUPPORTED_TYPES: frozenset[type[Any]] = frozenset(
|
|
28
29
|
{bool, int, float, Decimal, str, date, datetime, time, UUID}
|
|
29
30
|
)
|
|
31
|
+
_SUPPORTED_CONTAINER_ORIGINS: frozenset[Any] = frozenset({list, dict})
|
|
30
32
|
_UNSUPPORTED_TYPES: frozenset[type[Any]] = frozenset({timedelta})
|
|
31
|
-
_UNSUPPORTED_ORIGINS: frozenset[Any] = frozenset({
|
|
33
|
+
_UNSUPPORTED_ORIGINS: frozenset[Any] = frozenset({tuple, set, frozenset})
|
|
32
34
|
_LENGTH_OPERATORS: frozenset[str] = frozenset({"min_length", "max_length"})
|
|
33
35
|
_NUMERIC_OPERATORS: frozenset[str] = frozenset({"gt", "ge", "lt", "le", "multiple_of"})
|
|
36
|
+
_PORTABLE_OPERATORS: frozenset[str] = _LENGTH_OPERATORS | _NUMERIC_OPERATORS | {"pattern"}
|
|
34
37
|
_TEMPORAL_TYPES: frozenset[type[Any]] = frozenset({date, datetime, time})
|
|
35
38
|
_VALIDATOR_TYPE_NAMES = frozenset(
|
|
36
39
|
{
|
|
@@ -126,6 +129,13 @@ def _concrete_type(field: FieldDescriptor) -> Any:
|
|
|
126
129
|
return annotation
|
|
127
130
|
|
|
128
131
|
|
|
132
|
+
def _is_container_type(annotation: Any) -> bool:
|
|
133
|
+
origin = get_origin(annotation)
|
|
134
|
+
if origin in _SUPPORTED_CONTAINER_ORIGINS:
|
|
135
|
+
return True
|
|
136
|
+
return annotation in _SUPPORTED_CONTAINER_ORIGINS
|
|
137
|
+
|
|
138
|
+
|
|
129
139
|
def ensure_supported_type(field: FieldDescriptor) -> None:
|
|
130
140
|
"""Raise when a field annotation is outside the support matrix."""
|
|
131
141
|
annotation = _concrete_type(field)
|
|
@@ -134,16 +144,20 @@ def ensure_supported_type(field: FieldDescriptor) -> None:
|
|
|
134
144
|
if origin is Literal:
|
|
135
145
|
return
|
|
136
146
|
|
|
137
|
-
if origin in _UNSUPPORTED_ORIGINS:
|
|
147
|
+
if origin in _UNSUPPORTED_ORIGINS or annotation in _UNSUPPORTED_ORIGINS:
|
|
138
148
|
raise UnsupportedConstraintError(
|
|
139
149
|
field=field.name,
|
|
140
|
-
operator=getattr(origin, "__name__", str(origin)),
|
|
150
|
+
operator=getattr(origin or annotation, "__name__", str(origin or annotation)),
|
|
141
151
|
value=annotation,
|
|
142
152
|
suggestion=(
|
|
143
|
-
"Remove the field or wait for a future SQLRules release with
|
|
153
|
+
"Remove the field or wait for a future SQLRules release with "
|
|
154
|
+
f"{getattr(origin or annotation, '__name__', origin or annotation)!r} support."
|
|
144
155
|
),
|
|
145
156
|
)
|
|
146
157
|
|
|
158
|
+
if _is_container_type(annotation):
|
|
159
|
+
return
|
|
160
|
+
|
|
147
161
|
if isinstance(annotation, type) and issubclass(annotation, Enum):
|
|
148
162
|
return
|
|
149
163
|
|
|
@@ -171,11 +185,16 @@ def ensure_supported_type(field: FieldDescriptor) -> None:
|
|
|
171
185
|
)
|
|
172
186
|
|
|
173
187
|
|
|
174
|
-
def _normalize_pattern(field_name: str, pattern: Any) ->
|
|
175
|
-
if isinstance(pattern,
|
|
188
|
+
def _normalize_pattern(field_name: str, pattern: Any) -> PatternSpec:
|
|
189
|
+
if isinstance(pattern, PatternSpec):
|
|
176
190
|
return pattern
|
|
191
|
+
if isinstance(pattern, str):
|
|
192
|
+
return PatternSpec(pattern=pattern, ignore_case=False)
|
|
177
193
|
if isinstance(pattern, re.Pattern):
|
|
178
|
-
return
|
|
194
|
+
return PatternSpec(
|
|
195
|
+
pattern=str(pattern.pattern),
|
|
196
|
+
ignore_case=bool(pattern.flags & re.IGNORECASE),
|
|
197
|
+
)
|
|
179
198
|
raise UnsupportedConstraintError(
|
|
180
199
|
field=field_name,
|
|
181
200
|
operator="pattern",
|
|
@@ -184,6 +203,15 @@ def _normalize_pattern(field_name: str, pattern: Any) -> str:
|
|
|
184
203
|
)
|
|
185
204
|
|
|
186
205
|
|
|
206
|
+
def pattern_text(value: Any) -> tuple[str, bool]:
|
|
207
|
+
"""Return ``(pattern, ignore_case)`` from a ``pattern`` constraint value."""
|
|
208
|
+
if isinstance(value, PatternSpec):
|
|
209
|
+
return value.pattern, value.ignore_case
|
|
210
|
+
if isinstance(value, str):
|
|
211
|
+
return value, False
|
|
212
|
+
raise TypeError(f"pattern value must be str or PatternSpec, got {type(value)!r}")
|
|
213
|
+
|
|
214
|
+
|
|
187
215
|
def _constraints_from_mapping(field_name: str, data: dict[str, Any]) -> list[Constraint]:
|
|
188
216
|
"""Map whitelisted metadata keys into IR; ignore validation-only flags."""
|
|
189
217
|
constraints: list[Constraint] = []
|
|
@@ -202,7 +230,16 @@ def _constraints_from_mapping(field_name: str, data: dict[str, Any]) -> list[Con
|
|
|
202
230
|
return constraints
|
|
203
231
|
|
|
204
232
|
|
|
233
|
+
def _is_constraint_marker(item: Any) -> bool:
|
|
234
|
+
if isinstance(item, type):
|
|
235
|
+
return False
|
|
236
|
+
return isinstance(item, ConstraintMarker)
|
|
237
|
+
|
|
238
|
+
|
|
205
239
|
def _unsupported_constraints(field_name: str, item: Any) -> list[Constraint]:
|
|
240
|
+
if _is_constraint_marker(item):
|
|
241
|
+
return [Constraint(field_name, item.operator, item.value)]
|
|
242
|
+
|
|
206
243
|
if isinstance(item, Predicate):
|
|
207
244
|
return [Constraint(field_name, "predicate", item.func)]
|
|
208
245
|
|
|
@@ -218,7 +255,7 @@ def _unsupported_constraints(field_name: str, item: Any) -> list[Constraint]:
|
|
|
218
255
|
|
|
219
256
|
# First-class pattern attribute (e.g. _PydanticGeneralMetadata(pattern=...)).
|
|
220
257
|
pattern = getattr(item, "pattern", None)
|
|
221
|
-
if pattern is not None and isinstance(pattern, (str, re.Pattern)):
|
|
258
|
+
if pattern is not None and isinstance(pattern, (str, re.Pattern, PatternSpec)):
|
|
222
259
|
constraints = _constraints_from_mapping(
|
|
223
260
|
field_name,
|
|
224
261
|
{
|
|
@@ -243,6 +280,17 @@ def _reject_type_operator_mismatch(field: FieldDescriptor, constraint: Constrain
|
|
|
243
280
|
annotation = _concrete_type(field)
|
|
244
281
|
origin = get_origin(annotation)
|
|
245
282
|
|
|
283
|
+
if _is_container_type(annotation) and constraint.operator in _PORTABLE_OPERATORS:
|
|
284
|
+
raise UnsupportedConstraintError(
|
|
285
|
+
field=field.name,
|
|
286
|
+
operator=constraint.operator,
|
|
287
|
+
value=constraint.value,
|
|
288
|
+
suggestion=(
|
|
289
|
+
"Portable constraints are not valid on list/dict fields; "
|
|
290
|
+
"use sqlrules.markers (e.g. JsonContains, ArrayContains) instead."
|
|
291
|
+
),
|
|
292
|
+
)
|
|
293
|
+
|
|
246
294
|
if origin is Literal:
|
|
247
295
|
if (
|
|
248
296
|
constraint.operator not in {"literal", "enum"}
|
|
@@ -322,7 +370,9 @@ def extract_constraints(field: FieldDescriptor) -> list[Constraint]:
|
|
|
322
370
|
constraints: list[Constraint] = []
|
|
323
371
|
|
|
324
372
|
for item in _iter_metadata(_field_metadata(field)):
|
|
325
|
-
if
|
|
373
|
+
if _is_constraint_marker(item):
|
|
374
|
+
constraints.append(Constraint(field.name, item.operator, item.value))
|
|
375
|
+
elif isinstance(item, Gt):
|
|
326
376
|
constraints.append(Constraint(field.name, "gt", item.gt))
|
|
327
377
|
elif isinstance(item, Ge):
|
|
328
378
|
constraints.append(Constraint(field.name, "ge", item.ge))
|
|
@@ -351,7 +401,7 @@ def extract_constraints(field: FieldDescriptor) -> list[Constraint]:
|
|
|
351
401
|
constraints.append(Constraint(field.name, "enum", values))
|
|
352
402
|
|
|
353
403
|
for constraint in constraints:
|
|
354
|
-
if constraint.operator in
|
|
404
|
+
if constraint.operator in _PORTABLE_OPERATORS:
|
|
355
405
|
_reject_type_operator_mismatch(field, constraint)
|
|
356
406
|
|
|
357
407
|
return constraints
|
sqlrules/ir.py
CHANGED
|
@@ -8,6 +8,14 @@ OnConflict = Literal["raise", "replace", "ignore"]
|
|
|
8
8
|
DiagnosticSeverity = Literal["warning", "info"]
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
@dataclass(frozen=True, slots=True)
|
|
12
|
+
class PatternSpec:
|
|
13
|
+
"""Normalized ``pattern`` constraint value (preserves case-folding intent)."""
|
|
14
|
+
|
|
15
|
+
pattern: str
|
|
16
|
+
ignore_case: bool = False
|
|
17
|
+
|
|
18
|
+
|
|
11
19
|
@dataclass(frozen=True, slots=True)
|
|
12
20
|
class Constraint:
|
|
13
21
|
field: str
|
sqlrules/markers.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Dialect-oriented constraint markers for Annotated metadata.
|
|
2
|
+
|
|
3
|
+
Markers are extracted into IR as ``Constraint(field, marker.operator, marker.value)``.
|
|
4
|
+
Core does not translate these operators; dialect plugins register translators.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from typing import Any, Protocol, runtime_checkable
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@runtime_checkable
|
|
14
|
+
class ConstraintMarker(Protocol):
|
|
15
|
+
"""Protocol for Annotated metadata that becomes a SQLRules IR constraint."""
|
|
16
|
+
|
|
17
|
+
operator: str
|
|
18
|
+
value: Any
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True, slots=True)
|
|
22
|
+
class JsonContains:
|
|
23
|
+
"""Require JSON/JSONB containment (``json_contains``)."""
|
|
24
|
+
|
|
25
|
+
value: Any
|
|
26
|
+
operator: str = field(default="json_contains", init=False, repr=False)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True, slots=True)
|
|
30
|
+
class JsonHasKey:
|
|
31
|
+
"""Require a JSON object key (``json_has_key``)."""
|
|
32
|
+
|
|
33
|
+
value: Any
|
|
34
|
+
operator: str = field(default="json_has_key", init=False, repr=False)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True, slots=True)
|
|
38
|
+
class ArrayContains:
|
|
39
|
+
"""Require array containment (``array_contains``)."""
|
|
40
|
+
|
|
41
|
+
value: Any
|
|
42
|
+
operator: str = field(default="array_contains", init=False, repr=False)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True, slots=True)
|
|
46
|
+
class ArrayOverlap:
|
|
47
|
+
"""Require array overlap (``array_overlap``)."""
|
|
48
|
+
|
|
49
|
+
value: Any
|
|
50
|
+
operator: str = field(default="array_overlap", init=False, repr=False)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(frozen=True, slots=True)
|
|
54
|
+
class RangeContains:
|
|
55
|
+
"""Require range containment (``range_contains``)."""
|
|
56
|
+
|
|
57
|
+
value: Any
|
|
58
|
+
operator: str = field(default="range_contains", init=False, repr=False)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass(frozen=True, slots=True)
|
|
62
|
+
class RangeOverlap:
|
|
63
|
+
"""Require range overlap (``range_overlap``)."""
|
|
64
|
+
|
|
65
|
+
value: Any
|
|
66
|
+
operator: str = field(default="range_overlap", init=False, repr=False)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass(frozen=True, slots=True)
|
|
70
|
+
class FullTextMatch:
|
|
71
|
+
"""Require a full-text match (``fulltext_match``)."""
|
|
72
|
+
|
|
73
|
+
value: Any
|
|
74
|
+
operator: str = field(default="fulltext_match", init=False, repr=False)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
__all__ = [
|
|
78
|
+
"ArrayContains",
|
|
79
|
+
"ArrayOverlap",
|
|
80
|
+
"ConstraintMarker",
|
|
81
|
+
"FullTextMatch",
|
|
82
|
+
"JsonContains",
|
|
83
|
+
"JsonHasKey",
|
|
84
|
+
"RangeContains",
|
|
85
|
+
"RangeOverlap",
|
|
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
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
sqlrules/__init__.py,sha256=
|
|
1
|
+
sqlrules/__init__.py,sha256=60Wwpv204ZOs36qCHvRWD8r8ZYLvAS3qunLvB3qSWNY,1513
|
|
2
2
|
sqlrules/cache.py,sha256=9NBDunUMvQR3bsx9QxIiO8L8JNbpI1tdLlFNapUZMj8,1051
|
|
3
3
|
sqlrules/columns.py,sha256=TcDhFG8wxgKC3-kwXhOBP27Ekrlfncm-WWrUkMLCplo,2428
|
|
4
4
|
sqlrules/compiler.py,sha256=sVlpcP5qv-BUjKSNwd4Q6I3aQvqVOgDo89EAwxTqo5I,6799
|
|
5
|
-
sqlrules/conformance.py,sha256=
|
|
6
|
-
sqlrules/constraints.py,sha256=
|
|
5
|
+
sqlrules/conformance.py,sha256=WZfjnv3JfQZ2J93VE3kBD8AFlrz0DX6XjuZ7Z_0D0I0,5646
|
|
6
|
+
sqlrules/constraints.py,sha256=nHuX2xuoSnyx-l6D6hk8Ncb946r_IkehtQ2iumbmbzs,14346
|
|
7
7
|
sqlrules/errors.py,sha256=qcSCcnL19kvfzAd1eMN1pVVNbV3cg1WLb94uGna8w-M,2989
|
|
8
8
|
sqlrules/inspectors.py,sha256=mEi7ixA1aQe7lyZtcFvLHVqVsAXt_tm60YNgJrYbrp8,1700
|
|
9
|
-
sqlrules/ir.py,sha256=
|
|
9
|
+
sqlrules/ir.py,sha256=jUw8BjZeTBHt5o0KcwBfbUfC7VlcEl4McQahTfZ0DAA,2285
|
|
10
|
+
sqlrules/markers.py,sha256=mAFlZYn050wCKCungONWawIvnYlXTF7aG_VzZ7Hz5GQ,2159
|
|
10
11
|
sqlrules/plugins.py,sha256=ClXVdISpgNzVQkQCJk9ptgnXxqWLwi99EBcNP9wYXyI,1740
|
|
11
12
|
sqlrules/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
13
|
sqlrules/translators.py,sha256=aw8Q8hgfNUqdUEosUXgMW4IqTTBNUtSA-Yc2aCiPKNs,7949
|
|
13
|
-
sqlrules-0.
|
|
14
|
-
sqlrules-0.
|
|
15
|
-
sqlrules-0.
|
|
16
|
-
sqlrules-0.
|
|
14
|
+
sqlrules-0.4.0.dist-info/METADATA,sha256=icGrZdcSygSpxjeX-rYsDJvRYy6xGA0owVvXQf0yy2Y,5485
|
|
15
|
+
sqlrules-0.4.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
16
|
+
sqlrules-0.4.0.dist-info/licenses/LICENSE,sha256=cLMlkCH6RhjWAkXBWR7LazV98TOG0WkPtCKB5zqqkAs,1078
|
|
17
|
+
sqlrules-0.4.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|