diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2021-01-21 11:15:06 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2021-01-21 11:15:06 -0500 |
commit | 851a3a362ee5e05b8438f92e2e1df63c68f79d68 (patch) | |
tree | 2d862d02a1369d1730d78c933e09b709d2ef8bf6 /lib/sqlalchemy/sql/functions.py | |
parent | 05a31f2708590161d4b3b4c7ff65196c99b4a22b (diff) | |
download | sqlalchemy-851a3a362ee5e05b8438f92e2e1df63c68f79d68.tar.gz |
Revert "Implement support for functions as FROM with columns clause support"
This reverts commit 05a31f2708590161d4b3b4c7ff65196c99b4a22b.
Atom has this little button called "push" and just pushes to master,
I wasn't even *on* master. oops
Diffstat (limited to 'lib/sqlalchemy/sql/functions.py')
-rw-r--r-- | lib/sqlalchemy/sql/functions.py | 272 |
1 files changed, 22 insertions, 250 deletions
diff --git a/lib/sqlalchemy/sql/functions.py b/lib/sqlalchemy/sql/functions.py index f493b08db..a9ea98d04 100644 --- a/lib/sqlalchemy/sql/functions.py +++ b/lib/sqlalchemy/sql/functions.py @@ -17,25 +17,22 @@ from . import sqltypes from . import util as sqlutil from .base import ColumnCollection from .base import Executable -from .base import Generative from .base import HasMemoized from .elements import _type_from_args from .elements import BinaryExpression from .elements import BindParameter from .elements import Cast from .elements import ClauseList -from .elements import ColumnClause from .elements import ColumnElement from .elements import Extract from .elements import FunctionFilter from .elements import Grouping from .elements import literal_column -from .elements import NamedColumn from .elements import Over from .elements import WithinGroup +from .selectable import Alias from .selectable import FromClause from .selectable import Select -from .selectable import TableValuedAlias from .visitors import InternalTraversal from .visitors import TraversibleType from .. import util @@ -66,7 +63,7 @@ def register_function(identifier, fn, package="_default"): reg[identifier] = fn -class FunctionElement(Executable, ColumnElement, FromClause, Generative): +class FunctionElement(Executable, ColumnElement, FromClause): """Base for SQL function-oriented constructs. .. seealso:: @@ -83,19 +80,11 @@ class FunctionElement(Executable, ColumnElement, FromClause, Generative): """ - _traverse_internals = [ - ("clause_expr", InternalTraversal.dp_clauseelement), - ("_table_valued", InternalTraversal.dp_clauseelement_tuple), - ("_table_values_named", InternalTraversal.dp_boolean), - ("_with_ordinality", InternalTraversal.dp_boolean), - ] + _traverse_internals = [("clause_expr", InternalTraversal.dp_clauseelement)] packagenames = () _has_args = False - _table_valued = None - _table_values_named = False - _with_ordinality = False def __init__(self, *clauses, **kwargs): r"""Construct a :class:`.FunctionElement`. @@ -134,171 +123,6 @@ class FunctionElement(Executable, ColumnElement, FromClause, Generative): self, multiparams, params, execution_options ) - def scalar_table_valued(self, name, type_=None): - """Return a column expression that's against this - :class:`_functions.FunctionElement` as a scalar - table-valued expression. - - The returned expression is similar to that returned by a single column - accessed off of a :meth:`_functions.FunctionElement.table_valued` - construct, except no FROM clause is generated; the function is rendered - in the similar way as a scalar subquery. - - E.g.:: - - >>> from sqlalchemy import func, select - >>> fn = func.jsonb_each("{'k', 'v'}").scalar_table_valued("key") - >>> print(select(fn)) - SELECT (jsonb_each(:jsonb_each_1)).key - - .. versionadded:: 1.4.0b2 - - .. seealso:: - - :meth:`_functions.FunctionElement.table_valued` - - :meth:`_functions.FunctionElement.named_table_valued` - - :meth:`_functions.FunctionElement.alias` - - :meth:`_functions.FunctionElement.column_valued` - - """ # noqa E501 - - return ScalarFunctionColumn(self, name, type_) - - def table_valued(self, *expr, **kw): - """Return a :class:`_sql.TableValuedAlias` representation of this - :class:`_functions.FunctionElement` with table-valued expressions added. - - e.g.:: - - >>> fn = ( - ... func.generate_series(1, 5). - ... table_valued("value", "start", "stop", "step") - ... ) - - >>> print(select(fn)) - SELECT anon_1.value, anon_1.start, anon_1.stop, anon_1.step - FROM generate_series(:generate_series_1, :generate_series_2) AS anon_1 - - >>> print(select(fn.c.value, fn.c.stop).where(fn.c.value > 2)) - SELECT anon_1.value, anon_1.stop - FROM generate_series(:generate_series_1, :generate_series_2) AS anon_1 - WHERE anon_1.value > :value_1 - - A WITH ORDINALITY expression may be generated by passing the keyword - argument "with_ordinality":: - - >>> fn = func.generate_series(4, 1, -1).table_valued("gen", with_ordinality="ordinality") - >>> print(select(fn)) - - .. versionadded:: 1.4.0b2 - - .. seealso:: - - :meth:`_functions.FunctionElement.table_valued` - - :meth:`_functions.FunctionElement.named_table_valued` - - :meth:`_functions.FunctionElement.alias` - - - """ # noqa 501 - - new_func = self._generate() - new_func._table_valued = [ - coercions.expect(roles.StrAsPlainColumnRole, elem) for elem in expr - ] - - with_ordinality = kw.pop("with_ordinality", None) - if with_ordinality: - new_func._table_valued += ( - coercions.expect(roles.StrAsPlainColumnRole, with_ordinality), - ) - new_func._with_ordinality = True - - return new_func.alias() - - def named_table_valued(self, *expr, **kw): - """Return a :class:`_sql.TableValuedAlias` representation of this - :class:`_functions.FunctionElement` with named table-valued - expressions added. - - E.g.:: - - - >>> fn = ( - ... func.json_to_recordset( - ... '[{"a":1,"b":"foo"},{"a":"2","c":"bar"}]' - ... ) - ... .named_table_valued(column("a", Integer), column("b", String)) - ... ) - >>> print(select(fn.c.a, fn.c.b)) - SELECT anon_1.a, anon_1.b - FROM json_to_recordset(:json_to_recordset_1) AS anon_1(a INTEGER, b VARCHAR) - - A WITH ORDINALITY expression may be generated by passing the keyword - argument "with_ordinality":: - - >>> fn = ( - ... func.json_object_keys('{"a1":"1","a2":"2","a3":"3"}') - ... .named_table_valued("keys", with_ordinality="n") - ... ) - - >>> print(select(fn)) - - .. seealso:: - - :meth:`_functions.FunctionElement.table_valued` - - :meth:`_functions.FunctionElement.named_table_valued` - - :meth:`_functions.FunctionElement.alias` - - - """ # noqa E501 - - new_func = self._generate() - new_func._table_valued = [ - coercions.expect(roles.StrAsPlainColumnRole, elem) for elem in expr - ] - with_ordinality = kw.pop("with_ordinality", None) - - if with_ordinality: - new_func._table_valued += ( - coercions.expect(roles.StrAsPlainColumnRole, with_ordinality), - ) - new_func._with_ordinality = True - - new_func._table_values_named = True - return new_func.alias() - - def column_valued(self, name=None): - """Return this :class:`_function.FunctionElement` as a column expression that - selects from itself as a FROM clause. - - E.g.:: - - >>> from sqlalchemy import select, func - >>> gs = func.generate_series(1, 5, -1).column_valued() - >>> print(select(gs)) - SELECT anon_1 - FROM generate_series(:generate_series_1, :generate_series_2, :generate_series_3) AS anon_1 - - This is shorthand for:: - - gs = func.generate_series(1, 5, -1).alias().column - - - .. seealso:: - - :meth:`_functions.FunctionElement.alias` - - """ # noqa 501 - - return self.alias(name=name).column - @property def columns(self): r"""The set of columns exported by this :class:`.FunctionElement`. @@ -318,15 +142,8 @@ class FunctionElement(Executable, ColumnElement, FromClause, Generative): """ - if self._table_valued: - cols = [ - ColumnClause(elem) if isinstance(elem, str) else elem - for elem in self._table_valued - ] - else: - cols = [self.label(None)] - - return ColumnCollection(columns=[(col.key, col) for col in cols]) + col = self.label(None) + return ColumnCollection(columns=[(col.key, col)]) @HasMemoized.memoized_attribute def clauses(self): @@ -488,65 +305,37 @@ class FunctionElement(Executable, ColumnElement, FromClause, Generative): return None - def alias(self, name=None): + def alias(self, name=None, flat=False): r"""Produce a :class:`_expression.Alias` construct against this :class:`.FunctionElement`. - .. note:: - - The :meth:`_functions.FunctionElement.alias` method is part of the - mechanism by which "table valued" SQL functions are created. - However, most use cases are covered by higher level methods on - :class:`_functions.FunctionElement` including - :meth:`_functions.FunctionElement.table_valued`, - :meth:`_functions.FunctionElement.named_table_valued`, and - :meth:`_functions.FunctionElement.column_valued`. - This construct wraps the function in a named alias which is suitable for the FROM clause, in the style accepted for example - by PostgreSQL. A column expression is also provided using the - special ``.column`` attribute, which may - be used to refer to the output of the function as a scalar value - in the columns or where clause, for a backend such as PostgreSQL. - - For a full table-valued expression, use the - :meth:`_function.FunctionElement.table_valued` method first to - establish named columns. + by PostgreSQL. e.g.:: - >>> from sqlalchemy import func, select, column - >>> data_view = func.unnest([1, 2, 3]).alias("data_view") - >>> print(select(data_view.column)) - SELECT data_view - FROM unnest(:unnest_1) AS data_view - - The :meth:`_functions.FunctionElement.column_valued` method provides - a shortcut for the above pattern:: - - >>> data_view = func.unnest([1, 2, 3]).column_valued("data_view") - >>> print(select(data_view)) - SELECT data_view - FROM unnest(:unnest_1) AS data_view - - .. versionadded:: 1.4.0b2 Added the ``.column`` accessor - - .. seealso:: + from sqlalchemy.sql import column - :meth:`_functions.FunctionElement.table_valued` + stmt = select(column('data_view')).\ + select_from(SomeTable).\ + select_from(func.unnest(SomeTable.data).alias('data_view') + ) - :meth:`_functions.FunctionElement.named_table_valued` + Would produce: - :meth:`_functions.FunctionElement.scalar_table_valued` + .. sourcecode:: sql - :meth:`_functions.FunctionElement.column_valued` + SELECT data_view + FROM sometable, 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 TableValuedAlias._construct( - self, name, named=self._table_values_named - ) + return Alias._construct(self, name) def select(self): """Produce a :func:`_expression.select` construct @@ -652,24 +441,6 @@ class FunctionAsBinary(BinaryExpression): self.sql_function.clauses.clauses[self.right_index - 1] = value -class ScalarFunctionColumn(NamedColumn): - __visit_name__ = "scalar_function_column" - - _traverse_internals = [ - ("name", InternalTraversal.dp_anon_name), - ("type", InternalTraversal.dp_type), - ("fn", InternalTraversal.dp_clauseelement), - ] - - is_literal = False - table = None - - def __init__(self, fn, name, type_=None): - self.fn = fn - self.name = name - self.type = sqltypes.to_instance(type_) - - class _FunctionGenerator(object): """Generate SQL function expressions. @@ -815,9 +586,10 @@ class Function(FunctionElement): func.mypackage.some_function(col1, col2) + .. seealso:: - :ref:`tutorial_functions` - in the :ref:`unified_tutorial` + :ref:`coretutorial_functions` :data:`.func` - namespace which produces registered or ad-hoc :class:`.Function` instances. |