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