2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Bacula Catalog Database Create record interface routines
31 * Kern Sibbald, March 2000
37 static const int dbglevel = 100;
39 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI
45 /* -----------------------------------------------------------------------
47 * Generic Routines (or almost generic)
49 * -----------------------------------------------------------------------
52 /* Forward referenced subroutines */
53 static int db_create_file_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar);
54 static int db_create_filename_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar);
56 /** Create a new record for the Job
57 * Returns: false on failure
61 db_create_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr)
64 char dt[MAX_TIME_LENGTH];
74 stime = jr->SchedTime;
77 (void)localtime_r(&stime, &tm);
78 strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
79 JobTDate = (utime_t)stime;
81 len = strlen(jcr->comment); /* TODO: use jr instead of jcr to get comment */
82 buf.check_size(len*2+1);
83 db_escape_string(jcr, mdb, buf.c_str(), jcr->comment, len);
87 "INSERT INTO Job (Job,Name,Type,Level,JobStatus,SchedTime,JobTDate,"
89 "VALUES ('%s','%s','%c','%c','%c','%s',%s,%s,'%s')",
90 jr->Job, jr->Name, (char)(jr->JobType), (char)(jr->JobLevel),
91 (char)(jr->JobStatus), dt, edit_uint64(JobTDate, ed1),
92 edit_int64(jr->ClientId, ed2), buf.c_str());
94 jr->JobId = sql_insert_autokey_record(mdb, mdb->cmd, NT_("Job"));
96 Mmsg2(&mdb->errmsg, _("Create DB Job record %s failed. ERR=%s\n"),
97 mdb->cmd, sql_strerror(mdb));
107 /** Create a JobMedia record for medium used this job
108 * Returns: false on failure
112 db_create_jobmedia_record(JCR *jcr, B_DB *mdb, JOBMEDIA_DBR *jm)
116 char ed1[50], ed2[50];
120 /* Now get count for VolIndex */
121 Mmsg(mdb->cmd, "SELECT count(*) from JobMedia WHERE JobId=%s",
122 edit_int64(jm->JobId, ed1));
123 count = get_sql_record_max(jcr, mdb);
130 "INSERT INTO JobMedia (JobId,MediaId,FirstIndex,LastIndex,"
131 "StartFile,EndFile,StartBlock,EndBlock,VolIndex) "
132 "VALUES (%s,%s,%u,%u,%u,%u,%u,%u,%u)",
133 edit_int64(jm->JobId, ed1),
134 edit_int64(jm->MediaId, ed2),
135 jm->FirstIndex, jm->LastIndex,
136 jm->StartFile, jm->EndFile, jm->StartBlock, jm->EndBlock,count);
138 Dmsg0(300, mdb->cmd);
139 if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
140 Mmsg2(&mdb->errmsg, _("Create JobMedia record %s failed: ERR=%s\n"), mdb->cmd,
144 /* Worked, now update the Media record with the EndFile and EndBlock */
146 "UPDATE Media SET EndFile=%u, EndBlock=%u WHERE MediaId=%u",
147 jm->EndFile, jm->EndBlock, jm->MediaId);
148 if (!UPDATE_DB(jcr, mdb, mdb->cmd)) {
149 Mmsg2(&mdb->errmsg, _("Update Media record %s failed: ERR=%s\n"), mdb->cmd,
155 Dmsg0(300, "Return from JobMedia\n");
159 /** Create Unique Pool record
160 * Returns: false on failure
164 db_create_pool_record(JCR *jcr, B_DB *mdb, POOL_DBR *pr)
167 char ed1[30], ed2[30], ed3[50], ed4[50], ed5[50];
170 Dmsg0(200, "In create pool\n");
172 Mmsg(mdb->cmd, "SELECT PoolId,Name FROM Pool WHERE Name='%s'", pr->Name);
173 Dmsg1(200, "selectpool: %s\n", mdb->cmd);
175 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
176 num_rows = sql_num_rows(mdb);
178 Mmsg1(&mdb->errmsg, _("pool record %s already exists\n"), pr->Name);
179 sql_free_result(mdb);
183 sql_free_result(mdb);
188 "INSERT INTO Pool (Name,NumVols,MaxVols,UseOnce,UseCatalog,"
189 "AcceptAnyVolume,AutoPrune,Recycle,VolRetention,VolUseDuration,"
190 "MaxVolJobs,MaxVolFiles,MaxVolBytes,PoolType,LabelType,LabelFormat,"
191 "RecyclePoolId,ScratchPoolId,ActionOnPurge) "
192 "VALUES ('%s',%u,%u,%d,%d,%d,%d,%d,%s,%s,%u,%u,%s,'%s',%d,'%s',%s,%s,%d)",
194 pr->NumVols, pr->MaxVols,
195 pr->UseOnce, pr->UseCatalog,
197 pr->AutoPrune, pr->Recycle,
198 edit_uint64(pr->VolRetention, ed1),
199 edit_uint64(pr->VolUseDuration, ed2),
200 pr->MaxVolJobs, pr->MaxVolFiles,
201 edit_uint64(pr->MaxVolBytes, ed3),
202 pr->PoolType, pr->LabelType, pr->LabelFormat,
203 edit_int64(pr->RecyclePoolId,ed4),
204 edit_int64(pr->ScratchPoolId,ed5),
207 Dmsg1(200, "Create Pool: %s\n", mdb->cmd);
208 pr->PoolId = sql_insert_autokey_record(mdb, mdb->cmd, NT_("Pool"));
209 if (pr->PoolId == 0) {
210 Mmsg2(&mdb->errmsg, _("Create db Pool record %s failed: ERR=%s\n"),
211 mdb->cmd, sql_strerror(mdb));
217 Dmsg0(500, "Create Pool: done\n");
222 * Create Unique Device record
223 * Returns: false on failure
227 db_create_device_record(JCR *jcr, B_DB *mdb, DEVICE_DBR *dr)
230 char ed1[30], ed2[30];
233 Dmsg0(200, "In create Device\n");
235 Mmsg(mdb->cmd, "SELECT DeviceId,Name FROM Device WHERE Name='%s'", dr->Name);
236 Dmsg1(200, "selectdevice: %s\n", mdb->cmd);
238 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
239 num_rows = sql_num_rows(mdb);
241 Mmsg1(&mdb->errmsg, _("Device record %s already exists\n"), dr->Name);
242 sql_free_result(mdb);
246 sql_free_result(mdb);
251 "INSERT INTO Device (Name,MediaTypeId,StorageId) VALUES ('%s',%s,%s)",
253 edit_uint64(dr->MediaTypeId, ed1),
254 edit_int64(dr->StorageId, ed2));
255 Dmsg1(200, "Create Device: %s\n", mdb->cmd);
256 dr->DeviceId = sql_insert_autokey_record(mdb, mdb->cmd, NT_("Device"));
257 if (dr->DeviceId == 0) {
258 Mmsg2(&mdb->errmsg, _("Create db Device record %s failed: ERR=%s\n"),
259 mdb->cmd, sql_strerror(mdb));
271 * Create a Unique record for Storage -- no duplicates
272 * Returns: false on failure
273 * true on success with id in sr->StorageId
275 bool db_create_storage_record(JCR *jcr, B_DB *mdb, STORAGE_DBR *sr)
282 Mmsg(mdb->cmd, "SELECT StorageId,AutoChanger FROM Storage WHERE Name='%s'", sr->Name);
286 /* Check if it already exists */
287 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
288 num_rows = sql_num_rows(mdb);
289 /* If more than one, report error, but return first row */
291 Mmsg1(&mdb->errmsg, _("More than one Storage record!: %d\n"), num_rows);
292 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
295 if ((row = sql_fetch_row(mdb)) == NULL) {
296 Mmsg1(&mdb->errmsg, _("error fetching Storage row: %s\n"), sql_strerror(mdb));
297 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
298 sql_free_result(mdb);
302 sr->StorageId = str_to_int64(row[0]);
303 sr->AutoChanger = atoi(row[1]); /* bool */
304 sql_free_result(mdb);
308 sql_free_result(mdb);
312 Mmsg(mdb->cmd, "INSERT INTO Storage (Name,AutoChanger)"
313 " VALUES ('%s',%d)", sr->Name, sr->AutoChanger);
315 sr->StorageId = sql_insert_autokey_record(mdb, mdb->cmd, NT_("Storage"));
316 if (sr->StorageId == 0) {
317 Mmsg2(&mdb->errmsg, _("Create DB Storage record %s failed. ERR=%s\n"),
318 mdb->cmd, sql_strerror(mdb));
319 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
331 * Create Unique MediaType record
332 * Returns: false on failure
336 db_create_mediatype_record(JCR *jcr, B_DB *mdb, MEDIATYPE_DBR *mr)
341 Dmsg0(200, "In create mediatype\n");
343 Mmsg(mdb->cmd, "SELECT MediaTypeId,MediaType FROM MediaType WHERE MediaType='%s'", mr->MediaType);
344 Dmsg1(200, "selectmediatype: %s\n", mdb->cmd);
346 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
347 num_rows = sql_num_rows(mdb);
349 Mmsg1(&mdb->errmsg, _("mediatype record %s already exists\n"), mr->MediaType);
350 sql_free_result(mdb);
354 sql_free_result(mdb);
359 "INSERT INTO MediaType (MediaType,ReadOnly) "
363 Dmsg1(200, "Create mediatype: %s\n", mdb->cmd);
364 mr->MediaTypeId = sql_insert_autokey_record(mdb, mdb->cmd, NT_("MediaType"));
365 if (mr->MediaTypeId == 0) {
366 Mmsg2(&mdb->errmsg, _("Create db mediatype record %s failed: ERR=%s\n"),
367 mdb->cmd, sql_strerror(mdb));
378 * Create Media record. VolumeName and non-zero Slot must be unique
380 * Returns: 0 on failure
384 db_create_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr)
387 char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50], ed6[50], ed7[50], ed8[50];
388 char ed9[50], ed10[50], ed11[50], ed12[50];
393 Mmsg(mdb->cmd, "SELECT MediaId FROM Media WHERE VolumeName='%s'",
395 Dmsg1(500, "selectpool: %s\n", mdb->cmd);
397 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
398 num_rows = sql_num_rows(mdb);
400 Mmsg1(&mdb->errmsg, _("Volume \"%s\" already exists.\n"), mr->VolumeName);
401 sql_free_result(mdb);
405 sql_free_result(mdb);
410 "INSERT INTO Media (VolumeName,MediaType,MediaTypeId,PoolId,MaxVolBytes,"
411 "VolCapacityBytes,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
412 "VolStatus,Slot,VolBytes,InChanger,VolReadTime,VolWriteTime,VolParts,"
413 "EndFile,EndBlock,LabelType,StorageId,DeviceId,LocationId,"
414 "ScratchPoolId,RecyclePoolId,Enabled,ActionOnPurge)"
415 "VALUES ('%s','%s',0,%u,%s,%s,%d,%s,%s,%u,%u,'%s',%d,%s,%d,%s,%s,%d,0,0,%d,%s,"
416 "%s,%s,%s,%s,%d,%d)",
418 mr->MediaType, mr->PoolId,
419 edit_uint64(mr->MaxVolBytes,ed1),
420 edit_uint64(mr->VolCapacityBytes, ed2),
422 edit_uint64(mr->VolRetention, ed3),
423 edit_uint64(mr->VolUseDuration, ed4),
428 edit_uint64(mr->VolBytes, ed5),
430 edit_int64(mr->VolReadTime, ed6),
431 edit_int64(mr->VolWriteTime, ed7),
434 edit_int64(mr->StorageId, ed8),
435 edit_int64(mr->DeviceId, ed9),
436 edit_int64(mr->LocationId, ed10),
437 edit_int64(mr->ScratchPoolId, ed11),
438 edit_int64(mr->RecyclePoolId, ed12),
439 mr->Enabled, mr->ActionOnPurge
443 Dmsg1(500, "Create Volume: %s\n", mdb->cmd);
444 mr->MediaId = sql_insert_autokey_record(mdb, mdb->cmd, NT_("Media"));
445 if (mr->MediaId == 0) {
446 Mmsg2(&mdb->errmsg, _("Create DB Media record %s failed. ERR=%s\n"),
447 mdb->cmd, sql_strerror(mdb));
451 if (mr->set_label_date) {
452 char dt[MAX_TIME_LENGTH];
453 if (mr->LabelDate == 0) {
454 mr->LabelDate = time(NULL);
456 (void)localtime_r(&mr->LabelDate, &tm);
457 strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
458 Mmsg(mdb->cmd, "UPDATE Media SET LabelDate='%s' "
459 "WHERE MediaId=%d", dt, mr->MediaId);
460 stat = UPDATE_DB(jcr, mdb, mdb->cmd);
463 * Make sure that if InChanger is non-zero any other identical slot
464 * has InChanger zero.
466 db_make_inchanger_unique(jcr, mdb, mr);
474 * Create a Unique record for the client -- no duplicates
475 * Returns: 0 on failure
476 * 1 on success with id in cr->ClientId
478 int db_create_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cr)
482 char ed1[50], ed2[50];
486 Mmsg(mdb->cmd, "SELECT ClientId,Uname FROM Client WHERE Name='%s'", cr->Name);
489 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
490 num_rows = sql_num_rows(mdb);
491 /* If more than one, report error, but return first row */
493 Mmsg1(&mdb->errmsg, _("More than one Client!: %d\n"), num_rows);
494 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
497 if ((row = sql_fetch_row(mdb)) == NULL) {
498 Mmsg1(&mdb->errmsg, _("error fetching Client row: %s\n"), sql_strerror(mdb));
499 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
500 sql_free_result(mdb);
504 cr->ClientId = str_to_int64(row[0]);
506 bstrncpy(cr->Uname, row[1], sizeof(cr->Uname));
508 cr->Uname[0] = 0; /* no name */
510 sql_free_result(mdb);
514 sql_free_result(mdb);
518 Mmsg(mdb->cmd, "INSERT INTO Client (Name,Uname,AutoPrune,"
519 "FileRetention,JobRetention) VALUES "
520 "('%s','%s',%d,%s,%s)", cr->Name, cr->Uname, cr->AutoPrune,
521 edit_uint64(cr->FileRetention, ed1),
522 edit_uint64(cr->JobRetention, ed2));
524 cr->ClientId = sql_insert_autokey_record(mdb, mdb->cmd, NT_("Client"));
525 if (cr->ClientId == 0) {
526 Mmsg2(&mdb->errmsg, _("Create DB Client record %s failed. ERR=%s\n"),
527 mdb->cmd, sql_strerror(mdb));
528 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
538 /** Create a Unique record for the Path -- no duplicates */
539 int db_create_path_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
545 mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->pnl+2);
546 db_escape_string(jcr, mdb, mdb->esc_name, mdb->path, mdb->pnl);
548 if (mdb->cached_path_id != 0 && mdb->cached_path_len == mdb->pnl &&
549 strcmp(mdb->cached_path, mdb->path) == 0) {
550 ar->PathId = mdb->cached_path_id;
554 Mmsg(mdb->cmd, "SELECT PathId FROM Path WHERE Path='%s'", mdb->esc_name);
556 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
557 num_rows = sql_num_rows(mdb);
560 Mmsg2(&mdb->errmsg, _("More than one Path!: %s for path: %s\n"),
561 edit_uint64(num_rows, ed1), mdb->path);
562 Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
564 /* Even if there are multiple paths, take the first one */
566 if ((row = sql_fetch_row(mdb)) == NULL) {
567 Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
568 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
569 sql_free_result(mdb);
574 ar->PathId = str_to_int64(row[0]);
575 sql_free_result(mdb);
577 if (ar->PathId != mdb->cached_path_id) {
578 mdb->cached_path_id = ar->PathId;
579 mdb->cached_path_len = mdb->pnl;
580 pm_strcpy(mdb->cached_path, mdb->path);
585 sql_free_result(mdb);
588 Mmsg(mdb->cmd, "INSERT INTO Path (Path) VALUES ('%s')", mdb->esc_name);
590 ar->PathId = sql_insert_autokey_record(mdb, mdb->cmd, NT_("Path"));
591 if (ar->PathId == 0) {
592 Mmsg2(&mdb->errmsg, _("Create db Path record %s failed. ERR=%s\n"),
593 mdb->cmd, sql_strerror(mdb));
594 Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
602 if (stat && ar->PathId != mdb->cached_path_id) {
603 mdb->cached_path_id = ar->PathId;
604 mdb->cached_path_len = mdb->pnl;
605 pm_strcpy(mdb->cached_path, mdb->path);
611 * Create a Unique record for the counter -- no duplicates
612 * Returns: 0 on failure
613 * 1 on success with counter filled in
615 int db_create_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr)
621 memset(&mcr, 0, sizeof(mcr));
622 bstrncpy(mcr.Counter, cr->Counter, sizeof(mcr.Counter));
623 if (db_get_counter_record(jcr, mdb, &mcr)) {
624 memcpy(cr, &mcr, sizeof(COUNTER_DBR));
630 Mmsg(mdb->cmd, "INSERT INTO Counters (Counter,\"MinValue\",\"MaxValue\",CurrentValue,"
631 "WrapCounter) VALUES ('%s','%d','%d','%d','%s')",
632 cr->Counter, cr->MinValue, cr->MaxValue, cr->CurrentValue,
635 if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
636 Mmsg2(&mdb->errmsg, _("Create DB Counters record %s failed. ERR=%s\n"),
637 mdb->cmd, sql_strerror(mdb));
638 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
649 * Create a FileSet record. This record is unique in the
650 * name and the MD5 signature of the include/exclude sets.
651 * Returns: 0 on failure
652 * 1 on success with FileSetId in record
654 bool db_create_fileset_record(JCR *jcr, B_DB *mdb, FILESET_DBR *fsr)
662 fsr->created = false;
663 Mmsg(mdb->cmd, "SELECT FileSetId,CreateTime FROM FileSet WHERE "
664 "FileSet='%s' AND MD5='%s'", fsr->FileSet, fsr->MD5);
667 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
668 num_rows = sql_num_rows(mdb);
670 Mmsg1(&mdb->errmsg, _("More than one FileSet!: %d\n"), num_rows);
671 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
674 if ((row = sql_fetch_row(mdb)) == NULL) {
675 Mmsg1(&mdb->errmsg, _("error fetching FileSet row: ERR=%s\n"), sql_strerror(mdb));
676 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
677 sql_free_result(mdb);
681 fsr->FileSetId = str_to_int64(row[0]);
682 if (row[1] == NULL) {
683 fsr->cCreateTime[0] = 0;
685 bstrncpy(fsr->cCreateTime, row[1], sizeof(fsr->cCreateTime));
687 sql_free_result(mdb);
691 sql_free_result(mdb);
694 if (fsr->CreateTime == 0 && fsr->cCreateTime[0] == 0) {
695 fsr->CreateTime = time(NULL);
697 (void)localtime_r(&fsr->CreateTime, &tm);
698 strftime(fsr->cCreateTime, sizeof(fsr->cCreateTime), "%Y-%m-%d %H:%M:%S", &tm);
701 Mmsg(mdb->cmd, "INSERT INTO FileSet (FileSet,MD5,CreateTime) "
702 "VALUES ('%s','%s','%s')", fsr->FileSet, fsr->MD5, fsr->cCreateTime);
704 fsr->FileSetId = sql_insert_autokey_record(mdb, mdb->cmd, NT_("FileSet"));
705 if (fsr->FileSetId == 0) {
706 Mmsg2(&mdb->errmsg, _("Create DB FileSet record %s failed. ERR=%s\n"),
707 mdb->cmd, sql_strerror(mdb));
708 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
723 * dev_t st_dev; * device *
724 * ino_t st_ino; * inode *
725 * mode_t st_mode; * protection *
726 * nlink_t st_nlink; * number of hard links *
727 * uid_t st_uid; * user ID of owner *
728 * gid_t st_gid; * group ID of owner *
729 * dev_t st_rdev; * device type (if inode device) *
730 * off_t st_size; * total size, in bytes *
731 * unsigned long st_blksize; * blocksize for filesystem I/O *
732 * unsigned long st_blocks; * number of blocks allocated *
733 * time_t st_atime; * time of last access *
734 * time_t st_mtime; * time of last modification *
735 * time_t st_ctime; * time of last inode change *
740 * All sql_batch_* functions are used to do bulk batch insert in File/Filename/Path
744 * - bulk load a temp table
745 * - insert missing filenames into filename with a single query (lock filenames
746 * - table before that to avoid possible duplicate inserts with concurrent update)
747 * - insert missing paths into path with another single query
748 * - then insert the join between the temp, filename and path tables into file.
755 bool db_write_batch_file_records(JCR *jcr)
758 int JobStatus = jcr->JobStatus;
760 if (!jcr->batch_started) { /* no files to backup ? */
761 Dmsg0(50,"db_create_file_record : no files\n");
764 if (job_canceled(jcr)) {
768 Dmsg1(50,"db_create_file_record changes=%u\n",jcr->db_batch->changes);
770 jcr->JobStatus = JS_AttrInserting;
771 if (!sql_batch_end(jcr, jcr->db_batch, NULL)) {
772 Jmsg1(jcr, M_FATAL, 0, "Batch end %s\n", jcr->db_batch->errmsg);
775 if (job_canceled(jcr)) {
780 * We have to lock tables
782 if (!db_sql_query(jcr->db_batch, batch_lock_path_query[db_get_type_index(jcr->db_batch)], NULL, NULL)) {
783 Jmsg1(jcr, M_FATAL, 0, "Lock Path table %s\n", jcr->db_batch->errmsg);
787 if (!db_sql_query(jcr->db_batch, batch_fill_path_query[db_get_type_index(jcr->db_batch)], NULL, NULL)) {
788 Jmsg1(jcr, M_FATAL, 0, "Fill Path table %s\n",jcr->db_batch->errmsg);
789 db_sql_query(jcr->db_batch, batch_unlock_tables_query[db_get_type_index(jcr->db_batch)], NULL, NULL);
793 if (!db_sql_query(jcr->db_batch, batch_unlock_tables_query[db_get_type_index(jcr->db_batch)], NULL, NULL)) {
794 Jmsg1(jcr, M_FATAL, 0, "Unlock Path table %s\n", jcr->db_batch->errmsg);
799 * We have to lock tables
801 if (!db_sql_query(jcr->db_batch, batch_lock_filename_query[db_get_type_index(jcr->db_batch)], NULL, NULL)) {
802 Jmsg1(jcr, M_FATAL, 0, "Lock Filename table %s\n", jcr->db_batch->errmsg);
806 if (!db_sql_query(jcr->db_batch, batch_fill_filename_query[db_get_type_index(jcr->db_batch)], NULL, NULL)) {
807 Jmsg1(jcr,M_FATAL,0,"Fill Filename table %s\n",jcr->db_batch->errmsg);
808 db_sql_query(jcr->db_batch, batch_unlock_tables_query[db_get_type_index(jcr->db_batch)], NULL, NULL);
812 if (!db_sql_query(jcr->db_batch, batch_unlock_tables_query[db_get_type_index(jcr->db_batch)], NULL, NULL)) {
813 Jmsg1(jcr, M_FATAL, 0, "Unlock Filename table %s\n", jcr->db_batch->errmsg);
817 if (!db_sql_query(jcr->db_batch,
818 "INSERT INTO File (FileIndex, JobId, PathId, FilenameId, LStat, MD5, MarkId) "
819 "SELECT batch.FileIndex, batch.JobId, Path.PathId, "
820 "Filename.FilenameId,batch.LStat, batch.MD5, batch.MarkId "
822 "JOIN Path ON (batch.Path = Path.Path) "
823 "JOIN Filename ON (batch.Name = Filename.Name)",
826 Jmsg1(jcr, M_FATAL, 0, "Fill File table %s\n", jcr->db_batch->errmsg);
830 jcr->JobStatus = JobStatus; /* reset entry status */
834 db_sql_query(jcr->db_batch, "DROP TABLE batch", NULL,NULL);
840 * Create File record in B_DB
842 * In order to reduce database size, we store the File attributes,
843 * the FileName, and the Path separately. In principle, there
844 * is a single FileName record and a single Path record, no matter
845 * how many times it occurs. This is this subroutine, we separate
846 * the file and the path and fill temporary tables with this three records.
848 * Note: all routines that call this expect to be able to call
849 * db_strerror(mdb) to get the error message, so the error message
850 * MUST be edited into mdb->errmsg before returning an error status.
852 bool db_create_batch_file_attributes_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
854 ASSERT(ar->FileType != FT_BASE);
856 Dmsg1(dbglevel, "Fname=%s\n", ar->fname);
857 Dmsg0(dbglevel, "put_file_into_catalog\n");
859 /* Open the dedicated connexion */
860 if (!jcr->batch_started) {
861 if (!db_open_batch_connexion(jcr, mdb)) {
862 return false; /* error already printed */
864 if (!sql_batch_start(jcr, jcr->db_batch)) {
866 "Can't start batch mode: ERR=%s", db_strerror(jcr->db_batch));
867 Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
870 jcr->batch_started = true;
872 B_DB *bdb = jcr->db_batch;
874 split_path_and_file(jcr, bdb, ar->fname);
878 * if (bdb->changes > 100000) {
879 * db_write_batch_file_records(jcr);
881 * sql_batch_start(jcr, bdb);
885 return sql_batch_insert(jcr, bdb, ar);
889 * Create File record in B_DB
891 * In order to reduce database size, we store the File attributes,
892 * the FileName, and the Path separately. In principle, there
893 * is a single FileName record and a single Path record, no matter
894 * how many times it occurs. This is this subroutine, we separate
895 * the file and the path and create three database records.
897 bool db_create_file_attributes_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
900 Dmsg1(dbglevel, "Fname=%s\n", ar->fname);
901 Dmsg0(dbglevel, "put_file_into_catalog\n");
903 split_path_and_file(jcr, mdb, ar->fname);
905 if (!db_create_filename_record(jcr, mdb, ar)) {
908 Dmsg1(dbglevel, "db_create_filename_record: %s\n", mdb->esc_name);
911 if (!db_create_path_record(jcr, mdb, ar)) {
914 Dmsg1(dbglevel, "db_create_path_record: %s\n", mdb->esc_name);
916 /* Now create master File record */
917 if (!db_create_file_record(jcr, mdb, ar)) {
920 Dmsg0(dbglevel, "db_create_file_record OK\n");
922 Dmsg3(dbglevel, "CreateAttributes Path=%s File=%s FilenameId=%d\n", mdb->path, mdb->fname, ar->FilenameId);
931 * This is the master File entry containing the attributes.
932 * The filename and path records have already been created.
934 static int db_create_file_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
937 static const char *no_digest = "0";
942 ASSERT(ar->FilenameId);
944 if (ar->Digest == NULL || ar->Digest[0] == 0) {
952 "INSERT INTO File (FileIndex,JobId,PathId,FilenameId,"
953 "LStat,MD5,MarkId) VALUES (%u,%u,%u,%u,'%s','%s',%u)",
954 ar->FileIndex, ar->JobId, ar->PathId, ar->FilenameId,
955 ar->attr, digest, ar->DeltaSeq);
957 ar->FileId = sql_insert_autokey_record(mdb, mdb->cmd, NT_("File"));
958 if (ar->FileId == 0) {
959 Mmsg2(&mdb->errmsg, _("Create db File record %s failed. ERR=%s"),
960 mdb->cmd, sql_strerror(mdb));
961 Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
969 /** Create a Unique record for the filename -- no duplicates */
970 static int db_create_filename_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
975 mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->fnl+2);
976 db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
978 Mmsg(mdb->cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", mdb->esc_name);
980 if (QUERY_DB(jcr, mdb, mdb->cmd)) {
981 num_rows = sql_num_rows(mdb);
984 Mmsg2(&mdb->errmsg, _("More than one Filename! %s for file: %s\n"),
985 edit_uint64(num_rows, ed1), mdb->fname);
986 Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
989 if ((row = sql_fetch_row(mdb)) == NULL) {
990 Mmsg2(&mdb->errmsg, _("Error fetching row for file=%s: ERR=%s\n"),
991 mdb->fname, sql_strerror(mdb));
992 Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
995 ar->FilenameId = str_to_int64(row[0]);
997 sql_free_result(mdb);
998 return ar->FilenameId > 0;
1000 sql_free_result(mdb);
1003 Mmsg(mdb->cmd, "INSERT INTO Filename (Name) VALUES ('%s')", mdb->esc_name);
1005 ar->FilenameId = sql_insert_autokey_record(mdb, mdb->cmd, NT_("Filename"));
1006 if (ar->FilenameId == 0) {
1007 Mmsg2(&mdb->errmsg, _("Create db Filename record %s failed. ERR=%s\n"),
1008 mdb->cmd, sql_strerror(mdb));
1009 Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
1011 return ar->FilenameId > 0;
1015 * Create file attributes record, or base file attributes record
1017 bool db_create_attributes_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
1022 * Make sure we have an acceptable attributes record.
1024 if (!(ar->Stream == STREAM_UNIX_ATTRIBUTES ||
1025 ar->Stream == STREAM_UNIX_ATTRIBUTES_EX)) {
1026 Jmsg(jcr, M_FATAL, 0, _("Attempt to put non-attributes into catalog. Stream=%d\n"));
1030 if (ar->FileType != FT_BASE) {
1031 if (mdb->batch_insert_available()) {
1032 ret = db_create_batch_file_attributes_record(jcr, mdb, ar);
1034 ret = db_create_file_attributes_record(jcr, mdb, ar);
1036 } else if (jcr->HasBase) {
1037 ret = db_create_base_file_attributes_record(jcr, mdb, ar);
1039 Jmsg0(jcr, M_FATAL, 0, _("Can't Copy/Migrate job using BaseJob"));
1040 ret = true; /* in copy/migration what do we do ? */
1047 * Create Base File record in B_DB
1050 bool db_create_base_file_attributes_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
1053 Dmsg1(dbglevel, "create_base_file Fname=%s\n", ar->fname);
1054 Dmsg0(dbglevel, "put_base_file_into_catalog\n");
1057 split_path_and_file(jcr, mdb, ar->fname);
1059 mdb->esc_name = check_pool_memory_size(mdb->esc_name, mdb->fnl*2+1);
1060 db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
1062 mdb->esc_path = check_pool_memory_size(mdb->esc_path, mdb->pnl*2+1);
1063 db_escape_string(jcr, mdb, mdb->esc_path, mdb->path, mdb->pnl);
1065 Mmsg(mdb->cmd, "INSERT INTO basefile%lld (Path, Name) VALUES ('%s','%s')",
1066 (uint64_t)jcr->JobId, mdb->esc_path, mdb->esc_name);
1068 ret = INSERT_DB(jcr, mdb, mdb->cmd);
1075 * Cleanup the base file temporary tables
1077 static void db_cleanup_base_file(JCR *jcr, B_DB *mdb)
1079 POOL_MEM buf(PM_MESSAGE);
1080 Mmsg(buf, "DROP TABLE new_basefile%lld", (uint64_t) jcr->JobId);
1081 db_sql_query(mdb, buf.c_str(), NULL, NULL);
1083 Mmsg(buf, "DROP TABLE basefile%lld", (uint64_t) jcr->JobId);
1084 db_sql_query(mdb, buf.c_str(), NULL, NULL);
1088 * Put all base file seen in the backup to the BaseFile table
1089 * and cleanup temporary tables
1091 bool db_commit_base_file_attributes_record(JCR *jcr, B_DB *mdb)
1099 "INSERT INTO BaseFiles (BaseJobId, JobId, FileId, FileIndex) "
1100 "SELECT B.JobId AS BaseJobId, %s AS JobId, "
1101 "B.FileId, B.FileIndex "
1102 "FROM basefile%s AS A, new_basefile%s AS B "
1103 "WHERE A.Path = B.Path "
1104 "AND A.Name = B.Name "
1105 "ORDER BY B.FileId",
1106 edit_uint64(jcr->JobId, ed1), ed1, ed1);
1107 ret = db_sql_query(mdb, mdb->cmd, NULL, NULL);
1108 jcr->nb_base_files_used = sql_affected_rows(mdb);
1109 db_cleanup_base_file(jcr, mdb);
1116 * Find the last "accurate" backup state with Base jobs
1117 * 1) Get all files with jobid in list (F subquery)
1118 * 2) Take only the last version of each file (Temp subquery) => accurate list is ok
1119 * 3) Put the result in a temporary table for the end of job
1122 bool db_create_base_file_list(JCR *jcr, B_DB *mdb, char *jobids)
1130 Mmsg(mdb->errmsg, _("ERR=JobIds are empty\n"));
1134 Mmsg(mdb->cmd, create_temp_basefile[db_get_type_index(mdb)], (uint64_t) jcr->JobId);
1135 if (!db_sql_query(mdb, mdb->cmd, NULL, NULL)) {
1138 Mmsg(buf, select_recent_version[db_get_type_index(mdb)], jobids, jobids);
1139 Mmsg(mdb->cmd, create_temp_new_basefile[db_get_type_index(mdb)], (uint64_t)jcr->JobId, buf.c_str());
1141 ret = db_sql_query(mdb, mdb->cmd, NULL, NULL);
1148 * Create Restore Object record in B_DB
1151 bool db_create_restore_object_record(JCR *jcr, B_DB *mdb, ROBJECT_DBR *ro)
1155 POOLMEM *esc_plug_name = get_pool_memory(PM_MESSAGE);
1159 Dmsg1(dbglevel, "Oname=%s\n", ro->object_name);
1160 Dmsg0(dbglevel, "put_object_into_catalog\n");
1162 mdb->fnl = strlen(ro->object_name);
1163 mdb->esc_name = check_pool_memory_size(mdb->esc_name, mdb->fnl*2+1);
1164 db_escape_string(jcr, mdb, mdb->esc_name, ro->object_name, mdb->fnl);
1166 db_escape_object(jcr, mdb, ro->object, ro->object_len);
1168 plug_name_len = strlen(ro->plugin_name);
1169 esc_plug_name = check_pool_memory_size(esc_plug_name, plug_name_len*2+1);
1170 db_escape_string(jcr, mdb, esc_plug_name, ro->plugin_name, plug_name_len);
1173 "INSERT INTO RestoreObject (ObjectName,PluginName,RestoreObject,"
1174 "ObjectLength,ObjectFullLength,ObjectIndex,ObjectType,"
1175 "ObjectCompression,FileIndex,JobId) "
1176 "VALUES ('%s','%s','%s',%d,%d,%d,%d,%d,%d,%u)",
1177 mdb->esc_name, esc_plug_name, mdb->esc_obj,
1178 ro->object_len, ro->object_full_len, ro->object_index,
1179 FT_RESTORE_FIRST, ro->object_compression, ro->FileIndex, ro->JobId);
1181 ro->RestoreObjectId = sql_insert_autokey_record(mdb, mdb->cmd, NT_("RestoreObject"));
1182 if (ro->RestoreObjectId == 0) {
1183 Mmsg2(&mdb->errmsg, _("Create db Object record %s failed. ERR=%s"),
1184 mdb->cmd, sql_strerror(mdb));
1185 Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
1191 free_pool_memory(esc_plug_name);
1195 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI */