diff --git a/server/core/modutil.c b/server/core/modutil.c index 33c302e7f..bcfbb0940 100644 --- a/server/core/modutil.c +++ b/server/core/modutil.c @@ -32,6 +32,20 @@ #include #include #include + +/** 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, + uint32_t flags); + + /** * Check if a GWBUF structure is a MySQL COM_QUERY packet * @@ -327,7 +341,7 @@ GWBUF *modutil_create_mysql_err_msg( const char *msg) { 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_payload = NULL; uint8_t field_count = 0; @@ -528,7 +542,7 @@ modutil_count_signal_packets(GWBUF *reply, int use_ok, int n_found) unsigned char* ptr = (unsigned char*) reply->start; unsigned char* end = (unsigned char*) reply->end; 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 iserr = 0, iseof = 0; while(ptr < end) @@ -583,3 +597,59 @@ modutil_count_signal_packets(GWBUF *reply, int use_ok, int n_found) 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 + * @param flags GWBUF type flags + */ +void modutil_reply_parse_error( + DCB* backend_dcb, + char* errstr, + uint32_t flags) +{ + CHK_DCB(backend_dcb); + modutil_reply_routing_error(backend_dcb, 1064, "42000", errstr, flags); +} + +/** + * 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 + * @param flags GWBUF type flags + */ +static void modutil_reply_routing_error( + DCB* backend_dcb, + int error, + char* state, + char* errstr, + uint32_t flags) +{ + 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 process reply correctly */ + gwbuf_set_type(buf, flags); + /** Create an incoming event for backend DCB */ + poll_add_epollin_event_to_dcb(backend_dcb, buf); + return; +} \ No newline at end of file diff --git a/server/include/modutil.h b/server/include/modutil.h index e287b080e..28abc1686 100644 --- a/server/include/modutil.h +++ b/server/include/modutil.h @@ -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_complete_packets(GWBUF** p_readbuf); int modutil_MySQL_query_len(GWBUF* buf, int* nbytes_missing); +void modutil_reply_parse_error(DCB* backend_dcb, char* errstr, uint32_t flags); GWBUF *modutil_create_mysql_err_msg( int packet_number, diff --git a/server/include/router.h b/server/include/router.h index 5c2ec5913..3815253bf 100644 --- a/server/include/router.h +++ b/server/include/router.h @@ -43,6 +43,11 @@ */ typedef void *ROUTER; +typedef enum error_action { + ERRACT_NEW_CONNECTION = 0x001, + ERRACT_REPLY_CLIENT = 0x002, + ERRACT_RESET = 0x004 +} error_action_t; /** * @verbatim @@ -66,13 +71,6 @@ typedef void *ROUTER; * * @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 { ROUTER *(*createInstance)(SERVICE *service, char **options); void *(*newSession)(ROUTER *instance, SESSION *session); diff --git a/server/modules/protocol/mysql_backend.c b/server/modules/protocol/mysql_backend.c index c52802306..5d3939868 100644 --- a/server/modules/protocol/mysql_backend.c +++ b/server/modules/protocol/mysql_backend.c @@ -357,8 +357,7 @@ static int gw_read_backend_event(DCB *dcb) { dcb->delayq, GWBUF_LENGTH(dcb->delayq))) != NULL); } - spinlock_release(&dcb->delayqlock); - + spinlock_release(&dcb->delayqlock); { GWBUF* errbuf; bool succp; @@ -1464,7 +1463,7 @@ static int gw_change_user( rv = 0; 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_SESCMD_RESPONSE); gwbuf_set_type(buf, GWBUF_TYPE_RESPONSE_END); diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index e21936aba..b5dbb565b 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -865,7 +865,7 @@ int gw_read_client_event( rc = SESSION_ROUTE_QUERY(session, read_buffer); } - /** succeed */ + /** Routing succeed */ if (rc) { rc = 0; /**< here '0' means success */ @@ -874,25 +874,6 @@ int gw_read_client_event( { bool succp; 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 * can't be continued. @@ -903,10 +884,10 @@ int gw_read_client_event( "Routing failed. Session is closed."); /** * Ensure that there are enough backends - * available. + * available. */ router->handleError( - router_instance, + router_instance, session->router_session, errbuf, dcb, diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 7832c0b46..9cd1b67d3 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -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( ROUTER_INSTANCE* inst, ROUTER_CLIENT_SES* rses, @@ -2162,12 +2171,14 @@ static bool route_single_stmt( if (TARGET_IS_MASTER(route_target) || TARGET_IS_SLAVE(route_target)) { + backend_ref_t* bref = rses->rses_backend_ref; + char* query_str = modutil_get_query(querybuf); char* qtype_str = skygw_get_qtype_str(qtype); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, - "Error: Can't route %s:%s:\"%s\". SELECT with " + "Error : Can't route %s:%s:\"%s\". SELECT with " "session data modification is not supported " "if configuration parameter " "use_sql_variables_in=all .", @@ -2181,9 +2192,23 @@ static bool route_single_stmt( "modification from other " "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."), + 0); + } if (query_str) free (query_str); if (qtype_str) free(qtype_str); - succp = false; + succp = true; goto retblock; } /**