]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql.c
Pull compiler warning cleanup code + other from master
[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 inline 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 /*
304  * Utility routine for deletes
305  *
306  * Returns: -1 on error
307  *           n number of rows affected
308  */
309 int
310 DeleteDB(const char *file, int line, JCR *jcr, B_DB *mdb, char *cmd)
311 {
312
313    if (!sql_query(mdb, cmd)) {
314       m_msg(file, line, &mdb->errmsg, _("delete %s failed:\n%s\n"), cmd, sql_strerror(mdb));
315       j_msg(file, line, jcr, M_ERROR, 0, "%s", mdb->errmsg);
316       if (verbose) {
317          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
318       }
319       return -1;
320    }
321    mdb->changes++;
322    return sql_affected_rows(mdb);
323 }
324
325
326 /*
327  * Get record max. Query is already in mdb->cmd
328  *  No locking done
329  *
330  * Returns: -1 on failure
331  *          count on success
332  */
333 int get_sql_record_max(JCR *jcr, B_DB *mdb)
334 {
335    SQL_ROW row;
336    int stat = 0;
337
338    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
339       if ((row = sql_fetch_row(mdb)) == NULL) {
340          Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
341          stat = -1;
342       } else {
343          stat = str_to_int64(row[0]);
344       }
345       sql_free_result(mdb);
346    } else {
347       Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
348       stat = -1;
349    }
350    return stat;
351 }
352
353 /*
354  * Return pre-edited error message
355  */
356 char *db_strerror(B_DB *mdb)
357 {
358    return mdb->errmsg;
359 }
360
361 /*
362  * Given a full filename, split it into its path
363  *  and filename parts. They are returned in pool memory
364  *  in the mdb structure.
365  */
366 void split_path_and_file(JCR *jcr, B_DB *mdb, const char *fname)
367 {
368    const char *p, *f;
369
370    /* Find path without the filename.
371     * I.e. everything after the last / is a "filename".
372     * OK, maybe it is a directory name, but we treat it like
373     * a filename. If we don't find a / then the whole name
374     * must be a path name (e.g. c:).
375     */
376    for (p=f=fname; *p; p++) {
377       if (IsPathSeparator(*p)) {
378          f = p;                       /* set pos of last slash */
379       }
380    }
381    if (IsPathSeparator(*f)) {                   /* did we find a slash? */
382       f++;                            /* yes, point to filename */
383    } else {                           /* no, whole thing must be path name */
384       f = p;
385    }
386
387    /* If filename doesn't exist (i.e. root directory), we
388     * simply create a blank name consisting of a single
389     * space. This makes handling zero length filenames
390     * easier.
391     */
392    mdb->fnl = p - f;
393    if (mdb->fnl > 0) {
394       mdb->fname = check_pool_memory_size(mdb->fname, mdb->fnl+1);
395       memcpy(mdb->fname, f, mdb->fnl);    /* copy filename */
396       mdb->fname[mdb->fnl] = 0;
397    } else {
398       mdb->fname[0] = 0;
399       mdb->fnl = 0;
400    }
401
402    mdb->pnl = f - fname;
403    if (mdb->pnl > 0) {
404       mdb->path = check_pool_memory_size(mdb->path, mdb->pnl+1);
405       memcpy(mdb->path, fname, mdb->pnl);
406       mdb->path[mdb->pnl] = 0;
407    } else {
408       Mmsg1(&mdb->errmsg, _("Path length is zero. File=%s\n"), fname);
409       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
410       mdb->path[0] = 0;
411       mdb->pnl = 0;
412    }
413
414    Dmsg2(500, "split path=%s file=%s\n", mdb->path, mdb->fname);
415 }
416
417 /*
418  * Set maximum field length to something reasonable
419  */
420 static int max_length(int max_length)
421 {
422    int max_len = max_length;
423    /* Sanity check */
424    if (max_len < 0) {
425       max_len = 2;
426    } else if (max_len > 100) {
427       max_len = 100;
428    }
429    return max_len;
430 }
431
432 /*
433  * List dashes as part of header for listing SQL results in a table
434  */
435 void list_dashes(B_DB *mdb, DB_LIST_HANDLER *send, void *ctx)
436 {
437    SQL_FIELD  *field;
438    int i, j;
439    int len;
440    int num_fields;
441
442    sql_field_seek(mdb, 0);
443    send(ctx, "+");
444    num_fields = sql_num_fields(mdb);
445    for (i = 0; i < num_fields; i++) {
446       field = sql_fetch_field(mdb);
447       if (!field) {
448          break;
449       }
450       len = max_length(field->max_length + 2);
451       for (j = 0; j < len; j++) {
452          send(ctx, "-");
453       }
454       send(ctx, "+");
455    }
456    send(ctx, "\n");
457 }
458
459 /* Small handler to print the last line of a list xxx command */
460 static void last_line_handler(void *vctx, const char *str)
461 {
462    LIST_CTX *ctx = (LIST_CTX *)vctx;
463    bstrncat(ctx->line, str, sizeof(ctx->line));
464 }
465
466 int list_result(void *vctx, int nb_col, char **row)
467 {
468    SQL_FIELD *field;
469    int i, col_len, max_len = 0;
470    int num_fields;
471    char buf[2000], ewc[30];
472
473    LIST_CTX *pctx = (LIST_CTX *)vctx;
474    DB_LIST_HANDLER *send = pctx->send;
475    e_list_type type = pctx->type;
476    B_DB *mdb = pctx->mdb;
477    void *ctx = pctx->ctx;
478    JCR *jcr = pctx->jcr;
479
480    num_fields = sql_num_fields(mdb);
481    if (!pctx->once) {
482       pctx->once = true;
483
484       Dmsg1(800, "list_result starts looking at %d fields\n", num_fields);
485       /* determine column display widths */
486       sql_field_seek(mdb, 0);
487       for (i = 0; i < num_fields; i++) {
488          Dmsg1(800, "list_result processing field %d\n", i);
489          field = sql_fetch_field(mdb);
490          if (!field) {
491             break;
492          }
493          col_len = cstrlen(field->name);
494          if (type == VERT_LIST) {
495             if (col_len > max_len) {
496                max_len = col_len;
497             }
498          } else {
499             if (sql_field_is_numeric(mdb, field->type) && (int)field->max_length > 0) { /* fixup for commas */
500                field->max_length += (field->max_length - 1) / 3;
501             }  
502             if (col_len < (int)field->max_length) {
503                col_len = field->max_length;
504             }  
505             if (col_len < 4 && !sql_field_is_not_null(mdb, field->flags)) {
506                col_len = 4;                 /* 4 = length of the word "NULL" */
507             }
508             field->max_length = col_len;    /* reset column info */
509          }
510       }
511
512       pctx->num_rows++;
513
514       Dmsg0(800, "list_result finished first loop\n");
515       if (type == VERT_LIST) {
516          goto vertical_list;
517       }
518
519       Dmsg1(800, "list_result starts second loop looking at %d fields\n", num_fields);
520
521       /* Keep the result to display the same line at the end of the table */
522       list_dashes(mdb, last_line_handler, pctx);
523       send(ctx, pctx->line);
524
525       send(ctx, "|");
526       sql_field_seek(mdb, 0);
527       for (i = 0; i < num_fields; i++) {
528          Dmsg1(800, "list_result looking at field %d\n", i);
529          field = sql_fetch_field(mdb);
530          if (!field) {
531             break;
532          }
533          max_len = max_length(field->max_length);
534          bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name);
535          send(ctx, buf);
536       }
537       send(ctx, "\n");
538       list_dashes(mdb, send, ctx);      
539    }
540    
541    Dmsg1(800, "list_result starts third loop looking at %d fields\n", num_fields);
542
543    sql_field_seek(mdb, 0);
544    send(ctx, "|");
545    for (i = 0; i < num_fields; i++) {
546       field = sql_fetch_field(mdb);
547       if (!field) {
548          break;
549       }
550       max_len = max_length(field->max_length);
551       if (row[i] == NULL) {
552          bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL");
553       } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) {
554          bsnprintf(buf, sizeof(buf), " %*s |", max_len,
555                    add_commas(row[i], ewc));
556       } else {
557          bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]);
558       }
559       send(ctx, buf);
560    }
561    send(ctx, "\n");
562    return 0;
563
564 vertical_list:
565
566    Dmsg1(800, "list_result starts vertical list at %d fields\n", num_fields);
567
568    sql_field_seek(mdb, 0);
569    for (i = 0; i < num_fields; i++) {
570       field = sql_fetch_field(mdb);
571       if (!field) {
572          break;
573       }
574       if (row[i] == NULL) {
575          bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL");
576       } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) {
577          bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name,
578                    add_commas(row[i], ewc));
579       } else {
580          bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]);
581       }
582       send(ctx, buf);
583    }
584    send(ctx, "\n");
585    return 0;
586 }
587
588 /*
589  * If full_list is set, we list vertically, otherwise, we
590  *  list on one line horizontally.
591  * Return number of rows
592  */
593 int list_result(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type type)
594 {
595    SQL_FIELD *field;
596    SQL_ROW row;
597    int i, col_len, max_len = 0;
598    int num_fields;
599    char buf[2000], ewc[30];
600
601    Dmsg0(800, "list_result starts\n");
602    if (sql_num_rows(mdb) == 0) {
603       send(ctx, _("No results to list.\n"));
604       return sql_num_rows(mdb);
605    }
606
607    num_fields = sql_num_fields(mdb);
608    Dmsg1(800, "list_result starts looking at %d fields\n", num_fields);
609    /* determine column display widths */
610    sql_field_seek(mdb, 0);
611    for (i = 0; i < num_fields; i++) {
612       Dmsg1(800, "list_result processing field %d\n", i);
613       field = sql_fetch_field(mdb);
614       if (!field) {
615          break;
616       }
617       col_len = cstrlen(field->name);
618       if (type == VERT_LIST) {
619          if (col_len > max_len) {
620             max_len = col_len;
621          }
622       } else {
623          if (sql_field_is_numeric(mdb, field->type) && (int)field->max_length > 0) { /* fixup for commas */
624             field->max_length += (field->max_length - 1) / 3;
625          }  
626          if (col_len < (int)field->max_length) {
627             col_len = field->max_length;
628          }  
629          if (col_len < 4 && !sql_field_is_not_null(mdb, field->flags)) {
630             col_len = 4;                 /* 4 = length of the word "NULL" */
631          }
632          field->max_length = col_len;    /* reset column info */
633       }
634    }
635
636    Dmsg0(800, "list_result finished first loop\n");
637    if (type == VERT_LIST) {
638       goto vertical_list;
639    }
640
641    Dmsg1(800, "list_result starts second loop looking at %d fields\n", num_fields);
642    list_dashes(mdb, send, ctx);
643    send(ctx, "|");
644    sql_field_seek(mdb, 0);
645    for (i = 0; i < num_fields; i++) {
646       Dmsg1(800, "list_result looking at field %d\n", i);
647       field = sql_fetch_field(mdb);
648       if (!field) {
649          break;
650       }
651       max_len = max_length(field->max_length);
652       bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name);
653       send(ctx, buf);
654    }
655    send(ctx, "\n");
656    list_dashes(mdb, send, ctx);
657
658    Dmsg1(800, "list_result starts third loop looking at %d fields\n", num_fields);
659    while ((row = sql_fetch_row(mdb)) != NULL) {
660       sql_field_seek(mdb, 0);
661       send(ctx, "|");
662       for (i = 0; i < num_fields; i++) {
663          field = sql_fetch_field(mdb);
664          if (!field) {
665             break;
666          }
667          max_len = max_length(field->max_length);
668          if (row[i] == NULL) {
669             bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL");
670          } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) {
671             bsnprintf(buf, sizeof(buf), " %*s |", max_len,
672                       add_commas(row[i], ewc));
673          } else {
674             bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]);
675          }
676          send(ctx, buf);
677       }
678       send(ctx, "\n");
679    }
680    list_dashes(mdb, send, ctx);
681    return sql_num_rows(mdb);
682
683 vertical_list:
684
685    Dmsg1(800, "list_result starts vertical list at %d fields\n", num_fields);
686    while ((row = sql_fetch_row(mdb)) != NULL) {
687       sql_field_seek(mdb, 0);
688       for (i = 0; i < num_fields; i++) {
689          field = sql_fetch_field(mdb);
690          if (!field) {
691             break;
692          }
693          if (row[i] == NULL) {
694             bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL");
695          } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) {
696             bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name,
697                 add_commas(row[i], ewc));
698          } else {
699             bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]);
700          }
701          send(ctx, buf);
702       }
703       send(ctx, "\n");
704    }
705    return sql_num_rows(mdb);
706 }
707
708 /* 
709  * Open a new connexion to mdb catalog. This function is used
710  * by batch and accurate mode.
711  */
712 bool db_open_batch_connexion(JCR *jcr, B_DB *mdb)
713 {
714    bool multi_db;
715
716    if (mdb->batch_insert_available())
717       multi_db = true;   /* we force a new connection only if batch insert is enabled */
718    else
719       multi_db = false;
720
721    if (!jcr->db_batch) {
722       jcr->db_batch = db_clone_database_connection(mdb, jcr, multi_db);
723       if (!jcr->db_batch) {
724          Mmsg0(&mdb->errmsg, _("Could not init database batch connection"));
725          Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
726          return false;
727       }
728
729       if (!db_open_database(jcr, jcr->db_batch)) {
730          Mmsg2(&mdb->errmsg,  _("Could not open database \"%s\": ERR=%s\n"),
731               jcr->db_batch->get_db_name(), db_strerror(jcr->db_batch));
732          Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
733          return false;
734       }      
735    }
736    return true;
737 }
738
739 /*
740  * !!! WARNING !!! Use this function only when bacula is stopped.
741  * ie, after a fatal signal and before exiting the program
742  * Print information about a B_DB object.
743  */
744 void db_debug_print(JCR *jcr, FILE *fp)
745 {
746    B_DB *mdb = jcr->db;
747
748    if (!mdb) {
749       return;
750    }
751
752    fprintf(fp, "B_DB=%p db_name=%s db_user=%s connected=%s\n",
753            mdb, NPRTB(mdb->get_db_name()), NPRTB(mdb->get_db_user()), mdb->is_connected() ? "true" : "false");
754    fprintf(fp, "\tcmd=\"%s\" changes=%i\n", NPRTB(mdb->cmd), mdb->changes);
755    mdb->print_lock_info(fp);
756 }
757
758 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI */