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