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