00001
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <config.h>
00024
00025 #include "xapian-check-brass.h"
00026 #include "xapian-check-chert.h"
00027 #include "xapian-check-flint.h"
00028
00029 #include "chert_check.h"
00030 #include "stringutils.h"
00031 #include "utils.h"
00032
00033 #include <xapian.h>
00034
00035 #include <stdexcept>
00036 #include <iostream>
00037
00038 using namespace std;
00039
00040 #define PROG_NAME "xapian-check"
00041 #define PROG_DESC "Check the consistency of a database or table"
00042
00043
00044
00045
00046
00047 static void show_usage() {
00048 cout << "Usage: "PROG_NAME" <database directory>|<path to btree and prefix> [[t][f][b][v][+]]\n\n"
00049 "If a whole database is checked, then additional cross-checks between\n"
00050 "the tables are performed.\n\n"
00051 "The btree(s) is/are always checked - control the output verbosity with:\n"
00052 " t = short tree printing\n"
00053 " f = full tree printing\n"
00054 " b = show bitmap\n"
00055 " v = show stats about B-tree (default)\n"
00056 " + = same as tbv\n"
00057 " e.g. "PROG_NAME" /var/lib/xapian/data/default\n"
00058 " "PROG_NAME" /var/lib/xapian/data/default/postlist fbv" << endl;
00059 }
00060
00061 static void
00062 reserve_doclens(vector<Xapian::termcount>& doclens, Xapian::docid last_docid)
00063 {
00064 if (last_docid >= 0x40000000ul / sizeof(Xapian::termcount)) {
00065
00066 cout << "Cross-checking document lengths between the postlist and "
00067 "termlist tables would use more than 1GB of memory, so "
00068 "skipping that check" << endl;
00069 return;
00070 }
00071 try {
00072 doclens.reserve(last_docid + 1);
00073 } catch (const std::bad_alloc &) {
00074
00075 cout << "Couldn't allocate enough memory for cross-checking document "
00076 "lengths between the postlist and termlist tables, so "
00077 "skipping that check" << endl;
00078 } catch (const std::length_error &) {
00079
00080 cout << "Couldn't allocate enough elements for cross-checking document "
00081 "lengths between the postlist and termlist tables, so "
00082 "skipping that check" << endl;
00083 }
00084 }
00085
00086 int
00087 main(int argc, char **argv)
00088 {
00089 if (argc > 1 && argv[1][0] == '-') {
00090 if (strcmp(argv[1], "--help") == 0) {
00091 cout << PROG_NAME" - "PROG_DESC"\n\n";
00092 show_usage();
00093 exit(0);
00094 }
00095 if (strcmp(argv[1], "--version") == 0) {
00096 cout << PROG_NAME" - "PACKAGE_STRING << endl;
00097 exit(0);
00098 }
00099 }
00100 if (argc < 2 || argc > 3) {
00101 show_usage();
00102 exit(1);
00103 }
00104
00105 int opts = 0;
00106 const char * opt_string = argv[2];
00107 if (!opt_string) opt_string = "v";
00108 for (const char *p = opt_string; *p; ++p) {
00109 switch (*p) {
00110 case 't': opts |= OPT_SHORT_TREE; break;
00111 case 'f': opts |= OPT_FULL_TREE; break;
00112 case 'b': opts |= OPT_SHOW_BITMAP; break;
00113 case 'v': opts |= OPT_SHOW_STATS; break;
00114 case '+':
00115 opts |= OPT_SHORT_TREE | OPT_SHOW_BITMAP | OPT_SHOW_STATS;
00116 break;
00117 default:
00118 cerr << "option " << opt_string << " unknown\n";
00119 cerr << "use t,f,b,v and/or + in the option string\n";
00120 exit(1);
00121 }
00122 }
00123
00124 try {
00125 vector<Xapian::termcount> doclens;
00126 size_t errors = 0;
00127 struct stat sb;
00128 string dir(argv[1]);
00129 if (stat((dir + "/iamflint").c_str(), &sb) == 0) {
00130 #ifndef XAPIAN_HAS_FLINT_BACKEND
00131 throw "Flint database support isn't enabled";
00132 #else
00133
00134 try {
00135 Xapian::Database db = Xapian::Flint::open(dir);
00136 Xapian::docid db_last_docid = db.get_lastdocid();
00137 reserve_doclens(doclens, db_last_docid);
00138 } catch (const Xapian::Error & e) {
00139
00140 cout << "Database couldn't be opened for reading: "
00141 << e.get_description()
00142 << "\nContinuing check anyway" << endl;
00143 ++errors;
00144 }
00145
00146
00147
00148 const char * tables[] = {
00149 "record", "termlist", "postlist", "position", "value",
00150 "spelling", "synonym"
00151 };
00152 for (const char **t = tables;
00153 t != tables + sizeof(tables)/sizeof(tables[0]); ++t) {
00154 string table(dir);
00155 table += '/';
00156 table += *t;
00157 cout << *t << ":\n";
00158 if (strcmp(*t, "position") == 0 ||
00159 strcmp(*t, "value") == 0 ||
00160 strcmp(*t, "spelling") == 0 ||
00161 strcmp(*t, "synonym") == 0) {
00162
00163 if (!file_exists(table + ".DB")) {
00164 cout << "Lazily created, and not yet used.\n" << endl;
00165 continue;
00166 }
00167 }
00168 errors += check_flint_table(*t, table, opts, doclens);
00169 }
00170 #endif
00171 } else if (stat((dir + "/iamchert").c_str(), &sb) == 0) {
00172 #ifndef XAPIAN_HAS_CHERT_BACKEND
00173 throw "Chert database support isn't enabled";
00174 #else
00175
00176
00177
00178 Xapian::docid db_last_docid = static_cast<Xapian::docid>(-1);
00179 try {
00180 Xapian::Database db = Xapian::Chert::open(dir);
00181 db_last_docid = db.get_lastdocid();
00182 } catch (const Xapian::Error & e) {
00183
00184 cout << "Database couldn't be opened for reading: "
00185 << e.get_description()
00186 << "\nContinuing check anyway" << endl;
00187 ++errors;
00188 }
00189 reserve_doclens(doclens, db_last_docid);
00190
00191
00192
00193 const char * tables[] = {
00194 "record", "termlist", "postlist", "position",
00195 "spelling", "synonym"
00196 };
00197 for (const char **t = tables;
00198 t != tables + sizeof(tables)/sizeof(tables[0]); ++t) {
00199 string table(dir);
00200 table += '/';
00201 table += *t;
00202 cout << *t << ":\n";
00203 if (strcmp(*t, "record") != 0 && strcmp(*t, "postlist") != 0) {
00204
00205 if (!file_exists(table + ".DB")) {
00206 if (strcmp(*t, "termlist") == 0) {
00207 cout << "Not present.\n";
00208 } else {
00209 cout << "Lazily created, and not yet used.\n";
00210 }
00211 cout << endl;
00212 continue;
00213 }
00214 }
00215 errors += check_chert_table(*t, table, opts, doclens,
00216 db_last_docid);
00217 }
00218 #endif
00219 } else if (stat((dir + "/iambrass").c_str(), &sb) == 0) {
00220 #ifndef XAPIAN_HAS_BRASS_BACKEND
00221 throw "Brass database support isn't enabled";
00222 #else
00223
00224
00225
00226 Xapian::docid db_last_docid = static_cast<Xapian::docid>(-1);
00227 try {
00228 Xapian::Database db = Xapian::Brass::open(dir);
00229 db_last_docid = db.get_lastdocid();
00230 } catch (const Xapian::Error & e) {
00231
00232 cout << "Database couldn't be opened for reading: "
00233 << e.get_description()
00234 << "\nContinuing check anyway" << endl;
00235 ++errors;
00236 }
00237 reserve_doclens(doclens, db_last_docid);
00238
00239
00240
00241 const char * tables[] = {
00242 "record", "termlist", "postlist", "position",
00243 "spelling", "synonym"
00244 };
00245 for (const char **t = tables;
00246 t != tables + sizeof(tables)/sizeof(tables[0]); ++t) {
00247 string table(dir);
00248 table += '/';
00249 table += *t;
00250 cout << *t << ":\n";
00251 if (strcmp(*t, "record") != 0 && strcmp(*t, "postlist") != 0) {
00252
00253 if (!file_exists(table + ".DB")) {
00254 if (strcmp(*t, "termlist") == 0) {
00255 cout << "Not present.\n";
00256 } else {
00257 cout << "Lazily created, and not yet used.\n";
00258 }
00259 cout << endl;
00260 continue;
00261 }
00262 }
00263 errors += check_brass_table(*t, table, opts, doclens,
00264 db_last_docid);
00265 }
00266 #endif
00267 } else {
00268 if (stat((dir + "/record_DB").c_str(), &sb) == 0) {
00269
00270 cerr << argv[0] << ": '" << dir << "' is a quartz database.\n"
00271 "Support for quartz was dropped in Xapian 1.1.0" << endl;
00272 exit(1);
00273 }
00274
00275
00276
00277 string filename = dir;
00278 if (endswith(filename, '.'))
00279 filename.resize(filename.size() - 1);
00280 else if (endswith(filename, ".DB"))
00281 filename.resize(filename.size() - 3);
00282
00283 size_t p = filename.find_last_of('/');
00284 #if defined __WIN32__ || defined __EMX__
00285 if (p == string::npos) p = 0;
00286 p = filename.find_last_of('\\', p);
00287 #endif
00288 if (p == string::npos) p = 0; else ++p;
00289
00290 string path(filename, 0, p);
00291
00292 string tablename;
00293 while (p != filename.size()) {
00294 tablename += tolower(static_cast<unsigned char>(filename[p++]));
00295 }
00296
00297
00298
00299 if (file_exists(path + "iamflint")) {
00300 #ifndef XAPIAN_HAS_FLINT_BACKEND
00301 throw "Flint database support isn't enabled";
00302 #else
00303 errors = check_flint_table(tablename.c_str(), filename, opts,
00304 doclens);
00305 #endif
00306 } else if (file_exists(path + "iambrass")) {
00307 #ifndef XAPIAN_HAS_BRASS_BACKEND
00308 throw "Brass database support isn't enabled";
00309 #else
00310
00311 Xapian::docid db_last_docid = static_cast<Xapian::docid>(-1);
00312 errors = check_brass_table(tablename.c_str(), filename, opts,
00313 doclens, db_last_docid);
00314 #endif
00315 } else {
00316 #ifndef XAPIAN_HAS_CHERT_BACKEND
00317 throw "Chert database support isn't enabled";
00318 #else
00319
00320 Xapian::docid db_last_docid = static_cast<Xapian::docid>(-1);
00321 errors = check_chert_table(tablename.c_str(), filename, opts,
00322 doclens, db_last_docid);
00323 #endif
00324 }
00325 }
00326 if (errors > 0) {
00327 cout << "Total errors found: " << errors << endl;
00328 exit(1);
00329 }
00330 cout << "No errors found" << endl;
00331 } catch (const char *error) {
00332 cerr << argv[0] << ": " << error << endl;
00333 exit(1);
00334 } catch (const Xapian::Error &error) {
00335 cerr << argv[0] << ": " << error.get_description() << endl;
00336 exit(1);
00337 } catch (...) {
00338 cerr << argv[0] << ": Unknown exception" << endl;
00339 exit(1);
00340 }
00341 }