summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--res/res_sorcery_astdb.c71
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;
}