1 /*
2  * Copyright (C) 2017 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 androidx.work.impl.model;
18 
19 import static android.arch.persistence.room.OnConflictStrategy.IGNORE;
20 
21 import static androidx.work.impl.model.WorkTypeConverters.StateIds.COMPLETED_STATES;
22 
23 import android.arch.lifecycle.LiveData;
24 import android.arch.persistence.room.Dao;
25 import android.arch.persistence.room.Insert;
26 import android.arch.persistence.room.Query;
27 import android.arch.persistence.room.Transaction;
28 import android.support.annotation.NonNull;
29 
30 import androidx.work.Data;
31 import androidx.work.State;
32 
33 import java.util.List;
34 
35 /**
36  * The Data Access Object for {@link WorkSpec}s.
37  */
38 @Dao
39 public interface WorkSpecDao {
40 
41     /**
42      * Attempts to insert a {@link WorkSpec} into the database.
43      *
44      * @param workSpec The WorkSpec to insert.
45      */
46     @Insert(onConflict = IGNORE)
insertWorkSpec(WorkSpec workSpec)47     void insertWorkSpec(WorkSpec workSpec);
48 
49     /**
50      * Deletes {@link WorkSpec}s from the database.
51      *
52      * @param id The WorkSpec id to delete.
53      */
54     @Query("DELETE FROM workspec WHERE id=:id")
delete(String id)55     void delete(String id);
56 
57     /**
58      * @param id The identifier
59      * @return The WorkSpec associated with that id
60      */
61     @Query("SELECT * FROM workspec WHERE id=:id")
getWorkSpec(String id)62     WorkSpec getWorkSpec(String id);
63 
64     /**
65      * Retrieves {@link WorkSpec}s with the identifiers.
66      *
67      * @param ids The identifiers of desired {@link WorkSpec}s
68      * @return The {@link WorkSpec}s with the requested IDs
69      */
70     @Query("SELECT * FROM workspec WHERE id IN (:ids)")
getWorkSpecs(List<String> ids)71     WorkSpec[] getWorkSpecs(List<String> ids);
72 
73     /**
74      * Retrieves {@link WorkSpec}s labelled with a given name.
75      *
76      * @param name The work graph name
77      * @return The {@link WorkSpec}s labelled with the given name
78      */
79     @Query("SELECT id, state FROM workspec WHERE id IN "
80             + "(SELECT work_spec_id FROM workname WHERE name=:name)")
getWorkSpecIdAndStatesForName(String name)81     List<WorkSpec.IdAndState> getWorkSpecIdAndStatesForName(String name);
82 
83     /**
84      * @return All WorkSpec ids in the database.
85      */
86     @Query("SELECT id FROM workspec")
getAllWorkSpecIds()87     List<String> getAllWorkSpecIds();
88 
89     /**
90      * Updates the state of at least one {@link WorkSpec} by ID.
91      *
92      * @param state The new state
93      * @param ids The IDs for the {@link WorkSpec}s to update
94      * @return The number of rows that were updated
95      */
96     @Query("UPDATE workspec SET state=:state WHERE id IN (:ids)")
setState(State state, String... ids)97     int setState(State state, String... ids);
98 
99     /**
100      * Updates the output of a {@link WorkSpec}.
101      *
102      * @param id The {@link WorkSpec} identifier to update
103      * @param output The {@link Data} to set as the output
104      */
105     @Query("UPDATE workspec SET output=:output WHERE id=:id")
setOutput(String id, Data output)106     void setOutput(String id, Data output);
107 
108     /**
109      * Updates the period start time of a {@link WorkSpec}.
110      *
111      * @param id The {@link WorkSpec} identifier to update
112      * @param periodStartTime The time when the period started.
113      */
114     @Query("UPDATE workspec SET period_start_time=:periodStartTime WHERE id=:id")
setPeriodStartTime(String id, long periodStartTime)115     void setPeriodStartTime(String id, long periodStartTime);
116 
117     /**
118      * Increment run attempt count of a {@link WorkSpec}.
119      *
120      * @param id The identifier for the {@link WorkSpec}
121      * @return The number of rows that were updated (should be 0 or 1)
122      */
123     @Query("UPDATE workspec SET run_attempt_count=run_attempt_count+1 WHERE id=:id")
incrementWorkSpecRunAttemptCount(String id)124     int incrementWorkSpecRunAttemptCount(String id);
125 
126     /**
127      * Reset run attempt count of a {@link WorkSpec}.
128      *
129      * @param id The identifier for the {@link WorkSpec}
130      * @return The number of rows that were updated (should be 0 or 1)
131      */
132     @Query("UPDATE workspec SET run_attempt_count=0 WHERE id=:id")
resetWorkSpecRunAttemptCount(String id)133     int resetWorkSpecRunAttemptCount(String id);
134 
135     /**
136      * Retrieves the state of a {@link WorkSpec}.
137      *
138      * @param id The identifier for the {@link WorkSpec}
139      * @return The state of the {@link WorkSpec}
140      */
141     @Query("SELECT state FROM workspec WHERE id=:id")
getState(String id)142     State getState(String id);
143 
144     /**
145      * For a {@link WorkSpec} identifier, retrieves its {@link WorkSpec.WorkStatusPojo}.
146      *
147      * @param id The identifier of the {@link WorkSpec}
148      * @return A list of {@link WorkSpec.WorkStatusPojo}
149      */
150     @Transaction
151     @Query("SELECT id, state, output FROM workspec WHERE id=:id")
getWorkStatusPojoForId(String id)152     WorkSpec.WorkStatusPojo getWorkStatusPojoForId(String id);
153 
154     /**
155      * For a list of {@link WorkSpec} identifiers, retrieves a {@link List} of their
156      * {@link WorkSpec.WorkStatusPojo}.
157      *
158      * @param ids The identifier of the {@link WorkSpec}s
159      * @return A {@link List} of {@link WorkSpec.WorkStatusPojo}
160      */
161     @Transaction
162     @Query("SELECT id, state, output FROM workspec WHERE id IN (:ids)")
getWorkStatusPojoForIds(List<String> ids)163     List<WorkSpec.WorkStatusPojo> getWorkStatusPojoForIds(List<String> ids);
164 
165     /**
166      * For a list of {@link WorkSpec} identifiers, retrieves a {@link LiveData} list of their
167      * {@link WorkSpec.WorkStatusPojo}.
168      *
169      * @param ids The identifier of the {@link WorkSpec}s
170      * @return A {@link LiveData} list of {@link WorkSpec.WorkStatusPojo}
171      */
172     @Transaction
173     @Query("SELECT id, state, output FROM workspec WHERE id IN (:ids)")
getWorkStatusPojoLiveDataForIds(List<String> ids)174     LiveData<List<WorkSpec.WorkStatusPojo>> getWorkStatusPojoLiveDataForIds(List<String> ids);
175 
176     /**
177      * Retrieves a list of {@link WorkSpec.WorkStatusPojo} for all work with a given tag.
178      *
179      * @param tag The tag for the {@link WorkSpec}s
180      * @return A list of {@link WorkSpec.WorkStatusPojo}
181      */
182     @Transaction
183     @Query("SELECT id, state, output FROM workspec WHERE id IN "
184             + "(SELECT work_spec_id FROM worktag WHERE tag=:tag)")
getWorkStatusPojoForTag(String tag)185     List<WorkSpec.WorkStatusPojo> getWorkStatusPojoForTag(String tag);
186 
187     /**
188      * Retrieves a {@link LiveData} list of {@link WorkSpec.WorkStatusPojo} for all work with a
189      * given tag.
190      *
191      * @param tag The tag for the {@link WorkSpec}s
192      * @return A {@link LiveData} list of {@link WorkSpec.WorkStatusPojo}
193      */
194     @Transaction
195     @Query("SELECT id, state, output FROM workspec WHERE id IN "
196             + "(SELECT work_spec_id FROM worktag WHERE tag=:tag)")
getWorkStatusPojoLiveDataForTag(String tag)197     LiveData<List<WorkSpec.WorkStatusPojo>> getWorkStatusPojoLiveDataForTag(String tag);
198 
199     /**
200      * Retrieves a list of {@link WorkSpec.WorkStatusPojo} for all work with a given name.
201      *
202      * @param name The name of the {@link WorkSpec}s
203      * @return A list of {@link WorkSpec.WorkStatusPojo}
204      */
205     @Transaction
206     @Query("SELECT id, state, output FROM workspec WHERE id IN "
207             + "(SELECT work_spec_id FROM workname WHERE name=:name)")
getWorkStatusPojoForName(String name)208     List<WorkSpec.WorkStatusPojo> getWorkStatusPojoForName(String name);
209 
210     /**
211      * Retrieves a {@link LiveData} list of {@link WorkSpec.WorkStatusPojo} for all work with a
212      * given name.
213      *
214      * @param name The name for the {@link WorkSpec}s
215      * @return A {@link LiveData} list of {@link WorkSpec.WorkStatusPojo}
216      */
217     @Transaction
218     @Query("SELECT id, state, output FROM workspec WHERE id IN "
219             + "(SELECT work_spec_id FROM workname WHERE name=:name)")
getWorkStatusPojoLiveDataForName(String name)220     LiveData<List<WorkSpec.WorkStatusPojo>> getWorkStatusPojoLiveDataForName(String name);
221 
222     /**
223      * Gets all inputs coming from prerequisites for a particular {@link WorkSpec}.  These are
224      * {@link Data} set via {@code Worker#setOutputData()}.
225      *
226      * @param id The {@link WorkSpec} identifier
227      * @return A list of all inputs coming from prerequisites for {@code id}
228      */
229     @Query("SELECT output FROM workspec WHERE id IN "
230             + "(SELECT prerequisite_id FROM dependency WHERE work_spec_id=:id)")
getInputsFromPrerequisites(String id)231     List<Data> getInputsFromPrerequisites(String id);
232 
233     /**
234      * Retrieves work ids for unfinished work with a given tag.
235      *
236      * @param tag The tag used to identify the work
237      * @return A list of work ids
238      */
239     @Query("SELECT id FROM workspec WHERE state NOT IN " + COMPLETED_STATES
240             + " AND id IN (SELECT work_spec_id FROM worktag WHERE tag=:tag)")
getUnfinishedWorkWithTag(@onNull String tag)241     List<String> getUnfinishedWorkWithTag(@NonNull String tag);
242 
243     /**
244      * Retrieves work ids for unfinished work with a given name.
245      *
246      * @param name THe tag used to identify the work
247      * @return A list of work ids
248      */
249     @Query("SELECT id FROM workspec WHERE state NOT IN " + COMPLETED_STATES
250             + " AND id IN (SELECT work_spec_id FROM workname WHERE name=:name)")
getUnfinishedWorkWithName(@onNull String name)251     List<String> getUnfinishedWorkWithName(@NonNull String name);
252 
253     /**
254      * Retrieves work ids for all unfinished work.
255      *
256      * @return A list of work ids
257      */
258     @Query("SELECT id FROM workspec WHERE state NOT IN " + COMPLETED_STATES)
getAllUnfinishedWork()259     List<String> getAllUnfinishedWork();
260 
261     /**
262      * Marks a {@link WorkSpec} as scheduled.
263      *
264      * @param id        The identifier for the {@link WorkSpec}
265      * @param startTime The time at which the {@link WorkSpec} was scheduled.
266      * @return The number of rows that were updated (should be 0 or 1)
267      */
268     @Query("UPDATE workspec SET schedule_requested_at=:startTime WHERE id=:id")
markWorkSpecScheduled(@onNull String id, long startTime)269     int markWorkSpecScheduled(@NonNull String id, long startTime);
270 
271     /**
272      * Resets the scheduled state on the {@link WorkSpec}s that are not in a a completed state.
273      * @return The number of rows that were updated
274      */
275     @Query("UPDATE workspec SET schedule_requested_at=" + WorkSpec.SCHEDULE_NOT_REQUESTED_YET
276             + " WHERE state NOT IN " + COMPLETED_STATES)
resetScheduledState()277     int resetScheduledState();
278 
279     /**
280      * @return The List of {@link WorkSpec}s that are eligible to be scheduled.
281      */
282     @Query("SELECT * from workspec WHERE "
283             + "state=" + WorkTypeConverters.StateIds.ENQUEUED
284             // We only want WorkSpecs which have not been previously scheduled.
285             + " AND schedule_requested_at=" + WorkSpec.SCHEDULE_NOT_REQUESTED_YET
286             + " LIMIT "
287                 + "(SELECT :schedulerLimit" + "-COUNT(*) FROM workspec WHERE"
288                     + " schedule_requested_at<>" + WorkSpec.SCHEDULE_NOT_REQUESTED_YET
289                     + " AND state NOT IN " + COMPLETED_STATES
290                 + ")"
291     )
getEligibleWorkForScheduling(int schedulerLimit)292     List<WorkSpec> getEligibleWorkForScheduling(int schedulerLimit);
293 
294     /**
295      * Immediately prunes eligible work from the database meeting the following criteria:
296      * - Is finished (succeeded, failed, or cancelled)
297      * - Has zero unfinished dependents
298      */
299     @Query("DELETE FROM workspec WHERE "
300             + "state IN " + COMPLETED_STATES
301             + " AND (SELECT COUNT(*)=0 FROM dependency WHERE "
302             + "    prerequisite_id=id AND "
303             + "    work_spec_id NOT IN "
304             + "        (SELECT id FROM workspec WHERE state IN " + COMPLETED_STATES + "))")
pruneFinishedWorkWithZeroDependentsIgnoringKeepForAtLeast()305     void pruneFinishedWorkWithZeroDependentsIgnoringKeepForAtLeast();
306 }
307