ormlambda 3.11.1__py3-none-any.whl → 3.12.2__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 (33) hide show
  1. ormlambda/__init__.py +1 -1
  2. ormlambda/caster/caster.py +1 -1
  3. ormlambda/common/abstract_classes/clause_info_converter.py +73 -0
  4. ormlambda/common/abstract_classes/decomposition_query.py +12 -68
  5. ormlambda/common/abstract_classes/non_query_base.py +2 -2
  6. ormlambda/common/interfaces/ICustomAlias.py +1 -1
  7. ormlambda/components/delete/abstract_delete.py +2 -2
  8. ormlambda/components/join/__init__.py +1 -0
  9. ormlambda/databases/my_sql/clauses/delete.py +0 -1
  10. ormlambda/databases/my_sql/clauses/drop_table.py +8 -5
  11. ormlambda/databases/my_sql/clauses/group_by.py +1 -2
  12. ormlambda/databases/my_sql/clauses/select.py +2 -0
  13. ormlambda/databases/my_sql/clauses/upsert.py +8 -4
  14. ormlambda/databases/my_sql/query_builder.py +158 -0
  15. ormlambda/databases/my_sql/statements.py +19 -156
  16. ormlambda/engine/__init__.py +1 -1
  17. ormlambda/engine/url.py +4 -1
  18. ormlambda/sql/clause_info/__init__.py +2 -1
  19. ormlambda/sql/clause_info/aggregate_function_base.py +86 -0
  20. ormlambda/sql/clause_info/clause_info.py +1 -98
  21. ormlambda/sql/clause_info/interface/IClauseInfo.py +37 -0
  22. ormlambda/sql/clause_info/interface/__init__.py +1 -0
  23. ormlambda/sql/column.py +3 -0
  24. ormlambda/statements/base_statement.py +6 -2
  25. ormlambda/statements/interfaces/IStatements.py +25 -19
  26. ormlambda/utils/module_tree/dynamic_module.py +3 -2
  27. {ormlambda-3.11.1.dist-info → ormlambda-3.12.2.dist-info}/METADATA +66 -16
  28. {ormlambda-3.11.1.dist-info → ormlambda-3.12.2.dist-info}/RECORD +31 -28
  29. {ormlambda-3.11.1.dist-info → ormlambda-3.12.2.dist-info}/WHEEL +1 -1
  30. ormlambda/databases/my_sql/caster/read.py +0 -39
  31. ormlambda/databases/my_sql/caster/write.py +0 -37
  32. /ormlambda/{databases/my_sql → components/join}/join_context.py +0 -0
  33. {ormlambda-3.11.1.dist-info → ormlambda-3.12.2.dist-info}/LICENSE +0 -0
@@ -1,23 +1,21 @@
1
1
  from __future__ import annotations
2
- from typing import Concatenate, Iterable, TypedDict, override, Type, TYPE_CHECKING, Any, Callable, Optional
2
+ from typing import Concatenate, Iterable, override, Type, TYPE_CHECKING, Any, Callable, Optional
3
3
  from mysql.connector import errors, errorcode
4
4
 
5
- from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext
6
- from ormlambda.databases.my_sql.clauses.joins import JoinSelector
7
5
  from ormlambda import ForeignKey
8
6
 
9
- from ormlambda.common.interfaces import IQuery
10
7
  from mysql.connector import MySQLConnection
11
8
 
12
9
 
13
10
  if TYPE_CHECKING:
11
+ from ormlambda.sql.types import AliasType
14
12
  from ormlambda import Table
15
13
  from ormlambda.statements.types import OrderTypes
16
14
  from ormlambda.sql.types import ColumnType
17
15
  from ormlambda.statements.types import SelectCols
18
- from ormlambda.repository.interfaces import IRepositoryBase
16
+ from ormlambda.repository import BaseRepository
19
17
  from ormlambda.statements.interfaces import IStatements_two_generic
20
- from ormlambda.repository.interfaces.IRepositoryBase import TypeExists
18
+ from ormlambda.statements.types import TypeExists
21
19
  from ormlambda.sql.clause_info import IAggregate
22
20
  from ormlambda.statements.types import WhereTypes
23
21
 
@@ -43,8 +41,10 @@ from .clauses import Alias
43
41
  from ormlambda import Table, Column
44
42
  from ormlambda.common.enums import JoinType
45
43
  from . import functions as func
46
- from .join_context import JoinContext, TupleJoinType
44
+ from ormlambda.components.join import JoinContext, TupleJoinType
45
+
47
46
  from ormlambda.common.global_checker import GlobalChecker
47
+ from .query_builder import QueryBuilder
48
48
 
49
49
 
50
50
  # COMMENT: It's so important to prevent information generated by other tests from being retained in the class.
@@ -62,152 +62,11 @@ def clear_list[T, **P](f: Callable[Concatenate[MySQLStatements, P], T]) -> Calla
62
62
  return wrapper
63
63
 
64
64
 
65
- class OrderType(TypedDict):
66
- Select: Select
67
- JoinSelector: JoinSelector
68
- Where: Where
69
- Order: Order
70
- GroupBy: GroupBy
71
- Having: Having
72
- Limit: Limit
73
- Offset: Offset
74
-
75
-
76
- class QueryBuilder(IQuery):
77
- __order__: tuple[str, ...] = ("Select", "JoinSelector", "Where", "GroupBy", "Having", "Order", "Limit", "Offset")
78
-
79
- def __init__(self, by: JoinType = JoinType.INNER_JOIN):
80
- self._context = ClauseInfoContext()
81
- self._query_list: OrderType = {}
82
- self._by = by
83
-
84
- self._joins: Optional[IQuery] = None
85
- self._select: Optional[IQuery] = None
86
- self._where: Optional[IQuery] = None
87
- self._order: Optional[IQuery] = None
88
- self._group_by: Optional[IQuery] = None
89
- self._limit: Optional[IQuery] = None
90
- self._offset: Optional[IQuery] = None
91
-
92
- def add_statement[T](self, clause: ClauseInfo[T]):
93
- self.update_context(clause)
94
- self._query_list[type(clause).__name__] = clause
95
-
96
- @property
97
- def by(self) -> JoinType:
98
- return self._by
99
-
100
- @by.setter
101
- def by(self, value: JoinType) -> None:
102
- self._by = value
103
-
104
- @property
105
- def JOINS(self) -> Optional[set[JoinSelector]]:
106
- return self._joins
107
-
108
- @property
109
- def SELECT(self) -> IQuery:
110
- return self._query_list.get("Select", None)
111
-
112
- @property
113
- def WHERE(self) -> IQuery:
114
- where = self._query_list.get("Where", None)
115
- if not isinstance(where, Iterable):
116
- if not where:
117
- return ()
118
- return (where,)
119
- return where
120
-
121
- @property
122
- def ORDER(self) -> IQuery:
123
- return self._query_list.get("Order", None)
124
-
125
- @property
126
- def GROUP_BY(self) -> IQuery:
127
- return self._query_list.get("GroupBy", None)
128
-
129
- @property
130
- def HAVING(self) -> IQuery:
131
- where = self._query_list.get("Having", None)
132
- if not isinstance(where, Iterable):
133
- if not where:
134
- return ()
135
- return (where,)
136
- return where
137
-
138
- @property
139
- def LIMIT(self) -> IQuery:
140
- return self._query_list.get("Limit", None)
141
-
142
- @property
143
- def OFFSET(self) -> IQuery:
144
- return self._query_list.get("Offset", None)
145
-
146
- @property
147
- def query(self) -> str:
148
- # COMMENT: (select.query, query)We must first create an alias for 'FROM' and then define all the remaining clauses.
149
- # This order is mandatory because it adds the clause name to the context when accessing the .query property of 'FROM'
150
-
151
- extract_joins = self.pop_tables_and_create_joins_from_ForeignKey(self._by)
152
-
153
- JOINS = self.stringify_foreign_key(extract_joins, " ")
154
- query_list: tuple[str, ...] = (
155
- self.SELECT.query,
156
- JOINS,
157
- Where.join_condition(self.WHERE, True, self._context) if self.WHERE else None,
158
- self.GROUP_BY.query if self.GROUP_BY else None,
159
- Having.join_condition(self.HAVING, True, self._context) if self.HAVING else None,
160
- self.ORDER.query if self.ORDER else None,
161
- self.LIMIT.query if self.LIMIT else None,
162
- self.OFFSET.query if self.OFFSET else None,
163
- )
164
- return " ".join([x for x in query_list if x])
165
-
166
- def stringify_foreign_key(self, joins: set[JoinSelector], sep: str = "\n") -> Optional[str]:
167
- if not joins:
168
- return None
169
- sorted_joins = JoinSelector.sort_join_selectors(joins)
170
- return f"{sep}".join([join.query for join in sorted_joins])
171
-
172
- def pop_tables_and_create_joins_from_ForeignKey(self, by: JoinType = JoinType.INNER_JOIN) -> set[JoinSelector]:
173
- # When we applied filters in any table that we wont select any column, we need to add manually all neccessary joins to achieve positive result.
174
- if not ForeignKey.stored_calls:
175
- return None
176
-
177
- joins = set()
178
- # Always it's gonna be a set of two
179
- # FIXME [x]: Resolved when we get Compare object instead ClauseInfo. For instance, when we have multiples condition using '&' or '|'
180
- for fk in ForeignKey.stored_calls.copy():
181
- fk = ForeignKey.stored_calls.pop(fk)
182
- self._context._add_table_alias(fk.tright, fk.alias)
183
- join = JoinSelector(fk.resolved_function(self._context), by, context=self._context, alias=fk.alias)
184
- joins.add(join)
185
-
186
- return joins
187
-
188
- def clear(self) -> None:
189
- self.__init__()
190
- return None
191
-
192
- def update_context(self, clause: ClauseInfo) -> None:
193
- if not hasattr(clause, "context"):
194
- return None
195
-
196
- if clause.context is not None:
197
- self._context.update(clause.context)
198
- clause.context = self._context
199
-
200
-
201
65
  class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
202
- def __init__(self, model: tuple[T, *Ts], repository: IRepositoryBase) -> None:
66
+ def __init__(self, model: tuple[T, *Ts], repository: BaseRepository) -> None:
203
67
  super().__init__(model, repository=repository)
204
68
  self._query_builder = QueryBuilder()
205
69
 
206
- @property
207
- @override
208
- def repository(self) -> IRepositoryBase:
209
- return self._repository
210
-
211
70
  @override
212
71
  def create_table(self, if_exists: TypeExists = "fail") -> None:
213
72
  name: str = self._model.__table_name__
@@ -231,10 +90,6 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
231
90
  self._repository.execute(query)
232
91
  return None
233
92
 
234
- @override
235
- def table_exists(self) -> bool:
236
- return self._repository.table_exists(self._model.__table_name__)
237
-
238
93
  @override
239
94
  @clear_list
240
95
  def insert(self, instances: T | list[T]) -> None:
@@ -292,7 +147,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
292
147
  @override
293
148
  def count[TProp](
294
149
  self,
295
- selection: None | SelectCols[T,TProp] = lambda x: "*",
150
+ selection: None | SelectCols[T, TProp] = lambda x: "*",
296
151
  alias="count",
297
152
  execute: bool = False,
298
153
  ) -> Optional[int]:
@@ -385,6 +240,9 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
385
240
  by: JoinType = JoinType.INNER_JOIN,
386
241
  **kwargs,
387
242
  ):
243
+ if "alias" in kwargs:
244
+ alias = kwargs.pop("alias")
245
+ kwargs["alias_clause"] = alias
388
246
  select_clause = GlobalChecker.resolved_callback_object(selector, self._models)
389
247
 
390
248
  if selector is None:
@@ -399,6 +257,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
399
257
  select = Select[T, *Ts](
400
258
  self._models,
401
259
  columns=select_clause,
260
+ **kwargs,
402
261
  )
403
262
  self._query_builder.add_statement(select)
404
263
 
@@ -457,14 +316,18 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
457
316
  )
458
317
 
459
318
  @override
460
- def groupby[TProp](self, column: ColumnType[TProp] | Callable[[T, *Ts], Any]):
319
+ def groupby[TProp](self, column: ColumnType[TProp] | Callable[[T, *Ts], Any]) -> IStatements_two_generic[T]:
320
+ column = GlobalChecker.resolved_callback_object(column, self.models)
321
+
461
322
  groupby = GroupBy(column=column, context=self._query_builder._context)
462
323
  # Only can be one LIMIT SQL parameter. We only use the last LimitQuery
463
324
  self._query_builder.add_statement(groupby)
464
325
  return self
465
326
 
466
327
  @override
467
- def alias[TProp](self, column: ColumnType[TProp], alias: str) -> ClauseInfo[T, TProp]:
328
+ def alias[TProp](self, column: SelectCols[T, TProp], alias: AliasType[ClauseInfo[T]]) -> ClauseInfo[T]:
329
+ column = GlobalChecker.resolved_callback_object(column, self.models)
330
+
468
331
  return Alias(
469
332
  table=column.table,
470
333
  column=column,
@@ -1,2 +1,2 @@
1
1
  from .create import create_engine # noqa: F401
2
- from .url import URL # noqa: F401
2
+ from .url import URL, make_url # noqa: F401
ormlambda/engine/url.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """
2
2
  URL class extracted from SQLAlchemy
3
3
  """
4
+
4
5
  from __future__ import annotations
5
6
 
6
7
  import re
@@ -27,7 +28,9 @@ from urllib.parse import (
27
28
 
28
29
  from . import utils
29
30
 
30
- type DrivernameType = Literal['mysql'] | str
31
+ type DrivernameType = Literal["mysql"] | str
32
+
33
+
31
34
  class URL(NamedTuple):
32
35
  """
33
36
  Represent the components of a URL used to connect to a database.
@@ -1,3 +1,4 @@
1
1
  from .interface import IAggregate # noqa: F401
2
- from .clause_info import ClauseInfo, AggregateFunctionBase # noqa: F401
2
+ from .clause_info import ClauseInfo # noqa: F401
3
+ from .aggregate_function_base import AggregateFunctionBase # noqa: F401
3
4
  from .clause_info_context import ClauseContextType, ClauseInfoContext # noqa: F401
@@ -0,0 +1,86 @@
1
+ from __future__ import annotations
2
+ import abc
3
+ import typing as tp
4
+
5
+ from ormlambda import Table
6
+ from ormlambda import Column
7
+ from ormlambda.sql.types import (
8
+ TableType,
9
+ ColumnType,
10
+ AliasType,
11
+ )
12
+ from .interface import IAggregate
13
+ from ormlambda.common.errors import NotKeysInIAggregateError
14
+ from ormlambda.sql import ForeignKey
15
+ from ormlambda.sql.table import TableMeta
16
+ from .clause_info import ClauseInfo
17
+ from .clause_info_context import ClauseContextType
18
+
19
+
20
+ class AggregateFunctionBase[T: Table](ClauseInfo[T], IAggregate):
21
+ def __init__[TProp: Column](
22
+ self,
23
+ table: TableType[T],
24
+ column: tp.Optional[ColumnType[TProp]] = None,
25
+ alias_table: tp.Optional[AliasType[ClauseInfo[T]]] = None,
26
+ alias_clause: tp.Optional[AliasType[ClauseInfo[T]]] = None,
27
+ context: ClauseContextType = None,
28
+ keep_asterisk: bool = False,
29
+ preserve_context: bool = False,
30
+ ):
31
+ self._alias_aggregate = alias_clause
32
+ super().__init__(
33
+ table=table,
34
+ column=column,
35
+ alias_table=alias_table,
36
+ context=context,
37
+ keep_asterisk=keep_asterisk,
38
+ preserve_context=preserve_context,
39
+ )
40
+
41
+ @staticmethod
42
+ @abc.abstractmethod
43
+ def FUNCTION_NAME() -> str: ...
44
+
45
+ @classmethod
46
+ def _convert_into_clauseInfo[TypeColumns, TProp](cls, columns: ClauseInfo | ColumnType[TProp], context: ClauseContextType) -> list[ClauseInfo]:
47
+ type DEFAULT = tp.Literal["default"]
48
+ type ClusterType = ColumnType | ForeignKey | DEFAULT
49
+
50
+ dicc_type: dict[ClusterType, tp.Callable[[ClusterType], ClauseInfo]] = {
51
+ Column: lambda column: ClauseInfo(column.table, column, context=context),
52
+ ClauseInfo: lambda column: column,
53
+ ForeignKey: lambda tbl: ClauseInfo(tbl.tright, tbl.tright, context=context),
54
+ TableMeta: lambda tbl: ClauseInfo(tbl, tbl, context=context),
55
+ "default": lambda column: ClauseInfo(table=None, column=column, context=context),
56
+ }
57
+ all_clauses: list[ClauseInfo] = []
58
+ if isinstance(columns, str) or not isinstance(columns, tp.Iterable):
59
+ columns = (columns,)
60
+ for value in columns:
61
+ all_clauses.append(dicc_type.get(type(value), dicc_type["default"])(value))
62
+
63
+ return all_clauses
64
+
65
+ @tp.override
66
+ @property
67
+ def query(self) -> str:
68
+ wrapped_ci = self.wrapped_clause_info(self)
69
+ if not self._alias_aggregate:
70
+ return wrapped_ci
71
+
72
+ return ClauseInfo(
73
+ table=None,
74
+ column=wrapped_ci,
75
+ alias_clause=self._alias_aggregate,
76
+ context=self._context,
77
+ keep_asterisk=self._keep_asterisk,
78
+ preserve_context=self._preserve_context,
79
+ ).query
80
+
81
+ def wrapped_clause_info(self, ci: ClauseInfo[T]) -> str:
82
+ # avoid use placeholder when using IAggregate because no make sense.
83
+ if self._alias_aggregate and (found := self._keyRegex.findall(self._alias_aggregate)):
84
+ raise NotKeysInIAggregateError(found)
85
+
86
+ return f"{self.FUNCTION_NAME()}({ci._create_query()})"
@@ -1,21 +1,17 @@
1
1
  from __future__ import annotations
2
- import abc
3
2
  import typing as tp
4
3
  import re
5
4
 
6
5
  from ormlambda import Table
7
6
  from ormlambda import Column
8
- from ormlambda.common.interfaces.IQueryCommand import IQuery
9
7
  from ormlambda.sql.types import (
10
8
  ASTERISK,
11
9
  TableType,
12
10
  ColumnType,
13
11
  AliasType,
14
12
  )
15
- from .interface import IAggregate
16
- from ormlambda.common.errors import NotKeysInIAggregateError
13
+ from .interface import IClauseInfo
17
14
  from ormlambda.sql import ForeignKey
18
- from ormlambda.sql.table import TableMeta
19
15
  from ormlambda.caster import Caster
20
16
 
21
17
 
@@ -32,30 +28,6 @@ class ReplacePlaceholderError(ValueError):
32
28
  return "You cannot use {" + self.placeholder + "} placeholder without using '" + self.attr + "' attribute"
33
29
 
34
30
 
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
31
  class ClauseInfo[T: Table](IClauseInfo[T]):
60
32
  _keyRegex: re.Pattern = re.compile(r"{([^{}:]+)}")
61
33
 
@@ -363,72 +335,3 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
363
335
  if isinstance(data, Column):
364
336
  return True
365
337
  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,37 @@
1
+ from __future__ import annotations
2
+ import abc
3
+ import typing as tp
4
+
5
+ from ormlambda import Table
6
+ from ormlambda.common.interfaces.IQueryCommand import IQuery
7
+ from ormlambda.sql.types import (
8
+ TableType,
9
+ ColumnType,
10
+ )
11
+
12
+
13
+ from ..clause_info_context import ClauseContextType
14
+
15
+
16
+ class IClauseInfo[T: Table](IQuery):
17
+ @property
18
+ @abc.abstractmethod
19
+ def table(self) -> TableType[T]: ...
20
+ @property
21
+ @abc.abstractmethod
22
+ def alias_clause(self) -> tp.Optional[str]: ...
23
+ @property
24
+ @abc.abstractmethod
25
+ def alias_table(self) -> tp.Optional[str]: ...
26
+ @property
27
+ @abc.abstractmethod
28
+ def column(self) -> str: ...
29
+ @property
30
+ @abc.abstractmethod
31
+ def unresolved_column(self) -> ColumnType: ...
32
+ @property
33
+ @abc.abstractmethod
34
+ def context(self) -> ClauseContextType: ...
35
+ @property
36
+ @abc.abstractmethod
37
+ def dtype[TProp](self) -> tp.Optional[tp.Type[TProp]]: ...
@@ -1 +1,2 @@
1
1
  from .IAggregate import IAggregate # noqa: F401
2
+ from .IClauseInfo import IClauseInfo # noqa: F401
ormlambda/sql/column.py CHANGED
@@ -128,6 +128,9 @@ class Column[TProp]:
128
128
  return self.__comparer_creator(other, ConditionType.GREATER_THAN_OR_EQUAL.value, *args)
129
129
 
130
130
  def contains[LTable, OTherTable, OtherProp](self, other: ColumnType[OtherProp], *args) -> Comparer[LTable, TProp, OTherTable, OtherProp]:
131
+ if not isinstance(other, tuple) and isinstance(other, Iterable):
132
+ other = tuple(other)
133
+
131
134
  return self.__comparer_creator(other, ConditionType.IN.value, *args)
132
135
 
133
136
  def not_contains[LTable, OTherTable, OtherProp](self, other: ColumnType[OtherProp], *args) -> Comparer[LTable, TProp, OTherTable, OtherProp]:
@@ -33,6 +33,10 @@ class BaseStatement[T: Table, TRepo](IStatements_two_generic[T, TRepo]):
33
33
  # Si no heredase de Table no sabriamos identificar el tipo de dato del que se trata porque al llamar a isinstance, obtendriamos el nombre de la clase que mapea a la tabla, Encargo, Edificio, Presupuesto y no podriamos crear una clase generica
34
34
  raise Exception(f"'{model}' class does not inherit from Table class")
35
35
 
36
+ @override
37
+ def table_exists(self) -> bool:
38
+ return self._repository.table_exists(self._model.__table_name__)
39
+
36
40
  @staticmethod
37
41
  def __valid_repository(repository: Any) -> bool:
38
42
  if not isinstance(repository, BaseRepository):
@@ -67,8 +71,8 @@ class BaseStatement[T: Table, TRepo](IStatements_two_generic[T, TRepo]):
67
71
  return self._models
68
72
 
69
73
  @property
70
- @override
71
- def repository(self) -> BaseRepository: ...
74
+ def repository(self) -> BaseRepository:
75
+ return self._repository
72
76
 
73
77
 
74
78
  class ClusterQuery[T]:
@@ -4,14 +4,16 @@ from enum import Enum
4
4
  from abc import abstractmethod, ABC
5
5
 
6
6
 
7
- from ormlambda.common.enums import JoinType
8
-
9
7
  if TYPE_CHECKING:
10
8
  from ormlambda.repository import BaseRepository
11
9
  from ormlambda import Table
12
10
  from ormlambda.sql.clause_info import IAggregate
13
11
  from ormlambda.sql.types import TupleJoinType, ColumnType
14
- from ormlambda.databases.my_sql.join_context import JoinContext
12
+ from ormlambda.components.join import JoinContext
13
+ from ormlambda.common.enums import JoinType
14
+ from ormlambda.sql.clause_info import ClauseInfo
15
+ from ormlambda.sql.types import AliasType
16
+
15
17
 
16
18
  from ..types import (
17
19
  OrderTypes,
@@ -246,33 +248,33 @@ class IStatements[T: Table](ABC):
246
248
  # @overload
247
249
  # def select[TFlavour](self, selector: Optional[Callable[[T], tuple]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> TFlavour: ...
248
250
  @overload
249
- def select[TRes](self, selector: SelectorFlavourType[T, TRes] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[TRes, ...]: ...
251
+ def select[TRes](self, selector: SelectorFlavourType[T, TRes] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> tuple[TRes, ...]: ...
250
252
  @overload
251
- def select[*TRes](self, selector: SelectorFlavourType[T, tuple[*TRes]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[tuple[*TRes]]: ...
253
+ def select[*TRes](self, selector: SelectorFlavourType[T, tuple[*TRes]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> tuple[tuple[*TRes]]: ...
252
254
  @overload
253
- def select[TFlavour](self, selector: SelectorFlavourType[T, tuple] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> tuple[TFlavour, ...]: ...
255
+ def select[TFlavour](self, selector: SelectorFlavourType[T, tuple] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] = ..., alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> tuple[TFlavour, ...]: ...
254
256
 
255
257
  @abstractmethod
256
- def select[TValue, TFlavour, P](self, selector: SelectorFlavourType[T, tuple[TValue, P]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour] = ..., by: JoinType = ..., **kwargs): ...
258
+ def select[TValue, TFlavour, P](self, selector: SelectorFlavourType[T, tuple[TValue, P]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour] = ..., by: JoinType = ..., alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs): ...
257
259
 
258
260
  # endregion
259
261
  # region select_one
260
262
  @overload
261
263
  def select_one(self) -> T: ...
262
264
  @overload
263
- def select_one[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
265
+ def select_one[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> TFlavour: ...
264
266
  @overload
265
267
  def select_one[T1](self, selector: SelectorOneType[T, T1 | tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
266
268
  @overload
267
269
  def select_one[*TRes](self, selector: SelectorOneType[T, tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
268
270
  @overload
269
- def select_one[T1](self, selector: SelectorOneType[T, tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, **kwargs) -> T1: ...
271
+ def select_one[T1](self, selector: SelectorOneType[T, tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> T1: ...
270
272
  @overload
271
- def select_one[T1, TFlavour](self, selector: SelectorOneType[T, T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
273
+ def select_one[T1, TFlavour](self, selector: SelectorOneType[T, T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> TFlavour: ...
272
274
  @overload
273
- def select_one[*TRest](self, selector: SelectorOneType[T, tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], **kwargs) -> tuple[*TRest]: ...
275
+ def select_one[*TRest](self, selector: SelectorOneType[T, tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> tuple[*TRest]: ...
274
276
  @overload
275
- def select_one[TFlavour](self, selector: SelectorOneType[T, tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
277
+ def select_one[TFlavour](self, selector: SelectorOneType[T, tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> TFlavour: ...
276
278
  @abstractmethod
277
279
  def select_one[TValue, TFlavour, *TRest](
278
280
  self,
@@ -292,15 +294,15 @@ class IStatements[T: Table](ABC):
292
294
  @overload
293
295
  def first[*TRes](self, selector: SelectorOneType[T, tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
294
296
  @overload
295
- def first[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
297
+ def first[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> TFlavour: ...
296
298
  @overload
297
- def first[T1](self, selector: SelectorOneType[T, tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, **kwargs) -> T1: ...
299
+ def first[T1](self, selector: SelectorOneType[T, tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> T1: ...
298
300
  @overload
299
- def first[T1, TFlavour](self, selector: SelectorOneType[T, T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
301
+ def first[T1, TFlavour](self, selector: SelectorOneType[T, T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> TFlavour: ...
300
302
  @overload
301
- def first[*TRest](self, selector: SelectorOneType[T, tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], **kwargs) -> tuple[*TRest]: ...
303
+ def first[*TRest](self, selector: SelectorOneType[T, tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> tuple[*TRest]: ...
302
304
  @overload
303
- def first[TFlavour](self, selector: SelectorOneType[T, tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
305
+ def first[TFlavour](self, selector: SelectorOneType[T, tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> TFlavour: ...
304
306
  @abstractmethod
305
307
  def first[TValue, TFlavour, *TRest](
306
308
  self,
@@ -313,13 +315,17 @@ class IStatements[T: Table](ABC):
313
315
  # endregion
314
316
 
315
317
  # region groupby
318
+ @overload
319
+ def groupby[TRepo](self, column: list[SelectCols[T, TRepo]]) -> IStatements[T]: ...
320
+ @overload
321
+ def groupby[TRepo](self, column: SelectCols[T, TRepo]) -> IStatements[T]: ...
316
322
  @abstractmethod
317
- def groupby[TRepo](self, column: Callable[[T], TRepo]) -> IStatements[T]: ...
323
+ def groupby[TRepo](self, column: list[SelectCols[T, TRepo]] | SelectCols[T, TRepo]) -> IStatements[T]: ...
318
324
 
319
325
  # endregion
320
326
 
321
327
  @abstractmethod
322
- def alias(self, column: Callable[[T], Any], alias: str) -> IStatements[T]: ...
328
+ def alias[TProp](self, column: SelectCols[T, TProp], alias: AliasType[ClauseInfo[T]]) -> ClauseInfo[T]: ...
323
329
 
324
330
 
325
331
  class IStatements_two_generic[T, TPool](IStatements[T]):