]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql.c
Tweak fix typo in comment
[bacula/bacula] / bacula / src / cats / sql.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 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 BDB::BDB()
171 {
172    init_acl();
173    acl_join = get_pool_memory(PM_MESSAGE);
174    acl_where = get_pool_memory(PM_MESSAGE);
175 }
176
177 BDB::~BDB()
178 {
179    free_acl();
180    free_pool_memory(acl_join);
181    free_pool_memory(acl_where);
182 }
183
184 /* Get the WHERE section of a query that permits to respect
185  * the console ACLs.
186  *
187  *  get_acls(DB_ACL_BIT(DB_ACL_JOB) | DB_ACL_BIT(DB_ACL_CLIENT), true)
188  *     -> WHERE Job.Name IN ('a', 'b', 'c') AND Client.Name IN ('d', 'e')
189  *
190  *  get_acls(DB_ACL_BIT(DB_ACL_JOB) | DB_ACL_BIT(DB_ACL_CLIENT), false)
191  *     -> AND Job.Name IN ('a', 'b', 'c') AND Client.Name IN ('d', 'e')
192  */
193 char *BDB::get_acls(int tables, bool where /* use WHERE or AND */)
194 {
195    POOL_MEM tmp;
196    pm_strcpy(acl_where, "");
197
198    for (int i=0 ;  i < DB_ACL_LAST; i++) {
199       if (tables & DB_ACL_BIT(i)) {
200          pm_strcat(acl_where, get_acl((DB_ACL_t)i, where));
201          where = acl_where[0] == 0 && where;
202       }
203    }
204    return acl_where;
205 }
206
207 /* Create the JOIN string that will help to filter queries results */
208 char *BDB::get_acl_join_filter(int tables)
209 {
210    POOL_MEM tmp;
211    pm_strcpy(acl_join, "");
212
213    if (tables & DB_ACL_BIT(DB_ACL_JOB)) {
214       Mmsg(tmp, " JOIN Job USING (JobId) ");
215       pm_strcat(acl_join, tmp);
216    }
217    if (tables & (DB_ACL_BIT(DB_ACL_CLIENT) | DB_ACL_BIT(DB_ACL_RCLIENT) | DB_ACL_BIT(DB_ACL_BCLIENT))) {
218       Mmsg(tmp, " JOIN Client USING (ClientId) ");
219       pm_strcat(acl_join, tmp);
220    }
221    if (tables & DB_ACL_BIT(DB_ACL_POOL)) {
222       Mmsg(tmp, " JOIN Pool USING (PoolId) ");
223       pm_strcat(acl_join, tmp);
224    }
225    if (tables & DB_ACL_BIT(DB_ACL_PATH)) {
226       Mmsg(tmp, " JOIN Path USING (PathId) ");
227       pm_strcat(acl_join, tmp);
228    }
229    if (tables & DB_ACL_BIT(DB_ACL_LOG)) {
230       Mmsg(tmp, " JOIN Log USING (JobId) ");
231       pm_strcat(acl_join, tmp);
232    }
233    if (tables & DB_ACL_BIT(DB_ACL_FILESET)) {
234       Mmsg(tmp, " LEFT JOIN FileSet USING (FileSetId) ");
235       pm_strcat(acl_join, tmp);
236    }
237    return acl_join;
238 }
239
240 /* Intialize the ACL list */
241 void BDB::init_acl()
242 {
243    for(int i=0; i < DB_ACL_LAST; i++) {
244       acls[i] = NULL;
245    }
246 }
247
248 /* Free ACL list */
249 void BDB::free_acl()
250 {
251    for(int i=0; i < DB_ACL_LAST; i++) {
252       free_and_null_pool_memory(acls[i]);
253    }
254 }
255
256 /* Get ACL for a given type */
257 const char *BDB::get_acl(DB_ACL_t type, bool where /* display WHERE or AND */)
258 {
259    if (!acls[type]) {
260       return "";
261    }
262    strcpy(acls[type], where?" WHERE ":"   AND ");
263    acls[type][7] = ' ' ;        /* replace \0 by ' ' */
264    return acls[type];
265 }
266
267 /* Keep UAContext ACLs in our structure for further SQL queries */
268 void BDB::set_acl(JCR *jcr, DB_ACL_t type, alist *list, alist *list2)
269 {
270    /* If the list is present, but we authorize everything */
271    if (list && list->size() == 1 && strcasecmp((char*)list->get(0), "*all*") == 0) {
272       return;
273    }
274
275    /* If the list is present, but we authorize everything */
276    if (list2 && list2->size() == 1 && strcasecmp((char*)list2->get(0), "*all*") == 0) {
277       return;
278    }
279
280    POOLMEM *tmp = get_pool_memory(PM_FNAME);
281    POOLMEM *where = get_pool_memory(PM_FNAME);
282
283    *where = 0;
284    *tmp = 0;
285
286    /* For clients, we can have up to 2 lists */
287    escape_acl_list(jcr, &tmp, list);
288    escape_acl_list(jcr, &tmp, list2);
289
290    switch(type) {
291    case DB_ACL_JOB:
292       Mmsg(where, "   AND  Job.Name IN (%s) ", tmp);
293       break;
294    case DB_ACL_CLIENT:
295       Mmsg(where, "   AND  Client.Name IN (%s) ", tmp);
296       break;
297    case DB_ACL_BCLIENT:
298       Mmsg(where, "   AND  Client.Name IN (%s) ", tmp);
299       break;
300    case DB_ACL_RCLIENT:
301       Mmsg(where, "   AND  Client.Name IN (%s) ", tmp);
302       break;
303    case  DB_ACL_FILESET:
304       Mmsg(where, "   AND  (FileSetId = 0 OR FileSet.FileSet IN (%s)) ", tmp);
305       break;
306    case DB_ACL_POOL:
307       Mmsg(where, "   AND  (PoolId = 0 OR Pool.Name IN (%s)) ", tmp);
308       break;
309    default:
310       break;
311    }
312    acls[type] = where;
313    free_pool_memory(tmp);
314 }
315
316 /* Convert a ACL list to a SQL IN() list */
317 char *BDB::escape_acl_list(JCR *jcr, POOLMEM **escaped_list, alist *lst)
318 {
319    char *elt;
320    int len;
321    POOL_MEM tmp;
322
323    if (!lst) {
324       return *escaped_list;     /* TODO: check how we handle the empty list */
325
326    /* List is empty, reject everything */
327    } else if (lst->size() == 0) {
328       Mmsg(escaped_list, "''");
329       return *escaped_list;
330    }
331
332    foreach_alist(elt, lst) {
333       if (elt && *elt) {
334          len = strlen(elt);
335          /* Escape + ' ' */
336          tmp.check_size(2 * len + 2 + 2);
337
338          pm_strcpy(tmp, "'");
339          bdb_lock();
340          bdb_escape_string(jcr, tmp.c_str() + 1 , elt, len);
341          bdb_unlock();
342          pm_strcat(tmp, "'");
343
344          if (*escaped_list[0]) {
345             pm_strcat(escaped_list, ",");
346          }
347
348          pm_strcat(escaped_list, tmp.c_str());
349       }
350    }
351    return *escaped_list;
352 }
353
354 /* 
355  * Check catalog max_connections setting 
356  */ 
357 bool BDB::bdb_check_max_connections(JCR *jcr, uint32_t max_concurrent_jobs) 
358
359    struct max_connections_context context; 
360    
361    /* Without Batch insert, no need to verify max_connections */ 
362    if (!batch_insert_available()) 
363       return true; 
364  
365    context.db = this; 
366    context.nr_connections = 0; 
367  
368    /* Check max_connections setting */ 
369    if (!bdb_sql_query(sql_get_max_connections[bdb_get_type_index()], 
370                      db_max_connections_handler, &context)) { 
371       Jmsg(jcr, M_ERROR, 0, "Can't verify max_connections settings %s", errmsg); 
372       return false; 
373    } 
374    if (context.nr_connections && max_concurrent_jobs && max_concurrent_jobs > context.nr_connections) { 
375       Mmsg(errmsg, 
376            _("Potential performance problem:\n" 
377              "max_connections=%d set for %s database \"%s\" should be larger than Director's " 
378              "MaxConcurrentJobs=%d\n"), 
379            context.nr_connections, bdb_get_engine_name(), get_db_name(), max_concurrent_jobs); 
380       Jmsg(jcr, M_WARNING, 0, "%s", errmsg); 
381       return false; 
382    } 
383  
384    return true; 
385
386  
387 /* NOTE!!! The following routines expect that the 
388  *  calling subroutine sets and clears the mutex 
389  */ 
390  
391 /* Check that the tables correspond to the version we want */ 
392 bool BDB::bdb_check_version(JCR *jcr) 
393
394    uint32_t bacula_db_version = 0; 
395    const char *query = "SELECT VersionId FROM Version"; 
396  
397    bacula_db_version = 0; 
398    if (!bdb_sql_query(query, db_int_handler, (void *)&bacula_db_version)) { 
399       Jmsg(jcr, M_FATAL, 0, "%s", errmsg); 
400       return false; 
401    } 
402    if (bacula_db_version != BDB_VERSION) { 
403       Mmsg(errmsg, "Version error for database \"%s\". Wanted %d, got %d\n", 
404           get_db_name(), BDB_VERSION, bacula_db_version); 
405       Jmsg(jcr, M_FATAL, 0, "%s", errmsg); 
406       return false; 
407    } 
408    return true; 
409
410  
411 /* 
412  * Utility routine for queries. The database MUST be locked before calling here. 
413  * Returns: 0 on failure 
414  *          1 on success 
415  */ 
416 bool BDB::QueryDB(JCR *jcr, char *cmd, const char *file, int line) 
417
418    sql_free_result(); 
419    if (!sql_query(cmd, QF_STORE_RESULT)) { 
420       m_msg(file, line, &errmsg, _("query %s failed:\n%s\n"), cmd, sql_strerror()); 
421       if (use_fatal_jmsg()) { 
422          j_msg(file, line, jcr, M_FATAL, 0, "%s", errmsg); 
423       } 
424       if (verbose) { 
425          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd); 
426       } 
427       return false; 
428    } 
429  
430    return true; 
431
432  
433 /* 
434  * Utility routine to do inserts 
435  * Returns: 0 on failure 
436  *          1 on success 
437  */ 
438 bool BDB::InsertDB(JCR *jcr, char *cmd, const char *file, int line) 
439
440    if (!sql_query(cmd)) { 
441       m_msg(file, line, &errmsg,  _("insert %s failed:\n%s\n"), cmd, sql_strerror()); 
442       if (use_fatal_jmsg()) { 
443          j_msg(file, line, jcr, M_FATAL, 0, "%s", errmsg); 
444       } 
445       if (verbose) { 
446          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd); 
447       } 
448       return false; 
449    } 
450    int num_rows = sql_affected_rows(); 
451    if (num_rows != 1) { 
452       char ed1[30]; 
453       m_msg(file, line, &errmsg, _("Insertion problem: affected_rows=%s\n"), 
454          edit_uint64(num_rows, ed1)); 
455       if (verbose) { 
456          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd); 
457       } 
458       return false; 
459    } 
460    changes++; 
461    return true; 
462
463  
464 /* Utility routine for updates. 
465  *  Returns: false on failure 
466  *           true  on success 
467  *
468  * Some UPDATE queries must update record(s), other queries might not update
469  * anything.
470  */ 
471 bool BDB::UpdateDB(JCR *jcr, char *cmd, bool can_be_empty,
472                    const char *file, int line) 
473
474    if (!sql_query(cmd)) { 
475       m_msg(file, line, &errmsg, _("update %s failed:\n%s\n"), cmd, sql_strerror()); 
476       j_msg(file, line, jcr, M_ERROR, 0, "%s", errmsg); 
477       if (verbose) { 
478          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd); 
479       } 
480       return false; 
481    } 
482    int num_rows = sql_affected_rows(); 
483    if ((num_rows == 0 && !can_be_empty) || num_rows < 0) { 
484       char ed1[30]; 
485       m_msg(file, line, &errmsg, _("Update failed: affected_rows=%s for %s\n"), 
486          edit_uint64(num_rows, ed1), cmd); 
487       if (verbose) { 
488 //       j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd); 
489       } 
490       return false; 
491    } 
492    changes++; 
493    return true; 
494
495  
496 /* Utility routine for deletes 
497  * 
498  * Returns: -1 on error 
499  *           n number of rows affected 
500  */ 
501 int BDB::DeleteDB(JCR *jcr, char *cmd, const char *file, int line) 
502
503  
504    if (!sql_query(cmd)) { 
505       m_msg(file, line, &errmsg, _("delete %s failed:\n%s\n"), cmd, sql_strerror()); 
506       j_msg(file, line, jcr, M_ERROR, 0, "%s", errmsg); 
507       if (verbose) { 
508          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd); 
509       } 
510       return -1; 
511    } 
512    changes++; 
513    return sql_affected_rows(); 
514
515  
516  
517 /* 
518  * Get record max. Query is already in mdb->cmd 
519  *  No locking done 
520  * 
521  * Returns: -1 on failure 
522  *          count on success 
523  */ 
524 int get_sql_record_max(JCR *jcr, BDB *mdb) 
525
526    SQL_ROW row; 
527    int stat = 0; 
528  
529    if (mdb->QueryDB(jcr, mdb->cmd)) { 
530       if ((row = mdb->sql_fetch_row()) == NULL) { 
531          Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), mdb->sql_strerror()); 
532          stat = -1; 
533       } else { 
534          stat = str_to_int64(row[0]); 
535       } 
536       mdb->sql_free_result(); 
537    } else { 
538       Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), mdb->sql_strerror()); 
539       stat = -1; 
540    } 
541    return stat; 
542
543  
544 /* 
545  * Given a full filename, split it into its path 
546  *  and filename parts. They are returned in pool memory 
547  *  in the mdb structure. 
548  */ 
549 void split_path_and_file(JCR *jcr, BDB *mdb, const char *afname) 
550
551    const char *p, *f; 
552  
553    /* Find path without the filename. 
554     * I.e. everything after the last / is a "filename". 
555     * OK, maybe it is a directory name, but we treat it like 
556     * a filename. If we don't find a / then the whole name 
557     * must be a path name (e.g. c:). 
558     */ 
559    for (p=f=afname; *p; p++) { 
560       if (IsPathSeparator(*p)) { 
561          f = p;                       /* set pos of last slash */ 
562       } 
563    } 
564    if (IsPathSeparator(*f)) {                   /* did we find a slash? */ 
565       f++;                            /* yes, point to filename */ 
566    } else {                           /* no, whole thing must be path name */ 
567       f = p; 
568    } 
569  
570    /* If filename doesn't exist (i.e. root directory), we 
571     * simply create a blank name consisting of a single 
572     * space. This makes handling zero length filenames 
573     * easier. 
574     */ 
575    mdb->fnl = p - f; 
576    if (mdb->fnl > 0) { 
577       mdb->fname = check_pool_memory_size(mdb->fname, mdb->fnl+1); 
578       memcpy(mdb->fname, f, mdb->fnl);    /* copy filename */ 
579       mdb->fname[mdb->fnl] = 0; 
580    } else { 
581       mdb->fname[0] = 0; 
582       mdb->fnl = 0; 
583    } 
584  
585    mdb->pnl = f - afname; 
586    if (mdb->pnl > 0) { 
587       mdb->path = check_pool_memory_size(mdb->path, mdb->pnl+1); 
588       memcpy(mdb->path, afname, mdb->pnl); 
589       mdb->path[mdb->pnl] = 0; 
590    } else { 
591       Mmsg1(&mdb->errmsg, _("Path length is zero. File=%s\n"), afname); 
592       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg); 
593       mdb->path[0] = 0; 
594       mdb->pnl = 0; 
595    } 
596  
597    Dmsg3(500, "split fname=%s: path=%s file=%s\n", afname, mdb->path, mdb->fname); 
598
599  
600 /* 
601  * Set maximum field length to something reasonable 
602  */ 
603 static int max_length(int max_length) 
604
605    int max_len = max_length; 
606    /* Sanity check */ 
607    if (max_len < 0) { 
608       max_len = 2; 
609    } else if (max_len > 100) { 
610       max_len = 100; 
611    } 
612    return max_len; 
613
614  
615 /* 
616  * List dashes as part of header for listing SQL results in a table 
617  */ 
618 void 
619 list_dashes(BDB *mdb, DB_LIST_HANDLER *send, void *ctx) 
620
621    SQL_FIELD  *field; 
622    int i, j; 
623    int len; 
624  
625    mdb->sql_field_seek(0); 
626    send(ctx, "+"); 
627    for (i = 0; i < mdb->sql_num_fields(); i++) { 
628       field = mdb->sql_fetch_field(); 
629       if (!field) { 
630          break; 
631       } 
632       len = max_length(field->max_length + 2); 
633       for (j = 0; j < len; j++) { 
634          send(ctx, "-"); 
635       } 
636       send(ctx, "+"); 
637    } 
638    send(ctx, "\n"); 
639
640  
641 /* Small handler to print the last line of a list xxx command */ 
642 static void last_line_handler(void *vctx, const char *str) 
643
644    LIST_CTX *ctx = (LIST_CTX *)vctx; 
645    bstrncat(ctx->line, str, sizeof(ctx->line)); 
646
647  
648 int list_result(void *vctx, int nb_col, char **row) 
649
650    SQL_FIELD *field; 
651    int i, col_len, max_len = 0; 
652    char buf[2000], ewc[30]; 
653  
654    LIST_CTX *pctx = (LIST_CTX *)vctx; 
655    DB_LIST_HANDLER *send = pctx->send; 
656    e_list_type type = pctx->type; 
657    BDB *mdb = pctx->mdb; 
658    void *ctx = pctx->ctx; 
659    JCR *jcr = pctx->jcr; 
660  
661    if (!pctx->once) { 
662       pctx->once = true; 
663  
664       Dmsg1(800, "list_result starts looking at %d fields\n", mdb->sql_num_fields()); 
665       /* determine column display widths */ 
666       mdb->sql_field_seek(0); 
667       for (i = 0; i < mdb->sql_num_fields(); i++) { 
668          Dmsg1(800, "list_result processing field %d\n", i); 
669          field = mdb->sql_fetch_field(); 
670          if (!field) { 
671             break; 
672          } 
673          col_len = cstrlen(field->name); 
674          if (type == VERT_LIST) { 
675             if (col_len > max_len) { 
676                max_len = col_len; 
677             } 
678          } else { 
679             if (mdb->sql_field_is_numeric(field->type) && (int)field->max_length > 0) { /* fixup for commas */ 
680                field->max_length += (field->max_length - 1) / 3; 
681             } 
682             if (col_len < (int)field->max_length) { 
683                col_len = field->max_length; 
684             } 
685             if (col_len < 4 && !mdb->sql_field_is_not_null(field->flags)) { 
686                col_len = 4;                 /* 4 = length of the word "NULL" */ 
687             } 
688             field->max_length = col_len;    /* reset column info */ 
689          } 
690       } 
691  
692       pctx->num_rows++; 
693  
694       Dmsg0(800, "list_result finished first loop\n"); 
695       if (type == VERT_LIST) { 
696          goto vertical_list; 
697       } 
698       if (type == ARG_LIST) { 
699          goto arg_list; 
700       } 
701  
702       Dmsg1(800, "list_result starts second loop looking at %d fields\n",  
703             mdb->sql_num_fields()); 
704  
705       /* Keep the result to display the same line at the end of the table */ 
706       list_dashes(mdb, last_line_handler, pctx); 
707       send(ctx, pctx->line); 
708  
709       send(ctx, "|"); 
710       mdb->sql_field_seek(0); 
711       for (i = 0; i < mdb->sql_num_fields(); i++) { 
712          Dmsg1(800, "list_result looking at field %d\n", i); 
713          field = mdb->sql_fetch_field(); 
714          if (!field) { 
715             break; 
716          } 
717          max_len = max_length(field->max_length); 
718          bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name); 
719          send(ctx, buf); 
720       } 
721       send(ctx, "\n"); 
722       list_dashes(mdb, send, ctx); 
723    } 
724    Dmsg1(800, "list_result starts third loop looking at %d fields\n",  
725          mdb->sql_num_fields()); 
726    mdb->sql_field_seek(0); 
727    send(ctx, "|"); 
728    for (i = 0; i < mdb->sql_num_fields(); i++) { 
729       field = mdb->sql_fetch_field(); 
730       if (!field) { 
731          break; 
732       } 
733       max_len = max_length(field->max_length); 
734       if (row[i] == NULL) { 
735          bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL"); 
736       } else if (mdb->sql_field_is_numeric(field->type) && !jcr->gui && is_an_integer(row[i])) { 
737          bsnprintf(buf, sizeof(buf), " %*s |", max_len, 
738                    add_commas(row[i], ewc)); 
739       } else { 
740          bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]); 
741       } 
742       send(ctx, buf); 
743    } 
744    send(ctx, "\n"); 
745    return 0; 
746  
747 vertical_list: 
748  
749    Dmsg1(800, "list_result starts vertical list at %d fields\n", mdb->sql_num_fields()); 
750    mdb->sql_field_seek(0); 
751    for (i = 0; i < mdb->sql_num_fields(); i++) { 
752       field = mdb->sql_fetch_field(); 
753       if (!field) { 
754          break; 
755       } 
756       if (row[i] == NULL) { 
757          bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL"); 
758       } else if (mdb->sql_field_is_numeric(field->type) && !jcr->gui && is_an_integer(row[i])) { 
759          bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, 
760                    add_commas(row[i], ewc)); 
761       } else { 
762          bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]); 
763       } 
764       send(ctx, buf); 
765    } 
766    send(ctx, "\n"); 
767    return 0; 
768  
769 arg_list: 
770    Dmsg1(800, "list_result starts simple list at %d fields\n", mdb->sql_num_fields()); 
771    mdb->sql_field_seek(0); 
772    for (i = 0; i < mdb->sql_num_fields(); i++) { 
773       field = mdb->sql_fetch_field(); 
774       if (!field) { 
775          break; 
776       } 
777       if (row[i] == NULL) { 
778          bsnprintf(buf, sizeof(buf), "%s%s=", (i>0?" ":""), field->name); 
779       } else { 
780          bash_spaces(row[i]); 
781          bsnprintf(buf, sizeof(buf), "%s%s=%s ", (i>0?" ":""), field->name, row[i]); 
782       } 
783       send(ctx, buf); 
784    } 
785    send(ctx, "\n"); 
786    return 0; 
787  
788
789  
790 /* 
791  * If full_list is set, we list vertically, otherwise, we 
792  *  list on one line horizontally. 
793  * Return number of rows 
794  */ 
795 int 
796 list_result(JCR *jcr, BDB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type type) 
797
798    SQL_FIELD *field; 
799    SQL_ROW row; 
800    int i, col_len, max_len = 0; 
801    char buf[2000], ewc[30]; 
802  
803    Dmsg0(800, "list_result starts\n"); 
804    if (mdb->sql_num_rows() == 0) { 
805       send(ctx, _("No results to list.\n")); 
806       return mdb->sql_num_rows(); 
807    } 
808  
809    Dmsg1(800, "list_result starts looking at %d fields\n", mdb->sql_num_fields()); 
810    /* determine column display widths */ 
811    mdb->sql_field_seek(0); 
812    for (i = 0; i < mdb->sql_num_fields(); i++) { 
813       Dmsg1(800, "list_result processing field %d\n", i); 
814       field = mdb->sql_fetch_field(); 
815       if (!field) { 
816          break; 
817       } 
818       col_len = cstrlen(field->name); 
819       if (type == VERT_LIST) { 
820          if (col_len > max_len) { 
821             max_len = col_len; 
822          } 
823       } else { 
824          if (mdb->sql_field_is_numeric(field->type) && (int)field->max_length > 0) { /* fixup for commas */ 
825             field->max_length += (field->max_length - 1) / 3; 
826          } 
827          if (col_len < (int)field->max_length) { 
828             col_len = field->max_length; 
829          } 
830          if (col_len < 4 && !mdb->sql_field_is_not_null(field->flags)) { 
831             col_len = 4;                 /* 4 = length of the word "NULL" */ 
832          } 
833          field->max_length = col_len;    /* reset column info */ 
834       } 
835    } 
836  
837    Dmsg0(800, "list_result finished first loop\n"); 
838    if (type == VERT_LIST) { 
839       goto vertical_list; 
840    } 
841    if (type == ARG_LIST) { 
842       goto arg_list; 
843    } 
844  
845    Dmsg1(800, "list_result starts second loop looking at %d fields\n", mdb->sql_num_fields()); 
846    list_dashes(mdb, send, ctx); 
847    send(ctx, "|"); 
848    mdb->sql_field_seek(0); 
849    for (i = 0; i < mdb->sql_num_fields(); i++) { 
850       Dmsg1(800, "list_result looking at field %d\n", i); 
851       field = mdb->sql_fetch_field(); 
852       if (!field) { 
853          break; 
854       } 
855       max_len = max_length(field->max_length); 
856       bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name); 
857       send(ctx, buf); 
858    } 
859    send(ctx, "\n"); 
860    list_dashes(mdb, send, ctx); 
861  
862    Dmsg1(800, "list_result starts third loop looking at %d fields\n", mdb->sql_num_fields()); 
863    while ((row = mdb->sql_fetch_row()) != NULL) { 
864       mdb->sql_field_seek(0); 
865       send(ctx, "|"); 
866       for (i = 0; i < mdb->sql_num_fields(); i++) { 
867          field = mdb->sql_fetch_field(); 
868          if (!field) { 
869             break; 
870          } 
871          max_len = max_length(field->max_length); 
872          if (row[i] == NULL) { 
873             bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL"); 
874          } else if (mdb->sql_field_is_numeric(field->type) && !jcr->gui && is_an_integer(row[i])) { 
875             bsnprintf(buf, sizeof(buf), " %*s |", max_len, 
876                       add_commas(row[i], ewc)); 
877          } else {
878             strip_trailing_junk(row[i]);
879             bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]); 
880          } 
881          send(ctx, buf); 
882       } 
883       send(ctx, "\n"); 
884    } 
885    list_dashes(mdb, send, ctx); 
886    return mdb->sql_num_rows(); 
887  
888 vertical_list: 
889  
890    Dmsg1(800, "list_result starts vertical list at %d fields\n", mdb->sql_num_fields()); 
891    while ((row = mdb->sql_fetch_row()) != NULL) { 
892       mdb->sql_field_seek(0); 
893       for (i = 0; i < mdb->sql_num_fields(); i++) { 
894          field = mdb->sql_fetch_field(); 
895          if (!field) { 
896             break; 
897          } 
898          if (row[i] == NULL) { 
899             bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL"); 
900          } else if (mdb->sql_field_is_numeric(field->type) && !jcr->gui && is_an_integer(row[i])) { 
901             bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, 
902                 add_commas(row[i], ewc)); 
903          } else {
904             strip_trailing_junk(row[i]);
905             bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]); 
906          } 
907          send(ctx, buf); 
908       } 
909       send(ctx, "\n"); 
910    } 
911  
912 arg_list: 
913  
914    Dmsg1(800, "list_result starts arg list at %d fields\n", mdb->sql_num_fields()); 
915    while ((row = mdb->sql_fetch_row()) != NULL) { 
916       mdb->sql_field_seek(0); 
917       for (i = 0; i < mdb->sql_num_fields(); i++) { 
918          field = mdb->sql_fetch_field(); 
919          if (!field) { 
920             break; 
921          } 
922          if (row[i] == NULL) { 
923             bsnprintf(buf, sizeof(buf), "%s%s=", (i>0?" ":""), field->name); 
924          } else { 
925             bash_spaces(row[i]); 
926             bsnprintf(buf, sizeof(buf), "%s%s=%s", (i>0?" ":""), field->name, row[i]); 
927          } 
928          send(ctx, buf); 
929       } 
930       send(ctx, "\n"); 
931    } 
932    return mdb->sql_num_rows(); 
933
934  
935 /* 
936  * Open a new connexion to mdb catalog. This function is used 
937  * by batch and accurate mode. 
938  */ 
939 bool BDB::bdb_open_batch_connexion(JCR *jcr) 
940
941    bool multi_db; 
942  
943    multi_db = batch_insert_available(); 
944  
945    if (!jcr->db_batch) { 
946       jcr->db_batch = bdb_clone_database_connection(jcr, multi_db); 
947       if (!jcr->db_batch) { 
948          Mmsg0(&errmsg, _("Could not init database batch connection\n")); 
949          Jmsg(jcr, M_FATAL, 0, "%s", errmsg); 
950          return false; 
951       } 
952  
953       if (!jcr->db_batch->bdb_open_database(jcr)) { 
954          Mmsg2(&errmsg,  _("Could not open database \"%s\": ERR=%s\n"), 
955               jcr->db_batch->get_db_name(), jcr->db_batch->bdb_strerror()); 
956          Jmsg(jcr, M_FATAL, 0, "%s", errmsg); 
957          return false; 
958       } 
959    } 
960    return true; 
961
962  
963 /* 
964  * !!! WARNING !!! Use this function only when bacula is stopped. 
965  * ie, after a fatal signal and before exiting the program 
966  * Print information about a BDB object. 
967  */ 
968 void bdb_debug_print(JCR *jcr, FILE *fp) 
969
970    BDB *mdb = jcr->db; 
971  
972    if (!mdb) { 
973       return; 
974    } 
975  
976    fprintf(fp, "BDB=%p db_name=%s db_user=%s connected=%s\n", 
977            mdb, NPRTB(mdb->get_db_name()), NPRTB(mdb->get_db_user()), mdb->is_connected() ? "true" : "false"); 
978    fprintf(fp, "\tcmd=\"%s\" changes=%i\n", NPRTB(mdb->cmd), mdb->changes); 
979    mdb->print_lock_info(fp); 
980
981  
982 bool BDB::bdb_check_settings(JCR *jcr, int64_t *starttime, int val, int64_t val2) 
983
984    return true; 
985
986  
987 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */