diff options
| author | mike bayer <mike_mp@zzzcomputing.com> | 2021-04-06 14:29:44 +0000 |
|---|---|---|
| committer | Gerrit Code Review <gerrit@ci3.zzzcomputing.com> | 2021-04-06 14:29:44 +0000 |
| commit | d5a22410474f51170f18958a623e4f6c05e6b47e (patch) | |
| tree | d5cdd8c79349d32d8b77e9a4e17d6ffc3a7b60e3 /lib/sqlalchemy | |
| parent | 3375aa7be1e34aa711416122bb3937615333b759 (diff) | |
| parent | ac2ed15740629967e7fe004d3a7369ccf97aac46 (diff) | |
| download | sqlalchemy-d5a22410474f51170f18958a623e4f6c05e6b47e.tar.gz | |
Merge "Disallow AliasedReturnsRows from execution"
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/orm/interfaces.py | 2 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/query.py | 2 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/session.py | 2 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/base.py | 2 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/coercions.py | 8 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/elements.py | 4 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/lambdas.py | 10 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/roles.py | 20 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/selectable.py | 21 | ||||
| -rw-r--r-- | lib/sqlalchemy/testing/suite/test_results.py | 14 |
10 files changed, 42 insertions, 43 deletions
diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py index e2cc36999..6f77fd706 100644 --- a/lib/sqlalchemy/orm/interfaces.py +++ b/lib/sqlalchemy/orm/interfaces.py @@ -65,7 +65,7 @@ __all__ = ( ) -class ORMStatementRole(roles.CoerceTextStatementRole): +class ORMStatementRole(roles.StatementRole): _role_name = ( "Executable SQL or text() construct, including ORM " "aware objects" ) diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 90ae1b700..38f1d26b4 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -3254,8 +3254,6 @@ class FromStatement(GroupedElement, SelectBase, Executable): _compile_state_factory = ORMFromStatementCompileState.create_for_statement - _is_future = True - _for_update_arg = None _traverse_internals = [ diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 340017adf..0562569bf 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -1584,7 +1584,7 @@ class Session(_SessionClassMethods): """ - statement = coercions.expect(roles.CoerceTextStatementRole, statement) + statement = coercions.expect(roles.StatementRole, statement) if kw: util.warn_deprecated_20( diff --git a/lib/sqlalchemy/sql/base.py b/lib/sqlalchemy/sql/base.py index ac9d66970..81685dfe0 100644 --- a/lib/sqlalchemy/sql/base.py +++ b/lib/sqlalchemy/sql/base.py @@ -754,7 +754,7 @@ class ExecutableOption(HasCopyInternals, HasCacheKey): return c -class Executable(roles.CoerceTextStatementRole, Generative): +class Executable(roles.StatementRole, Generative): """Mark a :class:`_expression.ClauseElement` as supporting execution. :class:`.Executable` is a superclass for all "statement" types diff --git a/lib/sqlalchemy/sql/coercions.py b/lib/sqlalchemy/sql/coercions.py index 35ac1a5ba..c00262fd5 100644 --- a/lib/sqlalchemy/sql/coercions.py +++ b/lib/sqlalchemy/sql/coercions.py @@ -824,11 +824,7 @@ class ReturnsRowsImpl(RoleImpl): __slots__ = () -class StatementImpl(_NoTextCoercion, RoleImpl): - __slots__ = () - - -class CoerceTextStatementImpl(_CoerceLiterals, RoleImpl): +class StatementImpl(_CoerceLiterals, RoleImpl): __slots__ = () def _implicit_coercions( @@ -837,7 +833,7 @@ class CoerceTextStatementImpl(_CoerceLiterals, RoleImpl): if resolved._is_lambda_element: return resolved else: - return super(CoerceTextStatementImpl, self)._implicit_coercions( + return super(StatementImpl, self)._implicit_coercions( original_element, resolved, argname=argname, **kw ) diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py index c48338303..dfa6c0f8f 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -307,9 +307,9 @@ class ClauseElement( return d def _execute_on_connection( - self, connection, multiparams, params, execution_options + self, connection, multiparams, params, execution_options, _force=False ): - if self.supports_execution: + if _force or self.supports_execution: return connection._execute_clauseelement( self, multiparams, params, execution_options ) diff --git a/lib/sqlalchemy/sql/lambdas.py b/lib/sqlalchemy/sql/lambdas.py index ebf576c8f..06db8f95e 100644 --- a/lib/sqlalchemy/sql/lambdas.py +++ b/lib/sqlalchemy/sql/lambdas.py @@ -100,7 +100,7 @@ def lambda_stmt( return StatementLambdaElement( lmb, - roles.CoerceTextStatementRole, + roles.StatementRole, LambdaOptions( enable_tracking=enable_tracking, track_on=track_on, @@ -155,9 +155,7 @@ class LambdaElement(elements.ClauseElement): self.tracker_key = (fn.__code__,) self.opts = opts - if apply_propagate_attrs is None and ( - role is roles.CoerceTextStatementRole - ): + if apply_propagate_attrs is None and (role is roles.StatementRole): apply_propagate_attrs = self rec = self._retrieve_tracker_rec(fn, apply_propagate_attrs, opts) @@ -493,10 +491,6 @@ class StatementLambdaElement(roles.AllowsLambdaRole, LambdaElement): return self._rec.expected_expr._effective_plugin_target @property - def _is_future(self): - return self._rec.expected_expr._is_future - - @property def _execution_options(self): return self._rec.expected_expr._execution_options diff --git a/lib/sqlalchemy/sql/roles.py b/lib/sqlalchemy/sql/roles.py index 1c8276eb6..7d64e8382 100644 --- a/lib/sqlalchemy/sql/roles.py +++ b/lib/sqlalchemy/sql/roles.py @@ -155,26 +155,20 @@ class AnonymizedFromClauseRole(StrictFromClauseRole): raise NotImplementedError() -class CoerceTextStatementRole(SQLRole): - _role_name = "Executable SQL or text() construct" +class ReturnsRowsRole(SQLRole): + _role_name = ( + "Row returning expression such as a SELECT, a FROM clause, or an " + "INSERT/UPDATE/DELETE with RETURNING" + ) -class StatementRole(CoerceTextStatementRole): +class StatementRole(SQLRole): _role_name = "Executable SQL or text() construct" - _is_future = False - _propagate_attrs = util.immutabledict() -class ReturnsRowsRole(StatementRole): - _role_name = ( - "Row returning expression such as a SELECT, a FROM clause, or an " - "INSERT/UPDATE/DELETE with RETURNING" - ) - - -class SelectStatementRole(ReturnsRowsRole): +class SelectStatementRole(StatementRole, ReturnsRowsRole): _role_name = "SELECT construct or equivalent text() construct" def subquery(self): diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py index a2e5780f8..f12744cfa 100644 --- a/lib/sqlalchemy/sql/selectable.py +++ b/lib/sqlalchemy/sql/selectable.py @@ -1575,9 +1575,6 @@ class AliasedReturnsRows(NoInit, FromClause): self.element = coercions.expect( roles.ReturnsRowsRole, selectable, apply_propagate_attrs=self ) - self.supports_execution = selectable.supports_execution - if self.supports_execution: - self._execution_options = selectable._execution_options self.element = selectable self._orig_name = name if name is None: @@ -2338,6 +2335,23 @@ class Subquery(AliasedReturnsRows): def as_scalar(self): return self.element.set_label_style(LABEL_STYLE_NONE).scalar_subquery() + def _execute_on_connection( + self, + connection, + multiparams, + params, + execution_options, + ): + util.warn_deprecated( + "Executing a subquery object is deprecated and will raise " + "ObjectNotExecutableError in an upcoming release. Please " + "execute the underlying select() statement directly.", + "1.4", + ) + return self.element._execute_on_connection( + connection, multiparams, params, execution_options, _force=True + ) + class FromGrouping(GroupedElement, FromClause): """Represent a grouping of a FROM clause""" @@ -4485,7 +4499,6 @@ class Select( ("_distinct", InternalTraversal.dp_boolean), ("_distinct_on", InternalTraversal.dp_clauseelement_tuple), ("_label_style", InternalTraversal.dp_plain_obj), - ("_is_future", InternalTraversal.dp_boolean), ] + HasPrefixes._has_prefixes_traverse_internals + HasSuffixes._has_suffixes_traverse_internals diff --git a/lib/sqlalchemy/testing/suite/test_results.py b/lib/sqlalchemy/testing/suite/test_results.py index 6c2880ad4..e8ad88f24 100644 --- a/lib/sqlalchemy/testing/suite/test_results.py +++ b/lib/sqlalchemy/testing/suite/test_results.py @@ -333,14 +333,18 @@ class ServerSideCursorsTest( def test_aliases_and_ss(self): engine = self._fixture(False) - s1 = select(1).execution_options(stream_results=True).alias() + s1 = ( + select(sql.literal_column("1").label("x")) + .execution_options(stream_results=True) + .subquery() + ) + + # options don't propagate out when subquery is used as a FROM clause with engine.begin() as conn: - result = conn.execute(s1) - assert self._is_server_side(result.cursor) + result = conn.execute(s1.select()) + assert not self._is_server_side(result.cursor) result.close() - # s1's options shouldn't affect s2 when s2 is used as a - # from_obj. s2 = select(1).select_from(s1) with engine.begin() as conn: result = conn.execute(s2) |
