ormlambda 3.12.2__py3-none-any.whl → 3.34.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/__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/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 +38 -0
- ormlambda/dialects/mysql/base.py +388 -0
- ormlambda/dialects/mysql/caster/caster.py +39 -0
- ormlambda/{databases/my_sql → dialects/mysql}/caster/types/__init__.py +1 -0
- ormlambda/dialects/mysql/caster/types/boolean.py +35 -0
- ormlambda/{databases/my_sql → dialects/mysql}/caster/types/bytes.py +7 -7
- ormlambda/{databases/my_sql → dialects/mysql}/caster/types/datetime.py +7 -7
- ormlambda/{databases/my_sql → dialects/mysql}/caster/types/float.py +7 -7
- ormlambda/{databases/my_sql → dialects/mysql}/caster/types/int.py +7 -7
- ormlambda/{databases/my_sql → dialects/mysql}/caster/types/iterable.py +7 -7
- ormlambda/{databases/my_sql → dialects/mysql}/caster/types/none.py +8 -7
- ormlambda/{databases/my_sql → dialects/mysql}/caster/types/point.py +4 -4
- ormlambda/{databases/my_sql → dialects/mysql}/caster/types/string.py +7 -7
- ormlambda/{databases/my_sql → dialects/mysql}/clauses/ST_AsText.py +8 -7
- ormlambda/{databases/my_sql → dialects/mysql}/clauses/ST_Contains.py +10 -5
- ormlambda/dialects/mysql/clauses/__init__.py +13 -0
- ormlambda/dialects/mysql/clauses/count.py +33 -0
- ormlambda/dialects/mysql/clauses/delete.py +9 -0
- ormlambda/dialects/mysql/clauses/group_by.py +17 -0
- ormlambda/dialects/mysql/clauses/having.py +12 -0
- ormlambda/dialects/mysql/clauses/insert.py +9 -0
- ormlambda/dialects/mysql/clauses/joins.py +14 -0
- ormlambda/dialects/mysql/clauses/limit.py +6 -0
- ormlambda/dialects/mysql/clauses/offset.py +6 -0
- ormlambda/dialects/mysql/clauses/order.py +8 -0
- ormlambda/dialects/mysql/clauses/update.py +8 -0
- ormlambda/dialects/mysql/clauses/upsert.py +9 -0
- ormlambda/dialects/mysql/clauses/where.py +7 -0
- ormlambda/dialects/mysql/mysqlconnector.py +46 -0
- ormlambda/dialects/mysql/repository/__init__.py +1 -0
- ormlambda/dialects/mysql/repository/repository.py +212 -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 +77 -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 +119 -12
- 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/{databases/my_sql → sql}/clauses/count.py +15 -1
- ormlambda/{databases/my_sql → sql}/clauses/delete.py +22 -7
- ormlambda/sql/clauses/group_by.py +30 -0
- ormlambda/{databases/my_sql → sql}/clauses/having.py +7 -2
- ormlambda/{databases/my_sql → sql}/clauses/insert.py +16 -9
- ormlambda/sql/clauses/interfaces/__init__.py +5 -0
- ormlambda/{components → sql/clauses}/join/join_context.py +15 -7
- ormlambda/{databases/my_sql → sql}/clauses/joins.py +29 -19
- ormlambda/sql/clauses/limit.py +15 -0
- ormlambda/sql/clauses/offset.py +15 -0
- ormlambda/{databases/my_sql → sql}/clauses/order.py +14 -24
- ormlambda/{databases/my_sql → sql}/clauses/select.py +12 -13
- ormlambda/{databases/my_sql → sql}/clauses/update.py +24 -11
- ormlambda/{databases/my_sql → sql}/clauses/upsert.py +17 -12
- ormlambda/{databases/my_sql → sql}/clauses/where.py +28 -8
- 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 +668 -0
- ormlambda/sql/ddl.py +82 -0
- ormlambda/sql/elements.py +36 -0
- ormlambda/sql/foreign_key.py +61 -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 +175 -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 +8 -4
- ormlambda/{databases/my_sql → statements}/query_builder.py +35 -30
- ormlambda/{databases/my_sql → statements}/statements.py +57 -61
- ormlambda/statements/types.py +2 -2
- ormlambda/types/__init__.py +24 -0
- ormlambda/types/metadata.py +42 -0
- ormlambda/util/__init__.py +87 -0
- ormlambda/{utils → util}/module_tree/dynamic_module.py +1 -1
- ormlambda/util/plugin_loader.py +32 -0
- ormlambda/util/typing.py +6 -0
- ormlambda-3.34.0.dist-info/AUTHORS +32 -0
- {ormlambda-3.12.2.dist-info → ormlambda-3.34.0.dist-info}/METADATA +1 -1
- ormlambda-3.34.0.dist-info/RECORD +152 -0
- 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/__init__.py +0 -5
- ormlambda/databases/my_sql/__init__.py +0 -4
- ormlambda/databases/my_sql/caster/caster.py +0 -39
- ormlambda/databases/my_sql/clauses/__init__.py +0 -20
- ormlambda/databases/my_sql/clauses/create_database.py +0 -35
- ormlambda/databases/my_sql/clauses/drop_database.py +0 -17
- ormlambda/databases/my_sql/clauses/drop_table.py +0 -26
- ormlambda/databases/my_sql/clauses/group_by.py +0 -30
- ormlambda/databases/my_sql/clauses/limit.py +0 -17
- ormlambda/databases/my_sql/clauses/offset.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 → dialects/mysql}/caster/__init__.py +0 -0
- /ormlambda/{databases/my_sql/types.py → dialects/mysql/repository/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-3.12.2.dist-info → ormlambda-3.34.0.dist-info}/LICENSE +0 -0
- {ormlambda-3.12.2.dist-info → ormlambda-3.34.0.dist-info}/WHEEL +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,12 +30,16 @@ 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
|
|
40
|
+
@abstractmethod
|
41
|
+
def drop_table(self) -> None: ...
|
42
|
+
|
40
43
|
# #TODOL [ ]: We must to implement this mehtod
|
41
44
|
# @abstractmethod
|
42
45
|
# def drop_table(self)->None: ...
|
@@ -334,6 +337,7 @@ class IStatements_two_generic[T, TPool](IStatements[T]):
|
|
334
337
|
def repository(self) -> BaseRepository[TPool]: ...
|
335
338
|
|
336
339
|
@property
|
340
|
+
@abstractmethod
|
337
341
|
def query(self) -> str: ...
|
338
342
|
|
339
343
|
@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,20 +46,20 @@ 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:
|
72
56
|
name: str = self._model.__table_name__
|
73
57
|
if self._repository.table_exists(name):
|
74
58
|
if if_exists == "replace":
|
75
|
-
self.
|
59
|
+
self.drop_table()
|
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,25 @@ 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
|
|
80
|
+
@override
|
81
|
+
def drop_table(self)->None:
|
82
|
+
q = self.model.drop_table(self.dialect)
|
83
|
+
self._repository.execute(q)
|
84
|
+
return None
|
85
|
+
|
93
86
|
@override
|
94
87
|
@clear_list
|
95
88
|
def insert(self, instances: T | list[T]) -> None:
|
96
|
-
insert =
|
89
|
+
insert = clauses.Insert(self._model, self.repository, self._dialect)
|
97
90
|
insert.insert(instances)
|
98
91
|
insert.execute()
|
99
92
|
return None
|
@@ -108,7 +101,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
108
101
|
# We always going to have a tuple of one element
|
109
102
|
return self.delete(response)
|
110
103
|
|
111
|
-
delete =
|
104
|
+
delete = clauses.Delete(self._model, self._repository, engine=self._engine)
|
112
105
|
delete.delete(instances)
|
113
106
|
delete.execute()
|
114
107
|
# not necessary to call self._query_builder.clear() because select() method already call it
|
@@ -117,7 +110,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
117
110
|
@override
|
118
111
|
@clear_list
|
119
112
|
def upsert(self, instances: T | list[T]) -> None:
|
120
|
-
upsert =
|
113
|
+
upsert = clauses.Upsert(self._model, self._repository, engine=self._engine)
|
121
114
|
upsert.upsert(instances)
|
122
115
|
upsert.execute()
|
123
116
|
return None
|
@@ -125,22 +118,22 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
125
118
|
@override
|
126
119
|
@clear_list
|
127
120
|
def update(self, dicc: dict[str, Any] | list[dict[str, Any]]) -> None:
|
128
|
-
update =
|
121
|
+
update = clauses.Update(self._model, self._repository, self._query_builder.WHERE, engine=self._engine)
|
129
122
|
update.update(dicc)
|
130
123
|
update.execute()
|
131
124
|
|
132
125
|
return None
|
133
126
|
|
134
127
|
@override
|
135
|
-
def limit(self, number: int) -> IStatements_two_generic[T,
|
136
|
-
limit = Limit(number)
|
128
|
+
def limit(self, number: int) -> IStatements_two_generic[T, TRepo]:
|
129
|
+
limit = clauses.Limit(number, dialect=self._dialect)
|
137
130
|
# Only can be one LIMIT SQL parameter. We only use the last LimitQuery
|
138
131
|
self._query_builder.add_statement(limit)
|
139
132
|
return self
|
140
133
|
|
141
134
|
@override
|
142
|
-
def offset(self, number: int) -> IStatements_two_generic[T,
|
143
|
-
offset = Offset(number)
|
135
|
+
def offset(self, number: int) -> IStatements_two_generic[T, TRepo]:
|
136
|
+
offset = clauses.Offset(number, dialect=self._dialect)
|
144
137
|
self._query_builder.add_statement(offset)
|
145
138
|
return self
|
146
139
|
|
@@ -155,40 +148,41 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
155
148
|
return self.select_one(self.count(selection, alias, False), flavour=dict)[alias]
|
156
149
|
|
157
150
|
selection = GlobalChecker.resolved_callback_object(selection, self.models)
|
158
|
-
return Count(
|
151
|
+
return clauses.Count(
|
152
|
+
element=selection,
|
153
|
+
alias_clause=alias,
|
154
|
+
context=self._query_builder._context,
|
155
|
+
dialect=self._dialect,
|
156
|
+
)
|
159
157
|
|
160
158
|
@override
|
161
|
-
def where(self, conditions: WhereTypes) -> IStatements_two_generic[T,
|
159
|
+
def where(self, conditions: WhereTypes) -> IStatements_two_generic[T, TRepo]:
|
162
160
|
# FIXME [x]: I've wrapped self._model into tuple to pass it instance attr. Idk if it's correct
|
163
161
|
|
164
162
|
conditions = GlobalChecker.resolved_callback_object(conditions, self._models)
|
165
163
|
if not isinstance(conditions, Iterable):
|
166
164
|
conditions = (conditions,)
|
167
|
-
self._query_builder.add_statement(Where(*conditions))
|
165
|
+
self._query_builder.add_statement(clauses.Where(*conditions))
|
168
166
|
return self
|
169
167
|
|
170
168
|
@override
|
171
|
-
def having(self, conditions: WhereTypes) -> IStatements_two_generic[T,
|
169
|
+
def having(self, conditions: WhereTypes) -> IStatements_two_generic[T, TRepo]:
|
172
170
|
conditions = GlobalChecker.resolved_callback_object(conditions, self._models)
|
173
171
|
if not isinstance(conditions, Iterable):
|
174
172
|
conditions = (conditions,)
|
175
|
-
self._query_builder.add_statement(Having(*conditions))
|
173
|
+
self._query_builder.add_statement(clauses.Having(*conditions))
|
176
174
|
return self
|
177
175
|
|
178
176
|
@override
|
179
|
-
def order[TValue](self, columns: Callable[[T], TValue], order_type: OrderTypes) -> IStatements_two_generic[T,
|
177
|
+
def order[TValue](self, columns: Callable[[T], TValue], order_type: OrderTypes) -> IStatements_two_generic[T, TRepo]:
|
180
178
|
query = GlobalChecker.resolved_callback_object(columns, self._models)
|
181
|
-
order = Order(query, order_type)
|
179
|
+
order = clauses.Order(query, order_type, dialect=self._dialect)
|
182
180
|
self._query_builder.add_statement(order)
|
183
181
|
return self
|
184
182
|
|
185
183
|
@override
|
186
184
|
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
|
-
)
|
185
|
+
return func.Concat(values=selector, alias_clause=alias, context=self._query_builder._context, dialect=self._dialect)
|
192
186
|
|
193
187
|
@override
|
194
188
|
def max[TProp](
|
@@ -200,7 +194,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
200
194
|
column = GlobalChecker.resolved_callback_object(column, self.models)
|
201
195
|
if execute is True:
|
202
196
|
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)
|
197
|
+
return func.Max(elements=column, alias_clause=alias, context=self._query_builder._context, dialect=self._dialect)
|
204
198
|
|
205
199
|
@override
|
206
200
|
def min[TProp](
|
@@ -212,7 +206,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
212
206
|
column = GlobalChecker.resolved_callback_object(column, self.models)
|
213
207
|
if execute is True:
|
214
208
|
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)
|
209
|
+
return func.Min(elements=column, alias_clause=alias, context=self._query_builder._context, dialect=self._dialect)
|
216
210
|
|
217
211
|
@override
|
218
212
|
def sum[TProp](
|
@@ -224,11 +218,11 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
224
218
|
column = GlobalChecker.resolved_callback_object(column, self.models)
|
225
219
|
if execute is True:
|
226
220
|
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)
|
221
|
+
return func.Sum(elements=column, alias_clause=alias, context=self._query_builder._context, dialect=self._dialect)
|
228
222
|
|
229
223
|
@override
|
230
224
|
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)
|
225
|
+
return JoinContext(self, joins, self._query_builder._context, self._dialect)
|
232
226
|
|
233
227
|
@override
|
234
228
|
@clear_list
|
@@ -254,15 +248,16 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
254
248
|
return result
|
255
249
|
return () if not result else result[0]
|
256
250
|
|
257
|
-
select = Select
|
251
|
+
select = clauses.Select(
|
258
252
|
self._models,
|
259
253
|
columns=select_clause,
|
254
|
+
dialect=self.dialect,
|
260
255
|
**kwargs,
|
261
256
|
)
|
262
257
|
self._query_builder.add_statement(select)
|
263
258
|
|
264
259
|
self._query_builder.by = by
|
265
|
-
self._query: str = self._query_builder.query
|
260
|
+
self._query: str = self._query_builder.query(self._dialect)
|
266
261
|
|
267
262
|
if flavour:
|
268
263
|
result = self._return_flavour(self.query, flavour, select, **kwargs)
|
@@ -316,21 +311,22 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
|
|
316
311
|
)
|
317
312
|
|
318
313
|
@override
|
319
|
-
def groupby[TProp](self, column: ColumnType[TProp] | Callable[[T
|
314
|
+
def groupby[TProp](self, column: ColumnType[TProp] | Callable[[T], Any]) -> IStatements_two_generic[T]:
|
320
315
|
column = GlobalChecker.resolved_callback_object(column, self.models)
|
321
316
|
|
322
|
-
groupby = GroupBy(column=column, context=self._query_builder._context)
|
317
|
+
groupby = clauses.GroupBy(column=column, context=self._query_builder._context, dialect=self.dialect)
|
323
318
|
# Only can be one LIMIT SQL parameter. We only use the last LimitQuery
|
324
319
|
self._query_builder.add_statement(groupby)
|
325
320
|
return self
|
326
321
|
|
327
322
|
@override
|
328
|
-
def alias[TProp](self, column: SelectCols[T, TProp], alias: AliasType[ClauseInfo[T]]) ->
|
323
|
+
def alias[TProp](self, column: SelectCols[T, TProp], alias: AliasType[ClauseInfo[T]]) -> clauses.Alias[T]:
|
329
324
|
column = GlobalChecker.resolved_callback_object(column, self.models)
|
330
325
|
|
331
|
-
return Alias(
|
326
|
+
return clauses.Alias(
|
332
327
|
table=column.table,
|
333
328
|
column=column,
|
334
329
|
alias_clause=alias,
|
335
330
|
context=self._query_builder._context,
|
331
|
+
dialect=self.dialect,
|
336
332
|
)
|
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,87 @@
|
|
1
|
+
from .module_tree import ModuleTree # noqa: F401
|
2
|
+
import types
|
3
|
+
import inspect
|
4
|
+
from typing import Any, Literal, Optional, overload, get_origin, TypeGuard, TypeAliasType
|
5
|
+
from ormlambda.util.typing import LITERAL_TYPES, _AnnotationScanType
|
6
|
+
from .plugin_loader import PluginLoader # noqa: F401
|
7
|
+
|
8
|
+
|
9
|
+
def _inspect_func_args(fn) -> tuple[list[str], bool]:
|
10
|
+
try:
|
11
|
+
co_varkeywords = inspect.CO_VARKEYWORDS
|
12
|
+
except AttributeError:
|
13
|
+
# https://docs.python.org/3/library/inspect.html
|
14
|
+
# The flags are specific to CPython, and may not be defined in other
|
15
|
+
# Python implementations. Furthermore, the flags are an implementation
|
16
|
+
# detail, and can be removed or deprecated in future Python releases.
|
17
|
+
spec = inspect.getfullargspec(fn)
|
18
|
+
return spec[0], bool(spec[2])
|
19
|
+
else:
|
20
|
+
# use fn.__code__ plus flags to reduce method call overhead
|
21
|
+
co = fn.__code__
|
22
|
+
nargs = co.co_argcount
|
23
|
+
return (
|
24
|
+
list(co.co_varnames[:nargs]),
|
25
|
+
bool(co.co_flags & co_varkeywords),
|
26
|
+
)
|
27
|
+
|
28
|
+
|
29
|
+
@overload
|
30
|
+
def get_cls_kwargs(cls: type, *, _set: Optional[set[str]] = None, raiseerr: Literal[True] = ...) -> set[str]: ...
|
31
|
+
|
32
|
+
|
33
|
+
@overload
|
34
|
+
def get_cls_kwargs(cls: type, *, _set: Optional[set[str]] = None, raiseerr: Literal[False] = ...) -> Optional[set[str]]: ...
|
35
|
+
|
36
|
+
|
37
|
+
def get_cls_kwargs(cls: type, *, _set: Optional[set[str]] = None, raiseerr: bool = False) -> Optional[set[str]]:
|
38
|
+
"""
|
39
|
+
Get the keyword arguments for a class constructor.
|
40
|
+
Args:
|
41
|
+
cls: The class to inspect.
|
42
|
+
_set: A set to store the keyword arguments.
|
43
|
+
raiseerr: Whether to raise an error if the class is not found.
|
44
|
+
Returns:
|
45
|
+
A set of keyword arguments for the class constructor.
|
46
|
+
"""
|
47
|
+
toplevel = _set is None
|
48
|
+
if toplevel:
|
49
|
+
_set = set()
|
50
|
+
assert _set is not None
|
51
|
+
|
52
|
+
ctr = cls.__dict__.get("__init__", False)
|
53
|
+
|
54
|
+
has_init = ctr and isinstance(ctr, types.FunctionType) and isinstance(ctr.__code__, types.CodeType)
|
55
|
+
|
56
|
+
if has_init:
|
57
|
+
names, has_kw = _inspect_func_args(ctr)
|
58
|
+
_set.update(names)
|
59
|
+
|
60
|
+
if not has_kw and not toplevel:
|
61
|
+
if raiseerr:
|
62
|
+
raise TypeError(f"given cls {cls} doesn't have an __init__ method")
|
63
|
+
else:
|
64
|
+
return None
|
65
|
+
else:
|
66
|
+
has_kw = False
|
67
|
+
|
68
|
+
if not has_init or has_kw:
|
69
|
+
for c in cls.__bases__:
|
70
|
+
if get_cls_kwargs(c, _set=_set) is None:
|
71
|
+
break
|
72
|
+
|
73
|
+
_set.discard("self")
|
74
|
+
return _set
|
75
|
+
|
76
|
+
|
77
|
+
def avoid_sql_injection(name: str):
|
78
|
+
if any(char in name for char in [";", "--", "/*", "*/"]):
|
79
|
+
raise ValueError("SQL injection detected")
|
80
|
+
|
81
|
+
|
82
|
+
def is_literal(type_: Any) -> bool:
|
83
|
+
return get_origin(type) in LITERAL_TYPES
|
84
|
+
|
85
|
+
|
86
|
+
def is_pep695(type_: _AnnotationScanType) -> TypeGuard[TypeAliasType]:
|
87
|
+
return isinstance(type_, TypeAliasType)
|
@@ -236,7 +236,7 @@ class ModuleTree:
|
|
236
236
|
# we need to ensure that the object we going to add in table_list is the same
|
237
237
|
for name, obj in table_class:
|
238
238
|
if name == node.class_name:
|
239
|
-
table_list.append(obj.
|
239
|
+
table_list.append(obj.create_table())
|
240
240
|
return tuple(table_list)
|
241
241
|
|
242
242
|
@staticmethod
|