Skip to content

Commit 22f6515

Browse files
gordthompsonzzzeek
andcommitted
Replace with_labels() and apply_labels() in ORM/Core
Replace :meth:`_orm.Query.with_labels` and :meth:`_sql.GenerativeSelect.apply_labels` with explicit getters and setters ``get_label_style`` and ``set_label_style`` to accommodate the three supported label styles: ``LABEL_STYLE_DISAMBIGUATE_ONLY`` (default), ``LABEL_STYLE_TABLENAME_PLUS_COL``, and ``LABEL_STYLE_NONE``. In addition, for Core and "future style" ORM queries, ``LABEL_STYLE_DISAMBIGUATE_ONLY`` is now the default label style. This style differs from the existing "no labels" style in that labeling is applied in the case of column name conflicts; with ``LABEL_STYLE_NONE``, a duplicate column name is not accessible via name in any case. For legacy ORM queries using :class:`_query.Query`, the table-plus-column names labeling style applied by ``LABEL_STYLE_TABLENAME_PLUS_COL`` continues to be used so that existing test suites and logging facilities see no change in behavior by default, however this style of labeling is no longer required for SQLAlchemy queries to function, as result sets are commonly matched to columns using a positional approach since SQLAlchemy 1.0. Within test suites, all use of apply_labels() / use_labels now uses the new methods. New tests added to test/sql/test_deprecations.py nad test/orm/test_deprecations.py to cover just the old apply_labels() method call. Tests in ORM that made explicit use apply_labels()/ etc. where it isn't needed for the ORM to work correctly use default label style now. Co-authored-by: Mike Bayer <[email protected]> Fixes: sqlalchemy#4757 Change-Id: I5fdcd2ed4ae8c7fe62f8be2b6d0e8f66409b6a54
1 parent 7bdb1f3 commit 22f6515

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1209
-547
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
.. change::
2+
:tags: sql
3+
:tickets: 4757
4+
5+
Replace :meth:`_orm.Query.with_labels` and
6+
:meth:`_sql.GenerativeSelect.apply_labels` with explicit getters and
7+
setters :meth:`_sql.GenerativeSelect.get_label_style` and
8+
:meth:`_sql.GenerativeSelect.set_label_style` to accommodate the three
9+
supported label styles: :data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY`,
10+
:data:`_sql.LABEL_STYLE_TABLENAME_PLUS_COL`, and
11+
:data:`_sql.LABEL_STYLE_NONE`.
12+
13+
In addition, for Core and "future style" ORM queries,
14+
``LABEL_STYLE_DISAMBIGUATE_ONLY`` is now the default label style. This
15+
style differs from the existing "no labels" style in that labeling is
16+
applied in the case of column name conflicts; with ``LABEL_STYLE_NONE``, a
17+
duplicate column name is not accessible via name in any case.
18+
19+
For cases where labeling is significant, namely that the ``.c`` collection
20+
of a subquery is able to refer to all columns unambiguously, the behavior
21+
of ``LABEL_STYLE_DISAMBIGUATE_ONLY`` is now sufficient for all
22+
SQLAlchemy features across Core and ORM which involve this behavior.
23+
Result set rows since SQLAlchemy 1.0 are usually aligned with column
24+
constructs positionally.
25+
26+
For legacy ORM queries using :class:`_query.Query`, the table-plus-column
27+
names labeling style applied by ``LABEL_STYLE_TABLENAME_PLUS_COL``
28+
continues to be used so that existing test suites and logging facilities
29+
see no change in behavior by default.

doc/build/core/selectable.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,28 @@ The classes here are generated using the constructors listed at
145145

146146
.. autoclass:: Values
147147
:members:
148+
149+
Label Style Constants
150+
---------------------
151+
152+
Constants used with the :meth:`_sql.GenerativeSelect.set_label_style`
153+
method.
154+
155+
.. autodata:: LABEL_STYLE_DISAMBIGUATE_ONLY
156+
157+
.. autodata:: LABEL_STYLE_NONE
158+
159+
.. autodata:: LABEL_STYLE_TABLENAME_PLUS_COL
160+
161+
.. data:: LABEL_STYLE_DEFAULT
162+
163+
The default label style, refers to :data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY`.
164+
165+
.. versionadded:: 1.4
166+
167+
.. seealso::
168+
169+
:meth:`_sql.Select.set_label_style`
170+
171+
:meth:`_sql.Select.get_label_style`
172+

doc/build/core/tutorial.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ our :func:`_expression.select` statement:
579579

580580
{sql}>>> for row in conn.execute(select(users, addresses)):
581581
... print(row)
582-
SELECT users.id, users.name, users.fullname, addresses.id, addresses.user_id, addresses.email_address
582+
SELECT users.id, users.name, users.fullname, addresses.id AS id_1, addresses.user_id, addresses.email_address
583583
FROM users, addresses
584584
[...] ()
585585
{stop}(1, u'jack', u'Jack Jones', 1, 1, u'[email protected]')
@@ -602,7 +602,7 @@ WHERE clause. We do that using :meth:`_expression.Select.where`:
602602
>>> s = select(users, addresses).where(users.c.id == addresses.c.user_id)
603603
{sql}>>> for row in conn.execute(s):
604604
... print(row)
605-
SELECT users.id, users.name, users.fullname, addresses.id,
605+
SELECT users.id, users.name, users.fullname, addresses.id AS id_1,
606606
addresses.user_id, addresses.email_address
607607
FROM users, addresses
608608
WHERE users.id = addresses.user_id
@@ -1277,8 +1277,8 @@ by a column name that appears more than once:
12771277
... order_by(u1a.c.name) # using "name" here would be ambiguous
12781278

12791279
{sql}>>> conn.execute(stmt).fetchall()
1280-
SELECT users_1.id, users_1.name, users_1.fullname, users_2.id,
1281-
users_2.name, users_2.fullname
1280+
SELECT users_1.id, users_1.name, users_1.fullname, users_2.id AS id_1,
1281+
users_2.name AS name_1, users_2.fullname AS fullname_1
12821282
FROM users AS users_1, users AS users_2
12831283
WHERE users_1.name > users_2.name ORDER BY users_1.name
12841284
[...] ()
@@ -1597,7 +1597,7 @@ single named value is needed in the execute parameters:
15971597
... select_from(users.outerjoin(addresses)).\
15981598
... order_by(addresses.c.id)
15991599
{sql}>>> conn.execute(s, {"name": "jack"}).fetchall()
1600-
SELECT users.id, users.name, users.fullname, addresses.id,
1600+
SELECT users.id, users.name, users.fullname, addresses.id AS id_1,
16011601
addresses.user_id, addresses.email_address
16021602
FROM users LEFT OUTER JOIN addresses ON users.id = addresses.user_id
16031603
WHERE users.name LIKE ? || '%' OR addresses.email_address LIKE ? || '@%'

doc/build/tutorial/data.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -879,7 +879,7 @@ below for example returns all unique pairs of user names::
879879
... select(user_alias_1.c.name, user_alias_2.c.name).
880880
... join_from(user_alias_1, user_alias_2, user_alias_1.c.id > user_alias_2.c.id)
881881
... )
882-
{opensql}SELECT user_account_1.name, user_account_2.name
882+
{opensql}SELECT user_account_1.name, user_account_2.name AS name_1
883883
FROM user_account AS user_account_1
884884
JOIN user_account AS user_account_2 ON user_account_1.id > user_account_2.id
885885

lib/sqlalchemy/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@
5454
from .sql import intersect # noqa
5555
from .sql import intersect_all # noqa
5656
from .sql import join # noqa
57+
from .sql import LABEL_STYLE_DEFAULT # noqa
58+
from .sql import LABEL_STYLE_DISAMBIGUATE_ONLY # noqa
59+
from .sql import LABEL_STYLE_NONE # noqa
60+
from .sql import LABEL_STYLE_TABLENAME_PLUS_COL # noqa
5761
from .sql import lambda_stmt # noqa
5862
from .sql import lateral # noqa
5963
from .sql import literal # noqa

lib/sqlalchemy/orm/context.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,9 +373,11 @@ def create_for_statement(cls, statement_container, compiler, **kw):
373373
if (
374374
isinstance(statement, expression.SelectBase)
375375
and not statement._is_textual
376-
and not statement.use_labels
376+
and statement._label_style is util.symbol("LABEL_STYLE_NONE")
377377
):
378-
self.statement = statement.apply_labels()
378+
self.statement = statement.set_label_style(
379+
LABEL_STYLE_TABLENAME_PLUS_COL
380+
)
379381
else:
380382
self.statement = statement
381383
self.order_by = None

lib/sqlalchemy/orm/loading.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from ..engine.result import FrozenResult
3232
from ..engine.result import SimpleResultMetaData
3333
from ..sql import util as sql_util
34+
from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL
3435

3536
_new_runid = util.counter()
3637

@@ -1396,7 +1397,9 @@ def load_scalar_attributes(mapper, state, attribute_names, passive):
13961397

13971398
result = load_on_ident(
13981399
session,
1399-
future.select(mapper).apply_labels(),
1400+
future.select(mapper).set_label_style(
1401+
LABEL_STYLE_TABLENAME_PLUS_COL
1402+
),
14001403
identity_key,
14011404
refresh_state=state,
14021405
only_load_props=attribute_names,

lib/sqlalchemy/orm/mapper.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
from ..sql import roles
5555
from ..sql import util as sql_util
5656
from ..sql import visitors
57+
from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL
5758
from ..util import HasMemoized
5859

5960

@@ -3008,7 +3009,11 @@ def visit_binary(binary):
30083009
cols = []
30093010
for key in col_attribute_names:
30103011
cols.extend(props[key].columns)
3011-
return sql.select(*cols).where(cond).apply_labels()
3012+
return (
3013+
sql.select(*cols)
3014+
.where(cond)
3015+
.set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
3016+
)
30123017

30133018
def _iterate_to_target_viawpoly(self, mapper):
30143019
if self.isa(mapper):

lib/sqlalchemy/orm/persistence.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
from ..sql.dml import DeleteDMLState
4141
from ..sql.dml import UpdateDMLState
4242
from ..sql.elements import BooleanClauseList
43+
from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL
4344

4445

4546
def _bulk_insert(
@@ -1521,7 +1522,9 @@ def _finalize_insert_update_commands(base_mapper, uowtransaction, states):
15211522

15221523
if toload_now:
15231524
state.key = base_mapper._identity_key_from_state(state)
1524-
stmt = future.select(mapper).apply_labels()
1525+
stmt = future.select(mapper).set_label_style(
1526+
LABEL_STYLE_TABLENAME_PLUS_COL
1527+
)
15251528
loading.load_on_ident(
15261529
uowtransaction.session,
15271530
stmt,

lib/sqlalchemy/orm/query.py

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ def subquery(
470470
"""
471471
q = self.enable_eagerloads(False)
472472
if with_labels:
473-
q = q.with_labels()
473+
q = q.set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
474474

475475
q = q.statement
476476

@@ -576,7 +576,11 @@ def scalar_subquery(self):
576576
return self.enable_eagerloads(False).statement.scalar_subquery()
577577

578578
def __clause_element__(self):
579-
return self.enable_eagerloads(False).with_labels().statement
579+
return (
580+
self.enable_eagerloads(False)
581+
.set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
582+
.statement
583+
)
580584

581585
@_generative
582586
def only_return_tuples(self, value):
@@ -637,8 +641,27 @@ def enable_eagerloads(self, value):
637641
"""
638642
self._compile_options += {"_enable_eagerloads": value}
639643

640-
@_generative
644+
@util.deprecated_20(
645+
":meth:`_orm.Query.with_labels` and :meth:`_orm.Query.apply_labels`",
646+
alternative="Use set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) "
647+
"instead.",
648+
)
641649
def with_labels(self):
650+
return self.set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
651+
652+
apply_labels = with_labels
653+
654+
@property
655+
def get_label_style(self):
656+
"""
657+
Retrieve the current label style.
658+
659+
.. versionadded:: 1.4
660+
661+
"""
662+
return self._label_style
663+
664+
def set_label_style(self, style):
642665
"""Apply column labels to the return value of Query.statement.
643666
644667
Indicates that this Query's `statement` accessor should return
@@ -650,26 +673,28 @@ def with_labels(self):
650673
When the `Query` actually issues SQL to load rows, it always
651674
uses column labeling.
652675
653-
.. note:: The :meth:`_query.Query.with_labels` method *only* applies
676+
.. note:: The :meth:`_query.Query.set_label_style` method *only* applies
654677
the output of :attr:`_query.Query.statement`, and *not* to any of
655678
the result-row invoking systems of :class:`_query.Query` itself,
656679
e.g.
657680
:meth:`_query.Query.first`, :meth:`_query.Query.all`, etc.
658681
To execute
659-
a query using :meth:`_query.Query.with_labels`, invoke the
682+
a query using :meth:`_query.Query.set_label_style`, invoke the
660683
:attr:`_query.Query.statement` using :meth:`.Session.execute`::
661684
662-
result = session.execute(query.with_labels().statement)
663-
664-
665-
"""
666-
self._label_style = LABEL_STYLE_TABLENAME_PLUS_COL
685+
result = session.execute(
686+
query
687+
.set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
688+
.statement
689+
)
667690
668-
apply_labels = with_labels
691+
.. versionadded:: 1.4
669692
670-
@property
671-
def use_labels(self):
672-
return self._label_style is LABEL_STYLE_TABLENAME_PLUS_COL
693+
""" # noqa
694+
if self._label_style is not style:
695+
self = self._generate()
696+
self._label_style = style
697+
return self
673698

674699
@_generative
675700
def enable_assertions(self, value):
@@ -1259,7 +1284,7 @@ def from_self(self, *entities):
12591284

12601285
def _from_self(self, *entities):
12611286
fromclause = (
1262-
self.with_labels()
1287+
self.set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
12631288
.enable_eagerloads(False)
12641289
.correlate(None)
12651290
.subquery()
@@ -2903,7 +2928,7 @@ def exists(self):
29032928
inner = (
29042929
self.enable_eagerloads(False)
29052930
.add_columns(sql.literal_column("1"))
2906-
.with_labels()
2931+
.set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
29072932
.statement.with_only_columns(1)
29082933
)
29092934

0 commit comments

Comments
 (0)