main commit
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-10-16 16:30:25 +09:00
parent 91c7e04474
commit 537e7b363f
1146 changed files with 45926 additions and 77196 deletions

View File

@@ -1,10 +1,12 @@
# engine/base.py
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
# Copyright (C) 2005-2023 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: https://www.opensource.org/licenses/mit-license.php
"""Defines :class:`_engine.Connection` and :class:`_engine.Engine`."""
"""Defines :class:`_engine.Connection` and :class:`_engine.Engine`.
"""
from __future__ import annotations
import contextlib
@@ -68,11 +70,12 @@ if typing.TYPE_CHECKING:
from ..sql._typing import _InfoType
from ..sql.compiler import Compiled
from ..sql.ddl import ExecutableDDLElement
from ..sql.ddl import InvokeDDLBase
from ..sql.ddl import SchemaDropper
from ..sql.ddl import SchemaGenerator
from ..sql.functions import FunctionElement
from ..sql.schema import DefaultGenerator
from ..sql.schema import HasSchemaAttr
from ..sql.schema import SchemaVisitable
from ..sql.schema import SchemaItem
from ..sql.selectable import TypedReturnsRows
@@ -106,7 +109,6 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
"""
dialect: Dialect
dispatch: dispatcher[ConnectionEventsTarget]
_sqla_logger_namespace = "sqlalchemy.engine.Connection"
@@ -171,9 +173,13 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
if self._has_events or self.engine._has_events:
self.dispatch.engine_connect(self)
# this can be assigned differently via
# characteristics.LoggingTokenCharacteristic
_message_formatter: Any = None
@util.memoized_property
def _message_formatter(self) -> Any:
if "logging_token" in self._execution_options:
token = self._execution_options["logging_token"]
return lambda msg: "[%s] %s" % (token, msg)
else:
return None
def _log_info(self, message: str, *arg: Any, **kw: Any) -> None:
fmt = self._message_formatter
@@ -199,9 +205,9 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
@property
def _schema_translate_map(self) -> Optional[SchemaTranslateMapType]:
schema_translate_map: Optional[SchemaTranslateMapType] = (
self._execution_options.get("schema_translate_map", None)
)
schema_translate_map: Optional[
SchemaTranslateMapType
] = self._execution_options.get("schema_translate_map", None)
return schema_translate_map
@@ -212,9 +218,9 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
"""
name = obj.schema
schema_translate_map: Optional[SchemaTranslateMapType] = (
self._execution_options.get("schema_translate_map", None)
)
schema_translate_map: Optional[
SchemaTranslateMapType
] = self._execution_options.get("schema_translate_map", None)
if (
schema_translate_map
@@ -244,12 +250,13 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
yield_per: int = ...,
insertmanyvalues_page_size: int = ...,
schema_translate_map: Optional[SchemaTranslateMapType] = ...,
preserve_rowcount: bool = False,
**opt: Any,
) -> Connection: ...
) -> Connection:
...
@overload
def execution_options(self, **opt: Any) -> Connection: ...
def execution_options(self, **opt: Any) -> Connection:
...
def execution_options(self, **opt: Any) -> Connection:
r"""Set non-SQL options for the connection which take effect
@@ -375,11 +382,12 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
:param stream_results: Available on: :class:`_engine.Connection`,
:class:`_sql.Executable`.
Indicate to the dialect that results should be "streamed" and not
pre-buffered, if possible. For backends such as PostgreSQL, MySQL
and MariaDB, this indicates the use of a "server side cursor" as
opposed to a client side cursor. Other backends such as that of
Oracle Database may already use server side cursors by default.
Indicate to the dialect that results should be
"streamed" and not pre-buffered, if possible. For backends
such as PostgreSQL, MySQL and MariaDB, this indicates the use of
a "server side cursor" as opposed to a client side cursor.
Other backends such as that of Oracle may already use server
side cursors by default.
The usage of
:paramref:`_engine.Connection.execution_options.stream_results` is
@@ -484,18 +492,6 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
:ref:`schema_translating`
:param preserve_rowcount: Boolean; when True, the ``cursor.rowcount``
attribute will be unconditionally memoized within the result and
made available via the :attr:`.CursorResult.rowcount` attribute.
Normally, this attribute is only preserved for UPDATE and DELETE
statements. Using this option, the DBAPIs rowcount value can
be accessed for other kinds of statements such as INSERT and SELECT,
to the degree that the DBAPI supports these statements. See
:attr:`.CursorResult.rowcount` for notes regarding the behavior
of this attribute.
.. versionadded:: 2.0.28
.. seealso::
:meth:`_engine.Engine.execution_options`
@@ -797,6 +793,7 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
with conn.begin() as trans:
conn.execute(table.insert(), {"username": "sandy"})
The returned object is an instance of :class:`_engine.RootTransaction`.
This object represents the "scope" of the transaction,
which completes when either the :meth:`_engine.Transaction.rollback`
@@ -902,7 +899,7 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
trans.rollback() # rollback to savepoint
# outer transaction continues
connection.execute(...)
connection.execute( ... )
If :meth:`_engine.Connection.begin_nested` is called without first
calling :meth:`_engine.Connection.begin` or
@@ -912,11 +909,11 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
with engine.connect() as connection: # begin() wasn't called
with connection.begin_nested(): # will auto-"begin()" first
connection.execute(...)
with connection.begin_nested(): will auto-"begin()" first
connection.execute( ... )
# savepoint is released
connection.execute(...)
connection.execute( ... )
# explicitly commit outer transaction
connection.commit()
@@ -1112,16 +1109,10 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
if self._still_open_and_dbapi_connection_is_valid:
if self._echo:
if self._is_autocommit_isolation():
if self.dialect.skip_autocommit_rollback:
self._log_info(
"ROLLBACK will be skipped by "
"skip_autocommit_rollback"
)
else:
self._log_info(
"ROLLBACK using DBAPI connection.rollback(); "
"set skip_autocommit_rollback to prevent fully"
)
self._log_info(
"ROLLBACK using DBAPI connection.rollback(), "
"DBAPI should ignore due to autocommit mode"
)
else:
self._log_info("ROLLBACK")
try:
@@ -1137,7 +1128,7 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
if self._is_autocommit_isolation():
self._log_info(
"COMMIT using DBAPI connection.commit(), "
"has no effect due to autocommit mode"
"DBAPI should ignore due to autocommit mode"
)
else:
self._log_info("COMMIT")
@@ -1271,7 +1262,8 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
parameters: Optional[_CoreSingleExecuteParams] = None,
*,
execution_options: Optional[CoreExecuteOptionsParameter] = None,
) -> Optional[_T]: ...
) -> Optional[_T]:
...
@overload
def scalar(
@@ -1280,7 +1272,8 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
parameters: Optional[_CoreSingleExecuteParams] = None,
*,
execution_options: Optional[CoreExecuteOptionsParameter] = None,
) -> Any: ...
) -> Any:
...
def scalar(
self,
@@ -1318,7 +1311,8 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
parameters: Optional[_CoreAnyExecuteParams] = None,
*,
execution_options: Optional[CoreExecuteOptionsParameter] = None,
) -> ScalarResult[_T]: ...
) -> ScalarResult[_T]:
...
@overload
def scalars(
@@ -1327,7 +1321,8 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
parameters: Optional[_CoreAnyExecuteParams] = None,
*,
execution_options: Optional[CoreExecuteOptionsParameter] = None,
) -> ScalarResult[Any]: ...
) -> ScalarResult[Any]:
...
def scalars(
self,
@@ -1361,7 +1356,8 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
parameters: Optional[_CoreAnyExecuteParams] = None,
*,
execution_options: Optional[CoreExecuteOptionsParameter] = None,
) -> CursorResult[_T]: ...
) -> CursorResult[_T]:
...
@overload
def execute(
@@ -1370,7 +1366,8 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
parameters: Optional[_CoreAnyExecuteParams] = None,
*,
execution_options: Optional[CoreExecuteOptionsParameter] = None,
) -> CursorResult[Any]: ...
) -> CursorResult[Any]:
...
def execute(
self,
@@ -1501,7 +1498,7 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
) -> CursorResult[Any]:
"""Execute a schema.DDL object."""
exec_opts = ddl._execution_options.merge_with(
execution_options = ddl._execution_options.merge_with(
self._execution_options, execution_options
)
@@ -1515,11 +1512,12 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
event_multiparams,
event_params,
) = self._invoke_before_exec_event(
ddl, distilled_parameters, exec_opts
ddl, distilled_parameters, execution_options
)
else:
event_multiparams = event_params = None
exec_opts = self._execution_options.merge_with(execution_options)
schema_translate_map = exec_opts.get("schema_translate_map", None)
dialect = self.dialect
@@ -1532,7 +1530,7 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
dialect.execution_ctx_cls._init_ddl,
compiled,
None,
exec_opts,
execution_options,
compiled,
)
if self._has_events or self.engine._has_events:
@@ -1541,7 +1539,7 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
ddl,
event_multiparams,
event_params,
exec_opts,
execution_options,
ret,
)
return ret
@@ -1739,20 +1737,21 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
conn.exec_driver_sql(
"INSERT INTO table (id, value) VALUES (%(id)s, %(value)s)",
[{"id": 1, "value": "v1"}, {"id": 2, "value": "v2"}],
[{"id":1, "value":"v1"}, {"id":2, "value":"v2"}]
)
Single dictionary::
conn.exec_driver_sql(
"INSERT INTO table (id, value) VALUES (%(id)s, %(value)s)",
dict(id=1, value="v1"),
dict(id=1, value="v1")
)
Single tuple::
conn.exec_driver_sql(
"INSERT INTO table (id, value) VALUES (?, ?)", (1, "v1")
"INSERT INTO table (id, value) VALUES (?, ?)",
(1, 'v1')
)
.. note:: The :meth:`_engine.Connection.exec_driver_sql` method does
@@ -1841,7 +1840,10 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
context.pre_exec()
if context.execute_style is ExecuteStyle.INSERTMANYVALUES:
return self._exec_insertmany_context(dialect, context)
return self._exec_insertmany_context(
dialect,
context,
)
else:
return self._exec_single_context(
dialect, context, statement, parameters
@@ -2016,22 +2018,16 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
engine_events = self._has_events or self.engine._has_events
if self.dialect._has_events:
do_execute_dispatch: Iterable[Any] = (
self.dialect.dispatch.do_execute
)
do_execute_dispatch: Iterable[
Any
] = self.dialect.dispatch.do_execute
else:
do_execute_dispatch = ()
if self._echo:
stats = context._get_cache_stats() + " (insertmanyvalues)"
preserve_rowcount = context.execution_options.get(
"preserve_rowcount", False
)
rowcount = 0
for imv_batch in dialect._deliver_insertmanyvalues_batches(
self,
cursor,
str_statement,
effective_parameters,
@@ -2052,7 +2048,6 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
imv_batch.replaced_parameters,
None,
context,
is_sub_exec=True,
)
sub_stmt = imv_batch.replaced_statement
@@ -2072,16 +2067,15 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
if self._echo:
self._log_info(sql_util._long_statement(sub_stmt))
imv_stats = f""" {imv_batch.batchnum}/{
imv_batch.total_batches
} ({
'ordered'
if imv_batch.rows_sorted else 'unordered'
}{
'; batch not supported'
if imv_batch.is_downgraded
else ''
})"""
imv_stats = f""" {
imv_batch.batchnum}/{imv_batch.total_batches} ({
'ordered'
if imv_batch.rows_sorted else 'unordered'
}{
'; batch not supported'
if imv_batch.is_downgraded
else ''
})"""
if imv_batch.batchnum == 1:
stats += imv_stats
@@ -2142,15 +2136,9 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
context.executemany,
)
if preserve_rowcount:
rowcount += imv_batch.current_batch_size
try:
context.post_exec()
if preserve_rowcount:
context._rowcount = rowcount # type: ignore[attr-defined]
result = context._setup_result_proxy()
except BaseException as e:
@@ -2392,9 +2380,9 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
None,
cast(Exception, e),
dialect.loaded_dbapi.Error,
hide_parameters=(
engine.hide_parameters if engine is not None else False
),
hide_parameters=engine.hide_parameters
if engine is not None
else False,
connection_invalidated=is_disconnect,
dialect=dialect,
)
@@ -2431,7 +2419,9 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
break
if sqlalchemy_exception and is_disconnect != ctx.is_disconnect:
sqlalchemy_exception.connection_invalidated = ctx.is_disconnect
sqlalchemy_exception.connection_invalidated = (
is_disconnect
) = ctx.is_disconnect
if newraise:
raise newraise.with_traceback(exc_info[2]) from e
@@ -2444,8 +2434,8 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
def _run_ddl_visitor(
self,
visitorcallable: Type[InvokeDDLBase],
element: SchemaVisitable,
visitorcallable: Type[Union[SchemaGenerator, SchemaDropper]],
element: SchemaItem,
**kwargs: Any,
) -> None:
"""run a DDL visitor.
@@ -2454,9 +2444,7 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
options given to the visitor so that "checkfirst" is skipped.
"""
visitorcallable(
dialect=self.dialect, connection=self, **kwargs
).traverse_single(element)
visitorcallable(self.dialect, self, **kwargs).traverse_single(element)
class ExceptionContextImpl(ExceptionContext):
@@ -2514,7 +2502,6 @@ class Transaction(TransactionalContext):
:class:`_engine.Connection`::
from sqlalchemy import create_engine
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/test")
connection = engine.connect()
trans = connection.begin()
@@ -3003,7 +2990,7 @@ class Engine(
This applies **only** to the built-in cache that is established
via the :paramref:`_engine.create_engine.query_cache_size` parameter.
It will not impact any dictionary caches that were passed via the
:paramref:`.Connection.execution_options.compiled_cache` parameter.
:paramref:`.Connection.execution_options.query_cache` parameter.
.. versionadded:: 1.4
@@ -3042,10 +3029,12 @@ class Engine(
insertmanyvalues_page_size: int = ...,
schema_translate_map: Optional[SchemaTranslateMapType] = ...,
**opt: Any,
) -> OptionEngine: ...
) -> OptionEngine:
...
@overload
def execution_options(self, **opt: Any) -> OptionEngine: ...
def execution_options(self, **opt: Any) -> OptionEngine:
...
def execution_options(self, **opt: Any) -> OptionEngine:
"""Return a new :class:`_engine.Engine` that will provide
@@ -3092,10 +3081,10 @@ class Engine(
shards = {"default": "base", "shard_1": "db1", "shard_2": "db2"}
@event.listens_for(Engine, "before_cursor_execute")
def _switch_shard(conn, cursor, stmt, params, context, executemany):
shard_id = conn.get_execution_options().get("shard_id", "default")
def _switch_shard(conn, cursor, stmt,
params, context, executemany):
shard_id = conn.get_execution_options().get('shard_id', "default")
current_shard = conn.info.get("current_shard", None)
if current_shard != shard_id:
@@ -3221,7 +3210,9 @@ class Engine(
E.g.::
with engine.begin() as conn:
conn.execute(text("insert into table (x, y, z) values (1, 2, 3)"))
conn.execute(
text("insert into table (x, y, z) values (1, 2, 3)")
)
conn.execute(text("my_special_procedure(5)"))
Upon successful operation, the :class:`.Transaction`
@@ -3237,15 +3228,15 @@ class Engine(
:meth:`_engine.Connection.begin` - start a :class:`.Transaction`
for a particular :class:`_engine.Connection`.
""" # noqa: E501
"""
with self.connect() as conn:
with conn.begin():
yield conn
def _run_ddl_visitor(
self,
visitorcallable: Type[InvokeDDLBase],
element: SchemaVisitable,
visitorcallable: Type[Union[SchemaGenerator, SchemaDropper]],
element: SchemaItem,
**kwargs: Any,
) -> None:
with self.begin() as conn: