2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Bacula Catalog Database Create record interface routines
22 * Written by Kern Sibbald, March 2000
27 static const int dbglevel = 160;
29 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
33 /* -----------------------------------------------------------------------
35 * Generic Routines (or almost generic)
37 * -----------------------------------------------------------------------
40 /** Create a new record for the Job
41 * Returns: false on failure
44 bool BDB::bdb_create_job_record(JCR *jcr, JOB_DBR *jr)
47 char dt[MAX_TIME_LENGTH];
54 char esc_job[MAX_ESCAPE_NAME_LENGTH];
55 char esc_name[MAX_ESCAPE_NAME_LENGTH];
59 stime = jr->SchedTime;
62 (void)localtime_r(&stime, &tm);
63 strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
64 JobTDate = (utime_t)stime;
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);
70 bdb_escape_string(jcr, esc_job, jr->Job, strlen(jr->Job));
71 bdb_escape_string(jcr, esc_name, jr->Name, strlen(jr->Name));
75 "INSERT INTO Job (Job,Name,Type,Level,JobStatus,SchedTime,JobTDate,"
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());
82 if ((jr->JobId = sql_insert_autokey_record(cmd, NT_("Job"))) == 0) {
83 Mmsg2(&errmsg, _("Create DB Job record %s failed. ERR=%s\n"),
94 /** Create a JobMedia record for medium used this job
95 * Returns: false on failure
98 bool BDB::bdb_create_jobmedia_record(JCR *jcr, JOBMEDIA_DBR *jm)
102 char ed1[50], ed2[50];
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);
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);
125 if (!InsertDB(jcr, cmd)) {
126 Mmsg2(&errmsg, _("Create JobMedia record %s failed: ERR=%s\n"), cmd,
130 /* Worked, now update the Media record with the EndFile and EndBlock */
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,
141 Dmsg0(300, "Return from JobMedia\n");
145 /** Create Unique Pool record
146 * Returns: false on failure
149 bool BDB::bdb_create_pool_record(JCR *jcr, POOL_DBR *pr)
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];
156 Dmsg0(200, "In create pool\n");
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);
163 if (QueryDB(jcr, cmd)) {
164 if (sql_num_rows() > 0) {
165 Mmsg1(&errmsg, _("pool record %s already exists\n"), pr->Name);
168 Dmsg1(200, "%s", errmsg); /* pool already exists */
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)",
182 pr->NumVols, pr->MaxVols,
183 pr->UseOnce, pr->UseCatalog,
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),
194 edit_uint64(pr->CacheRetention, ed6)
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());
209 * Create Unique Device record
210 * Returns: false on failure
213 bool BDB::bdb_create_device_record(JCR *jcr, DEVICE_DBR *dr)
216 char ed1[30], ed2[30];
217 char esc[MAX_ESCAPE_NAME_LENGTH];
219 Dmsg0(200, "In create Device\n");
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);
225 if (QueryDB(jcr, cmd)) {
226 if (sql_num_rows() > 0) {
227 Mmsg1(&errmsg, _("Device record %s already exists\n"), dr->Name);
237 "INSERT INTO Device (Name,MediaTypeId,StorageId) VALUES ('%s',%s,%s)",
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());
256 * Create a Unique record for Storage -- no duplicates
257 * Returns: false on failure
258 * true on success with id in sr->StorageId
260 bool BDB::bdb_create_storage_record(JCR *jcr, STORAGE_DBR *sr)
264 char esc[MAX_ESCAPE_NAME_LENGTH];
267 bdb_escape_string(jcr, esc, sr->Name, strlen(sr->Name));
268 Mmsg(cmd, "SELECT StorageId,AutoChanger FROM Storage WHERE Name='%s'",esc);
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);
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);
287 sr->StorageId = str_to_int64(row[0]);
288 sr->AutoChanger = atoi(row[1]); /* bool */
297 Mmsg(cmd, "INSERT INTO Storage (Name,AutoChanger)"
298 " VALUES ('%s',%d)", esc, sr->AutoChanger);
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);
315 * Create Unique MediaType record
316 * Returns: false on failure
319 bool BDB::bdb_create_mediatype_record(JCR *jcr, MEDIATYPE_DBR *mr)
322 char esc[MAX_ESCAPE_NAME_LENGTH];
324 Dmsg0(200, "In create mediatype\n");
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);
330 if (QueryDB(jcr, cmd)) {
331 if (sql_num_rows() > 0) {
332 Mmsg1(&errmsg, _("mediatype record %s already exists\n"), mr->MediaType);
342 "INSERT INTO MediaType (MediaType,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());
360 * Create Media record. VolumeName and non-zero Slot must be unique
362 * Returns: 0 on failure
365 int BDB::bdb_create_media_record(JCR *jcr, MEDIA_DBR *mr)
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];
371 char esc_name[MAX_ESCAPE_NAME_LENGTH];
372 char esc_mtype[MAX_ESCAPE_NAME_LENGTH];
373 char esc_status[MAX_ESCAPE_NAME_LENGTH];
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));
381 Mmsg(cmd, "SELECT MediaId FROM Media WHERE VolumeName='%s'", esc_name);
382 Dmsg1(500, "selectpool: %s\n", cmd);
384 if (QueryDB(jcr, cmd)) {
385 if (sql_num_rows() > 0) {
386 Mmsg1(&errmsg, _("Volume \"%s\" already exists.\n"), mr->VolumeName);
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)",
405 esc_mtype, mr->PoolId,
406 edit_uint64(mr->MaxVolBytes,ed1),
407 edit_uint64(mr->VolCapacityBytes, ed2),
409 edit_uint64(mr->VolRetention, ed3),
410 edit_uint64(mr->VolUseDuration, ed4),
415 edit_uint64(mr->VolBytes, ed5),
417 edit_int64(mr->VolReadTime, ed6),
418 edit_int64(mr->VolWriteTime, ed7),
422 edit_uint64(mr->LastPartBytes, ed8),
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),
433 edit_uint64(mr->CacheRetention, ed14)
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());
443 if (mr->set_label_date) {
444 char dt[MAX_TIME_LENGTH];
445 if (mr->LabelDate == 0) {
446 mr->LabelDate = time(NULL);
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);
455 * Make sure that if InChanger is non-zero any other identical slot
456 * has InChanger zero.
458 db_make_inchanger_unique(jcr, this, mr);
466 * Create a Unique record for the client -- no duplicates
467 * Returns: 0 on failure
468 * 1 on success with id in cr->ClientId
470 int BDB::bdb_create_client_record(JCR *jcr, CLIENT_DBR *cr)
474 char ed1[50], ed2[50];
475 char esc_name[MAX_ESCAPE_NAME_LENGTH];
476 char esc_uname[MAX_ESCAPE_NAME_LENGTH];
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);
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);
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);
498 cr->ClientId = str_to_int64(row[0]);
500 bstrncpy(cr->Uname, row[1], sizeof(cr->Uname));
502 cr->Uname[0] = 0; /* no name */
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));
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);
531 /** Create a Unique record for the Path -- no duplicates */
532 int BDB::bdb_create_path_record(JCR *jcr, ATTR_DBR *ar)
538 esc_name = check_pool_memory_size(esc_name, 2*pnl+2);
539 bdb_escape_string(jcr, esc_name, path, pnl);
541 if (cached_path_id != 0 && cached_path_len == pnl &&
542 strcmp(cached_path, path) == 0) {
543 ar->PathId = cached_path_id;
547 Mmsg(cmd, "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
549 if (QueryDB(jcr, cmd)) {
550 if (sql_num_rows() > 1) {
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);
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);
564 "Your Path table is broken. "
565 "Please, use dbcheck to correct it.");
568 ar->PathId = str_to_int64(row[0]);
571 if (ar->PathId != cached_path_id) {
572 cached_path_id = ar->PathId;
573 cached_path_len = pnl;
574 pm_strcpy(cached_path, path);
582 Mmsg(cmd, "INSERT INTO Path (Path) VALUES ('%s')", esc_name);
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);
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);
604 * Create a Unique record for the counter -- no duplicates
605 * Returns: 0 on failure
606 * 1 on success with counter filled in
608 int BDB::bdb_create_counter_record(JCR *jcr, COUNTER_DBR *cr)
610 char esc[MAX_ESCAPE_NAME_LENGTH];
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));
622 bdb_escape_string(jcr, esc, cr->Counter, strlen(cr->Counter));
625 Mmsg(cmd, insert_counter_values[bdb_get_type_index()],
626 esc, cr->MinValue, cr->MaxValue, cr->CurrentValue,
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);
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
647 bool BDB::bdb_create_fileset_record(JCR *jcr, FILESET_DBR *fsr)
652 char esc_fs[MAX_ESCAPE_NAME_LENGTH];
653 char esc_md5[MAX_ESCAPE_NAME_LENGTH];
655 /* TODO: Escape FileSet and MD5 */
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);
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);
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);
677 fsr->FileSetId = str_to_int64(row[0]);
678 if (row[1] == NULL) {
679 fsr->cCreateTime[0] = 0;
681 bstrncpy(fsr->cCreateTime, row[1], sizeof(fsr->cCreateTime));
690 if (fsr->CreateTime == 0 && fsr->cCreateTime[0] == 0) {
691 fsr->CreateTime = time(NULL);
693 (void)localtime_r(&fsr->CreateTime, &tm);
694 strftime(fsr->cCreateTime, sizeof(fsr->cCreateTime), "%Y-%m-%d %H:%M:%S", &tm);
697 Mmsg(cmd, "INSERT INTO FileSet (FileSet,MD5,CreateTime) "
698 "VALUES ('%s','%s','%s')", esc_fs, esc_md5, fsr->cCreateTime);
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);
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 *
734 /* For maintenance, we can put batch mode in hold */
735 static bool batch_mode_enabled = true;
737 void bdb_disable_batch_insert(bool enabled)
739 batch_mode_enabled = enabled;
743 * All sql_batch_xx functions are used to do bulk batch
744 * insert in File/Filename/Path tables.
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.
758 bool bdb_write_batch_file_records(JCR *jcr)
761 int JobStatus = jcr->JobStatus;
763 if (!jcr->batch_started) { /* no files to backup ? */
764 Dmsg0(50,"db_write_batch_file_records: no files\n");
768 if (job_canceled(jcr)) {
772 jcr->JobStatus = JS_AttrInserting;
774 /* Check if batch mode is on hold */
775 while (!batch_mode_enabled) {
776 Dmsg0(50, "batch mode is on hold\n");
779 if (job_canceled(jcr)) {
784 Dmsg1(50,"db_write_batch_file_records changes=%u\n",jcr->db_batch->changes);
786 if (!jcr->db_batch->sql_batch_end(jcr, NULL)) {
787 Jmsg1(jcr, M_FATAL, 0, "Batch end %s\n", jcr->db_batch->errmsg);
790 if (job_canceled(jcr)) {
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);
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);
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);
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);
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);
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);
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 "
833 "JOIN Path ON (batch.Path = Path.Path) "
834 "JOIN Filename ON (batch.Name = Filename.Name)",
837 Jmsg1(jcr, M_FATAL, 0, "Fill File table %s\n", jcr->db_batch->errmsg);
841 jcr->JobStatus = JobStatus; /* reset entry status */
845 jcr->db_batch->bdb_sql_query("DROP TABLE batch", NULL,NULL);
846 jcr->batch_started = false;
852 * Create File record in BDB
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.
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.
864 bool BDB::bdb_create_batch_file_attributes_record(JCR *jcr, ATTR_DBR *ar)
866 ASSERT(ar->FileType != FT_BASE);
867 Dmsg1(dbglevel, "Fname=%s\n", ar->fname);
868 Dmsg0(dbglevel, "put_file_into_catalog\n");
870 if (jcr->batch_started && jcr->db_batch->changes > 500000) {
871 bdb_write_batch_file_records(jcr);
872 jcr->db_batch->changes = 0;
875 /* Open the dedicated connexion */
876 if (!jcr->batch_started) {
877 if (!bdb_open_batch_connexion(jcr)) {
878 return false; /* error already printed */
880 if (!jcr->db_batch->sql_batch_start(jcr)) {
882 "Can't start batch mode: ERR=%s", jcr->db_batch->bdb_strerror());
883 Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
886 jcr->batch_started = true;
889 split_path_and_file(jcr, jcr->db_batch, ar->fname);
891 return jcr->db_batch->sql_batch_insert(jcr, ar);
895 * Create File record in BDB
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.
903 bool BDB::bdb_create_file_attributes_record(JCR *jcr, ATTR_DBR *ar)
906 Dmsg1(dbglevel, "Fname=%s\n", ar->fname);
907 Dmsg0(dbglevel, "put_file_into_catalog\n");
909 split_path_and_file(jcr, this, ar->fname);
911 if (!bdb_create_filename_record(jcr, ar)) {
914 Dmsg1(dbglevel, "bdb_create_filename_record: %s\n", esc_name);
917 if (!bdb_create_path_record(jcr, ar)) {
920 Dmsg1(dbglevel, "bdb_create_path_record: %s\n", esc_name);
922 /* Now create master File record */
923 if (!bdb_create_file_record(jcr, ar)) {
926 Dmsg0(dbglevel, "db_create_file_record OK\n");
928 Dmsg3(dbglevel, "CreateAttributes Path=%s File=%s FilenameId=%d\n", path, fname, ar->FilenameId);
937 * This is the master File entry containing the attributes.
938 * The filename and path records have already been created.
940 int BDB::bdb_create_file_record(JCR *jcr, ATTR_DBR *ar)
943 static const char *no_digest = "0";
948 ASSERT(ar->FilenameId);
950 if (ar->Digest == NULL || ar->Digest[0] == 0) {
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);
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);
974 /** Create a Unique record for the filename -- no duplicates */
975 int BDB::bdb_create_filename_record(JCR *jcr, ATTR_DBR *ar)
980 esc_name = check_pool_memory_size(esc_name, 2*fnl+2);
981 bdb_escape_string(jcr, esc_name, fname, fnl);
983 Mmsg(cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
985 if (QueryDB(jcr, cmd)) {
986 if (sql_num_rows() > 1) {
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);
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);
999 ar->FilenameId = str_to_int64(row[0]);
1002 return ar->FilenameId > 0;
1007 Mmsg(cmd, "INSERT INTO Filename (Name) VALUES ('%s')", esc_name);
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);
1015 return ar->FilenameId > 0;
1019 * Create file attributes record, or base file attributes record
1021 bool BDB::bdb_create_attributes_record(JCR *jcr, ATTR_DBR *ar)
1027 * Make sure we have an acceptable attributes record.
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"),
1033 Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
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 */
1042 ret = bdb_create_file_attributes_record(jcr, ar);
1044 } else if (jcr->HasBase) {
1045 ret = bdb_create_base_file_attributes_record(jcr, ar);
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 ? */
1056 * Create Base File record in BDB
1059 bool BDB::bdb_create_base_file_attributes_record(JCR *jcr, ATTR_DBR *ar)
1063 Dmsg1(dbglevel, "create_base_file Fname=%s\n", ar->fname);
1064 Dmsg0(dbglevel, "put_base_file_into_catalog\n");
1067 split_path_and_file(jcr, this, ar->fname);
1069 esc_name = check_pool_memory_size(esc_name, fnl*2+1);
1070 bdb_escape_string(jcr, esc_name, fname, fnl);
1072 esc_path = check_pool_memory_size(esc_path, pnl*2+1);
1073 bdb_escape_string(jcr, esc_path, path, pnl);
1075 Mmsg(cmd, "INSERT INTO basefile%lld (Path, Name) VALUES ('%s','%s')",
1076 (uint64_t)jcr->JobId, esc_path, esc_name);
1078 ret = InsertDB(jcr, cmd);
1085 * Cleanup the base file temporary tables
1087 static void db_cleanup_base_file(JCR *jcr, BDB *mdb)
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);
1093 Mmsg(buf, "DROP TABLE basefile%lld", (uint64_t) jcr->JobId);
1094 mdb->bdb_sql_query(buf.c_str(), NULL, NULL);
1098 * Put all base file seen in the backup to the BaseFile table
1099 * and cleanup temporary tables
1101 bool BDB::bdb_commit_base_file_attributes_record(JCR *jcr)
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);
1119 * Display error now, because the subsequent cleanup destroys the
1120 * error message from the above query.
1123 Jmsg1(jcr, M_FATAL, 0, "%s", jcr->db->bdb_strerror());
1125 jcr->nb_base_files_used = sql_affected_rows();
1126 db_cleanup_base_file(jcr, this);
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
1139 bool BDB::bdb_create_base_file_list(JCR *jcr, char *jobids)
1147 Mmsg(errmsg, _("ERR=JobIds are empty\n"));
1151 Mmsg(cmd, create_temp_basefile[bdb_get_type_index()], (uint64_t) jcr->JobId);
1152 if (!bdb_sql_query(cmd, NULL, NULL)) {
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());
1158 ret = bdb_sql_query(cmd, NULL, NULL);
1165 * Create Restore Object record in BDB
1168 bool BDB::bdb_create_restore_object_record(JCR *jcr, ROBJECT_DBR *ro)
1172 POOLMEM *esc_plug_name = get_pool_memory(PM_MESSAGE);
1176 Dmsg1(dbglevel, "Oname=%s\n", ro->object_name);
1177 Dmsg0(dbglevel, "put_object_into_catalog\n");
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);
1183 bdb_escape_object(jcr, ro->object, ro->object_len);
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);
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);
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);
1208 free_pool_memory(esc_plug_name);
1212 bool BDB::bdb_create_snapshot_record(JCR *jcr, SNAPSHOT_DBR *snap)
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];
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));
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));
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));
1237 bdb_escape_string(jcr, esc_comment, snap->Comment, strlen(snap->Comment));
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);
1244 Mmsg(esc_client, "%d", snap->ClientId);
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);
1252 Mmsg(esc_fs, "%d", snap->FileSetId);
1255 bdb_escape_string(jcr, esc_name, snap->Name, strlen(snap->Name));
1257 stime = snap->CreateTDate;
1258 (void)localtime_r(&stime, &tm);
1259 strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
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);
1267 if (bdb_sql_query(cmd, NULL, NULL)) {
1268 snap->SnapshotId = sql_insert_autokey_record(cmd, NT_("Snapshot"));
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);
1283 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */