ormlambda 2.9.0__py3-none-any.whl → 2.9.4__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 +97 -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 +52 -37
- ormlambda/model_base.py +3 -3
- {ormlambda-2.9.0.dist-info → ormlambda-2.9.4.dist-info}/METADATA +1 -1
- {ormlambda-2.9.0.dist-info → ormlambda-2.9.4.dist-info}/RECORD +18 -15
- {ormlambda-2.9.0.dist-info → ormlambda-2.9.4.dist-info}/LICENSE +0 -0
- {ormlambda-2.9.0.dist-info → ormlambda-2.9.4.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]]
|
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,68 @@ 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, joins: JoinCondition[T, T1]) -> IStatements[T, T1]: ...
|
231
|
+
@overload
|
232
|
+
def join[T1](self, joins: TupleJoins1[T, T1]) -> IStatements[T, T1]: ...
|
233
|
+
@overload
|
234
|
+
def join[T1, T2](self, joins: TupleJoins2[T, T1, T2]) -> IStatements[T, T1, T2]: ...
|
235
|
+
@overload
|
236
|
+
def join[T1, T2, T3](self, joins: TupleJoins3[T, T1, T2, T3]) -> IStatements[T, T1, T2, T3]: ...
|
237
|
+
@overload
|
238
|
+
def join[T1, T2, T3, T4](self, joins: TupleJoins4[T, T1, T2, T3, T4]) -> IStatements[T, T1, T2, T3, T4]: ...
|
239
|
+
@overload
|
240
|
+
def join[T1, T2, T3, T4, T5](self, joins: TupleJoins5[T, T1, T2, T3, T4, T5]) -> IStatements[T, T1, T2, T3, T4, T5]: ...
|
241
|
+
@overload
|
242
|
+
def join[T1, T2, T3, T4, T5, T6](self, joins: TupleJoins6[T, T1, T2, T3, T4, T5, T6]) -> IStatements[T, T1, T2, T3, T4, T5, T6]: ...
|
243
|
+
|
244
|
+
@abstractmethod
|
245
|
+
def join[*FKTables](self, joins) -> IStatements[T, *FKTables]: ...
|
246
|
+
|
203
247
|
# endregion
|
204
248
|
# region select
|
205
249
|
@overload
|
206
|
-
def select(self) ->
|
250
|
+
def select(self) -> SelectRes1[T]: ...
|
207
251
|
@overload
|
208
|
-
def select[T1](self, selector: Callable[[T], T1], *, by: Optional[Enum] = ...) ->
|
252
|
+
def select[T1](self, selector: Callable[[T], T1], *, by: Optional[Enum] = ...) -> SelectRes1[T1]: ...
|
209
253
|
@overload
|
210
|
-
def select[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ...) ->
|
254
|
+
def select[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ...) -> SelectRes1[T1]: ...
|
211
255
|
@overload
|
212
|
-
def select[T1, T2](self, selector: Callable[[T], tuple[T1, T2]], *, by: Optional[Enum] = ...) ->
|
256
|
+
def select[T1, T2](self, selector: Callable[[T, *Ts], tuple[T1, T2]], *, by: Optional[Enum] = ...) -> SelectRes2[T1, T2]: ...
|
213
257
|
@overload
|
214
|
-
def select[T1, T2, T3](self, selector: Callable[[T], tuple[T1, T2, T3]], *, by: Optional[Enum] = ...) ->
|
258
|
+
def select[T1, T2, T3](self, selector: Callable[[T, *Ts], tuple[T1, T2, T3]], *, by: Optional[Enum] = ...) -> SelectRes3[T1, T2, T3]: ...
|
215
259
|
@overload
|
216
|
-
def select[T1, T2, T3, T4](self, selector: Callable[[T], tuple[T1, T2, T3, T4]], *, by: Optional[Enum] = ...) ->
|
260
|
+
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
261
|
@overload
|
218
|
-
def select[T1, T2, T3, T4, T5](self, selector: Callable[[T], tuple[T1, T2, T3, T4, T5]], *, by: Optional[Enum] = ...) ->
|
262
|
+
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
263
|
@overload
|
220
|
-
def select[T1, T2, T3, T4, T5, T6](self, selector: Callable[[T], tuple[T1, T2, T3, T4, T5, T6]], *, by: Optional[Enum] = ...) ->
|
264
|
+
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
265
|
@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] = ...) ->
|
266
|
+
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
267
|
@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] = ...) ->
|
268
|
+
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
269
|
@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] = ...) ->
|
270
|
+
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
271
|
@overload
|
228
|
-
def select[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10](
|
229
|
-
|
230
|
-
) -> tuple[tuple[T1, ...], tuple[T2, ...], tuple[T3, ...], tuple[T4, ...], tuple[T5, ...], tuple[T6, ...], tuple[T7, ...], tuple[T8, ...], tuple[T9, ...], tuple[T10, ...]]: ...
|
272
|
+
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]: ...
|
273
|
+
|
231
274
|
@overload
|
232
|
-
def select[
|
275
|
+
def select[TFlavour](self, selector: Optional[Callable[[T, *Ts], tuple]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> TFlavour: ...
|
233
276
|
@overload
|
234
|
-
def select[
|
277
|
+
def select[TRes](self, selector: Optional[Callable[[T, *Ts], TRes]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[TRes, ...]: ...
|
235
278
|
@overload
|
236
|
-
def select[
|
279
|
+
def select[TRes](self, selector: Optional[Callable[[T, *Ts], tuple[TRes]]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[TRes, ...]: ...
|
237
280
|
@overload
|
238
|
-
def select[
|
281
|
+
def select[*TRes](self, selector: Optional[Callable[[T, *Ts], tuple[*TRes]]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[tuple[*TRes]]: ...
|
282
|
+
@overload
|
283
|
+
def select[TFlavour](self, selector: Optional[Callable[[T, *Ts], tuple]] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> tuple[TFlavour]: ...
|
284
|
+
|
239
285
|
@abstractmethod
|
240
|
-
def select[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = ..., *, flavour: Type[TFlavour] = ..., by: JoinType =
|
286
|
+
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
287
|
|
242
288
|
# endregion
|
243
289
|
# region select_one
|
@@ -246,24 +292,24 @@ class IStatements[T: Table](ABC):
|
|
246
292
|
@overload
|
247
293
|
def select_one[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> TFlavour: ...
|
248
294
|
@overload
|
249
|
-
def select_one[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
|
295
|
+
def select_one[T1](self, selector: Callable[[T, *Ts], tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
|
250
296
|
@overload
|
251
|
-
def select_one[*
|
297
|
+
def select_one[*TRes](self, selector: Callable[[T, *Ts], tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
|
252
298
|
@overload
|
253
|
-
def select_one[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type) -> T1: ...
|
299
|
+
def select_one[T1](self, selector: Callable[[T, *Ts], tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type) -> T1: ...
|
254
300
|
@overload
|
255
|
-
def select_one[T1, TFlavour](self, selector: Callable[[T], T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> T1: ...
|
301
|
+
def select_one[T1, TFlavour](self, selector: Callable[[T, *Ts], T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> T1: ...
|
256
302
|
@overload
|
257
|
-
def select_one[*Ts](self, selector: Callable[[T], tuple[*Ts]], *, by: Optional[Enum] = ..., flavour: Type[tuple]) -> tuple[*Ts]: ...
|
303
|
+
def select_one[*Ts](self, selector: Callable[[T, *Ts], tuple[*Ts]], *, by: Optional[Enum] = ..., flavour: Type[tuple]) -> tuple[*Ts]: ...
|
258
304
|
@overload
|
259
|
-
def select_one[TFlavour](self, selector: Callable[[T], tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> TFlavour: ...
|
305
|
+
def select_one[TFlavour](self, selector: Callable[[T, *Ts], tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> TFlavour: ...
|
260
306
|
@abstractmethod
|
261
|
-
def select_one[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Type[TFlavour] = ..., by: Optional[Enum] = ...): ...
|
307
|
+
def select_one[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T, *Ts], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Type[TFlavour] = ..., by: Optional[Enum] = ...): ...
|
262
308
|
|
263
309
|
# endregion
|
264
310
|
# region group_by
|
265
311
|
@abstractmethod
|
266
|
-
def group_by[TRepo
|
312
|
+
def group_by[TRepo](self, column: Callable[[T, *Ts], TRepo]) -> IStatements[T, *Ts]: ...
|
267
313
|
|
268
314
|
# endregion
|
269
315
|
|
@@ -271,7 +317,16 @@ class IStatements[T: Table](ABC):
|
|
271
317
|
def _build(self) -> str: ...
|
272
318
|
|
273
319
|
|
274
|
-
class IStatements_two_generic[T: Table, TRepo](IStatements[T]):
|
320
|
+
class IStatements_two_generic[T: Table, *Ts, TRepo](IStatements[T, *Ts]):
|
275
321
|
@property
|
276
322
|
@abstractmethod
|
277
323
|
def repository(self) -> IRepositoryBase[TRepo]: ...
|
324
|
+
|
325
|
+
@property
|
326
|
+
def query(self) -> str: ...
|
327
|
+
|
328
|
+
@property
|
329
|
+
def model(self) -> Type[T]: ...
|
330
|
+
|
331
|
+
@property
|
332
|
+
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
|