﻿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
36152	Postgres backend could quote `%` in column aliases instead of failing at the db level	Jacob Walls		"When developing with postgres, if you are creating dynamic annotations, you have to know to reject `%` in the provided alias or transform it to `%%`, otherwise you end up with an unhelpful exception from the db adapter.

test case:
{{{#!diff
diff --git a/tests/annotations/tests.py b/tests/annotations/tests.py
index 29660a827e..6bc9bc9ed4 100644
--- a/tests/annotations/tests.py
+++ b/tests/annotations/tests.py
@@ -1171,6 +1171,9 @@ class NonAggregateAnnotationTestCase(TestCase):
                 with self.assertRaisesMessage(ValueError, msg):
                     Book.objects.annotate(**{crafted_alias: Value(1)})
 
+    def test_annotation_contains_percent(self):
+        Book.objects.annotate(**{""contains_percent%"": F(""pk"")}).first()
+
     @skipUnless(connection.vendor == ""postgresql"", ""PostgreSQL tests"")
     @skipUnlessDBFeature(""supports_json_field"")
     def test_set_returning_functions(self):
}}}

Gives on psycopg2:

{{{
IndexError: tuple index out of range
}}}

On psycopg 3, the errors are more helpful, either:
{{{
django.db.utils.ProgrammingError: only '%s', '%b', '%t' are allowed as placeholders, got '%""'
}}}

Or if the annotation indeed included %s:
{{{
django.db.utils.ProgrammingError: the query has 2 placeholders but 1 parameters were passed
}}}

But I still think we could easily ~~fail at the ORM level instead of going to the backend and failing there~~ just quote `%` to `%%`.

The Oracle backend [https://github.com/django/django/blob/efec74b90868c2e611f863bf4301d92ce08067e8/django/db/backends/oracle/operations.py#L377 does this for you]."	Cleanup/optimization	new	Database layer (models, ORM)	5.1	Normal				Unreviewed	0	0	0	0	0	0
