Refs #20010 -- Unified DatabaseOperations.last_executed_query() on Oracle with other db backends.
Thanks Simon Charette for the review.
This commit is contained in:
parent
6b4e57d79f
commit
79065b55a7
|
@ -8,7 +8,7 @@ from django.db.backends.base.operations import BaseDatabaseOperations
|
||||||
from django.db.backends.utils import strip_quotes, truncate_name
|
from django.db.backends.utils import strip_quotes, truncate_name
|
||||||
from django.db.utils import DatabaseError
|
from django.db.utils import DatabaseError
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.encoding import force_bytes
|
from django.utils.encoding import force_bytes, force_str
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
from .base import Database
|
from .base import Database
|
||||||
|
@ -258,9 +258,16 @@ END;
|
||||||
# https://cx-oracle.readthedocs.io/en/latest/cursor.html#Cursor.statement
|
# https://cx-oracle.readthedocs.io/en/latest/cursor.html#Cursor.statement
|
||||||
# The DB API definition does not define this attribute.
|
# The DB API definition does not define this attribute.
|
||||||
statement = cursor.statement
|
statement = cursor.statement
|
||||||
# Unlike Psycopg's `query` and MySQLdb`'s `_executed`, CxOracle's
|
# Unlike Psycopg's `query` and MySQLdb`'s `_executed`, cx_Oracle's
|
||||||
# `statement` doesn't contain the query parameters. refs #20010.
|
# `statement` doesn't contain the query parameters. Substitute
|
||||||
return super().last_executed_query(cursor, statement, params)
|
# 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
|
||||||
|
|
||||||
def last_insert_id(self, cursor, table_name, pk_name):
|
def last_insert_id(self, cursor, table_name, pk_name):
|
||||||
sq_name = self._get_sequence_name(cursor, strip_quotes(table_name), pk_name)
|
sq_name = self._get_sequence_name(cursor, strip_quotes(table_name), pk_name)
|
||||||
|
|
|
@ -51,7 +51,7 @@ class DateQuotingTest(TestCase):
|
||||||
@override_settings(DEBUG=True)
|
@override_settings(DEBUG=True)
|
||||||
class LastExecutedQueryTest(TestCase):
|
class LastExecutedQueryTest(TestCase):
|
||||||
|
|
||||||
def test_last_executed_query(self):
|
def test_last_executed_query_without_previous_query(self):
|
||||||
"""
|
"""
|
||||||
last_executed_query should not raise an exception even if no previous
|
last_executed_query should not raise an exception even if no previous
|
||||||
query has been run.
|
query has been run.
|
||||||
|
@ -73,6 +73,36 @@ class LastExecutedQueryTest(TestCase):
|
||||||
last_sql = cursor.db.ops.last_executed_query(cursor, sql, params)
|
last_sql = cursor.db.ops.last_executed_query(cursor, sql, params)
|
||||||
self.assertIsInstance(last_sql, str)
|
self.assertIsInstance(last_sql, str)
|
||||||
|
|
||||||
|
def test_last_executed_query(self):
|
||||||
|
# last_executed_query() interpolate all parameters, in most cases it is
|
||||||
|
# not equal to QuerySet.query.
|
||||||
|
for qs in (
|
||||||
|
Article.objects.filter(pk=1),
|
||||||
|
Article.objects.filter(pk__in=(1, 2), reporter__pk=3),
|
||||||
|
):
|
||||||
|
sql, params = qs.query.sql_with_params()
|
||||||
|
cursor = qs.query.get_compiler(DEFAULT_DB_ALIAS).execute_sql(CURSOR)
|
||||||
|
self.assertEqual(
|
||||||
|
cursor.db.ops.last_executed_query(cursor, sql, params),
|
||||||
|
str(qs.query),
|
||||||
|
)
|
||||||
|
|
||||||
|
@skipUnlessDBFeature('supports_paramstyle_pyformat')
|
||||||
|
def test_last_executed_query_dict(self):
|
||||||
|
square_opts = Square._meta
|
||||||
|
sql = 'INSERT INTO %s (%s, %s) VALUES (%%(root)s, %%(square)s)' % (
|
||||||
|
connection.introspection.identifier_converter(square_opts.db_table),
|
||||||
|
connection.ops.quote_name(square_opts.get_field('root').column),
|
||||||
|
connection.ops.quote_name(square_opts.get_field('square').column),
|
||||||
|
)
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
params = {'root': 2, 'square': 4}
|
||||||
|
cursor.execute(sql, params)
|
||||||
|
self.assertEqual(
|
||||||
|
cursor.db.ops.last_executed_query(cursor, sql, params),
|
||||||
|
sql % params,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ParameterHandlingTest(TestCase):
|
class ParameterHandlingTest(TestCase):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue