iceaxe 0.7.1__cp313-cp313-macosx_11_0_arm64.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.
Potentially problematic release.
This version of iceaxe might be problematic. Click here for more details.
- iceaxe/__init__.py +20 -0
- iceaxe/__tests__/__init__.py +0 -0
- iceaxe/__tests__/benchmarks/__init__.py +0 -0
- iceaxe/__tests__/benchmarks/test_bulk_insert.py +45 -0
- iceaxe/__tests__/benchmarks/test_select.py +114 -0
- iceaxe/__tests__/conf_models.py +133 -0
- iceaxe/__tests__/conftest.py +204 -0
- iceaxe/__tests__/docker_helpers.py +208 -0
- iceaxe/__tests__/helpers.py +268 -0
- iceaxe/__tests__/migrations/__init__.py +0 -0
- iceaxe/__tests__/migrations/conftest.py +36 -0
- iceaxe/__tests__/migrations/test_action_sorter.py +237 -0
- iceaxe/__tests__/migrations/test_generator.py +140 -0
- iceaxe/__tests__/migrations/test_generics.py +91 -0
- iceaxe/__tests__/mountaineer/__init__.py +0 -0
- iceaxe/__tests__/mountaineer/dependencies/__init__.py +0 -0
- iceaxe/__tests__/mountaineer/dependencies/test_core.py +76 -0
- iceaxe/__tests__/schemas/__init__.py +0 -0
- iceaxe/__tests__/schemas/test_actions.py +1264 -0
- iceaxe/__tests__/schemas/test_cli.py +25 -0
- iceaxe/__tests__/schemas/test_db_memory_serializer.py +1525 -0
- iceaxe/__tests__/schemas/test_db_serializer.py +398 -0
- iceaxe/__tests__/schemas/test_db_stubs.py +190 -0
- iceaxe/__tests__/test_alias.py +83 -0
- iceaxe/__tests__/test_base.py +52 -0
- iceaxe/__tests__/test_comparison.py +383 -0
- iceaxe/__tests__/test_field.py +11 -0
- iceaxe/__tests__/test_helpers.py +9 -0
- iceaxe/__tests__/test_modifications.py +151 -0
- iceaxe/__tests__/test_queries.py +605 -0
- iceaxe/__tests__/test_queries_str.py +173 -0
- iceaxe/__tests__/test_session.py +1511 -0
- iceaxe/__tests__/test_text_search.py +287 -0
- iceaxe/alias_values.py +67 -0
- iceaxe/base.py +350 -0
- iceaxe/comparison.py +560 -0
- iceaxe/field.py +250 -0
- iceaxe/functions.py +906 -0
- iceaxe/generics.py +140 -0
- iceaxe/io.py +107 -0
- iceaxe/logging.py +91 -0
- iceaxe/migrations/__init__.py +5 -0
- iceaxe/migrations/action_sorter.py +98 -0
- iceaxe/migrations/cli.py +228 -0
- iceaxe/migrations/client_io.py +62 -0
- iceaxe/migrations/generator.py +404 -0
- iceaxe/migrations/migration.py +86 -0
- iceaxe/migrations/migrator.py +101 -0
- iceaxe/modifications.py +176 -0
- iceaxe/mountaineer/__init__.py +10 -0
- iceaxe/mountaineer/cli.py +74 -0
- iceaxe/mountaineer/config.py +46 -0
- iceaxe/mountaineer/dependencies/__init__.py +6 -0
- iceaxe/mountaineer/dependencies/core.py +67 -0
- iceaxe/postgres.py +133 -0
- iceaxe/py.typed +0 -0
- iceaxe/queries.py +1455 -0
- iceaxe/queries_str.py +294 -0
- iceaxe/schemas/__init__.py +0 -0
- iceaxe/schemas/actions.py +864 -0
- iceaxe/schemas/cli.py +30 -0
- iceaxe/schemas/db_memory_serializer.py +705 -0
- iceaxe/schemas/db_serializer.py +346 -0
- iceaxe/schemas/db_stubs.py +525 -0
- iceaxe/session.py +860 -0
- iceaxe/session_optimized.c +12035 -0
- iceaxe/session_optimized.cpython-313-darwin.so +0 -0
- iceaxe/session_optimized.pyx +212 -0
- iceaxe/sql_types.py +148 -0
- iceaxe/typing.py +73 -0
- iceaxe-0.7.1.dist-info/METADATA +261 -0
- iceaxe-0.7.1.dist-info/RECORD +75 -0
- iceaxe-0.7.1.dist-info/WHEEL +6 -0
- iceaxe-0.7.1.dist-info/licenses/LICENSE +21 -0
- iceaxe-0.7.1.dist-info/top_level.txt +1 -0
iceaxe/comparison.py
ADDED
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from enum import StrEnum
|
|
4
|
+
from typing import Any, Generic, Self, Sequence, TypeVar
|
|
5
|
+
|
|
6
|
+
from iceaxe.queries_str import QueryElementBase, QueryLiteral
|
|
7
|
+
from iceaxe.typing import is_column, is_comparison, is_comparison_group
|
|
8
|
+
|
|
9
|
+
T = TypeVar("T", bound="ComparisonBase")
|
|
10
|
+
J = TypeVar("J")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ComparisonType(StrEnum):
|
|
14
|
+
"""
|
|
15
|
+
Enumeration of SQL comparison operators used in query conditions.
|
|
16
|
+
These operators are used to build WHERE clauses and other conditional expressions.
|
|
17
|
+
|
|
18
|
+
```python {{sticky: True}}
|
|
19
|
+
# Using comparison operators in queries:
|
|
20
|
+
query = select(User).where(
|
|
21
|
+
User.name == "John", # Uses ComparisonType.EQ
|
|
22
|
+
User.age >= 21, # Uses ComparisonType.GE
|
|
23
|
+
User.status.in_(["active", "pending"]) # Uses ComparisonType.IN
|
|
24
|
+
)
|
|
25
|
+
```
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
EQ = "="
|
|
29
|
+
"""
|
|
30
|
+
Equal to comparison
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
NE = "!="
|
|
34
|
+
"""
|
|
35
|
+
Not equal to comparison
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
LT = "<"
|
|
39
|
+
"""
|
|
40
|
+
Less than comparison
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
LE = "<="
|
|
44
|
+
"""
|
|
45
|
+
Less than or equal to comparison
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
GT = ">"
|
|
49
|
+
"""
|
|
50
|
+
Greater than comparison
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
GE = ">="
|
|
54
|
+
"""
|
|
55
|
+
Greater than or equal to comparison
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
IN = "IN"
|
|
59
|
+
"""
|
|
60
|
+
Check if value is in a list of values
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
NOT_IN = "NOT IN"
|
|
64
|
+
"""
|
|
65
|
+
Check if value is not in a list of values
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
LIKE = "LIKE"
|
|
69
|
+
"""
|
|
70
|
+
Pattern matching with wildcards. Supports cases like:
|
|
71
|
+
- "John%" (matches "John Doe", "Johnny", etc.)
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
NOT_LIKE = "NOT LIKE"
|
|
75
|
+
"""
|
|
76
|
+
Negated pattern matching with wildcards. Supports cases like:
|
|
77
|
+
- "John%" (matches "Amy", "Bob", etc.)
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
ILIKE = "ILIKE"
|
|
81
|
+
"""
|
|
82
|
+
Case-insensitive pattern matching. Supports cases like:
|
|
83
|
+
- "john%" (matches "John Doe", "johnny", etc.)
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
NOT_ILIKE = "NOT ILIKE"
|
|
87
|
+
"""
|
|
88
|
+
Negated case-insensitive pattern matching. Supports cases like:
|
|
89
|
+
- "john%" (matches "Amy", "Bob", etc.)
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
IS = "IS"
|
|
93
|
+
"""
|
|
94
|
+
NULL comparison
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
IS_NOT = "IS NOT"
|
|
98
|
+
"""
|
|
99
|
+
NOT NULL comparison
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
IS_DISTINCT_FROM = "IS DISTINCT FROM"
|
|
103
|
+
"""
|
|
104
|
+
IS DISTINCT FROM comparison
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
IS_NOT_DISTINCT_FROM = "IS NOT DISTINCT FROM"
|
|
108
|
+
"""
|
|
109
|
+
IS NOT DISTINCT FROM comparison
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class ComparisonGroupType(StrEnum):
|
|
114
|
+
"""
|
|
115
|
+
Enumeration of logical operators used to combine multiple comparisons in SQL queries.
|
|
116
|
+
These operators allow building complex conditions by combining multiple WHERE clauses.
|
|
117
|
+
|
|
118
|
+
```python {{sticky: True}}
|
|
119
|
+
# Combining multiple conditions:
|
|
120
|
+
query = select(User).where(
|
|
121
|
+
and_(
|
|
122
|
+
User.age >= 21,
|
|
123
|
+
User.status == "active"
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Using OR conditions:
|
|
128
|
+
query = select(User).where(
|
|
129
|
+
or_(
|
|
130
|
+
User.role == "admin",
|
|
131
|
+
User.permissions.contains("manage_users")
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
```
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
AND = "AND"
|
|
138
|
+
"""
|
|
139
|
+
Logical AND operator, all conditions must be true
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
OR = "OR"
|
|
143
|
+
"""
|
|
144
|
+
Logical OR operator, at least one condition must be true
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@dataclass
|
|
149
|
+
class FieldComparison(Generic[T]):
|
|
150
|
+
"""
|
|
151
|
+
Represents a single SQL comparison operation between a field and a value or another field.
|
|
152
|
+
This class is typically created through the comparison operators (==, !=, >, <, etc.) on database fields.
|
|
153
|
+
|
|
154
|
+
```python {{sticky: True}}
|
|
155
|
+
# These expressions create FieldComparison objects:
|
|
156
|
+
User.age >= 21
|
|
157
|
+
User.status.in_(["active", "pending"])
|
|
158
|
+
User.name.like("%John%")
|
|
159
|
+
|
|
160
|
+
# Direct instantiation (rarely needed):
|
|
161
|
+
comparison = FieldComparison(
|
|
162
|
+
left=User.age,
|
|
163
|
+
comparison=ComparisonType.GE,
|
|
164
|
+
right=21
|
|
165
|
+
)
|
|
166
|
+
```
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
left: T
|
|
170
|
+
"""
|
|
171
|
+
The left side of the comparison (typically a database field)
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
comparison: ComparisonType
|
|
175
|
+
"""
|
|
176
|
+
The type of comparison to perform
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
right: T | Any
|
|
180
|
+
"""
|
|
181
|
+
The right side of the comparison (can be a value or another field)
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
python_expression: bool = False
|
|
185
|
+
"""
|
|
186
|
+
Implicit comparisons are created from Python expressions (like col1 == col2). If this
|
|
187
|
+
flag is False, it means the user explicitly used one of the column() helper functions like
|
|
188
|
+
.equals(), .not_equals(), etc.
|
|
189
|
+
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
def to_query(self, start: int = 1) -> tuple[QueryLiteral, list[Any]]:
|
|
193
|
+
"""
|
|
194
|
+
Converts the comparison to its SQL representation.
|
|
195
|
+
|
|
196
|
+
:param start: The starting index for query parameters, defaults to 1
|
|
197
|
+
:return: A tuple of the SQL query string and list of parameter values
|
|
198
|
+
"""
|
|
199
|
+
variables = []
|
|
200
|
+
|
|
201
|
+
field, left_vars = self.left.to_query()
|
|
202
|
+
variables += left_vars
|
|
203
|
+
|
|
204
|
+
value: QueryElementBase
|
|
205
|
+
comparison = self.comparison
|
|
206
|
+
if is_column(self.right):
|
|
207
|
+
# Support comparison to other fields (both identifiers)
|
|
208
|
+
value, right_vars = self.right.to_query()
|
|
209
|
+
variables += right_vars
|
|
210
|
+
else:
|
|
211
|
+
variable_offset = str(len(variables) + start)
|
|
212
|
+
|
|
213
|
+
if self.right is None:
|
|
214
|
+
# "None" values are not supported as query variables
|
|
215
|
+
value = QueryLiteral("NULL")
|
|
216
|
+
elif self.comparison in (ComparisonType.IN, ComparisonType.NOT_IN):
|
|
217
|
+
variables.append(self.right)
|
|
218
|
+
comparison_map = {
|
|
219
|
+
ComparisonType.IN: (ComparisonType.EQ, "ANY"),
|
|
220
|
+
ComparisonType.NOT_IN: (ComparisonType.NE, "ALL"),
|
|
221
|
+
}
|
|
222
|
+
comparison, operator = comparison_map[self.comparison]
|
|
223
|
+
value = QueryLiteral(f"{operator}(${variable_offset})")
|
|
224
|
+
else:
|
|
225
|
+
# Support comparison to static values
|
|
226
|
+
variables.append(self.right)
|
|
227
|
+
value = QueryLiteral(f"${variable_offset}")
|
|
228
|
+
|
|
229
|
+
return QueryLiteral(f"{field} {comparison.value} {value}"), variables
|
|
230
|
+
|
|
231
|
+
def force_join_constraints(self):
|
|
232
|
+
"""
|
|
233
|
+
Set the context of the comparison to be used in a join. This places certain constraints
|
|
234
|
+
on the comparison operations that can be applied, like using equals for columns instead
|
|
235
|
+
of IS DISTINCT FROM.
|
|
236
|
+
|
|
237
|
+
"""
|
|
238
|
+
comparison = self.comparison
|
|
239
|
+
|
|
240
|
+
# Only if we were created implicitly should we modify the comparison type
|
|
241
|
+
if self.python_expression:
|
|
242
|
+
if self.comparison == ComparisonType.IS_DISTINCT_FROM:
|
|
243
|
+
comparison = ComparisonType.NE
|
|
244
|
+
elif self.comparison == ComparisonType.IS_NOT_DISTINCT_FROM:
|
|
245
|
+
comparison = ComparisonType.EQ
|
|
246
|
+
|
|
247
|
+
return FieldComparison(left=self.left, comparison=comparison, right=self.right)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
@dataclass
|
|
251
|
+
class FieldComparisonGroup:
|
|
252
|
+
"""
|
|
253
|
+
Represents a group of field comparisons combined with a logical operator (AND/OR).
|
|
254
|
+
This class is typically created through the and_() and or_() functions.
|
|
255
|
+
|
|
256
|
+
```python {{sticky: True}}
|
|
257
|
+
# Using and_() to create an AND group:
|
|
258
|
+
query = select(User).where(
|
|
259
|
+
and_(
|
|
260
|
+
User.age >= 21,
|
|
261
|
+
User.status == "active",
|
|
262
|
+
or_(
|
|
263
|
+
User.role == "admin",
|
|
264
|
+
User.permissions.contains("manage_users")
|
|
265
|
+
)
|
|
266
|
+
)
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
# Direct instantiation (rarely needed):
|
|
270
|
+
group = FieldComparisonGroup(
|
|
271
|
+
type=ComparisonGroupType.AND,
|
|
272
|
+
elements=[
|
|
273
|
+
User.age >= 21,
|
|
274
|
+
User.status == "active"
|
|
275
|
+
]
|
|
276
|
+
)
|
|
277
|
+
```
|
|
278
|
+
"""
|
|
279
|
+
|
|
280
|
+
type: ComparisonGroupType
|
|
281
|
+
"""
|
|
282
|
+
The type of logical operator to use (AND/OR)
|
|
283
|
+
|
|
284
|
+
"""
|
|
285
|
+
|
|
286
|
+
elements: list["FieldComparison | FieldComparisonGroup"]
|
|
287
|
+
"""
|
|
288
|
+
List of comparisons or nested comparison groups to combine
|
|
289
|
+
"""
|
|
290
|
+
|
|
291
|
+
def to_query(self, start: int = 1) -> tuple[QueryLiteral, list[Any]]:
|
|
292
|
+
"""
|
|
293
|
+
Converts the comparison group to its SQL representation.
|
|
294
|
+
|
|
295
|
+
:param start: The starting index for query parameters, defaults to 1
|
|
296
|
+
:return: A tuple of the SQL query string and list of parameter values
|
|
297
|
+
"""
|
|
298
|
+
queries = ""
|
|
299
|
+
all_variables = []
|
|
300
|
+
|
|
301
|
+
for i, element in enumerate(self.elements):
|
|
302
|
+
if i > 0:
|
|
303
|
+
queries += f" {self.type.value} "
|
|
304
|
+
|
|
305
|
+
if is_comparison(element):
|
|
306
|
+
query, variables = element.to_query(start=start + len(all_variables))
|
|
307
|
+
queries += f"{query}"
|
|
308
|
+
all_variables += variables
|
|
309
|
+
elif is_comparison_group(element):
|
|
310
|
+
query, variables = element.to_query(start=start + len(all_variables))
|
|
311
|
+
queries += f"({query})"
|
|
312
|
+
all_variables += variables
|
|
313
|
+
else:
|
|
314
|
+
raise ValueError(f"Unexpected element type: {type(element)}")
|
|
315
|
+
|
|
316
|
+
return QueryLiteral(queries), all_variables
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
class ComparisonBase(ABC, Generic[J]):
|
|
320
|
+
"""
|
|
321
|
+
Abstract base class for database fields that can be used in comparisons.
|
|
322
|
+
Provides standard comparison operators and methods for SQL query generation.
|
|
323
|
+
|
|
324
|
+
This class implements Python's comparison magic methods (__eq__, __ne__, etc.)
|
|
325
|
+
to enable natural syntax for building SQL queries. It also provides additional
|
|
326
|
+
methods for SQL-specific operations like IN, LIKE, and NULL comparisons.
|
|
327
|
+
|
|
328
|
+
```python {{sticky: True}}
|
|
329
|
+
# ComparisonBase enables these operations on database fields:
|
|
330
|
+
User.age >= 21
|
|
331
|
+
User.status == "active"
|
|
332
|
+
User.name.like("%John%")
|
|
333
|
+
User.role.in_(["admin", "moderator"])
|
|
334
|
+
User.deleted_at.is_(None)
|
|
335
|
+
```
|
|
336
|
+
"""
|
|
337
|
+
|
|
338
|
+
def __eq__(self, other): # type: ignore
|
|
339
|
+
"""
|
|
340
|
+
Implements equality comparison, closer to Python's == operator.
|
|
341
|
+
Maps to SQL '=' or 'IS' for NULL comparisons.
|
|
342
|
+
Maps to SQL 'IS NOT DISTINCT FROM' for column comparisons.
|
|
343
|
+
|
|
344
|
+
:param other: Value to compare against
|
|
345
|
+
:return: A field comparison object
|
|
346
|
+
"""
|
|
347
|
+
raw_comparison: bool | None = None
|
|
348
|
+
if other is None:
|
|
349
|
+
raw_comparison = self.is_(None)
|
|
350
|
+
elif is_column(other):
|
|
351
|
+
raw_comparison = self.is_not_distinct_from(other)
|
|
352
|
+
|
|
353
|
+
comparison: FieldComparison[Self] = (
|
|
354
|
+
raw_comparison if raw_comparison is not None else self.equals(other)
|
|
355
|
+
) # type: ignore
|
|
356
|
+
comparison.python_expression = True
|
|
357
|
+
return comparison
|
|
358
|
+
|
|
359
|
+
def __ne__(self, other): # type: ignore
|
|
360
|
+
"""
|
|
361
|
+
Implements inequality comparison, closer to Python's != operator.
|
|
362
|
+
Maps to SQL '!=' or 'IS NOT' for NULL comparisons.
|
|
363
|
+
Maps to SQL 'IS DISTINCT FROM' for column comparisons.
|
|
364
|
+
|
|
365
|
+
:param other: Value to compare against
|
|
366
|
+
:return: A field comparison object
|
|
367
|
+
"""
|
|
368
|
+
raw_comparison: bool | None = None
|
|
369
|
+
if other is None:
|
|
370
|
+
raw_comparison = self.is_not(None)
|
|
371
|
+
elif is_column(other):
|
|
372
|
+
raw_comparison = self.is_distinct_from(other)
|
|
373
|
+
|
|
374
|
+
comparison: FieldComparison[Self] = (
|
|
375
|
+
raw_comparison if raw_comparison is not None else self.not_equals(other)
|
|
376
|
+
) # type: ignore
|
|
377
|
+
comparison.python_expression = True
|
|
378
|
+
return comparison
|
|
379
|
+
|
|
380
|
+
def __lt__(self, other):
|
|
381
|
+
"""
|
|
382
|
+
Implements less than comparison (<).
|
|
383
|
+
Maps to SQL '<'.
|
|
384
|
+
|
|
385
|
+
:param other: Value to compare against
|
|
386
|
+
:return: A field comparison object
|
|
387
|
+
"""
|
|
388
|
+
comparison = self._compare(ComparisonType.LT, other)
|
|
389
|
+
comparison.python_expression = True
|
|
390
|
+
return comparison
|
|
391
|
+
|
|
392
|
+
def __le__(self, other):
|
|
393
|
+
"""
|
|
394
|
+
Implements less than or equal comparison (<=).
|
|
395
|
+
Maps to SQL '<='.
|
|
396
|
+
|
|
397
|
+
:param other: Value to compare against
|
|
398
|
+
:return: A field comparison object
|
|
399
|
+
"""
|
|
400
|
+
comparison = self._compare(ComparisonType.LE, other)
|
|
401
|
+
comparison.python_expression = True
|
|
402
|
+
return comparison
|
|
403
|
+
|
|
404
|
+
def __gt__(self, other):
|
|
405
|
+
"""
|
|
406
|
+
Implements greater than comparison (>).
|
|
407
|
+
Maps to SQL '>'.
|
|
408
|
+
|
|
409
|
+
:param other: Value to compare against
|
|
410
|
+
:return: A field comparison object
|
|
411
|
+
"""
|
|
412
|
+
comparison = self._compare(ComparisonType.GT, other)
|
|
413
|
+
comparison.python_expression = True
|
|
414
|
+
return comparison
|
|
415
|
+
|
|
416
|
+
def __ge__(self, other):
|
|
417
|
+
"""
|
|
418
|
+
Implements greater than or equal comparison (>=).
|
|
419
|
+
Maps to SQL '>='.
|
|
420
|
+
|
|
421
|
+
:param other: Value to compare against
|
|
422
|
+
:return: A field comparison object
|
|
423
|
+
"""
|
|
424
|
+
comparison = self._compare(ComparisonType.GE, other)
|
|
425
|
+
comparison.python_expression = True
|
|
426
|
+
return comparison
|
|
427
|
+
|
|
428
|
+
def equals(self, other: Any) -> bool:
|
|
429
|
+
"""
|
|
430
|
+
Implements equality comparison (==).
|
|
431
|
+
|
|
432
|
+
:param other: Value to compare against
|
|
433
|
+
:return: A field comparison object
|
|
434
|
+
"""
|
|
435
|
+
return self._compare(ComparisonType.EQ, other) # type: ignore
|
|
436
|
+
|
|
437
|
+
def not_equals(self, other: Any) -> bool:
|
|
438
|
+
"""
|
|
439
|
+
Implements inequality comparison (!=).
|
|
440
|
+
|
|
441
|
+
:param other: Value to compare against
|
|
442
|
+
:return: A field comparison object
|
|
443
|
+
"""
|
|
444
|
+
return self._compare(ComparisonType.NE, other) # type: ignore
|
|
445
|
+
|
|
446
|
+
def is_(self, other: Any) -> bool:
|
|
447
|
+
"""
|
|
448
|
+
Implements SQL IS operator.
|
|
449
|
+
Checks if the field's value is NULL.
|
|
450
|
+
"""
|
|
451
|
+
return self._compare(ComparisonType.IS, other) # type: ignore
|
|
452
|
+
|
|
453
|
+
def is_not(self, other: Any) -> bool:
|
|
454
|
+
"""
|
|
455
|
+
Implements SQL IS NOT operator.
|
|
456
|
+
Checks if the field's value is not NULL.
|
|
457
|
+
"""
|
|
458
|
+
return self._compare(ComparisonType.IS_NOT, other) # type: ignore
|
|
459
|
+
|
|
460
|
+
def is_distinct_from(self, other: Any) -> bool:
|
|
461
|
+
"""
|
|
462
|
+
Implements SQL IS DISTINCT FROM operator.
|
|
463
|
+
Checks if the field's value is distinct from another value.
|
|
464
|
+
"""
|
|
465
|
+
return self._compare(ComparisonType.IS_DISTINCT_FROM, other) # type: ignore
|
|
466
|
+
|
|
467
|
+
def is_not_distinct_from(self, other: Any) -> bool:
|
|
468
|
+
"""
|
|
469
|
+
Implements SQL IS NOT DISTINCT FROM operator.
|
|
470
|
+
Checks if the field's value is not distinct from another value.
|
|
471
|
+
"""
|
|
472
|
+
return self._compare(ComparisonType.IS_NOT_DISTINCT_FROM, other) # type: ignore
|
|
473
|
+
|
|
474
|
+
def in_(self, other: Sequence[J]) -> bool:
|
|
475
|
+
"""
|
|
476
|
+
Implements SQL IN operator.
|
|
477
|
+
Checks if the field's value is in a sequence of values.
|
|
478
|
+
|
|
479
|
+
:param other: Sequence of values to check against
|
|
480
|
+
:return: A field comparison object
|
|
481
|
+
"""
|
|
482
|
+
return self._compare(ComparisonType.IN, other) # type: ignore
|
|
483
|
+
|
|
484
|
+
def not_in(self, other: Sequence[J]) -> bool:
|
|
485
|
+
"""
|
|
486
|
+
Implements SQL NOT IN operator.
|
|
487
|
+
Checks if the field's value is not in a sequence of values.
|
|
488
|
+
|
|
489
|
+
:param other: Sequence of values to check against
|
|
490
|
+
:return: A field comparison object
|
|
491
|
+
"""
|
|
492
|
+
return self._compare(ComparisonType.NOT_IN, other) # type: ignore
|
|
493
|
+
|
|
494
|
+
def like(
|
|
495
|
+
self: "ComparisonBase[str] | ComparisonBase[str | None]", other: str
|
|
496
|
+
) -> bool:
|
|
497
|
+
"""
|
|
498
|
+
Implements SQL LIKE operator for pattern matching.
|
|
499
|
+
Case-sensitive string pattern matching.
|
|
500
|
+
|
|
501
|
+
:param other: Pattern to match against
|
|
502
|
+
:return: A field comparison object
|
|
503
|
+
"""
|
|
504
|
+
return self._compare(ComparisonType.LIKE, other) # type: ignore
|
|
505
|
+
|
|
506
|
+
def not_like(
|
|
507
|
+
self: "ComparisonBase[str] | ComparisonBase[str | None]", other: str
|
|
508
|
+
) -> bool:
|
|
509
|
+
"""
|
|
510
|
+
Implements SQL NOT LIKE operator.
|
|
511
|
+
Case-sensitive string pattern non-matching.
|
|
512
|
+
|
|
513
|
+
:param other: Pattern to match against
|
|
514
|
+
:return: A field comparison object
|
|
515
|
+
"""
|
|
516
|
+
return self._compare(ComparisonType.NOT_LIKE, other) # type: ignore
|
|
517
|
+
|
|
518
|
+
def ilike(
|
|
519
|
+
self: "ComparisonBase[str] | ComparisonBase[str | None]", other: str
|
|
520
|
+
) -> bool:
|
|
521
|
+
"""
|
|
522
|
+
Implements PostgreSQL ILIKE operator.
|
|
523
|
+
Case-insensitive string pattern matching.
|
|
524
|
+
|
|
525
|
+
:param other: Pattern to match against
|
|
526
|
+
:return: A field comparison object
|
|
527
|
+
"""
|
|
528
|
+
return self._compare(ComparisonType.ILIKE, other) # type: ignore
|
|
529
|
+
|
|
530
|
+
def not_ilike(
|
|
531
|
+
self: "ComparisonBase[str] | ComparisonBase[str | None]", other: str
|
|
532
|
+
) -> bool:
|
|
533
|
+
"""
|
|
534
|
+
Implements PostgreSQL NOT ILIKE operator.
|
|
535
|
+
Case-insensitive string pattern non-matching.
|
|
536
|
+
|
|
537
|
+
:param other: Pattern to match against
|
|
538
|
+
:return: A field comparison object
|
|
539
|
+
"""
|
|
540
|
+
return self._compare(ComparisonType.NOT_ILIKE, other) # type: ignore
|
|
541
|
+
|
|
542
|
+
def _compare(self, comparison: ComparisonType, other: Any) -> FieldComparison[Self]:
|
|
543
|
+
"""
|
|
544
|
+
Internal method to create a field comparison.
|
|
545
|
+
|
|
546
|
+
:param comparison: Type of comparison to create
|
|
547
|
+
:param other: Value to compare against
|
|
548
|
+
:return: A field comparison object
|
|
549
|
+
"""
|
|
550
|
+
return FieldComparison(left=self, comparison=comparison, right=other)
|
|
551
|
+
|
|
552
|
+
@abstractmethod
|
|
553
|
+
def to_query(self) -> tuple["QueryLiteral", list[Any]]:
|
|
554
|
+
"""
|
|
555
|
+
Abstract method to convert the field to its SQL representation.
|
|
556
|
+
Must be implemented by subclasses.
|
|
557
|
+
|
|
558
|
+
:return: A tuple of the SQL query string and list of parameter values
|
|
559
|
+
"""
|
|
560
|
+
pass
|