ormlambda 3.7.1__py3-none-any.whl → 3.11.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 +2 -0
- ormlambda/caster/base_caster.py +3 -3
- ormlambda/common/global_checker.py +1 -1
- ormlambda/components/select/ISelect.py +4 -4
- ormlambda/components/select/__init__.py +1 -1
- ormlambda/databases/my_sql/caster/caster.py +1 -0
- ormlambda/databases/my_sql/caster/types/bytes.py +3 -3
- ormlambda/databases/my_sql/caster/types/datetime.py +3 -3
- ormlambda/databases/my_sql/caster/types/float.py +3 -3
- ormlambda/databases/my_sql/caster/types/int.py +3 -3
- ormlambda/databases/my_sql/caster/types/iterable.py +3 -3
- ormlambda/databases/my_sql/caster/types/none.py +3 -3
- ormlambda/databases/my_sql/caster/types/string.py +3 -3
- ormlambda/databases/my_sql/clauses/__init__.py +1 -0
- ormlambda/databases/my_sql/clauses/alias.py +15 -21
- ormlambda/databases/my_sql/clauses/group_by.py +19 -20
- ormlambda/databases/my_sql/clauses/having.py +16 -0
- ormlambda/databases/my_sql/clauses/order.py +6 -1
- ormlambda/databases/my_sql/clauses/update.py +1 -1
- ormlambda/databases/my_sql/clauses/where.py +3 -3
- ormlambda/databases/my_sql/functions/concat.py +8 -6
- ormlambda/databases/my_sql/join_context.py +3 -3
- ormlambda/databases/my_sql/repository/repository.py +60 -13
- ormlambda/databases/my_sql/statements.py +73 -22
- ormlambda/databases/my_sql/types.py +73 -0
- ormlambda/engine/__init__.py +2 -0
- ormlambda/engine/create.py +35 -0
- ormlambda/engine/url.py +744 -0
- ormlambda/engine/utils.py +17 -0
- ormlambda/repository/base_repository.py +2 -3
- ormlambda/repository/interfaces/IRepositoryBase.py +1 -0
- ormlambda/sql/column.py +27 -2
- ormlambda/sql/foreign_key.py +36 -4
- ormlambda/sql/table/table_constructor.py +2 -2
- ormlambda/statements/interfaces/IStatements.py +37 -25
- ormlambda/statements/types.py +4 -1
- {ormlambda-3.7.1.dist-info → ormlambda-3.11.0.dist-info}/METADATA +107 -8
- {ormlambda-3.7.1.dist-info → ormlambda-3.11.0.dist-info}/RECORD +40 -35
- {ormlambda-3.7.1.dist-info → ormlambda-3.11.0.dist-info}/LICENSE +0 -0
- {ormlambda-3.7.1.dist-info → ormlambda-3.11.0.dist-info}/WHEEL +0 -0
@@ -1,5 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import Concatenate, Iterable, override, Type, TYPE_CHECKING, Any, Callable, Optional
|
2
|
+
from typing import Concatenate, Iterable, TypedDict, override, Type, TYPE_CHECKING, Any, Callable, Optional
|
3
3
|
from mysql.connector import errors, errorcode
|
4
4
|
|
5
5
|
from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext
|
@@ -28,11 +28,12 @@ from .clauses import InsertQuery
|
|
28
28
|
from .clauses import Limit
|
29
29
|
from .clauses import Offset
|
30
30
|
from .clauses import Order
|
31
|
-
from .clauses
|
31
|
+
from .clauses import Select
|
32
32
|
|
33
33
|
from .clauses import UpsertQuery
|
34
34
|
from .clauses import UpdateQuery
|
35
35
|
from .clauses import Where
|
36
|
+
from .clauses import Having
|
36
37
|
from .clauses import Count
|
37
38
|
from .clauses import GroupBy
|
38
39
|
from .clauses import Alias
|
@@ -60,12 +61,23 @@ def clear_list[T, **P](f: Callable[Concatenate[MySQLStatements, P], T]) -> Calla
|
|
60
61
|
return wrapper
|
61
62
|
|
62
63
|
|
64
|
+
class OrderType(TypedDict):
|
65
|
+
Select: Select
|
66
|
+
JoinSelector: JoinSelector
|
67
|
+
Where: Where
|
68
|
+
Order: Order
|
69
|
+
GroupBy: GroupBy
|
70
|
+
Having: Having
|
71
|
+
Limit: Limit
|
72
|
+
Offset: Offset
|
73
|
+
|
74
|
+
|
63
75
|
class QueryBuilder(IQuery):
|
64
|
-
__order__: tuple[str, ...] = ("Select", "JoinSelector", "Where", "
|
76
|
+
__order__: tuple[str, ...] = ("Select", "JoinSelector", "Where", "GroupBy", "Having", "Order", "Limit", "Offset")
|
65
77
|
|
66
78
|
def __init__(self, by: JoinType = JoinType.INNER_JOIN):
|
67
79
|
self._context = ClauseInfoContext()
|
68
|
-
self._query_list:
|
80
|
+
self._query_list: OrderType = {}
|
69
81
|
self._by = by
|
70
82
|
|
71
83
|
self._joins: Optional[IQuery] = None
|
@@ -113,6 +125,15 @@ class QueryBuilder(IQuery):
|
|
113
125
|
def GROUP_BY(self) -> IQuery:
|
114
126
|
return self._query_list.get("GroupBy", None)
|
115
127
|
|
128
|
+
@property
|
129
|
+
def HAVING(self) -> IQuery:
|
130
|
+
where = self._query_list.get("Having", None)
|
131
|
+
if not isinstance(where, Iterable):
|
132
|
+
if not where:
|
133
|
+
return ()
|
134
|
+
return (where,)
|
135
|
+
return where
|
136
|
+
|
116
137
|
@property
|
117
138
|
def LIMIT(self) -> IQuery:
|
118
139
|
return self._query_list.get("Limit", None)
|
@@ -133,8 +154,9 @@ class QueryBuilder(IQuery):
|
|
133
154
|
self.SELECT.query,
|
134
155
|
JOINS,
|
135
156
|
Where.join_condition(self.WHERE, True, self._context) if self.WHERE else None,
|
136
|
-
self.ORDER.query if self.ORDER else None,
|
137
157
|
self.GROUP_BY.query if self.GROUP_BY else None,
|
158
|
+
Having.join_condition(self.HAVING, True, self._context) if self.HAVING else None,
|
159
|
+
self.ORDER.query if self.ORDER else None,
|
138
160
|
self.LIMIT.query if self.LIMIT else None,
|
139
161
|
self.OFFSET.query if self.OFFSET else None,
|
140
162
|
)
|
@@ -154,8 +176,8 @@ class QueryBuilder(IQuery):
|
|
154
176
|
joins = set()
|
155
177
|
# Always it's gonna be a set of two
|
156
178
|
# FIXME [x]: Resolved when we get Compare object instead ClauseInfo. For instance, when we have multiples condition using '&' or '|'
|
157
|
-
for
|
158
|
-
fk = ForeignKey.stored_calls.pop()
|
179
|
+
for fk in ForeignKey.stored_calls.copy():
|
180
|
+
fk = ForeignKey.stored_calls.pop(fk)
|
159
181
|
self._context._add_table_alias(fk.tright, fk.alias)
|
160
182
|
join = JoinSelector(fk.resolved_function(self._context), by, context=self._context, alias=fk.alias)
|
161
183
|
joins.add(join)
|
@@ -278,7 +300,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
278
300
|
|
279
301
|
if GlobalChecker.is_lambda_function(selection):
|
280
302
|
selection = selection(*self.models)
|
281
|
-
return Count
|
303
|
+
return Count(element=selection, alias_clause=alias_clause, context=self._query_builder._context)
|
282
304
|
|
283
305
|
@override
|
284
306
|
def where(self, conditions: WhereTypes) -> IStatements_two_generic[T, MySQLConnection]:
|
@@ -291,6 +313,15 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
291
313
|
self._query_builder.add_statement(Where(*conditions))
|
292
314
|
return self
|
293
315
|
|
316
|
+
@override
|
317
|
+
def having(self, conditions: WhereTypes) -> IStatements_two_generic[T, MySQLConnection]:
|
318
|
+
if GlobalChecker.is_lambda_function(conditions):
|
319
|
+
conditions = GlobalChecker.resolved_callback_object(conditions, self._models)
|
320
|
+
if not isinstance(conditions, Iterable):
|
321
|
+
conditions = (conditions,)
|
322
|
+
self._query_builder.add_statement(Having(*conditions))
|
323
|
+
return self
|
324
|
+
|
294
325
|
@override
|
295
326
|
def order[TValue](self, columns: Callable[[T], TValue], order_type: OrderTypes) -> IStatements_two_generic[T, MySQLConnection]:
|
296
327
|
query = GlobalChecker.resolved_callback_object(columns, self._models) if GlobalChecker.is_lambda_function(columns) else columns
|
@@ -299,23 +330,42 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
299
330
|
return self
|
300
331
|
|
301
332
|
@override
|
302
|
-
def concat
|
303
|
-
return func.Concat[T](
|
333
|
+
def concat(self, selector: str | Callable[[T], str], alias: str = "CONCAT") -> IAggregate:
|
334
|
+
return func.Concat[T](
|
335
|
+
values=selector,
|
336
|
+
alias_clause=alias,
|
337
|
+
context=self._query_builder._context,
|
338
|
+
)
|
304
339
|
|
305
340
|
@override
|
306
|
-
def max[TProp](
|
341
|
+
def max[TProp](
|
342
|
+
self,
|
343
|
+
column: Callable[[T], TProp],
|
344
|
+
alias: str = "max",
|
345
|
+
execute: bool = False,
|
346
|
+
) -> TProp:
|
307
347
|
if execute is True:
|
308
348
|
return self.select_one(self.max(column, alias, execute=False), flavour=dict)[alias]
|
309
349
|
return func.Max(elements=column, alias_clause=alias, context=self._query_builder._context)
|
310
350
|
|
311
351
|
@override
|
312
|
-
def min[TProp](
|
352
|
+
def min[TProp](
|
353
|
+
self,
|
354
|
+
column: Callable[[T], TProp],
|
355
|
+
alias: str = "min",
|
356
|
+
execute: bool = False,
|
357
|
+
) -> TProp:
|
313
358
|
if execute is True:
|
314
359
|
return self.select_one(self.min(column, alias, execute=False), flavour=dict)[alias]
|
315
360
|
return func.Min(elements=column, alias_clause=alias, context=self._query_builder._context)
|
316
361
|
|
317
362
|
@override
|
318
|
-
def sum[TProp](
|
363
|
+
def sum[TProp](
|
364
|
+
self,
|
365
|
+
column: Callable[[T], TProp],
|
366
|
+
alias: str = "sum",
|
367
|
+
execute: bool = False,
|
368
|
+
) -> TProp:
|
319
369
|
if execute is True:
|
320
370
|
return self.select_one(self.sum(column, alias, execute=False), flavour=dict)[alias]
|
321
371
|
return func.Sum(elements=column, alias_clause=alias, context=self._query_builder._context)
|
@@ -372,7 +422,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
372
422
|
):
|
373
423
|
self.limit(1)
|
374
424
|
|
375
|
-
response = self.select(selector=selector, flavour=flavour, by=by
|
425
|
+
response = self.select(selector=selector, flavour=flavour, by=by, **kwargs)
|
376
426
|
|
377
427
|
if not isinstance(response, Iterable):
|
378
428
|
return response
|
@@ -406,16 +456,17 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
406
456
|
)
|
407
457
|
|
408
458
|
@override
|
409
|
-
def
|
410
|
-
|
411
|
-
groupby = GroupBy[T, tuple[*Ts]](self._models, lambda x: column)
|
412
|
-
else:
|
413
|
-
groupby = GroupBy[T, tuple[*Ts]](self._models, column)
|
459
|
+
def groupby[TProp](self, column: ColumnType[TProp] | Callable[[T, *Ts], Any]):
|
460
|
+
groupby = GroupBy(column=column, context=self._query_builder._context)
|
414
461
|
# Only can be one LIMIT SQL parameter. We only use the last LimitQuery
|
415
462
|
self._query_builder.add_statement(groupby)
|
416
463
|
return self
|
417
464
|
|
418
465
|
@override
|
419
|
-
def alias[TProp](self, column: ColumnType[TProp], alias: str) ->
|
420
|
-
|
421
|
-
|
466
|
+
def alias[TProp](self, column: ColumnType[TProp], alias: str) -> ClauseInfo[T, TProp]:
|
467
|
+
return Alias(
|
468
|
+
table=column.table,
|
469
|
+
column=column,
|
470
|
+
alias_clause=alias,
|
471
|
+
context=self._query_builder._context,
|
472
|
+
)
|
@@ -0,0 +1,73 @@
|
|
1
|
+
from typing import Callable, Optional, Type, TypedDict, Any
|
2
|
+
|
3
|
+
from mysql.connector.constants import (
|
4
|
+
CharacterSet,
|
5
|
+
ClientFlag,
|
6
|
+
)
|
7
|
+
from mysql.connector.types import HandShakeType, BinaryProtocolType
|
8
|
+
from mysql.connector.conversion import MySQLConverter
|
9
|
+
|
10
|
+
|
11
|
+
class MySQLArgs(TypedDict):
|
12
|
+
client_flags: ClientFlag | int
|
13
|
+
sql_mode: Optional[str]
|
14
|
+
time_zone: Optional[str]
|
15
|
+
autocommit: bool
|
16
|
+
server_version: Optional[tuple[int, ...]]
|
17
|
+
handshake: Optional[HandShakeType]
|
18
|
+
conn_attrs: dict[str, str]
|
19
|
+
|
20
|
+
user: str
|
21
|
+
password: str
|
22
|
+
password1: str
|
23
|
+
password2: str
|
24
|
+
password3: str
|
25
|
+
database: str
|
26
|
+
host: str
|
27
|
+
port: int
|
28
|
+
unix_socket: Optional[str]
|
29
|
+
client_host: str
|
30
|
+
client_port: int
|
31
|
+
ssl: dict[str, Optional[str | bool | list[str]]]
|
32
|
+
ssl_disabled: bool
|
33
|
+
force_ipv6: bool
|
34
|
+
oci_config_file: Optional[str]
|
35
|
+
oci_config_profile: Optional[str]
|
36
|
+
webauthn_callback: Optional[str | Callable[[str], None]]
|
37
|
+
krb_service_principal: Optional[str]
|
38
|
+
openid_token_file: Optional[str]
|
39
|
+
|
40
|
+
use_unicode: bool
|
41
|
+
get_warnings: bool
|
42
|
+
raise_on_warnings: bool
|
43
|
+
connection_timeout: Optional[int]
|
44
|
+
read_timeout: Optional[int]
|
45
|
+
write_timeout: Optional[int]
|
46
|
+
buffered: bool
|
47
|
+
unread_result: bool
|
48
|
+
have_next_result: bool
|
49
|
+
raw: bool
|
50
|
+
in_transaction: bool
|
51
|
+
allow_local_infile: bool
|
52
|
+
allow_local_infile_in_path: Optional[str]
|
53
|
+
|
54
|
+
prepared_statements: Any
|
55
|
+
query_attrs: dict[str, BinaryProtocolType]
|
56
|
+
|
57
|
+
ssl_active: bool
|
58
|
+
auth_plugin: Optional[str]
|
59
|
+
auth_plugin_class: Optional[str]
|
60
|
+
pool_config_version: Any
|
61
|
+
converter_class: Optional[Type[MySQLConverter]]
|
62
|
+
converter_str_fallback: bool
|
63
|
+
compress: bool
|
64
|
+
|
65
|
+
consume_results: bool
|
66
|
+
init_command: Optional[str]
|
67
|
+
character_set: CharacterSet
|
68
|
+
|
69
|
+
|
70
|
+
__all__ = [
|
71
|
+
"MySQLArgs",
|
72
|
+
"ClientFlag",
|
73
|
+
]
|
ormlambda/engine/__init__.py
CHANGED
@@ -0,0 +1,35 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
from ormlambda.engine.url import URL, make_url
|
4
|
+
from ormlambda import BaseRepository
|
5
|
+
|
6
|
+
|
7
|
+
def create_engine(url: URL | str, **kwargs: Any) -> BaseRepository:
|
8
|
+
from ormlambda.databases import MySQLRepository
|
9
|
+
|
10
|
+
# create url.URL object
|
11
|
+
u = make_url(url)
|
12
|
+
url, kwargs = u._instantiate_plugins(kwargs)
|
13
|
+
|
14
|
+
repo_selector = {
|
15
|
+
"mysql": MySQLRepository,
|
16
|
+
}
|
17
|
+
|
18
|
+
if url.drivername not in repo_selector:
|
19
|
+
raise ValueError(f"drivername '{url.drivername}' not expected to load Repository class")
|
20
|
+
|
21
|
+
default_config = {
|
22
|
+
"user": url.username,
|
23
|
+
"password": url.password,
|
24
|
+
"host": url.host,
|
25
|
+
"database": url.database,
|
26
|
+
**kwargs,
|
27
|
+
}
|
28
|
+
|
29
|
+
if url.port:
|
30
|
+
try:
|
31
|
+
default_config["port"] = int(url.port)
|
32
|
+
except ValueError:
|
33
|
+
raise ValueError(f"The port must be an int. '{url.port}' passed.")
|
34
|
+
|
35
|
+
return repo_selector[url.drivername](**default_config)
|