Commit b2abdea6646 for php.net

commit b2abdea6646882868122d41b6b62e16386d058d9
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Tue Sep 8 06:37:02 2026 -0400

    odbc: make odbc_sql_error() formattable, drop odbc_colattribute_failed (#23612)

    odbc_colattribute_failed() existed only because odbc_sql_error() could not
    take a column number. Make the reporter variadic so callers can format the
    context into it, and route the two SQLColAttribute sites through it. Those
    failures now also populate the connection's last-error state, so
    odbc_error() and odbc_errormsg() report them.

    Closes GH-23612

diff --git a/NEWS b/NEWS
index 205b2f071ff..f02dbf699c8 100644
--- a/NEWS
+++ b/NEWS
@@ -56,6 +56,8 @@ PHP                                                                        NEWS
   . Fixed odbc_field_len(), odbc_field_scale() and odbc_field_type()
     returning uninitialized memory when SQLColAttribute fails.
     (Ilia Alshanetsky)
+  . odbc_error() and odbc_errormsg() now also report SQLColAttribute
+    failures. (Ilia Alshanetsky)

 - PCNTL:
   . Fixed the declared signature of pcntl_signal(), whose $restart_syscalls
diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c
index d1cf2716cfc..4b05d8dc445 100644
--- a/ext/odbc/php_odbc.c
+++ b/ext/odbc/php_odbc.c
@@ -577,7 +577,7 @@ PHP_MINFO_FUNCTION(odbc)
 /* }}} */

 /* {{{ odbc_sql_error */
-void odbc_sql_error(ODBC_SQL_ERROR_PARAMS)
+void odbc_sql_error(odbc_connection *conn_resource, ODBC_SQL_STMT_T stmt, const char *func, ...)
 {
 	SQLINTEGER	error;        /* Not used */
 	SQLSMALLINT	errormsgsize; /* Not used */
@@ -608,7 +608,14 @@ void odbc_sql_error(ODBC_SQL_ERROR_PARAMS)
 		memcpy(conn_resource->lasterrormsg, ODBCG(lasterrormsg), sizeof(ODBCG(lasterrormsg)));
 	}
 	if (func) {
-		php_error_docref(NULL, E_WARNING, "SQL error: %s, SQL state %s in %s", ODBCG(lasterrormsg), ODBCG(laststate), func);
+		va_list args;
+		char *desc;
+
+		va_start(args, func);
+		vspprintf(&desc, 0, func, args);
+		va_end(args);
+		php_error_docref(NULL, E_WARNING, "SQL error: %s, SQL state %s in %s", ODBCG(lasterrormsg), ODBCG(laststate), desc);
+		efree(desc);
 	} else {
 		php_error_docref(NULL, E_WARNING, "SQL error: %s, SQL state %s", ODBCG(lasterrormsg), ODBCG(laststate));
 	}
@@ -785,25 +792,6 @@ void odbc_transact(INTERNAL_FUNCTION_PARAMETERS, int type)
 }
 /* }}} */

-static void odbc_colattribute_failed(odbc_result *result, zend_long pv_num)
-{
-#if defined(ODBCVER) && (ODBCVER >= 0x0300)
-	SQLINTEGER diag_error;
-	SQLCHAR diag_state[6];
-	SQLCHAR diag_text[128];
-
-	memset(diag_state, '\0', sizeof(diag_state));
-	memset(diag_text, '\0', sizeof(diag_text));
-	if (SQL_SUCCESS == SQLGetDiagRec(SQL_HANDLE_STMT, result->stmt, 1, diag_state, &diag_error, diag_text, sizeof(diag_text), NULL)) {
-		diag_state[sizeof(diag_state) - 1] = '\0';
-		diag_text[sizeof(diag_text) - 1] = '\0';
-		php_error_docref(NULL, E_WARNING, "SQLColAttribute failed for field #%d: [%s] %s", (int)pv_num, diag_state, diag_text);
-		return;
-	}
-#endif
-	php_error_docref(NULL, E_WARNING, "SQLColAttribute failed for field #%d", (int)pv_num);
-}
-
 /* {{{ odbc_column_lengths */
 void odbc_column_lengths(INTERNAL_FUNCTION_PARAMETERS, int type)
 {
@@ -836,7 +824,7 @@ void odbc_column_lengths(INTERNAL_FUNCTION_PARAMETERS, int type)

 	rc = SQLColAttribute(result->stmt, (SQLUSMALLINT)pv_num, (SQLUSMALLINT)(type ? SQL_COLUMN_SCALE : SQL_COLUMN_PRECISION), NULL, 0, NULL, &len);
 	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
-		odbc_colattribute_failed(result, pv_num);
+		odbc_sql_error(result->conn_ptr, result->stmt, "SQLColAttribute column #" ZEND_LONG_FMT, pv_num);
 		len = 0;
 	}

@@ -2368,7 +2356,7 @@ PHP_FUNCTION(odbc_field_type)
 	tmp[0] = '\0';
 	rc = SQLColAttribute(result->stmt, (SQLUSMALLINT)pv_num, SQL_COLUMN_TYPE_NAME, tmp, sizeof(tmp) - 1, &tmplen, NULL);
 	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
-		odbc_colattribute_failed(result, pv_num);
+		odbc_sql_error(result->conn_ptr, result->stmt, "SQLColAttribute column #" ZEND_LONG_FMT, pv_num);
 		RETURN_FALSE;
 	}

diff --git a/ext/odbc/php_odbc_includes.h b/ext/odbc/php_odbc_includes.h
index 090dfc3089a..ad51454ec15 100644
--- a/ext/odbc/php_odbc_includes.h
+++ b/ext/odbc/php_odbc_includes.h
@@ -145,9 +145,7 @@ odbc_connection *odbc_get_conn(HashTable *list, int count);
 void odbc_del_conn(HashTable *list, int ind);
 void odbc_bindcols(odbc_result *result);

-#define ODBC_SQL_ERROR_PARAMS odbc_connection *conn_resource, ODBC_SQL_STMT_T stmt, char *func
-
-void odbc_sql_error(ODBC_SQL_ERROR_PARAMS);
+void odbc_sql_error(odbc_connection *conn_resource, ODBC_SQL_STMT_T stmt, const char *func, ...) ZEND_ATTRIBUTE_FORMAT(printf, 3, 4);

 #define IS_SQL_LONG(x) (x == SQL_LONGVARBINARY || x == SQL_LONGVARCHAR || x == SQL_WLONGVARCHAR)