2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many 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 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Bacula Catalog Database Get record interface routines
21 * Note, these routines generally get a record by id or
22 * by name. If more logic is involved, the routine
25 * Written by Kern Sibbald, March 2000
31 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
35 /* -----------------------------------------------------------------------
37 * Generic Routines (or almost generic)
39 * -----------------------------------------------------------------------
43 * Given a full filename (with path), look up the File record
44 * (with attributes) in the database.
46 * Returns: false on failure
47 * true on success with the File record in FILE_DBR
49 bool BDB::bdb_get_file_attributes_record(JCR *jcr, char *afname, JOB_DBR *jr, FILE_DBR *fdbr)
53 Dmsg1(500, "db_get_file_att_record fname=%s \n", afname);
57 split_path_and_file(jcr, this, afname);
59 fdbr->FilenameId = bdb_get_filename_record(jcr);
61 fdbr->PathId = bdb_get_path_record(jcr);
63 ok = bdb_get_file_record(jcr, jr, fdbr);
74 * DO NOT use Jmsg in this routine.
76 * Note in this routine, we do not use Jmsg because it may be
77 * called to get attributes of a non-existent file, which is
78 * "normal" if a new file is found during Verify.
80 * The following is a bit of a kludge: because we always backup a
81 * directory entry, we can end up with two copies of the directory
82 * in the backup. One is when we encounter the directory and find
83 * we cannot recurse into it, and the other is when we find an
84 * explicit mention of the directory. This can also happen if the
85 * use includes the directory twice. In this case, Verify
86 * VolumeToCatalog fails because we have two copies in the catalog, and
87 * only the first one is marked (twice). So, when calling from Verify,
88 * VolumeToCatalog jr is not NULL, and we know jr->FileIndex is the fileindex
89 * of the version of the directory/file we actually want and do
90 * a more explicit SQL search.
92 * Returns: false on failure
96 bool BDB::bdb_get_file_record(JCR *jcr, JOB_DBR *jr, FILE_DBR *fdbr)
100 char ed1[50], ed2[50], ed3[50];
102 switch (jcr->getJobLevel()) {
103 case L_VERIFY_VOLUME_TO_CATALOG:
105 "SELECT FileId, LStat, MD5 FROM File WHERE File.JobId=%s AND File.PathId=%s AND "
106 "File.FilenameId=%s AND File.FileIndex=%u",
107 edit_int64(fdbr->JobId, ed1),
108 edit_int64(fdbr->PathId, ed2),
109 edit_int64(fdbr->FilenameId,ed3),
112 case L_VERIFY_DISK_TO_CATALOG:
114 "SELECT FileId, LStat, MD5 FROM File,Job WHERE "
115 "File.JobId=Job.JobId AND File.PathId=%s AND "
116 "File.FilenameId=%s AND Job.Type='B' AND Job.JobStatus IN ('T','W') AND "
117 "ClientId=%s ORDER BY StartTime DESC LIMIT 1",
118 edit_int64(fdbr->PathId, ed1),
119 edit_int64(fdbr->FilenameId, ed2),
120 edit_int64(jr->ClientId,ed3));
124 "SELECT FileId, LStat, MD5 FROM File WHERE File.JobId=%s AND File.PathId=%s AND "
125 "File.FilenameId=%s",
126 edit_int64(fdbr->JobId, ed1),
127 edit_int64(fdbr->PathId, ed2),
128 edit_int64(fdbr->FilenameId,ed3));
132 Dmsg3(450, "Get_file_record JobId=%u FilenameId=%u PathId=%u\n",
133 fdbr->JobId, fdbr->FilenameId, fdbr->PathId);
135 Dmsg1(100, "Query=%s\n", cmd);
137 if (QueryDB(jcr, cmd)) {
138 Dmsg1(100, "get_file_record sql_num_rows()=%d\n", sql_num_rows());
139 if (sql_num_rows() >= 1) {
140 if ((row = sql_fetch_row()) == NULL) {
141 Mmsg1(errmsg, _("Error fetching row: %s\n"), sql_strerror());
143 fdbr->FileId = (FileId_t)str_to_int64(row[0]);
144 bstrncpy(fdbr->LStat, row[1], sizeof(fdbr->LStat));
145 bstrncpy(fdbr->Digest, row[2], sizeof(fdbr->Digest));
147 if (sql_num_rows() > 1) {
148 Mmsg3(errmsg, _("get_file_record want 1 got rows=%d PathId=%s FilenameId=%s\n"),
150 edit_int64(fdbr->PathId, ed1),
151 edit_int64(fdbr->FilenameId, ed2));
152 Dmsg1(000, "=== Problem! %s", errmsg);
156 Mmsg2(errmsg, _("File record for PathId=%s FilenameId=%s not found.\n"),
157 edit_int64(fdbr->PathId, ed1),
158 edit_int64(fdbr->FilenameId, ed2));
162 Mmsg(errmsg, _("File record not found in Catalog.\n"));
168 * Get Filename record
169 * Returns: 0 on failure
170 * FilenameId on success
172 * DO NOT use Jmsg in this routine (see notes for get_file_record)
174 int BDB::bdb_get_filename_record(JCR *jcr)
179 esc_name = check_pool_memory_size(esc_name, 2*fnl+2);
180 bdb_escape_string(jcr, esc_name, fname, fnl);
182 Mmsg(cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
183 if (QueryDB(jcr, cmd)) {
185 if (sql_num_rows() > 1) {
186 Mmsg2(errmsg, _("More than one Filename!: %s for file: %s\n"),
187 edit_uint64(sql_num_rows(), ed1), fname);
188 Jmsg(jcr, M_WARNING, 0, "%s", errmsg);
190 if (sql_num_rows() >= 1) {
191 if ((row = sql_fetch_row()) == NULL) {
192 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
194 FilenameId = str_to_int64(row[0]);
195 if (FilenameId <= 0) {
196 Mmsg2(errmsg, _("Get DB Filename record %s found bad record: %d\n"),
202 Mmsg1(errmsg, _("Filename record: %s not found.\n"), fname);
206 Mmsg(errmsg, _("Filename record: %s not found in Catalog.\n"), fname);
213 * Returns: 0 on failure
216 * DO NOT use Jmsg in this routine (see notes for get_file_record)
218 int BDB::bdb_get_path_record(JCR *jcr)
223 esc_name = check_pool_memory_size(esc_name, 2*pnl+2);
224 bdb_escape_string(jcr, esc_name, path, pnl);
226 if (cached_path_id != 0 && cached_path_len == pnl &&
227 strcmp(cached_path, path) == 0) {
228 return cached_path_id;
231 Mmsg(cmd, "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
233 if (QueryDB(jcr, cmd)) {
235 if (sql_num_rows() > 1) {
236 Mmsg2(errmsg, _("More than one Path!: %s for path: %s\n"),
237 edit_uint64(sql_num_rows(), ed1), path);
238 Jmsg(jcr, M_WARNING, 0, "%s", errmsg);
240 /* Even if there are multiple paths, take the first one */
241 if (sql_num_rows() >= 1) {
242 if ((row = sql_fetch_row()) == NULL) {
243 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
245 PathId = str_to_int64(row[0]);
247 Mmsg2(errmsg, _("Get DB path record %s found bad record: %s\n"),
248 cmd, edit_int64(PathId, ed1));
252 if (PathId != cached_path_id) {
253 cached_path_id = PathId;
254 cached_path_len = pnl;
255 pm_strcpy(cached_path, path);
260 Mmsg1(errmsg, _("Path record: %s not found.\n"), path);
264 Mmsg(errmsg, _("Path record: %s not found in Catalog.\n"), path);
271 * Get Job record for given JobId or Job name
272 * Returns: false on failure
275 bool BDB::bdb_get_job_record(JCR *jcr, JOB_DBR *jr)
279 char esc[MAX_ESCAPE_NAME_LENGTH];
282 if (jr->JobId == 0) {
283 bdb_escape_string(jcr, esc, jr->Job, strlen(jr->Job));
284 Mmsg(cmd, "SELECT VolSessionId,VolSessionTime,"
285 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
286 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
287 "SchedTime,RealEndTime,ReadBytes,HasBase,PurgedFiles "
288 "FROM Job WHERE Job='%s'", esc);
290 Mmsg(cmd, "SELECT VolSessionId,VolSessionTime,"
291 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
292 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
293 "SchedTime,RealEndTime,ReadBytes,HasBase,PurgedFiles "
294 "FROM Job WHERE JobId=%s",
295 edit_int64(jr->JobId, ed1));
298 if (!QueryDB(jcr, cmd)) {
300 return false; /* failed */
302 if ((row = sql_fetch_row()) == NULL) {
303 Mmsg1(errmsg, _("No Job found for JobId %s\n"), edit_int64(jr->JobId, ed1));
306 return false; /* failed */
309 jr->VolSessionId = str_to_uint64(row[0]);
310 jr->VolSessionTime = str_to_uint64(row[1]);
311 jr->PoolId = str_to_int64(row[2]);
312 bstrncpy(jr->cStartTime, row[3]!=NULL?row[3]:"", sizeof(jr->cStartTime));
313 bstrncpy(jr->cEndTime, row[4]!=NULL?row[4]:"", sizeof(jr->cEndTime));
314 jr->JobFiles = str_to_int64(row[5]);
315 jr->JobBytes = str_to_int64(row[6]);
316 jr->JobTDate = str_to_int64(row[7]);
317 bstrncpy(jr->Job, row[8]!=NULL?row[8]:"", sizeof(jr->Job));
318 jr->JobStatus = row[9]!=NULL?(int)*row[9]:JS_FatalError;
319 jr->JobType = row[10]!=NULL?(int)*row[10]:JT_BACKUP;
320 jr->JobLevel = row[11]!=NULL?(int)*row[11]:L_NONE;
321 jr->ClientId = str_to_uint64(row[12]!=NULL?row[12]:(char *)"");
322 bstrncpy(jr->Name, row[13]!=NULL?row[13]:"", sizeof(jr->Name));
323 jr->PriorJobId = str_to_uint64(row[14]!=NULL?row[14]:(char *)"");
324 bstrncpy(jr->cRealEndTime, row[15]!=NULL?row[15]:"", sizeof(jr->cRealEndTime));
325 if (jr->JobId == 0) {
326 jr->JobId = str_to_int64(row[16]);
328 jr->FileSetId = str_to_int64(row[17]);
329 bstrncpy(jr->cSchedTime, row[18]!=NULL?row[18]:"", sizeof(jr->cSchedTime));
330 bstrncpy(jr->cRealEndTime, row[19]!=NULL?row[19]:"", sizeof(jr->cRealEndTime));
331 jr->ReadBytes = str_to_int64(row[20]);
332 jr->StartTime = str_to_utime(jr->cStartTime);
333 jr->SchedTime = str_to_utime(jr->cSchedTime);
334 jr->EndTime = str_to_utime(jr->cEndTime);
335 jr->RealEndTime = str_to_utime(jr->cRealEndTime);
336 jr->HasBase = str_to_int64(row[21]);
337 jr->PurgedFiles = str_to_int64(row[22]);
345 * Find VolumeNames for a given JobId
346 * Returns: 0 on error or no Volumes found
347 * number of volumes on success
348 * Volumes are concatenated in VolumeNames
349 * separated by a vertical bar (|) in the order
350 * that they were written.
352 * Returns: number of volumes on success
354 int BDB::bdb_get_job_volume_names(JCR *jcr, JobId_t JobId, POOLMEM **VolumeNames)
362 /* Get one entry per VolumeName, but "sort" by VolIndex */
364 "SELECT VolumeName,MAX(VolIndex) FROM JobMedia,Media WHERE "
365 "JobMedia.JobId=%s AND JobMedia.MediaId=Media.MediaId "
366 "GROUP BY VolumeName "
367 "ORDER BY 2 ASC", edit_int64(JobId,ed1));
369 Dmsg1(130, "VolNam=%s\n", cmd);
371 if (QueryDB(jcr, cmd)) {
372 Dmsg1(130, "Num rows=%d\n", sql_num_rows());
373 if (sql_num_rows() <= 0) {
374 Mmsg1(errmsg, _("No volumes found for JobId=%d\n"), JobId);
377 stat = sql_num_rows();
378 for (i=0; i < stat; i++) {
379 if ((row = sql_fetch_row()) == NULL) {
380 Mmsg2(errmsg, _("Error fetching row %d: ERR=%s\n"), i, sql_strerror());
381 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
385 if (*VolumeNames[0] != 0) {
386 pm_strcat(VolumeNames, "|");
388 pm_strcat(VolumeNames, row[0]);
394 Mmsg(errmsg, _("No Volume for JobId %d found in Catalog.\n"), JobId);
401 * Find Volume parameters for a give JobId
402 * Returns: 0 on error or no Volumes found
403 * number of volumes on success
404 * List of Volumes and start/end file/blocks (malloced structure!)
406 * Returns: number of volumes on success
408 int BDB::bdb_get_job_volume_parameters(JCR *jcr, JobId_t JobId, VOL_PARAMS **VolParams)
414 VOL_PARAMS *Vols = NULL;
418 "SELECT VolumeName,MediaType,FirstIndex,LastIndex,StartFile,"
419 "JobMedia.EndFile,StartBlock,JobMedia.EndBlock,"
420 "Slot,StorageId,InChanger"
421 " FROM JobMedia,Media WHERE JobMedia.JobId=%s"
422 " AND JobMedia.MediaId=Media.MediaId ORDER BY VolIndex,JobMediaId",
423 edit_int64(JobId, ed1));
425 Dmsg1(130, "VolNam=%s\n", cmd);
426 if (QueryDB(jcr, cmd)) {
427 Dmsg1(200, "Num rows=%d\n", sql_num_rows());
428 if (sql_num_rows() <= 0) {
429 Mmsg1(errmsg, _("No volumes found for JobId=%d\n"), JobId);
432 stat = sql_num_rows();
435 *VolParams = Vols = (VOL_PARAMS *)malloc(stat * sizeof(VOL_PARAMS));
436 SId = (DBId_t *)malloc(stat * sizeof(DBId_t));
438 for (i=0; i < stat; i++) {
439 if ((row = sql_fetch_row()) == NULL) {
440 Mmsg2(errmsg, _("Error fetching row %d: ERR=%s\n"), i, sql_strerror());
441 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
446 uint32_t StartBlock, EndBlock, StartFile, EndFile;
447 bstrncpy(Vols[i].VolumeName, row[0], MAX_NAME_LENGTH);
448 bstrncpy(Vols[i].MediaType, row[1], MAX_NAME_LENGTH);
449 Vols[i].FirstIndex = str_to_uint64(row[2]);
450 Vols[i].LastIndex = str_to_uint64(row[3]);
451 StartFile = str_to_uint64(row[4]);
452 EndFile = str_to_uint64(row[5]);
453 StartBlock = str_to_uint64(row[6]);
454 EndBlock = str_to_uint64(row[7]);
455 Vols[i].StartAddr = (((uint64_t)StartFile)<<32) | StartBlock;
456 Vols[i].EndAddr = (((uint64_t)EndFile)<<32) | EndBlock;
457 Vols[i].Slot = str_to_uint64(row[8]);
458 StorageId = str_to_uint64(row[9]);
459 Vols[i].InChanger = str_to_uint64(row[10]);
460 Vols[i].Storage[0] = 0;
464 for (i=0; i < stat; i++) {
466 Mmsg(cmd, "SELECT Name from Storage WHERE StorageId=%s",
467 edit_int64(SId[i], ed1));
468 if (QueryDB(jcr, cmd)) {
469 if ((row = sql_fetch_row()) && row[0]) {
470 bstrncpy(Vols[i].Storage, row[0], MAX_NAME_LENGTH);
488 * Get the number of pool records
490 * Returns: -1 on failure
493 int BDB::bdb_get_num_pool_records(JCR *jcr)
498 Mmsg(cmd, "SELECT count(*) from Pool");
499 stat = get_sql_record_max(jcr, this);
505 * This function returns a list of all the Pool record ids.
506 * The caller must free ids if non-NULL.
508 * Returns 0: on failure
511 int BDB::bdb_get_pool_ids(JCR *jcr, int *num_ids, uint32_t *ids[])
520 Mmsg(cmd, "SELECT PoolId FROM Pool");
521 if (QueryDB(jcr, cmd)) {
522 *num_ids = sql_num_rows();
524 id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
525 while ((row = sql_fetch_row()) != NULL) {
526 id[i++] = str_to_uint64(row[0]);
533 Mmsg(errmsg, _("Pool id select failed: ERR=%s\n"), sql_strerror());
534 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
542 * This function returns a list of all the Client record ids.
543 * The caller must free ids if non-NULL.
545 * Returns 0: on failure
548 int BDB::bdb_get_client_ids(JCR *jcr, int *num_ids, uint32_t *ids[])
557 Mmsg(cmd, "SELECT ClientId FROM Client ORDER BY Name ASC");
558 if (QueryDB(jcr, cmd)) {
559 *num_ids = sql_num_rows();
561 id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
562 while ((row = sql_fetch_row()) != NULL) {
563 id[i++] = str_to_uint64(row[0]);
570 Mmsg(errmsg, _("Client id select failed: ERR=%s\n"), sql_strerror());
571 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
579 * Get Pool Id, Scratch Pool Id, Recycle Pool Id
580 * Returns: false on failure
583 bool BDB::bdb_get_pool_record(JCR *jcr, POOL_DBR *pdbr)
588 char esc[MAX_ESCAPE_NAME_LENGTH];
591 if (pdbr->PoolId != 0) { /* find by id */
593 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
594 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
595 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId,"
596 "ActionOnPurge FROM Pool WHERE Pool.PoolId=%s",
597 edit_int64(pdbr->PoolId, ed1));
598 } else { /* find by name */
599 bdb_escape_string(jcr, esc, pdbr->Name, strlen(pdbr->Name));
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.Name='%s'", esc);
606 if (QueryDB(jcr, cmd)) {
607 if (sql_num_rows() > 1) {
609 Mmsg1(errmsg, _("More than one Pool! Num=%s\n"),
610 edit_uint64(sql_num_rows(), ed1));
611 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
612 } else if (sql_num_rows() == 1) {
613 if ((row = sql_fetch_row()) == NULL) {
614 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
615 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
617 pdbr->PoolId = str_to_int64(row[0]);
618 bstrncpy(pdbr->Name, row[1]!=NULL?row[1]:"", sizeof(pdbr->Name));
619 pdbr->NumVols = str_to_int64(row[2]);
620 pdbr->MaxVols = str_to_int64(row[3]);
621 pdbr->UseOnce = str_to_int64(row[4]);
622 pdbr->UseCatalog = str_to_int64(row[5]);
623 pdbr->AcceptAnyVolume = str_to_int64(row[6]);
624 pdbr->AutoPrune = str_to_int64(row[7]);
625 pdbr->Recycle = str_to_int64(row[8]);
626 pdbr->VolRetention = str_to_int64(row[9]);
627 pdbr->VolUseDuration = str_to_int64(row[10]);
628 pdbr->MaxVolJobs = str_to_int64(row[11]);
629 pdbr->MaxVolFiles = str_to_int64(row[12]);
630 pdbr->MaxVolBytes = str_to_uint64(row[13]);
631 bstrncpy(pdbr->PoolType, row[14]!=NULL?row[14]:"", sizeof(pdbr->PoolType));
632 pdbr->LabelType = str_to_int64(row[15]);
633 bstrncpy(pdbr->LabelFormat, row[16]!=NULL?row[16]:"", sizeof(pdbr->LabelFormat));
634 pdbr->RecyclePoolId = str_to_int64(row[17]);
635 pdbr->ScratchPoolId = str_to_int64(row[18]);
636 pdbr->ActionOnPurge = str_to_int32(row[19]);
647 * If the PoolId is non-zero, we get its record,
648 * otherwise, we search on the PoolName and we compute the number of volumes
650 * Returns: false on failure
653 bool BDB::bdb_get_pool_numvols(JCR *jcr, POOL_DBR *pdbr)
658 ok = db_get_pool_record(jcr, this, pdbr);
663 Mmsg(cmd, "SELECT count(*) from Media WHERE PoolId=%s",
664 edit_int64(pdbr->PoolId, ed1));
665 NumVols = get_sql_record_max(jcr, this);
666 Dmsg2(400, "Actual NumVols=%d Pool NumVols=%d\n", NumVols, pdbr->NumVols);
667 if (NumVols != pdbr->NumVols) {
668 pdbr->NumVols = NumVols;
669 db_update_pool_record(jcr, this, pdbr);
672 Mmsg(errmsg, _("Pool record not found in Catalog.\n"));
679 * Free restoreobject record (some fields are allocated through malloc)
681 void db_free_restoreobject_record(JCR *jcr, ROBJECT_DBR *rr)
686 if (rr->object_name) {
687 free(rr->object_name);
689 if (rr->plugin_name) {
690 free(rr->plugin_name);
692 rr->object = rr->plugin_name = rr->object_name = NULL;
696 * Get RestoreObject Record
697 * If the RestoreObjectId is non-zero, we get its record
699 * You must call db_free_restoreobject_record() after db_get_restoreobject_record()
701 * Returns: false on failure
704 bool BDB::bdb_get_restoreobject_record(JCR *jcr, ROBJECT_DBR *rr)
713 "SELECT ObjectName, PluginName, ObjectType, JobId, ObjectCompression, "
714 "RestoreObject, ObjectLength, ObjectFullLength, FileIndex "
715 "FROM RestoreObject "
716 "WHERE RestoreObjectId=%s",
717 edit_int64(rr->RestoreObjectId, ed1));
719 /* Using the JobId permits to check the Job name against ACLs and
720 * make sure that the current user is authorized to see the Restore object
723 pm_strcat(cmd, " AND JobId=");
724 pm_strcat(cmd, edit_int64(rr->JobId, ed1));
726 } else if (rr->JobIds && is_a_number_list(rr->JobIds)) {
727 pm_strcat(cmd, " AND JobId IN (");
728 pm_strcat(cmd, rr->JobIds);
732 if (QueryDB(jcr, cmd)) {
733 if (sql_num_rows() > 1) {
735 Mmsg1(errmsg, _("Error got %s RestoreObjects but expected only one!\n"),
736 edit_uint64(sql_num_rows(), ed1));
737 sql_data_seek(sql_num_rows()-1);
739 if ((row = sql_fetch_row()) == NULL) {
740 Mmsg1(errmsg, _("RestoreObject record \"%d\" not found.\n"), rr->RestoreObjectId);
742 db_free_restoreobject_record(jcr, rr);
743 rr->object_name = bstrdup(row[0]);
744 rr->plugin_name = bstrdup(row[1]);
745 rr->FileType = str_to_uint64(row[2]);
746 rr->JobId = str_to_uint64(row[3]);
747 rr->object_compression = str_to_int64(row[4]);
748 rr->object_len = str_to_uint64(row[6]);
749 rr->object_full_len = str_to_uint64(row[7]);
750 rr->object_index = str_to_uint64(row[8]);
752 bdb_unescape_object(jcr,
754 rr->object_len, /* Object length */
757 if (rr->object_compression > 0) {
758 int out_len = rr->object_full_len + 100; /* full length */
759 char *obj = (char *)malloc(out_len);
760 Zinflate(cmd, rr->object_len, obj, out_len); /* out_len is updated */
761 if (out_len != (int)rr->object_full_len) {
762 Dmsg3(10, "Decompression failed. Len wanted=%d got=%d. Object=%s\n",
763 rr->object_full_len, out_len, rr->plugin_name);
765 Mmsg(errmsg, _("Decompression failed. Len wanted=%d got=%d. Object=%s\n"),
766 rr->object_full_len, out_len, rr->plugin_name);
770 rr->object_len = out_len;
773 rr->object = (char *)malloc(sizeof(char)*(len+1));
774 memcpy(rr->object, cmd, len);
776 rr->object_len = len;
783 Mmsg(errmsg, _("RestoreObject record not found in Catalog.\n"));
791 * If the ClientId is non-zero, we get its record,
792 * otherwise, we search on the Client Name
794 * Returns: 0 on failure
797 int BDB::bdb_get_client_record(JCR *jcr, CLIENT_DBR *cdbr)
802 char esc[MAX_ESCAPE_NAME_LENGTH];
805 if (cdbr->ClientId != 0) { /* find by id */
807 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
808 "FROM Client WHERE Client.ClientId=%s",
809 edit_int64(cdbr->ClientId, ed1));
810 } else { /* find by name */
811 bdb_escape_string(jcr, esc, cdbr->Name, strlen(cdbr->Name));
813 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
814 "FROM Client WHERE Client.Name='%s'", esc);
817 if (QueryDB(jcr, cmd)) {
818 if (sql_num_rows() > 1) {
819 Mmsg1(errmsg, _("More than one Client!: %s\n"),
820 edit_uint64(sql_num_rows(), ed1));
821 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
822 } else if (sql_num_rows() == 1) {
823 if ((row = sql_fetch_row()) == NULL) {
824 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
825 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
827 cdbr->ClientId = str_to_int64(row[0]);
828 bstrncpy(cdbr->Name, row[1]!=NULL?row[1]:"", sizeof(cdbr->Name));
829 bstrncpy(cdbr->Uname, row[2]!=NULL?row[2]:"", sizeof(cdbr->Uname));
830 cdbr->AutoPrune = str_to_int64(row[3]);
831 cdbr->FileRetention = str_to_int64(row[4]);
832 cdbr->JobRetention = str_to_int64(row[5]);
836 Mmsg(errmsg, _("Client record not found in Catalog.\n"));
840 Mmsg(errmsg, _("Client record not found in Catalog.\n"));
849 * Returns: 0 on failure
852 bool BDB::bdb_get_counter_record(JCR *jcr, COUNTER_DBR *cr)
855 char esc[MAX_ESCAPE_NAME_LENGTH];
858 bdb_escape_string(jcr, esc, cr->Counter, strlen(cr->Counter));
860 Mmsg(cmd, select_counter_values[bdb_get_type_index()], esc);
861 if (QueryDB(jcr, cmd)) {
863 /* If more than one, report error, but return first row */
864 if (sql_num_rows() > 1) {
865 Mmsg1(errmsg, _("More than one Counter!: %d\n"), sql_num_rows());
866 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
868 if (sql_num_rows() >= 1) {
869 if ((row = sql_fetch_row()) == NULL) {
870 Mmsg1(errmsg, _("error fetching Counter row: %s\n"), sql_strerror());
871 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
876 cr->MinValue = str_to_int64(row[0]);
877 cr->MaxValue = str_to_int64(row[1]);
878 cr->CurrentValue = str_to_int64(row[2]);
880 bstrncpy(cr->WrapCounter, row[3], sizeof(cr->WrapCounter));
882 cr->WrapCounter[0] = 0;
890 Mmsg(errmsg, _("Counter record: %s not found in Catalog.\n"), cr->Counter);
899 * If the FileSetId is non-zero, we get its record,
900 * otherwise, we search on the name
902 * Returns: 0 on failure
905 int BDB::bdb_get_fileset_record(JCR *jcr, FILESET_DBR *fsr)
910 char esc[MAX_ESCAPE_NAME_LENGTH];
913 if (fsr->FileSetId != 0) { /* find by id */
915 "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
916 "WHERE FileSetId=%s",
917 edit_int64(fsr->FileSetId, ed1));
918 } else { /* find by name */
919 bdb_escape_string(jcr, esc, fsr->FileSet, strlen(fsr->FileSet));
921 "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
922 "WHERE FileSet='%s' ORDER BY CreateTime DESC LIMIT 1", esc);
925 if (QueryDB(jcr, cmd)) {
926 if (sql_num_rows() > 1) {
928 Mmsg1(errmsg, _("Error got %s FileSets but expected only one!\n"),
929 edit_uint64(sql_num_rows(), ed1));
930 sql_data_seek(sql_num_rows()-1);
932 if ((row = sql_fetch_row()) == NULL) {
933 Mmsg1(errmsg, _("FileSet record \"%s\" not found.\n"), fsr->FileSet);
935 fsr->FileSetId = str_to_int64(row[0]);
936 bstrncpy(fsr->FileSet, row[1]!=NULL?row[1]:"", sizeof(fsr->FileSet));
937 bstrncpy(fsr->MD5, row[2]!=NULL?row[2]:"", sizeof(fsr->MD5));
938 bstrncpy(fsr->cCreateTime, row[3]!=NULL?row[3]:"", sizeof(fsr->cCreateTime));
939 stat = fsr->FileSetId;
943 Mmsg(errmsg, _("FileSet record not found in Catalog.\n"));
951 * Get the number of Media records
953 * Returns: -1 on failure
956 int BDB::bdb_get_num_media_records(JCR *jcr)
961 Mmsg(cmd, "SELECT count(*) from Media");
962 stat = get_sql_record_max(jcr, this);
968 * This function returns a list of all the Media record ids for
969 * the current Pool, the correct Media Type, Recyle, Enabled, StorageId, VolBytes
970 * VolumeName if specified
971 * The caller must free ids if non-NULL.
973 * Returns false: on failure
976 bool BDB::bdb_get_media_ids(JCR *jcr, MEDIA_DBR *mr, int *num_ids, uint32_t *ids[])
983 char buf[MAX_NAME_LENGTH*3]; /* Can contain MAX_NAME_LENGTH*2+1 + AND ....='' */
984 char esc[MAX_NAME_LENGTH*2+1];
989 Mmsg(cmd, "SELECT DISTINCT MediaId FROM Media WHERE Recycle=%d AND Enabled=%d ",
990 mr->Recycle, mr->Enabled);
992 if (*mr->MediaType) {
993 bdb_escape_string(jcr, esc, mr->MediaType, strlen(mr->MediaType));
994 bsnprintf(buf, sizeof(buf), "AND MediaType='%s' ", esc);
999 bsnprintf(buf, sizeof(buf), "AND StorageId=%s ", edit_uint64(mr->StorageId, ed1));
1000 pm_strcat(cmd, buf);
1004 bsnprintf(buf, sizeof(buf), "AND PoolId=%s ", edit_uint64(mr->PoolId, ed1));
1005 pm_strcat(cmd, buf);
1009 bsnprintf(buf, sizeof(buf), "AND VolBytes > %s ", edit_uint64(mr->VolBytes, ed1));
1010 pm_strcat(cmd, buf);
1013 if (*mr->VolumeName) {
1014 bdb_escape_string(jcr, esc, mr->VolumeName, strlen(mr->VolumeName));
1015 bsnprintf(buf, sizeof(buf), "AND VolumeName = '%s' ", esc);
1016 pm_strcat(cmd, buf);
1019 if (*mr->VolStatus) {
1020 bdb_escape_string(jcr, esc, mr->VolStatus, strlen(mr->VolStatus));
1021 bsnprintf(buf, sizeof(buf), "AND VolStatus = '%s' ", esc);
1022 pm_strcat(cmd, buf);
1025 Dmsg1(100, "q=%s\n", cmd);
1027 if (QueryDB(jcr, cmd)) {
1028 *num_ids = sql_num_rows();
1030 id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
1031 while ((row = sql_fetch_row()) != NULL) {
1032 id[i++] = str_to_uint64(row[0]);
1039 Mmsg(errmsg, _("Media id select failed: ERR=%s\n"), sql_strerror());
1040 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1049 * This function returns a list of all the DBIds that are returned
1052 * Returns false: on failure
1055 bool BDB::bdb_get_query_dbids(JCR *jcr, POOL_MEM &query, dbid_list &ids)
1063 if (QueryDB(jcr, query.c_str())) {
1064 ids.num_ids = sql_num_rows();
1065 if (ids.num_ids > 0) {
1066 if (ids.max_ids < ids.num_ids) {
1068 ids.DBId = (DBId_t *)malloc(ids.num_ids * sizeof(DBId_t));
1070 while ((row = sql_fetch_row()) != NULL) {
1071 ids.DBId[i++] = str_to_uint64(row[0]);
1077 Mmsg(errmsg, _("query dbids failed: ERR=%s\n"), sql_strerror());
1078 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1088 * Returns: false: on failure
1091 bool BDB::bdb_get_media_record(JCR *jcr, MEDIA_DBR *mr)
1096 char esc[MAX_ESCAPE_NAME_LENGTH];
1099 if (mr->MediaId == 0 && mr->VolumeName[0] == 0) {
1100 Mmsg(cmd, "SELECT count(*) from Media");
1101 mr->MediaId = get_sql_record_max(jcr, this);
1105 if (mr->MediaId != 0) { /* find by id */
1106 Mmsg(cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,"
1107 "VolBlocks,VolBytes,VolABytes,VolHoleBytes,VolHoles,VolMounts,"
1108 "VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
1109 "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
1110 "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
1111 "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
1112 "Enabled,LocationId,RecycleCount,InitialWrite,"
1113 "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge "
1114 "FROM Media WHERE MediaId=%s",
1115 edit_int64(mr->MediaId, ed1));
1116 } else { /* find by name */
1117 bdb_escape_string(jcr, esc, mr->VolumeName, strlen(mr->VolumeName));
1118 Mmsg(cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,"
1119 "VolBlocks,VolBytes,VolABytes,VolHoleBytes,VolHoles,VolMounts,"
1120 "VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
1121 "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
1122 "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
1123 "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
1124 "Enabled,LocationId,RecycleCount,InitialWrite,"
1125 "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge "
1126 "FROM Media WHERE VolumeName='%s'", esc);
1129 if (QueryDB(jcr, cmd)) {
1131 if (sql_num_rows() > 1) {
1132 Mmsg1(errmsg, _("More than one Volume!: %s\n"),
1133 edit_uint64(sql_num_rows(), ed1));
1134 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1135 } else if (sql_num_rows() == 1) {
1136 if ((row = sql_fetch_row()) == NULL) {
1137 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
1138 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1141 mr->MediaId = str_to_int64(row[0]);
1142 bstrncpy(mr->VolumeName, row[1]!=NULL?row[1]:"", sizeof(mr->VolumeName));
1143 mr->VolJobs = str_to_int64(row[2]);
1144 mr->VolFiles = str_to_int64(row[3]);
1145 mr->VolBlocks = str_to_int64(row[4]);
1146 mr->VolBytes = str_to_uint64(row[5]);
1147 mr->VolABytes = str_to_uint64(row[6]);
1148 mr->VolHoleBytes = str_to_uint64(row[7]);
1149 mr->VolHoles = str_to_int64(row[8]);
1150 mr->VolMounts = str_to_int64(row[9]);
1151 mr->VolErrors = str_to_int64(row[10]);
1152 mr->VolWrites = str_to_int64(row[11]);
1153 mr->MaxVolBytes = str_to_uint64(row[12]);
1154 mr->VolCapacityBytes = str_to_uint64(row[13]);
1155 bstrncpy(mr->MediaType, row[14]!=NULL?row[14]:"", sizeof(mr->MediaType));
1156 bstrncpy(mr->VolStatus, row[15]!=NULL?row[15]:"", sizeof(mr->VolStatus));
1157 mr->PoolId = str_to_int64(row[16]);
1158 mr->VolRetention = str_to_uint64(row[17]);
1159 mr->VolUseDuration = str_to_uint64(row[18]);
1160 mr->MaxVolJobs = str_to_int64(row[19]);
1161 mr->MaxVolFiles = str_to_int64(row[20]);
1162 mr->Recycle = str_to_int64(row[21]);
1163 mr->Slot = str_to_int64(row[22]);
1164 bstrncpy(mr->cFirstWritten, row[23]!=NULL?row[23]:"", sizeof(mr->cFirstWritten));
1165 mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten);
1166 bstrncpy(mr->cLastWritten, row[24]!=NULL?row[24]:"", sizeof(mr->cLastWritten));
1167 mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
1168 mr->InChanger = str_to_uint64(row[25]);
1169 mr->EndFile = str_to_uint64(row[26]);
1170 mr->EndBlock = str_to_uint64(row[27]);
1171 mr->VolType = str_to_int64(row[28]); /* formerly VolParts */
1172 mr->LabelType = str_to_int64(row[29]);
1173 bstrncpy(mr->cLabelDate, row[30]!=NULL?row[30]:"", sizeof(mr->cLabelDate));
1174 mr->LabelDate = (time_t)str_to_utime(mr->cLabelDate);
1175 mr->StorageId = str_to_int64(row[31]);
1176 mr->Enabled = str_to_int64(row[32]);
1177 mr->LocationId = str_to_int64(row[33]);
1178 mr->RecycleCount = str_to_int64(row[34]);
1179 bstrncpy(mr->cInitialWrite, row[35]!=NULL?row[35]:"", sizeof(mr->cInitialWrite));
1180 mr->InitialWrite = (time_t)str_to_utime(mr->cInitialWrite);
1181 mr->ScratchPoolId = str_to_int64(row[36]);
1182 mr->RecyclePoolId = str_to_int64(row[37]);
1183 mr->VolReadTime = str_to_int64(row[38]);
1184 mr->VolWriteTime = str_to_int64(row[39]);
1185 mr->ActionOnPurge = str_to_int32(row[40]);
1190 if (mr->MediaId != 0) {
1191 Mmsg1(errmsg, _("Media record with MediaId=%s not found.\n"),
1192 edit_int64(mr->MediaId, ed1));
1194 Mmsg1(errmsg, _("Media record for Volume name \"%s\" not found.\n"),
1200 if (mr->MediaId != 0) {
1201 Mmsg(errmsg, _("Media record for MediaId=%u not found in Catalog.\n"),
1204 Mmsg(errmsg, _("Media record for Volume Name \"%s\" not found in Catalog.\n"),
1211 /* Remove all MD5 from a query (can save lot of memory with many files) */
1212 static void strip_md5(char *q)
1215 while ((p = strstr(p, ", MD5"))) {
1216 memset(p, ' ', 5 * sizeof(char));
1221 * Find the last "accurate" backup state (that can take deleted files in
1223 * 1) Get all files with jobid in list (F subquery)
1224 * Get all files in BaseFiles with jobid in list
1225 * 2) Take only the last version of each file (Temp subquery) => accurate list
1227 * 3) Join the result to file table to get fileindex, jobid and lstat information
1229 * TODO: See if we can do the SORT only if needed (as an argument)
1231 bool BDB::bdb_get_file_list(JCR *jcr, char *jobids,
1232 bool use_md5, bool use_delta,
1233 DB_RESULT_HANDLER *result_handler, void *ctx)
1237 Mmsg(errmsg, _("ERR=JobIds are empty\n"));
1241 POOL_MEM buf(PM_MESSAGE);
1242 POOL_MEM buf2(PM_MESSAGE);
1244 Mmsg(buf2, select_recent_version_with_basejob_and_delta[bdb_get_type_index()],
1245 jobids, jobids, jobids, jobids);
1248 Mmsg(buf2, select_recent_version_with_basejob[bdb_get_type_index()],
1249 jobids, jobids, jobids, jobids);
1252 /* bsr code is optimized for JobId sorted, with Delta, we need to get
1253 * them ordered by date. JobTDate and JobId can be mixed if using Copy
1257 "SELECT Path.Path, Filename.Name, T1.FileIndex, T1.JobId, LStat, DeltaSeq, MD5 "
1258 "FROM ( %s ) AS T1 "
1259 "JOIN Filename ON (Filename.FilenameId = T1.FilenameId) "
1260 "JOIN Path ON (Path.PathId = T1.PathId) "
1261 "WHERE FileIndex > 0 "
1262 "ORDER BY T1.JobTDate, FileIndex ASC",/* Return sorted by JobTDate */
1263 /* FileIndex for restore code */
1267 strip_md5(buf.c_str());
1270 Dmsg1(100, "q=%s\n", buf.c_str());
1272 return bdb_big_sql_query(buf.c_str(), result_handler, ctx);
1276 * This procedure gets the base jobid list used by jobids,
1278 bool BDB::bdb_get_used_base_jobids(JCR *jcr,
1279 POOLMEM *jobids, db_list_ctx *result)
1284 "SELECT DISTINCT BaseJobId "
1285 " FROM Job JOIN BaseFiles USING (JobId) "
1286 " WHERE Job.HasBase = 1 "
1287 " AND Job.JobId IN (%s) ", jobids);
1288 return bdb_sql_query(buf.c_str(), db_list_handler, result);
1292 * The decision do change an incr/diff was done before
1294 * Differential : get the last full id
1295 * Incremental : get the last full + last diff + last incr(s) ids
1297 * If you specify jr->StartTime, it will be used to limit the search
1298 * in the time. (usually now)
1300 * TODO: look and merge from ua_restore.c
1302 bool BDB::bdb_get_accurate_jobids(JCR *jcr,
1303 JOB_DBR *jr, db_list_ctx *jobids)
1306 char clientid[50], jobid[50], filesetid[50];
1307 char date[MAX_TIME_LENGTH];
1308 POOL_MEM query(PM_FNAME);
1310 /* Take the current time as upper limit if nothing else specified */
1311 utime_t StartTime = (jr->StartTime)?jr->StartTime:time(NULL);
1313 bstrutime(date, sizeof(date), StartTime + 1);
1316 /* First, find the last good Full backup for this job/client/fileset */
1317 Mmsg(query, create_temp_accurate_jobids[bdb_get_type_index()],
1318 edit_uint64(jcr->JobId, jobid),
1319 edit_uint64(jr->ClientId, clientid),
1321 edit_uint64(jr->FileSetId, filesetid));
1323 if (!bdb_sql_query(query.c_str(), NULL, NULL)) {
1327 if (jr->JobLevel == L_INCREMENTAL || jr->JobLevel == L_VIRTUAL_FULL) {
1328 /* Now, find the last differential backup after the last full */
1330 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
1331 "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1332 "FROM Job JOIN FileSet USING (FileSetId) "
1333 "WHERE ClientId = %s "
1334 "AND Level='D' AND JobStatus IN ('T','W') AND Type='B' "
1335 "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
1336 "AND StartTime < '%s' "
1337 "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1338 "ORDER BY Job.JobTDate DESC LIMIT 1 ",
1345 if (!bdb_sql_query(query.c_str(), NULL, NULL)) {
1349 /* We just have to take all incremental after the last Full/Diff */
1351 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
1352 "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1353 "FROM Job JOIN FileSet USING (FileSetId) "
1354 "WHERE ClientId = %s "
1355 "AND Level='I' AND JobStatus IN ('T','W') AND Type='B' "
1356 "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
1357 "AND StartTime < '%s' "
1358 "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1359 "ORDER BY Job.JobTDate DESC ",
1365 if (!bdb_sql_query(query.c_str(), NULL, NULL)) {
1370 /* build a jobid list ie: 1,2,3,4 */
1371 Mmsg(query, "SELECT JobId FROM btemp3%s ORDER by JobTDate", jobid);
1372 bdb_sql_query(query.c_str(), db_list_handler, jobids);
1373 Dmsg1(1, "db_get_accurate_jobids=%s\n", jobids->list);
1377 Mmsg(query, "DROP TABLE btemp3%s", jobid);
1378 bdb_sql_query(query.c_str(), NULL, NULL);
1383 bool BDB::bdb_get_base_file_list(JCR *jcr, bool use_md5,
1384 DB_RESULT_HANDLER *result_handler, void *ctx)
1386 POOL_MEM buf(PM_MESSAGE);
1389 "SELECT Path, Name, FileIndex, JobId, LStat, 0 As DeltaSeq, MD5 "
1390 "FROM new_basefile%lld ORDER BY JobId, FileIndex ASC",
1391 (uint64_t) jcr->JobId);
1394 strip_md5(buf.c_str());
1396 return bdb_sql_query(buf.c_str(), result_handler, ctx);
1399 bool BDB::bdb_get_base_jobid(JCR *jcr, JOB_DBR *jr, JobId_t *jobid)
1401 POOL_MEM query(PM_FNAME);
1404 char date[MAX_TIME_LENGTH];
1405 char esc[MAX_ESCAPE_NAME_LENGTH];
1412 StartTime = (jr->StartTime)?jr->StartTime:time(NULL);
1413 bstrutime(date, sizeof(date), StartTime + 1);
1414 bdb_escape_string(jcr, esc, jr->Name, strlen(jr->Name));
1416 /* we can take also client name, fileset, etc... */
1419 "SELECT JobId, Job, StartTime, EndTime, JobTDate, PurgedFiles "
1421 // "JOIN FileSet USING (FileSetId) JOIN Client USING (ClientId) "
1422 "WHERE Job.Name = '%s' "
1423 "AND Level='B' AND JobStatus IN ('T','W') AND Type='B' "
1424 // "AND FileSet.FileSet= '%s' "
1425 // "AND Client.Name = '%s' "
1426 "AND StartTime<'%s' "
1427 "ORDER BY Job.JobTDate DESC LIMIT 1",
1429 // edit_uint64(jr->ClientId, clientid),
1430 // edit_uint64(jr->FileSetId, filesetid));
1433 Dmsg1(10, "db_get_base_jobid q=%s\n", query.c_str());
1434 if (!bdb_sql_query(query.c_str(), db_int64_handler, &lctx)) {
1437 *jobid = (JobId_t) lctx.value;
1439 Dmsg1(10, "db_get_base_jobid=%lld\n", *jobid);
1446 /* Get JobIds associated with a volume */
1447 bool BDB::bdb_get_volume_jobids(JCR *jcr,
1448 MEDIA_DBR *mr, db_list_ctx *lst)
1454 Mmsg(cmd, "SELECT DISTINCT JobId FROM JobMedia WHERE MediaId=%s",
1455 edit_int64(mr->MediaId, ed1));
1456 ret = bdb_sql_query(cmd, db_list_handler, lst);
1462 * Get Snapshot Record
1464 * Returns: false: on failure
1467 bool BDB::bdb_get_snapshot_record(JCR *jcr, SNAPSHOT_DBR *sr)
1472 char esc[MAX_ESCAPE_NAME_LENGTH];
1473 POOL_MEM filter1, filter2;
1475 if (sr->SnapshotId == 0 && (sr->Name[0] == 0 || sr->Device[0] == 0)) {
1476 Dmsg0(10, "No SnapshotId or Name/Device provided\n");
1482 if (sr->SnapshotId != 0) { /* find by id */
1483 Mmsg(filter1, "Snapshot.SnapshotId=%d", sr->SnapshotId);
1485 } else if (*sr->Name && *sr->Device) { /* find by name */
1486 bdb_escape_string(jcr, esc, sr->Name, strlen(sr->Name));
1487 Mmsg(filter1, "Snapshot.Name='%s'", esc);
1488 bdb_escape_string(jcr, esc, sr->Device, strlen(sr->Device));
1489 Mmsg(filter2, "AND Snapshot.Device='%s'", esc);
1492 Dmsg0(10, "No SnapshotId or Name and Device\n");
1496 Mmsg(cmd, "SELECT SnapshotId, Snapshot.Name, JobId, Snapshot.FileSetId, "
1497 "FileSet.FileSet, CreateTDate, CreateDate, "
1498 "Client.Name AS Client, Snapshot.ClientId, Volume, Device, Type, Retention, "
1499 "Comment FROM Snapshot JOIN Client USING (ClientId) LEFT JOIN FileSet USING (FileSetId) WHERE %s %s",
1500 filter1.c_str(), filter2.c_str());
1502 if (QueryDB(jcr, cmd)) {
1504 if (sql_num_rows() > 1) {
1505 Mmsg1(errmsg, _("More than one Snapshot!: %s\n"),
1506 edit_uint64(sql_num_rows(), ed1));
1507 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1508 } else if (sql_num_rows() == 1) {
1509 if ((row = sql_fetch_row()) == NULL) {
1510 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
1511 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
1515 sr->need_to_free = true;
1516 sr->SnapshotId = str_to_int64(row[0]);
1517 bstrncpy(sr->Name, row[1], sizeof(sr->Name));
1518 sr->JobId = str_to_int64(row[2]);
1519 sr->FileSetId = str_to_int64(row[3]);
1520 bstrncpy(sr->FileSet, row[4], sizeof(sr->FileSet));
1521 sr->CreateTDate = str_to_uint64(row[5]);
1522 bstrncpy(sr->CreateDate, row[6], sizeof(sr->CreateDate));
1523 bstrncpy(sr->Client, row[7], sizeof(sr->Client));
1524 sr->ClientId = str_to_int64(row[8]);
1525 sr->Volume = bstrdup(row[9]);
1526 sr->Device = bstrdup(row[10]);
1527 bstrncpy(sr->Type, row[11], sizeof(sr->Type));
1528 sr->Retention = str_to_int64(row[12]);
1529 bstrncpy(sr->Comment, NPRTB(row[13]), sizeof(sr->Comment));
1533 if (sr->SnapshotId != 0) {
1534 Mmsg1(errmsg, _("Snapshot record with SnapshotId=%s not found.\n"),
1535 edit_int64(sr->SnapshotId, ed1));
1537 Mmsg1(errmsg, _("Snapshot record for Snapshot name \"%s\" not found.\n"),
1543 if (sr->SnapshotId != 0) {
1544 Mmsg1(errmsg, _("Snapshot record with SnapshotId=%s not found.\n"),
1545 edit_int64(sr->SnapshotId, ed1));
1547 Mmsg1(errmsg, _("Snapshot record for Snapshot name \"%s\" not found.\n"),
1555 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */