ormlambda 3.35.2__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.
- ormlambda/__init__.py +79 -51
- ormlambda/caster/caster.py +6 -1
- ormlambda/common/abstract_classes/__init__.py +0 -2
- ormlambda/common/enums/__init__.py +1 -0
- ormlambda/common/enums/order_type.py +9 -0
- ormlambda/common/errors/__init__.py +13 -3
- ormlambda/common/global_checker.py +86 -8
- ormlambda/common/interfaces/IQueryCommand.py +2 -2
- ormlambda/common/interfaces/__init__.py +0 -2
- ormlambda/dialects/__init__.py +75 -3
- ormlambda/dialects/default/base.py +1 -1
- ormlambda/dialects/mysql/__init__.py +35 -78
- ormlambda/dialects/mysql/base.py +226 -40
- ormlambda/dialects/mysql/clauses/ST_AsText.py +26 -0
- ormlambda/dialects/mysql/clauses/ST_Contains.py +30 -0
- ormlambda/dialects/mysql/clauses/__init__.py +1 -0
- ormlambda/dialects/mysql/repository/__init__.py +1 -0
- ormlambda/{databases/my_sql → dialects/mysql/repository}/repository.py +0 -5
- ormlambda/dialects/mysql/types.py +6 -0
- ormlambda/engine/base.py +26 -4
- ormlambda/errors.py +9 -0
- ormlambda/model/base_model.py +3 -10
- ormlambda/repository/base_repository.py +1 -1
- ormlambda/repository/interfaces/IRepositoryBase.py +0 -7
- ormlambda/repository/response.py +12 -7
- ormlambda/sql/__init__.py +12 -3
- ormlambda/sql/clause_info/__init__.py +0 -2
- ormlambda/sql/clause_info/clause_info.py +94 -76
- ormlambda/sql/clause_info/interface/IAggregate.py +14 -4
- ormlambda/sql/clause_info/interface/IClauseInfo.py +6 -11
- ormlambda/sql/clauses/alias.py +6 -37
- ormlambda/sql/clauses/count.py +21 -36
- ormlambda/sql/clauses/group_by.py +13 -19
- ormlambda/sql/clauses/having.py +2 -6
- ormlambda/sql/clauses/insert.py +3 -3
- ormlambda/sql/clauses/interfaces/__init__.py +0 -1
- ormlambda/sql/clauses/join/join_context.py +5 -12
- ormlambda/sql/clauses/joins.py +34 -52
- ormlambda/sql/clauses/limit.py +1 -2
- ormlambda/sql/clauses/offset.py +1 -2
- ormlambda/sql/clauses/order.py +17 -21
- ormlambda/sql/clauses/select.py +56 -28
- ormlambda/sql/clauses/update.py +13 -10
- ormlambda/sql/clauses/where.py +20 -39
- ormlambda/sql/column/__init__.py +1 -0
- ormlambda/sql/column/column.py +19 -12
- ormlambda/sql/column/column_proxy.py +117 -0
- ormlambda/sql/column_table_proxy.py +23 -0
- ormlambda/sql/comparer.py +31 -65
- ormlambda/sql/compiler.py +248 -58
- ormlambda/sql/context/__init__.py +304 -0
- ormlambda/sql/ddl.py +19 -5
- ormlambda/sql/elements.py +3 -0
- ormlambda/sql/foreign_key.py +42 -64
- ormlambda/sql/functions/__init__.py +0 -1
- ormlambda/sql/functions/concat.py +35 -38
- ormlambda/sql/functions/max.py +12 -36
- ormlambda/sql/functions/min.py +13 -28
- ormlambda/sql/functions/sum.py +17 -33
- ormlambda/sql/sqltypes.py +2 -0
- ormlambda/sql/table/__init__.py +1 -0
- ormlambda/sql/table/table.py +32 -49
- ormlambda/sql/table/table_proxy.py +88 -0
- ormlambda/sql/type_api.py +4 -1
- ormlambda/sql/types.py +15 -12
- ormlambda/statements/__init__.py +0 -2
- ormlambda/statements/base_statement.py +51 -84
- ormlambda/statements/interfaces/IStatements.py +77 -123
- ormlambda/statements/interfaces/__init__.py +1 -1
- ormlambda/statements/query_builder.py +296 -128
- ormlambda/statements/statements.py +120 -110
- ormlambda/statements/types.py +5 -25
- ormlambda/util/__init__.py +7 -86
- ormlambda/util/langhelpers.py +102 -0
- ormlambda/util/module_tree/dynamic_module.py +1 -1
- ormlambda/util/preloaded.py +80 -0
- ormlambda/util/typing.py +12 -3
- {ormlambda-3.35.2.dist-info → ormlambda-4.0.0.dist-info}/METADATA +29 -31
- ormlambda-4.0.0.dist-info/RECORD +139 -0
- ormlambda/common/abstract_classes/clause_info_converter.py +0 -65
- ormlambda/common/abstract_classes/decomposition_query.py +0 -141
- ormlambda/common/abstract_classes/query_base.py +0 -15
- ormlambda/common/interfaces/ICustomAlias.py +0 -7
- ormlambda/common/interfaces/IDecompositionQuery.py +0 -33
- ormlambda/databases/__init__.py +0 -4
- ormlambda/databases/my_sql/__init__.py +0 -3
- ormlambda/databases/my_sql/clauses/ST_AsText.py +0 -37
- ormlambda/databases/my_sql/clauses/ST_Contains.py +0 -36
- ormlambda/databases/my_sql/clauses/__init__.py +0 -14
- ormlambda/databases/my_sql/clauses/count.py +0 -33
- ormlambda/databases/my_sql/clauses/delete.py +0 -9
- ormlambda/databases/my_sql/clauses/drop_table.py +0 -26
- ormlambda/databases/my_sql/clauses/group_by.py +0 -17
- ormlambda/databases/my_sql/clauses/having.py +0 -12
- ormlambda/databases/my_sql/clauses/insert.py +0 -9
- ormlambda/databases/my_sql/clauses/joins.py +0 -14
- ormlambda/databases/my_sql/clauses/limit.py +0 -6
- ormlambda/databases/my_sql/clauses/offset.py +0 -6
- ormlambda/databases/my_sql/clauses/order.py +0 -8
- ormlambda/databases/my_sql/clauses/update.py +0 -8
- ormlambda/databases/my_sql/clauses/upsert.py +0 -9
- ormlambda/databases/my_sql/clauses/where.py +0 -7
- ormlambda/dialects/interface/__init__.py +0 -1
- ormlambda/dialects/interface/dialect.py +0 -78
- ormlambda/sql/clause_info/aggregate_function_base.py +0 -96
- ormlambda/sql/clause_info/clause_info_context.py +0 -87
- ormlambda/sql/clauses/interfaces/ISelect.py +0 -17
- ormlambda/sql/clauses/new_join.py +0 -119
- ormlambda/util/load_module.py +0 -21
- ormlambda/util/plugin_loader.py +0 -32
- ormlambda-3.35.2.dist-info/RECORD +0 -159
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/__init__.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/caster.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/__init__.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/boolean.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/bytes.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/date.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/datetime.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/decimal.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/float.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/int.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/iterable.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/json.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/none.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/point.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/string.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql/repository}/pool_types.py +0 -0
- {ormlambda-3.35.2.dist-info → ormlambda-4.0.0.dist-info}/AUTHORS +0 -0
- {ormlambda-3.35.2.dist-info → ormlambda-4.0.0.dist-info}/LICENSE +0 -0
- {ormlambda-3.35.2.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 +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"]
|