diff options
Diffstat (limited to 'debug_toolbar')
| -rw-r--r-- | debug_toolbar/panels/sql.py | 36 | ||||
| -rw-r--r-- | debug_toolbar/templates/debug_toolbar/panels/sql.html | 10 | ||||
| -rw-r--r-- | debug_toolbar/utils/compat/db.py | 9 | ||||
| -rw-r--r-- | debug_toolbar/utils/tracking/db.py | 24 |
4 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 %} <a class="remoteCall" href="/__debug__/sql_select/?sql={{ query.raw_sql|urlencode }}&params={{ query.params|urlencode }}&duration={{ query.duration|floatformat:"2"|urlencode }}&hash={{ query.hash }}">Sel</a> <a class="remoteCall" href="/__debug__/sql_explain/?sql={{ query.raw_sql|urlencode }}&params={{ query.params|urlencode }}&duration={{ query.duration|floatformat:"2"|urlencode }}&hash={{ query.hash }}">Expl</a> - {% if is_mysql %} + {% ifequal query.engine 'mysql' %} <a class="remoteCall" href="/__debug__/sql_profile/?sql={{ query.raw_sql|urlencode }}&params={{ query.params|urlencode }}&duration={{ query.duration|floatformat:"2"|urlencode }}&hash={{ query.hash }}">Prof</a> - {% endif %} + {% endifequal %} {% endif %} {% endif %} </td> @@ -56,6 +56,12 @@ <td colspan="4"> <div class="djSQLDetailsDiv"> <p><strong>Connection:</strong> {{ query.alias }}</p> + {% if query.iso_level %} + <p><strong>Isolation Level:</strong> {{ query.iso_level }}</p> + {% endif %} + {% if query.trans_status %} + <p><strong>Transaction Status:</strong> {{ query.trans_status }}</p> + {% endif %} {% if query.stacktrace %} <pre>{{ query.stacktrace }}</pre> {% 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) |
