ormlambda 3.12.2__py3-none-any.whl → 3.34.1__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 +2 -0
- ormlambda/caster/__init__.py +1 -1
- ormlambda/caster/caster.py +29 -12
- ormlambda/common/abstract_classes/clause_info_converter.py +4 -12
- ormlambda/common/abstract_classes/decomposition_query.py +17 -2
- ormlambda/common/abstract_classes/non_query_base.py +9 -7
- ormlambda/common/abstract_classes/query_base.py +3 -1
- ormlambda/common/errors/__init__.py +29 -0
- ormlambda/common/interfaces/IQueryCommand.py +6 -2
- ormlambda/databases/__init__.py +0 -1
- ormlambda/databases/my_sql/__init__.py +0 -1
- ormlambda/databases/my_sql/caster/caster.py +23 -19
- ormlambda/databases/my_sql/caster/types/__init__.py +3 -0
- ormlambda/databases/my_sql/caster/types/boolean.py +35 -0
- ormlambda/databases/my_sql/caster/types/bytes.py +7 -7
- ormlambda/databases/my_sql/caster/types/date.py +34 -0
- ormlambda/databases/my_sql/caster/types/datetime.py +7 -7
- ormlambda/databases/my_sql/caster/types/decimal.py +32 -0
- ormlambda/databases/my_sql/caster/types/float.py +7 -7
- ormlambda/databases/my_sql/caster/types/int.py +7 -7
- ormlambda/databases/my_sql/caster/types/iterable.py +7 -7
- ormlambda/databases/my_sql/caster/types/none.py +8 -7
- ormlambda/databases/my_sql/caster/types/point.py +4 -4
- ormlambda/databases/my_sql/caster/types/string.py +7 -7
- ormlambda/databases/my_sql/clauses/ST_AsText.py +8 -7
- ormlambda/databases/my_sql/clauses/ST_Contains.py +10 -5
- ormlambda/databases/my_sql/clauses/__init__.py +4 -10
- ormlambda/databases/my_sql/clauses/count.py +5 -15
- ormlambda/databases/my_sql/clauses/delete.py +3 -50
- ormlambda/databases/my_sql/clauses/group_by.py +3 -16
- ormlambda/databases/my_sql/clauses/having.py +2 -6
- ormlambda/databases/my_sql/clauses/insert.py +4 -92
- ormlambda/databases/my_sql/clauses/joins.py +5 -140
- ormlambda/databases/my_sql/clauses/limit.py +4 -15
- ormlambda/databases/my_sql/clauses/offset.py +4 -15
- ormlambda/databases/my_sql/clauses/order.py +4 -61
- ormlambda/databases/my_sql/clauses/update.py +4 -67
- ormlambda/databases/my_sql/clauses/upsert.py +3 -66
- ormlambda/databases/my_sql/clauses/where.py +4 -42
- ormlambda/databases/my_sql/repository.py +217 -0
- 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 +8 -0
- ormlambda/dialects/mysql/base.py +387 -0
- ormlambda/dialects/mysql/mysqlconnector.py +46 -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 +58 -0
- ormlambda/engine/create.py +9 -23
- ormlambda/engine/url.py +31 -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 +121 -7
- ormlambda/repository/response.py +134 -0
- ormlambda/sql/clause_info/aggregate_function_base.py +19 -9
- ormlambda/sql/clause_info/clause_info.py +34 -17
- ormlambda/sql/clauses/__init__.py +14 -0
- ormlambda/{databases/my_sql → sql}/clauses/alias.py +23 -6
- ormlambda/sql/clauses/count.py +57 -0
- ormlambda/sql/clauses/delete.py +71 -0
- ormlambda/sql/clauses/group_by.py +30 -0
- ormlambda/sql/clauses/having.py +21 -0
- ormlambda/sql/clauses/insert.py +104 -0
- ormlambda/sql/clauses/interfaces/__init__.py +5 -0
- ormlambda/{components → sql/clauses}/join/join_context.py +15 -7
- ormlambda/sql/clauses/joins.py +159 -0
- ormlambda/sql/clauses/limit.py +15 -0
- ormlambda/sql/clauses/offset.py +15 -0
- ormlambda/sql/clauses/order.py +55 -0
- ormlambda/{databases/my_sql → sql}/clauses/select.py +12 -13
- ormlambda/sql/clauses/update.py +84 -0
- ormlambda/sql/clauses/upsert.py +77 -0
- ormlambda/sql/clauses/where.py +65 -0
- ormlambda/sql/column/__init__.py +1 -0
- ormlambda/sql/{column.py → column/column.py} +82 -22
- ormlambda/sql/comparer.py +51 -37
- ormlambda/sql/compiler.py +427 -0
- ormlambda/sql/ddl.py +68 -0
- ormlambda/sql/elements.py +36 -0
- ormlambda/sql/foreign_key.py +43 -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 +179 -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 +28 -38
- ormlambda/statements/interfaces/IStatements.py +5 -4
- ormlambda/{databases/my_sql → statements}/query_builder.py +35 -30
- ormlambda/{databases/my_sql → statements}/statements.py +50 -60
- ormlambda/statements/types.py +2 -2
- ormlambda/types/__init__.py +24 -0
- ormlambda/types/metadata.py +42 -0
- ormlambda/util/__init__.py +88 -0
- ormlambda/util/load_module.py +21 -0
- ormlambda/util/plugin_loader.py +32 -0
- ormlambda/util/typing.py +6 -0
- ormlambda-3.34.1.dist-info/AUTHORS +32 -0
- {ormlambda-3.12.2.dist-info → ormlambda-3.34.1.dist-info}/METADATA +2 -3
- ormlambda-3.34.1.dist-info/RECORD +157 -0
- {ormlambda-3.12.2.dist-info → ormlambda-3.34.1.dist-info}/WHEEL +1 -1
- 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/my_sql/clauses/create_database.py +0 -35
- ormlambda/databases/my_sql/clauses/drop_database.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.12.2.dist-info/RECORD +0 -125
- /ormlambda/databases/my_sql/{types.py → 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/{components → sql/clauses}/join/__init__.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/{utils → util}/module_tree/dynamic_module.py +0 -0
- {ormlambda-3.12.2.dist-info → ormlambda-3.34.1.dist-info}/LICENSE +0 -0
@@ -16,6 +16,9 @@ from ormlambda.sql.table import TableMeta
|
|
16
16
|
from .clause_info import ClauseInfo
|
17
17
|
from .clause_info_context import ClauseContextType
|
18
18
|
|
19
|
+
if tp.TYPE_CHECKING:
|
20
|
+
from ormlambda.dialects import Dialect
|
21
|
+
|
19
22
|
|
20
23
|
class AggregateFunctionBase[T: Table](ClauseInfo[T], IAggregate):
|
21
24
|
def __init__[TProp: Column](
|
@@ -27,6 +30,10 @@ class AggregateFunctionBase[T: Table](ClauseInfo[T], IAggregate):
|
|
27
30
|
context: ClauseContextType = None,
|
28
31
|
keep_asterisk: bool = False,
|
29
32
|
preserve_context: bool = False,
|
33
|
+
dtype: TProp = None,
|
34
|
+
*,
|
35
|
+
dialect: Dialect,
|
36
|
+
**kw,
|
30
37
|
):
|
31
38
|
self._alias_aggregate = alias_clause
|
32
39
|
super().__init__(
|
@@ -36,6 +43,9 @@ class AggregateFunctionBase[T: Table](ClauseInfo[T], IAggregate):
|
|
36
43
|
context=context,
|
37
44
|
keep_asterisk=keep_asterisk,
|
38
45
|
preserve_context=preserve_context,
|
46
|
+
dtype=dtype,
|
47
|
+
dialect=dialect,
|
48
|
+
**kw,
|
39
49
|
)
|
40
50
|
|
41
51
|
@staticmethod
|
@@ -43,16 +53,16 @@ class AggregateFunctionBase[T: Table](ClauseInfo[T], IAggregate):
|
|
43
53
|
def FUNCTION_NAME() -> str: ...
|
44
54
|
|
45
55
|
@classmethod
|
46
|
-
def _convert_into_clauseInfo[TypeColumns, TProp](cls, columns: ClauseInfo | ColumnType[TProp], context: ClauseContextType) -> list[ClauseInfo]:
|
56
|
+
def _convert_into_clauseInfo[TypeColumns, TProp](cls, columns: ClauseInfo | ColumnType[TProp], context: ClauseContextType, dialect: Dialect) -> list[ClauseInfo]:
|
47
57
|
type DEFAULT = tp.Literal["default"]
|
48
58
|
type ClusterType = ColumnType | ForeignKey | DEFAULT
|
49
59
|
|
50
60
|
dicc_type: dict[ClusterType, tp.Callable[[ClusterType], ClauseInfo]] = {
|
51
|
-
Column: lambda column: ClauseInfo(column.table, column, context=context),
|
61
|
+
Column: lambda column: ClauseInfo(column.table, column, context=context, dialect=dialect),
|
52
62
|
ClauseInfo: lambda column: column,
|
53
|
-
ForeignKey: lambda tbl: ClauseInfo(tbl.tright, tbl.tright, context=context),
|
54
|
-
TableMeta: lambda tbl: ClauseInfo(tbl, tbl, context=context),
|
55
|
-
"default": lambda column: ClauseInfo(table=None, column=column, context=context),
|
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),
|
56
66
|
}
|
57
67
|
all_clauses: list[ClauseInfo] = []
|
58
68
|
if isinstance(columns, str) or not isinstance(columns, tp.Iterable):
|
@@ -63,8 +73,7 @@ class AggregateFunctionBase[T: Table](ClauseInfo[T], IAggregate):
|
|
63
73
|
return all_clauses
|
64
74
|
|
65
75
|
@tp.override
|
66
|
-
|
67
|
-
def query(self) -> str:
|
76
|
+
def query(self, dialect: Dialect, **kwargs) -> str:
|
68
77
|
wrapped_ci = self.wrapped_clause_info(self)
|
69
78
|
if not self._alias_aggregate:
|
70
79
|
return wrapped_ci
|
@@ -76,11 +85,12 @@ class AggregateFunctionBase[T: Table](ClauseInfo[T], IAggregate):
|
|
76
85
|
context=self._context,
|
77
86
|
keep_asterisk=self._keep_asterisk,
|
78
87
|
preserve_context=self._preserve_context,
|
79
|
-
|
88
|
+
dialect=self._dialect,
|
89
|
+
).query(dialect, **kwargs)
|
80
90
|
|
81
91
|
def wrapped_clause_info(self, ci: ClauseInfo[T]) -> str:
|
82
92
|
# avoid use placeholder when using IAggregate because no make sense.
|
83
93
|
if self._alias_aggregate and (found := self._keyRegex.findall(self._alias_aggregate)):
|
84
94
|
raise NotKeysInIAggregateError(found)
|
85
95
|
|
86
|
-
return f"{self.FUNCTION_NAME()}({ci._create_query()})"
|
96
|
+
return f"{self.FUNCTION_NAME()}({ci._create_query(self._dialect)})"
|
@@ -12,11 +12,13 @@ from ormlambda.sql.types import (
|
|
12
12
|
)
|
13
13
|
from .interface import IClauseInfo
|
14
14
|
from ormlambda.sql import ForeignKey
|
15
|
-
from ormlambda.caster import Caster
|
16
15
|
|
17
16
|
|
18
17
|
from .clause_info_context import ClauseInfoContext, ClauseContextType
|
19
18
|
|
19
|
+
if tp.TYPE_CHECKING:
|
20
|
+
from ormlambda.dialects import Dialect
|
21
|
+
|
20
22
|
|
21
23
|
class ReplacePlaceholderError(ValueError):
|
22
24
|
def __init__(self, placeholder: str, attribute: str, *args):
|
@@ -45,6 +47,11 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
45
47
|
def __init__(self, table: TableType[T], keep_asterisk: tp.Optional[bool] = ...): ...
|
46
48
|
@tp.overload
|
47
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): ...
|
48
55
|
|
49
56
|
def __init__[TProp](
|
50
57
|
self,
|
@@ -55,6 +62,10 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
55
62
|
context: ClauseContextType = None,
|
56
63
|
keep_asterisk: bool = False,
|
57
64
|
preserve_context: bool = False,
|
65
|
+
dtype: tp.Optional[TProp] = None,
|
66
|
+
*,
|
67
|
+
dialect: Dialect,
|
68
|
+
**kw,
|
58
69
|
):
|
59
70
|
if not self.is_table(table):
|
60
71
|
column = table if not column else column
|
@@ -67,6 +78,9 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
67
78
|
self._context: ClauseContextType = context if context else ClauseInfoContext()
|
68
79
|
self._keep_asterisk: bool = keep_asterisk
|
69
80
|
self._preserve_context: bool = preserve_context
|
81
|
+
self._dtype = dtype
|
82
|
+
|
83
|
+
self._dialect: Dialect = dialect
|
70
84
|
|
71
85
|
self._placeholderValues: dict[str, tp.Callable[[TProp], str]] = {
|
72
86
|
"column": self.replace_column_placeholder,
|
@@ -76,8 +90,10 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
76
90
|
if not self._preserve_context and (self._context and any([alias_table, alias_clause])):
|
77
91
|
self._context.add_clause_to_context(self)
|
78
92
|
|
93
|
+
super().__init__(**kw)
|
94
|
+
|
79
95
|
def __repr__(self) -> str:
|
80
|
-
return f"{type(self).__name__}: query -> {self.query}"
|
96
|
+
return f"{type(self).__name__}: query -> {self.query(self._dialect)}"
|
81
97
|
|
82
98
|
def replace_column_placeholder[TProp](self, column: ColumnType[TProp]) -> str:
|
83
99
|
if not column:
|
@@ -140,6 +156,9 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
140
156
|
|
141
157
|
@property
|
142
158
|
def dtype[TProp](self) -> tp.Optional[tp.Type[TProp]]:
|
159
|
+
if self._dtype is not None:
|
160
|
+
return self._dtype
|
161
|
+
|
143
162
|
if isinstance(self._column, Column):
|
144
163
|
return self._column.dtype
|
145
164
|
|
@@ -147,11 +166,10 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
147
166
|
return self._column
|
148
167
|
return type(self._column)
|
149
168
|
|
150
|
-
|
151
|
-
|
152
|
-
return self._create_query()
|
169
|
+
def query(self, dialect: Dialect, **kwargs) -> str:
|
170
|
+
return self._create_query(dialect, **kwargs)
|
153
171
|
|
154
|
-
def _create_query(self) -> str:
|
172
|
+
def _create_query(self, dialect: Dialect, **kwargs) -> str:
|
155
173
|
# when passing some value that is not a column name
|
156
174
|
if not self.table and not self._alias_clause:
|
157
175
|
return self.column
|
@@ -173,10 +191,9 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
173
191
|
return self._join_table_and_column(self._column)
|
174
192
|
|
175
193
|
def _join_table_and_column[TProp](self, column: ColumnType[TProp]) -> str:
|
176
|
-
# FIXME [
|
177
|
-
from ormlambda.databases.my_sql.repository import MySQLRepository
|
194
|
+
# FIXME [x]: Study how to deacoplate from mysql database
|
178
195
|
|
179
|
-
caster =
|
196
|
+
caster = self._dialect.caster()
|
180
197
|
|
181
198
|
if self.alias_table:
|
182
199
|
table = self._wrapped_with_quotes(self.alias_table)
|
@@ -213,22 +230,20 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
213
230
|
alias_clause=self._alias_clause,
|
214
231
|
context=self._context,
|
215
232
|
keep_asterisk=self._keep_asterisk,
|
233
|
+
dialect=self._dialect,
|
216
234
|
)
|
217
235
|
|
218
236
|
if self._alias_table and self._alias_clause: # We'll add an "*" when we are certain that we have included 'alias_clause' attr
|
219
237
|
return self._join_table_and_column(ASTERISK)
|
220
238
|
|
221
|
-
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()]
|
222
240
|
|
223
241
|
return ", ".join(columns)
|
224
242
|
|
225
|
-
# FIXME [
|
243
|
+
# FIXME [x]: Study how to deacoplate from mysql database
|
226
244
|
def _column_resolver[TProp](self, column: ColumnType[TProp]) -> str:
|
227
|
-
from ormlambda.databases.my_sql.repository import MySQLRepository
|
228
|
-
|
229
|
-
caster = Caster(MySQLRepository)
|
230
245
|
if isinstance(column, ClauseInfo):
|
231
|
-
return column.query
|
246
|
+
return column.query(self._dialect)
|
232
247
|
|
233
248
|
if isinstance(column, tp.Iterable) and isinstance(column[0], ClauseInfo):
|
234
249
|
return self.join_clauses(column)
|
@@ -249,6 +264,7 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
249
264
|
if self.is_foreign_key(self._column):
|
250
265
|
return self._column.tright.__table_name__
|
251
266
|
|
267
|
+
caster = self._dialect.caster()
|
252
268
|
casted_value = caster.for_value(column, self.dtype)
|
253
269
|
if not self._table:
|
254
270
|
# if we haven't some table atrribute, we assume that the user want to retrieve the string_data from caster.
|
@@ -292,12 +308,13 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
292
308
|
return self._context.get_table_alias(self.table)
|
293
309
|
|
294
310
|
@staticmethod
|
295
|
-
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:
|
296
312
|
queries: list[str] = []
|
297
313
|
for c in clauses:
|
298
314
|
if context:
|
299
315
|
c.context = context
|
300
|
-
|
316
|
+
c._dialect = dialect
|
317
|
+
queries.append(c.query(dialect))
|
301
318
|
|
302
319
|
return f"{chr} ".join(queries)
|
303
320
|
|
@@ -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"]
|
@@ -0,0 +1,57 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from ormlambda.sql.clause_info import AggregateFunctionBase
|
3
|
+
from ormlambda.sql.clause_info.clause_info_context import ClauseContextType
|
4
|
+
|
5
|
+
from ormlambda.sql.types import AliasType, ColumnType
|
6
|
+
|
7
|
+
from ormlambda import Table
|
8
|
+
|
9
|
+
import typing as tp
|
10
|
+
|
11
|
+
from ormlambda.sql.types import ASTERISK
|
12
|
+
from ormlambda.sql.elements import ClauseElement
|
13
|
+
|
14
|
+
|
15
|
+
if tp.TYPE_CHECKING:
|
16
|
+
from ormlambda import Table
|
17
|
+
from ormlambda.sql.types import ColumnType, AliasType, TableType
|
18
|
+
from ormlambda.dialects import Dialect
|
19
|
+
|
20
|
+
|
21
|
+
class Count[T: Table](AggregateFunctionBase[T], ClauseElement):
|
22
|
+
__visit_name__ = "count"
|
23
|
+
|
24
|
+
@staticmethod
|
25
|
+
def FUNCTION_NAME() -> str:
|
26
|
+
return "COUNT"
|
27
|
+
|
28
|
+
def __init__[TProp: Table](
|
29
|
+
self,
|
30
|
+
element: ColumnType[T] | TableType[TProp],
|
31
|
+
alias_table: AliasType[ColumnType[TProp]] = None,
|
32
|
+
alias_clause: AliasType[ColumnType[TProp]] = "count",
|
33
|
+
context: ClauseContextType = None,
|
34
|
+
keep_asterisk: bool = True,
|
35
|
+
preserve_context: bool = True,
|
36
|
+
*,
|
37
|
+
dialect: Dialect,
|
38
|
+
**kw,
|
39
|
+
) -> None:
|
40
|
+
table = self.extract_table(element)
|
41
|
+
column = element if self.is_column(element) else ASTERISK
|
42
|
+
|
43
|
+
super().__init__(
|
44
|
+
table=table if (alias_table or (context and table in context._table_context)) else None,
|
45
|
+
column=column,
|
46
|
+
alias_table=alias_table,
|
47
|
+
alias_clause=alias_clause,
|
48
|
+
context=context,
|
49
|
+
keep_asterisk=keep_asterisk,
|
50
|
+
preserve_context=preserve_context,
|
51
|
+
dtype=int,
|
52
|
+
dialect=dialect,
|
53
|
+
**kw,
|
54
|
+
)
|
55
|
+
|
56
|
+
|
57
|
+
__all__ = ["Count"]
|
@@ -0,0 +1,71 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Any, Optional, override, Iterable, TYPE_CHECKING
|
3
|
+
|
4
|
+
if TYPE_CHECKING:
|
5
|
+
from ormlambda import Column
|
6
|
+
from ormlambda.engine import Engine
|
7
|
+
|
8
|
+
from ormlambda import Table
|
9
|
+
from ormlambda import BaseRepository
|
10
|
+
from ormlambda.sql.clauses.interfaces import IDelete
|
11
|
+
from ormlambda.common.abstract_classes import NonQueryBase
|
12
|
+
from ormlambda.sql.elements import ClauseElement
|
13
|
+
|
14
|
+
|
15
|
+
class Delete[T: Table, TRepo](NonQueryBase[T, TRepo], IDelete[T], ClauseElement):
|
16
|
+
__visit_name__ = "delete"
|
17
|
+
|
18
|
+
def __init__(self, model: T, repository: BaseRepository[TRepo], engine: Engine) -> None:
|
19
|
+
super().__init__(model, repository, engine=engine)
|
20
|
+
|
21
|
+
@property
|
22
|
+
def CLAUSE(self) -> str:
|
23
|
+
return "DELETE"
|
24
|
+
|
25
|
+
@override
|
26
|
+
def delete(self, instances: T | list[T]) -> None:
|
27
|
+
col: str = ""
|
28
|
+
if isinstance(instances, Table):
|
29
|
+
pk: Optional[Column] = instances.get_pk()
|
30
|
+
if pk is None:
|
31
|
+
raise Exception(f"You cannot use 'DELETE' query without set primary key in '{instances.__table_name__}'")
|
32
|
+
col = pk.column_name
|
33
|
+
|
34
|
+
pk_value = instances[pk]
|
35
|
+
|
36
|
+
if not pk_value:
|
37
|
+
raise ValueError(f"primary key value '{pk_value}' must not be empty.")
|
38
|
+
|
39
|
+
value = str(pk_value)
|
40
|
+
|
41
|
+
elif isinstance(instances, Iterable):
|
42
|
+
value: list[Any] = []
|
43
|
+
for ins in instances:
|
44
|
+
pk = type(ins).get_pk()
|
45
|
+
value.append(ins[pk])
|
46
|
+
col = pk.column_name
|
47
|
+
|
48
|
+
query: str = f"{self.CLAUSE} FROM {self._model.__table_name__} WHERE {col}"
|
49
|
+
if isinstance(value, str):
|
50
|
+
query += "= %s"
|
51
|
+
self._query = query
|
52
|
+
self._values = [value]
|
53
|
+
return None
|
54
|
+
|
55
|
+
elif isinstance(value, Iterable):
|
56
|
+
params = ", ".join(["%s"] * len(value))
|
57
|
+
query += f" IN ({params})"
|
58
|
+
self._query = query
|
59
|
+
self._values = value
|
60
|
+
return None
|
61
|
+
else:
|
62
|
+
raise Exception(f"'{type(value)}' no esperado")
|
63
|
+
|
64
|
+
@override
|
65
|
+
def execute(self) -> None:
|
66
|
+
if not self._query:
|
67
|
+
raise ValueError
|
68
|
+
return self._engine.repository.execute_with_values(self._query, self._values)
|
69
|
+
|
70
|
+
|
71
|
+
__all__ = ["Delete"]
|
@@ -0,0 +1,30 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import TYPE_CHECKING
|
3
|
+
|
4
|
+
if TYPE_CHECKING:
|
5
|
+
from ormlambda.dialects import Dialect
|
6
|
+
from ormlambda.sql.clause_info import AggregateFunctionBase, ClauseInfoContext
|
7
|
+
from ormlambda.sql.types import ColumnType
|
8
|
+
from ormlambda.sql.elements import ClauseElement
|
9
|
+
|
10
|
+
|
11
|
+
class GroupBy(AggregateFunctionBase, ClauseElement):
|
12
|
+
__visit_name__ = "group_by"
|
13
|
+
|
14
|
+
@classmethod
|
15
|
+
def FUNCTION_NAME(self) -> str:
|
16
|
+
return "GROUP BY"
|
17
|
+
|
18
|
+
def __init__(self, column: ColumnType, context: ClauseInfoContext, dialect: Dialect, **kwargs):
|
19
|
+
super().__init__(
|
20
|
+
table=column.table,
|
21
|
+
column=column,
|
22
|
+
alias_table=None,
|
23
|
+
alias_clause=None,
|
24
|
+
context=context,
|
25
|
+
dialect=dialect,
|
26
|
+
**kwargs,
|
27
|
+
)
|
28
|
+
|
29
|
+
|
30
|
+
__all__ = ["GroupBy"]
|
@@ -0,0 +1,21 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from .where import Where
|
4
|
+
|
5
|
+
|
6
|
+
class Having(Where):
|
7
|
+
"""
|
8
|
+
The purpose of this class is to create 'WHERE' condition queries properly.
|
9
|
+
"""
|
10
|
+
|
11
|
+
__visit_name__ = "having"
|
12
|
+
|
13
|
+
def __init__(self, *comparer, restrictive=True, context=None, **kw):
|
14
|
+
super().__init__(*comparer, restrictive=restrictive, context=context, **kw)
|
15
|
+
|
16
|
+
@staticmethod
|
17
|
+
def FUNCTION_NAME() -> str:
|
18
|
+
return "HAVING"
|
19
|
+
|
20
|
+
|
21
|
+
__all__ = ["Having"]
|
@@ -0,0 +1,104 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import override, Iterable, TYPE_CHECKING
|
4
|
+
|
5
|
+
from ormlambda import Table
|
6
|
+
from ormlambda import Column
|
7
|
+
|
8
|
+
from ormlambda.sql.clauses.interfaces import IInsert
|
9
|
+
from ormlambda.common.abstract_classes import NonQueryBase
|
10
|
+
from ormlambda.sql.elements import ClauseElement
|
11
|
+
|
12
|
+
|
13
|
+
if TYPE_CHECKING:
|
14
|
+
from ormlambda.dialects import Dialect
|
15
|
+
|
16
|
+
|
17
|
+
class Insert[T: Table, TRepo](NonQueryBase[T, TRepo], IInsert[T], ClauseElement):
|
18
|
+
__visit_name__ = "insert"
|
19
|
+
|
20
|
+
def __init__(self, model: T, repository: TRepo, dialect: Dialect) -> None:
|
21
|
+
super().__init__(model, repository, dialect=dialect)
|
22
|
+
|
23
|
+
@override
|
24
|
+
@property
|
25
|
+
def CLAUSE(self) -> str:
|
26
|
+
return "INSERT INTO"
|
27
|
+
|
28
|
+
@override
|
29
|
+
def execute(self) -> None:
|
30
|
+
if not self._query:
|
31
|
+
raise ValueError
|
32
|
+
return self._repository.executemany_with_values(self.query(self._dialect), self._values)
|
33
|
+
|
34
|
+
@override
|
35
|
+
def insert[TProp](self, instances: T | list[T]) -> None:
|
36
|
+
if not isinstance(instances, Iterable):
|
37
|
+
instances = (instances,)
|
38
|
+
valid_cols: list[list[Column[TProp]]] = []
|
39
|
+
self.__fill_dict_list(valid_cols, instances)
|
40
|
+
|
41
|
+
col_names: list[str] = []
|
42
|
+
wildcards: list[str] = []
|
43
|
+
col_values: list[list[str]] = []
|
44
|
+
for i, cols in enumerate(valid_cols):
|
45
|
+
col_values.append([])
|
46
|
+
CASTER = self._dialect.caster()
|
47
|
+
for col in cols:
|
48
|
+
clean_data = CASTER.for_column(col, instances[i]) # .resolve(instances[i][col])
|
49
|
+
if i == 0:
|
50
|
+
col_names.append(col.column_name)
|
51
|
+
wildcards.append(clean_data.wildcard_to_insert())
|
52
|
+
# COMMENT: avoid MySQLWriteCastBase.resolve when using PLACEHOLDERs
|
53
|
+
col_values[-1].append(clean_data.to_database)
|
54
|
+
|
55
|
+
join_cols = ", ".join(col_names)
|
56
|
+
unknown_rows = f'({", ".join(wildcards)})' # The number of "%s" must match the dict 'dicc_0' length
|
57
|
+
|
58
|
+
self._values = [tuple(x) for x in col_values]
|
59
|
+
self._query = f"{self.CLAUSE} {self._model.__table_name__} {f'({join_cols})'} VALUES {unknown_rows}"
|
60
|
+
return None
|
61
|
+
|
62
|
+
@staticmethod
|
63
|
+
def __is_valid[TProp](column: Column[TProp], value: TProp) -> bool:
|
64
|
+
"""
|
65
|
+
We want to delete the column from table when it's specified with an 'AUTO_INCREMENT' or 'AUTO GENERATED ALWAYS AS (__) STORED' statement.
|
66
|
+
|
67
|
+
if the column is auto-generated, it means the database creates the value for that column, so we must deleted it.
|
68
|
+
if the column is primary key and auto-increment, we should be able to create an object with specific pk value.
|
69
|
+
|
70
|
+
RETURN
|
71
|
+
-----
|
72
|
+
|
73
|
+
- True -> Do not delete the column from dict query
|
74
|
+
- False -> Delete the column from dict query
|
75
|
+
"""
|
76
|
+
|
77
|
+
is_pk_none_and_auto_increment: bool = all([value is None, column.is_primary_key, column.is_auto_increment])
|
78
|
+
|
79
|
+
if is_pk_none_and_auto_increment or column.is_auto_generated:
|
80
|
+
return False
|
81
|
+
return True
|
82
|
+
|
83
|
+
def __fill_dict_list[TProp](self, list_dict: list[str, TProp], values: list[T]) -> list[Column]:
|
84
|
+
if isinstance(values, Iterable):
|
85
|
+
for x in values:
|
86
|
+
self.__fill_dict_list(list_dict, x)
|
87
|
+
|
88
|
+
elif issubclass(values.__class__, Table):
|
89
|
+
new_list = []
|
90
|
+
for prop in type(values).__dict__.values():
|
91
|
+
if not isinstance(prop, Column):
|
92
|
+
continue
|
93
|
+
|
94
|
+
value = getattr(values, prop.column_name)
|
95
|
+
if self.__is_valid(prop, value):
|
96
|
+
new_list.append(prop)
|
97
|
+
list_dict.append(new_list)
|
98
|
+
|
99
|
+
else:
|
100
|
+
raise Exception(f"Tipo de dato'{type(values)}' no esperado")
|
101
|
+
return None
|
102
|
+
|
103
|
+
|
104
|
+
__all__ = ["Insert"]
|
@@ -11,24 +11,32 @@ if TYPE_CHECKING:
|
|
11
11
|
from ormlambda import Table
|
12
12
|
from ormlambda.sql.clause_info.clause_info_context import ClauseContextType
|
13
13
|
from ormlambda.common.enums.join_type import JoinType
|
14
|
+
from ormlambda.dialects import Dialect
|
14
15
|
|
15
16
|
|
16
|
-
type TupleJoinType[LTable: Table, LProp, RTable: Table, RProp] = tuple[Comparer
|
17
|
+
type TupleJoinType[LTable: Table, LProp, RTable: Table, RProp] = tuple[Comparer]
|
17
18
|
|
18
19
|
|
19
20
|
class JoinContext[TParent: Table, TRepo]:
|
20
|
-
def __init__(
|
21
|
+
def __init__(
|
22
|
+
self,
|
23
|
+
statements: IStatements_two_generic[TParent, TRepo],
|
24
|
+
joins: tuple,
|
25
|
+
context: ClauseContextType,
|
26
|
+
dialect: Dialect,
|
27
|
+
) -> None:
|
21
28
|
self._statements = statements
|
22
29
|
self._parent: TParent = statements.model
|
23
30
|
self._joins: Iterable[tuple[Comparer, JoinType]] = joins
|
24
31
|
self._context: ClauseContextType = context
|
32
|
+
self._dialect: Dialect = dialect
|
25
33
|
|
26
34
|
def __enter__(self) -> IStatements_two_generic[TParent, TRepo]:
|
27
35
|
for comparer, by in self._joins:
|
28
36
|
fk_clause, alias = self.get_fk_clause(comparer)
|
29
37
|
|
30
|
-
foreign_key: ForeignKey = ForeignKey(comparer=comparer, clause_name=alias, keep_alive=True)
|
31
|
-
fk_clause.alias_table = foreign_key.
|
38
|
+
foreign_key: ForeignKey = ForeignKey(comparer=comparer, clause_name=alias, keep_alive=True, dialect=self._dialect)
|
39
|
+
fk_clause.alias_table = foreign_key.get_alias(self._dialect)
|
32
40
|
self._context.add_clause_to_context(fk_clause)
|
33
41
|
setattr(self._parent, alias, foreign_key)
|
34
42
|
|
@@ -64,10 +72,10 @@ class JoinContext[TParent: Table, TRepo]:
|
|
64
72
|
>>> (A.fk_b == B.pk_b) & (B.fk_c == C.pk_c) # Incorrect
|
65
73
|
"""
|
66
74
|
clause_dicc: dict[Table, ClauseInfo] = {
|
67
|
-
comparer.left_condition.table: comparer.left_condition,
|
68
|
-
comparer.right_condition.table: comparer.right_condition,
|
75
|
+
comparer.left_condition(self._dialect).table: comparer.left_condition(self._dialect),
|
76
|
+
comparer.right_condition(self._dialect).table: comparer.right_condition(self._dialect),
|
69
77
|
}
|
70
|
-
conditions = set([comparer.left_condition.table, comparer.right_condition.table])
|
78
|
+
conditions = set([comparer.left_condition(self._dialect).table, comparer.right_condition(self._dialect).table])
|
71
79
|
model = set([self._statements.model])
|
72
80
|
|
73
81
|
parent_table = conditions.difference(model).pop()
|