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