Browse Source

Fixed #34103 -- Fixed logging SQL queries with duplicate parameters on Oracle.

David Sanders 2 years ago
parent
commit
64b3c413da
2 changed files with 25 additions and 8 deletions
  1. 8 8
      django/db/backends/oracle/operations.py
  2. 17 0
      tests/backends/tests.py

+ 8 - 8
django/db/backends/oracle/operations.py

@@ -323,16 +323,16 @@ END;
         # Unlike Psycopg's `query` and MySQLdb`'s `_executed`, cx_Oracle's
         # `statement` doesn't contain the query parameters. Substitute
         # parameters manually.
-        if isinstance(params, (tuple, list)):
-            for i, param in enumerate(reversed(params), start=1):
-                param_num = len(params) - i
-                statement = statement.replace(
-                    ":arg%d" % param_num, force_str(param, errors="replace")
-                )
-        elif isinstance(params, dict):
+        if params:
+            if isinstance(params, (tuple, list)):
+                params = {
+                    f":arg{i}": param for i, param in enumerate(dict.fromkeys(params))
+                }
+            elif isinstance(params, dict):
+                params = {f":{key}": val for (key, val) in params.items()}
             for key in sorted(params, key=len, reverse=True):
                 statement = statement.replace(
-                    ":%s" % key, force_str(params[key], errors="replace")
+                    key, force_str(params[key], errors="replace")
                 )
         return statement
 

+ 17 - 0
tests/backends/tests.py

@@ -142,6 +142,23 @@ class LastExecutedQueryTest(TestCase):
                 sql % params,
             )
 
+    def test_last_executed_query_with_duplicate_params(self):
+        square_opts = Square._meta
+        table = connection.introspection.identifier_converter(square_opts.db_table)
+        id_column = connection.ops.quote_name(square_opts.get_field("id").column)
+        root_column = connection.ops.quote_name(square_opts.get_field("root").column)
+        sql = f"UPDATE {table} SET {root_column} = %s + %s WHERE {id_column} = %s"
+        with connection.cursor() as cursor:
+            params = [42, 42, 1]
+            cursor.execute(sql, params)
+            last_executed_query = connection.ops.last_executed_query(
+                cursor, sql, params
+            )
+            self.assertEqual(
+                last_executed_query,
+                f"UPDATE {table} SET {root_column} = 42 + 42 WHERE {id_column} = 1",
+            )
+
 
 class ParameterHandlingTest(TestCase):
     def test_bad_parameter_count(self):