Esempio n. 1
0
    def process_query_property(self, query, paths, mappers):
        if self.chained:
            for path in paths[0:-1]:
                (root_mapper, propname) = path[-2:]
                prop = root_mapper._props[propname]
                adapter = query._polymorphic_adapters.get(prop.mapper, None)
                query._attributes.setdefault(
                            ("user_defined_eager_row_processor", 
                            interfaces._reduce_path(path)), adapter)

        if self.alias is not None:
            if isinstance(self.alias, basestring):
                (root_mapper, propname) = paths[-1][-2:]
                prop = root_mapper._props[propname]
                self.alias = prop.target.alias(self.alias)
            query._attributes[
                        ("user_defined_eager_row_processor", 
                        interfaces._reduce_path(paths[-1]))
                        ] = sql_util.ColumnAdapter(self.alias)
        else:
            (root_mapper, propname) = paths[-1][-2:]
            prop = root_mapper._props[propname]
            adapter = query._polymorphic_adapters.get(prop.mapper, None)
            query._attributes[
                        ("user_defined_eager_row_processor", 
                        interfaces._reduce_path(paths[-1]))] = adapter
Esempio n. 2
0
    def process_query_property(self, query, paths, mappers):
        if self.chained:
            for path in paths[0:-1]:
                (root_mapper, propname) = path[-2:]
                prop = root_mapper._props[propname]
                adapter = query._polymorphic_adapters.get(prop.mapper, None)
                query._attributes.setdefault(
                    ("user_defined_eager_row_processor",
                     interfaces._reduce_path(path)), adapter)

        if self.alias is not None:
            if isinstance(self.alias, str):
                (root_mapper, propname) = paths[-1][-2:]
                prop = root_mapper._props[propname]
                self.alias = prop.target.alias(self.alias)
            query._attributes[("user_defined_eager_row_processor",
                               interfaces._reduce_path(
                                   paths[-1]))] = sql_util.ColumnAdapter(
                                       self.alias)
        else:
            (root_mapper, propname) = paths[-1][-2:]
            prop = root_mapper._props[propname]
            adapter = query._polymorphic_adapters.get(prop.mapper, None)
            query._attributes[("user_defined_eager_row_processor",
                               interfaces._reduce_path(paths[-1]))] = adapter
Esempio n. 3
0
 def process_query_property(self, query, paths, mappers):
     if self.alias is not None:
         if isinstance(self.alias, basestring):
             mapper = mappers[-1]
             (root_mapper, propname) = paths[-1][-2:]
             prop = mapper.get_property(propname, resolve_synonyms=True)
             self.alias = prop.target.alias(self.alias)
         query._attributes[
             ("user_defined_eager_row_processor", interfaces._reduce_path(paths[-1]))
         ] = sql_util.ColumnAdapter(self.alias)
     else:
         (root_mapper, propname) = paths[-1][-2:]
         mapper = mappers[-1]
         prop = mapper.get_property(propname, resolve_synonyms=True)
         adapter = query._polymorphic_adapters.get(prop.mapper, None)
         query._attributes[("user_defined_eager_row_processor", interfaces._reduce_path(paths[-1]))] = adapter
Esempio n. 4
0
    def _create_eager_adapter(self, context, row, adapter, path):
        reduced_path = interfaces._reduce_path(path)
        if ("user_defined_eager_row_processor", reduced_path) in \
                                                    context.attributes:
            decorator = context.attributes[
                            ("user_defined_eager_row_processor",
                            reduced_path)]
            # user defined eagerloads are part of the "primary" 
            # portion of the load.
            # the adapters applied to the Query should be honored.
            if context.adapter and decorator:
                decorator = decorator.wrap(context.adapter)
            elif context.adapter:
                decorator = context.adapter
        elif ("eager_row_processor", reduced_path) in context.attributes:
            decorator = context.attributes[
                            ("eager_row_processor", reduced_path)]
        else:
            return False

        try:
            identity_key = self.mapper.identity_key_from_row(row, decorator)
            return decorator
        except KeyError, k:
            # no identity key - dont return a row 
            # processor, will cause a degrade to lazy
            return False
Esempio n. 5
0
    def _create_eager_adapter(self, context, row, adapter, path):
        reduced_path = interfaces._reduce_path(path)
        if ("user_defined_eager_row_processor", reduced_path) in \
                                                    context.attributes:
            decorator = context.attributes[("user_defined_eager_row_processor",
                                            reduced_path)]
            # user defined eagerloads are part of the "primary"
            # portion of the load.
            # the adapters applied to the Query should be honored.
            if context.adapter and decorator:
                decorator = decorator.wrap(context.adapter)
            elif context.adapter:
                decorator = context.adapter
        elif ("eager_row_processor", reduced_path) in context.attributes:
            decorator = context.attributes[("eager_row_processor",
                                            reduced_path)]
        else:
            return False

        try:
            identity_key = self.mapper.identity_key_from_row(row, decorator)
            return decorator
        except KeyError, k:
            # no identity key - dont return a row
            # processor, will cause a degrade to lazy
            return False
Esempio n. 6
0
    def setup_query(self, context, entity, path, adapter, \
                                column_collection=None, parentmapper=None, **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 = interfaces._reduce_path(path)
        
        # 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

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

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

        for value in self.mapper._iterate_polymorphic_properties():
            value.setup(
                context, 
                entity, 
                path + (self.mapper,), 
                clauses, 
                parentmapper=self.mapper, 
                column_collection=add_to_collection)
Esempio n. 7
0
 def process_query_property(self, query, paths, mappers):
     if self.alias is not None:
         if isinstance(self.alias, basestring):
             mapper = mappers[-1]
             (root_mapper, propname) = paths[-1][-2:]
             prop = mapper.get_property(propname, resolve_synonyms=True)
             self.alias = prop.target.alias(self.alias)
         query._attributes[("user_defined_eager_row_processor",
                            interfaces._reduce_path(
                                paths[-1]))] = sql_util.ColumnAdapter(
                                    self.alias)
     else:
         (root_mapper, propname) = paths[-1][-2:]
         mapper = mappers[-1]
         prop = mapper.get_property(propname, resolve_synonyms=True)
         adapter = query._polymorphic_adapters.get(prop.mapper, None)
         query._attributes[("user_defined_eager_row_processor",
                            interfaces._reduce_path(paths[-1]))] = adapter
Esempio n. 8
0
    def create_row_processor(self, context, path, mapper, row, adapter):
        path = path + (self.key,)

        path = interfaces._reduce_path(path)
        
        if ('subquery', path) not in context.attributes:
            return None, None
            
        local_cols, remote_cols = self._local_remote_columns(self.parent_property)

        remote_attr = [
                        self.mapper._get_col_to_prop(c).key 
                        for c in remote_cols]
        
        q = context.attributes[('subquery', path)]
        
        collections = dict(
                    (k, [v[0] for v in v]) 
                    for k, v in itertools.groupby(
                        q, 
                        lambda x:x[1:]
                    ))
        
        if adapter:
            local_cols = [adapter.columns[c] for c in local_cols]
        
        if self.uselist:
            def execute(state, dict_, row):
                collection = collections.get(
                    tuple([row[col] for col in local_cols]), 
                    ()
                )
                state.get_impl(self.key).\
                        set_committed_value(state, dict_, collection)
        else:
            def execute(state, dict_, row):
                collection = collections.get(
                    tuple([row[col] for col in local_cols]), 
                    (None,)
                )
                if len(collection) > 1:
                    util.warn(
                        "Multiple rows returned with "
                        "uselist=False for eagerly-loaded attribute '%s' "
                        % self)
                    
                scalar = collection[0]
                state.get_impl(self.key).\
                        set_committed_value(state, dict_, scalar)
            
        return execute, None
Esempio n. 9
0
    def create_row_processor(self, context, path, mapper, row, adapter):
        if not self.parent.class_manager[self.key].impl.supports_population:
            raise sa_exc.InvalidRequestError(
                "'%s' does not support object "
                "population - eager loading cannot be applied." % self)

        path = path + (self.key, )

        path = interfaces._reduce_path(path)

        if ('subquery', path) not in context.attributes:
            return None, None

        local_cols, remote_cols = self._local_remote_columns(
            self.parent_property)

        remote_attr = [
            self.mapper._columntoproperty[c].key for c in remote_cols
        ]

        q = context.attributes[('subquery', path)]

        collections = dict((k, [v[0] for v in v])
                           for k, v in itertools.groupby(q, lambda x: x[1:]))

        if adapter:
            local_cols = [adapter.columns[c] for c in local_cols]

        if self.uselist:

            def execute(state, dict_, row):
                collection = collections.get(
                    tuple([row[col] for col in local_cols]), ())
                state.get_impl(self.key).\
                        set_committed_value(state, dict_, collection)
        else:

            def execute(state, dict_, row):
                collection = collections.get(
                    tuple([row[col] for col in local_cols]), (None, ))
                if len(collection) > 1:
                    util.warn(
                        "Multiple rows returned with "
                        "uselist=False for eagerly-loaded attribute '%s' " %
                        self)

                scalar = collection[0]
                state.get_impl(self.key).\
                        set_committed_value(state, dict_, scalar)

        return execute, None
Esempio n. 10
0
    def create_row_processor(self, context, path, mapper, row, adapter):
        path = path + (self.key, )

        path = interfaces._reduce_path(path)

        if ('subquery', path) not in context.attributes:
            return None, None

        local_cols, remote_cols = self._local_remote_columns(
            self.parent_property)

        remote_attr = [
            self.mapper._get_col_to_prop(c).key for c in remote_cols
        ]

        q = context.attributes[('subquery', path)]

        collections = dict((k, [v[0] for v in v])
                           for k, v in itertools.groupby(q, lambda x: x[1:]))

        if adapter:
            local_cols = [adapter.columns[c] for c in local_cols]

        if self.uselist:

            def execute(state, dict_, row):
                collection = collections.get(
                    tuple([row[col] for col in local_cols]), ())
                state.get_impl(self.key).\
                        set_committed_value(state, dict_, collection)
        else:

            def execute(state, dict_, row):
                collection = collections.get(
                    tuple([row[col] for col in local_cols]), (None, ))
                if len(collection) > 1:
                    util.warn(
                        "Multiple rows returned with "
                        "uselist=False for eagerly-loaded attribute '%s' " %
                        self)

                scalar = collection[0]
                state.get_impl(self.key).\
                        set_committed_value(state, dict_, scalar)

        return execute, None
Esempio n. 11
0
    def create_row_processor(self, context, path, mapper, row, adapter):
        if not self.parent.class_manager[self.key].impl.supports_population:
            raise sa_exc.InvalidRequestError(
                "'%s' does not support object " "population - eager loading cannot be applied." % self
            )

        path = path + (self.key,)

        path = interfaces._reduce_path(path)

        if ("subquery", path) not in context.attributes:
            return None, None

        local_cols, remote_cols = self._local_remote_columns(self.parent_property)

        remote_attr = [self.mapper._columntoproperty[c].key for c in remote_cols]

        q = context.attributes[("subquery", path)]

        collections = dict((k, [v[0] for v in v]) for k, v in itertools.groupby(q, lambda x: x[1:]))

        if adapter:
            local_cols = [adapter.columns[c] for c in local_cols]

        if self.uselist:

            def execute(state, dict_, row):
                collection = collections.get(tuple([row[col] for col in local_cols]), ())
                state.get_impl(self.key).set_committed_value(state, dict_, collection)

        else:

            def execute(state, dict_, row):
                collection = collections.get(tuple([row[col] for col in local_cols]), (None,))
                if len(collection) > 1:
                    util.warn("Multiple rows returned with " "uselist=False for eagerly-loaded attribute '%s' " % self)

                scalar = collection[0]
                state.get_impl(self.key).set_committed_value(state, dict_, scalar)

        return execute, None
Esempio n. 12
0
    def setup_query(self, context, entity, path, adapter, column_collection=None, parentmapper=None, **kwargs):

        if not context.query._enable_eagerloads:
            return

        path = path + (self.key,)

        # build up a path indicating the path from the leftmost
        # entity to the thing we're subquery loading.
        subq_path = context.attributes.get(("subquery_path", None), ())

        subq_path = subq_path + path

        reduced_path = interfaces._reduce_path(path)

        # join-depth / recursion check
        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 interfaces._reduce_path(subq_path):
                    return

        orig_query = context.attributes.get(("orig_query", SubqueryLoader), context.query)

        # determine attributes of the leftmost mapper
        if self.parent.isa(subq_path[0]) and self.key == subq_path[1]:
            leftmost_mapper, leftmost_prop = self.parent, self.parent_property
        else:
            leftmost_mapper, leftmost_prop = subq_path[0], subq_path[0].get_property(subq_path[1])
        leftmost_cols, remote_cols = self._local_remote_columns(leftmost_prop)

        leftmost_attr = [leftmost_mapper._columntoproperty[c].class_attribute for c in leftmost_cols]

        # reformat the original query
        # to look only for significant columns
        q = orig_query._clone()

        # TODO: why does polymporphic etc. require hardcoding
        # into _adapt_col_list ?  Does query.add_columns(...) work
        # with polymorphic loading ?
        q._set_entities(q._adapt_col_list(leftmost_attr))

        # don't need ORDER BY if no limit/offset
        if q._limit is None and q._offset is None:
            q._order_by = None

        # the original query now becomes a subquery
        # which we'll join onto.
        embed_q = q.with_labels().subquery()
        left_alias = mapperutil.AliasedClass(leftmost_mapper, embed_q)

        # q becomes a new query.  basically doing a longhand
        # "from_self()".  (from_self() itself not quite industrial
        # strength enough for all contingencies...but very close)

        q = q.session.query(self.mapper)
        q._attributes = {("orig_query", SubqueryLoader): orig_query, ("subquery_path", None): subq_path}

        # figure out what's being joined.  a.k.a. the fun part
        to_join = [(subq_path[i], subq_path[i + 1]) for i in xrange(0, len(subq_path), 2)]

        if len(to_join) < 2:
            parent_alias = left_alias
        else:
            parent_alias = mapperutil.AliasedClass(self.parent)

        local_cols, remote_cols = self._local_remote_columns(self.parent_property)

        local_attr = [getattr(parent_alias, self.parent._columntoproperty[c].key) for c in local_cols]
        q = q.order_by(*local_attr)
        q = q.add_columns(*local_attr)

        for i, (mapper, key) in enumerate(to_join):

            # we need to use query.join() as opposed to
            # orm.join() here because of the
            # rich behavior it brings when dealing with
            # "with_polymorphic" mappers.  "aliased"
            # and "from_joinpoint" take care of most of
            # the chaining and aliasing for us.

            first = i == 0
            middle = i < len(to_join) - 1
            second_to_last = i == len(to_join) - 2

            if first:
                attr = getattr(left_alias, key)
            else:
                attr = key

            if second_to_last:
                q = q.join((parent_alias, attr), from_joinpoint=True)
            else:
                q = q.join(attr, aliased=middle, from_joinpoint=True)

        # propagate loader options etc. to the new query.
        # these will fire relative to subq_path.
        q = q._with_current_path(subq_path)
        q = q._conditional_options(*orig_query._with_options)

        if self.parent_property.order_by:
            # if there's an ORDER BY, alias it the same
            # way joinedloader does, but we have to pull out
            # the "eagerjoin" from the query.
            # this really only picks up the "secondary" table
            # right now.
            eagerjoin = q._from_obj[0]
            eager_order_by = eagerjoin._target_adapter.copy_and_process(util.to_list(self.parent_property.order_by))
            q = q.order_by(*eager_order_by)

        # add new query to attributes to be picked up
        # by create_row_processor
        context.attributes[("subquery", reduced_path)] = q
Esempio n. 13
0
    def setup_query(self,
                    context,
                    entity,
                    path,
                    reduced_path,
                    adapter,
                    column_collection=None,
                    parentmapper=None,
                    **kwargs):

        if not context.query._enable_eagerloads:
            return

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

        # build up a path indicating the path from the leftmost
        # entity to the thing we're subquery loading.
        subq_path = context.attributes.get(('subquery_path', None), ())

        subq_path = subq_path + path

        # join-depth / recursion check
        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 interfaces._reduce_path(
                        subq_path):
                    return

        orig_query = context.attributes.get(("orig_query", SubqueryLoader),
                                            context.query)

        subq_mapper = mapperutil._class_to_mapper(subq_path[0])

        # determine attributes of the leftmost mapper
        if self.parent.isa(subq_mapper) and self.key == subq_path[1]:
            leftmost_mapper, leftmost_prop = \
                                    self.parent, self.parent_property
        else:
            leftmost_mapper, leftmost_prop = \
                                    subq_mapper, \
                                    subq_mapper._props[subq_path[1]]
        leftmost_cols, remote_cols = self._local_remote_columns(leftmost_prop)

        leftmost_attr = [
            leftmost_mapper._columntoproperty[c].class_attribute
            for c in leftmost_cols
        ]

        # reformat the original query
        # to look only for significant columns
        q = orig_query._clone()

        # TODO: why does polymporphic etc. require hardcoding
        # into _adapt_col_list ?  Does query.add_columns(...) work
        # with polymorphic loading ?
        q._set_entities(q._adapt_col_list(leftmost_attr))

        # don't need ORDER BY if no limit/offset
        if q._limit is None and q._offset is None:
            q._order_by = None

        # the original query now becomes a subquery
        # which we'll join onto.
        embed_q = q.with_labels().subquery()
        left_alias = mapperutil.AliasedClass(leftmost_mapper, embed_q)

        # q becomes a new query.  basically doing a longhand
        # "from_self()".  (from_self() itself not quite industrial
        # strength enough for all contingencies...but very close)

        q = q.session.query(self.mapper)
        q._attributes = {
            ("orig_query", SubqueryLoader): orig_query,
            ('subquery_path', None): subq_path
        }
        q = q._enable_single_crit(False)

        # figure out what's being joined.  a.k.a. the fun part
        to_join = [(subq_path[i], subq_path[i + 1])
                   for i in range(0, len(subq_path), 2)]

        # determine the immediate parent class we are joining from,
        # which needs to be aliased.

        if len(to_join) < 2:
            # in the case of a one level eager load, this is the
            # leftmost "left_alias".
            parent_alias = left_alias
        elif subq_path[-2].isa(self.parent):
            # In the case of multiple levels, retrieve
            # it from subq_path[-2]. This is the same as self.parent
            # in the vast majority of cases, and [ticket:2014]
            # illustrates a case where sub_path[-2] is a subclass
            # of self.parent
            parent_alias = mapperutil.AliasedClass(subq_path[-2])
        else:
            # if of_type() were used leading to this relationship,
            # self.parent is more specific than subq_path[-2]
            parent_alias = mapperutil.AliasedClass(self.parent)

        local_cols, remote_cols = \
                        self._local_remote_columns(self.parent_property)

        local_attr = [
            getattr(parent_alias, self.parent._columntoproperty[c].key)
            for c in local_cols
        ]
        q = q.order_by(*local_attr)
        q = q.add_columns(*local_attr)

        for i, (mapper, key) in enumerate(to_join):

            # we need to use query.join() as opposed to
            # orm.join() here because of the
            # rich behavior it brings when dealing with
            # "with_polymorphic" mappers.  "aliased"
            # and "from_joinpoint" take care of most of
            # the chaining and aliasing for us.

            first = i == 0
            middle = i < len(to_join) - 1
            second_to_last = i == len(to_join) - 2

            if first:
                attr = getattr(left_alias, key)
            else:
                attr = key

            if second_to_last:
                q = q.join(parent_alias, attr, from_joinpoint=True)
            else:
                q = q.join(attr, aliased=middle, from_joinpoint=True)

        # propagate loader options etc. to the new query.
        # these will fire relative to subq_path.
        q = q._with_current_path(subq_path)
        q = q._conditional_options(*orig_query._with_options)

        if self.parent_property.order_by:
            # if there's an ORDER BY, alias it the same
            # way joinedloader does, but we have to pull out
            # the "eagerjoin" from the query.
            # this really only picks up the "secondary" table
            # right now.
            eagerjoin = q._from_obj[0]
            eager_order_by = \
                            eagerjoin._target_adapter.\
                                copy_and_process(
                                    util.to_list(
                                        self.parent_property.order_by
                                    )
                                )
            q = q.order_by(*eager_order_by)

        # add new query to attributes to be picked up
        # by create_row_processor
        context.attributes[('subquery', reduced_path)] = q
Esempio n. 14
0
    def setup_query(self, context, entity, path, adapter, \
                                column_collection=None, parentmapper=None,
                                **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 = interfaces._reduce_path(path)

        # 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

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

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

        for value in self.mapper._iterate_polymorphic_properties():
            value.setup(context,
                        entity,
                        path + (self.mapper, ),
                        clauses,
                        parentmapper=self.mapper,
                        column_collection=add_to_collection)