Complementary fix to #694,http://bugs.mariadb.com/show_bug.cgi?id=694
RWSplit router handles query processing errors that happened in router by sending parse error reply to client. routeQuery fails only when backend has failed.
This commit is contained in:
@ -32,6 +32,19 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <mysql_client_server_protocol.h>
|
#include <mysql_client_server_protocol.h>
|
||||||
#include <modutil.h>
|
#include <modutil.h>
|
||||||
|
|
||||||
|
/** Defined in log_manager.cc */
|
||||||
|
extern int lm_enabled_logfiles_bitmask;
|
||||||
|
extern size_t log_ses_count[];
|
||||||
|
extern __thread log_info_t tls_log_info;
|
||||||
|
|
||||||
|
static void modutil_reply_routing_error(
|
||||||
|
DCB* backend_dcb,
|
||||||
|
int error,
|
||||||
|
char* state,
|
||||||
|
char* errstr);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a GWBUF structure is a MySQL COM_QUERY packet
|
* Check if a GWBUF structure is a MySQL COM_QUERY packet
|
||||||
*
|
*
|
||||||
@ -327,7 +340,7 @@ GWBUF *modutil_create_mysql_err_msg(
|
|||||||
const char *msg)
|
const char *msg)
|
||||||
{
|
{
|
||||||
uint8_t *outbuf = NULL;
|
uint8_t *outbuf = NULL;
|
||||||
uint32_t mysql_payload_size = 0;
|
uint32_t mysql_payload_size = 0;
|
||||||
uint8_t mysql_packet_header[4];
|
uint8_t mysql_packet_header[4];
|
||||||
uint8_t *mysql_payload = NULL;
|
uint8_t *mysql_payload = NULL;
|
||||||
uint8_t field_count = 0;
|
uint8_t field_count = 0;
|
||||||
@ -528,7 +541,7 @@ modutil_count_signal_packets(GWBUF *reply, int use_ok, int n_found)
|
|||||||
unsigned char* ptr = (unsigned char*) reply->start;
|
unsigned char* ptr = (unsigned char*) reply->start;
|
||||||
unsigned char* end = (unsigned char*) reply->end;
|
unsigned char* end = (unsigned char*) reply->end;
|
||||||
unsigned char* prev = ptr;
|
unsigned char* prev = ptr;
|
||||||
int pktlen, eof = 0, err = 0, found = n_found;
|
int pktlen, eof = 0, err = 0;
|
||||||
int errlen = 0, eoflen = 0;
|
int errlen = 0, eoflen = 0;
|
||||||
int iserr = 0, iseof = 0;
|
int iserr = 0, iseof = 0;
|
||||||
while(ptr < end)
|
while(ptr < end)
|
||||||
@ -583,3 +596,57 @@ modutil_count_signal_packets(GWBUF *reply, int use_ok, int n_found)
|
|||||||
|
|
||||||
return(eof + err);
|
return(eof + err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create parse error and EPOLLIN event to event queue of the backend DCB.
|
||||||
|
* When event is notified the error message is processed as error reply and routed
|
||||||
|
* upstream to client.
|
||||||
|
*
|
||||||
|
* @param backend_dcb DCB where event is added
|
||||||
|
* @param errstr Plain-text string error
|
||||||
|
*/
|
||||||
|
void modutil_reply_parse_error(
|
||||||
|
DCB* backend_dcb,
|
||||||
|
char* errstr)
|
||||||
|
{
|
||||||
|
CHK_DCB(backend_dcb);
|
||||||
|
modutil_reply_routing_error(backend_dcb, 1064, "42000", errstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create error message and EPOLLIN event to event queue of the backend DCB.
|
||||||
|
* When event is notified the message is processed as error reply and routed
|
||||||
|
* upstream to client.
|
||||||
|
*
|
||||||
|
* @param backend_dcb DCB where event is added
|
||||||
|
* @param error SQL error number
|
||||||
|
* @param state SQL state
|
||||||
|
* @param errstr Plain-text string error
|
||||||
|
*/
|
||||||
|
static void modutil_reply_routing_error(
|
||||||
|
DCB* backend_dcb,
|
||||||
|
int error,
|
||||||
|
char* state,
|
||||||
|
char* errstr)
|
||||||
|
{
|
||||||
|
GWBUF* buf;
|
||||||
|
CHK_DCB(backend_dcb);
|
||||||
|
|
||||||
|
buf = modutil_create_mysql_err_msg(1, 0, error, state, errstr);
|
||||||
|
free(errstr);
|
||||||
|
|
||||||
|
if (buf == NULL)
|
||||||
|
{
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
|
LOGFILE_ERROR,
|
||||||
|
"Error : Creating buffer for error message failed.")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/** Set flags that help router to identify session commands reply */
|
||||||
|
gwbuf_set_type(buf, GWBUF_TYPE_MYSQL);
|
||||||
|
gwbuf_set_type(buf, GWBUF_TYPE_SESCMD_RESPONSE);
|
||||||
|
gwbuf_set_type(buf, GWBUF_TYPE_RESPONSE_END);
|
||||||
|
/** Create an incoming event for backend DCB */
|
||||||
|
poll_add_epollin_event_to_dcb(backend_dcb, buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
@ -50,6 +50,7 @@ extern int modutil_send_mysql_err_packet(DCB *, int, int, int, const char *, con
|
|||||||
GWBUF* modutil_get_next_MySQL_packet(GWBUF** p_readbuf);
|
GWBUF* modutil_get_next_MySQL_packet(GWBUF** p_readbuf);
|
||||||
GWBUF* modutil_get_complete_packets(GWBUF** p_readbuf);
|
GWBUF* modutil_get_complete_packets(GWBUF** p_readbuf);
|
||||||
int modutil_MySQL_query_len(GWBUF* buf, int* nbytes_missing);
|
int modutil_MySQL_query_len(GWBUF* buf, int* nbytes_missing);
|
||||||
|
void modutil_reply_parse_error(DCB* backend_dcb, char* errstr);
|
||||||
|
|
||||||
GWBUF *modutil_create_mysql_err_msg(
|
GWBUF *modutil_create_mysql_err_msg(
|
||||||
int packet_number,
|
int packet_number,
|
||||||
|
|||||||
@ -43,6 +43,11 @@
|
|||||||
*/
|
*/
|
||||||
typedef void *ROUTER;
|
typedef void *ROUTER;
|
||||||
|
|
||||||
|
typedef enum error_action {
|
||||||
|
ERRACT_NEW_CONNECTION = 0x001,
|
||||||
|
ERRACT_REPLY_CLIENT = 0x002,
|
||||||
|
ERRACT_RESET = 0x004
|
||||||
|
} error_action_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @verbatim
|
* @verbatim
|
||||||
@ -66,13 +71,6 @@ typedef void *ROUTER;
|
|||||||
*
|
*
|
||||||
* @see load_module
|
* @see load_module
|
||||||
*/
|
*/
|
||||||
typedef enum error_action {
|
|
||||||
ERRACT_NEW_CONNECTION = 0x001,
|
|
||||||
ERRACT_REPLY_CLIENT = 0x002,
|
|
||||||
ERRACT_RESET = 0x004
|
|
||||||
} error_action_t;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct router_object {
|
typedef struct router_object {
|
||||||
ROUTER *(*createInstance)(SERVICE *service, char **options);
|
ROUTER *(*createInstance)(SERVICE *service, char **options);
|
||||||
void *(*newSession)(ROUTER *instance, SESSION *session);
|
void *(*newSession)(ROUTER *instance, SESSION *session);
|
||||||
|
|||||||
@ -358,7 +358,6 @@ static int gw_read_backend_event(DCB *dcb) {
|
|||||||
GWBUF_LENGTH(dcb->delayq))) != NULL);
|
GWBUF_LENGTH(dcb->delayq))) != NULL);
|
||||||
}
|
}
|
||||||
spinlock_release(&dcb->delayqlock);
|
spinlock_release(&dcb->delayqlock);
|
||||||
|
|
||||||
{
|
{
|
||||||
GWBUF* errbuf;
|
GWBUF* errbuf;
|
||||||
bool succp;
|
bool succp;
|
||||||
@ -1464,7 +1463,7 @@ static int gw_change_user(
|
|||||||
rv = 0;
|
rv = 0;
|
||||||
goto retblock;
|
goto retblock;
|
||||||
}
|
}
|
||||||
/** Set flags that help router to identify session commans reply */
|
/** Set flags that help router to identify session commands reply */
|
||||||
gwbuf_set_type(buf, GWBUF_TYPE_MYSQL);
|
gwbuf_set_type(buf, GWBUF_TYPE_MYSQL);
|
||||||
gwbuf_set_type(buf, GWBUF_TYPE_SESCMD_RESPONSE);
|
gwbuf_set_type(buf, GWBUF_TYPE_SESCMD_RESPONSE);
|
||||||
gwbuf_set_type(buf, GWBUF_TYPE_RESPONSE_END);
|
gwbuf_set_type(buf, GWBUF_TYPE_RESPONSE_END);
|
||||||
|
|||||||
@ -865,7 +865,7 @@ int gw_read_client_event(
|
|||||||
rc = SESSION_ROUTE_QUERY(session, read_buffer);
|
rc = SESSION_ROUTE_QUERY(session, read_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** succeed */
|
/** Routing succeed */
|
||||||
if (rc)
|
if (rc)
|
||||||
{
|
{
|
||||||
rc = 0; /**< here '0' means success */
|
rc = 0; /**< here '0' means success */
|
||||||
@ -874,25 +874,6 @@ int gw_read_client_event(
|
|||||||
{
|
{
|
||||||
bool succp;
|
bool succp;
|
||||||
GWBUF* errbuf;
|
GWBUF* errbuf;
|
||||||
|
|
||||||
/**
|
|
||||||
* Create error to be sent to client if session
|
|
||||||
* can't be continued.
|
|
||||||
*/
|
|
||||||
errbuf = mysql_create_custom_error(
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
"Routing query to backend failed. See "
|
|
||||||
"the error log for further details.");
|
|
||||||
|
|
||||||
router->handleError(
|
|
||||||
router_instance,
|
|
||||||
session->router_session,
|
|
||||||
errbuf,
|
|
||||||
dcb,
|
|
||||||
ERRACT_REPLY_CLIENT,
|
|
||||||
&succp);
|
|
||||||
free(errbuf);
|
|
||||||
/**
|
/**
|
||||||
* Create error to be sent to client if session
|
* Create error to be sent to client if session
|
||||||
* can't be continued.
|
* can't be continued.
|
||||||
|
|||||||
@ -1972,7 +1972,16 @@ retblock:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Routing function. Find out query type, backend type, and target DCB(s).
|
||||||
|
* Then route query to found target(s).
|
||||||
|
* @param inst router instance
|
||||||
|
* @param rses router session
|
||||||
|
* @param querybuf GWBUF including the query
|
||||||
|
*
|
||||||
|
* @return true if routing succeed or if it failed due to unsupported query.
|
||||||
|
* false if backend failure was encountered.
|
||||||
|
*/
|
||||||
static bool route_single_stmt(
|
static bool route_single_stmt(
|
||||||
ROUTER_INSTANCE* inst,
|
ROUTER_INSTANCE* inst,
|
||||||
ROUTER_CLIENT_SES* rses,
|
ROUTER_CLIENT_SES* rses,
|
||||||
@ -2162,6 +2171,8 @@ static bool route_single_stmt(
|
|||||||
if (TARGET_IS_MASTER(route_target) ||
|
if (TARGET_IS_MASTER(route_target) ||
|
||||||
TARGET_IS_SLAVE(route_target))
|
TARGET_IS_SLAVE(route_target))
|
||||||
{
|
{
|
||||||
|
backend_ref_t* bref = rses->rses_backend_ref;
|
||||||
|
|
||||||
char* query_str = modutil_get_query(querybuf);
|
char* query_str = modutil_get_query(querybuf);
|
||||||
char* qtype_str = skygw_get_qtype_str(qtype);
|
char* qtype_str = skygw_get_qtype_str(qtype);
|
||||||
|
|
||||||
@ -2181,9 +2192,22 @@ static bool route_single_stmt(
|
|||||||
"modification from other "
|
"modification from other "
|
||||||
"servers. <")));
|
"servers. <")));
|
||||||
|
|
||||||
|
while (bref != NULL && !BREF_IS_IN_USE(bref))
|
||||||
|
{
|
||||||
|
bref++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bref != NULL && BREF_IS_IN_USE(bref))
|
||||||
|
{
|
||||||
|
modutil_reply_parse_error(
|
||||||
|
bref->bref_dcb,
|
||||||
|
strdup("Routing query to backend failed. "
|
||||||
|
"See the error log for further "
|
||||||
|
"details."));
|
||||||
|
}
|
||||||
if (query_str) free (query_str);
|
if (query_str) free (query_str);
|
||||||
if (qtype_str) free(qtype_str);
|
if (qtype_str) free(qtype_str);
|
||||||
succp = false;
|
succp = true;
|
||||||
goto retblock;
|
goto retblock;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user