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