MXS-1828: Simplify LOAD DATA LOCAL INFILE handling

By relying on the server to tell us that it is requesting the loading of a
local infile, we can remove one state from the state machine that governs
the loading of local files. It also removes the need to handle error and
success cases separately.

A side-effect of this change is that execution of multi-statement LOAD
DATA LOCAL INFILE no longer hangs. This is done by checking whether the
completion of one command initiates a new load.

The current code recursively checks the reply state and clones the
buffers. Neither of these are required nor should they be done but
refactoring the code is to be done in a separate commit.

Added two helper functions that are used to detect requests for local
infiles and to extract the total packet length from a non-contiguous
GWBUF.
This commit is contained in:
Markus Mäkelä
2018-05-16 17:41:55 +03:00
parent 35f2382263
commit 91cc5b1e89
8 changed files with 67 additions and 27 deletions

View File

@ -13,7 +13,8 @@ RWBackend::RWBackend(SERVER_REF* ref):
m_modutil_state({}),
m_command(0),
m_opening_cursor(false),
m_expected_rows(0)
m_expected_rows(0),
m_local_infile_requested(false)
{
}
@ -146,6 +147,8 @@ bool RWBackend::reply_is_complete(GWBUF *buffer)
else if (get_reply_state() == REPLY_STATE_START &&
(!mxs_mysql_is_result_set(buffer) || GWBUF_IS_COLLECTED_RESULT(buffer)))
{
m_local_infile_requested = false;
if (GWBUF_IS_COLLECTED_RESULT(buffer) ||
current_command() == MXS_COM_STMT_PREPARE ||
!mxs_mysql_is_ok_packet(buffer) ||
@ -153,6 +156,11 @@ bool RWBackend::reply_is_complete(GWBUF *buffer)
{
/** Not a result set, we have the complete response */
set_reply_state(REPLY_STATE_DONE);
if (mxs_mysql_is_local_infile(buffer))
{
m_local_infile_requested = true;
}
}
else
{
@ -162,8 +170,12 @@ bool RWBackend::reply_is_complete(GWBUF *buffer)
if (have_next_packet(buffer))
{
set_reply_state(REPLY_STATE_RSET_COLDEF);
return reply_is_complete(buffer);
// TODO: Don't clone the buffer
GWBUF* tmp = gwbuf_clone(buffer);
tmp = gwbuf_consume(tmp, mxs_mysql_get_packet_len(tmp));
bool rval = reply_is_complete(tmp);
gwbuf_free(tmp);
return rval;
}
}
}

View File

@ -76,6 +76,11 @@ public:
return m_command;
}
bool local_infile_requested() const
{
return m_local_infile_requested;
}
bool reply_is_complete(GWBUF *buffer);
private:
@ -85,6 +90,7 @@ private:
uint8_t m_command;
bool m_opening_cursor; /**< Whether we are opening a cursor */
uint32_t m_expected_rows; /**< Number of rows a COM_STMT_FETCH is retrieving */
bool m_local_infile_requested; /**< Whether a LOCAL INFILE was requested */
inline bool is_opening_cursor() const
{

View File

@ -983,13 +983,7 @@ bool RWSplitSession::handle_got_target(GWBUF* querybuf, SRWBackend& target, bool
target->set_reply_state(REPLY_STATE_START);
m_expected_responses++;
if (m_qc.load_data_state() == QueryClassifier::LOAD_DATA_START)
{
/** The first packet contains the actual query and the server
* will respond to it */
m_qc.set_load_data_state(QueryClassifier::LOAD_DATA_ACTIVE);
}
else if (m_qc.load_data_state() == QueryClassifier::LOAD_DATA_END)
if (m_qc.load_data_state() == QueryClassifier::LOAD_DATA_END)
{
/** The final packet in a LOAD DATA LOCAL INFILE is an empty packet
* to which the server responds with an OK or an ERR packet */

View File

@ -420,13 +420,6 @@ void RWSplitSession::clientReply(GWBUF *writebuf, DCB *backend_dcb)
SRWBackend& backend = get_backend_from_dcb(backend_dcb);
if (m_qc.load_data_state() == QueryClassifier::LOAD_DATA_ACTIVE &&
mxs_mysql_is_err_packet(writebuf))
{
// Server responded with an error to the LOAD DATA LOCAL INFILE
m_qc.set_load_data_state(QueryClassifier::LOAD_DATA_INACTIVE);
}
if ((writebuf = handle_causal_read_reply(writebuf, backend)) == NULL)
{
return; // Nothing to route, return
@ -475,6 +468,12 @@ void RWSplitSession::clientReply(GWBUF *writebuf, DCB *backend_dcb)
ss_dassert(m_expected_responses >= 0);
ss_dassert(backend->get_reply_state() == REPLY_STATE_DONE);
MXS_INFO("Reply complete, last reply from %s", backend->name());
if (backend->local_infile_requested())
{
// Server requested a local file, go into data streaming mode
m_qc.set_load_data_state(QueryClassifier::LOAD_DATA_ACTIVE);
}
}
else
{