X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fcats%2Fsql.c;h=ad7ce60caad1692216742c1b46d31ad7a1d9e280;hb=798adaee607d9b913115853e5fb84f2e2a424975;hp=131887723d5ed7a95f5f5f42b6f766703130d1b5;hpb=72028bbcb832a059405074c480370224b95ae5c1;p=bacula%2Fbacula diff --git a/bacula/src/cats/sql.c b/bacula/src/cats/sql.c index 131887723d..ad7ce60caa 100644 --- a/bacula/src/cats/sql.c +++ b/bacula/src/cats/sql.c @@ -10,7 +10,7 @@ */ /* - Copyright (C) 2000-2003 Kern Sibbald and John Walker + Copyright (C) 2000-2004 Kern Sibbald and John Walker This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -37,7 +37,9 @@ #include "bacula.h" #include "cats.h" -#if HAVE_MYSQL | HAVE_SQLITE +#if HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL + +uint32_t bacula_db_version = 0; /* Forward referenced subroutines */ void print_dashes(B_DB *mdb); @@ -50,11 +52,16 @@ static int int_handler(void *ctx, int num_fields, char **row) { uint32_t *val = (uint32_t *)ctx; + Dmsg1(800, "int_handler starts with row pointing at %x\n", row); + if (row[0]) { + Dmsg1(800, "int_handler finds '%s'\n", row[0]); *val = atoi(row[0]); } else { + Dmsg0(800, "int_handler finds zero\n"); *val = 0; } + Dmsg0(800, "int_handler finishes\n"); return 0; } @@ -64,34 +71,38 @@ static int int_handler(void *ctx, int num_fields, char **row) * calling subroutine sets and clears the mutex */ -/* Check that the tables conrrespond to the version we want */ -int check_tables_version(B_DB *mdb) +/* Check that the tables correspond to the version we want */ +int check_tables_version(JCR *jcr, B_DB *mdb) { - uint32_t version; - char *query = "SELECT VersionId FROM Version"; + const char *query = "SELECT VersionId FROM Version"; - version = 0; - db_sql_query(mdb, query, int_handler, (void *)&version); - if (version != BDB_VERSION) { + bacula_db_version = 0; + db_sql_query(mdb, query, int_handler, (void *)&bacula_db_version); + if (bacula_db_version != BDB_VERSION) { Mmsg(&mdb->errmsg, "Version error for database \"%s\". Wanted %d, got %d\n", - mdb->db_name, BDB_VERSION, version); - Jmsg(mdb->jcr, M_FATAL, 0, mdb->errmsg); + mdb->db_name, BDB_VERSION, bacula_db_version); + Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg); return 0; } return 1; } -/* Utility routine for queries */ +/* Utility routine for queries. The database MUST be locked before calling here. */ int -QueryDB(char *file, int line, B_DB *mdb, char *cmd) +QueryDB(const char *file, int line, JCR *jcr, B_DB *mdb, char *cmd) { - if (sql_query(mdb, cmd)) { + int status; + if ((status=sql_query(mdb, cmd)) != 0) { m_msg(file, line, &mdb->errmsg, _("query %s failed:\n%s\n"), cmd, sql_strerror(mdb)); - j_msg(file, line, mdb->jcr, M_FATAL, 0, mdb->errmsg); + j_msg(file, line, jcr, M_FATAL, 0, "%s", mdb->errmsg); + if (verbose) { + j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd); + } return 0; } + mdb->result = sql_store_result(mdb); - + return mdb->result != NULL; } @@ -101,11 +112,14 @@ QueryDB(char *file, int line, B_DB *mdb, char *cmd) * 1 on success */ int -InsertDB(char *file, int line, B_DB *mdb, char *cmd) +InsertDB(const char *file, int line, JCR *jcr, B_DB *mdb, char *cmd) { if (sql_query(mdb, cmd)) { m_msg(file, line, &mdb->errmsg, _("insert %s failed:\n%s\n"), cmd, sql_strerror(mdb)); - j_msg(file, line, mdb->jcr, M_FATAL, 0, mdb->errmsg); + j_msg(file, line, jcr, M_FATAL, 0, "%s", mdb->errmsg); + if (verbose) { + j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd); + } return 0; } if (mdb->have_insert_id) { @@ -115,8 +129,11 @@ InsertDB(char *file, int line, B_DB *mdb, char *cmd) } if (mdb->num_rows != 1) { char ed1[30]; - m_msg(file, line, &mdb->errmsg, _("Insertion problem: affect_rows=%s\n"), + m_msg(file, line, &mdb->errmsg, _("Insertion problem: affected_rows=%s\n"), edit_uint64(mdb->num_rows, ed1)); + if (verbose) { + j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd); + } return 0; } mdb->changes++; @@ -128,20 +145,25 @@ InsertDB(char *file, int line, B_DB *mdb, char *cmd) * 1 on success */ int -UpdateDB(char *file, int line, B_DB *mdb, char *cmd) +UpdateDB(const char *file, int line, JCR *jcr, B_DB *mdb, char *cmd) { if (sql_query(mdb, cmd)) { m_msg(file, line, &mdb->errmsg, _("update %s failed:\n%s\n"), cmd, sql_strerror(mdb)); - j_msg(file, line, mdb->jcr, M_ERROR, 0, mdb->errmsg); - j_msg(file, line, mdb->jcr, M_ERROR, 0, "%s\n", cmd); + j_msg(file, line, jcr, M_ERROR, 0, "%s", mdb->errmsg); + if (verbose) { + j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd); + } return 0; } mdb->num_rows = sql_affected_rows(mdb); - if (mdb->num_rows != 1) { + if (mdb->num_rows < 1) { char ed1[30]; - m_msg(file, line, &mdb->errmsg, _("Update problem: affect_rows=%s\n"), + m_msg(file, line, &mdb->errmsg, _("Update problem: affected_rows=%s\n"), edit_uint64(mdb->num_rows, ed1)); + if (verbose) { +// j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd); + } return 0; } mdb->changes++; @@ -154,12 +176,15 @@ UpdateDB(char *file, int line, B_DB *mdb, char *cmd) * n number of rows affected */ int -DeleteDB(char *file, int line, B_DB *mdb, char *cmd) +DeleteDB(const char *file, int line, JCR *jcr, B_DB *mdb, char *cmd) { if (sql_query(mdb, cmd)) { m_msg(file, line, &mdb->errmsg, _("delete %s failed:\n%s\n"), cmd, sql_strerror(mdb)); - j_msg(file, line, mdb->jcr, M_ERROR, 0, mdb->errmsg); + j_msg(file, line, jcr, M_ERROR, 0, "%s", mdb->errmsg); + if (verbose) { + j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd); + } return -1; } mdb->changes++; @@ -174,12 +199,12 @@ DeleteDB(char *file, int line, B_DB *mdb, char *cmd) * Returns: -1 on failure * count on success */ -int get_sql_record_max(B_DB *mdb) +int get_sql_record_max(JCR *jcr, B_DB *mdb) { SQL_ROW row; int stat = 0; - if (QUERY_DB(mdb, mdb->cmd)) { + if (QUERY_DB(jcr, mdb, mdb->cmd)) { if ((row = sql_fetch_row(mdb)) == NULL) { Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb)); stat = -1; @@ -194,25 +219,38 @@ int get_sql_record_max(B_DB *mdb) return stat; } +/* + * Return pre-edited error message + */ char *db_strerror(B_DB *mdb) { return mdb->errmsg; } -void _db_lock(char *file, int line, B_DB *mdb) +/* + * Lock database, this can be called multiple times by the same + * thread without blocking, but must be unlocked the number of + * times it was locked. + */ +void _db_lock(const char *file, int line, B_DB *mdb) { int errstat; if ((errstat=rwl_writelock(&mdb->lock)) != 0) { - j_msg(file, line, mdb->jcr, M_ABORT, 0, "rwl_writelock failure. ERR=%s\n", + e_msg(file, line, M_ABORT, 0, "rwl_writelock failure. ERR=%s\n", strerror(errstat)); } } -void _db_unlock(char *file, int line, B_DB *mdb) +/* + * Unlock the database. This can be called multiple times by the + * same thread up to the number of times that thread called + * db_lock()/ + */ +void _db_unlock(const char *file, int line, B_DB *mdb) { int errstat; if ((errstat=rwl_writeunlock(&mdb->lock)) != 0) { - j_msg(file, line, mdb->jcr, M_ABORT, 0, "rwl_writeunlock failure. ERR=%s\n", + e_msg(file, line, M_ABORT, 0, "rwl_writeunlock failure. ERR=%s\n", strerror(errstat)); } } @@ -222,13 +260,13 @@ void _db_unlock(char *file, int line, B_DB *mdb) * much more efficient. Usually started when inserting * file attributes. */ -void db_start_transaction(B_DB *mdb) +void db_start_transaction(JCR *jcr, B_DB *mdb) { #ifdef xAVE_SQLITE db_lock(mdb); /* Allow only 10,000 changes per transaction */ if (mdb->transaction && mdb->changes > 10000) { - db_end_transaction(mdb); + db_end_transaction(jcr, mdb); } if (!mdb->transaction) { my_sqlite_query(mdb, "BEGIN"); /* begin transaction */ @@ -238,9 +276,26 @@ void db_start_transaction(B_DB *mdb) db_unlock(mdb); #endif +/* + * This is turned off because transactions break + * if multiple simultaneous jobs are run. + */ +#ifdef xAVE_POSTGRESQL + db_lock(mdb); + /* Allow only 25,000 changes per transaction */ + if (mdb->transaction && mdb->changes > 25000) { + db_end_transaction(jcr, mdb); + } + if (!mdb->transaction) { + db_sql_query(mdb, "BEGIN", NULL, NULL); /* begin transaction */ + Dmsg0(400, "Start PosgreSQL transaction\n"); + mdb->transaction = 1; + } + db_unlock(mdb); +#endif } -void db_end_transaction(B_DB *mdb) +void db_end_transaction(JCR *jcr, B_DB *mdb) { #ifdef xAVE_SQLITE db_lock(mdb); @@ -252,11 +307,27 @@ void db_end_transaction(B_DB *mdb) mdb->changes = 0; db_unlock(mdb); #endif + +#ifdef xAVE_POSTGRESQL + db_lock(mdb); + if (mdb->transaction) { + db_sql_query(mdb, "COMMIT", NULL, NULL); /* end transaction */ + mdb->transaction = 0; + Dmsg1(400, "End PostgreSQL transaction changes=%d\n", mdb->changes); + } + mdb->changes = 0; + db_unlock(mdb); +#endif } -void split_path_and_filename(B_DB *mdb, char *fname) +/* + * Given a full filename, split it into its path + * and filename parts. They are returned in pool memory + * in the mdb structure. + */ +void split_path_and_file(JCR *jcr, B_DB *mdb, const char *fname) { - char *p, *f; + const char *p, *f; /* Find path without the filename. * I.e. everything after the last / is a "filename". @@ -283,7 +354,7 @@ void split_path_and_filename(B_DB *mdb, char *fname) mdb->fnl = p - f; if (mdb->fnl > 0) { mdb->fname = check_pool_memory_size(mdb->fname, mdb->fnl+1); - strncpy(mdb->fname, f, mdb->fnl); /* copy filename */ + memcpy(mdb->fname, f, mdb->fnl); /* copy filename */ mdb->fname[mdb->fnl] = 0; } else { mdb->fname[0] = ' '; /* blank filename */ @@ -294,17 +365,143 @@ void split_path_and_filename(B_DB *mdb, char *fname) mdb->pnl = f - fname; if (mdb->pnl > 0) { mdb->path = check_pool_memory_size(mdb->path, mdb->pnl+1); - strncpy(mdb->path, fname, mdb->pnl); + memcpy(mdb->path, fname, mdb->pnl); mdb->path[mdb->pnl] = 0; } else { Mmsg1(&mdb->errmsg, _("Path length is zero. File=%s\n"), fname); - Jmsg(mdb->jcr, M_ERROR, 0, "%s", mdb->errmsg); + Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg); mdb->path[0] = ' '; mdb->path[1] = 0; mdb->pnl = 1; } - Dmsg2(100, "sllit path=%s file=%s\n", mdb->path, mdb->fname); + Dmsg2(500, "split path=%s file=%s\n", mdb->path, mdb->fname); +} + +/* + * List dashes as part of header for listing SQL results in a table + */ +void +list_dashes(B_DB *mdb, DB_LIST_HANDLER *send, void *ctx) +{ + SQL_FIELD *field; + int i, j; + + sql_field_seek(mdb, 0); + send(ctx, "+"); + for (i = 0; i < sql_num_fields(mdb); i++) { + field = sql_fetch_field(mdb); + for (j = 0; j < (int)field->max_length + 2; j++) { + send(ctx, "-"); + } + send(ctx, "+"); + } + send(ctx, "\n"); +} + +/* + * If full_list is set, we list vertically, otherwise, we + * list on one line horizontally. + */ +void +list_result(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type type) +{ + SQL_FIELD *field; + SQL_ROW row; + int i, col_len, max_len = 0; + char buf[2000], ewc[30]; + + Dmsg0(800, "list_result starts\n"); + if (mdb->result == NULL || sql_num_rows(mdb) == 0) { + send(ctx, _("No results to list.\n")); + return; + } + + Dmsg1(800, "list_result starts looking at %d fields\n", sql_num_fields(mdb)); + /* determine column display widths */ + sql_field_seek(mdb, 0); + for (i = 0; i < sql_num_fields(mdb); i++) { + Dmsg1(800, "list_result processing field %d\n", i); + field = sql_fetch_field(mdb); + col_len = strlen(field->name); + if (type == VERT_LIST) { + if (col_len > max_len) { + max_len = col_len; + } + } else { + if (IS_NUM(field->type) && (int)field->max_length > 0) { /* fixup for commas */ + field->max_length += (field->max_length - 1) / 3; + } + if (col_len < (int)field->max_length) { + col_len = field->max_length; + } + if (col_len < 4 && !IS_NOT_NULL(field->flags)) { + col_len = 4; /* 4 = length of the word "NULL" */ + } + field->max_length = col_len; /* reset column info */ + } + } + + Dmsg0(800, "list_result finished first loop\n"); + if (type == VERT_LIST) { + goto vertical_list; + } + + Dmsg1(800, "list_result starts second loop looking at %d fields\n", sql_num_fields(mdb)); + list_dashes(mdb, send, ctx); + send(ctx, "|"); + sql_field_seek(mdb, 0); + for (i = 0; i < sql_num_fields(mdb); i++) { + Dmsg1(800, "list_result looking at field %d\n", i); + field = sql_fetch_field(mdb); + bsnprintf(buf, sizeof(buf), " %-*s |", (int)field->max_length, field->name); + send(ctx, buf); + } + send(ctx, "\n"); + list_dashes(mdb, send, ctx); + + Dmsg1(800, "list_result starts third loop looking at %d fields\n", sql_num_fields(mdb)); + while ((row = sql_fetch_row(mdb)) != NULL) { + sql_field_seek(mdb, 0); + send(ctx, "|"); + for (i = 0; i < sql_num_fields(mdb); i++) { + field = sql_fetch_field(mdb); + if (row[i] == NULL) { + bsnprintf(buf, sizeof(buf), " %-*s |", (int)field->max_length, "NULL"); + } else if (IS_NUM(field->type) && !jcr->gui && is_an_integer(row[i])) { + bsnprintf(buf, sizeof(buf), " %*s |", (int)field->max_length, + add_commas(row[i], ewc)); + } else { + bsnprintf(buf, sizeof(buf), " %-*s |", (int)field->max_length, row[i]); + } + send(ctx, buf); + } + send(ctx, "\n"); + } + list_dashes(mdb, send, ctx); + return; + +vertical_list: + + Dmsg1(800, "list_result starts vertical list at %d fields\n", sql_num_fields(mdb)); + while ((row = sql_fetch_row(mdb)) != NULL) { + sql_field_seek(mdb, 0); + for (i = 0; i < sql_num_fields(mdb); i++) { + field = sql_fetch_field(mdb); + if (row[i] == NULL) { + bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL"); + } else if (IS_NUM(field->type) && !jcr->gui && is_an_integer(row[i])) { + bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, + add_commas(row[i], ewc)); + } else { + bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]); + } + send(ctx, buf); + } + send(ctx, "\n"); + } + return; } -#endif /* HAVE_MYSQL | HAVE_SQLITE */ + +#endif /* HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL */