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.
- 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 +32 -49
- 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 -86
- 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.2.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.2.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.2.dist-info → ormlambda-4.0.0.dist-info}/AUTHORS +0 -0
- {ormlambda-3.35.2.dist-info → ormlambda-4.0.0.dist-info}/LICENSE +0 -0
- {ormlambda-3.35.2.dist-info → ormlambda-4.0.0.dist-info}/WHEEL +0 -0
@@ -1,23 +1,20 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
+
from collections import defaultdict
|
2
3
|
import typing as tp
|
3
4
|
import re
|
4
5
|
|
5
|
-
from ormlambda import
|
6
|
-
from ormlambda import
|
7
|
-
from ormlambda.sql.types import (
|
8
|
-
ASTERISK,
|
9
|
-
TableType,
|
10
|
-
ColumnType,
|
11
|
-
AliasType,
|
12
|
-
TypeEngine,
|
13
|
-
)
|
6
|
+
from ormlambda.sql.types import ASTERISK
|
7
|
+
from ormlambda.errors import DuplicatedClauseName
|
14
8
|
from .interface import IClauseInfo
|
15
|
-
from ormlambda.
|
9
|
+
from ormlambda.common import GlobalChecker
|
10
|
+
from ormlambda import util
|
16
11
|
|
17
12
|
|
18
|
-
from .clause_info_context import ClauseInfoContext, ClauseContextType
|
19
|
-
|
20
13
|
if tp.TYPE_CHECKING:
|
14
|
+
from ormlambda.sql import ForeignKey
|
15
|
+
from ormlambda import ColumnProxy
|
16
|
+
from ormlambda import Table
|
17
|
+
from ormlambda.sql.types import TableType, ColumnType, AliasType
|
21
18
|
from ormlambda.dialects import Dialect
|
22
19
|
|
23
20
|
|
@@ -39,11 +36,9 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
39
36
|
@tp.overload
|
40
37
|
def __init__[TProp](self, table: TableType[T], column: ColumnType[TProp]): ...
|
41
38
|
@tp.overload
|
42
|
-
def __init__[TProp](self, table: TableType[T], column: ColumnType[TProp], alias_table: AliasType[
|
43
|
-
@tp.overload
|
44
|
-
def __init__(self, table: TableType[T], alias_table: AliasType[ClauseInfo[T]] = ..., alias_clause: AliasType[ClauseInfo[T]] = ...): ...
|
39
|
+
def __init__[TProp](self, table: TableType[T], column: ColumnType[TProp], alias_table: AliasType[ColumnProxy] = ..., alias_clause: AliasType[ColumnProxy] = ...): ...
|
45
40
|
@tp.overload
|
46
|
-
def __init__
|
41
|
+
def __init__(self, table: TableType[T], alias_table: AliasType[ColumnProxy] = ..., alias_clause: AliasType[ColumnProxy] = ...): ...
|
47
42
|
@tp.overload
|
48
43
|
def __init__(self, table: TableType[T], keep_asterisk: tp.Optional[bool] = ...): ...
|
49
44
|
@tp.overload
|
@@ -58,12 +53,12 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
58
53
|
self,
|
59
54
|
table: TableType[T],
|
60
55
|
column: tp.Optional[ColumnType[TProp]] = None,
|
61
|
-
alias_table: tp.Optional[AliasType[
|
62
|
-
alias_clause: tp.Optional[AliasType[
|
63
|
-
context: ClauseContextType = None,
|
56
|
+
alias_table: tp.Optional[AliasType[ColumnProxy]] = None,
|
57
|
+
alias_clause: tp.Optional[AliasType[ColumnProxy]] = None,
|
64
58
|
keep_asterisk: bool = False,
|
65
59
|
preserve_context: bool = False,
|
66
60
|
dtype: tp.Optional[TProp] = None,
|
61
|
+
literal: bool = False,
|
67
62
|
*,
|
68
63
|
dialect: Dialect,
|
69
64
|
**kw,
|
@@ -74,12 +69,12 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
74
69
|
|
75
70
|
self._table: TableType[T] = table
|
76
71
|
self._column: TableType[T] | ColumnType[TProp] = column
|
77
|
-
self._alias_table: tp.Optional[AliasType[
|
78
|
-
self._alias_clause: tp.Optional[AliasType[
|
79
|
-
self._context: ClauseContextType = context if context else ClauseInfoContext()
|
72
|
+
self._alias_table: tp.Optional[AliasType[ColumnProxy]] = alias_table
|
73
|
+
self._alias_clause: tp.Optional[AliasType[ColumnProxy]] = alias_clause
|
80
74
|
self._keep_asterisk: bool = keep_asterisk
|
81
75
|
self._preserve_context: bool = preserve_context
|
82
76
|
self._dtype = dtype
|
77
|
+
self._literal = literal
|
83
78
|
|
84
79
|
self._dialect: Dialect = dialect
|
85
80
|
|
@@ -88,9 +83,6 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
88
83
|
"table": self.replace_table_placeholder,
|
89
84
|
}
|
90
85
|
|
91
|
-
if not self._preserve_context and (self._context and any([alias_table, alias_clause])):
|
92
|
-
self._context.add_clause_to_context(self)
|
93
|
-
|
94
86
|
super().__init__(**kw)
|
95
87
|
|
96
88
|
def __repr__(self) -> str:
|
@@ -118,7 +110,7 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
118
110
|
|
119
111
|
@property
|
120
112
|
def alias_clause(self) -> tp.Optional[str]:
|
121
|
-
alias = self._alias_clause
|
113
|
+
alias = self._alias_clause
|
122
114
|
return self._alias_resolver(alias)
|
123
115
|
|
124
116
|
# TODOL [ ]: if we using this setter, we don't update the _context with the new value. Study if it's necessary
|
@@ -128,7 +120,7 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
128
120
|
|
129
121
|
@property
|
130
122
|
def alias_table(self) -> tp.Optional[str]:
|
131
|
-
alias = self._alias_table
|
123
|
+
alias = self._alias_table
|
132
124
|
return self._alias_resolver(alias)
|
133
125
|
|
134
126
|
# TODOL [ ]: if we using this setter, we don't update the _context with the new value. Study if it's necessary
|
@@ -147,20 +139,12 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
147
139
|
def unresolved_column(self) -> ColumnType:
|
148
140
|
return self._column
|
149
141
|
|
150
|
-
@property
|
151
|
-
def context(self) -> ClauseContextType:
|
152
|
-
return self._context
|
153
|
-
|
154
|
-
@context.setter
|
155
|
-
def context(self, value: ClauseInfoContext) -> None:
|
156
|
-
self._context = value
|
157
|
-
|
158
142
|
@property
|
159
143
|
def dtype[TProp](self) -> tp.Optional[tp.Type[TProp]]:
|
160
144
|
if self._dtype is not None:
|
161
145
|
return self._dtype
|
162
146
|
|
163
|
-
if
|
147
|
+
if self.is_column(self._column):
|
164
148
|
return self._column.dtype
|
165
149
|
|
166
150
|
if isinstance(self._column, type):
|
@@ -182,6 +166,9 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
182
166
|
def query(self, dialect: Dialect, **kwargs) -> str:
|
183
167
|
return self._create_query(dialect, **kwargs)
|
184
168
|
|
169
|
+
def compile(self, dialect: Dialect, **kwargs) -> str:
|
170
|
+
return self._create_query(dialect, **kwargs)
|
171
|
+
|
185
172
|
def _create_query(self, dialect: Dialect, **kwargs) -> str:
|
186
173
|
# when passing some value that is not a column name
|
187
174
|
if not self.table and not self._alias_clause:
|
@@ -190,26 +177,29 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
190
177
|
if not self.table and self._alias_clause:
|
191
178
|
# it means that we are passing an object with alias. We should delete '' around the object
|
192
179
|
alias_clause = self.alias_clause
|
193
|
-
return self.
|
180
|
+
return self.concat_alias_and_column(self._column, alias_clause)
|
194
181
|
|
195
182
|
# When passing the Table itself without 'column'
|
196
183
|
if self.table and not self._column:
|
197
184
|
if not self._alias_table:
|
198
185
|
return self.table.__table_name__
|
199
186
|
alias_table = self.alias_table
|
200
|
-
return self.
|
187
|
+
return self.concat_alias_and_column(self.table.__table_name__, alias_table)
|
201
188
|
|
202
189
|
if self._return_all_columns():
|
203
|
-
return self._get_all_columns()
|
204
|
-
return self._join_table_and_column(self._column)
|
190
|
+
return self._get_all_columns(dialect)
|
191
|
+
return self._join_table_and_column(self._column, dialect)
|
205
192
|
|
206
|
-
|
207
|
-
|
193
|
+
@util.preload_module("ormlambda.sql")
|
194
|
+
def _join_table_and_column[TProp](self, column: ColumnType[TProp], dialect: Dialect) -> str:
|
195
|
+
ColumnProxy = util.preloaded.sql_column.ColumnProxy
|
208
196
|
|
209
|
-
caster =
|
197
|
+
caster = dialect.caster()
|
210
198
|
|
211
199
|
if self.alias_table:
|
212
|
-
table = self.
|
200
|
+
table = self.wrapped_with_quotes(self.alias_table)
|
201
|
+
elif isinstance(column, ColumnProxy):
|
202
|
+
table = self.wrapped_with_quotes(column.get_table_chain())
|
213
203
|
else:
|
214
204
|
table = self.table.__table_name__
|
215
205
|
|
@@ -219,7 +209,7 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
219
209
|
|
220
210
|
dtype = str if self.is_table(self.dtype) else self.dtype
|
221
211
|
wrapped_column = caster.for_value(table_column, dtype).wildcard_to_select(table_column)
|
222
|
-
return self.
|
212
|
+
return self.concat_alias_and_column(wrapped_column, self.alias_clause)
|
223
213
|
|
224
214
|
def _return_all_columns(self) -> bool:
|
225
215
|
if self._keep_asterisk:
|
@@ -234,22 +224,21 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
234
224
|
def is_asterisk(value: tp.Optional[str]) -> bool:
|
235
225
|
return isinstance(value, str) and value == ASTERISK
|
236
226
|
|
237
|
-
def _get_all_columns(self) -> str:
|
227
|
+
def _get_all_columns(self, dialect: Dialect) -> str:
|
238
228
|
def ClauseCreator(column: str) -> ClauseInfo:
|
239
229
|
return ClauseInfo(
|
240
230
|
table=self.table,
|
241
231
|
column=column,
|
242
232
|
alias_table=self._alias_table,
|
243
233
|
alias_clause=self._alias_clause,
|
244
|
-
context=self._context,
|
245
234
|
keep_asterisk=self._keep_asterisk,
|
246
|
-
dialect=
|
235
|
+
dialect=dialect,
|
247
236
|
)
|
248
237
|
|
249
238
|
if self._alias_table and self._alias_clause: # We'll add an "*" when we are certain that we have included 'alias_clause' attr
|
250
|
-
return self._join_table_and_column(ASTERISK)
|
239
|
+
return self._join_table_and_column(ASTERISK, dialect)
|
251
240
|
|
252
|
-
columns: list[ClauseInfo] = [ClauseCreator(column).query(
|
241
|
+
columns: list[ClauseInfo] = [ClauseCreator(column).query(dialect) for column in self.table.get_columns()]
|
253
242
|
|
254
243
|
return ", ".join(columns)
|
255
244
|
|
@@ -261,7 +250,7 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
261
250
|
if isinstance(column, tp.Iterable) and isinstance(column[0], ClauseInfo):
|
262
251
|
return self.join_clauses(column)
|
263
252
|
|
264
|
-
if
|
253
|
+
if self.is_column(column):
|
265
254
|
return column.column_name
|
266
255
|
|
267
256
|
# if we want to pass the name of a column as a string, the 'table' var must not be None
|
@@ -281,6 +270,10 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
281
270
|
casted_value = caster.for_value(column, self.dtype)
|
282
271
|
if not self._table:
|
283
272
|
# if we haven't some table atrribute, we assume that the user want to retrieve the string_data from caster.
|
273
|
+
if self._literal:
|
274
|
+
# That condition will be used when you need to pass a value without wrapped in any quote like using HAVING clause
|
275
|
+
# COMMENT: Check 'test_complex_1' test
|
276
|
+
return casted_value.value
|
284
277
|
return casted_value.string_data
|
285
278
|
return casted_value.wildcard_to_select()
|
286
279
|
|
@@ -295,48 +288,63 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
295
288
|
|
296
289
|
return func(self._column)
|
297
290
|
|
298
|
-
|
291
|
+
@classmethod
|
292
|
+
def concat_alias_and_column(cls, column: str, alias_clause: tp.Optional[str] = None) -> str:
|
299
293
|
if alias_clause is None:
|
300
294
|
return column
|
301
|
-
alias = f"{column} AS {
|
295
|
+
alias = f"{column} AS {cls.wrapped_with_quotes(alias_clause)}"
|
302
296
|
return alias
|
303
297
|
|
304
|
-
def _alias_resolver(self, alias: AliasType[
|
298
|
+
def _alias_resolver(self, alias: AliasType[ColumnProxy]) -> tp.Optional[str]:
|
305
299
|
if alias is None:
|
306
300
|
return None
|
307
301
|
|
308
302
|
if callable(alias):
|
309
|
-
return self._alias_resolver(alias(self))
|
303
|
+
return self._alias_resolver(alias(self._column))
|
310
304
|
|
311
305
|
return self._replace_placeholder(alias)
|
312
306
|
|
313
|
-
def get_clause_alias(self) -> tp.Optional[str]:
|
314
|
-
if not self._context:
|
315
|
-
return None
|
316
|
-
return self._context.get_clause_alias(self)
|
317
|
-
|
318
|
-
def get_table_alias(self) -> tp.Optional[str]:
|
319
|
-
if not self._context:
|
320
|
-
return None
|
321
|
-
return self._context.get_table_alias(self.table)
|
322
|
-
|
323
307
|
@staticmethod
|
324
|
-
def join_clauses(clauses: list[
|
308
|
+
def join_clauses(clauses: list[ColumnProxy], chr: str = ",", *, dialect: Dialect, **kw) -> str:
|
309
|
+
raise_alias_duplicated = False
|
310
|
+
all_aliases: dict[str, int] = defaultdict(int)
|
325
311
|
queries: list[str] = []
|
326
312
|
for c in clauses:
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
313
|
+
# That update control the alias we set by default on select clause
|
314
|
+
if "alias_clause" in kw:
|
315
|
+
alias_clause = kw["alias_clause"]
|
316
|
+
elif c.alias:
|
317
|
+
alias_clause = c.alias
|
318
|
+
else:
|
319
|
+
alias_clause = c.column_name
|
331
320
|
|
321
|
+
param = {**kw, "alias_clause": alias_clause}
|
322
|
+
compiled = c.compile(dialect, **param)
|
323
|
+
|
324
|
+
# FIXME [ ]: we use c.alias because we're modifying dynamically when compile the object insdie 'visit_column_proxy' method
|
325
|
+
# it's working right though it's not the way to do it.
|
326
|
+
NEW_ALIAS = c.alias
|
327
|
+
all_aliases[NEW_ALIAS] += 1
|
328
|
+
|
329
|
+
if all_aliases[NEW_ALIAS] > 1:
|
330
|
+
raise_alias_duplicated = True
|
331
|
+
|
332
|
+
queries.append(compiled.string)
|
333
|
+
|
334
|
+
if raise_alias_duplicated:
|
335
|
+
raise DuplicatedClauseName(tuple(alias for alias, number in all_aliases.items() if number > 1))
|
332
336
|
return f"{chr} ".join(queries)
|
333
337
|
|
334
338
|
@staticmethod
|
335
|
-
def
|
336
|
-
|
339
|
+
def wrapped_with_quotes(
|
340
|
+
string: str,
|
341
|
+
first: str = GlobalChecker.FIRST_QUOTE,
|
342
|
+
end: str = GlobalChecker.END_QUOTE,
|
343
|
+
) -> str:
|
344
|
+
return f"{first}{string}{end}"
|
337
345
|
|
338
346
|
@classmethod
|
339
|
-
def extract_table(cls, element: ColumnType[T] | TableType[T]) -> tp.Optional[T]:
|
347
|
+
def extract_table(cls, element: ForeignKey | ColumnType[T] | TableType[T]) -> tp.Optional[T]:
|
340
348
|
if element is None:
|
341
349
|
return None
|
342
350
|
|
@@ -346,22 +354,32 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
|
|
346
354
|
if cls.is_foreign_key(element):
|
347
355
|
return element.tright
|
348
356
|
|
349
|
-
if
|
357
|
+
if cls.is_column(element):
|
350
358
|
return element.table
|
351
359
|
return None
|
352
360
|
|
361
|
+
@util.preload_module("ormlambda.sql")
|
353
362
|
@staticmethod
|
354
363
|
def is_table(data: ColumnType | Table | ForeignKey) -> bool:
|
355
|
-
|
364
|
+
Table = util.preloaded.sql_table.Table
|
365
|
+
TableProxy = util.preloaded.sql_table.TableProxy
|
366
|
+
|
367
|
+
return isinstance(data, type) and issubclass(data, Table | TableProxy)
|
356
368
|
|
369
|
+
@util.preload_module("ormlambda.sql")
|
357
370
|
@staticmethod
|
358
371
|
def is_foreign_key(data: ColumnType | Table | ForeignKey) -> bool:
|
372
|
+
ForeignKey = util.preloaded.sql_foreign_key.ForeignKey
|
359
373
|
return isinstance(data, ForeignKey)
|
360
374
|
|
375
|
+
@util.preload_module("ormlambda.sql")
|
361
376
|
@classmethod
|
362
377
|
def is_column(cls, data: tp.Any) -> bool:
|
378
|
+
Column = util.preloaded.sql_column.Column
|
379
|
+
ColumnProxy = util.preloaded.sql_column.ColumnProxy
|
380
|
+
|
363
381
|
if cls.is_table(data) or cls.is_foreign_key(data) or cls.is_asterisk(data):
|
364
382
|
return False
|
365
|
-
if isinstance(data, Column):
|
383
|
+
if isinstance(data, Column | ColumnProxy):
|
366
384
|
return True
|
367
385
|
return False
|
@@ -1,10 +1,20 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
import abc
|
3
|
+
from typing import Any, Iterable, TYPE_CHECKING
|
3
4
|
|
4
|
-
|
5
|
+
if TYPE_CHECKING:
|
6
|
+
from ormlambda import ColumnProxy
|
5
7
|
|
6
8
|
|
7
|
-
class IAggregate(
|
8
|
-
|
9
|
+
class IAggregate():
|
10
|
+
alias: str
|
11
|
+
|
12
|
+
def __repr__(self):
|
13
|
+
return f"{IAggregate.__name__}: {type(self).__name__}"
|
14
|
+
|
15
|
+
@abc.abstractmethod
|
16
|
+
def used_columns(self) -> Iterable[ColumnProxy]: ...
|
17
|
+
|
18
|
+
@property
|
9
19
|
@abc.abstractmethod
|
10
|
-
def
|
20
|
+
def dtype(self) -> Any: ...
|
@@ -1,16 +1,14 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
import abc
|
3
|
-
|
4
|
-
|
5
|
-
from ormlambda import Table
|
3
|
+
from typing import Optional, Type, TYPE_CHECKING
|
6
4
|
from ormlambda.common.interfaces.IQueryCommand import IQuery
|
7
5
|
from ormlambda.sql.types import (
|
8
6
|
TableType,
|
9
7
|
ColumnType,
|
10
8
|
)
|
11
9
|
|
12
|
-
|
13
|
-
from
|
10
|
+
if TYPE_CHECKING:
|
11
|
+
from ormlambda import Table
|
14
12
|
|
15
13
|
|
16
14
|
class IClauseInfo[T: Table](IQuery):
|
@@ -19,10 +17,10 @@ class IClauseInfo[T: Table](IQuery):
|
|
19
17
|
def table(self) -> TableType[T]: ...
|
20
18
|
@property
|
21
19
|
@abc.abstractmethod
|
22
|
-
def alias_clause(self) ->
|
20
|
+
def alias_clause(self) -> Optional[str]: ...
|
23
21
|
@property
|
24
22
|
@abc.abstractmethod
|
25
|
-
def alias_table(self) ->
|
23
|
+
def alias_table(self) -> Optional[str]: ...
|
26
24
|
@property
|
27
25
|
@abc.abstractmethod
|
28
26
|
def column(self) -> str: ...
|
@@ -31,7 +29,4 @@ class IClauseInfo[T: Table](IQuery):
|
|
31
29
|
def unresolved_column(self) -> ColumnType: ...
|
32
30
|
@property
|
33
31
|
@abc.abstractmethod
|
34
|
-
def
|
35
|
-
@property
|
36
|
-
@abc.abstractmethod
|
37
|
-
def dtype[TProp](self) -> tp.Optional[tp.Type[TProp]]: ...
|
32
|
+
def dtype[TProp](self) -> Optional[Type[TProp]]: ...
|
ormlambda/sql/clauses/alias.py
CHANGED
@@ -1,45 +1,14 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
import typing as tp
|
3
2
|
|
4
|
-
from ormlambda
|
5
|
-
from ormlambda.sql.elements import ClauseElement
|
3
|
+
from ormlambda import ColumnProxy
|
6
4
|
|
7
5
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
from ormlambda.sql.types import TableType
|
12
|
-
from ormlambda.sql.types import ColumnType
|
13
|
-
from ormlambda.sql.types import AliasType
|
6
|
+
class Alias[T](ColumnProxy[T]):
|
7
|
+
def __init__(self, column: ColumnProxy[T], alias: str):
|
8
|
+
super().__init__(column._column, path=column.path, alias=alias)
|
14
9
|
|
15
|
-
|
16
|
-
|
17
|
-
__visit_name__ = "alias"
|
18
|
-
|
19
|
-
def __init__[TProp](
|
20
|
-
self,
|
21
|
-
table: TableType[T],
|
22
|
-
column: tp.Optional[ColumnType[TProp]] = None,
|
23
|
-
alias_table: tp.Optional[AliasType[ClauseInfo[T]]] = None,
|
24
|
-
alias_clause: tp.Optional[AliasType[ClauseInfo[T]]] = None,
|
25
|
-
context: ClauseContextType = None,
|
26
|
-
keep_asterisk: bool = False,
|
27
|
-
preserve_context: bool = False,
|
28
|
-
**kw,
|
29
|
-
):
|
30
|
-
if not alias_clause:
|
31
|
-
raise TypeError
|
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
|
-
)
|
10
|
+
def __repr__(self):
|
11
|
+
return f"{Alias.__name__}: {super().__repr__()}"
|
43
12
|
|
44
13
|
|
45
14
|
__all__ = ["Alias"]
|
ormlambda/sql/clauses/count.py
CHANGED
@@ -1,57 +1,42 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from ormlambda.sql.clause_info import
|
3
|
-
from ormlambda.sql.clause_info.clause_info_context import ClauseContextType
|
2
|
+
from ormlambda.sql.clause_info import IAggregate
|
4
3
|
|
5
|
-
from ormlambda.sql.types import
|
4
|
+
from ormlambda.sql.types import ColumnType
|
6
5
|
|
7
6
|
from ormlambda import Table
|
8
7
|
|
9
8
|
import typing as tp
|
10
9
|
|
11
|
-
from ormlambda.sql.types import ASTERISK
|
12
10
|
from ormlambda.sql.elements import ClauseElement
|
13
11
|
|
14
12
|
|
15
13
|
if tp.TYPE_CHECKING:
|
16
14
|
from ormlambda import Table
|
17
|
-
from ormlambda.sql.types import ColumnType,
|
18
|
-
from ormlambda.dialects import Dialect
|
15
|
+
from ormlambda.sql.types import ColumnType, TableType
|
19
16
|
|
20
17
|
|
21
|
-
class Count[T: Table](
|
18
|
+
class Count[T: Table](ClauseElement, IAggregate):
|
22
19
|
__visit_name__ = "count"
|
23
20
|
|
24
|
-
|
25
|
-
def FUNCTION_NAME() -> str:
|
26
|
-
return "COUNT"
|
27
|
-
|
28
|
-
def __init__[TProp: Table](
|
21
|
+
def __init__[TProp](
|
29
22
|
self,
|
30
|
-
element: ColumnType[
|
31
|
-
|
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,
|
23
|
+
element: str | ColumnType[TProp] | TableType[T] = "*",
|
24
|
+
alias: str = "count",
|
39
25
|
) -> None:
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
)
|
26
|
+
self.column = element
|
27
|
+
self.alias = alias
|
28
|
+
|
29
|
+
@property
|
30
|
+
def dtype(self) -> str:
|
31
|
+
return int
|
32
|
+
|
33
|
+
def used_columns(self) -> tp.Iterable:
|
34
|
+
if isinstance(self.column, str):
|
35
|
+
# If is str, probably will by because we're using * so we don't have to retrieve something
|
36
|
+
return []
|
37
|
+
if not isinstance(self.column, tp.Iterable):
|
38
|
+
return [self.column]
|
39
|
+
return self.column
|
55
40
|
|
56
41
|
|
57
42
|
__all__ = ["Count"]
|
@@ -1,30 +1,24 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import
|
2
|
+
from typing import Any, Iterable
|
3
|
+
|
4
|
+
from ormlambda.sql.clause_info import IAggregate
|
3
5
|
|
4
|
-
if TYPE_CHECKING:
|
5
|
-
from ormlambda.dialects import Dialect
|
6
|
-
from ormlambda.sql.clause_info import AggregateFunctionBase, ClauseInfoContext
|
7
6
|
from ormlambda.sql.types import ColumnType
|
8
7
|
from ormlambda.sql.elements import ClauseElement
|
9
8
|
|
10
9
|
|
11
|
-
class GroupBy(
|
10
|
+
class GroupBy(ClauseElement, IAggregate):
|
12
11
|
__visit_name__ = "group_by"
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
alias_clause=None,
|
24
|
-
context=context,
|
25
|
-
dialect=dialect,
|
26
|
-
**kwargs,
|
27
|
-
)
|
13
|
+
def __init__(self, *column: ColumnType):
|
14
|
+
self.column: tuple[ColumnType] = column if isinstance(column, Iterable) else [column]
|
15
|
+
|
16
|
+
def used_columns(self):
|
17
|
+
return self.column
|
18
|
+
|
19
|
+
@property
|
20
|
+
def dtype(self) -> Any:
|
21
|
+
return ...
|
28
22
|
|
29
23
|
|
30
24
|
__all__ = ["GroupBy"]
|
ormlambda/sql/clauses/having.py
CHANGED
@@ -10,12 +10,8 @@ class Having(Where):
|
|
10
10
|
|
11
11
|
__visit_name__ = "having"
|
12
12
|
|
13
|
-
def __init__(self, *comparer, restrictive=True,
|
14
|
-
super().__init__(*comparer, restrictive=restrictive,
|
15
|
-
|
16
|
-
@staticmethod
|
17
|
-
def FUNCTION_NAME() -> str:
|
18
|
-
return "HAVING"
|
13
|
+
def __init__(self, *comparer, restrictive=True, **kw):
|
14
|
+
super().__init__(*comparer, restrictive=restrictive, **kw)
|
19
15
|
|
20
16
|
|
21
17
|
__all__ = ["Having"]
|
ormlambda/sql/clauses/insert.py
CHANGED
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
3
3
|
from typing import override, Iterable, TYPE_CHECKING
|
4
4
|
|
5
5
|
from ormlambda import Table
|
6
|
-
from ormlambda import Column
|
6
|
+
from ormlambda import Column, ColumnProxy
|
7
7
|
|
8
8
|
from ormlambda.sql.clauses.interfaces import IInsert
|
9
9
|
from ormlambda.common.abstract_classes import NonQueryBase
|
@@ -53,7 +53,7 @@ class Insert[T: Table, TRepo](NonQueryBase[T, TRepo], IInsert[T], ClauseElement)
|
|
53
53
|
col_values[-1].append(clean_data.to_database)
|
54
54
|
|
55
55
|
join_cols = ", ".join(col_names)
|
56
|
-
unknown_rows = f
|
56
|
+
unknown_rows = f"({', '.join(wildcards)})" # The number of "%s" must match the dict 'dicc_0' length
|
57
57
|
|
58
58
|
self._values = [tuple(x) for x in col_values]
|
59
59
|
self._query = f"{self.CLAUSE} {self._model.__table_name__} {f'({join_cols})'} VALUES {unknown_rows}"
|
@@ -88,7 +88,7 @@ class Insert[T: Table, TRepo](NonQueryBase[T, TRepo], IInsert[T], ClauseElement)
|
|
88
88
|
elif issubclass(values.__class__, Table):
|
89
89
|
new_list = []
|
90
90
|
for prop in type(values).__dict__.values():
|
91
|
-
if not isinstance(prop, Column):
|
91
|
+
if not isinstance(prop, Column | ColumnProxy):
|
92
92
|
continue
|
93
93
|
|
94
94
|
value = getattr(values, prop.column_name)
|