diff options
-rw-r--r-- | res/res_sorcery_astdb.c | 71 |
1 files changed, 68 insertions, 3 deletions
diff --git a/res/res_sorcery_astdb.c b/res/res_sorcery_astdb.c index ae1b680ee..10d881e8f 100644 --- a/res/res_sorcery_astdb.c +++ b/res/res_sorcery_astdb.c @@ -215,10 +215,67 @@ static void sorcery_astdb_retrieve_multiple(const struct ast_sorcery *sorcery, v sorcery_astdb_retrieve_fields_common(sorcery, data, type, fields, objects); } +/*! + * \internal + * \brief Convert regex prefix pattern to astDB prefix pattern if possible. + * + * \param tree astDB prefix pattern buffer to fill. + * \param regex Extended regular expression with the start anchor character '^'. + * + * \note Since this is a helper function, the tree buffer is + * assumed to always be large enough. + * + * \retval 0 on success. + * \retval -1 on error. regex is invalid. + */ +static int make_astdb_prefix_pattern(char *tree, const char *regex) +{ + const char *src; + char *dst; + + for (dst = tree, src = regex + 1; *src; ++src) { + if (*src == '\\') { + /* Escaped regex char. */ + ++src; + if (!*src) { + /* Invalid regex. The caller escaped the string terminator. */ + return -1; + } + } else if (*src == '$') { + if (!src[1]) { + /* Remove the tail anchor character. */ + *dst = '\0'; + return 0; + } + } else if (strchr(".?*+{[(|", *src)) { + /* + * The regex is not a simple prefix pattern. + * + * XXX With more logic, it is possible to simply + * use the current prefix pattern. The last character + * needs to be removed if possible when the current regex + * token is "?*{". Also the rest of the regex pattern + * would need to be checked for subgroup/alternation. + * Subgroup/alternation is too complex for a simple prefix + * match. + */ + dst = tree; + break; + } + *dst++ = *src; + } + if (dst != tree) { + *dst++ = '%'; + } + *dst = '\0'; + return 0; +} + static void sorcery_astdb_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex) { const char *prefix = data; - char family[strlen(prefix) + strlen(type) + 2], tree[strlen(regex) + 1]; + char family[strlen(prefix) + strlen(type) + 2]; + char tree[strlen(regex) + 1]; RAII_VAR(struct ast_db_entry *, entries, NULL, ast_db_freetree); regex_t expression; struct ast_db_entry *entry; @@ -226,12 +283,20 @@ static void sorcery_astdb_retrieve_regex(const struct ast_sorcery *sorcery, void snprintf(family, sizeof(family), "%s/%s", prefix, type); if (regex[0] == '^') { - snprintf(tree, sizeof(tree), "%s%%", regex + 1); + /* + * For performance reasons, try to create an astDB prefix + * pattern from the regex to reduce the number of entries + * retrieved from astDB for regex to then match. + */ + if (make_astdb_prefix_pattern(tree, regex)) { + return; + } } else { tree[0] = '\0'; } - if (!(entries = ast_db_gettree(family, tree)) || regcomp(&expression, regex, REG_EXTENDED | REG_NOSUB)) { + if (!(entries = ast_db_gettree(family, tree)) + || regcomp(&expression, regex, REG_EXTENDED | REG_NOSUB)) { return; } |