]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql_get.c
Tweak leave SQL library links in rpm
[bacula/bacula] / bacula / src / cats / sql_get.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2016 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
29 #include  "bacula.h"
30
31 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
32
33 #include  "cats.h"
34
35 /* -----------------------------------------------------------------------
36  *
37  *   Generic Routines (or almost generic)
38  *
39  * -----------------------------------------------------------------------
40  */
41
42 /*
43  * Given a full filename (with path), look up the File record
44  * (with attributes) in the database.
45  *
46  *  Returns: false on failure
47  *           true on success with the File record in FILE_DBR
48  */
49 bool BDB::bdb_get_file_attributes_record(JCR *jcr, char *afname, JOB_DBR *jr, FILE_DBR *fdbr)
50 {
51    bool ok;
52
53    Dmsg1(500, "db_get_file_att_record fname=%s \n", afname);
54
55    bdb_lock();
56
57    split_path_and_file(jcr, this, afname);
58
59    fdbr->FilenameId = bdb_get_filename_record(jcr);
60
61    fdbr->PathId = bdb_get_path_record(jcr);
62
63    ok = bdb_get_file_record(jcr, jr, fdbr);
64
65    bdb_unlock();
66
67    return ok;
68 }
69
70
71 /*
72  * Get a File record
73  *
74  *  DO NOT use Jmsg in this routine.
75  *
76  *  Note in this routine, we do not use Jmsg because it may be
77  *    called to get attributes of a non-existent file, which is
78  *    "normal" if a new file is found during Verify.
79  *
80  *  The following is a bit of a kludge: because we always backup a
81  *    directory entry, we can end up with two copies of the directory
82  *    in the backup. One is when we encounter the directory and find
83  *    we cannot recurse into it, and the other is when we find an
84  *    explicit mention of the directory. This can also happen if the
85  *    use includes the directory twice.  In this case, Verify
86  *    VolumeToCatalog fails because we have two copies in the catalog, and
87  *    only the first one is marked (twice).  So, when calling from Verify,
88  *    VolumeToCatalog jr is not NULL, and we know jr->FileIndex is the fileindex
89  *    of the version of the directory/file we actually want and do
90  *    a more explicit SQL search.
91  *
92  * Returns: false on failure
93  *          true  on success
94  *
95  */
96 bool BDB::bdb_get_file_record(JCR *jcr, JOB_DBR *jr, FILE_DBR *fdbr)
97 {
98    SQL_ROW row;
99    bool ok = false;
100    char ed1[50], ed2[50], ed3[50];
101
102    switch (jcr->getJobLevel()) {
103    case L_VERIFY_VOLUME_TO_CATALOG:
104       Mmsg(cmd,
105 "SELECT FileId, LStat, MD5 FROM File WHERE File.JobId=%s AND File.PathId=%s AND "
106 "File.FilenameId=%s AND File.FileIndex=%u",
107       edit_int64(fdbr->JobId, ed1),
108       edit_int64(fdbr->PathId, ed2),
109       edit_int64(fdbr->FilenameId,ed3),
110       jr->FileIndex);
111       break;
112    case L_VERIFY_DISK_TO_CATALOG:
113       Mmsg(cmd,
114 "SELECT FileId, LStat, MD5 FROM File,Job WHERE "
115 "File.JobId=Job.JobId AND File.PathId=%s AND "
116 "File.FilenameId=%s AND Job.Type='B' AND Job.JobStatus IN ('T','W') AND "
117 "ClientId=%s ORDER BY StartTime DESC LIMIT 1",
118       edit_int64(fdbr->PathId, ed1), 
119       edit_int64(fdbr->FilenameId, ed2), 
120       edit_int64(jr->ClientId,ed3));
121       break;
122    default:
123       Mmsg(cmd,
124 "SELECT FileId, LStat, MD5 FROM File WHERE File.JobId=%s AND File.PathId=%s AND "
125 "File.FilenameId=%s",
126       edit_int64(fdbr->JobId, ed1),
127       edit_int64(fdbr->PathId, ed2),
128       edit_int64(fdbr->FilenameId,ed3));
129       break;
130    }
131
132    Dmsg3(450, "Get_file_record JobId=%u FilenameId=%u PathId=%u\n",
133       fdbr->JobId, fdbr->FilenameId, fdbr->PathId);
134
135    Dmsg1(100, "Query=%s\n", cmd);
136
137    if (QueryDB(jcr, cmd)) {
138       Dmsg1(100, "get_file_record sql_num_rows()=%d\n", sql_num_rows());
139       if (sql_num_rows() >= 1) {
140          if ((row = sql_fetch_row()) == NULL) {
141             Mmsg1(errmsg, _("Error fetching row: %s\n"), sql_strerror());
142          } else {
143             fdbr->FileId = (FileId_t)str_to_int64(row[0]);
144             bstrncpy(fdbr->LStat, row[1], sizeof(fdbr->LStat));
145             bstrncpy(fdbr->Digest, row[2], sizeof(fdbr->Digest));
146             ok = true;
147             if (sql_num_rows() > 1) {
148                Mmsg3(errmsg, _("get_file_record want 1 got rows=%d PathId=%s FilenameId=%s\n"),
149                   sql_num_rows(), 
150                   edit_int64(fdbr->PathId, ed1), 
151                   edit_int64(fdbr->FilenameId, ed2));
152                Dmsg1(000, "=== Problem!  %s", errmsg);
153             }
154          }
155       } else {
156          Mmsg2(errmsg, _("File record for PathId=%s FilenameId=%s not found.\n"),
157             edit_int64(fdbr->PathId, ed1), 
158             edit_int64(fdbr->FilenameId, ed2));
159       }
160       sql_free_result();
161    } else {
162       Mmsg(errmsg, _("File record not found in Catalog.\n"));
163    }
164    return ok;
165 }
166
167 /*
168  * Get Filename record
169  * Returns: 0 on failure
170  *          FilenameId on success
171  *
172  *   DO NOT use Jmsg in this routine (see notes for get_file_record)
173  */
174 int BDB::bdb_get_filename_record(JCR *jcr)
175 {
176    SQL_ROW row;
177    int FilenameId = 0;
178
179    esc_name = check_pool_memory_size(esc_name, 2*fnl+2);
180    bdb_escape_string(jcr, esc_name, fname, fnl);
181
182    Mmsg(cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
183    if (QueryDB(jcr, cmd)) {
184       char ed1[30];
185       if (sql_num_rows() > 1) { 
186          Mmsg2(errmsg, _("More than one Filename!: %s for file: %s\n"),
187             edit_uint64(sql_num_rows(), ed1), fname);
188          Jmsg(jcr, M_WARNING, 0, "%s", errmsg);
189       }
190       if (sql_num_rows() >= 1) { 
191          if ((row = sql_fetch_row()) == NULL) {
192             Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
193          } else {
194             FilenameId = str_to_int64(row[0]);
195             if (FilenameId <= 0) {
196                Mmsg2(errmsg, _("Get DB Filename record %s found bad record: %d\n"),
197                   cmd, FilenameId);
198                FilenameId = 0;
199             }
200          }
201       } else {
202          Mmsg1(errmsg, _("Filename record: %s not found.\n"), fname);
203       }
204       sql_free_result();
205    } else {
206       Mmsg(errmsg, _("Filename record: %s not found in Catalog.\n"), fname);
207    }
208    return FilenameId;
209 }
210
211 /**
212  * Get path record
213  * Returns: 0 on failure
214  *          PathId on success
215  *
216  *   DO NOT use Jmsg in this routine (see notes for get_file_record)
217  */
218 int BDB::bdb_get_path_record(JCR *jcr)
219 {
220    SQL_ROW row;
221    uint32_t PathId = 0;
222
223    esc_name = check_pool_memory_size(esc_name, 2*pnl+2);
224    bdb_escape_string(jcr, esc_name, path, pnl);
225
226    if (cached_path_id != 0 && cached_path_len == pnl &&
227        strcmp(cached_path, path) == 0) {
228       return cached_path_id;
229    }
230
231    Mmsg(cmd, "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
232
233    if (QueryDB(jcr, cmd)) {
234       char ed1[30];
235       if (sql_num_rows() > 1) {
236          Mmsg2(errmsg, _("More than one Path!: %s for path: %s\n"),
237             edit_uint64(sql_num_rows(), ed1), path);
238          Jmsg(jcr, M_WARNING, 0, "%s", errmsg);
239       }
240       /* Even if there are multiple paths, take the first one */
241       if (sql_num_rows() >= 1) {
242          if ((row = sql_fetch_row()) == NULL) {
243             Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
244          } else {
245             PathId = str_to_int64(row[0]);
246             if (PathId <= 0) {
247                Mmsg2(errmsg, _("Get DB path record %s found bad record: %s\n"),
248                   cmd, edit_int64(PathId, ed1));
249                PathId = 0;
250             } else {
251                /* Cache path */
252                if (PathId != cached_path_id) {
253                   cached_path_id = PathId;
254                   cached_path_len = pnl;
255                   pm_strcpy(cached_path, path);
256                }
257             }
258          }
259       } else {
260          Mmsg1(errmsg, _("Path record: %s not found.\n"), path);
261       }
262       sql_free_result();
263    } else {
264       Mmsg(errmsg, _("Path record: %s not found in Catalog.\n"), path);
265    }
266    return PathId;
267 }
268
269
270 /**
271  * Get Job record for given JobId or Job name
272  * Returns: false on failure
273  *          true  on success
274  */
275 bool BDB::bdb_get_job_record(JCR *jcr, JOB_DBR *jr)
276 {
277    SQL_ROW row;
278    char ed1[50];
279    char esc[MAX_ESCAPE_NAME_LENGTH];
280
281    bdb_lock();
282    if (jr->JobId == 0) {
283       bdb_escape_string(jcr, esc, jr->Job, strlen(jr->Job));
284       Mmsg(cmd, "SELECT VolSessionId,VolSessionTime,"
285 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
286 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
287 "SchedTime,RealEndTime,ReadBytes,HasBase,PurgedFiles "
288 "FROM Job WHERE Job='%s'", esc);
289     } else {
290       Mmsg(cmd, "SELECT VolSessionId,VolSessionTime,"
291 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
292 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
293 "SchedTime,RealEndTime,ReadBytes,HasBase,PurgedFiles "
294 "FROM Job WHERE JobId=%s",
295           edit_int64(jr->JobId, ed1));
296     }
297
298    if (!QueryDB(jcr, cmd)) {
299       bdb_unlock();
300       return false;                   /* failed */
301    }
302    if ((row = sql_fetch_row()) == NULL) {
303       Mmsg1(errmsg, _("No Job found for JobId %s\n"), edit_int64(jr->JobId, ed1));
304       sql_free_result();
305       bdb_unlock();
306       return false;                   /* failed */
307    }
308
309    jr->VolSessionId = str_to_uint64(row[0]);
310    jr->VolSessionTime = str_to_uint64(row[1]);
311    jr->PoolId = str_to_int64(row[2]);
312    bstrncpy(jr->cStartTime, row[3]!=NULL?row[3]:"", sizeof(jr->cStartTime));
313    bstrncpy(jr->cEndTime, row[4]!=NULL?row[4]:"", sizeof(jr->cEndTime));
314    jr->JobFiles = str_to_int64(row[5]);
315    jr->JobBytes = str_to_int64(row[6]);
316    jr->JobTDate = str_to_int64(row[7]);
317    bstrncpy(jr->Job, row[8]!=NULL?row[8]:"", sizeof(jr->Job));
318    jr->JobStatus = row[9]!=NULL?(int)*row[9]:JS_FatalError;
319    jr->JobType = row[10]!=NULL?(int)*row[10]:JT_BACKUP;
320    jr->JobLevel = row[11]!=NULL?(int)*row[11]:L_NONE;
321    jr->ClientId = str_to_uint64(row[12]!=NULL?row[12]:(char *)"");
322    bstrncpy(jr->Name, row[13]!=NULL?row[13]:"", sizeof(jr->Name));
323    jr->PriorJobId = str_to_uint64(row[14]!=NULL?row[14]:(char *)"");
324    bstrncpy(jr->cRealEndTime, row[15]!=NULL?row[15]:"", sizeof(jr->cRealEndTime));
325    if (jr->JobId == 0) {
326       jr->JobId = str_to_int64(row[16]);
327    }
328    jr->FileSetId = str_to_int64(row[17]);
329    bstrncpy(jr->cSchedTime, row[18]!=NULL?row[18]:"", sizeof(jr->cSchedTime));
330    bstrncpy(jr->cRealEndTime, row[19]!=NULL?row[19]:"", sizeof(jr->cRealEndTime));
331    jr->ReadBytes = str_to_int64(row[20]);
332    jr->StartTime = str_to_utime(jr->cStartTime);
333    jr->SchedTime = str_to_utime(jr->cSchedTime);
334    jr->EndTime = str_to_utime(jr->cEndTime);
335    jr->RealEndTime = str_to_utime(jr->cRealEndTime);
336    jr->HasBase = str_to_int64(row[21]);
337    jr->PurgedFiles = str_to_int64(row[22]);
338    sql_free_result();
339
340    bdb_unlock();
341    return true;
342 }
343
344 /**
345  * Find VolumeNames for a given JobId
346  *  Returns: 0 on error or no Volumes found
347  *           number of volumes on success
348  *              Volumes are concatenated in VolumeNames
349  *              separated by a vertical bar (|) in the order
350  *              that they were written.
351  *
352  *  Returns: number of volumes on success
353  */
354 int BDB::bdb_get_job_volume_names(JCR *jcr, JobId_t JobId, POOLMEM **VolumeNames)
355 {
356    SQL_ROW row;
357    char ed1[50];
358    int stat = 0;
359    int i;
360
361    bdb_lock();
362    /* Get one entry per VolumeName, but "sort" by VolIndex */
363    Mmsg(cmd,
364         "SELECT VolumeName,MAX(VolIndex) FROM JobMedia,Media WHERE "
365         "JobMedia.JobId=%s AND JobMedia.MediaId=Media.MediaId "
366         "GROUP BY VolumeName "
367         "ORDER BY 2 ASC", edit_int64(JobId,ed1));
368
369    Dmsg1(130, "VolNam=%s\n", cmd);
370    *VolumeNames[0] = 0;
371    if (QueryDB(jcr, cmd)) {
372       Dmsg1(130, "Num rows=%d\n", sql_num_rows());
373       if (sql_num_rows() <= 0) {
374          Mmsg1(errmsg, _("No volumes found for JobId=%d\n"), JobId);
375          stat = 0;
376       } else {
377          stat = sql_num_rows();
378          for (i=0; i < stat; i++) {
379             if ((row = sql_fetch_row()) == NULL) {
380                Mmsg2(errmsg, _("Error fetching row %d: ERR=%s\n"), i, sql_strerror());
381                Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
382                stat = 0;
383                break;
384             } else {
385                if (*VolumeNames[0] != 0) {
386                   pm_strcat(VolumeNames, "|");
387                }
388                pm_strcat(VolumeNames, row[0]);
389             }
390          }
391       }
392       sql_free_result();
393    } else {
394       Mmsg(errmsg, _("No Volume for JobId %d found in Catalog.\n"), JobId);
395    }
396    bdb_unlock();
397    return stat;
398 }
399
400 /**
401  * Find Volume parameters for a give JobId
402  *  Returns: 0 on error or no Volumes found
403  *           number of volumes on success
404  *           List of Volumes and start/end file/blocks (malloced structure!)
405  *
406  *  Returns: number of volumes on success
407  */
408 int BDB::bdb_get_job_volume_parameters(JCR *jcr, JobId_t JobId, VOL_PARAMS **VolParams)
409 {
410    SQL_ROW row;
411    char ed1[50];
412    int stat = 0;
413    int i;
414    VOL_PARAMS *Vols = NULL;
415
416    bdb_lock();
417    Mmsg(cmd,
418 "SELECT VolumeName,MediaType,FirstIndex,LastIndex,StartFile,"
419 "JobMedia.EndFile,StartBlock,JobMedia.EndBlock,"
420 "Slot,StorageId,InChanger"
421 " FROM JobMedia,Media WHERE JobMedia.JobId=%s"
422 " AND JobMedia.MediaId=Media.MediaId ORDER BY VolIndex,JobMediaId",
423         edit_int64(JobId, ed1));
424
425    Dmsg1(130, "VolNam=%s\n", cmd);
426    if (QueryDB(jcr, cmd)) {
427       Dmsg1(200, "Num rows=%d\n", sql_num_rows());
428       if (sql_num_rows() <= 0) {
429          Mmsg1(errmsg, _("No volumes found for JobId=%d\n"), JobId);
430          stat = 0;
431       } else {
432          stat = sql_num_rows();
433          DBId_t *SId = NULL;
434          if (stat > 0) {
435             *VolParams = Vols = (VOL_PARAMS *)malloc(stat * sizeof(VOL_PARAMS));
436             SId = (DBId_t *)malloc(stat * sizeof(DBId_t));
437          }
438          for (i=0; i < stat; i++) {
439             if ((row = sql_fetch_row()) == NULL) {
440                Mmsg2(errmsg, _("Error fetching row %d: ERR=%s\n"), i, sql_strerror());
441                Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
442                stat = 0;
443                break;
444             } else {
445                DBId_t StorageId;
446                uint32_t StartBlock, EndBlock, StartFile, EndFile;
447                bstrncpy(Vols[i].VolumeName, row[0], MAX_NAME_LENGTH);
448                bstrncpy(Vols[i].MediaType, row[1], MAX_NAME_LENGTH);
449                Vols[i].FirstIndex = str_to_uint64(row[2]);
450                Vols[i].LastIndex = str_to_uint64(row[3]);
451                StartFile = str_to_uint64(row[4]);
452                EndFile = str_to_uint64(row[5]);
453                StartBlock = str_to_uint64(row[6]);
454                EndBlock = str_to_uint64(row[7]);
455                Vols[i].StartAddr = (((uint64_t)StartFile)<<32) | StartBlock;
456                Vols[i].EndAddr =   (((uint64_t)EndFile)<<32) | EndBlock;
457                Vols[i].Slot = str_to_uint64(row[8]);
458                StorageId = str_to_uint64(row[9]);
459                Vols[i].InChanger = str_to_uint64(row[10]);
460                Vols[i].Storage[0] = 0;
461                SId[i] = StorageId;
462             }
463          }
464          for (i=0; i < stat; i++) {
465             if (SId[i] != 0) {
466                Mmsg(cmd, "SELECT Name from Storage WHERE StorageId=%s",
467                   edit_int64(SId[i], ed1));
468                if (QueryDB(jcr, cmd)) {
469                   if ((row = sql_fetch_row()) && row[0]) {
470                      bstrncpy(Vols[i].Storage, row[0], MAX_NAME_LENGTH);
471                   }
472                }
473             }
474          }
475          if (SId) {
476             free(SId);
477          }
478       }
479       sql_free_result();
480    }
481    bdb_unlock();
482    return stat;
483 }
484
485
486
487 /**
488  * Get the number of pool records
489  *
490  * Returns: -1 on failure
491  *          number on success
492  */
493 int BDB::bdb_get_num_pool_records(JCR *jcr)
494 {
495    int stat = 0;
496
497    bdb_lock();
498    Mmsg(cmd, "SELECT count(*) from Pool");
499    stat = get_sql_record_max(jcr, this);
500    bdb_unlock();
501    return stat;
502 }
503
504 /**
505  * This function returns a list of all the Pool record ids.
506  *  The caller must free ids if non-NULL.
507  *
508  *  Returns 0: on failure
509  *          1: on success
510  */
511 int BDB::bdb_get_pool_ids(JCR *jcr, int *num_ids, uint32_t *ids[])
512 {
513    SQL_ROW row;
514    int stat = 0;
515    int i = 0;
516    uint32_t *id;
517
518    bdb_lock();
519    *ids = NULL;
520    Mmsg(cmd, "SELECT PoolId FROM Pool");
521    if (QueryDB(jcr, cmd)) {
522       *num_ids = sql_num_rows();
523       if (*num_ids > 0) {
524          id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
525          while ((row = sql_fetch_row()) != NULL) {
526             id[i++] = str_to_uint64(row[0]);
527          }
528          *ids = id;
529       }
530       sql_free_result();
531       stat = 1;
532    } else {
533       Mmsg(errmsg, _("Pool id select failed: ERR=%s\n"), sql_strerror());
534       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
535       stat = 0;
536    }
537    bdb_unlock();
538    return stat;
539 }
540
541 /**
542  * This function returns a list of all the Client record ids.
543  *  The caller must free ids if non-NULL.
544  *
545  *  Returns 0: on failure
546  *          1: on success
547  */
548 int BDB::bdb_get_client_ids(JCR *jcr, int *num_ids, uint32_t *ids[])
549 {
550    SQL_ROW row;
551    int stat = 0;
552    int i = 0;
553    uint32_t *id;
554
555    bdb_lock();
556    *ids = NULL;
557    Mmsg(cmd, "SELECT ClientId FROM Client ORDER BY Name ASC");
558    if (QueryDB(jcr, cmd)) {
559       *num_ids = sql_num_rows();
560       if (*num_ids > 0) {
561          id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
562          while ((row = sql_fetch_row()) != NULL) {
563             id[i++] = str_to_uint64(row[0]);
564          }
565          *ids = id;
566       }
567       sql_free_result();
568       stat = 1;
569    } else {
570       Mmsg(errmsg, _("Client id select failed: ERR=%s\n"), sql_strerror());
571       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
572       stat = 0;
573    }
574    bdb_unlock();
575    return stat;
576 }
577
578 /**
579  * Get Pool Id, Scratch Pool Id, Recycle Pool Id
580  * Returns: false on failure
581  *          true on success
582  */
583 bool BDB::bdb_get_pool_record(JCR *jcr, POOL_DBR *pdbr)
584 {
585    SQL_ROW row;
586    bool ok = false;
587    char ed1[50];
588    char esc[MAX_ESCAPE_NAME_LENGTH];
589
590    bdb_lock();
591    if (pdbr->PoolId != 0) {               /* find by id */
592       Mmsg(cmd,
593 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
594 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
595 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId,"
596 "ActionOnPurge FROM Pool WHERE Pool.PoolId=%s",
597          edit_int64(pdbr->PoolId, ed1));
598    } else {                           /* find by name */
599       bdb_escape_string(jcr, esc, pdbr->Name, strlen(pdbr->Name));
600       Mmsg(cmd,
601 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
602 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
603 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId,"
604 "ActionOnPurge FROM Pool WHERE Pool.Name='%s'", esc);
605    }
606    if (QueryDB(jcr, cmd)) {
607       if (sql_num_rows() > 1) {
608          char ed1[30];
609          Mmsg1(errmsg, _("More than one Pool! Num=%s\n"),
610             edit_uint64(sql_num_rows(), ed1));
611          Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
612       } else if (sql_num_rows() == 1) {
613          if ((row = sql_fetch_row()) == NULL) {
614             Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
615             Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
616          } else {
617             pdbr->PoolId = str_to_int64(row[0]);
618             bstrncpy(pdbr->Name, row[1]!=NULL?row[1]:"", sizeof(pdbr->Name));
619             pdbr->NumVols = str_to_int64(row[2]);
620             pdbr->MaxVols = str_to_int64(row[3]);
621             pdbr->UseOnce = str_to_int64(row[4]);
622             pdbr->UseCatalog = str_to_int64(row[5]);
623             pdbr->AcceptAnyVolume = str_to_int64(row[6]);
624             pdbr->AutoPrune = str_to_int64(row[7]);
625             pdbr->Recycle = str_to_int64(row[8]);
626             pdbr->VolRetention = str_to_int64(row[9]);
627             pdbr->VolUseDuration = str_to_int64(row[10]);
628             pdbr->MaxVolJobs = str_to_int64(row[11]);
629             pdbr->MaxVolFiles = str_to_int64(row[12]);
630             pdbr->MaxVolBytes = str_to_uint64(row[13]);
631             bstrncpy(pdbr->PoolType, row[14]!=NULL?row[14]:"", sizeof(pdbr->PoolType));
632             pdbr->LabelType = str_to_int64(row[15]);
633             bstrncpy(pdbr->LabelFormat, row[16]!=NULL?row[16]:"", sizeof(pdbr->LabelFormat));
634             pdbr->RecyclePoolId = str_to_int64(row[17]);
635             pdbr->ScratchPoolId = str_to_int64(row[18]);
636             pdbr->ActionOnPurge = str_to_int32(row[19]);
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 Recycle=%d AND Enabled=%d ",
990         mr->Recycle, mr->Enabled);
991
992    if (*mr->MediaType) {
993       bdb_escape_string(jcr, esc, mr->MediaType, strlen(mr->MediaType));
994       bsnprintf(buf, sizeof(buf), "AND MediaType='%s' ", esc);
995       pm_strcat(cmd, buf);
996    }
997
998    if (mr->StorageId) {
999       bsnprintf(buf, sizeof(buf), "AND StorageId=%s ", edit_uint64(mr->StorageId, ed1));
1000       pm_strcat(cmd, buf);
1001    }
1002
1003    if (mr->PoolId) {
1004       bsnprintf(buf, sizeof(buf), "AND PoolId=%s ", edit_uint64(mr->PoolId, ed1));
1005       pm_strcat(cmd, buf);
1006    }
1007
1008    if (mr->VolBytes) {
1009       bsnprintf(buf, sizeof(buf), "AND VolBytes > %s ", edit_uint64(mr->VolBytes, ed1));
1010       pm_strcat(cmd, buf);
1011    }
1012
1013    if (*mr->VolumeName) {
1014       bdb_escape_string(jcr, esc, mr->VolumeName, strlen(mr->VolumeName));
1015       bsnprintf(buf, sizeof(buf), "AND VolumeName = '%s' ", esc);
1016       pm_strcat(cmd, buf);
1017    }
1018
1019    if (*mr->VolStatus) {
1020       bdb_escape_string(jcr, esc, mr->VolStatus, strlen(mr->VolStatus));
1021       bsnprintf(buf, sizeof(buf), "AND VolStatus = '%s' ", esc);
1022       pm_strcat(cmd, buf);
1023    }
1024
1025    Dmsg1(100, "q=%s\n", cmd);
1026
1027    if (QueryDB(jcr, cmd)) {
1028       *num_ids = sql_num_rows();
1029       if (*num_ids > 0) {
1030          id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
1031          while ((row = sql_fetch_row()) != NULL) {
1032             id[i++] = str_to_uint64(row[0]);
1033          }
1034          *ids = id;
1035       }
1036       sql_free_result();
1037       ok = true;
1038    } else {
1039       Mmsg(errmsg, _("Media id select failed: ERR=%s\n"), sql_strerror());
1040       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1041       ok = false;
1042    }
1043    bdb_unlock();
1044    return ok;
1045 }
1046
1047
1048 /**
1049  * This function returns a list of all the DBIds that are returned
1050  *   for the query.
1051  *
1052  *  Returns false: on failure
1053  *          true:  on success
1054  */
1055 bool BDB::bdb_get_query_dbids(JCR *jcr, POOL_MEM &query, dbid_list &ids)
1056 {
1057    SQL_ROW row;
1058    int i = 0;
1059    bool ok = false;
1060
1061    bdb_lock();
1062    ids.num_ids = 0;
1063    if (QueryDB(jcr, query.c_str())) {
1064       ids.num_ids = sql_num_rows();
1065       if (ids.num_ids > 0) {
1066          if (ids.max_ids < ids.num_ids) {
1067             free(ids.DBId);
1068             ids.DBId = (DBId_t *)malloc(ids.num_ids * sizeof(DBId_t));
1069          }
1070          while ((row = sql_fetch_row()) != NULL) {
1071             ids.DBId[i++] = str_to_uint64(row[0]);
1072          }
1073       }
1074       sql_free_result();
1075       ok = true;
1076    } else {
1077       Mmsg(errmsg, _("query dbids failed: ERR=%s\n"), sql_strerror());
1078       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1079       ok = false;
1080    }
1081    bdb_unlock();
1082    return ok;
1083 }
1084
1085 /**
1086  * Get Media Record
1087  *
1088  * Returns: false: on failure
1089  *          true:  on success
1090  */
1091 bool BDB::bdb_get_media_record(JCR *jcr, MEDIA_DBR *mr)
1092 {
1093    SQL_ROW row;
1094    char ed1[50];
1095    bool ok = false;
1096    char esc[MAX_ESCAPE_NAME_LENGTH];
1097
1098    bdb_lock();
1099    if (mr->MediaId == 0 && mr->VolumeName[0] == 0) {
1100       Mmsg(cmd, "SELECT count(*) from Media");
1101       mr->MediaId = get_sql_record_max(jcr, this);
1102       bdb_unlock();
1103       return true;
1104    }
1105    if (mr->MediaId != 0) {               /* find by id */
1106       Mmsg(cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,"
1107          "VolBlocks,VolBytes,VolABytes,VolHoleBytes,VolHoles,VolMounts,"
1108          "VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
1109          "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
1110          "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
1111          "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
1112          "Enabled,LocationId,RecycleCount,InitialWrite,"
1113          "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge "
1114          "FROM Media WHERE MediaId=%s",
1115          edit_int64(mr->MediaId, ed1));
1116    } else {                           /* find by name */
1117       bdb_escape_string(jcr, esc, mr->VolumeName, strlen(mr->VolumeName));
1118       Mmsg(cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,"
1119          "VolBlocks,VolBytes,VolABytes,VolHoleBytes,VolHoles,VolMounts,"
1120          "VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
1121          "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
1122          "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
1123          "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
1124          "Enabled,LocationId,RecycleCount,InitialWrite,"
1125          "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge "
1126          "FROM Media WHERE VolumeName='%s'", esc);
1127    }
1128
1129    if (QueryDB(jcr, cmd)) {
1130       char ed1[50];
1131       if (sql_num_rows() > 1) {
1132          Mmsg1(errmsg, _("More than one Volume!: %s\n"),
1133             edit_uint64(sql_num_rows(), ed1));
1134          Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1135       } else if (sql_num_rows() == 1) {
1136          if ((row = sql_fetch_row()) == NULL) {
1137             Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
1138             Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1139          } else {
1140             /* return values */
1141             mr->MediaId = str_to_int64(row[0]);
1142             bstrncpy(mr->VolumeName, row[1]!=NULL?row[1]:"", sizeof(mr->VolumeName));
1143             mr->VolJobs = str_to_int64(row[2]);
1144             mr->VolFiles = str_to_int64(row[3]);
1145             mr->VolBlocks = str_to_int64(row[4]);
1146             mr->VolBytes = str_to_uint64(row[5]);
1147             mr->VolABytes = str_to_uint64(row[6]);
1148             mr->VolHoleBytes = str_to_uint64(row[7]);
1149             mr->VolHoles = str_to_int64(row[8]);
1150             mr->VolMounts = str_to_int64(row[9]);
1151             mr->VolErrors = str_to_int64(row[10]);
1152             mr->VolWrites = str_to_int64(row[11]);
1153             mr->MaxVolBytes = str_to_uint64(row[12]);
1154             mr->VolCapacityBytes = str_to_uint64(row[13]);
1155             bstrncpy(mr->MediaType, row[14]!=NULL?row[14]:"", sizeof(mr->MediaType));
1156             bstrncpy(mr->VolStatus, row[15]!=NULL?row[15]:"", sizeof(mr->VolStatus));
1157             mr->PoolId = str_to_int64(row[16]);
1158             mr->VolRetention = str_to_uint64(row[17]);
1159             mr->VolUseDuration = str_to_uint64(row[18]);
1160             mr->MaxVolJobs = str_to_int64(row[19]);
1161             mr->MaxVolFiles = str_to_int64(row[20]);
1162             mr->Recycle = str_to_int64(row[21]);
1163             mr->Slot = str_to_int64(row[22]);
1164             bstrncpy(mr->cFirstWritten, row[23]!=NULL?row[23]:"", sizeof(mr->cFirstWritten));
1165             mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten);
1166             bstrncpy(mr->cLastWritten, row[24]!=NULL?row[24]:"", sizeof(mr->cLastWritten));
1167             mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
1168             mr->InChanger = str_to_uint64(row[25]);
1169             mr->EndFile = str_to_uint64(row[26]);
1170             mr->EndBlock = str_to_uint64(row[27]);
1171             mr->VolType = str_to_int64(row[28]);   /* formerly VolParts */
1172             mr->LabelType = str_to_int64(row[29]);
1173             bstrncpy(mr->cLabelDate, row[30]!=NULL?row[30]:"", sizeof(mr->cLabelDate));
1174             mr->LabelDate = (time_t)str_to_utime(mr->cLabelDate);
1175             mr->StorageId = str_to_int64(row[31]);
1176             mr->Enabled = str_to_int64(row[32]);
1177             mr->LocationId = str_to_int64(row[33]);
1178             mr->RecycleCount = str_to_int64(row[34]);
1179             bstrncpy(mr->cInitialWrite, row[35]!=NULL?row[35]:"", sizeof(mr->cInitialWrite));
1180             mr->InitialWrite = (time_t)str_to_utime(mr->cInitialWrite);
1181             mr->ScratchPoolId = str_to_int64(row[36]);
1182             mr->RecyclePoolId = str_to_int64(row[37]);
1183             mr->VolReadTime = str_to_int64(row[38]);
1184             mr->VolWriteTime = str_to_int64(row[39]);
1185             mr->ActionOnPurge = str_to_int32(row[40]);
1186
1187             ok = true;
1188          }
1189       } else {
1190          if (mr->MediaId != 0) {
1191             Mmsg1(errmsg, _("Media record with MediaId=%s not found.\n"),
1192                edit_int64(mr->MediaId, ed1));
1193          } else {
1194             Mmsg1(errmsg, _("Media record for Volume name \"%s\" not found.\n"),
1195                   mr->VolumeName);
1196          }
1197       }
1198       sql_free_result();
1199    } else {
1200       if (mr->MediaId != 0) {
1201          Mmsg(errmsg, _("Media record for MediaId=%u not found in Catalog.\n"),
1202             mr->MediaId);
1203        } else {
1204          Mmsg(errmsg, _("Media record for Volume Name \"%s\" not found in Catalog.\n"),
1205             mr->VolumeName);
1206    }   }
1207    bdb_unlock();
1208    return ok;
1209 }
1210
1211 /* Remove all MD5 from a query (can save lot of memory with many files) */
1212 static void strip_md5(char *q)
1213 {
1214    char *p = q;
1215    while ((p = strstr(p, ", MD5"))) {
1216       memset(p, ' ', 5 * sizeof(char));
1217    }
1218 }
1219
1220 /**
1221  * Find the last "accurate" backup state (that can take deleted files in
1222  * account)
1223  * 1) Get all files with jobid in list (F subquery)
1224  *    Get all files in BaseFiles with jobid in list
1225  * 2) Take only the last version of each file (Temp subquery) => accurate list
1226  *    is ok
1227  * 3) Join the result to file table to get fileindex, jobid and lstat information
1228  *
1229  * TODO: See if we can do the SORT only if needed (as an argument)
1230  */
1231 bool BDB::bdb_get_file_list(JCR *jcr, char *jobids,
1232                       bool use_md5, bool use_delta,
1233                       DB_RESULT_HANDLER *result_handler, void *ctx)
1234 {
1235    if (!*jobids) {
1236       bdb_lock();
1237       Mmsg(errmsg, _("ERR=JobIds are empty\n"));
1238       bdb_unlock();
1239       return false;
1240    }
1241    POOL_MEM buf(PM_MESSAGE);
1242    POOL_MEM buf2(PM_MESSAGE);
1243    if (use_delta) {
1244       Mmsg(buf2, select_recent_version_with_basejob_and_delta[bdb_get_type_index()],
1245            jobids, jobids, jobids, jobids);
1246
1247    } else {
1248       Mmsg(buf2, select_recent_version_with_basejob[bdb_get_type_index()],
1249            jobids, jobids, jobids, jobids);
1250    }
1251
1252    /* bsr code is optimized for JobId sorted, with Delta, we need to get
1253     * them ordered by date. JobTDate and JobId can be mixed if using Copy
1254     * or Migration
1255     */
1256    Mmsg(buf,
1257 "SELECT Path.Path, Filename.Name, T1.FileIndex, T1.JobId, LStat, DeltaSeq, MD5 "
1258  "FROM ( %s ) AS T1 "
1259  "JOIN Filename ON (Filename.FilenameId = T1.FilenameId) "
1260  "JOIN Path ON (Path.PathId = T1.PathId) "
1261 "WHERE FileIndex > 0 "
1262 "ORDER BY T1.JobTDate, FileIndex ASC",/* Return sorted by JobTDate */
1263                                       /* FileIndex for restore code */
1264         buf2.c_str());
1265
1266    if (!use_md5) {
1267       strip_md5(buf.c_str());
1268    }
1269
1270    Dmsg1(100, "q=%s\n", buf.c_str());
1271
1272    return bdb_big_sql_query(buf.c_str(), result_handler, ctx);
1273 }
1274
1275 /**
1276  * This procedure gets the base jobid list used by jobids,
1277  */
1278 bool BDB::bdb_get_used_base_jobids(JCR *jcr,
1279                              POOLMEM *jobids, db_list_ctx *result)
1280 {
1281    POOL_MEM buf;
1282
1283    Mmsg(buf,
1284  "SELECT DISTINCT BaseJobId "
1285  "  FROM Job JOIN BaseFiles USING (JobId) "
1286  " WHERE Job.HasBase = 1 "
1287  "   AND Job.JobId IN (%s) ", jobids);
1288    return bdb_sql_query(buf.c_str(), db_list_handler, result);
1289 }
1290
1291 /**
1292  * The decision do change an incr/diff was done before
1293  * Full : do nothing
1294  * Differential : get the last full id
1295  * Incremental : get the last full + last diff + last incr(s) ids
1296  *
1297  * If you specify jr->StartTime, it will be used to limit the search
1298  * in the time. (usually now)
1299  *
1300  * TODO: look and merge from ua_restore.c
1301  */
1302 bool BDB::bdb_get_accurate_jobids(JCR *jcr,
1303                             JOB_DBR *jr, db_list_ctx *jobids)
1304 {
1305    bool ret=false;
1306    char clientid[50], jobid[50], filesetid[50];
1307    char date[MAX_TIME_LENGTH];
1308    POOL_MEM query(PM_FNAME);
1309
1310    /* Take the current time as upper limit if nothing else specified */
1311    utime_t StartTime = (jr->StartTime)?jr->StartTime:time(NULL);
1312
1313    bstrutime(date, sizeof(date),  StartTime + 1);
1314    jobids->reset();
1315
1316    /* First, find the last good Full backup for this job/client/fileset */
1317    Mmsg(query, create_temp_accurate_jobids[bdb_get_type_index()],
1318         edit_uint64(jcr->JobId, jobid),
1319         edit_uint64(jr->ClientId, clientid),
1320         date,
1321         edit_uint64(jr->FileSetId, filesetid));
1322
1323    if (!bdb_sql_query(query.c_str(), NULL, NULL)) {
1324       goto bail_out;
1325    }
1326
1327    if (jr->JobLevel == L_INCREMENTAL || jr->JobLevel == L_VIRTUAL_FULL) {
1328       /* Now, find the last differential backup after the last full */
1329       Mmsg(query,
1330 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
1331  "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1332    "FROM Job JOIN FileSet USING (FileSetId) "
1333   "WHERE ClientId = %s "
1334     "AND Level='D' AND JobStatus IN ('T','W') AND Type='B' "
1335     "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
1336     "AND StartTime < '%s' "
1337     "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1338   "ORDER BY Job.JobTDate DESC LIMIT 1 ",
1339            jobid,
1340            clientid,
1341            jobid,
1342            date,
1343            filesetid);
1344
1345       if (!bdb_sql_query(query.c_str(), NULL, NULL)) {
1346          goto bail_out;
1347       }
1348
1349       /* We just have to take all incremental after the last Full/Diff */
1350       Mmsg(query,
1351 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
1352  "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1353    "FROM Job JOIN FileSet USING (FileSetId) "
1354   "WHERE ClientId = %s "
1355     "AND Level='I' AND JobStatus IN ('T','W') AND Type='B' "
1356     "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
1357     "AND StartTime < '%s' "
1358     "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1359   "ORDER BY Job.JobTDate DESC ",
1360            jobid,
1361            clientid,
1362            jobid,
1363            date,
1364            filesetid);
1365       if (!bdb_sql_query(query.c_str(), NULL, NULL)) {
1366          goto bail_out;
1367       }
1368    }
1369
1370    /* build a jobid list ie: 1,2,3,4 */
1371    Mmsg(query, "SELECT JobId FROM btemp3%s ORDER by JobTDate", jobid);
1372    bdb_sql_query(query.c_str(), db_list_handler, jobids);
1373    Dmsg1(1, "db_get_accurate_jobids=%s\n", jobids->list);
1374    ret = true;
1375
1376 bail_out:
1377    Mmsg(query, "DROP TABLE btemp3%s", jobid);
1378    bdb_sql_query(query.c_str(), NULL, NULL);
1379
1380    return ret;
1381 }
1382
1383 bool BDB::bdb_get_base_file_list(JCR *jcr, bool use_md5,
1384                            DB_RESULT_HANDLER *result_handler, void *ctx)
1385 {
1386    POOL_MEM buf(PM_MESSAGE);
1387
1388    Mmsg(buf,
1389  "SELECT Path, Name, FileIndex, JobId, LStat, 0 As DeltaSeq, MD5 "
1390    "FROM new_basefile%lld ORDER BY JobId, FileIndex ASC",
1391         (uint64_t) jcr->JobId);
1392
1393    if (!use_md5) {
1394       strip_md5(buf.c_str());
1395    }
1396    return bdb_sql_query(buf.c_str(), result_handler, ctx);
1397 }
1398
1399 bool BDB::bdb_get_base_jobid(JCR *jcr, JOB_DBR *jr, JobId_t *jobid)
1400 {
1401    POOL_MEM query(PM_FNAME);
1402    utime_t StartTime;
1403    db_int64_ctx lctx;
1404    char date[MAX_TIME_LENGTH];
1405    char esc[MAX_ESCAPE_NAME_LENGTH];
1406    bool ret = false;
1407
1408    *jobid = 0;
1409    lctx.count = 0;
1410    lctx.value = 0;
1411
1412    StartTime = (jr->StartTime)?jr->StartTime:time(NULL);
1413    bstrutime(date, sizeof(date),  StartTime + 1);
1414    bdb_escape_string(jcr, esc, jr->Name, strlen(jr->Name));
1415
1416    /* we can take also client name, fileset, etc... */
1417
1418    Mmsg(query,
1419  "SELECT JobId, Job, StartTime, EndTime, JobTDate, PurgedFiles "
1420    "FROM Job "
1421 // "JOIN FileSet USING (FileSetId) JOIN Client USING (ClientId) "
1422   "WHERE Job.Name = '%s' "
1423     "AND Level='B' AND JobStatus IN ('T','W') AND Type='B' "
1424 //    "AND FileSet.FileSet= '%s' "
1425 //    "AND Client.Name = '%s' "
1426     "AND StartTime<'%s' "
1427   "ORDER BY Job.JobTDate DESC LIMIT 1",
1428         esc,
1429 //      edit_uint64(jr->ClientId, clientid),
1430 //      edit_uint64(jr->FileSetId, filesetid));
1431         date);
1432
1433    Dmsg1(10, "db_get_base_jobid q=%s\n", query.c_str());
1434    if (!bdb_sql_query(query.c_str(), db_int64_handler, &lctx)) {
1435       goto bail_out;
1436    }
1437    *jobid = (JobId_t) lctx.value;
1438
1439    Dmsg1(10, "db_get_base_jobid=%lld\n", *jobid);
1440    ret = true;
1441
1442 bail_out:
1443    return ret;
1444 }
1445
1446 /* Get JobIds associated with a volume */
1447 bool BDB::bdb_get_volume_jobids(JCR *jcr,
1448                          MEDIA_DBR *mr, db_list_ctx *lst)
1449 {
1450    char ed1[50];
1451    bool ret = false;
1452
1453    bdb_lock();
1454    Mmsg(cmd, "SELECT DISTINCT JobId FROM JobMedia WHERE MediaId=%s",
1455         edit_int64(mr->MediaId, ed1));
1456    ret = bdb_sql_query(cmd, db_list_handler, lst);
1457    bdb_unlock();
1458    return ret;
1459 }
1460
1461 /**
1462  * Get Snapshot Record
1463  *
1464  * Returns: false: on failure
1465  *          true:  on success
1466  */
1467 bool BDB::bdb_get_snapshot_record(JCR *jcr, SNAPSHOT_DBR *sr)
1468 {
1469    SQL_ROW row;
1470    char ed1[50];
1471    bool ok = false;
1472    char esc[MAX_ESCAPE_NAME_LENGTH];
1473    POOL_MEM filter1, filter2;
1474
1475    if (sr->SnapshotId == 0 && (sr->Name[0] == 0 || sr->Device[0] == 0)) {
1476       Dmsg0(10, "No SnapshotId or Name/Device provided\n");
1477       return false;
1478    }
1479
1480    bdb_lock();
1481
1482    if (sr->SnapshotId != 0) {               /* find by id */
1483       Mmsg(filter1, "Snapshot.SnapshotId=%d", sr->SnapshotId);
1484
1485    } else if (*sr->Name && *sr->Device) { /* find by name */
1486       bdb_escape_string(jcr, esc, sr->Name, strlen(sr->Name));
1487       Mmsg(filter1, "Snapshot.Name='%s'", esc);
1488       bdb_escape_string(jcr, esc, sr->Device, strlen(sr->Device));
1489       Mmsg(filter2, "AND Snapshot.Device='%s'", esc);
1490
1491    } else {
1492       Dmsg0(10, "No SnapshotId or Name and Device\n");
1493       return false;
1494    }
1495
1496    Mmsg(cmd, "SELECT SnapshotId, Snapshot.Name, JobId, Snapshot.FileSetId, "
1497         "FileSet.FileSet, CreateTDate, CreateDate, "
1498         "Client.Name AS Client, Snapshot.ClientId, Volume, Device, Type, Retention, "
1499         "Comment FROM Snapshot JOIN Client USING (ClientId) LEFT JOIN FileSet USING (FileSetId) WHERE %s %s",
1500         filter1.c_str(), filter2.c_str());
1501
1502    if (QueryDB(jcr, cmd)) {
1503       char ed1[50];
1504       if (sql_num_rows() > 1) {
1505          Mmsg1(errmsg, _("More than one Snapshot!: %s\n"),
1506             edit_uint64(sql_num_rows(), ed1));
1507          Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1508       } else if (sql_num_rows() == 1) {
1509          if ((row = sql_fetch_row()) == NULL) {
1510             Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
1511             Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1512          } else {
1513             /* return values */
1514             sr->reset();
1515             sr->need_to_free = true;
1516             sr->SnapshotId = str_to_int64(row[0]);
1517             bstrncpy(sr->Name, row[1], sizeof(sr->Name));
1518             sr->JobId = str_to_int64(row[2]);
1519             sr->FileSetId = str_to_int64(row[3]);
1520             bstrncpy(sr->FileSet, row[4], sizeof(sr->FileSet));
1521             sr->CreateTDate = str_to_uint64(row[5]);
1522             bstrncpy(sr->CreateDate, row[6], sizeof(sr->CreateDate));
1523             bstrncpy(sr->Client, row[7], sizeof(sr->Client));
1524             sr->ClientId = str_to_int64(row[8]);
1525             sr->Volume = bstrdup(row[9]);
1526             sr->Device = bstrdup(row[10]);
1527             bstrncpy(sr->Type, row[11], sizeof(sr->Type));
1528             sr->Retention = str_to_int64(row[12]);
1529             bstrncpy(sr->Comment, NPRTB(row[13]), sizeof(sr->Comment));
1530             ok = true;
1531          }
1532       } else {
1533          if (sr->SnapshotId != 0) {
1534             Mmsg1(errmsg, _("Snapshot record with SnapshotId=%s not found.\n"),
1535                edit_int64(sr->SnapshotId, ed1));
1536          } else {
1537             Mmsg1(errmsg, _("Snapshot record for Snapshot name \"%s\" not found.\n"),
1538                   sr->Name);
1539          }
1540       }
1541       sql_free_result();
1542    } else {
1543       if (sr->SnapshotId != 0) {
1544          Mmsg1(errmsg, _("Snapshot record with SnapshotId=%s not found.\n"),
1545                edit_int64(sr->SnapshotId, ed1));
1546       } else {
1547          Mmsg1(errmsg, _("Snapshot record for Snapshot name \"%s\" not found.\n"),
1548                   sr->Name);
1549       }
1550    }
1551    bdb_unlock();
1552    return ok;
1553 }
1554
1555 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */