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