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