#35028: psycopg3: distinct query iteration causes
psycopg.errors.InvalidColumnReference: for SELECT DISTINCT, ORDER BY
expressions must appear in select list
-------------------------------------+-------------------------------------
Reporter: Richard Ebeling | Owner: nobody
Type: Bug | Status: new
Component: Database layer | Version: 5.0
(models, ORM) |
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Description changed by Richard Ebeling:
Old description:
> Using PostgreSQL, after upgrading from psycopg2 to psycopg3
> (`psycopg[binary]==3.1.14`), iterating a distinct queryset of a model
> with an ordering that uses `Value` expressions causes
> `psycopg.errors.InvalidColumnReference` error saying that the `ORDER BY`
> expression must appear in the select list.
>
> This is a minimal reproducible example:
> {{{
> ### MODELS
>
> from django.db import models
>
> class TestModel(models.Model):
> test_field = models.CharField()
>
> class Meta:
> ordering = [NullIf("test_field", Value(""))]
>
> ### TEST
>
> from name.models import TestModel
> from django.test import TestCase
>
> class TestTestModel(TestCase):
> def test_iterating(self):
> for el in TestModel.objects.all().distinct().iterator():
> pass
> }}}
>
> which gives this output when running:
> {{{
> $ ./manage.py test -k test_iterating
> Found 1 test(s).
> Creating test database for alias 'default'...
> System check identified no issues (0 silenced).
> E
> ======================================================================
> ERROR: test_iterating (name.tests.test_models.TestTestModel)
> ----------------------------------------------------------------------
> Traceback (most recent call last):
> File "/.../venv/lib/python3.10/site-
> packages/django/db/backends/utils.py", line 105, in _execute
> return self.cursor.execute(sql, params)
> File "/.../venv/lib/python3.10/site-packages/psycopg/server_cursor.py",
> line 294, in execute
> raise ex.with_traceback(None)
> psycopg.errors.InvalidColumnReference: for SELECT DISTINCT, ORDER BY
> expressions must appear in select list
> LINE 1: ..._field", $1) FROM "evaluation_testmodel" ORDER BY
> NULLIF("ev...
> ^
>
> The above exception was the direct cause of the following exception:
>
> Traceback (most recent call last):
> File ".../tests/test_models.py", line 43, in test_iterating
> for el in TestModel.objects.all().distinct().iterator():
> File "/.../venv/lib/python3.10/site-
> packages/django/db/models/query.py", line 516, in _iterator
> yield from iterable
> File "/.../venv/lib/python3.10/site-
> packages/django/db/models/query.py", line 91, in __iter__
> results = compiler.execute_sql(
> File "/.../venv/lib/python3.10/site-
> packages/django/db/models/sql/compiler.py", line 1562, in execute_sql
> cursor.execute(sql, params)
> File "/.../venv/lib/python3.10/site-
> packages/django/db/backends/utils.py", line 79, in execute
> return self._execute_with_wrappers(
> File "/.../venv/lib/python3.10/site-
> packages/django/db/backends/utils.py", line 92, in _execute_with_wrappers
> return executor(sql, params, many, context)
> File "/.../venv/lib/python3.10/site-
> packages/django/db/backends/utils.py", line 100, in _execute
> with self.db.wrap_database_errors:
> File "/.../venv/lib/python3.10/site-packages/django/db/utils.py", line
> 91, in __exit__
> raise dj_exc_value.with_traceback(traceback) from exc_value
> File "/.../venv/lib/python3.10/site-
> packages/django/db/backends/utils.py", line 105, in _execute
> return self.cursor.execute(sql, params)
> File "/.../venv/lib/python3.10/site-packages/psycopg/server_cursor.py",
> line 294, in execute
> raise ex.with_traceback(None)
> django.db.utils.ProgrammingError: for SELECT DISTINCT, ORDER BY
> expressions must appear in select list
> LINE 1: ..._field", $1) FROM "evaluation_testmodel" ORDER BY
> NULLIF("ev...
> ^
>
> ----------------------------------------------------------------------
> Ran 1 test in 0.010s
>
> FAILED (errors=1)
> Destroying test database for alias 'default'...
> }}}
>
> Removing the `.distinct()` call prevents the error.
>
> This is the relevant excerpt from the postgres log:
> {{{
> 2023-12-11 19:15:15.900 UTC [18994] name@test_name ERROR: for SELECT
> DISTINCT, ORDER BY expressions must appear in select list at character
> 235
> 2023-12-11 19:15:15.900 UTC [18994] name@test_name STATEMENT: DECLARE
> "_django_curs_139772156350464_sync_1" NO SCROLL CURSOR FOR SELECT
> DISTINCT "name_testmodel"."id", "name_testmodel"."test_field",
> NULLIF("name_testmodel"."test_field", $1) FROM "name_testmodel" ORDER BY
> NULLIF("name_testmodel"."test_field", $2) ASC
> }}}
>
> When using psycopg2, this is the resulting postgres log (without errors):
> {{{
> 2023-12-11 19:19:32.456 UTC [19038] name@test_name LOG: statement:
> DECLARE "_django_curs_140476300926976_sync_1" NO SCROLL CURSOR WITHOUT
> HOLD FOR SELECT DISTINCT "name_testmodel"."id",
> "name_testmodel"."test_field", NULLIF("name_testmodel"."test_field", '')
> FROM "name_testmodel" ORDER BY NULLIF("name_testmodel"."test_field", '')
> ASC
> 2023-12-11 19:19:32.456 UTC [19038] name@test_name LOG: statement: FETCH
> FORWARD 2000 FROM "_django_curs_140476300926976_sync_1"
> 2023-12-11 19:19:32.456 UTC [19038] name@test_name LOG: statement: CLOSE
> "_django_curs_140476300926976_sync_1"
> }}}
New description:
Using PostgreSQL, after upgrading from psycopg2 to psycopg3
(`psycopg[binary]==3.1.14`), iterating a distinct queryset of a model with
an ordering that uses `Value` expressions causes
`psycopg.errors.InvalidColumnReference` error saying that the `ORDER BY`
expression must appear in the select list.
This is a minimal reproducible example:
{{{
### MODELS
from django.db import models
class TestModel(models.Model):
test_field = models.CharField()
class Meta:
ordering = [NullIf("test_field", Value(""))]
### TEST
from name.models import TestModel
from django.test import TestCase
class TestTestModel(TestCase):
def test_iterating(self):
for el in TestModel.objects.all().distinct().iterator():
pass
}}}
which gives this output when running:
{{{
$ ./manage.py test -k test_iterating
Found 1 test(s).
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
E
======================================================================
ERROR: test_iterating (name.tests.test_models.TestTestModel)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/.../venv/lib/python3.10/site-
packages/django/db/backends/utils.py", line 105, in _execute
return self.cursor.execute(sql, params)
File "/.../venv/lib/python3.10/site-packages/psycopg/server_cursor.py",
line 294, in execute
raise ex.with_traceback(None)
psycopg.errors.InvalidColumnReference: for SELECT DISTINCT, ORDER BY
expressions must appear in select list
LINE 1: ..._field", $1) FROM "name_testmodel" ORDER BY NULLIF("ev...
^
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File ".../tests/test_models.py", line 43, in test_iterating
for el in TestModel.objects.all().distinct().iterator():
File "/.../venv/lib/python3.10/site-packages/django/db/models/query.py",
line 516, in _iterator
yield from iterable
File "/.../venv/lib/python3.10/site-packages/django/db/models/query.py",
line 91, in __iter__
results = compiler.execute_sql(
File "/.../venv/lib/python3.10/site-
packages/django/db/models/sql/compiler.py", line 1562, in execute_sql
cursor.execute(sql, params)
File "/.../venv/lib/python3.10/site-
packages/django/db/backends/utils.py", line 79, in execute
return self._execute_with_wrappers(
File "/.../venv/lib/python3.10/site-
packages/django/db/backends/utils.py", line 92, in _execute_with_wrappers
return executor(sql, params, many, context)
File "/.../venv/lib/python3.10/site-
packages/django/db/backends/utils.py", line 100, in _execute
with self.db.wrap_database_errors:
File "/.../venv/lib/python3.10/site-packages/django/db/utils.py", line
91, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/.../venv/lib/python3.10/site-
packages/django/db/backends/utils.py", line 105, in _execute
return self.cursor.execute(sql, params)
File "/.../venv/lib/python3.10/site-packages/psycopg/server_cursor.py",
line 294, in execute
raise ex.with_traceback(None)
django.db.utils.ProgrammingError: for SELECT DISTINCT, ORDER BY
expressions must appear in select list
LINE 1: ..._field", $1) FROM "name_testmodel" ORDER BY NULLIF("ev...
^
----------------------------------------------------------------------
Ran 1 test in 0.010s
FAILED (errors=1)
Destroying test database for alias 'default'...
}}}
Removing the `.distinct()` call prevents the error.
This is the relevant excerpt from the postgres log:
{{{
2023-12-11 19:15:15.900 UTC [18994] name@test_name ERROR: for SELECT
DISTINCT, ORDER BY expressions must appear in select list at character 235
2023-12-11 19:15:15.900 UTC [18994] name@test_name STATEMENT: DECLARE
"_django_curs_139772156350464_sync_1" NO SCROLL CURSOR FOR SELECT DISTINCT
"name_testmodel"."id", "name_testmodel"."test_field",
NULLIF("name_testmodel"."test_field", $1) FROM "name_testmodel" ORDER BY
NULLIF("name_testmodel"."test_field", $2) ASC
}}}
When using psycopg2, this is the resulting postgres log (without errors):
{{{
2023-12-11 19:19:32.456 UTC [19038] name@test_name LOG: statement:
DECLARE "_django_curs_140476300926976_sync_1" NO SCROLL CURSOR WITHOUT
HOLD FOR SELECT DISTINCT "name_testmodel"."id",
"name_testmodel"."test_field", NULLIF("name_testmodel"."test_field", '')
FROM "name_testmodel" ORDER BY NULLIF("name_testmodel"."test_field", '')
ASC
2023-12-11 19:19:32.456 UTC [19038] name@test_name LOG: statement: FETCH
FORWARD 2000 FROM "_django_curs_140476300926976_sync_1"
2023-12-11 19:19:32.456 UTC [19038] name@test_name LOG: statement: CLOSE
"_django_curs_140476300926976_sync_1"
}}}
--
--
Ticket URL: <https://code.djangoproject.com/ticket/35028#comment:1>
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 on the web visit
https://groups.google.com/d/msgid/django-updates/0107018c5a5ce4c3-f6236a7a-0775-40c7-8ee5-933066c55b40-000000%40eu-central-1.amazonses.com.