--- /dev/null
+.. change::
+ :tags: bug, sql
+ :tickets: 6204
+
+ Executing a :class:`_sql.Subquery` using :meth:`_engine.Connection.execute`
+ is deprecated and will emit a deprecation warning; this use case was an
+ oversight that should have been removed from 1.4. The operation will now
+ execute the underlying :class:`_sql.Select` object directly for backwards
+ compatibility. Similarly, the :class:`_sql.CTE` class is also not
+ appropriate for execution. In 1.3, attempting to execute a CTE would result
+ in an invalid "blank" SQL statement being executed; since this use case was
+ not working it now raises :class:`_exc.ObjectNotExecutableError`.
+ Previously, 1.4 was attempting to execute the CTE as a statement however it
+ was working only erratically.
)
-class ORMStatementRole(roles.CoerceTextStatementRole):
+class ORMStatementRole(roles.StatementRole):
_role_name = (
"Executable SQL or text() construct, including ORM " "aware objects"
)
_compile_state_factory = ORMFromStatementCompileState.create_for_statement
- _is_future = True
-
_for_update_arg = None
_traverse_internals = [
"""
- statement = coercions.expect(roles.CoerceTextStatementRole, statement)
+ statement = coercions.expect(roles.StatementRole, statement)
if kw:
util.warn_deprecated_20(
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
__slots__ = ()
-class StatementImpl(_NoTextCoercion, RoleImpl):
- __slots__ = ()
-
-
-class CoerceTextStatementImpl(_CoerceLiterals, RoleImpl):
+class StatementImpl(_CoerceLiterals, RoleImpl):
__slots__ = ()
def _implicit_coercions(
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
)
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
)
return StatementLambdaElement(
lmb,
- roles.CoerceTextStatementRole,
+ roles.StatementRole,
LambdaOptions(
enable_tracking=enable_tracking,
track_on=track_on,
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)
def _effective_plugin_target(self):
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
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):
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:
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"""
("_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
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)
from sqlalchemy.testing import is_not
from sqlalchemy.testing import is_true
from sqlalchemy.testing import mock
+from sqlalchemy.testing.assertions import expect_deprecated
from sqlalchemy.testing.assertsql import CompiledSQL
from sqlalchemy.testing.mock import call
from sqlalchemy.testing.mock import Mock
tsa.and_(True).compile(),
column("foo"),
column("foo").compile(),
+ select(1).cte(),
+ # select(1).subquery(),
MetaData(),
Integer(),
tsa.Index(name="foo"),
obj,
)
+ def test_subquery_exec_warning(self):
+ for obj in (select(1).alias(), select(1).subquery()):
+ with testing.db.connect() as conn:
+ with expect_deprecated(
+ "Executing a subquery object is deprecated and will "
+ "raise ObjectNotExecutableError"
+ ):
+ eq_(conn.execute(obj).scalar(), 1)
+
def test_stmt_exception_bytestring_raised(self):
name = util.u("méil")
users = self.tables.users
eq_(compiled.execution_options, {"autocommit": True})
def test_embedded_element_true_to_none(self):
- stmt = table1.insert().cte()
+ stmt = table1.insert()
eq_(stmt._execution_options, {"autocommit": True})
- s2 = select(table1).select_from(stmt)
+ s2 = select(table1).select_from(stmt.cte())
eq_(s2._execution_options, {})
compiled = s2.compile()
eq_(compiled.execution_options, {"autocommit": True})
def test_embedded_element_true_to_false(self):
- stmt = table1.insert().cte()
+ stmt = table1.insert()
eq_(stmt._execution_options, {"autocommit": True})
s2 = (
select(table1)
- .select_from(stmt)
+ .select_from(stmt.cte())
.execution_options(autocommit=False)
)
eq_(s2._execution_options, {"autocommit": False})
products = table("products", column("id"), column("price"))
cte = products.select().cte("pd")
- assert "autocommit" not in cte._execution_options
+ assert "autocommit" not in cte.select()._execution_options
stmt = products.update().where(products.c.price == cte.c.price)
eq_(stmt.compile().execution_options["autocommit"], True)
):
expect(roles.ExpressionElementRole, t.select().alias())
- def test_statement_no_text_coercion(self):
- assert_raises_message(
- exc.ArgumentError,
- r"Textual SQL expression 'select \* from table' should be "
- r"explicitly declared",
- expect,
- roles.StatementRole,
- "select * from table",
- )
-
def test_statement_text_coercion(self):
with testing.expect_deprecated_20(
"Using plain strings to indicate SQL statements"
):
is_true(
- expect(
- roles.CoerceTextStatementRole, "select * from table"
- ).compare(text("select * from table"))
+ expect(roles.StatementRole, "select * from table").compare(
+ text("select * from table")
+ )
)
def test_select_statement_no_text_coercion(self):
)
def test_statement_coercion_select(self):
- is_true(
- expect(roles.CoerceTextStatementRole, select(t)).compare(select(t))
- )
+ is_true(expect(roles.StatementRole, select(t)).compare(select(t)))
def test_statement_coercion_ddl(self):
d1 = DDL("hi")
- is_(expect(roles.CoerceTextStatementRole, d1), d1)
+ is_(expect(roles.StatementRole, d1), d1)
def test_strict_from_clause_role(self):
stmt = select(t).subquery()
def test_statement_coercion_sequence(self):
s1 = Sequence("hi")
- is_(expect(roles.CoerceTextStatementRole, s1), s1)
+ is_(expect(roles.StatementRole, s1), s1)
def test_columns_clause_role(self):
is_(expect(roles.ColumnsClauseRole, t.c.q), t.c.q)