ormlambda 2.6.0__py3-none-any.whl → 2.7.2__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 +1 -1
- ormlambda/common/abstract_classes/decomposition_query.py +0 -1
- ormlambda/common/interfaces/IDecompositionQuery.py +1 -1
- ormlambda/common/interfaces/IRepositoryBase.py +1 -14
- ormlambda/common/interfaces/IStatements.py +19 -25
- ormlambda/databases/my_sql/clauses/order.py +6 -2
- ormlambda/databases/my_sql/clauses/select.py +10 -1
- ormlambda/databases/my_sql/functions/__init__.py +1 -0
- ormlambda/databases/my_sql/functions/min.py +39 -0
- ormlambda/databases/my_sql/repository.py +27 -49
- ormlambda/databases/my_sql/statements.py +68 -23
- {ormlambda-2.6.0.dist-info → ormlambda-2.7.2.dist-info}/METADATA +1 -1
- {ormlambda-2.6.0.dist-info → ormlambda-2.7.2.dist-info}/RECORD +15 -14
- {ormlambda-2.6.0.dist-info → ormlambda-2.7.2.dist-info}/LICENSE +0 -0
- {ormlambda-2.6.0.dist-info → ormlambda-2.7.2.dist-info}/WHEEL +0 -0
@@ -76,7 +76,7 @@ class ClusterQuery[T]:
|
|
76
76
|
for clause in clauses:
|
77
77
|
if not hasattr(table, clause.column):
|
78
78
|
agg_methods = self.get_all_aggregate_method(clauses)
|
79
|
-
raise ValueError(f"You cannot use aggregation method like '{agg_methods}' to return model objects")
|
79
|
+
raise ValueError(f"You cannot use aggregation method like '{agg_methods}' to return model objects. Try specifying 'flavour' attribute as 'dict'.")
|
80
80
|
valid_attr[clause.column] = dicc_cols[clause.alias]
|
81
81
|
|
82
82
|
# COMMENT: At this point we are going to instantiate Table class with specific attributes getting directly from database
|
@@ -28,7 +28,6 @@ class ClauseInfo[T: tp.Type[Table]]:
|
|
28
28
|
self._alias: tp.Optional[str] = self._alias_children_resolver(self)
|
29
29
|
|
30
30
|
self._query: str = self.__create_value_string(self._column)
|
31
|
-
pass
|
32
31
|
|
33
32
|
def __repr__(self) -> str:
|
34
33
|
return f"{ClauseInfo.__name__}: {self.query}"
|
@@ -31,7 +31,7 @@ class IDecompositionQuery[T: tp.Type[Table]](IQuery):
|
|
31
31
|
|
32
32
|
@property
|
33
33
|
@abc.abstractmethod
|
34
|
-
def fk_relationship(self) ->
|
34
|
+
def fk_relationship(self) -> set[tuple[tp.Type[Table], tp.Type[Table]]]: ...
|
35
35
|
|
36
36
|
@property
|
37
37
|
@abc.abstractmethod
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from abc import ABC, abstractmethod
|
2
|
-
from typing import
|
2
|
+
from typing import Literal, Optional, Type
|
3
3
|
|
4
4
|
TypeExists = Literal["fail", "replace", "append"]
|
5
5
|
|
@@ -8,15 +8,6 @@ class IRepositoryBase[T](ABC):
|
|
8
8
|
def __repr__(self) -> str:
|
9
9
|
return f"{IRepositoryBase.__name__}: {self.__class__.__name__}"
|
10
10
|
|
11
|
-
@abstractmethod
|
12
|
-
def is_connected(self) -> bool: ...
|
13
|
-
|
14
|
-
@abstractmethod
|
15
|
-
def connect(self, **kwargs: Any) -> None: ...
|
16
|
-
|
17
|
-
@abstractmethod
|
18
|
-
def close_connection(self) -> None: ...
|
19
|
-
|
20
11
|
@abstractmethod
|
21
12
|
def read_sql[TFlavour](self, query: str, flavour: Optional[Type[TFlavour]], **kwargs) -> tuple[TFlavour]: ...
|
22
13
|
|
@@ -44,10 +35,6 @@ class IRepositoryBase[T](ABC):
|
|
44
35
|
@abstractmethod
|
45
36
|
def database_exists(self, name: str) -> bool: ...
|
46
37
|
|
47
|
-
@property
|
48
|
-
@abstractmethod
|
49
|
-
def connection(self) -> T: ...
|
50
|
-
|
51
38
|
@property
|
52
39
|
@abstractmethod
|
53
40
|
def database(self) -> Optional[str]: ...
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import Any, Callable, Iterable, Optional, Literal, Type, overload, TYPE_CHECKING
|
2
|
+
from typing import Any, Callable, Iterable, Optional, Literal, Type, Union, overload, TYPE_CHECKING, TypeVar
|
3
3
|
from enum import Enum
|
4
4
|
from abc import abstractmethod, ABC
|
5
5
|
|
@@ -8,12 +8,16 @@ from ormlambda.common.enums import JoinType
|
|
8
8
|
|
9
9
|
if TYPE_CHECKING:
|
10
10
|
from ormlambda import Table
|
11
|
+
from .IAggregate import IAggregate
|
11
12
|
|
12
13
|
OrderType = Literal["ASC", "DESC"]
|
13
14
|
|
14
15
|
# TODOH: This var is duplicated from 'src\ormlambda\databases\my_sql\clauses\create_database.py'
|
15
16
|
TypeExists = Literal["fail", "replace", "append"]
|
16
17
|
|
18
|
+
T = TypeVar("T")
|
19
|
+
WhereTypes = Union[Callable[[T], bool], Iterable[Callable[[T], bool]]]
|
20
|
+
|
17
21
|
|
18
22
|
class IStatements[T: Table](ABC):
|
19
23
|
@abstractmethod
|
@@ -45,7 +49,6 @@ class IStatements[T: Table](ABC):
|
|
45
49
|
def insert(self, values: T | list[T]) -> None: ...
|
46
50
|
|
47
51
|
# endregion
|
48
|
-
|
49
52
|
# region upsert
|
50
53
|
@overload
|
51
54
|
def upsert(self, values: T) -> None:
|
@@ -76,25 +79,21 @@ class IStatements[T: Table](ABC):
|
|
76
79
|
def update(self, dicc: dict[str | property, Any]) -> None: ...
|
77
80
|
|
78
81
|
# endregion
|
79
|
-
|
80
82
|
# region limit
|
81
83
|
@abstractmethod
|
82
84
|
def limit(self, number: int) -> IStatements[T]: ...
|
83
85
|
|
84
86
|
# endregion
|
85
|
-
|
86
87
|
# region offset
|
87
88
|
@abstractmethod
|
88
89
|
def offset(self, number: int) -> IStatements[T]: ...
|
89
90
|
|
90
91
|
# endregion
|
91
|
-
|
92
92
|
# region count
|
93
93
|
@abstractmethod
|
94
94
|
def count(self, selection: Callable[[T], property]) -> int: ...
|
95
95
|
|
96
96
|
# endregion
|
97
|
-
|
98
97
|
# region delete
|
99
98
|
@overload
|
100
99
|
def delete(self) -> None: ...
|
@@ -108,28 +107,15 @@ class IStatements[T: Table](ABC):
|
|
108
107
|
def delete(self, instance: Optional[T | list[T]] = None) -> None: ...
|
109
108
|
|
110
109
|
# endregion
|
111
|
-
|
112
110
|
# region join
|
113
111
|
@abstractmethod
|
114
112
|
def join(self, table_left: Table, table_right: Table, *, by: str) -> IStatements[T]: ...
|
115
113
|
|
116
114
|
# endregion
|
117
|
-
|
118
115
|
# region where
|
119
|
-
@overload
|
120
|
-
def where(self, lambda_: Callable[[T], bool]) -> IStatements[T]:
|
121
|
-
"""
|
122
|
-
This method creates where clause by passing the lambda's condition
|
123
|
-
|
124
|
-
EXAMPLE
|
125
|
-
-
|
126
|
-
mb = BaseModel()
|
127
|
-
mb.where(lambda a: 10 <= a.city_id <= 100)
|
128
|
-
"""
|
129
|
-
...
|
130
116
|
|
131
117
|
@overload
|
132
|
-
def where(self,
|
118
|
+
def where(self, conditions: Iterable[Callable[[T], bool]]) -> IStatements[T]:
|
133
119
|
"""
|
134
120
|
This method creates where clause by passing the Iterable in lambda function
|
135
121
|
EXAMPLE
|
@@ -140,7 +126,7 @@ class IStatements[T: Table](ABC):
|
|
140
126
|
...
|
141
127
|
|
142
128
|
@overload
|
143
|
-
def where(self,
|
129
|
+
def where(self, conditions: Callable[[T], bool], **kwargs) -> IStatements[T]:
|
144
130
|
"""
|
145
131
|
PARAM
|
146
132
|
-
|
@@ -157,10 +143,9 @@ class IStatements[T: Table](ABC):
|
|
157
143
|
...
|
158
144
|
|
159
145
|
@abstractmethod
|
160
|
-
def where(self,
|
146
|
+
def where(self, conditions: WhereTypes = lambda: None, **kwargs) -> IStatements[T]: ...
|
161
147
|
|
162
148
|
# endregion
|
163
|
-
|
164
149
|
# region order
|
165
150
|
@overload
|
166
151
|
def order[TValue](self, _lambda_col: Callable[[T], TValue]) -> IStatements[T]: ...
|
@@ -170,7 +155,18 @@ class IStatements[T: Table](ABC):
|
|
170
155
|
def order[TValue](self, _lambda_col: Callable[[T], TValue], order_type: OrderType) -> IStatements[T]: ...
|
171
156
|
|
172
157
|
# endregion
|
158
|
+
# region concat
|
159
|
+
@overload
|
160
|
+
def concat[*Ts](self, selector: Callable[[T], tuple[*Ts]]) -> IAggregate[T]: ...
|
161
|
+
|
162
|
+
# endregion
|
163
|
+
# region max
|
164
|
+
@overload
|
165
|
+
def max[TProp](self, column: Callable[[T], TProp], alias: bool = True, alias_name: str = "max") -> TProp: ...
|
166
|
+
@overload
|
167
|
+
def min[TProp](self, column: Callable[[T], TProp], alias: bool = True, alias_name: str = "min") -> TProp: ...
|
173
168
|
|
169
|
+
# endregion
|
174
170
|
# region select
|
175
171
|
@overload
|
176
172
|
def select(self) -> tuple[T, ...]: ...
|
@@ -212,7 +208,6 @@ class IStatements[T: Table](ABC):
|
|
212
208
|
def select[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Type[TFlavour] = None, by: JoinType = JoinType.INNER_JOIN): ...
|
213
209
|
|
214
210
|
# endregion
|
215
|
-
|
216
211
|
# region select_one
|
217
212
|
@overload
|
218
213
|
def select_one(self) -> T: ...
|
@@ -234,7 +229,6 @@ class IStatements[T: Table](ABC):
|
|
234
229
|
def select_one[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Type[TFlavour] = None, by: Optional[Enum] = JoinType.INNER_JOIN): ...
|
235
230
|
|
236
231
|
# endregion
|
237
|
-
|
238
232
|
# region group_by
|
239
233
|
@abstractmethod
|
240
234
|
def group_by[TRepo, *Ts](self, column: Callable[[T], TRepo], select_query: Callable[[T], tuple[*Ts]]) -> tuple[tuple[*Ts]]: ...
|
@@ -1,4 +1,8 @@
|
|
1
|
-
from
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import override, Callable, TYPE_CHECKING
|
3
|
+
|
4
|
+
if TYPE_CHECKING:
|
5
|
+
from ormlambda import Table
|
2
6
|
|
3
7
|
from ormlambda.utils.lambda_disassembler.tree_instruction import TreeInstruction
|
4
8
|
from ormlambda.common.interfaces.IQueryCommand import IQuery
|
@@ -8,7 +12,7 @@ ASC = "ASC"
|
|
8
12
|
DESC = "DESC"
|
9
13
|
|
10
14
|
|
11
|
-
class OrderQuery[T](IQuery):
|
15
|
+
class OrderQuery[T:Table](IQuery):
|
12
16
|
ORDER = "ORDER BY"
|
13
17
|
|
14
18
|
def __init__(self, instance: T, order_lambda: Callable[[T], None], order_type: OrderType) -> None:
|
@@ -2,6 +2,7 @@ from typing import override, Type, Callable, TYPE_CHECKING
|
|
2
2
|
|
3
3
|
from ormlambda.common.abstract_classes.decomposition_query import DecompositionQueryBase
|
4
4
|
from ormlambda.common.enums.join_type import JoinType
|
5
|
+
from ormlambda.common.interfaces.IAggregate import IAggregate
|
5
6
|
|
6
7
|
if TYPE_CHECKING:
|
7
8
|
from ormlambda import Table
|
@@ -34,7 +35,15 @@ class Select[T: Type[Table]](DecompositionQueryBase[T]):
|
|
34
35
|
@override
|
35
36
|
@property
|
36
37
|
def query(self) -> str:
|
37
|
-
|
38
|
+
cols:list[str] = []
|
39
|
+
for x in self.all_clauses:
|
40
|
+
cols.append(x.query)
|
41
|
+
|
42
|
+
if isinstance(x._row_column,IAggregate) and x._row_column.has_foreign_keys:
|
43
|
+
self._fk_relationship.update(x._row_column.fk_relationship)
|
44
|
+
|
45
|
+
|
46
|
+
col: str = ", ".join(cols)
|
38
47
|
query: str = f"{self.CLAUSE} {col} FROM {self._table.__table_name__}"
|
39
48
|
alias = ""
|
40
49
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
from ormlambda.common.interfaces import IAggregate
|
2
|
+
import typing as tp
|
3
|
+
|
4
|
+
from ormlambda.common.abstract_classes.decomposition_query import DecompositionQueryBase, ClauseInfo
|
5
|
+
|
6
|
+
if tp.TYPE_CHECKING:
|
7
|
+
from ormlambda import Table
|
8
|
+
|
9
|
+
|
10
|
+
class Min[T: tp.Type[Table]](DecompositionQueryBase[T], IAggregate[T]):
|
11
|
+
NAME: str = "MIN"
|
12
|
+
|
13
|
+
@tp.overload
|
14
|
+
def __init__[T: tp.Type[Table]](self, table: T, column: tp.Callable[[T], tp.Any], *, alias: bool = True, alias_name: str = "min") -> None: ...
|
15
|
+
|
16
|
+
def __init__(
|
17
|
+
self,
|
18
|
+
table: T,
|
19
|
+
column: str | tp.Callable[[T], tuple],
|
20
|
+
*,
|
21
|
+
alias: bool = True,
|
22
|
+
alias_name: str = "min",
|
23
|
+
) -> None:
|
24
|
+
super().__init__(
|
25
|
+
table,
|
26
|
+
lambda_query=column,
|
27
|
+
alias=alias,
|
28
|
+
alias_name=alias_name,
|
29
|
+
)
|
30
|
+
|
31
|
+
def alias_children_resolver[Tclause: tp.Type[Table]](self, clause_info: ClauseInfo[Tclause]):
|
32
|
+
if isinstance(clause_info._row_column, IAggregate):
|
33
|
+
return clause_info._row_column.alias
|
34
|
+
return None
|
35
|
+
|
36
|
+
@property
|
37
|
+
def query(self) -> str:
|
38
|
+
col = ", ".join([x.query for x in self.all_clauses])
|
39
|
+
return f"{self.NAME}({col})"
|
@@ -77,45 +77,27 @@ class Response[TFlavour, *Ts]:
|
|
77
77
|
class MySQLRepository(IRepositoryBase[MySQLConnection]):
|
78
78
|
def get_connection(func: Callable[..., Any]):
|
79
79
|
@functools.wraps(func)
|
80
|
-
def wrapper(self:
|
81
|
-
self.
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
self.close_connection()
|
89
|
-
return foo
|
80
|
+
def wrapper(self: MySQLRepository, *args, **kwargs):
|
81
|
+
with self._pool.get_connection() as cnx:
|
82
|
+
try:
|
83
|
+
foo = func(self, cnx._cnx, *args, **kwargs)
|
84
|
+
return foo
|
85
|
+
except Exception as e:
|
86
|
+
cnx._cnx.rollback()
|
87
|
+
raise e
|
90
88
|
|
91
89
|
return wrapper
|
92
90
|
|
93
91
|
def __init__(self, **kwargs: Any) -> None:
|
94
92
|
self._data_config: dict[str, Any] = kwargs
|
95
93
|
self._pool: MySQLConnectionPool = self.__create_MySQLConnectionPool()
|
96
|
-
self._connection: PooledMySQLConnection = None
|
97
94
|
|
98
95
|
def __create_MySQLConnectionPool(self):
|
99
96
|
return MySQLConnectionPool(pool_name="mypool", pool_size=10, **self._data_config)
|
100
97
|
|
101
|
-
@override
|
102
|
-
def is_connected(self) -> bool:
|
103
|
-
return self._connection._cnx is not None if self._connection else False
|
104
|
-
|
105
|
-
@override
|
106
|
-
def connect(self) -> None:
|
107
|
-
self._connection = self._pool.get_connection()
|
108
|
-
return None
|
109
|
-
|
110
|
-
@override
|
111
|
-
def close_connection(self) -> None:
|
112
|
-
if self.is_connected():
|
113
|
-
self._connection.close()
|
114
|
-
return None
|
115
|
-
|
116
98
|
@override
|
117
99
|
@get_connection
|
118
|
-
def read_sql[TFlavour](self, query: str, flavour: Type[TFlavour] = tuple, **kwargs) -> tuple[TFlavour]:
|
100
|
+
def read_sql[TFlavour](self, cnx: MySQLConnection, query: str, flavour: Type[TFlavour] = tuple, **kwargs) -> tuple[TFlavour]:
|
119
101
|
"""
|
120
102
|
Return tuple of tuples by default.
|
121
103
|
|
@@ -125,7 +107,7 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
|
|
125
107
|
- flavour: Type[TFlavour]: Useful to return tuple of any Iterable type as dict,set,list...
|
126
108
|
"""
|
127
109
|
|
128
|
-
with
|
110
|
+
with cnx.cursor(buffered=True) as cursor:
|
129
111
|
cursor.execute(query)
|
130
112
|
values: list[tuple] = cursor.fetchall()
|
131
113
|
columns: tuple[str] = cursor.column_names
|
@@ -133,7 +115,7 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
|
|
133
115
|
|
134
116
|
# FIXME [ ]: this method does not comply with the implemented interface
|
135
117
|
@get_connection
|
136
|
-
def create_tables_code_first(self, path: str | Path) -> None:
|
118
|
+
def create_tables_code_first(self, cnx: MySQLConnection, path: str | Path) -> None:
|
137
119
|
if not isinstance(path, Path | str):
|
138
120
|
raise ValueError
|
139
121
|
|
@@ -148,33 +130,33 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
|
|
148
130
|
queries_list: list[str] = module_tree.get_queries()
|
149
131
|
|
150
132
|
for query in queries_list:
|
151
|
-
with
|
133
|
+
with cnx.cursor(buffered=True) as cursor:
|
152
134
|
cursor.execute(query)
|
153
|
-
|
135
|
+
cnx.commit()
|
154
136
|
return None
|
155
137
|
|
156
138
|
@override
|
157
139
|
@get_connection
|
158
|
-
def executemany_with_values(self, query: str, values) -> None:
|
159
|
-
with
|
140
|
+
def executemany_with_values(self, cnx: MySQLConnection, query: str, values) -> None:
|
141
|
+
with cnx.cursor(buffered=True) as cursor:
|
160
142
|
cursor.executemany(query, values)
|
161
|
-
|
143
|
+
cnx.commit()
|
162
144
|
return None
|
163
145
|
|
164
146
|
@override
|
165
147
|
@get_connection
|
166
|
-
def execute_with_values(self, query: str, values) -> None:
|
167
|
-
with
|
148
|
+
def execute_with_values(self, cnx: MySQLConnection, query: str, values) -> None:
|
149
|
+
with cnx.cursor(buffered=True) as cursor:
|
168
150
|
cursor.execute(query, values)
|
169
|
-
|
151
|
+
cnx.commit()
|
170
152
|
return None
|
171
153
|
|
172
154
|
@override
|
173
155
|
@get_connection
|
174
|
-
def execute(self, query: str) -> None:
|
175
|
-
with
|
156
|
+
def execute(self, cnx: MySQLConnection, query: str) -> None:
|
157
|
+
with cnx.cursor(buffered=True) as cursor:
|
176
158
|
cursor.execute(query)
|
177
|
-
|
159
|
+
cnx.commit()
|
178
160
|
return None
|
179
161
|
|
180
162
|
@override
|
@@ -183,9 +165,9 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
|
|
183
165
|
|
184
166
|
@override
|
185
167
|
@get_connection
|
186
|
-
def database_exists(self, name: str) -> bool:
|
168
|
+
def database_exists(self, cnx: MySQLConnection, name: str) -> bool:
|
187
169
|
query = "SHOW DATABASES LIKE %s;"
|
188
|
-
with
|
170
|
+
with cnx.cursor(buffered=True) as cursor:
|
189
171
|
cursor.execute(query, (name,))
|
190
172
|
res = cursor.fetchmany(1)
|
191
173
|
return len(res) > 0
|
@@ -196,12 +178,12 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
|
|
196
178
|
|
197
179
|
@override
|
198
180
|
@get_connection
|
199
|
-
def table_exists(self, name: str) -> bool:
|
200
|
-
if not
|
181
|
+
def table_exists(self, cnx: MySQLConnection, name: str) -> bool:
|
182
|
+
if not cnx.database:
|
201
183
|
raise Exception("No database selected")
|
202
184
|
|
203
185
|
query = "SHOW TABLES LIKE %s;"
|
204
|
-
with
|
186
|
+
with cnx.cursor(buffered=True) as cursor:
|
205
187
|
cursor.execute(query, (name,))
|
206
188
|
res = cursor.fetchmany(1)
|
207
189
|
return len(res) > 0
|
@@ -210,10 +192,6 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
|
|
210
192
|
def create_database(self, name: str, if_exists: TypeExists = "fail") -> None:
|
211
193
|
return CreateDatabase(self).execute(name, if_exists)
|
212
194
|
|
213
|
-
@override
|
214
|
-
@property
|
215
|
-
def connection(self) -> MySQLConnection:
|
216
|
-
return self._connection
|
217
195
|
|
218
196
|
@property
|
219
197
|
def database(self) -> Optional[str]:
|
@@ -1,5 +1,8 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import override, Type, TYPE_CHECKING, Any, Callable, Optional
|
2
|
+
from typing import Iterable, override, Type, TYPE_CHECKING, Any, Callable, Optional
|
3
|
+
import inspect
|
4
|
+
from mysql.connector import MySQLConnection, errors, errorcode
|
5
|
+
|
3
6
|
|
4
7
|
if TYPE_CHECKING:
|
5
8
|
from ormlambda import Table
|
@@ -7,6 +10,8 @@ if TYPE_CHECKING:
|
|
7
10
|
from ormlambda.common.interfaces.IStatements import OrderType
|
8
11
|
from ormlambda.common.interfaces import IQuery, IRepositoryBase, IStatements_two_generic
|
9
12
|
from ormlambda.common.interfaces.IRepositoryBase import TypeExists
|
13
|
+
from ormlambda.common.interfaces import IAggregate
|
14
|
+
from ormlambda.common.interfaces.IStatements import WhereTypes
|
10
15
|
|
11
16
|
from ormlambda import AbstractSQLStatements
|
12
17
|
from .clauses import DeleteQuery
|
@@ -23,13 +28,10 @@ from .clauses import WhereCondition
|
|
23
28
|
from .clauses import Count
|
24
29
|
from .clauses import GroupBy
|
25
30
|
|
26
|
-
from mysql.connector import MySQLConnection, errors, errorcode
|
27
|
-
|
28
|
-
|
29
|
-
import inspect
|
30
31
|
|
31
32
|
from ormlambda.utils import ForeignKey, Table
|
32
33
|
from ormlambda.common.enums import JoinType
|
34
|
+
from . import functions as func
|
33
35
|
|
34
36
|
|
35
37
|
class MySQLStatements[T: Table](AbstractSQLStatements[T, MySQLConnection]):
|
@@ -140,9 +142,15 @@ class MySQLStatements[T: Table](AbstractSQLStatements[T, MySQLConnection]):
|
|
140
142
|
return self
|
141
143
|
|
142
144
|
@override
|
143
|
-
def where(self,
|
145
|
+
def where(self, conditions: WhereTypes = lambda: None, **kwargs) -> IStatements_two_generic[T, MySQLConnection]:
|
144
146
|
# FIXME [x]: I've wrapped self._model into tuple to pass it instance attr. Idk if it's correct
|
145
|
-
|
147
|
+
|
148
|
+
if isinstance(conditions, Iterable):
|
149
|
+
for x in conditions:
|
150
|
+
self._query_list["where"].append(WhereCondition[T](function=x, instances=(self._model,), **kwargs))
|
151
|
+
return self
|
152
|
+
|
153
|
+
where_query = WhereCondition[T](function=conditions, instances=(self._model,), **kwargs)
|
146
154
|
self._query_list["where"].append(where_query)
|
147
155
|
return self
|
148
156
|
|
@@ -152,6 +160,18 @@ class MySQLStatements[T: Table](AbstractSQLStatements[T, MySQLConnection]):
|
|
152
160
|
self._query_list["order"].append(order)
|
153
161
|
return self
|
154
162
|
|
163
|
+
@override
|
164
|
+
def concat[*Ts](self, selector: Callable[[T], tuple[*Ts]], alias: bool = True, alias_name: str = "CONCAT") -> IAggregate[T]:
|
165
|
+
return func.Concat[T](self._model, selector, alias=alias, alias_name=alias_name)
|
166
|
+
|
167
|
+
@override
|
168
|
+
def max[TProp](self, column: Callable[[T], TProp], alias: bool = True, alias_name: str = "max") -> TProp:
|
169
|
+
return func.Max[T](self._model, column=column, alias=alias, alias_name=alias_name)
|
170
|
+
|
171
|
+
@override
|
172
|
+
def min[TProp](self, column: Callable[[T], TProp], alias: bool = True, alias_name: str = "min") -> TProp:
|
173
|
+
return func.Min[T](self._model, column=column, alias=alias, alias_name=alias_name)
|
174
|
+
|
155
175
|
@override
|
156
176
|
def select[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Optional[Type[TFlavour]] = None, by: JoinType = JoinType.INNER_JOIN):
|
157
177
|
if len(inspect.signature(selector).parameters) == 0:
|
@@ -198,24 +218,33 @@ class MySQLStatements[T: Table](AbstractSQLStatements[T, MySQLConnection]):
|
|
198
218
|
def _build(self) -> str:
|
199
219
|
query: str = ""
|
200
220
|
|
201
|
-
# self.__create_necessary_inner_join()
|
202
221
|
for x in self.__order__:
|
203
222
|
sub_query: Optional[list[IQuery]] = self._query_list.get(x, None)
|
204
|
-
if sub_query is
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
223
|
+
if sub_query is None:
|
224
|
+
continue
|
225
|
+
|
226
|
+
if isinstance(sub_query[0], WhereCondition):
|
227
|
+
query_ = self.__build_where_clause(sub_query)
|
228
|
+
|
229
|
+
# we must check if any join already exists on query string
|
230
|
+
elif isinstance(sub_query[0], JoinSelector):
|
231
|
+
select_query: str = self._query_list["select"][0].query
|
232
|
+
query_ = ""
|
233
|
+
for join in sub_query:
|
234
|
+
if join.query not in select_query:
|
235
|
+
query_ += f"\n{join.query}"
|
236
|
+
|
237
|
+
elif isinstance((select := sub_query[0]), Select):
|
238
|
+
query_: str = ""
|
239
|
+
where_joins = self.__create_necessary_inner_join()
|
240
|
+
if where_joins:
|
241
|
+
select._fk_relationship.update(where_joins)
|
242
|
+
query_ = select.query
|
243
|
+
|
244
|
+
else:
|
245
|
+
query_ = "\n".join([x.query for x in sub_query])
|
246
|
+
|
247
|
+
query += f"\n{query_}" if query != "" else query_
|
219
248
|
self._query_list.clear()
|
220
249
|
return query
|
221
250
|
|
@@ -227,3 +256,19 @@ class MySQLStatements[T: Table](AbstractSQLStatements[T, MySQLConnection]):
|
|
227
256
|
and_, clause = q.split(" ", maxsplit=1)
|
228
257
|
query += f" {and_} ({clause})"
|
229
258
|
return query
|
259
|
+
|
260
|
+
def __create_necessary_inner_join(self) -> Optional[set[tuple[Type[Table], Type[Table]]]]:
|
261
|
+
# When we applied filters in any table that we wont select any column, we need to add manually all neccessary joins to achieve positive result.
|
262
|
+
if "where" not in self._query_list:
|
263
|
+
return None
|
264
|
+
|
265
|
+
res = []
|
266
|
+
for where in self._query_list["where"]:
|
267
|
+
where: AbstractWhere
|
268
|
+
|
269
|
+
tables = where.get_involved_tables()
|
270
|
+
|
271
|
+
if tables:
|
272
|
+
[res.append(x) for x in tables]
|
273
|
+
|
274
|
+
return set(res)
|
@@ -1,19 +1,19 @@
|
|
1
1
|
ormlambda/__init__.py,sha256=L-Enc4FPmW3ldBMnWOU3gLn9i4FEc7sQzLWy2mIEti8,538
|
2
2
|
ormlambda/common/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
3
3
|
ormlambda/common/abstract_classes/__init__.py,sha256=tk2J4Mn_nD-1ZjtMVBE5FH7KR_8ppN_1Kx1s6c38GjM,167
|
4
|
-
ormlambda/common/abstract_classes/abstract_model.py,sha256=
|
5
|
-
ormlambda/common/abstract_classes/decomposition_query.py,sha256=
|
4
|
+
ormlambda/common/abstract_classes/abstract_model.py,sha256=0mGoW-MF0bX1VrAp2AJZ8Nk_BV7N_2NtzZoUFCg5Wm4,4595
|
5
|
+
ormlambda/common/abstract_classes/decomposition_query.py,sha256=c-Vo0f2dnVCv9Qsmqt9AZaZAYvyYhtZFdc3HKyHCMZ0,11396
|
6
6
|
ormlambda/common/abstract_classes/non_query_base.py,sha256=5jhvyT7OZaJxlGp9XMP3vQ4ei5QQZBn-fFtJnD640mE,980
|
7
7
|
ormlambda/common/abstract_classes/query_base.py,sha256=6qUFPwsVx45kUW3b66pHiSyjhcH4mzbdkddlGeUnG7c,266
|
8
8
|
ormlambda/common/enums/__init__.py,sha256=4lVKCHi1JalwgNzjsAXqX-C54NJEH83y2v5baMO8fN4,103
|
9
9
|
ormlambda/common/enums/condition_types.py,sha256=mDPMrtzZu4IYv3-q5Zw4NNgwUoNAx4lih5SIDFRn1Qo,309
|
10
10
|
ormlambda/common/enums/join_type.py,sha256=7LEhE34bzYOwJxe8yxPJo_EjQpGylgClAPX1Wt3z4n4,292
|
11
11
|
ormlambda/common/interfaces/IAggregate.py,sha256=i9zZIYrstP2oolUYqR-udeQQl7M0NwSzrr-VUL0b5HI,271
|
12
|
-
ormlambda/common/interfaces/IDecompositionQuery.py,sha256=
|
12
|
+
ormlambda/common/interfaces/IDecompositionQuery.py,sha256=dHdCxezbWwy-c4U4po2hQB8f_wa0wGFvA2TKaHBx0Do,1363
|
13
13
|
ormlambda/common/interfaces/INonQueryCommand.py,sha256=7CjLW4sKqkR5zUIGvhRXOtzTs6vypJW1a9EJHlgCw2c,260
|
14
14
|
ormlambda/common/interfaces/IQueryCommand.py,sha256=hfzCosK4-n8RJIb2PYs8b0qU3TNmfYluZXBf47KxxKs,331
|
15
|
-
ormlambda/common/interfaces/IRepositoryBase.py,sha256=
|
16
|
-
ormlambda/common/interfaces/IStatements.py,sha256=
|
15
|
+
ormlambda/common/interfaces/IRepositoryBase.py,sha256=zGm7tDrFLKj3FMYN-MWI-em3INws27g2yByNYYMx0qk,1126
|
16
|
+
ormlambda/common/interfaces/IStatements.py,sha256=dlH8rL0flsF6QuvDsOb3LuM-FhtO9_D64SDjz07f3zg,10296
|
17
17
|
ormlambda/common/interfaces/__init__.py,sha256=00ca9a-u_A8DzyEyxPfBMxfqLKFzzUgJaeNmoRitAbA,360
|
18
18
|
ormlambda/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
19
|
ormlambda/components/delete/IDelete.py,sha256=06ZEdbKBxsHSwsGMBu0E1om4WJjojZAm-L3b95eQrcc,139
|
@@ -42,17 +42,18 @@ ormlambda/databases/my_sql/clauses/insert.py,sha256=LO9H8VVK3j62dICXqpEUXKxOHPxk
|
|
42
42
|
ormlambda/databases/my_sql/clauses/joins.py,sha256=U6JnUvQo7AXyEeK-X1jMvckXefgAB7ugSmJCZhH1XQI,3058
|
43
43
|
ormlambda/databases/my_sql/clauses/limit.py,sha256=a4lI8FVRKpfXwBQTXdkbVtlQkmzcjE20ymiCy1IaSc4,391
|
44
44
|
ormlambda/databases/my_sql/clauses/offset.py,sha256=81170JhsQndjKlDfQj1ll-tiYHQbW8SuU4IE3mhQF7Y,395
|
45
|
-
ormlambda/databases/my_sql/clauses/order.py,sha256=
|
46
|
-
ormlambda/databases/my_sql/clauses/select.py,sha256=
|
45
|
+
ormlambda/databases/my_sql/clauses/order.py,sha256=RVqyEtD0InnOPAmRvjtNJ-vrAy0V9OHMLoFB8TkpX0I,1137
|
46
|
+
ormlambda/databases/my_sql/clauses/select.py,sha256=unJBteFK5D360KnIPyqz8OMaVJqXJlI5Kkp6u0JJ1vU,1574
|
47
47
|
ormlambda/databases/my_sql/clauses/update.py,sha256=3Htw0_PT3EckEiF214-V2r63lcNoRBroKZI9yg48Ddg,1867
|
48
48
|
ormlambda/databases/my_sql/clauses/upsert.py,sha256=eW2pQ4ax-GKuXiaWKoSRSS1GrHuILJBsmj83ADbBQ34,1951
|
49
49
|
ormlambda/databases/my_sql/clauses/where_condition.py,sha256=UgU5vnTqFAx91wKwnECpww5tETDZ9F7IdC_SiZOgZhI,8336
|
50
|
-
ormlambda/databases/my_sql/functions/__init__.py,sha256=
|
50
|
+
ormlambda/databases/my_sql/functions/__init__.py,sha256=XpLhHssbw43-0NRDpY0T9SKhyedzCQOCGQWdrfHvbVw,155
|
51
51
|
ormlambda/databases/my_sql/functions/concat.py,sha256=cZztl5eSATpYMKVKfyPbul6OocaUkaEGpt3uWmhf-3o,1177
|
52
52
|
ormlambda/databases/my_sql/functions/group_by.py,sha256=ym1L6RBA70hmzrkJ-IA_-6Esq7eoVgNdf1vWpqxRq-8,1080
|
53
53
|
ormlambda/databases/my_sql/functions/max.py,sha256=zrO_RBKsHhyokEmSpPI6Yg5OY6Jf4GGp2RveBJdOuuA,1190
|
54
|
-
ormlambda/databases/my_sql/
|
55
|
-
ormlambda/databases/my_sql/
|
54
|
+
ormlambda/databases/my_sql/functions/min.py,sha256=SEVuUdIJNz9zM5za1kLTWalFkhErjsjyBb8SU8n0F94,1190
|
55
|
+
ormlambda/databases/my_sql/repository.py,sha256=fSAAmO_T5mC1bodOVZn5CY9JSfRq4VhsdlWT1JLl7Zw,6927
|
56
|
+
ormlambda/databases/my_sql/statements.py,sha256=NTAZs9TDEI_KoH0GNG2Ea8oTFckXeTfTg_cdUHSYziA,11493
|
56
57
|
ormlambda/model_base.py,sha256=RrAATQWX_bHJ0ZQ5sCHyJKA4NiR7ZJY4I6dqhnGWWAc,1216
|
57
58
|
ormlambda/utils/__init__.py,sha256=ywMdWqmA2jHj19-W-S04yfaYF5hv4IZ1lZDq0B8Jnjs,142
|
58
59
|
ormlambda/utils/column.py,sha256=5FAGzCU4yvNS4MhwJJ5i73h7RvHD5UCVox0NdzMsMiE,1945
|
@@ -69,7 +70,7 @@ ormlambda/utils/module_tree/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
|
|
69
70
|
ormlambda/utils/module_tree/dfs_traversal.py,sha256=lSF03G63XtJFLp03ueAmsHMBvhUkjptDbK3IugXm8iU,1425
|
70
71
|
ormlambda/utils/module_tree/dynamic_module.py,sha256=zwvjU3U2cz6H2CDp9Gncs5D5bSAyfITNa2SDqFDl8rw,8551
|
71
72
|
ormlambda/utils/table_constructor.py,sha256=8Apm44K6MiYMK3PQyK74MUV18OatbFI9eDLAVklQO0w,11660
|
72
|
-
ormlambda-2.
|
73
|
-
ormlambda-2.
|
74
|
-
ormlambda-2.
|
75
|
-
ormlambda-2.
|
73
|
+
ormlambda-2.7.2.dist-info/LICENSE,sha256=xBprFw8GJLdHMOoUqDk0427EvjIcbEREvXXVFULuuXU,1080
|
74
|
+
ormlambda-2.7.2.dist-info/METADATA,sha256=8CXacsolfAa439Blpjufzi4bSOTOjked1fz4C_4gYZ4,8428
|
75
|
+
ormlambda-2.7.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
76
|
+
ormlambda-2.7.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|