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