651 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			651 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2016 MariaDB Corporation Ab
 | 
						|
 *
 | 
						|
 * Use of this software is governed by the Business Source License included
 | 
						|
 * in the LICENSE.TXT file and at www.mariadb.com/bsl11.
 | 
						|
 *
 | 
						|
 * Change Date: 2024-03-10
 | 
						|
 *
 | 
						|
 * On the date above, in accordance with the Business Source License, use
 | 
						|
 * of this software will be governed by version 2 or later of the General
 | 
						|
 * Public License.
 | 
						|
 */
 | 
						|
 | 
						|
// To ensure that ss_info_assert asserts also when builing in non-debug mode.
 | 
						|
#if !defined (SS_DEBUG)
 | 
						|
#define SS_DEBUG
 | 
						|
#endif
 | 
						|
#if defined (NDEBUG)
 | 
						|
#undef NDEBUG
 | 
						|
#endif
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#include <maxbase/log.hh>
 | 
						|
#include <maxbase/alloc.h>
 | 
						|
#include <maxscale/modutil.hh>
 | 
						|
#include <maxscale/buffer.hh>
 | 
						|
 | 
						|
/**
 | 
						|
 * test1    Allocate a service and do lots of other things
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
static int test1()
 | 
						|
{
 | 
						|
    GWBUF* buffer;
 | 
						|
    char* sql;
 | 
						|
    int result, length, residual;
 | 
						|
 | 
						|
    /* Poll tests */
 | 
						|
    fprintf(stderr,
 | 
						|
            "testmodutil : Rudimentary tests.");
 | 
						|
    buffer = gwbuf_alloc(100);
 | 
						|
    mxb_assert_message(GWBUF_IS_CONTIGUOUS(buffer), "Allocated buffer should be continuos");
 | 
						|
    memset(GWBUF_DATA(buffer), 0, GWBUF_LENGTH(buffer));
 | 
						|
    mxb_assert_message(0 == modutil_is_SQL(buffer), "Default buffer should be diagnosed as not SQL");
 | 
						|
    /* There would ideally be some straightforward way to create a SQL buffer? */
 | 
						|
    fprintf(stderr, "\t..done\nExtract SQL from buffer");
 | 
						|
    mxb_assert_message(0 == modutil_extract_SQL(buffer, &sql, &length), "Default buffer should fail");
 | 
						|
    fprintf(stderr, "\t..done\nExtract SQL from buffer different way?");
 | 
						|
    mxb_assert_message(0 == modutil_MySQL_Query(buffer, &sql, &length, &residual),
 | 
						|
                       "Default buffer should fail");
 | 
						|
    fprintf(stderr, "\t..done\nReplace SQL in buffer");
 | 
						|
    mxb_assert_message(0 == modutil_replace_SQL(buffer, (char*)"select * from some_table;"),
 | 
						|
                       "Default buffer should fail");
 | 
						|
    fprintf(stderr, "\t..done\nTidy up.");
 | 
						|
    gwbuf_free(buffer);
 | 
						|
    fprintf(stderr, "\t..done\n");
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int test2()
 | 
						|
{
 | 
						|
    GWBUF* buffer;
 | 
						|
    unsigned int len = 128;
 | 
						|
    char query[129];
 | 
						|
 | 
						|
    /** Allocate space for the COM_QUERY header and payload */
 | 
						|
    buffer = gwbuf_alloc(5 + 128);
 | 
						|
    mxb_assert_message((buffer != NULL), "Buffer should not be null");
 | 
						|
 | 
						|
    memset(query, ';', 128);
 | 
						|
    memset(query + 128, '\0', 1);
 | 
						|
    *((unsigned char*)buffer->start) = len;
 | 
						|
    *((unsigned char*)buffer->start + 1) = 0;
 | 
						|
    *((unsigned char*)buffer->start + 2) = 0;
 | 
						|
    *((unsigned char*)buffer->start + 3) = 1;
 | 
						|
    *((unsigned char*)buffer->start + 4) = 0x03;
 | 
						|
    memcpy((uint8_t*)buffer->start + 5, query, strlen(query));
 | 
						|
    char* result = modutil_get_SQL(buffer);
 | 
						|
    mxb_assert(strcmp(result, query) == 0);
 | 
						|
    gwbuf_free(buffer);
 | 
						|
    MXS_FREE(result);
 | 
						|
    fprintf(stderr, "\t..done\n");
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/** This is a standard OK packet */
 | 
						|
static char ok[] =
 | 
						|
{
 | 
						|
    0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00
 | 
						|
};
 | 
						|
 | 
						|
/** Created with:
 | 
						|
 * CREATE OR REPLACE TABLE test.t1 (id int);
 | 
						|
 * INSERT INTO test.t1 VALUES (3000);
 | 
						|
 * SELECT * FROM test.t1; */
 | 
						|
static const uint8_t resultset[] =
 | 
						|
{
 | 
						|
    /* Packet 1 */
 | 
						|
    0x01, 0x00, 0x00, 0x01, 0x01,
 | 
						|
    /* Packet 2 */
 | 
						|
    0x22, 0x00, 0x00, 0x02, 0x03,0x64,  0x65, 0x66, 0x04, 0x74, 0x65, 0x73, 0x74, 0x02, 0x74, 0x31,
 | 
						|
    0x02, 0x74, 0x31, 0x02, 0x69,0x64,  0x02, 0x69, 0x64, 0x0c, 0x3f,
 | 
						|
    0x00, 0x0b, 0x00, 0x00, 0x00,0x03,  0x00, 0x00, 0x00, 0x00, 0x00,
 | 
						|
    /* Packet 3 */
 | 
						|
    0x05, 0x00, 0x00, 0x03, 0xfe,0x00,  0x00, 0x22, 0x00,
 | 
						|
    /* Packet 4 */
 | 
						|
    0x05, 0x00, 0x00, 0x04, 0x04,0x33,  0x30, 0x30, 0x30,
 | 
						|
    /* Packet 5 */
 | 
						|
    0x05, 0x00, 0x00, 0x05, 0xfe,0x00,  0x00, 0x22, 0x00
 | 
						|
};
 | 
						|
 | 
						|
#define PACKET_HDR_LEN 4
 | 
						|
 | 
						|
#define PACKET_1_IDX 0
 | 
						|
#define PACKET_1_LEN (PACKET_HDR_LEN + 0x01)        // resultset[PACKET_1_IDX])
 | 
						|
#define PACKET_2_IDX (PACKET_1_IDX + PACKET_1_LEN)
 | 
						|
#define PACKET_2_LEN (PACKET_HDR_LEN + 0x22)        // resultset[PACKET_2_IDX]);
 | 
						|
#define PACKET_3_IDX (PACKET_2_IDX + PACKET_2_LEN)
 | 
						|
#define PACKET_3_LEN (PACKET_HDR_LEN + 0x05)        // resultset[PACKET_3_IDX]);
 | 
						|
#define PACKET_4_IDX (PACKET_3_IDX + PACKET_3_LEN)
 | 
						|
#define PACKET_4_LEN (PACKET_HDR_LEN + 0x05)        // resultset[PACKET_4_IDX]);
 | 
						|
#define PACKET_5_IDX (PACKET_4_IDX + PACKET_4_LEN)
 | 
						|
#define PACKET_5_LEN (PACKET_HDR_LEN + 0x05)        // resultset[PACKET_5_IDX]);
 | 
						|
 | 
						|
struct packet
 | 
						|
{
 | 
						|
    int          index;
 | 
						|
    unsigned int length;
 | 
						|
} packets[] =
 | 
						|
{
 | 
						|
    {PACKET_1_IDX, PACKET_1_LEN},
 | 
						|
    {PACKET_2_IDX, PACKET_2_LEN},
 | 
						|
    {PACKET_3_IDX, PACKET_3_LEN},
 | 
						|
    {PACKET_4_IDX, PACKET_4_LEN},
 | 
						|
    {PACKET_5_IDX, PACKET_5_LEN},
 | 
						|
};
 | 
						|
 | 
						|
#define N_PACKETS (sizeof(packets) / sizeof(packets[0]))
 | 
						|
 | 
						|
 | 
						|
//
 | 
						|
// modutil_get_complete_packets
 | 
						|
//
 | 
						|
void test_single_sql_packet1()
 | 
						|
{
 | 
						|
    printf("%s\n", __func__);
 | 
						|
    /** Single packet */
 | 
						|
    GWBUF* buffer = gwbuf_alloc_and_load(sizeof(ok), ok);
 | 
						|
    GWBUF* complete = modutil_get_complete_packets(&buffer);
 | 
						|
    mxb_assert_message(buffer == NULL, "Old buffer should be NULL");
 | 
						|
    mxb_assert_message(complete, "Complete packet buffer should not be NULL");
 | 
						|
    mxb_assert_message(gwbuf_length(complete) == sizeof(ok),
 | 
						|
                       "Complete packet buffer should contain enough data");
 | 
						|
    mxb_assert_message(memcmp(GWBUF_DATA(complete), ok, GWBUF_LENGTH(complete)) == 0,
 | 
						|
                       "Complete packet buffer's data should be equal to original data");
 | 
						|
    gwbuf_free(complete);
 | 
						|
 | 
						|
    /** Partial single packet */
 | 
						|
    buffer = gwbuf_alloc_and_load(sizeof(ok) - 4, ok);
 | 
						|
    complete = modutil_get_complete_packets(&buffer);
 | 
						|
    mxb_assert_message(buffer, "Old buffer should be not NULL");
 | 
						|
    mxb_assert_message(complete == NULL, "Complete packet buffer should be NULL");
 | 
						|
    mxb_assert_message(gwbuf_length(buffer) == sizeof(ok) - 4,
 | 
						|
                       "Old buffer should contain right amount of data");
 | 
						|
 | 
						|
    /** Add the missing data */
 | 
						|
    buffer = gwbuf_append(buffer, gwbuf_alloc_and_load(4, ok + sizeof(ok) - 4));
 | 
						|
    complete = modutil_get_complete_packets(&buffer);
 | 
						|
    mxb_assert_message(buffer == NULL, "Old buffer should be NULL");
 | 
						|
    mxb_assert_message(complete, "Complete packet buffer should not be NULL");
 | 
						|
    mxb_assert_message(complete->next, "The complete packet should be a chain of buffers");
 | 
						|
    mxb_assert_message(gwbuf_length(complete) == sizeof(ok), "Buffer should contain all data");
 | 
						|
    gwbuf_free(complete);
 | 
						|
}
 | 
						|
 | 
						|
void test_multiple_sql_packets1()
 | 
						|
{
 | 
						|
    printf("%s\n", __func__);
 | 
						|
    /** All of the data */
 | 
						|
    GWBUF* buffer = gwbuf_alloc_and_load(sizeof(resultset), resultset);
 | 
						|
    GWBUF* complete = modutil_get_complete_packets(&buffer);
 | 
						|
    mxb_assert_message(buffer == NULL, "Old buffer should be NULL");
 | 
						|
    mxb_assert_message(complete, "Complete packet buffer should not be NULL");
 | 
						|
    mxb_assert_message(gwbuf_length(complete) == sizeof(resultset),
 | 
						|
                       "Complete packet buffer should contain enough data");
 | 
						|
    mxb_assert_message(memcmp(GWBUF_DATA(complete), resultset, GWBUF_LENGTH(complete)) == 0,
 | 
						|
                       "Complete packet buffer's data should be equal to original data");
 | 
						|
    gwbuf_free(complete);
 | 
						|
 | 
						|
    /** Partial data available with one complete packet */
 | 
						|
    GWBUF* head = gwbuf_alloc_and_load(7, resultset);
 | 
						|
    GWBUF* tail = gwbuf_alloc_and_load(sizeof(resultset) - 7, resultset + 7);
 | 
						|
    complete = modutil_get_complete_packets(&head);
 | 
						|
    mxb_assert_message(head, "Old buffer should not be NULL");
 | 
						|
    mxb_assert_message(complete, "Complete buffer should not be NULL");
 | 
						|
    mxb_assert_message(gwbuf_length(complete) == 5, "Complete buffer should contain first packet only");
 | 
						|
    mxb_assert_message(gwbuf_length(head) == 2, "Complete buffer should contain first packet only");
 | 
						|
    gwbuf_free(complete);
 | 
						|
 | 
						|
    /** All packets are available */
 | 
						|
    head = gwbuf_append(head, tail);
 | 
						|
    complete = modutil_get_complete_packets(&head);
 | 
						|
    mxb_assert_message(head == NULL, "Old buffer should be NULL");
 | 
						|
    mxb_assert_message(complete, "Complete packet buffer should not be NULL");
 | 
						|
    mxb_assert_message(gwbuf_length(complete) == sizeof(resultset) - 5,
 | 
						|
                       "Complete packet should be sizeof(resultset) - 5 bytes");
 | 
						|
    gwbuf_free(complete);
 | 
						|
 | 
						|
    /** Sliding cutoff of the buffer boundary */
 | 
						|
    for (size_t i = 0; i < sizeof(resultset); i++)
 | 
						|
    {
 | 
						|
        head = gwbuf_alloc_and_load(i, resultset);
 | 
						|
        tail = gwbuf_alloc_and_load(sizeof(resultset) - i, resultset + i);
 | 
						|
        head = gwbuf_append(head, tail);
 | 
						|
        complete = modutil_get_complete_packets(&head);
 | 
						|
        int headlen = head ? gwbuf_length(head) : 0;
 | 
						|
        int completelen = complete ? gwbuf_length(complete) : 0;
 | 
						|
        mxb_assert_message(headlen + completelen == sizeof(resultset),
 | 
						|
                           "Both buffers should sum up to sizeof(resutlset) bytes");
 | 
						|
        uint8_t databuf[sizeof(resultset)];
 | 
						|
        gwbuf_copy_data(complete, 0, completelen, databuf);
 | 
						|
        if (head)
 | 
						|
        {
 | 
						|
            gwbuf_copy_data(head, 0, headlen, databuf + completelen);
 | 
						|
        }
 | 
						|
        mxb_assert_message(memcmp(databuf, resultset, sizeof(resultset)) == 0, "Data should be OK");
 | 
						|
        gwbuf_free(head);
 | 
						|
        gwbuf_free(complete);
 | 
						|
    }
 | 
						|
 | 
						|
    /** Fragmented buffer chain */
 | 
						|
    size_t chunk = 5;
 | 
						|
    size_t total = 0;
 | 
						|
    head = NULL;
 | 
						|
 | 
						|
    do
 | 
						|
    {
 | 
						|
        chunk = chunk + 5 < sizeof(resultset) ? 5 : (chunk + 5) - sizeof(resultset);
 | 
						|
        head = gwbuf_append(head, gwbuf_alloc_and_load(chunk, resultset + total));
 | 
						|
        total += chunk;
 | 
						|
    }
 | 
						|
    while (total < sizeof(resultset));
 | 
						|
 | 
						|
    mxb_assert_message(gwbuf_length(head) == sizeof(resultset), "Head should be sizeof(resulset) bytes long");
 | 
						|
    complete = modutil_get_complete_packets(&head);
 | 
						|
    mxb_assert_message(head == NULL, "Head should be NULL");
 | 
						|
    mxb_assert_message(complete, "Complete should not be NULL");
 | 
						|
    mxb_assert_message(gwbuf_length(complete) == sizeof(resultset),
 | 
						|
                       "Complete should be sizeof(resulset) bytes long");
 | 
						|
 | 
						|
    unsigned int headlen = head ? gwbuf_length(head) : 0;
 | 
						|
    unsigned int completelen = complete ? gwbuf_length(complete) : 0;
 | 
						|
    uint8_t databuf[sizeof(resultset)];
 | 
						|
    mxb_assert_message(gwbuf_copy_data(complete, 0, completelen, databuf) == completelen,
 | 
						|
                       "Expected data should be readable");
 | 
						|
    if (head)
 | 
						|
    {
 | 
						|
        mxb_assert_message(gwbuf_copy_data(head, 0, headlen, databuf + completelen) == headlen,
 | 
						|
                           "Expected data should be readable");
 | 
						|
    }
 | 
						|
    mxb_assert_message(memcmp(databuf, resultset, sizeof(resultset)) == 0, "Data should be OK");
 | 
						|
 | 
						|
    /** Fragmented buffer split into multiple chains and then reassembled as a complete resultset */
 | 
						|
    GWBUF* half = complete;
 | 
						|
    GWBUF* quarter = gwbuf_split(&half, gwbuf_length(half) / 2);
 | 
						|
    head = gwbuf_split(&quarter, gwbuf_length(quarter) / 2);
 | 
						|
    mxb_assert_message(half && quarter && head, "gwbuf_split should work");
 | 
						|
 | 
						|
    complete = modutil_get_complete_packets(&head);
 | 
						|
    mxb_assert_message(complete && head, "Both buffers should have data");
 | 
						|
    mxb_assert_message(gwbuf_length(complete) + gwbuf_length(head) + gwbuf_length(quarter)
 | 
						|
                       + gwbuf_length(half) == sizeof(resultset),
 | 
						|
                       "a quarter of data should be available");
 | 
						|
 | 
						|
    quarter = gwbuf_append(gwbuf_append(complete, head), quarter);
 | 
						|
    complete = modutil_get_complete_packets(&quarter);
 | 
						|
    mxb_assert_message(gwbuf_length(complete) + gwbuf_length(quarter)
 | 
						|
                       + gwbuf_length(half) == sizeof(resultset),
 | 
						|
                       "half of data should be available");
 | 
						|
 | 
						|
    half = gwbuf_append(gwbuf_append(complete, quarter), half);
 | 
						|
    complete = modutil_get_complete_packets(&half);
 | 
						|
    mxb_assert_message(complete, "Complete should not be NULL");
 | 
						|
    mxb_assert_message(half == NULL, "Old buffer should be NULL");
 | 
						|
    mxb_assert_message(gwbuf_length(complete) == sizeof(resultset),
 | 
						|
                       "Complete should contain all of the data");
 | 
						|
 | 
						|
    completelen = gwbuf_length(complete);
 | 
						|
    mxb_assert_message(gwbuf_copy_data(complete, 0, completelen, databuf) == completelen,
 | 
						|
                       "All data should be readable");
 | 
						|
    mxb_assert_message(memcmp(databuf, resultset, sizeof(resultset)) == 0, "Data should be OK");
 | 
						|
    gwbuf_free(complete);
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// modutil_get_next_MySQL_packet
 | 
						|
//
 | 
						|
void test_single_sql_packet2()
 | 
						|
{
 | 
						|
    printf("%s\n", __func__);
 | 
						|
    /** Single packet */
 | 
						|
    GWBUF* buffer;
 | 
						|
    GWBUF* next;
 | 
						|
 | 
						|
    buffer = gwbuf_alloc_and_load(sizeof(ok), ok);
 | 
						|
    next = modutil_get_next_MySQL_packet(&buffer);
 | 
						|
    mxb_assert_message(buffer == NULL, "Old buffer should be NULL");
 | 
						|
    mxb_assert_message(next, "Next packet buffer should not be NULL");
 | 
						|
    mxb_assert_message(gwbuf_length(next) == sizeof(ok), "Next packet buffer should contain enough data");
 | 
						|
    mxb_assert_message(memcmp(GWBUF_DATA(next), ok, GWBUF_LENGTH(next)) == 0,
 | 
						|
                       "Next packet buffer's data should be equal to original data");
 | 
						|
    gwbuf_free(buffer);
 | 
						|
    gwbuf_free(next);
 | 
						|
 | 
						|
    /** Partial single packet */
 | 
						|
    buffer = gwbuf_alloc_and_load(sizeof(ok) - 4, ok);
 | 
						|
    next = modutil_get_next_MySQL_packet(&buffer);
 | 
						|
    mxb_assert_message(buffer, "Old buffer should be not NULL");
 | 
						|
    mxb_assert_message(next == NULL, "Next packet buffer should be NULL");
 | 
						|
    mxb_assert_message(gwbuf_length(buffer) == sizeof(ok) - 4,
 | 
						|
                       "Old buffer should contain right amount of data");
 | 
						|
 | 
						|
    /** Add the missing data */
 | 
						|
    buffer = gwbuf_append(buffer, gwbuf_alloc_and_load(4, ok + sizeof(ok) - 4));
 | 
						|
    next = modutil_get_next_MySQL_packet(&buffer);
 | 
						|
    mxb_assert_message(buffer == NULL, "Old buffer should be NULL");
 | 
						|
    mxb_assert_message(next, "Next packet buffer should not be NULL");
 | 
						|
    // To be put back when the current realloc behaviour is replaced with splitting behaviour.
 | 
						|
    // mxb_assert_message(next->next, "The next packet should be a chain of buffers");
 | 
						|
    mxb_assert_message(gwbuf_length(next) == sizeof(ok), "Buffer should contain all data");
 | 
						|
    gwbuf_free(next);
 | 
						|
}
 | 
						|
 | 
						|
void test_multiple_sql_packets2()
 | 
						|
{
 | 
						|
    printf("%s\n", __func__);
 | 
						|
    /** All of the data */
 | 
						|
    GWBUF* buffer;
 | 
						|
    GWBUF* next;
 | 
						|
 | 
						|
    buffer = gwbuf_alloc_and_load(sizeof(resultset), resultset);
 | 
						|
    // Empty buffer packet by packet.
 | 
						|
    for (unsigned int i = 0; i < N_PACKETS; i++)
 | 
						|
    {
 | 
						|
        GWBUF* next = modutil_get_next_MySQL_packet(&buffer);
 | 
						|
        mxb_assert_message(next, "Next packet buffer should not be NULL");
 | 
						|
        mxb_assert_message(gwbuf_length(next) == packets[i].length,
 | 
						|
                           "Next packet buffer should contain enough data");
 | 
						|
        mxb_assert_message(memcmp(GWBUF_DATA(next), &resultset[packets[i].index], GWBUF_LENGTH(next)) == 0,
 | 
						|
                           "Next packet buffer's data should be equal to original data");
 | 
						|
        gwbuf_free(next);
 | 
						|
    }
 | 
						|
    mxb_assert_message(buffer == NULL, "Buffer should be NULL");
 | 
						|
 | 
						|
    size_t len;
 | 
						|
    // Exactly one packet
 | 
						|
    len = PACKET_1_LEN;
 | 
						|
    buffer = gwbuf_alloc_and_load(len, resultset);
 | 
						|
    next = modutil_get_next_MySQL_packet(&buffer);
 | 
						|
    mxb_assert_message(buffer == NULL, "Old buffer should be NULL.");
 | 
						|
    mxb_assert_message(next, "Next should not be NULL.");
 | 
						|
    mxb_assert_message(GWBUF_LENGTH(next) == PACKET_1_LEN, "Length should match.");
 | 
						|
    gwbuf_free(next);
 | 
						|
 | 
						|
    // Slightly less than one packet
 | 
						|
    len = PACKET_1_LEN - 1;
 | 
						|
    buffer = gwbuf_alloc_and_load(len, resultset);
 | 
						|
    next = modutil_get_next_MySQL_packet(&buffer);
 | 
						|
    mxb_assert_message(buffer, "Old buffer should not be NULL.");
 | 
						|
    mxb_assert_message(next == NULL, "Next should be NULL.");
 | 
						|
 | 
						|
    GWBUF* tail = gwbuf_alloc_and_load(sizeof(resultset) - len, resultset + len);
 | 
						|
    buffer = gwbuf_append(buffer, tail);
 | 
						|
    next = modutil_get_next_MySQL_packet(&buffer);
 | 
						|
    mxb_assert_message(buffer, "Old buffer should not be NULL.");
 | 
						|
    mxb_assert_message(next, "Next should not be NULL.");
 | 
						|
    mxb_assert_message(gwbuf_length(next) == PACKET_1_LEN, "Length should match.");
 | 
						|
    gwbuf_free(buffer);
 | 
						|
    gwbuf_free(next);
 | 
						|
 | 
						|
    // Slightly more than one packet
 | 
						|
    len = PACKET_1_LEN + 1;
 | 
						|
    buffer = gwbuf_alloc_and_load(len, resultset);
 | 
						|
    next = modutil_get_next_MySQL_packet(&buffer);
 | 
						|
    mxb_assert_message(buffer, "Old buffer should not be NULL.");
 | 
						|
    mxb_assert_message(next, "Next should not be NULL.");
 | 
						|
    mxb_assert_message(GWBUF_LENGTH(next) == PACKET_1_LEN, "Length should match.");
 | 
						|
    gwbuf_free(next);
 | 
						|
 | 
						|
    next = modutil_get_next_MySQL_packet(&buffer);
 | 
						|
    mxb_assert_message(buffer, "Old buffer should not be NULL.");
 | 
						|
    mxb_assert_message(next == NULL, "Next should be NULL.");
 | 
						|
 | 
						|
    tail = gwbuf_alloc_and_load(sizeof(resultset) - len, resultset + len);
 | 
						|
    buffer = gwbuf_append(buffer, tail);
 | 
						|
    next = modutil_get_next_MySQL_packet(&buffer);
 | 
						|
    mxb_assert_message(buffer, "Old buffer should not be NULL.");
 | 
						|
    mxb_assert_message(next, "Next should not be NULL.");
 | 
						|
    mxb_assert_message(gwbuf_length(next) == PACKET_2_LEN, "Length should match.");
 | 
						|
    mxb_assert_message(memcmp(GWBUF_DATA(next), &resultset[PACKET_2_IDX], GWBUF_LENGTH(next)) == 0,
 | 
						|
                       "Next packet buffer's data should be equal to original data");
 | 
						|
    gwbuf_free(buffer);
 | 
						|
    gwbuf_free(next);
 | 
						|
 | 
						|
    GWBUF* head;
 | 
						|
    /** Sliding cutoff of the buffer boundary */
 | 
						|
    for (size_t i = 0; i < sizeof(resultset); i++)
 | 
						|
    {
 | 
						|
        head = gwbuf_alloc_and_load(i, resultset);
 | 
						|
        tail = gwbuf_alloc_and_load(sizeof(resultset) - i, resultset + i);
 | 
						|
        head = gwbuf_append(head, tail);
 | 
						|
        next = modutil_get_next_MySQL_packet(&head);
 | 
						|
        int headlen = gwbuf_length(head);
 | 
						|
        int nextlen = next ? gwbuf_length(next) : 0;
 | 
						|
        mxb_assert_message(headlen + nextlen == sizeof(resultset),
 | 
						|
                           "Both buffers should sum up to sizeof(resutlset) bytes");
 | 
						|
        uint8_t databuf[sizeof(resultset)];
 | 
						|
        gwbuf_copy_data(next, 0, nextlen, databuf);
 | 
						|
        gwbuf_copy_data(head, 0, headlen, databuf + nextlen);
 | 
						|
        mxb_assert_message(memcmp(databuf, resultset, sizeof(resultset)) == 0, "Data should be OK");
 | 
						|
        gwbuf_free(head);
 | 
						|
        gwbuf_free(next);
 | 
						|
    }
 | 
						|
 | 
						|
    /** Fragmented buffer chain */
 | 
						|
    size_t chunk = 5;
 | 
						|
    size_t total = 0;
 | 
						|
    buffer = NULL;
 | 
						|
 | 
						|
    do
 | 
						|
    {
 | 
						|
        chunk = chunk + 5 < sizeof(resultset) ? 5 : (chunk + 5) - sizeof(resultset);
 | 
						|
        buffer = gwbuf_append(buffer, gwbuf_alloc_and_load(chunk, resultset + total));
 | 
						|
        total += chunk;
 | 
						|
    }
 | 
						|
    while (total < sizeof(resultset));
 | 
						|
 | 
						|
    for (unsigned int i = 0; i < N_PACKETS; i++)
 | 
						|
    {
 | 
						|
        GWBUF* next = modutil_get_next_MySQL_packet(&buffer);
 | 
						|
        mxb_assert_message(next, "Next packet buffer should not be NULL");
 | 
						|
        mxb_assert_message(gwbuf_length(next) == packets[i].length,
 | 
						|
                           "Next packet buffer should contain enough data");
 | 
						|
        next = gwbuf_make_contiguous(next);
 | 
						|
        mxb_assert_message(memcmp(GWBUF_DATA(next), &resultset[packets[i].index], GWBUF_LENGTH(next)) == 0,
 | 
						|
                           "Next packet buffer's data should be equal to original data");
 | 
						|
        gwbuf_free(next);
 | 
						|
    }
 | 
						|
    mxb_assert_message(buffer == NULL, "Buffer should be NULL");
 | 
						|
}
 | 
						|
 | 
						|
void test_strnchr_esc_mysql()
 | 
						|
{
 | 
						|
    char comment1[] = "This will -- fail.";
 | 
						|
    mxb_assert_message(strnchr_esc_mysql(comment1, '.', sizeof(comment1) - 1) == NULL,
 | 
						|
                       "Commented character should return NULL");
 | 
						|
 | 
						|
    char comment2[] = "This will # fail.";
 | 
						|
    mxb_assert_message(strnchr_esc_mysql(comment2, '.', sizeof(comment2) - 1) == NULL,
 | 
						|
                       "Commented character should return NULL");
 | 
						|
 | 
						|
    char comment3[] = "This will fail/* . */";
 | 
						|
    mxb_assert_message(strnchr_esc_mysql(comment3, '.', sizeof(comment3) - 1) == NULL,
 | 
						|
                       "Commented character should return NULL");
 | 
						|
 | 
						|
    char comment4[] = "This will not /* . */ fail.";
 | 
						|
    mxb_assert_message(strnchr_esc_mysql(comment4, '.', sizeof(comment4) - 1) == strrchr(comment4, '.'),
 | 
						|
                       "Uncommented character should be matched");
 | 
						|
 | 
						|
    char comment5[] = "This will fail/* . ";
 | 
						|
    mxb_assert_message(strnchr_esc_mysql(comment5, '.', sizeof(comment5) - 1) == NULL,
 | 
						|
                       "Bad comment should fail");
 | 
						|
}
 | 
						|
 | 
						|
void test_strnchr_esc()
 | 
						|
{
 | 
						|
    /** Single escaped and quoted characters */
 | 
						|
    char esc1[] = "This will fail\\.";
 | 
						|
    mxb_assert_message(strnchr_esc(esc1, '.', sizeof(esc1) - 1) == NULL,
 | 
						|
                       "Only escaped character should return NULL");
 | 
						|
    mxb_assert_message(strnchr_esc_mysql(esc1, '.', sizeof(esc1) - 1) == NULL,
 | 
						|
                       "Only escaped character should return NULL");
 | 
						|
 | 
						|
    char esc2[] = "This will fail\".\"";
 | 
						|
    mxb_assert_message(strnchr_esc(esc1, '.', sizeof(esc1) - 1) == NULL,
 | 
						|
                       "Only escaped character should return NULL");
 | 
						|
    mxb_assert_message(strnchr_esc_mysql(esc1, '.', sizeof(esc1) - 1) == NULL,
 | 
						|
                       "Only escaped character should return NULL");
 | 
						|
 | 
						|
    char esc3[] = "This will fail'.'";
 | 
						|
    mxb_assert_message(strnchr_esc(esc1, '.', sizeof(esc1) - 1) == NULL,
 | 
						|
                       "Only escaped character should return NULL");
 | 
						|
    mxb_assert_message(strnchr_esc_mysql(esc1, '.', sizeof(esc1) - 1) == NULL,
 | 
						|
                       "Only escaped character should return NULL");
 | 
						|
 | 
						|
    /** Test escaped and quoted characters */
 | 
						|
    char str1[] = "this \\. is a test.";
 | 
						|
    mxb_assert_message(strnchr_esc(str1, '.', sizeof(str1) - 1) == strrchr(str1, '.'),
 | 
						|
                       "Escaped characters should be ignored");
 | 
						|
    mxb_assert_message(strnchr_esc_mysql(str1, '.', sizeof(str1) - 1) == strrchr(str1, '.'),
 | 
						|
                       "Escaped characters should be ignored");
 | 
						|
    char str2[] = "this \"is . \" a test .";
 | 
						|
    mxb_assert_message(strnchr_esc(str2, '.', sizeof(str2) - 1) == strrchr(str2, '.'),
 | 
						|
                       "Double quoted characters should be ignored");
 | 
						|
    mxb_assert_message(strnchr_esc_mysql(str2, '.', sizeof(str2) - 1) == strrchr(str2, '.'),
 | 
						|
                       "Double quoted characters should be ignored");
 | 
						|
    char str3[] = "this 'is . ' a test .";
 | 
						|
    mxb_assert_message(strnchr_esc(str3, '.', sizeof(str3) - 1) == strrchr(str3, '.'),
 | 
						|
                       "Double quoted characters should be ignored");
 | 
						|
    mxb_assert_message(strnchr_esc_mysql(str3, '.', sizeof(str3) - 1) == strrchr(str3, '.'),
 | 
						|
                       "Double quoted characters should be ignored");
 | 
						|
 | 
						|
    /** Bad quotation tests */
 | 
						|
    char bad1[] = "This will \" fail.";
 | 
						|
    mxb_assert_message(strnchr_esc(bad1, '.', sizeof(bad1) - 1) == NULL, "Bad quotation should fail");
 | 
						|
    mxb_assert_message(strnchr_esc_mysql(bad1, '.', sizeof(bad1) - 1) == NULL, "Bad quotation should fail");
 | 
						|
 | 
						|
    char bad2[] = "This will ' fail.";
 | 
						|
    mxb_assert_message(strnchr_esc(bad2, '.', sizeof(bad2) - 1) == NULL, "Bad quotation should fail");
 | 
						|
    mxb_assert_message(strnchr_esc_mysql(bad2, '.', sizeof(bad2) - 1) == NULL, "Bad quotation should fail");
 | 
						|
 | 
						|
    char bad3[] = "This will \" fail. '";
 | 
						|
    mxb_assert_message(strnchr_esc(bad3, '.', sizeof(bad3) - 1) == NULL, "Different quote pairs should fail");
 | 
						|
    mxb_assert_message(strnchr_esc_mysql(bad3, '.', sizeof(bad3) - 1) == NULL,
 | 
						|
                       "Different quote pairs should fail");
 | 
						|
 | 
						|
    char bad4[] = "This will ' fail. \"";
 | 
						|
    mxb_assert_message(strnchr_esc(bad4, '.', sizeof(bad4) - 1) == NULL, "Different quote pairs should fail");
 | 
						|
    mxb_assert_message(strnchr_esc_mysql(bad4, '.', sizeof(bad4) - 1) == NULL,
 | 
						|
                       "Different quote pairs should fail");
 | 
						|
}
 | 
						|
 | 
						|
GWBUF* create_buffer(size_t size)
 | 
						|
{
 | 
						|
    GWBUF* buffer = gwbuf_alloc(size + 4);
 | 
						|
    uint8_t* data = (uint8_t*)GWBUF_DATA(buffer);
 | 
						|
    *(data + 0) = size;
 | 
						|
    *(data + 1) = size >> 8;
 | 
						|
    *(data + 2) = size >> 16;
 | 
						|
    *(data + 3) = 0;
 | 
						|
    return buffer;
 | 
						|
}
 | 
						|
 | 
						|
void test_large_packets()
 | 
						|
{
 | 
						|
    /** Two complete large packets */
 | 
						|
    for (int i = -4; i < 5; i++)
 | 
						|
    {
 | 
						|
        unsigned long ul = 0x00ffffff + i;
 | 
						|
        size_t first_len = ul > 0x00ffffff ? 0x00ffffff : ul;
 | 
						|
        GWBUF* buffer = create_buffer(first_len);
 | 
						|
 | 
						|
        if (first_len < ul)
 | 
						|
        {
 | 
						|
            buffer = gwbuf_append(buffer, create_buffer(ul - first_len));
 | 
						|
        }
 | 
						|
        size_t before = gwbuf_length(buffer);
 | 
						|
        GWBUF* complete = modutil_get_complete_packets(&buffer);
 | 
						|
 | 
						|
        mxb_assert_message(buffer == NULL, "Original buffer should be NULL");
 | 
						|
        mxb_assert_message(complete, "Complete buffer should not be NULL");
 | 
						|
        mxb_assert_message(gwbuf_length(complete) == before, "Complete buffer should contain all data");
 | 
						|
        gwbuf_free(complete);
 | 
						|
    }
 | 
						|
 | 
						|
    /** Incomplete packet */
 | 
						|
    for (int i = 0; i < 5; i++)
 | 
						|
    {
 | 
						|
        GWBUF* buffer = create_buffer(0x00ffffff - i);
 | 
						|
        buffer = gwbuf_rtrim(buffer, 4);
 | 
						|
        GWBUF* complete = modutil_get_complete_packets(&buffer);
 | 
						|
        mxb_assert_message(buffer, "Incomplete buffer is not NULL");
 | 
						|
        mxb_assert_message(complete == NULL, "The complete buffer is NULL");
 | 
						|
        gwbuf_free(buffer);
 | 
						|
    }
 | 
						|
 | 
						|
    /** Incomplete second packet */
 | 
						|
    for (int i = 2; i < 8; i++)
 | 
						|
    {
 | 
						|
        GWBUF* buffer = gwbuf_append(create_buffer(0x00ffffff), create_buffer(i));
 | 
						|
        mxb_assert(gwbuf_length(buffer) == 0xffffffUL + i + 8);
 | 
						|
        GWBUF_RTRIM(buffer->next, 1);
 | 
						|
        GWBUF* complete = modutil_get_complete_packets(&buffer);
 | 
						|
        mxb_assert_message(buffer, "Incomplete buffer is not NULL");
 | 
						|
        mxb_assert_message(complete, "The complete buffer is not NULL");
 | 
						|
        mxb_assert_message(gwbuf_length(complete) == 0xffffff + 4, "Length should be correct");
 | 
						|
        gwbuf_free(buffer);
 | 
						|
        gwbuf_free(complete);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
char* bypass_whitespace(const char* sql)
 | 
						|
{
 | 
						|
    return modutil_MySQL_bypass_whitespace((char*)sql, strlen(sql));
 | 
						|
}
 | 
						|
 | 
						|
void test_bypass_whitespace()
 | 
						|
{
 | 
						|
    char* sql;
 | 
						|
 | 
						|
    sql = bypass_whitespace("SELECT");
 | 
						|
    mxb_assert_message(*sql == 'S', "1");
 | 
						|
 | 
						|
    sql = bypass_whitespace(" SELECT");
 | 
						|
    mxb_assert_message(*sql == 'S', "2");
 | 
						|
 | 
						|
    sql = bypass_whitespace("\tSELECT");
 | 
						|
    mxb_assert_message(*sql == 'S', "3");
 | 
						|
 | 
						|
    sql = bypass_whitespace("\nSELECT");
 | 
						|
    mxb_assert_message(*sql == 'S', "4");
 | 
						|
 | 
						|
    sql = bypass_whitespace("/* comment */SELECT");
 | 
						|
    mxb_assert_message(*sql == 'S', "5");
 | 
						|
 | 
						|
    sql = bypass_whitespace(" /* comment */ SELECT");
 | 
						|
    mxb_assert_message(*sql == 'S', "6");
 | 
						|
 | 
						|
    sql = bypass_whitespace("-- comment\nSELECT");
 | 
						|
    mxb_assert_message(*sql == 'S', "7");
 | 
						|
 | 
						|
    sql = bypass_whitespace("-- comment\n /* comment */ SELECT");
 | 
						|
    mxb_assert_message(*sql == 'S', "8");
 | 
						|
 | 
						|
    sql = bypass_whitespace("# comment\nSELECT");
 | 
						|
    mxb_assert_message(*sql == 'S', "9");
 | 
						|
}
 | 
						|
 | 
						|
int main(int argc, char** argv)
 | 
						|
{
 | 
						|
    mxb::Log log;
 | 
						|
 | 
						|
    int result = 0;
 | 
						|
 | 
						|
    result += test1();
 | 
						|
    result += test2();
 | 
						|
    test_single_sql_packet1();
 | 
						|
    test_single_sql_packet2();
 | 
						|
    test_multiple_sql_packets1();
 | 
						|
    test_multiple_sql_packets2();
 | 
						|
    test_strnchr_esc();
 | 
						|
    test_strnchr_esc_mysql();
 | 
						|
    test_large_packets();
 | 
						|
    test_bypass_whitespace();
 | 
						|
    exit(result);
 | 
						|
}
 |