Skip to content

Commit 0cd1104

Browse files
committed
remove _implicit_subquery and all derived functions
The ``.c`` and ``.columns`` attributes on the :class:`.Select` and :class:`.TextualSelect` constructs, which are not instances of :class:`.FromClause`, have been removed completely, in addition to the ``.select()`` method as well as other codepaths which would implicitly generate a subquery from a :class:`.Select` without the need to explicitly call the :meth:`.Select.subquery` method. In the case of ``.c`` and ``.columns``, these attributes were never useful in practice and have caused a great deal of confusion, hence were deprecated back in version 1.4, and have emitted warnings since that version. Accessing the columns that are specific to a :class:`.Select` construct is done via the :attr:`.Select.selected_columns` attribute, which was added in version 1.4 to suit the use case that users often expected ``.c`` to accomplish. In the larger sense, implicit production of subqueries works against SQLAlchemy's modern practice of making SQL structure as explicit as possible. Note that this is **not related** to the usual :attr:`.FromClause.c` and :attr:`.FromClause.columns` attributes, common to objects such as :class:`.Table` and :class:`.Subquery`, which are unaffected by this change. Fixes: #10236 Change-Id: If241b8674ccacce7e860bfed25b5d266bfe1aca7
1 parent 4c063e7 commit 0cd1104

17 files changed

Lines changed: 74 additions & 740 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
.. change::
2+
:tags: change, sql
3+
:tickets: 10236
4+
5+
The ``.c`` and ``.columns`` attributes on the :class:`.Select` and
6+
:class:`.TextualSelect` constructs, which are not instances of
7+
:class:`.FromClause`, have been removed completely, in addition to the
8+
``.select()`` method as well as other codepaths which would implicitly
9+
generate a subquery from a :class:`.Select` without the need to explicitly
10+
call the :meth:`.Select.subquery` method.
11+
12+
In the case of ``.c`` and ``.columns``, these attributes were never useful
13+
in practice and have caused a great deal of confusion, hence were
14+
deprecated back in version 1.4, and have emitted warnings since that
15+
version. Accessing the columns that are specific to a :class:`.Select`
16+
construct is done via the :attr:`.Select.selected_columns` attribute, which
17+
was added in version 1.4 to suit the use case that users often expected
18+
``.c`` to accomplish. In the larger sense, implicit production of
19+
subqueries works against SQLAlchemy's modern practice of making SQL
20+
structure as explicit as possible.
21+
22+
Note that this is **not related** to the usual :attr:`.FromClause.c` and
23+
:attr:`.FromClause.columns` attributes, common to objects such as
24+
:class:`.Table` and :class:`.Subquery`, which are unaffected by this
25+
change.
26+
27+
.. seealso::
28+
29+
:ref:`change_4617` - original notes from SQLAlchemy 1.4
30+

lib/sqlalchemy/orm/interfaces.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ class ORMEntityColumnsClauseRole(ORMColumnsClauseRole[_T]):
134134
_role_name = "ORM mapped or aliased entity"
135135

136136

137-
class ORMFromClauseRole(roles.StrictFromClauseRole):
137+
class ORMFromClauseRole(roles.FromClauseRole):
138138
__slots__ = ()
139139
_role_name = "ORM mapped entity, aliased entity, or FROM expression"
140140

lib/sqlalchemy/orm/mapper.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@ def generate_version(version):
767767

768768
if local_table is not None:
769769
self.local_table = coercions.expect(
770-
roles.StrictFromClauseRole,
770+
roles.FromClauseRole,
771771
local_table,
772772
disable_inspection=True,
773773
argname="local_table",
@@ -1416,9 +1416,8 @@ def _set_with_polymorphic(
14161416
self.with_polymorphic = (
14171417
self.with_polymorphic[0],
14181418
coercions.expect(
1419-
roles.StrictFromClauseRole,
1419+
roles.FromClauseRole,
14201420
self.with_polymorphic[1],
1421-
allow_select=True,
14221421
),
14231422
)
14241423

@@ -2918,7 +2917,8 @@ def _with_polymorphic_args(
29182917
) -> Tuple[Sequence[Mapper[Any]], FromClause]:
29192918
if selectable not in (None, False):
29202919
selectable = coercions.expect(
2921-
roles.StrictFromClauseRole, selectable, allow_select=True
2920+
roles.FromClauseRole,
2921+
selectable,
29222922
)
29232923

29242924
if self.with_polymorphic:

lib/sqlalchemy/orm/query.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -368,9 +368,8 @@ def _set_select_from(
368368
) -> None:
369369
fa = [
370370
coercions.expect(
371-
roles.StrictFromClauseRole,
371+
roles.FromClauseRole,
372372
elem,
373-
allow_select=True,
374373
apply_propagate_attrs=self,
375374
)
376375
for elem in obj

lib/sqlalchemy/orm/util.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,9 +366,7 @@ def polymorphic_union(
366366
for key in table_map:
367367
table = table_map[key]
368368

369-
table = coercions.expect(
370-
roles.StrictFromClauseRole, table, allow_select=True
371-
)
369+
table = coercions.expect(roles.FromClauseRole, table)
372370
table_map[key] = table
373371

374372
m = {}

lib/sqlalchemy/sql/coercions.py

Lines changed: 5 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,25 +1273,12 @@ def _implicit_coercions(
12731273
argname: Optional[str] = None,
12741274
*,
12751275
explicit_subquery: bool = False,
1276-
allow_select: bool = True,
12771276
**kw: Any,
12781277
) -> Any:
1279-
if resolved._is_select_base:
1280-
if explicit_subquery:
1281-
return resolved.subquery()
1282-
elif allow_select:
1283-
util.warn_deprecated(
1284-
"Implicit coercion of SELECT and textual SELECT "
1285-
"constructs into FROM clauses is deprecated; please call "
1286-
".subquery() on any Core select or ORM Query object in "
1287-
"order to produce a subquery object.",
1288-
version="1.4",
1289-
)
1290-
return resolved._implicit_subquery
1291-
elif resolved._is_text_clause:
1292-
return resolved
1293-
else:
1294-
self._raise_for_expected(element, argname, resolved)
1278+
if resolved._is_select_base and explicit_subquery:
1279+
return resolved.subquery()
1280+
1281+
self._raise_for_expected(element, argname, resolved)
12951282

12961283
def _post_coercion(self, element, *, deannotate=False, **kw):
12971284
if deannotate:
@@ -1300,32 +1287,7 @@ def _post_coercion(self, element, *, deannotate=False, **kw):
13001287
return element
13011288

13021289

1303-
class StrictFromClauseImpl(FromClauseImpl):
1304-
__slots__ = ()
1305-
1306-
def _implicit_coercions(
1307-
self,
1308-
element: Any,
1309-
resolved: Any,
1310-
argname: Optional[str] = None,
1311-
*,
1312-
allow_select: bool = False,
1313-
**kw: Any,
1314-
) -> Any:
1315-
if resolved._is_select_base and allow_select:
1316-
util.warn_deprecated(
1317-
"Implicit coercion of SELECT and textual SELECT constructs "
1318-
"into FROM clauses is deprecated; please call .subquery() "
1319-
"on any Core select or ORM Query object in order to produce a "
1320-
"subquery object.",
1321-
version="1.4",
1322-
)
1323-
return resolved._implicit_subquery
1324-
else:
1325-
self._raise_for_expected(element, argname, resolved)
1326-
1327-
1328-
class AnonymizedFromClauseImpl(StrictFromClauseImpl):
1290+
class AnonymizedFromClauseImpl(FromClauseImpl):
13291291
__slots__ = ()
13301292

13311293
def _post_coercion(self, element, *, flat=False, name=None, **kw):

lib/sqlalchemy/sql/roles.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -215,12 +215,7 @@ class FromClauseRole(ColumnsClauseRole, JoinTargetRole):
215215
named_with_column: bool
216216

217217

218-
class StrictFromClauseRole(FromClauseRole):
219-
__slots__ = ()
220-
# does not allow text() or select() objects
221-
222-
223-
class AnonymizedFromClauseRole(StrictFromClauseRole):
218+
class AnonymizedFromClauseRole(FromClauseRole):
224219
__slots__ = ()
225220

226221
if TYPE_CHECKING:

lib/sqlalchemy/sql/selectable.py

Lines changed: 4 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,7 @@ def exported_columns(
842842
) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]:
843843
"""A :class:`_expression.ColumnCollection`
844844
that represents the "exported"
845-
columns of this :class:`_expression.Selectable`.
845+
columns of this :class:`_expression.FromClause`.
846846
847847
The "exported" columns for a :class:`_expression.FromClause`
848848
object are synonymous
@@ -1751,9 +1751,9 @@ def _factory(
17511751
name: Optional[str] = None,
17521752
flat: bool = False,
17531753
) -> NamedFromClause:
1754-
return coercions.expect(
1755-
roles.FromClauseRole, selectable, allow_select=True
1756-
).alias(name=name, flat=flat)
1754+
return coercions.expect(roles.FromClauseRole, selectable).alias(
1755+
name=name, flat=flat
1756+
)
17571757

17581758

17591759
class TableValuedAlias(LateralFromClause, Alias):
@@ -3485,29 +3485,6 @@ def exported_columns(
34853485
"""
34863486
return self.selected_columns.as_readonly()
34873487

3488-
@property
3489-
@util.deprecated(
3490-
"1.4",
3491-
"The :attr:`_expression.SelectBase.c` and "
3492-
":attr:`_expression.SelectBase.columns` attributes "
3493-
"are deprecated and will be removed in a future release; these "
3494-
"attributes implicitly create a subquery that should be explicit. "
3495-
"Please call :meth:`_expression.SelectBase.subquery` "
3496-
"first in order to create "
3497-
"a subquery, which then contains this attribute. To access the "
3498-
"columns that this SELECT object SELECTs "
3499-
"from, use the :attr:`_expression.SelectBase.selected_columns` "
3500-
"attribute.",
3501-
)
3502-
def c(self) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]:
3503-
return self._implicit_subquery.columns
3504-
3505-
@property
3506-
def columns(
3507-
self,
3508-
) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]:
3509-
return self.c
3510-
35113488
def get_label_style(self) -> SelectLabelStyle:
35123489
"""
35133490
Retrieve the current label style.
@@ -3526,22 +3503,6 @@ def set_label_style(self, style: SelectLabelStyle) -> Self:
35263503

35273504
raise NotImplementedError()
35283505

3529-
@util.deprecated(
3530-
"1.4",
3531-
"The :meth:`_expression.SelectBase.select` method is deprecated "
3532-
"and will be removed in a future release; this method implicitly "
3533-
"creates a subquery that should be explicit. "
3534-
"Please call :meth:`_expression.SelectBase.subquery` "
3535-
"first in order to create "
3536-
"a subquery, which then can be selected.",
3537-
)
3538-
def select(self, *arg: Any, **kw: Any) -> Select[Unpack[TupleAny]]:
3539-
return self._implicit_subquery.select(*arg, **kw)
3540-
3541-
@HasMemoized.memoized_attribute
3542-
def _implicit_subquery(self) -> Subquery:
3543-
return self.subquery()
3544-
35453506
def _scalar_type(self) -> TypeEngine[Any]:
35463507
raise NotImplementedError()
35473508

lib/sqlalchemy/testing/suite/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
# the MIT License: https://www.opensource.org/licenses/mit-license.php
77
from .test_cte import * # noqa
88
from .test_ddl import * # noqa
9-
from .test_deprecations import * # noqa
109
from .test_dialect import * # noqa
1110
from .test_insert import * # noqa
1211
from .test_reflection import * # noqa

0 commit comments

Comments
 (0)