2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
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.
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.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
21 * Bacula Catalog Database Create record interface routines
23 * Written by Kern Sibbald, March 2000
29 static const int dbglevel = 100;
31 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
35 /* -----------------------------------------------------------------------
37 * Generic Routines (or almost generic)
39 * -----------------------------------------------------------------------
42 /** Create a new record for the Job
43 * Returns: false on failure
46 bool BDB::bdb_create_job_record(JCR *jcr, JOB_DBR *jr)
49 char dt[MAX_TIME_LENGTH];
56 char esc_job[MAX_ESCAPE_NAME_LENGTH];
57 char esc_name[MAX_ESCAPE_NAME_LENGTH];
61 stime = jr->SchedTime;
64 (void)localtime_r(&stime, &tm);
65 strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
66 JobTDate = (utime_t)stime;
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);
72 bdb_escape_string(jcr, esc_job, jr->Job, strlen(jr->Job));
73 bdb_escape_string(jcr, esc_name, jr->Name, strlen(jr->Name));
77 "INSERT INTO Job (Job,Name,Type,Level,JobStatus,SchedTime,JobTDate,"
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());
84 if ((jr->JobId = sql_insert_autokey_record(cmd, NT_("Job"))) == 0) {
85 Mmsg2(&errmsg, _("Create DB Job record %s failed. ERR=%s\n"),
96 /** Create a JobMedia record for medium used this job
97 * Returns: false on failure
100 bool BDB::bdb_create_jobmedia_record(JCR *jcr, JOBMEDIA_DBR *jm)
104 char ed1[50], ed2[50];
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);
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);
127 if (!InsertDB(jcr, cmd)) {
128 Mmsg2(&errmsg, _("Create JobMedia record %s failed: ERR=%s\n"), cmd,
132 /* Worked, now update the Media record with the EndFile and EndBlock */
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,
143 Dmsg0(300, "Return from JobMedia\n");
147 /** Create Unique Pool record
148 * Returns: false on failure
151 bool BDB::bdb_create_pool_record(JCR *jcr, POOL_DBR *pr)
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];
158 Dmsg0(200, "In create pool\n");
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);
165 if (QueryDB(jcr, cmd)) {
166 if (sql_num_rows() > 0) {
167 Mmsg1(&errmsg, _("pool record %s already exists\n"), pr->Name);
170 Dmsg1(200, "%s", errmsg); /* pool already exists */
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)",
184 pr->NumVols, pr->MaxVols,
185 pr->UseOnce, pr->UseCatalog,
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),
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());
210 * Create Unique Device record
211 * Returns: false on failure
214 bool BDB::bdb_create_device_record(JCR *jcr, DEVICE_DBR *dr)
217 char ed1[30], ed2[30];
218 char esc[MAX_ESCAPE_NAME_LENGTH];
220 Dmsg0(200, "In create Device\n");
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);
226 if (QueryDB(jcr, cmd)) {
227 if (sql_num_rows() > 0) {
228 Mmsg1(&errmsg, _("Device record %s already exists\n"), dr->Name);
238 "INSERT INTO Device (Name,MediaTypeId,StorageId) VALUES ('%s',%s,%s)",
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());
257 * Create a Unique record for Storage -- no duplicates
258 * Returns: false on failure
259 * true on success with id in sr->StorageId
261 bool BDB::bdb_create_storage_record(JCR *jcr, STORAGE_DBR *sr)
265 char esc[MAX_ESCAPE_NAME_LENGTH];
268 bdb_escape_string(jcr, esc, sr->Name, strlen(sr->Name));
269 Mmsg(cmd, "SELECT StorageId,AutoChanger FROM Storage WHERE Name='%s'",esc);
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);
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);
288 sr->StorageId = str_to_int64(row[0]);
289 sr->AutoChanger = atoi(row[1]); /* bool */
298 Mmsg(cmd, "INSERT INTO Storage (Name,AutoChanger)"
299 " VALUES ('%s',%d)", esc, sr->AutoChanger);
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);
316 * Create Unique MediaType record
317 * Returns: false on failure
320 bool BDB::bdb_create_mediatype_record(JCR *jcr, MEDIATYPE_DBR *mr)
323 char esc[MAX_ESCAPE_NAME_LENGTH];
325 Dmsg0(200, "In create mediatype\n");
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);
331 if (QueryDB(jcr, cmd)) {
332 if (sql_num_rows() > 0) {
333 Mmsg1(&errmsg, _("mediatype record %s already exists\n"), mr->MediaType);
343 "INSERT INTO MediaType (MediaType,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());
361 * Create Media record. VolumeName and non-zero Slot must be unique
363 * Returns: 0 on failure
366 int BDB::bdb_create_media_record(JCR *jcr, MEDIA_DBR *mr)
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];
372 char esc_name[MAX_ESCAPE_NAME_LENGTH];
373 char esc_mtype[MAX_ESCAPE_NAME_LENGTH];
374 char esc_status[MAX_ESCAPE_NAME_LENGTH];
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));
382 Mmsg(cmd, "SELECT MediaId FROM Media WHERE VolumeName='%s'", esc_name);
383 Dmsg1(500, "selectpool: %s\n", cmd);
385 if (QueryDB(jcr, cmd)) {
386 if (sql_num_rows() > 0) {
387 Mmsg1(&errmsg, _("Volume \"%s\" already exists.\n"), mr->VolumeName);
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)",
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),
419 mr->VolType, /* formerly VolParts */
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
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());
437 if (mr->set_label_date) {
438 char dt[MAX_TIME_LENGTH];
439 if (mr->LabelDate == 0) {
440 mr->LabelDate = time(NULL);
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);
449 * Make sure that if InChanger is non-zero any other identical slot
450 * has InChanger zero.
452 db_make_inchanger_unique(jcr, this, mr);
460 * Create a Unique record for the client -- no duplicates
461 * Returns: 0 on failure
462 * 1 on success with id in cr->ClientId
464 int BDB::bdb_create_client_record(JCR *jcr, CLIENT_DBR *cr)
468 char ed1[50], ed2[50];
469 char esc_name[MAX_ESCAPE_NAME_LENGTH];
470 char esc_uname[MAX_ESCAPE_NAME_LENGTH];
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);
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);
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);
492 cr->ClientId = str_to_int64(row[0]);
494 bstrncpy(cr->Uname, row[1], sizeof(cr->Uname));
496 cr->Uname[0] = 0; /* no name */
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));
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);
525 /** Create a Unique record for the Path -- no duplicates */
526 int BDB::bdb_create_path_record(JCR *jcr, ATTR_DBR *ar)
532 esc_name = check_pool_memory_size(esc_name, 2*pnl+2);
533 bdb_escape_string(jcr, esc_name, path, pnl);
535 if (cached_path_id != 0 && cached_path_len == pnl &&
536 strcmp(cached_path, path) == 0) {
537 ar->PathId = cached_path_id;
541 Mmsg(cmd, "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
543 if (QueryDB(jcr, cmd)) {
544 if (sql_num_rows() > 1) {
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);
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);
558 "Your Path table is broken. "
559 "Please, use dbcheck to correct it.");
562 ar->PathId = str_to_int64(row[0]);
565 if (ar->PathId != cached_path_id) {
566 cached_path_id = ar->PathId;
567 cached_path_len = pnl;
568 pm_strcpy(cached_path, path);
576 Mmsg(cmd, "INSERT INTO Path (Path) VALUES ('%s')", esc_name);
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);
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);
598 * Create a Unique record for the counter -- no duplicates
599 * Returns: 0 on failure
600 * 1 on success with counter filled in
602 int BDB::bdb_create_counter_record(JCR *jcr, COUNTER_DBR *cr)
604 char esc[MAX_ESCAPE_NAME_LENGTH];
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));
616 bdb_escape_string(jcr, esc, cr->Counter, strlen(cr->Counter));
619 Mmsg(cmd, insert_counter_values[bdb_get_type_index()],
620 esc, cr->MinValue, cr->MaxValue, cr->CurrentValue,
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);
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
641 bool BDB::bdb_create_fileset_record(JCR *jcr, FILESET_DBR *fsr)
646 char esc_fs[MAX_ESCAPE_NAME_LENGTH];
647 char esc_md5[MAX_ESCAPE_NAME_LENGTH];
649 /* TODO: Escape FileSet and MD5 */
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);
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);
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);
671 fsr->FileSetId = str_to_int64(row[0]);
672 if (row[1] == NULL) {
673 fsr->cCreateTime[0] = 0;
675 bstrncpy(fsr->cCreateTime, row[1], sizeof(fsr->cCreateTime));
684 if (fsr->CreateTime == 0 && fsr->cCreateTime[0] == 0) {
685 fsr->CreateTime = time(NULL);
687 (void)localtime_r(&fsr->CreateTime, &tm);
688 strftime(fsr->cCreateTime, sizeof(fsr->cCreateTime), "%Y-%m-%d %H:%M:%S", &tm);
691 Mmsg(cmd, "INSERT INTO FileSet (FileSet,MD5,CreateTime) "
692 "VALUES ('%s','%s','%s')", esc_fs, esc_md5, fsr->cCreateTime);
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);
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 *
728 /* For maintenance, we can put batch mode in hold */
729 static bool batch_mode_enabled = true;
731 void bdb_disable_batch_insert(bool enabled)
733 batch_mode_enabled = enabled;
737 * All sql_batch_xx functions are used to do bulk batch
738 * insert in File/Filename/Path tables.
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.
752 bool bdb_write_batch_file_records(JCR *jcr)
755 int JobStatus = jcr->JobStatus;
757 if (!jcr->batch_started) { /* no files to backup ? */
758 Dmsg0(50,"db_write_batch_file_records: no files\n");
762 if (job_canceled(jcr)) {
766 jcr->JobStatus = JS_AttrInserting;
768 /* Check if batch mode is on hold */
769 while (!batch_mode_enabled) {
770 Dmsg0(50, "batch mode is on hold\n");
773 if (job_canceled(jcr)) {
778 Dmsg1(50,"db_write_batch_file_records changes=%u\n",jcr->db_batch->changes);
780 if (!jcr->db_batch->sql_batch_end(jcr, NULL)) {
781 Jmsg1(jcr, M_FATAL, 0, "Batch end %s\n", jcr->db_batch->errmsg);
784 if (job_canceled(jcr)) {
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);
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);
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);
806 * We have to lock tables
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);
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);
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);
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 "
829 "JOIN Path ON (batch.Path = Path.Path) "
830 "JOIN Filename ON (batch.Name = Filename.Name)",
833 Jmsg1(jcr, M_FATAL, 0, "Fill File table %s\n", jcr->db_batch->errmsg);
837 jcr->JobStatus = JobStatus; /* reset entry status */
841 jcr->db_batch->bdb_sql_query("DROP TABLE batch", NULL,NULL);
842 jcr->batch_started = false;
848 * Create File record in BDB
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.
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.
860 bool BDB::bdb_create_batch_file_attributes_record(JCR *jcr, ATTR_DBR *ar)
862 ASSERT(ar->FileType != FT_BASE);
863 Dmsg1(dbglevel, "Fname=%s\n", ar->fname);
864 Dmsg0(dbglevel, "put_file_into_catalog\n");
866 if (jcr->batch_started && jcr->db_batch->changes > 500000) {
867 bdb_write_batch_file_records(jcr);
868 jcr->db_batch->changes = 0;
871 /* Open the dedicated connexion */
872 if (!jcr->batch_started) {
873 if (!bdb_open_batch_connexion(jcr)) {
874 return false; /* error already printed */
876 if (!jcr->db_batch->sql_batch_start(jcr)) {
878 "Can't start batch mode: ERR=%s", db_strerror(jcr->db_batch));
879 Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
882 jcr->batch_started = true;
885 split_path_and_file(jcr, jcr->db_batch, ar->fname);
887 return jcr->db_batch->sql_batch_insert(jcr, ar);
891 * Create File record in BDB
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.
899 bool BDB::bdb_create_file_attributes_record(JCR *jcr, ATTR_DBR *ar)
902 Dmsg1(dbglevel, "Fname=%s\n", ar->fname);
903 Dmsg0(dbglevel, "put_file_into_catalog\n");
905 split_path_and_file(jcr, this, ar->fname);
907 if (!bdb_create_filename_record(jcr, ar)) {
910 Dmsg1(dbglevel, "bdb_create_filename_record: %s\n", esc_name);
913 if (!bdb_create_path_record(jcr, ar)) {
916 Dmsg1(dbglevel, "bdb_create_path_record: %s\n", esc_name);
918 /* Now create master File record */
919 if (!bdb_create_file_record(jcr, ar)) {
922 Dmsg0(dbglevel, "db_create_file_record OK\n");
924 Dmsg3(dbglevel, "CreateAttributes Path=%s File=%s FilenameId=%d\n", path, fname, ar->FilenameId);
933 * This is the master File entry containing the attributes.
934 * The filename and path records have already been created.
936 int BDB::bdb_create_file_record(JCR *jcr, ATTR_DBR *ar)
939 static const char *no_digest = "0";
944 ASSERT(ar->FilenameId);
946 if (ar->Digest == NULL || ar->Digest[0] == 0) {
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);
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);
970 /** Create a Unique record for the filename -- no duplicates */
971 int BDB::bdb_create_filename_record(JCR *jcr, ATTR_DBR *ar)
976 esc_name = check_pool_memory_size(esc_name, 2*fnl+2);
977 bdb_escape_string(jcr, esc_name, fname, fnl);
979 Mmsg(cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
981 if (QueryDB(jcr, cmd)) {
982 if (sql_num_rows() > 1) {
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);
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);
995 ar->FilenameId = str_to_int64(row[0]);
998 return ar->FilenameId > 0;
1003 Mmsg(cmd, "INSERT INTO Filename (Name) VALUES ('%s')", esc_name);
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);
1011 return ar->FilenameId > 0;
1015 * Create file attributes record, or base file attributes record
1017 bool BDB::bdb_create_attributes_record(JCR *jcr, ATTR_DBR *ar)
1023 * Make sure we have an acceptable attributes record.
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"),
1029 Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
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 */
1038 ret = bdb_create_file_attributes_record(jcr, ar);
1040 } else if (jcr->HasBase) {
1041 ret = bdb_create_base_file_attributes_record(jcr, ar);
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 ? */
1052 * Create Base File record in BDB
1055 bool BDB::bdb_create_base_file_attributes_record(JCR *jcr, ATTR_DBR *ar)
1059 Dmsg1(dbglevel, "create_base_file Fname=%s\n", ar->fname);
1060 Dmsg0(dbglevel, "put_base_file_into_catalog\n");
1063 split_path_and_file(jcr, this, ar->fname);
1065 esc_name = check_pool_memory_size(esc_name, fnl*2+1);
1066 bdb_escape_string(jcr, esc_name, fname, fnl);
1068 esc_path = check_pool_memory_size(esc_path, pnl*2+1);
1069 bdb_escape_string(jcr, esc_path, path, pnl);
1071 Mmsg(cmd, "INSERT INTO basefile%lld (Path, Name) VALUES ('%s','%s')",
1072 (uint64_t)jcr->JobId, esc_path, esc_name);
1074 ret = InsertDB(jcr, cmd);
1081 * Cleanup the base file temporary tables
1083 static void db_cleanup_base_file(JCR *jcr, BDB *mdb)
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);
1089 Mmsg(buf, "DROP TABLE basefile%lld", (uint64_t) jcr->JobId);
1090 mdb->bdb_sql_query(buf.c_str(), NULL, NULL);
1094 * Put all base file seen in the backup to the BaseFile table
1095 * and cleanup temporary tables
1097 bool BDB::bdb_commit_base_file_attributes_record(JCR *jcr)
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);
1115 * Display error now, because the subsequent cleanup destroys the
1116 * error message from the above query.
1119 Jmsg1(jcr, M_FATAL, 0, "%s", jcr->db->bdb_strerror());
1121 jcr->nb_base_files_used = sql_affected_rows();
1122 db_cleanup_base_file(jcr, this);
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
1135 bool BDB::bdb_create_base_file_list(JCR *jcr, char *jobids)
1143 Mmsg(errmsg, _("ERR=JobIds are empty\n"));
1147 Mmsg(cmd, create_temp_basefile[bdb_get_type_index()], (uint64_t) jcr->JobId);
1148 if (!bdb_sql_query(cmd, NULL, NULL)) {
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());
1154 ret = bdb_sql_query(cmd, NULL, NULL);
1161 * Create Restore Object record in BDB
1164 bool BDB::bdb_create_restore_object_record(JCR *jcr, ROBJECT_DBR *ro)
1168 POOLMEM *esc_plug_name = get_pool_memory(PM_MESSAGE);
1172 Dmsg1(dbglevel, "Oname=%s\n", ro->object_name);
1173 Dmsg0(dbglevel, "put_object_into_catalog\n");
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);
1179 bdb_escape_object(jcr, ro->object, ro->object_len);
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);
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);
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);
1204 free_pool_memory(esc_plug_name);
1208 bool BDB::bdb_create_snapshot_record(JCR *jcr, SNAPSHOT_DBR *snap)
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];
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));
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));
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));
1233 bdb_escape_string(jcr, esc_comment, snap->Comment, strlen(snap->Comment));
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);
1240 Mmsg(esc_client, "%d", snap->ClientId);
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);
1248 Mmsg(esc_fs, "%d", snap->FileSetId);
1251 bdb_escape_string(jcr, esc_name, snap->Name, strlen(snap->Name));
1253 stime = snap->CreateTDate;
1254 (void)localtime_r(&stime, &tm);
1255 strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
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);
1263 if (bdb_sql_query(cmd, NULL, NULL)) {
1264 snap->SnapshotId = sql_insert_autokey_record(cmd, NT_("Snapshot"));
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);
1279 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */