Added debug_toolbar app to project.
[wolnelektury.git] / apps / debug_toolbar / panels / sql.py
1 from debug_toolbar.panels import DebugPanel
2 from django.db import connection
3 from django.db.backends import util
4 from django.template.loader import render_to_string
5 from django.shortcuts import render_to_response
6 from django.http import HttpResponse
7 from django.utils import simplejson
8 from time import time
9
10 class DatabaseStatTracker(util.CursorDebugWrapper):
11     """Replacement for CursorDebugWrapper which stores additional information
12     in `connection.queries`."""
13     def execute(self, sql, params=()):
14         start = time()
15         try:
16             return self.cursor.execute(sql, params)
17         finally:
18             stop = time()
19             # We keep `sql` to maintain backwards compatibility
20             self.db.queries.append({
21                 'sql': self.db.ops.last_executed_query(self.cursor, sql, params),
22                 'time': stop - start,
23                 'raw_sql': sql,
24                 'params': params,
25             })
26
27 util.CursorDebugWrapper = DatabaseStatTracker
28
29 class SQLDebugPanel(DebugPanel):
30     """
31     Panel that displays information about the SQL queries run while processing the request.
32     """
33     name = 'SQL'
34     
35     def reformat_sql(self, sql):
36         sql = sql.replace('`,`', '`, `')
37         sql = sql.replace('` FROM `', '` \n  FROM `')
38         sql = sql.replace('` WHERE ', '` \n  WHERE ')
39         sql = sql.replace('`) WHERE ', '`) \n  WHERE ')
40         sql = sql.replace(' ORDER BY ', ' \n  ORDER BY ')
41         return sql
42     
43     def process_ajax(self, request):
44         action = request.GET.get('op')
45         if action == 'explain':
46             # XXX: loop through each sql statement to output explain?
47             sql = request.GET.get('sql', '').split(';')[0]
48             if sql.lower().startswith('select'):
49                 if 'params' in request.GET:
50                     params = simplejson.loads(request.GET['params'])
51                 else:
52                     params = []
53                 cursor = connection.cursor()
54                 cursor.execute("EXPLAIN %s" % (sql,), params)
55                 response = cursor.fetchall()
56                 cursor.close()
57                 context = {'explain': response, 'sql': self.reformat_sql(sql), 'params': params}
58                 return render_to_response('debug_toolbar/panels/sql_explain.html', context)
59             else:
60                 return HttpResponse('Invalid SQL', mimetype="text/plain", status_code=403)
61     
62     def process_request(self, request):
63         self.queries_offset = len(connection.queries)
64     
65     def process_response(self, request, response):
66         self.queries = connection.queries[self.queries_offset:]
67         self.total_time = sum(map(lambda q: float(q['time'])*1000, self.queries))
68         return response
69
70     def title(self):
71         return 'SQL: %.2fms (%d queries)' % (self.total_time, len(self.queries))
72
73     def url(self):
74         return ''
75
76     def content(self):
77         queries = [(q['time'], q['sql'], q['raw_sql'], simplejson.dumps(q['params'])) for q in sorted(self.queries, key=lambda x: x['time'])[::-1]]
78         context = {'queries': queries}
79         return render_to_string('debug_toolbar/panels/sql.html', context)