ormlambda 3.35.3__py3-none-any.whl → 4.0.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 (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 +12 -7
  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 +51 -84
  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 +120 -110
  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.0.dist-info}/METADATA +29 -31
  79. ormlambda-4.0.0.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.0.dist-info}/AUTHORS +0 -0
  129. {ormlambda-3.35.3.dist-info → ormlambda-4.0.0.dist-info}/LICENSE +0 -0
  130. {ormlambda-3.35.3.dist-info → ormlambda-4.0.0.dist-info}/WHEEL +0 -0
@@ -1,37 +0,0 @@
1
- from __future__ import annotations
2
- from typing import TYPE_CHECKING
3
- from ormlambda.sql.clause_info import AggregateFunctionBase
4
- from ormlambda.sql.types import ColumnType, AliasType
5
- from ormlambda.sql.clause_info.clause_info_context import ClauseContextType
6
-
7
- if TYPE_CHECKING:
8
- from ormlambda.dialects import Dialect
9
-
10
-
11
- class ST_AsText[T, TProp](AggregateFunctionBase[None]):
12
- """
13
- https://dev.mysql.com/doc/refman/8.4/en/fetching-spatial-data.html
14
-
15
- The ST_AsText() function converts a geometry from internal format to a WKT string.
16
- """
17
-
18
- @staticmethod
19
- def FUNCTION_NAME() -> str:
20
- return "ST_AsText"
21
-
22
- def __init__(
23
- self,
24
- point: ColumnType[TProp],
25
- alias_table: AliasType[ColumnType[TProp]] = None,
26
- alias_clause: AliasType[ColumnType[TProp]] = None,
27
- context: ClauseContextType = None,
28
- *,
29
- dialect: Dialect,
30
- ) -> None:
31
- default_alias_clause = self.create_alias_from(point) if not alias_clause else alias_clause
32
-
33
- super().__init__(table=point.table, column=point, alias_table=alias_table, alias_clause=default_alias_clause, context=context, dialect=dialect)
34
-
35
- @staticmethod
36
- def create_alias_from(element: ColumnType[TProp]) -> str:
37
- return element.column_name
@@ -1,36 +0,0 @@
1
- from __future__ import annotations
2
- import typing as tp
3
-
4
- from shapely import Point
5
-
6
- from ormlambda import Column
7
- from ormlambda.sql.types import ColumnType, AliasType
8
- from ormlambda.sql.clause_info import ClauseInfo, IAggregate
9
-
10
- if tp.TYPE_CHECKING:
11
- from ormlambda.dialects import Dialect
12
-
13
-
14
- class ST_Contains(IAggregate):
15
- FUNCTION_NAME: str = "ST_Contains"
16
-
17
- def __init__[TProp: Column](
18
- self,
19
- column: ColumnType[TProp],
20
- point: Point,
21
- alias_table: tp.Optional[AliasType[ColumnType[TProp]]] = None,
22
- alias_clause: tp.Optional[AliasType[ColumnType[TProp]]] = None,
23
- *,
24
- dialect: Dialect,
25
- ):
26
- self.attr1: ClauseInfo[Point] = ClauseInfo(column.table, column, alias_table, dialect=dialect)
27
- self.attr2: ClauseInfo[Point] = ClauseInfo[Point](None, point, dialect=dialect)
28
-
29
- self._alias_clause: AliasType[ColumnType[TProp]] = alias_clause
30
-
31
- def query(self, dialect: Dialect, **kwargs) -> str:
32
- return f"{self.FUNCTION_NAME}({self.attr1.query(dialect,**kwargs)}, {self.attr2.query(dialect,**kwargs)})"
33
-
34
- @property
35
- def alias_clause(self) -> tp.Optional[str]:
36
- return self._alias_clause
@@ -1,14 +0,0 @@
1
- from .delete import DeleteQuery as Delete
2
- from .drop_table import DropTable as DropTable
3
- from .insert import InsertQuery as Insert
4
- from .joins import JoinSelector as JoinSelector
5
- from .limit import Limit as Limit
6
- from .offset import Offset as Offset
7
- from .order import Order as Order
8
- from .update import Update as Update
9
- from .upsert import UpsertQuery as Upsert
10
- from .where import Where as Where
11
- from .having import Having as Having
12
- from .count import Count as Count
13
- from .group_by import GroupBy as GroupBy
14
- from .ST_AsText import ST_AsText as ST_AsText
@@ -1,33 +0,0 @@
1
- from __future__ import annotations
2
- from ormlambda.sql.clauses import Count
3
- from ormlambda.sql.clause_info.clause_info_context import ClauseContextType
4
-
5
- from ormlambda.sql.types import AliasType, ColumnType
6
-
7
- from ormlambda import Table
8
-
9
- from typing import TYPE_CHECKING
10
-
11
- if TYPE_CHECKING:
12
- from ormlambda import Table
13
- from ormlambda.sql.types import ColumnType, AliasType, TableType
14
-
15
-
16
- class Count[T: Table](Count[T]):
17
- def __init__[TProp: Table](
18
- self,
19
- element: ColumnType[T] | TableType[TProp],
20
- alias_table: AliasType[ColumnType[TProp]] = None,
21
- alias_clause: AliasType[ColumnType[TProp]] = "count",
22
- context: ClauseContextType = None,
23
- keep_asterisk: bool = True,
24
- preserve_context: bool = True,
25
- ) -> None:
26
- super().__init__(
27
- element=element,
28
- alias_table=alias_table,
29
- alias_clause=alias_clause,
30
- context=context,
31
- keep_asterisk=keep_asterisk,
32
- preserve_context=preserve_context,
33
- )
@@ -1,9 +0,0 @@
1
- from ormlambda import Table
2
- from ormlambda.repository import IRepositoryBase
3
- from ormlambda.sql.clauses import Delete
4
- from mysql.connector import MySQLConnection
5
-
6
-
7
- class DeleteQuery[T: Table](Delete[T, MySQLConnection]):
8
- def __init__(self, model: T, repository: IRepositoryBase) -> None:
9
- super().__init__(model, repository)
@@ -1,26 +0,0 @@
1
- from __future__ import annotations
2
- from typing import Literal, override, TYPE_CHECKING
3
-
4
- if TYPE_CHECKING:
5
- from mysql.connector import MySQLConnection
6
-
7
- from ormlambda.repository import BaseRepository
8
-
9
-
10
- TypeExists = Literal["fail", "replace", "append"]
11
-
12
-
13
- class DropTable:
14
- def __init__(self, repository: BaseRepository[MySQLConnection]) -> None:
15
- self._repository: BaseRepository[MySQLConnection] = repository
16
-
17
- @override
18
- def execute(self, name: str = None) -> None:
19
- query = rf"{self.CLAUSE} {name}"
20
- self._repository.execute(query)
21
- return None
22
-
23
- @property
24
- @override
25
- def CLAUSE(self) -> str:
26
- return "DROP TABLE"
@@ -1,17 +0,0 @@
1
- from ormlambda.sql.clause_info import ClauseInfoContext
2
- from ormlambda.sql.clauses import GroupBy
3
- from ormlambda.sql.types import ColumnType
4
-
5
-
6
- class GroupBy(GroupBy):
7
- def __init__(
8
- self,
9
- column: ColumnType,
10
- context: ClauseInfoContext,
11
- **kwargs,
12
- ):
13
- super().__init__(
14
- column=column,
15
- context=context,
16
- **kwargs,
17
- )
@@ -1,12 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from ormlambda.sql.clauses import Having
4
-
5
-
6
- class Having(Having):
7
- """
8
- The purpose of this class is to create 'WHERE' condition queries properly.
9
- """
10
-
11
- def __init__(self, *comparer, restrictive=True, context=None):
12
- super().__init__(*comparer, restrictive=restrictive, context=context)
@@ -1,9 +0,0 @@
1
- from __future__ import annotations
2
- from ormlambda import Table
3
- from ormlambda.sql.clauses import Insert
4
- from mysql.connector import MySQLConnection
5
-
6
-
7
- class InsertQuery[T: Table](Insert[T, MySQLConnection]):
8
- def __init__(self, model, repository):
9
- super().__init__(model, repository)
@@ -1,14 +0,0 @@
1
- from __future__ import annotations
2
- from typing import TYPE_CHECKING
3
-
4
-
5
- from ormlambda.sql.clauses import JoinSelector
6
-
7
- # TODOL [x]: Try to import Table module without circular import Error
8
- if TYPE_CHECKING:
9
- from ormlambda import Table
10
-
11
-
12
- class JoinSelector[TLeft: Table, TRight: Table](JoinSelector[TLeft, TRight]):
13
- def __init__(self, where, by, alias="{table}", context=None, **kw):
14
- super().__init__(where, by, alias, context, **kw)
@@ -1,6 +0,0 @@
1
- from ormlambda.sql.clauses import Limit
2
-
3
-
4
- class Limit(Limit):
5
- def __init__(self, number):
6
- super().__init__(number)
@@ -1,6 +0,0 @@
1
- from ormlambda.sql.clauses import Offset
2
-
3
-
4
- class Offset(Offset):
5
- def __init__(self, number):
6
- super().__init__(number)
@@ -1,8 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from ormlambda.sql.clauses import Order
4
-
5
-
6
- class Order(Order):
7
- def __init__(self, column, order_type, context=None):
8
- super().__init__(column, order_type, context)
@@ -1,8 +0,0 @@
1
- from mysql.connector import MySQLConnection
2
-
3
- from ormlambda.sql.clauses import Update
4
-
5
-
6
- class Update[T](Update[T, MySQLConnection]):
7
- def __init__(self, model, repository, where):
8
- super().__init__(model, repository, where)
@@ -1,9 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from ormlambda import Table
4
- from ormlambda.sql.clauses.upsert import Upsert
5
-
6
-
7
- class UpsertQuery[T: Table](Upsert):
8
- def __init__(self, model, repository):
9
- super().__init__(model, repository)
@@ -1,7 +0,0 @@
1
- from __future__ import annotations
2
- from ormlambda.sql.clauses import Where
3
-
4
-
5
- class Where(Where):
6
- def __init__(self, *comparer, restrictive=True, context=None):
7
- super().__init__(*comparer, restrictive=restrictive, context=context)
@@ -1 +0,0 @@
1
- from .dialect import Dialect # noqa: F401
@@ -1,78 +0,0 @@
1
- from __future__ import annotations
2
- import abc
3
- from typing import ClassVar, Optional, Type, TYPE_CHECKING
4
-
5
-
6
- if TYPE_CHECKING:
7
- from ormlambda.caster.caster import Caster
8
- from ormlambda.repository.interfaces.IRepositoryBase import DBAPIConnection
9
- from ormlambda.sql.types import DDLCompiler, SQLCompiler, TypeCompiler
10
- from ormlambda import BaseRepository
11
-
12
-
13
- class Dialect(abc.ABC):
14
- """
15
- Abstract base class for all database dialects.
16
- """
17
-
18
- dbapi: Optional[DBAPIConnection]
19
- """A reference to the DBAPI module object itself.
20
-
21
- Ormlambda dialects import DBAPI modules using the classmethod
22
- :meth:`.Dialect.import_dbapi`. The rationale is so that any dialect
23
- module can be imported and used to generate SQL statements without the
24
- need for the actual DBAPI driver to be installed. Only when an
25
- :class:`.Engine` is constructed using :func:`.create_engine` does the
26
- DBAPI get imported; at that point, the creation process will assign
27
- the DBAPI module to this attribute.
28
-
29
- Dialects should therefore implement :meth:`.Dialect.import_dbapi`
30
- which will import the necessary module and return it, and then refer
31
- to ``self.dbapi`` in dialect code in order to refer to the DBAPI module
32
- contents.
33
-
34
- .. versionchanged:: The :attr:`.Dialect.dbapi` attribute is exclusively
35
- used as the per-:class:`.Dialect`-instance reference to the DBAPI
36
- module. The previous not-fully-documented ``.Dialect.dbapi()``
37
- classmethod is deprecated and replaced by :meth:`.Dialect.import_dbapi`.
38
-
39
- """
40
-
41
- name: ClassVar[str]
42
- """The name of the dialect, e.g. 'sqlite', 'postgresql', etc."""
43
- driver: ClassVar[str]
44
- """The driver used by the dialect, e.g. 'sqlite3', 'psycopg2', etc."""
45
-
46
- ddl_compiler: ClassVar[Type[DDLCompiler]]
47
- """The DDL compiler class used by the dialect."""
48
-
49
- statement_compiler: ClassVar[Type[SQLCompiler]]
50
- """The statement compiler class used by the dialect."""
51
-
52
- type_compiler_cls: ClassVar[Type[TypeCompiler]]
53
- """The type compiler class used by the dialect."""
54
-
55
- type_compiler_instance: ClassVar[TypeCompiler]
56
- """The instance of the type compiler class used by the dialect."""
57
-
58
- repository_cls: ClassVar[Type[BaseRepository]]
59
- """The repository class used by the dialect."""
60
-
61
- caster: ClassVar[Type[Caster]]
62
-
63
- @classmethod
64
- def get_dialect_cls(cls) -> Type[Dialect]:
65
- return cls
66
-
67
- @classmethod
68
- @abc.abstractmethod
69
- def import_dbapi(cls) -> DBAPIConnection:
70
- """
71
- Import the DB API module for the dialect.
72
- This method should be implemented by subclasses to import the
73
- appropriate DB API module for the dialect.
74
- """
75
- ...
76
-
77
- def __repr__(self):
78
- return f"{Dialect.__name__}: {type(self).__name__}"
@@ -1,96 +0,0 @@
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
- if tp.TYPE_CHECKING:
20
- from ormlambda.dialects import Dialect
21
-
22
-
23
- class AggregateFunctionBase[T: Table](ClauseInfo[T], IAggregate):
24
- def __init__[TProp: Column](
25
- self,
26
- table: TableType[T],
27
- column: tp.Optional[ColumnType[TProp]] = None,
28
- alias_table: tp.Optional[AliasType[ClauseInfo[T]]] = None,
29
- alias_clause: tp.Optional[AliasType[ClauseInfo[T]]] = None,
30
- context: ClauseContextType = None,
31
- keep_asterisk: bool = False,
32
- preserve_context: bool = False,
33
- dtype: TProp = None,
34
- *,
35
- dialect: Dialect,
36
- **kw,
37
- ):
38
- self._alias_aggregate = alias_clause
39
- super().__init__(
40
- table=table,
41
- column=column,
42
- alias_table=alias_table,
43
- context=context,
44
- keep_asterisk=keep_asterisk,
45
- preserve_context=preserve_context,
46
- dtype=dtype,
47
- dialect=dialect,
48
- **kw,
49
- )
50
-
51
- @staticmethod
52
- @abc.abstractmethod
53
- def FUNCTION_NAME() -> str: ...
54
-
55
- @classmethod
56
- def _convert_into_clauseInfo[TypeColumns, TProp](cls, columns: ClauseInfo | ColumnType[TProp], context: ClauseContextType, dialect: Dialect) -> list[ClauseInfo]:
57
- type DEFAULT = tp.Literal["default"]
58
- type ClusterType = ColumnType | ForeignKey | DEFAULT
59
-
60
- dicc_type: dict[ClusterType, tp.Callable[[ClusterType], ClauseInfo]] = {
61
- Column: lambda column: ClauseInfo(column.table, column, context=context, dialect=dialect),
62
- ClauseInfo: lambda column: column,
63
- ForeignKey: lambda tbl: ClauseInfo(tbl.tright, tbl.tright, context=context, dialect=dialect),
64
- TableMeta: lambda tbl: ClauseInfo(tbl, tbl, context=context, dialect=dialect),
65
- "default": lambda column: ClauseInfo(table=None, column=column, context=context, dialect=dialect),
66
- }
67
- all_clauses: list[ClauseInfo] = []
68
- if isinstance(columns, str) or not isinstance(columns, tp.Iterable):
69
- columns = (columns,)
70
- for value in columns:
71
- all_clauses.append(dicc_type.get(type(value), dicc_type["default"])(value))
72
-
73
- return all_clauses
74
-
75
- @tp.override
76
- def query(self, dialect: Dialect, **kwargs) -> str:
77
- wrapped_ci = self.wrapped_clause_info(self)
78
- if not self._alias_aggregate:
79
- return wrapped_ci
80
-
81
- return ClauseInfo(
82
- table=None,
83
- column=wrapped_ci,
84
- alias_clause=self._alias_aggregate,
85
- context=self._context,
86
- keep_asterisk=self._keep_asterisk,
87
- preserve_context=self._preserve_context,
88
- dialect=self._dialect,
89
- ).query(dialect, **kwargs)
90
-
91
- def wrapped_clause_info(self, ci: ClauseInfo[T]) -> str:
92
- # avoid use placeholder when using IAggregate because no make sense.
93
- if self._alias_aggregate and (found := self._keyRegex.findall(self._alias_aggregate)):
94
- raise NotKeysInIAggregateError(found)
95
-
96
- return f"{self.FUNCTION_NAME()}({ci._create_query(self._dialect)})"
@@ -1,87 +0,0 @@
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
@@ -1,17 +0,0 @@
1
- from __future__ import annotations
2
- import abc
3
- from ormlambda.common.interfaces import IQuery
4
- from typing import TYPE_CHECKING
5
-
6
- if TYPE_CHECKING:
7
- from ormlambda.sql.clause_info import ClauseInfo
8
-
9
-
10
- class ISelect(IQuery):
11
- @property
12
- @abc.abstractmethod
13
- def FROM(self) -> ClauseInfo: ...
14
-
15
- @property
16
- @abc.abstractmethod
17
- def COLUMNS(self) -> str: ...
@@ -1,119 +0,0 @@
1
- from __future__ import annotations
2
- from collections import defaultdict
3
- from typing import override, Optional, TYPE_CHECKING, Type
4
-
5
-
6
- from ormlambda.util.module_tree.dfs_traversal import DFSTraversal
7
- from ormlambda.common.interfaces.IQueryCommand import IQuery
8
- from ormlambda import JoinType
9
- from ormlambda.sql.clause_info import ClauseInfo
10
- from ormlambda.sql.comparer import Comparer
11
- from ormlambda.sql.elements import ClauseElement
12
-
13
-
14
- # TODOL [x]: Try to import Table module without circular import Error
15
- if TYPE_CHECKING:
16
- from ormlambda.dialects import Dialect
17
-
18
-
19
- class Join(ClauseElement):
20
- __visit_name__ = "join"
21
- __slots__: tuple = (
22
- "comparer",
23
- "alias",
24
- "from_clause",
25
- "left_clause",
26
- "right_clause",
27
- "by",
28
- "_dialect",
29
- )
30
-
31
- DEFAULT_TABLE: str = "{table}"
32
-
33
- @override
34
- def __repr__(self) -> str:
35
- return f"{IQuery.__name__}: {self.query(self._dialect)}"
36
-
37
- def __init__(
38
- self,
39
- comparer: Comparer,
40
- right_alias: Optional[str] = DEFAULT_TABLE,
41
- left_alias: Optional[str] = DEFAULT_TABLE,
42
- by: JoinType = JoinType.LEFT_EXCLUSIVE,
43
- *,
44
- dialect: Dialect,
45
- **kw,
46
- ) -> None:
47
- self.comparer = comparer
48
- self._dialect = dialect
49
- self.by = by
50
-
51
- # COMMENT: When multiple columns reference the same table, we need to create an alias to maintain clear references.
52
- self.alias: Optional[str] = right_alias
53
- self.left_clause: ClauseInfo = comparer.left_condition(dialect)
54
- self.left_clause.alias_table = left_alias if left_alias is not None else self.DEFAULT_TABLE
55
-
56
- self.right_clause: ClauseInfo = comparer.right_condition(dialect)
57
- self.right_clause.alias_table = right_alias if right_alias is not None else self.DEFAULT_TABLE
58
- self.from_clause = ClauseInfo(self.right_clause.table, None, alias_table=self.alias, dialect=dialect)
59
-
60
- def __eq__(self, __value: Join) -> bool:
61
- return isinstance(__value, Join) and self.__hash__() == __value.__hash__()
62
-
63
- def __hash__(self) -> int:
64
- return hash(
65
- (
66
- self.by,
67
- self.left_clause.query(self._dialect),
68
- self.right_clause.query(self._dialect),
69
- self.from_clause.query(self._dialect),
70
- )
71
- )
72
-
73
- @classmethod
74
- def join_selectors(cls, dialect: Dialect, *args: Join) -> str:
75
- return "\n".join([x.query(dialect) for x in args])
76
-
77
- def query(self, dialect: Dialect) -> str:
78
- list_ = [
79
- self.by.value, # inner join
80
- self.from_clause.query(dialect),
81
- "ON",
82
- self.left_clause.query(dialect),
83
- "=",
84
- self.right_clause.query(dialect),
85
- ]
86
- return " ".join([x for x in list_ if x is not None])
87
-
88
- @classmethod
89
- def sort_join_selectors(cls, joins: set[Join]) -> tuple[Join]:
90
- # FIXME [x]: How to sort when needed because it's not necessary at this point. It is for testing purpouse
91
- if len(joins) == 1:
92
- return tuple(joins)
93
-
94
- # create graph and sort it using 'Depth First Search' algorithm
95
- graph: dict[str, list[str]] = defaultdict(list)
96
- for join in joins:
97
- graph[join.left_clause.alias_table].append(join.right_clause.alias_table)
98
-
99
- sorted_graph = DFSTraversal.sort(graph)[::-1]
100
-
101
- if not sorted_graph:
102
- return tuple(joins)
103
-
104
- # Mapped Join class using his unique alias
105
- join_object_map: dict[str, Join] = {}
106
- for obj in joins:
107
- join_object_map[obj.alias] = obj
108
-
109
- res = []
110
- for table in sorted_graph:
111
- tables = join_object_map.get(table, None)
112
-
113
- if not tables:
114
- continue
115
- res.append(tables)
116
- return res
117
-
118
-
119
- __all__ = ["Join"]