]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql_create.c
Big backport from Enterprise
[bacula/bacula] / bacula / src / cats / sql_create.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 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 Create record interface routines
21  *
22  *    Written by Kern Sibbald, March 2000
23  */
24
25 #include  "bacula.h"
26
27 static const int dbglevel = 160;
28
29 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
30  
31 #include  "cats.h"
32
33 /* -----------------------------------------------------------------------
34  *
35  *   Generic Routines (or almost generic)
36  *
37  * -----------------------------------------------------------------------
38  */
39
40 /** Create a new record for the Job
41  *  Returns: false on failure
42  *          true  on success
43  */
44 bool BDB::bdb_create_job_record(JCR *jcr, JOB_DBR *jr)
45 {
46    POOL_MEM buf;
47    char dt[MAX_TIME_LENGTH];
48    time_t stime;
49    struct tm tm;
50    bool ok;
51    int len;
52    utime_t JobTDate;
53    char ed1[30],ed2[30];
54    char esc_job[MAX_ESCAPE_NAME_LENGTH];
55    char esc_name[MAX_ESCAPE_NAME_LENGTH];
56
57    bdb_lock();
58
59    stime = jr->SchedTime;
60    ASSERT(stime != 0);
61
62    (void)localtime_r(&stime, &tm);
63    strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
64    JobTDate = (utime_t)stime;
65
66    len = strlen(jcr->comment);  /* TODO: use jr instead of jcr to get comment */
67    buf.check_size(len*2+1);
68    bdb_escape_string(jcr, buf.c_str(), jcr->comment, len);
69
70    bdb_escape_string(jcr, esc_job, jr->Job, strlen(jr->Job));
71    bdb_escape_string(jcr, esc_name, jr->Name, strlen(jr->Name));
72
73    /* Must create it */
74    Mmsg(cmd,
75 "INSERT INTO Job (Job,Name,Type,Level,JobStatus,SchedTime,JobTDate,"
76                  "ClientId,Comment) "
77 "VALUES ('%s','%s','%c','%c','%c','%s',%s,%s,'%s')",
78            esc_job, esc_name, (char)(jr->JobType), (char)(jr->JobLevel),
79            (char)(jr->JobStatus), dt, edit_uint64(JobTDate, ed1),
80            edit_int64(jr->ClientId, ed2), buf.c_str());
81
82    if ((jr->JobId = sql_insert_autokey_record(cmd, NT_("Job"))) == 0) {
83       Mmsg2(&errmsg, _("Create DB Job record %s failed. ERR=%s\n"),
84             cmd, sql_strerror());
85       ok = false;
86    } else {
87       ok = true;
88    }
89    bdb_unlock();
90    return ok;
91 }
92
93
94 /** Create a JobMedia record for medium used this job
95  *  Returns: false on failure
96  *          true  on success
97  */
98 bool BDB::bdb_create_jobmedia_record(JCR *jcr, JOBMEDIA_DBR *jm)
99 {
100    bool ok = true;
101    int count;
102    char ed1[50], ed2[50];
103
104    bdb_lock();
105
106    /* Now get count for VolIndex */
107    Mmsg(cmd, "SELECT count(*) from JobMedia WHERE JobId=%s",
108         edit_int64(jm->JobId, ed1));
109    count = get_sql_record_max(jcr, this);
110    if (count < 0) {
111       count = 0;
112    }
113    count++;
114
115    Mmsg(cmd,
116         "INSERT INTO JobMedia (JobId,MediaId,FirstIndex,LastIndex,"
117         "StartFile,EndFile,StartBlock,EndBlock,VolIndex) "
118         "VALUES (%s,%s,%u,%u,%u,%u,%u,%u,%u)",
119         edit_int64(jm->JobId, ed1),
120         edit_int64(jm->MediaId, ed2),
121         jm->FirstIndex, jm->LastIndex,
122         jm->StartFile, jm->EndFile, jm->StartBlock, jm->EndBlock,count);
123
124    Dmsg0(300, cmd);
125    if (!InsertDB(jcr, cmd)) {
126       Mmsg2(&errmsg, _("Create JobMedia record %s failed: ERR=%s\n"), cmd,
127          sql_strerror());
128       ok = false;
129    } else {
130       /* Worked, now update the Media record with the EndFile and EndBlock */
131       Mmsg(cmd,
132            "UPDATE Media SET EndFile=%lu, EndBlock=%lu WHERE MediaId=%lu",
133            jm->EndFile, jm->EndBlock, jm->MediaId);
134       if (!UpdateDB(jcr, cmd, false)) {
135          Mmsg2(&errmsg, _("Update Media record %s failed: ERR=%s\n"), cmd,
136               sql_strerror());
137          ok = false;
138       }
139    }
140    bdb_unlock();
141    Dmsg0(300, "Return from JobMedia\n");
142    return ok;
143 }
144
145 /** Create Unique Pool record
146  *  Returns: false on failure
147  *          true  on success
148  */
149 bool BDB::bdb_create_pool_record(JCR *jcr, POOL_DBR *pr)
150 {
151    bool stat;
152    char ed1[30], ed2[30], ed3[50], ed4[50], ed5[50], ed6[50];
153    char esc_name[MAX_ESCAPE_NAME_LENGTH];
154    char esc_lf[MAX_ESCAPE_NAME_LENGTH];
155
156    Dmsg0(200, "In create pool\n");
157    bdb_lock();
158    bdb_escape_string(jcr, esc_name, pr->Name, strlen(pr->Name));
159    bdb_escape_string(jcr, esc_lf, pr->LabelFormat, strlen(pr->LabelFormat));
160    Mmsg(cmd, "SELECT PoolId,Name FROM Pool WHERE Name='%s'", esc_name);
161    Dmsg1(200, "selectpool: %s\n", cmd);
162
163    if (QueryDB(jcr, cmd)) {
164       if (sql_num_rows() > 0) { 
165          Mmsg1(&errmsg, _("pool record %s already exists\n"), pr->Name);
166          sql_free_result();
167          bdb_unlock();
168          Dmsg1(200, "%s", errmsg); /* pool already exists */
169          return false;
170       }
171       sql_free_result();
172    }
173
174    /* Must create it */
175    Mmsg(cmd,
176 "INSERT INTO Pool (Name,NumVols,MaxVols,UseOnce,UseCatalog,"
177 "AcceptAnyVolume,AutoPrune,Recycle,VolRetention,VolUseDuration,"
178 "MaxVolJobs,MaxVolFiles,MaxVolBytes,PoolType,LabelType,LabelFormat,"
179 "RecyclePoolId,ScratchPoolId,ActionOnPurge,CacheRetention) "
180 "VALUES ('%s',%u,%u,%d,%d,%d,%d,%d,%s,%s,%u,%u,%s,'%s',%d,'%s',%s,%s,%d,%s)",
181                   esc_name,
182                   pr->NumVols, pr->MaxVols,
183                   pr->UseOnce, pr->UseCatalog,
184                   pr->AcceptAnyVolume,
185                   pr->AutoPrune, pr->Recycle,
186                   edit_uint64(pr->VolRetention, ed1),
187                   edit_uint64(pr->VolUseDuration, ed2),
188                   pr->MaxVolJobs, pr->MaxVolFiles,
189                   edit_uint64(pr->MaxVolBytes, ed3),
190                   pr->PoolType, pr->LabelType, esc_lf,
191                   edit_int64(pr->RecyclePoolId,ed4),
192                   edit_int64(pr->ScratchPoolId,ed5),
193                   pr->ActionOnPurge,
194                   edit_uint64(pr->CacheRetention, ed6)
195       );
196    Dmsg1(200, "Create Pool: %s\n", cmd);
197    if ((pr->PoolId = sql_insert_autokey_record(cmd, NT_("Pool"))) == 0) {
198       Mmsg2(&errmsg, _("Create db Pool record %s failed: ERR=%s\n"),
199             cmd, sql_strerror());
200       stat = false;
201    } else {
202       stat = true;
203    }
204    bdb_unlock();
205    return stat;
206 }
207
208 /**
209  * Create Unique Device record
210  * Returns: false on failure
211  *          true  on success
212  */
213 bool BDB::bdb_create_device_record(JCR *jcr, DEVICE_DBR *dr)
214 {
215    bool ok;
216    char ed1[30], ed2[30];
217    char esc[MAX_ESCAPE_NAME_LENGTH];
218
219    Dmsg0(200, "In create Device\n");
220    bdb_lock();
221    bdb_escape_string(jcr, esc, dr->Name, strlen(dr->Name));
222    Mmsg(cmd, "SELECT DeviceId,Name FROM Device WHERE Name='%s'", esc);
223    Dmsg1(200, "selectdevice: %s\n", cmd);
224
225    if (QueryDB(jcr, cmd)) {
226       if (sql_num_rows() > 0) { 
227          Mmsg1(&errmsg, _("Device record %s already exists\n"), dr->Name);
228          sql_free_result();
229          bdb_unlock();
230          return false;
231       }
232       sql_free_result();
233    }
234
235    /* Must create it */
236    Mmsg(cmd,
237 "INSERT INTO Device (Name,MediaTypeId,StorageId) VALUES ('%s',%s,%s)",
238                   esc,
239                   edit_uint64(dr->MediaTypeId, ed1),
240                   edit_int64(dr->StorageId, ed2));
241    Dmsg1(200, "Create Device: %s\n", cmd);
242    if ((dr->DeviceId = sql_insert_autokey_record(cmd, NT_("Device"))) == 0) {
243       Mmsg2(&errmsg, _("Create db Device record %s failed: ERR=%s\n"),
244             cmd, sql_strerror());
245       ok = false;
246    } else {
247       ok = true;
248    }
249    bdb_unlock();
250    return ok;
251 }
252
253
254
255 /**
256  * Create a Unique record for Storage -- no duplicates
257  * Returns: false on failure
258  *          true  on success with id in sr->StorageId
259  */
260 bool BDB::bdb_create_storage_record(JCR *jcr, STORAGE_DBR *sr)
261 {
262    SQL_ROW row;
263    bool ok;
264    char esc[MAX_ESCAPE_NAME_LENGTH];
265
266    bdb_lock();
267    bdb_escape_string(jcr, esc, sr->Name, strlen(sr->Name));
268    Mmsg(cmd, "SELECT StorageId,AutoChanger FROM Storage WHERE Name='%s'",esc);
269
270    sr->StorageId = 0;
271    sr->created = false;
272    /* Check if it already exists */
273    if (QueryDB(jcr, cmd)) {
274       /* If more than one, report error, but return first row */
275       if (sql_num_rows() > 1) { 
276          Mmsg1(&errmsg, _("More than one Storage record!: %d\n"), sql_num_rows());
277          Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
278       }
279       if (sql_num_rows() >= 1) { 
280          if ((row = sql_fetch_row()) == NULL) {
281             Mmsg1(&errmsg, _("error fetching Storage row: %s\n"), sql_strerror());
282             Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
283             sql_free_result();
284             bdb_unlock();
285             return false;
286          }
287          sr->StorageId = str_to_int64(row[0]);
288          sr->AutoChanger = atoi(row[1]);   /* bool */
289          sql_free_result();
290          bdb_unlock();
291          return true;
292       }
293       sql_free_result();
294    }
295
296    /* Must create it */
297    Mmsg(cmd, "INSERT INTO Storage (Name,AutoChanger)"
298         " VALUES ('%s',%d)", esc, sr->AutoChanger);
299
300    if ((sr->StorageId = sql_insert_autokey_record(cmd, NT_("Storage"))) == 0) {
301       Mmsg2(&errmsg, _("Create DB Storage record %s failed. ERR=%s\n"),
302             cmd, sql_strerror());
303       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
304       ok = false;
305    } else {
306       sr->created = true;
307       ok = true;
308    }
309    bdb_unlock();
310    return ok;
311 }
312
313
314 /**
315  * Create Unique MediaType record
316  * Returns: false on failure
317  *          true  on success
318  */
319 bool BDB::bdb_create_mediatype_record(JCR *jcr, MEDIATYPE_DBR *mr)
320 {
321    bool stat;
322    char esc[MAX_ESCAPE_NAME_LENGTH];
323
324    Dmsg0(200, "In create mediatype\n");
325    bdb_lock();
326    bdb_escape_string(jcr, esc, mr->MediaType, strlen(mr->MediaType));
327    Mmsg(cmd, "SELECT MediaTypeId,MediaType FROM MediaType WHERE MediaType='%s'", esc);
328    Dmsg1(200, "selectmediatype: %s\n", cmd);
329
330    if (QueryDB(jcr, cmd)) {
331       if (sql_num_rows() > 0) { 
332          Mmsg1(&errmsg, _("mediatype record %s already exists\n"), mr->MediaType);
333          sql_free_result();
334          bdb_unlock();
335          return false;
336       }
337       sql_free_result();
338    }
339
340    /* Must create it */
341    Mmsg(cmd,
342 "INSERT INTO MediaType (MediaType,ReadOnly) "
343 "VALUES ('%s',%d)",
344                   mr->MediaType,
345                   mr->ReadOnly);
346    Dmsg1(200, "Create mediatype: %s\n", cmd);
347    if ((mr->MediaTypeId = sql_insert_autokey_record(cmd, NT_("MediaType"))) == 0) {
348       Mmsg2(&errmsg, _("Create db mediatype record %s failed: ERR=%s\n"),
349             cmd, sql_strerror());
350       stat = false;
351    } else {
352       stat = true;
353    }
354    bdb_unlock();
355    return stat;
356 }
357
358
359 /**
360  * Create Media record. VolumeName and non-zero Slot must be unique
361  *
362  * Returns: 0 on failure
363  *          1 on success
364  */
365 int BDB::bdb_create_media_record(JCR *jcr, MEDIA_DBR *mr)
366 {
367    int stat;
368    char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50], ed6[50], ed7[50], ed8[50];
369    char ed9[50], ed10[50], ed11[50], ed12[50], ed13[50], ed14[50];
370    struct tm tm;
371    char esc_name[MAX_ESCAPE_NAME_LENGTH];
372    char esc_mtype[MAX_ESCAPE_NAME_LENGTH];
373    char esc_status[MAX_ESCAPE_NAME_LENGTH];
374
375
376    bdb_lock();
377    bdb_escape_string(jcr, esc_name, mr->VolumeName, strlen(mr->VolumeName));
378    bdb_escape_string(jcr, esc_mtype, mr->MediaType, strlen(mr->MediaType));
379    bdb_escape_string(jcr, esc_status, mr->VolStatus, strlen(mr->VolStatus));
380
381    Mmsg(cmd, "SELECT MediaId FROM Media WHERE VolumeName='%s'", esc_name);
382    Dmsg1(500, "selectpool: %s\n", cmd);
383
384    if (QueryDB(jcr, cmd)) {
385       if (sql_num_rows() > 0) { 
386          Mmsg1(&errmsg, _("Volume \"%s\" already exists.\n"), mr->VolumeName);
387          sql_free_result();
388          bdb_unlock();
389          return 0;
390       }
391       sql_free_result();
392    }
393
394    /* Must create it */
395    Mmsg(cmd,
396 "INSERT INTO Media (VolumeName,MediaType,MediaTypeId,PoolId,MaxVolBytes,"
397 "VolCapacityBytes,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
398 "VolStatus,Slot,VolBytes,InChanger,VolReadTime,VolWriteTime,VolType,"
399 "VolParts,VolCloudParts,LastPartBytes,"
400 "EndFile,EndBlock,LabelType,StorageId,DeviceId,LocationId,"
401 "ScratchPoolId,RecyclePoolId,Enabled,ActionOnPurge,CacheRetention)"
402 "VALUES ('%s','%s',0,%u,%s,%s,%d,%s,%s,%u,%u,'%s',%d,%s,%d,%s,%s,%d,"
403         "%d,%d,'%s',%d,%d,%d,%s,%s,%s,%s,%s,%d,%d,%s)",
404           esc_name,
405           esc_mtype, mr->PoolId,
406           edit_uint64(mr->MaxVolBytes,ed1),
407           edit_uint64(mr->VolCapacityBytes, ed2),
408           mr->Recycle,
409           edit_uint64(mr->VolRetention, ed3),
410           edit_uint64(mr->VolUseDuration, ed4),
411           mr->MaxVolJobs,
412           mr->MaxVolFiles,
413           esc_status,
414           mr->Slot,
415           edit_uint64(mr->VolBytes, ed5),
416           mr->InChanger,
417           edit_int64(mr->VolReadTime, ed6),
418           edit_int64(mr->VolWriteTime, ed7),
419           mr->VolType,
420           mr->VolParts,
421           mr->VolCloudParts,
422           edit_uint64(mr->LastPartBytes, ed8),
423           mr->EndFile,
424           mr->EndBlock,
425           mr->LabelType,
426           edit_int64(mr->StorageId, ed9),
427           edit_int64(mr->DeviceId, ed10),
428           edit_int64(mr->LocationId, ed11),
429           edit_int64(mr->ScratchPoolId, ed12),
430           edit_int64(mr->RecyclePoolId, ed13),
431           mr->Enabled, 
432           mr->ActionOnPurge,
433           edit_uint64(mr->CacheRetention, ed14)
434           );
435
436    Dmsg1(500, "Create Volume: %s\n", cmd);
437    if ((mr->MediaId = sql_insert_autokey_record(cmd, NT_("Media"))) == 0) {
438       Mmsg2(&errmsg, _("Create DB Media record %s failed. ERR=%s\n"),
439             cmd, sql_strerror());
440       stat = 0;
441    } else {
442       stat = 1;
443       if (mr->set_label_date) {
444          char dt[MAX_TIME_LENGTH];
445          if (mr->LabelDate == 0) {
446             mr->LabelDate = time(NULL);
447          }
448          (void)localtime_r(&mr->LabelDate, &tm);
449          strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
450          Mmsg(cmd, "UPDATE Media SET LabelDate='%s' "
451               "WHERE MediaId=%lu", dt, mr->MediaId);
452          stat = UpdateDB(jcr, cmd, false);
453       }
454       /*
455        * Make sure that if InChanger is non-zero any other identical slot
456        *   has InChanger zero.
457        */
458       db_make_inchanger_unique(jcr, this, mr);
459    }
460
461    bdb_unlock();
462    return stat;
463 }
464
465 /**
466  * Create a Unique record for the client -- no duplicates
467  * Returns: 0 on failure
468  *          1 on success with id in cr->ClientId
469  */
470 int BDB::bdb_create_client_record(JCR *jcr, CLIENT_DBR *cr)
471 {
472    SQL_ROW row;
473    int stat;
474    char ed1[50], ed2[50];
475    char esc_name[MAX_ESCAPE_NAME_LENGTH];
476    char esc_uname[MAX_ESCAPE_NAME_LENGTH];
477
478    bdb_lock();
479    bdb_escape_string(jcr, esc_name, cr->Name, strlen(cr->Name));
480    bdb_escape_string(jcr, esc_uname, cr->Uname, strlen(cr->Uname));
481    Mmsg(cmd, "SELECT ClientId,Uname FROM Client WHERE Name='%s'",esc_name);
482
483    cr->ClientId = 0;
484    if (QueryDB(jcr, cmd)) {
485       /* If more than one, report error, but return first row */
486       if (sql_num_rows() > 1) { 
487          Mmsg1(&errmsg, _("More than one Client!: %d\n"), sql_num_rows());
488          Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
489       }
490       if (sql_num_rows() >= 1) { 
491          if ((row = sql_fetch_row()) == NULL) {
492             Mmsg1(&errmsg, _("error fetching Client row: %s\n"), sql_strerror());
493             Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
494             sql_free_result();
495             bdb_unlock();
496             return 0;
497          }
498          cr->ClientId = str_to_int64(row[0]);
499          if (row[1]) {
500             bstrncpy(cr->Uname, row[1], sizeof(cr->Uname));
501          } else {
502             cr->Uname[0] = 0;         /* no name */
503          }
504          sql_free_result();
505          bdb_unlock();
506          return 1;
507       }
508       sql_free_result();
509    }
510
511    /* Must create it */
512    Mmsg(cmd, "INSERT INTO Client (Name,Uname,AutoPrune,"
513 "FileRetention,JobRetention) VALUES "
514 "('%s','%s',%d,%s,%s)", esc_name, esc_uname, cr->AutoPrune,
515       edit_uint64(cr->FileRetention, ed1),
516       edit_uint64(cr->JobRetention, ed2));
517
518    if ((cr->ClientId = sql_insert_autokey_record(cmd, NT_("Client"))) == 0) {
519       Mmsg2(&errmsg, _("Create DB Client record %s failed. ERR=%s\n"),
520             cmd, sql_strerror());
521       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
522       stat = 0;
523    } else {
524       stat = 1;
525    }
526    bdb_unlock();
527    return stat;
528 }
529
530
531 /** Create a Unique record for the Path -- no duplicates */
532 int BDB::bdb_create_path_record(JCR *jcr, ATTR_DBR *ar)
533 {
534    SQL_ROW row;
535    int stat;
536
537    errmsg[0] = 0;
538    esc_name = check_pool_memory_size(esc_name, 2*pnl+2);
539    bdb_escape_string(jcr, esc_name, path, pnl);
540
541    if (cached_path_id != 0 && cached_path_len == pnl &&
542        strcmp(cached_path, path) == 0) {
543       ar->PathId = cached_path_id;
544       return 1;
545    }
546
547    Mmsg(cmd, "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
548
549    if (QueryDB(jcr, cmd)) {
550       if (sql_num_rows() > 1) { 
551          char ed1[30];
552          Mmsg2(&errmsg, _("More than one Path!: %s for path: %s\n"),
553             edit_uint64(sql_num_rows(), ed1), path);
554          Jmsg(jcr, M_WARNING, 0, "%s", errmsg);
555       }
556       /* Even if there are multiple paths, take the first one */
557       if (sql_num_rows() >= 1) { 
558          if ((row = sql_fetch_row()) == NULL) {
559             Mmsg1(&errmsg, _("error fetching row: %s\n"), sql_strerror());
560             Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
561             sql_free_result();
562             ar->PathId = 0;
563             ASSERT2(ar->PathId,
564                     "Your Path table is broken. "
565                     "Please, use dbcheck to correct it.");
566             return 0;
567          }
568          ar->PathId = str_to_int64(row[0]);
569          sql_free_result();
570          /* Cache path */
571          if (ar->PathId != cached_path_id) {
572             cached_path_id = ar->PathId;
573             cached_path_len = pnl;
574             pm_strcpy(cached_path, path);
575          }
576          ASSERT(ar->PathId);
577          return 1;
578       }
579       sql_free_result();
580    }
581
582    Mmsg(cmd, "INSERT INTO Path (Path) VALUES ('%s')", esc_name);
583
584    if ((ar->PathId = sql_insert_autokey_record(cmd, NT_("Path"))) == 0) {
585       Mmsg2(&errmsg, _("Create db Path record %s failed. ERR=%s\n"),
586          cmd, sql_strerror());
587       Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
588       ar->PathId = 0;
589       stat = 0;
590    } else {
591       stat = 1;
592    }
593
594    /* Cache path */
595    if (stat && ar->PathId != cached_path_id) {
596       cached_path_id = ar->PathId;
597       cached_path_len = pnl;
598       pm_strcpy(cached_path, path);
599    }
600    return stat;
601 }
602
603 /**
604  * Create a Unique record for the counter -- no duplicates
605  * Returns: 0 on failure
606  *          1 on success with counter filled in
607  */
608 int BDB::bdb_create_counter_record(JCR *jcr, COUNTER_DBR *cr)
609 {
610    char esc[MAX_ESCAPE_NAME_LENGTH];
611    COUNTER_DBR mcr;
612    int stat;
613
614    bdb_lock();
615    memset(&mcr, 0, sizeof(mcr));
616    bstrncpy(mcr.Counter, cr->Counter, sizeof(mcr.Counter));
617    if (bdb_get_counter_record(jcr, &mcr)) {
618       memcpy(cr, &mcr, sizeof(COUNTER_DBR));
619       bdb_unlock();
620       return 1;
621    }
622    bdb_escape_string(jcr, esc, cr->Counter, strlen(cr->Counter));
623  
624    /* Must create it */
625    Mmsg(cmd, insert_counter_values[bdb_get_type_index()],
626         esc, cr->MinValue, cr->MaxValue, cr->CurrentValue,
627         cr->WrapCounter);
628
629    if (!InsertDB(jcr, cmd)) {
630       Mmsg2(&errmsg, _("Create DB Counters record %s failed. ERR=%s\n"),
631             cmd, sql_strerror());
632       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
633       stat = 0;
634    } else {
635       stat = 1;
636    }
637    bdb_unlock();
638    return stat;
639 }
640
641 /**
642  * Create a FileSet record. This record is unique in the
643  *  name and the MD5 signature of the include/exclude sets.
644  *  Returns: 0 on failure
645  *           1 on success with FileSetId in record
646  */
647 bool BDB::bdb_create_fileset_record(JCR *jcr, FILESET_DBR *fsr)
648 {
649    SQL_ROW row;
650    bool stat;
651    struct tm tm;
652    char esc_fs[MAX_ESCAPE_NAME_LENGTH];
653    char esc_md5[MAX_ESCAPE_NAME_LENGTH];
654
655    /* TODO: Escape FileSet and MD5 */
656    bdb_lock();
657    fsr->created = false;
658    bdb_escape_string(jcr, esc_fs, fsr->FileSet, strlen(fsr->FileSet));
659    bdb_escape_string(jcr, esc_md5, fsr->MD5, strlen(fsr->MD5));
660    Mmsg(cmd, "SELECT FileSetId,CreateTime FROM FileSet WHERE "
661                   "FileSet='%s' AND MD5='%s'", esc_fs, esc_md5);
662
663    fsr->FileSetId = 0;
664    if (QueryDB(jcr, cmd)) {
665       if (sql_num_rows() > 1) { 
666          Mmsg1(&errmsg, _("More than one FileSet!: %d\n"), sql_num_rows());
667          Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
668       }
669       if (sql_num_rows() >= 1) { 
670          if ((row = sql_fetch_row()) == NULL) {
671             Mmsg1(&errmsg, _("error fetching FileSet row: ERR=%s\n"), sql_strerror());
672             Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
673             sql_free_result();
674             bdb_unlock();
675             return false;
676          }
677          fsr->FileSetId = str_to_int64(row[0]);
678          if (row[1] == NULL) {
679             fsr->cCreateTime[0] = 0;
680          } else {
681             bstrncpy(fsr->cCreateTime, row[1], sizeof(fsr->cCreateTime));
682          }
683          sql_free_result();
684          bdb_unlock();
685          return true;
686       }
687       sql_free_result();
688    }
689
690    if (fsr->CreateTime == 0 && fsr->cCreateTime[0] == 0) {
691       fsr->CreateTime = time(NULL);
692    }
693    (void)localtime_r(&fsr->CreateTime, &tm);
694    strftime(fsr->cCreateTime, sizeof(fsr->cCreateTime), "%Y-%m-%d %H:%M:%S", &tm);
695
696    /* Must create it */
697       Mmsg(cmd, "INSERT INTO FileSet (FileSet,MD5,CreateTime) "
698 "VALUES ('%s','%s','%s')", esc_fs, esc_md5, fsr->cCreateTime);
699
700    if ((fsr->FileSetId = sql_insert_autokey_record(cmd, NT_("FileSet"))) == 0) {
701       Mmsg2(&errmsg, _("Create DB FileSet record %s failed. ERR=%s\n"),
702             cmd, sql_strerror());
703       Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
704       stat = false;
705    } else {
706       fsr->created = true;
707       stat = true;
708    }
709
710    bdb_unlock();
711    return stat;
712 }
713
714
715 /**
716  *  struct stat
717  *  {
718  *      dev_t         st_dev;       * device *
719  *      ino_t         st_ino;       * inode *
720  *      mode_t        st_mode;      * protection *
721  *      nlink_t       st_nlink;     * number of hard links *
722  *      uid_t         st_uid;       * user ID of owner *
723  *      gid_t         st_gid;       * group ID of owner *
724  *      dev_t         st_rdev;      * device type (if inode device) *
725  *      off_t         st_size;      * total size, in bytes *
726  *      unsigned long st_blksize;   * blocksize for filesystem I/O *
727  *      unsigned long st_blocks;    * number of blocks allocated *
728  *      time_t        st_atime;     * time of last access *
729  *      time_t        st_mtime;     * time of last modification *
730  *      time_t        st_ctime;     * time of last inode change *
731  *  };
732  */
733
734 /* For maintenance, we can put batch mode in hold */
735 static bool batch_mode_enabled = true;
736
737 void bdb_disable_batch_insert(bool enabled)
738 {
739    batch_mode_enabled = enabled;
740 }
741
742 /*
743  * All sql_batch_xx functions are used to do bulk batch 
744  *  insert in File/Filename/Path tables.
745  *
746  *  To sum up :
747  *   - bulk load a temp table
748  *   - insert missing filenames into filename with a single query (lock filenames 
749  *   - table before that to avoid possible duplicate inserts with concurrent update)
750  *   - insert missing paths into path with another single query
751  *   - then insert the join between the temp, filename and path tables into file.
752  */
753
754 /*
755  *  Returns true if OK
756  *          false if failed
757  */
758 bool bdb_write_batch_file_records(JCR *jcr)
759 {
760    bool retval = false; 
761    int JobStatus = jcr->JobStatus;
762
763    if (!jcr->batch_started) {         /* no files to backup ? */
764       Dmsg0(50,"db_write_batch_file_records: no files\n");
765       return true;
766    }
767
768    if (job_canceled(jcr)) {
769       goto bail_out; 
770    }
771
772    jcr->JobStatus = JS_AttrInserting;
773
774    /* Check if batch mode is on hold */
775    while (!batch_mode_enabled) {
776       Dmsg0(50, "batch mode is on hold\n");
777       bmicrosleep(10, 0);
778
779       if (job_canceled(jcr)) {
780          goto bail_out; 
781       }
782    }
783
784    Dmsg1(50,"db_write_batch_file_records changes=%u\n",jcr->db_batch->changes);
785
786    if (!jcr->db_batch->sql_batch_end(jcr, NULL)) {
787       Jmsg1(jcr, M_FATAL, 0, "Batch end %s\n", jcr->db_batch->errmsg);
788       goto bail_out; 
789    }
790    if (job_canceled(jcr)) {
791       goto bail_out; 
792    }
793
794    /* We have to lock tables */
795    if (!jcr->db_batch->bdb_sql_query(batch_lock_path_query[jcr->db_batch->bdb_get_type_index()], NULL, NULL)) {
796       Jmsg1(jcr, M_FATAL, 0, "Lock Path table %s\n", jcr->db_batch->errmsg);
797       goto bail_out; 
798    }
799
800    if (!jcr->db_batch->bdb_sql_query(batch_fill_path_query[jcr->db_batch->bdb_get_type_index()], NULL, NULL)) {
801       Jmsg1(jcr, M_FATAL, 0, "Fill Path table %s\n",jcr->db_batch->errmsg);
802       jcr->db_batch->bdb_sql_query(batch_unlock_tables_query[jcr->db_batch->bdb_get_type_index()], NULL, NULL);
803       goto bail_out; 
804    }
805
806    if (!jcr->db_batch->bdb_sql_query(batch_unlock_tables_query[jcr->db_batch->bdb_get_type_index()], NULL, NULL)) {
807       Jmsg1(jcr, M_FATAL, 0, "Unlock Path table %s\n", jcr->db_batch->errmsg);
808       goto bail_out; 
809    }
810
811    /* We have to lock tables */
812    if (!db_sql_query(jcr->db_batch, batch_lock_filename_query[db_get_type_index(jcr->db_batch)], NULL, NULL)) { 
813       Jmsg1(jcr, M_FATAL, 0, "Lock Filename table %s\n", jcr->db_batch->errmsg);
814       goto bail_out; 
815    }
816    
817    if (!db_sql_query(jcr->db_batch, batch_fill_filename_query[db_get_type_index(jcr->db_batch)], NULL, NULL)) { 
818       Jmsg1(jcr,M_FATAL,0,"Fill Filename table %s\n",jcr->db_batch->errmsg);
819       db_sql_query(jcr->db_batch, batch_unlock_tables_query[db_get_type_index(jcr->db_batch)], NULL, NULL); 
820       goto bail_out; 
821    }
822
823    if (!db_sql_query(jcr->db_batch, batch_unlock_tables_query[db_get_type_index(jcr->db_batch)], NULL, NULL)) { 
824       Jmsg1(jcr, M_FATAL, 0, "Unlock Filename table %s\n", jcr->db_batch->errmsg);
825       goto bail_out; 
826    }
827    
828    if (!db_sql_query(jcr->db_batch, 
829 "INSERT INTO File (FileIndex, JobId, PathId, FilenameId, LStat, MD5, DeltaSeq) "
830     "SELECT batch.FileIndex, batch.JobId, Path.PathId, "
831            "Filename.FilenameId,batch.LStat, batch.MD5, batch.DeltaSeq "
832       "FROM batch "
833       "JOIN Path ON (batch.Path = Path.Path) "
834       "JOIN Filename ON (batch.Name = Filename.Name)",
835          NULL, NULL))
836    {
837       Jmsg1(jcr, M_FATAL, 0, "Fill File table %s\n", jcr->db_batch->errmsg);
838       goto bail_out; 
839    }
840
841    jcr->JobStatus = JobStatus;    /* reset entry status */
842    retval = true; 
843  
844 bail_out: 
845    jcr->db_batch->bdb_sql_query("DROP TABLE batch", NULL,NULL);
846    jcr->batch_started = false;
847
848    return retval; 
849 }
850
851 /*
852  * Create File record in BDB
853  *
854  *  In order to reduce database size, we store the File attributes,
855  *  the FileName, and the Path separately.  In principle, there
856  *  is a single FileName record and a single Path record, no matter
857  *  how many times it occurs.  This is this subroutine, we separate
858  *  the file and the path and fill temporary tables with this three records.
859  *
860  *  Note: all routines that call this expect to be able to call
861  *    db_strerror(mdb) to get the error message, so the error message
862  *    MUST be edited into errmsg before returning an error status.
863  */
864 bool BDB::bdb_create_batch_file_attributes_record(JCR *jcr, ATTR_DBR *ar)
865 {
866    ASSERT(ar->FileType != FT_BASE);
867    Dmsg1(dbglevel, "Fname=%s\n", ar->fname);
868    Dmsg0(dbglevel, "put_file_into_catalog\n");
869
870    if (jcr->batch_started && jcr->db_batch->changes > 500000) {
871       bdb_write_batch_file_records(jcr);
872       jcr->db_batch->changes = 0;
873    }
874
875    /* Open the dedicated connexion */
876    if (!jcr->batch_started) {
877       if (!bdb_open_batch_connexion(jcr)) {
878          return false;     /* error already printed */
879       }
880       if (!jcr->db_batch->sql_batch_start(jcr)) {
881          Mmsg1(&errmsg,
882               "Can't start batch mode: ERR=%s", jcr->db_batch->bdb_strerror());
883          Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
884          return false;
885       }
886       jcr->batch_started = true;
887    }
888
889    split_path_and_file(jcr, jcr->db_batch, ar->fname);
890
891    return jcr->db_batch->sql_batch_insert(jcr, ar);
892 }
893
894 /**
895  * Create File record in BDB
896  *
897  *  In order to reduce database size, we store the File attributes,
898  *  the FileName, and the Path separately.  In principle, there
899  *  is a single FileName record and a single Path record, no matter
900  *  how many times it occurs.  This is this subroutine, we separate
901  *  the file and the path and create three database records.
902  */
903 bool BDB::bdb_create_file_attributes_record(JCR *jcr, ATTR_DBR *ar)
904 {
905    bdb_lock();
906    Dmsg1(dbglevel, "Fname=%s\n", ar->fname);
907    Dmsg0(dbglevel, "put_file_into_catalog\n");
908
909    split_path_and_file(jcr, this, ar->fname);
910
911    if (!bdb_create_filename_record(jcr, ar)) {
912       goto bail_out;
913    }
914    Dmsg1(dbglevel, "bdb_create_filename_record: %s\n", esc_name);
915
916
917    if (!bdb_create_path_record(jcr, ar)) {
918       goto bail_out;
919    }
920    Dmsg1(dbglevel, "bdb_create_path_record: %s\n", esc_name);
921
922    /* Now create master File record */
923    if (!bdb_create_file_record(jcr, ar)) {
924       goto bail_out; 
925    }
926    Dmsg0(dbglevel, "db_create_file_record OK\n");
927
928    Dmsg3(dbglevel, "CreateAttributes Path=%s File=%s FilenameId=%d\n", path, fname, ar->FilenameId);
929    bdb_unlock();
930    return true;
931
932 bail_out: 
933    bdb_unlock();
934    return false;
935 }
936 /**
937  * This is the master File entry containing the attributes.
938  *  The filename and path records have already been created.
939  */
940 int BDB::bdb_create_file_record(JCR *jcr, ATTR_DBR *ar)
941 {
942    int stat;
943    static const char *no_digest = "0";
944    const char *digest;
945
946    ASSERT(ar->JobId);
947    ASSERT(ar->PathId);
948    ASSERT(ar->FilenameId);
949
950    if (ar->Digest == NULL || ar->Digest[0] == 0) {
951       digest = no_digest;
952    } else {
953       digest = ar->Digest;
954    }
955
956    /* Must create it */
957    Mmsg(cmd,
958         "INSERT INTO File (FileIndex,JobId,PathId,FilenameId,"
959         "LStat,MD5,DeltaSeq) VALUES (%u,%u,%u,%u,'%s','%s',%u)",
960         ar->FileIndex, ar->JobId, ar->PathId, ar->FilenameId,
961         ar->attr, digest, ar->DeltaSeq);
962
963    if ((ar->FileId = sql_insert_autokey_record(cmd, NT_("File"))) == 0) {
964       Mmsg2(&errmsg, _("Create db File record %s failed. ERR=%s"),
965          cmd, sql_strerror());
966       Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
967       stat = 0;
968    } else {
969       stat = 1;
970    }
971    return stat;
972 }
973
974 /** Create a Unique record for the filename -- no duplicates */
975 int BDB::bdb_create_filename_record(JCR *jcr, ATTR_DBR *ar)
976 {
977    SQL_ROW row;
978
979    errmsg[0] = 0;
980    esc_name = check_pool_memory_size(esc_name, 2*fnl+2);
981    bdb_escape_string(jcr, esc_name, fname, fnl);
982    
983    Mmsg(cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
984
985    if (QueryDB(jcr, cmd)) {
986       if (sql_num_rows() > 1) {
987          char ed1[30];
988          Mmsg2(&errmsg, _("More than one Filename! %s for file: %s\n"),
989             edit_uint64(sql_num_rows(), ed1), fname);
990          Jmsg(jcr, M_WARNING, 0, "%s", errmsg);
991       }
992       if (sql_num_rows() >= 1) {
993          if ((row = sql_fetch_row()) == NULL) {
994             Mmsg2(&errmsg, _("Error fetching row for file=%s: ERR=%s\n"),
995                 fname, sql_strerror());
996             Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
997             ar->FilenameId = 0;
998          } else {
999             ar->FilenameId = str_to_int64(row[0]);
1000          }
1001          sql_free_result();
1002          return ar->FilenameId > 0;
1003       }
1004       sql_free_result();
1005    }
1006
1007    Mmsg(cmd, "INSERT INTO Filename (Name) VALUES ('%s')", esc_name);
1008
1009    ar->FilenameId = sql_insert_autokey_record(cmd, NT_("Filename"));
1010    if (ar->FilenameId == 0) { 
1011       Mmsg2(&errmsg, _("Create db Filename record %s failed. ERR=%s\n"),
1012             cmd, sql_strerror());
1013       Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
1014    }
1015    return ar->FilenameId > 0;
1016 }
1017
1018 /* 
1019  * Create file attributes record, or base file attributes record
1020  */
1021 bool BDB::bdb_create_attributes_record(JCR *jcr, ATTR_DBR *ar)
1022 {
1023    bool ret;
1024
1025    errmsg[0] = 0;
1026    /*
1027     * Make sure we have an acceptable attributes record.
1028     */
1029    if (!(ar->Stream == STREAM_UNIX_ATTRIBUTES ||
1030          ar->Stream == STREAM_UNIX_ATTRIBUTES_EX)) {
1031       Mmsg1(&errmsg, _("Attempt to put non-attributes into catalog. Stream=%d\n"),
1032          ar->Stream);
1033       Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
1034       return false;
1035    }
1036
1037    if (ar->FileType != FT_BASE) {
1038       if (batch_insert_available()) {
1039          ret = bdb_create_batch_file_attributes_record(jcr, ar);
1040          /* Error message already printed */
1041       } else { 
1042          ret = bdb_create_file_attributes_record(jcr, ar);
1043       } 
1044    } else if (jcr->HasBase) {
1045       ret = bdb_create_base_file_attributes_record(jcr, ar);
1046    } else {
1047       Mmsg0(&errmsg, _("Cannot Copy/Migrate job using BaseJob.\n"));
1048       Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
1049       ret = true;               /* in copy/migration what do we do ? */
1050    }
1051
1052    return ret;
1053 }
1054
1055 /**
1056  * Create Base File record in BDB
1057  *
1058  */
1059 bool BDB::bdb_create_base_file_attributes_record(JCR *jcr, ATTR_DBR *ar)
1060 {
1061    bool ret;
1062
1063    Dmsg1(dbglevel, "create_base_file Fname=%s\n", ar->fname);
1064    Dmsg0(dbglevel, "put_base_file_into_catalog\n");
1065
1066    bdb_lock();
1067    split_path_and_file(jcr, this, ar->fname);
1068
1069    esc_name = check_pool_memory_size(esc_name, fnl*2+1);
1070    bdb_escape_string(jcr, esc_name, fname, fnl);
1071
1072    esc_path = check_pool_memory_size(esc_path, pnl*2+1);
1073    bdb_escape_string(jcr, esc_path, path, pnl);
1074
1075    Mmsg(cmd, "INSERT INTO basefile%lld (Path, Name) VALUES ('%s','%s')",
1076         (uint64_t)jcr->JobId, esc_path, esc_name);
1077
1078    ret = InsertDB(jcr, cmd);
1079    bdb_unlock();
1080
1081    return ret;
1082 }
1083
1084 /**
1085  * Cleanup the base file temporary tables
1086  */
1087 static void db_cleanup_base_file(JCR *jcr, BDB *mdb)
1088 {
1089    POOL_MEM buf(PM_MESSAGE);
1090    Mmsg(buf, "DROP TABLE new_basefile%lld", (uint64_t) jcr->JobId);
1091    mdb->bdb_sql_query(buf.c_str(), NULL, NULL);
1092
1093    Mmsg(buf, "DROP TABLE basefile%lld", (uint64_t) jcr->JobId);
1094    mdb->bdb_sql_query(buf.c_str(), NULL, NULL);
1095 }
1096
1097 /**
1098  * Put all base file seen in the backup to the BaseFile table
1099  * and cleanup temporary tables
1100  */
1101 bool BDB::bdb_commit_base_file_attributes_record(JCR *jcr)
1102 {
1103    bool ret;
1104    char ed1[50];
1105
1106    bdb_lock();
1107
1108    Mmsg(cmd,
1109   "INSERT INTO BaseFiles (BaseJobId, JobId, FileId, FileIndex) "
1110    "SELECT B.JobId AS BaseJobId, %s AS JobId, "
1111           "B.FileId, B.FileIndex "
1112      "FROM basefile%s AS A, new_basefile%s AS B "
1113     "WHERE A.Path = B.Path "
1114       "AND A.Name = B.Name "
1115     "ORDER BY B.FileId",
1116         edit_uint64(jcr->JobId, ed1), ed1, ed1);
1117    ret = bdb_sql_query(cmd, NULL, NULL);
1118    /*
1119     * Display error now, because the subsequent cleanup destroys the
1120     *  error message from the above query.
1121     */
1122    if (!ret) {
1123       Jmsg1(jcr, M_FATAL, 0, "%s", jcr->db->bdb_strerror());
1124    }
1125    jcr->nb_base_files_used = sql_affected_rows();
1126    db_cleanup_base_file(jcr, this);
1127
1128    bdb_unlock();
1129    return ret;
1130 }
1131
1132 /**
1133  * Find the last "accurate" backup state with Base jobs
1134  * 1) Get all files with jobid in list (F subquery)
1135  * 2) Take only the last version of each file (Temp subquery) => accurate list is ok
1136  * 3) Put the result in a temporary table for the end of job
1137  *
1138  */
1139 bool BDB::bdb_create_base_file_list(JCR *jcr, char *jobids)
1140 {
1141    POOL_MEM buf;
1142    bool ret = false;
1143
1144    bdb_lock();
1145
1146    if (!*jobids) {
1147       Mmsg(errmsg, _("ERR=JobIds are empty\n"));
1148       goto bail_out; 
1149    }
1150
1151    Mmsg(cmd, create_temp_basefile[bdb_get_type_index()], (uint64_t) jcr->JobId);
1152    if (!bdb_sql_query(cmd, NULL, NULL)) {
1153       goto bail_out; 
1154    }
1155    Mmsg(buf, select_recent_version[bdb_get_type_index()], jobids, jobids);
1156    Mmsg(cmd, create_temp_new_basefile[bdb_get_type_index()], (uint64_t)jcr->JobId, buf.c_str());
1157
1158    ret = bdb_sql_query(cmd, NULL, NULL);
1159 bail_out: 
1160    bdb_unlock();
1161    return ret;
1162 }
1163
1164 /**
1165  * Create Restore Object record in BDB
1166  *
1167  */
1168 bool BDB::bdb_create_restore_object_record(JCR *jcr, ROBJECT_DBR *ro)
1169 {
1170    bool stat;
1171    int plug_name_len;
1172    POOLMEM *esc_plug_name = get_pool_memory(PM_MESSAGE);
1173
1174    bdb_lock();
1175
1176    Dmsg1(dbglevel, "Oname=%s\n", ro->object_name);
1177    Dmsg0(dbglevel, "put_object_into_catalog\n");
1178
1179    fnl = strlen(ro->object_name);
1180    esc_name = check_pool_memory_size(esc_name, fnl*2+1);
1181    bdb_escape_string(jcr, esc_name, ro->object_name, fnl);
1182
1183    bdb_escape_object(jcr, ro->object, ro->object_len);
1184
1185    plug_name_len = strlen(ro->plugin_name);
1186    esc_plug_name = check_pool_memory_size(esc_plug_name, plug_name_len*2+1);
1187    bdb_escape_string(jcr, esc_plug_name, ro->plugin_name, plug_name_len);
1188
1189    Mmsg(cmd,
1190         "INSERT INTO RestoreObject (ObjectName,PluginName,RestoreObject,"
1191         "ObjectLength,ObjectFullLength,ObjectIndex,ObjectType,"
1192         "ObjectCompression,FileIndex,JobId) "
1193         "VALUES ('%s','%s','%s',%d,%d,%d,%d,%d,%d,%u)",
1194         esc_name, esc_plug_name, esc_obj,
1195         ro->object_len, ro->object_full_len, ro->object_index,
1196         ro->FileType, ro->object_compression, ro->FileIndex, ro->JobId);
1197
1198    ro->RestoreObjectId = sql_insert_autokey_record(cmd, NT_("RestoreObject"));
1199    if (ro->RestoreObjectId == 0) {
1200       Mmsg2(&errmsg, _("Create db Object record %s failed. ERR=%s"),
1201          cmd, sql_strerror());
1202       Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
1203       stat = false;
1204    } else {
1205       stat = true;
1206    }
1207    bdb_unlock();
1208    free_pool_memory(esc_plug_name);
1209    return stat;
1210 }
1211
1212 bool BDB::bdb_create_snapshot_record(JCR *jcr, SNAPSHOT_DBR *snap)
1213 {
1214    bool status = false;
1215    char esc_name[MAX_ESCAPE_NAME_LENGTH];
1216    POOLMEM *esc_vol = get_pool_memory(PM_MESSAGE);
1217    POOLMEM *esc_dev = get_pool_memory(PM_MESSAGE);
1218    POOLMEM *esc_type = get_pool_memory(PM_MESSAGE);
1219    POOLMEM *esc_client = get_pool_memory(PM_MESSAGE);
1220    POOLMEM *esc_fs = get_pool_memory(PM_MESSAGE);
1221    char esc_comment[MAX_ESCAPE_NAME_LENGTH];
1222    char dt[MAX_TIME_LENGTH], ed1[50], ed2[50];
1223    time_t stime;
1224    struct tm tm;
1225
1226    bdb_lock();
1227
1228    esc_vol = check_pool_memory_size(esc_vol, strlen(snap->Volume) * 2 + 1);
1229    bdb_escape_string(jcr, esc_vol, snap->Volume, strlen(snap->Volume));
1230
1231    esc_dev = check_pool_memory_size(esc_dev, strlen(snap->Device) * 2 + 1);
1232    bdb_escape_string(jcr, esc_dev, snap->Device, strlen(snap->Device));
1233
1234    esc_type = check_pool_memory_size(esc_type, strlen(snap->Type) * 2 + 1);
1235    bdb_escape_string(jcr, esc_type, snap->Type, strlen(snap->Type));
1236
1237    bdb_escape_string(jcr, esc_comment, snap->Comment, strlen(snap->Comment));
1238
1239    if (*snap->Client) {
1240       bdb_escape_string(jcr, esc_name, snap->Client, strlen(snap->Client));
1241       Mmsg(esc_client, "(SELECT ClientId FROM Client WHERE Name='%s')", esc_name);
1242
1243    } else {
1244       Mmsg(esc_client, "%d", snap->ClientId);
1245    }
1246
1247    if (*snap->FileSet) {
1248       bdb_escape_string(jcr, esc_name, snap->FileSet, strlen(snap->FileSet));
1249       Mmsg(esc_fs, "(SELECT FileSetId FROM FileSet WHERE FileSet='%s' ORDER BY CreateTime DESC LIMIT 1)", esc_name);
1250
1251    } else {
1252       Mmsg(esc_fs, "%d", snap->FileSetId);
1253    }
1254
1255    bdb_escape_string(jcr, esc_name, snap->Name, strlen(snap->Name));
1256
1257    stime = snap->CreateTDate;
1258    (void)localtime_r(&stime, &tm);
1259    strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
1260
1261    Mmsg(cmd, "INSERT INTO Snapshot "
1262         "(Name, JobId, CreateTDate, CreateDate, ClientId, FileSetId, Volume, Device, Type, Retention, Comment) "
1263  "VALUES ('%s', %s, %d, '%s', %s, %s, '%s', '%s', '%s', %s, '%s')",
1264         esc_name, edit_uint64(snap->JobId, ed2), stime, dt, esc_client, esc_fs, esc_vol,
1265         esc_dev, esc_type, edit_int64(snap->Retention, ed1), esc_comment);
1266
1267    if (bdb_sql_query(cmd, NULL, NULL)) {
1268       snap->SnapshotId = sql_insert_autokey_record(cmd, NT_("Snapshot"));
1269       status = true;
1270    }
1271
1272    bdb_unlock();
1273
1274    free_pool_memory(esc_vol);
1275    free_pool_memory(esc_dev);
1276    free_pool_memory(esc_type);
1277    free_pool_memory(esc_client);
1278    free_pool_memory(esc_fs);
1279
1280    return status;
1281 }
1282
1283 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */