1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2016 Planets Communications B.V.
6 Copyright (C) 2013-2019 Bareos GmbH & Co. KG
7
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
11 in the file LICENSE.
12
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 Affero General Public License for more details.
17
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
21 02110-1301, USA.
22 */
23 /*
24 * Kern Sibbald, March 2000
25 */
26 /**
27 * @file
28 * BAREOS Catalog Database interface routines
29 *
30 * Almost generic set of SQL database interface routines
31 * (with a little more work) SQL engine specific routines are in
32 * mysql.c, postgresql.c, sqlite.c, ...
33 */
34
35 #include "include/bareos.h"
36
37 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI
38
39 #include "cats.h"
40 #include "lib/edit.h"
41
42 /* Forward referenced subroutines */
43
dbid_list()44 dbid_list::dbid_list()
45 {
46 max_ids = 1000;
47 DBId = (DBId_t*)malloc(max_ids * sizeof(DBId_t));
48 num_ids = num_seen = tot_ids = 0;
49 PurgedFiles = nullptr;
50 }
51
~dbid_list()52 dbid_list::~dbid_list() { free(DBId); }
53
get(int i) const54 DBId_t dbid_list::get(int i) const
55 {
56 if (i >= size()) {
57 Emsg2(
58 M_ERROR_TERM, 0,
59 _("Unable to access dbid_list entry %d. Only %d entries available.\n"),
60 i, size());
61 return (DBId_t)0;
62 }
63 return DBId[i];
64 }
65
66
67 /**
68 * Called here to retrieve an integer from the database
69 */
DbIntHandler(void * ctx,int num_fields,char ** row)70 int DbIntHandler(void* ctx, int num_fields, char** row)
71 {
72 uint32_t* val = (uint32_t*)ctx;
73
74 Dmsg1(800, "int_handler starts with row pointing at %x\n", row);
75
76 if (row[0]) {
77 Dmsg1(800, "int_handler finds '%s'\n", row[0]);
78 *val = str_to_int64(row[0]);
79 } else {
80 Dmsg0(800, "int_handler finds zero\n");
81 *val = 0;
82 }
83 Dmsg0(800, "int_handler finishes\n");
84 return 0;
85 }
86
87 /**
88 * Called here to retrieve a 32/64 bit integer from the database.
89 * The returned integer will be extended to 64 bit.
90 */
db_int64_handler(void * ctx,int num_fields,char ** row)91 int db_int64_handler(void* ctx, int num_fields, char** row)
92 {
93 db_int64_ctx* lctx = (db_int64_ctx*)ctx;
94
95 if (row[0]) {
96 lctx->value = str_to_int64(row[0]);
97 lctx->count++;
98 }
99 return 0;
100 }
101
102 /**
103 * Called here to retrieve a btime from the database.
104 * The returned integer will be extended to 64 bit.
105 */
DbStrtimeHandler(void * ctx,int num_fields,char ** row)106 int DbStrtimeHandler(void* ctx, int num_fields, char** row)
107 {
108 db_int64_ctx* lctx = (db_int64_ctx*)ctx;
109
110 if (row[0]) {
111 lctx->value = StrToUtime(row[0]);
112 lctx->count++;
113 }
114 return 0;
115 }
116
117 /**
118 * Use to build a comma separated list of values from a query. "10,20,30"
119 */
DbListHandler(void * ctx,int num_fields,char ** row)120 int DbListHandler(void* ctx, int num_fields, char** row)
121 {
122 db_list_ctx* lctx = (db_list_ctx*)ctx;
123 if (num_fields == 1 && row[0]) { lctx->add(row[0]); }
124 return 0;
125 }
126
127 /**
128 * specific context passed from db_check_max_connections to
129 * DbMaxConnectionsHandler.
130 */
131 struct max_connections_context {
132 BareosDb* db;
133 uint32_t nr_connections;
134 };
135
136 /**
137 * Called here to retrieve an integer from the database
138 */
DbMaxConnectionsHandler(void * ctx,int num_fields,char ** row)139 static inline int DbMaxConnectionsHandler(void* ctx, int num_fields, char** row)
140 {
141 struct max_connections_context* context;
142 uint32_t index;
143
144 context = (struct max_connections_context*)ctx;
145 switch (context->db->GetTypeIndex()) {
146 case SQL_TYPE_MYSQL:
147 index = 1;
148 break;
149 default:
150 index = 0;
151 break;
152 }
153
154 if (row[index]) {
155 context->nr_connections = str_to_int64(row[index]);
156 } else {
157 Dmsg0(800, "int_handler finds zero\n");
158 context->nr_connections = 0;
159 }
160 return 0;
161 }
162
163 /**
164 * Check catalog max_connections setting
165 */
CheckMaxConnections(JobControlRecord * jcr,uint32_t max_concurrent_jobs)166 bool BareosDb::CheckMaxConnections(JobControlRecord* jcr,
167 uint32_t max_concurrent_jobs)
168 {
169 PoolMem query(PM_MESSAGE);
170 struct max_connections_context context;
171
172 /*
173 * Without Batch insert, no need to verify max_connections
174 */
175 if (!BatchInsertAvailable()) return true;
176
177 context.db = this;
178 context.nr_connections = 0;
179
180 /*
181 * Check max_connections setting
182 */
183 FillQuery(query, SQL_QUERY::sql_get_max_connections);
184 if (!SqlQueryWithHandler(query.c_str(), DbMaxConnectionsHandler, &context)) {
185 Jmsg(jcr, M_ERROR, 0, "Can't verify max_connections settings %s", errmsg);
186 return false;
187 }
188
189 if (context.nr_connections && max_concurrent_jobs &&
190 max_concurrent_jobs > context.nr_connections) {
191 Mmsg(errmsg,
192 _("Potential performance problem:\n"
193 "max_connections=%d set for %s database \"%s\" should be larger "
194 "than Director's "
195 "MaxConcurrentJobs=%d\n"),
196 context.nr_connections, GetType(), get_db_name(), max_concurrent_jobs);
197 Jmsg(jcr, M_WARNING, 0, "%s", errmsg);
198 return false;
199 }
200
201 return true;
202 }
203
204 /* NOTE!!! The following routines expect that the
205 * calling subroutine sets and clears the mutex
206 */
207
208 /*
209 * Check that the tables correspond to the version we want
210 */
CheckTablesVersion(JobControlRecord * jcr)211 bool BareosDb::CheckTablesVersion(JobControlRecord* jcr)
212 {
213 uint32_t bareos_db_version = 0;
214 const char* query = "SELECT VersionId FROM Version";
215
216 if (!SqlQueryWithHandler(query, DbIntHandler, (void*)&bareos_db_version)) {
217 Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
218 return false;
219 }
220
221 if (bareos_db_version != BDB_VERSION) {
222 Mmsg(errmsg, "Version error for database \"%s\". Wanted %d, got %d\n",
223 get_db_name(), BDB_VERSION, bareos_db_version);
224 Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
225 return false;
226 }
227
228 return true;
229 }
230
231 /**
232 * Utility routine for queries. The database MUST be locked before calling here.
233 * Returns: false on failure
234 * true on success
235 */
QueryDB(const char * file,int line,JobControlRecord * jcr,const char * select_cmd)236 bool BareosDb::QueryDB(const char* file,
237 int line,
238 JobControlRecord* jcr,
239 const char* select_cmd)
240 {
241 SqlFreeResult();
242 Dmsg1(1000, "query: %s\n", select_cmd);
243 if (!SqlQuery(select_cmd, QF_STORE_RESULT)) {
244 msg_(file, line, errmsg, _("query %s failed:\n%s\n"), select_cmd,
245 sql_strerror());
246 j_msg(file, line, jcr, M_FATAL, 0, "%s", errmsg);
247 if (verbose) { j_msg(file, line, jcr, M_INFO, 0, "%s\n", select_cmd); }
248 return false;
249 }
250
251 return true;
252 }
253
254 /**
255 * Utility routine to do inserts
256 * Returns: false on failure
257 * true on success
258 */
InsertDB(const char * file,int line,JobControlRecord * jcr,const char * select_cmd)259 bool BareosDb::InsertDB(const char* file,
260 int line,
261 JobControlRecord* jcr,
262 const char* select_cmd)
263 {
264 int num_rows;
265
266 if (!SqlQuery(select_cmd)) {
267 msg_(file, line, errmsg, _("insert %s failed:\n%s\n"), select_cmd,
268 sql_strerror());
269 j_msg(file, line, jcr, M_FATAL, 0, "%s", errmsg);
270 if (verbose) { j_msg(file, line, jcr, M_INFO, 0, "%s\n", select_cmd); }
271 return false;
272 }
273 num_rows = SqlAffectedRows();
274 if (num_rows != 1) {
275 char ed1[30];
276 msg_(file, line, errmsg, _("Insertion problem: affected_rows=%s\n"),
277 edit_uint64(num_rows, ed1));
278 if (verbose) { j_msg(file, line, jcr, M_INFO, 0, "%s\n", select_cmd); }
279 return false;
280 }
281 changes++;
282 return true;
283 }
284
285 /**
286 * Utility routine for updates.
287 * Returns: false on failure
288 * true on success
289 */
UpdateDB(const char * file,int line,JobControlRecord * jcr,const char * UpdateCmd,int nr_afr)290 bool BareosDb::UpdateDB(const char* file,
291 int line,
292 JobControlRecord* jcr,
293 const char* UpdateCmd,
294 int nr_afr)
295 {
296 int num_rows;
297
298 if (!SqlQuery(UpdateCmd)) {
299 msg_(file, line, errmsg, _("update %s failed:\n%s\n"), UpdateCmd,
300 sql_strerror());
301 j_msg(file, line, jcr, M_ERROR, 0, "%s", errmsg);
302 if (verbose) { j_msg(file, line, jcr, M_INFO, 0, "%s\n", UpdateCmd); }
303 return false;
304 }
305
306 if (nr_afr > 0) {
307 num_rows = SqlAffectedRows();
308 if (num_rows < nr_afr) {
309 char ed1[30];
310 msg_(file, line, errmsg, _("Update failed: affected_rows=%s for %s\n"),
311 edit_uint64(num_rows, ed1), UpdateCmd);
312 if (verbose) {
313 // j_msg(file, line, jcr, M_INFO, 0, "%s\n", UpdateCmd);
314 }
315 return false;
316 }
317 }
318
319 changes++;
320 return true;
321 }
322
323 /**
324 * Utility routine for deletes
325 *
326 * Returns: -1 on error
327 * n number of rows affected
328 */
DeleteDB(const char * file,int line,JobControlRecord * jcr,const char * DeleteCmd)329 int BareosDb::DeleteDB(const char* file,
330 int line,
331 JobControlRecord* jcr,
332 const char* DeleteCmd)
333 {
334 if (!SqlQuery(DeleteCmd)) {
335 msg_(file, line, errmsg, _("delete %s failed:\n%s\n"), DeleteCmd,
336 sql_strerror());
337 j_msg(file, line, jcr, M_ERROR, 0, "%s", errmsg);
338 if (verbose) { j_msg(file, line, jcr, M_INFO, 0, "%s\n", DeleteCmd); }
339 return -1;
340 }
341 changes++;
342 return SqlAffectedRows();
343 }
344
345 /**
346 * Get record max. Query is already in mdb->cmd
347 * No locking done
348 *
349 * Returns: -1 on failure
350 * count on success
351 */
GetSqlRecordMax(JobControlRecord * jcr)352 int BareosDb::GetSqlRecordMax(JobControlRecord* jcr)
353 {
354 SQL_ROW row;
355 int retval = 0;
356
357 if (QUERY_DB(jcr, cmd)) {
358 if ((row = SqlFetchRow()) == NULL) {
359 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
360 retval = -1;
361 } else {
362 retval = str_to_int64(row[0]);
363 }
364 SqlFreeResult();
365 } else {
366 Mmsg1(errmsg, _("error fetching row: %s\n"), sql_strerror());
367 retval = -1;
368 }
369 return retval;
370 }
371
372 /**
373 * Return pre-edited error message
374 */
strerror()375 char* BareosDb::strerror() { return errmsg; }
376
377 /**
378 * Given a full filename, split it into its path
379 * and filename parts. They are returned in pool memory
380 * in the mdb structure.
381 */
SplitPathAndFile(JobControlRecord * jcr,const char * filename)382 void BareosDb::SplitPathAndFile(JobControlRecord* jcr, const char* filename)
383 {
384 const char *p, *f;
385
386 /* Find path without the filename.
387 * I.e. everything after the last / is a "filename".
388 * OK, maybe it is a directory name, but we treat it like
389 * a filename. If we don't find a / then the whole name
390 * must be a path name (e.g. c:).
391 */
392 for (p = f = filename; *p; p++) {
393 if (IsPathSeparator(*p)) { f = p; /* set pos of last slash */ }
394 }
395 if (IsPathSeparator(*f)) { /* did we find a slash? */
396 f++; /* yes, point to filename */
397 } else {
398 f = p; /* no, whole thing must be path name */
399 }
400
401 /* If filename doesn't exist (i.e. root directory), we
402 * simply create a blank name consisting of a single
403 * space. This makes handling zero length filenames
404 * easier.
405 */
406 fnl = p - f;
407 if (fnl > 0) {
408 fname = CheckPoolMemorySize(fname, fnl + 1);
409 memcpy(fname, f, fnl); /* copy filename */
410 fname[fnl] = 0;
411 } else {
412 fname[0] = 0;
413 fnl = 0;
414 }
415
416 pnl = f - filename;
417 if (pnl > 0) {
418 path = CheckPoolMemorySize(path, pnl + 1);
419 memcpy(path, filename, pnl);
420 path[pnl] = 0;
421 } else {
422 Mmsg1(errmsg, _("Path length is zero. File=%s\n"), fname);
423 Jmsg(jcr, M_ERROR, 0, "%s", errmsg);
424 path[0] = 0;
425 pnl = 0;
426 }
427
428 Dmsg2(500, "split path=%s file=%s\n", path, fname);
429 }
430
431 /**
432 * Set maximum field length to something reasonable
433 */
MaxLength(int MaxLength)434 static int MaxLength(int MaxLength)
435 {
436 int max_len = MaxLength;
437 /* Sanity check */
438 if (max_len < 0) {
439 max_len = 2;
440 } else if (max_len > 100) {
441 max_len = 100;
442 }
443 return max_len;
444 }
445
446 /**
447 * List dashes as part of header for listing SQL results in a table
448 */
ListDashes(OutputFormatter * send)449 void BareosDb::ListDashes(OutputFormatter* send)
450 {
451 int len;
452 int num_fields;
453 SQL_FIELD* field;
454
455 SqlFieldSeek(0);
456 send->Decoration("+");
457 num_fields = SqlNumFields();
458 for (int i = 0; i < num_fields; i++) {
459 field = SqlFetchField();
460 if (!field) { break; }
461 len = MaxLength(field->max_length + 2);
462 for (int j = 0; j < len; j++) { send->Decoration("-"); }
463 send->Decoration("+");
464 }
465 send->Decoration("\n");
466 }
467
468 /**
469 * List result handler used by queries done with db_big_sql_query()
470 */
ListResult(void * vctx,int nb_col,char ** row)471 int BareosDb::ListResult(void* vctx, int nb_col, char** row)
472 {
473 JobControlRecord* jcr;
474 char ewc[30];
475 PoolMem key;
476 PoolMem value;
477 int num_fields;
478 SQL_FIELD* field;
479 e_list_type type;
480 OutputFormatter* send;
481 int col_len, max_len = 0;
482 ListContext* pctx = (ListContext*)vctx;
483
484 /*
485 * Get pointers from context.
486 */
487 type = pctx->type;
488 send = pctx->send;
489 jcr = pctx->jcr;
490
491 /*
492 * See if this row must be filtered.
493 */
494 if (send->HasFilters() && !send->FilterData(row)) { return 0; }
495
496 send->ObjectStart();
497
498 num_fields = SqlNumFields();
499 switch (type) {
500 case NF_LIST:
501 case RAW_LIST:
502 /*
503 * No need to calculate things like maximum field lenght for
504 * unformated or raw output.
505 */
506 break;
507 case HORZ_LIST:
508 case VERT_LIST:
509 if (!pctx->once) {
510 pctx->once = true;
511
512 Dmsg1(800, "ListResult starts looking at %d fields\n", num_fields);
513 /*
514 * Determine column display widths
515 */
516 SqlFieldSeek(0);
517 for (int i = 0; i < num_fields; i++) {
518 Dmsg1(800, "ListResult processing field %d\n", i);
519 field = SqlFetchField();
520 if (!field) { break; }
521
522 /*
523 * See if this is a hidden column.
524 */
525 if (send->IsHiddenColumn(i)) {
526 Dmsg1(800, "ListResult field %d is hidden\n", i);
527 continue;
528 }
529
530 col_len = cstrlen(field->name);
531 if (type == VERT_LIST) {
532 if (col_len > max_len) { max_len = col_len; }
533 } else {
534 if (SqlFieldIsNumeric(field->type) &&
535 (int)field->max_length > 0) { /* fixup for commas */
536 field->max_length += (field->max_length - 1) / 3;
537 }
538 if (col_len < (int)field->max_length) {
539 col_len = field->max_length;
540 }
541 if (col_len < 4 && !SqlFieldIsNotNull(field->flags)) {
542 col_len = 4; /* 4 = length of the word "NULL" */
543 }
544 field->max_length = col_len; /* reset column info */
545 }
546 }
547
548 pctx->num_rows++;
549
550 Dmsg0(800, "ListResult finished first loop\n");
551 if (type == VERT_LIST) { break; }
552
553 Dmsg1(800, "ListResult starts second loop looking at %d fields\n",
554 num_fields);
555
556 /*
557 * Keep the result to display the same line at the end of the table
558 */
559 ListDashes(send);
560
561 send->Decoration("|");
562 SqlFieldSeek(0);
563 for (int i = 0; i < num_fields; i++) {
564 Dmsg1(800, "ListResult looking at field %d\n", i);
565
566 field = SqlFetchField();
567 if (!field) { break; }
568
569 /*
570 * See if this is a hidden column.
571 */
572 if (send->IsHiddenColumn(i)) {
573 Dmsg1(800, "ListResult field %d is hidden\n", i);
574 continue;
575 }
576
577 max_len = MaxLength(field->max_length);
578 send->Decoration(" %-*s |", max_len, field->name);
579 }
580 send->Decoration("\n");
581 ListDashes(send);
582 }
583 break;
584 default:
585 break;
586 }
587
588 switch (type) {
589 case NF_LIST:
590 case RAW_LIST:
591 Dmsg1(800, "ListResult starts third loop looking at %d fields\n",
592 num_fields);
593 SqlFieldSeek(0);
594 for (int i = 0; i < num_fields; i++) {
595 field = SqlFetchField();
596 if (!field) { break; }
597
598 /*
599 * See if this is a hidden column.
600 */
601 if (send->IsHiddenColumn(i)) {
602 Dmsg1(800, "ListResult field %d is hidden\n", i);
603 continue;
604 }
605
606 if (row[i] == NULL) {
607 value.bsprintf("%s", "NULL");
608 } else {
609 value.bsprintf("%s", row[i]);
610 }
611 send->ObjectKeyValue(field->name, value.c_str(), " %s");
612 }
613 if (type != RAW_LIST) { send->Decoration("\n"); }
614 break;
615 case HORZ_LIST:
616 Dmsg1(800, "ListResult starts third loop looking at %d fields\n",
617 num_fields);
618 SqlFieldSeek(0);
619 send->Decoration("|");
620 for (int i = 0; i < num_fields; i++) {
621 field = SqlFetchField();
622 if (!field) { break; }
623
624 /*
625 * See if this is a hidden column.
626 */
627 if (send->IsHiddenColumn(i)) {
628 Dmsg1(800, "ListResult field %d is hidden\n", i);
629 continue;
630 }
631
632 max_len = MaxLength(field->max_length);
633 if (row[i] == NULL) {
634 value.bsprintf(" %-*s |", max_len, "NULL");
635 } else if (SqlFieldIsNumeric(field->type) && !jcr->gui &&
636 IsAnInteger(row[i])) {
637 value.bsprintf(" %*s |", max_len, add_commas(row[i], ewc));
638 } else {
639 value.bsprintf(" %-*s |", max_len, row[i]);
640 }
641
642 /*
643 * Use value format string to send preformated value.
644 */
645 send->ObjectKeyValue(field->name, row[i], value.c_str());
646 }
647 send->Decoration("\n");
648 break;
649 case VERT_LIST:
650 Dmsg1(800, "ListResult starts vertical list at %d fields\n", num_fields);
651 SqlFieldSeek(0);
652 for (int i = 0; i < num_fields; i++) {
653 field = SqlFetchField();
654 if (!field) { break; }
655
656 /*
657 * See if this is a hidden column.
658 */
659 if (send->IsHiddenColumn(i)) {
660 Dmsg1(800, "ListResult field %d is hidden\n", i);
661 continue;
662 }
663
664 if (row[i] == NULL) {
665 key.bsprintf(" %*s: ", max_len, field->name);
666 value.bsprintf("%s\n", "NULL");
667 } else if (SqlFieldIsNumeric(field->type) && !jcr->gui &&
668 IsAnInteger(row[i])) {
669 key.bsprintf(" %*s: ", max_len, field->name);
670 value.bsprintf("%s\n", add_commas(row[i], ewc));
671 } else {
672 key.bsprintf(" %*s: ", max_len, field->name);
673 value.bsprintf("%s\n", row[i]);
674 }
675
676 /*
677 * Use value format string to send preformated value.
678 */
679 send->ObjectKeyValue(field->name, key.c_str(), row[i], value.c_str());
680 }
681 send->Decoration("\n");
682 break;
683 default:
684 break;
685 }
686 send->ObjectEnd();
687
688 return 0;
689 }
690
ListResult(void * vctx,int nb_col,char ** row)691 int ListResult(void* vctx, int nb_col, char** row)
692 {
693 ListContext* pctx = (ListContext*)vctx;
694 BareosDb* mdb = pctx->mdb;
695
696 return mdb->ListResult(vctx, nb_col, row);
697 }
698
699 /**
700 * If full_list is set, we list vertically, otherwise, we list on one line
701 * horizontally.
702 *
703 * Return number of rows
704 */
ListResult(JobControlRecord * jcr,OutputFormatter * send,e_list_type type)705 int BareosDb::ListResult(JobControlRecord* jcr,
706 OutputFormatter* send,
707 e_list_type type)
708 {
709 SQL_ROW row;
710 char ewc[30];
711 PoolMem key;
712 PoolMem value;
713 int num_fields;
714 SQL_FIELD* field;
715 bool filters_enabled;
716 int col_len, max_len = 0;
717
718 Dmsg0(800, "ListResult starts\n");
719 if (SqlNumRows() == 0) {
720 send->Decoration(_("No results to list.\n"));
721 return 0;
722 }
723
724 num_fields = SqlNumFields();
725 switch (type) {
726 case E_LIST_INIT:
727 case NF_LIST:
728 case RAW_LIST:
729 /*
730 * No need to calculate things like column widths for unformatted or raw
731 * output.
732 */
733 break;
734 case HORZ_LIST:
735 case VERT_LIST:
736 Dmsg1(800, "ListResult starts looking at %d fields\n", num_fields);
737 /*
738 * Determine column display widths
739 */
740 SqlFieldSeek(0);
741 for (int i = 0; i < num_fields; i++) {
742 Dmsg1(800, "ListResult processing field %d\n", i);
743
744 field = SqlFetchField();
745 if (!field) { break; }
746
747 /*
748 * See if this is a hidden column.
749 */
750 if (send->IsHiddenColumn(i)) {
751 Dmsg1(800, "ListResult field %d is hidden\n", i);
752 continue;
753 }
754
755 col_len = cstrlen(field->name);
756 if (type == VERT_LIST) {
757 if (col_len > max_len) { max_len = col_len; }
758 } else {
759 if (SqlFieldIsNumeric(field->type) &&
760 (int)field->max_length > 0) { /* fixup for commas */
761 field->max_length += (field->max_length - 1) / 3;
762 }
763 if (col_len < (int)field->max_length) { col_len = field->max_length; }
764 if (col_len < 4 && !SqlFieldIsNotNull(field->flags)) {
765 col_len = 4; /* 4 = length of the word "NULL" */
766 }
767 field->max_length = col_len; /* reset column info */
768 }
769 }
770 break;
771 }
772
773 Dmsg0(800, "ListResult finished first loop\n");
774
775 /*
776 * See if filters are enabled for this list function.
777 * We use this to shortcut for calling the FilterData() method in the
778 * OutputFormatter class.
779 */
780 filters_enabled = send->HasFilters();
781
782 switch (type) {
783 case E_LIST_INIT:
784 case NF_LIST:
785 case RAW_LIST:
786 Dmsg1(800, "ListResult starts second loop looking at %d fields\n",
787 num_fields);
788 while ((row = SqlFetchRow()) != NULL) {
789 /*
790 * See if we should allow this under the current filtering.
791 */
792 if (filters_enabled && !send->FilterData(row)) { continue; }
793
794 send->ObjectStart();
795 SqlFieldSeek(0);
796 for (int i = 0; i < num_fields; i++) {
797 field = SqlFetchField();
798 if (!field) { break; }
799
800 /*
801 * See if this is a hidden column.
802 */
803 if (send->IsHiddenColumn(i)) {
804 Dmsg1(800, "ListResult field %d is hidden\n", i);
805 continue;
806 }
807
808 if (row[i] == NULL) {
809 value.bsprintf("%s", "NULL");
810 } else {
811 value.bsprintf("%s", row[i]);
812 }
813 send->ObjectKeyValue(field->name, value.c_str(), " %s");
814 }
815 if (type != RAW_LIST) { send->Decoration("\n"); }
816 send->ObjectEnd();
817 }
818 break;
819 case HORZ_LIST:
820 Dmsg1(800, "ListResult starts second loop looking at %d fields\n",
821 num_fields);
822 ListDashes(send);
823 send->Decoration("|");
824 SqlFieldSeek(0);
825 for (int i = 0; i < num_fields; i++) {
826 Dmsg1(800, "ListResult looking at field %d\n", i);
827
828 field = SqlFetchField();
829 if (!field) { break; }
830
831 /*
832 * See if this is a hidden column.
833 */
834 if (send->IsHiddenColumn(i)) {
835 Dmsg1(800, "ListResult field %d is hidden\n", i);
836 continue;
837 }
838
839 max_len = MaxLength(field->max_length);
840 send->Decoration(" %-*s |", max_len, field->name);
841 }
842 send->Decoration("\n");
843 ListDashes(send);
844
845 Dmsg1(800, "ListResult starts third loop looking at %d fields\n",
846 num_fields);
847 while ((row = SqlFetchRow()) != NULL) {
848 /*
849 * See if we should allow this under the current filtering.
850 */
851 if (filters_enabled && !send->FilterData(row)) { continue; }
852
853 send->ObjectStart();
854 SqlFieldSeek(0);
855 send->Decoration("|");
856 for (int i = 0; i < num_fields; i++) {
857 field = SqlFetchField();
858 if (!field) { break; }
859
860 /*
861 * See if this is a hidden column.
862 */
863 if (send->IsHiddenColumn(i)) {
864 Dmsg1(800, "ListResult field %d is hidden\n", i);
865 continue;
866 }
867
868 max_len = MaxLength(field->max_length);
869 if (row[i] == NULL) {
870 value.bsprintf(" %-*s |", max_len, "NULL");
871 } else if (SqlFieldIsNumeric(field->type) && !jcr->gui &&
872 IsAnInteger(row[i])) {
873 value.bsprintf(" %*s |", max_len, add_commas(row[i], ewc));
874 } else {
875 value.bsprintf(" %-*s |", max_len, row[i]);
876 }
877 if (i == num_fields - 1) { value.strcat("\n"); }
878
879 /*
880 * Use value format string to send preformated value
881 */
882 send->ObjectKeyValue(field->name, row[i], value.c_str());
883 }
884 send->ObjectEnd();
885 }
886 ListDashes(send);
887 break;
888 case VERT_LIST:
889 Dmsg1(800, "ListResult starts vertical list at %d fields\n", num_fields);
890 while ((row = SqlFetchRow()) != NULL) {
891 /*
892 * See if we should allow this under the current filtering.
893 */
894 if (filters_enabled && !send->FilterData(row)) { continue; }
895
896 send->ObjectStart();
897 SqlFieldSeek(0);
898 for (int i = 0; i < num_fields; i++) {
899 field = SqlFetchField();
900 if (!field) { break; }
901
902 /*
903 * See if this is a hidden column.
904 */
905 if (send->IsHiddenColumn(i)) {
906 Dmsg1(800, "ListResult field %d is hidden\n", i);
907 continue;
908 }
909
910 if (row[i] == NULL) {
911 key.bsprintf(" %*s: ", max_len, field->name);
912 value.bsprintf("%s\n", "NULL");
913 } else if (SqlFieldIsNumeric(field->type) && !jcr->gui &&
914 IsAnInteger(row[i])) {
915 key.bsprintf(" %*s: ", max_len, field->name);
916 value.bsprintf("%s\n", add_commas(row[i], ewc));
917 } else {
918 key.bsprintf(" %*s: ", max_len, field->name);
919 value.bsprintf("%s\n", row[i]);
920 }
921
922 /*
923 * Use value format string to send preformated value
924 */
925 send->ObjectKeyValue(field->name, key.c_str(), row[i], value.c_str());
926 }
927 send->Decoration("\n");
928 send->ObjectEnd();
929 }
930 break;
931 }
932
933 return SqlNumRows();
934 }
935
936 /*
937 * If full_list is set, we list vertically, otherwise, we list on one line
938 * horizontally.
939 *
940 * Return number of rows
941 */
ListResult(JobControlRecord * jcr,BareosDb * mdb,OutputFormatter * send,e_list_type type)942 int ListResult(JobControlRecord* jcr,
943 BareosDb* mdb,
944 OutputFormatter* send,
945 e_list_type type)
946 {
947 return mdb->ListResult(jcr, send, type);
948 }
949
950 /**
951 * Open a new connexion to mdb catalog. This function is used by batch and
952 * accurate mode.
953 */
OpenBatchConnection(JobControlRecord * jcr)954 bool BareosDb::OpenBatchConnection(JobControlRecord* jcr)
955 {
956 bool multi_db;
957
958 multi_db = BatchInsertAvailable();
959 if (!jcr->db_batch) {
960 jcr->db_batch = CloneDatabaseConnection(jcr, multi_db, multi_db);
961 if (!jcr->db_batch) {
962 Mmsg0(errmsg, _("Could not init database batch connection\n"));
963 Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
964 return false;
965 }
966 }
967 return true;
968 }
969
DbDebugPrint(FILE * fp)970 void BareosDb::DbDebugPrint(FILE* fp)
971 {
972 fprintf(fp, "BareosDb=%p db_name=%s db_user=%s connected=%s\n", this,
973 NPRTB(get_db_name()), NPRTB(get_db_user()),
974 IsConnected() ? "true" : "false");
975 fprintf(fp, "\tcmd=\"%s\" changes=%i\n", NPRTB(cmd), changes);
976
977 PrintLockInfo(fp);
978 }
979
980 /**
981 * !!! WARNING !!! Use this function only when bareos is stopped.
982 * ie, after a fatal signal and before exiting the program
983 * Print information about a BareosDb object.
984 */
DbDebugPrint(JobControlRecord * jcr,FILE * fp)985 void DbDebugPrint(JobControlRecord* jcr, FILE* fp)
986 {
987 BareosDb* mdb = jcr->db;
988
989 if (!mdb) { return; }
990
991 mdb->DbDebugPrint(fp);
992 }
993 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || \
994 HAVE_DBI */
995