monotone

monotone Mtn Source Tree

Root/database.cc

1// copyright (C) 2002, 2003 graydon hoare <graydon@pobox.com>
2// all rights reserved.
3// licensed to the public under the terms of the GNU GPL (>= 2)
4// see the file COPYING for details
5
6#include <iterator>
7#include <list>
8#include <set>
9#include <sstream>
10#include <vector>
11
12#include <stdarg.h>
13
14#include <boost/shared_ptr.hpp>
15#include <boost/lexical_cast.hpp>
16#include <boost/filesystem/path.hpp>
17#include <boost/filesystem/operations.hpp>
18
19#include "cert.hh"
20#include "cleanup.hh"
21#include "constants.hh"
22#include "database.hh"
23#include "keys.hh"
24#include "nntp_tasks.hh"
25#include "sanity.hh"
26#include "schema_migration.hh"
27#include "cert.hh"
28#include "transforms.hh"
29#include "ui.hh"
30#include "vocab.hh"
31#include "xdelta.hh"
32
33#include "sqlite/sqlite.h"
34
35// defined in schema.sql, converted to header:
36#include "schema.h"
37
38// this file defines a public, typed interface to the database.
39// the database class encapsulates all knowledge about sqlite,
40// the schema, and all SQL statements used to access the schema.
41//
42// see file schema.sql for the text of the schema.
43
44using boost::shared_ptr;
45using boost::lexical_cast;
46using namespace std;
47
48int const one_row = 1;
49int const one_col = 1;
50int const any_rows = -1;
51int const any_cols = -1;
52
53extern "C" {
54 // strangely this isn't declared, even though it's present in my sqlite.
55 char *sqlite_vmprintf(const char *zFormat, va_list);
56}
57
58database::database(fs::path const & fn) :
59 filename(fn),
60 // nb. update this if you change the schema. unfortunately we are not
61 // using self-digesting schemas due to comment irregularities and
62 // non-alphabetic ordering of tables in sql source files. we could create
63 // a temporary db, write our intended schema into it, and read it back,
64 // but this seems like it would be too rude. possibly revisit this issue.
65 schema("8929e54f40bf4d3b4aea8b037d2c9263e82abdf4"),
66 __sql(NULL),
67 transaction_level(0)
68{}
69
70void database::check_schema()
71{
72 string db_schema_id;
73 calculate_schema_id (__sql, db_schema_id);
74 N (schema == db_schema_id,
75 F("database schemas do not match: "
76 "wanted %s, got %s. try migrating database")
77 % schema % db_schema_id);
78}
79
80struct sqlite * database::sql(bool init)
81{
82 if (! __sql)
83 {
84 if (! init)
85{
86 if (filename.string() == "")
87 throw informative_failure(string("no database specified"));
88 else if (! fs::exists(filename))
89 throw informative_failure(string("database ") + filename.string() +
90 string(" does not exist"));
91}
92 N(filename.string() != "",
93F("need database name"));
94 char * errmsg = NULL;
95 __sql = sqlite_open(filename.string().c_str(), 0755, &errmsg);
96 if (! __sql)
97throw oops(string("could not open database: ") + filename.string() +
98 (errmsg ? (": " + string(errmsg)) : ""));
99 if (init)
100execute (schema_constant);
101
102 check_schema();
103 }
104 return __sql;
105}
106
107
108void database::initialize()
109{
110 if (__sql)
111 throw oops("cannot initialize database while it is open");
112
113 N(!fs::exists(filename),
114 F("could not initialize database: %s: already exists")
115 % filename.string());
116
117 fs::path journal = mkpath(filename.string() + "-journal");
118 N(!fs::exists(journal),
119 F("existing (possibly stale) journal file '%s' has same stem as new database '%s'")
120 % journal.string() % filename.string());
121
122 sqlite *s = sql(true);
123 I(s != NULL);
124}
125
126
127struct dump_request
128{
129 dump_request() {};
130 struct sqlite *sql;
131 string table_name;
132 ostream *out;
133};
134
135static int dump_row_cb(void *data, int n, char **vals, char **cols)
136{
137 dump_request *dump = reinterpret_cast<dump_request *>(data);
138 I(dump != NULL);
139 I(vals != NULL);
140 I(dump->out != NULL);
141 *(dump->out) << F("INSERT INTO %s VALUES(") % dump->table_name;
142 for (int i = 0; i < n; ++i)
143 {
144 if (i != 0)
145*(dump->out) << ',';
146
147 if (vals[i] == NULL)
148*(dump->out) << "NULL";
149 else
150{
151 *(dump->out) << "'";
152 for (char *cp = vals[i]; *cp; ++cp)
153 {
154 if (*cp == '\'')
155*(dump->out) << "''";
156 else
157*(dump->out) << *cp;
158 }
159 *(dump->out) << "'";
160}
161 }
162 *(dump->out) << ");\n";
163 return 0;
164}
165
166static int dump_table_cb(void *data, int n, char **vals, char **cols)
167{
168 dump_request *dump = reinterpret_cast<dump_request *>(data);
169 I(dump != NULL);
170 I(dump->sql != NULL);
171 I(vals != NULL);
172 I(vals[0] != NULL);
173 I(vals[1] != NULL);
174 I(vals[2] != NULL);
175 I(n == 3);
176 *(dump->out) << vals[2] << ";\n";
177 if (string(vals[1]) == "table")
178 {
179 dump->table_name = string(vals[0]);
180 sqlite_exec_printf(dump->sql, "SELECT * FROM '%q'",
181 dump_row_cb, data, NULL, vals[0]);
182 }
183 return 0;
184}
185
186void database::dump(ostream & out)
187{
188 dump_request req;
189 req.out = &out;
190 req.sql = sql();
191 out << "BEGIN TRANSACTION;\n";
192 int res = sqlite_exec(req.sql,
193"SELECT name, type, sql FROM sqlite_master "
194"WHERE type!='meta' AND sql NOT NULL "
195"ORDER BY substr(type,2,1), name",
196dump_table_cb, &req, NULL);
197 I(res == SQLITE_OK);
198 out << "COMMIT;\n";
199}
200
201void database::load(istream & in)
202{
203 char buf[constants::bufsz];
204 string tmp;
205
206 N(filename.string() != "",
207 F("need database name"));
208 char * errmsg = NULL;
209 __sql = sqlite_open(filename.string().c_str(), 0755, &errmsg);
210 if (! __sql)
211 throw oops(string("could not open database: ") + filename.string() +
212 (errmsg ? (": " + string(errmsg)) : ""));
213
214 while(in)
215 {
216 in.read(buf, constants::bufsz);
217 tmp.append(buf, in.gcount());
218 }
219
220 execute(tmp.c_str());
221}
222
223
224void database::debug(string const & sql, ostream & out)
225{
226 results res;
227 fetch(res, any_cols, any_rows, sql.c_str());
228 out << "'" << sql << "' -> " << res.size() << " rows\n" << endl;
229 for (size_t i = 0; i < res.size(); ++i)
230 {
231 for (size_t j = 0; j < res[i].size(); ++j)
232{
233 if (j != 0)
234 out << " | ";
235 out << res[i][j];
236}
237 out << endl;
238 }
239}
240
241unsigned long database::get_statistic(string const & query)
242{
243 results res;
244 fetch(res, 1, 1, query.c_str());
245 return lexical_cast<unsigned long>(res[0][0]);
246}
247
248void database::info(ostream & out)
249{
250 string id;
251 calculate_schema_id(sql(), id);
252 out << "schema version : " << id << endl;
253 out << "full manifests : " << get_statistic("SELECT COUNT(*) FROM manifests") << endl;
254 out << "manifest deltas : " << get_statistic("SELECT COUNT(*) FROM manifest_deltas") << endl;
255 out << "full files : " << get_statistic("SELECT COUNT(*) FROM files") << endl;
256 out << "file deltas : " << get_statistic("SELECT COUNT(*) FROM file_deltas") << endl;
257}
258
259void database::version(ostream & out)
260{
261 string id;
262 calculate_schema_id(sql(), id);
263 out << "database schema version: " << id << endl;
264}
265
266void database::migrate()
267{
268 N(filename.string() != "",
269 F("need database name"));
270 char * errmsg = NULL;
271 __sql = sqlite_open(filename.string().c_str(), 0755, &errmsg);
272 if (! __sql)
273 throw oops(string("could not open database: ") + filename.string() +
274 (errmsg ? (": " + string(errmsg)) : ""));
275 migrate_monotone_schema(__sql);
276 sqlite_close(__sql);
277}
278
279void database::rehash()
280{
281 transaction_guard guard(*this);
282 ticker mcerts("mcerts", 1);
283 ticker fcerts("fcerts", 1);
284 ticker pubkeys("pubkeys", 1);
285 ticker privkeys("privkeys", 1);
286
287 {
288 // rehash all mcerts
289 results res;
290 vector<cert> certs;
291 fetch(res, 5, any_rows,
292 "SELECT id, name, value, keypair, signature "
293 "FROM manifest_certs");
294 results_to_certs(res, certs);
295 execute("DELETE FROM manifest_certs");
296 for(vector<cert>::const_iterator i = certs.begin(); i != certs.end(); ++i)
297 {
298put_cert(*i, "manifest_certs");
299++mcerts;
300 }
301 }
302
303 {
304 // rehash all fcerts
305 results res;
306 vector<cert> certs;
307 fetch(res, 5, any_rows,
308 "SELECT id, name, value, keypair, signature "
309 "FROM file_certs");
310 results_to_certs(res, certs);
311 execute("DELETE FROM file_certs");
312 for(vector<cert>::const_iterator i = certs.begin(); i != certs.end(); ++i)
313 {
314put_cert(*i, "file_certs");
315++fcerts;
316 }
317 }
318
319
320 {
321 // rehash all pubkeys
322 results res;
323 fetch(res, 2, any_rows, "SELECT id, keydata FROM public_keys");
324 execute("DELETE FROM public_keys");
325 for (size_t i = 0; i < res.size(); ++i)
326 {
327hexenc<id> tmp;
328key_hash_code(rsa_keypair_id(res[i][0]), base64<rsa_pub_key>(res[i][1]), tmp);
329execute("INSERT INTO public_keys VALUES('%q', '%q', '%q')",
330tmp().c_str(), res[i][0].c_str(), res[i][1].c_str());
331++pubkeys;
332 }
333 }
334
335{
336 // rehash all privkeys
337 results res;
338 fetch(res, 2, any_rows, "SELECT id, keydata FROM private_keys");
339 execute("DELETE FROM private_keys");
340 for (size_t i = 0; i < res.size(); ++i)
341 {
342hexenc<id> tmp;
343key_hash_code(rsa_keypair_id(res[i][0]), base64< arc4<rsa_priv_key> >(res[i][1]), tmp);
344execute("INSERT INTO private_keys VALUES('%q', '%q', '%q')",
345tmp().c_str(), res[i][0].c_str(), res[i][1].c_str());
346++privkeys;
347 }
348 }
349
350 guard.commit();
351}
352
353void database::ensure_open()
354{
355 sqlite *s = sql();
356 I(s != NULL);
357}
358
359database::~database()
360{
361 if (__sql)
362 {
363 sqlite_close(__sql);
364 __sql = 0;
365 }
366}
367
368static void assert_sqlite_ok(int res)
369{
370 switch (res)
371 {
372 case SQLITE_OK:
373 break;
374
375 case SQLITE_ERROR:
376 throw oops("SQL error or missing database");
377 break;
378
379 case SQLITE_INTERNAL:
380 throw oops("An internal logic error in SQLite");
381 break;
382
383 case SQLITE_PERM:
384 throw oops("Access permission denied");
385 break;
386
387 case SQLITE_ABORT:
388 throw oops("Callback routine requested an abort");
389 break;
390
391 case SQLITE_BUSY:
392 throw oops("The database file is locked");
393 break;
394
395 case SQLITE_LOCKED:
396 throw oops("A table in the database is locked");
397 break;
398
399 case SQLITE_NOMEM:
400 throw oops("A malloc() failed");
401 break;
402
403 case SQLITE_READONLY:
404 throw oops("Attempt to write a readonly database");
405 break;
406
407 case SQLITE_INTERRUPT:
408 throw oops("Operation terminated by sqlite_interrupt()");
409 break;
410
411 case SQLITE_IOERR:
412 throw oops("Some kind of disk I/O error occurred");
413 break;
414
415 case SQLITE_CORRUPT:
416 throw oops("The database disk image is malformed");
417 break;
418
419 case SQLITE_NOTFOUND:
420 throw oops("(Internal Only) Table or record not found");
421 break;
422
423 case SQLITE_FULL:
424 throw oops("Insertion failed because database is full");
425 break;
426
427 case SQLITE_CANTOPEN:
428 throw oops("Unable to open the database file");
429 break;
430
431 case SQLITE_PROTOCOL:
432 throw oops("database lock protocol error");
433 break;
434
435 case SQLITE_EMPTY:
436 throw oops("(Internal Only) database table is empty");
437 break;
438
439 case SQLITE_SCHEMA:
440 throw oops("The database schema changed");
441 break;
442
443 case SQLITE_TOOBIG:
444 throw oops("Too much data for one row of a table");
445 break;
446
447 case SQLITE_CONSTRAINT:
448 throw oops("Abort due to contraint violation");
449 break;
450
451 case SQLITE_MISMATCH:
452 throw oops("Data type mismatch");
453 break;
454
455 case SQLITE_MISUSE:
456 throw oops("Library used incorrectly");
457 break;
458
459 default:
460 throw oops(string("Unknown DB result code: ") + lexical_cast<string>(res));
461 break;
462 }
463}
464
465void database::execute(char const * query, ...)
466{
467 va_list ap;
468 int res;
469 char * errmsg = NULL;
470
471 va_start(ap, query);
472
473 // log it
474 char * formatted = sqlite_vmprintf(query, ap);
475 string qq(formatted);
476 if (qq.size() > constants::db_log_line_sz)
477 qq = qq.substr(0, constants::db_log_line_sz) + string(" ...");
478 L(F("db.execute(\"%s\")\n") % qq);
479 sqlite_freemem(formatted);
480
481 va_end(ap);
482 va_start(ap, query);
483
484 // do it
485 res = sqlite_exec_vprintf(sql(), query, NULL, NULL, &errmsg, ap);
486
487 va_end(ap);
488
489 if (errmsg)
490 throw oops(string("sqlite exec error ") + errmsg);
491
492 assert_sqlite_ok(res);
493
494}
495
496void database::fetch(results & res,
497 int const want_cols,
498 int const want_rows,
499 char const * query, ...)
500{
501 char ** result = NULL;
502 int nrow;
503 int ncol;
504 char * errmsg = NULL;
505 int rescode;
506
507 va_list ap;
508 res.clear();
509 res.resize(0);
510 va_start(ap, query);
511
512 // log it
513 char * formatted = sqlite_vmprintf(query, ap);
514 string qq(formatted);
515 if (qq.size() > constants::log_line_sz)
516 qq = qq.substr(0, constants::log_line_sz) + string(" ...");
517 L(F("db.fetch(\"%s\")\n") % qq);
518 sqlite_freemem(formatted);
519
520 va_end(ap);
521 va_start(ap, query);
522
523 // do it
524 rescode = sqlite_get_table_vprintf(sql(), query, &result, &nrow, &ncol, &errmsg, ap);
525
526 va_end(ap);
527
528 cleanup_ptr<char **, void>
529 result_guard(result, &sqlite_free_table);
530
531 string ctx = string("db query [") + string(query) + "]: ";
532
533 if (errmsg)
534 throw oops(ctx + string("sqlite error ") + errmsg);
535 assert_sqlite_ok(rescode);
536
537 if (want_cols == 0 && ncol == 0) return;
538 if (want_rows == 0 && nrow == 0) return;
539 if (want_cols == any_rows && ncol == 0) return;
540 if (want_rows == any_rows && nrow == 0) return;
541
542 if (want_cols != any_cols &&
543 ncol != want_cols)
544 throw oops((F("%s wanted %d columns, got %s")
545% ctx % want_cols % ncol).str());
546
547 if (want_rows != any_rows &&
548 nrow != want_rows)
549 throw oops((F("%s wanted %d rows, got %s")
550% ctx % want_rows % nrow).str());
551
552 if (!result)
553 throw oops(ctx + "null result set");
554
555 for (int i = 0; i < ncol; ++i)
556 if (!result[i])
557 throw oops(ctx + "null column name");
558
559 for (int row = 0; row < nrow; ++row)
560 {
561 vector<string> rowvec;
562 for (int col = 0; col < ncol; ++col)
563{
564 int i = ((1 + row) * ncol) + col;
565 if (!result[i])
566 throw oops(ctx + "null result value");
567 else
568 rowvec.push_back(result[i]);
569}
570 res.push_back(rowvec);
571 }
572}
573
574// general application-level logic
575
576void database::set_filename(fs::path const & file)
577{
578 if (__sql)
579 {
580 throw oops("cannot change filename to " + file.string() + " while db is open");
581 }
582 filename = file;
583}
584
585void database::begin_transaction()
586{
587 if (transaction_level == 0)
588 execute("BEGIN");
589 transaction_level++;
590}
591
592void database::commit_transaction()
593{
594 if (transaction_level == 1)
595 execute("COMMIT");
596 transaction_level--;
597}
598
599void database::rollback_transaction()
600{
601 if (transaction_level == 1)
602 execute("ROLLBACK");
603 transaction_level--;
604}
605
606
607bool database::exists(hexenc<id> const & ident,
608 string const & table)
609{
610 results res;
611 fetch(res, one_col, any_rows,
612"SELECT id FROM '%q' WHERE id = '%q'",
613table.c_str(), ident().c_str());
614 I((res.size() == 1) || (res.size() == 0));
615 return res.size() == 1;
616}
617
618
619bool database::delta_exists(hexenc<id> const & ident,
620 string const & table)
621{
622 results res;
623 fetch(res, one_col, any_rows,
624"SELECT id FROM '%q' WHERE id = '%q'",
625table.c_str(), ident().c_str());
626 return res.size() > 0;
627}
628
629bool database::delta_exists(hexenc<id> const & ident,
630 hexenc<id> const & base,
631 string const & table)
632{
633 results res;
634 fetch(res, one_col, any_rows,
635"SELECT id FROM '%q' WHERE id = '%q' AND base = '%q'",
636table.c_str(), ident().c_str(), base().c_str());
637 I((res.size() == 1) || (res.size() == 0));
638 return res.size() == 1;
639}
640
641int database::count(string const & table)
642{
643 results res;
644 fetch(res, one_col, one_row,
645"SELECT COUNT(*) FROM '%q'",
646table.c_str());
647 return lexical_cast<int>(res[0][0]);
648}
649
650void database::get(hexenc<id> const & ident,
651 base64< gzip<data> > & dat,
652 string const & table)
653{
654 results res;
655 fetch(res, one_col, one_row,
656"SELECT data FROM '%q' WHERE id = '%q'",
657table.c_str(), ident().c_str());
658
659 // consistency check
660 base64<gzip<data> > rdata(res[0][0]);
661 hexenc<id> tid;
662 calculate_ident(rdata, tid);
663 I(tid == ident);
664
665 dat = rdata;
666}
667
668void database::get_delta(hexenc<id> const & ident,
669 hexenc<id> const & base,
670 base64< gzip<delta> > & del,
671 string const & table)
672{
673 I(ident() != "");
674 I(base() != "");
675 results res;
676 fetch(res, one_col, one_row,
677"SELECT delta FROM '%q' WHERE id = '%q' AND base = '%q'",
678table.c_str(), ident().c_str(), base().c_str());
679 del = res[0][0];
680}
681
682void database::put(hexenc<id> const & ident,
683 base64< gzip<data> > const & dat,
684 string const & table)
685{
686 // consistency check
687 I(ident() != "");
688 hexenc<id> tid;
689 calculate_ident(dat, tid);
690 I(tid == ident);
691
692 execute("INSERT INTO '%q' VALUES('%q', '%q')",
693 table.c_str(), ident().c_str(), dat().c_str());
694}
695
696
697void database::put_delta(hexenc<id> const & ident,
698 hexenc<id> const & base,
699 base64<gzip<delta> > const & del,
700 string const & table)
701{
702 // nb: delta schema is (id, base, delta)
703 I(ident() != "");
704 I(base() != "");
705 execute("INSERT INTO '%q' VALUES('%q', '%q', '%q')",
706 table.c_str(),
707 ident().c_str(), base().c_str(), del().c_str());
708}
709
710void database::get_version(hexenc<id> const & ident,
711 base64< gzip<data> > & dat,
712 string const & data_table,
713 string const & delta_table)
714{
715 I(ident() != "");
716 if (exists(ident, data_table))
717 {
718 // easy path
719 get(ident, dat, data_table);
720 }
721 else
722 {
723 // tricky path
724
725 // we start from the file we want to reconstruct and work *forwards*
726 // through the database, until we get to a full data object. we then
727 // trace back through the list of edges we followed to get to the data
728 // object, applying reverse deltas.
729 //
730 // the effect of this algorithm is breadth-first search, backwards
731 // through the storage graph, to discover a forwards shortest path, and
732 // then following that shortest path with delta application.
733 //
734 // we used to do this with the boost graph library, but it invovled
735 // loading too much of the storage graph into memory at any moment. this
736 // imperative version only loads the descendents of the reconstruction
737 // node, so it much cheaper in terms of memory.
738 //
739 // we also maintain a cycle-detecting set, just to be safe
740
741 L(F("reconstructing %s in %s\n") % ident % delta_table);
742 I(delta_exists(ident, delta_table));
743
744 // nb: an edge map goes in the direction of the
745 // delta, *not* the direction we discover things in,
746 // i.e. each map is of the form [newid] -> [oldid]
747
748 typedef map< hexenc<id>, hexenc<id> > edgemap;
749 list< shared_ptr<edgemap> > paths;
750
751 set< hexenc<id> > frontier, cycles;
752 frontier.insert(ident);
753
754 bool found_root = false;
755 hexenc<id> root("");
756
757 while (! found_root)
758{
759 set< hexenc<id> > next_frontier;
760 shared_ptr<edgemap> frontier_map(new edgemap());
761
762 for (set< hexenc<id> >::const_iterator i = frontier.begin();
763 i != frontier.end(); ++i)
764 {
765 if (exists(*i, data_table))
766{
767 root = *i;
768 found_root = true;
769 break;
770}
771 else
772{
773 cycles.insert(*i);
774 results res;
775 fetch(res, one_col, any_rows, "SELECT base from '%q' WHERE id = '%q'",
776delta_table.c_str(), (*i)().c_str());
777 for (size_t k = 0; k < res.size(); ++k)
778 {
779 hexenc<id> const nxt(res[k][0]);
780
781 if (cycles.find(nxt) != cycles.end())
782throw oops("cycle in table '" + delta_table + "', at node "
783 + (*i)() + " <- " + nxt());
784
785 next_frontier.insert(nxt);
786
787 if (frontier_map->find(nxt) == frontier_map->end())
788{
789 L(F("inserting edge: %s <- %s\n") % (*i) % nxt);
790 frontier_map->insert(make_pair(nxt, *i));
791}
792 else
793L(F("skipping merge edge %s <- %s\n") % (*i) % nxt);
794 }
795}
796 }
797 if (!found_root)
798 {
799 frontier = next_frontier;
800 paths.push_front(frontier_map);
801 }
802}
803
804 // path built, now all we need to do is follow it back
805
806 I(found_root);
807 I(root() != "");
808 base64< gzip<data> > begin_packed;
809 data begin;
810 get(root, begin_packed, data_table);
811 unpack(begin_packed, begin);
812 hexenc<id> curr = root;
813
814 boost::shared_ptr<delta_applicator> app = new_piecewise_applicator();
815 app->begin(begin());
816
817 for (list< shared_ptr<edgemap> >::const_iterator p = paths.begin();
818 p != paths.end(); ++p)
819{
820 shared_ptr<edgemap> i = *p;
821 I(i->find(curr) != i->end());
822 hexenc<id> const nxt = i->find(curr)->second;
823
824 L(F("following delta %s -> %s\n") % curr % nxt);
825 base64< gzip<delta> > del_packed;
826 get_delta(nxt, curr, del_packed, delta_table);
827 delta del;
828 unpack(del_packed, del);
829 apply_delta (app, del());
830 app->next();
831 curr = nxt;
832}
833
834 string tmp;
835 app->finish(tmp);
836 data end(tmp);
837
838 hexenc<id> final;
839 calculate_ident(end, final);
840 I(final == ident);
841 pack(end, dat);
842 }
843}
844
845
846void database::drop(hexenc<id> const & ident,
847 string const & table)
848{
849 execute("DELETE FROM '%q' WHERE id = '%q'",
850 table.c_str(),
851 ident().c_str());
852}
853
854
855void database::put_version(hexenc<id> const & old_id,
856 hexenc<id> const & new_id,
857 base64< gzip<delta> > const & del,
858 string const & data_table,
859 string const & delta_table)
860{
861
862 base64< gzip<data> > old_data, new_data;
863 base64< gzip<delta> > reverse_delta;
864
865 get_version(old_id, old_data, data_table, delta_table);
866 patch(old_data, del, new_data);
867 diff(new_data, old_data, reverse_delta);
868
869 transaction_guard guard(*this);
870 if (exists(old_id, data_table))
871 {
872 // descendent of a head version replaces the head, therefore old head
873 // must be disposed of
874 drop(old_id, data_table);
875 }
876 put(new_id, new_data, data_table);
877 put_delta(old_id, new_id, reverse_delta, delta_table);
878 guard.commit();
879}
880
881
882
883// ------------------------------------------------------------
884// -- --
885// -- public interface follows --
886// -- --
887// ------------------------------------------------------------
888
889bool database::file_version_exists(file_id const & id)
890{
891 return delta_exists(id.inner(), "file_deltas")
892 || exists(id.inner(), "files");
893}
894
895bool database::manifest_version_exists(manifest_id const & id)
896{
897 return delta_exists(id.inner(), "manifest_deltas")
898 || exists(id.inner(), "manifests");
899}
900
901void database::get_file_version(file_id const & id,
902file_data & dat)
903{
904 base64< gzip<data> > tmp;
905 get_version(id.inner(), tmp, "files", "file_deltas");
906 dat = tmp;
907}
908
909void database::get_manifest_version(manifest_id const & id,
910 manifest_data & dat)
911{
912 base64< gzip<data> > tmp;
913 get_version(id.inner(), tmp, "manifests", "manifest_deltas");
914 dat = tmp;
915}
916
917
918bool database::manifest_delta_exists(manifest_id const & new_id,
919 manifest_id const & old_id)
920{
921 return delta_exists(old_id.inner(), new_id.inner(), "manifest_deltas");
922}
923
924void database::compute_older_version(manifest_id const & new_id,
925 manifest_id const & old_id,
926 data const & m_new,
927 data & m_old)
928{
929 base64< gzip<delta> > del;
930 I(delta_exists(old_id.inner(), new_id.inner(), "manifest_deltas"));
931 get_delta(old_id.inner(), new_id.inner(), del, "manifest_deltas");
932 patch(m_new, del, m_old);
933}
934
935void database::compute_older_version(manifest_id const & new_id,
936 manifest_id const & old_id,
937 manifest_data const & m_new,
938 manifest_data & m_old)
939{
940 data old_data, new_data;
941 base64< gzip<data> > old_packed;
942 unpack(m_new.inner(), new_data);
943 compute_older_version(new_id, old_id, new_data, old_data);
944 pack(old_data, old_packed);
945 m_old = manifest_data(old_packed);
946}
947
948void database::put_file(file_id const & id,
949file_data const & dat)
950{
951 put(id.inner(), dat.inner(), "files");
952}
953
954void database::put_file_version(file_id const & old_id,
955file_id const & new_id,
956file_delta const & del)
957{
958 put_version(old_id.inner(), new_id.inner(), del.inner(),
959 "files", "file_deltas");
960}
961
962
963void database::put_manifest(manifest_id const & id,
964 manifest_data const & dat)
965{
966 put(id.inner(), dat.inner(), "manifests");
967}
968
969void database::put_manifest_version(manifest_id const & old_id,
970 manifest_id const & new_id,
971 manifest_delta const & del)
972{
973 put_version(old_id.inner(), new_id.inner(), del.inner(),
974 "manifests", "manifest_deltas");
975}
976
977// crypto key management
978
979void database::get_key_ids(string const & pattern,
980 vector<rsa_keypair_id> & pubkeys,
981 vector<rsa_keypair_id> & privkeys)
982{
983 pubkeys.clear();
984 privkeys.clear();
985 results res;
986
987 if (pattern != "")
988 fetch(res, one_col, any_rows,
989 "SELECT id from public_keys WHERE id GLOB '%q'",
990 pattern.c_str());
991 else
992 fetch(res, one_col, any_rows,
993 "SELECT id from public_keys");
994
995 for (size_t i = 0; i < res.size(); ++i)
996 pubkeys.push_back(res[i][0]);
997
998 if (pattern != "")
999 fetch(res, one_col, any_rows,
1000 "SELECT id from private_keys WHERE id GLOB '%q'",
1001 pattern.c_str());
1002 else
1003 fetch(res, one_col, any_rows,
1004 "SELECT id from private_keys");
1005
1006 for (size_t i = 0; i < res.size(); ++i)
1007 privkeys.push_back(res[i][0]);
1008}
1009
1010void database::get_private_keys(vector<rsa_keypair_id> & privkeys)
1011{
1012 privkeys.clear();
1013 results res;
1014 fetch(res, one_col, any_rows, "SELECT id from private_keys");
1015 for (size_t i = 0; i < res.size(); ++i)
1016 privkeys.push_back(res[i][0]);
1017}
1018
1019bool database::public_key_exists(hexenc<id> const & hash)
1020{
1021 results res;
1022 fetch(res, one_col, any_rows,
1023"SELECT id FROM public_keys WHERE hash = '%q'",
1024hash().c_str());
1025 I((res.size() == 1) || (res.size() == 0));
1026 if (res.size() == 1)
1027 return true;
1028 return false;
1029}
1030
1031bool database::public_key_exists(rsa_keypair_id const & id)
1032{
1033 results res;
1034 fetch(res, one_col, any_rows,
1035"SELECT id FROM public_keys WHERE id = '%q'",
1036id().c_str());
1037 I((res.size() == 1) || (res.size() == 0));
1038 if (res.size() == 1)
1039 return true;
1040 return false;
1041}
1042
1043bool database::private_key_exists(rsa_keypair_id const & id)
1044{
1045 results res;
1046 fetch(res, one_col, any_rows,
1047"SELECT id FROM private_keys WHERE id = '%q'",
1048id().c_str());
1049 I((res.size() == 1) || (res.size() == 0));
1050 if (res.size() == 1)
1051 return true;
1052 return false;
1053}
1054
1055bool database::key_exists(rsa_keypair_id const & id)
1056{
1057 return public_key_exists(id) || private_key_exists(id);
1058}
1059
1060void database::get_pubkey(hexenc<id> const & hash,
1061 rsa_keypair_id & id,
1062 base64<rsa_pub_key> & pub_encoded)
1063{
1064 results res;
1065 fetch(res, 2, one_row,
1066"SELECT id, keydata FROM public_keys where hash = '%q'",
1067hash().c_str());
1068 id = res[0][0];
1069 pub_encoded = res[0][1];
1070}
1071
1072void database::get_key(rsa_keypair_id const & pub_id,
1073 base64<rsa_pub_key> & pub_encoded)
1074{
1075 results res;
1076 fetch(res, one_col, one_row,
1077"SELECT keydata FROM public_keys where id = '%q'",
1078pub_id().c_str());
1079 pub_encoded = res[0][0];
1080}
1081
1082void database::get_key(rsa_keypair_id const & priv_id,
1083 base64< arc4<rsa_priv_key> > & priv_encoded)
1084{
1085 results res;
1086 fetch(res, one_col, one_col,
1087"SELECT keydata FROM private_keys where id = '%q'",
1088priv_id().c_str());
1089 priv_encoded = res[0][0];
1090}
1091
1092
1093void database::put_key(rsa_keypair_id const & pub_id,
1094 base64<rsa_pub_key> const & pub_encoded)
1095{
1096 hexenc<id> thash;
1097 key_hash_code(pub_id, pub_encoded, thash);
1098 execute("INSERT INTO public_keys VALUES('%q', '%q', '%q')",
1099 thash().c_str(), pub_id().c_str(), pub_encoded().c_str());
1100}
1101
1102void database::put_key(rsa_keypair_id const & priv_id,
1103 base64< arc4<rsa_priv_key> > const & priv_encoded)
1104{
1105
1106 hexenc<id> thash;
1107 key_hash_code(priv_id, priv_encoded, thash);
1108 execute("INSERT INTO private_keys VALUES('%q', '%q', '%q')",
1109 thash().c_str(), priv_id().c_str(), priv_encoded().c_str());
1110}
1111
1112void database::put_key_pair(rsa_keypair_id const & id,
1113 base64<rsa_pub_key> const & pub_encoded,
1114 base64< arc4<rsa_priv_key> > const & priv_encoded)
1115{
1116 transaction_guard guard(*this);
1117 put_key(id, pub_encoded);
1118 put_key(id, priv_encoded);
1119 guard.commit();
1120}
1121
1122
1123// cert management
1124
1125bool database::cert_exists(cert const & t,
1126 string const & table)
1127{
1128 results res;
1129 fetch(res, 1, any_rows,
1130"SELECT id FROM '%q' WHERE id = '%q' "
1131"AND name = '%q' AND value = '%q' "
1132"AND keypair = '%q' AND signature = '%q' ",
1133table.c_str(),
1134t.ident().c_str(),
1135t.name().c_str(),
1136t.value().c_str(),
1137t.key().c_str(),
1138t.sig().c_str());
1139 I(res.size() == 0 || res.size() == 1);
1140 return res.size() == 1;
1141}
1142
1143void database::put_cert(cert const & t,
1144 string const & table)
1145{
1146 hexenc<id> thash;
1147 cert_hash_code(t, thash);
1148 execute("INSERT INTO '%q' VALUES('%q', '%q', '%q', '%q', '%q', '%q')",
1149 table.c_str(),
1150 thash().c_str(),
1151 t.ident().c_str(),
1152 t.name().c_str(),
1153 t.value().c_str(),
1154 t.key().c_str(),
1155 t.sig().c_str());
1156}
1157
1158void database::results_to_certs(results const & res,
1159 vector<cert> & certs)
1160{
1161 certs.clear();
1162 for (size_t i = 0; i < res.size(); ++i)
1163 {
1164 cert t;
1165 t = cert(hexenc<id>(res[i][0]),
1166 cert_name(res[i][1]),
1167 base64<cert_value>(res[i][2]),
1168 rsa_keypair_id(res[i][3]),
1169 base64<rsa_sha1_signature>(res[i][4]));
1170 certs.push_back(t);
1171 }
1172}
1173
1174void database::get_head_candidates(string const & branch_encoded,
1175 vector< manifest<cert> > & branch_certs,
1176 vector< manifest<cert> > & ancestry_certs)
1177{
1178 results res;
1179 fetch(res, 5, any_rows,
1180"SELECT id, name, value, keypair, signature "
1181"FROM manifest_certs "
1182"WHERE (name = 'ancestor' OR name = 'branch') "
1183"AND id IN "
1184"("
1185"SELECT id FROM manifest_certs WHERE name = 'branch' "
1186"AND value = '%q'"
1187")",
1188branch_encoded.c_str());
1189
1190 branch_certs.clear();
1191 ancestry_certs.clear();
1192 for (size_t i = 0; i < res.size(); ++i)
1193 {
1194 manifest<cert> t;
1195 t = manifest<cert>(cert(hexenc<id>(res[i][0]),
1196 cert_name(res[i][1]),
1197 base64<cert_value>(res[i][2]),
1198 rsa_keypair_id(res[i][3]),
1199 base64<rsa_sha1_signature>(res[i][4])));
1200 if (res[i][1] == "branch")
1201branch_certs.push_back(t);
1202 else
1203ancestry_certs.push_back(t);
1204 }
1205}
1206
1207
1208void database::get_certs(hexenc<id> const & ident,
1209vector<cert> & certs,
1210string const & table)
1211{
1212 results res;
1213 fetch(res, 5, any_rows,
1214"SELECT id, name, value, keypair, signature FROM '%q' "
1215"WHERE id = '%q'",
1216table.c_str(),
1217ident().c_str());
1218 results_to_certs(res, certs);
1219}
1220
1221
1222void database::get_certs(cert_name const & name,
1223vector<cert> & certs,
1224string const & table)
1225{
1226 results res;
1227 fetch(res, 5, any_rows,
1228"SELECT id, name, value, keypair, signature "
1229"FROM '%q' WHERE name = '%q'",
1230table.c_str(),
1231name().c_str());
1232 results_to_certs(res, certs);
1233}
1234
1235
1236void database::get_certs(hexenc<id> const & ident,
1237cert_name const & name,
1238vector<cert> & certs,
1239string const & table)
1240{
1241 results res;
1242 fetch(res, 5, any_rows,
1243"SELECT id, name, value, keypair, signature "
1244"FROM '%q' "
1245"WHERE id = '%q' AND name = '%q'",
1246table.c_str(),
1247ident().c_str(),
1248name().c_str());
1249 results_to_certs(res, certs);
1250}
1251
1252void database::get_certs(cert_name const & name,
1253base64<cert_value> const & val,
1254vector<cert> & certs,
1255string const & table)
1256{
1257 results res;
1258 fetch(res, 5, any_rows,
1259"SELECT id, name, value, keypair, signature "
1260"FROM '%q' "
1261"WHERE name = '%q' AND value = '%q'",
1262table.c_str(),
1263name().c_str(),
1264val().c_str());
1265 results_to_certs(res, certs);
1266}
1267
1268
1269void database::get_certs(hexenc<id> const & ident,
1270cert_name const & name,
1271base64<cert_value> const & value,
1272vector<cert> & certs,
1273string const & table)
1274{
1275 results res;
1276 fetch(res, 5, any_rows,
1277"SELECT id, name, value, keypair, signature "
1278"FROM '%q' "
1279"WHERE id = '%q' AND name = '%q' AND value = '%q'",
1280table.c_str(),
1281ident().c_str(),
1282name().c_str(),
1283value().c_str());
1284 results_to_certs(res, certs);
1285}
1286
1287
1288
1289
1290bool database::manifest_cert_exists(manifest<cert> const & cert)
1291{ return cert_exists(cert.inner(), "manifest_certs"); }
1292
1293bool database::file_cert_exists(file<cert> const & cert)
1294{ return cert_exists(cert.inner(), "file_certs"); }
1295
1296void database::put_manifest_cert(manifest<cert> const & cert)
1297{ put_cert(cert.inner(), "manifest_certs"); }
1298
1299void database::put_file_cert(file<cert> const & cert)
1300{ put_cert(cert.inner(), "file_certs"); }
1301
1302void database::get_file_certs(cert_name const & name,
1303 vector< file<cert> > & ts)
1304{
1305 vector<cert> certs;
1306 get_certs(name, certs, "file_certs");
1307 ts.clear();
1308 copy(certs.begin(), certs.end(), back_inserter(ts));
1309}
1310
1311void database::get_file_certs(file_id const & id,
1312 cert_name const & name,
1313 vector< file<cert> > & ts)
1314{
1315 vector<cert> certs;
1316 get_certs(id.inner(), name, certs, "file_certs");
1317 ts.clear();
1318 copy(certs.begin(), certs.end(), back_inserter(ts));
1319}
1320
1321void database::get_file_certs(cert_name const & name,
1322 base64<cert_value> const & val,
1323 vector< file<cert> > & ts)
1324{
1325 vector<cert> certs;
1326 get_certs(name, val, certs, "file_certs");
1327 ts.clear();
1328 copy(certs.begin(), certs.end(), back_inserter(ts));
1329}
1330
1331void database::get_file_certs(file_id const & id,
1332 cert_name const & name,
1333 base64<cert_value> const & val,
1334 vector< file<cert> > & ts)
1335{
1336 vector<cert> certs;
1337 get_certs(id.inner(), name, val, certs, "file_certs");
1338 ts.clear();
1339 copy(certs.begin(), certs.end(), back_inserter(ts));
1340}
1341
1342void database::get_file_certs(file_id const & id,
1343 vector< file<cert> > & ts)
1344{
1345 vector<cert> certs;
1346 get_certs(id.inner(), certs, "file_certs");
1347 ts.clear();
1348 copy(certs.begin(), certs.end(), back_inserter(ts));
1349}
1350
1351
1352bool database::file_cert_exists(hexenc<id> const & hash)
1353{
1354 results res;
1355 vector<cert> certs;
1356 fetch(res, one_col, any_rows,
1357"SELECT id "
1358"FROM file_certs "
1359"WHERE hash = '%q'",
1360hash().c_str());
1361 I(res.size() == 0 || res.size() == 1);
1362 return (res.size() == 1);
1363}
1364
1365void database::get_file_cert(hexenc<id> const & hash,
1366 file<cert> & c)
1367{
1368 results res;
1369 vector<cert> certs;
1370 fetch(res, 5, one_row,
1371"SELECT id, name, value, keypair, signature "
1372"FROM file_certs "
1373"WHERE hash = '%q'",
1374hash().c_str());
1375 results_to_certs(res, certs);
1376 I(certs.size() == 1);
1377 c = file<cert>(certs[0]);
1378}
1379
1380
1381
1382
1383
1384void database::get_manifest_certs(cert_name const & name,
1385 vector< manifest<cert> > & ts)
1386{
1387 vector<cert> certs;
1388 get_certs(name, certs, "manifest_certs");
1389 ts.clear();
1390 copy(certs.begin(), certs.end(), back_inserter(ts));
1391}
1392
1393void database::get_manifest_certs(manifest_id const & id,
1394 cert_name const & name,
1395 vector< manifest<cert> > & ts)
1396{
1397 vector<cert> certs;
1398 get_certs(id.inner(), name, certs, "manifest_certs");
1399 ts.clear();
1400 copy(certs.begin(), certs.end(), back_inserter(ts));
1401}
1402
1403void database::get_manifest_certs(manifest_id const & id,
1404 cert_name const & name,
1405 base64<cert_value> const & val,
1406 vector< manifest<cert> > & ts)
1407{
1408 vector<cert> certs;
1409 get_certs(id.inner(), name, val, certs, "manifest_certs");
1410 ts.clear();
1411 copy(certs.begin(), certs.end(), back_inserter(ts));
1412}
1413
1414void database::get_manifest_certs(cert_name const & name,
1415 base64<cert_value> const & val,
1416 vector< manifest<cert> > & ts)
1417{
1418 vector<cert> certs;
1419 get_certs(name, val, certs, "manifest_certs");
1420 ts.clear();
1421 copy(certs.begin(), certs.end(), back_inserter(ts));
1422}
1423
1424void database::get_manifest_certs(manifest_id const & id,
1425 vector< manifest<cert> > & ts)
1426{
1427 vector<cert> certs;
1428 get_certs(id.inner(), certs, "manifest_certs");
1429 ts.clear();
1430 copy(certs.begin(), certs.end(), back_inserter(ts));
1431}
1432
1433bool database::manifest_cert_exists(hexenc<id> const & hash)
1434{
1435 results res;
1436 vector<cert> certs;
1437 fetch(res, one_col, any_rows,
1438"SELECT id "
1439"FROM manifest_certs "
1440"WHERE hash = '%q'",
1441hash().c_str());
1442 I(res.size() == 0 || res.size() == 1);
1443 return (res.size() == 1);
1444}
1445
1446void database::get_manifest_cert(hexenc<id> const & hash,
1447 manifest<cert> & c)
1448{
1449 results res;
1450 vector<cert> certs;
1451 fetch(res, 5, one_row,
1452"SELECT id, name, value, keypair, signature "
1453"FROM manifest_certs "
1454"WHERE hash = '%q'",
1455hash().c_str());
1456 results_to_certs(res, certs);
1457 I(certs.size() == 1);
1458 c = manifest<cert>(certs[0]);
1459}
1460
1461
1462// network stuff
1463
1464void database::get_queued_targets(set<url> & targets)
1465{
1466 results res;
1467 fetch(res, 1, any_rows,
1468"SELECT url FROM posting_queue "
1469"GROUP BY url");
1470 targets.clear();
1471 for (size_t i = 0; i < res.size(); ++i)
1472 targets.insert(url(res[i][0]));
1473}
1474
1475void database::get_queue_count(url const & u,
1476 size_t & num_packets)
1477{
1478 results res;
1479 fetch(res, one_col, one_row,
1480"SELECT count(*) FROM posting_queue "
1481"WHERE url = '%s'",
1482u().c_str());
1483 num_packets = lexical_cast<size_t>(res[0][0]);
1484}
1485
1486void database::get_queued_content(url const & u,
1487 size_t const & queue_pos,
1488 string & content)
1489{
1490 results res;
1491 fetch(res, one_col, one_row,
1492"SELECT content FROM posting_queue "
1493"WHERE url = '%s' "
1494"LIMIT 1 OFFSET %d",
1495u().c_str(), queue_pos);
1496 content = res[0][0];
1497}
1498
1499void database::get_sequences(url const & u,
1500 unsigned long & maj,
1501 unsigned long & min)
1502{
1503 results res;
1504 fetch(res, 2, any_rows,
1505"SELECT major, minor "
1506"FROM sequence_numbers "
1507"WHERE url = '%q' "
1508"LIMIT 1",
1509u().c_str());
1510 if (res.size() == 1)
1511 {
1512 maj = lexical_cast<unsigned long>(res[0][0]);
1513 min = lexical_cast<unsigned long>(res[0][1]);
1514 }
1515 else
1516 {
1517 maj = 0;
1518 min = 0;
1519 execute("INSERT INTO sequence_numbers "
1520 "VALUES ('%q', %lu, %lu)",
1521 u().c_str(), maj, min);
1522 }
1523}
1524
1525void database::get_all_known_sources(set<url> & sources)
1526{
1527 results res;
1528 fetch(res, 1, any_rows,
1529"SELECT url "
1530"FROM sequence_numbers "
1531"GROUP BY url");
1532
1533 sources.clear();
1534 for (size_t i = 0; i < res.size(); ++i)
1535 {
1536 I(res[i].size() == 1);
1537 sources.insert(url(res[i][0]));
1538 }
1539}
1540
1541void database::put_sequences(url const & u,
1542 unsigned long maj,
1543 unsigned long min)
1544{
1545 execute("UPDATE sequence_numbers "
1546 "SET major = %lu, minor = %lu "
1547 "WHERE url = '%q'",
1548 maj, min, u().c_str());
1549}
1550
1551void database::queue_posting(url const & u,
1552 string const & content)
1553{
1554 execute("INSERT INTO posting_queue "
1555 "VALUES ('%q', '%q')",
1556 u().c_str(), content.c_str());
1557}
1558
1559void database::delete_posting(url const & u,
1560 size_t const & queue_pos)
1561{
1562 results res;
1563 fetch(res, 1, any_rows,
1564"SELECT OID FROM posting_queue "
1565"WHERE url = '%s' "
1566"LIMIT 1 OFFSET %d",
1567u().c_str(), queue_pos);
1568 I(res.size() == 1);
1569 size_t oid = lexical_cast<size_t>(res[0][0]);
1570 execute("DELETE FROM posting_queue WHERE OID = %d ", oid);
1571}
1572
1573bool database::manifest_exists_on_netserver(url const & u,
1574 manifest_id const & m)
1575{
1576 results res;
1577 fetch(res, 1, any_rows,
1578"SELECT manifest FROM netserver_manifests "
1579"WHERE url = '%q' "
1580"AND manifest = '%q' ",
1581u().c_str(),
1582m.inner()().c_str());
1583 I(res.size() == 0 || res.size() == 1);
1584 return (res.size() == 1);
1585}
1586
1587void database::note_manifest_on_netserver (url const & u,
1588 manifest_id const & m)
1589{
1590 if (!manifest_exists_on_netserver (u, m))
1591 execute("INSERT INTO netserver_manifests "
1592 "VALUES ('%q', '%q')",
1593 u().c_str(), m.inner()().c_str());
1594}
1595
1596// completions
1597
1598void database::complete(string const & partial,
1599set<manifest_id> & completions)
1600{
1601 results res;
1602 completions.clear();
1603
1604 fetch(res, 1, any_rows,
1605"SELECT id FROM manifests WHERE id GLOB '%q*'",
1606partial.c_str());
1607
1608 for (size_t i = 0; i < res.size(); ++i)
1609 completions.insert(manifest_id(res[i][0]));
1610
1611 res.clear();
1612
1613 fetch(res, 1, any_rows,
1614"SELECT id FROM manifest_deltas WHERE id GLOB '%q*'",
1615partial.c_str());
1616
1617 for (size_t i = 0; i < res.size(); ++i)
1618 completions.insert(manifest_id(res[i][0]));
1619}
1620
1621void database::complete(string const & partial,
1622set<file_id> & completions)
1623{
1624 results res;
1625 completions.clear();
1626
1627 fetch(res, 1, any_rows,
1628"SELECT id FROM files WHERE id GLOB '%q*'",
1629partial.c_str());
1630
1631 for (size_t i = 0; i < res.size(); ++i)
1632 completions.insert(file_id(res[i][0]));
1633
1634 res.clear();
1635
1636 fetch(res, 1, any_rows,
1637"SELECT id FROM file_deltas WHERE id GLOB '%q*'",
1638partial.c_str());
1639
1640 for (size_t i = 0; i < res.size(); ++i)
1641 completions.insert(file_id(res[i][0]));
1642}
1643
1644// merkle nodes
1645
1646bool database::merkle_node_exists(string const & type,
1647 utf8 const & collection,
1648 size_t level,
1649 hexenc<prefix> const & prefix)
1650{
1651 results res;
1652 fetch(res, one_col, one_row,
1653"SELECT COUNT(*) "
1654"FROM merkle_nodes "
1655"WHERE type = '%q' "
1656"AND collection = '%q' "
1657"AND level = %d "
1658"AND prefix = '%q' ",
1659type.c_str(), collection().c_str(), level, prefix().c_str());
1660 size_t n_nodes = lexical_cast<size_t>(res[0][0]);
1661 I(n_nodes == 0 || n_nodes == 1);
1662 return n_nodes == 1;
1663}
1664
1665void database::get_merkle_node(string const & type,
1666 utf8 const & collection,
1667 size_t level,
1668 hexenc<prefix> const & prefix,
1669 base64<merkle> & node)
1670{
1671 results res;
1672 fetch(res, one_col, one_row,
1673"SELECT body "
1674"FROM merkle_nodes "
1675"WHERE type = '%q' "
1676"AND collection = '%q' "
1677"AND level = %d "
1678"AND prefix = '%q'",
1679type.c_str(), collection().c_str(), level, prefix().c_str());
1680 node = res[0][0];
1681}
1682
1683void database::put_merkle_node(string const & type,
1684 utf8 const & collection,
1685 size_t level,
1686 hexenc<prefix> const & prefix,
1687 base64<merkle> const & node)
1688{
1689 execute("INSERT OR REPLACE "
1690 "INTO merkle_nodes "
1691 "VALUES ('%q', '%q', %d, '%q', '%q')",
1692 type.c_str(), collection().c_str(), level, prefix().c_str(), node().c_str());
1693}
1694
1695void database::erase_merkle_nodes(string const & type,
1696 utf8 const & collection)
1697{
1698 execute("DELETE FROM merkle_nodes "
1699 "WHERE type = '%q' "
1700 "AND collection = '%q'",
1701 type.c_str(), collection().c_str());
1702}
1703
1704// transaction guards
1705
1706transaction_guard::transaction_guard(database & d) : committed(false), db(d)
1707{
1708 db.begin_transaction();
1709}
1710transaction_guard::~transaction_guard()
1711{
1712 if (committed)
1713 db.commit_transaction();
1714 else
1715 db.rollback_transaction();
1716}
1717
1718void transaction_guard::commit()
1719{
1720 committed = true;
1721}
1722
1723
1724// reverse queue
1725
1726static int global_rq_refcount = 0;
1727
1728reverse_queue::reverse_queue(reverse_queue const & other) : db(other.db)
1729{
1730 ++global_rq_refcount;
1731}
1732
1733reverse_queue::reverse_queue(database & d) : db(d)
1734{
1735 if (global_rq_refcount == 0)
1736 {
1737 db.execute("CREATE TEMPORARY TABLE "
1738 "reverse_queue (url not null, content not null)");
1739 }
1740 ++global_rq_refcount;
1741}
1742
1743void reverse_queue::reverse_queue_posting(url const & u,
1744 std::string const & contents)
1745{
1746 db.execute("INSERT INTO reverse_queue "
1747 "VALUES ('%q', '%q')",
1748 u().c_str(), contents.c_str());
1749}
1750
1751reverse_queue::~reverse_queue()
1752{
1753 if (global_rq_refcount == 1)
1754 {
1755 db.execute("INSERT INTO posting_queue "
1756 "SELECT * FROM reverse_queue "
1757 "ORDER BY OID DESC");
1758
1759 db.execute("DROP TABLE reverse_queue");
1760 }
1761 --global_rq_refcount;
1762}

Archive Download this file

Branches

Tags

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