ormlambda 2.11.1__py3-none-any.whl → 3.7.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 +11 -9
- ormlambda/caster/__init__.py +3 -0
- ormlambda/caster/base_caster.py +69 -0
- ormlambda/caster/caster.py +48 -0
- ormlambda/caster/interfaces/ICaster.py +26 -0
- ormlambda/caster/interfaces/__init__.py +1 -0
- ormlambda/common/__init__.py +1 -1
- ormlambda/common/abstract_classes/__init__.py +3 -3
- ormlambda/common/abstract_classes/decomposition_query.py +117 -315
- ormlambda/common/abstract_classes/non_query_base.py +1 -1
- ormlambda/common/enums/condition_types.py +2 -1
- ormlambda/common/enums/join_type.py +4 -1
- ormlambda/common/errors/__init__.py +15 -2
- ormlambda/common/global_checker.py +28 -0
- ormlambda/common/interfaces/ICustomAlias.py +4 -1
- ormlambda/common/interfaces/IDecompositionQuery.py +9 -34
- ormlambda/common/interfaces/IJoinSelector.py +21 -0
- ormlambda/common/interfaces/__init__.py +4 -6
- ormlambda/components/__init__.py +4 -0
- ormlambda/components/insert/abstract_insert.py +1 -1
- ormlambda/components/select/ISelect.py +17 -0
- ormlambda/components/select/__init__.py +1 -0
- ormlambda/components/update/abstract_update.py +4 -4
- ormlambda/components/upsert/abstract_upsert.py +1 -1
- ormlambda/databases/__init__.py +5 -0
- ormlambda/databases/my_sql/__init__.py +3 -1
- ormlambda/databases/my_sql/caster/__init__.py +1 -0
- ormlambda/databases/my_sql/caster/caster.py +38 -0
- ormlambda/databases/my_sql/caster/read.py +39 -0
- ormlambda/databases/my_sql/caster/types/__init__.py +8 -0
- ormlambda/databases/my_sql/caster/types/bytes.py +31 -0
- ormlambda/databases/my_sql/caster/types/datetime.py +34 -0
- ormlambda/databases/my_sql/caster/types/float.py +31 -0
- ormlambda/databases/my_sql/caster/types/int.py +31 -0
- ormlambda/databases/my_sql/caster/types/iterable.py +31 -0
- ormlambda/databases/my_sql/caster/types/none.py +30 -0
- ormlambda/databases/my_sql/caster/types/point.py +43 -0
- ormlambda/databases/my_sql/caster/types/string.py +31 -0
- ormlambda/databases/my_sql/caster/write.py +37 -0
- ormlambda/databases/my_sql/clauses/ST_AsText.py +36 -0
- ormlambda/databases/my_sql/clauses/ST_Contains.py +31 -0
- ormlambda/databases/my_sql/clauses/__init__.py +6 -4
- ormlambda/databases/my_sql/clauses/alias.py +24 -21
- ormlambda/databases/my_sql/clauses/count.py +32 -28
- ormlambda/databases/my_sql/clauses/create_database.py +3 -4
- ormlambda/databases/my_sql/clauses/delete.py +10 -10
- ormlambda/databases/my_sql/clauses/drop_database.py +3 -5
- ormlambda/databases/my_sql/clauses/drop_table.py +3 -3
- ormlambda/databases/my_sql/clauses/group_by.py +4 -7
- ormlambda/databases/my_sql/clauses/insert.py +33 -19
- ormlambda/databases/my_sql/clauses/joins.py +66 -59
- ormlambda/databases/my_sql/clauses/limit.py +1 -1
- ormlambda/databases/my_sql/clauses/offset.py +1 -1
- ormlambda/databases/my_sql/clauses/order.py +36 -23
- ormlambda/databases/my_sql/clauses/select.py +25 -36
- ormlambda/databases/my_sql/clauses/update.py +38 -13
- ormlambda/databases/my_sql/clauses/upsert.py +2 -2
- ormlambda/databases/my_sql/clauses/where.py +45 -0
- ormlambda/databases/my_sql/functions/concat.py +24 -27
- ormlambda/databases/my_sql/functions/max.py +32 -28
- ormlambda/databases/my_sql/functions/min.py +32 -28
- ormlambda/databases/my_sql/functions/sum.py +32 -28
- ormlambda/databases/my_sql/join_context.py +75 -0
- ormlambda/databases/my_sql/repository/__init__.py +1 -0
- ormlambda/databases/my_sql/{repository.py → repository/repository.py} +104 -73
- ormlambda/databases/my_sql/statements.py +231 -153
- ormlambda/engine/__init__.py +0 -0
- ormlambda/engine/template.py +47 -0
- ormlambda/model/__init__.py +0 -0
- ormlambda/model/base_model.py +37 -0
- ormlambda/repository/__init__.py +2 -0
- ormlambda/repository/base_repository.py +14 -0
- ormlambda/repository/interfaces/IDatabaseConnection.py +12 -0
- ormlambda/{common → repository}/interfaces/IRepositoryBase.py +6 -5
- ormlambda/repository/interfaces/__init__.py +2 -0
- ormlambda/sql/__init__.py +3 -0
- ormlambda/sql/clause_info/__init__.py +3 -0
- ormlambda/sql/clause_info/clause_info.py +434 -0
- ormlambda/sql/clause_info/clause_info_context.py +87 -0
- ormlambda/sql/clause_info/interface/IAggregate.py +10 -0
- ormlambda/sql/clause_info/interface/__init__.py +1 -0
- ormlambda/sql/column.py +126 -0
- ormlambda/sql/comparer.py +156 -0
- ormlambda/sql/foreign_key.py +115 -0
- ormlambda/sql/interfaces/__init__.py +0 -0
- ormlambda/sql/table/__init__.py +1 -0
- ormlambda/{utils → sql/table}/fields.py +6 -5
- ormlambda/{utils → sql/table}/table_constructor.py +43 -91
- ormlambda/sql/types.py +25 -0
- ormlambda/statements/__init__.py +2 -0
- ormlambda/statements/base_statement.py +129 -0
- ormlambda/statements/interfaces/IStatements.py +309 -0
- ormlambda/statements/interfaces/__init__.py +1 -0
- ormlambda/statements/types.py +51 -0
- ormlambda/utils/__init__.py +1 -3
- ormlambda/utils/module_tree/__init__.py +1 -0
- ormlambda/utils/module_tree/dynamic_module.py +20 -14
- {ormlambda-2.11.1.dist-info → ormlambda-3.7.0.dist-info}/METADATA +132 -68
- ormlambda-3.7.0.dist-info/RECORD +117 -0
- ormlambda/common/abstract_classes/abstract_model.py +0 -115
- ormlambda/common/interfaces/IAggregate.py +0 -10
- ormlambda/common/interfaces/IStatements.py +0 -348
- ormlambda/components/where/__init__.py +0 -1
- ormlambda/components/where/abstract_where.py +0 -15
- ormlambda/databases/my_sql/clauses/where_condition.py +0 -222
- ormlambda/model_base.py +0 -36
- ormlambda/utils/column.py +0 -105
- ormlambda/utils/foreign_key.py +0 -81
- ormlambda/utils/lambda_disassembler/__init__.py +0 -4
- ormlambda/utils/lambda_disassembler/dis_types.py +0 -133
- ormlambda/utils/lambda_disassembler/disassembler.py +0 -69
- ormlambda/utils/lambda_disassembler/dtypes.py +0 -103
- ormlambda/utils/lambda_disassembler/name_of.py +0 -41
- ormlambda/utils/lambda_disassembler/nested_element.py +0 -44
- ormlambda/utils/lambda_disassembler/tree_instruction.py +0 -145
- ormlambda-2.11.1.dist-info/RECORD +0 -81
- /ormlambda/{utils → sql}/dtypes.py +0 -0
- {ormlambda-2.11.1.dist-info → ormlambda-3.7.0.dist-info}/LICENSE +0 -0
- {ormlambda-2.11.1.dist-info → ormlambda-3.7.0.dist-info}/WHEEL +0 -0
@@ -1,322 +1,160 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from collections import defaultdict
|
3
2
|
import typing as tp
|
4
|
-
import inspect
|
5
3
|
import abc
|
6
|
-
from ormlambda import Table
|
4
|
+
from ormlambda import Table, Column
|
7
5
|
|
8
|
-
from ormlambda.
|
9
|
-
from ormlambda.
|
10
|
-
from ormlambda.
|
11
|
-
from ormlambda import
|
12
|
-
from ormlambda
|
6
|
+
from ormlambda.common.interfaces import IDecompositionQuery, ICustomAlias
|
7
|
+
from ormlambda.sql.clause_info import IAggregate
|
8
|
+
from ormlambda.sql.clause_info import ClauseInfo, AggregateFunctionBase
|
9
|
+
from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext, ClauseContextType
|
10
|
+
from ormlambda import ForeignKey
|
11
|
+
from ormlambda.common.global_checker import GlobalChecker
|
13
12
|
|
14
|
-
from
|
13
|
+
from ormlambda.sql.types import AliasType, TableType, ColumnType
|
15
14
|
|
16
|
-
type ClauseDataType = property | str | ICustomAlias
|
17
|
-
type AliasType[T] = tp.Type[Table] | tp.Callable[[tp.Type[Table]], T]
|
18
15
|
|
16
|
+
type TableTupleType[T, *Ts] = tuple[T:TableType, *Ts]
|
19
17
|
type ValueType = tp.Union[
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
ICustomAlias,
|
18
|
+
tp.Type[IAggregate],
|
19
|
+
tp.Type[Table],
|
20
|
+
tp.Type[str],
|
21
|
+
tp.Type[ICustomAlias],
|
25
22
|
]
|
26
23
|
|
27
24
|
|
28
|
-
class
|
29
|
-
@
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
@property
|
47
|
-
def table(self) -> T:
|
48
|
-
return self._table
|
49
|
-
|
50
|
-
@property
|
51
|
-
def column(self) -> ClauseDataType:
|
52
|
-
return self._column
|
53
|
-
|
54
|
-
@property
|
55
|
-
def alias(self) -> str:
|
56
|
-
return self._alias
|
57
|
-
|
58
|
-
@property
|
59
|
-
def query(self) -> str:
|
60
|
-
return self._query
|
61
|
-
|
62
|
-
@property
|
63
|
-
def dtype[TProp](self) -> tp.Optional[tp.Type[TProp]]:
|
64
|
-
try:
|
65
|
-
return self._table.get_column(self.column).dtype
|
66
|
-
except ValueError:
|
67
|
-
return None
|
68
|
-
|
69
|
-
def _resolve_column(self, data: ClauseDataType) -> str:
|
70
|
-
if isinstance(data, property):
|
71
|
-
return self._table.__properties_mapped__[data]
|
72
|
-
|
73
|
-
elif isinstance(data, IAggregate):
|
74
|
-
return data.alias_name
|
75
|
-
|
76
|
-
elif isinstance(data, str):
|
77
|
-
# TODOL: refactor to base the condition in dict with '*' as key. '*' must to work as special character
|
78
|
-
return f"'{data}'" if data != DecompositionQueryBase.CHAR else data
|
25
|
+
class ClauseInfoConverter[T, TProp](abc.ABC):
|
26
|
+
@classmethod
|
27
|
+
@abc.abstractmethod
|
28
|
+
def convert(cls, data: T, alias_table: AliasType[ColumnType[TProp]] = "{table}", context: ClauseContextType = None) -> list[ClauseInfo[T]]: ...
|
29
|
+
|
30
|
+
|
31
|
+
class ConvertFromAnyType(ClauseInfoConverter[None, None]):
|
32
|
+
@classmethod
|
33
|
+
def convert(cls, data: tp.Any, alias_table: AliasType = "{table}", context: ClauseContextType = None) -> list[ClauseInfo[None]]:
|
34
|
+
return [
|
35
|
+
ClauseInfo[None](
|
36
|
+
table=None,
|
37
|
+
column=data,
|
38
|
+
alias_table=alias_table,
|
39
|
+
alias_clause=None,
|
40
|
+
context=context,
|
41
|
+
)
|
42
|
+
]
|
79
43
|
|
80
|
-
elif isinstance(data, ICustomAlias):
|
81
|
-
return data.all_clauses[0].column
|
82
44
|
|
83
|
-
|
84
|
-
|
45
|
+
class ConvertFromForeignKey[LT: Table, RT: Table](ClauseInfoConverter[RT, None]):
|
46
|
+
@classmethod
|
47
|
+
def convert(cls, data: ForeignKey[LT, RT], alias_table=None, context: ClauseContextType = None) -> list[ClauseInfo[RT]]:
|
48
|
+
return ConvertFromTable[RT].convert(data.tright, data.alias, context)
|
85
49
|
|
86
|
-
def __create_value_string(self, name: str) -> str:
|
87
|
-
if isinstance(self._row_column, property):
|
88
|
-
return self.concat_with_alias(f"{self._table.__table_name__}.{name}")
|
89
50
|
|
90
|
-
|
91
|
-
|
51
|
+
class ConvertFromColumn[TProp](ClauseInfoConverter[None, TProp]):
|
52
|
+
@classmethod
|
53
|
+
def convert(cls, data: ColumnType[TProp], alias_table: AliasType[ColumnType[TProp]] = "{table}", context: ClauseContextType = None) -> list[ClauseInfo[None]]:
|
54
|
+
# COMMENT: if the property belongs to the main class, the columnn name in not prefixed. This only done if the property comes from any join.
|
55
|
+
clause_info = ClauseInfo[None](
|
56
|
+
table=data.table,
|
57
|
+
column=data,
|
58
|
+
alias_table=alias_table,
|
59
|
+
alias_clause="{table}_{column}",
|
60
|
+
context=context,
|
61
|
+
)
|
62
|
+
return [clause_info]
|
92
63
|
|
93
|
-
return self.concat_with_alias(self.column)
|
94
64
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
return
|
65
|
+
class ConvertFromIAggregate(ClauseInfoConverter[None, None]):
|
66
|
+
@classmethod
|
67
|
+
def convert(cls, data: AggregateFunctionBase, alias_table=None, context: ClauseContextType = None) -> list[ClauseInfo[None]]:
|
68
|
+
return [data]
|
99
69
|
|
100
70
|
|
101
|
-
class
|
102
|
-
|
71
|
+
class ConvertFromTable[T: Table](ClauseInfoConverter[T, None]):
|
72
|
+
@classmethod
|
73
|
+
def convert(cls, data: T, alias_table: AliasType[ColumnType] = "{table}", context: ClauseContextType = None) -> list[ClauseInfo[T]]:
|
74
|
+
"""
|
75
|
+
if the data is Table, means that we want to retrieve all columns
|
76
|
+
"""
|
77
|
+
return cls._extract_all_clauses(data, alias_table, context)
|
103
78
|
|
104
79
|
@staticmethod
|
105
|
-
def
|
106
|
-
|
80
|
+
def _extract_all_clauses(table: TableType[T], alias_table: AliasType[ColumnType], context: ClauseContextType = None) -> list[ClauseInfo[TableType[T]]]:
|
81
|
+
# all columns
|
82
|
+
column_clauses = []
|
83
|
+
for column in table.get_columns():
|
84
|
+
column_clauses.extend(ConvertFromColumn.convert(column, alias_table=alias_table, context=context))
|
85
|
+
return column_clauses
|
107
86
|
|
87
|
+
|
88
|
+
class DecompositionQueryBase[T: Table, *Ts](IDecompositionQuery[T, *Ts]):
|
108
89
|
@tp.overload
|
109
|
-
def __init__(self, tables:
|
110
|
-
@tp.overload
|
111
|
-
def __init__(self, tables: T, lambda_query: tp.Callable[[T], tuple], *, alias: bool = ...) -> None: ...
|
112
|
-
@tp.overload
|
113
|
-
def __init__(self, tables: T, lambda_query: tp.Callable[[T], tuple], *, alias: bool = ..., alias_name: tp.Optional[str] = ...) -> None: ...
|
114
|
-
@tp.overload
|
115
|
-
def __init__(self, tables: T, lambda_query: tp.Callable[[T], tuple], *, alias: bool = ..., alias_name: tp.Optional[str] = ..., by: JoinType = ...) -> None: ...
|
90
|
+
def __init__(self, tables: tuple[TableType[T]], columns: tuple[ColumnType]) -> None: ...
|
116
91
|
@tp.overload
|
117
|
-
def __init__(self, tables:
|
92
|
+
def __init__(self, tables: tuple[TableType[T]], columns: tuple[ColumnType], context: ClauseContextType = ...) -> None: ...
|
118
93
|
@tp.overload
|
119
|
-
def __init__(self, tables:
|
120
|
-
|
121
|
-
def __init__(
|
122
|
-
self,
|
123
|
-
tables: tuple[T, *Ts],
|
124
|
-
lambda_query: tp.Callable[[T], tuple[*Ts]],
|
125
|
-
*,
|
126
|
-
alias: bool = True,
|
127
|
-
alias_name: tp.Optional[str] = None,
|
128
|
-
by: JoinType = JoinType.INNER_JOIN,
|
129
|
-
replace_asterisk_char: bool = True,
|
130
|
-
joins: tp.Optional[list[JoinType]] = None,
|
131
|
-
) -> None:
|
132
|
-
self._tables: tuple[T, *Ts] = tables if isinstance(tables, tp.Iterable) else (tables,)
|
133
|
-
self._lambda_query: tp.Callable[[T], tuple] = lambda_query
|
134
|
-
self._alias: bool = alias
|
135
|
-
self._alias_name: tp.Optional[str] = alias_name
|
136
|
-
self._by: JoinType = by
|
137
|
-
self._joins: set[JoinSelector] = set(joins) if joins is not None else set()
|
138
|
-
|
139
|
-
self._clauses_group_by_tables: dict[tp.Type[Table], list[ClauseInfo[T]]] = defaultdict(list)
|
140
|
-
self._all_clauses: list[ClauseInfo] = []
|
141
|
-
self.alias_cache: dict[str, AliasType] = {self.CHAR: self._asterik_resolver}
|
142
|
-
self._replace_asterisk_char: bool = replace_asterisk_char
|
143
|
-
self.__assign_lambda_variables_to_table(lambda_query)
|
144
|
-
|
145
|
-
self.__clauses_list_generetor(lambda_query)
|
146
|
-
|
147
|
-
def __getitem__(self, key: str) -> ClauseInfo:
|
148
|
-
for clause in self._all_clauses:
|
149
|
-
if clause.alias == key:
|
150
|
-
return clause
|
151
|
-
|
152
|
-
def alias_children_resolver[Tclause: tp.Type[Table]](self, clause_info: ClauseInfo[Tclause]):
|
153
|
-
DEFAULT_ALIAS: str = f"{clause_info._table.__table_name__}_{clause_info._column}"
|
94
|
+
def __init__(self, tables: tuple[TableType[T]], columns: tuple[ColumnType], alias_table: str, context: ClauseContextType = ...) -> None: ...
|
154
95
|
|
155
|
-
|
156
|
-
|
157
|
-
return DEFAULT_ALIAS
|
96
|
+
def __init__(self, tables: tuple[TableType[T]], columns: tuple[ColumnType], alias_table: str = "{table}", *, context: ClauseContextType = ClauseInfoContext()) -> None:
|
97
|
+
self._tables: tuple[TableType[T]] = tables if isinstance(tables, tp.Iterable) else (tables,)
|
158
98
|
|
159
|
-
|
160
|
-
|
161
|
-
|
99
|
+
self._columns: tp.Callable[[T], tuple] = columns
|
100
|
+
self._all_clauses: list[ClauseInfo | AggregateFunctionBase] = []
|
101
|
+
self._context: ClauseContextType = context if context else ClauseInfoContext()
|
102
|
+
self._alias_table: str = alias_table
|
103
|
+
self.__clauses_list_generetor()
|
162
104
|
|
105
|
+
def __getitem__(self, key: str) -> ClauseInfo | AggregateFunctionBase:
|
106
|
+
for clause in self._all_clauses:
|
107
|
+
is_agg_function = isinstance(clause, AggregateFunctionBase)
|
108
|
+
is_clause_info = isinstance(clause, ClauseInfo)
|
109
|
+
if (is_agg_function and clause._alias_aggregate == key) or (is_clause_info and clause.alias_clause == key):
|
110
|
+
return clause
|
163
111
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
>>> # "a": Address,
|
168
|
-
>>> # "ci": City,
|
169
|
-
>>> # "co": Country,
|
170
|
-
>>> # }
|
171
|
-
"""
|
172
|
-
lambda_vars = tuple(inspect.signature(_lambda).parameters)
|
173
|
-
|
174
|
-
if len(lambda_vars) != (expected := len(self._tables)):
|
175
|
-
raise UnmatchedLambdaParameterError(expected, found_param=lambda_vars)
|
176
|
-
|
177
|
-
# COMMENT: We don't pass a lambda method because lambda reads the las value of 'i'
|
178
|
-
for i, param in enumerate(lambda_vars):
|
179
|
-
self.alias_cache[param] = self._tables[i]
|
180
|
-
return None
|
181
|
-
|
182
|
-
def __clauses_list_generetor(self, function: tp.Callable[[T], tp.Any]) -> None:
|
183
|
-
if not callable(function):
|
184
|
-
return None
|
112
|
+
def __clauses_list_generetor(self) -> None:
|
113
|
+
# Clean self._all_clauses if we update the context
|
114
|
+
self._all_clauses.clear() if self._all_clauses else None
|
185
115
|
|
186
|
-
resolved_function =
|
116
|
+
resolved_function = GlobalChecker.resolved_callback_object(self._columns, self.tables)
|
187
117
|
|
188
|
-
tree_list = TreeInstruction(function).to_list()
|
189
118
|
# Python treats string objects as iterable, so we need to prevent this behavior
|
190
119
|
if isinstance(resolved_function, str) or not isinstance(resolved_function, tp.Iterable):
|
191
|
-
resolved_function = (resolved_function,)
|
192
|
-
|
193
|
-
for col_index, data in enumerate(resolved_function):
|
194
|
-
ti = tree_list[col_index] if tree_list else TupleInstruction(self.CHAR, NestedElement(self.CHAR))
|
195
|
-
|
196
|
-
values: ClauseInfo | list[ClauseInfo] = self.__identify_value_type(data, ti)
|
120
|
+
resolved_function = (self.table,) if ClauseInfo.is_asterisk(resolved_function) else (resolved_function,)
|
197
121
|
|
198
|
-
|
199
|
-
|
122
|
+
for data in resolved_function:
|
123
|
+
if not isinstance(data, ClauseInfo):
|
124
|
+
values = self.__convert_into_ClauseInfo(data)
|
200
125
|
else:
|
201
|
-
|
126
|
+
values = [data]
|
127
|
+
self.__add_clause(values)
|
202
128
|
|
203
129
|
return None
|
204
130
|
|
205
|
-
def
|
131
|
+
def __convert_into_ClauseInfo[TProp](self, data: TProp) -> list[ClauseInfo[T]]:
|
206
132
|
"""
|
207
133
|
A method that behaves based on the variable's type
|
208
134
|
"""
|
209
135
|
|
210
|
-
def validation(data: TProp, type_: ValueType):
|
211
|
-
|
212
|
-
return
|
213
|
-
[
|
214
|
-
isinstance(data, type_),
|
215
|
-
is_table,
|
216
|
-
]
|
217
|
-
)
|
136
|
+
def validation(data: TProp, type_: ValueType) -> bool:
|
137
|
+
is_valid_type: bool = isinstance(data, type) and issubclass(data, type_)
|
138
|
+
return isinstance(data, type_) or is_valid_type
|
218
139
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
ICustomAlias: self._ICustomAlias_type,
|
140
|
+
VALUE_TYPE_MAPPED: dict[tp.Type[ValueType], ClauseInfoConverter[T, TProp]] = {
|
141
|
+
ForeignKey: ConvertFromForeignKey[T, Table],
|
142
|
+
Column: ConvertFromColumn[TProp],
|
143
|
+
IAggregate: ConvertFromIAggregate,
|
144
|
+
Table: ConvertFromTable[T],
|
225
145
|
}
|
146
|
+
classConverter = next((converter for obj, converter in VALUE_TYPE_MAPPED.items() if validation(data, obj)), ConvertFromAnyType)
|
226
147
|
|
227
|
-
|
228
|
-
|
229
|
-
if not function:
|
230
|
-
raise NotImplementedError(f"type of value '{data}' is not implemented.")
|
231
|
-
|
232
|
-
return function(data, tuple_instruction)
|
233
|
-
|
234
|
-
def _property_type(self, data: property, ti: TupleInstruction):
|
235
|
-
if ti.var == self.CHAR:
|
236
|
-
table_left = self.table
|
237
|
-
else:
|
238
|
-
table_left = self.alias_cache[ti.var]
|
239
|
-
|
240
|
-
if data in table_left.__properties_mapped__:
|
241
|
-
# if self.table != table_left:
|
242
|
-
# self._add_fk_relationship(self.table, table_left)
|
243
|
-
return ClauseInfo[T](table_left, data, self.alias_children_resolver)
|
244
|
-
|
245
|
-
for table in self.tables:
|
246
|
-
try:
|
247
|
-
return self._search_correct_table_for_prop(table, ti, data)
|
248
|
-
except ValueError:
|
249
|
-
continue
|
250
|
-
|
251
|
-
def _IAggregate_type(self, data: IAggregate, ti: TupleInstruction):
|
252
|
-
return ClauseInfo[T](self.table, data, self.alias_children_resolver)
|
253
|
-
|
254
|
-
def _table_type(self, data: tp.Type[Table], ti: TupleInstruction):
|
255
|
-
if data not in self._tables:
|
256
|
-
self.__add_necessary_fk(ti, data)
|
257
|
-
# all columns
|
258
|
-
clauses: list[ClauseInfo] = []
|
259
|
-
for prop in data.__properties_mapped__:
|
260
|
-
if isinstance(prop, property):
|
261
|
-
clauses.append(self.__identify_value_type(prop, ti))
|
262
|
-
return clauses
|
263
|
-
|
264
|
-
def _str_type(self, data: str, ti: TupleInstruction):
|
265
|
-
# COMMENT: use self.table instead self._tables because if we hit this conditional, means that
|
266
|
-
# COMMENT: alias_cache to replace '*' by all columns
|
267
|
-
if self._replace_asterisk_char and (replace_value := self.alias_cache.get(data, None)) is not None:
|
268
|
-
return self.__identify_value_type(replace_value(self.table), ti)
|
269
|
-
return ClauseInfo[T](self.table, data, alias_children_resolver=self.alias_children_resolver)
|
270
|
-
|
271
|
-
def _ICustomAlias_type(self, data: ICustomAlias, ti: TupleInstruction):
|
272
|
-
return ClauseInfo(data.table, data, data.alias_children_resolver)
|
273
|
-
|
274
|
-
def _search_correct_table_for_prop[TTable](self, table: tp.Type[Table], tuple_instruction: TupleInstruction, prop: property) -> ClauseInfo[TTable]:
|
275
|
-
temp_table: tp.Type[Table] = table
|
276
|
-
|
277
|
-
_, *table_list = tuple_instruction.nested_element.parents
|
278
|
-
counter: int = 0
|
279
|
-
while prop not in temp_table.__properties_mapped__:
|
280
|
-
new_table: TTable = getattr(temp_table(), table_list[counter])
|
281
|
-
|
282
|
-
if not isinstance(new_table, type) or not issubclass(new_table, Table):
|
283
|
-
raise ValueError(f"new_table var must be '{Table.__class__}' type and is '{type(new_table)}'")
|
284
|
-
self._add_fk_relationship(temp_table, new_table)
|
285
|
-
|
286
|
-
temp_table = new_table
|
287
|
-
counter += 1
|
288
|
-
|
289
|
-
if prop in new_table.__properties_mapped__:
|
290
|
-
return ClauseInfo[TTable](new_table, prop, self.alias_children_resolver)
|
148
|
+
return classConverter.convert(data, alias_table=self._alias_table, context=self._context)
|
291
149
|
|
292
|
-
|
150
|
+
def __add_clause[TTable: TableType](self, clauses: list[ClauseInfo[TTable]] | ClauseInfo[TTable]) -> None:
|
151
|
+
if not isinstance(clauses, tp.Iterable):
|
152
|
+
raise ValueError(f"Iterable expected. '{type(clauses)}' got instead.")
|
293
153
|
|
294
|
-
|
295
|
-
|
296
|
-
self._clauses_group_by_tables[clause.table].append(clause)
|
154
|
+
for clause in clauses:
|
155
|
+
self._all_clauses.append(clause)
|
297
156
|
return None
|
298
157
|
|
299
|
-
def __add_necessary_fk(self, tuple_instruction: TupleInstruction, tables: tp.Type[Table]) -> None:
|
300
|
-
old_table = self.table
|
301
|
-
|
302
|
-
table_inherit_list: list[Table] = tuple_instruction.nested_element.parents[1:]
|
303
|
-
counter: int = 0
|
304
|
-
while tables not in old_table.__dict__.values():
|
305
|
-
new_table: tp.Type[Table] = getattr(old_table(), table_inherit_list[counter])
|
306
|
-
|
307
|
-
if not issubclass(new_table, Table):
|
308
|
-
raise ValueError(f"new_table var must be '{Table.__class__}' type and is '{type(new_table)}'")
|
309
|
-
|
310
|
-
self._add_fk_relationship(old_table, new_table)
|
311
|
-
|
312
|
-
if tables in new_table.__dict__.values():
|
313
|
-
return self._add_fk_relationship(new_table, tables)
|
314
|
-
|
315
|
-
old_table = new_table
|
316
|
-
counter += 1
|
317
|
-
|
318
|
-
return self._add_fk_relationship(old_table, tables)
|
319
|
-
|
320
158
|
@property
|
321
159
|
def table(self) -> T:
|
322
160
|
return self.tables[0] if isinstance(self.tables, tp.Iterable) else self.tables
|
@@ -326,55 +164,19 @@ class DecompositionQueryBase[T: tp.Type[Table], *Ts](IDecompositionQuery[T, *Ts]
|
|
326
164
|
return self._tables
|
327
165
|
|
328
166
|
@property
|
329
|
-
def
|
330
|
-
return self.
|
167
|
+
def columns[*Ts](self) -> tp.Callable[[T], tuple[*Ts]]:
|
168
|
+
return self._columns
|
331
169
|
|
332
170
|
@property
|
333
171
|
def all_clauses(self) -> list[ClauseInfo[T]]:
|
334
172
|
return self._all_clauses
|
335
173
|
|
336
174
|
@property
|
337
|
-
def
|
338
|
-
return self.
|
339
|
-
|
340
|
-
@property
|
341
|
-
def has_foreign_keys(self) -> bool:
|
342
|
-
return len(self._joins) > 0
|
175
|
+
def context(self) -> tp.Optional[ClauseInfoContext]:
|
176
|
+
return self._context
|
343
177
|
|
344
|
-
@
|
345
|
-
def
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
@abc.abstractmethod
|
350
|
-
def query(self) -> str: ...
|
351
|
-
|
352
|
-
@property
|
353
|
-
def alias(self) -> str:
|
354
|
-
return self._alias
|
355
|
-
|
356
|
-
@property
|
357
|
-
def alias_name(self) -> str:
|
358
|
-
return self._alias_name
|
359
|
-
|
360
|
-
@alias_name.setter
|
361
|
-
def alias_name(self, value: tp.Optional[str]) -> None:
|
362
|
-
if value is None and self._alias:
|
363
|
-
self._alias = False
|
364
|
-
else:
|
365
|
-
self._alias = True
|
366
|
-
|
367
|
-
self._alias_name = value
|
368
|
-
|
369
|
-
def stringify_foreign_key(self, sep: str = "\n") -> str:
|
370
|
-
sorted_joins = JoinSelector.sort_join_selectors(self._joins)
|
371
|
-
return f"{sep}".join([join.query for join in sorted_joins])
|
372
|
-
|
373
|
-
def _add_fk_relationship[T1: tp.Type[Table], T2: tp.Type[Table]](self, t1: T1, t2: T2) -> None:
|
374
|
-
lambda_relationship = ForeignKey.MAPPED[t1.__table_name__].referenced_tables[t2.__table_name__].relationship
|
375
|
-
|
376
|
-
tables = list(self._tables)
|
377
|
-
if t2 not in tables:
|
378
|
-
tables.append(t2)
|
379
|
-
self._tables = tuple(tables)
|
380
|
-
return self._joins.add(JoinSelector[T1, T2](t1, t2, self._by, where=lambda_relationship))
|
178
|
+
@context.setter
|
179
|
+
def context(self, value: ClauseInfoContext) -> None:
|
180
|
+
self._context = value
|
181
|
+
self.__clauses_list_generetor()
|
182
|
+
return None
|
@@ -5,7 +5,7 @@ from typing import Any, Optional, Type, override, TYPE_CHECKING
|
|
5
5
|
from ormlambda.common.interfaces.INonQueryCommand import INonQueryCommand
|
6
6
|
|
7
7
|
if TYPE_CHECKING:
|
8
|
-
from ormlambda import IRepositoryBase
|
8
|
+
from ormlambda.repository import IRepositoryBase
|
9
9
|
from ormlambda import Table
|
10
10
|
|
11
11
|
|
@@ -2,7 +2,7 @@ from enum import Enum
|
|
2
2
|
|
3
3
|
|
4
4
|
class ConditionType(Enum):
|
5
|
-
EQUAL = "
|
5
|
+
EQUAL = "="
|
6
6
|
LESS_THAN = "<"
|
7
7
|
GREATER_THAN = ">"
|
8
8
|
LESS_THAN_OR_EQUAL = "<="
|
@@ -12,5 +12,6 @@ class ConditionType(Enum):
|
|
12
12
|
BETWEEN = "BETWEEN"
|
13
13
|
LIKE = "LIKE"
|
14
14
|
IN = "IN"
|
15
|
+
NOT_IN = "NOT IN"
|
15
16
|
IS = "IS"
|
16
17
|
IS_NOT = "IS NOT"
|
@@ -1,8 +1,21 @@
|
|
1
|
+
import inspect
|
2
|
+
import typing as tp
|
3
|
+
|
4
|
+
|
1
5
|
class UnmatchedLambdaParameterError(Exception):
|
2
|
-
def __init__(self, expected_params: int,
|
6
|
+
def __init__(self, expected_params: int, function: tp.Callable[..., tp.Any], *args: object) -> None:
|
3
7
|
super().__init__(*args)
|
4
8
|
self.expected_params = expected_params
|
5
|
-
self.found_param: tuple[str, ...] =
|
9
|
+
self.found_param: tuple[str, ...] = tuple(inspect.signature(function).parameters)
|
6
10
|
|
7
11
|
def __str__(self) -> str:
|
8
12
|
return f"Unmatched number of parameters in lambda function with the number of tables: Expected {self.expected_params} parameters but found {str(self.found_param)}."
|
13
|
+
|
14
|
+
|
15
|
+
class NotKeysInIAggregateError(Exception):
|
16
|
+
def __init__(self, match_regex: list[str], *args: object) -> None:
|
17
|
+
super().__init__(*args)
|
18
|
+
self._match_regex: list[str] = match_regex
|
19
|
+
|
20
|
+
def __str__(self) -> str:
|
21
|
+
return f"We cannot use placeholders in IAggregate class. You used {self._match_regex}"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
import re
|
3
|
+
from typing import Any, TYPE_CHECKING
|
4
|
+
|
5
|
+
from ormlambda.common.errors import UnmatchedLambdaParameterError
|
6
|
+
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
from ormlambda.sql import Table
|
9
|
+
|
10
|
+
|
11
|
+
class GlobalChecker:
|
12
|
+
@staticmethod
|
13
|
+
def is_lambda_function(obj: Any) -> bool:
|
14
|
+
return callable(obj) and not isinstance(obj, type)
|
15
|
+
|
16
|
+
@classmethod
|
17
|
+
def resolved_callback_object(cls, obj: Any, tables: tuple[Table, ...]):
|
18
|
+
if not cls.is_lambda_function(obj):
|
19
|
+
return obj
|
20
|
+
|
21
|
+
try:
|
22
|
+
return obj(*tables)
|
23
|
+
except TypeError as err:
|
24
|
+
cond1 = r"takes \d+ positional argument but \d+ were given"
|
25
|
+
cond2 = r"missing \d+ required positional arguments:"
|
26
|
+
if re.search(r"("+f"{cond1}|{cond2}"+r")", err.args[0]):
|
27
|
+
raise UnmatchedLambdaParameterError(len(tables), obj)
|
28
|
+
raise err
|
@@ -4,55 +4,30 @@ import typing as tp
|
|
4
4
|
|
5
5
|
|
6
6
|
if tp.TYPE_CHECKING:
|
7
|
-
from ormlambda import Table
|
8
|
-
|
9
7
|
# TODOH: Changed to avoid mysql dependency
|
10
|
-
from ormlambda.
|
11
|
-
|
12
|
-
from .IQueryCommand import IQuery
|
8
|
+
from ormlambda.sql.clause_info import ClauseInfo, TableType
|
9
|
+
from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext
|
13
10
|
|
14
11
|
|
15
|
-
class IDecompositionQuery_one_arg[T:
|
12
|
+
class IDecompositionQuery_one_arg[T: TableType]:
|
16
13
|
@property
|
17
14
|
@abc.abstractmethod
|
18
15
|
def table(self) -> T: ...
|
19
16
|
|
20
17
|
@property
|
21
18
|
@abc.abstractmethod
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
class IDecompositionQuery[T: tp.Type[Table], *Ts](IDecompositionQuery_one_arg[T], IQuery):
|
26
|
-
@property
|
27
|
-
@abc.abstractmethod
|
28
|
-
def tables(self) -> tuple[*Ts]: ...
|
19
|
+
def context(self) -> tp.Optional[ClauseInfoContext]: ...
|
29
20
|
|
30
|
-
@
|
21
|
+
@context.setter
|
31
22
|
@abc.abstractmethod
|
32
|
-
def
|
23
|
+
def context(self) -> tp.Optional[ClauseInfoContext]: ...
|
33
24
|
|
34
|
-
@property
|
35
|
-
@abc.abstractmethod
|
36
|
-
def all_clauses(self) -> list[ClauseInfo]: ...
|
37
25
|
|
26
|
+
class IDecompositionQuery[T: TableType, *Ts](IDecompositionQuery_one_arg[T]):
|
38
27
|
@property
|
39
28
|
@abc.abstractmethod
|
40
|
-
def
|
41
|
-
|
42
|
-
@property
|
43
|
-
@abc.abstractmethod
|
44
|
-
def fk_relationship(self) -> set[tuple[tp.Type[Table], tp.Type[Table]]]: ...
|
45
|
-
|
46
|
-
@property
|
47
|
-
@abc.abstractmethod
|
48
|
-
def alias_name(self) -> tp.Optional[str]: ...
|
29
|
+
def tables(self) -> tuple[*Ts]: ...
|
49
30
|
|
50
31
|
@property
|
51
32
|
@abc.abstractmethod
|
52
|
-
def
|
53
|
-
|
54
|
-
@abc.abstractmethod
|
55
|
-
def stringify_foreign_key(self, sep: str = "\n"): ...
|
56
|
-
|
57
|
-
@abc.abstractmethod
|
58
|
-
def alias_children_resolver(self) -> tp.Callable[[tp.Type[Table], str], str]: ...
|
33
|
+
def all_clauses(self) -> list[ClauseInfo]: ...
|