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