﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
37438	Negated XOR conditions incorrectly use INNER JOIN for nullable relations	Pravin	Pravin	"Given the following models:

{{{
class Detail(models.Model):
    first = models.CharField(max_length=20)


class Item(models.Model):
    detail = models.ForeignKey(
        Detail,
        models.CASCADE,
        null=True,
    )
}}}

The following query should return both items:

{{{
from django.db.models import Q

detail = Detail.objects.create(first=""match"")
with_detail = Item.objects.create(detail=detail)
without_detail = Item.objects.create()

items = Item.objects.filter(
    ~(
        Q(detail__first__isnull=True)
        ^ Q(pk=without_detail.pk)
    )
).order_by(""pk"")

print(list(items.values_list(""pk"", flat=True)))
}}}

For {{{with_detail}}}, {{{detail__first__isnull}}} and
{{{pk=without_detail.pk}}} are both false.

For {{{without_detail}}}, the missing {{{detail}}} produces a {{{NULL}}}
value and its primary key matches {{{without_detail.pk}}}, so both conditions
are true.

A negated XOR matches when both conditions have the same result. Therefore,
both items should be returned.

Expected result:

{{{
[with_detail.pk, without_detail.pk]
}}}

Actual result:

{{{
[with_detail.pk]
}}}

{{{JoinPromoter}}} doesn't handle {{{XOR}}} separately. For join promotion,
a negated XOR is treated as an {{{AND}}}, so an {{{INNER JOIN}}} is considered
safe. The inner join removes {{{without_detail}}} before the conditions are
evaluated."	Bug	assigned	Database layer (models, ORM)	dev	Normal		XOR, JoinPromoter	Pravin	Unreviewed	0	0	0	0	0	0
