X-Git-Url: https://git.notmuchmail.org/git?a=blobdiff_plain;f=lib%2Fdatabase.cc;h=7d09119cecddf54a94e34b8f2a9948ee203fbb85;hb=6ca6c089e9df7affe6bee0392197509a24ab2546;hp=f7f9943f8a5ac6e03e90c40640b9198d95a2b8cf;hpb=9257622da8ed50502e59adec69e5daa043ef242a;p=notmuch diff --git a/lib/database.cc b/lib/database.cc index f7f9943f..7d09119c 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -63,6 +63,11 @@ typedef struct { * * tag: Any tags associated with this message by the user. * + * direntry: A colon-separated pair of values (INTEGER:STRING), + * where INTEGER is the document ID of a directory + * document, and STRING is the name of a file within + * that directory for this mail message. + * * A mail document also has two values: * * TIMESTAMP: The time_t value corresponding to the message's @@ -75,8 +80,7 @@ typedef struct { * user in searching. But the database doesn't really care itself * about any of these. * - * Finally, the data portion of a mail document contains the path name - * of the mail message (relative to the database path). + * The data portion of a mail document is empty. * * Directory document * ------------------ @@ -84,13 +88,22 @@ typedef struct { * maintain data necessary to allow for efficient polling of mail * directories. * - * The directory document is indexed with a single prefixed term: + * The directory document contains the following terms: + * + * directory: The directory path (relative to the database path) + * Or the SHA1 sum of the directory path (if the + * path itself is too long to fit in a Xapian + * term). * - * directory: The directory path (an absolute path) + * parent: The document ID of the parent directory document. + * Top-level directories will have a parent value of 0. * * and has a single value: * * TIMESTAMP: The mtime of the directory (at last scan) + * + * The data portion of a directory document contains the path of the + * directory (relative to the datbase path). */ /* With these prefix values we follow the conventions published here: @@ -112,8 +125,9 @@ prefix_t BOOLEAN_PREFIX_INTERNAL[] = { { "type", "T" }, { "reference", "XREFERENCE" }, { "replyto", "XREPLYTO" }, - /* XXX: Need a flag day to rename XTIMESTAMP. */ - { "directory", "XTIMESTAMP" }, + { "directory", "XDIRECTORY" }, + { "direntry", "XDIRENTRY" }, + { "parent", "XPARENT" }, }; prefix_t BOOLEAN_PREFIX_EXTERNAL[] = { @@ -588,6 +602,138 @@ directory_db_path (const char *path) return path; } +/* Given a path, split it into two parts: the directory part is all + * components except for the last, and the basename is that last + * component. Getting the return-value for either part is optional + * (the caller can pass NULL). + * + * The original 'path' can represent either a regular file or a + * directory---the splitting will be carried out in the same way in + * either case. Trailing slashes on 'path' will be ignored, and any + * cases of multiple '/' characters appearing in series will be + * treated as a single '/'. + * + * Allocation (if any) will have 'ctx' as the talloc owner. But + * pointers will be returned within the original path string whenever + * possible. + * + * Note: If 'path' is non-empty and contains no non-trailing slash, + * (that is, consists of a filename with no parent directory), then + * the directory returned will be an empty string. However, if 'path' + * is an empty string, then both directory and basename will be + * returned as NULL. + */ +notmuch_status_t +_notmuch_database_split_path (void *ctx, + const char *path, + const char **directory, + const char **basename) +{ + const char *slash; + + if (path == NULL || *path == '\0') { + if (directory) + *directory = NULL; + if (basename) + *basename = NULL; + return NOTMUCH_STATUS_SUCCESS; + } + + /* Find the last slash (not counting a trailing slash), if any. */ + + slash = path + strlen (path) - 1; + + /* First, skip trailing slashes. */ + while (slash != path) { + if (*slash != '/') + break; + + --slash; + } + + /* Then, find a slash. */ + while (slash != path) { + if (*slash == '/') + break; + + if (basename) + *basename = slash; + + --slash; + } + + /* Finally, skip multiple slashes. */ + while (slash != path) { + if (*slash != '/') + break; + + --slash; + } + + if (slash == path) { + if (directory) + *directory = talloc_strdup (ctx, ""); + if (basename) + *basename = path; + } else { + if (directory) + *directory = talloc_strndup (ctx, path, slash - path + 1); + } + + return NOTMUCH_STATUS_SUCCESS; +} + +notmuch_status_t +_notmuch_database_find_directory_id (notmuch_database_t *notmuch, + const char *path, + unsigned int *directory_id) +{ + notmuch_private_status_t private_status; + notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; + const char *db_path; + + if (path == NULL) { + *directory_id = 0; + return NOTMUCH_STATUS_SUCCESS; + } + + db_path = directory_db_path (path); + + private_status = find_unique_doc_id (notmuch, "directory", + db_path, directory_id); + if (private_status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND) { + status = notmuch_database_set_directory_mtime (notmuch, + path, 0); + if (status) + goto DONE; + + private_status = find_unique_doc_id (notmuch, "directory", + db_path, directory_id); + status = COERCE_STATUS (private_status, "_find_directory_id"); + } + + DONE: + if (db_path != path) + free ((char *) db_path); + + if (status) + *directory_id = -1; + + return status; +} + +const char * +_notmuch_database_get_directory_path (void *ctx, + notmuch_database_t *notmuch, + unsigned int doc_id) +{ + Xapian::Document document; + + document = find_document_for_doc_id (notmuch, doc_id); + + return talloc_strdup (ctx, document.get_data ().c_str ()); +} + /* Given a legal 'path' for the database, return the relative path. * * The return value will be a pointer to the originl path contents, @@ -632,13 +778,17 @@ notmuch_database_set_directory_mtime (notmuch_database_t *notmuch, unsigned int doc_id; notmuch_private_status_t status; notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS; - const char *db_path = NULL; + const char *parent, *db_path = NULL; + unsigned int parent_id; + void *local = talloc_new (notmuch); if (notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY) { fprintf (stderr, "Attempted to update a read-only database.\n"); return NOTMUCH_STATUS_READONLY_DATABASE; } + path = _notmuch_database_relative_path (notmuch, path); + db = static_cast (notmuch->xapian_db); db_path = directory_db_path (path); @@ -649,10 +799,20 @@ notmuch_database_set_directory_mtime (notmuch_database_t *notmuch, Xapian::sortable_serialise (mtime)); if (status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND) { - char *term = talloc_asprintf (NULL, "%s%s", + char *term = talloc_asprintf (local, "%s%s", _find_prefix ("directory"), db_path); doc.add_term (term); - talloc_free (term); + + doc.set_data (path); + + _notmuch_database_split_path (local, path, &parent, NULL); + + _notmuch_database_find_directory_id (notmuch, parent, &parent_id); + + term = talloc_asprintf (local, "%s%u", + _find_prefix ("parent"), + parent_id); + doc.add_term (term); db->add_document (doc); } else { @@ -669,6 +829,8 @@ notmuch_database_set_directory_mtime (notmuch_database_t *notmuch, if (db_path != path) free ((char *) db_path); + talloc_free (local); + return ret; } @@ -681,6 +843,7 @@ notmuch_database_get_directory_mtime (notmuch_database_t *notmuch, notmuch_private_status_t status; const char *db_path = NULL; time_t ret = 0; + void *local = talloc_new (notmuch); db_path = directory_db_path (path);