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