]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql_get.c
Merge branch 'master' of ssh://bacula.git.sourceforge.net/gitroot/bacula/bacula
[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,"
612 "ActionOnPurge FROM Pool WHERE Pool.PoolId=%s", 
613          edit_int64(pdbr->PoolId, ed1));
614    } else {                           /* find by name */
615       Mmsg(mdb->cmd,
616 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
617 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
618 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId,"
619 "ActionOnPurge FROM Pool WHERE Pool.Name='%s'", 
620          pdbr->Name);
621    }
622    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
623       mdb->num_rows = sql_num_rows(mdb);
624       if (mdb->num_rows > 1) {
625          char ed1[30];
626          Mmsg1(mdb->errmsg, _("More than one Pool!: %s\n"),
627             edit_uint64(mdb->num_rows, ed1));
628          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
629       } else if (mdb->num_rows == 1) {
630          if ((row = sql_fetch_row(mdb)) == NULL) {
631             Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
632             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
633          } else {
634             pdbr->PoolId = str_to_int64(row[0]);
635             bstrncpy(pdbr->Name, row[1]!=NULL?row[1]:"", sizeof(pdbr->Name));
636             pdbr->NumVols = str_to_int64(row[2]);
637             pdbr->MaxVols = str_to_int64(row[3]);
638             pdbr->UseOnce = str_to_int64(row[4]);
639             pdbr->UseCatalog = str_to_int64(row[5]);
640             pdbr->AcceptAnyVolume = str_to_int64(row[6]);
641             pdbr->AutoPrune = str_to_int64(row[7]);
642             pdbr->Recycle = str_to_int64(row[8]);
643             pdbr->VolRetention = str_to_int64(row[9]);
644             pdbr->VolUseDuration = str_to_int64(row[10]);
645             pdbr->MaxVolJobs = str_to_int64(row[11]);
646             pdbr->MaxVolFiles = str_to_int64(row[12]);
647             pdbr->MaxVolBytes = str_to_uint64(row[13]);
648             bstrncpy(pdbr->PoolType, row[14]!=NULL?row[14]:"", sizeof(pdbr->PoolType));
649             pdbr->LabelType = str_to_int64(row[15]);
650             bstrncpy(pdbr->LabelFormat, row[16]!=NULL?row[16]:"", sizeof(pdbr->LabelFormat));
651             pdbr->RecyclePoolId = str_to_int64(row[17]);
652             pdbr->ScratchPoolId = str_to_int64(row[18]);
653             pdbr->ActionOnPurge = str_to_int32(row[19]);
654             ok = true;
655          }
656       }
657       sql_free_result(mdb);
658    }
659    if (ok) {
660       uint32_t NumVols;
661       Mmsg(mdb->cmd, "SELECT count(*) from Media WHERE PoolId=%s",
662          edit_int64(pdbr->PoolId, ed1));
663       NumVols = get_sql_record_max(jcr, mdb);
664       Dmsg2(400, "Actual NumVols=%d Pool NumVols=%d\n", NumVols, pdbr->NumVols);
665       if (NumVols != pdbr->NumVols) {
666          pdbr->NumVols = NumVols;
667          db_update_pool_record(jcr, mdb, pdbr);
668       }
669    } else {
670       Mmsg(mdb->errmsg, _("Pool record not found in Catalog.\n"));
671    }
672    db_unlock(mdb);
673    return ok;
674 }
675
676 /* Get Client Record
677  * If the ClientId is non-zero, we get its record,
678  *  otherwise, we search on the Client Name
679  *
680  * Returns: 0 on failure
681  *          1 on success
682  */
683 int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cdbr)
684 {
685    SQL_ROW row;
686    int stat = 0;
687    char ed1[50];
688
689    db_lock(mdb);
690    if (cdbr->ClientId != 0) {               /* find by id */
691       Mmsg(mdb->cmd,
692 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
693 "FROM Client WHERE Client.ClientId=%s", 
694         edit_int64(cdbr->ClientId, ed1));
695    } else {                           /* find by name */
696       Mmsg(mdb->cmd,
697 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
698 "FROM Client WHERE Client.Name='%s'", cdbr->Name);
699    }
700
701    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
702       mdb->num_rows = sql_num_rows(mdb);
703       if (mdb->num_rows > 1) {
704          Mmsg1(mdb->errmsg, _("More than one Client!: %s\n"),
705             edit_uint64(mdb->num_rows, ed1));
706          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
707       } else if (mdb->num_rows == 1) {
708          if ((row = sql_fetch_row(mdb)) == NULL) {
709             Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
710             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
711          } else {
712             cdbr->ClientId = str_to_int64(row[0]);
713             bstrncpy(cdbr->Name, row[1]!=NULL?row[1]:"", sizeof(cdbr->Name));
714             bstrncpy(cdbr->Uname, row[2]!=NULL?row[2]:"", sizeof(cdbr->Uname));
715             cdbr->AutoPrune = str_to_int64(row[3]);
716             cdbr->FileRetention = str_to_int64(row[4]);
717             cdbr->JobRetention = str_to_int64(row[5]);
718             stat = 1;
719          }
720       } else {
721          Mmsg(mdb->errmsg, _("Client record not found in Catalog.\n"));
722       }
723       sql_free_result(mdb);
724    } else {
725       Mmsg(mdb->errmsg, _("Client record not found in Catalog.\n"));
726    }
727    db_unlock(mdb);
728    return stat;
729 }
730
731 /*
732  * Get Counter Record
733  *
734  * Returns: 0 on failure
735  *          1 on success
736  */
737 int db_get_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr)
738 {
739    SQL_ROW row;
740
741    db_lock(mdb);
742    Mmsg(mdb->cmd, "SELECT MinValue,MaxValue,CurrentValue,WrapCounter "
743       "FROM Counters WHERE Counter='%s'", cr->Counter);
744
745    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
746       mdb->num_rows = sql_num_rows(mdb);
747
748       /* If more than one, report error, but return first row */
749       if (mdb->num_rows > 1) {
750          Mmsg1(mdb->errmsg, _("More than one Counter!: %d\n"), (int)(mdb->num_rows));
751          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
752       }
753       if (mdb->num_rows >= 1) {
754          if ((row = sql_fetch_row(mdb)) == NULL) {
755             Mmsg1(mdb->errmsg, _("error fetching Counter row: %s\n"), sql_strerror(mdb));
756             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
757             sql_free_result(mdb);
758             db_unlock(mdb);
759             return 0;
760          }
761          cr->MinValue = str_to_int64(row[0]);
762          cr->MaxValue = str_to_int64(row[1]);
763          cr->CurrentValue = str_to_int64(row[2]);
764          if (row[3]) {
765             bstrncpy(cr->WrapCounter, row[3], sizeof(cr->WrapCounter));
766          } else {
767             cr->WrapCounter[0] = 0;
768          }
769          sql_free_result(mdb);
770          db_unlock(mdb);
771          return 1;
772       }
773       sql_free_result(mdb);
774    } else {
775       Mmsg(mdb->errmsg, _("Counter record: %s not found in Catalog.\n"), cr->Counter);
776    }
777    db_unlock(mdb);
778    return 0;
779 }
780
781
782 /* Get FileSet Record
783  * If the FileSetId is non-zero, we get its record,
784  *  otherwise, we search on the name
785  *
786  * Returns: 0 on failure
787  *          id on success
788  */
789 int db_get_fileset_record(JCR *jcr, B_DB *mdb, FILESET_DBR *fsr)
790 {
791    SQL_ROW row;
792    int stat = 0;
793    char ed1[50];
794
795    db_lock(mdb);
796    if (fsr->FileSetId != 0) {               /* find by id */
797       Mmsg(mdb->cmd,
798            "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
799            "WHERE FileSetId=%s", 
800            edit_int64(fsr->FileSetId, ed1));
801    } else {                           /* find by name */
802       Mmsg(mdb->cmd,
803            "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
804            "WHERE FileSet='%s' ORDER BY CreateTime DESC LIMIT 1", fsr->FileSet);
805    }
806
807    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
808       mdb->num_rows = sql_num_rows(mdb);
809       if (mdb->num_rows > 1) {
810          char ed1[30];
811          Mmsg1(mdb->errmsg, _("Error got %s FileSets but expected only one!\n"),
812             edit_uint64(mdb->num_rows, ed1));
813          sql_data_seek(mdb, mdb->num_rows-1);
814       }
815       if ((row = sql_fetch_row(mdb)) == NULL) {
816          Mmsg1(mdb->errmsg, _("FileSet record \"%s\" not found.\n"), fsr->FileSet);
817       } else {
818          fsr->FileSetId = str_to_int64(row[0]);
819          bstrncpy(fsr->FileSet, row[1]!=NULL?row[1]:"", sizeof(fsr->FileSet));
820          bstrncpy(fsr->MD5, row[2]!=NULL?row[2]:"", sizeof(fsr->MD5));
821          bstrncpy(fsr->cCreateTime, row[3]!=NULL?row[3]:"", sizeof(fsr->cCreateTime));
822          stat = fsr->FileSetId;
823       }
824       sql_free_result(mdb);
825    } else {
826       Mmsg(mdb->errmsg, _("FileSet record not found in Catalog.\n"));
827    }
828    db_unlock(mdb);
829    return stat;
830 }
831
832
833 /*
834  * Get the number of Media records
835  *
836  * Returns: -1 on failure
837  *          number on success
838  */
839 int db_get_num_media_records(JCR *jcr, B_DB *mdb)
840 {
841    int stat = 0;
842
843    db_lock(mdb);
844    Mmsg(mdb->cmd, "SELECT count(*) from Media");
845    stat = get_sql_record_max(jcr, mdb);
846    db_unlock(mdb);
847    return stat;
848 }
849
850
851 /*
852  * This function returns a list of all the Media record ids for
853  *     the current Pool with the correct Media Type.
854  *  The caller must free ids if non-NULL.
855  *
856  *  Returns false: on failure
857  *          true:  on success
858  */
859 bool db_get_media_ids(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr, int *num_ids, uint32_t *ids[])
860 {
861    SQL_ROW row;
862    int i = 0;
863    uint32_t *id;
864    char ed1[50];
865    bool ok = false;
866
867    db_lock(mdb);
868    *ids = NULL;
869    Mmsg(mdb->cmd, "SELECT DISTINCT MediaId FROM Media WHERE PoolId=%s "
870          " AND MediaType='%s'",
871        edit_int64(mr->PoolId, ed1), mr->MediaType);
872    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
873       *num_ids = sql_num_rows(mdb);
874       if (*num_ids > 0) {
875          id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
876          while ((row = sql_fetch_row(mdb)) != NULL) {
877             id[i++] = str_to_uint64(row[0]);
878          }
879          *ids = id;
880       }
881       sql_free_result(mdb);
882       ok = true;
883    } else {
884       Mmsg(mdb->errmsg, _("Media id select failed: ERR=%s\n"), sql_strerror(mdb));
885       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
886       ok = false;
887    }
888    db_unlock(mdb);
889    return ok;
890 }
891
892
893 /*
894  * This function returns a list of all the DBIds that are returned
895  *   for the query.
896  *
897  *  Returns false: on failure
898  *          true:  on success
899  */
900 bool db_get_query_dbids(JCR *jcr, B_DB *mdb, POOL_MEM &query, dbid_list &ids)
901 {
902    SQL_ROW row;
903    int i = 0;
904    bool ok = false;
905
906    db_lock(mdb);
907    ids.num_ids = 0;
908    if (QUERY_DB(jcr, mdb, query.c_str())) {
909       ids.num_ids = sql_num_rows(mdb);
910       if (ids.num_ids > 0) {
911          if (ids.max_ids < ids.num_ids) {
912             free(ids.DBId);
913             ids.DBId = (DBId_t *)malloc(ids.num_ids * sizeof(DBId_t));
914          }
915          while ((row = sql_fetch_row(mdb)) != NULL) {
916             ids.DBId[i++] = str_to_uint64(row[0]);
917          }
918       }
919       sql_free_result(mdb);
920       ok = true;
921    } else {
922       Mmsg(mdb->errmsg, _("query dbids failed: ERR=%s\n"), sql_strerror(mdb));
923       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
924       ok = false;
925    }
926    db_unlock(mdb);
927    return ok;
928 }
929
930 /* Get Media Record
931  *
932  * Returns: false: on failure
933  *          true:  on success
934  */
935 bool db_get_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr)
936 {
937    SQL_ROW row;
938    char ed1[50];
939    bool ok = false;
940
941    db_lock(mdb);
942    if (mr->MediaId == 0 && mr->VolumeName[0] == 0) {
943       Mmsg(mdb->cmd, "SELECT count(*) from Media");
944       mr->MediaId = get_sql_record_max(jcr, mdb);
945       db_unlock(mdb);
946       return true;
947    }
948    if (mr->MediaId != 0) {               /* find by id */
949       Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
950          "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
951          "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
952          "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
953          "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
954          "Enabled,LocationId,RecycleCount,InitialWrite,"
955          "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge "
956          "FROM Media WHERE MediaId=%s", 
957          edit_int64(mr->MediaId, ed1));
958    } else {                           /* find by name */
959       Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
960          "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
961          "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
962          "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
963          "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
964          "Enabled,LocationId,RecycleCount,InitialWrite,"
965          "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge "
966          "FROM Media WHERE VolumeName='%s'", mr->VolumeName);
967    }
968
969    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
970       char ed1[50];
971       mdb->num_rows = sql_num_rows(mdb);
972       if (mdb->num_rows > 1) {
973          Mmsg1(mdb->errmsg, _("More than one Volume!: %s\n"),
974             edit_uint64(mdb->num_rows, ed1));
975          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
976       } else if (mdb->num_rows == 1) {
977          if ((row = sql_fetch_row(mdb)) == NULL) {
978             Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
979             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
980          } else {
981             /* return values */
982             mr->MediaId = str_to_int64(row[0]);
983             bstrncpy(mr->VolumeName, row[1]!=NULL?row[1]:"", sizeof(mr->VolumeName));
984             mr->VolJobs = str_to_int64(row[2]);
985             mr->VolFiles = str_to_int64(row[3]);
986             mr->VolBlocks = str_to_int64(row[4]);
987             mr->VolBytes = str_to_uint64(row[5]);
988             mr->VolMounts = str_to_int64(row[6]);
989             mr->VolErrors = str_to_int64(row[7]);
990             mr->VolWrites = str_to_int64(row[8]);
991             mr->MaxVolBytes = str_to_uint64(row[9]);
992             mr->VolCapacityBytes = str_to_uint64(row[10]);
993             bstrncpy(mr->MediaType, row[11]!=NULL?row[11]:"", sizeof(mr->MediaType));
994             bstrncpy(mr->VolStatus, row[12]!=NULL?row[12]:"", sizeof(mr->VolStatus));
995             mr->PoolId = str_to_int64(row[13]);
996             mr->VolRetention = str_to_uint64(row[14]);
997             mr->VolUseDuration = str_to_uint64(row[15]);
998             mr->MaxVolJobs = str_to_int64(row[16]);
999             mr->MaxVolFiles = str_to_int64(row[17]);
1000             mr->Recycle = str_to_int64(row[18]);
1001             mr->Slot = str_to_int64(row[19]);
1002             bstrncpy(mr->cFirstWritten, row[20]!=NULL?row[20]:"", sizeof(mr->cFirstWritten));
1003             mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten);
1004             bstrncpy(mr->cLastWritten, row[21]!=NULL?row[21]:"", sizeof(mr->cLastWritten));
1005             mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
1006             mr->InChanger = str_to_uint64(row[22]);
1007             mr->EndFile = str_to_uint64(row[23]);
1008             mr->EndBlock = str_to_uint64(row[24]);
1009             mr->VolParts = str_to_int64(row[25]);
1010             mr->LabelType = str_to_int64(row[26]);
1011             bstrncpy(mr->cLabelDate, row[27]!=NULL?row[27]:"", sizeof(mr->cLabelDate));
1012             mr->LabelDate = (time_t)str_to_utime(mr->cLabelDate);
1013             mr->StorageId = str_to_int64(row[28]);
1014             mr->Enabled = str_to_int64(row[29]);
1015             mr->LocationId = str_to_int64(row[30]);
1016             mr->RecycleCount = str_to_int64(row[31]);
1017             bstrncpy(mr->cInitialWrite, row[32]!=NULL?row[32]:"", sizeof(mr->cInitialWrite));
1018             mr->InitialWrite = (time_t)str_to_utime(mr->cInitialWrite);
1019             mr->ScratchPoolId = str_to_int64(row[33]);
1020             mr->RecyclePoolId = str_to_int64(row[34]);
1021             mr->VolReadTime = str_to_int64(row[35]);
1022             mr->VolWriteTime = str_to_int64(row[36]);
1023             mr->ActionOnPurge = str_to_int32(row[37]);
1024             
1025             ok = true;
1026          }
1027       } else {
1028          if (mr->MediaId != 0) {
1029             Mmsg1(mdb->errmsg, _("Media record MediaId=%s not found.\n"), 
1030                edit_int64(mr->MediaId, ed1));
1031          } else {
1032             Mmsg1(mdb->errmsg, _("Media record for Volume \"%s\" not found.\n"),
1033                   mr->VolumeName);
1034          }
1035       }
1036       sql_free_result(mdb);
1037    } else {
1038       if (mr->MediaId != 0) {
1039          Mmsg(mdb->errmsg, _("Media record for MediaId=%u not found in Catalog.\n"),
1040             mr->MediaId);
1041        } else {
1042          Mmsg(mdb->errmsg, _("Media record for Vol=%s not found in Catalog.\n"),
1043             mr->VolumeName);
1044    }   }
1045    db_unlock(mdb);
1046    return ok;
1047 }
1048
1049 /*
1050  * Find the last "accurate" backup state (that can take deleted files in account)
1051  * 1) Get all files with jobid in list (F subquery) 
1052  * 2) Take only the last version of each file (Temp subquery) => accurate list is ok
1053  * 3) Join the result to file table to get fileindex, jobid and lstat information
1054  *
1055  * TODO: See if we can do the SORT only if needed (as an argument)
1056  */
1057 bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids, 
1058                       DB_RESULT_HANDLER *result_handler, void *ctx)
1059 {
1060    if (!*jobids) {
1061       db_lock(mdb);
1062       Mmsg(mdb->errmsg, _("ERR=JobIds are empty\n"));
1063       db_unlock(mdb);
1064       return false;
1065    }
1066    POOL_MEM buf(PM_MESSAGE);
1067          
1068 #define new_db_get_file_list
1069 #ifdef new_db_get_file_list
1070    /* This is broken, at least if called from ua_restore.c */
1071    Mmsg(buf,
1072  "SELECT Path.Path, Filename.Name, File.FileIndex, File.JobId, File.LStat "
1073  "FROM ( "
1074   "SELECT max(FileId) as FileId, PathId, FilenameId "
1075     "FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (%s)) AS F "
1076    "GROUP BY PathId, FilenameId "
1077   ") AS Temp "
1078  "JOIN Filename ON (Filename.FilenameId = Temp.FilenameId) "
1079  "JOIN Path ON (Path.PathId = Temp.PathId) "
1080  "JOIN File ON (File.FileId = Temp.FileId) "
1081 "WHERE File.FileIndex > 0 ORDER BY JobId, FileIndex ASC",     /* Return sorted by JobId, */
1082                                                               /* FileIndex for restore code */ 
1083              jobids);
1084 #else
1085    /*  
1086     * I am not sure that this works the same as the code in ua_restore.c
1087     *  but it is very similar. The accurate-test fails in a restore. Bad file count.
1088     */
1089    Mmsg(buf, uar_sel_files, jobids);
1090 #endif
1091
1092    return db_sql_query(mdb, buf.c_str(), result_handler, ctx);
1093 }
1094
1095 /* The decision do change an incr/diff was done before
1096  * Full : do nothing
1097  * Differential : get the last full id
1098  * Incremental : get the last full + last diff + last incr(s) ids
1099  *
1100  * If you specify jr->StartTime, it will be used to limit the search
1101  * in the time. (usually now) 
1102  *
1103  * TODO: look and merge from ua_restore.c
1104  */
1105 bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb, 
1106                             JOB_DBR *jr, db_list_ctx *jobids)
1107 {
1108    bool ret=false;
1109    char clientid[50], jobid[50], filesetid[50];
1110    char date[MAX_TIME_LENGTH];
1111    POOL_MEM query(PM_FNAME);
1112    
1113    /* Take the current time as upper limit if nothing else specified */
1114    utime_t StartTime = (jr->StartTime)?jr->StartTime:time(NULL);
1115
1116    bstrutime(date, sizeof(date),  StartTime + 1);
1117    jobids->list[0] = 0;
1118    jobids->count = 0;
1119
1120    /* First, find the last good Full backup for this job/client/fileset */
1121    Mmsg(query, 
1122 "CREATE TABLE btemp3%s AS "
1123  "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1124    "FROM Job JOIN FileSet USING (FileSetId) "
1125   "WHERE ClientId = %s "
1126     "AND Level='F' AND JobStatus IN ('T','W') AND Type='B' "
1127     "AND StartTime<'%s' "
1128     "AND FileSet.FileSet=(SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1129   "ORDER BY Job.JobTDate DESC LIMIT 1",
1130         edit_uint64(jcr->JobId, jobid),
1131         edit_uint64(jr->ClientId, clientid),
1132         date,
1133         edit_uint64(jr->FileSetId, filesetid));
1134
1135    if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
1136       goto bail_out;
1137    }
1138
1139    if (jr->JobLevel == L_INCREMENTAL || jr->JobLevel == L_VIRTUAL_FULL) {
1140       /* Now, find the last differential backup after the last full */
1141       Mmsg(query, 
1142 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
1143  "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1144    "FROM Job JOIN FileSet USING (FileSetId) "
1145   "WHERE ClientId = %s "
1146     "AND Level='D' AND JobStatus IN ('T','W') AND Type='B' "
1147     "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
1148     "AND StartTime < '%s' "
1149     "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1150   "ORDER BY Job.JobTDate DESC LIMIT 1 ",
1151            jobid,
1152            clientid,
1153            jobid,
1154            date,
1155            filesetid);
1156
1157       if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
1158          goto bail_out;
1159       }
1160
1161       /* We just have to take all incremental after the last Full/Diff */
1162       Mmsg(query, 
1163 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
1164  "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1165    "FROM Job JOIN FileSet USING (FileSetId) "
1166   "WHERE ClientId = %s "
1167     "AND Level='I' AND JobStatus IN ('T','W') AND Type='B' "
1168     "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
1169     "AND StartTime < '%s' "
1170     "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1171   "ORDER BY Job.JobTDate DESC ",
1172            jobid,
1173            clientid,
1174            jobid,
1175            date,
1176            filesetid);
1177       if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
1178          goto bail_out;
1179       }
1180    }
1181
1182    /* build a jobid list ie: 1,2,3,4 */
1183    Mmsg(query, "SELECT JobId FROM btemp3%s ORDER by JobTDate", jobid);
1184    db_sql_query(mdb, query.c_str(), db_list_handler, jobids);
1185    Dmsg1(1, "db_accurate_get_jobids=%s\n", jobids->list);
1186    ret = true;
1187
1188 bail_out:
1189    Mmsg(query, "DROP TABLE btemp3%s", jobid);
1190    db_sql_query(mdb, query.c_str(), NULL, NULL);
1191
1192    return ret;
1193 }
1194
1195 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI */