ormlambda 3.35.2__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.
Files changed (130) hide show
  1. ormlambda/__init__.py +79 -51
  2. ormlambda/caster/caster.py +6 -1
  3. ormlambda/common/abstract_classes/__init__.py +0 -2
  4. ormlambda/common/enums/__init__.py +1 -0
  5. ormlambda/common/enums/order_type.py +9 -0
  6. ormlambda/common/errors/__init__.py +13 -3
  7. ormlambda/common/global_checker.py +86 -8
  8. ormlambda/common/interfaces/IQueryCommand.py +2 -2
  9. ormlambda/common/interfaces/__init__.py +0 -2
  10. ormlambda/dialects/__init__.py +75 -3
  11. ormlambda/dialects/default/base.py +1 -1
  12. ormlambda/dialects/mysql/__init__.py +35 -78
  13. ormlambda/dialects/mysql/base.py +226 -40
  14. ormlambda/dialects/mysql/clauses/ST_AsText.py +26 -0
  15. ormlambda/dialects/mysql/clauses/ST_Contains.py +30 -0
  16. ormlambda/dialects/mysql/clauses/__init__.py +1 -0
  17. ormlambda/dialects/mysql/repository/__init__.py +1 -0
  18. ormlambda/{databases/my_sql → dialects/mysql/repository}/repository.py +0 -5
  19. ormlambda/dialects/mysql/types.py +6 -0
  20. ormlambda/engine/base.py +26 -4
  21. ormlambda/errors.py +9 -0
  22. ormlambda/model/base_model.py +3 -10
  23. ormlambda/repository/base_repository.py +1 -1
  24. ormlambda/repository/interfaces/IRepositoryBase.py +0 -7
  25. ormlambda/repository/response.py +12 -7
  26. ormlambda/sql/__init__.py +12 -3
  27. ormlambda/sql/clause_info/__init__.py +0 -2
  28. ormlambda/sql/clause_info/clause_info.py +94 -76
  29. ormlambda/sql/clause_info/interface/IAggregate.py +14 -4
  30. ormlambda/sql/clause_info/interface/IClauseInfo.py +6 -11
  31. ormlambda/sql/clauses/alias.py +6 -37
  32. ormlambda/sql/clauses/count.py +21 -36
  33. ormlambda/sql/clauses/group_by.py +13 -19
  34. ormlambda/sql/clauses/having.py +2 -6
  35. ormlambda/sql/clauses/insert.py +3 -3
  36. ormlambda/sql/clauses/interfaces/__init__.py +0 -1
  37. ormlambda/sql/clauses/join/join_context.py +5 -12
  38. ormlambda/sql/clauses/joins.py +34 -52
  39. ormlambda/sql/clauses/limit.py +1 -2
  40. ormlambda/sql/clauses/offset.py +1 -2
  41. ormlambda/sql/clauses/order.py +17 -21
  42. ormlambda/sql/clauses/select.py +56 -28
  43. ormlambda/sql/clauses/update.py +13 -10
  44. ormlambda/sql/clauses/where.py +20 -39
  45. ormlambda/sql/column/__init__.py +1 -0
  46. ormlambda/sql/column/column.py +19 -12
  47. ormlambda/sql/column/column_proxy.py +117 -0
  48. ormlambda/sql/column_table_proxy.py +23 -0
  49. ormlambda/sql/comparer.py +31 -65
  50. ormlambda/sql/compiler.py +248 -58
  51. ormlambda/sql/context/__init__.py +304 -0
  52. ormlambda/sql/ddl.py +19 -5
  53. ormlambda/sql/elements.py +3 -0
  54. ormlambda/sql/foreign_key.py +42 -64
  55. ormlambda/sql/functions/__init__.py +0 -1
  56. ormlambda/sql/functions/concat.py +35 -38
  57. ormlambda/sql/functions/max.py +12 -36
  58. ormlambda/sql/functions/min.py +13 -28
  59. ormlambda/sql/functions/sum.py +17 -33
  60. ormlambda/sql/sqltypes.py +2 -0
  61. ormlambda/sql/table/__init__.py +1 -0
  62. ormlambda/sql/table/table.py +32 -49
  63. ormlambda/sql/table/table_proxy.py +88 -0
  64. ormlambda/sql/type_api.py +4 -1
  65. ormlambda/sql/types.py +15 -12
  66. ormlambda/statements/__init__.py +0 -2
  67. ormlambda/statements/base_statement.py +51 -84
  68. ormlambda/statements/interfaces/IStatements.py +77 -123
  69. ormlambda/statements/interfaces/__init__.py +1 -1
  70. ormlambda/statements/query_builder.py +296 -128
  71. ormlambda/statements/statements.py +120 -110
  72. ormlambda/statements/types.py +5 -25
  73. ormlambda/util/__init__.py +7 -86
  74. ormlambda/util/langhelpers.py +102 -0
  75. ormlambda/util/module_tree/dynamic_module.py +1 -1
  76. ormlambda/util/preloaded.py +80 -0
  77. ormlambda/util/typing.py +12 -3
  78. {ormlambda-3.35.2.dist-info → ormlambda-4.0.0.dist-info}/METADATA +29 -31
  79. ormlambda-4.0.0.dist-info/RECORD +139 -0
  80. ormlambda/common/abstract_classes/clause_info_converter.py +0 -65
  81. ormlambda/common/abstract_classes/decomposition_query.py +0 -141
  82. ormlambda/common/abstract_classes/query_base.py +0 -15
  83. ormlambda/common/interfaces/ICustomAlias.py +0 -7
  84. ormlambda/common/interfaces/IDecompositionQuery.py +0 -33
  85. ormlambda/databases/__init__.py +0 -4
  86. ormlambda/databases/my_sql/__init__.py +0 -3
  87. ormlambda/databases/my_sql/clauses/ST_AsText.py +0 -37
  88. ormlambda/databases/my_sql/clauses/ST_Contains.py +0 -36
  89. ormlambda/databases/my_sql/clauses/__init__.py +0 -14
  90. ormlambda/databases/my_sql/clauses/count.py +0 -33
  91. ormlambda/databases/my_sql/clauses/delete.py +0 -9
  92. ormlambda/databases/my_sql/clauses/drop_table.py +0 -26
  93. ormlambda/databases/my_sql/clauses/group_by.py +0 -17
  94. ormlambda/databases/my_sql/clauses/having.py +0 -12
  95. ormlambda/databases/my_sql/clauses/insert.py +0 -9
  96. ormlambda/databases/my_sql/clauses/joins.py +0 -14
  97. ormlambda/databases/my_sql/clauses/limit.py +0 -6
  98. ormlambda/databases/my_sql/clauses/offset.py +0 -6
  99. ormlambda/databases/my_sql/clauses/order.py +0 -8
  100. ormlambda/databases/my_sql/clauses/update.py +0 -8
  101. ormlambda/databases/my_sql/clauses/upsert.py +0 -9
  102. ormlambda/databases/my_sql/clauses/where.py +0 -7
  103. ormlambda/dialects/interface/__init__.py +0 -1
  104. ormlambda/dialects/interface/dialect.py +0 -78
  105. ormlambda/sql/clause_info/aggregate_function_base.py +0 -96
  106. ormlambda/sql/clause_info/clause_info_context.py +0 -87
  107. ormlambda/sql/clauses/interfaces/ISelect.py +0 -17
  108. ormlambda/sql/clauses/new_join.py +0 -119
  109. ormlambda/util/load_module.py +0 -21
  110. ormlambda/util/plugin_loader.py +0 -32
  111. ormlambda-3.35.2.dist-info/RECORD +0 -159
  112. /ormlambda/{databases/my_sql → dialects/mysql}/caster/__init__.py +0 -0
  113. /ormlambda/{databases/my_sql → dialects/mysql}/caster/caster.py +0 -0
  114. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/__init__.py +0 -0
  115. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/boolean.py +0 -0
  116. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/bytes.py +0 -0
  117. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/date.py +0 -0
  118. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/datetime.py +0 -0
  119. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/decimal.py +0 -0
  120. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/float.py +0 -0
  121. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/int.py +0 -0
  122. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/iterable.py +0 -0
  123. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/json.py +0 -0
  124. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/none.py +0 -0
  125. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/point.py +0 -0
  126. /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/string.py +0 -0
  127. /ormlambda/{databases/my_sql → dialects/mysql/repository}/pool_types.py +0 -0
  128. {ormlambda-3.35.2.dist-info → ormlambda-4.0.0.dist-info}/AUTHORS +0 -0
  129. {ormlambda-3.35.2.dist-info → ormlambda-4.0.0.dist-info}/LICENSE +0 -0
  130. {ormlambda-3.35.2.dist-info → ormlambda-4.0.0.dist-info}/WHEEL +0 -0
@@ -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 visit_select(self, select: Select, **kw):
88
- return f"{select.CLAUSE} {select.COLUMNS} FROM {select.FROM.query(self.dialect, **kw)}"
89
-
90
- def visit_group_by(self, groupby: GroupBy, **kw):
91
- column = groupby._create_query(self.dialect, **kw)
92
- return f"{groupby.FUNCTION_NAME()} {column}"
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"{limit.LIMIT} {limit._number}"
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) -> Insert: ...
99
- def visit_delete(self, delete: Delete, **kw) -> Delete: ...
100
- def visit_upsert(self, upsert: Upsert, **kw) -> Upsert: ...
101
- def visit_update(self, update: Update, **kw) -> Update: ...
102
- def visit_offset(self, offset: Offset, **kw) -> Offset:
103
- return f"{offset.OFFSET} {offset._number}"
104
-
105
- def visit_count(self, count: Count, **kw) -> Count: ...
106
- def visit_where(self, where: Where, **kw) -> Where: ...
107
- def visit_having(self, having: Having, **kw) -> Having: ...
108
- def visit_order(self, order: Order, **kw) -> Order:
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.unresolved_column
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
- context = ClauseInfoContext(table_context=order._context._table_context, clause_context=None) if order._context else None
123
- for index, clause in enumerate(order._convert_into_clauseInfo(columns, context, dialect=self.dialect)):
124
- clause.alias_clause = None
125
- string_columns.append(f"{clause.query(self.dialect, **kw)} {str(order._order_type[index])}")
126
-
127
- return f"{order.FUNCTION_NAME()} {', '.join(string_columns)}"
128
-
129
- def visit_concat(self, concat: Concat, **kw) -> Concat: ...
130
- def visit_max(self, max: Max, **kw) -> Max: ...
131
- def visit_min(self, min: Min, **kw) -> Min: ...
132
- def visit_sum(self, sum: Sum, **kw) -> Sum: ...
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 typing import TYPE_CHECKING, Literal
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== 'append').compile(self.dialect).string
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'"
@@ -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 IStatements_two_generic
11
-
12
-
13
- # endregion
10
+ from ormlambda.statements.interfaces import IStatements
14
11
 
15
12
 
16
13
  class BaseModel[T]:
17
14
  """
18
- Class to select the correct BaseStatement class depends on the repository.
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
- # region Constructor
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
 
@@ -5,7 +5,7 @@ import abc
5
5
 
6
6
  if TYPE_CHECKING:
7
7
  from ormlambda import URL as _URL
8
- from ormlambda.dialects.interface.dialect import Dialect
8
+ from ormlambda.dialects import Dialect
9
9
 
10
10
 
11
11
  class BaseRepository[TPool](IRepositoryBase):
@@ -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
 
@@ -1,13 +1,13 @@
1
1
  from __future__ import annotations
2
2
  from typing import Type, Any, TYPE_CHECKING, Optional
3
- from ormlambda.dialects.interface.dialect import Dialect
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.common.abstract_classes.decomposition_query import ClauseInfo
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.all_clauses:
92
- if hasattr(col, "_alias_aggregate") or col.alias_clause is None or isinstance(col, Alias):
91
+ for col in self._select.columns:
92
+ if isinstance(col, Alias):
93
93
  continue
94
- replacer_dicc[col.alias_clause] = col.column
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
- clause_info = self._select[alias]
120
- parse_data = self._caster.for_value(data, value_type=clause_info.dtype).from_database
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 as Column
2
- from .foreign_key import ForeignKey as ForeignKey
3
- from .table import Table as 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
+ )
@@ -1,4 +1,2 @@
1
1
  from .interface import IAggregate # noqa: F401
2
2
  from .clause_info import ClauseInfo # noqa: F401
3
- from .aggregate_function_base import AggregateFunctionBase # noqa: F401
4
- from .clause_info_context import ClauseContextType, ClauseInfoContext # noqa: F401