clarify rules of Executable propagation of .bind
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)
-
repo owner -
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)
-
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.
-
repo owner - changed milestone to 1.x.xx
- changed title to clarify rules of Executable propagation of .bind
- changed component to sql
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.
- Log in to comment
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.