]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/tools/dbcheck.c
Apply dbcheck patch from Yuri Timofeev <tim4dev@gmail.com>.
[bacula/bacula] / bacula / src / tools / dbcheck.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2008 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *  Program to check a Bacula database for consistency and to
31  *   make repairs
32  *
33  *   Kern E. Sibbald, August 2002
34  *
35  *   Version $Id$
36  *
37  */
38
39 #include "bacula.h"
40 #include "cats/cats.h"
41 #include "lib/runscript.h"
42 #include "dird/dird_conf.h"
43
44 extern bool parse_dir_config(CONFIG *config, const char *configfile, int exit_code);
45
46 /* Dummy functions */
47 int generate_daemon_event(JCR *jcr, const char *event) 
48    { return 1; }
49
50 typedef struct s_id_ctx {
51    int64_t *Id;                       /* ids to be modified */
52    int num_ids;                       /* ids stored */
53    int max_ids;                       /* size of array */
54    int num_del;                       /* number deleted */
55    int tot_ids;                       /* total to process */
56 } ID_LIST;
57
58 typedef struct s_name_ctx {
59    char **name;                       /* list of names */
60    int num_ids;                       /* ids stored */
61    int max_ids;                       /* size of array */
62    int num_del;                       /* number deleted */
63    int tot_ids;                       /* total to process */
64 } NAME_LIST;
65
66 /* Global variables */
67 static bool fix = false;
68 static bool batch = false;
69 static B_DB *db;
70 static ID_LIST id_list;
71 static NAME_LIST name_list;
72 static char buf[20000];
73 static bool quit = false;
74 static CONFIG *config;
75
76 #define MAX_ID_LIST_LEN 10000000
77
78 /* Forward referenced functions */
79 static int make_id_list(const char *query, ID_LIST *id_list);
80 static int delete_id_list(const char *query, ID_LIST *id_list);
81 static int make_name_list(const char *query, NAME_LIST *name_list);
82 static void print_name_list(NAME_LIST *name_list);
83 static void free_name_list(NAME_LIST *name_list);
84 static char *get_cmd(const char *prompt);
85 static void eliminate_duplicate_filenames();
86 static void eliminate_duplicate_paths();
87 static void eliminate_orphaned_jobmedia_records();
88 static void eliminate_orphaned_file_records();
89 static void eliminate_orphaned_path_records();
90 static void eliminate_orphaned_filename_records();
91 static void eliminate_orphaned_fileset_records();
92 static void eliminate_orphaned_client_records();
93 static void eliminate_orphaned_job_records();
94 static void eliminate_admin_records();
95 static void eliminate_restore_records();
96 static void repair_bad_paths();
97 static void repair_bad_filenames();
98 static void do_interactive_mode();
99 static bool yes_no(const char *prompt);
100 static bool check_idx(const char *col_name);
101 static bool create_tmp_idx(const char *idx_name, const char *table_name, 
102               const char *col_name);
103 static bool drop_tmp_idx(const char *idx_name, const char *table_name);
104 #ifdef HAVE_MYSQL
105 static int check_idx_handler(void *ctx, int num_fields, char **row);
106 #endif
107
108
109 /* Global variables */
110 static const char *idx_tmp_name;
111
112
113 static void usage()
114 {
115    fprintf(stderr,
116 "Usage: dbcheck [-c config] [-C catalog name] [-d debug_level] <working-directory> <bacula-database> <user> <password> [<dbhost>] [<dbport>]\n"
117 "       -b              batch mode\n"
118 "       -C              catalog name in the director conf file\n"
119 "       -c              Director conf filename\n"
120 "       -d <nn>         set debug level to <nn>\n"
121 "       -dt             print timestamp in debug output\n"
122 "       -f              fix inconsistencies\n"
123 "       -v              verbose\n"
124 "       -?              print this message\n\n");
125    exit(1);
126 }
127
128 int main (int argc, char *argv[])
129 {
130    int ch;
131    const char *user, *password, *db_name, *dbhost;
132    int dbport = 0;
133    char *configfile = NULL;
134    char *catalogname = NULL;
135    char *endptr;
136
137    setlocale(LC_ALL, "");
138    bindtextdomain("bacula", LOCALEDIR);
139    textdomain("bacula");
140
141    my_name_is(argc, argv, "dbcheck");
142    init_msg(NULL, NULL);              /* setup message handler */
143
144    memset(&id_list, 0, sizeof(id_list));
145    memset(&name_list, 0, sizeof(name_list));
146
147    while ((ch = getopt(argc, argv, "bc:C:d:fv?")) != -1) {
148       switch (ch) {
149       case 'b':                    /* batch */
150          batch = true;
151          break;
152
153       case 'C':                    /* CatalogName */
154           catalogname = optarg;
155          break;
156
157       case 'c':                    /* configfile */
158           configfile = optarg;
159          break;
160
161       case 'd':                    /* debug level */
162          if (*optarg == 't') {
163             dbg_timestamp = true;
164          } else {
165             debug_level = atoi(optarg);
166             if (debug_level <= 0) {
167                debug_level = 1;
168             }
169          }
170          break;
171
172       case 'f':                    /* fix inconsistencies */
173          fix = true;
174          break;
175
176       case 'v':
177          verbose++;
178          break;
179
180       case '?':
181       default:
182          usage();
183       }
184    }
185    argc -= optind;
186    argv += optind;
187
188    OSDependentInit();
189
190    if (configfile) {
191       CAT *catalog = NULL;
192       int found = 0;
193       if (argc > 0) {
194          Pmsg0(0, _("Warning skipping the additional parameters for working directory/dbname/user/password/host.\n"));
195       }
196       config = new_config_parser();
197       parse_dir_config(config, configfile, M_ERROR_TERM);
198       LockRes();
199       foreach_res(catalog, R_CATALOG) {
200          if (catalogname && !strcmp(catalog->hdr.name, catalogname)) {
201             ++found;
202             break;
203          } else if (!catalogname) { // stop on first if no catalogname is given
204            ++found;
205            break;
206          }
207       }
208       UnlockRes();
209       if (!found) {
210          if (catalogname) {
211             Pmsg2(0, _("Error can not find the Catalog name[%s] in the given config file [%s]\n"), catalogname, configfile);
212          } else {
213             Pmsg1(0, _("Error there is no Catalog section in the given config file [%s]\n"), configfile);
214          }
215          exit(1);
216       } else {
217          DIRRES *director;
218          LockRes();
219          director = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
220          UnlockRes();
221          if (!director) {
222             Pmsg0(0, _("Error no Director resource defined.\n"));
223             exit(1);
224          }
225          set_working_directory(director->working_directory);
226          db_name = catalog->db_name;
227          user = catalog->db_user;
228          password = catalog->db_password;
229          dbhost = catalog->db_address;
230          if (dbhost && dbhost[0] == 0) {
231             dbhost = NULL;
232          }
233          dbport = catalog->db_port;
234       }
235    } else {
236       if (argc > 6) {
237          Pmsg0(0, _("Wrong number of arguments.\n"));
238          usage();
239       }
240
241       if (argc < 1) {
242          Pmsg0(0, _("Working directory not supplied.\n"));
243          usage();
244       }
245
246       /* This is needed by SQLite to find the db */
247       working_directory = argv[0];
248       db_name = "bacula";
249       user = db_name;
250       password = "";
251       dbhost = NULL;
252
253       if (argc == 2) {
254          db_name = argv[1];
255          user = db_name;
256       } else if (argc == 3) {
257          db_name = argv[1];
258          user = argv[2];
259       } else if (argc == 4) {
260          db_name = argv[1];
261          user = argv[2];
262          password = argv[3];
263       } else if (argc == 5) {
264          db_name = argv[1];
265          user = argv[2];
266          password = argv[3];
267          dbhost = argv[4];
268       } else if (argc == 6) {
269          db_name = argv[1];
270          user = argv[2];
271          password = argv[3];
272          dbhost = argv[4];
273          errno = 0;
274          dbport = strtol(argv[5], &endptr, 10);
275          if (*endptr != '\0') {
276             Pmsg0(0, _("Database port must be a numeric value.\n"));
277             exit(1);
278          } else if (errno == ERANGE) {
279             Pmsg0(0, _("Database port must be a int value.\n"));
280             exit(1);
281          }
282       }
283    }
284
285    /* Open database */
286    db = db_init_database(NULL, db_name, user, password, dbhost, dbport, NULL, 0);
287    if (!db_open_database(NULL, db)) {
288       Emsg1(M_FATAL, 0, "%s", db_strerror(db));
289           return 1;
290    }
291
292    if (batch) {
293       repair_bad_paths();
294       repair_bad_filenames();
295       eliminate_duplicate_filenames();
296       eliminate_duplicate_paths();
297       eliminate_orphaned_jobmedia_records();
298       eliminate_orphaned_file_records();
299       eliminate_orphaned_path_records();
300       eliminate_orphaned_filename_records();
301       eliminate_orphaned_fileset_records();
302       eliminate_orphaned_client_records();
303       eliminate_orphaned_job_records();
304       eliminate_admin_records();
305       eliminate_restore_records();
306    } else {
307       do_interactive_mode();
308    }
309
310    db_close_database(NULL, db);
311    close_msg(NULL);
312    term_msg();
313    return 0;
314 }
315
316 static void do_interactive_mode()
317 {
318    const char *cmd;
319
320    printf(_("Hello, this is the database check/correct program.\n"));
321    if (fix)
322       printf(_("Modify database is on."));
323    else
324       printf(_("Modify database is off."));
325    if (verbose)
326       printf(_(" Verbose is on.\n"));
327    else
328       printf(_(" Verbose is off.\n"));
329
330    printf(_("Please select the fuction you want to perform.\n"));
331
332    while (!quit) {
333       if (fix) {
334          printf(_("\n"
335 "     1) Toggle modify database flag\n"
336 "     2) Toggle verbose flag\n"
337 "     3) Repair bad Filename records\n"
338 "     4) Repair bad Path records\n"
339 "     5) Eliminate duplicate Filename records\n"
340 "     6) Eliminate duplicate Path records\n"
341 "     7) Eliminate orphaned Jobmedia records\n"
342 "     8) Eliminate orphaned File records\n"
343 "     9) Eliminate orphaned Path records\n"
344 "    10) Eliminate orphaned Filename records\n"
345 "    11) Eliminate orphaned FileSet records\n"
346 "    12) Eliminate orphaned Client records\n"
347 "    13) Eliminate orphaned Job records\n"
348 "    14) Eliminate all Admin records\n"
349 "    15) Eliminate all Restore records\n"
350 "    16) All (3-15)\n"
351 "    17) Quit\n"));
352        } else {
353          printf(_("\n"
354 "     1) Toggle modify database flag\n"
355 "     2) Toggle verbose flag\n"
356 "     3) Check for bad Filename records\n"
357 "     4) Check for bad Path records\n"
358 "     5) Check for duplicate Filename records\n"
359 "     6) Check for duplicate Path records\n"
360 "     7) Check for orphaned Jobmedia records\n"
361 "     8) Check for orphaned File records\n"
362 "     9) Check for orphaned Path records\n"
363 "    10) Check for orphaned Filename records\n"
364 "    11) Check for orphaned FileSet records\n"
365 "    12) Check for orphaned Client records\n"
366 "    13) Check for orphaned Job records\n"
367 "    14) Check for all Admin records\n"
368 "    15) Check for all Restore records\n"
369 "    16) All (3-15)\n"
370 "    17) Quit\n"));
371        }
372
373       cmd = get_cmd(_("Select function number: "));
374       if (cmd) {
375          int item = atoi(cmd);
376          switch (item) {
377          case 1:
378             fix = !fix;
379             if (fix)
380                printf(_("Database will be modified.\n"));
381             else
382                printf(_("Database will NOT be modified.\n"));
383             break;
384          case 2:
385             verbose = verbose?0:1;
386             if (verbose)
387                printf(_(" Verbose is on.\n"));
388             else
389                printf(_(" Verbose is off.\n"));
390             break;
391          case 3:
392             repair_bad_filenames();
393             break;
394          case 4:
395             repair_bad_paths();
396             break;
397          case 5:
398             eliminate_duplicate_filenames();
399             break;
400          case 6:
401             eliminate_duplicate_paths();
402             break;
403          case 7:
404             eliminate_orphaned_jobmedia_records();
405             break;
406          case 8:
407             eliminate_orphaned_file_records();
408             break;
409          case 9:
410             eliminate_orphaned_path_records();
411             break;
412          case 10:
413             eliminate_orphaned_filename_records();
414             break;
415          case 11:
416             eliminate_orphaned_fileset_records();
417             break;
418          case 12:
419             eliminate_orphaned_client_records();
420             break;
421          case 13:
422             eliminate_orphaned_job_records();
423             break;
424          case 14:
425             eliminate_admin_records();
426             break;
427          case 15:
428             eliminate_restore_records();
429             break;
430          case 16:
431             repair_bad_filenames();
432             repair_bad_paths();
433             eliminate_duplicate_filenames();
434             eliminate_duplicate_paths();
435             eliminate_orphaned_jobmedia_records();
436             eliminate_orphaned_file_records();
437             eliminate_orphaned_path_records();
438             eliminate_orphaned_filename_records();
439             eliminate_orphaned_fileset_records();
440             eliminate_orphaned_client_records();
441             eliminate_orphaned_job_records();
442             eliminate_admin_records();
443             eliminate_restore_records();
444             break;
445          case 17:
446             quit = true;
447             break;
448          }
449       }
450    }
451 }
452
453 static int print_name_handler(void *ctx, int num_fields, char **row)
454 {
455    if (row[0]) {
456       printf("%s\n", row[0]);
457    }
458    return 0;
459 }
460
461 static int get_name_handler(void *ctx, int num_fields, char **row)
462 {
463    POOLMEM *buf = (POOLMEM *)ctx;
464    if (row[0]) {
465       pm_strcpy(&buf, row[0]);
466    }
467    return 0;
468 }
469
470 static int print_job_handler(void *ctx, int num_fields, char **row)
471 {
472    printf(_("JobId=%s Name=\"%s\" StartTime=%s\n"),
473               NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
474    return 0;
475 }
476
477
478 static int print_jobmedia_handler(void *ctx, int num_fields, char **row)
479 {
480    printf(_("Orphaned JobMediaId=%s JobId=%s Volume=\"%s\"\n"),
481               NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
482    return 0;
483 }
484
485 static int print_file_handler(void *ctx, int num_fields, char **row)
486 {
487    printf(_("Orphaned FileId=%s JobId=%s Volume=\"%s\"\n"),
488               NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
489    return 0;
490 }
491
492 static int print_fileset_handler(void *ctx, int num_fields, char **row)
493 {
494    printf(_("Orphaned FileSetId=%s FileSet=\"%s\" MD5=%s\n"),
495               NPRT(row[0]), NPRT(row[1]), NPRT(row[2]));
496    return 0;
497 }
498
499 static int print_client_handler(void *ctx, int num_fields, char **row)
500 {
501    printf(_("Orphaned ClientId=%s Name=\"%s\"\n"),
502               NPRT(row[0]), NPRT(row[1]));
503    return 0;
504 }
505
506
507 /*
508  * Called here with each id to be added to the list
509  */
510 static int id_list_handler(void *ctx, int num_fields, char **row)
511 {
512    ID_LIST *lst = (ID_LIST *)ctx;
513
514    if (lst->num_ids == MAX_ID_LIST_LEN) {
515       return 1;
516    }
517    if (lst->num_ids == lst->max_ids) {
518       if (lst->max_ids == 0) {
519          lst->max_ids = 10000;
520          lst->Id = (int64_t *)bmalloc(sizeof(int64_t) * lst->max_ids);
521       } else {
522          lst->max_ids = (lst->max_ids * 3) / 2;
523          lst->Id = (int64_t *)brealloc(lst->Id, sizeof(int64_t) * lst->max_ids);
524       }
525    }
526    lst->Id[lst->num_ids++] = str_to_int64(row[0]);
527    return 0;
528 }
529
530 /*
531  * Construct record id list
532  */
533 static int make_id_list(const char *query, ID_LIST *id_list)
534 {
535    id_list->num_ids = 0;
536    id_list->num_del = 0;
537    id_list->tot_ids = 0;
538
539    if (!db_sql_query(db, query, id_list_handler, (void *)id_list)) {
540       printf("%s", db_strerror(db));
541       return 0;
542    }
543    return 1;
544 }
545
546 /*
547  * Delete all entries in the list
548  */
549 static int delete_id_list(const char *query, ID_LIST *id_list)
550 {
551    char ed1[50];
552    for (int i=0; i < id_list->num_ids; i++) {
553       bsnprintf(buf, sizeof(buf), query, edit_int64(id_list->Id[i], ed1));
554       if (verbose) {
555          printf(_("Deleting: %s\n"), buf);
556       }
557       db_sql_query(db, buf, NULL, NULL);
558    }
559    return 1;
560 }
561
562 /*
563  * Called here with each name to be added to the list
564  */
565 static int name_list_handler(void *ctx, int num_fields, char **row)
566 {
567    NAME_LIST *name = (NAME_LIST *)ctx;
568
569    if (name->num_ids == MAX_ID_LIST_LEN) {
570       return 1;
571    }
572    if (name->num_ids == name->max_ids) {
573       if (name->max_ids == 0) {
574          name->max_ids = 10000;
575          name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
576       } else {
577          name->max_ids = (name->max_ids * 3) / 2;
578          name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
579       }
580    }
581    name->name[name->num_ids++] = bstrdup(row[0]);
582    return 0;
583 }
584
585
586 /*
587  * Construct name list
588  */
589 static int make_name_list(const char *query, NAME_LIST *name_list)
590 {
591    name_list->num_ids = 0;
592    name_list->num_del = 0;
593    name_list->tot_ids = 0;
594
595    if (!db_sql_query(db, query, name_list_handler, (void *)name_list)) {
596       printf("%s", db_strerror(db));
597       return 0;
598    }
599    return 1;
600 }
601
602 /*
603  * Print names in the list
604  */
605 static void print_name_list(NAME_LIST *name_list)
606 {
607    for (int i=0; i < name_list->num_ids; i++) {
608       printf("%s\n", name_list->name[i]);
609    }
610 }
611
612
613 /*
614  * Free names in the list
615  */
616 static void free_name_list(NAME_LIST *name_list)
617 {
618    for (int i=0; i < name_list->num_ids; i++) {
619       free(name_list->name[i]);
620    }
621    name_list->num_ids = 0;
622 }
623
624 static void eliminate_duplicate_filenames()
625 {
626    const char *query;
627    char esc_name[5000];
628
629    printf(_("Checking for duplicate Filename entries.\n"));
630
631    /* Make list of duplicated names */
632    query = "SELECT Name, count(Name) as Count FROM Filename GROUP BY  Name "
633            "HAVING count(Name) > 1";
634
635    if (!make_name_list(query, &name_list)) {
636       exit(1);
637    }
638    printf(_("Found %d duplicate Filename records.\n"), name_list.num_ids);
639    if (name_list.num_ids && verbose && yes_no(_("Print the list? (yes/no): "))) {
640       print_name_list(&name_list);
641    }
642    if (quit) {
643       return;
644    }
645    if (fix) {
646       /* Loop through list of duplicate names */
647       for (int i=0; i<name_list.num_ids; i++) {
648          /* Get all the Ids of each name */
649          db_escape_string(NULL, db, esc_name, name_list.name[i], strlen(name_list.name[i]));
650          bsnprintf(buf, sizeof(buf), "SELECT FilenameId FROM Filename WHERE Name='%s'", esc_name);
651          if (verbose > 1) {
652             printf("%s\n", buf);
653          }
654          if (!make_id_list(buf, &id_list)) {
655             exit(1);
656          }
657          if (verbose) {
658             printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
659          }
660          /* Force all records to use the first id then delete the other ids */
661          for (int j=1; j<id_list.num_ids; j++) {
662             char ed1[50], ed2[50];
663             bsnprintf(buf, sizeof(buf), "UPDATE File SET FilenameId=%s WHERE FilenameId=%s",
664                edit_int64(id_list.Id[0], ed1), edit_int64(id_list.Id[j], ed2));
665             if (verbose > 1) {
666                printf("%s\n", buf);
667             }
668             db_sql_query(db, buf, NULL, NULL);
669             bsnprintf(buf, sizeof(buf), "DELETE FROM Filename WHERE FilenameId=%s",
670                ed2);
671             if (verbose > 2) {
672                printf("%s\n", buf);
673             }
674             db_sql_query(db, buf, NULL, NULL);
675          }
676       }
677    }
678    free_name_list(&name_list);
679 }
680
681 static void eliminate_duplicate_paths()
682 {
683    const char *query;
684    char esc_name[5000];
685
686    printf(_("Checking for duplicate Path entries.\n"));
687
688    /* Make list of duplicated names */
689
690    query = "SELECT Path, count(Path) as Count FROM Path "
691            "GROUP BY Path HAVING count(Path) > 1";
692
693    if (!make_name_list(query, &name_list)) {
694       exit(1);
695    }
696    printf(_("Found %d duplicate Path records.\n"), name_list.num_ids);
697    if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
698       print_name_list(&name_list);
699    }
700    if (quit) {
701       return;
702    }
703    if (fix) {
704       /* Loop through list of duplicate names */
705       for (int i=0; i<name_list.num_ids; i++) {
706          /* Get all the Ids of each name */
707          db_escape_string(NULL, db, esc_name, name_list.name[i], strlen(name_list.name[i]));
708          bsnprintf(buf, sizeof(buf), "SELECT PathId FROM Path WHERE Path='%s'", esc_name);
709          if (verbose > 1) {
710             printf("%s\n", buf);
711          }
712          if (!make_id_list(buf, &id_list)) {
713             exit(1);
714          }
715          if (verbose) {
716             printf(_("Found %d for: %s\n"), id_list.num_ids, name_list.name[i]);
717          }
718          /* Force all records to use the first id then delete the other ids */
719          for (int j=1; j<id_list.num_ids; j++) {
720             char ed1[50], ed2[50];
721             bsnprintf(buf, sizeof(buf), "UPDATE File SET PathId=%s WHERE PathId=%s",
722                edit_int64(id_list.Id[0], ed1), edit_int64(id_list.Id[j], ed2));
723             if (verbose > 1) {
724                printf("%s\n", buf);
725             }
726             db_sql_query(db, buf, NULL, NULL);
727             bsnprintf(buf, sizeof(buf), "DELETE FROM Path WHERE PathId=%s", ed2);
728             if (verbose > 2) {
729                printf("%s\n", buf);
730             }
731             db_sql_query(db, buf, NULL, NULL);
732          }
733       }
734    }
735    free_name_list(&name_list);
736 }
737
738 static void eliminate_orphaned_jobmedia_records()
739 {
740    const char *query = "SELECT JobMedia.JobMediaId,Job.JobId FROM JobMedia "
741                 "LEFT OUTER JOIN Job ON (JobMedia.JobId=Job.JobId) "
742                 "WHERE Job.JobId IS NULL LIMIT 300000";
743
744    printf(_("Checking for orphaned JobMedia entries.\n"));
745    if (!make_id_list(query, &id_list)) {
746       exit(1);
747    }
748    /* Loop doing 300000 at a time */
749    while (id_list.num_ids != 0) {
750       printf(_("Found %d orphaned JobMedia records.\n"), id_list.num_ids);
751       if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
752          for (int i=0; i < id_list.num_ids; i++) {
753             char ed1[50];
754             bsnprintf(buf, sizeof(buf),
755 "SELECT JobMedia.JobMediaId,JobMedia.JobId,Media.VolumeName FROM JobMedia,Media "
756 "WHERE JobMedia.JobMediaId=%s AND Media.MediaId=JobMedia.MediaId", 
757                edit_int64(id_list.Id[i], ed1));
758             if (!db_sql_query(db, buf, print_jobmedia_handler, NULL)) {
759                printf("%s\n", db_strerror(db));
760             }
761          }
762       }
763       if (quit) {
764          return;
765       }
766
767       if (fix && id_list.num_ids > 0) {
768          printf(_("Deleting %d orphaned JobMedia records.\n"), id_list.num_ids);
769          delete_id_list("DELETE FROM JobMedia WHERE JobMediaId=%s", &id_list);
770       } else {
771          break;                       /* get out if not updating db */
772       }
773       if (!make_id_list(query, &id_list)) {
774          exit(1);
775       }
776    }
777 }
778
779 static void eliminate_orphaned_file_records()
780 {
781    const char *query = "SELECT File.FileId,Job.JobId FROM File "
782                 "LEFT OUTER JOIN Job ON (File.JobId=Job.JobId) "
783                "WHERE Job.JobId IS NULL LIMIT 300000";
784
785    printf(_("Checking for orphaned File entries. This may take some time!\n"));
786    if (verbose > 1) {
787       printf("%s\n", query);
788    }
789    if (!make_id_list(query, &id_list)) {
790       exit(1);
791    }
792    /* Loop doing 300000 at a time */
793    while (id_list.num_ids != 0) {
794       printf(_("Found %d orphaned File records.\n"), id_list.num_ids);
795       if (name_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
796          for (int i=0; i < id_list.num_ids; i++) {
797             char ed1[50];
798             bsnprintf(buf, sizeof(buf),
799 "SELECT File.FileId,File.JobId,Filename.Name FROM File,Filename "
800 "WHERE File.FileId=%s AND File.FilenameId=Filename.FilenameId", 
801                edit_int64(id_list.Id[i], ed1));
802             if (!db_sql_query(db, buf, print_file_handler, NULL)) {
803                printf("%s\n", db_strerror(db));
804             }
805          }
806       }
807       if (quit) {
808          return;
809       }
810       if (fix && id_list.num_ids > 0) {
811          printf(_("Deleting %d orphaned File records.\n"), id_list.num_ids);
812          delete_id_list("DELETE FROM File WHERE FileId=%s", &id_list);
813       } else {
814          break;                       /* get out if not updating db */
815       }
816       if (!make_id_list(query, &id_list)) {
817          exit(1);
818       }
819    }
820 }
821
822 static void eliminate_orphaned_path_records()
823 {
824    idx_tmp_name = NULL;
825    /* check the existence of the required "one column" index */
826    if (!check_idx("PathId"))  {
827       if (yes_no(_("Create temporary index? (yes/no): "))) {
828          /* create temporary index PathId  */
829          create_tmp_idx("idxPIchk", "File", "PathId");
830       }
831    }
832
833    const char *query = "SELECT DISTINCT Path.PathId,File.PathId FROM Path "
834                "LEFT OUTER JOIN File ON (Path.PathId=File.PathId) "
835                "WHERE File.PathId IS NULL LIMIT 300000";
836
837    printf(_("Checking for orphaned Path entries. This may take some time!\n"));
838    if (verbose > 1) {
839       printf("%s\n", query);
840    }
841    if (!make_id_list(query, &id_list)) {
842       exit(1);
843    }
844    /* Loop doing 300000 at a time */
845    while (id_list.num_ids != 0) {
846       printf(_("Found %d orphaned Path records.\n"), id_list.num_ids);
847       if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
848          for (int i=0; i < id_list.num_ids; i++) {
849             char ed1[50];
850             bsnprintf(buf, sizeof(buf), "SELECT Path FROM Path WHERE PathId=%s", 
851                edit_int64(id_list.Id[i], ed1));
852             db_sql_query(db, buf, print_name_handler, NULL);
853          }
854       }
855       if (quit) {
856          return;
857       }
858       if (fix && id_list.num_ids > 0) {
859          printf(_("Deleting %d orphaned Path records.\n"), id_list.num_ids);
860          delete_id_list("DELETE FROM Path WHERE PathId=%s", &id_list);
861       } else {
862          break;                       /* get out if not updating db */
863       }
864       if (!make_id_list(query, &id_list)) {
865          exit(1);
866       }
867    } 
868    /* drop temporary index idx_tmp_name */
869    drop_tmp_idx("idxPIchk", "File");
870 }
871
872 static void eliminate_orphaned_filename_records()
873 {
874    idx_tmp_name = NULL;
875    /* check the existence of the required "one column" index */
876    if (!check_idx("FilenameId") )      {
877       if (yes_no(_("Create temporary index? (yes/no): "))) {
878          /* create temporary index FilenameId  */
879          create_tmp_idx("idxFIchk", "File", "FilenameId");
880       }
881    }
882
883    const char *query = "SELECT Filename.FilenameId,File.FilenameId FROM Filename "
884                 "LEFT OUTER JOIN File ON (Filename.FilenameId=File.FilenameId) "
885                 "WHERE File.FilenameId IS NULL LIMIT 300000";
886
887    printf(_("Checking for orphaned Filename entries. This may take some time!\n"));
888    if (verbose > 1) {
889       printf("%s\n", query);
890    }
891    if (!make_id_list(query, &id_list)) {
892       exit(1);
893    }
894    /* Loop doing 300000 at a time */
895    while (id_list.num_ids != 0) {
896       printf(_("Found %d orphaned Filename records.\n"), id_list.num_ids);
897       if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
898          for (int i=0; i < id_list.num_ids; i++) {
899             char ed1[50];
900             bsnprintf(buf, sizeof(buf), "SELECT Name FROM Filename WHERE FilenameId=%s", 
901                edit_int64(id_list.Id[i], ed1));
902             db_sql_query(db, buf, print_name_handler, NULL);
903          }
904       }
905       if (quit) {
906          return;
907       }
908       if (fix && id_list.num_ids > 0) {
909          printf(_("Deleting %d orphaned Filename records.\n"), id_list.num_ids);
910          delete_id_list("DELETE FROM Filename WHERE FilenameId=%s", &id_list);
911       } else {
912          break;                       /* get out if not updating db */
913       }
914       if (!make_id_list(query, &id_list)) {
915          exit(1);
916       }
917    }
918    /* drop temporary index idx_tmp_name */
919    drop_tmp_idx("idxFIchk", "File");
920
921 }
922
923 static void eliminate_orphaned_fileset_records()
924 {
925    const char *query;
926
927    printf(_("Checking for orphaned FileSet entries. This takes some time!\n"));
928    query = "SELECT FileSet.FileSetId,Job.FileSetId FROM FileSet "
929            "LEFT OUTER JOIN Job ON (FileSet.FileSetId=Job.FileSetId) "
930            "WHERE Job.FileSetId IS NULL";
931    if (verbose > 1) {
932       printf("%s\n", query);
933    }
934    if (!make_id_list(query, &id_list)) {
935       exit(1);
936    }
937    printf(_("Found %d orphaned FileSet records.\n"), id_list.num_ids);
938    if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
939       for (int i=0; i < id_list.num_ids; i++) {
940          char ed1[50];
941          bsnprintf(buf, sizeof(buf), "SELECT FileSetId,FileSet,MD5 FROM FileSet "
942                       "WHERE FileSetId=%s", edit_int64(id_list.Id[i], ed1));
943          if (!db_sql_query(db, buf, print_fileset_handler, NULL)) {
944             printf("%s\n", db_strerror(db));
945          }
946       }
947    }
948    if (quit) {
949       return;
950    }
951    if (fix && id_list.num_ids > 0) {
952       printf(_("Deleting %d orphaned FileSet records.\n"), id_list.num_ids);
953       delete_id_list("DELETE FROM FileSet WHERE FileSetId=%s", &id_list);
954    }
955 }
956
957 static void eliminate_orphaned_client_records()
958 {
959    const char *query;
960
961    printf(_("Checking for orphaned Client entries.\n"));
962    /* In English:
963     *   Wiffle through Client for every Client
964     *   joining with the Job table including every Client even if
965     *   there is not a match in Job (left outer join), then
966     *   filter out only those where no Job points to a Client
967     *   i.e. Job.Client is NULL
968     */
969    query = "SELECT Client.ClientId,Client.Name FROM Client "
970            "LEFT OUTER JOIN Job ON (Client.ClientId=Job.ClientId) "
971            "WHERE Job.ClientId IS NULL";
972    if (verbose > 1) {
973       printf("%s\n", query);
974    }
975    if (!make_id_list(query, &id_list)) {
976       exit(1);
977    }
978    printf(_("Found %d orphaned Client records.\n"), id_list.num_ids);
979    if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
980       for (int i=0; i < id_list.num_ids; i++) {
981          char ed1[50];
982          bsnprintf(buf, sizeof(buf), "SELECT ClientId,Name FROM Client "
983                       "WHERE ClientId=%s", edit_int64(id_list.Id[i], ed1));
984          if (!db_sql_query(db, buf, print_client_handler, NULL)) {
985             printf("%s\n", db_strerror(db));
986          }
987       }
988    }
989    if (quit) {
990       return;
991    }
992    if (fix && id_list.num_ids > 0) {
993       printf(_("Deleting %d orphaned Client records.\n"), id_list.num_ids);
994       delete_id_list("DELETE FROM Client WHERE ClientId=%s", &id_list);
995    }
996 }
997
998 static void eliminate_orphaned_job_records()
999 {
1000    const char *query;
1001
1002    printf(_("Checking for orphaned Job entries.\n"));
1003    /* In English:
1004     *   Wiffle through Job for every Job
1005     *   joining with the Client table including every Job even if
1006     *   there is not a match in Client (left outer join), then
1007     *   filter out only those where no Client exists
1008     *   i.e. Client.Name is NULL
1009     */
1010    query = "SELECT Job.JobId,Job.Name FROM Job "
1011            "LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId) "
1012            "WHERE Client.Name IS NULL";
1013    if (verbose > 1) {
1014       printf("%s\n", query);
1015    }
1016    if (!make_id_list(query, &id_list)) {
1017       exit(1);
1018    }
1019    printf(_("Found %d orphaned Job records.\n"), id_list.num_ids);
1020    if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1021       for (int i=0; i < id_list.num_ids; i++) {
1022          char ed1[50];
1023          bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
1024                       "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
1025          if (!db_sql_query(db, buf, print_job_handler, NULL)) {
1026             printf("%s\n", db_strerror(db));
1027          }
1028       }
1029    }
1030    if (quit) {
1031       return;
1032    }
1033    if (fix && id_list.num_ids > 0) {
1034       printf(_("Deleting %d orphaned Job records.\n"), id_list.num_ids);
1035       delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1036       printf(_("Deleting JobMedia records of orphaned Job records.\n"));
1037       delete_id_list("DELETE FROM JobMedia WHERE JobId=%s", &id_list);
1038       printf(_("Deleting Log records of orphaned Job records.\n"));
1039       delete_id_list("DELETE FROM Log WHERE JobId=%s", &id_list);
1040    }
1041 }
1042
1043
1044 static void eliminate_admin_records()
1045 {
1046    const char *query;
1047
1048    printf(_("Checking for Admin Job entries.\n"));
1049    query = "SELECT Job.JobId FROM Job "
1050            "WHERE Job.Type='D'";
1051    if (verbose > 1) {
1052       printf("%s\n", query);
1053    }
1054    if (!make_id_list(query, &id_list)) {
1055       exit(1);
1056    }
1057    printf(_("Found %d Admin Job records.\n"), id_list.num_ids);
1058    if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1059       for (int i=0; i < id_list.num_ids; i++) {
1060          char ed1[50];
1061          bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
1062                       "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
1063          if (!db_sql_query(db, buf, print_job_handler, NULL)) {
1064             printf("%s\n", db_strerror(db));
1065          }
1066       }
1067    }
1068    if (quit) {
1069       return;
1070    }
1071    if (fix && id_list.num_ids > 0) {
1072       printf(_("Deleting %d Admin Job records.\n"), id_list.num_ids);
1073       delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1074    }
1075 }
1076
1077 static void eliminate_restore_records()
1078 {
1079    const char *query;
1080
1081    printf(_("Checking for Restore Job entries.\n"));
1082    query = "SELECT Job.JobId FROM Job "
1083            "WHERE Job.Type='R'";
1084    if (verbose > 1) {
1085       printf("%s\n", query);
1086    }
1087    if (!make_id_list(query, &id_list)) {
1088       exit(1);
1089    }
1090    printf(_("Found %d Restore Job records.\n"), id_list.num_ids);
1091    if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1092       for (int i=0; i < id_list.num_ids; i++) {
1093          char ed1[50];
1094          bsnprintf(buf, sizeof(buf), "SELECT JobId,Name,StartTime FROM Job "
1095                       "WHERE JobId=%s", edit_int64(id_list.Id[i], ed1));
1096          if (!db_sql_query(db, buf, print_job_handler, NULL)) {
1097             printf("%s\n", db_strerror(db));
1098          }
1099       }
1100    }
1101    if (quit) {
1102       return;
1103    }
1104    if (fix && id_list.num_ids > 0) {
1105       printf(_("Deleting %d Restore Job records.\n"), id_list.num_ids);
1106       delete_id_list("DELETE FROM Job WHERE JobId=%s", &id_list);
1107    }
1108 }
1109
1110
1111
1112
1113 static void repair_bad_filenames()
1114 {
1115    const char *query;
1116    int i;
1117
1118    printf(_("Checking for Filenames with a trailing slash\n"));
1119    query = "SELECT FilenameId,Name from Filename "
1120            "WHERE Name LIKE '%/'";
1121    if (verbose > 1) {
1122       printf("%s\n", query);
1123    }
1124    if (!make_id_list(query, &id_list)) {
1125       exit(1);
1126    }
1127    printf(_("Found %d bad Filename records.\n"), id_list.num_ids);
1128    if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1129       for (i=0; i < id_list.num_ids; i++) {
1130          char ed1[50];
1131          bsnprintf(buf, sizeof(buf),
1132             "SELECT Name FROM Filename WHERE FilenameId=%s", 
1133                 edit_int64(id_list.Id[i], ed1));
1134          if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1135             printf("%s\n", db_strerror(db));
1136          }
1137       }
1138    }
1139    if (quit) {
1140       return;
1141    }
1142    if (fix && id_list.num_ids > 0) {
1143       POOLMEM *name = get_pool_memory(PM_FNAME);
1144       char esc_name[5000];
1145       printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1146       for (i=0; i < id_list.num_ids; i++) {
1147          int len;
1148          char ed1[50];
1149          bsnprintf(buf, sizeof(buf),
1150             "SELECT Name FROM Filename WHERE FilenameId=%s", 
1151                edit_int64(id_list.Id[i], ed1));
1152          if (!db_sql_query(db, buf, get_name_handler, name)) {
1153             printf("%s\n", db_strerror(db));
1154          }
1155          /* Strip trailing slash(es) */
1156          for (len=strlen(name); len > 0 && IsPathSeparator(name[len-1]); len--)
1157             {  }
1158          if (len == 0) {
1159             len = 1;
1160             esc_name[0] = ' ';
1161             esc_name[1] = 0;
1162          } else {
1163             name[len-1] = 0;
1164             db_escape_string(NULL, db, esc_name, name, len);
1165          }
1166          bsnprintf(buf, sizeof(buf),
1167             "UPDATE Filename SET Name='%s' WHERE FilenameId=%s",
1168             esc_name, edit_int64(id_list.Id[i], ed1));
1169          if (verbose > 1) {
1170             printf("%s\n", buf);
1171          }
1172          db_sql_query(db, buf, NULL, NULL);
1173       }
1174    }
1175 }
1176
1177 static void repair_bad_paths()
1178 {
1179    const char *query;
1180    int i;
1181
1182    printf(_("Checking for Paths without a trailing slash\n"));
1183    query = "SELECT PathId,Path from Path "
1184            "WHERE Path NOT LIKE '%/'";
1185    if (verbose > 1) {
1186       printf("%s\n", query);
1187    }
1188    if (!make_id_list(query, &id_list)) {
1189       exit(1);
1190    }
1191    printf(_("Found %d bad Path records.\n"), id_list.num_ids);
1192    if (id_list.num_ids && verbose && yes_no(_("Print them? (yes/no): "))) {
1193       for (i=0; i < id_list.num_ids; i++) {
1194          char ed1[50];
1195          bsnprintf(buf, sizeof(buf),
1196             "SELECT Path FROM Path WHERE PathId=%s", edit_int64(id_list.Id[i], ed1));
1197          if (!db_sql_query(db, buf, print_name_handler, NULL)) {
1198             printf("%s\n", db_strerror(db));
1199          }
1200       }
1201    }
1202    if (quit) {
1203       return;
1204    }
1205    if (fix && id_list.num_ids > 0) {
1206       POOLMEM *name = get_pool_memory(PM_FNAME);
1207       char esc_name[5000];
1208       printf(_("Reparing %d bad Filename records.\n"), id_list.num_ids);
1209       for (i=0; i < id_list.num_ids; i++) {
1210          int len;
1211          char ed1[50];
1212          bsnprintf(buf, sizeof(buf),
1213             "SELECT Path FROM Path WHERE PathId=%s", edit_int64(id_list.Id[i], ed1));
1214          if (!db_sql_query(db, buf, get_name_handler, name)) {
1215             printf("%s\n", db_strerror(db));
1216          }
1217          /* Strip trailing blanks */
1218          for (len=strlen(name); len > 0 && name[len-1]==' '; len--) {
1219             name[len-1] = 0;
1220          }
1221          /* Add trailing slash */
1222          len = pm_strcat(&name, "/");
1223          db_escape_string(NULL, db, esc_name, name, len);
1224          bsnprintf(buf, sizeof(buf), "UPDATE Path SET Path='%s' WHERE PathId=%s",
1225             esc_name, edit_int64(id_list.Id[i], ed1));
1226          if (verbose > 1) {
1227             printf("%s\n", buf);
1228          }
1229          db_sql_query(db, buf, NULL, NULL);
1230       }
1231    }
1232 }
1233
1234
1235 /*
1236  * Gen next input command from the terminal
1237  */
1238 static char *get_cmd(const char *prompt)
1239 {
1240    static char cmd[1000];
1241
1242    printf("%s", prompt);
1243    if (fgets(cmd, sizeof(cmd), stdin) == NULL) {
1244       printf("\n");
1245       quit = true;
1246       return NULL;
1247    }
1248    strip_trailing_junk(cmd);
1249    return cmd;
1250 }
1251
1252 static bool yes_no(const char *prompt)
1253 {
1254    char *cmd;
1255    cmd = get_cmd(prompt);
1256    if (!cmd) {
1257       quit = true;
1258       return false;
1259    }
1260    return (strcasecmp(cmd, "yes") == 0) || (strcasecmp(cmd, _("yes")) == 0);
1261 }
1262
1263 bool python_set_prog(JCR*, char const*) { return false; }
1264
1265
1266 /*
1267  * The code below to add indexes is needed only for MySQL, and
1268  *  that to improve the performance.
1269  */
1270
1271 #ifdef HAVE_MYSQL
1272 #define MAXIDX          100
1273 typedef struct s_idx_list {
1274    char *key_name;
1275    int  count_key; /* how many times the index meets *key_name */
1276    int  count_col; /* how many times meets the desired column name */
1277 } IDX_LIST;
1278
1279 static IDX_LIST idx_list[MAXIDX];
1280
1281 /* 
1282  * Called here with each table index to be added to the list
1283  */
1284 static int check_idx_handler(void *ctx, int num_fields, char **row)
1285 {
1286    /* Table | Non_unique | Key_name | Seq_in_index | Column_name |...
1287     * File  |          0 | PRIMARY  |            1 | FileId      |... 
1288     */
1289    char *name, *key_name, *col_name;
1290    int i, len;
1291    int found = false;
1292    name = (char *)ctx;
1293    key_name = row[2];
1294    col_name = row[4];
1295    for(i = 0; (idx_list[i].key_name != NULL) && (i < MAXIDX); i++) {
1296       if (strcasecmp(idx_list[i].key_name, key_name) == 0 ) {
1297          idx_list[i].count_key++;
1298          found = true;
1299          if (strcasecmp(col_name, name) == 0) {
1300             idx_list[i].count_col++;
1301          }
1302          break;
1303       }
1304    }
1305    /* if the new Key_name, add it to the list */
1306    if (!found) {
1307       len = strlen(key_name) + 1;
1308       idx_list[i].key_name = (char *)malloc(len);
1309       bstrncpy(idx_list[i].key_name, key_name, len);
1310       idx_list[i].count_key = 1;
1311       if (strcasecmp(col_name, name) == 0) {
1312          idx_list[i].count_col = 1;
1313       } else {
1314          idx_list[i].count_col = 0;
1315       }
1316    }
1317    return 0;
1318 }
1319 #endif
1320
1321 /*
1322  * Return TRUE if "one column" index over *col_name exists
1323  */
1324 static bool check_idx(const char *col_name)
1325 {
1326 #ifdef HAVE_MYSQL
1327    int i;
1328    int found = false;
1329
1330    memset(&idx_list, 0, sizeof(idx_list));
1331    char *query = "SHOW INDEX FROM File";
1332    if (!db_sql_query(db, query, check_idx_handler, (void *)col_name)) {
1333       printf("%s\n", db_strerror(db));
1334    }
1335
1336    for(i = 0; (idx_list[i].key_name != NULL) && (i < MAXIDX) ; i++) {
1337       /* NOTE : if (idx_list[i].count_key > 1) then index idx_list[i].key_name is "multiple-column" index */
1338       if ((idx_list[i].count_key == 1) && (idx_list[i].count_col == 1)) {
1339          /* "one column" index over *col_name found */
1340          found = true;
1341       }
1342    }
1343    if (found) {
1344       if (verbose) {
1345          printf(_("Ok. Index over the %s column already exists and dbcheck will work faster.\n"), col_name);
1346       }
1347    } else {
1348       printf(_("Note. Index over the %s column not found, that can greatly slow down dbcheck.\n"), col_name);
1349    }
1350
1351    return found;
1352 #else
1353    return true;
1354 #endif
1355 }
1356
1357 /*
1358  * Create temporary one-column index
1359  */
1360 static bool create_tmp_idx(const char *idx_name, const char *table_name, 
1361                const char *col_name)
1362 {
1363    idx_tmp_name = NULL;
1364    printf(_("Create temporary index... This may take some time!\n"));
1365    bsnprintf(buf, sizeof(buf), "CREATE INDEX %s ON %s (%s)", idx_name, table_name, col_name); 
1366    if (verbose) {
1367       printf("%s\n", buf);
1368    }
1369    if (db_sql_query(db, buf, NULL, NULL)) {
1370       idx_tmp_name = idx_name;
1371       if (verbose) {
1372          printf(_("Temporary index created.\n"));
1373       }
1374    } else {
1375       printf("%s\n", db_strerror(db));
1376       return false;
1377    }
1378    return true;
1379 }
1380
1381 /*
1382  * Drop temporary index
1383  */
1384 static bool drop_tmp_idx(const char *idx_name, const char *table_name)
1385 {
1386    if (idx_tmp_name != NULL) {
1387       printf(_("Drop temporary index.\n"));
1388       bsnprintf(buf, sizeof(buf), "DROP INDEX %s ON %s", idx_name, table_name);
1389       if (verbose) {
1390          printf("%s\n", buf);
1391       }
1392       if (!db_sql_query(db, buf, NULL, NULL)) {
1393          printf("%s\n", db_strerror(db));
1394          return false;
1395       } else {
1396          if (verbose) {
1397             printf(_("Temporary index %s deleted.\n"), idx_tmp_name);
1398          }
1399       }
1400    }
1401    idx_tmp_name = NULL;
1402    return true;
1403 }