From 1dac2263372df2b85db5d029a45721fa158a5c9d Mon Sep 17 00:00:00 2001 From: xiubuzhe Date: Sun, 8 Oct 2023 20:59:00 +0800 Subject: first add files --- lib/sqlalchemy/future/__init__.py | 18 ++ lib/sqlalchemy/future/engine.py | 413 ++++++++++++++++++++++++++++++++++ lib/sqlalchemy/future/orm/__init__.py | 10 + 3 files changed, 441 insertions(+) create mode 100644 lib/sqlalchemy/future/__init__.py create mode 100644 lib/sqlalchemy/future/engine.py create mode 100644 lib/sqlalchemy/future/orm/__init__.py (limited to 'lib/sqlalchemy/future') diff --git a/lib/sqlalchemy/future/__init__.py b/lib/sqlalchemy/future/__init__.py new file mode 100644 index 0000000..a2bed07 --- /dev/null +++ b/lib/sqlalchemy/future/__init__.py @@ -0,0 +1,18 @@ +# sql/future/__init__.py +# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors +# +# +# This module is part of SQLAlchemy and is released under +# the MIT License: https://www.opensource.org/licenses/mit-license.php + +"""Future 2.0 API features. + +""" +from .engine import Connection +from .engine import create_engine +from .engine import Engine +from ..sql.selectable import Select +from ..util.langhelpers import public_factory + + +select = public_factory(Select._create_future_select, ".future.select") diff --git a/lib/sqlalchemy/future/engine.py b/lib/sqlalchemy/future/engine.py new file mode 100644 index 0000000..3235529 --- /dev/null +++ b/lib/sqlalchemy/future/engine.py @@ -0,0 +1,413 @@ +from .. import util +from ..engine import Connection as _LegacyConnection +from ..engine import create_engine as _create_engine +from ..engine import Engine as _LegacyEngine +from ..engine.base import OptionEngineMixin + +NO_OPTIONS = util.immutabledict() + + +def create_engine(*arg, **kw): + """Create a new :class:`_future.Engine` instance. + + Arguments passed to :func:`_future.create_engine` are mostly identical + to those passed to the 1.x :func:`_sa.create_engine` function. + The difference is that the object returned is the :class:`._future.Engine` + which has the 2.0 version of the API. + + """ + + kw["_future_engine_class"] = Engine + return _create_engine(*arg, **kw) + + +class Connection(_LegacyConnection): + """Provides high-level functionality for a wrapped DB-API connection. + + The :class:`_future.Connection` object is procured by calling + the :meth:`_future.Engine.connect` method of the :class:`_future.Engine` + object, and provides services for execution of SQL statements as well + as transaction control. + + **This is the SQLAlchemy 2.0 version** of the :class:`_engine.Connection` + class. The API and behavior of this object is largely the same, with the + following differences in behavior: + + * The result object returned for results is the + :class:`_engine.CursorResult` + object, which is a subclass of the :class:`_engine.Result`. + This object has a slightly different API and behavior than the + :class:`_engine.LegacyCursorResult` returned for 1.x style usage. + + * The object has :meth:`_future.Connection.commit` and + :meth:`_future.Connection.rollback` methods which commit or roll back + the current transaction in progress, if any. + + * The object features "autobegin" behavior, such that any call to + :meth:`_future.Connection.execute` will + unconditionally start a + transaction which can be controlled using the above mentioned + :meth:`_future.Connection.commit` and + :meth:`_future.Connection.rollback` methods. + + * The object does not have any "autocommit" functionality. Any SQL + statement or DDL statement will not be followed by any COMMIT until + the transaction is explicitly committed, either via the + :meth:`_future.Connection.commit` method, or if the connection is + being used in a context manager that commits such as the one + returned by :meth:`_future.Engine.begin`. + + * The SAVEPOINT method :meth:`_future.Connection.begin_nested` returns + a :class:`_engine.NestedTransaction` as was always the case, and the + savepoint can be controlled by invoking + :meth:`_engine.NestedTransaction.commit` or + :meth:`_engine.NestedTransaction.rollback` as was the case before. + However, this savepoint "transaction" is not associated with the + transaction that is controlled by the connection itself; the overall + transaction can be committed or rolled back directly which will not emit + any special instructions for the SAVEPOINT (this will typically have the + effect that one desires). + + * The :class:`_future.Connection` object does not support "branching", + which was a pattern by which a sub "connection" would be used that + refers to this connection as a parent. + + + + """ + + _is_future = True + + def _branch(self): + raise NotImplementedError( + "sqlalchemy.future.Connection does not support " + "'branching' of new connections." + ) + + def begin(self): + """Begin a transaction prior to autobegin occurring. + + 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` + or :meth:`_engine.Transaction.commit` method is called. + + The :meth:`_future.Connection.begin` method in SQLAlchemy 2.0 begins a + transaction that normally will be begun in any case when the connection + is first used to execute a statement. The reason this method might be + used would be to invoke the :meth:`_events.ConnectionEvents.begin` + event at a specific time, or to organize code within the scope of a + connection checkout in terms of context managed blocks, such as:: + + with engine.connect() as conn: + with conn.begin(): + conn.execute(...) + conn.execute(...) + + with conn.begin(): + conn.execute(...) + conn.execute(...) + + The above code is not fundamentally any different in its behavior than + the following code which does not use + :meth:`_future.Connection.begin`; the below style is referred towards + as "commit as you go" style:: + + with engine.connect() as conn: + conn.execute(...) + conn.execute(...) + conn.commit() + + conn.execute(...) + conn.execute(...) + conn.commit() + + From a database point of view, the :meth:`_future.Connection.begin` + method does not emit any SQL or change the state of the underlying + DBAPI connection in any way; the Python DBAPI does not have any + concept of explicit transaction begin. + + .. seealso:: + + :ref:`tutorial_working_with_transactions` - in the + :ref:`unified_tutorial` + + :meth:`_future.Connection.begin_nested` - use a SAVEPOINT + + :meth:`_engine.Connection.begin_twophase` - + use a two phase /XID transaction + + :meth:`_future.Engine.begin` - context manager available from + :class:`_future.Engine` + + """ + return super(Connection, self).begin() + + def begin_nested(self): + """Begin a nested transaction (i.e. SAVEPOINT) and return a transaction + handle. + + The returned object is an instance of + :class:`_engine.NestedTransaction`. + + Nested transactions require SAVEPOINT support in the + underlying database. Any transaction in the hierarchy may + ``commit`` and ``rollback``, however the outermost transaction + still controls the overall ``commit`` or ``rollback`` of the + transaction of a whole. + + If an outer :class:`.RootTransaction` is not present on this + :class:`_future.Connection`, a new one is created using "autobegin". + This outer transaction may be completed using "commit-as-you-go" style + usage, by calling upon :meth:`_future.Connection.commit` or + :meth:`_future.Connection.rollback`. + + .. tip:: + + The "autobegin" behavior of :meth:`_future.Connection.begin_nested` + is specific to :term:`2.0 style` use; for legacy behaviors, see + :meth:`_engine.Connection.begin_nested`. + + The :class:`_engine.NestedTransaction` remains independent of the + :class:`_future.Connection` object itself. Calling the + :meth:`_future.Connection.commit` or + :meth:`_future.Connection.rollback` will always affect the actual + containing database transaction itself, and not the SAVEPOINT itself. + When a database transaction is committed, any SAVEPOINTs that have been + established are cleared and the data changes within their scope is also + committed. + + .. seealso:: + + :meth:`_future.Connection.begin` + + + """ + return super(Connection, self).begin_nested() + + def commit(self): + """Commit the transaction that is currently in progress. + + This method commits the current transaction if one has been started. + If no transaction was started, the method has no effect, assuming + the connection is in a non-invalidated state. + + A transaction is begun on a :class:`_future.Connection` automatically + whenever a statement is first executed, or when the + :meth:`_future.Connection.begin` method is called. + + .. note:: The :meth:`_future.Connection.commit` method only acts upon + the primary database transaction that is linked to the + :class:`_future.Connection` object. It does not operate upon a + SAVEPOINT that would have been invoked from the + :meth:`_future.Connection.begin_nested` method; for control of a + SAVEPOINT, call :meth:`_engine.NestedTransaction.commit` on the + :class:`_engine.NestedTransaction` that is returned by the + :meth:`_future.Connection.begin_nested` method itself. + + + """ + if self._transaction: + self._transaction.commit() + + def rollback(self): + """Roll back the transaction that is currently in progress. + + This method rolls back the current transaction if one has been started. + If no transaction was started, the method has no effect. If a + transaction was started and the connection is in an invalidated state, + the transaction is cleared using this method. + + A transaction is begun on a :class:`_future.Connection` automatically + whenever a statement is first executed, or when the + :meth:`_future.Connection.begin` method is called. + + .. note:: The :meth:`_future.Connection.rollback` method only acts + upon the primary database transaction that is linked to the + :class:`_future.Connection` object. It does not operate upon a + SAVEPOINT that would have been invoked from the + :meth:`_future.Connection.begin_nested` method; for control of a + SAVEPOINT, call :meth:`_engine.NestedTransaction.rollback` on the + :class:`_engine.NestedTransaction` that is returned by the + :meth:`_future.Connection.begin_nested` method itself. + + + """ + if self._transaction: + self._transaction.rollback() + + def close(self): + """Close this :class:`_future.Connection`. + + This has the effect of also calling :meth:`_future.Connection.rollback` + if any transaction is in place. + + """ + super(Connection, self).close() + + def execute(self, statement, parameters=None, execution_options=None): + r"""Executes a SQL statement construct and returns a + :class:`_engine.Result`. + + :param statement: The statement to be executed. This is always + an object that is in both the :class:`_expression.ClauseElement` and + :class:`_expression.Executable` hierarchies, including: + + * :class:`_expression.Select` + * :class:`_expression.Insert`, :class:`_expression.Update`, + :class:`_expression.Delete` + * :class:`_expression.TextClause` and + :class:`_expression.TextualSelect` + * :class:`_schema.DDL` and objects which inherit from + :class:`_schema.DDLElement` + + :param parameters: parameters which will be bound into the statement. + This may be either a dictionary of parameter names to values, + or a mutable sequence (e.g. a list) of dictionaries. When a + list of dictionaries is passed, the underlying statement execution + will make use of the DBAPI ``cursor.executemany()`` method. + When a single dictionary is passed, the DBAPI ``cursor.execute()`` + method will be used. + + :param execution_options: optional dictionary of execution options, + which will be associated with the statement execution. This + dictionary can provide a subset of the options that are accepted + by :meth:`_future.Connection.execution_options`. + + :return: a :class:`_engine.Result` object. + + """ + return self._execute_20( + statement, parameters, execution_options or NO_OPTIONS + ) + + def scalar(self, statement, parameters=None, execution_options=None): + r"""Executes a SQL statement construct and returns a scalar object. + + This method is shorthand for invoking the + :meth:`_engine.Result.scalar` method after invoking the + :meth:`_future.Connection.execute` method. Parameters are equivalent. + + :return: a scalar Python value representing the first column of the + first row returned. + + """ + return self.execute(statement, parameters, execution_options).scalar() + + +class Engine(_LegacyEngine): + """Connects a :class:`_pool.Pool` and + :class:`_engine.Dialect` together to provide a + source of database connectivity and behavior. + + **This is the SQLAlchemy 2.0 version** of the :class:`~.engine.Engine`. + + An :class:`.future.Engine` object is instantiated publicly using the + :func:`~sqlalchemy.future.create_engine` function. + + .. seealso:: + + :doc:`/core/engines` + + :ref:`connections_toplevel` + + """ + + _connection_cls = Connection + _is_future = True + + def _not_implemented(self, *arg, **kw): + raise NotImplementedError( + "This method is not implemented for SQLAlchemy 2.0." + ) + + transaction = ( + run_callable + ) = ( + execute + ) = ( + scalar + ) = ( + _execute_clauseelement + ) = _execute_compiled = table_names = has_table = _not_implemented + + def _run_ddl_visitor(self, visitorcallable, element, **kwargs): + # TODO: this is for create_all support etc. not clear if we + # want to provide this in 2.0, that is, a way to execute SQL where + # they aren't calling "engine.begin()" explicitly, however, DDL + # may be a special case for which we want to continue doing it this + # way. A big win here is that the full DDL sequence is inside of a + # single transaction rather than COMMIT for each statement. + with self.begin() as conn: + conn._run_ddl_visitor(visitorcallable, element, **kwargs) + + @classmethod + def _future_facade(self, legacy_engine): + return Engine( + legacy_engine.pool, + legacy_engine.dialect, + legacy_engine.url, + logging_name=legacy_engine.logging_name, + echo=legacy_engine.echo, + hide_parameters=legacy_engine.hide_parameters, + execution_options=legacy_engine._execution_options, + ) + + @util.contextmanager + def begin(self): + """Return a :class:`_future.Connection` object with a transaction + begun. + + Use of this method is similar to that of + :meth:`_future.Engine.connect`, typically as a context manager, which + will automatically maintain the state of the transaction when the block + ends, either by calling :meth:`_future.Connection.commit` when the + block succeeds normally, or :meth:`_future.Connection.rollback` when an + exception is raised, before propagating the exception outwards:: + + with engine.begin() as connection: + connection.execute(text("insert into table values ('foo')")) + + + .. seealso:: + + :meth:`_future.Engine.connect` + + :meth:`_future.Connection.begin` + + """ + with self.connect() as conn: + with conn.begin(): + yield conn + + def connect(self): + """Return a new :class:`_future.Connection` object. + + The :class:`_future.Connection` acts as a Python context manager, so + the typical use of this method looks like:: + + with engine.connect() as connection: + connection.execute(text("insert into table values ('foo')")) + connection.commit() + + Where above, after the block is completed, the connection is "closed" + and its underlying DBAPI resources are returned to the connection pool. + This also has the effect of rolling back any transaction that + was explicitly begun or was begun via autobegin, and will + emit the :meth:`_events.ConnectionEvents.rollback` event if one was + started and is still in progress. + + .. seealso:: + + :meth:`_future.Engine.begin` + + + """ + return super(Engine, self).connect() + + +class OptionEngine(OptionEngineMixin, Engine): + pass + + +Engine._option_cls = OptionEngine diff --git a/lib/sqlalchemy/future/orm/__init__.py b/lib/sqlalchemy/future/orm/__init__.py new file mode 100644 index 0000000..629631b --- /dev/null +++ b/lib/sqlalchemy/future/orm/__init__.py @@ -0,0 +1,10 @@ +# sql/future/orm/__init__.py +# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors +# +# +# This module is part of SQLAlchemy and is released under +# the MIT License: https://www.opensource.org/licenses/mit-license.php + +"""Future 2.0 API features for Orm. + +""" -- cgit v1.2.3