qc_mysqlembedded: Replace '?':s with '0':s

The embedded parser is not capable of parsing preparable statements
containing ?:s as ordinary statements, so they have to be replaced
with something else. Replacing them with a dummy variable cannot be
done as that will affect the type of the statement. Replacing with
0 seems to be a good compromise.
This commit is contained in:
Johan Wikman
2017-01-17 10:41:51 +02:00
parent a40692d81d
commit 842e8440d4

View File

@ -1790,56 +1790,38 @@ int32_t qc_mysql_get_preparable_stmt(GWBUF* stmt, GWBUF** preparable_stmt)
if (!pi->preparable_stmt) if (!pi->preparable_stmt)
{ {
// This is terriby inefficient, but as qc_mysqlembedded is not used const char* preparable_stmt = lex->prepared_stmt_code.str;
// for anything else but comparisons it is ok. size_t preparable_stmt_len = lex->prepared_stmt_code.length;
const char* preparable_str = lex->prepared_stmt_code.str;
size_t preparable_str_len = lex->prepared_stmt_code.length;
// MySQL does not parse e.g. "select * from x where ?=5". To work
// around that we'll replace all "?":s with "@a":s. We might replace
// something unnecessarily, but that won't hurt the classification.
size_t n_questions = 0;
const char* p = preparable_str;
while (p < preparable_str + preparable_str_len)
{
if (*p == '?')
{
++n_questions;
}
++p;
}
size_t preparable_stmt_len = preparable_str_len + n_questions * 2;
size_t payload_len = preparable_stmt_len + 1; size_t payload_len = preparable_stmt_len + 1;
size_t packet_len = MYSQL_HEADER_LEN + payload_len; size_t packet_len = MYSQL_HEADER_LEN + payload_len;
GWBUF* preperable_stmt = gwbuf_alloc(packet_len); GWBUF* preperable_packet = gwbuf_alloc(packet_len);
if (preperable_stmt) if (preperable_packet)
{ {
// Encode the length of the payload in the 3 first bytes. // Encode the length of the payload in the 3 first bytes.
*((unsigned char*)GWBUF_DATA(preperable_stmt) + 0) = payload_len; *((unsigned char*)GWBUF_DATA(preperable_packet) + 0) = payload_len;
*((unsigned char*)GWBUF_DATA(preperable_stmt) + 1) = (payload_len >> 8); *((unsigned char*)GWBUF_DATA(preperable_packet) + 1) = (payload_len >> 8);
*((unsigned char*)GWBUF_DATA(preperable_stmt) + 2) = (payload_len >> 16); *((unsigned char*)GWBUF_DATA(preperable_packet) + 2) = (payload_len >> 16);
// Sequence id // Sequence id
*((unsigned char*)GWBUF_DATA(preperable_stmt) + 3) = 0x00; *((unsigned char*)GWBUF_DATA(preperable_packet) + 3) = 0x00;
// Payload, starts with command. // Payload, starts with command.
*((unsigned char*)GWBUF_DATA(preperable_stmt) + 4) = COM_QUERY; *((unsigned char*)GWBUF_DATA(preperable_packet) + 4) = COM_QUERY;
// Is followed by the statement. // Is followed by the statement.
char *s = (char*)GWBUF_DATA(preperable_stmt) + 5; char *s = (char*)GWBUF_DATA(preperable_packet) + 5;
p = preparable_str;
while (p < preparable_str + preparable_str_len) // We copy the statment, blindly replacing all '?':s with '0':s as
// otherwise the parsing of the preparable statement as a regular
// statement will not always succeed.
const char* p = preparable_stmt;
while (p < preparable_stmt + preparable_stmt_len)
{ {
switch (*p) if (*p == '?')
{
*s = '0';
}
else
{ {
case '?':
*s++ = '@';
*s = 'a';
break;
default:
*s = *p; *s = *p;
} }
@ -1848,7 +1830,7 @@ int32_t qc_mysql_get_preparable_stmt(GWBUF* stmt, GWBUF** preparable_stmt)
} }
} }
pi->preparable_stmt = preperable_stmt; pi->preparable_stmt = preperable_packet;
} }
*preparable_stmt = pi->preparable_stmt; *preparable_stmt = pi->preparable_stmt;