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
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
from enum import IntEnum, StrEnum
|
|
2
|
+
from typing import TYPE_CHECKING, Literal
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from iceaxe.__tests__.conf_models import (
|
|
7
|
+
ArtifactDemo,
|
|
8
|
+
Employee,
|
|
9
|
+
FunctionDemoModel,
|
|
10
|
+
UserDemo,
|
|
11
|
+
)
|
|
12
|
+
from iceaxe.functions import func
|
|
13
|
+
from iceaxe.queries import QueryBuilder, and_, or_, select
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class UserStatus(StrEnum):
|
|
17
|
+
ACTIVE = "active"
|
|
18
|
+
INACTIVE = "inactive"
|
|
19
|
+
PENDING = "pending"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_select():
|
|
23
|
+
new_query = QueryBuilder().select(UserDemo)
|
|
24
|
+
assert new_query.build() == (
|
|
25
|
+
'SELECT "userdemo"."id" AS "userdemo_id", "userdemo"."name" AS '
|
|
26
|
+
'"userdemo_name", "userdemo"."email" AS "userdemo_email" FROM "userdemo"',
|
|
27
|
+
[],
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_select_single_field():
|
|
32
|
+
new_query = QueryBuilder().select(UserDemo.email)
|
|
33
|
+
assert new_query.build() == (
|
|
34
|
+
'SELECT "userdemo"."email" AS "userdemo_email" FROM "userdemo"',
|
|
35
|
+
[],
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_select_multiple_fields():
|
|
40
|
+
new_query = QueryBuilder().select((UserDemo.id, UserDemo.name, UserDemo.email))
|
|
41
|
+
assert new_query.build() == (
|
|
42
|
+
'SELECT "userdemo"."id" AS "userdemo_id", "userdemo"."name" AS "userdemo_name", "userdemo"."email" AS "userdemo_email" FROM "userdemo"',
|
|
43
|
+
[],
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_where():
|
|
48
|
+
new_query = QueryBuilder().select(UserDemo.id).where(UserDemo.id > 0)
|
|
49
|
+
assert new_query.build() == (
|
|
50
|
+
'SELECT "userdemo"."id" AS "userdemo_id" FROM "userdemo" WHERE "userdemo"."id" > $1',
|
|
51
|
+
[0],
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_where_columns():
|
|
56
|
+
new_query = (
|
|
57
|
+
QueryBuilder().select(UserDemo.id).where(UserDemo.name == UserDemo.email)
|
|
58
|
+
)
|
|
59
|
+
assert new_query.build() == (
|
|
60
|
+
'SELECT "userdemo"."id" AS "userdemo_id" FROM "userdemo" WHERE "userdemo"."name" IS NOT DISTINCT FROM "userdemo"."email"',
|
|
61
|
+
[],
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_multiple_where_conditions():
|
|
66
|
+
new_query = (
|
|
67
|
+
QueryBuilder()
|
|
68
|
+
.select(UserDemo.id)
|
|
69
|
+
.where(UserDemo.id > 0, UserDemo.name == "John")
|
|
70
|
+
)
|
|
71
|
+
assert new_query.build() == (
|
|
72
|
+
'SELECT "userdemo"."id" AS "userdemo_id" FROM "userdemo" WHERE "userdemo"."id" > $1 AND "userdemo"."name" = $2',
|
|
73
|
+
[0, "John"],
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_order_by():
|
|
78
|
+
new_query = QueryBuilder().select(UserDemo.id).order_by(UserDemo.id, "DESC")
|
|
79
|
+
assert new_query.build() == (
|
|
80
|
+
'SELECT "userdemo"."id" AS "userdemo_id" FROM "userdemo" ORDER BY "userdemo"."id" DESC',
|
|
81
|
+
[],
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_multiple_order_by():
|
|
86
|
+
new_query = (
|
|
87
|
+
QueryBuilder()
|
|
88
|
+
.select(UserDemo.id)
|
|
89
|
+
.order_by(UserDemo.id, "DESC")
|
|
90
|
+
.order_by(UserDemo.name, "ASC")
|
|
91
|
+
)
|
|
92
|
+
assert new_query.build() == (
|
|
93
|
+
'SELECT "userdemo"."id" AS "userdemo_id" FROM "userdemo" ORDER BY "userdemo"."id" DESC, "userdemo"."name" ASC',
|
|
94
|
+
[],
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_join():
|
|
99
|
+
new_query = (
|
|
100
|
+
QueryBuilder()
|
|
101
|
+
.select((UserDemo.id, ArtifactDemo.title))
|
|
102
|
+
.join(ArtifactDemo, UserDemo.id == ArtifactDemo.user_id)
|
|
103
|
+
)
|
|
104
|
+
assert new_query.build() == (
|
|
105
|
+
'SELECT "userdemo"."id" AS "userdemo_id", "artifactdemo"."title" AS "artifactdemo_title" FROM "userdemo" INNER JOIN "artifactdemo" ON "userdemo"."id" = "artifactdemo"."user_id"',
|
|
106
|
+
[],
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_left_join():
|
|
111
|
+
new_query = (
|
|
112
|
+
QueryBuilder()
|
|
113
|
+
.select((UserDemo.id, ArtifactDemo.title))
|
|
114
|
+
.join(ArtifactDemo, UserDemo.id == ArtifactDemo.user_id, "LEFT")
|
|
115
|
+
)
|
|
116
|
+
assert new_query.build() == (
|
|
117
|
+
'SELECT "userdemo"."id" AS "userdemo_id", "artifactdemo"."title" AS "artifactdemo_title" FROM "userdemo" LEFT JOIN "artifactdemo" ON "userdemo"."id" = "artifactdemo"."user_id"',
|
|
118
|
+
[],
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_limit():
|
|
123
|
+
new_query = QueryBuilder().select(UserDemo.id).limit(10)
|
|
124
|
+
assert new_query.build() == (
|
|
125
|
+
'SELECT "userdemo"."id" AS "userdemo_id" FROM "userdemo" LIMIT 10',
|
|
126
|
+
[],
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_offset():
|
|
131
|
+
new_query = QueryBuilder().select(UserDemo.id).offset(5)
|
|
132
|
+
assert new_query.build() == (
|
|
133
|
+
'SELECT "userdemo"."id" AS "userdemo_id" FROM "userdemo" OFFSET 5',
|
|
134
|
+
[],
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_limit_and_offset():
|
|
139
|
+
new_query = QueryBuilder().select(UserDemo.id).limit(10).offset(5)
|
|
140
|
+
assert new_query.build() == (
|
|
141
|
+
'SELECT "userdemo"."id" AS "userdemo_id" FROM "userdemo" LIMIT 10 OFFSET 5',
|
|
142
|
+
[],
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_group_by():
|
|
147
|
+
new_query = (
|
|
148
|
+
QueryBuilder()
|
|
149
|
+
.select((UserDemo.name, func.count(UserDemo.id)))
|
|
150
|
+
.group_by(UserDemo.name)
|
|
151
|
+
)
|
|
152
|
+
assert new_query.build() == (
|
|
153
|
+
'SELECT "userdemo"."name" AS "userdemo_name", count("userdemo"."id") AS aggregate_0 FROM "userdemo" GROUP BY "userdemo"."name"',
|
|
154
|
+
[],
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def test_update():
|
|
159
|
+
new_query = (
|
|
160
|
+
QueryBuilder()
|
|
161
|
+
.update(UserDemo)
|
|
162
|
+
.set(UserDemo.name, "John")
|
|
163
|
+
.where(UserDemo.id == 1)
|
|
164
|
+
)
|
|
165
|
+
assert new_query.build() == (
|
|
166
|
+
'UPDATE "userdemo" SET name = $1 WHERE "userdemo"."id" = $2',
|
|
167
|
+
["John", 1],
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def test_delete():
|
|
172
|
+
new_query = QueryBuilder().delete(UserDemo).where(UserDemo.id == 1)
|
|
173
|
+
assert new_query.build() == (
|
|
174
|
+
'DELETE FROM "userdemo" WHERE "userdemo"."id" = $1',
|
|
175
|
+
[1],
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def test_text():
|
|
180
|
+
new_query = QueryBuilder().text("SELECT * FROM users WHERE id = $1", 1)
|
|
181
|
+
assert new_query.build() == ("SELECT * FROM users WHERE id = $1", [1])
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def test_function_count():
|
|
185
|
+
new_query = QueryBuilder().select(func.count(UserDemo.id))
|
|
186
|
+
assert new_query.build() == (
|
|
187
|
+
'SELECT count("userdemo"."id") AS aggregate_0 FROM "userdemo"',
|
|
188
|
+
[],
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def test_function_distinct():
|
|
193
|
+
new_query = QueryBuilder().select(func.distinct(UserDemo.name))
|
|
194
|
+
assert new_query.build() == (
|
|
195
|
+
'SELECT distinct "userdemo"."name" AS aggregate_0 FROM "userdemo"',
|
|
196
|
+
[],
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def test_function_abs():
|
|
201
|
+
new_query = QueryBuilder().select(func.abs(FunctionDemoModel.balance))
|
|
202
|
+
assert new_query.build() == (
|
|
203
|
+
'SELECT abs("functiondemomodel"."balance") AS aggregate_0 FROM "functiondemomodel"',
|
|
204
|
+
[],
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def test_function_date_trunc():
|
|
209
|
+
new_query = QueryBuilder().select(
|
|
210
|
+
func.date_trunc("month", FunctionDemoModel.created_at)
|
|
211
|
+
)
|
|
212
|
+
assert new_query.build() == (
|
|
213
|
+
'SELECT date_trunc(\'month\', "functiondemomodel"."created_at") AS aggregate_0 FROM "functiondemomodel"',
|
|
214
|
+
[],
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def test_function_date_part():
|
|
219
|
+
new_query = QueryBuilder().select(
|
|
220
|
+
func.date_part("year", FunctionDemoModel.created_at)
|
|
221
|
+
)
|
|
222
|
+
assert new_query.build() == (
|
|
223
|
+
'SELECT date_part(\'year\', "functiondemomodel"."created_at") AS aggregate_0 FROM "functiondemomodel"',
|
|
224
|
+
[],
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def test_function_extract():
|
|
229
|
+
new_query = QueryBuilder().select(
|
|
230
|
+
func.extract("month", FunctionDemoModel.created_at)
|
|
231
|
+
)
|
|
232
|
+
assert new_query.build() == (
|
|
233
|
+
'SELECT extract(month from "functiondemomodel"."created_at") AS aggregate_0 FROM "functiondemomodel"',
|
|
234
|
+
[],
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def test_function_age():
|
|
239
|
+
# Test age with single argument
|
|
240
|
+
new_query = QueryBuilder().select(func.age(FunctionDemoModel.birth_date))
|
|
241
|
+
assert new_query.build() == (
|
|
242
|
+
'SELECT age("functiondemomodel"."birth_date") AS aggregate_0 FROM "functiondemomodel"',
|
|
243
|
+
[],
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# Test age with two arguments
|
|
247
|
+
new_query = QueryBuilder().select(
|
|
248
|
+
func.age(FunctionDemoModel.end_date, FunctionDemoModel.start_date)
|
|
249
|
+
)
|
|
250
|
+
assert new_query.build() == (
|
|
251
|
+
'SELECT age("functiondemomodel"."end_date", "functiondemomodel"."start_date") AS aggregate_0 FROM "functiondemomodel"',
|
|
252
|
+
[],
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def test_function_date():
|
|
257
|
+
new_query = QueryBuilder().select(func.date(FunctionDemoModel.created_at))
|
|
258
|
+
assert new_query.build() == (
|
|
259
|
+
'SELECT date("functiondemomodel"."created_at") AS aggregate_0 FROM "functiondemomodel"',
|
|
260
|
+
[],
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def test_function_transformations():
|
|
265
|
+
# Test string functions
|
|
266
|
+
new_query = QueryBuilder().select(
|
|
267
|
+
(
|
|
268
|
+
func.lower(FunctionDemoModel.name),
|
|
269
|
+
func.upper(FunctionDemoModel.name),
|
|
270
|
+
func.length(FunctionDemoModel.name),
|
|
271
|
+
func.trim(FunctionDemoModel.name),
|
|
272
|
+
func.substring(FunctionDemoModel.name, 1, 3),
|
|
273
|
+
)
|
|
274
|
+
)
|
|
275
|
+
assert new_query.build() == (
|
|
276
|
+
'SELECT lower("functiondemomodel"."name") AS aggregate_0, '
|
|
277
|
+
'upper("functiondemomodel"."name") AS aggregate_1, '
|
|
278
|
+
'length("functiondemomodel"."name") AS aggregate_2, '
|
|
279
|
+
'trim("functiondemomodel"."name") AS aggregate_3, '
|
|
280
|
+
'substring("functiondemomodel"."name" from 1 for 3) AS aggregate_4 '
|
|
281
|
+
'FROM "functiondemomodel"',
|
|
282
|
+
[],
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
# Test mathematical functions
|
|
286
|
+
new_query = QueryBuilder().select(
|
|
287
|
+
(
|
|
288
|
+
func.round(FunctionDemoModel.balance),
|
|
289
|
+
func.ceil(FunctionDemoModel.balance),
|
|
290
|
+
func.floor(FunctionDemoModel.balance),
|
|
291
|
+
func.power(FunctionDemoModel.balance, 2),
|
|
292
|
+
func.sqrt(FunctionDemoModel.balance),
|
|
293
|
+
)
|
|
294
|
+
)
|
|
295
|
+
assert new_query.build() == (
|
|
296
|
+
'SELECT round("functiondemomodel"."balance") AS aggregate_0, '
|
|
297
|
+
'ceil("functiondemomodel"."balance") AS aggregate_1, '
|
|
298
|
+
'floor("functiondemomodel"."balance") AS aggregate_2, '
|
|
299
|
+
'power("functiondemomodel"."balance", 2) AS aggregate_3, '
|
|
300
|
+
'sqrt("functiondemomodel"."balance") AS aggregate_4 '
|
|
301
|
+
'FROM "functiondemomodel"',
|
|
302
|
+
[],
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
# Test aggregate functions
|
|
306
|
+
new_query = QueryBuilder().select(
|
|
307
|
+
(
|
|
308
|
+
func.array_agg(FunctionDemoModel.name),
|
|
309
|
+
func.string_agg(FunctionDemoModel.name, ","),
|
|
310
|
+
)
|
|
311
|
+
)
|
|
312
|
+
assert new_query.build() == (
|
|
313
|
+
'SELECT array_agg("functiondemomodel"."name") AS aggregate_0, '
|
|
314
|
+
'string_agg("functiondemomodel"."name", \',\') AS aggregate_1 '
|
|
315
|
+
'FROM "functiondemomodel"',
|
|
316
|
+
[],
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
# Test type conversion functions
|
|
320
|
+
new_query = QueryBuilder().select(
|
|
321
|
+
(
|
|
322
|
+
func.cast(FunctionDemoModel.balance, int),
|
|
323
|
+
func.cast(FunctionDemoModel.name, UserStatus),
|
|
324
|
+
func.to_char(FunctionDemoModel.created_at, "YYYY-MM-DD"),
|
|
325
|
+
func.to_number(FunctionDemoModel.balance_str, "999999.99"),
|
|
326
|
+
func.to_timestamp(FunctionDemoModel.timestamp_str, "YYYY-MM-DD HH24:MI:SS"),
|
|
327
|
+
)
|
|
328
|
+
)
|
|
329
|
+
assert new_query.build() == (
|
|
330
|
+
'SELECT cast("functiondemomodel"."balance" as integer) AS aggregate_0, '
|
|
331
|
+
'cast("functiondemomodel"."name" as userstatus) AS aggregate_1, '
|
|
332
|
+
'to_char("functiondemomodel"."created_at", \'YYYY-MM-DD\') AS aggregate_2, '
|
|
333
|
+
'to_number("functiondemomodel"."balance_str", \'999999.99\') AS aggregate_3, '
|
|
334
|
+
'to_timestamp("functiondemomodel"."timestamp_str", \'YYYY-MM-DD HH24:MI:SS\') AS aggregate_4 '
|
|
335
|
+
'FROM "functiondemomodel"',
|
|
336
|
+
[],
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def test_invalid_where_condition():
|
|
341
|
+
with pytest.raises(ValueError):
|
|
342
|
+
QueryBuilder().select(UserDemo.id).where("invalid condition") # type: ignore
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def test_invalid_join_condition():
|
|
346
|
+
with pytest.raises(ValueError):
|
|
347
|
+
QueryBuilder().select(UserDemo.id).join(ArtifactDemo, "invalid condition") # type: ignore
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def test_invalid_group_by():
|
|
351
|
+
with pytest.raises(ValueError):
|
|
352
|
+
QueryBuilder().select(UserDemo.id).group_by("invalid field")
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
#
|
|
356
|
+
# Comparison groups
|
|
357
|
+
#
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def test_and_group():
|
|
361
|
+
new_query = (
|
|
362
|
+
QueryBuilder()
|
|
363
|
+
.select(UserDemo.id)
|
|
364
|
+
.where(
|
|
365
|
+
and_(
|
|
366
|
+
UserDemo.name == UserDemo.email,
|
|
367
|
+
UserDemo.id > 0,
|
|
368
|
+
)
|
|
369
|
+
)
|
|
370
|
+
)
|
|
371
|
+
assert new_query.build() == (
|
|
372
|
+
'SELECT "userdemo"."id" AS "userdemo_id" FROM "userdemo" WHERE ("userdemo"."name" IS NOT DISTINCT FROM "userdemo"."email" AND "userdemo"."id" > $1)',
|
|
373
|
+
[0],
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def test_or_group():
|
|
378
|
+
new_query = (
|
|
379
|
+
QueryBuilder()
|
|
380
|
+
.select(UserDemo.id)
|
|
381
|
+
.where(
|
|
382
|
+
or_(
|
|
383
|
+
UserDemo.name == UserDemo.email,
|
|
384
|
+
UserDemo.id > 0,
|
|
385
|
+
)
|
|
386
|
+
)
|
|
387
|
+
)
|
|
388
|
+
assert new_query.build() == (
|
|
389
|
+
'SELECT "userdemo"."id" AS "userdemo_id" FROM "userdemo" WHERE ("userdemo"."name" IS NOT DISTINCT FROM "userdemo"."email" OR "userdemo"."id" > $1)',
|
|
390
|
+
[0],
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def test_nested_and_or_group():
|
|
395
|
+
new_query = (
|
|
396
|
+
QueryBuilder()
|
|
397
|
+
.select(UserDemo.id)
|
|
398
|
+
.where(
|
|
399
|
+
and_(
|
|
400
|
+
or_(
|
|
401
|
+
UserDemo.name == UserDemo.email,
|
|
402
|
+
UserDemo.id > 0,
|
|
403
|
+
),
|
|
404
|
+
UserDemo.id < 10,
|
|
405
|
+
)
|
|
406
|
+
)
|
|
407
|
+
)
|
|
408
|
+
assert new_query.build() == (
|
|
409
|
+
'SELECT "userdemo"."id" AS "userdemo_id" FROM "userdemo" WHERE (("userdemo"."name" IS NOT DISTINCT FROM "userdemo"."email" OR "userdemo"."id" > $1) AND "userdemo"."id" < $2)',
|
|
410
|
+
[0, 10],
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
#
|
|
415
|
+
# Typehinting
|
|
416
|
+
# These checks are run AS part of the static typechecking we do
|
|
417
|
+
# for our codebase, not AS part of the pytest runtime.
|
|
418
|
+
#
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def test_select_single_typehint():
|
|
422
|
+
query = select(UserDemo)
|
|
423
|
+
if TYPE_CHECKING:
|
|
424
|
+
_: QueryBuilder[UserDemo, Literal["SELECT"]] = query
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def test_select_multiple_typehints():
|
|
428
|
+
query = select((UserDemo, UserDemo.id, UserDemo.name))
|
|
429
|
+
if TYPE_CHECKING:
|
|
430
|
+
_: QueryBuilder[tuple[UserDemo, int, str], Literal["SELECT"]] = query
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def test_allow_branching():
|
|
434
|
+
base_query = select(UserDemo)
|
|
435
|
+
|
|
436
|
+
query_1 = base_query.limit(1)
|
|
437
|
+
query_2 = base_query.limit(2)
|
|
438
|
+
|
|
439
|
+
assert query_1._limit_value == 1
|
|
440
|
+
assert query_2._limit_value == 2
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def test_distinct_on():
|
|
444
|
+
new_query = (
|
|
445
|
+
QueryBuilder()
|
|
446
|
+
.select((UserDemo.name, UserDemo.email))
|
|
447
|
+
.distinct_on(UserDemo.name)
|
|
448
|
+
)
|
|
449
|
+
assert new_query.build() == (
|
|
450
|
+
'SELECT DISTINCT ON ("userdemo"."name") "userdemo"."name" AS "userdemo_name", "userdemo"."email" AS "userdemo_email" FROM "userdemo"',
|
|
451
|
+
[],
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
def test_distinct_on_multiple_fields():
|
|
456
|
+
new_query = (
|
|
457
|
+
QueryBuilder()
|
|
458
|
+
.select((UserDemo.name, UserDemo.email))
|
|
459
|
+
.distinct_on(UserDemo.name, UserDemo.email)
|
|
460
|
+
)
|
|
461
|
+
assert new_query.build() == (
|
|
462
|
+
'SELECT DISTINCT ON ("userdemo"."name", "userdemo"."email") "userdemo"."name" AS "userdemo_name", "userdemo"."email" AS "userdemo_email" FROM "userdemo"',
|
|
463
|
+
[],
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def test_for_update_basic():
|
|
468
|
+
new_query = QueryBuilder().select(UserDemo).for_update()
|
|
469
|
+
assert new_query.build() == (
|
|
470
|
+
'SELECT "userdemo"."id" AS "userdemo_id", "userdemo"."name" AS '
|
|
471
|
+
'"userdemo_name", "userdemo"."email" AS "userdemo_email" FROM "userdemo" FOR UPDATE',
|
|
472
|
+
[],
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def test_for_update_nowait():
|
|
477
|
+
new_query = QueryBuilder().select(UserDemo).for_update(nowait=True)
|
|
478
|
+
assert new_query.build() == (
|
|
479
|
+
'SELECT "userdemo"."id" AS "userdemo_id", "userdemo"."name" AS '
|
|
480
|
+
'"userdemo_name", "userdemo"."email" AS "userdemo_email" FROM "userdemo" FOR UPDATE NOWAIT',
|
|
481
|
+
[],
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def test_for_update_skip_locked():
|
|
486
|
+
new_query = QueryBuilder().select(UserDemo).for_update(skip_locked=True)
|
|
487
|
+
assert new_query.build() == (
|
|
488
|
+
'SELECT "userdemo"."id" AS "userdemo_id", "userdemo"."name" AS '
|
|
489
|
+
'"userdemo_name", "userdemo"."email" AS "userdemo_email" FROM "userdemo" FOR UPDATE SKIP LOCKED',
|
|
490
|
+
[],
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def test_for_update_of():
|
|
495
|
+
new_query = QueryBuilder().select(UserDemo).for_update(of=(UserDemo,))
|
|
496
|
+
assert new_query.build() == (
|
|
497
|
+
'SELECT "userdemo"."id" AS "userdemo_id", "userdemo"."name" AS '
|
|
498
|
+
'"userdemo_name", "userdemo"."email" AS "userdemo_email" FROM "userdemo" FOR UPDATE OF "userdemo"',
|
|
499
|
+
[],
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
def test_for_update_multiple_calls():
|
|
504
|
+
new_query = (
|
|
505
|
+
QueryBuilder()
|
|
506
|
+
.select(UserDemo)
|
|
507
|
+
.for_update(of=(UserDemo,))
|
|
508
|
+
.for_update(nowait=True)
|
|
509
|
+
)
|
|
510
|
+
assert new_query.build() == (
|
|
511
|
+
'SELECT "userdemo"."id" AS "userdemo_id", "userdemo"."name" AS '
|
|
512
|
+
'"userdemo_name", "userdemo"."email" AS "userdemo_email" FROM "userdemo" FOR UPDATE OF "userdemo" NOWAIT',
|
|
513
|
+
[],
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
def test_for_update_multiple_of():
|
|
518
|
+
new_query = (
|
|
519
|
+
QueryBuilder()
|
|
520
|
+
.select((UserDemo, ArtifactDemo))
|
|
521
|
+
.join(ArtifactDemo, UserDemo.id == ArtifactDemo.user_id)
|
|
522
|
+
.for_update(of=(UserDemo,))
|
|
523
|
+
.for_update(of=(ArtifactDemo,))
|
|
524
|
+
)
|
|
525
|
+
assert new_query.build() == (
|
|
526
|
+
'SELECT "userdemo"."id" AS "userdemo_id", "userdemo"."name" AS "userdemo_name", "userdemo"."email" AS "userdemo_email", '
|
|
527
|
+
'"artifactdemo"."id" AS "artifactdemo_id", "artifactdemo"."title" AS "artifactdemo_title", "artifactdemo"."user_id" AS "artifactdemo_user_id" '
|
|
528
|
+
'FROM "userdemo" INNER JOIN "artifactdemo" ON "userdemo"."id" = "artifactdemo"."user_id" '
|
|
529
|
+
'FOR UPDATE OF "artifactdemo", "userdemo"',
|
|
530
|
+
[],
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def test_function_cast_enum():
|
|
535
|
+
"""
|
|
536
|
+
Test casting to enum types.
|
|
537
|
+
"""
|
|
538
|
+
|
|
539
|
+
class UserStatus(StrEnum):
|
|
540
|
+
ACTIVE = "active"
|
|
541
|
+
INACTIVE = "inactive"
|
|
542
|
+
PENDING = "pending"
|
|
543
|
+
|
|
544
|
+
class UserLevel(IntEnum):
|
|
545
|
+
BASIC = 1
|
|
546
|
+
PREMIUM = 2
|
|
547
|
+
VIP = 3
|
|
548
|
+
|
|
549
|
+
# Test casting to StrEnum
|
|
550
|
+
new_query = QueryBuilder().select(func.cast(FunctionDemoModel.name, UserStatus))
|
|
551
|
+
assert new_query.build() == (
|
|
552
|
+
'SELECT cast("functiondemomodel"."name" as userstatus) AS aggregate_0 '
|
|
553
|
+
'FROM "functiondemomodel"',
|
|
554
|
+
[],
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
# Test casting to IntEnum
|
|
558
|
+
new_query = QueryBuilder().select(func.cast(FunctionDemoModel.balance, UserLevel))
|
|
559
|
+
assert new_query.build() == (
|
|
560
|
+
'SELECT cast("functiondemomodel"."balance" as userlevel) AS aggregate_0 '
|
|
561
|
+
'FROM "functiondemomodel"',
|
|
562
|
+
[],
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
def test_multiple_group_by():
|
|
567
|
+
new_query = (
|
|
568
|
+
QueryBuilder()
|
|
569
|
+
.select(
|
|
570
|
+
(
|
|
571
|
+
Employee.department,
|
|
572
|
+
Employee.last_name,
|
|
573
|
+
func.count(Employee.id),
|
|
574
|
+
func.avg(Employee.salary),
|
|
575
|
+
)
|
|
576
|
+
)
|
|
577
|
+
.group_by(Employee.department)
|
|
578
|
+
.group_by(Employee.last_name)
|
|
579
|
+
)
|
|
580
|
+
assert new_query.build() == (
|
|
581
|
+
'SELECT "employee"."department" AS "employee_department", '
|
|
582
|
+
'"employee"."last_name" AS "employee_last_name", '
|
|
583
|
+
'count("employee"."id") AS aggregate_0, '
|
|
584
|
+
'avg("employee"."salary") AS aggregate_1 '
|
|
585
|
+
'FROM "employee" '
|
|
586
|
+
'GROUP BY "employee"."department", "employee"."last_name"',
|
|
587
|
+
[],
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
def test_group_by_with_function():
|
|
592
|
+
new_query = (
|
|
593
|
+
QueryBuilder()
|
|
594
|
+
.select(
|
|
595
|
+
(
|
|
596
|
+
func.date_trunc("month", FunctionDemoModel.created_at),
|
|
597
|
+
func.count(FunctionDemoModel.id),
|
|
598
|
+
)
|
|
599
|
+
)
|
|
600
|
+
.group_by(func.date_trunc("month", FunctionDemoModel.created_at))
|
|
601
|
+
)
|
|
602
|
+
assert new_query.build() == (
|
|
603
|
+
'SELECT date_trunc(\'month\', "functiondemomodel"."created_at") AS aggregate_0, count("functiondemomodel"."id") AS aggregate_1 FROM "functiondemomodel" GROUP BY date_trunc(\'month\', "functiondemomodel"."created_at")',
|
|
604
|
+
[],
|
|
605
|
+
)
|