diff options
Diffstat (limited to 'lib/sqlalchemy/sql/selectable.py')
| -rw-r--r-- | lib/sqlalchemy/sql/selectable.py | 132 |
1 files changed, 86 insertions, 46 deletions
diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py index 4eab60801..e39d61fdb 100644 --- a/lib/sqlalchemy/sql/selectable.py +++ b/lib/sqlalchemy/sql/selectable.py @@ -106,31 +106,33 @@ class ReturnsRows(roles.ReturnsRowsRole, ClauseElement): def selectable(self): raise NotImplementedError() + def _exported_columns_iterator(self): + """An iterator of column objects that represents the "exported" + columns of this :class:`.ReturnsRows`. -class Selectable(ReturnsRows): - """mark a class as being selectable. - - """ + This is the same set of columns as are returned by + :meth:`.ReturnsRows.exported_columns` except they are returned + as a simple iterator or sequence, rather than as a + :class:`.ColumnCollection` namespace. - __visit_name__ = "selectable" - - is_selectable = True + Subclasses should re-implement this method to bypass the interim + creation of the :class:`.ColumnCollection` if appropriate. - @property - def selectable(self): - return self + """ + return iter(self.exported_columns) @property def exported_columns(self): """A :class:`.ColumnCollection` that represents the "exported" - columns of this :class:`.Selectable`. + columns of this :class:`.ReturnsRows`. The "exported" columns represent the collection of :class:`.ColumnElement` expressions that are rendered by this SQL - construct. There are two primary varieties which are the + construct. There are primary varieties which are the "FROM clause columns" of a FROM clause, such as a table, join, - or subquery, and the "SELECTed columns", which are the columns in - the "columns clause" of a SELECT statement. + or subquery, the "SELECTed columns", which are the columns in + the "columns clause" of a SELECT statement, and the RETURNING + columns in a DML statement.. .. versionadded:: 1.4 @@ -143,6 +145,20 @@ class Selectable(ReturnsRows): raise NotImplementedError() + +class Selectable(ReturnsRows): + """mark a class as being selectable. + + """ + + __visit_name__ = "selectable" + + is_selectable = True + + @property + def selectable(self): + return self + def _refresh_for_new_column(self, column): raise NotImplementedError() @@ -312,7 +328,7 @@ class HasSuffixes(object): ) -class FromClause(HasMemoized, roles.AnonymizedFromClauseRole, Selectable): +class FromClause(roles.AnonymizedFromClauseRole, Selectable): """Represent an element that can be used within the ``FROM`` clause of a ``SELECT`` statement. @@ -350,8 +366,6 @@ class FromClause(HasMemoized, roles.AnonymizedFromClauseRole, Selectable): _use_schema_map = False - _memoized_property = util.group_expirable_memoized_property(["_columns"]) - @util.deprecated( "1.1", message="The :meth:`.FromClause.count` method is deprecated, " @@ -571,7 +585,7 @@ class FromClause(HasMemoized, roles.AnonymizedFromClauseRole, Selectable): """ return self.columns - @_memoized_property + @util.memoized_property def columns(self): """A named-based collection of :class:`.ColumnElement` objects maintained by this :class:`.FromClause`. @@ -589,7 +603,7 @@ class FromClause(HasMemoized, roles.AnonymizedFromClauseRole, Selectable): self._populate_column_collection() return self._columns.as_immutable() - @_memoized_property + @util.memoized_property def primary_key(self): """Return the collection of Column objects which comprise the primary key of this FromClause.""" @@ -598,7 +612,7 @@ class FromClause(HasMemoized, roles.AnonymizedFromClauseRole, Selectable): self._populate_column_collection() return self.primary_key - @_memoized_property + @util.memoized_property def foreign_keys(self): """Return the collection of ForeignKey objects which this FromClause references.""" @@ -607,6 +621,23 @@ class FromClause(HasMemoized, roles.AnonymizedFromClauseRole, Selectable): self._populate_column_collection() return self.foreign_keys + def _reset_column_collection(self): + """Reset the attributes linked to the FromClause.c attribute. + + This collection is separate from all the other memoized things + as it has shown to be sensitive to being cleared out in situations + where enclosing code, typically in a replacement traversal scenario, + has already established strong relationships + with the exported columns. + + The collection is cleared for the case where a table is having a + column added to it as well as within a Join during copy internals. + + """ + + for key in ["_columns", "columns", "primary_key", "foreign_keys"]: + self.__dict__.pop(key, None) + c = property( attrgetter("columns"), doc="An alias for the :attr:`.columns` attribute.", @@ -659,7 +690,7 @@ class FromClause(HasMemoized, roles.AnonymizedFromClauseRole, Selectable): derivations. """ - self._reset_exported() + self._reset_column_collection() class Join(FromClause): @@ -1239,7 +1270,7 @@ class AliasedReturnsRows(NoInit, FromClause): # same object. don't reset exported .c. collections and other # memoized details if nothing changed if element is not self.element: - self._reset_exported() + self._reset_column_collection() self.element = element @property @@ -2141,7 +2172,6 @@ class SelectBase( roles.DMLSelectRole, roles.CompoundElementRole, roles.InElementRole, - HasMemoized, HasCTE, Executable, SupportsCloneAnnotations, @@ -2158,8 +2188,6 @@ class SelectBase( _is_select_statement = True - _memoized_property = util.group_expirable_memoized_property() - def _generate_fromclause_column_proxies(self, fromclause): # type: (FromClause) -> None raise NotImplementedError() @@ -2254,7 +2282,7 @@ class SelectBase( def outerjoin(self, *arg, **kw): return self._implicit_subquery.outerjoin(*arg, **kw) - @_memoized_property + @HasMemoized.memoized_attribute def _implicit_subquery(self): return self.subquery() @@ -2315,15 +2343,6 @@ class SelectBase( """ return Lateral._factory(self, name) - def _generate(self): - """Override the default _generate() method to also clear out - exported collections.""" - - s = self.__class__.__new__(self.__class__) - s.__dict__ = self.__dict__.copy() - s._reset_memoizations() - return s - @property def _from_objects(self): return [self] @@ -2431,6 +2450,9 @@ class SelectStatementGrouping(GroupedElement, SelectBase): def _generate_proxy_for_new_column(self, column, subquery): return self.element._generate_proxy_for_new_column(subquery) + def _exported_columns_iterator(self): + return self.element._exported_columns_iterator() + @property def selected_columns(self): """A :class:`.ColumnCollection` representing the columns that @@ -3046,6 +3068,9 @@ class CompoundSelect(HasCompileState, GenerativeSelect): for select in self.selects: select._refresh_for_new_column(column) + def _exported_columns_iterator(self): + return self.selects[0]._exported_columns_iterator() + @property def selected_columns(self): """A :class:`.ColumnCollection` representing the columns that @@ -3339,8 +3364,6 @@ class Select( _from_obj = () _auto_correlate = True - _memoized_property = SelectBase._memoized_property - _traverse_internals = ( [ ("_from_obj", InternalTraversal.dp_clauseelement_list), @@ -3400,8 +3423,7 @@ class Select( self = cls.__new__(cls) self._raw_columns = [ - coercions.expect(roles.ColumnsClauseRole, ent) - for ent in util.to_list(entities) + coercions.expect(roles.ColumnsClauseRole, ent) for ent in entities ] GenerativeSelect.__init__(self) @@ -3739,8 +3761,12 @@ class Select( """an iterator of all ColumnElement expressions which would be rendered into the columns clause of the resulting SELECT statement. + This method is legacy as of 1.4 and is superseded by the + :attr:`.Select.exported_columns` collection. + """ - return _select_iterables(self._raw_columns) + + return self._exported_columns_iterator() def is_derived_from(self, fromclause): if self in fromclause._cloned_set: @@ -3786,7 +3812,10 @@ class Select( clone=clone, omit_attrs=("_from_obj",), **kw ) - self._reset_memoizations() + # memoizations should be cleared here as of + # I95c560ffcbfa30b26644999412fb6a385125f663 , asserting this + # is the case for now. + self._assert_no_memoizations() def get_children(self, **kwargs): return list(set(self._iterate_from_elements())) + super( @@ -3809,7 +3838,10 @@ class Select( :class:`.Select` object. """ - self._reset_memoizations() + # memoizations should be cleared here as of + # I95c560ffcbfa30b26644999412fb6a385125f663 , asserting this + # is the case for now. + self._assert_no_memoizations() self._raw_columns = self._raw_columns + [ coercions.expect(roles.ColumnsClauseRole, column,) @@ -3861,7 +3893,7 @@ class Select( """ return self.with_only_columns( util.preloaded.sql_util.reduce_columns( - self.inner_columns, + self._exported_columns_iterator(), only_synonyms=only_synonyms, *(self._where_criteria + self._from_obj) ) @@ -3935,7 +3967,12 @@ class Select( being asked to select both from ``table1`` as well as itself. """ - self._reset_memoizations() + + # memoizations should be cleared here as of + # I95c560ffcbfa30b26644999412fb6a385125f663 , asserting this + # is the case for now. + self._assert_no_memoizations() + rc = [] for c in columns: c = coercions.expect(roles.ColumnsClauseRole, c,) @@ -4112,7 +4149,7 @@ class Select( coercions.expect(roles.FromClauseRole, f) for f in fromclauses ) - @_memoized_property + @HasMemoized.memoized_attribute def selected_columns(self): """A :class:`.ColumnCollection` representing the columns that this SELECT statement or similar construct returns in its result set. @@ -4167,6 +4204,9 @@ class Select( return ColumnCollection(collection).as_immutable() + def _exported_columns_iterator(self): + return _select_iterables(self._raw_columns) + def _ensure_disambiguated_names(self): if self._label_style is LABEL_STYLE_NONE: self = self._set_label_style(LABEL_STYLE_DISAMBIGUATE_ONLY) @@ -4558,7 +4598,7 @@ class TextualSelect(SelectBase): ] self.positional = positional - @SelectBase._memoized_property + @HasMemoized.memoized_attribute def selected_columns(self): """A :class:`.ColumnCollection` representing the columns that this SELECT statement or similar construct returns in its result set. |
