summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql/selectable.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/sql/selectable.py')
-rw-r--r--lib/sqlalchemy/sql/selectable.py128
1 files changed, 117 insertions, 11 deletions
diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py
index b6cf7f55e..0040db6da 100644
--- a/lib/sqlalchemy/sql/selectable.py
+++ b/lib/sqlalchemy/sql/selectable.py
@@ -4224,6 +4224,14 @@ class SelectState(util.MemoizedSlots, CompileState):
cls._plugin_not_implemented()
@classmethod
+ def get_columns_clause_froms(cls, statement):
+ return cls._normalize_froms(
+ itertools.chain.from_iterable(
+ element._from_objects for element in statement._raw_columns
+ )
+ )
+
+ @classmethod
def _column_naming_convention(cls, label_style):
table_qualified = label_style is LABEL_STYLE_TABLENAME_PLUS_COL
@@ -4284,7 +4292,8 @@ class SelectState(util.MemoizedSlots, CompileState):
check_statement=statement,
)
- def _normalize_froms(self, iterable_of_froms, check_statement=None):
+ @classmethod
+ def _normalize_froms(cls, iterable_of_froms, check_statement=None):
"""given an iterable of things to select FROM, reduce them to what
would actually render in the FROM clause of a SELECT.
@@ -5347,13 +5356,79 @@ class Select(
"""
return self.join(target, onclause=onclause, isouter=True, full=full)
+ def get_final_froms(self):
+ """Compute the final displayed list of :class:`_expression.FromClause`
+ elements.
+
+ This method will run through the full computation required to
+ determine what FROM elements will be displayed in the resulting
+ SELECT statement, including shadowing individual tables with
+ JOIN objects, as well as full computation for ORM use cases including
+ eager loading clauses.
+
+ For ORM use, this accessor returns the **post compilation**
+ list of FROM objects; this collection will include elements such as
+ eagerly loaded tables and joins. The objects will **not** be
+ ORM enabled and not work as a replacement for the
+ :meth:`_sql.Select.select_froms` collection; additionally, the
+ method is not well performing for an ORM enabled statement as it
+ will incur the full ORM construction process.
+
+ To retrieve the FROM list that's implied by the "columns" collection
+ passed to the :class:`_sql.Select` originally, use the
+ :attr:`_sql.Select.columns_clause_froms` accessor.
+
+ To select from an alternative set of columns while maintaining the
+ FROM list, use the :meth:`_sql.Select.with_only_columns` method and
+ pass the
+ :paramref:`_sql.Select.with_only_columns.maintain_column_froms`
+ parameter.
+
+ .. versionadded:: 1.4.23 - the :meth:`_sql.Select.get_final_froms`
+ method replaces the previous :attr:`_sql.Select.froms` accessor,
+ which is deprecated.
+
+ .. seealso::
+
+ :attr:`_sql.Select.columns_clause_froms`
+
+ """
+ return self._compile_state_factory(self, None)._get_display_froms()
+
@property
+ @util.deprecated(
+ "1.4.23",
+ "The :attr:`_expression.Select.froms` attribute is moved to "
+ "the :meth:`_expression.Select.get_final_froms` method.",
+ )
def froms(self):
"""Return the displayed list of :class:`_expression.FromClause`
elements.
+
"""
- return self._compile_state_factory(self, None)._get_display_froms()
+ return self.get_final_froms()
+
+ @property
+ def columns_clause_froms(self):
+ """Return the set of :class:`_expression.FromClause` objects implied
+ by the columns clause of this SELECT statement.
+
+ .. versionadded:: 1.4.23
+
+ .. seealso::
+
+ :attr:`_sql.Select.froms` - "final" FROM list taking the full
+ statement into account
+
+ :meth:`_sql.Select.with_only_columns` - makes use of this
+ collection to set up a new FROM list
+
+ """
+
+ return SelectState.get_plugin_class(self).get_columns_clause_froms(
+ self
+ )
@property
def inner_columns(self):
@@ -5525,13 +5600,13 @@ class Select(
)
@_generative
- def with_only_columns(self, *columns):
+ def with_only_columns(self, *columns, **kw):
r"""Return a new :func:`_expression.select` construct with its columns
clause replaced with the given columns.
- This method is exactly equivalent to as if the original
+ By default, this method is exactly equivalent to as if the original
:func:`_expression.select` had been called with the given columns
- clause. I.e. a statement::
+ clause. E.g. a statement::
s = select(table1.c.a, table1.c.b)
s = s.with_only_columns(table1.c.b)
@@ -5540,13 +5615,30 @@ class Select(
s = select(table1.c.b)
- Note that this will also dynamically alter the FROM clause of the
- statement if it is not explicitly stated. To maintain the FROM
- clause, ensure the :meth:`_sql.Select.select_from` method is
- used appropriately::
+ In this mode of operation, :meth:`_sql.Select.with_only_columns`
+ will also dynamically alter the FROM clause of the
+ statement if it is not explicitly stated.
+ To maintain the existing set of FROMs including those implied by the
+ current columns clause, add the
+ :paramref:`_sql.Select.with_only_columns.maintain_column_froms`
+ parameter::
+
+ s = select(table1.c.a, table2.c.b)
+ s = s.with_only_columns(table1.c.a, maintain_column_froms=True)
+
+ The above parameter performs a transfer of the effective FROMs
+ in the columns collection to the :meth:`_sql.Select.select_from`
+ method, as though the following were invoked::
s = select(table1.c.a, table2.c.b)
- s = s.select_from(table2.c.b).with_only_columns(table1.c.a)
+ s = s.select_from(table1, table2).with_only_columns(table1.c.a)
+
+ The :paramref:`_sql.Select.with_only_columns.maintain_column_froms`
+ parameter makes use of the :attr:`_sql.Select.columns_clause_froms`
+ collection and performs an operation equivalent to the following::
+
+ s = select(table1.c.a, table2.c.b)
+ s = s.select_from(*s.columns_clause_froms).with_only_columns(table1.c.a)
:param \*columns: column expressions to be used.
@@ -5554,13 +5646,27 @@ class Select(
method accepts the list of column expressions positionally;
passing the expressions as a list is deprecated.
- """
+ :param maintain_column_froms: boolean parameter that will ensure the
+ FROM list implied from the current columns clause will be transferred
+ to the :meth:`_sql.Select.select_from` method first.
+
+ .. versionadded:: 1.4.23
+
+ """ # noqa E501
# memoizations should be cleared here as of
# I95c560ffcbfa30b26644999412fb6a385125f663 , asserting this
# is the case for now.
self._assert_no_memoizations()
+ maintain_column_froms = kw.pop("maintain_column_froms", False)
+ if kw:
+ raise TypeError("unknown parameters: %s" % (", ".join(kw),))
+
+ if maintain_column_froms:
+ self.select_from.non_generative(self, *self.columns_clause_froms)
+
+ # then memoize the FROMs etc.
_MemoizedSelectEntities._generate_for_statement(self)
self._raw_columns = [