You've already forked mariadb-connector-c
mirror of
https://github.com/mariadb-corporation/mariadb-connector-c.git
synced 2025-08-08 14:02:17 +03:00
Workaround/Fix for CONC-424:
1) In case a cursor is used in a stored procedure server sends a SERVER_STATUS_CURSOR_EXISTS flag, even if the client didn't open a cursor via mysql_stmt_attr_set. This ends in a "comands out of sync" error, since the client is sending a COM_STMT_FETCH command while the result sets were not read before. We check now in case server sends a SERVER_STATUS_CURSOR_EXIST flag, if the client opened a cursor before. 2) The stmt error codes weren't update for all COM_STMT* commands correctly, e.g. COM_STMT_FETCH didn't show an error even if it failed.
This commit is contained in:
@@ -56,6 +56,9 @@
|
|||||||
#include <mysql/client_plugin.h>
|
#include <mysql/client_plugin.h>
|
||||||
#include <ma_common.h>
|
#include <ma_common.h>
|
||||||
|
|
||||||
|
#define UPDATE_STMT_ERROR(stmt)\
|
||||||
|
SET_CLIENT_STMT_ERROR((stmt), (stmt)->mysql->net.last_errno, (stmt)->mysql->net.sqlstate, (stmt)->mysql->net.last_error)
|
||||||
|
|
||||||
#define STMT_NUM_OFS(type, a,r) ((type *)(a))[r]
|
#define STMT_NUM_OFS(type, a,r) ((type *)(a))[r]
|
||||||
#define MADB_RESET_ERROR 1
|
#define MADB_RESET_ERROR 1
|
||||||
#define MADB_RESET_LONGDATA 2
|
#define MADB_RESET_LONGDATA 2
|
||||||
@@ -310,7 +313,10 @@ static int stmt_cursor_fetch(MYSQL_STMT *stmt, uchar **row)
|
|||||||
int4store(buf + STMT_ID_LENGTH, stmt->prefetch_rows);
|
int4store(buf + STMT_ID_LENGTH, stmt->prefetch_rows);
|
||||||
|
|
||||||
if (stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_FETCH, (char *)buf, sizeof(buf), 1, stmt))
|
if (stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_FETCH, (char *)buf, sizeof(buf), 1, stmt))
|
||||||
|
{
|
||||||
|
UPDATE_STMT_ERROR(stmt);
|
||||||
return(1);
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
/* free previously allocated buffer */
|
/* free previously allocated buffer */
|
||||||
ma_free_root(&result->alloc, MYF(MY_KEEP_PREALLOC));
|
ma_free_root(&result->alloc, MYF(MY_KEEP_PREALLOC));
|
||||||
@@ -1327,7 +1333,7 @@ static my_bool net_stmt_close(MYSQL_STMT *stmt, my_bool remove)
|
|||||||
if (stmt->mysql->methods->db_command(stmt->mysql,COM_STMT_CLOSE, stmt_id,
|
if (stmt->mysql->methods->db_command(stmt->mysql,COM_STMT_CLOSE, stmt_id,
|
||||||
sizeof(stmt_id), 1, stmt))
|
sizeof(stmt_id), 1, stmt))
|
||||||
{
|
{
|
||||||
SET_CLIENT_STMT_ERROR(stmt, stmt->mysql->net.last_errno, stmt->mysql->net.sqlstate, stmt->mysql->net.last_error);
|
UPDATE_STMT_ERROR(stmt);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1671,8 +1677,7 @@ int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, unsigned lon
|
|||||||
|
|
||||||
fail:
|
fail:
|
||||||
stmt->state= MYSQL_STMT_INITTED;
|
stmt->state= MYSQL_STMT_INITTED;
|
||||||
SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate,
|
UPDATE_STMT_ERROR(stmt);
|
||||||
mysql->net.last_error);
|
|
||||||
return(rc);
|
return(rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1886,7 +1891,8 @@ int stmt_read_execute_response(MYSQL_STMT *stmt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS)
|
if ((stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) &&
|
||||||
|
(stmt->flags & CURSOR_TYPE_READ_ONLY))
|
||||||
{
|
{
|
||||||
stmt->cursor_exists = TRUE;
|
stmt->cursor_exists = TRUE;
|
||||||
mysql->status = MYSQL_STATUS_READY;
|
mysql->status = MYSQL_STATUS_READY;
|
||||||
@@ -2006,8 +2012,7 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
|
|||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
{
|
{
|
||||||
SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate,
|
UPDATE_STMT_ERROR(stmt);
|
||||||
mysql->net.last_error);
|
|
||||||
return(1);
|
return(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2078,8 +2083,7 @@ static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags)
|
|||||||
if ((ret= stmt->mysql->methods->db_command(mysql,COM_STMT_RESET, (char *)cmd_buf,
|
if ((ret= stmt->mysql->methods->db_command(mysql,COM_STMT_RESET, (char *)cmd_buf,
|
||||||
sizeof(cmd_buf), 0, stmt)))
|
sizeof(cmd_buf), 0, stmt)))
|
||||||
{
|
{
|
||||||
SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate,
|
UPDATE_STMT_ERROR(stmt);
|
||||||
mysql->net.last_error);
|
|
||||||
return(ret);
|
return(ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2231,6 +2235,8 @@ my_bool STDCALL mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number,
|
|||||||
stmt->params[param_number].long_data_used= 1;
|
stmt->params[param_number].long_data_used= 1;
|
||||||
ret= stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_SEND_LONG_DATA,
|
ret= stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_SEND_LONG_DATA,
|
||||||
(char *)cmd_buff, packet_len, 1, stmt);
|
(char *)cmd_buff, packet_len, 1, stmt);
|
||||||
|
if (ret)
|
||||||
|
UPDATE_STMT_ERROR(stmt);
|
||||||
free(cmd_buff);
|
free(cmd_buff);
|
||||||
return(ret);
|
return(ret);
|
||||||
}
|
}
|
||||||
@@ -2441,8 +2447,7 @@ int STDCALL mariadb_stmt_execute_direct(MYSQL_STMT *stmt,
|
|||||||
fail:
|
fail:
|
||||||
/* check if we need to set error message */
|
/* check if we need to set error message */
|
||||||
if (!mysql_stmt_errno(stmt))
|
if (!mysql_stmt_errno(stmt))
|
||||||
SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate,
|
UPDATE_STMT_ERROR(stmt);
|
||||||
mysql->net.last_error);
|
|
||||||
do {
|
do {
|
||||||
stmt->mysql->methods->db_stmt_flush_unbuffered(stmt);
|
stmt->mysql->methods->db_stmt_flush_unbuffered(stmt);
|
||||||
} while(mysql_stmt_more_results(stmt));
|
} while(mysql_stmt_more_results(stmt));
|
||||||
|
@@ -5042,8 +5042,77 @@ static int test_zerofill_1byte(MYSQL *mysql)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int test_conc424(MYSQL *mysql)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
MYSQL_STMT *stmt;
|
||||||
|
my_bool max_len= 1;
|
||||||
|
|
||||||
|
rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_table1");
|
||||||
|
check_mysql_rc(rc, mysql);
|
||||||
|
rc= mysql_query(mysql, "CREATE TABLE test_table1 (test_int INT, b int)");
|
||||||
|
check_mysql_rc(rc, mysql);
|
||||||
|
rc= mysql_query(mysql, "INSERT INTO test_table1 values(10,11),(11,12)");
|
||||||
|
check_mysql_rc(rc, mysql);
|
||||||
|
|
||||||
|
rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS testCursor");
|
||||||
|
check_mysql_rc(rc, mysql);
|
||||||
|
|
||||||
|
rc= mysql_query(mysql, "CREATE PROCEDURE testCursor()\n"
|
||||||
|
"BEGIN\n"
|
||||||
|
"DECLARE test_int INT;\n"
|
||||||
|
"DECLARE b INT;\n"
|
||||||
|
"DECLARE done INT DEFAULT FALSE;\n"
|
||||||
|
"DECLARE testCursor CURSOR\n"
|
||||||
|
"FOR\n"
|
||||||
|
"SELECT test_int,b FROM test_table1;\n"
|
||||||
|
"DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;\n"
|
||||||
|
"OPEN testCursor;\n"
|
||||||
|
|
||||||
|
" read_loop: LOOP\n"
|
||||||
|
" FETCH testCursor INTO test_int, b;\n"
|
||||||
|
" IF done THEN\n"
|
||||||
|
" LEAVE read_loop;\n"
|
||||||
|
" END IF;\n"
|
||||||
|
" SELECT test_int,b;"
|
||||||
|
" END LOOP;\n"
|
||||||
|
"CLOSE testCursor;\n"
|
||||||
|
"END");
|
||||||
|
check_mysql_rc(rc, mysql);
|
||||||
|
|
||||||
|
stmt= mysql_stmt_init(mysql);
|
||||||
|
rc= mysql_stmt_prepare(stmt, SL("CALL testCursor()"));
|
||||||
|
check_stmt_rc(rc, stmt);
|
||||||
|
|
||||||
|
rc= mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &max_len);
|
||||||
|
check_stmt_rc(rc, stmt);
|
||||||
|
|
||||||
|
rc= mysql_stmt_execute(stmt);
|
||||||
|
check_stmt_rc(rc, stmt);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (mysql_stmt_field_count(stmt))
|
||||||
|
{
|
||||||
|
MYSQL_RES *res= mysql_stmt_result_metadata(stmt);
|
||||||
|
rc= mysql_stmt_fetch(stmt);
|
||||||
|
FAIL_IF(rc, "Wrong return code");
|
||||||
|
mysql_free_result(res);
|
||||||
|
}
|
||||||
|
rc= mysql_stmt_next_result(stmt);
|
||||||
|
|
||||||
|
} while (!rc);
|
||||||
|
|
||||||
|
rc= mysql_query(mysql, "DROP PROCEDURE testCursor");
|
||||||
|
check_mysql_rc(rc, mysql);
|
||||||
|
|
||||||
|
rc= mysql_query(mysql, "DROP TABLE test_table1");
|
||||||
|
check_mysql_rc(rc, mysql);
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
struct my_tests_st my_tests[] = {
|
struct my_tests_st my_tests[] = {
|
||||||
|
{"test_conc424", test_conc424, TEST_CONNECTION_NEW, 0, NULL, NULL},
|
||||||
{"test_conc344", test_conc344, TEST_CONNECTION_NEW, 0, NULL, NULL},
|
{"test_conc344", test_conc344, TEST_CONNECTION_NEW, 0, NULL, NULL},
|
||||||
{"test_conc334", test_conc334, TEST_CONNECTION_NEW, 0, NULL, NULL},
|
{"test_conc334", test_conc334, TEST_CONNECTION_NEW, 0, NULL, NULL},
|
||||||
{"test_compress", test_compress, TEST_CONNECTION_NEW, CLIENT_COMPRESS, NULL, NULL},
|
{"test_compress", test_compress, TEST_CONNECTION_NEW, CLIENT_COMPRESS, NULL, NULL},
|
||||||
|
Reference in New Issue
Block a user