]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql_get.c
ebl Add a busy handler to use multi db connection with sqlite
[bacula/bacula] / bacula / src / cats / sql_get.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2007 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 plus additions
11    that are listed 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 John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  * Bacula Catalog Database Get record interface routines
30  *  Note, these routines generally get a record by id or
31  *        by name.  If more logic is involved, the routine
32  *        should be in find.c
33  *
34  *    Kern Sibbald, March 2000
35  *
36  *    Version $Id$
37  */
38
39
40 /* The following is necessary so that we do not include
41  * the dummy external definition of DB.
42  */
43 #define __SQL_C                       /* indicate that this is sql.c */
44
45 #include "bacula.h"
46 #include "cats.h"
47
48 #if    HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL
49
50 /* -----------------------------------------------------------------------
51  *
52  *   Generic Routines (or almost generic)
53  *
54  * -----------------------------------------------------------------------
55  */
56
57 /* Forward referenced functions */
58 static int db_get_file_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, FILE_DBR *fdbr);
59 static int db_get_filename_record(JCR *jcr, B_DB *mdb);
60 static int db_get_path_record(JCR *jcr, B_DB *mdb);
61
62
63 /*
64  * Given a full filename (with path), look up the File record
65  * (with attributes) in the database.
66  *
67  *  Returns: 0 on failure
68  *           1 on success with the File record in FILE_DBR
69  */
70 int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JOB_DBR *jr, FILE_DBR *fdbr)
71 {
72    int stat;
73    Dmsg1(100, "db_get_file_att_record fname=%s \n", fname);
74
75    db_lock(mdb);
76    split_path_and_file(jcr, mdb, fname);
77
78    fdbr->FilenameId = db_get_filename_record(jcr, mdb);
79
80    fdbr->PathId = db_get_path_record(jcr, mdb);
81
82    stat = db_get_file_record(jcr, mdb, jr, fdbr);
83
84    db_unlock(mdb);
85
86    return stat;
87 }
88
89
90 /*
91  * Get a File record
92  * Returns: 0 on failure
93  *          1 on success
94  *
95  *  DO NOT use Jmsg in this routine.
96  *
97  *  Note in this routine, we do not use Jmsg because it may be
98  *    called to get attributes of a non-existent file, which is
99  *    "normal" if a new file is found during Verify.
100  */
101 static
102 int db_get_file_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, FILE_DBR *fdbr)
103 {
104    SQL_ROW row;
105    int stat = 0;
106    char ed1[50], ed2[50], ed3[50];
107
108    if (jcr->JobLevel == L_VERIFY_DISK_TO_CATALOG) {
109    Mmsg(mdb->cmd,
110 "SELECT FileId, LStat, MD5 FROM File,Job WHERE "
111 "File.JobId=Job.JobId AND File.PathId=%s AND "
112 "File.FilenameId=%s AND Job.Type='B' AND Job.JobSTATUS='T' AND "
113 "ClientId=%s ORDER BY StartTime DESC LIMIT 1",
114       edit_int64(fdbr->PathId, ed1), 
115       edit_int64(fdbr->FilenameId, ed2), 
116       edit_int64(jr->ClientId,ed3));
117
118    } else {
119       Mmsg(mdb->cmd,
120 "SELECT FileId, LStat, MD5 FROM File WHERE File.JobId=%s AND File.PathId=%s AND "
121 "File.FilenameId=%s", 
122       edit_int64(fdbr->JobId, ed1), 
123       edit_int64(fdbr->PathId, ed2), 
124       edit_int64(fdbr->FilenameId,ed3));
125    }
126    Dmsg3(050, "Get_file_record JobId=%u FilenameId=%u PathId=%u\n",
127       fdbr->JobId, fdbr->FilenameId, fdbr->PathId);
128
129    Dmsg1(100, "Query=%s\n", mdb->cmd);
130
131    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
132       mdb->num_rows = sql_num_rows(mdb);
133       Dmsg1(050, "get_file_record num_rows=%d\n", (int)mdb->num_rows);
134       if (mdb->num_rows > 1) {
135          Mmsg1(mdb->errmsg, _("get_file_record want 1 got rows=%d\n"),
136             mdb->num_rows);
137       }
138       if (mdb->num_rows >= 1) {
139          if ((row = sql_fetch_row(mdb)) == NULL) {
140             Mmsg1(mdb->errmsg, _("Error fetching row: %s\n"), sql_strerror(mdb));
141          } else {
142             fdbr->FileId = (FileId_t)str_to_int64(row[0]);
143             bstrncpy(fdbr->LStat, row[1], sizeof(fdbr->LStat));
144             bstrncpy(fdbr->Digest, row[2], sizeof(fdbr->Digest));
145             stat = 1;
146          }
147       } else {
148          Mmsg2(mdb->errmsg, _("File record for PathId=%s FilenameId=%s not found.\n"),
149             edit_int64(fdbr->PathId, ed1), 
150             edit_int64(fdbr->FilenameId, ed2));
151       }
152       sql_free_result(mdb);
153    } else {
154       Mmsg(mdb->errmsg, _("File record not found in Catalog.\n"));
155    }
156    return stat;
157
158 }
159
160 /* Get Filename record
161  * Returns: 0 on failure
162  *          FilenameId on success
163  *
164  *   DO NOT use Jmsg in this routine (see notes for get_file_record)
165  */
166 static int db_get_filename_record(JCR *jcr, B_DB *mdb)
167 {
168    SQL_ROW row;
169    int FilenameId = 0;
170
171    mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->fnl+2);
172    db_escape_string(mdb->esc_name, mdb->fname, mdb->fnl);
173
174    Mmsg(mdb->cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", mdb->esc_name);
175    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
176       char ed1[30];
177       mdb->num_rows = sql_num_rows(mdb);
178       if (mdb->num_rows > 1) {
179          Mmsg2(mdb->errmsg, _("More than one Filename!: %s for file: %s\n"),
180             edit_uint64(mdb->num_rows, ed1), mdb->fname);
181          Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
182       }
183       if (mdb->num_rows >= 1) {
184          if ((row = sql_fetch_row(mdb)) == NULL) {
185             Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
186          } else {
187             FilenameId = str_to_int64(row[0]);
188             if (FilenameId <= 0) {
189                Mmsg2(mdb->errmsg, _("Get DB Filename record %s found bad record: %d\n"),
190                   mdb->cmd, FilenameId);
191                FilenameId = 0;
192             }
193          }
194       } else {
195          Mmsg1(mdb->errmsg, _("Filename record: %s not found.\n"), mdb->fname);
196       }
197       sql_free_result(mdb);
198    } else {
199       Mmsg(mdb->errmsg, _("Filename record: %s not found in Catalog.\n"), mdb->fname);
200    }
201    return FilenameId;
202 }
203
204 /* Get path record
205  * Returns: 0 on failure
206  *          PathId on success
207  *
208  *   DO NOT use Jmsg in this routine (see notes for get_file_record)
209  */
210 static int db_get_path_record(JCR *jcr, B_DB *mdb)
211 {
212    SQL_ROW row;
213    uint32_t PathId = 0;
214
215    mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->pnl+2);
216    db_escape_string(mdb->esc_name, mdb->path, mdb->pnl);
217
218    if (mdb->cached_path_id != 0 && mdb->cached_path_len == mdb->pnl &&
219        strcmp(mdb->cached_path, mdb->path) == 0) {
220       return mdb->cached_path_id;
221    }
222
223    Mmsg(mdb->cmd, "SELECT PathId FROM Path WHERE Path='%s'", mdb->esc_name);
224
225    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
226       char ed1[30];
227       mdb->num_rows = sql_num_rows(mdb);
228       if (mdb->num_rows > 1) {
229          Mmsg2(mdb->errmsg, _("More than one Path!: %s for path: %s\n"),
230             edit_uint64(mdb->num_rows, ed1), mdb->path);
231          Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
232       }
233       /* Even if there are multiple paths, take the first one */
234       if (mdb->num_rows >= 1) {
235          if ((row = sql_fetch_row(mdb)) == NULL) {
236             Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
237          } else {
238             PathId = str_to_int64(row[0]);
239             if (PathId <= 0) {
240                Mmsg2(mdb->errmsg, _("Get DB path record %s found bad record: %s\n"),
241                   mdb->cmd, edit_int64(PathId, ed1));
242                PathId = 0;
243             } else {
244                /* Cache path */
245                if (PathId != mdb->cached_path_id) {
246                   mdb->cached_path_id = PathId;
247                   mdb->cached_path_len = mdb->pnl;
248                   pm_strcpy(mdb->cached_path, mdb->path);
249                }
250             }
251          }
252       } else {
253          Mmsg1(mdb->errmsg, _("Path record: %s not found.\n"), mdb->path);
254       }
255       sql_free_result(mdb);
256    } else {
257       Mmsg(mdb->errmsg, _("Path record: %s not found in Catalog.\n"), mdb->path);
258    }
259    return PathId;
260 }
261
262
263 /*
264  * Get Job record for given JobId or Job name
265  * Returns: false on failure
266  *          true  on success
267  */
268 bool db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr)
269 {
270    SQL_ROW row;
271    char ed1[50];
272
273    db_lock(mdb);
274    if (jr->JobId == 0) {
275       Mmsg(mdb->cmd, "SELECT VolSessionId,VolSessionTime,"
276 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
277 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId "
278 "FROM Job WHERE Job='%s'", jr->Job);
279     } else {
280       Mmsg(mdb->cmd, "SELECT VolSessionId,VolSessionTime,"
281 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
282 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId "
283 "FROM Job WHERE JobId=%s", 
284           edit_int64(jr->JobId, ed1));
285     }
286
287    if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
288       db_unlock(mdb);
289       return false;                   /* failed */
290    }
291    if ((row = sql_fetch_row(mdb)) == NULL) {
292       Mmsg1(mdb->errmsg, _("No Job found for JobId %s\n"), edit_int64(jr->JobId, ed1));
293       sql_free_result(mdb);
294       db_unlock(mdb);
295       return false;                   /* failed */
296    }
297
298    jr->VolSessionId = str_to_uint64(row[0]);
299    jr->VolSessionTime = str_to_uint64(row[1]);
300    jr->PoolId = str_to_int64(row[2]);
301    bstrncpy(jr->cStartTime, row[3]!=NULL?row[3]:"", sizeof(jr->cStartTime));
302    bstrncpy(jr->cEndTime, row[4]!=NULL?row[4]:"", sizeof(jr->cEndTime));
303    jr->JobFiles = str_to_int64(row[5]);
304    jr->JobBytes = str_to_int64(row[6]);
305    jr->JobTDate = str_to_int64(row[7]);
306    bstrncpy(jr->Job, row[8]!=NULL?row[8]:"", sizeof(jr->Job));
307    jr->JobStatus = (int)*row[9];
308    jr->JobType = (int)*row[10];
309    jr->JobLevel = (int)*row[11];
310    jr->ClientId = str_to_uint64(row[12]!=NULL?row[12]:(char *)"");
311    bstrncpy(jr->Name, row[13]!=NULL?row[13]:"", sizeof(jr->Name));
312    jr->PriorJobId = str_to_uint64(row[14]!=NULL?row[14]:(char *)"");
313    bstrncpy(jr->cRealEndTime, row[15]!=NULL?row[15]:"", sizeof(jr->cRealEndTime));
314    if (jr->JobId == 0) {
315       jr->JobId = str_to_int64(row[16]);
316    }
317    sql_free_result(mdb);
318
319    db_unlock(mdb);
320    return true;
321 }
322
323 /*
324  * Find VolumeNames for a given JobId
325  *  Returns: 0 on error or no Volumes found
326  *           number of volumes on success
327  *              Volumes are concatenated in VolumeNames
328  *              separated by a vertical bar (|) in the order
329  *              that they were written.
330  *
331  *  Returns: number of volumes on success
332  */
333 int db_get_job_volume_names(JCR *jcr, B_DB *mdb, JobId_t JobId, POOLMEM **VolumeNames)
334 {
335    SQL_ROW row;
336    char ed1[50];
337    int stat = 0;
338    int i;
339
340    db_lock(mdb);
341    /* Get one entry per VolumeName, but "sort" by VolIndex */
342    Mmsg(mdb->cmd,
343         "SELECT VolumeName,MAX(VolIndex) FROM JobMedia,Media WHERE "
344         "JobMedia.JobId=%s AND JobMedia.MediaId=Media.MediaId "
345         "GROUP BY VolumeName "
346         "ORDER BY 2 ASC", edit_int64(JobId,ed1));
347
348    Dmsg1(130, "VolNam=%s\n", mdb->cmd);
349    *VolumeNames[0] = 0;
350    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
351       mdb->num_rows = sql_num_rows(mdb);
352       Dmsg1(130, "Num rows=%d\n", mdb->num_rows);
353       if (mdb->num_rows <= 0) {
354          Mmsg1(mdb->errmsg, _("No volumes found for JobId=%d\n"), JobId);
355          stat = 0;
356       } else {
357          stat = mdb->num_rows;
358          for (i=0; i < stat; i++) {
359             if ((row = sql_fetch_row(mdb)) == NULL) {
360                Mmsg2(mdb->errmsg, _("Error fetching row %d: ERR=%s\n"), i, sql_strerror(mdb));
361                Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
362                stat = 0;
363                break;
364             } else {
365                if (*VolumeNames[0] != 0) {
366                   pm_strcat(VolumeNames, "|");
367                }
368                pm_strcat(VolumeNames, row[0]);
369             }
370          }
371       }
372       sql_free_result(mdb);
373    } else {
374       Mmsg(mdb->errmsg, _("No Volume for JobId %d found in Catalog.\n"), JobId);
375    }
376    db_unlock(mdb);
377    return stat;
378 }
379
380 /*
381  * Find Volume parameters for a give JobId
382  *  Returns: 0 on error or no Volumes found
383  *           number of volumes on success
384  *           List of Volumes and start/end file/blocks (malloced structure!)
385  *
386  *  Returns: number of volumes on success
387  */
388 int db_get_job_volume_parameters(JCR *jcr, B_DB *mdb, JobId_t JobId, VOL_PARAMS **VolParams)
389 {
390    SQL_ROW row;
391    char ed1[50];
392    int stat = 0;
393    int i;
394    VOL_PARAMS *Vols = NULL;
395
396    db_lock(mdb);
397    Mmsg(mdb->cmd,
398 "SELECT VolumeName,MediaType,FirstIndex,LastIndex,StartFile,"
399 "JobMedia.EndFile,StartBlock,JobMedia.EndBlock,Copy,"
400 "Slot,StorageId"
401 " FROM JobMedia,Media WHERE JobMedia.JobId=%s"
402 " AND JobMedia.MediaId=Media.MediaId ORDER BY VolIndex,JobMediaId",
403         edit_int64(JobId, ed1));
404
405    Dmsg1(130, "VolNam=%s\n", mdb->cmd);
406    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
407       mdb->num_rows = sql_num_rows(mdb);
408       Dmsg1(200, "Num rows=%d\n", mdb->num_rows);
409       if (mdb->num_rows <= 0) {
410          Mmsg1(mdb->errmsg, _("No volumes found for JobId=%d\n"), JobId);
411          stat = 0;
412       } else {
413          stat = mdb->num_rows;
414          DBId_t *SId = NULL;
415          if (stat > 0) {
416             *VolParams = Vols = (VOL_PARAMS *)malloc(stat * sizeof(VOL_PARAMS));
417             SId = (DBId_t *)malloc(stat * sizeof(DBId_t));
418          }
419          for (i=0; i < stat; i++) {
420             if ((row = sql_fetch_row(mdb)) == NULL) {
421                Mmsg2(mdb->errmsg, _("Error fetching row %d: ERR=%s\n"), i, sql_strerror(mdb));
422                Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
423                stat = 0;
424                break;
425             } else {
426                DBId_t StorageId;
427                bstrncpy(Vols[i].VolumeName, row[0], MAX_NAME_LENGTH);
428                bstrncpy(Vols[i].MediaType, row[1], MAX_NAME_LENGTH);
429                Vols[i].FirstIndex = str_to_uint64(row[2]);
430                Vols[i].LastIndex = str_to_uint64(row[3]);
431                Vols[i].StartFile = str_to_uint64(row[4]);
432                Vols[i].EndFile = str_to_uint64(row[5]);
433                Vols[i].StartBlock = str_to_uint64(row[6]);
434                Vols[i].EndBlock = str_to_uint64(row[7]);
435 //             Vols[i].Copy = str_to_uint64(row[8]);
436                Vols[i].Slot = str_to_uint64(row[9]);
437                StorageId = str_to_uint64(row[10]);
438                Vols[i].Storage[0] = 0;
439                SId[i] = StorageId;
440             }
441          }
442          for (i=0; i < stat; i++) {
443             if (SId[i] != 0) {
444                Mmsg(mdb->cmd, "SELECT Name from Storage WHERE StorageId=%s",
445                   edit_int64(SId[i], ed1));
446                if (QUERY_DB(jcr, mdb, mdb->cmd)) {
447                   if ((row = sql_fetch_row(mdb)) != NULL) {
448                      bstrncpy(Vols[i].Storage, row[0], MAX_NAME_LENGTH);
449                   }
450                }
451             }
452          }
453          if (SId) {
454             free(SId);
455          }
456       }
457       sql_free_result(mdb);
458    }
459    db_unlock(mdb);
460    return stat;
461 }
462
463
464
465 /*
466  * Get the number of pool records
467  *
468  * Returns: -1 on failure
469  *          number on success
470  */
471 int db_get_num_pool_records(JCR *jcr, B_DB *mdb)
472 {
473    int stat = 0;
474
475    db_lock(mdb);
476    Mmsg(mdb->cmd, "SELECT count(*) from Pool");
477    stat = get_sql_record_max(jcr, mdb);
478    db_unlock(mdb);
479    return stat;
480 }
481
482 /*
483  * This function returns a list of all the Pool record ids.
484  *  The caller must free ids if non-NULL.
485  *
486  *  Returns 0: on failure
487  *          1: on success
488  */
489 int db_get_pool_ids(JCR *jcr, B_DB *mdb, int *num_ids, uint32_t *ids[])
490 {
491    SQL_ROW row;
492    int stat = 0;
493    int i = 0;
494    uint32_t *id;
495
496    db_lock(mdb);
497    *ids = NULL;
498    Mmsg(mdb->cmd, "SELECT PoolId FROM Pool");
499    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
500       *num_ids = sql_num_rows(mdb);
501       if (*num_ids > 0) {
502          id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
503          while ((row = sql_fetch_row(mdb)) != NULL) {
504             id[i++] = str_to_uint64(row[0]);
505          }
506          *ids = id;
507       }
508       sql_free_result(mdb);
509       stat = 1;
510    } else {
511       Mmsg(mdb->errmsg, _("Pool id select failed: ERR=%s\n"), sql_strerror(mdb));
512       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
513       stat = 0;
514    }
515    db_unlock(mdb);
516    return stat;
517 }
518
519 /*
520  * This function returns a list of all the Client record ids.
521  *  The caller must free ids if non-NULL.
522  *
523  *  Returns 0: on failure
524  *          1: on success
525  */
526 int db_get_client_ids(JCR *jcr, B_DB *mdb, int *num_ids, uint32_t *ids[])
527 {
528    SQL_ROW row;
529    int stat = 0;
530    int i = 0;
531    uint32_t *id;
532
533    db_lock(mdb);
534    *ids = NULL;
535    Mmsg(mdb->cmd, "SELECT ClientId FROM Client");
536    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
537       *num_ids = sql_num_rows(mdb);
538       if (*num_ids > 0) {
539          id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
540          while ((row = sql_fetch_row(mdb)) != NULL) {
541             id[i++] = str_to_uint64(row[0]);
542          }
543          *ids = id;
544       }
545       sql_free_result(mdb);
546       stat = 1;
547    } else {
548       Mmsg(mdb->errmsg, _("Client id select failed: ERR=%s\n"), sql_strerror(mdb));
549       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
550       stat = 0;
551    }
552    db_unlock(mdb);
553    return stat;
554 }
555
556
557
558 /* Get Pool Record
559  * If the PoolId is non-zero, we get its record,
560  *  otherwise, we search on the PoolName
561  *
562  * Returns: false on failure
563  *          true on success
564  */
565 bool db_get_pool_record(JCR *jcr, B_DB *mdb, POOL_DBR *pdbr)
566 {
567    SQL_ROW row;
568    bool ok = false;
569    char ed1[50];
570
571    db_lock(mdb);
572    if (pdbr->PoolId != 0) {               /* find by id */
573       Mmsg(mdb->cmd,
574 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
575 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
576 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId FROM Pool WHERE Pool.PoolId=%s", 
577          edit_int64(pdbr->PoolId, ed1));
578    } else {                           /* find by name */
579       Mmsg(mdb->cmd,
580 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
581 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
582 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId FROM Pool WHERE Pool.Name='%s'", 
583          pdbr->Name);
584    }
585
586    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
587       mdb->num_rows = sql_num_rows(mdb);
588       if (mdb->num_rows > 1) {
589          char ed1[30];
590          Mmsg1(mdb->errmsg, _("More than one Pool!: %s\n"),
591             edit_uint64(mdb->num_rows, ed1));
592          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
593       } else if (mdb->num_rows == 1) {
594          if ((row = sql_fetch_row(mdb)) == NULL) {
595             Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
596             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
597          } else {
598             pdbr->PoolId = str_to_int64(row[0]);
599             bstrncpy(pdbr->Name, row[1]!=NULL?row[1]:"", sizeof(pdbr->Name));
600             pdbr->NumVols = str_to_int64(row[2]);
601             pdbr->MaxVols = str_to_int64(row[3]);
602             pdbr->UseOnce = str_to_int64(row[4]);
603             pdbr->UseCatalog = str_to_int64(row[5]);
604             pdbr->AcceptAnyVolume = str_to_int64(row[6]);
605             pdbr->AutoPrune = str_to_int64(row[7]);
606             pdbr->Recycle = str_to_int64(row[8]);
607             pdbr->VolRetention = str_to_int64(row[9]);
608             pdbr->VolUseDuration = str_to_int64(row[10]);
609             pdbr->MaxVolJobs = str_to_int64(row[11]);
610             pdbr->MaxVolFiles = str_to_int64(row[12]);
611             pdbr->MaxVolBytes = str_to_uint64(row[13]);
612             bstrncpy(pdbr->PoolType, row[14]!=NULL?row[14]:"", sizeof(pdbr->PoolType));
613             pdbr->LabelType = str_to_int64(row[15]);
614             bstrncpy(pdbr->LabelFormat, row[16]!=NULL?row[16]:"", sizeof(pdbr->LabelFormat));
615             pdbr->RecyclePoolId = str_to_int64(row[17]);
616             ok = true;
617          }
618       }
619       sql_free_result(mdb);
620    }
621    if (ok) {
622       uint32_t NumVols;
623       Mmsg(mdb->cmd, "SELECT count(*) from Media WHERE PoolId=%s",
624          edit_int64(pdbr->PoolId, ed1));
625       NumVols = get_sql_record_max(jcr, mdb);
626       Dmsg2(400, "Actual NumVols=%d Pool NumVols=%d\n", NumVols, pdbr->NumVols);
627       if (NumVols != pdbr->NumVols) {
628          pdbr->NumVols = NumVols;
629          db_update_pool_record(jcr, mdb, pdbr);
630       }
631    } else {
632       Mmsg(mdb->errmsg, _("Pool record not found in Catalog.\n"));
633    }
634    db_unlock(mdb);
635    return ok;
636 }
637
638 /* Get Client Record
639  * If the ClientId is non-zero, we get its record,
640  *  otherwise, we search on the Client Name
641  *
642  * Returns: 0 on failure
643  *          1 on success
644  */
645 int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cdbr)
646 {
647    SQL_ROW row;
648    int stat = 0;
649    char ed1[50];
650
651    db_lock(mdb);
652    if (cdbr->ClientId != 0) {               /* find by id */
653       Mmsg(mdb->cmd,
654 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
655 "FROM Client WHERE Client.ClientId=%s", 
656         edit_int64(cdbr->ClientId, ed1));
657    } else {                           /* find by name */
658       Mmsg(mdb->cmd,
659 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
660 "FROM Client WHERE Client.Name='%s'", cdbr->Name);
661    }
662
663    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
664       mdb->num_rows = sql_num_rows(mdb);
665       if (mdb->num_rows > 1) {
666          Mmsg1(mdb->errmsg, _("More than one Client!: %s\n"),
667             edit_uint64(mdb->num_rows, ed1));
668          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
669       } else if (mdb->num_rows == 1) {
670          if ((row = sql_fetch_row(mdb)) == NULL) {
671             Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
672             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
673          } else {
674             cdbr->ClientId = str_to_int64(row[0]);
675             bstrncpy(cdbr->Name, row[1]!=NULL?row[1]:"", sizeof(cdbr->Name));
676             bstrncpy(cdbr->Uname, row[2]!=NULL?row[2]:"", sizeof(cdbr->Uname));
677             cdbr->AutoPrune = str_to_int64(row[3]);
678             cdbr->FileRetention = str_to_int64(row[4]);
679             cdbr->JobRetention = str_to_int64(row[5]);
680             stat = 1;
681          }
682       } else {
683          Mmsg(mdb->errmsg, _("Client record not found in Catalog.\n"));
684       }
685       sql_free_result(mdb);
686    } else {
687       Mmsg(mdb->errmsg, _("Client record not found in Catalog.\n"));
688    }
689    db_unlock(mdb);
690    return stat;
691 }
692
693 /*
694  * Get Counter Record
695  *
696  * Returns: 0 on failure
697  *          1 on success
698  */
699 int db_get_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr)
700 {
701    SQL_ROW row;
702
703    db_lock(mdb);
704    Mmsg(mdb->cmd, "SELECT MinValue,MaxValue,CurrentValue,WrapCounter "
705       "FROM Counters WHERE Counter='%s'", cr->Counter);
706
707    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
708       mdb->num_rows = sql_num_rows(mdb);
709
710       /* If more than one, report error, but return first row */
711       if (mdb->num_rows > 1) {
712          Mmsg1(mdb->errmsg, _("More than one Counter!: %d\n"), (int)(mdb->num_rows));
713          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
714       }
715       if (mdb->num_rows >= 1) {
716          if ((row = sql_fetch_row(mdb)) == NULL) {
717             Mmsg1(mdb->errmsg, _("error fetching Counter row: %s\n"), sql_strerror(mdb));
718             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
719             sql_free_result(mdb);
720             db_unlock(mdb);
721             return 0;
722          }
723          cr->MinValue = str_to_int64(row[0]);
724          cr->MaxValue = str_to_int64(row[1]);
725          cr->CurrentValue = str_to_int64(row[2]);
726          if (row[3]) {
727             bstrncpy(cr->WrapCounter, row[3], sizeof(cr->WrapCounter));
728          } else {
729             cr->WrapCounter[0] = 0;
730          }
731          sql_free_result(mdb);
732          db_unlock(mdb);
733          return 1;
734       }
735       sql_free_result(mdb);
736    } else {
737       Mmsg(mdb->errmsg, _("Counter record: %s not found in Catalog.\n"), cr->Counter);
738    }
739    db_unlock(mdb);
740    return 0;
741 }
742
743
744 /* Get FileSet Record
745  * If the FileSetId is non-zero, we get its record,
746  *  otherwise, we search on the name
747  *
748  * Returns: 0 on failure
749  *          id on success
750  */
751 int db_get_fileset_record(JCR *jcr, B_DB *mdb, FILESET_DBR *fsr)
752 {
753    SQL_ROW row;
754    int stat = 0;
755    char ed1[50];
756
757    db_lock(mdb);
758    if (fsr->FileSetId != 0) {               /* find by id */
759       Mmsg(mdb->cmd,
760            "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
761            "WHERE FileSetId=%s", 
762            edit_int64(fsr->FileSetId, ed1));
763    } else {                           /* find by name */
764       Mmsg(mdb->cmd,
765            "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
766            "WHERE FileSet='%s' ORDER BY CreateTime DESC LIMIT 1", fsr->FileSet);
767    }
768
769    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
770       mdb->num_rows = sql_num_rows(mdb);
771       if (mdb->num_rows > 1) {
772          char ed1[30];
773          Mmsg1(mdb->errmsg, _("Error got %s FileSets but expected only one!\n"),
774             edit_uint64(mdb->num_rows, ed1));
775          sql_data_seek(mdb, mdb->num_rows-1);
776       }
777       if ((row = sql_fetch_row(mdb)) == NULL) {
778          Mmsg1(mdb->errmsg, _("FileSet record \"%s\" not found.\n"), fsr->FileSet);
779       } else {
780          fsr->FileSetId = str_to_int64(row[0]);
781          bstrncpy(fsr->FileSet, row[1]!=NULL?row[1]:"", sizeof(fsr->FileSet));
782          bstrncpy(fsr->MD5, row[2]!=NULL?row[2]:"", sizeof(fsr->MD5));
783          bstrncpy(fsr->cCreateTime, row[3]!=NULL?row[3]:"", sizeof(fsr->cCreateTime));
784          stat = fsr->FileSetId;
785       }
786       sql_free_result(mdb);
787    } else {
788       Mmsg(mdb->errmsg, _("FileSet record not found in Catalog.\n"));
789    }
790    db_unlock(mdb);
791    return stat;
792 }
793
794
795 /*
796  * Get the number of Media records
797  *
798  * Returns: -1 on failure
799  *          number on success
800  */
801 int db_get_num_media_records(JCR *jcr, B_DB *mdb)
802 {
803    int stat = 0;
804
805    db_lock(mdb);
806    Mmsg(mdb->cmd, "SELECT count(*) from Media");
807    stat = get_sql_record_max(jcr, mdb);
808    db_unlock(mdb);
809    return stat;
810 }
811
812
813 /*
814  * This function returns a list of all the Media record ids for
815  *     the current Pool with the correct Media Type.
816  *  The caller must free ids if non-NULL.
817  *
818  *  Returns false: on failure
819  *          true:  on success
820  */
821 bool db_get_media_ids(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr, int *num_ids, uint32_t *ids[])
822 {
823    SQL_ROW row;
824    int i = 0;
825    uint32_t *id;
826    char ed1[50];
827    bool ok = false;
828
829    db_lock(mdb);
830    *ids = NULL;
831    Mmsg(mdb->cmd, "SELECT DISTINCT MediaId FROM Media WHERE PoolId=%s "
832          " AND MediaType='%s'",
833        edit_int64(mr->PoolId, ed1), mr->MediaType);
834    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
835       *num_ids = sql_num_rows(mdb);
836       if (*num_ids > 0) {
837          id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
838          while ((row = sql_fetch_row(mdb)) != NULL) {
839             id[i++] = str_to_uint64(row[0]);
840          }
841          *ids = id;
842       }
843       sql_free_result(mdb);
844       ok = true;
845    } else {
846       Mmsg(mdb->errmsg, _("Media id select failed: ERR=%s\n"), sql_strerror(mdb));
847       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
848       ok = false;
849    }
850    db_unlock(mdb);
851    return ok;
852 }
853
854
855 /* Get Media Record
856  *
857  * Returns: false: on failure
858  *          true:  on success
859  */
860 bool db_get_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr)
861 {
862    SQL_ROW row;
863    char ed1[50];
864    bool ok = false;
865
866    db_lock(mdb);
867    if (mr->MediaId == 0 && mr->VolumeName[0] == 0) {
868       Mmsg(mdb->cmd, "SELECT count(*) from Media");
869       mr->MediaId = get_sql_record_max(jcr, mdb);
870       db_unlock(mdb);
871       return true;
872    }
873    if (mr->MediaId != 0) {               /* find by id */
874       Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
875          "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
876          "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
877          "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
878          "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
879          "Enabled,LocationId,RecycleCount,InitialWrite,"
880          "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime "
881          "FROM Media WHERE MediaId=%s", 
882          edit_int64(mr->MediaId, ed1));
883    } else {                           /* find by name */
884       Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
885          "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
886          "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
887          "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
888          "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
889          "Enabled,LocationId,RecycleCount,InitialWrite,"
890          "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime "
891          "FROM Media WHERE VolumeName='%s'", mr->VolumeName);
892    }
893
894    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
895       char ed1[50];
896       mdb->num_rows = sql_num_rows(mdb);
897       if (mdb->num_rows > 1) {
898          Mmsg1(mdb->errmsg, _("More than one Volume!: %s\n"),
899             edit_uint64(mdb->num_rows, ed1));
900          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
901       } else if (mdb->num_rows == 1) {
902          if ((row = sql_fetch_row(mdb)) == NULL) {
903             Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
904             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
905          } else {
906             /* return values */
907             mr->MediaId = str_to_int64(row[0]);
908             bstrncpy(mr->VolumeName, row[1]!=NULL?row[1]:"", sizeof(mr->VolumeName));
909             mr->VolJobs = str_to_int64(row[2]);
910             mr->VolFiles = str_to_int64(row[3]);
911             mr->VolBlocks = str_to_int64(row[4]);
912             mr->VolBytes = str_to_uint64(row[5]);
913             mr->VolMounts = str_to_int64(row[6]);
914             mr->VolErrors = str_to_int64(row[7]);
915             mr->VolWrites = str_to_int64(row[8]);
916             mr->MaxVolBytes = str_to_uint64(row[9]);
917             mr->VolCapacityBytes = str_to_uint64(row[10]);
918             bstrncpy(mr->MediaType, row[11]!=NULL?row[11]:"", sizeof(mr->MediaType));
919             bstrncpy(mr->VolStatus, row[12]!=NULL?row[12]:"", sizeof(mr->VolStatus));
920             mr->PoolId = str_to_int64(row[13]);
921             mr->VolRetention = str_to_uint64(row[14]);
922             mr->VolUseDuration = str_to_uint64(row[15]);
923             mr->MaxVolJobs = str_to_int64(row[16]);
924             mr->MaxVolFiles = str_to_int64(row[17]);
925             mr->Recycle = str_to_int64(row[18]);
926             mr->Slot = str_to_int64(row[19]);
927             bstrncpy(mr->cFirstWritten, row[20]!=NULL?row[20]:"", sizeof(mr->cFirstWritten));
928             mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten);
929             bstrncpy(mr->cLastWritten, row[21]!=NULL?row[21]:"", sizeof(mr->cLastWritten));
930             mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
931             mr->InChanger = str_to_uint64(row[22]);
932             mr->EndFile = str_to_uint64(row[23]);
933             mr->EndBlock = str_to_uint64(row[24]);
934             mr->VolParts = str_to_int64(row[25]);
935             mr->LabelType = str_to_int64(row[26]);
936             bstrncpy(mr->cLabelDate, row[27]!=NULL?row[27]:"", sizeof(mr->cLabelDate));
937             mr->LabelDate = (time_t)str_to_utime(mr->cLabelDate);
938             mr->StorageId = str_to_int64(row[28]);
939             mr->Enabled = str_to_int64(row[29]);
940             mr->LocationId = str_to_int64(row[30]);
941             mr->RecycleCount = str_to_int64(row[31]);
942             bstrncpy(mr->cInitialWrite, row[32]!=NULL?row[32]:"", sizeof(mr->cInitialWrite));
943             mr->InitialWrite = (time_t)str_to_utime(mr->cInitialWrite);
944             mr->ScratchPoolId = str_to_int64(row[33]);
945             mr->RecyclePoolId = str_to_int64(row[34]);
946             mr->VolReadTime = str_to_int64(row[35]);
947             mr->VolWriteTime = str_to_int64(row[36]);
948             
949             ok = true;
950          }
951       } else {
952          if (mr->MediaId != 0) {
953             Mmsg1(mdb->errmsg, _("Media record MediaId=%s not found.\n"), 
954                edit_int64(mr->MediaId, ed1));
955          } else {
956             Mmsg1(mdb->errmsg, _("Media record for Volume \"%s\" not found.\n"),
957                   mr->VolumeName);
958          }
959       }
960       sql_free_result(mdb);
961    } else {
962       if (mr->MediaId != 0) {
963          Mmsg(mdb->errmsg, _("Media record for MediaId=%u not found in Catalog.\n"),
964             mr->MediaId);
965        } else {
966          Mmsg(mdb->errmsg, _("Media record for Vol=%s not found in Catalog.\n"),
967             mr->VolumeName);
968    }   }
969    db_unlock(mdb);
970    return ok;
971 }
972
973
974 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/