#
# 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.
#
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[] = {
{ 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.-_: /" },
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
* 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 */
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
*/