summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2013-05-27 19:22:59 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2013-05-27 19:22:59 -0400
commita9ed16f80d5e6d96d800004953b555b9cf1a592e (patch)
tree253f56891eea6e9edcd333d03daac6cbb5dfb600
parente2b8c893ca98bb0141405a7d7d40c4024d5bdf41 (diff)
downloadsqlalchemy-a9ed16f80d5e6d96d800004953b555b9cf1a592e.tar.gz
attempt number one, doesn't detect though if the label in the order by is not directly present there.
-rw-r--r--lib/sqlalchemy/engine/default.py2
-rw-r--r--lib/sqlalchemy/sql/compiler.py22
-rw-r--r--test/sql/test_compiler.py63
3 files changed, 84 insertions, 3 deletions
diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py
index daa9fe085..dc45e12b1 100644
--- a/lib/sqlalchemy/engine/default.py
+++ b/lib/sqlalchemy/engine/default.py
@@ -52,6 +52,8 @@ class DefaultDialect(interfaces.Dialect):
supports_native_enum = False
supports_native_boolean = False
+ supports_simple_order_by_label = True
+
# if the NUMERIC type
# returns decimal.Decimal.
# *not* the FLOAT type however.
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index b902f9ffc..8eb8c5fd8 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -389,16 +389,24 @@ class SQLCompiler(engine.Compiled):
def visit_label(self, label,
add_to_result_map=None,
within_label_clause=False,
- within_columns_clause=False, **kw):
+ within_columns_clause=False,
+ order_by_labels=None, **kw):
# only render labels within the columns clause
# or ORDER BY clause of a select. dialect-specific compilers
# can modify this behavior.
- if within_columns_clause and not within_label_clause:
+# if order_by_labels:
+# import pdb
+# pdb.set_trace()
+ render_label_with_as = within_columns_clause and not within_label_clause
+ render_label_only = order_by_labels and label in order_by_labels
+
+ if render_label_only or render_label_with_as:
if isinstance(label.name, sql._truncated_label):
labelname = self._truncated_identifier("colident", label.name)
else:
labelname = label.name
+ if render_label_with_as:
if add_to_result_map is not None:
add_to_result_map(
labelname,
@@ -413,6 +421,8 @@ class SQLCompiler(engine.Compiled):
**kw) + \
OPERATORS[operators.as_] + \
self.preparer.format_label(label, labelname)
+ elif render_label_only:
+ return labelname
else:
return label.element._compiler_dispatch(self,
within_columns_clause=False,
@@ -1181,7 +1191,13 @@ class SQLCompiler(engine.Compiled):
text += " \nHAVING " + t
if select._order_by_clause.clauses:
- text += self.order_by_clause(select, **kwargs)
+ if self.dialect.supports_simple_order_by_label:
+ order_by_labels = set(c for k, c in select._columns_plus_names)
+ else:
+ order_by_labels = None
+
+ text += self.order_by_clause(select,
+ order_by_labels=order_by_labels, **kwargs)
if select._limit is not None or select._offset is not None:
text += self.limit_clause(select)
if select.for_update:
diff --git a/test/sql/test_compiler.py b/test/sql/test_compiler.py
index 9cd893c1a..887676f94 100644
--- a/test/sql/test_compiler.py
+++ b/test/sql/test_compiler.py
@@ -746,6 +746,69 @@ class SelectTest(fixtures.TestBase, AssertsCompiledSQL):
'foo || :param_1')
+ def test_labels_in_expressions(self):
+ """test that label() constructs in ORDER BY render as the labelname.
+
+ Postgres' behavior was used as the guide for this,
+ so that only a simple label expression
+ and not a more complex expression involving the label
+ name would be rendered using the label name.
+
+ """
+ lab1 = (table1.c.myid + "12").label('foo')
+ lab2 = func.somefunc(table1.c.name).label('bar')
+
+ dialect = default.DefaultDialect()
+ self.assert_compile(select([lab1, lab2]).order_by(lab1, desc(lab2)),
+ "SELECT mytable.myid + :myid_1 AS foo, "
+ "somefunc(mytable.name) AS bar FROM mytable "
+ "ORDER BY foo, bar DESC",
+ dialect=dialect
+ )
+
+ # the function embedded label renders as the function
+ self.assert_compile(
+ select([lab1, lab2]).order_by(func.hoho(lab1), desc(lab2)),
+ "SELECT mytable.myid + :myid_1 AS foo, "
+ "somefunc(mytable.name) AS bar FROM mytable "
+ "ORDER BY hoho(mytable.myid + :myid_1), bar DESC",
+ dialect=dialect
+ )
+
+ # binary expressions render as the expression without labels
+ self.assert_compile(select([lab1, lab2]).order_by(lab1 + "test"),
+ "SELECT mytable.myid + :myid_1 AS foo, "
+ "somefunc(mytable.name) AS bar FROM mytable "
+ "ORDER BY mytable.myid + :myid_1 + :param_1",
+ dialect=dialect
+ )
+
+ # labels within functions in the columns clause render
+ # with the expression
+ self.assert_compile(
+ select([lab1, func.foo(lab1)]),
+ "SELECT mytable.myid + :myid_1 AS foo, "
+ "foo(mytable.myid + :myid_1) AS foo_1 FROM mytable",
+ dialect=dialect
+ )
+
+ dialect = default.DefaultDialect()
+ dialect.supports_simple_order_by_label = False
+ self.assert_compile(select([lab1, lab2]).order_by(lab1, desc(lab2)),
+ "SELECT mytable.myid + :myid_1 AS foo, "
+ "somefunc(mytable.name) AS bar FROM mytable "
+ "ORDER BY mytable.myid + :myid_1, somefunc(mytable.name) DESC",
+ dialect=dialect
+ )
+ self.assert_compile(
+ select([lab1, lab2]).order_by(func.hoho(lab1), desc(lab2)),
+ "SELECT mytable.myid + :myid_1 AS foo, "
+ "somefunc(mytable.name) AS bar FROM mytable "
+ "ORDER BY hoho(mytable.myid + :myid_1), "
+ "somefunc(mytable.name) DESC",
+ dialect=dialect
+ )
+
def test_conjunctions(self):
a, b, c = 'a', 'b', 'c'
x = and_(a, b, c)