From c9592aa05ee5a0d663dc45dd51faddcdd2af7141 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Wed, 1 Mar 2017 14:48:28 +0100 Subject: [PATCH] MXS-1149: maxrows should return empty resultset instead of OK maxrows returns empty resultset instead of OK. The response with coulmndefs is sent then an EOF without flags is added. Limitations: in case of MULTI_RESULT sets only the first one coulmndefs is sent with EOF --- server/modules/filter/maxrows/maxrows.c | 98 ++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 4 deletions(-) diff --git a/server/modules/filter/maxrows/maxrows.c b/server/modules/filter/maxrows/maxrows.c index 1ca42d4cf..e7d8d567d 100644 --- a/server/modules/filter/maxrows/maxrows.c +++ b/server/modules/filter/maxrows/maxrows.c @@ -147,6 +147,7 @@ typedef struct maxrows_response_state size_t n_fields; /**< How many fields we have received, <= n_totalfields. */ size_t n_rows; /**< How many rows we have received. */ size_t offset; /**< Where we are in the response buffer. */ + size_t rows_offset; /**< Offset to first row in result set */ } MAXROWS_RESPONSE_STATE; static void maxrows_response_state_reset(MAXROWS_RESPONSE_STATE *state); @@ -175,6 +176,7 @@ static bool process_params(char **options, MXS_CONFIG_PARAMETER *params, MAXROWS static int send_upstream(MAXROWS_SESSION_DATA *csdata); static int send_ok_upstream(MAXROWS_SESSION_DATA *csdata); +static int send_eof_upstream(MAXROWS_SESSION_DATA *csdata, size_t offset); /* API BEGIN */ @@ -438,6 +440,7 @@ static void maxrows_response_state_reset(MAXROWS_RESPONSE_STATE *state) state->n_fields = 0; state->n_rows = 0; state->offset = 0; + state->rows_offset = 0; } /** @@ -511,6 +514,15 @@ static int handle_expecting_fields(MAXROWS_SESSION_DATA *csdata) { case 0xfe: // EOF, the one after the fields. csdata->res.offset += packetlen; + + /* Now set the offset to the first resultset + * this could be used for empty response handler + */ + if (!csdata->res.rows_offset) + { + csdata->res.rows_offset = csdata->res.offset; + } + csdata->state = MAXROWS_EXPECTING_ROWS; rv = handle_rows(csdata); break; @@ -599,7 +611,7 @@ static int handle_expecting_response(MAXROWS_SESSION_DATA *csdata) if (csdata->discard_resultset) { - rv = send_ok_upstream(csdata); + rv = send_eof_upstream(csdata, csdata->res.rows_offset); csdata->state = MAXROWS_EXPECTING_NOTHING; } else @@ -746,7 +758,7 @@ static int handle_rows(MAXROWS_SESSION_DATA *csdata) // Send data in buffer or empty resultset if (csdata->discard_resultset) { - rv = send_ok_upstream(csdata); + rv = send_eof_upstream(csdata, csdata->res.rows_offset); } else { @@ -779,7 +791,7 @@ static int handle_rows(MAXROWS_SESSION_DATA *csdata) if (packetlen < MYSQL_EOF_PACKET_LEN) { MXS_ERROR("EOF packet has size of %lu instead of %d", packetlen, MYSQL_EOF_PACKET_LEN); - rv = send_ok_upstream(csdata); + rv = send_eof_upstream(csdata, csdata->res.rows_offset); csdata->state = MAXROWS_EXPECTING_NOTHING; break; } @@ -800,7 +812,7 @@ static int handle_rows(MAXROWS_SESSION_DATA *csdata) // Discard data or send data if (csdata->discard_resultset) { - rv = send_ok_upstream(csdata); + rv = send_eof_upstream(csdata, csdata->res.rows_offset); } else { @@ -907,7 +919,20 @@ static int send_ok_upstream(MAXROWS_SESSION_DATA *csdata) { /* Note: sequence id is always 01 (4th byte) */ uint8_t ok[MYSQL_OK_PACKET_MIN_LEN] = {07, 00, 00, 01, 00, 00, 00, 02, 00, 00, 00}; + + ss_dassert(csdata->res.data != NULL); + GWBUF *packet = gwbuf_alloc(MYSQL_OK_PACKET_MIN_LEN); + if(!packet) + { + /* Abort clienrt connection */ + poll_fake_hangup_event(csdata->session->client_dcb); + gwbuf_free(csdata->res.data); + csdata->res.data = NULL; + gwbuf_free(packet); + return 0; + } + uint8_t *ptr = GWBUF_DATA(packet); memcpy(ptr, &ok, MYSQL_OK_PACKET_MIN_LEN); @@ -919,3 +944,68 @@ static int send_ok_upstream(MAXROWS_SESSION_DATA *csdata) return rv; } + +/** + * Send upstream the Respnse Buffer up to columns def in response + * including its EOF of the first result set + * An EOF packet for empty result set with no MULTI flags is added + * at the end. + * + * @param csdata Session data + * @param offset The offset to server reply pointing to + * next byte after EOF or column definitions of first result set + * + * @return Whatever the upstream returns. + */ +static int send_eof_upstream(MAXROWS_SESSION_DATA *csdata, size_t offset) +{ + int rv = -1; + /* Sequence byte is #3 */ + uint8_t eof[MYSQL_EOF_PACKET_LEN] = {05, 00, 00, 01, 0xfe, 00, 00, 02, 00}; + GWBUF *new_pkt = NULL; + + ss_dassert(csdata->res.data != NULL); + + /* Data to send + added EOF */ + uint8_t *new_result = MXS_MALLOC(offset + MYSQL_EOF_PACKET_LEN); + + if (new_result) + { + /* Get contiguous data from beginning to specified offset */ + gwbuf_copy_data(csdata->res.data, 0, offset, new_result); + + /* Increment sequence number for the EOF being added for empty resultset: + * last one if found in EOF terminating column def + */ + eof[3] = new_result[offset - (MYSQL_EOF_PACKET_LEN - 3)] + 1; + + /* Copy EOF data */ + memcpy(new_result + offset, &eof, MYSQL_EOF_PACKET_LEN); + + /* Create new packet */ + new_pkt = gwbuf_alloc_and_load(offset + MYSQL_EOF_PACKET_LEN, new_result); + + /* Free intermediate data */ + MXS_FREE(new_result); + + if (new_pkt) + { + /* new_pkt will be freed by write routine */ + rv = csdata->up.clientReply(csdata->up.instance, csdata->up.session, new_pkt); + } + } + + /* Abort client connection */ + if (!(new_result && new_pkt)) + { + /* Abort client connection */ + poll_fake_hangup_event(csdata->session->client_dcb); + rv = 0; + } + + /* Free full input buffer */ + gwbuf_free(csdata->res.data); + csdata->res.data = NULL; + + return rv; +}