summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2019-10-05 00:05:33 +0000
committerGerrit Code Review <gerrit@bbpush.zzzcomputing.com>2019-10-05 00:05:33 +0000
commitc6abd4766abb0396c9bf532d81d16226b970a35a (patch)
tree5ac87ae9557926cceacc2a58ece4b43053a6798f /lib/sqlalchemy/sql
parentb73c191903d5f23b02188474f6a8a1b877988a40 (diff)
parent485216dea6d7a5814d200b4f14b8a363ed0f8caa (diff)
downloadsqlalchemy-c6abd4766abb0396c9bf532d81d16226b970a35a.tar.gz
Merge "Deprecate textual column matching in Row"
Diffstat (limited to 'lib/sqlalchemy/sql')
-rw-r--r--lib/sqlalchemy/sql/compiler.py29
-rw-r--r--lib/sqlalchemy/sql/elements.py9
-rw-r--r--lib/sqlalchemy/sql/selectable.py5
-rw-r--r--lib/sqlalchemy/sql/util.py4
4 files changed, 38 insertions, 9 deletions
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index 5e432a74c..1381e734c 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -490,6 +490,13 @@ class SQLCompiler(Compiled):
True unless using an unordered TextualSelect.
"""
+ _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
+ than the base name.
+
+ """
+
_numeric_binds = False
"""
True if paramstyle is "numeric". This paramstyle is trickier than
@@ -799,6 +806,7 @@ class SQLCompiler(Compiled):
within_label_clause=False,
within_columns_clause=False,
render_label_as_label=None,
+ result_map_targets=(),
**kw
):
# only render labels within the columns clause
@@ -820,7 +828,7 @@ class SQLCompiler(Compiled):
add_to_result_map(
labelname,
label.name,
- (label, labelname) + label._alt_names,
+ (label, labelname) + label._alt_names + result_map_targets,
label.type,
)
@@ -847,7 +855,12 @@ class SQLCompiler(Compiled):
)
def visit_column(
- self, column, add_to_result_map=None, include_table=True, **kwargs
+ self,
+ column,
+ add_to_result_map=None,
+ include_table=True,
+ result_map_targets=(),
+ **kwargs
):
name = orig_name = column.name
if name is None:
@@ -859,7 +872,10 @@ class SQLCompiler(Compiled):
if add_to_result_map is not None:
add_to_result_map(
- name, orig_name, (column, name, column.key), column.type
+ name,
+ orig_name,
+ (column, name, column.key, column._label) + result_map_targets,
+ column.type,
)
if is_literal:
@@ -948,6 +964,13 @@ class SQLCompiler(Compiled):
self._ordered_columns = (
self._textual_ordered_columns
) = taf.positional
+
+ # enable looser result column matching when the SQL text links to
+ # Column objects by name only
+ self._loose_column_name_matching = not taf.positional and bool(
+ taf.column_args
+ )
+
for c in taf.column_args:
self.process(
c,
diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py
index 3045cb84e..8ee157b6f 100644
--- a/lib/sqlalchemy/sql/elements.py
+++ b/lib/sqlalchemy/sql/elements.py
@@ -4278,7 +4278,13 @@ class ColumnClause(roles.LabeledColumnExprRole, Immutable, ColumnElement):
label = quoted_name(label, t.name.quote)
# ensure the label name doesn't conflict with that
- # of an existing column
+ # of an existing column. note that this implies that any
+ # Column must **not** set up its _label before its parent table
+ # has all of its other Column objects set up. There are several
+ # tables in the test suite which will fail otherwise; example:
+ # table "owner" has columns "name" and "owner_name". Therefore
+ # column owner.name cannot use the label "owner_name", it has
+ # to be "owner_name_1".
if label in t.c:
_label = label
counter = 1
@@ -4339,7 +4345,6 @@ class ColumnClause(roles.LabeledColumnExprRole, Immutable, ColumnElement):
c._proxies = [self]
if selectable._is_clone_of is not None:
c._is_clone_of = selectable._is_clone_of.columns.get(c.key)
-
return c.key, c
diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py
index b41a77622..33ba95717 100644
--- a/lib/sqlalchemy/sql/selectable.py
+++ b/lib/sqlalchemy/sql/selectable.py
@@ -4449,7 +4449,10 @@ class TextualSelect(SelectBase):
def __init__(self, text, columns, positional=False):
self.element = text
- self.column_args = columns
+ # convert for ORM attributes->columns, etc
+ self.column_args = [
+ coercions.expect(roles.ColumnsClauseRole, c) for c in columns
+ ]
self.positional = positional
@SelectBase._memoized_property
diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py
index 780cdc7b2..5aeed0c1c 100644
--- a/lib/sqlalchemy/sql/util.py
+++ b/lib/sqlalchemy/sql/util.py
@@ -798,9 +798,7 @@ class ClauseAdapter(visitors.ReplacingCloningVisitor):
if newcol is not None:
return newcol
if self.adapt_on_names and newcol is None:
- # TODO: this should be changed to .exported_columns if and
- # when we need to be able to adapt a plain Select statement
- newcol = self.selectable.c.get(col.name)
+ newcol = self.selectable.exported_columns.get(col.name)
return newcol
def replace(self, col):