monotone

monotone Mtn Source Tree

Root/schema_migration.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 <algorithm>
7#include <string>
8#include <vector>
9#include <locale>
10#include <stdexcept>
11#include <iostream>
12
13#include <boost/tokenizer.hpp>
14
15#include <sqlite3.h>
16
17#include "sanity.hh"
18#include "schema_migration.hh"
19#include "botan/botan.h"
20
21// this file knows how to migrate schema databases. the general strategy is
22// to hash each schema we ever use, and make a list of the SQL commands
23// required to get from one hash value to the next. when you do a
24// migration, the migrator locates your current db's state on the list and
25// then runs all the migration functions between that point and the target
26// of the migration.
27
28// you will notice a little bit of duplicated code between here and
29// transforms.cc / database.cc; this was originally to facilitate inclusion of
30// migration capability into the depot code, which did not link against those
31// objects. the depot code is gone, but this isn't the sort of code that
32// should ever be touched after being written, so the duplication remains.
33
34using namespace std;
35
36typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
37
38extern "C" {
39 const char *sqlite3_value_text_s(sqlite3_value *v);
40}
41
42static string
43lowercase(string const & in)
44{
45 I(40==in.size());
46 const int sz=40;
47 char buf[sz];
48 in.copy(buf, sz);
49 locale loc;
50 use_facet< ctype<char> >(loc).tolower(buf, buf+sz);
51 return string(buf,sz);
52}
53
54static void
55massage_sql_tokens(string const & in,
56 string & out)
57{
58 boost::char_separator<char> sep(" \r\n\t", "(),;");
59 tokenizer tokens(in, sep);
60 out.clear();
61 for (tokenizer::iterator i = tokens.begin();
62 i != tokens.end(); ++i)
63 {
64 if (i != tokens.begin())
65 out += " ";
66 out += *i;
67 }
68}
69
70static void
71calculate_id(string const & in,
72 string & ident)
73{
74 Botan::Pipe p(new Botan::Hash_Filter("SHA-1"), new Botan::Hex_Encoder());
75 p.process_msg(in);
76
77 ident = lowercase(p.read_all_as_string());
78}
79
80
81struct
82is_ws
83{
84 bool operator()(char c) const
85 {
86 return c == '\r' || c == '\n' || c == '\t' || c == ' ';
87 }
88};
89
90static void
91sqlite_sha1_fn(sqlite3_context *f, int nargs, sqlite3_value ** args)
92{
93 string tmp, sha;
94 if (nargs <= 1)
95 {
96 sqlite3_result_error(f, "need at least 1 arg to sha1()", -1);
97 return;
98 }
99
100 if (nargs == 1)
101 {
102 string s = (sqlite3_value_text_s(args[0]));
103 s.erase(remove_if(s.begin(), s.end(), is_ws()),s.end());
104 tmp = s;
105 }
106 else
107 {
108 string sep = string(sqlite3_value_text_s(args[0]));
109 string s = (sqlite3_value_text_s(args[1]));
110 s.erase(remove_if(s.begin(), s.end(), is_ws()),s.end());
111 tmp = s;
112 for (int i = 2; i < nargs; ++i)
113 {
114 s = string(sqlite3_value_text_s(args[i]));
115 s.erase(remove_if(s.begin(), s.end(), is_ws()),s.end());
116 tmp += sep + s;
117 }
118 }
119 calculate_id(tmp, sha);
120 sqlite3_result_text(f,sha.c_str(),sha.size(),SQLITE_TRANSIENT);
121}
122
123int
124append_sql_stmt(void * vp,
125 int ncols,
126 char ** values,
127 char ** colnames)
128{
129 if (ncols != 1)
130 return 1;
131
132 if (vp == NULL)
133 return 1;
134
135 if (values == NULL)
136 return 1;
137
138 if (values[0] == NULL)
139 return 1;
140
141 string *str = reinterpret_cast<string *>(vp);
142 str->append(values[0]);
143 str->append("\n");
144 return 0;
145}
146
147void
148calculate_schema_id(sqlite3 *sql, string & id)
149{
150 id.clear();
151 string tmp, tmp2;
152 int res = sqlite3_exec(sql,
153 "SELECT sql FROM sqlite_master "
154 "WHERE (type = 'table' OR type = 'index') "
155 // filter out NULL sql statements, because
156 // those are auto-generated indices (for
157 // UNIQUE constraints, etc.).
158 "AND sql IS NOT NULL "
159 "AND name != 'sqlite_stat1' "
160 "ORDER BY name",
161 &append_sql_stmt, &tmp, NULL);
162 if (res != SQLITE_OK)
163 {
164 sqlite3_exec(sql, "ROLLBACK", NULL, NULL, NULL);
165 throw runtime_error("failure extracting schema from sqlite_master");
166 }
167 massage_sql_tokens(tmp, tmp2);
168 calculate_id(tmp2, id);
169}
170
171typedef bool (*migrator_cb)(sqlite3 *, char **);
172
173struct
174migrator
175{
176 vector< pair<string,migrator_cb> > migration_events;
177
178 void add(string schema_id, migrator_cb cb)
179 {
180 migration_events.push_back(make_pair(schema_id, cb));
181 }
182
183 void migrate(sqlite3 *sql, string target_id)
184 {
185 string init;
186
187 if (sql == NULL)
188 throw runtime_error("NULL sqlite object given to migrate");
189
190 calculate_schema_id(sql, init);
191
192 if (sqlite3_create_function(sql, "sha1", -1, SQLITE_UTF8, NULL,
193 &sqlite_sha1_fn, NULL, NULL))
194 throw runtime_error("error registering sha1 function with sqlite");
195
196 bool migrating = false;
197 for (vector< pair<string, migrator_cb> >::const_iterator i = migration_events.begin();
198 i != migration_events.end(); ++i)
199 {
200
201 if (i->first == init)
202 {
203 if (sqlite3_exec(sql, "BEGIN", NULL, NULL, NULL) != SQLITE_OK)
204 throw runtime_error("error at transaction BEGIN statement");
205 migrating = true;
206 }
207
208 if (migrating)
209 {
210 // confirm that we are where we ought to be
211 string curr;
212 char *errmsg = NULL;
213 calculate_schema_id(sql, curr);
214 if (curr != i->first)
215 {
216 if (migrating)
217 sqlite3_exec(sql, "ROLLBACK", NULL, NULL, NULL);
218 throw runtime_error("mismatched pre-state to migration step");
219 }
220
221 if (i->second == NULL)
222 {
223 sqlite3_exec(sql, "ROLLBACK", NULL, NULL, NULL);
224 throw runtime_error("NULL migration specifier");
225 }
226
227 // do this migration step
228 else if (! i->second(sql, &errmsg))
229 {
230 string e("migration step failed");
231 if (errmsg != NULL)
232 e.append(string(": ") + errmsg);
233 sqlite3_exec(sql, "ROLLBACK", NULL, NULL, NULL);
234 throw runtime_error(e);
235 }
236 }
237 }
238
239 // confirm that our target schema was met
240 if (migrating)
241 {
242 string curr;
243 calculate_schema_id(sql, curr);
244 if (curr != target_id)
245 {
246 sqlite3_exec(sql, "ROLLBACK", NULL, NULL, NULL);
247 throw runtime_error("mismatched result of migration, "
248 "got " + curr + ", wanted " + target_id);
249 }
250 if (sqlite3_exec(sql, "COMMIT", NULL, NULL, NULL) != SQLITE_OK)
251 {
252 throw runtime_error("failure on COMMIT");
253 }
254
255 if (sqlite3_exec(sql, "VACUUM", NULL, NULL, NULL) != SQLITE_OK)
256 throw runtime_error("error vacuuming after migration");
257 }
258 else
259 {
260 // if we didn't do anything, make sure that it's because we were
261 // already up to date.
262 E(init == target_id,
263 F("database schema %s is unknown; cannot perform migration") % init);
264 // We really want 'db migrate' on an up-to-date schema to be a no-op
265 // (no vacuum or anything, even), so that automated scripts can fire
266 // one off optimistically and not have to worry about getting their
267 // administrators to do it by hand.
268 P(F("no migration performed; database schema already up-to-date at %s\n") % init);
269 }
270 }
271};
272
273static bool move_table(sqlite3 *sql, char **errmsg,
274 char const * srcname,
275 char const * dstname,
276 char const * dstschema)
277{
278 string create = "CREATE TABLE ";
279 create += dstname;
280 create += " ";
281 create += dstschema;
282
283 int res = sqlite3_exec(sql, create.c_str(), NULL, NULL, errmsg);
284 if (res != SQLITE_OK)
285 return false;
286
287 string insert = "INSERT INTO ";
288 insert += dstname;
289 insert += " SELECT * FROM ";
290 insert += srcname;
291
292 res = sqlite3_exec(sql, insert.c_str(), NULL, NULL, errmsg);
293 if (res != SQLITE_OK)
294 return false;
295
296 string drop = "DROP TABLE ";
297 drop += srcname;
298
299 res = sqlite3_exec(sql, drop.c_str(), NULL, NULL, errmsg);
300 if (res != SQLITE_OK)
301 return false;
302
303 return true;
304}
305
306
307static bool
308migrate_client_merge_url_and_group(sqlite3 * sql,
309 char ** errmsg)
310{
311
312 // migrate the posting_queue table
313 if (!move_table(sql, errmsg,
314 "posting_queue",
315 "tmp",
316 "("
317 "url not null,"
318 "groupname not null,"
319 "content not null"
320 ")"))
321 return false;
322
323 int res = sqlite3_exec(sql, "CREATE TABLE posting_queue "
324 "("
325 "url not null, -- URL we are going to send this to\n"
326 "content not null -- the packets we're going to send\n"
327 ")", NULL, NULL, errmsg);
328 if (res != SQLITE_OK)
329 return false;
330
331 res = sqlite3_exec(sql, "INSERT INTO posting_queue "
332 "SELECT "
333 "(url || '/' || groupname), "
334 "content "
335 "FROM tmp", NULL, NULL, errmsg);
336 if (res != SQLITE_OK)
337 return false;
338
339 res = sqlite3_exec(sql, "DROP TABLE tmp", NULL, NULL, errmsg);
340 if (res != SQLITE_OK)
341 return false;
342
343
344 // migrate the incoming_queue table
345 if (!move_table(sql, errmsg,
346 "incoming_queue",
347 "tmp",
348 "("
349 "url not null,"
350 "groupname not null,"
351 "content not null"
352 ")"))
353 return false;
354
355 res = sqlite3_exec(sql, "CREATE TABLE incoming_queue "
356 "("
357 "url not null, -- URL we got this bundle from\n"
358 "content not null -- the packets we're going to read\n"
359 ")", NULL, NULL, errmsg);
360 if (res != SQLITE_OK)
361 return false;
362
363 res = sqlite3_exec(sql, "INSERT INTO incoming_queue "
364 "SELECT "
365 "(url || '/' || groupname), "
366 "content "
367 "FROM tmp", NULL, NULL, errmsg);
368 if (res != SQLITE_OK)
369 return false;
370
371 res = sqlite3_exec(sql, "DROP TABLE tmp", NULL, NULL, errmsg);
372 if (res != SQLITE_OK)
373 return false;
374
375
376 // migrate the sequence_numbers table
377 if (!move_table(sql, errmsg,
378 "sequence_numbers",
379 "tmp",
380 "("
381 "url not null,"
382 "groupname not null,"
383 "major not null,"
384 "minor not null,"
385 "unique(url, groupname)"
386 ")"
387 ))
388 return false;
389
390 res = sqlite3_exec(sql, "CREATE TABLE sequence_numbers "
391 "("
392 "url primary key, -- URL to read from\n"
393 "major not null, -- 0 in news servers, may be higher in depots\n"
394 "minor not null -- last article / packet sequence number we got\n"
395 ")", NULL, NULL, errmsg);
396 if (res != SQLITE_OK)
397 return false;
398
399 res = sqlite3_exec(sql, "INSERT INTO sequence_numbers "
400 "SELECT "
401 "(url || '/' || groupname), "
402 "major, "
403 "minor "
404 "FROM tmp", NULL, NULL, errmsg);
405 if (res != SQLITE_OK)
406 return false;
407
408 res = sqlite3_exec(sql, "DROP TABLE tmp", NULL, NULL, errmsg);
409 if (res != SQLITE_OK)
410 return false;
411
412
413 // migrate the netserver_manifests table
414 if (!move_table(sql, errmsg,
415 "netserver_manifests",
416 "tmp",
417 "("
418 "url not null,"
419 "groupname not null,"
420 "manifest not null,"
421 "unique(url, groupname, manifest)"
422 ")"
423 ))
424 return false;
425
426 res = sqlite3_exec(sql, "CREATE TABLE netserver_manifests "
427 "("
428 "url not null, -- url of some server\n"
429 "manifest not null, -- manifest which exists on url\n"
430 "unique(url, manifest)"
431 ")", NULL, NULL, errmsg);
432 if (res != SQLITE_OK)
433 return false;
434
435 res = sqlite3_exec(sql, "INSERT INTO netserver_manifests "
436 "SELECT "
437 "(url || '/' || groupname), "
438 "manifest "
439 "FROM tmp", NULL, NULL, errmsg);
440 if (res != SQLITE_OK)
441 return false;
442
443 res = sqlite3_exec(sql, "DROP TABLE tmp", NULL, NULL, errmsg);
444 if (res != SQLITE_OK)
445 return false;
446
447 return true;
448}
449
450static bool
451migrate_client_add_hashes_and_merkle_trees(sqlite3 * sql,
452 char ** errmsg)
453{
454
455 // add the column to manifest_certs
456 if (!move_table(sql, errmsg,
457 "manifest_certs",
458 "tmp",
459 "("
460 "id not null,"
461 "name not null,"
462 "value not null,"
463 "keypair not null,"
464 "signature not null,"
465 "unique(name, id, value, keypair, signature)"
466 ")"))
467 return false;
468
469 int res = sqlite3_exec(sql, "CREATE TABLE manifest_certs\n"
470 "(\n"
471 "hash not null unique, -- hash of remaining fields separated by \":\"\n"
472 "id not null, -- joins with manifests.id or manifest_deltas.id\n"
473 "name not null, -- opaque string chosen by user\n"
474 "value not null, -- opaque blob\n"
475 "keypair not null, -- joins with public_keys.id\n"
476 "signature not null, -- RSA/SHA1 signature of \"[name@id:val]\"\n"
477 "unique(name, id, value, keypair, signature)\n"
478 ")", NULL, NULL, errmsg);
479 if (res != SQLITE_OK)
480 return false;
481
482 res = sqlite3_exec(sql, "INSERT INTO manifest_certs "
483 "SELECT "
484 "sha1(':', id, name, value, keypair, signature), "
485 "id, name, value, keypair, signature "
486 "FROM tmp", NULL, NULL, errmsg);
487 if (res != SQLITE_OK)
488 return false;
489
490 res = sqlite3_exec(sql, "DROP TABLE tmp", NULL, NULL, errmsg);
491 if (res != SQLITE_OK)
492 return false;
493
494 // add the column to file_certs
495 if (!move_table(sql, errmsg,
496 "file_certs",
497 "tmp",
498 "("
499 "id not null,"
500 "name not null,"
501 "value not null,"
502 "keypair not null,"
503 "signature not null,"
504 "unique(name, id, value, keypair, signature)"
505 ")"))
506 return false;
507
508 res = sqlite3_exec(sql, "CREATE TABLE file_certs\n"
509 "(\n"
510 "hash not null unique, -- hash of remaining fields separated by \":\"\n"
511 "id not null, -- joins with files.id or file_deltas.id\n"
512 "name not null, -- opaque string chosen by user\n"
513 "value not null, -- opaque blob\n"
514 "keypair not null, -- joins with public_keys.id\n"
515 "signature not null, -- RSA/SHA1 signature of \"[name@id:val]\"\n"
516 "unique(name, id, value, keypair, signature)\n"
517 ")", NULL, NULL, errmsg);
518 if (res != SQLITE_OK)
519 return false;
520
521 res = sqlite3_exec(sql, "INSERT INTO file_certs "
522 "SELECT "
523 "sha1(':', id, name, value, keypair, signature), "
524 "id, name, value, keypair, signature "
525 "FROM tmp", NULL, NULL, errmsg);
526 if (res != SQLITE_OK)
527 return false;
528
529 res = sqlite3_exec(sql, "DROP TABLE tmp", NULL, NULL, errmsg);
530 if (res != SQLITE_OK)
531 return false;
532
533 // add the column to public_keys
534 if (!move_table(sql, errmsg,
535 "public_keys",
536 "tmp",
537 "("
538 "id primary key,"
539 "keydata not null"
540 ")"))
541 return false;
542
543 res = sqlite3_exec(sql, "CREATE TABLE public_keys\n"
544 "(\n"
545 "hash not null unique, -- hash of remaining fields separated by \":\"\n"
546 "id primary key, -- key identifier chosen by user\n"
547 "keydata not null -- RSA public params\n"
548 ")", NULL, NULL, errmsg);
549 if (res != SQLITE_OK)
550 return false;
551
552 res = sqlite3_exec(sql, "INSERT INTO public_keys "
553 "SELECT "
554 "sha1(':', id, keydata), "
555 "id, keydata "
556 "FROM tmp", NULL, NULL, errmsg);
557 if (res != SQLITE_OK)
558 return false;
559
560 res = sqlite3_exec(sql, "DROP TABLE tmp", NULL, NULL, errmsg);
561 if (res != SQLITE_OK)
562 return false;
563
564 // add the column to private_keys
565 if (!move_table(sql, errmsg,
566 "private_keys",
567 "tmp",
568 "("
569 "id primary key,"
570 "keydata not null"
571 ")"))
572 return false;
573
574 res = sqlite3_exec(sql, "CREATE TABLE private_keys\n"
575 "(\n"
576 "hash not null unique, -- hash of remaining fields separated by \":\"\n"
577 "id primary key, -- as in public_keys (same identifiers, in fact)\n"
578 "keydata not null -- encrypted RSA private params\n"
579 ")", NULL, NULL, errmsg);
580 if (res != SQLITE_OK)
581 return false;
582
583 res = sqlite3_exec(sql, "INSERT INTO private_keys "
584 "SELECT "
585 "sha1(':', id, keydata), "
586 "id, keydata "
587 "FROM tmp", NULL, NULL, errmsg);
588 if (res != SQLITE_OK)
589 return false;
590
591 res = sqlite3_exec(sql, "DROP TABLE tmp", NULL, NULL, errmsg);
592 if (res != SQLITE_OK)
593 return false;
594
595 // add the merkle tree stuff
596
597 res = sqlite3_exec(sql,
598 "CREATE TABLE merkle_nodes\n"
599 "(\n"
600 "type not null, -- \"key\", \"mcert\", \"fcert\", \"manifest\"\n"
601 "collection not null, -- name chosen by user\n"
602 "level not null, -- tree level this prefix encodes\n"
603 "prefix not null, -- label identifying node in tree\n"
604 "body not null, -- binary, base64'ed node contents\n"
605 "unique(type, collection, level, prefix)\n"
606 ")", NULL, NULL, errmsg);
607 if (res != SQLITE_OK)
608 return false;
609
610 return true;
611}
612
613static bool
614migrate_client_to_revisions(sqlite3 * sql,
615 char ** errmsg)
616{
617 int res;
618
619 res = sqlite3_exec(sql, "DROP TABLE schema_version;", NULL, NULL, errmsg);
620 if (res != SQLITE_OK)
621 return false;
622
623 res = sqlite3_exec(sql, "DROP TABLE posting_queue;", NULL, NULL, errmsg);
624 if (res != SQLITE_OK)
625 return false;
626
627 res = sqlite3_exec(sql, "DROP TABLE incoming_queue;", NULL, NULL, errmsg);
628 if (res != SQLITE_OK)
629 return false;
630
631 res = sqlite3_exec(sql, "DROP TABLE sequence_numbers;", NULL, NULL, errmsg);
632 if (res != SQLITE_OK)
633 return false;
634
635 res = sqlite3_exec(sql, "DROP TABLE file_certs;", NULL, NULL, errmsg);
636 if (res != SQLITE_OK)
637 return false;
638
639 res = sqlite3_exec(sql, "DROP TABLE netserver_manifests;", NULL, NULL, errmsg);
640 if (res != SQLITE_OK)
641 return false;
642
643 res = sqlite3_exec(sql, "DROP TABLE merkle_nodes;", NULL, NULL, errmsg);
644 if (res != SQLITE_OK)
645 return false;
646
647 res = sqlite3_exec(sql,
648 "CREATE TABLE merkle_nodes\n"
649 "(\n"
650 "type not null, -- \"key\", \"mcert\", \"fcert\", \"rcert\"\n"
651 "collection not null, -- name chosen by user\n"
652 "level not null, -- tree level this prefix encodes\n"
653 "prefix not null, -- label identifying node in tree\n"
654 "body not null, -- binary, base64'ed node contents\n"
655 "unique(type, collection, level, prefix)\n"
656 ")", NULL, NULL, errmsg);
657 if (res != SQLITE_OK)
658 return false;
659
660 res = sqlite3_exec(sql, "CREATE TABLE revision_certs\n"
661 "(\n"
662 "hash not null unique, -- hash of remaining fields separated by \":\"\n"
663 "id not null, -- joins with revisions.id\n"
664 "name not null, -- opaque string chosen by user\n"
665 "value not null, -- opaque blob\n"
666 "keypair not null, -- joins with public_keys.id\n"
667 "signature not null, -- RSA/SHA1 signature of \"[name@id:val]\"\n"
668 "unique(name, id, value, keypair, signature)\n"
669 ")", NULL, NULL, errmsg);
670 if (res != SQLITE_OK)
671 return false;
672
673 res = sqlite3_exec(sql, "CREATE TABLE revisions\n"
674 "(\n"
675 "id primary key, -- SHA1(text of revision)\n"
676 "data not null -- compressed, encoded contents of a revision\n"
677 ")", NULL, NULL, errmsg);
678 if (res != SQLITE_OK)
679 return false;
680
681 res = sqlite3_exec(sql, "CREATE TABLE revision_ancestry\n"
682 "(\n"
683 "parent not null, -- joins with revisions.id\n"
684 "child not null, -- joins with revisions.id\n"
685 "unique(parent, child)\n"
686 ")", NULL, NULL, errmsg);
687 if (res != SQLITE_OK)
688 return false;
689
690 return true;
691}
692
693
694static bool
695migrate_client_to_epochs(sqlite3 * sql,
696 char ** errmsg)
697{
698 int res;
699
700 res = sqlite3_exec(sql, "DROP TABLE merkle_nodes;", NULL, NULL, errmsg);
701 if (res != SQLITE_OK)
702 return false;
703
704
705 res = sqlite3_exec(sql,
706 "CREATE TABLE branch_epochs\n"
707 "(\n"
708 "hash not null unique, -- hash of remaining fields separated by \":\"\n"
709 "branch not null unique, -- joins with revision_certs.value\n"
710 "epoch not null -- random hex-encoded id\n"
711 ");", NULL, NULL, errmsg);
712 if (res != SQLITE_OK)
713 return false;
714
715 return true;
716}
717
718static bool
719migrate_client_to_vars(sqlite3 * sql,
720 char ** errmsg)
721{
722 int res;
723
724 res = sqlite3_exec(sql,
725 "CREATE TABLE db_vars\n"
726 "(\n"
727 "domain not null, -- scope of application of a var\n"
728 "name not null, -- var key\n"
729 "value not null, -- var value\n"
730 "unique(domain, name)\n"
731 ");", NULL, NULL, errmsg);
732 if (res != SQLITE_OK)
733 return false;
734
735 return true;
736}
737
738static bool
739migrate_client_to_add_indexes(sqlite3 * sql,
740 char ** errmsg)
741{
742 int res;
743
744 res = sqlite3_exec(sql,
745 "CREATE INDEX revision_ancestry__child "
746 "ON revision_ancestry (child)",
747 NULL, NULL, errmsg);
748 if (res != SQLITE_OK)
749 return false;
750
751 res = sqlite3_exec(sql,
752 "CREATE INDEX revision_certs__id "
753 "ON revision_certs (id);",
754 NULL, NULL, errmsg);
755 if (res != SQLITE_OK)
756 return false;
757
758 res = sqlite3_exec(sql,
759 "CREATE INDEX revision_certs__name_value "
760 "ON revision_certs (name, value);",
761 NULL, NULL, errmsg);
762 if (res != SQLITE_OK)
763 return false;
764
765 return true;
766}
767
768void
769migrate_monotone_schema(sqlite3 *sql)
770{
771
772 migrator m;
773
774 m.add("edb5fa6cef65bcb7d0c612023d267c3aeaa1e57a",
775 &migrate_client_merge_url_and_group);
776
777 m.add("f042f3c4d0a4f98f6658cbaf603d376acf88ff4b",
778 &migrate_client_add_hashes_and_merkle_trees);
779
780 m.add("8929e54f40bf4d3b4aea8b037d2c9263e82abdf4",
781 &migrate_client_to_revisions);
782
783 m.add("c1e86588e11ad07fa53e5d294edc043ce1d4005a",
784 &migrate_client_to_epochs);
785
786 m.add("40369a7bda66463c5785d160819ab6398b9d44f4",
787 &migrate_client_to_vars);
788
789 m.add("e372b508bea9b991816d1c74680f7ae10d2a6d94",
790 &migrate_client_to_add_indexes);
791
792 // IMPORTANT: whenever you modify this to add a new schema version, you must
793 // also add a new migration test for the new schema version. See
794 // tests/t_migrate_schema.at for details.
795
796 m.migrate(sql, "1509fd75019aebef5ac3da3a5edf1312393b70e9");
797}

Archive Download this file

Branches

Tags

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