1 /**
2 * \file datastore_sqlite.c SQLite 3 database driver back-end
3 * \author Matthias Andree <matthias.andree@gmx.de>
4 * \date 2004, 2005
5 *
6 * This file handles a static table named "bogofilter" in a SQLite3
7 * database. The table has two "BLOB"-typed columns, key and value.
8 *
9 * GNU GENERAL PUBLIC LICENSE v2
10 */
11
12 #include "common.h"
13
14 #include <errno.h>
15 #include <sqlite3.h>
16
17 #include "datastore_db.h"
18
19 #include "error.h"
20 #include "rand_sleep.h"
21 #include "xmalloc.h"
22 #include "xstrdup.h"
23
24 /** Structure to hold database handle and associated data. */
25 struct dbhsqlite_t {
26 char *path; /**< directory to hold database */
27 char *name; /**< database file name */
28 sqlite3 *db; /**< pointer to SQLite3 handle */
29 sqlite3_stmt *stmt_select; /**< prepared SELECT statement for DB retrieval */
30 sqlite3_stmt *stmt_insert; /**< prepared INSERT OR REPLACE for DB update */
31 sqlite3_stmt *stmt_delete; /**< prepared DELETE statement */
32 bool created; /**< gets set by db_open if it created the database new */
33 bool swapped; /**< if endian swapped on disk vs. current host */
34 };
35
36 /** Convenience shortcut to avoid typing "struct dbh_t" */
37 typedef struct dbhsqlite_t dbh_t;
38
39 static const char *ENDIAN32 = ".ENDIAN32";
40
db_flush(void * unused)41 void db_flush(void *unused) { (void)unused; }
42
43 static int sql_txn_begin(void *vhandle);
44 static int sql_txn_abort(void *vhandle);
45 static int sql_txn_commit(void *vhandle);
46 static u_int32_t sql_pagesize(bfpath *bfp);
47 static ex_t sql_verify(bfpath *bfp);
48
49 /** The layout of the bogofilter table, formatted as SQL statement.
50 *
51 * The additional index, although making writes a bit slower, speeds up
52 * queries noticably as it improves locality of referenced data and
53 * reduces complexity of the retrieval of the value column.
54 */
55 #define LAYOUT \
56 "CREATE TABLE bogofilter (" \
57 " key BLOB PRIMARY KEY," \
58 " value BLOB);" \
59 "CREATE INDEX bfidx ON bogofilter(key,value);"
60 /*
61 * another experimental layout is as follows,
62 * but does not appear to make a lot of difference
63 * performance-wise (evaluation in other environments
64 * is required though):
65 *
66 #define LAYOUT \
67 "CREATE TABLE bogofilter (key BLOB, value BLOB); " \
68 "CREATE INDEX bfidx ON bogofilter(key,value);" \
69 "CREATE TRIGGER bfuniquekey BEFORE INSERT ON bogofilter " \
70 " FOR EACH ROW WHEN EXISTS(SELECT key FROM bogofilter WHERE (key=NEW.key) LIMIT 1) " \
71 " BEGIN UPDATE bogofilter SET value=NEW.value WHERE (key=NEW.key); SELECT RAISE(IGNORE); END;"
72 #endif
73 */
74
75 dsm_t dsm_sqlite = {
76 /* public -- used in datastore.c */
77 &sql_txn_begin,
78 &sql_txn_abort,
79 &sql_txn_commit,
80
81 /* private -- used in datastore_db_*.c */
82 NULL, /* dsm_env_init */
83 NULL, /* dsm_cleanup */
84 NULL, /* dsm_cleanup_lite */
85 NULL, /* dsm_get_env_dbe */
86 NULL, /* dsm_database_name */
87 NULL, /* dsm_recover_open */
88 NULL, /* dsm_auto_commit_flags*/
89 NULL, /* dsm_get_rmw_flag */
90 NULL, /* dsm_lock */
91 NULL, /* dsm_common_close */
92 NULL, /* dsm_sync */
93 NULL, /* dsm_log_flush */
94 &sql_pagesize,/* dsm_pagesize */
95 NULL, /* dsm_purgelogs */
96 NULL, /* dsm_checkpoint */
97 NULL, /* dsm_recover */
98 NULL, /* dsm_remove */
99 &sql_verify,/* dsm_verify */
100 NULL, /* dsm_list_logfiles */
101 NULL /* dsm_leafpages */
102 };
103
104 dsm_t *dsm = &dsm_sqlite;
105
106 /** The command to begin a regular transaction. */
107 #define BEGIN \
108 "BEGIN TRANSACTION;"
109
110 /* real functions */
111 /** Initialize database handle and return it.
112 * \returns non-NULL, as it exits with EX_ERROR in case of trouble. */
dbh_init(bfpath * bfp)113 static dbh_t *dbh_init(bfpath *bfp)
114 {
115 dbh_t *handle;
116
117 dsm = &dsm_sqlite;
118
119 handle = (dbh_t *)xmalloc(sizeof(dbh_t));
120 memset(handle, 0, sizeof(dbh_t));
121
122 handle->name = xstrdup(bfp->filepath);
123
124 return handle;
125 }
126
127 /** Free internal database handle \a dbh. */
free_dbh(dbh_t * dbh)128 static void free_dbh(dbh_t *dbh) {
129 if (!dbh)
130 return;
131 xfree(dbh->name);
132 xfree(dbh->path);
133 xfree(dbh);
134 }
135
136 /** Executes the SQL statement \a cmd on the database \a db and returns
137 * the sqlite3_exec return code. If the return code is nonzero, this
138 * routine will have printed an error message.
139 */
sqlexec(sqlite3 * db,const char * cmd)140 static int sqlexec(sqlite3 *db, const char *cmd) {
141 char *e = NULL;
142 int rc;
143
144 rc = sqlite3_exec(db, cmd, NULL, NULL, &e);
145 if (rc) {
146 print_error(__FILE__, __LINE__,
147 "Error executing \"%s\": %s (#%d)\n",
148 cmd, e ? e : "NULL", rc);
149 if (e)
150 sqlite3_free(e);
151 }
152 return rc;
153 }
154
155 /** Compile SQL statement \a cmd for database handle \a dbh, exiting on
156 * failure if \a bailout is true. */
sqlprep(dbh_t * dbh,const char * cmd,bool bailout)157 static sqlite3_stmt *sqlprep(dbh_t *dbh /** data base handle */,
158 const char *cmd /** sqlite command to compile */,
159 bool bailout /** exit on error? */) {
160 const char *tail; /* dummy */
161 sqlite3_stmt *ptr;
162 if (sqlite3_prepare_v2(dbh->db, cmd, strlen(cmd), &ptr, &tail) != SQLITE_OK) {
163 print_error(__FILE__, __LINE__, "cannot compile %s: %s\n", cmd, sqlite3_errmsg(dbh->db));
164 if (bailout)
165 exit(EX_ERROR);
166 return NULL;
167 }
168 return ptr;
169 }
170
171 /** Short trace handler function, passed to SQLite if debugging is
172 * enabled. */
db_trace(void * userdata,const char * log)173 static void db_trace(void *userdata /** unused */,
174 const char *log /** log message */) {
175 (void)userdata;
176 fprintf(dbgout, "SQLite[%ld]: %s\n", (long)getpid(), log);
177 }
178
179 /** Foreach function, we call \a hook for
180 * each (key, value) tuple in the database.
181 */
db_loop(sqlite3 * db,const char * cmd,db_foreach_t hook,void * userdata)182 static int db_loop(sqlite3 *db, /**< SQLite3 database handle */
183 const char *cmd, /**< SQL command to obtain data */
184 db_foreach_t hook, /**< if non-NULL, called for each value */
185 void *userdata /** this is passed to the \a hook */
186 ) {
187 const char *tail;
188 sqlite3_stmt *stmt;
189 int rc;
190 bool loop, found = false;
191 dbv_t key;
192 dbv_const_t val;
193
194 /* sqlite3_exec doesn't allow us to retrieve BLOBs */
195 rc = sqlite3_prepare_v2(db, cmd, strlen(cmd), &stmt, &tail);
196 if (rc) {
197 print_error(__FILE__, __LINE__,
198 "Error preparing \"%s\": %s (#%d)\n",
199 cmd, sqlite3_errmsg(db), rc);
200 sqlite3_finalize(stmt);
201 return rc;
202 }
203 loop = true;
204 while (loop) {
205 rc = sqlite3_step(stmt);
206 switch (rc) {
207 case SQLITE_ROW:
208 found = true;
209 if (hook != NULL)
210 {
211 key.leng = sqlite3_column_bytes(stmt, /* column */ 0);
212 key.data = xmalloc(key.leng);
213 memcpy(key.data, sqlite3_column_blob(stmt, 0), key.leng);
214
215 val.leng = sqlite3_column_bytes(stmt, /* column */ 1);
216 val.data = sqlite3_column_blob(stmt, 1);
217
218 /* skip ENDIAN32 token */
219 if (key.leng != strlen(ENDIAN32)
220 || memcmp(key.data, ENDIAN32, key.leng) != 0)
221 rc = hook(&key, &val, userdata);
222 else
223 rc = 0;
224
225 xfree(key.data);
226 if (rc) {
227 sqlite3_finalize(stmt);
228 return rc;
229 }
230 }
231 break;
232 case SQLITE_DONE:
233 loop = false;
234 break;
235 default:
236 print_error(__FILE__, __LINE__, "Error executing \"%s\": %s (#%d)\n",
237 cmd, sqlite3_errmsg(db), rc);
238 sqlite3_finalize(stmt);
239 return rc;
240 }
241 }
242 /* free resources */
243 sqlite3_finalize(stmt);
244 return found ? 0 : DS_NOTFOUND;
245 }
246
247 /** This busy handler just sleeps a while and retries */
busyhandler(void * dummy,int count)248 static int busyhandler(void *dummy, int count)
249 {
250 (void)dummy;
251 (void)count;
252 rand_sleep(1000, 1000000);
253 return 1;
254 }
255
check_sqlite_version(void)256 static void check_sqlite_version(void)
257 {
258 const unsigned int wmaj = 3, wmin = 6, wpl = 12; /* desired version of sqlite3 library */
259 unsigned int vmaj, vmin, vpl; /* actual version of sqlite3 library */
260 static int complained;
261 const char *v;
262
263 if (complained)
264 return;
265 complained = 1;
266 v = sqlite3_libversion();
267 sscanf(v, "%u.%u.%u", &vmaj, &vmin, &vpl);
268 if (vmaj > wmaj) return;
269 if (vmaj == wmaj && vmin > wmin) return;
270 if (vmaj == wmaj && vmin == wmin && vpl >= wpl) return;
271 if (!getenv("BF_USE_OLD_SQLITE"))
272 fprintf(stderr,
273 "\n"
274 "WARNING: please update sqlite to %d.%d.%d or newer.\n"
275 "\n", wmaj, wmin, wpl);
276 }
277
db_open(void * dummyenv,bfpath * bfp,dbmode_t mode)278 void *db_open(void *dummyenv, bfpath *bfp, dbmode_t mode)
279 {
280 int rc;
281 dbh_t *dbh;
282 dbv_t k, v;
283
284 (void)dummyenv;
285
286 check_sqlite_version();
287
288 dbh = dbh_init(bfp);
289
290 /* open database file */
291 if (DEBUG_DATABASE(1) || getenv("BF_DEBUG_DB")) {
292 fprintf(dbgout, "SQLite: db_open(%s)\n", dbh->name);
293 fflush(dbgout);
294 }
295 rc = sqlite3_open(dbh->name, &dbh->db);
296 if (rc) {
297 print_error(__FILE__, __LINE__, "Can't open database %s: %s\n",
298 dbh->name, sqlite3_errmsg(dbh->db));
299 goto barf;
300 }
301
302 /* request extended result codes for improved error reporting */
303 (void)sqlite3_extended_result_codes(dbh->db, true);
304
305 /* set trace mode */
306 if (DEBUG_DATABASE(1) || getenv("BF_DEBUG_DB"))
307 sqlite3_trace(dbh->db, db_trace, NULL);
308
309 /* set busy handler */
310 if (sqlite3_busy_handler(dbh->db, busyhandler, NULL)) {
311 print_error(__FILE__, __LINE__, "Can't set busy handler: %s\n",
312 sqlite3_errmsg(dbh->db));
313 goto barf;
314 }
315
316 /* check/set endianness marker and create table if needed */
317 if (mode != DS_READ) {
318 /* using IMMEDIATE or DEFERRED here locks up in t.lock3
319 * or t.bulkmode
320 * using EXCLUSIVE locks up in t.lock3 on MAC OSX
321 */
322 if (sqlexec(dbh->db, BEGIN)) goto barf;
323 /*
324 * trick: the sqlite_master table (see SQLite FAQ) is read-only
325 * and lists all tables, indexes etc. so we use it to check if
326 * the bogofilter table is already there, the error codes are
327 * too vague either way, for "no such table" and "table already
328 * exists" we always get SQLITE_ERROR, which we'll also get for
329 * syntax errors, such as "EXCLUSIVE" not supported on older
330 * versions :-(
331 */
332 rc = db_loop(dbh->db, "SELECT name FROM sqlite_master "
333 "WHERE type='table' AND name='bogofilter';",
334 NULL, NULL);
335 switch (rc) {
336 case 0:
337 if (sqlexec(dbh->db, "COMMIT;")) goto barf;
338 break;
339 case DS_NOTFOUND:
340 {
341 u_int32_t p[2] = { 0x01020304, 0x01020304 };
342
343 if (sqlexec(dbh->db, LAYOUT)) goto barf;
344
345 /* set endianness marker */
346 k.data = xstrdup(ENDIAN32);
347 k.leng = strlen((const char *)k.data);
348 v.data = p;
349 v.leng = sizeof(p);
350 rc = db_set_dbvalue(dbh, &k, &v);
351 xfree(k.data);
352 if (rc)
353 goto barf;
354
355 if (sqlexec(dbh->db, "COMMIT;")) goto barf;
356 dbh->created = true;
357 }
358 break;
359 default:
360 goto barf;
361 }
362 }
363
364 /*
365 * initialize common statements
366 * dbh->insert is not here as it's needed earlier,
367 * so it sets itself up lazily
368 */
369 dbh->stmt_select = sqlprep(dbh, "SELECT value FROM bogofilter WHERE key=? LIMIT 1;", false);
370 if (dbh->stmt_select == NULL)
371 {
372 fprintf(stderr,
373 "\nRemember to register some spam and ham messages before you\n"
374 "use bogofilter to evaluate mail for its probable spam status!\n\n");
375 exit(EX_ERROR);
376 }
377
378 dbh->stmt_delete = sqlprep(dbh, "DELETE FROM bogofilter WHERE(key = ?);", true);
379
380 /* check if byteswapped */
381 {
382 u_int32_t t, b[2];
383 int ee;
384
385 k.data = xstrdup(ENDIAN32);
386 k.leng = strlen((const char *)k.data);
387 v.data = b;
388 v.leng = sizeof(b);
389
390 ee = db_get_dbvalue(dbh, &k, &v);
391 xfree(k.data);
392 switch (ee) {
393 case 0: /* found endian marker token, read it */
394 if (v.leng < 4)
395 goto barf;
396 t = ((u_int32_t *)v.data)[0];
397 switch (t) {
398 case 0x01020304: /* same endian, "UNIX" */
399 dbh->swapped = false;
400 break;
401 case 0x04030201: /* swapped, "XINU" */
402 dbh->swapped = true;
403 break;
404 default: /* NUXI or IXUN or crap */
405 print_error(__FILE__, __LINE__,
406 "Unknown endianness on %s: %08x.\n",
407 dbh->name, ((u_int32_t *)v.data)[0]);
408 goto barf2;
409 }
410 break;
411 case DS_NOTFOUND: /* no marker token, assume not swapped */
412 dbh->swapped = false;
413 break;
414 default:
415 goto barf;
416 }
417 }
418
419 return dbh;
420 barf:
421 print_error(__FILE__, __LINE__, "Error on database %s: %s\n",
422 dbh->name, sqlite3_errmsg(dbh->db));
423 barf2:
424 db_close(dbh);
425 return NULL;
426 }
427
db_close(void * handle)428 void db_close(void *handle) {
429 int rc;
430 dbh_t *dbh = (dbh_t *)handle;
431 if (dbh->stmt_delete) sqlite3_finalize(dbh->stmt_delete);
432 if (dbh->stmt_insert) sqlite3_finalize(dbh->stmt_insert);
433 if (dbh->stmt_select) sqlite3_finalize(dbh->stmt_select);
434 rc = sqlite3_close(dbh->db);
435 if (rc) {
436 print_error(__FILE__, __LINE__, "Can't close database %s: %d",
437 dbh->name, rc);
438 exit(EX_ERROR);
439 }
440 free_dbh(dbh);
441 }
442
db_version_str(void)443 const char *db_version_str(void) {
444 static char buf[80];
445
446 if (!buf[0])
447 snprintf(buf, sizeof(buf), "SQLite %s", sqlite3_libversion());
448 return buf;
449 }
450
sql_txn_begin(void * vhandle)451 static int sql_txn_begin(void *vhandle) {
452 dbh_t *dbh = (dbh_t *)vhandle;
453 return sqlexec(dbh->db, BEGIN );
454 }
455
sql_txn_abort(void * vhandle)456 static int sql_txn_abort(void *vhandle) {
457 dbh_t *dbh = (dbh_t *)vhandle;
458 return sqlexec(dbh->db, "ROLLBACK;");
459 }
460
sql_txn_commit(void * vhandle)461 static int sql_txn_commit(void *vhandle) {
462 dbh_t *dbh = (dbh_t *)vhandle;
463 return sqlexec(dbh->db, "COMMIT;");
464 }
465
466 /** common code for db_delete, db_(get|set)_dbvalue.
467 * This works by setting variables in precompiled statements (see PREP,
468 * sqlite3_prepare, sqlite3_bind_*, sqlite3_reset) and avoids encoding
469 * binary data into SQL's hex representation as well as compiling the
470 * same SQL statement over and over again. */
sql_fastpath(dbh_t * dbh,const char * func,sqlite3_stmt * stmt,dbv_t * val,int retnotfound)471 static int sql_fastpath(
472 dbh_t *dbh, /**< database handle */
473 const char *func, /**< function name to report in errors */
474 sqlite3_stmt *stmt, /**< SQLite3 statement to execute/reset */
475 dbv_t *val, /**< OUT value from first row, NULL ok */
476 int retnotfound /** return value if no rows found */
477 )
478 {
479 int rc;
480 bool found = false;
481
482 while (1) {
483 rc = sqlite3_step(stmt);
484 switch (rc) {
485 case SQLITE_ROW: /* this is the only branch that loops */
486 if (val) {
487 int len = min(INT_MAX, val->leng);
488 val->leng = min(len, sqlite3_column_bytes(stmt, 0));
489 memcpy(val->data, sqlite3_column_blob(stmt, 0), val->leng);
490 }
491 found = 1;
492 break;
493 /* all other branches below return control to the caller */
494 case SQLITE_BUSY:
495 sqlite3_reset(stmt);
496 sql_txn_abort(dbh);
497 return DS_ABORT_RETRY;
498
499 case SQLITE_DONE:
500 sqlite3_reset(stmt);
501 return found ? 0 : retnotfound;
502
503 default:
504 print_error(__FILE__, __LINE__,
505 "%s: error executing statement on %s: %s (%d)\n",
506 func, dbh->name, sqlite3_errmsg(dbh->db), rc);
507 sqlite3_reset(stmt);
508 return rc;
509 }
510 }
511 }
512
db_delete(void * vhandle,const dbv_t * key)513 int db_delete(void *vhandle, const dbv_t *key) {
514 dbh_t *dbh = (dbh_t *)vhandle;
515
516 sqlite3_bind_blob(dbh->stmt_delete, 1, key->data, key->leng, SQLITE_STATIC);
517 return sql_fastpath(dbh, "db_delete", dbh->stmt_delete, NULL, 0);
518 }
519
db_set_dbvalue(void * vhandle,const dbv_t * key,const dbv_t * val)520 int db_set_dbvalue(void *vhandle, const dbv_t *key, const dbv_t *val) {
521 dbh_t *dbh = (dbh_t *)vhandle;
522
523 if (!dbh->stmt_insert)
524 dbh->stmt_insert = sqlprep(dbh, "INSERT OR REPLACE INTO bogofilter VALUES(?,?);", true);
525
526 sqlite3_bind_blob(dbh->stmt_insert, 1, key->data, key->leng, SQLITE_STATIC);
527 sqlite3_bind_blob(dbh->stmt_insert, 2, val->data, val->leng, SQLITE_STATIC);
528 return sql_fastpath(dbh, "db_set_dbvalue", dbh->stmt_insert, NULL, 0);
529 }
530
db_get_dbvalue(void * vhandle,const dbv_t * token,dbv_t * val)531 int db_get_dbvalue(void *vhandle, const dbv_t* token, /*@out@*/ dbv_t *val) {
532 dbh_t *dbh = (dbh_t *)vhandle;
533
534 sqlite3_bind_blob(dbh->stmt_select, 1, token->data, token->leng, SQLITE_STATIC);
535 return sql_fastpath(dbh, "db_get_dbvalue", dbh->stmt_select, val, DS_NOTFOUND);
536 }
537
db_foreach(void * vhandle,db_foreach_t hook,void * userdata)538 ex_t db_foreach(void *vhandle, db_foreach_t hook, void *userdata) {
539 dbh_t *dbh = (dbh_t *)vhandle;
540 const char *cmd = "SELECT key, value FROM bogofilter;";
541 return db_loop(dbh->db, cmd, hook, userdata) == 0 ? EX_OK : EX_ERROR;
542 }
543
db_str_err(int e)544 const char *db_str_err(int e) {
545 return e == 0 ? "no error" : "unknown condition (not yet implemented)";
546 }
547
db_created(void * vhandle)548 bool db_created(void *vhandle) {
549 dbh_t *dbh = (dbh_t *)vhandle;
550 return dbh->created;
551 }
552
db_is_swapped(void * vhandle)553 bool db_is_swapped(void *vhandle) {
554 dbh_t *dbh = (dbh_t *)vhandle;
555 return dbh->swapped;
556 }
557
pagesize_cb(void * ptr,int argc,char ** argv,char ** dummy)558 static int pagesize_cb(void *ptr, int argc, char **argv, char **dummy) {
559 u_int32_t *uptr = (u_int32_t *)ptr;
560
561 (void)dummy;
562
563 if (argc != 1)
564 return -1;
565 errno = 0;
566 *uptr = strtoul(argv[0], NULL, 0);
567 return errno;
568 }
569
sql_pagesize(bfpath * bfp)570 static u_int32_t sql_pagesize(bfpath *bfp)
571 {
572 dbh_t *dbh;
573 int rc;
574 u_int32_t size;
575
576 dbh = (dbh_t *)db_open(NULL, bfp, DS_READ);
577 if (!dbh)
578 return 0xffffffff;
579 rc = sqlite3_exec(dbh->db, "PRAGMA page_size;", pagesize_cb, &size, NULL);
580 if (rc != SQLITE_OK) {
581 return 0xffffffff;
582 }
583 db_close(dbh);
584 return size;
585 }
586
587 static int cb_first;
588
589 /** callback function for sql_verify,
590 * \returns 0 to exhaust the full list of error messages. */
cb_verify(void * errflag,int columns,char ** values,char ** names)591 static int cb_verify(void *errflag, int columns,
592 char * * values, char * * names)
593 {
594 int *e = (int *)errflag;
595
596 if (columns != 1) {
597 fprintf(stderr, "Strange: got row with more or less than one column.\n"
598 "Aborting verification.\n");
599 return 1;
600 }
601
602 if (cb_first != 1 || 0 != strcmp(values[0], "ok")) {
603 *e = 1; /* If we get any output other than a single row with the
604 "ok" text, there's something wrong with the database ->
605 set error flag and print the error. */
606 fprintf(stderr, "%s\n", values[0]);
607 }
608
609 (void)names;
610 cb_first = 0;
611 return 0;
612 }
613
614 /** Run database verification PRAGMA. Prints all error messages
615 * and returns EX_OK if none were delivered by sqlite3 or EX_ERROR in
616 * case of trouble. */
sql_verify(bfpath * bfp)617 static ex_t sql_verify(bfpath *bfp)
618 {
619 dbh_t *dbh;
620 int rc;
621 int faulty = 0;
622 char *errmsg = NULL;
623 const char stmt[] = "PRAGMA integrity_check;";
624
625 if (DEBUG_DATABASE(1) || getenv("BF_DEBUG_DB")) {
626 fprintf(dbgout, "SQLite: sql_verify(%s)\n", bfp->filename);
627 fflush(dbgout);
628 }
629 dbh = (dbh_t *)db_open(NULL, bfp, DS_READ);
630 if (!dbh)
631 return EX_ERROR;
632 cb_first = 1;
633 rc = sqlite3_exec(dbh->db, stmt, cb_verify, &faulty, &errmsg);
634 if (rc != SQLITE_OK) {
635 print_error(__FILE__, __LINE__, "Error while evaluating \"%s\": %s", stmt,
636 errmsg ? errmsg : "(unknown)");
637 if (errmsg) sqlite3_free(errmsg);
638 return EX_ERROR;
639 }
640 db_close(dbh);
641 if (faulty == 0) {
642 /* success */
643 if (verbose) printf("%s: OK\n", bfp->filename);
644 } else {
645 /* database faulty */
646 fprintf(stderr, "%s: Database integrity check failed.\n", bfp->filename);
647 }
648 return faulty ? EX_ERROR : EX_OK;
649 }
650