]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql_find.c
- Implement first cut of Migration.
[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: true  on success
192  *          false on failure
193  */
194 bool
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               jr->JobType == JT_MIGRATE) {
212       if (Name) {
213          Mmsg(mdb->cmd,
214 "SELECT JobId FROM Job WHERE Type='B' AND JobStatus='T' AND "
215 "Name='%s' ORDER BY StartTime DESC LIMIT 1", Name);
216       } else {
217          Mmsg(mdb->cmd,
218 "SELECT JobId FROM Job WHERE Type='B' AND JobStatus='T' AND "
219 "ClientId=%s ORDER BY StartTime DESC LIMIT 1", 
220            edit_int64(jr->ClientId, ed1));
221       }
222    } else {
223       Mmsg1(&mdb->errmsg, _("Unknown Job level=%c\n"), jr->JobLevel);
224       db_unlock(mdb);
225       return false;
226    }
227    Dmsg1(100, "Query: %s\n", mdb->cmd);
228    if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
229       db_unlock(mdb);
230       return false;
231    }
232    if ((row = sql_fetch_row(mdb)) == NULL) {
233       Mmsg1(&mdb->errmsg, _("No Job found for: %s.\n"), mdb->cmd);
234       sql_free_result(mdb);
235       db_unlock(mdb);
236       return false;
237    }
238
239    jr->JobId = str_to_int64(row[0]);
240    sql_free_result(mdb);
241
242    Dmsg1(100, "db_get_last_jobid: got JobId=%d\n", jr->JobId);
243    if (jr->JobId <= 0) {
244       Mmsg1(&mdb->errmsg, _("No Job found for: %s\n"), mdb->cmd);
245       db_unlock(mdb);
246       return false;
247    }
248
249    db_unlock(mdb);
250    return true;
251 }
252
253 /*
254  * Find Available Media (Volume) for Pool
255  *
256  * Find a Volume for a given PoolId, MediaType, and Status.
257  *
258  * Returns: 0 on failure
259  *          numrows on success
260  */
261 int
262 db_find_next_volume(JCR *jcr, B_DB *mdb, int item, bool InChanger, MEDIA_DBR *mr)
263 {
264    SQL_ROW row;
265    int numrows;
266    const char *order;
267
268    char ed1[50];
269
270    db_lock(mdb);
271    if (item == -1) {       /* find oldest volume */
272       /* Find oldest volume */
273       Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
274           "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
275           "VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,Recycle,Slot,"
276           "FirstWritten,LastWritten,VolStatus,InChanger,VolParts,"
277           "LabelType "
278           "FROM Media WHERE PoolId=%s AND MediaType='%s' AND VolStatus IN ('Full',"
279           "'Recycle','Purged','Used','Append') "
280           "ORDER BY LastWritten LIMIT 1", 
281           edit_int64(mr->PoolId, ed1), mr->MediaType);
282      item = 1;
283    } else {
284       char changer[100];
285       /* Find next available volume */
286       if (InChanger) {
287          bsnprintf(changer, sizeof(changer), "AND InChanger=1 AND StorageId=%s",
288             edit_int64(mr->StorageId, ed1));
289       } else {
290          changer[0] = 0;
291       }
292       if (strcmp(mr->VolStatus, "Recycled") == 0 ||
293           strcmp(mr->VolStatus, "Purged") == 0) {
294          order = "ORDER BY LastWritten ASC,MediaId";  /* take oldest */
295       } else {
296          order = "ORDER BY LastWritten IS NULL,LastWritten DESC,MediaId";   /* take most recently written */
297       }
298       Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
299           "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
300           "VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,Recycle,Slot,"
301           "FirstWritten,LastWritten,VolStatus,InChanger,VolParts,"
302           "LabelType "
303           "FROM Media WHERE PoolId=%s AND MediaType='%s' AND VolStatus='%s' "
304           "%s "
305           "%s LIMIT %d",
306           edit_int64(mr->PoolId, ed1), mr->MediaType,
307           mr->VolStatus, changer, order, item);
308    }
309    if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
310       db_unlock(mdb);
311       return 0;
312    }
313
314    numrows = sql_num_rows(mdb);
315    if (item > numrows) {
316       Mmsg2(&mdb->errmsg, _("Request for Volume item %d greater than max %d\n"),
317          item, numrows);
318       db_unlock(mdb);
319       return 0;
320    }
321
322    /* Seek to desired item
323     * Note, we use base 1; SQL uses base 0
324     */
325    sql_data_seek(mdb, item-1);
326
327    if ((row = sql_fetch_row(mdb)) == NULL) {
328       Mmsg1(&mdb->errmsg, _("No Volume record found for item %d.\n"), item);
329       sql_free_result(mdb);
330       db_unlock(mdb);
331       return 0;
332    }
333
334    /* Return fields in Media Record */
335    mr->MediaId = str_to_int64(row[0]);
336    bstrncpy(mr->VolumeName, row[1], sizeof(mr->VolumeName));
337    mr->VolJobs = str_to_int64(row[2]);
338    mr->VolFiles = str_to_int64(row[3]);
339    mr->VolBlocks = str_to_int64(row[4]);
340    mr->VolBytes = str_to_uint64(row[5]);
341    mr->VolMounts = str_to_int64(row[6]);
342    mr->VolErrors = str_to_int64(row[7]);
343    mr->VolWrites = str_to_int64(row[8]);
344    mr->MaxVolBytes = str_to_uint64(row[9]);
345    mr->VolCapacityBytes = str_to_uint64(row[10]);
346    mr->VolRetention = str_to_uint64(row[11]);
347    mr->VolUseDuration = str_to_uint64(row[12]);
348    mr->MaxVolJobs = str_to_int64(row[13]);
349    mr->MaxVolFiles = str_to_int64(row[14]);
350    mr->Recycle = str_to_int64(row[15]);
351    mr->Slot = str_to_int64(row[16]);
352    bstrncpy(mr->cFirstWritten, row[17]!=NULL?row[17]:"", sizeof(mr->cFirstWritten));
353    mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten);
354    bstrncpy(mr->cLastWritten, row[18]!=NULL?row[18]:"", sizeof(mr->cLastWritten));
355    mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
356    bstrncpy(mr->VolStatus, row[19], sizeof(mr->VolStatus));
357    mr->InChanger = str_to_int64(row[20]);
358    mr->VolParts = str_to_int64(row[21]);
359    mr->LabelType = str_to_int64(row[22]);
360    sql_free_result(mdb);
361
362    db_unlock(mdb);
363    return numrows;
364 }
365
366
367 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/