]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql_get.c
Big backport from Enterprise
[bacula/bacula] / bacula / src / cats / sql_get.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many 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    This notice must be preserved when any source code is 
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /**
20  * Bacula Catalog Database Get record interface routines
21  *  Note, these routines generally get a record by id or
22  *        by name.  If more logic is involved, the routine
23  *        should be in find.c
24  *
25  *    Written by Kern Sibbald, March 2000
26  */
27
28 #include  "bacula.h"
29
30 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
31
32 #include  "cats.h"
33
34 /* -----------------------------------------------------------------------
35  *
36  *   Generic Routines (or almost generic)
37  *
38  * -----------------------------------------------------------------------
39  */
40
41 /*
42  * Given a full filename (with path), look up the File record
43  * (with attributes) in the database.
44  *
45  *  Returns: false on failure
46  *           true on success with the File record in FILE_DBR
47  */
48 bool BDB::bdb_get_file_attributes_record(JCR *jcr, char *afname, JOB_DBR *jr, FILE_DBR *fdbr)
49 {
50    bool ok;
51
52    Dmsg1(500, "db_get_file_att_record fname=%s \n", afname);
53
54    bdb_lock();
55
56    split_path_and_file(jcr, this, afname);
57
58    fdbr->FilenameId = bdb_get_filename_record(jcr);
59
60    fdbr->PathId = bdb_get_path_record(jcr);
61
62    ok = bdb_get_file_record(jcr, jr, fdbr);
63
64    bdb_unlock();
65
66    return ok;
67 }
68
69
70 /*
71  * Get a File record
72  *
73  *  DO NOT use Jmsg in this routine.
74  *
75  *  Note in this routine, we do not use Jmsg because it may be
76  *    called to get attributes of a non-existent file, which is
77  *    "normal" if a new file is found during Verify.
78  *
79  *  The following is a bit of a kludge: because we always backup a
80  *    directory entry, we can end up with two copies of the directory
81  *    in the backup. One is when we encounter the directory and find
82  *    we cannot recurse into it, and the other is when we find an
83  *    explicit mention of the directory. This can also happen if the
84  *    use includes the directory twice.  In this case, Verify
85  *    VolumeToCatalog fails because we have two copies in the catalog, and
86  *    only the first one is marked (twice).  So, when calling from Verify,
87  *    VolumeToCatalog jr is not NULL, and we know jr->FileIndex is the fileindex
88  *    of the version of the directory/file we actually want and do
89  *    a more explicit SQL search.
90  *
91  * Returns: false on failure
92  *          true  on success
93  *
94  */
95 bool BDB::bdb_get_file_record(JCR *jcr, JOB_DBR *jr, FILE_DBR *fdbr)
96 {
97    SQL_ROW row;
98    bool ok = false;
99    char ed1[50], ed2[50], ed3[50];
100
101    switch (jcr->getJobLevel()) {
102    case L_VERIFY_VOLUME_TO_CATALOG:
103       Mmsg(cmd,
104 "SELECT FileId, LStat, MD5 FROM File WHERE File.JobId=%s AND File.PathId=%s AND "
105 "File.FilenameId=%s AND File.FileIndex=%u",
106       edit_int64(fdbr->JobId, ed1),
107       edit_int64(fdbr->PathId, ed2),
108       edit_int64(fdbr->FilenameId,ed3),
109       jr->FileIndex);
110       break;
111    case L_VERIFY_DISK_TO_CATALOG:
112       Mmsg(cmd,
113 "SELECT FileId, LStat, MD5 FROM File,Job WHERE "
114 "File.JobId=Job.JobId AND File.PathId=%s AND "
115 "File.FilenameId=%s AND Job.Type='B' AND Job.JobStatus IN ('T','W') AND "
116 "ClientId=%s ORDER BY StartTime DESC LIMIT 1",
117       edit_int64(fdbr->PathId, ed1), 
118       edit_int64(fdbr->FilenameId, ed2), 
119       edit_int64(jr->ClientId,ed3));
120       break;
121    default:
122       Mmsg(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       break;
129    }
130
131    Dmsg3(450, "Get_file_record JobId=%u FilenameId=%u PathId=%u\n",
132       fdbr->JobId, fdbr->FilenameId, fdbr->PathId);
133
134    Dmsg1(100, "Query=%s\n", cmd);
135
136    if (QueryDB(jcr, cmd)) {
137       Dmsg1(100, "get_file_record sql_num_rows()=%d\n", sql_num_rows());
138       if (sql_num_rows() >= 1) {
139          if ((row = sql_fetch_row()) == NULL) {
140             Mmsg1(errmsg, _("Error fetching row: %s\n"), sql_strerror());
141          } else {
142             fdbr->FileId = (FileId_t)str_to_int64(row[0]);
143             bstrncpy(fdbr->LStat, row[1], sizeof(fdbr->LStat));
144             bstrncpy(fdbr->Digest, row[2], sizeof(fdbr->Digest));
145             ok = true;
146             if (sql_num_rows() > 1) {
147                Mmsg3(errmsg, _("get_file_record want 1 got rows=%d PathId=%s FilenameId=%s\n"),
148                   sql_num_rows(), 
149                   edit_int64(fdbr->PathId, ed1), 
150                   edit_int64(fdbr->FilenameId, ed2));
151                Dmsg1(000, "=== Problem!  %s", errmsg);
152             }
153          }
154       } else {
155          Mmsg2(errmsg, _("File record for PathId=%s FilenameId=%s not found.\n"),
156             edit_int64(fdbr->PathId, ed1), 
157             edit_int64(fdbr->FilenameId, ed2));
158       }
159       sql_free_result();
160    } else {
161       Mmsg(errmsg, _("File record not found in Catalog.\n"));
162    }
163    return ok;
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 int BDB::bdb_get_filename_record(JCR *jcr)
174 {
175    SQL_ROW row;
176    int FilenameId = 0;
177
178    esc_name = check_pool_memory_size(esc_name, 2*fnl+2);
179    bdb_escape_string(jcr, esc_name, fname, fnl);
180
181    Mmsg(cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
182    if (QueryDB(jcr, cmd)) {
183       char ed1[30];
184       if (sql_num_rows() > 1) { 
185          Mmsg2(errmsg, _("More than one Filename!: %s for file: %s\n"),
186             edit_uint64(sql_num_rows(), ed1), fname);
187          Jmsg(jcr, M_WARNING, 0, "%s", errmsg);
188       }
189       if (sql_num_rows() >= 1) { 
190          if ((row = sql_fetch_row()) == NULL) {
191             Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
192          } else {
193             FilenameId = str_to_int64(row[0]);
194             if (FilenameId <= 0) {
195                Mmsg2(errmsg, _("Get DB Filename record %s found bad record: %d\n"),
196                   cmd, FilenameId);
197                FilenameId = 0;
198             }
199          }
200       } else {
201          Mmsg1(errmsg, _("Filename record: %s not found.\n"), fname);
202       }
203       sql_free_result();
204    } else {
205       Mmsg(errmsg, _("Filename record: %s not found in Catalog.\n"), fname);
206    }
207    return FilenameId;
208 }
209
210 /**
211  * Get path record
212  * Returns: 0 on failure
213  *          PathId on success
214  *
215  *   DO NOT use Jmsg in this routine (see notes for get_file_record)
216  */
217 int BDB::bdb_get_path_record(JCR *jcr)
218 {
219    SQL_ROW row;
220    uint32_t PathId = 0;
221
222    esc_name = check_pool_memory_size(esc_name, 2*pnl+2);
223    bdb_escape_string(jcr, esc_name, path, pnl);
224
225    if (cached_path_id != 0 && cached_path_len == pnl &&
226        strcmp(cached_path, path) == 0) {
227       return cached_path_id;
228    }
229
230    Mmsg(cmd, "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
231
232    if (QueryDB(jcr, cmd)) {
233       char ed1[30];
234       if (sql_num_rows() > 1) {
235          Mmsg2(errmsg, _("More than one Path!: %s for path: %s\n"),
236             edit_uint64(sql_num_rows(), ed1), path);
237          Jmsg(jcr, M_WARNING, 0, "%s", errmsg);
238       }
239       /* Even if there are multiple paths, take the first one */
240       if (sql_num_rows() >= 1) {
241          if ((row = sql_fetch_row()) == NULL) {
242             Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
243          } else {
244             PathId = str_to_int64(row[0]);
245             if (PathId <= 0) {
246                Mmsg2(errmsg, _("Get DB path record %s found bad record: %s\n"),
247                   cmd, edit_int64(PathId, ed1));
248                PathId = 0;
249             } else {
250                /* Cache path */
251                if (PathId != cached_path_id) {
252                   cached_path_id = PathId;
253                   cached_path_len = pnl;
254                   pm_strcpy(cached_path, path);
255                }
256             }
257          }
258       } else {
259          Mmsg1(errmsg, _("Path record: %s not found.\n"), path);
260       }
261       sql_free_result();
262    } else {
263       Mmsg(errmsg, _("Path record: %s not found in Catalog.\n"), path);
264    }
265    return PathId;
266 }
267
268
269 /**
270  * Get Job record for given JobId or Job name
271  * Returns: false on failure
272  *          true  on success
273  */
274 bool BDB::bdb_get_job_record(JCR *jcr, JOB_DBR *jr)
275 {
276    SQL_ROW row;
277    char ed1[50];
278    char esc[MAX_ESCAPE_NAME_LENGTH];
279
280    bdb_lock();
281    if (jr->JobId == 0) {
282       bdb_escape_string(jcr, esc, jr->Job, strlen(jr->Job));
283       Mmsg(cmd, "SELECT VolSessionId,VolSessionTime,"
284 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
285 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
286 "SchedTime,RealEndTime,ReadBytes,HasBase,PurgedFiles "
287 "FROM Job WHERE Job='%s'", esc);
288     } else {
289       Mmsg(cmd, "SELECT VolSessionId,VolSessionTime,"
290 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
291 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
292 "SchedTime,RealEndTime,ReadBytes,HasBase,PurgedFiles "
293 "FROM Job WHERE JobId=%s",
294           edit_int64(jr->JobId, ed1));
295     }
296
297    if (!QueryDB(jcr, cmd)) {
298       bdb_unlock();
299       return false;                   /* failed */
300    }
301    if ((row = sql_fetch_row()) == NULL) {
302       Mmsg1(errmsg, _("No Job found for JobId %s\n"), edit_int64(jr->JobId, ed1));
303       sql_free_result();
304       bdb_unlock();
305       return false;                   /* failed */
306    }
307
308    jr->VolSessionId = str_to_uint64(row[0]);
309    jr->VolSessionTime = str_to_uint64(row[1]);
310    jr->PoolId = str_to_int64(row[2]);
311    bstrncpy(jr->cStartTime, row[3]!=NULL?row[3]:"", sizeof(jr->cStartTime));
312    bstrncpy(jr->cEndTime, row[4]!=NULL?row[4]:"", sizeof(jr->cEndTime));
313    jr->JobFiles = str_to_int64(row[5]);
314    jr->JobBytes = str_to_int64(row[6]);
315    jr->JobTDate = str_to_int64(row[7]);
316    bstrncpy(jr->Job, row[8]!=NULL?row[8]:"", sizeof(jr->Job));
317    jr->JobStatus = row[9]!=NULL?(int)*row[9]:JS_FatalError;
318    jr->JobType = row[10]!=NULL?(int)*row[10]:JT_BACKUP;
319    jr->JobLevel = row[11]!=NULL?(int)*row[11]:L_NONE;
320    jr->ClientId = str_to_uint64(row[12]!=NULL?row[12]:(char *)"");
321    bstrncpy(jr->Name, row[13]!=NULL?row[13]:"", sizeof(jr->Name));
322    jr->PriorJobId = str_to_uint64(row[14]!=NULL?row[14]:(char *)"");
323    bstrncpy(jr->cRealEndTime, row[15]!=NULL?row[15]:"", sizeof(jr->cRealEndTime));
324    if (jr->JobId == 0) {
325       jr->JobId = str_to_int64(row[16]);
326    }
327    jr->FileSetId = str_to_int64(row[17]);
328    bstrncpy(jr->cSchedTime, row[18]!=NULL?row[18]:"", sizeof(jr->cSchedTime));
329    bstrncpy(jr->cRealEndTime, row[19]!=NULL?row[19]:"", sizeof(jr->cRealEndTime));
330    jr->ReadBytes = str_to_int64(row[20]);
331    jr->StartTime = str_to_utime(jr->cStartTime);
332    jr->SchedTime = str_to_utime(jr->cSchedTime);
333    jr->EndTime = str_to_utime(jr->cEndTime);
334    jr->RealEndTime = str_to_utime(jr->cRealEndTime);
335    jr->HasBase = str_to_int64(row[21]);
336    jr->PurgedFiles = str_to_int64(row[22]);
337    sql_free_result();
338
339    bdb_unlock();
340    return true;
341 }
342
343 /**
344  * Find VolumeNames for a given JobId
345  *  Returns: 0 on error or no Volumes found
346  *           number of volumes on success
347  *              Volumes are concatenated in VolumeNames
348  *              separated by a vertical bar (|) in the order
349  *              that they were written.
350  *
351  *  Returns: number of volumes on success
352  */
353 int BDB::bdb_get_job_volume_names(JCR *jcr, JobId_t JobId, POOLMEM **VolumeNames)
354 {
355    SQL_ROW row;
356    char ed1[50];
357    int stat = 0;
358    int i;
359
360    bdb_lock();
361    /* Get one entry per VolumeName, but "sort" by VolIndex */
362    Mmsg(cmd,
363         "SELECT VolumeName,MAX(VolIndex) FROM JobMedia,Media WHERE "
364         "JobMedia.JobId=%s AND JobMedia.MediaId=Media.MediaId "
365         "GROUP BY VolumeName "
366         "ORDER BY 2 ASC", edit_int64(JobId,ed1));
367
368    Dmsg1(130, "VolNam=%s\n", cmd);
369    *VolumeNames[0] = 0;
370    if (QueryDB(jcr, cmd)) {
371       Dmsg1(130, "Num rows=%d\n", sql_num_rows());
372       if (sql_num_rows() <= 0) {
373          Mmsg1(errmsg, _("No volumes found for JobId=%d\n"), JobId);
374          stat = 0;
375       } else {
376          stat = sql_num_rows();
377          for (i=0; i < stat; i++) {
378             if ((row = sql_fetch_row()) == NULL) {
379                Mmsg2(errmsg, _("Error fetching row %d: ERR=%s\n"), i, sql_strerror());
380                Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
381                stat = 0;
382                break;
383             } else {
384                if (*VolumeNames[0] != 0) {
385                   pm_strcat(VolumeNames, "|");
386                }
387                pm_strcat(VolumeNames, row[0]);
388             }
389          }
390       }
391       sql_free_result();
392    } else {
393       Mmsg(errmsg, _("No Volume for JobId %d found in Catalog.\n"), JobId);
394    }
395    bdb_unlock();
396    return stat;
397 }
398
399 /**
400  * Find Volume parameters for a give JobId
401  *  Returns: 0 on error or no Volumes found
402  *           number of volumes on success
403  *           List of Volumes and start/end file/blocks (malloced structure!)
404  *
405  *  Returns: number of volumes on success
406  */
407 int BDB::bdb_get_job_volume_parameters(JCR *jcr, JobId_t JobId, VOL_PARAMS **VolParams)
408 {
409    SQL_ROW row;
410    char ed1[50];
411    int stat = 0;
412    int i;
413    VOL_PARAMS *Vols = NULL;
414
415    bdb_lock();
416    Mmsg(cmd,
417 "SELECT VolumeName,MediaType,FirstIndex,LastIndex,StartFile,"
418 "JobMedia.EndFile,StartBlock,JobMedia.EndBlock,"
419 "Slot,StorageId,InChanger"
420 " FROM JobMedia,Media WHERE JobMedia.JobId=%s"
421 " AND JobMedia.MediaId=Media.MediaId ORDER BY VolIndex,JobMediaId",
422         edit_int64(JobId, ed1));
423
424    Dmsg1(130, "VolNam=%s\n", cmd);
425    if (QueryDB(jcr, cmd)) {
426       Dmsg1(200, "Num rows=%d\n", sql_num_rows());
427       if (sql_num_rows() <= 0) {
428          Mmsg1(errmsg, _("No volumes found for JobId=%d\n"), JobId);
429          stat = 0;
430       } else {
431          stat = sql_num_rows();
432          DBId_t *SId = NULL;
433          if (stat > 0) {
434             *VolParams = Vols = (VOL_PARAMS *)malloc(stat * sizeof(VOL_PARAMS));
435             SId = (DBId_t *)malloc(stat * sizeof(DBId_t));
436          }
437          for (i=0; i < stat; i++) {
438             if ((row = sql_fetch_row()) == NULL) {
439                Mmsg2(errmsg, _("Error fetching row %d: ERR=%s\n"), i, sql_strerror());
440                Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
441                stat = 0;
442                break;
443             } else {
444                DBId_t StorageId;
445                uint32_t StartBlock, EndBlock, StartFile, EndFile;
446                bstrncpy(Vols[i].VolumeName, row[0], MAX_NAME_LENGTH);
447                bstrncpy(Vols[i].MediaType, row[1], MAX_NAME_LENGTH);
448                Vols[i].FirstIndex = str_to_uint64(row[2]);
449                Vols[i].LastIndex = str_to_uint64(row[3]);
450                StartFile = str_to_uint64(row[4]);
451                EndFile = str_to_uint64(row[5]);
452                StartBlock = str_to_uint64(row[6]);
453                EndBlock = str_to_uint64(row[7]);
454                Vols[i].StartAddr = (((uint64_t)StartFile)<<32) | StartBlock;
455                Vols[i].EndAddr =   (((uint64_t)EndFile)<<32) | EndBlock;
456                Vols[i].Slot = str_to_uint64(row[8]);
457                StorageId = str_to_uint64(row[9]);
458                Vols[i].InChanger = str_to_uint64(row[10]);
459                Vols[i].Storage[0] = 0;
460                SId[i] = StorageId;
461             }
462          }
463          for (i=0; i < stat; i++) {
464             if (SId[i] != 0) {
465                Mmsg(cmd, "SELECT Name from Storage WHERE StorageId=%s",
466                   edit_int64(SId[i], ed1));
467                if (QueryDB(jcr, cmd)) {
468                   if ((row = sql_fetch_row()) && row[0]) {
469                      bstrncpy(Vols[i].Storage, row[0], MAX_NAME_LENGTH);
470                   }
471                }
472             }
473          }
474          if (SId) {
475             free(SId);
476          }
477       }
478       sql_free_result();
479    }
480    bdb_unlock();
481    return stat;
482 }
483
484
485
486 /**
487  * Get the number of pool records
488  *
489  * Returns: -1 on failure
490  *          number on success
491  */
492 int BDB::bdb_get_num_pool_records(JCR *jcr)
493 {
494    int stat = 0;
495
496    bdb_lock();
497    Mmsg(cmd, "SELECT count(*) from Pool");
498    stat = get_sql_record_max(jcr, this);
499    bdb_unlock();
500    return stat;
501 }
502
503 /**
504  * This function returns a list of all the Pool record ids.
505  *  The caller must free ids if non-NULL.
506  *
507  *  Returns 0: on failure
508  *          1: on success
509  */
510 int BDB::bdb_get_pool_ids(JCR *jcr, int *num_ids, uint32_t *ids[])
511 {
512    SQL_ROW row;
513    int stat = 0;
514    int i = 0;
515    uint32_t *id;
516
517    bdb_lock();
518    *ids = NULL;
519    Mmsg(cmd, "SELECT PoolId FROM Pool ORDER By Name");
520    if (QueryDB(jcr, cmd)) {
521       *num_ids = sql_num_rows();
522       if (*num_ids > 0) {
523          id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
524          while ((row = sql_fetch_row()) != NULL) {
525             id[i++] = str_to_uint64(row[0]);
526          }
527          *ids = id;
528       }
529       sql_free_result();
530       stat = 1;
531    } else {
532       Mmsg(errmsg, _("Pool id select failed: ERR=%s\n"), sql_strerror());
533       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
534       stat = 0;
535    }
536    bdb_unlock();
537    return stat;
538 }
539
540 /**
541  * This function returns a list of all the Client record ids.
542  *  The caller must free ids if non-NULL.
543  *
544  *  Returns 0: on failure
545  *          1: on success
546  */
547 int BDB::bdb_get_client_ids(JCR *jcr, int *num_ids, uint32_t *ids[])
548 {
549    SQL_ROW row;
550    int stat = 0;
551    int i = 0;
552    uint32_t *id;
553
554    bdb_lock();
555    *ids = NULL;
556    Mmsg(cmd, "SELECT ClientId FROM Client ORDER BY Name ASC");
557    if (QueryDB(jcr, cmd)) {
558       *num_ids = sql_num_rows();
559       if (*num_ids > 0) {
560          id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
561          while ((row = sql_fetch_row()) != NULL) {
562             id[i++] = str_to_uint64(row[0]);
563          }
564          *ids = id;
565       }
566       sql_free_result();
567       stat = 1;
568    } else {
569       Mmsg(errmsg, _("Client id select failed: ERR=%s\n"), sql_strerror());
570       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
571       stat = 0;
572    }
573    bdb_unlock();
574    return stat;
575 }
576
577 /**
578  * Get Pool Id, Scratch Pool Id, Recycle Pool Id
579  * Returns: false on failure
580  *          true on success
581  */
582 bool BDB::bdb_get_pool_record(JCR *jcr, POOL_DBR *pdbr)
583 {
584    SQL_ROW row;
585    bool ok = false;
586    char ed1[50];
587    char esc[MAX_ESCAPE_NAME_LENGTH];
588
589    bdb_lock();
590    if (pdbr->PoolId != 0) {               /* find by id */
591       Mmsg(cmd,
592 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
593 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
594 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId,"
595 "ActionOnPurge,CacheRetention FROM Pool WHERE Pool.PoolId=%s",
596          edit_int64(pdbr->PoolId, ed1));
597    } else {                           /* find by name */
598       bdb_escape_string(jcr, esc, pdbr->Name, strlen(pdbr->Name));
599       Mmsg(cmd,
600 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
601 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
602 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId,"
603 "ActionOnPurge,CacheRetention FROM Pool WHERE Pool.Name='%s'", esc);
604    }
605    if (QueryDB(jcr, cmd)) {
606       if (sql_num_rows() > 1) {
607          char ed1[30];
608          Mmsg1(errmsg, _("More than one Pool! Num=%s\n"),
609             edit_uint64(sql_num_rows(), ed1));
610          Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
611       } else if (sql_num_rows() == 1) {
612          if ((row = sql_fetch_row()) == NULL) {
613             Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
614             Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
615          } else {
616             pdbr->PoolId = str_to_int64(row[0]);
617             bstrncpy(pdbr->Name, row[1]!=NULL?row[1]:"", sizeof(pdbr->Name));
618             pdbr->NumVols = str_to_int64(row[2]);
619             pdbr->MaxVols = str_to_int64(row[3]);
620             pdbr->UseOnce = str_to_int64(row[4]);
621             pdbr->UseCatalog = str_to_int64(row[5]);
622             pdbr->AcceptAnyVolume = str_to_int64(row[6]);
623             pdbr->AutoPrune = str_to_int64(row[7]);
624             pdbr->Recycle = str_to_int64(row[8]);
625             pdbr->VolRetention = str_to_int64(row[9]);
626             pdbr->VolUseDuration = str_to_int64(row[10]);
627             pdbr->MaxVolJobs = str_to_int64(row[11]);
628             pdbr->MaxVolFiles = str_to_int64(row[12]);
629             pdbr->MaxVolBytes = str_to_uint64(row[13]);
630             bstrncpy(pdbr->PoolType, row[14]!=NULL?row[14]:"", sizeof(pdbr->PoolType));
631             pdbr->LabelType = str_to_int64(row[15]);
632             bstrncpy(pdbr->LabelFormat, row[16]!=NULL?row[16]:"", sizeof(pdbr->LabelFormat));
633             pdbr->RecyclePoolId = str_to_int64(row[17]);
634             pdbr->ScratchPoolId = str_to_int64(row[18]);
635             pdbr->ActionOnPurge = str_to_int32(row[19]);
636             pdbr->CacheRetention = str_to_int64(row[20]);
637             ok = true;
638          }
639       }
640       sql_free_result();
641    }
642    bdb_unlock();
643    return ok;
644 }
645 /**
646  * Get Pool numvols
647  * If the PoolId is non-zero, we get its record,
648  *  otherwise, we search on the PoolName and we compute the number of volumes
649  *
650  * Returns: false on failure
651  *          true on success
652  */
653 bool BDB::bdb_get_pool_numvols(JCR *jcr, POOL_DBR *pdbr)
654 {
655    bool ok;
656    char ed1[50];
657
658    ok = db_get_pool_record(jcr, this, pdbr);
659
660    bdb_lock();
661    if (ok) {
662       uint32_t NumVols;
663       Mmsg(cmd, "SELECT count(*) from Media WHERE PoolId=%s",
664          edit_int64(pdbr->PoolId, ed1));
665       NumVols = get_sql_record_max(jcr, this);
666       Dmsg2(400, "Actual NumVols=%d Pool NumVols=%d\n", NumVols, pdbr->NumVols);
667       if (NumVols != pdbr->NumVols) {
668          pdbr->NumVols = NumVols;
669          db_update_pool_record(jcr, this, pdbr);
670       }
671    } else {
672       Mmsg(errmsg, _("Pool record not found in Catalog.\n"));
673    }
674    bdb_unlock();
675    return ok;
676 }
677
678 /*
679  * Free restoreobject record (some fields are allocated through malloc)
680  */
681 void db_free_restoreobject_record(JCR *jcr, ROBJECT_DBR *rr)
682 {
683    if (rr->object) {
684       free(rr->object);
685    }
686    if (rr->object_name) {
687       free(rr->object_name);
688    }
689    if (rr->plugin_name) {
690       free(rr->plugin_name);
691    }
692    rr->object = rr->plugin_name = rr->object_name = NULL;
693 }
694
695 /*
696  * Get RestoreObject  Record
697  * If the RestoreObjectId is non-zero, we get its record
698  *
699  * You must call db_free_restoreobject_record() after db_get_restoreobject_record()
700  *
701  * Returns: false on failure
702  *          true  on success
703  */
704 bool BDB::bdb_get_restoreobject_record(JCR *jcr, ROBJECT_DBR *rr)
705 {
706    SQL_ROW row;
707    int stat = false;
708    char ed1[50];
709    int32_t len;
710
711    bdb_lock();
712    Mmsg(cmd,
713         "SELECT ObjectName, PluginName, ObjectType, JobId, ObjectCompression, "
714                "RestoreObject, ObjectLength, ObjectFullLength, FileIndex "
715           "FROM RestoreObject "
716          "WHERE RestoreObjectId=%s",
717            edit_int64(rr->RestoreObjectId, ed1));
718
719    /* Using the JobId permits to check the Job name against ACLs and
720     * make sure that the current user is authorized to see the Restore object
721     */
722    if (rr->JobId) {
723       pm_strcat(cmd, " AND JobId=");
724       pm_strcat(cmd, edit_int64(rr->JobId, ed1));
725
726    } else if (rr->JobIds && is_a_number_list(rr->JobIds)) {
727       pm_strcat(cmd, " AND JobId IN (");
728       pm_strcat(cmd, rr->JobIds);
729       pm_strcat(cmd, ")");
730    }
731
732    if (QueryDB(jcr, cmd)) {
733       if (sql_num_rows() > 1) {
734          char ed1[30];
735          Mmsg1(errmsg, _("Error got %s RestoreObjects but expected only one!\n"),
736             edit_uint64(sql_num_rows(), ed1));
737          sql_data_seek(sql_num_rows()-1);
738       }
739       if ((row = sql_fetch_row()) == NULL) {
740          Mmsg1(errmsg, _("RestoreObject record \"%d\" not found.\n"), rr->RestoreObjectId);
741       } else {
742          db_free_restoreobject_record(jcr, rr);
743          rr->object_name = bstrdup(row[0]);
744          rr->plugin_name = bstrdup(row[1]);
745          rr->FileType = str_to_uint64(row[2]);
746          rr->JobId = str_to_uint64(row[3]);
747          rr->object_compression = str_to_int64(row[4]);
748          rr->object_len  = str_to_uint64(row[6]);
749          rr->object_full_len = str_to_uint64(row[7]);
750          rr->object_index = str_to_uint64(row[8]);
751
752          bdb_unescape_object(jcr,
753                             row[5],                /* Object  */
754                             rr->object_len,        /* Object length */
755                             &cmd, &len);
756
757          if (rr->object_compression > 0) {
758             int out_len = rr->object_full_len + 100; /* full length */
759             char *obj = (char *)malloc(out_len);
760             Zinflate(cmd, rr->object_len, obj, out_len); /* out_len is updated */
761             if (out_len != (int)rr->object_full_len) {
762                Dmsg3(10, "Decompression failed. Len wanted=%d got=%d. Object=%s\n",
763                      rr->object_full_len, out_len, rr->plugin_name);
764
765                Mmsg(errmsg, _("Decompression failed. Len wanted=%d got=%d. Object=%s\n"),
766                     rr->object_full_len, out_len, rr->plugin_name);
767             }
768             obj[out_len] = 0;
769             rr->object = obj;
770             rr->object_len = out_len;
771
772          } else {
773             rr->object = (char *)malloc(sizeof(char)*(len+1));
774             memcpy(rr->object, cmd, len);
775             rr->object[len] = 0;
776             rr->object_len = len;
777          }
778
779          stat = true;
780       }
781       sql_free_result();
782    } else {
783       Mmsg(errmsg, _("RestoreObject record not found in Catalog.\n"));
784    }
785    bdb_unlock();
786    return stat;
787 }
788
789 /**
790  * Get Client Record
791  * If the ClientId is non-zero, we get its record,
792  *  otherwise, we search on the Client Name
793  *
794  * Returns: 0 on failure
795  *          1 on success
796  */
797 int BDB::bdb_get_client_record(JCR *jcr, CLIENT_DBR *cdbr)
798 {
799    SQL_ROW row;
800    int stat = 0;
801    char ed1[50];
802    char esc[MAX_ESCAPE_NAME_LENGTH];
803
804    bdb_lock();
805    if (cdbr->ClientId != 0) {               /* find by id */
806       Mmsg(cmd,
807 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
808 "FROM Client WHERE Client.ClientId=%s",
809         edit_int64(cdbr->ClientId, ed1));
810    } else {                           /* find by name */
811       bdb_escape_string(jcr, esc, cdbr->Name, strlen(cdbr->Name));
812       Mmsg(cmd,
813 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
814 "FROM Client WHERE Client.Name='%s'", esc);
815    }
816
817    if (QueryDB(jcr, cmd)) {
818       if (sql_num_rows() > 1) {
819          Mmsg1(errmsg, _("More than one Client!: %s\n"),
820             edit_uint64(sql_num_rows(), ed1));
821          Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
822       } else if (sql_num_rows() == 1) {
823          if ((row = sql_fetch_row()) == NULL) {
824             Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
825             Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
826          } else {
827             cdbr->ClientId = str_to_int64(row[0]);
828             bstrncpy(cdbr->Name, row[1]!=NULL?row[1]:"", sizeof(cdbr->Name));
829             bstrncpy(cdbr->Uname, row[2]!=NULL?row[2]:"", sizeof(cdbr->Uname));
830             cdbr->AutoPrune = str_to_int64(row[3]);
831             cdbr->FileRetention = str_to_int64(row[4]);
832             cdbr->JobRetention = str_to_int64(row[5]);
833             stat = 1;
834          }
835       } else {
836          Mmsg(errmsg, _("Client record not found in Catalog.\n"));
837       }
838       sql_free_result();
839    } else {
840       Mmsg(errmsg, _("Client record not found in Catalog.\n"));
841    }
842    bdb_unlock();
843    return stat;
844 }
845
846 /**
847  * Get Counter Record
848  *
849  * Returns: 0 on failure
850  *          1 on success
851  */
852 bool BDB::bdb_get_counter_record(JCR *jcr, COUNTER_DBR *cr)
853 {
854    SQL_ROW row;
855    char esc[MAX_ESCAPE_NAME_LENGTH];
856
857    bdb_lock();
858    bdb_escape_string(jcr, esc, cr->Counter, strlen(cr->Counter));
859
860    Mmsg(cmd, select_counter_values[bdb_get_type_index()], esc);
861    if (QueryDB(jcr, cmd)) {
862
863       /* If more than one, report error, but return first row */
864       if (sql_num_rows() > 1) {
865          Mmsg1(errmsg, _("More than one Counter!: %d\n"), sql_num_rows());
866          Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
867       }
868       if (sql_num_rows() >= 1) {
869          if ((row = sql_fetch_row()) == NULL) {
870             Mmsg1(errmsg, _("error fetching Counter row: %s\n"), sql_strerror());
871             Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
872             sql_free_result();
873             bdb_unlock();
874             return false;
875          }
876          cr->MinValue = str_to_int64(row[0]);
877          cr->MaxValue = str_to_int64(row[1]);
878          cr->CurrentValue = str_to_int64(row[2]);
879          if (row[3]) {
880             bstrncpy(cr->WrapCounter, row[3], sizeof(cr->WrapCounter));
881          } else {
882             cr->WrapCounter[0] = 0;
883          }
884          sql_free_result();
885          bdb_unlock();
886          return true;
887       }
888       sql_free_result();
889    } else {
890       Mmsg(errmsg, _("Counter record: %s not found in Catalog.\n"), cr->Counter);
891    }
892    bdb_unlock();
893    return false;
894 }
895
896
897 /**
898  * Get FileSet Record
899  * If the FileSetId is non-zero, we get its record,
900  *  otherwise, we search on the name
901  *
902  * Returns: 0 on failure
903  *          id on success
904  */
905 int BDB::bdb_get_fileset_record(JCR *jcr, FILESET_DBR *fsr)
906 {
907    SQL_ROW row;
908    int stat = 0;
909    char ed1[50];
910    char esc[MAX_ESCAPE_NAME_LENGTH];
911
912    bdb_lock();
913    if (fsr->FileSetId != 0) {               /* find by id */
914       Mmsg(cmd,
915            "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
916            "WHERE FileSetId=%s",
917            edit_int64(fsr->FileSetId, ed1));
918    } else {                           /* find by name */
919       bdb_escape_string(jcr, esc, fsr->FileSet, strlen(fsr->FileSet));
920       Mmsg(cmd,
921            "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
922            "WHERE FileSet='%s' ORDER BY CreateTime DESC LIMIT 1", esc);
923    }
924
925    if (QueryDB(jcr, cmd)) {
926       if (sql_num_rows() > 1) {
927          char ed1[30];
928          Mmsg1(errmsg, _("Error got %s FileSets but expected only one!\n"),
929             edit_uint64(sql_num_rows(), ed1));
930          sql_data_seek(sql_num_rows()-1);
931       }
932       if ((row = sql_fetch_row()) == NULL) {
933          Mmsg1(errmsg, _("FileSet record \"%s\" not found.\n"), fsr->FileSet);
934       } else {
935          fsr->FileSetId = str_to_int64(row[0]);
936          bstrncpy(fsr->FileSet, row[1]!=NULL?row[1]:"", sizeof(fsr->FileSet));
937          bstrncpy(fsr->MD5, row[2]!=NULL?row[2]:"", sizeof(fsr->MD5));
938          bstrncpy(fsr->cCreateTime, row[3]!=NULL?row[3]:"", sizeof(fsr->cCreateTime));
939          stat = fsr->FileSetId;
940       }
941       sql_free_result();
942    } else {
943       Mmsg(errmsg, _("FileSet record not found in Catalog.\n"));
944    }
945    bdb_unlock();
946    return stat;
947 }
948
949
950 /**
951  * Get the number of Media records
952  *
953  * Returns: -1 on failure
954  *          number on success
955  */
956 int BDB::bdb_get_num_media_records(JCR *jcr)
957 {
958    int stat = 0;
959
960    bdb_lock();
961    Mmsg(cmd, "SELECT count(*) from Media");
962    stat = get_sql_record_max(jcr, this);
963    bdb_unlock();
964    return stat;
965 }
966
967 /**
968  * This function returns a list of all the Media record ids for
969  *     the current Pool, the correct Media Type, Recyle, Enabled, StorageId, VolBytes
970  *     VolumeName if specified
971  *  The caller must free ids if non-NULL.
972  *
973  *  Returns false: on failure
974  *          true:  on success
975  */
976 bool BDB::bdb_get_media_ids(JCR *jcr, MEDIA_DBR *mr, int *num_ids, uint32_t *ids[])
977 {
978    SQL_ROW row;
979    int i = 0;
980    uint32_t *id;
981    char ed1[50];
982    bool ok = false;
983    char buf[MAX_NAME_LENGTH*3]; /* Can contain MAX_NAME_LENGTH*2+1 + AND ....='' */
984    char esc[MAX_NAME_LENGTH*2+1];
985
986    bdb_lock();
987    *ids = NULL;
988
989    Mmsg(cmd, "SELECT DISTINCT MediaId FROM Media WHERE Enabled=%d ",
990         mr->Enabled);
991
992    if (mr->Recycle >= 0) {
993       bsnprintf(buf, sizeof(buf), "AND Recycle=%d ", mr->Recycle);
994       pm_strcat(cmd, buf);
995    }
996
997    if (*mr->MediaType) {
998       bdb_escape_string(jcr, esc, mr->MediaType, strlen(mr->MediaType));
999       bsnprintf(buf, sizeof(buf), "AND MediaType='%s' ", esc);
1000       pm_strcat(cmd, buf);
1001    }
1002
1003    if (mr->sid_group) {
1004       bsnprintf(buf, sizeof(buf), "AND StorageId IN (%s) ", mr->sid_group);
1005       pm_strcat(cmd, buf);
1006    } else if (mr->StorageId) {
1007       bsnprintf(buf, sizeof(buf), "AND StorageId=%s ", edit_uint64(mr->StorageId, ed1));
1008       pm_strcat(cmd, buf);
1009    }
1010
1011    if (mr->PoolId) {
1012       bsnprintf(buf, sizeof(buf), "AND PoolId=%s ", edit_uint64(mr->PoolId, ed1));
1013       pm_strcat(cmd, buf);
1014    }
1015
1016    if (mr->VolBytes) {
1017       bsnprintf(buf, sizeof(buf), "AND VolBytes > %s ", edit_uint64(mr->VolBytes, ed1));
1018       pm_strcat(cmd, buf);
1019    }
1020
1021    if (*mr->VolumeName) {
1022       bdb_escape_string(jcr, esc, mr->VolumeName, strlen(mr->VolumeName));
1023       bsnprintf(buf, sizeof(buf), "AND VolumeName = '%s' ", esc);
1024       pm_strcat(cmd, buf);
1025    }
1026
1027    if (*mr->VolStatus) {
1028       bdb_escape_string(jcr, esc, mr->VolStatus, strlen(mr->VolStatus));
1029       bsnprintf(buf, sizeof(buf), "AND VolStatus = '%s' ", esc);
1030       pm_strcat(cmd, buf);
1031    }
1032
1033    /* Filter the volumes with the CacheRetention */
1034    if (mr->CacheRetention) {
1035       bsnprintf(buf, sizeof(buf), "AND %s ", prune_cache[bdb_get_type_index()]);
1036       pm_strcat(cmd, buf);
1037    }
1038
1039    Dmsg1(100, "q=%s\n", cmd);
1040
1041    if (QueryDB(jcr, cmd)) {
1042       *num_ids = sql_num_rows();
1043       if (*num_ids > 0) {
1044          id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
1045          while ((row = sql_fetch_row()) != NULL) {
1046             id[i++] = str_to_uint64(row[0]);
1047          }
1048          *ids = id;
1049       }
1050       sql_free_result();
1051       ok = true;
1052    } else {
1053       Mmsg(errmsg, _("Media id select failed: ERR=%s\n"), sql_strerror());
1054       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1055       ok = false;
1056    }
1057    bdb_unlock();
1058    return ok;
1059 }
1060
1061 /**
1062  * This function returns a list of all the DBIds that are returned
1063  *   for the query.
1064  *
1065  *  Returns false: on failure
1066  *          true:  on success
1067  */
1068 bool BDB::bdb_get_query_dbids(JCR *jcr, POOL_MEM &query, dbid_list &ids)
1069 {
1070    SQL_ROW row;
1071    int i = 0;
1072    bool ok = false;
1073
1074    bdb_lock();
1075    ids.num_ids = 0;
1076    if (QueryDB(jcr, query.c_str())) {
1077       ids.num_ids = sql_num_rows();
1078       if (ids.num_ids > 0) {
1079          if (ids.max_ids < ids.num_ids) {
1080             free(ids.DBId);
1081             ids.DBId = (DBId_t *)malloc(ids.num_ids * sizeof(DBId_t));
1082          }
1083          while ((row = sql_fetch_row()) != NULL) {
1084             ids.DBId[i++] = str_to_uint64(row[0]);
1085          }
1086       }
1087       sql_free_result();
1088       ok = true;
1089    } else {
1090       Mmsg(errmsg, _("query dbids failed: ERR=%s\n"), sql_strerror());
1091       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1092       ok = false;
1093    }
1094    bdb_unlock();
1095    return ok;
1096 }
1097
1098 /**
1099  * Get Media Record
1100  *
1101  * Returns: false: on failure
1102  *          true:  on success
1103  */
1104 bool BDB::bdb_get_media_record(JCR *jcr, MEDIA_DBR *mr)
1105 {
1106    SQL_ROW row;
1107    char ed1[50];
1108    bool ok = false;
1109    char esc[MAX_ESCAPE_NAME_LENGTH];
1110
1111    bdb_lock();
1112    if (mr->MediaId == 0 && mr->VolumeName[0] == 0) {
1113       Mmsg(cmd, "SELECT count(*) from Media");
1114       mr->MediaId = get_sql_record_max(jcr, this);
1115       bdb_unlock();
1116       return true;
1117    }
1118    if (mr->MediaId != 0) {               /* find by id */
1119       Mmsg(cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,"
1120          "VolBlocks,VolBytes,VolABytes,VolHoleBytes,VolHoles,VolMounts,"
1121          "VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
1122          "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
1123          "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
1124          "EndFile,EndBlock,VolType,VolParts,VolCloudParts,LastPartBytes,"
1125          "LabelType,LabelDate,StorageId,"
1126          "Enabled,LocationId,RecycleCount,InitialWrite,"
1127          "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge,CacheRetention "
1128          "FROM Media WHERE MediaId=%s",
1129          edit_int64(mr->MediaId, ed1));
1130    } else {                           /* find by name */
1131       bdb_escape_string(jcr, esc, mr->VolumeName, strlen(mr->VolumeName));
1132       Mmsg(cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,"
1133          "VolBlocks,VolBytes,VolABytes,VolHoleBytes,VolHoles,VolMounts,"
1134          "VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
1135          "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
1136          "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
1137          "EndFile,EndBlock,VolType,VolParts,VolCloudParts,LastPartBytes,"
1138          "LabelType,LabelDate,StorageId,"
1139          "Enabled,LocationId,RecycleCount,InitialWrite,"
1140          "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge,CacheRetention "
1141          "FROM Media WHERE VolumeName='%s'", esc);
1142    }
1143
1144    if (QueryDB(jcr, cmd)) {
1145       char ed1[50];
1146       if (sql_num_rows() > 1) {
1147          Mmsg1(errmsg, _("More than one Volume!: %s\n"),
1148             edit_uint64(sql_num_rows(), ed1));
1149          Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1150       } else if (sql_num_rows() == 1) {
1151          if ((row = sql_fetch_row()) == NULL) {
1152             Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
1153             Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1154          } else {
1155             mr->MediaId = str_to_int64(row[0]);
1156             bstrncpy(mr->VolumeName, row[1]!=NULL?row[1]:"", sizeof(mr->VolumeName));
1157             mr->VolJobs = str_to_int64(row[2]);
1158             mr->VolFiles = str_to_int64(row[3]);
1159             mr->VolBlocks = str_to_int64(row[4]);
1160             mr->VolBytes = str_to_uint64(row[5]);
1161             mr->VolABytes = str_to_uint64(row[6]);
1162             mr->VolHoleBytes = str_to_uint64(row[7]);
1163             mr->VolHoles = str_to_int64(row[8]);
1164             mr->VolMounts = str_to_int64(row[9]);
1165             mr->VolErrors = str_to_int64(row[10]);
1166             mr->VolWrites = str_to_int64(row[11]);
1167             mr->MaxVolBytes = str_to_uint64(row[12]);
1168             mr->VolCapacityBytes = str_to_uint64(row[13]);
1169             bstrncpy(mr->MediaType, row[14]!=NULL?row[14]:"", sizeof(mr->MediaType));
1170             bstrncpy(mr->VolStatus, row[15]!=NULL?row[15]:"", sizeof(mr->VolStatus));
1171             mr->PoolId = str_to_int64(row[16]);
1172             mr->VolRetention = str_to_uint64(row[17]);
1173             mr->VolUseDuration = str_to_uint64(row[18]);
1174             mr->MaxVolJobs = str_to_int64(row[19]);
1175             mr->MaxVolFiles = str_to_int64(row[20]);
1176             mr->Recycle = str_to_int64(row[21]);
1177             mr->Slot = str_to_int64(row[22]);
1178             bstrncpy(mr->cFirstWritten, row[23]!=NULL?row[23]:"", sizeof(mr->cFirstWritten));
1179             mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten);
1180             bstrncpy(mr->cLastWritten, row[24]!=NULL?row[24]:"", sizeof(mr->cLastWritten));
1181             mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
1182             mr->InChanger = str_to_uint64(row[25]);
1183             mr->EndFile = str_to_uint64(row[26]);
1184             mr->EndBlock = str_to_uint64(row[27]);
1185             mr->VolType = str_to_int64(row[28]);
1186             mr->VolParts = str_to_int64(row[29]);
1187             mr->VolCloudParts = str_to_int64(row[30]);
1188             mr->LastPartBytes = str_to_uint64(row[31]);
1189             mr->LabelType = str_to_int64(row[32]);
1190             bstrncpy(mr->cLabelDate, row[33]!=NULL?row[33]:"", sizeof(mr->cLabelDate));
1191             mr->LabelDate = (time_t)str_to_utime(mr->cLabelDate);
1192             mr->StorageId = str_to_int64(row[34]);
1193             mr->Enabled = str_to_int64(row[35]);
1194             mr->LocationId = str_to_int64(row[36]);
1195             mr->RecycleCount = str_to_int64(row[37]);
1196             bstrncpy(mr->cInitialWrite, row[38]!=NULL?row[38]:"", sizeof(mr->cInitialWrite));
1197             mr->InitialWrite = (time_t)str_to_utime(mr->cInitialWrite);
1198             mr->ScratchPoolId = str_to_int64(row[39]);
1199             mr->RecyclePoolId = str_to_int64(row[40]);
1200             mr->VolReadTime = str_to_int64(row[41]);
1201             mr->VolWriteTime = str_to_int64(row[42]);
1202             mr->ActionOnPurge = str_to_int32(row[43]);
1203             mr->CacheRetention = str_to_int64(row[44]);
1204
1205             ok = true;
1206          }
1207       } else {
1208          if (mr->MediaId != 0) {
1209             Mmsg1(errmsg, _("Media record with MediaId=%s not found.\n"),
1210                edit_int64(mr->MediaId, ed1));
1211          } else {
1212             Mmsg1(errmsg, _("Media record for Volume name \"%s\" not found.\n"),
1213                   mr->VolumeName);
1214          }
1215       }
1216       sql_free_result();
1217    } else {
1218       if (mr->MediaId != 0) {
1219          Mmsg(errmsg, _("Media record for MediaId=%u not found in Catalog.\n"),
1220             mr->MediaId);
1221        } else {
1222          Mmsg(errmsg, _("Media record for Volume Name \"%s\" not found in Catalog.\n"),
1223             mr->VolumeName);
1224    }   }
1225    bdb_unlock();
1226    return ok;
1227 }
1228
1229 /* Remove all MD5 from a query (can save lot of memory with many files) */
1230 static void strip_md5(char *q)
1231 {
1232    char *p = q;
1233    while ((p = strstr(p, ", MD5"))) {
1234       memset(p, ' ', 5 * sizeof(char));
1235    }
1236 }
1237
1238 /**
1239  * Find the last "accurate" backup state (that can take deleted files in
1240  * account)
1241  * 1) Get all files with jobid in list (F subquery)
1242  *    Get all files in BaseFiles with jobid in list
1243  * 2) Take only the last version of each file (Temp subquery) => accurate list
1244  *    is ok
1245  * 3) Join the result to file table to get fileindex, jobid and lstat information
1246  *
1247  * TODO: See if we can do the SORT only if needed (as an argument)
1248  */
1249 bool BDB::bdb_get_file_list(JCR *jcr, char *jobids,
1250                       bool use_md5, bool use_delta,
1251                       DB_RESULT_HANDLER *result_handler, void *ctx)
1252 {
1253    if (!*jobids) {
1254       bdb_lock();
1255       Mmsg(errmsg, _("ERR=JobIds are empty\n"));
1256       bdb_unlock();
1257       return false;
1258    }
1259    POOL_MEM buf(PM_MESSAGE);
1260    POOL_MEM buf2(PM_MESSAGE);
1261    if (use_delta) {
1262       Mmsg(buf2, select_recent_version_with_basejob_and_delta[bdb_get_type_index()],
1263            jobids, jobids, jobids, jobids);
1264
1265    } else {
1266       Mmsg(buf2, select_recent_version_with_basejob[bdb_get_type_index()],
1267            jobids, jobids, jobids, jobids);
1268    }
1269
1270    /* bsr code is optimized for JobId sorted, with Delta, we need to get
1271     * them ordered by date. JobTDate and JobId can be mixed if using Copy
1272     * or Migration
1273     */
1274    Mmsg(buf,
1275 "SELECT Path.Path, Filename.Name, T1.FileIndex, T1.JobId, LStat, DeltaSeq, MD5 "
1276  "FROM ( %s ) AS T1 "
1277  "JOIN Filename ON (Filename.FilenameId = T1.FilenameId) "
1278  "JOIN Path ON (Path.PathId = T1.PathId) "
1279 "WHERE FileIndex > 0 "
1280 "ORDER BY T1.JobTDate, FileIndex ASC",/* Return sorted by JobTDate */
1281                                       /* FileIndex for restore code */
1282         buf2.c_str());
1283
1284    if (!use_md5) {
1285       strip_md5(buf.c_str());
1286    }
1287
1288    Dmsg1(100, "q=%s\n", buf.c_str());
1289
1290    return bdb_big_sql_query(buf.c_str(), result_handler, ctx);
1291 }
1292
1293 /**
1294  * This procedure gets the base jobid list used by jobids,
1295  */
1296 bool BDB::bdb_get_used_base_jobids(JCR *jcr,
1297                              POOLMEM *jobids, db_list_ctx *result)
1298 {
1299    POOL_MEM buf;
1300
1301    Mmsg(buf,
1302  "SELECT DISTINCT BaseJobId "
1303  "  FROM Job JOIN BaseFiles USING (JobId) "
1304  " WHERE Job.HasBase = 1 "
1305  "   AND Job.JobId IN (%s) ", jobids);
1306    return bdb_sql_query(buf.c_str(), db_list_handler, result);
1307 }
1308
1309 /* Mutex used to have global counter on btemp table */
1310 static pthread_mutex_t btemp_mutex = PTHREAD_MUTEX_INITIALIZER;
1311 static uint32_t btemp_cur = 1;
1312
1313 /**
1314  * The decision do change an incr/diff was done before
1315  * Full : do nothing
1316  * Differential : get the last full id
1317  * Incremental : get the last full + last diff + last incr(s) ids
1318  *
1319  * If you specify jr->StartTime, it will be used to limit the search
1320  * in the time. (usually now)
1321  *
1322  * TODO: look and merge from ua_restore.c
1323  */
1324 bool BDB::bdb_get_accurate_jobids(JCR *jcr,
1325                             JOB_DBR *jr, db_list_ctx *jobids)
1326 {
1327    bool ret=false;
1328    char clientid[50], jobid[50], filesetid[50];
1329    char date[MAX_TIME_LENGTH];
1330    POOL_MEM query(PM_FNAME);
1331
1332    /* Take the current time as upper limit if nothing else specified */
1333    utime_t StartTime = (jr->StartTime)?jr->StartTime:time(NULL);
1334
1335    bstrutime(date, sizeof(date),  StartTime + 1);
1336    jobids->reset();
1337
1338    /* If we are comming from bconsole, we must ensure that we
1339     * have a unique name.
1340     */
1341    if (jcr->JobId == 0) {
1342       P(btemp_mutex);
1343       bsnprintf(jobid, sizeof(jobid), "0%u", btemp_cur++);
1344       V(btemp_mutex);
1345    } else {
1346       edit_uint64(jcr->JobId, jobid);
1347    }
1348
1349    /* First, find the last good Full backup for this job/client/fileset */
1350    Mmsg(query, create_temp_accurate_jobids[bdb_get_type_index()],
1351         jobid,
1352         edit_uint64(jr->ClientId, clientid),
1353         date,
1354         edit_uint64(jr->FileSetId, filesetid));
1355
1356    if (!bdb_sql_query(query.c_str(), NULL, NULL)) {
1357       goto bail_out;
1358    }
1359
1360    if (jr->JobLevel == L_INCREMENTAL || jr->JobLevel == L_VIRTUAL_FULL) {
1361       /* Now, find the last differential backup after the last full */
1362       Mmsg(query,
1363 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
1364  "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1365    "FROM Job JOIN FileSet USING (FileSetId) "
1366   "WHERE ClientId = %s "
1367     "AND Level='D' AND JobStatus IN ('T','W') AND Type='B' "
1368     "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
1369     "AND StartTime < '%s' "
1370     "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1371   "ORDER BY Job.JobTDate DESC LIMIT 1 ",
1372            jobid,
1373            clientid,
1374            jobid,
1375            date,
1376            filesetid);
1377
1378       if (!bdb_sql_query(query.c_str(), NULL, NULL)) {
1379          goto bail_out;
1380       }
1381
1382       /* We just have to take all incremental after the last Full/Diff */
1383       Mmsg(query,
1384 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
1385  "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1386    "FROM Job JOIN FileSet USING (FileSetId) "
1387   "WHERE ClientId = %s "
1388     "AND Level='I' AND JobStatus IN ('T','W') AND Type='B' "
1389     "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
1390     "AND StartTime < '%s' "
1391     "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1392   "ORDER BY Job.JobTDate DESC ",
1393            jobid,
1394            clientid,
1395            jobid,
1396            date,
1397            filesetid);
1398       if (!bdb_sql_query(query.c_str(), NULL, NULL)) {
1399          goto bail_out;
1400       }
1401    }
1402
1403    /* build a jobid list ie: 1,2,3,4 */
1404    Mmsg(query, "SELECT JobId FROM btemp3%s ORDER by JobTDate", jobid);
1405    bdb_sql_query(query.c_str(), db_list_handler, jobids);
1406    Dmsg1(1, "db_get_accurate_jobids=%s\n", jobids->list);
1407    ret = true;
1408
1409 bail_out:
1410    Mmsg(query, "DROP TABLE btemp3%s", jobid);
1411    bdb_sql_query(query.c_str(), NULL, NULL);
1412
1413    return ret;
1414 }
1415
1416 bool BDB::bdb_get_base_file_list(JCR *jcr, bool use_md5,
1417                            DB_RESULT_HANDLER *result_handler, void *ctx)
1418 {
1419    POOL_MEM buf(PM_MESSAGE);
1420
1421    Mmsg(buf,
1422  "SELECT Path, Name, FileIndex, JobId, LStat, 0 As DeltaSeq, MD5 "
1423    "FROM new_basefile%lld ORDER BY JobId, FileIndex ASC",
1424         (uint64_t) jcr->JobId);
1425
1426    if (!use_md5) {
1427       strip_md5(buf.c_str());
1428    }
1429    return bdb_sql_query(buf.c_str(), result_handler, ctx);
1430 }
1431
1432 bool BDB::bdb_get_base_jobid(JCR *jcr, JOB_DBR *jr, JobId_t *jobid)
1433 {
1434    POOL_MEM query(PM_FNAME);
1435    utime_t StartTime;
1436    db_int64_ctx lctx;
1437    char date[MAX_TIME_LENGTH];
1438    char esc[MAX_ESCAPE_NAME_LENGTH];
1439    bool ret = false;
1440
1441    *jobid = 0;
1442    lctx.count = 0;
1443    lctx.value = 0;
1444
1445    StartTime = (jr->StartTime)?jr->StartTime:time(NULL);
1446    bstrutime(date, sizeof(date),  StartTime + 1);
1447    bdb_escape_string(jcr, esc, jr->Name, strlen(jr->Name));
1448
1449    /* we can take also client name, fileset, etc... */
1450
1451    Mmsg(query,
1452  "SELECT JobId, Job, StartTime, EndTime, JobTDate, PurgedFiles "
1453    "FROM Job "
1454 // "JOIN FileSet USING (FileSetId) JOIN Client USING (ClientId) "
1455   "WHERE Job.Name = '%s' "
1456     "AND Level='B' AND JobStatus IN ('T','W') AND Type='B' "
1457 //    "AND FileSet.FileSet= '%s' "
1458 //    "AND Client.Name = '%s' "
1459     "AND StartTime<'%s' "
1460   "ORDER BY Job.JobTDate DESC LIMIT 1",
1461         esc,
1462 //      edit_uint64(jr->ClientId, clientid),
1463 //      edit_uint64(jr->FileSetId, filesetid));
1464         date);
1465
1466    Dmsg1(10, "db_get_base_jobid q=%s\n", query.c_str());
1467    if (!bdb_sql_query(query.c_str(), db_int64_handler, &lctx)) {
1468       goto bail_out;
1469    }
1470    *jobid = (JobId_t) lctx.value;
1471
1472    Dmsg1(10, "db_get_base_jobid=%lld\n", *jobid);
1473    ret = true;
1474
1475 bail_out:
1476    return ret;
1477 }
1478
1479 /* Get JobIds associated with a volume */
1480 bool BDB::bdb_get_volume_jobids(JCR *jcr,
1481                          MEDIA_DBR *mr, db_list_ctx *lst)
1482 {
1483    char ed1[50];
1484    bool ret = false;
1485
1486    bdb_lock();
1487    Mmsg(cmd, "SELECT DISTINCT JobId FROM JobMedia WHERE MediaId=%s",
1488         edit_int64(mr->MediaId, ed1));
1489    ret = bdb_sql_query(cmd, db_list_handler, lst);
1490    bdb_unlock();
1491    return ret;
1492 }
1493
1494 /**
1495  * Get Snapshot Record
1496  *
1497  * Returns: false: on failure
1498  *          true:  on success
1499  */
1500 bool BDB::bdb_get_snapshot_record(JCR *jcr, SNAPSHOT_DBR *sr)
1501 {
1502    SQL_ROW row;
1503    char ed1[50];
1504    bool ok = false;
1505    char esc[MAX_ESCAPE_NAME_LENGTH];
1506    POOL_MEM filter1, filter2;
1507
1508    if (sr->SnapshotId == 0 && (sr->Name[0] == 0 || sr->Device[0] == 0)) {
1509       Dmsg0(10, "No SnapshotId or Name/Device provided\n");
1510       return false;
1511    }
1512
1513    bdb_lock();
1514
1515    if (sr->SnapshotId != 0) {               /* find by id */
1516       Mmsg(filter1, "Snapshot.SnapshotId=%d", sr->SnapshotId);
1517
1518    } else if (*sr->Name && *sr->Device) { /* find by name */
1519       bdb_escape_string(jcr, esc, sr->Name, strlen(sr->Name));
1520       Mmsg(filter1, "Snapshot.Name='%s'", esc);
1521       bdb_escape_string(jcr, esc, sr->Device, strlen(sr->Device));
1522       Mmsg(filter2, "AND Snapshot.Device='%s'", esc);
1523
1524    } else {
1525       Dmsg0(10, "No SnapshotId or Name and Device\n");
1526       return false;
1527    }
1528
1529    Mmsg(cmd, "SELECT SnapshotId, Snapshot.Name, JobId, Snapshot.FileSetId, "
1530         "FileSet.FileSet, CreateTDate, CreateDate, "
1531         "Client.Name AS Client, Snapshot.ClientId, Volume, Device, Type, Retention, "
1532         "Comment FROM Snapshot JOIN Client USING (ClientId) LEFT JOIN FileSet USING (FileSetId) WHERE %s %s",
1533         filter1.c_str(), filter2.c_str());
1534
1535    if (QueryDB(jcr, cmd)) {
1536       char ed1[50];
1537       if (sql_num_rows() > 1) {
1538          Mmsg1(errmsg, _("More than one Snapshot!: %s\n"),
1539             edit_uint64(sql_num_rows(), ed1));
1540          Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1541       } else if (sql_num_rows() == 1) {
1542          if ((row = sql_fetch_row()) == NULL) {
1543             Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
1544             Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1545          } else {
1546             /* return values */
1547             sr->reset();
1548             sr->need_to_free = true;
1549             sr->SnapshotId = str_to_int64(row[0]);
1550             bstrncpy(sr->Name, row[1], sizeof(sr->Name));
1551             sr->JobId = str_to_int64(row[2]);
1552             sr->FileSetId = str_to_int64(row[3]);
1553             bstrncpy(sr->FileSet, row[4], sizeof(sr->FileSet));
1554             sr->CreateTDate = str_to_uint64(row[5]);
1555             bstrncpy(sr->CreateDate, row[6], sizeof(sr->CreateDate));
1556             bstrncpy(sr->Client, row[7], sizeof(sr->Client));
1557             sr->ClientId = str_to_int64(row[8]);
1558             sr->Volume = bstrdup(row[9]);
1559             sr->Device = bstrdup(row[10]);
1560             bstrncpy(sr->Type, row[11], sizeof(sr->Type));
1561             sr->Retention = str_to_int64(row[12]);
1562             bstrncpy(sr->Comment, NPRTB(row[13]), sizeof(sr->Comment));
1563             ok = true;
1564          }
1565       } else {
1566          if (sr->SnapshotId != 0) {
1567             Mmsg1(errmsg, _("Snapshot record with SnapshotId=%s not found.\n"),
1568                edit_int64(sr->SnapshotId, ed1));
1569          } else {
1570             Mmsg1(errmsg, _("Snapshot record for Snapshot name \"%s\" not found.\n"),
1571                   sr->Name);
1572          }
1573       }
1574       sql_free_result();
1575    } else {
1576       if (sr->SnapshotId != 0) {
1577          Mmsg1(errmsg, _("Snapshot record with SnapshotId=%s not found.\n"),
1578                edit_int64(sr->SnapshotId, ed1));
1579       } else {
1580          Mmsg1(errmsg, _("Snapshot record for Snapshot name \"%s\" not found.\n"),
1581                   sr->Name);
1582       }
1583    }
1584    bdb_unlock();
1585    return ok;
1586 }
1587
1588 /* Job, Level */
1589 static void build_estimate_query(BDB *db, POOL_MEM &query, const char *mode,
1590                                  char *job_esc, char level)
1591 {
1592    POOL_MEM filter, tmp;
1593    char ed1[50];
1594
1595
1596    if (level == 0) {
1597       level = 'F';
1598    }
1599    /* MySQL doesn't have statistic functions */
1600    if (db->bdb_get_type_index() == SQL_TYPE_POSTGRESQL) {
1601       /* postgresql have functions that permit to handle lineal regression
1602        * in y=ax + b
1603        * REGR_SLOPE(Y,X) = get x
1604        * REGR_INTERCEPT(Y,X) = get b
1605        * and we need y when x=now()
1606        * CORR gives the correlation
1607        * (TODO: display progress bar only if CORR > 0.8)
1608        */
1609       btime_t now = time(NULL);
1610       Mmsg(query,
1611            "SELECT temp.jobname AS jobname, "
1612            "COALESCE(CORR(value,JobTDate),0) AS corr, "
1613            "(%s*REGR_SLOPE(value,JobTDate) "
1614            " + REGR_INTERCEPT(value,JobTDate)) AS value, "
1615            "AVG(value) AS avg_value, "
1616            " COUNT(1) AS nb ", edit_int64(now, ed1));
1617    } else {
1618       Mmsg(query,
1619            "SELECT jobname AS jobname, "
1620            "0.1 AS corr, AVG(value) AS value, AVG(value) AS avg_value, "
1621            "COUNT(1) AS nb ");
1622     }
1623
1624     /* if it's a differential, we need to compare since the last full
1625      * 
1626      *   F D D D F D D D      F I I I I D I I I
1627      * | #     # #     #    | #         #
1628      * | #   # # #   # #    | #         #
1629      * | # # # # # # # #    | # # # # # # # # #
1630      * +-----------------   +-------------------
1631      */
1632    if (level == L_DIFFERENTIAL) {
1633       Mmsg(filter,
1634            " AND Job.StartTime > ( "
1635              " SELECT StartTime "
1636               " FROM Job "
1637              " WHERE Job.Name = '%s' " 
1638              " AND Job.Level = 'F' "
1639              " AND Job.JobStatus IN ('T', 'W') "
1640            " ORDER BY Job.StartTime DESC LIMIT 1) ",
1641            job_esc);
1642    }
1643    Mmsg(tmp,
1644         " FROM ( "
1645          " SELECT Job.Name AS jobname, "
1646          " %s AS value, "
1647          " JobTDate AS jobtdate "
1648           " FROM Job INNER JOIN Client USING (ClientId) "
1649          " WHERE Job.Name = '%s' "
1650           " AND Job.Level = '%c' "
1651           " AND Job.JobStatus IN ('T', 'W') "
1652         "%s "
1653         "ORDER BY StartTime DESC "
1654         "LIMIT 4"
1655         ") AS temp GROUP BY temp.jobname",
1656         mode, job_esc, level, filter.c_str()
1657       );
1658    pm_strcat(query, tmp.c_str());
1659 }
1660
1661 bool BDB::bdb_get_job_statistics(JCR *jcr, JOB_DBR *jr)
1662 {
1663    SQL_ROW row;
1664    POOL_MEM queryB, queryF, query;
1665    char job_esc[MAX_ESCAPE_NAME_LENGTH];
1666    bool ok = false;
1667
1668    bdb_lock();
1669    bdb_escape_string(jcr, job_esc, jr->Name, strlen(jr->Name));
1670    build_estimate_query(this, queryB, "JobBytes", job_esc, jr->JobLevel);
1671    build_estimate_query(this, queryF, "JobFiles", job_esc, jr->JobLevel);
1672    Mmsg(query,
1673         "SELECT  bytes.corr * 100 AS corr_jobbytes, " /* 0 */
1674                 "bytes.value AS jobbytes, "           /* 1 */
1675                 "bytes.avg_value AS avg_jobbytes, "   /* 2 */
1676                 "bytes.nb AS nb_jobbytes, "           /* 3 */
1677                 "files.corr * 100 AS corr_jobfiles, " /* 4 */
1678                 "files.value AS jobfiles, "           /* 5 */
1679                 "files.avg_value AS avg_jobfiles, "   /* 6 */
1680                 "files.nb AS nb_jobfiles "            /* 7 */
1681         "FROM (%s) AS bytes LEFT JOIN (%s) AS files USING (jobname)",
1682         queryB.c_str(), queryF.c_str());
1683    Dmsg1(100, "query=%s\n", query.c_str());
1684
1685    if (QueryDB(jcr, query.c_str())) {
1686       char ed1[50];
1687       if (sql_num_rows() > 1) {
1688          Mmsg1(errmsg, _("More than one Result!: %s\n"),
1689             edit_uint64(sql_num_rows(), ed1));
1690          goto bail_out;
1691       }
1692       ok = true;
1693
1694       if ((row = sql_fetch_row()) == NULL) {
1695          Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
1696       } else {
1697          jr->CorrJobBytes = str_to_int64(row[0]);
1698          jr->JobBytes = str_to_int64(row[1]);
1699
1700          /* lineal expression with only one job doesn't return a correct value */
1701          if (str_to_int64(row[3]) == 1) {
1702             jr->JobBytes = str_to_int64(row[2]); /* Take the AVG value */
1703          }
1704          jr->CorrNbJob = str_to_int64(row[3]); /* Number of jobs used in this sample */
1705          jr->CorrJobFiles = str_to_int64(row[4]);
1706          jr->JobFiles = str_to_int64(row[5]);
1707
1708          if (str_to_int64(row[7]) == 1) {
1709             jr->JobFiles = str_to_int64(row[6]); /* Take the AVG value */
1710          }
1711       }
1712       sql_free_result();
1713    }
1714 bail_out:
1715    bdb_unlock();
1716    return ok;
1717 }
1718
1719 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */