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