SQLAlchemy 2.0.48__py3-none-any.whl → 2.0.50__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.
- sqlalchemy/__init__.py +1 -1
- sqlalchemy/connectors/aioodbc.py +10 -1
- sqlalchemy/dialects/_typing.py +0 -1
- sqlalchemy/dialects/mssql/__init__.py +0 -1
- sqlalchemy/dialects/mssql/base.py +40 -29
- sqlalchemy/dialects/mssql/information_schema.py +0 -1
- sqlalchemy/dialects/mssql/pymssql.py +1 -0
- sqlalchemy/dialects/mssql/pyodbc.py +0 -1
- sqlalchemy/dialects/mysql/aiomysql.py +26 -2
- sqlalchemy/dialects/mysql/asyncmy.py +25 -3
- sqlalchemy/dialects/mysql/base.py +6 -4
- sqlalchemy/dialects/mysql/cymysql.py +1 -0
- sqlalchemy/dialects/mysql/dml.py +0 -1
- sqlalchemy/dialects/mysql/mariadbconnector.py +1 -0
- sqlalchemy/dialects/mysql/mysqlconnector.py +3 -2
- sqlalchemy/dialects/mysql/mysqldb.py +1 -0
- sqlalchemy/dialects/mysql/pymysql.py +30 -9
- sqlalchemy/dialects/mysql/pyodbc.py +1 -0
- sqlalchemy/dialects/mysql/reflection.py +2 -2
- sqlalchemy/dialects/oracle/base.py +4 -0
- sqlalchemy/dialects/oracle/cx_oracle.py +1 -0
- sqlalchemy/dialects/oracle/oracledb.py +1 -0
- sqlalchemy/dialects/oracle/vector.py +2 -4
- sqlalchemy/dialects/postgresql/__init__.py +0 -1
- sqlalchemy/dialects/postgresql/_psycopg_common.py +36 -0
- sqlalchemy/dialects/postgresql/asyncpg.py +4 -1
- sqlalchemy/dialects/postgresql/base.py +39 -4
- sqlalchemy/dialects/postgresql/dml.py +0 -1
- sqlalchemy/dialects/postgresql/ext.py +6 -0
- sqlalchemy/dialects/postgresql/hstore.py +0 -1
- sqlalchemy/dialects/postgresql/operators.py +0 -1
- sqlalchemy/dialects/postgresql/pg8000.py +3 -5
- sqlalchemy/dialects/postgresql/provision.py +3 -2
- sqlalchemy/dialects/postgresql/psycopg.py +22 -38
- sqlalchemy/dialects/postgresql/psycopg2.py +3 -29
- sqlalchemy/dialects/postgresql/psycopg2cffi.py +1 -0
- sqlalchemy/dialects/sqlite/aiosqlite.py +1 -0
- sqlalchemy/dialects/sqlite/base.py +8 -11
- sqlalchemy/dialects/sqlite/pysqlcipher.py +11 -7
- sqlalchemy/dialects/sqlite/pysqlite.py +1 -0
- sqlalchemy/engine/_py_processors.py +0 -1
- sqlalchemy/engine/base.py +3 -1
- sqlalchemy/engine/create.py +1 -1
- sqlalchemy/engine/cursor.py +5 -2
- sqlalchemy/engine/interfaces.py +1 -1
- sqlalchemy/engine/mock.py +0 -1
- sqlalchemy/engine/processors.py +1 -0
- sqlalchemy/engine/reflection.py +1 -0
- sqlalchemy/engine/result.py +32 -9
- sqlalchemy/event/api.py +1 -1
- sqlalchemy/event/attr.py +1 -0
- sqlalchemy/event/base.py +1 -0
- sqlalchemy/event/legacy.py +1 -0
- sqlalchemy/event/registry.py +1 -0
- sqlalchemy/exc.py +1 -0
- sqlalchemy/ext/__init__.py +0 -1
- sqlalchemy/ext/associationproxy.py +1 -0
- sqlalchemy/ext/asyncio/engine.py +1 -1
- sqlalchemy/ext/asyncio/session.py +0 -1
- sqlalchemy/ext/automap.py +1 -0
- sqlalchemy/ext/baked.py +0 -1
- sqlalchemy/ext/compiler.py +1 -0
- sqlalchemy/ext/declarative/extensions.py +1 -0
- sqlalchemy/ext/horizontal_shard.py +1 -0
- sqlalchemy/ext/hybrid.py +1 -1
- sqlalchemy/ext/instrumentation.py +1 -1
- sqlalchemy/ext/mutable.py +2 -2
- sqlalchemy/ext/mypy/plugin.py +1 -0
- sqlalchemy/ext/orderinglist.py +1 -0
- sqlalchemy/ext/serializer.py +0 -1
- sqlalchemy/future/__init__.py +1 -0
- sqlalchemy/inspection.py +1 -0
- sqlalchemy/log.py +2 -2
- sqlalchemy/orm/collections.py +1 -0
- sqlalchemy/orm/context.py +1 -1
- sqlalchemy/orm/decl_api.py +14 -11
- sqlalchemy/orm/dependency.py +1 -1
- sqlalchemy/orm/descriptor_props.py +1 -1
- sqlalchemy/orm/dynamic.py +9 -3
- sqlalchemy/orm/evaluator.py +0 -1
- sqlalchemy/orm/events.py +1 -0
- sqlalchemy/orm/instrumentation.py +0 -1
- sqlalchemy/orm/loading.py +2 -2
- sqlalchemy/orm/mapper.py +1 -0
- sqlalchemy/orm/path_registry.py +19 -4
- sqlalchemy/orm/persistence.py +1 -0
- sqlalchemy/orm/properties.py +3 -4
- sqlalchemy/orm/query.py +8 -1
- sqlalchemy/orm/relationships.py +25 -18
- sqlalchemy/orm/session.py +29 -8
- sqlalchemy/orm/strategies.py +14 -2
- sqlalchemy/orm/strategy_options.py +20 -2
- sqlalchemy/orm/unitofwork.py +2 -3
- sqlalchemy/orm/util.py +4 -0
- sqlalchemy/pool/impl.py +1 -0
- sqlalchemy/sql/_orm_types.py +0 -1
- sqlalchemy/sql/_py_util.py +1 -1
- sqlalchemy/sql/base.py +0 -1
- sqlalchemy/sql/compiler.py +5 -1
- sqlalchemy/sql/crud.py +2 -1
- sqlalchemy/sql/ddl.py +1 -0
- sqlalchemy/sql/dml.py +4 -0
- sqlalchemy/sql/elements.py +0 -1
- sqlalchemy/sql/expression.py +0 -1
- sqlalchemy/sql/functions.py +92 -51
- sqlalchemy/sql/lambdas.py +1 -2
- sqlalchemy/sql/schema.py +7 -5
- sqlalchemy/sql/selectable.py +1 -2
- sqlalchemy/sql/sqltypes.py +1 -1
- sqlalchemy/sql/traversals.py +0 -1
- sqlalchemy/sql/util.py +2 -1
- sqlalchemy/testing/engines.py +0 -1
- sqlalchemy/testing/fixtures/mypy.py +15 -16
- sqlalchemy/testing/plugin/bootstrap.py +0 -1
- sqlalchemy/testing/plugin/pytestplugin.py +2 -5
- sqlalchemy/testing/profiling.py +0 -1
- sqlalchemy/testing/provision.py +0 -1
- sqlalchemy/testing/suite/test_reflection.py +0 -1
- sqlalchemy/testing/util.py +0 -1
- sqlalchemy/types.py +0 -1
- sqlalchemy/util/_collections.py +1 -1
- sqlalchemy/util/_py_collections.py +5 -5
- sqlalchemy/util/compat.py +1 -2
- sqlalchemy/util/langhelpers.py +17 -14
- sqlalchemy/util/preloaded.py +1 -0
- sqlalchemy/util/queue.py +1 -1
- sqlalchemy/util/tool_support.py +1 -0
- sqlalchemy/util/typing.py +1 -1
- {sqlalchemy-2.0.48.dist-info → sqlalchemy-2.0.50.dist-info}/METADATA +1 -1
- {sqlalchemy-2.0.48.dist-info → sqlalchemy-2.0.50.dist-info}/RECORD +133 -133
- {sqlalchemy-2.0.48.dist-info → sqlalchemy-2.0.50.dist-info}/WHEEL +1 -1
- {sqlalchemy-2.0.48.dist-info → sqlalchemy-2.0.50.dist-info}/licenses/LICENSE +0 -0
- {sqlalchemy-2.0.48.dist-info → sqlalchemy-2.0.50.dist-info}/top_level.txt +0 -0
sqlalchemy/__init__.py
CHANGED
sqlalchemy/connectors/aioodbc.py
CHANGED
|
@@ -20,7 +20,6 @@ from .. import util
|
|
|
20
20
|
from ..util.concurrency import await_fallback
|
|
21
21
|
from ..util.concurrency import await_only
|
|
22
22
|
|
|
23
|
-
|
|
24
23
|
if TYPE_CHECKING:
|
|
25
24
|
from ..engine.interfaces import ConnectArgsType
|
|
26
25
|
from ..engine.url import URL
|
|
@@ -36,6 +35,14 @@ class AsyncAdapt_aioodbc_cursor(AsyncAdapt_dbapi_cursor):
|
|
|
36
35
|
# how it's supposed to work
|
|
37
36
|
# return self.await_(self._cursor.setinputsizes(*inputsizes))
|
|
38
37
|
|
|
38
|
+
@property
|
|
39
|
+
def fast_executemany(self):
|
|
40
|
+
return self._cursor._impl.fast_executemany
|
|
41
|
+
|
|
42
|
+
@fast_executemany.setter
|
|
43
|
+
def fast_executemany(self, value):
|
|
44
|
+
self._cursor._impl.fast_executemany = value
|
|
45
|
+
|
|
39
46
|
|
|
40
47
|
class AsyncAdapt_aioodbc_ss_cursor(
|
|
41
48
|
AsyncAdapt_aioodbc_cursor, AsyncAdapt_dbapi_ss_cursor
|
|
@@ -125,6 +132,7 @@ class AsyncAdapt_aioodbc_dbapi:
|
|
|
125
132
|
"ProgrammingError",
|
|
126
133
|
"InternalError",
|
|
127
134
|
"NotSupportedError",
|
|
135
|
+
"SQL_DRIVER_NAME",
|
|
128
136
|
"NUMBER",
|
|
129
137
|
"STRING",
|
|
130
138
|
"DATETIME",
|
|
@@ -133,6 +141,7 @@ class AsyncAdapt_aioodbc_dbapi:
|
|
|
133
141
|
"BinaryNull",
|
|
134
142
|
"SQL_VARCHAR",
|
|
135
143
|
"SQL_WVARCHAR",
|
|
144
|
+
"SQL_DECIMAL",
|
|
136
145
|
):
|
|
137
146
|
setattr(self, name, getattr(self.pyodbc, name))
|
|
138
147
|
|
sqlalchemy/dialects/_typing.py
CHANGED
|
@@ -18,7 +18,6 @@ from ..sql.schema import Column
|
|
|
18
18
|
from ..sql.schema import ColumnCollectionConstraint
|
|
19
19
|
from ..sql.schema import Index
|
|
20
20
|
|
|
21
|
-
|
|
22
21
|
_OnConflictConstraintT = Union[str, ColumnCollectionConstraint, Index, None]
|
|
23
22
|
_OnConflictIndexElementsT = Optional[
|
|
24
23
|
Iterable[Union[Column[Any], str, roles.DDLConstraintColumnRole]]
|
|
@@ -3220,8 +3220,7 @@ class MSDialect(default.DefaultDialect):
|
|
|
3220
3220
|
|
|
3221
3221
|
view_name = f"sys.{row[0]}"
|
|
3222
3222
|
|
|
3223
|
-
cursor.execute(
|
|
3224
|
-
"""
|
|
3223
|
+
cursor.execute("""
|
|
3225
3224
|
SELECT CASE transaction_isolation_level
|
|
3226
3225
|
WHEN 0 THEN NULL
|
|
3227
3226
|
WHEN 1 THEN 'READ UNCOMMITTED'
|
|
@@ -3232,10 +3231,7 @@ class MSDialect(default.DefaultDialect):
|
|
|
3232
3231
|
AS TRANSACTION_ISOLATION_LEVEL
|
|
3233
3232
|
FROM {}
|
|
3234
3233
|
where session_id = @@SPID
|
|
3235
|
-
""".format(
|
|
3236
|
-
view_name
|
|
3237
|
-
)
|
|
3238
|
-
)
|
|
3234
|
+
""".format(view_name))
|
|
3239
3235
|
except self.dbapi.Error as err:
|
|
3240
3236
|
raise NotImplementedError(
|
|
3241
3237
|
"Can't fetch isolation level; encountered error {} when "
|
|
@@ -3254,13 +3250,6 @@ class MSDialect(default.DefaultDialect):
|
|
|
3254
3250
|
self._setup_supports_comments(connection)
|
|
3255
3251
|
|
|
3256
3252
|
def _setup_version_attributes(self):
|
|
3257
|
-
if self.server_version_info[0] not in list(range(8, 17)):
|
|
3258
|
-
util.warn(
|
|
3259
|
-
"Unrecognized server version info '%s'. Some SQL Server "
|
|
3260
|
-
"features may not function properly."
|
|
3261
|
-
% ".".join(str(x) for x in self.server_version_info)
|
|
3262
|
-
)
|
|
3263
|
-
|
|
3264
3253
|
if self.server_version_info >= MS_2008_VERSION:
|
|
3265
3254
|
self.supports_multivalues_insert = True
|
|
3266
3255
|
else:
|
|
@@ -3439,8 +3428,7 @@ class MSDialect(default.DefaultDialect):
|
|
|
3439
3428
|
else "NULL as filter_definition"
|
|
3440
3429
|
)
|
|
3441
3430
|
rp = connection.execution_options(future_result=True).execute(
|
|
3442
|
-
sql.text(
|
|
3443
|
-
f"""
|
|
3431
|
+
sql.text(f"""
|
|
3444
3432
|
select
|
|
3445
3433
|
ind.index_id,
|
|
3446
3434
|
ind.is_unique,
|
|
@@ -3460,8 +3448,7 @@ where
|
|
|
3460
3448
|
and ind.type != 0
|
|
3461
3449
|
order by
|
|
3462
3450
|
ind.name
|
|
3463
|
-
"""
|
|
3464
|
-
)
|
|
3451
|
+
""")
|
|
3465
3452
|
.bindparams(
|
|
3466
3453
|
sql.bindparam("tabname", tablename, ischema.CoerceUnicode()),
|
|
3467
3454
|
sql.bindparam("schname", owner, ischema.CoerceUnicode()),
|
|
@@ -3489,8 +3476,7 @@ order by
|
|
|
3489
3476
|
do["mssql_where"] = row["filter_definition"]
|
|
3490
3477
|
|
|
3491
3478
|
rp = connection.execution_options(future_result=True).execute(
|
|
3492
|
-
sql.text(
|
|
3493
|
-
"""
|
|
3479
|
+
sql.text("""
|
|
3494
3480
|
select
|
|
3495
3481
|
ind_col.index_id,
|
|
3496
3482
|
col.name,
|
|
@@ -3510,8 +3496,7 @@ where
|
|
|
3510
3496
|
order by
|
|
3511
3497
|
ind_col.index_id,
|
|
3512
3498
|
ind_col.key_ordinal
|
|
3513
|
-
"""
|
|
3514
|
-
)
|
|
3499
|
+
""")
|
|
3515
3500
|
.bindparams(
|
|
3516
3501
|
sql.bindparam("tabname", tablename, ischema.CoerceUnicode()),
|
|
3517
3502
|
sql.bindparam("schname", owner, ischema.CoerceUnicode()),
|
|
@@ -3641,6 +3626,7 @@ order by
|
|
|
3641
3626
|
def get_columns(self, connection, tablename, dbname, owner, schema, **kw):
|
|
3642
3627
|
sys_columns = ischema.sys_columns
|
|
3643
3628
|
sys_types = ischema.sys_types
|
|
3629
|
+
sys_base_types = ischema.sys_types.alias("base_types")
|
|
3644
3630
|
sys_default_constraints = ischema.sys_default_constraints
|
|
3645
3631
|
computed_cols = ischema.computed_columns
|
|
3646
3632
|
identity_cols = ischema.identity_columns
|
|
@@ -3682,6 +3668,7 @@ order by
|
|
|
3682
3668
|
sql.select(
|
|
3683
3669
|
sys_columns.c.name,
|
|
3684
3670
|
sys_types.c.name,
|
|
3671
|
+
sys_base_types.c.name.label("base_type"),
|
|
3685
3672
|
sys_columns.c.is_nullable,
|
|
3686
3673
|
sys_columns.c.max_length,
|
|
3687
3674
|
sys_columns.c.precision,
|
|
@@ -3701,6 +3688,15 @@ order by
|
|
|
3701
3688
|
onclause=sys_columns.c.user_type_id
|
|
3702
3689
|
== sys_types.c.user_type_id,
|
|
3703
3690
|
)
|
|
3691
|
+
.outerjoin(
|
|
3692
|
+
sys_base_types,
|
|
3693
|
+
onclause=sql.and_(
|
|
3694
|
+
sys_types.c.system_type_id
|
|
3695
|
+
== sys_base_types.c.system_type_id,
|
|
3696
|
+
sys_base_types.c.user_type_id
|
|
3697
|
+
== sys_base_types.c.system_type_id,
|
|
3698
|
+
),
|
|
3699
|
+
)
|
|
3704
3700
|
.outerjoin(
|
|
3705
3701
|
sys_default_constraints,
|
|
3706
3702
|
sql.and_(
|
|
@@ -3747,6 +3743,7 @@ order by
|
|
|
3747
3743
|
for row in c.mappings():
|
|
3748
3744
|
name = row[sys_columns.c.name]
|
|
3749
3745
|
type_ = row[sys_types.c.name]
|
|
3746
|
+
base_type = row["base_type"]
|
|
3750
3747
|
nullable = row[sys_columns.c.is_nullable] == 1
|
|
3751
3748
|
maxlen = row[sys_columns.c.max_length]
|
|
3752
3749
|
numericprec = row[sys_columns.c.precision]
|
|
@@ -3760,7 +3757,17 @@ order by
|
|
|
3760
3757
|
identity_increment = row[identity_cols.c.increment_value]
|
|
3761
3758
|
comment = row[extended_properties.c.value]
|
|
3762
3759
|
|
|
3760
|
+
# Try to resolve the user type first (e.g., "sysname"),
|
|
3761
|
+
# then fall back to the base type (e.g., "nvarchar").
|
|
3762
|
+
# base_type may be None for CLR types (geography, geometry,
|
|
3763
|
+
# hierarchyid) which have no corresponding base type.
|
|
3763
3764
|
coltype = self.ischema_names.get(type_, None)
|
|
3765
|
+
if (
|
|
3766
|
+
coltype is None
|
|
3767
|
+
and base_type is not None
|
|
3768
|
+
and base_type != type_
|
|
3769
|
+
):
|
|
3770
|
+
coltype = self.ischema_names.get(base_type, None)
|
|
3764
3771
|
|
|
3765
3772
|
kwargs = {}
|
|
3766
3773
|
|
|
@@ -3788,10 +3795,16 @@ order by
|
|
|
3788
3795
|
kwargs["collation"] = collation
|
|
3789
3796
|
|
|
3790
3797
|
if coltype is None:
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3798
|
+
if base_type is not None and base_type != type_:
|
|
3799
|
+
util.warn(
|
|
3800
|
+
"Did not recognize type '%s' (user type) or '%s' "
|
|
3801
|
+
"(base type) of column '%s'" % (type_, base_type, name)
|
|
3802
|
+
)
|
|
3803
|
+
else:
|
|
3804
|
+
util.warn(
|
|
3805
|
+
"Did not recognize type '%s' of column '%s'"
|
|
3806
|
+
% (type_, name)
|
|
3807
|
+
)
|
|
3795
3808
|
coltype = sqltypes.NULLTYPE
|
|
3796
3809
|
else:
|
|
3797
3810
|
if issubclass(coltype, sqltypes.Numeric):
|
|
@@ -3909,8 +3922,7 @@ order by
|
|
|
3909
3922
|
):
|
|
3910
3923
|
# Foreign key constraints
|
|
3911
3924
|
s = (
|
|
3912
|
-
text(
|
|
3913
|
-
"""\
|
|
3925
|
+
text("""\
|
|
3914
3926
|
WITH fk_info AS (
|
|
3915
3927
|
SELECT
|
|
3916
3928
|
ischema_ref_con.constraint_schema,
|
|
@@ -4010,8 +4022,7 @@ index_info AS (
|
|
|
4010
4022
|
|
|
4011
4023
|
ORDER BY fk_info.constraint_schema, fk_info.constraint_name,
|
|
4012
4024
|
fk_info.ordinal_position
|
|
4013
|
-
"""
|
|
4014
|
-
)
|
|
4025
|
+
""")
|
|
4015
4026
|
.bindparams(
|
|
4016
4027
|
sql.bindparam("tablename", tablename, ischema.CoerceUnicode()),
|
|
4017
4028
|
sql.bindparam("owner", owner, ischema.CoerceUnicode()),
|
|
@@ -28,6 +28,7 @@ This dialect should normally be used only with the
|
|
|
28
28
|
)
|
|
29
29
|
|
|
30
30
|
""" # noqa
|
|
31
|
+
|
|
31
32
|
from __future__ import annotations
|
|
32
33
|
|
|
33
34
|
from types import ModuleType
|
|
@@ -38,6 +39,7 @@ from typing import Tuple
|
|
|
38
39
|
from typing import TYPE_CHECKING
|
|
39
40
|
from typing import Union
|
|
40
41
|
|
|
42
|
+
from .pymysql import _connection_ping_reconnects_true
|
|
41
43
|
from .pymysql import MySQLDialect_pymysql
|
|
42
44
|
from ... import pool
|
|
43
45
|
from ... import util
|
|
@@ -46,6 +48,7 @@ from ...connectors.asyncio import AsyncAdapt_dbapi_cursor
|
|
|
46
48
|
from ...connectors.asyncio import AsyncAdapt_dbapi_module
|
|
47
49
|
from ...connectors.asyncio import AsyncAdapt_dbapi_ss_cursor
|
|
48
50
|
from ...connectors.asyncio import AsyncAdapt_terminate
|
|
51
|
+
from ...util import langhelpers
|
|
49
52
|
from ...util.concurrency import await_fallback
|
|
50
53
|
from ...util.concurrency import await_only
|
|
51
54
|
|
|
@@ -91,9 +94,12 @@ class AsyncAdapt_aiomysql_connection(
|
|
|
91
94
|
_cursor_cls = AsyncAdapt_aiomysql_cursor
|
|
92
95
|
_ss_cursor_cls = AsyncAdapt_aiomysql_ss_cursor
|
|
93
96
|
|
|
94
|
-
def ping(self, reconnect: bool) -> None:
|
|
97
|
+
def ping(self, reconnect: bool = False) -> None:
|
|
95
98
|
assert not reconnect
|
|
96
|
-
self.
|
|
99
|
+
if self.dbapi._send_false_to_ping:
|
|
100
|
+
self.await_(self._connection.ping(reconnect=False))
|
|
101
|
+
else:
|
|
102
|
+
self.await_(self._connection.ping())
|
|
97
103
|
|
|
98
104
|
def character_set_name(self) -> Optional[str]:
|
|
99
105
|
return self._connection.character_set_name() # type: ignore[no-any-return] # noqa: E501
|
|
@@ -170,6 +176,24 @@ class AsyncAdapt_aiomysql_dbapi(AsyncAdapt_dbapi_module):
|
|
|
170
176
|
await_only(creator_fn(*arg, **kw)),
|
|
171
177
|
)
|
|
172
178
|
|
|
179
|
+
@langhelpers.memoized_property
|
|
180
|
+
def _send_false_to_ping(self) -> bool:
|
|
181
|
+
"""determine if aiomysql has deprecated, changed the default of,
|
|
182
|
+
or removed the 'reconnect' argument of connection.ping().
|
|
183
|
+
|
|
184
|
+
See #13306 and #10492
|
|
185
|
+
|
|
186
|
+
""" # noqa: E501
|
|
187
|
+
|
|
188
|
+
try:
|
|
189
|
+
Connection = __import__(
|
|
190
|
+
"aiomysql.connection"
|
|
191
|
+
).connection.Connection
|
|
192
|
+
except (ImportError, AttributeError):
|
|
193
|
+
return True
|
|
194
|
+
else:
|
|
195
|
+
return _connection_ping_reconnects_true(Connection)
|
|
196
|
+
|
|
173
197
|
def _init_cursors_subclasses(
|
|
174
198
|
self,
|
|
175
199
|
) -> Tuple[AsyncIODBAPICursor, AsyncIODBAPICursor]:
|
|
@@ -26,6 +26,7 @@ This dialect should normally be used only with the
|
|
|
26
26
|
)
|
|
27
27
|
|
|
28
28
|
""" # noqa
|
|
29
|
+
|
|
29
30
|
from __future__ import annotations
|
|
30
31
|
|
|
31
32
|
from types import ModuleType
|
|
@@ -35,6 +36,7 @@ from typing import Optional
|
|
|
35
36
|
from typing import TYPE_CHECKING
|
|
36
37
|
from typing import Union
|
|
37
38
|
|
|
39
|
+
from .pymysql import _connection_ping_reconnects_true
|
|
38
40
|
from .pymysql import MySQLDialect_pymysql
|
|
39
41
|
from ... import pool
|
|
40
42
|
from ... import util
|
|
@@ -43,6 +45,7 @@ from ...connectors.asyncio import AsyncAdapt_dbapi_cursor
|
|
|
43
45
|
from ...connectors.asyncio import AsyncAdapt_dbapi_module
|
|
44
46
|
from ...connectors.asyncio import AsyncAdapt_dbapi_ss_cursor
|
|
45
47
|
from ...connectors.asyncio import AsyncAdapt_terminate
|
|
48
|
+
from ...util import langhelpers
|
|
46
49
|
from ...util.concurrency import await_fallback
|
|
47
50
|
from ...util.concurrency import await_only
|
|
48
51
|
|
|
@@ -86,18 +89,21 @@ class AsyncAdapt_asyncmy_connection(
|
|
|
86
89
|
if isinstance(error, AttributeError):
|
|
87
90
|
raise self.dbapi.InternalError(
|
|
88
91
|
"network operation failed due to asyncmy attribute error"
|
|
89
|
-
)
|
|
92
|
+
) from error
|
|
90
93
|
|
|
91
94
|
raise error
|
|
92
95
|
|
|
93
|
-
def ping(self, reconnect: bool) -> None:
|
|
96
|
+
def ping(self, reconnect: bool = False) -> None:
|
|
94
97
|
assert not reconnect
|
|
95
98
|
return self.await_(self._do_ping())
|
|
96
99
|
|
|
97
100
|
async def _do_ping(self) -> None:
|
|
98
101
|
try:
|
|
99
102
|
async with self._execute_mutex:
|
|
100
|
-
|
|
103
|
+
if self.dbapi._send_false_to_ping:
|
|
104
|
+
await self._connection.ping(reconnect=False)
|
|
105
|
+
else:
|
|
106
|
+
await self._connection.ping()
|
|
101
107
|
except Exception as error:
|
|
102
108
|
self._handle_exception(error)
|
|
103
109
|
|
|
@@ -171,6 +177,22 @@ class AsyncAdapt_asyncmy_dbapi(AsyncAdapt_dbapi_module):
|
|
|
171
177
|
await_only(creator_fn(*arg, **kw)),
|
|
172
178
|
)
|
|
173
179
|
|
|
180
|
+
@langhelpers.memoized_property
|
|
181
|
+
def _send_false_to_ping(self) -> bool:
|
|
182
|
+
"""determine if asyncmy has deprecated, changed the default of,
|
|
183
|
+
or removed the 'reconnect' argument of connection.ping().
|
|
184
|
+
|
|
185
|
+
See #13306 and #10492
|
|
186
|
+
|
|
187
|
+
""" # noqa: E501
|
|
188
|
+
|
|
189
|
+
try:
|
|
190
|
+
Connection = __import__("asyncmy.connection").connection.Connection
|
|
191
|
+
except (ImportError, AttributeError):
|
|
192
|
+
return True
|
|
193
|
+
else:
|
|
194
|
+
return _connection_ping_reconnects_true(Connection)
|
|
195
|
+
|
|
174
196
|
|
|
175
197
|
class MySQLDialect_asyncmy(MySQLDialect_pymysql):
|
|
176
198
|
driver = "asyncmy"
|
|
@@ -1063,6 +1063,7 @@ output:
|
|
|
1063
1063
|
)
|
|
1064
1064
|
|
|
1065
1065
|
""" # noqa
|
|
1066
|
+
|
|
1066
1067
|
from __future__ import annotations
|
|
1067
1068
|
|
|
1068
1069
|
from collections import defaultdict
|
|
@@ -2764,6 +2765,7 @@ class MySQLDialect(default.DefaultDialect):
|
|
|
2764
2765
|
# i.e. first connect
|
|
2765
2766
|
_backslash_escapes = True
|
|
2766
2767
|
_server_ansiquotes = False
|
|
2768
|
+
_casing = 0
|
|
2767
2769
|
|
|
2768
2770
|
server_version_info: Tuple[int, ...]
|
|
2769
2771
|
identifier_preparer: MySQLIdentifierPreparer
|
|
@@ -3186,9 +3188,9 @@ class MySQLDialect(default.DefaultDialect):
|
|
|
3186
3188
|
self._is_mysql and self.server_version_info >= (8, 0, 1)
|
|
3187
3189
|
)
|
|
3188
3190
|
|
|
3189
|
-
self._needs_correct_for_88718_96365 = (
|
|
3190
|
-
|
|
3191
|
-
)
|
|
3191
|
+
self._needs_correct_for_88718_96365 = self.server_version_info >= (
|
|
3192
|
+
8,
|
|
3193
|
+
) and (self.server_version_info < (8, 0, 14) or self._casing == 2)
|
|
3192
3194
|
|
|
3193
3195
|
self.delete_returning = (
|
|
3194
3196
|
self.is_mariadb and self.server_version_info >= (10, 0, 5)
|
|
@@ -3253,7 +3255,7 @@ class MySQLDialect(default.DefaultDialect):
|
|
|
3253
3255
|
def _is_mariadb_102(self) -> bool:
|
|
3254
3256
|
return (
|
|
3255
3257
|
self.is_mariadb
|
|
3256
|
-
and self._mariadb_normalized_version_info # type:ignore[operator]
|
|
3258
|
+
and self._mariadb_normalized_version_info # type: ignore[operator]
|
|
3257
3259
|
> (
|
|
3258
3260
|
10,
|
|
3259
3261
|
2,
|
sqlalchemy/dialects/mysql/dml.py
CHANGED
|
@@ -45,6 +45,7 @@ charset/collation will allow connectivity.
|
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
""" # noqa
|
|
48
|
+
|
|
48
49
|
from __future__ import annotations
|
|
49
50
|
|
|
50
51
|
import re
|
|
@@ -110,8 +111,8 @@ class IdentifierPreparerCommon_mysqlconnector:
|
|
|
110
111
|
|
|
111
112
|
def _escape_identifier(self, value: str) -> str:
|
|
112
113
|
value = value.replace(
|
|
113
|
-
self.escape_quote, # type:ignore[attr-defined]
|
|
114
|
-
self.escape_to_quote, # type:ignore[attr-defined]
|
|
114
|
+
self.escape_quote, # type: ignore[attr-defined]
|
|
115
|
+
self.escape_to_quote, # type: ignore[attr-defined]
|
|
115
116
|
)
|
|
116
117
|
return value
|
|
117
118
|
|
|
@@ -47,11 +47,13 @@ and targets 100% compatibility. Most behavioral notes for MySQL-python apply
|
|
|
47
47
|
to the pymysql driver as well.
|
|
48
48
|
|
|
49
49
|
""" # noqa
|
|
50
|
+
|
|
50
51
|
from __future__ import annotations
|
|
51
52
|
|
|
52
53
|
from typing import Any
|
|
53
54
|
from typing import Dict
|
|
54
55
|
from typing import Optional
|
|
56
|
+
from typing import Type
|
|
55
57
|
from typing import TYPE_CHECKING
|
|
56
58
|
from typing import Union
|
|
57
59
|
|
|
@@ -69,6 +71,31 @@ if TYPE_CHECKING:
|
|
|
69
71
|
from ...engine.url import URL
|
|
70
72
|
|
|
71
73
|
|
|
74
|
+
def _connection_ping_reconnects_true(connection_cls: Type[Any]) -> bool:
|
|
75
|
+
"""Given a Connection class like pymysql.Connection, aiomysql.Connection,
|
|
76
|
+
asyncmy.Connection, inspect the ping() method and determine if it
|
|
77
|
+
has a "reconnect" parameter that either defaults to True, or is positional.
|
|
78
|
+
|
|
79
|
+
a return value of True here means that when we call ``connection.ping()``,
|
|
80
|
+
we **must** pass `reconnect=False`. a return value of False means that
|
|
81
|
+
we should call ``connection.ping()`` with **no arguments**.
|
|
82
|
+
|
|
83
|
+
This routine originates from issue #10492 for pymysql, however arg
|
|
84
|
+
signature mismatches in aiomysql/asyncmy tracked by issue #13306
|
|
85
|
+
necessitated a more open ended function.
|
|
86
|
+
|
|
87
|
+
"""
|
|
88
|
+
insp = langhelpers.get_callable_argspec(connection_cls.ping)
|
|
89
|
+
try:
|
|
90
|
+
reconnect_arg = insp.args[1]
|
|
91
|
+
except IndexError:
|
|
92
|
+
return False
|
|
93
|
+
else:
|
|
94
|
+
return reconnect_arg == "reconnect" and (
|
|
95
|
+
not insp.defaults or insp.defaults[0] is not False
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
72
99
|
class MySQLDialect_pymysql(MySQLDialect_mysqldb):
|
|
73
100
|
driver = "pymysql"
|
|
74
101
|
supports_statement_cache = True
|
|
@@ -97,6 +124,8 @@ class MySQLDialect_pymysql(MySQLDialect_mysqldb):
|
|
|
97
124
|
https://github.com/PyMySQL/mysqlclient/discussions/651#discussioncomment-7308971
|
|
98
125
|
for background.
|
|
99
126
|
|
|
127
|
+
Revised as part of #13306
|
|
128
|
+
|
|
100
129
|
""" # noqa: E501
|
|
101
130
|
|
|
102
131
|
try:
|
|
@@ -106,15 +135,7 @@ class MySQLDialect_pymysql(MySQLDialect_mysqldb):
|
|
|
106
135
|
except (ImportError, AttributeError):
|
|
107
136
|
return True
|
|
108
137
|
else:
|
|
109
|
-
|
|
110
|
-
try:
|
|
111
|
-
reconnect_arg = insp.args[1]
|
|
112
|
-
except IndexError:
|
|
113
|
-
return False
|
|
114
|
-
else:
|
|
115
|
-
return reconnect_arg == "reconnect" and (
|
|
116
|
-
not insp.defaults or insp.defaults[0] is not False
|
|
117
|
-
)
|
|
138
|
+
return _connection_ping_reconnects_true(Connection)
|
|
118
139
|
|
|
119
140
|
def do_ping(self, dbapi_connection: DBAPIConnection) -> Literal[True]:
|
|
120
141
|
if self._send_false_to_ping:
|
|
@@ -371,7 +371,7 @@ class MySQLTableDefinitionParser:
|
|
|
371
371
|
|
|
372
372
|
buffer = []
|
|
373
373
|
for row in columns:
|
|
374
|
-
|
|
374
|
+
name, col_type, nullable, default, extra = (
|
|
375
375
|
row[i] for i in (0, 1, 2, 4, 5)
|
|
376
376
|
)
|
|
377
377
|
|
|
@@ -704,7 +704,7 @@ def cleanup_text(raw_text: str) -> str:
|
|
|
704
704
|
if "\\" in raw_text:
|
|
705
705
|
raw_text = re.sub(
|
|
706
706
|
_control_char_regexp,
|
|
707
|
-
lambda s: _control_char_map[s[0]], # type: ignore[index]
|
|
707
|
+
lambda s: _control_char_map[s[0]], # type: ignore[unused-ignore,index] # noqa: E501
|
|
708
708
|
raw_text,
|
|
709
709
|
)
|
|
710
710
|
return raw_text.replace("''", "'")
|
|
@@ -2778,6 +2778,7 @@ class OracleDialect(default.DefaultDialect):
|
|
|
2778
2778
|
all_cols.c.column_name,
|
|
2779
2779
|
all_cols.c.data_type,
|
|
2780
2780
|
all_cols.c.char_length,
|
|
2781
|
+
all_cols.c.data_length,
|
|
2781
2782
|
all_cols.c.data_precision,
|
|
2782
2783
|
all_cols.c.data_scale,
|
|
2783
2784
|
all_cols.c.nullable,
|
|
@@ -2896,6 +2897,9 @@ class OracleDialect(default.DefaultDialect):
|
|
|
2896
2897
|
elif coltype in ("VARCHAR2", "NVARCHAR2", "CHAR", "NCHAR"):
|
|
2897
2898
|
char_length = maybe_int(row_dict["char_length"])
|
|
2898
2899
|
coltype = self.ischema_names.get(coltype)(char_length)
|
|
2900
|
+
elif coltype == "RAW":
|
|
2901
|
+
data_length = maybe_int(row_dict["data_length"])
|
|
2902
|
+
coltype = RAW(data_length)
|
|
2899
2903
|
elif "WITH TIME ZONE" in coltype:
|
|
2900
2904
|
coltype = TIMESTAMP(timezone=True)
|
|
2901
2905
|
elif "WITH LOCAL TIME ZONE" in coltype:
|
|
@@ -313,12 +313,10 @@ class VECTOR(types.TypeEngine):
|
|
|
313
313
|
)
|
|
314
314
|
|
|
315
315
|
else:
|
|
316
|
-
raise TypeError(
|
|
317
|
-
"""
|
|
316
|
+
raise TypeError("""
|
|
318
317
|
Invalid input for VECTOR: expected a list, an array.array,
|
|
319
318
|
or a SparseVector object.
|
|
320
|
-
"""
|
|
321
|
-
)
|
|
319
|
+
""")
|
|
322
320
|
|
|
323
321
|
return process
|
|
324
322
|
|