2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Bacula Catalog Database Find record interface routines
31 * Note, generally, these routines are more complicated
32 * that a simple search by name or id. Such simple
33 * request are in get.c
35 * Kern Sibbald, December 2000
41 /* The following is necessary so that we do not include
42 * the dummy external definition of DB.
44 #define __SQL_C /* indicate that this is sql.c */
49 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI
51 /* -----------------------------------------------------------------------
53 * Generic Routines (or almost generic)
55 * -----------------------------------------------------------------------
59 * Find job start time if JobId specified, otherwise
60 * find last Job start time Incremental and Differential saves.
62 * StartTime is returned in stime
64 * Returns: 0 on failure
65 * 1 on success, jr is unchanged, but stime is set
68 db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime)
71 char ed1[50], ed2[50];
75 pm_strcpy(stime, "0000-00-00 00:00:00"); /* default */
76 /* If no Id given, we must find corresponding job */
78 /* Differential is since last Full backup */
80 "SELECT StartTime FROM Job WHERE JobStatus IN ('T','W') AND Type='%c' AND "
81 "Level='%c' AND Name='%s' AND ClientId=%s AND FileSetId=%s "
82 "ORDER BY StartTime DESC LIMIT 1",
83 jr->JobType, L_FULL, jr->Name,
84 edit_int64(jr->ClientId, ed1), edit_int64(jr->FileSetId, ed2));
86 if (jr->JobLevel == L_DIFFERENTIAL) {
87 /* SQL cmd for Differential backup already edited above */
89 /* Incremental is since last Full, Incremental, or Differential */
90 } else if (jr->JobLevel == L_INCREMENTAL) {
92 * For an Incremental job, we must first ensure
93 * that a Full backup was done (cmd edited above)
94 * then we do a second look to find the most recent
97 if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
98 Mmsg2(&mdb->errmsg, _("Query error for start time request: ERR=%s\nCMD=%s\n"),
99 sql_strerror(mdb), mdb->cmd);
102 if ((row = sql_fetch_row(mdb)) == NULL) {
103 sql_free_result(mdb);
104 Mmsg(mdb->errmsg, _("No prior Full backup Job record found.\n"));
107 sql_free_result(mdb);
108 /* Now edit SQL command for Incremental Job */
110 "SELECT StartTime FROM Job WHERE JobStatus IN ('T','W') AND Type='%c' AND "
111 "Level IN ('%c','%c','%c') AND Name='%s' AND ClientId=%s "
112 "AND FileSetId=%s ORDER BY StartTime DESC LIMIT 1",
113 jr->JobType, L_INCREMENTAL, L_DIFFERENTIAL, L_FULL, jr->Name,
114 edit_int64(jr->ClientId, ed1), edit_int64(jr->FileSetId, ed2));
116 Mmsg1(mdb->errmsg, _("Unknown level=%d\n"), jr->JobLevel);
120 Dmsg1(100, "Submitting: %s\n", mdb->cmd);
121 Mmsg(mdb->cmd, "SELECT StartTime FROM Job WHERE Job.JobId=%s",
122 edit_int64(jr->JobId, ed1));
125 if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
126 pm_strcpy(stime, ""); /* set EOS */
127 Mmsg2(&mdb->errmsg, _("Query error for start time request: ERR=%s\nCMD=%s\n"),
128 sql_strerror(mdb), mdb->cmd);
132 if ((row = sql_fetch_row(mdb)) == NULL) {
133 Mmsg2(&mdb->errmsg, _("No Job record found: ERR=%s\nCMD=%s\n"),
134 sql_strerror(mdb), mdb->cmd);
135 sql_free_result(mdb);
138 Dmsg1(100, "Got start time: %s\n", row[0]);
139 pm_strcpy(stime, row[0]);
141 sql_free_result(mdb);
153 * Find the last job start time for the specified JobLevel
155 * StartTime is returned in stime
157 * Returns: false on failure
158 * true on success, jr is unchanged, but stime is set
161 db_find_last_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime, int JobLevel)
164 char ed1[50], ed2[50];
168 pm_strcpy(stime, "0000-00-00 00:00:00"); /* default */
171 "SELECT StartTime FROM Job WHERE JobStatus IN ('T','W') AND Type='%c' AND "
172 "Level='%c' AND Name='%s' AND ClientId=%s AND FileSetId=%s "
173 "ORDER BY StartTime DESC LIMIT 1",
174 jr->JobType, JobLevel, jr->Name,
175 edit_int64(jr->ClientId, ed1), edit_int64(jr->FileSetId, ed2));
176 if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
177 Mmsg2(&mdb->errmsg, _("Query error for start time request: ERR=%s\nCMD=%s\n"),
178 sql_strerror(mdb), mdb->cmd);
181 if ((row = sql_fetch_row(mdb)) == NULL) {
182 sql_free_result(mdb);
183 Mmsg(mdb->errmsg, _("No prior Full backup Job record found.\n"));
186 Dmsg1(100, "Got start time: %s\n", row[0]);
187 pm_strcpy(stime, row[0]);
188 sql_free_result(mdb);
198 * Find last failed job since given start-time
199 * it must be either Full or Diff.
201 * Returns: false on failure
202 * true on success, jr is unchanged and stime unchanged
203 * level returned in JobLevel
206 db_find_failed_job_since(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM *stime, int &JobLevel)
209 char ed1[50], ed2[50];
212 /* Differential is since last Full backup */
214 "SELECT Level FROM Job WHERE JobStatus!='T' AND Type='%c' AND "
215 "Level IN ('%c','%c') AND Name='%s' AND ClientId=%s "
216 "AND FileSetId=%s AND StartTime>'%s' "
217 "ORDER BY StartTime DESC LIMIT 1",
218 jr->JobType, L_FULL, L_DIFFERENTIAL, jr->Name,
219 edit_int64(jr->ClientId, ed1), edit_int64(jr->FileSetId, ed2),
222 if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
227 if ((row = sql_fetch_row(mdb)) == NULL) {
228 sql_free_result(mdb);
232 JobLevel = (int)*row[0];
233 sql_free_result(mdb);
241 * Find JobId of last job that ran. E.g. for
242 * VERIFY_CATALOG we want the JobId of the last INIT.
243 * For VERIFY_VOLUME_TO_CATALOG, we want the JobId of the last Job.
245 * Returns: true on success
249 db_find_last_jobid(JCR *jcr, B_DB *mdb, const char *Name, JOB_DBR *jr)
256 Dmsg2(100, "JobLevel=%d JobType=%d\n", jr->JobLevel, jr->JobType);
257 if (jr->JobLevel == L_VERIFY_CATALOG) {
259 "SELECT JobId FROM Job WHERE Type='V' AND Level='%c' AND "
260 " JobStatus IN ('T','W') AND Name='%s' AND "
261 "ClientId=%s ORDER BY StartTime DESC LIMIT 1",
262 L_VERIFY_INIT, jr->Name,
263 edit_int64(jr->ClientId, ed1));
264 } else if (jr->JobLevel == L_VERIFY_VOLUME_TO_CATALOG ||
265 jr->JobLevel == L_VERIFY_DISK_TO_CATALOG ||
266 jr->JobType == JT_BACKUP) {
269 "SELECT JobId FROM Job WHERE Type='B' AND JobStatus IN ('T','W') AND "
270 "Name='%s' ORDER BY StartTime DESC LIMIT 1", Name);
273 "SELECT JobId FROM Job WHERE Type='B' AND JobStatus IN ('T','W') AND "
274 "ClientId=%s ORDER BY StartTime DESC LIMIT 1",
275 edit_int64(jr->ClientId, ed1));
278 Mmsg1(&mdb->errmsg, _("Unknown Job level=%d\n"), jr->JobLevel);
282 Dmsg1(100, "Query: %s\n", mdb->cmd);
283 if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
287 if ((row = sql_fetch_row(mdb)) == NULL) {
288 Mmsg1(&mdb->errmsg, _("No Job found for: %s.\n"), mdb->cmd);
289 sql_free_result(mdb);
294 jr->JobId = str_to_int64(row[0]);
295 sql_free_result(mdb);
297 Dmsg1(100, "db_get_last_jobid: got JobId=%d\n", jr->JobId);
298 if (jr->JobId <= 0) {
299 Mmsg1(&mdb->errmsg, _("No Job found for: %s\n"), mdb->cmd);
309 * Find Available Media (Volume) for Pool
311 * Find a Volume for a given PoolId, MediaType, and Status.
313 * Returns: 0 on failure
317 db_find_next_volume(JCR *jcr, B_DB *mdb, int item, bool InChanger, MEDIA_DBR *mr)
326 if (item == -1) { /* find oldest volume */
327 /* Find oldest volume */
328 Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
329 "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
330 "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
331 "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
332 "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
333 "Enabled,LocationId,RecycleCount,InitialWrite,"
334 "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime "
335 "FROM Media WHERE PoolId=%s AND MediaType='%s' AND VolStatus IN ('Full',"
336 "'Recycle','Purged','Used','Append') AND Enabled=1 "
337 "ORDER BY LastWritten LIMIT 1",
338 edit_int64(mr->PoolId, ed1), mr->MediaType);
341 POOL_MEM changer(PM_FNAME);
342 /* Find next available volume */
344 Mmsg(changer, "AND InChanger=1 AND StorageId=%s",
345 edit_int64(mr->StorageId, ed1));
347 if (strcmp(mr->VolStatus, "Recycle") == 0 ||
348 strcmp(mr->VolStatus, "Purged") == 0) {
349 order = "AND Recycle=1 ORDER BY LastWritten ASC,MediaId"; /* take oldest that can be recycled */
351 order = "ORDER BY LastWritten IS NULL,LastWritten DESC,MediaId"; /* take most recently written */
353 Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
354 "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
355 "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
356 "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
357 "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
358 "Enabled,LocationId,RecycleCount,InitialWrite,"
359 "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime "
360 "FROM Media WHERE PoolId=%s AND MediaType='%s' AND Enabled=1 "
361 "AND VolStatus='%s' "
364 edit_int64(mr->PoolId, ed1), mr->MediaType,
365 mr->VolStatus, changer.c_str(), order, item);
367 Dmsg1(050, "fnextvol=%s\n", mdb->cmd);
368 if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
373 numrows = sql_num_rows(mdb);
374 if (item > numrows || item < 1) {
375 Dmsg2(050, "item=%d got=%d\n", item, numrows);
376 Mmsg2(&mdb->errmsg, _("Request for Volume item %d greater than max %d or less than 1\n"),
382 /* Note, we previously seeked to the row using:
383 * sql_data_seek(mdb, item-1);
384 * but this failed on PostgreSQL, so now we loop
385 * over all the records. This should not be too horrible since
386 * the maximum Volumes we look at in any case is 20.
389 if ((row = sql_fetch_row(mdb)) == NULL) {
390 Dmsg1(050, "Fail fetch item=%d\n", item+1);
391 Mmsg1(&mdb->errmsg, _("No Volume record found for item %d.\n"), item);
392 sql_free_result(mdb);
398 /* Return fields in Media Record */
399 mr->MediaId = str_to_int64(row[0]);
400 bstrncpy(mr->VolumeName, row[1]!=NULL?row[1]:"", sizeof(mr->VolumeName));
401 mr->VolJobs = str_to_int64(row[2]);
402 mr->VolFiles = str_to_int64(row[3]);
403 mr->VolBlocks = str_to_int64(row[4]);
404 mr->VolBytes = str_to_uint64(row[5]);
405 mr->VolMounts = str_to_int64(row[6]);
406 mr->VolErrors = str_to_int64(row[7]);
407 mr->VolWrites = str_to_int64(row[8]);
408 mr->MaxVolBytes = str_to_uint64(row[9]);
409 mr->VolCapacityBytes = str_to_uint64(row[10]);
410 bstrncpy(mr->MediaType, row[11]!=NULL?row[11]:"", sizeof(mr->MediaType));
411 bstrncpy(mr->VolStatus, row[12]!=NULL?row[12]:"", sizeof(mr->VolStatus));
412 mr->PoolId = str_to_int64(row[13]);
413 mr->VolRetention = str_to_uint64(row[14]);
414 mr->VolUseDuration = str_to_uint64(row[15]);
415 mr->MaxVolJobs = str_to_int64(row[16]);
416 mr->MaxVolFiles = str_to_int64(row[17]);
417 mr->Recycle = str_to_int64(row[18]);
418 mr->Slot = str_to_int64(row[19]);
419 bstrncpy(mr->cFirstWritten, row[20]!=NULL?row[20]:"", sizeof(mr->cFirstWritten));
420 mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten);
421 bstrncpy(mr->cLastWritten, row[21]!=NULL?row[21]:"", sizeof(mr->cLastWritten));
422 mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
423 mr->InChanger = str_to_uint64(row[22]);
424 mr->EndFile = str_to_uint64(row[23]);
425 mr->EndBlock = str_to_uint64(row[24]);
426 mr->VolParts = str_to_int64(row[25]);
427 mr->LabelType = str_to_int64(row[26]);
428 bstrncpy(mr->cLabelDate, row[27]!=NULL?row[27]:"", sizeof(mr->cLabelDate));
429 mr->LabelDate = (time_t)str_to_utime(mr->cLabelDate);
430 mr->StorageId = str_to_int64(row[28]);
431 mr->Enabled = str_to_int64(row[29]);
432 mr->LocationId = str_to_int64(row[30]);
433 mr->RecycleCount = str_to_int64(row[31]);
434 bstrncpy(mr->cInitialWrite, row[32]!=NULL?row[32]:"", sizeof(mr->cInitialWrite));
435 mr->InitialWrite = (time_t)str_to_utime(mr->cInitialWrite);
436 mr->ScratchPoolId = str_to_int64(row[33]);
437 mr->RecyclePoolId = str_to_int64(row[34]);
438 mr->VolReadTime = str_to_int64(row[35]);
439 mr->VolWriteTime = str_to_int64(row[36]);
441 sql_free_result(mdb);
444 Dmsg1(050, "Rtn numrows=%d\n", numrows);
449 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/