]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql.c
Add simple way to add string elements to db_list_ctx
[bacula/bacula] / bacula / src / cats / sql.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  * Bacula Catalog Database interface routines
30  *
31  *     Almost generic set of SQL database interface routines
32  *      (with a little more work)
33  *     SQL engine specific routines are in mysql.c, postgresql.c,
34  *       sqlite.c, ...
35  *
36  *    Kern Sibbald, March 2000
37  *
38  *    Version $Id: sql.c 8034 2008-11-11 14:33:46Z ricozz $
39  */
40
41 #include "bacula.h"
42
43 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI
44
45 #include "cats.h"
46 #include "bdb_priv.h"
47 #include "sql_glue.h"
48
49 /* Forward referenced subroutines */
50 void print_dashes(B_DB *mdb);
51 void print_result(B_DB *mdb);
52
53 dbid_list::dbid_list()
54 {
55    memset(this, 0, sizeof(dbid_list));
56    max_ids = 1000;
57    DBId = (DBId_t *)malloc(max_ids * sizeof(DBId_t));
58    num_ids = num_seen = tot_ids = 0;
59    PurgedFiles = NULL;
60 }
61
62 dbid_list::~dbid_list()
63 {
64    free(DBId);
65 }
66
67 /*
68  * Called here to retrieve an integer from the database
69  */
70 int db_int_handler(void *ctx, int num_fields, char **row)
71 {
72    uint32_t *val = (uint32_t *)ctx;
73
74    Dmsg1(800, "int_handler starts with row pointing at %x\n", row);
75
76    if (row[0]) {
77       Dmsg1(800, "int_handler finds '%s'\n", row[0]);
78       *val = str_to_int64(row[0]);
79    } else {
80       Dmsg0(800, "int_handler finds zero\n");
81       *val = 0;
82    }
83    Dmsg0(800, "int_handler finishes\n");
84    return 0;
85 }
86
87 /*
88  * Called here to retrieve a 32/64 bit integer from the database.
89  *   The returned integer will be extended to 64 bit.
90  */
91 int db_int64_handler(void *ctx, int num_fields, char **row)
92 {
93    db_int64_ctx *lctx = (db_int64_ctx *)ctx;
94
95    if (row[0]) {
96       lctx->value = str_to_int64(row[0]);
97       lctx->count++;
98    }
99    return 0;
100 }
101
102 /*
103  * Use to build a comma separated list of values from a query. "10,20,30"
104  */
105 int db_list_handler(void *ctx, int num_fields, char **row)
106 {
107    db_list_ctx *lctx = (db_list_ctx *)ctx;
108    if (num_fields == 1 && row[0]) {
109       lctx->cat(row[0]);
110    }
111    return 0;
112 }
113
114 /*
115  *  * specific context passed from db_check_max_connections to db_max_connections_handler.
116  *   */
117 struct max_connections_context {
118    B_DB *db;
119    uint32_t nr_connections;
120 };
121
122 /*
123  *  * Called here to retrieve an integer from the database
124  *   */
125 static int db_max_connections_handler(void *ctx, int num_fields, char **row)
126 {
127    struct max_connections_context *context;
128    uint32_t index;
129
130    context = (struct max_connections_context *)ctx;
131    switch (db_get_type_index(context->db)) {
132    case SQL_TYPE_MYSQL:
133       index = 1;
134    default:
135       index = 0;
136    }
137
138    if (row[index]) {
139       context->nr_connections = str_to_int64(row[index]);
140    } else {
141       Dmsg0(800, "int_handler finds zero\n");
142       context->nr_connections = 0;
143    }
144    return 0;
145 }
146
147 /*
148  *  * Check catalog max_connections setting
149  *   */
150 bool db_check_max_connections(JCR *jcr, B_DB *mdb, uint32_t max_concurrent_jobs)
151 {
152    struct max_connections_context context;
153
154    /* Without Batch insert, no need to verify max_connections */
155    if (!mdb->batch_insert_available())
156       return true;
157
158    context.db = mdb;
159    context.nr_connections = 0;
160
161    /* Check max_connections setting */
162    if (!db_sql_query(mdb, sql_get_max_connections[db_get_type_index(mdb)],
163                      db_max_connections_handler, &context)) {
164       Jmsg(jcr, M_ERROR, 0, "Can't verify max_connections settings %s", mdb->errmsg);
165       return false;
166    }
167    if (context.nr_connections && max_concurrent_jobs && max_concurrent_jobs > context.nr_connections) {
168       Mmsg(mdb->errmsg,
169            _("Potential performance problem:\n"
170              "max_connections=%d set for %s database \"%s\" should be larger than Director's "
171              "MaxConcurrentJobs=%d\n"),
172            context.nr_connections, db_get_type(mdb), mdb->get_db_name(), max_concurrent_jobs);
173       Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
174       return false;
175    }
176
177    return true;
178 }
179
180 /* NOTE!!! The following routines expect that the
181  *  calling subroutine sets and clears the mutex
182  */
183
184 /* Check that the tables correspond to the version we want */
185 bool check_tables_version(JCR *jcr, B_DB *mdb)
186 {
187    uint32_t bacula_db_version = 0;
188    const char *query = "SELECT VersionId FROM Version";
189
190    bacula_db_version = 0;
191    if (!db_sql_query(mdb, query, db_int_handler, (void *)&bacula_db_version)) {
192       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
193       return false;
194    }
195    if (bacula_db_version != BDB_VERSION) {
196       Mmsg(mdb->errmsg, "Version error for database \"%s\". Wanted %d, got %d\n",
197           mdb->get_db_name(), BDB_VERSION, bacula_db_version);
198       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
199       return false;
200    }
201    return true;
202 }
203
204 /*
205  * Utility routine for queries. The database MUST be locked before calling here.
206  * Returns: 0 on failure
207  *          1 on success
208  */
209 int
210 QueryDB(const char *file, int line, JCR *jcr, B_DB *mdb, char *cmd)
211 {
212    sql_free_result(mdb);
213    if (!sql_query(mdb, cmd, QF_STORE_RESULT)) {
214       m_msg(file, line, &mdb->errmsg, _("query %s failed:\n%s\n"), cmd, sql_strerror(mdb));
215       j_msg(file, line, jcr, M_FATAL, 0, "%s", mdb->errmsg);
216       if (verbose) {
217          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
218       }
219       return 0;
220    }
221
222    return 1;
223 }
224
225 /*
226  * Utility routine to do inserts
227  * Returns: 0 on failure
228  *          1 on success
229  */
230 int
231 InsertDB(const char *file, int line, JCR *jcr, B_DB *mdb, char *cmd)
232 {
233    int num_rows;
234
235    if (!sql_query(mdb, cmd)) {
236       m_msg(file, line, &mdb->errmsg,  _("insert %s failed:\n%s\n"), cmd, sql_strerror(mdb));
237       j_msg(file, line, jcr, M_FATAL, 0, "%s", mdb->errmsg);
238       if (verbose) {
239          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
240       }
241       return 0;
242    }
243    num_rows = sql_affected_rows(mdb);
244    if (num_rows != 1) {
245       char ed1[30];
246       m_msg(file, line, &mdb->errmsg, _("Insertion problem: affected_rows=%s\n"),
247          edit_uint64(num_rows, ed1));
248       if (verbose) {
249          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
250       }
251       return 0;
252    }
253    mdb->changes++;
254    return 1;
255 }
256
257 /* Utility routine for updates.
258  *  Returns: 0 on failure
259  *           1 on success
260  */
261 int
262 UpdateDB(const char *file, int line, JCR *jcr, B_DB *mdb, char *cmd)
263 {
264    int num_rows;
265
266    if (!sql_query(mdb, cmd)) {
267       m_msg(file, line, &mdb->errmsg, _("update %s failed:\n%s\n"), cmd, sql_strerror(mdb));
268       j_msg(file, line, jcr, M_ERROR, 0, "%s", mdb->errmsg);
269       if (verbose) {
270          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
271       }
272       return 0;
273    }
274    num_rows = sql_affected_rows(mdb);
275    if (num_rows < 1) {
276       char ed1[30];
277       m_msg(file, line, &mdb->errmsg, _("Update failed: affected_rows=%s for %s\n"),
278          edit_uint64(num_rows, ed1), cmd);
279       if (verbose) {
280 //       j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
281       }
282       return 0;
283    }
284    mdb->changes++;
285    return 1;
286 }
287
288 /* Utility routine for deletes
289  *
290  * Returns: -1 on error
291  *           n number of rows affected
292  */
293 int
294 DeleteDB(const char *file, int line, JCR *jcr, B_DB *mdb, char *cmd)
295 {
296
297    if (!sql_query(mdb, cmd)) {
298       m_msg(file, line, &mdb->errmsg, _("delete %s failed:\n%s\n"), cmd, sql_strerror(mdb));
299       j_msg(file, line, jcr, M_ERROR, 0, "%s", mdb->errmsg);
300       if (verbose) {
301          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
302       }
303       return -1;
304    }
305    mdb->changes++;
306    return sql_affected_rows(mdb);
307 }
308
309
310 /*
311  * Get record max. Query is already in mdb->cmd
312  *  No locking done
313  *
314  * Returns: -1 on failure
315  *          count on success
316  */
317 int get_sql_record_max(JCR *jcr, B_DB *mdb)
318 {
319    SQL_ROW row;
320    int stat = 0;
321
322    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
323       if ((row = sql_fetch_row(mdb)) == NULL) {
324          Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
325          stat = -1;
326       } else {
327          stat = str_to_int64(row[0]);
328       }
329       sql_free_result(mdb);
330    } else {
331       Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
332       stat = -1;
333    }
334    return stat;
335 }
336
337 /*
338  *  * Return pre-edited error message
339  *   */
340 char *db_strerror(B_DB *mdb)
341 {
342    return mdb->errmsg;
343 }
344
345 /*
346  * Given a full filename, split it into its path
347  *  and filename parts. They are returned in pool memory
348  *  in the mdb structure.
349  */
350 void split_path_and_file(JCR *jcr, B_DB *mdb, const char *fname)
351 {
352    const char *p, *f;
353
354    /* Find path without the filename.
355     * I.e. everything after the last / is a "filename".
356     * OK, maybe it is a directory name, but we treat it like
357     * a filename. If we don't find a / then the whole name
358     * must be a path name (e.g. c:).
359     */
360    for (p=f=fname; *p; p++) {
361       if (IsPathSeparator(*p)) {
362          f = p;                       /* set pos of last slash */
363       }
364    }
365    if (IsPathSeparator(*f)) {                   /* did we find a slash? */
366       f++;                            /* yes, point to filename */
367    } else {                           /* no, whole thing must be path name */
368       f = p;
369    }
370
371    /* If filename doesn't exist (i.e. root directory), we
372     * simply create a blank name consisting of a single
373     * space. This makes handling zero length filenames
374     * easier.
375     */
376    mdb->fnl = p - f;
377    if (mdb->fnl > 0) {
378       mdb->fname = check_pool_memory_size(mdb->fname, mdb->fnl+1);
379       memcpy(mdb->fname, f, mdb->fnl);    /* copy filename */
380       mdb->fname[mdb->fnl] = 0;
381    } else {
382       mdb->fname[0] = 0;
383       mdb->fnl = 0;
384    }
385
386    mdb->pnl = f - fname;
387    if (mdb->pnl > 0) {
388       mdb->path = check_pool_memory_size(mdb->path, mdb->pnl+1);
389       memcpy(mdb->path, fname, mdb->pnl);
390       mdb->path[mdb->pnl] = 0;
391    } else {
392       Mmsg1(&mdb->errmsg, _("Path length is zero. File=%s\n"), fname);
393       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
394       mdb->path[0] = 0;
395       mdb->pnl = 0;
396    }
397
398    Dmsg2(500, "split path=%s file=%s\n", mdb->path, mdb->fname);
399 }
400
401 /*
402  * Set maximum field length to something reasonable
403  */
404 static int max_length(int max_length)
405 {
406    int max_len = max_length;
407    /* Sanity check */
408    if (max_len < 0) {
409       max_len = 2;
410    } else if (max_len > 100) {
411       max_len = 100;
412    }
413    return max_len;
414 }
415
416 /*
417  * List dashes as part of header for listing SQL results in a table
418  */
419 void
420 list_dashes(B_DB *mdb, DB_LIST_HANDLER *send, void *ctx)
421 {
422    SQL_FIELD  *field;
423    int i, j;
424    int len;
425
426    sql_field_seek(mdb, 0);
427    send(ctx, "+");
428    for (i = 0; i < sql_num_fields(mdb); i++) {
429       field = sql_fetch_field(mdb);
430       if (!field) {
431          break;
432       }
433       len = max_length(field->max_length + 2);
434       for (j = 0; j < len; j++) {
435          send(ctx, "-");
436       }
437       send(ctx, "+");
438    }
439    send(ctx, "\n");
440 }
441
442 /* Small handler to print the last line of a list xxx command */
443 static void last_line_handler(void *vctx, const char *str)
444 {
445    LIST_CTX *ctx = (LIST_CTX *)vctx;
446    bstrncat(ctx->line, str, sizeof(ctx->line));
447 }
448
449 int list_result(void *vctx, int nb_col, char **row)
450 {
451    SQL_FIELD *field;
452    int i, col_len, max_len = 0;
453    char buf[2000], ewc[30];
454
455    LIST_CTX *pctx = (LIST_CTX *)vctx;
456    DB_LIST_HANDLER *send = pctx->send;
457    e_list_type type = pctx->type;
458    B_DB *mdb = pctx->mdb;
459    void *ctx = pctx->ctx;
460    JCR *jcr = pctx->jcr;
461
462    if (!pctx->once) {
463       pctx->once = true;
464
465       Dmsg1(800, "list_result starts looking at %d fields\n", sql_num_fields(mdb));
466       /* determine column display widths */
467       sql_field_seek(mdb, 0);
468       for (i = 0; i < sql_num_fields(mdb); i++) {
469          Dmsg1(800, "list_result processing field %d\n", i);
470          field = sql_fetch_field(mdb);
471          if (!field) {
472             break;
473          }
474          col_len = cstrlen(field->name);
475          if (type == VERT_LIST) {
476             if (col_len > max_len) {
477                max_len = col_len;
478             }
479          } else {
480             if (sql_field_is_numeric(mdb, field->type) && (int)field->max_length > 0) { /* fixup for commas */
481                field->max_length += (field->max_length - 1) / 3;
482             }  
483             if (col_len < (int)field->max_length) {
484                col_len = field->max_length;
485             }  
486             if (col_len < 4 && !sql_field_is_not_null(mdb, field->flags)) {
487                col_len = 4;                 /* 4 = length of the word "NULL" */
488             }
489             field->max_length = col_len;    /* reset column info */
490          }
491       }
492
493       pctx->num_rows++;
494
495       Dmsg0(800, "list_result finished first loop\n");
496       if (type == VERT_LIST) {
497          goto vertical_list;
498       }
499
500       Dmsg1(800, "list_result starts second loop looking at %d fields\n", 
501             sql_num_fields(mdb));
502
503       /* Keep the result to display the same line at the end of the table */
504       list_dashes(mdb, last_line_handler, pctx);
505       send(ctx, pctx->line);
506
507       send(ctx, "|");
508       sql_field_seek(mdb, 0);
509       for (i = 0; i < sql_num_fields(mdb); i++) {
510          Dmsg1(800, "list_result looking at field %d\n", i);
511          field = sql_fetch_field(mdb);
512          if (!field) {
513             break;
514          }
515          max_len = max_length(field->max_length);
516          bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name);
517          send(ctx, buf);
518       }
519       send(ctx, "\n");
520       list_dashes(mdb, send, ctx);      
521    }
522    
523    Dmsg1(800, "list_result starts third loop looking at %d fields\n", 
524          sql_num_fields(mdb));
525
526    sql_field_seek(mdb, 0);
527    send(ctx, "|");
528    for (i = 0; i < sql_num_fields(mdb); i++) {
529       field = sql_fetch_field(mdb);
530       if (!field) {
531          break;
532       }
533       max_len = max_length(field->max_length);
534       if (row[i] == NULL) {
535          bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL");
536       } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) {
537          bsnprintf(buf, sizeof(buf), " %*s |", max_len,
538                    add_commas(row[i], ewc));
539       } else {
540          bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]);
541       }
542       send(ctx, buf);
543    }
544    send(ctx, "\n");
545    return 0;
546
547 vertical_list:
548
549    Dmsg1(800, "list_result starts vertical list at %d fields\n", sql_num_fields(mdb));
550
551    sql_field_seek(mdb, 0);
552    for (i = 0; i < sql_num_fields(mdb); i++) {
553       field = sql_fetch_field(mdb);
554       if (!field) {
555          break;
556       }
557       if (row[i] == NULL) {
558          bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL");
559       } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) {
560          bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name,
561                    add_commas(row[i], ewc));
562       } else {
563          bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]);
564       }
565       send(ctx, buf);
566    }
567    send(ctx, "\n");
568    return 0;
569 }
570
571 /* 
572  * Open a new connexion to mdb catalog. This function is used
573  * by batch and accurate mode.
574  */
575 bool db_open_batch_connexion(JCR *jcr, B_DB *mdb)
576 {
577    bool multi_db;
578
579    if (mdb->batch_insert_available())
580       multi_db = true;   /* we force a new connection only if batch insert is enabled */
581    else
582       multi_db = false;
583
584    if (!jcr->db_batch) {
585       jcr->db_batch = db_clone_database_connection(mdb, jcr, multi_db);
586       if (!jcr->db_batch) {
587          Mmsg0(&mdb->errmsg, _("Could not init database batch connection"));
588          Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
589          return false;
590       }
591
592       if (!db_open_database(jcr, jcr->db_batch)) {
593          Mmsg2(&mdb->errmsg,  _("Could not open database \"%s\": ERR=%s\n"),
594               jcr->db_batch->get_db_name(), db_strerror(jcr->db_batch));
595          Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
596          return false;
597       }      
598    }
599    return true;
600 }
601
602 /*
603  * !!! WARNING !!! Use this function only when bacula is stopped.
604  * ie, after a fatal signal and before exiting the program
605  * Print information about a B_DB object.
606  */
607 void db_debug_print(JCR *jcr, FILE *fp)
608 {
609    B_DB *mdb = jcr->db;
610
611    if (!mdb) {
612       return;
613    }
614
615    fprintf(fp, "B_DB=%p db_name=%s db_user=%s connected=%s\n",
616            mdb, NPRTB(mdb->get_db_name()), NPRTB(mdb->get_db_user()), mdb->is_connected() ? "true" : "false");
617    fprintf(fp, "\tcmd=\"%s\" changes=%i\n", NPRTB(mdb->cmd), mdb->changes);
618    mdb->print_lock_info(fp);
619 }
620
621 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI */