diff options
author | mike bayer <mike_mp@zzzcomputing.com> | 2021-07-13 14:25:13 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@ci3.zzzcomputing.com> | 2021-07-13 14:25:13 +0000 |
commit | 673ca806b323f47ef7064dd64ffc98240818b930 (patch) | |
tree | 56fbe5fde5a3892475949ce16e84793364bf0eb2 /lib/sqlalchemy/sql/compiler.py | |
parent | 326e3f5dc26f22ca51f4aa2509856d8077e76dec (diff) | |
parent | 707e5d70fcdcfaaddcd0aaee51f4f1b881e5e3e2 (diff) | |
download | sqlalchemy-673ca806b323f47ef7064dd64ffc98240818b930.tar.gz |
Merge "labeling refactor"
Diffstat (limited to 'lib/sqlalchemy/sql/compiler.py')
-rw-r--r-- | lib/sqlalchemy/sql/compiler.py | 176 |
1 files changed, 128 insertions, 48 deletions
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index e31a3839e..7007c2e86 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -35,7 +35,6 @@ from . import crud from . import elements from . import functions from . import operators -from . import roles from . import schema from . import selectable from . import sqltypes @@ -612,7 +611,7 @@ class SQLCompiler(Compiled): _loose_column_name_matching = False """tell the result object that the SQL staement is textual, wants to match - up to Column objects, and may be using the ._label in the SELECT rather + up to Column objects, and may be using the ._tq_label in the SELECT rather than the base name. """ @@ -1457,8 +1456,8 @@ class SQLCompiler(Compiled): if add_to_result_map is not None: targets = (column, name, column.key) + result_map_targets - if column._label: - targets += (column._label,) + if column._tq_label: + targets += (column._tq_label,) add_to_result_map(name, orig_name, targets, column.type) @@ -2816,6 +2815,24 @@ class SQLCompiler(Compiled): ) self._result_columns.append((keyname, name, objects, type_)) + def _label_returning_column(self, stmt, column, column_clause_args=None): + """Render a column with necessary labels inside of a RETURNING clause. + + This method is provided for individual dialects in place of calling + the _label_select_column method directly, so that the two use cases + of RETURNING vs. SELECT can be disambiguated going forward. + + .. versionadded:: 1.4.21 + + """ + return self._label_select_column( + None, + column, + True, + False, + {} if column_clause_args is None else column_clause_args, + ) + def _label_select_column( self, select, @@ -2824,6 +2841,8 @@ class SQLCompiler(Compiled): asfrom, column_clause_args, name=None, + proxy_name=None, + fallback_label_name=None, within_columns_clause=True, column_is_repeated=False, need_column_expressions=False, @@ -2867,9 +2886,17 @@ class SQLCompiler(Compiled): else: add_to_result_map = None - if not within_columns_clause: - result_expr = col_expr - elif isinstance(column, elements.Label): + # this method is used by some of the dialects for RETURNING, + # which has different inputs. _label_returning_column was added + # as the better target for this now however for 1.4 we will keep + # _label_select_column directly compatible with this use case. + # these assertions right now set up the current expected inputs + assert within_columns_clause, ( + "_label_select_column is only relevant within " + "the columns clause of a SELECT or RETURNING" + ) + + if isinstance(column, elements.Label): if col_expr is not column: result_expr = _CompileLabel( col_expr, column.name, alt_names=(column.element,) @@ -2877,50 +2904,91 @@ class SQLCompiler(Compiled): else: result_expr = col_expr - elif select is not None and name: - result_expr = _CompileLabel( - col_expr, name, alt_names=(column._key_label,) - ) - elif ( - asfrom - and isinstance(column, elements.ColumnClause) - and not column.is_literal - and column.table is not None - and not isinstance(column.table, selectable.Select) - ): - result_expr = _CompileLabel( - col_expr, - coercions.expect(roles.TruncatedLabelRole, column.name), - alt_names=(column.key,), - ) - elif ( - not isinstance(column, elements.TextClause) - and ( - not isinstance(column, elements.UnaryExpression) - or column.wraps_column_expression - or asfrom - ) - and ( - not hasattr(column, "name") - or isinstance(column, functions.FunctionElement) - ) - ): - result_expr = _CompileLabel( - col_expr, - column._anon_name_label - if not column_is_repeated - else column._dedupe_label_anon_label, - ) - elif col_expr is not column: - # TODO: are we sure "column" has a .name and .key here ? - # assert isinstance(column, elements.ColumnClause) + elif name: + # here, _columns_plus_names has determined there's an explicit + # label name we need to use. this is the default for + # tablenames_plus_columnnames as well as when columns are being + # deduplicated on name + + assert ( + proxy_name is not None + ), "proxy_name is required if 'name' is passed" + result_expr = _CompileLabel( col_expr, - coercions.expect(roles.TruncatedLabelRole, column.name), - alt_names=(column.key,), + name, + alt_names=( + proxy_name, + # this is a hack to allow legacy result column lookups + # to work as they did before; this goes away in 2.0. + # TODO: this only seems to be tested indirectly + # via test/orm/test_deprecations.py. should be a + # resultset test for this + column._tq_label, + ), ) else: - result_expr = col_expr + # determine here whether this column should be rendered in + # a labelled context or not, as we were given no required label + # name from the caller. Here we apply heuristics based on the kind + # of SQL expression involved. + + if col_expr is not column: + # type-specific expression wrapping the given column, + # so we render a label + render_with_label = True + elif isinstance(column, elements.ColumnClause): + # table-bound column, we render its name as a label if we are + # inside of a subquery only + render_with_label = ( + asfrom + and not column.is_literal + and column.table is not None + ) + elif isinstance(column, elements.TextClause): + render_with_label = False + elif isinstance(column, elements.UnaryExpression): + render_with_label = column.wraps_column_expression or asfrom + elif ( + # general class of expressions that don't have a SQL-column + # addressible name. includes scalar selects, bind parameters, + # SQL functions, others + not isinstance(column, elements.NamedColumn) + # deeper check that indicates there's no natural "name" to + # this element, which accommodates for custom SQL constructs + # that might have a ".name" attribute (but aren't SQL + # functions) but are not implementing this more recently added + # base class. in theory the "NamedColumn" check should be + # enough, however here we seek to maintain legacy behaviors + # as well. + and column._non_anon_label is None + ): + render_with_label = True + else: + render_with_label = False + + if render_with_label: + if not fallback_label_name: + # used by the RETURNING case right now. we generate it + # here as 3rd party dialects may be referring to + # _label_select_column method directly instead of the + # just-added _label_returning_column method + assert not column_is_repeated + fallback_label_name = column._anon_name_label + + fallback_label_name = ( + elements._truncated_label(fallback_label_name) + if not isinstance( + fallback_label_name, elements._truncated_label + ) + else fallback_label_name + ) + + result_expr = _CompileLabel( + col_expr, fallback_label_name, alt_names=(proxy_name,) + ) + else: + result_expr = col_expr column_clause_args.update( within_columns_clause=within_columns_clause, @@ -3096,10 +3164,18 @@ class SQLCompiler(Compiled): asfrom, column_clause_args, name=name, + proxy_name=proxy_name, + fallback_label_name=fallback_label_name, column_is_repeated=repeated, need_column_expressions=need_column_expressions, ) - for name, column, repeated in compile_state.columns_plus_names + for ( + name, + proxy_name, + fallback_label_name, + column, + repeated, + ) in compile_state.columns_plus_names ] if c is not None ] @@ -3114,6 +3190,8 @@ class SQLCompiler(Compiled): name for ( key, + proxy_name, + fallback_label_name, name, repeated, ) in compile_state.columns_plus_names @@ -3122,6 +3200,8 @@ class SQLCompiler(Compiled): name for ( key, + proxy_name, + fallback_label_name, name, repeated, ) in compile_state_wraps_for.columns_plus_names |