]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/tools/dbcheck.c
Tweak fix MySQL quoting again :-(
[bacula/bacula] / bacula / src / tools / dbcheck.c
index 48769c8133b83d6c66658ff9303f2e0d2f10c001..6ed8a7c3e0b44ca82e2fb65356997a5aab63c308 100644 (file)
@@ -1,12 +1,12 @@
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2002-2008 Free Software Foundation Europe e.V.
+   Copyright (C) 2002-2009 Free Software Foundation Europe e.V.
 
    The main author of Bacula is Kern Sibbald, with contributions from
    many others, a complete list can be found in the file AUTHORS.
    This program is Free Software; you can redistribute it and/or
-   modify it under the terms of version two of the GNU General Public
+   modify it under the terms of version three of the GNU Affero General Public
    License as published by the Free Software Foundation and included
    in the file LICENSE.
 
@@ -15,7 +15,7 @@
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    General Public License for more details.
 
-   You should have received a copy of the GNU General Public License
+   You should have received a copy of the GNU Affero General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
@@ -63,8 +63,6 @@ typedef struct s_name_ctx {
    int tot_ids;                       /* total to process */
 } NAME_LIST;
 
-
-
 /* Global variables */
 static bool fix = false;
 static bool batch = false;
@@ -99,18 +97,30 @@ static void repair_bad_paths();
 static void repair_bad_filenames();
 static void do_interactive_mode();
 static bool yes_no(const char *prompt);
+static bool check_idx(const char *col_name);
+static bool create_tmp_idx(const char *idx_name, const char *table_name, 
+              const char *col_name);
+static bool drop_tmp_idx(const char *idx_name, const char *table_name);
+#ifdef HAVE_MYSQL
+static int check_idx_handler(void *ctx, int num_fields, char **row);
+#endif
 
 
+/* Global variables */
+static const char *idx_tmp_name;
+
 static void usage()
 {
    fprintf(stderr,
-"Usage: dbcheck [-c config] [-C catalog name] [-d debug_level] <working-directory> <bacula-database> <user> <password> [<dbhost>] [<dbport>]\n"
+"Usage: dbcheck [-c config ] [-B] [-C catalog name] [-d debug_level] <working-directory> <bacula-database> <user> <password> [<dbhost>] [<dbport>]\n"
 "       -b              batch mode\n"
 "       -C              catalog name in the director conf file\n"
 "       -c              Director conf filename\n"
+"       -B              print catalog configuration and exit\n"
 "       -d <nn>         set debug level to <nn>\n"
-"       -dt             print timestamp in debug output\n"
+"       -dt             print timestamp in debug output\n"
 "       -f              fix inconsistencies\n"
+"       -t              test if client library is thread-safe\n"
 "       -v              verbose\n"
 "       -?              print this message\n\n");
    exit(1);
@@ -121,6 +131,8 @@ int main (int argc, char *argv[])
    int ch;
    const char *user, *password, *db_name, *dbhost;
    int dbport = 0;
+   bool test_thread=false;
+   bool print_catalog=false;
    char *configfile = NULL;
    char *catalogname = NULL;
    char *endptr;
@@ -128,6 +140,7 @@ int main (int argc, char *argv[])
    setlocale(LC_ALL, "");
    bindtextdomain("bacula", LOCALEDIR);
    textdomain("bacula");
+   lmgr_init_thread();
 
    my_name_is(argc, argv, "dbcheck");
    init_msg(NULL, NULL);              /* setup message handler */
@@ -135,9 +148,12 @@ int main (int argc, char *argv[])
    memset(&id_list, 0, sizeof(id_list));
    memset(&name_list, 0, sizeof(name_list));
 
-
-   while ((ch = getopt(argc, argv, "bc:C:d:fv?")) != -1) {
+   while ((ch = getopt(argc, argv, "bc:C:d:fvBt?")) != -1) {
       switch (ch) {
+      case 'B':
+         print_catalog = true;     /* get catalog information from config */
+         break;
+
       case 'b':                    /* batch */
          batch = true;
          break;
@@ -168,6 +184,9 @@ int main (int argc, char *argv[])
       case 'v':
          verbose++;
          break;
+      case 't':
+         test_thread=true;
+         break;
 
       case '?':
       default:
@@ -179,6 +198,16 @@ int main (int argc, char *argv[])
 
    OSDependentInit();
 
+   if (test_thread) {
+      /* When we will load the SQL backend with ldopen, this check would be
+       * moved after the database initialization. It will need a valid config
+       * file.
+       */
+      db_check_backend_thread_safe();
+      Pmsg0(0, _("OK - DB backend seems to be thread-safe.\n"));
+      exit(0);
+   }
+
    if (configfile) {
       CAT *catalog = NULL;
       int found = 0;
@@ -215,6 +244,16 @@ int main (int argc, char *argv[])
             exit(1);
          }
          set_working_directory(director->working_directory);
+
+         /* Print catalog information and exit (-B) */
+         if (print_catalog) {
+            POOLMEM *buf = get_pool_memory(PM_MESSAGE);
+            printf("%sdb_type=%s\nworking_dir=%s\n", catalog->display(buf),
+                   db_get_type(), working_directory);
+            free_pool_memory(buf);
+            exit(0);
+         }
+
          db_name = catalog->db_name;
          user = catalog->db_user;
          password = catalog->db_password;
@@ -262,11 +301,12 @@ int main (int argc, char *argv[])
          user = argv[2];
          password = argv[3];
          dbhost = argv[4];
+         errno = 0;
          dbport = strtol(argv[5], &endptr, 10);
          if (*endptr != '\0') {
             Pmsg0(0, _("Database port must be a numeric value.\n"));
             exit(1);
-         } else if (dbport == LONG_MIN || dbport == LONG_MAX) {
+         } else if (errno == ERANGE) {
             Pmsg0(0, _("Database port must be a int value.\n"));
             exit(1);
          }
@@ -301,6 +341,7 @@ int main (int argc, char *argv[])
    db_close_database(NULL, db);
    close_msg(NULL);
    term_msg();
+   lmgr_cleanup_main();
    return 0;
 }
 
@@ -318,7 +359,7 @@ static void do_interactive_mode()
    else
       printf(_(" Verbose is off.\n"));
 
-   printf(_("Please select the fuction you want to perform.\n"));
+   printf(_("Please select the function you want to perform.\n"));
 
    while (!quit) {
       if (fix) {
@@ -812,6 +853,15 @@ static void eliminate_orphaned_file_records()
 
 static void eliminate_orphaned_path_records()
 {
+   idx_tmp_name = NULL;
+   /* check the existence of the required "one column" index */
+   if (!check_idx("PathId"))  {
+      if (yes_no(_("Create temporary index? (yes/no): "))) {
+         /* create temporary index PathId  */
+         create_tmp_idx("idxPIchk", "File", "PathId");
+      }
+   }
+
    const char *query = "SELECT DISTINCT Path.PathId,File.PathId FROM Path "
                "LEFT OUTER JOIN File ON (Path.PathId=File.PathId) "
                "WHERE File.PathId IS NULL LIMIT 300000";
@@ -846,11 +896,22 @@ static void eliminate_orphaned_path_records()
       if (!make_id_list(query, &id_list)) {
          exit(1);
       }
-   }
+   } 
+   /* drop temporary index idx_tmp_name */
+   drop_tmp_idx("idxPIchk", "File");
 }
 
 static void eliminate_orphaned_filename_records()
 {
+   idx_tmp_name = NULL;
+   /* check the existence of the required "one column" index */
+   if (!check_idx("FilenameId") )      {
+      if (yes_no(_("Create temporary index? (yes/no): "))) {
+         /* create temporary index FilenameId  */
+         create_tmp_idx("idxFIchk", "File", "FilenameId");
+      }
+   }
+
    const char *query = "SELECT Filename.FilenameId,File.FilenameId FROM Filename "
                 "LEFT OUTER JOIN File ON (Filename.FilenameId=File.FilenameId) "
                 "WHERE File.FilenameId IS NULL LIMIT 300000";
@@ -886,6 +947,9 @@ static void eliminate_orphaned_filename_records()
          exit(1);
       }
    }
+   /* drop temporary index idx_tmp_name */
+   drop_tmp_idx("idxFIchk", "File");
+
 }
 
 static void eliminate_orphaned_fileset_records()
@@ -1229,3 +1293,143 @@ static bool yes_no(const char *prompt)
 }
 
 bool python_set_prog(JCR*, char const*) { return false; }
+
+
+/*
+ * The code below to add indexes is needed only for MySQL, and
+ *  that to improve the performance.
+ */
+
+#ifdef HAVE_MYSQL
+#define MAXIDX          100
+typedef struct s_idx_list {
+   char *key_name;
+   int  count_key; /* how many times the index meets *key_name */
+   int  count_col; /* how many times meets the desired column name */
+} IDX_LIST;
+
+static IDX_LIST idx_list[MAXIDX];
+
+/* 
+ * Called here with each table index to be added to the list
+ */
+static int check_idx_handler(void *ctx, int num_fields, char **row)
+{
+   /* Table | Non_unique | Key_name | Seq_in_index | Column_name |...
+    * File  |          0 | PRIMARY  |            1 | FileId      |... 
+    */
+   char *name, *key_name, *col_name;
+   int i, len;
+   int found = false;
+   name = (char *)ctx;
+   key_name = row[2];
+   col_name = row[4];
+   for(i = 0; (idx_list[i].key_name != NULL) && (i < MAXIDX); i++) {
+      if (strcasecmp(idx_list[i].key_name, key_name) == 0 ) {
+         idx_list[i].count_key++;
+         found = true;
+         if (strcasecmp(col_name, name) == 0) {
+            idx_list[i].count_col++;
+         }
+         break;
+      }
+   }
+   /* if the new Key_name, add it to the list */
+   if (!found) {
+      len = strlen(key_name) + 1;
+      idx_list[i].key_name = (char *)malloc(len);
+      bstrncpy(idx_list[i].key_name, key_name, len);
+      idx_list[i].count_key = 1;
+      if (strcasecmp(col_name, name) == 0) {
+         idx_list[i].count_col = 1;
+      } else {
+         idx_list[i].count_col = 0;
+      }
+   }
+   return 0;
+}
+#endif
+
+/*
+ * Return TRUE if "one column" index over *col_name exists
+ */
+static bool check_idx(const char *col_name)
+{
+#ifdef HAVE_MYSQL
+   int i;
+   int found = false;
+
+   memset(&idx_list, 0, sizeof(idx_list));
+   const char *query = "SHOW INDEX FROM File";
+   if (!db_sql_query(db, query, check_idx_handler, (void *)col_name)) {
+      printf("%s\n", db_strerror(db));
+   }
+
+   for(i = 0; (idx_list[i].key_name != NULL) && (i < MAXIDX) ; i++) {
+      /* NOTE : if (idx_list[i].count_key > 1) then index idx_list[i].key_name is "multiple-column" index */
+      if ((idx_list[i].count_key == 1) && (idx_list[i].count_col == 1)) {
+         /* "one column" index over *col_name found */
+         found = true;
+      }
+   }
+   if (found) {
+      if (verbose) {
+         printf(_("Ok. Index over the %s column already exists and dbcheck will work faster.\n"), col_name);
+      }
+   } else {
+      printf(_("Note. Index over the %s column not found, that can greatly slow down dbcheck.\n"), col_name);
+   }
+
+   return found;
+#else
+   return true;
+#endif
+}
+
+/*
+ * Create temporary one-column index
+ */
+static bool create_tmp_idx(const char *idx_name, const char *table_name, 
+               const char *col_name)
+{
+   idx_tmp_name = NULL;
+   printf(_("Create temporary index... This may take some time!\n"));
+   bsnprintf(buf, sizeof(buf), "CREATE INDEX %s ON %s (%s)", idx_name, table_name, col_name); 
+   if (verbose) {
+      printf("%s\n", buf);
+   }
+   if (db_sql_query(db, buf, NULL, NULL)) {
+      idx_tmp_name = idx_name;
+      if (verbose) {
+         printf(_("Temporary index created.\n"));
+      }
+   } else {
+      printf("%s\n", db_strerror(db));
+      return false;
+   }
+   return true;
+}
+
+/*
+ * Drop temporary index
+ */
+static bool drop_tmp_idx(const char *idx_name, const char *table_name)
+{
+   if (idx_tmp_name != NULL) {
+      printf(_("Drop temporary index.\n"));
+      bsnprintf(buf, sizeof(buf), "DROP INDEX %s ON %s", idx_name, table_name);
+      if (verbose) {
+         printf("%s\n", buf);
+      }
+      if (!db_sql_query(db, buf, NULL, NULL)) {
+         printf("%s\n", db_strerror(db));
+         return false;
+      } else {
+         if (verbose) {
+            printf(_("Temporary index %s deleted.\n"), idx_tmp_name);
+         }
+      }
+   }
+   idx_tmp_name = NULL;
+   return true;
+}