2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2009 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 two of the GNU 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 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
36 * Version $Id: sql_get.c 8918 2009-06-23 11:56:35Z ricozz $
40 /* The following is necessary so that we do not include
41 * the dummy external definition of DB.
43 #define __SQL_C /* indicate that this is sql.c */
48 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI
50 /* -----------------------------------------------------------------------
52 * Generic Routines (or almost generic)
54 * -----------------------------------------------------------------------
57 /* Forward referenced functions */
58 static int db_get_file_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, FILE_DBR *fdbr);
59 static int db_get_filename_record(JCR *jcr, B_DB *mdb);
63 * Given a full filename (with path), look up the File record
64 * (with attributes) in the database.
66 * Returns: 0 on failure
67 * 1 on success with the File record in FILE_DBR
69 int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JOB_DBR *jr, FILE_DBR *fdbr)
72 Dmsg1(100, "db_get_file_att_record fname=%s \n", fname);
75 split_path_and_file(jcr, mdb, fname);
77 fdbr->FilenameId = db_get_filename_record(jcr, mdb);
79 fdbr->PathId = db_get_path_record(jcr, mdb);
81 stat = db_get_file_record(jcr, mdb, jr, fdbr);
91 * Returns: 0 on failure
94 * DO NOT use Jmsg in this routine.
96 * Note in this routine, we do not use Jmsg because it may be
97 * called to get attributes of a non-existent file, which is
98 * "normal" if a new file is found during Verify.
100 * The following is a bit of a kludge: because we always backup a
101 * directory entry, we can end up with two copies of the directory
102 * in the backup. One is when we encounter the directory and find
103 * we cannot recurse into it, and the other is when we find an
104 * explicit mention of the directory. This can also happen if the
105 * use includes the directory twice. In this case, Verify
106 * VolumeToCatalog fails because we have two copies in the catalog,
107 * and only the first one is marked (twice). So, when calling from Verify,
108 * jr is not NULL and we know jr->FileIndex is the fileindex
109 * of the version of the directory/file we actually want and do
110 * a more explicit SQL search.
113 int db_get_file_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, FILE_DBR *fdbr)
117 char ed1[50], ed2[50], ed3[50];
119 if (jcr->get_JobLevel() == L_VERIFY_DISK_TO_CATALOG) {
121 "SELECT FileId, LStat, MD5 FROM File,Job WHERE "
122 "File.JobId=Job.JobId AND File.PathId=%s AND "
123 "File.FilenameId=%s AND Job.Type='B' AND Job.JobStatus IN ('T','W') AND "
124 "ClientId=%s ORDER BY StartTime DESC LIMIT 1",
125 edit_int64(fdbr->PathId, ed1),
126 edit_int64(fdbr->FilenameId, ed2),
127 edit_int64(jr->ClientId,ed3));
129 } else if (jr != NULL) {
130 /* Called from Verify so jr->FileIndex is valid */
132 "SELECT FileId, LStat, MD5 FROM File WHERE File.JobId=%s AND File.PathId=%s AND "
133 "File.FilenameId=%s AND FileIndex=%u",
134 edit_int64(fdbr->JobId, ed1),
135 edit_int64(fdbr->PathId, ed2),
136 edit_int64(fdbr->FilenameId,ed3),
140 "SELECT FileId, LStat, MD5 FROM File WHERE File.JobId=%s AND File.PathId=%s AND "
141 "File.FilenameId=%s",
142 edit_int64(fdbr->JobId, ed1),
143 edit_int64(fdbr->PathId, ed2),
144 edit_int64(fdbr->FilenameId,ed3));
146 Dmsg3(450, "Get_file_record JobId=%u FilenameId=%u PathId=%u\n",
147 fdbr->JobId, fdbr->FilenameId, fdbr->PathId);
149 Dmsg1(100, "Query=%s\n", mdb->cmd);
151 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
152 mdb->num_rows = sql_num_rows(mdb);
153 Dmsg1(050, "get_file_record num_rows=%d\n", (int)mdb->num_rows);
154 if (mdb->num_rows > 1) {
155 Mmsg1(mdb->errmsg, _("get_file_record want 1 got rows=%d\n"),
157 Dmsg1(000, "=== Problem! %s", mdb->errmsg);
159 if (mdb->num_rows >= 1) {
160 if ((row = sql_fetch_row(mdb)) == NULL) {
161 Mmsg1(mdb->errmsg, _("Error fetching row: %s\n"), sql_strerror(mdb));
163 fdbr->FileId = (FileId_t)str_to_int64(row[0]);
164 bstrncpy(fdbr->LStat, row[1], sizeof(fdbr->LStat));
165 bstrncpy(fdbr->Digest, row[2], sizeof(fdbr->Digest));
169 Mmsg2(mdb->errmsg, _("File record for PathId=%s FilenameId=%s not found.\n"),
170 edit_int64(fdbr->PathId, ed1),
171 edit_int64(fdbr->FilenameId, ed2));
173 sql_free_result(mdb);
175 Mmsg(mdb->errmsg, _("File record not found in Catalog.\n"));
181 /* Get Filename record
182 * Returns: 0 on failure
183 * FilenameId on success
185 * DO NOT use Jmsg in this routine (see notes for get_file_record)
187 static int db_get_filename_record(JCR *jcr, B_DB *mdb)
192 mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->fnl+2);
193 db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
195 Mmsg(mdb->cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", mdb->esc_name);
196 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
198 mdb->num_rows = sql_num_rows(mdb);
199 if (mdb->num_rows > 1) {
200 Mmsg2(mdb->errmsg, _("More than one Filename!: %s for file: %s\n"),
201 edit_uint64(mdb->num_rows, ed1), mdb->fname);
202 Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
204 if (mdb->num_rows >= 1) {
205 if ((row = sql_fetch_row(mdb)) == NULL) {
206 Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
208 FilenameId = str_to_int64(row[0]);
209 if (FilenameId <= 0) {
210 Mmsg2(mdb->errmsg, _("Get DB Filename record %s found bad record: %d\n"),
211 mdb->cmd, FilenameId);
216 Mmsg1(mdb->errmsg, _("Filename record: %s not found.\n"), mdb->fname);
218 sql_free_result(mdb);
220 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)
236 mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->pnl+2);
237 db_escape_string(jcr, mdb, mdb->esc_name, mdb->path, mdb->pnl);
239 if (mdb->cached_path_id != 0 && mdb->cached_path_len == mdb->pnl &&
240 strcmp(mdb->cached_path, mdb->path) == 0) {
241 return mdb->cached_path_id;
244 Mmsg(mdb->cmd, "SELECT PathId FROM Path WHERE Path='%s'", mdb->esc_name);
246 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
248 mdb->num_rows = sql_num_rows(mdb);
249 if (mdb->num_rows > 1) {
250 Mmsg2(mdb->errmsg, _("More than one Path!: %s for path: %s\n"),
251 edit_uint64(mdb->num_rows, ed1), mdb->path);
252 Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
254 /* Even if there are multiple paths, take the first one */
255 if (mdb->num_rows >= 1) {
256 if ((row = sql_fetch_row(mdb)) == NULL) {
257 Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
259 PathId = str_to_int64(row[0]);
261 Mmsg2(mdb->errmsg, _("Get DB path record %s found bad record: %s\n"),
262 mdb->cmd, edit_int64(PathId, ed1));
266 if (PathId != mdb->cached_path_id) {
267 mdb->cached_path_id = PathId;
268 mdb->cached_path_len = mdb->pnl;
269 pm_strcpy(mdb->cached_path, mdb->path);
274 Mmsg1(mdb->errmsg, _("Path record: %s not found.\n"), mdb->path);
276 sql_free_result(mdb);
278 Mmsg(mdb->errmsg, _("Path record: %s not found in Catalog.\n"), mdb->path);
285 * Get Job record for given JobId or Job name
286 * Returns: false on failure
289 bool db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr)
295 if (jr->JobId == 0) {
296 Mmsg(mdb->cmd, "SELECT VolSessionId,VolSessionTime,"
297 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
298 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
299 "SchedTime,RealEndTime,ReadBytes "
300 "FROM Job WHERE Job='%s'", jr->Job);
302 Mmsg(mdb->cmd, "SELECT VolSessionId,VolSessionTime,"
303 "PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,"
304 "Type,Level,ClientId,Name,PriorJobId,RealEndTime,JobId,FileSetId,"
305 "SchedTime,RealEndTime,ReadBytes "
306 "FROM Job WHERE JobId=%s",
307 edit_int64(jr->JobId, ed1));
310 if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
312 return false; /* failed */
314 if ((row = sql_fetch_row(mdb)) == NULL) {
315 Mmsg1(mdb->errmsg, _("No Job found for JobId %s\n"), edit_int64(jr->JobId, ed1));
316 sql_free_result(mdb);
318 return false; /* failed */
321 jr->VolSessionId = str_to_uint64(row[0]);
322 jr->VolSessionTime = str_to_uint64(row[1]);
323 jr->PoolId = str_to_int64(row[2]);
324 bstrncpy(jr->cStartTime, row[3]!=NULL?row[3]:"", sizeof(jr->cStartTime));
325 bstrncpy(jr->cEndTime, row[4]!=NULL?row[4]:"", sizeof(jr->cEndTime));
326 jr->JobFiles = str_to_int64(row[5]);
327 jr->JobBytes = str_to_int64(row[6]);
328 jr->JobTDate = str_to_int64(row[7]);
329 bstrncpy(jr->Job, row[8]!=NULL?row[8]:"", sizeof(jr->Job));
330 jr->JobStatus = row[9]!=NULL?(int)*row[9]:JS_FatalError;
331 jr->JobType = row[10]!=NULL?(int)*row[10]:JT_BACKUP;
332 jr->JobLevel = row[11]!=NULL?(int)*row[11]:L_NONE;
333 jr->ClientId = str_to_uint64(row[12]!=NULL?row[12]:(char *)"");
334 bstrncpy(jr->Name, row[13]!=NULL?row[13]:"", sizeof(jr->Name));
335 jr->PriorJobId = str_to_uint64(row[14]!=NULL?row[14]:(char *)"");
336 bstrncpy(jr->cRealEndTime, row[15]!=NULL?row[15]:"", sizeof(jr->cRealEndTime));
337 if (jr->JobId == 0) {
338 jr->JobId = str_to_int64(row[16]);
340 jr->FileSetId = str_to_int64(row[17]);
341 bstrncpy(jr->cSchedTime, row[3]!=NULL?row[18]:"", sizeof(jr->cSchedTime));
342 bstrncpy(jr->cRealEndTime, row[3]!=NULL?row[19]:"", sizeof(jr->cRealEndTime));
343 jr->ReadBytes = str_to_int64(row[20]);
344 jr->StartTime = str_to_utime(jr->cStartTime);
345 jr->SchedTime = str_to_utime(jr->cSchedTime);
346 jr->EndTime = str_to_utime(jr->cEndTime);
347 jr->RealEndTime = str_to_utime(jr->cRealEndTime);
348 sql_free_result(mdb);
355 * Find VolumeNames for a given JobId
356 * Returns: 0 on error or no Volumes found
357 * number of volumes on success
358 * Volumes are concatenated in VolumeNames
359 * separated by a vertical bar (|) in the order
360 * that they were written.
362 * Returns: number of volumes on success
364 int db_get_job_volume_names(JCR *jcr, B_DB *mdb, JobId_t JobId, POOLMEM **VolumeNames)
372 /* Get one entry per VolumeName, but "sort" by VolIndex */
374 "SELECT VolumeName,MAX(VolIndex) FROM JobMedia,Media WHERE "
375 "JobMedia.JobId=%s AND JobMedia.MediaId=Media.MediaId "
376 "GROUP BY VolumeName "
377 "ORDER BY 2 ASC", edit_int64(JobId,ed1));
379 Dmsg1(130, "VolNam=%s\n", mdb->cmd);
381 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
382 mdb->num_rows = sql_num_rows(mdb);
383 Dmsg1(130, "Num rows=%d\n", mdb->num_rows);
384 if (mdb->num_rows <= 0) {
385 Mmsg1(mdb->errmsg, _("No volumes found for JobId=%d\n"), JobId);
388 stat = mdb->num_rows;
389 for (i=0; i < stat; i++) {
390 if ((row = sql_fetch_row(mdb)) == NULL) {
391 Mmsg2(mdb->errmsg, _("Error fetching row %d: ERR=%s\n"), i, sql_strerror(mdb));
392 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
396 if (*VolumeNames[0] != 0) {
397 pm_strcat(VolumeNames, "|");
399 pm_strcat(VolumeNames, row[0]);
403 sql_free_result(mdb);
405 Mmsg(mdb->errmsg, _("No Volume for JobId %d found in Catalog.\n"), JobId);
412 * Find Volume parameters for a give JobId
413 * Returns: 0 on error or no Volumes found
414 * number of volumes on success
415 * List of Volumes and start/end file/blocks (malloced structure!)
417 * Returns: number of volumes on success
419 int db_get_job_volume_parameters(JCR *jcr, B_DB *mdb, JobId_t JobId, VOL_PARAMS **VolParams)
425 VOL_PARAMS *Vols = NULL;
429 "SELECT VolumeName,MediaType,FirstIndex,LastIndex,StartFile,"
430 "JobMedia.EndFile,StartBlock,JobMedia.EndBlock,Copy,"
431 "Slot,StorageId,InChanger"
432 " FROM JobMedia,Media WHERE JobMedia.JobId=%s"
433 " AND JobMedia.MediaId=Media.MediaId ORDER BY VolIndex,JobMediaId",
434 edit_int64(JobId, ed1));
436 Dmsg1(130, "VolNam=%s\n", mdb->cmd);
437 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
438 mdb->num_rows = sql_num_rows(mdb);
439 Dmsg1(200, "Num rows=%d\n", mdb->num_rows);
440 if (mdb->num_rows <= 0) {
441 Mmsg1(mdb->errmsg, _("No volumes found for JobId=%d\n"), JobId);
444 stat = mdb->num_rows;
447 *VolParams = Vols = (VOL_PARAMS *)malloc(stat * sizeof(VOL_PARAMS));
448 SId = (DBId_t *)malloc(stat * sizeof(DBId_t));
450 for (i=0; i < stat; i++) {
451 if ((row = sql_fetch_row(mdb)) == NULL) {
452 Mmsg2(mdb->errmsg, _("Error fetching row %d: ERR=%s\n"), i, sql_strerror(mdb));
453 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
458 uint32_t StartBlock, EndBlock, StartFile, EndFile;
459 bstrncpy(Vols[i].VolumeName, row[0], MAX_NAME_LENGTH);
460 bstrncpy(Vols[i].MediaType, row[1], MAX_NAME_LENGTH);
461 Vols[i].FirstIndex = str_to_uint64(row[2]);
462 Vols[i].LastIndex = str_to_uint64(row[3]);
463 StartFile = str_to_uint64(row[4]);
464 EndFile = str_to_uint64(row[5]);
465 StartBlock = str_to_uint64(row[6]);
466 EndBlock = str_to_uint64(row[7]);
467 Vols[i].StartAddr = (((uint64_t)StartFile)<<32) | StartBlock;
468 Vols[i].EndAddr = (((uint64_t)EndFile)<<32) | EndBlock;
469 // Vols[i].Copy = str_to_uint64(row[8]);
470 Vols[i].Slot = str_to_uint64(row[9]);
471 StorageId = str_to_uint64(row[10]);
472 Vols[i].InChanger = str_to_uint64(row[11]);
473 Vols[i].Storage[0] = 0;
477 for (i=0; i < stat; i++) {
479 Mmsg(mdb->cmd, "SELECT Name from Storage WHERE StorageId=%s",
480 edit_int64(SId[i], ed1));
481 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
482 if ((row = sql_fetch_row(mdb)) && row[0]) {
483 bstrncpy(Vols[i].Storage, row[0], MAX_NAME_LENGTH);
492 sql_free_result(mdb);
501 * Get the number of pool records
503 * Returns: -1 on failure
506 int db_get_num_pool_records(JCR *jcr, B_DB *mdb)
511 Mmsg(mdb->cmd, "SELECT count(*) from Pool");
512 stat = get_sql_record_max(jcr, mdb);
518 * This function returns a list of all the Pool record ids.
519 * The caller must free ids if non-NULL.
521 * Returns 0: on failure
524 int db_get_pool_ids(JCR *jcr, B_DB *mdb, int *num_ids, uint32_t *ids[])
533 Mmsg(mdb->cmd, "SELECT PoolId FROM Pool");
534 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
535 *num_ids = sql_num_rows(mdb);
537 id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
538 while ((row = sql_fetch_row(mdb)) != NULL) {
539 id[i++] = str_to_uint64(row[0]);
543 sql_free_result(mdb);
546 Mmsg(mdb->errmsg, _("Pool id select failed: ERR=%s\n"), sql_strerror(mdb));
547 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
555 * This function returns a list of all the Client record ids.
556 * The caller must free ids if non-NULL.
558 * Returns 0: on failure
561 int db_get_client_ids(JCR *jcr, B_DB *mdb, int *num_ids, uint32_t *ids[])
570 Mmsg(mdb->cmd, "SELECT ClientId FROM Client");
571 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
572 *num_ids = sql_num_rows(mdb);
574 id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
575 while ((row = sql_fetch_row(mdb)) != NULL) {
576 id[i++] = str_to_uint64(row[0]);
580 sql_free_result(mdb);
583 Mmsg(mdb->errmsg, _("Client id select failed: ERR=%s\n"), sql_strerror(mdb));
584 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
594 * If the PoolId is non-zero, we get its record,
595 * otherwise, we search on the PoolName
597 * Returns: false on failure
600 bool db_get_pool_record(JCR *jcr, B_DB *mdb, POOL_DBR *pdbr)
607 if (pdbr->PoolId != 0) { /* find by id */
609 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
610 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
611 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId,"
612 "ActionOnPurge FROM Pool WHERE Pool.PoolId=%s",
613 edit_int64(pdbr->PoolId, ed1));
614 } else { /* find by name */
616 "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
617 "AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
618 "MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId,"
619 "ActionOnPurge FROM Pool WHERE Pool.Name='%s'",
622 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
623 mdb->num_rows = sql_num_rows(mdb);
624 if (mdb->num_rows > 1) {
626 Mmsg1(mdb->errmsg, _("More than one Pool!: %s\n"),
627 edit_uint64(mdb->num_rows, ed1));
628 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
629 } else if (mdb->num_rows == 1) {
630 if ((row = sql_fetch_row(mdb)) == NULL) {
631 Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
632 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
634 pdbr->PoolId = str_to_int64(row[0]);
635 bstrncpy(pdbr->Name, row[1]!=NULL?row[1]:"", sizeof(pdbr->Name));
636 pdbr->NumVols = str_to_int64(row[2]);
637 pdbr->MaxVols = str_to_int64(row[3]);
638 pdbr->UseOnce = str_to_int64(row[4]);
639 pdbr->UseCatalog = str_to_int64(row[5]);
640 pdbr->AcceptAnyVolume = str_to_int64(row[6]);
641 pdbr->AutoPrune = str_to_int64(row[7]);
642 pdbr->Recycle = str_to_int64(row[8]);
643 pdbr->VolRetention = str_to_int64(row[9]);
644 pdbr->VolUseDuration = str_to_int64(row[10]);
645 pdbr->MaxVolJobs = str_to_int64(row[11]);
646 pdbr->MaxVolFiles = str_to_int64(row[12]);
647 pdbr->MaxVolBytes = str_to_uint64(row[13]);
648 bstrncpy(pdbr->PoolType, row[14]!=NULL?row[14]:"", sizeof(pdbr->PoolType));
649 pdbr->LabelType = str_to_int64(row[15]);
650 bstrncpy(pdbr->LabelFormat, row[16]!=NULL?row[16]:"", sizeof(pdbr->LabelFormat));
651 pdbr->RecyclePoolId = str_to_int64(row[17]);
652 pdbr->ScratchPoolId = str_to_int64(row[18]);
653 pdbr->ActionOnPurge = str_to_int32(row[19]);
657 sql_free_result(mdb);
661 Mmsg(mdb->cmd, "SELECT count(*) from Media WHERE PoolId=%s",
662 edit_int64(pdbr->PoolId, ed1));
663 NumVols = get_sql_record_max(jcr, mdb);
664 Dmsg2(400, "Actual NumVols=%d Pool NumVols=%d\n", NumVols, pdbr->NumVols);
665 if (NumVols != pdbr->NumVols) {
666 pdbr->NumVols = NumVols;
667 db_update_pool_record(jcr, mdb, pdbr);
670 Mmsg(mdb->errmsg, _("Pool record not found in Catalog.\n"));
677 * If the ClientId is non-zero, we get its record,
678 * otherwise, we search on the Client Name
680 * Returns: 0 on failure
683 int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cdbr)
690 if (cdbr->ClientId != 0) { /* find by id */
692 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
693 "FROM Client WHERE Client.ClientId=%s",
694 edit_int64(cdbr->ClientId, ed1));
695 } else { /* find by name */
697 "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,JobRetention "
698 "FROM Client WHERE Client.Name='%s'", cdbr->Name);
701 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
702 mdb->num_rows = sql_num_rows(mdb);
703 if (mdb->num_rows > 1) {
704 Mmsg1(mdb->errmsg, _("More than one Client!: %s\n"),
705 edit_uint64(mdb->num_rows, ed1));
706 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
707 } else if (mdb->num_rows == 1) {
708 if ((row = sql_fetch_row(mdb)) == NULL) {
709 Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
710 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
712 cdbr->ClientId = str_to_int64(row[0]);
713 bstrncpy(cdbr->Name, row[1]!=NULL?row[1]:"", sizeof(cdbr->Name));
714 bstrncpy(cdbr->Uname, row[2]!=NULL?row[2]:"", sizeof(cdbr->Uname));
715 cdbr->AutoPrune = str_to_int64(row[3]);
716 cdbr->FileRetention = str_to_int64(row[4]);
717 cdbr->JobRetention = str_to_int64(row[5]);
721 Mmsg(mdb->errmsg, _("Client record not found in Catalog.\n"));
723 sql_free_result(mdb);
725 Mmsg(mdb->errmsg, _("Client record not found in Catalog.\n"));
734 * Returns: 0 on failure
737 int db_get_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr)
742 Mmsg(mdb->cmd, "SELECT MinValue,MaxValue,CurrentValue,WrapCounter "
743 "FROM Counters WHERE Counter='%s'", cr->Counter);
745 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
746 mdb->num_rows = sql_num_rows(mdb);
748 /* If more than one, report error, but return first row */
749 if (mdb->num_rows > 1) {
750 Mmsg1(mdb->errmsg, _("More than one Counter!: %d\n"), (int)(mdb->num_rows));
751 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
753 if (mdb->num_rows >= 1) {
754 if ((row = sql_fetch_row(mdb)) == NULL) {
755 Mmsg1(mdb->errmsg, _("error fetching Counter row: %s\n"), sql_strerror(mdb));
756 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
757 sql_free_result(mdb);
761 cr->MinValue = str_to_int64(row[0]);
762 cr->MaxValue = str_to_int64(row[1]);
763 cr->CurrentValue = str_to_int64(row[2]);
765 bstrncpy(cr->WrapCounter, row[3], sizeof(cr->WrapCounter));
767 cr->WrapCounter[0] = 0;
769 sql_free_result(mdb);
773 sql_free_result(mdb);
775 Mmsg(mdb->errmsg, _("Counter record: %s not found in Catalog.\n"), cr->Counter);
782 /* Get FileSet Record
783 * If the FileSetId is non-zero, we get its record,
784 * otherwise, we search on the name
786 * Returns: 0 on failure
789 int db_get_fileset_record(JCR *jcr, B_DB *mdb, FILESET_DBR *fsr)
796 if (fsr->FileSetId != 0) { /* find by id */
798 "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
799 "WHERE FileSetId=%s",
800 edit_int64(fsr->FileSetId, ed1));
801 } else { /* find by name */
803 "SELECT FileSetId,FileSet,MD5,CreateTime FROM FileSet "
804 "WHERE FileSet='%s' ORDER BY CreateTime DESC LIMIT 1", fsr->FileSet);
807 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
808 mdb->num_rows = sql_num_rows(mdb);
809 if (mdb->num_rows > 1) {
811 Mmsg1(mdb->errmsg, _("Error got %s FileSets but expected only one!\n"),
812 edit_uint64(mdb->num_rows, ed1));
813 sql_data_seek(mdb, mdb->num_rows-1);
815 if ((row = sql_fetch_row(mdb)) == NULL) {
816 Mmsg1(mdb->errmsg, _("FileSet record \"%s\" not found.\n"), fsr->FileSet);
818 fsr->FileSetId = str_to_int64(row[0]);
819 bstrncpy(fsr->FileSet, row[1]!=NULL?row[1]:"", sizeof(fsr->FileSet));
820 bstrncpy(fsr->MD5, row[2]!=NULL?row[2]:"", sizeof(fsr->MD5));
821 bstrncpy(fsr->cCreateTime, row[3]!=NULL?row[3]:"", sizeof(fsr->cCreateTime));
822 stat = fsr->FileSetId;
824 sql_free_result(mdb);
826 Mmsg(mdb->errmsg, _("FileSet record not found in Catalog.\n"));
834 * Get the number of Media records
836 * Returns: -1 on failure
839 int db_get_num_media_records(JCR *jcr, B_DB *mdb)
844 Mmsg(mdb->cmd, "SELECT count(*) from Media");
845 stat = get_sql_record_max(jcr, mdb);
852 * This function returns a list of all the Media record ids for
853 * the current Pool with the correct Media Type.
854 * The caller must free ids if non-NULL.
856 * Returns false: on failure
859 bool db_get_media_ids(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr, int *num_ids, uint32_t *ids[])
869 Mmsg(mdb->cmd, "SELECT DISTINCT MediaId FROM Media WHERE PoolId=%s "
870 " AND MediaType='%s'",
871 edit_int64(mr->PoolId, ed1), mr->MediaType);
872 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
873 *num_ids = sql_num_rows(mdb);
875 id = (uint32_t *)malloc(*num_ids * sizeof(uint32_t));
876 while ((row = sql_fetch_row(mdb)) != NULL) {
877 id[i++] = str_to_uint64(row[0]);
881 sql_free_result(mdb);
884 Mmsg(mdb->errmsg, _("Media id select failed: ERR=%s\n"), sql_strerror(mdb));
885 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
894 * This function returns a list of all the DBIds that are returned
897 * Returns false: on failure
900 bool db_get_query_dbids(JCR *jcr, B_DB *mdb, POOL_MEM &query, dbid_list &ids)
908 if (QUERY_DB(jcr, mdb, query.c_str())) {
909 ids.num_ids = sql_num_rows(mdb);
910 if (ids.num_ids > 0) {
911 if (ids.max_ids < ids.num_ids) {
913 ids.DBId = (DBId_t *)malloc(ids.num_ids * sizeof(DBId_t));
915 while ((row = sql_fetch_row(mdb)) != NULL) {
916 ids.DBId[i++] = str_to_uint64(row[0]);
919 sql_free_result(mdb);
922 Mmsg(mdb->errmsg, _("query dbids failed: ERR=%s\n"), sql_strerror(mdb));
923 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
932 * Returns: false: on failure
935 bool db_get_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr)
942 if (mr->MediaId == 0 && mr->VolumeName[0] == 0) {
943 Mmsg(mdb->cmd, "SELECT count(*) from Media");
944 mr->MediaId = get_sql_record_max(jcr, mdb);
948 if (mr->MediaId != 0) { /* find by id */
949 Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
950 "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
951 "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
952 "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
953 "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
954 "Enabled,LocationId,RecycleCount,InitialWrite,"
955 "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge "
956 "FROM Media WHERE MediaId=%s",
957 edit_int64(mr->MediaId, ed1));
958 } else { /* find by name */
959 Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
960 "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
961 "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
962 "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
963 "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId,"
964 "Enabled,LocationId,RecycleCount,InitialWrite,"
965 "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge "
966 "FROM Media WHERE VolumeName='%s'", mr->VolumeName);
969 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
971 mdb->num_rows = sql_num_rows(mdb);
972 if (mdb->num_rows > 1) {
973 Mmsg1(mdb->errmsg, _("More than one Volume!: %s\n"),
974 edit_uint64(mdb->num_rows, ed1));
975 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
976 } else if (mdb->num_rows == 1) {
977 if ((row = sql_fetch_row(mdb)) == NULL) {
978 Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
979 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
982 mr->MediaId = str_to_int64(row[0]);
983 bstrncpy(mr->VolumeName, row[1]!=NULL?row[1]:"", sizeof(mr->VolumeName));
984 mr->VolJobs = str_to_int64(row[2]);
985 mr->VolFiles = str_to_int64(row[3]);
986 mr->VolBlocks = str_to_int64(row[4]);
987 mr->VolBytes = str_to_uint64(row[5]);
988 mr->VolMounts = str_to_int64(row[6]);
989 mr->VolErrors = str_to_int64(row[7]);
990 mr->VolWrites = str_to_int64(row[8]);
991 mr->MaxVolBytes = str_to_uint64(row[9]);
992 mr->VolCapacityBytes = str_to_uint64(row[10]);
993 bstrncpy(mr->MediaType, row[11]!=NULL?row[11]:"", sizeof(mr->MediaType));
994 bstrncpy(mr->VolStatus, row[12]!=NULL?row[12]:"", sizeof(mr->VolStatus));
995 mr->PoolId = str_to_int64(row[13]);
996 mr->VolRetention = str_to_uint64(row[14]);
997 mr->VolUseDuration = str_to_uint64(row[15]);
998 mr->MaxVolJobs = str_to_int64(row[16]);
999 mr->MaxVolFiles = str_to_int64(row[17]);
1000 mr->Recycle = str_to_int64(row[18]);
1001 mr->Slot = str_to_int64(row[19]);
1002 bstrncpy(mr->cFirstWritten, row[20]!=NULL?row[20]:"", sizeof(mr->cFirstWritten));
1003 mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten);
1004 bstrncpy(mr->cLastWritten, row[21]!=NULL?row[21]:"", sizeof(mr->cLastWritten));
1005 mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
1006 mr->InChanger = str_to_uint64(row[22]);
1007 mr->EndFile = str_to_uint64(row[23]);
1008 mr->EndBlock = str_to_uint64(row[24]);
1009 mr->VolParts = str_to_int64(row[25]);
1010 mr->LabelType = str_to_int64(row[26]);
1011 bstrncpy(mr->cLabelDate, row[27]!=NULL?row[27]:"", sizeof(mr->cLabelDate));
1012 mr->LabelDate = (time_t)str_to_utime(mr->cLabelDate);
1013 mr->StorageId = str_to_int64(row[28]);
1014 mr->Enabled = str_to_int64(row[29]);
1015 mr->LocationId = str_to_int64(row[30]);
1016 mr->RecycleCount = str_to_int64(row[31]);
1017 bstrncpy(mr->cInitialWrite, row[32]!=NULL?row[32]:"", sizeof(mr->cInitialWrite));
1018 mr->InitialWrite = (time_t)str_to_utime(mr->cInitialWrite);
1019 mr->ScratchPoolId = str_to_int64(row[33]);
1020 mr->RecyclePoolId = str_to_int64(row[34]);
1021 mr->VolReadTime = str_to_int64(row[35]);
1022 mr->VolWriteTime = str_to_int64(row[36]);
1023 mr->ActionOnPurge = str_to_int32(row[37]);
1028 if (mr->MediaId != 0) {
1029 Mmsg1(mdb->errmsg, _("Media record MediaId=%s not found.\n"),
1030 edit_int64(mr->MediaId, ed1));
1032 Mmsg1(mdb->errmsg, _("Media record for Volume \"%s\" not found.\n"),
1036 sql_free_result(mdb);
1038 if (mr->MediaId != 0) {
1039 Mmsg(mdb->errmsg, _("Media record for MediaId=%u not found in Catalog.\n"),
1042 Mmsg(mdb->errmsg, _("Media record for Vol=%s not found in Catalog.\n"),
1050 * Find the last "accurate" backup state (that can take deleted files in account)
1051 * 1) Get all files with jobid in list (F subquery)
1052 * 2) Take only the last version of each file (Temp subquery) => accurate list is ok
1053 * 3) Join the result to file table to get fileindex, jobid and lstat information
1055 * TODO: See if we can do the SORT only if needed (as an argument)
1057 bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids,
1058 DB_RESULT_HANDLER *result_handler, void *ctx)
1062 Mmsg(mdb->errmsg, _("ERR=JobIds are empty\n"));
1066 POOL_MEM buf(PM_MESSAGE);
1068 #define new_db_get_file_list
1069 #ifdef new_db_get_file_list
1070 /* This is broken, at least if called from ua_restore.c */
1072 "SELECT Path.Path, Filename.Name, File.FileIndex, File.JobId, File.LStat "
1074 "SELECT max(FileId) as FileId, PathId, FilenameId "
1075 "FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (%s)) AS F "
1076 "GROUP BY PathId, FilenameId "
1078 "JOIN Filename ON (Filename.FilenameId = Temp.FilenameId) "
1079 "JOIN Path ON (Path.PathId = Temp.PathId) "
1080 "JOIN File ON (File.FileId = Temp.FileId) "
1081 "WHERE File.FileIndex > 0 ORDER BY JobId, FileIndex ASC", /* Return sorted by JobId, */
1082 /* FileIndex for restore code */
1086 * I am not sure that this works the same as the code in ua_restore.c
1087 * but it is very similar. The accurate-test fails in a restore. Bad file count.
1089 Mmsg(buf, uar_sel_files, jobids);
1092 return db_sql_query(mdb, buf.c_str(), result_handler, ctx);
1095 /* The decision do change an incr/diff was done before
1097 * Differential : get the last full id
1098 * Incremental : get the last full + last diff + last incr(s) ids
1100 * If you specify jr->StartTime, it will be used to limit the search
1101 * in the time. (usually now)
1103 * TODO: look and merge from ua_restore.c
1105 bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb,
1106 JOB_DBR *jr, db_list_ctx *jobids)
1109 char clientid[50], jobid[50], filesetid[50];
1110 char date[MAX_TIME_LENGTH];
1111 POOL_MEM query(PM_FNAME);
1113 /* Take the current time as upper limit if nothing else specified */
1114 utime_t StartTime = (jr->StartTime)?jr->StartTime:time(NULL);
1116 bstrutime(date, sizeof(date), StartTime + 1);
1117 jobids->list[0] = 0;
1120 /* First, find the last good Full backup for this job/client/fileset */
1122 "CREATE TABLE btemp3%s AS "
1123 "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1124 "FROM Job JOIN FileSet USING (FileSetId) "
1125 "WHERE ClientId = %s "
1126 "AND Level='F' AND JobStatus IN ('T','W') AND Type='B' "
1127 "AND StartTime<'%s' "
1128 "AND FileSet.FileSet=(SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1129 "ORDER BY Job.JobTDate DESC LIMIT 1",
1130 edit_uint64(jcr->JobId, jobid),
1131 edit_uint64(jr->ClientId, clientid),
1133 edit_uint64(jr->FileSetId, filesetid));
1135 if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
1139 if (jr->JobLevel == L_INCREMENTAL || jr->JobLevel == L_VIRTUAL_FULL) {
1140 /* Now, find the last differential backup after the last full */
1142 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
1143 "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1144 "FROM Job JOIN FileSet USING (FileSetId) "
1145 "WHERE ClientId = %s "
1146 "AND Level='D' AND JobStatus IN ('T','W') AND Type='B' "
1147 "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
1148 "AND StartTime < '%s' "
1149 "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1150 "ORDER BY Job.JobTDate DESC LIMIT 1 ",
1157 if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
1161 /* We just have to take all incremental after the last Full/Diff */
1163 "INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
1164 "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
1165 "FROM Job JOIN FileSet USING (FileSetId) "
1166 "WHERE ClientId = %s "
1167 "AND Level='I' AND JobStatus IN ('T','W') AND Type='B' "
1168 "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
1169 "AND StartTime < '%s' "
1170 "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
1171 "ORDER BY Job.JobTDate DESC ",
1177 if (!db_sql_query(mdb, query.c_str(), NULL, NULL)) {
1182 /* build a jobid list ie: 1,2,3,4 */
1183 Mmsg(query, "SELECT JobId FROM btemp3%s ORDER by JobTDate", jobid);
1184 db_sql_query(mdb, query.c_str(), db_list_handler, jobids);
1185 Dmsg1(1, "db_accurate_get_jobids=%s\n", jobids->list);
1189 Mmsg(query, "DROP TABLE btemp3%s", jobid);
1190 db_sql_query(mdb, query.c_str(), NULL, NULL);
1195 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI */