]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql.c
Correct improperly formated list command output reported by Dan.
[bacula/bacula] / bacula / src / cats / sql.c
1 /*
2  * Bacula Catalog Database interface routines
3  *
4  *     Almost generic set of SQL database interface routines
5  *      (with a little more work)
6  *
7  *    Kern Sibbald, March 2000
8  *
9  *    Version $Id$
10  */
11
12 /*
13    Copyright (C) 2000-2006 Kern Sibbald
14
15    This program is free software; you can redistribute it and/or
16    modify it under the terms of the GNU General Public License
17    version 2 as amended with additional clauses defined in the
18    file LICENSE in the main source directory.
19
20    This program is distributed in the hope that it will be useful,
21    but WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
23    the file LICENSE for additional details.
24
25  */
26
27 /* The following is necessary so that we do not include
28  * the dummy external definition of B_DB.
29  */
30 #define __SQL_C                       /* indicate that this is sql.c */
31
32 #include "bacula.h"
33 #include "cats.h"
34
35 #if    HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL
36
37 uint32_t bacula_db_version = 0;
38
39 /* Forward referenced subroutines */
40 void print_dashes(B_DB *mdb);
41 void print_result(B_DB *mdb);
42
43 /*
44  * Called here to retrieve an integer from the database
45  */
46 static int int_handler(void *ctx, int num_fields, char **row)
47 {
48    uint32_t *val = (uint32_t *)ctx;
49
50    Dmsg1(800, "int_handler starts with row pointing at %x\n", row);
51
52    if (row[0]) {
53       Dmsg1(800, "int_handler finds '%s'\n", row[0]);
54       *val = str_to_int64(row[0]);
55    } else {
56       Dmsg0(800, "int_handler finds zero\n");
57       *val = 0;
58    }
59    Dmsg0(800, "int_handler finishes\n");
60    return 0;
61 }
62
63 /*
64  * Called here to retrieve a 32/64 bit integer from the database.
65  *   The returned integer will be extended to 64 bit.
66  */
67 int db_int64_handler(void *ctx, int num_fields, char **row)
68 {
69    db_int64_ctx *lctx = (db_int64_ctx *)ctx;
70
71    if (row[0]) {
72       lctx->value = str_to_int64(row[0]);
73       lctx->count++;
74    }
75    return 0;
76 }
77
78
79
80 /* NOTE!!! The following routines expect that the
81  *  calling subroutine sets and clears the mutex
82  */
83
84 /* Check that the tables correspond to the version we want */
85 bool check_tables_version(JCR *jcr, B_DB *mdb)
86 {
87    const char *query = "SELECT VersionId FROM Version";
88
89    bacula_db_version = 0;
90    if (!db_sql_query(mdb, query, int_handler, (void *)&bacula_db_version)) {
91       Mmsg(mdb->errmsg, "Database not created or server not running.\n");
92       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
93       return false;
94    }
95    if (bacula_db_version != BDB_VERSION) {
96       Mmsg(mdb->errmsg, "Version error for database \"%s\". Wanted %d, got %d\n",
97           mdb->db_name, BDB_VERSION, bacula_db_version);
98       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
99       return false;
100    }
101    return true;
102 }
103
104 /* Utility routine for queries. The database MUST be locked before calling here. */
105 int
106 QueryDB(const char *file, int line, JCR *jcr, B_DB *mdb, char *cmd)
107 {
108    int status;
109    if ((status=sql_query(mdb, cmd)) != 0) {
110       m_msg(file, line, &mdb->errmsg, _("query %s failed:\n%s\n"), cmd, sql_strerror(mdb));
111       j_msg(file, line, jcr, M_FATAL, 0, "%s", mdb->errmsg);
112       if (verbose) {
113          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
114       }
115       return 0;
116    }
117
118    mdb->result = sql_store_result(mdb);
119
120    return mdb->result != NULL;
121 }
122
123 /*
124  * Utility routine to do inserts
125  * Returns: 0 on failure
126  *          1 on success
127  */
128 int
129 InsertDB(const char *file, int line, JCR *jcr, B_DB *mdb, char *cmd)
130 {
131    if (sql_query(mdb, cmd)) {
132       m_msg(file, line, &mdb->errmsg,  _("insert %s failed:\n%s\n"), cmd, sql_strerror(mdb));
133       j_msg(file, line, jcr, M_FATAL, 0, "%s", mdb->errmsg);
134       if (verbose) {
135          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
136       }
137       return 0;
138    }
139    if (mdb->have_insert_id) {
140       mdb->num_rows = sql_affected_rows(mdb);
141    } else {
142       mdb->num_rows = 1;
143    }
144    if (mdb->num_rows != 1) {
145       char ed1[30];
146       m_msg(file, line, &mdb->errmsg, _("Insertion problem: affected_rows=%s\n"),
147          edit_uint64(mdb->num_rows, ed1));
148       if (verbose) {
149          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
150       }
151       return 0;
152    }
153    mdb->changes++;
154    return 1;
155 }
156
157 /* Utility routine for updates.
158  *  Returns: 0 on failure
159  *           1 on success
160  */
161 int
162 UpdateDB(const char *file, int line, JCR *jcr, B_DB *mdb, char *cmd)
163 {
164
165    if (sql_query(mdb, cmd)) {
166       m_msg(file, line, &mdb->errmsg, _("update %s failed:\n%s\n"), cmd, sql_strerror(mdb));
167       j_msg(file, line, jcr, M_ERROR, 0, "%s", mdb->errmsg);
168       if (verbose) {
169          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
170       }
171       return 0;
172    }
173    mdb->num_rows = sql_affected_rows(mdb);
174    if (mdb->num_rows < 1) {
175       char ed1[30];
176       m_msg(file, line, &mdb->errmsg, _("Update problem: affected_rows=%s\n"),
177          edit_uint64(mdb->num_rows, ed1));
178       if (verbose) {
179 //       j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
180       }
181       return 0;
182    }
183    mdb->changes++;
184    return 1;
185 }
186
187 /* Utility routine for deletes
188  *
189  * Returns: -1 on error
190  *           n number of rows affected
191  */
192 int
193 DeleteDB(const char *file, int line, JCR *jcr, B_DB *mdb, char *cmd)
194 {
195
196    if (sql_query(mdb, cmd)) {
197       m_msg(file, line, &mdb->errmsg, _("delete %s failed:\n%s\n"), cmd, sql_strerror(mdb));
198       j_msg(file, line, jcr, M_ERROR, 0, "%s", mdb->errmsg);
199       if (verbose) {
200          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
201       }
202       return -1;
203    }
204    mdb->changes++;
205    return sql_affected_rows(mdb);
206 }
207
208
209 /*
210  * Get record max. Query is already in mdb->cmd
211  *  No locking done
212  *
213  * Returns: -1 on failure
214  *          count on success
215  */
216 int get_sql_record_max(JCR *jcr, B_DB *mdb)
217 {
218    SQL_ROW row;
219    int stat = 0;
220
221    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
222       if ((row = sql_fetch_row(mdb)) == NULL) {
223          Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
224          stat = -1;
225       } else {
226          stat = str_to_int64(row[0]);
227       }
228       sql_free_result(mdb);
229    } else {
230       Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
231       stat = -1;
232    }
233    return stat;
234 }
235
236 /*
237  * Return pre-edited error message
238  */
239 char *db_strerror(B_DB *mdb)
240 {
241    return mdb->errmsg;
242 }
243
244 /*
245  * Lock database, this can be called multiple times by the same
246  *   thread without blocking, but must be unlocked the number of
247  *   times it was locked.
248  */
249 void _db_lock(const char *file, int line, B_DB *mdb)
250 {
251    int errstat;
252    if ((errstat=rwl_writelock(&mdb->lock)) != 0) {
253       berrno be;
254       e_msg(file, line, M_FATAL, 0, "rwl_writelock failure. stat=%d: ERR=%s\n",
255            errstat, be.strerror(errstat));
256    }
257 }
258
259 /*
260  * Unlock the database. This can be called multiple times by the
261  *   same thread up to the number of times that thread called
262  *   db_lock()/
263  */
264 void _db_unlock(const char *file, int line, B_DB *mdb)
265 {
266    int errstat;
267    if ((errstat=rwl_writeunlock(&mdb->lock)) != 0) {
268       berrno be;
269       e_msg(file, line, M_FATAL, 0, "rwl_writeunlock failure. stat=%d: ERR=%s\n",
270            errstat, be.strerror(errstat));
271    }
272 }
273
274 /*
275  * Start a transaction. This groups inserts and makes things
276  *  much more efficient. Usually started when inserting
277  *  file attributes.
278  */
279 void db_start_transaction(JCR *jcr, B_DB *mdb)
280 {
281    if (!jcr->attr) {
282       jcr->attr = get_pool_memory(PM_FNAME);
283    }
284    if (!jcr->ar) {
285       jcr->ar = (ATTR_DBR *)malloc(sizeof(ATTR_DBR));
286    }
287
288 #ifdef HAVE_SQLITE
289    if (!mdb->allow_transactions) {
290       return;
291    }
292    db_lock(mdb);
293    /* Allow only 10,000 changes per transaction */
294    if (mdb->transaction && mdb->changes > 10000) {
295       db_end_transaction(jcr, mdb);
296    }
297    if (!mdb->transaction) {
298       my_sqlite_query(mdb, "BEGIN");  /* begin transaction */
299       Dmsg0(400, "Start SQLite transaction\n");
300       mdb->transaction = 1;
301    }
302    db_unlock(mdb);
303 #endif
304
305 /*
306  * This is turned off because transactions break
307  * if multiple simultaneous jobs are run.
308  */
309 #ifdef HAVE_POSTGRESQL
310    if (!mdb->allow_transactions) {
311       return;
312    }
313    db_lock(mdb);
314    /* Allow only 25,000 changes per transaction */
315    if (mdb->transaction && mdb->changes > 25000) {
316       db_end_transaction(jcr, mdb);
317    }
318    if (!mdb->transaction) {
319       db_sql_query(mdb, "BEGIN", NULL, NULL);  /* begin transaction */
320       Dmsg0(400, "Start PosgreSQL transaction\n");
321       mdb->transaction = 1;
322    }
323    db_unlock(mdb);
324 #endif
325 }
326
327 void db_end_transaction(JCR *jcr, B_DB *mdb)
328 {
329    /*
330     * This can be called during thread cleanup and
331     *   the db may already be closed.  So simply return.
332     */
333    if (!mdb) {
334       return;
335    }
336
337    if (jcr && jcr->cached_attribute) {
338       Dmsg0(400, "Flush last cached attribute.\n");
339       if (!db_create_file_attributes_record(jcr, mdb, jcr->ar)) {
340          Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
341       }
342       jcr->cached_attribute = false;
343    }
344
345 #ifdef HAVE_SQLITE
346    if (!mdb->allow_transactions) {
347       return;
348    }
349    db_lock(mdb);
350    if (mdb->transaction) {
351       my_sqlite_query(mdb, "COMMIT"); /* end transaction */
352       mdb->transaction = 0;
353       Dmsg1(400, "End SQLite transaction changes=%d\n", mdb->changes);
354    }
355    mdb->changes = 0;
356    db_unlock(mdb);
357 #endif
358
359 #ifdef HAVE_POSTGRESQL
360    if (!mdb->allow_transactions) {
361       return;
362    }
363    db_lock(mdb);
364    if (mdb->transaction) {
365       db_sql_query(mdb, "COMMIT", NULL, NULL); /* end transaction */
366       mdb->transaction = 0;
367       Dmsg1(400, "End PostgreSQL transaction changes=%d\n", mdb->changes);
368    }
369    mdb->changes = 0;
370    db_unlock(mdb);
371 #endif
372 }
373
374 /*
375  * Given a full filename, split it into its path
376  *  and filename parts. They are returned in pool memory
377  *  in the mdb structure.
378  */
379 void split_path_and_file(JCR *jcr, B_DB *mdb, const char *fname)
380 {
381    const char *p, *f;
382
383    /* Find path without the filename.
384     * I.e. everything after the last / is a "filename".
385     * OK, maybe it is a directory name, but we treat it like
386     * a filename. If we don't find a / then the whole name
387     * must be a path name (e.g. c:).
388     */
389    for (p=f=fname; *p; p++) {
390       if (*p == '/') {
391          f = p;                       /* set pos of last slash */
392       }
393    }
394    if (*f == '/') {                   /* did we find a slash? */
395       f++;                            /* yes, point to filename */
396    } else {                           /* no, whole thing must be path name */
397       f = p;
398    }
399
400    /* If filename doesn't exist (i.e. root directory), we
401     * simply create a blank name consisting of a single
402     * space. This makes handling zero length filenames
403     * easier.
404     */
405    mdb->fnl = p - f;
406    if (mdb->fnl > 0) {
407       mdb->fname = check_pool_memory_size(mdb->fname, mdb->fnl+1);
408       memcpy(mdb->fname, f, mdb->fnl);    /* copy filename */
409       mdb->fname[mdb->fnl] = 0;
410    } else {
411       mdb->fname[0] = 0;
412       mdb->fnl = 0;
413    }
414
415    mdb->pnl = f - fname;
416    if (mdb->pnl > 0) {
417       mdb->path = check_pool_memory_size(mdb->path, mdb->pnl+1);
418       memcpy(mdb->path, fname, mdb->pnl);
419       mdb->path[mdb->pnl] = 0;
420    } else {
421       Mmsg1(&mdb->errmsg, _("Path length is zero. File=%s\n"), fname);
422       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
423       mdb->path[0] = 0;
424       mdb->pnl = 0;
425    }
426
427    Dmsg2(500, "split path=%s file=%s\n", mdb->path, mdb->fname);
428 }
429
430 /*
431  * List dashes as part of header for listing SQL results in a table
432  */
433 void
434 list_dashes(B_DB *mdb, DB_LIST_HANDLER *send, void *ctx)
435 {
436    SQL_FIELD  *field;
437    int i, j;
438
439    sql_field_seek(mdb, 0);
440    send(ctx, "+");
441    for (i = 0; i < sql_num_fields(mdb); i++) {
442       field = sql_fetch_field(mdb);
443       for (j = 0; j < (int)field->max_length + 2; j++) {
444          send(ctx, "-");
445       }
446       send(ctx, "+");
447    }
448    send(ctx, "\n");
449 }
450
451 /*
452  * If full_list is set, we list vertically, otherwise, we
453  * list on one line horizontally.
454  */
455 void
456 list_result(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type type)
457 {
458    SQL_FIELD *field;
459    SQL_ROW row;
460    int i, col_len, max_len = 0;
461    char buf[2000], ewc[30];
462
463    Dmsg0(800, "list_result starts\n");
464    if (mdb->result == NULL || sql_num_rows(mdb) == 0) {
465       send(ctx, _("No results to list.\n"));
466       return;
467    }
468
469    Dmsg1(800, "list_result starts looking at %d fields\n", sql_num_fields(mdb));
470    /* determine column display widths */
471    sql_field_seek(mdb, 0);
472    for (i = 0; i < sql_num_fields(mdb); i++) {
473       Dmsg1(800, "list_result processing field %d\n", i);
474       field = sql_fetch_field(mdb);
475       col_len = cstrlen(field->name);
476       if (type == VERT_LIST) {
477          if (col_len > max_len) {
478             max_len = col_len;
479          }
480       } else {
481          if (IS_NUM(field->type) && (int)field->max_length > 0) { /* fixup for commas */
482             field->max_length += (field->max_length - 1) / 3;
483          }
484          if (col_len < (int)field->max_length) {
485             col_len = field->max_length;
486          }
487          if (col_len < 4 && !IS_NOT_NULL(field->flags)) {
488             col_len = 4;                 /* 4 = length of the word "NULL" */
489          }
490          field->max_length = col_len;    /* reset column info */
491       }
492    }
493
494    Dmsg0(800, "list_result finished first loop\n");
495    if (type == VERT_LIST) {
496       goto vertical_list;
497    }
498
499    Dmsg1(800, "list_result starts second loop looking at %d fields\n", sql_num_fields(mdb));
500    list_dashes(mdb, send, ctx);
501    send(ctx, "|");
502    sql_field_seek(mdb, 0);
503    for (i = 0; i < sql_num_fields(mdb); i++) {
504       Dmsg1(800, "list_result looking at field %d\n", i);
505       field = sql_fetch_field(mdb);
506       bsnprintf(buf, sizeof(buf), " %-*s |", (int)field->max_length, field->name);
507       send(ctx, buf);
508    }
509    send(ctx, "\n");
510    list_dashes(mdb, send, ctx);
511
512    Dmsg1(800, "list_result starts third loop looking at %d fields\n", sql_num_fields(mdb));
513    while ((row = sql_fetch_row(mdb)) != NULL) {
514       sql_field_seek(mdb, 0);
515       send(ctx, "|");
516       for (i = 0; i < sql_num_fields(mdb); i++) {
517          field = sql_fetch_field(mdb);
518          if (row[i] == NULL) {
519             bsnprintf(buf, sizeof(buf), " %-*s |", (int)field->max_length, "NULL");
520          } else if (IS_NUM(field->type) && !jcr->gui && is_an_integer(row[i])) {
521             bsnprintf(buf, sizeof(buf), " %*s |", (int)field->max_length,
522                       add_commas(row[i], ewc));
523          } else {
524             bsnprintf(buf, sizeof(buf), " %-*s |", (int)field->max_length, row[i]);
525          }
526          send(ctx, buf);
527       }
528       send(ctx, "\n");
529    }
530    list_dashes(mdb, send, ctx);
531    return;
532
533 vertical_list:
534
535    Dmsg1(800, "list_result starts vertical list at %d fields\n", sql_num_fields(mdb));
536    while ((row = sql_fetch_row(mdb)) != NULL) {
537       sql_field_seek(mdb, 0);
538       for (i = 0; i < sql_num_fields(mdb); i++) {
539          field = sql_fetch_field(mdb);
540          if (row[i] == NULL) {
541             bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL");
542          } else if (IS_NUM(field->type) && !jcr->gui && is_an_integer(row[i])) {
543             bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name,
544                 add_commas(row[i], ewc));
545          } else {
546             bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]);
547          }
548          send(ctx, buf);
549       }
550       send(ctx, "\n");
551    }
552    return;
553 }
554
555
556 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/