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