Opened 2 years ago
Closed 2 years ago
#33815 closed Bug (fixed)
last_executed_query() incorrectly substitutes parameters on Oracle.
Reported by: | Brian | Owned by: | Mariusz Felisiak |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Normal | Keywords: | oracle |
Cc: | Triage Stage: | Accepted | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Since Django 3.0, the variable substitution performed in the Oracle backend's last_executed_query
function can incorrectly track the sql statements issued to the backend. Here's the method copied from https://github.com/django/django/blob/main/django/db/backends/oracle/operations.py#L304:
def last_executed_query(self, cursor, sql, params): # https://cx-oracle.readthedocs.io/en/latest/api_manual/cursor.html#Cursor.statement # The DB API definition does not define this attribute. statement = cursor.statement # 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(params): statement = statement.replace( ":arg%d" % i, force_str(param, errors="replace") ) elif isinstance(params, dict): for key, param in params.items(): statement = statement.replace( ":%s" % key, force_str(param, errors="replace") ) return statement
The problem is that statement.replace
will end up replacing all matches in the statement, even those that are not a full match for the argument identifier. This can result in values that are a mashup of the subbed in value and the argument identifiers. For example, if you have values A-L that need to be substituted into a query, you'd have 12 arguments that would need to be substituted in, and the following scenario would occur:
Statement Pre-substitution:
SELECT "EMPLOYEE"."ID", "EMPLOYEE"."USERNAME", "EMPLOYEE"."NAME", "EMPLOYEE"."EMAIL" FROM "EMPLOYEE" WHERE "EMPLOYEE"."ID" IN (:arg0, :arg1, :arg2, :arg3, :arg4, :arg5, :arg6, :arg7, :arg8, :arg9 :arg10, :arg11)
Statement Post-substitution:
SELECT "EMPLOYEE"."ID", "EMPLOYEE"."USERNAME", "EMPLOYEE"."NAME", "EMPLOYEE"."EMAIL" FROM "EMPLOYEE" WHERE "EMPLOYEE"."ID" IN (A, B, C, D, E, F, G, H, I , J, B0, B1)
Expected Output:
SELECT "EMPLOYEE"."ID", "EMPLOYEE"."USERNAME", "EMPLOYEE"."NAME", "EMPLOYEE"."EMAIL" FROM "EMPLOYEE" WHERE "EMPLOYEE"."ID" IN (A, B, C, D, E, F, G, H, I , J, K, L)
Change History (4)
comment:1 by , 2 years ago
Component: | Uncategorized → Database layer (models, ORM) |
---|---|
Keywords: | oracle added |
Summary: | Oracle sql tracking incorrectly substitutes in parameters when 10 or more parameters are used. → last_executed_query() incorrectly substitutes parameters on Oracle. |
Triage Stage: | Unreviewed → Accepted |
Thanks for the report. I attached a small regression test:
tests/backends/tests.py