]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/tools/dbcheck.c
update version
[bacula/bacula] / bacula / src / tools / dbcheck.c
index 149bd5e9811e72cad81020d3633e4238fe9a8ba7..a61b1c70dc03034e655e9f7a58314da3c9aac5d9 100644 (file)
@@ -1,26 +1,26 @@
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2002-2007 Free Software Foundation Europe e.V.
+   Copyright (C) 2002-2011 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
-   License as published by the Free Software Foundation plus additions
-   that are listed in the file LICENSE.
+   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.
 
    This program is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    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.
 
-   Bacula® is a registered trademark of John Walker.
+   Bacula® is a registered trademark of Kern Sibbald.
    The licensor of Bacula is the Free Software Foundation Europe
    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
    Switzerland, email:ftf@fsfeurope.org.
  *
  *   Kern E. Sibbald, August 2002
  *
- *   Version $Id$
- *
  */
 
 #include "bacula.h"
 #include "cats/cats.h"
+#include "cats/sql_glue.h"
 #include "lib/runscript.h"
 #include "dird/dird_conf.h"
 
-/* Dummy functions */
-int generate_daemon_event(JCR *jcr, const char *event) 
+extern bool parse_dir_config(CONFIG *config, const char *configfile, int exit_code);
+
+/*
+ * Dummy functions
+ */
+int generate_daemon_event(JCR *jcr, const char *event)
    { return 1; }
 
 typedef struct s_id_ctx {
@@ -61,9 +64,9 @@ typedef struct s_name_ctx {
    int tot_ids;                       /* total to process */
 } NAME_LIST;
 
-
-
-/* Global variables */
+/*
+ * Global variables
+ */
 static bool fix = false;
 static bool batch = false;
 static B_DB *db;
@@ -71,10 +74,15 @@ static ID_LIST id_list;
 static NAME_LIST name_list;
 static char buf[20000];
 static bool quit = false;
+static CONFIG *config;
+static const char *idx_tmp_name;
 
 #define MAX_ID_LIST_LEN 10000000
 
-/* Forward referenced functions */
+/*
+ * Forward referenced functions
+ */
+static void print_catalog_details(CAT *catalog, const char *working_dir);
 static int make_id_list(const char *query, ID_LIST *id_list);
 static int delete_id_list(const char *query, ID_LIST *id_list);
 static int make_name_list(const char *query, NAME_LIST *name_list);
@@ -96,17 +104,24 @@ 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);
+static int check_idx_handler(void *ctx, int num_fields, char **row);
 
 static void usage()
 {
    fprintf(stderr,
-"Usage: dbcheck [-c config] [-C catalog name] [-d debug_level] <working-directory> <bacula-database> <user> <password> [<dbhost>]\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"
-"       -dnn            set debug level to nn\n"
+"       -c              Director conf filename\n"
+"       -B              print catalog configuration and exit\n"
+"       -d <nn>         set debug level to <nn>\n"
+"       -dt             print a 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);
@@ -116,48 +131,53 @@ int main (int argc, char *argv[])
 {
    int ch;
    const char *user, *password, *db_name, *dbhost;
+   int dbport = 0;
+   bool print_catalog=false;
    char *configfile = NULL;
    char *catalogname = NULL;
+   char *endptr;
 
    setlocale(LC_ALL, "");
    bindtextdomain("bacula", LOCALEDIR);
    textdomain("bacula");
+   lmgr_init_thread();
 
    my_name_is(argc, argv, "dbcheck");
-   init_msg(NULL, NULL);              /* setup message handler */
+   init_msg(NULL, NULL);           /* setup message handler */
 
    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:fvB?")) != -1) {
       switch (ch) {
+      case 'B':
+         print_catalog = true;     /* get catalog information from config */
+         break;
       case 'b':                    /* batch */
          batch = true;
          break;
-
       case 'C':                    /* CatalogName */
           catalogname = optarg;
          break;
-
       case 'c':                    /* configfile */
           configfile = optarg;
          break;
-
       case 'd':                    /* debug level */
-         debug_level = atoi(optarg);
-         if (debug_level <= 0)
-            debug_level = 1;
+         if (*optarg == 't') {
+            dbg_timestamp = true;
+         } else {
+            debug_level = atoi(optarg);
+            if (debug_level <= 0) {
+               debug_level = 1;
+            }
+         }
          break;
-
       case 'f':                    /* fix inconsistencies */
          fix = true;
          break;
-
       case 'v':
          verbose++;
          break;
-
       case '?':
       default:
          usage();
@@ -174,7 +194,8 @@ int main (int argc, char *argv[])
       if (argc > 0) {
          Pmsg0(0, _("Warning skipping the additional parameters for working directory/dbname/user/password/host.\n"));
       }
-      parse_config(configfile);
+      config = new_config_parser();
+      parse_dir_config(config, configfile, M_ERROR_TERM);
       LockRes();
       foreach_res(catalog, R_CATALOG) {
          if (catalogname && !strcmp(catalog->hdr.name, catalogname)) {
@@ -203,6 +224,15 @@ int main (int argc, char *argv[])
             exit(1);
          }
          set_working_directory(director->working_directory);
+
+         /*
+          * Print catalog information and exit (-B)
+          */
+         if (print_catalog) {
+            print_catalog_details(catalog, director->working_directory);
+            exit(0);
+         }
+
          db_name = catalog->db_name;
          user = catalog->db_user;
          password = catalog->db_password;
@@ -210,9 +240,10 @@ int main (int argc, char *argv[])
          if (dbhost && dbhost[0] == 0) {
             dbhost = NULL;
          }
+         dbport = catalog->db_port;
       }
    } else {
-      if (argc > 5) {
+      if (argc > 6) {
          Pmsg0(0, _("Wrong number of arguments.\n"));
          usage();
       }
@@ -222,7 +253,9 @@ int main (int argc, char *argv[])
          usage();
       }
 
-      /* This is needed by SQLite to find the db */
+      /*
+       * This is needed by SQLite to find the db
+       */
       working_directory = argv[0];
       db_name = "bacula";
       user = db_name;
@@ -244,16 +277,37 @@ int main (int argc, char *argv[])
          user = argv[2];
          password = argv[3];
          dbhost = argv[4];
+      } else if (argc == 6) {
+         db_name = argv[1];
+         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 (errno == ERANGE) {
+            Pmsg0(0, _("Database port must be a int value.\n"));
+            exit(1);
+         }
       }
    }
 
-   /* Open database */
-   db = db_init_database(NULL, db_name, user, password, dbhost, 0, NULL, 0);
+   /*
+    * Open database
+    */
+   db = db_init_database(NULL, NULL, db_name, user, password, dbhost, dbport, NULL, false, false);
    if (!db_open_database(NULL, db)) {
       Emsg1(M_FATAL, 0, "%s", db_strerror(db));
           return 1;
    }
 
+   /*
+    * Drop temporary index idx_tmp_name if it already exists
+    */
+   drop_tmp_idx("idxPIchk", "File");
+
    if (batch) {
       repair_bad_paths();
       repair_bad_filenames();
@@ -272,12 +326,38 @@ int main (int argc, char *argv[])
       do_interactive_mode();
    }
 
+   /*
+    * Drop temporary index idx_tmp_name
+    */
+   drop_tmp_idx("idxPIchk", "File");
+
    db_close_database(NULL, db);
    close_msg(NULL);
    term_msg();
+   lmgr_cleanup_main();
    return 0;
 }
 
+static void print_catalog_details(CAT *catalog, const char *working_dir)
+{
+   POOLMEM *catalog_details = get_pool_memory(PM_MESSAGE);
+
+   /*
+    * Instantiate a B_DB class and see what db_type gets assigned to it.
+    */
+   db = db_init_database(NULL, catalog->db_driver, catalog->db_name, catalog->db_user,
+                         catalog->db_password, catalog->db_address,
+                         catalog->db_port, catalog->db_socket,
+                         catalog->mult_db_connections,
+                         catalog->disable_batch_insert);
+   if (db) {
+      printf("%sdb_type=%s\nworking_dir=%s\n", catalog->display(catalog_details),
+             db->db_get_type(), working_directory);
+      db_close_database(NULL, db);
+   }
+   free_pool_memory(catalog_details);
+}
+
 static void do_interactive_mode()
 {
    const char *cmd;
@@ -292,7 +372,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) {
@@ -425,9 +505,10 @@ static int print_name_handler(void *ctx, int num_fields, char **row)
 
 static int get_name_handler(void *ctx, int num_fields, char **row)
 {
-   POOLMEM *buf = (POOLMEM *)ctx;
+   POOLMEM *name = (POOLMEM *)ctx;
+
    if (row[0]) {
-      pm_strcpy(&buf, row[0]);
+      pm_strcpy(&name, row[0]);
    }
    return 0;
 }
@@ -439,7 +520,6 @@ static int print_job_handler(void *ctx, int num_fields, char **row)
    return 0;
 }
 
-
 static int print_jobmedia_handler(void *ctx, int num_fields, char **row)
 {
    printf(_("Orphaned JobMediaId=%s JobId=%s Volume=\"%s\"\n"),
@@ -468,7 +548,6 @@ static int print_client_handler(void *ctx, int num_fields, char **row)
    return 0;
 }
 
-
 /*
  * Called here with each id to be added to the list
  */
@@ -547,7 +626,6 @@ static int name_list_handler(void *ctx, int num_fields, char **row)
    return 0;
 }
 
-
 /*
  * Construct name list
  */
@@ -574,7 +652,6 @@ static void print_name_list(NAME_LIST *name_list)
    }
 }
 
-
 /*
  * Free names in the list
  */
@@ -593,7 +670,9 @@ static void eliminate_duplicate_filenames()
 
    printf(_("Checking for duplicate Filename entries.\n"));
 
-   /* Make list of duplicated names */
+   /*
+    * Make list of duplicated names
+    */
    query = "SELECT Name, count(Name) as Count FROM Filename GROUP BY  Name "
            "HAVING count(Name) > 1";
 
@@ -608,10 +687,14 @@ static void eliminate_duplicate_filenames()
       return;
    }
    if (fix) {
-      /* Loop through list of duplicate names */
+      /*
+       * Loop through list of duplicate names
+       */
       for (int i=0; i<name_list.num_ids; i++) {
-         /* Get all the Ids of each name */
-         db_escape_string(esc_name, name_list.name[i], strlen(name_list.name[i]));
+         /*
+          * Get all the Ids of each name
+          */
+         db_escape_string(NULL, db, esc_name, name_list.name[i], strlen(name_list.name[i]));
          bsnprintf(buf, sizeof(buf), "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
          if (verbose > 1) {
             printf("%s\n", buf);
@@ -622,7 +705,9 @@ static void eliminate_duplicate_filenames()
          if (verbose) {
             printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
          }
-         /* Force all records to use the first id then delete the other ids */
+         /*
+          * Force all records to use the first id then delete the other ids
+          */
          for (int j=1; j<id_list.num_ids; j++) {
             char ed1[50], ed2[50];
             bsnprintf(buf, sizeof(buf), "UPDATE File SET FilenameId=%s WHERE FilenameId=%s",
@@ -650,8 +735,9 @@ static void eliminate_duplicate_paths()
 
    printf(_("Checking for duplicate Path entries.\n"));
 
-   /* Make list of duplicated names */
-
+   /*
+    * Make list of duplicated names
+    */
    query = "SELECT Path, count(Path) as Count FROM Path "
            "GROUP BY Path HAVING count(Path) > 1";
 
@@ -666,10 +752,14 @@ static void eliminate_duplicate_paths()
       return;
    }
    if (fix) {
-      /* Loop through list of duplicate names */
+      /*
+       * Loop through list of duplicate names
+       */
       for (int i=0; i<name_list.num_ids; i++) {
-         /* Get all the Ids of each name */
-         db_escape_string(esc_name, name_list.name[i], strlen(name_list.name[i]));
+         /*
+          * Get all the Ids of each name
+          */
+         db_escape_string(NULL, db, esc_name, name_list.name[i], strlen(name_list.name[i]));
          bsnprintf(buf, sizeof(buf), "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
          if (verbose > 1) {
             printf("%s\n", buf);
@@ -680,7 +770,9 @@ static void eliminate_duplicate_paths()
          if (verbose) {
             printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
          }
-         /* Force all records to use the first id then delete the other ids */
+         /*
+          * Force all records to use the first id then delete the other ids
+          */
          for (int j=1; j<id_list.num_ids; j++) {
             char ed1[50], ed2[50];
             bsnprintf(buf, sizeof(buf), "UPDATE File SET PathId=%s WHERE PathId=%s",
@@ -710,7 +802,9 @@ static void eliminate_orphaned_jobmedia_records()
    if (!make_id_list(query, &id_list)) {
       exit(1);
    }
-   /* Loop doing 300000 at a time */
+   /*
+    * Loop doing 300000 at a time
+    */
    while (id_list.num_ids != 0) {
       printf(_("Found %d orphaned JobMedia records.\n"), id_list.num_ids);
       if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
@@ -718,7 +812,7 @@ static void eliminate_orphaned_jobmedia_records()
             char ed1[50];
             bsnprintf(buf, sizeof(buf),
 "SELECT JobMedia.JobMediaId,JobMedia.JobId,Media.VolumeName FROM JobMedia,Media "
-"WHERE JobMedia.JobMediaId=%s AND Media.MediaId=JobMedia.MediaId", 
+"WHERE JobMedia.JobMediaId=%s AND Media.MediaId=JobMedia.MediaId",
                edit_int64(id_list.Id[i], ed1));
             if (!db_sql_query(db, buf, print_jobmedia_handler, NULL)) {
                printf("%s\n", db_strerror(db));
@@ -754,7 +848,9 @@ static void eliminate_orphaned_file_records()
    if (!make_id_list(query, &id_list)) {
       exit(1);
    }
-   /* Loop doing 300000 at a time */
+   /*
+    * Loop doing 300000 at a time
+    */
    while (id_list.num_ids != 0) {
       printf(_("Found %d orphaned File records.\n"), id_list.num_ids);
       if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
@@ -762,7 +858,7 @@ static void eliminate_orphaned_file_records()
             char ed1[50];
             bsnprintf(buf, sizeof(buf),
 "SELECT File.FileId,File.JobId,Filename.Name FROM File,Filename "
-"WHERE File.FileId=%s AND File.FilenameId=Filename.FilenameId", 
+"WHERE File.FileId=%s AND File.FilenameId=Filename.FilenameId",
                edit_int64(id_list.Id[i], ed1));
             if (!db_sql_query(db, buf, print_file_handler, NULL)) {
                printf("%s\n", db_strerror(db));
@@ -786,6 +882,19 @@ 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";
@@ -797,13 +906,15 @@ static void eliminate_orphaned_path_records()
    if (!make_id_list(query, &id_list)) {
       exit(1);
    }
-   /* Loop doing 300000 at a time */
+   /*
+    * Loop doing 300000 at a time
+    */
    while (id_list.num_ids != 0) {
       printf(_("Found %d orphaned Path records.\n"), id_list.num_ids);
       if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
          for (int i=0; i < id_list.num_ids; i++) {
             char ed1[50];
-            bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%s", 
+            bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%s",
                edit_int64(id_list.Id[i], ed1));
             db_sql_query(db, buf, print_name_handler, NULL);
          }
@@ -821,10 +932,27 @@ static void eliminate_orphaned_path_records()
          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";
@@ -836,13 +964,15 @@ static void eliminate_orphaned_filename_records()
    if (!make_id_list(query, &id_list)) {
       exit(1);
    }
-   /* Loop doing 300000 at a time */
+   /*
+    * Loop doing 300000 at a time
+    */
    while (id_list.num_ids != 0) {
       printf(_("Found %d orphaned Filename records.\n"), id_list.num_ids);
       if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
          for (int i=0; i < id_list.num_ids; i++) {
             char ed1[50];
-            bsnprintf(buf, sizeof(buf), "SELECT Name FROM Filename WHERE FilenameId=%s", 
+            bsnprintf(buf, sizeof(buf), "SELECT Name FROM Filename WHERE FilenameId=%s",
                edit_int64(id_list.Id[i], ed1));
             db_sql_query(db, buf, print_name_handler, NULL);
          }
@@ -860,6 +990,11 @@ 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()
@@ -901,7 +1036,8 @@ static void eliminate_orphaned_client_records()
    const char *query;
 
    printf(_("Checking for orphaned Client entries.\n"));
-   /* In English:
+   /*
+    * In English:
     *   Wiffle through Client for every Client
     *   joining with the Job table including every Client even if
     *   there is not a match in Job (left outer join), then
@@ -942,7 +1078,8 @@ static void eliminate_orphaned_job_records()
    const char *query;
 
    printf(_("Checking for orphaned Job entries.\n"));
-   /* In English:
+   /*
+    * In English:
     *   Wiffle through Job for every Job
     *   joining with the Client table including every Job even if
     *   there is not a match in Client (left outer join), then
@@ -982,7 +1119,6 @@ static void eliminate_orphaned_job_records()
    }
 }
 
-
 static void eliminate_admin_records()
 {
    const char *query;
@@ -1049,9 +1185,6 @@ static void eliminate_restore_records()
    }
 }
 
-
-
-
 static void repair_bad_filenames()
 {
    const char *query;
@@ -1071,7 +1204,7 @@ static void repair_bad_filenames()
       for (i=0; i < id_list.num_ids; i++) {
          char ed1[50];
          bsnprintf(buf, sizeof(buf),
-            "SELECT Name FROM Filename WHERE FilenameId=%s", 
+            "SELECT Name FROM Filename WHERE FilenameId=%s",
                 edit_int64(id_list.Id[i], ed1));
          if (!db_sql_query(db, buf, print_name_handler, NULL)) {
             printf("%s\n", db_strerror(db));
@@ -1089,12 +1222,14 @@ static void repair_bad_filenames()
          int len;
          char ed1[50];
          bsnprintf(buf, sizeof(buf),
-            "SELECT Name FROM Filename WHERE FilenameId=%s", 
+            "SELECT Name FROM Filename WHERE FilenameId=%s",
                edit_int64(id_list.Id[i], ed1));
          if (!db_sql_query(db, buf, get_name_handler, name)) {
             printf("%s\n", db_strerror(db));
          }
-         /* Strip trailing slash(es) */
+         /*
+          * Strip trailing slash(es)
+          */
          for (len=strlen(name); len > 0 && IsPathSeparator(name[len-1]); len--)
             {  }
          if (len == 0) {
@@ -1103,7 +1238,7 @@ static void repair_bad_filenames()
             esc_name[1] = 0;
          } else {
             name[len-1] = 0;
-            db_escape_string(esc_name, name, len);
+            db_escape_string(NULL, db, esc_name, name, len);
          }
          bsnprintf(buf, sizeof(buf),
             "UPDATE Filename SET Name='%s' WHERE FilenameId=%s",
@@ -1113,6 +1248,7 @@ static void repair_bad_filenames()
          }
          db_sql_query(db, buf, NULL, NULL);
       }
+      free_pool_memory(name);
    }
 }
 
@@ -1156,13 +1292,17 @@ static void repair_bad_paths()
          if (!db_sql_query(db, buf, get_name_handler, name)) {
             printf("%s\n", db_strerror(db));
          }
-         /* Strip trailing blanks */
+         /*
+          * Strip trailing blanks
+          */
          for (len=strlen(name); len > 0 && name[len-1]==' '; len--) {
             name[len-1] = 0;
          }
-         /* Add trailing slash */
+         /*
+          * Add trailing slash
+          */
          len = pm_strcat(&name, "/");
-         db_escape_string(esc_name, name, len);
+         db_escape_string(NULL, db, esc_name, name, len);
          bsnprintf(buf, sizeof(buf), "UPDATE Path SET Path='%s' WHERE PathId=%s",
             esc_name, edit_int64(id_list.Id[i], ed1));
          if (verbose > 1) {
@@ -1170,10 +1310,10 @@ static void repair_bad_paths()
          }
          db_sql_query(db, buf, NULL, NULL);
       }
+      free_pool_memory(name);
    }
 }
 
-
 /*
  * Gen next input command from the terminal
  */
@@ -1203,3 +1343,147 @@ 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.
+ */
+
+#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;
+}
+
+/*
+ * Return TRUE if "one column" index over *col_name exists
+ */
+static bool check_idx(const char *col_name)
+{
+   int i;
+   int found = false;
+   const char *query = "SHOW INDEX FROM File";
+
+   switch (db_get_type_index(db)) {
+   case SQL_TYPE_MYSQL:
+      memset(&idx_list, 0, sizeof(idx_list));
+      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;
+   default:
+      return true;
+   }
+}
+
+/*
+ * 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;
+}