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.
- 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 +21 -8
- 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 +31 -45
- 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 +53 -91
- 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 +122 -115
- ormlambda/statements/types.py +5 -25
- ormlambda/util/__init__.py +7 -100
- 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.3.dist-info → ormlambda-4.0.4.dist-info}/METADATA +56 -79
- ormlambda-4.0.4.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.3.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.3.dist-info → ormlambda-4.0.4.dist-info}/AUTHORS +0 -0
- {ormlambda-3.35.3.dist-info → ormlambda-4.0.4.dist-info}/LICENSE +0 -0
- {ormlambda-3.35.3.dist-info → ormlambda-4.0.4.dist-info}/WHEEL +0 -0
ormlambda/sql/functions/min.py
CHANGED
@@ -1,39 +1,24 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
import typing as tp
|
3
2
|
|
4
|
-
from ormlambda.sql.
|
5
|
-
from ormlambda.sql.clause_info import ClauseInfo
|
3
|
+
from ormlambda.sql.elements import ClauseElement
|
6
4
|
from ormlambda.sql.types import ColumnType, AliasType
|
7
|
-
from ormlambda.sql.clause_info import
|
5
|
+
from ormlambda.sql.clause_info import IAggregate
|
8
6
|
|
9
|
-
if tp.TYPE_CHECKING:
|
10
|
-
from ormlambda.dialects import Dialect
|
11
7
|
|
12
|
-
|
13
|
-
|
14
|
-
@staticmethod
|
15
|
-
def FUNCTION_NAME() -> str:
|
16
|
-
return "MIN"
|
8
|
+
class Min(ClauseElement, IAggregate):
|
9
|
+
__visit_name__ = "min"
|
17
10
|
|
18
11
|
def __init__[TProp](
|
19
12
|
self,
|
20
|
-
elements:
|
21
|
-
|
22
|
-
context: ClauseContextType = None,
|
23
|
-
*,
|
24
|
-
dialect: Dialect,
|
13
|
+
elements: ColumnType[TProp],
|
14
|
+
alias: AliasType[ColumnType[TProp]] = "min",
|
25
15
|
):
|
26
|
-
|
27
|
-
|
28
|
-
@tp.override
|
29
|
-
def query(self, dialect: Dialect, **kwargs) -> str:
|
30
|
-
columns: list[str] = []
|
16
|
+
self.column = elements
|
17
|
+
self.alias = alias
|
31
18
|
|
32
|
-
|
33
|
-
|
34
|
-
new_clause = clause
|
35
|
-
new_clause.alias_clause = None
|
36
|
-
columns.append(new_clause)
|
19
|
+
def used_columns(self):
|
20
|
+
return [self.column]
|
37
21
|
|
38
|
-
|
39
|
-
|
22
|
+
@property
|
23
|
+
def dtype(self) -> int:
|
24
|
+
return int
|
ormlambda/sql/functions/sum.py
CHANGED
@@ -1,41 +1,25 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
import typing as tp
|
3
2
|
|
4
|
-
from ormlambda.sql.
|
5
|
-
from ormlambda.sql.clause_info import ClauseInfo
|
6
|
-
from ormlambda.sql.types import ColumnType, AliasType
|
7
|
-
from ormlambda.sql.clause_info import AggregateFunctionBase
|
8
|
-
|
9
|
-
if tp.TYPE_CHECKING:
|
10
|
-
from ormlambda.dialects import Dialect
|
3
|
+
from ormlambda.sql.elements import ClauseElement
|
11
4
|
|
5
|
+
from ormlambda.sql.types import ColumnType, AliasType
|
6
|
+
from ormlambda.sql.clause_info import IAggregate
|
12
7
|
|
13
|
-
class Sum(AggregateFunctionBase):
|
14
|
-
@staticmethod
|
15
|
-
def FUNCTION_NAME() -> str:
|
16
|
-
return "SUM"
|
17
8
|
|
18
|
-
|
19
|
-
|
20
|
-
table=None,
|
21
|
-
column=elements,
|
22
|
-
alias_table=None,
|
23
|
-
alias_clause=alias_clause,
|
24
|
-
context=context,
|
25
|
-
keep_asterisk=False,
|
26
|
-
preserve_context=False,
|
27
|
-
dialect=dialect,
|
28
|
-
)
|
9
|
+
class Sum(ClauseElement, IAggregate):
|
10
|
+
__visit_name__ = "sum"
|
29
11
|
|
30
|
-
|
31
|
-
|
32
|
-
|
12
|
+
def __init__[TProp](
|
13
|
+
self,
|
14
|
+
elements: ColumnType[TProp],
|
15
|
+
alias: AliasType[ColumnType[TProp]] = "sum",
|
16
|
+
):
|
17
|
+
self.column = elements
|
18
|
+
self.alias = alias
|
33
19
|
|
34
|
-
|
35
|
-
|
36
|
-
new_clause = clause
|
37
|
-
new_clause.alias_clause = None
|
38
|
-
columns.append(new_clause)
|
20
|
+
def used_columns(self):
|
21
|
+
return [self.column]
|
39
22
|
|
40
|
-
|
41
|
-
|
23
|
+
@property
|
24
|
+
def dtype(self) -> int:
|
25
|
+
return int
|
ormlambda/sql/sqltypes.py
CHANGED
ormlambda/sql/table/__init__.py
CHANGED
ormlambda/sql/table/table.py
CHANGED
@@ -2,15 +2,13 @@ from __future__ import annotations
|
|
2
2
|
from typing import Any, Optional, Type, dataclass_transform, TYPE_CHECKING
|
3
3
|
import json
|
4
4
|
|
5
|
-
from ormlambda.sql import
|
6
|
-
from ormlambda
|
7
|
-
from ormlambda.util.module_tree.dfs_traversal import DFSTraversal
|
8
|
-
from ormlambda.sql.ddl import CreateTable
|
9
|
-
from ormlambda.util import make_hashable
|
5
|
+
from ormlambda.sql.ddl import CreateTable, DropTable
|
6
|
+
from ormlambda import util
|
10
7
|
|
11
8
|
if TYPE_CHECKING:
|
12
|
-
from ormlambda.
|
9
|
+
from ormlambda.sql import Column
|
13
10
|
from ormlambda.dialects import Dialect
|
11
|
+
from ormlambda import ForeignKey
|
14
12
|
|
15
13
|
from .table_constructor import __init_constructor__
|
16
14
|
|
@@ -34,6 +32,7 @@ class TableMeta(type):
|
|
34
32
|
raise Exception(f"class variable '__table_name__' of '{cls_object.__name__}' class must be 'str'")
|
35
33
|
|
36
34
|
self = __init_constructor__(cls_object)
|
35
|
+
|
37
36
|
return self
|
38
37
|
|
39
38
|
def __repr__(cls: "Table") -> str:
|
@@ -85,7 +84,7 @@ class Table(metaclass=TableMeta):
|
|
85
84
|
|
86
85
|
dicc: dict[str, str] = {x: str(getattr(self, x)) for x in self.__annotations__}
|
87
86
|
equal_loop = ["=".join((x, __cast_long_variables(y))) for x, y in dicc.items()]
|
88
|
-
return f
|
87
|
+
return f"{self.__class__.__name__}({', '.join(equal_loop)})"
|
89
88
|
|
90
89
|
def __getitem__[TProp](self, value: str | Column[TProp]) -> Optional[TProp]:
|
91
90
|
name = value if isinstance(value, str) else value.column_name
|
@@ -94,21 +93,40 @@ class Table(metaclass=TableMeta):
|
|
94
93
|
return None
|
95
94
|
|
96
95
|
def to_dict(self) -> dict[str, str | int]:
|
96
|
+
def make_hashable(item: Any) -> Any:
|
97
|
+
if isinstance(item, dict):
|
98
|
+
return tuple(sorted((k, make_hashable(x)) for k, x in item.items()))
|
99
|
+
|
100
|
+
if isinstance(item, (list | set)):
|
101
|
+
return tuple(make_hashable(x) for x in item)
|
102
|
+
if hasattr(item, "__iter__") and not isinstance(item, str | bytes):
|
103
|
+
try:
|
104
|
+
return tuple(make_hashable(x) for x in item)
|
105
|
+
except TypeError:
|
106
|
+
return item # if it fails, it's already hashable
|
107
|
+
return item
|
108
|
+
|
97
109
|
dicc: dict[str, Any] = {}
|
98
110
|
for x in self.__annotations__:
|
99
111
|
value = getattr(self, x)
|
100
112
|
dicc[x] = make_hashable(value)
|
101
113
|
return dicc
|
102
114
|
|
115
|
+
@util.preload_module("ormlambda.sql")
|
103
116
|
@classmethod
|
104
117
|
def get_pk(cls) -> Optional[Column]:
|
118
|
+
Column = util.preloaded.sql_column.Column
|
119
|
+
ColumnProxy = util.preloaded.sql_column.ColumnProxy
|
105
120
|
for obj in cls.__dict__.values():
|
106
|
-
if isinstance(obj, Column) and obj.is_primary_key:
|
121
|
+
if isinstance(obj, Column | ColumnProxy) and obj.is_primary_key:
|
107
122
|
return obj
|
108
123
|
return None
|
109
124
|
|
125
|
+
@util.preload_module("ormlambda.sql")
|
110
126
|
@classmethod
|
111
127
|
def get_columns(cls) -> tuple[Column, ...]:
|
128
|
+
Column = util.preloaded.sql_column.Column
|
129
|
+
|
112
130
|
return tuple([x for x in cls.__annotations__.values() if isinstance(x, Column)])
|
113
131
|
|
114
132
|
@classmethod
|
@@ -117,48 +135,13 @@ class Table(metaclass=TableMeta):
|
|
117
135
|
if name == key:
|
118
136
|
return value
|
119
137
|
|
120
|
-
@classmethod
|
121
|
-
def create_table_query(cls, statement: BaseStatement) -> str:
|
122
|
-
"""It's classmethod because of it does not matter the columns values to create the table"""
|
123
|
-
from ormlambda.sql.schema_generator import SchemaGeneratorFactory
|
124
|
-
|
125
|
-
return SchemaGeneratorFactory.get_generator(statement._dialect).create_table(cls)
|
126
|
-
|
127
138
|
@classmethod
|
128
139
|
def create_table(cls, dialect: Dialect) -> str:
|
129
140
|
return CreateTable(cls).compile(dialect).string
|
130
141
|
|
131
142
|
@classmethod
|
132
|
-
def
|
133
|
-
|
134
|
-
return
|
135
|
-
|
136
|
-
# TODOL: Dive into new way to return dependent tables
|
137
|
-
def get_involved_tables(graph: dict[Table, list[Table]], table_name: str) -> None:
|
138
|
-
"""
|
139
|
-
Create a graph to be ordered
|
140
|
-
"""
|
141
|
-
table = ForeignKey[Table, Table].MAPPED[table_name]
|
142
|
-
for x in table.referenced_tables:
|
143
|
-
if data := ForeignKey.MAPPED.get(x, None):
|
144
|
-
get_involved_tables(graph, data.table_object.__table_name__)
|
145
|
-
|
146
|
-
graph[table.table_object.__table_name__] = list(table.referenced_tables)
|
147
|
-
return None
|
148
|
-
|
149
|
-
graph: dict[Table, list[Table]] = {}
|
150
|
-
dependent = ForeignKey.MAPPED.get(cls.__table_name__, None)
|
151
|
-
if dependent is None:
|
152
|
-
return tuple([])
|
153
|
-
|
154
|
-
graph[cls.__table_name__] = list(dependent.referenced_tables)
|
155
|
-
get_involved_tables(graph, cls.__table_name__)
|
156
|
-
|
157
|
-
dfs = DFSTraversal.sort(graph)
|
158
|
-
|
159
|
-
order_table = dfs[: dfs.index(cls.__table_name__)]
|
160
|
-
|
161
|
-
return [ForeignKey.MAPPED[x].table_object for x in order_table]
|
143
|
+
def drop_table(cls, dialect: Dialect) -> str:
|
144
|
+
return DropTable(cls).compile(dialect).string
|
162
145
|
|
163
146
|
def __eq__(self, __value: Any) -> bool:
|
164
147
|
return hash(self) == hash(__value)
|
@@ -172,6 +155,9 @@ class Table(metaclass=TableMeta):
|
|
172
155
|
return f"`{cls.__table_name__}_{column}`"
|
173
156
|
return cls.__table_name__
|
174
157
|
|
158
|
+
@util.preload_module("ormlambda.sql")
|
175
159
|
@classmethod
|
176
160
|
def foreign_keys(cls) -> dict[str, ForeignKey]:
|
161
|
+
ForeignKey = util.preloaded.sql_foreign_key.ForeignKey
|
162
|
+
|
177
163
|
return {key: value for key, value in cls.__dict__.items() if isinstance(value, ForeignKey)}
|
@@ -0,0 +1,88 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import TYPE_CHECKING, Optional, Any, Type
|
3
|
+
|
4
|
+
from ormlambda.sql.column_table_proxy import ColumnTableProxy
|
5
|
+
from ormlambda.sql.elements import ClauseElement
|
6
|
+
from ormlambda.sql.context import FKChain
|
7
|
+
|
8
|
+
from ormlambda import util
|
9
|
+
|
10
|
+
if TYPE_CHECKING:
|
11
|
+
from ormlambda import Table
|
12
|
+
from ormlambda import ForeignKey
|
13
|
+
|
14
|
+
|
15
|
+
type CurrentPathType = Optional[FKChain]
|
16
|
+
type ForeignKeyRegistryType = dict[str, list[ForeignKey]]
|
17
|
+
type PathAliasesType = dict[str, str]
|
18
|
+
type QueryMetadataType = dict[str, Any]
|
19
|
+
|
20
|
+
|
21
|
+
class TableProxy[T: Table](ColumnTableProxy, ClauseElement):
|
22
|
+
__visit_name__ = "table_proxy"
|
23
|
+
_table_class: Type[Table]
|
24
|
+
_path: FKChain
|
25
|
+
|
26
|
+
def __init__(self, table_class: Type[T], path: Optional[FKChain] = None):
|
27
|
+
if not path:
|
28
|
+
path = FKChain(table_class, [])
|
29
|
+
|
30
|
+
self._table_class = table_class
|
31
|
+
super().__init__(path.copy())
|
32
|
+
|
33
|
+
def __repr__(self) -> str:
|
34
|
+
return f"{TableProxy.__name__}({self._table_class.__table_name__}) Path={self._path.get_path_key()})"
|
35
|
+
|
36
|
+
@util.preload_module(
|
37
|
+
"ormlambda.sql.foreign_key",
|
38
|
+
"ormlambda.sql.column",
|
39
|
+
)
|
40
|
+
def __getattr__(self, name: str):
|
41
|
+
"""Intercept attribute access to handle foreign keys and columns"""
|
42
|
+
|
43
|
+
ColumnProxy = util.preloaded.sql_column.ColumnProxy
|
44
|
+
Column = util.preloaded.sql_column.Column
|
45
|
+
ForeignKey = util.preloaded.sql_foreign_key.ForeignKey
|
46
|
+
|
47
|
+
# Get the actual attribute from the table class
|
48
|
+
try:
|
49
|
+
attr = getattr(self._table_class, name)
|
50
|
+
except AttributeError:
|
51
|
+
# If column doesn't exist is because we're dealing with aliases like
|
52
|
+
# `lambda x: x.count` where 'count' is actually an alias not a column name
|
53
|
+
# we don't want use table name
|
54
|
+
attr = Column(dtype=str)
|
55
|
+
attr.column_name = name
|
56
|
+
return ColumnProxy(attr, path=FKChain(None, []))
|
57
|
+
|
58
|
+
if isinstance(attr, ForeignKey):
|
59
|
+
new_path = self._path.copy()
|
60
|
+
new_path.add_step(attr)
|
61
|
+
return TableProxy(attr.tright, new_path)
|
62
|
+
|
63
|
+
elif isinstance(attr, Column):
|
64
|
+
# Accessing a column - return column reference with path info
|
65
|
+
|
66
|
+
column = ColumnProxy(attr, self._path.copy())
|
67
|
+
self._path.clear()
|
68
|
+
return column
|
69
|
+
|
70
|
+
else:
|
71
|
+
return attr
|
72
|
+
|
73
|
+
def get_alias(self) -> str:
|
74
|
+
"""Get the alias for this table based on its path"""
|
75
|
+
return self._path.get_alias()
|
76
|
+
|
77
|
+
def get_table_chain(self):
|
78
|
+
return self.get_alias()
|
79
|
+
|
80
|
+
@util.preload_module("ormlambda.sql.column")
|
81
|
+
def get_columns(self) -> tuple[ColumnTableProxy]:
|
82
|
+
ColumnProxy = util.preloaded.sql_column.ColumnProxy
|
83
|
+
|
84
|
+
result = []
|
85
|
+
for column in self._table_class.get_columns():
|
86
|
+
col_proxy = ColumnProxy(column, self._path)
|
87
|
+
result.append(col_proxy)
|
88
|
+
return result
|
ormlambda/sql/type_api.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
from typing import Any, ClassVar
|
3
3
|
from ormlambda.sql.visitors import Element
|
4
|
+
from ormlambda import util
|
4
5
|
import abc
|
5
6
|
|
6
7
|
|
@@ -26,8 +27,10 @@ class TypeEngine[T: Any](Element, abc.ABC):
|
|
26
27
|
def _resolve_for_literal_value(self, value: T) -> TypeEngine[T]:
|
27
28
|
return self
|
28
29
|
|
30
|
+
@util.preload_module("ormlambda.sql.sqltypes")
|
29
31
|
def coerce_compared_value[TType](self, value: TType) -> TypeEngine[TType]:
|
30
|
-
|
32
|
+
resolve_primitive_types = util.preloaded.sql_types.resolve_primitive_types
|
33
|
+
NULLTYPE = util.preloaded.sql_types.NULLTYPE
|
31
34
|
|
32
35
|
_coerced_type = resolve_primitive_types(value)
|
33
36
|
if _coerced_type is NULLTYPE:
|
ormlambda/sql/types.py
CHANGED
@@ -1,27 +1,30 @@
|
|
1
|
-
|
1
|
+
from typing import TYPE_CHECKING, Literal, Callable, Type
|
2
2
|
|
3
3
|
|
4
|
-
if
|
5
|
-
from ormlambda import
|
4
|
+
if TYPE_CHECKING:
|
5
|
+
from ormlambda.sql.clause_info import IAggregate
|
6
|
+
from ormlambda import Table
|
6
7
|
from ormlambda.sql.comparer import Comparer
|
7
8
|
from ormlambda import ConditionType as ConditionEnum
|
8
|
-
from ormlambda.common.enums.join_type import JoinType
|
9
|
+
from ormlambda.common.enums.join_type import JoinType as JoinType
|
10
|
+
from ormlambda import ColumnProxy, TableProxy
|
9
11
|
|
10
12
|
|
11
|
-
type
|
12
|
-
type
|
13
|
-
type
|
14
|
-
type AliasType[T] = tp.Optional[str | tp.Callable[[T], str]]
|
13
|
+
type TableType[T: Table] = Type[T] | TableProxy[T]
|
14
|
+
type ColumnType[TProp] = TProp | ColumnProxy[TProp]
|
15
|
+
type AliasType[TProp] = str | Callable[[ColumnProxy[TProp]], str]
|
15
16
|
|
16
17
|
# region Comparer Types
|
17
|
-
type ComparerType =
|
18
|
+
type ComparerType = Literal["=", "!=", "<", "<=", ">", ">=", "in"]
|
18
19
|
type ConditionType[TProp] = Comparer | ColumnType[TProp]
|
19
|
-
type UnionType =
|
20
|
+
type UnionType = Literal["AND", "OR", ""]
|
20
21
|
type ComparerTypes = ComparerType | UnionType | ConditionEnum
|
22
|
+
type SelectCol = ColumnProxy | IAggregate | Comparer
|
21
23
|
# endregion
|
22
24
|
|
23
25
|
type TupleJoinType[T] = tuple[Comparer]
|
24
26
|
|
25
|
-
ASTERISK
|
27
|
+
ASTERISK = "*"
|
26
28
|
|
27
|
-
|
29
|
+
# TODOL []: Look if we can avoid this *
|
30
|
+
from .compiler import * # noqa: F403, E402
|
ormlambda/statements/__init__.py
CHANGED
@@ -1,123 +1,85 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import Any, Type,
|
2
|
+
from typing import Any, Type, Iterable, Literal, TYPE_CHECKING
|
3
3
|
from collections import defaultdict
|
4
4
|
|
5
5
|
|
6
|
-
from ormlambda.repository import BaseRepository
|
7
|
-
from ormlambda.statements.interfaces import IStatements_two_generic
|
8
6
|
from ormlambda import Table
|
9
7
|
|
10
8
|
from ormlambda.common.errors import AggregateFunctionError
|
9
|
+
from ormlambda.sql.clause_info import IAggregate
|
10
|
+
|
11
11
|
|
12
12
|
if TYPE_CHECKING:
|
13
13
|
from ormlambda.engine.base import Engine
|
14
|
-
from ormlambda.dialects.interface.dialect import Dialect
|
15
14
|
from ormlambda.sql.clauses import Select
|
15
|
+
from ormlambda import ColumnProxy
|
16
16
|
|
17
17
|
|
18
18
|
ORDER_QUERIES = Literal["select", "join", "where", "order", "with", "group by", "limit", "offset"]
|
19
19
|
|
20
20
|
|
21
|
-
|
22
|
-
def __init__(self, model: tuple[T, ...], engine: Engine) -> None:
|
23
|
-
self._engine = engine
|
24
|
-
self._dialect = engine.dialect
|
25
|
-
self._query: Optional[str] = None
|
26
|
-
self._model: T = model[0] if isinstance(model, Iterable) else model
|
27
|
-
self._models: tuple[T] = self._model if isinstance(model, Iterable) else (model,)
|
21
|
+
type ResponseType = Iterable[dict[str, Any]]
|
28
22
|
|
29
|
-
repository = engine.repository
|
30
|
-
self.__valid_repository(repository)
|
31
|
-
self._repository: BaseRepository[TRepo] = repository
|
32
23
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
24
|
+
class ClusterResponse[T, TFlavour]:
|
25
|
+
def __init__(self, select: Select[T], engine: Engine, flavour: TFlavour, query: str) -> None:
|
26
|
+
self._select: Select[T] = select
|
27
|
+
self.engine = engine
|
28
|
+
self.flavour = flavour
|
29
|
+
self.query = query
|
37
30
|
|
38
|
-
|
39
|
-
|
40
|
-
return self._dialect
|
31
|
+
def cluster(self, response_sql: ResponseType) -> tuple[dict[Type[Table], tuple[Table, ...]]]:
|
32
|
+
# We'll create a default list of dicts *once* we know how many rows are in _response_sql
|
41
33
|
|
42
|
-
|
43
|
-
|
44
|
-
|
34
|
+
tables: dict[Table, list[ColumnProxy]] = defaultdict(list)
|
35
|
+
for clause in self._select.columns:
|
36
|
+
if isinstance(clause, IAggregate):
|
37
|
+
raise AggregateFunctionError(clause)
|
45
38
|
|
46
|
-
|
47
|
-
def __valid_repository(repository: Any) -> bool:
|
48
|
-
if not isinstance(repository, BaseRepository):
|
49
|
-
raise ValueError(f"'repository' attribute does not instance of '{BaseRepository.__name__}'")
|
50
|
-
return True
|
39
|
+
tables[clause.table].append(clause)
|
51
40
|
|
52
|
-
|
53
|
-
|
41
|
+
res = []
|
42
|
+
for dicc_cols in response_sql:
|
43
|
+
converted_row = []
|
44
|
+
for table, columns in tables.items():
|
45
|
+
dicc = {}
|
46
|
+
for col in columns:
|
47
|
+
if not hasattr(col, "column_name"):
|
48
|
+
pass
|
49
|
+
dicc[col.column_name] = dicc_cols[col.alias]
|
50
|
+
converted_row.append(table(**dicc))
|
51
|
+
res.append(tuple(converted_row))
|
54
52
|
|
55
|
-
|
56
|
-
return self._repository.read_sql(query, flavour=flavour, select=select, **kwargs)
|
53
|
+
tuple_response = tuple(res)
|
57
54
|
|
58
|
-
|
59
|
-
|
60
|
-
if response_sql and isinstance(response_sql, Iterable):
|
61
|
-
return ClusterResponse(self._dialect, select, response_sql).cluster()
|
55
|
+
if not tuple_response:
|
56
|
+
return tuple_response
|
62
57
|
|
63
|
-
|
58
|
+
if len(tuple_response) == 1:
|
59
|
+
return tuple_response[0]
|
64
60
|
|
65
|
-
|
66
|
-
|
67
|
-
return
|
61
|
+
if len(tuple_response[0]) == 1:
|
62
|
+
return tuple([x[0] for x in tuple_response])
|
63
|
+
return tuple_response
|
68
64
|
|
69
|
-
|
70
|
-
|
71
|
-
|
65
|
+
def cluster_data(self, **kwargs) -> TFlavour[T, ...]:
|
66
|
+
if not self.flavour:
|
67
|
+
return self._return_model()
|
72
68
|
|
73
|
-
|
74
|
-
@property
|
75
|
-
def models(self) -> tuple:
|
76
|
-
return self._models
|
69
|
+
return self._return_flavour(self.flavour, **kwargs)
|
77
70
|
|
78
|
-
|
79
|
-
|
80
|
-
|
71
|
+
def _return_flavour[TValue](self, flavour: Type[TValue], **kwargs) -> tuple[TValue]:
|
72
|
+
return self.engine.repository.read_sql(
|
73
|
+
query=self.query,
|
74
|
+
flavour=flavour,
|
75
|
+
select=self._select,
|
76
|
+
**kwargs,
|
77
|
+
)
|
81
78
|
|
79
|
+
def _return_model(self) -> tuple[tuple[T]]:
|
80
|
+
response_sql = self._return_flavour(flavour=dict)
|
82
81
|
|
83
|
-
|
84
|
-
|
85
|
-
self._dialect: Dialect = dialect
|
86
|
-
self._select: Select[T] = select
|
87
|
-
self._response_sql: tuple[dict[str, Any]] = response_sql
|
88
|
-
self._caster = dialect.caster
|
89
|
-
|
90
|
-
def cluster(self) -> tuple[dict[Type[Table], tuple[Table, ...]]]:
|
91
|
-
tbl_dicc: dict[Type[Table], list[dict[str, Any]]] = self._create_cluster()
|
92
|
-
|
93
|
-
response = {}
|
94
|
-
tuple_response = []
|
95
|
-
# it not depend of flavour attr
|
96
|
-
for table, attribute_list in tbl_dicc.items():
|
97
|
-
new_instance = []
|
98
|
-
for attrs in attribute_list:
|
99
|
-
new_instance.append(table(**attrs))
|
100
|
-
response[table] = tuple(new_instance)
|
101
|
-
tuple_response.append(tuple(new_instance))
|
102
|
-
return tuple(tuple_response)
|
103
|
-
|
104
|
-
def _create_cluster(self) -> dict[Type[Table], list[dict[str, Any]]]:
|
105
|
-
# We'll create a default list of dicts *once* we know how many rows are in _response_sql
|
106
|
-
row_count = len(self._response_sql)
|
107
|
-
|
108
|
-
def make_list_of_dicts() -> list[dict[str, Any]]:
|
109
|
-
return [{} for _ in range(row_count)]
|
110
|
-
|
111
|
-
table_attr_dict = defaultdict(make_list_of_dicts)
|
112
|
-
|
113
|
-
for i, dicc_cols in enumerate(self._response_sql):
|
114
|
-
for clause in self._select.all_clauses:
|
115
|
-
table = clause.table
|
116
|
-
col = clause.column
|
117
|
-
|
118
|
-
if col is None or not hasattr(table, col):
|
119
|
-
raise AggregateFunctionError(clause)
|
82
|
+
if response_sql and isinstance(response_sql, Iterable):
|
83
|
+
return self.cluster(response_sql)
|
120
84
|
|
121
|
-
|
122
|
-
# Convert back to a normal dict if you like (defaultdict is a dict subclass).
|
123
|
-
return dict(table_attr_dict)
|
85
|
+
return response_sql
|