pydoptic-sql 0.0.1.post1.dev2__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.
- pydoptic_sql/__init__.py +49 -0
- pydoptic_sql/py.typed +0 -0
- pydoptic_sql/sql_computed.py +93 -0
- pydoptic_sql/sql_constraint.py +782 -0
- pydoptic_sql/sql_having.py +728 -0
- pydoptic_sql/sql_order.py +28 -0
- pydoptic_sql/sql_query.py +1144 -0
- pydoptic_sql/sql_service.py +335 -0
- pydoptic_sql/sql_table.py +188 -0
- pydoptic_sql-0.0.1.post1.dev2.dist-info/METADATA +75 -0
- pydoptic_sql-0.0.1.post1.dev2.dist-info/RECORD +14 -0
- pydoptic_sql-0.0.1.post1.dev2.dist-info/WHEEL +5 -0
- pydoptic_sql-0.0.1.post1.dev2.dist-info/licenses/LICENSE +21 -0
- pydoptic_sql-0.0.1.post1.dev2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,782 @@
|
|
|
1
|
+
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Any, Generic, List, Sequence, Tuple, TypeVar
|
|
5
|
+
|
|
6
|
+
from pydoptic.selector import PropSelect, Prop, PropOpt
|
|
7
|
+
from pydoptic_sql import SqlTable
|
|
8
|
+
|
|
9
|
+
A = TypeVar('A')
|
|
10
|
+
TC = TypeVar('TC', bound=SqlTable, contravariant=True)
|
|
11
|
+
TC1 = TypeVar('TC1', bound=SqlTable, contravariant=True)
|
|
12
|
+
TC2 = TypeVar('TC2', bound=SqlTable, contravariant=True)
|
|
13
|
+
TC3 = TypeVar('TC3', bound=SqlTable, contravariant=True)
|
|
14
|
+
|
|
15
|
+
def _qualified_label(prop: PropSelect[Any, Any]) -> str:
|
|
16
|
+
return f'{prop.origin.__name__.lower()}.{prop.label}'
|
|
17
|
+
|
|
18
|
+
def _render_value(value: Any, qualify: bool) -> Tuple[str, List[Any]]:
|
|
19
|
+
"""
|
|
20
|
+
Render a constraint operand: a `PropSelect` becomes a (possibly table-qualified) column
|
|
21
|
+
reference with no params, anything else becomes a `%s` placeholder bound to that value --
|
|
22
|
+
letting psycopg's own adapters handle quoting/escaping instead of interpolating it as text.
|
|
23
|
+
"""
|
|
24
|
+
if isinstance(value, PropSelect):
|
|
25
|
+
return (_qualified_label(value) if qualify else value.label), []
|
|
26
|
+
return '%s', [value]
|
|
27
|
+
|
|
28
|
+
class Comparison(Enum):
|
|
29
|
+
EQ = 1
|
|
30
|
+
GT = 2
|
|
31
|
+
GTE = 3
|
|
32
|
+
LT = 4
|
|
33
|
+
LTE = 5
|
|
34
|
+
NE = 6
|
|
35
|
+
LIKE = 7
|
|
36
|
+
|
|
37
|
+
def _comparison_symbol(comp: Comparison) -> str:
|
|
38
|
+
match comp:
|
|
39
|
+
case Comparison.EQ:
|
|
40
|
+
return '='
|
|
41
|
+
case Comparison.LT:
|
|
42
|
+
return '<'
|
|
43
|
+
case Comparison.LTE:
|
|
44
|
+
return '<='
|
|
45
|
+
case Comparison.GT:
|
|
46
|
+
return '>'
|
|
47
|
+
case Comparison.GTE:
|
|
48
|
+
return '>='
|
|
49
|
+
case Comparison.LIKE:
|
|
50
|
+
return 'LIKE'
|
|
51
|
+
case Comparison.NE:
|
|
52
|
+
return '<>'
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# --- arity 1: constraints over a single table ---
|
|
56
|
+
|
|
57
|
+
class Constraint(Generic[TC]):
|
|
58
|
+
def to_sql(self) -> str:
|
|
59
|
+
raise NotImplementedError()
|
|
60
|
+
|
|
61
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
62
|
+
raise NotImplementedError()
|
|
63
|
+
|
|
64
|
+
def incr_arity(self) -> 'Constraint2[TC, TC1]':
|
|
65
|
+
raise NotImplementedError()
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def any(cls, *constraints: 'Constraint[TC]') -> 'OrConstraint[TC]':
|
|
69
|
+
return OrConstraint(list(constraints))
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def all(cls, *constraints: 'Constraint[TC]') -> 'AndConstraint[TC]':
|
|
73
|
+
return AndConstraint(list(constraints))
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def eq(cls, prop: Prop[TC, A] | PropOpt[TC, A], other: Prop[TC, A] | PropOpt[TC, A] | A) -> 'CompConstraint[TC, A]':
|
|
77
|
+
return CompConstraint(prop, other, Comparison.EQ)
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def gt(cls, prop: Prop[TC, A] | PropOpt[TC, A], other: Prop[TC, A] | PropOpt[TC, A] | A) -> 'CompConstraint[TC, A]':
|
|
81
|
+
return CompConstraint(prop, other, Comparison.GT)
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
def gte(cls, prop: Prop[TC, A] | PropOpt[TC, A], other: Prop[TC, A] | PropOpt[TC, A] | A) -> 'CompConstraint[TC, A]':
|
|
85
|
+
return CompConstraint(prop, other, Comparison.GTE)
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def lt(cls, prop: Prop[TC, A] | PropOpt[TC, A], other: Prop[TC, A] | PropOpt[TC, A] | A) -> 'CompConstraint[TC, A]':
|
|
89
|
+
return CompConstraint(prop, other, Comparison.LT)
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
def lte(cls, prop: Prop[TC, A] | PropOpt[TC, A], other: Prop[TC, A] | PropOpt[TC, A] | A) -> 'CompConstraint[TC, A]':
|
|
93
|
+
return CompConstraint(prop, other, Comparison.LTE)
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
def ne(cls, prop: Prop[TC, A] | PropOpt[TC, A], other: Prop[TC, A] | PropOpt[TC, A] | A) -> 'CompConstraint[TC, A]':
|
|
97
|
+
return CompConstraint(prop, other, Comparison.NE)
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def like(cls, prop: Prop[TC, A] | PropOpt[TC, A], other: Prop[TC, A] | PropOpt[TC, A] | A) -> 'CompConstraint[TC, A]':
|
|
101
|
+
return CompConstraint(prop, other, Comparison.LIKE)
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def between(cls, prop: Prop[TC, A] | PropOpt[TC, A], lower: Prop[TC, A] | PropOpt[TC, A] | A, upper: Prop[TC, A] | PropOpt[TC, A] | A) -> 'BetweenConstraint[TC, A]':
|
|
105
|
+
return BetweenConstraint(prop, lower, upper)
|
|
106
|
+
|
|
107
|
+
@classmethod
|
|
108
|
+
def in_(cls, prop: Prop[TC, A] | PropOpt[TC, A], values: Sequence[Prop[TC, A] | PropOpt[TC, A] | A]) -> 'InConstraint[TC, A]':
|
|
109
|
+
return InConstraint(prop, values)
|
|
110
|
+
|
|
111
|
+
def AND(self, other: 'Constraint[TC]') -> 'AndConstraint[TC]':
|
|
112
|
+
return AndConstraint([self, other])
|
|
113
|
+
|
|
114
|
+
def OR(self, other: 'Constraint[TC]') -> 'OrConstraint[TC]':
|
|
115
|
+
return OrConstraint([self, other])
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def NOT(self) -> 'NotConstraint[TC]':
|
|
119
|
+
return NotConstraint(self)
|
|
120
|
+
|
|
121
|
+
@dataclass(frozen=True)
|
|
122
|
+
class OrConstraint(Constraint[TC]):
|
|
123
|
+
constraints: List[Constraint[TC]]
|
|
124
|
+
|
|
125
|
+
def to_sql(self) -> str:
|
|
126
|
+
return '(' + ' OR '.join(c.to_sql() for c in self.constraints) + ')'
|
|
127
|
+
|
|
128
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
129
|
+
parts_params = [c.to_sql_params() for c in self.constraints]
|
|
130
|
+
sql = '(' + ' OR '.join(part for part, _ in parts_params) + ')'
|
|
131
|
+
params = [p for _, params in parts_params for p in params]
|
|
132
|
+
return sql, params
|
|
133
|
+
|
|
134
|
+
def incr_arity(self) -> 'OrConstraint2[TC, TC1]':
|
|
135
|
+
return OrConstraint2([c.incr_arity() for c in self.constraints])
|
|
136
|
+
|
|
137
|
+
@dataclass(frozen=True)
|
|
138
|
+
class AndConstraint(Constraint[TC]):
|
|
139
|
+
constraints: List[Constraint[TC]]
|
|
140
|
+
|
|
141
|
+
def to_sql(self) -> str:
|
|
142
|
+
return '(' + ' AND '.join(c.to_sql() for c in self.constraints) + ')'
|
|
143
|
+
|
|
144
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
145
|
+
parts_params = [c.to_sql_params() for c in self.constraints]
|
|
146
|
+
sql = '(' + ' AND '.join(part for part, _ in parts_params) + ')'
|
|
147
|
+
params = [p for _, params in parts_params for p in params]
|
|
148
|
+
return sql, params
|
|
149
|
+
|
|
150
|
+
def incr_arity(self) -> 'AndConstraint2[TC, TC1]':
|
|
151
|
+
return AndConstraint2([c.incr_arity() for c in self.constraints])
|
|
152
|
+
|
|
153
|
+
@dataclass(frozen=True)
|
|
154
|
+
class NotConstraint(Constraint[TC]):
|
|
155
|
+
constraint: Constraint[TC]
|
|
156
|
+
|
|
157
|
+
def to_sql(self) -> str:
|
|
158
|
+
return 'NOT (' + self.constraint.to_sql() + ')'
|
|
159
|
+
|
|
160
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
161
|
+
sql, params = self.constraint.to_sql_params()
|
|
162
|
+
return 'NOT (' + sql + ')', params
|
|
163
|
+
|
|
164
|
+
def incr_arity(self) -> 'NotConstraint2[TC, TC1]':
|
|
165
|
+
return NotConstraint2(self.constraint.incr_arity())
|
|
166
|
+
|
|
167
|
+
@dataclass(frozen=True)
|
|
168
|
+
class CompConstraint(Generic[TC, A], Constraint[TC]):
|
|
169
|
+
value_1: Prop[TC, A] | PropOpt[TC, A]
|
|
170
|
+
value_2: Prop[TC, A] | PropOpt[TC, A] | A
|
|
171
|
+
comp: Comparison
|
|
172
|
+
|
|
173
|
+
def to_sql(self) -> str:
|
|
174
|
+
if isinstance(self.value_2, PropSelect):
|
|
175
|
+
value_2 = self.value_2.label
|
|
176
|
+
else:
|
|
177
|
+
value_2 = str(self.value_2)
|
|
178
|
+
if self.value_1.target is str:
|
|
179
|
+
value_2 = "'" + value_2 + "'"
|
|
180
|
+
return self.value_1.label + ' ' + _comparison_symbol(self.comp) + ' ' + value_2
|
|
181
|
+
|
|
182
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
183
|
+
value_2, params = _render_value(self.value_2, qualify=False)
|
|
184
|
+
return self.value_1.label + ' ' + _comparison_symbol(self.comp) + ' ' + value_2, params
|
|
185
|
+
|
|
186
|
+
def incr_arity(self) -> 'CompConstraint2[TC, TC1, A]':
|
|
187
|
+
return CompConstraint2(self.value_1, self.value_2, self.comp)
|
|
188
|
+
|
|
189
|
+
@dataclass(frozen=True)
|
|
190
|
+
class BetweenConstraint(Generic[TC, A], Constraint[TC]):
|
|
191
|
+
value: Prop[TC, A] | PropOpt[TC, A]
|
|
192
|
+
lower: Prop[TC, A] | PropOpt[TC, A] | A
|
|
193
|
+
upper: Prop[TC, A] | PropOpt[TC, A] | A
|
|
194
|
+
|
|
195
|
+
def to_sql(self) -> str:
|
|
196
|
+
lower = self.lower.label if isinstance(self.lower, PropSelect) else str(self.lower)
|
|
197
|
+
upper = self.upper.label if isinstance(self.upper, PropSelect) else str(self.upper)
|
|
198
|
+
return f'{self.value.label} BETWEEN {lower} AND {upper}'
|
|
199
|
+
|
|
200
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
201
|
+
lower, lower_params = _render_value(self.lower, qualify=False)
|
|
202
|
+
upper, upper_params = _render_value(self.upper, qualify=False)
|
|
203
|
+
return f'{self.value.label} BETWEEN {lower} AND {upper}', lower_params + upper_params
|
|
204
|
+
|
|
205
|
+
def incr_arity(self) -> 'BetweenConstraint2[TC, TC1, A]':
|
|
206
|
+
return BetweenConstraint2(self.value, self.lower, self.upper)
|
|
207
|
+
|
|
208
|
+
@dataclass(frozen=True)
|
|
209
|
+
class InConstraint(Generic[TC, A], Constraint[TC]):
|
|
210
|
+
value: Prop[TC, A] | PropOpt[TC, A]
|
|
211
|
+
values: Sequence[Prop[TC, A] | PropOpt[TC, A] | A]
|
|
212
|
+
|
|
213
|
+
def to_sql(self) -> str:
|
|
214
|
+
def render(c: Prop[TC, A] | PropOpt[TC, A] | A) -> str:
|
|
215
|
+
if isinstance(c, PropSelect):
|
|
216
|
+
return c.label
|
|
217
|
+
value = str(c)
|
|
218
|
+
if self.value.target is str:
|
|
219
|
+
value = "'" + value + "'"
|
|
220
|
+
return value
|
|
221
|
+
|
|
222
|
+
values = [render(c) for c in self.values]
|
|
223
|
+
return f'{self.value.label} IN ({", ".join(values)})'
|
|
224
|
+
|
|
225
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
226
|
+
rendered = [_render_value(c, qualify=False) for c in self.values]
|
|
227
|
+
sql = f'{self.value.label} IN ({", ".join(part for part, _ in rendered)})'
|
|
228
|
+
params = [p for _, params in rendered for p in params]
|
|
229
|
+
return sql, params
|
|
230
|
+
|
|
231
|
+
def incr_arity(self) -> 'InConstraint2[TC, TC1, A]':
|
|
232
|
+
return InConstraint2(self.value, self.values)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# --- arity 2: constraints over a pair of joined tables ---
|
|
236
|
+
# Column references always render table-qualified ("table.column"), since a constraint here may
|
|
237
|
+
# reference either table and there's no other way to disambiguate.
|
|
238
|
+
|
|
239
|
+
class Constraint2(Generic[TC, TC1]):
|
|
240
|
+
def to_sql(self) -> str:
|
|
241
|
+
raise NotImplementedError()
|
|
242
|
+
|
|
243
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
244
|
+
raise NotImplementedError()
|
|
245
|
+
|
|
246
|
+
def incr_arity(self) -> 'Constraint3[TC, TC1, TC2]':
|
|
247
|
+
raise NotImplementedError()
|
|
248
|
+
|
|
249
|
+
@classmethod
|
|
250
|
+
def any(cls, *constraints: 'Constraint2[TC, TC1]') -> 'OrConstraint2[TC, TC1]':
|
|
251
|
+
return OrConstraint2(list(constraints))
|
|
252
|
+
|
|
253
|
+
@classmethod
|
|
254
|
+
def all(cls, *constraints: 'Constraint2[TC, TC1]') -> 'AndConstraint2[TC, TC1]':
|
|
255
|
+
return AndConstraint2(list(constraints))
|
|
256
|
+
|
|
257
|
+
@classmethod
|
|
258
|
+
def eq(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | A) -> 'CompConstraint2[TC, TC1, A]':
|
|
259
|
+
return CompConstraint2(prop, other, Comparison.EQ)
|
|
260
|
+
|
|
261
|
+
@classmethod
|
|
262
|
+
def gt(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | A) -> 'CompConstraint2[TC, TC1, A]':
|
|
263
|
+
return CompConstraint2(prop, other, Comparison.GT)
|
|
264
|
+
|
|
265
|
+
@classmethod
|
|
266
|
+
def gte(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | A) -> 'CompConstraint2[TC, TC1, A]':
|
|
267
|
+
return CompConstraint2(prop, other, Comparison.GTE)
|
|
268
|
+
|
|
269
|
+
@classmethod
|
|
270
|
+
def lt(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | A) -> 'CompConstraint2[TC, TC1, A]':
|
|
271
|
+
return CompConstraint2(prop, other, Comparison.LT)
|
|
272
|
+
|
|
273
|
+
@classmethod
|
|
274
|
+
def lte(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | A) -> 'CompConstraint2[TC, TC1, A]':
|
|
275
|
+
return CompConstraint2(prop, other, Comparison.LTE)
|
|
276
|
+
|
|
277
|
+
@classmethod
|
|
278
|
+
def ne(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | A) -> 'CompConstraint2[TC, TC1, A]':
|
|
279
|
+
return CompConstraint2(prop, other, Comparison.NE)
|
|
280
|
+
|
|
281
|
+
@classmethod
|
|
282
|
+
def like(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | A) -> 'CompConstraint2[TC, TC1, A]':
|
|
283
|
+
return CompConstraint2(prop, other, Comparison.LIKE)
|
|
284
|
+
|
|
285
|
+
@classmethod
|
|
286
|
+
def between(
|
|
287
|
+
cls,
|
|
288
|
+
prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A],
|
|
289
|
+
lower: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | A,
|
|
290
|
+
upper: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | A,
|
|
291
|
+
) -> 'BetweenConstraint2[TC, TC1, A]':
|
|
292
|
+
return BetweenConstraint2(prop, lower, upper)
|
|
293
|
+
|
|
294
|
+
@classmethod
|
|
295
|
+
def in_(
|
|
296
|
+
cls,
|
|
297
|
+
prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A],
|
|
298
|
+
values: Sequence[Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | A],
|
|
299
|
+
) -> 'InConstraint2[TC, TC1, A]':
|
|
300
|
+
return InConstraint2(prop, values)
|
|
301
|
+
|
|
302
|
+
def AND(self, other: 'Constraint2[TC, TC1]') -> 'AndConstraint2[TC, TC1]':
|
|
303
|
+
return AndConstraint2([self, other])
|
|
304
|
+
|
|
305
|
+
def OR(self, other: 'Constraint2[TC, TC1]') -> 'OrConstraint2[TC, TC1]':
|
|
306
|
+
return OrConstraint2([self, other])
|
|
307
|
+
|
|
308
|
+
@property
|
|
309
|
+
def NOT(self) -> 'NotConstraint2[TC, TC1]':
|
|
310
|
+
return NotConstraint2(self)
|
|
311
|
+
|
|
312
|
+
@dataclass(frozen=True)
|
|
313
|
+
class OrConstraint2(Constraint2[TC, TC1]):
|
|
314
|
+
constraints: List[Constraint2[TC, TC1]]
|
|
315
|
+
|
|
316
|
+
def to_sql(self) -> str:
|
|
317
|
+
return '(' + ' OR '.join(c.to_sql() for c in self.constraints) + ')'
|
|
318
|
+
|
|
319
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
320
|
+
parts_params = [c.to_sql_params() for c in self.constraints]
|
|
321
|
+
sql = '(' + ' OR '.join(part for part, _ in parts_params) + ')'
|
|
322
|
+
params = [p for _, params in parts_params for p in params]
|
|
323
|
+
return sql, params
|
|
324
|
+
|
|
325
|
+
def incr_arity(self) -> 'OrConstraint3[TC, TC1, TC2]':
|
|
326
|
+
return OrConstraint3([c.incr_arity() for c in self.constraints])
|
|
327
|
+
|
|
328
|
+
@dataclass(frozen=True)
|
|
329
|
+
class AndConstraint2(Constraint2[TC, TC1]):
|
|
330
|
+
constraints: List[Constraint2[TC, TC1]]
|
|
331
|
+
|
|
332
|
+
def to_sql(self) -> str:
|
|
333
|
+
return '(' + ' AND '.join(c.to_sql() for c in self.constraints) + ')'
|
|
334
|
+
|
|
335
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
336
|
+
parts_params = [c.to_sql_params() for c in self.constraints]
|
|
337
|
+
sql = '(' + ' AND '.join(part for part, _ in parts_params) + ')'
|
|
338
|
+
params = [p for _, params in parts_params for p in params]
|
|
339
|
+
return sql, params
|
|
340
|
+
|
|
341
|
+
def incr_arity(self) -> 'AndConstraint3[TC, TC1, TC2]':
|
|
342
|
+
return AndConstraint3([c.incr_arity() for c in self.constraints])
|
|
343
|
+
|
|
344
|
+
@dataclass(frozen=True)
|
|
345
|
+
class NotConstraint2(Constraint2[TC, TC1]):
|
|
346
|
+
constraint: Constraint2[TC, TC1]
|
|
347
|
+
|
|
348
|
+
def to_sql(self) -> str:
|
|
349
|
+
return 'NOT (' + self.constraint.to_sql() + ')'
|
|
350
|
+
|
|
351
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
352
|
+
sql, params = self.constraint.to_sql_params()
|
|
353
|
+
return 'NOT (' + sql + ')', params
|
|
354
|
+
|
|
355
|
+
def incr_arity(self) -> 'NotConstraint3[TC, TC1, TC2]':
|
|
356
|
+
return NotConstraint3(self.constraint.incr_arity())
|
|
357
|
+
|
|
358
|
+
@dataclass(frozen=True)
|
|
359
|
+
class CompConstraint2(Generic[TC, TC1, A], Constraint2[TC, TC1]):
|
|
360
|
+
value_1: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A]
|
|
361
|
+
value_2: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | A
|
|
362
|
+
comp: Comparison
|
|
363
|
+
|
|
364
|
+
def to_sql(self) -> str:
|
|
365
|
+
if isinstance(self.value_2, PropSelect):
|
|
366
|
+
value_2 = _qualified_label(self.value_2)
|
|
367
|
+
else:
|
|
368
|
+
value_2 = str(self.value_2)
|
|
369
|
+
if self.value_1.target is str:
|
|
370
|
+
value_2 = "'" + value_2 + "'"
|
|
371
|
+
return _qualified_label(self.value_1) + ' ' + _comparison_symbol(self.comp) + ' ' + value_2
|
|
372
|
+
|
|
373
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
374
|
+
value_2, params = _render_value(self.value_2, qualify=True)
|
|
375
|
+
return _qualified_label(self.value_1) + ' ' + _comparison_symbol(self.comp) + ' ' + value_2, params
|
|
376
|
+
|
|
377
|
+
def incr_arity(self) -> 'CompConstraint3[TC, TC1, TC2, A]':
|
|
378
|
+
return CompConstraint3(self.value_1, self.value_2, self.comp)
|
|
379
|
+
|
|
380
|
+
@dataclass(frozen=True)
|
|
381
|
+
class BetweenConstraint2(Generic[TC, TC1, A], Constraint2[TC, TC1]):
|
|
382
|
+
value: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A]
|
|
383
|
+
lower: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | A
|
|
384
|
+
upper: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | A
|
|
385
|
+
|
|
386
|
+
def to_sql(self) -> str:
|
|
387
|
+
lower = _qualified_label(self.lower) if isinstance(self.lower, PropSelect) else str(self.lower)
|
|
388
|
+
upper = _qualified_label(self.upper) if isinstance(self.upper, PropSelect) else str(self.upper)
|
|
389
|
+
return f'{_qualified_label(self.value)} BETWEEN {lower} AND {upper}'
|
|
390
|
+
|
|
391
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
392
|
+
lower, lower_params = _render_value(self.lower, qualify=True)
|
|
393
|
+
upper, upper_params = _render_value(self.upper, qualify=True)
|
|
394
|
+
return f'{_qualified_label(self.value)} BETWEEN {lower} AND {upper}', lower_params + upper_params
|
|
395
|
+
|
|
396
|
+
def incr_arity(self) -> 'BetweenConstraint3[TC, TC1, TC2, A]':
|
|
397
|
+
return BetweenConstraint3(self.value, self.lower, self.upper)
|
|
398
|
+
|
|
399
|
+
@dataclass(frozen=True)
|
|
400
|
+
class InConstraint2(Generic[TC, TC1, A], Constraint2[TC, TC1]):
|
|
401
|
+
value: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A]
|
|
402
|
+
values: Sequence[Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | A]
|
|
403
|
+
|
|
404
|
+
def to_sql(self) -> str:
|
|
405
|
+
def render(c: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | A) -> str:
|
|
406
|
+
if isinstance(c, PropSelect):
|
|
407
|
+
return _qualified_label(c)
|
|
408
|
+
value = str(c)
|
|
409
|
+
if self.value.target is str:
|
|
410
|
+
value = "'" + value + "'"
|
|
411
|
+
return value
|
|
412
|
+
|
|
413
|
+
values = [render(c) for c in self.values]
|
|
414
|
+
return f'{_qualified_label(self.value)} IN ({", ".join(values)})'
|
|
415
|
+
|
|
416
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
417
|
+
rendered = [_render_value(c, qualify=True) for c in self.values]
|
|
418
|
+
sql = f'{_qualified_label(self.value)} IN ({", ".join(part for part, _ in rendered)})'
|
|
419
|
+
params = [p for _, params in rendered for p in params]
|
|
420
|
+
return sql, params
|
|
421
|
+
|
|
422
|
+
def incr_arity(self) -> 'InConstraint3[TC, TC1, TC2, A]':
|
|
423
|
+
return InConstraint3(self.value, self.values)
|
|
424
|
+
|
|
425
|
+
# --- arity 3: constraints over 3 joined tables ---
|
|
426
|
+
# Column references always render table-qualified ("table.column"), since a constraint here may
|
|
427
|
+
# reference any of the joined tables and there's no other way to disambiguate.
|
|
428
|
+
|
|
429
|
+
class Constraint3(Generic[TC, TC1, TC2]):
|
|
430
|
+
def to_sql(self) -> str:
|
|
431
|
+
raise NotImplementedError()
|
|
432
|
+
|
|
433
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
434
|
+
raise NotImplementedError()
|
|
435
|
+
|
|
436
|
+
def incr_arity(self) -> 'Constraint4[TC, TC1, TC2, TC3]':
|
|
437
|
+
raise NotImplementedError()
|
|
438
|
+
|
|
439
|
+
@classmethod
|
|
440
|
+
def any(cls, *constraints: 'Constraint3[TC, TC1, TC2]') -> 'OrConstraint3[TC, TC1, TC2]':
|
|
441
|
+
return OrConstraint3(list(constraints))
|
|
442
|
+
|
|
443
|
+
@classmethod
|
|
444
|
+
def all(cls, *constraints: 'Constraint3[TC, TC1, TC2]') -> 'AndConstraint3[TC, TC1, TC2]':
|
|
445
|
+
return AndConstraint3(list(constraints))
|
|
446
|
+
|
|
447
|
+
@classmethod
|
|
448
|
+
def eq(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | A) -> 'CompConstraint3[TC, TC1, TC2, A]':
|
|
449
|
+
return CompConstraint3(prop, other, Comparison.EQ)
|
|
450
|
+
|
|
451
|
+
@classmethod
|
|
452
|
+
def gt(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | A) -> 'CompConstraint3[TC, TC1, TC2, A]':
|
|
453
|
+
return CompConstraint3(prop, other, Comparison.GT)
|
|
454
|
+
|
|
455
|
+
@classmethod
|
|
456
|
+
def gte(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | A) -> 'CompConstraint3[TC, TC1, TC2, A]':
|
|
457
|
+
return CompConstraint3(prop, other, Comparison.GTE)
|
|
458
|
+
|
|
459
|
+
@classmethod
|
|
460
|
+
def lt(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | A) -> 'CompConstraint3[TC, TC1, TC2, A]':
|
|
461
|
+
return CompConstraint3(prop, other, Comparison.LT)
|
|
462
|
+
|
|
463
|
+
@classmethod
|
|
464
|
+
def lte(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | A) -> 'CompConstraint3[TC, TC1, TC2, A]':
|
|
465
|
+
return CompConstraint3(prop, other, Comparison.LTE)
|
|
466
|
+
|
|
467
|
+
@classmethod
|
|
468
|
+
def ne(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | A) -> 'CompConstraint3[TC, TC1, TC2, A]':
|
|
469
|
+
return CompConstraint3(prop, other, Comparison.NE)
|
|
470
|
+
|
|
471
|
+
@classmethod
|
|
472
|
+
def like(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | A) -> 'CompConstraint3[TC, TC1, TC2, A]':
|
|
473
|
+
return CompConstraint3(prop, other, Comparison.LIKE)
|
|
474
|
+
|
|
475
|
+
@classmethod
|
|
476
|
+
def between(
|
|
477
|
+
cls,
|
|
478
|
+
prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A],
|
|
479
|
+
lower: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | A,
|
|
480
|
+
upper: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | A,
|
|
481
|
+
) -> 'BetweenConstraint3[TC, TC1, TC2, A]':
|
|
482
|
+
return BetweenConstraint3(prop, lower, upper)
|
|
483
|
+
|
|
484
|
+
@classmethod
|
|
485
|
+
def in_(
|
|
486
|
+
cls,
|
|
487
|
+
prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A],
|
|
488
|
+
values: Sequence[Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | A],
|
|
489
|
+
) -> 'InConstraint3[TC, TC1, TC2, A]':
|
|
490
|
+
return InConstraint3(prop, values)
|
|
491
|
+
|
|
492
|
+
def AND(self, other: 'Constraint3[TC, TC1, TC2]') -> 'AndConstraint3[TC, TC1, TC2]':
|
|
493
|
+
return AndConstraint3([self, other])
|
|
494
|
+
|
|
495
|
+
def OR(self, other: 'Constraint3[TC, TC1, TC2]') -> 'OrConstraint3[TC, TC1, TC2]':
|
|
496
|
+
return OrConstraint3([self, other])
|
|
497
|
+
|
|
498
|
+
@property
|
|
499
|
+
def NOT(self) -> 'NotConstraint3[TC, TC1, TC2]':
|
|
500
|
+
return NotConstraint3(self)
|
|
501
|
+
|
|
502
|
+
@dataclass(frozen=True)
|
|
503
|
+
class OrConstraint3(Constraint3[TC, TC1, TC2]):
|
|
504
|
+
constraints: List[Constraint3[TC, TC1, TC2]]
|
|
505
|
+
|
|
506
|
+
def to_sql(self) -> str:
|
|
507
|
+
return '(' + ' OR '.join(c.to_sql() for c in self.constraints) + ')'
|
|
508
|
+
|
|
509
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
510
|
+
parts_params = [c.to_sql_params() for c in self.constraints]
|
|
511
|
+
sql = '(' + ' OR '.join(part for part, _ in parts_params) + ')'
|
|
512
|
+
params = [p for _, params in parts_params for p in params]
|
|
513
|
+
return sql, params
|
|
514
|
+
|
|
515
|
+
def incr_arity(self) -> 'OrConstraint4[TC, TC1, TC2, TC3]':
|
|
516
|
+
return OrConstraint4([c.incr_arity() for c in self.constraints])
|
|
517
|
+
|
|
518
|
+
@dataclass(frozen=True)
|
|
519
|
+
class AndConstraint3(Constraint3[TC, TC1, TC2]):
|
|
520
|
+
constraints: List[Constraint3[TC, TC1, TC2]]
|
|
521
|
+
|
|
522
|
+
def to_sql(self) -> str:
|
|
523
|
+
return '(' + ' AND '.join(c.to_sql() for c in self.constraints) + ')'
|
|
524
|
+
|
|
525
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
526
|
+
parts_params = [c.to_sql_params() for c in self.constraints]
|
|
527
|
+
sql = '(' + ' AND '.join(part for part, _ in parts_params) + ')'
|
|
528
|
+
params = [p for _, params in parts_params for p in params]
|
|
529
|
+
return sql, params
|
|
530
|
+
|
|
531
|
+
def incr_arity(self) -> 'AndConstraint4[TC, TC1, TC2, TC3]':
|
|
532
|
+
return AndConstraint4([c.incr_arity() for c in self.constraints])
|
|
533
|
+
|
|
534
|
+
@dataclass(frozen=True)
|
|
535
|
+
class NotConstraint3(Constraint3[TC, TC1, TC2]):
|
|
536
|
+
constraint: Constraint3[TC, TC1, TC2]
|
|
537
|
+
|
|
538
|
+
def to_sql(self) -> str:
|
|
539
|
+
return 'NOT (' + self.constraint.to_sql() + ')'
|
|
540
|
+
|
|
541
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
542
|
+
sql, params = self.constraint.to_sql_params()
|
|
543
|
+
return 'NOT (' + sql + ')', params
|
|
544
|
+
|
|
545
|
+
def incr_arity(self) -> 'NotConstraint4[TC, TC1, TC2, TC3]':
|
|
546
|
+
return NotConstraint4(self.constraint.incr_arity())
|
|
547
|
+
|
|
548
|
+
@dataclass(frozen=True)
|
|
549
|
+
class CompConstraint3(Generic[TC, TC1, TC2, A], Constraint3[TC, TC1, TC2]):
|
|
550
|
+
value_1: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A]
|
|
551
|
+
value_2: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | A
|
|
552
|
+
comp: Comparison
|
|
553
|
+
|
|
554
|
+
def to_sql(self) -> str:
|
|
555
|
+
if isinstance(self.value_2, PropSelect):
|
|
556
|
+
value_2 = _qualified_label(self.value_2)
|
|
557
|
+
else:
|
|
558
|
+
value_2 = str(self.value_2)
|
|
559
|
+
if self.value_1.target is str:
|
|
560
|
+
value_2 = "'" + value_2 + "'"
|
|
561
|
+
return _qualified_label(self.value_1) + ' ' + _comparison_symbol(self.comp) + ' ' + value_2
|
|
562
|
+
|
|
563
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
564
|
+
value_2, params = _render_value(self.value_2, qualify=True)
|
|
565
|
+
return _qualified_label(self.value_1) + ' ' + _comparison_symbol(self.comp) + ' ' + value_2, params
|
|
566
|
+
|
|
567
|
+
def incr_arity(self) -> 'CompConstraint4[TC, TC1, TC2, TC3, A]':
|
|
568
|
+
return CompConstraint4(self.value_1, self.value_2, self.comp)
|
|
569
|
+
|
|
570
|
+
@dataclass(frozen=True)
|
|
571
|
+
class BetweenConstraint3(Generic[TC, TC1, TC2, A], Constraint3[TC, TC1, TC2]):
|
|
572
|
+
value: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A]
|
|
573
|
+
lower: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | A
|
|
574
|
+
upper: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | A
|
|
575
|
+
|
|
576
|
+
def to_sql(self) -> str:
|
|
577
|
+
lower = _qualified_label(self.lower) if isinstance(self.lower, PropSelect) else str(self.lower)
|
|
578
|
+
upper = _qualified_label(self.upper) if isinstance(self.upper, PropSelect) else str(self.upper)
|
|
579
|
+
return f'{_qualified_label(self.value)} BETWEEN {lower} AND {upper}'
|
|
580
|
+
|
|
581
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
582
|
+
lower, lower_params = _render_value(self.lower, qualify=True)
|
|
583
|
+
upper, upper_params = _render_value(self.upper, qualify=True)
|
|
584
|
+
return f'{_qualified_label(self.value)} BETWEEN {lower} AND {upper}', lower_params + upper_params
|
|
585
|
+
|
|
586
|
+
def incr_arity(self) -> 'BetweenConstraint4[TC, TC1, TC2, TC3, A]':
|
|
587
|
+
return BetweenConstraint4(self.value, self.lower, self.upper)
|
|
588
|
+
|
|
589
|
+
@dataclass(frozen=True)
|
|
590
|
+
class InConstraint3(Generic[TC, TC1, TC2, A], Constraint3[TC, TC1, TC2]):
|
|
591
|
+
value: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A]
|
|
592
|
+
values: Sequence[Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | A]
|
|
593
|
+
|
|
594
|
+
def to_sql(self) -> str:
|
|
595
|
+
def render(c: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | A) -> str:
|
|
596
|
+
if isinstance(c, PropSelect):
|
|
597
|
+
return _qualified_label(c)
|
|
598
|
+
value = str(c)
|
|
599
|
+
if self.value.target is str:
|
|
600
|
+
value = "'" + value + "'"
|
|
601
|
+
return value
|
|
602
|
+
|
|
603
|
+
values = [render(c) for c in self.values]
|
|
604
|
+
return f'{_qualified_label(self.value)} IN ({", ".join(values)})'
|
|
605
|
+
|
|
606
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
607
|
+
rendered = [_render_value(c, qualify=True) for c in self.values]
|
|
608
|
+
sql = f'{_qualified_label(self.value)} IN ({", ".join(part for part, _ in rendered)})'
|
|
609
|
+
params = [p for _, params in rendered for p in params]
|
|
610
|
+
return sql, params
|
|
611
|
+
|
|
612
|
+
def incr_arity(self) -> 'InConstraint4[TC, TC1, TC2, TC3, A]':
|
|
613
|
+
return InConstraint4(self.value, self.values)
|
|
614
|
+
|
|
615
|
+
# --- arity 4: constraints over 4 joined tables ---
|
|
616
|
+
# Column references always render table-qualified ("table.column"), since a constraint here may
|
|
617
|
+
# reference any of the joined tables and there's no other way to disambiguate.
|
|
618
|
+
|
|
619
|
+
class Constraint4(Generic[TC, TC1, TC2, TC3]):
|
|
620
|
+
def to_sql(self) -> str:
|
|
621
|
+
raise NotImplementedError()
|
|
622
|
+
|
|
623
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
624
|
+
raise NotImplementedError()
|
|
625
|
+
|
|
626
|
+
@classmethod
|
|
627
|
+
def any(cls, *constraints: 'Constraint4[TC, TC1, TC2, TC3]') -> 'OrConstraint4[TC, TC1, TC2, TC3]':
|
|
628
|
+
return OrConstraint4(list(constraints))
|
|
629
|
+
|
|
630
|
+
@classmethod
|
|
631
|
+
def all(cls, *constraints: 'Constraint4[TC, TC1, TC2, TC3]') -> 'AndConstraint4[TC, TC1, TC2, TC3]':
|
|
632
|
+
return AndConstraint4(list(constraints))
|
|
633
|
+
|
|
634
|
+
@classmethod
|
|
635
|
+
def eq(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | A) -> 'CompConstraint4[TC, TC1, TC2, TC3, A]':
|
|
636
|
+
return CompConstraint4(prop, other, Comparison.EQ)
|
|
637
|
+
|
|
638
|
+
@classmethod
|
|
639
|
+
def gt(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | A) -> 'CompConstraint4[TC, TC1, TC2, TC3, A]':
|
|
640
|
+
return CompConstraint4(prop, other, Comparison.GT)
|
|
641
|
+
|
|
642
|
+
@classmethod
|
|
643
|
+
def gte(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | A) -> 'CompConstraint4[TC, TC1, TC2, TC3, A]':
|
|
644
|
+
return CompConstraint4(prop, other, Comparison.GTE)
|
|
645
|
+
|
|
646
|
+
@classmethod
|
|
647
|
+
def lt(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | A) -> 'CompConstraint4[TC, TC1, TC2, TC3, A]':
|
|
648
|
+
return CompConstraint4(prop, other, Comparison.LT)
|
|
649
|
+
|
|
650
|
+
@classmethod
|
|
651
|
+
def lte(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | A) -> 'CompConstraint4[TC, TC1, TC2, TC3, A]':
|
|
652
|
+
return CompConstraint4(prop, other, Comparison.LTE)
|
|
653
|
+
|
|
654
|
+
@classmethod
|
|
655
|
+
def ne(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | A) -> 'CompConstraint4[TC, TC1, TC2, TC3, A]':
|
|
656
|
+
return CompConstraint4(prop, other, Comparison.NE)
|
|
657
|
+
|
|
658
|
+
@classmethod
|
|
659
|
+
def like(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A], other: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | A) -> 'CompConstraint4[TC, TC1, TC2, TC3, A]':
|
|
660
|
+
return CompConstraint4(prop, other, Comparison.LIKE)
|
|
661
|
+
|
|
662
|
+
@classmethod
|
|
663
|
+
def between(
|
|
664
|
+
cls,
|
|
665
|
+
prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A],
|
|
666
|
+
lower: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | A,
|
|
667
|
+
upper: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | A,
|
|
668
|
+
) -> 'BetweenConstraint4[TC, TC1, TC2, TC3, A]':
|
|
669
|
+
return BetweenConstraint4(prop, lower, upper)
|
|
670
|
+
|
|
671
|
+
@classmethod
|
|
672
|
+
def in_(
|
|
673
|
+
cls,
|
|
674
|
+
prop: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A],
|
|
675
|
+
values: Sequence[Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | A],
|
|
676
|
+
) -> 'InConstraint4[TC, TC1, TC2, TC3, A]':
|
|
677
|
+
return InConstraint4(prop, values)
|
|
678
|
+
|
|
679
|
+
def AND(self, other: 'Constraint4[TC, TC1, TC2, TC3]') -> 'AndConstraint4[TC, TC1, TC2, TC3]':
|
|
680
|
+
return AndConstraint4([self, other])
|
|
681
|
+
|
|
682
|
+
def OR(self, other: 'Constraint4[TC, TC1, TC2, TC3]') -> 'OrConstraint4[TC, TC1, TC2, TC3]':
|
|
683
|
+
return OrConstraint4([self, other])
|
|
684
|
+
|
|
685
|
+
@property
|
|
686
|
+
def NOT(self) -> 'NotConstraint4[TC, TC1, TC2, TC3]':
|
|
687
|
+
return NotConstraint4(self)
|
|
688
|
+
|
|
689
|
+
@dataclass(frozen=True)
|
|
690
|
+
class OrConstraint4(Constraint4[TC, TC1, TC2, TC3]):
|
|
691
|
+
constraints: List[Constraint4[TC, TC1, TC2, TC3]]
|
|
692
|
+
|
|
693
|
+
def to_sql(self) -> str:
|
|
694
|
+
return '(' + ' OR '.join(c.to_sql() for c in self.constraints) + ')'
|
|
695
|
+
|
|
696
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
697
|
+
parts_params = [c.to_sql_params() for c in self.constraints]
|
|
698
|
+
sql = '(' + ' OR '.join(part for part, _ in parts_params) + ')'
|
|
699
|
+
params = [p for _, params in parts_params for p in params]
|
|
700
|
+
return sql, params
|
|
701
|
+
|
|
702
|
+
@dataclass(frozen=True)
|
|
703
|
+
class AndConstraint4(Constraint4[TC, TC1, TC2, TC3]):
|
|
704
|
+
constraints: List[Constraint4[TC, TC1, TC2, TC3]]
|
|
705
|
+
|
|
706
|
+
def to_sql(self) -> str:
|
|
707
|
+
return '(' + ' AND '.join(c.to_sql() for c in self.constraints) + ')'
|
|
708
|
+
|
|
709
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
710
|
+
parts_params = [c.to_sql_params() for c in self.constraints]
|
|
711
|
+
sql = '(' + ' AND '.join(part for part, _ in parts_params) + ')'
|
|
712
|
+
params = [p for _, params in parts_params for p in params]
|
|
713
|
+
return sql, params
|
|
714
|
+
|
|
715
|
+
@dataclass(frozen=True)
|
|
716
|
+
class NotConstraint4(Constraint4[TC, TC1, TC2, TC3]):
|
|
717
|
+
constraint: Constraint4[TC, TC1, TC2, TC3]
|
|
718
|
+
|
|
719
|
+
def to_sql(self) -> str:
|
|
720
|
+
return 'NOT (' + self.constraint.to_sql() + ')'
|
|
721
|
+
|
|
722
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
723
|
+
sql, params = self.constraint.to_sql_params()
|
|
724
|
+
return 'NOT (' + sql + ')', params
|
|
725
|
+
|
|
726
|
+
@dataclass(frozen=True)
|
|
727
|
+
class CompConstraint4(Generic[TC, TC1, TC2, TC3, A], Constraint4[TC, TC1, TC2, TC3]):
|
|
728
|
+
value_1: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A]
|
|
729
|
+
value_2: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | A
|
|
730
|
+
comp: Comparison
|
|
731
|
+
|
|
732
|
+
def to_sql(self) -> str:
|
|
733
|
+
if isinstance(self.value_2, PropSelect):
|
|
734
|
+
value_2 = _qualified_label(self.value_2)
|
|
735
|
+
else:
|
|
736
|
+
value_2 = str(self.value_2)
|
|
737
|
+
if self.value_1.target is str:
|
|
738
|
+
value_2 = "'" + value_2 + "'"
|
|
739
|
+
return _qualified_label(self.value_1) + ' ' + _comparison_symbol(self.comp) + ' ' + value_2
|
|
740
|
+
|
|
741
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
742
|
+
value_2, params = _render_value(self.value_2, qualify=True)
|
|
743
|
+
return _qualified_label(self.value_1) + ' ' + _comparison_symbol(self.comp) + ' ' + value_2, params
|
|
744
|
+
|
|
745
|
+
@dataclass(frozen=True)
|
|
746
|
+
class BetweenConstraint4(Generic[TC, TC1, TC2, TC3, A], Constraint4[TC, TC1, TC2, TC3]):
|
|
747
|
+
value: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A]
|
|
748
|
+
lower: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | A
|
|
749
|
+
upper: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | A
|
|
750
|
+
|
|
751
|
+
def to_sql(self) -> str:
|
|
752
|
+
lower = _qualified_label(self.lower) if isinstance(self.lower, PropSelect) else str(self.lower)
|
|
753
|
+
upper = _qualified_label(self.upper) if isinstance(self.upper, PropSelect) else str(self.upper)
|
|
754
|
+
return f'{_qualified_label(self.value)} BETWEEN {lower} AND {upper}'
|
|
755
|
+
|
|
756
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
757
|
+
lower, lower_params = _render_value(self.lower, qualify=True)
|
|
758
|
+
upper, upper_params = _render_value(self.upper, qualify=True)
|
|
759
|
+
return f'{_qualified_label(self.value)} BETWEEN {lower} AND {upper}', lower_params + upper_params
|
|
760
|
+
|
|
761
|
+
@dataclass(frozen=True)
|
|
762
|
+
class InConstraint4(Generic[TC, TC1, TC2, TC3, A], Constraint4[TC, TC1, TC2, TC3]):
|
|
763
|
+
value: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A]
|
|
764
|
+
values: Sequence[Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | A]
|
|
765
|
+
|
|
766
|
+
def to_sql(self) -> str:
|
|
767
|
+
def render(c: Prop[TC, A] | PropOpt[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | A) -> str:
|
|
768
|
+
if isinstance(c, PropSelect):
|
|
769
|
+
return _qualified_label(c)
|
|
770
|
+
value = str(c)
|
|
771
|
+
if self.value.target is str:
|
|
772
|
+
value = "'" + value + "'"
|
|
773
|
+
return value
|
|
774
|
+
|
|
775
|
+
values = [render(c) for c in self.values]
|
|
776
|
+
return f'{_qualified_label(self.value)} IN ({", ".join(values)})'
|
|
777
|
+
|
|
778
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
779
|
+
rendered = [_render_value(c, qualify=True) for c in self.values]
|
|
780
|
+
sql = f'{_qualified_label(self.value)} IN ({", ".join(part for part, _ in rendered)})'
|
|
781
|
+
params = [p for _, params in rendered for p in params]
|
|
782
|
+
return sql, params
|