]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql_update.c
Fix spurious MD5 update errors when nothing changed should fix bug #2237 and others
[bacula/bacula] / bacula / src / cats / sql_update.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2016 Kern Sibbald
5
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.
8
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.
13
14    This notice must be preserved when any source code is 
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  * Bacula Catalog Database Update record interface routines
21  *
22  *    Written by Kern Sibbald, March 2000
23  *
24  */
25
26 #include  "bacula.h"
27
28 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
29  
30 #include  "cats.h"
31
32 #define dbglevel1  100
33 #define dbglevel2  400
34
35 /* -----------------------------------------------------------------------
36  *
37  *   Generic Routines (or almost generic)
38  *
39  * -----------------------------------------------------------------------
40  */
41
42 /* -----------------------------------------------------------------------
43  *
44  *   Generic Routines (or almost generic)
45  *
46  * -----------------------------------------------------------------------
47  */
48 /* Update the attributes record by adding the file digest */
49 int BDB::bdb_add_digest_to_file_record(JCR *jcr, FileId_t FileId, char *digest,
50                           int type)
51 {
52    int ret;
53    char ed1[50];
54    int len = strlen(digest);
55
56    bdb_lock();
57    esc_name = check_pool_memory_size(esc_name, len*2+1);
58    bdb_escape_string(jcr, esc_name, digest, len);
59    Mmsg(cmd, "UPDATE File SET MD5='%s' WHERE FileId=%s", esc_name,
60         edit_int64(FileId, ed1));
61    ret = UpdateDB(jcr, cmd, true);
62    bdb_unlock();
63    return ret;
64 }
65
66 /* Mark the file record as being visited during database
67  * verify compare. Stuff JobId into the MarkId field
68  */
69 int BDB::bdb_mark_file_record(JCR *jcr, FileId_t FileId, JobId_t JobId)
70 {
71    int stat;
72    char ed1[50], ed2[50];
73
74    bdb_lock();
75    Mmsg(cmd, "UPDATE File SET MarkId=%s WHERE FileId=%s",
76       edit_int64(JobId, ed1), edit_int64(FileId, ed2));
77    stat = UpdateDB(jcr, cmd, false);
78    bdb_unlock();
79    return stat;
80 }
81
82 /*
83  * Update the Job record at start of Job
84  *
85  *  Returns: false on failure
86  *           true  on success
87  */
88 bool BDB::bdb_update_job_start_record(JCR *jcr, JOB_DBR *jr)
89 {
90    char dt[MAX_TIME_LENGTH];
91    time_t stime;
92    struct tm tm;
93    btime_t JobTDate;
94    int stat;
95    char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50];
96
97    stime = jr->StartTime;
98    (void)localtime_r(&stime, &tm);
99    strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
100    JobTDate = (btime_t)stime;
101
102    bdb_lock();
103    Mmsg(cmd, "UPDATE Job SET JobStatus='%c',Level='%c',StartTime='%s',"
104 "ClientId=%s,JobTDate=%s,PoolId=%s,FileSetId=%s WHERE JobId=%s",
105       (char)(jcr->JobStatus),
106       (char)(jr->JobLevel), dt,
107       edit_int64(jr->ClientId, ed1),
108       edit_uint64(JobTDate, ed2),
109       edit_int64(jr->PoolId, ed3),
110       edit_int64(jr->FileSetId, ed4),
111       edit_int64(jr->JobId, ed5));
112
113    stat = UpdateDB(jcr, cmd, false);
114    changes = 0;
115    bdb_unlock();
116    return stat;
117 }
118
119 /*
120  * Update Long term statistics with all jobs that were run before
121  * age seconds
122  */
123 int BDB::bdb_update_stats(JCR *jcr, utime_t age)
124 {
125    char ed1[30];
126    int rows;
127
128    utime_t now = (utime_t)time(NULL);
129    edit_uint64(now - age, ed1);
130
131    bdb_lock();
132
133    Mmsg(cmd, fill_jobhisto, ed1);
134    QueryDB(jcr, cmd); /* TODO: get a message ? */
135    rows = sql_affected_rows();
136
137    bdb_unlock();
138
139    return rows;
140 }
141
142 /*
143  * Update the Job record at end of Job
144  *
145  *  Returns: 0 on failure
146  *           1 on success
147  */
148 int BDB::bdb_update_job_end_record(JCR *jcr, JOB_DBR *jr)
149 {
150    char dt[MAX_TIME_LENGTH];
151    char rdt[MAX_TIME_LENGTH];
152    time_t ttime;
153    struct tm tm;
154    int stat;
155    char ed1[30], ed2[30], ed3[50], ed4[50];
156    btime_t JobTDate;
157    char PriorJobId[50];
158
159    if (jr->PriorJobId) {
160       bstrncpy(PriorJobId, edit_int64(jr->PriorJobId, ed1), sizeof(PriorJobId));
161    } else {
162       bstrncpy(PriorJobId, "0", sizeof(PriorJobId));
163    }
164
165    ttime = jr->EndTime;
166    (void)localtime_r(&ttime, &tm);
167    strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
168
169    if (jr->RealEndTime == 0 || jr->RealEndTime < jr->EndTime) {
170       jr->RealEndTime = jr->EndTime;
171    }
172    ttime = jr->RealEndTime;
173    (void)localtime_r(&ttime, &tm);
174    strftime(rdt, sizeof(rdt), "%Y-%m-%d %H:%M:%S", &tm);
175
176    JobTDate = ttime;
177
178    bdb_lock();
179    Mmsg(cmd,
180       "UPDATE Job SET JobStatus='%c',EndTime='%s',"
181 "ClientId=%u,JobBytes=%s,ReadBytes=%s,JobFiles=%u,JobErrors=%u,VolSessionId=%u,"
182 "VolSessionTime=%u,PoolId=%u,FileSetId=%u,JobTDate=%s,"
183 "RealEndTime='%s',PriorJobId=%s,HasBase=%u,PurgedFiles=%u WHERE JobId=%s",
184       (char)(jr->JobStatus), dt, jr->ClientId, edit_uint64(jr->JobBytes, ed1),
185       edit_uint64(jr->ReadBytes, ed4),
186       jr->JobFiles, jr->JobErrors, jr->VolSessionId, jr->VolSessionTime,
187       jr->PoolId, jr->FileSetId, edit_uint64(JobTDate, ed2),
188       rdt, PriorJobId, jr->HasBase, jr->PurgedFiles,
189       edit_int64(jr->JobId, ed3));
190
191    stat = UpdateDB(jcr, cmd, false);
192
193    bdb_unlock();
194    return stat;
195 }
196
197 /*
198  * Update Client record
199  *   Returns: 0 on failure
200  *            1 on success
201  */
202 int BDB::bdb_update_client_record(JCR *jcr, CLIENT_DBR *cr)
203 {
204    int stat;
205    char ed1[50], ed2[50];
206    char esc_name[MAX_ESCAPE_NAME_LENGTH];
207    char esc_uname[MAX_ESCAPE_NAME_LENGTH];
208    CLIENT_DBR tcr;
209
210    bdb_lock();
211    memcpy(&tcr, cr, sizeof(tcr));
212    if (!bdb_create_client_record(jcr, &tcr)) {
213       bdb_unlock();
214       return 0;
215    }
216
217    bdb_escape_string(jcr, esc_name, cr->Name, strlen(cr->Name));
218    bdb_escape_string(jcr, esc_uname, cr->Uname, strlen(cr->Uname));
219    Mmsg(cmd,
220 "UPDATE Client SET AutoPrune=%d,FileRetention=%s,JobRetention=%s,"
221 "Uname='%s' WHERE Name='%s'",
222       cr->AutoPrune,
223       edit_uint64(cr->FileRetention, ed1),
224       edit_uint64(cr->JobRetention, ed2),
225       esc_uname, esc_name);
226
227    stat = UpdateDB(jcr, cmd, false);
228    bdb_unlock();
229    return stat;
230 }
231
232
233 /*
234  * Update Counters record
235  *   Returns: 0 on failure
236  *            1 on success
237  */
238 int BDB::bdb_update_counter_record(JCR *jcr, COUNTER_DBR *cr)
239 {
240    char esc[MAX_ESCAPE_NAME_LENGTH];
241
242    bdb_lock();
243    bdb_escape_string(jcr, esc, cr->Counter, strlen(cr->Counter));
244    Mmsg(cmd, update_counter_values[bdb_get_type_index()],
245       cr->MinValue, cr->MaxValue, cr->CurrentValue,
246       cr->WrapCounter, esc);
247
248    int stat = UpdateDB(jcr, cmd, false);
249    bdb_unlock();
250    return stat;
251 }
252
253
254 int BDB::bdb_update_pool_record(JCR *jcr, POOL_DBR *pr)
255 {
256    int stat;
257    char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50], ed6[50];
258    char esc[MAX_ESCAPE_NAME_LENGTH];
259
260    bdb_lock();
261    bdb_escape_string(jcr, esc, pr->LabelFormat, strlen(pr->LabelFormat));
262
263    Mmsg(cmd, "SELECT count(*) from Media WHERE PoolId=%s",
264       edit_int64(pr->PoolId, ed4));
265    pr->NumVols = get_sql_record_max(jcr, this);
266    Dmsg1(dbglevel2, "NumVols=%d\n", pr->NumVols);
267
268    Mmsg(cmd,
269 "UPDATE Pool SET NumVols=%u,MaxVols=%u,UseOnce=%d,UseCatalog=%d,"
270 "AcceptAnyVolume=%d,VolRetention='%s',VolUseDuration='%s',"
271 "MaxVolJobs=%u,MaxVolFiles=%u,MaxVolBytes=%s,Recycle=%d,"
272 "AutoPrune=%d,LabelType=%d,LabelFormat='%s',RecyclePoolId=%s,"
273 "ScratchPoolId=%s,ActionOnPurge=%d WHERE PoolId=%s",
274       pr->NumVols, pr->MaxVols, pr->UseOnce, pr->UseCatalog,
275       pr->AcceptAnyVolume, edit_uint64(pr->VolRetention, ed1),
276       edit_uint64(pr->VolUseDuration, ed2),
277       pr->MaxVolJobs, pr->MaxVolFiles,
278       edit_uint64(pr->MaxVolBytes, ed3),
279       pr->Recycle, pr->AutoPrune, pr->LabelType,
280       esc, edit_int64(pr->RecyclePoolId,ed5),
281       edit_int64(pr->ScratchPoolId,ed6),
282       pr->ActionOnPurge,
283       ed4);
284    stat = UpdateDB(jcr, cmd, false);
285    bdb_unlock();
286    return stat;
287 }
288
289 bool BDB::bdb_update_storage_record(JCR *jcr, STORAGE_DBR *sr)
290 {
291    int stat;
292    char ed1[50];
293
294    bdb_lock();
295    Mmsg(cmd, "UPDATE Storage SET AutoChanger=%d WHERE StorageId=%s",
296       sr->AutoChanger, edit_int64(sr->StorageId, ed1));
297
298    stat = UpdateDB(jcr, cmd, false);
299    bdb_unlock();
300    return stat;
301 }
302
303
304 /*
305  * Update the Media Record at end of Session
306  *
307  * Returns: 0 on failure
308  *          numrows on success
309  */
310 int BDB::bdb_update_media_record(JCR *jcr, MEDIA_DBR *mr)
311 {
312    char dt[MAX_TIME_LENGTH];
313    time_t ttime;
314    struct tm tm;
315    int stat;
316    char ed1[50], ed2[50],  ed3[50],  ed4[50];
317    char ed5[50], ed6[50],  ed7[50],  ed8[50];
318    char ed9[50], ed10[50], ed11[50], ed12[50];
319    char ed13[50], ed14[50];
320    char esc_name[MAX_ESCAPE_NAME_LENGTH];
321    char esc_status[MAX_ESCAPE_NAME_LENGTH];
322
323    Dmsg1(dbglevel1, "update_media: FirstWritten=%d\n", mr->FirstWritten);
324    bdb_lock();
325    bdb_escape_string(jcr, esc_name, mr->VolumeName, strlen(mr->VolumeName));
326    bdb_escape_string(jcr, esc_status, mr->VolStatus, strlen(mr->VolStatus));
327
328    if (mr->set_first_written) {
329       Dmsg1(dbglevel2, "Set FirstWritten Vol=%s\n", mr->VolumeName);
330       ttime = mr->FirstWritten;
331       (void)localtime_r(&ttime, &tm);
332       strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
333       Mmsg(cmd, "UPDATE Media SET FirstWritten='%s'"
334            " WHERE VolumeName='%s'", dt, esc_name);
335       stat = UpdateDB(jcr, cmd, false);
336       Dmsg1(dbglevel2, "Firstwritten=%d\n", mr->FirstWritten);
337    }
338
339    /* Label just done? */
340    if (mr->set_label_date) {
341       ttime = mr->LabelDate;
342       if (ttime == 0) {
343          ttime = time(NULL);
344       }
345       (void)localtime_r(&ttime, &tm);
346       strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
347       Mmsg(cmd, "UPDATE Media SET LabelDate='%s' "
348            "WHERE VolumeName='%s'", dt, esc_name);
349       UpdateDB(jcr, cmd, false);
350    }
351
352    if (mr->LastWritten != 0) {
353       ttime = mr->LastWritten;
354       (void)localtime_r(&ttime, &tm);
355       strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
356       Mmsg(cmd, "UPDATE Media Set LastWritten='%s' "
357            "WHERE VolumeName='%s'", dt, esc_name);
358       UpdateDB(jcr, cmd, false);
359    }
360
361    /* sanity checks for #1066 */
362    if (mr->VolReadTime < 0) {
363       mr->VolReadTime = 0;
364    }
365    if (mr->VolWriteTime < 0) {
366       mr->VolWriteTime = 0;
367    }
368
369    Mmsg(cmd, "UPDATE Media SET VolJobs=%u,"
370         "VolFiles=%u,VolBlocks=%u,VolBytes=%s,VolABytes=%s,"
371         "VolHoleBytes=%s,VolHoles=%u,VolMounts=%u,VolErrors=%u,"
372         "VolWrites=%s,MaxVolBytes=%s,VolStatus='%s',"
373         "Slot=%d,InChanger=%d,VolReadTime=%s,VolWriteTime=%s,VolParts=%d,"
374         "LabelType=%d,StorageId=%s,PoolId=%s,VolRetention=%s,VolUseDuration=%s,"
375         "MaxVolJobs=%d,MaxVolFiles=%d,Enabled=%d,LocationId=%s,"
376         "ScratchPoolId=%s,RecyclePoolId=%s,RecycleCount=%d,Recycle=%d,"
377         "ActionOnPurge=%d"
378         " WHERE VolumeName='%s'",
379         mr->VolJobs, mr->VolFiles, mr->VolBlocks,
380         edit_uint64(mr->VolBytes, ed1),
381         edit_uint64(mr->VolABytes, ed2),
382         edit_uint64(mr->VolHoleBytes, ed3),
383         mr->VolHoles, mr->VolMounts, mr->VolErrors,
384         edit_uint64(mr->VolWrites, ed4),
385         edit_uint64(mr->MaxVolBytes, ed5),
386         esc_status, mr->Slot, mr->InChanger,
387         edit_int64(mr->VolReadTime, ed6),
388         edit_int64(mr->VolWriteTime, ed7),
389         mr->VolType,  /* formerly VolParts */
390         mr->LabelType,
391         edit_int64(mr->StorageId, ed8),
392         edit_int64(mr->PoolId, ed9),
393         edit_uint64(mr->VolRetention, ed10),
394         edit_uint64(mr->VolUseDuration, ed11),
395         mr->MaxVolJobs, mr->MaxVolFiles,
396         mr->Enabled, edit_uint64(mr->LocationId, ed12),
397         edit_uint64(mr->ScratchPoolId, ed13),
398         edit_uint64(mr->RecyclePoolId, ed14),
399         mr->RecycleCount,mr->Recycle, mr->ActionOnPurge,
400         esc_name);
401
402    Dmsg1(dbglevel1, "%s\n", cmd);
403
404    stat = UpdateDB(jcr, cmd, false);
405
406    /* Make sure InChanger is 0 for any record having the same Slot */
407    db_make_inchanger_unique(jcr, this, mr);
408
409    bdb_unlock();
410    return stat;
411 }
412
413 /*
414  * Update the Media Record Default values from Pool
415  *
416  * Returns: 0 on failure
417  *          numrows on success
418  */
419 int BDB::bdb_update_media_defaults(JCR *jcr, MEDIA_DBR *mr)
420 {
421    int stat;
422    char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50];
423    char esc[MAX_ESCAPE_NAME_LENGTH];
424    bool can_be_empty;
425
426    bdb_lock();
427    if (mr->VolumeName[0]) {
428       bdb_escape_string(jcr, esc, mr->VolumeName, strlen(mr->VolumeName));
429       Mmsg(cmd, "UPDATE Media SET "
430            "ActionOnPurge=%d, Recycle=%d,VolRetention=%s,VolUseDuration=%s,"
431            "MaxVolJobs=%u,MaxVolFiles=%u,MaxVolBytes=%s,RecyclePoolId=%s"
432            " WHERE VolumeName='%s'",
433            mr->ActionOnPurge, mr->Recycle,edit_uint64(mr->VolRetention, ed1),
434            edit_uint64(mr->VolUseDuration, ed2),
435            mr->MaxVolJobs, mr->MaxVolFiles,
436            edit_uint64(mr->MaxVolBytes, ed3),
437            edit_uint64(mr->RecyclePoolId, ed4),
438            esc);
439       can_be_empty = false;
440
441    } else {
442       Mmsg(cmd, "UPDATE Media SET "
443            "ActionOnPurge=%d, Recycle=%d,VolRetention=%s,VolUseDuration=%s,"
444            "MaxVolJobs=%u,MaxVolFiles=%u,MaxVolBytes=%s,RecyclePoolId=%s"
445            " WHERE PoolId=%s",
446            mr->ActionOnPurge, mr->Recycle,edit_uint64(mr->VolRetention, ed1),
447            edit_uint64(mr->VolUseDuration, ed2),
448            mr->MaxVolJobs, mr->MaxVolFiles,
449            edit_uint64(mr->MaxVolBytes, ed3),
450            edit_int64(mr->RecyclePoolId, ed4),
451            edit_int64(mr->PoolId, ed5));
452       can_be_empty = true;
453    }
454
455    Dmsg1(dbglevel1, "%s\n", cmd);
456
457    stat = UpdateDB(jcr, cmd, can_be_empty);
458
459    bdb_unlock();
460    return stat;
461 }
462
463
464 /*
465  * If we have a non-zero InChanger, ensure that no other Media
466  *  record has InChanger set on the same Slot.
467  *
468  * This routine assumes the database is already locked.
469  */
470 void BDB::bdb_make_inchanger_unique(JCR *jcr, MEDIA_DBR *mr)
471 {
472    char ed1[50], ed2[50];
473    char esc[MAX_ESCAPE_NAME_LENGTH];
474
475    if (mr->InChanger != 0 && mr->Slot != 0 && mr->StorageId != 0) {
476        if (mr->MediaId != 0) {
477           Mmsg(cmd, "UPDATE Media SET InChanger=0, Slot=0 WHERE "
478                "Slot=%d AND StorageId=%s AND MediaId!=%s",
479                mr->Slot,
480                edit_int64(mr->StorageId, ed1), edit_int64(mr->MediaId, ed2));
481
482        } else if (*mr->VolumeName) {
483           bdb_escape_string(jcr, esc,mr->VolumeName,strlen(mr->VolumeName));
484           Mmsg(cmd, "UPDATE Media SET InChanger=0, Slot=0 WHERE "
485                "Slot=%d AND StorageId=%s AND VolumeName!='%s'",
486                mr->Slot,
487                edit_int64(mr->StorageId, ed1), esc);
488
489        } else {  /* used by ua_label to reset all volume with this slot */
490           Mmsg(cmd, "UPDATE Media SET InChanger=0, Slot=0 WHERE "
491                "Slot=%d AND StorageId=%s",
492                mr->Slot,
493                edit_int64(mr->StorageId, ed1), mr->VolumeName);
494        }
495        Dmsg1(dbglevel1, "%s\n", cmd);
496        UpdateDB(jcr, cmd, true);
497    }
498 }
499
500 /* Update only Retention */
501 bool BDB::bdb_update_snapshot_record(JCR *jcr, SNAPSHOT_DBR *sr)
502 {
503    int stat, len;
504    char ed1[50], ed2[50];
505
506    len = strlen(sr->Comment);
507    bdb_lock();
508
509    esc_name = check_pool_memory_size(esc_name, len*2+1);
510    bdb_escape_string(jcr, esc_name, sr->Comment, len);
511
512    Mmsg(cmd, "UPDATE Snapshot SET Retention=%s, Comment='%s' WHERE SnapshotId=%s",
513         edit_int64(sr->Retention, ed2), sr->Comment, edit_int64(sr->SnapshotId, ed1));
514
515    stat = UpdateDB(jcr, cmd, false);
516    bdb_unlock();
517    return stat;
518 }
519
520 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */