2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 Bacula® is a registered trademark of Kern Sibbald.
17 * Bacula Catalog Database Get record interface routines
18 * Note, these routines generally get a record by id or
19 * by name. If more logic is involved, the routine
22 * Written by Kern Sibbald, March 2000
28 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
34 /* -----------------------------------------------------------------------
36 * Generic Routines (or almost generic)
38 * -----------------------------------------------------------------------
41 /* Forward referenced functions */
42 static int db_get_file_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, FILE_DBR *fdbr);
43 static int db_get_filename_record(JCR *jcr, B_DB *mdb);
47 * Given a full filename (with path), look up the File record
48 * (with attributes) in the database.
50 * Returns: 0 on failure
51 * 1 on success with the File record in FILE_DBR
53 int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JOB_DBR *jr, FILE_DBR *fdbr)
56 Dmsg1(100, "db_get_file_att_record fname=%s \n", fname);
59 split_path_and_file(jcr, mdb, fname);
61 fdbr->FilenameId = db_get_filename_record(jcr, mdb);
63 fdbr->PathId = db_get_path_record(jcr, mdb);
65 stat = db_get_file_record(jcr, mdb, jr, fdbr);
75 * Returns: 0 on failure
78 * DO NOT use Jmsg in this routine.
80 * Note in this routine, we do not use Jmsg because it may be
81 * called to get attributes of a non-existent file, which is
82 * "normal" if a new file is found during Verify.
84 * The following is a bit of a kludge: because we always backup a
85 * directory entry, we can end up with two copies of the directory
86 * in the backup. One is when we encounter the directory and find
87 * we cannot recurse into it, and the other is when we find an
88 * explicit mention of the directory. This can also happen if the
89 * use includes the directory twice. In this case, Verify
90 * VolumeToCatalog fails because we have two copies in the catalog,
91 * and only the first one is marked (twice). So, when calling from Verify,
92 * VolumeToCatalog jr is not NULL and we know jr->FileIndex is the fileindex
93 * of the version of the directory/file we actually want and do
94 * a more explicit SQL search.
97 int db_get_file_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, FILE_DBR *fdbr)
101 char ed1[50], ed2[50], ed3[50];
104 if (jcr->getJobLevel() == L_VERIFY_DISK_TO_CATALOG) {
106 "SELECT FileId, LStat, MD5 FROM File,Job WHERE "
107 "File.JobId=Job.JobId AND File.PathId=%s AND "
108 "File.FilenameId=%s AND Job.Type='B' AND Job.JobStatus IN ('T','W') AND "
109 "ClientId=%s ORDER BY StartTime DESC LIMIT 1",
110 edit_int64(fdbr->PathId, ed1),
111 edit_int64(fdbr->FilenameId, ed2),
112 edit_int64(jr->ClientId,ed3));
113 } else if (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG) {
115 "SELECT FileId, LStat, MD5 FROM File WHERE File.JobId=%s AND File.PathId=%s AND "
116 "File.FilenameId=%s AND File.FileIndex=%u",
117 edit_int64(fdbr->JobId, ed1),
118 edit_int64(fdbr->PathId, ed2),
119 edit_int64(fdbr->FilenameId,ed3),
123 "SELECT FileId, LStat, MD5 FROM File WHERE File.JobId=%s AND File.PathId=%s AND "
124 "File.FilenameId=%s",
125 edit_int64(fdbr->JobId, ed1),
126 edit_int64(fdbr->PathId, ed2),
127 edit_int64(fdbr->FilenameId,ed3));
129 Dmsg3(450, "Get_file_record JobId=%u FilenameId=%u PathId=%u\n",
130 fdbr->JobId, fdbr->FilenameId, fdbr->PathId);
132 Dmsg1(100, "Query=%s\n", mdb->cmd);
134 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
135 num_rows = sql_num_rows(mdb);
136 Dmsg1(050, "get_file_record num_rows=%d\n", num_rows);
138 if ((row = sql_fetch_row(mdb)) == NULL) {
139 Mmsg1(mdb->errmsg, _("Error fetching row: %s\n"), sql_strerror(mdb));
141 fdbr->FileId = (FileId_t)str_to_int64(row[0]);
142 bstrncpy(fdbr->LStat, row[1], sizeof(fdbr->LStat));
143 bstrncpy(fdbr->Digest, row[2], sizeof(fdbr->Digest));
146 Mmsg3(mdb->errmsg, _("get_file_record want 1 got rows=%d PathId=%s FilenameId=%s\n"),
148 edit_int64(fdbr->PathId, ed1),
149 edit_int64(fdbr->FilenameId, ed2));
150 Dmsg1(000, "=== Problem! %s", mdb->errmsg);
154 Mmsg2(mdb->errmsg, _("File record for PathId=%s FilenameId=%s not found.\n"),
155 edit_int64(fdbr->PathId, ed1),
156 edit_int64(fdbr->FilenameId, ed2));
158 sql_free_result(mdb);
160 Mmsg(mdb->errmsg, _("File record not found in Catalog.\n"));
167 * Get Filename record
168 * Returns: 0 on failure
169 * FilenameId on success
171 * DO NOT use Jmsg in this routine (see notes for get_file_record)
173 static int db_get_filename_record(JCR *jcr, B_DB *mdb)
179 mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->fnl+2);
180 db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
182 Mmsg(mdb->cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", mdb->esc_name);
183 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
185 num_rows = sql_num_rows(mdb);
187 Mmsg2(mdb->errmsg, _("More than one Filename!: %s for file: %s\n"),
188 edit_uint64(num_rows, ed1), mdb->fname);
189 Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
192 if ((row = sql_fetch_row(mdb)) == NULL) {
193 Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
195 FilenameId = str_to_int64(row[0]);
196 if (FilenameId <= 0) {
197 Mmsg2(mdb->errmsg, _("Get DB Filename record %s found bad record: %d\n"),
198 mdb->cmd, FilenameId);
203 Mmsg1(mdb->errmsg, _("Filename record: %s not found.\n"), mdb->fname);
205 sql_free_result(mdb);
207 Mmsg(mdb->errmsg, _("Filename record: %s not found in Catalog.\n"), mdb->fname);
214 * Returns: 0 on failure
217 * DO NOT use Jmsg in this routine (see notes for get_file_record)
219 int db_get_path_record(JCR *jcr, B_DB *mdb)
225 mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->pnl+2);
226 db_escape_string(jcr, mdb, mdb->esc_name, mdb->path, mdb->pnl);
228 if (mdb->cached_path_id != 0 && mdb->cached_path_len == mdb->pnl &&
229 strcmp(mdb->cached_path, mdb->path) == 0) {
230 return mdb->cached_path_id;
233 Mmsg(mdb->cmd, "SELECT PathId FROM Path WHERE Path='%s'", mdb->esc_name);
235 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
237 num_rows = sql_num_rows(mdb);
239 Mmsg2(mdb->errmsg, _("More than one Path!: %s for path: %s\n"),
240 edit_uint64(num_rows, ed1), mdb->path);
241 Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
243 /* Even if there are multiple paths, take the first one */
245 if ((row = sql_fetch_row(mdb)) == NULL) {
246 Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
248 PathId = str_to_int64(row[0]);
250 Mmsg2(mdb->errmsg, _("Get DB path record %s found bad record: %s\n"),
251 mdb->cmd, edit_int64(PathId, ed1));
255 if (PathId != mdb->cached_path_id) {
256 mdb->cached_path_id = PathId;
257 mdb->cached_path_len = mdb->pnl;
258 pm_strcpy(mdb->cached_path, mdb->path);
263 Mmsg1(mdb->errmsg, _("Path record: %s not found.\n"), mdb->path);
265 sql_free_result(mdb);
267 Mmsg(mdb->errmsg, _("Path record: %s not found in Catalog.\n"), mdb->path);
274 * Get Job record for given JobId or Job name
275 * Returns: false on failure
278 bool db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr)
282 char esc[MAX_ESCAPE_NAME_LENGTH];
285 if (jr->JobId == 0) {
286 mdb->db_escape_string(jcr, esc, jr->Job, strlen(jr->Job));
287 Mmsg(mdb->cmd, "SELECT VolSessionId,VolSessionTime,"
288 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
289 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
290 "SchedTime,RealEndTime,ReadBytes,HasBase,PurgedFiles "
291 "FROM Job WHERE Job='%s'", esc);
293 Mmsg(mdb->cmd, "SELECT VolSessionId,VolSessionTime,"
294 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
295 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
296 "SchedTime,RealEndTime,ReadBytes,HasBase,PurgedFiles "
297 "FROM Job WHERE JobId=%s",
298 edit_int64(jr->JobId, ed1));
301 if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
303 return false; /* failed */
305 if ((row = sql_fetch_row(mdb)) == NULL) {
306 Mmsg1(mdb->errmsg, _("No Job found for JobId %s\n"), edit_int64(jr->JobId, ed1));
307 sql_free_result(mdb);
309 return false; /* failed */
312 jr->VolSessionId = str_to_uint64(row[0]);
313 jr->VolSessionTime = str_to_uint64(row[1]);
314 jr->PoolId = str_to_int64(row[2]);
315 bstrncpy(jr->cStartTime, row[3]!=NULL?row[3]:"", sizeof(jr->cStartTime));
316 bstrncpy(jr->cEndTime, row[4]!=NULL?row[4]:"", sizeof(jr->cEndTime));
317 jr->JobFiles = str_to_int64(row[5]);
318 jr->JobBytes = str_to_int64(row[6]);
319 jr->JobTDate = str_to_int64(row[7]);
320 bstrncpy(jr->Job, row[8]!=NULL?row[8]:"", sizeof(jr->Job));
321 jr->JobStatus = row[9]!=NULL?(int)*row[9]:JS_FatalError;
322 jr->JobType = row[10]!=NULL?(int)*row[10]:JT_BACKUP;
323 jr->JobLevel = row[11]!=NULL?(int)*row[11]:L_NONE;
324 jr->ClientId = str_to_uint64(row[12]!=NULL?row[12]:(char *)"");
325 bstrncpy(jr->Name, row[13]!=NULL?row[13]:"", sizeof(jr->Name));
326 jr->PriorJobId = str_to_uint64(row[14]!=NULL?row[14]:(char *)"");
327 bstrncpy(jr->cRealEndTime, row[15]!=NULL?row[15]:"", sizeof(jr->cRealEndTime));
328 if (jr->JobId == 0) {
329 jr->JobId = str_to_int64(row[16]);
331 jr->FileSetId = str_to_int64(row[17]);
332 bstrncpy(jr->cSchedTime, row[3]!=NULL?row[18]:"", sizeof(jr->cSchedTime));
333 bstrncpy(jr->cRealEndTime, row[3]!=NULL?row[19]:"", sizeof(jr->cRealEndTime));
334 jr->ReadBytes = str_to_int64(row[20]);
335 jr->StartTime = str_to_utime(jr->cStartTime);
336 jr->SchedTime = str_to_utime(jr->cSchedTime);
337 jr->EndTime = str_to_utime(jr->cEndTime);
338 jr->RealEndTime = str_to_utime(jr->cRealEndTime);
339 jr->HasBase = str_to_int64(row[21]);
340 jr->PurgedFiles = str_to_int64(row[22]);
341 sql_free_result(mdb);
348 * Find VolumeNames for a given JobId
349 * Returns: 0 on error or no Volumes found
350 * number of volumes on success
351 * Volumes are concatenated in VolumeNames
352 * separated by a vertical bar (|) in the order
353 * that they were written.
355 * Returns: number of volumes on success
357 int db_get_job_volume_names(JCR *jcr, B_DB *mdb, JobId_t JobId, POOLMEM **VolumeNames)
366 /* Get one entry per VolumeName, but "sort" by VolIndex */
368 "SELECT VolumeName,MAX(VolIndex) FROM JobMedia,Media WHERE "
369 "JobMedia.JobId=%s AND JobMedia.MediaId=Media.MediaId "
370 "GROUP BY VolumeName "
371 "ORDER BY 2 ASC", edit_int64(JobId,ed1));
373 Dmsg1(130, "VolNam=%s\n", mdb->cmd);
375 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
376 num_rows = sql_num_rows(mdb);
377 Dmsg1(130, "Num rows=%d\n", num_rows);
379 Mmsg1(mdb->errmsg, _("No volumes found for JobId=%d\n"), JobId);
383 for (i=0; i < stat; i++) {
384 if ((row = sql_fetch_row(mdb)) == NULL) {
385 Mmsg2(mdb->errmsg, _("Error fetching row %d: ERR=%s\n"), i, sql_strerror(mdb));
386 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
390 if (*VolumeNames[0] != 0) {
391 pm_strcat(VolumeNames, "|");
393 pm_strcat(VolumeNames, row[0]);
397 sql_free_result(mdb);
399 Mmsg(mdb->errmsg, _("No Volume for JobId %d found in Catalog.\n"), JobId);
406 * Find Volume parameters for a give JobId
407 * Returns: 0 on error or no Volumes found
408 * number of volumes on success
409 * List of Volumes and start/end file/blocks (malloced structure!)
411 * Returns: number of volumes on success
413 int db_get_job_volume_parameters(JCR *jcr, B_DB *mdb, JobId_t JobId, VOL_PARAMS **VolParams)
419 VOL_PARAMS *Vols = NULL;
424 "SELECT VolumeName,MediaType,FirstIndex,LastIndex,StartFile,"
425 "JobMedia.EndFile,StartBlock,JobMedia.EndBlock,"
426 "Slot,StorageId,InChanger"
427 " FROM JobMedia,Media WHERE JobMedia.JobId=%s"
428 " AND JobMedia.MediaId=Media.MediaId ORDER BY VolIndex,JobMediaId",
429 edit_int64(JobId, ed1));
431 Dmsg1(130, "VolNam=%s\n", mdb->cmd);
432 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
433 num_rows = sql_num_rows(mdb);
434 Dmsg1(200, "Num rows=%d\n", num_rows);
436 Mmsg1(mdb->errmsg, _("No volumes found for JobId=%d\n"), JobId);
442 *VolParams = Vols = (VOL_PARAMS *)malloc(stat * sizeof(VOL_PARAMS));
443 SId = (DBId_t *)malloc(stat * sizeof(DBId_t));
445 for (i=0; i < stat; i++) {
446 if ((row = sql_fetch_row(mdb)) == NULL) {
447 Mmsg2(mdb->errmsg, _("Error fetching row %d: ERR=%s\n"), i, sql_strerror(mdb));
448 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
453 uint32_t StartBlock, EndBlock, StartFile, EndFile;
454 bstrncpy(Vols[i].VolumeName, row[0], MAX_NAME_LENGTH);
455 bstrncpy(Vols[i].MediaType, row[1], MAX_NAME_LENGTH);
456 Vols[i].FirstIndex = str_to_uint64(row[2]);
457 Vols[i].LastIndex = str_to_uint64(row[3]);
458 StartFile = str_to_uint64(row[4]);
459 EndFile = str_to_uint64(row[5]);
460 StartBlock = str_to_uint64(row[6]);
461 EndBlock = str_to_uint64(row[7]);
462 Vols[i].StartAddr = (((uint64_t)StartFile)<<32) | StartBlock;
463 Vols[i].EndAddr = (((uint64_t)EndFile)<<32) | EndBlock;
464 Vols[i].Slot = str_to_uint64(row[8]);
465 StorageId = str_to_uint64(row[9]);
466 Vols[i].InChanger = str_to_uint64(row[10]);
467 Vols[i].Storage[0] = 0;
471 for (i=0; i < stat; i++) {
473 Mmsg(mdb->cmd, "SELECT Name from Storage WHERE StorageId=%s",
474 edit_int64(SId[i], ed1));
475 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
476 if ((row = sql_fetch_row(mdb)) && row[0]) {
477 bstrncpy(Vols[i].Storage, row[0], MAX_NAME_LENGTH);
486 sql_free_result(mdb);
495 * Get the number of pool records
497 * Returns: -1 on failure
500 int db_get_num_pool_records(JCR *jcr, B_DB *mdb)
505 Mmsg(mdb->cmd, "SELECT count(*) from Pool");
506 stat = get_sql_record_max(jcr, mdb);
512 * This function returns a list of all the Pool record ids.
513 * The caller must free ids if non-NULL.
515 * Returns 0: on failure
518 int db_get_pool_ids(JCR *jcr, B_DB *mdb, int *num_ids, uint32_t *ids[])
527 Mmsg(mdb->cmd, "SELECT PoolId FROM Pool");
528 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
529 *num_ids = sql_num_rows(mdb);
531 id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
532 while ((row = sql_fetch_row(mdb)) != NULL) {
533 id[i++] = str_to_uint64(row[0]);
537 sql_free_result(mdb);
540 Mmsg(mdb->errmsg, _("Pool id select failed: ERR=%s\n"), sql_strerror(mdb));
541 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
549 * This function returns a list of all the Client record ids.
550 * The caller must free ids if non-NULL.
552 * Returns 0: on failure
555 int db_get_client_ids(JCR *jcr, B_DB *mdb, int *num_ids, uint32_t *ids[])
564 Mmsg(mdb->cmd, "SELECT ClientId FROM Client ORDER BY Name");
565 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
566 *num_ids = sql_num_rows(mdb);
568 id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
569 while ((row = sql_fetch_row(mdb)) != NULL) {
570 id[i++] = str_to_uint64(row[0]);
574 sql_free_result(mdb);
577 Mmsg(mdb->errmsg, _("Client id select failed: ERR=%s\n"), sql_strerror(mdb));
578 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
586 * Get Pool Id, Scratch Pool Id, Recycle Pool Id
587 * Returns: false on failure
590 bool db_get_pool_record(JCR *jcr, B_DB *mdb, POOL_DBR *pdbr)
596 char esc[MAX_ESCAPE_NAME_LENGTH];
599 if (pdbr->PoolId != 0) { /* find by id */
601 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
602 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
603 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId,"
604 "ActionOnPurge FROM Pool WHERE Pool.PoolId=%s",
605 edit_int64(pdbr->PoolId, ed1));
606 } else { /* find by name */
607 mdb->db_escape_string(jcr, esc, pdbr->Name, strlen(pdbr->Name));
609 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
610 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
611 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId,"
612 "ActionOnPurge FROM Pool WHERE Pool.Name='%s'", esc);
614 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
615 num_rows = sql_num_rows(mdb);
618 Mmsg1(mdb->errmsg, _("More than one Pool!: %s\n"),
619 edit_uint64(num_rows, ed1));
620 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
621 } else if (num_rows == 1) {
622 if ((row = sql_fetch_row(mdb)) == NULL) {
623 Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
624 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
626 pdbr->PoolId = str_to_int64(row[0]);
627 bstrncpy(pdbr->Name, row[1]!=NULL?row[1]:"", sizeof(pdbr->Name));
628 pdbr->NumVols = str_to_int64(row[2]);
629 pdbr->MaxVols = str_to_int64(row[3]);
630 pdbr->UseOnce = str_to_int64(row[4]);
631 pdbr->UseCatalog = str_to_int64(row[5]);
632 pdbr->AcceptAnyVolume = str_to_int64(row[6]);
633 pdbr->AutoPrune = str_to_int64(row[7]);
634 pdbr->Recycle = str_to_int64(row[8]);
635 pdbr->VolRetention = str_to_int64(row[9]);
636 pdbr->VolUseDuration = str_to_int64(row[10]);
637 pdbr->MaxVolJobs = str_to_int64(row[11]);
638 pdbr->MaxVolFiles = str_to_int64(row[12]);
639 pdbr->MaxVolBytes = str_to_uint64(row[13]);
640 bstrncpy(pdbr->PoolType, row[14]!=NULL?row[14]:"", sizeof(pdbr->PoolType));
641 pdbr->LabelType = str_to_int64(row[15]);
642 bstrncpy(pdbr->LabelFormat, row[16]!=NULL?row[16]:"", sizeof(pdbr->LabelFormat));
643 pdbr->RecyclePoolId = str_to_int64(row[17]);
644 pdbr->ScratchPoolId = str_to_int64(row[18]);
645 pdbr->ActionOnPurge = str_to_int32(row[19]);
649 sql_free_result(mdb);
656 * If the PoolId is non-zero, we get its record,
657 * otherwise, we search on the PoolName and we compute the number of volumes
659 * Returns: false on failure
662 bool db_get_pool_numvols(JCR *jcr, B_DB *mdb, POOL_DBR *pdbr)
667 ok = db_get_pool_record(jcr, mdb, pdbr);
672 Mmsg(mdb->cmd, "SELECT count(*) from Media WHERE PoolId=%s",
673 edit_int64(pdbr->PoolId, ed1));
674 NumVols = get_sql_record_max(jcr, mdb);
675 Dmsg2(400, "Actual NumVols=%d Pool NumVols=%d\n", NumVols, pdbr->NumVols);
676 if (NumVols != pdbr->NumVols) {
677 pdbr->NumVols = NumVols;
678 db_update_pool_record(jcr, mdb, pdbr);
681 Mmsg(mdb->errmsg, _("Pool record not found in Catalog.\n"));
689 * If the ClientId is non-zero, we get its record,
690 * otherwise, we search on the Client Name
692 * Returns: 0 on failure
695 int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cdbr)
701 char esc[MAX_ESCAPE_NAME_LENGTH];
704 if (cdbr->ClientId != 0) { /* find by id */
706 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
707 "FROM Client WHERE Client.ClientId=%s",
708 edit_int64(cdbr->ClientId, ed1));
709 } else { /* find by name */
710 mdb->db_escape_string(jcr, esc, cdbr->Name, strlen(cdbr->Name));
712 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
713 "FROM Client WHERE Client.Name='%s'", esc);
716 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
717 num_rows = sql_num_rows(mdb);
719 Mmsg1(mdb->errmsg, _("More than one Client!: %s\n"),
720 edit_uint64(num_rows, ed1));
721 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
722 } else if (num_rows == 1) {
723 if ((row = sql_fetch_row(mdb)) == NULL) {
724 Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
725 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
727 cdbr->ClientId = str_to_int64(row[0]);
728 bstrncpy(cdbr->Name, row[1]!=NULL?row[1]:"", sizeof(cdbr->Name));
729 bstrncpy(cdbr->Uname, row[2]!=NULL?row[2]:"", sizeof(cdbr->Uname));
730 cdbr->AutoPrune = str_to_int64(row[3]);
731 cdbr->FileRetention = str_to_int64(row[4]);
732 cdbr->JobRetention = str_to_int64(row[5]);
736 Mmsg(mdb->errmsg, _("Client record not found in Catalog.\n"));
738 sql_free_result(mdb);
740 Mmsg(mdb->errmsg, _("Client record not found in Catalog.\n"));
749 * Returns: 0 on failure
752 int db_get_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr)
756 char esc[MAX_ESCAPE_NAME_LENGTH];
759 mdb->db_escape_string(jcr, esc, cr->Counter, strlen(cr->Counter));
761 Mmsg(mdb->cmd, select_counter_values[mdb->db_get_type_index()], esc);
762 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
763 num_rows = sql_num_rows(mdb);
765 /* If more than one, report error, but return first row */
767 Mmsg1(mdb->errmsg, _("More than one Counter!: %d\n"), num_rows);
768 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
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);
778 cr->MinValue = str_to_int64(row[0]);
779 cr->MaxValue = str_to_int64(row[1]);
780 cr->CurrentValue = str_to_int64(row[2]);
782 bstrncpy(cr->WrapCounter, row[3], sizeof(cr->WrapCounter));
784 cr->WrapCounter[0] = 0;
786 sql_free_result(mdb);
790 sql_free_result(mdb);
792 Mmsg(mdb->errmsg, _("Counter record: %s not found in Catalog.\n"), cr->Counter);
801 * If the FileSetId is non-zero, we get its record,
802 * otherwise, we search on the name
804 * Returns: 0 on failure
807 int db_get_fileset_record(JCR *jcr, B_DB *mdb, FILESET_DBR *fsr)
813 char esc[MAX_ESCAPE_NAME_LENGTH];
816 if (fsr->FileSetId != 0) { /* find by id */
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));
824 "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
825 "WHERE FileSet='%s' ORDER BY CreateTime DESC LIMIT 1", esc);
828 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
829 num_rows = sql_num_rows(mdb);
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);
836 if ((row = sql_fetch_row(mdb)) == NULL) {
837 Mmsg1(mdb->errmsg, _("FileSet record \"%s\" not found.\n"), fsr->FileSet);
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;
845 sql_free_result(mdb);
847 Mmsg(mdb->errmsg, _("FileSet record not found in Catalog.\n"));
855 * Get the number of Media records
857 * Returns: -1 on failure
860 int db_get_num_media_records(JCR *jcr, B_DB *mdb)
865 Mmsg(mdb->cmd, "SELECT count(*) from Media");
866 stat = get_sql_record_max(jcr, mdb);
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.
877 * Returns false: on failure
880 bool db_get_media_ids(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr, int *num_ids, uint32_t *ids[])
887 char buf[MAX_NAME_LENGTH*3]; /* Can contain MAX_NAME_LENGTH*2+1 + AND ....='' */
888 char esc[MAX_NAME_LENGTH*2+1];
893 Mmsg(mdb->cmd, "SELECT DISTINCT MediaId FROM Media WHERE Recycle=%d AND Enabled=%d ",
894 mr->Recycle, mr->Enabled);
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);
903 bsnprintf(buf, sizeof(buf), "AND StorageId=%s ", edit_uint64(mr->StorageId, ed1));
904 pm_strcat(mdb->cmd, buf);
908 bsnprintf(buf, sizeof(buf), "AND PoolId=%s ", edit_uint64(mr->PoolId, ed1));
909 pm_strcat(mdb->cmd, buf);
913 bsnprintf(buf, sizeof(buf), "AND VolBytes > %s ", edit_uint64(mr->VolBytes, ed1));
914 pm_strcat(mdb->cmd, buf);
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);
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);
929 Dmsg1(100, "q=%s\n", mdb->cmd);
931 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
932 *num_ids = sql_num_rows(mdb);
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]);
940 sql_free_result(mdb);
943 Mmsg(mdb->errmsg, _("Media id select failed: ERR=%s\n"), sql_strerror(mdb));
944 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
953 * This function returns a list of all the DBIds that are returned
956 * Returns false: on failure
959 bool db_get_query_dbids(JCR *jcr, B_DB *mdb, POOL_MEM &query, dbid_list &ids)
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) {
972 ids.DBId = (DBId_t *)malloc(ids.num_ids * sizeof(DBId_t));
974 while ((row = sql_fetch_row(mdb)) != NULL) {
975 ids.DBId[i++] = str_to_uint64(row[0]);
978 sql_free_result(mdb);
981 Mmsg(mdb->errmsg, _("query dbids failed: ERR=%s\n"), sql_strerror(mdb));
982 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
992 * Returns: false: on failure
995 bool db_get_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr)
1001 char esc[MAX_ESCAPE_NAME_LENGTH];
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);
1010 if (mr->MediaId != 0) { /* find by id */
1011 Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,"
1012 "VolBlocks,VolBytes,VolMounts,"
1013 "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 MediaId=%s",
1020 edit_int64(mr->MediaId, ed1));
1021 } else { /* find by name */
1022 mdb->db_escape_string(jcr, esc, mr->VolumeName, strlen(mr->VolumeName));
1023 Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,"
1024 "VolBlocks,VolBytes,VolMounts,"
1025 "VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
1026 "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
1027 "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
1028 "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
1029 "Enabled,LocationId,RecycleCount,InitialWrite,"
1030 "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge "
1031 "FROM Media WHERE VolumeName='%s'", esc);
1034 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
1036 num_rows = sql_num_rows(mdb);
1038 Mmsg1(mdb->errmsg, _("More than one Volume!: %s\n"),
1039 edit_uint64(num_rows, ed1));
1040 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
1041 } else if (num_rows == 1) {
1042 if ((row = sql_fetch_row(mdb)) == NULL) {
1043 Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
1044 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
1047 mr->MediaId = str_to_int64(row[0]);
1048 bstrncpy(mr->VolumeName, row[1]!=NULL?row[1]:"", sizeof(mr->VolumeName));
1049 mr->VolJobs = str_to_int64(row[2]);
1050 mr->VolFiles = str_to_int64(row[3]);
1051 mr->VolBlocks = str_to_int64(row[4]);
1052 mr->VolBytes = str_to_uint64(row[5]);
1053 mr->VolMounts = str_to_int64(row[6]);
1054 mr->VolErrors = str_to_int64(row[7]);
1055 mr->VolWrites = str_to_int64(row[8]);
1056 mr->MaxVolBytes = str_to_uint64(row[9]);
1057 mr->VolCapacityBytes = str_to_uint64(row[10]);
1058 bstrncpy(mr->MediaType, row[11]!=NULL?row[11]:"", sizeof(mr->MediaType));
1059 bstrncpy(mr->VolStatus, row[12]!=NULL?row[12]:"", sizeof(mr->VolStatus));
1060 mr->PoolId = str_to_int64(row[13]);
1061 mr->VolRetention = str_to_uint64(row[14]);
1062 mr->VolUseDuration = str_to_uint64(row[15]);
1063 mr->MaxVolJobs = str_to_int64(row[16]);
1064 mr->MaxVolFiles = str_to_int64(row[17]);
1065 mr->Recycle = str_to_int64(row[18]);
1066 mr->Slot = str_to_int64(row[19]);
1067 bstrncpy(mr->cFirstWritten, row[20]!=NULL?row[20]:"", sizeof(mr->cFirstWritten));
1068 mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten);
1069 bstrncpy(mr->cLastWritten, row[21]!=NULL?row[21]:"", sizeof(mr->cLastWritten));
1070 mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
1071 mr->InChanger = str_to_uint64(row[22]);
1072 mr->EndFile = str_to_uint64(row[23]);
1073 mr->EndBlock = str_to_uint64(row[24]);
1074 mr->VolParts = str_to_int64(row[25]);
1075 mr->LabelType = str_to_int64(row[26]);
1076 bstrncpy(mr->cLabelDate, row[27]!=NULL?row[27]:"", sizeof(mr->cLabelDate));
1077 mr->LabelDate = (time_t)str_to_utime(mr->cLabelDate);
1078 mr->StorageId = str_to_int64(row[28]);
1079 mr->Enabled = str_to_int64(row[29]);
1080 mr->LocationId = str_to_int64(row[30]);
1081 mr->RecycleCount = str_to_int64(row[31]);
1082 bstrncpy(mr->cInitialWrite, row[32]!=NULL?row[32]:"", sizeof(mr->cInitialWrite));
1083 mr->InitialWrite = (time_t)str_to_utime(mr->cInitialWrite);
1084 mr->ScratchPoolId = str_to_int64(row[33]);
1085 mr->RecyclePoolId = str_to_int64(row[34]);
1086 mr->VolReadTime = str_to_int64(row[35]);
1087 mr->VolWriteTime = str_to_int64(row[36]);
1088 mr->ActionOnPurge = str_to_int32(row[37]);
1093 if (mr->MediaId != 0) {
1094 Mmsg1(mdb->errmsg, _("Media record with MediaId=%s not found.\n"),
1095 edit_int64(mr->MediaId, ed1));
1097 Mmsg1(mdb->errmsg, _("Media record for Volume name \"%s\" not found.\n"),
1101 sql_free_result(mdb);
1103 if (mr->MediaId != 0) {
1104 Mmsg(mdb->errmsg, _("Media record for MediaId=%u not found in Catalog.\n"),
1107 Mmsg(mdb->errmsg, _("Media record for Volume Name \"%s\" not found in Catalog.\n"),
1114 /* Remove all MD5 from a query (can save lot of memory with many files) */
1115 static void strip_md5(char *q)
1118 while ((p = strstr(p, ", MD5"))) {
1119 memset(p, ' ', 5 * sizeof(char));
1124 * Find the last "accurate" backup state (that can take deleted files in
1126 * 1) Get all files with jobid in list (F subquery)
1127 * Get all files in BaseFiles with jobid in list
1128 * 2) Take only the last version of each file (Temp subquery) => accurate list
1130 * 3) Join the result to file table to get fileindex, jobid and lstat information
1132 * TODO: See if we can do the SORT only if needed (as an argument)
1134 bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids,
1135 bool use_md5, bool use_delta,
1136 DB_RESULT_HANDLER *result_handler, void *ctx)
1140 Mmsg(mdb->errmsg, _("ERR=JobIds are empty\n"));
1144 POOL_MEM buf(PM_MESSAGE);
1145 POOL_MEM buf2(PM_MESSAGE);
1147 Mmsg(buf2, select_recent_version_with_basejob_and_delta[db_get_type_index(mdb)],
1148 jobids, jobids, jobids, jobids);
1151 Mmsg(buf2, select_recent_version_with_basejob[db_get_type_index(mdb)],
1152 jobids, jobids, jobids, jobids);
1155 /* bsr code is optimized for JobId sorted, with Delta, we need to get
1156 * them ordered by date. JobTDate and JobId can be mixed if using Copy
1160 "SELECT Path.Path, Filename.Name, T1.FileIndex, T1.JobId, LStat, DeltaSeq, MD5 "
1161 "FROM ( %s ) AS T1 "
1162 "JOIN Filename ON (Filename.FilenameId = T1.FilenameId) "
1163 "JOIN Path ON (Path.PathId = T1.PathId) "
1164 "WHERE FileIndex > 0 "
1165 "ORDER BY T1.JobTDate, FileIndex ASC",/* Return sorted by JobTDate */
1166 /* FileIndex for restore code */
1170 strip_md5(buf.c_str());
1173 Dmsg1(100, "q=%s\n", buf.c_str());
1175 return db_big_sql_query(mdb, buf.c_str(), result_handler, ctx);
1179 * This procedure gets the base jobid list used by jobids,
1181 bool db_get_used_base_jobids(JCR *jcr, B_DB *mdb,
1182 POOLMEM *jobids, db_list_ctx *result)
1186 "SELECT DISTINCT BaseJobId "
1187 " FROM Job JOIN BaseFiles USING (JobId) "
1188 " WHERE Job.HasBase = 1 "
1189 " AND Job.JobId IN (%s) ", jobids);
1190 return db_sql_query(mdb, buf.c_str(), db_list_handler, result);
1194 * The decision do change an incr/diff was done before
1196 * Differential : get the last full id
1197 * Incremental : get the last full + last diff + last incr(s) ids
1199 * If you specify jr->StartTime, it will be used to limit the search
1200 * in the time. (usually now)
1202 * TODO: look and merge from ua_restore.c
1204 bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb,
1205 JOB_DBR *jr, db_list_ctx *jobids)
1208 char clientid[50], jobid[50], filesetid[50];
1209 char date[MAX_TIME_LENGTH];
1210 POOL_MEM query(PM_FNAME);
1212 /* Take the current time as upper limit if nothing else specified */
1213 utime_t StartTime = (jr->StartTime)?jr->StartTime:time(NULL);
1215 bstrutime(date, sizeof(date), StartTime + 1);
1218 /* First, find the last good Full backup for this job/client/fileset */
1219 Mmsg(query, create_temp_accurate_jobids[db_get_type_index(mdb)],
1220 edit_uint64(jcr->JobId, jobid),
1221 edit_uint64(jr->ClientId, clientid),
1223 edit_uint64(jr->FileSetId, filesetid));
1225 if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
1229 if (jr->JobLevel == L_INCREMENTAL || jr->JobLevel == L_VIRTUAL_FULL) {
1230 /* Now, find the last differential backup after the last full */
1232 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
1233 "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1234 "FROM Job JOIN FileSet USING (FileSetId) "
1235 "WHERE ClientId = %s "
1236 "AND Level='D' AND JobStatus IN ('T','W') AND Type='B' "
1237 "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
1238 "AND StartTime < '%s' "
1239 "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1240 "ORDER BY Job.JobTDate DESC LIMIT 1 ",
1247 if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
1251 /* We just have to take all incremental after the last Full/Diff */
1253 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
1254 "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1255 "FROM Job JOIN FileSet USING (FileSetId) "
1256 "WHERE ClientId = %s "
1257 "AND Level='I' AND JobStatus IN ('T','W') AND Type='B' "
1258 "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
1259 "AND StartTime < '%s' "
1260 "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1261 "ORDER BY Job.JobTDate DESC ",
1267 if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
1272 /* build a jobid list ie: 1,2,3,4 */
1273 Mmsg(query, "SELECT JobId FROM btemp3%s ORDER by JobTDate", jobid);
1274 db_sql_query(mdb, query.c_str(), db_list_handler, jobids);
1275 Dmsg1(1, "db_accurate_get_jobids=%s\n", jobids->list);
1279 Mmsg(query, "DROP TABLE btemp3%s", jobid);
1280 db_sql_query(mdb, query.c_str(), NULL, NULL);
1285 bool db_get_base_file_list(JCR *jcr, B_DB *mdb, bool use_md5,
1286 DB_RESULT_HANDLER *result_handler, void *ctx)
1288 POOL_MEM buf(PM_MESSAGE);
1291 "SELECT Path, Name, FileIndex, JobId, LStat, 0 As DeltaSeq, MD5 "
1292 "FROM new_basefile%lld ORDER BY JobId, FileIndex ASC",
1293 (uint64_t) jcr->JobId);
1296 strip_md5(buf.c_str());
1298 return db_sql_query(mdb, buf.c_str(), result_handler, ctx);
1301 bool db_get_base_jobid(JCR *jcr, B_DB *mdb, JOB_DBR *jr, JobId_t *jobid)
1303 POOL_MEM query(PM_FNAME);
1306 char date[MAX_TIME_LENGTH];
1307 char esc[MAX_ESCAPE_NAME_LENGTH];
1309 // char clientid[50], filesetid[50];
1314 StartTime = (jr->StartTime)?jr->StartTime:time(NULL);
1315 bstrutime(date, sizeof(date), StartTime + 1);
1316 mdb->db_escape_string(jcr, esc, jr->Name, strlen(jr->Name));
1318 /* we can take also client name, fileset, etc... */
1321 "SELECT JobId, Job, StartTime, EndTime, JobTDate, PurgedFiles "
1323 // "JOIN FileSet USING (FileSetId) JOIN Client USING (ClientId) "
1324 "WHERE Job.Name = '%s' "
1325 "AND Level='B' AND JobStatus IN ('T','W') AND Type='B' "
1326 // "AND FileSet.FileSet= '%s' "
1327 // "AND Client.Name = '%s' "
1328 "AND StartTime<'%s' "
1329 "ORDER BY Job.JobTDate DESC LIMIT 1",
1331 // edit_uint64(jr->ClientId, clientid),
1332 // edit_uint64(jr->FileSetId, filesetid));
1335 Dmsg1(10, "db_get_base_jobid q=%s\n", query.c_str());
1336 if (!db_sql_query(mdb, query.c_str(), db_int64_handler, &lctx)) {
1339 *jobid = (JobId_t) lctx.value;
1341 Dmsg1(10, "db_get_base_jobid=%lld\n", *jobid);
1348 /* Get JobIds associated with a volume */
1349 bool db_get_volume_jobids(JCR *jcr, B_DB *mdb,
1350 MEDIA_DBR *mr, db_list_ctx *lst)
1356 Mmsg(mdb->cmd, "SELECT DISTINCT JobId FROM JobMedia WHERE MediaId=%s",
1357 edit_int64(mr->MediaId, ed1));
1358 ret = db_sql_query(mdb, mdb->cmd, db_list_handler, lst);
1363 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */