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.
@@ -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