#36875: Avoid unnecessary Coalesce in Concat/ConcatPair
-------------------------------------+-------------------------------------
     Reporter:  David                |                     Type:
                                     |  Cleanup/optimization
       Status:  new                  |                Component:  Database
                                     |  layer (models, ORM)
      Version:  5.2                  |                 Severity:  Normal
     Keywords:  concat,coalese       |             Triage Stage:
                                     |  Unreviewed
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
 Currently `ConcatPair` (which is used by `Concat`) when invoking the
 `.coalesce` method it will force the `COALESCE` on every argument provided
 to the expression.

 
https://github.com/django/django/blob/3851601b2e080df34fb9227fe5d2fd43af604263/django/db/models/functions/text.py#L112-L122

 This does not take into account:

 - if any involved expression have nullable output (which I understand can
 be hard to check)
 - if any involved expression is a fixed value (which should easier to
 detect)

 The case in which I am more interested is the latter, because it is
 completely useless to invoke `COALESCE` on  a fixed value (unless it was
 provided `NULL` from the beginning, which should be easy to check).


 I came along this while writing a query to detect custom permissions on
 the database:

 {{{
 from django.contrib.auth.models import Permission
 from django.db.models import Value, F
 from django.db.models.functions import Concat

 custom_permissions = Permission.objects.exclude(
     codename__regex=Concat(
         Value("^(add|change|delete|view)_"),
         F("content_type__model"),
         Value("$"),
     )
 )
 }}}

 I was expecting a WHERE clause like:

 {{{
 "auth_permission"."codename"::text ~ ('^(add|change|delete|view)_' ||
 "django_content_type"."model" || '$')
 }}}

 And I was stunned when I saw the following (which is hard to read):

 {{{
 "auth_permission"."codename"::text ~
 (COALESCE('^(add|change|delete|view)_', '') ||
 COALESCE((COALESCE("django_content_type"."model", '') ||  COALESCE('$',
 '')), ''))
 }}}

 My proposal is to change how  `ConcatPair.coalesce` behaves to detect if
 `COALESCE` can be skipped (ie: fixed value, already enforced coalesce on
 previous expression, etc) before putting that function in the output.
  This could improve readability for generated queries and may reduce the
 overhad of building the `Coalesce` expression.
-- 
Ticket URL: <https://code.djangoproject.com/ticket/36875>
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 view this discussion visit 
https://groups.google.com/d/msgid/django-updates/0107019be1347555-72d603a6-de59-4343-84fa-af1133fa6108-000000%40eu-central-1.amazonses.com.

Reply via email to