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,19 +1,19 @@
|
|
1
1
|
from typing import Optional
|
2
|
-
from ormlambda.caster import BaseCaster,
|
2
|
+
from ormlambda.caster import BaseCaster, Caster
|
3
3
|
|
4
4
|
|
5
5
|
class StringCaster[TType](BaseCaster[str, TType]):
|
6
6
|
def __init__(self, value: str, type_value: TType):
|
7
7
|
super().__init__(value, type_value)
|
8
8
|
|
9
|
-
def wildcard_to_select(self, value: str =
|
10
|
-
return value
|
9
|
+
def wildcard_to_select(self, value: Optional[str] = None) -> str:
|
10
|
+
return Caster.PLACEHOLDER if value is None else value
|
11
11
|
|
12
|
-
def wildcard_to_where(self, value: str =
|
13
|
-
return value
|
12
|
+
def wildcard_to_where(self, value: Optional[str] = None) -> str:
|
13
|
+
return Caster.PLACEHOLDER if value is None else value
|
14
14
|
|
15
|
-
def wildcard_to_insert(self, value: str =
|
16
|
-
return value
|
15
|
+
def wildcard_to_insert(self, value: Optional[str] = None) -> str:
|
16
|
+
return Caster.PLACEHOLDER if value is None else value
|
17
17
|
|
18
18
|
@property
|
19
19
|
@BaseCaster.return_value_if_exists
|
@@ -1,7 +1,12 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import TYPE_CHECKING
|
1
3
|
from ormlambda.sql.clause_info import AggregateFunctionBase
|
2
4
|
from ormlambda.sql.types import ColumnType, AliasType
|
3
5
|
from ormlambda.sql.clause_info.clause_info_context import ClauseContextType
|
4
6
|
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
from ormlambda.dialects import Dialect
|
9
|
+
|
5
10
|
|
6
11
|
class ST_AsText[T, TProp](AggregateFunctionBase[None]):
|
7
12
|
"""
|
@@ -20,16 +25,12 @@ class ST_AsText[T, TProp](AggregateFunctionBase[None]):
|
|
20
25
|
alias_table: AliasType[ColumnType[TProp]] = None,
|
21
26
|
alias_clause: AliasType[ColumnType[TProp]] = None,
|
22
27
|
context: ClauseContextType = None,
|
28
|
+
*,
|
29
|
+
dialect: Dialect,
|
23
30
|
) -> None:
|
24
31
|
default_alias_clause = self.create_alias_from(point) if not alias_clause else alias_clause
|
25
32
|
|
26
|
-
super().__init__(
|
27
|
-
table=point.table,
|
28
|
-
column=point,
|
29
|
-
alias_table=alias_table,
|
30
|
-
alias_clause=default_alias_clause,
|
31
|
-
context=context,
|
32
|
-
)
|
33
|
+
super().__init__(table=point.table, column=point, alias_table=alias_table, alias_clause=default_alias_clause, context=context, dialect=dialect)
|
33
34
|
|
34
35
|
@staticmethod
|
35
36
|
def create_alias_from(element: ColumnType[TProp]) -> str:
|
@@ -1,3 +1,4 @@
|
|
1
|
+
from __future__ import annotations
|
1
2
|
import typing as tp
|
2
3
|
|
3
4
|
from shapely import Point
|
@@ -6,6 +7,9 @@ from ormlambda import Column
|
|
6
7
|
from ormlambda.sql.types import ColumnType, AliasType
|
7
8
|
from ormlambda.sql.clause_info import ClauseInfo, IAggregate
|
8
9
|
|
10
|
+
if tp.TYPE_CHECKING:
|
11
|
+
from ormlambda.dialects import Dialect
|
12
|
+
|
9
13
|
|
10
14
|
class ST_Contains(IAggregate):
|
11
15
|
FUNCTION_NAME: str = "ST_Contains"
|
@@ -16,15 +20,16 @@ class ST_Contains(IAggregate):
|
|
16
20
|
point: Point,
|
17
21
|
alias_table: tp.Optional[AliasType[ColumnType[TProp]]] = None,
|
18
22
|
alias_clause: tp.Optional[AliasType[ColumnType[TProp]]] = None,
|
23
|
+
*,
|
24
|
+
dialect: Dialect,
|
19
25
|
):
|
20
|
-
self.attr1: ClauseInfo[Point] = ClauseInfo(column.table, column, alias_table)
|
21
|
-
self.attr2: ClauseInfo[Point] = ClauseInfo[Point](None, point)
|
26
|
+
self.attr1: ClauseInfo[Point] = ClauseInfo(column.table, column, alias_table, dialect=dialect)
|
27
|
+
self.attr2: ClauseInfo[Point] = ClauseInfo[Point](None, point, dialect=dialect)
|
22
28
|
|
23
29
|
self._alias_clause: AliasType[ColumnType[TProp]] = alias_clause
|
24
30
|
|
25
|
-
|
26
|
-
|
27
|
-
return f"{self.FUNCTION_NAME}({self.attr1.query}, {self.attr2.query})"
|
31
|
+
def query(self, dialect: Dialect, **kwargs) -> str:
|
32
|
+
return f"{self.FUNCTION_NAME}({self.attr1.query(dialect,**kwargs)}, {self.attr2.query(dialect,**kwargs)})"
|
28
33
|
|
29
34
|
@property
|
30
35
|
def alias_clause(self) -> tp.Optional[str]:
|
@@ -1,20 +1,14 @@
|
|
1
|
-
from .
|
2
|
-
from .create_database import TypeExists as TypeExists
|
3
|
-
from .delete import DeleteQuery as DeleteQuery
|
4
|
-
from .drop_database import DropDatabase as DropDatabase
|
1
|
+
from .delete import DeleteQuery as Delete
|
5
2
|
from .drop_table import DropTable as DropTable
|
6
|
-
from .insert import InsertQuery as
|
3
|
+
from .insert import InsertQuery as Insert
|
7
4
|
from .joins import JoinSelector as JoinSelector
|
8
|
-
from .joins import JoinType as JoinType
|
9
5
|
from .limit import Limit as Limit
|
10
6
|
from .offset import Offset as Offset
|
11
7
|
from .order import Order as Order
|
12
|
-
from .update import
|
13
|
-
from .upsert import UpsertQuery as
|
8
|
+
from .update import Update as Update
|
9
|
+
from .upsert import UpsertQuery as Upsert
|
14
10
|
from .where import Where as Where
|
15
11
|
from .having import Having as Having
|
16
12
|
from .count import Count as Count
|
17
13
|
from .group_by import GroupBy as GroupBy
|
18
|
-
from .alias import Alias as Alias
|
19
14
|
from .ST_AsText import ST_AsText as ST_AsText
|
20
|
-
from .select import Select as Select
|
@@ -1,25 +1,19 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from ormlambda.sql.
|
2
|
+
from ormlambda.sql.clauses import Count
|
3
3
|
from ormlambda.sql.clause_info.clause_info_context import ClauseContextType
|
4
4
|
|
5
5
|
from ormlambda.sql.types import AliasType, ColumnType
|
6
6
|
|
7
7
|
from ormlambda import Table
|
8
8
|
|
9
|
-
|
9
|
+
from typing import TYPE_CHECKING
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
if tp.TYPE_CHECKING:
|
11
|
+
if TYPE_CHECKING:
|
14
12
|
from ormlambda import Table
|
15
13
|
from ormlambda.sql.types import ColumnType, AliasType, TableType
|
16
14
|
|
17
15
|
|
18
|
-
class Count[T: Table](
|
19
|
-
@staticmethod
|
20
|
-
def FUNCTION_NAME() -> str:
|
21
|
-
return "COUNT"
|
22
|
-
|
16
|
+
class Count[T: Table](Count[T]):
|
23
17
|
def __init__[TProp: Table](
|
24
18
|
self,
|
25
19
|
element: ColumnType[T] | TableType[TProp],
|
@@ -29,12 +23,8 @@ class Count[T: Table](AggregateFunctionBase[T]):
|
|
29
23
|
keep_asterisk: bool = True,
|
30
24
|
preserve_context: bool = True,
|
31
25
|
) -> None:
|
32
|
-
table = self.extract_table(element)
|
33
|
-
column = element if self.is_column(element) else ASTERISK
|
34
|
-
|
35
26
|
super().__init__(
|
36
|
-
|
37
|
-
column=column,
|
27
|
+
element=element,
|
38
28
|
alias_table=alias_table,
|
39
29
|
alias_clause=alias_clause,
|
40
30
|
context=context,
|
@@ -1,56 +1,9 @@
|
|
1
|
-
from typing import Any, Optional, override, Iterable, TYPE_CHECKING
|
2
|
-
|
3
|
-
if TYPE_CHECKING:
|
4
|
-
from ormlambda import Column
|
5
|
-
|
6
1
|
from ormlambda import Table
|
7
2
|
from ormlambda.repository import IRepositoryBase
|
8
|
-
from ormlambda.
|
3
|
+
from ormlambda.sql.clauses import Delete
|
4
|
+
from mysql.connector import MySQLConnection
|
9
5
|
|
10
6
|
|
11
|
-
class DeleteQuery[T: Table](
|
7
|
+
class DeleteQuery[T: Table](Delete[T, MySQLConnection]):
|
12
8
|
def __init__(self, model: T, repository: IRepositoryBase) -> None:
|
13
9
|
super().__init__(model, repository)
|
14
|
-
|
15
|
-
@property
|
16
|
-
def CLAUSE(self) -> str:
|
17
|
-
return "DELETE"
|
18
|
-
|
19
|
-
@override
|
20
|
-
def delete(self, instances: T | list[T]) -> None:
|
21
|
-
col: str = ""
|
22
|
-
if isinstance(instances, Table):
|
23
|
-
pk: Optional[Column] = instances.get_pk()
|
24
|
-
if pk is None:
|
25
|
-
raise Exception(f"You cannot use 'DELETE' query without set primary key in '{instances.__table_name__}'")
|
26
|
-
col = pk.column_name
|
27
|
-
value = str(instances[pk])
|
28
|
-
|
29
|
-
elif isinstance(instances, Iterable):
|
30
|
-
value: list[Any] = []
|
31
|
-
for ins in instances:
|
32
|
-
pk = type(ins).get_pk()
|
33
|
-
value.append(ins[pk])
|
34
|
-
col = pk.column_name
|
35
|
-
|
36
|
-
query: str = f"{self.CLAUSE} FROM {self._model.__table_name__} WHERE {col}"
|
37
|
-
if isinstance(value, str):
|
38
|
-
query += "= %s"
|
39
|
-
self._query = query
|
40
|
-
self._values = [value]
|
41
|
-
return None
|
42
|
-
|
43
|
-
elif isinstance(value, Iterable):
|
44
|
-
params = ", ".join(["%s"] * len(value))
|
45
|
-
query += f" IN ({params})"
|
46
|
-
self._query = query
|
47
|
-
self._values = value
|
48
|
-
return None
|
49
|
-
else:
|
50
|
-
raise Exception(f"'{type(value)}' no esperado")
|
51
|
-
|
52
|
-
@override
|
53
|
-
def execute(self) -> None:
|
54
|
-
if not self._query:
|
55
|
-
raise ValueError
|
56
|
-
return self._repository.execute_with_values(self._query, self._values)
|
@@ -1,14 +1,9 @@
|
|
1
|
-
|
2
|
-
from ormlambda import
|
3
|
-
from ormlambda.sql.clause_info import AggregateFunctionBase, ClauseInfoContext
|
1
|
+
from ormlambda.sql.clause_info import ClauseInfoContext
|
2
|
+
from ormlambda.sql.clauses import GroupBy
|
4
3
|
from ormlambda.sql.types import ColumnType
|
5
4
|
|
6
5
|
|
7
|
-
class GroupBy
|
8
|
-
@classmethod
|
9
|
-
def FUNCTION_NAME(self) -> str:
|
10
|
-
return "GROUP BY"
|
11
|
-
|
6
|
+
class GroupBy(GroupBy):
|
12
7
|
def __init__(
|
13
8
|
self,
|
14
9
|
column: ColumnType,
|
@@ -16,15 +11,7 @@ class GroupBy[T: tp.Type[Table], *Ts, TProp](AggregateFunctionBase):
|
|
16
11
|
**kwargs,
|
17
12
|
):
|
18
13
|
super().__init__(
|
19
|
-
table=column.table,
|
20
14
|
column=column,
|
21
|
-
alias_table=None,
|
22
|
-
alias_clause=None,
|
23
15
|
context=context,
|
24
16
|
**kwargs,
|
25
17
|
)
|
26
|
-
|
27
|
-
@property
|
28
|
-
def query(self) -> str:
|
29
|
-
column = self._create_query()
|
30
|
-
return f"{self.FUNCTION_NAME()} {column}"
|
@@ -1,16 +1,12 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from .
|
3
|
+
from ormlambda.sql.clauses import Having
|
4
4
|
|
5
5
|
|
6
|
-
class Having(
|
6
|
+
class Having(Having):
|
7
7
|
"""
|
8
8
|
The purpose of this class is to create 'WHERE' condition queries properly.
|
9
9
|
"""
|
10
10
|
|
11
11
|
def __init__(self, *comparer, restrictive=True, context=None):
|
12
12
|
super().__init__(*comparer, restrictive=restrictive, context=context)
|
13
|
-
|
14
|
-
@staticmethod
|
15
|
-
def FUNCTION_NAME() -> str:
|
16
|
-
return "HAVING"
|
@@ -1,97 +1,9 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
|
3
|
-
from typing import override, Iterable, TYPE_CHECKING
|
4
|
-
|
5
2
|
from ormlambda import Table
|
6
|
-
from ormlambda import
|
7
|
-
from
|
8
|
-
from ormlambda.repository import IRepositoryBase
|
9
|
-
from ormlambda.caster import Caster
|
10
|
-
|
11
|
-
if TYPE_CHECKING:
|
12
|
-
from ormlambda.databases.my_sql import MySQLRepository
|
3
|
+
from ormlambda.sql.clauses import Insert
|
4
|
+
from mysql.connector import MySQLConnection
|
13
5
|
|
14
6
|
|
15
|
-
class InsertQuery[T: Table](
|
16
|
-
def __init__(self, model
|
7
|
+
class InsertQuery[T: Table](Insert[T, MySQLConnection]):
|
8
|
+
def __init__(self, model, repository):
|
17
9
|
super().__init__(model, repository)
|
18
|
-
|
19
|
-
@override
|
20
|
-
@property
|
21
|
-
def CLAUSE(self) -> str:
|
22
|
-
return "INSERT INTO"
|
23
|
-
|
24
|
-
@override
|
25
|
-
def execute(self) -> None:
|
26
|
-
if not self._query:
|
27
|
-
raise ValueError
|
28
|
-
return self._repository.executemany_with_values(self.query, self._values)
|
29
|
-
|
30
|
-
@override
|
31
|
-
def insert[TProp](self, instances: T | list[T]) -> None:
|
32
|
-
if not isinstance(instances, Iterable):
|
33
|
-
instances = (instances,)
|
34
|
-
valid_cols: list[list[Column[TProp]]] = []
|
35
|
-
self.__fill_dict_list(valid_cols, instances)
|
36
|
-
|
37
|
-
col_names: list[str] = []
|
38
|
-
wildcards: list[str] = []
|
39
|
-
col_values: list[list[str]] = []
|
40
|
-
for i, cols in enumerate(valid_cols):
|
41
|
-
col_values.append([])
|
42
|
-
CASTER = Caster(self._repository)
|
43
|
-
for col in cols:
|
44
|
-
clean_data = CASTER.for_column(col, instances[i]) # .resolve(instances[i][col])
|
45
|
-
if i == 0:
|
46
|
-
col_names.append(col.column_name)
|
47
|
-
wildcards.append(clean_data.wildcard_to_insert())
|
48
|
-
# COMMENT: avoid MySQLWriteCastBase.resolve when using PLACEHOLDERs
|
49
|
-
col_values[-1].append(clean_data.to_database)
|
50
|
-
|
51
|
-
join_cols = ", ".join(col_names)
|
52
|
-
unknown_rows = f'({", ".join(wildcards)})' # The number of "%s" must match the dict 'dicc_0' length
|
53
|
-
|
54
|
-
self._values = [tuple(x) for x in col_values]
|
55
|
-
self._query = f"{self.CLAUSE} {self._model.__table_name__} {f'({join_cols})'} VALUES {unknown_rows}"
|
56
|
-
return None
|
57
|
-
|
58
|
-
@staticmethod
|
59
|
-
def __is_valid[TProp](column: Column[TProp], value: TProp) -> bool:
|
60
|
-
"""
|
61
|
-
We want to delete the column from table when it's specified with an 'AUTO_INCREMENT' or 'AUTO GENERATED ALWAYS AS (__) STORED' statement.
|
62
|
-
|
63
|
-
if the column is auto-generated, it means the database creates the value for that column, so we must deleted it.
|
64
|
-
if the column is primary key and auto-increment, we should be able to create an object with specific pk value.
|
65
|
-
|
66
|
-
RETURN
|
67
|
-
-----
|
68
|
-
|
69
|
-
- True -> Do not delete the column from dict query
|
70
|
-
- False -> Delete the column from dict query
|
71
|
-
"""
|
72
|
-
|
73
|
-
is_pk_none_and_auto_increment: bool = all([value is None, column.is_primary_key, column.is_auto_increment])
|
74
|
-
|
75
|
-
if is_pk_none_and_auto_increment or column.is_auto_generated:
|
76
|
-
return False
|
77
|
-
return True
|
78
|
-
|
79
|
-
def __fill_dict_list[TProp](self, list_dict: list[str, TProp], values: list[T]) -> list[Column]:
|
80
|
-
if isinstance(values, Iterable):
|
81
|
-
for x in values:
|
82
|
-
self.__fill_dict_list(list_dict, x)
|
83
|
-
|
84
|
-
elif issubclass(values.__class__, Table):
|
85
|
-
new_list = []
|
86
|
-
for prop in type(values).__dict__.values():
|
87
|
-
if not isinstance(prop, Column):
|
88
|
-
continue
|
89
|
-
|
90
|
-
value = getattr(values, prop.column_name)
|
91
|
-
if self.__is_valid(prop, value):
|
92
|
-
new_list.append(prop)
|
93
|
-
list_dict.append(new_list)
|
94
|
-
|
95
|
-
else:
|
96
|
-
raise Exception(f"Tipo de dato'{type(values)}' no esperado")
|
97
|
-
return None
|
@@ -1,149 +1,14 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from
|
3
|
-
from typing import override, Optional, TYPE_CHECKING, Type
|
2
|
+
from typing import TYPE_CHECKING
|
4
3
|
|
5
4
|
|
6
|
-
from ormlambda.
|
7
|
-
from ormlambda.common.interfaces.IJoinSelector import IJoinSelector
|
8
|
-
from ormlambda.common.interfaces.IQueryCommand import IQuery
|
9
|
-
from ormlambda import JoinType
|
10
|
-
from ormlambda.sql.clause_info import ClauseInfo
|
11
|
-
from ormlambda.sql.comparer import Comparer
|
12
|
-
from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext, ClauseContextType
|
5
|
+
from ormlambda.sql.clauses import JoinSelector
|
13
6
|
|
14
7
|
# TODOL [x]: Try to import Table module without circular import Error
|
15
8
|
if TYPE_CHECKING:
|
16
9
|
from ormlambda import Table
|
17
10
|
|
18
11
|
|
19
|
-
class JoinSelector[TLeft: Table, TRight: Table](
|
20
|
-
|
21
|
-
|
22
|
-
"_orig_table",
|
23
|
-
"_right_table",
|
24
|
-
"_by",
|
25
|
-
"_left_col",
|
26
|
-
"_right_col",
|
27
|
-
"_compareop",
|
28
|
-
"_alias",
|
29
|
-
)
|
30
|
-
|
31
|
-
@override
|
32
|
-
def __repr__(self) -> str:
|
33
|
-
table_col_left: str = f"{self.left_table.table_alias()}.{self._left_col}"
|
34
|
-
table_col_right: str = f"{self.right_table.table_alias()}.{self._right_col}"
|
35
|
-
|
36
|
-
return f"{IQuery.__name__}: {self.__class__.__name__} ({table_col_left} == {table_col_right})"
|
37
|
-
|
38
|
-
def __init__[LProp, RProp](
|
39
|
-
self,
|
40
|
-
where: Comparer[TLeft, LProp, TRight, RProp],
|
41
|
-
by: JoinType,
|
42
|
-
alias: Optional[str] = "{table}",
|
43
|
-
context: ClauseContextType = None,
|
44
|
-
) -> None:
|
45
|
-
self._comparer: Comparer[TLeft, LProp, TRight, RProp] = where
|
46
|
-
self._orig_table: TLeft = where.left_condition.table
|
47
|
-
self._right_table: TRight = where.right_condition.table
|
48
|
-
self._by: JoinType = by
|
49
|
-
self._left_col: str = where.left_condition._column.column_name
|
50
|
-
self._right_col: str = where.right_condition._column.column_name
|
51
|
-
self._compareop = where._compare
|
52
|
-
self._context: ClauseContextType = context if context else ClauseInfoContext()
|
53
|
-
|
54
|
-
# COMMENT: When multiple columns reference the same table, we need to create an alias to maintain clear references.
|
55
|
-
self._alias: Optional[str] = alias
|
56
|
-
|
57
|
-
self._from_clause = ClauseInfo(self.right_table, alias_table=alias, context=self._context)
|
58
|
-
self._left_table_clause = ClauseInfo(self.left_table, column=self.left_col, alias_clause=None, context=self._create_partial_context())
|
59
|
-
self._right_table_clause = ClauseInfo(self.right_table, column=self.right_col, alias_clause=None, context=self._create_partial_context())
|
60
|
-
|
61
|
-
def __eq__(self, __value: JoinSelector) -> bool:
|
62
|
-
return isinstance(__value, JoinSelector) and self.__hash__() == __value.__hash__()
|
63
|
-
|
64
|
-
def __hash__(self) -> int:
|
65
|
-
return hash(
|
66
|
-
(
|
67
|
-
self.left_table,
|
68
|
-
self.right_table,
|
69
|
-
self._by,
|
70
|
-
self._left_col,
|
71
|
-
self._right_col,
|
72
|
-
self._compareop,
|
73
|
-
)
|
74
|
-
)
|
75
|
-
|
76
|
-
def _create_partial_context(self) -> ClauseInfoContext:
|
77
|
-
"""
|
78
|
-
Only use table_context from global context
|
79
|
-
"""
|
80
|
-
if not self._context:
|
81
|
-
return ClauseInfoContext()
|
82
|
-
return ClauseInfoContext(clause_context=None, table_context=self._context._table_context)
|
83
|
-
|
84
|
-
@classmethod
|
85
|
-
def join_selectors(cls, *args: JoinSelector) -> str:
|
86
|
-
return "\n".join([x.query for x in args])
|
87
|
-
|
88
|
-
@property
|
89
|
-
@override
|
90
|
-
def query(self) -> str:
|
91
|
-
self._context = ClauseInfoContext(clause_context=None, table_context=self._context._table_context)
|
92
|
-
list_ = [
|
93
|
-
self._by.value, # inner join
|
94
|
-
self._from_clause.query,
|
95
|
-
"ON",
|
96
|
-
self._left_table_clause.query,
|
97
|
-
self._compareop, # =
|
98
|
-
self._right_table_clause.query,
|
99
|
-
]
|
100
|
-
return " ".join([x for x in list_ if x is not None])
|
101
|
-
|
102
|
-
@property
|
103
|
-
def left_table(self) -> TLeft:
|
104
|
-
return self._orig_table
|
105
|
-
|
106
|
-
@property
|
107
|
-
def right_table(self) -> TRight:
|
108
|
-
return self._right_table
|
109
|
-
|
110
|
-
@property
|
111
|
-
def left_col(self) -> str:
|
112
|
-
return self._left_col
|
113
|
-
|
114
|
-
@property
|
115
|
-
def right_col(self) -> str:
|
116
|
-
return self._right_col
|
117
|
-
|
118
|
-
@property
|
119
|
-
def alias(self) -> str:
|
120
|
-
return self._alias
|
121
|
-
|
122
|
-
@classmethod
|
123
|
-
def sort_join_selectors(cls, joins: set[JoinSelector]) -> tuple[JoinSelector]:
|
124
|
-
# FIXME [x]: How to sort when needed because it's not necessary at this point. It is for testing purpouse
|
125
|
-
if len(joins) == 1:
|
126
|
-
return tuple(joins)
|
127
|
-
|
128
|
-
join_object_map: dict[str, list[JoinSelector]] = defaultdict(list)
|
129
|
-
|
130
|
-
for obj in joins:
|
131
|
-
join_object_map[obj.left_table].append(obj)
|
132
|
-
|
133
|
-
graph: dict[Type[Table], list[Type[Table]]] = defaultdict(list)
|
134
|
-
for join in joins:
|
135
|
-
graph[join.left_table].append(join.right_table)
|
136
|
-
|
137
|
-
sorted_graph = DFSTraversal.sort(graph)[::-1]
|
138
|
-
|
139
|
-
if not sorted_graph:
|
140
|
-
return tuple(joins)
|
141
|
-
|
142
|
-
res = []
|
143
|
-
for table in sorted_graph:
|
144
|
-
tables = join_object_map[table]
|
145
|
-
|
146
|
-
if not tables:
|
147
|
-
continue
|
148
|
-
res.extend(tables)
|
149
|
-
return res
|
12
|
+
class JoinSelector[TLeft: Table, TRight: Table](JoinSelector[TLeft, TRight]):
|
13
|
+
def __init__(self, where, by, alias="{table}", context=None, **kw):
|
14
|
+
super().__init__(where, by, alias, context, **kw)
|
@@ -1,17 +1,6 @@
|
|
1
|
-
from
|
1
|
+
from ormlambda.sql.clauses import Limit
|
2
2
|
|
3
|
-
from ormlambda.common.interfaces.IQueryCommand import IQuery
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def __init__(self, number: int) -> None:
|
10
|
-
if not isinstance(number, int):
|
11
|
-
raise ValueError
|
12
|
-
self._number: int = number
|
13
|
-
|
14
|
-
@override
|
15
|
-
@property
|
16
|
-
def query(self) -> str:
|
17
|
-
return f"{self.LIMIT} {self._number}"
|
4
|
+
class Limit(Limit):
|
5
|
+
def __init__(self, number):
|
6
|
+
super().__init__(number)
|
@@ -1,17 +1,6 @@
|
|
1
|
-
from
|
1
|
+
from ormlambda.sql.clauses import Offset
|
2
2
|
|
3
|
-
from ormlambda.common.interfaces.IQueryCommand import IQuery
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def __init__(self, number: int) -> None:
|
10
|
-
if not isinstance(number, int):
|
11
|
-
raise ValueError
|
12
|
-
self._number: int = number
|
13
|
-
|
14
|
-
@override
|
15
|
-
@property
|
16
|
-
def query(self) -> str:
|
17
|
-
return f"{self.OFFSET} {self._number}"
|
4
|
+
class Offset(Offset):
|
5
|
+
def __init__(self, number):
|
6
|
+
super().__init__(number)
|
@@ -1,65 +1,8 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
import typing as tp
|
3
2
|
|
4
|
-
from ormlambda.sql.
|
5
|
-
from ormlambda.sql.types import ColumnType
|
6
|
-
from ormlambda.sql.clause_info import AggregateFunctionBase
|
3
|
+
from ormlambda.sql.clauses import Order
|
7
4
|
|
8
|
-
from ormlambda.statements import OrderType
|
9
5
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def FUNCTION_NAME() -> str:
|
14
|
-
return "ORDER BY"
|
15
|
-
|
16
|
-
def __init__[TProp](
|
17
|
-
self,
|
18
|
-
column: tuple[ColumnType[TProp], ...] | ColumnType[TProp],
|
19
|
-
order_type: tp.Iterable[OrderType],
|
20
|
-
context: ClauseContextType = None,
|
21
|
-
):
|
22
|
-
super().__init__(
|
23
|
-
table=None,
|
24
|
-
column=column,
|
25
|
-
context=context,
|
26
|
-
)
|
27
|
-
|
28
|
-
if isinstance(order_type, str) or not isinstance(order_type, tp.Iterable):
|
29
|
-
order_type = (order_type,)
|
30
|
-
|
31
|
-
self._order_type: list[OrderType] = [self.__cast_to_OrderType(x) for x in order_type]
|
32
|
-
|
33
|
-
def __cast_to_OrderType(self, _value: tp.Any) -> tp.Iterable[OrderType]:
|
34
|
-
if isinstance(_value, OrderType):
|
35
|
-
return _value
|
36
|
-
|
37
|
-
if isinstance(_value, str):
|
38
|
-
try:
|
39
|
-
return OrderType(_value)
|
40
|
-
except Exception:
|
41
|
-
pass
|
42
|
-
raise Exception(f"order_type param only can be 'ASC' or 'DESC' string or '{OrderType.__name__}' enum")
|
43
|
-
|
44
|
-
@tp.override
|
45
|
-
@property
|
46
|
-
def query(self) -> str:
|
47
|
-
string_columns: list[str] = []
|
48
|
-
columns = self.unresolved_column
|
49
|
-
|
50
|
-
# if this attr is not iterable means that we only pass one column without wrapped in a list or tuple
|
51
|
-
if isinstance(columns, str):
|
52
|
-
string_columns = f"{columns} {str(self._order_type[0])}"
|
53
|
-
return f"{self.FUNCTION_NAME()} {string_columns}"
|
54
|
-
|
55
|
-
if not isinstance(columns, tp.Iterable):
|
56
|
-
columns = (columns,)
|
57
|
-
|
58
|
-
assert len(columns) == len(self._order_type)
|
59
|
-
|
60
|
-
context = ClauseInfoContext(table_context=self._context._table_context, clause_context=None) if self._context else None
|
61
|
-
for index, clause in enumerate(self._convert_into_clauseInfo(columns, context)):
|
62
|
-
clause.alias_clause = None
|
63
|
-
string_columns.append(f"{clause.query} {str(self._order_type[index])}")
|
64
|
-
|
65
|
-
return f"{self.FUNCTION_NAME()} {', '.join(string_columns)}"
|
6
|
+
class Order(Order):
|
7
|
+
def __init__(self, column, order_type, context=None):
|
8
|
+
super().__init__(column, order_type, context)
|