ormlambda 2.9.0__py3-none-any.whl → 2.10.0__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.
- ormlambda/common/abstract_classes/abstract_model.py +25 -11
- ormlambda/common/abstract_classes/decomposition_query.py +106 -91
- ormlambda/common/errors/__init__.py +3 -0
- ormlambda/common/interfaces/ICustomAlias.py +4 -0
- ormlambda/common/interfaces/IDecompositionQuery.py +5 -1
- ormlambda/common/interfaces/IRepositoryBase.py +1 -1
- ormlambda/common/interfaces/IStatements.py +104 -42
- ormlambda/databases/my_sql/clauses/alias.py +31 -0
- ormlambda/databases/my_sql/clauses/group_by.py +2 -2
- ormlambda/databases/my_sql/clauses/joins.py +39 -1
- ormlambda/databases/my_sql/clauses/select.py +12 -9
- ormlambda/databases/my_sql/repository.py +36 -25
- ormlambda/databases/my_sql/statements.py +63 -37
- ormlambda/model_base.py +3 -3
- {ormlambda-2.9.0.dist-info → ormlambda-2.10.0.dist-info}/METADATA +1 -1
- {ormlambda-2.9.0.dist-info → ormlambda-2.10.0.dist-info}/RECORD +18 -15
- {ormlambda-2.9.0.dist-info → ormlambda-2.10.0.dist-info}/LICENSE +0 -0
- {ormlambda-2.9.0.dist-info → ormlambda-2.10.0.dist-info}/WHEEL +0 -0
@@ -1,5 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import Any, Callable, Iterable, Optional, Literal, Type, Union, overload, TYPE_CHECKING
|
2
|
+
from typing import Any, Callable, Iterable, Optional, Literal, Type, Union, overload, TYPE_CHECKING
|
3
3
|
from enum import Enum
|
4
4
|
from abc import abstractmethod, ABC
|
5
5
|
import enum
|
@@ -17,21 +17,48 @@ class OrderType(enum.Enum):
|
|
17
17
|
DESC = "DESC"
|
18
18
|
|
19
19
|
|
20
|
-
|
20
|
+
type OrderTypes = Literal["ASC", "DESC"] | OrderType | Iterable[OrderType]
|
21
|
+
|
22
|
+
|
23
|
+
type Tuple[T] = tuple[T, ...]
|
24
|
+
|
25
|
+
type SelectRes1[T1] = tuple[Tuple[T1]]
|
26
|
+
type SelectRes2[T1, T2] = tuple[*SelectRes1[T1], Tuple[T2]]
|
27
|
+
type SelectRes3[T1, T2, T3] = tuple[*SelectRes2[T1, T2], Tuple[T3]]
|
28
|
+
type SelectRes4[T1, T2, T3, T4] = tuple[*SelectRes3[T1, T2, T3], Tuple[T4]]
|
29
|
+
type SelectRes5[T1, T2, T3, T4, T5] = tuple[*SelectRes4[T1, T2, T3, T4], Tuple[T5]]
|
30
|
+
type SelectRes6[T1, T2, T3, T4, T5, T6] = tuple[*SelectRes5[T1, T2, T3, T4, T5], Tuple[T6]]
|
31
|
+
type SelectRes7[T1, T2, T3, T4, T5, T6, T7] = tuple[*SelectRes6[T1, T2, T3, T4, T5, T6], Tuple[T7]]
|
32
|
+
type SelectRes8[T1, T2, T3, T4, T5, T6, T7, T8] = tuple[*SelectRes7[T1, T2, T3, T4, T5, T6, T7], Tuple[T8]]
|
33
|
+
type SelectRes9[T1, T2, T3, T4, T5, T6, T7, T8, T9] = tuple[*SelectRes8[T1, T2, T3, T4, T5, T6, T7, T8], Tuple[T9]]
|
34
|
+
type SelectRes10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] = tuple[*SelectRes9[T1, T2, T3, T4, T5, T6, T7, T8, T9], Tuple[T10]]
|
35
|
+
|
36
|
+
|
37
|
+
type WhereCondition[T, T1] = Callable[[T, T1], bool]
|
38
|
+
type JoinCondition[T, T1] = tuple[T1, WhereCondition[T, T1], Optional[JoinType]]
|
39
|
+
|
40
|
+
type TupleJoins1[T, T1] = tuple[JoinCondition[T, T1]]
|
41
|
+
type TupleJoins2[T, T1, T2] = tuple[*TupleJoins1[T, T1], JoinCondition[T, T2]]
|
42
|
+
type TupleJoins3[T, T1, T2, T3] = tuple[*TupleJoins2[T, T1, T2], JoinCondition[T, T3]]
|
43
|
+
type TupleJoins4[T, T1, T2, T3, T4] = tuple[*TupleJoins3[T, T1, T2, T3], JoinCondition[T, T4]]
|
44
|
+
type TupleJoins5[T, T1, T2, T3, T4, T5] = tuple[*TupleJoins4[T, T1, T2, T3, T4], JoinCondition[T, T5]]
|
45
|
+
type TupleJoins6[T, T1, T2, T3, T4, T5, T6] = tuple[*TupleJoins5[T, T1, T2, T3, T4, T5], JoinCondition[T, T6]]
|
21
46
|
|
22
|
-
OrderTypes = OrderTypeString | OrderType | Iterable[OrderType]
|
23
47
|
|
24
48
|
# TODOH: This var is duplicated from 'src\ormlambda\databases\my_sql\clauses\create_database.py'
|
25
49
|
TypeExists = Literal["fail", "replace", "append"]
|
26
50
|
|
27
|
-
T =
|
28
|
-
WhereTypes = Union[Callable[[T], bool], Iterable[Callable[[T], bool]]]
|
51
|
+
type WhereTypes[T, *Ts] = Union[Callable[[T, *Ts], bool], Iterable[Callable[[T, *Ts], bool]]]
|
29
52
|
|
30
53
|
|
31
|
-
class IStatements[T
|
54
|
+
class IStatements[T, *Ts](ABC):
|
32
55
|
@abstractmethod
|
33
56
|
def create_table(self, if_exists: TypeExists) -> None: ...
|
34
57
|
|
58
|
+
# #TODOL [ ]: We must to implement this mehtod
|
59
|
+
# @abstractmethod
|
60
|
+
# def drop_table(self)->None: ...
|
61
|
+
|
35
62
|
@abstractmethod
|
36
63
|
def table_exists(self) -> bool: ...
|
37
64
|
|
@@ -121,15 +148,10 @@ class IStatements[T: Table](ABC):
|
|
121
148
|
def delete(self, instance: Optional[T | list[T]] = ...) -> None: ...
|
122
149
|
|
123
150
|
# endregion
|
124
|
-
# region join
|
125
|
-
@abstractmethod
|
126
|
-
def join(self, table_left: Table, table_right: Table, *, by: str) -> IStatements[T]: ...
|
127
151
|
|
128
|
-
# endregion
|
129
152
|
# region where
|
130
|
-
|
131
153
|
@overload
|
132
|
-
def where(self, conditions: Iterable[Callable[[T], bool]]) -> IStatements[T]:
|
154
|
+
def where(self, conditions: Iterable[Callable[[T, *Ts], bool]]) -> IStatements[T, *Ts]:
|
133
155
|
"""
|
134
156
|
This method creates where clause by passing the Iterable in lambda function
|
135
157
|
EXAMPLE
|
@@ -140,7 +162,7 @@ class IStatements[T: Table](ABC):
|
|
140
162
|
...
|
141
163
|
|
142
164
|
@overload
|
143
|
-
def where(self, conditions: Callable[[T], bool], **kwargs) -> IStatements[T]:
|
165
|
+
def where(self, conditions: Callable[[T, *Ts], bool], **kwargs) -> IStatements[T, *Ts]:
|
144
166
|
"""
|
145
167
|
PARAM
|
146
168
|
-
|
@@ -157,7 +179,7 @@ class IStatements[T: Table](ABC):
|
|
157
179
|
...
|
158
180
|
|
159
181
|
@abstractmethod
|
160
|
-
def where(self, conditions: WhereTypes = lambda: None, **kwargs) -> IStatements[T]: ...
|
182
|
+
def where(self, conditions: WhereTypes[T, *Ts] = lambda: None, **kwargs) -> IStatements[T, *Ts]: ...
|
161
183
|
|
162
184
|
# endregion
|
163
185
|
# region order
|
@@ -200,44 +222,75 @@ class IStatements[T: Table](ABC):
|
|
200
222
|
alias: bool = ...,
|
201
223
|
alias_name: Optional[str] = ...,
|
202
224
|
) -> TProp: ...
|
225
|
+
|
226
|
+
# endregion
|
227
|
+
# region join
|
228
|
+
|
229
|
+
@overload
|
230
|
+
def join[T1](self, table: T1, relation: WhereCondition[T, T1], join_type: Optional[JoinType]) -> IStatements[T, T1]: ...
|
231
|
+
@overload
|
232
|
+
def join[T1](self, table: JoinCondition[T, T1]) -> IStatements[T, T1]: ...
|
233
|
+
@overload
|
234
|
+
def join[T1](self, table: TupleJoins1[T, T1]) -> IStatements[T, T1]: ...
|
235
|
+
@overload
|
236
|
+
def join[T1, T2](self, table: TupleJoins2[T, T1, T2]) -> IStatements[T, T1, T2]: ...
|
237
|
+
@overload
|
238
|
+
def join[T1, T2, T3](self, table: TupleJoins3[T, T1, T2, T3]) -> IStatements[T, T1, T2, T3]: ...
|
239
|
+
@overload
|
240
|
+
def join[T1, T2, T3, T4](self, table: TupleJoins4[T, T1, T2, T3, T4]) -> IStatements[T, T1, T2, T3, T4]: ...
|
241
|
+
@overload
|
242
|
+
def join[T1, T2, T3, T4, T5](self, table: TupleJoins5[T, T1, T2, T3, T4, T5]) -> IStatements[T, T1, T2, T3, T4, T5]: ...
|
243
|
+
@overload
|
244
|
+
def join[T1, T2, T3, T4, T5, T6](self, table: TupleJoins6[T, T1, T2, T3, T4, T5, T6]) -> IStatements[T, T1, T2, T3, T4, T5, T6]: ...
|
245
|
+
|
246
|
+
@abstractmethod
|
247
|
+
def join[*FKTables](
|
248
|
+
self,
|
249
|
+
table: Optional[T] = ...,
|
250
|
+
relation: Optional[WhereCondition[T, FKTables]] = ...,
|
251
|
+
join_type: Optional[JoinType] = ...,
|
252
|
+
) -> IStatements[T, *FKTables]: ...
|
253
|
+
|
203
254
|
# endregion
|
204
255
|
# region select
|
205
256
|
@overload
|
206
|
-
def select(self) ->
|
257
|
+
def select(self) -> SelectRes1[T]: ...
|
207
258
|
@overload
|
208
|
-
def select[T1](self, selector: Callable[[T], T1], *, by: Optional[Enum] = ...) ->
|
259
|
+
def select[T1](self, selector: Callable[[T], T1], *, by: Optional[Enum] = ...) -> SelectRes1[T1]: ...
|
209
260
|
@overload
|
210
|
-
def select[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ...) ->
|
261
|
+
def select[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ...) -> SelectRes1[T1]: ...
|
211
262
|
@overload
|
212
|
-
def select[T1, T2](self, selector: Callable[[T], tuple[T1, T2]], *, by: Optional[Enum] = ...) ->
|
263
|
+
def select[T1, T2](self, selector: Callable[[T, *Ts], tuple[T1, T2]], *, by: Optional[Enum] = ...) -> SelectRes2[T1, T2]: ...
|
213
264
|
@overload
|
214
|
-
def select[T1, T2, T3](self, selector: Callable[[T], tuple[T1, T2, T3]], *, by: Optional[Enum] = ...) ->
|
265
|
+
def select[T1, T2, T3](self, selector: Callable[[T, *Ts], tuple[T1, T2, T3]], *, by: Optional[Enum] = ...) -> SelectRes3[T1, T2, T3]: ...
|
215
266
|
@overload
|
216
|
-
def select[T1, T2, T3, T4](self, selector: Callable[[T], tuple[T1, T2, T3, T4]], *, by: Optional[Enum] = ...) ->
|
267
|
+
def select[T1, T2, T3, T4](self, selector: Callable[[T, *Ts], tuple[T1, T2, T3, T4]], *, by: Optional[Enum] = ...) -> SelectRes4[T1, T2, T3, T4]: ...
|
217
268
|
@overload
|
218
|
-
def select[T1, T2, T3, T4, T5](self, selector: Callable[[T], tuple[T1, T2, T3, T4, T5]], *, by: Optional[Enum] = ...) ->
|
269
|
+
def select[T1, T2, T3, T4, T5](self, selector: Callable[[T, *Ts], tuple[T1, T2, T3, T4, T5]], *, by: Optional[Enum] = ...) -> SelectRes5[T1, T2, T3, T4, T5]: ...
|
219
270
|
@overload
|
220
|
-
def select[T1, T2, T3, T4, T5, T6](self, selector: Callable[[T], tuple[T1, T2, T3, T4, T5, T6]], *, by: Optional[Enum] = ...) ->
|
271
|
+
def select[T1, T2, T3, T4, T5, T6](self, selector: Callable[[T, *Ts], tuple[T1, T2, T3, T4, T5, T6]], *, by: Optional[Enum] = ...) -> SelectRes6[T1, T2, T3, T4, T5, T6]: ...
|
221
272
|
@overload
|
222
|
-
def select[T1, T2, T3, T4, T5, T6, T7](self, selector: Callable[[T], tuple[T1, T2, T3, T4, T5, T6, T7]], *, by: Optional[Enum] = ...) ->
|
273
|
+
def select[T1, T2, T3, T4, T5, T6, T7](self, selector: Callable[[T, *Ts], tuple[T1, T2, T3, T4, T5, T6, T7]], *, by: Optional[Enum] = ...) -> SelectRes7[T1, T2, T3, T4, T5, T6, T7]: ...
|
223
274
|
@overload
|
224
|
-
def select[T1, T2, T3, T4, T5, T6, T7, T8](self, selector: Callable[[T], tuple[T1, T2, T3, T4, T5, T6, T7, T8]], *, by: Optional[Enum] = ...) ->
|
275
|
+
def select[T1, T2, T3, T4, T5, T6, T7, T8](self, selector: Callable[[T, *Ts], tuple[T1, T2, T3, T4, T5, T6, T7, T8]], *, by: Optional[Enum] = ...) -> SelectRes8[T1, T2, T3, T4, T5, T6, T7, T8]: ...
|
225
276
|
@overload
|
226
|
-
def select[T1, T2, T3, T4, T5, T6, T7, T8, T9](self, selector: Callable[[T], tuple[T1, T2, T3, T4, T5, T6, T7, T8, T9]], *, by: Optional[Enum] = ...) ->
|
277
|
+
def select[T1, T2, T3, T4, T5, T6, T7, T8, T9](self, selector: Callable[[T, *Ts], tuple[T1, T2, T3, T4, T5, T6, T7, T8, T9]], *, by: Optional[Enum] = ...) -> SelectRes9[T1, T2, T3, T4, T5, T6, T7, T8, T9]: ...
|
227
278
|
@overload
|
228
|
-
def select[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10](
|
229
|
-
|
230
|
-
|
279
|
+
def select[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10](self, selector: Callable[[T, *Ts], tuple[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], *, by: Optional[Enum] = ...) -> SelectRes10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]: ...
|
280
|
+
|
281
|
+
@overload
|
282
|
+
def select[TFlavour](self, selector: Optional[Callable[[T, *Ts], tuple]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> TFlavour: ...
|
231
283
|
@overload
|
232
|
-
def select[
|
284
|
+
def select[TRes](self, selector: Optional[Callable[[T, *Ts], TRes]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[TRes, ...]: ...
|
233
285
|
@overload
|
234
|
-
def select[
|
286
|
+
def select[TRes](self, selector: Optional[Callable[[T, *Ts], tuple[TRes]]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[TRes, ...]: ...
|
235
287
|
@overload
|
236
|
-
def select[*
|
288
|
+
def select[*TRes](self, selector: Optional[Callable[[T, *Ts], tuple[*TRes]]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[tuple[*TRes]]: ...
|
237
289
|
@overload
|
238
|
-
def select[TFlavour](self, selector: Optional[Callable[[T], tuple]] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] =
|
290
|
+
def select[TFlavour](self, selector: Optional[Callable[[T, *Ts], tuple]] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> tuple[TFlavour]: ...
|
291
|
+
|
239
292
|
@abstractmethod
|
240
|
-
def select[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = ..., *, flavour: Type[TFlavour] = ..., by: JoinType =
|
293
|
+
def select[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T, *Ts], tuple[TValue, *Ts]]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour] = ..., by: JoinType = ..., **kwargs): ...
|
241
294
|
|
242
295
|
# endregion
|
243
296
|
# region select_one
|
@@ -246,24 +299,24 @@ class IStatements[T: Table](ABC):
|
|
246
299
|
@overload
|
247
300
|
def select_one[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> TFlavour: ...
|
248
301
|
@overload
|
249
|
-
def select_one[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
|
302
|
+
def select_one[T1](self, selector: Callable[[T, *Ts], tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
|
250
303
|
@overload
|
251
|
-
def select_one[*
|
304
|
+
def select_one[*TRes](self, selector: Callable[[T, *Ts], tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
|
252
305
|
@overload
|
253
|
-
def select_one[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type) -> T1: ...
|
306
|
+
def select_one[T1](self, selector: Callable[[T, *Ts], tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type) -> T1: ...
|
254
307
|
@overload
|
255
|
-
def select_one[T1, TFlavour](self, selector: Callable[[T], T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> T1: ...
|
308
|
+
def select_one[T1, TFlavour](self, selector: Callable[[T, *Ts], T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> T1: ...
|
256
309
|
@overload
|
257
|
-
def select_one[*Ts](self, selector: Callable[[T], tuple[*Ts]], *, by: Optional[Enum] = ..., flavour: Type[tuple]) -> tuple[*Ts]: ...
|
310
|
+
def select_one[*Ts](self, selector: Callable[[T, *Ts], tuple[*Ts]], *, by: Optional[Enum] = ..., flavour: Type[tuple]) -> tuple[*Ts]: ...
|
258
311
|
@overload
|
259
|
-
def select_one[TFlavour](self, selector: Callable[[T], tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> TFlavour: ...
|
312
|
+
def select_one[TFlavour](self, selector: Callable[[T, *Ts], tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> TFlavour: ...
|
260
313
|
@abstractmethod
|
261
|
-
def select_one[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Type[TFlavour] = ..., by: Optional[Enum] = ...): ...
|
314
|
+
def select_one[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T, *Ts], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Type[TFlavour] = ..., by: Optional[Enum] = ...): ...
|
262
315
|
|
263
316
|
# endregion
|
264
317
|
# region group_by
|
265
318
|
@abstractmethod
|
266
|
-
def group_by[TRepo
|
319
|
+
def group_by[TRepo](self, column: Callable[[T, *Ts], TRepo]) -> IStatements[T, *Ts]: ...
|
267
320
|
|
268
321
|
# endregion
|
269
322
|
|
@@ -271,7 +324,16 @@ class IStatements[T: Table](ABC):
|
|
271
324
|
def _build(self) -> str: ...
|
272
325
|
|
273
326
|
|
274
|
-
class IStatements_two_generic[T: Table, TRepo](IStatements[T]):
|
327
|
+
class IStatements_two_generic[T: Table, *Ts, TRepo](IStatements[T, *Ts]):
|
275
328
|
@property
|
276
329
|
@abstractmethod
|
277
330
|
def repository(self) -> IRepositoryBase[TRepo]: ...
|
331
|
+
|
332
|
+
@property
|
333
|
+
def query(self) -> str: ...
|
334
|
+
|
335
|
+
@property
|
336
|
+
def model(self) -> Type[T]: ...
|
337
|
+
|
338
|
+
@property
|
339
|
+
def models(self) -> tuple[*Ts]: ...
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import typing as tp
|
2
|
+
from ormlambda.common.abstract_classes.decomposition_query import ClauseInfo, DecompositionQueryBase
|
3
|
+
from ormlambda import Table
|
4
|
+
|
5
|
+
from ormlambda.common.interfaces import ICustomAlias
|
6
|
+
|
7
|
+
|
8
|
+
class Alias[T: tp.Type[Table], *Ts](DecompositionQueryBase[T,*Ts], ICustomAlias[T,*Ts]):
|
9
|
+
def __init__(
|
10
|
+
self,
|
11
|
+
table: T,
|
12
|
+
query: tp.Callable[[T, *Ts], tp.Any],
|
13
|
+
*,
|
14
|
+
alias: bool = True,
|
15
|
+
alias_name: str | None = None,
|
16
|
+
) -> None:
|
17
|
+
super().__init__(
|
18
|
+
table,
|
19
|
+
lambda_query=query,
|
20
|
+
alias=alias,
|
21
|
+
alias_name=alias_name,
|
22
|
+
)
|
23
|
+
|
24
|
+
def alias_children_resolver[Tclause: tp.Type[Table]](self, clause_info: ClauseInfo[Tclause]):
|
25
|
+
return self.alias_name
|
26
|
+
|
27
|
+
@property
|
28
|
+
def query(self) -> str:
|
29
|
+
assert len(self.all_clauses) == 1
|
30
|
+
|
31
|
+
return self.all_clauses[0].query
|
@@ -5,13 +5,13 @@ from ormlambda.common.interfaces.IAggregate import IAggregate
|
|
5
5
|
from ormlambda import Table
|
6
6
|
|
7
7
|
|
8
|
-
class GroupBy[T: tp.Type[Table], TProp](DecompositionQueryBase[T], IAggregate[T]):
|
8
|
+
class GroupBy[T: tp.Type[Table], *Ts, TProp](DecompositionQueryBase[T], IAggregate[T]):
|
9
9
|
CLAUSE: str = "GROUP BY"
|
10
10
|
|
11
11
|
def __init__(
|
12
12
|
self,
|
13
13
|
table: T,
|
14
|
-
column: tp.Callable[[T], TProp],
|
14
|
+
column: tp.Callable[[T, *Ts], TProp],
|
15
15
|
*,
|
16
16
|
alias: bool = True,
|
17
17
|
alias_name: str | None = None,
|
@@ -1,7 +1,9 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from
|
2
|
+
from collections import defaultdict
|
3
|
+
from typing import override, Callable, overload, Optional, TYPE_CHECKING, Type
|
3
4
|
|
4
5
|
|
6
|
+
from ormlambda.utils.module_tree.dfs_traversal import DFSTraversal
|
5
7
|
from ormlambda.common.interfaces.IQueryCommand import IQuery
|
6
8
|
from ormlambda import Disassembler
|
7
9
|
from ormlambda import JoinType
|
@@ -21,6 +23,13 @@ class JoinSelector[TLeft, TRight](IQuery):
|
|
21
23
|
"_compareop",
|
22
24
|
)
|
23
25
|
|
26
|
+
@override
|
27
|
+
def __repr__(self) -> str:
|
28
|
+
table_col_left: str = f"{self._orig_table.__table_name__}.{self._left_col}"
|
29
|
+
table_col_right: str = f"{self._table_right.__table_name__}.{self._right_col}"
|
30
|
+
|
31
|
+
return f"{IQuery.__name__}: {self.__class__.__name__} ({table_col_left} == {table_col_right})"
|
32
|
+
|
24
33
|
@overload
|
25
34
|
def __init__(
|
26
35
|
self,
|
@@ -102,3 +111,32 @@ class JoinSelector[TLeft, TRight](IQuery):
|
|
102
111
|
right_col, # second_col
|
103
112
|
]
|
104
113
|
return " ".join(list_)
|
114
|
+
|
115
|
+
@classmethod
|
116
|
+
def sort_join_selectors(cls, joins: set[JoinSelector]) -> tuple[JoinSelector]:
|
117
|
+
# FIXME [x]: How to sort when needed because it's not necessary at this point. It is for testing purpouse
|
118
|
+
if len(joins) == 1:
|
119
|
+
return tuple(joins)
|
120
|
+
|
121
|
+
join_object_map: dict[str, list[JoinSelector]] = defaultdict(list)
|
122
|
+
|
123
|
+
for obj in joins:
|
124
|
+
join_object_map[obj._orig_table].append(obj)
|
125
|
+
|
126
|
+
graph: dict[Type[Table], list[Type[Table]]] = defaultdict(list)
|
127
|
+
for join in joins:
|
128
|
+
graph[join._orig_table].append(join._table_right)
|
129
|
+
|
130
|
+
sorted_graph = DFSTraversal.sort(graph)[::-1]
|
131
|
+
|
132
|
+
if not sorted_graph:
|
133
|
+
return tuple(joins)
|
134
|
+
|
135
|
+
res = []
|
136
|
+
for table in sorted_graph:
|
137
|
+
tables = join_object_map[table]
|
138
|
+
|
139
|
+
if not tables:
|
140
|
+
continue
|
141
|
+
res.extend(tables)
|
142
|
+
return res
|
@@ -1,32 +1,37 @@
|
|
1
|
-
from
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import override, Type, Callable, TYPE_CHECKING, Optional
|
2
3
|
|
3
4
|
from ormlambda.common.abstract_classes.decomposition_query import DecompositionQueryBase
|
4
5
|
from ormlambda.common.enums.join_type import JoinType
|
5
6
|
from ormlambda.common.interfaces.IAggregate import IAggregate
|
6
7
|
import shapely as shp
|
7
8
|
|
9
|
+
|
8
10
|
if TYPE_CHECKING:
|
9
11
|
from ormlambda import Table
|
12
|
+
from .joins import JoinSelector
|
10
13
|
|
11
14
|
|
12
|
-
class Select[T: Type[Table]](DecompositionQueryBase[T]):
|
15
|
+
class Select[T: Type[Table], *Ts](DecompositionQueryBase[T, *Ts]):
|
13
16
|
CLAUSE: str = "SELECT"
|
14
17
|
|
15
18
|
def __init__(
|
16
19
|
self,
|
17
|
-
|
20
|
+
tables: tuple[T, *Ts],
|
18
21
|
lambda_query: Callable[[T], tuple] = lambda x: x,
|
19
22
|
*,
|
20
23
|
alias: bool = False,
|
21
24
|
alias_name: str | None = None,
|
25
|
+
joins: Optional[list[JoinSelector]] = None,
|
22
26
|
by: JoinType = JoinType.INNER_JOIN,
|
23
27
|
) -> None:
|
24
28
|
super().__init__(
|
25
|
-
|
29
|
+
tables,
|
26
30
|
lambda_query,
|
27
31
|
alias=alias,
|
28
32
|
alias_name=alias_name,
|
29
33
|
by=by,
|
34
|
+
joins=joins,
|
30
35
|
)
|
31
36
|
|
32
37
|
# @classmethod
|
@@ -40,18 +45,16 @@ class Select[T: Type[Table]](DecompositionQueryBase[T]):
|
|
40
45
|
cols: list[str] = []
|
41
46
|
for x in self.all_clauses:
|
42
47
|
if x.dtype is shp.Point:
|
43
|
-
cols.append(x.concat_with_alias(f"ST_AsText({self.
|
48
|
+
cols.append(x.concat_with_alias(f"ST_AsText({self.table.__table_name__}.{x.column})"))
|
44
49
|
else:
|
45
50
|
cols.append(x.query)
|
46
51
|
|
47
52
|
if isinstance(x._row_column, IAggregate) and x._row_column.has_foreign_keys:
|
48
|
-
self.
|
53
|
+
self._joins.update(x._row_column.fk_relationship)
|
49
54
|
|
50
55
|
col: str = ", ".join(cols)
|
51
|
-
query: str = f"{self.CLAUSE} {col} FROM {self.
|
52
|
-
alias = ""
|
56
|
+
query: str = f"{self.CLAUSE} {col} FROM {self.table.__table_name__}"
|
53
57
|
|
54
|
-
query += alias
|
55
58
|
if self.has_foreign_keys:
|
56
59
|
query += " " + self.stringify_foreign_key(" ")
|
57
60
|
|
@@ -22,13 +22,14 @@ if TYPE_CHECKING:
|
|
22
22
|
from ormlambda import Table
|
23
23
|
from src.ormlambda.databases.my_sql.clauses.select import Select
|
24
24
|
|
25
|
+
type TResponse[TFlavour, *Ts] = TFlavour | tuple[dict[str, tuple[*Ts]]] | tuple[tuple[*Ts]] | tuple[TFlavour]
|
26
|
+
|
25
27
|
|
26
28
|
class Response[TFlavour, *Ts]:
|
27
|
-
def __init__(self, response_values: list[tuple[*Ts]], columns: tuple[str], flavour: Type[TFlavour], model: Optional[Table] = None, select: Optional[Select] = None
|
29
|
+
def __init__(self, response_values: list[tuple[*Ts]], columns: tuple[str], flavour: Type[TFlavour], model: Optional[Table] = None, select: Optional[Select] = None) -> None:
|
28
30
|
self._response_values: list[tuple[*Ts]] = response_values
|
29
31
|
self._columns: tuple[str] = columns
|
30
32
|
self._flavour: Type[TFlavour] = flavour
|
31
|
-
self._kwargs: dict[str, Any] = kwargs
|
32
33
|
self._model: Table = model
|
33
34
|
self._select: Select = select
|
34
35
|
|
@@ -47,23 +48,28 @@ class Response[TFlavour, *Ts]:
|
|
47
48
|
def is_many(self) -> bool:
|
48
49
|
return self._response_values_index > 1
|
49
50
|
|
50
|
-
|
51
|
-
def response(self) -> tuple[dict[str, tuple[*Ts]]] | tuple[tuple[*Ts]] | tuple[TFlavour]:
|
51
|
+
def response(self, _tuple: bool, **kwargs) -> TResponse[TFlavour, *Ts]:
|
52
52
|
if not self.is_there_response:
|
53
53
|
return tuple([])
|
54
|
-
|
54
|
+
cleaned_response = self._response_values
|
55
|
+
|
55
56
|
if self._select is not None:
|
56
|
-
|
57
|
-
|
57
|
+
cleaned_response = self._parser_response()
|
58
|
+
|
59
|
+
cast_flavour = self._cast_to_flavour(cleaned_response, **kwargs)
|
60
|
+
if _tuple is not True:
|
61
|
+
return cast_flavour
|
58
62
|
|
59
|
-
|
60
|
-
|
63
|
+
return tuple(cast_flavour)
|
64
|
+
|
65
|
+
def _cast_to_flavour(self, data: list[tuple[*Ts]], **kwargs) -> list[dict[str, tuple[*Ts]]] | list[tuple[*Ts]] | list[TFlavour]:
|
66
|
+
def _dict(**kwargs) -> list[dict[str, tuple[*Ts]]]:
|
61
67
|
return [dict(zip(self._columns, x)) for x in data]
|
62
68
|
|
63
|
-
def _tuple() -> list[tuple[*Ts]]:
|
69
|
+
def _tuple(**kwargs) -> list[tuple[*Ts]]:
|
64
70
|
return data
|
65
71
|
|
66
|
-
def _set() -> list[set]:
|
72
|
+
def _set(**kwargs) -> list[set]:
|
67
73
|
for d in data:
|
68
74
|
n = len(d)
|
69
75
|
for i in range(n):
|
@@ -73,16 +79,20 @@ class Response[TFlavour, *Ts]:
|
|
73
79
|
raise TypeError(f"unhashable type '{type(d[i])}' found in '{type(d)}' when attempting to cast the result into a '{set.__name__}' object")
|
74
80
|
return [set(x) for x in data]
|
75
81
|
|
76
|
-
def
|
77
|
-
return [
|
82
|
+
def _list(**kwargs) -> list[list]:
|
83
|
+
return [list(x) for x in data]
|
84
|
+
|
85
|
+
def _default(**kwargs) -> list[TFlavour]:
|
86
|
+
return self._flavour(data, **kwargs)
|
78
87
|
|
79
88
|
selector: dict[Type[object], Any] = {
|
80
89
|
dict: _dict,
|
81
90
|
tuple: _tuple,
|
82
91
|
set: _set,
|
92
|
+
list: _list,
|
83
93
|
}
|
84
94
|
|
85
|
-
return selector.get(self._flavour, _default)()
|
95
|
+
return selector.get(self._flavour, _default)(**kwargs)
|
86
96
|
|
87
97
|
def _parser_response(self) -> TFlavour:
|
88
98
|
new_response: list[list] = []
|
@@ -123,8 +133,7 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
|
|
123
133
|
def wrapper(self: MySQLRepository, *args, **kwargs):
|
124
134
|
with self._pool.get_connection() as cnx:
|
125
135
|
try:
|
126
|
-
|
127
|
-
return foo
|
136
|
+
return func(self, cnx._cnx, *args, **kwargs)
|
128
137
|
except Exception as e:
|
129
138
|
cnx._cnx.rollback()
|
130
139
|
raise e
|
@@ -140,7 +149,13 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
|
|
140
149
|
|
141
150
|
@override
|
142
151
|
@get_connection
|
143
|
-
def read_sql[TFlavour](
|
152
|
+
def read_sql[TFlavour](
|
153
|
+
self,
|
154
|
+
cnx: MySQLConnection,
|
155
|
+
query: str,
|
156
|
+
flavour: Type[TFlavour] = tuple,
|
157
|
+
**kwargs,
|
158
|
+
) -> tuple[TFlavour]:
|
144
159
|
"""
|
145
160
|
Return tuple of tuples by default.
|
146
161
|
|
@@ -150,19 +165,15 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
|
|
150
165
|
- flavour: Type[TFlavour]: Useful to return tuple of any Iterable type as dict,set,list...
|
151
166
|
"""
|
152
167
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
return None
|
157
|
-
|
158
|
-
model: Table = get_and_drop_key("model")
|
159
|
-
select: Select = get_and_drop_key("select")
|
168
|
+
model: Table = kwargs.pop("model", None)
|
169
|
+
select: Select = kwargs.pop("select", None)
|
170
|
+
cast_to_tuple: bool = kwargs.pop("cast_to_tuple", True)
|
160
171
|
|
161
172
|
with cnx.cursor(buffered=True) as cursor:
|
162
173
|
cursor.execute(query)
|
163
174
|
values: list[tuple] = cursor.fetchall()
|
164
175
|
columns: tuple[str] = cursor.column_names
|
165
|
-
return Response[TFlavour](model=model, response_values=values, columns=columns, flavour=flavour, select=select, **kwargs)
|
176
|
+
return Response[TFlavour](model=model, response_values=values, columns=columns, flavour=flavour, select=select).response(_tuple=cast_to_tuple, **kwargs)
|
166
177
|
|
167
178
|
# FIXME [ ]: this method does not comply with the implemented interface
|
168
179
|
@get_connection
|