<div>Here is my work on the subject.</div>
<div> </div>
<div>First, I wrote a patch that iterates through multi-results in sql_store_result() and finally, takes the last result to store (<a href="http://tifs.anarxi.st/freeradius-1.1.3-mysql5_sp.v1.patch">http://tifs.anarxi.st/freeradius-1.1.3-mysql5_sp.v1.patch
</a>)</div>
<div> </div>
<div>But later I understood that better way to do so is in sql_finish_select_query(). Besides, I wanted a possibility to use stored procedures in accounting calls. So I've also written some code to sql_finish_query(). Oher changes were made as well.
</div>
<div> </div>
<div>While making the changes, I followed next rules:</div>
<div> - don't break sql_mysql.c code-style;</div>
<div> - add functionality only for MYSQL_VERSION_ID >= 50000</div>
<div> - use hints from rlm_sql_postgresql (like "rlm_sql_mysql: affected rows = %d" I took from there);</div>
<div> - follow mysql version 5.0 reference;</div>
<div> - final code should be able to handle all kinds of multi-results;</div>
<div> </div>
<div>Patch v2 (<a href="http://tifs.anarxi.st/freeradius-1.1.3-mysql5_sp.v2.patch">http://tifs.anarxi.st/freeradius-1.1.3-mysql5_sp.v2.patch</a>) allows:</div>
<div> </div>
<div> - Use stored procedures in 'select' calls (used in authorization queries).</div>
<div> First result that contains any fields is used.</div>
<div> Any other results are ignored and properly freed, and debug message is printed:</div>
<div>> rlm_sql_mysql: SQL query returned redundant result</div>
<div>> rlm_sql_mysql: redundant result (5 fields) ignored</div>
<div> Any affected rows result is dumped as:</div>
<div>> rlm_sql_mysql: affected rows = 0<br> </div>
<div> - Use stored procedures in 'update/insert query' calls (used in accounting).</div>
<div> Results with fields are ignored (with correspondind message).</div>
<div> First proper affected rows result is used for sql_affected_rows() if called.</div>
<div> Any other affected rows result is dumped.</div>
<div> </div>
<div>However it seems that MySQL currently is unable to return more than one affected rows count and some code can be cutted out. But I left it for compatibility with MySQL v5.0 reference.</div>
<div> </div>
<div>I've performed some testing:</div>
<div> - using SP as authorization reply query, trying to confuse rlm_sql_myql with multiple results (everything works ok, first result is used other ones ignored);</div>
<div> - using SP as accounting start query (sql_query() -> sql_finish_query()), works ok, redundant results are ignored, affected rows dumped;</div>
<div> - using SP as accounting update query (sql_query() -> sql_affected_rows() -> sql_finish_query()), works ok until we select any result set. affected rows = 0 comes instead of real affected rows count and freeradius tries alternative query. Though SP is still proceed ;)
</div>
<div> </div>
<div>Hope this will be useful for Freeradius project! Please, use this code if you found it suitable.</div>
<div> </div>
<div>The patch itself comes below (you can also download it here: <a href="http://tifs.anarxi.st/freeradius-1.1.3-mysql5_sp.v2.patch">http://tifs.anarxi.st/freeradius-1.1.3-mysql5_sp.v2.patch</a>)</div>
<div> </div>
<div>diff -ruN freeradius-1.1.3.orig/src/modules/rlm_sql/drivers/rlm_sql_mysql/sql_mysql.c freeradius-1.1.3/src/modules/rlm_sql/drivers/rlm_sql_mysql/sql_mysql.c<br>--- freeradius-1.1.3.orig/src/modules/rlm_sql/drivers/rlm_sql_mysql/sql_mysql.c 2005-12-09 18:10:
08.000000000 +0200<br>+++ freeradius-1.1.3/src/modules/rlm_sql/drivers/rlm_sql_mysql/sql_mysql.c 2007-05-17 02:10:13.000000000 +0300<br>@@ -47,9 +47,13 @@<br>MYSQL conn;<br>MYSQL *sock;<br>MYSQL_RES *result;<br>+ int num_fields;
<br>SQL_ROW row;<br>} rlm_sql_mysql_sock;<br><br>+/* Prototypes */<br>+static int sql_num_fields(SQLSOCK * sqlsocket, SQL_CONFIG *config);<br>+<br>/*************************************************************************
<br> *<br> * Function: sql_create_socket<br>@@ -82,7 +86,11 @@<br> config->sql_db,<br> atoi(config->sql_port),<br> NULL,<br>+#if MYSQL_VERSION_ID < 50000<br> CLIENT_FOUND_ROWS))) {<br>+#else<br>+ CLIENT_FOUND_ROWS|CLIENT_MULTI_STATEMENTS|CLIENT_MULTI_RESULTS))) {
<br>+#endif<br>radlog(L_ERR, "rlm_sql_mysql: Couldn't connect socket to MySQL server %s@%s:%s", config->sql_login, config->sql_server, config->sql_db);<br>radlog(L_ERR, "rlm_sql_mysql: Mysql error '%s'", mysql_error(&mysql_sock->conn));
<br>mysql_sock->sock = NULL;<br>@@ -173,18 +181,43 @@<br> *************************************************************************/<br>static int sql_store_result(SQLSOCK * sqlsocket, SQL_CONFIG *config)<br>{<br>+#if MYSQL_VERSION_ID >= 50000
<br>+ int status;<br>+#endif<br>rlm_sql_mysql_sock *mysql_sock = sqlsocket->conn;<br><br>if (mysql_sock->sock == NULL) {<br>radlog(L_ERR, "rlm_sql_mysql: Socket not connected");<br>return SQL_DOWN;<br>}<br>
+#if MYSQL_VERSION_ID >= 50000<br>+ /*<br>+ * Get every affected row count prior to result<br>+ */<br>+ while (!(mysql_field_count(mysql_sock->sock))) {<br>+ radlog(L_DBG, "rlm_sql_mysql: affected rows = %d",
<br>+ mysql_affected_rows(mysql_sock->sock));<br>+<br>+ if ((status = mysql_next_result(mysql_sock->sock))) {<br>+ if (status > 0) {<br>+ radlog(L_ERR, "rlm_sql_mysql: MYSQL Error: Cannot get next result");
<br>+ radlog(L_ERR, "rlm_sql_mysql: MYSQL Error: %s",<br>+ mysql_error(mysql_sock->sock));<br>+ return sql_check_error(mysql_errno(mysql_sock->sock));<br>+ }<br>+ break;<br>+ }<br>+ }<br>+#endif<br>
+<br>if (!(mysql_sock->result = mysql_store_result(mysql_sock->sock))) {<br>radlog(L_ERR, "rlm_sql_mysql: MYSQL Error: Cannot get result");<br>radlog(L_ERR, "rlm_sql_mysql: MYSQL Error: %s",<br> mysql_error(mysql_sock->sock));
<br>return sql_check_error(mysql_errno(mysql_sock->sock));<br>}<br>+ mysql_sock->num_fields = sql_num_fields(sqlsocket, config);<br>+<br>return 0;<br>}<br><br>@@ -235,12 +268,6 @@<br>return ret;<br>}<br><br>- /* Why? Per
<a href="http://www.mysql.com/doc/n/o/node_591.html">http://www.mysql.com/doc/n/o/node_591.html</a>,<br>- * this cannot return an error. Perhaps just to complain if no<br>- * fields are found?<br>- */<br>- sql_num_fields(sqlsocket, config);
<br>-<br>return ret;<br>}<br><br>@@ -364,6 +391,59 @@<br> *************************************************************************/<br>static int sql_finish_query(SQLSOCK * sqlsocket, SQL_CONFIG *config)<br>{<br>+#if MYSQL_VERSION_ID >= 50000
<br>+ int status;<br>+ int field_count;<br>+ MYSQL_RES *result;<br>+ rlm_sql_mysql_sock *mysql_sock = sqlsocket->conn;<br>+<br>+ /*<br>+ * Free current result if we have one<br>+ */<br>+ if ((field_count = mysql_field_count(mysql_sock->sock))) {
<br>+ radlog(L_DBG, "rlm_sql_mysql: SQL query returned redundant result");<br>+ if (!(result = mysql_store_result(mysql_sock->sock))) {<br>+ radlog(L_ERR, "rlm_sql_mysql: MYSQL Error: Cannot get result");
<br>+ radlog(L_ERR, "rlm_sql_mysql: MYSQL Error: %s",<br>+ mysql_error(mysql_sock->sock));<br>+ return sql_check_error(mysql_errno(mysql_sock->sock));<br>+ }<br>+ radlog(L_DBG, "rlm_sql_mysql: redundant result (%d fields) ignored",
<br>+ field_count);<br>+ mysql_free_result(result);<br>+ }<br>+<br>+ /*<br>+ * Get and free every next result or affected row count<br>+ */<br>+ for (;;) {<br>+ if ((status = mysql_next_result(mysql_sock->sock))) {
<br>+ if (status > 0) {<br>+ radlog(L_ERR, "rlm_sql_mysql: MYSQL Error: Cannot get next result");<br>+ radlog(L_ERR, "rlm_sql_mysql: MYSQL Error: %s",<br>+ mysql_error(mysql_sock->sock));<br>
+ return sql_check_error(mysql_errno(mysql_sock->sock));<br>+ }<br>+ break;<br>+ }<br>+ if ((field_count = mysql_field_count(mysql_sock->sock))) {<br>+ radlog(L_DBG, "rlm_sql_mysql: SQL query returned redundant result");
<br>+ if (!(result = mysql_store_result(mysql_sock->sock))) {<br>+ radlog(L_ERR, "rlm_sql_mysql: MYSQL Error: Cannot get result");<br>+ radlog(L_ERR, "rlm_sql_mysql: MYSQL Error: %s",<br>+ mysql_error(mysql_sock->sock));
<br>+ return sql_check_error(mysql_errno(mysql_sock->sock));<br>+ }<br>+ radlog(L_DBG, "rlm_sql_mysql: redundant result (%d fields) ignored",<br>+ field_count);<br>+ mysql_free_result(result);
<br>+ } else {<br>+ radlog(L_DBG, "rlm_sql_mysql: affected rows = %d",<br>+ mysql_affected_rows(mysql_sock->sock));<br>+ }<br>+ }<br>+#endif<br>+<br>return 0;<br>}<br><br>@@ -378,7 +458,45 @@<br> *************************************************************************/
<br>static int sql_finish_select_query(SQLSOCK * sqlsocket, SQL_CONFIG *config)<br>{<br>+#if MYSQL_VERSION_ID >= 50000<br>+ int status;<br>+ int field_count;<br>+ MYSQL_RES *result;<br>+ rlm_sql_mysql_sock *mysql_sock = sqlsocket->conn;
<br>+#endif<br>+<br>sql_free_result(sqlsocket, config);<br>+#if MYSQL_VERSION_ID >= 50000<br>+ /*<br>+ * Get and free every next result or affected row count<br>+ */<br>+ for (;;) {<br>+ if ((status = mysql_next_result(mysql_sock->sock))) {
<br>+ if (status > 0) {<br>+ radlog(L_ERR, "rlm_sql_mysql: MYSQL Error: Cannot get next result");<br>+ radlog(L_ERR, "rlm_sql_mysql: MYSQL Error: %s",<br>+ mysql_error(mysql_sock->sock));<br>
+ return sql_check_error(mysql_errno(mysql_sock->sock));<br>+ }<br>+ break;<br>+ }<br>+ if ((field_count = mysql_field_count(mysql_sock->sock))) {<br>+ radlog(L_DBG, "rlm_sql_mysql: SQL query returned redundant result");
<br>+ if (!(result = mysql_store_result(mysql_sock->sock))) {<br>+ radlog(L_ERR, "rlm_sql_mysql: MYSQL Error: Cannot get result");<br>+ radlog(L_ERR, "rlm_sql_mysql: MYSQL Error: %s",<br>+ mysql_error(mysql_sock->sock));
<br>+ return sql_check_error(mysql_errno(mysql_sock->sock));<br>+ }<br>+ radlog(L_DBG, "rlm_sql_mysql: redundant result (%d fields) ignored",<br>+ field_count);<br>+ mysql_free_result(result);
<br>+ } else {<br>+ radlog(L_DBG, "rlm_sql_mysql: affected rows = %d",<br>+ mysql_affected_rows(mysql_sock->sock));<br>+ }<br>+ }<br>+#endif<br><br>return 0;<br>}<br>@@ -393,8 +511,40 @@<br> *************************************************************************/
<br>static int sql_affected_rows(SQLSOCK * sqlsocket, SQL_CONFIG *config)<br>{<br>+#if MYSQL_VERSION_ID >= 50000<br>+ int status;<br>+ int field_count;<br>+ MYSQL_RES *result;<br>+#endif<br>rlm_sql_mysql_sock *mysql_sock = sqlsocket->conn;
<br><br>+#if MYSQL_VERSION_ID >= 50000<br>+ /*<br>+ * Get and free every result prior to affected row count<br>+ */<br>+ while ((field_count = mysql_field_count(mysql_sock->sock))) {<br>+ radlog(L_DBG, "rlm_sql_mysql: SQL query returned redundant result");
<br>+ if (!(result = mysql_store_result(mysql_sock->sock))) {<br>+ radlog(L_ERR, "rlm_sql_mysql: MYSQL Error: Cannot get result");<br>+ radlog(L_ERR, "rlm_sql_mysql: MYSQL Error: %s",<br>+ mysql_error(mysql_sock->sock));
<br>+ return -1;<br>+ }<br>+ radlog(L_DBG, "rlm_sql_mysql: redundant result (%d fields) ignored",<br>+ field_count);<br>+ mysql_free_result(result);<br>+<br>+ if ((status = mysql_next_result(mysql_sock->sock))) {
<br>+ if (status > 0) {<br>+ radlog(L_ERR, "rlm_sql_mysql: MYSQL Error: Cannot get next result");<br>+ radlog(L_ERR, "rlm_sql_mysql: MYSQL Error: %s",<br>+ mysql_error(mysql_sock->sock));<br>
+ return -1;<br>+ }<br>+ return -1;<br>+ }<br>+ }<br>+#endif<br>return mysql_affected_rows(mysql_sock->sock);<br>}<br> </div>