Django

Code

Changeset 7247

Show
Ignore:
Timestamp:
03/15/08 04:13:22 (8 months ago)
Author:
mtredinnick
Message:

queryset-refactor: Optimised some internal data structures in sql/query.py.
Mostly this involves changing them to a "copy on write" implementation, which
speeds up cloning (and the relevant structures aren't updated very frequently).

Files:

Legend:

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

    r7246 r7247  
    422422                "Cannot change a query once a slice has been taken" 
    423423        clone = self._clone() 
    424         if select: 
    425             clone.query.extra_select.update(select) 
    426         if where: 
    427             clone.query.extra_where.extend(where) 
    428         if params: 
    429             clone.query.extra_params.extend(params) 
    430         if tables: 
    431             clone.query.extra_tables.extend(tables) 
    432         if order_by: 
    433             clone.query.extra_order_by = order_by 
     424        clone.query.add_extra(select, where, params, tables, order_by) 
    434425        return clone 
    435426 
  • django/branches/queryset-refactor/django/db/models/sql/constants.py

    r7164 r7247  
    2323LHS_JOIN_COL = 4 
    2424RHS_JOIN_COL = 5 
    25 # Alias map lists 
    26 ALIAS_TABLE = 0 
    27 ALIAS_REFCOUNT = 1 
    28 ALIAS_JOIN = 2 
    29 ALIAS_NULLABLE=3 
     25NULLABLE = 6 
    3026 
    3127# How many results to expect from a cursor.execute call 
  • django/branches/queryset-refactor/django/db/models/sql/query.py

    r7242 r7247  
    88""" 
    99 
    10 import copy 
     10from copy import deepcopy 
    1111 
    1212from django.utils.tree import Node 
     
    4343        self.model = model 
    4444        self.connection = connection 
    45         self.alias_map = {}     # Maps alias to table name 
     45        self.alias_refcount = {} 
     46        self.alias_map = {}     # Maps alias to join information 
    4647        self.table_map = {}     # Maps table names to list of aliases. 
    4748        self.rev_join_map = {}  # Reverse of join_map. (FIXME: Update comment) 
     
    7071        # These are for extensions. The contents are more or less appended 
    7172        # verbatim to the appropriate clause. 
    72         self.extra_select = SortedDict()  # Maps col_alias -> col_sql. 
    73         self.extra_tables = [] 
    74         self.extra_where = [] 
    75         self.extra_params = [] 
    76         self.extra_order_by = [] 
     73        self.extra_select = {}  # Maps col_alias -> col_sql. 
     74        self.extra_tables = () 
     75        self.extra_where = () 
     76        self.extra_params = () 
     77        self.extra_order_by = () 
    7778 
    7879    def __str__(self): 
     
    126127        obj.model = self.model 
    127128        obj.connection = self.connection 
    128         obj.alias_map = copy.deepcopy(self.alias_map) 
     129        obj.alias_refcount = self.alias_refcount.copy() 
     130        obj.alias_map = self.alias_map.copy() 
    129131        obj.table_map = self.table_map.copy() 
    130132        obj.rev_join_map = self.rev_join_map.copy() 
     
    136138        obj.select = self.select[:] 
    137139        obj.tables = self.tables[:] 
    138         obj.where = copy.deepcopy(self.where) 
     140        obj.where = deepcopy(self.where) 
    139141        obj.where_class = self.where_class 
    140142        obj.group_by = self.group_by[:] 
     
    146148        obj.max_depth = self.max_depth 
    147149        obj.extra_select = self.extra_select.copy() 
    148         obj.extra_tables = self.extra_tables[:] 
    149         obj.extra_where = self.extra_where[:] 
    150         obj.extra_params = self.extra_params[:] 
    151         obj.extra_order_by = self.extra_order_by[:] 
     150        obj.extra_tables = self.extra_tables 
     151        obj.extra_where = self.extra_where 
     152        obj.extra_params = self.extra_params 
     153        obj.extra_order_by = self.extra_order_by 
    152154        obj.__dict__.update(kwargs) 
    153155        if hasattr(obj, '_setup_query'): 
     
    180182                    distinct=False) 
    181183            obj.select = [] 
    182             obj.extra_select = SortedDict() 
     184            obj.extra_select = {} 
    183185        obj.add_count_column() 
    184186        data = obj.execute_sql(SINGLE) 
     
    273275        first = True 
    274276        for alias in rhs.tables: 
    275             if not rhs.alias_map[alias][ALIAS_REFCOUNT]: 
     277            if not rhs.alias_refcount[alias]: 
    276278                # An unused alias. 
    277279                continue 
    278             promote = (rhs.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] == 
    279                     self.LOUTER) 
     280            promote = (rhs.alias_map[alias][JOIN_TYPE] == self.LOUTER) 
    280281            new_alias = self.join(rhs.rev_join_map[alias], 
    281282                    (conjunction and not first), used, promote, not conjunction) 
     
    289290        if not conjunction: 
    290291            for alias in self.tables[1:]: 
    291                 if self.alias_map[alias][ALIAS_REFCOUNT] == 1: 
    292                     self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER 
     292                if self.alias_refcount[alias] == 1: 
     293                    self.promote_alias(alias, True) 
    293294                    break 
    294295 
     
    296297        # one. 
    297298        if rhs.where: 
    298             w = copy.deepcopy(rhs.where) 
     299            w = deepcopy(rhs.where) 
    299300            w.relabel_aliases(change_map) 
    300301            if not self.where: 
     
    317318                self.select.append((change_map.get(col[0], col[0]), col[1])) 
    318319            else: 
    319                 item = copy.deepcopy(col) 
     320                item = deepcopy(col) 
    320321                item.relabel_aliases(change_map) 
    321322                self.select.append(item) 
    322323        self.extra_select = rhs.extra_select.copy() 
    323         self.extra_tables = rhs.extra_tables[:] 
    324         self.extra_where = rhs.extra_where[:] 
    325         self.extra_params = rhs.extra_params[:] 
     324        self.extra_tables = rhs.extra_tables 
     325        self.extra_where = rhs.extra_where 
     326        self.extra_params = rhs.extra_params 
    326327 
    327328        # Ordering uses the 'rhs' ordering, unless it has none, in which case 
    328329        # the current ordering is used. 
    329330        self.order_by = rhs.order_by and rhs.order_by[:] or self.order_by 
    330         self.extra_order_by = (rhs.extra_order_by and rhs.extra_order_by[:] or 
    331                 self.extra_order_by) 
     331        self.extra_order_by = rhs.extra_order_by or self.extra_order_by 
    332332 
    333333    def pre_sql_setup(self): 
     
    412412        first = True 
    413413        for alias in self.tables: 
    414             if not self.alias_map[alias][ALIAS_REFCOUNT]: 
     414            if not self.alias_refcount[alias]: 
    415415                continue 
    416             join = self.alias_map[alias][ALIAS_JOIN] 
     416            join = self.alias_map[alias] 
    417417            if join: 
    418                 name, alias, join_type, lhs, lhs_col, col = join 
     418                name, alias, join_type, lhs, lhs_col, col, nullable = join 
    419419                alias_str = (alias != name and ' AS %s' % alias or '') 
    420420            else: 
     
    430430                result.append('%s%s%s' % (connector, qn(name), alias_str)) 
    431431            first = False 
    432         extra_tables = [] 
    433432        for t in self.extra_tables: 
    434433            alias, created = self.table_alias(t) 
     
    555554            # add_filter, since the final column might not otherwise be part of 
    556555            # the select set (so we can't order on it). 
    557             join = self.alias_map[alias][ALIAS_JOIN] 
     556            join = self.alias_map[alias] 
    558557            if col == join[RHS_JOIN_COL]: 
    559558                self.unref_alias(alias) 
     
    572571        if not create and table_name in self.table_map: 
    573572            alias = self.table_map[table_name][-1] 
    574             self.alias_map[alias][ALIAS_REFCOUNT] += 1 
     573            self.alias_refcount[alias] += 1 
    575574            return alias, False 
    576575 
     
    581580        else: 
    582581            alias = '%s%d' % (self.alias_prefix, len(self.alias_map) + 1) 
    583         self.alias_map[alias] = [table_name, 1, None, False] 
     582        self.alias_refcount[alias] = 1 
     583        self.alias_map[alias] = None 
    584584        self.table_map.setdefault(table_name, []).append(alias) 
    585585        self.tables.append(alias) 
     
    588588    def ref_alias(self, alias): 
    589589        """ Increases the reference count for this alias. """ 
    590         self.alias_map[alias][ALIAS_REFCOUNT] += 1 
     590        self.alias_refcount[alias] += 1 
    591591 
    592592    def unref_alias(self, alias): 
    593593        """ Decreases the reference count for this alias. """ 
    594         self.alias_map[alias][ALIAS_REFCOUNT] -= 1 
    595  
    596     def promote_alias(self, alias): 
     594        self.alias_refcount[alias] -= 1 
     595 
     596    def promote_alias(self, alias, unconditional=False): 
    597597        """ 
    598598        Promotes the join type of an alias to an outer join if it's possible 
    599         for the join to contain NULL values on the left. 
    600         """ 
    601         if self.alias_map[alias][ALIAS_NULLABLE]: 
    602             self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER 
     599        for the join to contain NULL values on the left. If 'unconditional' is 
     600        False, the join is only promoted if it is nullable, otherwise it is 
     601        always promoted. 
     602        """ 
     603        if ((unconditional or self.alias_map[alias][NULLABLE]) and 
     604                self.alias_map[alias] != self.LOUTER): 
     605            data = list(self.alias_map[alias]) 
     606            data[JOIN_TYPE] = self.LOUTER 
     607            self.alias_map[alias] = tuple(data) 
    603608 
    604609    def change_aliases(self, change_map): 
     
    620625        # 2. Rename the alias in the internal table/alias datastructures. 
    621626        for old_alias, new_alias in change_map.items(): 
    622             alias_data = self.alias_map[old_alias] 
    623             alias_data[ALIAS_JOIN][RHS_ALIAS] = new_alias 
     627            alias_data = list(self.alias_map[old_alias]) 
     628            alias_data[RHS_ALIAS] = new_alias 
     629 
    624630            self.rev_join_map[new_alias] = self.rev_join_map[old_alias] 
    625631            del self.rev_join_map[old_alias] 
    626             table_aliases = self.table_map[alias_data[ALIAS_TABLE]] 
     632            self.alias_refcount[new_alias] = self.alias_refcount[old_alias] 
     633            del self.alias_refcount[old_alias] 
     634            self.alias_map[new_alias] = tuple(alias_data) 
     635            del self.alias_map[old_alias] 
     636 
     637            table_aliases = self.table_map[alias_data[TABLE_NAME]] 
    627638            for pos, alias in enumerate(table_aliases): 
    628639                if alias == old_alias: 
    629640                    table_aliases[pos] = new_alias 
    630641                    break 
    631             self.alias_map[new_alias] = alias_data 
    632             del self.alias_map[old_alias] 
    633642            for pos, alias in enumerate(self.tables): 
    634643                if alias == old_alias: 
     
    637646 
    638647        # 3. Update any joins that refer to the old alias. 
    639         for data in self.alias_map.values(): 
    640             alias = data[ALIAS_JOIN][LHS_ALIAS] 
    641             if alias in change_map: 
    642                 data[ALIAS_JOIN][LHS_ALIAS] = change_map[alias] 
     648        for alias, data in self.alias_map.items(): 
     649            lhs = data[LHS_ALIAS] 
     650            if lhs in change_map: 
     651                data = list(data) 
     652                data[LHS_ALIAS] = change_map[lhs] 
     653                self.alias_map[alias] = tuple(data) 
     654 
    643655 
    644656    def bump_prefix(self): 
     
    679691        count. 
    680692        """ 
    681         return len([1 for o in self.alias_map.values() if o[ALIAS_REFCOUNT]]) 
     693        return len([1 for count in self.alias_refcount.values() if count]) 
    682694 
    683695    def join(self, connection, always_create=False, exclusions=(), 
     
    718730            is_table = True 
    719731        else: 
    720             lhs_table = self.alias_map[lhs][ALIAS_TABLE] 
     732            lhs_table = self.alias_map[lhs][TABLE_NAME] 
    721733            is_table = False 
    722734        t_ident = (lhs_table, table, lhs_col, col) 
     
    725737                if t_ident == row and alias not in exclusions: 
    726738                    self.ref_alias(alias) 
    727                     if promote and self.alias_map[alias][ALIAS_NULLABLE]
    728                         self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER 
     739                    if promote
     740                        self.promote_alias(alias) 
    729741                    return alias 
    730                 # If we get to here (no non-excluded alias exists), we'll fall 
    731                 # through to creating a new alias. 
     742            # If we get to here (no non-excluded alias exists), we'll fall 
     743            # through to creating a new alias. 
    732744 
    733745        # No reuse is possible, so we need a new alias. 
     
    735747                "Must pass in lhs alias when creating a new join." 
    736748        alias, _ = self.table_alias(table, True) 
    737         if promote or outer_if_first: 
     749        if not lhs: 
     750            # Not all tables need to be joined to anything. No join type 
     751            # means the later columns are ignored. 
     752            join_type = None 
     753        elif promote or outer_if_first: 
    738754            join_type = self.LOUTER 
    739755        else: 
    740756            join_type = self.INNER 
    741         join = [table, alias, join_type, lhs, lhs_col, col] 
    742         if not lhs: 
    743             # Not all tables need to be joined to anything. No join type 
    744             # means the later columns are ignored. 
    745             join[JOIN_TYPE] = None 
    746         self.alias_map[alias][ALIAS_JOIN] = join 
    747         self.alias_map[alias][ALIAS_NULLABLE] = nullable 
     757        join = (table, alias, join_type, lhs, lhs_col, col, nullable) 
     758        self.alias_map[alias] = join 
    748759        self.rev_join_map[alias] = t_ident 
    749760        return alias 
     
    851862            extra = join_list[-1] 
    852863            join_list = join_list[:-1] 
    853             col = self.alias_map[extra[0]][ALIAS_JOIN][LHS_JOIN_COL] 
     864            col = self.alias_map[extra[0]][LHS_JOIN_COL] 
    854865            for alias in extra: 
    855866                self.unref_alias(alias) 
     
    863874            # chain and compare against the lhs of the join instead. The result 
    864875            # (potentially) involves one less table join. 
    865             join = self.alias_map[alias][ALIAS_JOIN] 
     876            join = self.alias_map[alias] 
    866877            if col == join[RHS_JOIN_COL]: 
    867878                self.unref_alias(alias) 
     
    892903            for join in join_it: 
    893904                table = table_it.next() 
    894                 if join == table and self.alias_map[join][ALIAS_REFCOUNT] > 1: 
     905                if join == table and self.alias_refcount[join] > 1: 
    895906                    continue 
    896907                self.promote_alias(join) 
     
    905916                self.promote_alias(table) 
    906917 
    907         entry = [alias, col, field, lookup_type, value] 
     918        entry = (alias, col, field, lookup_type, value) 
    908919        if merge_negated: 
    909920            # This case is when we're doing the Q2 filter in exclude(Q1, Q2). 
     
    927938                self.where.negate() 
    928939            if count > 1 and lookup_type != 'isnull': 
    929                 j_col = self.alias_map[alias][ALIAS_JOIN][RHS_JOIN_COL] 
    930                 entry = Node([[alias, j_col, None, 'isnull', True]]) 
     940                j_col = self.alias_map[alias][RHS_JOIN_COL] 
     941                entry = Node([(alias, j_col, None, 'isnull', True)]) 
    931942                entry.negate() 
    932943                self.where.add(entry, AND) 
     
    11991210        """ 
    12001211        self.order_by = [] 
    1201         self.extra_order_by = [] 
     1212        self.extra_order_by = () 
    12021213        if force_empty: 
    12031214            self.default_ordering = False 
     
    12331244            self.distinct = False 
    12341245        self.select = [select] 
    1235         self.extra_select = SortedDict() 
     1246        self.extra_select = {} 
    12361247 
    12371248    def add_select_related(self, fields): 
     
    12481259        self.select_related = field_dict 
    12491260 
     1261    def add_extra(self, select, where, params, tables, order_by): 
     1262        """ 
     1263        Adds data to the various extra_* attributes for user-created additiosn 
     1264        to the query. 
     1265        """ 
     1266        if select: 
     1267            # The extra select might be ordered (because it will be accepting 
     1268            # parameters). 
     1269            if (isinstance(select, SortedDict) and 
     1270                    not isinstance(self.extra_select, SortedDict)): 
     1271                self.extra_select = SortedDict(self.extra_select) 
     1272            self.extra_select.update(select) 
     1273        if where: 
     1274            self.extra_where += tuple(where) 
     1275        if params: 
     1276            self.extra_params += tuple(params) 
     1277        if tables: 
     1278            self.extra_tables += tuple(tables) 
     1279        if order_by: 
     1280            self.extra_order_by = order_by 
     1281 
    12501282    def set_start(self, start): 
    12511283        """ 
     
    12641296                opts, alias, False) 
    12651297        alias = joins[-1][0] 
    1266         self.select = [(alias, self.alias_map[alias][ALIAS_JOIN][RHS_JOIN_COL])] 
     1298        self.select = [(alias, self.alias_map[alias][RHS_JOIN_COL])] 
    12671299        self.start_meta = opts 
    12681300 
  • django/branches/queryset-refactor/django/db/models/sql/subqueries.py

    r7234 r7247  
    176176            self.add_filter(('pk__in', query)) 
    177177        for alias in self.tables[1:]: 
    178             self.alias_map[alias][ALIAS_REFCOUNT] = 0 
     178            self.alias_refcount[alias] = 0 
    179179 
    180180    def clear_related(self, related_field, pk_list): 
  • django/branches/queryset-refactor/django/db/models/sql/where.py

    r7244 r7247  
    152152        if not node: 
    153153            node = self 
    154         for child in node.children
     154        for pos, child in enumerate(node.children)
    155155            if hasattr(child, 'relabel_aliases'): 
    156156                child.relabel_aliases(change_map) 
     
    158158                self.relabel_aliases(change_map, child) 
    159159            else: 
    160                 val = child[0] 
    161                 child[0] = change_map.get(val, val) 
     160                if child[0] in change_map: 
     161                    node.children[pos] = (change_map[child[0]],) + child[1:] 
    162162 
    163163class EverythingNode(object): 
  • django/branches/queryset-refactor/tests/regressiontests/queries/models.py

    r7224 r7247  
    206206Checking that no join types are "left outer" joins. 
    207207>>> query = Item.objects.filter(tags=t2).query 
    208 >>> query.LOUTER not in [x[2][2] for x in query.alias_map.values()] 
     208>>> query.LOUTER not in [x[2] for x in query.alias_map.values()] 
    209209True 
    210210 
     
    333333# Excluding from a relation that cannot be NULL should not use outer joins. 
    334334>>> query = Item.objects.exclude(creator__in=[a1, a2]).query 
    335 >>> query.LOUTER not in [x[2][2] for x in query.alias_map.values()] 
     335>>> query.LOUTER not in [x[2] for x in query.alias_map.values()] 
    336336True 
    337337 
    338338Similarly, when one of the joins cannot possibly, ever, involve NULL values (Author -> ExtraInfo, in the following), it should never be promoted to a left outer join. So hte following query should only involve one "left outer" join (Author -> Item is 0-to-many). 
    339339>>> qs = Author.objects.filter(id=a1.id).filter(Q(extra__note=n1)|Q(item__note=n3)) 
    340 >>> len([x[2][2] for x in qs.query.alias_map.values() if x[2][2] == query.LOUTER]) 
     340>>> len([x[2] for x in qs.query.alias_map.values() if x[2] == query.LOUTER]) 
    3413411 
    342342