Changeset 7247
- Timestamp:
- 03/15/08 04:13:22 (8 months ago)
- Files:
-
- django/branches/queryset-refactor/django/db/models/query.py (modified) (1 diff)
- django/branches/queryset-refactor/django/db/models/sql/constants.py (modified) (1 diff)
- django/branches/queryset-refactor/django/db/models/sql/query.py (modified) (32 diffs)
- django/branches/queryset-refactor/django/db/models/sql/subqueries.py (modified) (1 diff)
- django/branches/queryset-refactor/django/db/models/sql/where.py (modified) (2 diffs)
- django/branches/queryset-refactor/tests/regressiontests/queries/models.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/queryset-refactor/django/db/models/query.py
r7246 r7247 422 422 "Cannot change a query once a slice has been taken" 423 423 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) 434 425 return clone 435 426 django/branches/queryset-refactor/django/db/models/sql/constants.py
r7164 r7247 23 23 LHS_JOIN_COL = 4 24 24 RHS_JOIN_COL = 5 25 # Alias map lists 26 ALIAS_TABLE = 0 27 ALIAS_REFCOUNT = 1 28 ALIAS_JOIN = 2 29 ALIAS_NULLABLE=3 25 NULLABLE = 6 30 26 31 27 # How many results to expect from a cursor.execute call django/branches/queryset-refactor/django/db/models/sql/query.py
r7242 r7247 8 8 """ 9 9 10 importcopy10 from copy import deepcopy 11 11 12 12 from django.utils.tree import Node … … 43 43 self.model = model 44 44 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 46 47 self.table_map = {} # Maps table names to list of aliases. 47 48 self.rev_join_map = {} # Reverse of join_map. (FIXME: Update comment) … … 70 71 # These are for extensions. The contents are more or less appended 71 72 # 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 = () 77 78 78 79 def __str__(self): … … 126 127 obj.model = self.model 127 128 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() 129 131 obj.table_map = self.table_map.copy() 130 132 obj.rev_join_map = self.rev_join_map.copy() … … 136 138 obj.select = self.select[:] 137 139 obj.tables = self.tables[:] 138 obj.where = copy.deepcopy(self.where)140 obj.where = deepcopy(self.where) 139 141 obj.where_class = self.where_class 140 142 obj.group_by = self.group_by[:] … … 146 148 obj.max_depth = self.max_depth 147 149 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 152 154 obj.__dict__.update(kwargs) 153 155 if hasattr(obj, '_setup_query'): … … 180 182 distinct=False) 181 183 obj.select = [] 182 obj.extra_select = SortedDict()184 obj.extra_select = {} 183 185 obj.add_count_column() 184 186 data = obj.execute_sql(SINGLE) … … 273 275 first = True 274 276 for alias in rhs.tables: 275 if not rhs.alias_ map[alias][ALIAS_REFCOUNT]:277 if not rhs.alias_refcount[alias]: 276 278 # An unused alias. 277 279 continue 278 promote = (rhs.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] == 279 self.LOUTER) 280 promote = (rhs.alias_map[alias][JOIN_TYPE] == self.LOUTER) 280 281 new_alias = self.join(rhs.rev_join_map[alias], 281 282 (conjunction and not first), used, promote, not conjunction) … … 289 290 if not conjunction: 290 291 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.LOUTER292 if self.alias_refcount[alias] == 1: 293 self.promote_alias(alias, True) 293 294 break 294 295 … … 296 297 # one. 297 298 if rhs.where: 298 w = copy.deepcopy(rhs.where)299 w = deepcopy(rhs.where) 299 300 w.relabel_aliases(change_map) 300 301 if not self.where: … … 317 318 self.select.append((change_map.get(col[0], col[0]), col[1])) 318 319 else: 319 item = copy.deepcopy(col)320 item = deepcopy(col) 320 321 item.relabel_aliases(change_map) 321 322 self.select.append(item) 322 323 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 326 327 327 328 # Ordering uses the 'rhs' ordering, unless it has none, in which case 328 329 # the current ordering is used. 329 330 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 332 332 333 333 def pre_sql_setup(self): … … 412 412 first = True 413 413 for alias in self.tables: 414 if not self.alias_ map[alias][ALIAS_REFCOUNT]:414 if not self.alias_refcount[alias]: 415 415 continue 416 join = self.alias_map[alias] [ALIAS_JOIN]416 join = self.alias_map[alias] 417 417 if join: 418 name, alias, join_type, lhs, lhs_col, col = join418 name, alias, join_type, lhs, lhs_col, col, nullable = join 419 419 alias_str = (alias != name and ' AS %s' % alias or '') 420 420 else: … … 430 430 result.append('%s%s%s' % (connector, qn(name), alias_str)) 431 431 first = False 432 extra_tables = []433 432 for t in self.extra_tables: 434 433 alias, created = self.table_alias(t) … … 555 554 # add_filter, since the final column might not otherwise be part of 556 555 # the select set (so we can't order on it). 557 join = self.alias_map[alias] [ALIAS_JOIN]556 join = self.alias_map[alias] 558 557 if col == join[RHS_JOIN_COL]: 559 558 self.unref_alias(alias) … … 572 571 if not create and table_name in self.table_map: 573 572 alias = self.table_map[table_name][-1] 574 self.alias_ map[alias][ALIAS_REFCOUNT] += 1573 self.alias_refcount[alias] += 1 575 574 return alias, False 576 575 … … 581 580 else: 582 581 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 584 584 self.table_map.setdefault(table_name, []).append(alias) 585 585 self.tables.append(alias) … … 588 588 def ref_alias(self, alias): 589 589 """ Increases the reference count for this alias. """ 590 self.alias_ map[alias][ALIAS_REFCOUNT] += 1590 self.alias_refcount[alias] += 1 591 591 592 592 def unref_alias(self, alias): 593 593 """ Decreases the reference count for this alias. """ 594 self.alias_ map[alias][ALIAS_REFCOUNT] -= 1595 596 def promote_alias(self, alias ):594 self.alias_refcount[alias] -= 1 595 596 def promote_alias(self, alias, unconditional=False): 597 597 """ 598 598 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) 603 608 604 609 def change_aliases(self, change_map): … … 620 625 # 2. Rename the alias in the internal table/alias datastructures. 621 626 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 624 630 self.rev_join_map[new_alias] = self.rev_join_map[old_alias] 625 631 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]] 627 638 for pos, alias in enumerate(table_aliases): 628 639 if alias == old_alias: 629 640 table_aliases[pos] = new_alias 630 641 break 631 self.alias_map[new_alias] = alias_data632 del self.alias_map[old_alias]633 642 for pos, alias in enumerate(self.tables): 634 643 if alias == old_alias: … … 637 646 638 647 # 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 643 655 644 656 def bump_prefix(self): … … 679 691 count. 680 692 """ 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]) 682 694 683 695 def join(self, connection, always_create=False, exclusions=(), … … 718 730 is_table = True 719 731 else: 720 lhs_table = self.alias_map[lhs][ ALIAS_TABLE]732 lhs_table = self.alias_map[lhs][TABLE_NAME] 721 733 is_table = False 722 734 t_ident = (lhs_table, table, lhs_col, col) … … 725 737 if t_ident == row and alias not in exclusions: 726 738 self.ref_alias(alias) 727 if promote and self.alias_map[alias][ALIAS_NULLABLE]:728 self. alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER739 if promote: 740 self.promote_alias(alias) 729 741 return alias 730 # If we get to here (no non-excluded alias exists), we'll fall731 # 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. 732 744 733 745 # No reuse is possible, so we need a new alias. … … 735 747 "Must pass in lhs alias when creating a new join." 736 748 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: 738 754 join_type = self.LOUTER 739 755 else: 740 756 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 748 759 self.rev_join_map[alias] = t_ident 749 760 return alias … … 851 862 extra = join_list[-1] 852 863 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] 854 865 for alias in extra: 855 866 self.unref_alias(alias) … … 863 874 # chain and compare against the lhs of the join instead. The result 864 875 # (potentially) involves one less table join. 865 join = self.alias_map[alias] [ALIAS_JOIN]876 join = self.alias_map[alias] 866 877 if col == join[RHS_JOIN_COL]: 867 878 self.unref_alias(alias) … … 892 903 for join in join_it: 893 904 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: 895 906 continue 896 907 self.promote_alias(join) … … 905 916 self.promote_alias(table) 906 917 907 entry = [alias, col, field, lookup_type, value]918 entry = (alias, col, field, lookup_type, value) 908 919 if merge_negated: 909 920 # This case is when we're doing the Q2 filter in exclude(Q1, Q2). … … 927 938 self.where.negate() 928 939 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)]) 931 942 entry.negate() 932 943 self.where.add(entry, AND) … … 1199 1210 """ 1200 1211 self.order_by = [] 1201 self.extra_order_by = []1212 self.extra_order_by = () 1202 1213 if force_empty: 1203 1214 self.default_ordering = False … … 1233 1244 self.distinct = False 1234 1245 self.select = [select] 1235 self.extra_select = SortedDict()1246 self.extra_select = {} 1236 1247 1237 1248 def add_select_related(self, fields): … … 1248 1259 self.select_related = field_dict 1249 1260 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 1250 1282 def set_start(self, start): 1251 1283 """ … … 1264 1296 opts, alias, False) 1265 1297 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])] 1267 1299 self.start_meta = opts 1268 1300 django/branches/queryset-refactor/django/db/models/sql/subqueries.py
r7234 r7247 176 176 self.add_filter(('pk__in', query)) 177 177 for alias in self.tables[1:]: 178 self.alias_ map[alias][ALIAS_REFCOUNT] = 0178 self.alias_refcount[alias] = 0 179 179 180 180 def clear_related(self, related_field, pk_list): django/branches/queryset-refactor/django/db/models/sql/where.py
r7244 r7247 152 152 if not node: 153 153 node = self 154 for child in node.children:154 for pos, child in enumerate(node.children): 155 155 if hasattr(child, 'relabel_aliases'): 156 156 child.relabel_aliases(change_map) … … 158 158 self.relabel_aliases(change_map, child) 159 159 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:] 162 162 163 163 class EverythingNode(object): django/branches/queryset-refactor/tests/regressiontests/queries/models.py
r7224 r7247 206 206 Checking that no join types are "left outer" joins. 207 207 >>> 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()] 209 209 True 210 210 … … 333 333 # Excluding from a relation that cannot be NULL should not use outer joins. 334 334 >>> 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()] 336 336 True 337 337 338 338 Similarly, 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). 339 339 >>> 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]) 341 341 1 342 342
