From: Nick Porter Date: Fri, 9 May 2025 12:39:33 +0000 (+0100) Subject: Add query_number_attribute option to rlm_sql X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=9eb8eafa35b1ef979e2cf45cd76d007089a8a651;p=thirdparty%2Ffreeradius-server.git Add query_number_attribute option to rlm_sql An optional attribute which can be used to record which out of a series of queries updated records. --- diff --git a/raddb/mods-available/sql b/raddb/mods-available/sql index 9216fe818d8..609a9226827 100644 --- a/raddb/mods-available/sql +++ b/raddb/mods-available/sql @@ -349,6 +349,23 @@ sql { # # cache_groups = no + # + # query_number_attribute:: An attribute to write the successful query number to. + # + # When calling the `sql` module in a section such as `accounting` where there are a list of queries to + # try, and success is determined by a query modifying one or more records, this option can be used to + # record which query succeeded. + # + # The attribute named here will be populated in the `control` list with the number of the query + # which succeeded. + # + # If, as is the case with the default Interim-Update queries, the first query is an `UPDATE` and the + # second is an `INSERT`, this can be used to determine if calling the module resulted in a new record + # being written or an existing one being updated. I.e. if the `UPDATE` succeeds, the attribute will be + # populated with 1, whereas if the `INSERT` succeeds, the attribute will be populated with 2. + # +# query_number_attribute = 'Query-Number' + # # .Read database-specific queries. # diff --git a/src/modules/rlm_sql/rlm_sql.c b/src/modules/rlm_sql/rlm_sql.c index 12e82042a25..a236eadfd98 100644 --- a/src/modules/rlm_sql/rlm_sql.c +++ b/src/modules/rlm_sql/rlm_sql.c @@ -52,6 +52,7 @@ static int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM * typedef struct { fr_dict_attr_t const *group_da; + fr_dict_attr_t const *query_number_da; } rlm_sql_boot_t; static const conf_parser_t module_config[] = { @@ -67,6 +68,7 @@ static const conf_parser_t module_config[] = { { FR_CONF_OFFSET("cache_groups", rlm_sql_config_t, cache_groups) }, { FR_CONF_OFFSET("read_profiles", rlm_sql_config_t, read_profiles), .dflt = "yes" }, { FR_CONF_OFFSET("open_query", rlm_sql_config_t, connect_query) }, + { FR_CONF_OFFSET("query_number_attribute", rlm_sql_config_t, query_number_attribute) }, { FR_CONF_OFFSET("safe_characters", rlm_sql_config_t, allowed_chars), .dflt = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /" }, @@ -1812,7 +1814,15 @@ static unlang_action_t mod_sql_redundant_query_resume(rlm_rcode_t *p_result, UNU TALLOC_FREE(query_ctx); RDEBUG2("%i record(s) updated", numaffected); - if (numaffected > 0) RETURN_MODULE_OK; /* A query succeeded, were done! */ + if (numaffected > 0) { + if (inst->query_number_da) { + fr_pair_t *vp; + pair_update_control(&vp, inst->query_number_da); + vp->vp_uint32 = redundant_ctx->query_no + 1; + RDEBUG2("control.%pP", vp); + } + RETURN_MODULE_OK; /* A query succeeded, were done! */ + } next: /* * Look to see if there are any more queries to expand @@ -2094,6 +2104,7 @@ static int mod_instantiate(module_inst_ctx_t const *mctx) * point in making rlm_sql_boot_t available everywhere. */ inst->group_da = boot->group_da; + inst->query_number_da = boot->query_number_da; inst->name = mctx->mi->name; /* Need this for functions in sql.c */ inst->mi = mctx->mi; /* For looking up thread instance data */ @@ -2245,6 +2256,35 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx) xlat_func_args_set(xlat, sql_xlat_arg); } + /* + * If we need to record which query from a redundant set succeeds, find / create the attribute to use. + */ + if (inst->config.query_number_attribute) { + boot->query_number_da = fr_dict_attr_by_name(NULL, fr_dict_root(dict_freeradius), + inst->config.query_number_attribute); + if (!boot->query_number_da) { + if (fr_dict_attr_add_name_only(fr_dict_unconst(dict_freeradius), fr_dict_root(dict_freeradius), + inst->config.query_number_attribute, FR_TYPE_UINT32, NULL) < 0) { + ERROR("Failed defining query number attribute \"%s\"", inst->config.query_number_attribute); + return -1; + } + + boot->query_number_da = fr_dict_attr_search_by_qualified_oid(NULL, dict_freeradius, + inst->config.query_number_attribute, + false, false); + if (!boot->query_number_da) { + ERROR("Failed resolving query number attribute \"%s\"", inst->config.query_number_attribute); + return -1; + } + } else { + if (boot->query_number_da->type != FR_TYPE_UINT32) { + ERROR("Query number attribute \"%s\" is type \"%s\", needs to be uint32", + inst->config.query_number_attribute, fr_type_to_str(boot->query_number_da->type)); + return -1; + } + } + } + /* * Register the SQL xlat function */ diff --git a/src/modules/rlm_sql/rlm_sql.h b/src/modules/rlm_sql/rlm_sql.h index d3db49d9a66..4c1c68398e1 100644 --- a/src/modules/rlm_sql/rlm_sql.h +++ b/src/modules/rlm_sql/rlm_sql.h @@ -98,6 +98,10 @@ typedef struct { char const *connect_query; //!< Query executed after establishing //!< new connection. + char const *query_number_attribute; //!< Name of the attribute to populate with the + ///< query number which succeeded when running a + ///< series of redundant queries. + trunk_conf_t trunk_conf; //!< Configuration for trunk connections. } rlm_sql_config_t; @@ -240,6 +244,7 @@ struct sql_inst { char const *name; //!< Module instance name. fr_dict_attr_t const *group_da; //!< Group dictionary attribute. + fr_dict_attr_t const *query_number_da; //!< Query number attribute. module_instance_t const *mi; //!< Module instance data for thread lookups. };