diff options
Diffstat (limited to 'lib/sqlalchemy/sql')
-rw-r--r-- | lib/sqlalchemy/sql/expression.py | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/operators.py | 4 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/util.py | 28 |
3 files changed, 31 insertions, 3 deletions
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index 9dd7bd335..66a87c26f 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -3710,7 +3710,7 @@ class _Label(ColumnElement): sub_element, type_=self._type) else: - return self._element + return self @property def primary_key(self): diff --git a/lib/sqlalchemy/sql/operators.py b/lib/sqlalchemy/sql/operators.py index dcea5a0f6..db6c40e9a 100644 --- a/lib/sqlalchemy/sql/operators.py +++ b/lib/sqlalchemy/sql/operators.py @@ -524,6 +524,10 @@ _commutative = set([eq, ne, add, mul]) def is_commutative(op): return op in _commutative +def is_ordering_modifier(op): + return op in (asc_op, desc_op, + nullsfirst_op, nullslast_op) + _associative = _commutative.union([concat_op, and_, or_]) diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py index 1a3f7d2f8..f003a9691 100644 --- a/lib/sqlalchemy/sql/util.py +++ b/lib/sqlalchemy/sql/util.py @@ -8,6 +8,7 @@ from sqlalchemy import exc, schema, util, sql, types as sqltypes from sqlalchemy.util import topological from sqlalchemy.sql import expression, operators, visitors from itertools import chain +from collections import deque """Utility functions that build upon SQL and Schema constructs.""" @@ -99,6 +100,25 @@ def find_columns(clause): visitors.traverse(clause, {}, {'column':cols.add}) return cols +def unwrap_order_by(clause): + """Break up an 'order by' expression into individual column-expressions, + without DESC/ASC/NULLS FIRST/NULLS LAST""" + + cols = util.column_set() + stack = deque([clause]) + while stack: + t = stack.popleft() + if isinstance(t, expression.ColumnElement) and \ + ( + not isinstance(t, expression._UnaryExpression) or \ + not operators.is_ordering_modifier(t.modifier) + ): + cols.add(t) + else: + for c in t.get_children(): + stack.append(c) + return cols + def clause_is_present(clause, search): """Given a target clause and a second to search within, return True if the target is plainly present in the search without any @@ -624,11 +644,15 @@ class ClauseAdapter(visitors.ReplacingCloningVisitor): self.equivalents = util.column_dict(equivalents or {}) def _corresponding_column(self, col, require_embedded, _seen=util.EMPTY_SET): - newcol = self.selectable.corresponding_column(col, require_embedded=require_embedded) + newcol = self.selectable.corresponding_column( + col, + require_embedded=require_embedded) if newcol is None and col in self.equivalents and col not in _seen: for equiv in self.equivalents[col]: - newcol = self._corresponding_column(equiv, require_embedded=require_embedded, _seen=_seen.union([col])) + newcol = self._corresponding_column(equiv, + require_embedded=require_embedded, + _seen=_seen.union([col])) if newcol is not None: return newcol return newcol |