ormlambda 2.11.1__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 -315
  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.1.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.1.dist-info/RECORD +0 -81
  117. /ormlambda/{utils → sql}/dtypes.py +0 -0
  118. {ormlambda-2.11.1.dist-info → ormlambda-3.7.0.dist-info}/LICENSE +0 -0
  119. {ormlambda-2.11.1.dist-info → ormlambda-3.7.0.dist-info}/WHEEL +0 -0
@@ -1,322 +1,160 @@
1
1
  from __future__ import annotations
2
- from collections import defaultdict
3
2
  import typing as tp
4
- import inspect
5
3
  import abc
6
- from ormlambda import Table
4
+ from ormlambda import Table, Column
7
5
 
8
- from ormlambda.utils.lambda_disassembler.tree_instruction import TreeInstruction, TupleInstruction, NestedElement
9
- from ormlambda.common.interfaces import IAggregate, IDecompositionQuery, ICustomAlias
10
- from ormlambda.common.interfaces.IDecompositionQuery import IDecompositionQuery_one_arg
11
- from ormlambda import JoinType, ForeignKey
12
- from ormlambda.databases.my_sql.clauses.joins import JoinSelector
6
+ from ormlambda.common.interfaces import IDecompositionQuery, ICustomAlias
7
+ from ormlambda.sql.clause_info import IAggregate
8
+ from ormlambda.sql.clause_info import ClauseInfo, AggregateFunctionBase
9
+ from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext, ClauseContextType
10
+ from ormlambda import ForeignKey
11
+ from ormlambda.common.global_checker import GlobalChecker
13
12
 
14
- from ..errors import UnmatchedLambdaParameterError
13
+ from ormlambda.sql.types import AliasType, TableType, ColumnType
15
14
 
16
- type ClauseDataType = property | str | ICustomAlias
17
- type AliasType[T] = tp.Type[Table] | tp.Callable[[tp.Type[Table]], T]
18
15
 
16
+ type TableTupleType[T, *Ts] = tuple[T:TableType, *Ts]
19
17
  type ValueType = tp.Union[
20
- property,
21
- IAggregate,
22
- Table,
23
- str,
24
- ICustomAlias,
18
+ tp.Type[IAggregate],
19
+ tp.Type[Table],
20
+ tp.Type[str],
21
+ tp.Type[ICustomAlias],
25
22
  ]
26
23
 
27
24
 
28
- class ClauseInfo[T: tp.Type[Table]](IDecompositionQuery_one_arg[T]):
29
- @tp.overload
30
- def __init__(self, table: T, column: property, alias_children_resolver: tp.Callable[..., str]): ...
31
- @tp.overload
32
- def __init__(self, table: T, column: str, alias_children_resolver: tp.Callable[..., str]): ...
33
-
34
- def __init__(self, table: T, column: ClauseDataType, alias_children_resolver: tp.Callable[[DecompositionQueryBase[T], str], str]):
35
- self._table: T = table
36
- self._row_column: ClauseDataType = column
37
- self._column: str = self._resolve_column(column)
38
- self._alias_children_resolver: tp.Callable[[DecompositionQueryBase[T], str], str] = alias_children_resolver
39
- self._alias: tp.Optional[str] = self._alias_children_resolver(self)
40
-
41
- self._query: str = self.__create_value_string(self._column)
42
-
43
- def __repr__(self) -> str:
44
- return f"{ClauseInfo.__name__}: {self.query}"
45
-
46
- @property
47
- def table(self) -> T:
48
- return self._table
49
-
50
- @property
51
- def column(self) -> ClauseDataType:
52
- return self._column
53
-
54
- @property
55
- def alias(self) -> str:
56
- return self._alias
57
-
58
- @property
59
- def query(self) -> str:
60
- return self._query
61
-
62
- @property
63
- def dtype[TProp](self) -> tp.Optional[tp.Type[TProp]]:
64
- try:
65
- return self._table.get_column(self.column).dtype
66
- except ValueError:
67
- return None
68
-
69
- def _resolve_column(self, data: ClauseDataType) -> str:
70
- if isinstance(data, property):
71
- return self._table.__properties_mapped__[data]
72
-
73
- elif isinstance(data, IAggregate):
74
- return data.alias_name
75
-
76
- elif isinstance(data, str):
77
- # TODOL: refactor to base the condition in dict with '*' as key. '*' must to work as special character
78
- return f"'{data}'" if data != DecompositionQueryBase.CHAR else data
25
+ class ClauseInfoConverter[T, TProp](abc.ABC):
26
+ @classmethod
27
+ @abc.abstractmethod
28
+ def convert(cls, data: T, alias_table: AliasType[ColumnType[TProp]] = "{table}", context: ClauseContextType = None) -> list[ClauseInfo[T]]: ...
29
+
30
+
31
+ class ConvertFromAnyType(ClauseInfoConverter[None, None]):
32
+ @classmethod
33
+ def convert(cls, data: tp.Any, alias_table: AliasType = "{table}", context: ClauseContextType = None) -> list[ClauseInfo[None]]:
34
+ return [
35
+ ClauseInfo[None](
36
+ table=None,
37
+ column=data,
38
+ alias_table=alias_table,
39
+ alias_clause=None,
40
+ context=context,
41
+ )
42
+ ]
79
43
 
80
- elif isinstance(data, ICustomAlias):
81
- return data.all_clauses[0].column
82
44
 
83
- else:
84
- raise NotImplementedError(f"type of value '{type(data)}' is not implemented.")
45
+ class ConvertFromForeignKey[LT: Table, RT: Table](ClauseInfoConverter[RT, None]):
46
+ @classmethod
47
+ def convert(cls, data: ForeignKey[LT, RT], alias_table=None, context: ClauseContextType = None) -> list[ClauseInfo[RT]]:
48
+ return ConvertFromTable[RT].convert(data.tright, data.alias, context)
85
49
 
86
- def __create_value_string(self, name: str) -> str:
87
- if isinstance(self._row_column, property):
88
- return self.concat_with_alias(f"{self._table.__table_name__}.{name}")
89
50
 
90
- if isinstance(self._row_column, IAggregate):
91
- return self.concat_with_alias(self._row_column.query)
51
+ class ConvertFromColumn[TProp](ClauseInfoConverter[None, TProp]):
52
+ @classmethod
53
+ def convert(cls, data: ColumnType[TProp], alias_table: AliasType[ColumnType[TProp]] = "{table}", context: ClauseContextType = None) -> list[ClauseInfo[None]]:
54
+ # COMMENT: if the property belongs to the main class, the columnn name in not prefixed. This only done if the property comes from any join.
55
+ clause_info = ClauseInfo[None](
56
+ table=data.table,
57
+ column=data,
58
+ alias_table=alias_table,
59
+ alias_clause="{table}_{column}",
60
+ context=context,
61
+ )
62
+ return [clause_info]
92
63
 
93
- return self.concat_with_alias(self.column)
94
64
 
95
- def concat_with_alias(self, column_name: str) -> str:
96
- if not self._alias:
97
- return column_name
98
- return f"{column_name} as `{self._alias}`"
65
+ class ConvertFromIAggregate(ClauseInfoConverter[None, None]):
66
+ @classmethod
67
+ def convert(cls, data: AggregateFunctionBase, alias_table=None, context: ClauseContextType = None) -> list[ClauseInfo[None]]:
68
+ return [data]
99
69
 
100
70
 
101
- class DecompositionQueryBase[T: tp.Type[Table], *Ts](IDecompositionQuery[T, *Ts]):
102
- CHAR: str = "*"
71
+ class ConvertFromTable[T: Table](ClauseInfoConverter[T, None]):
72
+ @classmethod
73
+ def convert(cls, data: T, alias_table: AliasType[ColumnType] = "{table}", context: ClauseContextType = None) -> list[ClauseInfo[T]]:
74
+ """
75
+ if the data is Table, means that we want to retrieve all columns
76
+ """
77
+ return cls._extract_all_clauses(data, alias_table, context)
103
78
 
104
79
  @staticmethod
105
- def _asterik_resolver(table: tp.Type[Table]):
106
- return table
80
+ def _extract_all_clauses(table: TableType[T], alias_table: AliasType[ColumnType], context: ClauseContextType = None) -> list[ClauseInfo[TableType[T]]]:
81
+ # all columns
82
+ column_clauses = []
83
+ for column in table.get_columns():
84
+ column_clauses.extend(ConvertFromColumn.convert(column, alias_table=alias_table, context=context))
85
+ return column_clauses
107
86
 
87
+
88
+ class DecompositionQueryBase[T: Table, *Ts](IDecompositionQuery[T, *Ts]):
108
89
  @tp.overload
109
- def __init__(self, tables: T, lambda_query: tp.Callable[[T], tuple]) -> None: ...
110
- @tp.overload
111
- def __init__(self, tables: T, lambda_query: tp.Callable[[T], tuple], *, alias: bool = ...) -> None: ...
112
- @tp.overload
113
- def __init__(self, tables: T, lambda_query: tp.Callable[[T], tuple], *, alias: bool = ..., alias_name: tp.Optional[str] = ...) -> None: ...
114
- @tp.overload
115
- def __init__(self, tables: T, lambda_query: tp.Callable[[T], tuple], *, alias: bool = ..., alias_name: tp.Optional[str] = ..., by: JoinType = ...) -> None: ...
90
+ def __init__(self, tables: tuple[TableType[T]], columns: tuple[ColumnType]) -> None: ...
116
91
  @tp.overload
117
- def __init__(self, tables: T, lambda_query: tp.Callable[[T], tuple], *, alias: bool = ..., alias_name: tp.Optional[str] = ..., by: JoinType = ..., replace_asterisk_char: bool = ...) -> None: ...
92
+ def __init__(self, tables: tuple[TableType[T]], columns: tuple[ColumnType], context: ClauseContextType = ...) -> None: ...
118
93
  @tp.overload
119
- def __init__(self, tables: T, lambda_query: tp.Callable[[T], tuple], *, alias: bool = ..., alias_name: tp.Optional[str] = ..., by: JoinType = ..., replace_asterisk_char: bool = ..., joins: tp.Optional[list[JoinSelector]] = ...) -> None: ...
120
-
121
- def __init__(
122
- self,
123
- tables: tuple[T, *Ts],
124
- lambda_query: tp.Callable[[T], tuple[*Ts]],
125
- *,
126
- alias: bool = True,
127
- alias_name: tp.Optional[str] = None,
128
- by: JoinType = JoinType.INNER_JOIN,
129
- replace_asterisk_char: bool = True,
130
- joins: tp.Optional[list[JoinType]] = None,
131
- ) -> None:
132
- self._tables: tuple[T, *Ts] = tables if isinstance(tables, tp.Iterable) else (tables,)
133
- self._lambda_query: tp.Callable[[T], tuple] = lambda_query
134
- self._alias: bool = alias
135
- self._alias_name: tp.Optional[str] = alias_name
136
- self._by: JoinType = by
137
- self._joins: set[JoinSelector] = set(joins) if joins is not None else set()
138
-
139
- self._clauses_group_by_tables: dict[tp.Type[Table], list[ClauseInfo[T]]] = defaultdict(list)
140
- self._all_clauses: list[ClauseInfo] = []
141
- self.alias_cache: dict[str, AliasType] = {self.CHAR: self._asterik_resolver}
142
- self._replace_asterisk_char: bool = replace_asterisk_char
143
- self.__assign_lambda_variables_to_table(lambda_query)
144
-
145
- self.__clauses_list_generetor(lambda_query)
146
-
147
- def __getitem__(self, key: str) -> ClauseInfo:
148
- for clause in self._all_clauses:
149
- if clause.alias == key:
150
- return clause
151
-
152
- def alias_children_resolver[Tclause: tp.Type[Table]](self, clause_info: ClauseInfo[Tclause]):
153
- DEFAULT_ALIAS: str = f"{clause_info._table.__table_name__}_{clause_info._column}"
94
+ def __init__(self, tables: tuple[TableType[T]], columns: tuple[ColumnType], alias_table: str, context: ClauseContextType = ...) -> None: ...
154
95
 
155
- if isinstance(clause_info._row_column, IAggregate):
156
- return clause_info._row_column.alias_name
157
- return DEFAULT_ALIAS
96
+ def __init__(self, tables: tuple[TableType[T]], columns: tuple[ColumnType], alias_table: str = "{table}", *, context: ClauseContextType = ClauseInfoContext()) -> None:
97
+ self._tables: tuple[TableType[T]] = tables if isinstance(tables, tp.Iterable) else (tables,)
158
98
 
159
- def __assign_lambda_variables_to_table(self, _lambda: tp.Callable[[T], None]) -> None:
160
- """
161
- return a dictionary with the lambda's parameters as keys and Type[Table] as the values
99
+ self._columns: tp.Callable[[T], tuple] = columns
100
+ self._all_clauses: list[ClauseInfo | AggregateFunctionBase] = []
101
+ self._context: ClauseContextType = context if context else ClauseInfoContext()
102
+ self._alias_table: str = alias_table
103
+ self.__clauses_list_generetor()
162
104
 
105
+ def __getitem__(self, key: str) -> ClauseInfo | AggregateFunctionBase:
106
+ for clause in self._all_clauses:
107
+ is_agg_function = isinstance(clause, AggregateFunctionBase)
108
+ is_clause_info = isinstance(clause, ClauseInfo)
109
+ if (is_agg_function and clause._alias_aggregate == key) or (is_clause_info and clause.alias_clause == key):
110
+ return clause
163
111
 
164
- >>> res = _assign_lambda_variables_to_table(lambda a,ci,co: ...)
165
- >>> print(res)
166
- >>> # {
167
- >>> # "a": Address,
168
- >>> # "ci": City,
169
- >>> # "co": Country,
170
- >>> # }
171
- """
172
- lambda_vars = tuple(inspect.signature(_lambda).parameters)
173
-
174
- if len(lambda_vars) != (expected := len(self._tables)):
175
- raise UnmatchedLambdaParameterError(expected, found_param=lambda_vars)
176
-
177
- # COMMENT: We don't pass a lambda method because lambda reads the las value of 'i'
178
- for i, param in enumerate(lambda_vars):
179
- self.alias_cache[param] = self._tables[i]
180
- return None
181
-
182
- def __clauses_list_generetor(self, function: tp.Callable[[T], tp.Any]) -> None:
183
- if not callable(function):
184
- return None
112
+ def __clauses_list_generetor(self) -> None:
113
+ # Clean self._all_clauses if we update the context
114
+ self._all_clauses.clear() if self._all_clauses else None
185
115
 
186
- resolved_function = function(*self._tables)
116
+ resolved_function = GlobalChecker.resolved_callback_object(self._columns, self.tables)
187
117
 
188
- tree_list = TreeInstruction(function).to_list()
189
118
  # Python treats string objects as iterable, so we need to prevent this behavior
190
119
  if isinstance(resolved_function, str) or not isinstance(resolved_function, tp.Iterable):
191
- resolved_function = (resolved_function,)
192
-
193
- for col_index, data in enumerate(resolved_function):
194
- ti = tree_list[col_index] if tree_list else TupleInstruction(self.CHAR, NestedElement(self.CHAR))
195
-
196
- values: ClauseInfo | list[ClauseInfo] = self.__identify_value_type(data, ti)
120
+ resolved_function = (self.table,) if ClauseInfo.is_asterisk(resolved_function) else (resolved_function,)
197
121
 
198
- if isinstance(values, tp.Iterable):
199
- [self.__add_clause(x) for x in values]
122
+ for data in resolved_function:
123
+ if not isinstance(data, ClauseInfo):
124
+ values = self.__convert_into_ClauseInfo(data)
200
125
  else:
201
- self.__add_clause(values)
126
+ values = [data]
127
+ self.__add_clause(values)
202
128
 
203
129
  return None
204
130
 
205
- def __identify_value_type[TProp](self, data: TProp, tuple_instruction: TupleInstruction) -> ClauseInfo[T]:
131
+ def __convert_into_ClauseInfo[TProp](self, data: TProp) -> list[ClauseInfo[T]]:
206
132
  """
207
133
  A method that behaves based on the variable's type
208
134
  """
209
135
 
210
- def validation(data: TProp, type_: ValueType):
211
- is_table: bool = isinstance(data, type) and issubclass(data, type_)
212
- return any(
213
- [
214
- isinstance(data, type_),
215
- is_table,
216
- ]
217
- )
136
+ def validation(data: TProp, type_: ValueType) -> bool:
137
+ is_valid_type: bool = isinstance(data, type) and issubclass(data, type_)
138
+ return isinstance(data, type_) or is_valid_type
218
139
 
219
- value_type_mapped: dict[tp.Type[ValueType], tp.Callable[[TProp, TupleInstruction], ClauseInfo[T]]] = {
220
- property: self._property_type,
221
- IAggregate: self._IAggregate_type,
222
- Table: self._table_type,
223
- str: self._str_type,
224
- ICustomAlias: self._ICustomAlias_type,
140
+ VALUE_TYPE_MAPPED: dict[tp.Type[ValueType], ClauseInfoConverter[T, TProp]] = {
141
+ ForeignKey: ConvertFromForeignKey[T, Table],
142
+ Column: ConvertFromColumn[TProp],
143
+ IAggregate: ConvertFromIAggregate,
144
+ Table: ConvertFromTable[T],
225
145
  }
146
+ classConverter = next((converter for obj, converter in VALUE_TYPE_MAPPED.items() if validation(data, obj)), ConvertFromAnyType)
226
147
 
227
- function = next((handler for cls, handler in value_type_mapped.items() if validation(data, cls)), None)
228
-
229
- if not function:
230
- raise NotImplementedError(f"type of value '{data}' is not implemented.")
231
-
232
- return function(data, tuple_instruction)
233
-
234
- def _property_type(self, data: property, ti: TupleInstruction):
235
- if ti.var == self.CHAR:
236
- table_left = self.table
237
- else:
238
- table_left = self.alias_cache[ti.var]
239
-
240
- if data in table_left.__properties_mapped__:
241
- # if self.table != table_left:
242
- # self._add_fk_relationship(self.table, table_left)
243
- return ClauseInfo[T](table_left, data, self.alias_children_resolver)
244
-
245
- for table in self.tables:
246
- try:
247
- return self._search_correct_table_for_prop(table, ti, data)
248
- except ValueError:
249
- continue
250
-
251
- def _IAggregate_type(self, data: IAggregate, ti: TupleInstruction):
252
- return ClauseInfo[T](self.table, data, self.alias_children_resolver)
253
-
254
- def _table_type(self, data: tp.Type[Table], ti: TupleInstruction):
255
- if data not in self._tables:
256
- self.__add_necessary_fk(ti, data)
257
- # all columns
258
- clauses: list[ClauseInfo] = []
259
- for prop in data.__properties_mapped__:
260
- if isinstance(prop, property):
261
- clauses.append(self.__identify_value_type(prop, ti))
262
- return clauses
263
-
264
- def _str_type(self, data: str, ti: TupleInstruction):
265
- # COMMENT: use self.table instead self._tables because if we hit this conditional, means that
266
- # COMMENT: alias_cache to replace '*' by all columns
267
- if self._replace_asterisk_char and (replace_value := self.alias_cache.get(data, None)) is not None:
268
- return self.__identify_value_type(replace_value(self.table), ti)
269
- return ClauseInfo[T](self.table, data, alias_children_resolver=self.alias_children_resolver)
270
-
271
- def _ICustomAlias_type(self, data: ICustomAlias, ti: TupleInstruction):
272
- return ClauseInfo(data.table, data, data.alias_children_resolver)
273
-
274
- def _search_correct_table_for_prop[TTable](self, table: tp.Type[Table], tuple_instruction: TupleInstruction, prop: property) -> ClauseInfo[TTable]:
275
- temp_table: tp.Type[Table] = table
276
-
277
- _, *table_list = tuple_instruction.nested_element.parents
278
- counter: int = 0
279
- while prop not in temp_table.__properties_mapped__:
280
- new_table: TTable = getattr(temp_table(), table_list[counter])
281
-
282
- if not isinstance(new_table, type) or not issubclass(new_table, Table):
283
- raise ValueError(f"new_table var must be '{Table.__class__}' type and is '{type(new_table)}'")
284
- self._add_fk_relationship(temp_table, new_table)
285
-
286
- temp_table = new_table
287
- counter += 1
288
-
289
- if prop in new_table.__properties_mapped__:
290
- return ClauseInfo[TTable](new_table, prop, self.alias_children_resolver)
148
+ return classConverter.convert(data, alias_table=self._alias_table, context=self._context)
291
149
 
292
- raise ValueError(f"property '{prop}' does not exist in any inherit tables.")
150
+ def __add_clause[TTable: TableType](self, clauses: list[ClauseInfo[TTable]] | ClauseInfo[TTable]) -> None:
151
+ if not isinstance(clauses, tp.Iterable):
152
+ raise ValueError(f"Iterable expected. '{type(clauses)}' got instead.")
293
153
 
294
- def __add_clause[Tc: tp.Type[Table]](self, clause: ClauseInfo[Tc]) -> None:
295
- self._all_clauses.append(clause)
296
- self._clauses_group_by_tables[clause.table].append(clause)
154
+ for clause in clauses:
155
+ self._all_clauses.append(clause)
297
156
  return None
298
157
 
299
- def __add_necessary_fk(self, tuple_instruction: TupleInstruction, tables: tp.Type[Table]) -> None:
300
- old_table = self.table
301
-
302
- table_inherit_list: list[Table] = tuple_instruction.nested_element.parents[1:]
303
- counter: int = 0
304
- while tables not in old_table.__dict__.values():
305
- new_table: tp.Type[Table] = getattr(old_table(), table_inherit_list[counter])
306
-
307
- if not issubclass(new_table, Table):
308
- raise ValueError(f"new_table var must be '{Table.__class__}' type and is '{type(new_table)}'")
309
-
310
- self._add_fk_relationship(old_table, new_table)
311
-
312
- if tables in new_table.__dict__.values():
313
- return self._add_fk_relationship(new_table, tables)
314
-
315
- old_table = new_table
316
- counter += 1
317
-
318
- return self._add_fk_relationship(old_table, tables)
319
-
320
158
  @property
321
159
  def table(self) -> T:
322
160
  return self.tables[0] if isinstance(self.tables, tp.Iterable) else self.tables
@@ -326,55 +164,19 @@ class DecompositionQueryBase[T: tp.Type[Table], *Ts](IDecompositionQuery[T, *Ts]
326
164
  return self._tables
327
165
 
328
166
  @property
329
- def lambda_query[*Ts](self) -> tp.Callable[[T], tuple[*Ts]]:
330
- return self._lambda_query
167
+ def columns[*Ts](self) -> tp.Callable[[T], tuple[*Ts]]:
168
+ return self._columns
331
169
 
332
170
  @property
333
171
  def all_clauses(self) -> list[ClauseInfo[T]]:
334
172
  return self._all_clauses
335
173
 
336
174
  @property
337
- def clauses_group_by_tables(self) -> dict[tp.Type[Table], list[ClauseInfo[T]]]:
338
- return self._clauses_group_by_tables
339
-
340
- @property
341
- def has_foreign_keys(self) -> bool:
342
- return len(self._joins) > 0
175
+ def context(self) -> tp.Optional[ClauseInfoContext]:
176
+ return self._context
343
177
 
344
- @property
345
- def fk_relationship(self) -> set[tuple[tp.Type[Table], tp.Type[Table]]]:
346
- return self._joins
347
-
348
- @property
349
- @abc.abstractmethod
350
- def query(self) -> str: ...
351
-
352
- @property
353
- def alias(self) -> str:
354
- return self._alias
355
-
356
- @property
357
- def alias_name(self) -> str:
358
- return self._alias_name
359
-
360
- @alias_name.setter
361
- def alias_name(self, value: tp.Optional[str]) -> None:
362
- if value is None and self._alias:
363
- self._alias = False
364
- else:
365
- self._alias = True
366
-
367
- self._alias_name = value
368
-
369
- def stringify_foreign_key(self, sep: str = "\n") -> str:
370
- sorted_joins = JoinSelector.sort_join_selectors(self._joins)
371
- return f"{sep}".join([join.query for join in sorted_joins])
372
-
373
- def _add_fk_relationship[T1: tp.Type[Table], T2: tp.Type[Table]](self, t1: T1, t2: T2) -> None:
374
- lambda_relationship = ForeignKey.MAPPED[t1.__table_name__].referenced_tables[t2.__table_name__].relationship
375
-
376
- tables = list(self._tables)
377
- if t2 not in tables:
378
- tables.append(t2)
379
- self._tables = tuple(tables)
380
- return self._joins.add(JoinSelector[T1, T2](t1, t2, self._by, where=lambda_relationship))
178
+ @context.setter
179
+ def context(self, value: ClauseInfoContext) -> None:
180
+ self._context = value
181
+ self.__clauses_list_generetor()
182
+ return None
@@ -5,7 +5,7 @@ from typing import Any, Optional, Type, override, TYPE_CHECKING
5
5
  from ormlambda.common.interfaces.INonQueryCommand import INonQueryCommand
6
6
 
7
7
  if TYPE_CHECKING:
8
- from ormlambda import IRepositoryBase
8
+ from ormlambda.repository import IRepositoryBase
9
9
  from ormlambda import Table
10
10
 
11
11
 
@@ -2,7 +2,7 @@ from enum import Enum
2
2
 
3
3
 
4
4
  class ConditionType(Enum):
5
- EQUAL = "=="
5
+ EQUAL = "="
6
6
  LESS_THAN = "<"
7
7
  GREATER_THAN = ">"
8
8
  LESS_THAN_OR_EQUAL = "<="
@@ -12,5 +12,6 @@ class ConditionType(Enum):
12
12
  BETWEEN = "BETWEEN"
13
13
  LIKE = "LIKE"
14
14
  IN = "IN"
15
+ NOT_IN = "NOT IN"
15
16
  IS = "IS"
16
17
  IS_NOT = "IS NOT"
@@ -1,7 +1,10 @@
1
1
  from enum import Enum
2
2
 
3
3
 
4
- class JoinType(Enum):
4
+ class JoinType(str, Enum):
5
+ def __str__(self):
6
+ return super().__str__()
7
+
5
8
  RIGHT_INCLUSIVE = "RIGHT JOIN"
6
9
  LEFT_INCLUSIVE = "LEFT JOIN"
7
10
  RIGHT_EXCLUSIVE = "RIGHT JOIN"
@@ -1,8 +1,21 @@
1
+ import inspect
2
+ import typing as tp
3
+
4
+
1
5
  class UnmatchedLambdaParameterError(Exception):
2
- def __init__(self, expected_params: int, found_param: tuple[str, ...], *args: object) -> None:
6
+ def __init__(self, expected_params: int, function: tp.Callable[..., tp.Any], *args: object) -> None:
3
7
  super().__init__(*args)
4
8
  self.expected_params = expected_params
5
- self.found_param: tuple[str, ...] = found_param
9
+ self.found_param: tuple[str, ...] = tuple(inspect.signature(function).parameters)
6
10
 
7
11
  def __str__(self) -> str:
8
12
  return f"Unmatched number of parameters in lambda function with the number of tables: Expected {self.expected_params} parameters but found {str(self.found_param)}."
13
+
14
+
15
+ class NotKeysInIAggregateError(Exception):
16
+ def __init__(self, match_regex: list[str], *args: object) -> None:
17
+ super().__init__(*args)
18
+ self._match_regex: list[str] = match_regex
19
+
20
+ def __str__(self) -> str:
21
+ return f"We cannot use placeholders in IAggregate class. You used {self._match_regex}"
@@ -0,0 +1,28 @@
1
+ from __future__ import annotations
2
+ import re
3
+ from typing import Any, TYPE_CHECKING
4
+
5
+ from ormlambda.common.errors import UnmatchedLambdaParameterError
6
+
7
+ if TYPE_CHECKING:
8
+ from ormlambda.sql import Table
9
+
10
+
11
+ class GlobalChecker:
12
+ @staticmethod
13
+ def is_lambda_function(obj: Any) -> bool:
14
+ return callable(obj) and not isinstance(obj, type)
15
+
16
+ @classmethod
17
+ def resolved_callback_object(cls, obj: Any, tables: tuple[Table, ...]):
18
+ if not cls.is_lambda_function(obj):
19
+ return obj
20
+
21
+ try:
22
+ return obj(*tables)
23
+ except TypeError as err:
24
+ cond1 = r"takes \d+ positional argument but \d+ were given"
25
+ cond2 = r"missing \d+ required positional arguments:"
26
+ if re.search(r"("+f"{cond1}|{cond2}"+r")", err.args[0]):
27
+ raise UnmatchedLambdaParameterError(len(tables), obj)
28
+ raise err
@@ -1,4 +1,7 @@
1
1
  from ormlambda.common.interfaces import IDecompositionQuery
2
2
 
3
3
 
4
- class ICustomAlias[T, *Ts](IDecompositionQuery[T, *Ts]): ...
4
+ class ICustomAlias[T, *Ts]: ...
5
+
6
+
7
+ # class ICustomAlias[T, *Ts](IDecompositionQuery[T, *Ts]): ...
@@ -4,55 +4,30 @@ import typing as tp
4
4
 
5
5
 
6
6
  if tp.TYPE_CHECKING:
7
- from ormlambda import Table
8
-
9
7
  # TODOH: Changed to avoid mysql dependency
10
- from ormlambda.common.abstract_classes.decomposition_query import ClauseInfo
11
-
12
- from .IQueryCommand import IQuery
8
+ from ormlambda.sql.clause_info import ClauseInfo, TableType
9
+ from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext
13
10
 
14
11
 
15
- class IDecompositionQuery_one_arg[T: tp.Type[Table]](IQuery):
12
+ class IDecompositionQuery_one_arg[T: TableType]:
16
13
  @property
17
14
  @abc.abstractmethod
18
15
  def table(self) -> T: ...
19
16
 
20
17
  @property
21
18
  @abc.abstractmethod
22
- def alias(self) -> bool: ...
23
-
24
-
25
- class IDecompositionQuery[T: tp.Type[Table], *Ts](IDecompositionQuery_one_arg[T], IQuery):
26
- @property
27
- @abc.abstractmethod
28
- def tables(self) -> tuple[*Ts]: ...
19
+ def context(self) -> tp.Optional[ClauseInfoContext]: ...
29
20
 
30
- @property
21
+ @context.setter
31
22
  @abc.abstractmethod
32
- def lambda_query[*Ts](self) -> tp.Callable[[T], tuple[*Ts]]: ...
23
+ def context(self) -> tp.Optional[ClauseInfoContext]: ...
33
24
 
34
- @property
35
- @abc.abstractmethod
36
- def all_clauses(self) -> list[ClauseInfo]: ...
37
25
 
26
+ class IDecompositionQuery[T: TableType, *Ts](IDecompositionQuery_one_arg[T]):
38
27
  @property
39
28
  @abc.abstractmethod
40
- def clauses_group_by_tables(self) -> dict[tp.Type[Table], list[ClauseInfo[T]]]: ...
41
-
42
- @property
43
- @abc.abstractmethod
44
- def fk_relationship(self) -> set[tuple[tp.Type[Table], tp.Type[Table]]]: ...
45
-
46
- @property
47
- @abc.abstractmethod
48
- def alias_name(self) -> tp.Optional[str]: ...
29
+ def tables(self) -> tuple[*Ts]: ...
49
30
 
50
31
  @property
51
32
  @abc.abstractmethod
52
- def has_foreign_keys(self) -> bool: ...
53
-
54
- @abc.abstractmethod
55
- def stringify_foreign_key(self, sep: str = "\n"): ...
56
-
57
- @abc.abstractmethod
58
- def alias_children_resolver(self) -> tp.Callable[[tp.Type[Table], str], str]: ...
33
+ def all_clauses(self) -> list[ClauseInfo]: ...