From 72271115974463f30a2f9f8755e42acc69e204c3 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Tue, 5 Apr 2011 14:02:47 -0700 Subject: Add support for engine-per-query as well as pulling out psycopg2's isolation level and transaction status --- debug_toolbar/panels/sql.py | 36 +++++++++++++++++++-- .../templates/debug_toolbar/panels/sql.html | 10 ++++-- debug_toolbar/utils/compat/db.py | 9 +++++- debug_toolbar/utils/tracking/db.py | 24 +++++++++++--- example/example.db | Bin 55296 -> 55296 bytes 5 files changed, 69 insertions(+), 10 deletions(-) diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py index ba83455..344f5d6 100644 --- a/debug_toolbar/panels/sql.py +++ b/debug_toolbar/panels/sql.py @@ -1,6 +1,5 @@ import re -from django.conf import settings from django.db.backends import BaseDatabaseWrapper from django.template.loader import render_to_string from django.utils.html import escape @@ -26,6 +25,36 @@ def cursor(func, self): return CursorWrapper(result, self, logger=logger) +def get_isolation_level_display(engine, level): + if engine == 'psycopg2': + import psycopg2.extensions + choices = { + psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT: 'Autocommit', + psycopg2.extensions.ISOLATION_LEVEL_READ_UNCOMMITTED: 'Read uncommitted', + psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED: 'Read committed', + psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ: 'Repeatable read', + psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE: 'Serializable', + } + else: + raise ValueError(engine) + + return choices.get(level) + +def get_transaction_status_display(engine, level): + if engine == 'psycopg2': + import psycopg2.extensions + choices = { + psycopg2.extensions.TRANSACTION_STATUS_IDLE: 'Idle', + psycopg2.extensions.TRANSACTION_STATUS_ACTIVE: 'Active', + psycopg2.extensions.TRANSACTION_STATUS_INTRANS: 'In transaction', + psycopg2.extensions.TRANSACTION_STATUS_INERROR: 'In error', + psycopg2.extensions.TRANSACTION_STATUS_UNKNOWN: 'Unknown', + } + else: + raise ValueError(engine) + + return choices.get(level) + class SQLDebugPanel(DebugPanel): """ Panel that displays information about the SQL queries run while processing @@ -102,6 +131,10 @@ class SQLDebugPanel(DebugPanel): for alias, query in self._queries: query['alias'] = alias + if 'iso_level' in query: + query['iso_level'] = get_isolation_level_display(query['engine'], query['iso_level']) + if 'trans_status' in query: + query['trans_status'] = get_transaction_status_display(query['engine'], query['trans_status']) query['sql'] = reformat_sql(query['sql']) query['rgb_color'] = self._databases[alias]['rgb_color'] try: @@ -123,7 +156,6 @@ class SQLDebugPanel(DebugPanel): 'databases': sorted(self._databases.items(), key=lambda x: -x[1]['time_spent']), 'queries': [q for a, q in self._queries], 'sql_time': self._sql_time, - 'is_mysql': settings.DATABASE_ENGINE == 'mysql', }) return render_to_string('debug_toolbar/panels/sql.html', context) diff --git a/debug_toolbar/templates/debug_toolbar/panels/sql.html b/debug_toolbar/templates/debug_toolbar/panels/sql.html index bec97a4..436f3ce 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/sql.html +++ b/debug_toolbar/templates/debug_toolbar/panels/sql.html @@ -44,9 +44,9 @@ {% if query.is_select %} Sel Expl - {% if is_mysql %} + {% ifequal query.engine 'mysql' %} Prof - {% endif %} + {% endifequal %} {% endif %} {% endif %} @@ -56,6 +56,12 @@

Connection: {{ query.alias }}

+ {% if query.iso_level %} +

Isolation Level: {{ query.iso_level }}

+ {% endif %} + {% if query.trans_status %} +

Transaction Status: {{ query.trans_status }}

+ {% endif %} {% if query.stacktrace %}
{{ query.stacktrace }}
{% endif %} diff --git a/debug_toolbar/utils/compat/db.py b/debug_toolbar/utils/compat/db.py index f3b37e6..4273d9e 100644 --- a/debug_toolbar/utils/compat/db.py +++ b/debug_toolbar/utils/compat/db.py @@ -1,6 +1,13 @@ +from django.conf import settings try: from django.db import connections + dbconf = settings.DATABASES except ImportError: # Compat with < Django 1.2 from django.db import connection - connections = {'default': connection} \ No newline at end of file + connections = {'default': connection} + dbconf = { + 'default': { + 'ENGINE': settings.DATABASE_ENGINE, + } + } \ No newline at end of file diff --git a/debug_toolbar/utils/tracking/db.py b/debug_toolbar/utils/tracking/db.py index 4c9ee53..de84785 100644 --- a/debug_toolbar/utils/tracking/db.py +++ b/debug_toolbar/utils/tracking/db.py @@ -10,7 +10,7 @@ from django.utils.encoding import force_unicode from django.utils.hashcompat import sha_constructor from debug_toolbar.utils import ms_from_timedelta, tidy_stacktrace, get_template_info - +from debug_toolbar.utils.compat.db import connections # TODO:This should be set in the toolbar loader as a default and panels should # get a copy of the toolbar object with access to its config dictionary SQL_WARNING_THRESHOLD = getattr(settings, 'DEBUG_TOOLBAR_CONFIG', {}) \ @@ -29,6 +29,9 @@ class CursorWrapper(object): self.logger = logger def execute(self, sql, params=()): + alias = getattr(self, 'alias', 'default') + # HACK: avoid imports + engine = connections[alias].connection.__class__.__module__.split('.', 1)[0] start = datetime.now() try: return self.cursor.execute(sql, params) @@ -56,9 +59,9 @@ class CursorWrapper(object): pass del cur_frame - # We keep `sql` to maintain backwards compatibility - self.logger.record(**{ - 'alias': getattr(self, 'alias', 'default'), + params = { + 'engine': engine, + 'alias': alias, 'sql': self.db.ops.last_executed_query(self.cursor, sql, params), 'duration': duration, 'raw_sql': sql, @@ -70,7 +73,18 @@ class CursorWrapper(object): 'is_slow': (duration > SQL_WARNING_THRESHOLD), 'is_select': sql.lower().strip().startswith('select'), 'template_info': template_info, - }) + } + + if engine == 'psycopg2': + conn = connections[alias].connection + params.update({ + 'trans_status': conn.get_transaction_status(), + 'iso_level': conn.isolation_level, + 'encoding': conn.encoding, + }) + + # We keep `sql` to maintain backwards compatibility + self.logger.record(**params) def executemany(self, sql, param_list): return self.cursor.executemany(sql, param_list) diff --git a/example/example.db b/example/example.db index 3ca0f39..c607f31 100644 Binary files a/example/example.db and b/example/example.db differ -- cgit v1.2.3