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.
Files changed (133) hide show
  1. sqlalchemy/__init__.py +1 -1
  2. sqlalchemy/connectors/aioodbc.py +10 -1
  3. sqlalchemy/dialects/_typing.py +0 -1
  4. sqlalchemy/dialects/mssql/__init__.py +0 -1
  5. sqlalchemy/dialects/mssql/base.py +40 -29
  6. sqlalchemy/dialects/mssql/information_schema.py +0 -1
  7. sqlalchemy/dialects/mssql/pymssql.py +1 -0
  8. sqlalchemy/dialects/mssql/pyodbc.py +0 -1
  9. sqlalchemy/dialects/mysql/aiomysql.py +26 -2
  10. sqlalchemy/dialects/mysql/asyncmy.py +25 -3
  11. sqlalchemy/dialects/mysql/base.py +6 -4
  12. sqlalchemy/dialects/mysql/cymysql.py +1 -0
  13. sqlalchemy/dialects/mysql/dml.py +0 -1
  14. sqlalchemy/dialects/mysql/mariadbconnector.py +1 -0
  15. sqlalchemy/dialects/mysql/mysqlconnector.py +3 -2
  16. sqlalchemy/dialects/mysql/mysqldb.py +1 -0
  17. sqlalchemy/dialects/mysql/pymysql.py +30 -9
  18. sqlalchemy/dialects/mysql/pyodbc.py +1 -0
  19. sqlalchemy/dialects/mysql/reflection.py +2 -2
  20. sqlalchemy/dialects/oracle/base.py +4 -0
  21. sqlalchemy/dialects/oracle/cx_oracle.py +1 -0
  22. sqlalchemy/dialects/oracle/oracledb.py +1 -0
  23. sqlalchemy/dialects/oracle/vector.py +2 -4
  24. sqlalchemy/dialects/postgresql/__init__.py +0 -1
  25. sqlalchemy/dialects/postgresql/_psycopg_common.py +36 -0
  26. sqlalchemy/dialects/postgresql/asyncpg.py +4 -1
  27. sqlalchemy/dialects/postgresql/base.py +39 -4
  28. sqlalchemy/dialects/postgresql/dml.py +0 -1
  29. sqlalchemy/dialects/postgresql/ext.py +6 -0
  30. sqlalchemy/dialects/postgresql/hstore.py +0 -1
  31. sqlalchemy/dialects/postgresql/operators.py +0 -1
  32. sqlalchemy/dialects/postgresql/pg8000.py +3 -5
  33. sqlalchemy/dialects/postgresql/provision.py +3 -2
  34. sqlalchemy/dialects/postgresql/psycopg.py +22 -38
  35. sqlalchemy/dialects/postgresql/psycopg2.py +3 -29
  36. sqlalchemy/dialects/postgresql/psycopg2cffi.py +1 -0
  37. sqlalchemy/dialects/sqlite/aiosqlite.py +1 -0
  38. sqlalchemy/dialects/sqlite/base.py +8 -11
  39. sqlalchemy/dialects/sqlite/pysqlcipher.py +11 -7
  40. sqlalchemy/dialects/sqlite/pysqlite.py +1 -0
  41. sqlalchemy/engine/_py_processors.py +0 -1
  42. sqlalchemy/engine/base.py +3 -1
  43. sqlalchemy/engine/create.py +1 -1
  44. sqlalchemy/engine/cursor.py +5 -2
  45. sqlalchemy/engine/interfaces.py +1 -1
  46. sqlalchemy/engine/mock.py +0 -1
  47. sqlalchemy/engine/processors.py +1 -0
  48. sqlalchemy/engine/reflection.py +1 -0
  49. sqlalchemy/engine/result.py +32 -9
  50. sqlalchemy/event/api.py +1 -1
  51. sqlalchemy/event/attr.py +1 -0
  52. sqlalchemy/event/base.py +1 -0
  53. sqlalchemy/event/legacy.py +1 -0
  54. sqlalchemy/event/registry.py +1 -0
  55. sqlalchemy/exc.py +1 -0
  56. sqlalchemy/ext/__init__.py +0 -1
  57. sqlalchemy/ext/associationproxy.py +1 -0
  58. sqlalchemy/ext/asyncio/engine.py +1 -1
  59. sqlalchemy/ext/asyncio/session.py +0 -1
  60. sqlalchemy/ext/automap.py +1 -0
  61. sqlalchemy/ext/baked.py +0 -1
  62. sqlalchemy/ext/compiler.py +1 -0
  63. sqlalchemy/ext/declarative/extensions.py +1 -0
  64. sqlalchemy/ext/horizontal_shard.py +1 -0
  65. sqlalchemy/ext/hybrid.py +1 -1
  66. sqlalchemy/ext/instrumentation.py +1 -1
  67. sqlalchemy/ext/mutable.py +2 -2
  68. sqlalchemy/ext/mypy/plugin.py +1 -0
  69. sqlalchemy/ext/orderinglist.py +1 -0
  70. sqlalchemy/ext/serializer.py +0 -1
  71. sqlalchemy/future/__init__.py +1 -0
  72. sqlalchemy/inspection.py +1 -0
  73. sqlalchemy/log.py +2 -2
  74. sqlalchemy/orm/collections.py +1 -0
  75. sqlalchemy/orm/context.py +1 -1
  76. sqlalchemy/orm/decl_api.py +14 -11
  77. sqlalchemy/orm/dependency.py +1 -1
  78. sqlalchemy/orm/descriptor_props.py +1 -1
  79. sqlalchemy/orm/dynamic.py +9 -3
  80. sqlalchemy/orm/evaluator.py +0 -1
  81. sqlalchemy/orm/events.py +1 -0
  82. sqlalchemy/orm/instrumentation.py +0 -1
  83. sqlalchemy/orm/loading.py +2 -2
  84. sqlalchemy/orm/mapper.py +1 -0
  85. sqlalchemy/orm/path_registry.py +19 -4
  86. sqlalchemy/orm/persistence.py +1 -0
  87. sqlalchemy/orm/properties.py +3 -4
  88. sqlalchemy/orm/query.py +8 -1
  89. sqlalchemy/orm/relationships.py +25 -18
  90. sqlalchemy/orm/session.py +29 -8
  91. sqlalchemy/orm/strategies.py +14 -2
  92. sqlalchemy/orm/strategy_options.py +20 -2
  93. sqlalchemy/orm/unitofwork.py +2 -3
  94. sqlalchemy/orm/util.py +4 -0
  95. sqlalchemy/pool/impl.py +1 -0
  96. sqlalchemy/sql/_orm_types.py +0 -1
  97. sqlalchemy/sql/_py_util.py +1 -1
  98. sqlalchemy/sql/base.py +0 -1
  99. sqlalchemy/sql/compiler.py +5 -1
  100. sqlalchemy/sql/crud.py +2 -1
  101. sqlalchemy/sql/ddl.py +1 -0
  102. sqlalchemy/sql/dml.py +4 -0
  103. sqlalchemy/sql/elements.py +0 -1
  104. sqlalchemy/sql/expression.py +0 -1
  105. sqlalchemy/sql/functions.py +92 -51
  106. sqlalchemy/sql/lambdas.py +1 -2
  107. sqlalchemy/sql/schema.py +7 -5
  108. sqlalchemy/sql/selectable.py +1 -2
  109. sqlalchemy/sql/sqltypes.py +1 -1
  110. sqlalchemy/sql/traversals.py +0 -1
  111. sqlalchemy/sql/util.py +2 -1
  112. sqlalchemy/testing/engines.py +0 -1
  113. sqlalchemy/testing/fixtures/mypy.py +15 -16
  114. sqlalchemy/testing/plugin/bootstrap.py +0 -1
  115. sqlalchemy/testing/plugin/pytestplugin.py +2 -5
  116. sqlalchemy/testing/profiling.py +0 -1
  117. sqlalchemy/testing/provision.py +0 -1
  118. sqlalchemy/testing/suite/test_reflection.py +0 -1
  119. sqlalchemy/testing/util.py +0 -1
  120. sqlalchemy/types.py +0 -1
  121. sqlalchemy/util/_collections.py +1 -1
  122. sqlalchemy/util/_py_collections.py +5 -5
  123. sqlalchemy/util/compat.py +1 -2
  124. sqlalchemy/util/langhelpers.py +17 -14
  125. sqlalchemy/util/preloaded.py +1 -0
  126. sqlalchemy/util/queue.py +1 -1
  127. sqlalchemy/util/tool_support.py +1 -0
  128. sqlalchemy/util/typing.py +1 -1
  129. {sqlalchemy-2.0.48.dist-info → sqlalchemy-2.0.50.dist-info}/METADATA +1 -1
  130. {sqlalchemy-2.0.48.dist-info → sqlalchemy-2.0.50.dist-info}/RECORD +133 -133
  131. {sqlalchemy-2.0.48.dist-info → sqlalchemy-2.0.50.dist-info}/WHEEL +1 -1
  132. {sqlalchemy-2.0.48.dist-info → sqlalchemy-2.0.50.dist-info}/licenses/LICENSE +0 -0
  133. {sqlalchemy-2.0.48.dist-info → sqlalchemy-2.0.50.dist-info}/top_level.txt +0 -0
sqlalchemy/__init__.py CHANGED
@@ -269,7 +269,7 @@ from .types import Uuid as Uuid
269
269
  from .types import VARBINARY as VARBINARY
270
270
  from .types import VARCHAR as VARCHAR
271
271
 
272
- __version__ = "2.0.48"
272
+ __version__ = "2.0.50"
273
273
 
274
274
 
275
275
  def __go(lcls: Any) -> None:
@@ -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
 
@@ -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]]
@@ -45,7 +45,6 @@ from .base import VARCHAR
45
45
  from .base import XML
46
46
  from ...sql import try_cast
47
47
 
48
-
49
48
  base.dialect = dialect = pyodbc.dialect
50
49
 
51
50
 
@@ -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
- util.warn(
3792
- "Did not recognize type '%s' of column '%s'"
3793
- % (type_, name)
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()),
@@ -20,7 +20,6 @@ from ...types import String
20
20
  from ...types import TypeDecorator
21
21
  from ...types import Unicode
22
22
 
23
-
24
23
  ischema = MetaData()
25
24
 
26
25
 
@@ -22,6 +22,7 @@ pymssql is a Python module that provides a Python DBAPI interface around
22
22
 
23
23
 
24
24
  """ # noqa
25
+
25
26
  import re
26
27
 
27
28
  from .base import MSDialect
@@ -359,7 +359,6 @@ hook. See that method for usage examples.
359
359
 
360
360
  """ # noqa
361
361
 
362
-
363
362
  import datetime
364
363
  import decimal
365
364
  import re
@@ -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.await_(self._connection.ping(reconnect))
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
- await self._connection.ping(False)
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
- not self.is_mariadb and self.server_version_info >= (8,)
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,
@@ -20,6 +20,7 @@ r"""
20
20
  dialects are mysqlclient and PyMySQL.
21
21
 
22
22
  """ # noqa
23
+
23
24
  from __future__ import annotations
24
25
 
25
26
  from typing import Any
@@ -28,7 +28,6 @@ from ...sql.expression import alias
28
28
  from ...sql.selectable import NamedFromClause
29
29
  from ...util.typing import Self
30
30
 
31
-
32
31
  __all__ = ("Insert", "insert")
33
32
 
34
33
 
@@ -27,6 +27,7 @@ be ``mysqldb``. ``mariadb+mariadbconnector://`` is required to use this driver.
27
27
  .. mariadb: https://github.com/mariadb-corporation/mariadb-connector-python
28
28
 
29
29
  """ # noqa
30
+
30
31
  from __future__ import annotations
31
32
 
32
33
  import re
@@ -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
 
@@ -84,6 +84,7 @@ Server Side Cursors
84
84
  The mysqldb dialect supports server-side cursors. See :ref:`mysql_ss_cursors`.
85
85
 
86
86
  """
87
+
87
88
  from __future__ import annotations
88
89
 
89
90
  import re
@@ -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
- insp = langhelpers.get_callable_argspec(Connection.ping)
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:
@@ -42,6 +42,7 @@ Pass through exact pyodbc connection string::
42
42
  connection_uri = "mysql+pyodbc:///?odbc_connect=%s" % params
43
43
 
44
44
  """ # noqa
45
+
45
46
  from __future__ import annotations
46
47
 
47
48
  import datetime
@@ -371,7 +371,7 @@ class MySQLTableDefinitionParser:
371
371
 
372
372
  buffer = []
373
373
  for row in columns:
374
- (name, col_type, nullable, default, extra) = (
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:
@@ -489,6 +489,7 @@ SQLAlchemy type (or a subclass of such).
489
489
  as better integration of outputtypehandlers.
490
490
 
491
491
  """ # noqa
492
+
492
493
  from __future__ import annotations
493
494
 
494
495
  import decimal
@@ -588,6 +588,7 @@ SQLAlchemy type (or a subclass of such).
588
588
  .. versionadded:: 2.0.0 added support for the python-oracledb driver.
589
589
 
590
590
  """ # noqa
591
+
591
592
  from __future__ import annotations
592
593
 
593
594
  import collections
@@ -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
 
@@ -89,7 +89,6 @@ from .types import TIMESTAMP
89
89
  from .types import TSQUERY
90
90
  from .types import TSVECTOR
91
91
 
92
-
93
92
  # Alias psycopg also as psycopg_async
94
93
  psycopg_async = type(
95
94
  "psycopg_async", (ModuleType,), {"dialect": psycopg.dialect_async}