]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql_get.c
Drop Ingres specific versions of queries with #ifdefs
[bacula/bacula] / bacula / src / cats / sql_get.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2010 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  */
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_INGRES || 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->getJobLevel() == 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    } else {
129       Mmsg(mdb->cmd,
130 "SELECT FileId, LStat, MD5 FROM File WHERE File.JobId=%s AND File.PathId=%s AND "
131 "File.FilenameId=%s", 
132       edit_int64(fdbr->JobId, ed1), 
133       edit_int64(fdbr->PathId, ed2), 
134       edit_int64(fdbr->FilenameId,ed3));
135    }
136    Dmsg3(450, "Get_file_record JobId=%u FilenameId=%u PathId=%u\n",
137       fdbr->JobId, fdbr->FilenameId, fdbr->PathId);
138
139    Dmsg1(100, "Query=%s\n", mdb->cmd);
140
141    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
142       mdb->num_rows = sql_num_rows(mdb);
143       Dmsg1(050, "get_file_record num_rows=%d\n", (int)mdb->num_rows);
144       if (mdb->num_rows >= 1) {
145          if ((row = sql_fetch_row(mdb)) == NULL) {
146             Mmsg1(mdb->errmsg, _("Error fetching row: %s\n"), sql_strerror(mdb));
147          } else {
148             fdbr->FileId = (FileId_t)str_to_int64(row[0]);
149             bstrncpy(fdbr->LStat, row[1], sizeof(fdbr->LStat));
150             bstrncpy(fdbr->Digest, row[2], sizeof(fdbr->Digest));
151             stat = 1;
152             if (mdb->num_rows > 1) {
153                Mmsg3(mdb->errmsg, _("get_file_record want 1 got rows=%d PathId=%s FilenameId=%s\n"),
154                   mdb->num_rows, 
155                   edit_int64(fdbr->PathId, ed1), 
156                   edit_int64(fdbr->FilenameId, ed2));
157                Dmsg1(000, "=== Problem!  %s", mdb->errmsg);
158             }
159          }
160       } else {
161          Mmsg2(mdb->errmsg, _("File record for PathId=%s FilenameId=%s not found.\n"),
162             edit_int64(fdbr->PathId, ed1), 
163             edit_int64(fdbr->FilenameId, ed2));
164       }
165       sql_free_result(mdb);
166    } else {
167       Mmsg(mdb->errmsg, _("File record not found in Catalog.\n"));
168    }
169    return stat;
170
171 }
172
173 /**
174  * Get Filename record
175  * Returns: 0 on failure
176  *          FilenameId on success
177  *
178  *   DO NOT use Jmsg in this routine (see notes for get_file_record)
179  */
180 static int db_get_filename_record(JCR *jcr, B_DB *mdb)
181 {
182    SQL_ROW row;
183    int FilenameId = 0;
184
185    mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->fnl+2);
186    db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
187
188    Mmsg(mdb->cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", mdb->esc_name);
189    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
190       char ed1[30];
191       mdb->num_rows = sql_num_rows(mdb);
192       if (mdb->num_rows > 1) {
193          Mmsg2(mdb->errmsg, _("More than one Filename!: %s for file: %s\n"),
194             edit_uint64(mdb->num_rows, ed1), mdb->fname);
195          Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
196       }
197       if (mdb->num_rows >= 1) {
198          if ((row = sql_fetch_row(mdb)) == NULL) {
199             Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
200          } else {
201             FilenameId = str_to_int64(row[0]);
202             if (FilenameId <= 0) {
203                Mmsg2(mdb->errmsg, _("Get DB Filename record %s found bad record: %d\n"),
204                   mdb->cmd, FilenameId);
205                FilenameId = 0;
206             }
207          }
208       } else {
209          Mmsg1(mdb->errmsg, _("Filename record: %s not found.\n"), mdb->fname);
210       }
211       sql_free_result(mdb);
212    } else {
213       Mmsg(mdb->errmsg, _("Filename record: %s not found in Catalog.\n"), mdb->fname);
214    }
215    return FilenameId;
216 }
217
218 /**
219  * Get path record
220  * Returns: 0 on failure
221  *          PathId on success
222  *
223  *   DO NOT use Jmsg in this routine (see notes for get_file_record)
224  */
225 int db_get_path_record(JCR *jcr, B_DB *mdb)
226 {
227    SQL_ROW row;
228    uint32_t PathId = 0;
229
230    mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->pnl+2);
231    db_escape_string(jcr, mdb, mdb->esc_name, mdb->path, mdb->pnl);
232
233    if (mdb->cached_path_id != 0 && mdb->cached_path_len == mdb->pnl &&
234        strcmp(mdb->cached_path, mdb->path) == 0) {
235       return mdb->cached_path_id;
236    }
237
238    Mmsg(mdb->cmd, "SELECT PathId FROM Path WHERE Path='%s'", mdb->esc_name);
239
240    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
241       char ed1[30];
242       mdb->num_rows = sql_num_rows(mdb);
243       if (mdb->num_rows > 1) {
244          Mmsg2(mdb->errmsg, _("More than one Path!: %s for path: %s\n"),
245             edit_uint64(mdb->num_rows, ed1), mdb->path);
246          Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
247       }
248       /* Even if there are multiple paths, take the first one */
249       if (mdb->num_rows >= 1) {
250          if ((row = sql_fetch_row(mdb)) == NULL) {
251             Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
252          } else {
253             PathId = str_to_int64(row[0]);
254             if (PathId <= 0) {
255                Mmsg2(mdb->errmsg, _("Get DB path record %s found bad record: %s\n"),
256                   mdb->cmd, edit_int64(PathId, ed1));
257                PathId = 0;
258             } else {
259                /* Cache path */
260                if (PathId != mdb->cached_path_id) {
261                   mdb->cached_path_id = PathId;
262                   mdb->cached_path_len = mdb->pnl;
263                   pm_strcpy(mdb->cached_path, mdb->path);
264                }
265             }
266          }
267       } else {
268          Mmsg1(mdb->errmsg, _("Path record: %s not found.\n"), mdb->path);
269       }
270       sql_free_result(mdb);
271    } else {
272       Mmsg(mdb->errmsg, _("Path record: %s not found in Catalog.\n"), mdb->path);
273    }
274    return PathId;
275 }
276
277
278 /**
279  * Get Job record for given JobId or Job name
280  * Returns: false on failure
281  *          true  on success
282  */
283 bool db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr)
284 {
285    SQL_ROW row;
286    char ed1[50];
287
288    db_lock(mdb);
289    if (jr->JobId == 0) {
290       Mmsg(mdb->cmd, "SELECT VolSessionId,VolSessionTime,"
291 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
292 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
293 "SchedTime,RealEndTime,ReadBytes,HasBase "
294 "FROM Job WHERE Job='%s'", jr->Job);
295     } else {
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 JobId=%s", 
301           edit_int64(jr->JobId, ed1));
302     }
303
304    if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
305       db_unlock(mdb);
306       return false;                   /* failed */
307    }
308    if ((row = sql_fetch_row(mdb)) == NULL) {
309       Mmsg1(mdb->errmsg, _("No Job found for JobId %s\n"), edit_int64(jr->JobId, ed1));
310       sql_free_result(mdb);
311       db_unlock(mdb);
312       return false;                   /* failed */
313    }
314
315    jr->VolSessionId = str_to_uint64(row[0]);
316    jr->VolSessionTime = str_to_uint64(row[1]);
317    jr->PoolId = str_to_int64(row[2]);
318    bstrncpy(jr->cStartTime, row[3]!=NULL?row[3]:"", sizeof(jr->cStartTime));
319    bstrncpy(jr->cEndTime, row[4]!=NULL?row[4]:"", sizeof(jr->cEndTime));
320    jr->JobFiles = str_to_int64(row[5]);
321    jr->JobBytes = str_to_int64(row[6]);
322    jr->JobTDate = str_to_int64(row[7]);
323    bstrncpy(jr->Job, row[8]!=NULL?row[8]:"", sizeof(jr->Job));
324    jr->JobStatus = row[9]!=NULL?(int)*row[9]:JS_FatalError;
325    jr->JobType = row[10]!=NULL?(int)*row[10]:JT_BACKUP;
326    jr->JobLevel = row[11]!=NULL?(int)*row[11]:L_NONE;
327    jr->ClientId = str_to_uint64(row[12]!=NULL?row[12]:(char *)"");
328    bstrncpy(jr->Name, row[13]!=NULL?row[13]:"", sizeof(jr->Name));
329    jr->PriorJobId = str_to_uint64(row[14]!=NULL?row[14]:(char *)"");
330    bstrncpy(jr->cRealEndTime, row[15]!=NULL?row[15]:"", sizeof(jr->cRealEndTime));
331    if (jr->JobId == 0) {
332       jr->JobId = str_to_int64(row[16]);
333    }
334    jr->FileSetId = str_to_int64(row[17]);
335    bstrncpy(jr->cSchedTime, row[3]!=NULL?row[18]:"", sizeof(jr->cSchedTime));
336    bstrncpy(jr->cRealEndTime, row[3]!=NULL?row[19]:"", sizeof(jr->cRealEndTime));
337    jr->ReadBytes = str_to_int64(row[20]);
338    jr->StartTime = str_to_utime(jr->cStartTime);
339    jr->SchedTime = str_to_utime(jr->cSchedTime);
340    jr->EndTime = str_to_utime(jr->cEndTime);
341    jr->RealEndTime = str_to_utime(jr->cRealEndTime);
342    jr->HasBase = str_to_int64(row[21]);
343    sql_free_result(mdb);
344
345    db_unlock(mdb);
346    return true;
347 }
348
349 /**
350  * Find VolumeNames for a given JobId
351  *  Returns: 0 on error or no Volumes found
352  *           number of volumes on success
353  *              Volumes are concatenated in VolumeNames
354  *              separated by a vertical bar (|) in the order
355  *              that they were written.
356  *
357  *  Returns: number of volumes on success
358  */
359 int db_get_job_volume_names(JCR *jcr, B_DB *mdb, JobId_t JobId, POOLMEM **VolumeNames)
360 {
361    SQL_ROW row;
362    char ed1[50];
363    int stat = 0;
364    int i;
365
366    db_lock(mdb);
367    /* Get one entry per VolumeName, but "sort" by VolIndex */
368    Mmsg(mdb->cmd,
369         "SELECT VolumeName,MAX(VolIndex) FROM JobMedia,Media WHERE "
370         "JobMedia.JobId=%s AND JobMedia.MediaId=Media.MediaId "
371         "GROUP BY VolumeName "
372         "ORDER BY 2 ASC", edit_int64(JobId,ed1));
373
374    Dmsg1(130, "VolNam=%s\n", mdb->cmd);
375    *VolumeNames[0] = 0;
376    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
377       mdb->num_rows = sql_num_rows(mdb);
378       Dmsg1(130, "Num rows=%d\n", mdb->num_rows);
379       if (mdb->num_rows <= 0) {
380          Mmsg1(mdb->errmsg, _("No volumes found for JobId=%d\n"), JobId);
381          stat = 0;
382       } else {
383          stat = mdb->num_rows;
384          for (i=0; i < stat; i++) {
385             if ((row = sql_fetch_row(mdb)) == NULL) {
386                Mmsg2(mdb->errmsg, _("Error fetching row %d: ERR=%s\n"), i, sql_strerror(mdb));
387                Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
388                stat = 0;
389                break;
390             } else {
391                if (*VolumeNames[0] != 0) {
392                   pm_strcat(VolumeNames, "|");
393                }
394                pm_strcat(VolumeNames, row[0]);
395             }
396          }
397       }
398       sql_free_result(mdb);
399    } else {
400       Mmsg(mdb->errmsg, _("No Volume for JobId %d found in Catalog.\n"), JobId);
401    }
402    db_unlock(mdb);
403    return stat;
404 }
405
406 /**
407  * Find Volume parameters for a give JobId
408  *  Returns: 0 on error or no Volumes found
409  *           number of volumes on success
410  *           List of Volumes and start/end file/blocks (malloced structure!)
411  *
412  *  Returns: number of volumes on success
413  */
414 int db_get_job_volume_parameters(JCR *jcr, B_DB *mdb, JobId_t JobId, VOL_PARAMS **VolParams)
415 {
416    SQL_ROW row;
417    char ed1[50];
418    int stat = 0;
419    int i;
420    VOL_PARAMS *Vols = NULL;
421
422    db_lock(mdb);
423    Mmsg(mdb->cmd,
424 "SELECT VolumeName,MediaType,FirstIndex,LastIndex,StartFile,"
425 "JobMedia.EndFile,StartBlock,JobMedia.EndBlock,"
426 "Slot,StorageId,InChanger"
427 " FROM JobMedia,Media WHERE JobMedia.JobId=%s"
428 " AND JobMedia.MediaId=Media.MediaId ORDER BY VolIndex,JobMediaId",
429         edit_int64(JobId, ed1));
430
431    Dmsg1(130, "VolNam=%s\n", mdb->cmd);
432    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
433       mdb->num_rows = sql_num_rows(mdb);
434       Dmsg1(200, "Num rows=%d\n", mdb->num_rows);
435       if (mdb->num_rows <= 0) {
436          Mmsg1(mdb->errmsg, _("No volumes found for JobId=%d\n"), JobId);
437          stat = 0;
438       } else {
439          stat = mdb->num_rows;
440          DBId_t *SId = NULL;
441          if (stat > 0) {
442             *VolParams = Vols = (VOL_PARAMS *)malloc(stat * sizeof(VOL_PARAMS));
443             SId = (DBId_t *)malloc(stat * sizeof(DBId_t));
444          }
445          for (i=0; i < stat; i++) {
446             if ((row = sql_fetch_row(mdb)) == NULL) {
447                Mmsg2(mdb->errmsg, _("Error fetching row %d: ERR=%s\n"), i, sql_strerror(mdb));
448                Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
449                stat = 0;
450                break;
451             } else {
452                DBId_t StorageId;
453                uint32_t StartBlock, EndBlock, StartFile, EndFile;
454                bstrncpy(Vols[i].VolumeName, row[0], MAX_NAME_LENGTH);
455                bstrncpy(Vols[i].MediaType, row[1], MAX_NAME_LENGTH);
456                Vols[i].FirstIndex = str_to_uint64(row[2]);
457                Vols[i].LastIndex = str_to_uint64(row[3]);
458                StartFile = str_to_uint64(row[4]);
459                EndFile = str_to_uint64(row[5]);
460                StartBlock = str_to_uint64(row[6]);
461                EndBlock = str_to_uint64(row[7]);
462                Vols[i].StartAddr = (((uint64_t)StartFile)<<32) | StartBlock;
463                Vols[i].EndAddr =   (((uint64_t)EndFile)<<32) | EndBlock;
464                Vols[i].Slot = str_to_uint64(row[8]);
465                StorageId = str_to_uint64(row[9]);
466                Vols[i].InChanger = str_to_uint64(row[10]);
467                Vols[i].Storage[0] = 0;
468                SId[i] = StorageId;
469             }
470          }
471          for (i=0; i < stat; i++) {
472             if (SId[i] != 0) {
473                Mmsg(mdb->cmd, "SELECT Name from Storage WHERE StorageId=%s",
474                   edit_int64(SId[i], ed1));
475                if (QUERY_DB(jcr, mdb, mdb->cmd)) {
476                   if ((row = sql_fetch_row(mdb)) && row[0]) {
477                      bstrncpy(Vols[i].Storage, row[0], MAX_NAME_LENGTH);
478                   }
479                }
480             }
481          }
482          if (SId) {
483             free(SId);
484          }
485       }
486       sql_free_result(mdb);
487    }
488    db_unlock(mdb);
489    return stat;
490 }
491
492
493
494 /**
495  * Get the number of pool records
496  *
497  * Returns: -1 on failure
498  *          number on success
499  */
500 int db_get_num_pool_records(JCR *jcr, B_DB *mdb)
501 {
502    int stat = 0;
503
504    db_lock(mdb);
505    Mmsg(mdb->cmd, "SELECT count(*) from Pool");
506    stat = get_sql_record_max(jcr, mdb);
507    db_unlock(mdb);
508    return stat;
509 }
510
511 /**
512  * This function returns a list of all the Pool record ids.
513  *  The caller must free ids if non-NULL.
514  *
515  *  Returns 0: on failure
516  *          1: on success
517  */
518 int db_get_pool_ids(JCR *jcr, B_DB *mdb, int *num_ids, uint32_t *ids[])
519 {
520    SQL_ROW row;
521    int stat = 0;
522    int i = 0;
523    uint32_t *id;
524
525    db_lock(mdb);
526    *ids = NULL;
527    Mmsg(mdb->cmd, "SELECT PoolId FROM Pool");
528    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
529       *num_ids = sql_num_rows(mdb);
530       if (*num_ids > 0) {
531          id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
532          while ((row = sql_fetch_row(mdb)) != NULL) {
533             id[i++] = str_to_uint64(row[0]);
534          }
535          *ids = id;
536       }
537       sql_free_result(mdb);
538       stat = 1;
539    } else {
540       Mmsg(mdb->errmsg, _("Pool id select failed: ERR=%s\n"), sql_strerror(mdb));
541       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
542       stat = 0;
543    }
544    db_unlock(mdb);
545    return stat;
546 }
547
548 /**
549  * This function returns a list of all the Client record ids.
550  *  The caller must free ids if non-NULL.
551  *
552  *  Returns 0: on failure
553  *          1: on success
554  */
555 int db_get_client_ids(JCR *jcr, B_DB *mdb, int *num_ids, uint32_t *ids[])
556 {
557    SQL_ROW row;
558    int stat = 0;
559    int i = 0;
560    uint32_t *id;
561
562    db_lock(mdb);
563    *ids = NULL;
564    Mmsg(mdb->cmd, "SELECT ClientId FROM Client ORDER BY Name");
565    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
566       *num_ids = sql_num_rows(mdb);
567       if (*num_ids > 0) {
568          id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
569          while ((row = sql_fetch_row(mdb)) != NULL) {
570             id[i++] = str_to_uint64(row[0]);
571          }
572          *ids = id;
573       }
574       sql_free_result(mdb);
575       stat = 1;
576    } else {
577       Mmsg(mdb->errmsg, _("Client id select failed: ERR=%s\n"), sql_strerror(mdb));
578       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
579       stat = 0;
580    }
581    db_unlock(mdb);
582    return stat;
583 }
584
585
586
587 /**
588  * Get Pool Record
589  * If the PoolId is non-zero, we get its record,
590  *  otherwise, we search on the PoolName
591  *
592  * Returns: false on failure
593  *          true on success
594  */
595 bool db_get_pool_record(JCR *jcr, B_DB *mdb, POOL_DBR *pdbr)
596 {
597    SQL_ROW row;
598    bool ok = false;
599    char ed1[50];
600
601    db_lock(mdb);
602    if (pdbr->PoolId != 0) {               /* find by id */
603       Mmsg(mdb->cmd,
604 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
605 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
606 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId,"
607 "ActionOnPurge FROM Pool WHERE Pool.PoolId=%s", 
608          edit_int64(pdbr->PoolId, ed1));
609    } else {                           /* find by name */
610       Mmsg(mdb->cmd,
611 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
612 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
613 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId,"
614 "ActionOnPurge FROM Pool WHERE Pool.Name='%s'", 
615          pdbr->Name);
616    }
617    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
618       mdb->num_rows = sql_num_rows(mdb);
619       if (mdb->num_rows > 1) {
620          char ed1[30];
621          Mmsg1(mdb->errmsg, _("More than one Pool!: %s\n"),
622             edit_uint64(mdb->num_rows, ed1));
623          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
624       } else if (mdb->num_rows == 1) {
625          if ((row = sql_fetch_row(mdb)) == NULL) {
626             Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
627             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
628          } else {
629             pdbr->PoolId = str_to_int64(row[0]);
630             bstrncpy(pdbr->Name, row[1]!=NULL?row[1]:"", sizeof(pdbr->Name));
631             pdbr->NumVols = str_to_int64(row[2]);
632             pdbr->MaxVols = str_to_int64(row[3]);
633             pdbr->UseOnce = str_to_int64(row[4]);
634             pdbr->UseCatalog = str_to_int64(row[5]);
635             pdbr->AcceptAnyVolume = str_to_int64(row[6]);
636             pdbr->AutoPrune = str_to_int64(row[7]);
637             pdbr->Recycle = str_to_int64(row[8]);
638             pdbr->VolRetention = str_to_int64(row[9]);
639             pdbr->VolUseDuration = str_to_int64(row[10]);
640             pdbr->MaxVolJobs = str_to_int64(row[11]);
641             pdbr->MaxVolFiles = str_to_int64(row[12]);
642             pdbr->MaxVolBytes = str_to_uint64(row[13]);
643             bstrncpy(pdbr->PoolType, row[14]!=NULL?row[14]:"", sizeof(pdbr->PoolType));
644             pdbr->LabelType = str_to_int64(row[15]);
645             bstrncpy(pdbr->LabelFormat, row[16]!=NULL?row[16]:"", sizeof(pdbr->LabelFormat));
646             pdbr->RecyclePoolId = str_to_int64(row[17]);
647             pdbr->ScratchPoolId = str_to_int64(row[18]);
648             pdbr->ActionOnPurge = str_to_int32(row[19]);
649             ok = true;
650          }
651       }
652       sql_free_result(mdb);
653    }
654    if (ok) {
655       uint32_t NumVols;
656       Mmsg(mdb->cmd, "SELECT count(*) from Media WHERE PoolId=%s",
657          edit_int64(pdbr->PoolId, ed1));
658       NumVols = get_sql_record_max(jcr, mdb);
659       Dmsg2(400, "Actual NumVols=%d Pool NumVols=%d\n", NumVols, pdbr->NumVols);
660       if (NumVols != pdbr->NumVols) {
661          pdbr->NumVols = NumVols;
662          db_update_pool_record(jcr, mdb, pdbr);
663       }
664    } else {
665       Mmsg(mdb->errmsg, _("Pool record not found in Catalog.\n"));
666    }
667    db_unlock(mdb);
668    return ok;
669 }
670
671 /**
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 /**
779  * Get FileSet Record
780  * If the FileSetId is non-zero, we get its record,
781  *  otherwise, we search on the name
782  *
783  * Returns: 0 on failure
784  *          id on success
785  */
786 int db_get_fileset_record(JCR *jcr, B_DB *mdb, FILESET_DBR *fsr)
787 {
788    SQL_ROW row;
789    int stat = 0;
790    char ed1[50];
791
792    db_lock(mdb);
793    if (fsr->FileSetId != 0) {               /* find by id */
794       Mmsg(mdb->cmd,
795            "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
796            "WHERE FileSetId=%s", 
797            edit_int64(fsr->FileSetId, ed1));
798    } else {                           /* find by name */
799       Mmsg(mdb->cmd,
800            "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
801            "WHERE FileSet='%s' ORDER BY CreateTime DESC LIMIT 1", fsr->FileSet);
802    }
803
804    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
805       mdb->num_rows = sql_num_rows(mdb);
806       if (mdb->num_rows > 1) {
807          char ed1[30];
808          Mmsg1(mdb->errmsg, _("Error got %s FileSets but expected only one!\n"),
809             edit_uint64(mdb->num_rows, ed1));
810          sql_data_seek(mdb, mdb->num_rows-1);
811       }
812       if ((row = sql_fetch_row(mdb)) == NULL) {
813          Mmsg1(mdb->errmsg, _("FileSet record \"%s\" not found.\n"), fsr->FileSet);
814       } else {
815          fsr->FileSetId = str_to_int64(row[0]);
816          bstrncpy(fsr->FileSet, row[1]!=NULL?row[1]:"", sizeof(fsr->FileSet));
817          bstrncpy(fsr->MD5, row[2]!=NULL?row[2]:"", sizeof(fsr->MD5));
818          bstrncpy(fsr->cCreateTime, row[3]!=NULL?row[3]:"", sizeof(fsr->cCreateTime));
819          stat = fsr->FileSetId;
820       }
821       sql_free_result(mdb);
822    } else {
823       Mmsg(mdb->errmsg, _("FileSet record not found in Catalog.\n"));
824    }
825    db_unlock(mdb);
826    return stat;
827 }
828
829
830 /**
831  * Get the number of Media records
832  *
833  * Returns: -1 on failure
834  *          number on success
835  */
836 int db_get_num_media_records(JCR *jcr, B_DB *mdb)
837 {
838    int stat = 0;
839
840    db_lock(mdb);
841    Mmsg(mdb->cmd, "SELECT count(*) from Media");
842    stat = get_sql_record_max(jcr, mdb);
843    db_unlock(mdb);
844    return stat;
845 }
846
847 /**
848  * This function returns a list of all the Media record ids for
849  *     the current Pool, the correct Media Type, Recyle, Enabled, StorageId, VolBytes
850  *     VolumeName if specified
851  *  The caller must free ids if non-NULL.
852  *
853  *  Returns false: on failure
854  *          true:  on success
855  */
856 bool db_get_media_ids(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr, int *num_ids, uint32_t *ids[])
857 {
858    SQL_ROW row;
859    int i = 0;
860    uint32_t *id;
861    char ed1[50];
862    bool ok = false;
863    char buf[MAX_NAME_LENGTH*3]; /* Can contain MAX_NAME_LENGTH*2+1 + AND ....='' */
864    char esc[MAX_NAME_LENGTH*2+1];
865
866    db_lock(mdb);
867    *ids = NULL;
868
869    Mmsg(mdb->cmd, "SELECT DISTINCT MediaId FROM Media WHERE Recycle=%d AND Enabled=%d ",
870         mr->Recycle, mr->Enabled);
871
872    if (*mr->MediaType) {
873       db_escape_string(jcr, mdb, esc, mr->MediaType, strlen(mr->MediaType));
874       bsnprintf(buf, sizeof(buf), "AND MediaType='%s' ", esc);
875       pm_strcat(mdb->cmd, buf);
876    }
877
878    if (mr->StorageId) {
879       bsnprintf(buf, sizeof(buf), "AND StorageId=%s ", edit_uint64(mr->StorageId, ed1));
880       pm_strcat(mdb->cmd, buf);
881    }
882
883    if (mr->PoolId) {
884       bsnprintf(buf, sizeof(buf), "AND PoolId=%s ", edit_uint64(mr->PoolId, ed1));
885       pm_strcat(mdb->cmd, buf);
886    }
887
888    if (mr->VolBytes) {
889       bsnprintf(buf, sizeof(buf), "AND VolBytes > %s ", edit_uint64(mr->VolBytes, ed1));
890       pm_strcat(mdb->cmd, buf);
891    }
892
893    if (*mr->VolumeName) {
894       db_escape_string(jcr, mdb, esc, mr->VolumeName, strlen(mr->VolumeName));
895       bsnprintf(buf, sizeof(buf), "AND VolumeName = '%s' ", esc);
896       pm_strcat(mdb->cmd, buf);
897    }
898
899    if (*mr->VolStatus) {
900       db_escape_string(jcr, mdb, esc, mr->VolStatus, strlen(mr->VolStatus));
901       bsnprintf(buf, sizeof(buf), "AND VolStatus = '%s' ", esc);
902       pm_strcat(mdb->cmd, buf);
903    }
904
905    Dmsg1(100, "q=%s\n", mdb->cmd);
906
907    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
908       *num_ids = sql_num_rows(mdb);
909       if (*num_ids > 0) {
910          id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
911          while ((row = sql_fetch_row(mdb)) != NULL) {
912             id[i++] = str_to_uint64(row[0]);
913          }
914          *ids = id;
915       }
916       sql_free_result(mdb);
917       ok = true;
918    } else {
919       Mmsg(mdb->errmsg, _("Media id select failed: ERR=%s\n"), sql_strerror(mdb));
920       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
921       ok = false;
922    }
923    db_unlock(mdb);
924    return ok;
925 }
926
927
928 /**
929  * This function returns a list of all the DBIds that are returned
930  *   for the query.
931  *
932  *  Returns false: on failure
933  *          true:  on success
934  */
935 bool db_get_query_dbids(JCR *jcr, B_DB *mdb, POOL_MEM &query, dbid_list &ids)
936 {
937    SQL_ROW row;
938    int i = 0;
939    bool ok = false;
940
941    db_lock(mdb);
942    ids.num_ids = 0;
943    if (QUERY_DB(jcr, mdb, query.c_str())) {
944       ids.num_ids = sql_num_rows(mdb);
945       if (ids.num_ids > 0) {
946          if (ids.max_ids < ids.num_ids) {
947             free(ids.DBId);
948             ids.DBId = (DBId_t *)malloc(ids.num_ids * sizeof(DBId_t));
949          }
950          while ((row = sql_fetch_row(mdb)) != NULL) {
951             ids.DBId[i++] = str_to_uint64(row[0]);
952          }
953       }
954       sql_free_result(mdb);
955       ok = true;
956    } else {
957       Mmsg(mdb->errmsg, _("query dbids failed: ERR=%s\n"), sql_strerror(mdb));
958       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
959       ok = false;
960    }
961    db_unlock(mdb);
962    return ok;
963 }
964
965 /**
966  * Get Media Record
967  *
968  * Returns: false: on failure
969  *          true:  on success
970  */
971 bool db_get_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr)
972 {
973    SQL_ROW row;
974    char ed1[50];
975    bool ok = false;
976
977    db_lock(mdb);
978    if (mr->MediaId == 0 && mr->VolumeName[0] == 0) {
979       Mmsg(mdb->cmd, "SELECT count(*) from Media");
980       mr->MediaId = get_sql_record_max(jcr, mdb);
981       db_unlock(mdb);
982       return true;
983    }
984    if (mr->MediaId != 0) {               /* find by id */
985       Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
986          "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
987          "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
988          "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
989          "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
990          "Enabled,LocationId,RecycleCount,InitialWrite,"
991          "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge "
992          "FROM Media WHERE MediaId=%s", 
993          edit_int64(mr->MediaId, ed1));
994    } else {                           /* find by name */
995       Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
996          "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
997          "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
998          "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
999          "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
1000          "Enabled,LocationId,RecycleCount,InitialWrite,"
1001          "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge "
1002          "FROM Media WHERE VolumeName='%s'", mr->VolumeName);
1003    }
1004
1005    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
1006       char ed1[50];
1007       mdb->num_rows = sql_num_rows(mdb);
1008       if (mdb->num_rows > 1) {
1009          Mmsg1(mdb->errmsg, _("More than one Volume!: %s\n"),
1010             edit_uint64(mdb->num_rows, ed1));
1011          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
1012       } else if (mdb->num_rows == 1) {
1013          if ((row = sql_fetch_row(mdb)) == NULL) {
1014             Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
1015             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
1016          } else {
1017             /* return values */
1018             mr->MediaId = str_to_int64(row[0]);
1019             bstrncpy(mr->VolumeName, row[1]!=NULL?row[1]:"", sizeof(mr->VolumeName));
1020             mr->VolJobs = str_to_int64(row[2]);
1021             mr->VolFiles = str_to_int64(row[3]);
1022             mr->VolBlocks = str_to_int64(row[4]);
1023             mr->VolBytes = str_to_uint64(row[5]);
1024             mr->VolMounts = str_to_int64(row[6]);
1025             mr->VolErrors = str_to_int64(row[7]);
1026             mr->VolWrites = str_to_int64(row[8]);
1027             mr->MaxVolBytes = str_to_uint64(row[9]);
1028             mr->VolCapacityBytes = str_to_uint64(row[10]);
1029             bstrncpy(mr->MediaType, row[11]!=NULL?row[11]:"", sizeof(mr->MediaType));
1030             bstrncpy(mr->VolStatus, row[12]!=NULL?row[12]:"", sizeof(mr->VolStatus));
1031             mr->PoolId = str_to_int64(row[13]);
1032             mr->VolRetention = str_to_uint64(row[14]);
1033             mr->VolUseDuration = str_to_uint64(row[15]);
1034             mr->MaxVolJobs = str_to_int64(row[16]);
1035             mr->MaxVolFiles = str_to_int64(row[17]);
1036             mr->Recycle = str_to_int64(row[18]);
1037             mr->Slot = str_to_int64(row[19]);
1038             bstrncpy(mr->cFirstWritten, row[20]!=NULL?row[20]:"", sizeof(mr->cFirstWritten));
1039             mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten);
1040             bstrncpy(mr->cLastWritten, row[21]!=NULL?row[21]:"", sizeof(mr->cLastWritten));
1041             mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
1042             mr->InChanger = str_to_uint64(row[22]);
1043             mr->EndFile = str_to_uint64(row[23]);
1044             mr->EndBlock = str_to_uint64(row[24]);
1045             mr->VolParts = str_to_int64(row[25]);
1046             mr->LabelType = str_to_int64(row[26]);
1047             bstrncpy(mr->cLabelDate, row[27]!=NULL?row[27]:"", sizeof(mr->cLabelDate));
1048             mr->LabelDate = (time_t)str_to_utime(mr->cLabelDate);
1049             mr->StorageId = str_to_int64(row[28]);
1050             mr->Enabled = str_to_int64(row[29]);
1051             mr->LocationId = str_to_int64(row[30]);
1052             mr->RecycleCount = str_to_int64(row[31]);
1053             bstrncpy(mr->cInitialWrite, row[32]!=NULL?row[32]:"", sizeof(mr->cInitialWrite));
1054             mr->InitialWrite = (time_t)str_to_utime(mr->cInitialWrite);
1055             mr->ScratchPoolId = str_to_int64(row[33]);
1056             mr->RecyclePoolId = str_to_int64(row[34]);
1057             mr->VolReadTime = str_to_int64(row[35]);
1058             mr->VolWriteTime = str_to_int64(row[36]);
1059             mr->ActionOnPurge = str_to_int32(row[37]);
1060             
1061             ok = true;
1062          }
1063       } else {
1064          if (mr->MediaId != 0) {
1065             Mmsg1(mdb->errmsg, _("Media record MediaId=%s not found.\n"), 
1066                edit_int64(mr->MediaId, ed1));
1067          } else {
1068             Mmsg1(mdb->errmsg, _("Media record for Volume \"%s\" not found.\n"),
1069                   mr->VolumeName);
1070          }
1071       }
1072       sql_free_result(mdb);
1073    } else {
1074       if (mr->MediaId != 0) {
1075          Mmsg(mdb->errmsg, _("Media record for MediaId=%u not found in Catalog.\n"),
1076             mr->MediaId);
1077        } else {
1078          Mmsg(mdb->errmsg, _("Media record for Vol=%s not found in Catalog.\n"),
1079             mr->VolumeName);
1080    }   }
1081    db_unlock(mdb);
1082    return ok;
1083 }
1084
1085 /**
1086  * Find the last "accurate" backup state (that can take deleted files in
1087  * account)
1088  * 1) Get all files with jobid in list (F subquery)
1089  *    Get all files in BaseFiles with jobid in list
1090  * 2) Take only the last version of each file (Temp subquery) => accurate list
1091  *    is ok
1092  * 3) Join the result to file table to get fileindex, jobid and lstat information
1093  *
1094  * TODO: See if we can do the SORT only if needed (as an argument)
1095  */
1096 bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids, 
1097                       DB_RESULT_HANDLER *result_handler, void *ctx)
1098 {
1099    if (!*jobids) {
1100       db_lock(mdb);
1101       Mmsg(mdb->errmsg, _("ERR=JobIds are empty\n"));
1102       db_unlock(mdb);
1103       return false;
1104    }
1105    POOL_MEM buf(PM_MESSAGE);
1106          
1107 #define new_db_get_file_list
1108 #ifdef new_db_get_file_list
1109    POOL_MEM buf2(PM_MESSAGE);
1110    Mmsg(buf2, select_recent_version_with_basejob[db_type], 
1111         jobids, jobids, jobids, jobids);
1112    Mmsg(buf,
1113 "SELECT Path.Path, Filename.Name, Temp.FileIndex, Temp.JobId, LStat, MD5 "
1114  "FROM ( %s ) AS Temp "
1115  "JOIN Filename ON (Filename.FilenameId = Temp.FilenameId) "
1116  "JOIN Path ON (Path.PathId = Temp.PathId) "
1117 "WHERE FileIndex > 0 "
1118 "ORDER BY Temp.JobId, FileIndex ASC",/* Return sorted by JobId, */
1119                                      /* FileIndex for restore code */ 
1120         buf2.c_str());
1121 #else
1122    /*  
1123     * I am not sure that this works the same as the code in ua_restore.c but it
1124     *  is very similar. The accurate-test fails in a restore. Bad file count.
1125     */
1126    Mmsg(buf, uar_sel_files, jobids);
1127 #endif
1128
1129    return db_sql_query(mdb, buf.c_str(), result_handler, ctx);
1130 }
1131
1132 /**
1133  * This procedure gets the base jobid list used by jobids,
1134  */
1135 bool db_get_used_base_jobids(JCR *jcr, B_DB *mdb, 
1136                              POOLMEM *jobids, db_list_ctx *result)
1137 {
1138    POOL_MEM buf;
1139    Mmsg(buf,
1140  "SELECT DISTINCT BaseJobId "
1141  "  FROM Job JOIN BaseFiles USING (JobId) "
1142  " WHERE Job.HasBase = 1 "
1143  "   AND Job.JobId IN (%s) ", jobids);
1144    return db_sql_query(mdb, buf.c_str(), db_list_handler, result);
1145 }
1146
1147 /**
1148  * The decision do change an incr/diff was done before
1149  * Full : do nothing
1150  * Differential : get the last full id
1151  * Incremental : get the last full + last diff + last incr(s) ids
1152  *
1153  * If you specify jr->StartTime, it will be used to limit the search
1154  * in the time. (usually now) 
1155  *
1156  * TODO: look and merge from ua_restore.c
1157  */
1158 bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb, 
1159                             JOB_DBR *jr, db_list_ctx *jobids)
1160 {
1161    bool ret=false;
1162    char clientid[50], jobid[50], filesetid[50];
1163    char date[MAX_TIME_LENGTH];
1164    POOL_MEM query(PM_FNAME);
1165    
1166    /* Take the current time as upper limit if nothing else specified */
1167    utime_t StartTime = (jr->StartTime)?jr->StartTime:time(NULL);
1168
1169    bstrutime(date, sizeof(date),  StartTime + 1);
1170    jobids->list[0] = 0;
1171    jobids->count = 0;
1172
1173    /* First, find the last good Full backup for this job/client/fileset */
1174    Mmsg(query, 
1175 "CREATE TABLE btemp3%s AS "
1176  "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1177    "FROM Job JOIN FileSet USING (FileSetId) "
1178   "WHERE ClientId = %s "
1179     "AND Level='F' AND JobStatus IN ('T','W') AND Type='B' "
1180     "AND StartTime<'%s' "
1181     "AND FileSet.FileSet=(SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1182   "ORDER BY Job.JobTDate DESC LIMIT 1",
1183         edit_uint64(jcr->JobId, jobid),
1184         edit_uint64(jr->ClientId, clientid),
1185         date,
1186         edit_uint64(jr->FileSetId, filesetid));
1187
1188    if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
1189       goto bail_out;
1190    }
1191
1192    if (jr->JobLevel == L_INCREMENTAL || jr->JobLevel == L_VIRTUAL_FULL) {
1193       /* Now, find the last differential backup after the last full */
1194       Mmsg(query, 
1195 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
1196  "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1197    "FROM Job JOIN FileSet USING (FileSetId) "
1198   "WHERE ClientId = %s "
1199     "AND Level='D' AND JobStatus IN ('T','W') AND Type='B' "
1200     "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
1201     "AND StartTime < '%s' "
1202     "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1203   "ORDER BY Job.JobTDate DESC LIMIT 1 ",
1204            jobid,
1205            clientid,
1206            jobid,
1207            date,
1208            filesetid);
1209
1210       if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
1211          goto bail_out;
1212       }
1213
1214       /* We just have to take all incremental after the last Full/Diff */
1215       Mmsg(query, 
1216 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
1217  "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1218    "FROM Job JOIN FileSet USING (FileSetId) "
1219   "WHERE ClientId = %s "
1220     "AND Level='I' AND JobStatus IN ('T','W') AND Type='B' "
1221     "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
1222     "AND StartTime < '%s' "
1223     "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1224   "ORDER BY Job.JobTDate DESC ",
1225            jobid,
1226            clientid,
1227            jobid,
1228            date,
1229            filesetid);
1230       if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
1231          goto bail_out;
1232       }
1233    }
1234
1235    /* build a jobid list ie: 1,2,3,4 */
1236    Mmsg(query, "SELECT JobId FROM btemp3%s ORDER by JobTDate", jobid);
1237    db_sql_query(mdb, query.c_str(), db_list_handler, jobids);
1238    Dmsg1(1, "db_accurate_get_jobids=%s\n", jobids->list);
1239    ret = true;
1240
1241 bail_out:
1242    Mmsg(query, "DROP TABLE btemp3%s", jobid);
1243    db_sql_query(mdb, query.c_str(), NULL, NULL);
1244
1245    return ret;
1246 }
1247
1248 bool db_get_base_file_list(JCR *jcr, B_DB *mdb,
1249                            DB_RESULT_HANDLER *result_handler, void *ctx)
1250 {
1251    POOL_MEM buf(PM_MESSAGE);
1252          
1253    Mmsg(buf,
1254  "SELECT Path, Name, FileIndex, JobId, LStat, MD5 "
1255    "FROM new_basefile%lld ORDER BY JobId, FileIndex ASC",
1256         (uint64_t) jcr->JobId);
1257
1258    return db_sql_query(mdb, buf.c_str(), result_handler, ctx);
1259 }
1260
1261 bool db_get_base_jobid(JCR *jcr, B_DB *mdb, JOB_DBR *jr, JobId_t *jobid)
1262 {
1263    POOL_MEM query(PM_FNAME);
1264    utime_t StartTime;
1265    db_int64_ctx lctx;
1266    char date[MAX_TIME_LENGTH];
1267    bool ret=false;
1268    *jobid = 0;
1269
1270 // char clientid[50], filesetid[50];
1271
1272    StartTime = (jr->StartTime)?jr->StartTime:time(NULL);
1273    bstrutime(date, sizeof(date),  StartTime + 1);
1274
1275    /* we can take also client name, fileset, etc... */
1276
1277    Mmsg(query,
1278  "SELECT JobId, Job, StartTime, EndTime, JobTDate, PurgedFiles "
1279    "FROM Job "
1280 // "JOIN FileSet USING (FileSetId) JOIN Client USING (ClientId) "
1281   "WHERE Job.Name = '%s' "
1282     "AND Level='B' AND JobStatus IN ('T','W') AND Type='B' "
1283 //    "AND FileSet.FileSet= '%s' "
1284 //    "AND Client.Name = '%s' "
1285     "AND StartTime<'%s' "
1286   "ORDER BY Job.JobTDate DESC LIMIT 1",
1287         jr->Name,
1288 //      edit_uint64(jr->ClientId, clientid),
1289 //      edit_uint64(jr->FileSetId, filesetid));
1290         date);
1291
1292    Dmsg1(10, "db_get_base_jobid q=%s\n", query.c_str());
1293    if (!db_sql_query(mdb, query.c_str(), db_int64_handler, &lctx)) {
1294       goto bail_out;
1295    }
1296    *jobid = (JobId_t) lctx.value;
1297
1298    Dmsg1(10, "db_get_base_jobid=%lld\n", *jobid);
1299    ret = true;
1300
1301 bail_out:
1302    return ret;
1303 }
1304
1305 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI */