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