<p>Sean Bright has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/7164">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/64/7164/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 bfb2c39..bafca5f 100644<br>--- a/include/asterisk/sorcery.h<br>+++ b/include/asterisk/sorcery.h<br>@@ -298,6 +298,14 @@<br>        /*! \brief Callback for retrieving multiple objects using a regex on their id */<br>      void (*retrieve_regex)(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex);<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>  /*! \brief Optional callback for retrieving an object using fields */<br>         void *(*retrieve_fields)(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields);<br> <br>@@ -1241,6 +1249,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 01b7791..51b55c5 100644<br>--- a/main/sorcery.c<br>+++ b/main/sorcery.c<br>@@ -2036,6 +2036,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 8d16335..872c5de 100644<br>--- a/res/res_sorcery_astdb.c<br>+++ b/res/res_sorcery_astdb.c<br>@@ -44,6 +44,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>@@ -56,6 +57,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>@@ -327,6 +329,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 0de34c6..2017888 100644<br>--- a/res/res_sorcery_config.c<br>+++ b/res/res_sorcery_config.c<br>@@ -71,6 +71,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>@@ -83,6 +89,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>@@ -94,6 +101,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>@@ -115,6 +123,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>@@ -206,6 +219,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, &params);<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 57d5eac..6c91dad 100644<br>--- a/res/res_sorcery_memory.c<br>+++ b/res/res_sorcery_memory.c<br>@@ -46,6 +46,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>@@ -58,6 +59,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>@@ -73,6 +75,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>@@ -122,6 +130,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>@@ -198,6 +211,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, &params);<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, &params);<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 3f11404..00594a5 100644<br>--- a/res/res_sorcery_realtime.c<br>+++ b/res/res_sorcery_realtime.c<br>@@ -57,6 +57,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>@@ -69,6 +71,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>@@ -260,6 +263,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/7164">change 7164</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/7164"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </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: 7164 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Sean Bright <sean.bright@gmail.com> </div>