11import logging
22import traceback
33
4- from django .core .exceptions import EmptyResultSet
4+ from django .apps import apps
5+ from django .db import connection
56from django .utils import timezone
67from django .utils .encoding import force_str
78
8- from silk .collector import DataCollector
99from silk .config import SilkyConfig
1010
1111Logger = logging .getLogger ('silk.sql' )
1212
1313
14- def _should_wrap (sql_query ):
15- if not DataCollector ().request :
16- return False
17-
18- for ignore_str in SilkyConfig ().SILKY_IGNORE_QUERIES :
19- if ignore_str in sql_query :
20- return False
21- return True
22-
23-
2414def _unpack_explanation (result ):
2515 for row in result :
2616 if not isinstance (row , str ):
@@ -34,16 +24,14 @@ def _explain_query(connection, q, params):
3424 if SilkyConfig ().SILKY_ANALYZE_QUERIES :
3525 # Work around some DB engines not supporting analyze option
3626 try :
37- prefix = connection .ops .explain_query_prefix (
38- analyze = True , ** (SilkyConfig ().SILKY_EXPLAIN_FLAGS or {})
39- )
27+ prefix = connection .ops .explain_query_prefix (analyze = True , ** (SilkyConfig ().SILKY_EXPLAIN_FLAGS or {}))
4028 except ValueError as error :
4129 error_str = str (error )
4230 if error_str .startswith ("Unknown options:" ):
4331 Logger .warning (
44- "Database does not support analyzing queries with provided params. %s."
32+ "Database does not support analyzing queries with provided params. %s. "
4533 "SILKY_ANALYZE_QUERIES option will be ignored" ,
46- error_str
34+ error_str ,
4735 )
4836 prefix = connection .ops .explain_query_prefix ()
4937 else :
@@ -61,40 +49,53 @@ def _explain_query(connection, q, params):
6149 return None
6250
6351
64- def execute_sql (self , * args , ** kwargs ):
65- """wrapper around real execute_sql in order to extract information"""
52+ class SilkQueryWrapper :
53+ def __init__ (self ):
54+ # Local import to prevent messing app.ready()
55+ from silk .collector import DataCollector
6656
67- try :
68- q , params = self .as_sql ()
69- if not q :
70- raise EmptyResultSet
71- except EmptyResultSet :
72- try :
73- result_type = args [0 ]
74- except IndexError :
75- result_type = kwargs .get ('result_type' , 'multi' )
76- if result_type == 'multi' :
77- return iter ([])
78- else :
79- return
80- tb = '' .join (reversed (traceback .format_stack ()))
81- sql_query = q % tuple (force_str (param ) for param in params )
82- if _should_wrap (sql_query ):
83- query_dict = {
84- 'query' : sql_query ,
85- 'start_time' : timezone .now (),
86- 'traceback' : tb
87- }
57+ self .data_collector = DataCollector ()
58+ self .silk_model_table_names = [model ._meta .db_table for model in apps .get_app_config ('silk' ).get_models ()]
59+
60+ def __call__ (self , execute , sql , params , many , context ):
61+ sql_query = sql % tuple (force_str (param ) for param in params ) if params else sql
62+ query_dict = None
63+ if self ._should_wrap (sql_query ):
64+ tb = '' .join (reversed (traceback .format_stack ()))
65+ query_dict = {'query' : sql_query , 'start_time' : timezone .now (), 'traceback' : tb }
8866 try :
89- return self . _execute_sql ( * args , ** kwargs )
67+ return execute ( sql , params , many , context )
9068 finally :
91- query_dict ['end_time' ] = timezone .now ()
92- request = DataCollector ().request
93- if request :
94- query_dict ['request' ] = request
95- if getattr (self .query .model , '__module__' , '' ) != 'silk.models' :
96- query_dict ['analysis' ] = _explain_query (self .connection , q , params )
97- DataCollector ().register_query (query_dict )
98- else :
99- DataCollector ().register_silk_query (query_dict )
100- return self ._execute_sql (* args , ** kwargs )
69+ if query_dict :
70+ query_dict ['end_time' ] = timezone .now ()
71+ request = self .data_collector .request
72+ if request :
73+ query_dict ['request' ] = request
74+ if not any (table_name in sql_query for table_name in self .silk_model_table_names ):
75+ query_dict ['analysis' ] = _explain_query (connection , sql , params )
76+ self .data_collector .register_query (query_dict )
77+ else :
78+ self .data_collector .register_silk_query (query_dict )
79+
80+ def _should_wrap (self , sql_query ):
81+ # Must have a request ongoing
82+ if not self .data_collector .request :
83+ return False
84+
85+ # Must not try to explain 'EXPLAIN' queries or transaction stuff
86+ if any (
87+ sql_query .startswith (keyword )
88+ for keyword in [
89+ 'SAVEPOINT' ,
90+ 'RELEASE SAVEPOINT' ,
91+ 'ROLLBACK TO SAVEPOINT' ,
92+ 'PRAGMA' ,
93+ connection .ops .explain_query_prefix (),
94+ ]
95+ ):
96+ return False
97+
98+ for ignore_str in SilkyConfig ().SILKY_IGNORE_QUERIES :
99+ if ignore_str in sql_query :
100+ return False
101+ return True
0 commit comments