Exemple #1
0
    def _create_eager_join(self, context, entity, path, adapter, parentmapper):
        # check for join_depth or basic recursion,
        # if the current path was not explicitly stated as
        # a desired "loaderstrategy" (i.e. via query.options())
        if ("loaderstrategy", path) not in context.attributes:
            if self.join_depth:
                if len(path) / 2 > self.join_depth:
                    return
            else:
                if self.mapper.base_mapper in path:
                    return

        if parentmapper is None:
            localparent = entity.mapper
        else:
            localparent = parentmapper

        # whether or not the Query will wrap the selectable in a subquery,
        # and then attach eager load joins to that (i.e., in the case of LIMIT/OFFSET etc.)
        should_nest_selectable = context.query._should_nest_selectable

        if entity in context.eager_joins:
            entity_key, default_towrap = entity, entity.selectable
        elif should_nest_selectable or not context.from_clause or not sql_util.search(
                context.from_clause, entity.selectable):
            # if no from_clause, or a from_clause we can't join to, or a subquery is going to be generated,
            # store eager joins per _MappedEntity; Query._compile_context will
            # add them as separate selectables to the select(), or splice them together
            # after the subquery is generated
            entity_key, default_towrap = entity, entity.selectable
        else:
            # otherwise, create a single eager join from the from clause.
            # Query._compile_context will adapt as needed and append to the
            # FROM clause of the select().
            entity_key, default_towrap = None, context.from_clause

        towrap = context.eager_joins.setdefault(entity_key, default_towrap)

        # create AliasedClauses object to build up the eager query.
        clauses = mapperutil.ORMAdapter(
            mapperutil.AliasedClass(self.mapper),
            equivalents=self.mapper._equivalent_columns)

        join_to_left = False
        if adapter:
            if getattr(adapter, 'aliased_class', None):
                onclause = getattr(adapter.aliased_class, self.key,
                                   self.parent_property)
            else:
                onclause = getattr(
                    mapperutil.AliasedClass(self.parent, adapter.selectable),
                    self.key, self.parent_property)

            if onclause is self.parent_property:
                # TODO: this is a temporary hack to account for polymorphic eager loads where
                # the eagerload is referencing via of_type().
                join_to_left = True
        else:
            onclause = self.parent_property

        context.eager_joins[entity_key] = eagerjoin = mapperutil.outerjoin(
            towrap, clauses.aliased_class, onclause, join_to_left=join_to_left)

        # send a hint to the Query as to where it may "splice" this join
        eagerjoin.stop_on = entity.selectable

        if not self.parent_property.secondary and context.query._should_nest_selectable and not parentmapper:
            # for parentclause that is the non-eager end of the join,
            # ensure all the parent cols in the primaryjoin are actually in the
            # columns clause (i.e. are not deferred), so that aliasing applied by the Query propagates
            # those columns outward.  This has the effect of "undefering" those columns.
            for col in sql_util.find_columns(self.parent_property.primaryjoin):
                if localparent.mapped_table.c.contains_column(col):
                    if adapter:
                        col = adapter.columns[col]
                    context.primary_columns.append(col)

        if self.parent_property.order_by:
            context.eager_order_by += eagerjoin._target_adapter.copy_and_process(
                util.to_list(self.parent_property.order_by))

        return clauses
Exemple #2
0
    def setup_query(self, context, entity, path, reduced_path, adapter, \
                                column_collection=None, parentmapper=None,
                                allow_innerjoin=True,
                                **kwargs):
        """Add a left outer join to the statement thats being constructed."""

        if not context.query._enable_eagerloads:
            return

        path = path + (self.key, )
        reduced_path = reduced_path + (self.key, )

        # check for user-defined eager alias
        if ("user_defined_eager_row_processor", reduced_path) in\
                context.attributes:
            clauses = context.attributes[("user_defined_eager_row_processor",
                                          reduced_path)]

            adapter = entity._get_entity_clauses(context.query, context)
            if adapter and clauses:
                context.attributes[(
                    "user_defined_eager_row_processor",
                    reduced_path)] = clauses = clauses.wrap(adapter)
            elif adapter:
                context.attributes[("user_defined_eager_row_processor",
                                    reduced_path)] = clauses = adapter

            add_to_collection = context.primary_columns

        else:
            # check for join_depth or basic recursion,
            # if the current path was not explicitly stated as
            # a desired "loaderstrategy" (i.e. via query.options())
            if ("loaderstrategy", reduced_path) not in context.attributes:
                if self.join_depth:
                    if len(path) / 2 > self.join_depth:
                        return
                else:
                    if self.mapper.base_mapper in reduced_path:
                        return

            clauses = mapperutil.ORMAdapter(
                mapperutil.AliasedClass(self.mapper),
                equivalents=self.mapper._equivalent_columns,
                adapt_required=True)

            if self.parent_property.direction != interfaces.MANYTOONE:
                context.multi_row_eager_loaders = True

            innerjoin = allow_innerjoin and context.attributes.get(
                ("eager_join_type", path), self.parent_property.innerjoin)
            if not innerjoin:
                # if this is an outer join, all eager joins from
                # here must also be outer joins
                allow_innerjoin = False

            context.create_eager_joins.append(
                (self._create_eager_join, context, entity, path, adapter,
                 parentmapper, clauses, innerjoin))

            add_to_collection = context.secondary_columns
            context.attributes[("eager_row_processor", reduced_path)] = clauses

        path += (self.mapper, )
        reduced_path += (self.mapper.base_mapper, )

        for value in self.mapper._polymorphic_properties:
            value.setup(context,
                        entity,
                        path,
                        reduced_path,
                        clauses,
                        parentmapper=self.mapper,
                        column_collection=add_to_collection,
                        allow_innerjoin=allow_innerjoin)