1# orm/util.py
2# Copyright (C) 2005-2016 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: http://www.opensource.org/licenses/mit-license.php
7
8
9from .. import sql, util, event, exc as sa_exc, inspection
10from ..sql import expression, util as sql_util, operators
11from .interfaces import PropComparator, MapperProperty
12from . import attributes
13import re
14
15from .base import instance_str, state_str, state_class_str, attribute_str, \
16    state_attribute_str, object_mapper, object_state, _none_set, _never_set
17from .base import class_mapper, _class_to_mapper
18from .base import InspectionAttr
19from .path_registry import PathRegistry
20
21all_cascades = frozenset(("delete", "delete-orphan", "all", "merge",
22                          "expunge", "save-update", "refresh-expire",
23                          "none"))
24
25
26class CascadeOptions(frozenset):
27    """Keeps track of the options sent to relationship().cascade"""
28
29    _add_w_all_cascades = all_cascades.difference([
30        'all', 'none', 'delete-orphan'])
31    _allowed_cascades = all_cascades
32
33    __slots__ = (
34        'save_update', 'delete', 'refresh_expire', 'merge',
35        'expunge', 'delete_orphan')
36
37    def __new__(cls, value_list):
38        if isinstance(value_list, util.string_types) or value_list is None:
39            return cls.from_string(value_list)
40        values = set(value_list)
41        if values.difference(cls._allowed_cascades):
42            raise sa_exc.ArgumentError(
43                "Invalid cascade option(s): %s" %
44                ", ".join([repr(x) for x in
45                           sorted(values.difference(cls._allowed_cascades))]))
46
47        if "all" in values:
48            values.update(cls._add_w_all_cascades)
49        if "none" in values:
50            values.clear()
51        values.discard('all')
52
53        self = frozenset.__new__(CascadeOptions, values)
54        self.save_update = 'save-update' in values
55        self.delete = 'delete' in values
56        self.refresh_expire = 'refresh-expire' in values
57        self.merge = 'merge' in values
58        self.expunge = 'expunge' in values
59        self.delete_orphan = "delete-orphan" in values
60
61        if self.delete_orphan and not self.delete:
62            util.warn("The 'delete-orphan' cascade "
63                      "option requires 'delete'.")
64        return self
65
66    def __repr__(self):
67        return "CascadeOptions(%r)" % (
68            ",".join([x for x in sorted(self)])
69        )
70
71    @classmethod
72    def from_string(cls, arg):
73        values = [
74            c for c
75            in re.split('\s*,\s*', arg or "")
76            if c
77        ]
78        return cls(values)
79
80
81def _validator_events(
82        desc, key, validator, include_removes, include_backrefs):
83    """Runs a validation method on an attribute value to be set or
84    appended.
85    """
86
87    if not include_backrefs:
88        def detect_is_backref(state, initiator):
89            impl = state.manager[key].impl
90            return initiator.impl is not impl
91
92    if include_removes:
93        def append(state, value, initiator):
94            if include_backrefs or not detect_is_backref(state, initiator):
95                return validator(state.obj(), key, value, False)
96            else:
97                return value
98
99        def set_(state, value, oldvalue, initiator):
100            if include_backrefs or not detect_is_backref(state, initiator):
101                return validator(state.obj(), key, value, False)
102            else:
103                return value
104
105        def remove(state, value, initiator):
106            if include_backrefs or not detect_is_backref(state, initiator):
107                validator(state.obj(), key, value, True)
108
109    else:
110        def append(state, value, initiator):
111            if include_backrefs or not detect_is_backref(state, initiator):
112                return validator(state.obj(), key, value)
113            else:
114                return value
115
116        def set_(state, value, oldvalue, initiator):
117            if include_backrefs or not detect_is_backref(state, initiator):
118                return validator(state.obj(), key, value)
119            else:
120                return value
121
122    event.listen(desc, 'append', append, raw=True, retval=True)
123    event.listen(desc, 'set', set_, raw=True, retval=True)
124    if include_removes:
125        event.listen(desc, "remove", remove, raw=True, retval=True)
126
127
128def polymorphic_union(table_map, typecolname,
129                      aliasname='p_union', cast_nulls=True):
130    """Create a ``UNION`` statement used by a polymorphic mapper.
131
132    See  :ref:`concrete_inheritance` for an example of how
133    this is used.
134
135    :param table_map: mapping of polymorphic identities to
136     :class:`.Table` objects.
137    :param typecolname: string name of a "discriminator" column, which will be
138     derived from the query, producing the polymorphic identity for
139     each row.  If ``None``, no polymorphic discriminator is generated.
140    :param aliasname: name of the :func:`~sqlalchemy.sql.expression.alias()`
141     construct generated.
142    :param cast_nulls: if True, non-existent columns, which are represented
143     as labeled NULLs, will be passed into CAST.   This is a legacy behavior
144     that is problematic on some backends such as Oracle - in which case it
145     can be set to False.
146
147    """
148
149    colnames = util.OrderedSet()
150    colnamemaps = {}
151    types = {}
152    for key in table_map:
153        table = table_map[key]
154
155        # mysql doesn't like selecting from a select;
156        # make it an alias of the select
157        if isinstance(table, sql.Select):
158            table = table.alias()
159            table_map[key] = table
160
161        m = {}
162        for c in table.c:
163            colnames.add(c.key)
164            m[c.key] = c
165            types[c.key] = c.type
166        colnamemaps[table] = m
167
168    def col(name, table):
169        try:
170            return colnamemaps[table][name]
171        except KeyError:
172            if cast_nulls:
173                return sql.cast(sql.null(), types[name]).label(name)
174            else:
175                return sql.type_coerce(sql.null(), types[name]).label(name)
176
177    result = []
178    for type, table in table_map.items():
179        if typecolname is not None:
180            result.append(
181                sql.select([col(name, table) for name in colnames] +
182                           [sql.literal_column(
183                               sql_util._quote_ddl_expr(type)).
184                               label(typecolname)],
185                           from_obj=[table]))
186        else:
187            result.append(sql.select([col(name, table) for name in colnames],
188                                     from_obj=[table]))
189    return sql.union_all(*result).alias(aliasname)
190
191
192def identity_key(*args, **kwargs):
193    """Generate "identity key" tuples, as are used as keys in the
194    :attr:`.Session.identity_map` dictionary.
195
196    This function has several call styles:
197
198    * ``identity_key(class, ident)``
199
200      This form receives a mapped class and a primary key scalar or
201      tuple as an argument.
202
203      E.g.::
204
205        >>> identity_key(MyClass, (1, 2))
206        (<class '__main__.MyClass'>, (1, 2))
207
208      :param class: mapped class (must be a positional argument)
209      :param ident: primary key, may be a scalar or tuple argument.
210
211
212    * ``identity_key(instance=instance)``
213
214      This form will produce the identity key for a given instance.  The
215      instance need not be persistent, only that its primary key attributes
216      are populated (else the key will contain ``None`` for those missing
217      values).
218
219      E.g.::
220
221        >>> instance = MyClass(1, 2)
222        >>> identity_key(instance=instance)
223        (<class '__main__.MyClass'>, (1, 2))
224
225      In this form, the given instance is ultimately run though
226      :meth:`.Mapper.identity_key_from_instance`, which will have the
227      effect of performing a database check for the corresponding row
228      if the object is expired.
229
230      :param instance: object instance (must be given as a keyword arg)
231
232    * ``identity_key(class, row=row)``
233
234      This form is similar to the class/tuple form, except is passed a
235      database result row as a :class:`.RowProxy` object.
236
237      E.g.::
238
239        >>> row = engine.execute("select * from table where a=1 and b=2").\
240first()
241        >>> identity_key(MyClass, row=row)
242        (<class '__main__.MyClass'>, (1, 2))
243
244      :param class: mapped class (must be a positional argument)
245      :param row: :class:`.RowProxy` row returned by a :class:`.ResultProxy`
246       (must be given as a keyword arg)
247
248    """
249    if args:
250        if len(args) == 1:
251            class_ = args[0]
252            try:
253                row = kwargs.pop("row")
254            except KeyError:
255                ident = kwargs.pop("ident")
256        elif len(args) == 2:
257            class_, ident = args
258        elif len(args) == 3:
259            class_, ident = args
260        else:
261            raise sa_exc.ArgumentError(
262                "expected up to three positional arguments, "
263                "got %s" % len(args))
264        if kwargs:
265            raise sa_exc.ArgumentError("unknown keyword arguments: %s"
266                                       % ", ".join(kwargs))
267        mapper = class_mapper(class_)
268        if "ident" in locals():
269            return mapper.identity_key_from_primary_key(util.to_list(ident))
270        return mapper.identity_key_from_row(row)
271    instance = kwargs.pop("instance")
272    if kwargs:
273        raise sa_exc.ArgumentError("unknown keyword arguments: %s"
274                                   % ", ".join(kwargs.keys))
275    mapper = object_mapper(instance)
276    return mapper.identity_key_from_instance(instance)
277
278
279class ORMAdapter(sql_util.ColumnAdapter):
280    """ColumnAdapter subclass which excludes adaptation of entities from
281    non-matching mappers.
282
283    """
284
285    def __init__(self, entity, equivalents=None, adapt_required=False,
286                 chain_to=None, allow_label_resolve=True,
287                 anonymize_labels=False):
288        info = inspection.inspect(entity)
289
290        self.mapper = info.mapper
291        selectable = info.selectable
292        is_aliased_class = info.is_aliased_class
293        if is_aliased_class:
294            self.aliased_class = entity
295        else:
296            self.aliased_class = None
297
298        sql_util.ColumnAdapter.__init__(
299            self, selectable, equivalents, chain_to,
300            adapt_required=adapt_required,
301            allow_label_resolve=allow_label_resolve,
302            anonymize_labels=anonymize_labels,
303            include_fn=self._include_fn
304        )
305
306    def _include_fn(self, elem):
307        entity = elem._annotations.get('parentmapper', None)
308        return not entity or entity.isa(self.mapper)
309
310
311class AliasedClass(object):
312    """Represents an "aliased" form of a mapped class for usage with Query.
313
314    The ORM equivalent of a :func:`sqlalchemy.sql.expression.alias`
315    construct, this object mimics the mapped class using a
316    __getattr__ scheme and maintains a reference to a
317    real :class:`~sqlalchemy.sql.expression.Alias` object.
318
319    Usage is via the :func:`.orm.aliased` function, or alternatively
320    via the :func:`.orm.with_polymorphic` function.
321
322    Usage example::
323
324        # find all pairs of users with the same name
325        user_alias = aliased(User)
326        session.query(User, user_alias).\\
327                        join((user_alias, User.id > user_alias.id)).\\
328                        filter(User.name==user_alias.name)
329
330    The resulting object is an instance of :class:`.AliasedClass`.
331    This object implements an attribute scheme which produces the
332    same attribute and method interface as the original mapped
333    class, allowing :class:`.AliasedClass` to be compatible
334    with any attribute technique which works on the original class,
335    including hybrid attributes (see :ref:`hybrids_toplevel`).
336
337    The :class:`.AliasedClass` can be inspected for its underlying
338    :class:`.Mapper`, aliased selectable, and other information
339    using :func:`.inspect`::
340
341        from sqlalchemy import inspect
342        my_alias = aliased(MyClass)
343        insp = inspect(my_alias)
344
345    The resulting inspection object is an instance of :class:`.AliasedInsp`.
346
347    See :func:`.aliased` and :func:`.with_polymorphic` for construction
348    argument descriptions.
349
350    """
351
352    def __init__(self, cls, alias=None,
353                 name=None,
354                 flat=False,
355                 adapt_on_names=False,
356                 #  TODO: None for default here?
357                 with_polymorphic_mappers=(),
358                 with_polymorphic_discriminator=None,
359                 base_alias=None,
360                 use_mapper_path=False):
361        mapper = _class_to_mapper(cls)
362        if alias is None:
363            alias = mapper._with_polymorphic_selectable.alias(
364                name=name, flat=flat)
365
366        self._aliased_insp = AliasedInsp(
367            self,
368            mapper,
369            alias,
370            name,
371            with_polymorphic_mappers
372            if with_polymorphic_mappers
373            else mapper.with_polymorphic_mappers,
374            with_polymorphic_discriminator
375            if with_polymorphic_discriminator is not None
376            else mapper.polymorphic_on,
377            base_alias,
378            use_mapper_path,
379            adapt_on_names
380        )
381
382        self.__name__ = 'AliasedClass_%s' % mapper.class_.__name__
383
384    def __getattr__(self, key):
385        try:
386            _aliased_insp = self.__dict__['_aliased_insp']
387        except KeyError:
388            raise AttributeError()
389        else:
390            for base in _aliased_insp._target.__mro__:
391                try:
392                    attr = object.__getattribute__(base, key)
393                except AttributeError:
394                    continue
395                else:
396                    break
397            else:
398                raise AttributeError(key)
399
400        if isinstance(attr, PropComparator):
401            ret = attr.adapt_to_entity(_aliased_insp)
402            setattr(self, key, ret)
403            return ret
404        elif hasattr(attr, 'func_code'):
405            is_method = getattr(_aliased_insp._target, key, None)
406            if is_method and is_method.__self__ is not None:
407                return util.types.MethodType(attr.__func__, self, self)
408            else:
409                return None
410        elif hasattr(attr, '__get__'):
411            ret = attr.__get__(None, self)
412            if isinstance(ret, PropComparator):
413                return ret.adapt_to_entity(_aliased_insp)
414            else:
415                return ret
416        else:
417            return attr
418
419    def __repr__(self):
420        return '<AliasedClass at 0x%x; %s>' % (
421            id(self), self._aliased_insp._target.__name__)
422
423
424class AliasedInsp(InspectionAttr):
425    """Provide an inspection interface for an
426    :class:`.AliasedClass` object.
427
428    The :class:`.AliasedInsp` object is returned
429    given an :class:`.AliasedClass` using the
430    :func:`.inspect` function::
431
432        from sqlalchemy import inspect
433        from sqlalchemy.orm import aliased
434
435        my_alias = aliased(MyMappedClass)
436        insp = inspect(my_alias)
437
438    Attributes on :class:`.AliasedInsp`
439    include:
440
441    * ``entity`` - the :class:`.AliasedClass` represented.
442    * ``mapper`` - the :class:`.Mapper` mapping the underlying class.
443    * ``selectable`` - the :class:`.Alias` construct which ultimately
444      represents an aliased :class:`.Table` or :class:`.Select`
445      construct.
446    * ``name`` - the name of the alias.  Also is used as the attribute
447      name when returned in a result tuple from :class:`.Query`.
448    * ``with_polymorphic_mappers`` - collection of :class:`.Mapper` objects
449      indicating all those mappers expressed in the select construct
450      for the :class:`.AliasedClass`.
451    * ``polymorphic_on`` - an alternate column or SQL expression which
452      will be used as the "discriminator" for a polymorphic load.
453
454    .. seealso::
455
456        :ref:`inspection_toplevel`
457
458    """
459
460    def __init__(self, entity, mapper, selectable, name,
461                 with_polymorphic_mappers, polymorphic_on,
462                 _base_alias, _use_mapper_path, adapt_on_names):
463        self.entity = entity
464        self.mapper = mapper
465        self.selectable = selectable
466        self.name = name
467        self.with_polymorphic_mappers = with_polymorphic_mappers
468        self.polymorphic_on = polymorphic_on
469        self._base_alias = _base_alias or self
470        self._use_mapper_path = _use_mapper_path
471
472        self._adapter = sql_util.ColumnAdapter(
473            selectable, equivalents=mapper._equivalent_columns,
474            adapt_on_names=adapt_on_names, anonymize_labels=True)
475
476        self._adapt_on_names = adapt_on_names
477        self._target = mapper.class_
478
479        for poly in self.with_polymorphic_mappers:
480            if poly is not mapper:
481                setattr(self.entity, poly.class_.__name__,
482                        AliasedClass(poly.class_, selectable, base_alias=self,
483                                     adapt_on_names=adapt_on_names,
484                                     use_mapper_path=_use_mapper_path))
485
486    is_aliased_class = True
487    "always returns True"
488
489    @property
490    def class_(self):
491        """Return the mapped class ultimately represented by this
492        :class:`.AliasedInsp`."""
493        return self.mapper.class_
494
495    @util.memoized_property
496    def _path_registry(self):
497        if self._use_mapper_path:
498            return self.mapper._path_registry
499        else:
500            return PathRegistry.per_mapper(self)
501
502    def __getstate__(self):
503        return {
504            'entity': self.entity,
505            'mapper': self.mapper,
506            'alias': self.selectable,
507            'name': self.name,
508            'adapt_on_names': self._adapt_on_names,
509            'with_polymorphic_mappers':
510                self.with_polymorphic_mappers,
511            'with_polymorphic_discriminator':
512                self.polymorphic_on,
513            'base_alias': self._base_alias,
514            'use_mapper_path': self._use_mapper_path
515        }
516
517    def __setstate__(self, state):
518        self.__init__(
519            state['entity'],
520            state['mapper'],
521            state['alias'],
522            state['name'],
523            state['with_polymorphic_mappers'],
524            state['with_polymorphic_discriminator'],
525            state['base_alias'],
526            state['use_mapper_path'],
527            state['adapt_on_names']
528        )
529
530    def _adapt_element(self, elem):
531        return self._adapter.traverse(elem).\
532            _annotate({
533                'parententity': self,
534                'parentmapper': self.mapper}
535        )
536
537    def _entity_for_mapper(self, mapper):
538        self_poly = self.with_polymorphic_mappers
539        if mapper in self_poly:
540            if mapper is self.mapper:
541                return self
542            else:
543                return getattr(
544                    self.entity, mapper.class_.__name__)._aliased_insp
545        elif mapper.isa(self.mapper):
546            return self
547        else:
548            assert False, "mapper %s doesn't correspond to %s" % (
549                mapper, self)
550
551    def __repr__(self):
552        if self.with_polymorphic_mappers:
553            with_poly = "(%s)" % ", ".join(
554                mp.class_.__name__ for mp in self.with_polymorphic_mappers)
555        else:
556            with_poly = ""
557        return '<AliasedInsp at 0x%x; %s%s>' % (
558            id(self), self.class_.__name__, with_poly)
559
560
561inspection._inspects(AliasedClass)(lambda target: target._aliased_insp)
562inspection._inspects(AliasedInsp)(lambda target: target)
563
564
565def aliased(element, alias=None, name=None, flat=False, adapt_on_names=False):
566    """Produce an alias of the given element, usually an :class:`.AliasedClass`
567    instance.
568
569    E.g.::
570
571        my_alias = aliased(MyClass)
572
573        session.query(MyClass, my_alias).filter(MyClass.id > my_alias.id)
574
575    The :func:`.aliased` function is used to create an ad-hoc mapping
576    of a mapped class to a new selectable.  By default, a selectable
577    is generated from the normally mapped selectable (typically a
578    :class:`.Table`) using the :meth:`.FromClause.alias` method.
579    However, :func:`.aliased` can also be used to link the class to
580    a new :func:`.select` statement.   Also, the :func:`.with_polymorphic`
581    function is a variant of :func:`.aliased` that is intended to specify
582    a so-called "polymorphic selectable", that corresponds to the union
583    of several joined-inheritance subclasses at once.
584
585    For convenience, the :func:`.aliased` function also accepts plain
586    :class:`.FromClause` constructs, such as a :class:`.Table` or
587    :func:`.select` construct.   In those cases, the :meth:`.FromClause.alias`
588    method is called on the object and the new :class:`.Alias` object
589    returned.  The returned :class:`.Alias` is not ORM-mapped in this case.
590
591    :param element: element to be aliased.  Is normally a mapped class,
592     but for convenience can also be a :class:`.FromClause` element.
593
594    :param alias: Optional selectable unit to map the element to.  This should
595     normally be a :class:`.Alias` object corresponding to the :class:`.Table`
596     to which the class is mapped, or to a :func:`.select` construct that
597     is compatible with the mapping.   By default, a simple anonymous
598     alias of the mapped table is generated.
599
600    :param name: optional string name to use for the alias, if not specified
601     by the ``alias`` parameter.  The name, among other things, forms the
602     attribute name that will be accessible via tuples returned by a
603     :class:`.Query` object.
604
605    :param flat: Boolean, will be passed through to the
606     :meth:`.FromClause.alias` call so that aliases of :class:`.Join` objects
607     don't include an enclosing SELECT.  This can lead to more efficient
608     queries in many circumstances.  A JOIN against a nested JOIN will be
609     rewritten as a JOIN against an aliased SELECT subquery on backends that
610     don't support this syntax.
611
612     .. versionadded:: 0.9.0
613
614     .. seealso:: :meth:`.Join.alias`
615
616    :param adapt_on_names: if True, more liberal "matching" will be used when
617     mapping the mapped columns of the ORM entity to those of the
618     given selectable - a name-based match will be performed if the
619     given selectable doesn't otherwise have a column that corresponds
620     to one on the entity.  The use case for this is when associating
621     an entity with some derived selectable such as one that uses
622     aggregate functions::
623
624        class UnitPrice(Base):
625            __tablename__ = 'unit_price'
626            ...
627            unit_id = Column(Integer)
628            price = Column(Numeric)
629
630        aggregated_unit_price = Session.query(
631                                    func.sum(UnitPrice.price).label('price')
632                                ).group_by(UnitPrice.unit_id).subquery()
633
634        aggregated_unit_price = aliased(UnitPrice,
635                    alias=aggregated_unit_price, adapt_on_names=True)
636
637     Above, functions on ``aggregated_unit_price`` which refer to
638     ``.price`` will return the
639     ``fund.sum(UnitPrice.price).label('price')`` column, as it is
640     matched on the name "price".  Ordinarily, the "price" function
641     wouldn't have any "column correspondence" to the actual
642     ``UnitPrice.price`` column as it is not a proxy of the original.
643
644     .. versionadded:: 0.7.3
645
646
647    """
648    if isinstance(element, expression.FromClause):
649        if adapt_on_names:
650            raise sa_exc.ArgumentError(
651                "adapt_on_names only applies to ORM elements"
652            )
653        return element.alias(name, flat=flat)
654    else:
655        return AliasedClass(element, alias=alias, flat=flat,
656                            name=name, adapt_on_names=adapt_on_names)
657
658
659def with_polymorphic(base, classes, selectable=False,
660                     flat=False,
661                     polymorphic_on=None, aliased=False,
662                     innerjoin=False, _use_mapper_path=False,
663                     _existing_alias=None):
664    """Produce an :class:`.AliasedClass` construct which specifies
665    columns for descendant mappers of the given base.
666
667    .. versionadded:: 0.8
668        :func:`.orm.with_polymorphic` is in addition to the existing
669        :class:`.Query` method :meth:`.Query.with_polymorphic`,
670        which has the same purpose but is not as flexible in its usage.
671
672    Using this method will ensure that each descendant mapper's
673    tables are included in the FROM clause, and will allow filter()
674    criterion to be used against those tables.  The resulting
675    instances will also have those columns already loaded so that
676    no "post fetch" of those columns will be required.
677
678    See the examples at :ref:`with_polymorphic`.
679
680    :param base: Base class to be aliased.
681
682    :param classes: a single class or mapper, or list of
683        class/mappers, which inherit from the base class.
684        Alternatively, it may also be the string ``'*'``, in which case
685        all descending mapped classes will be added to the FROM clause.
686
687    :param aliased: when True, the selectable will be wrapped in an
688        alias, that is ``(SELECT * FROM <fromclauses>) AS anon_1``.
689        This can be important when using the with_polymorphic()
690        to create the target of a JOIN on a backend that does not
691        support parenthesized joins, such as SQLite and older
692        versions of MySQL.
693
694    :param flat: Boolean, will be passed through to the
695        :meth:`.FromClause.alias` call so that aliases of :class:`.Join`
696        objects don't include an enclosing SELECT.  This can lead to more
697        efficient queries in many circumstances.  A JOIN against a nested JOIN
698        will be rewritten as a JOIN against an aliased SELECT subquery on
699        backends that don't support this syntax.
700
701     Setting ``flat`` to ``True`` implies the ``aliased`` flag is
702     also ``True``.
703
704     .. versionadded:: 0.9.0
705
706     .. seealso:: :meth:`.Join.alias`
707
708    :param selectable: a table or select() statement that will
709        be used in place of the generated FROM clause. This argument is
710        required if any of the desired classes use concrete table
711        inheritance, since SQLAlchemy currently cannot generate UNIONs
712        among tables automatically. If used, the ``selectable`` argument
713        must represent the full set of tables and columns mapped by every
714        mapped class. Otherwise, the unaccounted mapped columns will
715        result in their table being appended directly to the FROM clause
716        which will usually lead to incorrect results.
717
718    :param polymorphic_on: a column to be used as the "discriminator"
719        column for the given selectable. If not given, the polymorphic_on
720        attribute of the base classes' mapper will be used, if any. This
721        is useful for mappings that don't have polymorphic loading
722        behavior by default.
723
724    :param innerjoin: if True, an INNER JOIN will be used.  This should
725       only be specified if querying for one specific subtype only
726    """
727    primary_mapper = _class_to_mapper(base)
728    if _existing_alias:
729        assert _existing_alias.mapper is primary_mapper
730        classes = util.to_set(classes)
731        new_classes = set([
732            mp.class_ for mp in
733            _existing_alias.with_polymorphic_mappers])
734        if classes == new_classes:
735            return _existing_alias
736        else:
737            classes = classes.union(new_classes)
738    mappers, selectable = primary_mapper.\
739        _with_polymorphic_args(classes, selectable,
740                               innerjoin=innerjoin)
741    if aliased or flat:
742        selectable = selectable.alias(flat=flat)
743    return AliasedClass(base,
744                        selectable,
745                        with_polymorphic_mappers=mappers,
746                        with_polymorphic_discriminator=polymorphic_on,
747                        use_mapper_path=_use_mapper_path)
748
749
750def _orm_annotate(element, exclude=None):
751    """Deep copy the given ClauseElement, annotating each element with the
752    "_orm_adapt" flag.
753
754    Elements within the exclude collection will be cloned but not annotated.
755
756    """
757    return sql_util._deep_annotate(element, {'_orm_adapt': True}, exclude)
758
759
760def _orm_deannotate(element):
761    """Remove annotations that link a column to a particular mapping.
762
763    Note this doesn't affect "remote" and "foreign" annotations
764    passed by the :func:`.orm.foreign` and :func:`.orm.remote`
765    annotators.
766
767    """
768
769    return sql_util._deep_deannotate(element,
770                                     values=("_orm_adapt", "parententity")
771                                     )
772
773
774def _orm_full_deannotate(element):
775    return sql_util._deep_deannotate(element)
776
777
778class _ORMJoin(expression.Join):
779    """Extend Join to support ORM constructs as input."""
780
781    __visit_name__ = expression.Join.__visit_name__
782
783    def __init__(
784            self,
785            left, right, onclause=None, isouter=False,
786            _left_memo=None, _right_memo=None):
787
788        left_info = inspection.inspect(left)
789        left_orm_info = getattr(left, '_joined_from_info', left_info)
790
791        right_info = inspection.inspect(right)
792        adapt_to = right_info.selectable
793
794        self._joined_from_info = right_info
795
796        self._left_memo = _left_memo
797        self._right_memo = _right_memo
798
799        if isinstance(onclause, util.string_types):
800            onclause = getattr(left_orm_info.entity, onclause)
801
802        if isinstance(onclause, attributes.QueryableAttribute):
803            on_selectable = onclause.comparator._source_selectable()
804            prop = onclause.property
805        elif isinstance(onclause, MapperProperty):
806            prop = onclause
807            on_selectable = prop.parent.selectable
808        else:
809            prop = None
810
811        if prop:
812            if sql_util.clause_is_present(
813                    on_selectable, left_info.selectable):
814                adapt_from = on_selectable
815            else:
816                adapt_from = left_info.selectable
817
818            pj, sj, source, dest, \
819                secondary, target_adapter = prop._create_joins(
820                    source_selectable=adapt_from,
821                    dest_selectable=adapt_to,
822                    source_polymorphic=True,
823                    dest_polymorphic=True,
824                    of_type=right_info.mapper)
825
826            if sj is not None:
827                if isouter:
828                    # note this is an inner join from secondary->right
829                    right = sql.join(secondary, right, sj)
830                    onclause = pj
831                else:
832                    left = sql.join(left, secondary, pj, isouter)
833                    onclause = sj
834            else:
835                onclause = pj
836            self._target_adapter = target_adapter
837
838        expression.Join.__init__(self, left, right, onclause, isouter)
839
840        if not prop and getattr(right_info, 'mapper', None) \
841                and right_info.mapper.single:
842            # if single inheritance target and we are using a manual
843            # or implicit ON clause, augment it the same way we'd augment the
844            # WHERE.
845            single_crit = right_info.mapper._single_table_criterion
846            if single_crit is not None:
847                if right_info.is_aliased_class:
848                    single_crit = right_info._adapter.traverse(single_crit)
849                self.onclause = self.onclause & single_crit
850
851    def _splice_into_center(self, other):
852        """Splice a join into the center.
853
854        Given join(a, b) and join(b, c), return join(a, b).join(c)
855
856        """
857        leftmost = other
858        while isinstance(leftmost, sql.Join):
859            leftmost = leftmost.left
860
861        assert self.right is leftmost
862
863        left = _ORMJoin(
864            self.left, other.left,
865            self.onclause, isouter=self.isouter,
866            _left_memo=self._left_memo,
867            _right_memo=other._left_memo
868        )
869
870        return _ORMJoin(
871            left,
872            other.right,
873            other.onclause, isouter=other.isouter,
874            _right_memo=other._right_memo
875        )
876
877    def join(self, right, onclause=None, isouter=False, join_to_left=None):
878        return _ORMJoin(self, right, onclause, isouter)
879
880    def outerjoin(self, right, onclause=None, join_to_left=None):
881        return _ORMJoin(self, right, onclause, True)
882
883
884def join(left, right, onclause=None, isouter=False, join_to_left=None):
885    """Produce an inner join between left and right clauses.
886
887    :func:`.orm.join` is an extension to the core join interface
888    provided by :func:`.sql.expression.join()`, where the
889    left and right selectables may be not only core selectable
890    objects such as :class:`.Table`, but also mapped classes or
891    :class:`.AliasedClass` instances.   The "on" clause can
892    be a SQL expression, or an attribute or string name
893    referencing a configured :func:`.relationship`.
894
895    :func:`.orm.join` is not commonly needed in modern usage,
896    as its functionality is encapsulated within that of the
897    :meth:`.Query.join` method, which features a
898    significant amount of automation beyond :func:`.orm.join`
899    by itself.  Explicit usage of :func:`.orm.join`
900    with :class:`.Query` involves usage of the
901    :meth:`.Query.select_from` method, as in::
902
903        from sqlalchemy.orm import join
904        session.query(User).\\
905            select_from(join(User, Address, User.addresses)).\\
906            filter(Address.email_address=='foo@bar.com')
907
908    In modern SQLAlchemy the above join can be written more
909    succinctly as::
910
911        session.query(User).\\
912                join(User.addresses).\\
913                filter(Address.email_address=='foo@bar.com')
914
915    See :meth:`.Query.join` for information on modern usage
916    of ORM level joins.
917
918    .. versionchanged:: 0.8.1 - the ``join_to_left`` parameter
919       is no longer used, and is deprecated.
920
921    """
922    return _ORMJoin(left, right, onclause, isouter)
923
924
925def outerjoin(left, right, onclause=None, join_to_left=None):
926    """Produce a left outer join between left and right clauses.
927
928    This is the "outer join" version of the :func:`.orm.join` function,
929    featuring the same behavior except that an OUTER JOIN is generated.
930    See that function's documentation for other usage details.
931
932    """
933    return _ORMJoin(left, right, onclause, True)
934
935
936def with_parent(instance, prop):
937    """Create filtering criterion that relates this query's primary entity
938    to the given related instance, using established :func:`.relationship()`
939    configuration.
940
941    The SQL rendered is the same as that rendered when a lazy loader
942    would fire off from the given parent on that attribute, meaning
943    that the appropriate state is taken from the parent object in
944    Python without the need to render joins to the parent table
945    in the rendered statement.
946
947    .. versionchanged:: 0.6.4
948        This method accepts parent instances in all
949        persistence states, including transient, persistent, and detached.
950        Only the requisite primary key/foreign key attributes need to
951        be populated.  Previous versions didn't work with transient
952        instances.
953
954    :param instance:
955      An instance which has some :func:`.relationship`.
956
957    :param property:
958      String property name, or class-bound attribute, which indicates
959      what relationship from the instance should be used to reconcile the
960      parent/child relationship.
961
962    """
963    if isinstance(prop, util.string_types):
964        mapper = object_mapper(instance)
965        prop = getattr(mapper.class_, prop).property
966    elif isinstance(prop, attributes.QueryableAttribute):
967        prop = prop.property
968
969    return prop._with_parent(instance)
970
971
972def has_identity(object):
973    """Return True if the given object has a database
974    identity.
975
976    This typically corresponds to the object being
977    in either the persistent or detached state.
978
979    .. seealso::
980
981        :func:`.was_deleted`
982
983    """
984    state = attributes.instance_state(object)
985    return state.has_identity
986
987
988def was_deleted(object):
989    """Return True if the given object was deleted
990    within a session flush.
991
992    .. versionadded:: 0.8.0
993
994    """
995
996    state = attributes.instance_state(object)
997    return state.deleted
998
999
1000def randomize_unitofwork():
1001    """Use random-ordering sets within the unit of work in order
1002    to detect unit of work sorting issues.
1003
1004    This is a utility function that can be used to help reproduce
1005    inconsistent unit of work sorting issues.   For example,
1006    if two kinds of objects A and B are being inserted, and
1007    B has a foreign key reference to A - the A must be inserted first.
1008    However, if there is no relationship between A and B, the unit of work
1009    won't know to perform this sorting, and an operation may or may not
1010    fail, depending on how the ordering works out.   Since Python sets
1011    and dictionaries have non-deterministic ordering, such an issue may
1012    occur on some runs and not on others, and in practice it tends to
1013    have a great dependence on the state of the interpreter.  This leads
1014    to so-called "heisenbugs" where changing entirely irrelevant aspects
1015    of the test program still cause the failure behavior to change.
1016
1017    By calling ``randomize_unitofwork()`` when a script first runs, the
1018    ordering of a key series of sets within the unit of work implementation
1019    are randomized, so that the script can be minimized down to the
1020    fundamental mapping and operation that's failing, while still reproducing
1021    the issue on at least some runs.
1022
1023    This utility is also available when running the test suite via the
1024    ``--reversetop`` flag.
1025
1026    .. versionadded:: 0.8.1 created a standalone version of the
1027       ``--reversetop`` feature.
1028
1029    """
1030    from sqlalchemy.orm import unitofwork, session, mapper, dependency
1031    from sqlalchemy.util import topological
1032    from sqlalchemy.testing.util import RandomSet
1033    topological.set = unitofwork.set = session.set = mapper.set = \
1034        dependency.set = RandomSet
1035