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