# # # add_file "query_args.hh" # content [7492927fb5ee7ed019834339d5c0da9abbe6f617] # # patch "AUTHORS" # from [388b6818cd6ab44a800bbb8778680f1e97971e8b] # to [be2883d201fdd61b63582334f52312a640e012a1] # # patch "ChangeLog" # from [6769db3a8e03f10e9536f529fb425c86a5ee309f] # to [4714a67c78c948868a09ed8add6e3a610eaa70a1] # # patch "database.cc" # from [6cf553977563b3bdfe1a469a9e1587723988fa6d] # to [e4f0c515bff3ad55a7880aeb3c8eeeb2686348a3] # # patch "database.hh" # from [65e7daaaa720624144572538ee668d33d1a0385e] # to [8aac44bf6fb3bdb0477933e06a915671cb448029] # ============================================================ --- query_args.hh 7492927fb5ee7ed019834339d5c0da9abbe6f617 +++ query_args.hh 7492927fb5ee7ed019834339d5c0da9abbe6f617 @@ -0,0 +1,41 @@ +// -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s: +// copyright (C) 2006 vinzenz feenstra +// all rights reserved. +// licensed to the public under the terms of the GNU GPL (>= 2) +// see the file COPYING for details +#ifndef __QUERY_ARGS_HH__ +#define __QUERY_ARGS_HH__ + +#include +#include + +struct query_args_param +{ + enum arg_type{ text, blob }; + arg_type type; + std::string data; + size_t size; +}; + +struct query_args +{ + query_args(char const * cmd) + : sql_cmd(cmd) + {} + + query_args(std::string const & cmd) + : sql_cmd(cmd) + {} + + query_args & operator%(query_args_param const & qap) + { + args.push_back(qap); + return *this; + } + + std::vector args; + std::string sql_cmd; +}; + +#endif //__QUERY_ARGS_HH__ ============================================================ --- AUTHORS 388b6818cd6ab44a800bbb8778680f1e97971e8b +++ AUTHORS be2883d201fdd61b63582334f52312a640e012a1 @@ -71,8 +71,8 @@ Marcel van der Boom Roland McGrath Daniel Carosone + Vinzenz Feenstra - Several people have also contributed to the translation of monotone into non-English languages; their work is available in the po/ subdirectory. Contributors include: ============================================================ --- ChangeLog 6769db3a8e03f10e9536f529fb425c86a5ee309f +++ ChangeLog 4714a67c78c948868a09ed8add6e3a610eaa70a1 @@ -1,3 +1,11 @@ +2006-01-24 Vinzenz Feenstra + + * query_args.hh: Introduced struct query_args and struct + query_args_param. Used to typesafe arguments for database::execute + and database::fetch + * database.cc/.hh: Adjusted database to the new fetch and execute + argument style via operator% which is more typesafe. + 2006-01-23 Nathaniel Smith * database.cc (assert_sqlite3_ok): Improve the hint message given ============================================================ --- database.cc 6cf553977563b3bdfe1a469a9e1587723988fa6d +++ database.cc e4f0c515bff3ad55a7880aeb3c8eeeb2686348a3 @@ -1,9 +1,12 @@ -// -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil -*- +// -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s: // copyright (C) 2002, 2003 graydon hoare +// copyright (C) 2006 vinzenz feenstra // all rights reserved. // licensed to the public under the terms of the GNU GPL (>= 2) // see the file COPYING for details + #include #include #include @@ -35,12 +38,13 @@ #include "xdelta.hh" #include "epoch.hh" +#include "query_args.hh" + // defined in schema.sql, converted to header: #include "schema.h" // defined in views.sql, converted to header: #include "views.h" - // this file defines a public, typed interface to the database. // the database class encapsulates all knowledge about sqlite, // the schema, and all SQL statements used to access the schema. @@ -56,10 +60,57 @@ int const any_rows = -1; int const any_cols = -1; -namespace +namespace { - // track all open databases for close_all_databases() handler - set sql_contexts; + query_args_param + text(std::string const & txt) + { + query_args_param q = { + query_args_param::text, + txt, + txt.size() + }; + return q; + } + + query_args_param + text(char const * txt) + { + query_args_param q = { + query_args_param::text, + std::string(txt), + strlen(txt) + }; + return q; + } + + query_args_param + blob(void const * data, + size_t const size) + { + char const * ptr = reinterpret_cast(data); + query_args_param q = { + query_args_param::blob, + std::string(ptr,ptr+size), + size + }; + return q; + } + + query_args_param + blob(char const * data, + size_t const size) + { + query_args_param q = { + query_args_param::blob, + std::string(data,data+size), + size + }; + return q; + } + + // track all open databases for close_all_databases() handler + set sql_contexts; } extern "C" { @@ -101,14 +152,14 @@ string revisions_query = "SELECT 1 FROM revisions LIMIT 1"; string rosters_query = "SELECT 1 FROM rosters LIMIT 1"; - fetch(res_revisions, one_col, any_rows, revisions_query.c_str()); + fetch(res_revisions, one_col, any_rows, query_args(revisions_query)); if (res_revisions.size() > 0) { // they have revisions, so they can't be _ancient_, but they still might // not have rosters results res_rosters; - fetch(res_rosters, one_col, any_rows, rosters_query.c_str()); + fetch(res_rosters, one_col, any_rows, query_args(rosters_query)); N(res_rosters.size() != 0, F("database %s contains revisions but no rosters\n" "if you are a project leader or doing local testing:\n" @@ -127,7 +178,7 @@ // init; commit; db kill_rev_locally", and then upgrading to a // rosterified monotone.) results res_manifests; - fetch(res_manifests, one_col, any_rows, manifests_query.c_str()); + fetch(res_manifests, one_col, any_rows, query_args(manifests_query)); N(res_manifests.size() == 0, F("database %s contains manifests but no revisions\n" "this is a very old database; it needs to be upgraded\n" @@ -431,7 +482,7 @@ database::debug(string const & sql, ostream & out) { results res; - fetch(res, any_cols, any_rows, sql.c_str()); + fetch(res, any_cols, any_rows, query_args(sql)); out << "'" << sql << "' -> " << res.size() << " rows\n" << endl; for (size_t i = 0; i < res.size(); ++i) { @@ -565,34 +616,18 @@ } void -database::execute(char const * query, ...) +database::execute(query_args const & query) { results res; - va_list args; - va_start(args, query); - fetch(res, 0, 0, query, args); - va_end(args); + fetch(res, 0, 0, query ); } void database::fetch(results & res, int const want_cols, int const want_rows, - char const * query, ...) + query_args const & query) { - va_list args; - va_start(args, query); - fetch(res, want_cols, want_rows, query, args); - va_end(args); -} - -void -database::fetch(results & res, - int const want_cols, - int const want_rows, - char const * query, - va_list args) -{ int nrow; int ncol; int rescode; @@ -600,44 +635,45 @@ res.clear(); res.resize(0); - map::iterator i = statement_cache.find(query); + map::iterator i = statement_cache.find(query.sql_cmd); if (i == statement_cache.end()) { - statement_cache.insert(make_pair(query, statement())); - i = statement_cache.find(query); + statement_cache.insert(make_pair(query.sql_cmd, statement())); + i = statement_cache.find(query.sql_cmd); I(i != statement_cache.end()); const char * tail; - sqlite3_prepare(sql(), query, -1, i->second.stmt.paddr(), &tail); + sqlite3_prepare(sql(), query.sql_cmd.c_str(), -1, i->second.stmt.paddr(), &tail); assert_sqlite3_ok(sql()); - L(FL("prepared statement %s\n") % query); + L(FL("prepared statement %s\n") % query.sql_cmd); // no support for multiple statements here E(*tail == 0, - F("multiple statements in query: %s\n") % query); + F("multiple statements in query: %s\n") % query.sql_cmd); } ncol = sqlite3_column_count(i->second.stmt()); E(want_cols == any_cols || want_cols == ncol, - F("wanted %d columns got %d in query: %s\n") % want_cols % ncol % query); + F("wanted %d columns got %d in query: %s\n") % want_cols % ncol % query.sql_cmd); // bind parameters for this execution int params = sqlite3_bind_parameter_count(i->second.stmt()); + // Ensure that not to less or to much parameters are given + I( params == int(query.args.size())); + // profiling finds this logging to be quite expensive if (global_sanity.debug) - L(FL("binding %d parameters for %s\n") % params % query); + L(FL("binding %d parameters for %s\n") % params % query.sql_cmd); for (int param = 1; param <= params; param++) { - char *value = va_arg(args, char *); - // profiling finds this logging to be quite expensive if (global_sanity.debug) { - string log = string(value); + string log = query.args[param-1].data; if (log.size() > constants::log_line_sz) log = log.substr(0, constants::log_line_sz); @@ -645,7 +681,9 @@ L(FL("binding %d with value '%s'\n") % param % log); } - sqlite3_bind_text(i->second.stmt(), param, value, -1, SQLITE_TRANSIENT); + sqlite3_bind_text(i->second.stmt(), param , + query.args[param-1].data.c_str(), + -1, SQLITE_TRANSIENT); assert_sqlite3_ok(sql()); } @@ -659,7 +697,7 @@ for (int col = 0; col < ncol; col++) { const char * value = sqlite3_column_text_s(i->second.stmt(), col); - E(value, F("null result in query: %s\n") % query); + E(value, F("null result in query: %s\n") % query.sql_cmd); row.push_back(value); //L(FL("row %d col %d value='%s'\n") % nrow % col % value); } @@ -677,7 +715,7 @@ i->second.count++; E(want_rows == any_rows || want_rows == nrow, - F("wanted %d rows got %s in query: %s\n") % want_rows % nrow % query); + F("wanted %d rows got %s in query: %s\n") % want_rows % nrow % query.sql_cmd); } // general application-level logic @@ -695,9 +733,9 @@ if (transaction_level == 0) { if (exclusive) - execute("BEGIN EXCLUSIVE"); + execute(query_args("BEGIN EXCLUSIVE")); else - execute("BEGIN DEFERRED"); + execute(query_args("BEGIN DEFERRED")); transaction_exclusive = exclusive; } else @@ -713,7 +751,7 @@ database::commit_transaction() { if (transaction_level == 1) - execute("COMMIT"); + execute(query_args("COMMIT")); transaction_level--; } @@ -721,7 +759,7 @@ database::rollback_transaction() { if (transaction_level == 1) - execute("ROLLBACK"); + execute(query_args("ROLLBACK")); transaction_level--; } @@ -732,7 +770,7 @@ { results res; string query = "SELECT id FROM " + table + " WHERE id = ?"; - fetch(res, one_col, any_rows, query.c_str(), ident().c_str()); + fetch(res, one_col, any_rows, query_args(query) % text(ident())); I((res.size() == 1) || (res.size() == 0)); return res.size() == 1; } @@ -744,7 +782,7 @@ { results res; string query = "SELECT id FROM " + table + " WHERE id = ?"; - fetch(res, one_col, any_rows, query.c_str(), ident().c_str()); + fetch(res, one_col, any_rows, query_args(query) % text(ident())); return res.size() > 0; } @@ -753,7 +791,7 @@ { results res; string query = "SELECT COUNT(*) FROM " + table; - fetch(res, one_col, one_row, query.c_str()); + fetch(res, one_col, one_row, query_args(query)); return lexical_cast(res[0][0]); } @@ -764,7 +802,7 @@ // COALESCE is required since SUM({empty set}) is NULL. // the sqlite docs for SUM suggest this as a workaround string query = "SELECT COALESCE(SUM(LENGTH(" + concatenated_columns + ")), 0) FROM " + table; - fetch(res, one_col, one_row, query.c_str()); + fetch(res, one_col, one_row, query_args(query)); return lexical_cast(res[0][0]); } @@ -773,7 +811,7 @@ { results res; string query = "SELECT id FROM " + table; - fetch(res, one_col, any_rows, query.c_str()); + fetch(res, one_col, any_rows, query_args(query)); for (size_t i = 0; i < res.size(); ++i) { @@ -788,7 +826,7 @@ { results res; string query = "SELECT data FROM " + table + " WHERE id = ?"; - fetch(res, one_col, one_row, query.c_str(), ident().c_str()); + fetch(res, one_col, one_row, query_args(query) % text(ident())); // consistency check base64 > rdata(res[0][0]); @@ -812,8 +850,9 @@ I(base() != ""); results res; string query = "SELECT delta FROM " + table + " WHERE id = ? AND base = ?"; - fetch(res, one_col, one_row, query.c_str(), - ident().c_str(), base().c_str()); + fetch(res, one_col, one_row, query_args(query) + % text(ident()) + % text(base())); base64 > del_packed = res[0][0]; unpack(del_packed, del); @@ -836,7 +875,9 @@ pack(dat, dat_packed); string insert = "INSERT INTO " + table + " VALUES(?, ?)"; - execute(insert.c_str(),ident().c_str(), dat_packed().c_str()); + execute(query_args(insert) + % text(ident()) + % text(dat_packed())); } void database::put_delta(hexenc const & ident, @@ -852,7 +893,10 @@ pack(del, del_packed); string insert = "INSERT INTO "+table+" VALUES(?, ?, ?)"; - execute(insert.c_str(), ident().c_str(), base().c_str(), del_packed().c_str()); + execute(query_args(insert) + % text(ident()) + % text(base()) + % text(del_packed())); } // static ticker cache_hits("vcache hits", "h", 1); @@ -1025,7 +1069,8 @@ // This tip is not a root, so extend the path. results res; fetch(res, one_col, any_rows, - delta_query.c_str(), tip().c_str()); + query_args(delta_query) + % text(tip())); I(res.size() != 0); @@ -1109,7 +1154,7 @@ string const & table) { string drop = "DELETE FROM " + table + " WHERE id = ?"; - execute(drop.c_str(), ident().c_str()); + execute(query_args(drop) % text(ident())); } void @@ -1166,7 +1211,8 @@ results res; string query = "SELECT id FROM " + delta_table + " WHERE base = ?"; fetch(res, one_col, any_rows, - query.c_str(), target_id().c_str()); + query_args(query) + % text(target_id())); for (size_t i = 0; i < res.size(); ++i) { hexenc old_id(res[i][0]); @@ -1189,7 +1235,8 @@ results res; string query = "SELECT base FROM " + delta_table + " WHERE id = ?"; fetch(res, one_col, any_rows, - query.c_str(), target_id().c_str()); + query_args(query) + % text(target_id())); I(res.size() > 0); newer_id = hexenc(res[0][0]); get_version(newer_id, newer_data, data_table, delta_table); @@ -1202,7 +1249,7 @@ } } string query = "DELETE from " + delta_table + " WHERE id = ?"; - execute(query.c_str(), target_id().c_str()); + execute(query_args(query) % text(target_id())); } else { @@ -1212,7 +1259,7 @@ i != older.end(); ++i) put(i->first, i->second, data_table); string query = "DELETE from " + data_table + " WHERE id = ?"; - execute(query.c_str(), target_id().c_str()); + execute(query_args(query) % text(target_id())); } guard.commit(); @@ -1250,8 +1297,9 @@ { results res; string query = ("SELECT roster_id FROM revision_roster WHERE rev_id = ? "); - fetch(res, one_col, any_rows, query.c_str(), - rev_id.inner()().c_str()); + fetch(res, one_col, any_rows, + query_args(query.c_str()) + % text(rev_id.inner()())); I((res.size() == 1) || (res.size() == 0)); return res.size() == 1; } @@ -1261,8 +1309,9 @@ { results res; string query = ("SELECT roster_id FROM revision_roster WHERE rev_id = ? "); - fetch(res, one_col, any_rows, query.c_str(), - rev_id.inner()().c_str()); + fetch(res, one_col, any_rows, + query_args(query) + % text(rev_id.inner()())); I((res.size() == 1) || (res.size() == 0)); return (res.size() == 1) && roster_version_exists(hexenc(res[0][0])); } @@ -1273,7 +1322,7 @@ links.clear(); results res; string query = ("SELECT rev_id, roster_id FROM revision_roster"); - fetch(res, 2, any_rows, query.c_str()); + fetch(res, 2, any_rows, query_args(query)); for (size_t i = 0; i < res.size(); ++i) { links.insert(make_pair(revision_id(res[i][0]), @@ -1350,7 +1399,7 @@ results res; graph.clear(); fetch(res, 2, any_rows, - "SELECT parent,child FROM revision_ancestry"); + query_args("SELECT parent,child FROM revision_ancestry")); for (size_t i = 0; i < res.size(); ++i) graph.insert(std::make_pair(revision_id(res[i][0]), revision_id(res[i][1]))); @@ -1364,8 +1413,8 @@ results res; parents.clear(); fetch(res, one_col, any_rows, - "SELECT parent FROM revision_ancestry WHERE child = ?", - id.inner()().c_str()); + query_args("SELECT parent FROM revision_ancestry WHERE child = ?") + % text(id.inner()())); for (size_t i = 0; i < res.size(); ++i) parents.insert(revision_id(res[i][0])); } @@ -1377,8 +1426,8 @@ results res; children.clear(); fetch(res, one_col, any_rows, - "SELECT child FROM revision_ancestry WHERE parent = ?", - id.inner()().c_str()); + query_args("SELECT child FROM revision_ancestry WHERE parent = ?") + % text(id.inner()())); for (size_t i = 0; i < res.size(); ++i) children.insert(revision_id(res[i][0])); } @@ -1408,8 +1457,8 @@ I(!null_id(id)); results res; fetch(res, one_col, one_row, - "SELECT data FROM revisions WHERE id = ?", - id.inner()().c_str()); + query_args("SELECT data FROM revisions WHERE id = ?") + % text(id.inner()())); base64 > rdat_packed; rdat_packed = base64 >(res[0][0]); @@ -1507,16 +1556,16 @@ base64 > d_packed; pack(d.inner(), d_packed); - execute("INSERT INTO revisions VALUES(?, ?)", - new_id.inner()().c_str(), - d_packed().c_str()); + execute(query_args("INSERT INTO revisions VALUES(?, ?)") + % text(new_id.inner()()) + % text(d_packed())); for (edge_map::const_iterator e = rev.edges.begin(); e != rev.edges.end(); ++e) { - execute("INSERT INTO revision_ancestry VALUES(?, ?)", - edge_old_revision(e).inner()().c_str(), - new_id.inner()().c_str()); + execute(query_args("INSERT INTO revision_ancestry VALUES(?, ?)") + % text(edge_old_revision(e).inner()()) + % text(new_id.inner()())); } deltify_revision(new_id); @@ -1541,16 +1590,16 @@ void database::delete_existing_revs_and_certs() { - execute("DELETE FROM revisions"); - execute("DELETE FROM revision_ancestry"); - execute("DELETE FROM revision_certs"); + execute(query_args("DELETE FROM revisions")); + execute(query_args("DELETE FROM revision_ancestry")); + execute(query_args("DELETE FROM revision_certs")); } void database::delete_existing_manifests() { - execute("DELETE FROM manifests"); - execute("DELETE FROM manifest_deltas"); + execute(query_args("DELETE FROM manifests")); + execute(query_args("DELETE FROM manifest_deltas")); } /// Deletes one revision from the local database. @@ -1570,9 +1619,14 @@ L(FL("Killing revision %s locally\n") % rid); // Kill the certs, ancestry, and rev itself. - execute("DELETE from revision_certs WHERE id = ?",rid.inner()().c_str()); - execute("DELETE from revision_ancestry WHERE child = ?", rid.inner()().c_str()); - execute("DELETE from revisions WHERE id = ?",rid.inner()().c_str()); + execute(query_args("DELETE from revision_certs WHERE id = ?") + % text(rid.inner()())); + + execute(query_args("DELETE from revision_ancestry WHERE child = ?") + % text(rid.inner()())); + + execute(query_args("DELETE from revisions WHERE id = ?") + % text(rid.inner()())); // Find the associated roster and count the number of links to it hexenc roster_id; @@ -1582,13 +1636,14 @@ results res; string query = ("SELECT rev_id, roster_id FROM revision_roster " "WHERE roster_id = ?"); - fetch(res, 2, any_rows, query.c_str(), roster_id().c_str()); + fetch(res, 2, any_rows, query_args(query) % text(roster_id())); I(res.size() > 0); link_count = res.size(); } // Delete our link. - execute("DELETE from revision_roster WHERE rev_id = ?", rid.inner()().c_str()); + execute(query_args("DELETE from revision_roster WHERE rev_id = ?") + % text(rid.inner()())); // If that was the last link to the roster, kill the roster too. if (link_count == 1) @@ -1604,10 +1659,10 @@ base64 encoded; encode_base64(branch, encoded); L(FL("Deleting all references to branch %s\n") % branch); - execute("DELETE FROM revision_certs WHERE name='branch' AND value =?", - encoded().c_str()); - execute("DELETE FROM branch_epochs WHERE branch=?", - encoded().c_str()); + execute(query_args("DELETE FROM revision_certs WHERE name='branch' AND value =?") + % text(encoded())); + execute(query_args("DELETE FROM branch_epochs WHERE branch=?") + % text(encoded())); } /// Deletes all certs referring to a particular tag. @@ -1617,8 +1672,8 @@ base64 encoded; encode_base64(tag, encoded); L(FL("Deleting all references to tag %s\n") % tag); - execute("DELETE FROM revision_certs WHERE name='tag' AND value =?", - encoded().c_str()); + execute(query_args("DELETE FROM revision_certs WHERE name='tag' AND value =?") + % text(encoded())); } // crypto key management @@ -1632,11 +1687,11 @@ if (pattern != "") fetch(res, one_col, any_rows, - "SELECT id FROM public_keys WHERE id GLOB ?", - pattern.c_str()); + query_args("SELECT id FROM public_keys WHERE id GLOB ?") + % text(pattern)); else fetch(res, one_col, any_rows, - "SELECT id FROM public_keys"); + query_args("SELECT id FROM public_keys")); for (size_t i = 0; i < res.size(); ++i) pubkeys.push_back(res[i][0]); @@ -1648,7 +1703,7 @@ keys.clear(); results res; string query = "SELECT id FROM " + table; - fetch(res, one_col, any_rows, query.c_str()); + fetch(res, one_col, any_rows, query_args(query)); for (size_t i = 0; i < res.size(); ++i) keys.push_back(res[i][0]); } @@ -1664,8 +1719,8 @@ { results res; fetch(res, one_col, any_rows, - "SELECT id FROM public_keys WHERE hash = ?", - hash().c_str()); + query_args("SELECT id FROM public_keys WHERE hash = ?") + % text(hash())); I((res.size() == 1) || (res.size() == 0)); if (res.size() == 1) return true; @@ -1677,8 +1732,8 @@ { results res; fetch(res, one_col, any_rows, - "SELECT id FROM public_keys WHERE id = ?", - id().c_str()); + query_args("SELECT id FROM public_keys WHERE id = ?") + % text(id())); I((res.size() == 1) || (res.size() == 0)); if (res.size() == 1) return true; @@ -1692,8 +1747,8 @@ { results res; fetch(res, 2, one_row, - "SELECT id, keydata FROM public_keys WHERE hash = ?", - hash().c_str()); + query_args("SELECT id, keydata FROM public_keys WHERE hash = ?") + % text(hash())); id = res[0][0]; pub_encoded = res[0][1]; } @@ -1704,8 +1759,8 @@ { results res; fetch(res, one_col, one_row, - "SELECT keydata FROM public_keys WHERE id = ?", - pub_id().c_str()); + query_args("SELECT keydata FROM public_keys WHERE id = ?") + % text(pub_id())); pub_encoded = res[0][0]; } @@ -1718,15 +1773,17 @@ I(!public_key_exists(thash)); E(!public_key_exists(pub_id), F("another key with name '%s' already exists") % pub_id); - execute("INSERT INTO public_keys VALUES(?, ?, ?)", - thash().c_str(), pub_id().c_str(), pub_encoded().c_str()); + execute(query_args("INSERT INTO public_keys VALUES(?, ?, ?)") + % text(thash()) + % text(pub_id()) + % text(pub_encoded())); } void database::delete_public_key(rsa_keypair_id const & pub_id) { - execute("DELETE FROM public_keys WHERE id = ?", - pub_id().c_str()); + execute(query_args("DELETE FROM public_keys WHERE id = ?") + % text(pub_id())); } // cert management @@ -1743,12 +1800,13 @@ "AND keypair = ? " "AND signature = ?"; - fetch(res, 1, any_rows, query.c_str(), - t.ident().c_str(), - t.name().c_str(), - t.value().c_str(), - t.key().c_str(), - t.sig().c_str()); + fetch(res, 1, any_rows, + query_args(query) + % text(t.ident()) + % text(t.name()) + % text(t.value()) + % text(t.key()) + % text(t.sig())); I(res.size() == 0 || res.size() == 1); return res.size() == 1; } @@ -1762,13 +1820,13 @@ string insert = "INSERT INTO " + table + " VALUES(?, ?, ?, ?, ?, ?)"; - execute(insert.c_str(), - thash().c_str(), - t.ident().c_str(), - t.name().c_str(), - t.value().c_str(), - t.key().c_str(), - t.sig().c_str()); + execute(query_args(insert) + % text(thash()) + % text(t.ident()) + % text(t.name()) + % text(t.value()) + % text(t.key()) + % text(t.sig())); } void @@ -1830,7 +1888,7 @@ { results res; string query = "SELECT id, name, value, keypair, signature FROM " + table; - fetch(res, 5, any_rows, query.c_str()); + fetch(res, 5, any_rows, query_args(query)); results_to_certs(res, certs); } @@ -1845,7 +1903,7 @@ "SELECT id, name, value, keypair, signature FROM " + table + " WHERE id = ?"; - fetch(res, 5, any_rows, query.c_str(), ident().c_str()); + fetch(res, 5, any_rows, query_args(query) % text(ident())); results_to_certs(res, certs); } @@ -1859,7 +1917,7 @@ string query = "SELECT id, name, value, keypair, signature FROM " + table + " WHERE name = ?"; - fetch(res, 5, any_rows, query.c_str(), name().c_str()); + fetch(res, 5, any_rows, query_args(query) % text(name())); results_to_certs(res, certs); } @@ -1875,8 +1933,10 @@ "SELECT id, name, value, keypair, signature FROM " + table + " WHERE id = ? AND name = ?"; - fetch(res, 5, any_rows, query.c_str(), - ident().c_str(), name().c_str()); + fetch(res, 5, any_rows, + query_args(query) + % text(ident()) + % text(name())); results_to_certs(res, certs); } @@ -1891,8 +1951,10 @@ "SELECT id, name, value, keypair, signature FROM " + table + " WHERE name = ? AND value = ?"; - fetch(res, 5, any_rows, query.c_str(), - name().c_str(), val().c_str()); + fetch(res, 5, any_rows, + query_args(query) + % text(name()) + % text(val())); results_to_certs(res, certs); } @@ -1909,10 +1971,11 @@ "SELECT id, name, value, keypair, signature FROM " + table + " WHERE id = ? AND name = ? AND value = ?"; - fetch(res, 5, any_rows, query.c_str(), - ident().c_str(), - name().c_str(), - value().c_str()); + fetch(res, 5, any_rows, + query_args(query) + % text(ident()) + % text(name()) + % text(value())); results_to_certs(res, certs); } @@ -1935,8 +1998,8 @@ { results res; fetch(res, 3, any_rows, - "SELECT hash, id, keypair " - "FROM 'revision_certs' WHERE name != 'branch'"); + query_args("SELECT hash, id, keypair " + "FROM 'revision_certs' WHERE name != 'branch'")); idx.clear(); idx.reserve(res.size()); @@ -2018,10 +2081,10 @@ results res; vector certs; fetch(res, one_col, any_rows, - "SELECT hash " + query_args("SELECT hash " "FROM revision_certs " - "WHERE id = ?", - ident.inner()().c_str()); + "WHERE id = ?") + % text(ident.inner()())); ts.clear(); for (size_t i = 0; i < res.size(); ++i) ts.push_back(hexenc(res[i][0])); @@ -2034,10 +2097,10 @@ results res; vector certs; fetch(res, 5, one_row, - "SELECT id, name, value, keypair, signature " + query_args("SELECT id, name, value, keypair, signature " "FROM revision_certs " - "WHERE hash = ?", - hash().c_str()); + "WHERE hash = ?") + % text(hash())); results_to_certs(res, certs); I(certs.size() == 1); c = revision(certs[0]); @@ -2049,10 +2112,10 @@ results res; vector certs; fetch(res, one_col, any_rows, - "SELECT id " + query_args("SELECT id " "FROM revision_certs " - "WHERE hash = ?", - hash().c_str()); + "WHERE hash = ?") + % text(hash())); I(res.size() == 0 || res.size() == 1); return (res.size() == 1); } @@ -2090,8 +2153,8 @@ string pattern = partial + "*"; fetch(res, 1, any_rows, - "SELECT id FROM revisions WHERE id GLOB ?", - pattern.c_str()); + query_args("SELECT id FROM revisions WHERE id GLOB ?") + % text(pattern)); for (size_t i = 0; i < res.size(); ++i) completions.insert(revision_id(res[i][0])); @@ -2108,8 +2171,8 @@ string pattern = partial + "*"; fetch(res, 1, any_rows, - "SELECT id FROM files WHERE id GLOB ?", - pattern.c_str()); + query_args("SELECT id FROM files WHERE id GLOB ?") + % text(pattern)); for (size_t i = 0; i < res.size(); ++i) completions.insert(file_id(res[i][0])); @@ -2117,8 +2180,8 @@ res.clear(); fetch(res, 1, any_rows, - "SELECT id FROM file_deltas WHERE id GLOB ?", - pattern.c_str()); + query_args("SELECT id FROM file_deltas WHERE id GLOB ?") + % text(pattern)); for (size_t i = 0; i < res.size(); ++i) completions.insert(file_id(res[i][0])); @@ -2134,8 +2197,8 @@ string pattern = partial + "*"; fetch(res, 2, any_rows, - "SELECT hash, id FROM public_keys WHERE hash GLOB ?", - pattern.c_str()); + query_args("SELECT hash, id FROM public_keys WHERE hash GLOB ?") + % text(pattern)); for (size_t i = 0; i < res.size(); ++i) completions.insert(make_pair(key_id(res[i][0]), utf8(res[i][1]))); @@ -2265,7 +2328,7 @@ string subquery = (boost::format("SELECT DISTINCT value FROM revision_certs WHERE name='%s' and unbase64(value) glob '%s'") % branch_cert_name % i->second).str(); results res; - fetch(res, one_col, any_rows, subquery.c_str()); + fetch(res, one_col, any_rows, query_args(subquery)); for (size_t i = 0; i < res.size(); ++i) { base64 row_encoded(res[i][0]); @@ -2375,7 +2438,7 @@ // std::cerr << query << std::endl; // debug expr results res; - fetch(res, one_col, any_rows, query.c_str()); + fetch(res, one_col, any_rows, query_args(query)); for (size_t i = 0; i < res.size(); ++i) { if (ty == selectors::sel_ident) @@ -2397,7 +2460,7 @@ { epochs.clear(); results res; - fetch(res, 2, any_rows, "SELECT branch, epoch FROM branch_epochs"); + fetch(res, 2, any_rows, query_args("SELECT branch, epoch FROM branch_epochs")); for (results::const_iterator i = res.begin(); i != res.end(); ++i) { base64 encoded(idx(*i, 0)); @@ -2415,9 +2478,9 @@ I(epoch_exists(eid)); results res; fetch(res, 2, any_rows, - "SELECT branch, epoch FROM branch_epochs" - " WHERE hash = ?", - eid.inner()().c_str()); + query_args("SELECT branch, epoch FROM branch_epochs" + " WHERE hash = ?") + % text(eid.inner()())); I(res.size() == 1); base64 encoded(idx(idx(res, 0), 0)); decode_base64(encoded, branch); @@ -2429,8 +2492,8 @@ { results res; fetch(res, one_col, any_rows, - "SELECT hash FROM branch_epochs WHERE hash = ?", - eid.inner()().c_str()); + query_args("SELECT hash FROM branch_epochs WHERE hash = ?") + % text(eid.inner()())); I(res.size() == 1 || res.size() == 0); return res.size() == 1; } @@ -2443,8 +2506,10 @@ encode_base64(branch, encoded); epoch_hash_code(branch, epo, eid); I(epo.inner()().size() == constants::epochlen); - execute("INSERT OR REPLACE INTO branch_epochs VALUES(?, ?, ?)", - eid.inner()().c_str(), encoded().c_str(), epo.inner()().c_str()); + execute(query_args("INSERT OR REPLACE INTO branch_epochs VALUES(?, ?, ?)") + % text(eid.inner()()) + % text(encoded()) + % text(epo.inner()())); } void @@ -2452,7 +2517,8 @@ { base64 encoded; encode_base64(branch, encoded); - execute("DELETE FROM branch_epochs WHERE branch = ?", encoded().c_str()); + execute(query_args("DELETE FROM branch_epochs WHERE branch = ?") + % text(encoded())); } // vars @@ -2462,7 +2528,7 @@ { vars.clear(); results res; - fetch(res, 3, any_rows, "SELECT domain, name, value FROM db_vars"); + fetch(res, 3, any_rows, query_args("SELECT domain, name, value FROM db_vars")); for (results::const_iterator i = res.begin(); i != res.end(); ++i) { var_domain domain(idx(*i, 0)); @@ -2505,10 +2571,10 @@ encode_base64(key.second, name_encoded); base64 value_encoded; encode_base64(value, value_encoded); - execute("INSERT OR REPLACE INTO db_vars VALUES(?, ?, ?)", - key.first().c_str(), - name_encoded().c_str(), - value_encoded().c_str()); + execute(query_args("INSERT OR REPLACE INTO db_vars VALUES(?, ?, ?)") + % text(key.first()) + % text(name_encoded()) + % text(value_encoded())); } void @@ -2516,8 +2582,9 @@ { base64 name_encoded; encode_base64(key.second, name_encoded); - execute("DELETE FROM db_vars WHERE domain = ? AND name = ?", - key.first().c_str(), name_encoded().c_str()); + execute(query_args("DELETE FROM db_vars WHERE domain = ? AND name = ?") + % text(key.first()) + % text(name_encoded())); } // branches @@ -2528,7 +2595,7 @@ results res; string query="SELECT DISTINCT value FROM revision_certs WHERE name= ?"; string cert_name="branch"; - fetch(res, one_col, any_rows, query.c_str(), cert_name.c_str()); + fetch(res, one_col, any_rows, query_args(query) % text(cert_name)); for (size_t i = 0; i < res.size(); ++i) { base64 row_encoded(res[i][0]); @@ -2550,8 +2617,9 @@ results res; string query = ("SELECT roster_id FROM revision_roster WHERE rev_id = ? "); - fetch(res, one_col, any_rows, query.c_str(), - rev_id.inner()().c_str()); + fetch(res, one_col, any_rows, + query_args(query) + % text(rev_id.inner()())); I(res.size() == 1); roster_id = hexenc(res[0][0]); } @@ -2617,9 +2685,9 @@ transaction_guard guard(*this); - execute("INSERT into revision_roster VALUES (?, ?)", - rev_id.inner()().c_str(), - new_id().c_str()); + execute(query_args("INSERT into revision_roster VALUES (?, ?)") + % text(rev_id.inner()()) + % text(new_id())); if (exists(new_id, data_table) || delta_exists(new_id, delta_table)) @@ -2706,7 +2774,7 @@ b_uncommon_ancs.clear(); fetch(res, 2, any_rows, - "SELECT parent,child FROM revision_ancestry"); + query_args("SELECT parent,child FROM revision_ancestry")); set a_ancs, b_ancs; @@ -2735,22 +2803,22 @@ // We implement this as a fixed db var. fetch(res, one_col, any_rows, - "SELECT node FROM next_roster_node_number"); + query_args("SELECT node FROM next_roster_node_number")); node_id n; if (res.empty()) { n = 1; - execute ("INSERT INTO next_roster_node_number VALUES(?)", - lexical_cast(n).c_str()); + execute (query_args("INSERT INTO next_roster_node_number VALUES(?)") + % text(lexical_cast(n))); } else { I(res.size() == 1); n = lexical_cast(res[0][0]); ++n; - execute ("UPDATE next_roster_node_number SET node = ?", - lexical_cast(n).c_str()); + execute (query_args("UPDATE next_roster_node_number SET node = ?") + % text(lexical_cast(n))); } guard.commit(); ============================================================ --- database.hh 65e7daaaa720624144572538ee668d33d1a0385e +++ database.hh 8aac44bf6fb3bdb0477933e06a915671cb448029 @@ -1,10 +1,12 @@ -#ifndef __DATABASE_HH__ -#define __DATABASE_HH__ - +// -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s: // copyright (C) 2002, 2003 graydon hoare +// copyright (C) 2006 vinzenz feenstra // all rights reserved. // licensed to the public under the terms of the GNU GPL (>= 2) // see the file COPYING for details +#ifndef __DATABASE_HH__ +#define __DATABASE_HH__ struct sqlite3; struct sqlite3_stmt; @@ -69,6 +71,7 @@ struct posting; struct app_state; struct revision_set; +struct query_args; class database { @@ -96,18 +99,12 @@ typedef std::vector< std::vector > results; - void execute(char const * query, ...); + void execute(query_args const & query); void fetch(results & res, int const want_cols, int const want_rows, - char const * query, ...); - - void fetch(results & res, - int const want_cols, - int const want_rows, - char const * query, - va_list args); + query_args const & query); bool exists(hexenc const & ident, std::string const & table);