diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-02-28 18:51:39 -0500 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-02-28 18:51:39 -0500 |
| commit | 149bcd108d9ab7b1a49ad728f72af1d3df7a7ecc (patch) | |
| tree | 3ad732068bd35d90fd7ae1f0d14210048970464b | |
| parent | 0a629e73297905462d4072f330d2389f5fc6d839 (diff) | |
| download | sqlalchemy-149bcd108d9ab7b1a49ad728f72af1d3df7a7ecc.tar.gz | |
- OK this version not working for polymorphic or bundles yet,
but for large loads it ties master, for many short loads it
is slightly faster. if the code can be made clearer then
it's a win
| -rw-r--r-- | lib/sqlalchemy/orm/loading.py | 63 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/query.py | 71 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/strategies.py | 6 | ||||
| -rw-r--r-- | test/orm/test_loading.py | 2 |
4 files changed, 97 insertions, 45 deletions
diff --git a/lib/sqlalchemy/orm/loading.py b/lib/sqlalchemy/orm/loading.py index c4099a743..33b916fd4 100644 --- a/lib/sqlalchemy/orm/loading.py +++ b/lib/sqlalchemy/orm/loading.py @@ -42,12 +42,22 @@ def instances(query, cursor, context): def filter_fn(row): return tuple(fn(x) for x, fn in zip(row, filter_fns)) - if context._predefined_statement: - for query_entity in query._entities: - query_entity.setup_context(query, context, cursor) - try: - (labels, process) = list(zip(*context.loaders)) + if context._predefined_statement: + labels, process = list(zip( + *[ + entity.create_row_processor(context, cursor, None) + for entity in query._entities + ] + )) + else: + labels, process = list(zip( + *[ + entity.create_row_processor(context, cursor, loader) + for entity, loader + in zip(query._entities, context.loaders) + ] + )) if not single_entity: # TODO: this should be in context, so it can also be cached. @@ -219,8 +229,9 @@ def load_on_ident(query, key, def _instance_processor( - mapper, props_toload, context, result, column_collection, + mapper, context, column_collection, query_entity, path, adapter, + props_toload=None, result=None, only_load_props=None, refresh_state=None, polymorphic_discriminator=None, _polymorphic_from=None): @@ -231,7 +242,7 @@ def _instance_processor( populators = collections.defaultdict(list) - if result is None: + if props_toload: for prop in props_toload: prop.setup( @@ -252,15 +263,6 @@ def _instance_processor( identity_class = mapper._identity_class - if result is not None: - props = mapper._props.values() - if only_load_props is not None: - props = (p for p in props if p.key in only_load_props) - - for prop in props: - prop.create_row_processor( - result, context, path, mapper, adapter, populators) - propagate_options = context.propagate_options if propagate_options: load_path = context.query._current_path + path \ @@ -308,7 +310,7 @@ def _instance_processor( # session, or we have to create a new one identitykey = ( identity_class, - tuple(row[col] for col in pk_cols) + tuple([row[col] for col in pk_cols]) ) instance = session_identity_map.get(identitykey) @@ -400,6 +402,31 @@ def _instance_processor( return instance + if result is not None: + props = mapper._props.values() + if only_load_props is not None: + props = (p for p in props if p.key in only_load_props) + + for prop in props: + prop.create_row_processor( + result, + context, path, mapper, adapter, populators) + return _instance + + def setup_result(result): + if context.outer_adapter: + populators["quick"] = [ + (key, result._getter(context.outer_adapter.columns[col])) + for key, col in populators["quick"] + ] + else: + populators["quick"] = [ + (key, result._getter(col)) for key, col in populators["quick"] + ] + return _instance + + return setup_result + if load_is_polymorphic and not _polymorphic_from and not refresh_state: # if we are doing polymorphic, dispatch to a different _instance() # method specific to the subclass mapper @@ -407,7 +434,7 @@ def _instance_processor( _instance, context, mapper, query_entity, path, polymorphic_discriminator, adapter) - return _instance + #return _instance def _populate_full( diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 8988cac1b..01fd26827 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -3261,22 +3261,21 @@ class _MapperEntity(_QueryEntity): return ret - def setup_context(self, query, context, result=None): + def setup_context(self, query, context): adapter = self._get_entity_clauses(query, context) - if result is None: - context.froms += (self.selectable,) + context.froms += (self.selectable,) - if context.order_by is False and self.mapper.order_by: - context.order_by = self.mapper.order_by + if context.order_by is False and self.mapper.order_by: + context.order_by = self.mapper.order_by - # apply adaptation to the mapper's order_by if needed. - if adapter: - context.order_by = adapter.adapt_list( - util.to_list( - context.order_by - ) + # apply adaptation to the mapper's order_by if needed. + if adapter: + context.order_by = adapter.adapt_list( + util.to_list( + context.order_by ) + ) if self._with_polymorphic: poly_properties = self.mapper._iterate_polymorphic_properties( @@ -3298,19 +3297,45 @@ class _MapperEntity(_QueryEntity): _instance = loading._instance_processor( self.mapper, - props_toload, context, - result, context.primary_columns, self, self.path, adapter, + props_toload=props_toload, + only_load_props=only_load_props, + refresh_state=refresh_state, + polymorphic_discriminator=self._polymorphic_discriminator + ) + + context.loaders.append(_instance) + + def create_row_processor(self, context, result, loader): + if loader is not None: + return self._label_name, loader(result) + + adapter = self._get_entity_clauses(context.query, context) + + if context.query._primary_entity is self: + only_load_props = context.query._only_load_props + refresh_state = context.refresh_state + else: + only_load_props = refresh_state = None + + _instance = loading._instance_processor( + self.mapper, + context, + context.primary_columns, + self, + self.path, + adapter, + result=result, only_load_props=only_load_props, refresh_state=refresh_state, polymorphic_discriminator=self._polymorphic_discriminator ) - context.loaders.append((self._label_name, _instance)) + return self._label_name, _instance def __str__(self): return str(self.mapper) @@ -3486,12 +3511,12 @@ class _BundleEntity(_QueryEntity): for ent in self._entities: ent.setup_entity(ext_info, aliased_adapter) - def setup_context(self, query, context, result=None, loaders=None): + def setup_context(self, query, context, loaders=None): our_loaders = [] for ent in self._entities: ent.setup_context( - query, context, result=result, loaders=our_loaders) + query, context, loaders=our_loaders) labels, procs = zip( *our_loaders @@ -3635,17 +3660,21 @@ class _ColumnEntity(_QueryEntity): def _resolve_expr_against_query_aliases(self, query, expr, context): return query._adapt_clause(expr, False, True) - def setup_context(self, query, context, result=None, loaders=None): + def setup_context(self, query, context, loaders=None): column = self._resolve_expr_against_query_aliases( query, self.column, context) context.froms += tuple(self.froms) context.primary_columns.append(column) + context.loaders.append(column) - getter = operator.itemgetter(column) - if loaders is not None: - loaders.append((self._label_name, getter)) + def create_row_processor(self, context, result, loader): + if loader is None: + column = self._resolve_expr_against_query_aliases( + context.query, self.column, context) else: - context.loaders.append((self._label_name, getter)) + column = loader + getter = result._getter(column) + return self._label_name, getter def __str__(self): return str(self.column) diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 41f678955..a3885d3e0 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -149,12 +149,8 @@ class ColumnLoader(LoaderStrategy): # should be added here so that we need not bother # querying out every column. - # TODO: wrong. we are again fetching by column and we are - # not applying context.outer_adapter if we're in the primary - # collection. Also, context.outer_adapter - # isn't even set up yet. So again, we can't do this. populators["quick"].append( - (self.key, operator.itemgetter(self.columns[0])) + (self.key, self.columns[0]) ) def create_row_processor( diff --git a/test/orm/test_loading.py b/test/orm/test_loading.py index bfc1ad0c8..627a181ad 100644 --- a/test/orm/test_loading.py +++ b/test/orm/test_loading.py @@ -36,7 +36,7 @@ class InstancesTest(_fixtures.FixtureTest): ) assert cursor.close.called, "Cursor wasn't closed" - def test_query_load_entity(self): + def test_query_load_entity_one(self): User = self.classes.User s = Session() |
