summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2021-04-06 14:29:44 +0000
committerGerrit Code Review <gerrit@ci3.zzzcomputing.com>2021-04-06 14:29:44 +0000
commitd5a22410474f51170f18958a623e4f6c05e6b47e (patch)
treed5cdd8c79349d32d8b77e9a4e17d6ffc3a7b60e3 /lib/sqlalchemy
parent3375aa7be1e34aa711416122bb3937615333b759 (diff)
parentac2ed15740629967e7fe004d3a7369ccf97aac46 (diff)
downloadsqlalchemy-d5a22410474f51170f18958a623e4f6c05e6b47e.tar.gz
Merge "Disallow AliasedReturnsRows from execution"
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/orm/interfaces.py2
-rw-r--r--lib/sqlalchemy/orm/query.py2
-rw-r--r--lib/sqlalchemy/orm/session.py2
-rw-r--r--lib/sqlalchemy/sql/base.py2
-rw-r--r--lib/sqlalchemy/sql/coercions.py8
-rw-r--r--lib/sqlalchemy/sql/elements.py4
-rw-r--r--lib/sqlalchemy/sql/lambdas.py10
-rw-r--r--lib/sqlalchemy/sql/roles.py20
-rw-r--r--lib/sqlalchemy/sql/selectable.py21
-rw-r--r--lib/sqlalchemy/testing/suite/test_results.py14
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)