From c599de2ac63033fd6d027b34f1230ee574118ddb Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Tue, 23 Oct 2007 19:00:31 +0000 Subject: [PATCH] Added a BaseDatabaseOperations.last_executed_query() hook, which allows a database backend to specify how to get the last-executed query on a given cursor. Implemented it for the psycopg2 backend. This means that for psycopg2, the SQL statements in django.db.connection.queries will now reflect the exact SQL as sent to the server, instead of a naive and misleading string-interpolated version git-svn-id: http://code.djangoproject.com/svn/django/trunk@6601 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/db/backends/__init__.py | 21 +++++++++++++++++++ .../db/backends/postgresql_psycopg2/base.py | 9 +++++++- django/db/backends/util.py | 18 ++++------------ 3 files changed, 33 insertions(+), 15 deletions(-) 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 # ###############################################