aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeal H. Walfield <[email protected]>2015-10-23 11:42:50 +0000
committerNeal H. Walfield <[email protected]>2015-10-23 15:38:16 +0000
commit297cf8660ce346638e42934d84d746768f8bb10a (patch)
tree9ac0e4c5a57f590c1f2ee3e0c2dfbe8e95c6db24
parentgpg: Return the DBs meta-handle rather than the sqlite3 handle. (diff)
downloadgnupg-297cf8660ce346638e42934d84d746768f8bb10a.tar.gz
gnupg-297cf8660ce346638e42934d84d746768f8bb10a.zip
gpg: Cache prepared SQL queries and open DB connections.
* g10/tofu.c: Include <stdarg.h>. (prepares_saved) [DEBUG_TOFU_CACHE]: New variable. (queries) [DEBUG_TOFU_CACHE]: New variable. (struct db): Add fields prevp, begin_transaction, end_transaction, rollback, record_binding_get_old_policy, record_binding_update, record_binding_update2, get_policy_select_policy_and_conflict, get_trust_bindings_with_this_email, get_trust_gather_other_user_ids, get_trust_gather_other_keys, register_already_seen, and register_insert. [DEBUG_TOFU_CACHE]: Add field hits. (STRINGIFY): New macro. (STRINGIFY2): New macro. (enum sqlite_arg_type): New enum. (sqlite3_stepx): New function. (combined_db): Remove variable. (opendb): Don't cache the combined db. (struct dbs): New struct. Update users to use this as the head of the local DB list rather than overloading struct db. (unlink_db): New function. (link_db): New function. (db_cache): New variable. (db_cache_count): New variable. (DB_CACHE_ENTRIES): Define. (getdb): If the dbs specific cache doesn't include the DB, look at DB_CACHE. Only if that also doesn't include the DB open the corresponding DB. (closedb): New function. (opendbs): Don't open the combined DB. Just return an initialized struct dbs. (closedbs): Don't close the dbs specific dbs. Attach them to the front of DB_CACHE. If DB_CACHE contains more than DB_CACHE_ENTRIES, close enough dbs from the end of the DB_CACHE list such that DB_CACHE only contains DB_CACHE_ENTRIES. Don't directly close the dbs, instead use the new closedb function. [DEBUG_TOFU_CACHE]: Print out some statistics. (record_binding): Use sqlite3_stepx instead of sqlite3_exec or sqlite3_exec_printf. (get_policy): Likewise. (get_trust): Likewise. (tofu_register): Likewise. -- Signed-off-by: Neal H. Walfield <[email protected]>
-rw-r--r--g10/tofu.c729
1 files changed, 518 insertions, 211 deletions
diff --git a/g10/tofu.c b/g10/tofu.c
index 69a2b3bbc..3b8ced043 100644
--- a/g10/tofu.c
+++ b/g10/tofu.c
@@ -27,6 +27,7 @@
#include <stdio.h>
#include <sys/stat.h>
#include <assert.h>
+#include <stdarg.h>
#include <sqlite3.h>
#include "gpg.h"
@@ -41,6 +42,12 @@
#include "tofu.h"
+#define DEBUG_TOFU_CACHE 0
+#if DEBUG_TOFU_CACHE
+static int prepares_saved;
+static int queries;
+#endif
+
/* The TOFU data can be saved in two different formats: either in a
single combined database (opt.tofu_db_format == TOFU_DB_FLAT) or in
a split file format (opt.tofu_db_format == TOFU_DB_SPLIT). In the
@@ -71,17 +78,43 @@ enum db_type
struct db
{
struct db *next;
+ struct db **prevp;
enum db_type type;
sqlite3 *db;
+ struct
+ {
+ sqlite3_stmt *begin_transaction;
+ sqlite3_stmt *end_transaction;
+ sqlite3_stmt *rollback;
+
+ sqlite3_stmt *record_binding_get_old_policy;
+ sqlite3_stmt *record_binding_update;
+ sqlite3_stmt *record_binding_update2;
+ sqlite3_stmt *get_policy_select_policy_and_conflict;
+ sqlite3_stmt *get_trust_bindings_with_this_email;
+ sqlite3_stmt *get_trust_gather_other_user_ids;
+ sqlite3_stmt *get_trust_gather_other_keys;
+ sqlite3_stmt *register_already_seen;
+ sqlite3_stmt *register_insert;
+ } s;
+
+
+#if DEBUG_TOFU_CACHE
+ int hits;
+#endif
+
/* If TYPE is DB_COMBINED, this is "". Otherwise, it is either the
fingerprint (type == DB_KEY) or the normalized email address
(type == DB_EMAIL). */
char name[1];
};
+#define STRINGIFY(s) STRINGIFY2(s)
+#define STRINGIFY2(s) #s
+
/* The grouping parameters when collecting signature statistics. */
/* If a message is signed a couple of hours in the future, just assume
@@ -187,6 +220,186 @@ sqlite3_exec_printf (sqlite3 *db,
return rc;
}
+enum sqlite_arg_type
+ {
+ SQLITE_ARG_END = 0xdead001,
+ SQLITE_ARG_INT,
+ SQLITE_ARG_LONG_LONG,
+ SQLITE_ARG_STRING
+ };
+
+static int
+sqlite3_stepx (sqlite3 *db,
+ sqlite3_stmt **stmtp,
+ int (*callback) (void*,int,char**,char**),
+ void *cookie,
+ char **errmsg,
+ const char *sql, ...)
+{
+ int rc;
+ int err = 0;
+ sqlite3_stmt *stmt = NULL;
+
+ va_list va;
+ int args;
+ enum sqlite_arg_type t;
+ int i;
+
+ int cols;
+ /* Names of the columns. We initialize this lazily to avoid the
+ overhead in case the query doesn't return any results. */
+ const char **azColName = 0;
+ int callback_initialized = 0;
+
+ const char **azVals = 0;
+
+ callback_initialized = 0;
+
+ if (stmtp && *stmtp)
+ {
+ stmt = *stmtp;
+
+ /* Make sure this statement is associated with the supplied db. */
+ assert (db == sqlite3_db_handle (stmt));
+
+#if DEBUG_TOFU_CACHE
+ prepares_saved ++;
+#endif
+ }
+ else
+ {
+ rc = sqlite3_prepare_v2 (db, sql, -1, &stmt, NULL);
+ if (rc)
+ log_fatal ("failed to prepare SQL: %s", sql);
+
+ if (stmtp)
+ *stmtp = stmt;
+ }
+
+#if DEBUG_TOFU_CACHE
+ queries ++;
+#endif
+
+ args = sqlite3_bind_parameter_count (stmt);
+ va_start (va, sql);
+ if (args)
+ {
+ for (i = 1; i <= args; i ++)
+ {
+ t = va_arg (va, enum sqlite_arg_type);
+ switch (t)
+ {
+ case SQLITE_ARG_INT:
+ {
+ int value = va_arg (va, int);
+ err = sqlite3_bind_int (stmt, i, value);
+ break;
+ }
+ case SQLITE_ARG_LONG_LONG:
+ {
+ long long value = va_arg (va, long long);
+ err = sqlite3_bind_int64 (stmt, i, value);
+ break;
+ }
+ case SQLITE_ARG_STRING:
+ {
+ char *text = va_arg (va, char *);
+ err = sqlite3_bind_text (stmt, i, text, -1, SQLITE_STATIC);
+ break;
+ }
+ default:
+ /* Internal error. Likely corruption. */
+ log_fatal ("Bad value for parameter type %d.\n", t);
+ }
+
+ if (err)
+ {
+ log_fatal ("Error binding parameter %d\n", i);
+ goto out;
+ }
+ }
+
+ }
+ t = va_arg (va, enum sqlite_arg_type);
+ assert (t == SQLITE_ARG_END);
+ va_end (va);
+
+ for (;;)
+ {
+ rc = sqlite3_step (stmt);
+
+ if (rc != SQLITE_ROW)
+ /* No more data (SQLITE_DONE) or an error occured. */
+ break;
+
+ if (! callback)
+ continue;
+
+ if (! callback_initialized)
+ {
+ cols = sqlite3_column_count (stmt);
+ azColName = xmalloc (2 * cols * sizeof (const char *) + 1);
+
+ for (i = 0; i < cols; i ++)
+ azColName[i] = sqlite3_column_name (stmt, i);
+
+ callback_initialized = 1;
+ }
+
+ azVals = &azColName[cols];
+ for (i = 0; i < cols; i ++)
+ {
+ azVals[i] = sqlite3_column_text (stmt, i);
+ if (! azVals[i] && sqlite3_column_type (stmt, i) != SQLITE_NULL)
+ /* Out of memory. */
+ {
+ err = SQLITE_NOMEM;
+ break;
+ }
+ }
+
+ if (callback (cookie, cols, (char **) azVals, (char **) azColName))
+ /* A non-zero result means to abort. */
+ {
+ err = SQLITE_ABORT;
+ break;
+ }
+ }
+
+ out:
+ xfree (azColName);
+
+ if (stmtp)
+ rc = sqlite3_reset (stmt);
+ else
+ rc = sqlite3_finalize (stmt);
+ if (rc == SQLITE_OK && err)
+ /* Local error. */
+ {
+ rc = err;
+ if (errmsg)
+ {
+ const char *e = sqlite3_errstr (err);
+ size_t l = strlen (e) + 1;
+ *errmsg = sqlite3_malloc (l);
+ if (! *errmsg)
+ log_fatal ("Out of memory.\n");
+ memcpy (*errmsg, e, l);
+ }
+ }
+ else if (rc != SQLITE_OK && errmsg)
+ /* Error reported by sqlite. */
+ {
+ const char * e = sqlite3_errmsg (db);
+ size_t l = strlen (e) + 1;
+ *errmsg = sqlite3_malloc (l);
+ if (! *errmsg)
+ log_fatal ("Out of memory.\n");
+ memcpy (*errmsg, e, l);
+ }
+
+ return rc;
+}
/* Collect results of a select count (*) ...; style query. Aborts if
the argument is not a valid integer (or real of the form X.0). */
@@ -451,8 +664,6 @@ initdb (sqlite3 *db, enum db_type type)
}
}
-static sqlite3 *combined_db;
-
/* Open and initialize a low-level TOFU database. Returns NULL on
failure. This function should not normally be directly called to
get a database handle. Instead, use getdb(). */
@@ -468,9 +679,6 @@ opendb (char *filename, enum db_type type)
assert (! filename);
assert (type == DB_COMBINED);
- if (combined_db)
- return combined_db;
-
filename = make_filename (opt.homedir, "tofu.db", NULL);
filename_free = 1;
}
@@ -502,12 +710,36 @@ opendb (char *filename, enum db_type type)
db = NULL;
}
- if (opt.tofu_db_format == TOFU_DB_FLAT)
- combined_db = db;
-
return db;
}
+struct dbs
+{
+ struct db *db;
+};
+
+static void
+unlink_db (struct db *db)
+{
+ *db->prevp = db->next;
+ if (db->next)
+ db->next->prevp = db->prevp;
+}
+
+static void
+link_db (struct db **head, struct db *db)
+{
+ db->next = *head;
+ if (db->next)
+ db->next->prevp = &db->next;
+ db->prevp = head;
+ *head = db;
+}
+
+static struct db *db_cache;
+static int db_cache_count;
+#define DB_CACHE_ENTRIES 16
+
/* Return a database handle. <type, name> describes the required
database. If there is a cached handle in DBS, that handle is
returned. Otherwise, the database is opened and cached in DBS.
@@ -517,108 +749,165 @@ opendb (char *filename, enum db_type type)
TYPE must be either DB_MAIL or DB_KEY. In the combined format, the
combined DB is always returned. */
static struct db *
-getdb (struct db *dbs, const char *name, enum db_type type)
+getdb (struct dbs *dbs, const char *name, enum db_type type)
{
struct db *t = NULL;
- sqlite3 *sqlitedb = NULL;
char *name_sanitized = NULL;
+ int count;
char *filename = NULL;
- int i;
+ int need_link = 1;
+ sqlite3 *sqlitedb = NULL;
+ assert (dbs);
assert (name);
assert (type == DB_EMAIL || type == DB_KEY);
- assert (dbs);
- /* The first entry is always for the combined DB. */
- assert (dbs->type == DB_COMBINED);
- assert (! dbs->name[0]);
-
if (opt.tofu_db_format == TOFU_DB_FLAT)
- /* When using the flat format, we only have a single combined
- DB. */
+ /* When using the flat format, we only have a single DB, the
+ combined DB. */
{
- assert (dbs->db);
- assert (! dbs->next);
- return dbs;
- }
- else
- /* When using the split format the first entry on the DB list is a
- dummy entry. */
- assert (! dbs->db);
+ if (dbs->db)
+ {
+ assert (dbs->db->type == DB_COMBINED);
+ assert (! dbs->db->next);
+ return dbs->db;
+ }
- /* We have the split format. */
+ type = DB_COMBINED;
+ }
- /* Only allow alpha-numeric characters in the filename. */
- name_sanitized = xstrdup (name);
- for (i = 0; name[i]; i ++)
+ if (type != DB_COMBINED)
+ /* Only allow alpha-numeric characters in the name. */
{
- char c = name_sanitized[i];
- if (! (('a' <= c && c <= 'z')
- || ('A' <= c && c <= 'Z')
- || ('0' <= c && c <= '9')))
- name_sanitized[i] = '_';
+ int i;
+
+ name_sanitized = xstrdup (name);
+ for (i = 0; name[i]; i ++)
+ {
+ char c = name_sanitized[i];
+ if (! (('a' <= c && c <= 'z')
+ || ('A' <= c && c <= 'Z')
+ || ('0' <= c && c <= '9')))
+ name_sanitized[i] = '_';
+ }
}
/* See if the DB is cached. */
- for (t = dbs->next; t; t = t->next)
- if (type == t->type && strcmp (t->name, name_sanitized) == 0)
- goto out;
+ for (t = dbs->db; t; t = t->next)
+ if (t->type == type
+ && (type == DB_COMBINED || strcmp (t->name, name_sanitized) == 0))
+ {
+ need_link = 0;
+ goto out;
+ }
- /* Open the DB. The filename has the form:
+ for (t = db_cache, count = 0; t; t = t->next, count ++)
+ if (type == t->type
+ && (type == DB_COMBINED || strcmp (t->name, name_sanitized) == 0))
+ {
+ unlink_db (t);
+ db_cache_count --;
+ goto out;
+ }
- tofu.d/TYPE/PREFIX/NAME.db
+ assert (db_cache_count == count);
- We use a short prefix to try to avoid having many files in a
- single directory. */
- {
- char *type_str = type == DB_EMAIL ? "email" : "key";
- char prefix[3] = { name_sanitized[0], name_sanitized[1], 0 };
- char *name_db;
+ if (type == DB_COMBINED)
+ filename = NULL;
+ else
+ {
+ /* Open the DB. The filename has the form:
- /* Make the directory. */
- if (gnupg_mkdir_p (opt.homedir, "tofu.d", type_str, prefix, NULL) != 0)
+ tofu.d/TYPE/PREFIX/NAME.db
+
+ We use a short prefix to try to avoid having many files in a
+ single directory. */
{
- log_error (_("unable to create directory %s/%s/%s/%s"),
- opt.homedir, "tofu.d", type_str, prefix);
- goto out;
+ char *type_str = type == DB_EMAIL ? "email" : "key";
+ char prefix[3] = { name_sanitized[0], name_sanitized[1], 0 };
+ char *name_db;
+
+ /* Make the directory. */
+ if (gnupg_mkdir_p (opt.homedir, "tofu.d", type_str, prefix, NULL) != 0)
+ {
+ log_error (_("unable to create directory %s/%s/%s/%s"),
+ opt.homedir, "tofu.d", type_str, prefix);
+ goto out;
+ }
+
+ name_db = xstrconcat (name_sanitized, ".db", NULL);
+ filename = make_filename
+ (opt.homedir, "tofu.d", type_str, prefix, name_db, NULL);
+ xfree (name_db);
}
-
- name_db = xstrconcat (name_sanitized, ".db", NULL);
- filename = make_filename
- (opt.homedir, "tofu.d", type_str, prefix, name_db, NULL);
- xfree (name_db);
- }
+ }
sqlitedb = opendb (filename, type);
if (! sqlitedb)
goto out;
- t = xmalloc (sizeof (struct db) + strlen (name_sanitized));
+ t = xmalloc_clear (sizeof (struct db)
+ + (name_sanitized ? strlen (name_sanitized) : 0));
t->type = type;
t->db = sqlitedb;
- strcpy (t->name, name_sanitized);
-
- /* Insert it immediately after the first element. */
- t->next = dbs->next;
- dbs->next = t;
+ if (name_sanitized)
+ strcpy (t->name, name_sanitized);
out:
+ if (t && need_link)
+ link_db (&dbs->db, t);
+
+#if DEBUG_TOFU_CACHE
+ if (t)
+ t->hits ++;
+#endif
+
xfree (filename);
xfree (name_sanitized);
-
- if (! t)
- return NULL;
return t;
}
+static void
+closedb (struct db *db)
+{
+ sqlite3_stmt **statements;
+
+ if (opt.tofu_db_format == TOFU_DB_FLAT)
+ /* If we are using the flat format, then there is only ever the
+ combined DB. */
+ assert (! db->next);
+
+ if (db->type == DB_COMBINED)
+ {
+ assert (opt.tofu_db_format == TOFU_DB_FLAT);
+ assert (! db->name[0]);
+ }
+ else
+ {
+ assert (opt.tofu_db_format == TOFU_DB_SPLIT);
+ assert (db->type != DB_COMBINED);
+ assert (db->name[0]);
+ }
+
+ for (statements = &db->s.begin_transaction;
+ (void *) statements < (void *) &(&db->s)[1];
+ statements ++)
+ sqlite3_finalize (*statements);
+
+ sqlite3_close (db->db);
+
+#if DEBUG_TOFU_CACHE
+ log_debug ("Freeing db. Used %d times.\n", db->hits);
+#endif
+
+ xfree (db);
+}
+
/* Create a new DB meta-handle. Returns NULL on error. */
-static struct db *
+static struct dbs *
opendbs (void)
{
- sqlite3 *db = NULL;
- struct db *dbs;
-
if (opt.tofu_db_format == TOFU_DB_AUTO)
{
char *filename = make_filename (opt.homedir, "tofu.db", NULL);
@@ -679,88 +968,63 @@ opendbs (void)
}
}
- if (opt.tofu_db_format == TOFU_DB_FLAT)
- {
- db = opendb (NULL, DB_COMBINED);
- if (! db)
- return NULL;
- }
- else
- {
- /* Create a dummy entry so that we have a handle. */
- }
-
- dbs = xmalloc_clear (sizeof (*dbs));
- dbs->db = db;
- dbs->type = DB_COMBINED;
-
- return dbs;
+ return xmalloc_clear (sizeof (struct dbs));
}
/* Release all of the resources associated with a DB meta-handle. */
static void
-closedbs (struct db *dbs)
+closedbs (struct dbs *dbs)
{
- struct db *db;
- struct db *n;
-
- /* The first entry is always the combined DB. */
- assert (dbs->type == DB_COMBINED);
- if (opt.tofu_db_format == TOFU_DB_FLAT)
+ if (dbs->db)
{
- /* If we are using the flat format, then there is only ever the
- combined DB. */
- assert (! dbs->next);
- assert (dbs->db);
- assert (dbs->db == combined_db);
- }
- else
- /* In the split format, the combined record is just a place holder
- so that we have a stable handle. */
- assert (! dbs->db);
+ struct db *old_head = db_cache;
+ struct db *db;
+ int count;
- for (db = dbs; db; db = n)
- {
- n = db->next;
+ /* Find the last DB. */
+ for (db = dbs->db, count = 1; db->next; db = db->next, count ++)
+ ;
- if (combined_db && db->db == combined_db)
- {
- assert (opt.tofu_db_format == TOFU_DB_FLAT);
- assert (dbs == db);
- assert (db->type == DB_COMBINED);
- assert (! db->name[0]);
- }
- else if (db->db)
- /* Not the dummy entry. */
- {
- if (dbs == db)
- /* The first entry. */
- {
- assert (opt.tofu_db_format == TOFU_DB_FLAT);
- assert (db->type == DB_COMBINED);
- assert (! db->name[0]);
- }
- else
- /* Not the first entry. */
- {
- assert (opt.tofu_db_format == TOFU_DB_SPLIT);
- assert (db->type != DB_COMBINED);
- assert (db->name[0]);
- }
+ /* Join the two lists. */
+ db->next = db_cache;
+ if (db_cache)
+ db_cache->prevp = &db->next;
- sqlite3_close (db->db);
- }
- else
- /* The dummy entry. */
- {
- assert (opt.tofu_db_format == TOFU_DB_SPLIT);
- assert (dbs == db);
- assert (db->type == DB_COMBINED);
- assert (! db->name[0]);
- }
+ /* Update the (new) first element. */
+ db_cache = dbs->db;
+ dbs->db->prevp = &db_cache;
+
+ db_cache_count += count;
+
+ /* Make sure that we don't have too many DBs on DB_CACHE. If
+ so, free some. */
+ if (db_cache_count > DB_CACHE_ENTRIES)
+ {
+ /* We need to find the (DB_CACHE_ENTRIES + 1)th entry. It
+ is easy to skip the first COUNT entries since we still
+ have a handle on the old head. */
+ int skip = DB_CACHE_ENTRIES - count;
+ while (-- skip > 0)
+ old_head = old_head->next;
- xfree (db);
+ *old_head->prevp = NULL;
+
+ while (old_head)
+ {
+ db = old_head->next;
+ closedb (old_head);
+ old_head = db;
+ db_cache_count --;
+ }
+ }
}
+
+ xfree (dbs);
+
+#if DEBUG_TOFU_CACHE
+ log_debug ("Queries: %d (prepares saved: %d)\n",
+ queries, prepares_saved);
+#endif
}
@@ -790,7 +1054,7 @@ get_single_long_cb (void *cookie, int argc, char **argv, char **azColName)
If SHOW_OLD is set, the binding's old policy is displayed. */
static gpg_error_t
-record_binding (struct db *dbs, const char *fingerprint, const char *email,
+record_binding (struct dbs *dbs, const char *fingerprint, const char *email,
const char *user_id, enum tofu_policy policy, int show_old)
{
struct db *db_email = NULL, *db_key = NULL;
@@ -820,7 +1084,9 @@ record_binding (struct db *dbs, const char *fingerprint, const char *email,
if (! db_key)
return gpg_error (GPG_ERR_GENERAL);
- rc = sqlite3_exec (db_email->db, "begin transaction;", NULL, NULL, &err);
+ rc = sqlite3_stepx (db_email->db, &db_email->s.begin_transaction,
+ NULL, NULL, &err,
+ "begin transaction;", SQLITE_ARG_END);
if (rc)
{
log_error (_("error beginning transaction on TOFU %s database: %s\n"),
@@ -829,7 +1095,9 @@ record_binding (struct db *dbs, const char *fingerprint, const char *email,
return gpg_error (GPG_ERR_GENERAL);
}
- rc = sqlite3_exec (db_key->db, "begin transaction;", NULL, NULL, &err);
+ rc = sqlite3_stepx (db_key->db, &db_key->s.begin_transaction,
+ NULL, NULL, &err,
+ "begin transaction;", SQLITE_ARG_END);
if (rc)
{
log_error (_("error beginning transaction on TOFU %s database: %s\n"),
@@ -844,10 +1112,12 @@ record_binding (struct db *dbs, const char *fingerprint, const char *email,
purposes, there is no need to start a transaction or to die if
there is a failure. */
{
- rc = sqlite3_exec_printf
- (db_email->db, get_single_long_cb, &policy_old, &err,
- "select policy from bindings where fingerprint = %Q and email = %Q",
- fingerprint, email);
+ rc = sqlite3_stepx
+ (db_email->db, &db_email->s.record_binding_get_old_policy,
+ get_single_long_cb, &policy_old, &err,
+ "select policy from bindings where fingerprint = ? and email = ?",
+ SQLITE_ARG_STRING, fingerprint, SQLITE_ARG_STRING, email,
+ SQLITE_ARG_END);
if (rc)
{
log_debug ("TOFU: Error reading from binding database"
@@ -875,17 +1145,20 @@ record_binding (struct db *dbs, const char *fingerprint, const char *email,
/* Nothing to do. */
goto out;
- rc = sqlite3_exec_printf
- (db_email->db, NULL, NULL, &err,
+ rc = sqlite3_stepx
+ (db_email->db, &db_email->s.record_binding_update, NULL, NULL, &err,
"insert or replace into bindings\n"
" (oid, fingerprint, email, user_id, time, policy)\n"
" values (\n"
/* If we don't explicitly reuse the OID, then SQLite will
reallocate a new one. We just need to search for the OID
based on the fingerprint and email since they are unique. */
- " (select oid from bindings where fingerprint = %Q and email = %Q),\n"
- " %Q, %Q, %Q, strftime('%%s','now'), %d);",
- fingerprint, email, fingerprint, email, user_id, policy);
+ " (select oid from bindings where fingerprint = ? and email = ?),\n"
+ " ?, ?, ?, strftime('%s','now'), ?);",
+ SQLITE_ARG_STRING, fingerprint, SQLITE_ARG_STRING, email,
+ SQLITE_ARG_STRING, fingerprint, SQLITE_ARG_STRING, email,
+ SQLITE_ARG_STRING, user_id, SQLITE_ARG_INT, (int) policy,
+ SQLITE_ARG_END);
if (rc)
{
log_error (_("error updating TOFU binding database"
@@ -901,17 +1174,19 @@ record_binding (struct db *dbs, const char *fingerprint, const char *email,
{
assert (opt.tofu_db_format == TOFU_DB_SPLIT);
- rc = sqlite3_exec_printf
- (db_key->db, NULL, NULL, &err,
+ rc = sqlite3_stepx
+ (db_key->db, &db_key->s.record_binding_update2, NULL, NULL, &err,
"insert or replace into bindings\n"
" (oid, fingerprint, email, user_id)\n"
" values (\n"
/* If we don't explicitly reuse the OID, then SQLite will
reallocate a new one. We just need to search for the OID
based on the fingerprint and email since they are unique. */
- " (select oid from bindings where fingerprint = %Q and email = %Q),\n"
- " %Q, %Q, %Q);",
- fingerprint, email, fingerprint, email, user_id);
+ " (select oid from bindings where fingerprint = ? and email = ?),\n"
+ " ?, ?, ?);",
+ SQLITE_ARG_STRING, fingerprint, SQLITE_ARG_STRING, email,
+ SQLITE_ARG_STRING, fingerprint, SQLITE_ARG_STRING, email,
+ SQLITE_ARG_STRING, user_id, SQLITE_ARG_END);
if (rc)
{
log_error (_("error updating TOFU binding database"
@@ -930,8 +1205,14 @@ record_binding (struct db *dbs, const char *fingerprint, const char *email,
{
int rc2;
- rc2 = sqlite3_exec_printf (db_key->db, NULL, NULL, &err,
- rc ? "rollback;" : "end transaction;");
+ if (rc)
+ rc2 = sqlite3_stepx (db_key->db, &db_key->s.rollback,
+ NULL, NULL, &err,
+ "rollback;", SQLITE_ARG_END);
+ else
+ rc2 = sqlite3_stepx (db_key->db, &db_key->s.end_transaction,
+ NULL, NULL, &err,
+ "end transaction;", SQLITE_ARG_END);
if (rc2)
{
log_error (_("error ending transaction on TOFU database: %s\n"),
@@ -940,8 +1221,14 @@ record_binding (struct db *dbs, const char *fingerprint, const char *email,
}
out_revert_one:
- rc2 = sqlite3_exec_printf (db_email->db, NULL, NULL, &err,
- rc ? "rollback;" : "end transaction;");
+ if (rc)
+ rc2 = sqlite3_stepx (db_email->db, &db_email->s.rollback,
+ NULL, NULL, &err,
+ "rollback;", SQLITE_ARG_END);
+ else
+ rc2 = sqlite3_stepx (db_email->db, &db_email->s.end_transaction,
+ NULL, NULL, &err,
+ "end transaction;", SQLITE_ARG_END);
if (rc2)
{
log_error (_("error ending transaction on TOFU database: %s\n"),
@@ -1154,7 +1441,7 @@ time_ago_unit (signed long t)
if CONFLICT is not NULL. Returns _tofu_GET_POLICY_ERROR if an error
occurs. */
static enum tofu_policy
-get_policy (struct db *dbs, const char *fingerprint, const char *email,
+get_policy (struct dbs *dbs, const char *fingerprint, const char *email,
char **conflict)
{
struct db *db;
@@ -1172,11 +1459,13 @@ get_policy (struct db *dbs, const char *fingerprint, const char *email,
(TOFU_POLICY_NONE cannot appear in the DB. Thus, if POLICY is
still TOFU_POLICY_NONE after executing the query, then the
result set was empty.) */
- rc = sqlite3_exec_printf
- (db->db, strings_collect_cb, &strlist, &err,
- "select policy, conflict from bindings\n"
- " where fingerprint = %Q and email = %Q",
- fingerprint, email);
+ rc = sqlite3_stepx (db->db, &db->s.get_policy_select_policy_and_conflict,
+ strings_collect_cb, &strlist, &err,
+ "select policy, conflict from bindings\n"
+ " where fingerprint = ? and email = ?",
+ SQLITE_ARG_STRING, fingerprint,
+ SQLITE_ARG_STRING, email,
+ SQLITE_ARG_END);
if (rc)
{
log_error (_("error reading from TOFU database"
@@ -1264,7 +1553,7 @@ get_policy (struct db *dbs, const char *fingerprint, const char *email,
conflicting binding's policy to TOFU_POLICY_ASK. In either case,
we return TRUST_UNDEFINED. */
static enum tofu_policy
-get_trust (struct db *dbs, const char *fingerprint, const char *email,
+get_trust (struct dbs *dbs, const char *fingerprint, const char *email,
const char *user_id, int may_ask)
{
struct db *db;
@@ -1408,16 +1697,17 @@ get_trust (struct db *dbs, const char *fingerprint, const char *email,
(need to check for a conflict).
*/
- /* Look for conflicts. This is need in all 3 cases.
+ /* Look for conflicts. This is needed in all 3 cases.
Get the fingerprints of any bindings that share the email
address. Note: if the binding in question is in the DB, it will
also be returned. Thus, if the result set is empty, then this is
a new binding. */
- rc = sqlite3_exec_printf
- (db->db, strings_collect_cb, &bindings_with_this_email, &err,
- "select distinct fingerprint from bindings where email = %Q;",
- email);
+ rc = sqlite3_stepx
+ (db->db, &db->s.get_trust_bindings_with_this_email,
+ strings_collect_cb, &bindings_with_this_email, &err,
+ "select distinct fingerprint from bindings where email = ?;",
+ SQLITE_ARG_STRING, email, SQLITE_ARG_END);
if (rc)
{
log_error (_("error reading from TOFU database"
@@ -1556,11 +1846,13 @@ get_trust (struct db *dbs, const char *fingerprint, const char *email,
if (db_key)
{
- rc = sqlite3_exec_printf
- (db_key->db, strings_collect_cb, &other_user_ids, &err,
- "select user_id, %s from bindings where fingerprint = %Q;",
- opt.tofu_db_format == TOFU_DB_SPLIT ? "email" : "policy",
- fingerprint);
+ rc = sqlite3_stepx
+ (db_key->db, &db_key->s.get_trust_gather_other_user_ids,
+ strings_collect_cb, &other_user_ids, &err,
+ opt.tofu_db_format == TOFU_DB_SPLIT
+ ? "select user_id, email from bindings where fingerprint = ?;"
+ : "select user_id, policy from bindings where fingerprint = ?;",
+ SQLITE_ARG_STRING, fingerprint, SQLITE_ARG_END);
if (rc)
{
log_error (_("error gathering other user ids: %s.\n"), err);
@@ -1605,8 +1897,9 @@ get_trust (struct db *dbs, const char *fingerprint, const char *email,
/* XXX: When generating the statistics, do we want the time
embedded in the signature (column 'sig_time') or the time that
we first verified the signature (column 'time'). */
- rc = sqlite3_exec_printf
- (db->db, signature_stats_collect_cb, &stats, &err,
+ rc = sqlite3_stepx
+ (db->db, &db->s.get_trust_gather_other_keys,
+ signature_stats_collect_cb, &stats, &err,
"select fingerprint, policy, time_ago, count(*)\n"
" from (select bindings.*,\n"
" case\n"
@@ -1615,27 +1908,30 @@ get_trust (struct db *dbs, const char *fingerprint, const char *email,
small, medium or large units? (Note: whatever we do, we
keep the value in seconds. Then when we group, everything
that rounds to the same number of seconds is grouped.) */
- " when delta < -%d then -1\n"
- " when delta < %d then max(0, round(delta / %d) * %d)\n"
- " when delta < %d then round(delta / %d) * %d\n"
- " else round(delta / %d) * %d\n"
+ " when delta < -("STRINGIFY (TIME_AGO_FUTURE_IGNORE)") then -1\n"
+ " when delta < ("STRINGIFY (TIME_AGO_MEDIUM_THRESHOLD)")\n"
+ " then max(0,\n"
+ " round(delta / ("STRINGIFY (TIME_AGO_UNIT_SMALL)"))\n"
+ " * ("STRINGIFY (TIME_AGO_UNIT_SMALL)"))\n"
+ " when delta < ("STRINGIFY (TIME_AGO_LARGE_THRESHOLD)")\n"
+ " then round(delta / ("STRINGIFY (TIME_AGO_UNIT_MEDIUM)"))\n"
+ " * ("STRINGIFY (TIME_AGO_UNIT_MEDIUM)")\n"
+ " else round(delta / ("STRINGIFY (TIME_AGO_UNIT_LARGE)"))\n"
+ " * ("STRINGIFY (TIME_AGO_UNIT_LARGE)")\n"
" end time_ago,\n"
" delta time_ago_raw\n"
" from bindings\n"
" left join\n"
" (select *,\n"
- " cast(strftime('%%s','now') - sig_time as real) delta\n"
+ " cast(strftime('%s','now') - sig_time as real) delta\n"
" from signatures) ss\n"
" on ss.binding = bindings.oid)\n"
- " where email = %Q\n"
+ " where email = ?\n"
" group by fingerprint, time_ago\n"
/* Make sure the current key is first. */
- " order by fingerprint = %Q asc, fingerprint desc, time_ago desc;\n",
- TIME_AGO_FUTURE_IGNORE,
- TIME_AGO_MEDIUM_THRESHOLD, TIME_AGO_UNIT_SMALL, TIME_AGO_UNIT_SMALL,
- TIME_AGO_LARGE_THRESHOLD, TIME_AGO_UNIT_MEDIUM, TIME_AGO_UNIT_MEDIUM,
- TIME_AGO_UNIT_LARGE, TIME_AGO_UNIT_LARGE,
- email, fingerprint);
+ " order by fingerprint = ? asc, fingerprint desc, time_ago desc;\n",
+ SQLITE_ARG_STRING, email, SQLITE_ARG_STRING, fingerprint,
+ SQLITE_ARG_END);
if (rc)
{
strlist_t strlist_iter;
@@ -1825,7 +2121,7 @@ get_trust (struct db *dbs, const char *fingerprint, const char *email,
}
static void
-show_statistics (struct db *dbs, const char *fingerprint,
+show_statistics (struct dbs *dbs, const char *fingerprint,
const char *email, const char *user_id,
const char *sig_exclude)
{
@@ -2174,7 +2470,7 @@ tofu_register (const byte *fingerprint_bin, const char *user_id,
const byte *sig_digest_bin, int sig_digest_bin_len,
time_t sig_time, const char *origin, int may_ask)
{
- struct db *dbs;
+ struct dbs *dbs;
struct db *db;
char *fingerprint = NULL;
char *email = NULL;
@@ -2228,7 +2524,9 @@ tofu_register (const byte *fingerprint_bin, const char *user_id,
/* We do a query and then an insert. Make sure they are atomic
by wrapping them in a transaction. */
- rc = sqlite3_exec (db->db, "begin transaction;", NULL, NULL, &err);
+ rc = sqlite3_stepx (db->db, &db->s.begin_transaction,
+ NULL, NULL, &err, "begin transaction;",
+ SQLITE_ARG_END);
if (rc)
{
log_error (_("error beginning transaction on TOFU database: %s\n"), err);
@@ -2238,14 +2536,18 @@ tofu_register (const byte *fingerprint_bin, const char *user_id,
/* If we've already seen this signature before, then don't add
it again. */
- rc = sqlite3_exec_printf
- (db->db, get_single_unsigned_long_cb, &c, &err,
+ rc = sqlite3_stepx
+ (db->db, &db->s.register_already_seen,
+ get_single_unsigned_long_cb, &c, &err,
"select count (*)\n"
" from signatures left join bindings\n"
" on signatures.binding = bindings.oid\n"
- " where fingerprint = %Q and email = %Q and sig_time = 0x%lx\n"
- " and sig_digest = %Q",
- fingerprint, email, (unsigned long) sig_time, sig_digest);
+ " where fingerprint = ? and email = ? and sig_time = ?\n"
+ " and sig_digest = ?",
+ SQLITE_ARG_STRING, fingerprint, SQLITE_ARG_STRING, email,
+ SQLITE_ARG_LONG_LONG, (long long) sig_time,
+ SQLITE_ARG_STRING, sig_digest,
+ SQLITE_ARG_END);
if (rc)
{
log_error (_("error reading from signatures database"
@@ -2281,15 +2583,18 @@ tofu_register (const byte *fingerprint_bin, const char *user_id,
assert (c == 0);
- rc = sqlite3_exec_printf
- (db->db, NULL, NULL, &err,
+ rc = sqlite3_stepx
+ (db->db, &db->s.register_insert, NULL, NULL, &err,
"insert into signatures\n"
" (binding, sig_digest, origin, sig_time, time)\n"
" values\n"
" ((select oid from bindings\n"
- " where fingerprint = %Q and email = %Q),\n"
- " %Q, %Q, 0x%lx, strftime('%%s', 'now'));",
- fingerprint, email, sig_digest, origin, (unsigned long) sig_time);
+ " where fingerprint = ? and email = ?),\n"
+ " ?, ?, ?, strftime('%s', 'now'));",
+ SQLITE_ARG_STRING, fingerprint, SQLITE_ARG_STRING, email,
+ SQLITE_ARG_STRING, sig_digest, SQLITE_ARG_STRING, origin,
+ SQLITE_ARG_LONG_LONG, (long long) sig_time,
+ SQLITE_ARG_END);
if (rc)
{
log_error (_("error updating TOFU DB"
@@ -2302,9 +2607,11 @@ tofu_register (const byte *fingerprint_bin, const char *user_id,
/* It only matters whether we abort or commit the transaction
(so long as we do something) if we execute the insert. */
if (rc)
- rc = sqlite3_exec (db->db, "rollback;", NULL, NULL, &err);
+ rc = sqlite3_stepx (db->db, &db->s.rollback, NULL, NULL, &err,
+ "rollback;", SQLITE_ARG_END);
else
- rc = sqlite3_exec (db->db, "commit transaction;", NULL, NULL, &err);
+ rc = sqlite3_stepx (db->db, &db->s.end_transaction, NULL, NULL, &err,
+ "end transaction;", SQLITE_ARG_END);
if (rc)
{
log_error (_("error ending transaction on TOFU database: %s\n"), err);
@@ -2392,7 +2699,7 @@ int
tofu_get_validity (const byte *fingerprint_bin, const char *user_id,
int may_ask)
{
- struct db *dbs;
+ struct dbs *dbs;
char *fingerprint = NULL;
char *email = NULL;
int trust_level = TRUST_UNDEFINED;
@@ -2441,7 +2748,7 @@ tofu_get_validity (const byte *fingerprint_bin, const char *user_id,
gpg_error_t
tofu_set_policy (kbnode_t kb, enum tofu_policy policy)
{
- struct db *dbs;
+ struct dbs *dbs;
PKT_public_key *pk;
char fingerprint_bin[MAX_FINGERPRINT_LEN];
size_t fingerprint_bin_len = sizeof (fingerprint_bin);
@@ -2524,7 +2831,7 @@ gpg_error_t
tofu_get_policy (PKT_public_key *pk, PKT_user_id *user_id,
enum tofu_policy *policy)
{
- struct db *dbs;
+ struct dbs *dbs;
char fingerprint_bin[MAX_FINGERPRINT_LEN];
size_t fingerprint_bin_len = sizeof (fingerprint_bin);
char *fingerprint;