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