@@ -54,12 +54,14 @@ def fetch_testcases(self, selector: QuerySelector, name_patt=None,
5454 '''
5555
5656 @abc .abstractmethod
57- def fetch_sessions (self , selector : QuerySelector ):
57+ def fetch_sessions (self , selector : QuerySelector , decode = True ):
5858 '''Fetch sessions based on the specified query selector.
5959
6060 :arg selector: an instance of :class:`QuerySelector` that will specify
6161 the actual type of query requested.
62- :returns: A list of matching sessions.
62+ :arg decode: If set to :obj:`False`, do not decode the returned
63+ sessions and leave them JSON-encoded.
64+ :returns: A list of matching sessions, either decoded or not.
6365 '''
6466
6567 @abc .abstractmethod
@@ -101,6 +103,7 @@ def _db_file(self):
101103
102104 self ._db_create ()
103105
106+ self ._db_create_indexes ()
104107 self ._db_schema_check ()
105108 return self .__db_file
106109
@@ -161,12 +164,20 @@ def _db_create(self):
161164 'uuid TEXT, '
162165 'FOREIGN KEY(session_uuid) '
163166 'REFERENCES sessions(uuid) ON DELETE CASCADE)' )
167+
168+ # Update DB file mode
169+ os .chmod (self .__db_file , self .__db_file_mode )
170+
171+ def _db_create_indexes (self ):
172+ clsname = type (self ).__name__
173+ getlogger ().debug (f'{ clsname } : creating database indexes if needed' )
174+ with self ._db_connect (self .__db_file ) as conn :
164175 conn .execute ('CREATE INDEX IF NOT EXISTS index_testcases_time '
165176 'on testcases(job_completion_time_unix)' )
166177 conn .execute ('CREATE TABLE IF NOT EXISTS metadata('
167178 'schema_version TEXT)' )
168- # Update DB file mode
169- os . chmod ( self . __db_file , self . __db_file_mode )
179+ conn . execute ( 'CREATE INDEX IF NOT EXISTS index_sessions_time '
180+ 'on sessions(session_start_unix)' )
170181
171182 def _db_schema_check (self ):
172183 with self ._db_read (self .__db_file ) as conn :
@@ -232,10 +243,16 @@ def store(self, report, report_file=None):
232243 return self ._db_store_report (conn , report , report_file )
233244
234245 @time_function
235- def _decode_sessions (self , results , sess_filter ):
236- '''Decode sessions from the raw DB results.
246+ def _mass_json_decode (self , * json_objs ):
247+ data = rf'[{ "," .join (json_objs )} ]'
248+ getlogger ().debug (f'decoding JSON raw data of length { len (data )} ' )
249+ return json .loads (data )
237250
238- Return a map of session uuids to decoded session data
251+ @time_function
252+ def _fetch_sessions (self , results , sess_filter ):
253+ '''Fetch JSON-encoded sessions from the DB by applying a filter.
254+
255+ :returns: A list of the JSON-encoded valid sessions.
239256 '''
240257 sess_info_patt = re .compile (
241258 r'\"session_info\":\s+(?P<sess_info>\{.*?\})'
@@ -245,34 +262,31 @@ def _decode_sessions(self, results, sess_filter):
245262 def _extract_sess_info (s ):
246263 return sess_info_patt .search (s ).group ('sess_info' )
247264
248- @time_function
249- def _mass_json_decode (json_objs ):
250- data = '[' + ',' .join (json_objs ) + ']'
251- getlogger ().debug (
252- f'decoding JSON raw data of length { len (data )} '
253- )
254- return json .loads (data )
255-
256265 session_infos = {}
257266 sessions = {}
258267 for uuid , json_blob in results :
259268 sessions .setdefault (uuid , json_blob )
260269 session_infos .setdefault (uuid , _extract_sess_info (json_blob ))
261270
262- # Find the UUIDs to decode fully by inspecting only the session info
271+ # Find the relevant sessions by inspecting only the session info
263272 uuids = []
264- for sess_info in _mass_json_decode (session_infos .values ()):
273+ infos = self ._mass_json_decode (* session_infos .values ())
274+ for sess_info in infos :
265275 try :
266276 if self ._db_filter_json (sess_filter , sess_info ):
267277 uuids .append (sess_info ['uuid' ])
268278 except Exception :
269279 continue
270280
271- # Decode selected sessions
272- reports = _mass_json_decode (sessions [uuid ] for uuid in uuids )
281+ return [sessions [uuid ] for uuid in uuids ]
273282
274- # Return only the selected sessions
275- return {rpt ['session_info' ]['uuid' ]: rpt for rpt in reports }
283+ def _decode_and_index_sessions (self , json_blobs ):
284+ '''Decode the sessions and index them by their uuid.
285+
286+ :returns: A dictionary with uuids as keys and the sessions as values.
287+ '''
288+ return {sess ['session_info' ]['uuid' ]: sess
289+ for sess in self ._mass_json_decode (* json_blobs )}
276290
277291 @time_function
278292 def _fetch_testcases_raw (self , condition ):
@@ -289,7 +303,11 @@ def _fetch_testcases_raw(self, condition):
289303 results = conn .execute (query ).fetchall ()
290304
291305 getprofiler ().exit_region ()
292- sessions = self ._decode_sessions (results , None )
306+
307+ # Fetch, decode and index the sessions by their uuid
308+ sessions = self ._decode_and_index_sessions (
309+ self ._fetch_sessions (results , None )
310+ )
293311
294312 # Extract the test case data by extracting their UUIDs
295313 getprofiler ().enter_region ('sqlite testcase query' )
@@ -319,8 +337,8 @@ def _fetch_testcases_raw(self, condition):
319337 return testcases
320338
321339 @time_function
322- def _fetch_testcases_from_session (self , selector ,
323- name_patt = None , test_filter = None ):
340+ def _fetch_testcases_from_session (self , selector , name_patt = None ,
341+ test_filter = None ):
324342 query = 'SELECT uuid, json_blob from sessions'
325343 if selector .by_session_uuid ():
326344 query += f' WHERE uuid == "{ selector .uuid } "'
@@ -338,9 +356,11 @@ def _fetch_testcases_from_session(self, selector,
338356 if not results :
339357 return []
340358
341- sessions = self ._decode_sessions (
342- results ,
343- selector .sess_filter if selector .by_session_filter () else None
359+ sessions = self ._decode_and_index_sessions (
360+ self ._fetch_sessions (
361+ results ,
362+ selector .sess_filter if selector .by_session_filter () else None
363+ )
344364 )
345365 return [tc for sess in sessions .values ()
346366 for run in sess ['runs' ] for tc in run ['testcases' ]
@@ -366,15 +386,15 @@ def fetch_testcases(self, selector: QuerySelector,
366386 name_patt = None , test_filter = None ):
367387 if selector .by_session ():
368388 return self ._fetch_testcases_from_session (
369- selector , name_patt , test_filter
389+ selector , name_patt , test_filter ,
370390 )
371391 else :
372392 return self ._fetch_testcases_time_period (
373393 * selector .time_period , name_patt , test_filter
374394 )
375395
376396 @time_function
377- def fetch_sessions (self , selector : QuerySelector ):
397+ def fetch_sessions (self , selector : QuerySelector , decode = True ):
378398 query = 'SELECT uuid, json_blob FROM sessions'
379399 if selector .by_time_period ():
380400 ts_start , ts_end = selector .time_period
@@ -389,11 +409,14 @@ def fetch_sessions(self, selector: QuerySelector):
389409 results = conn .execute (query ).fetchall ()
390410
391411 getprofiler ().exit_region ()
392- session = self ._decode_sessions (
412+ raw_sessions = self ._fetch_sessions (
393413 results ,
394414 selector .sess_filter if selector .by_session_filter () else None
395415 )
396- return [* session .values ()]
416+ if decode :
417+ return [* self ._decode_and_index_sessions (raw_sessions ).values ()]
418+ else :
419+ return raw_sessions
397420
398421 def _do_remove (self , conn , uuids ):
399422 '''Remove sessions'''
0 commit comments