/** * Trim bytes from the end of a GWBUF structure that may be the first * in a list. If the buffer has n_bytes or less then it will be freed and * the next buffer in the list will be returned, or if none, NULL. * * @param head The buffer to trim * @param n_bytes The number of bytes to trim off * @return The buffer chain or NULL if buffer chain now empty */ GWBUF * gwbuf_rtrim(GWBUF *head, unsigned int n_bytes) { GWBUF *rval = head; CHK_GWBUF(head); GWBUF_RTRIM(head, n_bytes); CHK_GWBUF(head); if (GWBUF_EMPTY(head)) { rval = head->next; gwbuf_free(head); } return rval; }
/** * Consume data from a buffer in the linked list. The assumption is to consume * n bytes from the buffer chain. * * If after consuming the bytes from the first buffer that buffer becomes * empty it will be freed and the linked list updated. * * The return value is the new head of the linked list. * * This call should be made with the caller holding the lock for the linked * list. * * @param head The head of the linked list * @param length The amount of data to consume * @return The head of the linked list */ GWBUF * gwbuf_consume(GWBUF *head, unsigned int length) { GWBUF *rval = head; CHK_GWBUF(head); GWBUF_CONSUME(head, length); CHK_GWBUF(head); if (GWBUF_EMPTY(head)) { rval = head->next; gwbuf_free(head); } return rval; }
/** * Remove the first mysql statement from buffer. Return pointer to the removed * statement or NULL if buffer is empty. * * Clone buf, calculate the length of included mysql stmt, and point the * statement with cloned buffer. Move the start pointer of buf accordingly * so that it only cover the remaining buffer. * */ GWBUF* gw_MySQL_get_next_stmt( GWBUF** p_readbuf) { GWBUF* stmtbuf; size_t buflen; size_t strlen; uint8_t* packet; if (*p_readbuf == NULL) { stmtbuf = NULL; goto return_stmtbuf; } CHK_GWBUF(*p_readbuf); if (GWBUF_EMPTY(*p_readbuf)) { stmtbuf = NULL; goto return_stmtbuf; } buflen = GWBUF_LENGTH((*p_readbuf)); packet = GWBUF_DATA((*p_readbuf)); strlen = MYSQL_GET_PACKET_LEN(packet); if (strlen+4 == buflen) { stmtbuf = *p_readbuf; *p_readbuf = NULL; goto return_stmtbuf; } /** vraa :Multi-packet stmt is not supported as of 7.3.14 */ if (strlen-1 > buflen-5) { stmtbuf = NULL; goto return_stmtbuf; } stmtbuf = gwbuf_clone_portion(*p_readbuf, 0, strlen+4); *p_readbuf = gwbuf_consume(*p_readbuf, strlen+4); return_stmtbuf: return stmtbuf; }
/** * Consume data from a buffer in the linked list. The assumption is to consume * n bytes from the buffer chain. * * If after consuming the bytes from the first buffer that buffer becomes * empty it will be freed and the linked list updated. * * The return value is the new head of the linked list. * * This call should be made with the caller holding the lock for the linked * list. * * @param head The head of the linked list * @param length The amount of data to consume * @return The head of the linked list */ GWBUF * gwbuf_consume(GWBUF *head, unsigned int length) { GWBUF *rval = head; CHK_GWBUF(head); GWBUF_CONSUME(head, length); CHK_GWBUF(head); if (GWBUF_EMPTY(head)) { rval = head->next; if (head->next) head->next->tail = head->tail; gwbuf_free(head); } ss_dassert(rval == NULL || (rval->end > rval->start)); return rval; }
/** * The clientReply entry point. This is passed the response buffer * to which the filter should be applied. Once processed the * query is passed to the upstream component * (filter or router) in the filter chain. * * @param instance The filter instance data * @param session The filter session * @param reply The response data */ static int clientReply(FILTER* instance, void *session, GWBUF *reply) { int rc, branch, eof; TEE_SESSION *my_session = (TEE_SESSION *) session; bool route = false, mpkt; GWBUF *complete = NULL; unsigned char *ptr; uint16_t flags = 0; int min_eof = my_session->command != 0x04 ? 2 : 1; int more_results = 0; #ifdef SS_DEBUG int prev_debug_seq = atomic_add(&debug_seq, 1); ptr = (unsigned char*) reply->start; MXS_INFO("Tee clientReply [%s] [%s] [%s]: %d", instance ? "parent" : "child", my_session->active ? "open" : "closed", PTR_IS_ERR(ptr) ? "ERR" : PTR_IS_OK(ptr) ? "OK" : "RSET", prev_debug_seq); #endif spinlock_acquire(&my_session->tee_lock); if (!my_session->active) { MXS_INFO("Tee: Failed to return reply, session is closed"); gwbuf_free(reply); rc = 0; if (my_session->waiting[PARENT]) { GWBUF* errbuf = modutil_create_mysql_err_msg(1, 0, 1, "0000", "Session closed."); my_session->waiting[PARENT] = false; my_session->up.clientReply(my_session->up.instance, my_session->up.session, errbuf); } goto retblock; } branch = instance == NULL ? CHILD : PARENT; my_session->tee_partials[branch] = gwbuf_append(my_session->tee_partials[branch], reply); my_session->tee_partials[branch] = gwbuf_make_contiguous(my_session->tee_partials[branch]); complete = modutil_get_complete_packets(&my_session->tee_partials[branch]); if (complete == NULL) { /** Incomplete packet */ MXS_DEBUG("tee.c: Incomplete packet, " "waiting for a complete packet before forwarding."); rc = 1; goto retblock; } complete = gwbuf_make_contiguous(complete); if (my_session->tee_partials[branch] && GWBUF_EMPTY(my_session->tee_partials[branch])) { gwbuf_free(my_session->tee_partials[branch]); my_session->tee_partials[branch] = NULL; } ptr = (unsigned char*) complete->start; if (my_session->replies[branch] == 0) { MXS_INFO("Tee: First reply to a query for [%s].", branch == PARENT ? "PARENT" : "CHILD"); /* Reply is in a single packet if it is an OK, ERR or LOCAL_INFILE packet. * Otherwise the reply is a result set and the amount of packets is unknown. */ if (PTR_IS_ERR(ptr) || PTR_IS_LOCAL_INFILE(ptr) || PTR_IS_OK(ptr) || !my_session->multipacket[branch]) { my_session->waiting[branch] = false; my_session->multipacket[branch] = false; if (PTR_IS_OK(ptr)) { flags = get_response_flags(ptr, true); more_results = (flags & 0x08) && my_session->client_multistatement; if (more_results) { MXS_INFO("Tee: [%s] waiting for more results.", branch == PARENT ? "PARENT" : "CHILD"); } } } #ifdef SS_DEBUG else { MXS_DEBUG("tee.c: [%ld] Waiting for a result set from %s session.", my_session->d_id, branch == PARENT ? "parent" : "child"); } #endif } if (my_session->waiting[branch]) { eof = modutil_count_signal_packets(complete, my_session->use_ok, my_session->eof[branch] > 0, &more_results); more_results &= my_session->client_multistatement; my_session->eof[branch] += eof; if (my_session->eof[branch] >= min_eof) { #ifdef SS_DEBUG MXS_DEBUG("tee.c [%ld] %s received last EOF packet", my_session->d_id, branch == PARENT ? "parent" : "child"); #endif my_session->waiting[branch] = more_results; if (more_results) { my_session->eof[branch] = 0; } } } if (branch == PARENT) { my_session->tee_replybuf = gwbuf_append(my_session->tee_replybuf, complete); } else { gwbuf_free(complete); } my_session->replies[branch]++; rc = 1; mpkt = my_session->multipacket[PARENT] || my_session->multipacket[CHILD]; if (my_session->tee_replybuf != NULL) { if (my_session->branch_session == NULL) { rc = 0; gwbuf_free(my_session->tee_replybuf); my_session->tee_replybuf = NULL; MXS_ERROR("Tee child session was closed."); } if (mpkt) { if (my_session->waiting[PARENT]) { route = true; } else if (my_session->eof[PARENT] >= min_eof && my_session->eof[CHILD] >= min_eof) { route = true; #ifdef SS_DEBUG MXS_DEBUG("tee.c:[%ld] Routing final packet of response set.", my_session->d_id); #endif } } else if (!my_session->waiting[PARENT] && !my_session->waiting[CHILD]) { #ifdef SS_DEBUG MXS_DEBUG("tee.c:[%ld] Routing single packet response.", my_session->d_id); #endif route = true; } } if (route) { #ifdef SS_DEBUG MXS_DEBUG("tee.c:[%ld] Routing buffer '%p' parent(waiting [%s] replies [%d] eof[%d])" " child(waiting [%s] replies[%d] eof [%d])", my_session->d_id, my_session->tee_replybuf, my_session->waiting[PARENT] ? "true" : "false", my_session->replies[PARENT], my_session->eof[PARENT], my_session->waiting[CHILD] ? "true" : "false", my_session->replies[CHILD], my_session->eof[CHILD]); #endif rc = my_session->up.clientReply(my_session->up.instance, my_session->up.session, my_session->tee_replybuf); my_session->tee_replybuf = NULL; } if (my_session->queue && !my_session->waiting[PARENT] && !my_session->waiting[CHILD]) { GWBUF* buffer = modutil_get_next_MySQL_packet(&my_session->queue); GWBUF* clone = clone_query(my_session->instance, my_session, buffer); reset_session_state(my_session, buffer); route_single_query(my_session->instance, my_session, buffer, clone); MXS_INFO("tee: routing queued query"); } retblock: spinlock_release(&my_session->tee_lock); return rc; }