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.
Files changed (119) hide show
  1. ormlambda/__init__.py +11 -9
  2. ormlambda/caster/__init__.py +3 -0
  3. ormlambda/caster/base_caster.py +69 -0
  4. ormlambda/caster/caster.py +48 -0
  5. ormlambda/caster/interfaces/ICaster.py +26 -0
  6. ormlambda/caster/interfaces/__init__.py +1 -0
  7. ormlambda/common/__init__.py +1 -1
  8. ormlambda/common/abstract_classes/__init__.py +3 -3
  9. ormlambda/common/abstract_classes/decomposition_query.py +117 -319
  10. ormlambda/common/abstract_classes/non_query_base.py +1 -1
  11. ormlambda/common/enums/condition_types.py +2 -1
  12. ormlambda/common/enums/join_type.py +4 -1
  13. ormlambda/common/errors/__init__.py +15 -2
  14. ormlambda/common/global_checker.py +28 -0
  15. ormlambda/common/interfaces/ICustomAlias.py +4 -1
  16. ormlambda/common/interfaces/IDecompositionQuery.py +9 -34
  17. ormlambda/common/interfaces/IJoinSelector.py +21 -0
  18. ormlambda/common/interfaces/__init__.py +4 -6
  19. ormlambda/components/__init__.py +4 -0
  20. ormlambda/components/insert/abstract_insert.py +1 -1
  21. ormlambda/components/select/ISelect.py +17 -0
  22. ormlambda/components/select/__init__.py +1 -0
  23. ormlambda/components/update/abstract_update.py +4 -4
  24. ormlambda/components/upsert/abstract_upsert.py +1 -1
  25. ormlambda/databases/__init__.py +5 -0
  26. ormlambda/databases/my_sql/__init__.py +3 -1
  27. ormlambda/databases/my_sql/caster/__init__.py +1 -0
  28. ormlambda/databases/my_sql/caster/caster.py +38 -0
  29. ormlambda/databases/my_sql/caster/read.py +39 -0
  30. ormlambda/databases/my_sql/caster/types/__init__.py +8 -0
  31. ormlambda/databases/my_sql/caster/types/bytes.py +31 -0
  32. ormlambda/databases/my_sql/caster/types/datetime.py +34 -0
  33. ormlambda/databases/my_sql/caster/types/float.py +31 -0
  34. ormlambda/databases/my_sql/caster/types/int.py +31 -0
  35. ormlambda/databases/my_sql/caster/types/iterable.py +31 -0
  36. ormlambda/databases/my_sql/caster/types/none.py +30 -0
  37. ormlambda/databases/my_sql/caster/types/point.py +43 -0
  38. ormlambda/databases/my_sql/caster/types/string.py +31 -0
  39. ormlambda/databases/my_sql/caster/write.py +37 -0
  40. ormlambda/databases/my_sql/clauses/ST_AsText.py +36 -0
  41. ormlambda/databases/my_sql/clauses/ST_Contains.py +31 -0
  42. ormlambda/databases/my_sql/clauses/__init__.py +6 -4
  43. ormlambda/databases/my_sql/clauses/alias.py +24 -21
  44. ormlambda/databases/my_sql/clauses/count.py +32 -28
  45. ormlambda/databases/my_sql/clauses/create_database.py +3 -4
  46. ormlambda/databases/my_sql/clauses/delete.py +10 -10
  47. ormlambda/databases/my_sql/clauses/drop_database.py +3 -5
  48. ormlambda/databases/my_sql/clauses/drop_table.py +3 -3
  49. ormlambda/databases/my_sql/clauses/group_by.py +4 -7
  50. ormlambda/databases/my_sql/clauses/insert.py +33 -19
  51. ormlambda/databases/my_sql/clauses/joins.py +66 -59
  52. ormlambda/databases/my_sql/clauses/limit.py +1 -1
  53. ormlambda/databases/my_sql/clauses/offset.py +1 -1
  54. ormlambda/databases/my_sql/clauses/order.py +36 -23
  55. ormlambda/databases/my_sql/clauses/select.py +25 -36
  56. ormlambda/databases/my_sql/clauses/update.py +38 -13
  57. ormlambda/databases/my_sql/clauses/upsert.py +2 -2
  58. ormlambda/databases/my_sql/clauses/where.py +45 -0
  59. ormlambda/databases/my_sql/functions/concat.py +24 -27
  60. ormlambda/databases/my_sql/functions/max.py +32 -28
  61. ormlambda/databases/my_sql/functions/min.py +32 -28
  62. ormlambda/databases/my_sql/functions/sum.py +32 -28
  63. ormlambda/databases/my_sql/join_context.py +75 -0
  64. ormlambda/databases/my_sql/repository/__init__.py +1 -0
  65. ormlambda/databases/my_sql/{repository.py → repository/repository.py} +104 -73
  66. ormlambda/databases/my_sql/statements.py +231 -153
  67. ormlambda/engine/__init__.py +0 -0
  68. ormlambda/engine/template.py +47 -0
  69. ormlambda/model/__init__.py +0 -0
  70. ormlambda/model/base_model.py +37 -0
  71. ormlambda/repository/__init__.py +2 -0
  72. ormlambda/repository/base_repository.py +14 -0
  73. ormlambda/repository/interfaces/IDatabaseConnection.py +12 -0
  74. ormlambda/{common → repository}/interfaces/IRepositoryBase.py +6 -5
  75. ormlambda/repository/interfaces/__init__.py +2 -0
  76. ormlambda/sql/__init__.py +3 -0
  77. ormlambda/sql/clause_info/__init__.py +3 -0
  78. ormlambda/sql/clause_info/clause_info.py +434 -0
  79. ormlambda/sql/clause_info/clause_info_context.py +87 -0
  80. ormlambda/sql/clause_info/interface/IAggregate.py +10 -0
  81. ormlambda/sql/clause_info/interface/__init__.py +1 -0
  82. ormlambda/sql/column.py +126 -0
  83. ormlambda/sql/comparer.py +156 -0
  84. ormlambda/sql/foreign_key.py +115 -0
  85. ormlambda/sql/interfaces/__init__.py +0 -0
  86. ormlambda/sql/table/__init__.py +1 -0
  87. ormlambda/{utils → sql/table}/fields.py +6 -5
  88. ormlambda/{utils → sql/table}/table_constructor.py +43 -91
  89. ormlambda/sql/types.py +25 -0
  90. ormlambda/statements/__init__.py +2 -0
  91. ormlambda/statements/base_statement.py +129 -0
  92. ormlambda/statements/interfaces/IStatements.py +309 -0
  93. ormlambda/statements/interfaces/__init__.py +1 -0
  94. ormlambda/statements/types.py +51 -0
  95. ormlambda/utils/__init__.py +1 -3
  96. ormlambda/utils/module_tree/__init__.py +1 -0
  97. ormlambda/utils/module_tree/dynamic_module.py +20 -14
  98. {ormlambda-2.11.2.dist-info → ormlambda-3.7.0.dist-info}/METADATA +132 -68
  99. ormlambda-3.7.0.dist-info/RECORD +117 -0
  100. ormlambda/common/abstract_classes/abstract_model.py +0 -115
  101. ormlambda/common/interfaces/IAggregate.py +0 -10
  102. ormlambda/common/interfaces/IStatements.py +0 -348
  103. ormlambda/components/where/__init__.py +0 -1
  104. ormlambda/components/where/abstract_where.py +0 -15
  105. ormlambda/databases/my_sql/clauses/where_condition.py +0 -222
  106. ormlambda/model_base.py +0 -36
  107. ormlambda/utils/column.py +0 -105
  108. ormlambda/utils/foreign_key.py +0 -81
  109. ormlambda/utils/lambda_disassembler/__init__.py +0 -4
  110. ormlambda/utils/lambda_disassembler/dis_types.py +0 -133
  111. ormlambda/utils/lambda_disassembler/disassembler.py +0 -69
  112. ormlambda/utils/lambda_disassembler/dtypes.py +0 -103
  113. ormlambda/utils/lambda_disassembler/name_of.py +0 -41
  114. ormlambda/utils/lambda_disassembler/nested_element.py +0 -44
  115. ormlambda/utils/lambda_disassembler/tree_instruction.py +0 -145
  116. ormlambda-2.11.2.dist-info/RECORD +0 -81
  117. /ormlambda/{utils → sql}/dtypes.py +0 -0
  118. {ormlambda-2.11.2.dist-info → ormlambda-3.7.0.dist-info}/LICENSE +0 -0
  119. {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, Optional
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.common.enums.join_type import JoinType
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
- lambda_query: Callable[[T], tuple] = lambda x: x,
20
+ columns: Callable[[T], tuple] = lambda x: x,
22
21
  *,
23
- alias: bool = False,
24
- alias_name: str | None = None,
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
- lambda_query,
31
- alias=alias,
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
- # @classmethod
38
- # def alias_children_resolver[Tclause: Type[Table]](self, clause_info: ClauseInfo[Tclause]):
39
- # return f"{clause.table.__table_name__}_{name}"
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
- cols: list[str] = []
46
- for x in self.all_clauses:
47
- if x.dtype is shp.Point:
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 .where_condition import WhereCondition
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 UpdateQuery[T: Type[Table]](UpdateQueryBase[T, IRepositoryBase[MySQLConnection]]):
11
- def __init__(self, model: T, repository: Any, where: list[WhereCondition]) -> None:
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
- self._query += " " + WhereCondition.join_condition(*self._where)
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: Any | dict[str | property, Any]) -> None:
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
- name_cols: list[Column] = []
31
-
47
+ col_names: list[Column] = []
48
+ CASTER = Caster(self._repository)
32
49
  for col, value in dicc.items():
33
- col: Column = self._model.get_column(col, value)
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
- name_cols.append(col)
37
- self._values.append(col.column_value_to_query)
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([col.column_name, col.placeholder]) for col in name_cols])
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[MySQLConnection]]):
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.common.interfaces.IAggregate import IAggregate
2
- from ormlambda.common.abstract_classes.decomposition_query import DecompositionQueryBase, ClauseInfo
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.common.enums.join_type import JoinType
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
- class Concat[T: tp.Type[Table]](DecompositionQueryBase[T], IAggregate[T]):
13
- CLAUSE = "CONCAT"
14
-
15
- def __init__[*Ts](
15
+ def __init__[TProp](
16
16
  self,
17
- table: T,
18
- lambda_query: str | tp.Callable[[T], tuple[*Ts]],
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
- lambda_query,
27
- alias=alias,
28
- alias_name=alias_name,
29
- by=by,
22
+ table=None,
23
+ column=values,
24
+ alias_clause=alias_clause,
25
+ context=context,
30
26
  )
31
27
 
32
- def alias_children_resolver[Tclause: tp.Type[Table]](self, clause_info: ClauseInfo[Tclause]):
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
- col: str = ", ".join([x.query for x in self.all_clauses])
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
- return f"{self.CLAUSE}({col})"
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 ormlambda.common.interfaces import IAggregate
1
+ from __future__ import annotations
2
2
  import typing as tp
3
3
 
4
- from ormlambda.common.abstract_classes.decomposition_query import DecompositionQueryBase, ClauseInfo
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
- class Max[T: tp.Type[Table]](DecompositionQueryBase[T], IAggregate[T]):
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
- table: T,
19
- column: str | tp.Callable[[T], tuple],
20
- *,
21
- alias: bool = True,
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
- lambda_query=column,
27
- alias=alias,
28
- alias_name=alias_name,
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
- def alias_children_resolver[Tclause: tp.Type[Table]](self, clause_info: ClauseInfo[Tclause]):
32
- if isinstance(clause_info._row_column, IAggregate):
33
- return clause_info._row_column.alias
34
- return None
35
-
31
+ @tp.override
36
32
  @property
37
33
  def query(self) -> str:
38
- col = ", ".join([x.query for x in self.all_clauses])
39
- return f"{self.NAME}({col})"
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 ormlambda.common.interfaces import IAggregate
1
+ from __future__ import annotations
2
2
  import typing as tp
3
3
 
4
- from ormlambda.common.abstract_classes.decomposition_query import DecompositionQueryBase, ClauseInfo
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
- class Min[T: tp.Type[Table]](DecompositionQueryBase[T], IAggregate[T]):
11
- NAME: str = "MIN"
12
-
13
- @tp.overload
14
- def __init__[T: tp.Type[Table]](self, table: T, column: tp.Callable[[T], tp.Any], *, alias: bool = True, alias_name: str = "min") -> None: ...
15
-
16
- def __init__(
15
+ def __init__[TProp](
17
16
  self,
18
- table: T,
19
- column: str | tp.Callable[[T], tuple],
20
- *,
21
- alias: bool = True,
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
- lambda_query=column,
27
- alias=alias,
28
- alias_name=alias_name,
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
- def alias_children_resolver[Tclause: tp.Type[Table]](self, clause_info: ClauseInfo[Tclause]):
32
- if isinstance(clause_info._row_column, IAggregate):
33
- return clause_info._row_column.alias
34
- return None
35
-
31
+ @tp.override
36
32
  @property
37
33
  def query(self) -> str:
38
- col = ", ".join([x.query for x in self.all_clauses])
39
- return f"{self.NAME}({col})"
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 ormlambda.common.interfaces import IAggregate
1
+ from __future__ import annotations
2
2
  import typing as tp
3
3
 
4
- from ormlambda.common.abstract_classes.decomposition_query import DecompositionQueryBase, ClauseInfo
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
- class Sum[T: tp.Type[Table]](DecompositionQueryBase[T], IAggregate[T]):
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
- table: T,
19
- column: str | tp.Callable[[T], tuple],
20
- *,
21
- alias: bool = True,
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
- lambda_query=column,
27
- alias=alias,
28
- alias_name=alias_name,
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
- def alias_children_resolver[Tclause: tp.Type[Table]](self, clause_info: ClauseInfo[Tclause]):
32
- if isinstance(clause_info._row_column, IAggregate):
33
- return clause_info._row_column.alias
34
- return None
35
-
31
+ @tp.override
36
32
  @property
37
33
  def query(self) -> str:
38
- col = ", ".join([x.query for x in self.all_clauses])
39
- return f"{self.NAME}({col})"
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