engine.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. # future/engine.py
  2. # Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: https://www.opensource.org/licenses/mit-license.php
  7. from .. import util
  8. from ..engine import Connection as _LegacyConnection
  9. from ..engine import create_engine as _create_engine
  10. from ..engine import Engine as _LegacyEngine
  11. from ..engine.base import OptionEngineMixin
  12. NO_OPTIONS = util.immutabledict()
  13. def create_engine(*arg, **kw):
  14. """Create a new :class:`_future.Engine` instance.
  15. Arguments passed to :func:`_future.create_engine` are mostly identical
  16. to those passed to the 1.x :func:`_sa.create_engine` function.
  17. The difference is that the object returned is the :class:`._future.Engine`
  18. which has the 2.0 version of the API.
  19. """
  20. kw["_future_engine_class"] = Engine
  21. return _create_engine(*arg, **kw)
  22. class Connection(_LegacyConnection):
  23. """Provides high-level functionality for a wrapped DB-API connection.
  24. The :class:`_future.Connection` object is procured by calling
  25. the :meth:`_future.Engine.connect` method of the :class:`_future.Engine`
  26. object, and provides services for execution of SQL statements as well
  27. as transaction control.
  28. **This is the SQLAlchemy 2.0 version** of the :class:`_engine.Connection`
  29. class. The API and behavior of this object is largely the same, with the
  30. following differences in behavior:
  31. * The result object returned for results is the
  32. :class:`_engine.CursorResult`
  33. object, which is a subclass of the :class:`_engine.Result`.
  34. This object has a slightly different API and behavior than the
  35. :class:`_engine.LegacyCursorResult` returned for 1.x style usage.
  36. * The object has :meth:`_future.Connection.commit` and
  37. :meth:`_future.Connection.rollback` methods which commit or roll back
  38. the current transaction in progress, if any.
  39. * The object features "autobegin" behavior, such that any call to
  40. :meth:`_future.Connection.execute` will
  41. unconditionally start a
  42. transaction which can be controlled using the above mentioned
  43. :meth:`_future.Connection.commit` and
  44. :meth:`_future.Connection.rollback` methods.
  45. * The object does not have any "autocommit" functionality. Any SQL
  46. statement or DDL statement will not be followed by any COMMIT until
  47. the transaction is explicitly committed, either via the
  48. :meth:`_future.Connection.commit` method, or if the connection is
  49. being used in a context manager that commits such as the one
  50. returned by :meth:`_future.Engine.begin`.
  51. * The SAVEPOINT method :meth:`_future.Connection.begin_nested` returns
  52. a :class:`_engine.NestedTransaction` as was always the case, and the
  53. savepoint can be controlled by invoking
  54. :meth:`_engine.NestedTransaction.commit` or
  55. :meth:`_engine.NestedTransaction.rollback` as was the case before.
  56. However, this savepoint "transaction" is not associated with the
  57. transaction that is controlled by the connection itself; the overall
  58. transaction can be committed or rolled back directly which will not emit
  59. any special instructions for the SAVEPOINT (this will typically have the
  60. effect that one desires).
  61. * The :class:`_future.Connection` object does not support "branching",
  62. which was a pattern by which a sub "connection" would be used that
  63. refers to this connection as a parent.
  64. """
  65. _is_future = True
  66. def _branch(self):
  67. raise NotImplementedError(
  68. "sqlalchemy.future.Connection does not support "
  69. "'branching' of new connections."
  70. )
  71. def begin(self):
  72. """Begin a transaction prior to autobegin occurring.
  73. The returned object is an instance of :class:`_engine.RootTransaction`.
  74. This object represents the "scope" of the transaction,
  75. which completes when either the :meth:`_engine.Transaction.rollback`
  76. or :meth:`_engine.Transaction.commit` method is called.
  77. The :meth:`_future.Connection.begin` method in SQLAlchemy 2.0 begins a
  78. transaction that normally will be begun in any case when the connection
  79. is first used to execute a statement. The reason this method might be
  80. used would be to invoke the :meth:`_events.ConnectionEvents.begin`
  81. event at a specific time, or to organize code within the scope of a
  82. connection checkout in terms of context managed blocks, such as::
  83. with engine.connect() as conn:
  84. with conn.begin():
  85. conn.execute(...)
  86. conn.execute(...)
  87. with conn.begin():
  88. conn.execute(...)
  89. conn.execute(...)
  90. The above code is not fundamentally any different in its behavior than
  91. the following code which does not use
  92. :meth:`_future.Connection.begin`; the below style is referred towards
  93. as "commit as you go" style::
  94. with engine.connect() as conn:
  95. conn.execute(...)
  96. conn.execute(...)
  97. conn.commit()
  98. conn.execute(...)
  99. conn.execute(...)
  100. conn.commit()
  101. From a database point of view, the :meth:`_future.Connection.begin`
  102. method does not emit any SQL or change the state of the underlying
  103. DBAPI connection in any way; the Python DBAPI does not have any
  104. concept of explicit transaction begin.
  105. .. seealso::
  106. :ref:`tutorial_working_with_transactions` - in the
  107. :ref:`unified_tutorial`
  108. :meth:`_future.Connection.begin_nested` - use a SAVEPOINT
  109. :meth:`_engine.Connection.begin_twophase` -
  110. use a two phase /XID transaction
  111. :meth:`_future.Engine.begin` - context manager available from
  112. :class:`_future.Engine`
  113. """
  114. return super(Connection, self).begin()
  115. def begin_nested(self):
  116. """Begin a nested transaction (i.e. SAVEPOINT) and return a transaction
  117. handle.
  118. The returned object is an instance of
  119. :class:`_engine.NestedTransaction`.
  120. Nested transactions require SAVEPOINT support in the
  121. underlying database. Any transaction in the hierarchy may
  122. ``commit`` and ``rollback``, however the outermost transaction
  123. still controls the overall ``commit`` or ``rollback`` of the
  124. transaction of a whole.
  125. If an outer :class:`.RootTransaction` is not present on this
  126. :class:`_future.Connection`, a new one is created using "autobegin".
  127. This outer transaction may be completed using "commit-as-you-go" style
  128. usage, by calling upon :meth:`_future.Connection.commit` or
  129. :meth:`_future.Connection.rollback`.
  130. .. tip::
  131. The "autobegin" behavior of :meth:`_future.Connection.begin_nested`
  132. is specific to :term:`2.0 style` use; for legacy behaviors, see
  133. :meth:`_engine.Connection.begin_nested`.
  134. The :class:`_engine.NestedTransaction` remains independent of the
  135. :class:`_future.Connection` object itself. Calling the
  136. :meth:`_future.Connection.commit` or
  137. :meth:`_future.Connection.rollback` will always affect the actual
  138. containing database transaction itself, and not the SAVEPOINT itself.
  139. When a database transaction is committed, any SAVEPOINTs that have been
  140. established are cleared and the data changes within their scope is also
  141. committed.
  142. .. seealso::
  143. :meth:`_future.Connection.begin`
  144. """
  145. return super(Connection, self).begin_nested()
  146. def commit(self):
  147. """Commit the transaction that is currently in progress.
  148. This method commits the current transaction if one has been started.
  149. If no transaction was started, the method has no effect, assuming
  150. the connection is in a non-invalidated state.
  151. A transaction is begun on a :class:`_future.Connection` automatically
  152. whenever a statement is first executed, or when the
  153. :meth:`_future.Connection.begin` method is called.
  154. .. note:: The :meth:`_future.Connection.commit` method only acts upon
  155. the primary database transaction that is linked to the
  156. :class:`_future.Connection` object. It does not operate upon a
  157. SAVEPOINT that would have been invoked from the
  158. :meth:`_future.Connection.begin_nested` method; for control of a
  159. SAVEPOINT, call :meth:`_engine.NestedTransaction.commit` on the
  160. :class:`_engine.NestedTransaction` that is returned by the
  161. :meth:`_future.Connection.begin_nested` method itself.
  162. """
  163. if self._transaction:
  164. self._transaction.commit()
  165. def rollback(self):
  166. """Roll back the transaction that is currently in progress.
  167. This method rolls back the current transaction if one has been started.
  168. If no transaction was started, the method has no effect. If a
  169. transaction was started and the connection is in an invalidated state,
  170. the transaction is cleared using this method.
  171. A transaction is begun on a :class:`_future.Connection` automatically
  172. whenever a statement is first executed, or when the
  173. :meth:`_future.Connection.begin` method is called.
  174. .. note:: The :meth:`_future.Connection.rollback` method only acts
  175. upon the primary database transaction that is linked to the
  176. :class:`_future.Connection` object. It does not operate upon a
  177. SAVEPOINT that would have been invoked from the
  178. :meth:`_future.Connection.begin_nested` method; for control of a
  179. SAVEPOINT, call :meth:`_engine.NestedTransaction.rollback` on the
  180. :class:`_engine.NestedTransaction` that is returned by the
  181. :meth:`_future.Connection.begin_nested` method itself.
  182. """
  183. if self._transaction:
  184. self._transaction.rollback()
  185. def close(self):
  186. """Close this :class:`_future.Connection`.
  187. This has the effect of also calling :meth:`_future.Connection.rollback`
  188. if any transaction is in place.
  189. """
  190. super(Connection, self).close()
  191. def execute(self, statement, parameters=None, execution_options=None):
  192. r"""Executes a SQL statement construct and returns a
  193. :class:`_engine.Result`.
  194. :param statement: The statement to be executed. This is always
  195. an object that is in both the :class:`_expression.ClauseElement` and
  196. :class:`_expression.Executable` hierarchies, including:
  197. * :class:`_expression.Select`
  198. * :class:`_expression.Insert`, :class:`_expression.Update`,
  199. :class:`_expression.Delete`
  200. * :class:`_expression.TextClause` and
  201. :class:`_expression.TextualSelect`
  202. * :class:`_schema.DDL` and objects which inherit from
  203. :class:`_schema.DDLElement`
  204. :param parameters: parameters which will be bound into the statement.
  205. This may be either a dictionary of parameter names to values,
  206. or a mutable sequence (e.g. a list) of dictionaries. When a
  207. list of dictionaries is passed, the underlying statement execution
  208. will make use of the DBAPI ``cursor.executemany()`` method.
  209. When a single dictionary is passed, the DBAPI ``cursor.execute()``
  210. method will be used.
  211. :param execution_options: optional dictionary of execution options,
  212. which will be associated with the statement execution. This
  213. dictionary can provide a subset of the options that are accepted
  214. by :meth:`_future.Connection.execution_options`.
  215. :return: a :class:`_engine.Result` object.
  216. """
  217. return self._execute_20(
  218. statement, parameters, execution_options or NO_OPTIONS
  219. )
  220. def scalar(self, statement, parameters=None, execution_options=None):
  221. r"""Executes a SQL statement construct and returns a scalar object.
  222. This method is shorthand for invoking the
  223. :meth:`_engine.Result.scalar` method after invoking the
  224. :meth:`_future.Connection.execute` method. Parameters are equivalent.
  225. :return: a scalar Python value representing the first column of the
  226. first row returned.
  227. """
  228. return self.execute(statement, parameters, execution_options).scalar()
  229. class Engine(_LegacyEngine):
  230. """Connects a :class:`_pool.Pool` and
  231. :class:`_engine.Dialect` together to provide a
  232. source of database connectivity and behavior.
  233. **This is the SQLAlchemy 2.0 version** of the :class:`~.engine.Engine`.
  234. An :class:`.future.Engine` object is instantiated publicly using the
  235. :func:`~sqlalchemy.future.create_engine` function.
  236. .. seealso::
  237. :doc:`/core/engines`
  238. :ref:`connections_toplevel`
  239. """
  240. _connection_cls = Connection
  241. _is_future = True
  242. def _not_implemented(self, *arg, **kw):
  243. raise NotImplementedError(
  244. "This method is not implemented for SQLAlchemy 2.0."
  245. )
  246. transaction = (
  247. run_callable
  248. ) = (
  249. execute
  250. ) = (
  251. scalar
  252. ) = (
  253. _execute_clauseelement
  254. ) = _execute_compiled = table_names = has_table = _not_implemented
  255. def _run_ddl_visitor(self, visitorcallable, element, **kwargs):
  256. # TODO: this is for create_all support etc. not clear if we
  257. # want to provide this in 2.0, that is, a way to execute SQL where
  258. # they aren't calling "engine.begin()" explicitly, however, DDL
  259. # may be a special case for which we want to continue doing it this
  260. # way. A big win here is that the full DDL sequence is inside of a
  261. # single transaction rather than COMMIT for each statement.
  262. with self.begin() as conn:
  263. conn._run_ddl_visitor(visitorcallable, element, **kwargs)
  264. @classmethod
  265. def _future_facade(self, legacy_engine):
  266. return Engine(
  267. legacy_engine.pool,
  268. legacy_engine.dialect,
  269. legacy_engine.url,
  270. logging_name=legacy_engine.logging_name,
  271. echo=legacy_engine.echo,
  272. hide_parameters=legacy_engine.hide_parameters,
  273. execution_options=legacy_engine._execution_options,
  274. )
  275. @util.contextmanager
  276. def begin(self):
  277. """Return a :class:`_future.Connection` object with a transaction
  278. begun.
  279. Use of this method is similar to that of
  280. :meth:`_future.Engine.connect`, typically as a context manager, which
  281. will automatically maintain the state of the transaction when the block
  282. ends, either by calling :meth:`_future.Connection.commit` when the
  283. block succeeds normally, or :meth:`_future.Connection.rollback` when an
  284. exception is raised, before propagating the exception outwards::
  285. with engine.begin() as connection:
  286. connection.execute(text("insert into table values ('foo')"))
  287. .. seealso::
  288. :meth:`_future.Engine.connect`
  289. :meth:`_future.Connection.begin`
  290. """
  291. with self.connect() as conn:
  292. with conn.begin():
  293. yield conn
  294. def connect(self):
  295. """Return a new :class:`_future.Connection` object.
  296. The :class:`_future.Connection` acts as a Python context manager, so
  297. the typical use of this method looks like::
  298. with engine.connect() as connection:
  299. connection.execute(text("insert into table values ('foo')"))
  300. connection.commit()
  301. Where above, after the block is completed, the connection is "closed"
  302. and its underlying DBAPI resources are returned to the connection pool.
  303. This also has the effect of rolling back any transaction that
  304. was explicitly begun or was begun via autobegin, and will
  305. emit the :meth:`_events.ConnectionEvents.rollback` event if one was
  306. started and is still in progress.
  307. .. seealso::
  308. :meth:`_future.Engine.begin`
  309. """
  310. return super(Engine, self).connect()
  311. class OptionEngine(OptionEngineMixin, Engine):
  312. pass
  313. Engine._option_cls = OptionEngine