Skip to content

Commit a1934d1

Browse files
committed
Refined the sql debug view at the bottom of each page. Added a column showing the WHERE clause, as that is quite helpful in working out where a given query is coming from. Added an sql_debug template variable to make it easier to switch between the sql debug view and using the django-debug-toolbar.
- Legacy-Id: 12225
1 parent c6177d4 commit a1934d1

3 files changed

Lines changed: 87 additions & 41 deletions

File tree

ietf/context_processors.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ def debug_mark_queries_from_view(request):
1818
if settings.DEBUG and request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS:
1919
from django.db import connection
2020
for query in connection.queries:
21-
query['where'] = 'V' # V is for 'view'
21+
query['loc'] = 'V' # V is for 'view'
2222
return context_extras
23+
24+
def sql_debug(request):
25+
if settings.DEBUG and request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS:
26+
return {'sql_debug': True }
27+
else:
28+
return {'sql_debug': False }
2329

ietf/templates/debug.html

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,49 @@
11
{# Copyright The IETF Trust 2015, All Rights Reserved #}
22
{% load origin %}{% origin %}
33
{% if debug %}
4-
{% load debug_filters %}
5-
{% load future %}
4+
{% if sql_debug %}
5+
{% load debug_filters %}
6+
{% load future %}
67

7-
<div id="debug">
8-
<hr>
9-
<p>
10-
{{ sql_queries|length }} queries ({{ sql_queries|timesum }}s)
11-
{% if sql_queries|length != 0 %}
12-
<a class="btn btn-default btn-xs"
13-
onclick="$('#debug-query-table').toggleClass('hide');">Show</a>
14-
{% endif %}
15-
</p>
16-
<table class="table table-condensed table-striped tablesorter hide" id="debug-query-table">
17-
<thead>
18-
<tr>
19-
<th data-header="sequence">#</th>
20-
<th data-header="query">SQL</th>
21-
<th data-header="count">Count</th>
22-
<th data-header="where">View/ Templ.</th>
23-
<th data-header="time">Time</th>
24-
<th data-header="acc">Acc.</th>
25-
</tr>
26-
</thead>
27-
<tbody>
28-
{% with sql_queries|annotate_sql_queries as sql_query_info %}
29-
{% for query in sql_query_info %}
30-
<tr>
31-
<td>{{ forloop.counter }}</td>
32-
<td>{{ query.sql|expand_comma|escape }}</td>
33-
<td>{{ query.count }}</td>
34-
<td>{{ query.where }}</td>
35-
<td>{{ query.time }}</td>
36-
<td>{{ query.time_accum }}</td>
37-
</tr>
38-
{% endfor %}
39-
{% endwith %}
40-
</tbody>
41-
</table>
42-
</div>
8+
<div id="debug">
9+
<hr>
10+
<p>
11+
{{ sql_queries|length }} queries ({{ sql_queries|timesum }}s)
12+
{% if sql_queries|length != 0 %}
13+
<a class="btn btn-default btn-xs"
14+
onclick="$('#debug-query-table').toggleClass('hide');">Show</a>
15+
{% endif %}
16+
</p>
17+
<table class="table table-condensed table-striped tablesorter hide" id="debug-query-table">
18+
<thead>
19+
<tr>
20+
<th data-header="sequence">#</th>
21+
<th data-header="query">SQL</th>
22+
<th data-header="count">Count</th>
23+
<th data-header="where">WHERE</th>
24+
<th data-header="loc">View/ Templ.</th>
25+
<th data-header="time">Time</th>
26+
<th data-header="acc">Acc.</th>
27+
</tr>
28+
</thead>
29+
<tbody>
30+
{% with sql_queries|annotate_sql_queries as sql_query_info %}
31+
{% for query in sql_query_info %}
32+
<tr>
33+
<td>{{ forloop.counter }}</td>
34+
<td>{{ query.sql|expand_comma|escape }}</td>
35+
<td>{{ query.count }}</td>
36+
<td>{{ query.where }}</td>
37+
<td>{{ query.loc }}</td>
38+
<td>{{ query.time }}</td>
39+
<td>{{ query.time_accum }}</td>
40+
</tr>
41+
{% endfor %}
42+
{% endwith %}
43+
</tbody>
44+
</table>
45+
</div>
46+
{% else %}
47+
<div class='text-center text-muted small'>Add 'ietf.context_processors.sql_debug' to settings.TEMPLATE_CONTECT_PROCESSORS to turn on the SQL statement table</div>
48+
{% endif %}
4349
{% endif %}

ietf/utils/templatetags/debug_filters.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import sys
2+
import sqlparse
3+
14
from django import template
25

36
register = template.Library()
@@ -20,21 +23,52 @@ def expand_comma(value):
2023
return value.replace(",", ", ")
2124

2225

26+
def get_sql_parts(sql):
27+
q = {}
28+
s = sqlparse.parse(sql)[0] # assuming there's only one statement
29+
q['where'] = None
30+
q['from'] = None
31+
# use sqlparse to pick out some interesting parts of the statement
32+
state = None
33+
for e in s:
34+
if e.is_whitespace:
35+
continue
36+
if state == None:
37+
if e.is_keyword:
38+
key = e.normalized.lower()
39+
state = 'value'
40+
elif e.is_group and e[0].is_keyword:
41+
key = e[0].normalized.lower()
42+
val = str(e)
43+
state = 'store'
44+
else:
45+
pass
46+
elif state == 'value':
47+
val = str(e)
48+
state = 'store'
49+
else:
50+
sys.stderr.write("Unexpected sqlparse iteration state in annotate_sql_queries(): '%s'" % state )
51+
if state == 'store':
52+
q[key] = val
53+
state = None
54+
return q
55+
2356
@register.filter()
2457
def annotate_sql_queries(queries):
2558
counts = {}
2659
timeacc = {}
2760
for q in queries:
2861
sql = q['sql']
62+
q.update(get_sql_parts(sql))
2963
if not sql in counts:
3064
counts[sql] = 0;
3165
counts[sql] += 1
3266
if not sql in timeacc:
3367
timeacc[sql] = 0.0;
3468
timeacc[sql] += float(q['time'])
3569
for q in queries:
36-
if q.get('where', None) == None:
37-
q['where'] = 'T' # template
70+
if q.get('loc', None) == None:
71+
q['loc'] = 'T' # template
3872
sql = q['sql']
3973
q['count'] = str(counts[sql])
4074
q['time_accum'] = "%4.3f" % timeacc[sql]

0 commit comments

Comments
 (0)