]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Added a supported :meth:`.FunctionElement.alias` method to functions,
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 24 Jul 2014 18:33:50 +0000 (14:33 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 24 Jul 2014 18:33:50 +0000 (14:33 -0400)
e.g. the ``func`` construct.  Previously, behavior for this method
was undefined.  The current behavior mimics that of pre-0.9.4,
which is that the function is turned into a single-column FROM
clause with the given alias name, where the column itself is
anonymously named.
fixes #3137

doc/build/changelog/changelog_09.rst
lib/sqlalchemy/sql/base.py
lib/sqlalchemy/sql/functions.py
test/sql/test_functions.py

index 3ec09076bcfd0e0a3484529b48e25f94628f23b8..44b633a1d4c38664a5c728119bc236f298339817 100644 (file)
 .. changelog::
     :version: 0.9.8
 
+    .. change::
+        :tags: bug, postgresql
+        :versions: 1.0.0
+        :tickets: 3137
+
+        Added a supported :meth:`.FunctionElement.alias` method to functions,
+        e.g. the ``func`` construct.  Previously, behavior for this method
+        was undefined.  The current behavior mimics that of pre-0.9.4,
+        which is that the function is turned into a single-column FROM
+        clause with the given alias name, where the column itself is
+        anonymously named.
+
 .. changelog::
     :version: 0.9.7
     :released: July 22, 2014
index 5358d95b5c8e5b16d3099aee71cc850981ad96c6..2d06109b9780f67aabd8f02923f9f511d181d052 100644 (file)
@@ -449,10 +449,12 @@ class ColumnCollection(util.OrderedProperties):
 
     """
 
-    def __init__(self):
+    def __init__(self, *columns):
         super(ColumnCollection, self).__init__()
         self.__dict__['_all_col_set'] = util.column_set()
         self.__dict__['_all_columns'] = []
+        for c in columns:
+            self.add(c)
 
     def __str__(self):
         return repr([str(c) for c in self])
index 11e75836481d6a0eca70b5ed7251f01f42856fcd..7efb1e916e57e240b1c473553856b7824788feb8 100644 (file)
@@ -9,11 +9,11 @@
 
 """
 from . import sqltypes, schema
-from .base import Executable
+from .base import Executable, ColumnCollection
 from .elements import ClauseList, Cast, Extract, _literal_as_binds, \
     literal_column, _type_from_args, ColumnElement, _clone,\
     Over, BindParameter
-from .selectable import FromClause, Select
+from .selectable import FromClause, Select, Alias
 
 from . import operators
 from .visitors import VisitableType
@@ -67,12 +67,24 @@ class FunctionElement(Executable, ColumnElement, FromClause):
 
     @property
     def columns(self):
-        """Fulfill the 'columns' contract of :class:`.ColumnElement`.
+        """The set of columns exported by this :class:`.FunctionElement`.
+
+        Function objects currently have no result column names built in;
+        this method returns a single-element column collection with
+        an anonymously named column.
+
+        An interim approach to providing named columns for a function
+        as a FROM clause is to build a :func:`.select` with the
+        desired columns::
+
+            from sqlalchemy.sql import column
+
+            stmt = select([column('x'), column('y')]).\
+                select_from(func.myfunction())
 
-        Returns a single-element list consisting of this object.
 
         """
-        return [self]
+        return ColumnCollection(self.label(None))
 
     @util.memoized_property
     def clauses(self):
@@ -116,6 +128,36 @@ class FunctionElement(Executable, ColumnElement, FromClause):
         self._reset_exported()
         FunctionElement.clauses._reset(self)
 
+    def alias(self, name=None, flat=False):
+        """Produce a :class:`.Alias` construct against this
+        :class:`.FunctionElement`.
+
+        This construct wraps the function in a named alias which
+        is suitable for the FROM clause.
+
+        e.g.::
+
+            from sqlalchemy.sql import column
+
+            stmt = select([column('data')]).select_from(
+                func.unnest(Table.data).alias('data_view')
+            )
+
+        Would produce:
+
+        .. sourcecode:: sql
+
+            SELECT data
+            FROM unnest(sometable.data) AS data_view
+
+        .. versionadded:: 0.9.8 The :meth:`.FunctionElement.alias` method
+           is now supported.  Previously, this method's behavior was
+           undefined and did not behave consistently across versions.
+
+        """
+
+        return Alias(self, name)
+
     def select(self):
         """Produce a :func:`~.expression.select` construct
         against this :class:`.FunctionElement`.
index 4d066be8a406163324dcb0f2c98b5e46f861293f..d3b7186459eaf0e8c651c0073cb983bffbbcbef4 100644 (file)
@@ -316,6 +316,57 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL):
         expr = func.extract("year", datetime.date(2010, 12, 5))
         self.assert_compile(expr, "EXTRACT(year FROM :param_1)")
 
+    def test_select_method_one(self):
+        expr = func.rows("foo")
+        self.assert_compile(
+            expr.select(),
+            "SELECT rows(:rows_2) AS rows_1"
+        )
+
+    def test_alias_method_one(self):
+        expr = func.rows("foo")
+        self.assert_compile(
+            expr.alias(),
+            "rows(:rows_1)"
+        )
+
+    def test_select_method_two(self):
+        expr = func.rows("foo")
+        self.assert_compile(
+            select(['*']).select_from(expr.select()),
+            "SELECT * FROM (SELECT rows(:rows_2) AS rows_1)"
+        )
+
+    def test_select_method_three(self):
+        expr = func.rows("foo")
+        self.assert_compile(
+            select(['foo']).select_from(expr),
+            "SELECT foo FROM rows(:rows_1)"
+        )
+
+    def test_alias_method_two(self):
+        expr = func.rows("foo")
+        self.assert_compile(
+            select(['*']).select_from(expr.alias('bar')),
+            "SELECT * FROM rows(:rows_1) AS bar"
+        )
+
+    def test_alias_method_columns(self):
+        expr = func.rows("foo").alias('bar')
+
+        # this isn't very useful but is the old behavior
+        # prior to #2974.
+        # testing here that the expression exports its column
+        # list in a way that at least doesn't break.
+        self.assert_compile(
+            select([expr]),
+            "SELECT bar.rows_1 FROM rows(:rows_2) AS bar"
+        )
+
+    def test_alias_method_columns_two(self):
+        expr = func.rows("foo").alias('bar')
+        assert len(expr.c)
+
 
 class ExecuteTest(fixtures.TestBase):