ormlambda 3.11.1__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/caster/caster.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/delete.py +0 -1
- 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 +19 -156
- ormlambda/engine/__init__.py +1 -1
- 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 +25 -19
- ormlambda/utils/module_tree/dynamic_module.py +3 -2
- {ormlambda-3.11.1.dist-info → ormlambda-3.12.2.dist-info}/METADATA +66 -16
- {ormlambda-3.11.1.dist-info → ormlambda-3.12.2.dist-info}/RECORD +31 -28
- {ormlambda-3.11.1.dist-info → ormlambda-3.12.2.dist-info}/WHEEL +1 -1
- ormlambda/databases/my_sql/caster/read.py +0 -39
- ormlambda/databases/my_sql/caster/write.py +0 -37
- /ormlambda/{databases/my_sql → components/join}/join_context.py +0 -0
- {ormlambda-3.11.1.dist-info → ormlambda-3.12.2.dist-info}/LICENSE +0 -0
@@ -1,23 +1,21 @@
|
|
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
|
|
13
10
|
if TYPE_CHECKING:
|
11
|
+
from ormlambda.sql.types import AliasType
|
14
12
|
from ormlambda import Table
|
15
13
|
from ormlambda.statements.types import OrderTypes
|
16
14
|
from ormlambda.sql.types import ColumnType
|
17
15
|
from ormlambda.statements.types import SelectCols
|
18
|
-
from ormlambda.repository
|
16
|
+
from ormlambda.repository import BaseRepository
|
19
17
|
from ormlambda.statements.interfaces import IStatements_two_generic
|
20
|
-
from ormlambda.
|
18
|
+
from ormlambda.statements.types import TypeExists
|
21
19
|
from ormlambda.sql.clause_info import IAggregate
|
22
20
|
from ormlambda.statements.types import WhereTypes
|
23
21
|
|
@@ -43,8 +41,10 @@ from .clauses import Alias
|
|
43
41
|
from ormlambda import Table, Column
|
44
42
|
from ormlambda.common.enums import JoinType
|
45
43
|
from . import functions as func
|
46
|
-
from .
|
44
|
+
from ormlambda.components.join import JoinContext, TupleJoinType
|
45
|
+
|
47
46
|
from ormlambda.common.global_checker import GlobalChecker
|
47
|
+
from .query_builder import QueryBuilder
|
48
48
|
|
49
49
|
|
50
50
|
# COMMENT: It's so important to prevent information generated by other tests from being retained in the class.
|
@@ -62,152 +62,11 @@ def clear_list[T, **P](f: Callable[Concatenate[MySQLStatements, P], T]) -> Calla
|
|
62
62
|
return wrapper
|
63
63
|
|
64
64
|
|
65
|
-
class OrderType(TypedDict):
|
66
|
-
Select: Select
|
67
|
-
JoinSelector: JoinSelector
|
68
|
-
Where: Where
|
69
|
-
Order: Order
|
70
|
-
GroupBy: GroupBy
|
71
|
-
Having: Having
|
72
|
-
Limit: Limit
|
73
|
-
Offset: Offset
|
74
|
-
|
75
|
-
|
76
|
-
class QueryBuilder(IQuery):
|
77
|
-
__order__: tuple[str, ...] = ("Select", "JoinSelector", "Where", "GroupBy", "Having", "Order", "Limit", "Offset")
|
78
|
-
|
79
|
-
def __init__(self, by: JoinType = JoinType.INNER_JOIN):
|
80
|
-
self._context = ClauseInfoContext()
|
81
|
-
self._query_list: OrderType = {}
|
82
|
-
self._by = by
|
83
|
-
|
84
|
-
self._joins: Optional[IQuery] = None
|
85
|
-
self._select: Optional[IQuery] = None
|
86
|
-
self._where: Optional[IQuery] = None
|
87
|
-
self._order: Optional[IQuery] = None
|
88
|
-
self._group_by: Optional[IQuery] = None
|
89
|
-
self._limit: Optional[IQuery] = None
|
90
|
-
self._offset: Optional[IQuery] = None
|
91
|
-
|
92
|
-
def add_statement[T](self, clause: ClauseInfo[T]):
|
93
|
-
self.update_context(clause)
|
94
|
-
self._query_list[type(clause).__name__] = clause
|
95
|
-
|
96
|
-
@property
|
97
|
-
def by(self) -> JoinType:
|
98
|
-
return self._by
|
99
|
-
|
100
|
-
@by.setter
|
101
|
-
def by(self, value: JoinType) -> None:
|
102
|
-
self._by = value
|
103
|
-
|
104
|
-
@property
|
105
|
-
def JOINS(self) -> Optional[set[JoinSelector]]:
|
106
|
-
return self._joins
|
107
|
-
|
108
|
-
@property
|
109
|
-
def SELECT(self) -> IQuery:
|
110
|
-
return self._query_list.get("Select", None)
|
111
|
-
|
112
|
-
@property
|
113
|
-
def WHERE(self) -> IQuery:
|
114
|
-
where = self._query_list.get("Where", None)
|
115
|
-
if not isinstance(where, Iterable):
|
116
|
-
if not where:
|
117
|
-
return ()
|
118
|
-
return (where,)
|
119
|
-
return where
|
120
|
-
|
121
|
-
@property
|
122
|
-
def ORDER(self) -> IQuery:
|
123
|
-
return self._query_list.get("Order", None)
|
124
|
-
|
125
|
-
@property
|
126
|
-
def GROUP_BY(self) -> IQuery:
|
127
|
-
return self._query_list.get("GroupBy", None)
|
128
|
-
|
129
|
-
@property
|
130
|
-
def HAVING(self) -> IQuery:
|
131
|
-
where = self._query_list.get("Having", None)
|
132
|
-
if not isinstance(where, Iterable):
|
133
|
-
if not where:
|
134
|
-
return ()
|
135
|
-
return (where,)
|
136
|
-
return where
|
137
|
-
|
138
|
-
@property
|
139
|
-
def LIMIT(self) -> IQuery:
|
140
|
-
return self._query_list.get("Limit", None)
|
141
|
-
|
142
|
-
@property
|
143
|
-
def OFFSET(self) -> IQuery:
|
144
|
-
return self._query_list.get("Offset", None)
|
145
|
-
|
146
|
-
@property
|
147
|
-
def query(self) -> str:
|
148
|
-
# COMMENT: (select.query, query)We must first create an alias for 'FROM' and then define all the remaining clauses.
|
149
|
-
# This order is mandatory because it adds the clause name to the context when accessing the .query property of 'FROM'
|
150
|
-
|
151
|
-
extract_joins = self.pop_tables_and_create_joins_from_ForeignKey(self._by)
|
152
|
-
|
153
|
-
JOINS = self.stringify_foreign_key(extract_joins, " ")
|
154
|
-
query_list: tuple[str, ...] = (
|
155
|
-
self.SELECT.query,
|
156
|
-
JOINS,
|
157
|
-
Where.join_condition(self.WHERE, True, self._context) if self.WHERE else None,
|
158
|
-
self.GROUP_BY.query if self.GROUP_BY else None,
|
159
|
-
Having.join_condition(self.HAVING, True, self._context) if self.HAVING else None,
|
160
|
-
self.ORDER.query if self.ORDER else None,
|
161
|
-
self.LIMIT.query if self.LIMIT else None,
|
162
|
-
self.OFFSET.query if self.OFFSET else None,
|
163
|
-
)
|
164
|
-
return " ".join([x for x in query_list if x])
|
165
|
-
|
166
|
-
def stringify_foreign_key(self, joins: set[JoinSelector], sep: str = "\n") -> Optional[str]:
|
167
|
-
if not joins:
|
168
|
-
return None
|
169
|
-
sorted_joins = JoinSelector.sort_join_selectors(joins)
|
170
|
-
return f"{sep}".join([join.query for join in sorted_joins])
|
171
|
-
|
172
|
-
def pop_tables_and_create_joins_from_ForeignKey(self, by: JoinType = JoinType.INNER_JOIN) -> set[JoinSelector]:
|
173
|
-
# 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.
|
174
|
-
if not ForeignKey.stored_calls:
|
175
|
-
return None
|
176
|
-
|
177
|
-
joins = set()
|
178
|
-
# Always it's gonna be a set of two
|
179
|
-
# FIXME [x]: Resolved when we get Compare object instead ClauseInfo. For instance, when we have multiples condition using '&' or '|'
|
180
|
-
for fk in ForeignKey.stored_calls.copy():
|
181
|
-
fk = ForeignKey.stored_calls.pop(fk)
|
182
|
-
self._context._add_table_alias(fk.tright, fk.alias)
|
183
|
-
join = JoinSelector(fk.resolved_function(self._context), by, context=self._context, alias=fk.alias)
|
184
|
-
joins.add(join)
|
185
|
-
|
186
|
-
return joins
|
187
|
-
|
188
|
-
def clear(self) -> None:
|
189
|
-
self.__init__()
|
190
|
-
return None
|
191
|
-
|
192
|
-
def update_context(self, clause: ClauseInfo) -> None:
|
193
|
-
if not hasattr(clause, "context"):
|
194
|
-
return None
|
195
|
-
|
196
|
-
if clause.context is not None:
|
197
|
-
self._context.update(clause.context)
|
198
|
-
clause.context = self._context
|
199
|
-
|
200
|
-
|
201
65
|
class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
202
|
-
def __init__(self, model: tuple[T, *Ts], repository:
|
66
|
+
def __init__(self, model: tuple[T, *Ts], repository: BaseRepository) -> None:
|
203
67
|
super().__init__(model, repository=repository)
|
204
68
|
self._query_builder = QueryBuilder()
|
205
69
|
|
206
|
-
@property
|
207
|
-
@override
|
208
|
-
def repository(self) -> IRepositoryBase:
|
209
|
-
return self._repository
|
210
|
-
|
211
70
|
@override
|
212
71
|
def create_table(self, if_exists: TypeExists = "fail") -> None:
|
213
72
|
name: str = self._model.__table_name__
|
@@ -231,10 +90,6 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
231
90
|
self._repository.execute(query)
|
232
91
|
return None
|
233
92
|
|
234
|
-
@override
|
235
|
-
def table_exists(self) -> bool:
|
236
|
-
return self._repository.table_exists(self._model.__table_name__)
|
237
|
-
|
238
93
|
@override
|
239
94
|
@clear_list
|
240
95
|
def insert(self, instances: T | list[T]) -> None:
|
@@ -292,7 +147,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
292
147
|
@override
|
293
148
|
def count[TProp](
|
294
149
|
self,
|
295
|
-
selection: None | SelectCols[T,TProp] = lambda x: "*",
|
150
|
+
selection: None | SelectCols[T, TProp] = lambda x: "*",
|
296
151
|
alias="count",
|
297
152
|
execute: bool = False,
|
298
153
|
) -> Optional[int]:
|
@@ -385,6 +240,9 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
385
240
|
by: JoinType = JoinType.INNER_JOIN,
|
386
241
|
**kwargs,
|
387
242
|
):
|
243
|
+
if "alias" in kwargs:
|
244
|
+
alias = kwargs.pop("alias")
|
245
|
+
kwargs["alias_clause"] = alias
|
388
246
|
select_clause = GlobalChecker.resolved_callback_object(selector, self._models)
|
389
247
|
|
390
248
|
if selector is None:
|
@@ -399,6 +257,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
399
257
|
select = Select[T, *Ts](
|
400
258
|
self._models,
|
401
259
|
columns=select_clause,
|
260
|
+
**kwargs,
|
402
261
|
)
|
403
262
|
self._query_builder.add_statement(select)
|
404
263
|
|
@@ -457,14 +316,18 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
457
316
|
)
|
458
317
|
|
459
318
|
@override
|
460
|
-
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
|
+
|
461
322
|
groupby = GroupBy(column=column, context=self._query_builder._context)
|
462
323
|
# Only can be one LIMIT SQL parameter. We only use the last LimitQuery
|
463
324
|
self._query_builder.add_statement(groupby)
|
464
325
|
return self
|
465
326
|
|
466
327
|
@override
|
467
|
-
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
|
+
|
468
331
|
return Alias(
|
469
332
|
table=column.table,
|
470
333
|
column=column,
|
ormlambda/engine/__init__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
from .create import create_engine # noqa: F401
|
2
|
-
from .url import URL # noqa: F401
|
2
|
+
from .url import URL, make_url # noqa: F401
|
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,14 +4,16 @@ from enum import Enum
|
|
4
4
|
from abc import abstractmethod, ABC
|
5
5
|
|
6
6
|
|
7
|
-
from ormlambda.common.enums import JoinType
|
8
|
-
|
9
7
|
if TYPE_CHECKING:
|
10
8
|
from ormlambda.repository import BaseRepository
|
11
9
|
from ormlambda import Table
|
12
10
|
from ormlambda.sql.clause_info import IAggregate
|
13
11
|
from ormlambda.sql.types import TupleJoinType, ColumnType
|
14
|
-
from ormlambda.
|
12
|
+
from ormlambda.components.join import JoinContext
|
13
|
+
from ormlambda.common.enums import JoinType
|
14
|
+
from ormlambda.sql.clause_info import ClauseInfo
|
15
|
+
from ormlambda.sql.types import AliasType
|
16
|
+
|
15
17
|
|
16
18
|
from ..types import (
|
17
19
|
OrderTypes,
|
@@ -246,33 +248,33 @@ class IStatements[T: Table](ABC):
|
|
246
248
|
# @overload
|
247
249
|
# def select[TFlavour](self, selector: Optional[Callable[[T], tuple]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> TFlavour: ...
|
248
250
|
@overload
|
249
|
-
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, ...]: ...
|
250
252
|
@overload
|
251
|
-
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]]: ...
|
252
254
|
@overload
|
253
|
-
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, ...]: ...
|
254
256
|
|
255
257
|
@abstractmethod
|
256
|
-
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): ...
|
257
259
|
|
258
260
|
# endregion
|
259
261
|
# region select_one
|
260
262
|
@overload
|
261
263
|
def select_one(self) -> T: ...
|
262
264
|
@overload
|
263
|
-
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: ...
|
264
266
|
@overload
|
265
267
|
def select_one[T1](self, selector: SelectorOneType[T, T1 | tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
|
266
268
|
@overload
|
267
269
|
def select_one[*TRes](self, selector: SelectorOneType[T, tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
|
268
270
|
@overload
|
269
|
-
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: ...
|
270
272
|
@overload
|
271
|
-
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: ...
|
272
274
|
@overload
|
273
|
-
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]: ...
|
274
276
|
@overload
|
275
|
-
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: ...
|
276
278
|
@abstractmethod
|
277
279
|
def select_one[TValue, TFlavour, *TRest](
|
278
280
|
self,
|
@@ -292,15 +294,15 @@ class IStatements[T: Table](ABC):
|
|
292
294
|
@overload
|
293
295
|
def first[*TRes](self, selector: SelectorOneType[T, tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
|
294
296
|
@overload
|
295
|
-
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: ...
|
296
298
|
@overload
|
297
|
-
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: ...
|
298
300
|
@overload
|
299
|
-
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: ...
|
300
302
|
@overload
|
301
|
-
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]: ...
|
302
304
|
@overload
|
303
|
-
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: ...
|
304
306
|
@abstractmethod
|
305
307
|
def first[TValue, TFlavour, *TRest](
|
306
308
|
self,
|
@@ -313,13 +315,17 @@ class IStatements[T: Table](ABC):
|
|
313
315
|
# endregion
|
314
316
|
|
315
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]: ...
|
316
322
|
@abstractmethod
|
317
|
-
def groupby[TRepo](self, column:
|
323
|
+
def groupby[TRepo](self, column: list[SelectCols[T, TRepo]] | SelectCols[T, TRepo]) -> IStatements[T]: ...
|
318
324
|
|
319
325
|
# endregion
|
320
326
|
|
321
327
|
@abstractmethod
|
322
|
-
def alias(self, column:
|
328
|
+
def alias[TProp](self, column: SelectCols[T, TProp], alias: AliasType[ClauseInfo[T]]) -> ClauseInfo[T]: ...
|
323
329
|
|
324
330
|
|
325
331
|
class IStatements_two_generic[T, TPool](IStatements[T]):
|