ormlambda 3.12.2__py3-none-any.whl → 3.34.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ormlambda/__init__.py +2 -0
- ormlambda/caster/__init__.py +1 -1
- ormlambda/caster/caster.py +29 -12
- ormlambda/common/abstract_classes/clause_info_converter.py +4 -12
- ormlambda/common/abstract_classes/decomposition_query.py +17 -2
- ormlambda/common/abstract_classes/non_query_base.py +9 -7
- ormlambda/common/abstract_classes/query_base.py +3 -1
- ormlambda/common/errors/__init__.py +29 -0
- ormlambda/common/interfaces/IQueryCommand.py +6 -2
- ormlambda/databases/__init__.py +0 -1
- ormlambda/databases/my_sql/__init__.py +0 -1
- ormlambda/databases/my_sql/caster/caster.py +23 -19
- ormlambda/databases/my_sql/caster/types/__init__.py +3 -0
- ormlambda/databases/my_sql/caster/types/boolean.py +35 -0
- ormlambda/databases/my_sql/caster/types/bytes.py +7 -7
- ormlambda/databases/my_sql/caster/types/date.py +34 -0
- ormlambda/databases/my_sql/caster/types/datetime.py +7 -7
- ormlambda/databases/my_sql/caster/types/decimal.py +32 -0
- ormlambda/databases/my_sql/caster/types/float.py +7 -7
- ormlambda/databases/my_sql/caster/types/int.py +7 -7
- ormlambda/databases/my_sql/caster/types/iterable.py +7 -7
- ormlambda/databases/my_sql/caster/types/none.py +8 -7
- ormlambda/databases/my_sql/caster/types/point.py +4 -4
- ormlambda/databases/my_sql/caster/types/string.py +7 -7
- ormlambda/databases/my_sql/clauses/ST_AsText.py +8 -7
- ormlambda/databases/my_sql/clauses/ST_Contains.py +10 -5
- ormlambda/databases/my_sql/clauses/__init__.py +4 -10
- ormlambda/databases/my_sql/clauses/count.py +5 -15
- ormlambda/databases/my_sql/clauses/delete.py +3 -50
- ormlambda/databases/my_sql/clauses/group_by.py +3 -16
- ormlambda/databases/my_sql/clauses/having.py +2 -6
- ormlambda/databases/my_sql/clauses/insert.py +4 -92
- ormlambda/databases/my_sql/clauses/joins.py +5 -140
- ormlambda/databases/my_sql/clauses/limit.py +4 -15
- ormlambda/databases/my_sql/clauses/offset.py +4 -15
- ormlambda/databases/my_sql/clauses/order.py +4 -61
- ormlambda/databases/my_sql/clauses/update.py +4 -67
- ormlambda/databases/my_sql/clauses/upsert.py +3 -66
- ormlambda/databases/my_sql/clauses/where.py +4 -42
- ormlambda/databases/my_sql/repository.py +217 -0
- ormlambda/dialects/__init__.py +39 -0
- ormlambda/dialects/default/__init__.py +1 -0
- ormlambda/dialects/default/base.py +39 -0
- ormlambda/dialects/interface/__init__.py +1 -0
- ormlambda/dialects/interface/dialect.py +78 -0
- ormlambda/dialects/mysql/__init__.py +8 -0
- ormlambda/dialects/mysql/base.py +387 -0
- ormlambda/dialects/mysql/mysqlconnector.py +46 -0
- ormlambda/dialects/mysql/types.py +732 -0
- ormlambda/dialects/sqlite/__init__.py +5 -0
- ormlambda/dialects/sqlite/base.py +47 -0
- ormlambda/dialects/sqlite/pysqlite.py +32 -0
- ormlambda/engine/__init__.py +1 -0
- ormlambda/engine/base.py +58 -0
- ormlambda/engine/create.py +9 -23
- ormlambda/engine/url.py +31 -19
- ormlambda/env.py +30 -0
- ormlambda/errors.py +17 -0
- ormlambda/model/base_model.py +7 -9
- ormlambda/repository/base_repository.py +36 -5
- ormlambda/repository/interfaces/IRepositoryBase.py +121 -7
- ormlambda/repository/response.py +134 -0
- ormlambda/sql/clause_info/aggregate_function_base.py +19 -9
- ormlambda/sql/clause_info/clause_info.py +34 -17
- ormlambda/sql/clauses/__init__.py +14 -0
- ormlambda/{databases/my_sql → sql}/clauses/alias.py +23 -6
- ormlambda/sql/clauses/count.py +57 -0
- ormlambda/sql/clauses/delete.py +71 -0
- ormlambda/sql/clauses/group_by.py +30 -0
- ormlambda/sql/clauses/having.py +21 -0
- ormlambda/sql/clauses/insert.py +104 -0
- ormlambda/sql/clauses/interfaces/__init__.py +5 -0
- ormlambda/{components → sql/clauses}/join/join_context.py +15 -7
- ormlambda/sql/clauses/joins.py +159 -0
- ormlambda/sql/clauses/limit.py +15 -0
- ormlambda/sql/clauses/offset.py +15 -0
- ormlambda/sql/clauses/order.py +55 -0
- ormlambda/{databases/my_sql → sql}/clauses/select.py +12 -13
- ormlambda/sql/clauses/update.py +84 -0
- ormlambda/sql/clauses/upsert.py +77 -0
- ormlambda/sql/clauses/where.py +65 -0
- ormlambda/sql/column/__init__.py +1 -0
- ormlambda/sql/{column.py → column/column.py} +82 -22
- ormlambda/sql/comparer.py +51 -37
- ormlambda/sql/compiler.py +427 -0
- ormlambda/sql/ddl.py +68 -0
- ormlambda/sql/elements.py +36 -0
- ormlambda/sql/foreign_key.py +43 -39
- ormlambda/{databases/my_sql → sql}/functions/concat.py +13 -5
- ormlambda/{databases/my_sql → sql}/functions/max.py +9 -4
- ormlambda/{databases/my_sql → sql}/functions/min.py +9 -13
- ormlambda/{databases/my_sql → sql}/functions/sum.py +8 -10
- ormlambda/sql/sqltypes.py +647 -0
- ormlambda/sql/table/__init__.py +1 -1
- ormlambda/sql/table/table.py +179 -0
- ormlambda/sql/table/table_constructor.py +1 -208
- ormlambda/sql/type_api.py +35 -0
- ormlambda/sql/types.py +3 -1
- ormlambda/sql/visitors.py +74 -0
- ormlambda/statements/__init__.py +1 -0
- ormlambda/statements/base_statement.py +28 -38
- ormlambda/statements/interfaces/IStatements.py +5 -4
- ormlambda/{databases/my_sql → statements}/query_builder.py +35 -30
- ormlambda/{databases/my_sql → statements}/statements.py +50 -60
- ormlambda/statements/types.py +2 -2
- ormlambda/types/__init__.py +24 -0
- ormlambda/types/metadata.py +42 -0
- ormlambda/util/__init__.py +88 -0
- ormlambda/util/load_module.py +21 -0
- ormlambda/util/plugin_loader.py +32 -0
- ormlambda/util/typing.py +6 -0
- ormlambda-3.34.1.dist-info/AUTHORS +32 -0
- {ormlambda-3.12.2.dist-info → ormlambda-3.34.1.dist-info}/METADATA +2 -3
- ormlambda-3.34.1.dist-info/RECORD +157 -0
- {ormlambda-3.12.2.dist-info → ormlambda-3.34.1.dist-info}/WHEEL +1 -1
- ormlambda/components/__init__.py +0 -4
- ormlambda/components/delete/__init__.py +0 -2
- ormlambda/components/delete/abstract_delete.py +0 -17
- ormlambda/components/insert/__init__.py +0 -2
- ormlambda/components/insert/abstract_insert.py +0 -25
- ormlambda/components/select/__init__.py +0 -1
- ormlambda/components/update/__init__.py +0 -2
- ormlambda/components/update/abstract_update.py +0 -29
- ormlambda/components/upsert/__init__.py +0 -2
- ormlambda/components/upsert/abstract_upsert.py +0 -25
- ormlambda/databases/my_sql/clauses/create_database.py +0 -35
- ormlambda/databases/my_sql/clauses/drop_database.py +0 -17
- ormlambda/databases/my_sql/repository/__init__.py +0 -1
- ormlambda/databases/my_sql/repository/repository.py +0 -351
- ormlambda/engine/template.py +0 -47
- ormlambda/sql/dtypes.py +0 -94
- ormlambda/utils/__init__.py +0 -1
- ormlambda-3.12.2.dist-info/RECORD +0 -125
- /ormlambda/databases/my_sql/{types.py → pool_types.py} +0 -0
- /ormlambda/{components/delete → sql/clauses/interfaces}/IDelete.py +0 -0
- /ormlambda/{components/insert → sql/clauses/interfaces}/IInsert.py +0 -0
- /ormlambda/{components/select → sql/clauses/interfaces}/ISelect.py +0 -0
- /ormlambda/{components/update → sql/clauses/interfaces}/IUpdate.py +0 -0
- /ormlambda/{components/upsert → sql/clauses/interfaces}/IUpsert.py +0 -0
- /ormlambda/{components → sql/clauses}/join/__init__.py +0 -0
- /ormlambda/{databases/my_sql → sql}/functions/__init__.py +0 -0
- /ormlambda/{utils → util}/module_tree/__init__.py +0 -0
- /ormlambda/{utils → util}/module_tree/dfs_traversal.py +0 -0
- /ormlambda/{utils → util}/module_tree/dynamic_module.py +0 -0
- {ormlambda-3.12.2.dist-info → ormlambda-3.34.1.dist-info}/LICENSE +0 -0
@@ -1,7 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
from typing import Any, Callable, Optional, Type, overload, TYPE_CHECKING
|
3
3
|
from enum import Enum
|
4
|
-
from abc import abstractmethod
|
4
|
+
from abc import abstractmethod
|
5
5
|
|
6
6
|
|
7
7
|
if TYPE_CHECKING:
|
@@ -9,12 +9,11 @@ if TYPE_CHECKING:
|
|
9
9
|
from ormlambda import Table
|
10
10
|
from ormlambda.sql.clause_info import IAggregate
|
11
11
|
from ormlambda.sql.types import TupleJoinType, ColumnType
|
12
|
-
from ormlambda.
|
12
|
+
from ormlambda.sql.clauses.join import JoinContext
|
13
13
|
from ormlambda.common.enums import JoinType
|
14
14
|
from ormlambda.sql.clause_info import ClauseInfo
|
15
15
|
from ormlambda.sql.types import AliasType
|
16
16
|
|
17
|
-
|
18
17
|
from ..types import (
|
19
18
|
OrderTypes,
|
20
19
|
Tuple,
|
@@ -31,9 +30,10 @@ from ..types import (
|
|
31
30
|
WhereTypes,
|
32
31
|
SelectCols,
|
33
32
|
)
|
33
|
+
from ormlambda.sql.elements import Element
|
34
34
|
|
35
35
|
|
36
|
-
class IStatements[T: Table](
|
36
|
+
class IStatements[T: Table](Element):
|
37
37
|
@abstractmethod
|
38
38
|
def create_table(self, if_exists: TypeExists = "fail") -> None: ...
|
39
39
|
|
@@ -334,6 +334,7 @@ class IStatements_two_generic[T, TPool](IStatements[T]):
|
|
334
334
|
def repository(self) -> BaseRepository[TPool]: ...
|
335
335
|
|
336
336
|
@property
|
337
|
+
@abstractmethod
|
337
338
|
def query(self) -> str: ...
|
338
339
|
|
339
340
|
@property
|
@@ -1,25 +1,29 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import Iterable, TypedDict, Optional
|
2
|
+
from typing import Iterable, TypedDict, Optional, TYPE_CHECKING
|
3
3
|
|
4
4
|
from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext
|
5
|
-
from ormlambda.
|
5
|
+
from ormlambda.sql.clauses import JoinSelector
|
6
6
|
from ormlambda import ForeignKey
|
7
7
|
|
8
8
|
from ormlambda.common.interfaces import IQuery
|
9
9
|
|
10
10
|
|
11
11
|
from ormlambda.sql.clause_info import ClauseInfo
|
12
|
-
from .clauses import Limit
|
13
|
-
from .clauses import Offset
|
14
|
-
from .clauses import Order
|
15
|
-
from .clauses import Select
|
16
12
|
|
17
|
-
|
18
|
-
from .clauses import
|
19
|
-
from .clauses import
|
13
|
+
if TYPE_CHECKING:
|
14
|
+
from ..sql.clauses import Limit as Limit
|
15
|
+
from ..sql.clauses import Offset as Offset
|
16
|
+
from ..sql.clauses import Order as Order
|
17
|
+
from ..sql.clauses import Select as Select
|
20
18
|
|
19
|
+
from ..sql.clauses import GroupBy as GroupBy
|
21
20
|
|
21
|
+
from ormlambda.dialects import Dialect
|
22
|
+
|
23
|
+
from ..sql.clauses import Where as Where
|
24
|
+
from ..sql.clauses import Having as Having
|
22
25
|
from ormlambda.common.enums import JoinType
|
26
|
+
from ormlambda.sql.elements import ClauseElement
|
23
27
|
|
24
28
|
|
25
29
|
class OrderType(TypedDict):
|
@@ -36,7 +40,8 @@ class OrderType(TypedDict):
|
|
36
40
|
class QueryBuilder(IQuery):
|
37
41
|
__order__: tuple[str, ...] = ("Select", "JoinSelector", "Where", "GroupBy", "Having", "Order", "Limit", "Offset")
|
38
42
|
|
39
|
-
def __init__(self, by: JoinType = JoinType.INNER_JOIN):
|
43
|
+
def __init__(self, dialect: Dialect, by: JoinType = JoinType.INNER_JOIN):
|
44
|
+
self.dialect = dialect
|
40
45
|
self._context = ClauseInfoContext()
|
41
46
|
self._query_list: OrderType = {}
|
42
47
|
self._by = by
|
@@ -66,11 +71,11 @@ class QueryBuilder(IQuery):
|
|
66
71
|
return self._joins
|
67
72
|
|
68
73
|
@property
|
69
|
-
def SELECT(self) ->
|
74
|
+
def SELECT(self) -> ClauseElement:
|
70
75
|
return self._query_list.get("Select", None)
|
71
76
|
|
72
77
|
@property
|
73
|
-
def WHERE(self) ->
|
78
|
+
def WHERE(self) -> ClauseElement:
|
74
79
|
where = self._query_list.get("Where", None)
|
75
80
|
if not isinstance(where, Iterable):
|
76
81
|
if not where:
|
@@ -79,15 +84,15 @@ class QueryBuilder(IQuery):
|
|
79
84
|
return where
|
80
85
|
|
81
86
|
@property
|
82
|
-
def ORDER(self) ->
|
87
|
+
def ORDER(self) -> ClauseElement:
|
83
88
|
return self._query_list.get("Order", None)
|
84
89
|
|
85
90
|
@property
|
86
|
-
def GROUP_BY(self) ->
|
91
|
+
def GROUP_BY(self) -> ClauseElement:
|
87
92
|
return self._query_list.get("GroupBy", None)
|
88
93
|
|
89
94
|
@property
|
90
|
-
def HAVING(self) ->
|
95
|
+
def HAVING(self) -> ClauseElement:
|
91
96
|
where = self._query_list.get("Having", None)
|
92
97
|
if not isinstance(where, Iterable):
|
93
98
|
if not where:
|
@@ -96,15 +101,14 @@ class QueryBuilder(IQuery):
|
|
96
101
|
return where
|
97
102
|
|
98
103
|
@property
|
99
|
-
def LIMIT(self) ->
|
104
|
+
def LIMIT(self) -> ClauseElement:
|
100
105
|
return self._query_list.get("Limit", None)
|
101
106
|
|
102
107
|
@property
|
103
|
-
def OFFSET(self) ->
|
108
|
+
def OFFSET(self) -> ClauseElement:
|
104
109
|
return self._query_list.get("Offset", None)
|
105
110
|
|
106
|
-
|
107
|
-
def query(self) -> str:
|
111
|
+
def query(self, dialect: Dialect, **kwargs) -> str:
|
108
112
|
# COMMENT: (select.query, query)We must first create an alias for 'FROM' and then define all the remaining clauses.
|
109
113
|
# This order is mandatory because it adds the clause name to the context when accessing the .query property of 'FROM'
|
110
114
|
|
@@ -112,14 +116,14 @@ class QueryBuilder(IQuery):
|
|
112
116
|
|
113
117
|
JOINS = self.stringify_foreign_key(extract_joins, " ")
|
114
118
|
query_list: tuple[str, ...] = (
|
115
|
-
self.SELECT.
|
119
|
+
self.SELECT.compile(dialect).string,
|
116
120
|
JOINS,
|
117
|
-
Where.join_condition(self.WHERE, True, self._context) if self.WHERE else None,
|
118
|
-
self.GROUP_BY.
|
119
|
-
Having.join_condition(self.HAVING, True, self._context) if self.HAVING else None,
|
120
|
-
self.ORDER.
|
121
|
-
self.LIMIT.
|
122
|
-
self.OFFSET.
|
121
|
+
Where.join_condition(self.WHERE, True, self._context, dialect=dialect) if self.WHERE else None,
|
122
|
+
self.GROUP_BY.compile(dialect).string if self.GROUP_BY else None,
|
123
|
+
Having.join_condition(self.HAVING, True, self._context, dialect=dialect) if self.HAVING else None,
|
124
|
+
self.ORDER.compile(dialect).string if self.ORDER else None,
|
125
|
+
self.LIMIT.compile(dialect).string if self.LIMIT else None,
|
126
|
+
self.OFFSET.compile(dialect).string if self.OFFSET else None,
|
123
127
|
)
|
124
128
|
return " ".join([x for x in query_list if x])
|
125
129
|
|
@@ -127,7 +131,7 @@ class QueryBuilder(IQuery):
|
|
127
131
|
if not joins:
|
128
132
|
return None
|
129
133
|
sorted_joins = JoinSelector.sort_join_selectors(joins)
|
130
|
-
return f"{sep}".join([join.query for join in sorted_joins])
|
134
|
+
return f"{sep}".join([join.query(self.dialect) for join in sorted_joins])
|
131
135
|
|
132
136
|
def pop_tables_and_create_joins_from_ForeignKey(self, by: JoinType = JoinType.INNER_JOIN) -> set[JoinSelector]:
|
133
137
|
# When we applied filters in any table that we wont select any column, we need to add manually all neccessary joins to achieve positive result.
|
@@ -139,14 +143,15 @@ class QueryBuilder(IQuery):
|
|
139
143
|
# FIXME [x]: Resolved when we get Compare object instead ClauseInfo. For instance, when we have multiples condition using '&' or '|'
|
140
144
|
for fk in ForeignKey.stored_calls.copy():
|
141
145
|
fk = ForeignKey.stored_calls.pop(fk)
|
142
|
-
|
143
|
-
|
146
|
+
fk_alias = fk.get_alias(self.dialect)
|
147
|
+
self._context._add_table_alias(fk.tright, fk_alias)
|
148
|
+
join = JoinSelector(fk.resolved_function(self._context), by, context=self._context, alias=fk_alias, dialect=self.dialect)
|
144
149
|
joins.add(join)
|
145
150
|
|
146
151
|
return joins
|
147
152
|
|
148
153
|
def clear(self) -> None:
|
149
|
-
self.__init__()
|
154
|
+
self.__init__(self.dialect, self.by)
|
150
155
|
return None
|
151
156
|
|
152
157
|
def update_context(self, clause: ClauseInfo) -> None:
|
@@ -1,19 +1,16 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
from typing import Concatenate, Iterable, override, Type, TYPE_CHECKING, Any, Callable, Optional
|
3
|
-
from mysql.connector import errors, errorcode
|
4
3
|
|
5
4
|
from ormlambda import ForeignKey
|
6
5
|
|
7
|
-
from mysql.connector import MySQLConnection
|
8
|
-
|
9
6
|
|
10
7
|
if TYPE_CHECKING:
|
8
|
+
from ormlambda.engine.base import Engine
|
11
9
|
from ormlambda.sql.types import AliasType
|
12
10
|
from ormlambda import Table
|
13
11
|
from ormlambda.statements.types import OrderTypes
|
14
12
|
from ormlambda.sql.types import ColumnType
|
15
13
|
from ormlambda.statements.types import SelectCols
|
16
|
-
from ormlambda.repository import BaseRepository
|
17
14
|
from ormlambda.statements.interfaces import IStatements_two_generic
|
18
15
|
from ormlambda.statements.types import TypeExists
|
19
16
|
from ormlambda.sql.clause_info import IAggregate
|
@@ -22,35 +19,22 @@ if TYPE_CHECKING:
|
|
22
19
|
|
23
20
|
from ormlambda.sql.clause_info import ClauseInfo
|
24
21
|
from ormlambda.statements import BaseStatement
|
25
|
-
from .clauses import DeleteQuery
|
26
|
-
from .clauses import InsertQuery
|
27
|
-
from .clauses import Limit
|
28
|
-
from .clauses import Offset
|
29
|
-
from .clauses import Order
|
30
|
-
from .clauses import Select
|
31
|
-
|
32
|
-
from .clauses import UpsertQuery
|
33
|
-
from .clauses import UpdateQuery
|
34
|
-
from .clauses import Where
|
35
|
-
from .clauses import Having
|
36
|
-
from .clauses import Count
|
37
|
-
from .clauses import GroupBy
|
38
|
-
from .clauses import Alias
|
39
|
-
|
40
22
|
|
41
23
|
from ormlambda import Table, Column
|
42
24
|
from ormlambda.common.enums import JoinType
|
43
|
-
from . import
|
44
|
-
from ormlambda.components.join import JoinContext, TupleJoinType
|
25
|
+
from ormlambda.sql.clauses.join import JoinContext, TupleJoinType
|
45
26
|
|
46
27
|
from ormlambda.common.global_checker import GlobalChecker
|
47
28
|
from .query_builder import QueryBuilder
|
48
29
|
|
30
|
+
from ormlambda.sql import clauses
|
31
|
+
from ormlambda.sql import functions as func
|
32
|
+
|
49
33
|
|
50
34
|
# COMMENT: It's so important to prevent information generated by other tests from being retained in the class.
|
51
35
|
@staticmethod
|
52
|
-
def clear_list[T, **P](f: Callable[Concatenate[
|
53
|
-
def wrapper(self:
|
36
|
+
def clear_list[T, **P](f: Callable[Concatenate[Statements, P], T]) -> Callable[P, T]:
|
37
|
+
def wrapper(self: Statements, *args: P.args, **kwargs: P.kwargs) -> T:
|
54
38
|
try:
|
55
39
|
return f(self, *args, **kwargs)
|
56
40
|
except Exception as err:
|
@@ -62,10 +46,10 @@ def clear_list[T, **P](f: Callable[Concatenate[MySQLStatements, P], T]) -> Calla
|
|
62
46
|
return wrapper
|
63
47
|
|
64
48
|
|
65
|
-
class
|
66
|
-
def __init__(self, model:
|
67
|
-
super().__init__(model,
|
68
|
-
self._query_builder = QueryBuilder()
|
49
|
+
class Statements[T: Table, TRepo](BaseStatement[T, None]):
|
50
|
+
def __init__(self, model: T, engine: Engine) -> None:
|
51
|
+
super().__init__(model, engine)
|
52
|
+
self._query_builder = QueryBuilder(self.dialect)
|
69
53
|
|
70
54
|
@override
|
71
55
|
def create_table(self, if_exists: TypeExists = "fail") -> None:
|
@@ -75,7 +59,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
75
59
|
self._repository.drop_table(name)
|
76
60
|
|
77
61
|
elif if_exists == "fail":
|
78
|
-
raise
|
62
|
+
raise ValueError(f"Table '{self._model.__table_name__}' already exists")
|
79
63
|
|
80
64
|
elif if_exists == "append":
|
81
65
|
counter: int = 0
|
@@ -84,16 +68,19 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
84
68
|
counter += 1
|
85
69
|
char = f"_{counter}"
|
86
70
|
name += char
|
87
|
-
self._model.__table_name__ = name
|
88
71
|
|
89
|
-
|
72
|
+
new_model = self._model
|
73
|
+
new_model.__table_name__ = name
|
74
|
+
return new_model.create_table(self.dialect)
|
75
|
+
|
76
|
+
query = self.model.create_table(self.dialect)
|
90
77
|
self._repository.execute(query)
|
91
78
|
return None
|
92
79
|
|
93
80
|
@override
|
94
81
|
@clear_list
|
95
82
|
def insert(self, instances: T | list[T]) -> None:
|
96
|
-
insert =
|
83
|
+
insert = clauses.Insert(self._model, self.repository, self._dialect)
|
97
84
|
insert.insert(instances)
|
98
85
|
insert.execute()
|
99
86
|
return None
|
@@ -108,7 +95,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
108
95
|
# We always going to have a tuple of one element
|
109
96
|
return self.delete(response)
|
110
97
|
|
111
|
-
delete =
|
98
|
+
delete = clauses.Delete(self._model, self._repository, engine=self._engine)
|
112
99
|
delete.delete(instances)
|
113
100
|
delete.execute()
|
114
101
|
# not necessary to call self._query_builder.clear() because select() method already call it
|
@@ -117,7 +104,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
117
104
|
@override
|
118
105
|
@clear_list
|
119
106
|
def upsert(self, instances: T | list[T]) -> None:
|
120
|
-
upsert =
|
107
|
+
upsert = clauses.Upsert(self._model, self._repository, engine=self._engine)
|
121
108
|
upsert.upsert(instances)
|
122
109
|
upsert.execute()
|
123
110
|
return None
|
@@ -125,22 +112,22 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
125
112
|
@override
|
126
113
|
@clear_list
|
127
114
|
def update(self, dicc: dict[str, Any] | list[dict[str, Any]]) -> None:
|
128
|
-
update =
|
115
|
+
update = clauses.Update(self._model, self._repository, self._query_builder.WHERE, engine=self._engine)
|
129
116
|
update.update(dicc)
|
130
117
|
update.execute()
|
131
118
|
|
132
119
|
return None
|
133
120
|
|
134
121
|
@override
|
135
|
-
def limit(self, number: int) -> IStatements_two_generic[T,
|
136
|
-
limit = Limit(number)
|
122
|
+
def limit(self, number: int) -> IStatements_two_generic[T, TRepo]:
|
123
|
+
limit = clauses.Limit(number, dialect=self._dialect)
|
137
124
|
# Only can be one LIMIT SQL parameter. We only use the last LimitQuery
|
138
125
|
self._query_builder.add_statement(limit)
|
139
126
|
return self
|
140
127
|
|
141
128
|
@override
|
142
|
-
def offset(self, number: int) -> IStatements_two_generic[T,
|
143
|
-
offset = Offset(number)
|
129
|
+
def offset(self, number: int) -> IStatements_two_generic[T, TRepo]:
|
130
|
+
offset = clauses.Offset(number, dialect=self._dialect)
|
144
131
|
self._query_builder.add_statement(offset)
|
145
132
|
return self
|
146
133
|
|
@@ -155,40 +142,41 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
155
142
|
return self.select_one(self.count(selection, alias, False), flavour=dict)[alias]
|
156
143
|
|
157
144
|
selection = GlobalChecker.resolved_callback_object(selection, self.models)
|
158
|
-
return Count(
|
145
|
+
return clauses.Count(
|
146
|
+
element=selection,
|
147
|
+
alias_clause=alias,
|
148
|
+
context=self._query_builder._context,
|
149
|
+
dialect=self._dialect,
|
150
|
+
)
|
159
151
|
|
160
152
|
@override
|
161
|
-
def where(self, conditions: WhereTypes) -> IStatements_two_generic[T,
|
153
|
+
def where(self, conditions: WhereTypes) -> IStatements_two_generic[T, TRepo]:
|
162
154
|
# FIXME [x]: I've wrapped self._model into tuple to pass it instance attr. Idk if it's correct
|
163
155
|
|
164
156
|
conditions = GlobalChecker.resolved_callback_object(conditions, self._models)
|
165
157
|
if not isinstance(conditions, Iterable):
|
166
158
|
conditions = (conditions,)
|
167
|
-
self._query_builder.add_statement(Where(*conditions))
|
159
|
+
self._query_builder.add_statement(clauses.Where(*conditions))
|
168
160
|
return self
|
169
161
|
|
170
162
|
@override
|
171
|
-
def having(self, conditions: WhereTypes) -> IStatements_two_generic[T,
|
163
|
+
def having(self, conditions: WhereTypes) -> IStatements_two_generic[T, TRepo]:
|
172
164
|
conditions = GlobalChecker.resolved_callback_object(conditions, self._models)
|
173
165
|
if not isinstance(conditions, Iterable):
|
174
166
|
conditions = (conditions,)
|
175
|
-
self._query_builder.add_statement(Having(*conditions))
|
167
|
+
self._query_builder.add_statement(clauses.Having(*conditions))
|
176
168
|
return self
|
177
169
|
|
178
170
|
@override
|
179
|
-
def order[TValue](self, columns: Callable[[T], TValue], order_type: OrderTypes) -> IStatements_two_generic[T,
|
171
|
+
def order[TValue](self, columns: Callable[[T], TValue], order_type: OrderTypes) -> IStatements_two_generic[T, TRepo]:
|
180
172
|
query = GlobalChecker.resolved_callback_object(columns, self._models)
|
181
|
-
order = Order(query, order_type)
|
173
|
+
order = clauses.Order(query, order_type, dialect=self._dialect)
|
182
174
|
self._query_builder.add_statement(order)
|
183
175
|
return self
|
184
176
|
|
185
177
|
@override
|
186
178
|
def concat(self, selector: SelectCols[T, str], alias: str = "concat") -> IAggregate:
|
187
|
-
return func.Concat
|
188
|
-
values=selector,
|
189
|
-
alias_clause=alias,
|
190
|
-
context=self._query_builder._context,
|
191
|
-
)
|
179
|
+
return func.Concat(values=selector, alias_clause=alias, context=self._query_builder._context, dialect=self._dialect)
|
192
180
|
|
193
181
|
@override
|
194
182
|
def max[TProp](
|
@@ -200,7 +188,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
200
188
|
column = GlobalChecker.resolved_callback_object(column, self.models)
|
201
189
|
if execute is True:
|
202
190
|
return self.select_one(self.max(column, alias, execute=False), flavour=dict)[alias]
|
203
|
-
return func.Max(elements=column, alias_clause=alias, context=self._query_builder._context)
|
191
|
+
return func.Max(elements=column, alias_clause=alias, context=self._query_builder._context, dialect=self._dialect)
|
204
192
|
|
205
193
|
@override
|
206
194
|
def min[TProp](
|
@@ -212,7 +200,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
212
200
|
column = GlobalChecker.resolved_callback_object(column, self.models)
|
213
201
|
if execute is True:
|
214
202
|
return self.select_one(self.min(column, alias, execute=False), flavour=dict)[alias]
|
215
|
-
return func.Min(elements=column, alias_clause=alias, context=self._query_builder._context)
|
203
|
+
return func.Min(elements=column, alias_clause=alias, context=self._query_builder._context, dialect=self._dialect)
|
216
204
|
|
217
205
|
@override
|
218
206
|
def sum[TProp](
|
@@ -224,11 +212,11 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
224
212
|
column = GlobalChecker.resolved_callback_object(column, self.models)
|
225
213
|
if execute is True:
|
226
214
|
return self.select_one(self.sum(column, alias, execute=False), flavour=dict)[alias]
|
227
|
-
return func.Sum(elements=column, alias_clause=alias, context=self._query_builder._context)
|
215
|
+
return func.Sum(elements=column, alias_clause=alias, context=self._query_builder._context, dialect=self._dialect)
|
228
216
|
|
229
217
|
@override
|
230
218
|
def join[LTable: Table, LProp, RTable: Table, RProp](self, joins: tuple[TupleJoinType[LTable, LProp, RTable, RProp]]) -> JoinContext[tuple[*TupleJoinType[LTable, LProp, RTable, RProp]]]:
|
231
|
-
return JoinContext(self, joins, self._query_builder._context)
|
219
|
+
return JoinContext(self, joins, self._query_builder._context, self._dialect)
|
232
220
|
|
233
221
|
@override
|
234
222
|
@clear_list
|
@@ -254,15 +242,16 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
254
242
|
return result
|
255
243
|
return () if not result else result[0]
|
256
244
|
|
257
|
-
select = Select
|
245
|
+
select = clauses.Select(
|
258
246
|
self._models,
|
259
247
|
columns=select_clause,
|
248
|
+
dialect=self.dialect,
|
260
249
|
**kwargs,
|
261
250
|
)
|
262
251
|
self._query_builder.add_statement(select)
|
263
252
|
|
264
253
|
self._query_builder.by = by
|
265
|
-
self._query: str = self._query_builder.query
|
254
|
+
self._query: str = self._query_builder.query(self._dialect)
|
266
255
|
|
267
256
|
if flavour:
|
268
257
|
result = self._return_flavour(self.query, flavour, select, **kwargs)
|
@@ -316,21 +305,22 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
316
305
|
)
|
317
306
|
|
318
307
|
@override
|
319
|
-
def groupby[TProp](self, column: ColumnType[TProp] | Callable[[T
|
308
|
+
def groupby[TProp](self, column: ColumnType[TProp] | Callable[[T], Any]) -> IStatements_two_generic[T]:
|
320
309
|
column = GlobalChecker.resolved_callback_object(column, self.models)
|
321
310
|
|
322
|
-
groupby = GroupBy(column=column, context=self._query_builder._context)
|
311
|
+
groupby = clauses.GroupBy(column=column, context=self._query_builder._context, dialect=self.dialect)
|
323
312
|
# Only can be one LIMIT SQL parameter. We only use the last LimitQuery
|
324
313
|
self._query_builder.add_statement(groupby)
|
325
314
|
return self
|
326
315
|
|
327
316
|
@override
|
328
|
-
def alias[TProp](self, column: SelectCols[T, TProp], alias: AliasType[ClauseInfo[T]]) ->
|
317
|
+
def alias[TProp](self, column: SelectCols[T, TProp], alias: AliasType[ClauseInfo[T]]) -> clauses.Alias[T]:
|
329
318
|
column = GlobalChecker.resolved_callback_object(column, self.models)
|
330
319
|
|
331
|
-
return Alias(
|
320
|
+
return clauses.Alias(
|
332
321
|
table=column.table,
|
333
322
|
column=column,
|
334
323
|
alias_clause=alias,
|
335
324
|
context=self._query_builder._context,
|
325
|
+
dialect=self.dialect,
|
336
326
|
)
|
ormlambda/statements/types.py
CHANGED
@@ -48,8 +48,8 @@ type TypeExists = Literal["fail", "replace", "append"]
|
|
48
48
|
|
49
49
|
type WhereTypes[LTable, LProp, RTable, RProp] = Union[
|
50
50
|
bool,
|
51
|
-
Comparer
|
52
|
-
tuple[Comparer
|
51
|
+
Comparer,
|
52
|
+
tuple[Comparer],
|
53
53
|
Callable[[LTable], WhereTypes[LTable, LProp, RTable, RProp]],
|
54
54
|
]
|
55
55
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
|
4
|
+
from .metadata import (
|
5
|
+
PrimaryKey,
|
6
|
+
AutoGenerated,
|
7
|
+
AutoIncrement,
|
8
|
+
Unique,
|
9
|
+
CheckTypes,
|
10
|
+
Default,
|
11
|
+
NotNull,
|
12
|
+
)
|
13
|
+
|
14
|
+
|
15
|
+
__all__ = [
|
16
|
+
"PrimaryKey",
|
17
|
+
"AutoGenerated",
|
18
|
+
"AutoIncrement",
|
19
|
+
"Unique",
|
20
|
+
"CheckTypes",
|
21
|
+
"Default",
|
22
|
+
"NotNull",
|
23
|
+
"Binary",
|
24
|
+
]
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class PrimaryKey:
|
2
|
+
"""Marks a column as a primary key"""
|
3
|
+
|
4
|
+
...
|
5
|
+
|
6
|
+
|
7
|
+
class AutoGenerated:
|
8
|
+
"""Marks a column as auto-generated"""
|
9
|
+
|
10
|
+
...
|
11
|
+
|
12
|
+
|
13
|
+
class AutoIncrement:
|
14
|
+
"""Marks a column as auto-increment"""
|
15
|
+
|
16
|
+
...
|
17
|
+
|
18
|
+
|
19
|
+
class Unique:
|
20
|
+
"""Marks a column as unique"""
|
21
|
+
|
22
|
+
...
|
23
|
+
|
24
|
+
|
25
|
+
class CheckTypes:
|
26
|
+
"""Controls type checking for this column"""
|
27
|
+
|
28
|
+
def __init__(self, enabled: bool = True):
|
29
|
+
self.enabled: bool = enabled
|
30
|
+
|
31
|
+
|
32
|
+
class Default[T]:
|
33
|
+
"""Sets a default value for a column"""
|
34
|
+
|
35
|
+
def __init__(self, value: T):
|
36
|
+
self.value: T = value
|
37
|
+
|
38
|
+
|
39
|
+
class NotNull:
|
40
|
+
"""Marks a column as not-null"""
|
41
|
+
|
42
|
+
...
|
@@ -0,0 +1,88 @@
|
|
1
|
+
from .module_tree import ModuleTree # noqa: F401
|
2
|
+
from .load_module import __load_module__ # noqa: F401
|
3
|
+
import types
|
4
|
+
import inspect
|
5
|
+
from typing import Any, Literal, Optional, Sequence, overload, get_origin, TypeGuard, TypeAliasType
|
6
|
+
from ormlambda.util.typing import LITERAL_TYPES, _AnnotationScanType
|
7
|
+
from .plugin_loader import PluginLoader # noqa: F401
|
8
|
+
|
9
|
+
|
10
|
+
def _inspect_func_args(fn) -> tuple[list[str], bool]:
|
11
|
+
try:
|
12
|
+
co_varkeywords = inspect.CO_VARKEYWORDS
|
13
|
+
except AttributeError:
|
14
|
+
# https://docs.python.org/3/library/inspect.html
|
15
|
+
# The flags are specific to CPython, and may not be defined in other
|
16
|
+
# Python implementations. Furthermore, the flags are an implementation
|
17
|
+
# detail, and can be removed or deprecated in future Python releases.
|
18
|
+
spec = inspect.getfullargspec(fn)
|
19
|
+
return spec[0], bool(spec[2])
|
20
|
+
else:
|
21
|
+
# use fn.__code__ plus flags to reduce method call overhead
|
22
|
+
co = fn.__code__
|
23
|
+
nargs = co.co_argcount
|
24
|
+
return (
|
25
|
+
list(co.co_varnames[:nargs]),
|
26
|
+
bool(co.co_flags & co_varkeywords),
|
27
|
+
)
|
28
|
+
|
29
|
+
|
30
|
+
@overload
|
31
|
+
def get_cls_kwargs(cls: type, *, _set: Optional[set[str]] = None, raiseerr: Literal[True] = ...) -> set[str]: ...
|
32
|
+
|
33
|
+
|
34
|
+
@overload
|
35
|
+
def get_cls_kwargs(cls: type, *, _set: Optional[set[str]] = None, raiseerr: Literal[False] = ...) -> Optional[set[str]]: ...
|
36
|
+
|
37
|
+
|
38
|
+
def get_cls_kwargs(cls: type, *, _set: Optional[set[str]] = None, raiseerr: bool = False) -> Optional[set[str]]:
|
39
|
+
"""
|
40
|
+
Get the keyword arguments for a class constructor.
|
41
|
+
Args:
|
42
|
+
cls: The class to inspect.
|
43
|
+
_set: A set to store the keyword arguments.
|
44
|
+
raiseerr: Whether to raise an error if the class is not found.
|
45
|
+
Returns:
|
46
|
+
A set of keyword arguments for the class constructor.
|
47
|
+
"""
|
48
|
+
toplevel = _set is None
|
49
|
+
if toplevel:
|
50
|
+
_set = set()
|
51
|
+
assert _set is not None
|
52
|
+
|
53
|
+
ctr = cls.__dict__.get("__init__", False)
|
54
|
+
|
55
|
+
has_init = ctr and isinstance(ctr, types.FunctionType) and isinstance(ctr.__code__, types.CodeType)
|
56
|
+
|
57
|
+
if has_init:
|
58
|
+
names, has_kw = _inspect_func_args(ctr)
|
59
|
+
_set.update(names)
|
60
|
+
|
61
|
+
if not has_kw and not toplevel:
|
62
|
+
if raiseerr:
|
63
|
+
raise TypeError(f"given cls {cls} doesn't have an __init__ method")
|
64
|
+
else:
|
65
|
+
return None
|
66
|
+
else:
|
67
|
+
has_kw = False
|
68
|
+
|
69
|
+
if not has_init or has_kw:
|
70
|
+
for c in cls.__bases__:
|
71
|
+
if get_cls_kwargs(c, _set=_set) is None:
|
72
|
+
break
|
73
|
+
|
74
|
+
_set.discard("self")
|
75
|
+
return _set
|
76
|
+
|
77
|
+
|
78
|
+
def avoid_sql_injection(name: str):
|
79
|
+
if any(char in name for char in [";", "--", "/*", "*/"]):
|
80
|
+
raise ValueError("SQL injection detected")
|
81
|
+
|
82
|
+
|
83
|
+
def is_literal(type_: Any) -> bool:
|
84
|
+
return get_origin(type) in LITERAL_TYPES
|
85
|
+
|
86
|
+
|
87
|
+
def is_pep695(type_: _AnnotationScanType) -> TypeGuard[TypeAliasType]:
|
88
|
+
return isinstance(type_, TypeAliasType)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import logging
|
2
|
+
import importlib
|
3
|
+
from typing import Any, Optional
|
4
|
+
|
5
|
+
log = logging.getLogger(__name__)
|
6
|
+
|
7
|
+
|
8
|
+
def __load_module__(module_path: str, dialect_name: Optional[str] = None) -> Optional[Any]:
|
9
|
+
"""
|
10
|
+
Load a module by its path and return the module object.
|
11
|
+
Returns None if the module cannot be imported
|
12
|
+
Args:
|
13
|
+
module_path: The dot-separated path to the module
|
14
|
+
|
15
|
+
Returns:
|
16
|
+
The loaded module or None if import fails
|
17
|
+
"""
|
18
|
+
try:
|
19
|
+
return importlib.import_module(module_path)
|
20
|
+
except ImportError:
|
21
|
+
log.error(f"{module_path.split('.')[-1] if not dialect_name else dialect_name} dialect not available")
|