[Sqlalchemy-commits] [2665] sqlalchemy/branches/rel_0_4: - merged "find the equivalent columns" log
Brought to you by:
zzzeek
From: <co...@sq...> - 2007-05-26 23:22:03
|
<!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>[2665] sqlalchemy/branches/rel_0_4: - merged "find the equivalent columns" logic together (although both methodologies are needed....)</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>2665</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2007-05-26 19:21:56 -0400 (Sat, 26 May 2007)</dd> </dl> <h3>Log Message</h3> <pre>- merged "find the equivalent columns" logic together (although both methodologies are needed....) - uniqueappender has to use a set to handle staggered joins</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesrel_0_4libsqlalchemyenginebasepy">sqlalchemy/branches/rel_0_4/lib/sqlalchemy/engine/base.py</a></li> <li><a href="#sqlalchemybranchesrel_0_4libsqlalchemyormmapperpy">sqlalchemy/branches/rel_0_4/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemybranchesrel_0_4libsqlalchemyormpropertiespy">sqlalchemy/branches/rel_0_4/lib/sqlalchemy/orm/properties.py</a></li> <li><a href="#sqlalchemybranchesrel_0_4libsqlalchemysql_utilpy">sqlalchemy/branches/rel_0_4/lib/sqlalchemy/sql_util.py</a></li> <li><a href="#sqlalchemybranchesrel_0_4libsqlalchemyutilpy">sqlalchemy/branches/rel_0_4/lib/sqlalchemy/util.py</a></li> <li><a href="#sqlalchemybranchesrel_0_4testormeagertest2py">sqlalchemy/branches/rel_0_4/test/orm/eagertest2.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesrel_0_4libsqlalchemyenginebasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/rel_0_4/lib/sqlalchemy/engine/base.py (2664 => 2665)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/rel_0_4/lib/sqlalchemy/engine/base.py 2007-05-26 22:21:31 UTC (rev 2664) +++ sqlalchemy/branches/rel_0_4/lib/sqlalchemy/engine/base.py 2007-05-26 23:21:56 UTC (rev 2665) </span><span class="lines">@@ -888,6 +888,9 @@ </span><span class="cx"> self.__keys.append(colname) </span><span class="cx"> self.__props[i] = rec </span><span class="cx"> </span><ins>+ if self.__echo: + self.context.engine.logger.debug("Cls " + repr(tuple([x[0] for x in metadata]))) + </ins><span class="cx"> def close(self): </span><span class="cx"> """Close this ResultProxy, and the underlying DBAPI cursor corresponding to the execution. </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesrel_0_4libsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/rel_0_4/lib/sqlalchemy/orm/mapper.py (2664 => 2665)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/rel_0_4/lib/sqlalchemy/orm/mapper.py 2007-05-26 22:21:31 UTC (rev 2664) +++ sqlalchemy/branches/rel_0_4/lib/sqlalchemy/orm/mapper.py 2007-05-26 23:21:56 UTC (rev 2665) </span><span class="lines">@@ -523,20 +523,14 @@ </span><span class="cx"> # into one column, where "equivalent" means that one column references the other via foreign key, or </span><span class="cx"> # multiple columns that all reference a common parent column. it will also resolve the column </span><span class="cx"> # against the "mapped_table" of this mapper. </span><ins>+ equivalent_columns = self._get_equivalent_columns() + </ins><span class="cx"> primary_key = sql.ColumnCollection() </span><del>- # TODO: wrong ! this is a duplicate / slightly different approach to - # _get_inherited_column_equivalents(). pick one approach and stick with it ! - equivs = {} </del><ins>+ </ins><span class="cx"> for col in (self.primary_key_argument or self.pks_by_table[self.mapped_table]): </span><del>- if not len(col.foreign_keys): - equivs.setdefault(col, util.Set()).add(col) - else: - for fk in col.foreign_keys: - equivs.setdefault(fk.column, util.Set()).add(col) - for col in equivs: </del><span class="cx"> c = self.mapped_table.corresponding_column(col, raiseerr=False) </span><span class="cx"> if c is None: </span><del>- for cc in equivs[col]: </del><ins>+ for cc in equivalent_columns[col]: </ins><span class="cx"> c = self.mapped_table.corresponding_column(cc, raiseerr=False) </span><span class="cx"> if c is not None: </span><span class="cx"> break </span><span class="lines">@@ -548,13 +542,67 @@ </span><span class="cx"> raise exceptions.ArgumentError("Could not assemble any primary key columns for mapped table '%s'" % (self.mapped_table.name)) </span><span class="cx"> </span><span class="cx"> self.primary_key = primary_key </span><del>- </del><ins>+ self.__log("Identified primary key columns: " + str(primary_key)) + </ins><span class="cx"> _get_clause = sql.and_() </span><span class="cx"> for primary_key in self.primary_key: </span><span class="cx"> _get_clause.clauses.append(primary_key == sql.bindparam(primary_key._label, type=primary_key.type, unique=True)) </span><span class="cx"> self._get_clause = _get_clause </span><span class="cx"> </span><ins>+ def _get_equivalent_columns(self): + """Create a map of all *equivalent* columns, based on + the determination of column pairs that are equated to + one another either by an established foreign key relationship + or by a joined-table inheritance join. + + This is used to determine the minimal set of primary key + columns for the mapper, as well as when relating + columns to those of a polymorphic selectable (i.e. a UNION of + several mapped tables), as that selectable usually only contains + one column in its columns clause out of a group of several which + are equated to each other. + + The resulting structure is a dictionary of columns mapped + to lists of equivalent columns, i.e. + + { + tablea.col1: + set([tableb.col1, tablec.col1]), + tablea.col2: + set([tabled.col2]) + } </ins><span class="cx"> </span><ins>+ this method is called repeatedly during the compilation process as + the resulting dictionary contains more equivalents as more inheriting + mappers are compiled. the repetition of this process may be open to some optimization. + """ + + result = {} + def visit_binary(binary): + if binary.operator == '=': + if binary.left in result: + result[binary.left].add(binary.right) + else: + result[binary.left] = util.Set([binary.right]) + if binary.right in result: + result[binary.right].add(binary.left) + else: + result[binary.right] = util.Set([binary.left]) + vis = mapperutil.BinaryVisitor(visit_binary) + + for mapper in self.base_mapper().polymorphic_iterator(): + if mapper.inherit_condition is not None: + vis.traverse(mapper.inherit_condition) + + for col in (self.primary_key_argument or self.pks_by_table[self.mapped_table]): + if not len(col.foreign_keys): + result.setdefault(col, util.Set()).add(col) + else: + for fk in col.foreign_keys: + result.setdefault(fk.column, util.Set()).add(col) + + return result + </ins><span class="cx"> def _compile_properties(self): </span><span class="cx"> """Inspect the properties dictionary sent to the Mapper's </span><span class="cx"> constructor as well as the mapped_table, and create </span><span class="lines">@@ -769,44 +817,7 @@ </span><span class="cx"> for m in mapper.polymorphic_iterator(): </span><span class="cx"> yield m </span><span class="cx"> </span><del>- def _get_inherited_column_equivalents(self): - """Return a map of all *equivalent* columns, based on - traversing the full set of inherit_conditions across all - inheriting mappers and determining column pairs that are - equated to one another. </del><span class="cx"> </span><del>- This is used when relating columns to those of a polymorphic - selectable, as the selectable usually only contains one of two (or more) - columns that are equated to one another. - - The resulting structure is a dictionary of columns mapped - to lists of equivalent columns, i.e. - - { - tablea.col1: - [tableb.col1, tablec.col1], - tablea.col2: - [tabled.col2] - } - """ - - result = {} - def visit_binary(binary): - if binary.operator == '=': - if binary.left in result: - result[binary.left].append(binary.right) - else: - result[binary.left] = [binary.right] - if binary.right in result: - result[binary.right].append(binary.left) - else: - result[binary.right] = [binary.left] - vis = mapperutil.BinaryVisitor(visit_binary) - for mapper in self.base_mapper().polymorphic_iterator(): - if mapper.inherit_condition is not None: - vis.traverse(mapper.inherit_condition) - return result - </del><span class="cx"> def add_properties(self, dict_of_properties): </span><span class="cx"> """Add the given dictionary of properties to this mapper, </span><span class="cx"> using `add_property`. </span></span></pre></div> <a id="sqlalchemybranchesrel_0_4libsqlalchemyormpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/rel_0_4/lib/sqlalchemy/orm/properties.py (2664 => 2665)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/rel_0_4/lib/sqlalchemy/orm/properties.py 2007-05-26 22:21:31 UTC (rev 2664) +++ sqlalchemy/branches/rel_0_4/lib/sqlalchemy/orm/properties.py 2007-05-26 23:21:56 UTC (rev 2665) </span><span class="lines">@@ -383,8 +383,9 @@ </span><span class="cx"> # as we will be using the polymorphic selectables (i.e. select_table argument to Mapper) to figure this out, </span><span class="cx"> # first create maps of all the "equivalent" columns, since polymorphic selectables will often munge </span><span class="cx"> # several "equivalent" columns (such as parent/child fk cols) into just one column. </span><del>- target_equivalents = self.mapper._get_inherited_column_equivalents() </del><span class="cx"> </span><ins>+ target_equivalents = self.mapper._get_equivalent_columns() + </ins><span class="cx"> # if the target mapper loads polymorphically, adapt the clauses to the target's selectable </span><span class="cx"> if self.loads_polymorphic: </span><span class="cx"> if self.secondaryjoin: </span><span class="lines">@@ -403,7 +404,7 @@ </span><span class="cx"> for c in list(self.remote_side): </span><span class="cx"> if self.secondary and c in self.secondary.columns: </span><span class="cx"> continue </span><del>- for equiv in [c] + (c in target_equivalents and target_equivalents[c] or []): </del><ins>+ for equiv in [c] + (c in target_equivalents and list(target_equivalents[c]) or []): </ins><span class="cx"> corr = self.mapper.select_table.corresponding_column(equiv, raiseerr=False) </span><span class="cx"> if corr: </span><span class="cx"> self.remote_side.add(corr) </span><span class="lines">@@ -454,7 +455,7 @@ </span><span class="cx"> try: </span><span class="cx"> return self._parent_join_cache[(parent, primary, secondary)] </span><span class="cx"> except KeyError: </span><del>- parent_equivalents = parent._get_inherited_column_equivalents() </del><ins>+ parent_equivalents = parent._get_equivalent_columns() </ins><span class="cx"> primaryjoin = self.polymorphic_primaryjoin.copy_container() </span><span class="cx"> if self.secondaryjoin is not None: </span><span class="cx"> secondaryjoin = self.polymorphic_secondaryjoin.copy_container() </span></span></pre></div> <a id="sqlalchemybranchesrel_0_4libsqlalchemysql_utilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/rel_0_4/lib/sqlalchemy/sql_util.py (2664 => 2665)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/rel_0_4/lib/sqlalchemy/sql_util.py 2007-05-26 22:21:31 UTC (rev 2664) +++ sqlalchemy/branches/rel_0_4/lib/sqlalchemy/sql_util.py 2007-05-26 23:21:56 UTC (rev 2665) </span><span class="lines">@@ -67,12 +67,12 @@ </span><span class="cx"> class TableFinder(TableCollection, sql.NoColumnVisitor): </span><span class="cx"> """locate all Tables within a clause.""" </span><span class="cx"> </span><del>- def __init__(self, table, check_columns=False, include_aliases=False): </del><ins>+ def __init__(self, clause, check_columns=False, include_aliases=False): </ins><span class="cx"> TableCollection.__init__(self) </span><span class="cx"> self.check_columns = check_columns </span><span class="cx"> self.include_aliases = include_aliases </span><del>- if table is not None: - self.traverse(table) </del><ins>+ if clause is not None: + self.traverse(clause) </ins><span class="cx"> </span><span class="cx"> def visit_alias(self, alias): </span><span class="cx"> if self.include_aliases: </span></span></pre></div> <a id="sqlalchemybranchesrel_0_4libsqlalchemyutilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/rel_0_4/lib/sqlalchemy/util.py (2664 => 2665)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/rel_0_4/lib/sqlalchemy/util.py 2007-05-26 22:21:31 UTC (rev 2664) +++ sqlalchemy/branches/rel_0_4/lib/sqlalchemy/util.py 2007-05-26 23:21:56 UTC (rev 2665) </span><span class="lines">@@ -417,21 +417,22 @@ </span><span class="cx"> __isub__ = difference_update </span><span class="cx"> </span><span class="cx"> class UniqueAppender(object): </span><del>- """appends items to a list such that consecutive repeats of - a particular item are skipped.""" </del><ins>+ """appends items to a collection such that only unique items + are added.""" </ins><span class="cx"> </span><span class="cx"> def __init__(self, data): </span><span class="cx"> self.data = data </span><ins>+ self._unique = Set() </ins><span class="cx"> if hasattr(data, 'append'): </span><span class="cx"> self._data_appender = data.append </span><span class="cx"> elif hasattr(data, 'add'): </span><ins>+ # TODO: we think its a set here. bypass unneeded uniquing logic ? </ins><span class="cx"> self._data_appender = data.add </span><del>- self.__last = None </del><span class="cx"> </span><span class="cx"> def append(self, item): </span><del>- if item is not self.__last: </del><ins>+ if item not in self._unique: </ins><span class="cx"> self._data_appender(item) </span><del>- self.__last = item </del><ins>+ self._unique.add(item) </ins><span class="cx"> </span><span class="cx"> def __iter__(self): </span><span class="cx"> return iter(self.data) </span></span></pre></div> <a id="sqlalchemybranchesrel_0_4testormeagertest2py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/rel_0_4/test/orm/eagertest2.py (2664 => 2665)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/rel_0_4/test/orm/eagertest2.py 2007-05-26 22:21:31 UTC (rev 2664) +++ sqlalchemy/branches/rel_0_4/test/orm/eagertest2.py 2007-05-26 23:21:56 UTC (rev 2665) </span><span class="lines">@@ -231,9 +231,8 @@ </span><span class="cx"> ctx.current.clear() </span><span class="cx"> </span><span class="cx"> i = ctx.current.query(Invoice).get(invoice_id) </span><del>- self.echo(repr(i)) </del><span class="cx"> </span><del>- self.assert_(repr(i.company) == repr(c)) </del><ins>+ assert repr(i.company) == repr(c), repr(i.company) + " does not match " + repr(c) </ins><span class="cx"> </span><span class="cx"> if __name__ == "__main__": </span><span class="cx"> testbase.main() </span></span></pre> </div> </div> </body> </html> |