From 3b4097824159c6fdebb8a88b5447beb9a6226ea3 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sat, 8 Aug 2020 11:16:44 -0300 Subject: [PATCH] lib: factor out prefix related code to its own file Reduce the size of database.cc, and limit the scope of prefix_table, make sure it's accessed via a well-defined internal API. --- lib/Makefile.local | 3 +- lib/database-private.h | 7 ++ lib/database.cc | 201 ++------------------------------------- lib/prefix.cc | 210 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 227 insertions(+), 194 deletions(-) create mode 100644 lib/prefix.cc diff --git a/lib/Makefile.local b/lib/Makefile.local index 04418fa8..9721d01e 100644 --- a/lib/Makefile.local +++ b/lib/Makefile.local @@ -60,7 +60,8 @@ libnotmuch_cxx_srcs = \ $(dir)/regexp-fields.cc \ $(dir)/thread.cc \ $(dir)/thread-fp.cc \ - $(dir)/features.cc + $(dir)/features.cc \ + $(dir)/prefix.cc libnotmuch_modules := $(libnotmuch_c_srcs:.c=.o) $(libnotmuch_cxx_srcs:.cc=.o) diff --git a/lib/database-private.h b/lib/database-private.h index 2d220811..c9bc712b 100644 --- a/lib/database-private.h +++ b/lib/database-private.h @@ -277,4 +277,11 @@ _notmuch_database_parse_features (const void *ctx, const char *features, unsigne char * _notmuch_database_print_features (const void *ctx, unsigned int features); +/* prefix.cc */ +notmuch_status_t +_notmuch_database_setup_standard_query_fields (notmuch_database_t *notmuch); + +notmuch_status_t +_notmuch_database_setup_user_query_fields (notmuch_database_t *notmuch); + #endif diff --git a/lib/database.cc b/lib/database.cc index e08d43ca..defa3062 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -263,80 +263,6 @@ _notmuch_database_mode (notmuch_database_t *notmuch) * same thread. */ -/* With these prefix values we follow the conventions published here: - * - * https://xapian.org/docs/omega/termprefixes.html - * - * as much as makes sense. Note that I took some liberty in matching - * the reserved prefix values to notmuch concepts, (for example, 'G' - * is documented as "newsGroup (or similar entity - e.g. a web forum - * name)", for which I think the thread is the closest analogue in - * notmuch. This in spite of the fact that we will eventually be - * storing mailing-list messages where 'G' for "mailing list name" - * might be even a closer analogue. I'm treating the single-character - * prefixes preferentially for core notmuch concepts (which will be - * nearly universal to all mail messages). - */ - -static const -prefix_t prefix_table[] = { - /* name term prefix flags */ - { "type", "T", NOTMUCH_FIELD_NO_FLAGS }, - { "reference", "XREFERENCE", NOTMUCH_FIELD_NO_FLAGS }, - { "replyto", "XREPLYTO", NOTMUCH_FIELD_NO_FLAGS }, - { "directory", "XDIRECTORY", NOTMUCH_FIELD_NO_FLAGS }, - { "file-direntry", "XFDIRENTRY", NOTMUCH_FIELD_NO_FLAGS }, - { "directory-direntry", "XDDIRENTRY", NOTMUCH_FIELD_NO_FLAGS }, - { "body", "", NOTMUCH_FIELD_EXTERNAL | - NOTMUCH_FIELD_PROBABILISTIC }, - { "thread", "G", NOTMUCH_FIELD_EXTERNAL | - NOTMUCH_FIELD_PROCESSOR }, - { "tag", "K", NOTMUCH_FIELD_EXTERNAL | - NOTMUCH_FIELD_PROCESSOR }, - { "is", "K", NOTMUCH_FIELD_EXTERNAL | - NOTMUCH_FIELD_PROCESSOR }, - { "id", "Q", NOTMUCH_FIELD_EXTERNAL }, - { "mid", "Q", NOTMUCH_FIELD_EXTERNAL | - NOTMUCH_FIELD_PROCESSOR }, - { "path", "P", NOTMUCH_FIELD_EXTERNAL | - NOTMUCH_FIELD_PROCESSOR }, - { "property", "XPROPERTY", NOTMUCH_FIELD_EXTERNAL }, - /* - * Unconditionally add ':' to reduce potential ambiguity with - * overlapping prefixes and/or terms that start with capital - * letters. See Xapian document termprefixes.html for related - * discussion. - */ - { "folder", "XFOLDER:", NOTMUCH_FIELD_EXTERNAL | - NOTMUCH_FIELD_PROCESSOR }, - { "date", NULL, NOTMUCH_FIELD_EXTERNAL | - NOTMUCH_FIELD_PROCESSOR }, - { "query", NULL, NOTMUCH_FIELD_EXTERNAL | - NOTMUCH_FIELD_PROCESSOR }, - { "from", "XFROM", NOTMUCH_FIELD_EXTERNAL | - NOTMUCH_FIELD_PROBABILISTIC | - NOTMUCH_FIELD_PROCESSOR }, - { "to", "XTO", NOTMUCH_FIELD_EXTERNAL | - NOTMUCH_FIELD_PROBABILISTIC }, - { "attachment", "XATTACHMENT", NOTMUCH_FIELD_EXTERNAL | - NOTMUCH_FIELD_PROBABILISTIC }, - { "mimetype", "XMIMETYPE", NOTMUCH_FIELD_EXTERNAL | - NOTMUCH_FIELD_PROBABILISTIC }, - { "subject", "XSUBJECT", NOTMUCH_FIELD_EXTERNAL | - NOTMUCH_FIELD_PROBABILISTIC | - NOTMUCH_FIELD_PROCESSOR }, -}; - -static void -_setup_query_field_default (const prefix_t *prefix, notmuch_database_t *notmuch) -{ - if (prefix->prefix) - notmuch->query_parser->add_prefix ("", prefix->prefix); - if (prefix->flags & NOTMUCH_FIELD_PROBABILISTIC) - notmuch->query_parser->add_prefix (prefix->name, prefix->prefix); - else - notmuch->query_parser->add_boolean_prefix (prefix->name, prefix->prefix); -} notmuch_string_map_iterator_t * _notmuch_database_user_headers (notmuch_database_t *notmuch) @@ -344,118 +270,6 @@ _notmuch_database_user_headers (notmuch_database_t *notmuch) return _notmuch_string_map_iterator_create (notmuch->user_header, "", false); } -const char * -_user_prefix (void *ctx, const char *name) -{ - return talloc_asprintf (ctx, "XU%s:", name); -} - -static notmuch_status_t -_setup_user_query_fields (notmuch_database_t *notmuch) -{ - notmuch_config_list_t *list; - notmuch_status_t status; - - notmuch->user_prefix = _notmuch_string_map_create (notmuch); - if (notmuch->user_prefix == NULL) - return NOTMUCH_STATUS_OUT_OF_MEMORY; - - notmuch->user_header = _notmuch_string_map_create (notmuch); - if (notmuch->user_header == NULL) - return NOTMUCH_STATUS_OUT_OF_MEMORY; - - status = notmuch_database_get_config_list (notmuch, CONFIG_HEADER_PREFIX, &list); - if (status) - return status; - - for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) { - - prefix_t query_field; - - const char *key = notmuch_config_list_key (list) - + sizeof (CONFIG_HEADER_PREFIX) - 1; - - _notmuch_string_map_append (notmuch->user_prefix, - key, - _user_prefix (notmuch, key)); - - _notmuch_string_map_append (notmuch->user_header, - key, - notmuch_config_list_value (list)); - - query_field.name = talloc_strdup (notmuch, key); - query_field.prefix = _user_prefix (notmuch, key); - query_field.flags = NOTMUCH_FIELD_PROBABILISTIC - | NOTMUCH_FIELD_EXTERNAL; - - _setup_query_field_default (&query_field, notmuch); - } - - notmuch_config_list_destroy (list); - - return NOTMUCH_STATUS_SUCCESS; -} - -static void -_setup_query_field (const prefix_t *prefix, notmuch_database_t *notmuch) -{ - if (prefix->flags & NOTMUCH_FIELD_PROCESSOR) { - Xapian::FieldProcessor *fp; - - if (STRNCMP_LITERAL (prefix->name, "date") == 0) - fp = (new DateFieldProcessor(NOTMUCH_VALUE_TIMESTAMP))->release (); - else if (STRNCMP_LITERAL(prefix->name, "query") == 0) - fp = (new QueryFieldProcessor (*notmuch->query_parser, notmuch))->release (); - else if (STRNCMP_LITERAL (prefix->name, "thread") == 0) - fp = (new ThreadFieldProcessor (*notmuch->query_parser, notmuch))->release (); - else - fp = (new RegexpFieldProcessor (prefix->name, prefix->flags, - *notmuch->query_parser, notmuch))->release (); - - /* we treat all field-processor fields as boolean in order to get the raw input */ - if (prefix->prefix) - notmuch->query_parser->add_prefix ("", prefix->prefix); - notmuch->query_parser->add_boolean_prefix (prefix->name, fp); - } else { - _setup_query_field_default (prefix, notmuch); - } -} - -const char * -_find_prefix (const char *name) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE (prefix_table); i++) { - if (strcmp (name, prefix_table[i].name) == 0) - return prefix_table[i].prefix; - } - - INTERNAL_ERROR ("No prefix exists for '%s'\n", name); - - return ""; -} - -/* Like find prefix, but include the possibility of user defined - * prefixes specific to this database */ - -const char * -_notmuch_database_prefix (notmuch_database_t *notmuch, const char *name) -{ - unsigned int i; - - /*XXX TODO: reduce code duplication */ - for (i = 0; i < ARRAY_SIZE (prefix_table); i++) { - if (strcmp (name, prefix_table[i].name) == 0) - return prefix_table[i].prefix; - } - - if (notmuch->user_prefix) - return _notmuch_string_map_get (notmuch->user_prefix, name); - - return NULL; -} - const char * notmuch_status_to_string (notmuch_status_t status) { @@ -952,13 +766,14 @@ notmuch_database_open_verbose (const char *path, notmuch->query_parser->add_rangeprocessor (notmuch->date_range_processor); notmuch->query_parser->add_rangeprocessor (notmuch->last_mod_range_processor); - for (i = 0; i < ARRAY_SIZE (prefix_table); i++) { - const prefix_t *prefix = &prefix_table[i]; - if (prefix->flags & NOTMUCH_FIELD_EXTERNAL) { - _setup_query_field (prefix, notmuch); - } - } - status = _setup_user_query_fields (notmuch); + status = _notmuch_database_setup_standard_query_fields (notmuch); + if (status) + goto DONE; + + status = _notmuch_database_setup_user_query_fields (notmuch); + if (status) + goto DONE; + } catch (const Xapian::Error &error) { IGNORE_RESULT (asprintf (&message, "A Xapian exception occurred opening database: %s\n", error.get_msg ().c_str ())); diff --git a/lib/prefix.cc b/lib/prefix.cc new file mode 100644 index 00000000..dd7b193d --- /dev/null +++ b/lib/prefix.cc @@ -0,0 +1,210 @@ +#include "database-private.h" +#include "query-fp.h" +#include "thread-fp.h" +#include "regexp-fields.h" +#include "parse-time-vrp.h" + +typedef struct { + const char *name; + const char *prefix; + notmuch_field_flag_t flags; +} prefix_t; + +/* With these prefix values we follow the conventions published here: + * + * https://xapian.org/docs/omega/termprefixes.html + * + * as much as makes sense. Note that I took some liberty in matching + * the reserved prefix values to notmuch concepts, (for example, 'G' + * is documented as "newsGroup (or similar entity - e.g. a web forum + * name)", for which I think the thread is the closest analogue in + * notmuch. This in spite of the fact that we will eventually be + * storing mailing-list messages where 'G' for "mailing list name" + * might be even a closer analogue. I'm treating the single-character + * prefixes preferentially for core notmuch concepts (which will be + * nearly universal to all mail messages). + */ + +static const +prefix_t prefix_table[] = { + /* name term prefix flags */ + { "type", "T", NOTMUCH_FIELD_NO_FLAGS }, + { "reference", "XREFERENCE", NOTMUCH_FIELD_NO_FLAGS }, + { "replyto", "XREPLYTO", NOTMUCH_FIELD_NO_FLAGS }, + { "directory", "XDIRECTORY", NOTMUCH_FIELD_NO_FLAGS }, + { "file-direntry", "XFDIRENTRY", NOTMUCH_FIELD_NO_FLAGS }, + { "directory-direntry", "XDDIRENTRY", NOTMUCH_FIELD_NO_FLAGS }, + { "body", "", NOTMUCH_FIELD_EXTERNAL | + NOTMUCH_FIELD_PROBABILISTIC }, + { "thread", "G", NOTMUCH_FIELD_EXTERNAL | + NOTMUCH_FIELD_PROCESSOR }, + { "tag", "K", NOTMUCH_FIELD_EXTERNAL | + NOTMUCH_FIELD_PROCESSOR }, + { "is", "K", NOTMUCH_FIELD_EXTERNAL | + NOTMUCH_FIELD_PROCESSOR }, + { "id", "Q", NOTMUCH_FIELD_EXTERNAL }, + { "mid", "Q", NOTMUCH_FIELD_EXTERNAL | + NOTMUCH_FIELD_PROCESSOR }, + { "path", "P", NOTMUCH_FIELD_EXTERNAL | + NOTMUCH_FIELD_PROCESSOR }, + { "property", "XPROPERTY", NOTMUCH_FIELD_EXTERNAL }, + /* + * Unconditionally add ':' to reduce potential ambiguity with + * overlapping prefixes and/or terms that start with capital + * letters. See Xapian document termprefixes.html for related + * discussion. + */ + { "folder", "XFOLDER:", NOTMUCH_FIELD_EXTERNAL | + NOTMUCH_FIELD_PROCESSOR }, + { "date", NULL, NOTMUCH_FIELD_EXTERNAL | + NOTMUCH_FIELD_PROCESSOR }, + { "query", NULL, NOTMUCH_FIELD_EXTERNAL | + NOTMUCH_FIELD_PROCESSOR }, + { "from", "XFROM", NOTMUCH_FIELD_EXTERNAL | + NOTMUCH_FIELD_PROBABILISTIC | + NOTMUCH_FIELD_PROCESSOR }, + { "to", "XTO", NOTMUCH_FIELD_EXTERNAL | + NOTMUCH_FIELD_PROBABILISTIC }, + { "attachment", "XATTACHMENT", NOTMUCH_FIELD_EXTERNAL | + NOTMUCH_FIELD_PROBABILISTIC }, + { "mimetype", "XMIMETYPE", NOTMUCH_FIELD_EXTERNAL | + NOTMUCH_FIELD_PROBABILISTIC }, + { "subject", "XSUBJECT", NOTMUCH_FIELD_EXTERNAL | + NOTMUCH_FIELD_PROBABILISTIC | + NOTMUCH_FIELD_PROCESSOR }, +}; + +static const char * +_user_prefix (void *ctx, const char *name) +{ + return talloc_asprintf (ctx, "XU%s:", name); +} + +const char * +_find_prefix (const char *name) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE (prefix_table); i++) { + if (strcmp (name, prefix_table[i].name) == 0) + return prefix_table[i].prefix; + } + + INTERNAL_ERROR ("No prefix exists for '%s'\n", name); + + return ""; +} + +/* Like find prefix, but include the possibility of user defined + * prefixes specific to this database */ + +const char * +_notmuch_database_prefix (notmuch_database_t *notmuch, const char *name) +{ + unsigned int i; + + /*XXX TODO: reduce code duplication */ + for (i = 0; i < ARRAY_SIZE (prefix_table); i++) { + if (strcmp (name, prefix_table[i].name) == 0) + return prefix_table[i].prefix; + } + + if (notmuch->user_prefix) + return _notmuch_string_map_get (notmuch->user_prefix, name); + + return NULL; +} + +static void +_setup_query_field_default (const prefix_t *prefix, notmuch_database_t *notmuch) +{ + if (prefix->prefix) + notmuch->query_parser->add_prefix ("", prefix->prefix); + if (prefix->flags & NOTMUCH_FIELD_PROBABILISTIC) + notmuch->query_parser->add_prefix (prefix->name, prefix->prefix); + else + notmuch->query_parser->add_boolean_prefix (prefix->name, prefix->prefix); +} + +static void +_setup_query_field (const prefix_t *prefix, notmuch_database_t *notmuch) +{ + if (prefix->flags & NOTMUCH_FIELD_PROCESSOR) { + Xapian::FieldProcessor *fp; + + if (STRNCMP_LITERAL (prefix->name, "date") == 0) + fp = (new DateFieldProcessor(NOTMUCH_VALUE_TIMESTAMP))->release (); + else if (STRNCMP_LITERAL(prefix->name, "query") == 0) + fp = (new QueryFieldProcessor (*notmuch->query_parser, notmuch))->release (); + else if (STRNCMP_LITERAL (prefix->name, "thread") == 0) + fp = (new ThreadFieldProcessor (*notmuch->query_parser, notmuch))->release (); + else + fp = (new RegexpFieldProcessor (prefix->name, prefix->flags, + *notmuch->query_parser, notmuch))->release (); + + /* we treat all field-processor fields as boolean in order to get the raw input */ + if (prefix->prefix) + notmuch->query_parser->add_prefix ("", prefix->prefix); + notmuch->query_parser->add_boolean_prefix (prefix->name, fp); + } else { + _setup_query_field_default (prefix, notmuch); + } +} + +notmuch_status_t +_notmuch_database_setup_standard_query_fields (notmuch_database_t *notmuch) +{ + for (unsigned int i = 0; i < ARRAY_SIZE (prefix_table); i++) { + const prefix_t *prefix = &prefix_table[i]; + if (prefix->flags & NOTMUCH_FIELD_EXTERNAL) { + _setup_query_field (prefix, notmuch); + } + } + return NOTMUCH_STATUS_SUCCESS; +} + +notmuch_status_t +_notmuch_database_setup_user_query_fields (notmuch_database_t *notmuch) +{ + notmuch_config_list_t *list; + notmuch_status_t status; + + notmuch->user_prefix = _notmuch_string_map_create (notmuch); + if (notmuch->user_prefix == NULL) + return NOTMUCH_STATUS_OUT_OF_MEMORY; + + notmuch->user_header = _notmuch_string_map_create (notmuch); + if (notmuch->user_header == NULL) + return NOTMUCH_STATUS_OUT_OF_MEMORY; + + status = notmuch_database_get_config_list (notmuch, CONFIG_HEADER_PREFIX, &list); + if (status) + return status; + + for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) { + + prefix_t query_field; + + const char *key = notmuch_config_list_key (list) + + sizeof (CONFIG_HEADER_PREFIX) - 1; + + _notmuch_string_map_append (notmuch->user_prefix, + key, + _user_prefix (notmuch, key)); + + _notmuch_string_map_append (notmuch->user_header, + key, + notmuch_config_list_value (list)); + + query_field.name = talloc_strdup (notmuch, key); + query_field.prefix = _user_prefix (notmuch, key); + query_field.flags = NOTMUCH_FIELD_PROBABILISTIC + | NOTMUCH_FIELD_EXTERNAL; + + _setup_query_field_default (&query_field, notmuch); + } + + notmuch_config_list_destroy (list); + + return NOTMUCH_STATUS_SUCCESS; +} -- 2.43.0