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