]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
res_config_odbc: Prevent Realtime fallback on record-not-found (SQL_NO_DATA)
authorAlexei Gradinari <alex2grad@gmail.com>
Tue, 15 Jul 2025 21:56:39 +0000 (17:56 -0400)
committergithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Wed, 30 Jul 2025 15:38:31 +0000 (15:38 +0000)
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
include/asterisk/config.h
main/config.c
res/res_config_odbc.c

index 954508b7183ecb831700920ad103f9ab17fe179b..fff6890bcf42325c31500c9713f61faa37423017 100644 (file)
@@ -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;
index 10798bad141f3f9d878190a8e445050b15794c97..388f4c5438e4f78cbdc116073af4dc7386fb8b7d 100644 (file)
@@ -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;
index cecbd1792f80d818a02a46ac6bb6af26ddda392b..7007baf3354bb12e8c05ae7a80a83f42a76154f4 100644 (file)
@@ -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));