Merge branch '2.2' into develop

This commit is contained in:
Markus Mäkelä
2018-04-18 08:00:48 +03:00
8 changed files with 172 additions and 23 deletions

View File

@ -217,6 +217,9 @@ SELECT ..INTO variable|OUTFILE|DUMPFILE
SET autocommit=1|0
```
Session commands that are 2²⁴ - 1 bytes or longer are not supported and
cause the session to be closed.
There is a possibility for misbehavior. If `USE mytable` is executed in one of
the slaves and fails, it may be due to replication lag rather than the database
not existing. Thus, the same command may produce different result in different

View File

@ -681,6 +681,14 @@ add_test_executable(mxs1786_statistics.cpp mxs1786_statistics replication LABELS
# https://jira.mariadb.org/browse/MXS-1787
add_test_executable(mxs1787_call_ps.cpp mxs1787_call_ps replication LABELS readwritesplit REPL_BACKEND)
# MXS-1804: request 16M-1 stmt_prepare command packet connect hang
# https://jira.mariadb.org/browse/MXS-1804
add_test_executable(mxs1804_long_ps_hang.cpp mxs1804_long_ps_hang replication LABELS readwritesplit REPL_BACKEND)
# MXS-1808: Crash with mysql_stmt_send_long_data
# https://jira.mariadb.org/browse/MXS-1808
add_test_executable(mxs1808_long_data.cpp mxs1808_long_data replication LABELS readwritesplit REPL_BACKEND)
# 'namedserverfilter' test
add_test_executable(namedserverfilter.cpp namedserverfilter namedserverfilter LABELS namedserverfilter LIGHT REPL_BACKEND)

View File

@ -0,0 +1,41 @@
/**
* MXS-1804: request 16M-1 stmt_prepare command packet connect hang
*
* https://jira.mariadb.org/browse/MXS-1804
*/
#include "testconnections.h"
int sql_str_size(int sqlsize)
{
char prefx[] = "select ''";
return sqlsize - strlen(prefx) - 1;
}
void gen_select_sqlstr(char *sqlstr, unsigned int strsize, int sqlsize)
{
strcpy(sqlstr, "select '");
memset(sqlstr + strlen("select '"), 'f', strsize);
sqlstr[sqlsize - 2] = '\'';
sqlstr[sqlsize - 1] = '\0';
}
int main(int argc, char** argv)
{
TestConnections test(argc, argv);
int sqlsize = 16777215;
int strsize = sql_str_size(sqlsize);
char* sqlstr = (char *)malloc(sqlsize);
gen_select_sqlstr(sqlstr, strsize, sqlsize);
test.set_timeout(30);
test.maxscales->connect();
MYSQL_STMT* stmt = mysql_stmt_init(test.maxscales->conn_rwsplit[0]);
test.assert(mysql_stmt_prepare(stmt, sqlstr, strlen(sqlstr)) != 0, "Prepare should fail in 2.2 but not hang",
mysql_stmt_error(stmt));
mysql_stmt_close(stmt);
return test.global_result;
}

View File

@ -0,0 +1,86 @@
#include "testconnections.h"
#include <iostream>
using namespace std;
void print_stmt_error(MYSQL_STMT *stmt, const char* msg)
{
cout << "Error: " << msg << ": " << mysql_stmt_error(stmt) << endl;
}
static int test_long_data(MYSQL *conn, int sqlsize)
{
int data1size = sqlsize / 2;
char * data1 = (char*) malloc(data1size);
memset(data1, 97, data1size);
char * data3 = (char*) malloc(sqlsize);
memset(data3, 99, sqlsize);
MYSQL_STMT *stmt;
stmt = mysql_stmt_init(conn);
int rc, int_data;
MYSQL_RES *result;
MYSQL_BIND my_bind[1];
rc = mysql_autocommit(conn, 1);
if (NULL == stmt)
{
fprintf(stderr, "%s", mysql_error(conn));
return 0;
}
if (mysql_stmt_prepare(stmt, "select ?", strlen("select ?")) != 0)
{
print_stmt_error(stmt, "stmt prepare fail");
return 0;
}
memset((char*) my_bind, 0, sizeof(my_bind));
my_bind[0].buffer = (void *)&int_data;
my_bind[0].buffer_type = MYSQL_TYPE_STRING;
if (mysql_stmt_bind_param(stmt, my_bind) != 0)
{
print_stmt_error(stmt, "bind param error");
return 0;
}
/* supply data in pieces */
if (mysql_stmt_send_long_data(stmt, 0, data1, data1size) != 0)
{
print_stmt_error(stmt, "send long data1 failed");
return 0;
}
if (mysql_stmt_send_long_data(stmt, 0, data3, sqlsize) != 0)
{
print_stmt_error(stmt, "send long data3 failed");
return 0;
}
/* execute */
if (mysql_stmt_execute(stmt) != 0)
{
print_stmt_error(stmt, "execute prepare stmt failed");
return 0;
}
/* get the result */
result = mysql_store_result(conn);
mysql_free_result(result);
mysql_stmt_close(stmt);
free(data1);
free(data3);
return 1;
}
int main(int argc, char** argv)
{
TestConnections test(argc, argv);
test.maxscales->connect();
test.assert(test_long_data(test.maxscales->conn_rwsplit[0], 123456), "Test should work");
test.maxscales->disconnect();
return test.global_result;
}

View File

@ -78,11 +78,11 @@ bool binlog_next_file_exists(const char* binlogdir, const char* binlog)
{
char buf[BLRM_BINLOG_NAME_STR_LEN + 1];
char filename[PATH_MAX + 1];
char next_file[BLRM_BINLOG_NAME_STR_LEN + 1];
char next_file[BLRM_BINLOG_NAME_STR_LEN + 1 + 20];
int offset = sptr - binlog;
memcpy(buf, binlog, offset);
buf[offset] = '\0';
sprintf(next_file, BINLOG_NAMEFMT, buf, filenum);
snprintf(next_file, sizeof(next_file), BINLOG_NAMEFMT, buf, filenum);
snprintf(filename, PATH_MAX, "%s/%s", binlogdir, next_file);
filename[PATH_MAX] = '\0';

View File

@ -240,7 +240,7 @@ typedef struct start_encryption_event
int
blr_file_init(ROUTER_INSTANCE *router)
{
char path[PATH_MAX + 1] = "";
char path[PATH_MAX + 1 - BINLOG_FILE_EXTRA_INFO - BINLOG_FNAMELEN - 2] = "";
char filename[PATH_MAX + 1] = "";
int file_found, n = 1;
int root_len, i;
@ -262,9 +262,7 @@ blr_file_init(ROUTER_INSTANCE *router)
return 0;
}
strcpy(path, datadir);
strcat(path, "/");
strcat(path, router->service->name);
snprintf(path, sizeof(path), "%s/%s", datadir, router->service->name);
if (access(path, R_OK) == -1)
{

View File

@ -7047,9 +7047,9 @@ static bool blr_slave_gtid_request(ROUTER_INSTANCE *router,
SQLITE_OPEN_READONLY,
NULL) != SQLITE_OK)
{
char errmsg[BINLOG_ERROR_MSG_LEN + 1];
char errmsg[BINLOG_ERROR_MSG_LEN + sizeof(dbpath) + 1];
snprintf(errmsg,
BINLOG_ERROR_MSG_LEN,
sizeof(errmsg),
"Slave %lu: failed to open GTID maps db '%s': %s",
(unsigned long)slave->serverid,
dbpath,
@ -8180,7 +8180,7 @@ static bool blr_handle_admin_stmt(ROUTER_INSTANCE *router,
else
{
int rc;
char error_string[BINLOG_ERROR_MSG_LEN + 1] = "";
char error_string[BINLOG_ERROR_MSG_LEN + 1 + BINLOG_ERROR_MSG_LEN + 1] = "";
MASTER_SERVER_CFG *current_master = NULL;
current_master = (MASTER_SERVER_CFG *)MXS_CALLOC(1, sizeof(MASTER_SERVER_CFG));
@ -8228,7 +8228,7 @@ static bool blr_handle_admin_stmt(ROUTER_INSTANCE *router,
spinlock_release(&router->lock);
snprintf(error_string, BINLOG_ERROR_MSG_LEN,
snprintf(error_string, sizeof(error_string),
"Error writing into %s/master.ini: %s",
router->binlogdir,
error);

View File

@ -192,7 +192,13 @@ bool RWSplitSession::route_single_stmt(GWBUF *querybuf)
if ((target = handle_slave_is_target(command, stmt_id)))
{
succp = true;
store_stmt = m_config.retry_failed_reads;
if (m_config.retry_failed_reads &&
(command == MXS_COM_QUERY || command == MXS_COM_STMT_EXECUTE))
{
// Only commands that can contain an SQL statement should be stored
store_stmt = true;
}
}
}
else if (TARGET_IS_MASTER(route_target))
@ -258,6 +264,18 @@ bool RWSplitSession::route_single_stmt(GWBUF *querybuf)
return succp;
}
static inline bool is_large_query(GWBUF* buf)
{
uint32_t buflen = gwbuf_length(buf);
// The buffer should contain at most (2^24 - 1) + 4 bytes ...
ss_dassert(buflen <= MYSQL_HEADER_LEN + GW_MYSQL_MAX_PACKET_LEN);
// ... and the payload should be buflen - 4 bytes
ss_dassert(MYSQL_GET_PAYLOAD_LEN(GWBUF_DATA(buf)) == buflen - MYSQL_HEADER_LEN);
return buflen == MYSQL_HEADER_LEN + GW_MYSQL_MAX_PACKET_LEN;
}
/**
* Purge session command history
*
@ -326,6 +344,13 @@ void RWSplitSession::purge_history(mxs::SSessionCommand& sescmd)
*/
bool RWSplitSession::route_session_write(GWBUF *querybuf, uint8_t command, uint32_t type)
{
if (is_large_query(querybuf))
{
MXS_ERROR("Session command is too large, session cannot continue. "
"Large session commands are not supported in 2.2.");
return false;
}
/** The SessionCommand takes ownership of the buffer */
uint64_t id = m_sescmd_count++;
mxs::SSessionCommand sescmd(new mxs::SessionCommand(querybuf, id));
@ -848,18 +873,6 @@ bool RWSplitSession::handle_master_is_target(SRWBackend* dest)
return succp;
}
static inline bool is_large_query(GWBUF* buf)
{
uint32_t buflen = gwbuf_length(buf);
// The buffer should contain at most (2^24 - 1) + 4 bytes ...
ss_dassert(buflen <= MYSQL_HEADER_LEN + GW_MYSQL_MAX_PACKET_LEN);
// ... and the payload should be buflen - 4 bytes
ss_dassert(MYSQL_GET_PAYLOAD_LEN(GWBUF_DATA(buf)) == buflen - MYSQL_HEADER_LEN);
return buflen == MYSQL_HEADER_LEN + GW_MYSQL_MAX_PACKET_LEN;
}
/*
* Add a wait gitd query in front of user's query to achive causal read;
*