]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql.c
eb8aefb860bab8dd2fd4698c891fe7b8eca6a69e
[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  * Some UPDATE queries must update record(s), other queries might not update
285  * anything.
286  */ 
287 bool BDB::UpdateDB(JCR *jcr, char *cmd, bool can_be_empty,
288                    const char *file, int line) 
289
290    if (!sql_query(cmd)) { 
291       m_msg(file, line, &errmsg, _("update %s failed:\n%s\n"), cmd, sql_strerror()); 
292       j_msg(file, line, jcr, M_ERROR, 0, "%s", errmsg); 
293       if (verbose) { 
294          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd); 
295       } 
296       return false; 
297    } 
298    int num_rows = sql_affected_rows(); 
299    if ((num_rows == 0 && !can_be_empty) || num_rows < 0) { 
300       char ed1[30]; 
301       m_msg(file, line, &errmsg, _("Update failed: affected_rows=%s for %s\n"), 
302          edit_uint64(num_rows, ed1), cmd); 
303       if (verbose) { 
304 //       j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd); 
305       } 
306       return false; 
307    } 
308    changes++; 
309    return true; 
310
311  
312 /* Utility routine for deletes 
313  * 
314  * Returns: -1 on error 
315  *           n number of rows affected 
316  */ 
317 int BDB::DeleteDB(JCR *jcr, char *cmd, const char *file, int line) 
318
319  
320    if (!sql_query(cmd)) { 
321       m_msg(file, line, &errmsg, _("delete %s failed:\n%s\n"), cmd, sql_strerror()); 
322       j_msg(file, line, jcr, M_ERROR, 0, "%s", errmsg); 
323       if (verbose) { 
324          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd); 
325       } 
326       return -1; 
327    } 
328    changes++; 
329    return sql_affected_rows(); 
330
331  
332  
333 /* 
334  * Get record max. Query is already in mdb->cmd 
335  *  No locking done 
336  * 
337  * Returns: -1 on failure 
338  *          count on success 
339  */ 
340 int get_sql_record_max(JCR *jcr, BDB *mdb) 
341
342    SQL_ROW row; 
343    int stat = 0; 
344  
345    if (mdb->QueryDB(jcr, mdb->cmd)) { 
346       if ((row = mdb->sql_fetch_row()) == NULL) { 
347          Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), mdb->sql_strerror()); 
348          stat = -1; 
349       } else { 
350          stat = str_to_int64(row[0]); 
351       } 
352       mdb->sql_free_result(); 
353    } else { 
354       Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), mdb->sql_strerror()); 
355       stat = -1; 
356    } 
357    return stat; 
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, BDB *mdb, const char *afname) 
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=afname; *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 - afname; 
402    if (mdb->pnl > 0) { 
403       mdb->path = check_pool_memory_size(mdb->path, mdb->pnl+1); 
404       memcpy(mdb->path, afname, mdb->pnl); 
405       mdb->path[mdb->pnl] = 0; 
406    } else { 
407       Mmsg1(&mdb->errmsg, _("Path length is zero. File=%s\n"), afname); 
408       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg); 
409       mdb->path[0] = 0; 
410       mdb->pnl = 0; 
411    } 
412  
413    Dmsg3(500, "split fname=%s: path=%s file=%s\n", afname, 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(BDB *mdb, DB_LIST_HANDLER *send, void *ctx) 
436
437    SQL_FIELD  *field; 
438    int i, j; 
439    int len; 
440  
441    mdb->sql_field_seek(0); 
442    send(ctx, "+"); 
443    for (i = 0; i < mdb->sql_num_fields(); i++) { 
444       field = mdb->sql_fetch_field(); 
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    BDB *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", mdb->sql_num_fields()); 
481       /* determine column display widths */ 
482       mdb->sql_field_seek(0); 
483       for (i = 0; i < mdb->sql_num_fields(); i++) { 
484          Dmsg1(800, "list_result processing field %d\n", i); 
485          field = mdb->sql_fetch_field(); 
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 (mdb->sql_field_is_numeric(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 && !mdb->sql_field_is_not_null(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       if (type == ARG_LIST) { 
515          goto arg_list; 
516       } 
517  
518       Dmsg1(800, "list_result starts second loop looking at %d fields\n",  
519             mdb->sql_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       mdb->sql_field_seek(0); 
527       for (i = 0; i < mdb->sql_num_fields(); i++) { 
528          Dmsg1(800, "list_result looking at field %d\n", i); 
529          field = mdb->sql_fetch_field(); 
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    Dmsg1(800, "list_result starts third loop looking at %d fields\n",  
541          mdb->sql_num_fields()); 
542    mdb->sql_field_seek(0); 
543    send(ctx, "|"); 
544    for (i = 0; i < mdb->sql_num_fields(); i++) { 
545       field = mdb->sql_fetch_field(); 
546       if (!field) { 
547          break; 
548       } 
549       max_len = max_length(field->max_length); 
550       if (row[i] == NULL) { 
551          bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL"); 
552       } else if (mdb->sql_field_is_numeric(field->type) && !jcr->gui && is_an_integer(row[i])) { 
553          bsnprintf(buf, sizeof(buf), " %*s |", max_len, 
554                    add_commas(row[i], ewc)); 
555       } else { 
556          bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]); 
557       } 
558       send(ctx, buf); 
559    } 
560    send(ctx, "\n"); 
561    return 0; 
562  
563 vertical_list: 
564  
565    Dmsg1(800, "list_result starts vertical list at %d fields\n", mdb->sql_num_fields()); 
566    mdb->sql_field_seek(0); 
567    for (i = 0; i < mdb->sql_num_fields(); i++) { 
568       field = mdb->sql_fetch_field(); 
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 (mdb->sql_field_is_numeric(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 arg_list: 
586    Dmsg1(800, "list_result starts simple list at %d fields\n", mdb->sql_num_fields()); 
587    mdb->sql_field_seek(0); 
588    for (i = 0; i < mdb->sql_num_fields(); i++) { 
589       field = mdb->sql_fetch_field(); 
590       if (!field) { 
591          break; 
592       } 
593       if (row[i] == NULL) { 
594          bsnprintf(buf, sizeof(buf), "%s%s=", (i>0?" ":""), field->name); 
595       } else { 
596          bash_spaces(row[i]); 
597          bsnprintf(buf, sizeof(buf), "%s%s=%s ", (i>0?" ":""), field->name, row[i]); 
598       } 
599       send(ctx, buf); 
600    } 
601    send(ctx, "\n"); 
602    return 0; 
603  
604
605  
606 /* 
607  * If full_list is set, we list vertically, otherwise, we 
608  *  list on one line horizontally. 
609  * Return number of rows 
610  */ 
611 int 
612 list_result(JCR *jcr, BDB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type type) 
613
614    SQL_FIELD *field; 
615    SQL_ROW row; 
616    int i, col_len, max_len = 0; 
617    char buf[2000], ewc[30]; 
618  
619    Dmsg0(800, "list_result starts\n"); 
620    if (mdb->sql_num_rows() == 0) { 
621       send(ctx, _("No results to list.\n")); 
622       return mdb->sql_num_rows(); 
623    } 
624  
625    Dmsg1(800, "list_result starts looking at %d fields\n", mdb->sql_num_fields()); 
626    /* determine column display widths */ 
627    mdb->sql_field_seek(0); 
628    for (i = 0; i < mdb->sql_num_fields(); i++) { 
629       Dmsg1(800, "list_result processing field %d\n", i); 
630       field = mdb->sql_fetch_field(); 
631       if (!field) { 
632          break; 
633       } 
634       col_len = cstrlen(field->name); 
635       if (type == VERT_LIST) { 
636          if (col_len > max_len) { 
637             max_len = col_len; 
638          } 
639       } else { 
640          if (mdb->sql_field_is_numeric(field->type) && (int)field->max_length > 0) { /* fixup for commas */ 
641             field->max_length += (field->max_length - 1) / 3; 
642          } 
643          if (col_len < (int)field->max_length) { 
644             col_len = field->max_length; 
645          } 
646          if (col_len < 4 && !mdb->sql_field_is_not_null(field->flags)) { 
647             col_len = 4;                 /* 4 = length of the word "NULL" */ 
648          } 
649          field->max_length = col_len;    /* reset column info */ 
650       } 
651    } 
652  
653    Dmsg0(800, "list_result finished first loop\n"); 
654    if (type == VERT_LIST) { 
655       goto vertical_list; 
656    } 
657    if (type == ARG_LIST) { 
658       goto arg_list; 
659    } 
660  
661    Dmsg1(800, "list_result starts second loop looking at %d fields\n", mdb->sql_num_fields()); 
662    list_dashes(mdb, send, ctx); 
663    send(ctx, "|"); 
664    mdb->sql_field_seek(0); 
665    for (i = 0; i < mdb->sql_num_fields(); i++) { 
666       Dmsg1(800, "list_result looking at field %d\n", i); 
667       field = mdb->sql_fetch_field(); 
668       if (!field) { 
669          break; 
670       } 
671       max_len = max_length(field->max_length); 
672       bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name); 
673       send(ctx, buf); 
674    } 
675    send(ctx, "\n"); 
676    list_dashes(mdb, send, ctx); 
677  
678    Dmsg1(800, "list_result starts third loop looking at %d fields\n", mdb->sql_num_fields()); 
679    while ((row = mdb->sql_fetch_row()) != NULL) { 
680       mdb->sql_field_seek(0); 
681       send(ctx, "|"); 
682       for (i = 0; i < mdb->sql_num_fields(); i++) { 
683          field = mdb->sql_fetch_field(); 
684          if (!field) { 
685             break; 
686          } 
687          max_len = max_length(field->max_length); 
688          if (row[i] == NULL) { 
689             bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL"); 
690          } else if (mdb->sql_field_is_numeric(field->type) && !jcr->gui && is_an_integer(row[i])) { 
691             bsnprintf(buf, sizeof(buf), " %*s |", max_len, 
692                       add_commas(row[i], ewc)); 
693          } else {
694             strip_trailing_junk(row[i]);
695             bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]); 
696          } 
697          send(ctx, buf); 
698       } 
699       send(ctx, "\n"); 
700    } 
701    list_dashes(mdb, send, ctx); 
702    return mdb->sql_num_rows(); 
703  
704 vertical_list: 
705  
706    Dmsg1(800, "list_result starts vertical list at %d fields\n", mdb->sql_num_fields()); 
707    while ((row = mdb->sql_fetch_row()) != NULL) { 
708       mdb->sql_field_seek(0); 
709       for (i = 0; i < mdb->sql_num_fields(); i++) { 
710          field = mdb->sql_fetch_field(); 
711          if (!field) { 
712             break; 
713          } 
714          if (row[i] == NULL) { 
715             bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL"); 
716          } else if (mdb->sql_field_is_numeric(field->type) && !jcr->gui && is_an_integer(row[i])) { 
717             bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, 
718                 add_commas(row[i], ewc)); 
719          } else {
720             strip_trailing_junk(row[i]);
721             bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]); 
722          } 
723          send(ctx, buf); 
724       } 
725       send(ctx, "\n"); 
726    } 
727  
728 arg_list: 
729  
730    Dmsg1(800, "list_result starts arg list at %d fields\n", mdb->sql_num_fields()); 
731    while ((row = mdb->sql_fetch_row()) != NULL) { 
732       mdb->sql_field_seek(0); 
733       for (i = 0; i < mdb->sql_num_fields(); i++) { 
734          field = mdb->sql_fetch_field(); 
735          if (!field) { 
736             break; 
737          } 
738          if (row[i] == NULL) { 
739             bsnprintf(buf, sizeof(buf), "%s%s=", (i>0?" ":""), field->name); 
740          } else { 
741             bash_spaces(row[i]); 
742             bsnprintf(buf, sizeof(buf), "%s%s=%s", (i>0?" ":""), field->name, row[i]); 
743          } 
744          send(ctx, buf); 
745       } 
746       send(ctx, "\n"); 
747    } 
748    return mdb->sql_num_rows(); 
749
750  
751 /* 
752  * Open a new connexion to mdb catalog. This function is used 
753  * by batch and accurate mode. 
754  */ 
755 bool BDB::bdb_open_batch_connexion(JCR *jcr) 
756
757    bool multi_db; 
758  
759    multi_db = batch_insert_available(); 
760  
761    if (!jcr->db_batch) { 
762       jcr->db_batch = bdb_clone_database_connection(jcr, multi_db); 
763       if (!jcr->db_batch) { 
764          Mmsg0(&errmsg, _("Could not init database batch connection\n")); 
765          Jmsg(jcr, M_FATAL, 0, "%s", errmsg); 
766          return false; 
767       } 
768  
769       if (!jcr->db_batch->bdb_open_database(jcr)) { 
770          Mmsg2(&errmsg,  _("Could not open database \"%s\": ERR=%s\n"), 
771               jcr->db_batch->get_db_name(), jcr->db_batch->bdb_strerror()); 
772          Jmsg(jcr, M_FATAL, 0, "%s", errmsg); 
773          return false; 
774       } 
775    } 
776    return true; 
777
778  
779 /* 
780  * !!! WARNING !!! Use this function only when bacula is stopped. 
781  * ie, after a fatal signal and before exiting the program 
782  * Print information about a BDB object. 
783  */ 
784 void bdb_debug_print(JCR *jcr, FILE *fp) 
785
786    BDB *mdb = jcr->db; 
787  
788    if (!mdb) { 
789       return; 
790    } 
791  
792    fprintf(fp, "BDB=%p db_name=%s db_user=%s connected=%s\n", 
793            mdb, NPRTB(mdb->get_db_name()), NPRTB(mdb->get_db_user()), mdb->is_connected() ? "true" : "false"); 
794    fprintf(fp, "\tcmd=\"%s\" changes=%i\n", NPRTB(mdb->cmd), mdb->changes); 
795    mdb->print_lock_info(fp); 
796
797  
798 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */