2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2012 Free Software Foundation Europe e.V.
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
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.
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
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.
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
34 * Kern Sibbald, March 2000
40 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI
46 /* -----------------------------------------------------------------------
48 * Generic Routines (or almost generic)
50 * -----------------------------------------------------------------------
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);
59 * Given a full filename (with path), look up the File record
60 * (with attributes) in the database.
62 * Returns: 0 on failure
63 * 1 on success with the File record in FILE_DBR
65 int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JOB_DBR *jr, FILE_DBR *fdbr)
68 Dmsg1(100, "db_get_file_att_record fname=%s \n", fname);
71 split_path_and_file(jcr, mdb, fname);
73 fdbr->FilenameId = db_get_filename_record(jcr, mdb);
75 fdbr->PathId = db_get_path_record(jcr, mdb);
77 stat = db_get_file_record(jcr, mdb, jr, fdbr);
87 * Returns: 0 on failure
90 * DO NOT use Jmsg in this routine.
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.
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.
109 int db_get_file_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, FILE_DBR *fdbr)
113 char ed1[50], ed2[50], ed3[50];
116 if (jcr->getJobLevel() == L_VERIFY_DISK_TO_CATALOG) {
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) {
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),
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));
141 Dmsg3(450, "Get_file_record JobId=%u FilenameId=%u PathId=%u\n",
142 fdbr->JobId, fdbr->FilenameId, fdbr->PathId);
144 Dmsg1(100, "Query=%s\n", mdb->cmd);
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);
150 if ((row = sql_fetch_row(mdb)) == NULL) {
151 Mmsg1(mdb->errmsg, _("Error fetching row: %s\n"), sql_strerror(mdb));
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));
158 Mmsg3(mdb->errmsg, _("get_file_record want 1 got rows=%d PathId=%s FilenameId=%s\n"),
160 edit_int64(fdbr->PathId, ed1),
161 edit_int64(fdbr->FilenameId, ed2));
162 Dmsg1(000, "=== Problem! %s", mdb->errmsg);
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));
170 sql_free_result(mdb);
172 Mmsg(mdb->errmsg, _("File record not found in Catalog.\n"));
179 * Get Filename record
180 * Returns: 0 on failure
181 * FilenameId on success
183 * DO NOT use Jmsg in this routine (see notes for get_file_record)
185 static int db_get_filename_record(JCR *jcr, B_DB *mdb)
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);
194 Mmsg(mdb->cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", mdb->esc_name);
195 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
197 num_rows = sql_num_rows(mdb);
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);
204 if ((row = sql_fetch_row(mdb)) == NULL) {
205 Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
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);
215 Mmsg1(mdb->errmsg, _("Filename record: %s not found.\n"), mdb->fname);
217 sql_free_result(mdb);
219 Mmsg(mdb->errmsg, _("Filename record: %s not found in Catalog.\n"), mdb->fname);
226 * Returns: 0 on failure
229 * DO NOT use Jmsg in this routine (see notes for get_file_record)
231 int db_get_path_record(JCR *jcr, B_DB *mdb)
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);
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;
245 Mmsg(mdb->cmd, "SELECT PathId FROM Path WHERE Path='%s'", mdb->esc_name);
247 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
249 num_rows = sql_num_rows(mdb);
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);
255 /* Even if there are multiple paths, take the first one */
257 if ((row = sql_fetch_row(mdb)) == NULL) {
258 Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
260 PathId = str_to_int64(row[0]);
262 Mmsg2(mdb->errmsg, _("Get DB path record %s found bad record: %s\n"),
263 mdb->cmd, edit_int64(PathId, ed1));
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);
275 Mmsg1(mdb->errmsg, _("Path record: %s not found.\n"), mdb->path);
277 sql_free_result(mdb);
279 Mmsg(mdb->errmsg, _("Path record: %s not found in Catalog.\n"), mdb->path);
286 * Get Job record for given JobId or Job name
287 * Returns: false on failure
290 bool db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr)
294 char esc[MAX_ESCAPE_NAME_LENGTH];
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);
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));
313 if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
315 return false; /* failed */
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);
321 return false; /* failed */
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]);
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);
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.
367 * Returns: number of volumes on success
369 int db_get_job_volume_names(JCR *jcr, B_DB *mdb, JobId_t JobId, POOLMEM **VolumeNames)
378 /* Get one entry per VolumeName, but "sort" by VolIndex */
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));
385 Dmsg1(130, "VolNam=%s\n", mdb->cmd);
387 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
388 num_rows = sql_num_rows(mdb);
389 Dmsg1(130, "Num rows=%d\n", num_rows);
391 Mmsg1(mdb->errmsg, _("No volumes found for JobId=%d\n"), JobId);
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);
402 if (*VolumeNames[0] != 0) {
403 pm_strcat(VolumeNames, "|");
405 pm_strcat(VolumeNames, row[0]);
409 sql_free_result(mdb);
411 Mmsg(mdb->errmsg, _("No Volume for JobId %d found in Catalog.\n"), JobId);
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!)
423 * Returns: number of volumes on success
425 int db_get_job_volume_parameters(JCR *jcr, B_DB *mdb, JobId_t JobId, VOL_PARAMS **VolParams)
431 VOL_PARAMS *Vols = NULL;
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));
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);
448 Mmsg1(mdb->errmsg, _("No volumes found for JobId=%d\n"), JobId);
454 *VolParams = Vols = (VOL_PARAMS *)malloc(stat * sizeof(VOL_PARAMS));
455 SId = (DBId_t *)malloc(stat * sizeof(DBId_t));
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);
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;
483 for (i=0; i < stat; i++) {
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);
498 sql_free_result(mdb);
507 * Get the number of pool records
509 * Returns: -1 on failure
512 int db_get_num_pool_records(JCR *jcr, B_DB *mdb)
517 Mmsg(mdb->cmd, "SELECT count(*) from Pool");
518 stat = get_sql_record_max(jcr, mdb);
524 * This function returns a list of all the Pool record ids.
525 * The caller must free ids if non-NULL.
527 * Returns 0: on failure
530 int db_get_pool_ids(JCR *jcr, B_DB *mdb, int *num_ids, uint32_t *ids[])
539 Mmsg(mdb->cmd, "SELECT PoolId FROM Pool");
540 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
541 *num_ids = sql_num_rows(mdb);
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]);
549 sql_free_result(mdb);
552 Mmsg(mdb->errmsg, _("Pool id select failed: ERR=%s\n"), sql_strerror(mdb));
553 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
561 * This function returns a list of all the Client record ids.
562 * The caller must free ids if non-NULL.
564 * Returns 0: on failure
567 int db_get_client_ids(JCR *jcr, B_DB *mdb, int *num_ids, uint32_t *ids[])
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);
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]);
586 sql_free_result(mdb);
589 Mmsg(mdb->errmsg, _("Client id select failed: ERR=%s\n"), sql_strerror(mdb));
590 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
601 * If the PoolId is non-zero, we get its record,
602 * otherwise, we search on the PoolName
604 * Returns: false on failure
607 bool db_get_pool_record(JCR *jcr, B_DB *mdb, POOL_DBR *pdbr)
613 char esc[MAX_ESCAPE_NAME_LENGTH];
616 if (pdbr->PoolId != 0) { /* find by id */
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));
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);
631 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
632 num_rows = sql_num_rows(mdb);
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);
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]);
666 sql_free_result(mdb);
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);
679 Mmsg(mdb->errmsg, _("Pool record not found in Catalog.\n"));
687 * If the ClientId is non-zero, we get its record,
688 * otherwise, we search on the Client Name
690 * Returns: 0 on failure
693 int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cdbr)
699 char esc[MAX_ESCAPE_NAME_LENGTH];
702 if (cdbr->ClientId != 0) { /* find by id */
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));
710 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
711 "FROM Client WHERE Client.Name='%s'", esc);
714 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
715 num_rows = sql_num_rows(mdb);
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);
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]);
734 Mmsg(mdb->errmsg, _("Client record not found in Catalog.\n"));
736 sql_free_result(mdb);
738 Mmsg(mdb->errmsg, _("Client record not found in Catalog.\n"));
747 * Returns: 0 on failure
750 int db_get_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr)
754 char esc[MAX_ESCAPE_NAME_LENGTH];
757 mdb->db_escape_string(jcr, esc, cr->Counter, strlen(cr->Counter));
759 Mmsg(mdb->cmd, "SELECT Counters.MinValue,Counters.MaxValue,CurrentValue,WrapCounter "
760 "FROM Counters WHERE Counter='%s'", 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,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);
1032 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
1034 num_rows = sql_num_rows(mdb);
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);
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]);
1091 if (mr->MediaId != 0) {
1092 Mmsg1(mdb->errmsg, _("Media record MediaId=%s not found.\n"),
1093 edit_int64(mr->MediaId, ed1));
1095 Mmsg1(mdb->errmsg, _("Media record for Volume \"%s\" not found.\n"),
1099 sql_free_result(mdb);
1101 if (mr->MediaId != 0) {
1102 Mmsg(mdb->errmsg, _("Media record for MediaId=%u not found in Catalog.\n"),
1105 Mmsg(mdb->errmsg, _("Media record for Vol=%s not found in Catalog.\n"),
1112 /* Remove all MD5 from a query (can save lot of memory with many files) */
1113 static void strip_md5(char *q)
1116 while ((p = strstr(p, ", MD5"))) {
1117 memset(p, ' ', 5 * sizeof(char));
1122 * Find the last "accurate" backup state (that can take deleted files in
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
1128 * 3) Join the result to file table to get fileindex, jobid and lstat information
1130 * TODO: See if we can do the SORT only if needed (as an argument)
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)
1138 Mmsg(mdb->errmsg, _("ERR=JobIds are empty\n"));
1142 POOL_MEM buf(PM_MESSAGE);
1143 POOL_MEM buf2(PM_MESSAGE);
1145 Mmsg(buf2, select_recent_version_with_basejob_and_delta[db_get_type_index(mdb)],
1146 jobids, jobids, jobids, jobids);
1149 Mmsg(buf2, select_recent_version_with_basejob[db_get_type_index(mdb)],
1150 jobids, jobids, jobids, jobids);
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
1158 "SELECT Path.Path, Filename.Name, T1.FileIndex, T1.JobId, LStat, DeltaSeq, 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 */
1168 strip_md5(buf.c_str());
1171 Dmsg1(100, "q=%s\n", buf.c_str());
1173 return db_big_sql_query(mdb, buf.c_str(), result_handler, ctx);
1177 * This procedure gets the base jobid list used by jobids,
1179 bool db_get_used_base_jobids(JCR *jcr, B_DB *mdb,
1180 POOLMEM *jobids, db_list_ctx *result)
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);
1192 * The decision do change an incr/diff was done before
1194 * Differential : get the last full id
1195 * Incremental : get the last full + last diff + last incr(s) ids
1197 * If you specify jr->StartTime, it will be used to limit the search
1198 * in the time. (usually now)
1200 * TODO: look and merge from ua_restore.c
1202 bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb,
1203 JOB_DBR *jr, db_list_ctx *jobids)
1206 char clientid[50], jobid[50], filesetid[50];
1207 char date[MAX_TIME_LENGTH];
1208 POOL_MEM query(PM_FNAME);
1210 /* Take the current time as upper limit if nothing else specified */
1211 utime_t StartTime = (jr->StartTime)?jr->StartTime:time(NULL);
1213 bstrutime(date, sizeof(date), StartTime + 1);
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),
1221 edit_uint64(jr->FileSetId, filesetid));
1223 if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
1227 if (jr->JobLevel == L_INCREMENTAL || jr->JobLevel == L_VIRTUAL_FULL) {
1228 /* Now, find the last differential backup after the last full */
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 ",
1245 if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
1249 /* We just have to take all incremental after the last Full/Diff */
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 ",
1265 if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
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);
1277 Mmsg(query, "DROP TABLE btemp3%s", jobid);
1278 db_sql_query(mdb, query.c_str(), NULL, NULL);
1283 bool db_get_base_file_list(JCR *jcr, B_DB *mdb, bool use_md5,
1284 DB_RESULT_HANDLER *result_handler, void *ctx)
1286 POOL_MEM buf(PM_MESSAGE);
1289 "SELECT Path, Name, FileIndex, JobId, LStat, 0 As DeltaSeq, MD5 "
1290 "FROM new_basefile%lld ORDER BY JobId, FileIndex ASC",
1291 (uint64_t) jcr->JobId);
1294 strip_md5(buf.c_str());
1296 return db_sql_query(mdb, buf.c_str(), result_handler, ctx);
1299 bool db_get_base_jobid(JCR *jcr, B_DB *mdb, JOB_DBR *jr, JobId_t *jobid)
1301 POOL_MEM query(PM_FNAME);
1304 char date[MAX_TIME_LENGTH];
1305 char esc[MAX_ESCAPE_NAME_LENGTH];
1307 // char clientid[50], filesetid[50];
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));
1316 /* we can take also client name, fileset, etc... */
1319 "SELECT JobId, Job, StartTime, EndTime, JobTDate, PurgedFiles "
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",
1329 // edit_uint64(jr->ClientId, clientid),
1330 // edit_uint64(jr->FileSetId, filesetid));
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)) {
1337 *jobid = (JobId_t) lctx.value;
1339 Dmsg1(10, "db_get_base_jobid=%lld\n", *jobid);
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)
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);
1361 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI */