Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nulls_first or nulls_last ordering produces incorrect SQL Server syntax #31

Closed
davidjb opened this issue Apr 19, 2021 · 7 comments · Fixed by #32
Closed

nulls_first or nulls_last ordering produces incorrect SQL Server syntax #31

davidjb opened this issue Apr 19, 2021 · 7 comments · Fixed by #32

Comments

@davidjb
Copy link
Contributor

davidjb commented Apr 19, 2021

Using the latest dev (f2d27ee), attempts to use nulls_first or nulls_last ordering produce invalid SQL.

#19 reports a related issue, but the change in #20 that modifies the database backend's features causes Django's other conditional pathway at https://github.com/django/django/blob/main/django/db/models/expressions.py#L1214-L1227 to be followed, resulting in invalid SQL being included in the SQL template, leading to the following syntax error:

Incorrect syntax near the keyword 'IS'

caused by of either of the following SQL statements that get created by sqlserver_orderby are invalid syntax:

  • .desc(nulls_first=True)

    '%(expression)s IS NOT NULL, CASE WHEN %(expression)s IS NULL THEN 0 ELSE 1 END, %(expression)s %(ordering)s'
  • .asc(nulls_last=True)

    '%(expression)s IS NULL, CASE WHEN %(expression)s IS NULL THEN 1 ELSE 0 END, %(expression)s %(ordering)s'

Tested with Django 3.1.8 but appears to still affect 3.2+. I believe https://code.djangoproject.com/ticket/32584 needs to be reopened because the template is still being overwritten, unless there's a better strategy for overriding Django's core behaviour. @timnyborg, do you have any thoughts?

Example Django model illustrating the issue:

from django.db import models

class Event(models.Model):
    # ... fields go here ...

    class Meta(PersonMetadata.Meta):
        ordering = (
            F("end_year").desc(nulls_first=True),
            F("start_year").asc(nulls_last=True),
        )

Event.objects.first()  # Incorrect syntax near the keyword 'IS'
@davidjb
Copy link
Contributor Author

davidjb commented Apr 19, 2021

Reopened https://code.djangoproject.com/ticket/32584 and expanded the description. Thanks @timnyborg for starting that.

@timnyborg
Copy link
Contributor

Thanks for reopening - I hadn't encountered that particular case.

davidjb added a commit to davidjb/mssql-django that referenced this issue Apr 20, 2021
By making a copy of the OrderBy expression and disabling its
nulls_first/nulls_last flags, this prevents Django's core OrderBy.as_sql
function from modifying the custom templates SQL Server requires.

See <https://code.djangoproject.com/ticket/32584> for details on why
this isn't implemented as a feature flag.

Fixes microsoft#31
@davidjb
Copy link
Contributor Author

davidjb commented Apr 20, 2021

@timnyborg I just opened #32 to resolve this issue

@absci
Copy link
Contributor

absci commented Apr 21, 2021

Thanks for all your work. I'll review and merge it if everything's good.

@davidjb
Copy link
Contributor Author

davidjb commented May 7, 2021

@absci Just wondering if you've had a chance to review this PR. Without it, I'm unable to query Django with this ordering enabled on models.

@absci absci closed this as completed in #32 May 7, 2021
@absci
Copy link
Contributor

absci commented May 7, 2021

@absci Just wondering if you've had a chance to review this PR. Without it, I'm unable to query Django with this ordering enabled on models.

Looks very good, I have merged it. I got busy with other things recently.

@davidjb
Copy link
Contributor Author

davidjb commented May 7, 2021

No worries, thanks very much @absci!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants