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