From cce1c9547facb5cf28dc4e74f9d2369e83f3a212 Mon Sep 17 00:00:00 2001 From: Joshua Colp Date: Sat, 16 Feb 2013 16:24:21 +0000 Subject: Add support for retrieving multiple objects from sorcery using a regex on their id. Review: https://reviewboard.asterisk.org/r/2329/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@381614 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- include/asterisk/sorcery.h | 17 +++++++++++ main/sorcery.c | 24 ++++++++++++++++ res/res_sorcery_config.c | 40 +++++++++++++++++++++++--- res/res_sorcery_memory.c | 38 ++++++++++++++++++++++--- tests/test_sorcery.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 181 insertions(+), 8 deletions(-) diff --git a/include/asterisk/sorcery.h b/include/asterisk/sorcery.h index e5f727a7b..b5344c61c 100644 --- a/include/asterisk/sorcery.h +++ b/include/asterisk/sorcery.h @@ -197,6 +197,9 @@ struct ast_sorcery_wizard { /*! \brief Callback for retrieving an object using an id */ void *(*retrieve_id)(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id); + /*! \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 an object using fields */ void *(*retrieve_fields)(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields); @@ -548,6 +551,20 @@ void *ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char * */ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, struct ast_variable *fields); +/*! + * \brief Retrieve multiple objects using a regular expression on their id + * + * \param sorcery Pointer to a sorcery structure + * \param type Type of object to retrieve + * \param regex Regular expression + * + * \retval non-NULL if error occurs + * \retval NULL success + * + * \note The provided regex is treated as extended case sensitive. + */ +struct ao2_container *ast_sorcery_retrieve_by_regex(const struct ast_sorcery *sorcery, const char *type, const char *regex); + /*! * \brief Update an object * diff --git a/main/sorcery.c b/main/sorcery.c index 6a0fb4acc..242d65bdc 100644 --- a/main/sorcery.c +++ b/main/sorcery.c @@ -959,6 +959,30 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch return object; } +struct ao2_container *ast_sorcery_retrieve_by_regex(const struct ast_sorcery *sorcery, const char *type, const char *regex) +{ + RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup); + struct ao2_container *objects; + struct ao2_iterator i; + struct ast_sorcery_object_wizard *wizard; + + if (!object_type || !(objects = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) { + return NULL; + } + + i = ao2_iterator_init(object_type->wizards, 0); + for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) { + if (!wizard->wizard->retrieve_regex) { + continue; + } + + wizard->wizard->retrieve_regex(sorcery, wizard->data, object_type->name, objects, regex); + } + ao2_iterator_destroy(&i); + + return objects; +} + /*! \brief Internal function which returns if the wizard has created the object */ static int sorcery_wizard_create(void *obj, void *arg, int flags) { diff --git a/res/res_sorcery_config.c b/res/res_sorcery_config.c index 1b43a187e..635eeef92 100644 --- a/res/res_sorcery_config.c +++ b/res/res_sorcery_config.c @@ -32,6 +32,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") +#include + #include "asterisk/module.h" #include "asterisk/sorcery.h" #include "asterisk/astobj2.h" @@ -70,6 +72,9 @@ struct sorcery_config_fields_cmp_params { /*! \brief Pointer to the fields to check */ const struct ast_variable *fields; + /*! \brief Regular expression for checking object id */ + regex_t *regex; + /*! \brief Optional container to put object into */ struct ao2_container *container; }; @@ -81,6 +86,7 @@ static void *sorcery_config_retrieve_id(const struct ast_sorcery *sorcery, void static void *sorcery_config_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields); 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_close(void *data); static struct ast_sorcery_wizard config_object_wizard = { @@ -91,6 +97,7 @@ static struct ast_sorcery_wizard config_object_wizard = { .retrieve_id = sorcery_config_retrieve_id, .retrieve_fields = sorcery_config_retrieve_fields, .retrieve_multiple = sorcery_config_retrieve_multiple, + .retrieve_regex = sorcery_config_retrieve_regex, .close = sorcery_config_close, }; @@ -126,13 +133,19 @@ static int sorcery_config_fields_cmp(void *obj, void *arg, int flags) RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy); RAII_VAR(struct ast_variable *, diff, NULL, ast_variables_destroy); - /* If we can't turn the object into an object set OR if differences exist between the fields - * passed in and what are present on the object they are not a match. - */ - if (params->fields && + if (params->regex) { + /* If a regular expression has been provided see if it matches, otherwise move on */ + if (!regexec(params->regex, ast_sorcery_object_get_id(obj), 0, NULL, 0)) { + ao2_link(params->container, obj); + } + return 0; + } else if (params->fields && (!(objset = ast_sorcery_objectset_create(params->sorcery, obj)) || (ast_sorcery_changeset_create(objset, params->fields, &diff)) || diff)) { + /* If we can't turn the object into an object set OR if differences exist between the fields + * passed in and what are present on the object they are not a match. + */ return 0; } @@ -190,6 +203,25 @@ static void sorcery_config_retrieve_multiple(const struct ast_sorcery *sorcery, ao2_callback(config_objects, 0, sorcery_config_fields_cmp, ¶ms); } +static void sorcery_config_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex) +{ + struct sorcery_config *config = data; + RAII_VAR(struct ao2_container *, config_objects, ao2_global_obj_ref(config->objects), ao2_cleanup); + regex_t expression; + struct sorcery_config_fields_cmp_params params = { + .sorcery = sorcery, + .container = objects, + .regex = &expression, + }; + + if (!config_objects || regcomp(&expression, regex, REG_EXTENDED | REG_NOSUB)) { + return; + } + + ao2_callback(config_objects, 0, sorcery_config_fields_cmp, ¶ms); + regfree(&expression); +} + /*! \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) { diff --git a/res/res_sorcery_memory.c b/res/res_sorcery_memory.c index ccf195651..03b825313 100644 --- a/res/res_sorcery_memory.c +++ b/res/res_sorcery_memory.c @@ -32,6 +32,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") +#include + #include "asterisk/module.h" #include "asterisk/sorcery.h" #include "asterisk/astobj2.h" @@ -45,6 +47,7 @@ static void *sorcery_memory_retrieve_id(const struct ast_sorcery *sorcery, void static void *sorcery_memory_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields); 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 int sorcery_memory_update(void *data, void *object); static int sorcery_memory_delete(void *data, void *object); static void sorcery_memory_close(void *data); @@ -56,6 +59,7 @@ static struct ast_sorcery_wizard memory_object_wizard = { .retrieve_id = sorcery_memory_retrieve_id, .retrieve_fields = sorcery_memory_retrieve_fields, .retrieve_multiple = sorcery_memory_retrieve_multiple, + .retrieve_regex = sorcery_memory_retrieve_regex, .update = sorcery_memory_update, .delete = sorcery_memory_delete, .close = sorcery_memory_close, @@ -69,6 +73,9 @@ struct sorcery_memory_fields_cmp_params { /*! \brief Pointer to the fields to check */ const struct ast_variable *fields; + /*! \brief Regular expression for checking object id */ + regex_t *regex; + /*! \brief Optional container to put object into */ struct ao2_container *container; }; @@ -101,13 +108,19 @@ static int sorcery_memory_fields_cmp(void *obj, void *arg, int flags) RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy); RAII_VAR(struct ast_variable *, diff, NULL, ast_variables_destroy); - /* If we can't turn the object into an object set OR if differences exist between the fields - * passed in and what are present on the object they are not a match. - */ - if (params->fields && + if (params->regex) { + /* If a regular expression has been provided see if it matches, otherwise move on */ + if (!regexec(params->regex, ast_sorcery_object_get_id(obj), 0, NULL, 0)) { + ao2_link(params->container, obj); + } + return 0; + } else if (params->fields && (!(objset = ast_sorcery_objectset_create(params->sorcery, obj)) || (ast_sorcery_changeset_create(objset, params->fields, &diff)) || diff)) { + /* If we can't turn the object into an object set OR if differences exist between the fields + * passed in and what are present on the object they are not a match. + */ return 0; } @@ -154,6 +167,23 @@ static void sorcery_memory_retrieve_multiple(const struct ast_sorcery *sorcery, ao2_callback(data, 0, sorcery_memory_fields_cmp, ¶ms); } +static void sorcery_memory_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex) +{ + regex_t expression; + struct sorcery_memory_fields_cmp_params params = { + .sorcery = sorcery, + .container = objects, + .regex = &expression, + }; + + if (regcomp(&expression, regex, REG_EXTENDED | REG_NOSUB)) { + return; + } + + ao2_callback(data, 0, sorcery_memory_fields_cmp, ¶ms); + regfree(&expression); +} + static int sorcery_memory_update(void *data, void *object) { RAII_VAR(void *, existing, NULL, ao2_cleanup); diff --git a/tests/test_sorcery.c b/tests/test_sorcery.c index 68e4fe8fc..74b15fdcc 100644 --- a/tests/test_sorcery.c +++ b/tests/test_sorcery.c @@ -1419,6 +1419,74 @@ AST_TEST_DEFINE(object_retrieve_multiple_field) return AST_TEST_PASS; } +AST_TEST_DEFINE(object_retrieve_regex) +{ + RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref); + RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup); + RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup); + + switch (cmd) { + case TEST_INIT: + info->name = "object_retrieve_regex"; + info->category = "/main/sorcery/"; + info->summary = "sorcery multiple object retrieval using regex unit test"; + info->description = + "Test multiple object retrieval in sorcery using regular expression for matching"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + if (!(sorcery = alloc_and_initialize_sorcery())) { + ast_test_status_update(test, "Failed to open sorcery structure\n"); + return AST_TEST_FAIL; + } + + if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah-98joe"))) { + ast_test_status_update(test, "Failed to allocate a known object type\n"); + return AST_TEST_FAIL; + } + + if (ast_sorcery_create(sorcery, obj)) { + ast_test_status_update(test, "Failed to create object using in-memory wizard\n"); + return AST_TEST_FAIL; + } + + ao2_cleanup(obj); + + if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah-93joe"))) { + ast_test_status_update(test, "Failed to allocate second instance of a known object type\n"); + return AST_TEST_FAIL; + } + + if (ast_sorcery_create(sorcery, obj)) { + ast_test_status_update(test, "Failed to create second object using in-memory wizard\n"); + return AST_TEST_FAIL; + } + + ao2_cleanup(obj); + + if (!(obj = ast_sorcery_alloc(sorcery, "test", "neener-93joe"))) { + ast_test_status_update(test, "Failed to allocate third instance of a known object type\n"); + return AST_TEST_FAIL; + } + + if (ast_sorcery_create(sorcery, obj)) { + ast_test_status_update(test, "Failed to create third object using in-memory wizard\n"); + return AST_TEST_FAIL; + } + + if (!(objects = ast_sorcery_retrieve_by_regex(sorcery, "test", "^blah-"))) { + ast_test_status_update(test, "Failed to retrieve a container of objects\n"); + return AST_TEST_FAIL; + } else if (ao2_container_count(objects) != 2) { + ast_test_status_update(test, "Received a container with incorrect number of objects in it\n"); + return AST_TEST_FAIL; + } + + return AST_TEST_PASS; +} + AST_TEST_DEFINE(object_update) { RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref); @@ -2099,6 +2167,7 @@ static int unload_module(void) AST_TEST_UNREGISTER(object_retrieve_field); AST_TEST_UNREGISTER(object_retrieve_multiple_all); AST_TEST_UNREGISTER(object_retrieve_multiple_field); + AST_TEST_UNREGISTER(object_retrieve_regex); AST_TEST_UNREGISTER(object_update); AST_TEST_UNREGISTER(object_update_uncreated); AST_TEST_UNREGISTER(object_delete); @@ -2140,6 +2209,7 @@ static int load_module(void) AST_TEST_REGISTER(object_retrieve_field); AST_TEST_REGISTER(object_retrieve_multiple_all); AST_TEST_REGISTER(object_retrieve_multiple_field); + AST_TEST_REGISTER(object_retrieve_regex); AST_TEST_REGISTER(object_update); AST_TEST_REGISTER(object_update_uncreated); AST_TEST_REGISTER(object_delete); -- cgit v1.2.3