sorcery: Add ast_sorcery_retrieve_by_prefix()
authorSean Bright <sean.bright@gmail.com>
Thu, 9 Nov 2017 14:21:38 +0000 (09:21 -0500)
committerSean Bright <sean.bright@gmail.com>
Mon, 13 Nov 2017 20:15:33 +0000 (15:15 -0500)
Some consumers of the sorcery API use ast_sorcery_retrieve_by_regex
only so that they can anchor the potential match as a prefix and not
because they truly need regular expressions.

Rather than using regular expressions for simple prefix lookups, add
a new operation - ast_sorcery_retrieve_by_prefix - that does them.

Change-Id: I56f4e20ba1154bd52281f995c27a429a854f6a79

include/asterisk/sorcery.h
main/sorcery.c
res/res_sorcery_astdb.c
res/res_sorcery_config.c
res/res_sorcery_memory.c
res/res_sorcery_memory_cache.c
res/res_sorcery_realtime.c

index bfb2c39..bafca5f 100644 (file)
@@ -298,6 +298,14 @@ struct ast_sorcery_wizard {
        /*! \brief Callback for retrieving multiple objects using a regex on their id */
        void (*retrieve_regex)(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex);
 
+       /*! \brief Optional callback for retrieving multiple objects by matching their id with a prefix */
+       void (*retrieve_prefix)(const struct ast_sorcery *sorcery,
+                       void *data,
+                       const char *type,
+                       struct ao2_container *objects,
+                       const char *prefix,
+                       const size_t prefix_len);
+
        /*! \brief Optional callback for retrieving an object using fields */
        void *(*retrieve_fields)(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields);
 
@@ -1241,6 +1249,22 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch
 struct ao2_container *ast_sorcery_retrieve_by_regex(const struct ast_sorcery *sorcery, const char *type, const char *regex);
 
 /*!
+ * \brief Retrieve multiple objects whose id begins with the specified prefix
+ * \since 13.19.0
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object to retrieve
+ * \param prefix Object id prefix
+ * \param prefix_len The length of prefix in bytes
+ *
+ * \retval non-NULL if error occurs
+ * \retval NULL success
+ *
+ * \note The prefix is matched in a case sensitive manner.
+ */
+struct ao2_container *ast_sorcery_retrieve_by_prefix(const struct ast_sorcery *sorcery, const char *type, const char *prefix, const size_t prefix_len);
+
+/*!
  * \brief Update an object
  *
  * \param sorcery Pointer to a sorcery structure
index 01b7791..51b55c5 100644 (file)
@@ -2036,6 +2036,36 @@ struct ao2_container *ast_sorcery_retrieve_by_regex(const struct ast_sorcery *so
        return objects;
 }
 
+struct ao2_container *ast_sorcery_retrieve_by_prefix(const struct ast_sorcery *sorcery, const char *type, const char *prefix, const size_t prefix_len)
+{
+       RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+       struct ao2_container *objects;
+       int i;
+
+       if (!object_type || !(objects = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) {
+               return NULL;
+       }
+
+       AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+       for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+               struct ast_sorcery_object_wizard *wizard =
+                       AST_VECTOR_GET(&object_type->wizards, i);
+
+               if (!wizard->wizard->callbacks.retrieve_prefix) {
+                       continue;
+               }
+
+               wizard->wizard->callbacks.retrieve_prefix(sorcery, wizard->data, object_type->name, objects, prefix, prefix_len);
+
+               if (wizard->caching && ao2_container_count(objects)) {
+                       break;
+               }
+       }
+       AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+
+       return objects;
+}
+
 /*! \brief Internal function which returns if the wizard has created the object */
 static int sorcery_wizard_create(const struct ast_sorcery_object_wizard *object_wizard, const struct sorcery_details *details)
 {
index 8d16335..8b93b57 100644 (file)
@@ -44,6 +44,7 @@ static void *sorcery_astdb_retrieve_fields(const struct ast_sorcery *sorcery, vo
 static void sorcery_astdb_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects,
                                             const struct ast_variable *fields);
 static void sorcery_astdb_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex);
+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);
 static int sorcery_astdb_update(const struct ast_sorcery *sorcery, void *data, void *object);
 static int sorcery_astdb_delete(const struct ast_sorcery *sorcery, void *data, void *object);
 static void sorcery_astdb_close(void *data);
@@ -56,6 +57,7 @@ static struct ast_sorcery_wizard astdb_object_wizard = {
        .retrieve_fields = sorcery_astdb_retrieve_fields,
        .retrieve_multiple = sorcery_astdb_retrieve_multiple,
        .retrieve_regex = sorcery_astdb_retrieve_regex,
+       .retrieve_prefix = sorcery_astdb_retrieve_prefix,
        .update = sorcery_astdb_update,
        .delete = sorcery_astdb_delete,
        .close = sorcery_astdb_close,
@@ -327,6 +329,42 @@ static void sorcery_astdb_retrieve_regex(const struct ast_sorcery *sorcery, void
        regfree(&expression);
 }
 
+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)
+{
+       const char *family_prefix = data;
+       size_t family_len = strlen(family_prefix) + strlen(type) + 1; /* +1 for slash delimiter */
+       char family[family_len + 1];
+       char tree[prefix_len + sizeof("%")];
+       RAII_VAR(struct ast_db_entry *, entries, NULL, ast_db_freetree);
+       struct ast_db_entry *entry;
+
+       snprintf(tree, sizeof(tree), "%.*s%%", (int) prefix_len, prefix);
+       snprintf(family, sizeof(family), "%s/%s", family_prefix, type);
+
+       if (!(entries = ast_db_gettree(family, tree))) {
+               return;
+       }
+
+       for (entry = entries; entry; entry = entry->next) {
+               /* The key in the entry includes the family, so we need to strip it out */
+               const char *key = entry->key + family_len + 2;
+               RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
+               struct ast_json_error error;
+               RAII_VAR(void *, object, NULL, ao2_cleanup);
+               RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
+
+               if (!(json = ast_json_load_string(entry->data, &error))
+                  || (ast_json_to_ast_variables(json, &objset) != AST_JSON_TO_AST_VARS_CODE_SUCCESS)
+                  || !(objset = sorcery_astdb_filter_objectset(objset, sorcery, type))
+                  || !(object = ast_sorcery_alloc(sorcery, type, key))
+                  || ast_sorcery_objectset_apply(sorcery, object, objset)) {
+                       return;
+               }
+
+               ao2_link(objects, object);
+       }
+}
+
 static int sorcery_astdb_update(const struct ast_sorcery *sorcery, void *data, void *object)
 {
        const char *prefix = data;
index 0de34c6..2017888 100644 (file)
@@ -71,6 +71,12 @@ struct sorcery_config_fields_cmp_params {
        /*! \brief Regular expression for checking object id */
        regex_t *regex;
 
+       /*! \brief Prefix for matching object id */
+       const char *prefix;
+
+       /*! \brief Prefix length in bytes for matching object id */
+       const size_t prefix_len;
+
        /*! \brief Optional container to put object into */
        struct ao2_container *container;
 };
@@ -83,6 +89,7 @@ static void *sorcery_config_retrieve_fields(const struct ast_sorcery *sorcery, v
 static void sorcery_config_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects,
                                             const struct ast_variable *fields);
 static void sorcery_config_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex);
+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);
 static void sorcery_config_close(void *data);
 
 static struct ast_sorcery_wizard config_object_wizard = {
@@ -94,6 +101,7 @@ static struct ast_sorcery_wizard config_object_wizard = {
        .retrieve_fields = sorcery_config_retrieve_fields,
        .retrieve_multiple = sorcery_config_retrieve_multiple,
        .retrieve_regex = sorcery_config_retrieve_regex,
+       .retrieve_prefix = sorcery_config_retrieve_prefix,
        .close = sorcery_config_close,
 };
 
@@ -118,6 +126,11 @@ static int sorcery_config_fields_cmp(void *obj, void *arg, int flags)
                        ao2_link(params->container, obj);
                }
                return 0;
+       } else if (params->prefix) {
+               if (!strncmp(params->prefix, ast_sorcery_object_get_id(obj), params->prefix_len)) {
+                       ao2_link(params->container, obj);
+               }
+               return 0;
        } else if (params->fields &&
            (!(objset = ast_sorcery_objectset_create(params->sorcery, obj)) ||
             (!ast_variable_lists_match(objset, params->fields, 0)))) {
@@ -206,6 +219,24 @@ static void sorcery_config_retrieve_regex(const struct ast_sorcery *sorcery, voi
        regfree(&expression);
 }
 
+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)
+{
+       struct sorcery_config *config = data;
+       RAII_VAR(struct ao2_container *, config_objects, ao2_global_obj_ref(config->objects), ao2_cleanup);
+       struct sorcery_config_fields_cmp_params params = {
+               .sorcery = sorcery,
+               .container = objects,
+               .prefix = prefix,
+               .prefix_len = prefix_len,
+       };
+
+       if (!config_objects) {
+               return;
+       }
+
+       ao2_callback(config_objects, OBJ_NODATA | OBJ_MULTIPLE, sorcery_config_fields_cmp, &params);
+}
+
 /*! \brief Internal function which determines if criteria has been met for considering an object set applicable */
 static int sorcery_is_criteria_met(struct ast_variable *objset, struct ast_variable *criteria)
 {
index 57d5eac..6c91dad 100644 (file)
@@ -46,6 +46,7 @@ static void *sorcery_memory_retrieve_fields(const struct ast_sorcery *sorcery, v
 static void sorcery_memory_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects,
                                             const struct ast_variable *fields);
 static void sorcery_memory_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex);
+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);
 static int sorcery_memory_update(const struct ast_sorcery *sorcery, void *data, void *object);
 static int sorcery_memory_delete(const struct ast_sorcery *sorcery, void *data, void *object);
 static void sorcery_memory_close(void *data);
@@ -58,6 +59,7 @@ static struct ast_sorcery_wizard memory_object_wizard = {
        .retrieve_fields = sorcery_memory_retrieve_fields,
        .retrieve_multiple = sorcery_memory_retrieve_multiple,
        .retrieve_regex = sorcery_memory_retrieve_regex,
+       .retrieve_prefix = sorcery_memory_retrieve_prefix,
        .update = sorcery_memory_update,
        .delete = sorcery_memory_delete,
        .close = sorcery_memory_close,
@@ -74,6 +76,12 @@ struct sorcery_memory_fields_cmp_params {
        /*! \brief Regular expression for checking object id */
        regex_t *regex;
 
+       /*! \brief Prefix for matching object id */
+       const char *prefix;
+
+       /*! \brief Prefix length in bytes for matching object id */
+       const size_t prefix_len;
+
        /*! \brief Optional container to put object into */
        struct ao2_container *container;
 };
@@ -125,6 +133,11 @@ static int sorcery_memory_fields_cmp(void *obj, void *arg, int flags)
                        ao2_link(params->container, obj);
                }
                return 0;
+       } else if (params->prefix) {
+               if (!strncmp(params->prefix, ast_sorcery_object_get_id(obj), params->prefix_len)) {
+                       ao2_link(params->container, obj);
+               }
+               return 0;
        } else if (params->fields &&
            (!(objset = ast_sorcery_objectset_create(params->sorcery, obj)) ||
             (!ast_variable_lists_match(objset, params->fields, 0)))) {
@@ -198,6 +211,18 @@ static void sorcery_memory_retrieve_regex(const struct ast_sorcery *sorcery, voi
        regfree(&expression);
 }
 
+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)
+{
+       struct sorcery_memory_fields_cmp_params params = {
+               .sorcery = sorcery,
+               .container = objects,
+               .prefix = prefix,
+               .prefix_len = prefix_len,
+       };
+
+       ao2_callback(data, 0, sorcery_memory_fields_cmp, &params);
+}
+
 static int sorcery_memory_update(const struct ast_sorcery *sorcery, void *data, void *object)
 {
        RAII_VAR(void *, existing, NULL, ao2_cleanup);
index bf2347c..30e6ef0 100644 (file)
@@ -185,6 +185,10 @@ struct sorcery_memory_cache_fields_cmp_params {
        const struct ast_variable *fields;
        /*! \brief Regular expression for checking object id */
        regex_t *regex;
+       /*! \brief Prefix for matching object id */
+       const char *prefix;
+       /*! \brief Prefix length in bytes for matching object id */
+       const size_t prefix_len;
        /*! \brief Optional container to put object into */
        struct ao2_container *container;
 };
@@ -201,6 +205,8 @@ static void sorcery_memory_cache_retrieve_multiple(const struct ast_sorcery *sor
        struct ao2_container *objects, const struct ast_variable *fields);
 static void sorcery_memory_cache_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type,
        struct ao2_container *objects, const char *regex);
+static void sorcery_memory_cache_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type,
+       struct ao2_container *objects, const char *prefix, const size_t prefix_len);
 static int sorcery_memory_cache_delete(const struct ast_sorcery *sorcery, void *data, void *object);
 static void sorcery_memory_cache_close(void *data);
 
@@ -216,6 +222,7 @@ static struct ast_sorcery_wizard memory_cache_object_wizard = {
        .retrieve_fields = sorcery_memory_cache_retrieve_fields,
        .retrieve_multiple = sorcery_memory_cache_retrieve_multiple,
        .retrieve_regex = sorcery_memory_cache_retrieve_regex,
+       .retrieve_prefix = sorcery_memory_cache_retrieve_prefix,
        .close = sorcery_memory_cache_close,
 };
 
@@ -1253,6 +1260,11 @@ static int sorcery_memory_cache_fields_cmp(void *obj, void *arg, int flags)
                        ao2_link(params->container, cached->object);
                }
                return 0;
+       } else if (params->prefix) {
+               if (!strncmp(params->prefix, ast_sorcery_object_get_id(cached->object), params->prefix_len)) {
+                       ao2_link(params->container, cached->object);
+               }
+               return 0;
        } else if (params->fields &&
             (!ast_variable_lists_match(cached->objectset, params->fields, 0))) {
                /* If we can't turn the object into an object set OR if differences exist between the fields
@@ -1378,6 +1390,40 @@ static void sorcery_memory_cache_retrieve_regex(const struct ast_sorcery *sorcer
 
 /*!
  * \internal
+ * \brief Callback function to retrieve multiple objects whose id matches a prefix
+ *
+ * \param sorcery The sorcery instance
+ * \param data The sorcery memory cache
+ * \param type The type of the object to retrieve
+ * \param objects Container to place the objects into
+ * \param prefix Prefix to match against the object id
+ */
+static void sorcery_memory_cache_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type,
+       struct ao2_container *objects, const char *prefix, const size_t prefix_len)
+{
+       struct sorcery_memory_cache *cache = data;
+       struct sorcery_memory_cache_fields_cmp_params params = {
+               .sorcery = sorcery,
+               .cache = cache,
+               .container = objects,
+               .prefix = prefix,
+               .prefix_len = prefix_len,
+       };
+
+       if (is_passthru_update() || !cache->full_backend_cache) {
+               return;
+       }
+
+       memory_cache_full_update(sorcery, type, cache);
+       ao2_callback(cache->objects, 0, sorcery_memory_cache_fields_cmp, &params);
+
+       if (ao2_container_count(objects)) {
+               memory_cache_stale_check(sorcery, cache);
+       }
+}
+
+/*!
+ * \internal
  * \brief Callback function to finish configuring the memory cache
  *
  * \param data The sorcery memory cache
index 3f11404..a858cbc 100644 (file)
@@ -57,6 +57,8 @@ static void *sorcery_realtime_retrieve_fields(const struct ast_sorcery *sorcery,
 static void sorcery_realtime_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects,
                                             const struct ast_variable *fields);
 static void sorcery_realtime_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex);
+static void sorcery_realtime_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type,
+                                            struct ao2_container *objects, const char *prefix, const size_t prefix_len);
 static int sorcery_realtime_update(const struct ast_sorcery *sorcery, void *data, void *object);
 static int sorcery_realtime_delete(const struct ast_sorcery *sorcery, void *data, void *object);
 static void sorcery_realtime_close(void *data);
@@ -69,6 +71,7 @@ static struct ast_sorcery_wizard realtime_object_wizard = {
        .retrieve_fields = sorcery_realtime_retrieve_fields,
        .retrieve_multiple = sorcery_realtime_retrieve_multiple,
        .retrieve_regex = sorcery_realtime_retrieve_regex,
+       .retrieve_prefix = sorcery_realtime_retrieve_prefix,
        .update = sorcery_realtime_update,
        .delete = sorcery_realtime_delete,
        .close = sorcery_realtime_close,
@@ -260,6 +263,23 @@ static void sorcery_realtime_retrieve_regex(const struct ast_sorcery *sorcery, v
        sorcery_realtime_retrieve_multiple(sorcery, data, type, objects, fields);
 }
 
+static void sorcery_realtime_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type,
+                                            struct ao2_container *objects, const char *prefix, const size_t prefix_len)
+{
+       char field[strlen(UUID_FIELD) + 6], value[prefix_len + 2];
+       RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
+
+       if (prefix_len) {
+               snprintf(field, sizeof(field), "%s LIKE", UUID_FIELD);
+               snprintf(value, sizeof(value), "%.*s%%", (int) prefix_len, prefix);
+               if (!(fields = ast_variable_new(field, value, ""))) {
+                       return;
+               }
+       }
+
+       sorcery_realtime_retrieve_multiple(sorcery, data, type, objects, fields);
+}
+
 static int sorcery_realtime_update(const struct ast_sorcery *sorcery, void *data, void *object)
 {
        struct sorcery_config *config = data;