1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.cobalt.data;
18 
19 import androidx.room.Dao;
20 import androidx.room.Delete;
21 import androidx.room.Insert;
22 import androidx.room.MapInfo;
23 import androidx.room.OnConflictStrategy;
24 import androidx.room.Query;
25 import androidx.room.Update;
26 
27 import com.google.cobalt.AggregateValue;
28 import com.google.cobalt.UnencryptedObservationBatch;
29 import com.google.common.hash.HashCode;
30 
31 import java.util.ArrayList;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Optional;
35 
36 /**
37  * Data Access Object offering low-level operations to the database that can be used for more
38  * complex work.
39  */
40 @Dao
41 public abstract class DaoBuildingBlocks {
42     /**
43      * Inserts an entry into the system profiles table.
44      *
45      * @param systemProfileEntity the system profile to insert
46      */
47     @Insert(onConflict = OnConflictStrategy.IGNORE)
insertSystemProfile(SystemProfileEntity systemProfileEntity)48     abstract Void insertSystemProfile(SystemProfileEntity systemProfileEntity);
49 
50     /**
51      * Inserts an aggregate value in the aggregate store.
52      *
53      * @param aggregateStoreEntity the aggregate to insert
54      */
55     @Insert(onConflict = OnConflictStrategy.ROLLBACK)
insertAggregateValue(AggregateStoreEntity aggregateStoreEntity)56     abstract Void insertAggregateValue(AggregateStoreEntity aggregateStoreEntity);
57 
58     /**
59      * Inserts a string hash into the string hashes store.
60      *
61      * @param stringHashEntity the string hash to insert
62      */
63     @Insert(onConflict = OnConflictStrategy.IGNORE)
insertStringHash(StringHashEntity stringHashEntity)64     abstract Void insertStringHash(StringHashEntity stringHashEntity);
65 
66     /**
67      * Inserts the value for ths key of `globalValueEntity`.
68      *
69      * @param globalValueEntity the key, value pair to insert or update
70      */
71     @Insert(onConflict = OnConflictStrategy.IGNORE)
insertGlobalValue(GlobalValueEntity globalValueEntity)72     abstract Void insertGlobalValue(GlobalValueEntity globalValueEntity);
73 
74     /**
75      * Inserts or update the value for ths key of `globalValueEntity`.
76      *
77      * @param globalValueEntity the key, value pair to insert or update
78      */
79     @Insert(onConflict = OnConflictStrategy.REPLACE)
insertOrReplaceGlobalValue(GlobalValueEntity globalValueEntity)80     abstract Void insertOrReplaceGlobalValue(GlobalValueEntity globalValueEntity);
81 
82     /**
83      * Inserts the day a report was last sent.
84      *
85      * @param reportKey the report
86      * @param dayIndex the day
87      */
88     @Insert(onConflict = OnConflictStrategy.IGNORE)
insertLastSentDayIndex(ReportKey reportKey, int dayIndex)89     Void insertLastSentDayIndex(ReportKey reportKey, int dayIndex) {
90         return insertLastSentDayIndex(ReportEntity.create(reportKey, dayIndex));
91     }
92 
93     /**
94      * Inserts the day a report was last sent.
95      *
96      * @param reportEntity the report and day index to insert
97      */
98     @Insert(onConflict = OnConflictStrategy.IGNORE)
insertLastSentDayIndex(ReportEntity reportEntity)99     abstract Void insertLastSentDayIndex(ReportEntity reportEntity);
100 
101     /**
102      * Inserts observation batches into the observation store.
103      *
104      * @param observationBatches the observation batches to insert
105      */
insertObservationBatches(List<UnencryptedObservationBatch> observationBatches)106     Void insertObservationBatches(List<UnencryptedObservationBatch> observationBatches) {
107         List<ObservationStoreEntity> observationStoreEntities = new ArrayList<>();
108         for (UnencryptedObservationBatch batch : observationBatches) {
109             observationStoreEntities.add(ObservationStoreEntity.createForInsertion(batch));
110         }
111         return insertObservations(observationStoreEntities);
112     }
113 
114     /**
115      * Inserts observations into the observation store.
116      *
117      * @param observationStoreEntities the observations to insert
118      */
119     @Insert(onConflict = OnConflictStrategy.ROLLBACK)
insertObservations(List<ObservationStoreEntity> observationStoreEntities)120     abstract Void insertObservations(List<ObservationStoreEntity> observationStoreEntities);
121 
122     /**
123      * Update the day a report was last sent.
124      *
125      * @param reportKey the report
126      * @param dayIndex the new day index day index to update
127      */
updateLastSentDayIndex(ReportKey reportKey, int dayIndex)128     Void updateLastSentDayIndex(ReportKey reportKey, int dayIndex) {
129         return updateLastSentDayIndex(ReportEntity.create(reportKey, dayIndex));
130     }
131 
132     /**
133      * Update the day a report was last sent.
134      *
135      * @param reportEntity the report and day index to update
136      */
137     @Update(entity = ReportEntity.class)
updateLastSentDayIndex(ReportEntity reportEntity)138     abstract Void updateLastSentDayIndex(ReportEntity reportEntity);
139 
140     /**
141      * Update an aggregate value in the aggregate store.
142      *
143      * @param reportKey the report to update the value under
144      * @param dayIndex the day the event occrurred
145      * @param eventVector the event vector the value is being aggregated for
146      * @param systemProfileHash the system profile hash of the event
147      * @param newAggregateValue the new aggregate value
148      */
updateAggregateValue( ReportKey reportKey, int dayIndex, EventVector eventVector, long systemProfileHash, AggregateValue newAggregateValue)149     Void updateAggregateValue(
150             ReportKey reportKey,
151             int dayIndex,
152             EventVector eventVector,
153             long systemProfileHash,
154             AggregateValue newAggregateValue) {
155         return updateAggregateValue(
156                 reportKey.customerId(),
157                 reportKey.projectId(),
158                 reportKey.metricId(),
159                 reportKey.reportId(),
160                 dayIndex,
161                 eventVector,
162                 systemProfileHash,
163                 newAggregateValue);
164     }
165 
166     /**
167      * Update an aggregate value in the aggregate store.
168      *
169      * @param customerId the customer id to update the value under
170      * @param projectId the project id to update the value under
171      * @param metricId the metric id to update the value under
172      * @param reportId the report id to update the value under
173      * @param dayIndex the day the event occrurred
174      * @param eventVector the event vector the value is being aggregated for
175      * @param systemProfileHash the system profile hash of the event
176      * @param newAggregateValue the new aggregate value
177      */
178     @Query(
179             "UPDATE AggregateStore "
180                     + "SET aggregate_value = :newAggregateValue "
181                     + "WHERE customer_id = :customerId "
182                     + "AND project_id = :projectId "
183                     + "AND metric_id = :metricId "
184                     + "AND report_id = :reportId "
185                     + "AND day_index= :dayIndex "
186                     + "AND event_vector = :eventVector "
187                     + "AND system_profile_hash = :systemProfileHash")
updateAggregateValue( long customerId, long projectId, long metricId, long reportId, int dayIndex, EventVector eventVector, long systemProfileHash, AggregateValue newAggregateValue)188     abstract Void updateAggregateValue(
189             long customerId,
190             long projectId,
191             long metricId,
192             long reportId,
193             int dayIndex,
194             EventVector eventVector,
195             long systemProfileHash,
196             AggregateValue newAggregateValue);
197 
198     /**
199      * Update the system profile hash of an aggregate value.
200      *
201      * @param customerId the customer id to update the value under
202      * @param projectId the project id to update the value under
203      * @param metricId the metric id to update the value under
204      * @param reportId the report id to update the value under
205      * @param dayIndex the day the event occrurred
206      * @param eventVector the event vector the value is being aggregated for
207      * @param currentSystemProfileHash the current system profile hash of the event
208      * @param newSystemProfileHash the new system profile hash of the event
209      */
210     @Query(
211             "UPDATE AggregateStore "
212                     + "SET system_profile_hash = :newSystemProfileHash "
213                     + "WHERE customer_id = :customerId "
214                     + "AND project_id = :projectId "
215                     + "AND metric_id = :metricId "
216                     + "AND report_id = :reportId "
217                     + "AND day_index= :dayIndex "
218                     + "AND event_vector = :eventVector "
219                     + "AND system_profile_hash = :currentSystemProfileHash")
updateSystemProfileHash( long customerId, long projectId, long metricId, long reportId, int dayIndex, EventVector eventVector, long currentSystemProfileHash, long newSystemProfileHash)220     abstract Void updateSystemProfileHash(
221             long customerId,
222             long projectId,
223             long metricId,
224             long reportId,
225             int dayIndex,
226             EventVector eventVector,
227             long currentSystemProfileHash,
228             long newSystemProfileHash);
229 
230     /**
231      * Update the system profile hash and aggregate value of an event.
232      *
233      * @param customerId the customer id to update the value under
234      * @param projectId the project id to update the value under
235      * @param metricId the metric id to update the value under
236      * @param reportId the report id to update the value under
237      * @param dayIndex the day the event occrurred
238      * @param eventVector the event vector the value is being aggregated for
239      * @param currentSystemProfileHash the current system profile hash of the event
240      * @param newSystemProfileHash the new system profile hash of the event
241      * @param newAggregateValue the new aggregate value
242      */
243     @Query(
244             "UPDATE AggregateStore "
245                     + "SET system_profile_hash = :newSystemProfileHash, "
246                     + "aggregate_value = :newAggregateValue "
247                     + "WHERE customer_id = :customerId "
248                     + "AND project_id = :projectId "
249                     + "AND metric_id = :metricId "
250                     + "AND report_id = :reportId "
251                     + "AND day_index= :dayIndex "
252                     + "AND event_vector = :eventVector "
253                     + "AND system_profile_hash = :currentSystemProfileHash")
updateSystemProfileHashAndAggregateValue( long customerId, long projectId, long metricId, long reportId, int dayIndex, EventVector eventVector, long currentSystemProfileHash, long newSystemProfileHash, AggregateValue newAggregateValue)254     abstract Void updateSystemProfileHashAndAggregateValue(
255             long customerId,
256             long projectId,
257             long metricId,
258             long reportId,
259             int dayIndex,
260             EventVector eventVector,
261             long currentSystemProfileHash,
262             long newSystemProfileHash,
263             AggregateValue newAggregateValue);
264 
265     /**
266      * Query the saved global values related to enablement.
267      *
268      * @return a map of the enablement values which have been saved
269      */
270     @MapInfo(keyColumn = "key", valueColumn = "value")
271     @Query(
272             "SELECT * FROM GlobalValues WHERE key IN ('INITIAL_ENABLED_TIME',"
273                     + " 'INITIAL_DISABLED_TIME')")
queryEnablementTimes()274     abstract Map<GlobalValueEntity.Key, String> queryEnablementTimes();
275 
276     /**
277      * Selects one (system profile hash, aggregate value) pair from the DB for the given report,
278      * day, and event vector to update. The returned system profile hash will be
279      * `systemProfileHashHint` if it exists for the report, day, and event vector. Otherwise, a row
280      * with a random system profile associated with report, day, and event vector will be returned.
281      *
282      * @param reportKey the report selected values are for
283      * @param dayIndex the day selected values are aggregated on
284      * @param eventVector the event codes (if any) selected values match
285      * @param systemProfileHashHint the system profile of the record to return, if it exists
286      * @return the system profile hash and aggregate value, if one is found
287      */
queryOneSystemProfileAndAggregateValue( ReportKey reportKey, int dayIndex, EventVector eventVector, long systemProfileHashHint)288     Optional<SystemProfileAndAggregateValue> queryOneSystemProfileAndAggregateValue(
289             ReportKey reportKey,
290             int dayIndex,
291             EventVector eventVector,
292             long systemProfileHashHint) {
293         return queryOneSystemProfileAndAggregateValue(
294                 reportKey.customerId(),
295                 reportKey.projectId(),
296                 reportKey.metricId(),
297                 reportKey.reportId(),
298                 dayIndex,
299                 eventVector,
300                 systemProfileHashHint);
301     }
302 
303     /**
304      * Selects one (system profile hash, aggregate value) pair from the DB for the given report,
305      * day, and event vector to update. The returned system profile hash will be
306      * `systemProfileHashHint` if it exists for the report, day, and event vector. Otherwise, a row
307      * with a random system profile associated with report, day, and event vector will be returned.
308      *
309      * @param customerId the customer selected values are for
310      * @param projectId the project selected values are for
311      * @param metricId the metric selected values are for
312      * @param reportId the report selected values are for
313      * @param dayIndex the day selected values are aggregated on
314      * @param eventVector the event codes (if any) selected values match
315      * @param systemProfileHashHint the system profile of the record to return, if it exists
316      * @return the system profile hash and aggregate value, if one is found
317      */
318     @Query(
319             "SELECT system_profile_hash, aggregate_value FROM AggregateStore "
320                     + "WHERE customer_id = :customerId "
321                     + "AND project_id = :projectId "
322                     + "AND metric_id = :metricId "
323                     + "AND report_id = :reportId "
324                     + "AND day_index = :dayIndex "
325                     + "AND event_vector = :eventVector "
326                     + "ORDER BY system_profile_hash = :systemProfileHashHint DESC, "
327                     + "system_profile_hash ASC "
328                     + "LIMIT 1")
queryOneSystemProfileAndAggregateValue( long customerId, long projectId, long metricId, long reportId, int dayIndex, EventVector eventVector, long systemProfileHashHint)329     abstract Optional<SystemProfileAndAggregateValue> queryOneSystemProfileAndAggregateValue(
330             long customerId,
331             long projectId,
332             long metricId,
333             long reportId,
334             int dayIndex,
335             EventVector eventVector,
336             long systemProfileHashHint);
337 
338     /**
339      * Count the number of distinct event vectors saved for a report on a given day and under a
340      * specific system profile.
341      *
342      * @param reportKey the report to search under
343      * @param dayIndex the day to search under
344      * @param systemProfileHash the system profile hash of the event
345      * @return the number of distnct event codes
346      */
queryCountEventVectors(ReportKey reportKey, int dayIndex, long systemProfileHash)347     int queryCountEventVectors(ReportKey reportKey, int dayIndex, long systemProfileHash) {
348         return queryCountEventVectors(
349                 reportKey.customerId(),
350                 reportKey.projectId(),
351                 reportKey.metricId(),
352                 reportKey.reportId(),
353                 dayIndex,
354                 systemProfileHash);
355     }
356 
357     /**
358      * Returns the index a string hash should be assigned for a report on a given day. The returned
359      * value will be the index of `stringHashHint` if it exists in the list, the next string index
360      * value if string buffer max does not apply, or -1 if the string buffer max is in effect.
361      *
362      * @param customerId the customer id to search under
363      * @param projectId the project id to search under
364      * @param metricId the metric id to search under
365      * @param reportId the report id to search under
366      * @param dayIndex the day to search under
367      * @param stringBufferMax the maximum number of strings that should be stored
368      * @param stringHashHint the string hash that will be added
369      * @return the index that should be used for `stringHashHint` or -1 if it should not be inserted
370      */
371     @Query(
372             "SELECT CASE "
373                     + "  WHEN hash_is_equal THEN max_list_index "
374                     + "  WHEN :stringBufferMax < 1 THEN max_list_index + 1 "
375                     + "  WHEN max_list_index + 1 < :stringBufferMax THEN max_list_index + 1 "
376                     + "  ELSE -1 "
377                     + "END "
378                     + "FROM ("
379                     + "  SELECT "
380                     + "  string_hash = :stringHashHint AS hash_is_equal, "
381                     + "  MAX(list_index) AS max_list_index "
382                     + "  FROM StringHashes "
383                     + "  WHERE customer_id = :customerId "
384                     + "    AND project_id = :projectId "
385                     + "    AND metric_id = :metricId "
386                     + "    AND report_id = :reportId "
387                     + "    AND day_index = :dayIndex "
388                     + "  GROUP BY string_hash = :stringHashHint "
389                     + "  ORDER BY hash_is_equal DESC "
390                     + ") "
391                     + "LIMIT 1")
queryStringListIndex( long customerId, long projectId, long metricId, long reportId, int dayIndex, long stringBufferMax, HashCode stringHashHint)392     abstract int queryStringListIndex(
393             long customerId,
394             long projectId,
395             long metricId,
396             long reportId,
397             int dayIndex,
398             long stringBufferMax,
399             HashCode stringHashHint);
400 
401     /**
402      * Returns the index a string hash should be assigned for a report on a given day. The returned
403      * value will be the index of `stringHashHint` if it exists in the list, the next string index
404      * value if string buffer max does not apply, or -1 if the string buffer max is in effect.
405      *
406      * @param reportKey the report
407      * @param dayIndex the day to search under
408      * @param stringBufferMax the maximum number of strings that should be stored
409      * @param stringHashHint the string hash that will be added
410      * @return the index that should be used for `stringHashHint` or -1 if it should not be inserted
411      */
queryStringListIndex( ReportKey reportKey, int dayIndex, long stringBufferMax, HashCode stringHashHint)412     int queryStringListIndex(
413             ReportKey reportKey, int dayIndex, long stringBufferMax, HashCode stringHashHint) {
414         return queryStringListIndex(
415                 reportKey.customerId(),
416                 reportKey.projectId(),
417                 reportKey.metricId(),
418                 reportKey.reportId(),
419                 dayIndex,
420                 stringBufferMax,
421                 stringHashHint);
422     }
423 
424     /**
425      * Returns the string hash list for a specific report and day.
426      *
427      * <p>Note, results do not have a guaranteed order.
428      *
429      * @param customerId the customer id to search under
430      * @param projectId the project id to search under
431      * @param metricId the metric id to search under
432      * @param reportId the report id to search under
433      * @param dayIndex the day to search under
434      * @return a list of string hashes and their respective indices
435      */
436     @Query(
437             "SELECT "
438                     + "list_index, "
439                     + "string_hash "
440                     + "FROM StringHashes "
441                     + "WHERE customer_id = :customerId "
442                     + "AND project_id = :projectId "
443                     + "AND metric_id = :metricId "
444                     + "AND report_id = :reportId "
445                     + "AND day_index = :dayIndex")
queryStringHashList( long customerId, long projectId, long metricId, long reportId, int dayIndex)446     abstract List<StringListEntry> queryStringHashList(
447             long customerId, long projectId, long metricId, long reportId, int dayIndex);
448 
449     /**
450      * Returns the string hash list for a specific report and day.
451      *
452      * <p>Note, results do not have a guaranteed order.
453      *
454      * @param reportKey the report
455      * @param dayIndex the day to search under
456      * @return a list of string hashes and their respective indices
457      */
queryStringHashList(ReportKey reportKey, int dayIndex)458     public List<StringListEntry> queryStringHashList(ReportKey reportKey, int dayIndex) {
459         return queryStringHashList(
460                 reportKey.customerId(),
461                 reportKey.projectId(),
462                 reportKey.metricId(),
463                 reportKey.reportId(),
464                 dayIndex);
465     }
466 
467     /**
468      * Count the number of distinct event vectors saved for a report on a given day and under a
469      * specific system profile.
470      *
471      * @param customerId the customer id to search under
472      * @param projectId the project id to search under
473      * @param metricId the metric id to search under
474      * @param reportId the report id to search under
475      * @param dayIndex the day to search under
476      * @param systemProfileHash the system profile hash of the event
477      * @return the number of distnct event codes
478      */
479     @Query(
480             "SELECT COUNT(DISTINCT event_vector) "
481                     + "FROM AggregateStore "
482                     + "WHERE customer_id = :customerId "
483                     + "AND project_id = :projectId "
484                     + "AND metric_id = :metricId "
485                     + "AND report_id = :reportId "
486                     + "AND day_index= :dayIndex "
487                     + "AND system_profile_hash = :systemProfileHash")
queryCountEventVectors( long customerId, long projectId, long metricId, long reportId, int dayIndex, long systemProfileHash)488     abstract int queryCountEventVectors(
489             long customerId,
490             long projectId,
491             long metricId,
492             long reportId,
493             int dayIndex,
494             long systemProfileHash);
495 
496     /**
497      * Get the aggregated values for a given report on a given day.
498      *
499      * @param reportKey the report to search under
500      * @param dayIndex the day to search under
501      * @return a list of events to be used for observation generation
502      */
queryEventRecordsForDay(ReportKey reportKey, int dayIndex)503     List<EventRecordAndSystemProfile> queryEventRecordsForDay(ReportKey reportKey, int dayIndex) {
504         return queryEventRecordsForDay(
505                 reportKey.customerId(),
506                 reportKey.projectId(),
507                 reportKey.metricId(),
508                 reportKey.reportId(),
509                 dayIndex);
510     }
511 
512     /**
513      * Get the aggregated values for a given report on a given day.
514      *
515      * @param customerId the customer id to search under
516      * @param projectId the project id to search under
517      * @param metricId the metric id to search under
518      * @param reportId the report id to search under
519      * @param dayIndex the day to search under
520      * @return a list of events to be used for observation generation
521      */
522     @Query(
523             "SELECT "
524                     + "profile.system_profile, "
525                     + "aggregate.event_vector, "
526                     + "aggregate.aggregate_value "
527                     + "FROM "
528                     + "AggregateStore AS aggregate "
529                     + "INNER JOIN SystemProfiles AS profile "
530                     + "ON aggregate.system_profile_hash = profile.system_profile_hash "
531                     + "WHERE customer_id = :customerId "
532                     + "AND project_id = :projectId "
533                     + "AND metric_id = :metricId "
534                     + "AND report_id = :reportId "
535                     + "AND day_index= :dayIndex "
536                     + "ORDER BY aggregate.system_profile_hash, "
537                     + "aggregate.event_vector")
queryEventRecordsForDay( long customerId, long projectId, long metricId, long reportId, int dayIndex)538     abstract List<EventRecordAndSystemProfile> queryEventRecordsForDay(
539             long customerId, long projectId, long metricId, long reportId, int dayIndex);
540 
541     /**
542      * Returns the observations in the observation store, ordered by creation time.
543      *
544      * @return an list of observations, ordered by creation time
545      */
546     @Query("SELECT * FROM ObservationStore ORDER BY observation_store_id ASC")
queryOldestObservations()547     abstract List<ObservationStoreEntity> queryOldestObservations();
548 
549     /**
550      * Returns the day a report was last sent, if in the reports table.
551      *
552      * @param reportKey the report
553      * @return the last sent day index, if found
554      */
queryLastSentDayIndex(ReportKey reportKey)555     Optional<Integer> queryLastSentDayIndex(ReportKey reportKey) {
556         return queryLastSentDayIndex(
557                 reportKey.customerId(),
558                 reportKey.projectId(),
559                 reportKey.metricId(),
560                 reportKey.reportId());
561     }
562 
563     /** Returns the keys of all reports in the report store. */
564     @Query("SELECT customer_id, project_id, metric_id, report_id FROM Reports")
queryReportKeys()565     abstract List<ReportKey> queryReportKeys();
566 
567     /**
568      * Returns the day a report was last sent, if in the reports table.
569      *
570      * @param customerId the customer id for the report
571      * @param projectId the project id for the report
572      * @param metricId the metric id for the report
573      * @param reportId the report id for the report
574      * @return the last sent day index, if found
575      */
576     @Query(
577             "SELECT last_sent_day_index "
578                     + "FROM Reports "
579                     + "WHERE customer_id = :customerId "
580                     + "AND project_id = :projectId "
581                     + "AND metric_id = :metricId "
582                     + "AND report_id = :reportId")
queryLastSentDayIndex( long customerId, long projectId, long metricId, long reportId)583     abstract Optional<Integer> queryLastSentDayIndex(
584             long customerId, long projectId, long metricId, long reportId);
585 
586     /** Delete the saved initial disabled time. */
587     @Query("DELETE FROM GlobalValues WHERE key = 'INITIAL_DISABLED_TIME'")
deleteDisabledTime()588     abstract Void deleteDisabledTime();
589 
590     /**
591      * Delete aggregate values from before the specified day.
592      *
593      * @param oldestDayIndex oldest day index to keep events from
594      */
595     @Query("DELETE FROM AggregateStore WHERE day_index < :oldestDayIndex")
deleteOldAggregates(int oldestDayIndex)596     abstract Void deleteOldAggregates(int oldestDayIndex);
597 
598     /** Deletes string hashes that don't have corresponding values in the aggregate store. */
599     @Query(
600             "DELETE FROM StringHashes "
601                     + "WHERE (customer_id, project_id, metric_id, report_id, day_index) "
602                     + "NOT IN ( "
603                     + "SELECT DISTINCT customer_id, project_id, metric_id, report_id, day_index "
604                     + "FROM AggregateStore "
605                     + ")")
deleteUnusedStringHashes()606     abstract void deleteUnusedStringHashes();
607 
608     /** Delete system profiles which don't appear in the aggregate store. */
609     @Query(
610             "DELETE FROM SystemProfiles "
611                     + "WHERE system_profile_hash NOT IN ("
612                     + "SELECT DISTINCT system_profile_hash FROM AggregateStore"
613                     + ")")
deleteUnusedSystemProfileHashes()614     abstract Void deleteUnusedSystemProfileHashes();
615 
616     /**
617      * Delete the specified observations from the observation store.
618      *
619      * @param observationStoreIds ids of the observations to delete
620      */
621     @Query("DELETE FROM ObservationStore WHERE observation_store_id IN (:observationStoreIds)")
deleteByObservationId(List<Integer> observationStoreIds)622     abstract Void deleteByObservationId(List<Integer> observationStoreIds);
623 
624     /**
625      * Delete the specified reports from the report store.
626      *
627      * @param reportKeys the reports to delete
628      */
629     @Delete(entity = ReportEntity.class)
deleteReports(List<ReportKey> reportKeys)630     abstract Void deleteReports(List<ReportKey> reportKeys);
631 }
632