[Sqlalchemy-commits] [4367] sqlalchemy/branches/with_polymorphic/lib/sqlalchemy: - fixed visitor t
Brought to you by:
zzzeek
From: <co...@sq...> - 2008-03-28 21:13:59
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><meta http-equiv="content-type" content="text/html; charset=utf-8" /><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li, #header, #footer { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[4367] sqlalchemy/branches/with_polymorphic/lib/sqlalchemy: - fixed visitor traversal to be depth first, uses deque when available for efficiency</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>4367</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2008-03-28 17:13:53 -0400 (Fri, 28 Mar 2008)</dd> </dl> <h3>Log Message</h3> <pre>- fixed visitor traversal to be depth first, uses deque when available for efficiency - moved awareness of 'polymorphic_fetch' attribute out of Query</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybrancheswith_polymorphiclibsqlalchemyormmapperpy">sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemybrancheswith_polymorphiclibsqlalchemyormquerypy">sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/orm/query.py</a></li> <li><a href="#sqlalchemybrancheswith_polymorphiclibsqlalchemysqlutilpy">sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/sql/util.py</a></li> <li><a href="#sqlalchemybrancheswith_polymorphiclibsqlalchemysqlvisitorspy">sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/sql/visitors.py</a></li> <li><a href="#sqlalchemybrancheswith_polymorphiclibsqlalchemyutilpy">sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/util.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybrancheswith_polymorphiclibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/orm/mapper.py (4366 => 4367)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/orm/mapper.py 2008-03-28 15:55:26 UTC (rev 4366) +++ sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/orm/mapper.py 2008-03-28 21:13:53 UTC (rev 4367) </span><span class="lines">@@ -115,6 +115,7 @@ </span><span class="cx"> if not issubclass(class_, object): </span><span class="cx"> raise exceptions.ArgumentError("Class '%s' is not a new-style class" % class_.__name__) </span><span class="cx"> </span><ins>+ self.select_table = select_table </ins><span class="cx"> if select_table: </span><span class="cx"> self.with_polymorphic = ('*', select_table) </span><span class="cx"> else: </span><span class="lines">@@ -1304,7 +1305,7 @@ </span><span class="cx"> for (c, m) in prop.cascade_iterator(type, state, recursive, halt_on=halt_on): </span><span class="cx"> yield (c, m) </span><span class="cx"> </span><del>- def _instance(self, context, row, result=None, skip_polymorphic=False, extension=None, only_load_props=None, refresh_instance=None): </del><ins>+ def _instance(self, context, row, result=None, polymorphic_from=None, extension=None, only_load_props=None, refresh_instance=None): </ins><span class="cx"> if not extension: </span><span class="cx"> extension = self.extension </span><span class="cx"> </span><span class="lines">@@ -1313,14 +1314,22 @@ </span><span class="cx"> if ret is not EXT_CONTINUE: </span><span class="cx"> row = ret </span><span class="cx"> </span><del>- if not refresh_instance and not skip_polymorphic and self.polymorphic_on: </del><ins>+ if polymorphic_from: + # if we are called from a base mapper doing a polymorphic load, figure out what tables, + # if any, will need to be "post-fetched" based on the tables present in the row, + # or from the options set up on the query + if ('polymorphic_fetch', self) not in context.attributes: + if self in context.query._with_polymorphic: + context.attributes[('polymorphic_fetch', self)] = (polymorphic_from, []) + else: + context.attributes[('polymorphic_fetch', self)] = (polymorphic_from, [t for t in self.tables if t not in polymorphic_from.tables]) + + elif not refresh_instance and self.polymorphic_on: </ins><span class="cx"> discriminator = row[self.polymorphic_on] </span><span class="cx"> if discriminator: </span><span class="cx"> mapper = self.polymorphic_map[discriminator] </span><span class="cx"> if mapper is not self: </span><del>- if ('polymorphic_fetch', mapper) not in context.attributes: - context.attributes[('polymorphic_fetch', mapper)] = (self, [t for t in mapper.tables if t not in self.tables]) - return mapper._instance(context, row, result=result, skip_polymorphic=True) </del><ins>+ return mapper._instance(context, row, result=result, polymorphic_from=self) </ins><span class="cx"> </span><span class="cx"> # determine identity key </span><span class="cx"> if refresh_instance: </span></span></pre></div> <a id="sqlalchemybrancheswith_polymorphiclibsqlalchemyormquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/orm/query.py (4366 => 4367)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/orm/query.py 2008-03-28 15:55:26 UTC (rev 4366) +++ sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/orm/query.py 2008-03-28 21:13:53 UTC (rev 4367) </span><span class="lines">@@ -906,11 +906,6 @@ </span><span class="cx"> </span><span class="cx"> context.runid = _new_runid() </span><span class="cx"> </span><del>- # for with_polymorphic, instruct descendant mappers that they - # don't need to post-fetch anything - for m in self._with_polymorphic: - context.attributes[('polymorphic_fetch', m)] = (self.mapper, []) - </del><span class="cx"> entities = self._entities + [_QueryEntity.legacy_guess_type(mc) for mc in mappers_or_columns] </span><span class="cx"> should_unique = isinstance(entities[0], _PrimaryMapperEntity) and len(entities) == 1 </span><span class="cx"> process = [query_entity.row_processor(self, context) for query_entity in entities] </span><span class="lines">@@ -1082,34 +1077,31 @@ </span><span class="cx"> # eager loaders are present, and the SELECT has limiting criterion </span><span class="cx"> # produce a "wrapped" selectable. </span><span class="cx"> </span><ins>+ order_by = [expression._literal_as_text(o) for o in util.to_list(order_by) or []] + if adapter: + context.primary_columns = adapter.adapt_list(context.primary_columns) + order_by = adapter.adapt_list(order_by) + </ins><span class="cx"> # locate all embedded Column clauses so they can be added to the </span><span class="cx"> # "inner" select statement where they'll be available to the enclosing </span><span class="cx"> # statement's "order by" </span><del>- cf = util.Set() - if order_by: - order_by = [expression._literal_as_text(o) for o in util.to_list(order_by) or []] - for o in order_by: - cf.update(sql_util.find_columns(o)) </del><ins>+ # TODO: this likely doesn't work with very involved ORDER BY expressions, + # such as those including subqueries + order_by_col_expr = list(chain(*[sql_util.find_columns(o) for o in order_by])) + + inner = sql.select(context.primary_columns + order_by_col_expr, context.whereclause, from_obj=context.from_clause, use_labels=True, correlate=False, order_by=util.to_list(order_by), **self._select_args).alias() + local_adapter = sql_util.ClauseAdapter(inner) </ins><span class="cx"> </span><del>- if adapter: - context.primary_columns = adapter.adapt_list(context.primary_columns) - cf = adapter.adapt_list(cf) - order_by = adapter.adapt_list(order_by) </del><ins>+ context.row_adapter = mapperutil.create_row_adapter(inner, equivalent_columns=self.mapper._equivalent_columns) </ins><span class="cx"> </span><del>- s2 = sql.select(context.primary_columns + list(cf), context.whereclause, from_obj=context.from_clause, use_labels=True, correlate=False, order_by=util.to_list(order_by), **self._select_args) </del><ins>+ statement = sql.select([inner] + context.secondary_columns, for_update=for_update, use_labels=True) </ins><span class="cx"> </span><del>- s3 = s2.alias() - - context.row_adapter = mapperutil.create_row_adapter(s3, equivalent_columns=self.mapper._equivalent_columns) - - statement = sql.select([s3] + context.secondary_columns, for_update=for_update, use_labels=True) - </del><span class="cx"> if context.eager_joins: </span><del>- eager_joins = sql_util.ClauseAdapter(s3).traverse(context.eager_joins) </del><ins>+ eager_joins = local_adapter.traverse(context.eager_joins) </ins><span class="cx"> statement.append_from(eager_joins, _copy_collection=False) </span><span class="cx"> </span><span class="cx"> if order_by: </span><del>- statement.append_order_by(*sql_util.ClauseAdapter(s3).copy_and_process(order_by)) </del><ins>+ statement.append_order_by(*local_adapter.copy_and_process(order_by)) </ins><span class="cx"> </span><span class="cx"> statement.append_order_by(*context.eager_order_by) </span><span class="cx"> else: </span><span class="lines">@@ -1121,7 +1113,6 @@ </span><span class="cx"> order_by = adapter.adapt_list(order_by) </span><span class="cx"> </span><span class="cx"> if self._distinct: </span><del>- </del><span class="cx"> if self._distinct and order_by: </span><span class="cx"> cf = util.Set() </span><span class="cx"> for o in order_by: </span></span></pre></div> <a id="sqlalchemybrancheswith_polymorphiclibsqlalchemysqlutilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/sql/util.py (4366 => 4367)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/sql/util.py 2008-03-28 15:55:26 UTC (rev 4366) +++ sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/sql/util.py 2008-03-28 21:13:53 UTC (rev 4367) </span><span class="lines">@@ -121,7 +121,7 @@ </span><span class="cx"> </span><span class="cx"> if equivalent_columns is None: </span><span class="cx"> equivalent_columns = {} </span><del>- assert isinstance(equivalent_columns, dict) </del><ins>+ </ins><span class="cx"> def locate_col(col): </span><span class="cx"> c = from_.corresponding_column(col) </span><span class="cx"> if c: </span></span></pre></div> <a id="sqlalchemybrancheswith_polymorphiclibsqlalchemysqlvisitorspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/sql/visitors.py (4366 => 4367)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/sql/visitors.py 2008-03-28 15:55:26 UTC (rev 4366) +++ sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/sql/visitors.py 2008-03-28 21:13:53 UTC (rev 4367) </span><span class="lines">@@ -40,14 +40,14 @@ </span><span class="cx"> """traverse the given expression structure, returning an iterator of all elements.""" </span><span class="cx"> </span><span class="cx"> stack = [obj] </span><del>- traversal = [] </del><ins>+ traversal = util.deque() </ins><span class="cx"> while stack: </span><span class="cx"> t = stack.pop() </span><del>- yield t - traversal.insert(0, t) </del><ins>+ traversal.appendleft(t) </ins><span class="cx"> for c in t.get_children(**self.__traverse_options__): </span><span class="cx"> stack.append(c) </span><del>- </del><ins>+ return iter(traversal) + </ins><span class="cx"> def traverse(self, obj, clone=False): </span><span class="cx"> """traverse and visit the given expression structure. </span><span class="cx"> </span><span class="lines">@@ -126,10 +126,10 @@ </span><span class="cx"> if e not in stop_on: </span><span class="cx"> self._cloned_traversal_impl(e, stop_on, cloned) </span><span class="cx"> return elem </span><del>- </del><ins>+ </ins><span class="cx"> def _non_cloned_traversal(self, obj): </span><span class="cx"> """a non-recursive, non-cloning traversal.""" </span><del>- </del><ins>+ </ins><span class="cx"> for target in self.iterate(obj): </span><span class="cx"> self.traverse_single(target) </span><span class="cx"> return obj </span></span></pre></div> <a id="sqlalchemybrancheswith_polymorphiclibsqlalchemyutilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/util.py (4366 => 4367)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/util.py 2008-03-28 15:55:26 UTC (rev 4366) +++ sqlalchemy/branches/with_polymorphic/lib/sqlalchemy/util.py 2008-03-28 21:13:53 UTC (rev 4367) </span><span class="lines">@@ -163,6 +163,23 @@ </span><span class="cx"> return 'defaultdict(%s, %s)' % (self.default_factory, </span><span class="cx"> dict.__repr__(self)) </span><span class="cx"> </span><ins>+try: + from collections import deque +except ImportError: + class deque(list): + def appendleft(self, x): + self.insert(0, x) + + def extendleft(self, iterable): + self[0:0] = list(iterable) + + def popleft(self): + return self.pop(0) + + def rotate(self, n): + for i in xrange(n): + self.appendleft(self.pop()) + </ins><span class="cx"> def to_list(x, default=None): </span><span class="cx"> if x is None: </span><span class="cx"> return default </span></span></pre> </div> </div> </body> </html> |