From 926643b1881422b2712a952a3c82697cbcad8285 Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Wed, 23 Mar 2011 18:02:06 -0700
Subject: Overhaul SQL panel to include better timeline, more compact queries,
and better view of stacktrace
---
debug_toolbar/panels/sql.py | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index e1e9bdf..86ecbbf 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -1,5 +1,6 @@
from datetime import datetime
import os
+import re
import sys
import SocketServer
import traceback
@@ -10,10 +11,12 @@ from django.db import connection
from django.db.backends import util
from django.views.debug import linebreak_iter
from django.template import Node
+from django.template.defaultfilters import escape
from django.template.loader import render_to_string
from django.utils import simplejson
from django.utils.encoding import force_unicode
from django.utils.hashcompat import sha_constructor
+from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from debug_toolbar.panels import DebugPanel
@@ -172,7 +175,14 @@ class SQLDebugPanel(DebugPanel):
except ZeroDivisionError:
query['width_ratio'] = 0
query['start_offset'] = width_ratio_tally
+ query['end_offset'] = query['width_ratio'] + query['start_offset']
width_ratio_tally += query['width_ratio']
+
+ stacktrace = []
+ for frame in query['stacktrace']:
+ params = map(escape, frame[0].rsplit('/', 1) + list(frame[1:]))
+ stacktrace.append('{0}/{1} in {3}({2})\n {4}"'.format(*params))
+ query['stacktrace'] = mark_safe('\n'.join(stacktrace))
context = self.context.copy()
context.update({
@@ -201,8 +211,11 @@ class BoldKeywordFilter(sqlparse.filters.Filter):
if is_keyword:
yield sqlparse.tokens.Text, ''
+def swap_fields(sql):
+ return re.sub('SELECT (.*) FROM', 'SELECT \g<1> FROM', sql)
+
def reformat_sql(sql):
stack = sqlparse.engine.FilterStack()
stack.preprocess.append(BoldKeywordFilter()) # add our custom filter
stack.postprocess.append(sqlparse.filters.SerializerUnicode()) # tokens -> strings
- return ''.join(stack.run(sql))
+ return swap_fields(''.join(stack.run(sql)))
--
cgit v1.2.3
From d29f055713636c5629ddb7f48805536d39e550cb Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Thu, 24 Mar 2011 16:13:15 -0700
Subject: Add multi db support and summaries to SQL panel
---
debug_toolbar/panels/sql.py | 31 +++++++++++++++++++++++++------
1 file changed, 25 insertions(+), 6 deletions(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index 86ecbbf..8fd07d3 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -7,7 +7,12 @@ import traceback
import django
from django.conf import settings
-from django.db import connection
+try:
+ from django.db import connections
+except ImportError:
+ # Compat with < Django 1.2
+ from django.db import connection
+ connections = {'default': connection}
from django.db.backends import util
from django.views.debug import linebreak_iter
from django.template import Node
@@ -119,6 +124,7 @@ class DatabaseStatTracker(util.CursorDebugWrapper):
# We keep `sql` to maintain backwards compatibility
self.db.queries.append({
'sql': self.db.ops.last_executed_query(self.cursor, sql, params),
+ 'time': duration * 1000,
'duration': duration,
'raw_sql': sql,
'params': _params,
@@ -142,16 +148,27 @@ class SQLDebugPanel(DebugPanel):
def __init__(self, *args, **kwargs):
super(self.__class__, self).__init__(*args, **kwargs)
- self._offset = len(connection.queries)
+ self._offset = dict((k, len(connections[k].queries)) for k in connections)
self._sql_time = 0
self._queries = []
+ self._databases = {}
def nav_title(self):
return _('SQL')
def nav_subtitle(self):
- self._queries = connection.queries[self._offset:]
- self._sql_time = sum([q['duration'] for q in self._queries])
+ self._queries = []
+ self._databases = {}
+ for alias in connections:
+ db_queries = connections[alias].queries[self._offset[alias]:]
+ self._databases[alias] = {
+ 'time_spent': sum(q['duration'] for q in db_queries),
+ 'queries': len(db_queries),
+ }
+ self._queries.extend([(alias, q) for q in db_queries])
+
+ self._queries.sort(key=lambda x: x[1]['start_time'])
+ self._sql_time = sum([d['time_spent'] for d in self._databases.itervalues()])
num_queries = len(self._queries)
# TODO l10n: use ngettext
return "%d %s in %.2fms" % (
@@ -168,7 +185,8 @@ class SQLDebugPanel(DebugPanel):
def content(self):
width_ratio_tally = 0
- for query in self._queries:
+ for alias, query in self._queries:
+ query['alias'] = alias
query['sql'] = reformat_sql(query['sql'])
try:
query['width_ratio'] = (query['duration'] / self._sql_time) * 100
@@ -186,7 +204,8 @@ class SQLDebugPanel(DebugPanel):
context = self.context.copy()
context.update({
- 'queries': self._queries,
+ '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',
})
--
cgit v1.2.3
From 4bb644ad825ed9009176c7e78967b6ba07d7a681 Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Thu, 24 Mar 2011 16:50:09 -0700
Subject: Only show connections which executed queries. Show number of used
connections in title
---
debug_toolbar/panels/sql.py | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index 8fd07d3..15c6ff3 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -22,7 +22,7 @@ from django.utils import simplejson
from django.utils.encoding import force_unicode
from django.utils.hashcompat import sha_constructor
from django.utils.safestring import mark_safe
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import ugettext_lazy as _, ungettext_lazy as __
from debug_toolbar.panels import DebugPanel
from debug_toolbar.utils import sqlparse
@@ -161,11 +161,13 @@ class SQLDebugPanel(DebugPanel):
self._databases = {}
for alias in connections:
db_queries = connections[alias].queries[self._offset[alias]:]
- self._databases[alias] = {
- 'time_spent': sum(q['duration'] for q in db_queries),
- 'queries': len(db_queries),
- }
- self._queries.extend([(alias, q) for q in db_queries])
+ num_queries = len(db_queries)
+ if num_queries:
+ self._databases[alias] = {
+ 'time_spent': sum(q['duration'] for q in db_queries),
+ 'queries': num_queries,
+ }
+ self._queries.extend([(alias, q) for q in db_queries])
self._queries.sort(key=lambda x: x[1]['start_time'])
self._sql_time = sum([d['time_spent'] for d in self._databases.itervalues()])
@@ -178,7 +180,11 @@ class SQLDebugPanel(DebugPanel):
)
def title(self):
- return _('SQL Queries')
+ count = len(self._databases)
+
+ return __('SQL Queries from %(count)d connection', 'SQL Queries from %(count)d connections', count) % dict(
+ count=count,
+ )
def url(self):
return ''
--
cgit v1.2.3
From 79ddcefb629612da3fb05ccbb8b602d1f026b1f9 Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Thu, 24 Mar 2011 17:34:55 -0700
Subject: basic color code grouping of db aliases
---
debug_toolbar/panels/sql.py | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index 15c6ff3..19a4767 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -1,4 +1,5 @@
from datetime import datetime
+import itertools
import os
import re
import sys
@@ -191,9 +192,21 @@ class SQLDebugPanel(DebugPanel):
def content(self):
width_ratio_tally = 0
+ colors = [
+ (256, 0, 0), # red
+ (0, 256, 0), # blue
+ (0, 0, 256), # green
+ ]
+ for n, db in enumerate(self._databases.itervalues()):
+ rgb = [0, 0, 0]
+ color = n % 3
+ rgb[color] = 256 - n/3*32
+ db['rgb_color'] = rgb
+
for alias, query in self._queries:
query['alias'] = alias
query['sql'] = reformat_sql(query['sql'])
+ query['rgb_color'] = self._databases[alias]['rgb_color']
try:
query['width_ratio'] = (query['duration'] / self._sql_time) * 100
except ZeroDivisionError:
@@ -207,7 +220,7 @@ class SQLDebugPanel(DebugPanel):
params = map(escape, frame[0].rsplit('/', 1) + list(frame[1:]))
stacktrace.append('{0}/{1} in {3}({2})\n {4}"'.format(*params))
query['stacktrace'] = mark_safe('\n'.join(stacktrace))
-
+
context = self.context.copy()
context.update({
'databases': sorted(self._databases.items(), key=lambda x: -x[1]['time_spent']),
--
cgit v1.2.3
From 1212fec3a0df762c2f63a34925e304c49fa7354d Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Thu, 24 Mar 2011 17:45:58 -0700
Subject: Much better coloring scale
---
debug_toolbar/panels/sql.py | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index 19a4767..dc096c0 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -197,10 +197,20 @@ class SQLDebugPanel(DebugPanel):
(0, 256, 0), # blue
(0, 0, 256), # green
]
+ factor = int(256.0/len(self._databases)*2.5)
for n, db in enumerate(self._databases.itervalues()):
rgb = [0, 0, 0]
color = n % 3
- rgb[color] = 256 - n/3*32
+ rgb[color] = 256 - n/3*factor
+ nn = color
+ # XXX: pretty sure this is horrible after so many aliases
+ while rgb[color] < factor:
+ nc = min(256 - rgb[color], 256)
+ rgb[color] += nc
+ nn += 1
+ if nn > 2:
+ nn = 0
+ rgb[nn] = nc
db['rgb_color'] = rgb
for alias, query in self._queries:
--
cgit v1.2.3
From 85e9976164b3d7746385664506ce6c5409e7e9d8 Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Thu, 24 Mar 2011 17:48:59 -0700
Subject: Fix for infinite math fail :)
---
debug_toolbar/panels/sql.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index dc096c0..258fab8 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -197,7 +197,8 @@ class SQLDebugPanel(DebugPanel):
(0, 256, 0), # blue
(0, 0, 256), # green
]
- factor = int(256.0/len(self._databases)*2.5)
+ factor = int(256.0/(len(self._databases)*2.5))
+ print factor
for n, db in enumerate(self._databases.itervalues()):
rgb = [0, 0, 0]
color = n % 3
@@ -205,6 +206,7 @@ class SQLDebugPanel(DebugPanel):
nn = color
# XXX: pretty sure this is horrible after so many aliases
while rgb[color] < factor:
+ print rgb[color], factor
nc = min(256 - rgb[color], 256)
rgb[color] += nc
nn += 1
--
cgit v1.2.3
From 7a07b70db07b91be652e55eebee7b04dac01ee64 Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Thu, 24 Mar 2011 17:49:20 -0700
Subject: Remove debug print
---
debug_toolbar/panels/sql.py | 1 -
1 file changed, 1 deletion(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index 258fab8..5769df6 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -198,7 +198,6 @@ class SQLDebugPanel(DebugPanel):
(0, 0, 256), # green
]
factor = int(256.0/(len(self._databases)*2.5))
- print factor
for n, db in enumerate(self._databases.itervalues()):
rgb = [0, 0, 0]
color = n % 3
--
cgit v1.2.3
From 5aff6ee75f8af3dd46254953b0b0de7c8e19c8e2 Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Mon, 28 Mar 2011 17:25:21 -0700
Subject: SQL injection now happens without interfering with the underlying
db.queries objects (and no longer requires DEBUG to be set)
---
debug_toolbar/panels/sql.py | 61 ++++++++++++++++++++++++++-------------------
1 file changed, 36 insertions(+), 25 deletions(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index 5769df6..5efafbb 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -1,5 +1,4 @@
from datetime import datetime
-import itertools
import os
import re
import sys
@@ -25,6 +24,7 @@ from django.utils.hashcompat import sha_constructor
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _, ungettext_lazy as __
+from debug_toolbar.middleware import DebugToolbarMiddleware
from debug_toolbar.panels import DebugPanel
from debug_toolbar.utils import sqlparse
@@ -89,15 +89,21 @@ def get_template_info(source, context_lines=3):
'context': context,
}
-class DatabaseStatTracker(util.CursorDebugWrapper):
+def inject_sql_tracker(cls):
"""
- Replacement for CursorDebugWrapper which stores additional information
- in `connection.queries`.
+ Injects a replacement execute method which records queries within the SQLPanel.
"""
+ if getattr(cls.execute, 'is_tracked', False):
+ return
def execute(self, sql, params=()):
+ djdt = DebugToolbarMiddleware.get_current()
+ if not djdt:
+ return cls.execute.__wraps(self, sql, params)
+
+ panel = djdt.get_panel(SQLDebugPanel)
start = datetime.now()
try:
- return self.cursor.execute(sql, params)
+ return cls.execute.__wraps(self, sql, params)
finally:
stop = datetime.now()
duration = ms_from_timedelta(stop - start)
@@ -123,9 +129,9 @@ class DatabaseStatTracker(util.CursorDebugWrapper):
del cur_frame
# We keep `sql` to maintain backwards compatibility
- self.db.queries.append({
+ panel.record(**{
+ 'alias': getattr(self, 'alias', 'default'),
'sql': self.db.ops.last_executed_query(self.cursor, sql, params),
- 'time': duration * 1000,
'duration': duration,
'raw_sql': sql,
'params': _params,
@@ -137,7 +143,13 @@ class DatabaseStatTracker(util.CursorDebugWrapper):
'is_select': sql.lower().strip().startswith('select'),
'template_info': template_info,
})
-util.CursorDebugWrapper = DatabaseStatTracker
+ execute.is_tracked = True
+ execute.__wraps = cls.execute
+
+ cls.execute = execute
+
+inject_sql_tracker(util.CursorWrapper)
+inject_sql_tracker(util.CursorDebugWrapper)
class SQLDebugPanel(DebugPanel):
"""
@@ -151,32 +163,31 @@ class SQLDebugPanel(DebugPanel):
super(self.__class__, self).__init__(*args, **kwargs)
self._offset = dict((k, len(connections[k].queries)) for k in connections)
self._sql_time = 0
+ self._num_queries = 0
self._queries = []
self._databases = {}
+
+ def record(self, alias, **kwargs):
+ self._queries.append((alias, kwargs))
+ if alias not in self._databases:
+ self._databases[alias] = {
+ 'time_spent': kwargs['duration'],
+ 'num_queries': 1,
+ }
+ else:
+ self._databases[alias]['time_spent'] += kwargs['duration']
+ self._databases[alias]['num_queries'] += 1
+ self._sql_time += kwargs['duration']
+ self._num_queries += 1
def nav_title(self):
return _('SQL')
def nav_subtitle(self):
- self._queries = []
- self._databases = {}
- for alias in connections:
- db_queries = connections[alias].queries[self._offset[alias]:]
- num_queries = len(db_queries)
- if num_queries:
- self._databases[alias] = {
- 'time_spent': sum(q['duration'] for q in db_queries),
- 'queries': num_queries,
- }
- self._queries.extend([(alias, q) for q in db_queries])
-
- self._queries.sort(key=lambda x: x[1]['start_time'])
- self._sql_time = sum([d['time_spent'] for d in self._databases.itervalues()])
- num_queries = len(self._queries)
# TODO l10n: use ngettext
return "%d %s in %.2fms" % (
- num_queries,
- (num_queries == 1) and 'query' or 'queries',
+ self._num_queries,
+ (self._num_queries == 1) and 'query' or 'queries',
self._sql_time
)
--
cgit v1.2.3
From 4c7c43300ed0bfee6243cb5b59887837f2becb13 Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Mon, 28 Mar 2011 17:30:19 -0700
Subject: Fail gracefully when theres no queries on a page
---
debug_toolbar/panels/sql.py | 77 +++++++++++++++++++++++----------------------
1 file changed, 39 insertions(+), 38 deletions(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index 5efafbb..e9e2569 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -202,46 +202,47 @@ class SQLDebugPanel(DebugPanel):
return ''
def content(self):
- width_ratio_tally = 0
- colors = [
- (256, 0, 0), # red
- (0, 256, 0), # blue
- (0, 0, 256), # green
- ]
- factor = int(256.0/(len(self._databases)*2.5))
- for n, db in enumerate(self._databases.itervalues()):
- rgb = [0, 0, 0]
- color = n % 3
- rgb[color] = 256 - n/3*factor
- nn = color
- # XXX: pretty sure this is horrible after so many aliases
- while rgb[color] < factor:
- print rgb[color], factor
- nc = min(256 - rgb[color], 256)
- rgb[color] += nc
- nn += 1
- if nn > 2:
- nn = 0
- rgb[nn] = nc
- db['rgb_color'] = rgb
+ if self._queries:
+ width_ratio_tally = 0
+ colors = [
+ (256, 0, 0), # red
+ (0, 256, 0), # blue
+ (0, 0, 256), # green
+ ]
+ factor = int(256.0/(len(self._databases)*2.5))
+ for n, db in enumerate(self._databases.itervalues()):
+ rgb = [0, 0, 0]
+ color = n % 3
+ rgb[color] = 256 - n/3*factor
+ nn = color
+ # XXX: pretty sure this is horrible after so many aliases
+ while rgb[color] < factor:
+ print rgb[color], factor
+ nc = min(256 - rgb[color], 256)
+ rgb[color] += nc
+ nn += 1
+ if nn > 2:
+ nn = 0
+ rgb[nn] = nc
+ db['rgb_color'] = rgb
- for alias, query in self._queries:
- query['alias'] = alias
- query['sql'] = reformat_sql(query['sql'])
- query['rgb_color'] = self._databases[alias]['rgb_color']
- try:
- query['width_ratio'] = (query['duration'] / self._sql_time) * 100
- except ZeroDivisionError:
- query['width_ratio'] = 0
- query['start_offset'] = width_ratio_tally
- query['end_offset'] = query['width_ratio'] + query['start_offset']
- width_ratio_tally += query['width_ratio']
+ for alias, query in self._queries:
+ query['alias'] = alias
+ query['sql'] = reformat_sql(query['sql'])
+ query['rgb_color'] = self._databases[alias]['rgb_color']
+ try:
+ query['width_ratio'] = (query['duration'] / self._sql_time) * 100
+ except ZeroDivisionError:
+ query['width_ratio'] = 0
+ query['start_offset'] = width_ratio_tally
+ query['end_offset'] = query['width_ratio'] + query['start_offset']
+ width_ratio_tally += query['width_ratio']
- stacktrace = []
- for frame in query['stacktrace']:
- params = map(escape, frame[0].rsplit('/', 1) + list(frame[1:]))
- stacktrace.append('{0}/{1} in {3}({2})\n {4}"'.format(*params))
- query['stacktrace'] = mark_safe('\n'.join(stacktrace))
+ stacktrace = []
+ for frame in query['stacktrace']:
+ params = map(escape, frame[0].rsplit('/', 1) + list(frame[1:]))
+ stacktrace.append('{0}/{1} in {3}({2})\n {4}"'.format(*params))
+ query['stacktrace'] = mark_safe('\n'.join(stacktrace))
context = self.context.copy()
context.update({
--
cgit v1.2.3
From da85fc0a8b5e1855b6b6219076a34cd0df88bcc3 Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Mon, 28 Mar 2011 17:32:55 -0700
Subject: Store actual function as __wrapped__ instead of __wraps for Python
3.2 compatibility
---
debug_toolbar/panels/sql.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index e9e2569..b3b44da 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -98,12 +98,12 @@ def inject_sql_tracker(cls):
def execute(self, sql, params=()):
djdt = DebugToolbarMiddleware.get_current()
if not djdt:
- return cls.execute.__wraps(self, sql, params)
+ return cls.execute.__wrapped__(self, sql, params)
panel = djdt.get_panel(SQLDebugPanel)
start = datetime.now()
try:
- return cls.execute.__wraps(self, sql, params)
+ return cls.execute.__wrapped__(self, sql, params)
finally:
stop = datetime.now()
duration = ms_from_timedelta(stop - start)
@@ -144,7 +144,7 @@ def inject_sql_tracker(cls):
'template_info': template_info,
})
execute.is_tracked = True
- execute.__wraps = cls.execute
+ execute.__wrapped__ = cls.execute
cls.execute = execute
--
cgit v1.2.3
From 48c8d0dd09c352532326b83a39800be047686356 Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Wed, 30 Mar 2011 15:31:27 -0700
Subject: Ensure we wrap all cursors
---
debug_toolbar/panels/sql.py | 35 ++++++++++++++++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index b3b44da..ba40c34 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -13,7 +13,7 @@ except ImportError:
# Compat with < Django 1.2
from django.db import connection
connections = {'default': connection}
-from django.db.backends import util
+from django.db.backends import util, BaseDatabaseWrapper
from django.views.debug import linebreak_iter
from django.template import Node
from django.template.defaultfilters import escape
@@ -148,6 +148,39 @@ def inject_sql_tracker(cls):
cls.execute = execute
+class CursorWrapper(object):
+ def __init__(self, cursor, db):
+ self.cursor = cursor
+ self.db = db # Instance of a BaseDatabaseWrapper subclass
+
+ def execute(self, sql, params=None):
+ return self.cursor.execute(sql, params)
+
+ def executemany(self, sql, param_list):
+ return self.cursor.executemany(sql, param_list)
+
+ def __getattr__(self, attr):
+ if attr in self.__dict__:
+ return self.__dict__[attr]
+ else:
+ return getattr(self.cursor, attr)
+
+ def __iter__(self):
+ return iter(self.cursor)
+
+if not hasattr(util, 'CursorWrapper'):
+ # Inject our CursorWrapper class
+ util.CursorWrapper = CursorWrapper
+
+def cursor(self):
+ from django.conf import settings
+ cursor = self._cursor()
+ if settings.DEBUG:
+ return self.make_debug_cursor(cursor)
+ return util.CursorWrapper(cursor, self)
+
+BaseDatabaseWrapper.cursor = cursor
+
inject_sql_tracker(util.CursorWrapper)
inject_sql_tracker(util.CursorDebugWrapper)
--
cgit v1.2.3
From 65969f7777ce0bd6bba53540d960a93c27b346ce Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Wed, 30 Mar 2011 15:57:51 -0700
Subject: Inject our SQL tracker on BaseDatabaseWrapper.cursor rather than
discovering different CursorWrapper's
---
debug_toolbar/panels/sql.py | 63 ++++++++++++++++++++++-----------------------
1 file changed, 31 insertions(+), 32 deletions(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index ba40c34..a78a3e2 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -13,7 +13,7 @@ except ImportError:
# Compat with < Django 1.2
from django.db import connection
connections = {'default': connection}
-from django.db.backends import util, BaseDatabaseWrapper
+from django.db.backends import BaseDatabaseWrapper
from django.views.debug import linebreak_iter
from django.template import Node
from django.template.defaultfilters import escape
@@ -89,21 +89,25 @@ def get_template_info(source, context_lines=3):
'context': context,
}
-def inject_sql_tracker(cls):
+
+class CursorWrapper(object):
"""
- Injects a replacement execute method which records queries within the SQLPanel.
+ Wraps a cursor and logs queries.
"""
- if getattr(cls.execute, 'is_tracked', False):
- return
+
+ def __init__(self, cursor, db):
+ self.cursor = cursor
+ self.db = db # Instance of a BaseDatabaseWrapper subclass
+
def execute(self, sql, params=()):
djdt = DebugToolbarMiddleware.get_current()
if not djdt:
- return cls.execute.__wrapped__(self, sql, params)
+ return self.cursor.execute(self, sql, params)
panel = djdt.get_panel(SQLDebugPanel)
start = datetime.now()
try:
- return cls.execute.__wrapped__(self, sql, params)
+ return self.cursor.execute(self, sql, params)
finally:
stop = datetime.now()
duration = ms_from_timedelta(stop - start)
@@ -143,18 +147,6 @@ def inject_sql_tracker(cls):
'is_select': sql.lower().strip().startswith('select'),
'template_info': template_info,
})
- execute.is_tracked = True
- execute.__wrapped__ = cls.execute
-
- cls.execute = execute
-
-class CursorWrapper(object):
- def __init__(self, cursor, db):
- self.cursor = cursor
- self.db = db # Instance of a BaseDatabaseWrapper subclass
-
- def execute(self, sql, params=None):
- return self.cursor.execute(sql, params)
def executemany(self, sql, param_list):
return self.cursor.executemany(sql, param_list)
@@ -168,21 +160,29 @@ class CursorWrapper(object):
def __iter__(self):
return iter(self.cursor)
-if not hasattr(util, 'CursorWrapper'):
- # Inject our CursorWrapper class
- util.CursorWrapper = CursorWrapper
+def inject_sql_tracker(cls):
+ """
+ Injects a replacement execute method which records queries within the SQLPanel.
+ """
+ import warnings
+
+ if not hasattr(cls, 'cursor'):
+ warnings.warn('Unable to patch %r: missing cursor method' % cls)
+
+ if getattr(cls.cursor, 'djdt_tracked', False):
+ return
+
+ def cursor(self):
+ result = cls.cursor.__wrapped__(self)
+ return CursorWrapper(result, self)
-def cursor(self):
- from django.conf import settings
- cursor = self._cursor()
- if settings.DEBUG:
- return self.make_debug_cursor(cursor)
- return util.CursorWrapper(cursor, self)
+ cursor.djdt_tracked = True
+ cursor.__wrapped__ = cls.cursor
-BaseDatabaseWrapper.cursor = cursor
+ cls.cursor = cursor
-inject_sql_tracker(util.CursorWrapper)
-inject_sql_tracker(util.CursorDebugWrapper)
+# Inject our tracking code into the existing CursorWrapper's
+inject_sql_tracker(BaseDatabaseWrapper)
class SQLDebugPanel(DebugPanel):
"""
@@ -250,7 +250,6 @@ class SQLDebugPanel(DebugPanel):
nn = color
# XXX: pretty sure this is horrible after so many aliases
while rgb[color] < factor:
- print rgb[color], factor
nc = min(256 - rgb[color], 256)
rgb[color] += nc
nn += 1
--
cgit v1.2.3
From f492b56c8200eebb77b8023ab386c9ef412cc06b Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Wed, 30 Mar 2011 16:33:34 -0700
Subject: Some initial tests and fix for execution model
---
debug_toolbar/panels/sql.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index a78a3e2..4c7b8a1 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -102,12 +102,12 @@ class CursorWrapper(object):
def execute(self, sql, params=()):
djdt = DebugToolbarMiddleware.get_current()
if not djdt:
- return self.cursor.execute(self, sql, params)
+ return self.cursor.execute(sql, params)
panel = djdt.get_panel(SQLDebugPanel)
start = datetime.now()
try:
- return self.cursor.execute(self, sql, params)
+ return self.cursor.execute(sql, params)
finally:
stop = datetime.now()
duration = ms_from_timedelta(stop - start)
--
cgit v1.2.3
From 3f578cf684b7e3b9a20d9c777950c28c44db074a Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Thu, 31 Mar 2011 11:42:23 -0700
Subject: Add utilities to inject and monitor functions. Change DB tracking to
use new injection method on BaseDatabaseWrapper.cursor
---
debug_toolbar/panels/sql.py | 196 ++++----------------------------------------
1 file changed, 16 insertions(+), 180 deletions(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index 4c7b8a1..ba83455 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -1,188 +1,30 @@
-from datetime import datetime
-import os
import re
-import sys
-import SocketServer
-import traceback
-import django
from django.conf import settings
-try:
- from django.db import connections
-except ImportError:
- # Compat with < Django 1.2
- from django.db import connection
- connections = {'default': connection}
from django.db.backends import BaseDatabaseWrapper
-from django.views.debug import linebreak_iter
-from django.template import Node
-from django.template.defaultfilters import escape
from django.template.loader import render_to_string
-from django.utils import simplejson
-from django.utils.encoding import force_unicode
-from django.utils.hashcompat import sha_constructor
+from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _, ungettext_lazy as __
+from debug_toolbar.utils.compat.db import connections
from debug_toolbar.middleware import DebugToolbarMiddleware
from debug_toolbar.panels import DebugPanel
from debug_toolbar.utils import sqlparse
-
-# Figure out some paths
-django_path = os.path.realpath(os.path.dirname(django.__file__))
-socketserver_path = os.path.realpath(os.path.dirname(SocketServer.__file__))
-
-# 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', {}) \
- .get('SQL_WARNING_THRESHOLD', 500)
-
-def tidy_stacktrace(strace):
- """
- Clean up stacktrace and remove all entries that:
- 1. Are part of Django (except contrib apps)
- 2. Are part of SocketServer (used by Django's dev server)
- 3. Are the last entry (which is part of our stacktracing code)
- """
- trace = []
- for s in strace[:-1]:
- s_path = os.path.realpath(s[0])
- if getattr(settings, 'DEBUG_TOOLBAR_CONFIG', {}).get('HIDE_DJANGO_SQL', True) \
- and django_path in s_path and not 'django/contrib' in s_path:
- continue
- if socketserver_path in s_path:
- continue
- trace.append((s[0], s[1], s[2], s[3]))
- return trace
-
-def get_template_info(source, context_lines=3):
- line = 0
- upto = 0
- source_lines = []
- before = during = after = ""
-
- origin, (start, end) = source
- template_source = origin.reload()
-
- for num, next in enumerate(linebreak_iter(template_source)):
- if start >= upto and end <= next:
- line = num
- before = template_source[upto:start]
- during = template_source[start:end]
- after = template_source[end:next]
- source_lines.append((num, template_source[upto:next]))
- upto = next
-
- top = max(1, line - context_lines)
- bottom = min(len(source_lines), line + 1 + context_lines)
-
- context = []
- for num, content in source_lines[top:bottom]:
- context.append({
- 'num': num,
- 'content': content,
- 'highlight': (num == line),
- })
-
- return {
- 'name': origin.name,
- 'context': context,
- }
-
-
-class CursorWrapper(object):
- """
- Wraps a cursor and logs queries.
- """
-
- def __init__(self, cursor, db):
- self.cursor = cursor
- self.db = db # Instance of a BaseDatabaseWrapper subclass
-
- def execute(self, sql, params=()):
- djdt = DebugToolbarMiddleware.get_current()
- if not djdt:
- return self.cursor.execute(sql, params)
-
- panel = djdt.get_panel(SQLDebugPanel)
- start = datetime.now()
- try:
- return self.cursor.execute(sql, params)
- finally:
- stop = datetime.now()
- duration = ms_from_timedelta(stop - start)
- stacktrace = tidy_stacktrace(traceback.extract_stack())
- _params = ''
- try:
- _params = simplejson.dumps([force_unicode(x, strings_only=True) for x in params])
- except TypeError:
- pass # object not JSON serializable
-
- template_info = None
- cur_frame = sys._getframe().f_back
- try:
- while cur_frame is not None:
- if cur_frame.f_code.co_name == 'render':
- node = cur_frame.f_locals['self']
- if isinstance(node, Node):
- template_info = get_template_info(node.source)
- break
- cur_frame = cur_frame.f_back
- except:
- pass
- del cur_frame
-
- # We keep `sql` to maintain backwards compatibility
- panel.record(**{
- 'alias': getattr(self, 'alias', 'default'),
- 'sql': self.db.ops.last_executed_query(self.cursor, sql, params),
- 'duration': duration,
- 'raw_sql': sql,
- 'params': _params,
- 'hash': sha_constructor(settings.SECRET_KEY + sql + _params).hexdigest(),
- 'stacktrace': stacktrace,
- 'start_time': start,
- 'stop_time': stop,
- 'is_slow': (duration > SQL_WARNING_THRESHOLD),
- 'is_select': sql.lower().strip().startswith('select'),
- 'template_info': template_info,
- })
-
- def executemany(self, sql, param_list):
- return self.cursor.executemany(sql, param_list)
-
- def __getattr__(self, attr):
- if attr in self.__dict__:
- return self.__dict__[attr]
- else:
- return getattr(self.cursor, attr)
-
- def __iter__(self):
- return iter(self.cursor)
-
-def inject_sql_tracker(cls):
- """
- Injects a replacement execute method which records queries within the SQLPanel.
- """
- import warnings
+from debug_toolbar.utils.tracking.db import CursorWrapper
+from debug_toolbar.utils.tracking import replace_call
+
+# Inject our tracking cursor
+@replace_call(BaseDatabaseWrapper.cursor)
+def cursor(func, self):
+ result = func(self)
+
+ djdt = DebugToolbarMiddleware.get_current()
+ if not djdt:
+ return result
+ logger = djdt.get_panel(SQLDebugPanel)
- if not hasattr(cls, 'cursor'):
- warnings.warn('Unable to patch %r: missing cursor method' % cls)
-
- if getattr(cls.cursor, 'djdt_tracked', False):
- return
-
- def cursor(self):
- result = cls.cursor.__wrapped__(self)
- return CursorWrapper(result, self)
-
- cursor.djdt_tracked = True
- cursor.__wrapped__ = cls.cursor
-
- cls.cursor = cursor
-
-# Inject our tracking code into the existing CursorWrapper's
-inject_sql_tracker(BaseDatabaseWrapper)
+ return CursorWrapper(result, self, logger=logger)
class SQLDebugPanel(DebugPanel):
"""
@@ -286,12 +128,6 @@ class SQLDebugPanel(DebugPanel):
return render_to_string('debug_toolbar/panels/sql.html', context)
-def ms_from_timedelta(td):
- """
- Given a timedelta object, returns a float representing milliseconds
- """
- return (td.seconds * 1000) + (td.microseconds / 1000.0)
-
class BoldKeywordFilter(sqlparse.filters.Filter):
"""sqlparse filter to bold SQL keywords"""
def process(self, stack, stream):
@@ -300,7 +136,7 @@ class BoldKeywordFilter(sqlparse.filters.Filter):
is_keyword = token_type in sqlparse.tokens.Keyword
if is_keyword:
yield sqlparse.tokens.Text, ''
- yield token_type, django.utils.html.escape(value)
+ yield token_type, escape(value)
if is_keyword:
yield sqlparse.tokens.Text, ''
--
cgit v1.2.3
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 ++++++++++++++++++++++++++++++++++--
1 file changed, 34 insertions(+), 2 deletions(-)
(limited to 'debug_toolbar/panels/sql.py')
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)
--
cgit v1.2.3
From a9b466b6672c72f498e3f011524d48726937d1d5 Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Wed, 6 Apr 2011 16:07:46 -0700
Subject: SQL panel now guesses at Psycopg2 transactions (when autocommit swaps
to in trans and vice versa)
---
debug_toolbar/panels/sql.py | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index 344f5d6..9f4e83c 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -70,6 +70,23 @@ class SQLDebugPanel(DebugPanel):
self._num_queries = 0
self._queries = []
self._databases = {}
+ self._transaction_status = {}
+
+ def get_transaction_status(self, alias, reset=False):
+ conn = connections[alias].connection
+ if not conn:
+ return None
+
+ engine = conn.__class__.__module__.split('.', 1)[0]
+
+ if reset or self._transaction_status.get(alias) is None:
+ if engine == 'psycopg2':
+ self._transaction_status[alias] = conn.get_transaction_status()
+ else:
+ raise ValueError(engine)
+
+ return self._transaction_status[alias]
+
def record(self, alias, **kwargs):
self._queries.append((alias, kwargs))
--
cgit v1.2.3
From 0556fd7b48c709b5ff803eff01e0dd7eb07ba3fc Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Wed, 6 Apr 2011 16:29:33 -0700
Subject: Generate fake transaction ids so we can match up transaction end
state correctly
---
debug_toolbar/panels/sql.py | 46 ++++++++++++++++++++++++++++++++++++---------
1 file changed, 37 insertions(+), 9 deletions(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index 9f4e83c..0c36f48 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -1,4 +1,5 @@
import re
+import uuid
from django.db.backends import BaseDatabaseWrapper
from django.template.loader import render_to_string
@@ -71,22 +72,30 @@ class SQLDebugPanel(DebugPanel):
self._queries = []
self._databases = {}
self._transaction_status = {}
+ self._transaction_ids = {}
- def get_transaction_status(self, alias, reset=False):
+ def get_transaction_id(self, alias):
conn = connections[alias].connection
if not conn:
return None
engine = conn.__class__.__module__.split('.', 1)[0]
+ if engine == 'psycopg2':
+ cur_status = conn.get_transaction_status()
+ else:
+ raise ValueError(engine)
+
+ last_status = self._transaction_status.get(alias)
+ self._transaction_status[alias] = cur_status
+
+ if not cur_status:
+ # No available state
+ return None
+
+ if cur_status != last_status:
+ self._transaction_ids[alias] = uuid.uuid4().hex
- if reset or self._transaction_status.get(alias) is None:
- if engine == 'psycopg2':
- self._transaction_status[alias] = conn.get_transaction_status()
- else:
- raise ValueError(engine)
-
- return self._transaction_status[alias]
-
+ return self._transaction_ids[alias]
def record(self, alias, **kwargs):
self._queries.append((alias, kwargs))
@@ -146,7 +155,22 @@ class SQLDebugPanel(DebugPanel):
rgb[nn] = nc
db['rgb_color'] = rgb
+ trans_ids = {}
+ trans_id = None
+ i = 0
for alias, query in self._queries:
+ trans_id = query.get('trans_id')
+ last_trans_id = trans_ids.get(alias)
+
+ print trans_id, last_trans_id
+ if query['engine'] == 'psycopg2' and trans_id != last_trans_id:
+ if last_trans_id:
+ self._queries[i][1]['ends_trans'] = True
+ trans_ids[alias] = trans_id
+ query['starts_trans'] = True
+ if trans_id:
+ query['in_trans'] = True
+
query['alias'] = alias
if 'iso_level' in query:
query['iso_level'] = get_isolation_level_display(query['engine'], query['iso_level'])
@@ -167,6 +191,10 @@ class SQLDebugPanel(DebugPanel):
params = map(escape, frame[0].rsplit('/', 1) + list(frame[1:]))
stacktrace.append('{0}/{1} in {3}({2})\n {4}"'.format(*params))
query['stacktrace'] = mark_safe('\n'.join(stacktrace))
+ i += 1
+
+ if trans_id:
+ self._queries[i][1]['ends_trans'] = True
context = self.context.copy()
context.update({
--
cgit v1.2.3
From 5542f52d7ca2bfda8bd5135476eef1f93164ed50 Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Wed, 6 Apr 2011 16:34:34 -0700
Subject: Transaction id tracing is not specific to psycopg2
---
debug_toolbar/panels/sql.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index 0c36f48..7427101 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -162,8 +162,7 @@ class SQLDebugPanel(DebugPanel):
trans_id = query.get('trans_id')
last_trans_id = trans_ids.get(alias)
- print trans_id, last_trans_id
- if query['engine'] == 'psycopg2' and trans_id != last_trans_id:
+ if trans_id != last_trans_id:
if last_trans_id:
self._queries[i][1]['ends_trans'] = True
trans_ids[alias] = trans_id
--
cgit v1.2.3
From 42e693a94aa3a92f7b270df9fb22374da2bc5a05 Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Wed, 6 Apr 2011 16:52:25 -0700
Subject: Mark the correct query as ending the transaction
---
debug_toolbar/panels/sql.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index 7427101..a3812de 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -164,7 +164,7 @@ class SQLDebugPanel(DebugPanel):
if trans_id != last_trans_id:
if last_trans_id:
- self._queries[i][1]['ends_trans'] = True
+ self._queries[i-1][1]['ends_trans'] = True
trans_ids[alias] = trans_id
query['starts_trans'] = True
if trans_id:
--
cgit v1.2.3
From 34eff57a293ba456d87a04e5cc2a72056c1a2010 Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Wed, 6 Apr 2011 16:54:42 -0700
Subject: unset transaction id if we're not in a transaction
---
debug_toolbar/panels/sql.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index a3812de..9dfee71 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -93,7 +93,10 @@ class SQLDebugPanel(DebugPanel):
return None
if cur_status != last_status:
- self._transaction_ids[alias] = uuid.uuid4().hex
+ if cur_status:
+ self._transaction_ids[alias] = uuid.uuid4().hex
+ else:
+ self._transaction_ids[alias] = None
return self._transaction_ids[alias]
--
cgit v1.2.3
From 43a95e175c889064f580a8ab472ae45a16e2f738 Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Wed, 6 Apr 2011 16:56:30 -0700
Subject: Only mark starting a new transaction if new trans_id is set
---
debug_toolbar/panels/sql.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index 9dfee71..0fac42b 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -169,7 +169,8 @@ class SQLDebugPanel(DebugPanel):
if last_trans_id:
self._queries[i-1][1]['ends_trans'] = True
trans_ids[alias] = trans_id
- query['starts_trans'] = True
+ if trans_id:
+ query['starts_trans'] = True
if trans_id:
query['in_trans'] = True
--
cgit v1.2.3
From 95b1654028f74db4478da2214460ae8c363e8084 Mon Sep 17 00:00:00 2001
From: David Cramer
Date: Wed, 13 Apr 2011 11:10:53 -0700
Subject: Fix a bug with marking the last query in a transaction as the end of
chain
---
debug_toolbar/panels/sql.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'debug_toolbar/panels/sql.py')
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index 0fac42b..ff63b6f 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -197,7 +197,7 @@ class SQLDebugPanel(DebugPanel):
i += 1
if trans_id:
- self._queries[i][1]['ends_trans'] = True
+ self._queries[i-1][1]['ends_trans'] = True
context = self.context.copy()
context.update({
--
cgit v1.2.3