]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql_get.c
kes Fix bug #1046 VolumeToCatalog incorrectly reports mounted
[bacula/bacula] / bacula / src / cats / sql_get.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 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 Get record interface routines
30  *  Note, these routines generally get a record by id or
31  *        by name.  If more logic is involved, the routine
32  *        should be in find.c
33  *
34  *    Kern Sibbald, March 2000
35  *
36  *    Version $Id$
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_DBI
49
50 /* -----------------------------------------------------------------------
51  *
52  *   Generic Routines (or almost generic)
53  *
54  * -----------------------------------------------------------------------
55  */
56
57 /* Forward referenced functions */
58 static int db_get_file_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, FILE_DBR *fdbr);
59 static int db_get_filename_record(JCR *jcr, B_DB *mdb);
60 static int db_get_path_record(JCR *jcr, B_DB *mdb);
61
62
63 /*
64  * Given a full filename (with path), look up the File record
65  * (with attributes) in the database.
66  *
67  *  Returns: 0 on failure
68  *           1 on success with the File record in FILE_DBR
69  */
70 int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JOB_DBR *jr, FILE_DBR *fdbr)
71 {
72    int stat;
73    Dmsg1(100, "db_get_file_att_record fname=%s \n", fname);
74
75    db_lock(mdb);
76    split_path_and_file(jcr, mdb, fname);
77
78    fdbr->FilenameId = db_get_filename_record(jcr, mdb);
79
80    fdbr->PathId = db_get_path_record(jcr, mdb);
81
82    stat = db_get_file_record(jcr, mdb, jr, fdbr);
83
84    db_unlock(mdb);
85
86    return stat;
87 }
88
89
90 /*
91  * Get a File record
92  * Returns: 0 on failure
93  *          1 on success
94  *
95  *  DO NOT use Jmsg in this routine.
96  *
97  *  Note in this routine, we do not use Jmsg because it may be
98  *    called to get attributes of a non-existent file, which is
99  *    "normal" if a new file is found during Verify.
100  *
101  *  The following is a bit of a kludge: because we always backup a 
102  *    directory entry, we can end up with two copies of the directory 
103  *    in the backup. One is when we encounter the directory and find 
104  *    we cannot recurse into it, and the other is when we find an 
105  *    explicit mention of the directory. This can also happen if the 
106  *    use includes the directory twice.  In this case, Verify 
107  *    VolumeToCatalog fails because we have two copies in the catalog, 
108  *    and only the first one is marked (twice).  So, when calling from Verify, 
109  *    jr is not NULL and we know jr->FileIndex is the fileindex
110  *    of the version of the directory/file we actually want and do
111  *    a more explicit SQL search.
112  */
113 static
114 int db_get_file_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, FILE_DBR *fdbr)
115 {
116    SQL_ROW row;
117    int stat = 0;
118    char ed1[50], ed2[50], ed3[50];
119
120    if (jcr->get_JobLevel() == L_VERIFY_DISK_TO_CATALOG) {
121       Mmsg(mdb->cmd,
122 "SELECT FileId, LStat, MD5 FROM File,Job WHERE "
123 "File.JobId=Job.JobId AND File.PathId=%s AND "
124 "File.FilenameId=%s AND Job.Type='B' AND Job.JobStatus='T' AND "
125 "ClientId=%s ORDER BY StartTime DESC LIMIT 1",
126       edit_int64(fdbr->PathId, ed1), 
127       edit_int64(fdbr->FilenameId, ed2), 
128       edit_int64(jr->ClientId,ed3));
129
130    } else if (jr != NULL) {
131       /* Called from Verify so jr->FileIndex is valid */
132       Mmsg(mdb->cmd,
133 "SELECT FileId, LStat, MD5 FROM File WHERE File.JobId=%s AND File.PathId=%s AND "
134 "File.FilenameId=%s AND FileIndex=%u", 
135       edit_int64(fdbr->JobId, ed1), 
136       edit_int64(fdbr->PathId, ed2), 
137       edit_int64(fdbr->FilenameId,ed3),
138       jr->FileIndex);
139    } else {
140       Mmsg(mdb->cmd,
141 "SELECT FileId, LStat, MD5 FROM File WHERE File.JobId=%s AND File.PathId=%s AND "
142 "File.FilenameId=%s", 
143       edit_int64(fdbr->JobId, ed1), 
144       edit_int64(fdbr->PathId, ed2), 
145       edit_int64(fdbr->FilenameId,ed3));
146    }
147    Dmsg3(450, "Get_file_record JobId=%u FilenameId=%u PathId=%u\n",
148       fdbr->JobId, fdbr->FilenameId, fdbr->PathId);
149
150    Dmsg1(100, "Query=%s\n", mdb->cmd);
151
152    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
153       mdb->num_rows = sql_num_rows(mdb);
154       Dmsg1(050, "get_file_record num_rows=%d\n", (int)mdb->num_rows);
155       if (mdb->num_rows > 1) {
156          Mmsg1(mdb->errmsg, _("get_file_record want 1 got rows=%d\n"),
157             mdb->num_rows);
158          Dmsg1(000, "=== Problem!  %s", mdb->errmsg);
159       }
160       if (mdb->num_rows >= 1) {
161          if ((row = sql_fetch_row(mdb)) == NULL) {
162             Mmsg1(mdb->errmsg, _("Error fetching row: %s\n"), sql_strerror(mdb));
163          } else {
164             fdbr->FileId = (FileId_t)str_to_int64(row[0]);
165             bstrncpy(fdbr->LStat, row[1], sizeof(fdbr->LStat));
166             bstrncpy(fdbr->Digest, row[2], sizeof(fdbr->Digest));
167             stat = 1;
168          }
169       } else {
170          Mmsg2(mdb->errmsg, _("File record for PathId=%s FilenameId=%s not found.\n"),
171             edit_int64(fdbr->PathId, ed1), 
172             edit_int64(fdbr->FilenameId, ed2));
173       }
174       sql_free_result(mdb);
175    } else {
176       Mmsg(mdb->errmsg, _("File record not found in Catalog.\n"));
177    }
178    return stat;
179
180 }
181
182 /* Get Filename record
183  * Returns: 0 on failure
184  *          FilenameId on success
185  *
186  *   DO NOT use Jmsg in this routine (see notes for get_file_record)
187  */
188 static int db_get_filename_record(JCR *jcr, B_DB *mdb)
189 {
190    SQL_ROW row;
191    int FilenameId = 0;
192
193    mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->fnl+2);
194    db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
195
196    Mmsg(mdb->cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", mdb->esc_name);
197    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
198       char ed1[30];
199       mdb->num_rows = sql_num_rows(mdb);
200       if (mdb->num_rows > 1) {
201          Mmsg2(mdb->errmsg, _("More than one Filename!: %s for file: %s\n"),
202             edit_uint64(mdb->num_rows, ed1), mdb->fname);
203          Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
204       }
205       if (mdb->num_rows >= 1) {
206          if ((row = sql_fetch_row(mdb)) == NULL) {
207             Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
208          } else {
209             FilenameId = str_to_int64(row[0]);
210             if (FilenameId <= 0) {
211                Mmsg2(mdb->errmsg, _("Get DB Filename record %s found bad record: %d\n"),
212                   mdb->cmd, FilenameId);
213                FilenameId = 0;
214             }
215          }
216       } else {
217          Mmsg1(mdb->errmsg, _("Filename record: %s not found.\n"), mdb->fname);
218       }
219       sql_free_result(mdb);
220    } else {
221       Mmsg(mdb->errmsg, _("Filename record: %s not found in Catalog.\n"), mdb->fname);
222    }
223    return FilenameId;
224 }
225
226 /* Get path record
227  * Returns: 0 on failure
228  *          PathId on success
229  *
230  *   DO NOT use Jmsg in this routine (see notes for get_file_record)
231  */
232 static int db_get_path_record(JCR *jcr, B_DB *mdb)
233 {
234    SQL_ROW row;
235    uint32_t PathId = 0;
236
237    mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->pnl+2);
238    db_escape_string(jcr, mdb, mdb->esc_name, mdb->path, mdb->pnl);
239
240    if (mdb->cached_path_id != 0 && mdb->cached_path_len == mdb->pnl &&
241        strcmp(mdb->cached_path, mdb->path) == 0) {
242       return mdb->cached_path_id;
243    }
244
245    Mmsg(mdb->cmd, "SELECT PathId FROM Path WHERE Path='%s'", mdb->esc_name);
246
247    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
248       char ed1[30];
249       mdb->num_rows = sql_num_rows(mdb);
250       if (mdb->num_rows > 1) {
251          Mmsg2(mdb->errmsg, _("More than one Path!: %s for path: %s\n"),
252             edit_uint64(mdb->num_rows, ed1), mdb->path);
253          Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
254       }
255       /* Even if there are multiple paths, take the first one */
256       if (mdb->num_rows >= 1) {
257          if ((row = sql_fetch_row(mdb)) == NULL) {
258             Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
259          } else {
260             PathId = str_to_int64(row[0]);
261             if (PathId <= 0) {
262                Mmsg2(mdb->errmsg, _("Get DB path record %s found bad record: %s\n"),
263                   mdb->cmd, edit_int64(PathId, ed1));
264                PathId = 0;
265             } else {
266                /* Cache path */
267                if (PathId != mdb->cached_path_id) {
268                   mdb->cached_path_id = PathId;
269                   mdb->cached_path_len = mdb->pnl;
270                   pm_strcpy(mdb->cached_path, mdb->path);
271                }
272             }
273          }
274       } else {
275          Mmsg1(mdb->errmsg, _("Path record: %s not found.\n"), mdb->path);
276       }
277       sql_free_result(mdb);
278    } else {
279       Mmsg(mdb->errmsg, _("Path record: %s not found in Catalog.\n"), mdb->path);
280    }
281    return PathId;
282 }
283
284
285 /*
286  * Get Job record for given JobId or Job name
287  * Returns: false on failure
288  *          true  on success
289  */
290 bool db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr)
291 {
292    SQL_ROW row;
293    char ed1[50];
294
295    db_lock(mdb);
296    if (jr->JobId == 0) {
297       Mmsg(mdb->cmd, "SELECT VolSessionId,VolSessionTime,"
298 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
299 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
300 "SchedTime,RealEndTime "
301 "FROM Job WHERE Job='%s'", jr->Job);
302     } else {
303       Mmsg(mdb->cmd, "SELECT VolSessionId,VolSessionTime,"
304 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
305 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
306 "SchedTime,RealEndTime "
307 "FROM Job WHERE JobId=%s", 
308           edit_int64(jr->JobId, ed1));
309     }
310
311    if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
312       db_unlock(mdb);
313       return false;                   /* failed */
314    }
315    if ((row = sql_fetch_row(mdb)) == NULL) {
316       Mmsg1(mdb->errmsg, _("No Job found for JobId %s\n"), edit_int64(jr->JobId, ed1));
317       sql_free_result(mdb);
318       db_unlock(mdb);
319       return false;                   /* failed */
320    }
321
322    jr->VolSessionId = str_to_uint64(row[0]);
323    jr->VolSessionTime = str_to_uint64(row[1]);
324    jr->PoolId = str_to_int64(row[2]);
325    bstrncpy(jr->cStartTime, row[3]!=NULL?row[3]:"", sizeof(jr->cStartTime));
326    bstrncpy(jr->cEndTime, row[4]!=NULL?row[4]:"", sizeof(jr->cEndTime));
327    jr->JobFiles = str_to_int64(row[5]);
328    jr->JobBytes = str_to_int64(row[6]);
329    jr->JobTDate = str_to_int64(row[7]);
330    bstrncpy(jr->Job, row[8]!=NULL?row[8]:"", sizeof(jr->Job));
331    jr->JobStatus = row[9]!=NULL?(int)*row[9]:JS_FatalError;
332    jr->JobType = row[10]!=NULL?(int)*row[10]:JT_BACKUP;
333    jr->JobLevel = row[11]!=NULL?(int)*row[11]:L_NONE;
334    jr->ClientId = str_to_uint64(row[12]!=NULL?row[12]:(char *)"");
335    bstrncpy(jr->Name, row[13]!=NULL?row[13]:"", sizeof(jr->Name));
336    jr->PriorJobId = str_to_uint64(row[14]!=NULL?row[14]:(char *)"");
337    bstrncpy(jr->cRealEndTime, row[15]!=NULL?row[15]:"", sizeof(jr->cRealEndTime));
338    if (jr->JobId == 0) {
339       jr->JobId = str_to_int64(row[16]);
340    }
341    jr->FileSetId = str_to_int64(row[17]);
342    bstrncpy(jr->cSchedTime, row[3]!=NULL?row[18]:"", sizeof(jr->cSchedTime));
343    bstrncpy(jr->cRealEndTime, row[3]!=NULL?row[19]:"", sizeof(jr->cRealEndTime));
344    jr->StartTime = str_to_utime(jr->cStartTime);
345    jr->SchedTime = str_to_utime(jr->cSchedTime);
346    jr->EndTime = str_to_utime(jr->cEndTime);
347    jr->RealEndTime = str_to_utime(jr->cRealEndTime);
348    sql_free_result(mdb);
349
350    db_unlock(mdb);
351    return true;
352 }
353
354 /*
355  * Find VolumeNames for a given JobId
356  *  Returns: 0 on error or no Volumes found
357  *           number of volumes on success
358  *              Volumes are concatenated in VolumeNames
359  *              separated by a vertical bar (|) in the order
360  *              that they were written.
361  *
362  *  Returns: number of volumes on success
363  */
364 int db_get_job_volume_names(JCR *jcr, B_DB *mdb, JobId_t JobId, POOLMEM **VolumeNames)
365 {
366    SQL_ROW row;
367    char ed1[50];
368    int stat = 0;
369    int i;
370
371    db_lock(mdb);
372    /* Get one entry per VolumeName, but "sort" by VolIndex */
373    Mmsg(mdb->cmd,
374         "SELECT VolumeName,MAX(VolIndex) FROM JobMedia,Media WHERE "
375         "JobMedia.JobId=%s AND JobMedia.MediaId=Media.MediaId "
376         "GROUP BY VolumeName "
377         "ORDER BY 2 ASC", edit_int64(JobId,ed1));
378
379    Dmsg1(130, "VolNam=%s\n", mdb->cmd);
380    *VolumeNames[0] = 0;
381    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
382       mdb->num_rows = sql_num_rows(mdb);
383       Dmsg1(130, "Num rows=%d\n", mdb->num_rows);
384       if (mdb->num_rows <= 0) {
385          Mmsg1(mdb->errmsg, _("No volumes found for JobId=%d\n"), JobId);
386          stat = 0;
387       } else {
388          stat = mdb->num_rows;
389          for (i=0; i < stat; i++) {
390             if ((row = sql_fetch_row(mdb)) == NULL) {
391                Mmsg2(mdb->errmsg, _("Error fetching row %d: ERR=%s\n"), i, sql_strerror(mdb));
392                Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
393                stat = 0;
394                break;
395             } else {
396                if (*VolumeNames[0] != 0) {
397                   pm_strcat(VolumeNames, "|");
398                }
399                pm_strcat(VolumeNames, row[0]);
400             }
401          }
402       }
403       sql_free_result(mdb);
404    } else {
405       Mmsg(mdb->errmsg, _("No Volume for JobId %d found in Catalog.\n"), JobId);
406    }
407    db_unlock(mdb);
408    return stat;
409 }
410
411 /*
412  * Find Volume parameters for a give JobId
413  *  Returns: 0 on error or no Volumes found
414  *           number of volumes on success
415  *           List of Volumes and start/end file/blocks (malloced structure!)
416  *
417  *  Returns: number of volumes on success
418  */
419 int db_get_job_volume_parameters(JCR *jcr, B_DB *mdb, JobId_t JobId, VOL_PARAMS **VolParams)
420 {
421    SQL_ROW row;
422    char ed1[50];
423    int stat = 0;
424    int i;
425    VOL_PARAMS *Vols = NULL;
426
427    db_lock(mdb);
428    Mmsg(mdb->cmd,
429 "SELECT VolumeName,MediaType,FirstIndex,LastIndex,StartFile,"
430 "JobMedia.EndFile,StartBlock,JobMedia.EndBlock,Copy,"
431 "Slot,StorageId"
432 " FROM JobMedia,Media WHERE JobMedia.JobId=%s"
433 " AND JobMedia.MediaId=Media.MediaId ORDER BY VolIndex,JobMediaId",
434         edit_int64(JobId, ed1));
435
436    Dmsg1(130, "VolNam=%s\n", mdb->cmd);
437    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
438       mdb->num_rows = sql_num_rows(mdb);
439       Dmsg1(200, "Num rows=%d\n", mdb->num_rows);
440       if (mdb->num_rows <= 0) {
441          Mmsg1(mdb->errmsg, _("No volumes found for JobId=%d\n"), JobId);
442          stat = 0;
443       } else {
444          stat = mdb->num_rows;
445          DBId_t *SId = NULL;
446          if (stat > 0) {
447             *VolParams = Vols = (VOL_PARAMS *)malloc(stat * sizeof(VOL_PARAMS));
448             SId = (DBId_t *)malloc(stat * sizeof(DBId_t));
449          }
450          for (i=0; i < stat; i++) {
451             if ((row = sql_fetch_row(mdb)) == NULL) {
452                Mmsg2(mdb->errmsg, _("Error fetching row %d: ERR=%s\n"), i, sql_strerror(mdb));
453                Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
454                stat = 0;
455                break;
456             } else {
457                DBId_t StorageId;
458                bstrncpy(Vols[i].VolumeName, row[0], MAX_NAME_LENGTH);
459                bstrncpy(Vols[i].MediaType, row[1], MAX_NAME_LENGTH);
460                Vols[i].FirstIndex = str_to_uint64(row[2]);
461                Vols[i].LastIndex = str_to_uint64(row[3]);
462                Vols[i].StartFile = str_to_uint64(row[4]);
463                Vols[i].EndFile = str_to_uint64(row[5]);
464                Vols[i].StartBlock = str_to_uint64(row[6]);
465                Vols[i].EndBlock = str_to_uint64(row[7]);
466 //             Vols[i].Copy = str_to_uint64(row[8]);
467                Vols[i].Slot = str_to_uint64(row[9]);
468                StorageId = str_to_uint64(row[10]);
469                Vols[i].Storage[0] = 0;
470                SId[i] = StorageId;
471             }
472          }
473          for (i=0; i < stat; i++) {
474             if (SId[i] != 0) {
475                Mmsg(mdb->cmd, "SELECT Name from Storage WHERE StorageId=%s",
476                   edit_int64(SId[i], ed1));
477                if (QUERY_DB(jcr, mdb, mdb->cmd)) {
478                   if ((row = sql_fetch_row(mdb)) && row[0]) {
479                      bstrncpy(Vols[i].Storage, row[0], MAX_NAME_LENGTH);
480                   }
481                }
482             }
483          }
484          if (SId) {
485             free(SId);
486          }
487       }
488       sql_free_result(mdb);
489    }
490    db_unlock(mdb);
491    return stat;
492 }
493
494
495
496 /*
497  * Get the number of pool records
498  *
499  * Returns: -1 on failure
500  *          number on success
501  */
502 int db_get_num_pool_records(JCR *jcr, B_DB *mdb)
503 {
504    int stat = 0;
505
506    db_lock(mdb);
507    Mmsg(mdb->cmd, "SELECT count(*) from Pool");
508    stat = get_sql_record_max(jcr, mdb);
509    db_unlock(mdb);
510    return stat;
511 }
512
513 /*
514  * This function returns a list of all the Pool record ids.
515  *  The caller must free ids if non-NULL.
516  *
517  *  Returns 0: on failure
518  *          1: on success
519  */
520 int db_get_pool_ids(JCR *jcr, B_DB *mdb, int *num_ids, uint32_t *ids[])
521 {
522    SQL_ROW row;
523    int stat = 0;
524    int i = 0;
525    uint32_t *id;
526
527    db_lock(mdb);
528    *ids = NULL;
529    Mmsg(mdb->cmd, "SELECT PoolId FROM Pool");
530    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
531       *num_ids = sql_num_rows(mdb);
532       if (*num_ids > 0) {
533          id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
534          while ((row = sql_fetch_row(mdb)) != NULL) {
535             id[i++] = str_to_uint64(row[0]);
536          }
537          *ids = id;
538       }
539       sql_free_result(mdb);
540       stat = 1;
541    } else {
542       Mmsg(mdb->errmsg, _("Pool id select failed: ERR=%s\n"), sql_strerror(mdb));
543       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
544       stat = 0;
545    }
546    db_unlock(mdb);
547    return stat;
548 }
549
550 /*
551  * This function returns a list of all the Client record ids.
552  *  The caller must free ids if non-NULL.
553  *
554  *  Returns 0: on failure
555  *          1: on success
556  */
557 int db_get_client_ids(JCR *jcr, B_DB *mdb, int *num_ids, uint32_t *ids[])
558 {
559    SQL_ROW row;
560    int stat = 0;
561    int i = 0;
562    uint32_t *id;
563
564    db_lock(mdb);
565    *ids = NULL;
566    Mmsg(mdb->cmd, "SELECT ClientId FROM Client");
567    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
568       *num_ids = sql_num_rows(mdb);
569       if (*num_ids > 0) {
570          id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
571          while ((row = sql_fetch_row(mdb)) != NULL) {
572             id[i++] = str_to_uint64(row[0]);
573          }
574          *ids = id;
575       }
576       sql_free_result(mdb);
577       stat = 1;
578    } else {
579       Mmsg(mdb->errmsg, _("Client id select failed: ERR=%s\n"), sql_strerror(mdb));
580       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
581       stat = 0;
582    }
583    db_unlock(mdb);
584    return stat;
585 }
586
587
588
589 /* Get Pool Record
590  * If the PoolId is non-zero, we get its record,
591  *  otherwise, we search on the PoolName
592  *
593  * Returns: false on failure
594  *          true on success
595  */
596 bool db_get_pool_record(JCR *jcr, B_DB *mdb, POOL_DBR *pdbr)
597 {
598    SQL_ROW row;
599    bool ok = false;
600    char ed1[50];
601
602    db_lock(mdb);
603    if (pdbr->PoolId != 0) {               /* find by id */
604       Mmsg(mdb->cmd,
605 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
606 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
607 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId FROM Pool WHERE Pool.PoolId=%s", 
608          edit_int64(pdbr->PoolId, ed1));
609    } else {                           /* find by name */
610       Mmsg(mdb->cmd,
611 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
612 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
613 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId FROM Pool WHERE Pool.Name='%s'", 
614          pdbr->Name);
615    }
616
617    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
618       mdb->num_rows = sql_num_rows(mdb);
619       if (mdb->num_rows > 1) {
620          char ed1[30];
621          Mmsg1(mdb->errmsg, _("More than one Pool!: %s\n"),
622             edit_uint64(mdb->num_rows, ed1));
623          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
624       } else if (mdb->num_rows == 1) {
625          if ((row = sql_fetch_row(mdb)) == NULL) {
626             Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
627             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
628          } else {
629             pdbr->PoolId = str_to_int64(row[0]);
630             bstrncpy(pdbr->Name, row[1]!=NULL?row[1]:"", sizeof(pdbr->Name));
631             pdbr->NumVols = str_to_int64(row[2]);
632             pdbr->MaxVols = str_to_int64(row[3]);
633             pdbr->UseOnce = str_to_int64(row[4]);
634             pdbr->UseCatalog = str_to_int64(row[5]);
635             pdbr->AcceptAnyVolume = str_to_int64(row[6]);
636             pdbr->AutoPrune = str_to_int64(row[7]);
637             pdbr->Recycle = str_to_int64(row[8]);
638             pdbr->VolRetention = str_to_int64(row[9]);
639             pdbr->VolUseDuration = str_to_int64(row[10]);
640             pdbr->MaxVolJobs = str_to_int64(row[11]);
641             pdbr->MaxVolFiles = str_to_int64(row[12]);
642             pdbr->MaxVolBytes = str_to_uint64(row[13]);
643             bstrncpy(pdbr->PoolType, row[14]!=NULL?row[14]:"", sizeof(pdbr->PoolType));
644             pdbr->LabelType = str_to_int64(row[15]);
645             bstrncpy(pdbr->LabelFormat, row[16]!=NULL?row[16]:"", sizeof(pdbr->LabelFormat));
646             pdbr->RecyclePoolId = str_to_int64(row[17]);
647             ok = true;
648          }
649       }
650       sql_free_result(mdb);
651    }
652    if (ok) {
653       uint32_t NumVols;
654       Mmsg(mdb->cmd, "SELECT count(*) from Media WHERE PoolId=%s",
655          edit_int64(pdbr->PoolId, ed1));
656       NumVols = get_sql_record_max(jcr, mdb);
657       Dmsg2(400, "Actual NumVols=%d Pool NumVols=%d\n", NumVols, pdbr->NumVols);
658       if (NumVols != pdbr->NumVols) {
659          pdbr->NumVols = NumVols;
660          db_update_pool_record(jcr, mdb, pdbr);
661       }
662    } else {
663       Mmsg(mdb->errmsg, _("Pool record not found in Catalog.\n"));
664    }
665    db_unlock(mdb);
666    return ok;
667 }
668
669 /* Get Client Record
670  * If the ClientId is non-zero, we get its record,
671  *  otherwise, we search on the Client Name
672  *
673  * Returns: 0 on failure
674  *          1 on success
675  */
676 int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cdbr)
677 {
678    SQL_ROW row;
679    int stat = 0;
680    char ed1[50];
681
682    db_lock(mdb);
683    if (cdbr->ClientId != 0) {               /* find by id */
684       Mmsg(mdb->cmd,
685 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
686 "FROM Client WHERE Client.ClientId=%s", 
687         edit_int64(cdbr->ClientId, ed1));
688    } else {                           /* find by name */
689       Mmsg(mdb->cmd,
690 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
691 "FROM Client WHERE Client.Name='%s'", cdbr->Name);
692    }
693
694    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
695       mdb->num_rows = sql_num_rows(mdb);
696       if (mdb->num_rows > 1) {
697          Mmsg1(mdb->errmsg, _("More than one Client!: %s\n"),
698             edit_uint64(mdb->num_rows, ed1));
699          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
700       } else if (mdb->num_rows == 1) {
701          if ((row = sql_fetch_row(mdb)) == NULL) {
702             Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
703             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
704          } else {
705             cdbr->ClientId = str_to_int64(row[0]);
706             bstrncpy(cdbr->Name, row[1]!=NULL?row[1]:"", sizeof(cdbr->Name));
707             bstrncpy(cdbr->Uname, row[2]!=NULL?row[2]:"", sizeof(cdbr->Uname));
708             cdbr->AutoPrune = str_to_int64(row[3]);
709             cdbr->FileRetention = str_to_int64(row[4]);
710             cdbr->JobRetention = str_to_int64(row[5]);
711             stat = 1;
712          }
713       } else {
714          Mmsg(mdb->errmsg, _("Client record not found in Catalog.\n"));
715       }
716       sql_free_result(mdb);
717    } else {
718       Mmsg(mdb->errmsg, _("Client record not found in Catalog.\n"));
719    }
720    db_unlock(mdb);
721    return stat;
722 }
723
724 /*
725  * Get Counter Record
726  *
727  * Returns: 0 on failure
728  *          1 on success
729  */
730 int db_get_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr)
731 {
732    SQL_ROW row;
733
734    db_lock(mdb);
735    Mmsg(mdb->cmd, "SELECT MinValue,MaxValue,CurrentValue,WrapCounter "
736       "FROM Counters WHERE Counter='%s'", cr->Counter);
737
738    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
739       mdb->num_rows = sql_num_rows(mdb);
740
741       /* If more than one, report error, but return first row */
742       if (mdb->num_rows > 1) {
743          Mmsg1(mdb->errmsg, _("More than one Counter!: %d\n"), (int)(mdb->num_rows));
744          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
745       }
746       if (mdb->num_rows >= 1) {
747          if ((row = sql_fetch_row(mdb)) == NULL) {
748             Mmsg1(mdb->errmsg, _("error fetching Counter row: %s\n"), sql_strerror(mdb));
749             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
750             sql_free_result(mdb);
751             db_unlock(mdb);
752             return 0;
753          }
754          cr->MinValue = str_to_int64(row[0]);
755          cr->MaxValue = str_to_int64(row[1]);
756          cr->CurrentValue = str_to_int64(row[2]);
757          if (row[3]) {
758             bstrncpy(cr->WrapCounter, row[3], sizeof(cr->WrapCounter));
759          } else {
760             cr->WrapCounter[0] = 0;
761          }
762          sql_free_result(mdb);
763          db_unlock(mdb);
764          return 1;
765       }
766       sql_free_result(mdb);
767    } else {
768       Mmsg(mdb->errmsg, _("Counter record: %s not found in Catalog.\n"), cr->Counter);
769    }
770    db_unlock(mdb);
771    return 0;
772 }
773
774
775 /* Get FileSet Record
776  * If the FileSetId is non-zero, we get its record,
777  *  otherwise, we search on the name
778  *
779  * Returns: 0 on failure
780  *          id on success
781  */
782 int db_get_fileset_record(JCR *jcr, B_DB *mdb, FILESET_DBR *fsr)
783 {
784    SQL_ROW row;
785    int stat = 0;
786    char ed1[50];
787
788    db_lock(mdb);
789    if (fsr->FileSetId != 0) {               /* find by id */
790       Mmsg(mdb->cmd,
791            "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
792            "WHERE FileSetId=%s", 
793            edit_int64(fsr->FileSetId, ed1));
794    } else {                           /* find by name */
795       Mmsg(mdb->cmd,
796            "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
797            "WHERE FileSet='%s' ORDER BY CreateTime DESC LIMIT 1", fsr->FileSet);
798    }
799
800    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
801       mdb->num_rows = sql_num_rows(mdb);
802       if (mdb->num_rows > 1) {
803          char ed1[30];
804          Mmsg1(mdb->errmsg, _("Error got %s FileSets but expected only one!\n"),
805             edit_uint64(mdb->num_rows, ed1));
806          sql_data_seek(mdb, mdb->num_rows-1);
807       }
808       if ((row = sql_fetch_row(mdb)) == NULL) {
809          Mmsg1(mdb->errmsg, _("FileSet record \"%s\" not found.\n"), fsr->FileSet);
810       } else {
811          fsr->FileSetId = str_to_int64(row[0]);
812          bstrncpy(fsr->FileSet, row[1]!=NULL?row[1]:"", sizeof(fsr->FileSet));
813          bstrncpy(fsr->MD5, row[2]!=NULL?row[2]:"", sizeof(fsr->MD5));
814          bstrncpy(fsr->cCreateTime, row[3]!=NULL?row[3]:"", sizeof(fsr->cCreateTime));
815          stat = fsr->FileSetId;
816       }
817       sql_free_result(mdb);
818    } else {
819       Mmsg(mdb->errmsg, _("FileSet record not found in Catalog.\n"));
820    }
821    db_unlock(mdb);
822    return stat;
823 }
824
825
826 /*
827  * Get the number of Media records
828  *
829  * Returns: -1 on failure
830  *          number on success
831  */
832 int db_get_num_media_records(JCR *jcr, B_DB *mdb)
833 {
834    int stat = 0;
835
836    db_lock(mdb);
837    Mmsg(mdb->cmd, "SELECT count(*) from Media");
838    stat = get_sql_record_max(jcr, mdb);
839    db_unlock(mdb);
840    return stat;
841 }
842
843
844 /*
845  * This function returns a list of all the Media record ids for
846  *     the current Pool with the correct Media Type.
847  *  The caller must free ids if non-NULL.
848  *
849  *  Returns false: on failure
850  *          true:  on success
851  */
852 bool db_get_media_ids(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr, int *num_ids, uint32_t *ids[])
853 {
854    SQL_ROW row;
855    int i = 0;
856    uint32_t *id;
857    char ed1[50];
858    bool ok = false;
859
860    db_lock(mdb);
861    *ids = NULL;
862    Mmsg(mdb->cmd, "SELECT DISTINCT MediaId FROM Media WHERE PoolId=%s "
863          " AND MediaType='%s'",
864        edit_int64(mr->PoolId, ed1), mr->MediaType);
865    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
866       *num_ids = sql_num_rows(mdb);
867       if (*num_ids > 0) {
868          id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
869          while ((row = sql_fetch_row(mdb)) != NULL) {
870             id[i++] = str_to_uint64(row[0]);
871          }
872          *ids = id;
873       }
874       sql_free_result(mdb);
875       ok = true;
876    } else {
877       Mmsg(mdb->errmsg, _("Media id select failed: ERR=%s\n"), sql_strerror(mdb));
878       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
879       ok = false;
880    }
881    db_unlock(mdb);
882    return ok;
883 }
884
885
886 /*
887  * This function returns a list of all the DBIds that are returned
888  *   for the query.
889  *
890  *  Returns false: on failure
891  *          true:  on success
892  */
893 bool db_get_query_dbids(JCR *jcr, B_DB *mdb, POOL_MEM &query, dbid_list &ids)
894 {
895    SQL_ROW row;
896    int i = 0;
897    bool ok = false;
898
899    db_lock(mdb);
900    ids.num_ids = 0;
901    if (QUERY_DB(jcr, mdb, query.c_str())) {
902       ids.num_ids = sql_num_rows(mdb);
903       if (ids.num_ids > 0) {
904          if (ids.max_ids < ids.num_ids) {
905             free(ids.DBId);
906             ids.DBId = (DBId_t *)malloc(ids.num_ids * sizeof(DBId_t));
907          }
908          while ((row = sql_fetch_row(mdb)) != NULL) {
909             ids.DBId[i++] = str_to_uint64(row[0]);
910          }
911       }
912       sql_free_result(mdb);
913       ok = true;
914    } else {
915       Mmsg(mdb->errmsg, _("query dbids failed: ERR=%s\n"), sql_strerror(mdb));
916       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
917       ok = false;
918    }
919    db_unlock(mdb);
920    return ok;
921 }
922
923 /* Get Media Record
924  *
925  * Returns: false: on failure
926  *          true:  on success
927  */
928 bool db_get_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr)
929 {
930    SQL_ROW row;
931    char ed1[50];
932    bool ok = false;
933
934    db_lock(mdb);
935    if (mr->MediaId == 0 && mr->VolumeName[0] == 0) {
936       Mmsg(mdb->cmd, "SELECT count(*) from Media");
937       mr->MediaId = get_sql_record_max(jcr, mdb);
938       db_unlock(mdb);
939       return true;
940    }
941    if (mr->MediaId != 0) {               /* find by id */
942       Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
943          "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
944          "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
945          "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
946          "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
947          "Enabled,LocationId,RecycleCount,InitialWrite,"
948          "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime "
949          "FROM Media WHERE MediaId=%s", 
950          edit_int64(mr->MediaId, ed1));
951    } else {                           /* find by name */
952       Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
953          "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
954          "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
955          "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
956          "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
957          "Enabled,LocationId,RecycleCount,InitialWrite,"
958          "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime "
959          "FROM Media WHERE VolumeName='%s'", mr->VolumeName);
960    }
961
962    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
963       char ed1[50];
964       mdb->num_rows = sql_num_rows(mdb);
965       if (mdb->num_rows > 1) {
966          Mmsg1(mdb->errmsg, _("More than one Volume!: %s\n"),
967             edit_uint64(mdb->num_rows, ed1));
968          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
969       } else if (mdb->num_rows == 1) {
970          if ((row = sql_fetch_row(mdb)) == NULL) {
971             Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
972             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
973          } else {
974             /* return values */
975             mr->MediaId = str_to_int64(row[0]);
976             bstrncpy(mr->VolumeName, row[1]!=NULL?row[1]:"", sizeof(mr->VolumeName));
977             mr->VolJobs = str_to_int64(row[2]);
978             mr->VolFiles = str_to_int64(row[3]);
979             mr->VolBlocks = str_to_int64(row[4]);
980             mr->VolBytes = str_to_uint64(row[5]);
981             mr->VolMounts = str_to_int64(row[6]);
982             mr->VolErrors = str_to_int64(row[7]);
983             mr->VolWrites = str_to_int64(row[8]);
984             mr->MaxVolBytes = str_to_uint64(row[9]);
985             mr->VolCapacityBytes = str_to_uint64(row[10]);
986             bstrncpy(mr->MediaType, row[11]!=NULL?row[11]:"", sizeof(mr->MediaType));
987             bstrncpy(mr->VolStatus, row[12]!=NULL?row[12]:"", sizeof(mr->VolStatus));
988             mr->PoolId = str_to_int64(row[13]);
989             mr->VolRetention = str_to_uint64(row[14]);
990             mr->VolUseDuration = str_to_uint64(row[15]);
991             mr->MaxVolJobs = str_to_int64(row[16]);
992             mr->MaxVolFiles = str_to_int64(row[17]);
993             mr->Recycle = str_to_int64(row[18]);
994             mr->Slot = str_to_int64(row[19]);
995             bstrncpy(mr->cFirstWritten, row[20]!=NULL?row[20]:"", sizeof(mr->cFirstWritten));
996             mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten);
997             bstrncpy(mr->cLastWritten, row[21]!=NULL?row[21]:"", sizeof(mr->cLastWritten));
998             mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
999             mr->InChanger = str_to_uint64(row[22]);
1000             mr->EndFile = str_to_uint64(row[23]);
1001             mr->EndBlock = str_to_uint64(row[24]);
1002             mr->VolParts = str_to_int64(row[25]);
1003             mr->LabelType = str_to_int64(row[26]);
1004             bstrncpy(mr->cLabelDate, row[27]!=NULL?row[27]:"", sizeof(mr->cLabelDate));
1005             mr->LabelDate = (time_t)str_to_utime(mr->cLabelDate);
1006             mr->StorageId = str_to_int64(row[28]);
1007             mr->Enabled = str_to_int64(row[29]);
1008             mr->LocationId = str_to_int64(row[30]);
1009             mr->RecycleCount = str_to_int64(row[31]);
1010             bstrncpy(mr->cInitialWrite, row[32]!=NULL?row[32]:"", sizeof(mr->cInitialWrite));
1011             mr->InitialWrite = (time_t)str_to_utime(mr->cInitialWrite);
1012             mr->ScratchPoolId = str_to_int64(row[33]);
1013             mr->RecyclePoolId = str_to_int64(row[34]);
1014             mr->VolReadTime = str_to_int64(row[35]);
1015             mr->VolWriteTime = str_to_int64(row[36]);
1016             
1017             ok = true;
1018          }
1019       } else {
1020          if (mr->MediaId != 0) {
1021             Mmsg1(mdb->errmsg, _("Media record MediaId=%s not found.\n"), 
1022                edit_int64(mr->MediaId, ed1));
1023          } else {
1024             Mmsg1(mdb->errmsg, _("Media record for Volume \"%s\" not found.\n"),
1025                   mr->VolumeName);
1026          }
1027       }
1028       sql_free_result(mdb);
1029    } else {
1030       if (mr->MediaId != 0) {
1031          Mmsg(mdb->errmsg, _("Media record for MediaId=%u not found in Catalog.\n"),
1032             mr->MediaId);
1033        } else {
1034          Mmsg(mdb->errmsg, _("Media record for Vol=%s not found in Catalog.\n"),
1035             mr->VolumeName);
1036    }   }
1037    db_unlock(mdb);
1038    return ok;
1039 }
1040
1041 /*
1042  * Find the last "accurate" backup state (that can take deleted files in account)
1043  * 1) Get all files with jobid in list (F subquery) 
1044  * 2) Take only the last version of each file (Temp subquery) => accurate list is ok
1045  * 3) Join the result to file table to get fileindex, jobid and lstat information
1046  *
1047  * TODO: See if we can do the SORT only if needed (as an argument)
1048  */
1049 bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids, 
1050                       DB_RESULT_HANDLER *result_handler, void *ctx)
1051 {
1052    if (!*jobids) {
1053       db_lock(mdb);
1054       Mmsg(mdb->errmsg, _("ERR=JobIds are empty\n"));
1055       db_unlock(mdb);
1056       return false;
1057    }
1058    POOL_MEM buf(PM_MESSAGE);
1059          
1060 #define new_db_get_file_list
1061 #ifdef new_db_get_file_list
1062    /* This is broken, at least if called from ua_restore.c */
1063    Mmsg(buf,
1064  "SELECT Path.Path, Filename.Name, File.FileIndex, File.JobId, File.LStat "
1065  "FROM ( "
1066   "SELECT max(FileId) as FileId, PathId, FilenameId "
1067     "FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (%s)) AS F "
1068    "GROUP BY PathId, FilenameId "
1069   ") AS Temp "
1070  "JOIN Filename ON (Filename.FilenameId = Temp.FilenameId) "
1071  "JOIN Path ON (Path.PathId = Temp.PathId) "
1072  "JOIN File ON (File.FileId = Temp.FileId) "
1073 "WHERE File.FileIndex > 0 ORDER BY JobId, FileIndex ASC",     /* Return sorted by JobId, */
1074                                                               /* FileIndex for restore code */ 
1075              jobids);
1076 #else
1077    /*  
1078     * I am not sure that this works the same as the code in ua_restore.c
1079     *  but it is very similar.
1080     */
1081    Mmsg(buf, uar_sel_files, jobids);
1082 #endif
1083
1084    return db_sql_query(mdb, buf.c_str(), result_handler, ctx);
1085 }
1086
1087 /* The decision do change an incr/diff was done before
1088  * Full : do nothing
1089  * Differential : get the last full id
1090  * Incremental : get the last full + last diff + last incr(s) ids
1091  *
1092  * TODO: look and merge from ua_restore.c
1093  */
1094 bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb, 
1095                             JOB_DBR *jr, POOLMEM *jobids)
1096 {
1097    bool ret=false;
1098    char clientid[50], jobid[50], filesetid[50];
1099    char date[MAX_TIME_LENGTH];
1100    POOL_MEM query(PM_FNAME);
1101    bstrutime(date, sizeof(date),  time(NULL) + 1);
1102    jobids[0]='\0';
1103
1104    /* First, find the last good Full backup for this job/client/fileset */
1105    Mmsg(query, 
1106 "CREATE TABLE btemp3%s AS "
1107  "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1108    "FROM Job JOIN FileSet USING (FileSetId) "
1109   "WHERE ClientId = %s "
1110     "AND Level='F' AND JobStatus='T' AND Type='B' "
1111     "AND StartTime<'%s' "
1112     "AND FileSet.FileSet=(SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1113   "ORDER BY Job.JobTDate DESC LIMIT 1",
1114         edit_uint64(jcr->JobId, jobid),
1115         edit_uint64(jr->ClientId, clientid),
1116         date,
1117         edit_uint64(jr->FileSetId, filesetid));
1118
1119    if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
1120       goto bail_out;
1121    }
1122
1123    if (jr->JobLevel == L_INCREMENTAL || jr->JobLevel == L_VIRTUAL_FULL) {
1124       /* Now, find the last differential backup after the last full */
1125       Mmsg(query, 
1126 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
1127  "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1128    "FROM Job JOIN FileSet USING (FileSetId) "
1129   "WHERE ClientId = %s "
1130     "AND Level='D' AND JobStatus='T' AND Type='B' "
1131     "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
1132     "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1133   "ORDER BY Job.JobTDate DESC LIMIT 1 ",
1134            jobid,
1135            clientid,
1136            jobid,
1137            filesetid);
1138
1139       if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
1140          goto bail_out;
1141       }
1142
1143       /* We just have to take all incremental after the last Full/Diff */
1144       Mmsg(query, 
1145 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
1146  "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1147    "FROM Job JOIN FileSet USING (FileSetId) "
1148   "WHERE ClientId = %s "
1149     "AND Level='I' AND JobStatus='T' AND Type='B' "
1150     "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
1151     "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1152   "ORDER BY Job.JobTDate DESC ",
1153            jobid,
1154            clientid,
1155            jobid,
1156            filesetid);
1157       if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
1158          goto bail_out;
1159       }
1160    }
1161
1162    /* build a jobid list ie: 1,2,3,4 */
1163    Mmsg(query, "SELECT JobId FROM btemp3%s ORDER by JobTDate", jobid);
1164    db_sql_query(mdb, query.c_str(), db_get_int_handler, jobids);
1165    Dmsg1(1, "db_accurate_get_jobids=%s\n", jobids);
1166    ret = true;
1167
1168 bail_out:
1169    Mmsg(query, "DROP TABLE btemp3%s", jobid);
1170    db_sql_query(mdb, query.c_str(), NULL, NULL);
1171
1172    return ret;
1173 }
1174
1175 /*
1176  * Use to build a string of int list from a query. "10,20,30"
1177  */
1178 int db_get_int_handler(void *ctx, int num_fields, char **row)
1179 {
1180    POOLMEM *ret = (POOLMEM *)ctx;
1181    if (num_fields == 1) {
1182       if (ret[0]) {
1183          pm_strcat(ret, ",");
1184       }
1185       pm_strcat(ret, row[0]);
1186    }
1187    return 0;
1188 }
1189
1190 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI */