diff options
Diffstat (limited to 'lib/sqlalchemy/sql/selectable.py')
-rw-r--r-- | lib/sqlalchemy/sql/selectable.py | 812 |
1 files changed, 500 insertions, 312 deletions
diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py index f64f152c4..1f1800514 100644 --- a/lib/sqlalchemy/sql/selectable.py +++ b/lib/sqlalchemy/sql/selectable.py @@ -10,15 +10,39 @@ SQL tables and derived rowsets. """ -from .elements import ClauseElement, TextClause, ClauseList, \ - and_, Grouping, UnaryExpression, literal_column, BindParameter -from .elements import _clone, \ - _literal_as_text, _interpret_as_column_or_from, _expand_cloned,\ - _select_iterables, _anonymous_label, _clause_element_as_expr,\ - _cloned_intersection, _cloned_difference, True_, \ - _literal_as_label_reference, _literal_and_labels_as_label_reference -from .base import Immutable, Executable, _generative, \ - ColumnCollection, ColumnSet, _from_objects, Generative +from .elements import ( + ClauseElement, + TextClause, + ClauseList, + and_, + Grouping, + UnaryExpression, + literal_column, + BindParameter, +) +from .elements import ( + _clone, + _literal_as_text, + _interpret_as_column_or_from, + _expand_cloned, + _select_iterables, + _anonymous_label, + _clause_element_as_expr, + _cloned_intersection, + _cloned_difference, + True_, + _literal_as_label_reference, + _literal_and_labels_as_label_reference, +) +from .base import ( + Immutable, + Executable, + _generative, + ColumnCollection, + ColumnSet, + _from_objects, + Generative, +) from . import type_api from .. import inspection from .. import util @@ -40,7 +64,8 @@ def _interpret_as_from(element): "Textual SQL FROM expression %(expr)r should be " "explicitly declared as text(%(expr)r), " "or use table(%(expr)r) for more specificity", - {"expr": util.ellipses_string(element)}) + {"expr": util.ellipses_string(element)}, + ) return TextClause(util.text_type(element)) try: @@ -73,7 +98,7 @@ def _offset_or_limit_clause(element, name=None, type_=None): """ if element is None: return None - elif hasattr(element, '__clause_element__'): + elif hasattr(element, "__clause_element__"): return element.__clause_element__() elif isinstance(element, Visitable): return element @@ -97,7 +122,8 @@ def _offset_or_limit_clause_asint(clause, attrname): except AttributeError: raise exc.CompileError( "This SELECT structure does not use a simple " - "integer value for %s" % attrname) + "integer value for %s" % attrname + ) else: return util.asint(value) @@ -225,12 +251,14 @@ def tablesample(selectable, sampling, name=None, seed=None): """ return _interpret_as_from(selectable).tablesample( - sampling, name=name, seed=seed) + sampling, name=name, seed=seed + ) class Selectable(ClauseElement): """mark a class as being selectable""" - __visit_name__ = 'selectable' + + __visit_name__ = "selectable" is_selectable = True @@ -265,15 +293,17 @@ class HasPrefixes(object): limit rendering of this prefix to only that dialect. """ - dialect = kw.pop('dialect', None) + dialect = kw.pop("dialect", None) if kw: - raise exc.ArgumentError("Unsupported argument(s): %s" % - ",".join(kw)) + raise exc.ArgumentError( + "Unsupported argument(s): %s" % ",".join(kw) + ) self._setup_prefixes(expr, dialect) def _setup_prefixes(self, prefixes, dialect=None): self._prefixes = self._prefixes + tuple( - [(_literal_as_text(p, warn=False), dialect) for p in prefixes]) + [(_literal_as_text(p, warn=False), dialect) for p in prefixes] + ) class HasSuffixes(object): @@ -301,15 +331,17 @@ class HasSuffixes(object): limit rendering of this suffix to only that dialect. """ - dialect = kw.pop('dialect', None) + dialect = kw.pop("dialect", None) if kw: - raise exc.ArgumentError("Unsupported argument(s): %s" % - ",".join(kw)) + raise exc.ArgumentError( + "Unsupported argument(s): %s" % ",".join(kw) + ) self._setup_suffixes(expr, dialect) def _setup_suffixes(self, suffixes, dialect=None): self._suffixes = self._suffixes + tuple( - [(_literal_as_text(p, warn=False), dialect) for p in suffixes]) + [(_literal_as_text(p, warn=False), dialect) for p in suffixes] + ) class FromClause(Selectable): @@ -330,7 +362,8 @@ class FromClause(Selectable): """ - __visit_name__ = 'fromclause' + + __visit_name__ = "fromclause" named_with_column = False _hide_froms = [] @@ -359,13 +392,14 @@ class FromClause(Selectable): _memoized_property = util.group_expirable_memoized_property(["_columns"]) @util.deprecated( - '1.1', + "1.1", message="``FromClause.count()`` is deprecated. Counting " "rows requires that the correct column expression and " "accommodations for joins, DISTINCT, etc. must be made, " "otherwise results may not be what's expected. " "Please use an appropriate ``func.count()`` expression " - "directly.") + "directly.", + ) @util.dependencies("sqlalchemy.sql.functions") def count(self, functions, whereclause=None, **params): """return a SELECT COUNT generated against this @@ -392,10 +426,11 @@ class FromClause(Selectable): else: col = list(self.columns)[0] return Select( - [functions.func.count(col).label('tbl_row_count')], + [functions.func.count(col).label("tbl_row_count")], whereclause, from_obj=[self], - **params) + **params + ) def select(self, whereclause=None, **params): """return a SELECT of this :class:`.FromClause`. @@ -603,8 +638,9 @@ class FromClause(Selectable): def embedded(expanded_proxy_set, target_set): for t in target_set.difference(expanded_proxy_set): - if not set(_expand_cloned([t]) - ).intersection(expanded_proxy_set): + if not set(_expand_cloned([t])).intersection( + expanded_proxy_set + ): return False return True @@ -617,8 +653,10 @@ class FromClause(Selectable): for c in cols: expanded_proxy_set = set(_expand_cloned(c.proxy_set)) i = target_set.intersection(expanded_proxy_set) - if i and (not require_embedded - or embedded(expanded_proxy_set, target_set)): + if i and ( + not require_embedded + or embedded(expanded_proxy_set, target_set) + ): if col is None: # no corresponding column yet, pick this one. @@ -646,12 +684,20 @@ class FromClause(Selectable): col_distance = util.reduce( operator.add, - [sc._annotations.get('weight', 1) for sc in - col.proxy_set if sc.shares_lineage(column)]) + [ + sc._annotations.get("weight", 1) + for sc in col.proxy_set + if sc.shares_lineage(column) + ], + ) c_distance = util.reduce( operator.add, - [sc._annotations.get('weight', 1) for sc in - c.proxy_set if sc.shares_lineage(column)]) + [ + sc._annotations.get("weight", 1) + for sc in c.proxy_set + if sc.shares_lineage(column) + ], + ) if c_distance < col_distance: col, intersect = c, i return col @@ -663,7 +709,7 @@ class FromClause(Selectable): Used primarily for error message formatting. """ - return getattr(self, 'name', self.__class__.__name__ + " object") + return getattr(self, "name", self.__class__.__name__ + " object") def _reset_exported(self): """delete memoized collections when a FromClause is cloned.""" @@ -683,7 +729,7 @@ class FromClause(Selectable): """ - if '_columns' not in self.__dict__: + if "_columns" not in self.__dict__: self._init_collections() self._populate_column_collection() return self._columns.as_immutable() @@ -706,14 +752,16 @@ class FromClause(Selectable): self._populate_column_collection() return self.foreign_keys - c = property(attrgetter('columns'), - doc="An alias for the :attr:`.columns` attribute.") - _select_iterable = property(attrgetter('columns')) + c = property( + attrgetter("columns"), + doc="An alias for the :attr:`.columns` attribute.", + ) + _select_iterable = property(attrgetter("columns")) def _init_collections(self): - assert '_columns' not in self.__dict__ - assert 'primary_key' not in self.__dict__ - assert 'foreign_keys' not in self.__dict__ + assert "_columns" not in self.__dict__ + assert "primary_key" not in self.__dict__ + assert "foreign_keys" not in self.__dict__ self._columns = ColumnCollection() self.primary_key = ColumnSet() @@ -721,7 +769,7 @@ class FromClause(Selectable): @property def _cols_populated(self): - return '_columns' in self.__dict__ + return "_columns" in self.__dict__ def _populate_column_collection(self): """Called on subclasses to establish the .c collection. @@ -758,8 +806,7 @@ class FromClause(Selectable): """ if not self._cols_populated: return None - elif (column.key in self.columns and - self.columns[column.key] is column): + elif column.key in self.columns and self.columns[column.key] is column: return column else: return None @@ -780,7 +827,8 @@ class Join(FromClause): :meth:`.FromClause.join` """ - __visit_name__ = 'join' + + __visit_name__ = "join" _is_join = True @@ -829,8 +877,9 @@ class Join(FromClause): return cls(left, right, onclause, isouter=True, full=full) @classmethod - def _create_join(cls, left, right, onclause=None, isouter=False, - full=False): + def _create_join( + cls, left, right, onclause=None, isouter=False, full=False + ): """Produce a :class:`.Join` object, given two :class:`.FromClause` expressions. @@ -882,26 +931,34 @@ class Join(FromClause): self.left.description, id(self.left), self.right.description, - id(self.right)) + id(self.right), + ) def is_derived_from(self, fromclause): - return fromclause is self or \ - self.left.is_derived_from(fromclause) or \ - self.right.is_derived_from(fromclause) + return ( + fromclause is self + or self.left.is_derived_from(fromclause) + or self.right.is_derived_from(fromclause) + ) def self_group(self, against=None): return FromGrouping(self) @util.dependencies("sqlalchemy.sql.util") def _populate_column_collection(self, sqlutil): - columns = [c for c in self.left.columns] + \ - [c for c in self.right.columns] + columns = [c for c in self.left.columns] + [ + c for c in self.right.columns + ] - self.primary_key.extend(sqlutil.reduce_columns( - (c for c in columns if c.primary_key), self.onclause)) + self.primary_key.extend( + sqlutil.reduce_columns( + (c for c in columns if c.primary_key), self.onclause + ) + ) self._columns.update((col._label, col) for col in columns) - self.foreign_keys.update(itertools.chain( - *[col.foreign_keys for col in columns])) + self.foreign_keys.update( + itertools.chain(*[col.foreign_keys for col in columns]) + ) def _refresh_for_new_column(self, column): col = self.left._refresh_for_new_column(column) @@ -933,9 +990,14 @@ class Join(FromClause): return self._join_condition(left, right, a_subset=left_right) @classmethod - def _join_condition(cls, a, b, ignore_nonexistent_tables=False, - a_subset=None, - consider_as_foreign_keys=None): + def _join_condition( + cls, + a, + b, + ignore_nonexistent_tables=False, + a_subset=None, + consider_as_foreign_keys=None, + ): """create a join condition between two tables or selectables. e.g.:: @@ -963,26 +1025,31 @@ class Join(FromClause): """ constraints = cls._joincond_scan_left_right( - a, a_subset, b, consider_as_foreign_keys) + a, a_subset, b, consider_as_foreign_keys + ) if len(constraints) > 1: cls._joincond_trim_constraints( - a, b, constraints, consider_as_foreign_keys) + a, b, constraints, consider_as_foreign_keys + ) if len(constraints) == 0: if isinstance(b, FromGrouping): - hint = " Perhaps you meant to convert the right side to a "\ + hint = ( + " Perhaps you meant to convert the right side to a " "subquery using alias()?" + ) else: hint = "" raise exc.NoForeignKeysError( "Can't find any foreign key relationships " - "between '%s' and '%s'.%s" % - (a.description, b.description, hint)) + "between '%s' and '%s'.%s" + % (a.description, b.description, hint) + ) crit = [(x == y) for x, y in list(constraints.values())[0]] if len(crit) == 1: - return (crit[0]) + return crit[0] else: return and_(*crit) @@ -994,24 +1061,30 @@ class Join(FromClause): left_right = None constraints = cls._joincond_scan_left_right( - a=left, b=right, a_subset=left_right, - consider_as_foreign_keys=consider_as_foreign_keys) + a=left, + b=right, + a_subset=left_right, + consider_as_foreign_keys=consider_as_foreign_keys, + ) return bool(constraints) @classmethod def _joincond_scan_left_right( - cls, a, a_subset, b, consider_as_foreign_keys): + cls, a, a_subset, b, consider_as_foreign_keys + ): constraints = collections.defaultdict(list) for left in (a_subset, a): if left is None: continue for fk in sorted( - b.foreign_keys, - key=lambda fk: fk.parent._creation_order): - if consider_as_foreign_keys is not None and \ - fk.parent not in consider_as_foreign_keys: + b.foreign_keys, key=lambda fk: fk.parent._creation_order + ): + if ( + consider_as_foreign_keys is not None + and fk.parent not in consider_as_foreign_keys + ): continue try: col = fk.get_referent(left) @@ -1025,10 +1098,12 @@ class Join(FromClause): constraints[fk.constraint].append((col, fk.parent)) if left is not b: for fk in sorted( - left.foreign_keys, - key=lambda fk: fk.parent._creation_order): - if consider_as_foreign_keys is not None and \ - fk.parent not in consider_as_foreign_keys: + left.foreign_keys, key=lambda fk: fk.parent._creation_order + ): + if ( + consider_as_foreign_keys is not None + and fk.parent not in consider_as_foreign_keys + ): continue try: col = fk.get_referent(b) @@ -1046,14 +1121,16 @@ class Join(FromClause): @classmethod def _joincond_trim_constraints( - cls, a, b, constraints, consider_as_foreign_keys): + cls, a, b, constraints, consider_as_foreign_keys + ): # more than one constraint matched. narrow down the list # to include just those FKCs that match exactly to # "consider_as_foreign_keys". if consider_as_foreign_keys: for const in list(constraints): if set(f.parent for f in const.elements) != set( - consider_as_foreign_keys): + consider_as_foreign_keys + ): del constraints[const] # if still multiple constraints, but @@ -1070,8 +1147,8 @@ class Join(FromClause): "tables have more than one foreign key " "constraint relationship between them. " "Please specify the 'onclause' of this " - "join explicitly." % (a.description, b.description)) - + "join explicitly." % (a.description, b.description) + ) def select(self, whereclause=None, **kwargs): r"""Create a :class:`.Select` from this :class:`.Join`. @@ -1200,27 +1277,37 @@ class Join(FromClause): """ if flat: assert name is None, "Can't send name argument with flat" - left_a, right_a = self.left.alias(flat=True), \ - self.right.alias(flat=True) - adapter = sqlutil.ClauseAdapter(left_a).\ - chain(sqlutil.ClauseAdapter(right_a)) + left_a, right_a = ( + self.left.alias(flat=True), + self.right.alias(flat=True), + ) + adapter = sqlutil.ClauseAdapter(left_a).chain( + sqlutil.ClauseAdapter(right_a) + ) - return left_a.join(right_a, adapter.traverse(self.onclause), - isouter=self.isouter, full=self.full) + return left_a.join( + right_a, + adapter.traverse(self.onclause), + isouter=self.isouter, + full=self.full, + ) else: return self.select(use_labels=True, correlate=False).alias(name) @property def _hide_froms(self): - return itertools.chain(*[_from_objects(x.left, x.right) - for x in self._cloned_set]) + return itertools.chain( + *[_from_objects(x.left, x.right) for x in self._cloned_set] + ) @property def _from_objects(self): - return [self] + \ - self.onclause._from_objects + \ - self.left._from_objects + \ - self.right._from_objects + return ( + [self] + + self.onclause._from_objects + + self.left._from_objects + + self.right._from_objects + ) class Alias(FromClause): @@ -1236,7 +1323,7 @@ class Alias(FromClause): """ - __visit_name__ = 'alias' + __visit_name__ = "alias" named_with_column = True _is_from_container = True @@ -1252,15 +1339,16 @@ class Alias(FromClause): self.element = selectable if name is None: if self.original.named_with_column: - name = getattr(self.original, 'name', None) - name = _anonymous_label('%%(%d %s)s' % (id(self), name - or 'anon')) + name = getattr(self.original, "name", None) + name = _anonymous_label("%%(%d %s)s" % (id(self), name or "anon")) self.name = name def self_group(self, against=None): - if isinstance(against, CompoundSelect) and \ - isinstance(self.original, Select) and \ - self.original._needs_parens_for_grouping(): + if ( + isinstance(against, CompoundSelect) + and isinstance(self.original, Select) + and self.original._needs_parens_for_grouping() + ): return FromGrouping(self) return super(Alias, self).self_group(against=against) @@ -1270,14 +1358,15 @@ class Alias(FromClause): if util.py3k: return self.name else: - return self.name.encode('ascii', 'backslashreplace') + return self.name.encode("ascii", "backslashreplace") def as_scalar(self): try: return self.element.as_scalar() except AttributeError: - raise AttributeError("Element %s does not support " - "'as_scalar()'" % self.element) + raise AttributeError( + "Element %s does not support " "'as_scalar()'" % self.element + ) def is_derived_from(self, fromclause): if fromclause in self._cloned_set: @@ -1344,7 +1433,7 @@ class Lateral(Alias): """ - __visit_name__ = 'lateral' + __visit_name__ = "lateral" _is_lateral = True @@ -1363,11 +1452,9 @@ class TableSample(Alias): """ - __visit_name__ = 'tablesample' + __visit_name__ = "tablesample" - def __init__(self, selectable, sampling, - name=None, - seed=None): + def __init__(self, selectable, sampling, name=None, seed=None): self.sampling = sampling self.seed = seed super(TableSample, self).__init__(selectable, name=name) @@ -1390,14 +1477,18 @@ class CTE(Generative, HasSuffixes, Alias): .. versionadded:: 0.7.6 """ - __visit_name__ = 'cte' - - def __init__(self, selectable, - name=None, - recursive=False, - _cte_alias=None, - _restates=frozenset(), - _suffixes=None): + + __visit_name__ = "cte" + + def __init__( + self, + selectable, + name=None, + recursive=False, + _cte_alias=None, + _restates=frozenset(), + _suffixes=None, + ): self.recursive = recursive self._cte_alias = _cte_alias self._restates = _restates @@ -1409,9 +1500,9 @@ class CTE(Generative, HasSuffixes, Alias): super(CTE, self)._copy_internals(clone, **kw) if self._cte_alias is not None: self._cte_alias = clone(self._cte_alias, **kw) - self._restates = frozenset([ - clone(elem, **kw) for elem in self._restates - ]) + self._restates = frozenset( + [clone(elem, **kw) for elem in self._restates] + ) @util.dependencies("sqlalchemy.sql.dml") def _populate_column_collection(self, dml): @@ -1428,7 +1519,7 @@ class CTE(Generative, HasSuffixes, Alias): name=name, recursive=self.recursive, _cte_alias=self, - _suffixes=self._suffixes + _suffixes=self._suffixes, ) def union(self, other): @@ -1437,7 +1528,7 @@ class CTE(Generative, HasSuffixes, Alias): name=self.name, recursive=self.recursive, _restates=self._restates.union([self]), - _suffixes=self._suffixes + _suffixes=self._suffixes, ) def union_all(self, other): @@ -1446,7 +1537,7 @@ class CTE(Generative, HasSuffixes, Alias): name=self.name, recursive=self.recursive, _restates=self._restates.union([self]), - _suffixes=self._suffixes + _suffixes=self._suffixes, ) @@ -1620,7 +1711,8 @@ class HasCTE(object): class FromGrouping(FromClause): """Represent a grouping of a FROM clause""" - __visit_name__ = 'grouping' + + __visit_name__ = "grouping" def __init__(self, element): self.element = element @@ -1651,7 +1743,7 @@ class FromGrouping(FromClause): return self.element._hide_froms def get_children(self, **kwargs): - return self.element, + return (self.element,) def _copy_internals(self, clone=_clone, **kw): self.element = clone(self.element, **kw) @@ -1664,10 +1756,10 @@ class FromGrouping(FromClause): return getattr(self.element, attr) def __getstate__(self): - return {'element': self.element} + return {"element": self.element} def __setstate__(self, state): - self.element = state['element'] + self.element = state["element"] class TableClause(Immutable, FromClause): @@ -1699,7 +1791,7 @@ class TableClause(Immutable, FromClause): """ - __visit_name__ = 'table' + __visit_name__ = "table" named_with_column = True @@ -1744,7 +1836,7 @@ class TableClause(Immutable, FromClause): if util.py3k: return self.name else: - return self.name.encode('ascii', 'backslashreplace') + return self.name.encode("ascii", "backslashreplace") def append_column(self, c): self._columns[c.key] = c @@ -1773,7 +1865,8 @@ class TableClause(Immutable, FromClause): @util.dependencies("sqlalchemy.sql.dml") def update( - self, dml, whereclause=None, values=None, inline=False, **kwargs): + self, dml, whereclause=None, values=None, inline=False, **kwargs + ): """Generate an :func:`.update` construct against this :class:`.TableClause`. @@ -1785,8 +1878,13 @@ class TableClause(Immutable, FromClause): """ - return dml.Update(self, whereclause=whereclause, - values=values, inline=inline, **kwargs) + return dml.Update( + self, + whereclause=whereclause, + values=values, + inline=inline, + **kwargs + ) @util.dependencies("sqlalchemy.sql.dml") def delete(self, dml, whereclause=None, **kwargs): @@ -1809,7 +1907,6 @@ class TableClause(Immutable, FromClause): class ForUpdateArg(ClauseElement): - @classmethod def parse_legacy_select(self, arg): """Parse the for_update argument of :func:`.select`. @@ -1836,11 +1933,11 @@ class ForUpdateArg(ClauseElement): return None nowait = read = False - if arg == 'nowait': + if arg == "nowait": nowait = True - elif arg == 'read': + elif arg == "read": read = True - elif arg == 'read_nowait': + elif arg == "read_nowait": read = nowait = True elif arg is not True: raise exc.ArgumentError("Unknown for_update argument: %r" % arg) @@ -1860,12 +1957,12 @@ class ForUpdateArg(ClauseElement): def __eq__(self, other): return ( - isinstance(other, ForUpdateArg) and - other.nowait == self.nowait and - other.read == self.read and - other.skip_locked == self.skip_locked and - other.key_share == self.key_share and - other.of is self.of + isinstance(other, ForUpdateArg) + and other.nowait == self.nowait + and other.read == self.read + and other.skip_locked == self.skip_locked + and other.key_share == self.key_share + and other.of is self.of ) def __hash__(self): @@ -1876,8 +1973,13 @@ class ForUpdateArg(ClauseElement): self.of = [clone(col, **kw) for col in self.of] def __init__( - self, nowait=False, read=False, of=None, - skip_locked=False, key_share=False): + self, + nowait=False, + read=False, + of=None, + skip_locked=False, + key_share=False, + ): """Represents arguments specified to :meth:`.Select.for_update`. .. versionadded:: 0.9.0 @@ -1889,8 +1991,9 @@ class ForUpdateArg(ClauseElement): self.skip_locked = skip_locked self.key_share = key_share if of is not None: - self.of = [_interpret_as_column_or_from(elem) - for elem in util.to_list(of)] + self.of = [ + _interpret_as_column_or_from(elem) for elem in util.to_list(of) + ] else: self.of = None @@ -1930,17 +2033,20 @@ class SelectBase(HasCTE, Executable, FromClause): return self.as_scalar().label(name) @_generative - @util.deprecated('0.6', - message="``autocommit()`` is deprecated. Use " - ":meth:`.Executable.execution_options` with the " - "'autocommit' flag.") + @util.deprecated( + "0.6", + message="``autocommit()`` is deprecated. Use " + ":meth:`.Executable.execution_options` with the " + "'autocommit' flag.", + ) def autocommit(self): """return a new selectable with the 'autocommit' flag set to True. """ - self._execution_options = \ - self._execution_options.union({'autocommit': True}) + self._execution_options = self._execution_options.union( + {"autocommit": True} + ) def _generate(self): """Override the default _generate() method to also clear out @@ -1973,34 +2079,38 @@ class GenerativeSelect(SelectBase): used for other SELECT-like objects, e.g. :class:`.TextAsFrom`. """ + _order_by_clause = ClauseList() _group_by_clause = ClauseList() _limit_clause = None _offset_clause = None _for_update_arg = None - def __init__(self, - use_labels=False, - for_update=False, - limit=None, - offset=None, - order_by=None, - group_by=None, - bind=None, - autocommit=None): + def __init__( + self, + use_labels=False, + for_update=False, + limit=None, + offset=None, + order_by=None, + group_by=None, + bind=None, + autocommit=None, + ): self.use_labels = use_labels if for_update is not False: - self._for_update_arg = (ForUpdateArg. - parse_legacy_select(for_update)) + self._for_update_arg = ForUpdateArg.parse_legacy_select(for_update) if autocommit is not None: - util.warn_deprecated('autocommit on select() is ' - 'deprecated. Use .execution_options(a' - 'utocommit=True)') - self._execution_options = \ - self._execution_options.union( - {'autocommit': autocommit}) + util.warn_deprecated( + "autocommit on select() is " + "deprecated. Use .execution_options(a" + "utocommit=True)" + ) + self._execution_options = self._execution_options.union( + {"autocommit": autocommit} + ) if limit is not None: self._limit_clause = _offset_or_limit_clause(limit) if offset is not None: @@ -2010,11 +2120,13 @@ class GenerativeSelect(SelectBase): if order_by is not None: self._order_by_clause = ClauseList( *util.to_list(order_by), - _literal_as_text=_literal_and_labels_as_label_reference) + _literal_as_text=_literal_and_labels_as_label_reference + ) if group_by is not None: self._group_by_clause = ClauseList( *util.to_list(group_by), - _literal_as_text=_literal_as_label_reference) + _literal_as_text=_literal_as_label_reference + ) @property def for_update(self): @@ -2030,8 +2142,14 @@ class GenerativeSelect(SelectBase): self._for_update_arg = ForUpdateArg.parse_legacy_select(value) @_generative - def with_for_update(self, nowait=False, read=False, of=None, - skip_locked=False, key_share=False): + def with_for_update( + self, + nowait=False, + read=False, + of=None, + skip_locked=False, + key_share=False, + ): """Specify a ``FOR UPDATE`` clause for this :class:`.GenerativeSelect`. E.g.:: @@ -2079,9 +2197,13 @@ class GenerativeSelect(SelectBase): .. versionadded:: 1.1.0 """ - self._for_update_arg = ForUpdateArg(nowait=nowait, read=read, of=of, - skip_locked=skip_locked, - key_share=key_share) + self._for_update_arg = ForUpdateArg( + nowait=nowait, + read=read, + of=of, + skip_locked=skip_locked, + key_share=key_share, + ) @_generative def apply_labels(self): @@ -2209,11 +2331,12 @@ class GenerativeSelect(SelectBase): if len(clauses) == 1 and clauses[0] is None: self._order_by_clause = ClauseList() else: - if getattr(self, '_order_by_clause', None) is not None: + if getattr(self, "_order_by_clause", None) is not None: clauses = list(self._order_by_clause) + list(clauses) self._order_by_clause = ClauseList( *clauses, - _literal_as_text=_literal_and_labels_as_label_reference) + _literal_as_text=_literal_and_labels_as_label_reference + ) def append_group_by(self, *clauses): """Append the given GROUP BY criterion applied to this selectable. @@ -2228,10 +2351,11 @@ class GenerativeSelect(SelectBase): if len(clauses) == 1 and clauses[0] is None: self._group_by_clause = ClauseList() else: - if getattr(self, '_group_by_clause', None) is not None: + if getattr(self, "_group_by_clause", None) is not None: clauses = list(self._group_by_clause) + list(clauses) self._group_by_clause = ClauseList( - *clauses, _literal_as_text=_literal_as_label_reference) + *clauses, _literal_as_text=_literal_as_label_reference + ) @property def _label_resolve_dict(self): @@ -2265,19 +2389,19 @@ class CompoundSelect(GenerativeSelect): """ - __visit_name__ = 'compound_select' + __visit_name__ = "compound_select" - UNION = util.symbol('UNION') - UNION_ALL = util.symbol('UNION ALL') - EXCEPT = util.symbol('EXCEPT') - EXCEPT_ALL = util.symbol('EXCEPT ALL') - INTERSECT = util.symbol('INTERSECT') - INTERSECT_ALL = util.symbol('INTERSECT ALL') + UNION = util.symbol("UNION") + UNION_ALL = util.symbol("UNION ALL") + EXCEPT = util.symbol("EXCEPT") + EXCEPT_ALL = util.symbol("EXCEPT ALL") + INTERSECT = util.symbol("INTERSECT") + INTERSECT_ALL = util.symbol("INTERSECT ALL") _is_from_container = True def __init__(self, keyword, *selects, **kwargs): - self._auto_correlate = kwargs.pop('correlate', False) + self._auto_correlate = kwargs.pop("correlate", False) self.keyword = keyword self.selects = [] @@ -2291,12 +2415,16 @@ class CompoundSelect(GenerativeSelect): numcols = len(s.c._all_columns) elif len(s.c._all_columns) != numcols: raise exc.ArgumentError( - 'All selectables passed to ' - 'CompoundSelect must have identical numbers of ' - 'columns; select #%d has %d columns, select ' - '#%d has %d' % - (1, len(self.selects[0].c._all_columns), - n + 1, len(s.c._all_columns)) + "All selectables passed to " + "CompoundSelect must have identical numbers of " + "columns; select #%d has %d columns, select " + "#%d has %d" + % ( + 1, + len(self.selects[0].c._all_columns), + n + 1, + len(s.c._all_columns), + ) ) self.selects.append(s.self_group(against=self)) @@ -2305,9 +2433,7 @@ class CompoundSelect(GenerativeSelect): @property def _label_resolve_dict(self): - d = dict( - (c.key, c) for c in self.c - ) + d = dict((c.key, c) for c in self.c) return d, d, d @classmethod @@ -2416,8 +2542,7 @@ class CompoundSelect(GenerativeSelect): :func:`select`. """ - return CompoundSelect( - CompoundSelect.INTERSECT_ALL, *selects, **kwargs) + return CompoundSelect(CompoundSelect.INTERSECT_ALL, *selects, **kwargs) def _scalar_type(self): return self.selects[0]._scalar_type() @@ -2445,8 +2570,10 @@ class CompoundSelect(GenerativeSelect): # those fks too. proxy = cols[0]._make_proxy( - self, name=cols[0]._label if self.use_labels else None, - key=cols[0]._key_label if self.use_labels else None) + self, + name=cols[0]._label if self.use_labels else None, + key=cols[0]._key_label if self.use_labels else None, + ) # hand-construct the "_proxies" collection to include all # derived columns place a 'weight' annotation corresponding @@ -2455,7 +2582,8 @@ class CompoundSelect(GenerativeSelect): # conflicts proxy._proxies = [ - c._annotate({'weight': i + 1}) for (i, c) in enumerate(cols)] + c._annotate({"weight": i + 1}) for (i, c) in enumerate(cols) + ] def _refresh_for_new_column(self, column): for s in self.selects: @@ -2464,25 +2592,32 @@ class CompoundSelect(GenerativeSelect): if not self._cols_populated: return None - raise NotImplementedError("CompoundSelect constructs don't support " - "addition of columns to underlying " - "selectables") + raise NotImplementedError( + "CompoundSelect constructs don't support " + "addition of columns to underlying " + "selectables" + ) def _copy_internals(self, clone=_clone, **kw): super(CompoundSelect, self)._copy_internals(clone, **kw) self._reset_exported() self.selects = [clone(s, **kw) for s in self.selects] - if hasattr(self, '_col_map'): + if hasattr(self, "_col_map"): del self._col_map for attr in ( - '_order_by_clause', '_group_by_clause', '_for_update_arg'): + "_order_by_clause", + "_group_by_clause", + "_for_update_arg", + ): if getattr(self, attr) is not None: setattr(self, attr, clone(getattr(self, attr), **kw)) def get_children(self, column_collections=True, **kwargs): - return (column_collections and list(self.c) or []) \ - + [self._order_by_clause, self._group_by_clause] \ + return ( + (column_collections and list(self.c) or []) + + [self._order_by_clause, self._group_by_clause] + list(self.selects) + ) def bind(self): if self._bind: @@ -2496,6 +2631,7 @@ class CompoundSelect(GenerativeSelect): def _set_bind(self, bind): self._bind = bind + bind = property(bind, _set_bind) @@ -2504,7 +2640,7 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): """ - __visit_name__ = 'select' + __visit_name__ = "select" _prefixes = () _suffixes = () @@ -2517,16 +2653,18 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): _memoized_property = SelectBase._memoized_property _is_select = True - def __init__(self, - columns=None, - whereclause=None, - from_obj=None, - distinct=False, - having=None, - correlate=True, - prefixes=None, - suffixes=None, - **kwargs): + def __init__( + self, + columns=None, + whereclause=None, + from_obj=None, + distinct=False, + having=None, + correlate=True, + prefixes=None, + suffixes=None, + **kwargs + ): """Construct a new :class:`.Select`. Similar functionality is also available via the @@ -2729,22 +2867,23 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): self._distinct = True else: self._distinct = [ - _literal_as_text(e) - for e in util.to_list(distinct) + _literal_as_text(e) for e in util.to_list(distinct) ] if from_obj is not None: self._from_obj = util.OrderedSet( - _interpret_as_from(f) - for f in util.to_list(from_obj)) + _interpret_as_from(f) for f in util.to_list(from_obj) + ) else: self._from_obj = util.OrderedSet() try: cols_present = bool(columns) except TypeError: - raise exc.ArgumentError("columns argument to select() must " - "be a Python list or other iterable") + raise exc.ArgumentError( + "columns argument to select() must " + "be a Python list or other iterable" + ) if cols_present: self._raw_columns = [] @@ -2757,14 +2896,16 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): self._raw_columns = [] if whereclause is not None: - self._whereclause = _literal_as_text( - whereclause).self_group(against=operators._asbool) + self._whereclause = _literal_as_text(whereclause).self_group( + against=operators._asbool + ) else: self._whereclause = None if having is not None: - self._having = _literal_as_text( - having).self_group(against=operators._asbool) + self._having = _literal_as_text(having).self_group( + against=operators._asbool + ) else: self._having = None @@ -2789,12 +2930,14 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): for item in itertools.chain( _from_objects(*self._raw_columns), _from_objects(self._whereclause) - if self._whereclause is not None else (), - self._from_obj + if self._whereclause is not None + else (), + self._from_obj, ): if item is self: raise exc.InvalidRequestError( - "select() construct refers to itself as a FROM") + "select() construct refers to itself as a FROM" + ) if translate and item in translate: item = translate[item] if not seen.intersection(item._cloned_set): @@ -2803,8 +2946,9 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): return froms - def _get_display_froms(self, explicit_correlate_froms=None, - implicit_correlate_froms=None): + def _get_display_froms( + self, explicit_correlate_froms=None, implicit_correlate_froms=None + ): """Return the full list of 'from' clauses to be displayed. Takes into account a set of existing froms which may be @@ -2815,17 +2959,17 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): """ froms = self._froms - toremove = set(itertools.chain(*[ - _expand_cloned(f._hide_froms) - for f in froms])) + toremove = set( + itertools.chain(*[_expand_cloned(f._hide_froms) for f in froms]) + ) if toremove: # if we're maintaining clones of froms, # add the copies out to the toremove list. only include # clones that are lexical equivalents. if self._from_cloned: toremove.update( - self._from_cloned[f] for f in - toremove.intersection(self._from_cloned) + self._from_cloned[f] + for f in toremove.intersection(self._from_cloned) if self._from_cloned[f]._is_lexical_equivalent(f) ) # filter out to FROM clauses not in the list, @@ -2836,41 +2980,53 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): to_correlate = self._correlate if to_correlate: froms = [ - f for f in froms if f not in - _cloned_intersection( + f + for f in froms + if f + not in _cloned_intersection( _cloned_intersection( - froms, explicit_correlate_froms or ()), - to_correlate + froms, explicit_correlate_froms or () + ), + to_correlate, ) ] if self._correlate_except is not None: froms = [ - f for f in froms if f not in - _cloned_difference( + f + for f in froms + if f + not in _cloned_difference( _cloned_intersection( - froms, explicit_correlate_froms or ()), - self._correlate_except + froms, explicit_correlate_froms or () + ), + self._correlate_except, ) ] - if self._auto_correlate and \ - implicit_correlate_froms and \ - len(froms) > 1: + if ( + self._auto_correlate + and implicit_correlate_froms + and len(froms) > 1 + ): froms = [ - f for f in froms if f not in - _cloned_intersection(froms, implicit_correlate_froms) + f + for f in froms + if f + not in _cloned_intersection(froms, implicit_correlate_froms) ] if not len(froms): - raise exc.InvalidRequestError("Select statement '%s" - "' returned no FROM clauses " - "due to auto-correlation; " - "specify correlate(<tables>) " - "to control correlation " - "manually." % self) + raise exc.InvalidRequestError( + "Select statement '%s" + "' returned no FROM clauses " + "due to auto-correlation; " + "specify correlate(<tables>) " + "to control correlation " + "manually." % self + ) return froms @@ -2885,7 +3041,7 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): return self._get_display_froms() - def with_statement_hint(self, text, dialect_name='*'): + def with_statement_hint(self, text, dialect_name="*"): """add a statement hint to this :class:`.Select`. This method is similar to :meth:`.Select.with_hint` except that @@ -2906,7 +3062,7 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): return self.with_hint(None, text, dialect_name) @_generative - def with_hint(self, selectable, text, dialect_name='*'): + def with_hint(self, selectable, text, dialect_name="*"): r"""Add an indexing or other executional context hint for the given selectable to this :class:`.Select`. @@ -2940,17 +3096,18 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): """ if selectable is None: - self._statement_hints += ((dialect_name, text), ) + self._statement_hints += ((dialect_name, text),) else: - self._hints = self._hints.union( - {(selectable, dialect_name): text}) + self._hints = self._hints.union({(selectable, dialect_name): text}) @property def type(self): - raise exc.InvalidRequestError("Select objects don't have a type. " - "Call as_scalar() on this Select " - "object to return a 'scalar' version " - "of this Select.") + raise exc.InvalidRequestError( + "Select objects don't have a type. " + "Call as_scalar() on this Select " + "object to return a 'scalar' version " + "of this Select." + ) @_memoized_property.method def locate_all_froms(self): @@ -2977,10 +3134,13 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): with_cols = dict( (c._resolve_label or c._label or c.key, c) for c in _select_iterables(self._raw_columns) - if c._allow_label_resolve) + if c._allow_label_resolve + ) only_froms = dict( - (c.key, c) for c in - _select_iterables(self.froms) if c._allow_label_resolve) + (c.key, c) + for c in _select_iterables(self.froms) + if c._allow_label_resolve + ) only_cols = with_cols.copy() for key, value in only_froms.items(): with_cols.setdefault(key, value) @@ -3011,11 +3171,13 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): # gets cleared on each generation. previously we were "baking" # _froms into self._from_obj. self._from_cloned = from_cloned = dict( - (f, clone(f, **kw)) for f in self._from_obj.union(self._froms)) + (f, clone(f, **kw)) for f in self._from_obj.union(self._froms) + ) # 3. update persistent _from_obj with the cloned versions. - self._from_obj = util.OrderedSet(from_cloned[f] for f in - self._from_obj) + self._from_obj = util.OrderedSet( + from_cloned[f] for f in self._from_obj + ) # the _correlate collection is done separately, what can happen # here is the same item is _correlate as in _from_obj but the @@ -3023,16 +3185,22 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): # RelationshipProperty.Comparator._criterion_exists() does # this). Also keep _correlate liberally open with its previous # contents, as this set is used for matching, not rendering. - self._correlate = set(clone(f) for f in - self._correlate).union(self._correlate) + self._correlate = set(clone(f) for f in self._correlate).union( + self._correlate + ) # 4. clone other things. The difficulty here is that Column # objects are not actually cloned, and refer to their original # .table, resulting in the wrong "from" parent after a clone # operation. Hence _from_cloned and _from_obj supersede what is # present here. self._raw_columns = [clone(c, **kw) for c in self._raw_columns] - for attr in '_whereclause', '_having', '_order_by_clause', \ - '_group_by_clause', '_for_update_arg': + for attr in ( + "_whereclause", + "_having", + "_order_by_clause", + "_group_by_clause", + "_for_update_arg", + ): if getattr(self, attr) is not None: setattr(self, attr, clone(getattr(self, attr), **kw)) @@ -3043,12 +3211,21 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): def get_children(self, column_collections=True, **kwargs): """return child elements as per the ClauseElement specification.""" - return (column_collections and list(self.columns) or []) + \ - self._raw_columns + list(self._froms) + \ - [x for x in - (self._whereclause, self._having, - self._order_by_clause, self._group_by_clause) - if x is not None] + return ( + (column_collections and list(self.columns) or []) + + self._raw_columns + + list(self._froms) + + [ + x + for x in ( + self._whereclause, + self._having, + self._order_by_clause, + self._group_by_clause, + ) + if x is not None + ] + ) @_generative def column(self, column): @@ -3094,7 +3271,7 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): sqlutil.reduce_columns( self.inner_columns, only_synonyms=only_synonyms, - *(self._whereclause, ) + tuple(self._from_obj) + *(self._whereclause,) + tuple(self._from_obj) ) ) @@ -3307,7 +3484,8 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): self._correlate = () else: self._correlate = set(self._correlate).union( - _interpret_as_from(f) for f in fromclauses) + _interpret_as_from(f) for f in fromclauses + ) @_generative def correlate_except(self, *fromclauses): @@ -3349,7 +3527,8 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): self._correlate_except = () else: self._correlate_except = set(self._correlate_except or ()).union( - _interpret_as_from(f) for f in fromclauses) + _interpret_as_from(f) for f in fromclauses + ) def append_correlation(self, fromclause): """append the given correlation expression to this select() @@ -3363,7 +3542,8 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): self._auto_correlate = False self._correlate = set(self._correlate).union( - _interpret_as_from(f) for f in fromclause) + _interpret_as_from(f) for f in fromclause + ) def append_column(self, column): """append the given column expression to the columns clause of this @@ -3415,8 +3595,7 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): """ self._reset_exported() - self._whereclause = and_( - True_._ifnone(self._whereclause), whereclause) + self._whereclause = and_(True_._ifnone(self._whereclause), whereclause) def append_having(self, having): """append the given expression to this select() construct's HAVING @@ -3463,19 +3642,17 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): return [ name_for_col(c) - for c in util.unique_list( - _select_iterables(self._raw_columns)) + for c in util.unique_list(_select_iterables(self._raw_columns)) ] else: return [ (None, c) - for c in util.unique_list( - _select_iterables(self._raw_columns)) + for c in util.unique_list(_select_iterables(self._raw_columns)) ] def _populate_column_collection(self): for name, c in self._columns_plus_names: - if not hasattr(c, '_make_proxy'): + if not hasattr(c, "_make_proxy"): continue if name is None: key = None @@ -3486,9 +3663,7 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): else: key = None - c._make_proxy(self, key=key, - name=name, - name_is_truncatable=True) + c._make_proxy(self, key=key, name=name, name_is_truncatable=True) def _refresh_for_new_column(self, column): for fromclause in self._froms: @@ -3501,15 +3676,16 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): self, name=col._label if self.use_labels else None, key=col._key_label if self.use_labels else None, - name_is_truncatable=True) + name_is_truncatable=True, + ) return None return None def _needs_parens_for_grouping(self): return ( - self._limit_clause is not None or - self._offset_clause is not None or - bool(self._order_by_clause.clauses) + self._limit_clause is not None + or self._offset_clause is not None + or bool(self._order_by_clause.clauses) ) def self_group(self, against=None): @@ -3521,8 +3697,10 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): expressions and should not require explicit use. """ - if isinstance(against, CompoundSelect) and \ - not self._needs_parens_for_grouping(): + if ( + isinstance(against, CompoundSelect) + and not self._needs_parens_for_grouping() + ): return self return FromGrouping(self) @@ -3586,6 +3764,7 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): def _set_bind(self, bind): self._bind = bind + bind = property(bind, _set_bind) @@ -3600,9 +3779,12 @@ class ScalarSelect(Generative, Grouping): @property def columns(self): - raise exc.InvalidRequestError('Scalar Select expression has no ' - 'columns; use this object directly ' - 'within a column-level expression.') + raise exc.InvalidRequestError( + "Scalar Select expression has no " + "columns; use this object directly " + "within a column-level expression." + ) + c = columns @_generative @@ -3621,6 +3803,7 @@ class Exists(UnaryExpression): """Represent an ``EXISTS`` clause. """ + __visit_name__ = UnaryExpression.__visit_name__ _from_objects = [] @@ -3646,12 +3829,16 @@ class Exists(UnaryExpression): s = args[0] else: if not args: - args = ([literal_column('*')],) + args = ([literal_column("*")],) s = Select(*args, **kwargs).as_scalar().self_group() - UnaryExpression.__init__(self, s, operator=operators.exists, - type_=type_api.BOOLEANTYPE, - wraps_column_expression=True) + UnaryExpression.__init__( + self, + s, + operator=operators.exists, + type_=type_api.BOOLEANTYPE, + wraps_column_expression=True, + ) def select(self, whereclause=None, **params): return Select([self], whereclause, **params) @@ -3706,6 +3893,7 @@ class TextAsFrom(SelectBase): :meth:`.TextClause.columns` """ + __visit_name__ = "text_as_from" _textual = True |