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