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