sqlrules-postgresql 1.0.1__py3-none-any.whl → 2.0.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.
@@ -1,14 +1,21 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Mapping
4
+ from typing import Any
5
+
6
+ from sqlalchemy.dialects.postgresql import JSONB
7
+
8
+ from sqlrules.backend import prepare_scalar
9
+ from sqlrules.errors import CapabilityError
10
+ from sqlrules.ir import CompilationContext, PreparedValue, RuleField
3
11
  from sqlrules.plugins import PLUGIN_API_VERSION
4
12
  from sqlrules.translators import TranslatorRegistry
5
13
  from sqlrules_postgresql.array import translate_array_contains, translate_array_overlap
6
14
  from sqlrules_postgresql.jsonb import translate_json_contains, translate_json_has_key
7
15
  from sqlrules_postgresql.pattern import translate_pattern
8
16
  from sqlrules_postgresql.range import translate_range_contains, translate_range_overlap
9
- from sqlrules_postgresql.type_check import translate_type_check
10
17
 
11
- __version__ = "1.0.1"
18
+ __version__ = "2.0.0"
12
19
 
13
20
 
14
21
  class PostgresPlugin:
@@ -17,17 +24,63 @@ class PostgresPlugin:
17
24
  name = "postgresql"
18
25
  api_version = PLUGIN_API_VERSION
19
26
 
27
+ def __init__(self, *, server_version: tuple[int, ...] | None = None) -> None:
28
+ self.server_version = tuple(server_version) if server_version is not None else None
29
+
30
+ def capabilities(self) -> Mapping[str, Any]:
31
+ return {
32
+ "backend": self.name,
33
+ "server_version": self.server_version,
34
+ "native_scalar_types": (
35
+ "bool",
36
+ "int",
37
+ "float",
38
+ "decimal",
39
+ "str",
40
+ "date",
41
+ "datetime",
42
+ "time",
43
+ "uuid",
44
+ ),
45
+ "safe_text_numeric_conversion": self.server_version is not None
46
+ and self.server_version >= (16, 0),
47
+ "safe_text_numeric_targets": (
48
+ ("int", "float", "decimal")
49
+ if self.server_version is not None and self.server_version >= (16, 0)
50
+ else ()
51
+ ),
52
+ "assumptions": ("PostgreSQL native column types provide logical type evidence.",),
53
+ }
54
+
55
+ def prepare_value(
56
+ self,
57
+ column: Any,
58
+ field: RuleField,
59
+ context: CompilationContext,
60
+ ) -> PreparedValue:
61
+ if (
62
+ field.python_type is dict
63
+ and any(
64
+ item.operator in {"json_contains", "json_has_key"} for item in field.constraints
65
+ )
66
+ and not isinstance(column.type, JSONB)
67
+ ):
68
+ raise CapabilityError(
69
+ self.name,
70
+ field.name,
71
+ "jsonb",
72
+ type(column.type).__name__,
73
+ "PostgreSQL JSON markers require a JSONB column; generic JSON does not provide "
74
+ "the containment and key operators used by SQLRules.",
75
+ )
76
+ return prepare_scalar(column, field, context, backend=self.name)
77
+
20
78
  def register(self, registry: TranslatorRegistry) -> None:
21
79
  registry.register_constraint(
22
80
  "pattern",
23
81
  translate_pattern,
24
82
  on_conflict="replace",
25
83
  )
26
- registry.register_constraint(
27
- "type_check",
28
- translate_type_check,
29
- on_conflict="replace",
30
- )
31
84
  for operator, translator in (
32
85
  ("json_contains", translate_json_contains),
33
86
  ("json_has_key", translate_json_has_key),
@@ -49,5 +102,4 @@ __all__ = [
49
102
  "translate_pattern",
50
103
  "translate_range_contains",
51
104
  "translate_range_overlap",
52
- "translate_type_check",
53
105
  ]
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from typing import Any, cast
4
4
 
5
+ from sqlalchemy import func
5
6
  from sqlalchemy.sql.elements import ColumnElement
6
7
 
7
8
  from sqlrules.ir import CompilationContext, Constraint
@@ -22,4 +23,7 @@ def translate_json_has_key(
22
23
  context: CompilationContext,
23
24
  ) -> ColumnElement[bool]:
24
25
  """Translate ``json_has_key`` to JSONB ``?`` / ``has_key``."""
25
- return cast(ColumnElement[bool], column.has_key(constraint.value))
26
+ return cast(
27
+ ColumnElement[bool],
28
+ (func.jsonb_typeof(column) == "object") & column.has_key(constraint.value),
29
+ )
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.5
2
+ Name: sqlrules-postgresql
3
+ Version: 2.0.0
4
+ Summary: PostgreSQL dialect plugin for SQLRules (regex, JSONB, ARRAY, range).
5
+ Project-URL: Homepage, https://github.com/eddiethedean/sqlrules
6
+ Project-URL: Repository, https://github.com/eddiethedean/sqlrules
7
+ Project-URL: Issues, https://github.com/eddiethedean/sqlrules/issues
8
+ Author: SQLRules Contributors
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: postgresql,pydantic,sqlalchemy,sqlrules
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Typing :: Typed
16
+ Requires-Python: >=3.10
17
+ Requires-Dist: sqlalchemy<3,>=2.0
18
+ Requires-Dist: sqlrules<3,>=2
19
+ Provides-Extra: dev
20
+ Requires-Dist: pytest>=8.0; extra == 'dev'
21
+ Description-Content-Type: text/markdown
22
+
23
+ # sqlrules-postgresql
24
+
25
+ PostgreSQL backend provider for [SQLRules](https://github.com/eddiethedean/sqlrules).
26
+ The package version follows the core 2.x line.
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ pip install "sqlrules>=2,<3" "sqlrules-postgresql>=2,<3"
32
+ ```
33
+
34
+ ## Use
35
+
36
+ ```python
37
+ from typing import Annotated, Any
38
+
39
+ from pydantic import Field
40
+ from sqlalchemy import Column, MetaData, String, Table
41
+ from sqlalchemy.dialects.postgresql import ARRAY, INT4RANGE, JSONB
42
+
43
+ from sqlrules import ArrayContains, Compiler, JsonContains, RangeContains, RuleSchema, where
44
+ from sqlrules_postgresql import PostgresPlugin
45
+
46
+ rows = Table(
47
+ "rows",
48
+ MetaData(),
49
+ Column("name", String),
50
+ Column("meta", JSONB),
51
+ Column("tags", ARRAY(String)),
52
+ Column("span", INT4RANGE),
53
+ )
54
+
55
+
56
+ class RowRules(RuleSchema):
57
+ name: Annotated[str, Field(pattern=r"^A")]
58
+ meta: Annotated[dict[str, Any], JsonContains({"active": True})]
59
+ tags: Annotated[list[str], ArrayContains(["admin"])]
60
+ span: Annotated[int, RangeContains(5)]
61
+
62
+
63
+ compiled = Compiler(plugins=[PostgresPlugin(server_version=(16, 0))]).compile(RowRules, rows)
64
+ statement = rows.select().where(*where(compiled))
65
+ ```
66
+
67
+ ## Capabilities
68
+
69
+ - `pattern`: PostgreSQL `~` / `~*`
70
+ - JSONB containment and key membership
71
+ - ARRAY containment and overlap
72
+ - Range containment and overlap
73
+ - Safe lax text-to-int/float/Decimal parsing on PostgreSQL 16+
74
+
75
+ String Literal and Enum fields require a column with `C` or `POSIX`
76
+ collation. Review the [type support matrix](https://sqlrules.readthedocs.io/en/latest/TYPE_SUPPORT.html)
77
+ for the exact conversion profile and limitations.
78
+
79
+ ## Pattern cost
80
+
81
+ Untrusted regular expressions can cause expensive engine-side evaluation.
82
+ Prefer patterns authored with the rule model. See the SQLRules
83
+ [security notes](https://sqlrules.readthedocs.io/en/latest/SECURITY.html).
@@ -0,0 +1,10 @@
1
+ sqlrules_postgresql/__init__.py,sha256=PFGK_zUbwUWHt-PE-3njuzw0C5zZIWTZBTf7zXycM-w,3684
2
+ sqlrules_postgresql/array.py,sha256=Kzin-DQRwidhVsL6hL8c04ejSjEfGjcMZewjSHmH_3s,764
3
+ sqlrules_postgresql/jsonb.py,sha256=KYl8AyZAdGqjFT7hjeWm4yLpJXbKuMYLDPomZJuMVuc,843
4
+ sqlrules_postgresql/pattern.py,sha256=swLYUSmd0sgW-XSYunYBBnoT2QpwkzDsXfuL38cgXqU,599
5
+ sqlrules_postgresql/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ sqlrules_postgresql/range.py,sha256=WBvd5yHpkP2UNmU_TxCNdHj-HdTMYVPO_PztqWj6TUs,774
7
+ sqlrules_postgresql-2.0.0.dist-info/METADATA,sha256=Fip_8BMPGyf6l-KlxGsxkyZhsnajXqCa1WQRX3kFUxg,2657
8
+ sqlrules_postgresql-2.0.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
9
+ sqlrules_postgresql-2.0.0.dist-info/licenses/LICENSE,sha256=cLMlkCH6RhjWAkXBWR7LazV98TOG0WkPtCKB5zqqkAs,1078
10
+ sqlrules_postgresql-2.0.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.31.0
2
+ Generator: hatchling 1.32.4
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,250 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from datetime import date, datetime, time
4
- from decimal import Decimal
5
- from typing import Any, cast
6
- from uuid import UUID
7
-
8
- from sqlalchemy import Boolean, Date, DateTime, Float, Integer, Numeric, String, Time
9
- from sqlalchemy.sql.elements import ColumnElement
10
- from sqlalchemy.sql.sqltypes import NullType
11
- from sqlalchemy.types import TypeEngine
12
-
13
- from sqlrules.constraints import type_spec
14
- from sqlrules.errors import UnsupportedConstraintError
15
- from sqlrules.ir import CompilationContext, Constraint, TypeSpec
16
-
17
- # Integer-like text (Pydantic lax int accepts digit strings).
18
- _INT_TEXT = r"^[+-]?(0|[1-9]\d*)$"
19
- # Float / Decimal-like text (simplified; not full Pydantic parity).
20
- _FLOAT_TEXT = r"^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$"
21
- # ISO-ish date / datetime / time (approximate).
22
- _DATE_TEXT = r"^\d{4}-\d{2}-\d{2}$"
23
- _DATETIME_TEXT = r"^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}(\.\d+)?([+-]\d{2}:?\d{2}|Z)?$"
24
- _TIME_TEXT = r"^\d{2}:\d{2}:\d{2}(\.\d+)?$"
25
- _UUID_TEXT = (
26
- r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-"
27
- r"[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
28
- )
29
-
30
-
31
- def _col_type(column: ColumnElement[Any]) -> TypeEngine[Any]:
32
- return column.type
33
-
34
-
35
- def _is_type(column: ColumnElement[Any], *bases: type[TypeEngine[Any]]) -> bool:
36
- col_type = _col_type(column)
37
- if isinstance(col_type, NullType):
38
- return False
39
- return isinstance(col_type, bases)
40
-
41
-
42
- def _regex(column: ColumnElement[Any], pattern: str) -> ColumnElement[bool]:
43
- return cast(ColumnElement[bool], column.op("~")(pattern))
44
-
45
-
46
- def _wrap_none(
47
- column: ColumnElement[Any],
48
- predicate: ColumnElement[bool],
49
- *,
50
- allow_none: bool,
51
- ) -> ColumnElement[bool]:
52
- if allow_none:
53
- return cast(ColumnElement[bool], column.is_(None) | predicate)
54
- return predicate
55
-
56
-
57
- def _unsupported(field: str, spec: TypeSpec, suggestion: str) -> None:
58
- raise UnsupportedConstraintError(
59
- field=field,
60
- operator="type_check",
61
- value=spec,
62
- suggestion=suggestion,
63
- )
64
-
65
-
66
- def _predicate_int(
67
- column: ColumnElement[Any],
68
- spec: TypeSpec,
69
- field: str,
70
- ) -> ColumnElement[bool]:
71
- if _is_type(column, Integer):
72
- # Typed integer column: every non-null value is already an int.
73
- return cast(ColumnElement[bool], column.isnot(None))
74
- if spec.strict:
75
- _unsupported(
76
- field,
77
- spec,
78
- "PostgreSQL strict int type_check requires an Integer column "
79
- "(Pydantic strict mode rejects string/bool coercion).",
80
- )
81
- if _is_type(column, String):
82
- return _regex(column, _INT_TEXT)
83
- if _is_type(column, Float, Numeric):
84
- # Whole numbers only (approximate Pydantic float→int when no fractional part).
85
- return cast(ColumnElement[bool], column == column.cast(Integer).cast(Float))
86
- _unsupported(
87
- field,
88
- spec,
89
- "PostgreSQL lax int type_check supports Integer, String, Float, or Numeric columns.",
90
- )
91
- raise AssertionError("unreachable")
92
-
93
-
94
- def _predicate_bool(
95
- column: ColumnElement[Any],
96
- spec: TypeSpec,
97
- field: str,
98
- ) -> ColumnElement[bool]:
99
- if spec.strict:
100
- if _is_type(column, Boolean):
101
- return cast(ColumnElement[bool], column.in_((True, False)))
102
- _unsupported(
103
- field,
104
- spec,
105
- "PostgreSQL strict bool type_check requires a Boolean column.",
106
- )
107
- _unsupported(
108
- field,
109
- spec,
110
- "PostgreSQL lax bool type_check is not supported (coercion set is not "
111
- "deterministically expressible). Use Field(strict=True) or model_config "
112
- "strict=True with a Boolean column.",
113
- )
114
- raise AssertionError("unreachable")
115
-
116
-
117
- def _predicate_str(
118
- column: ColumnElement[Any],
119
- spec: TypeSpec,
120
- field: str,
121
- ) -> ColumnElement[bool]:
122
- if _is_type(column, String):
123
- return cast(ColumnElement[bool], column.isnot(None))
124
- _unsupported(
125
- field,
126
- spec,
127
- "PostgreSQL str type_check requires a String/Text column "
128
- "(Pydantic does not coerce other SQL types to str by default).",
129
- )
130
- raise AssertionError("unreachable")
131
-
132
-
133
- def _predicate_float(
134
- column: ColumnElement[Any],
135
- spec: TypeSpec,
136
- field: str,
137
- ) -> ColumnElement[bool]:
138
- if _is_type(column, Float, Numeric, Integer):
139
- return cast(ColumnElement[bool], column.isnot(None))
140
- if spec.strict:
141
- _unsupported(
142
- field,
143
- spec,
144
- "PostgreSQL strict float/Decimal type_check requires a numeric column.",
145
- )
146
- if _is_type(column, String):
147
- return _regex(column, _FLOAT_TEXT)
148
- _unsupported(
149
- field,
150
- spec,
151
- "PostgreSQL float/Decimal type_check supports numeric or String columns.",
152
- )
153
- raise AssertionError("unreachable")
154
-
155
-
156
- def _predicate_temporal(
157
- column: ColumnElement[Any],
158
- spec: TypeSpec,
159
- field: str,
160
- *,
161
- sa_types: tuple[type[TypeEngine[Any]], ...],
162
- text_pattern: str,
163
- ) -> ColumnElement[bool]:
164
- if _is_type(column, *sa_types):
165
- return cast(ColumnElement[bool], column.isnot(None))
166
- if spec.strict:
167
- type_name = spec.python_type.__name__
168
- _unsupported(
169
- field,
170
- spec,
171
- f"PostgreSQL strict {type_name} type_check requires a typed {type_name} column.",
172
- )
173
- if _is_type(column, String):
174
- return _regex(column, text_pattern)
175
- type_name = spec.python_type.__name__
176
- _unsupported(
177
- field,
178
- spec,
179
- f"PostgreSQL {type_name} type_check supports typed {type_name} or String columns.",
180
- )
181
- raise AssertionError("unreachable")
182
-
183
-
184
- def _predicate_uuid(
185
- column: ColumnElement[Any],
186
- spec: TypeSpec,
187
- field: str,
188
- ) -> ColumnElement[bool]:
189
- col_type = _col_type(column)
190
- type_name = type(col_type).__name__.lower()
191
- if "uuid" in type_name:
192
- return cast(ColumnElement[bool], column.isnot(None))
193
- if spec.strict:
194
- _unsupported(
195
- field,
196
- spec,
197
- "PostgreSQL strict UUID type_check requires a UUID column.",
198
- )
199
- if _is_type(column, String):
200
- return _regex(column, _UUID_TEXT)
201
- _unsupported(
202
- field,
203
- spec,
204
- "PostgreSQL UUID type_check supports UUID or String columns.",
205
- )
206
- raise AssertionError("unreachable")
207
-
208
-
209
- def _build_predicate(
210
- column: ColumnElement[Any],
211
- spec: TypeSpec,
212
- field: str,
213
- ) -> ColumnElement[bool]:
214
- python_type = spec.python_type
215
- if python_type is int:
216
- return _predicate_int(column, spec, field)
217
- if python_type is bool:
218
- return _predicate_bool(column, spec, field)
219
- if python_type is str:
220
- return _predicate_str(column, spec, field)
221
- if python_type in {float, Decimal}:
222
- return _predicate_float(column, spec, field)
223
- if python_type is date:
224
- return _predicate_temporal(column, spec, field, sa_types=(Date,), text_pattern=_DATE_TEXT)
225
- if python_type is datetime:
226
- return _predicate_temporal(
227
- column, spec, field, sa_types=(DateTime,), text_pattern=_DATETIME_TEXT
228
- )
229
- if python_type is time:
230
- return _predicate_temporal(column, spec, field, sa_types=(Time,), text_pattern=_TIME_TEXT)
231
- if python_type is UUID:
232
- return _predicate_uuid(column, spec, field)
233
- _unsupported(
234
- field,
235
- spec,
236
- f"PostgreSQL type_check has no translator for {python_type!r}.",
237
- )
238
- raise AssertionError("unreachable")
239
-
240
-
241
- def translate_type_check(
242
- constraint: Constraint,
243
- column: ColumnElement[Any],
244
- context: CompilationContext,
245
- ) -> ColumnElement[bool]:
246
- """Translate ``type_check`` into a PostgreSQL shape/type predicate."""
247
- del context # dialect hint unused; plugin is already PostgreSQL-specific
248
- spec = type_spec(constraint.value)
249
- predicate = _build_predicate(column, spec, constraint.field)
250
- return _wrap_none(column, predicate, allow_none=spec.allow_none)
@@ -1,98 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: sqlrules-postgresql
3
- Version: 1.0.1
4
- Summary: PostgreSQL dialect plugin for SQLRules (regex, JSONB, ARRAY, range).
5
- Project-URL: Homepage, https://github.com/eddiethedean/sqlrules
6
- Project-URL: Repository, https://github.com/eddiethedean/sqlrules
7
- Project-URL: Issues, https://github.com/eddiethedean/sqlrules/issues
8
- Author: SQLRules Contributors
9
- License-Expression: MIT
10
- License-File: LICENSE
11
- Keywords: postgresql,pydantic,sqlalchemy,sqlrules
12
- Classifier: Development Status :: 5 - Production/Stable
13
- Classifier: License :: OSI Approved :: MIT License
14
- Classifier: Programming Language :: Python :: 3
15
- Classifier: Typing :: Typed
16
- Requires-Python: >=3.10
17
- Requires-Dist: sqlalchemy<3,>=2.0
18
- Requires-Dist: sqlrules<2,>=1
19
- Provides-Extra: dev
20
- Requires-Dist: pytest>=8.0; extra == 'dev'
21
- Description-Content-Type: text/markdown
22
-
23
- # sqlrules-postgresql
24
-
25
- PostgreSQL dialect plugin for [SQLRules](https://github.com/eddiethedean/sqlrules).
26
-
27
- ## Install
28
-
29
- ```bash
30
- pip install sqlrules-postgresql
31
- ```
32
-
33
- ## Usage
34
-
35
- ```python
36
- import re
37
- from typing import Annotated, Any
38
-
39
- from pydantic import BaseModel, Field
40
- from sqlalchemy import Column, MetaData, Table
41
- from sqlalchemy.dialects.postgresql import ARRAY, INT4RANGE, JSONB, TEXT
42
-
43
- from sqlrules import ArrayContains, Compiler, JsonContains, RangeContains
44
- from sqlrules_postgresql import PostgresPlugin
45
-
46
- class RowFilter(BaseModel):
47
- name: Annotated[str, Field(pattern=re.compile(r"^a", re.I))]
48
- meta: Annotated[dict[str, Any], JsonContains({"active": True})]
49
- tags: Annotated[list[str], ArrayContains(["admin"])]
50
- span: Annotated[int, RangeContains(5)]
51
-
52
- table = Table(
53
- "rows",
54
- MetaData(),
55
- Column("name", TEXT),
56
- Column("meta", JSONB),
57
- Column("tags", ARRAY(TEXT)),
58
- Column("span", INT4RANGE),
59
- )
60
-
61
- compiler = Compiler(plugins=[PostgresPlugin()], dialect="postgresql")
62
- rules = compiler.compile(RowFilter, table)
63
- ```
64
-
65
- ## Operators
66
-
67
- | IR operator | SQLAlchemy / PostgreSQL |
68
- |---|---|
69
- | `pattern` | `~` or `~*` (when `PatternSpec.ignore_case`) |
70
- | `type_check` | Shape/type predicates from `TypeSpec` (see matrix below) |
71
- | `json_contains` | JSONB `contains` / `@>` |
72
- | `json_has_key` | JSONB `has_key` / `?` |
73
- | `array_contains` | array `contains` |
74
- | `array_overlap` | array `overlap` / `&&` |
75
- | `range_contains` | range `@>` |
76
- | `range_overlap` | range `&&` |
77
-
78
- ### `type_check` matrix (approximate)
79
-
80
- Enable with `Compiler(..., emit_type_checks=True)`. Not full Pydantic
81
- parity — inexpressible pairs raise.
82
-
83
- | Python type | Lax | Strict |
84
- |---|---|---|
85
- | `int` | Integer column; String `~` digit pattern; numeric whole-number | Integer column only |
86
- | `bool` | unsupported (raise) | Boolean `IN (true, false)` |
87
- | `str` | String/Text `IS NOT NULL` | same |
88
- | `float` / `Decimal` | numeric column; String float-ish `~` | numeric column |
89
- | `date` / `datetime` / `time` / `UUID` | typed column or String format `~` | typed column |
90
-
91
- `Optional[T]` → `(column IS NULL) OR <predicate>`.
92
-
93
- ## Security note
94
-
95
- `pattern` becomes a PostgreSQL regex (`~` / `~*`). Untrusted pattern strings
96
- can cause expensive engine-side evaluation (ReDoS-class cost). Prefer
97
- static/allowlisted patterns. See
98
- [SECURITY](https://sqlrules.readthedocs.io/en/latest/SECURITY.html).
@@ -1,11 +0,0 @@
1
- sqlrules_postgresql/__init__.py,sha256=Axgd7Vsg1PKCIMCi4iU9SaEtK3sePkM14v9dOVqLOL4,1805
2
- sqlrules_postgresql/array.py,sha256=Kzin-DQRwidhVsL6hL8c04ejSjEfGjcMZewjSHmH_3s,764
3
- sqlrules_postgresql/jsonb.py,sha256=SKuAD5S8RZZ27-ntbXSi1jVJ3b7Yw7K956DOUYFYUvU,750
4
- sqlrules_postgresql/pattern.py,sha256=swLYUSmd0sgW-XSYunYBBnoT2QpwkzDsXfuL38cgXqU,599
5
- sqlrules_postgresql/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- sqlrules_postgresql/range.py,sha256=WBvd5yHpkP2UNmU_TxCNdHj-HdTMYVPO_PztqWj6TUs,774
7
- sqlrules_postgresql/type_check.py,sha256=q7AA5hMiVLgDmdhfWt64123sBHcGG3ntkmprf-w58s8,7927
8
- sqlrules_postgresql-1.0.1.dist-info/METADATA,sha256=ILbicMQXIGVkPQ3gtYYNgLJvdf4FnvRTmlrMSJLHMeI,3232
9
- sqlrules_postgresql-1.0.1.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
10
- sqlrules_postgresql-1.0.1.dist-info/licenses/LICENSE,sha256=cLMlkCH6RhjWAkXBWR7LazV98TOG0WkPtCKB5zqqkAs,1078
11
- sqlrules_postgresql-1.0.1.dist-info/RECORD,,