Пример #1
0
/**
 * 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;
}
Пример #2
0
/**
 * 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;
}
Пример #3
0
/**
 * 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;
}
Пример #4
0
/**
 * 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;
}
Пример #5
0
/**
 * 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;
}