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
@@ -0,0 +1,434 @@
1
+ from __future__ import annotations
2
+ import abc
3
+ import typing as tp
4
+ import re
5
+
6
+ from ormlambda import Table
7
+ from ormlambda import Column
8
+ from ormlambda.common.interfaces.IQueryCommand import IQuery
9
+ from ormlambda.sql.types import (
10
+ ASTERISK,
11
+ TableType,
12
+ ColumnType,
13
+ AliasType,
14
+ )
15
+ from .interface import IAggregate
16
+ from ormlambda.common.errors import NotKeysInIAggregateError
17
+ from ormlambda.sql import ForeignKey
18
+ from ormlambda.sql.table import TableMeta
19
+ from ormlambda.caster import Caster
20
+
21
+
22
+ from .clause_info_context import ClauseInfoContext, ClauseContextType
23
+
24
+
25
+ class ReplacePlaceholderError(ValueError):
26
+ def __init__(self, placeholder: str, attribute: str, *args):
27
+ super().__init__(*args)
28
+ self.placeholder: str = placeholder
29
+ self.attr: str = attribute
30
+
31
+ def __str__(self):
32
+ return "You cannot use {" + self.placeholder + "} placeholder without using '" + self.attr + "' attribute"
33
+
34
+
35
+ class IClauseInfo[T: Table](IQuery):
36
+ @property
37
+ @abc.abstractmethod
38
+ def table(self) -> TableType[T]: ...
39
+ @property
40
+ @abc.abstractmethod
41
+ def alias_clause(self) -> tp.Optional[str]: ...
42
+ @property
43
+ @abc.abstractmethod
44
+ def alias_table(self) -> tp.Optional[str]: ...
45
+ @property
46
+ @abc.abstractmethod
47
+ def column(self) -> str: ...
48
+ @property
49
+ @abc.abstractmethod
50
+ def unresolved_column(self) -> ColumnType: ...
51
+ @property
52
+ @abc.abstractmethod
53
+ def context(self) -> ClauseContextType: ...
54
+ @property
55
+ @abc.abstractmethod
56
+ def dtype[TProp](self) -> tp.Optional[tp.Type[TProp]]: ...
57
+
58
+
59
+ class ClauseInfo[T: Table](IClauseInfo[T]):
60
+ _keyRegex: re.Pattern = re.compile(r"{([^{}:]+)}")
61
+
62
+ @tp.overload
63
+ def __init__(self, table: TableType[T]): ...
64
+ @tp.overload
65
+ def __init__[TProp](self, table: TableType[T], column: ColumnType[TProp]): ...
66
+ @tp.overload
67
+ def __init__[TProp](self, table: TableType[T], column: ColumnType[TProp], alias_table: AliasType[ClauseInfo[T]] = ..., alias_clause: AliasType[ClauseInfo[T]] = ...): ...
68
+ @tp.overload
69
+ def __init__(self, table: TableType[T], alias_table: AliasType[ClauseInfo[T]] = ..., alias_clause: AliasType[ClauseInfo[T]] = ...): ...
70
+ @tp.overload
71
+ def __init__[TProp](self, table: TableType[T], column: ColumnType[TProp], context: ClauseContextType): ...
72
+ @tp.overload
73
+ def __init__(self, table: TableType[T], keep_asterisk: tp.Optional[bool] = ...): ...
74
+ @tp.overload
75
+ def __init__(self, table: TableType[T], preserve_context: tp.Optional[bool] = ...): ...
76
+
77
+ def __init__[TProp](
78
+ self,
79
+ table: TableType[T],
80
+ column: tp.Optional[ColumnType[TProp]] = None,
81
+ alias_table: tp.Optional[AliasType[ClauseInfo[T]]] = None,
82
+ alias_clause: tp.Optional[AliasType[ClauseInfo[T]]] = None,
83
+ context: ClauseContextType = None,
84
+ keep_asterisk: bool = False,
85
+ preserve_context: bool = False,
86
+ ):
87
+ if not self.is_table(table):
88
+ column = table if not column else column
89
+ table = self.extract_table(table)
90
+
91
+ self._table: TableType[T] = table
92
+ self._column: TableType[T] | ColumnType[TProp] = column
93
+ self._alias_table: tp.Optional[AliasType[ClauseInfo[T]]] = alias_table
94
+ self._alias_clause: tp.Optional[AliasType[ClauseInfo[T]]] = alias_clause
95
+ self._context: ClauseContextType = context if context else ClauseInfoContext()
96
+ self._keep_asterisk: bool = keep_asterisk
97
+ self._preserve_context: bool = preserve_context
98
+
99
+ self._placeholderValues: dict[str, tp.Callable[[TProp], str]] = {
100
+ "column": self.replace_column_placeholder,
101
+ "table": self.replace_table_placeholder,
102
+ }
103
+
104
+ if not self._preserve_context and (self._context and any([alias_table, alias_clause])):
105
+ self._context.add_clause_to_context(self)
106
+
107
+ def __repr__(self) -> str:
108
+ return f"{type(self).__name__}: query -> {self.query}"
109
+
110
+ def replace_column_placeholder[TProp](self, column: ColumnType[TProp]) -> str:
111
+ if not column:
112
+ raise ReplacePlaceholderError("column", "column")
113
+ return self._column_resolver(column)
114
+
115
+ def replace_table_placeholder[TProp](self, _: ColumnType[TProp]) -> str:
116
+ if not self.table:
117
+ raise ReplacePlaceholderError("table", "table")
118
+ return self.table.__table_name__
119
+
120
+ @property
121
+ def table(self) -> TableType[T]:
122
+ if self.is_foreign_key(self._table):
123
+ return self._table.tright
124
+ return self._table
125
+
126
+ @table.setter
127
+ def table(self, value: TableType[T]) -> None:
128
+ self._table = value
129
+
130
+ @property
131
+ def alias_clause(self) -> tp.Optional[str]:
132
+ alias = self._alias_clause if not (a := self.get_clause_alias()) else a
133
+ return self._alias_resolver(alias)
134
+
135
+ # TODOL [ ]: if we using this setter, we don't update the _context with the new value. Study if it's necessary
136
+ @alias_clause.setter
137
+ def alias_clause(self, value: str) -> str:
138
+ self._alias_clause = value
139
+
140
+ @property
141
+ def alias_table(self) -> tp.Optional[str]:
142
+ alias = self._alias_table if not (a := self.get_table_alias()) else a
143
+ return self._alias_resolver(alias)
144
+
145
+ # TODOL [ ]: if we using this setter, we don't update the _context with the new value. Study if it's necessary
146
+ @alias_table.setter
147
+ def alias_table(self, value: str) -> str:
148
+ self._alias_table = value
149
+
150
+ def replaced_alias(self, value: str) -> tp.Optional[str]:
151
+ return value
152
+
153
+ @property
154
+ def column(self) -> str:
155
+ return self._column_resolver(self._column)
156
+
157
+ @property
158
+ def unresolved_column(self) -> ColumnType:
159
+ return self._column
160
+
161
+ @property
162
+ def context(self) -> ClauseContextType:
163
+ return self._context
164
+
165
+ @context.setter
166
+ def context(self, value: ClauseInfoContext) -> None:
167
+ self._context = value
168
+
169
+ @property
170
+ def dtype[TProp](self) -> tp.Optional[tp.Type[TProp]]:
171
+ if isinstance(self._column, Column):
172
+ return self._column.dtype
173
+
174
+ if isinstance(self._column, type):
175
+ return self._column
176
+ return type(self._column)
177
+
178
+ @property
179
+ def query(self) -> str:
180
+ return self._create_query()
181
+
182
+ def _create_query(self) -> str:
183
+ # when passing some value that is not a column name
184
+ if not self.table and not self._alias_clause:
185
+ return self.column
186
+
187
+ if not self.table and self._alias_clause:
188
+ # it means that we are passing an object with alias. We should delete '' around the object
189
+ alias_clause = self.alias_clause
190
+ return self._concat_alias_and_column(self._column, alias_clause)
191
+
192
+ # When passing the Table itself without 'column'
193
+ if self.table and not self._column:
194
+ if not self._alias_table:
195
+ return self.table.__table_name__
196
+ alias_table = self.alias_table
197
+ return self._concat_alias_and_column(self.table.__table_name__, alias_table)
198
+
199
+ if self._return_all_columns():
200
+ return self._get_all_columns()
201
+ return self._join_table_and_column(self._column)
202
+
203
+ def _join_table_and_column[TProp](self, column: ColumnType[TProp]) -> str:
204
+ # FIXME [ ]: Study how to deacoplate from mysql database
205
+ from ormlambda.databases.my_sql.repository import MySQLRepository
206
+
207
+ caster = Caster(MySQLRepository)
208
+
209
+ if self.alias_table:
210
+ table = self._wrapped_with_quotes(self.alias_table)
211
+ else:
212
+ table = self.table.__table_name__
213
+
214
+ column: str = self._column_resolver(column)
215
+
216
+ table_column = f"{table}.{column}"
217
+
218
+ dtype = str if self.is_table(self.dtype) else self.dtype
219
+ wrapped_column = caster.for_value(table_column, dtype).wildcard_to_select(table_column)
220
+ return self._concat_alias_and_column(wrapped_column, self.alias_clause)
221
+
222
+ def _return_all_columns(self) -> bool:
223
+ if self._keep_asterisk:
224
+ return False
225
+ if self.is_foreign_key(self._column) or self.is_table(self._column):
226
+ return True
227
+
228
+ C1 = self._column is self.table and self.is_table(self._column)
229
+ return any([self.is_asterisk(self._column), C1])
230
+
231
+ @staticmethod
232
+ def is_asterisk(value: tp.Optional[str]) -> bool:
233
+ return isinstance(value, str) and value == ASTERISK
234
+
235
+ def _get_all_columns(self) -> str:
236
+ def ClauseCreator(column: str) -> ClauseInfo:
237
+ return ClauseInfo(
238
+ table=self.table,
239
+ column=column,
240
+ alias_table=self._alias_table,
241
+ alias_clause=self._alias_clause,
242
+ context=self._context,
243
+ keep_asterisk=self._keep_asterisk,
244
+ )
245
+
246
+ if self._alias_table and self._alias_clause: # We'll add an "*" when we are certain that we have included 'alias_clause' attr
247
+ return self._join_table_and_column(ASTERISK)
248
+
249
+ columns: list[ClauseInfo] = [ClauseCreator(column).query for column in self.table.get_columns()]
250
+
251
+ return ", ".join(columns)
252
+
253
+ # FIXME [ ]: Study how to deacoplate from mysql database
254
+ def _column_resolver[TProp](self, column: ColumnType[TProp]) -> str:
255
+ from ormlambda.databases.my_sql.repository import MySQLRepository
256
+
257
+ caster = Caster(MySQLRepository)
258
+ if isinstance(column, ClauseInfo):
259
+ return column.query
260
+
261
+ if isinstance(column, tp.Iterable) and isinstance(column[0], ClauseInfo):
262
+ return self.join_clauses(column)
263
+
264
+ if isinstance(column, Column):
265
+ return column.column_name
266
+
267
+ # if we want to pass the name of a column as a string, the 'table' var must not be None
268
+ if self.table and isinstance(self._column, str):
269
+ return self._column
270
+
271
+ if self.is_asterisk(column):
272
+ return ASTERISK
273
+
274
+ if self.is_table(self._column):
275
+ return self._column.__table_name__
276
+
277
+ if self.is_foreign_key(self._column):
278
+ return self._column.tright.__table_name__
279
+
280
+ casted_value = caster.for_value(column, self.dtype)
281
+ if not self._table:
282
+ # if we haven't some table atrribute, we assume that the user want to retrieve the string_data from caster.
283
+ return casted_value.string_data
284
+ return casted_value.wildcard_to_select()
285
+
286
+ def _replace_placeholder(self, string: str) -> str:
287
+ return self._keyRegex.sub(self._replace, string)
288
+
289
+ def _replace(self, match: re.Match[str]) -> str:
290
+ key = match.group(1)
291
+
292
+ if not (func := self._placeholderValues.get(key, None)):
293
+ return match.group(0) # No placeholder / value
294
+
295
+ return func(self._column)
296
+
297
+ def _concat_alias_and_column(self, column: str, alias_clause: tp.Optional[str] = None) -> str:
298
+ if alias_clause is None:
299
+ return column
300
+ alias = f"{column} AS {self._wrapped_with_quotes(alias_clause)}"
301
+ return alias
302
+
303
+ def _alias_resolver(self, alias: AliasType[ClauseInfo[T]]) -> tp.Optional[str]:
304
+ if alias is None:
305
+ return None
306
+
307
+ if callable(alias):
308
+ return self._alias_resolver(alias(self))
309
+
310
+ return self._replace_placeholder(alias)
311
+
312
+ def get_clause_alias(self) -> tp.Optional[str]:
313
+ if not self._context:
314
+ return None
315
+ return self._context.get_clause_alias(self)
316
+
317
+ def get_table_alias(self) -> tp.Optional[str]:
318
+ if not self._context:
319
+ return None
320
+ return self._context.get_table_alias(self.table)
321
+
322
+ @staticmethod
323
+ def join_clauses(clauses: list[ClauseInfo[T]], chr: str = ",", context: tp.Optional[ClauseInfoContext] = None) -> str:
324
+ queries: list[str] = []
325
+ for c in clauses:
326
+ if context:
327
+ c.context = context
328
+ queries.append(c.query)
329
+
330
+ return f"{chr} ".join(queries)
331
+
332
+ @staticmethod
333
+ def _wrapped_with_quotes(string: str) -> str:
334
+ return f"`{string}`"
335
+
336
+ @classmethod
337
+ def extract_table(cls, element: ColumnType[T] | TableType[T]) -> tp.Optional[T]:
338
+ if element is None:
339
+ return None
340
+
341
+ if cls.is_table(element):
342
+ return element
343
+
344
+ if cls.is_foreign_key(element):
345
+ return element.tright
346
+
347
+ if isinstance(element, Column):
348
+ return element.table
349
+ return None
350
+
351
+ @staticmethod
352
+ def is_table(data: ColumnType | Table | ForeignKey) -> bool:
353
+ return isinstance(data, type) and issubclass(data, Table)
354
+
355
+ @staticmethod
356
+ def is_foreign_key(data: ColumnType | Table | ForeignKey) -> bool:
357
+ return isinstance(data, ForeignKey)
358
+
359
+ @classmethod
360
+ def is_column(cls, data: tp.Any) -> bool:
361
+ if cls.is_table(data) or cls.is_foreign_key(data) or cls.is_asterisk(data):
362
+ return False
363
+ if isinstance(data, Column):
364
+ return True
365
+ return False
366
+
367
+
368
+ class AggregateFunctionBase[T: Table](ClauseInfo[T], IAggregate):
369
+ def __init__[TProp: Column](
370
+ self,
371
+ table: TableType[T],
372
+ column: tp.Optional[ColumnType[TProp]] = None,
373
+ alias_table: tp.Optional[AliasType[ClauseInfo[T]]] = None,
374
+ alias_clause: tp.Optional[AliasType[ClauseInfo[T]]] = None,
375
+ context: ClauseContextType = None,
376
+ keep_asterisk: bool = False,
377
+ preserve_context: bool = False,
378
+ ):
379
+ self._alias_aggregate = alias_clause
380
+ super().__init__(
381
+ table=table,
382
+ column=column,
383
+ alias_table=alias_table,
384
+ context=context,
385
+ keep_asterisk=keep_asterisk,
386
+ preserve_context=preserve_context,
387
+ )
388
+
389
+ @staticmethod
390
+ @abc.abstractmethod
391
+ def FUNCTION_NAME() -> str: ...
392
+
393
+ @classmethod
394
+ def _convert_into_clauseInfo[TypeColumns, TProp](cls, columns: ClauseInfo | ColumnType[TProp], context: ClauseContextType) -> list[ClauseInfo]:
395
+ type DEFAULT = tp.Literal["default"]
396
+ type ClusterType = ColumnType | ForeignKey | DEFAULT
397
+
398
+ dicc_type: dict[ClusterType, tp.Callable[[ClusterType], ClauseInfo]] = {
399
+ Column: lambda column: ClauseInfo(column.table, column, context=context),
400
+ ClauseInfo: lambda column: column,
401
+ ForeignKey: lambda tbl: ClauseInfo(tbl.tright, tbl.tright, context=context),
402
+ TableMeta: lambda tbl: ClauseInfo(tbl, tbl, context=context),
403
+ "default": lambda column: ClauseInfo(table=None, column=column, context=context),
404
+ }
405
+ all_clauses: list[ClauseInfo] = []
406
+ if isinstance(columns, str) or not isinstance(columns, tp.Iterable):
407
+ columns = (columns,)
408
+ for value in columns:
409
+ all_clauses.append(dicc_type.get(type(value), dicc_type["default"])(value))
410
+
411
+ return all_clauses
412
+
413
+ @tp.override
414
+ @property
415
+ def query(self) -> str:
416
+ wrapped_ci = self.wrapped_clause_info(self)
417
+ if not self._alias_aggregate:
418
+ return wrapped_ci
419
+
420
+ return ClauseInfo(
421
+ table=None,
422
+ column=wrapped_ci,
423
+ alias_clause=self._alias_aggregate,
424
+ context=self._context,
425
+ keep_asterisk=self._keep_asterisk,
426
+ preserve_context=self._preserve_context,
427
+ ).query
428
+
429
+ def wrapped_clause_info(self, ci: ClauseInfo[T]) -> str:
430
+ # avoid use placeholder when using IAggregate because no make sense.
431
+ if self._alias_aggregate and (found := self._keyRegex.findall(self._alias_aggregate)):
432
+ raise NotKeysInIAggregateError(found)
433
+
434
+ return f"{self.FUNCTION_NAME()}({ci._create_query()})"
@@ -0,0 +1,87 @@
1
+ from __future__ import annotations
2
+ import abc
3
+ from typing import Literal, Optional, TYPE_CHECKING, overload
4
+
5
+ from ormlambda import Table
6
+ from ormlambda.sql.types import (
7
+ TableType,
8
+ )
9
+
10
+ from ormlambda import Column
11
+
12
+ if TYPE_CHECKING:
13
+ from .clause_info import ClauseInfo
14
+
15
+
16
+ class IClauseInfo(abc.ABC): ...
17
+
18
+
19
+ AliasChoiceType = Literal["TABLE", "CLAUSE"]
20
+
21
+ type ClauseAliasKey[T: Table, TProp] = tuple[TableType[T], Column[TProp]]
22
+ type TableAliasKey[T: Table] = T
23
+
24
+ type AliasKey[T: Table, TProp] = ClauseAliasKey[T, TProp] | TableAliasKey[T]
25
+
26
+ type ClauseContextType = Optional[ClauseInfoContext]
27
+
28
+
29
+ class ClauseInfoContext(IClauseInfo):
30
+ @overload
31
+ def __init__(self) -> None: ...
32
+ @overload
33
+ def __init__[T: Table, TProp](self, clause_context: dict[ClauseAliasKey[T, TProp], str]) -> None: ...
34
+ @overload
35
+ def __init__[T: Table](self, table_context: dict[TableAliasKey[T], str]) -> None: ...
36
+
37
+ def __init__[T: Table, TProp](
38
+ self,
39
+ clause_context: Optional[dict[ClauseAliasKey[T, TProp], str]] = None,
40
+ table_context: Optional[dict[TableAliasKey[T], str]] = None,
41
+ ) -> None:
42
+ self._clause_context: dict[AliasKey[T, TProp], str] = clause_context if clause_context else {}
43
+ self._table_context: dict[AliasKey[T, TProp], str] = table_context if table_context else {}
44
+
45
+ def add_clause_to_context[T: Table](self, clause: ClauseInfo[T]) -> None:
46
+ if not clause:
47
+ return None
48
+
49
+ if t := clause.table:
50
+ self._add_table_alias(t, clause._alias_table)
51
+ if c := clause.column:
52
+ self._add_clause_alias((t, c, type(clause)), clause._alias_clause)
53
+
54
+ return None
55
+
56
+ def _add_clause_alias[T: Table, TProp](self, key: AliasKey[T, TProp], alias: str) -> None:
57
+ if not all([key, alias]):
58
+ return None
59
+
60
+ self._clause_context[key] = alias
61
+
62
+ return None
63
+
64
+ def _add_table_alias[T: Table, TProp](self, key: AliasKey[T, TProp], alias: str) -> None:
65
+ if not all([key, alias]):
66
+ return None
67
+
68
+ self._table_context[key] = alias
69
+
70
+ return None
71
+
72
+ def get_clause_alias[T: Table, TProp](self, clause: ClauseInfo[T]) -> Optional[str]:
73
+ table_col: ClauseAliasKey[T, TProp] = (clause.table, clause.column, type(clause))
74
+ return self._clause_context.get(table_col, None)
75
+
76
+ def get_table_alias[T: Table](self, table: T) -> Optional[str]:
77
+ return self._table_context.get(table, None)
78
+
79
+ def update(self, context: ClauseInfoContext) -> None:
80
+ if not context:
81
+ return None
82
+ if not isinstance(context, ClauseInfoContext):
83
+ raise ValueError(f"A '{ClauseInfoContext.__name__}' type was expected")
84
+
85
+ self._table_context.update(context._table_context)
86
+ self._clause_context.update(context._clause_context)
87
+ return None
@@ -0,0 +1,10 @@
1
+ from __future__ import annotations
2
+ import abc
3
+
4
+ from ormlambda.common.interfaces.IQueryCommand import IQuery
5
+
6
+
7
+ class IAggregate(IQuery):
8
+ @classmethod
9
+ @abc.abstractmethod
10
+ def FUNCTION_NAME(cls) -> str: ...
@@ -0,0 +1 @@
1
+ from .IAggregate import IAggregate # noqa: F401
@@ -0,0 +1,126 @@
1
+ from __future__ import annotations
2
+ from typing import Iterable, Type, Optional, TYPE_CHECKING
3
+ import abc
4
+ from ormlambda.sql.types import TableType, ComparerType, ColumnType
5
+ from ormlambda import ConditionType
6
+
7
+ if TYPE_CHECKING:
8
+ import re
9
+ from ormlambda import Table
10
+ from ormlambda.sql.comparer import Comparer, Regex, Like
11
+
12
+
13
+ class Column[TProp]:
14
+ PRIVATE_CHAR: str = "_"
15
+
16
+ __slots__ = (
17
+ "dtype",
18
+ "column_name",
19
+ "table",
20
+ "is_primary_key",
21
+ "is_auto_generated",
22
+ "is_auto_increment",
23
+ "is_unique",
24
+ "__private_name",
25
+ "_check",
26
+ )
27
+
28
+ def __init__[T: Table](
29
+ self,
30
+ dtype: Type[TProp],
31
+ is_primary_key: bool = False,
32
+ is_auto_generated: bool = False,
33
+ is_auto_increment: bool = False,
34
+ is_unique: bool = False,
35
+ check_types: bool = True,
36
+ ) -> None:
37
+ self.dtype: Type[TProp] = dtype
38
+ self.table: Optional[TableType[T]] = None
39
+ self.column_name: Optional[str] = None
40
+ self.__private_name: Optional[str] = None
41
+ self._check = check_types
42
+
43
+ self.is_primary_key: bool = is_primary_key
44
+ self.is_auto_generated: bool = is_auto_generated
45
+ self.is_auto_increment: bool = is_auto_increment
46
+ self.is_unique: bool = is_unique
47
+
48
+ def __repr__(self) -> str:
49
+ return f"{type(self).__name__}[{self.dtype.__name__}] => {self.column_name}"
50
+
51
+ def __str__(self) -> str:
52
+ return self.table.__table_name__ + "." + self.column_name
53
+
54
+ def __set_name__[T: Table](self, owner: TableType[T], name: str) -> None:
55
+ self.table: TableType[T] = owner
56
+ self.column_name = name
57
+ self.__private_name = self.PRIVATE_CHAR + name
58
+
59
+ def __get__(self, obj, objtype=None) -> ColumnType[TProp]:
60
+ if not obj:
61
+ return self
62
+ return getattr(obj, self.__private_name)
63
+
64
+ def __set__(self, obj, value):
65
+ if self._check and value is not None:
66
+ if not isinstance(value, self.dtype):
67
+ raise ValueError(f"The '{self.column_name}' Column from '{self.table.__table_name__}' table expected '{str(self.dtype)}' type. You passed '{type(value).__name__}' type")
68
+ setattr(obj, self.__private_name, value)
69
+
70
+ def __hash__(self) -> int:
71
+ return hash(
72
+ (
73
+ self.column_name,
74
+ self.is_primary_key,
75
+ self.is_auto_generated,
76
+ self.is_auto_increment,
77
+ self.is_unique,
78
+ )
79
+ )
80
+
81
+ @abc.abstractmethod
82
+ def __comparer_creator[LTable: Table, OTherTable: Table, OTherType](self, other: ColumnType[OTherType], compare: ComparerType, *args) -> Comparer:
83
+ from ormlambda.sql.comparer import Comparer
84
+
85
+ return Comparer[LTable, TProp, OTherTable, OTherType](self, other, compare, *args)
86
+
87
+ def __eq__[LTable, OTherTable, OTherProp](self, other: ColumnType[OTherProp], *args) -> Comparer[LTable, TProp, OTherTable, OTherProp]:
88
+ return self.__comparer_creator(other, ConditionType.EQUAL.value, *args)
89
+
90
+ def __ne__[LTable, OTherTable, OtherProp](self, other: ColumnType[OtherProp], *args) -> Comparer[LTable, TProp, OTherTable, OtherProp]:
91
+ return self.__comparer_creator(other, ConditionType.NOT_EQUAL.value, *args)
92
+
93
+ def __lt__[LTable, OTherTable, OtherProp](self, other: ColumnType[OtherProp], *args) -> Comparer[LTable, TProp, OTherTable, OtherProp]:
94
+ return self.__comparer_creator(other, ConditionType.LESS_THAN.value, *args)
95
+
96
+ def __le__[LTable, OTherTable, OtherProp](self, other: ColumnType[OtherProp], *args) -> Comparer[LTable, TProp, OTherTable, OtherProp]:
97
+ return self.__comparer_creator(other, ConditionType.LESS_THAN_OR_EQUAL.value, *args)
98
+
99
+ def __gt__[LTable, OTherTable, OtherProp](self, other: ColumnType[OtherProp], *args) -> Comparer[LTable, TProp, OTherTable, OtherProp]:
100
+ return self.__comparer_creator(other, ConditionType.GREATER_THAN.value, *args)
101
+
102
+ def __ge__[LTable, OTherTable, OtherProp](self, other: ColumnType[OtherProp], *args) -> Comparer[LTable, TProp, OTherTable, OtherProp]:
103
+ return self.__comparer_creator(other, ConditionType.GREATER_THAN_OR_EQUAL.value, *args)
104
+
105
+ def contains[LTable, OTherTable, OtherProp](self, other: ColumnType[OtherProp], *args) -> Comparer[LTable, TProp, OTherTable, OtherProp]:
106
+ return self.__comparer_creator(other, ConditionType.IN.value, *args)
107
+
108
+ def not_contains[LTable, OTherTable, OtherProp](self, other: ColumnType[OtherProp], *args) -> Comparer[LTable, TProp, OTherTable, OtherProp]:
109
+ return self.__comparer_creator(other, ConditionType.NOT_IN.value, *args)
110
+
111
+ def regex[LProp, RProp](self, pattern: str, flags: Optional[re.RegexFlag | Iterable[re.RegexFlag]] = None) -> Regex[LProp, RProp]:
112
+ from ormlambda.sql.comparer import Regex
113
+
114
+ if not isinstance(flags, Iterable):
115
+ flags = (flags,)
116
+ return Regex(
117
+ left_condition=self,
118
+ right_condition=pattern,
119
+ context=None,
120
+ flags=flags,
121
+ )
122
+
123
+ def like[LProp, RProp](self, pattern: str) -> Like[LProp, RProp]:
124
+ from ormlambda.sql.comparer import Like
125
+
126
+ return Like(self, pattern)