summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql/expression.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2009-01-03 02:42:34 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2009-01-03 02:42:34 +0000
commit9fe69cb503de4bcbace8d74b14dd6d096d457e72 (patch)
tree7ca3cc8d4cd983e5c5f17cea4f98160b918ca2b3 /lib/sqlalchemy/sql/expression.py
parent0bd31484ea0698275bd34653f63fb8c6668ef369 (diff)
downloadsqlalchemy-9fe69cb503de4bcbace8d74b14dd6d096d457e72.tar.gz
- Fixed some deep "column correspondence" issues which could
impact a Query made against a selectable containing multiple versions of the same table, as well as unions and similar which contained the same table columns in different column positions at different levels. [ticket:1268]
Diffstat (limited to 'lib/sqlalchemy/sql/expression.py')
-rw-r--r--lib/sqlalchemy/sql/expression.py38
1 files changed, 33 insertions, 5 deletions
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py
index 7204e2956..7eeff0660 100644
--- a/lib/sqlalchemy/sql/expression.py
+++ b/lib/sqlalchemy/sql/expression.py
@@ -33,6 +33,7 @@ from sqlalchemy import util, exc
from sqlalchemy.sql import operators
from sqlalchemy.sql.visitors import Visitable, cloned_traverse
from sqlalchemy import types as sqltypes
+import operator
functions, schema, sql_util = None, None, None
DefaultDialect, ClauseAdapter, Annotated = None, None, None
@@ -1840,9 +1841,32 @@ class FromClause(Selectable):
for c in cols:
i = c.proxy_set.intersection(target_set)
if i and \
- (not require_embedded or c.proxy_set.issuperset(target_set)) and \
- (intersect is None or len(i) > len(intersect)):
- col, intersect = c, i
+ (not require_embedded or c.proxy_set.issuperset(target_set)):
+
+ if col is None:
+ # no corresponding column yet, pick this one.
+ col, intersect = c, i
+ elif len(i) > len(intersect):
+ # 'c' has a larger field of correspondence than 'col'.
+ # i.e. selectable.c.a1_x->a1.c.x->table.c.x matches a1.c.x->table.c.x better than
+ # selectable.c.x->table.c.x does.
+ col, intersect = c, i
+ elif i == intersect:
+ # they have the same field of correspondence.
+ # see which proxy_set has fewer columns in it, which indicates a
+ # closer relationship with the root column. Also take into account the
+ # "weight" attribute which CompoundSelect() uses to give higher precedence to
+ # columns based on vertical position in the compound statement, and discard columns
+ # that have no reference to the target column (also occurs with CompoundSelect)
+ col_distance = util.reduce(operator.add,
+ [sc._annotations.get('weight', 1) for sc in col.proxy_set if sc.shares_lineage(column)]
+ )
+ c_distance = util.reduce(operator.add,
+ [sc._annotations.get('weight', 1) for sc in c.proxy_set if sc.shares_lineage(column)]
+ )
+ if \
+ c_distance < col_distance:
+ col, intersect = c, i
return col
@property
@@ -3097,8 +3121,12 @@ class CompoundSelect(_SelectBaseMixin, FromClause):
def _populate_column_collection(self):
for cols in zip(*[s.c for s in self.selects]):
proxy = cols[0]._make_proxy(self, name=self.use_labels and cols[0]._label or None)
- proxy.proxies = cols
-
+
+ # place a 'weight' annotation corresponding to how low in the list of select()s
+ # the column occurs, so that the corresponding_column() operation
+ # can resolve conflicts
+ proxy.proxies = [c._annotate({'weight':i + 1}) for i, c in enumerate(cols)]
+
def _copy_internals(self, clone=_clone):
self._reset_exported()
self.selects = [clone(s) for s in self.selects]