#9342: query optimization bug
------------------------------------------+---------------------------------
          Reporter:  [EMAIL PROTECTED]  |         Owner:  nobody
            Status:  new                  |     Milestone:        
         Component:  Uncategorized        |       Version:  1.0   
        Resolution:                       |      Keywords:        
             Stage:  Unreviewed           |     Has_patch:  0     
        Needs_docs:  0                    |   Needs_tests:  0     
Needs_better_patch:  0                    |  
------------------------------------------+---------------------------------
Changes (by [EMAIL PROTECTED]):

  * summary:  query optimization issue => query optimization bug

Comment:

 I think the issue with my hack was that it was not considering whether
 alias reference count was zero or not in found alias_map. I created a test
 case that proves that this issue exists in 1.0 (basically the model
 outline above):
 {{{
 class tableA(models.Model):
     key = models.AutoField(primary_key=True)

 class tableB(models.Model):
     key = models.ForeignKey(tableA)

 class tableC(models.Model):
     key = models.OneToOneField(tableA, primary_key=True)
     data = models.CharField(max_length=40, null=True)
 }}}

 Here is the doc test:
 {{{
 >>> tableB.objects.exclude(key__tablec__data__iexact='test').count()
 0
 }}}

 Here is the exception:
 {{{
 File "C:\Python25\Lib\site-
 packages\tests\regressiontests\queries\models.py", line ?, in
 regressiontests.queries.models.__test__.API_TESTS
 Failed example:
     tableB.objects.exclude(key__tablec__data__iexact='test').count()
 Exception raised:
     Traceback (most recent call last):
       File "C:\Python25\lib\site-packages\django\test\_doctest.py", line
 1267, in __run
         compileflags, 1) in test.globs
       File "<doctest
 regressiontests.queries.models.__test__.API_TESTS[47]>", line 1, in
 <module>
         tableB.objects.exclude(key__tablec__data__iexact='test').count()
       File "C:\Python25\lib\site-packages\django\db\models\query.py", line
 290, in count
         return self.query.get_count()
       File "C:\Python25\lib\site-packages\django\db\models\sql\query.py",
 line 237, in get_count
         data = obj.execute_sql(SINGLE)
       File "C:\Python25\lib\site-packages\django\db\models\sql\query.py",
 line 1720, in execute_sql
         cursor.execute(sql, params)
     ProgrammingError: missing FROM-clause entry in subquery for table "u1"
     LINE 1: ...ies_tableb" U0 INNER JOIN "queries_tablec" U2 ON (U1."key"
 =...
 }}}


 Here is the revised hack:
 {{{
     def get_from_clause(self):
         """
         Returns a list of strings that are joined together to go after the
         "FROM" part of the query, as well as a list any extra parameters
 that
         need to be included. Sub-classes, can override this to create a
         from-clause via a "select", for example (e.g. CountQuery).

         This should only be called after any SQL construction methods that
         might change the tables we need. This means the select columns and
         ordering must be done first.
         """
         result = []
         qn = self.quote_name_unless_alias
         qn2 = self.connection.ops.quote_name
         first = True
         local_alias_refcount = deepcopy(self.alias_refcount)
         for alias in self.tables:
             if not local_alias_refcount[alias]:
                 #START HACK: No sure if it is a problem with
 self.alias_refcount somewhere else or this code.
                 #      Need to iterate over self.alias_map to ensure that
 another alias map does not
                 #      reference an alias that has a reference count of
 zero.
                 for alias_check in self.tables:
                     #Check only alias maps that initially had a reference
 count.
                     if self.alias_refcount[alias_check] and alias !=
 alias_check:
                         try:
                             name, rhs, join_type, lhs, lhs_col, col,
 nullable = self.alias_map[alias_check]
                         except KeyError:
                             continue
                         if alias in [rhs,lhs]:
                             local_alias_refcount[alias] = 1
                 #END HACK

             if not local_alias_refcount[alias]:
                 continue

             #if not self.alias_refcount[alias]:
             #    continue

             try:
                 name, alias, join_type, lhs, lhs_col, col, nullable =
 self.alias_map[alias]
             except KeyError:
                 # Extra tables can end up in self.tables, but not in the
                 # alias_map if they aren't in a join. That's OK. We skip
 them.
                 continue
             alias_str = (alias != name and ' %s' % alias or '')
             if join_type and not first:
                 result.append('%s %s%s ON (%s.%s = %s.%s)'
                         % (join_type, qn(name), alias_str, qn(lhs),
                            qn2(lhs_col), qn(alias), qn2(col)))
             else:
                 connector = not first and ', ' or ''
                 result.append('%s%s%s' % (connector, qn(name), alias_str))
             first = False
         for t in self.extra_tables:
             alias, unused = self.table_alias(t)
             # Only add the alias if it's not already present (the
 table_alias()
             # calls increments the refcount, so an alias refcount of one
 means
             # this is the only reference.
             if alias not in self.alias_map or self.alias_refcount[alias]
 == 1:
                 connector = not first and ', ' or ''
                 result.append('%s%s' % (connector, qn(alias)))
                 first = False
         return result, []
 }}}

 Revised code passes all tests in:
 regressiontests.queries.models.__test__.API_TESTS

-- 
Ticket URL: <http://code.djangoproject.com/ticket/9342#comment:3>
Django <http://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to