From: Alexei Gradinari Date: Tue, 15 Jul 2025 21:56:39 +0000 (-0400) Subject: res_config_odbc: Prevent Realtime fallback on record-not-found (SQL_NO_DATA) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3e178dcfd6073e0e0e5c64da8f3b55f3067f9214;p=thirdparty%2Fasterisk.git res_config_odbc: Prevent Realtime fallback on record-not-found (SQL_NO_DATA) This patch fixes an issue in the ODBC Realtime engine where Asterisk incorrectly falls back to the next configured backend when the current one returns SQL_NO_DATA (i.e., no record found). This is a logical error and performance risk in multi-backend configurations. Solution: Introduced CONFIG_RT_NOT_FOUND ((void *)-1) as a special return marker. ODBC Realtime backend now return CONFIG_RT_NOT_FOUND when no data is found. Core engine stops iterating on this marker, avoiding unnecessary fallback. Notes: Other Realtime backends (PostgreSQL, LDAP, etc.) can be updated similarly. This patch only covers ODBC. Fixes: #1305 --- diff --git a/include/asterisk/config.h b/include/asterisk/config.h index 954508b718..fff6890bcf 100644 --- a/include/asterisk/config.h +++ b/include/asterisk/config.h @@ -126,6 +126,12 @@ typedef int realtime_require(const char *database, const char *table, va_list ap */ typedef int realtime_unload(const char *database, const char *table); +/*! Special return value indicating a successful query that returned no data. + * Used by realtime backends to signal "not found" vs an actual backend failure. + * This allows the core engine to differentiate and avoid unnecessary failover. + */ +#define CONFIG_RT_NOT_FOUND (void *)-1 + /*! \brief Configuration engine structure, used to define realtime drivers */ struct ast_config_engine { char *name; diff --git a/main/config.c b/main/config.c index 10798bad14..388f4c5438 100644 --- a/main/config.c +++ b/main/config.c @@ -3660,8 +3660,20 @@ struct ast_variable *ast_load_realtime_all_fields(const char *family, const stru for (i = 1; ; i++) { if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) { - if (eng->realtime_func && (res = eng->realtime_func(db, table, fields))) { - return res; + if (eng->realtime_func) { + res = eng->realtime_func(db, table, fields); + + /* If a backend returns CONFIG_RT_NOT_FOUND, stop iteration and return NULL, + * indicating that the requested record does not exist and no failover should occur. + * Only continue iteration if the result is NULL and not CONFIG_RT_NOT_FOUND, + * which signals a backend failure. + */ + if (res == CONFIG_RT_NOT_FOUND) { + return NULL; + } + if (res) { + return res; + } } } else { return NULL; diff --git a/res/res_config_odbc.c b/res/res_config_odbc.c index cecbd1792f..7007baf335 100644 --- a/res/res_config_odbc.c +++ b/res/res_config_odbc.c @@ -167,7 +167,8 @@ static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data) * Sub-in the values to the prepared statement and execute it. Return results * as a ast_variable list. * - * \return var on success + * \return var on success (data found) + * \return CONFIG_RT_NOT_FOUND on success but no record * \retval NULL on failure */ static struct ast_variable *realtime_odbc(const char *database, const char *table, const struct ast_variable *fields) @@ -237,9 +238,13 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl res = SQLFetch(stmt); if (res == SQL_NO_DATA) { + /* SQL_NO_DATA indicates that the query was valid but no record was found. + * Instead of returning NULL (which signals a backend error to the core), + * return CONFIG_RT_NOT_FOUND to prevent incorrect failover. + */ SQLFreeHandle (SQL_HANDLE_STMT, stmt); ast_odbc_release_obj(obj); - return NULL; + return CONFIG_RT_NOT_FOUND; } if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { ast_log(LOG_WARNING, "SQL Fetch error! [%s]\n", ast_str_buffer(sql));