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

Archive Download this file

Branches

Tags

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