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