ormlambda 2.11.2__py3-none-any.whl → 3.7.1__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} +114 -74
  66. ormlambda/databases/my_sql/statements.py +249 -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 +331 -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.1.dist-info}/METADATA +132 -68
  99. ormlambda-3.7.1.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.1.dist-info}/LICENSE +0 -0
  119. {ormlambda-2.11.2.dist-info → ormlambda-3.7.1.dist-info}/WHEEL +0 -0
@@ -1,39 +1,43 @@
1
+ from __future__ import annotations
2
+ from ormlambda.sql.clause_info import AggregateFunctionBase
3
+ from ormlambda.sql.clause_info.clause_info_context import ClauseContextType
4
+
5
+ from ormlambda.sql.types import AliasType, ColumnType
6
+
7
+ from ormlambda import Table
8
+
1
9
  import typing as tp
2
10
 
11
+ from ormlambda.sql.types import ASTERISK
12
+
3
13
  if tp.TYPE_CHECKING:
4
14
  from ormlambda import Table
5
- from ormlambda.common.interfaces import IAggregate
6
- from ormlambda.common.abstract_classes.decomposition_query import DecompositionQueryBase, ClauseInfo
7
- from ormlambda import JoinType
15
+ from ormlambda.sql.types import ColumnType, AliasType, TableType
8
16
 
9
17
 
10
- class Count[T: tp.Type[Table]](DecompositionQueryBase[T], IAggregate[T]):
11
- NAME: str = "COUNT"
18
+ class Count[T: Table](AggregateFunctionBase[T]):
19
+ @staticmethod
20
+ def FUNCTION_NAME() -> str:
21
+ return "COUNT"
12
22
 
13
- def __init__(
23
+ def __init__[TProp: Table](
14
24
  self,
15
- table: T,
16
- lambda_query: str | tp.Callable[[T], tuple],
17
- *,
18
- alias: bool = True,
19
- alias_name: str = "count",
20
- by: JoinType = JoinType.INNER_JOIN,
25
+ element: ColumnType[T] | TableType[TProp],
26
+ alias_table: AliasType[ColumnType[TProp]] = None,
27
+ alias_clause: AliasType[ColumnType[TProp]] = "count",
28
+ context: ClauseContextType = None,
29
+ keep_asterisk: bool = True,
30
+ preserve_context: bool = True,
21
31
  ) -> None:
32
+ table = self.extract_table(element)
33
+ column = element if self.is_column(element) else ASTERISK
34
+
22
35
  super().__init__(
23
- table,
24
- lambda_query=lambda_query,
25
- alias=alias,
26
- alias_name=alias_name,
27
- by=by,
28
- replace_asterisk_char=False,
36
+ table=table if (alias_table or (context and table in context._table_context)) else None,
37
+ column=column,
38
+ alias_table=alias_table,
39
+ alias_clause=alias_clause,
40
+ context=context,
41
+ keep_asterisk=keep_asterisk,
42
+ preserve_context=preserve_context,
29
43
  )
30
-
31
- def alias_children_resolver[Tclause: tp.Type[Table]](self, clause_info: ClauseInfo[Tclause]):
32
- if isinstance(clause_info._row_column, IAggregate):
33
- return clause_info._row_column.alias
34
- return None
35
-
36
- @property
37
- def query(self) -> str:
38
- col = ", ".join([x.query for x in self.all_clauses])
39
- return f"{self.NAME}({col})"
@@ -1,15 +1,14 @@
1
1
  from typing import Literal, override
2
2
  from mysql.connector import errorcode, errors
3
- from mysql.connector import MySQLConnection
4
3
 
5
- from ormlambda import IRepositoryBase
4
+ from ormlambda.repository import BaseRepository
6
5
 
7
6
  TypeExists = Literal["fail", "replace", "append"]
8
7
 
9
8
 
10
9
  class CreateDatabase:
11
- def __init__(self, repository: IRepositoryBase[MySQLConnection]) -> None:
12
- self._repository: IRepositoryBase[MySQLConnection] = repository
10
+ def __init__(self, repository: BaseRepository) -> None:
11
+ self._repository: BaseRepository = repository
13
12
 
14
13
  @override
15
14
  @property
@@ -1,16 +1,16 @@
1
- from typing import Any, override, Iterable, TYPE_CHECKING
1
+ from typing import Any, Optional, override, Iterable, TYPE_CHECKING
2
2
 
3
3
  if TYPE_CHECKING:
4
4
  from ormlambda import Column
5
5
 
6
6
  from ormlambda import Table
7
- from ormlambda import IRepositoryBase
7
+ from ormlambda.repository import IRepositoryBase
8
8
  from ormlambda.components.delete import DeleteQueryBase
9
9
  from mysql.connector import MySQLConnection
10
10
 
11
11
 
12
- class DeleteQuery[T: Table](DeleteQueryBase[T, IRepositoryBase[MySQLConnection]]):
13
- def __init__(self, model: T, repository: IRepositoryBase[MySQLConnection]) -> None:
12
+ class DeleteQuery[T: Table](DeleteQueryBase[T, IRepositoryBase]):
13
+ def __init__(self, model: T, repository: IRepositoryBase) -> None:
14
14
  super().__init__(model, repository)
15
15
 
16
16
  @property
@@ -21,18 +21,18 @@ class DeleteQuery[T: Table](DeleteQueryBase[T, IRepositoryBase[MySQLConnection]]
21
21
  def delete(self, instances: T | list[T]) -> None:
22
22
  col: str = ""
23
23
  if isinstance(instances, Table):
24
- pk: Column = instances.get_pk()
25
- if pk.column_value is None:
24
+ pk: Optional[Column] = instances.get_pk()
25
+ if pk is None:
26
26
  raise Exception(f"You cannot use 'DELETE' query without set primary key in '{instances.__table_name__}'")
27
27
  col = pk.column_name
28
- value = str(pk.column_value)
28
+ value = str(instances[pk])
29
29
 
30
30
  elif isinstance(instances, Iterable):
31
31
  value: list[Any] = []
32
32
  for ins in instances:
33
- pk = ins.get_pk()
34
- col = pk.column_name
35
- value.append(pk.column_value)
33
+ pk = type(ins).get_pk()
34
+ value.append(ins[pk])
35
+ col = pk.column_name
36
36
 
37
37
  query: str = f"{self.CLAUSE} FROM {self._model.__table_name__} WHERE {col}"
38
38
  if isinstance(value, str):
@@ -1,13 +1,11 @@
1
1
  from typing import override
2
- from mysql.connector import MySQLConnection
3
2
 
4
-
5
- from ormlambda import IRepositoryBase
3
+ from ormlambda.repository import IRepositoryBase
6
4
 
7
5
 
8
6
  class DropDatabase:
9
- def __init__(self, repository: IRepositoryBase[MySQLConnection]) -> None:
10
- self._repository: IRepositoryBase[MySQLConnection] = repository
7
+ def __init__(self, repository: IRepositoryBase) -> None:
8
+ self._repository: IRepositoryBase = repository
11
9
 
12
10
  @override
13
11
  def execute(self, name: str) -> None:
@@ -1,6 +1,6 @@
1
1
  from typing import Literal, override
2
2
 
3
- from ormlambda import IRepositoryBase
3
+ from ormlambda.repository import IRepositoryBase
4
4
 
5
5
  from mysql.connector import MySQLConnection
6
6
 
@@ -8,8 +8,8 @@ TypeExists = Literal["fail", "replace", "append"]
8
8
 
9
9
 
10
10
  class DropTable:
11
- def __init__(self, repository: IRepositoryBase[MySQLConnection]) -> None:
12
- self._repository: IRepositoryBase[MySQLConnection] = repository
11
+ def __init__(self, repository: IRepositoryBase) -> None:
12
+ self._repository: IRepositoryBase = repository
13
13
 
14
14
  @override
15
15
  def execute(self, name: str = None) -> None:
@@ -1,11 +1,11 @@
1
1
  import typing as tp
2
2
  from ormlambda.common.enums.join_type import JoinType
3
- from ormlambda.common.abstract_classes.decomposition_query import ClauseInfo, DecompositionQueryBase
4
- from ormlambda.common.interfaces.IAggregate import IAggregate
3
+ from ormlambda.sql.clause_info import IAggregate
5
4
  from ormlambda import Table
5
+ from ormlambda.common.abstract_classes.decomposition_query import DecompositionQueryBase
6
6
 
7
7
 
8
- class GroupBy[T: tp.Type[Table], *Ts, TProp](DecompositionQueryBase[T], IAggregate[T]):
8
+ class GroupBy[T: tp.Type[Table], *Ts, TProp](DecompositionQueryBase[T], IAggregate):
9
9
  CLAUSE: str = "GROUP BY"
10
10
 
11
11
  def __init__(
@@ -19,15 +19,12 @@ class GroupBy[T: tp.Type[Table], *Ts, TProp](DecompositionQueryBase[T], IAggrega
19
19
  ) -> None:
20
20
  super().__init__(
21
21
  table,
22
- lambda_query=column,
22
+ columns=column,
23
23
  alias=alias,
24
24
  alias_name=alias_name,
25
25
  by=by,
26
26
  )
27
27
 
28
- def alias_children_resolver[Tclause: tp.Type[Table]](self, clause_info: ClauseInfo[Tclause]):
29
- return None
30
-
31
28
  @property
32
29
  def query(self) -> str:
33
30
  col: str = ", ".join([x.query for x in self.all_clauses])
@@ -1,14 +1,19 @@
1
- from typing import override, Iterable
2
- from mysql.connector import MySQLConnection
1
+ from __future__ import annotations
2
+
3
+ from typing import override, Iterable, TYPE_CHECKING
3
4
 
4
5
  from ormlambda import Table
5
6
  from ormlambda import Column
6
7
  from ormlambda.components.insert import InsertQueryBase
7
- from ormlambda import IRepositoryBase
8
+ from ormlambda.repository import IRepositoryBase
9
+ from ormlambda.caster import Caster
10
+
11
+ if TYPE_CHECKING:
12
+ from ormlambda.databases.my_sql import MySQLRepository
8
13
 
9
14
 
10
- class InsertQuery[T: Table](InsertQueryBase[T, IRepositoryBase[MySQLConnection]]):
11
- def __init__(self, model: T, repository: IRepositoryBase[IRepositoryBase[MySQLConnection]]) -> None:
15
+ class InsertQuery[T: Table](InsertQueryBase[T, IRepositoryBase]):
16
+ def __init__(self, model: T, repository: MySQLRepository) -> None:
12
17
  super().__init__(model, repository)
13
18
 
14
19
  @override
@@ -23,8 +28,10 @@ class InsertQuery[T: Table](InsertQueryBase[T, IRepositoryBase[MySQLConnection]]
23
28
  return self._repository.executemany_with_values(self.query, self._values)
24
29
 
25
30
  @override
26
- def insert(self, instances: T | list[T]) -> None:
27
- valid_cols: list[list[Column]] = []
31
+ def insert[TProp](self, instances: T | list[T]) -> None:
32
+ if not isinstance(instances, Iterable):
33
+ instances = (instances,)
34
+ valid_cols: list[list[Column[TProp]]] = []
28
35
  self.__fill_dict_list(valid_cols, instances)
29
36
 
30
37
  col_names: list[str] = []
@@ -32,11 +39,14 @@ class InsertQuery[T: Table](InsertQueryBase[T, IRepositoryBase[MySQLConnection]]
32
39
  col_values: list[list[str]] = []
33
40
  for i, cols in enumerate(valid_cols):
34
41
  col_values.append([])
42
+ CASTER = Caster(self._repository)
35
43
  for col in cols:
44
+ clean_data = CASTER.for_column(col, instances[i]) # .resolve(instances[i][col])
36
45
  if i == 0:
37
46
  col_names.append(col.column_name)
38
- wildcards.append(col.placeholder)
39
- col_values[-1].append(col.column_value_to_query)
47
+ wildcards.append(clean_data.wildcard_to_insert())
48
+ # COMMENT: avoid MySQLWriteCastBase.resolve when using PLACEHOLDERs
49
+ col_values[-1].append(clean_data.to_database)
40
50
 
41
51
  join_cols = ", ".join(col_names)
42
52
  unknown_rows = f'({", ".join(wildcards)})' # The number of "%s" must match the dict 'dicc_0' length
@@ -46,7 +56,7 @@ class InsertQuery[T: Table](InsertQueryBase[T, IRepositoryBase[MySQLConnection]]
46
56
  return None
47
57
 
48
58
  @staticmethod
49
- def __is_valid(column: Column) -> bool:
59
+ def __is_valid[TProp](column: Column[TProp], value: TProp) -> bool:
50
60
  """
51
61
  We want to delete the column from table when it's specified with an 'AUTO_INCREMENT' or 'AUTO GENERATED ALWAYS AS (__) STORED' statement.
52
62
 
@@ -60,24 +70,28 @@ class InsertQuery[T: Table](InsertQueryBase[T, IRepositoryBase[MySQLConnection]]
60
70
  - False -> Delete the column from dict query
61
71
  """
62
72
 
63
- is_pk_none_and_auto_increment: bool = all([column.column_value is None, column.is_primary_key, column.is_auto_increment])
73
+ is_pk_none_and_auto_increment: bool = all([value is None, column.is_primary_key, column.is_auto_increment])
64
74
 
65
75
  if is_pk_none_and_auto_increment or column.is_auto_generated:
66
76
  return False
67
77
  return True
68
78
 
69
- def __fill_dict_list[TProp](self, list_dict: list[str, TProp], values: T | list[T]) -> list[Column]:
70
- if issubclass(values.__class__, Table):
79
+ def __fill_dict_list[TProp](self, list_dict: list[str, TProp], values: list[T]) -> list[Column]:
80
+ if isinstance(values, Iterable):
81
+ for x in values:
82
+ self.__fill_dict_list(list_dict, x)
83
+
84
+ elif issubclass(values.__class__, Table):
71
85
  new_list = []
72
- for col in values.__dict__.values():
73
- if isinstance(col, Column) and self.__is_valid(col):
74
- new_list.append(col)
86
+ for prop in type(values).__dict__.values():
87
+ if not isinstance(prop, Column):
88
+ continue
75
89
 
90
+ value = getattr(values, prop.column_name)
91
+ if self.__is_valid(prop, value):
92
+ new_list.append(prop)
76
93
  list_dict.append(new_list)
77
94
 
78
- elif isinstance(values, Iterable):
79
- for x in values:
80
- self.__fill_dict_list(list_dict, x)
81
95
  else:
82
96
  raise Exception(f"Tipo de dato'{type(values)}' no esperado")
83
97
  return None
@@ -1,88 +1,71 @@
1
1
  from __future__ import annotations
2
2
  from collections import defaultdict
3
- from typing import override, Callable, overload, Optional, TYPE_CHECKING, Type
3
+ from typing import override, Optional, TYPE_CHECKING, Type
4
4
 
5
5
 
6
6
  from ormlambda.utils.module_tree.dfs_traversal import DFSTraversal
7
+ from ormlambda.common.interfaces.IJoinSelector import IJoinSelector
7
8
  from ormlambda.common.interfaces.IQueryCommand import IQuery
8
- from ormlambda import Disassembler
9
9
  from ormlambda import JoinType
10
+ from ormlambda.sql.clause_info import ClauseInfo
11
+ from ormlambda.sql.comparer import Comparer
12
+ from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext, ClauseContextType
10
13
 
11
14
  # TODOL [x]: Try to import Table module without circular import Error
12
15
  if TYPE_CHECKING:
13
16
  from ormlambda import Table
14
17
 
15
18
 
16
- class JoinSelector[TLeft, TRight](IQuery):
19
+ class JoinSelector[TLeft: Table, TRight: Table](IJoinSelector[TLeft, TRight]):
17
20
  __slots__: tuple = (
21
+ "_comparer",
18
22
  "_orig_table",
19
- "_table_right",
23
+ "_right_table",
20
24
  "_by",
21
25
  "_left_col",
22
26
  "_right_col",
23
27
  "_compareop",
28
+ "_alias",
24
29
  )
25
30
 
26
31
  @override
27
32
  def __repr__(self) -> str:
28
- table_col_left: str = f"{self._orig_table.__table_name__}.{self._left_col}"
29
- table_col_right: str = f"{self._table_right.__table_name__}.{self._right_col}"
33
+ table_col_left: str = f"{self.left_table.table_alias()}.{self._left_col}"
34
+ table_col_right: str = f"{self.right_table.table_alias()}.{self._right_col}"
30
35
 
31
36
  return f"{IQuery.__name__}: {self.__class__.__name__} ({table_col_left} == {table_col_right})"
32
37
 
33
- @overload
34
- def __init__(
38
+ def __init__[LProp, RProp](
35
39
  self,
36
- table_left: TLeft,
37
- table_right: TRight,
38
- col_left: str,
39
- col_right: str,
40
+ where: Comparer[TLeft, LProp, TRight, RProp],
40
41
  by: JoinType,
41
- ) -> None: ...
42
-
43
- @overload
44
- def __init__(
45
- self,
46
- table_left: TLeft,
47
- table_right: TRight,
48
- by: JoinType,
49
- where: Callable[[TLeft, TRight], bool],
50
- ) -> None: ...
51
-
52
- def __init__(
53
- self,
54
- table_left: Table,
55
- table_right: Table,
56
- by: JoinType,
57
- col_left: Optional[str] = None,
58
- col_right: Optional[str] = None,
59
- where: Optional[Callable[[TLeft, TRight], bool]] = None,
42
+ alias: Optional[str] = "{table}",
43
+ context: ClauseContextType = None,
60
44
  ) -> None:
61
- self._orig_table: Table = table_left
62
- self._table_right: Table = table_right
45
+ self._comparer: Comparer[TLeft, LProp, TRight, RProp] = where
46
+ self._orig_table: TLeft = where.left_condition.table
47
+ self._right_table: TRight = where.right_condition.table
63
48
  self._by: JoinType = by
49
+ self._left_col: str = where.left_condition._column.column_name
50
+ self._right_col: str = where.right_condition._column.column_name
51
+ self._compareop = where._compare
52
+ self._context: ClauseContextType = context if context else ClauseInfoContext()
64
53
 
65
- if all(x is None for x in (col_left, col_right, where)):
66
- raise ValueError("You must specify at least 'where' clausule or ('_left_col',_right_col')")
54
+ # COMMENT: When multiple columns reference the same table, we need to create an alias to maintain clear references.
55
+ self._alias: Optional[str] = alias
67
56
 
68
- if where is None:
69
- self._left_col: str = col_left
70
- self._right_col: str = col_right
71
- self._compareop: str = "="
72
- else:
73
- _dis: Disassembler[TLeft, TRight] = Disassembler[TLeft, TRight](where)
74
- self._left_col: str = _dis.cond_1.name
75
- self._right_col: str = _dis.cond_2.name
76
- self._compareop: str = _dis.compare_op
57
+ self._from_clause = ClauseInfo(self.right_table, alias_table=alias, context=self._context)
58
+ self._left_table_clause = ClauseInfo(self.left_table, column=self.left_col, alias_clause=None, context=self._create_partial_context())
59
+ self._right_table_clause = ClauseInfo(self.right_table, column=self.right_col, alias_clause=None, context=self._create_partial_context())
77
60
 
78
- def __eq__(self, __value: "JoinSelector") -> bool:
61
+ def __eq__(self, __value: JoinSelector) -> bool:
79
62
  return isinstance(__value, JoinSelector) and self.__hash__() == __value.__hash__()
80
63
 
81
64
  def __hash__(self) -> int:
82
65
  return hash(
83
66
  (
84
- self._orig_table,
85
- self._table_right,
67
+ self.left_table,
68
+ self.right_table,
86
69
  self._by,
87
70
  self._left_col,
88
71
  self._right_col,
@@ -90,27 +73,51 @@ class JoinSelector[TLeft, TRight](IQuery):
90
73
  )
91
74
  )
92
75
 
76
+ def _create_partial_context(self) -> ClauseInfoContext:
77
+ """
78
+ Only use table_context from global context
79
+ """
80
+ if not self._context:
81
+ return ClauseInfoContext()
82
+ return ClauseInfoContext(clause_context=None, table_context=self._context._table_context)
83
+
93
84
  @classmethod
94
- def join_selectors(cls, *args: "JoinSelector") -> str:
85
+ def join_selectors(cls, *args: JoinSelector) -> str:
95
86
  return "\n".join([x.query for x in args])
96
87
 
97
88
  @property
98
89
  @override
99
90
  def query(self) -> str:
100
- # {inner join} table_name on
101
- # table_name.first col = table_name.second_col
102
-
103
- left_col = f"{self._orig_table.__table_name__}.{self._left_col}"
104
- right_col = f"{self._table_right.__table_name__}.{self._right_col}"
91
+ self._context = ClauseInfoContext(clause_context=None, table_context=self._context._table_context)
105
92
  list_ = [
106
93
  self._by.value, # inner join
107
- self._table_right.__table_name__, # table_name
94
+ self._from_clause.query,
108
95
  "ON",
109
- left_col, # first_col
96
+ self._left_table_clause.query,
110
97
  self._compareop, # =
111
- right_col, # second_col
98
+ self._right_table_clause.query,
112
99
  ]
113
- return " ".join(list_)
100
+ return " ".join([x for x in list_ if x is not None])
101
+
102
+ @property
103
+ def left_table(self) -> TLeft:
104
+ return self._orig_table
105
+
106
+ @property
107
+ def right_table(self) -> TRight:
108
+ return self._right_table
109
+
110
+ @property
111
+ def left_col(self) -> str:
112
+ return self._left_col
113
+
114
+ @property
115
+ def right_col(self) -> str:
116
+ return self._right_col
117
+
118
+ @property
119
+ def alias(self) -> str:
120
+ return self._alias
114
121
 
115
122
  @classmethod
116
123
  def sort_join_selectors(cls, joins: set[JoinSelector]) -> tuple[JoinSelector]:
@@ -121,11 +128,11 @@ class JoinSelector[TLeft, TRight](IQuery):
121
128
  join_object_map: dict[str, list[JoinSelector]] = defaultdict(list)
122
129
 
123
130
  for obj in joins:
124
- join_object_map[obj._orig_table].append(obj)
131
+ join_object_map[obj.left_table].append(obj)
125
132
 
126
133
  graph: dict[Type[Table], list[Type[Table]]] = defaultdict(list)
127
134
  for join in joins:
128
- graph[join._orig_table].append(join._table_right)
135
+ graph[join.left_table].append(join.right_table)
129
136
 
130
137
  sorted_graph = DFSTraversal.sort(graph)[::-1]
131
138
 
@@ -3,7 +3,7 @@ from typing import override
3
3
  from ormlambda.common.interfaces.IQueryCommand import IQuery
4
4
 
5
5
 
6
- class LimitQuery(IQuery):
6
+ class Limit(IQuery):
7
7
  LIMIT = "LIMIT"
8
8
 
9
9
  def __init__(self, number: int) -> None:
@@ -3,7 +3,7 @@ from typing import override
3
3
  from ormlambda.common.interfaces.IQueryCommand import IQuery
4
4
 
5
5
 
6
- class OffsetQuery(IQuery):
6
+ class Offset(IQuery):
7
7
  OFFSET = "OFFSET"
8
8
 
9
9
  def __init__(self, number: int) -> None:
@@ -1,28 +1,36 @@
1
1
  from __future__ import annotations
2
- from typing import override, Callable, TYPE_CHECKING, Any, Iterable
2
+ import typing as tp
3
3
 
4
- from ormlambda.common.abstract_classes.decomposition_query import ClauseInfo
4
+ from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext, ClauseContextType
5
+ from ormlambda.sql.types import ColumnType
6
+ from ormlambda.sql.clause_info import AggregateFunctionBase
5
7
 
8
+ from ormlambda.statements import OrderType
6
9
 
7
- if TYPE_CHECKING:
8
- from ormlambda import Table
9
10
 
10
- from ormlambda.common.abstract_classes.decomposition_query import DecompositionQueryBase
11
- from ormlambda.common.interfaces.IStatements import OrderType
11
+ class Order(AggregateFunctionBase):
12
+ @staticmethod
13
+ def FUNCTION_NAME() -> str:
14
+ return "ORDER BY"
12
15
 
16
+ def __init__[TProp](
17
+ self,
18
+ column: tuple[ColumnType[TProp], ...] | ColumnType[TProp],
19
+ order_type: tp.Iterable[OrderType],
20
+ context: ClauseContextType = None,
21
+ ):
22
+ super().__init__(
23
+ table=None,
24
+ column=column,
25
+ context=context,
26
+ )
13
27
 
14
- class OrderQuery[T: Table](DecompositionQueryBase[T]):
15
- ORDER = "ORDER BY"
16
-
17
- def __init__[*Ts](self, instance: T, lambda_query: Callable[[Any], tuple[*Ts]], order_type: Iterable[OrderType]) -> None:
18
- super().__init__(instance, lambda_query)
19
-
20
- if isinstance(order_type, str) or not isinstance(order_type, Iterable):
28
+ if isinstance(order_type, str) or not isinstance(order_type, tp.Iterable):
21
29
  order_type = (order_type,)
22
30
 
23
31
  self._order_type: list[OrderType] = [self.__cast_to_OrderType(x) for x in order_type]
24
32
 
25
- def __cast_to_OrderType(self, _value: Any) -> Iterable[OrderType]:
33
+ def __cast_to_OrderType(self, _value: tp.Any) -> tp.Iterable[OrderType]:
26
34
  if isinstance(_value, OrderType):
27
35
  return _value
28
36
 
@@ -33,15 +41,20 @@ class OrderQuery[T: Table](DecompositionQueryBase[T]):
33
41
  pass
34
42
  raise Exception(f"order_type param only can be 'ASC' or 'DESC' string or '{OrderType.__name__}' enum")
35
43
 
36
- def alias_children_resolver[Tclause](self, clause_info: ClauseInfo[Tclause]):
37
- return None
38
-
39
- @override
44
+ @tp.override
40
45
  @property
41
46
  def query(self) -> str:
42
- assert len(self.all_clauses) == len(self._order_type)
47
+ string_columns: list[str] = []
48
+ columns = self.unresolved_column
49
+
50
+ # if this attr is not iterable means that we only pass one column without wrapped in a list or tuple
51
+ if not isinstance(columns, tp.Iterable):
52
+ columns = (columns,)
53
+ assert len(columns) == len(self._order_type)
54
+
55
+ context = ClauseInfoContext(table_context=self._context._table_context, clause_context=None) if self._context else None
56
+ for index, clause in enumerate(self._convert_into_clauseInfo(columns, context)):
57
+ clause.alias_clause = None
58
+ string_columns.append(f"{clause.query} {self._order_type[index].value}")
43
59
 
44
- query: list[str] = []
45
- for index, x in enumerate(self.all_clauses):
46
- query.append(f"{x.query} {self._order_type[index].value}")
47
- return f"{self.ORDER} {", ".join(query)}"
60
+ return f"{self.FUNCTION_NAME()} {', '.join(string_columns)}"