]> git.notmuchmail.org Git - notmuch/blob - lib/open.cc
test/T391-python-cffi
[notmuch] / lib / open.cc
1 #include <unistd.h>
2 #include "database-private.h"
3 #include "parse-time-vrp.h"
4
5 #if HAVE_XAPIAN_DB_RETRY_LOCK
6 #define DB_ACTION (Xapian::DB_CREATE_OR_OPEN | Xapian::DB_RETRY_LOCK)
7 #else
8 #define DB_ACTION Xapian::DB_CREATE_OR_OPEN
9 #endif
10
11 notmuch_status_t
12 notmuch_database_open (const char *path,
13                        notmuch_database_mode_t mode,
14                        notmuch_database_t **database)
15 {
16     char *status_string = NULL;
17     notmuch_status_t status;
18
19     status = notmuch_database_open_verbose (path, mode, database,
20                                             &status_string);
21
22     if (status_string) {
23         fputs (status_string, stderr);
24         free (status_string);
25     }
26
27     return status;
28 }
29
30 notmuch_status_t
31 notmuch_database_open_verbose (const char *path,
32                                notmuch_database_mode_t mode,
33                                notmuch_database_t **database,
34                                char **status_string)
35 {
36     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
37     void *local = talloc_new (NULL);
38     notmuch_database_t *notmuch = NULL;
39     char *notmuch_path, *xapian_path, *incompat_features;
40     char *message = NULL;
41     struct stat st;
42     int err;
43     unsigned int version;
44     static int initialized = 0;
45
46     if (path == NULL) {
47         message = strdup ("Error: Cannot open a database for a NULL path.\n");
48         status = NOTMUCH_STATUS_NULL_POINTER;
49         goto DONE;
50     }
51
52     if (path[0] != '/') {
53         message = strdup ("Error: Database path must be absolute.\n");
54         status = NOTMUCH_STATUS_PATH_ERROR;
55         goto DONE;
56     }
57
58     if (! (notmuch_path = talloc_asprintf (local, "%s/%s", path, ".notmuch"))) {
59         message = strdup ("Out of memory\n");
60         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
61         goto DONE;
62     }
63
64     err = stat (notmuch_path, &st);
65     if (err) {
66         IGNORE_RESULT (asprintf (&message, "Error opening database at %s: %s\n",
67                                  notmuch_path, strerror (errno)));
68         status = NOTMUCH_STATUS_FILE_ERROR;
69         goto DONE;
70     }
71
72     if (! (xapian_path = talloc_asprintf (local, "%s/%s", notmuch_path, "xapian"))) {
73         message = strdup ("Out of memory\n");
74         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
75         goto DONE;
76     }
77
78     /* Initialize the GLib type system and threads */
79 #if ! GLIB_CHECK_VERSION (2, 35, 1)
80     g_type_init ();
81 #endif
82
83     /* Initialize gmime */
84     if (! initialized) {
85         g_mime_init ();
86         initialized = 1;
87     }
88
89     notmuch = talloc_zero (NULL, notmuch_database_t);
90     notmuch->exception_reported = false;
91     notmuch->status_string = NULL;
92     notmuch->path = talloc_strdup (notmuch, path);
93
94     strip_trailing (notmuch->path, '/');
95
96     notmuch->writable_xapian_db = NULL;
97     notmuch->atomic_nesting = 0;
98     notmuch->view = 1;
99     try {
100         std::string last_thread_id;
101         std::string last_mod;
102
103         if (mode == NOTMUCH_DATABASE_MODE_READ_WRITE) {
104             notmuch->writable_xapian_db = new Xapian::WritableDatabase (xapian_path,
105                                                                         DB_ACTION);
106             notmuch->xapian_db = notmuch->writable_xapian_db;
107         } else {
108             notmuch->xapian_db = new Xapian::Database (xapian_path);
109         }
110
111         /* Check version.  As of database version 3, we represent
112          * changes in terms of features, so assume a version bump
113          * means a dramatically incompatible change. */
114         version = notmuch_database_get_version (notmuch);
115         if (version > NOTMUCH_DATABASE_VERSION) {
116             IGNORE_RESULT (asprintf (&message,
117                                      "Error: Notmuch database at %s\n"
118                                      "       has a newer database format version (%u) than supported by this\n"
119                                      "       version of notmuch (%u).\n",
120                                      notmuch_path, version, NOTMUCH_DATABASE_VERSION));
121             notmuch_database_destroy (notmuch);
122             notmuch = NULL;
123             status = NOTMUCH_STATUS_FILE_ERROR;
124             goto DONE;
125         }
126
127         /* Check features. */
128         incompat_features = NULL;
129         notmuch->features = _notmuch_database_parse_features (
130             local, notmuch->xapian_db->get_metadata ("features").c_str (),
131             version, mode == NOTMUCH_DATABASE_MODE_READ_WRITE ? 'w' : 'r',
132             &incompat_features);
133         if (incompat_features) {
134             IGNORE_RESULT (asprintf (&message,
135                                      "Error: Notmuch database at %s\n"
136                                      "       requires features (%s)\n"
137                                      "       not supported by this version of notmuch.\n",
138                                      notmuch_path, incompat_features));
139             notmuch_database_destroy (notmuch);
140             notmuch = NULL;
141             status = NOTMUCH_STATUS_FILE_ERROR;
142             goto DONE;
143         }
144
145         notmuch->last_doc_id = notmuch->xapian_db->get_lastdocid ();
146         last_thread_id = notmuch->xapian_db->get_metadata ("last_thread_id");
147         if (last_thread_id.empty ()) {
148             notmuch->last_thread_id = 0;
149         } else {
150             const char *str;
151             char *end;
152
153             str = last_thread_id.c_str ();
154             notmuch->last_thread_id = strtoull (str, &end, 16);
155             if (*end != '\0')
156                 INTERNAL_ERROR ("Malformed database last_thread_id: %s", str);
157         }
158
159         /* Get current highest revision number. */
160         last_mod = notmuch->xapian_db->get_value_upper_bound (
161             NOTMUCH_VALUE_LAST_MOD);
162         if (last_mod.empty ())
163             notmuch->revision = 0;
164         else
165             notmuch->revision = Xapian::sortable_unserialise (last_mod);
166         notmuch->uuid = talloc_strdup (
167             notmuch, notmuch->xapian_db->get_uuid ().c_str ());
168
169         notmuch->query_parser = new Xapian::QueryParser;
170         notmuch->term_gen = new Xapian::TermGenerator;
171         notmuch->term_gen->set_stemmer (Xapian::Stem ("english"));
172         notmuch->value_range_processor = new Xapian::NumberRangeProcessor (NOTMUCH_VALUE_TIMESTAMP);
173         notmuch->date_range_processor = new ParseTimeRangeProcessor (NOTMUCH_VALUE_TIMESTAMP, "date:");
174         notmuch->last_mod_range_processor = new Xapian::NumberRangeProcessor (NOTMUCH_VALUE_LAST_MOD, "lastmod:");
175         notmuch->query_parser->set_default_op (Xapian::Query::OP_AND);
176         notmuch->query_parser->set_database (*notmuch->xapian_db);
177         notmuch->query_parser->set_stemmer (Xapian::Stem ("english"));
178         notmuch->query_parser->set_stemming_strategy (Xapian::QueryParser::STEM_SOME);
179         notmuch->query_parser->add_rangeprocessor (notmuch->value_range_processor);
180         notmuch->query_parser->add_rangeprocessor (notmuch->date_range_processor);
181         notmuch->query_parser->add_rangeprocessor (notmuch->last_mod_range_processor);
182
183         status = _notmuch_database_setup_standard_query_fields (notmuch);
184         if (status)
185             goto DONE;
186
187         status = _notmuch_database_setup_user_query_fields (notmuch);
188         if (status)
189             goto DONE;
190
191     } catch (const Xapian::Error &error) {
192         IGNORE_RESULT (asprintf (&message, "A Xapian exception occurred opening database: %s\n",
193                                  error.get_msg ().c_str ()));
194         notmuch_database_destroy (notmuch);
195         notmuch = NULL;
196         status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
197     }
198
199   DONE:
200     talloc_free (local);
201
202     if (message) {
203         if (status_string)
204             *status_string = message;
205         else
206             free (message);
207     }
208
209     if (database)
210         *database = notmuch;
211     else
212         talloc_free (notmuch);
213
214     if (notmuch)
215         notmuch->open = true;
216
217     return status;
218 }