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