monotone

monotone Mtn Source Tree

Root/database.cc

1// -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil -*-
2// copyright (C) 2002, 2003 graydon hoare <graydon@pobox.com>
3// all rights reserved.
4// licensed to the public under the terms of the GNU GPL (>= 2)
5// see the file COPYING for details
6
7#include <fstream>
8#include <iterator>
9#include <list>
10#include <set>
11#include <sstream>
12#include <vector>
13
14#include <string.h>
15
16#include <boost/shared_ptr.hpp>
17#include <boost/lexical_cast.hpp>
18
19#include <sqlite3.h>
20
21#include "app_state.hh"
22#include "cert.hh"
23#include "cleanup.hh"
24#include "constants.hh"
25#include "database.hh"
26#include "keys.hh"
27#include "sanity.hh"
28#include "schema_migration.hh"
29#include "transforms.hh"
30#include "ui.hh"
31#include "vocab.hh"
32#include "xdelta.hh"
33#include "epoch.hh"
34
35// defined in schema.sql, converted to header:
36#include "schema.h"
37
38// defined in views.sql, converted to header:
39#include "views.h"
40
41// this file defines a public, typed interface to the database.
42// the database class encapsulates all knowledge about sqlite,
43// the schema, and all SQL statements used to access the schema.
44//
45// see file schema.sql for the text of the schema.
46
47using boost::shared_ptr;
48using boost::lexical_cast;
49using namespace std;
50
51int const one_row = 1;
52int const one_col = 1;
53int const any_rows = -1;
54int const any_cols = -1;
55
56namespace
57{
58 // track all open databases for close_all_databases() handler
59 set<sqlite3*> sql_contexts;
60}
61
62extern "C" {
63// some wrappers to ease migration
64 const char *sqlite3_value_text_s(sqlite3_value *v);
65 const char *sqlite3_column_text_s(sqlite3_stmt*, int col);
66}
67
68database::database(system_path const & fn) :
69 filename(fn),
70 // nb. update this if you change the schema. unfortunately we are not
71 // using self-digesting schemas due to comment irregularities and
72 // non-alphabetic ordering of tables in sql source files. we could create
73 // a temporary db, write our intended schema into it, and read it back,
74 // but this seems like it would be too rude. possibly revisit this issue.
75 schema("bd86f9a90b5d552f0be1fa9aee847ea0f317778b"),
76 __sql(NULL),
77 transaction_level(0)
78{}
79
80void
81database::check_schema()
82{
83 string db_schema_id;
84 calculate_schema_id (__sql, db_schema_id);
85 N (schema == db_schema_id,
86 F("layout of database %s doesn't match this version of monotone\n"
87 "wanted schema %s, got %s\n"
88 "try 'monotone db migrate' to upgrade\n"
89 "(this is irreversible; you may want to make a backup copy first)")
90 % filename % schema % db_schema_id);
91}
92
93// sqlite3_value_text gives a const unsigned char * but most of the time
94// we need a signed char
95const char *
96sqlite3_value_text_s(sqlite3_value *v)
97{
98 return (const char *)(sqlite3_value_text(v));
99}
100
101const char *
102sqlite3_column_text_s(sqlite3_stmt *stmt, int col)
103{
104 return (const char *)(sqlite3_column_text(stmt, col));
105}
106
107static void
108sqlite3_unbase64_fn(sqlite3_context *f, int nargs, sqlite3_value ** args)
109{
110 if (nargs != 1)
111 {
112 sqlite3_result_error(f, "need exactly 1 arg to unbase64()", -1);
113 return;
114 }
115 data decoded;
116 decode_base64(base64<data>(string(sqlite3_value_text_s(args[0]))), decoded);
117 sqlite3_result_blob(f, decoded().c_str(), decoded().size(), SQLITE_TRANSIENT);
118}
119
120static void
121sqlite3_unpack_fn(sqlite3_context *f, int nargs, sqlite3_value ** args)
122{
123 if (nargs != 1)
124 {
125 sqlite3_result_error(f, "need exactly 1 arg to unpack()", -1);
126 return;
127 }
128 data unpacked;
129 unpack(base64< gzip<data> >(string(sqlite3_value_text_s(args[0]))), unpacked);
130 sqlite3_result_blob(f, unpacked().c_str(), unpacked().size(), SQLITE_TRANSIENT);
131}
132
133void
134database::set_app(app_state * app)
135{
136 __app = app;
137}
138
139static void
140check_sqlite_format_version(system_path const & filename)
141{
142 // sqlite 3 files begin with this constant string
143 // (version 2 files begin with a different one)
144 std::string version_string("SQLite format 3");
145
146 std::ifstream file(filename.as_external().c_str());
147 N(file, F("unable to probe database version in file %s") % filename);
148
149 for (std::string::const_iterator i = version_string.begin();
150 i != version_string.end(); ++i)
151 {
152 char c;
153 file.get(c);
154 N(c == *i, F("database %s is not an sqlite version 3 file, "
155 "try dump and reload") % filename);
156 }
157}
158
159
160static void
161assert_sqlite3_ok(sqlite3 *s)
162{
163 int errcode = sqlite3_errcode(s);
164
165 if (errcode == SQLITE_OK) return;
166
167 const char * errmsg = sqlite3_errmsg(s);
168
169 // sometimes sqlite is not very helpful
170 // so we keep a table of errors people have gotten and more helpful versions
171 if (errcode != SQLITE_OK)
172 {
173 // first log the code so we can find _out_ what the confusing code
174 // was... note that code does not uniquely identify the errmsg, unlike
175 // errno's.
176 L(F("sqlite error: %d: %s") % errcode % errmsg);
177 }
178 std::string auxiliary_message = "";
179 if (errcode == SQLITE_ERROR)
180 {
181 auxiliary_message = _("make sure database and containing directory are writeable");
182 }
183 // if the last message is empty, the \n will be stripped off too
184 E(errcode == SQLITE_OK,
185 // kind of string surgery to avoid ~duplicate strings
186 boost::format("%s\n%s")
187 % (F("sqlite error: %d: %s") % errcode % errmsg).str()
188 % auxiliary_message);
189}
190
191struct sqlite3 *
192database::sql(bool init)
193{
194 if (! __sql)
195 {
196 check_filename();
197
198 if (! init)
199 {
200 check_db_exists();
201 check_sqlite_format_version(filename);
202 }
203
204 open();
205
206 if (init)
207 {
208 sqlite3_exec(__sql, schema_constant, NULL, NULL, NULL);
209 assert_sqlite3_ok(__sql);
210 }
211
212 check_schema();
213 install_functions(__app);
214 install_views();
215 }
216 return __sql;
217}
218
219void
220database::initialize()
221{
222 if (__sql)
223 throw oops("cannot initialize database while it is open");
224
225 require_path_is_nonexistent(filename,
226 F("could not initialize database: %s: already exists")
227 % filename);
228
229 system_path journal(filename.as_internal() + "-journal");
230 require_path_is_nonexistent(journal,
231 F("existing (possibly stale) journal file '%s' "
232 "has same stem as new database '%s'\n"
233 "cancelling database creation")
234 % journal % filename);
235
236 sqlite3 *s = sql(true);
237 I(s != NULL);
238}
239
240
241struct
242dump_request
243{
244 dump_request() {};
245 struct sqlite3 *sql;
246 string table_name;
247 ostream *out;
248};
249
250static int
251dump_row_cb(void *data, int n, char **vals, char **cols)
252{
253 dump_request *dump = reinterpret_cast<dump_request *>(data);
254 I(dump != NULL);
255 I(vals != NULL);
256 I(dump->out != NULL);
257 *(dump->out) << boost::format("INSERT INTO %s VALUES(") % dump->table_name;
258 for (int i = 0; i < n; ++i)
259 {
260 if (i != 0)
261 *(dump->out) << ',';
262
263 if (vals[i] == NULL)
264 *(dump->out) << "NULL";
265 else
266 {
267 *(dump->out) << "'";
268 for (char *cp = vals[i]; *cp; ++cp)
269 {
270 if (*cp == '\'')
271 *(dump->out) << "''";
272 else
273 *(dump->out) << *cp;
274 }
275 *(dump->out) << "'";
276 }
277 }
278 *(dump->out) << ");\n";
279 return 0;
280}
281
282static int
283dump_table_cb(void *data, int n, char **vals, char **cols)
284{
285 dump_request *dump = reinterpret_cast<dump_request *>(data);
286 I(dump != NULL);
287 I(dump->sql != NULL);
288 I(vals != NULL);
289 I(vals[0] != NULL);
290 I(vals[1] != NULL);
291 I(vals[2] != NULL);
292 I(n == 3);
293 I(string(vals[1]) == "table");
294 *(dump->out) << vals[2] << ";\n";
295 dump->table_name = string(vals[0]);
296 string query = "SELECT * FROM " + string(vals[0]);
297 sqlite3_exec(dump->sql, query.c_str(), dump_row_cb, data, NULL);
298 assert_sqlite3_ok(dump->sql);
299 return 0;
300}
301
302static int
303dump_index_cb(void *data, int n, char **vals, char **cols)
304{
305 dump_request *dump = reinterpret_cast<dump_request *>(data);
306 I(dump != NULL);
307 I(dump->sql != NULL);
308 I(vals != NULL);
309 I(vals[0] != NULL);
310 I(vals[1] != NULL);
311 I(vals[2] != NULL);
312 I(n == 3);
313 I(string(vals[1]) == "index");
314 *(dump->out) << vals[2] << ";\n";
315 return 0;
316}
317
318void
319database::dump(ostream & out)
320{
321 transaction_guard guard(*this);
322 dump_request req;
323 req.out = &out;
324 req.sql = sql();
325 out << "BEGIN EXCLUSIVE;\n";
326 int res;
327 res = sqlite3_exec(req.sql,
328 "SELECT name, type, sql FROM sqlite_master "
329 "WHERE type='table' AND sql NOT NULL "
330 "AND name not like 'sqlite_stat%' "
331 "ORDER BY name",
332 dump_table_cb, &req, NULL);
333 assert_sqlite3_ok(req.sql);
334 res = sqlite3_exec(req.sql,
335 "SELECT name, type, sql FROM sqlite_master "
336 "WHERE type='index' AND sql NOT NULL "
337 "ORDER BY name",
338 dump_index_cb, &req, NULL);
339 assert_sqlite3_ok(req.sql);
340 out << "COMMIT;\n";
341 guard.commit();
342}
343
344void
345database::load(istream & in)
346{
347 char buf[constants::bufsz];
348 string tmp;
349
350 check_filename();
351
352 require_path_is_nonexistent(filename,
353 F("cannot create %s; it already exists") % filename);
354
355 open();
356
357 while(in)
358 {
359 in.read(buf, constants::bufsz);
360 tmp.append(buf, in.gcount());
361
362 const char* last_statement = 0;
363 sqlite3_complete_last(tmp.c_str(), &last_statement);
364 if (last_statement == 0)
365 continue;
366 string::size_type len = last_statement + 1 - tmp.c_str();
367 sqlite3_exec(__sql, tmp.substr(0, len).c_str(), NULL, NULL, NULL);
368 tmp.erase(0, len);
369 }
370
371 if (!tmp.empty())
372 sqlite3_exec(__sql, tmp.c_str(), NULL, NULL, NULL);
373 assert_sqlite3_ok(__sql);
374}
375
376
377void
378database::debug(string const & sql, ostream & out)
379{
380 results res;
381 fetch(res, any_cols, any_rows, sql.c_str());
382 out << "'" << sql << "' -> " << res.size() << " rows\n" << endl;
383 for (size_t i = 0; i < res.size(); ++i)
384 {
385 for (size_t j = 0; j < res[i].size(); ++j)
386 {
387 if (j != 0)
388 out << " | ";
389 out << res[i][j];
390 }
391 out << endl;
392 }
393}
394
395
396namespace
397{
398 unsigned long
399 add(unsigned long count, unsigned long & total)
400 {
401 total += count;
402 return count;
403 }
404}
405
406void
407database::info(ostream & out)
408{
409 string id;
410 calculate_schema_id(sql(), id);
411
412 unsigned long total = 0UL;
413
414#define SPACE_USAGE(TABLE, COLS) add(space_usage(TABLE, COLS), total)
415
416 out << \
417 F("schema version : %s\n"
418 "counts:\n"
419 " full manifests : %u\n"
420 " manifest deltas : %u\n"
421 " full files : %u\n"
422 " file deltas : %u\n"
423 " revisions : %u\n"
424 " ancestry edges : %u\n"
425 " certs : %u\n"
426 "bytes:\n"
427 " full manifests : %u\n"
428 " manifest deltas : %u\n"
429 " full files : %u\n"
430 " file deltas : %u\n"
431 " revisions : %u\n"
432 " cached ancestry : %u\n"
433 " certs : %u\n"
434 " total : %u\n"
435 )
436 % id
437 // counts
438 % count("manifests")
439 % count("manifest_deltas")
440 % count("files")
441 % count("file_deltas")
442 % count("revisions")
443 % count("revision_ancestry")
444 % count("revision_certs")
445 // bytes
446 % SPACE_USAGE("manifests", "id || data")
447 % SPACE_USAGE("manifest_deltas", "id || base || delta")
448 % SPACE_USAGE("files", "id || data")
449 % SPACE_USAGE("file_deltas", "id || base || delta")
450 % SPACE_USAGE("revisions", "id || data")
451 % SPACE_USAGE("revision_ancestry", "parent || child")
452 % SPACE_USAGE("revision_certs", "hash || id || name || value || keypair || signature")
453 % total;
454
455#undef SPACE_USAGE
456}
457
458void
459database::version(ostream & out)
460{
461 string id;
462
463 check_filename();
464 check_db_exists();
465 open();
466
467 calculate_schema_id(__sql, id);
468
469 close();
470
471 out << F("database schema version: %s") % id << endl;
472}
473
474void
475database::migrate()
476{
477 check_filename();
478 check_db_exists();
479 open();
480
481 migrate_monotone_schema(__sql, __app);
482
483 close();
484}
485
486void
487database::rehash()
488{
489 transaction_guard guard(*this);
490 ticker mcerts(_("mcerts"), "m", 1);
491 ticker pubkeys(_("pubkeys"), "+", 1);
492 ticker privkeys(_("privkeys"), "!", 1);
493
494 {
495 // rehash all mcerts
496 results res;
497 vector<cert> certs;
498 fetch(res, 5, any_rows,
499 "SELECT id, name, value, keypair, signature "
500 "FROM manifest_certs");
501 results_to_certs(res, certs);
502 execute("DELETE FROM manifest_certs");
503 for(vector<cert>::const_iterator i = certs.begin(); i != certs.end(); ++i)
504 {
505 put_cert(*i, "manifest_certs");
506 ++mcerts;
507 }
508 }
509
510 {
511 // rehash all pubkeys
512 results res;
513 fetch(res, 2, any_rows, "SELECT id, keydata FROM public_keys");
514 execute("DELETE FROM public_keys");
515 for (size_t i = 0; i < res.size(); ++i)
516 {
517 hexenc<id> tmp;
518 key_hash_code(rsa_keypair_id(res[i][0]), base64<rsa_pub_key>(res[i][1]), tmp);
519 execute("INSERT INTO public_keys VALUES(?, ?, ?)",
520 tmp().c_str(), res[i][0].c_str(), res[i][1].c_str());
521 ++pubkeys;
522 }
523 }
524 guard.commit();
525}
526
527void
528database::ensure_open()
529{
530 sqlite3 *s = sql();
531 I(s != NULL);
532}
533
534database::~database()
535{
536 L(F("statement cache statistics\n"));
537 L(F("prepared %d statements\n") % statement_cache.size());
538
539 for (map<string, statement>::const_iterator i = statement_cache.begin();
540 i != statement_cache.end(); ++i)
541 L(F("%d executions of %s\n") % i->second.count % i->first);
542 // trigger destructors to finalize cached statements
543 statement_cache.clear();
544
545 close();
546}
547
548void
549database::execute(char const * query, ...)
550{
551 results res;
552 va_list args;
553 va_start(args, query);
554 fetch(res, 0, 0, query, args);
555 va_end(args);
556}
557
558void
559database::fetch(results & res,
560 int const want_cols,
561 int const want_rows,
562 char const * query, ...)
563{
564 va_list args;
565 va_start(args, query);
566 fetch(res, want_cols, want_rows, query, args);
567 va_end(args);
568}
569
570void
571database::fetch(results & res,
572 int const want_cols,
573 int const want_rows,
574 char const * query,
575 va_list args)
576{
577 int nrow;
578 int ncol;
579 int rescode;
580
581 res.clear();
582 res.resize(0);
583
584 map<string, statement>::iterator i = statement_cache.find(query);
585 if (i == statement_cache.end())
586 {
587 statement_cache.insert(make_pair(query, statement()));
588 i = statement_cache.find(query);
589 I(i != statement_cache.end());
590
591 const char * tail;
592 sqlite3_prepare(sql(), query, -1, i->second.stmt.paddr(), &tail);
593 assert_sqlite3_ok(sql());
594 L(F("prepared statement %s\n") % query);
595
596 // no support for multiple statements here
597 E(*tail == 0,
598 F("multiple statements in query: %s\n") % query);
599 }
600
601 ncol = sqlite3_column_count(i->second.stmt());
602
603 E(want_cols == any_cols || want_cols == ncol,
604 F("wanted %d columns got %d in query: %s\n") % want_cols % ncol % query);
605
606 // bind parameters for this execution
607
608 int params = sqlite3_bind_parameter_count(i->second.stmt());
609
610 L(F("binding %d parameters for %s\n") % params % query);
611
612 for (int param = 1; param <= params; param++)
613 {
614 char *value = va_arg(args, char *);
615 // nb: transient will not be good for inserts with large data blobs
616 // however, it's no worse than the previous '%q' stuff in this regard
617 // might want to wrap this logging with --debug or --verbose to limit it
618
619 string log = string(value);
620
621 if (log.size() > constants::log_line_sz)
622 log = log.substr(0, constants::log_line_sz);
623
624 L(F("binding %d with value '%s'\n") % param % log);
625
626 sqlite3_bind_text(i->second.stmt(), param, value, -1, SQLITE_TRANSIENT);
627 assert_sqlite3_ok(sql());
628 }
629
630 // execute and process results
631
632 nrow = 0;
633 for (rescode = sqlite3_step(i->second.stmt()); rescode == SQLITE_ROW;
634 rescode = sqlite3_step(i->second.stmt()))
635 {
636 vector<string> row;
637 for (int col = 0; col < ncol; col++)
638 {
639 const char * value = sqlite3_column_text_s(i->second.stmt(), col);
640 E(value, F("null result in query: %s\n") % query);
641 row.push_back(value);
642 //L(F("row %d col %d value='%s'\n") % nrow % col % value);
643 }
644 res.push_back(row);
645 }
646
647 if (rescode != SQLITE_DONE)
648 assert_sqlite3_ok(sql());
649
650 sqlite3_reset(i->second.stmt());
651 assert_sqlite3_ok(sql());
652
653 nrow = res.size();
654
655 i->second.count++;
656
657 E(want_rows == any_rows || want_rows == nrow,
658 F("wanted %d rows got %s in query: %s\n") % want_rows % nrow % query);
659}
660
661// general application-level logic
662
663void
664database::set_filename(system_path const & file)
665{
666 if (__sql)
667 {
668 throw oops((F("cannot change filename to %s while db is open") % file).str());
669 }
670 filename = file;
671}
672
673void
674database::begin_transaction(bool exclusive)
675{
676 if (transaction_level == 0)
677 {
678 if (exclusive)
679 execute("BEGIN EXCLUSIVE");
680 else
681 execute("BEGIN DEFERRED");
682 transaction_exclusive = exclusive;
683 }
684 else
685 {
686 E(!exclusive || transaction_exclusive, F("Attempt to start exclusive transaction within non-exclusive transaction."));
687 }
688 transaction_level++;
689}
690
691void
692database::commit_transaction()
693{
694 if (transaction_level == 1)
695 execute("COMMIT");
696 transaction_level--;
697}
698
699void
700database::rollback_transaction()
701{
702 if (transaction_level == 1)
703 execute("ROLLBACK");
704 transaction_level--;
705}
706
707
708bool
709database::exists(hexenc<id> const & ident,
710 string const & table)
711{
712 results res;
713 string query = "SELECT id FROM " + table + " WHERE id = ?";
714 fetch(res, one_col, any_rows, query.c_str(), ident().c_str());
715 I((res.size() == 1) || (res.size() == 0));
716 return res.size() == 1;
717}
718
719
720bool
721database::delta_exists(hexenc<id> const & ident,
722 string const & table)
723{
724 results res;
725 string query = "SELECT id FROM " + table + " WHERE id = ?";
726 fetch(res, one_col, any_rows, query.c_str(), ident().c_str());
727 return res.size() > 0;
728}
729
730bool
731database::delta_exists(hexenc<id> const & ident,
732 hexenc<id> const & base,
733 string const & table)
734{
735 results res;
736 string query = "SELECT id FROM " + table + " WHERE id = ? AND base = ?";
737 fetch(res, one_col, any_rows, query.c_str(),
738 ident().c_str(), base().c_str());
739 I((res.size() == 1) || (res.size() == 0));
740 return res.size() == 1;
741}
742
743unsigned long
744database::count(string const & table)
745{
746 results res;
747 string query = "SELECT COUNT(*) FROM " + table;
748 fetch(res, one_col, one_row, query.c_str());
749 return lexical_cast<unsigned long>(res[0][0]);
750}
751
752unsigned long
753database::space_usage(string const & table, string const & concatenated_columns)
754{
755 results res;
756 // COALESCE is required since SUM({empty set}) is NULL.
757 // the sqlite docs for SUM suggest this as a workaround
758 string query = "SELECT COALESCE(SUM(LENGTH(" + concatenated_columns + ")), 0) FROM " + table;
759 fetch(res, one_col, one_row, query.c_str());
760 return lexical_cast<unsigned long>(res[0][0]);
761}
762
763void
764database::get_ids(string const & table, set< hexenc<id> > & ids)
765{
766 results res;
767 string query = "SELECT id FROM " + table;
768 fetch(res, one_col, any_rows, query.c_str());
769
770 for (size_t i = 0; i < res.size(); ++i)
771 {
772 ids.insert(hexenc<id>(res[i][0]));
773 }
774}
775
776void
777database::get(hexenc<id> const & ident,
778 data & dat,
779 string const & table)
780{
781 results res;
782 string query = "SELECT data FROM " + table + " WHERE id = ?";
783 fetch(res, one_col, one_row, query.c_str(), ident().c_str());
784
785 // consistency check
786 base64<gzip<data> > rdata(res[0][0]);
787 data rdata_unpacked;
788 unpack(rdata, rdata_unpacked);
789
790 hexenc<id> tid;
791 calculate_ident(rdata_unpacked, tid);
792 I(tid == ident);
793
794 dat = rdata_unpacked;
795}
796
797void
798database::get_delta(hexenc<id> const & ident,
799 hexenc<id> const & base,
800 delta & del,
801 string const & table)
802{
803 I(ident() != "");
804 I(base() != "");
805 results res;
806 string query = "SELECT delta FROM " + table + " WHERE id = ? AND base = ?";
807 fetch(res, one_col, one_row, query.c_str(),
808 ident().c_str(), base().c_str());
809
810 base64<gzip<delta> > del_packed = res[0][0];
811 unpack(del_packed, del);
812}
813
814void
815database::put(hexenc<id> const & ident,
816 data const & dat,
817 string const & table)
818{
819 // consistency check
820 I(ident() != "");
821 hexenc<id> tid;
822 calculate_ident(dat, tid);
823 MM(ident);
824 MM(tid);
825 I(tid == ident);
826
827 base64<gzip<data> > dat_packed;
828 pack(dat, dat_packed);
829
830 string insert = "INSERT INTO " + table + " VALUES(?, ?)";
831 execute(insert.c_str(),ident().c_str(), dat_packed().c_str());
832}
833void
834database::put_delta(hexenc<id> const & ident,
835 hexenc<id> const & base,
836 delta const & del,
837 string const & table)
838{
839 // nb: delta schema is (id, base, delta)
840 I(ident() != "");
841 I(base() != "");
842
843 base64<gzip<delta> > del_packed;
844 pack(del, del_packed);
845
846 string insert = "INSERT INTO "+table+" VALUES(?, ?, ?)";
847 execute(insert.c_str(), ident().c_str(), base().c_str(), del_packed().c_str());
848}
849
850// static ticker cache_hits("vcache hits", "h", 1);
851
852struct version_cache
853{
854 size_t capacity;
855 size_t use;
856 std::map<hexenc<id>, data> cache;
857
858 version_cache() : capacity(constants::db_version_cache_sz), use(0)
859 {
860 srand(time(NULL));
861 }
862
863 void put(hexenc<id> const & ident, data const & dat)
864 {
865 while (!cache.empty()
866 && use + dat().size() > capacity)
867 {
868 std::string key = (boost::format("%08.8x%08.8x%08.8x%08.8x%08.8x")
869 % rand() % rand() % rand() % rand() % rand()).str();
870 std::map<hexenc<id>, data>::const_iterator i;
871 i = cache.lower_bound(hexenc<id>(key));
872 if (i == cache.end())
873 {
874 // we can't find a random entry, probably there's only one
875 // entry and we missed it. delete first entry instead.
876 i = cache.begin();
877 }
878 I(i != cache.end());
879 I(use >= i->second().size());
880 L(F("version cache expiring %s\n") % i->first);
881 use -= i->second().size();
882 cache.erase(i->first);
883 }
884 cache.insert(std::make_pair(ident, dat));
885 use += dat().size();
886 }
887
888 bool exists(hexenc<id> const & ident)
889 {
890 std::map<hexenc<id>, data>::const_iterator i;
891 i = cache.find(ident);
892 return i != cache.end();
893 }
894
895 bool get(hexenc<id> const & ident, data & dat)
896 {
897 std::map<hexenc<id>, data>::const_iterator i;
898 i = cache.find(ident);
899 if (i == cache.end())
900 return false;
901 // ++cache_hits;
902 L(F("version cache hit on %s\n") % ident);
903 dat = i->second;
904 return true;
905 }
906};
907
908static version_cache vcache;
909
910typedef vector< hexenc<id> > version_path;
911
912static void
913extend_path_if_not_cycle(string table_name,
914 shared_ptr<version_path> p,
915 hexenc<id> const & ext,
916 set< hexenc<id> > seen_nodes,
917 vector< shared_ptr<version_path> > & next_paths)
918{
919 for (version_path::const_iterator i = p->begin(); i != p->end(); ++i)
920 {
921 if ((*i)() == ext())
922 throw oops("cycle in table '" + table_name + "', at node "
923 + (*i)() + " <- " + ext());
924 }
925
926 if (seen_nodes.find(ext) == seen_nodes.end())
927 {
928 p->push_back(ext);
929 next_paths.push_back(p);
930 seen_nodes.insert(ext);
931 }
932}
933
934void
935database::get_version(hexenc<id> const & ident,
936 data & dat,
937 string const & data_table,
938 string const & delta_table)
939{
940 I(ident() != "");
941
942 if (vcache.get(ident, dat))
943 {
944 return;
945 }
946 else if (exists(ident, data_table))
947 {
948 // easy path
949 get(ident, dat, data_table);
950 }
951 else
952 {
953 // tricky path
954
955 // we start from the file we want to reconstruct and work *forwards*
956 // through the database, until we get to a full data object. we then
957 // trace back through the list of edges we followed to get to the data
958 // object, applying reverse deltas.
959 //
960 // the effect of this algorithm is breadth-first search, backwards
961 // through the storage graph, to discover a forwards shortest path, and
962 // then following that shortest path with delta application.
963 //
964 // we used to do this with the boost graph library, but it invovled
965 // loading too much of the storage graph into memory at any moment. this
966 // imperative version only loads the descendents of the reconstruction
967 // node, so it much cheaper in terms of memory.
968 //
969 // we also maintain a cycle-detecting set, just to be safe
970
971 L(F("reconstructing %s in %s\n") % ident % delta_table);
972 I(delta_exists(ident, delta_table));
973
974 // Our reconstruction algorithm involves keeping a set of parallel
975 // linear paths, starting from ident, moving forward through the
976 // storage DAG until we hit a storage root.
977 //
978 // On each iteration, we extend every active path by one step. If our
979 // extension involves a fork, we duplicate the path. If any path
980 // contains a cycle, we fault.
981 //
982 // If, by extending a path C, we enter a node which another path
983 // D has already seen, we kill path C. This avoids the possibility of
984 // exponential growth in the number of paths due to extensive forking
985 // and merging.
986
987 vector< shared_ptr<version_path> > live_paths;
988
989 string delta_query = "SELECT base FROM " + delta_table + " WHERE id = ?";
990
991 {
992 shared_ptr<version_path> pth0 = shared_ptr<version_path>(new version_path());
993 pth0->push_back(ident);
994 live_paths.push_back(pth0);
995 }
996
997 shared_ptr<version_path> selected_path;
998 set< hexenc<id> > seen_nodes;
999
1000 while (!selected_path)
1001 {
1002 vector< shared_ptr<version_path> > next_paths;
1003
1004 for (vector<shared_ptr<version_path> >::const_iterator i = live_paths.begin();
1005 i != live_paths.end(); ++i)
1006 {
1007 shared_ptr<version_path> pth = *i;
1008 hexenc<id> tip = pth->back();
1009
1010 if (vcache.exists(tip) || exists(tip, data_table))
1011 {
1012 selected_path = pth;
1013 break;
1014 }
1015 else
1016 {
1017 // This tip is not a root, so extend the path.
1018 results res;
1019 fetch(res, one_col, any_rows,
1020 delta_query.c_str(), tip().c_str());
1021
1022 I(res.size() != 0);
1023
1024 // Replicate the path if there's a fork.
1025 for (size_t k = 1; k < res.size(); ++k)
1026 {
1027 shared_ptr<version_path> pthN
1028 = shared_ptr<version_path>(new version_path(*pth));
1029 extend_path_if_not_cycle(delta_table, pthN,
1030 hexenc<id>(res[k][0]),
1031 seen_nodes, next_paths);
1032 }
1033
1034 // And extend the base path we're examining.
1035 extend_path_if_not_cycle(delta_table, pth,
1036 hexenc<id>(res[0][0]),
1037 seen_nodes, next_paths);
1038 }
1039 }
1040
1041 live_paths = next_paths;
1042 }
1043
1044 // Found a root, now trace it back along the path.
1045
1046 I(selected_path);
1047 I(selected_path->size() > 1);
1048
1049 hexenc<id> curr = selected_path->back();
1050 selected_path->pop_back();
1051 data begin;
1052
1053 if (vcache.exists(curr))
1054 {
1055 I(vcache.get(curr, begin));
1056 }
1057 else
1058 {
1059 get(curr, begin, data_table);
1060 }
1061
1062 boost::shared_ptr<delta_applicator> app = new_piecewise_applicator();
1063 app->begin(begin());
1064
1065 for (version_path::reverse_iterator i = selected_path->rbegin();
1066 i != selected_path->rend(); ++i)
1067 {
1068 hexenc<id> const nxt = *i;
1069
1070 if (!vcache.exists(curr))
1071 {
1072 string tmp;
1073 app->finish(tmp);
1074 vcache.put(curr, tmp);
1075 }
1076
1077 L(F("following delta %s -> %s\n") % curr % nxt);
1078 delta del;
1079 get_delta(nxt, curr, del, delta_table);
1080 apply_delta (app, del());
1081
1082 app->next();
1083 curr = nxt;
1084 }
1085
1086 string tmp;
1087 app->finish(tmp);
1088 dat = data(tmp);
1089
1090 hexenc<id> final;
1091 calculate_ident(dat, final);
1092 I(final == ident);
1093 }
1094 vcache.put(ident, dat);
1095}
1096
1097
1098void
1099database::drop(hexenc<id> const & ident,
1100 string const & table)
1101{
1102 string drop = "DELETE FROM " + table + " WHERE id = ?";
1103 execute(drop.c_str(), ident().c_str());
1104}
1105
1106void
1107database::put_version(hexenc<id> const & old_id,
1108 hexenc<id> const & new_id,
1109 delta const & del,
1110 string const & data_table,
1111 string const & delta_table)
1112{
1113
1114 data old_data, new_data;
1115 delta reverse_delta;
1116
1117 get_version(old_id, old_data, data_table, delta_table);
1118 patch(old_data, del, new_data);
1119 diff(new_data, old_data, reverse_delta);
1120
1121 transaction_guard guard(*this);
1122 if (exists(old_id, data_table))
1123 {
1124 // descendent of a head version replaces the head, therefore old head
1125 // must be disposed of
1126 drop(old_id, data_table);
1127 }
1128 put(new_id, new_data, data_table);
1129 put_delta(old_id, new_id, reverse_delta, delta_table);
1130 guard.commit();
1131}
1132
1133void
1134database::put_reverse_version(hexenc<id> const & new_id,
1135 hexenc<id> const & old_id,
1136 delta const & reverse_del,
1137 string const & data_table,
1138 string const & delta_table)
1139{
1140 data old_data, new_data;
1141
1142 get_version(new_id, new_data, data_table, delta_table);
1143 patch(new_data, reverse_del, old_data);
1144 hexenc<id> check;
1145 calculate_ident(old_data, check);
1146 I(old_id == check);
1147
1148 transaction_guard guard(*this);
1149 put_delta(old_id, new_id, reverse_del, delta_table);
1150 guard.commit();
1151}
1152
1153
1154
1155// ------------------------------------------------------------
1156// -- --
1157// -- public interface follows --
1158// -- --
1159// ------------------------------------------------------------
1160
1161bool
1162database::file_version_exists(file_id const & id)
1163{
1164 return delta_exists(id.inner(), "file_deltas")
1165 || exists(id.inner(), "files");
1166}
1167
1168bool
1169database::manifest_version_exists(manifest_id const & id)
1170{
1171 return delta_exists(id.inner(), "manifest_deltas")
1172 || exists(id.inner(), "manifests");
1173}
1174
1175bool
1176database::revision_exists(revision_id const & id)
1177{
1178 return exists(id.inner(), "revisions");
1179}
1180
1181void
1182database::get_file_ids(set<file_id> & ids)
1183{
1184 ids.clear();
1185 set< hexenc<id> > tmp;
1186 get_ids("files", tmp);
1187 get_ids("file_deltas", tmp);
1188 ids.insert(tmp.begin(), tmp.end());
1189}
1190
1191void
1192database::get_manifest_ids(set<manifest_id> & ids)
1193{
1194 ids.clear();
1195 set< hexenc<id> > tmp;
1196 get_ids("manifests", tmp);
1197 get_ids("manifest_deltas", tmp);
1198 ids.insert(tmp.begin(), tmp.end());
1199}
1200
1201void
1202database::get_revision_ids(set<revision_id> & ids)
1203{
1204 ids.clear();
1205 set< hexenc<id> > tmp;
1206 get_ids("revisions", tmp);
1207 ids.insert(tmp.begin(), tmp.end());
1208}
1209
1210void
1211database::get_file_version(file_id const & id,
1212 file_data & dat)
1213{
1214 data tmp;
1215 get_version(id.inner(), tmp, "files", "file_deltas");
1216 dat = tmp;
1217}
1218
1219void
1220database::get_manifest_version(manifest_id const & id,
1221 manifest_data & dat)
1222{
1223 data tmp;
1224 get_version(id.inner(), tmp, "manifests", "manifest_deltas");
1225 dat = tmp;
1226}
1227
1228void
1229database::get_manifest(manifest_id const & id,
1230 manifest_map & mm)
1231{
1232 manifest_data mdat;
1233 get_manifest_version(id, mdat);
1234 read_manifest_map(mdat, mm);
1235}
1236
1237
1238void
1239database::put_file(file_id const & id,
1240 file_data const & dat)
1241{
1242 put(id.inner(), dat.inner(), "files");
1243}
1244
1245void
1246database::put_file_version(file_id const & old_id,
1247 file_id const & new_id,
1248 file_delta const & del)
1249{
1250 put_version(old_id.inner(), new_id.inner(), del.inner(),
1251 "files", "file_deltas");
1252}
1253
1254void
1255database::put_file_reverse_version(file_id const & new_id,
1256 file_id const & old_id,
1257 file_delta const & del)
1258{
1259 put_reverse_version(new_id.inner(), old_id.inner(), del.inner(),
1260 "files", "file_deltas");
1261}
1262
1263
1264void
1265database::put_manifest(manifest_id const & id,
1266 manifest_data const & dat)
1267{
1268 put(id.inner(), dat.inner(), "manifests");
1269}
1270
1271void
1272database::put_manifest_version(manifest_id const & old_id,
1273 manifest_id const & new_id,
1274 manifest_delta const & del)
1275{
1276 put_version(old_id.inner(), new_id.inner(), del.inner(),
1277 "manifests", "manifest_deltas");
1278}
1279
1280void
1281database::put_manifest_reverse_version(manifest_id const & new_id,
1282 manifest_id const & old_id,
1283 manifest_delta const & del)
1284{
1285 put_reverse_version(new_id.inner(), old_id.inner(), del.inner(),
1286 "manifests", "manifest_deltas");
1287}
1288
1289
1290void
1291database::get_revision_ancestry(std::multimap<revision_id, revision_id> & graph)
1292{
1293 results res;
1294 graph.clear();
1295 fetch(res, 2, any_rows,
1296 "SELECT parent,child FROM revision_ancestry");
1297 for (size_t i = 0; i < res.size(); ++i)
1298 graph.insert(std::make_pair(revision_id(res[i][0]),
1299 revision_id(res[i][1])));
1300}
1301
1302void
1303database::get_revision_parents(revision_id const & id,
1304 set<revision_id> & parents)
1305{
1306 I(!null_id(id));
1307 results res;
1308 parents.clear();
1309 fetch(res, one_col, any_rows,
1310 "SELECT parent FROM revision_ancestry WHERE child = ?",
1311 id.inner()().c_str());
1312 for (size_t i = 0; i < res.size(); ++i)
1313 parents.insert(revision_id(res[i][0]));
1314}
1315
1316void
1317database::get_revision_children(revision_id const & id,
1318 set<revision_id> & children)
1319{
1320 I(!null_id(id));
1321 results res;
1322 children.clear();
1323 fetch(res, one_col, any_rows,
1324 "SELECT child FROM revision_ancestry WHERE parent = ?",
1325 id.inner()().c_str());
1326 for (size_t i = 0; i < res.size(); ++i)
1327 children.insert(revision_id(res[i][0]));
1328}
1329
1330void
1331database::get_revision_manifest(revision_id const & rid,
1332 manifest_id & mid)
1333{
1334 revision_set rev;
1335 get_revision(rid, rev);
1336 mid = rev.new_manifest;
1337}
1338
1339void
1340database::get_revision(revision_id const & id,
1341 revision_set & rev)
1342{
1343 revision_data d;
1344 get_revision(id, d);
1345 read_revision_set(d, rev);
1346}
1347
1348void
1349database::get_revision(revision_id const & id,
1350 revision_data & dat)
1351{
1352 I(!null_id(id));
1353 results res;
1354 fetch(res, one_col, one_row,
1355 "SELECT data FROM revisions WHERE id = ?",
1356 id.inner()().c_str());
1357
1358 base64<gzip<data> > rdat_packed;
1359 rdat_packed = base64<gzip<data> >(res[0][0]);
1360 data rdat;
1361 unpack(rdat_packed, rdat);
1362
1363 // verify that we got a revision with the right id
1364 {
1365 revision_id tmp;
1366 calculate_ident(rdat, tmp);
1367 I(id == tmp);
1368 }
1369
1370 dat = rdat;
1371}
1372
1373void
1374database::deltify_revision(revision_id const & rid)
1375{
1376 transaction_guard guard(*this);
1377 revision_set rev;
1378 get_revision(rid, rev);
1379 // make sure that all parent revs have their manifests and files
1380 // replaced with deltas from this rev's manifest and files
1381 // assume that if the manifest is already deltafied, so are the files
1382 {
1383 MM(rev.new_manifest);
1384 for (edge_map::const_iterator i = rev.edges.begin();
1385 i != rev.edges.end(); ++i)
1386 {
1387 manifest_id oldman = edge_old_manifest(i);
1388 MM(oldman);
1389 if (exists(oldman.inner(), "manifests") &&
1390 !(oldman == rev.new_manifest) &&
1391 manifest_version_exists(oldman))
1392 {
1393 manifest_data mdat_new, mdat_old;
1394 get_manifest_version(oldman, mdat_old);
1395 get_manifest_version(rev.new_manifest, mdat_new);
1396 delta delt;
1397 diff(mdat_old.inner(), mdat_new.inner(), delt);
1398 manifest_delta mdelt(delt);
1399 drop(rev.new_manifest.inner(), "manifests");
1400 drop(rev.new_manifest.inner(), "manifest_deltas");
1401 put_manifest_version(oldman, rev.new_manifest, mdelt);
1402 }
1403
1404 for (change_set::delta_map::const_iterator
1405 j = edge_changes(i).deltas.begin();
1406 j != edge_changes(i).deltas.end(); ++j)
1407 {
1408 if (! delta_entry_src(j).inner()().empty() &&
1409 exists(delta_entry_src(j).inner(), "files") &&
1410 file_version_exists(delta_entry_dst(j)))
1411 {
1412 file_data old_data;
1413 file_data new_data;
1414 get_file_version(delta_entry_src(j), old_data);
1415 get_file_version(delta_entry_dst(j), new_data);
1416 delta delt;
1417 diff(old_data.inner(), new_data.inner(), delt);
1418 file_delta del(delt);
1419 drop(delta_entry_dst(j).inner(), "files");
1420 drop(delta_entry_dst(j).inner(), "file_deltas");
1421 put_file_version(delta_entry_src(j), delta_entry_dst(j), del);
1422 }
1423 }
1424 }
1425 }
1426 guard.commit();
1427}
1428
1429void
1430database::put_revision(revision_id const & new_id,
1431 revision_set const & rev)
1432{
1433
1434 I(!null_id(new_id));
1435 I(!revision_exists(new_id));
1436 revision_data d;
1437
1438 rev.check_sane();
1439
1440 write_revision_set(rev, d);
1441 revision_id tmp;
1442 calculate_ident(d, tmp);
1443 I(tmp == new_id);
1444
1445 base64<gzip<data> > d_packed;
1446 pack(d.inner(), d_packed);
1447
1448 transaction_guard guard(*this);
1449
1450 execute("INSERT INTO revisions VALUES(?, ?)",
1451 new_id.inner()().c_str(),
1452 d_packed().c_str());
1453
1454 for (edge_map::const_iterator e = rev.edges.begin();
1455 e != rev.edges.end(); ++e)
1456 {
1457 execute("INSERT INTO revision_ancestry VALUES(?, ?)",
1458 edge_old_revision(e).inner()().c_str(),
1459 new_id.inner()().c_str());
1460 }
1461
1462 deltify_revision(new_id);
1463
1464 check_sane_history(new_id, constants::verify_depth, *__app);
1465
1466 guard.commit();
1467}
1468
1469void
1470database::put_revision(revision_id const & new_id,
1471 revision_data const & dat)
1472{
1473 revision_set rev;
1474 read_revision_set(dat, rev);
1475 put_revision(new_id, rev);
1476}
1477
1478
1479void
1480database::delete_existing_revs_and_certs()
1481{
1482 execute("DELETE FROM revisions");
1483 execute("DELETE FROM revision_ancestry");
1484 execute("DELETE FROM revision_certs");
1485}
1486
1487/// Deletes one revision from the local database.
1488/// @see kill_rev_locally
1489void
1490database::delete_existing_rev_and_certs(revision_id const & rid){
1491
1492 //check that the revision exists and doesn't have any children
1493 I(revision_exists(rid));
1494 set<revision_id> children;
1495 get_revision_children(rid, children);
1496 I(!children.size());
1497
1498 // perform the actual SQL transactions to kill rev rid here
1499 L(F("Killing revision %s locally\n") % rid);
1500 execute("DELETE from revision_certs WHERE id = ?",rid.inner()().c_str());
1501 execute("DELETE from revision_ancestry WHERE child = ?", rid.inner()().c_str());
1502 execute("DELETE from revisions WHERE id = ?",rid.inner()().c_str());
1503}
1504
1505/// Deletes all certs referring to a particular branch.
1506void
1507database::delete_branch_named(cert_value const & branch)
1508{
1509 base64<cert_value> encoded;
1510 encode_base64(branch, encoded);
1511 L(F("Deleting all references to branch %s\n") % branch);
1512 execute("DELETE FROM revision_certs WHERE name='branch' AND value =?",
1513 encoded().c_str());
1514 execute("DELETE FROM branch_epochs WHERE branch=?",
1515 encoded().c_str());
1516}
1517
1518/// Deletes all certs referring to a particular tag.
1519void
1520database::delete_tag_named(cert_value const & tag)
1521{
1522 base64<cert_value> encoded;
1523 encode_base64(tag, encoded);
1524 L(F("Deleting all references to tag %s\n") % tag);
1525 execute("DELETE FROM revision_certs WHERE name='tag' AND value =?",
1526 encoded().c_str());
1527}
1528
1529// crypto key management
1530
1531void
1532database::get_key_ids(string const & pattern,
1533 vector<rsa_keypair_id> & pubkeys)
1534{
1535 pubkeys.clear();
1536 results res;
1537
1538 if (pattern != "")
1539 fetch(res, one_col, any_rows,
1540 "SELECT id FROM public_keys WHERE id GLOB ?",
1541 pattern.c_str());
1542 else
1543 fetch(res, one_col, any_rows,
1544 "SELECT id FROM public_keys");
1545
1546 for (size_t i = 0; i < res.size(); ++i)
1547 pubkeys.push_back(res[i][0]);
1548}
1549
1550void
1551database::get_keys(string const & table, vector<rsa_keypair_id> & keys)
1552{
1553 keys.clear();
1554 results res;
1555 string query = "SELECT id FROM " + table;
1556 fetch(res, one_col, any_rows, query.c_str());
1557 for (size_t i = 0; i < res.size(); ++i)
1558 keys.push_back(res[i][0]);
1559}
1560
1561void
1562database::get_public_keys(vector<rsa_keypair_id> & keys)
1563{
1564 get_keys("public_keys", keys);
1565}
1566
1567bool
1568database::public_key_exists(hexenc<id> const & hash)
1569{
1570 results res;
1571 fetch(res, one_col, any_rows,
1572 "SELECT id FROM public_keys WHERE hash = ?",
1573 hash().c_str());
1574 I((res.size() == 1) || (res.size() == 0));
1575 if (res.size() == 1)
1576 return true;
1577 return false;
1578}
1579
1580bool
1581database::public_key_exists(rsa_keypair_id const & id)
1582{
1583 results res;
1584 fetch(res, one_col, any_rows,
1585 "SELECT id FROM public_keys WHERE id = ?",
1586 id().c_str());
1587 I((res.size() == 1) || (res.size() == 0));
1588 if (res.size() == 1)
1589 return true;
1590 return false;
1591}
1592
1593void
1594database::get_pubkey(hexenc<id> const & hash,
1595 rsa_keypair_id & id,
1596 base64<rsa_pub_key> & pub_encoded)
1597{
1598 results res;
1599 fetch(res, 2, one_row,
1600 "SELECT id, keydata FROM public_keys WHERE hash = ?",
1601 hash().c_str());
1602 id = res[0][0];
1603 pub_encoded = res[0][1];
1604}
1605
1606void
1607database::get_key(rsa_keypair_id const & pub_id,
1608 base64<rsa_pub_key> & pub_encoded)
1609{
1610 results res;
1611 fetch(res, one_col, one_row,
1612 "SELECT keydata FROM public_keys WHERE id = ?",
1613 pub_id().c_str());
1614 pub_encoded = res[0][0];
1615}
1616
1617void
1618database::put_key(rsa_keypair_id const & pub_id,
1619 base64<rsa_pub_key> const & pub_encoded)
1620{
1621 hexenc<id> thash;
1622 key_hash_code(pub_id, pub_encoded, thash);
1623 I(!public_key_exists(thash));
1624 E(!public_key_exists(pub_id),
1625 F("another key with name '%s' already exists") % pub_id);
1626 execute("INSERT INTO public_keys VALUES(?, ?, ?)",
1627 thash().c_str(), pub_id().c_str(), pub_encoded().c_str());
1628}
1629
1630void
1631database::delete_public_key(rsa_keypair_id const & pub_id)
1632{
1633 execute("DELETE FROM public_keys WHERE id = ?",
1634 pub_id().c_str());
1635}
1636
1637// cert management
1638
1639bool
1640database::cert_exists(cert const & t,
1641 string const & table)
1642{
1643 results res;
1644 string query =
1645 "SELECT id FROM " + table + " WHERE id = ? "
1646 "AND name = ? "
1647 "AND value = ? "
1648 "AND keypair = ? "
1649 "AND signature = ?";
1650
1651 fetch(res, 1, any_rows, query.c_str(),
1652 t.ident().c_str(),
1653 t.name().c_str(),
1654 t.value().c_str(),
1655 t.key().c_str(),
1656 t.sig().c_str());
1657 I(res.size() == 0 || res.size() == 1);
1658 return res.size() == 1;
1659}
1660
1661void
1662database::put_cert(cert const & t,
1663 string const & table)
1664{
1665 hexenc<id> thash;
1666 cert_hash_code(t, thash);
1667
1668 string insert = "INSERT INTO " + table + " VALUES(?, ?, ?, ?, ?, ?)";
1669
1670 execute(insert.c_str(),
1671 thash().c_str(),
1672 t.ident().c_str(),
1673 t.name().c_str(),
1674 t.value().c_str(),
1675 t.key().c_str(),
1676 t.sig().c_str());
1677}
1678
1679void
1680database::results_to_certs(results const & res,
1681 vector<cert> & certs)
1682{
1683 certs.clear();
1684 for (size_t i = 0; i < res.size(); ++i)
1685 {
1686 cert t;
1687 t = cert(hexenc<id>(res[i][0]),
1688 cert_name(res[i][1]),
1689 base64<cert_value>(res[i][2]),
1690 rsa_keypair_id(res[i][3]),
1691 base64<rsa_sha1_signature>(res[i][4]));
1692 certs.push_back(t);
1693 }
1694}
1695
1696void
1697database::install_functions(app_state * app)
1698{
1699 // register any functions we're going to use
1700 I(sqlite3_create_function(sql(), "unbase64", -1,
1701 SQLITE_UTF8, NULL,
1702 &sqlite3_unbase64_fn,
1703 NULL, NULL) == 0);
1704 I(sqlite3_create_function(sql(), "unpack", -1,
1705 SQLITE_UTF8, NULL,
1706 &sqlite3_unpack_fn,
1707 NULL, NULL) == 0);
1708}
1709
1710void
1711database::install_views()
1712{
1713 // we don't currently use any views. re-enable this code if you find a
1714 // compelling reason to use views.
1715
1716 /*
1717 // delete any existing views
1718 results res;
1719 fetch(res, one_col, any_rows,
1720 "SELECT name FROM sqlite_master WHERE type='view'");
1721
1722 for (size_t i = 0; i < res.size(); ++i)
1723 {
1724 string drop = "DROP VIEW " + res[i][0];
1725 execute(drop.c_str());
1726 }
1727 // register any views we're going to use
1728 execute(views_constant);
1729 */
1730}
1731
1732void
1733database::get_certs(vector<cert> & certs,
1734 string const & table)
1735{
1736 results res;
1737 string query = "SELECT id, name, value, keypair, signature FROM " + table;
1738 fetch(res, 5, any_rows, query.c_str());
1739 results_to_certs(res, certs);
1740}
1741
1742
1743void
1744database::get_certs(hexenc<id> const & ident,
1745 vector<cert> & certs,
1746 string const & table)
1747{
1748 results res;
1749 string query =
1750 "SELECT id, name, value, keypair, signature FROM " + table +
1751 " WHERE id = ?";
1752
1753 fetch(res, 5, any_rows, query.c_str(), ident().c_str());
1754 results_to_certs(res, certs);
1755}
1756
1757
1758void
1759database::get_certs(cert_name const & name,
1760 vector<cert> & certs,
1761 string const & table)
1762{
1763 results res;
1764 string query =
1765 "SELECT id, name, value, keypair, signature FROM " + table +
1766 " WHERE name = ?";
1767 fetch(res, 5, any_rows, query.c_str(), name().c_str());
1768 results_to_certs(res, certs);
1769}
1770
1771
1772void
1773database::get_certs(hexenc<id> const & ident,
1774 cert_name const & name,
1775 vector<cert> & certs,
1776 string const & table)
1777{
1778 results res;
1779 string query =
1780 "SELECT id, name, value, keypair, signature FROM " + table +
1781 " WHERE id = ? AND name = ?";
1782
1783 fetch(res, 5, any_rows, query.c_str(),
1784 ident().c_str(), name().c_str());
1785 results_to_certs(res, certs);
1786}
1787
1788void
1789database::get_certs(cert_name const & name,
1790 base64<cert_value> const & val,
1791 vector<cert> & certs,
1792 string const & table)
1793{
1794 results res;
1795 string query =
1796 "SELECT id, name, value, keypair, signature FROM " + table +
1797 " WHERE name = ? AND value = ?";
1798
1799 fetch(res, 5, any_rows, query.c_str(),
1800 name().c_str(), val().c_str());
1801 results_to_certs(res, certs);
1802}
1803
1804
1805void
1806database::get_certs(hexenc<id> const & ident,
1807 cert_name const & name,
1808 base64<cert_value> const & value,
1809 vector<cert> & certs,
1810 string const & table)
1811{
1812 results res;
1813 string query =
1814 "SELECT id, name, value, keypair, signature FROM " + table +
1815 " WHERE id = ? AND name = ? AND value = ?";
1816
1817 fetch(res, 5, any_rows, query.c_str(),
1818 ident().c_str(),
1819 name().c_str(),
1820 value().c_str());
1821 results_to_certs(res, certs);
1822}
1823
1824
1825
1826bool
1827database::revision_cert_exists(revision<cert> const & cert)
1828{
1829 return cert_exists(cert.inner(), "revision_certs");
1830}
1831
1832bool
1833database::manifest_cert_exists(manifest<cert> const & cert)
1834{
1835 return cert_exists(cert.inner(), "manifest_certs");
1836}
1837
1838void
1839database::put_manifest_cert(manifest<cert> const & cert)
1840{
1841 put_cert(cert.inner(), "manifest_certs");
1842}
1843
1844void
1845database::put_revision_cert(revision<cert> const & cert)
1846{
1847 put_cert(cert.inner(), "revision_certs");
1848}
1849
1850void database::get_revision_cert_nobranch_index(std::vector< std::pair<hexenc<id>,
1851 std::pair<revision_id, rsa_keypair_id> > > & idx)
1852{
1853 results res;
1854 fetch(res, 3, any_rows,
1855 "SELECT hash, id, keypair "
1856 "FROM 'revision_certs' WHERE name != 'branch'");
1857
1858 idx.clear();
1859 idx.reserve(res.size());
1860 for (results::const_iterator i = res.begin(); i != res.end(); ++i)
1861 {
1862 idx.push_back(std::make_pair(hexenc<id>((*i)[0]),
1863 std::make_pair(revision_id((*i)[1]),
1864 rsa_keypair_id((*i)[2]))));
1865 }
1866}
1867
1868void
1869database::get_revision_certs(vector< revision<cert> > & ts)
1870{
1871 vector<cert> certs;
1872 get_certs(certs, "revision_certs");
1873 ts.clear();
1874 copy(certs.begin(), certs.end(), back_inserter(ts));
1875}
1876
1877void
1878database::get_revision_certs(cert_name const & name,
1879 vector< revision<cert> > & ts)
1880{
1881 vector<cert> certs;
1882 get_certs(name, certs, "revision_certs");
1883 ts.clear();
1884 copy(certs.begin(), certs.end(), back_inserter(ts));
1885}
1886
1887void
1888database::get_revision_certs(revision_id const & id,
1889 cert_name const & name,
1890 vector< revision<cert> > & ts)
1891{
1892 vector<cert> certs;
1893 get_certs(id.inner(), name, certs, "revision_certs");
1894 ts.clear();
1895 copy(certs.begin(), certs.end(), back_inserter(ts));
1896}
1897
1898void
1899database::get_revision_certs(revision_id const & id,
1900 cert_name const & name,
1901 base64<cert_value> const & val,
1902 vector< revision<cert> > & ts)
1903{
1904 vector<cert> certs;
1905 get_certs(id.inner(), name, val, certs, "revision_certs");
1906 ts.clear();
1907 copy(certs.begin(), certs.end(), back_inserter(ts));
1908}
1909
1910void
1911database::get_revision_certs(cert_name const & name,
1912 base64<cert_value> const & val,
1913 vector< revision<cert> > & ts)
1914{
1915 vector<cert> certs;
1916 get_certs(name, val, certs, "revision_certs");
1917 ts.clear();
1918 copy(certs.begin(), certs.end(), back_inserter(ts));
1919}
1920
1921void
1922database::get_revision_certs(revision_id const & id,
1923 vector< revision<cert> > & ts)
1924{
1925 vector<cert> certs;
1926 get_certs(id.inner(), certs, "revision_certs");
1927 ts.clear();
1928 copy(certs.begin(), certs.end(), back_inserter(ts));
1929}
1930
1931void
1932database::get_revision_cert(hexenc<id> const & hash,
1933 revision<cert> & c)
1934{
1935 results res;
1936 vector<cert> certs;
1937 fetch(res, 5, one_row,
1938 "SELECT id, name, value, keypair, signature "
1939 "FROM revision_certs "
1940 "WHERE hash = ?",
1941 hash().c_str());
1942 results_to_certs(res, certs);
1943 I(certs.size() == 1);
1944 c = revision<cert>(certs[0]);
1945}
1946
1947bool
1948database::revision_cert_exists(hexenc<id> const & hash)
1949{
1950 results res;
1951 vector<cert> certs;
1952 fetch(res, one_col, any_rows,
1953 "SELECT id "
1954 "FROM revision_certs "
1955 "WHERE hash = ?",
1956 hash().c_str());
1957 I(res.size() == 0 || res.size() == 1);
1958 return (res.size() == 1);
1959}
1960
1961bool
1962database::manifest_cert_exists(hexenc<id> const & hash)
1963{
1964 results res;
1965 vector<cert> certs;
1966 fetch(res, one_col, any_rows,
1967 "SELECT id "
1968 "FROM manifest_certs "
1969 "WHERE hash = ?",
1970 hash().c_str());
1971 I(res.size() == 0 || res.size() == 1);
1972 return (res.size() == 1);
1973}
1974
1975void
1976database::get_manifest_cert(hexenc<id> const & hash,
1977 manifest<cert> & c)
1978{
1979 results res;
1980 vector<cert> certs;
1981 fetch(res, 5, one_row,
1982 "SELECT id, name, value, keypair, signature "
1983 "FROM manifest_certs "
1984 "WHERE hash = ?",
1985 hash().c_str());
1986 results_to_certs(res, certs);
1987 I(certs.size() == 1);
1988 c = manifest<cert>(certs[0]);
1989}
1990
1991void
1992database::get_manifest_certs(manifest_id const & id,
1993 vector< manifest<cert> > & ts)
1994{
1995 vector<cert> certs;
1996 get_certs(id.inner(), certs, "manifest_certs");
1997 ts.clear();
1998 copy(certs.begin(), certs.end(), back_inserter(ts));
1999}
2000
2001
2002void
2003database::get_manifest_certs(cert_name const & name,
2004 vector< manifest<cert> > & ts)
2005{
2006 vector<cert> certs;
2007 get_certs(name, certs, "manifest_certs");
2008 ts.clear();
2009 copy(certs.begin(), certs.end(), back_inserter(ts));
2010}
2011
2012void
2013database::get_manifest_certs(manifest_id const & id,
2014 cert_name const & name,
2015 vector< manifest<cert> > & ts)
2016{
2017 vector<cert> certs;
2018 get_certs(id.inner(), name, certs, "manifest_certs");
2019 ts.clear();
2020 copy(certs.begin(), certs.end(), back_inserter(ts));
2021}
2022
2023
2024// completions
2025void
2026database::complete(string const & partial,
2027 set<revision_id> & completions)
2028{
2029 results res;
2030 completions.clear();
2031
2032 string pattern = partial + "*";
2033
2034 fetch(res, 1, any_rows,
2035 "SELECT id FROM revisions WHERE id GLOB ?",
2036 pattern.c_str());
2037
2038 for (size_t i = 0; i < res.size(); ++i)
2039 completions.insert(revision_id(res[i][0]));
2040}
2041
2042
2043void
2044database::complete(string const & partial,
2045 set<manifest_id> & completions)
2046{
2047 results res;
2048 completions.clear();
2049
2050 string pattern = partial + "*";
2051
2052 fetch(res, 1, any_rows,
2053 "SELECT id FROM manifests WHERE id GLOB ?",
2054 pattern.c_str());
2055
2056 for (size_t i = 0; i < res.size(); ++i)
2057 completions.insert(manifest_id(res[i][0]));
2058
2059 res.clear();
2060
2061 fetch(res, 1, any_rows,
2062 "SELECT id FROM manifest_deltas WHERE id GLOB ?",
2063 pattern.c_str());
2064
2065 for (size_t i = 0; i < res.size(); ++i)
2066 completions.insert(manifest_id(res[i][0]));
2067}
2068
2069void
2070database::complete(string const & partial,
2071 set<file_id> & completions)
2072{
2073 results res;
2074 completions.clear();
2075
2076 string pattern = partial + "*";
2077
2078 fetch(res, 1, any_rows,
2079 "SELECT id FROM files WHERE id GLOB ?",
2080 pattern.c_str());
2081
2082 for (size_t i = 0; i < res.size(); ++i)
2083 completions.insert(file_id(res[i][0]));
2084
2085 res.clear();
2086
2087 fetch(res, 1, any_rows,
2088 "SELECT id FROM file_deltas WHERE id GLOB ?",
2089 pattern.c_str());
2090
2091 for (size_t i = 0; i < res.size(); ++i)
2092 completions.insert(file_id(res[i][0]));
2093}
2094
2095void
2096database::complete(string const & partial,
2097 set< pair<key_id, utf8 > > & completions)
2098{
2099 results res;
2100 completions.clear();
2101
2102 string pattern = partial + "*";
2103
2104 fetch(res, 2, any_rows,
2105 "SELECT hash, id FROM public_keys WHERE hash GLOB ?",
2106 pattern.c_str());
2107
2108 for (size_t i = 0; i < res.size(); ++i)
2109 completions.insert(make_pair(key_id(res[i][0]), utf8(res[i][1])));
2110}
2111
2112using selectors::selector_type;
2113
2114static void selector_to_certname(selector_type ty,
2115 string & s,
2116 string & prefix,
2117 string & suffix)
2118{
2119 prefix = suffix = "*";
2120 switch (ty)
2121 {
2122 case selectors::sel_author:
2123 s = author_cert_name;
2124 break;
2125 case selectors::sel_branch:
2126 prefix = suffix = "";
2127 s = branch_cert_name;
2128 break;
2129 case selectors::sel_head:
2130 prefix = suffix = "";
2131 s = branch_cert_name;
2132 break;
2133 case selectors::sel_date:
2134 case selectors::sel_later:
2135 case selectors::sel_earlier:
2136 s = date_cert_name;
2137 break;
2138 case selectors::sel_tag:
2139 prefix = suffix = "";
2140 s = tag_cert_name;
2141 break;
2142 case selectors::sel_ident:
2143 case selectors::sel_cert:
2144 case selectors::sel_unknown:
2145 I(false); // don't do this.
2146 break;
2147 }
2148}
2149
2150void database::complete(selector_type ty,
2151 string const & partial,
2152 vector<pair<selector_type, string> > const & limit,
2153 set<string> & completions)
2154{
2155 //L(F("database::complete for partial '%s'\n") % partial);
2156 completions.clear();
2157
2158 // step 1: the limit is transformed into an SQL select statement which
2159 // selects a set of IDs from the manifest_certs table which match the
2160 // limit. this is done by building an SQL select statement for each term
2161 // in the limit and then INTERSECTing them all.
2162
2163 string lim = "(";
2164 if (limit.empty())
2165 {
2166 lim += "SELECT id FROM revision_certs";
2167 }
2168 else
2169 {
2170 bool first_limit = true;
2171 for (vector<pair<selector_type, string> >::const_iterator i = limit.begin();
2172 i != limit.end(); ++i)
2173 {
2174 if (first_limit)
2175 first_limit = false;
2176 else
2177 lim += " INTERSECT ";
2178
2179 if (i->first == selectors::sel_ident)
2180 {
2181 lim += "SELECT id FROM revision_certs ";
2182 lim += (boost::format("WHERE id GLOB '%s*'")
2183 % i->second).str();
2184 }
2185 else if (i->first == selectors::sel_cert)
2186 {
2187 if (i->second.length() > 0)
2188 {
2189 size_t spot = i->second.find("=");
2190
2191 if (spot != (size_t)-1)
2192 {
2193 string certname;
2194 string certvalue;
2195
2196 certname = i->second.substr(0, spot);
2197 spot++;
2198 certvalue = i->second.substr(spot);
2199 lim += "SELECT id FROM revision_certs ";
2200 lim += (boost::format("WHERE name='%s' AND unbase64(value) glob '%s'")
2201 % certname % certvalue).str();
2202 }
2203 else
2204 {
2205 lim += "SELECT id FROM revision_certs ";
2206 lim += (boost::format("WHERE name='%s'")
2207 % i->second).str();
2208 }
2209
2210 }
2211 }
2212 else if (i->first == selectors::sel_unknown)
2213 {
2214 lim += "SELECT id FROM revision_certs ";
2215 lim += (boost::format(" WHERE (name='%s' OR name='%s' OR name='%s')")
2216 % author_cert_name
2217 % tag_cert_name
2218 % branch_cert_name).str();
2219 lim += (boost::format(" AND unbase64(value) glob '*%s*'")
2220 % i->second).str();
2221 }
2222 else if (i->first == selectors::sel_head)
2223 {
2224 // get branch names
2225 vector<cert_value> branch_names;
2226 if (i->second.size() == 0)
2227 {
2228 __app->require_working_copy("the empty head selector h: refers to the head of the current branch");
2229 branch_names.push_back((__app->branch_name)());
2230 }
2231 else
2232 {
2233 string subquery = (boost::format("SELECT DISTINCT value FROM revision_certs WHERE name='%s' and unbase64(value) glob '%s'")
2234 % branch_cert_name % i->second).str();
2235 results res;
2236 fetch(res, one_col, any_rows, subquery.c_str());
2237 for (size_t i = 0; i < res.size(); ++i)
2238 {
2239 base64<data> row_encoded(res[i][0]);
2240 data row_decoded;
2241 decode_base64(row_encoded, row_decoded);
2242 branch_names.push_back(row_decoded());
2243 }
2244 }
2245
2246 // for each branch name, get the branch heads
2247 set<revision_id> heads;
2248 for (vector<cert_value>::const_iterator bn = branch_names.begin(); bn != branch_names.end(); bn++)
2249 {
2250 set<revision_id> branch_heads;
2251 get_branch_heads(*bn, *__app, branch_heads);
2252 heads.insert(branch_heads.begin(), branch_heads.end());
2253 L(F("after get_branch_heads for %s, heads has %d entries\n") % (*bn) % heads.size());
2254 }
2255
2256 lim += "SELECT id FROM revision_certs WHERE id IN (";
2257 if (heads.size())
2258 {
2259 set<revision_id>::const_iterator r = heads.begin();
2260 lim += (boost::format("'%s'") % r->inner()()).str();
2261 r++;
2262 while (r != heads.end())
2263 {
2264 lim += (boost::format(", '%s'") % r->inner()()).str();
2265 r++;
2266 }
2267 }
2268 lim += ") ";
2269 }
2270 else
2271 {
2272 string certname;
2273 string prefix;
2274 string suffix;
2275 selector_to_certname(i->first, certname, prefix, suffix);
2276 L(F("processing selector type %d with i->second '%s'\n") % ty % i->second);
2277 if ((i->first == selectors::sel_branch) && (i->second.size() == 0))
2278 {
2279 __app->require_working_copy("the empty branch selector b: refers to the current branch");
2280 // FIXME: why do we have to glob on the unbase64(value), rather than being able to use == ?
2281 lim += (boost::format("SELECT id FROM revision_certs WHERE name='%s' AND unbase64(value) glob '%s'")
2282 % branch_cert_name % __app->branch_name).str();
2283 L(F("limiting to current branch '%s'\n") % __app->branch_name);
2284 }
2285 else
2286 {
2287 lim += (boost::format("SELECT id FROM revision_certs WHERE name='%s' AND ") % certname).str();
2288 switch (i->first)
2289 {
2290 case selectors::sel_earlier:
2291 lim += (boost::format("unbase64(value) <= X'%s'") % encode_hexenc(i->second)).str();
2292 break;
2293 case selectors::sel_later:
2294 lim += (boost::format("unbase64(value) > X'%s'") % encode_hexenc(i->second)).str();
2295 break;
2296 default:
2297 lim += (boost::format("unbase64(value) glob '%s%s%s'")
2298 % prefix % i->second % suffix).str();
2299 break;
2300 }
2301 }
2302 }
2303 //L(F("found selector type %d, selecting_head is now %d\n") % i->first % selecting_head);
2304 }
2305 }
2306 lim += ")";
2307
2308 // step 2: depending on what we've been asked to disambiguate, we
2309 // will complete either some idents, or cert values, or "unknown"
2310 // which generally means "author, tag or branch"
2311
2312 string query;
2313 if (ty == selectors::sel_ident)
2314 {
2315 query = (boost::format("SELECT id FROM %s") % lim).str();
2316 }
2317 else
2318 {
2319 string prefix = "*";
2320 string suffix = "*";
2321 query = "SELECT value FROM revision_certs WHERE";
2322 if (ty == selectors::sel_unknown)
2323 {
2324 query +=
2325 (boost::format(" (name='%s' OR name='%s' OR name='%s')")
2326 % author_cert_name
2327 % tag_cert_name
2328 % branch_cert_name).str();
2329 }
2330 else
2331 {
2332 string certname;
2333 selector_to_certname(ty, certname, prefix, suffix);
2334 query +=
2335 (boost::format(" (name='%s')") % certname).str();
2336 }
2337
2338 query += (boost::format(" AND (unbase64(value) GLOB '%s%s%s')")
2339 % prefix % partial % suffix).str();
2340 query += (boost::format(" AND (id IN %s)") % lim).str();
2341 }
2342
2343 // std::cerr << query << std::endl; // debug expr
2344
2345 results res;
2346 fetch(res, one_col, any_rows, query.c_str());
2347 for (size_t i = 0; i < res.size(); ++i)
2348 {
2349 if (ty == selectors::sel_ident)
2350 completions.insert(res[i][0]);
2351 else
2352 {
2353 base64<data> row_encoded(res[i][0]);
2354 data row_decoded;
2355 decode_base64(row_encoded, row_decoded);
2356 completions.insert(row_decoded());
2357 }
2358 }
2359}
2360
2361// epochs
2362
2363void
2364database::get_epochs(std::map<cert_value, epoch_data> & epochs)
2365{
2366 epochs.clear();
2367 results res;
2368 fetch(res, 2, any_rows, "SELECT branch, epoch FROM branch_epochs");
2369 for (results::const_iterator i = res.begin(); i != res.end(); ++i)
2370 {
2371 base64<cert_value> encoded(idx(*i, 0));
2372 cert_value decoded;
2373 decode_base64(encoded, decoded);
2374 I(epochs.find(decoded) == epochs.end());
2375 epochs.insert(std::make_pair(decoded, epoch_data(idx(*i, 1))));
2376 }
2377}
2378
2379void
2380database::get_epoch(epoch_id const & eid,
2381 cert_value & branch, epoch_data & epo)
2382{
2383 I(epoch_exists(eid));
2384 results res;
2385 fetch(res, 2, any_rows,
2386 "SELECT branch, epoch FROM branch_epochs"
2387 " WHERE hash = ?",
2388 eid.inner()().c_str());
2389 I(res.size() == 1);
2390 base64<cert_value> encoded(idx(idx(res, 0), 0));
2391 decode_base64(encoded, branch);
2392 epo = epoch_data(idx(idx(res, 0), 1));
2393}
2394
2395bool
2396database::epoch_exists(epoch_id const & eid)
2397{
2398 results res;
2399 fetch(res, one_col, any_rows,
2400 "SELECT hash FROM branch_epochs WHERE hash = ?",
2401 eid.inner()().c_str());
2402 I(res.size() == 1 || res.size() == 0);
2403 return res.size() == 1;
2404}
2405
2406void
2407database::set_epoch(cert_value const & branch, epoch_data const & epo)
2408{
2409 epoch_id eid;
2410 base64<cert_value> encoded;
2411 encode_base64(branch, encoded);
2412 epoch_hash_code(branch, epo, eid);
2413 I(epo.inner()().size() == constants::epochlen);
2414 execute("INSERT OR REPLACE INTO branch_epochs VALUES(?, ?, ?)",
2415 eid.inner()().c_str(), encoded().c_str(), epo.inner()().c_str());
2416}
2417
2418void
2419database::clear_epoch(cert_value const & branch)
2420{
2421 base64<cert_value> encoded;
2422 encode_base64(branch, encoded);
2423 execute("DELETE FROM branch_epochs WHERE branch = ?", encoded().c_str());
2424}
2425
2426// vars
2427
2428void
2429database::get_vars(std::map<var_key, var_value> & vars)
2430{
2431 vars.clear();
2432 results res;
2433 fetch(res, 3, any_rows, "SELECT domain, name, value FROM db_vars");
2434 for (results::const_iterator i = res.begin(); i != res.end(); ++i)
2435 {
2436 var_domain domain(idx(*i, 0));
2437 base64<var_name> name_encoded(idx(*i, 1));
2438 var_name name;
2439 decode_base64(name_encoded, name);
2440 base64<var_value> value_encoded(idx(*i, 2));
2441 var_value value;
2442 decode_base64(value_encoded, value);
2443 I(vars.find(std::make_pair(domain, name)) == vars.end());
2444 vars.insert(std::make_pair(std::make_pair(domain, name), value));
2445 }
2446}
2447
2448void
2449database::get_var(var_key const & key, var_value & value)
2450{
2451 // FIXME: sillyly inefficient. Doesn't really matter, though.
2452 std::map<var_key, var_value> vars;
2453 get_vars(vars);
2454 std::map<var_key, var_value>::const_iterator i = vars.find(key);
2455 I(i != vars.end());
2456 value = i->second;
2457}
2458
2459bool
2460database::var_exists(var_key const & key)
2461{
2462 // FIXME: sillyly inefficient. Doesn't really matter, though.
2463 std::map<var_key, var_value> vars;
2464 get_vars(vars);
2465 std::map<var_key, var_value>::const_iterator i = vars.find(key);
2466 return i != vars.end();
2467}
2468
2469void
2470database::set_var(var_key const & key, var_value const & value)
2471{
2472 base64<var_name> name_encoded;
2473 encode_base64(key.second, name_encoded);
2474 base64<var_value> value_encoded;
2475 encode_base64(value, value_encoded);
2476 execute("INSERT OR REPLACE INTO db_vars VALUES(?, ?, ?)",
2477 key.first().c_str(),
2478 name_encoded().c_str(),
2479 value_encoded().c_str());
2480}
2481
2482void
2483database::clear_var(var_key const & key)
2484{
2485 base64<var_name> name_encoded;
2486 encode_base64(key.second, name_encoded);
2487 execute("DELETE FROM db_vars WHERE domain = ? AND name = ?",
2488 key.first().c_str(), name_encoded().c_str());
2489}
2490
2491// branches
2492
2493void
2494database::get_branches(vector<string> & names)
2495{
2496 results res;
2497 string query="SELECT DISTINCT value FROM revision_certs WHERE name= ?";
2498 string cert_name="branch";
2499 fetch(res,one_col,any_rows,query.c_str(),cert_name.c_str());
2500 for (size_t i = 0; i < res.size(); ++i)
2501 {
2502 base64<data> row_encoded(res[i][0]);
2503 data name;
2504 decode_base64(row_encoded, name);
2505 names.push_back(name());
2506 }
2507}
2508
2509
2510void
2511database::check_filename()
2512{
2513 N(!filename.empty(), F("no database specified"));
2514}
2515
2516
2517void
2518database::check_db_exists()
2519{
2520 require_path_is_file(filename,
2521 F("database %s does not exist") % filename,
2522 F("%s is a directory, not a database") % filename);
2523}
2524
2525bool
2526database::database_specified()
2527{
2528 return !filename.empty();
2529}
2530
2531
2532void
2533database::open()
2534{
2535 int error;
2536
2537 I(!__sql);
2538
2539 error = sqlite3_open(filename.as_external().c_str(), &__sql);
2540
2541 if (__sql)
2542 {
2543 I(sql_contexts.find(__sql) == sql_contexts.end());
2544 sql_contexts.insert(__sql);
2545 }
2546
2547 N(!error, (F("could not open database '%s': %s")
2548 % filename % string(sqlite3_errmsg(__sql))));
2549}
2550
2551void
2552database::close()
2553{
2554 if (__sql)
2555 {
2556 sqlite3_close(__sql);
2557 I(sql_contexts.find(__sql) != sql_contexts.end());
2558 sql_contexts.erase(__sql);
2559 __sql = 0;
2560 }
2561}
2562
2563
2564// transaction guards
2565
2566transaction_guard::transaction_guard(database & d, bool exclusive) : committed(false), db(d)
2567{
2568 db.begin_transaction(exclusive);
2569}
2570
2571transaction_guard::~transaction_guard()
2572{
2573 if (committed)
2574 db.commit_transaction();
2575 else
2576 db.rollback_transaction();
2577}
2578
2579void
2580transaction_guard::commit()
2581{
2582 committed = true;
2583}
2584
2585
2586
2587// called to avoid foo.db-journal files hanging around if we exit cleanly
2588// without unwinding the stack (happens with SIGINT & SIGTERM)
2589void
2590close_all_databases()
2591{
2592 L(F("attempting to rollback and close %d databases") % sql_contexts.size());
2593 for (set<sqlite3*>::iterator i = sql_contexts.begin();
2594 i != sql_contexts.end(); i++)
2595 {
2596 // the ROLLBACK is required here, even though the sqlite docs
2597 // imply that transactions are rolled back on database closure
2598 int exec_err = sqlite3_exec(*i, "ROLLBACK", NULL, NULL, NULL);
2599 int close_err = sqlite3_close(*i);
2600
2601 L(F("exec_err = %d, close_err = %d") % exec_err % close_err);
2602 }
2603 sql_contexts.clear();
2604}

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status