ormlambda 3.11.2__py3-none-any.whl → 3.34.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 +3 -1
- ormlambda/caster/__init__.py +1 -1
- ormlambda/caster/caster.py +29 -12
- ormlambda/common/abstract_classes/clause_info_converter.py +65 -0
- ormlambda/common/abstract_classes/decomposition_query.py +27 -68
- ormlambda/common/abstract_classes/non_query_base.py +10 -8
- ormlambda/common/abstract_classes/query_base.py +3 -1
- ormlambda/common/errors/__init__.py +29 -0
- ormlambda/common/interfaces/ICustomAlias.py +1 -1
- ormlambda/common/interfaces/IQueryCommand.py +6 -2
- ormlambda/dialects/__init__.py +39 -0
- ormlambda/dialects/default/__init__.py +1 -0
- ormlambda/dialects/default/base.py +39 -0
- ormlambda/dialects/interface/__init__.py +1 -0
- ormlambda/dialects/interface/dialect.py +78 -0
- ormlambda/dialects/mysql/__init__.py +38 -0
- ormlambda/dialects/mysql/base.py +388 -0
- ormlambda/dialects/mysql/caster/caster.py +39 -0
- ormlambda/{databases/my_sql → dialects/mysql}/caster/types/__init__.py +1 -0
- ormlambda/dialects/mysql/caster/types/boolean.py +35 -0
- ormlambda/{databases/my_sql → dialects/mysql}/caster/types/bytes.py +7 -7
- ormlambda/{databases/my_sql → dialects/mysql}/caster/types/datetime.py +7 -7
- ormlambda/{databases/my_sql → dialects/mysql}/caster/types/float.py +7 -7
- ormlambda/{databases/my_sql → dialects/mysql}/caster/types/int.py +7 -7
- ormlambda/{databases/my_sql → dialects/mysql}/caster/types/iterable.py +7 -7
- ormlambda/{databases/my_sql → dialects/mysql}/caster/types/none.py +8 -7
- ormlambda/{databases/my_sql → dialects/mysql}/caster/types/point.py +4 -4
- ormlambda/{databases/my_sql → dialects/mysql}/caster/types/string.py +7 -7
- ormlambda/{databases/my_sql → dialects/mysql}/clauses/ST_AsText.py +8 -7
- ormlambda/{databases/my_sql → dialects/mysql}/clauses/ST_Contains.py +10 -5
- ormlambda/dialects/mysql/clauses/__init__.py +13 -0
- ormlambda/dialects/mysql/clauses/count.py +33 -0
- ormlambda/dialects/mysql/clauses/delete.py +9 -0
- ormlambda/dialects/mysql/clauses/group_by.py +17 -0
- ormlambda/dialects/mysql/clauses/having.py +12 -0
- ormlambda/dialects/mysql/clauses/insert.py +9 -0
- ormlambda/dialects/mysql/clauses/joins.py +14 -0
- ormlambda/dialects/mysql/clauses/limit.py +6 -0
- ormlambda/dialects/mysql/clauses/offset.py +6 -0
- ormlambda/dialects/mysql/clauses/order.py +8 -0
- ormlambda/dialects/mysql/clauses/update.py +8 -0
- ormlambda/dialects/mysql/clauses/upsert.py +9 -0
- ormlambda/dialects/mysql/clauses/where.py +7 -0
- ormlambda/dialects/mysql/mysqlconnector.py +46 -0
- ormlambda/dialects/mysql/repository/__init__.py +1 -0
- ormlambda/dialects/mysql/repository/repository.py +212 -0
- ormlambda/dialects/mysql/types.py +732 -0
- ormlambda/dialects/sqlite/__init__.py +5 -0
- ormlambda/dialects/sqlite/base.py +47 -0
- ormlambda/dialects/sqlite/pysqlite.py +32 -0
- ormlambda/engine/__init__.py +1 -0
- ormlambda/engine/base.py +77 -0
- ormlambda/engine/create.py +9 -23
- ormlambda/engine/url.py +34 -19
- ormlambda/env.py +30 -0
- ormlambda/errors.py +17 -0
- ormlambda/model/base_model.py +7 -9
- ormlambda/repository/base_repository.py +36 -5
- ormlambda/repository/interfaces/IRepositoryBase.py +119 -12
- ormlambda/repository/response.py +134 -0
- ormlambda/sql/clause_info/__init__.py +2 -1
- ormlambda/sql/clause_info/aggregate_function_base.py +96 -0
- ormlambda/sql/clause_info/clause_info.py +35 -115
- ormlambda/sql/clause_info/interface/IClauseInfo.py +37 -0
- ormlambda/sql/clause_info/interface/__init__.py +1 -0
- ormlambda/sql/clauses/__init__.py +14 -0
- ormlambda/{databases/my_sql → sql}/clauses/alias.py +23 -6
- ormlambda/{databases/my_sql → sql}/clauses/count.py +15 -1
- ormlambda/{databases/my_sql → sql}/clauses/delete.py +22 -7
- ormlambda/sql/clauses/group_by.py +30 -0
- ormlambda/{databases/my_sql → sql}/clauses/having.py +7 -2
- ormlambda/{databases/my_sql → sql}/clauses/insert.py +16 -9
- ormlambda/sql/clauses/interfaces/__init__.py +5 -0
- ormlambda/sql/clauses/join/__init__.py +1 -0
- ormlambda/{databases/my_sql → sql/clauses/join}/join_context.py +15 -7
- ormlambda/{databases/my_sql → sql}/clauses/joins.py +29 -19
- ormlambda/sql/clauses/limit.py +15 -0
- ormlambda/sql/clauses/offset.py +15 -0
- ormlambda/{databases/my_sql → sql}/clauses/order.py +14 -24
- ormlambda/{databases/my_sql → sql}/clauses/select.py +14 -13
- ormlambda/{databases/my_sql → sql}/clauses/update.py +24 -11
- ormlambda/{databases/my_sql → sql}/clauses/upsert.py +19 -10
- ormlambda/{databases/my_sql → sql}/clauses/where.py +28 -8
- ormlambda/sql/column/__init__.py +1 -0
- ormlambda/sql/{column.py → column/column.py} +85 -22
- ormlambda/sql/comparer.py +51 -37
- ormlambda/sql/compiler.py +668 -0
- ormlambda/sql/ddl.py +82 -0
- ormlambda/sql/elements.py +36 -0
- ormlambda/sql/foreign_key.py +61 -39
- ormlambda/{databases/my_sql → sql}/functions/concat.py +13 -5
- ormlambda/{databases/my_sql → sql}/functions/max.py +9 -4
- ormlambda/{databases/my_sql → sql}/functions/min.py +9 -13
- ormlambda/{databases/my_sql → sql}/functions/sum.py +8 -10
- ormlambda/sql/sqltypes.py +647 -0
- ormlambda/sql/table/__init__.py +1 -1
- ormlambda/sql/table/table.py +175 -0
- ormlambda/sql/table/table_constructor.py +1 -208
- ormlambda/sql/type_api.py +35 -0
- ormlambda/sql/types.py +3 -1
- ormlambda/sql/visitors.py +74 -0
- ormlambda/statements/__init__.py +1 -0
- ormlambda/statements/base_statement.py +34 -40
- ormlambda/statements/interfaces/IStatements.py +28 -21
- ormlambda/statements/query_builder.py +163 -0
- ormlambda/{databases/my_sql → statements}/statements.py +68 -210
- ormlambda/statements/types.py +2 -2
- ormlambda/types/__init__.py +24 -0
- ormlambda/types/metadata.py +42 -0
- ormlambda/util/__init__.py +87 -0
- ormlambda/{utils → util}/module_tree/dynamic_module.py +4 -3
- ormlambda/util/plugin_loader.py +32 -0
- ormlambda/util/typing.py +6 -0
- ormlambda-3.34.0.dist-info/AUTHORS +32 -0
- {ormlambda-3.11.2.dist-info → ormlambda-3.34.0.dist-info}/METADATA +56 -10
- ormlambda-3.34.0.dist-info/RECORD +152 -0
- ormlambda/components/__init__.py +0 -4
- ormlambda/components/delete/__init__.py +0 -2
- ormlambda/components/delete/abstract_delete.py +0 -17
- ormlambda/components/insert/__init__.py +0 -2
- ormlambda/components/insert/abstract_insert.py +0 -25
- ormlambda/components/select/__init__.py +0 -1
- ormlambda/components/update/__init__.py +0 -2
- ormlambda/components/update/abstract_update.py +0 -29
- ormlambda/components/upsert/__init__.py +0 -2
- ormlambda/components/upsert/abstract_upsert.py +0 -25
- ormlambda/databases/__init__.py +0 -5
- ormlambda/databases/my_sql/__init__.py +0 -4
- ormlambda/databases/my_sql/caster/caster.py +0 -39
- ormlambda/databases/my_sql/clauses/__init__.py +0 -20
- ormlambda/databases/my_sql/clauses/create_database.py +0 -35
- ormlambda/databases/my_sql/clauses/drop_database.py +0 -17
- ormlambda/databases/my_sql/clauses/drop_table.py +0 -23
- ormlambda/databases/my_sql/clauses/group_by.py +0 -31
- ormlambda/databases/my_sql/clauses/limit.py +0 -17
- ormlambda/databases/my_sql/clauses/offset.py +0 -17
- ormlambda/databases/my_sql/repository/__init__.py +0 -1
- ormlambda/databases/my_sql/repository/repository.py +0 -351
- ormlambda/engine/template.py +0 -47
- ormlambda/sql/dtypes.py +0 -94
- ormlambda/utils/__init__.py +0 -1
- ormlambda-3.11.2.dist-info/RECORD +0 -120
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/__init__.py +0 -0
- /ormlambda/{databases/my_sql/types.py → dialects/mysql/repository/pool_types.py} +0 -0
- /ormlambda/{components/delete → sql/clauses/interfaces}/IDelete.py +0 -0
- /ormlambda/{components/insert → sql/clauses/interfaces}/IInsert.py +0 -0
- /ormlambda/{components/select → sql/clauses/interfaces}/ISelect.py +0 -0
- /ormlambda/{components/update → sql/clauses/interfaces}/IUpdate.py +0 -0
- /ormlambda/{components/upsert → sql/clauses/interfaces}/IUpsert.py +0 -0
- /ormlambda/{databases/my_sql → sql}/functions/__init__.py +0 -0
- /ormlambda/{utils → util}/module_tree/__init__.py +0 -0
- /ormlambda/{utils → util}/module_tree/dfs_traversal.py +0 -0
- {ormlambda-3.11.2.dist-info → ormlambda-3.34.0.dist-info}/LICENSE +0 -0
- {ormlambda-3.11.2.dist-info → ormlambda-3.34.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,134 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Type, Any, TYPE_CHECKING, Optional
|
3
|
+
from ormlambda.dialects.interface.dialect import Dialect
|
4
|
+
import shapely as shp
|
5
|
+
|
6
|
+
# Custom libraries
|
7
|
+
from ormlambda.sql.clauses import Alias
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from ormlambda.common.abstract_classes.decomposition_query import ClauseInfo
|
11
|
+
from ormlambda import Table
|
12
|
+
from ormlambda.sql.clauses import Select
|
13
|
+
|
14
|
+
|
15
|
+
type TResponse[TFlavour, *Ts] = TFlavour | tuple[dict[str, tuple[*Ts]]] | tuple[tuple[*Ts]] | tuple[TFlavour]
|
16
|
+
|
17
|
+
|
18
|
+
class Response[TFlavour, *Ts]:
|
19
|
+
def __init__(
|
20
|
+
self,
|
21
|
+
dialect: Dialect,
|
22
|
+
response_values: list[tuple[*Ts]],
|
23
|
+
columns: tuple[str],
|
24
|
+
flavour: Type[TFlavour],
|
25
|
+
select: Optional[Select] = None,
|
26
|
+
) -> None:
|
27
|
+
self._dialect: Dialect = dialect
|
28
|
+
self._response_values: list[tuple[*Ts]] = response_values
|
29
|
+
self._columns: tuple[str] = columns
|
30
|
+
self._flavour: Type[TFlavour] = flavour
|
31
|
+
self._select: Select = select
|
32
|
+
|
33
|
+
self._response_values_index: int = len(self._response_values)
|
34
|
+
|
35
|
+
self._caster = dialect.caster()
|
36
|
+
|
37
|
+
@property
|
38
|
+
def is_one(self) -> bool:
|
39
|
+
return self._response_values_index == 1
|
40
|
+
|
41
|
+
@property
|
42
|
+
def is_there_response(self) -> bool:
|
43
|
+
return self._response_values_index != 0
|
44
|
+
|
45
|
+
@property
|
46
|
+
def is_many(self) -> bool:
|
47
|
+
return self._response_values_index > 1
|
48
|
+
|
49
|
+
def response(self, **kwargs) -> TResponse[TFlavour, *Ts]:
|
50
|
+
if not self.is_there_response:
|
51
|
+
return tuple([])
|
52
|
+
|
53
|
+
# Cast data using caster
|
54
|
+
cleaned_response = self._response_values
|
55
|
+
|
56
|
+
if self._select is not None:
|
57
|
+
cleaned_response = self._clean_response()
|
58
|
+
|
59
|
+
cast_flavour = self._cast_to_flavour(cleaned_response, **kwargs)
|
60
|
+
|
61
|
+
return tuple(cast_flavour)
|
62
|
+
|
63
|
+
def _cast_to_flavour(self, data: list[tuple[*Ts]], **kwargs) -> list[dict[str, tuple[*Ts]]] | list[tuple[*Ts]] | list[TFlavour]:
|
64
|
+
def _dict(**kwargs) -> list[dict[str, tuple[*Ts]]]:
|
65
|
+
nonlocal data
|
66
|
+
return [dict(zip(self._columns, x)) for x in data]
|
67
|
+
|
68
|
+
def _tuple(**kwargs) -> list[tuple[*Ts]]:
|
69
|
+
nonlocal data
|
70
|
+
return data
|
71
|
+
|
72
|
+
def _set(**kwargs) -> list[set]:
|
73
|
+
nonlocal data
|
74
|
+
for d in data:
|
75
|
+
n = len(d)
|
76
|
+
for i in range(n):
|
77
|
+
try:
|
78
|
+
hash(d[i])
|
79
|
+
except TypeError:
|
80
|
+
raise TypeError(f"unhashable type '{type(d[i])}' found in '{type(d)}' when attempting to cast the result into a '{set.__name__}' object")
|
81
|
+
return [set(x) for x in data]
|
82
|
+
|
83
|
+
def _list(**kwargs) -> list[list]:
|
84
|
+
nonlocal data
|
85
|
+
return [list(x) for x in data]
|
86
|
+
|
87
|
+
def _default(**kwargs) -> list[TFlavour]:
|
88
|
+
nonlocal data
|
89
|
+
replacer_dicc: dict[str, str] = {}
|
90
|
+
|
91
|
+
for col in self._select.all_clauses:
|
92
|
+
if hasattr(col, "_alias_aggregate") or col.alias_clause is None or isinstance(col, Alias):
|
93
|
+
continue
|
94
|
+
replacer_dicc[col.alias_clause] = col.column
|
95
|
+
|
96
|
+
cleaned_column_names = [replacer_dicc.get(col, col) for col in self._columns]
|
97
|
+
|
98
|
+
result = []
|
99
|
+
for attr in data:
|
100
|
+
dicc_attr = dict(zip(cleaned_column_names, attr))
|
101
|
+
result.append(self._flavour(**dicc_attr, **kwargs))
|
102
|
+
|
103
|
+
return result
|
104
|
+
|
105
|
+
selector: dict[Type[object], Any] = {
|
106
|
+
dict: _dict,
|
107
|
+
tuple: _tuple,
|
108
|
+
set: _set,
|
109
|
+
list: _list,
|
110
|
+
}
|
111
|
+
return selector.get(self._flavour, _default)(**kwargs)
|
112
|
+
|
113
|
+
def _clean_response(self) -> TFlavour:
|
114
|
+
new_response: list[tuple] = []
|
115
|
+
for row in self._response_values:
|
116
|
+
new_row: list = []
|
117
|
+
for i, data in enumerate(row):
|
118
|
+
alias = self._columns[i]
|
119
|
+
clause_info = self._select[alias]
|
120
|
+
parse_data = self._caster.for_value(data, value_type=clause_info.dtype).from_database
|
121
|
+
new_row.append(parse_data)
|
122
|
+
new_row = tuple(new_row)
|
123
|
+
if not isinstance(new_row, tuple):
|
124
|
+
new_row = tuple(new_row)
|
125
|
+
|
126
|
+
new_response.append(new_row)
|
127
|
+
return new_response
|
128
|
+
|
129
|
+
@staticmethod
|
130
|
+
def _is_parser_required[T: Table](clause_info: ClauseInfo[T]) -> bool:
|
131
|
+
if clause_info is None:
|
132
|
+
return False
|
133
|
+
|
134
|
+
return clause_info.dtype is shp.Point
|
@@ -1,3 +1,4 @@
|
|
1
1
|
from .interface import IAggregate # noqa: F401
|
2
|
-
from .clause_info import ClauseInfo
|
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,96 @@
|
|
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,26 +1,24 @@
|
|
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
|
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
|
-
from ormlambda.caster import Caster
|
20
15
|
|
21
16
|
|
22
17
|
from .clause_info_context import ClauseInfoContext, ClauseContextType
|
23
18
|
|
19
|
+
if tp.TYPE_CHECKING:
|
20
|
+
from ormlambda.dialects import Dialect
|
21
|
+
|
24
22
|
|
25
23
|
class ReplacePlaceholderError(ValueError):
|
26
24
|
def __init__(self, placeholder: str, attribute: str, *args):
|
@@ -32,30 +30,6 @@ class ReplacePlaceholderError(ValueError):
|
|
32
30
|
return "You cannot use {" + self.placeholder + "} placeholder without using '" + self.attr + "' attribute"
|
33
31
|
|
34
32
|
|
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
33
|
class ClauseInfo[T: Table](IClauseInfo[T]):
|
60
34
|
_keyRegex: re.Pattern = re.compile(r"{([^{}:]+)}")
|
61
35
|
|
@@ -73,6 +47,11 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
73
47
|
def __init__(self, table: TableType[T], keep_asterisk: tp.Optional[bool] = ...): ...
|
74
48
|
@tp.overload
|
75
49
|
def __init__(self, table: TableType[T], preserve_context: tp.Optional[bool] = ...): ...
|
50
|
+
@tp.overload
|
51
|
+
def __init__[TProp](self, table: TableType[T], dtype: tp.Optional[TProp] = ...): ...
|
52
|
+
|
53
|
+
@tp.overload
|
54
|
+
def __init__(self, dialect: Dialect, *args, **kwargs): ...
|
76
55
|
|
77
56
|
def __init__[TProp](
|
78
57
|
self,
|
@@ -83,6 +62,10 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
83
62
|
context: ClauseContextType = None,
|
84
63
|
keep_asterisk: bool = False,
|
85
64
|
preserve_context: bool = False,
|
65
|
+
dtype: tp.Optional[TProp] = None,
|
66
|
+
*,
|
67
|
+
dialect: Dialect,
|
68
|
+
**kw,
|
86
69
|
):
|
87
70
|
if not self.is_table(table):
|
88
71
|
column = table if not column else column
|
@@ -95,6 +78,9 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
95
78
|
self._context: ClauseContextType = context if context else ClauseInfoContext()
|
96
79
|
self._keep_asterisk: bool = keep_asterisk
|
97
80
|
self._preserve_context: bool = preserve_context
|
81
|
+
self._dtype = dtype
|
82
|
+
|
83
|
+
self._dialect: Dialect = dialect
|
98
84
|
|
99
85
|
self._placeholderValues: dict[str, tp.Callable[[TProp], str]] = {
|
100
86
|
"column": self.replace_column_placeholder,
|
@@ -104,8 +90,10 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
104
90
|
if not self._preserve_context and (self._context and any([alias_table, alias_clause])):
|
105
91
|
self._context.add_clause_to_context(self)
|
106
92
|
|
93
|
+
super().__init__(**kw)
|
94
|
+
|
107
95
|
def __repr__(self) -> str:
|
108
|
-
return f"{type(self).__name__}: query -> {self.query}"
|
96
|
+
return f"{type(self).__name__}: query -> {self.query(self._dialect)}"
|
109
97
|
|
110
98
|
def replace_column_placeholder[TProp](self, column: ColumnType[TProp]) -> str:
|
111
99
|
if not column:
|
@@ -168,6 +156,9 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
168
156
|
|
169
157
|
@property
|
170
158
|
def dtype[TProp](self) -> tp.Optional[tp.Type[TProp]]:
|
159
|
+
if self._dtype is not None:
|
160
|
+
return self._dtype
|
161
|
+
|
171
162
|
if isinstance(self._column, Column):
|
172
163
|
return self._column.dtype
|
173
164
|
|
@@ -175,11 +166,10 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
175
166
|
return self._column
|
176
167
|
return type(self._column)
|
177
168
|
|
178
|
-
|
179
|
-
|
180
|
-
return self._create_query()
|
169
|
+
def query(self, dialect: Dialect, **kwargs) -> str:
|
170
|
+
return self._create_query(dialect, **kwargs)
|
181
171
|
|
182
|
-
def _create_query(self) -> str:
|
172
|
+
def _create_query(self, dialect: Dialect, **kwargs) -> str:
|
183
173
|
# when passing some value that is not a column name
|
184
174
|
if not self.table and not self._alias_clause:
|
185
175
|
return self.column
|
@@ -201,10 +191,9 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
201
191
|
return self._join_table_and_column(self._column)
|
202
192
|
|
203
193
|
def _join_table_and_column[TProp](self, column: ColumnType[TProp]) -> str:
|
204
|
-
# FIXME [
|
205
|
-
from ormlambda.databases.my_sql.repository import MySQLRepository
|
194
|
+
# FIXME [x]: Study how to deacoplate from mysql database
|
206
195
|
|
207
|
-
caster =
|
196
|
+
caster = self._dialect.caster()
|
208
197
|
|
209
198
|
if self.alias_table:
|
210
199
|
table = self._wrapped_with_quotes(self.alias_table)
|
@@ -241,22 +230,20 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
241
230
|
alias_clause=self._alias_clause,
|
242
231
|
context=self._context,
|
243
232
|
keep_asterisk=self._keep_asterisk,
|
233
|
+
dialect=self._dialect,
|
244
234
|
)
|
245
235
|
|
246
236
|
if self._alias_table and self._alias_clause: # We'll add an "*" when we are certain that we have included 'alias_clause' attr
|
247
237
|
return self._join_table_and_column(ASTERISK)
|
248
238
|
|
249
|
-
columns: list[ClauseInfo] = [ClauseCreator(column).query for column in self.table.get_columns()]
|
239
|
+
columns: list[ClauseInfo] = [ClauseCreator(column).query(self._dialect) for column in self.table.get_columns()]
|
250
240
|
|
251
241
|
return ", ".join(columns)
|
252
242
|
|
253
|
-
# FIXME [
|
243
|
+
# FIXME [x]: Study how to deacoplate from mysql database
|
254
244
|
def _column_resolver[TProp](self, column: ColumnType[TProp]) -> str:
|
255
|
-
from ormlambda.databases.my_sql.repository import MySQLRepository
|
256
|
-
|
257
|
-
caster = Caster(MySQLRepository)
|
258
245
|
if isinstance(column, ClauseInfo):
|
259
|
-
return column.query
|
246
|
+
return column.query(self._dialect)
|
260
247
|
|
261
248
|
if isinstance(column, tp.Iterable) and isinstance(column[0], ClauseInfo):
|
262
249
|
return self.join_clauses(column)
|
@@ -277,6 +264,7 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
277
264
|
if self.is_foreign_key(self._column):
|
278
265
|
return self._column.tright.__table_name__
|
279
266
|
|
267
|
+
caster = self._dialect.caster()
|
280
268
|
casted_value = caster.for_value(column, self.dtype)
|
281
269
|
if not self._table:
|
282
270
|
# if we haven't some table atrribute, we assume that the user want to retrieve the string_data from caster.
|
@@ -320,12 +308,13 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
320
308
|
return self._context.get_table_alias(self.table)
|
321
309
|
|
322
310
|
@staticmethod
|
323
|
-
def join_clauses(clauses: list[ClauseInfo[T]], chr: str = ",", context: tp.Optional[ClauseInfoContext] = None) -> str:
|
311
|
+
def join_clauses(clauses: list[ClauseInfo[T]], chr: str = ",", context: tp.Optional[ClauseInfoContext] = None, *, dialect: Dialect) -> str:
|
324
312
|
queries: list[str] = []
|
325
313
|
for c in clauses:
|
326
314
|
if context:
|
327
315
|
c.context = context
|
328
|
-
|
316
|
+
c._dialect = dialect
|
317
|
+
queries.append(c.query(dialect))
|
329
318
|
|
330
319
|
return f"{chr} ".join(queries)
|
331
320
|
|
@@ -363,72 +352,3 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
363
352
|
if isinstance(data, Column):
|
364
353
|
return True
|
365
354
|
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]]: ...
|
@@ -0,0 +1,14 @@
|
|
1
|
+
from .alias import Alias # noqa: F401
|
2
|
+
from .count import Count # noqa: F401
|
3
|
+
from .delete import Delete # noqa: F401
|
4
|
+
from .group_by import GroupBy # noqa: F401
|
5
|
+
from .insert import Insert # noqa: F401
|
6
|
+
from .joins import JoinSelector # noqa: F401
|
7
|
+
from .limit import Limit # noqa: F401
|
8
|
+
from .offset import Offset # noqa: F401
|
9
|
+
from .order import Order # noqa: F401
|
10
|
+
from .select import Select # noqa: F401
|
11
|
+
from .where import Where # noqa: F401
|
12
|
+
from .having import Having # noqa: F401
|
13
|
+
from .update import Update # noqa: F401
|
14
|
+
from .upsert import Upsert # noqa: F401
|
@@ -1,18 +1,21 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
import typing as tp
|
3
3
|
|
4
|
-
from ormlambda import Table
|
5
4
|
from ormlambda.sql.clause_info import ClauseInfo
|
6
|
-
from ormlambda.sql.
|
7
|
-
|
5
|
+
from ormlambda.sql.elements import ClauseElement
|
6
|
+
|
8
7
|
|
9
8
|
if tp.TYPE_CHECKING:
|
10
|
-
from ormlambda.sql.types import ColumnType
|
11
9
|
from ormlambda import Table
|
10
|
+
from ormlambda.sql.clause_info.clause_info_context import ClauseContextType
|
11
|
+
from ormlambda.sql.types import TableType
|
12
|
+
from ormlambda.sql.types import ColumnType
|
12
13
|
from ormlambda.sql.types import AliasType
|
13
14
|
|
14
15
|
|
15
|
-
class Alias[T: Table](ClauseInfo[T]):
|
16
|
+
class Alias[T: Table](ClauseInfo[T], ClauseElement):
|
17
|
+
__visit_name__ = "alias"
|
18
|
+
|
16
19
|
def __init__[TProp](
|
17
20
|
self,
|
18
21
|
table: TableType[T],
|
@@ -22,7 +25,21 @@ class Alias[T: Table](ClauseInfo[T]):
|
|
22
25
|
context: ClauseContextType = None,
|
23
26
|
keep_asterisk: bool = False,
|
24
27
|
preserve_context: bool = False,
|
28
|
+
**kw,
|
25
29
|
):
|
26
30
|
if not alias_clause:
|
27
31
|
raise TypeError
|
28
|
-
super().__init__(
|
32
|
+
super().__init__(
|
33
|
+
table,
|
34
|
+
column,
|
35
|
+
alias_table=alias_table,
|
36
|
+
alias_clause=alias_clause,
|
37
|
+
context=context,
|
38
|
+
keep_asterisk=keep_asterisk,
|
39
|
+
preserve_context=preserve_context,
|
40
|
+
dtype=None,
|
41
|
+
**kw,
|
42
|
+
)
|
43
|
+
|
44
|
+
|
45
|
+
__all__ = ["Alias"]
|
@@ -9,13 +9,18 @@ from ormlambda import Table
|
|
9
9
|
import typing as tp
|
10
10
|
|
11
11
|
from ormlambda.sql.types import ASTERISK
|
12
|
+
from ormlambda.sql.elements import ClauseElement
|
13
|
+
|
12
14
|
|
13
15
|
if tp.TYPE_CHECKING:
|
14
16
|
from ormlambda import Table
|
15
17
|
from ormlambda.sql.types import ColumnType, AliasType, TableType
|
18
|
+
from ormlambda.dialects import Dialect
|
19
|
+
|
16
20
|
|
21
|
+
class Count[T: Table](AggregateFunctionBase[T], ClauseElement):
|
22
|
+
__visit_name__ = "count"
|
17
23
|
|
18
|
-
class Count[T: Table](AggregateFunctionBase[T]):
|
19
24
|
@staticmethod
|
20
25
|
def FUNCTION_NAME() -> str:
|
21
26
|
return "COUNT"
|
@@ -28,6 +33,9 @@ class Count[T: Table](AggregateFunctionBase[T]):
|
|
28
33
|
context: ClauseContextType = None,
|
29
34
|
keep_asterisk: bool = True,
|
30
35
|
preserve_context: bool = True,
|
36
|
+
*,
|
37
|
+
dialect: Dialect,
|
38
|
+
**kw,
|
31
39
|
) -> None:
|
32
40
|
table = self.extract_table(element)
|
33
41
|
column = element if self.is_column(element) else ASTERISK
|
@@ -40,4 +48,10 @@ class Count[T: Table](AggregateFunctionBase[T]):
|
|
40
48
|
context=context,
|
41
49
|
keep_asterisk=keep_asterisk,
|
42
50
|
preserve_context=preserve_context,
|
51
|
+
dtype=int,
|
52
|
+
dialect=dialect,
|
53
|
+
**kw,
|
43
54
|
)
|
55
|
+
|
56
|
+
|
57
|
+
__all__ = ["Count"]
|