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