#25517: Concat() database function is not idempotent in SQLite -------------------------------------+------------------------------------- Reporter: wsmith323 | Owner: wsmith323 Type: Bug | Status: assigned Component: Database layer | Version: master (models, ORM) | Severity: Normal | Resolution: Keywords: concat coalesce | Triage Stage: sqlite | Unreviewed Has patch: 0 | Needs documentation: 0 Needs tests: 0 | Patch needs improvement: 0 Easy pickings: 0 | UI/UX: 0 -------------------------------------+------------------------------------- Changes (by wsmith323):
* owner: nobody => wsmith323 * status: new => assigned Old description: > When running on sqlite, with a `Concat()` expression used in the query, > the `ConcatPair.coalesce()` method is called every time the SQL is > generated. This is normal, expected, and AFAIK, correct behavior. > > The problem is that `ConcatPair.coalesce()` is not idempotent. So, > **//every//** time the SQL is generated, each expression within the > {{{ConcatPair}}} instance is wrapped with an additional `COALESCE()` SQL > function. If generated enough times, the SQL can reach a point where it > will crash the sqlite query parser. > > This problem may not manifest itself in a typical request/response > context, as the SQL with the additional `COALESCE()` calls will work > identically to the original and the SQL may not be re-generated a > sufficient number of times to crash the parser. > > However, in a long-running process (where this bug was found), it can be > easily triggered. For example, say I have a "base" queryset with a > `Concat()` within an `.annotate()`. I never actually evaluate this > queryset, but I use it to construct other querysets which I do evaluate. > Because all of these querysets share the same instance of > `Query._annotations`, evaluating ANY of these querysets will add an > additional level of `COALESCE()` to the SQL generated by the others. > > I have a fix coded. I will submit a PR shortly. New description: When running on sqlite, with a `Concat()` expression used in the query, the `ConcatPair.coalesce()` method is called every time the SQL is generated. This is normal, expected, and AFAIK, correct behavior. The problem is that `ConcatPair.coalesce()` is not idempotent. So, **//every//** time the SQL is generated, each expression within the {{{ConcatPair}}} instance is wrapped with an additional `COALESCE()` SQL function. If generated enough times, the SQL can reach a point where it will crash the sqlite query parser. This problem may not manifest itself in a typical request/response context, as the SQL with the additional `COALESCE()` calls will work identically to the original and the SQL may not be re-generated a sufficient number of times to crash the parser. However, in a long-running process (where this bug was found), it can be easily triggered. For example, say I have a "base" queryset with a `Concat()` within an `.annotate()`. I never actually evaluate this queryset, but I use it to construct other querysets which I do evaluate. Because all of these querysets share the same instance of `Query._annotations`, evaluating ANY of these querysets will add an additional level of `COALESCE()` to the SQL generated by the others. -- -- Ticket URL: <https://code.djangoproject.com/ticket/25517#comment:3> 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 django-updates+unsubscr...@googlegroups.com. To post to this group, send email to django-updates@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-updates/067.30b0e6af437b915ae297fece685e2e70%40djangoproject.com. For more options, visit https://groups.google.com/d/optout.