MXS-1475 Allow the setting on N vars with one stmt

With this change, the following will be possible.

SET @maxscale.cache.populate=false, @maxscale.cache.use=true;
This commit is contained in:
Johan Wikman
2018-03-08 14:18:36 +02:00
parent 805e3578a2
commit 34dd8a52bb
4 changed files with 345 additions and 131 deletions

View File

@ -21,6 +21,7 @@
#include <netinet/tcp.h>
#include <sys/stat.h>
#include <string>
#include <vector>
#include <maxscale/alloc.h>
#include <maxscale/authenticator.h>
@ -899,31 +900,55 @@ char* handle_variables(MXS_SESSION* session, GWBUF** read_buffer)
{
SqlModeParser sql_mode_parser;
switch (sql_mode_parser.get_sql_mode(result.value_begin(), result.value_end()))
const SetParser::Result::Items& values = result.values();
for (SetParser::Result::Items::const_iterator i = values.begin(); i != values.end(); ++i)
{
case SqlModeParser::ORACLE:
session_set_autocommit(session, false);
session->client_protocol_data = QC_SQL_MODE_ORACLE;
break;
const SetParser::Result::Item& value = *i;
case SqlModeParser::DEFAULT:
session_set_autocommit(session, true);
session->client_protocol_data = QC_SQL_MODE_DEFAULT;
break;
switch (sql_mode_parser.get_sql_mode(value.first, value.second))
{
case SqlModeParser::ORACLE:
session_set_autocommit(session, false);
session->client_protocol_data = QC_SQL_MODE_ORACLE;
break;
case SqlModeParser::SOMETHING:
break;
case SqlModeParser::DEFAULT:
session_set_autocommit(session, true);
session->client_protocol_data = QC_SQL_MODE_DEFAULT;
break;
default:
ss_dassert(!true);
case SqlModeParser::SOMETHING:
break;
default:
ss_dassert(!true);
}
}
}
break;
case SetParser::IS_SET_MAXSCALE:
message = session_set_variable_value(session,
result.variable_begin(), result.variable_end(),
result.value_begin(), result.value_end());
{
const SetParser::Result::Items& variables = result.variables();
const SetParser::Result::Items& values = result.values();
SetParser::Result::Items::const_iterator i = variables.begin();
SetParser::Result::Items::const_iterator j = values.begin();
while (!message && (i != variables.end()))
{
const SetParser::Result::Item& variable = *i;
const SetParser::Result::Item& value = *j;
message = session_set_variable_value(session,
variable.first, variable.second,
value.first, value.second);
++i;
++j;
}
}
break;
case SetParser::NOT_RELEVANT:

View File

@ -13,6 +13,7 @@
*/
#include <maxscale/cppdefs.hh>
#include <vector>
#include <maxscale/customparser.hh>
#include <maxscale/protocol/mysql.h>
@ -47,28 +48,28 @@ public:
class Result
{
public:
typedef std::pair<const char*, const char*> Item;
typedef std::vector<Item> Items;
Result()
: m_pVariable_begin(NULL)
, m_pVariable_end(NULL)
, m_pValue_begin(NULL)
, m_pValue_end(NULL)
{}
const char* variable_begin() const { return m_pVariable_begin; }
const char* variable_end() const { return m_pVariable_end; }
const char* value_begin() const { return m_pValue_begin; }
const char* value_end() const { return m_pValue_end; }
const Items& variables() const { return m_variables; }
const Items& values() const { return m_values; }
void set_variable_begin(const char* pVariable_begin) { m_pVariable_begin = pVariable_begin; }
void set_variable_end(const char* pVariable_end) { m_pVariable_end = pVariable_end; }
void set_value_begin(const char* pValue_begin) { m_pValue_begin = pValue_begin; }
void set_value_end(const char* pValue_end) { m_pValue_end = pValue_end; }
void add_variable(const char *begin, const char* end)
{
m_variables.push_back(Item(begin, end));
}
void add_value(const char* begin, const char* end)
{
m_values.push_back(Item(begin, end));
}
private:
const char* m_pVariable_begin;
const char* m_pVariable_end;
const char* m_pValue_begin;
const char* m_pValue_end;
std::vector<Item> m_variables;
std::vector<Item> m_values;
};
/**
@ -270,7 +271,8 @@ private:
++m_pI;
while ((m_pI < m_pEnd) && (is_alpha(*m_pI) || is_number(*m_pI) || (*m_pI == '.') || (*m_pI == '_')))
while ((m_pI < m_pEnd) &&
(is_alpha(*m_pI) || is_number(*m_pI) || (*m_pI == '.') || (*m_pI == '_')))
{
++m_pI;
}
@ -385,8 +387,7 @@ private:
if (next_token() == '=')
{
pResult->set_variable_begin(pVariable_begin);
pResult->set_variable_end(pVariable_end);
pResult->add_variable(pVariable_begin, pVariable_end);
bypass_whitespace();
@ -395,8 +396,7 @@ private:
consume_value(&pValue_end);
pResult->set_value_begin(pValue_begin);
pResult->set_value_end(pValue_end);
pResult->add_value(pValue_begin, pValue_end);
rv = IS_SET_SQL_MODE;
}
@ -417,8 +417,7 @@ private:
if (next_token() == '=')
{
pResult->set_variable_begin(pVariable_begin);
pResult->set_variable_end(pVariable_end);
pResult->add_variable(pVariable_begin, pVariable_end);
bypass_whitespace();
@ -427,8 +426,7 @@ private:
consume_value(&pValue_end);
pResult->set_value_begin(pValue_begin);
pResult->set_value_end(pValue_end);
pResult->add_value(pValue_begin, pValue_end);
rv = IS_SET_MAXSCALE;
}

View File

@ -52,183 +52,328 @@ struct TEST_CASE
{
const char* zStmt;
SetParser::status_t status;
const char* zVariable;
const char* zValue;
struct EXPECTATION
{
const char* zVariable;
const char* zValue;
} expectations[10];
} test_cases[] =
{
{
"SET SQL_MODE=DEFAULT",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"DEFAULT"
{
{
"SQL_MODE",
"DEFAULT"
},
{ NULL, NULL }
}
},
{
"SET SQL_MODE=DEFAULT;",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"DEFAULT"
{
{
"SQL_MODE",
"DEFAULT"
},
{ NULL, NULL }
}
},
{
"SET SQL_MODE=DEFAULT; ",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"DEFAULT"
{
{
"SQL_MODE",
"DEFAULT"
},
{ NULL, NULL }
}
},
{
"-- This is a comment\nSET SQL_MODE=DEFAULT",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"DEFAULT"
{
{
"SQL_MODE",
"DEFAULT"
},
{ NULL, NULL }
}
},
{
"#This is a comment\nSET SQL_MODE=DEFAULT",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"DEFAULT"
{
{
"SQL_MODE",
"DEFAULT"
},
{ NULL, NULL }
}
},
{
"/*blah*/ SET /*blah*/ SQL_MODE /*blah*/ = /*blah*/ DEFAULT /*blah*/ ",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"DEFAULT"
{
{
"SQL_MODE",
"DEFAULT"
},
{ NULL, NULL }
}
},
{
"SET SQL_MODE=ORACLE",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"ORACLE"
{
{
"SQL_MODE",
"ORACLE"
},
{ NULL, NULL }
}
},
{
"SET SQL_MODE=BLAH", // So short that it cannot be DEFAULT|ORACLE
P::IS_SET_SQL_MODE,
"SQL_MODE",
"BLAH"
{
{
"SQL_MODE",
"BLAH"
},
{ NULL, NULL }
}
},
{
"SET SQL_MODE='BLAH'",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"'BLAH'"
{
{
"SQL_MODE",
"'BLAH'"
},
{ NULL, NULL }
}
},
{
"SET SQL_MODE=BLAHBLAH",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"BLAHBLAH"
{
{
"SQL_MODE",
"BLAHBLAH"
},
{ NULL, NULL }
}
},
{
"SET SQL_MODE='ORACLE'",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"'ORACLE'"
{
{
"SQL_MODE",
"'ORACLE'"
},
{ NULL, NULL }
}
},
{
"SET SQL_MODE='BLAH, A, B, ORACLE'",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"'BLAH, A, B, ORACLE'"
{
{
"SQL_MODE",
"'BLAH, A, B, ORACLE'"
},
{ NULL, NULL }
}
},
{
"SET SQL_MODE='BLAH, A, B, XYZ_123'",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"'BLAH, A, B, XYZ_123'"
{
{
"SQL_MODE",
"'BLAH, A, B, XYZ_123'"
},
{ NULL, NULL }
}
},
{
"SET VAR1=1234, VAR2=3456, SQL_MODE='A,B, ORACLE'",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"'A,B, ORACLE'"
{
{
"SQL_MODE",
"'A,B, ORACLE'"
},
{ NULL, NULL }
}
},
{
"SET SQL_MODE=ORACLE, VAR1=3456, VAR2='A=b, c=d', SQL_MODE='A,B, ORACLE'",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"ORACLE"
{
{
"SQL_MODE",
"ORACLE"
},
{ NULL, NULL }
}
},
{
"SET GLOBAL SQL_MODE=ORACLE",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"ORACLE"
{
{
"SQL_MODE",
"ORACLE"
},
{ NULL, NULL }
}
},
{
"SET SESSION SQL_MODE=ORACLE",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"ORACLE"
{
{
"SQL_MODE",
"ORACLE"
},
{ NULL, NULL }
}
},
{
"SET LOCAL SQL_MODE=ORACLE",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"ORACLE"
{
{
"SQL_MODE",
"ORACLE"
},
{ NULL, NULL }
}
},
{
"SET @@GLOBAL.SQL_MODE=ORACLE",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"ORACLE"
{
{
"SQL_MODE",
"ORACLE"
},
{ NULL, NULL }
}
},
{
"SET @@SESSION.SQL_MODE=ORACLE",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"ORACLE"
{
{
"SQL_MODE",
"ORACLE"
},
{ NULL, NULL }
}
},
{
"SET @@LOCAL.SQL_MODE=ORACLE",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"ORACLE"
{
{
"SQL_MODE",
"ORACLE"
},
{ NULL, NULL }
}
},
{
"SET @@LOCAL . SQL_MODE = ORACLE",
P::IS_SET_SQL_MODE,
"SQL_MODE",
"ORACLE"
{
{
"SQL_MODE",
"ORACLE"
},
{ NULL, NULL }
}
},
{
"SET @@SESSION.blah = 1234, @@GLOBAL.blahblah = something, sql_mode=ORACLE",
P::IS_SET_SQL_MODE,
"sql_mode",
"ORACLE"
{
{
"sql_mode",
"ORACLE"
},
{ NULL, NULL }
}
},
{
"SET MAXSCALE=",
P::NOT_RELEVANT,
NULL,
NULL
{
{ NULL, NULL }
}
},
{
"SET MAXSCALE.CACHE.ENABLED=TRUE",
P::NOT_RELEVANT,
NULL,
NULL
{
{ NULL, NULL }
}
},
{
"SET @MAXSCALE.CACHE.ENABLED=TRUE",
P::IS_SET_MAXSCALE,
"@MAXSCALE.CACHE.ENABLED",
"TRUE"
{
{
"@MAXSCALE.CACHE.ENABLED",
"TRUE"
},
{ NULL, NULL }
}
},
{
"SET @MAXSCALE.CACHE.ENABLED = TRUE /*blah*/",
P::IS_SET_MAXSCALE,
"@MAXSCALE.CACHE.ENABLED",
"TRUE"
{
{
"@MAXSCALE.CACHE.ENABLED",
"TRUE"
},
{ NULL, NULL }
}
},
{
"SET @MAXSCALE.CACHE.ENABLED = TRUE, @maxscale.cache.enabled = FALSE",
P::IS_SET_MAXSCALE,
"@maxscale.cache.enabled",
"FALSE"
{
{
"@MAXSCALE.CACHE.ENABLED",
"TRUE"
},
{
"@maxscale.cache.enabled",
"FALSE"
},
{ NULL, NULL }
}
},
};
const int N_TEST_CASES = sizeof(test_cases) / sizeof(test_cases[0]);
int test(GWBUF** ppStmt, SetParser::status_t expected_status, const char* zVariable, const char* zValue)
int test(GWBUF** ppStmt, SetParser::status_t expected_status, const TEST_CASE::EXPECTATION expectations[])
{
int rv = EXIT_SUCCESS;
@ -241,33 +386,72 @@ int test(GWBUF** ppStmt, SetParser::status_t expected_status, const char* zVaria
{
if ((status != SetParser::ERROR) && (status != SetParser::NOT_RELEVANT))
{
size_t l1 = result.variable_end() - result.variable_begin();
const SetParser::Result::Items& variables = result.variables();
const SetParser::Result::Items& values = result.values();
ss_dassert(variables.size() == values.size());
if ((l1 == strlen(zVariable)) && (memcmp(result.variable_begin(), zVariable, l1) == 0))
SetParser::Result::Items::const_iterator i = variables.begin();
SetParser::Result::Items::const_iterator j = values.begin();
int k = 0;
while (i != variables.end())
{
size_t l2 = result.value_end() - result.value_begin();
const SetParser::Result::Item& variable = *i;
const SetParser::Result::Item& value = *j;
const TEST_CASE::EXPECTATION& expectation = expectations[k];
if ((l2 == strlen(zValue)) && (memcmp(result.value_begin(), zValue, l2) == 0))
if (expectation.zVariable)
{
cout << "OK";
size_t l1 = variable.second - variable.first;
if ((l1 == strlen(expectation.zVariable)) &&
(memcmp(variable.first, expectation.zVariable, l1) == 0))
{
size_t l2 = value.second - value.first;
if ((l2 == strlen(expectation.zValue)) &&
(memcmp(value.first, expectation.zValue, l2) == 0))
{
cout << "OK";
}
else
{
cout << "ERROR: Expected value "
<< "'" << expectation.zValue << "'"
<< ", got '";
cout.write(value.first, l2);
cout << "'.";
rv = EXIT_FAILURE;
}
}
else
{
cout << "ERROR: Expected variable "
<< "'" << expectation.zVariable << "'"
<< ", got '";
cout.write(variable.first, l1);
cout << "'.";
rv = EXIT_FAILURE;
}
}
else
{
cout << "ERROR: Expected value "
<< "'" << zValue << "'"
<< ", got '";
cout.write(result.value_begin(), l2);
cout << "'.";
cout << "ERROR: Nothing expected for variable '";
cout.write(variable.first, variable.second - variable.first);
cout << "'";
rv = EXIT_FAILURE;
}
++i;
++j;
++k;
}
else
if (expectations[k].zVariable)
{
cout << "ERROR: Expected variable "
<< "'" << zVariable << "'"
<< ", got '";
cout.write(result.variable_begin(), l1);
cout << "'.";
cout << "ERROR: "
<< expectations[k].zVariable << " = " << expectations[k].zValue
<< " not reported.";
rv = EXIT_FAILURE;
}
}
@ -300,7 +484,7 @@ int test(const TEST_CASE& test_case)
GWBUF* pStmt = gwbuf_create_com_query(test_case.zStmt);
ss_dassert(pStmt);
rv = test(&pStmt, test_case.status, test_case.zVariable, test_case.zValue);
rv = test(&pStmt, test_case.status, test_case.expectations);
gwbuf_free(pStmt);
@ -362,7 +546,7 @@ int test_non_contiguous()
cout << "): " << flush;
if (test(&pStmt, test_case.status, test_case.zVariable, test_case.zValue) == EXIT_FAILURE)
if (test(&pStmt, test_case.status, test_case.expectations) == EXIT_FAILURE)
{
rv = EXIT_FAILURE;
}

View File

@ -191,21 +191,28 @@ int test(GWBUF** ppStmt,
{
if (status == SetParser::IS_SET_SQL_MODE)
{
SqlModeParser sql_mode_parser;
SqlModeParser::sql_mode_t sql_mode = sql_mode_parser.get_sql_mode(result.value_begin(), result.value_end());
const SetParser::Result::Items& values = result.values();
if (sql_mode == expected_sql_mode)
for (SetParser::Result::Items::const_iterator i = values.begin(); i != values.end(); ++i)
{
cout << "OK";
}
else
{
cout << "ERROR: Expected "
<< "'" << SqlModeParser::to_string(expected_sql_mode) << "'"
<< ", got "
<< "'" << SqlModeParser::to_string(sql_mode) << "'"
<< ".";
rv = EXIT_FAILURE;
const SetParser::Result::Item& item = *i;
SqlModeParser sql_mode_parser;
SqlModeParser::sql_mode_t sql_mode = sql_mode_parser.get_sql_mode(item.first, item.second);
if (sql_mode == expected_sql_mode)
{
cout << "OK";
}
else
{
cout << "ERROR: Expected "
<< "'" << SqlModeParser::to_string(expected_sql_mode) << "'"
<< ", got "
<< "'" << SqlModeParser::to_string(sql_mode) << "'"
<< ".";
rv = EXIT_FAILURE;
}
}
}
else