]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql_find.c
- Fix how FileSet is saved in job record to correct continual
[bacula/bacula] / bacula / src / cats / sql_find.c
1 /*
2  * Bacula Catalog Database Find record interface routines
3  *
4  *  Note, generally, these routines are more complicated
5  *        that a simple search by name or id. Such simple
6  *        request are in get.c
7  *
8  *    Kern Sibbald, December 2000
9  *
10  *    Version $Id$
11  */
12 /*
13    Copyright (C) 2000-2005 Kern Sibbald
14
15    This program is free software; you can redistribute it and/or
16    modify it under the terms of the GNU General Public License
17    version 2 as amended with additional clauses defined in the
18    file LICENSE in the main source directory.
19
20    This program is distributed in the hope that it will be useful,
21    but WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
23    the file LICENSE for additional details.
24
25  */
26
27
28
29 /* The following is necessary so that we do not include
30  * the dummy external definition of DB.
31  */
32 #define __SQL_C                       /* indicate that this is sql.c */
33
34 #include "bacula.h"
35 #include "cats.h"
36
37 #if    HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL
38
39 /* -----------------------------------------------------------------------
40  *
41  *   Generic Routines (or almost generic)
42  *
43  * -----------------------------------------------------------------------
44  */
45
46 /* Imported subroutines */
47 extern void print_result(B_DB *mdb);
48 extern int QueryDB(const char *file, int line, JCR *jcr, B_DB *db, char *select_cmd);
49
50 /*
51  * Find job start time if JobId specified, otherwise
52  * find last full save for Incremental and Differential saves.
53  *
54  *  StartTime is returned in stime
55  *
56  * Returns: 0 on failure
57  *          1 on success, jr is unchanged, but stime is set
58  */
59 bool
60 db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime)
61 {
62    SQL_ROW row;
63    char ed1[50], ed2[50];
64
65    db_lock(mdb);
66
67    pm_strcpy(stime, "0000-00-00 00:00:00");   /* default */
68    /* If no Id given, we must find corresponding job */
69    if (jr->JobId == 0) {
70       /* Differential is since last Full backup */
71          Mmsg(mdb->cmd,
72 "SELECT StartTime FROM Job WHERE JobStatus='T' AND Type='%c' AND "
73 "Level='%c' AND Name='%s' AND ClientId=%s AND FileSetId=%s "
74 "ORDER BY StartTime DESC LIMIT 1",
75            jr->JobType, L_FULL, jr->Name, 
76            edit_int64(jr->ClientId, ed1), edit_int64(jr->FileSetId, ed2));
77
78       if (jr->JobLevel == L_DIFFERENTIAL) {
79          /* SQL cmd for Differential backup already edited above */
80
81       /* Incremental is since last Full, Incremental, or Differential */
82       } else if (jr->JobLevel == L_INCREMENTAL) {
83          /*
84           * For an Incremental job, we must first ensure
85           *  that a Full backup was done (cmd edited above)
86           *  then we do a second look to find the most recent
87           *  backup
88           */
89          if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
90             Mmsg2(&mdb->errmsg, _("Query error for start time request: ERR=%s\nCMD=%s\n"),
91                sql_strerror(mdb), mdb->cmd);
92             goto bail_out;
93          }
94          if ((row = sql_fetch_row(mdb)) == NULL) {
95             sql_free_result(mdb);
96             Mmsg(mdb->errmsg, _("No prior Full backup Job record found.\n"));
97             goto bail_out;
98          }
99          sql_free_result(mdb);
100          /* Now edit SQL command for Incremental Job */
101          Mmsg(mdb->cmd,
102 "SELECT StartTime FROM Job WHERE JobStatus='T' AND Type='%c' AND "
103 "Level IN ('%c','%c','%c') AND Name='%s' AND ClientId=%s "
104 "AND FileSetId=%s ORDER BY StartTime DESC LIMIT 1",
105             jr->JobType, L_INCREMENTAL, L_DIFFERENTIAL, L_FULL, jr->Name,
106             edit_int64(jr->ClientId, ed1), edit_int64(jr->FileSetId, ed2));
107       } else {
108          Mmsg1(&mdb->errmsg, _("Unknown level=%d\n"), jr->JobLevel);
109          goto bail_out;
110       }
111    } else {
112       Dmsg1(100, "Submitting: %s\n", mdb->cmd);
113       Mmsg(mdb->cmd, "SELECT StartTime FROM Job WHERE Job.JobId=%s", 
114            edit_int64(jr->JobId, ed1));
115    }
116
117    if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
118       pm_strcpy(stime, "");                   /* set EOS */
119       Mmsg2(&mdb->errmsg, _("Query error for start time request: ERR=%s\nCMD=%s\n"),
120          sql_strerror(mdb),  mdb->cmd);
121       goto bail_out;
122    }
123
124    if ((row = sql_fetch_row(mdb)) == NULL) {
125       Mmsg2(&mdb->errmsg, _("No Job record found: ERR=%s\nCMD=%s\n"),
126          sql_strerror(mdb),  mdb->cmd);
127       sql_free_result(mdb);
128       goto bail_out;
129    }
130    Dmsg1(100, "Got start time: %s\n", row[0]);
131    pm_strcpy(stime, row[0]);
132
133    sql_free_result(mdb);
134
135    db_unlock(mdb);
136    return true;
137
138 bail_out:
139    db_unlock(mdb);
140    return false;
141 }
142
143 /*
144  * Find last failed job since given start-time
145  *   it must be either Full or Diff.
146  *
147  * Returns: false on failure
148  *          true  on success, jr is unchanged and stime unchanged
149  *                level returned in JobLevel
150  */
151 bool
152 db_find_failed_job_since(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM *stime, int &JobLevel)
153 {
154    SQL_ROW row;
155    char ed1[50], ed2[50];
156
157    db_lock(mdb);
158    /* Differential is since last Full backup */
159    Mmsg(mdb->cmd,
160 "SELECT Level FROM Job WHERE JobStatus!='T' AND Type='%c' AND "
161 "Level IN ('%c','%c') AND Name='%s' AND ClientId=%s "
162 "AND FileSetId=%s AND StartTime>'%s' "
163 "ORDER BY StartTime DESC LIMIT 1",
164          jr->JobType, L_FULL, L_DIFFERENTIAL, jr->Name,
165          edit_int64(jr->ClientId, ed1), edit_int64(jr->FileSetId, ed2),
166          stime);
167
168    if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
169       db_unlock(mdb);
170       return false;
171    }
172
173    if ((row = sql_fetch_row(mdb)) == NULL) {
174       sql_free_result(mdb);
175       db_unlock(mdb);
176       return false;
177    }
178    JobLevel = (int)*row[0];
179    sql_free_result(mdb);
180
181    db_unlock(mdb);
182    return true;
183 }
184
185
186 /*
187  * Find JobId of last job that ran.  E.g. for
188  *   VERIFY_CATALOG we want the JobId of the last INIT.
189  *   For VERIFY_VOLUME_TO_CATALOG, we want the JobId of the last Job.
190  *
191  * Returns: 1 on success
192  *          0 on failure
193  */
194 int
195 db_find_last_jobid(JCR *jcr, B_DB *mdb, const char *Name, JOB_DBR *jr)
196 {
197    SQL_ROW row;
198    char ed1[50];
199
200    /* Find last full */
201    db_lock(mdb);
202    if (jr->JobLevel == L_VERIFY_CATALOG) {
203       Mmsg(mdb->cmd,
204 "SELECT JobId FROM Job WHERE Type='V' AND Level='%c' AND "
205 " JobStatus='T' AND Name='%s' AND "
206 "ClientId=%s ORDER BY StartTime DESC LIMIT 1",
207            L_VERIFY_INIT, jr->Name, 
208            edit_int64(jr->ClientId, ed1));
209    } else if (jr->JobLevel == L_VERIFY_VOLUME_TO_CATALOG ||
210               jr->JobLevel == L_VERIFY_DISK_TO_CATALOG) {
211       if (Name) {
212          Mmsg(mdb->cmd,
213 "SELECT JobId FROM Job WHERE Type='B' AND JobStatus='T' AND "
214 "Name='%s' ORDER BY StartTime DESC LIMIT 1", Name);
215       } else {
216          Mmsg(mdb->cmd,
217 "SELECT JobId FROM Job WHERE Type='B' AND JobStatus='T' AND "
218 "ClientId=%s ORDER BY StartTime DESC LIMIT 1", 
219            edit_int64(jr->ClientId, ed1));
220       }
221    } else {
222       Mmsg1(&mdb->errmsg, _("Unknown Job level=%c\n"), jr->JobLevel);
223       db_unlock(mdb);
224       return 0;
225    }
226    Dmsg1(100, "Query: %s\n", mdb->cmd);
227    if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
228       db_unlock(mdb);
229       return 0;
230    }
231    if ((row = sql_fetch_row(mdb)) == NULL) {
232       Mmsg1(&mdb->errmsg, _("No Job found for: %s.\n"), mdb->cmd);
233       sql_free_result(mdb);
234       db_unlock(mdb);
235       return 0;
236    }
237
238    jr->JobId = str_to_int64(row[0]);
239    sql_free_result(mdb);
240
241    Dmsg1(100, "db_get_last_jobid: got JobId=%d\n", jr->JobId);
242    if (jr->JobId <= 0) {
243       Mmsg1(&mdb->errmsg, _("No Job found for: %s\n"), mdb->cmd);
244       db_unlock(mdb);
245       return 0;
246    }
247
248    db_unlock(mdb);
249    return 1;
250 }
251
252 /*
253  * Find Available Media (Volume) for Pool
254  *
255  * Find a Volume for a given PoolId, MediaType, and Status.
256  *
257  * Returns: 0 on failure
258  *          numrows on success
259  */
260 int
261 db_find_next_volume(JCR *jcr, B_DB *mdb, int item, bool InChanger, MEDIA_DBR *mr)
262 {
263    SQL_ROW row;
264    int numrows;
265    const char *changer, *order;
266    char ed1[50];
267
268    db_lock(mdb);
269    if (item == -1) {       /* find oldest volume */
270       /* Find oldest volume */
271       Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
272           "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
273           "VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,Recycle,Slot,"
274           "FirstWritten,LastWritten,VolStatus,InChanger,VolParts,"
275           "LabelType "
276           "FROM Media WHERE PoolId=%s AND MediaType='%s' AND VolStatus IN ('Full',"
277           "'Recycle','Purged','Used','Append') "
278           "ORDER BY LastWritten LIMIT 1", 
279           edit_int64(mr->PoolId, ed1), mr->MediaType);
280      item = 1;
281    } else {
282       /* Find next available volume */
283       if (InChanger) {
284          changer = "AND InChanger=1";
285       } else {
286          changer = "";
287       }
288       if (strcmp(mr->VolStatus, "Recycled") == 0 ||
289           strcmp(mr->VolStatus, "Purged") == 0) {
290          order = "ORDER BY LastWritten ASC,MediaId";  /* take oldest */
291       } else {
292          order = "ORDER BY LastWritten IS NULL,LastWritten DESC,MediaId";   /* take most recently written */
293       }
294       Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
295           "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
296           "VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,Recycle,Slot,"
297           "FirstWritten,LastWritten,VolStatus,InChanger,VolParts,"
298           "LabelType "
299           "FROM Media WHERE PoolId=%s AND MediaType='%s' AND VolStatus='%s' "
300           "%s "
301           "%s LIMIT %d",
302           edit_int64(mr->PoolId, ed1), mr->MediaType,
303           mr->VolStatus, changer, order, item);
304    }
305    if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
306       db_unlock(mdb);
307       return 0;
308    }
309
310    numrows = sql_num_rows(mdb);
311    if (item > numrows) {
312       Mmsg2(&mdb->errmsg, _("Request for Volume item %d greater than max %d\n"),
313          item, numrows);
314       db_unlock(mdb);
315       return 0;
316    }
317
318    /* Seek to desired item
319     * Note, we use base 1; SQL uses base 0
320     */
321    sql_data_seek(mdb, item-1);
322
323    if ((row = sql_fetch_row(mdb)) == NULL) {
324       Mmsg1(&mdb->errmsg, _("No Volume record found for item %d.\n"), item);
325       sql_free_result(mdb);
326       db_unlock(mdb);
327       return 0;
328    }
329
330    /* Return fields in Media Record */
331    mr->MediaId = str_to_int64(row[0]);
332    bstrncpy(mr->VolumeName, row[1], sizeof(mr->VolumeName));
333    mr->VolJobs = str_to_int64(row[2]);
334    mr->VolFiles = str_to_int64(row[3]);
335    mr->VolBlocks = str_to_int64(row[4]);
336    mr->VolBytes = str_to_uint64(row[5]);
337    mr->VolMounts = str_to_int64(row[6]);
338    mr->VolErrors = str_to_int64(row[7]);
339    mr->VolWrites = str_to_int64(row[8]);
340    mr->MaxVolBytes = str_to_uint64(row[9]);
341    mr->VolCapacityBytes = str_to_uint64(row[10]);
342    mr->VolRetention = str_to_uint64(row[11]);
343    mr->VolUseDuration = str_to_uint64(row[12]);
344    mr->MaxVolJobs = str_to_int64(row[13]);
345    mr->MaxVolFiles = str_to_int64(row[14]);
346    mr->Recycle = str_to_int64(row[15]);
347    mr->Slot = str_to_int64(row[16]);
348    bstrncpy(mr->cFirstWritten, row[17]!=NULL?row[17]:"", sizeof(mr->cFirstWritten));
349    mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten);
350    bstrncpy(mr->cLastWritten, row[18]!=NULL?row[18]:"", sizeof(mr->cLastWritten));
351    mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
352    bstrncpy(mr->VolStatus, row[19], sizeof(mr->VolStatus));
353    mr->InChanger = str_to_int64(row[20]);
354    mr->VolParts = str_to_int64(row[21]);
355    mr->LabelType = str_to_int64(row[22]);
356    sql_free_result(mdb);
357
358    db_unlock(mdb);
359    return numrows;
360 }
361
362
363 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/