ormlambda 3.11.2__py3-none-any.whl → 3.12.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/__init__.py +1 -1
- ormlambda/common/abstract_classes/clause_info_converter.py +73 -0
- ormlambda/common/abstract_classes/decomposition_query.py +12 -68
- ormlambda/common/abstract_classes/non_query_base.py +2 -2
- ormlambda/common/interfaces/ICustomAlias.py +1 -1
- ormlambda/components/delete/abstract_delete.py +2 -2
- ormlambda/components/join/__init__.py +1 -0
- ormlambda/databases/my_sql/clauses/drop_table.py +8 -5
- ormlambda/databases/my_sql/clauses/group_by.py +1 -2
- ormlambda/databases/my_sql/clauses/select.py +2 -0
- ormlambda/databases/my_sql/clauses/upsert.py +8 -4
- ormlambda/databases/my_sql/query_builder.py +158 -0
- ormlambda/databases/my_sql/statements.py +18 -156
- ormlambda/engine/url.py +4 -1
- ormlambda/sql/clause_info/__init__.py +2 -1
- ormlambda/sql/clause_info/aggregate_function_base.py +86 -0
- ormlambda/sql/clause_info/clause_info.py +1 -98
- ormlambda/sql/clause_info/interface/IClauseInfo.py +37 -0
- ormlambda/sql/clause_info/interface/__init__.py +1 -0
- ormlambda/sql/column.py +3 -0
- ormlambda/statements/base_statement.py +6 -2
- ormlambda/statements/interfaces/IStatements.py +21 -18
- ormlambda/utils/module_tree/dynamic_module.py +3 -2
- {ormlambda-3.11.2.dist-info → ormlambda-3.12.2.dist-info}/METADATA +56 -10
- {ormlambda-3.11.2.dist-info → ormlambda-3.12.2.dist-info}/RECORD +28 -23
- /ormlambda/{databases/my_sql → components/join}/join_context.py +0 -0
- {ormlambda-3.11.2.dist-info → ormlambda-3.12.2.dist-info}/LICENSE +0 -0
- {ormlambda-3.11.2.dist-info → ormlambda-3.12.2.dist-info}/WHEEL +0 -0
@@ -1,12 +1,9 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import Concatenate, Iterable,
|
2
|
+
from typing import Concatenate, Iterable, override, Type, TYPE_CHECKING, Any, Callable, Optional
|
3
3
|
from mysql.connector import errors, errorcode
|
4
4
|
|
5
|
-
from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext
|
6
|
-
from ormlambda.databases.my_sql.clauses.joins import JoinSelector
|
7
5
|
from ormlambda import ForeignKey
|
8
6
|
|
9
|
-
from ormlambda.common.interfaces import IQuery
|
10
7
|
from mysql.connector import MySQLConnection
|
11
8
|
|
12
9
|
|
@@ -16,9 +13,9 @@ if TYPE_CHECKING:
|
|
16
13
|
from ormlambda.statements.types import OrderTypes
|
17
14
|
from ormlambda.sql.types import ColumnType
|
18
15
|
from ormlambda.statements.types import SelectCols
|
19
|
-
from ormlambda.repository
|
16
|
+
from ormlambda.repository import BaseRepository
|
20
17
|
from ormlambda.statements.interfaces import IStatements_two_generic
|
21
|
-
from ormlambda.
|
18
|
+
from ormlambda.statements.types import TypeExists
|
22
19
|
from ormlambda.sql.clause_info import IAggregate
|
23
20
|
from ormlambda.statements.types import WhereTypes
|
24
21
|
|
@@ -44,8 +41,10 @@ from .clauses import Alias
|
|
44
41
|
from ormlambda import Table, Column
|
45
42
|
from ormlambda.common.enums import JoinType
|
46
43
|
from . import functions as func
|
47
|
-
from .
|
44
|
+
from ormlambda.components.join import JoinContext, TupleJoinType
|
45
|
+
|
48
46
|
from ormlambda.common.global_checker import GlobalChecker
|
47
|
+
from .query_builder import QueryBuilder
|
49
48
|
|
50
49
|
|
51
50
|
# COMMENT: It's so important to prevent information generated by other tests from being retained in the class.
|
@@ -63,152 +62,11 @@ def clear_list[T, **P](f: Callable[Concatenate[MySQLStatements, P], T]) -> Calla
|
|
63
62
|
return wrapper
|
64
63
|
|
65
64
|
|
66
|
-
class OrderType(TypedDict):
|
67
|
-
Select: Select
|
68
|
-
JoinSelector: JoinSelector
|
69
|
-
Where: Where
|
70
|
-
Order: Order
|
71
|
-
GroupBy: GroupBy
|
72
|
-
Having: Having
|
73
|
-
Limit: Limit
|
74
|
-
Offset: Offset
|
75
|
-
|
76
|
-
|
77
|
-
class QueryBuilder(IQuery):
|
78
|
-
__order__: tuple[str, ...] = ("Select", "JoinSelector", "Where", "GroupBy", "Having", "Order", "Limit", "Offset")
|
79
|
-
|
80
|
-
def __init__(self, by: JoinType = JoinType.INNER_JOIN):
|
81
|
-
self._context = ClauseInfoContext()
|
82
|
-
self._query_list: OrderType = {}
|
83
|
-
self._by = by
|
84
|
-
|
85
|
-
self._joins: Optional[IQuery] = None
|
86
|
-
self._select: Optional[IQuery] = None
|
87
|
-
self._where: Optional[IQuery] = None
|
88
|
-
self._order: Optional[IQuery] = None
|
89
|
-
self._group_by: Optional[IQuery] = None
|
90
|
-
self._limit: Optional[IQuery] = None
|
91
|
-
self._offset: Optional[IQuery] = None
|
92
|
-
|
93
|
-
def add_statement[T](self, clause: ClauseInfo[T]):
|
94
|
-
self.update_context(clause)
|
95
|
-
self._query_list[type(clause).__name__] = clause
|
96
|
-
|
97
|
-
@property
|
98
|
-
def by(self) -> JoinType:
|
99
|
-
return self._by
|
100
|
-
|
101
|
-
@by.setter
|
102
|
-
def by(self, value: JoinType) -> None:
|
103
|
-
self._by = value
|
104
|
-
|
105
|
-
@property
|
106
|
-
def JOINS(self) -> Optional[set[JoinSelector]]:
|
107
|
-
return self._joins
|
108
|
-
|
109
|
-
@property
|
110
|
-
def SELECT(self) -> IQuery:
|
111
|
-
return self._query_list.get("Select", None)
|
112
|
-
|
113
|
-
@property
|
114
|
-
def WHERE(self) -> IQuery:
|
115
|
-
where = self._query_list.get("Where", None)
|
116
|
-
if not isinstance(where, Iterable):
|
117
|
-
if not where:
|
118
|
-
return ()
|
119
|
-
return (where,)
|
120
|
-
return where
|
121
|
-
|
122
|
-
@property
|
123
|
-
def ORDER(self) -> IQuery:
|
124
|
-
return self._query_list.get("Order", None)
|
125
|
-
|
126
|
-
@property
|
127
|
-
def GROUP_BY(self) -> IQuery:
|
128
|
-
return self._query_list.get("GroupBy", None)
|
129
|
-
|
130
|
-
@property
|
131
|
-
def HAVING(self) -> IQuery:
|
132
|
-
where = self._query_list.get("Having", None)
|
133
|
-
if not isinstance(where, Iterable):
|
134
|
-
if not where:
|
135
|
-
return ()
|
136
|
-
return (where,)
|
137
|
-
return where
|
138
|
-
|
139
|
-
@property
|
140
|
-
def LIMIT(self) -> IQuery:
|
141
|
-
return self._query_list.get("Limit", None)
|
142
|
-
|
143
|
-
@property
|
144
|
-
def OFFSET(self) -> IQuery:
|
145
|
-
return self._query_list.get("Offset", None)
|
146
|
-
|
147
|
-
@property
|
148
|
-
def query(self) -> str:
|
149
|
-
# COMMENT: (select.query, query)We must first create an alias for 'FROM' and then define all the remaining clauses.
|
150
|
-
# This order is mandatory because it adds the clause name to the context when accessing the .query property of 'FROM'
|
151
|
-
|
152
|
-
extract_joins = self.pop_tables_and_create_joins_from_ForeignKey(self._by)
|
153
|
-
|
154
|
-
JOINS = self.stringify_foreign_key(extract_joins, " ")
|
155
|
-
query_list: tuple[str, ...] = (
|
156
|
-
self.SELECT.query,
|
157
|
-
JOINS,
|
158
|
-
Where.join_condition(self.WHERE, True, self._context) if self.WHERE else None,
|
159
|
-
self.GROUP_BY.query if self.GROUP_BY else None,
|
160
|
-
Having.join_condition(self.HAVING, True, self._context) if self.HAVING else None,
|
161
|
-
self.ORDER.query if self.ORDER else None,
|
162
|
-
self.LIMIT.query if self.LIMIT else None,
|
163
|
-
self.OFFSET.query if self.OFFSET else None,
|
164
|
-
)
|
165
|
-
return " ".join([x for x in query_list if x])
|
166
|
-
|
167
|
-
def stringify_foreign_key(self, joins: set[JoinSelector], sep: str = "\n") -> Optional[str]:
|
168
|
-
if not joins:
|
169
|
-
return None
|
170
|
-
sorted_joins = JoinSelector.sort_join_selectors(joins)
|
171
|
-
return f"{sep}".join([join.query for join in sorted_joins])
|
172
|
-
|
173
|
-
def pop_tables_and_create_joins_from_ForeignKey(self, by: JoinType = JoinType.INNER_JOIN) -> set[JoinSelector]:
|
174
|
-
# 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.
|
175
|
-
if not ForeignKey.stored_calls:
|
176
|
-
return None
|
177
|
-
|
178
|
-
joins = set()
|
179
|
-
# Always it's gonna be a set of two
|
180
|
-
# FIXME [x]: Resolved when we get Compare object instead ClauseInfo. For instance, when we have multiples condition using '&' or '|'
|
181
|
-
for fk in ForeignKey.stored_calls.copy():
|
182
|
-
fk = ForeignKey.stored_calls.pop(fk)
|
183
|
-
self._context._add_table_alias(fk.tright, fk.alias)
|
184
|
-
join = JoinSelector(fk.resolved_function(self._context), by, context=self._context, alias=fk.alias)
|
185
|
-
joins.add(join)
|
186
|
-
|
187
|
-
return joins
|
188
|
-
|
189
|
-
def clear(self) -> None:
|
190
|
-
self.__init__()
|
191
|
-
return None
|
192
|
-
|
193
|
-
def update_context(self, clause: ClauseInfo) -> None:
|
194
|
-
if not hasattr(clause, "context"):
|
195
|
-
return None
|
196
|
-
|
197
|
-
if clause.context is not None:
|
198
|
-
self._context.update(clause.context)
|
199
|
-
clause.context = self._context
|
200
|
-
|
201
|
-
|
202
65
|
class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
203
|
-
def __init__(self, model: tuple[T, *Ts], repository:
|
66
|
+
def __init__(self, model: tuple[T, *Ts], repository: BaseRepository) -> None:
|
204
67
|
super().__init__(model, repository=repository)
|
205
68
|
self._query_builder = QueryBuilder()
|
206
69
|
|
207
|
-
@property
|
208
|
-
@override
|
209
|
-
def repository(self) -> IRepositoryBase:
|
210
|
-
return self._repository
|
211
|
-
|
212
70
|
@override
|
213
71
|
def create_table(self, if_exists: TypeExists = "fail") -> None:
|
214
72
|
name: str = self._model.__table_name__
|
@@ -232,10 +90,6 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
232
90
|
self._repository.execute(query)
|
233
91
|
return None
|
234
92
|
|
235
|
-
@override
|
236
|
-
def table_exists(self) -> bool:
|
237
|
-
return self._repository.table_exists(self._model.__table_name__)
|
238
|
-
|
239
93
|
@override
|
240
94
|
@clear_list
|
241
95
|
def insert(self, instances: T | list[T]) -> None:
|
@@ -293,7 +147,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
293
147
|
@override
|
294
148
|
def count[TProp](
|
295
149
|
self,
|
296
|
-
selection: None | SelectCols[T,TProp] = lambda x: "*",
|
150
|
+
selection: None | SelectCols[T, TProp] = lambda x: "*",
|
297
151
|
alias="count",
|
298
152
|
execute: bool = False,
|
299
153
|
) -> Optional[int]:
|
@@ -386,6 +240,9 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
386
240
|
by: JoinType = JoinType.INNER_JOIN,
|
387
241
|
**kwargs,
|
388
242
|
):
|
243
|
+
if "alias" in kwargs:
|
244
|
+
alias = kwargs.pop("alias")
|
245
|
+
kwargs["alias_clause"] = alias
|
389
246
|
select_clause = GlobalChecker.resolved_callback_object(selector, self._models)
|
390
247
|
|
391
248
|
if selector is None:
|
@@ -400,6 +257,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
400
257
|
select = Select[T, *Ts](
|
401
258
|
self._models,
|
402
259
|
columns=select_clause,
|
260
|
+
**kwargs,
|
403
261
|
)
|
404
262
|
self._query_builder.add_statement(select)
|
405
263
|
|
@@ -458,14 +316,18 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
458
316
|
)
|
459
317
|
|
460
318
|
@override
|
461
|
-
def groupby[TProp](self, column: ColumnType[TProp] | Callable[[T, *Ts], Any]):
|
319
|
+
def groupby[TProp](self, column: ColumnType[TProp] | Callable[[T, *Ts], Any]) -> IStatements_two_generic[T]:
|
320
|
+
column = GlobalChecker.resolved_callback_object(column, self.models)
|
321
|
+
|
462
322
|
groupby = GroupBy(column=column, context=self._query_builder._context)
|
463
323
|
# Only can be one LIMIT SQL parameter. We only use the last LimitQuery
|
464
324
|
self._query_builder.add_statement(groupby)
|
465
325
|
return self
|
466
326
|
|
467
327
|
@override
|
468
|
-
def alias[TProp](self, column:
|
328
|
+
def alias[TProp](self, column: SelectCols[T, TProp], alias: AliasType[ClauseInfo[T]]) -> ClauseInfo[T]:
|
329
|
+
column = GlobalChecker.resolved_callback_object(column, self.models)
|
330
|
+
|
469
331
|
return Alias(
|
470
332
|
table=column.table,
|
471
333
|
column=column,
|
ormlambda/engine/url.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
"""
|
2
2
|
URL class extracted from SQLAlchemy
|
3
3
|
"""
|
4
|
+
|
4
5
|
from __future__ import annotations
|
5
6
|
|
6
7
|
import re
|
@@ -27,7 +28,9 @@ from urllib.parse import (
|
|
27
28
|
|
28
29
|
from . import utils
|
29
30
|
|
30
|
-
type DrivernameType = Literal[
|
31
|
+
type DrivernameType = Literal["mysql"] | str
|
32
|
+
|
33
|
+
|
31
34
|
class URL(NamedTuple):
|
32
35
|
"""
|
33
36
|
Represent the components of a URL used to connect to a database.
|
@@ -1,3 +1,4 @@
|
|
1
1
|
from .interface import IAggregate # noqa: F401
|
2
|
-
from .clause_info import ClauseInfo
|
2
|
+
from .clause_info import ClauseInfo # noqa: F401
|
3
|
+
from .aggregate_function_base import AggregateFunctionBase # noqa: F401
|
3
4
|
from .clause_info_context import ClauseContextType, ClauseInfoContext # noqa: F401
|
@@ -0,0 +1,86 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
import abc
|
3
|
+
import typing as tp
|
4
|
+
|
5
|
+
from ormlambda import Table
|
6
|
+
from ormlambda import Column
|
7
|
+
from ormlambda.sql.types import (
|
8
|
+
TableType,
|
9
|
+
ColumnType,
|
10
|
+
AliasType,
|
11
|
+
)
|
12
|
+
from .interface import IAggregate
|
13
|
+
from ormlambda.common.errors import NotKeysInIAggregateError
|
14
|
+
from ormlambda.sql import ForeignKey
|
15
|
+
from ormlambda.sql.table import TableMeta
|
16
|
+
from .clause_info import ClauseInfo
|
17
|
+
from .clause_info_context import ClauseContextType
|
18
|
+
|
19
|
+
|
20
|
+
class AggregateFunctionBase[T: Table](ClauseInfo[T], IAggregate):
|
21
|
+
def __init__[TProp: Column](
|
22
|
+
self,
|
23
|
+
table: TableType[T],
|
24
|
+
column: tp.Optional[ColumnType[TProp]] = None,
|
25
|
+
alias_table: tp.Optional[AliasType[ClauseInfo[T]]] = None,
|
26
|
+
alias_clause: tp.Optional[AliasType[ClauseInfo[T]]] = None,
|
27
|
+
context: ClauseContextType = None,
|
28
|
+
keep_asterisk: bool = False,
|
29
|
+
preserve_context: bool = False,
|
30
|
+
):
|
31
|
+
self._alias_aggregate = alias_clause
|
32
|
+
super().__init__(
|
33
|
+
table=table,
|
34
|
+
column=column,
|
35
|
+
alias_table=alias_table,
|
36
|
+
context=context,
|
37
|
+
keep_asterisk=keep_asterisk,
|
38
|
+
preserve_context=preserve_context,
|
39
|
+
)
|
40
|
+
|
41
|
+
@staticmethod
|
42
|
+
@abc.abstractmethod
|
43
|
+
def FUNCTION_NAME() -> str: ...
|
44
|
+
|
45
|
+
@classmethod
|
46
|
+
def _convert_into_clauseInfo[TypeColumns, TProp](cls, columns: ClauseInfo | ColumnType[TProp], context: ClauseContextType) -> list[ClauseInfo]:
|
47
|
+
type DEFAULT = tp.Literal["default"]
|
48
|
+
type ClusterType = ColumnType | ForeignKey | DEFAULT
|
49
|
+
|
50
|
+
dicc_type: dict[ClusterType, tp.Callable[[ClusterType], ClauseInfo]] = {
|
51
|
+
Column: lambda column: ClauseInfo(column.table, column, context=context),
|
52
|
+
ClauseInfo: lambda column: column,
|
53
|
+
ForeignKey: lambda tbl: ClauseInfo(tbl.tright, tbl.tright, context=context),
|
54
|
+
TableMeta: lambda tbl: ClauseInfo(tbl, tbl, context=context),
|
55
|
+
"default": lambda column: ClauseInfo(table=None, column=column, context=context),
|
56
|
+
}
|
57
|
+
all_clauses: list[ClauseInfo] = []
|
58
|
+
if isinstance(columns, str) or not isinstance(columns, tp.Iterable):
|
59
|
+
columns = (columns,)
|
60
|
+
for value in columns:
|
61
|
+
all_clauses.append(dicc_type.get(type(value), dicc_type["default"])(value))
|
62
|
+
|
63
|
+
return all_clauses
|
64
|
+
|
65
|
+
@tp.override
|
66
|
+
@property
|
67
|
+
def query(self) -> str:
|
68
|
+
wrapped_ci = self.wrapped_clause_info(self)
|
69
|
+
if not self._alias_aggregate:
|
70
|
+
return wrapped_ci
|
71
|
+
|
72
|
+
return ClauseInfo(
|
73
|
+
table=None,
|
74
|
+
column=wrapped_ci,
|
75
|
+
alias_clause=self._alias_aggregate,
|
76
|
+
context=self._context,
|
77
|
+
keep_asterisk=self._keep_asterisk,
|
78
|
+
preserve_context=self._preserve_context,
|
79
|
+
).query
|
80
|
+
|
81
|
+
def wrapped_clause_info(self, ci: ClauseInfo[T]) -> str:
|
82
|
+
# avoid use placeholder when using IAggregate because no make sense.
|
83
|
+
if self._alias_aggregate and (found := self._keyRegex.findall(self._alias_aggregate)):
|
84
|
+
raise NotKeysInIAggregateError(found)
|
85
|
+
|
86
|
+
return f"{self.FUNCTION_NAME()}({ci._create_query()})"
|
@@ -1,21 +1,17 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
import abc
|
3
2
|
import typing as tp
|
4
3
|
import re
|
5
4
|
|
6
5
|
from ormlambda import Table
|
7
6
|
from ormlambda import Column
|
8
|
-
from ormlambda.common.interfaces.IQueryCommand import IQuery
|
9
7
|
from ormlambda.sql.types import (
|
10
8
|
ASTERISK,
|
11
9
|
TableType,
|
12
10
|
ColumnType,
|
13
11
|
AliasType,
|
14
12
|
)
|
15
|
-
from .interface import
|
16
|
-
from ormlambda.common.errors import NotKeysInIAggregateError
|
13
|
+
from .interface import IClauseInfo
|
17
14
|
from ormlambda.sql import ForeignKey
|
18
|
-
from ormlambda.sql.table import TableMeta
|
19
15
|
from ormlambda.caster import Caster
|
20
16
|
|
21
17
|
|
@@ -32,30 +28,6 @@ class ReplacePlaceholderError(ValueError):
|
|
32
28
|
return "You cannot use {" + self.placeholder + "} placeholder without using '" + self.attr + "' attribute"
|
33
29
|
|
34
30
|
|
35
|
-
class IClauseInfo[T: Table](IQuery):
|
36
|
-
@property
|
37
|
-
@abc.abstractmethod
|
38
|
-
def table(self) -> TableType[T]: ...
|
39
|
-
@property
|
40
|
-
@abc.abstractmethod
|
41
|
-
def alias_clause(self) -> tp.Optional[str]: ...
|
42
|
-
@property
|
43
|
-
@abc.abstractmethod
|
44
|
-
def alias_table(self) -> tp.Optional[str]: ...
|
45
|
-
@property
|
46
|
-
@abc.abstractmethod
|
47
|
-
def column(self) -> str: ...
|
48
|
-
@property
|
49
|
-
@abc.abstractmethod
|
50
|
-
def unresolved_column(self) -> ColumnType: ...
|
51
|
-
@property
|
52
|
-
@abc.abstractmethod
|
53
|
-
def context(self) -> ClauseContextType: ...
|
54
|
-
@property
|
55
|
-
@abc.abstractmethod
|
56
|
-
def dtype[TProp](self) -> tp.Optional[tp.Type[TProp]]: ...
|
57
|
-
|
58
|
-
|
59
31
|
class ClauseInfo[T: Table](IClauseInfo[T]):
|
60
32
|
_keyRegex: re.Pattern = re.compile(r"{([^{}:]+)}")
|
61
33
|
|
@@ -363,72 +335,3 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
363
335
|
if isinstance(data, Column):
|
364
336
|
return True
|
365
337
|
return False
|
366
|
-
|
367
|
-
|
368
|
-
class AggregateFunctionBase[T: Table](ClauseInfo[T], IAggregate):
|
369
|
-
def __init__[TProp: Column](
|
370
|
-
self,
|
371
|
-
table: TableType[T],
|
372
|
-
column: tp.Optional[ColumnType[TProp]] = None,
|
373
|
-
alias_table: tp.Optional[AliasType[ClauseInfo[T]]] = None,
|
374
|
-
alias_clause: tp.Optional[AliasType[ClauseInfo[T]]] = None,
|
375
|
-
context: ClauseContextType = None,
|
376
|
-
keep_asterisk: bool = False,
|
377
|
-
preserve_context: bool = False,
|
378
|
-
):
|
379
|
-
self._alias_aggregate = alias_clause
|
380
|
-
super().__init__(
|
381
|
-
table=table,
|
382
|
-
column=column,
|
383
|
-
alias_table=alias_table,
|
384
|
-
context=context,
|
385
|
-
keep_asterisk=keep_asterisk,
|
386
|
-
preserve_context=preserve_context,
|
387
|
-
)
|
388
|
-
|
389
|
-
@staticmethod
|
390
|
-
@abc.abstractmethod
|
391
|
-
def FUNCTION_NAME() -> str: ...
|
392
|
-
|
393
|
-
@classmethod
|
394
|
-
def _convert_into_clauseInfo[TypeColumns, TProp](cls, columns: ClauseInfo | ColumnType[TProp], context: ClauseContextType) -> list[ClauseInfo]:
|
395
|
-
type DEFAULT = tp.Literal["default"]
|
396
|
-
type ClusterType = ColumnType | ForeignKey | DEFAULT
|
397
|
-
|
398
|
-
dicc_type: dict[ClusterType, tp.Callable[[ClusterType], ClauseInfo]] = {
|
399
|
-
Column: lambda column: ClauseInfo(column.table, column, context=context),
|
400
|
-
ClauseInfo: lambda column: column,
|
401
|
-
ForeignKey: lambda tbl: ClauseInfo(tbl.tright, tbl.tright, context=context),
|
402
|
-
TableMeta: lambda tbl: ClauseInfo(tbl, tbl, context=context),
|
403
|
-
"default": lambda column: ClauseInfo(table=None, column=column, context=context),
|
404
|
-
}
|
405
|
-
all_clauses: list[ClauseInfo] = []
|
406
|
-
if isinstance(columns, str) or not isinstance(columns, tp.Iterable):
|
407
|
-
columns = (columns,)
|
408
|
-
for value in columns:
|
409
|
-
all_clauses.append(dicc_type.get(type(value), dicc_type["default"])(value))
|
410
|
-
|
411
|
-
return all_clauses
|
412
|
-
|
413
|
-
@tp.override
|
414
|
-
@property
|
415
|
-
def query(self) -> str:
|
416
|
-
wrapped_ci = self.wrapped_clause_info(self)
|
417
|
-
if not self._alias_aggregate:
|
418
|
-
return wrapped_ci
|
419
|
-
|
420
|
-
return ClauseInfo(
|
421
|
-
table=None,
|
422
|
-
column=wrapped_ci,
|
423
|
-
alias_clause=self._alias_aggregate,
|
424
|
-
context=self._context,
|
425
|
-
keep_asterisk=self._keep_asterisk,
|
426
|
-
preserve_context=self._preserve_context,
|
427
|
-
).query
|
428
|
-
|
429
|
-
def wrapped_clause_info(self, ci: ClauseInfo[T]) -> str:
|
430
|
-
# avoid use placeholder when using IAggregate because no make sense.
|
431
|
-
if self._alias_aggregate and (found := self._keyRegex.findall(self._alias_aggregate)):
|
432
|
-
raise NotKeysInIAggregateError(found)
|
433
|
-
|
434
|
-
return f"{self.FUNCTION_NAME()}({ci._create_query()})"
|
@@ -0,0 +1,37 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
import abc
|
3
|
+
import typing as tp
|
4
|
+
|
5
|
+
from ormlambda import Table
|
6
|
+
from ormlambda.common.interfaces.IQueryCommand import IQuery
|
7
|
+
from ormlambda.sql.types import (
|
8
|
+
TableType,
|
9
|
+
ColumnType,
|
10
|
+
)
|
11
|
+
|
12
|
+
|
13
|
+
from ..clause_info_context import ClauseContextType
|
14
|
+
|
15
|
+
|
16
|
+
class IClauseInfo[T: Table](IQuery):
|
17
|
+
@property
|
18
|
+
@abc.abstractmethod
|
19
|
+
def table(self) -> TableType[T]: ...
|
20
|
+
@property
|
21
|
+
@abc.abstractmethod
|
22
|
+
def alias_clause(self) -> tp.Optional[str]: ...
|
23
|
+
@property
|
24
|
+
@abc.abstractmethod
|
25
|
+
def alias_table(self) -> tp.Optional[str]: ...
|
26
|
+
@property
|
27
|
+
@abc.abstractmethod
|
28
|
+
def column(self) -> str: ...
|
29
|
+
@property
|
30
|
+
@abc.abstractmethod
|
31
|
+
def unresolved_column(self) -> ColumnType: ...
|
32
|
+
@property
|
33
|
+
@abc.abstractmethod
|
34
|
+
def context(self) -> ClauseContextType: ...
|
35
|
+
@property
|
36
|
+
@abc.abstractmethod
|
37
|
+
def dtype[TProp](self) -> tp.Optional[tp.Type[TProp]]: ...
|
ormlambda/sql/column.py
CHANGED
@@ -128,6 +128,9 @@ class Column[TProp]:
|
|
128
128
|
return self.__comparer_creator(other, ConditionType.GREATER_THAN_OR_EQUAL.value, *args)
|
129
129
|
|
130
130
|
def contains[LTable, OTherTable, OtherProp](self, other: ColumnType[OtherProp], *args) -> Comparer[LTable, TProp, OTherTable, OtherProp]:
|
131
|
+
if not isinstance(other, tuple) and isinstance(other, Iterable):
|
132
|
+
other = tuple(other)
|
133
|
+
|
131
134
|
return self.__comparer_creator(other, ConditionType.IN.value, *args)
|
132
135
|
|
133
136
|
def not_contains[LTable, OTherTable, OtherProp](self, other: ColumnType[OtherProp], *args) -> Comparer[LTable, TProp, OTherTable, OtherProp]:
|
@@ -33,6 +33,10 @@ class BaseStatement[T: Table, TRepo](IStatements_two_generic[T, TRepo]):
|
|
33
33
|
# Si no heredase de Table no sabriamos identificar el tipo de dato del que se trata porque al llamar a isinstance, obtendriamos el nombre de la clase que mapea a la tabla, Encargo, Edificio, Presupuesto y no podriamos crear una clase generica
|
34
34
|
raise Exception(f"'{model}' class does not inherit from Table class")
|
35
35
|
|
36
|
+
@override
|
37
|
+
def table_exists(self) -> bool:
|
38
|
+
return self._repository.table_exists(self._model.__table_name__)
|
39
|
+
|
36
40
|
@staticmethod
|
37
41
|
def __valid_repository(repository: Any) -> bool:
|
38
42
|
if not isinstance(repository, BaseRepository):
|
@@ -67,8 +71,8 @@ class BaseStatement[T: Table, TRepo](IStatements_two_generic[T, TRepo]):
|
|
67
71
|
return self._models
|
68
72
|
|
69
73
|
@property
|
70
|
-
|
71
|
-
|
74
|
+
def repository(self) -> BaseRepository:
|
75
|
+
return self._repository
|
72
76
|
|
73
77
|
|
74
78
|
class ClusterQuery[T]:
|
@@ -4,13 +4,12 @@ from enum import Enum
|
|
4
4
|
from abc import abstractmethod, ABC
|
5
5
|
|
6
6
|
|
7
|
-
|
8
7
|
if TYPE_CHECKING:
|
9
8
|
from ormlambda.repository import BaseRepository
|
10
9
|
from ormlambda import Table
|
11
10
|
from ormlambda.sql.clause_info import IAggregate
|
12
11
|
from ormlambda.sql.types import TupleJoinType, ColumnType
|
13
|
-
from ormlambda.
|
12
|
+
from ormlambda.components.join import JoinContext
|
14
13
|
from ormlambda.common.enums import JoinType
|
15
14
|
from ormlambda.sql.clause_info import ClauseInfo
|
16
15
|
from ormlambda.sql.types import AliasType
|
@@ -249,33 +248,33 @@ class IStatements[T: Table](ABC):
|
|
249
248
|
# @overload
|
250
249
|
# def select[TFlavour](self, selector: Optional[Callable[[T], tuple]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> TFlavour: ...
|
251
250
|
@overload
|
252
|
-
def select[TRes](self, selector: SelectorFlavourType[T, TRes] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[TRes, ...]: ...
|
251
|
+
def select[TRes](self, selector: SelectorFlavourType[T, TRes] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> tuple[TRes, ...]: ...
|
253
252
|
@overload
|
254
|
-
def select[*TRes](self, selector: SelectorFlavourType[T, tuple[*TRes]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[tuple[*TRes]]: ...
|
253
|
+
def select[*TRes](self, selector: SelectorFlavourType[T, tuple[*TRes]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> tuple[tuple[*TRes]]: ...
|
255
254
|
@overload
|
256
|
-
def select[TFlavour](self, selector: SelectorFlavourType[T, tuple] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> tuple[TFlavour, ...]: ...
|
255
|
+
def select[TFlavour](self, selector: SelectorFlavourType[T, tuple] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] = ..., alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> tuple[TFlavour, ...]: ...
|
257
256
|
|
258
257
|
@abstractmethod
|
259
|
-
def select[TValue, TFlavour, P](self, selector: SelectorFlavourType[T, tuple[TValue, P]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour] = ..., by: JoinType = ..., **kwargs): ...
|
258
|
+
def select[TValue, TFlavour, P](self, selector: SelectorFlavourType[T, tuple[TValue, P]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour] = ..., by: JoinType = ..., alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs): ...
|
260
259
|
|
261
260
|
# endregion
|
262
261
|
# region select_one
|
263
262
|
@overload
|
264
263
|
def select_one(self) -> T: ...
|
265
264
|
@overload
|
266
|
-
def select_one[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
|
265
|
+
def select_one[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> TFlavour: ...
|
267
266
|
@overload
|
268
267
|
def select_one[T1](self, selector: SelectorOneType[T, T1 | tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
|
269
268
|
@overload
|
270
269
|
def select_one[*TRes](self, selector: SelectorOneType[T, tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
|
271
270
|
@overload
|
272
|
-
def select_one[T1](self, selector: SelectorOneType[T, tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, **kwargs) -> T1: ...
|
271
|
+
def select_one[T1](self, selector: SelectorOneType[T, tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> T1: ...
|
273
272
|
@overload
|
274
|
-
def select_one[T1, TFlavour](self, selector: SelectorOneType[T, T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
|
273
|
+
def select_one[T1, TFlavour](self, selector: SelectorOneType[T, T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> TFlavour: ...
|
275
274
|
@overload
|
276
|
-
def select_one[*TRest](self, selector: SelectorOneType[T, tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], **kwargs) -> tuple[*TRest]: ...
|
275
|
+
def select_one[*TRest](self, selector: SelectorOneType[T, tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> tuple[*TRest]: ...
|
277
276
|
@overload
|
278
|
-
def select_one[TFlavour](self, selector: SelectorOneType[T, tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
|
277
|
+
def select_one[TFlavour](self, selector: SelectorOneType[T, tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> TFlavour: ...
|
279
278
|
@abstractmethod
|
280
279
|
def select_one[TValue, TFlavour, *TRest](
|
281
280
|
self,
|
@@ -295,15 +294,15 @@ class IStatements[T: Table](ABC):
|
|
295
294
|
@overload
|
296
295
|
def first[*TRes](self, selector: SelectorOneType[T, tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
|
297
296
|
@overload
|
298
|
-
def first[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
|
297
|
+
def first[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> TFlavour: ...
|
299
298
|
@overload
|
300
|
-
def first[T1](self, selector: SelectorOneType[T, tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, **kwargs) -> T1: ...
|
299
|
+
def first[T1](self, selector: SelectorOneType[T, tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> T1: ...
|
301
300
|
@overload
|
302
|
-
def first[T1, TFlavour](self, selector: SelectorOneType[T, T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
|
301
|
+
def first[T1, TFlavour](self, selector: SelectorOneType[T, T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> TFlavour: ...
|
303
302
|
@overload
|
304
|
-
def first[*TRest](self, selector: SelectorOneType[T, tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], **kwargs) -> tuple[*TRest]: ...
|
303
|
+
def first[*TRest](self, selector: SelectorOneType[T, tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> tuple[*TRest]: ...
|
305
304
|
@overload
|
306
|
-
def first[TFlavour](self, selector: SelectorOneType[T, tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
|
305
|
+
def first[TFlavour](self, selector: SelectorOneType[T, tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> TFlavour: ...
|
307
306
|
@abstractmethod
|
308
307
|
def first[TValue, TFlavour, *TRest](
|
309
308
|
self,
|
@@ -316,13 +315,17 @@ class IStatements[T: Table](ABC):
|
|
316
315
|
# endregion
|
317
316
|
|
318
317
|
# region groupby
|
318
|
+
@overload
|
319
|
+
def groupby[TRepo](self, column: list[SelectCols[T, TRepo]]) -> IStatements[T]: ...
|
320
|
+
@overload
|
321
|
+
def groupby[TRepo](self, column: SelectCols[T, TRepo]) -> IStatements[T]: ...
|
319
322
|
@abstractmethod
|
320
|
-
def groupby[TRepo](self, column:
|
323
|
+
def groupby[TRepo](self, column: list[SelectCols[T, TRepo]] | SelectCols[T, TRepo]) -> IStatements[T]: ...
|
321
324
|
|
322
325
|
# endregion
|
323
326
|
|
324
327
|
@abstractmethod
|
325
|
-
def alias[TProp](self, column:
|
328
|
+
def alias[TProp](self, column: SelectCols[T, TProp], alias: AliasType[ClauseInfo[T]]) -> ClauseInfo[T]: ...
|
326
329
|
|
327
330
|
|
328
331
|
class IStatements_two_generic[T, TPool](IStatements[T]):
|
@@ -2,7 +2,8 @@ from __future__ import annotations
|
|
2
2
|
import sys
|
3
3
|
from pathlib import Path
|
4
4
|
from typing import Optional, Type, TYPE_CHECKING
|
5
|
-
|
5
|
+
|
6
|
+
# from collections import defaultdict
|
6
7
|
import importlib.util
|
7
8
|
import inspect
|
8
9
|
import re
|
@@ -12,7 +13,7 @@ if TYPE_CHECKING:
|
|
12
13
|
|
13
14
|
|
14
15
|
# from ormlambda import ForeignKey
|
15
|
-
from .dfs_traversal import DFSTraversal
|
16
|
+
# from .dfs_traversal import DFSTraversal
|
16
17
|
|
17
18
|
|
18
19
|
class Node:
|