#26522: Non-deterministic crash in django.db.models.sql.Query.combine()
-------------------------------------+-------------------------------------
Reporter: Ole Laursen | Owner: nobody
Type: Bug | Status: closed
Component: Database layer | Version: 1.9
(models, ORM) |
Severity: Normal | Resolution: fixed
Keywords: | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Mikalai Radchuk):
It's a very tricky issue. I think, it can damage data in some cases.
In my case Django ORM didn't fail with assertion error: it generated wrong
SQL query so I got wrong data from my DB. On development environment I
didn't get this issue because it appears randomly. I'm a lucky person - in
my case I query for some data for UI, but if you perform some calculations
using data from a queryset that silently returns wrong data - you are in
trouble. It's quite possible that 4/5 of your processes work properly, but
one gives wrong results.
Here is simplified version of my issue.
Models:
{{{
#!python
from django.db import models
class Category(models.Model):
title = models.CharField(max_length=255, unique=True)
class Meta:
verbose_name_plural = 'categories'
def __str__(self):
return self.title
class Document(models.Model):
title = models.CharField(blank=True, max_length=255)
categories = models.ManyToManyField('Category', blank=True,
related_name='+')
def __str__(self):
return self.title
}}}
Build a query set that could return wrong data:
{{{
#!python
from app_with_models.models import Document, Category
queryset = Document.objects.all()
# Exclude documents without taxonomy (Category) tags
queryset = queryset.filter(categories__in=Category.objects.all())
# Apply predefined taxonomy filters
category_predefined = [1] # Should be in fixtures
queryset = queryset.filter(categories__in=category_predefined)
queryset = queryset.distinct()
used_category_ids = queryset.values_list('categories__pk', flat=True)
print(used_category_ids.query)
}}}
This code should generate the following SQL query (formatted):
{{{
#!sql
SELECT DISTINCT
"app_with_models_document_categories"."category_id"
FROM "app_with_models_document"
LEFT OUTER JOIN "app_with_models_document_categories" ON
("app_with_models_document"."id" =
"app_with_models_document_categories"."document_id")
INNER JOIN "app_with_models_document_categories" T4 ON
("app_with_models_document"."id" = T4."document_id")
WHERE (
"app_with_models_document_categories"."category_id" IN (SELECT U0."id"
AS Col1 FROM "app_with_models_category" U0)
AND T4."category_id" IN (1)
);
}}}
But in some cases it generates this:
{{{
#!sql
SELECT DISTINCT
T4."category_id"
FROM "app_with_models_document"
LEFT OUTER JOIN "app_with_models_document_categories" ON
("app_with_models_document"."id" =
"app_with_models_document_categories"."document_id")
INNER JOIN "app_with_models_document_categories" T4 ON
("app_with_models_document"."id" = T4."document_id")
WHERE (
"app_with_models_document_categories"."category_id" IN (SELECT U0."id"
AS Col1 FROM "app_with_models_category" U0)
AND T4."category_id" IN (1)
);
}}}
This query generates absolutely wrong results. In my example it will
always return `1` as `category_id`.
Some more details:
* Can be reproduced on `Django==1.10.7`, `Django==1.11.4` and, most
likely, on order versions too. Fixed in
9bbb6e2d2536c4ac20dc13a94c1f80494e51f8d9
* Can be reproduced on Python 3.5, but not on Python 3.6 (at least, I
didn't manage to do that). I guess it's because of
[https://docs.python.org/3/whatsnew/3.6.html#new-dict-implementation new
dict implementation].
* I've tested with Postgres 9.6.3 and `psycopg2==2.6.1` and
`psycopg2==2.7.3.1`, but it looks like the issue is not DB engine/adapter
specific.
Hope it helps.
Can it be backported into supported (or at least into LTS)? I would really
appreciate it. Me an my colleague have spent 4 days debugging our code an
I'm afraid that it could cause more serious issues for other people.
--
Ticket URL: <https://code.djangoproject.com/ticket/26522#comment:12>
Django <https://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 unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/068.dce1a80774207ba0e215cacb9c884167%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.