]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql_find.c
Apply patch (with some difficulties) from Joao Henrique Freitas
[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    Bacula® - The Network Backup Solution
14
15    Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
16
17    The main author of Bacula is Kern Sibbald, with contributions from
18    many others, a complete list can be found in the file AUTHORS.
19    This program is Free Software; you can redistribute it and/or
20    modify it under the terms of version two of the GNU General Public
21    License as published by the Free Software Foundation and included
22    in the file LICENSE.
23
24    This program is distributed in the hope that it will be useful, but
25    WITHOUT ANY WARRANTY; without even the implied warranty of
26    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27    General Public License for more details.
28
29    You should have received a copy of the GNU General Public License
30    along with this program; if not, write to the Free Software
31    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
32    02110-1301, USA.
33
34    Bacula® is a registered trademark of John Walker.
35    The licensor of Bacula is the Free Software Foundation Europe
36    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
37    Switzerland, email:ftf@fsfeurope.org.
38 */
39
40
41 /* The following is necessary so that we do not include
42  * the dummy external definition of DB.
43  */
44 #define __SQL_C                       /* indicate that this is sql.c */
45
46 #include "bacula.h"
47 #include "cats.h"
48
49 #if    HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI
50
51 /* -----------------------------------------------------------------------
52  *
53  *   Generic Routines (or almost generic)
54  *
55  * -----------------------------------------------------------------------
56  */
57
58 /*
59  * Find job start time if JobId specified, otherwise
60  * find last full save for Incremental and Differential saves.
61  *
62  *  StartTime is returned in stime
63  *
64  * Returns: 0 on failure
65  *          1 on success, jr is unchanged, but stime is set
66  */
67 bool
68 db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime)
69 {
70    SQL_ROW row;
71    char ed1[50], ed2[50];
72
73    db_lock(mdb);
74
75    pm_strcpy(stime, "0000-00-00 00:00:00");   /* default */
76    /* If no Id given, we must find corresponding job */
77    if (jr->JobId == 0) {
78       /* Differential is since last Full backup */
79          Mmsg(mdb->cmd,
80 "SELECT StartTime FROM Job WHERE JobStatus='T' 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));
85
86       if (jr->JobLevel == L_DIFFERENTIAL) {
87          /* SQL cmd for Differential backup already edited above */
88
89       /* Incremental is since last Full, Incremental, or Differential */
90       } else if (jr->JobLevel == L_INCREMENTAL) {
91          /*
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
95           *  backup
96           */
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);
100             goto bail_out;
101          }
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"));
105             goto bail_out;
106          }
107          sql_free_result(mdb);
108          /* Now edit SQL command for Incremental Job */
109          Mmsg(mdb->cmd,
110 "SELECT StartTime FROM Job WHERE JobStatus='T' 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));
115       } else {
116          Mmsg1(mdb->errmsg, _("Unknown level=%d\n"), jr->JobLevel);
117          goto bail_out;
118       }
119    } else {
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));
123    }
124
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);
129       goto bail_out;
130    }
131
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);
136       goto bail_out;
137    }
138    Dmsg1(100, "Got start time: %s\n", row[0]);
139    pm_strcpy(stime, row[0]);
140
141    sql_free_result(mdb);
142
143    db_unlock(mdb);
144    return true;
145
146 bail_out:
147    db_unlock(mdb);
148    return false;
149 }
150
151 /*
152  * Find last failed job since given start-time
153  *   it must be either Full or Diff.
154  *
155  * Returns: false on failure
156  *          true  on success, jr is unchanged and stime unchanged
157  *                level returned in JobLevel
158  */
159 bool
160 db_find_failed_job_since(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM *stime, int &JobLevel)
161 {
162    SQL_ROW row;
163    char ed1[50], ed2[50];
164
165    db_lock(mdb);
166    /* Differential is since last Full backup */
167    Mmsg(mdb->cmd,
168 "SELECT Level FROM Job WHERE JobStatus!='T' AND Type='%c' AND "
169 "Level IN ('%c','%c') AND Name='%s' AND ClientId=%s "
170 "AND FileSetId=%s AND StartTime>'%s' "
171 "ORDER BY StartTime DESC LIMIT 1",
172          jr->JobType, L_FULL, L_DIFFERENTIAL, jr->Name,
173          edit_int64(jr->ClientId, ed1), edit_int64(jr->FileSetId, ed2),
174          stime);
175
176    if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
177       db_unlock(mdb);
178       return false;
179    }
180
181    if ((row = sql_fetch_row(mdb)) == NULL) {
182       sql_free_result(mdb);
183       db_unlock(mdb);
184       return false;
185    }
186    JobLevel = (int)*row[0];
187    sql_free_result(mdb);
188
189    db_unlock(mdb);
190    return true;
191 }
192
193
194 /*
195  * Find JobId of last job that ran.  E.g. for
196  *   VERIFY_CATALOG we want the JobId of the last INIT.
197  *   For VERIFY_VOLUME_TO_CATALOG, we want the JobId of the last Job.
198  *
199  * Returns: true  on success
200  *          false on failure
201  */
202 bool
203 db_find_last_jobid(JCR *jcr, B_DB *mdb, const char *Name, JOB_DBR *jr)
204 {
205    SQL_ROW row;
206    char ed1[50];
207
208    /* Find last full */
209    db_lock(mdb);
210    Dmsg2(100, "JobLevel=%d JobType=%d\n", jr->JobLevel, jr->JobType);
211    if (jr->JobLevel == L_VERIFY_CATALOG) {
212       Mmsg(mdb->cmd,
213 "SELECT JobId FROM Job WHERE Type='V' AND Level='%c' AND "
214 " JobStatus='T' AND Name='%s' AND "
215 "ClientId=%s ORDER BY StartTime DESC LIMIT 1",
216            L_VERIFY_INIT, jr->Name, 
217            edit_int64(jr->ClientId, ed1));
218    } else if (jr->JobLevel == L_VERIFY_VOLUME_TO_CATALOG ||
219               jr->JobLevel == L_VERIFY_DISK_TO_CATALOG ||
220               jr->JobType == JT_BACKUP) {
221       if (Name) {
222          Mmsg(mdb->cmd,
223 "SELECT JobId FROM Job WHERE Type='B' AND JobStatus='T' AND "
224 "Name='%s' ORDER BY StartTime DESC LIMIT 1", Name);
225       } else {
226          Mmsg(mdb->cmd,
227 "SELECT JobId FROM Job WHERE Type='B' AND JobStatus='T' AND "
228 "ClientId=%s ORDER BY StartTime DESC LIMIT 1", 
229            edit_int64(jr->ClientId, ed1));
230       }
231    } else {
232       Mmsg1(&mdb->errmsg, _("Unknown Job level=%d\n"), jr->JobLevel);
233       db_unlock(mdb);
234       return false;
235    }
236    Dmsg1(100, "Query: %s\n", mdb->cmd);
237    if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
238       db_unlock(mdb);
239       return false;
240    }
241    if ((row = sql_fetch_row(mdb)) == NULL) {
242       Mmsg1(&mdb->errmsg, _("No Job found for: %s.\n"), mdb->cmd);
243       sql_free_result(mdb);
244       db_unlock(mdb);
245       return false;
246    }
247
248    jr->JobId = str_to_int64(row[0]);
249    sql_free_result(mdb);
250
251    Dmsg1(100, "db_get_last_jobid: got JobId=%d\n", jr->JobId);
252    if (jr->JobId <= 0) {
253       Mmsg1(&mdb->errmsg, _("No Job found for: %s\n"), mdb->cmd);
254       db_unlock(mdb);
255       return false;
256    }
257
258    db_unlock(mdb);
259    return true;
260 }
261
262 /*
263  * Find Available Media (Volume) for Pool
264  *
265  * Find a Volume for a given PoolId, MediaType, and Status.
266  *
267  * Returns: 0 on failure
268  *          numrows on success
269  */
270 int
271 db_find_next_volume(JCR *jcr, B_DB *mdb, int item, bool InChanger, MEDIA_DBR *mr)
272 {
273    SQL_ROW row = NULL;
274    int numrows;
275    const char *order;
276
277    char ed1[50];
278
279    db_lock(mdb);
280    if (item == -1) {       /* find oldest volume */
281       /* Find oldest volume */
282       Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
283          "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
284          "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
285          "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
286          "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
287          "Enabled,LocationId,RecycleCount,InitialWrite,"
288          "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime "
289          "FROM Media WHERE PoolId=%s AND MediaType='%s' AND VolStatus IN ('Full',"
290          "'Recycle','Purged','Used','Append') AND Enabled=1 "
291          "ORDER BY LastWritten LIMIT 1", 
292          edit_int64(mr->PoolId, ed1), mr->MediaType);
293      item = 1;
294    } else {
295       char changer[100];
296       /* Find next available volume */
297       if (InChanger) {
298          bsnprintf(changer, sizeof(changer), "AND InChanger=1 AND StorageId=%s",
299             edit_int64(mr->StorageId, ed1));
300       } else {
301          changer[0] = 0;
302       }
303       if (strcmp(mr->VolStatus, "Recycle") == 0 ||
304           strcmp(mr->VolStatus, "Purged") == 0) {
305          order = "ORDER BY LastWritten ASC,MediaId";  /* take oldest */
306       } else {
307          order = "ORDER BY LastWritten IS NULL,LastWritten DESC,MediaId";   /* take most recently written */
308       }
309       Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
310          "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
311          "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
312          "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
313          "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
314          "Enabled,LocationId,RecycleCount,InitialWrite,"
315          "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime "
316          "FROM Media WHERE PoolId=%s AND MediaType='%s' AND Enabled=1 "
317          "AND VolStatus='%s' "
318          "%s "
319          "%s LIMIT %d",
320          edit_int64(mr->PoolId, ed1), mr->MediaType,
321          mr->VolStatus, changer, order, item);
322    }
323    Dmsg1(050, "fnextvol=%s\n", mdb->cmd);
324    if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
325       db_unlock(mdb);
326       return 0;
327    }
328
329    numrows = sql_num_rows(mdb);
330    if (item > numrows || item < 1) {
331       Dmsg2(050, "item=%d got=%d\n", item, numrows);
332       Mmsg2(&mdb->errmsg, _("Request for Volume item %d greater than max %d or less than 1\n"),
333          item, numrows);
334       db_unlock(mdb);
335       return 0;
336    }
337
338    /* Note, we previously seeked to the row using:
339     *  sql_data_seek(mdb, item-1);
340     * but this failed on PostgreSQL, so now we loop
341     * over all the records.  This should not be too horrible since
342     * the maximum Volumes we look at in any case is 20.
343     */
344    while (item-- > 0) {
345       if ((row = sql_fetch_row(mdb)) == NULL) {
346          Dmsg1(050, "Fail fetch item=%d\n", item+1);
347          Mmsg1(&mdb->errmsg, _("No Volume record found for item %d.\n"), item);
348          sql_free_result(mdb);
349          db_unlock(mdb);
350          return 0;
351       }
352    }
353
354    /* Return fields in Media Record */
355    mr->MediaId = str_to_int64(row[0]);
356    bstrncpy(mr->VolumeName, row[1]!=NULL?row[1]:"", sizeof(mr->VolumeName));
357    mr->VolJobs = str_to_int64(row[2]);
358    mr->VolFiles = str_to_int64(row[3]);
359    mr->VolBlocks = str_to_int64(row[4]);
360    mr->VolBytes = str_to_uint64(row[5]);
361    mr->VolMounts = str_to_int64(row[6]);
362    mr->VolErrors = str_to_int64(row[7]);
363    mr->VolWrites = str_to_int64(row[8]);
364    mr->MaxVolBytes = str_to_uint64(row[9]);
365    mr->VolCapacityBytes = str_to_uint64(row[10]);
366    bstrncpy(mr->MediaType, row[11]!=NULL?row[11]:"", sizeof(mr->MediaType));
367    bstrncpy(mr->VolStatus, row[12]!=NULL?row[12]:"", sizeof(mr->VolStatus));
368    mr->PoolId = str_to_int64(row[13]);
369    mr->VolRetention = str_to_uint64(row[14]);
370    mr->VolUseDuration = str_to_uint64(row[15]);
371    mr->MaxVolJobs = str_to_int64(row[16]);
372    mr->MaxVolFiles = str_to_int64(row[17]);
373    mr->Recycle = str_to_int64(row[18]);
374    mr->Slot = str_to_int64(row[19]);
375    bstrncpy(mr->cFirstWritten, row[20]!=NULL?row[20]:"", sizeof(mr->cFirstWritten));
376    mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten);
377    bstrncpy(mr->cLastWritten, row[21]!=NULL?row[21]:"", sizeof(mr->cLastWritten));
378    mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
379    mr->InChanger = str_to_uint64(row[22]);
380    mr->EndFile = str_to_uint64(row[23]);
381    mr->EndBlock = str_to_uint64(row[24]);
382    mr->VolParts = str_to_int64(row[25]);
383    mr->LabelType = str_to_int64(row[26]);
384    bstrncpy(mr->cLabelDate, row[27]!=NULL?row[27]:"", sizeof(mr->cLabelDate));
385    mr->LabelDate = (time_t)str_to_utime(mr->cLabelDate);
386    mr->StorageId = str_to_int64(row[28]);
387    mr->Enabled = str_to_int64(row[29]);
388    mr->LocationId = str_to_int64(row[30]);
389    mr->RecycleCount = str_to_int64(row[31]);
390    bstrncpy(mr->cInitialWrite, row[32]!=NULL?row[32]:"", sizeof(mr->cInitialWrite));
391    mr->InitialWrite = (time_t)str_to_utime(mr->cInitialWrite);
392    mr->ScratchPoolId = str_to_int64(row[33]);
393    mr->RecyclePoolId = str_to_int64(row[34]);
394    mr->VolReadTime = str_to_int64(row[35]);
395    mr->VolWriteTime = str_to_int64(row[36]);
396
397    sql_free_result(mdb);
398
399    db_unlock(mdb);
400    Dmsg1(050, "Rtn numrows=%d\n", numrows);
401    return numrows;
402 }
403
404
405 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/