MXS-1776: Handle recursive COM_STMT_EXECUTE commands

Readwritesplit would not handle multiple overlapping COM_STMT_EXECUTE
commands properly if they opened cursors. This was due to the fact that
the result would not be marked as complete and COM_STMT_FETCH commands
were executed as if they did not return results.

The correct implementation is to consider a COM_STMT_EXECUTE that opens a
cursor complete only when the first EOF packet is read (that is, when the
resultset header is read). This allows subsequent COM_STMT_FETCH commands
to be handled separately.

The separate COM_STMT_FETCH handling must count the number of packets that
are being fetched. This allows correct tracking of the state of a
COM_STMT_FETCH by checking that the number of packets is correct or the
second EOF/ERR packet is read.
This commit is contained in:
Markus Mäkelä
2018-04-11 15:16:22 +03:00
parent 252475cdc5
commit 311adf817f
6 changed files with 79 additions and 5 deletions

View File

@ -17,7 +17,9 @@
RWBackend::RWBackend(SERVER_REF* ref):
mxs::Backend(ref),
m_reply_state(REPLY_STATE_DONE),
m_large_packet(false)
m_large_packet(false),
m_open_cursor(false),
m_expected_rows(0)
{
}
@ -72,12 +74,40 @@ bool RWBackend::write(GWBUF* buffer, response_type type)
/** Replace the client handle with the real PS handle */
uint8_t* ptr = GWBUF_DATA(buffer) + MYSQL_PS_ID_OFFSET;
gw_mysql_set_byte4(ptr, it->second);
uint8_t buf[4];
if (cmd == MXS_COM_STMT_EXECUTE &&
// Skip header and command byte for the 4 byte ID
gwbuf_copy_data(buffer, 5, 4, buf) == sizeof(buf) &&
// A flag value of 0 is no cursor
gw_mysql_get_byte4(buf) != 0)
{
m_open_cursor = true;
}
else if (cmd == MXS_COM_STMT_FETCH)
{
ss_dassert(m_open_cursor);
// Number of rows to fetch is a 4 byte integer after the ID
gwbuf_copy_data(buffer, 5 + 4, 4, buf);
m_expected_rows = gw_mysql_get_byte4(buf);
}
else
{
m_open_cursor = false;
}
}
}
return mxs::Backend::write(buffer);
}
bool RWBackend::consume_fetched_rows(GWBUF* buffer)
{
m_expected_rows -= modutil_count_packets(buffer);
ss_dassert(m_expected_rows >= 0);
return m_expected_rows == 0;
}
uint32_t get_internal_ps_id(RWSplitSession* rses, GWBUF* buffer)
{
uint32_t rval = 0;