]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql.c
Revert to using LGPLv3 for simple scripts
[bacula/bacula] / bacula / src / cats / sql.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from many
7    others, a complete list can be found in the file AUTHORS.
8
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.
13
14    Bacula® is a registered trademark of Kern Sibbald.
15 */
16 /*
17  * Bacula Catalog Database interface routines
18  *
19  *     Almost generic set of SQL database interface routines
20  *      (with a little more work)
21  *     SQL engine specific routines are in mysql.c, postgresql.c,
22  *       sqlite.c, ...
23  *
24  *    Written by Kern Sibbald, March 2000
25  *
26  */
27
28 #include "bacula.h"
29
30 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
31
32 #include "cats.h"
33 #include "bdb_priv.h"
34 #include "sql_glue.h"
35
36 /* Forward referenced subroutines */
37 void print_dashes(B_DB *mdb);
38 void print_result(B_DB *mdb);
39
40 dbid_list::dbid_list()
41 {
42    memset(this, 0, sizeof(dbid_list));
43    max_ids = 1000;
44    DBId = (DBId_t *)malloc(max_ids * sizeof(DBId_t));
45    num_ids = num_seen = tot_ids = 0;
46    PurgedFiles = NULL;
47 }
48
49 dbid_list::~dbid_list()
50 {
51    free(DBId);
52 }
53
54 /*
55  * Called here to retrieve an string list from the database
56  */
57 int db_string_list_handler(void *ctx, int num_fields, char **row)
58 {
59    alist **val = (alist **)ctx;
60
61    if (row[0]) {
62       (*val)->append(bstrdup(row[0]));
63    }
64
65    return 0;
66 }
67
68 /*
69  * Called here to retrieve an integer from the database
70  */
71 int db_int_handler(void *ctx, int num_fields, char **row)
72 {
73    uint32_t *val = (uint32_t *)ctx;
74
75    Dmsg1(800, "int_handler starts with row pointing at %x\n", row);
76
77    if (row[0]) {
78       Dmsg1(800, "int_handler finds '%s'\n", row[0]);
79       *val = str_to_int64(row[0]);
80    } else {
81       Dmsg0(800, "int_handler finds zero\n");
82       *val = 0;
83    }
84    Dmsg0(800, "int_handler finishes\n");
85    return 0;
86 }
87
88 /*
89  * Called here to retrieve a 32/64 bit integer from the database.
90  *   The returned integer will be extended to 64 bit.
91  */
92 int db_int64_handler(void *ctx, int num_fields, char **row)
93 {
94    db_int64_ctx *lctx = (db_int64_ctx *)ctx;
95
96    if (row[0]) {
97       lctx->value = str_to_int64(row[0]);
98       lctx->count++;
99    }
100    return 0;
101 }
102
103 /*
104  * Called here to retrieve a btime from the database.
105  *   The returned integer will be extended to 64 bit.
106  */
107 int db_strtime_handler(void *ctx, int num_fields, char **row)
108 {
109    db_int64_ctx *lctx = (db_int64_ctx *)ctx;
110
111    if (row[0]) {
112       lctx->value = str_to_utime(row[0]);
113       lctx->count++;
114    }
115    return 0;
116 }
117
118 /*
119  * Use to build a comma separated list of values from a query. "10,20,30"
120  */
121 int db_list_handler(void *ctx, int num_fields, char **row)
122 {
123    db_list_ctx *lctx = (db_list_ctx *)ctx;
124    if (num_fields == 1 && row[0]) {
125       lctx->add(row[0]);
126    }
127    return 0;
128 }
129
130 /*
131  * specific context passed from db_check_max_connections to db_max_connections_handler.
132  */
133 struct max_connections_context {
134    B_DB *db;
135    uint32_t nr_connections;
136 };
137
138 /*
139  * Called here to retrieve an integer from the database
140  */
141 static inline int db_max_connections_handler(void *ctx, int num_fields, char **row)
142 {
143    struct max_connections_context *context;
144    uint32_t index;
145
146    context = (struct max_connections_context *)ctx;
147    switch (db_get_type_index(context->db)) {
148    case SQL_TYPE_MYSQL:
149       index = 1;
150    default:
151       index = 0;
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  */
166 bool db_check_max_connections(JCR *jcr, B_DB *mdb, uint32_t max_concurrent_jobs)
167 {
168    struct max_connections_context context;
169
170    /* Without Batch insert, no need to verify max_connections */
171    if (!mdb->batch_insert_available())
172       return true;
173
174    context.db = mdb;
175    context.nr_connections = 0;
176
177    /* Check max_connections setting */
178    if (!db_sql_query(mdb, sql_get_max_connections[db_get_type_index(mdb)],
179                      db_max_connections_handler, &context)) {
180       Jmsg(jcr, M_ERROR, 0, "Can't verify max_connections settings %s", mdb->errmsg);
181       return false;
182    }
183    if (context.nr_connections && max_concurrent_jobs && max_concurrent_jobs > context.nr_connections) {
184       Mmsg(mdb->errmsg,
185            _("Potential performance problem:\n"
186              "max_connections=%d set for %s database \"%s\" should be larger than Director's "
187              "MaxConcurrentJobs=%d\n"),
188            context.nr_connections, db_get_type(mdb), mdb->get_db_name(), max_concurrent_jobs);
189       Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
190       return false;
191    }
192
193    return true;
194 }
195
196 /* NOTE!!! The following routines expect that the
197  *  calling subroutine sets and clears the mutex
198  */
199
200 /* Check that the tables correspond to the version we want */
201 bool check_tables_version(JCR *jcr, B_DB *mdb)
202 {
203    uint32_t bacula_db_version = 0;
204    const char *query = "SELECT VersionId FROM Version";
205
206    bacula_db_version = 0;
207    if (!db_sql_query(mdb, query, db_int_handler, (void *)&bacula_db_version)) {
208       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
209       return false;
210    }
211    if (bacula_db_version != BDB_VERSION) {
212       Mmsg(mdb->errmsg, "Version error for database \"%s\". Wanted %d, got %d\n",
213           mdb->get_db_name(), BDB_VERSION, bacula_db_version);
214       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
215       return false;
216    }
217    return true;
218 }
219
220 /*
221  * Utility routine for queries. The database MUST be locked before calling here.
222  * Returns: 0 on failure
223  *          1 on success
224  */
225 int
226 QueryDB(const char *file, int line, JCR *jcr, B_DB *mdb, char *cmd)
227 {
228    sql_free_result(mdb);
229    if (!sql_query(mdb, cmd, QF_STORE_RESULT)) {
230       m_msg(file, line, &mdb->errmsg, _("query %s failed:\n%s\n"), cmd, sql_strerror(mdb));
231       j_msg(file, line, jcr, M_FATAL, 0, "%s", mdb->errmsg);
232       if (verbose) {
233          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
234       }
235       return 0;
236    }
237
238    return 1;
239 }
240
241 /*
242  * Utility routine to do inserts
243  * Returns: 0 on failure
244  *          1 on success
245  */
246 int
247 InsertDB(const char *file, int line, JCR *jcr, B_DB *mdb, char *cmd)
248 {
249    int num_rows;
250
251    if (!sql_query(mdb, cmd)) {
252       m_msg(file, line, &mdb->errmsg,  _("insert %s failed:\n%s\n"), cmd, sql_strerror(mdb));
253       j_msg(file, line, jcr, M_FATAL, 0, "%s", mdb->errmsg);
254       if (verbose) {
255          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
256       }
257       return 0;
258    }
259    num_rows = sql_affected_rows(mdb);
260    if (num_rows != 1) {
261       char ed1[30];
262       m_msg(file, line, &mdb->errmsg, _("Insertion problem: affected_rows=%s\n"),
263          edit_uint64(num_rows, ed1));
264       if (verbose) {
265          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
266       }
267       return 0;
268    }
269    mdb->changes++;
270    return 1;
271 }
272
273 /* Utility routine for updates.
274  *  Returns: 0 on failure
275  *           1 on success
276  */
277 int
278 UpdateDB(const char *file, int line, JCR *jcr, B_DB *mdb, char *cmd)
279 {
280    int num_rows;
281
282    if (!sql_query(mdb, cmd)) {
283       m_msg(file, line, &mdb->errmsg, _("update %s failed:\n%s\n"), cmd, sql_strerror(mdb));
284       j_msg(file, line, jcr, M_ERROR, 0, "%s", mdb->errmsg);
285       if (verbose) {
286          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
287       }
288       return 0;
289    }
290    num_rows = sql_affected_rows(mdb);
291    if (num_rows < 1) {
292       char ed1[30];
293       m_msg(file, line, &mdb->errmsg, _("Update failed: affected_rows=%s for %s\n"),
294          edit_uint64(num_rows, ed1), cmd);
295       if (verbose) {
296 //       j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
297       }
298       return 0;
299    }
300    mdb->changes++;
301    return 1;
302 }
303
304 /*
305  * Utility routine for deletes
306  *
307  * Returns: -1 on error
308  *           n number of rows affected
309  */
310 int
311 DeleteDB(const char *file, int line, JCR *jcr, B_DB *mdb, char *cmd)
312 {
313
314    if (!sql_query(mdb, cmd)) {
315       m_msg(file, line, &mdb->errmsg, _("delete %s failed:\n%s\n"), cmd, sql_strerror(mdb));
316       j_msg(file, line, jcr, M_ERROR, 0, "%s", mdb->errmsg);
317       if (verbose) {
318          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
319       }
320       return -1;
321    }
322    mdb->changes++;
323    return sql_affected_rows(mdb);
324 }
325
326
327 /*
328  * Get record max. Query is already in mdb->cmd
329  *  No locking done
330  *
331  * Returns: -1 on failure
332  *          count on success
333  */
334 int get_sql_record_max(JCR *jcr, B_DB *mdb)
335 {
336    SQL_ROW row;
337    int stat = 0;
338
339    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
340       if ((row = sql_fetch_row(mdb)) == NULL) {
341          Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
342          stat = -1;
343       } else {
344          stat = str_to_int64(row[0]);
345       }
346       sql_free_result(mdb);
347    } else {
348       Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
349       stat = -1;
350    }
351    return stat;
352 }
353
354 /*
355  * Return pre-edited error message
356  */
357 char *db_strerror(B_DB *mdb)
358 {
359    return mdb->errmsg;
360 }
361
362 /*
363  * Given a full filename, split it into its path
364  *  and filename parts. They are returned in pool memory
365  *  in the mdb structure.
366  */
367 void split_path_and_file(JCR *jcr, B_DB *mdb, const char *fname)
368 {
369    const char *p, *f;
370
371    /* Find path without the filename.
372     * I.e. everything after the last / is a "filename".
373     * OK, maybe it is a directory name, but we treat it like
374     * a filename. If we don't find a / then the whole name
375     * must be a path name (e.g. c:).
376     */
377    for (p=f=fname; *p; p++) {
378       if (IsPathSeparator(*p)) {
379          f = p;                       /* set pos of last slash */
380       }
381    }
382    if (IsPathSeparator(*f)) {                   /* did we find a slash? */
383       f++;                            /* yes, point to filename */
384    } else {                           /* no, whole thing must be path name */
385       f = p;
386    }
387
388    /* If filename doesn't exist (i.e. root directory), we
389     * simply create a blank name consisting of a single
390     * space. This makes handling zero length filenames
391     * easier.
392     */
393    mdb->fnl = p - f;
394    if (mdb->fnl > 0) {
395       mdb->fname = check_pool_memory_size(mdb->fname, mdb->fnl+1);
396       memcpy(mdb->fname, f, mdb->fnl);    /* copy filename */
397       mdb->fname[mdb->fnl] = 0;
398    } else {
399       mdb->fname[0] = 0;
400       mdb->fnl = 0;
401    }
402
403    mdb->pnl = f - fname;
404    if (mdb->pnl > 0) {
405       mdb->path = check_pool_memory_size(mdb->path, mdb->pnl+1);
406       memcpy(mdb->path, fname, mdb->pnl);
407       mdb->path[mdb->pnl] = 0;
408    } else {
409       Mmsg1(&mdb->errmsg, _("Path length is zero. File=%s\n"), fname);
410       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
411       mdb->path[0] = 0;
412       mdb->pnl = 0;
413    }
414
415    Dmsg2(500, "split path=%s file=%s\n", mdb->path, mdb->fname);
416 }
417
418 /*
419  * Set maximum field length to something reasonable
420  */
421 static int max_length(int max_length)
422 {
423    int max_len = max_length;
424    /* Sanity check */
425    if (max_len < 0) {
426       max_len = 2;
427    } else if (max_len > 100) {
428       max_len = 100;
429    }
430    return max_len;
431 }
432
433 /*
434  * List dashes as part of header for listing SQL results in a table
435  */
436 void list_dashes(B_DB *mdb, DB_LIST_HANDLER *send, void *ctx)
437 {
438    SQL_FIELD  *field;
439    int i, j;
440    int len;
441    int num_fields;
442
443    sql_field_seek(mdb, 0);
444    send(ctx, "+");
445    num_fields = sql_num_fields(mdb);
446    for (i = 0; i < num_fields; i++) {
447       field = sql_fetch_field(mdb);
448       if (!field) {
449          break;
450       }
451       len = max_length(field->max_length + 2);
452       for (j = 0; j < len; j++) {
453          send(ctx, "-");
454       }
455       send(ctx, "+");
456    }
457    send(ctx, "\n");
458 }
459
460 /* Small handler to print the last line of a list xxx command */
461 static void last_line_handler(void *vctx, const char *str)
462 {
463    LIST_CTX *ctx = (LIST_CTX *)vctx;
464    bstrncat(ctx->line, str, sizeof(ctx->line));
465 }
466
467 int list_result(void *vctx, int nb_col, char **row)
468 {
469    SQL_FIELD *field;
470    int i, col_len, max_len = 0;
471    int num_fields;
472    char buf[2000], ewc[30];
473
474    LIST_CTX *pctx = (LIST_CTX *)vctx;
475    DB_LIST_HANDLER *send = pctx->send;
476    e_list_type type = pctx->type;
477    B_DB *mdb = pctx->mdb;
478    void *ctx = pctx->ctx;
479    JCR *jcr = pctx->jcr;
480
481    num_fields = sql_num_fields(mdb);
482    if (!pctx->once) {
483       pctx->once = true;
484
485       Dmsg1(800, "list_result starts looking at %d fields\n", num_fields);
486       /* determine column display widths */
487       sql_field_seek(mdb, 0);
488       for (i = 0; i < num_fields; i++) {
489          Dmsg1(800, "list_result processing field %d\n", i);
490          field = sql_fetch_field(mdb);
491          if (!field) {
492             break;
493          }
494          col_len = cstrlen(field->name);
495          if (type == VERT_LIST) {
496             if (col_len > max_len) {
497                max_len = col_len;
498             }
499          } else {
500             if (sql_field_is_numeric(mdb, field->type) && (int)field->max_length > 0) { /* fixup for commas */
501                field->max_length += (field->max_length - 1) / 3;
502             }
503             if (col_len < (int)field->max_length) {
504                col_len = field->max_length;
505             }
506             if (col_len < 4 && !sql_field_is_not_null(mdb, field->flags)) {
507                col_len = 4;                 /* 4 = length of the word "NULL" */
508             }
509             field->max_length = col_len;    /* reset column info */
510          }
511       }
512
513       pctx->num_rows++;
514
515       Dmsg0(800, "list_result finished first loop\n");
516       if (type == VERT_LIST) {
517          goto vertical_list;
518       }
519
520       Dmsg1(800, "list_result starts second loop looking at %d fields\n", num_fields);
521
522       /* Keep the result to display the same line at the end of the table */
523       list_dashes(mdb, last_line_handler, pctx);
524       send(ctx, pctx->line);
525
526       send(ctx, "|");
527       sql_field_seek(mdb, 0);
528       for (i = 0; i < num_fields; i++) {
529          Dmsg1(800, "list_result looking at field %d\n", i);
530          field = sql_fetch_field(mdb);
531          if (!field) {
532             break;
533          }
534          max_len = max_length(field->max_length);
535          bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name);
536          send(ctx, buf);
537       }
538       send(ctx, "\n");
539       list_dashes(mdb, send, ctx);
540    }
541
542    Dmsg1(800, "list_result starts third loop looking at %d fields\n", num_fields);
543
544    sql_field_seek(mdb, 0);
545    send(ctx, "|");
546    for (i = 0; i < num_fields; i++) {
547       field = sql_fetch_field(mdb);
548       if (!field) {
549          break;
550       }
551       max_len = max_length(field->max_length);
552       if (row[i] == NULL) {
553          bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL");
554       } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) {
555          bsnprintf(buf, sizeof(buf), " %*s |", max_len,
556                    add_commas(row[i], ewc));
557       } else {
558          bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]);
559       }
560       send(ctx, buf);
561    }
562    send(ctx, "\n");
563    return 0;
564
565 vertical_list:
566
567    Dmsg1(800, "list_result starts vertical list at %d fields\n", num_fields);
568
569    sql_field_seek(mdb, 0);
570    for (i = 0; i < num_fields; i++) {
571       field = sql_fetch_field(mdb);
572       if (!field) {
573          break;
574       }
575       if (row[i] == NULL) {
576          bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL");
577       } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) {
578          bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name,
579                    add_commas(row[i], ewc));
580       } else {
581          bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]);
582       }
583       send(ctx, buf);
584    }
585    send(ctx, "\n");
586    return 0;
587 }
588
589 /*
590  * If full_list is set, we list vertically, otherwise, we
591  *  list on one line horizontally.
592  * Return number of rows
593  */
594 int list_result(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type type)
595 {
596    SQL_FIELD *field;
597    SQL_ROW row;
598    int i, col_len, max_len = 0;
599    int num_fields;
600    char buf[2000], ewc[30];
601
602    Dmsg0(800, "list_result starts\n");
603    if (sql_num_rows(mdb) == 0) {
604       send(ctx, _("No results to list.\n"));
605       return sql_num_rows(mdb);
606    }
607
608    num_fields = sql_num_fields(mdb);
609    Dmsg1(800, "list_result starts looking at %d fields\n", num_fields);
610    /* determine column display widths */
611    sql_field_seek(mdb, 0);
612    for (i = 0; i < num_fields; i++) {
613       Dmsg1(800, "list_result processing field %d\n", i);
614       field = sql_fetch_field(mdb);
615       if (!field) {
616          break;
617       }
618       col_len = cstrlen(field->name);
619       if (type == VERT_LIST) {
620          if (col_len > max_len) {
621             max_len = col_len;
622          }
623       } else {
624          if (sql_field_is_numeric(mdb, field->type) && (int)field->max_length > 0) { /* fixup for commas */
625             field->max_length += (field->max_length - 1) / 3;
626          }
627          if (col_len < (int)field->max_length) {
628             col_len = field->max_length;
629          }
630          if (col_len < 4 && !sql_field_is_not_null(mdb, field->flags)) {
631             col_len = 4;                 /* 4 = length of the word "NULL" */
632          }
633          field->max_length = col_len;    /* reset column info */
634       }
635    }
636
637    Dmsg0(800, "list_result finished first loop\n");
638    if (type == VERT_LIST) {
639       goto vertical_list;
640    }
641
642    Dmsg1(800, "list_result starts second loop looking at %d fields\n", num_fields);
643    list_dashes(mdb, send, ctx);
644    send(ctx, "|");
645    sql_field_seek(mdb, 0);
646    for (i = 0; i < num_fields; i++) {
647       Dmsg1(800, "list_result looking at field %d\n", i);
648       field = sql_fetch_field(mdb);
649       if (!field) {
650          break;
651       }
652       max_len = max_length(field->max_length);
653       bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name);
654       send(ctx, buf);
655    }
656    send(ctx, "\n");
657    list_dashes(mdb, send, ctx);
658
659    Dmsg1(800, "list_result starts third loop looking at %d fields\n", num_fields);
660    while ((row = sql_fetch_row(mdb)) != NULL) {
661       sql_field_seek(mdb, 0);
662       send(ctx, "|");
663       for (i = 0; i < num_fields; i++) {
664          field = sql_fetch_field(mdb);
665          if (!field) {
666             break;
667          }
668          max_len = max_length(field->max_length);
669          if (row[i] == NULL) {
670             bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL");
671          } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) {
672             bsnprintf(buf, sizeof(buf), " %*s |", max_len,
673                       add_commas(row[i], ewc));
674          } else {
675             bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]);
676          }
677          send(ctx, buf);
678       }
679       send(ctx, "\n");
680    }
681    list_dashes(mdb, send, ctx);
682    return sql_num_rows(mdb);
683
684 vertical_list:
685
686    Dmsg1(800, "list_result starts vertical list at %d fields\n", num_fields);
687    while ((row = sql_fetch_row(mdb)) != NULL) {
688       sql_field_seek(mdb, 0);
689       for (i = 0; i < num_fields; i++) {
690          field = sql_fetch_field(mdb);
691          if (!field) {
692             break;
693          }
694          if (row[i] == NULL) {
695             bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL");
696          } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) {
697             bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name,
698                 add_commas(row[i], ewc));
699          } else {
700             bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]);
701          }
702          send(ctx, buf);
703       }
704       send(ctx, "\n");
705    }
706    return sql_num_rows(mdb);
707 }
708
709 /*
710  * Open a new connexion to mdb catalog. This function is used
711  * by batch and accurate mode.
712  */
713 bool db_open_batch_connexion(JCR *jcr, B_DB *mdb)
714 {
715    bool multi_db;
716
717    multi_db = mdb->batch_insert_available();
718
719    if (!jcr->db_batch) {
720       jcr->db_batch = db_clone_database_connection(mdb, jcr, multi_db);
721       if (!jcr->db_batch) {
722          Mmsg0(&mdb->errmsg, _("Could not init database batch connection\n"));
723          Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
724          return false;
725       }
726
727       if (!db_open_database(jcr, jcr->db_batch)) {
728          Mmsg2(&mdb->errmsg,  _("Could not open database \"%s\": ERR=%s\n"),
729               jcr->db_batch->get_db_name(), db_strerror(jcr->db_batch));
730          Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
731          return false;
732       }
733    }
734    return true;
735 }
736
737 /*
738  * !!! WARNING !!! Use this function only when bacula is stopped.
739  * ie, after a fatal signal and before exiting the program
740  * Print information about a B_DB object.
741  */
742 void db_debug_print(JCR *jcr, FILE *fp)
743 {
744    B_DB *mdb = jcr->db;
745
746    if (!mdb) {
747       return;
748    }
749
750    fprintf(fp, "B_DB=%p db_name=%s db_user=%s connected=%s\n",
751            mdb, NPRTB(mdb->get_db_name()), NPRTB(mdb->get_db_user()), mdb->is_connected() ? "true" : "false");
752    fprintf(fp, "\tcmd=\"%s\" changes=%i\n", NPRTB(mdb->cmd), mdb->changes);
753    mdb->print_lock_info(fp);
754 }
755
756 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */