]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql_find.c
Simplify db_check_max_connections code and ifdeffing
[bacula/bacula] / bacula / src / cats / sql_find.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
5
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
11    in the file LICENSE.
12
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.
17
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
21    02110-1301, USA.
22
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.
27 */
28 /*
29  * Bacula Catalog Database Find record interface routines
30  *
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
34  *
35  *    Kern Sibbald, December 2000
36  *
37  */
38
39
40 /* The following is necessary so that we do not include
41  * the dummy external definition of DB.
42  */
43 #define __SQL_C                       /* indicate that this is sql.c */
44
45 #include "bacula.h"
46 #include "cats.h"
47
48 #if    HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI
49
50 /* -----------------------------------------------------------------------
51  *
52  *   Generic Routines (or almost generic)
53  *
54  * -----------------------------------------------------------------------
55  */
56
57 /*
58  * Find job start time if JobId specified, otherwise
59  * find last Job start time Incremental and Differential saves.
60  *
61  *  StartTime is returned in stime
62  *
63  * Returns: 0 on failure
64  *          1 on success, jr is unchanged, but stime is set
65  */
66 bool
67 db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime)
68 {
69    SQL_ROW row;
70    char ed1[50], ed2[50];
71
72    db_lock(mdb);
73
74    pm_strcpy(stime, "0000-00-00 00:00:00");   /* default */
75    /* If no Id given, we must find corresponding job */
76    if (jr->JobId == 0) {
77       /* Differential is since last Full backup */
78       Mmsg(mdb->cmd,
79 "SELECT StartTime FROM Job WHERE JobStatus IN ('T','W') AND Type='%c' AND "
80 "Level='%c' AND Name='%s' AND ClientId=%s AND FileSetId=%s "
81 "ORDER BY StartTime DESC LIMIT 1",
82            jr->JobType, L_FULL, jr->Name, 
83            edit_int64(jr->ClientId, ed1), edit_int64(jr->FileSetId, ed2));
84
85       if (jr->JobLevel == L_DIFFERENTIAL) {
86          /* SQL cmd for Differential backup already edited above */
87
88       /* Incremental is since last Full, Incremental, or Differential */
89       } else if (jr->JobLevel == L_INCREMENTAL) {
90          /*
91           * For an Incremental job, we must first ensure
92           *  that a Full backup was done (cmd edited above)
93           *  then we do a second look to find the most recent
94           *  backup
95           */
96          if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
97             Mmsg2(&mdb->errmsg, _("Query error for start time request: ERR=%s\nCMD=%s\n"),
98                sql_strerror(mdb), mdb->cmd);
99             goto bail_out;
100          }
101          if ((row = sql_fetch_row(mdb)) == NULL) {
102             sql_free_result(mdb);
103             Mmsg(mdb->errmsg, _("No prior Full backup Job record found.\n"));
104             goto bail_out;
105          }
106          sql_free_result(mdb);
107          /* Now edit SQL command for Incremental Job */
108          Mmsg(mdb->cmd,
109 "SELECT StartTime FROM Job WHERE JobStatus IN ('T','W') AND Type='%c' AND "
110 "Level IN ('%c','%c','%c') AND Name='%s' AND ClientId=%s "
111 "AND FileSetId=%s ORDER BY StartTime DESC LIMIT 1",
112             jr->JobType, L_INCREMENTAL, L_DIFFERENTIAL, L_FULL, jr->Name,
113             edit_int64(jr->ClientId, ed1), edit_int64(jr->FileSetId, ed2));
114       } else {
115          Mmsg1(mdb->errmsg, _("Unknown level=%d\n"), jr->JobLevel);
116          goto bail_out;
117       }
118    } else {
119       Dmsg1(100, "Submitting: %s\n", mdb->cmd);
120       Mmsg(mdb->cmd, "SELECT StartTime FROM Job WHERE Job.JobId=%s", 
121            edit_int64(jr->JobId, ed1));
122    }
123
124    if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
125       pm_strcpy(stime, "");                   /* set EOS */
126       Mmsg2(&mdb->errmsg, _("Query error for start time request: ERR=%s\nCMD=%s\n"),
127          sql_strerror(mdb),  mdb->cmd);
128       goto bail_out;
129    }
130
131    if ((row = sql_fetch_row(mdb)) == NULL) {
132       Mmsg2(&mdb->errmsg, _("No Job record found: ERR=%s\nCMD=%s\n"),
133          sql_strerror(mdb),  mdb->cmd);
134       sql_free_result(mdb);
135       goto bail_out;
136    }
137    Dmsg1(100, "Got start time: %s\n", row[0]);
138    pm_strcpy(stime, row[0]);
139
140    sql_free_result(mdb);
141
142    db_unlock(mdb);
143    return true;
144
145 bail_out:
146    db_unlock(mdb);
147    return false;
148 }
149
150
151 /*
152  * Find the last job start time for the specified JobLevel
153  *
154  *  StartTime is returned in stime
155  *
156  * Returns: false on failure
157  *          true  on success, jr is unchanged, but stime is set
158  */
159 bool
160 db_find_last_job_start_time(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
167    pm_strcpy(stime, "0000-00-00 00:00:00");   /* default */
168
169    Mmsg(mdb->cmd,
170 "SELECT StartTime FROM Job WHERE JobStatus IN ('T','W') AND Type='%c' AND "
171 "Level='%c' AND Name='%s' AND ClientId=%s AND FileSetId=%s "
172 "ORDER BY StartTime DESC LIMIT 1",
173       jr->JobType, JobLevel, jr->Name, 
174       edit_int64(jr->ClientId, ed1), edit_int64(jr->FileSetId, ed2));
175    if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
176       Mmsg2(&mdb->errmsg, _("Query error for start time request: ERR=%s\nCMD=%s\n"),
177          sql_strerror(mdb), mdb->cmd);
178       goto bail_out;
179    }
180    if ((row = sql_fetch_row(mdb)) == NULL) {
181       sql_free_result(mdb);
182       Mmsg(mdb->errmsg, _("No prior Full backup Job record found.\n"));
183       goto bail_out;
184    }
185    Dmsg1(100, "Got start time: %s\n", row[0]);
186    pm_strcpy(stime, row[0]);
187    sql_free_result(mdb);
188    db_unlock(mdb);
189    return true;
190
191 bail_out:
192    db_unlock(mdb);
193    return false;
194 }
195
196 /*
197  * Find last failed job since given start-time
198  *   it must be either Full or Diff.
199  *
200  * Returns: false on failure
201  *          true  on success, jr is unchanged and stime unchanged
202  *                level returned in JobLevel
203  */
204 bool
205 db_find_failed_job_since(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM *stime, int &JobLevel)
206 {
207    SQL_ROW row;
208    char ed1[50], ed2[50];
209
210    db_lock(mdb);
211    /* Differential is since last Full backup */
212    Mmsg(mdb->cmd,
213 "SELECT Level FROM Job WHERE JobStatus NOT IN ('T','W') AND "
214 "Type='%c' AND Level IN ('%c','%c') AND Name='%s' AND ClientId=%s "
215 "AND FileSetId=%s AND StartTime>'%s' "
216 "ORDER BY StartTime DESC LIMIT 1",
217          jr->JobType, L_FULL, L_DIFFERENTIAL, jr->Name,
218          edit_int64(jr->ClientId, ed1), edit_int64(jr->FileSetId, ed2),
219          stime);
220
221    if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
222       db_unlock(mdb);
223       return false;
224    }
225
226    if ((row = sql_fetch_row(mdb)) == NULL) {
227       sql_free_result(mdb);
228       db_unlock(mdb);
229       return false;
230    }
231    JobLevel = (int)*row[0];
232    sql_free_result(mdb);
233
234    db_unlock(mdb);
235    return true;
236 }
237
238
239 /*
240  * Find JobId of last job that ran.  E.g. for
241  *   VERIFY_CATALOG we want the JobId of the last INIT.
242  *   For VERIFY_VOLUME_TO_CATALOG, we want the JobId of the last Job.
243  *
244  * Returns: true  on success
245  *          false on failure
246  */
247 bool
248 db_find_last_jobid(JCR *jcr, B_DB *mdb, const char *Name, JOB_DBR *jr)
249 {
250    SQL_ROW row;
251    char ed1[50];
252
253    /* Find last full */
254    db_lock(mdb);
255    Dmsg2(100, "JobLevel=%d JobType=%d\n", jr->JobLevel, jr->JobType);
256    if (jr->JobLevel == L_VERIFY_CATALOG) {
257       Mmsg(mdb->cmd,
258 "SELECT JobId FROM Job WHERE Type='V' AND Level='%c' AND "
259 " JobStatus IN ('T','W') AND Name='%s' AND "
260 "ClientId=%s ORDER BY StartTime DESC LIMIT 1",
261            L_VERIFY_INIT, jr->Name, 
262            edit_int64(jr->ClientId, ed1));
263    } else if (jr->JobLevel == L_VERIFY_VOLUME_TO_CATALOG ||
264               jr->JobLevel == L_VERIFY_DISK_TO_CATALOG ||
265               jr->JobType == JT_BACKUP) {
266       if (Name) {
267          Mmsg(mdb->cmd,
268 "SELECT JobId FROM Job WHERE Type='B' AND JobStatus IN ('T','W') AND "
269 "Name='%s' ORDER BY StartTime DESC LIMIT 1", Name);
270       } else {
271          Mmsg(mdb->cmd,
272 "SELECT JobId FROM Job WHERE Type='B' AND JobStatus IN ('T','W') AND "
273 "ClientId=%s ORDER BY StartTime DESC LIMIT 1", 
274            edit_int64(jr->ClientId, ed1));
275       }
276    } else {
277       Mmsg1(&mdb->errmsg, _("Unknown Job level=%d\n"), jr->JobLevel);
278       db_unlock(mdb);
279       return false;
280    }
281    Dmsg1(100, "Query: %s\n", mdb->cmd);
282    if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
283       db_unlock(mdb);
284       return false;
285    }
286    if ((row = sql_fetch_row(mdb)) == NULL) {
287       Mmsg1(&mdb->errmsg, _("No Job found for: %s.\n"), mdb->cmd);
288       sql_free_result(mdb);
289       db_unlock(mdb);
290       return false;
291    }
292
293    jr->JobId = str_to_int64(row[0]);
294    sql_free_result(mdb);
295
296    Dmsg1(100, "db_get_last_jobid: got JobId=%d\n", jr->JobId);
297    if (jr->JobId <= 0) {
298       Mmsg1(&mdb->errmsg, _("No Job found for: %s\n"), mdb->cmd);
299       db_unlock(mdb);
300       return false;
301    }
302
303    db_unlock(mdb);
304    return true;
305 }
306
307 /*
308  * Find Available Media (Volume) for Pool
309  *
310  * Find a Volume for a given PoolId, MediaType, and Status.
311  *
312  * Returns: 0 on failure
313  *          numrows on success
314  */
315 int
316 db_find_next_volume(JCR *jcr, B_DB *mdb, int item, bool InChanger, MEDIA_DBR *mr)
317 {
318    SQL_ROW row = NULL;
319    int numrows;
320    const char *order;
321
322    char ed1[50];
323
324    db_lock(mdb);
325    if (item == -1) {       /* find oldest volume */
326       /* Find oldest volume */
327       Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
328          "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
329          "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
330          "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
331          "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
332          "Enabled,LocationId,RecycleCount,InitialWrite,"
333          "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge "
334          "FROM Media WHERE PoolId=%s AND MediaType='%s' AND VolStatus IN ('Full',"
335          "'Recycle','Purged','Used','Append') AND Enabled=1 "
336          "ORDER BY LastWritten LIMIT 1", 
337          edit_int64(mr->PoolId, ed1), mr->MediaType);
338      item = 1;
339    } else {
340       POOL_MEM changer(PM_FNAME);
341       /* Find next available volume */
342       if (InChanger) {
343          Mmsg(changer, "AND InChanger=1 AND StorageId=%s",
344               edit_int64(mr->StorageId, ed1));
345       }
346       if (strcmp(mr->VolStatus, "Recycle") == 0 ||
347           strcmp(mr->VolStatus, "Purged") == 0) {
348          order = "AND Recycle=1 ORDER BY LastWritten ASC,MediaId";  /* take oldest that can be recycled */
349       } else {
350          order = "ORDER BY LastWritten IS NULL,LastWritten DESC,MediaId";   /* take most recently written */
351       }
352       Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
353          "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
354          "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
355          "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
356          "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
357          "Enabled,LocationId,RecycleCount,InitialWrite,"
358          "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge "
359          "FROM Media WHERE PoolId=%s AND MediaType='%s' AND Enabled=1 "
360          "AND VolStatus='%s' "
361          "%s "
362          "%s LIMIT %d",
363          edit_int64(mr->PoolId, ed1), mr->MediaType,
364          mr->VolStatus, changer.c_str(), order, item);
365    }
366    Dmsg1(050, "fnextvol=%s\n", mdb->cmd);
367    if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
368       db_unlock(mdb);
369       return 0;
370    }
371
372    numrows = sql_num_rows(mdb);
373    if (item > numrows || item < 1) {
374       Dmsg2(050, "item=%d got=%d\n", item, numrows);
375       Mmsg2(&mdb->errmsg, _("Request for Volume item %d greater than max %d or less than 1\n"),
376          item, numrows);
377       db_unlock(mdb);
378       return 0;
379    }
380
381    /* Note, we previously seeked to the row using:
382     *  sql_data_seek(mdb, item-1);
383     * but this failed on PostgreSQL, so now we loop
384     * over all the records.  This should not be too horrible since
385     * the maximum Volumes we look at in any case is 20.
386     */
387    while (item-- > 0) {
388       if ((row = sql_fetch_row(mdb)) == NULL) {
389          Dmsg1(050, "Fail fetch item=%d\n", item+1);
390          Mmsg1(&mdb->errmsg, _("No Volume record found for item %d.\n"), item);
391          sql_free_result(mdb);
392          db_unlock(mdb);
393          return 0;
394       }
395    }
396
397    /* Return fields in Media Record */
398    mr->MediaId = str_to_int64(row[0]);
399    bstrncpy(mr->VolumeName, row[1]!=NULL?row[1]:"", sizeof(mr->VolumeName));
400    mr->VolJobs = str_to_int64(row[2]);
401    mr->VolFiles = str_to_int64(row[3]);
402    mr->VolBlocks = str_to_int64(row[4]);
403    mr->VolBytes = str_to_uint64(row[5]);
404    mr->VolMounts = str_to_int64(row[6]);
405    mr->VolErrors = str_to_int64(row[7]);
406    mr->VolWrites = str_to_int64(row[8]);
407    mr->MaxVolBytes = str_to_uint64(row[9]);
408    mr->VolCapacityBytes = str_to_uint64(row[10]);
409    bstrncpy(mr->MediaType, row[11]!=NULL?row[11]:"", sizeof(mr->MediaType));
410    bstrncpy(mr->VolStatus, row[12]!=NULL?row[12]:"", sizeof(mr->VolStatus));
411    mr->PoolId = str_to_int64(row[13]);
412    mr->VolRetention = str_to_uint64(row[14]);
413    mr->VolUseDuration = str_to_uint64(row[15]);
414    mr->MaxVolJobs = str_to_int64(row[16]);
415    mr->MaxVolFiles = str_to_int64(row[17]);
416    mr->Recycle = str_to_int64(row[18]);
417    mr->Slot = str_to_int64(row[19]);
418    bstrncpy(mr->cFirstWritten, row[20]!=NULL?row[20]:"", sizeof(mr->cFirstWritten));
419    mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten);
420    bstrncpy(mr->cLastWritten, row[21]!=NULL?row[21]:"", sizeof(mr->cLastWritten));
421    mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
422    mr->InChanger = str_to_uint64(row[22]);
423    mr->EndFile = str_to_uint64(row[23]);
424    mr->EndBlock = str_to_uint64(row[24]);
425    mr->VolParts = str_to_int64(row[25]);
426    mr->LabelType = str_to_int64(row[26]);
427    bstrncpy(mr->cLabelDate, row[27]!=NULL?row[27]:"", sizeof(mr->cLabelDate));
428    mr->LabelDate = (time_t)str_to_utime(mr->cLabelDate);
429    mr->StorageId = str_to_int64(row[28]);
430    mr->Enabled = str_to_int64(row[29]);
431    mr->LocationId = str_to_int64(row[30]);
432    mr->RecycleCount = str_to_int64(row[31]);
433    bstrncpy(mr->cInitialWrite, row[32]!=NULL?row[32]:"", sizeof(mr->cInitialWrite));
434    mr->InitialWrite = (time_t)str_to_utime(mr->cInitialWrite);
435    mr->ScratchPoolId = str_to_int64(row[33]);
436    mr->RecyclePoolId = str_to_int64(row[34]);
437    mr->VolReadTime = str_to_int64(row[35]);
438    mr->VolWriteTime = str_to_int64(row[36]);
439    mr->ActionOnPurge = str_to_int64(row[37]);
440
441    sql_free_result(mdb);
442
443    db_unlock(mdb);
444    Dmsg1(050, "Rtn numrows=%d\n", numrows);
445    return numrows;
446 }
447
448
449 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/