ormlambda 2.11.2__py3-none-any.whl → 3.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ormlambda/__init__.py +11 -9
- ormlambda/caster/__init__.py +3 -0
- ormlambda/caster/base_caster.py +69 -0
- ormlambda/caster/caster.py +48 -0
- ormlambda/caster/interfaces/ICaster.py +26 -0
- ormlambda/caster/interfaces/__init__.py +1 -0
- ormlambda/common/__init__.py +1 -1
- ormlambda/common/abstract_classes/__init__.py +3 -3
- ormlambda/common/abstract_classes/decomposition_query.py +117 -319
- ormlambda/common/abstract_classes/non_query_base.py +1 -1
- ormlambda/common/enums/condition_types.py +2 -1
- ormlambda/common/enums/join_type.py +4 -1
- ormlambda/common/errors/__init__.py +15 -2
- ormlambda/common/global_checker.py +28 -0
- ormlambda/common/interfaces/ICustomAlias.py +4 -1
- ormlambda/common/interfaces/IDecompositionQuery.py +9 -34
- ormlambda/common/interfaces/IJoinSelector.py +21 -0
- ormlambda/common/interfaces/__init__.py +4 -6
- ormlambda/components/__init__.py +4 -0
- ormlambda/components/insert/abstract_insert.py +1 -1
- ormlambda/components/select/ISelect.py +17 -0
- ormlambda/components/select/__init__.py +1 -0
- ormlambda/components/update/abstract_update.py +4 -4
- ormlambda/components/upsert/abstract_upsert.py +1 -1
- ormlambda/databases/__init__.py +5 -0
- ormlambda/databases/my_sql/__init__.py +3 -1
- ormlambda/databases/my_sql/caster/__init__.py +1 -0
- ormlambda/databases/my_sql/caster/caster.py +38 -0
- ormlambda/databases/my_sql/caster/read.py +39 -0
- ormlambda/databases/my_sql/caster/types/__init__.py +8 -0
- ormlambda/databases/my_sql/caster/types/bytes.py +31 -0
- ormlambda/databases/my_sql/caster/types/datetime.py +34 -0
- ormlambda/databases/my_sql/caster/types/float.py +31 -0
- ormlambda/databases/my_sql/caster/types/int.py +31 -0
- ormlambda/databases/my_sql/caster/types/iterable.py +31 -0
- ormlambda/databases/my_sql/caster/types/none.py +30 -0
- ormlambda/databases/my_sql/caster/types/point.py +43 -0
- ormlambda/databases/my_sql/caster/types/string.py +31 -0
- ormlambda/databases/my_sql/caster/write.py +37 -0
- ormlambda/databases/my_sql/clauses/ST_AsText.py +36 -0
- ormlambda/databases/my_sql/clauses/ST_Contains.py +31 -0
- ormlambda/databases/my_sql/clauses/__init__.py +6 -4
- ormlambda/databases/my_sql/clauses/alias.py +24 -21
- ormlambda/databases/my_sql/clauses/count.py +32 -28
- ormlambda/databases/my_sql/clauses/create_database.py +3 -4
- ormlambda/databases/my_sql/clauses/delete.py +10 -10
- ormlambda/databases/my_sql/clauses/drop_database.py +3 -5
- ormlambda/databases/my_sql/clauses/drop_table.py +3 -3
- ormlambda/databases/my_sql/clauses/group_by.py +4 -7
- ormlambda/databases/my_sql/clauses/insert.py +33 -19
- ormlambda/databases/my_sql/clauses/joins.py +66 -59
- ormlambda/databases/my_sql/clauses/limit.py +1 -1
- ormlambda/databases/my_sql/clauses/offset.py +1 -1
- ormlambda/databases/my_sql/clauses/order.py +36 -23
- ormlambda/databases/my_sql/clauses/select.py +25 -36
- ormlambda/databases/my_sql/clauses/update.py +38 -13
- ormlambda/databases/my_sql/clauses/upsert.py +2 -2
- ormlambda/databases/my_sql/clauses/where.py +45 -0
- ormlambda/databases/my_sql/functions/concat.py +24 -27
- ormlambda/databases/my_sql/functions/max.py +32 -28
- ormlambda/databases/my_sql/functions/min.py +32 -28
- ormlambda/databases/my_sql/functions/sum.py +32 -28
- ormlambda/databases/my_sql/join_context.py +75 -0
- ormlambda/databases/my_sql/repository/__init__.py +1 -0
- ormlambda/databases/my_sql/{repository.py → repository/repository.py} +104 -73
- ormlambda/databases/my_sql/statements.py +231 -153
- ormlambda/engine/__init__.py +0 -0
- ormlambda/engine/template.py +47 -0
- ormlambda/model/__init__.py +0 -0
- ormlambda/model/base_model.py +37 -0
- ormlambda/repository/__init__.py +2 -0
- ormlambda/repository/base_repository.py +14 -0
- ormlambda/repository/interfaces/IDatabaseConnection.py +12 -0
- ormlambda/{common → repository}/interfaces/IRepositoryBase.py +6 -5
- ormlambda/repository/interfaces/__init__.py +2 -0
- ormlambda/sql/__init__.py +3 -0
- ormlambda/sql/clause_info/__init__.py +3 -0
- ormlambda/sql/clause_info/clause_info.py +434 -0
- ormlambda/sql/clause_info/clause_info_context.py +87 -0
- ormlambda/sql/clause_info/interface/IAggregate.py +10 -0
- ormlambda/sql/clause_info/interface/__init__.py +1 -0
- ormlambda/sql/column.py +126 -0
- ormlambda/sql/comparer.py +156 -0
- ormlambda/sql/foreign_key.py +115 -0
- ormlambda/sql/interfaces/__init__.py +0 -0
- ormlambda/sql/table/__init__.py +1 -0
- ormlambda/{utils → sql/table}/fields.py +6 -5
- ormlambda/{utils → sql/table}/table_constructor.py +43 -91
- ormlambda/sql/types.py +25 -0
- ormlambda/statements/__init__.py +2 -0
- ormlambda/statements/base_statement.py +129 -0
- ormlambda/statements/interfaces/IStatements.py +309 -0
- ormlambda/statements/interfaces/__init__.py +1 -0
- ormlambda/statements/types.py +51 -0
- ormlambda/utils/__init__.py +1 -3
- ormlambda/utils/module_tree/__init__.py +1 -0
- ormlambda/utils/module_tree/dynamic_module.py +20 -14
- {ormlambda-2.11.2.dist-info → ormlambda-3.7.0.dist-info}/METADATA +132 -68
- ormlambda-3.7.0.dist-info/RECORD +117 -0
- ormlambda/common/abstract_classes/abstract_model.py +0 -115
- ormlambda/common/interfaces/IAggregate.py +0 -10
- ormlambda/common/interfaces/IStatements.py +0 -348
- ormlambda/components/where/__init__.py +0 -1
- ormlambda/components/where/abstract_where.py +0 -15
- ormlambda/databases/my_sql/clauses/where_condition.py +0 -222
- ormlambda/model_base.py +0 -36
- ormlambda/utils/column.py +0 -105
- ormlambda/utils/foreign_key.py +0 -81
- ormlambda/utils/lambda_disassembler/__init__.py +0 -4
- ormlambda/utils/lambda_disassembler/dis_types.py +0 -133
- ormlambda/utils/lambda_disassembler/disassembler.py +0 -69
- ormlambda/utils/lambda_disassembler/dtypes.py +0 -103
- ormlambda/utils/lambda_disassembler/name_of.py +0 -41
- ormlambda/utils/lambda_disassembler/nested_element.py +0 -44
- ormlambda/utils/lambda_disassembler/tree_instruction.py +0 -145
- ormlambda-2.11.2.dist-info/RECORD +0 -81
- /ormlambda/{utils → sql}/dtypes.py +0 -0
- {ormlambda-2.11.2.dist-info → ormlambda-3.7.0.dist-info}/LICENSE +0 -0
- {ormlambda-2.11.2.dist-info → ormlambda-3.7.0.dist-info}/WHEEL +0 -0
@@ -1,61 +1,50 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import override, Type, Callable, TYPE_CHECKING
|
2
|
+
from typing import Optional, override, Type, Callable, TYPE_CHECKING
|
3
3
|
|
4
|
+
from ormlambda.sql.clause_info import ClauseInfo
|
5
|
+
from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext
|
6
|
+
from ormlambda.sql.types import AliasType
|
4
7
|
from ormlambda.common.abstract_classes.decomposition_query import DecompositionQueryBase
|
5
|
-
from ormlambda.
|
6
|
-
from ormlambda.common.interfaces.IAggregate import IAggregate
|
7
|
-
import shapely as shp
|
8
|
-
|
8
|
+
from ormlambda.components import ISelect
|
9
9
|
|
10
10
|
if TYPE_CHECKING:
|
11
11
|
from ormlambda import Table
|
12
|
-
from .joins import JoinSelector
|
13
12
|
|
14
13
|
|
15
|
-
class Select[T: Type[Table], *Ts](DecompositionQueryBase[T, *Ts]):
|
14
|
+
class Select[T: Type[Table], *Ts](DecompositionQueryBase[T, *Ts], ISelect):
|
16
15
|
CLAUSE: str = "SELECT"
|
17
16
|
|
18
17
|
def __init__(
|
19
18
|
self,
|
20
19
|
tables: tuple[T, *Ts],
|
21
|
-
|
20
|
+
columns: Callable[[T], tuple] = lambda x: x,
|
22
21
|
*,
|
23
|
-
|
24
|
-
|
25
|
-
joins: Optional[list[JoinSelector]] = None,
|
26
|
-
by: JoinType = JoinType.INNER_JOIN,
|
22
|
+
alias_table: AliasType[ClauseInfo] = "{table}",
|
23
|
+
context: Optional[ClauseInfoContext] = None,
|
27
24
|
) -> None:
|
28
25
|
super().__init__(
|
29
26
|
tables,
|
30
|
-
|
31
|
-
|
32
|
-
alias_name=alias_name,
|
33
|
-
by=by,
|
34
|
-
joins=joins,
|
27
|
+
columns,
|
28
|
+
context=context,
|
35
29
|
)
|
30
|
+
self._alias_table = alias_table
|
31
|
+
# We always need to add the self alias of the Select
|
32
|
+
self._context._add_table_alias(self.table, self._alias_table)
|
33
|
+
|
34
|
+
@property
|
35
|
+
def FROM(self) -> ClauseInfo[T]:
|
36
|
+
return ClauseInfo(self.table, None, alias_table=self._alias_table, context=self._context)
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
@property
|
39
|
+
def COLUMNS(self) -> str:
|
40
|
+
return ClauseInfo.join_clauses(self._all_clauses, ",", self.context)
|
40
41
|
|
41
42
|
# TODOL: see who to deal when we will have to add more mysql methods
|
42
43
|
@override
|
43
44
|
@property
|
44
45
|
def query(self) -> str:
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
cols.append(x.concat_with_alias(f"ST_AsText({self.table.__table_name__}.{x.column})"))
|
49
|
-
else:
|
50
|
-
cols.append(x.query)
|
51
|
-
|
52
|
-
if isinstance(x._row_column, IAggregate) and x._row_column.has_foreign_keys:
|
53
|
-
self._joins.update(x._row_column.fk_relationship)
|
54
|
-
|
55
|
-
col: str = ", ".join(cols)
|
56
|
-
query: str = f"{self.CLAUSE} {col} FROM {self.table.__table_name__}"
|
57
|
-
|
58
|
-
if self.has_foreign_keys:
|
59
|
-
query += " " + self.stringify_foreign_key(" ")
|
46
|
+
# COMMENT: (select.query, query)We must first create an alias for 'FROM' and then define all the remaining clauses.
|
47
|
+
# This order is mandatory because it adds the clause name to the context when accessing the .query property of 'FROM'
|
48
|
+
FROM = self.FROM
|
60
49
|
|
61
|
-
return query
|
50
|
+
return f"{self.CLAUSE} {self.COLUMNS} FROM {FROM.query}"
|
@@ -1,14 +1,27 @@
|
|
1
1
|
from typing import Type, override, Any
|
2
|
-
from mysql.connector import MySQLConnection
|
3
2
|
|
4
3
|
from ormlambda.components.update import UpdateQueryBase
|
5
4
|
from ormlambda import Table, Column
|
6
|
-
from ormlambda import IRepositoryBase
|
7
|
-
from .
|
5
|
+
from ormlambda.repository import IRepositoryBase
|
6
|
+
from ormlambda.caster.caster import Caster
|
7
|
+
from .where import Where
|
8
|
+
from ormlambda.sql.types import ColumnType
|
8
9
|
|
9
10
|
|
10
|
-
class
|
11
|
-
def __init__(self,
|
11
|
+
class UpdateKeyError(KeyError):
|
12
|
+
def __init__(self, table: Type[Table], key: str | ColumnType, *args):
|
13
|
+
super().__init__(*args)
|
14
|
+
self._table: Type[Table] = table
|
15
|
+
self._key: str | ColumnType = key
|
16
|
+
|
17
|
+
def __str__(self):
|
18
|
+
if isinstance(self._key, Column):
|
19
|
+
return f"The column '{self._key.column_name}' does not belong to the table '{self._table.__table_name__}'; it belongs to the table '{self._key.table.__table_name__}'. Please check the columns in the query."
|
20
|
+
return f"The column '{self._key}' does not belong to the table '{self._table.__table_name__}'. Please check the columns in the query."
|
21
|
+
|
22
|
+
|
23
|
+
class UpdateQuery[T: Type[Table]](UpdateQueryBase[T, IRepositoryBase]):
|
24
|
+
def __init__(self, model: T, repository: Any, where: list[Where]) -> None:
|
12
25
|
super().__init__(model, repository, where)
|
13
26
|
|
14
27
|
@override
|
@@ -19,28 +32,40 @@ class UpdateQuery[T: Type[Table]](UpdateQueryBase[T, IRepositoryBase[MySQLConnec
|
|
19
32
|
@override
|
20
33
|
def execute(self) -> None:
|
21
34
|
if self._where:
|
22
|
-
|
35
|
+
for where in self._where:
|
36
|
+
query_with_table = where.query
|
37
|
+
for x in where._comparer:
|
38
|
+
# TODOH []: Refactor this part. We need to get only the columns withouth __table_name__ preffix
|
39
|
+
self._query += " " + query_with_table.replace(x.left_condition.table.__table_name__ + ".", "")
|
23
40
|
return self._repository.execute_with_values(self._query, self._values)
|
24
41
|
|
25
42
|
@override
|
26
|
-
def update(self, dicc:
|
43
|
+
def update[TProp](self, dicc: dict[str | ColumnType[TProp], Any]) -> None:
|
27
44
|
if not isinstance(dicc, dict):
|
28
45
|
raise TypeError
|
29
46
|
|
30
|
-
|
31
|
-
|
47
|
+
col_names: list[Column] = []
|
48
|
+
CASTER = Caster(self._repository)
|
32
49
|
for col, value in dicc.items():
|
33
|
-
|
50
|
+
if isinstance(col, str):
|
51
|
+
if not hasattr(self._model, col):
|
52
|
+
raise UpdateKeyError(self._model, col)
|
53
|
+
col = getattr(self._model, col)
|
54
|
+
if not isinstance(col, Column):
|
55
|
+
raise ValueError
|
34
56
|
|
35
57
|
if self.__is_valid__(col):
|
36
|
-
|
37
|
-
|
58
|
+
clean_data = CASTER.for_value(value)
|
59
|
+
col_names.append((col.column_name,clean_data.wildcard_to_insert()))
|
60
|
+
self._values.append(clean_data.to_database)
|
38
61
|
|
39
|
-
set_query: str = ",".join(["=".join(
|
62
|
+
set_query: str = ",".join(["=".join(col_data) for col_data in col_names])
|
40
63
|
|
41
64
|
self._query = f"{self.CLAUSE} {self._model.__table_name__} SET {set_query}"
|
42
65
|
self._values = tuple(self._values)
|
43
66
|
return None
|
44
67
|
|
45
68
|
def __is_valid__(self, col: Column) -> bool:
|
69
|
+
if self._model is not col.table:
|
70
|
+
raise UpdateKeyError(self._model, col)
|
46
71
|
return not col.is_auto_generated
|
@@ -2,13 +2,13 @@ from typing import override, Any
|
|
2
2
|
|
3
3
|
from ormlambda import Table
|
4
4
|
from ormlambda.components.upsert import UpsertQueryBase
|
5
|
-
from ormlambda import IRepositoryBase
|
5
|
+
from ormlambda.repository import IRepositoryBase
|
6
6
|
from mysql.connector import MySQLConnection
|
7
7
|
|
8
8
|
from .insert import InsertQuery
|
9
9
|
|
10
10
|
|
11
|
-
class UpsertQuery[T: Table](UpsertQueryBase[T, IRepositoryBase
|
11
|
+
class UpsertQuery[T: Table](UpsertQueryBase[T, IRepositoryBase]):
|
12
12
|
def __init__(self, model: T, repository: Any) -> None:
|
13
13
|
super().__init__(model, repository)
|
14
14
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
import typing as tp
|
3
|
+
from ormlambda.sql.comparer import Comparer
|
4
|
+
from ormlambda.sql.clause_info import AggregateFunctionBase
|
5
|
+
from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext, ClauseContextType
|
6
|
+
|
7
|
+
|
8
|
+
class Where(AggregateFunctionBase):
|
9
|
+
"""
|
10
|
+
The purpose of this class is to create 'WHERE' condition queries properly.
|
11
|
+
"""
|
12
|
+
|
13
|
+
def __init__(self, *comparer: Comparer, restrictive: bool = True, context: ClauseContextType = None) -> None:
|
14
|
+
self._comparer: tuple[Comparer] = comparer
|
15
|
+
self._restrictive: bool = restrictive
|
16
|
+
self._context: ClauseContextType = context if context else ClauseInfoContext()
|
17
|
+
|
18
|
+
@staticmethod
|
19
|
+
def FUNCTION_NAME() -> str:
|
20
|
+
return "WHERE"
|
21
|
+
|
22
|
+
@property
|
23
|
+
def query(self) -> str:
|
24
|
+
if isinstance(self._comparer, tp.Iterable):
|
25
|
+
context = ClauseInfoContext(table_context=self._context._table_context)
|
26
|
+
comparer = Comparer.join_comparers(self._comparer, restrictive=self._restrictive, context=context)
|
27
|
+
else:
|
28
|
+
comparer = self._comparer
|
29
|
+
return f"{self.FUNCTION_NAME()} {comparer}"
|
30
|
+
|
31
|
+
@property
|
32
|
+
def alias_clause(self) -> None:
|
33
|
+
return None
|
34
|
+
|
35
|
+
@staticmethod
|
36
|
+
def join_condition(wheres: tp.Iterable[Where], restrictive: bool, context: ClauseInfoContext) -> str:
|
37
|
+
if not isinstance(wheres, tp.Iterable):
|
38
|
+
wheres = (wheres,)
|
39
|
+
|
40
|
+
comparers: list[Comparer] = []
|
41
|
+
for where in wheres:
|
42
|
+
for c in where._comparer:
|
43
|
+
c.set_context(context)
|
44
|
+
comparers.append(c)
|
45
|
+
return Where(*comparers, restrictive=restrictive, context=context).query
|
@@ -1,41 +1,38 @@
|
|
1
|
-
from ormlambda.
|
2
|
-
from ormlambda.
|
1
|
+
from ormlambda.sql.clause_info import AggregateFunctionBase
|
2
|
+
from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext, ClauseContextType
|
3
3
|
|
4
4
|
|
5
5
|
import typing as tp
|
6
|
-
from ormlambda.
|
6
|
+
from ormlambda.sql.types import ColumnType, AliasType
|
7
|
+
from ormlambda.sql.clause_info import ClauseInfo
|
7
8
|
|
8
|
-
if tp.TYPE_CHECKING:
|
9
|
-
from ormlambda import Table
|
10
9
|
|
10
|
+
class Concat[*Ts](AggregateFunctionBase):
|
11
|
+
@staticmethod
|
12
|
+
def FUNCTION_NAME() -> str:
|
13
|
+
return "CONCAT"
|
11
14
|
|
12
|
-
|
13
|
-
CLAUSE = "CONCAT"
|
14
|
-
|
15
|
-
def __init__[*Ts](
|
15
|
+
def __init__[TProp](
|
16
16
|
self,
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
alias: bool = True,
|
21
|
-
alias_name: str = "CONCAT",
|
22
|
-
by: JoinType = JoinType.INNER_JOIN,
|
17
|
+
values: ColumnType[Ts] | tuple[ColumnType[Ts], ...],
|
18
|
+
alias_clause: AliasType[ColumnType[TProp]] = "concat",
|
19
|
+
context: ClauseContextType = None,
|
23
20
|
) -> None:
|
24
21
|
super().__init__(
|
25
|
-
table,
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
by=by,
|
22
|
+
table=None,
|
23
|
+
column=values,
|
24
|
+
alias_clause=alias_clause,
|
25
|
+
context=context,
|
30
26
|
)
|
31
27
|
|
32
|
-
|
33
|
-
if isinstance(clause_info._row_column, IAggregate):
|
34
|
-
return clause_info._row_column.alias
|
35
|
-
return None
|
36
|
-
|
28
|
+
@tp.override
|
37
29
|
@property
|
38
30
|
def query(self) -> str:
|
39
|
-
|
31
|
+
columns: list[str] = []
|
32
|
+
|
33
|
+
context = ClauseInfoContext(table_context=self._context._table_context, clause_context=None) if self._context else None
|
40
34
|
|
41
|
-
|
35
|
+
for clause in self._convert_into_clauseInfo(self.unresolved_column, context=context):
|
36
|
+
clause.alias_clause = None
|
37
|
+
columns.append(clause)
|
38
|
+
return self._concat_alias_and_column(f"{self.FUNCTION_NAME()}({ClauseInfo.join_clauses(columns)})", self._alias_aggregate)
|
@@ -1,39 +1,43 @@
|
|
1
|
-
from
|
1
|
+
from __future__ import annotations
|
2
2
|
import typing as tp
|
3
3
|
|
4
|
-
from ormlambda.
|
4
|
+
from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext, ClauseContextType
|
5
|
+
from ormlambda.sql.clause_info import ClauseInfo
|
6
|
+
from ormlambda.sql.types import ColumnType, AliasType
|
7
|
+
from ormlambda.sql.clause_info import AggregateFunctionBase
|
5
8
|
|
6
|
-
if tp.TYPE_CHECKING:
|
7
|
-
from ormlambda import Table
|
8
9
|
|
10
|
+
class Max(AggregateFunctionBase[None]):
|
11
|
+
@staticmethod
|
12
|
+
def FUNCTION_NAME() -> str:
|
13
|
+
return "MAX"
|
9
14
|
|
10
|
-
|
11
|
-
NAME: str = "MAX"
|
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 = "max") -> None: ...
|
15
|
-
|
16
|
-
def __init__(
|
15
|
+
def __init__[TProp](
|
17
16
|
self,
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
alias_name: str = "max",
|
23
|
-
) -> None:
|
17
|
+
elements: tuple[ColumnType[TProp], ...] | ColumnType[TProp],
|
18
|
+
alias_clause: AliasType[ColumnType[TProp]] = "max",
|
19
|
+
context: ClauseContextType = None,
|
20
|
+
):
|
24
21
|
super().__init__(
|
25
|
-
table,
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
table=None,
|
23
|
+
column=elements,
|
24
|
+
alias_table=None,
|
25
|
+
alias_clause=alias_clause,
|
26
|
+
context=context,
|
27
|
+
keep_asterisk=False,
|
28
|
+
preserve_context=False,
|
29
29
|
)
|
30
30
|
|
31
|
-
|
32
|
-
if isinstance(clause_info._row_column, IAggregate):
|
33
|
-
return clause_info._row_column.alias
|
34
|
-
return None
|
35
|
-
|
31
|
+
@tp.override
|
36
32
|
@property
|
37
33
|
def query(self) -> str:
|
38
|
-
|
39
|
-
|
34
|
+
columns: list[str] = []
|
35
|
+
|
36
|
+
context = ClauseInfoContext(table_context=self._context._table_context, clause_context=None) if self._context else None
|
37
|
+
for clause in self._convert_into_clauseInfo(self.unresolved_column, context):
|
38
|
+
new_clause = clause
|
39
|
+
new_clause.alias_clause = None
|
40
|
+
columns.append(new_clause)
|
41
|
+
|
42
|
+
method_string = f"{self.FUNCTION_NAME()}({ClauseInfo.join_clauses(columns)})"
|
43
|
+
return self._concat_alias_and_column(method_string, self._alias_aggregate)
|
@@ -1,39 +1,43 @@
|
|
1
|
-
from
|
1
|
+
from __future__ import annotations
|
2
2
|
import typing as tp
|
3
3
|
|
4
|
-
from ormlambda.
|
4
|
+
from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext, ClauseContextType
|
5
|
+
from ormlambda.sql.clause_info import ClauseInfo
|
6
|
+
from ormlambda.sql.types import ColumnType, AliasType
|
7
|
+
from ormlambda.sql.clause_info import AggregateFunctionBase
|
5
8
|
|
6
|
-
if tp.TYPE_CHECKING:
|
7
|
-
from ormlambda import Table
|
8
9
|
|
10
|
+
class Min(AggregateFunctionBase):
|
11
|
+
@staticmethod
|
12
|
+
def FUNCTION_NAME() -> str:
|
13
|
+
return "MIN"
|
9
14
|
|
10
|
-
|
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__(
|
15
|
+
def __init__[TProp](
|
17
16
|
self,
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
alias_name: str = "min",
|
23
|
-
) -> None:
|
17
|
+
elements: tuple[ColumnType[TProp], ...] | ColumnType[TProp],
|
18
|
+
alias_clause: AliasType[ColumnType[TProp]] = "min",
|
19
|
+
context: ClauseContextType = None,
|
20
|
+
):
|
24
21
|
super().__init__(
|
25
|
-
table,
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
table=None,
|
23
|
+
column=elements,
|
24
|
+
alias_table=None,
|
25
|
+
alias_clause=alias_clause,
|
26
|
+
context=context,
|
27
|
+
keep_asterisk=False,
|
28
|
+
preserve_context=False,
|
29
29
|
)
|
30
30
|
|
31
|
-
|
32
|
-
if isinstance(clause_info._row_column, IAggregate):
|
33
|
-
return clause_info._row_column.alias
|
34
|
-
return None
|
35
|
-
|
31
|
+
@tp.override
|
36
32
|
@property
|
37
33
|
def query(self) -> str:
|
38
|
-
|
39
|
-
|
34
|
+
columns: list[str] = []
|
35
|
+
|
36
|
+
context = ClauseInfoContext(table_context=self._context._table_context, clause_context=None) if self._context else None
|
37
|
+
for clause in self._convert_into_clauseInfo(self.unresolved_column, context):
|
38
|
+
new_clause = clause
|
39
|
+
new_clause.alias_clause = None
|
40
|
+
columns.append(new_clause)
|
41
|
+
|
42
|
+
method_string = f"{self.FUNCTION_NAME()}({ClauseInfo.join_clauses(columns)})"
|
43
|
+
return self._concat_alias_and_column(method_string, self._alias_aggregate)
|
@@ -1,39 +1,43 @@
|
|
1
|
-
from
|
1
|
+
from __future__ import annotations
|
2
2
|
import typing as tp
|
3
3
|
|
4
|
-
from ormlambda.
|
4
|
+
from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext, ClauseContextType
|
5
|
+
from ormlambda.sql.clause_info import ClauseInfo
|
6
|
+
from ormlambda.sql.types import ColumnType, AliasType
|
7
|
+
from ormlambda.sql.clause_info import AggregateFunctionBase
|
5
8
|
|
6
|
-
if tp.TYPE_CHECKING:
|
7
|
-
from ormlambda import Table
|
8
9
|
|
10
|
+
class Sum(AggregateFunctionBase):
|
11
|
+
@staticmethod
|
12
|
+
def FUNCTION_NAME() -> str:
|
13
|
+
return "SUM"
|
9
14
|
|
10
|
-
|
11
|
-
NAME: str = "SUM"
|
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 = ...) -> None: ...
|
15
|
-
|
16
|
-
def __init__(
|
15
|
+
def __init__[TProp](
|
17
16
|
self,
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
alias_name: str = "sum",
|
23
|
-
) -> None:
|
17
|
+
elements: tuple[ColumnType[TProp], ...] | ColumnType[TProp],
|
18
|
+
alias_clause: AliasType[ColumnType[TProp]] = "sum",
|
19
|
+
context: ClauseContextType = None,
|
20
|
+
):
|
24
21
|
super().__init__(
|
25
|
-
table,
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
table=None,
|
23
|
+
column=elements,
|
24
|
+
alias_table=None,
|
25
|
+
alias_clause=alias_clause,
|
26
|
+
context=context,
|
27
|
+
keep_asterisk=False,
|
28
|
+
preserve_context=False,
|
29
29
|
)
|
30
30
|
|
31
|
-
|
32
|
-
if isinstance(clause_info._row_column, IAggregate):
|
33
|
-
return clause_info._row_column.alias
|
34
|
-
return None
|
35
|
-
|
31
|
+
@tp.override
|
36
32
|
@property
|
37
33
|
def query(self) -> str:
|
38
|
-
|
39
|
-
|
34
|
+
columns: list[str] = []
|
35
|
+
|
36
|
+
context = ClauseInfoContext(table_context=self._context._table_context, clause_context=None) if self._context else None
|
37
|
+
for clause in self._convert_into_clauseInfo(self.unresolved_column, context):
|
38
|
+
new_clause = clause
|
39
|
+
new_clause.alias_clause = None
|
40
|
+
columns.append(new_clause)
|
41
|
+
|
42
|
+
method_string = f"{self.FUNCTION_NAME()}({ClauseInfo.join_clauses(columns)})"
|
43
|
+
return self._concat_alias_and_column(method_string, self._alias_aggregate)
|
@@ -0,0 +1,75 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Any, Iterable, TYPE_CHECKING
|
3
|
+
|
4
|
+
from ormlambda import ForeignKey
|
5
|
+
from ormlambda.sql.comparer import Comparer
|
6
|
+
|
7
|
+
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
from ormlambda.statements.interfaces.IStatements import IStatements_two_generic
|
10
|
+
from ormlambda.sql.clause_info import ClauseInfo
|
11
|
+
from ormlambda import Table
|
12
|
+
from ormlambda.sql.clause_info.clause_info_context import ClauseContextType
|
13
|
+
from ormlambda.common.enums.join_type import JoinType
|
14
|
+
|
15
|
+
|
16
|
+
type TupleJoinType[LTable: Table, LProp, RTable: Table, RProp] = tuple[Comparer[LTable, LProp, RTable, RProp], JoinType]
|
17
|
+
|
18
|
+
|
19
|
+
class JoinContext[TParent: Table, TRepo]:
|
20
|
+
def __init__(self, statements: IStatements_two_generic[TParent, TRepo], joins: tuple, context: ClauseContextType) -> None:
|
21
|
+
self._statements = statements
|
22
|
+
self._parent: TParent = statements.model
|
23
|
+
self._joins: Iterable[tuple[Comparer, JoinType]] = joins
|
24
|
+
self._context: ClauseContextType = context
|
25
|
+
|
26
|
+
def __enter__(self) -> IStatements_two_generic[TParent, TRepo]:
|
27
|
+
for comparer, by in self._joins:
|
28
|
+
fk_clause, alias = self.get_fk_clause(comparer)
|
29
|
+
|
30
|
+
foreign_key: ForeignKey = ForeignKey(comparer=comparer, clause_name=alias)
|
31
|
+
fk_clause.alias_table = foreign_key.alias
|
32
|
+
self._context.add_clause_to_context(fk_clause)
|
33
|
+
setattr(self._parent, alias, foreign_key)
|
34
|
+
|
35
|
+
# TODOH []: We need to preserve the 'foreign_key' variable while inside the 'with' clause.
|
36
|
+
# Keep in mind that 'ForeignKey.stored_calls' is cleared every time we call methods like
|
37
|
+
# .select(), .select_one(), .insert(), .update(), or .count(). This means we only retain
|
38
|
+
# the context from the first call of any of these methods.
|
39
|
+
ForeignKey.stored_calls.add(foreign_key)
|
40
|
+
|
41
|
+
return self
|
42
|
+
|
43
|
+
def __exit__(self, type: type, error: Any, traceback: str):
|
44
|
+
if error:
|
45
|
+
raise error
|
46
|
+
|
47
|
+
for comparer, _ in self._joins:
|
48
|
+
_, attribute = self.get_fk_clause(comparer)
|
49
|
+
fk: ForeignKey = getattr(self._parent, attribute)
|
50
|
+
delattr(self._parent, attribute)
|
51
|
+
del self._context._table_context[fk.tright]
|
52
|
+
return None
|
53
|
+
|
54
|
+
def __getattr__(self, name: str) -> TParent:
|
55
|
+
return getattr(self._parent, name)
|
56
|
+
|
57
|
+
def get_fk_clause(self, comparer: Comparer) -> tuple[ClauseInfo, str]:
|
58
|
+
"""
|
59
|
+
In this method the 'comparer' attributes always should be a one by one comparison.
|
60
|
+
Not a combining of Comparers like when using () & () | () ... operator
|
61
|
+
|
62
|
+
>>> A.fk_b == B.pk_b # Correct
|
63
|
+
>>> (A.fk_b == B.pk_b) & (B.fk_c == C.pk_c) # Incorrect
|
64
|
+
"""
|
65
|
+
clause_dicc: dict[Table, ClauseInfo] = {
|
66
|
+
comparer.left_condition.table: comparer.left_condition,
|
67
|
+
comparer.right_condition.table: comparer.right_condition,
|
68
|
+
}
|
69
|
+
conditions = set([comparer.left_condition.table, comparer.right_condition.table])
|
70
|
+
model = set([self._statements.model])
|
71
|
+
|
72
|
+
parent_table = conditions.difference(model).pop()
|
73
|
+
|
74
|
+
return clause_dicc[parent_table], clause_dicc[parent_table].table.__name__
|
75
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
from .repository import MySQLRepository # noqa: F401
|