MXS-1896: Distinct LOAD DATA LOCAL INFILE from LOAD DATA INFILE

The two operations return different types of results and need to be
treated differently in order for them to be handled correctly in 2.2.

This fixes the unexpected internal state errors that happened in all 2.2
versions due to a wrong assumption made by readwritesplit. This fix is not
necessary for newer versions as the LOAD DATA LOCAL INFILE processing is
done with a simpler, and more robust, method.
This commit is contained in:
Markus Mäkelä 2018-06-02 22:24:51 +03:00
parent c88aa11e11
commit cddcc6d7d5
No known key found for this signature in database
GPG Key ID: 72D48FCE664F7B19
10 changed files with 57 additions and 14 deletions

View File

@ -104,6 +104,7 @@ typedef enum qc_query_op
QUERY_OP_EXPLAIN,
QUERY_OP_GRANT,
QUERY_OP_INSERT,
QUERY_OP_LOAD_LOCAL,
QUERY_OP_LOAD,
QUERY_OP_REVOKE,
QUERY_OP_SELECT,

View File

@ -658,6 +658,10 @@ add_test_executable(mxs1831_unknown_param.cpp mxs1831_unknown_param replication
# https://jira.mariadb.org/browse/MXS-1873
add_test_executable(mxs1873_large_sescmd.cpp mxs1873_large_sescmd replication LABELS readwritesplit REPL_BACKEND)
# MXS-1896: LOAD DATA INFILE is mistaken for LOAD DATA LOCAL INFILE
# https://jira.mariadb.org/browse/MXS-1896
add_test_executable(mxs1896_load_data_infile.cpp mxs1896_load_data_infile replication LABELS readwritesplit REPL_BACKEND)
# 'namedserverfilter' test
add_test_executable(namedserverfilter.cpp namedserverfilter namedserverfilter LABELS namedserverfilter LIGHT REPL_BACKEND)

View File

@ -0,0 +1,32 @@
/**
* MXS-1896: LOAD DATA INFILE is mistaken for LOAD DATA LOCAL INFILE
*
* https://jira.mariadb.org/browse/MXS-1896
*/
#include "testconnections.h"
int main(int argc, char** argv)
{
TestConnections test(argc, argv);
test.set_timeout(30);
test.maxscales->connect();
test.try_query(test.maxscales->conn_rwsplit[0], "DROP TABLE IF EXISTS test.t1");
test.try_query(test.maxscales->conn_rwsplit[0], "CREATE TABLE test.t1(id INT)");
test.try_query(test.maxscales->conn_rwsplit[0], "INSERT INTO test.t1 VALUES (1), (2), (3)");
test.try_query(test.maxscales->conn_rwsplit[0], "SELECT * FROM test.t1 INTO OUTFILE '/tmp/test.csv'");
test.try_query(test.maxscales->conn_rwsplit[0], "LOAD DATA INFILE '/tmp/test.csv' INTO TABLE test.t1");
test.try_query(test.maxscales->conn_rwsplit[0], "DROP TABLE test.t1");
test.maxscales->disconnect();
// Clean up the generated files
for (int i = 0; i < 4; i++)
{
test.repl->ssh_node_f(i, true, "rm -f /tmp/test.csv");
}
return test.global_result;
}

View File

@ -2001,7 +2001,7 @@ int32_t qc_mysql_get_operation(GWBUF* querybuf, int32_t* operation)
break;
case SQLCOM_LOAD:
*operation = QUERY_OP_LOAD;
*operation = QUERY_OP_LOAD_LOCAL;
break;
case SQLCOM_GRANT:

View File

@ -2327,13 +2327,13 @@ public:
exposed_sqlite3SrcListDelete(pParse->db, pFullName);
}
void maxscaleLoadData(Parse* pParse, SrcList* pFullName)
void maxscaleLoadData(Parse* pParse, SrcList* pFullName, int local)
{
ss_dassert(this_thread.initialized);
m_status = QC_QUERY_PARSED;
m_type_mask = QUERY_TYPE_WRITE;
m_operation = QUERY_OP_LOAD;
m_operation = local ? QUERY_OP_LOAD_LOCAL: QUERY_OP_LOAD;
if (pFullName)
{
@ -3299,7 +3299,7 @@ extern void maxscaleExecuteImmediate(Parse*, Token* pName, ExprSpan* pExprSpan,
extern void maxscaleExplain(Parse*, Token* pNext);
extern void maxscaleFlush(Parse*, Token* pWhat);
extern void maxscaleHandler(Parse*, mxs_handler_t, SrcList* pFullName, Token* pName);
extern void maxscaleLoadData(Parse*, SrcList* pFullName);
extern void maxscaleLoadData(Parse*, SrcList* pFullName, int local);
extern void maxscaleLock(Parse*, mxs_lock_t, SrcList*);
extern void maxscalePrepare(Parse*, Token* pName, Expr* pStmt);
extern void maxscalePrivileges(Parse*, int kind);
@ -4201,14 +4201,14 @@ void maxscaleHandler(Parse* pParse, mxs_handler_t type, SrcList* pFullName, Toke
QC_EXCEPTION_GUARD(pInfo->maxscaleHandler(pParse, type, pFullName, pName));
}
void maxscaleLoadData(Parse* pParse, SrcList* pFullName)
void maxscaleLoadData(Parse* pParse, SrcList* pFullName, int local)
{
QC_TRACE();
QcSqliteInfo* pInfo = this_thread.pInfo;
ss_dassert(pInfo);
QC_EXCEPTION_GUARD(pInfo->maxscaleLoadData(pParse, pFullName));
QC_EXCEPTION_GUARD(pInfo->maxscaleLoadData(pParse, pFullName, local));
}
void maxscaleLock(Parse* pParse, mxs_lock_t type, SrcList* pTables)

View File

@ -120,7 +120,7 @@ extern void maxscaleExecuteImmediate(Parse*, Token* pName, ExprSpan* pExprSpan,
extern void maxscaleExplain(Parse*, Token* pNext);
extern void maxscaleFlush(Parse*, Token* pWhat);
extern void maxscaleHandler(Parse*, mxs_handler_t, SrcList* pFullName, Token* pName);
extern void maxscaleLoadData(Parse*, SrcList* pFullName);
extern void maxscaleLoadData(Parse*, SrcList* pFullName, int local);
extern void maxscaleLock(Parse*, mxs_lock_t, SrcList*);
extern void maxscalePrepare(Parse*, Token* pName, Expr* pStmt);
extern void maxscalePrivileges(Parse*, int kind);
@ -2913,19 +2913,21 @@ handler ::= HANDLER nm(X) CLOSE. {
//////////////////////// The LOAD DATA INFILE statement ////////////////////////////////////
//
%type ld_local_opt {int}
cmd ::= load_data.
ld_priority_opt ::= .
ld_priority_opt ::= LOW_PRIORITY.
ld_priority_opt ::= CONCURRENT.
ld_local_opt ::= .
ld_local_opt ::= LOCAL.
ld_local_opt(A) ::= . {A = 0;}
ld_local_opt(A) ::= LOCAL. {A = 1;}
ld_charset_opt ::= .
ld_charset_opt ::= CHARACTER SET ids.
load_data ::= LOAD DATA ld_priority_opt ld_local_opt
load_data ::= LOAD DATA ld_priority_opt ld_local_opt(Y)
INFILE STRING ignore_or_replace_opt
INTO TABLE fullname(X)
/* ld_partition_opt */
@ -2935,7 +2937,7 @@ load_data ::= LOAD DATA ld_priority_opt ld_local_opt
/* ld_col_name_or_user_var_opt */
/* ld_set */.
{
maxscaleLoadData(pParse, X);
maxscaleLoadData(pParse, X, Y);
}
//////////////////////// The LOCK/UNLOCK statement ////////////////////////////////////

View File

@ -389,6 +389,9 @@ const char* qc_op_to_string(qc_query_op_t op)
case QUERY_OP_LOAD:
return "QUERY_OP_LOAD";
case QUERY_OP_LOAD_LOCAL:
return "QUERY_OP_LOAD_LOCAL";
case QUERY_OP_REVOKE:
return "QUERY_OP_REVOKE";

View File

@ -85,6 +85,7 @@ static inline fw_op_t qc_op_to_fw_op(qc_query_op_t op)
case QUERY_OP_INSERT:
return FW_OP_INSERT;
case QUERY_OP_LOAD_LOCAL:
case QUERY_OP_LOAD:
return FW_OP_LOAD;
@ -288,4 +289,4 @@ char* create_error(const char* format, ...);
*/
bool rule_matches(Dbfw* my_instance, DbfwSession* my_session,
GWBUF *queue, SRule rule, char* query);
bool rule_is_active(SRule rule);
bool rule_is_active(SRule rule);

View File

@ -881,7 +881,7 @@ handle_multi_temp_and_load(RWSplitSession *rses, GWBUF *querybuf,
else if (is_packet_a_query(packet_type))
{
qc_query_op_t queryop = qc_get_operation(querybuf);
if (queryop == QUERY_OP_LOAD)
if (queryop == QUERY_OP_LOAD_LOCAL)
{
rses->load_data_state = LOAD_DATA_START;
rses->rses_load_data_sent = 0;

View File

@ -424,7 +424,7 @@ int32_t SchemaRouterSession::routeQuery(GWBUF* pPacket)
/** We know where to route this query */
SSRBackend bref = get_bref_from_dcb(target_dcb);
if (op == QUERY_OP_LOAD)
if (op == QUERY_OP_LOAD_LOCAL)
{
m_load_target = bref->backend()->server;
}