#34459: SearchVector() 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:  Release blocker   |               Resolution:
     Keywords:                    |             Triage Stage:  Accepted
    Has patch:  0                 |      Needs documentation:  0
  Needs tests:  0                 |  Patch needs improvement:  0
Easy pickings:  0                 |                    UI/UX:  0
----------------------------------+------------------------------------

Comment (by Mariusz Felisiak):

 We use `compose_sql()` in different places. Unfortunately, it's sometimes
 used together with uncomposed SQL statements causing issues when binding
 parameters. IMO, we should escape `%` when we know that the parameters are
 bound later, e.g.

 {{{#!diff
 diff --git a/django/contrib/postgres/search.py
 b/django/contrib/postgres/search.py
 index 4e370aa167..279a39e80e 100644
 --- a/django/contrib/postgres/search.py
 +++ b/django/contrib/postgres/search.py
 @@ -146,7 +146,9 @@ class SearchVector(SearchVectorCombinable, Func):

          # These parameters must be bound on the client side because we
 may
          # want to create an index on this expression.
 -        sql = connection.ops.compose_sql(sql, config_params + params +
 extra_params)
 +        sql = connection.ops.compose_sql(
 +            sql, config_params + params + extra_params, quote_params=True
 +        )
          return sql, []


 @@ -321,7 +323,9 @@ class SearchHeadline(Func):
          if self.options:
              options_params.append(
                  ", ".join(
 -                    connection.ops.compose_sql(f"{option}=%s", [value])
 +                    connection.ops.compose_sql(
 +                        f"{option}=%s", [value], quote_params=True
 +                    )
                      for option, value in self.options.items()
                  )
              )
 diff --git a/django/db/backends/postgresql/operations.py
 b/django/db/backends/postgresql/operations.py
 index 18cfcb29cb..8aa8bb5173 100644
 --- a/django/db/backends/postgresql/operations.py
 +++ b/django/db/backends/postgresql/operations.py
 @@ -201,7 +201,14 @@ class DatabaseOperations(BaseDatabaseOperations):
              return name  # Quoting once is enough.
          return '"%s"' % name

 -    def compose_sql(self, sql, params):
 +    def quote_value(self, value):
 +        if isinstance(value, str):
 +            value = value.replace("%", "%%")
 +        return value
 +
 +    def compose_sql(self, sql, params, quote_params=False):
 +        if quote_params and params:
 +            params = [self.quote_value(param) for param in params]
          return mogrify(sql, params, self.connection)

      def set_time_zone_sql(self):
 }}}
 Patryk, does it work for you?

-- 
Ticket URL: <https://code.djangoproject.com/ticket/34459#comment:5>
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/010701875068b052-09c0a72d-2ea6-498c-bfe3-6e3165b1b91a-000000%40eu-central-1.amazonses.com.

Reply via email to