monotone

monotone Mtn Source Tree

Root/src/cmd_db.cc

1// Copyright (C) 2002 Graydon Hoare <graydon@pobox.com>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10#include "base.hh"
11#include <iostream>
12#include <set>
13#include <utility>
14
15#include "charset.hh"
16#include "cmd.hh"
17#include "revision.hh"
18#include "roster.hh"
19#include "constants.hh"
20#include "app_state.hh"
21#include "database.hh"
22#include "project.hh"
23#include "keys.hh"
24#include "key_store.hh"
25#include "work.hh"
26#include "rev_height.hh"
27#include "transforms.hh"
28#include "ui.hh"
29#include "vocab_cast.hh"
30#include "migration.hh"
31
32using std::cin;
33using std::cout;
34using std::make_pair;
35using std::pair;
36using std::set;
37using std::string;
38using std::vector;
39
40CMD_GROUP(db, "db", "", CMD_REF(database),
41 N_("Deals with the database"),
42 "");
43
44CMD(db_init, "init", "", CMD_REF(db), "",
45 N_("Initializes a database"),
46 N_("Creates a new database file and initializes it."),
47 options::opts::none)
48{
49 E(args.size() == 0, origin::user,
50 F("no arguments needed"));
51
52 database db(app);
53 db.initialize();
54}
55
56CMD(db_info, "info", "", CMD_REF(db), "",
57 N_("Shows information about the database"),
58 "",
59 options::opts::full)
60{
61 E(args.size() == 0, origin::user,
62 F("no arguments needed"));
63
64 database db(app);
65 db.info(cout, app.opts.full);
66}
67
68CMD(db_version, "version", "", CMD_REF(db), "",
69 N_("Shows the database's version"),
70 "",
71 options::opts::none)
72{
73 E(args.size() == 0, origin::user,
74 F("no arguments needed"));
75
76 database db(app);
77 db.version(cout);
78}
79
80CMD(db_fix_certs, "fix_certs", "", CMD_REF(db), "",
81 N_("Attempt to fix bad certs"),
82 N_("Older monotone versions could sometimes associate certs with "
83 "the wrong key. This fixes such certs if you have the correct key, "
84 "and can optionally drop any certs that you don't have the "
85 "correct key for. This should only be needed if you had such certs "
86 "in your db when upgrading from 0.44 or earlier, or if you loaded "
87 "such certs with 'mtn read'."),
88 options::opts::drop_bad_certs)
89{
90 E(args.size() == 0, origin::user,
91 F("no arguments needed"));
92
93 database db(app);
94 db.fix_bad_certs(app.opts.drop_bad_certs);
95}
96
97CMD(db_dump, "dump", "", CMD_REF(db), "",
98 N_("Dumps the contents of the database"),
99 N_("Generates a list of SQL instructions that represent the whole "
100 "contents of the database. The resulting output is useful to later "
101 "restore the database from a text file that serves as a backup."),
102 options::opts::none)
103{
104 E(args.size() == 0, origin::user,
105 F("no arguments needed"));
106
107 database db(app);
108 db.dump(cout);
109}
110
111CMD(db_load, "load", "", CMD_REF(db), "",
112 N_("Loads the contents of the database"),
113 N_("Reads a list of SQL instructions that regenerate the contents of "
114 "the database. This is supposed to be used in conjunction with the "
115 "output generated by the 'dump' command."),
116 options::opts::none)
117{
118 E(args.size() == 0, origin::user,
119 F("no arguments needed"));
120
121 database db(app);
122 db.load(cin);
123}
124
125CMD(db_migrate, "migrate", "", CMD_REF(db), "",
126 N_("Migrates the database to a newer schema"),
127 N_("Updates the database's internal schema to the most recent one. "
128 "Needed to automatically resolve incompatibilities that may be "
129 "introduced in newer versions of monotone."),
130 options::opts::none)
131{
132 key_store keys(app);
133
134 E(args.size() == 0, origin::user,
135 F("no arguments needed"));
136
137 migration_status mstat;
138 {
139 database db(app);
140 db.migrate(keys, mstat);
141 database::reset_cache();
142 }
143
144 if (mstat.need_regen())
145 {
146 database db(app);
147 regenerate_caches(db, mstat.regen_type());
148 }
149
150 if (mstat.need_flag_day())
151 {
152 P(F("NOTE: because this database was last used by a rather old version\n"
153 "of monotone, you're not done yet. If you're a project leader, then\n"
154 "see the file UPGRADE for instructions on running '%s db %s'")
155 % prog_name % mstat.flag_day_name());
156 }
157}
158
159CMD(db_execute, "execute", "", CMD_REF(db), "",
160 N_("Executes an SQL command on the database"),
161 N_("Directly executes the given SQL command on the database"),
162 options::opts::none)
163{
164 if (args.size() != 1)
165 throw usage(execid);
166
167 database db(app);
168 db.debug(idx(args, 0)(), cout);
169}
170
171CMD_GROUP(db_local, "local", "", CMD_REF(database),
172 N_("Commands that delete items from the local database"),
173 N_("Deletions cannot be propagated through netsync, so the deleted items "
174 "will come back if you sync with a database that still has them."));
175
176CMD(db_kill_rev_locally, "kill_revision", "", CMD_REF(db_local), "REVID",
177 N_("Kills a revision from the local database"),
178 "",
179 options::opts::none)
180{
181 if (args.size() != 1)
182 throw usage(execid);
183
184 revision_id revid;
185
186 database db(app);
187 project_t project(db);
188 complete(app.opts, app.lua, project, idx(args, 0)(), revid);
189
190 // Check that the revision does not have any children
191 std::set<revision_id> children;
192 db.get_revision_children(revid, children);
193 E(!children.size(), origin::user,
194 F("revision %s already has children. We cannot kill it.")
195 % revid);
196
197 // If we're executing this in a workspace, check if the workspace parent
198 // revision is the one to kill. If so, write out the changes made in this
199 // particular revision to _MTN/revision to allow the user redo his (fixed)
200 // commit afterwards. Of course we can't do this at all if
201 //
202 // a) the user is currently not inside a workspace
203 // b) the user has updated the current workspace to another revision already
204 // thus the working revision is no longer based on the revision we're
205 // trying to kill
206 // c) there are uncomitted changes in the working revision of this workspace.
207 // this *eventually* could be handled with a workspace merge scenario, but
208 // is left out for now
209 if (workspace::found)
210 {
211 workspace work(app);
212 revision_t old_work_rev;
213 work.get_work_rev(old_work_rev);
214
215 for (edge_map::const_iterator i = old_work_rev.edges.begin();
216 i != old_work_rev.edges.end(); i++)
217 {
218 if (edge_old_revision(i) != revid)
219 continue;
220
221 E(!work.has_changes(db), origin::user,
222 F("cannot kill revision %s,\n"
223 "because it would leave the current workspace in an invalid\n"
224 "state, from which monotone cannot recover automatically since\n"
225 "the workspace contains uncommitted changes.\n"
226 "Consider updating your workspace to another revision first,\n"
227 "before you try to kill this revision again.")
228 % revid);
229
230 P(F("applying changes from %s on the current workspace")
231 % revid);
232
233 revision_t new_work_rev;
234 db.get_revision(revid, new_work_rev);
235 new_work_rev.made_for = made_for_workspace;
236 work.put_work_rev(new_work_rev);
237 work.maybe_update_inodeprints(db);
238
239 // extra paranoia... we _should_ never run this section twice
240 // since a merged workspace would fail early with work.has_changes()
241 break;
242 }
243 }
244
245 db.delete_existing_rev_and_certs(revid);
246}
247
248CMD(db_kill_certs_locally, "kill_certs", "", CMD_REF(db_local),
249 "SELECTOR CERTNAME [CERTVAL]",
250 N_("Deletes the specified certs from the local database"),
251 N_("Deletes all certs which are on the given revision(s) and "
252 "have the given name and if a value is specified then also "
253 "the given value."),
254 options::opts::none)
255{
256 if (args.size() < 2 || args.size() > 3)
257 throw usage(execid);
258
259 string selector = idx(args,0)();
260 cert_name name = typecast_vocab<cert_name>(idx(args,1));
261
262 database db(app);
263 project_t project(db);
264
265 set<revision_id> revisions;
266 complete(app.opts, app.lua, project, selector, revisions);
267
268
269 transaction_guard guard(db);
270 set<cert_value> branches;
271
272 if (args.size() == 2)
273 {
274 L(FL("deleting all certs named '%s' on %d revisions")
275 % name % revisions.size());
276 for (set<revision_id>::const_iterator r = revisions.begin();
277 r != revisions.end(); ++r)
278 {
279 if (name == branch_cert_name)
280 {
281 vector<cert> to_delete;
282 db.get_revision_certs(*r, name, to_delete);
283 for (vector<cert>::const_iterator i = to_delete.begin();
284 i != to_delete.end(); ++i)
285 {
286 branches.insert(i->value);
287 }
288 }
289 db.delete_certs_locally(*r, name);
290 }
291 }
292 else
293 {
294 cert_value value = typecast_vocab<cert_value>(idx(args,2));
295 L(FL("deleting all certs with name '%s' and value '%s' on %d revisions")
296 % name % value % revisions.size());
297 for (set<revision_id>::const_iterator r = revisions.begin();
298 r != revisions.end(); ++r)
299 {
300 db.delete_certs_locally(*r, name, value);
301 }
302 branches.insert(value);
303 }
304
305 for (set<cert_value>::const_iterator i = branches.begin();
306 i != branches.end(); ++i)
307 {
308 db.recalc_branch_leaves(*i);
309 set<revision_id> leaves;
310 db.get_branch_leaves(*i, leaves);
311 if (leaves.empty())
312 db.clear_epoch(typecast_vocab<branch_name>(*i));
313 }
314
315 guard.commit();
316}
317
318CMD(db_check, "check", "", CMD_REF(db), "",
319 N_("Does some sanity checks on the database"),
320 N_("Ensures that the database is consistent by issuing multiple "
321 "checks."),
322 options::opts::none)
323{
324 E(args.size() == 0, origin::user,
325 F("no arguments needed"));
326
327 database db(app);
328 check_db(db);
329}
330
331CMD(db_changesetify, "changesetify", "", CMD_REF(db), "",
332 N_("Converts the database to the changeset format"),
333 "",
334 options::opts::none)
335{
336 database db(app);
337 key_store keys(app);
338 project_t project(db);
339
340 E(args.size() == 0, origin::user,
341 F("no arguments needed"));
342
343 db.ensure_open_for_format_changes();
344 db.check_is_not_rosterified();
345
346 // early short-circuit to avoid failure after lots of work
347 cache_user_key(app.opts, project, keys, app.lua);
348
349 build_changesets_from_manifest_ancestry(db, keys, project, set<string>());
350}
351
352CMD(db_rosterify, "rosterify", "", CMD_REF(db), "",
353 N_("Converts the database to the rosters format"),
354 "",
355 options::opts::attrs_to_drop)
356{
357 database db(app);
358 key_store keys(app);
359 project_t project(db);
360
361 E(args.size() == 0, origin::user,
362 F("no arguments needed"));
363
364 db.ensure_open_for_format_changes();
365 db.check_is_not_rosterified();
366
367 // early short-circuit to avoid failure after lots of work
368 cache_user_key(app.opts, project, keys, app.lua);
369
370 build_roster_style_revs_from_manifest_style_revs(db, keys, project,
371 app.opts.attrs_to_drop);
372}
373
374CMD(db_regenerate_caches, "regenerate_caches", "", CMD_REF(db), "",
375 N_("Regenerates the caches stored in the database"),
376 "",
377 options::opts::none)
378{
379 E(args.size() == 0, origin::user,
380 F("no arguments needed"));
381
382 database db(app);
383 regenerate_caches(db, regen_all);
384}
385
386CMD_HIDDEN(clear_epoch, "clear_epoch", "", CMD_REF(db), "BRANCH",
387 N_("Clears the branch's epoch"),
388 "",
389 options::opts::none)
390{
391 if (args.size() != 1)
392 throw usage(execid);
393
394 database db(app);
395 db.clear_epoch(typecast_vocab<branch_name>(idx(args, 0)));
396}
397
398CMD(db_set_epoch, "set_epoch", "", CMD_REF(db), "BRANCH EPOCH",
399 N_("Sets the branch's epoch"),
400 "",
401 options::opts::none)
402{
403 if (args.size() != 2)
404 throw usage(execid);
405
406 E(idx(args, 1)().size() == constants::epochlen, origin::user,
407 F("The epoch must be %d characters") % constants::epochlen);
408
409 epoch_data ed(decode_hexenc_as<epoch_data>(idx(args, 1)(), origin::user));
410 database db(app);
411 db.set_epoch(branch_name(idx(args, 0)(), origin::user), ed);
412}
413
414CMD(set, "set", "", CMD_REF(variables), N_("DOMAIN NAME VALUE"),
415 N_("Sets a database variable"),
416 N_("This command modifies (or adds if it did not exist before) the "
417 "variable named NAME, stored in the database, and sets it to the "
418 "given value in VALUE. The variable is placed in the domain DOMAIN."),
419 options::opts::none)
420{
421 if (args.size() != 3)
422 throw usage(execid);
423
424 var_domain d = typecast_vocab<var_domain>(idx(args, 0));
425 var_name n;
426 var_value v;
427 n = typecast_vocab<var_name>(idx(args, 1));
428 v = typecast_vocab<var_value>(idx(args, 2));
429
430 database db(app);
431 db.set_var(make_pair(d, n), v);
432}
433
434CMD(unset, "unset", "", CMD_REF(variables), N_("DOMAIN NAME"),
435 N_("Unsets a database variable"),
436 N_("This command removes the variable NAME from domain DOMAIN, which "
437 "was previously stored in the database."),
438 options::opts::none)
439{
440 if (args.size() != 2)
441 throw usage(execid);
442
443 var_domain d = typecast_vocab<var_domain>(idx(args, 0));
444 var_name n;
445 n = typecast_vocab<var_name>(idx(args, 1));
446 var_key k(d, n);
447
448 database db(app);
449 E(db.var_exists(k), origin::user,
450 F("no var with name '%s' in domain '%s'") % n % d);
451 db.clear_var(k);
452}
453
454CMD(register_workspace, "register_workspace", "", CMD_REF(variables),
455 N_("[WORKSPACE_PATH]"),
456 N_("Registers a new workspace for the current database"),
457 N_("This command adds WORKSPACE_PATH to the list of `known-workspaces'."),
458 options::opts::none)
459{
460 if (args.size() > 1)
461 throw usage(execid);
462
463 E(args.size() == 1 || workspace::found, origin::user,
464 F("no workspace given"));
465
466 system_path workspace;
467 if (args.size() == 1)
468 workspace = system_path(idx(args, 0)(), origin::user);
469 else
470 get_current_workspace(workspace);
471
472 database db(app);
473 db.register_workspace(workspace);
474}
475
476CMD(unregister_workspace, "unregister_workspace", "", CMD_REF(variables),
477 N_("[WORKSPACE_PATH]"),
478 N_("Unregisters an existing workspace for the current database"),
479 N_("This command removes WORKSPACE_PATH to the list of `known-workspaces'."),
480 options::opts::none)
481{
482 if (args.size() > 1)
483 throw usage(execid);
484
485 E(args.size() == 1 || workspace::found, origin::user,
486 F("no workspace given"));
487
488 system_path workspace;
489 if (args.size() == 1)
490 workspace = system_path(idx(args, 0)(), origin::user);
491 else
492 get_current_workspace(workspace);
493
494 database db(app);
495 db.unregister_workspace(workspace);
496}
497
498CMD(cleanup_workspace_list, "cleanup_workspace_list", "", CMD_REF(variables), "",
499 N_("Removes all invalid, registered workspace paths for the current database"),
500 "",
501 options::opts::none)
502{
503 if (args.size() != 0)
504 throw usage(execid);
505
506 vector<system_path> original_workspaces, valid_workspaces;
507
508 database db(app);
509 db.get_registered_workspaces(original_workspaces);
510
511 database_path_helper helper(app.lua);
512
513 for (vector<system_path>::const_iterator i = original_workspaces.begin();
514 i != original_workspaces.end(); ++i)
515 {
516 system_path workspace_path(*i);
517 if (!directory_exists(workspace_path / bookkeeping_root_component))
518 {
519 L(FL("ignoring missing workspace '%s'") % workspace_path);
520 continue;
521 }
522
523 options workspace_opts;
524 workspace::get_options(workspace_path, workspace_opts);
525
526 system_path workspace_db_path;
527 helper.get_database_path(workspace_opts, workspace_db_path);
528
529 if (workspace_db_path != db.get_filename())
530 {
531 L(FL("ignoring workspace '%s', expected database %s, "
532 "but has %s configured in _MTN/options")
533 % workspace_path % db.get_filename() % workspace_db_path);
534 continue;
535 }
536
537 valid_workspaces.push_back(workspace_path);
538 }
539
540 db.set_registered_workspaces(valid_workspaces);
541}
542
543CMD(complete, "complete", "", CMD_REF(informative),
544 N_("(revision|file|key) PARTIAL-ID"),
545 N_("Completes a partial identifier"),
546 "",
547 options::opts::none)
548{
549 if (args.size() != 2)
550 throw usage(execid);
551
552 database db(app);
553 project_t project(db);
554
555 E(idx(args, 1)().find_first_not_of("abcdef0123456789") == string::npos,
556 origin::user,
557 F("non-hex digits in partial id"));
558
559 if (idx(args, 0)() == "revision")
560 {
561 set<revision_id> completions;
562 db.complete(idx(args, 1)(), completions);
563 for (set<revision_id>::const_iterator i = completions.begin();
564 i != completions.end(); ++i)
565 {
566 if (!app.opts.full) cout << *i << '\n';
567 else cout << describe_revision(app.opts, app.lua, project, *i) << '\n';
568 }
569 }
570 else if (idx(args, 0)() == "file")
571 {
572 set<file_id> completions;
573 db.complete(idx(args, 1)(), completions);
574 for (set<file_id>::const_iterator i = completions.begin();
575 i != completions.end(); ++i)
576 cout << *i << '\n';
577 }
578 else if (idx(args, 0)() == "key")
579 {
580 typedef set< pair<key_id, utf8 > > completions_t;
581 completions_t completions;
582 db.complete(idx(args, 1)(), completions);
583 for (completions_t::const_iterator i = completions.begin();
584 i != completions.end(); ++i)
585 {
586 cout << i->first;
587 if (app.opts.full) cout << ' ' << i->second();
588 cout << '\n';
589 }
590 }
591 else
592 throw usage(execid);
593}
594
595CMD_HIDDEN(test_migration_step, "test_migration_step", "", CMD_REF(db),
596 "SCHEMA",
597 N_("Runs one step of migration on the specified database"),
598 N_("This command migrates the given database from the specified "
599 "schema in SCHEMA to its successor."),
600 options::opts::none)
601{
602 database db(app);
603 key_store keys(app);
604
605 if (args.size() != 1)
606 throw usage(execid);
607 db.test_migration_step(keys, idx(args,0)());
608}
609
610CMD_HIDDEN(rev_height, "rev_height", "", CMD_REF(informative), N_("REV"),
611 N_("Shows a revision's height"),
612 "",
613 options::opts::none)
614{
615 if (args.size() != 1)
616 throw usage(execid);
617
618 revision_id rid(decode_hexenc_as<revision_id>(idx(args, 0)(), origin::user));
619 database db(app);
620 E(db.revision_exists(rid), origin::user,
621 F("no revision %s found in database") % rid);
622 rev_height height;
623 db.get_rev_height(rid, height);
624 P(F("cached height: %s") % height);
625}
626
627// loading revisions is relatively fast
628
629CMD_HIDDEN(load_revisions, "load_revisions", "", CMD_REF(db), "",
630 N_("Load all revisions from the database"),
631 N_("This command loads all revisions from the database and is "
632 "intended to be used for timing revision loading performance."),
633 options::opts::none)
634{
635 database db(app);
636 set<revision_id> ids;
637 vector<revision_id> revisions;
638
639 db.get_revision_ids(ids);
640 toposort(db, ids, revisions);
641
642 P(F("loading revisions"));
643 ticker loaded(_("revisions"), "r", 64);
644 loaded.set_total(revisions.size());
645
646 typedef vector<revision_id>::const_iterator revision_iterator;
647
648 for (revision_iterator i = revisions.begin(); i != revisions.end(); ++i)
649 {
650 revision_t revision;
651 db.get_revision(*i, revision);
652 ++loaded;
653 }
654}
655
656// loading rosters is slow compared with files, revisions or certs
657
658CMD_HIDDEN(load_rosters, "load_rosters", "", CMD_REF(db), "",
659 N_("Load all roster versions from the database"),
660 N_("This command loads all roster versions from the database and is "
661 "intended to be used for timing roster reconstruction performance."),
662 options::opts::none)
663{
664 database db(app);
665 set<revision_id> ids;
666 vector<revision_id> rosters;
667
668 db.get_revision_ids(ids);
669 toposort(db, ids, rosters);
670
671 P(F("loading rosters"));
672 ticker loaded(_("rosters"), "r", 1);
673 loaded.set_total(rosters.size());
674 typedef vector<revision_id>::const_iterator roster_iterator;
675
676 for (roster_iterator i = rosters.begin(); i != rosters.end(); ++i)
677 {
678 roster_t roster;
679 db.get_roster(*i, roster);
680 ++loaded;
681 }
682}
683
684// loading files is slower than revisions but faster than rosters
685
686CMD_HIDDEN(load_files, "load_files", "", CMD_REF(db), "",
687 N_("Load all file versions from the database"),
688 N_("This command loads all files versions from the database and is "
689 "intended to be used for timing file reconstruction performance."),
690 options::opts::none)
691{
692 database db(app);
693 set<file_id> files;
694 db.get_file_ids(files);
695
696 P(F("loading files"));
697 ticker loaded(_("files"), "f", 1);
698 loaded.set_total(files.size());
699
700 typedef set<file_id>::const_iterator file_iterator;
701
702 for (file_iterator i = files.begin(); i != files.end(); ++i)
703 {
704 file_data file;
705 db.get_file_version(*i, file);
706 ++loaded;
707 }
708}
709
710// loading certs is fast
711
712CMD_HIDDEN(load_certs, "load_certs", "", CMD_REF(db), "",
713 N_("Load all certs from the database"),
714 N_("This command loads all certs from the database and is "
715 "intended to be used for timing cert loading performance."),
716 options::opts::none)
717{
718 database db(app);
719 vector<cert> certs;
720
721 P(F("loading certs"));
722 db.get_revision_certs(certs);
723 P(F("loaded %d certs") % certs.size());
724}
725
726// Local Variables:
727// mode: C++
728// fill-column: 76
729// c-file-style: "gnu"
730// indent-tabs-mode: nil
731// End:
732// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

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