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
Fix for CONC-71: mysql_real_query crashes after server restart
- We now check socket status before net_flush (and return error if the socket is dead)
This commit is contained in:
@@ -21,12 +21,16 @@ IF(NOT WIN32)
|
|||||||
OPTION(WITH_OPENSSL "enables SSL support" ON)
|
OPTION(WITH_OPENSSL "enables SSL support" ON)
|
||||||
ELSE()
|
ELSE()
|
||||||
OPTION(WITH_OPENSSL "enables SSL support" OFF)
|
OPTION(WITH_OPENSSL "enables SSL support" OFF)
|
||||||
|
OPTION(WITH_RTC "enables run time checks for debug builds" OFF)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
OPTION(WITH_SQLITE "Enables Sqlite support" OFF)
|
OPTION(WITH_SQLITE "Enables Sqlite support" OFF)
|
||||||
OPTION(WITH_EXTERNAL_ZLIB "Enables use of external zlib" OFF)
|
OPTION(WITH_EXTERNAL_ZLIB "Enables use of external zlib" OFF)
|
||||||
###############
|
###############
|
||||||
|
|
||||||
|
IF(WITH_RTC)
|
||||||
|
SET(RTC_OPTIONS "/RTC1 /RTCc")
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
IF(MSVC)
|
IF(MSVC)
|
||||||
# Speedup system tests
|
# Speedup system tests
|
||||||
@@ -39,7 +43,7 @@ IF(MSVC)
|
|||||||
IF (NOT COMPILER_FLAGS STREQUAL "")
|
IF (NOT COMPILER_FLAGS STREQUAL "")
|
||||||
STRING(REPLACE "/MD" "/MT" COMPILER_FLAGS ${COMPILER_FLAGS})
|
STRING(REPLACE "/MD" "/MT" COMPILER_FLAGS ${COMPILER_FLAGS})
|
||||||
IF (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
IF (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
SET(COMPILER_FLAGS "${COMPILER_FLAGS} /RTC1 /RTCc")
|
SET(COMPILER_FLAGS "${COMPILER_FLAGS} ${RTC_OPTIONS}")
|
||||||
STRING(REPLACE "/Zi" "/ZI" COMPILER_FLAGS ${COMPILER_FLAGS})
|
STRING(REPLACE "/Zi" "/ZI" COMPILER_FLAGS ${COMPILER_FLAGS})
|
||||||
ENDIF()
|
ENDIF()
|
||||||
MESSAGE (STATUS "CMAKE_${COMPILER}_FLAGS_${BUILD_TYPE}= ${COMPILER_FLAGS}")
|
MESSAGE (STATUS "CMAKE_${COMPILER}_FLAGS_${BUILD_TYPE}= ${COMPILER_FLAGS}")
|
||||||
|
@@ -127,7 +127,6 @@ ELSE()
|
|||||||
../zlib/compress.c
|
../zlib/compress.c
|
||||||
../zlib/crc32.c
|
../zlib/crc32.c
|
||||||
../zlib/deflate.c
|
../zlib/deflate.c
|
||||||
../zlib/example.c
|
|
||||||
../zlib/gzclose.c
|
../zlib/gzclose.c
|
||||||
../zlib/gzlib.c
|
../zlib/gzlib.c
|
||||||
../zlib/gzread.c
|
../zlib/gzread.c
|
||||||
|
@@ -31,8 +31,8 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#ifdef MYSQL_SERVER
|
#ifndef _WIN32
|
||||||
#include <violite.h>
|
#include <poll.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MAX_PACKET_LENGTH (256L*256L*256L-1)
|
#define MAX_PACKET_LENGTH (256L*256L*256L-1)
|
||||||
@@ -114,8 +114,6 @@ int my_net_init(NET *net, Vio* vio)
|
|||||||
{
|
{
|
||||||
if (!(net->buff=(uchar*) my_malloc(net_buffer_length,MYF(MY_WME | MY_ZEROFILL))))
|
if (!(net->buff=(uchar*) my_malloc(net_buffer_length,MYF(MY_WME | MY_ZEROFILL))))
|
||||||
return 1;
|
return 1;
|
||||||
// if (net_buffer_length > max_allowed_packet)
|
|
||||||
// max_allowed_packet=net_buffer_length;
|
|
||||||
max_allowed_packet= net->max_packet_size= MAX(net_buffer_length, max_allowed_packet);
|
max_allowed_packet= net->max_packet_size= MAX(net_buffer_length, max_allowed_packet);
|
||||||
net->buff_end=net->buff+(net->max_packet=net_buffer_length);
|
net->buff_end=net->buff+(net->max_packet=net_buffer_length);
|
||||||
net->vio = vio;
|
net->vio = vio;
|
||||||
@@ -166,13 +164,10 @@ static my_bool net_realloc(NET *net, size_t length)
|
|||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1);
|
pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1);
|
||||||
if (!(buff=(uchar*) my_realloc((char*) net->buff, pkt_length, MYF(MY_WME))))
|
if (!(buff=(uchar*) my_realloc((char*) net->buff, pkt_length + 1, MYF(MY_WME))))
|
||||||
{
|
{
|
||||||
DBUG_PRINT("info", ("Out of memory"));
|
DBUG_PRINT("info", ("Out of memory"));
|
||||||
net->error=1;
|
net->error=1;
|
||||||
#ifdef MYSQL_SERVER
|
|
||||||
net->last_errno=ER_OUT_OF_RESOURCES;
|
|
||||||
#endif
|
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
net->buff=net->write_pos=buff;
|
net->buff=net->write_pos=buff;
|
||||||
@@ -180,21 +175,45 @@ static my_bool net_realloc(NET *net, size_t length)
|
|||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_SOCKET
|
|
||||||
static ssize_t net_check_if_data_available(Vio *vio)
|
/* check if the socket is still alive */
|
||||||
|
static my_bool net_check_socket_status(my_socket sock)
|
||||||
{
|
{
|
||||||
ssize_t length= 0;
|
#ifndef _WIN32
|
||||||
|
struct pollfd poll_fd;
|
||||||
if (vio->type != VIO_TYPE_SOCKET &&
|
#else
|
||||||
vio->type != VIO_TYPE_TCPIP)
|
FD_SET sfds;
|
||||||
return 0;
|
struct timeval tv= {0,0};
|
||||||
|
|
||||||
if (vio_read_peek(vio, (size_t *)&length))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
int res;
|
||||||
|
#ifndef _WIN32
|
||||||
|
memset(&poll_fd, 0, sizeof(struct pollfd));
|
||||||
|
poll_fd.events= POLLPRI | POLLIN;
|
||||||
|
poll_fd.fd= sock;
|
||||||
|
|
||||||
|
res= poll(&poll_fd, 1, 0);
|
||||||
|
if (res <= 0) /* timeout or error */
|
||||||
|
return FALSE;
|
||||||
|
if (!(poll_fd.revents & (POLLIN | POLLPRI)))
|
||||||
|
return FALSE;
|
||||||
|
return TRUE;
|
||||||
|
#else
|
||||||
|
/* We can't use the WSAPoll function, it's broken :-(
|
||||||
|
(see Windows 8 Bugs 309411 - WSAPoll does not report failed connections)
|
||||||
|
Instead we need to use select function:
|
||||||
|
If TIMEVAL is initialized to {0, 0}, select will return immediately;
|
||||||
|
this is used to poll the state of the selected sockets.
|
||||||
|
*/
|
||||||
|
FD_ZERO(&sfds);
|
||||||
|
FD_SET(sock, &sfds);
|
||||||
|
|
||||||
|
res= select(sock + 1, &sfds, NULL, NULL, &tv);
|
||||||
|
if (res > 0 && FD_ISSET(sock, &sfds))
|
||||||
|
return TRUE;
|
||||||
|
return FALSE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/* Remove unwanted characters from connection */
|
/* Remove unwanted characters from connection */
|
||||||
|
|
||||||
@@ -202,10 +221,19 @@ void net_clear(NET *net)
|
|||||||
{
|
{
|
||||||
DBUG_ENTER("net_clear");
|
DBUG_ENTER("net_clear");
|
||||||
|
|
||||||
#ifdef DEBUG_SOCKET
|
/* see conc-71: we need to check the socket status first:
|
||||||
DBUG_ASSERT(net_check_if_data_available(net->vio) < 2);
|
if the socket is dead we set net->error, so net_flush
|
||||||
#endif
|
will report an error */
|
||||||
|
while (net_check_socket_status(net->vio->sd))
|
||||||
|
{
|
||||||
|
/* vio_read returns size_t. so casting to long is required to check for -1 */
|
||||||
|
if ((long)vio_read(net->vio, (gptr)net->buff, (size_t) net->max_packet) <= 0)
|
||||||
|
{
|
||||||
|
net->error= 2;
|
||||||
|
DBUG_PRINT("info", ("socket disconnected"));
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
}
|
||||||
net->compress_pkt_nr= net->pkt_nr=0; /* Ready for new command */
|
net->compress_pkt_nr= net->pkt_nr=0; /* Ready for new command */
|
||||||
net->write_pos=net->buff;
|
net->write_pos=net->buff;
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
@@ -810,13 +838,14 @@ ulong my_net_read(NET *net)
|
|||||||
net->where_b=buffer_length;
|
net->where_b=buffer_length;
|
||||||
|
|
||||||
if ((packet_length = my_real_read(net,(size_t *)&complen)) == packet_error)
|
if ((packet_length = my_real_read(net,(size_t *)&complen)) == packet_error)
|
||||||
break;
|
return packet_error;
|
||||||
if (my_uncompress((unsigned char*) net->buff + net->where_b, &packet_length, &complen))
|
if (my_uncompress((unsigned char*) net->buff + net->where_b, &packet_length, &complen))
|
||||||
{
|
{
|
||||||
len= packet_error;
|
len= packet_error;
|
||||||
net->error=2; /* caller will close socket */
|
net->error=2; /* caller will close socket */
|
||||||
net->last_errno=ER_NET_UNCOMPRESS_ERROR;
|
net->last_errno=ER_NET_UNCOMPRESS_ERROR;
|
||||||
break;
|
break;
|
||||||
|
return packet_error;
|
||||||
}
|
}
|
||||||
buffer_length+= complen;
|
buffer_length+= complen;
|
||||||
}
|
}
|
||||||
@@ -827,11 +856,8 @@ ulong my_net_read(NET *net)
|
|||||||
len= current - start - 4;
|
len= current - start - 4;
|
||||||
if (is_multi_packet)
|
if (is_multi_packet)
|
||||||
len-= 4;
|
len-= 4;
|
||||||
if (len != packet_error)
|
net->save_char= net->read_pos[len]; /* Must be saved */
|
||||||
{
|
net->read_pos[len]=0; /* Safeguard for mysql_use_result */
|
||||||
net->save_char= net->read_pos[len]; /* Must be saved */
|
|
||||||
net->read_pos[len]=0; /* Safeguard for mysql_use_result */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return (ulong)len;
|
return (ulong)len;
|
||||||
|
@@ -30,6 +30,38 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "my_test.h"
|
#include "my_test.h"
|
||||||
#include "ma_common.h"
|
#include "ma_common.h"
|
||||||
|
|
||||||
|
static int test_conc71(MYSQL *my)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
MYSQL_RES *res;
|
||||||
|
MYSQL_ROW row;
|
||||||
|
char *query;
|
||||||
|
MYSQL *mysql;
|
||||||
|
|
||||||
|
/* uncomment if you want to test manually */
|
||||||
|
return SKIP;
|
||||||
|
|
||||||
|
mysql= mysql_init(NULL);
|
||||||
|
|
||||||
|
|
||||||
|
mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "utf8");
|
||||||
|
mysql_options(mysql, MYSQL_OPT_COMPRESS, 0);
|
||||||
|
mysql_options(mysql, MYSQL_INIT_COMMAND, "/*!40101 SET SQL_MODE='' */");
|
||||||
|
mysql_options(mysql, MYSQL_INIT_COMMAND, "/*!40101 set @@session.wait_timeout=28800 */");
|
||||||
|
|
||||||
|
FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema,
|
||||||
|
port, socketname, 0), mysql_error(my));
|
||||||
|
|
||||||
|
diag("kill server");
|
||||||
|
sleep(20);
|
||||||
|
|
||||||
|
rc= mysql_query(mysql, "SELECT 'foo' FROM DUAL");
|
||||||
|
check_mysql_rc(rc, mysql);
|
||||||
|
|
||||||
|
mysql_close(mysql);
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
static int test_conc70(MYSQL *my)
|
static int test_conc70(MYSQL *my)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
@@ -37,7 +69,7 @@ static int test_conc70(MYSQL *my)
|
|||||||
MYSQL_ROW row;
|
MYSQL_ROW row;
|
||||||
MYSQL *mysql= mysql_init(NULL);
|
MYSQL *mysql= mysql_init(NULL);
|
||||||
|
|
||||||
mysql_query(my, "SET @a:=@@max_allowed_packet");
|
rc= mysql_query(my, "SET @a:=@@max_allowed_packet");
|
||||||
check_mysql_rc(rc, my);
|
check_mysql_rc(rc, my);
|
||||||
|
|
||||||
mysql_query(my, "SET global max_allowed_packet=1024*1024*22");
|
mysql_query(my, "SET global max_allowed_packet=1024*1024*22");
|
||||||
@@ -84,7 +116,7 @@ static int test_conc68(MYSQL *my)
|
|||||||
MYSQL_ROW row;
|
MYSQL_ROW row;
|
||||||
MYSQL *mysql= mysql_init(NULL);
|
MYSQL *mysql= mysql_init(NULL);
|
||||||
|
|
||||||
mysql_query(my, "SET @a:=@@max_allowed_packet");
|
rc= mysql_query(my, "SET @a:=@@max_allowed_packet");
|
||||||
check_mysql_rc(rc, my);
|
check_mysql_rc(rc, my);
|
||||||
|
|
||||||
mysql_query(my, "SET global max_allowed_packet=1024*1024*22");
|
mysql_query(my, "SET global max_allowed_packet=1024*1024*22");
|
||||||
@@ -648,6 +680,7 @@ static int test_compressed(MYSQL *my)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct my_tests_st my_tests[] = {
|
struct my_tests_st my_tests[] = {
|
||||||
|
{"test_conc71", test_conc71, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||||
{"test_conc70", test_conc70, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
{"test_conc70", test_conc70, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||||
{"test_conc68", test_conc68, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
{"test_conc68", test_conc68, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||||
{"test_compressed", test_compressed, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
{"test_compressed", test_compressed, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
||||||
|
@@ -65,6 +65,9 @@ static int test_logs(MYSQL *mysql)
|
|||||||
int rc;
|
int rc;
|
||||||
short id;
|
short id;
|
||||||
|
|
||||||
|
rc= mysql_query(mysql, "SET session sql_mode=''");
|
||||||
|
check_mysql_rc(rc, mysql);
|
||||||
|
|
||||||
rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_logs");
|
rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_logs");
|
||||||
check_mysql_rc(rc, mysql);
|
check_mysql_rc(rc, mysql);
|
||||||
|
|
||||||
|
3
unittest/libmariadb/my.cnf
Normal file
3
unittest/libmariadb/my.cnf
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[conc-66]
|
||||||
|
user=conc66
|
||||||
|
password='test;#test'
|
@@ -1,13 +1,13 @@
|
|||||||
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/zlib)
|
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/zlib)
|
||||||
|
|
||||||
SET(SOURCE_FILES adler32.c compress.c crc32.c deflate.c example.c gzclose.c gzlib.c gzread.c gzwrite.c infback.c inffast.c inflate.c inftrees.c minigzip.c trees.c uncompr.c zutil.c)
|
SET(SOURCE_FILES adler32.c compress.c crc32.c deflate.c gzclose.c gzlib.c gzread.c gzwrite.c infback.c inffast.c inflate.c inftrees.c minigzip.c trees.c uncompr.c zutil.c)
|
||||||
|
|
||||||
ADD_LIBRARY(zlib ${SOURCE_FILES})
|
ADD_LIBRARY(zlib ${SOURCE_FILES})
|
||||||
SET_TARGET_PROPERTIES(zlib PROPERTIES COMPILE_FLAGS
|
SET_TARGET_PROPERTIES(zlib PROPERTIES COMPILE_FLAGS
|
||||||
"${CMAKE_SHARED_LIBRARY_C_FLAGS}")
|
"${CMAKE_SHARED_LIBRARY_C_FLAGS}")
|
||||||
|
|
||||||
INSTALL(TARGETS
|
INSTALL(TARGETS
|
||||||
zlib
|
zlib
|
||||||
RUNTIME DESTINATION "lib"
|
RUNTIME DESTINATION "lib"
|
||||||
LIBRARY DESTINATION "lib"
|
LIBRARY DESTINATION "lib"
|
||||||
ARCHIVE DESTINATION "lib")
|
ARCHIVE DESTINATION "lib")
|
||||||
|
Reference in New Issue
Block a user