diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 1b6ba07f24..dd50229461 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -127,6 +127,27 @@ class BaseDatabaseOperations(object): """ raise NotImplementedError('Full-text search is not implemented for this database backend') + def last_executed_query(self, cursor, sql, params): + """ + Returns a string of the query last executed by the given cursor, with + placeholders replaced with actual values. + + `sql` is the raw query containing placeholders, and `params` is the + sequence of parameters. These are used by default, but this method + exists for database backends to provide a better implementation + according to their own quoting schemes. + """ + from django.utils.encoding import smart_unicode, force_unicode + + # Convert params to contain Unicode values. + to_unicode = lambda s: force_unicode(s, strings_only=True) + if isinstance(params, (list, tuple)): + u_params = tuple([to_unicode(val) for val in params]) + else: + u_params = dict([(to_unicode(k), to_unicode(v)) for k, v in params.items()]) + + return smart_unicode(sql) % u_params + def last_insert_id(self, cursor, table_name, pk_name): """ Given a cursor object that has just performed an INSERT statement into diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py index a7b080d505..d7b3558344 100644 --- a/django/db/backends/postgresql_psycopg2/base.py +++ b/django/db/backends/postgresql_psycopg2/base.py @@ -5,7 +5,7 @@ Requires psycopg 2: http://initd.org/projects/psycopg2 """ from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures -from django.db.backends.postgresql.operations import DatabaseOperations +from django.db.backends.postgresql.operations import DatabaseOperations as PostgresqlDatabaseOperations try: import psycopg2 as Database import psycopg2.extensions @@ -21,6 +21,13 @@ psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) class DatabaseFeatures(BaseDatabaseFeatures): needs_datetime_string_cast = False +class DatabaseOperations(PostgresqlDatabaseOperations): + def last_executed_query(self, cursor, sql, params): + # With psycopg2, cursor objects have a "query" attribute that is the + # exact query sent to the database. See docs here: + # http://www.initd.org/tracker/psycopg/wiki/psycopg2_documentation#postgresql-status-message-and-executed-query + return cursor.query + class DatabaseWrapper(BaseDatabaseWrapper): features = DatabaseFeatures() ops = DatabaseOperations() diff --git a/django/db/backends/util.py b/django/db/backends/util.py index fa2df22927..ca4e90d6c2 100644 --- a/django/db/backends/util.py +++ b/django/db/backends/util.py @@ -1,7 +1,6 @@ import datetime import md5 from time import time -from django.utils.encoding import smart_unicode, force_unicode try: import decimal @@ -11,7 +10,7 @@ except ImportError: class CursorDebugWrapper(object): def __init__(self, cursor, db): self.cursor = cursor - self.db = db + self.db = db # Instance of a BaseDatabaseWrapper subclass def execute(self, sql, params=()): start = time() @@ -19,8 +18,9 @@ class CursorDebugWrapper(object): return self.cursor.execute(sql, params) finally: stop = time() + sql = self.db.ops.last_executed_query(self.cursor, sql, params) self.db.queries.append({ - 'sql': smart_unicode(sql) % convert_args(params), + 'sql': sql, 'time': "%.3f" % (stop - start), }) @@ -31,7 +31,7 @@ class CursorDebugWrapper(object): finally: stop = time() self.db.queries.append({ - 'sql': 'MANY: ' + sql + ' ' + smart_unicode(tuple(param_list)), + 'sql': '%s times: %s' % (len(param_list), sql), 'time': "%.3f" % (stop - start), }) @@ -41,16 +41,6 @@ class CursorDebugWrapper(object): else: return getattr(self.cursor, attr) -def convert_args(args): - """ - Convert sequence or dictionary to contain unicode values. - """ - to_unicode = lambda s: force_unicode(s, strings_only=True) - if isinstance(args, (list, tuple)): - return tuple([to_unicode(val) for val in args]) - else: - return dict([(to_unicode(k), to_unicode(v)) for k, v in args.items()]) - ############################################### # Converters from database (string) to Python # ###############################################