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