<p>Sean Bright has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/7162">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">sorcery: Add ast_sorcery_retrieve_by_prefix()<br><br>Some consumers of the sorcery API use ast_sorcery_retrieve_by_regex<br>only so that they can anchor the potential match as a prefix and not<br>because they truly need regular expressions.<br><br>Rather than using regular expressions for simple prefix lookups, add<br>a new operation - ast_sorcery_retrieve_by_prefix - that does them.<br><br>Change-Id: I56f4e20ba1154bd52281f995c27a429a854f6a79<br>---<br>M include/asterisk/sorcery.h<br>M main/sorcery.c<br>M res/res_sorcery_astdb.c<br>M res/res_sorcery_config.c<br>M res/res_sorcery_memory.c<br>M res/res_sorcery_memory_cache.c<br>M res/res_sorcery_realtime.c<br>7 files changed, 214 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/62/7162/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/include/asterisk/sorcery.h b/include/asterisk/sorcery.h<br>index 0cb4347..97597eb 100644<br>--- a/include/asterisk/sorcery.h<br>+++ b/include/asterisk/sorcery.h<br>@@ -312,6 +312,14 @@<br> <br> /*! \brief Callback for closing a wizard */<br> void (*close)(void *data);<br>+<br>+ /*! \brief Optional callback for retrieving multiple objects by matching their id with a prefix */<br>+ void (*retrieve_prefix)(const struct ast_sorcery *sorcery,<br>+ void *data,<br>+ const char *type,<br>+ struct ao2_container *objects,<br>+ const char *prefix,<br>+ const size_t prefix_len);<br> };<br> <br> /*! \brief Interface for a sorcery object type observer */<br>@@ -1218,6 +1226,22 @@<br> struct ao2_container *ast_sorcery_retrieve_by_regex(const struct ast_sorcery *sorcery, const char *type, const char *regex);<br> <br> /*!<br>+ * \brief Retrieve multiple objects whose id begins with the specified prefix<br>+ * \since 13.19.0<br>+ *<br>+ * \param sorcery Pointer to a sorcery structure<br>+ * \param type Type of object to retrieve<br>+ * \param prefix Object id prefix<br>+ * \param prefix_len The length of prefix in bytes<br>+ *<br>+ * \retval non-NULL if error occurs<br>+ * \retval NULL success<br>+ *<br>+ * \note The prefix is matched in a case sensitive manner.<br>+ */<br>+struct ao2_container *ast_sorcery_retrieve_by_prefix(const struct ast_sorcery *sorcery, const char *type, const char *prefix, const size_t prefix_len);<br>+<br>+/*!<br> * \brief Update an object<br> *<br> * \param sorcery Pointer to a sorcery structure<br>diff --git a/main/sorcery.c b/main/sorcery.c<br>index 1bdf2c2..c715f60 100644<br>--- a/main/sorcery.c<br>+++ b/main/sorcery.c<br>@@ -1982,6 +1982,36 @@<br> return objects;<br> }<br> <br>+struct ao2_container *ast_sorcery_retrieve_by_prefix(const struct ast_sorcery *sorcery, const char *type, const char *prefix, const size_t prefix_len)<br>+{<br>+ RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);<br>+ struct ao2_container *objects;<br>+ int i;<br>+<br>+ if (!object_type || !(objects = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) {<br>+ return NULL;<br>+ }<br>+<br>+ AST_VECTOR_RW_RDLOCK(&object_type->wizards);<br>+ for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {<br>+ struct ast_sorcery_object_wizard *wizard =<br>+ AST_VECTOR_GET(&object_type->wizards, i);<br>+<br>+ if (!wizard->wizard->callbacks.retrieve_prefix) {<br>+ continue;<br>+ }<br>+<br>+ wizard->wizard->callbacks.retrieve_prefix(sorcery, wizard->data, object_type->name, objects, prefix, prefix_len);<br>+<br>+ if (wizard->caching && ao2_container_count(objects)) {<br>+ break;<br>+ }<br>+ }<br>+ AST_VECTOR_RW_UNLOCK(&object_type->wizards);<br>+<br>+ return objects;<br>+}<br>+<br> /*! \brief Internal function which returns if the wizard has created the object */<br> static int sorcery_wizard_create(const struct ast_sorcery_object_wizard *object_wizard, const struct sorcery_details *details)<br> {<br>diff --git a/res/res_sorcery_astdb.c b/res/res_sorcery_astdb.c<br>index 1aec0be..998f1cc 100644<br>--- a/res/res_sorcery_astdb.c<br>+++ b/res/res_sorcery_astdb.c<br>@@ -46,6 +46,7 @@<br> static void sorcery_astdb_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects,<br> const struct ast_variable *fields);<br> static void sorcery_astdb_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex);<br>+static void sorcery_astdb_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len);<br> static int sorcery_astdb_update(const struct ast_sorcery *sorcery, void *data, void *object);<br> static int sorcery_astdb_delete(const struct ast_sorcery *sorcery, void *data, void *object);<br> static void sorcery_astdb_close(void *data);<br>@@ -58,6 +59,7 @@<br> .retrieve_fields = sorcery_astdb_retrieve_fields,<br> .retrieve_multiple = sorcery_astdb_retrieve_multiple,<br> .retrieve_regex = sorcery_astdb_retrieve_regex,<br>+ .retrieve_prefix = sorcery_astdb_retrieve_prefix,<br> .update = sorcery_astdb_update,<br> .delete = sorcery_astdb_delete,<br> .close = sorcery_astdb_close,<br>@@ -329,6 +331,42 @@<br> regfree(&expression);<br> }<br> <br>+static void sorcery_astdb_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len)<br>+{<br>+ const char *family_prefix = data;<br>+ size_t family_len = strlen(family_prefix) + strlen(type) + 1; /* +1 for slash delimiter */<br>+ char family[family_len + 1];<br>+ char tree[prefix_len + sizeof("%")];<br>+ RAII_VAR(struct ast_db_entry *, entries, NULL, ast_db_freetree);<br>+ struct ast_db_entry *entry;<br>+<br>+ snprintf(tree, sizeof(tree), "%s%%", prefix);<br>+ snprintf(family, sizeof(family), "%s/%s", family_prefix, type);<br>+<br>+ if (!(entries = ast_db_gettree(family, tree))) {<br>+ return;<br>+ }<br>+<br>+ for (entry = entries; entry; entry = entry->next) {<br>+ /* The key in the entry includes the family, so we need to strip it out */<br>+ const char *key = entry->key + family_len + 2;<br>+ RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);<br>+ struct ast_json_error error;<br>+ RAII_VAR(void *, object, NULL, ao2_cleanup);<br>+ RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);<br>+<br>+ if (!(json = ast_json_load_string(entry->data, &error))<br>+ || (ast_json_to_ast_variables(json, &objset) != AST_JSON_TO_AST_VARS_CODE_SUCCESS)<br>+ || !(objset = sorcery_astdb_filter_objectset(objset, sorcery, type))<br>+ || !(object = ast_sorcery_alloc(sorcery, type, key))<br>+ || ast_sorcery_objectset_apply(sorcery, object, objset)) {<br>+ return;<br>+ }<br>+<br>+ ao2_link(objects, object);<br>+ }<br>+}<br>+<br> static int sorcery_astdb_update(const struct ast_sorcery *sorcery, void *data, void *object)<br> {<br> const char *prefix = data;<br>diff --git a/res/res_sorcery_config.c b/res/res_sorcery_config.c<br>index 7fd7e76..b02000e 100644<br>--- a/res/res_sorcery_config.c<br>+++ b/res/res_sorcery_config.c<br>@@ -73,6 +73,12 @@<br> /*! \brief Regular expression for checking object id */<br> regex_t *regex;<br> <br>+ /*! \brief Prefix for matching object id */<br>+ const char *prefix;<br>+<br>+ /*! \brief Prefix length in bytes for matching object id */<br>+ const size_t prefix_len;<br>+<br> /*! \brief Optional container to put object into */<br> struct ao2_container *container;<br> };<br>@@ -85,6 +91,7 @@<br> static void sorcery_config_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects,<br> const struct ast_variable *fields);<br> static void sorcery_config_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex);<br>+static void sorcery_config_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len);<br> static void sorcery_config_close(void *data);<br> <br> static struct ast_sorcery_wizard config_object_wizard = {<br>@@ -96,6 +103,7 @@<br> .retrieve_fields = sorcery_config_retrieve_fields,<br> .retrieve_multiple = sorcery_config_retrieve_multiple,<br> .retrieve_regex = sorcery_config_retrieve_regex,<br>+ .retrieve_prefix = sorcery_config_retrieve_prefix,<br> .close = sorcery_config_close,<br> };<br> <br>@@ -117,6 +125,11 @@<br> if (params->regex) {<br> /* If a regular expression has been provided see if it matches, otherwise move on */<br> if (!regexec(params->regex, ast_sorcery_object_get_id(obj), 0, NULL, 0)) {<br>+ ao2_link(params->container, obj);<br>+ }<br>+ return 0;<br>+ } else if (params->prefix) {<br>+ if (!strncmp(params->prefix, ast_sorcery_object_get_id(obj), params->prefix_len)) {<br> ao2_link(params->container, obj);<br> }<br> return 0;<br>@@ -208,6 +221,24 @@<br> regfree(&expression);<br> }<br> <br>+static void sorcery_config_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len)<br>+{<br>+ struct sorcery_config *config = data;<br>+ RAII_VAR(struct ao2_container *, config_objects, ao2_global_obj_ref(config->objects), ao2_cleanup);<br>+ struct sorcery_config_fields_cmp_params params = {<br>+ .sorcery = sorcery,<br>+ .container = objects,<br>+ .prefix = prefix,<br>+ .prefix_len = prefix_len,<br>+ };<br>+<br>+ if (!config_objects) {<br>+ return;<br>+ }<br>+<br>+ ao2_callback(config_objects, OBJ_NODATA | OBJ_MULTIPLE, sorcery_config_fields_cmp, ¶ms);<br>+}<br>+<br> /*! \brief Internal function which determines if criteria has been met for considering an object set applicable */<br> static int sorcery_is_criteria_met(struct ast_variable *objset, struct ast_variable *criteria)<br> {<br>diff --git a/res/res_sorcery_memory.c b/res/res_sorcery_memory.c<br>index b2f0559..a05f05d 100644<br>--- a/res/res_sorcery_memory.c<br>+++ b/res/res_sorcery_memory.c<br>@@ -48,6 +48,7 @@<br> static void sorcery_memory_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects,<br> const struct ast_variable *fields);<br> static void sorcery_memory_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex);<br>+static void sorcery_memory_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len);<br> static int sorcery_memory_update(const struct ast_sorcery *sorcery, void *data, void *object);<br> static int sorcery_memory_delete(const struct ast_sorcery *sorcery, void *data, void *object);<br> static void sorcery_memory_close(void *data);<br>@@ -60,6 +61,7 @@<br> .retrieve_fields = sorcery_memory_retrieve_fields,<br> .retrieve_multiple = sorcery_memory_retrieve_multiple,<br> .retrieve_regex = sorcery_memory_retrieve_regex,<br>+ .retrieve_prefix = sorcery_memory_retrieve_prefix,<br> .update = sorcery_memory_update,<br> .delete = sorcery_memory_delete,<br> .close = sorcery_memory_close,<br>@@ -75,6 +77,12 @@<br> <br> /*! \brief Regular expression for checking object id */<br> regex_t *regex;<br>+<br>+ /*! \brief Prefix for matching object id */<br>+ const char *prefix;<br>+<br>+ /*! \brief Prefix length in bytes for matching object id */<br>+ const size_t prefix_len;<br> <br> /*! \brief Optional container to put object into */<br> struct ao2_container *container;<br>@@ -124,6 +132,11 @@<br> if (params->regex) {<br> /* If a regular expression has been provided see if it matches, otherwise move on */<br> if (!regexec(params->regex, ast_sorcery_object_get_id(obj), 0, NULL, 0)) {<br>+ ao2_link(params->container, obj);<br>+ }<br>+ return 0;<br>+ } else if (params->prefix) {<br>+ if (!strncmp(params->prefix, ast_sorcery_object_get_id(obj), params->prefix_len)) {<br> ao2_link(params->container, obj);<br> }<br> return 0;<br>@@ -200,6 +213,18 @@<br> regfree(&expression);<br> }<br> <br>+static void sorcery_memory_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len)<br>+{<br>+ struct sorcery_memory_fields_cmp_params params = {<br>+ .sorcery = sorcery,<br>+ .container = objects,<br>+ .prefix = prefix,<br>+ .prefix_len = prefix_len,<br>+ };<br>+<br>+ ao2_callback(data, 0, sorcery_memory_fields_cmp, ¶ms);<br>+}<br>+<br> static int sorcery_memory_update(const struct ast_sorcery *sorcery, void *data, void *object)<br> {<br> RAII_VAR(void *, existing, NULL, ao2_cleanup);<br>diff --git a/res/res_sorcery_memory_cache.c b/res/res_sorcery_memory_cache.c<br>index bf2347c..30e6ef0 100644<br>--- a/res/res_sorcery_memory_cache.c<br>+++ b/res/res_sorcery_memory_cache.c<br>@@ -185,6 +185,10 @@<br> const struct ast_variable *fields;<br> /*! \brief Regular expression for checking object id */<br> regex_t *regex;<br>+ /*! \brief Prefix for matching object id */<br>+ const char *prefix;<br>+ /*! \brief Prefix length in bytes for matching object id */<br>+ const size_t prefix_len;<br> /*! \brief Optional container to put object into */<br> struct ao2_container *container;<br> };<br>@@ -201,6 +205,8 @@<br> struct ao2_container *objects, const struct ast_variable *fields);<br> static void sorcery_memory_cache_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type,<br> struct ao2_container *objects, const char *regex);<br>+static void sorcery_memory_cache_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type,<br>+ struct ao2_container *objects, const char *prefix, const size_t prefix_len);<br> static int sorcery_memory_cache_delete(const struct ast_sorcery *sorcery, void *data, void *object);<br> static void sorcery_memory_cache_close(void *data);<br> <br>@@ -216,6 +222,7 @@<br> .retrieve_fields = sorcery_memory_cache_retrieve_fields,<br> .retrieve_multiple = sorcery_memory_cache_retrieve_multiple,<br> .retrieve_regex = sorcery_memory_cache_retrieve_regex,<br>+ .retrieve_prefix = sorcery_memory_cache_retrieve_prefix,<br> .close = sorcery_memory_cache_close,<br> };<br> <br>@@ -1253,6 +1260,11 @@<br> ao2_link(params->container, cached->object);<br> }<br> return 0;<br>+ } else if (params->prefix) {<br>+ if (!strncmp(params->prefix, ast_sorcery_object_get_id(cached->object), params->prefix_len)) {<br>+ ao2_link(params->container, cached->object);<br>+ }<br>+ return 0;<br> } else if (params->fields &&<br> (!ast_variable_lists_match(cached->objectset, params->fields, 0))) {<br> /* If we can't turn the object into an object set OR if differences exist between the fields<br>@@ -1378,6 +1390,40 @@<br> <br> /*!<br> * \internal<br>+ * \brief Callback function to retrieve multiple objects whose id matches a prefix<br>+ *<br>+ * \param sorcery The sorcery instance<br>+ * \param data The sorcery memory cache<br>+ * \param type The type of the object to retrieve<br>+ * \param objects Container to place the objects into<br>+ * \param prefix Prefix to match against the object id<br>+ */<br>+static void sorcery_memory_cache_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type,<br>+ struct ao2_container *objects, const char *prefix, const size_t prefix_len)<br>+{<br>+ struct sorcery_memory_cache *cache = data;<br>+ struct sorcery_memory_cache_fields_cmp_params params = {<br>+ .sorcery = sorcery,<br>+ .cache = cache,<br>+ .container = objects,<br>+ .prefix = prefix,<br>+ .prefix_len = prefix_len,<br>+ };<br>+<br>+ if (is_passthru_update() || !cache->full_backend_cache) {<br>+ return;<br>+ }<br>+<br>+ memory_cache_full_update(sorcery, type, cache);<br>+ ao2_callback(cache->objects, 0, sorcery_memory_cache_fields_cmp, ¶ms);<br>+<br>+ if (ao2_container_count(objects)) {<br>+ memory_cache_stale_check(sorcery, cache);<br>+ }<br>+}<br>+<br>+/*!<br>+ * \internal<br> * \brief Callback function to finish configuring the memory cache<br> *<br> * \param data The sorcery memory cache<br>diff --git a/res/res_sorcery_realtime.c b/res/res_sorcery_realtime.c<br>index 138d6ea..b211abc 100644<br>--- a/res/res_sorcery_realtime.c<br>+++ b/res/res_sorcery_realtime.c<br>@@ -59,6 +59,8 @@<br> static void sorcery_realtime_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects,<br> const struct ast_variable *fields);<br> static void sorcery_realtime_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex);<br>+static void sorcery_realtime_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type,<br>+ struct ao2_container *objects, const char *prefix, const size_t prefix_len);<br> static int sorcery_realtime_update(const struct ast_sorcery *sorcery, void *data, void *object);<br> static int sorcery_realtime_delete(const struct ast_sorcery *sorcery, void *data, void *object);<br> static void sorcery_realtime_close(void *data);<br>@@ -71,6 +73,7 @@<br> .retrieve_fields = sorcery_realtime_retrieve_fields,<br> .retrieve_multiple = sorcery_realtime_retrieve_multiple,<br> .retrieve_regex = sorcery_realtime_retrieve_regex,<br>+ .retrieve_prefix = sorcery_realtime_retrieve_prefix,<br> .update = sorcery_realtime_update,<br> .delete = sorcery_realtime_delete,<br> .close = sorcery_realtime_close,<br>@@ -262,6 +265,23 @@<br> sorcery_realtime_retrieve_multiple(sorcery, data, type, objects, fields);<br> }<br> <br>+static void sorcery_realtime_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type,<br>+ struct ao2_container *objects, const char *prefix, const size_t prefix_len)<br>+{<br>+ char field[strlen(UUID_FIELD) + 6], value[prefix_len + 2];<br>+ RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);<br>+<br>+ if (!ast_strlen_zero(prefix)) {<br>+ snprintf(field, sizeof(field), "%s LIKE", UUID_FIELD);<br>+ snprintf(value, sizeof(value), "%s%%", prefix);<br>+ if (!(fields = ast_variable_new(field, value, ""))) {<br>+ return;<br>+ }<br>+ }<br>+<br>+ sorcery_realtime_retrieve_multiple(sorcery, data, type, objects, fields);<br>+}<br>+<br> static int sorcery_realtime_update(const struct ast_sorcery *sorcery, void *data, void *object)<br> {<br> struct sorcery_config *config = data;<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/7162">change 7162</a>. To unsubscribe, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/7162"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 13 </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: I56f4e20ba1154bd52281f995c27a429a854f6a79 </div>
<div style="display:none"> Gerrit-Change-Number: 7162 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Sean Bright <sean.bright@gmail.com> </div>