2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
21 * Bacula Catalog Database Get record interface routines
22 * Note, these routines generally get a record by id or
23 * by name. If more logic is involved, the routine
26 * Written by Kern Sibbald, March 2000
32 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
36 /* -----------------------------------------------------------------------
38 * Generic Routines (or almost generic)
40 * -----------------------------------------------------------------------
44 * Given a full filename (with path), look up the File record
45 * (with attributes) in the database.
47 * Returns: false on failure
48 * true on success with the File record in FILE_DBR
50 bool BDB::bdb_get_file_attributes_record(JCR *jcr, char *afname, JOB_DBR *jr, FILE_DBR *fdbr)
54 Dmsg1(500, "db_get_file_att_record fname=%s \n", afname);
58 split_path_and_file(jcr, this, afname);
60 fdbr->FilenameId = bdb_get_filename_record(jcr);
62 fdbr->PathId = bdb_get_path_record(jcr);
64 ok = bdb_get_file_record(jcr, jr, fdbr);
75 * DO NOT use Jmsg in this routine.
77 * Note in this routine, we do not use Jmsg because it may be
78 * called to get attributes of a non-existent file, which is
79 * "normal" if a new file is found during Verify.
81 * The following is a bit of a kludge: because we always backup a
82 * directory entry, we can end up with two copies of the directory
83 * in the backup. One is when we encounter the directory and find
84 * we cannot recurse into it, and the other is when we find an
85 * explicit mention of the directory. This can also happen if the
86 * use includes the directory twice. In this case, Verify
87 * VolumeToCatalog fails because we have two copies in the catalog, and
88 * only the first one is marked (twice). So, when calling from Verify,
89 * VolumeToCatalog jr is not NULL, and we know jr->FileIndex is the fileindex
90 * of the version of the directory/file we actually want and do
91 * a more explicit SQL search.
93 * Returns: false on failure
97 bool BDB::bdb_get_file_record(JCR *jcr, JOB_DBR *jr, FILE_DBR *fdbr)
101 char ed1[50], ed2[50], ed3[50];
103 switch (jcr->getJobLevel()) {
104 case L_VERIFY_VOLUME_TO_CATALOG:
106 "SELECT FileId, LStat, MD5 FROM File WHERE File.JobId=%s AND File.PathId=%s AND "
107 "File.FilenameId=%s AND File.FileIndex=%u",
108 edit_int64(fdbr->JobId, ed1),
109 edit_int64(fdbr->PathId, ed2),
110 edit_int64(fdbr->FilenameId,ed3),
113 case L_VERIFY_DISK_TO_CATALOG:
115 "SELECT FileId, LStat, MD5 FROM File,Job WHERE "
116 "File.JobId=Job.JobId AND File.PathId=%s AND "
117 "File.FilenameId=%s AND Job.Type='B' AND Job.JobStatus IN ('T','W') AND "
118 "ClientId=%s ORDER BY StartTime DESC LIMIT 1",
119 edit_int64(fdbr->PathId, ed1),
120 edit_int64(fdbr->FilenameId, ed2),
121 edit_int64(jr->ClientId,ed3));
125 "SELECT FileId, LStat, MD5 FROM File WHERE File.JobId=%s AND File.PathId=%s AND "
126 "File.FilenameId=%s",
127 edit_int64(fdbr->JobId, ed1),
128 edit_int64(fdbr->PathId, ed2),
129 edit_int64(fdbr->FilenameId,ed3));
133 Dmsg3(450, "Get_file_record JobId=%u FilenameId=%u PathId=%u\n",
134 fdbr->JobId, fdbr->FilenameId, fdbr->PathId);
136 Dmsg1(100, "Query=%s\n", cmd);
138 if (QueryDB(jcr, cmd)) {
139 Dmsg1(100, "get_file_record sql_num_rows()=%d\n", sql_num_rows());
140 if (sql_num_rows() >= 1) {
141 if ((row = sql_fetch_row()) == NULL) {
142 Mmsg1(errmsg, _("Error fetching row: %s\n"), sql_strerror());
144 fdbr->FileId = (FileId_t)str_to_int64(row[0]);
145 bstrncpy(fdbr->LStat, row[1], sizeof(fdbr->LStat));
146 bstrncpy(fdbr->Digest, row[2], sizeof(fdbr->Digest));
148 if (sql_num_rows() > 1) {
149 Mmsg3(errmsg, _("get_file_record want 1 got rows=%d PathId=%s FilenameId=%s\n"),
151 edit_int64(fdbr->PathId, ed1),
152 edit_int64(fdbr->FilenameId, ed2));
153 Dmsg1(000, "=== Problem! %s", errmsg);
157 Mmsg2(errmsg, _("File record for PathId=%s FilenameId=%s not found.\n"),
158 edit_int64(fdbr->PathId, ed1),
159 edit_int64(fdbr->FilenameId, ed2));
163 Mmsg(errmsg, _("File record not found in Catalog.\n"));
170 * Get Filename record
171 * Returns: 0 on failure
172 * FilenameId on success
174 * DO NOT use Jmsg in this routine (see notes for get_file_record)
176 int BDB::bdb_get_filename_record(JCR *jcr)
182 esc_name = check_pool_memory_size(esc_name, 2*fnl+2);
183 bdb_escape_string(jcr, esc_name, fname, fnl);
185 Mmsg(cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
186 if (QueryDB(jcr, cmd)) {
188 num_rows = sql_num_rows();
190 Mmsg2(errmsg, _("More than one Filename!: %s for file: %s\n"),
191 edit_uint64(num_rows, ed1), fname);
192 Jmsg(jcr, M_WARNING, 0, "%s", errmsg);
195 if ((row = sql_fetch_row()) == NULL) {
196 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
198 FilenameId = str_to_int64(row[0]);
199 if (FilenameId <= 0) {
200 Mmsg2(errmsg, _("Get DB Filename record %s found bad record: %d\n"),
206 Mmsg1(errmsg, _("Filename record: %s not found.\n"), fname);
210 Mmsg(errmsg, _("Filename record: %s not found in Catalog.\n"), fname);
217 * Returns: 0 on failure
220 * DO NOT use Jmsg in this routine (see notes for get_file_record)
222 int BDB::bdb_get_path_record(JCR *jcr)
227 esc_name = check_pool_memory_size(esc_name, 2*pnl+2);
228 bdb_escape_string(jcr, esc_name, path, pnl);
230 if (cached_path_id != 0 && cached_path_len == pnl &&
231 strcmp(cached_path, path) == 0) {
232 return cached_path_id;
235 Mmsg(cmd, "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
237 if (QueryDB(jcr, cmd)) {
239 if (sql_num_rows() > 1) {
240 Mmsg2(errmsg, _("More than one Path!: %s for path: %s\n"),
241 edit_uint64(sql_num_rows(), ed1), path);
242 Jmsg(jcr, M_WARNING, 0, "%s", errmsg);
244 /* Even if there are multiple paths, take the first one */
245 if (sql_num_rows() >= 1) {
246 if ((row = sql_fetch_row()) == NULL) {
247 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
249 PathId = str_to_int64(row[0]);
251 Mmsg2(errmsg, _("Get DB path record %s found bad record: %s\n"),
252 cmd, edit_int64(PathId, ed1));
256 if (PathId != cached_path_id) {
257 cached_path_id = PathId;
258 cached_path_len = pnl;
259 pm_strcpy(cached_path, path);
264 Mmsg1(errmsg, _("Path record: %s not found.\n"), path);
268 Mmsg(errmsg, _("Path record: %s not found in Catalog.\n"), path);
275 * Get Job record for given JobId or Job name
276 * Returns: false on failure
279 bool BDB::bdb_get_job_record(JCR *jcr, JOB_DBR *jr)
283 char esc[MAX_ESCAPE_NAME_LENGTH];
286 if (jr->JobId == 0) {
287 bdb_escape_string(jcr, esc, jr->Job, strlen(jr->Job));
288 Mmsg(cmd, "SELECT VolSessionId,VolSessionTime,"
289 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
290 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
291 "SchedTime,RealEndTime,ReadBytes,HasBase,PurgedFiles "
292 "FROM Job WHERE Job='%s'", esc);
294 Mmsg(cmd, "SELECT VolSessionId,VolSessionTime,"
295 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
296 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
297 "SchedTime,RealEndTime,ReadBytes,HasBase,PurgedFiles "
298 "FROM Job WHERE JobId=%s",
299 edit_int64(jr->JobId, ed1));
302 if (!QueryDB(jcr, cmd)) {
304 return false; /* failed */
306 if ((row = sql_fetch_row()) == NULL) {
307 Mmsg1(errmsg, _("No Job found for JobId %s\n"), edit_int64(jr->JobId, ed1));
310 return false; /* failed */
313 jr->VolSessionId = str_to_uint64(row[0]);
314 jr->VolSessionTime = str_to_uint64(row[1]);
315 jr->PoolId = str_to_int64(row[2]);
316 bstrncpy(jr->cStartTime, row[3]!=NULL?row[3]:"", sizeof(jr->cStartTime));
317 bstrncpy(jr->cEndTime, row[4]!=NULL?row[4]:"", sizeof(jr->cEndTime));
318 jr->JobFiles = str_to_int64(row[5]);
319 jr->JobBytes = str_to_int64(row[6]);
320 jr->JobTDate = str_to_int64(row[7]);
321 bstrncpy(jr->Job, row[8]!=NULL?row[8]:"", sizeof(jr->Job));
322 jr->JobStatus = row[9]!=NULL?(int)*row[9]:JS_FatalError;
323 jr->JobType = row[10]!=NULL?(int)*row[10]:JT_BACKUP;
324 jr->JobLevel = row[11]!=NULL?(int)*row[11]:L_NONE;
325 jr->ClientId = str_to_uint64(row[12]!=NULL?row[12]:(char *)"");
326 bstrncpy(jr->Name, row[13]!=NULL?row[13]:"", sizeof(jr->Name));
327 jr->PriorJobId = str_to_uint64(row[14]!=NULL?row[14]:(char *)"");
328 bstrncpy(jr->cRealEndTime, row[15]!=NULL?row[15]:"", sizeof(jr->cRealEndTime));
329 if (jr->JobId == 0) {
330 jr->JobId = str_to_int64(row[16]);
332 jr->FileSetId = str_to_int64(row[17]);
333 bstrncpy(jr->cSchedTime, row[18]!=NULL?row[18]:"", sizeof(jr->cSchedTime));
334 bstrncpy(jr->cRealEndTime, row[19]!=NULL?row[19]:"", sizeof(jr->cRealEndTime));
335 jr->ReadBytes = str_to_int64(row[20]);
336 jr->StartTime = str_to_utime(jr->cStartTime);
337 jr->SchedTime = str_to_utime(jr->cSchedTime);
338 jr->EndTime = str_to_utime(jr->cEndTime);
339 jr->RealEndTime = str_to_utime(jr->cRealEndTime);
340 jr->HasBase = str_to_int64(row[21]);
341 jr->PurgedFiles = str_to_int64(row[22]);
349 * Find VolumeNames for a given JobId
350 * Returns: 0 on error or no Volumes found
351 * number of volumes on success
352 * Volumes are concatenated in VolumeNames
353 * separated by a vertical bar (|) in the order
354 * that they were written.
356 * Returns: number of volumes on success
358 int BDB::bdb_get_job_volume_names(JCR *jcr, 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", cmd);
375 if (QueryDB(jcr, cmd)) {
376 Dmsg1(130, "Num rows=%d\n", sql_num_rows());
377 if (sql_num_rows() <= 0) {
378 Mmsg1(errmsg, _("No volumes found for JobId=%d\n"), JobId);
381 stat = sql_num_rows();
382 for (i=0; i < stat; i++) {
383 if ((row = sql_fetch_row()) == NULL) {
384 Mmsg2(errmsg, _("Error fetching row %d: ERR=%s\n"), i, sql_strerror());
385 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
389 if (*VolumeNames[0] != 0) {
390 pm_strcat(VolumeNames, "|");
392 pm_strcat(VolumeNames, row[0]);
398 Mmsg(errmsg, _("No Volume for JobId %d found in Catalog.\n"), JobId);
405 * Find Volume parameters for a give JobId
406 * Returns: 0 on error or no Volumes found
407 * number of volumes on success
408 * List of Volumes and start/end file/blocks (malloced structure!)
410 * Returns: number of volumes on success
412 int BDB::bdb_get_job_volume_parameters(JCR *jcr, JobId_t JobId, VOL_PARAMS **VolParams)
418 VOL_PARAMS *Vols = NULL;
422 "SELECT VolumeName,MediaType,FirstIndex,LastIndex,StartFile,"
423 "JobMedia.EndFile,StartBlock,JobMedia.EndBlock,"
424 "Slot,StorageId,InChanger"
425 " FROM JobMedia,Media WHERE JobMedia.JobId=%s"
426 " AND JobMedia.MediaId=Media.MediaId ORDER BY VolIndex,JobMediaId",
427 edit_int64(JobId, ed1));
429 Dmsg1(130, "VolNam=%s\n", cmd);
430 if (QueryDB(jcr, cmd)) {
431 Dmsg1(200, "Num rows=%d\n", sql_num_rows());
432 if (sql_num_rows() <= 0) {
433 Mmsg1(errmsg, _("No volumes found for JobId=%d\n"), JobId);
436 stat = sql_num_rows();
439 *VolParams = Vols = (VOL_PARAMS *)malloc(stat * sizeof(VOL_PARAMS));
440 SId = (DBId_t *)malloc(stat * sizeof(DBId_t));
442 for (i=0; i < stat; i++) {
443 if ((row = sql_fetch_row()) == NULL) {
444 Mmsg2(errmsg, _("Error fetching row %d: ERR=%s\n"), i, sql_strerror());
445 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
450 uint32_t StartBlock, EndBlock, StartFile, EndFile;
451 bstrncpy(Vols[i].VolumeName, row[0], MAX_NAME_LENGTH);
452 bstrncpy(Vols[i].MediaType, row[1], MAX_NAME_LENGTH);
453 Vols[i].FirstIndex = str_to_uint64(row[2]);
454 Vols[i].LastIndex = str_to_uint64(row[3]);
455 StartFile = str_to_uint64(row[4]);
456 EndFile = str_to_uint64(row[5]);
457 StartBlock = str_to_uint64(row[6]);
458 EndBlock = str_to_uint64(row[7]);
459 Vols[i].StartAddr = (((uint64_t)StartFile)<<32) | StartBlock;
460 Vols[i].EndAddr = (((uint64_t)EndFile)<<32) | EndBlock;
461 Vols[i].Slot = str_to_uint64(row[8]);
462 StorageId = str_to_uint64(row[9]);
463 Vols[i].InChanger = str_to_uint64(row[10]);
464 Vols[i].Storage[0] = 0;
468 for (i=0; i < stat; i++) {
470 Mmsg(cmd, "SELECT Name from Storage WHERE StorageId=%s",
471 edit_int64(SId[i], ed1));
472 if (QueryDB(jcr, cmd)) {
473 if ((row = sql_fetch_row()) && row[0]) {
474 bstrncpy(Vols[i].Storage, row[0], MAX_NAME_LENGTH);
492 * Get the number of pool records
494 * Returns: -1 on failure
497 int BDB::bdb_get_num_pool_records(JCR *jcr)
502 Mmsg(cmd, "SELECT count(*) from Pool");
503 stat = get_sql_record_max(jcr, this);
509 * This function returns a list of all the Pool record ids.
510 * The caller must free ids if non-NULL.
512 * Returns 0: on failure
515 int BDB::bdb_get_pool_ids(JCR *jcr, int *num_ids, uint32_t *ids[])
524 Mmsg(cmd, "SELECT PoolId FROM Pool");
525 if (QueryDB(jcr, cmd)) {
526 *num_ids = sql_num_rows();
528 id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
529 while ((row = sql_fetch_row()) != NULL) {
530 id[i++] = str_to_uint64(row[0]);
537 Mmsg(errmsg, _("Pool id select failed: ERR=%s\n"), sql_strerror());
538 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
546 * This function returns a list of all the Client record ids.
547 * The caller must free ids if non-NULL.
549 * Returns 0: on failure
552 int BDB::bdb_get_client_ids(JCR *jcr, int *num_ids, uint32_t *ids[])
561 Mmsg(cmd, "SELECT ClientId FROM Client ORDER BY Name ASC");
562 if (QueryDB(jcr, cmd)) {
563 *num_ids = sql_num_rows();
565 id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
566 while ((row = sql_fetch_row()) != NULL) {
567 id[i++] = str_to_uint64(row[0]);
574 Mmsg(errmsg, _("Client id select failed: ERR=%s\n"), sql_strerror());
575 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
583 * Get Pool Id, Scratch Pool Id, Recycle Pool Id
584 * Returns: false on failure
587 bool BDB::bdb_get_pool_record(JCR *jcr, POOL_DBR *pdbr)
592 char esc[MAX_ESCAPE_NAME_LENGTH];
595 if (pdbr->PoolId != 0) { /* find by id */
597 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
598 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
599 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId,"
600 "ActionOnPurge FROM Pool WHERE Pool.PoolId=%s",
601 edit_int64(pdbr->PoolId, ed1));
602 } else { /* find by name */
603 bdb_escape_string(jcr, esc, pdbr->Name, strlen(pdbr->Name));
605 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
606 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
607 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId,"
608 "ActionOnPurge FROM Pool WHERE Pool.Name='%s'", esc);
610 if (QueryDB(jcr, cmd)) {
611 if (sql_num_rows() > 1) {
613 Mmsg1(errmsg, _("More than one Pool! Num=%s\n"),
614 edit_uint64(sql_num_rows(), ed1));
615 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
616 } else if (sql_num_rows() == 1) {
617 if ((row = sql_fetch_row()) == NULL) {
618 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
619 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
621 pdbr->PoolId = str_to_int64(row[0]);
622 bstrncpy(pdbr->Name, row[1]!=NULL?row[1]:"", sizeof(pdbr->Name));
623 pdbr->NumVols = str_to_int64(row[2]);
624 pdbr->MaxVols = str_to_int64(row[3]);
625 pdbr->UseOnce = str_to_int64(row[4]);
626 pdbr->UseCatalog = str_to_int64(row[5]);
627 pdbr->AcceptAnyVolume = str_to_int64(row[6]);
628 pdbr->AutoPrune = str_to_int64(row[7]);
629 pdbr->Recycle = str_to_int64(row[8]);
630 pdbr->VolRetention = str_to_int64(row[9]);
631 pdbr->VolUseDuration = str_to_int64(row[10]);
632 pdbr->MaxVolJobs = str_to_int64(row[11]);
633 pdbr->MaxVolFiles = str_to_int64(row[12]);
634 pdbr->MaxVolBytes = str_to_uint64(row[13]);
635 bstrncpy(pdbr->PoolType, row[14]!=NULL?row[14]:"", sizeof(pdbr->PoolType));
636 pdbr->LabelType = str_to_int64(row[15]);
637 bstrncpy(pdbr->LabelFormat, row[16]!=NULL?row[16]:"", sizeof(pdbr->LabelFormat));
638 pdbr->RecyclePoolId = str_to_int64(row[17]);
639 pdbr->ScratchPoolId = str_to_int64(row[18]);
640 pdbr->ActionOnPurge = str_to_int32(row[19]);
651 * If the PoolId is non-zero, we get its record,
652 * otherwise, we search on the PoolName and we compute the number of volumes
654 * Returns: false on failure
657 bool BDB::bdb_get_pool_numvols(JCR *jcr, POOL_DBR *pdbr)
662 ok = db_get_pool_record(jcr, this, pdbr);
667 Mmsg(cmd, "SELECT count(*) from Media WHERE PoolId=%s",
668 edit_int64(pdbr->PoolId, ed1));
669 NumVols = get_sql_record_max(jcr, this);
670 Dmsg2(400, "Actual NumVols=%d Pool NumVols=%d\n", NumVols, pdbr->NumVols);
671 if (NumVols != pdbr->NumVols) {
672 pdbr->NumVols = NumVols;
673 db_update_pool_record(jcr, this, pdbr);
676 Mmsg(errmsg, _("Pool record not found in Catalog.\n"));
683 * Free restoreobject record (some fields are allocated through malloc)
685 void db_free_restoreobject_record(JCR *jcr, ROBJECT_DBR *rr)
690 if (rr->object_name) {
691 free(rr->object_name);
693 if (rr->plugin_name) {
694 free(rr->plugin_name);
696 rr->object = rr->plugin_name = rr->object_name = NULL;
700 * Get RestoreObject Record
701 * If the RestoreObjectId is non-zero, we get its record
703 * You must call db_free_restoreobject_record() after db_get_restoreobject_record()
705 * Returns: false on failure
708 bool BDB::bdb_get_restoreobject_record(JCR *jcr, ROBJECT_DBR *rr)
717 "SELECT ObjectName, PluginName, ObjectType, JobId, ObjectCompression, "
718 "RestoreObject, ObjectLength, ObjectFullLength, FileIndex "
719 "FROM RestoreObject "
720 "WHERE RestoreObjectId=%s",
721 edit_int64(rr->RestoreObjectId, ed1));
723 /* Using the JobId permits to check the Job name against ACLs and
724 * make sure that the current user is authorized to see the Restore object
727 pm_strcat(cmd, " AND JobId=");
728 pm_strcat(cmd, edit_int64(rr->JobId, ed1));
730 } else if (rr->JobIds && is_a_number_list(rr->JobIds)) {
731 pm_strcat(cmd, " AND JobId IN (");
732 pm_strcat(cmd, rr->JobIds);
736 if (QueryDB(jcr, cmd)) {
737 if (sql_num_rows() > 1) {
739 Mmsg1(errmsg, _("Error got %s RestoreObjects but expected only one!\n"),
740 edit_uint64(sql_num_rows(), ed1));
741 sql_data_seek(sql_num_rows()-1);
743 if ((row = sql_fetch_row()) == NULL) {
744 Mmsg1(errmsg, _("RestoreObject record \"%d\" not found.\n"), rr->RestoreObjectId);
746 db_free_restoreobject_record(jcr, rr);
747 rr->object_name = bstrdup(row[0]);
748 rr->plugin_name = bstrdup(row[1]);
749 rr->FileType = str_to_uint64(row[2]);
750 rr->JobId = str_to_uint64(row[3]);
751 rr->object_compression = str_to_int64(row[4]);
752 rr->object_len = str_to_uint64(row[6]);
753 rr->object_full_len = str_to_uint64(row[7]);
754 rr->object_index = str_to_uint64(row[8]);
756 bdb_unescape_object(jcr,
758 rr->object_len, /* Object length */
761 if (rr->object_compression > 0) {
762 int out_len = rr->object_full_len + 100; /* full length */
763 char *obj = (char *)malloc(out_len);
764 Zinflate(cmd, rr->object_len, obj, out_len); /* out_len is updated */
765 if (out_len != (int)rr->object_full_len) {
766 Dmsg3(10, "Decompression failed. Len wanted=%d got=%d. Object=%s\n",
767 rr->object_full_len, out_len, rr->plugin_name);
769 Mmsg(errmsg, _("Decompression failed. Len wanted=%d got=%d. Object=%s\n"),
770 rr->object_full_len, out_len, rr->plugin_name);
774 rr->object_len = out_len;
777 rr->object = (char *)malloc(sizeof(char)*(len+1));
778 memcpy(rr->object, cmd, len);
780 rr->object_len = len;
787 Mmsg(errmsg, _("RestoreObject record not found in Catalog.\n"));
795 * If the ClientId is non-zero, we get its record,
796 * otherwise, we search on the Client Name
798 * Returns: 0 on failure
801 int BDB::bdb_get_client_record(JCR *jcr, CLIENT_DBR *cdbr)
806 char esc[MAX_ESCAPE_NAME_LENGTH];
809 if (cdbr->ClientId != 0) { /* find by id */
811 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
812 "FROM Client WHERE Client.ClientId=%s",
813 edit_int64(cdbr->ClientId, ed1));
814 } else { /* find by name */
815 bdb_escape_string(jcr, esc, cdbr->Name, strlen(cdbr->Name));
817 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
818 "FROM Client WHERE Client.Name='%s'", esc);
821 if (QueryDB(jcr, cmd)) {
822 if (sql_num_rows() > 1) {
823 Mmsg1(errmsg, _("More than one Client!: %s\n"),
824 edit_uint64(sql_num_rows(), ed1));
825 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
826 } else if (sql_num_rows() == 1) {
827 if ((row = sql_fetch_row()) == NULL) {
828 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
829 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
831 cdbr->ClientId = str_to_int64(row[0]);
832 bstrncpy(cdbr->Name, row[1]!=NULL?row[1]:"", sizeof(cdbr->Name));
833 bstrncpy(cdbr->Uname, row[2]!=NULL?row[2]:"", sizeof(cdbr->Uname));
834 cdbr->AutoPrune = str_to_int64(row[3]);
835 cdbr->FileRetention = str_to_int64(row[4]);
836 cdbr->JobRetention = str_to_int64(row[5]);
840 Mmsg(errmsg, _("Client record not found in Catalog.\n"));
844 Mmsg(errmsg, _("Client record not found in Catalog.\n"));
853 * Returns: 0 on failure
856 bool BDB::bdb_get_counter_record(JCR *jcr, COUNTER_DBR *cr)
859 char esc[MAX_ESCAPE_NAME_LENGTH];
862 bdb_escape_string(jcr, esc, cr->Counter, strlen(cr->Counter));
864 Mmsg(cmd, select_counter_values[bdb_get_type_index()], esc);
865 if (QueryDB(jcr, cmd)) {
867 /* If more than one, report error, but return first row */
868 if (sql_num_rows() > 1) {
869 Mmsg1(errmsg, _("More than one Counter!: %d\n"), sql_num_rows());
870 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
872 if (sql_num_rows() >= 1) {
873 if ((row = sql_fetch_row()) == NULL) {
874 Mmsg1(errmsg, _("error fetching Counter row: %s\n"), sql_strerror());
875 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
880 cr->MinValue = str_to_int64(row[0]);
881 cr->MaxValue = str_to_int64(row[1]);
882 cr->CurrentValue = str_to_int64(row[2]);
884 bstrncpy(cr->WrapCounter, row[3], sizeof(cr->WrapCounter));
886 cr->WrapCounter[0] = 0;
894 Mmsg(errmsg, _("Counter record: %s not found in Catalog.\n"), cr->Counter);
903 * If the FileSetId is non-zero, we get its record,
904 * otherwise, we search on the name
906 * Returns: 0 on failure
909 int BDB::bdb_get_fileset_record(JCR *jcr, FILESET_DBR *fsr)
914 char esc[MAX_ESCAPE_NAME_LENGTH];
917 if (fsr->FileSetId != 0) { /* find by id */
919 "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
920 "WHERE FileSetId=%s",
921 edit_int64(fsr->FileSetId, ed1));
922 } else { /* find by name */
923 bdb_escape_string(jcr, esc, fsr->FileSet, strlen(fsr->FileSet));
925 "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
926 "WHERE FileSet='%s' ORDER BY CreateTime DESC LIMIT 1", esc);
929 if (QueryDB(jcr, cmd)) {
930 if (sql_num_rows() > 1) {
932 Mmsg1(errmsg, _("Error got %s FileSets but expected only one!\n"),
933 edit_uint64(sql_num_rows(), ed1));
934 sql_data_seek(sql_num_rows()-1);
936 if ((row = sql_fetch_row()) == NULL) {
937 Mmsg1(errmsg, _("FileSet record \"%s\" not found.\n"), fsr->FileSet);
939 fsr->FileSetId = str_to_int64(row[0]);
940 bstrncpy(fsr->FileSet, row[1]!=NULL?row[1]:"", sizeof(fsr->FileSet));
941 bstrncpy(fsr->MD5, row[2]!=NULL?row[2]:"", sizeof(fsr->MD5));
942 bstrncpy(fsr->cCreateTime, row[3]!=NULL?row[3]:"", sizeof(fsr->cCreateTime));
943 stat = fsr->FileSetId;
947 Mmsg(errmsg, _("FileSet record not found in Catalog.\n"));
955 * Get the number of Media records
957 * Returns: -1 on failure
960 int BDB::bdb_get_num_media_records(JCR *jcr)
965 Mmsg(cmd, "SELECT count(*) from Media");
966 stat = get_sql_record_max(jcr, this);
972 * This function returns a list of all the Media record ids for
973 * the current Pool, the correct Media Type, Recyle, Enabled, StorageId, VolBytes
974 * VolumeName if specified
975 * The caller must free ids if non-NULL.
977 * Returns false: on failure
980 bool BDB::bdb_get_media_ids(JCR *jcr, MEDIA_DBR *mr, int *num_ids, uint32_t *ids[])
987 char buf[MAX_NAME_LENGTH*3]; /* Can contain MAX_NAME_LENGTH*2+1 + AND ....='' */
988 char esc[MAX_NAME_LENGTH*2+1];
993 Mmsg(cmd, "SELECT DISTINCT MediaId FROM Media WHERE Recycle=%d AND Enabled=%d ",
994 mr->Recycle, mr->Enabled);
996 if (*mr->MediaType) {
997 bdb_escape_string(jcr, esc, mr->MediaType, strlen(mr->MediaType));
998 bsnprintf(buf, sizeof(buf), "AND MediaType='%s' ", esc);
1002 if (mr->StorageId) {
1003 bsnprintf(buf, sizeof(buf), "AND StorageId=%s ", edit_uint64(mr->StorageId, ed1));
1004 pm_strcat(cmd, buf);
1008 bsnprintf(buf, sizeof(buf), "AND PoolId=%s ", edit_uint64(mr->PoolId, ed1));
1009 pm_strcat(cmd, buf);
1013 bsnprintf(buf, sizeof(buf), "AND VolBytes > %s ", edit_uint64(mr->VolBytes, ed1));
1014 pm_strcat(cmd, buf);
1017 if (*mr->VolumeName) {
1018 bdb_escape_string(jcr, esc, mr->VolumeName, strlen(mr->VolumeName));
1019 bsnprintf(buf, sizeof(buf), "AND VolumeName = '%s' ", esc);
1020 pm_strcat(cmd, buf);
1023 if (*mr->VolStatus) {
1024 bdb_escape_string(jcr, esc, mr->VolStatus, strlen(mr->VolStatus));
1025 bsnprintf(buf, sizeof(buf), "AND VolStatus = '%s' ", esc);
1026 pm_strcat(cmd, buf);
1029 Dmsg1(100, "q=%s\n", cmd);
1031 if (QueryDB(jcr, cmd)) {
1032 *num_ids = sql_num_rows();
1034 id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
1035 while ((row = sql_fetch_row()) != NULL) {
1036 id[i++] = str_to_uint64(row[0]);
1043 Mmsg(errmsg, _("Media id select failed: ERR=%s\n"), sql_strerror());
1044 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1053 * This function returns a list of all the DBIds that are returned
1056 * Returns false: on failure
1059 bool BDB::bdb_get_query_dbids(JCR *jcr, POOL_MEM &query, dbid_list &ids)
1067 if (QueryDB(jcr, query.c_str())) {
1068 ids.num_ids = sql_num_rows();
1069 if (ids.num_ids > 0) {
1070 if (ids.max_ids < ids.num_ids) {
1072 ids.DBId = (DBId_t *)malloc(ids.num_ids * sizeof(DBId_t));
1074 while ((row = sql_fetch_row()) != NULL) {
1075 ids.DBId[i++] = str_to_uint64(row[0]);
1081 Mmsg(errmsg, _("query dbids failed: ERR=%s\n"), sql_strerror());
1082 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1092 * Returns: false: on failure
1095 bool BDB::bdb_get_media_record(JCR *jcr, MEDIA_DBR *mr)
1100 char esc[MAX_ESCAPE_NAME_LENGTH];
1103 if (mr->MediaId == 0 && mr->VolumeName[0] == 0) {
1104 Mmsg(cmd, "SELECT count(*) from Media");
1105 mr->MediaId = get_sql_record_max(jcr, this);
1109 if (mr->MediaId != 0) { /* find by id */
1110 Mmsg(cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,"
1111 "VolBlocks,VolBytes,VolABytes,VolHoleBytes,VolHoles,VolMounts,"
1112 "VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
1113 "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
1114 "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
1115 "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
1116 "Enabled,LocationId,RecycleCount,InitialWrite,"
1117 "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge "
1118 "FROM Media WHERE MediaId=%s",
1119 edit_int64(mr->MediaId, ed1));
1120 } else { /* find by name */
1121 bdb_escape_string(jcr, esc, mr->VolumeName, strlen(mr->VolumeName));
1122 Mmsg(cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,"
1123 "VolBlocks,VolBytes,VolABytes,VolHoleBytes,VolHoles,VolMounts,"
1124 "VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
1125 "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
1126 "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
1127 "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
1128 "Enabled,LocationId,RecycleCount,InitialWrite,"
1129 "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge "
1130 "FROM Media WHERE VolumeName='%s'", esc);
1133 if (QueryDB(jcr, cmd)) {
1135 if (sql_num_rows() > 1) {
1136 Mmsg1(errmsg, _("More than one Volume!: %s\n"),
1137 edit_uint64(sql_num_rows(), ed1));
1138 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1139 } else if (sql_num_rows() == 1) {
1140 if ((row = sql_fetch_row()) == NULL) {
1141 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
1142 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1145 mr->MediaId = str_to_int64(row[0]);
1146 bstrncpy(mr->VolumeName, row[1]!=NULL?row[1]:"", sizeof(mr->VolumeName));
1147 mr->VolJobs = str_to_int64(row[2]);
1148 mr->VolFiles = str_to_int64(row[3]);
1149 mr->VolBlocks = str_to_int64(row[4]);
1150 mr->VolBytes = str_to_uint64(row[5]);
1151 mr->VolABytes = str_to_uint64(row[6]);
1152 mr->VolHoleBytes = str_to_uint64(row[7]);
1153 mr->VolHoles = str_to_int64(row[8]);
1154 mr->VolMounts = str_to_int64(row[9]);
1155 mr->VolErrors = str_to_int64(row[10]);
1156 mr->VolWrites = str_to_int64(row[11]);
1157 mr->MaxVolBytes = str_to_uint64(row[12]);
1158 mr->VolCapacityBytes = str_to_uint64(row[13]);
1159 bstrncpy(mr->MediaType, row[14]!=NULL?row[14]:"", sizeof(mr->MediaType));
1160 bstrncpy(mr->VolStatus, row[15]!=NULL?row[15]:"", sizeof(mr->VolStatus));
1161 mr->PoolId = str_to_int64(row[16]);
1162 mr->VolRetention = str_to_uint64(row[17]);
1163 mr->VolUseDuration = str_to_uint64(row[18]);
1164 mr->MaxVolJobs = str_to_int64(row[19]);
1165 mr->MaxVolFiles = str_to_int64(row[20]);
1166 mr->Recycle = str_to_int64(row[21]);
1167 mr->Slot = str_to_int64(row[22]);
1168 bstrncpy(mr->cFirstWritten, row[23]!=NULL?row[23]:"", sizeof(mr->cFirstWritten));
1169 mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten);
1170 bstrncpy(mr->cLastWritten, row[24]!=NULL?row[24]:"", sizeof(mr->cLastWritten));
1171 mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
1172 mr->InChanger = str_to_uint64(row[25]);
1173 mr->EndFile = str_to_uint64(row[26]);
1174 mr->EndBlock = str_to_uint64(row[27]);
1175 mr->VolType = str_to_int64(row[28]); /* formerly VolParts */
1176 mr->LabelType = str_to_int64(row[29]);
1177 bstrncpy(mr->cLabelDate, row[30]!=NULL?row[30]:"", sizeof(mr->cLabelDate));
1178 mr->LabelDate = (time_t)str_to_utime(mr->cLabelDate);
1179 mr->StorageId = str_to_int64(row[31]);
1180 mr->Enabled = str_to_int64(row[32]);
1181 mr->LocationId = str_to_int64(row[33]);
1182 mr->RecycleCount = str_to_int64(row[34]);
1183 bstrncpy(mr->cInitialWrite, row[35]!=NULL?row[35]:"", sizeof(mr->cInitialWrite));
1184 mr->InitialWrite = (time_t)str_to_utime(mr->cInitialWrite);
1185 mr->ScratchPoolId = str_to_int64(row[36]);
1186 mr->RecyclePoolId = str_to_int64(row[37]);
1187 mr->VolReadTime = str_to_int64(row[38]);
1188 mr->VolWriteTime = str_to_int64(row[39]);
1189 mr->ActionOnPurge = str_to_int32(row[40]);
1194 if (mr->MediaId != 0) {
1195 Mmsg1(errmsg, _("Media record with MediaId=%s not found.\n"),
1196 edit_int64(mr->MediaId, ed1));
1198 Mmsg1(errmsg, _("Media record for Volume name \"%s\" not found.\n"),
1204 if (mr->MediaId != 0) {
1205 Mmsg(errmsg, _("Media record for MediaId=%u not found in Catalog.\n"),
1208 Mmsg(errmsg, _("Media record for Volume Name \"%s\" not found in Catalog.\n"),
1215 /* Remove all MD5 from a query (can save lot of memory with many files) */
1216 static void strip_md5(char *q)
1219 while ((p = strstr(p, ", MD5"))) {
1220 memset(p, ' ', 5 * sizeof(char));
1225 * Find the last "accurate" backup state (that can take deleted files in
1227 * 1) Get all files with jobid in list (F subquery)
1228 * Get all files in BaseFiles with jobid in list
1229 * 2) Take only the last version of each file (Temp subquery) => accurate list
1231 * 3) Join the result to file table to get fileindex, jobid and lstat information
1233 * TODO: See if we can do the SORT only if needed (as an argument)
1235 bool BDB::bdb_get_file_list(JCR *jcr, char *jobids,
1236 bool use_md5, bool use_delta,
1237 DB_RESULT_HANDLER *result_handler, void *ctx)
1241 Mmsg(errmsg, _("ERR=JobIds are empty\n"));
1245 POOL_MEM buf(PM_MESSAGE);
1246 POOL_MEM buf2(PM_MESSAGE);
1248 Mmsg(buf2, select_recent_version_with_basejob_and_delta[bdb_get_type_index()],
1249 jobids, jobids, jobids, jobids);
1252 Mmsg(buf2, select_recent_version_with_basejob[bdb_get_type_index()],
1253 jobids, jobids, jobids, jobids);
1256 /* bsr code is optimized for JobId sorted, with Delta, we need to get
1257 * them ordered by date. JobTDate and JobId can be mixed if using Copy
1261 "SELECT Path.Path, Filename.Name, T1.FileIndex, T1.JobId, LStat, DeltaSeq, MD5 "
1262 "FROM ( %s ) AS T1 "
1263 "JOIN Filename ON (Filename.FilenameId = T1.FilenameId) "
1264 "JOIN Path ON (Path.PathId = T1.PathId) "
1265 "WHERE FileIndex > 0 "
1266 "ORDER BY T1.JobTDate, FileIndex ASC",/* Return sorted by JobTDate */
1267 /* FileIndex for restore code */
1271 strip_md5(buf.c_str());
1274 Dmsg1(100, "q=%s\n", buf.c_str());
1276 return bdb_big_sql_query(buf.c_str(), result_handler, ctx);
1280 * This procedure gets the base jobid list used by jobids,
1282 bool BDB::bdb_get_used_base_jobids(JCR *jcr,
1283 POOLMEM *jobids, db_list_ctx *result)
1288 "SELECT DISTINCT BaseJobId "
1289 " FROM Job JOIN BaseFiles USING (JobId) "
1290 " WHERE Job.HasBase = 1 "
1291 " AND Job.JobId IN (%s) ", jobids);
1292 return bdb_sql_query(buf.c_str(), db_list_handler, result);
1296 * The decision do change an incr/diff was done before
1298 * Differential : get the last full id
1299 * Incremental : get the last full + last diff + last incr(s) ids
1301 * If you specify jr->StartTime, it will be used to limit the search
1302 * in the time. (usually now)
1304 * TODO: look and merge from ua_restore.c
1306 bool BDB::bdb_get_accurate_jobids(JCR *jcr,
1307 JOB_DBR *jr, db_list_ctx *jobids)
1310 char clientid[50], jobid[50], filesetid[50];
1311 char date[MAX_TIME_LENGTH];
1312 POOL_MEM query(PM_FNAME);
1314 /* Take the current time as upper limit if nothing else specified */
1315 utime_t StartTime = (jr->StartTime)?jr->StartTime:time(NULL);
1317 bstrutime(date, sizeof(date), StartTime + 1);
1320 /* First, find the last good Full backup for this job/client/fileset */
1321 Mmsg(query, create_temp_accurate_jobids[bdb_get_type_index()],
1322 edit_uint64(jcr->JobId, jobid),
1323 edit_uint64(jr->ClientId, clientid),
1325 edit_uint64(jr->FileSetId, filesetid));
1327 if (!bdb_sql_query(query.c_str(), NULL, NULL)) {
1331 if (jr->JobLevel == L_INCREMENTAL || jr->JobLevel == L_VIRTUAL_FULL) {
1332 /* Now, find the last differential backup after the last full */
1334 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
1335 "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1336 "FROM Job JOIN FileSet USING (FileSetId) "
1337 "WHERE ClientId = %s "
1338 "AND Level='D' AND JobStatus IN ('T','W') AND Type='B' "
1339 "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
1340 "AND StartTime < '%s' "
1341 "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1342 "ORDER BY Job.JobTDate DESC LIMIT 1 ",
1349 if (!bdb_sql_query(query.c_str(), NULL, NULL)) {
1353 /* We just have to take all incremental after the last Full/Diff */
1355 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
1356 "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1357 "FROM Job JOIN FileSet USING (FileSetId) "
1358 "WHERE ClientId = %s "
1359 "AND Level='I' AND JobStatus IN ('T','W') AND Type='B' "
1360 "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
1361 "AND StartTime < '%s' "
1362 "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1363 "ORDER BY Job.JobTDate DESC ",
1369 if (!bdb_sql_query(query.c_str(), NULL, NULL)) {
1374 /* build a jobid list ie: 1,2,3,4 */
1375 Mmsg(query, "SELECT JobId FROM btemp3%s ORDER by JobTDate", jobid);
1376 bdb_sql_query(query.c_str(), db_list_handler, jobids);
1377 Dmsg1(1, "db_get_accurate_jobids=%s\n", jobids->list);
1381 Mmsg(query, "DROP TABLE btemp3%s", jobid);
1382 bdb_sql_query(query.c_str(), NULL, NULL);
1387 bool BDB::bdb_get_base_file_list(JCR *jcr, bool use_md5,
1388 DB_RESULT_HANDLER *result_handler, void *ctx)
1390 POOL_MEM buf(PM_MESSAGE);
1393 "SELECT Path, Name, FileIndex, JobId, LStat, 0 As DeltaSeq, MD5 "
1394 "FROM new_basefile%lld ORDER BY JobId, FileIndex ASC",
1395 (uint64_t) jcr->JobId);
1398 strip_md5(buf.c_str());
1400 return bdb_sql_query(buf.c_str(), result_handler, ctx);
1403 bool BDB::bdb_get_base_jobid(JCR *jcr, JOB_DBR *jr, JobId_t *jobid)
1405 POOL_MEM query(PM_FNAME);
1408 char date[MAX_TIME_LENGTH];
1409 char esc[MAX_ESCAPE_NAME_LENGTH];
1416 StartTime = (jr->StartTime)?jr->StartTime:time(NULL);
1417 bstrutime(date, sizeof(date), StartTime + 1);
1418 bdb_escape_string(jcr, esc, jr->Name, strlen(jr->Name));
1420 /* we can take also client name, fileset, etc... */
1423 "SELECT JobId, Job, StartTime, EndTime, JobTDate, PurgedFiles "
1425 // "JOIN FileSet USING (FileSetId) JOIN Client USING (ClientId) "
1426 "WHERE Job.Name = '%s' "
1427 "AND Level='B' AND JobStatus IN ('T','W') AND Type='B' "
1428 // "AND FileSet.FileSet= '%s' "
1429 // "AND Client.Name = '%s' "
1430 "AND StartTime<'%s' "
1431 "ORDER BY Job.JobTDate DESC LIMIT 1",
1433 // edit_uint64(jr->ClientId, clientid),
1434 // edit_uint64(jr->FileSetId, filesetid));
1437 Dmsg1(10, "db_get_base_jobid q=%s\n", query.c_str());
1438 if (!bdb_sql_query(query.c_str(), db_int64_handler, &lctx)) {
1441 *jobid = (JobId_t) lctx.value;
1443 Dmsg1(10, "db_get_base_jobid=%lld\n", *jobid);
1450 /* Get JobIds associated with a volume */
1451 bool BDB::bdb_get_volume_jobids(JCR *jcr,
1452 MEDIA_DBR *mr, db_list_ctx *lst)
1458 Mmsg(cmd, "SELECT DISTINCT JobId FROM JobMedia WHERE MediaId=%s",
1459 edit_int64(mr->MediaId, ed1));
1460 ret = bdb_sql_query(cmd, db_list_handler, lst);
1466 * Get Snapshot Record
1468 * Returns: false: on failure
1471 bool BDB::bdb_get_snapshot_record(JCR *jcr, SNAPSHOT_DBR *sr)
1476 char esc[MAX_ESCAPE_NAME_LENGTH];
1477 POOL_MEM filter1, filter2;
1479 if (sr->SnapshotId == 0 && (sr->Name[0] == 0 || sr->Device[0] == 0)) {
1480 Dmsg0(10, "No SnapshotId or Name/Device provided\n");
1486 if (sr->SnapshotId != 0) { /* find by id */
1487 Mmsg(filter1, "Snapshot.SnapshotId=%d", sr->SnapshotId);
1489 } else if (*sr->Name && *sr->Device) { /* find by name */
1490 bdb_escape_string(jcr, esc, sr->Name, strlen(sr->Name));
1491 Mmsg(filter1, "Snapshot.Name='%s'", esc);
1492 bdb_escape_string(jcr, esc, sr->Device, strlen(sr->Device));
1493 Mmsg(filter2, "AND Snapshot.Device='%s'", esc);
1496 Dmsg0(10, "No SnapshotId or Name and Device\n");
1500 Mmsg(cmd, "SELECT SnapshotId, Snapshot.Name, JobId, Snapshot.FileSetId, "
1501 "FileSet.FileSet, CreateTDate, CreateDate, "
1502 "Client.Name AS Client, Snapshot.ClientId, Volume, Device, Type, Retention, "
1503 "Comment FROM Snapshot JOIN Client USING (ClientId) LEFT JOIN FileSet USING (FileSetId) WHERE %s %s",
1504 filter1.c_str(), filter2.c_str());
1506 if (QueryDB(jcr, cmd)) {
1508 if (sql_num_rows() > 1) {
1509 Mmsg1(errmsg, _("More than one Snapshot!: %s\n"),
1510 edit_uint64(sql_num_rows(), ed1));
1511 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1512 } else if (sql_num_rows() == 1) {
1513 if ((row = sql_fetch_row()) == NULL) {
1514 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
1515 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1519 sr->need_to_free = true;
1520 sr->SnapshotId = str_to_int64(row[0]);
1521 bstrncpy(sr->Name, row[1], sizeof(sr->Name));
1522 sr->JobId = str_to_int64(row[2]);
1523 sr->FileSetId = str_to_int64(row[3]);
1524 bstrncpy(sr->FileSet, row[4], sizeof(sr->FileSet));
1525 sr->CreateTDate = str_to_uint64(row[5]);
1526 bstrncpy(sr->CreateDate, row[6], sizeof(sr->CreateDate));
1527 bstrncpy(sr->Client, row[7], sizeof(sr->Client));
1528 sr->ClientId = str_to_int64(row[8]);
1529 sr->Volume = bstrdup(row[9]);
1530 sr->Device = bstrdup(row[10]);
1531 bstrncpy(sr->Type, row[11], sizeof(sr->Type));
1532 sr->Retention = str_to_int64(row[12]);
1533 bstrncpy(sr->Comment, NPRTB(row[13]), sizeof(sr->Comment));
1537 if (sr->SnapshotId != 0) {
1538 Mmsg1(errmsg, _("Snapshot record with SnapshotId=%s not found.\n"),
1539 edit_int64(sr->SnapshotId, ed1));
1541 Mmsg1(errmsg, _("Snapshot record for Snapshot name \"%s\" not found.\n"),
1547 if (sr->SnapshotId != 0) {
1548 Mmsg1(errmsg, _("Snapshot record with SnapshotId=%s not found.\n"),
1549 edit_int64(sr->SnapshotId, ed1));
1551 Mmsg1(errmsg, _("Snapshot record for Snapshot name \"%s\" not found.\n"),
1559 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */