def reduce_columns(columns, *clauses, **kw): """given a list of columns, return a 'reduced' set based on natural equivalents. the set is reduced to the smallest list of columns which have no natural equivalent present in the list. A "natural equivalent" means that two columns will ultimately represent the same value because they are related by a foreign key. \*clauses is an optional list of join clauses which will be traversed to further identify columns that are "equivalent". \**kw may specify 'ignore_nonexistent_tables' to ignore foreign keys whose tables are not yet configured. This function is primarily used to determine the most minimal "primary key" from a selectable, by reducing the set of primary key columns present in the the selectable to just those that are not repeated. """ ignore_nonexistent_tables = kw.pop('ignore_nonexistent_tables', False) columns = util.ordered_column_set(columns) omit = util.column_set() for col in columns: for fk in chain(*[c.foreign_keys for c in col.proxy_set]): for c in columns: if c is col: continue try: fk_col = fk.column except exc.NoReferencedTableError: if ignore_nonexistent_tables: continue else: raise if fk_col.shares_lineage(c): omit.add(col) break if clauses: def visit_binary(binary): if binary.operator == operators.eq: cols = util.column_set( chain(*[c.proxy_set for c in columns.difference(omit)])) if binary.left in cols and binary.right in cols: for c in columns: if c.shares_lineage(binary.right): omit.add(c) break for clause in clauses: visitors.traverse(clause, {}, {'binary': visit_binary}) return expression.ColumnSet(columns.difference(omit))
def reduce_columns(columns, *clauses, **kw): """given a list of columns, return a 'reduced' set based on natural equivalents. the set is reduced to the smallest list of columns which have no natural equivalent present in the list. A "natural equivalent" means that two columns will ultimately represent the same value because they are related by a foreign key. \*clauses is an optional list of join clauses which will be traversed to further identify columns that are "equivalent". \**kw may specify 'ignore_nonexistent_tables' to ignore foreign keys whose tables are not yet configured. This function is primarily used to determine the most minimal "primary key" from a selectable, by reducing the set of primary key columns present in the the selectable to just those that are not repeated. """ ignore_nonexistent_tables = kw.pop("ignore_nonexistent_tables", False) columns = util.ordered_column_set(columns) omit = util.column_set() for col in columns: for fk in chain(*[c.foreign_keys for c in col.proxy_set]): for c in columns: if c is col: continue try: fk_col = fk.column except exc.NoReferencedTableError: if ignore_nonexistent_tables: continue else: raise if fk_col.shares_lineage(c): omit.add(col) break if clauses: def visit_binary(binary): if binary.operator == operators.eq: cols = util.column_set(chain(*[c.proxy_set for c in columns.difference(omit)])) if binary.left in cols and binary.right in cols: for c in columns: if c.shares_lineage(binary.right): omit.add(c) break for clause in clauses: visitors.traverse(clause, {}, {"binary": visit_binary}) return expression.ColumnSet(columns.difference(omit))
def _determine_local_remote_pairs(self): if not self.local_remote_pairs: if self.remote_side: if self.direction is MANYTOONE: self.local_remote_pairs = [ (r, l) for l, r in criterion_as_pairs(self.primaryjoin, consider_as_referenced_keys=self.remote_side, any_operator=True) ] else: self.local_remote_pairs = criterion_as_pairs(self.primaryjoin, consider_as_foreign_keys=self.remote_side, any_operator=True) if not self.local_remote_pairs: raise sa_exc.ArgumentError("Relation %s could not determine any local/remote column pairs from remote side argument %r" % (self, self.remote_side)) else: if self.viewonly: eq_pairs = self.synchronize_pairs if self.secondaryjoin: eq_pairs += self.secondary_synchronize_pairs else: eq_pairs = criterion_as_pairs(self.primaryjoin, consider_as_foreign_keys=self._foreign_keys, any_operator=True) if self.secondaryjoin: eq_pairs += criterion_as_pairs(self.secondaryjoin, consider_as_foreign_keys=self._foreign_keys, any_operator=True) eq_pairs = [(l, r) for l, r in eq_pairs if self._col_is_part_of_mappings(l) and self._col_is_part_of_mappings(r)] if self.direction is MANYTOONE: self.local_remote_pairs = [(r, l) for l, r in eq_pairs] else: self.local_remote_pairs = eq_pairs elif self.remote_side: raise sa_exc.ArgumentError("remote_side argument is redundant against more detailed _local_remote_side argument.") for l, r in self.local_remote_pairs: if self.direction is ONETOMANY and not self._col_is_part_of_mappings(l): raise sa_exc.ArgumentError("Local column '%s' is not part of mapping %s. " "Specify remote_side argument to indicate which column " "lazy join condition should compare against." % (l, self.parent)) elif self.direction is MANYTOONE and not self._col_is_part_of_mappings(r): raise sa_exc.ArgumentError("Remote column '%s' is not part of mapping %s. " "Specify remote_side argument to indicate which column lazy " "join condition should bind." % (r, self.mapper)) self.local_side, self.remote_side = [util.ordered_column_set(x) for x in zip(*list(self.local_remote_pairs))]
def _determine_local_remote_pairs(self): if not self.local_remote_pairs: if self.remote_side: if self.direction is MANYTOONE: self.local_remote_pairs = [ (r, l) for l, r in criterion_as_pairs( self.primaryjoin, consider_as_referenced_keys=self.remote_side, any_operator=True ) ] else: self.local_remote_pairs = criterion_as_pairs( self.primaryjoin, consider_as_foreign_keys=self.remote_side, any_operator=True ) if not self.local_remote_pairs: raise sa_exc.ArgumentError( "Relation %s could not determine any local/remote column pairs from remote side argument %r" % (self, self.remote_side) ) else: if self.viewonly: eq_pairs = self.synchronize_pairs if self.secondaryjoin: eq_pairs += self.secondary_synchronize_pairs else: eq_pairs = criterion_as_pairs( self.primaryjoin, consider_as_foreign_keys=self._foreign_keys, any_operator=True ) if self.secondaryjoin: eq_pairs += criterion_as_pairs( self.secondaryjoin, consider_as_foreign_keys=self._foreign_keys, any_operator=True ) eq_pairs = [ (l, r) for l, r in eq_pairs if self._col_is_part_of_mappings(l) and self._col_is_part_of_mappings(r) ] if self.direction is MANYTOONE: self.local_remote_pairs = [(r, l) for l, r in eq_pairs] else: self.local_remote_pairs = eq_pairs elif self.remote_side: raise sa_exc.ArgumentError( "remote_side argument is redundant against more detailed _local_remote_side argument." ) for l, r in self.local_remote_pairs: if self.direction is ONETOMANY and not self._col_is_part_of_mappings(l): raise sa_exc.ArgumentError( "Local column '%s' is not part of mapping %s. " "Specify remote_side argument to indicate which column " "lazy join condition should compare against." % (l, self.parent) ) elif self.direction is MANYTOONE and not self._col_is_part_of_mappings(r): raise sa_exc.ArgumentError( "Remote column '%s' is not part of mapping %s. " "Specify remote_side argument to indicate which column lazy " "join condition should bind." % (r, self.mapper) ) self.local_side, self.remote_side = [util.ordered_column_set(x) for x in zip(*list(self.local_remote_pairs))]