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
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
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
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
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
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)
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
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
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
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
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
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
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
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)