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,728 @@
|
|
|
1
|
+
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Any, Generic, List, Sequence, Tuple, TypeVar
|
|
4
|
+
|
|
5
|
+
from pydoptic.selector import PropSelect, Prop, PropOpt
|
|
6
|
+
from pydoptic_sql import SqlTable
|
|
7
|
+
from pydoptic_sql.sql_computed import Computed
|
|
8
|
+
from pydoptic_sql.sql_constraint import Comparison, _comparison_symbol, _qualified_label
|
|
9
|
+
|
|
10
|
+
A = TypeVar('A')
|
|
11
|
+
TC = TypeVar('TC', bound=SqlTable, contravariant=True)
|
|
12
|
+
TC1 = TypeVar('TC1', bound=SqlTable, contravariant=True)
|
|
13
|
+
TC2 = TypeVar('TC2', bound=SqlTable, contravariant=True)
|
|
14
|
+
TC3 = TypeVar('TC3', bound=SqlTable, contravariant=True)
|
|
15
|
+
|
|
16
|
+
# HavingConstraint mirrors Constraint's structure exactly (same arity ladder, same qualify-baked-
|
|
17
|
+
# in-per-class rendering), but every operand position also accepts a Computed[TC, A] -- since HAVING
|
|
18
|
+
# is the one clause allowed to reference aggregate expressions, unlike WHERE/ON (Constraint) which
|
|
19
|
+
# deliberately can't: Computed isn't a PropSelect, so it's statically unusable there. A Computed
|
|
20
|
+
# operand renders as its bare function call ("SUM(age)"), never its alias ("worker_age_sum") --
|
|
21
|
+
# Postgres doesn't make SELECT-list aliases visible inside HAVING.
|
|
22
|
+
|
|
23
|
+
def _having_ref_sql(value: 'PropSelect[Any, Any] | Computed[Any, Any]', qualify: bool) -> str:
|
|
24
|
+
"""Render an operand that's always a reference (a column or an aggregate expression), never a
|
|
25
|
+
literal -- used for the left-hand side of comparisons and the BETWEEN/IN 'value' operand."""
|
|
26
|
+
if isinstance(value, Computed):
|
|
27
|
+
col_ref = '*' if value.column is None else (_qualified_label(value.column) if qualify else value.column.label)
|
|
28
|
+
return f'{value.function.value}({col_ref})'
|
|
29
|
+
return _qualified_label(value) if qualify else value.label
|
|
30
|
+
|
|
31
|
+
def _having_display_value(value: Any, target_is_str: bool, qualify: bool) -> str:
|
|
32
|
+
"""Render an operand that might be a reference or a literal, for the interpolated (debug/display)
|
|
33
|
+
to_sql() text -- literals are quoted inline here since there's no params list to bind them to."""
|
|
34
|
+
if isinstance(value, (PropSelect, Computed)):
|
|
35
|
+
return _having_ref_sql(value, qualify)
|
|
36
|
+
text = str(value)
|
|
37
|
+
return "'" + text + "'" if target_is_str else text
|
|
38
|
+
|
|
39
|
+
def _render_having_value(value: Any, qualify: bool) -> Tuple[str, List[Any]]:
|
|
40
|
+
"""Render an operand that might be a reference or a literal, for the parameterized to_sql_params()
|
|
41
|
+
-- a literal becomes a `%s` placeholder bound to that value, same as _render_value in sql_constraint.py."""
|
|
42
|
+
if isinstance(value, (PropSelect, Computed)):
|
|
43
|
+
return _having_ref_sql(value, qualify), []
|
|
44
|
+
return '%s', [value]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# --- arity 1: HAVING constraints over a single table ---
|
|
48
|
+
|
|
49
|
+
class HavingConstraint(Generic[TC]):
|
|
50
|
+
def to_sql(self) -> str:
|
|
51
|
+
raise NotImplementedError()
|
|
52
|
+
|
|
53
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
54
|
+
raise NotImplementedError()
|
|
55
|
+
|
|
56
|
+
def incr_arity(self) -> 'HavingConstraint2[TC, TC1]':
|
|
57
|
+
raise NotImplementedError()
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def any(cls, *constraints: 'HavingConstraint[TC]') -> 'HavingOrConstraint[TC]':
|
|
61
|
+
return HavingOrConstraint(list(constraints))
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def all(cls, *constraints: 'HavingConstraint[TC]') -> 'HavingAndConstraint[TC]':
|
|
65
|
+
return HavingAndConstraint(list(constraints))
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def eq(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | A) -> 'HavingCompConstraint[TC, A]':
|
|
69
|
+
return HavingCompConstraint(prop, other, Comparison.EQ)
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def gt(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | A) -> 'HavingCompConstraint[TC, A]':
|
|
73
|
+
return HavingCompConstraint(prop, other, Comparison.GT)
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def gte(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | A) -> 'HavingCompConstraint[TC, A]':
|
|
77
|
+
return HavingCompConstraint(prop, other, Comparison.GTE)
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def lt(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | A) -> 'HavingCompConstraint[TC, A]':
|
|
81
|
+
return HavingCompConstraint(prop, other, Comparison.LT)
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
def lte(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | A) -> 'HavingCompConstraint[TC, A]':
|
|
85
|
+
return HavingCompConstraint(prop, other, Comparison.LTE)
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def ne(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | A) -> 'HavingCompConstraint[TC, A]':
|
|
89
|
+
return HavingCompConstraint(prop, other, Comparison.NE)
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
def like(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | A) -> 'HavingCompConstraint[TC, A]':
|
|
93
|
+
return HavingCompConstraint(prop, other, Comparison.LIKE)
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
def between(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A], lower: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | A, upper: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | A) -> 'HavingBetweenConstraint[TC, A]':
|
|
97
|
+
return HavingBetweenConstraint(prop, lower, upper)
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def in_(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A], values: Sequence[Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | A]) -> 'HavingInConstraint[TC, A]':
|
|
101
|
+
return HavingInConstraint(prop, values)
|
|
102
|
+
|
|
103
|
+
def AND(self, other: 'HavingConstraint[TC]') -> 'HavingAndConstraint[TC]':
|
|
104
|
+
return HavingAndConstraint([self, other])
|
|
105
|
+
|
|
106
|
+
def OR(self, other: 'HavingConstraint[TC]') -> 'HavingOrConstraint[TC]':
|
|
107
|
+
return HavingOrConstraint([self, other])
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def NOT(self) -> 'HavingNotConstraint[TC]':
|
|
111
|
+
return HavingNotConstraint(self)
|
|
112
|
+
|
|
113
|
+
@dataclass(frozen=True)
|
|
114
|
+
class HavingOrConstraint(HavingConstraint[TC]):
|
|
115
|
+
constraints: List[HavingConstraint[TC]]
|
|
116
|
+
|
|
117
|
+
def to_sql(self) -> str:
|
|
118
|
+
return '(' + ' OR '.join(c.to_sql() for c in self.constraints) + ')'
|
|
119
|
+
|
|
120
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
121
|
+
parts_params = [c.to_sql_params() for c in self.constraints]
|
|
122
|
+
sql = '(' + ' OR '.join(part for part, _ in parts_params) + ')'
|
|
123
|
+
params = [p for _, params in parts_params for p in params]
|
|
124
|
+
return sql, params
|
|
125
|
+
|
|
126
|
+
def incr_arity(self) -> 'HavingOrConstraint2[TC, TC1]':
|
|
127
|
+
return HavingOrConstraint2([c.incr_arity() for c in self.constraints])
|
|
128
|
+
|
|
129
|
+
@dataclass(frozen=True)
|
|
130
|
+
class HavingAndConstraint(HavingConstraint[TC]):
|
|
131
|
+
constraints: List[HavingConstraint[TC]]
|
|
132
|
+
|
|
133
|
+
def to_sql(self) -> str:
|
|
134
|
+
return '(' + ' AND '.join(c.to_sql() for c in self.constraints) + ')'
|
|
135
|
+
|
|
136
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
137
|
+
parts_params = [c.to_sql_params() for c in self.constraints]
|
|
138
|
+
sql = '(' + ' AND '.join(part for part, _ in parts_params) + ')'
|
|
139
|
+
params = [p for _, params in parts_params for p in params]
|
|
140
|
+
return sql, params
|
|
141
|
+
|
|
142
|
+
def incr_arity(self) -> 'HavingAndConstraint2[TC, TC1]':
|
|
143
|
+
return HavingAndConstraint2([c.incr_arity() for c in self.constraints])
|
|
144
|
+
|
|
145
|
+
@dataclass(frozen=True)
|
|
146
|
+
class HavingNotConstraint(HavingConstraint[TC]):
|
|
147
|
+
constraint: HavingConstraint[TC]
|
|
148
|
+
|
|
149
|
+
def to_sql(self) -> str:
|
|
150
|
+
return 'NOT (' + self.constraint.to_sql() + ')'
|
|
151
|
+
|
|
152
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
153
|
+
sql, params = self.constraint.to_sql_params()
|
|
154
|
+
return 'NOT (' + sql + ')', params
|
|
155
|
+
|
|
156
|
+
def incr_arity(self) -> 'HavingNotConstraint2[TC, TC1]':
|
|
157
|
+
return HavingNotConstraint2(self.constraint.incr_arity())
|
|
158
|
+
|
|
159
|
+
@dataclass(frozen=True)
|
|
160
|
+
class HavingCompConstraint(Generic[TC, A], HavingConstraint[TC]):
|
|
161
|
+
value_1: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A]
|
|
162
|
+
value_2: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | A
|
|
163
|
+
comp: Comparison
|
|
164
|
+
|
|
165
|
+
def to_sql(self) -> str:
|
|
166
|
+
value_2 = _having_display_value(self.value_2, self.value_1.target is str, qualify=False)
|
|
167
|
+
return _having_ref_sql(self.value_1, qualify=False) + ' ' + _comparison_symbol(self.comp) + ' ' + value_2
|
|
168
|
+
|
|
169
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
170
|
+
value_2, params = _render_having_value(self.value_2, qualify=False)
|
|
171
|
+
return _having_ref_sql(self.value_1, qualify=False) + ' ' + _comparison_symbol(self.comp) + ' ' + value_2, params
|
|
172
|
+
|
|
173
|
+
def incr_arity(self) -> 'HavingCompConstraint2[TC, TC1, A]':
|
|
174
|
+
return HavingCompConstraint2(self.value_1, self.value_2, self.comp)
|
|
175
|
+
|
|
176
|
+
@dataclass(frozen=True)
|
|
177
|
+
class HavingBetweenConstraint(Generic[TC, A], HavingConstraint[TC]):
|
|
178
|
+
value: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A]
|
|
179
|
+
lower: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | A
|
|
180
|
+
upper: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | A
|
|
181
|
+
|
|
182
|
+
def to_sql(self) -> str:
|
|
183
|
+
target_is_str = self.value.target is str
|
|
184
|
+
lower = _having_display_value(self.lower, target_is_str, qualify=False)
|
|
185
|
+
upper = _having_display_value(self.upper, target_is_str, qualify=False)
|
|
186
|
+
return f'{_having_ref_sql(self.value, qualify=False)} BETWEEN {lower} AND {upper}'
|
|
187
|
+
|
|
188
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
189
|
+
lower, lower_params = _render_having_value(self.lower, qualify=False)
|
|
190
|
+
upper, upper_params = _render_having_value(self.upper, qualify=False)
|
|
191
|
+
return f'{_having_ref_sql(self.value, qualify=False)} BETWEEN {lower} AND {upper}', lower_params + upper_params
|
|
192
|
+
|
|
193
|
+
def incr_arity(self) -> 'HavingBetweenConstraint2[TC, TC1, A]':
|
|
194
|
+
return HavingBetweenConstraint2(self.value, self.lower, self.upper)
|
|
195
|
+
|
|
196
|
+
@dataclass(frozen=True)
|
|
197
|
+
class HavingInConstraint(Generic[TC, A], HavingConstraint[TC]):
|
|
198
|
+
value: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A]
|
|
199
|
+
values: Sequence[Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | A]
|
|
200
|
+
|
|
201
|
+
def to_sql(self) -> str:
|
|
202
|
+
target_is_str = self.value.target is str
|
|
203
|
+
values = [_having_display_value(c, target_is_str, qualify=False) for c in self.values]
|
|
204
|
+
return f'{_having_ref_sql(self.value, qualify=False)} IN ({", ".join(values)})'
|
|
205
|
+
|
|
206
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
207
|
+
rendered = [_render_having_value(c, qualify=False) for c in self.values]
|
|
208
|
+
sql = f'{_having_ref_sql(self.value, qualify=False)} IN ({", ".join(part for part, _ in rendered)})'
|
|
209
|
+
params = [p for _, params in rendered for p in params]
|
|
210
|
+
return sql, params
|
|
211
|
+
|
|
212
|
+
def incr_arity(self) -> 'HavingInConstraint2[TC, TC1, A]':
|
|
213
|
+
return HavingInConstraint2(self.value, self.values)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# --- arity 2: HAVING constraints over a pair of joined tables ---
|
|
217
|
+
# Column/computed references always render table-qualified, since an operand here may reference
|
|
218
|
+
# either table and there's no other way to disambiguate.
|
|
219
|
+
|
|
220
|
+
class HavingConstraint2(Generic[TC, TC1]):
|
|
221
|
+
def to_sql(self) -> str:
|
|
222
|
+
raise NotImplementedError()
|
|
223
|
+
|
|
224
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
225
|
+
raise NotImplementedError()
|
|
226
|
+
|
|
227
|
+
def incr_arity(self) -> 'HavingConstraint3[TC, TC1, TC2]':
|
|
228
|
+
raise NotImplementedError()
|
|
229
|
+
|
|
230
|
+
@classmethod
|
|
231
|
+
def any(cls, *constraints: 'HavingConstraint2[TC, TC1]') -> 'HavingOrConstraint2[TC, TC1]':
|
|
232
|
+
return HavingOrConstraint2(list(constraints))
|
|
233
|
+
|
|
234
|
+
@classmethod
|
|
235
|
+
def all(cls, *constraints: 'HavingConstraint2[TC, TC1]') -> 'HavingAndConstraint2[TC, TC1]':
|
|
236
|
+
return HavingAndConstraint2(list(constraints))
|
|
237
|
+
|
|
238
|
+
@classmethod
|
|
239
|
+
def eq(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | A) -> 'HavingCompConstraint2[TC, TC1, A]':
|
|
240
|
+
return HavingCompConstraint2(prop, other, Comparison.EQ)
|
|
241
|
+
|
|
242
|
+
@classmethod
|
|
243
|
+
def gt(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | A) -> 'HavingCompConstraint2[TC, TC1, A]':
|
|
244
|
+
return HavingCompConstraint2(prop, other, Comparison.GT)
|
|
245
|
+
|
|
246
|
+
@classmethod
|
|
247
|
+
def gte(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | A) -> 'HavingCompConstraint2[TC, TC1, A]':
|
|
248
|
+
return HavingCompConstraint2(prop, other, Comparison.GTE)
|
|
249
|
+
|
|
250
|
+
@classmethod
|
|
251
|
+
def lt(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | A) -> 'HavingCompConstraint2[TC, TC1, A]':
|
|
252
|
+
return HavingCompConstraint2(prop, other, Comparison.LT)
|
|
253
|
+
|
|
254
|
+
@classmethod
|
|
255
|
+
def lte(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | A) -> 'HavingCompConstraint2[TC, TC1, A]':
|
|
256
|
+
return HavingCompConstraint2(prop, other, Comparison.LTE)
|
|
257
|
+
|
|
258
|
+
@classmethod
|
|
259
|
+
def ne(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | A) -> 'HavingCompConstraint2[TC, TC1, A]':
|
|
260
|
+
return HavingCompConstraint2(prop, other, Comparison.NE)
|
|
261
|
+
|
|
262
|
+
@classmethod
|
|
263
|
+
def like(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | A) -> 'HavingCompConstraint2[TC, TC1, A]':
|
|
264
|
+
return HavingCompConstraint2(prop, other, Comparison.LIKE)
|
|
265
|
+
|
|
266
|
+
@classmethod
|
|
267
|
+
def between(
|
|
268
|
+
cls,
|
|
269
|
+
prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A],
|
|
270
|
+
lower: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | A,
|
|
271
|
+
upper: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | A,
|
|
272
|
+
) -> 'HavingBetweenConstraint2[TC, TC1, A]':
|
|
273
|
+
return HavingBetweenConstraint2(prop, lower, upper)
|
|
274
|
+
|
|
275
|
+
@classmethod
|
|
276
|
+
def in_(
|
|
277
|
+
cls,
|
|
278
|
+
prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A],
|
|
279
|
+
values: Sequence[Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | A],
|
|
280
|
+
) -> 'HavingInConstraint2[TC, TC1, A]':
|
|
281
|
+
return HavingInConstraint2(prop, values)
|
|
282
|
+
|
|
283
|
+
def AND(self, other: 'HavingConstraint2[TC, TC1]') -> 'HavingAndConstraint2[TC, TC1]':
|
|
284
|
+
return HavingAndConstraint2([self, other])
|
|
285
|
+
|
|
286
|
+
def OR(self, other: 'HavingConstraint2[TC, TC1]') -> 'HavingOrConstraint2[TC, TC1]':
|
|
287
|
+
return HavingOrConstraint2([self, other])
|
|
288
|
+
|
|
289
|
+
@property
|
|
290
|
+
def NOT(self) -> 'HavingNotConstraint2[TC, TC1]':
|
|
291
|
+
return HavingNotConstraint2(self)
|
|
292
|
+
|
|
293
|
+
@dataclass(frozen=True)
|
|
294
|
+
class HavingOrConstraint2(HavingConstraint2[TC, TC1]):
|
|
295
|
+
constraints: List[HavingConstraint2[TC, TC1]]
|
|
296
|
+
|
|
297
|
+
def to_sql(self) -> str:
|
|
298
|
+
return '(' + ' OR '.join(c.to_sql() for c in self.constraints) + ')'
|
|
299
|
+
|
|
300
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
301
|
+
parts_params = [c.to_sql_params() for c in self.constraints]
|
|
302
|
+
sql = '(' + ' OR '.join(part for part, _ in parts_params) + ')'
|
|
303
|
+
params = [p for _, params in parts_params for p in params]
|
|
304
|
+
return sql, params
|
|
305
|
+
|
|
306
|
+
def incr_arity(self) -> 'HavingOrConstraint3[TC, TC1, TC2]':
|
|
307
|
+
return HavingOrConstraint3([c.incr_arity() for c in self.constraints])
|
|
308
|
+
|
|
309
|
+
@dataclass(frozen=True)
|
|
310
|
+
class HavingAndConstraint2(HavingConstraint2[TC, TC1]):
|
|
311
|
+
constraints: List[HavingConstraint2[TC, TC1]]
|
|
312
|
+
|
|
313
|
+
def to_sql(self) -> str:
|
|
314
|
+
return '(' + ' AND '.join(c.to_sql() for c in self.constraints) + ')'
|
|
315
|
+
|
|
316
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
317
|
+
parts_params = [c.to_sql_params() for c in self.constraints]
|
|
318
|
+
sql = '(' + ' AND '.join(part for part, _ in parts_params) + ')'
|
|
319
|
+
params = [p for _, params in parts_params for p in params]
|
|
320
|
+
return sql, params
|
|
321
|
+
|
|
322
|
+
def incr_arity(self) -> 'HavingAndConstraint3[TC, TC1, TC2]':
|
|
323
|
+
return HavingAndConstraint3([c.incr_arity() for c in self.constraints])
|
|
324
|
+
|
|
325
|
+
@dataclass(frozen=True)
|
|
326
|
+
class HavingNotConstraint2(HavingConstraint2[TC, TC1]):
|
|
327
|
+
constraint: HavingConstraint2[TC, TC1]
|
|
328
|
+
|
|
329
|
+
def to_sql(self) -> str:
|
|
330
|
+
return 'NOT (' + self.constraint.to_sql() + ')'
|
|
331
|
+
|
|
332
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
333
|
+
sql, params = self.constraint.to_sql_params()
|
|
334
|
+
return 'NOT (' + sql + ')', params
|
|
335
|
+
|
|
336
|
+
def incr_arity(self) -> 'HavingNotConstraint3[TC, TC1, TC2]':
|
|
337
|
+
return HavingNotConstraint3(self.constraint.incr_arity())
|
|
338
|
+
|
|
339
|
+
@dataclass(frozen=True)
|
|
340
|
+
class HavingCompConstraint2(Generic[TC, TC1, A], HavingConstraint2[TC, TC1]):
|
|
341
|
+
value_1: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A]
|
|
342
|
+
value_2: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | A
|
|
343
|
+
comp: Comparison
|
|
344
|
+
|
|
345
|
+
def to_sql(self) -> str:
|
|
346
|
+
value_2 = _having_display_value(self.value_2, self.value_1.target is str, qualify=True)
|
|
347
|
+
return _having_ref_sql(self.value_1, qualify=True) + ' ' + _comparison_symbol(self.comp) + ' ' + value_2
|
|
348
|
+
|
|
349
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
350
|
+
value_2, params = _render_having_value(self.value_2, qualify=True)
|
|
351
|
+
return _having_ref_sql(self.value_1, qualify=True) + ' ' + _comparison_symbol(self.comp) + ' ' + value_2, params
|
|
352
|
+
|
|
353
|
+
def incr_arity(self) -> 'HavingCompConstraint3[TC, TC1, TC2, A]':
|
|
354
|
+
return HavingCompConstraint3(self.value_1, self.value_2, self.comp)
|
|
355
|
+
|
|
356
|
+
@dataclass(frozen=True)
|
|
357
|
+
class HavingBetweenConstraint2(Generic[TC, TC1, A], HavingConstraint2[TC, TC1]):
|
|
358
|
+
value: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A]
|
|
359
|
+
lower: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | A
|
|
360
|
+
upper: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | A
|
|
361
|
+
|
|
362
|
+
def to_sql(self) -> str:
|
|
363
|
+
target_is_str = self.value.target is str
|
|
364
|
+
lower = _having_display_value(self.lower, target_is_str, qualify=True)
|
|
365
|
+
upper = _having_display_value(self.upper, target_is_str, qualify=True)
|
|
366
|
+
return f'{_having_ref_sql(self.value, qualify=True)} BETWEEN {lower} AND {upper}'
|
|
367
|
+
|
|
368
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
369
|
+
lower, lower_params = _render_having_value(self.lower, qualify=True)
|
|
370
|
+
upper, upper_params = _render_having_value(self.upper, qualify=True)
|
|
371
|
+
return f'{_having_ref_sql(self.value, qualify=True)} BETWEEN {lower} AND {upper}', lower_params + upper_params
|
|
372
|
+
|
|
373
|
+
def incr_arity(self) -> 'HavingBetweenConstraint3[TC, TC1, TC2, A]':
|
|
374
|
+
return HavingBetweenConstraint3(self.value, self.lower, self.upper)
|
|
375
|
+
|
|
376
|
+
@dataclass(frozen=True)
|
|
377
|
+
class HavingInConstraint2(Generic[TC, TC1, A], HavingConstraint2[TC, TC1]):
|
|
378
|
+
value: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A]
|
|
379
|
+
values: Sequence[Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | A]
|
|
380
|
+
|
|
381
|
+
def to_sql(self) -> str:
|
|
382
|
+
target_is_str = self.value.target is str
|
|
383
|
+
values = [_having_display_value(c, target_is_str, qualify=True) for c in self.values]
|
|
384
|
+
return f'{_having_ref_sql(self.value, qualify=True)} IN ({", ".join(values)})'
|
|
385
|
+
|
|
386
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
387
|
+
rendered = [_render_having_value(c, qualify=True) for c in self.values]
|
|
388
|
+
sql = f'{_having_ref_sql(self.value, qualify=True)} IN ({", ".join(part for part, _ in rendered)})'
|
|
389
|
+
params = [p for _, params in rendered for p in params]
|
|
390
|
+
return sql, params
|
|
391
|
+
|
|
392
|
+
def incr_arity(self) -> 'HavingInConstraint3[TC, TC1, TC2, A]':
|
|
393
|
+
return HavingInConstraint3(self.value, self.values)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
# --- arity 3: HAVING constraints over 3 joined tables ---
|
|
397
|
+
|
|
398
|
+
class HavingConstraint3(Generic[TC, TC1, TC2]):
|
|
399
|
+
def to_sql(self) -> str:
|
|
400
|
+
raise NotImplementedError()
|
|
401
|
+
|
|
402
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
403
|
+
raise NotImplementedError()
|
|
404
|
+
|
|
405
|
+
def incr_arity(self) -> 'HavingConstraint4[TC, TC1, TC2, TC3]':
|
|
406
|
+
raise NotImplementedError()
|
|
407
|
+
|
|
408
|
+
@classmethod
|
|
409
|
+
def any(cls, *constraints: 'HavingConstraint3[TC, TC1, TC2]') -> 'HavingOrConstraint3[TC, TC1, TC2]':
|
|
410
|
+
return HavingOrConstraint3(list(constraints))
|
|
411
|
+
|
|
412
|
+
@classmethod
|
|
413
|
+
def all(cls, *constraints: 'HavingConstraint3[TC, TC1, TC2]') -> 'HavingAndConstraint3[TC, TC1, TC2]':
|
|
414
|
+
return HavingAndConstraint3(list(constraints))
|
|
415
|
+
|
|
416
|
+
@classmethod
|
|
417
|
+
def eq(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | A) -> 'HavingCompConstraint3[TC, TC1, TC2, A]':
|
|
418
|
+
return HavingCompConstraint3(prop, other, Comparison.EQ)
|
|
419
|
+
|
|
420
|
+
@classmethod
|
|
421
|
+
def gt(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | A) -> 'HavingCompConstraint3[TC, TC1, TC2, A]':
|
|
422
|
+
return HavingCompConstraint3(prop, other, Comparison.GT)
|
|
423
|
+
|
|
424
|
+
@classmethod
|
|
425
|
+
def gte(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | A) -> 'HavingCompConstraint3[TC, TC1, TC2, A]':
|
|
426
|
+
return HavingCompConstraint3(prop, other, Comparison.GTE)
|
|
427
|
+
|
|
428
|
+
@classmethod
|
|
429
|
+
def lt(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | A) -> 'HavingCompConstraint3[TC, TC1, TC2, A]':
|
|
430
|
+
return HavingCompConstraint3(prop, other, Comparison.LT)
|
|
431
|
+
|
|
432
|
+
@classmethod
|
|
433
|
+
def lte(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | A) -> 'HavingCompConstraint3[TC, TC1, TC2, A]':
|
|
434
|
+
return HavingCompConstraint3(prop, other, Comparison.LTE)
|
|
435
|
+
|
|
436
|
+
@classmethod
|
|
437
|
+
def ne(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | A) -> 'HavingCompConstraint3[TC, TC1, TC2, A]':
|
|
438
|
+
return HavingCompConstraint3(prop, other, Comparison.NE)
|
|
439
|
+
|
|
440
|
+
@classmethod
|
|
441
|
+
def like(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | A) -> 'HavingCompConstraint3[TC, TC1, TC2, A]':
|
|
442
|
+
return HavingCompConstraint3(prop, other, Comparison.LIKE)
|
|
443
|
+
|
|
444
|
+
@classmethod
|
|
445
|
+
def between(
|
|
446
|
+
cls,
|
|
447
|
+
prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A],
|
|
448
|
+
lower: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | A,
|
|
449
|
+
upper: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | A,
|
|
450
|
+
) -> 'HavingBetweenConstraint3[TC, TC1, TC2, A]':
|
|
451
|
+
return HavingBetweenConstraint3(prop, lower, upper)
|
|
452
|
+
|
|
453
|
+
@classmethod
|
|
454
|
+
def in_(
|
|
455
|
+
cls,
|
|
456
|
+
prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A],
|
|
457
|
+
values: Sequence[Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | A],
|
|
458
|
+
) -> 'HavingInConstraint3[TC, TC1, TC2, A]':
|
|
459
|
+
return HavingInConstraint3(prop, values)
|
|
460
|
+
|
|
461
|
+
def AND(self, other: 'HavingConstraint3[TC, TC1, TC2]') -> 'HavingAndConstraint3[TC, TC1, TC2]':
|
|
462
|
+
return HavingAndConstraint3([self, other])
|
|
463
|
+
|
|
464
|
+
def OR(self, other: 'HavingConstraint3[TC, TC1, TC2]') -> 'HavingOrConstraint3[TC, TC1, TC2]':
|
|
465
|
+
return HavingOrConstraint3([self, other])
|
|
466
|
+
|
|
467
|
+
@property
|
|
468
|
+
def NOT(self) -> 'HavingNotConstraint3[TC, TC1, TC2]':
|
|
469
|
+
return HavingNotConstraint3(self)
|
|
470
|
+
|
|
471
|
+
@dataclass(frozen=True)
|
|
472
|
+
class HavingOrConstraint3(HavingConstraint3[TC, TC1, TC2]):
|
|
473
|
+
constraints: List[HavingConstraint3[TC, TC1, TC2]]
|
|
474
|
+
|
|
475
|
+
def to_sql(self) -> str:
|
|
476
|
+
return '(' + ' OR '.join(c.to_sql() for c in self.constraints) + ')'
|
|
477
|
+
|
|
478
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
479
|
+
parts_params = [c.to_sql_params() for c in self.constraints]
|
|
480
|
+
sql = '(' + ' OR '.join(part for part, _ in parts_params) + ')'
|
|
481
|
+
params = [p for _, params in parts_params for p in params]
|
|
482
|
+
return sql, params
|
|
483
|
+
|
|
484
|
+
def incr_arity(self) -> 'HavingOrConstraint4[TC, TC1, TC2, TC3]':
|
|
485
|
+
return HavingOrConstraint4([c.incr_arity() for c in self.constraints])
|
|
486
|
+
|
|
487
|
+
@dataclass(frozen=True)
|
|
488
|
+
class HavingAndConstraint3(HavingConstraint3[TC, TC1, TC2]):
|
|
489
|
+
constraints: List[HavingConstraint3[TC, TC1, TC2]]
|
|
490
|
+
|
|
491
|
+
def to_sql(self) -> str:
|
|
492
|
+
return '(' + ' AND '.join(c.to_sql() for c in self.constraints) + ')'
|
|
493
|
+
|
|
494
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
495
|
+
parts_params = [c.to_sql_params() for c in self.constraints]
|
|
496
|
+
sql = '(' + ' AND '.join(part for part, _ in parts_params) + ')'
|
|
497
|
+
params = [p for _, params in parts_params for p in params]
|
|
498
|
+
return sql, params
|
|
499
|
+
|
|
500
|
+
def incr_arity(self) -> 'HavingAndConstraint4[TC, TC1, TC2, TC3]':
|
|
501
|
+
return HavingAndConstraint4([c.incr_arity() for c in self.constraints])
|
|
502
|
+
|
|
503
|
+
@dataclass(frozen=True)
|
|
504
|
+
class HavingNotConstraint3(HavingConstraint3[TC, TC1, TC2]):
|
|
505
|
+
constraint: HavingConstraint3[TC, TC1, TC2]
|
|
506
|
+
|
|
507
|
+
def to_sql(self) -> str:
|
|
508
|
+
return 'NOT (' + self.constraint.to_sql() + ')'
|
|
509
|
+
|
|
510
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
511
|
+
sql, params = self.constraint.to_sql_params()
|
|
512
|
+
return 'NOT (' + sql + ')', params
|
|
513
|
+
|
|
514
|
+
def incr_arity(self) -> 'HavingNotConstraint4[TC, TC1, TC2, TC3]':
|
|
515
|
+
return HavingNotConstraint4(self.constraint.incr_arity())
|
|
516
|
+
|
|
517
|
+
@dataclass(frozen=True)
|
|
518
|
+
class HavingCompConstraint3(Generic[TC, TC1, TC2, A], HavingConstraint3[TC, TC1, TC2]):
|
|
519
|
+
value_1: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A]
|
|
520
|
+
value_2: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | A
|
|
521
|
+
comp: Comparison
|
|
522
|
+
|
|
523
|
+
def to_sql(self) -> str:
|
|
524
|
+
value_2 = _having_display_value(self.value_2, self.value_1.target is str, qualify=True)
|
|
525
|
+
return _having_ref_sql(self.value_1, qualify=True) + ' ' + _comparison_symbol(self.comp) + ' ' + value_2
|
|
526
|
+
|
|
527
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
528
|
+
value_2, params = _render_having_value(self.value_2, qualify=True)
|
|
529
|
+
return _having_ref_sql(self.value_1, qualify=True) + ' ' + _comparison_symbol(self.comp) + ' ' + value_2, params
|
|
530
|
+
|
|
531
|
+
def incr_arity(self) -> 'HavingCompConstraint4[TC, TC1, TC2, TC3, A]':
|
|
532
|
+
return HavingCompConstraint4(self.value_1, self.value_2, self.comp)
|
|
533
|
+
|
|
534
|
+
@dataclass(frozen=True)
|
|
535
|
+
class HavingBetweenConstraint3(Generic[TC, TC1, TC2, A], HavingConstraint3[TC, TC1, TC2]):
|
|
536
|
+
value: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A]
|
|
537
|
+
lower: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | A
|
|
538
|
+
upper: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | A
|
|
539
|
+
|
|
540
|
+
def to_sql(self) -> str:
|
|
541
|
+
target_is_str = self.value.target is str
|
|
542
|
+
lower = _having_display_value(self.lower, target_is_str, qualify=True)
|
|
543
|
+
upper = _having_display_value(self.upper, target_is_str, qualify=True)
|
|
544
|
+
return f'{_having_ref_sql(self.value, qualify=True)} BETWEEN {lower} AND {upper}'
|
|
545
|
+
|
|
546
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
547
|
+
lower, lower_params = _render_having_value(self.lower, qualify=True)
|
|
548
|
+
upper, upper_params = _render_having_value(self.upper, qualify=True)
|
|
549
|
+
return f'{_having_ref_sql(self.value, qualify=True)} BETWEEN {lower} AND {upper}', lower_params + upper_params
|
|
550
|
+
|
|
551
|
+
def incr_arity(self) -> 'HavingBetweenConstraint4[TC, TC1, TC2, TC3, A]':
|
|
552
|
+
return HavingBetweenConstraint4(self.value, self.lower, self.upper)
|
|
553
|
+
|
|
554
|
+
@dataclass(frozen=True)
|
|
555
|
+
class HavingInConstraint3(Generic[TC, TC1, TC2, A], HavingConstraint3[TC, TC1, TC2]):
|
|
556
|
+
value: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A]
|
|
557
|
+
values: Sequence[Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | A]
|
|
558
|
+
|
|
559
|
+
def to_sql(self) -> str:
|
|
560
|
+
target_is_str = self.value.target is str
|
|
561
|
+
values = [_having_display_value(c, target_is_str, qualify=True) for c in self.values]
|
|
562
|
+
return f'{_having_ref_sql(self.value, qualify=True)} IN ({", ".join(values)})'
|
|
563
|
+
|
|
564
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
565
|
+
rendered = [_render_having_value(c, qualify=True) for c in self.values]
|
|
566
|
+
sql = f'{_having_ref_sql(self.value, qualify=True)} IN ({", ".join(part for part, _ in rendered)})'
|
|
567
|
+
params = [p for _, params in rendered for p in params]
|
|
568
|
+
return sql, params
|
|
569
|
+
|
|
570
|
+
def incr_arity(self) -> 'HavingInConstraint4[TC, TC1, TC2, TC3, A]':
|
|
571
|
+
return HavingInConstraint4(self.value, self.values)
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
# --- arity 4: HAVING constraints over 4 joined tables ---
|
|
575
|
+
|
|
576
|
+
class HavingConstraint4(Generic[TC, TC1, TC2, TC3]):
|
|
577
|
+
def to_sql(self) -> str:
|
|
578
|
+
raise NotImplementedError()
|
|
579
|
+
|
|
580
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
581
|
+
raise NotImplementedError()
|
|
582
|
+
|
|
583
|
+
@classmethod
|
|
584
|
+
def any(cls, *constraints: 'HavingConstraint4[TC, TC1, TC2, TC3]') -> 'HavingOrConstraint4[TC, TC1, TC2, TC3]':
|
|
585
|
+
return HavingOrConstraint4(list(constraints))
|
|
586
|
+
|
|
587
|
+
@classmethod
|
|
588
|
+
def all(cls, *constraints: 'HavingConstraint4[TC, TC1, TC2, TC3]') -> 'HavingAndConstraint4[TC, TC1, TC2, TC3]':
|
|
589
|
+
return HavingAndConstraint4(list(constraints))
|
|
590
|
+
|
|
591
|
+
@classmethod
|
|
592
|
+
def eq(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A] | A) -> 'HavingCompConstraint4[TC, TC1, TC2, TC3, A]':
|
|
593
|
+
return HavingCompConstraint4(prop, other, Comparison.EQ)
|
|
594
|
+
|
|
595
|
+
@classmethod
|
|
596
|
+
def gt(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A] | A) -> 'HavingCompConstraint4[TC, TC1, TC2, TC3, A]':
|
|
597
|
+
return HavingCompConstraint4(prop, other, Comparison.GT)
|
|
598
|
+
|
|
599
|
+
@classmethod
|
|
600
|
+
def gte(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A] | A) -> 'HavingCompConstraint4[TC, TC1, TC2, TC3, A]':
|
|
601
|
+
return HavingCompConstraint4(prop, other, Comparison.GTE)
|
|
602
|
+
|
|
603
|
+
@classmethod
|
|
604
|
+
def lt(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A] | A) -> 'HavingCompConstraint4[TC, TC1, TC2, TC3, A]':
|
|
605
|
+
return HavingCompConstraint4(prop, other, Comparison.LT)
|
|
606
|
+
|
|
607
|
+
@classmethod
|
|
608
|
+
def lte(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A] | A) -> 'HavingCompConstraint4[TC, TC1, TC2, TC3, A]':
|
|
609
|
+
return HavingCompConstraint4(prop, other, Comparison.LTE)
|
|
610
|
+
|
|
611
|
+
@classmethod
|
|
612
|
+
def ne(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A] | A) -> 'HavingCompConstraint4[TC, TC1, TC2, TC3, A]':
|
|
613
|
+
return HavingCompConstraint4(prop, other, Comparison.NE)
|
|
614
|
+
|
|
615
|
+
@classmethod
|
|
616
|
+
def like(cls, prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A], other: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A] | A) -> 'HavingCompConstraint4[TC, TC1, TC2, TC3, A]':
|
|
617
|
+
return HavingCompConstraint4(prop, other, Comparison.LIKE)
|
|
618
|
+
|
|
619
|
+
@classmethod
|
|
620
|
+
def between(
|
|
621
|
+
cls,
|
|
622
|
+
prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A],
|
|
623
|
+
lower: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A] | A,
|
|
624
|
+
upper: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A] | A,
|
|
625
|
+
) -> 'HavingBetweenConstraint4[TC, TC1, TC2, TC3, A]':
|
|
626
|
+
return HavingBetweenConstraint4(prop, lower, upper)
|
|
627
|
+
|
|
628
|
+
@classmethod
|
|
629
|
+
def in_(
|
|
630
|
+
cls,
|
|
631
|
+
prop: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A],
|
|
632
|
+
values: Sequence[Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A] | A],
|
|
633
|
+
) -> 'HavingInConstraint4[TC, TC1, TC2, TC3, A]':
|
|
634
|
+
return HavingInConstraint4(prop, values)
|
|
635
|
+
|
|
636
|
+
def AND(self, other: 'HavingConstraint4[TC, TC1, TC2, TC3]') -> 'HavingAndConstraint4[TC, TC1, TC2, TC3]':
|
|
637
|
+
return HavingAndConstraint4([self, other])
|
|
638
|
+
|
|
639
|
+
def OR(self, other: 'HavingConstraint4[TC, TC1, TC2, TC3]') -> 'HavingOrConstraint4[TC, TC1, TC2, TC3]':
|
|
640
|
+
return HavingOrConstraint4([self, other])
|
|
641
|
+
|
|
642
|
+
@property
|
|
643
|
+
def NOT(self) -> 'HavingNotConstraint4[TC, TC1, TC2, TC3]':
|
|
644
|
+
return HavingNotConstraint4(self)
|
|
645
|
+
|
|
646
|
+
@dataclass(frozen=True)
|
|
647
|
+
class HavingOrConstraint4(HavingConstraint4[TC, TC1, TC2, TC3]):
|
|
648
|
+
constraints: List[HavingConstraint4[TC, TC1, TC2, TC3]]
|
|
649
|
+
|
|
650
|
+
def to_sql(self) -> str:
|
|
651
|
+
return '(' + ' OR '.join(c.to_sql() for c in self.constraints) + ')'
|
|
652
|
+
|
|
653
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
654
|
+
parts_params = [c.to_sql_params() for c in self.constraints]
|
|
655
|
+
sql = '(' + ' OR '.join(part for part, _ in parts_params) + ')'
|
|
656
|
+
params = [p for _, params in parts_params for p in params]
|
|
657
|
+
return sql, params
|
|
658
|
+
|
|
659
|
+
@dataclass(frozen=True)
|
|
660
|
+
class HavingAndConstraint4(HavingConstraint4[TC, TC1, TC2, TC3]):
|
|
661
|
+
constraints: List[HavingConstraint4[TC, TC1, TC2, TC3]]
|
|
662
|
+
|
|
663
|
+
def to_sql(self) -> str:
|
|
664
|
+
return '(' + ' AND '.join(c.to_sql() for c in self.constraints) + ')'
|
|
665
|
+
|
|
666
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
667
|
+
parts_params = [c.to_sql_params() for c in self.constraints]
|
|
668
|
+
sql = '(' + ' AND '.join(part for part, _ in parts_params) + ')'
|
|
669
|
+
params = [p for _, params in parts_params for p in params]
|
|
670
|
+
return sql, params
|
|
671
|
+
|
|
672
|
+
@dataclass(frozen=True)
|
|
673
|
+
class HavingNotConstraint4(HavingConstraint4[TC, TC1, TC2, TC3]):
|
|
674
|
+
constraint: HavingConstraint4[TC, TC1, TC2, TC3]
|
|
675
|
+
|
|
676
|
+
def to_sql(self) -> str:
|
|
677
|
+
return 'NOT (' + self.constraint.to_sql() + ')'
|
|
678
|
+
|
|
679
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
680
|
+
sql, params = self.constraint.to_sql_params()
|
|
681
|
+
return 'NOT (' + sql + ')', params
|
|
682
|
+
|
|
683
|
+
@dataclass(frozen=True)
|
|
684
|
+
class HavingCompConstraint4(Generic[TC, TC1, TC2, TC3, A], HavingConstraint4[TC, TC1, TC2, TC3]):
|
|
685
|
+
value_1: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A]
|
|
686
|
+
value_2: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A] | A
|
|
687
|
+
comp: Comparison
|
|
688
|
+
|
|
689
|
+
def to_sql(self) -> str:
|
|
690
|
+
value_2 = _having_display_value(self.value_2, self.value_1.target is str, qualify=True)
|
|
691
|
+
return _having_ref_sql(self.value_1, qualify=True) + ' ' + _comparison_symbol(self.comp) + ' ' + value_2
|
|
692
|
+
|
|
693
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
694
|
+
value_2, params = _render_having_value(self.value_2, qualify=True)
|
|
695
|
+
return _having_ref_sql(self.value_1, qualify=True) + ' ' + _comparison_symbol(self.comp) + ' ' + value_2, params
|
|
696
|
+
|
|
697
|
+
@dataclass(frozen=True)
|
|
698
|
+
class HavingBetweenConstraint4(Generic[TC, TC1, TC2, TC3, A], HavingConstraint4[TC, TC1, TC2, TC3]):
|
|
699
|
+
value: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A]
|
|
700
|
+
lower: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A] | A
|
|
701
|
+
upper: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A] | A
|
|
702
|
+
|
|
703
|
+
def to_sql(self) -> str:
|
|
704
|
+
target_is_str = self.value.target is str
|
|
705
|
+
lower = _having_display_value(self.lower, target_is_str, qualify=True)
|
|
706
|
+
upper = _having_display_value(self.upper, target_is_str, qualify=True)
|
|
707
|
+
return f'{_having_ref_sql(self.value, qualify=True)} BETWEEN {lower} AND {upper}'
|
|
708
|
+
|
|
709
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
710
|
+
lower, lower_params = _render_having_value(self.lower, qualify=True)
|
|
711
|
+
upper, upper_params = _render_having_value(self.upper, qualify=True)
|
|
712
|
+
return f'{_having_ref_sql(self.value, qualify=True)} BETWEEN {lower} AND {upper}', lower_params + upper_params
|
|
713
|
+
|
|
714
|
+
@dataclass(frozen=True)
|
|
715
|
+
class HavingInConstraint4(Generic[TC, TC1, TC2, TC3, A], HavingConstraint4[TC, TC1, TC2, TC3]):
|
|
716
|
+
value: Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A]
|
|
717
|
+
values: Sequence[Prop[TC, A] | PropOpt[TC, A] | Computed[TC, A] | Prop[TC1, A] | PropOpt[TC1, A] | Computed[TC1, A] | Prop[TC2, A] | PropOpt[TC2, A] | Computed[TC2, A] | Prop[TC3, A] | PropOpt[TC3, A] | Computed[TC3, A] | A]
|
|
718
|
+
|
|
719
|
+
def to_sql(self) -> str:
|
|
720
|
+
target_is_str = self.value.target is str
|
|
721
|
+
values = [_having_display_value(c, target_is_str, qualify=True) for c in self.values]
|
|
722
|
+
return f'{_having_ref_sql(self.value, qualify=True)} IN ({", ".join(values)})'
|
|
723
|
+
|
|
724
|
+
def to_sql_params(self) -> Tuple[str, List[Any]]:
|
|
725
|
+
rendered = [_render_having_value(c, qualify=True) for c in self.values]
|
|
726
|
+
sql = f'{_having_ref_sql(self.value, qualify=True)} IN ({", ".join(part for part, _ in rendered)})'
|
|
727
|
+
params = [p for _, params in rendered for p in params]
|
|
728
|
+
return sql, params
|