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