[Django] #34459: contrib.postgres.search.SearchVector.as_sql can return query strings that are unsafe to combine

5 views
Skip to first unread message

Django

unread,
Apr 4, 2023, 12:06:26 PM4/4/23
to django-...@googlegroups.com
#34459: contrib.postgres.search.SearchVector.as_sql can return query strings that
are unsafe to combine
--------------------------------------------+------------------------
Reporter: Patryk Zawadzki | Owner: (none)
Type: Bug | Status: new
Component: contrib.postgres | Version: 4.2
Severity: Normal | Keywords:
Triage Stage: Unreviewed | Has patch: 0
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
--------------------------------------------+------------------------
As the function specifically calls {{{connection.ops.compose_sql}}}, the
returned {{{sql}}} will have all parameters inlined.

An unintended consequence is that if you pass it a value that contains a
percent sign, like {{{Value("10% OFF")}}}, the resulting {{{sql}}} will
have the {{{%}}} character inlined. Such values will result in a
{{{ProgrammingError}}} as soon as you attempt to combine the SearchVector
with any expression that relies on {{{params}}}.

Depending on whether you use psycopg2 or psycopg 3, the resulting error
will tell you that there are not enough params to format the query
template or that there is an unescaped {{{%}}} in the query.

--
Ticket URL: <https://code.djangoproject.com/ticket/34459>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Apr 4, 2023, 12:48:56 PM4/4/23
to django-...@googlegroups.com
#34459: contrib.postgres.search.SearchVector.as_sql can return query strings that
are unsafe to combine
----------------------------------+------------------------------------

Reporter: Patryk Zawadzki | Owner: (none)
Type: Bug | Status: new
Component: contrib.postgres | Version: 4.2
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
----------------------------------+------------------------------------
Changes (by David Sanders):

* stage: Unreviewed => Accepted


Comment:

Tentatively accepting, I'm seeing errors but only when the args to
SearchVector are outside that which is documented to accept.

Felix & Simon will likely be able to clear things up.


So, to clarify, is this the behaviour you're seeing?

There's no issue if you filter by the search vector like so:

{{{
Foo.objects.annotate(search=SearchVector("text")).filter(search=Value("10%
off"))
}}}

It's when one of the arguments to SearchVector contain a percent that it
becomes an issue:

{{{
Foo.objects.annotate(search=SearchVector("text",
config="%")).filter(search=Value("10% off"))

...

django/db/models/query.py:373: in __repr__
data = list(self[: REPR_OUTPUT_SIZE + 1])
django/db/models/query.py:397: in __iter__
self._fetch_all()
django/db/models/query.py:1883: in _fetch_all
self._result_cache = list(self._iterable_class(self))
django/db/models/query.py:90: in __iter__
results = compiler.execute_sql(
django/db/models/sql/compiler.py:1560: in execute_sql
cursor.execute(sql, params)
django/db/backends/utils.py:67: in execute
return self._execute_with_wrappers(
django/db/backends/utils.py:80: in _execute_with_wrappers
return executor(sql, params, many, context)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <django.db.backends.utils.CursorWrapper object at 0x106e1bb20>
sql = 'SELECT "ticket_34459_foo"."id", "ticket_34459_foo"."text",
to_tsvector(\'%\'::regconfig,
COALESCE("ticket_34459_foo"....tsvector(\'%\'::regconfig,
COALESCE("ticket_34459_foo"."text", \'\')) @@
(plainto_tsquery(%s::regconfig, %s)) LIMIT 21'
params = ('%', '10% off')
ignored_wrapper_args = (False, {'connection': <DatabaseWrapper
vendor='postgresql' alias='default'>, 'cursor':
<django.db.backends.utils.CursorWrapper object at 0x106e1bb20>})

def _execute(self, sql, params, *ignored_wrapper_args):
self.db.validate_no_broken_transaction()
with self.db.wrap_database_errors:
if params is None:
# params default might be backend specific.
print(sql)
return self.cursor.execute(sql)
else:
> return self.cursor.execute(sql, params)
E IndexError: tuple index out of range

django/db/backends/utils.py:90: IndexError

}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/34459#comment:1>

Reply all
Reply to author
Forward
0 new messages