#
#
# patch "ChangeLog"
# from [ad12888403b0f47c1c60ec773118196a185fd73f]
# to [5936f83fd67862dfb87abb0de129240ebe96b00d]
#
# patch "schema_migration.cc"
# from [d45098178370d1ddfc169c00853c714a695fd5c1]
# to [e65188fd724fa36f580292185541216ce83cc281]
#
============================================================
--- ChangeLog ad12888403b0f47c1c60ec773118196a185fd73f
+++ ChangeLog 5936f83fd67862dfb87abb0de129240ebe96b00d
@@ -1,5 +1,13 @@ 2007-01-13 Zack Weinberg
+ * schema_migration.cc (logged_sqlite3_prepare, report_sqlite_error)
+ (logged_sqlite3_exec_int, sqlite3_column_string): New functions.
+ (logged_sqlite3_exec, calculate_schema_id)
+ (migrate_client_to_external_privkeys): Rewrite using
+ logged_sqlite3_prepare and sqlite3_step.
+ (massage_sql_tokens, append_sql_stmt, extract_key): Delete.
+ (diagnose_unrecognized_schema): Use logged_sqlite3_exec_int.
+
* schema_migration.cc: Further prune includes and using-declarations.
(lowercase_facet, calculate_id): Delete.
(is_ws): Convert from class to predicate function.
============================================================
--- schema_migration.cc d45098178370d1ddfc169c00853c714a695fd5c1
+++ schema_migration.cc e65188fd724fa36f580292185541216ce83cc281
@@ -33,20 +33,101 @@ using std::vector;
// in this file is easier to write and understand if it speaks directly
// to sqlite.
-static int logged_sqlite3_exec(sqlite3* db,
- const char* sql,
- sqlite3_callback cb,
- void* data,
- char** errmsg)
+// we do not use sqlite3_exec because we want the better error handling that
+// sqlite3_prepare_v2 gives us.
+
+static sqlite3_stmt *
+logged_sqlite3_prepare(sqlite3 * sql, char const * cmd)
{
- L(FL("executing SQL '%s'") % sql);
- int res = sqlite3_exec(db, sql, cb, data, errmsg);
- L(FL("result: %i (%s)") % res % sqlite3_errmsg(db));
- if (errmsg && ((*errmsg)!=0))
- L(FL("errmsg: %s") % *errmsg);
+ sqlite3_stmt * stmt;
+ char const * after;
+
+ L(FL("executing SQL '%s'") % cmd);
+
+ int res = sqlite3_prepare_v2(sql, cmd, strlen(cmd), &stmt, &after);
+ if (res != SQLITE_OK)
+ {
+ L(FL("prepare failure: %s") % sqlite3_errmsg(sql));
+ return 0;
+ }
+ //I(stmt);
+ //I(after == 0);
+
+ return stmt;
+}
+
+// this function can only be used with statements that do not return rows.
+static int
+logged_sqlite3_exec(sqlite3 * sql, char const * cmd,
+ void* d1, void* d2, char **errmsg)
+{
+ I(d1 == 0);
+ I(d2 == 0);
+
+ sqlite3_stmt * stmt = logged_sqlite3_prepare(sql, cmd);
+ if (stmt == 0)
+ return SQLITE_ERROR;
+ //I(sqlite3_column_count(stmt) == 0);
+
+ int res = sqlite3_step(stmt);
+
+ //I(res != SQLITE_ROW);
+ if (res == SQLITE_DONE)
+ {
+ // callers expect OK
+ L(FL("success"));
+ res = SQLITE_OK;
+ }
+ else if (errmsg)
+ *errmsg = const_cast(sqlite3_errmsg(sql));
+
+ sqlite3_finalize(stmt);
return res;
}
+static void NORETURN
+report_sqlite_error(sqlite3 * sql)
+{
+ // note: useful error messages should be kept consistent with
+ // assert_sqlite3_ok() in database.cc
+ char const * errmsg = sqlite3_errmsg(sql);
+ char const * auxiliary_message = "";
+
+ L(FL("sqlite error: %s") % errmsg);
+
+ if (sqlite3_errcode(sql) == SQLITE_ERROR)
+ auxiliary_message
+ = _("make sure database and containing directory are writeable\n"
+ "and you have not run out of disk space");
+
+ logged_sqlite3_exec(sql, "ROLLBACK", 0, 0, 0);
+ E(false, F("sqlite error: %s\n%s") % errmsg % auxiliary_message);
+}
+
+// execute an sql statement and return the single integer value that it
+// should produce.
+static int
+logged_sqlite3_exec_int(sqlite3 * sql, char const * cmd)
+{
+ sqlite3_stmt * stmt = logged_sqlite3_prepare(sql, cmd);
+ if (stmt == 0)
+ report_sqlite_error(sql);
+ //I(sqlite3_column_count(stmt) == 1);
+
+ if (sqlite3_step(stmt) != SQLITE_ROW)
+ report_sqlite_error(sql);
+
+ int res = sqlite3_column_int(stmt, 0);
+
+ if (sqlite3_step(stmt) != SQLITE_DONE)
+ report_sqlite_error(sql);
+
+ L(FL("success"));
+
+ sqlite3_finalize(stmt);
+ return res;
+}
+
// sqlite3_value_text returns unsigned char const *, which is inconvenient
inline char const *
sqlite3_value_cstr(sqlite3_value * arg)
@@ -54,6 +135,13 @@ sqlite3_value_cstr(sqlite3_value * arg)
return reinterpret_cast(sqlite3_value_text(arg));
}
+// sqlite3_column_text also returns unsigned char const *
+inline string
+sqlite3_column_string(sqlite3_stmt * stmt, int col)
+{
+ return string(reinterpret_cast(sqlite3_column_text(stmt, col)));
+}
+
inline bool is_ws(char c)
{
return c == '\r' || c == '\n' || c == '\t' || c == ' ';
@@ -94,85 +182,49 @@ sqlite_sha1_fn(sqlite3_context *f, int n
sqlite3_result_text(f, sha().c_str(), sha().size(), SQLITE_TRANSIENT);
}
+void
+calculate_schema_id(sqlite3 *sql, string & ident)
+{
+ sqlite3_stmt * stmt
+ = logged_sqlite3_prepare(sql,
+ "SELECT sql FROM sqlite_master "
+ "WHERE (type = 'table' OR type = 'index') "
+ // filter out NULL sql statements, because
+ // those are auto-generated indices (for
+ // UNIQUE constraints, etc.).
+ "AND sql IS NOT NULL "
+ "AND name not like 'sqlite_stat%' "
+ "ORDER BY name");
+ if (!stmt)
+ report_sqlite_error(sql);
+ //I(sqlite3_column_count(stmt) == 1);
-static void
-massage_sql_tokens(string const & in,
- string & out)
-{
+ int res;
+ string schema;
using boost::char_separator;
typedef boost::tokenizer > tokenizer;
-
char_separator sep(" \r\n\t", "(),;");
- tokenizer tokens(in, sep);
- out.clear();
- for (tokenizer::iterator i = tokens.begin();
- i != tokens.end(); ++i)
- {
- if (i != tokens.begin())
- out += " ";
- out += *i;
- }
-}
-static int
-append_sql_stmt(void * vp,
- int ncols,
- char ** values,
- char ** colnames)
-{
- if (ncols != 1)
- return 1;
-
- if (vp == NULL)
- return 1;
-
- if (values == NULL)
- return 1;
-
- if (values[0] == NULL)
- return 1;
-
- string *str = reinterpret_cast(vp);
- str->append(values[0]);
- str->append("\n");
- return 0;
-}
-
-void
-calculate_schema_id(sqlite3 *sql, string & ident)
-{
- ident.clear();
- string tmp, tmp2;
- int res = logged_sqlite3_exec(sql,
- "SELECT sql FROM sqlite_master "
- "WHERE (type = 'table' OR type = 'index') "
- // filter out NULL sql statements, because
- // those are auto-generated indices (for
- // UNIQUE constraints, etc.).
- "AND sql IS NOT NULL "
- "AND name not like 'sqlite_stat%' "
- "ORDER BY name",
- &append_sql_stmt, &tmp, NULL);
- if (res != SQLITE_OK)
+ while ((res = sqlite3_step(stmt)) == SQLITE_ROW)
{
- // note: useful error messages should be kept consistent with
- // assert_sqlite3_ok() in database.cc
- string errmsg(sqlite3_errmsg(sql));
- L(FL("calculate_schema_id sqlite error: %d: %s") % res % errmsg);
- string auxiliary_message = "";
- if (res == SQLITE_ERROR)
+ string table_schema(sqlite3_column_string(stmt, 0));
+ tokenizer tokens(table_schema, sep);
+ for (tokenizer::iterator i = tokens.begin(); i != tokens.end(); i++)
{
- auxiliary_message += _("make sure database and containing directory are writeable\n"
- "and you have not run out of disk space");
-
+ if (schema.size() != 0)
+ schema += " ";
+ schema += *i;
}
- logged_sqlite3_exec(sql, "ROLLBACK", NULL, NULL, NULL);
- E(false, F("sqlite error: %s\n%s") % errmsg % auxiliary_message);
}
- massage_sql_tokens(tmp, tmp2);
+ if (res != SQLITE_DONE)
+ report_sqlite_error(sql);
+
+ sqlite3_finalize(stmt);
+ L(FL("success"));
+
hexenc tid;
- calculate_ident(data(tmp2), tid);
+ calculate_ident(data(schema), tid);
ident = tid();
}
@@ -701,15 +753,6 @@ migrate_client_to_add_indexes(sqlite3 *
return true;
}
-static int
-extract_key(void *ptr, int ncols, char **values, char **names)
-{
- // This is stupid. The cast should not be needed.
- map *out = (map*)ptr;
- I(ncols == 2);
- out->insert(make_pair(string(values[0]), string(values[1])));
- return 0;
-}
static bool
migrate_client_to_external_privkeys(sqlite3 * sql,
char ** errmsg,
@@ -719,19 +762,47 @@ migrate_client_to_external_privkeys(sqli
int res;
map pub, priv;
vector pairs;
+ sqlite3_stmt * stmt;
- res = logged_sqlite3_exec(sql,
- "SELECT id, keydata FROM private_keys;",
- &extract_key, &priv, errmsg);
- if (res != SQLITE_OK)
- return false;
+ stmt = logged_sqlite3_prepare(sql, "SELECT id, keydata FROM private_keys;");
+ if (stmt == 0)
+ {
+ *errmsg = const_cast(sqlite3_errmsg(sql));
+ return false;
+ }
+ //I(sqlite3_column_count(stmt) == 2);
- res = logged_sqlite3_exec(sql,
- "SELECT id, keydata FROM public_keys;",
- &extract_key, &pub, errmsg);
- if (res != SQLITE_OK)
- return false;
+ while ((res = sqlite3_step(stmt)) == SQLITE_ROW)
+ priv.insert(make_pair(sqlite3_column_string(stmt, 0),
+ sqlite3_column_string(stmt, 1)));
+ if (res != SQLITE_DONE)
+ {
+ *errmsg = const_cast(sqlite3_errmsg(sql));
+ return false;
+ }
+ sqlite3_finalize(stmt);
+
+ stmt = logged_sqlite3_prepare(sql, "SELECT id, keydata FROM public_keys;");
+ if (stmt == 0)
+ {
+ *errmsg = const_cast(sqlite3_errmsg(sql));
+ return false;
+ }
+ //I(sqlite3_column_count(stmt) == 2);
+
+ while ((res = sqlite3_step(stmt)) == SQLITE_ROW)
+ pub.insert(make_pair(sqlite3_column_string(stmt, 0),
+ sqlite3_column_string(stmt, 1)));
+
+ if (res != SQLITE_DONE)
+ {
+ *errmsg = const_cast(sqlite3_errmsg(sql));
+ return false;
+ }
+ sqlite3_finalize(stmt);
+
+
for (map::const_iterator i = priv.begin();
i != priv.end(); ++i)
{
@@ -1102,15 +1173,13 @@ diagnose_unrecognized_schema(sqlite3 * s
// 'manifest_deltas'.
// ??? Use PRAGMA user_version to record an additional magic number in
// monotone databases.
- string tmp;
- logged_sqlite3_exec(sql,
- "SELECT COUNT(*) FROM sqlite_master "
- "WHERE type = 'table' AND sql IS NOT NULL "
- "AND (name = 'files' OR name = 'file_deltas'"
- " OR name = 'manifests'"
- " OR name = 'manifest_deltas')",
- append_sql_stmt, &tmp, 0);
- N(tmp == "4\n",
+ int n = logged_sqlite3_exec_int(sql,
+ "SELECT COUNT(*) FROM sqlite_master "
+ "WHERE type = 'table' AND sql IS NOT NULL "
+ "AND (name = 'files' OR name = 'file_deltas'"
+ " OR name = 'manifests'"
+ " OR name = 'manifest_deltas')");
+ N(n == 4,
F("%s does not appear to be a monotone database\n"
"(schema %s, core tables missing)")
% filename % id);
@@ -1176,7 +1245,7 @@ migrate_monotone_schema(sqlite3 * sql, a
P(F("migrating data"));
E(logged_sqlite3_exec(sql, "BEGIN EXCLUSIVE", NULL, NULL, NULL) == SQLITE_OK,
- F("error at transaction BEGIN statement"));
+ F("error at transaction BEGIN statement: %s") % sqlite3_errmsg(sql));
for (;;)
{