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
This commit is contained in:
Adrian Holovaty 2007-10-23 19:00:31 +00:00
parent 17301f2fa0
commit c599de2ac6
3 changed files with 33 additions and 15 deletions

View File

@ -127,6 +127,27 @@ class BaseDatabaseOperations(object):
""" """
raise NotImplementedError('Full-text search is not implemented for this database backend') 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): def last_insert_id(self, cursor, table_name, pk_name):
""" """
Given a cursor object that has just performed an INSERT statement into Given a cursor object that has just performed an INSERT statement into

View File

@ -5,7 +5,7 @@ Requires psycopg 2: http://initd.org/projects/psycopg2
""" """
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures 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: try:
import psycopg2 as Database import psycopg2 as Database
import psycopg2.extensions import psycopg2.extensions
@ -21,6 +21,13 @@ psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
class DatabaseFeatures(BaseDatabaseFeatures): class DatabaseFeatures(BaseDatabaseFeatures):
needs_datetime_string_cast = False 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): class DatabaseWrapper(BaseDatabaseWrapper):
features = DatabaseFeatures() features = DatabaseFeatures()
ops = DatabaseOperations() ops = DatabaseOperations()

View File

@ -1,7 +1,6 @@
import datetime import datetime
import md5 import md5
from time import time from time import time
from django.utils.encoding import smart_unicode, force_unicode
try: try:
import decimal import decimal
@ -11,7 +10,7 @@ except ImportError:
class CursorDebugWrapper(object): class CursorDebugWrapper(object):
def __init__(self, cursor, db): def __init__(self, cursor, db):
self.cursor = cursor self.cursor = cursor
self.db = db self.db = db # Instance of a BaseDatabaseWrapper subclass
def execute(self, sql, params=()): def execute(self, sql, params=()):
start = time() start = time()
@ -19,8 +18,9 @@ class CursorDebugWrapper(object):
return self.cursor.execute(sql, params) return self.cursor.execute(sql, params)
finally: finally:
stop = time() stop = time()
sql = self.db.ops.last_executed_query(self.cursor, sql, params)
self.db.queries.append({ self.db.queries.append({
'sql': smart_unicode(sql) % convert_args(params), 'sql': sql,
'time': "%.3f" % (stop - start), 'time': "%.3f" % (stop - start),
}) })
@ -31,7 +31,7 @@ class CursorDebugWrapper(object):
finally: finally:
stop = time() stop = time()
self.db.queries.append({ self.db.queries.append({
'sql': 'MANY: ' + sql + ' ' + smart_unicode(tuple(param_list)), 'sql': '%s times: %s' % (len(param_list), sql),
'time': "%.3f" % (stop - start), 'time': "%.3f" % (stop - start),
}) })
@ -41,16 +41,6 @@ class CursorDebugWrapper(object):
else: else:
return getattr(self.cursor, attr) 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 # # Converters from database (string) to Python #
############################################### ###############################################