ormlambda 3.35.3__py3-none-any.whl → 4.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ormlambda/__init__.py +79 -51
- ormlambda/caster/caster.py +6 -1
- ormlambda/common/abstract_classes/__init__.py +0 -2
- ormlambda/common/enums/__init__.py +1 -0
- ormlambda/common/enums/order_type.py +9 -0
- ormlambda/common/errors/__init__.py +13 -3
- ormlambda/common/global_checker.py +86 -8
- ormlambda/common/interfaces/IQueryCommand.py +2 -2
- ormlambda/common/interfaces/__init__.py +0 -2
- ormlambda/dialects/__init__.py +75 -3
- ormlambda/dialects/default/base.py +1 -1
- ormlambda/dialects/mysql/__init__.py +35 -78
- ormlambda/dialects/mysql/base.py +226 -40
- ormlambda/dialects/mysql/clauses/ST_AsText.py +26 -0
- ormlambda/dialects/mysql/clauses/ST_Contains.py +30 -0
- ormlambda/dialects/mysql/clauses/__init__.py +1 -0
- ormlambda/dialects/mysql/repository/__init__.py +1 -0
- ormlambda/{databases/my_sql → dialects/mysql/repository}/repository.py +0 -5
- ormlambda/dialects/mysql/types.py +6 -0
- ormlambda/engine/base.py +26 -4
- ormlambda/errors.py +9 -0
- ormlambda/model/base_model.py +3 -10
- ormlambda/repository/base_repository.py +1 -1
- ormlambda/repository/interfaces/IRepositoryBase.py +0 -7
- ormlambda/repository/response.py +12 -7
- ormlambda/sql/__init__.py +12 -3
- ormlambda/sql/clause_info/__init__.py +0 -2
- ormlambda/sql/clause_info/clause_info.py +94 -76
- ormlambda/sql/clause_info/interface/IAggregate.py +14 -4
- ormlambda/sql/clause_info/interface/IClauseInfo.py +6 -11
- ormlambda/sql/clauses/alias.py +6 -37
- ormlambda/sql/clauses/count.py +21 -36
- ormlambda/sql/clauses/group_by.py +13 -19
- ormlambda/sql/clauses/having.py +2 -6
- ormlambda/sql/clauses/insert.py +3 -3
- ormlambda/sql/clauses/interfaces/__init__.py +0 -1
- ormlambda/sql/clauses/join/join_context.py +5 -12
- ormlambda/sql/clauses/joins.py +34 -52
- ormlambda/sql/clauses/limit.py +1 -2
- ormlambda/sql/clauses/offset.py +1 -2
- ormlambda/sql/clauses/order.py +17 -21
- ormlambda/sql/clauses/select.py +56 -28
- ormlambda/sql/clauses/update.py +13 -10
- ormlambda/sql/clauses/where.py +20 -39
- ormlambda/sql/column/__init__.py +1 -0
- ormlambda/sql/column/column.py +19 -12
- ormlambda/sql/column/column_proxy.py +117 -0
- ormlambda/sql/column_table_proxy.py +23 -0
- ormlambda/sql/comparer.py +31 -65
- ormlambda/sql/compiler.py +248 -58
- ormlambda/sql/context/__init__.py +304 -0
- ormlambda/sql/ddl.py +19 -5
- ormlambda/sql/elements.py +3 -0
- ormlambda/sql/foreign_key.py +42 -64
- ormlambda/sql/functions/__init__.py +0 -1
- ormlambda/sql/functions/concat.py +35 -38
- ormlambda/sql/functions/max.py +12 -36
- ormlambda/sql/functions/min.py +13 -28
- ormlambda/sql/functions/sum.py +17 -33
- ormlambda/sql/sqltypes.py +2 -0
- ormlambda/sql/table/__init__.py +1 -0
- ormlambda/sql/table/table.py +31 -45
- ormlambda/sql/table/table_proxy.py +88 -0
- ormlambda/sql/type_api.py +4 -1
- ormlambda/sql/types.py +15 -12
- ormlambda/statements/__init__.py +0 -2
- ormlambda/statements/base_statement.py +51 -84
- ormlambda/statements/interfaces/IStatements.py +77 -123
- ormlambda/statements/interfaces/__init__.py +1 -1
- ormlambda/statements/query_builder.py +296 -128
- ormlambda/statements/statements.py +120 -110
- ormlambda/statements/types.py +5 -25
- ormlambda/util/__init__.py +7 -100
- ormlambda/util/langhelpers.py +102 -0
- ormlambda/util/module_tree/dynamic_module.py +1 -1
- ormlambda/util/preloaded.py +80 -0
- ormlambda/util/typing.py +12 -3
- {ormlambda-3.35.3.dist-info → ormlambda-4.0.0.dist-info}/METADATA +29 -31
- ormlambda-4.0.0.dist-info/RECORD +139 -0
- ormlambda/common/abstract_classes/clause_info_converter.py +0 -65
- ormlambda/common/abstract_classes/decomposition_query.py +0 -141
- ormlambda/common/abstract_classes/query_base.py +0 -15
- ormlambda/common/interfaces/ICustomAlias.py +0 -7
- ormlambda/common/interfaces/IDecompositionQuery.py +0 -33
- ormlambda/databases/__init__.py +0 -4
- ormlambda/databases/my_sql/__init__.py +0 -3
- ormlambda/databases/my_sql/clauses/ST_AsText.py +0 -37
- ormlambda/databases/my_sql/clauses/ST_Contains.py +0 -36
- ormlambda/databases/my_sql/clauses/__init__.py +0 -14
- ormlambda/databases/my_sql/clauses/count.py +0 -33
- ormlambda/databases/my_sql/clauses/delete.py +0 -9
- ormlambda/databases/my_sql/clauses/drop_table.py +0 -26
- ormlambda/databases/my_sql/clauses/group_by.py +0 -17
- ormlambda/databases/my_sql/clauses/having.py +0 -12
- ormlambda/databases/my_sql/clauses/insert.py +0 -9
- ormlambda/databases/my_sql/clauses/joins.py +0 -14
- ormlambda/databases/my_sql/clauses/limit.py +0 -6
- ormlambda/databases/my_sql/clauses/offset.py +0 -6
- ormlambda/databases/my_sql/clauses/order.py +0 -8
- ormlambda/databases/my_sql/clauses/update.py +0 -8
- ormlambda/databases/my_sql/clauses/upsert.py +0 -9
- ormlambda/databases/my_sql/clauses/where.py +0 -7
- ormlambda/dialects/interface/__init__.py +0 -1
- ormlambda/dialects/interface/dialect.py +0 -78
- ormlambda/sql/clause_info/aggregate_function_base.py +0 -96
- ormlambda/sql/clause_info/clause_info_context.py +0 -87
- ormlambda/sql/clauses/interfaces/ISelect.py +0 -17
- ormlambda/sql/clauses/new_join.py +0 -119
- ormlambda/util/load_module.py +0 -21
- ormlambda/util/plugin_loader.py +0 -32
- ormlambda-3.35.3.dist-info/RECORD +0 -159
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/__init__.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/caster.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/__init__.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/boolean.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/bytes.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/date.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/datetime.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/decimal.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/float.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/int.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/iterable.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/json.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/none.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/point.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/string.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql/repository}/pool_types.py +0 -0
- {ormlambda-3.35.3.dist-info → ormlambda-4.0.0.dist-info}/AUTHORS +0 -0
- {ormlambda-3.35.3.dist-info → ormlambda-4.0.0.dist-info}/LICENSE +0 -0
- {ormlambda-3.35.3.dist-info → ormlambda-4.0.0.dist-info}/WHEEL +0 -0
ormlambda/dialects/mysql/base.py
CHANGED
@@ -1,12 +1,22 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
from types import ModuleType
|
3
|
+
from ormlambda import ColumnProxy, ForeignKey, TableProxy
|
3
4
|
from ormlambda.sql import compiler
|
5
|
+
from ormlambda.sql.clause_info import ClauseInfo
|
6
|
+
from ormlambda.common.errors import NotKeysInIAggregateError
|
7
|
+
from ormlambda.sql.types import ASTERISK
|
8
|
+
|
9
|
+
|
4
10
|
from .. import default
|
5
11
|
from typing import TYPE_CHECKING, Any, Iterable
|
6
12
|
|
7
13
|
if TYPE_CHECKING:
|
14
|
+
from test.test_clause_info import ST_Contains
|
15
|
+
from ormlambda import JoinSelector
|
16
|
+
from ormlambda.sql.comparer import Comparer
|
8
17
|
from ormlambda.sql.column.column import Column
|
9
18
|
from mysql import connector
|
19
|
+
from ormlambda.dialects.mysql.clauses import ST_AsText
|
10
20
|
|
11
21
|
from .types import (
|
12
22
|
_NumericType,
|
@@ -46,11 +56,9 @@ from ormlambda.sql.sqltypes import (
|
|
46
56
|
DATE,
|
47
57
|
UUID,
|
48
58
|
VARBINARY,
|
49
|
-
BINARY,
|
50
59
|
)
|
51
|
-
|
52
|
-
|
53
|
-
from ormlambda.databases.my_sql import MySQLRepository, MySQLCaster
|
60
|
+
from .caster import MySQLCaster
|
61
|
+
from .repository import MySQLRepository
|
54
62
|
|
55
63
|
|
56
64
|
if TYPE_CHECKING:
|
@@ -77,59 +85,231 @@ if TYPE_CHECKING:
|
|
77
85
|
)
|
78
86
|
|
79
87
|
|
80
|
-
from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext
|
81
|
-
|
82
|
-
|
83
88
|
class MySQLCompiler(compiler.SQLCompiler):
|
84
89
|
render_table_with_column_in_update_from = True
|
85
90
|
"""Overridden from base SQLCompiler value"""
|
86
91
|
|
87
|
-
def
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
92
|
+
def visit_table_proxy(self, table: TableProxy, **kw) -> str:
|
93
|
+
param = {
|
94
|
+
"table": None,
|
95
|
+
"column": table._table_class.__table_name__,
|
96
|
+
"dialect": self.dialect,
|
97
|
+
"alias_clause": alias if (alias := table.get_table_chain()) else None,
|
98
|
+
**kw,
|
99
|
+
}
|
100
|
+
return ClauseInfo(**param).query(dialect=self.dialect)
|
101
|
+
|
102
|
+
def visit_column(self, column: Column, **kw) -> str:
|
103
|
+
params = {
|
104
|
+
"table": column.table,
|
105
|
+
"column": column.column_name,
|
106
|
+
"dtype": column.dtype,
|
107
|
+
"dialect": self.dialect,
|
108
|
+
**kw,
|
109
|
+
}
|
110
|
+
return ClauseInfo(**params).query(self.dialect)
|
111
|
+
|
112
|
+
def visit_column_proxy(self, column: ColumnProxy, **kw) -> str:
|
113
|
+
from ormlambda.sql.clause_info import ClauseInfo
|
114
|
+
|
115
|
+
alias_table = column.get_table_chain()
|
116
|
+
|
117
|
+
params = {
|
118
|
+
"table": column.table,
|
119
|
+
"column": column,
|
120
|
+
"alias_table": alias_table if alias_table else "{table}",
|
121
|
+
"alias_clause": column.alias or "{column}",
|
122
|
+
"dtype": column._column.dtype,
|
123
|
+
"dialect": self.dialect,
|
124
|
+
**kw,
|
125
|
+
}
|
126
|
+
clause_info = ClauseInfo(**params)
|
127
|
+
if column.alias != clause_info.alias_clause:
|
128
|
+
# FIXME [ ]: i don't understand why
|
129
|
+
column.alias = clause_info.alias_clause
|
130
|
+
return clause_info.query(self.dialect)
|
131
|
+
|
132
|
+
def visit_comparer(self, comparer: Comparer, **kwargs) -> str:
|
133
|
+
from ormlambda.sql.comparer import CleanValue, Comparer
|
134
|
+
|
135
|
+
def compile_condition(condition: Any):
|
136
|
+
if isinstance(condition, ColumnProxy | Comparer):
|
137
|
+
param = {
|
138
|
+
"alias_clause": None,
|
139
|
+
**kwargs,
|
140
|
+
}
|
141
|
+
return condition.compile(self.dialect, **param).string
|
142
|
+
return MySQLCaster.cast(condition, type(condition)).string_data
|
143
|
+
|
144
|
+
lcond = compile_condition(comparer.left_condition)
|
145
|
+
rcond = compile_condition(comparer.right_condition)
|
146
|
+
|
147
|
+
if comparer._flags:
|
148
|
+
rcond = CleanValue(rcond, comparer._flags).clean()
|
149
|
+
|
150
|
+
return f"{lcond} {comparer.compare} {rcond}"
|
151
|
+
|
152
|
+
def visit_join(self, join: JoinSelector) -> str:
|
153
|
+
rt = join.rcon.table
|
154
|
+
rtable = TableProxy(table_class=rt, path=join.rcon.path)
|
155
|
+
|
156
|
+
from_clause = rtable.compile(self.dialect, alias_clause=join.alias).string
|
157
|
+
left_table_clause = join.lcon.compile(self.dialect, alias_clause=None).string
|
158
|
+
right_table_clause = join.rcon.compile(self.dialect, alias_table=join.alias, alias_clause=None).string
|
159
|
+
list_ = [
|
160
|
+
join._by.value, # inner join
|
161
|
+
from_clause,
|
162
|
+
"ON",
|
163
|
+
left_table_clause,
|
164
|
+
join._compareop, # =
|
165
|
+
right_table_clause,
|
166
|
+
]
|
167
|
+
return " ".join([x for x in list_ if x is not None])
|
168
|
+
|
169
|
+
def visit_select(self, select: Select):
|
170
|
+
params = {}
|
171
|
+
|
172
|
+
# COMMENT: when passing alias into 'select' method, we gonna replace the current aliases of columns with the generic one.
|
173
|
+
# TODOM []: Check if we manage some conditions or not
|
174
|
+
if select.avoid_duplicates:
|
175
|
+
params["alias_clause"] = lambda x: x.get_full_chain("_")
|
176
|
+
elif select.alias:
|
177
|
+
params["alias_clause"] = select.alias
|
178
|
+
|
179
|
+
columns = ClauseInfo.join_clauses(select.columns, ",", dialect=self.dialect, **params)
|
180
|
+
from_ = ClauseInfo(
|
181
|
+
select._table,
|
182
|
+
None,
|
183
|
+
alias_table=select._alias_table,
|
184
|
+
dialect=self.dialect,
|
185
|
+
).query(self.dialect)
|
186
|
+
|
187
|
+
return f"SELECT {columns} FROM {from_}"
|
188
|
+
|
189
|
+
def visit_group_by(self, groupby: GroupBy):
|
190
|
+
column = ", ".join(x.compile(self.dialect, alias_clause=None).string for x in groupby.column)
|
191
|
+
return f"GROUP BY {column}"
|
93
192
|
|
94
193
|
def visit_limit(self, limit: Limit, **kw):
|
95
|
-
return f"
|
194
|
+
return f"LIMIT {limit._number}"
|
96
195
|
|
97
196
|
# TODOH []: include the rest of visit methods
|
98
|
-
def visit_insert(self, insert: Insert, **kw) ->
|
99
|
-
|
100
|
-
|
101
|
-
def
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
def
|
108
|
-
|
197
|
+
def visit_insert(self, insert: Insert, **kw) -> str:
|
198
|
+
pass
|
199
|
+
|
200
|
+
def visit_delete(self, delete: Delete, **kw) -> str:
|
201
|
+
pass
|
202
|
+
|
203
|
+
def visit_upsert(self, upsert: Upsert, **kw) -> str:
|
204
|
+
pass
|
205
|
+
|
206
|
+
def visit_update(self, update: Update, **kw) -> str:
|
207
|
+
pass
|
208
|
+
|
209
|
+
def visit_offset(self, offset: Offset, **kw) -> str:
|
210
|
+
return f"OFFSET {offset._number}"
|
211
|
+
|
212
|
+
def visit_count(self, count: Count, **kw) -> str:
|
213
|
+
if isinstance(count.column, ColumnProxy):
|
214
|
+
column = count.column.compile(self.dialect, alias_clause=None).string
|
215
|
+
|
216
|
+
elif isinstance(count.column, TableProxy):
|
217
|
+
column = ASTERISK
|
218
|
+
|
219
|
+
else:
|
220
|
+
column = count.column
|
221
|
+
|
222
|
+
return ClauseInfo.concat_alias_and_column(f"COUNT({column})", count.alias)
|
223
|
+
|
224
|
+
def visit_where(self, where: Where) -> str:
|
225
|
+
from ormlambda.sql.comparer import Comparer
|
226
|
+
|
227
|
+
if not where.comparer:
|
228
|
+
return ""
|
229
|
+
cols = Comparer.join_comparers(list(where.comparer), restrictive=where.restrictive, dialect=self.dialect)
|
230
|
+
return f"WHERE {cols}"
|
231
|
+
|
232
|
+
def visit_having(self, having: Having) -> str:
|
233
|
+
from ormlambda.sql.comparer import Comparer
|
234
|
+
|
235
|
+
cols = Comparer.join_comparers(list(having.comparer), restrictive=having.restrictive, dialect=self.dialect, table=None, literal=True)
|
236
|
+
return f"HAVING {cols}"
|
237
|
+
|
238
|
+
def visit_order(self, order: Order, **kw) -> str:
|
239
|
+
ORDER = "ORDER BY"
|
109
240
|
string_columns: list[str] = []
|
110
|
-
columns = order.
|
241
|
+
columns = order.columns
|
111
242
|
|
112
243
|
# if this attr is not iterable means that we only pass one column without wrapped in a list or tuple
|
113
|
-
if isinstance(columns, str):
|
114
|
-
string_columns = f"{columns} {str(order._order_type[0])}"
|
115
|
-
return f"{order.FUNCTION_NAME()} {string_columns}"
|
116
244
|
|
117
245
|
if not isinstance(columns, Iterable):
|
118
246
|
columns = (columns,)
|
119
247
|
|
120
248
|
assert len(columns) == len(order._order_type)
|
121
249
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
250
|
+
for index, clause in enumerate(columns):
|
251
|
+
# We need to avoid wrapped columns with `` or '' when clause hasn't table
|
252
|
+
if isinstance(columns, str):
|
253
|
+
string_columns = f"{columns} {str(order._order_type[0])}"
|
254
|
+
return f"{ORDER} {string_columns}"
|
255
|
+
|
256
|
+
if not clause.table:
|
257
|
+
string_column = clause.compile(
|
258
|
+
self.dialect,
|
259
|
+
table=None,
|
260
|
+
alias_clause=None,
|
261
|
+
literal=True,
|
262
|
+
).string
|
263
|
+
else:
|
264
|
+
string_column = clause.compile(self.dialect, alias_clause=None).string
|
265
|
+
|
266
|
+
string_columns.append(f"{string_column} {str(order._order_type[index])}")
|
267
|
+
|
268
|
+
return f"{ORDER} {', '.join(string_columns)}"
|
269
|
+
|
270
|
+
def visit_concat(self, concat: Concat, **kw) -> Concat:
|
271
|
+
columns: list[str] = []
|
272
|
+
|
273
|
+
for clause in concat.values:
|
274
|
+
if isinstance(clause, ColumnProxy):
|
275
|
+
compiled = clause.compile(self.dialect, alias_clause=None).string
|
276
|
+
else:
|
277
|
+
compiled = f"'{clause}'"
|
278
|
+
columns.append(compiled)
|
279
|
+
|
280
|
+
clause_info = ClauseInfo(
|
281
|
+
table=None,
|
282
|
+
column=f"CONCAT({', '.join(columns)})",
|
283
|
+
alias_clause=concat.alias,
|
284
|
+
dialect=self.dialect,
|
285
|
+
)
|
286
|
+
return clause_info.query(self.dialect)
|
287
|
+
|
288
|
+
def visit_max(self, max: Max, **kw) -> str:
|
289
|
+
attr = {**kw, "alias_clause": None}
|
290
|
+
column = max.column.compile(self.dialect, **attr).string
|
291
|
+
return ClauseInfo.concat_alias_and_column(f"MAX({column})", max.alias)
|
292
|
+
|
293
|
+
def visit_min(self, min: Min, **kw) -> str:
|
294
|
+
attr = {**kw, "alias_clause": None}
|
295
|
+
column = min.column.compile(self.dialect, **attr).string
|
296
|
+
return ClauseInfo.concat_alias_and_column(f"MIN({column})", min.alias)
|
297
|
+
|
298
|
+
def visit_sum(self, sum: Sum, **kw) -> str:
|
299
|
+
attr = {**kw, "alias_clause": None}
|
300
|
+
column = sum.column.compile(self.dialect, **attr).string
|
301
|
+
return ClauseInfo.concat_alias_and_column(f"SUM({column})", sum.alias)
|
302
|
+
|
303
|
+
def visit_st_astext(self, st_astext: ST_AsText) -> str:
|
304
|
+
# avoid use placeholder when using IAggregate because no make sense.
|
305
|
+
if st_astext.alias and (found := ClauseInfo._keyRegex.findall(st_astext.alias)):
|
306
|
+
raise NotKeysInIAggregateError(found)
|
307
|
+
return ClauseInfo.concat_alias_and_column(f"ST_AsText({st_astext.column.compile(self.dialect, alias_clause=None).string})", st_astext.alias)
|
308
|
+
|
309
|
+
def visit_st_contains(self, st_contains: ST_Contains) -> str:
|
310
|
+
attr1 = st_contains.column.compile(self.dialect, alias_clause=None).string
|
311
|
+
attr2 = ClauseInfo(None, st_contains.point, dialect=self.dialect).query(self.dialect, alias_clause=None)
|
312
|
+
return f"ST_Contains({attr1}, {attr2})"
|
133
313
|
|
134
314
|
|
135
315
|
class MySQLDDLCompiler(compiler.DDLCompiler):
|
@@ -149,6 +329,12 @@ class MySQLDDLCompiler(compiler.DDLCompiler):
|
|
149
329
|
|
150
330
|
return colspec
|
151
331
|
|
332
|
+
def visit_foreign_key(self, fk: ForeignKey, **kw) -> str:
|
333
|
+
compare = fk.resolved_function()
|
334
|
+
lcon = compare.left_condition
|
335
|
+
rcon = compare.right_condition
|
336
|
+
return f"FOREIGN KEY ({lcon.column_name}) REFERENCES {rcon.table.__table_name__}({rcon.column_name})"
|
337
|
+
|
152
338
|
|
153
339
|
class MySQLTypeCompiler(compiler.GenericTypeCompiler):
|
154
340
|
def mysql_type(self, type_: Any) -> bool:
|
@@ -0,0 +1,26 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from ormlambda.sql.clause_info import IAggregate
|
3
|
+
from ormlambda.sql.types import ColumnType, AliasType
|
4
|
+
|
5
|
+
from ormlambda.sql.elements import ClauseElement
|
6
|
+
|
7
|
+
|
8
|
+
class ST_AsText[T, TProp](ClauseElement, IAggregate):
|
9
|
+
"""
|
10
|
+
https://dev.mysql.com/doc/refman/8.4/en/fetching-spatial-data.html
|
11
|
+
|
12
|
+
The ST_AsText() function converts a geometry from internal format to a WKT string.
|
13
|
+
"""
|
14
|
+
|
15
|
+
__visit_name__ = "st_astext"
|
16
|
+
|
17
|
+
def __init__(self, point: ColumnType[TProp], alias: AliasType[TProp] = "st_astext") -> None:
|
18
|
+
self.alias = alias
|
19
|
+
self.column = point
|
20
|
+
|
21
|
+
def used_columns(self):
|
22
|
+
return [self.column]
|
23
|
+
|
24
|
+
@property
|
25
|
+
def dtype(self) -> str:
|
26
|
+
return str
|
@@ -0,0 +1,30 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
import typing as tp
|
3
|
+
|
4
|
+
from shapely import Point
|
5
|
+
|
6
|
+
from ormlambda import Column
|
7
|
+
from ormlambda.sql.types import ColumnType, AliasType
|
8
|
+
from ormlambda.sql.clause_info import IAggregate
|
9
|
+
from ormlambda.sql.elements import ClauseElement
|
10
|
+
|
11
|
+
|
12
|
+
class ST_Contains(ClauseElement, IAggregate):
|
13
|
+
__visit_name__ = "st_contains"
|
14
|
+
|
15
|
+
def __init__[TProp: Column](
|
16
|
+
self,
|
17
|
+
column: ColumnType[TProp],
|
18
|
+
point: Point,
|
19
|
+
alias: AliasType[ColumnType[TProp]] = "st_contains",
|
20
|
+
):
|
21
|
+
self.column = column
|
22
|
+
self.point = point
|
23
|
+
self.alias = alias
|
24
|
+
|
25
|
+
@property
|
26
|
+
def dtype(self) -> str:
|
27
|
+
return str
|
28
|
+
|
29
|
+
def used_columns(self) -> tp.Iterable:
|
30
|
+
return [self.attr1]
|
@@ -0,0 +1 @@
|
|
1
|
+
from .ST_AsText import ST_AsText as ST_AsText
|
@@ -0,0 +1 @@
|
|
1
|
+
from .repository import MySQLRepository # noqa: F401
|
@@ -10,7 +10,6 @@ from mysql.connector.pooling import MySQLConnectionPool # noqa: F401
|
|
10
10
|
from ormlambda.repository import BaseRepository
|
11
11
|
|
12
12
|
# Custom libraries
|
13
|
-
from .clauses import DropTable
|
14
13
|
from ormlambda.repository.response import Response
|
15
14
|
from ormlambda.caster import Caster
|
16
15
|
|
@@ -167,10 +166,6 @@ class MySQLRepository(BaseRepository[MySQLConnectionPool]):
|
|
167
166
|
cursor.execute(query)
|
168
167
|
return None
|
169
168
|
|
170
|
-
@override
|
171
|
-
def drop_table(self, name: str) -> None:
|
172
|
-
return DropTable(self).execute(name)
|
173
|
-
|
174
169
|
@override
|
175
170
|
def database_exists(self, name: str) -> bool:
|
176
171
|
temp_config = self._pool._cnx_config
|
@@ -11,6 +11,9 @@ import datetime
|
|
11
11
|
|
12
12
|
from ormlambda.sql import sqltypes
|
13
13
|
|
14
|
+
# to be able to import the same object with different dialect
|
15
|
+
from ormlambda.sql.sqltypes import POINT # noqa: F401
|
16
|
+
|
14
17
|
|
15
18
|
class _NumericCommonType:
|
16
19
|
"""Base for MySQL numeric types.
|
@@ -231,6 +234,9 @@ class INTEGER(_IntegerType, sqltypes.INTEGER):
|
|
231
234
|
super().__init__(display_width=display_width, **kw)
|
232
235
|
|
233
236
|
|
237
|
+
INT = INTEGER
|
238
|
+
|
239
|
+
|
234
240
|
class BIGINT(_IntegerType, sqltypes.BIGINT):
|
235
241
|
"""MySQL BIGINTEGER type."""
|
236
242
|
|
ormlambda/engine/base.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import TYPE_CHECKING, BinaryIO, Literal, Optional, TextIO
|
3
4
|
from ormlambda.engine import url
|
4
|
-
from ormlambda.sql.ddl import CreateSchema, DropSchema
|
5
|
-
|
5
|
+
from ormlambda.sql.ddl import CreateSchema, DropSchema, CreateBackup
|
6
6
|
|
7
7
|
if TYPE_CHECKING:
|
8
8
|
from ormlambda.dialects import Dialect
|
@@ -16,11 +16,14 @@ class Engine:
|
|
16
16
|
self.url = url
|
17
17
|
self.repository = self.dialect.repository_cls(url, dialect=dialect)
|
18
18
|
|
19
|
+
def __repr__(self):
|
20
|
+
return f"{Engine.__name__}: {self.url}"
|
21
|
+
|
19
22
|
def create_schema(self, schema_name: str, if_exists: TypeExists = "fail") -> None:
|
20
23
|
if if_exists == "replace":
|
21
24
|
self.drop_schema(schema_name, if_exists)
|
22
25
|
|
23
|
-
sql = CreateSchema(schema=schema_name, if_not_exists=if_exists==
|
26
|
+
sql = CreateSchema(schema=schema_name, if_not_exists=if_exists == "append").compile(self.dialect).string
|
24
27
|
try:
|
25
28
|
self.repository.execute(sql)
|
26
29
|
except Exception:
|
@@ -56,3 +59,22 @@ class Engine:
|
|
56
59
|
def set_database(self, name: str) -> None:
|
57
60
|
self.repository.database = name
|
58
61
|
return None
|
62
|
+
|
63
|
+
def create_backup(
|
64
|
+
self,
|
65
|
+
output: Optional[str | BinaryIO | TextIO] = None,
|
66
|
+
compress: bool = False,
|
67
|
+
backup_dir: str = "backups",
|
68
|
+
**kw,
|
69
|
+
) -> Optional[str | BinaryIO | Path]:
|
70
|
+
return (
|
71
|
+
CreateBackup(self.url)
|
72
|
+
.compile(
|
73
|
+
self.dialect,
|
74
|
+
output=output,
|
75
|
+
compress=compress,
|
76
|
+
backup_dir=backup_dir,
|
77
|
+
**kw,
|
78
|
+
)
|
79
|
+
.string
|
80
|
+
)
|
ormlambda/errors.py
CHANGED
@@ -15,3 +15,12 @@ class NoSuchModuleError(Exception):
|
|
15
15
|
|
16
16
|
def __str__(self):
|
17
17
|
return f"NoSuchModuleError: {self.args[0]}"
|
18
|
+
|
19
|
+
|
20
|
+
class DuplicatedClauseName(Exception):
|
21
|
+
def __init__(self, names: tuple[str], **kw):
|
22
|
+
self.names = names
|
23
|
+
super().__init__(**kw)
|
24
|
+
|
25
|
+
def __str__(self):
|
26
|
+
return f"Some clauses has the same alias. {self.names}\nTry wrapping the clause with the 'Alias' class first or setting 'avoid_duplicate' param as 'True'"
|
ormlambda/model/base_model.py
CHANGED
@@ -7,22 +7,15 @@ from ormlambda.statements import Statements
|
|
7
7
|
from ormlambda.engine import Engine
|
8
8
|
|
9
9
|
if TYPE_CHECKING:
|
10
|
-
from ormlambda.statements.interfaces import
|
11
|
-
|
12
|
-
|
13
|
-
# endregion
|
10
|
+
from ormlambda.statements.interfaces import IStatements
|
14
11
|
|
15
12
|
|
16
13
|
class BaseModel[T]:
|
17
14
|
"""
|
18
|
-
|
19
|
-
|
20
|
-
Contiene los metodos necesarios para hacer consultas a una tabla
|
15
|
+
This class contains those methods to make query to a table
|
21
16
|
"""
|
22
17
|
|
23
|
-
|
24
|
-
|
25
|
-
def __new__[TPool](cls, model: Type[T], engine: Engine) -> IStatements_two_generic[T, TPool]:
|
18
|
+
def __new__(cls, model: Type[T], engine: Engine) -> IStatements[T]:
|
26
19
|
if engine is None:
|
27
20
|
raise ValueError("`None` cannot be passed to the `repository` attribute when calling the `BaseModel` class")
|
28
21
|
|
@@ -9,12 +9,8 @@ from typing import (
|
|
9
9
|
Sequence,
|
10
10
|
Type,
|
11
11
|
Iterable,
|
12
|
-
TYPE_CHECKING,
|
13
12
|
)
|
14
13
|
|
15
|
-
if TYPE_CHECKING:
|
16
|
-
from ormlambda.statements.types import TypeExists
|
17
|
-
|
18
14
|
|
19
15
|
type _DBAPICursorDescription = Sequence[
|
20
16
|
tuple[
|
@@ -142,9 +138,6 @@ class IRepositoryBase(ABC):
|
|
142
138
|
@abstractmethod
|
143
139
|
def execute(self, query: str) -> None: ...
|
144
140
|
|
145
|
-
@abstractmethod
|
146
|
-
def drop_table(self, name: str) -> None: ...
|
147
|
-
|
148
141
|
@abstractmethod
|
149
142
|
def table_exists(self, name: str) -> bool: ...
|
150
143
|
|
ormlambda/repository/response.py
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
from typing import Type, Any, TYPE_CHECKING, Optional
|
3
|
-
from ormlambda.dialects
|
3
|
+
from ormlambda.dialects import Dialect
|
4
4
|
import shapely as shp
|
5
5
|
|
6
6
|
# Custom libraries
|
7
7
|
from ormlambda.sql.clauses import Alias
|
8
8
|
|
9
9
|
if TYPE_CHECKING:
|
10
|
-
from ormlambda.
|
10
|
+
from ormlambda.sql.clause_info import ClauseInfo
|
11
11
|
from ormlambda import Table
|
12
12
|
from ormlambda.sql.clauses import Select
|
13
13
|
|
@@ -88,10 +88,15 @@ class Response[TFlavour, *Ts]:
|
|
88
88
|
nonlocal data
|
89
89
|
replacer_dicc: dict[str, str] = {}
|
90
90
|
|
91
|
-
for col in self._select.
|
92
|
-
if
|
91
|
+
for col in self._select.columns:
|
92
|
+
if isinstance(col, Alias):
|
93
93
|
continue
|
94
|
-
|
94
|
+
|
95
|
+
if hasattr(col, "alias"):
|
96
|
+
continue
|
97
|
+
|
98
|
+
alias = col.alias if col.alias else col.get_full_chain("_")
|
99
|
+
replacer_dicc[alias] = col.column_name
|
95
100
|
|
96
101
|
cleaned_column_names = [replacer_dicc.get(col, col) for col in self._columns]
|
97
102
|
|
@@ -116,8 +121,8 @@ class Response[TFlavour, *Ts]:
|
|
116
121
|
new_row: list = []
|
117
122
|
for i, data in enumerate(row):
|
118
123
|
alias = self._columns[i]
|
119
|
-
|
120
|
-
parse_data = self._caster.for_value(data, value_type=
|
124
|
+
clause = self._select[alias]
|
125
|
+
parse_data = self._caster.for_value(data, value_type=clause.dtype).from_database
|
121
126
|
new_row.append(parse_data)
|
122
127
|
new_row = tuple(new_row)
|
123
128
|
if not isinstance(new_row, tuple):
|
ormlambda/sql/__init__.py
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
-
from .column import Column
|
2
|
-
from .foreign_key import ForeignKey
|
3
|
-
from .table import Table
|
1
|
+
from .column import Column, ColumnProxy
|
2
|
+
from .foreign_key import ForeignKey
|
3
|
+
from .table import Table, TableProxy
|
4
|
+
|
5
|
+
|
6
|
+
__all__ = (
|
7
|
+
"Column",
|
8
|
+
"ColumnProxy",
|
9
|
+
"ForeignKey",
|
10
|
+
"Table",
|
11
|
+
"TableProxy",
|
12
|
+
)
|