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