clarify rules of Executable propagation of .bind

Issue #3519 new
Joseph Jevnik created an issue

If I have an expression where the bind is set to some engine and then call .label() on it, the result will have a bind=None.

Comments (4)

  1. Mike Bayer repo owner

    hello -

    can you please provide a short example? The .label() method is typically invoked from column expressions which themselves to not have a "bind" attribute. There is a .label() method on the select() construct, but again that converts it into a column based scalar select.

    Also, bound metadata is an antipattern that is discouraged in the documentation. I would seek to not make use of it.

  2. Joseph Jevnik reporter

    Thank you for the quick reply! The particular case was that I had a function object with a bind set. I realize now that this problem is not label specific but actually function specific. For example:

    In [11]: a.bind
    Out[11]: Engine(postgresql://localhost/test)
    
    In [12]: (sa.func.f(bind=a.bind) + 1).bind
    

    It appears expressions that have bound functions do not inherit the bind like a select would.

    I am using this api to pass around the expression and dataset as a single entity for work on the sqlalchemy backend of blaze (https://github.com/blaze/blaze)

  3. Mike Bayer repo owner

    OK, a Function is special in that it is a column but also acts like a "from" object, so it has a .bind, but once you put it in a column expression, its no longer a "bound" element.

    There was a really old issue looking for this same thing, I'll never find it but im pretty sure it was a wontfix. I'd really recommend you try not to rely on bound metadata.

  4. Mike Bayer repo owner

    in this case we need to decide what it means when an object that is Executable is passed as a column to a select([]) statement. The patch below for example can change this behavior so that Function retains the ".bind" when used with func.foo().select(), as was the main intention of the .bind attribute on Function, but explicitly does not get the .bind when just passed to a select() object, which would be more consistent with every other column-oriented construct.

    However, func is special because select([column])] implies that the column has a Table in the FROM, and we get a bind in that case; a Function isn't like this, and we don't have logic now to search down into the expression for non-FROM-oriented binds, and I don't want to add one because it would crush performance.

    diff --git a/lib/sqlalchemy/sql/functions.py b/lib/sqlalchemy/sql/functions.py
    index 6cfbd12..643824b 100644
    --- a/lib/sqlalchemy/sql/functions.py
    +++ b/lib/sqlalchemy/sql/functions.py
    @@ -223,7 +223,7 @@ class FunctionElement(Executable, ColumnElement, FromClause):
                 s = select([function_element])
    
             """
    -        s = Select([self])
    +        s = Select([self], bind=self._bind)
             if self._execution_options:
                 s = s.execution_options(**self._execution_options)
             return s
    diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py
    index 7334105..cc1e9a8 100644
    --- a/lib/sqlalchemy/sql/selectable.py
    +++ b/lib/sqlalchemy/sql/selectable.py
    @@ -3281,13 +3281,7 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect):
             if self._bind:
                 return self._bind
             froms = self._froms
    -        if not froms:
    -            for c in self._raw_columns:
    -                e = c.bind
    -                if e:
    -                    self._bind = e
    -                    return e
    -        else:
    +        if froms:
                 e = list(froms)[0].bind
                 if e:
                     self._bind = e
    

    I don't think I'm ready to move on this in 1.1 or 1.2 quite yet so putting it out for now.

  5. Log in to comment