cli: add global option "--uuid"
authorDavid Bremner <david@tethera.net>
Sun, 5 Apr 2015 22:39:55 +0000 (07:39 +0900)
committerDavid Bremner <david@tethera.net>
Fri, 14 Aug 2015 16:23:49 +0000 (18:23 +0200)
The function notmuch_exit_if_unmatched_db_uuid is split from
notmuch_process_shared_options because it needs an open notmuch
database.

There are two exceptional cases in uuid handling.

1) notmuch config and notmuch setup don't currently open the database,
   so it doesn't make sense to check the UUID.

2) notmuch compact opens the database inside the library, so we either
   need to open the database just to check uuid, or change the API.

17 files changed:
doc/man1/notmuch.rst
notmuch-client.h
notmuch-compact.c
notmuch-config.c
notmuch-count.c
notmuch-dump.c
notmuch-insert.c
notmuch-new.c
notmuch-reply.c
notmuch-restore.c
notmuch-search.c
notmuch-setup.c
notmuch-show.c
notmuch-tag.c
notmuch.c
test/T570-revision-tracking.sh
test/random-corpus.c

index 0401c91bea89d3a0381eee37c2d59064ac69ec8c..3acfbdb436db953f3889e98e75f913135ae06858 100644 (file)
@@ -51,9 +51,16 @@ Supported global options for ``notmuch`` include
        Specify the configuration file to use. This overrides any
        configuration file specified by ${NOTMUCH\_CONFIG}.
 
+    ``--uuid=HEX``
+       Enforce that the database UUID (a unique identifier which
+       persists until e.g. the database is compacted)
+       is HEX; exit with an error if it is not. This is useful to
+       detect rollover in modification counts on messages. You can
+       find this UUID using e.g. ``notmuch count --lastmod``
+
 All global options except ``--config`` can also be specified after the
-command. For example, ``notmuch subcommand --version`` is equivalent to
-``notmuch --version subcommand``.
+command. For example, ``notmuch subcommand --uuid=HEX`` is
+equivalent to ``notmuch --uuid=HEX subcommand``.
 
 COMMANDS
 ========
index 78680aa122af56dc9cd9432982e51e713594db14..4a4f86c41b8794c5adc9b323779cdacc496cba7d 100644 (file)
@@ -466,7 +466,11 @@ notmuch_database_dump (notmuch_database_t *notmuch,
                       notmuch_bool_t gzip_output);
 
 #include "command-line-arguments.h"
+
+extern char *notmuch_requested_db_uuid;
 extern const notmuch_opt_desc_t  notmuch_shared_options [];
+void notmuch_exit_if_unmatched_db_uuid (notmuch_database_t *notmuch);
+
 void notmuch_process_shared_options (const char* subcommand_name);
 int notmuch_minimal_options (const char* subcommand_name,
                             int argc, char **argv);
index 5be551d4ed733750cf1d685bcc89ac831e19de4b..937372161df1abc95cc569da44a212367fe8a01f 100644 (file)
@@ -46,6 +46,11 @@ notmuch_compact_command (notmuch_config_t *config, int argc, char *argv[])
     if (opt_index < 0)
        return EXIT_FAILURE;
 
+    if (notmuch_requested_db_uuid) {
+       fprintf (stderr, "Error: --uuid not implemented for compact\n");
+       return EXIT_FAILURE;
+    }
+
     notmuch_process_shared_options (argv[0]);
 
     if (! quiet)
index 934827874b1eb3548fa6b9fa1fdf4d9955174ee6..d252bb25d4bb565d78c88a14e5719949259a076d 100644 (file)
@@ -878,6 +878,10 @@ notmuch_config_command (notmuch_config_t *config, int argc, char *argv[])
     if (opt_index < 0)
        return EXIT_FAILURE;
 
+    if (notmuch_requested_db_uuid)
+       fprintf (stderr, "Warning: ignoring --uuid=%s\n",
+                notmuch_requested_db_uuid);
+
     /* skip at least subcommand argument */
     argc-= opt_index;
     argv+= opt_index;
index 182710a67b0e8f61867860c6c7ad0c807ce7fb84..f26e726ae06b83c768e18d4f5309d7d77010c659 100644 (file)
@@ -189,6 +189,8 @@ notmuch_count_command (notmuch_config_t *config, int argc, char *argv[])
                               NOTMUCH_DATABASE_MODE_READ_ONLY, &notmuch))
        return EXIT_FAILURE;
 
+    notmuch_exit_if_unmatched_db_uuid (notmuch);
+
     query_str = query_string_from_args (config, argc-opt_index, argv+opt_index);
     if (query_str == NULL) {
        fprintf (stderr, "Out of memory.\n");
index fab22bdd6bcfe7f1b0ca26e433001d49e416c4ab..24fc2f237ae5c83975ffaa587ec6b66f12955a64 100644 (file)
@@ -215,6 +215,8 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])
                               NOTMUCH_DATABASE_MODE_READ_WRITE, &notmuch))
        return EXIT_FAILURE;
 
+    notmuch_exit_if_unmatched_db_uuid (notmuch);
+
     char *output_file_name = NULL;
     int opt_index;
 
index c277d6206712a23718cbd4c75d55c7fc3bb688e7..5205c17a290fb6cbea318d9cdfb76458953561c9 100644 (file)
@@ -536,6 +536,8 @@ notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[])
                               NOTMUCH_DATABASE_MODE_READ_WRITE, &notmuch))
        return EXIT_FAILURE;
 
+    notmuch_exit_if_unmatched_db_uuid (notmuch);
+
     /* Write the message to the Maildir new directory. */
     newpath = maildir_write_new (config, STDIN_FILENO, maildir);
     if (! newpath) {
index ee786a3bbbb9ada837dcab727b43392ff59280c4..514e06a4d1f31326a805d1cabef4fb6631ab4b9d 100644 (file)
@@ -1009,10 +1009,11 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[])
                fputs (status_string, stderr);
                free (status_string);
            }
-
            return EXIT_FAILURE;
        }
 
+       notmuch_exit_if_unmatched_db_uuid (notmuch);
+
        if (notmuch_database_needs_upgrade (notmuch)) {
            time_t now = time (NULL);
            struct tm *gm_time = gmtime (&now);
index 4464741fe3567e974e6f07038b93f6446b744150..7c5c28f427819c19f883f30b65f0c3f684537587 100644 (file)
@@ -831,6 +831,8 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[])
                               NOTMUCH_DATABASE_MODE_READ_ONLY, &notmuch))
        return EXIT_FAILURE;
 
+    notmuch_exit_if_unmatched_db_uuid (notmuch);
+
     query = notmuch_query_create (notmuch, query_string);
     if (query == NULL) {
        fprintf (stderr, "Out of memory\n");
index 2a534dc4544886febb16a246c2caf8d60245baf0..9abc64fdd89e6ae7c61e84bc3d3f7f269e72c8de 100644 (file)
@@ -165,6 +165,8 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[])
     }
 
     notmuch_process_shared_options (argv[0]);
+    notmuch_exit_if_unmatched_db_uuid (notmuch);
+
     name_for_error = input_file_name ? input_file_name : "stdin";
 
     if (! accumulate)
index b89a17e5c4bfd5adeb5c3a9ce4aa74ca1c58da14..3076c3f637b1ef5d37b44d023a5424fb50052adb 100644 (file)
@@ -583,6 +583,8 @@ _notmuch_search_prepare (search_context_t *ctx, notmuch_config_t *config, int ar
        return EXIT_FAILURE;
     }
 
+    notmuch_exit_if_unmatched_db_uuid (ctx->notmuch);
+
     query_str = query_string_from_args (ctx->notmuch, argc, argv);
     if (query_str == NULL) {
        fprintf (stderr, "Out of memory.\n");
index 7dd5822a9365c0490b9860532de03c1c4c18251c..9aaf9286e8b257bd4946c7f81804b80117d5e3ca 100644 (file)
@@ -148,6 +148,10 @@ notmuch_setup_command (notmuch_config_t *config,
     if (notmuch_minimal_options ("setup", argc, argv) < 0)
        return EXIT_FAILURE;
 
+    if (notmuch_requested_db_uuid)
+       fprintf (stderr, "Warning: ignoring --uuid=%s\n",
+                notmuch_requested_db_uuid);
+
     if (notmuch_config_is_new (config))
        welcome_message_pre_setup ();
 
index b80933ad3a00963e07047c56707863291d9e321e..6ef33085acb1060934940c6db55b5ef1b6764e34 100644 (file)
@@ -1213,6 +1213,8 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
                               NOTMUCH_DATABASE_MODE_READ_ONLY, &notmuch))
        return EXIT_FAILURE;
 
+    notmuch_exit_if_unmatched_db_uuid (notmuch);
+
     query = notmuch_query_create (notmuch, query_string);
     if (query == NULL) {
        fprintf (stderr, "Out of memory\n");
index 38d99aa9db0be0a40ee720b5776f0fa3ae7167f6..7ae98f6ca42056b0285255a4c1958b91932f2d2b 100644 (file)
@@ -261,6 +261,8 @@ notmuch_tag_command (notmuch_config_t *config, int argc, char *argv[])
                               NOTMUCH_DATABASE_MODE_READ_WRITE, &notmuch))
        return EXIT_FAILURE;
 
+    notmuch_exit_if_unmatched_db_uuid (notmuch);
+
     if (notmuch_config_get_maildir_synchronize_flags (config))
        tag_flags |= TAG_FLAG_MAILDIR_SYNC;
 
index 9580c3fe1e062b4821b8bd10677aafbd8f5ddc5c..ce6c5756c9700af25fa79d3e3f5379b7b9122e2c 100644 (file)
--- a/notmuch.c
+++ b/notmuch.c
@@ -47,10 +47,12 @@ static int
 _help_for (const char *topic);
 
 static notmuch_bool_t print_version = FALSE, print_help = FALSE;
+char *notmuch_requested_db_uuid = NULL;
 
 const notmuch_opt_desc_t notmuch_shared_options [] = {
     { NOTMUCH_OPT_BOOLEAN, &print_version, "version", 'v', 0 },
     { NOTMUCH_OPT_BOOLEAN, &print_help, "help", 'h', 0 },
+    { NOTMUCH_OPT_STRING, &notmuch_requested_db_uuid, "uuid", 'u', 0 },
     {0, 0, 0, 0, 0}
 };
 
@@ -218,6 +220,22 @@ be supported in the future.\n", notmuch_format_version);
     }
 }
 
+void
+notmuch_exit_if_unmatched_db_uuid (notmuch_database_t *notmuch)
+{
+    const char *uuid = NULL;
+
+    if (!notmuch_requested_db_uuid)
+       return;
+    IGNORE_RESULT (notmuch_database_get_revision (notmuch, &uuid));
+
+    if (strcmp (notmuch_requested_db_uuid, uuid) != 0){
+       fprintf (stderr, "Error: requested database revision %s does not match %s\n",
+                notmuch_requested_db_uuid, uuid);
+       exit (1);
+    }
+}
+
 static void
 exec_man (const char *page)
 {
index 4fff6896dd203b8dbb64760ad31bb3b633c90859..20b44cbe52943b5f098e552cfe5d2b112e016190 100755 (executable)
@@ -46,4 +46,31 @@ notmuch tag +a-random-tag-8743632 '*'
 after=$(notmuch count --lastmod '*' | cut -f3)
 result=$(($before < $after))
 test_expect_equal 1 ${result}
+
+notmuch count --lastmod '*' | cut -f2 > UUID
+
+test_expect_success 'search succeeds with correct uuid' \
+                   "notmuch search --uuid=$(cat UUID) '*'"
+
+test_expect_success 'uuid works as global option ' \
+                   "notmuch --uuid=$(cat UUID) search '*'"
+
+test_expect_code 1 'uuid works as global option II' \
+                   "notmuch --uuid=this-is-no-uuid search '*'"
+
+test_expect_code 1 'search fails with incorrect uuid' \
+                "notmuch search --uuid=this-is-no-uuid '*'"
+
+test_expect_success 'show succeeds with correct uuid' \
+                   "notmuch show --uuid=$(cat UUID) '*'"
+
+test_expect_code 1 'show fails with incorrect uuid' \
+                "notmuch show --uuid=this-is-no-uuid '*'"
+
+test_expect_success 'tag succeeds with correct uuid' \
+                   "notmuch tag --uuid=$(cat UUID) +test '*'"
+
+test_expect_code 1 'tag fails with incorrect uuid' \
+                "notmuch tag --uuid=this-is-no-uuid '*' +test2"
+
 test_done
index b377eb40bfdce0964880e736f493f33f3e2066d0..d74271d932312d77ea66a02131a753ea4b931762 100644 (file)
@@ -119,6 +119,8 @@ const notmuch_opt_desc_t notmuch_shared_options[] = {
        { 0, 0, 0, 0, 0 }
 };
 
+char *notmuch_requested_db_uuid = NULL;
+
 void
 notmuch_process_shared_options (unused (const char *dummy))
 {