ormlambda 3.35.3__py3-none-any.whl → 4.0.4__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 (130) hide show
  1. ormlambda/__init__.py +79 -51
  2. ormlambda/caster/caster.py +6 -1
  3. ormlambda/common/abstract_classes/__init__.py +0 -2
  4. ormlambda/common/enums/__init__.py +1 -0
  5. ormlambda/common/enums/order_type.py +9 -0
  6. ormlambda/common/errors/__init__.py +13 -3
  7. ormlambda/common/global_checker.py +86 -8
  8. ormlambda/common/interfaces/IQueryCommand.py +2 -2
  9. ormlambda/common/interfaces/__init__.py +0 -2
  10. ormlambda/dialects/__init__.py +75 -3
  11. ormlambda/dialects/default/base.py +1 -1
  12. ormlambda/dialects/mysql/__init__.py +35 -78
  13. ormlambda/dialects/mysql/base.py +226 -40
  14. ormlambda/dialects/mysql/clauses/ST_AsText.py +26 -0
  15. ormlambda/dialects/mysql/clauses/ST_Contains.py +30 -0
  16. ormlambda/dialects/mysql/clauses/__init__.py +1 -0
  17. ormlambda/dialects/mysql/repository/__init__.py +1 -0
  18. ormlambda/{databases/my_sql → dialects/mysql/repository}/repository.py +0 -5
  19. ormlambda/dialects/mysql/types.py +6 -0
  20. ormlambda/engine/base.py +26 -4
  21. ormlambda/errors.py +9 -0
  22. ormlambda/model/base_model.py +3 -10
  23. ormlambda/repository/base_repository.py +1 -1
  24. ormlambda/repository/interfaces/IRepositoryBase.py +0 -7
  25. ormlambda/repository/response.py +21 -8
  26. ormlambda/sql/__init__.py +12 -3
  27. ormlambda/sql/clause_info/__init__.py +0 -2
  28. ormlambda/sql/clause_info/clause_info.py +94 -76
  29. ormlambda/sql/clause_info/interface/IAggregate.py +14 -4
  30. ormlambda/sql/clause_info/interface/IClauseInfo.py +6 -11
  31. ormlambda/sql/clauses/alias.py +6 -37
  32. ormlambda/sql/clauses/count.py +21 -36
  33. ormlambda/sql/clauses/group_by.py +13 -19
  34. ormlambda/sql/clauses/having.py +2 -6
  35. ormlambda/sql/clauses/insert.py +3 -3
  36. ormlambda/sql/clauses/interfaces/__init__.py +0 -1
  37. ormlambda/sql/clauses/join/join_context.py +5 -12
  38. ormlambda/sql/clauses/joins.py +34 -52
  39. ormlambda/sql/clauses/limit.py +1 -2
  40. ormlambda/sql/clauses/offset.py +1 -2
  41. ormlambda/sql/clauses/order.py +17 -21
  42. ormlambda/sql/clauses/select.py +56 -28
  43. ormlambda/sql/clauses/update.py +13 -10
  44. ormlambda/sql/clauses/where.py +20 -39
  45. ormlambda/sql/column/__init__.py +1 -0
  46. ormlambda/sql/column/column.py +19 -12
  47. ormlambda/sql/column/column_proxy.py +117 -0
  48. ormlambda/sql/column_table_proxy.py +23 -0
  49. ormlambda/sql/comparer.py +31 -65
  50. ormlambda/sql/compiler.py +248 -58
  51. ormlambda/sql/context/__init__.py +304 -0
  52. ormlambda/sql/ddl.py +19 -5
  53. ormlambda/sql/elements.py +3 -0
  54. ormlambda/sql/foreign_key.py +42 -64
  55. ormlambda/sql/functions/__init__.py +0 -1
  56. ormlambda/sql/functions/concat.py +35 -38
  57. ormlambda/sql/functions/max.py +12 -36
  58. ormlambda/sql/functions/min.py +13 -28
  59. ormlambda/sql/functions/sum.py +17 -33
  60. ormlambda/sql/sqltypes.py +2 -0
  61. ormlambda/sql/table/__init__.py +1 -0
  62. ormlambda/sql/table/table.py +31 -45
  63. ormlambda/sql/table/table_proxy.py +88 -0
  64. ormlambda/sql/type_api.py +4 -1
  65. ormlambda/sql/types.py +15 -12
  66. ormlambda/statements/__init__.py +0 -2
  67. ormlambda/statements/base_statement.py +53 -91
  68. ormlambda/statements/interfaces/IStatements.py +77 -123
  69. ormlambda/statements/interfaces/__init__.py +1 -1
  70. ormlambda/statements/query_builder.py +296 -128
  71. ormlambda/statements/statements.py +122 -115
  72. ormlambda/statements/types.py +5 -25
  73. ormlambda/util/__init__.py +7 -100
  74. ormlambda/util/langhelpers.py +102 -0
  75. ormlambda/util/module_tree/dynamic_module.py +1 -1
  76. ormlambda/util/preloaded.py +80 -0
  77. ormlambda/util/typing.py +12 -3
  78. {ormlambda-3.35.3.dist-info → ormlambda-4.0.4.dist-info}/METADATA +56 -79
  79. ormlambda-4.0.4.dist-info/RECORD +139 -0
  80. ormlambda/common/abstract_classes/clause_info_converter.py +0 -65
  81. ormlambda/common/abstract_classes/decomposition_query.py +0 -141
  82. ormlambda/common/abstract_classes/query_base.py +0 -15
  83. ormlambda/common/interfaces/ICustomAlias.py +0 -7
  84. ormlambda/common/interfaces/IDecompositionQuery.py +0 -33
  85. ormlambda/databases/__init__.py +0 -4
  86. ormlambda/databases/my_sql/__init__.py +0 -3
  87. ormlambda/databases/my_sql/clauses/ST_AsText.py +0 -37
  88. ormlambda/databases/my_sql/clauses/ST_Contains.py +0 -36
  89. ormlambda/databases/my_sql/clauses/__init__.py +0 -14
  90. ormlambda/databases/my_sql/clauses/count.py +0 -33
  91. ormlambda/databases/my_sql/clauses/delete.py +0 -9
  92. ormlambda/databases/my_sql/clauses/drop_table.py +0 -26
  93. ormlambda/databases/my_sql/clauses/group_by.py +0 -17
  94. ormlambda/databases/my_sql/clauses/having.py +0 -12
  95. ormlambda/databases/my_sql/clauses/insert.py +0 -9
  96. ormlambda/databases/my_sql/clauses/joins.py +0 -14
  97. ormlambda/databases/my_sql/clauses/limit.py +0 -6
  98. ormlambda/databases/my_sql/clauses/offset.py +0 -6
  99. ormlambda/databases/my_sql/clauses/order.py +0 -8
  100. ormlambda/databases/my_sql/clauses/update.py +0 -8
  101. ormlambda/databases/my_sql/clauses/upsert.py +0 -9
  102. ormlambda/databases/my_sql/clauses/where.py +0 -7
  103. ormlambda/dialects/interface/__init__.py +0 -1
  104. ormlambda/dialects/interface/dialect.py +0 -78
  105. ormlambda/sql/clause_info/aggregate_function_base.py +0 -96
  106. ormlambda/sql/clause_info/clause_info_context.py +0 -87
  107. ormlambda/sql/clauses/interfaces/ISelect.py +0 -17
  108. ormlambda/sql/clauses/new_join.py +0 -119
  109. ormlambda/util/load_module.py +0 -21
  110. ormlambda/util/plugin_loader.py +0 -32
  111. ormlambda-3.35.3.dist-info/RECORD +0 -159
  112. /ormlambda/{databases/my_sql → dialects/mysql}/caster/__init__.py +0 -0
  113. /ormlambda/{databases/my_sql → dialects/mysql}/caster/caster.py +0 -0
  114. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/__init__.py +0 -0
  115. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/boolean.py +0 -0
  116. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/bytes.py +0 -0
  117. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/date.py +0 -0
  118. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/datetime.py +0 -0
  119. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/decimal.py +0 -0
  120. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/float.py +0 -0
  121. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/int.py +0 -0
  122. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/iterable.py +0 -0
  123. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/json.py +0 -0
  124. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/none.py +0 -0
  125. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/point.py +0 -0
  126. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/string.py +0 -0
  127. /ormlambda/{databases/my_sql → dialects/mysql/repository}/pool_types.py +0 -0
  128. {ormlambda-3.35.3.dist-info → ormlambda-4.0.4.dist-info}/AUTHORS +0 -0
  129. {ormlambda-3.35.3.dist-info → ormlambda-4.0.4.dist-info}/LICENSE +0 -0
  130. {ormlambda-3.35.3.dist-info → ormlambda-4.0.4.dist-info}/WHEEL +0 -0
@@ -1,23 +1,20 @@
1
1
  from __future__ import annotations
2
+ from collections import defaultdict
2
3
  import typing as tp
3
4
  import re
4
5
 
5
- from ormlambda import Table
6
- from ormlambda import Column
7
- from ormlambda.sql.types import (
8
- ASTERISK,
9
- TableType,
10
- ColumnType,
11
- AliasType,
12
- TypeEngine,
13
- )
6
+ from ormlambda.sql.types import ASTERISK
7
+ from ormlambda.errors import DuplicatedClauseName
14
8
  from .interface import IClauseInfo
15
- from ormlambda.sql import ForeignKey
9
+ from ormlambda.common import GlobalChecker
10
+ from ormlambda import util
16
11
 
17
12
 
18
- from .clause_info_context import ClauseInfoContext, ClauseContextType
19
-
20
13
  if tp.TYPE_CHECKING:
14
+ from ormlambda.sql import ForeignKey
15
+ from ormlambda import ColumnProxy
16
+ from ormlambda import Table
17
+ from ormlambda.sql.types import TableType, ColumnType, AliasType
21
18
  from ormlambda.dialects import Dialect
22
19
 
23
20
 
@@ -39,11 +36,9 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
39
36
  @tp.overload
40
37
  def __init__[TProp](self, table: TableType[T], column: ColumnType[TProp]): ...
41
38
  @tp.overload
42
- def __init__[TProp](self, table: TableType[T], column: ColumnType[TProp], alias_table: AliasType[ClauseInfo[T]] = ..., alias_clause: AliasType[ClauseInfo[T]] = ...): ...
43
- @tp.overload
44
- def __init__(self, table: TableType[T], alias_table: AliasType[ClauseInfo[T]] = ..., alias_clause: AliasType[ClauseInfo[T]] = ...): ...
39
+ def __init__[TProp](self, table: TableType[T], column: ColumnType[TProp], alias_table: AliasType[ColumnProxy] = ..., alias_clause: AliasType[ColumnProxy] = ...): ...
45
40
  @tp.overload
46
- def __init__[TProp](self, table: TableType[T], column: ColumnType[TProp], context: ClauseContextType): ...
41
+ def __init__(self, table: TableType[T], alias_table: AliasType[ColumnProxy] = ..., alias_clause: AliasType[ColumnProxy] = ...): ...
47
42
  @tp.overload
48
43
  def __init__(self, table: TableType[T], keep_asterisk: tp.Optional[bool] = ...): ...
49
44
  @tp.overload
@@ -58,12 +53,12 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
58
53
  self,
59
54
  table: TableType[T],
60
55
  column: tp.Optional[ColumnType[TProp]] = None,
61
- alias_table: tp.Optional[AliasType[ClauseInfo[T]]] = None,
62
- alias_clause: tp.Optional[AliasType[ClauseInfo[T]]] = None,
63
- context: ClauseContextType = None,
56
+ alias_table: tp.Optional[AliasType[ColumnProxy]] = None,
57
+ alias_clause: tp.Optional[AliasType[ColumnProxy]] = None,
64
58
  keep_asterisk: bool = False,
65
59
  preserve_context: bool = False,
66
60
  dtype: tp.Optional[TProp] = None,
61
+ literal: bool = False,
67
62
  *,
68
63
  dialect: Dialect,
69
64
  **kw,
@@ -74,12 +69,12 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
74
69
 
75
70
  self._table: TableType[T] = table
76
71
  self._column: TableType[T] | ColumnType[TProp] = column
77
- self._alias_table: tp.Optional[AliasType[ClauseInfo[T]]] = alias_table
78
- self._alias_clause: tp.Optional[AliasType[ClauseInfo[T]]] = alias_clause
79
- self._context: ClauseContextType = context if context else ClauseInfoContext()
72
+ self._alias_table: tp.Optional[AliasType[ColumnProxy]] = alias_table
73
+ self._alias_clause: tp.Optional[AliasType[ColumnProxy]] = alias_clause
80
74
  self._keep_asterisk: bool = keep_asterisk
81
75
  self._preserve_context: bool = preserve_context
82
76
  self._dtype = dtype
77
+ self._literal = literal
83
78
 
84
79
  self._dialect: Dialect = dialect
85
80
 
@@ -88,9 +83,6 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
88
83
  "table": self.replace_table_placeholder,
89
84
  }
90
85
 
91
- if not self._preserve_context and (self._context and any([alias_table, alias_clause])):
92
- self._context.add_clause_to_context(self)
93
-
94
86
  super().__init__(**kw)
95
87
 
96
88
  def __repr__(self) -> str:
@@ -118,7 +110,7 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
118
110
 
119
111
  @property
120
112
  def alias_clause(self) -> tp.Optional[str]:
121
- alias = self._alias_clause if not (a := self.get_clause_alias()) else a
113
+ alias = self._alias_clause
122
114
  return self._alias_resolver(alias)
123
115
 
124
116
  # TODOL [ ]: if we using this setter, we don't update the _context with the new value. Study if it's necessary
@@ -128,7 +120,7 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
128
120
 
129
121
  @property
130
122
  def alias_table(self) -> tp.Optional[str]:
131
- alias = self._alias_table if not (a := self.get_table_alias()) else a
123
+ alias = self._alias_table
132
124
  return self._alias_resolver(alias)
133
125
 
134
126
  # TODOL [ ]: if we using this setter, we don't update the _context with the new value. Study if it's necessary
@@ -147,20 +139,12 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
147
139
  def unresolved_column(self) -> ColumnType:
148
140
  return self._column
149
141
 
150
- @property
151
- def context(self) -> ClauseContextType:
152
- return self._context
153
-
154
- @context.setter
155
- def context(self, value: ClauseInfoContext) -> None:
156
- self._context = value
157
-
158
142
  @property
159
143
  def dtype[TProp](self) -> tp.Optional[tp.Type[TProp]]:
160
144
  if self._dtype is not None:
161
145
  return self._dtype
162
146
 
163
- if isinstance(self._column, Column):
147
+ if self.is_column(self._column):
164
148
  return self._column.dtype
165
149
 
166
150
  if isinstance(self._column, type):
@@ -182,6 +166,9 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
182
166
  def query(self, dialect: Dialect, **kwargs) -> str:
183
167
  return self._create_query(dialect, **kwargs)
184
168
 
169
+ def compile(self, dialect: Dialect, **kwargs) -> str:
170
+ return self._create_query(dialect, **kwargs)
171
+
185
172
  def _create_query(self, dialect: Dialect, **kwargs) -> str:
186
173
  # when passing some value that is not a column name
187
174
  if not self.table and not self._alias_clause:
@@ -190,26 +177,29 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
190
177
  if not self.table and self._alias_clause:
191
178
  # it means that we are passing an object with alias. We should delete '' around the object
192
179
  alias_clause = self.alias_clause
193
- return self._concat_alias_and_column(self._column, alias_clause)
180
+ return self.concat_alias_and_column(self._column, alias_clause)
194
181
 
195
182
  # When passing the Table itself without 'column'
196
183
  if self.table and not self._column:
197
184
  if not self._alias_table:
198
185
  return self.table.__table_name__
199
186
  alias_table = self.alias_table
200
- return self._concat_alias_and_column(self.table.__table_name__, alias_table)
187
+ return self.concat_alias_and_column(self.table.__table_name__, alias_table)
201
188
 
202
189
  if self._return_all_columns():
203
- return self._get_all_columns()
204
- return self._join_table_and_column(self._column)
190
+ return self._get_all_columns(dialect)
191
+ return self._join_table_and_column(self._column, dialect)
205
192
 
206
- def _join_table_and_column[TProp](self, column: ColumnType[TProp]) -> str:
207
- # FIXME [x]: Study how to deacoplate from mysql database
193
+ @util.preload_module("ormlambda.sql")
194
+ def _join_table_and_column[TProp](self, column: ColumnType[TProp], dialect: Dialect) -> str:
195
+ ColumnProxy = util.preloaded.sql_column.ColumnProxy
208
196
 
209
- caster = self._dialect.caster()
197
+ caster = dialect.caster()
210
198
 
211
199
  if self.alias_table:
212
- table = self._wrapped_with_quotes(self.alias_table)
200
+ table = self.wrapped_with_quotes(self.alias_table)
201
+ elif isinstance(column, ColumnProxy):
202
+ table = self.wrapped_with_quotes(column.get_table_chain())
213
203
  else:
214
204
  table = self.table.__table_name__
215
205
 
@@ -219,7 +209,7 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
219
209
 
220
210
  dtype = str if self.is_table(self.dtype) else self.dtype
221
211
  wrapped_column = caster.for_value(table_column, dtype).wildcard_to_select(table_column)
222
- return self._concat_alias_and_column(wrapped_column, self.alias_clause)
212
+ return self.concat_alias_and_column(wrapped_column, self.alias_clause)
223
213
 
224
214
  def _return_all_columns(self) -> bool:
225
215
  if self._keep_asterisk:
@@ -234,22 +224,21 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
234
224
  def is_asterisk(value: tp.Optional[str]) -> bool:
235
225
  return isinstance(value, str) and value == ASTERISK
236
226
 
237
- def _get_all_columns(self) -> str:
227
+ def _get_all_columns(self, dialect: Dialect) -> str:
238
228
  def ClauseCreator(column: str) -> ClauseInfo:
239
229
  return ClauseInfo(
240
230
  table=self.table,
241
231
  column=column,
242
232
  alias_table=self._alias_table,
243
233
  alias_clause=self._alias_clause,
244
- context=self._context,
245
234
  keep_asterisk=self._keep_asterisk,
246
- dialect=self._dialect,
235
+ dialect=dialect,
247
236
  )
248
237
 
249
238
  if self._alias_table and self._alias_clause: # We'll add an "*" when we are certain that we have included 'alias_clause' attr
250
- return self._join_table_and_column(ASTERISK)
239
+ return self._join_table_and_column(ASTERISK, dialect)
251
240
 
252
- columns: list[ClauseInfo] = [ClauseCreator(column).query(self._dialect) for column in self.table.get_columns()]
241
+ columns: list[ClauseInfo] = [ClauseCreator(column).query(dialect) for column in self.table.get_columns()]
253
242
 
254
243
  return ", ".join(columns)
255
244
 
@@ -261,7 +250,7 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
261
250
  if isinstance(column, tp.Iterable) and isinstance(column[0], ClauseInfo):
262
251
  return self.join_clauses(column)
263
252
 
264
- if isinstance(column, Column):
253
+ if self.is_column(column):
265
254
  return column.column_name
266
255
 
267
256
  # if we want to pass the name of a column as a string, the 'table' var must not be None
@@ -281,6 +270,10 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
281
270
  casted_value = caster.for_value(column, self.dtype)
282
271
  if not self._table:
283
272
  # if we haven't some table atrribute, we assume that the user want to retrieve the string_data from caster.
273
+ if self._literal:
274
+ # That condition will be used when you need to pass a value without wrapped in any quote like using HAVING clause
275
+ # COMMENT: Check 'test_complex_1' test
276
+ return casted_value.value
284
277
  return casted_value.string_data
285
278
  return casted_value.wildcard_to_select()
286
279
 
@@ -295,48 +288,63 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
295
288
 
296
289
  return func(self._column)
297
290
 
298
- def _concat_alias_and_column(self, column: str, alias_clause: tp.Optional[str] = None) -> str:
291
+ @classmethod
292
+ def concat_alias_and_column(cls, column: str, alias_clause: tp.Optional[str] = None) -> str:
299
293
  if alias_clause is None:
300
294
  return column
301
- alias = f"{column} AS {self._wrapped_with_quotes(alias_clause)}"
295
+ alias = f"{column} AS {cls.wrapped_with_quotes(alias_clause)}"
302
296
  return alias
303
297
 
304
- def _alias_resolver(self, alias: AliasType[ClauseInfo[T]]) -> tp.Optional[str]:
298
+ def _alias_resolver(self, alias: AliasType[ColumnProxy]) -> tp.Optional[str]:
305
299
  if alias is None:
306
300
  return None
307
301
 
308
302
  if callable(alias):
309
- return self._alias_resolver(alias(self))
303
+ return self._alias_resolver(alias(self._column))
310
304
 
311
305
  return self._replace_placeholder(alias)
312
306
 
313
- def get_clause_alias(self) -> tp.Optional[str]:
314
- if not self._context:
315
- return None
316
- return self._context.get_clause_alias(self)
317
-
318
- def get_table_alias(self) -> tp.Optional[str]:
319
- if not self._context:
320
- return None
321
- return self._context.get_table_alias(self.table)
322
-
323
307
  @staticmethod
324
- def join_clauses(clauses: list[ClauseInfo[T]], chr: str = ",", context: tp.Optional[ClauseInfoContext] = None, *, dialect: Dialect) -> str:
308
+ def join_clauses(clauses: list[ColumnProxy], chr: str = ",", *, dialect: Dialect, **kw) -> str:
309
+ raise_alias_duplicated = False
310
+ all_aliases: dict[str, int] = defaultdict(int)
325
311
  queries: list[str] = []
326
312
  for c in clauses:
327
- if context:
328
- c.context = context
329
- c._dialect = dialect
330
- queries.append(c.query(dialect))
313
+ # That update control the alias we set by default on select clause
314
+ if "alias_clause" in kw:
315
+ alias_clause = kw["alias_clause"]
316
+ elif c.alias:
317
+ alias_clause = c.alias
318
+ else:
319
+ alias_clause = c.column_name
331
320
 
321
+ param = {**kw, "alias_clause": alias_clause}
322
+ compiled = c.compile(dialect, **param)
323
+
324
+ # FIXME [ ]: we use c.alias because we're modifying dynamically when compile the object insdie 'visit_column_proxy' method
325
+ # it's working right though it's not the way to do it.
326
+ NEW_ALIAS = c.alias
327
+ all_aliases[NEW_ALIAS] += 1
328
+
329
+ if all_aliases[NEW_ALIAS] > 1:
330
+ raise_alias_duplicated = True
331
+
332
+ queries.append(compiled.string)
333
+
334
+ if raise_alias_duplicated:
335
+ raise DuplicatedClauseName(tuple(alias for alias, number in all_aliases.items() if number > 1))
332
336
  return f"{chr} ".join(queries)
333
337
 
334
338
  @staticmethod
335
- def _wrapped_with_quotes(string: str) -> str:
336
- return f"`{string}`"
339
+ def wrapped_with_quotes(
340
+ string: str,
341
+ first: str = GlobalChecker.FIRST_QUOTE,
342
+ end: str = GlobalChecker.END_QUOTE,
343
+ ) -> str:
344
+ return f"{first}{string}{end}"
337
345
 
338
346
  @classmethod
339
- def extract_table(cls, element: ColumnType[T] | TableType[T]) -> tp.Optional[T]:
347
+ def extract_table(cls, element: ForeignKey | ColumnType[T] | TableType[T]) -> tp.Optional[T]:
340
348
  if element is None:
341
349
  return None
342
350
 
@@ -346,22 +354,32 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
346
354
  if cls.is_foreign_key(element):
347
355
  return element.tright
348
356
 
349
- if isinstance(element, Column):
357
+ if cls.is_column(element):
350
358
  return element.table
351
359
  return None
352
360
 
361
+ @util.preload_module("ormlambda.sql")
353
362
  @staticmethod
354
363
  def is_table(data: ColumnType | Table | ForeignKey) -> bool:
355
- return isinstance(data, type) and issubclass(data, Table)
364
+ Table = util.preloaded.sql_table.Table
365
+ TableProxy = util.preloaded.sql_table.TableProxy
366
+
367
+ return isinstance(data, type) and issubclass(data, Table | TableProxy)
356
368
 
369
+ @util.preload_module("ormlambda.sql")
357
370
  @staticmethod
358
371
  def is_foreign_key(data: ColumnType | Table | ForeignKey) -> bool:
372
+ ForeignKey = util.preloaded.sql_foreign_key.ForeignKey
359
373
  return isinstance(data, ForeignKey)
360
374
 
375
+ @util.preload_module("ormlambda.sql")
361
376
  @classmethod
362
377
  def is_column(cls, data: tp.Any) -> bool:
378
+ Column = util.preloaded.sql_column.Column
379
+ ColumnProxy = util.preloaded.sql_column.ColumnProxy
380
+
363
381
  if cls.is_table(data) or cls.is_foreign_key(data) or cls.is_asterisk(data):
364
382
  return False
365
- if isinstance(data, Column):
383
+ if isinstance(data, Column | ColumnProxy):
366
384
  return True
367
385
  return False
@@ -1,10 +1,20 @@
1
1
  from __future__ import annotations
2
2
  import abc
3
+ from typing import Any, Iterable, TYPE_CHECKING
3
4
 
4
- from ormlambda.common.interfaces.IQueryCommand import IQuery
5
+ if TYPE_CHECKING:
6
+ from ormlambda import ColumnProxy
5
7
 
6
8
 
7
- class IAggregate(IQuery):
8
- @classmethod
9
+ class IAggregate():
10
+ alias: str
11
+
12
+ def __repr__(self):
13
+ return f"{IAggregate.__name__}: {type(self).__name__}"
14
+
15
+ @abc.abstractmethod
16
+ def used_columns(self) -> Iterable[ColumnProxy]: ...
17
+
18
+ @property
9
19
  @abc.abstractmethod
10
- def FUNCTION_NAME(cls) -> str: ...
20
+ def dtype(self) -> Any: ...
@@ -1,16 +1,14 @@
1
1
  from __future__ import annotations
2
2
  import abc
3
- import typing as tp
4
-
5
- from ormlambda import Table
3
+ from typing import Optional, Type, TYPE_CHECKING
6
4
  from ormlambda.common.interfaces.IQueryCommand import IQuery
7
5
  from ormlambda.sql.types import (
8
6
  TableType,
9
7
  ColumnType,
10
8
  )
11
9
 
12
-
13
- from ..clause_info_context import ClauseContextType
10
+ if TYPE_CHECKING:
11
+ from ormlambda import Table
14
12
 
15
13
 
16
14
  class IClauseInfo[T: Table](IQuery):
@@ -19,10 +17,10 @@ class IClauseInfo[T: Table](IQuery):
19
17
  def table(self) -> TableType[T]: ...
20
18
  @property
21
19
  @abc.abstractmethod
22
- def alias_clause(self) -> tp.Optional[str]: ...
20
+ def alias_clause(self) -> Optional[str]: ...
23
21
  @property
24
22
  @abc.abstractmethod
25
- def alias_table(self) -> tp.Optional[str]: ...
23
+ def alias_table(self) -> Optional[str]: ...
26
24
  @property
27
25
  @abc.abstractmethod
28
26
  def column(self) -> str: ...
@@ -31,7 +29,4 @@ class IClauseInfo[T: Table](IQuery):
31
29
  def unresolved_column(self) -> ColumnType: ...
32
30
  @property
33
31
  @abc.abstractmethod
34
- def context(self) -> ClauseContextType: ...
35
- @property
36
- @abc.abstractmethod
37
- def dtype[TProp](self) -> tp.Optional[tp.Type[TProp]]: ...
32
+ def dtype[TProp](self) -> Optional[Type[TProp]]: ...
@@ -1,45 +1,14 @@
1
1
  from __future__ import annotations
2
- import typing as tp
3
2
 
4
- from ormlambda.sql.clause_info import ClauseInfo
5
- from ormlambda.sql.elements import ClauseElement
3
+ from ormlambda import ColumnProxy
6
4
 
7
5
 
8
- if tp.TYPE_CHECKING:
9
- from ormlambda import Table
10
- from ormlambda.sql.clause_info.clause_info_context import ClauseContextType
11
- from ormlambda.sql.types import TableType
12
- from ormlambda.sql.types import ColumnType
13
- from ormlambda.sql.types import AliasType
6
+ class Alias[T](ColumnProxy[T]):
7
+ def __init__(self, column: ColumnProxy[T], alias: str):
8
+ super().__init__(column._column, path=column.path, alias=alias)
14
9
 
15
-
16
- class Alias[T: Table](ClauseInfo[T], ClauseElement):
17
- __visit_name__ = "alias"
18
-
19
- def __init__[TProp](
20
- self,
21
- table: TableType[T],
22
- column: tp.Optional[ColumnType[TProp]] = None,
23
- alias_table: tp.Optional[AliasType[ClauseInfo[T]]] = None,
24
- alias_clause: tp.Optional[AliasType[ClauseInfo[T]]] = None,
25
- context: ClauseContextType = None,
26
- keep_asterisk: bool = False,
27
- preserve_context: bool = False,
28
- **kw,
29
- ):
30
- if not alias_clause:
31
- raise TypeError
32
- super().__init__(
33
- table,
34
- column,
35
- alias_table=alias_table,
36
- alias_clause=alias_clause,
37
- context=context,
38
- keep_asterisk=keep_asterisk,
39
- preserve_context=preserve_context,
40
- dtype=None,
41
- **kw,
42
- )
10
+ def __repr__(self):
11
+ return f"{Alias.__name__}: {super().__repr__()}"
43
12
 
44
13
 
45
14
  __all__ = ["Alias"]
@@ -1,57 +1,42 @@
1
1
  from __future__ import annotations
2
- from ormlambda.sql.clause_info import AggregateFunctionBase
3
- from ormlambda.sql.clause_info.clause_info_context import ClauseContextType
2
+ from ormlambda.sql.clause_info import IAggregate
4
3
 
5
- from ormlambda.sql.types import AliasType, ColumnType
4
+ from ormlambda.sql.types import ColumnType
6
5
 
7
6
  from ormlambda import Table
8
7
 
9
8
  import typing as tp
10
9
 
11
- from ormlambda.sql.types import ASTERISK
12
10
  from ormlambda.sql.elements import ClauseElement
13
11
 
14
12
 
15
13
  if tp.TYPE_CHECKING:
16
14
  from ormlambda import Table
17
- from ormlambda.sql.types import ColumnType, AliasType, TableType
18
- from ormlambda.dialects import Dialect
15
+ from ormlambda.sql.types import ColumnType, TableType
19
16
 
20
17
 
21
- class Count[T: Table](AggregateFunctionBase[T], ClauseElement):
18
+ class Count[T: Table](ClauseElement, IAggregate):
22
19
  __visit_name__ = "count"
23
20
 
24
- @staticmethod
25
- def FUNCTION_NAME() -> str:
26
- return "COUNT"
27
-
28
- def __init__[TProp: Table](
21
+ def __init__[TProp](
29
22
  self,
30
- element: ColumnType[T] | TableType[TProp],
31
- alias_table: AliasType[ColumnType[TProp]] = None,
32
- alias_clause: AliasType[ColumnType[TProp]] = "count",
33
- context: ClauseContextType = None,
34
- keep_asterisk: bool = True,
35
- preserve_context: bool = True,
36
- *,
37
- dialect: Dialect,
38
- **kw,
23
+ element: str | ColumnType[TProp] | TableType[T] = "*",
24
+ alias: str = "count",
39
25
  ) -> None:
40
- table = self.extract_table(element)
41
- column = element if self.is_column(element) else ASTERISK
42
-
43
- super().__init__(
44
- table=table if (alias_table or (context and table in context._table_context)) else None,
45
- column=column,
46
- alias_table=alias_table,
47
- alias_clause=alias_clause,
48
- context=context,
49
- keep_asterisk=keep_asterisk,
50
- preserve_context=preserve_context,
51
- dtype=int,
52
- dialect=dialect,
53
- **kw,
54
- )
26
+ self.column = element
27
+ self.alias = alias
28
+
29
+ @property
30
+ def dtype(self) -> str:
31
+ return int
32
+
33
+ def used_columns(self) -> tp.Iterable:
34
+ if isinstance(self.column, str):
35
+ # If is str, probably will by because we're using * so we don't have to retrieve something
36
+ return []
37
+ if not isinstance(self.column, tp.Iterable):
38
+ return [self.column]
39
+ return self.column
55
40
 
56
41
 
57
42
  __all__ = ["Count"]
@@ -1,30 +1,24 @@
1
1
  from __future__ import annotations
2
- from typing import TYPE_CHECKING
2
+ from typing import Any, Iterable
3
+
4
+ from ormlambda.sql.clause_info import IAggregate
3
5
 
4
- if TYPE_CHECKING:
5
- from ormlambda.dialects import Dialect
6
- from ormlambda.sql.clause_info import AggregateFunctionBase, ClauseInfoContext
7
6
  from ormlambda.sql.types import ColumnType
8
7
  from ormlambda.sql.elements import ClauseElement
9
8
 
10
9
 
11
- class GroupBy(AggregateFunctionBase, ClauseElement):
10
+ class GroupBy(ClauseElement, IAggregate):
12
11
  __visit_name__ = "group_by"
13
12
 
14
- @classmethod
15
- def FUNCTION_NAME(self) -> str:
16
- return "GROUP BY"
17
-
18
- def __init__(self, column: ColumnType, context: ClauseInfoContext, dialect: Dialect, **kwargs):
19
- super().__init__(
20
- table=column.table,
21
- column=column,
22
- alias_table=None,
23
- alias_clause=None,
24
- context=context,
25
- dialect=dialect,
26
- **kwargs,
27
- )
13
+ def __init__(self, *column: ColumnType):
14
+ self.column: tuple[ColumnType] = column if isinstance(column, Iterable) else [column]
15
+
16
+ def used_columns(self):
17
+ return self.column
18
+
19
+ @property
20
+ def dtype(self) -> Any:
21
+ return ...
28
22
 
29
23
 
30
24
  __all__ = ["GroupBy"]
@@ -10,12 +10,8 @@ class Having(Where):
10
10
 
11
11
  __visit_name__ = "having"
12
12
 
13
- def __init__(self, *comparer, restrictive=True, context=None, **kw):
14
- super().__init__(*comparer, restrictive=restrictive, context=context, **kw)
15
-
16
- @staticmethod
17
- def FUNCTION_NAME() -> str:
18
- return "HAVING"
13
+ def __init__(self, *comparer, restrictive=True, **kw):
14
+ super().__init__(*comparer, restrictive=restrictive, **kw)
19
15
 
20
16
 
21
17
  __all__ = ["Having"]
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from typing import override, Iterable, TYPE_CHECKING
4
4
 
5
5
  from ormlambda import Table
6
- from ormlambda import Column
6
+ from ormlambda import Column, ColumnProxy
7
7
 
8
8
  from ormlambda.sql.clauses.interfaces import IInsert
9
9
  from ormlambda.common.abstract_classes import NonQueryBase
@@ -53,7 +53,7 @@ class Insert[T: Table, TRepo](NonQueryBase[T, TRepo], IInsert[T], ClauseElement)
53
53
  col_values[-1].append(clean_data.to_database)
54
54
 
55
55
  join_cols = ", ".join(col_names)
56
- unknown_rows = f'({", ".join(wildcards)})' # The number of "%s" must match the dict 'dicc_0' length
56
+ unknown_rows = f"({', '.join(wildcards)})" # The number of "%s" must match the dict 'dicc_0' length
57
57
 
58
58
  self._values = [tuple(x) for x in col_values]
59
59
  self._query = f"{self.CLAUSE} {self._model.__table_name__} {f'({join_cols})'} VALUES {unknown_rows}"
@@ -88,7 +88,7 @@ class Insert[T: Table, TRepo](NonQueryBase[T, TRepo], IInsert[T], ClauseElement)
88
88
  elif issubclass(values.__class__, Table):
89
89
  new_list = []
90
90
  for prop in type(values).__dict__.values():
91
- if not isinstance(prop, Column):
91
+ if not isinstance(prop, Column | ColumnProxy):
92
92
  continue
93
93
 
94
94
  value = getattr(values, prop.column_name)
@@ -1,5 +1,4 @@
1
1
  from .IDelete import IDelete # noqa: F401
2
2
  from .IInsert import IInsert # noqa: F401
3
- from .ISelect import ISelect # noqa: F401
4
3
  from .IUpdate import IUpdate # noqa: F401
5
4
  from .IUpsert import IUpsert # noqa: F401