Esempio n. 1
0
/**
 * The routeQuery entry point. This is passed the query buffer
 * to which the filter should be applied. Once applied the
 * query should normally be passed to the downstream component
 * (filter or router) in the filter chain.
 *
 * If my_session->residual is set then duplicate that many bytes
 * and send them to the branch.
 *
 * If my_session->residual is zero then this must be a new request
 * Extract the SQL text if possible, match against that text and forward
 * the request. If the requets is not contained witin the packet we have
 * then set my_session->residual to the number of outstanding bytes
 *
 * @param instance	The filter instance data
 * @param session	The filter session
 * @param queue		The query data
 */
static	int	
routeQuery(FILTER *instance, void *session, GWBUF *queue)
{
    TEE_INSTANCE	*my_instance = (TEE_INSTANCE *)instance;
    TEE_SESSION		*my_session = (TEE_SESSION *)session;
    char		*ptr;
    int			rval;
    GWBUF		*buffer = NULL, *clone = NULL;
    unsigned char	command = gwbuf_length(queue) >= 5 ?
	*((unsigned char*)queue->start + 4) : 1;

#ifdef SS_DEBUG
    skygw_log_write(LOGFILE_TRACE,"Tee routeQuery: %d : %s",
		    atomic_add(&debug_seq,1),
		    ((char*)queue->start + 5));
#endif


    spinlock_acquire(&my_session->tee_lock);

    if(!my_session->active)
    {
	skygw_log_write(LOGFILE_TRACE, "Tee: Received a reply when the session was closed.");
	gwbuf_free(queue);
	spinlock_release(&my_session->tee_lock);
	return 0;
    }

    if(my_session->queue)
    {
	my_session->queue = gwbuf_append(my_session->queue,queue);
	buffer = modutil_get_next_MySQL_packet(&my_session->queue);
    }
    else
    {
	buffer = modutil_get_next_MySQL_packet(&queue);
	my_session->queue = queue;
    }

    if(buffer == NULL)
    {
	spinlock_release(&my_session->tee_lock);
	return 1;
    }
    
    clone = clone_query(my_instance, my_session,buffer);
    spinlock_release(&my_session->tee_lock);

    /* Reset session state */
    if(!reset_session_state(my_session,buffer))
	return 0;

    /** Route query downstream */
    spinlock_acquire(&my_session->tee_lock);
    rval = route_single_query(my_instance,my_session,buffer,clone);
    spinlock_release(&my_session->tee_lock);

    return rval;
}
Esempio n. 2
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;
}