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
17package androidx.work.impl.model;
18
19import static android.arch.persistence.room.OnConflictStrategy.IGNORE;
20
21import static androidx.work.impl.model.WorkTypeConverters.StateIds.COMPLETED_STATES;
22
23import android.arch.lifecycle.LiveData;
24import android.arch.persistence.room.Dao;
25import android.arch.persistence.room.Insert;
26import android.arch.persistence.room.Query;
27import android.arch.persistence.room.Transaction;
28import android.support.annotation.NonNull;
29
30import androidx.work.Data;
31import androidx.work.State;
32
33import java.util.List;
34
35/**
36 * The Data Access Object for {@link WorkSpec}s.
37 */
38@Dao
39public 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)
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")
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")
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)")
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)")
81    List<WorkSpec.IdAndState> getWorkSpecIdAndStatesForName(String name);
82
83    /**
84     * @return All WorkSpec ids in the database.
85     */
86    @Query("SELECT id FROM workspec")
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)")
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")
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")
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")
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")
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")
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")
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)")
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)")
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)")
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)")
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)")
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)")
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)")
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)")
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)")
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)
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")
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)
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    )
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 + "))")
305    void pruneFinishedWorkWithZeroDependentsIgnoringKeepForAtLeast();
306}
307