Skip to content

Commit 6bba01e

Browse files
zzzeekGerrit Code Review
authored andcommitted
Merge "Replace with_labels() and apply_labels() in ORM/Core"
2 parents b6f83d6 + 22f6515 commit 6bba01e

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)