Django

Code

Changeset 7231

Show
Ignore:
Timestamp:
03/12/08 09:52:12 (7 months ago)
Author:
mtredinnick
Message:

queryset-refactor: Added a way to change the prefix of all aliases in a query.

This fixes the last few corner cases for nested queries that had overlapping
aliases. Also tidies up a couple of internal data structures.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/queryset-refactor/django/db/models/sql/query.py

    r7230 r7231  
    4545        self.alias_map = {}     # Maps alias to table name 
    4646        self.table_map = {}     # Maps table names to list of aliases. 
    47         self.join_map = {}      # Maps join_tuple to list of aliases. 
    4847        self.rev_join_map = {}  # Reverse of join_map. 
    4948        self.quote_cache = {} 
     
    129128        obj.alias_map = copy.deepcopy(self.alias_map) 
    130129        obj.table_map = self.table_map.copy() 
    131         obj.join_map = copy.deepcopy(self.join_map) 
    132         obj.rev_join_map = copy.deepcopy(self.rev_join_map) 
     130        obj.rev_join_map = self.rev_join_map.copy() 
    133131        obj.quote_cache = {} 
    134132        obj.default_cols = self.default_cols 
     
    585583            self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER 
    586584 
    587     def change_alias(self, old_alias, new_alias): 
    588         """ 
    589         Changes old_alias to new_alias, relabelling any references to it in 
    590         select columns and the where clause. 
    591         """ 
    592         assert new_alias not in self.alias_map 
     585    def change_aliases(self, change_map): 
     586        """ 
     587        Changes the aliases in change_map (which maps old-alias -> new-alias), 
     588        relabelling any references to them in select columns and the where 
     589        clause. 
     590        """ 
     591        assert set(change_map.keys()).intersection(set(change_map.values())) == set() 
    593592 
    594593        # 1. Update references in "select" and "where". 
    595         change_map = {old_alias: new_alias} 
    596594        self.where.relabel_aliases(change_map) 
    597595        for pos, col in enumerate(self.select): 
    598596            if isinstance(col, (list, tuple)): 
    599                 if col[0] == old_alias: 
    600                     self.select[pos] = (new_alias, col[1]) 
     597                self.select[pos] = (change_map.get(old_alias, old_alias), col[1]) 
    601598            else: 
    602599                col.relabel_aliases(change_map) 
    603600 
    604601        # 2. Rename the alias in the internal table/alias datastructures. 
    605         alias_data = self.alias_map[old_alias] 
    606         alias_data[ALIAS_JOIN][RHS_ALIAS] = new_alias 
    607         table_aliases = self.table_map[alias_data[ALIAS_TABLE]] 
    608         for pos, alias in enumerate(table_aliases): 
    609             if alias == old_alias: 
    610                 table_aliases[pos] = new_alias 
    611                 break 
    612         self.alias_map[new_alias] = alias_data 
    613         del self.alias_map[old_alias] 
    614         for pos, alias in enumerate(self.tables): 
    615             if alias == old_alias: 
    616                 self.tables[pos] = new_alias 
    617                 break 
     602        for old_alias, new_alias in change_map.items(): 
     603            alias_data = self.alias_map[old_alias] 
     604            alias_data[ALIAS_JOIN][RHS_ALIAS] = new_alias 
     605            self.rev_join_map[new_alias] = self.rev_join_map[old_alias] 
     606            del self.rev_join_map[old_alias] 
     607            table_aliases = self.table_map[alias_data[ALIAS_TABLE]] 
     608            for pos, alias in enumerate(table_aliases): 
     609                if alias == old_alias: 
     610                    table_aliases[pos] = new_alias 
     611                    break 
     612            self.alias_map[new_alias] = alias_data 
     613            del self.alias_map[old_alias] 
     614            for pos, alias in enumerate(self.tables): 
     615                if alias == old_alias: 
     616                    self.tables[pos] = new_alias 
     617                    break 
    618618 
    619619        # 3. Update any joins that refer to the old alias. 
    620620        for data in self.alias_map.values(): 
    621             if data[ALIAS_JOIN][LHS_ALIAS] == old_alias: 
    622                 data[ALIAS_JOIN][LHS_ALIAS] = new_alias 
     621            alias = data[ALIAS_JOIN][LHS_ALIAS] 
     622            if alias in change_map: 
     623                data[ALIAS_JOIN][LHS_ALIAS] = change_map[alias] 
     624 
     625    def bump_prefix(self): 
     626        """ 
     627        Changes the alias prefix to the next letter in the alphabet and 
     628        relabels all the aliases. Even tables that previously had no alias will 
     629        get an alias after this call (it's mostly used for nested queries and 
     630        the outer query will already be using the non-aliased table name). 
     631 
     632        Subclasses who create their own prefix should override this method to 
     633        produce a similar result (a new prefix and relabelled aliases). 
     634        """ 
     635        assert ord(self.alias_prefix) < ord('Z') 
     636        self.alias_prefix = chr(ord(self.alias_prefix) + 1) 
     637        change_map = {} 
     638        prefix = self.alias_prefix 
     639        for pos, alias in enumerate(self.tables): 
     640            new_alias = '%s%d' % (prefix, pos) 
     641            change_map[alias] = new_alias 
     642            self.tables[pos] = new_alias 
     643        self.change_aliases(change_map) 
    623644 
    624645    def get_initial_alias(self): 
     
    682703        t_ident = (lhs_table, table, lhs_col, col) 
    683704        if not always_create: 
    684             aliases = self.join_map.get(t_ident) 
    685             if aliases: 
    686                 for alias in aliases: 
    687                     if alias not in exclusions: 
    688                         self.ref_alias(alias) 
    689                         if promote and self.alias_map[alias][ALIAS_NULLABLE]: 
    690                             self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = \ 
    691                                     self.LOUTER 
    692                         return alias 
     705            for alias, row in self.rev_join_map.items(): 
     706                if t_ident == row and alias not in exclusions: 
     707                    self.ref_alias(alias) 
     708                    if promote and self.alias_map[alias][ALIAS_NULLABLE]: 
     709                        self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER 
     710                    return alias 
    693711                # If we get to here (no non-excluded alias exists), we'll fall 
    694712                # through to creating a new alias. 
     
    709727        self.alias_map[alias][ALIAS_JOIN] = join 
    710728        self.alias_map[alias][ALIAS_NULLABLE] = nullable 
    711         self.join_map.setdefault(t_ident, []).append(alias) 
    712729        self.rev_join_map[alias] = t_ident 
    713730        return alias 
  • django/branches/queryset-refactor/django/db/models/sql/subqueries.py

    r7230 r7231  
    159159        # from other tables. 
    160160        query = self.clone(klass=Query) 
    161         alias = '%s0' % self.alias_prefix 
    162         query.change_alias(query.tables[0], alias) 
     161        query.bump_prefix() 
    163162        self.add_fields([query.model._meta.pk.name]) 
    164163