ormlambda 4.0.5__tar.gz → 4.4.0__tar.gz
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-4.0.5 → ormlambda-4.4.0}/PKG-INFO +2 -3
- {ormlambda-4.0.5 → ormlambda-4.4.0}/pyproject.toml +1 -1
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/__init__.py +2 -6
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/common/enums/__init__.py +2 -1
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/common/enums/condition_types.py +4 -1
- ormlambda-4.4.0/src/ormlambda/common/enums/union_type.py +9 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/common/errors/__init__.py +9 -9
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/common/interfaces/__init__.py +0 -1
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/base.py +291 -37
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/caster/types/iterable.py +5 -1
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/clauses/ST_AsText.py +2 -2
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/clauses/ST_Contains.py +2 -2
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/errors.py +2 -2
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/repository/response.py +2 -1
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/clause_info/__init__.py +0 -1
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/clause_info/clause_info.py +15 -3
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/clause_info/interface/__init__.py +0 -1
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/clauses/__init__.py +0 -1
- ormlambda-4.4.0/src/ormlambda/sql/clauses/alias.py +52 -0
- ormlambda-4.4.0/src/ormlambda/sql/clauses/delete.py +21 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/clauses/group_by.py +2 -2
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/clauses/having.py +2 -2
- ormlambda-4.4.0/src/ormlambda/sql/clauses/insert.py +25 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/clauses/joins.py +1 -1
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/clauses/order.py +2 -2
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/clauses/select.py +7 -5
- ormlambda-4.4.0/src/ormlambda/sql/clauses/update.py +23 -0
- ormlambda-4.4.0/src/ormlambda/sql/clauses/upsert.py +23 -0
- ormlambda-4.4.0/src/ormlambda/sql/clauses/where.py +42 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/column/column.py +1 -1
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/column/column_proxy.py +28 -0
- ormlambda-4.4.0/src/ormlambda/sql/comparer.py +192 -0
- ormlambda-4.4.0/src/ormlambda/sql/functions/__init__.py +87 -0
- ormlambda-4.4.0/src/ormlambda/sql/functions/aggregate/avg.py +15 -0
- {ormlambda-4.0.5/src/ormlambda/sql/functions → ormlambda-4.4.0/src/ormlambda/sql/functions/aggregate}/concat.py +2 -2
- {ormlambda-4.0.5/src/ormlambda/sql/clauses → ormlambda-4.4.0/src/ormlambda/sql/functions/aggregate}/count.py +2 -2
- ormlambda-4.4.0/src/ormlambda/sql/functions/aggregate/max.py +15 -0
- ormlambda-4.4.0/src/ormlambda/sql/functions/aggregate/min.py +15 -0
- ormlambda-4.4.0/src/ormlambda/sql/functions/aggregate/sum.py +15 -0
- ormlambda-4.4.0/src/ormlambda/sql/functions/base.py +26 -0
- ormlambda-4.4.0/src/ormlambda/sql/functions/datetime/__init__.py +0 -0
- ormlambda-4.0.5/src/ormlambda/sql/clause_info/interface/IAggregate.py → ormlambda-4.4.0/src/ormlambda/sql/functions/interface/__init__.py +8 -4
- ormlambda-4.4.0/src/ormlambda/sql/functions/mathematical/__init__.py +0 -0
- ormlambda-4.4.0/src/ormlambda/sql/functions/mathematical/abs.py +15 -0
- ormlambda-4.4.0/src/ormlambda/sql/functions/mathematical/ceil.py +15 -0
- ormlambda-4.4.0/src/ormlambda/sql/functions/mathematical/floor.py +15 -0
- ormlambda-4.4.0/src/ormlambda/sql/functions/mathematical/mod.py +16 -0
- ormlambda-4.4.0/src/ormlambda/sql/functions/mathematical/pow.py +16 -0
- ormlambda-4.4.0/src/ormlambda/sql/functions/mathematical/rand.py +15 -0
- ormlambda-4.4.0/src/ormlambda/sql/functions/mathematical/round.py +15 -0
- ormlambda-4.4.0/src/ormlambda/sql/functions/mathematical/sqrt.py +15 -0
- ormlambda-4.4.0/src/ormlambda/sql/functions/mathematical/truncate.py +17 -0
- ormlambda-4.4.0/src/ormlambda/sql/functions/string/__init__.py +0 -0
- ormlambda-4.4.0/src/ormlambda/sql/types.py +44 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/statements/base_statement.py +8 -5
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/statements/interfaces/IStatements.py +17 -13
- ormlambda-4.4.0/src/ormlambda/statements/query_builder.py +259 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/statements/statements.py +55 -41
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/util/preloaded.py +2 -0
- ormlambda-4.0.5/src/ormlambda/common/abstract_classes/__init__.py +0 -1
- ormlambda-4.0.5/src/ormlambda/common/abstract_classes/non_query_base.py +0 -37
- ormlambda-4.0.5/src/ormlambda/common/interfaces/INonQueryCommand.py +0 -9
- ormlambda-4.0.5/src/ormlambda/sql/clauses/alias.py +0 -14
- ormlambda-4.0.5/src/ormlambda/sql/clauses/delete.py +0 -71
- ormlambda-4.0.5/src/ormlambda/sql/clauses/insert.py +0 -104
- ormlambda-4.0.5/src/ormlambda/sql/clauses/interfaces/IDelete.py +0 -6
- ormlambda-4.0.5/src/ormlambda/sql/clauses/interfaces/IInsert.py +0 -6
- ormlambda-4.0.5/src/ormlambda/sql/clauses/interfaces/IUpdate.py +0 -7
- ormlambda-4.0.5/src/ormlambda/sql/clauses/interfaces/IUpsert.py +0 -6
- ormlambda-4.0.5/src/ormlambda/sql/clauses/interfaces/__init__.py +0 -4
- ormlambda-4.0.5/src/ormlambda/sql/clauses/update.py +0 -87
- ormlambda-4.0.5/src/ormlambda/sql/clauses/upsert.py +0 -77
- ormlambda-4.0.5/src/ormlambda/sql/clauses/where.py +0 -46
- ormlambda-4.0.5/src/ormlambda/sql/comparer.py +0 -136
- ormlambda-4.0.5/src/ormlambda/sql/functions/__init__.py +0 -4
- ormlambda-4.0.5/src/ormlambda/sql/functions/max.py +0 -24
- ormlambda-4.0.5/src/ormlambda/sql/functions/min.py +0 -24
- ormlambda-4.0.5/src/ormlambda/sql/functions/sum.py +0 -25
- ormlambda-4.0.5/src/ormlambda/sql/types.py +0 -30
- ormlambda-4.0.5/src/ormlambda/statements/query_builder.py +0 -331
- {ormlambda-4.0.5 → ormlambda-4.4.0}/AUTHORS +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/LICENSE +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/README.md +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/caster/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/caster/base_caster.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/caster/caster.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/caster/interfaces/ICaster.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/caster/interfaces/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/common/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/common/enums/join_type.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/common/enums/order_type.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/common/global_checker.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/common/interfaces/IJoinSelector.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/common/interfaces/IQueryCommand.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/default/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/default/base.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/caster/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/caster/caster.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/caster/types/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/caster/types/boolean.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/caster/types/bytes.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/caster/types/date.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/caster/types/datetime.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/caster/types/decimal.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/caster/types/float.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/caster/types/int.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/caster/types/json.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/caster/types/none.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/caster/types/point.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/caster/types/string.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/clauses/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/mysqlconnector.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/repository/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/repository/pool_types.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/repository/repository.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/mysql/types.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/sqlite/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/sqlite/base.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/dialects/sqlite/pysqlite.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/engine/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/engine/base.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/engine/create.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/engine/url.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/engine/utils.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/env.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/model/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/model/base_model.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/repository/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/repository/base_repository.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/repository/interfaces/IDatabaseConnection.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/repository/interfaces/IRepositoryBase.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/repository/interfaces/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/clause_info/interface/IClauseInfo.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/clauses/join/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/clauses/join/join_context.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/clauses/limit.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/clauses/offset.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/column/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/column_table_proxy.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/compiler.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/context/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/ddl.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/elements.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/foreign_key.py +0 -0
- {ormlambda-4.0.5/src/ormlambda/sql/interfaces → ormlambda-4.4.0/src/ormlambda/sql/functions/aggregate}/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/sqltypes.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/table/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/table/fields.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/table/table.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/table/table_constructor.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/table/table_proxy.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/type_api.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/sql/visitors.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/statements/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/statements/interfaces/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/statements/types.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/types/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/types/metadata.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/util/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/util/langhelpers.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/util/module_tree/__init__.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/util/module_tree/dfs_traversal.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/util/module_tree/dynamic_module.py +0 -0
- {ormlambda-4.0.5 → ormlambda-4.4.0}/src/ormlambda/util/typing.py +0 -0
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: ormlambda
|
|
3
|
-
Version: 4.0
|
|
3
|
+
Version: 4.4.0
|
|
4
4
|
Summary: ORM designed to interact with the database (currently with MySQL) using lambda functions and nested functions
|
|
5
5
|
Author: p-hzamora
|
|
6
6
|
Author-email: p.hzamora@icloud.com
|
|
7
7
|
Requires-Python: >=3.12,<4.0
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
11
10
|
Requires-Dist: mysql-connector-python (>=9.0.0,<10.0.0)
|
|
12
11
|
Requires-Dist: shapely (>=2.0.6,<3.0.0)
|
|
13
12
|
Description-Content-Type: text/markdown
|
|
@@ -3,7 +3,7 @@ line-length = 320
|
|
|
3
3
|
|
|
4
4
|
[tool.poetry]
|
|
5
5
|
name = "ormlambda"
|
|
6
|
-
version = "4.0
|
|
6
|
+
version = "4.4.0"
|
|
7
7
|
description = "ORM designed to interact with the database (currently with MySQL) using lambda functions and nested functions"
|
|
8
8
|
authors = ["p-hzamora <p.hzamora@icloud.com>"]
|
|
9
9
|
readme = "README.md"
|
|
@@ -62,7 +62,6 @@ from .sql.sqltypes import POINT as POINT
|
|
|
62
62
|
|
|
63
63
|
|
|
64
64
|
from .sql.clauses import Alias as Alias
|
|
65
|
-
from .sql.clauses import Count as Count
|
|
66
65
|
from .sql.clauses import Delete as Delete
|
|
67
66
|
from .sql.clauses import GroupBy as GroupBy
|
|
68
67
|
from .sql.clauses import Insert as Insert
|
|
@@ -76,12 +75,9 @@ from .sql.clauses import Having as Having
|
|
|
76
75
|
from .sql.clauses import Update as Update
|
|
77
76
|
from .sql.clauses import Upsert as Upsert
|
|
78
77
|
|
|
79
|
-
from .sql
|
|
80
|
-
from .sql.functions import Min as Min
|
|
81
|
-
from .sql.functions import Concat as Concat
|
|
82
|
-
from .sql.functions import Sum as Sum
|
|
83
|
-
|
|
78
|
+
from .sql import functions as functions
|
|
84
79
|
|
|
80
|
+
from .sql.functions import * # noqa: F403
|
|
85
81
|
from . import util as _util
|
|
86
82
|
|
|
87
83
|
_util.import_prefix("ormlambda")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
class ConditionType(Enum):
|
|
4
|
+
class ConditionType(str, Enum):
|
|
5
5
|
EQUAL = "="
|
|
6
6
|
LESS_THAN = "<"
|
|
7
7
|
GREATER_THAN = ">"
|
|
@@ -15,3 +15,6 @@ class ConditionType(Enum):
|
|
|
15
15
|
NOT_IN = "NOT IN"
|
|
16
16
|
IS = "IS"
|
|
17
17
|
IS_NOT = "IS NOT"
|
|
18
|
+
|
|
19
|
+
def __str__(self):
|
|
20
|
+
return super().__str__()
|
|
@@ -18,16 +18,16 @@ class UnmatchedLambdaParameterError(Exception):
|
|
|
18
18
|
return f"Unmatched number of parameters in lambda function with the number of tables: Expected {self.expected_params} parameters but found {str(self.found_param)}."
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
class
|
|
21
|
+
class NotKeysInIFunctionError(Exception):
|
|
22
22
|
def __init__(self, match_regex: list[str], *args: object) -> None:
|
|
23
23
|
super().__init__(*args)
|
|
24
24
|
self._match_regex: list[str] = match_regex
|
|
25
25
|
|
|
26
26
|
def __str__(self) -> str:
|
|
27
|
-
return f"We cannot use placeholders in
|
|
27
|
+
return f"We cannot use placeholders in IFunction class. You used {self._match_regex}"
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
class
|
|
30
|
+
class FunctionFunctionError[T](Exception):
|
|
31
31
|
def __init__(self, clause: ClauseInfo[T], *args):
|
|
32
32
|
self.clause = clause
|
|
33
33
|
super().__init__(*args)
|
|
@@ -36,18 +36,18 @@ class AggregateFunctionError[T](Exception):
|
|
|
36
36
|
agg_methods = self.__get_all_aggregate_method(self.clause)
|
|
37
37
|
return f"You cannot use aggregation method like '{agg_methods}' to return model objects. Try specifying 'flavour' attribute as 'dict'."
|
|
38
38
|
|
|
39
|
-
@util.preload_module("ormlambda.sql.
|
|
39
|
+
@util.preload_module("ormlambda.sql.functions")
|
|
40
40
|
def __get_all_aggregate_method(self, clauses: list[ClauseInfo]) -> str:
|
|
41
41
|
"""
|
|
42
|
-
Get the class name of those classes that inherit from '
|
|
42
|
+
Get the class name of those classes that inherit from 'IFunction' class in order to create a better error message.
|
|
43
43
|
"""
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
IFunction = util.preloaded.sql_functions.IFunction
|
|
46
46
|
res: set[str] = set()
|
|
47
47
|
if not isinstance(clauses, tp.Iterable):
|
|
48
48
|
return clauses.__class__.__name__
|
|
49
49
|
for clause in clauses:
|
|
50
|
-
if isinstance(clause,
|
|
50
|
+
if isinstance(clause, IFunction):
|
|
51
51
|
res.add(clause.__class__.__name__)
|
|
52
52
|
return ", ".join(res)
|
|
53
53
|
|
|
@@ -56,5 +56,5 @@ class NotCallableError(ValueError):
|
|
|
56
56
|
def __init__(self, *args):
|
|
57
57
|
super().__init__(*args)
|
|
58
58
|
|
|
59
|
-
def __str__(self)->str:
|
|
60
|
-
return f"You must provide a function or callable to proceed with the query creation. Passed '{self.args[0].__class__.__name__}' "
|
|
59
|
+
def __str__(self) -> str:
|
|
60
|
+
return f"You must provide a function or callable to proceed with the query creation. Passed '{self.args[0].__class__.__name__}' "
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from types import ModuleType
|
|
3
|
-
from ormlambda import ColumnProxy, ForeignKey, TableProxy
|
|
3
|
+
from ormlambda import ColumnProxy, ForeignKey, TableProxy, Table, Column
|
|
4
4
|
from ormlambda.sql import compiler
|
|
5
5
|
from ormlambda.sql.clause_info import ClauseInfo
|
|
6
|
-
from ormlambda.common.errors import
|
|
6
|
+
from ormlambda.common.errors import NotKeysInIFunctionError
|
|
7
7
|
from ormlambda.sql.types import ASTERISK
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
from .. import default
|
|
11
|
-
from typing import TYPE_CHECKING, Any, Iterable
|
|
11
|
+
from typing import TYPE_CHECKING, Any, Iterable, Type
|
|
12
|
+
from ormlambda.sql.comparer import Comparer, ComparerCluster
|
|
12
13
|
|
|
13
14
|
if TYPE_CHECKING:
|
|
15
|
+
from ormlambda.sql.functions.interface import IFunction
|
|
16
|
+
from ormlambda.sql.types import ColumnType
|
|
14
17
|
from test.test_clause_info import ST_Contains
|
|
15
18
|
from ormlambda import JoinSelector
|
|
16
|
-
from ormlambda.sql.comparer import Comparer
|
|
17
19
|
from ormlambda.sql.column.column import Column
|
|
18
20
|
from mysql import connector
|
|
19
21
|
from ormlambda.dialects.mysql.clauses import ST_AsText
|
|
@@ -70,7 +72,6 @@ if TYPE_CHECKING:
|
|
|
70
72
|
Update,
|
|
71
73
|
Limit,
|
|
72
74
|
Offset,
|
|
73
|
-
Count,
|
|
74
75
|
Where,
|
|
75
76
|
Having,
|
|
76
77
|
Order,
|
|
@@ -78,10 +79,21 @@ if TYPE_CHECKING:
|
|
|
78
79
|
)
|
|
79
80
|
|
|
80
81
|
from ormlambda.sql.functions import (
|
|
81
|
-
Concat,
|
|
82
82
|
Max,
|
|
83
83
|
Min,
|
|
84
|
+
Concat,
|
|
84
85
|
Sum,
|
|
86
|
+
Count,
|
|
87
|
+
Avg,
|
|
88
|
+
Abs,
|
|
89
|
+
Ceil,
|
|
90
|
+
Floor,
|
|
91
|
+
Round,
|
|
92
|
+
Pow,
|
|
93
|
+
Sqrt,
|
|
94
|
+
Mod,
|
|
95
|
+
Rand,
|
|
96
|
+
Truncate,
|
|
85
97
|
)
|
|
86
98
|
|
|
87
99
|
|
|
@@ -129,6 +141,12 @@ class MySQLCompiler(compiler.SQLCompiler):
|
|
|
129
141
|
column.alias = clause_info.alias_clause
|
|
130
142
|
return clause_info.query(self.dialect)
|
|
131
143
|
|
|
144
|
+
def visit_comparer_cluster(self, cluster: ComparerCluster, **kw) -> str:
|
|
145
|
+
c1 = cluster.left_comparer.compile(self.dialect, **kw).string
|
|
146
|
+
c2 = cluster.right_comparer.compile(self.dialect, **kw).string
|
|
147
|
+
|
|
148
|
+
return f"{c1} {cluster.join} {c2}"
|
|
149
|
+
|
|
132
150
|
def visit_comparer(self, comparer: Comparer, **kwargs) -> str:
|
|
133
151
|
from ormlambda.sql.comparer import CleanValue, Comparer
|
|
134
152
|
|
|
@@ -149,6 +167,48 @@ class MySQLCompiler(compiler.SQLCompiler):
|
|
|
149
167
|
|
|
150
168
|
return f"{lcond} {comparer.compare} {rcond}"
|
|
151
169
|
|
|
170
|
+
def visit_where(self, where: Where) -> str:
|
|
171
|
+
assert (n := len(where.comparers)) == len(where.restrictive)
|
|
172
|
+
|
|
173
|
+
if not where.comparers:
|
|
174
|
+
return ""
|
|
175
|
+
|
|
176
|
+
cond = []
|
|
177
|
+
|
|
178
|
+
for i in range(n):
|
|
179
|
+
comp = where.comparers[i]
|
|
180
|
+
|
|
181
|
+
string = comp.compile(self.dialect).string
|
|
182
|
+
|
|
183
|
+
condition = f"({string})" if isinstance(comp, ComparerCluster) else string
|
|
184
|
+
|
|
185
|
+
union = f" {where.restrictive[i + 1]} " if i != n - 1 else ""
|
|
186
|
+
|
|
187
|
+
condition += union
|
|
188
|
+
cond.append(condition)
|
|
189
|
+
return f" WHERE {"".join(cond)}"
|
|
190
|
+
|
|
191
|
+
def visit_having(self, having: Having) -> str:
|
|
192
|
+
assert (n := len(having.comparers)) == len(having.restrictive)
|
|
193
|
+
|
|
194
|
+
if not having.comparers:
|
|
195
|
+
return ""
|
|
196
|
+
|
|
197
|
+
cond = []
|
|
198
|
+
|
|
199
|
+
for i in range(n):
|
|
200
|
+
comp = having.comparers[i]
|
|
201
|
+
|
|
202
|
+
string = comp.compile(self.dialect).string
|
|
203
|
+
|
|
204
|
+
condition = f"({string})" if isinstance(comp, ComparerCluster) else string
|
|
205
|
+
|
|
206
|
+
union = f" {having.restrictive[i + 1]} " if i != n - 1 else ""
|
|
207
|
+
|
|
208
|
+
condition += union
|
|
209
|
+
cond.append(condition)
|
|
210
|
+
return f" HAVING {"".join(cond)}"
|
|
211
|
+
|
|
152
212
|
def visit_join(self, join: JoinSelector) -> str:
|
|
153
213
|
rt = join.rcon.table
|
|
154
214
|
rtable = TableProxy(table_class=rt, path=join.rcon.path)
|
|
@@ -193,18 +253,191 @@ class MySQLCompiler(compiler.SQLCompiler):
|
|
|
193
253
|
def visit_limit(self, limit: Limit, **kw):
|
|
194
254
|
return f"LIMIT {limit._number}"
|
|
195
255
|
|
|
196
|
-
# TODOH []: include the rest of visit methods
|
|
197
|
-
def visit_insert(self, insert: Insert
|
|
198
|
-
|
|
256
|
+
# TODOH [x]: include the rest of visit methods
|
|
257
|
+
def visit_insert[T, TProp](self, insert: Insert) -> str:
|
|
258
|
+
def __is_valid[TProp](column: Column[TProp], value: TProp) -> bool:
|
|
259
|
+
"""
|
|
260
|
+
We want to delete the column from table when it's specified with an 'AUTO_INCREMENT' or 'AUTO GENERATED ALWAYS AS (__) STORED' statement.
|
|
261
|
+
|
|
262
|
+
if the column is auto-generated, it means the database creates the value for that column, so we must deleted it.
|
|
263
|
+
if the column is primary key and auto-increment, we should be able to create an object with specific pk value.
|
|
264
|
+
|
|
265
|
+
RETURN
|
|
266
|
+
-----
|
|
267
|
+
|
|
268
|
+
- True -> Do not delete the column from dict query
|
|
269
|
+
- False -> Delete the column from dict query
|
|
270
|
+
"""
|
|
271
|
+
|
|
272
|
+
is_pk_none_and_auto_increment: bool = all([value is None, column.is_primary_key, column.is_auto_increment])
|
|
273
|
+
|
|
274
|
+
if is_pk_none_and_auto_increment or column.is_auto_generated:
|
|
275
|
+
return False
|
|
276
|
+
return True
|
|
277
|
+
|
|
278
|
+
def __fill_dict_list(list_dict: list[str, TProp], values: list[T]) -> list[Column]:
|
|
279
|
+
if isinstance(values, Iterable):
|
|
280
|
+
for x in values:
|
|
281
|
+
__fill_dict_list(list_dict, x)
|
|
282
|
+
|
|
283
|
+
elif issubclass(values.__class__, Table):
|
|
284
|
+
new_list = []
|
|
285
|
+
for prop in type(values).__dict__.values():
|
|
286
|
+
if not isinstance(prop, Column):
|
|
287
|
+
continue
|
|
288
|
+
|
|
289
|
+
value = getattr(values, prop.column_name)
|
|
290
|
+
if __is_valid(prop, value):
|
|
291
|
+
new_list.append(prop)
|
|
292
|
+
list_dict.append(new_list)
|
|
293
|
+
|
|
294
|
+
else:
|
|
295
|
+
raise Exception(f"Tipo de dato'{type(values)}' no esperado")
|
|
296
|
+
return None
|
|
297
|
+
|
|
298
|
+
if not isinstance(insert.values, Iterable):
|
|
299
|
+
values = (insert.values,)
|
|
300
|
+
else:
|
|
301
|
+
values = insert.values
|
|
302
|
+
valid_cols: list[list[Column]] = []
|
|
303
|
+
__fill_dict_list(valid_cols, values)
|
|
304
|
+
|
|
305
|
+
col_names: list[str] = []
|
|
306
|
+
wildcards: list[str] = []
|
|
307
|
+
col_values: list[list[str]] = []
|
|
308
|
+
for i, cols in enumerate(valid_cols):
|
|
309
|
+
col_values.append([])
|
|
310
|
+
CASTER = self.dialect.caster()
|
|
311
|
+
for col in cols:
|
|
312
|
+
clean_data = CASTER.for_column(col, insert.values[i]) # .resolve(insert.values[i][col])
|
|
313
|
+
if i == 0:
|
|
314
|
+
col_names.append(col.column_name)
|
|
315
|
+
wildcards.append(clean_data.wildcard_to_insert())
|
|
316
|
+
# COMMENT: avoid MySQLWriteCastBase.resolve when using PLACEHOLDERs
|
|
317
|
+
col_values[-1].append(clean_data.to_database)
|
|
318
|
+
|
|
319
|
+
join_cols = ", ".join(col_names)
|
|
320
|
+
unknown_rows = f"({', '.join(wildcards)})" # The number of "%s" must match the dict 'dicc_0' length
|
|
321
|
+
|
|
322
|
+
insert.cleaned_values = [tuple(x) for x in col_values]
|
|
323
|
+
query = f"INSERT INTO {insert.table.__table_name__} {f'({join_cols})'} VALUES {unknown_rows}"
|
|
324
|
+
return query
|
|
199
325
|
|
|
200
326
|
def visit_delete(self, delete: Delete, **kw) -> str:
|
|
201
|
-
|
|
327
|
+
return f"DELETE FROM {delete.table.__table_name__}"
|
|
202
328
|
|
|
203
329
|
def visit_upsert(self, upsert: Upsert, **kw) -> str:
|
|
204
|
-
|
|
330
|
+
"""
|
|
331
|
+
Generate MySQL UPSERT query using INSERT ... ON DUPLICATE KEY UPDATE.
|
|
332
|
+
|
|
333
|
+
Works with both single dictionaries and lists of changes. The function only
|
|
334
|
+
generates placeholders and column names - actual values are bound separately.
|
|
335
|
+
|
|
336
|
+
Parameters
|
|
337
|
+
----------
|
|
338
|
+
upsert : Upsert
|
|
339
|
+
Upsert object containing table and values information
|
|
340
|
+
|
|
341
|
+
Returns
|
|
342
|
+
-------
|
|
343
|
+
str
|
|
344
|
+
SQL query string with placeholders
|
|
345
|
+
|
|
346
|
+
Examples
|
|
347
|
+
--------
|
|
348
|
+
Generated SQL:
|
|
349
|
+
INSERT INTO users (id, name, email)
|
|
350
|
+
VALUES (%s, %s, %s)
|
|
351
|
+
ON DUPLICATE KEY UPDATE
|
|
352
|
+
name = VALUES(name),
|
|
353
|
+
email = VALUES(email);
|
|
354
|
+
|
|
355
|
+
Actual execution with bound values:
|
|
356
|
+
[(1, 'Pablo', 'pablo@example.com'),
|
|
357
|
+
(2, 'Marina', 'marina@example.com')]
|
|
358
|
+
|
|
359
|
+
Notes
|
|
360
|
+
-----
|
|
361
|
+
- Primary key column is excluded from the UPDATE clause
|
|
362
|
+
- Uses MySQL's VALUES() function to reference inserted values
|
|
363
|
+
- For batch inserts, all rows must have the same column structure
|
|
364
|
+
"""
|
|
365
|
+
|
|
366
|
+
# Generate the INSERT portion of the query
|
|
367
|
+
query_insert = self.visit_insert(upsert)
|
|
368
|
+
|
|
369
|
+
# MySQL uses VALUES() function to reference inserted values in UPDATE clause
|
|
370
|
+
VALUES_FUNCTION = "VALUES"
|
|
371
|
+
|
|
372
|
+
# Validate that we have values to work with
|
|
373
|
+
if not upsert.values or len(upsert.values) == 0:
|
|
374
|
+
raise ValueError("Upsert requires at least one value dictionary")
|
|
375
|
+
|
|
376
|
+
# Get column information from first value (all rows should have same structure)
|
|
377
|
+
first_value = upsert.values[0]
|
|
378
|
+
columns = first_value.get_columns()
|
|
379
|
+
pk_column = first_value.get_pk().column_name
|
|
380
|
+
|
|
381
|
+
# Build UPDATE clause: exclude primary key from updates
|
|
382
|
+
# Format: column = VALUES(column)
|
|
383
|
+
# fmt: off
|
|
384
|
+
update_columns = [
|
|
385
|
+
f"{col} = {VALUES_FUNCTION}({col})"
|
|
386
|
+
for col in columns
|
|
387
|
+
if col != pk_column
|
|
388
|
+
]
|
|
389
|
+
# fmt: on
|
|
390
|
+
|
|
391
|
+
# Handle edge case: if only PK exists, nothing to update
|
|
392
|
+
if not update_columns:
|
|
393
|
+
raise ValueError(f"No columns to update besides primary key '{pk_column}'. " "UPSERT requires at least one non-PK column.")
|
|
394
|
+
|
|
395
|
+
update_clause = ", ".join(update_columns)
|
|
396
|
+
|
|
397
|
+
return f"{query_insert} ON DUPLICATE KEY UPDATE {update_clause};"
|
|
205
398
|
|
|
206
399
|
def visit_update(self, update: Update, **kw) -> str:
|
|
207
|
-
|
|
400
|
+
class UpdateKeyError(KeyError):
|
|
401
|
+
def __init__(self, table: Type[Table], key: str | ColumnType, *args):
|
|
402
|
+
super().__init__(*args)
|
|
403
|
+
self._table: Type[Table] = table
|
|
404
|
+
self._key: str | ColumnType = key
|
|
405
|
+
|
|
406
|
+
def __str__(self):
|
|
407
|
+
BASE_MSSG = lambda col: f"The column '{col}' does not belong to the table '{self._table.__table_name__}'" # noqa: E731
|
|
408
|
+
|
|
409
|
+
if isinstance(self._key, Column):
|
|
410
|
+
return BASE_MSSG(self._key.column_name) + f"; it belongs to the table '{self._key.table.__table_name__}'. Please check the columns in the query."
|
|
411
|
+
return BASE_MSSG(self._key) + ". Please check the columns in the query."
|
|
412
|
+
|
|
413
|
+
def __is_valid__(col: Column) -> bool:
|
|
414
|
+
if update.table is not col.table:
|
|
415
|
+
raise UpdateKeyError(update.table, col)
|
|
416
|
+
return not col.is_auto_generated
|
|
417
|
+
|
|
418
|
+
if not isinstance(update.values, dict):
|
|
419
|
+
raise TypeError
|
|
420
|
+
|
|
421
|
+
col_names: list[Column] = []
|
|
422
|
+
CASTER = self.dialect.caster()
|
|
423
|
+
for col, value in update.values.items():
|
|
424
|
+
if isinstance(col, str):
|
|
425
|
+
if not hasattr(update.table, col):
|
|
426
|
+
raise UpdateKeyError(update.table, col)
|
|
427
|
+
col = getattr(update.table, col)
|
|
428
|
+
if not isinstance(col, Column | ColumnProxy):
|
|
429
|
+
raise ValueError
|
|
430
|
+
|
|
431
|
+
if __is_valid__(col):
|
|
432
|
+
clean_data = CASTER.for_value(value)
|
|
433
|
+
col_names.append((col.column_name, clean_data.wildcard_to_insert()))
|
|
434
|
+
update.cleaned_values.append(clean_data.to_database)
|
|
435
|
+
|
|
436
|
+
set_query: str = ",".join(["=".join(col_data) for col_data in col_names])
|
|
437
|
+
|
|
438
|
+
query = f"UPDATE {update.table.__table_name__} SET {set_query}"
|
|
439
|
+
update.cleaned_values = tuple(update.cleaned_values)
|
|
440
|
+
return query
|
|
208
441
|
|
|
209
442
|
def visit_offset(self, offset: Offset, **kw) -> str:
|
|
210
443
|
return f"OFFSET {offset._number}"
|
|
@@ -221,20 +454,6 @@ class MySQLCompiler(compiler.SQLCompiler):
|
|
|
221
454
|
|
|
222
455
|
return ClauseInfo.concat_alias_and_column(f"COUNT({column})", count.alias)
|
|
223
456
|
|
|
224
|
-
def visit_where(self, where: Where) -> str:
|
|
225
|
-
from ormlambda.sql.comparer import Comparer
|
|
226
|
-
|
|
227
|
-
if not where.comparer:
|
|
228
|
-
return ""
|
|
229
|
-
cols = Comparer.join_comparers(list(where.comparer), restrictive=where.restrictive, dialect=self.dialect)
|
|
230
|
-
return f"WHERE {cols}"
|
|
231
|
-
|
|
232
|
-
def visit_having(self, having: Having) -> str:
|
|
233
|
-
from ormlambda.sql.comparer import Comparer
|
|
234
|
-
|
|
235
|
-
cols = Comparer.join_comparers(list(having.comparer), restrictive=having.restrictive, dialect=self.dialect, table=None, literal=True)
|
|
236
|
-
return f"HAVING {cols}"
|
|
237
|
-
|
|
238
457
|
def visit_order(self, order: Order, **kw) -> str:
|
|
239
458
|
ORDER = "ORDER BY"
|
|
240
459
|
string_columns: list[str] = []
|
|
@@ -285,25 +504,60 @@ class MySQLCompiler(compiler.SQLCompiler):
|
|
|
285
504
|
)
|
|
286
505
|
return clause_info.query(self.dialect)
|
|
287
506
|
|
|
288
|
-
def visit_max(self,
|
|
507
|
+
def visit_max(self, obj: Max, **kw) -> str:
|
|
508
|
+
return self._compile_aggregate_method("MAX", obj, **kw)
|
|
509
|
+
|
|
510
|
+
def visit_min(self, obj: Min, **kw) -> str:
|
|
511
|
+
return self._compile_aggregate_method("MIN", obj, **kw)
|
|
512
|
+
|
|
513
|
+
def visit_sum(self, obj: Sum, **kw) -> str:
|
|
514
|
+
return self._compile_aggregate_method("SUM", obj, **kw)
|
|
515
|
+
|
|
516
|
+
def visit_avg(self, obj: Avg, **kw) -> str:
|
|
517
|
+
return self._compile_aggregate_method("AVG", obj, **kw)
|
|
518
|
+
|
|
519
|
+
def visit_abs(self, obj: Abs, **kw) -> str:
|
|
520
|
+
return self._compile_aggregate_method("ABS", obj, **kw)
|
|
521
|
+
|
|
522
|
+
def visit_ceil(self, obj: Ceil, **kw) -> str:
|
|
523
|
+
return self._compile_aggregate_method("CEIL", obj, **kw)
|
|
524
|
+
|
|
525
|
+
def visit_floor(self, obj: Floor, **kw) -> str:
|
|
526
|
+
return self._compile_aggregate_method("FLOOR", obj, **kw)
|
|
527
|
+
|
|
528
|
+
def visit_round(self, obj: Round, **kw) -> str:
|
|
529
|
+
return self._compile_aggregate_method("ROUND", obj, **kw)
|
|
530
|
+
|
|
531
|
+
def visit_pow(self, obj: Pow, **kw) -> str:
|
|
289
532
|
attr = {**kw, "alias_clause": None}
|
|
290
|
-
column =
|
|
291
|
-
return ClauseInfo.concat_alias_and_column(f"
|
|
533
|
+
column = obj.column.compile(self.dialect, **attr).string
|
|
534
|
+
return ClauseInfo.concat_alias_and_column(f"POW({column}, {obj._exponent})", obj.alias)
|
|
535
|
+
|
|
536
|
+
def visit_sqrt(self, obj: Sqrt, **kw) -> str:
|
|
537
|
+
return self._compile_aggregate_method("SQRT", obj, **kw)
|
|
538
|
+
|
|
539
|
+
def visit_mod(self, obj: Mod, **kw) -> str:
|
|
540
|
+
attr = {**kw, "alias_clause": None}
|
|
541
|
+
column = obj.column.compile(self.dialect, **attr).string
|
|
542
|
+
return ClauseInfo.concat_alias_and_column(f"MOD({column}, {obj._divisor})", obj.alias)
|
|
543
|
+
|
|
544
|
+
def visit_rand(self, obj: Rand, **kw) -> str:
|
|
545
|
+
return self._compile_aggregate_method("RAND", obj, **kw)
|
|
292
546
|
|
|
293
|
-
def
|
|
547
|
+
def visit_truncate(self, obj: Truncate, **kw) -> str:
|
|
294
548
|
attr = {**kw, "alias_clause": None}
|
|
295
|
-
column =
|
|
296
|
-
return ClauseInfo.concat_alias_and_column(f"
|
|
549
|
+
column = obj.column.compile(self.dialect, **attr).string
|
|
550
|
+
return ClauseInfo.concat_alias_and_column(f"TRUNCATE({column}, {obj._decimal})", obj.alias)
|
|
297
551
|
|
|
298
|
-
def
|
|
552
|
+
def _compile_aggregate_method(self, name: str, function: IFunction, **kw) -> str:
|
|
299
553
|
attr = {**kw, "alias_clause": None}
|
|
300
|
-
column =
|
|
301
|
-
return ClauseInfo.concat_alias_and_column(f"
|
|
554
|
+
column = function.column.compile(self.dialect, **attr).string
|
|
555
|
+
return ClauseInfo.concat_alias_and_column(f"{name}({column})", function.alias)
|
|
302
556
|
|
|
303
557
|
def visit_st_astext(self, st_astext: ST_AsText) -> str:
|
|
304
|
-
# avoid use placeholder when using
|
|
558
|
+
# avoid use placeholder when using IFunction because no make sense.
|
|
305
559
|
if st_astext.alias and (found := ClauseInfo._keyRegex.findall(st_astext.alias)):
|
|
306
|
-
raise
|
|
560
|
+
raise NotKeysInIFunctionError(found)
|
|
307
561
|
return ClauseInfo.concat_alias_and_column(f"ST_AsText({st_astext.column.compile(self.dialect, alias_clause=None).string})", st_astext.alias)
|
|
308
562
|
|
|
309
563
|
def visit_st_contains(self, st_contains: ST_Contains) -> str:
|
|
@@ -29,4 +29,8 @@ class IterableCaster[TType](JsonCaster[TType]):
|
|
|
29
29
|
@property
|
|
30
30
|
@BaseCaster.return_value_if_exists
|
|
31
31
|
def string_data(self) -> Optional[str]:
|
|
32
|
-
|
|
32
|
+
value = tuple(self.value) if not isinstance(self.value, tuple) else self.value
|
|
33
|
+
|
|
34
|
+
if len(value) == 1:
|
|
35
|
+
return f"({value[0]})"
|
|
36
|
+
return str(value)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from ormlambda.sql.
|
|
2
|
+
from ormlambda.sql.functions.interface import IFunction
|
|
3
3
|
from ormlambda.sql.types import ColumnType, AliasType
|
|
4
4
|
|
|
5
5
|
from ormlambda.sql.elements import ClauseElement
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class ST_AsText[T, TProp](ClauseElement,
|
|
8
|
+
class ST_AsText[T, TProp](ClauseElement, IFunction):
|
|
9
9
|
"""
|
|
10
10
|
https://dev.mysql.com/doc/refman/8.4/en/fetching-spatial-data.html
|
|
11
11
|
|
|
@@ -5,11 +5,11 @@ from shapely import Point
|
|
|
5
5
|
|
|
6
6
|
from ormlambda import Column
|
|
7
7
|
from ormlambda.sql.types import ColumnType, AliasType
|
|
8
|
-
from ormlambda.sql.
|
|
8
|
+
from ormlambda.sql.functions.interface import IFunction
|
|
9
9
|
from ormlambda.sql.elements import ClauseElement
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class ST_Contains(ClauseElement,
|
|
12
|
+
class ST_Contains(ClauseElement, IFunction):
|
|
13
13
|
__visit_name__ = "st_contains"
|
|
14
14
|
|
|
15
15
|
def __init__[TProp: Column](
|
|
@@ -17,10 +17,10 @@ class NoSuchModuleError(Exception):
|
|
|
17
17
|
return f"NoSuchModuleError: {self.args[0]}"
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
class
|
|
20
|
+
class DuplicatedClauseNameError(Exception):
|
|
21
21
|
def __init__(self, names: tuple[str], **kw):
|
|
22
22
|
self.names = names
|
|
23
23
|
super().__init__(**kw)
|
|
24
24
|
|
|
25
25
|
def __str__(self):
|
|
26
|
-
return f"Some clauses has the same alias. {self.names}\nTry wrapping the clause with the 'Alias' class first or setting '
|
|
26
|
+
return f"Some clauses has the same alias. {self.names}\nTry wrapping the clause with the 'Alias' class first or setting 'avoid_duplicates' param as 'True'"
|
|
@@ -130,7 +130,8 @@ class Response[TFlavour, *Ts]:
|
|
|
130
130
|
for i, data in enumerate(row):
|
|
131
131
|
alias = self._columns[i]
|
|
132
132
|
clause = self._select[alias]
|
|
133
|
-
|
|
133
|
+
dtype = clause.dtype if hasattr(clause, "dtype") else None
|
|
134
|
+
parse_data = self._caster.for_value(data, value_type=dtype).from_database
|
|
134
135
|
new_row.append(parse_data)
|
|
135
136
|
new_row = tuple(new_row)
|
|
136
137
|
if not isinstance(new_row, tuple):
|